;[]---------------------------------------------------------------------------[]
;|                                                                             |
;|                  Witham Keyscan Monitor unit                                |
;|                                                                             |
;|                                                                             |
;|                                                   AirBorn Electronics, 1998 |
;[]---------------------------------------------------------------------------[]
;This code is copyright (C)1998 AirBorn Electronics
.include "8051.H"
.ECHO "  .."
.TITLE "*Witham Keyscan monitor unit, AirBorn   61-2-9925-0325"
.ECHO   "Witham Keyscan monitor unit, AirBorn   61-2-9925-0325"
.ECHO "\n"

;Revision History --------------------------------------------------------
;9  Oct  1998  Original code
;24 Nov  1998  Prototype tested okay

;[]------------------------[]
;|  Constants               |
;[]------------------------[]

Crystal equ     22118400        ;Crystal frequency
RomSize equ     0800h           ;RomSize - 2k (0800h) for 89C2051
ChipSpeed equ   1               ;Chip Speed, 1=8051 3=Dallas Hi-Speed
#if (ChipSpeed == 1)
.ECHO "  ..Assembling for standard 8051 with crystal frequency of   "
#endif
.ECHO (Crystal)
.ECHO "Hz\n"
#if (Crystal > 33000000)
  .ECHO "Is your crystal really > 33Mhz ??\n"
#endif
.ECHO "  ..Assembling for a ROM size of "
.ECHO (RomSize)
.ECHO " bytes\n"

Baud0A  equ     9600            ;Desired baud rate (A) for serial port
Baud0B  equ     115200          ;Desired baud rate (A) for serial port

T1RLA equ (Crystal/(Baud0A*192)) ;Work out correct T1 reload for the baud rate
T1RLB equ (Crystal/(Baud0B*192)) ;  and correct T1 reload for second baud rate

                                ;Sample rates selected to divide evenly
SmpRatA equ     500             ;Sample update rate in Hertz, for switches, A
SmpRatB equ     5000            ;Sample update rate in Hertz, for switches, B
SmpDiv  equ     SmpRatB/SmpRatA


SmpTime equ   1000000/SmpRatB   ;Sample time in microseconds
T0Pr    equ     20000/SmpRatB   ;Find an appropriate prescaler
T0RL  equ (Crystal/(12*SmpRatB*T0Pr)  ;Work out correct T0 reload for sample rate

;[]------------------------[]
;| Input/Output allocation  |
;[]------------------------[]

DatPort equ     P1              ;Port to read keyscan data from

GrnLED  equ     P3.7            ;Signal low (0) lights LED

SwPort   equ    P3              ;Port to read dip switches from
SwMask   equ    3Ch             ;Mask of switch inputs
SpeedBit equ    4               ;Dipswitch 1 mask: On=Fast (0)  Off=Slow (1)


;[]------------------------[]
;|  RAM Memory allocation   |
;[]------------------------[]

MnRBase equ     00h             ;Set the register base address for mainline
T0Prsc  equ     08h             ;Prescaler to time down sample interrupt rate
OldSw   equ     09h             ;History byte for switch settings
DivCnt  equ     0Ah             ;Count down for divider between fast/slow rates

StackBase equ   18h             ;Start of stack in internal RAM - allow 16 bytes

Flag0   equ     28h             ;Location for bit flags
OldSpd  bit (0+((Flag0-20h)*8)) ;Semaphore bit - when set, it is OK to Tx


;[]------------------------[]
;| Reset/Interrupt Vectors  |
;[]------------------------[]

        ORG     0               ;CPU Jumps here at reset
                                ;The .ORG directive starts code at that loc'tn.
                                ;Other assemblers use "CSEG AT 0"
        ajmp    Init


        ORG     3h              ;CPU Calls here on external int 0, if enabled
        ajmp    Stub


        ORG     0Bh             ;CPU Calls here on timer 0 overflow, if enabled
        djnz    T0Prsc,AReti    ;Exit if a Sample time has not elapsed yet
        mov     T0Prsc,#T0Pr    ;Set the Sample time prescaler to max again
        ajmp    Timer0          ;The program should jump to Timer 0 routine


        ORG     13h             ;CPU Calls here on external int 1, if enabled
                                ;The program should jump to ext int 1 routine
        ajmp    Stub            ;INT1 int is not enabled in the example program
                                ; and so should never happen - if it does then
                                ;  jump to "stub"



        ORG     1Bh             ;CPU Calls here on timer 1 overflow, if enabled
                                ;The program should jump to Timer 1 routine
        ajmp    Stub            ;TF1 int is not enabled in the example program
                                ; and so should never happen - if it does then
                                ;  jump to "stub"


        ORG     23h             ;CPU Calls here on Serial interrupt, if enabled
        ajmp    Stub

ChkStart:                       ;ROM checksumming starts from here
;[]------------------------[]
;|    Interrupt Routines    |
;[]------------------------[]

Stub:   clr     EA              ;Unexpected interrupt - disable all ints
                                ;If it is possible to give some indication of
                                ;  error, such as lighting an LED, do it here
        acall   AReti           ;Flag the interrupt routine is finished
                                ;A "reti" instruction tells the CPU that its
                                ;  interrupt routine has finished and it
                                ;  should return to the main program.
                                ;  However as we "called" the reti the CPU is
                                ;  manipulated into returning here, but in an
                                ;  "all interrupts finished" condition.
        ajmp    Init            ;Re-initialize: So unexpected Int causes reset
AReti:  reti
;------------------------------------------------------------------------------
Timer0:                         ;We are here at "SampRate" (In V1.00 = 5000Hz)
        push    PSW             ;Save the PSW register
        push    ACC             ;Save the Accumulator
        mov     PSW,#MnRBase    ;Set to register bank used by mainline

        mov     A,P3            ;Get the dip switch port
        orl     A,#(255-SwMask) ;Mask to see just the valid switches
        xch     A,OldSw         ;Exchange with the last switch port read value
        xrl     A,OldSw         ;Mask to see if any differences
        jz      Time2           ;Continue if no changes in switch position

                                ;There has been a change in switch position
        mov     A,OldSw         ;Fetch the switch settings again
        anl     A,#SpeedBit     ;Test the Baud rate setting bit
        jnz     Slow            ;Jump if on the slow setting
Fast:   mov   TH1,#(256-T1RLB)  ;Init the baud rate timer to fast rate
        sjmp    Time1
Slow:   mov   TH1,#(256-T1RLA)  ;Init the baud rate timer to slow rate

Time1:
Time2:  mov     A,OldSw         ;Fetch the switch settings again
        anl     A,#SpeedBit     ;Test the BaudRate/SampleRate setting bit
        jz      Time3           ;Jump if on the fast setting
                                ;We are on slow setting - need to divide SmpRate
        djnz    DivCnt,Time4    ;Skip next operation if we haven't counted div
        mov     DivCnt,#SmpDiv  ;Reload counter to SmpRatB/SmpRatA

Time3:  mov     SBUF,DatPort    ;Sample input port, transmit it out the UART

Time4:  pop     ACC             ;Restore Acc, previously pushed
        pop     PSW             ;Restore PSW, previously pushed
        reti                    ;Return
;------------------------------------------------------------------------------
;[]------------------------[]
;|   Setup                  |
;[]------------------------[]

Init:                           ;Here at initialization
        mov   SP,#(StackBase-1) ;Initialise the Stack pointer

        clr     A               ;Clear all RAM
        mov     R0,#7Fh         ;Start at location 7Fh - work down
        mov     R1,#7Eh         ;Do 7Eh locations - stop before R0/R1
Put0:   mov     @R0,A           ;Clear a byte of RAM
        dec     R0              ;Update the RAM pointer
        djnz    R1,Put0         ;Loop until all locations cleared
        dec     R0              ;Clear location 0 - the last location
        mov     PSW,#MnRBase    ;Set active register bank (Regbnk 0: Locs 0-7)

                                ;Init important things

        mov     TMOD,#22h       ;T1,T0: 8 bit Auto R/L
        mov     PCON,#80h       ;Enables high baud rate generation divisor
        mov   TH1,#(256-T1RLA)  ;Initialise Baud rate
        mov   TH0,#(256-T0RL)   ;Initialise the sample timer
        mov     T0Prsc,#T0Pr    ;Set the Sample time prescaler to max

        mov     TCON,#50h       ;Run both timers
        mov     SCON,#50h       ;Standard UART settings
        mov     IE,#10000010b   ;Enable interrupts: EA, Timer0
                                ;IE Register Map: EA-x-ET2-ES---ET1-EX1-ET0-EX0

;[]------------------------[]
;|    Main Program          |
;[]------------------------[]

Main:   mov     R2,#7           ;Main just flashes the diagnostic LED
        mov     R3,#0
        mov     R4,#0
Main1:  djnz    R4,Main1
        djnz    R3,Main1
        djnz    R2,Main1
        cpl     GrnLED
        sjmp    Main

;----------------------------------------------------------------------------

.ECHO "  .."
.ECHO ($+12)                     ;Report the number of bytes of code
.ECHO " bytes of program memory used = "
.ECHO (($*100)/(RomSize-12))
.ECHO "% of total memory capacity\n"
.ECHO "  .."
.ECHO (RomSize-($+12)            ;Report the number of bytes of code left
.ECHO " bytes of program memory space left unused\n"
#if ($>=(RomSize-11))
;generate an invalid statement to cause an error when we go past end of ROM
 !!! PROM bounds exceeded
#else
;.FILL (RomSize-($+12))           ;Fill the remainder of ROM with 0FFh
Version .db         "SW V1.00 ",0
.ECHO     "  ..Firmware V1.00    \n"
.CHK ChkStart                   ;Put an arithmetic checksum in 2nd last byte
        .ORG (RomSize-1)
.db 0AAh
#endif
        END
