
; S00045-A / MPU: SC85203CPT (mask-programmed MC6801)

; Low S-spec number strongly suggests the original LMT-I units were obsoleted
; VERY early in the lifespan of the EMETCON system!

; Boards:
; 2D26148Gxx - LMT-10x receiver (meter style), used w/ 5558C17Gxx front & 2D25922Gxx P/S
;              Late LMT-10x 'meter style' units have 2D26120Gxx P/S assembly
; 7312C18Gxx - LMT-11x receiver (LCR enclosure), used with 7312C21Gxx P/S

; Code reverse-engineered from dump taken from an IC borrowed from an LMT-10x main board

; MPU registers - $0000-$001F (on-chip)

; Ports - X = unused (set as output), O = output, I = Input

P1DDR   EQU   0 ; Port 1 data direction register (0 = in, 1 = out)
PORT1   EQU   2 ; Port 1 I/O register
; Pins 20 - 13
; bit 7 - O Deadman timer drive
; bit 6 - I Flag for masking Relay C / D (2D26148: open, 7312C18: grounded)
; bit 5 - O Relay D bit (used on LMT-2 only)
; bit 4 - O Relay C bit (available option on 7312C18 only)
; bit 3 - O Relay B bit
; bit 2 - O Relay A bit
; bit 1 - I Inbound data stream
; bit 0 - I Cold Load pickup (1 = shed relays for 7.5 minutes upon power-up)

P2DDR   EQU   1 ; Port 2 data direction register (0 = in, 1 = out)
PORT2   EQU   3 ; Port 2 I/O register
; Pins 12 - 8
; bit 7 - I Hidden bit PC2 (snapshot of P2.2 at power-up, sets chip mode)
; bit 6 - I Hidden bit PC1 (snapshot of P2.1 at power-up, sets chip mode)
; bit 5 - I Hidden bit PC0 (snapshot of P2.0 at power-up, sets chip mode)
; bit 4 - I Freq select jumper (1 = 12.5, 0 = 9.6)
; bit 3 - O (2D26148: open, 7312C18: pull-up)
; bit 2 - O pull-up
; bit 1 - I NMI drive (pull-up)
; bit 0 - O pull-up

P3DDR   EQU   4 ; Port 3 data direction register (0 = in, 1 = out)
PORT3   EQU   6 ; Port 3 I/O register
; Pins 37-30
; bit 7 - I Gold address bit 1
; bit 6 - I Gold address bit 0
; bit 5 - I Silver address bit 5
; bit 4 - I Silver address bit 4
; bit 3 - I Silver address bit 3
; bit 2 - I Silver address bit 2
; bit 1 - I Silver address bit 1
; bit 0 - I Silver address bit 0
; This is where the address comb with the fingers that can be clipped comes into play
; Intact finger = bit is 0, clipped finger = bit is pulled up to a 1
; Subtract 1 from desired address to get binary value to 'program' into the comb
; Range for Gold (top-level) addresses is 1-4, Silver (2nd level) is 1-60

P4DDR   EQU   5 ; Port 4 data direction register (0 = in, 1 = out)
PORT4   EQU   7 ; Port 4 I/O register
; Pins 29 - 22
; bit 7 - X pull-up
; bit 6 - X unused
; bit 5 - X unused
; bit 4 - X unused
; bit 3 - X unused
; bit 2 - I aux freq sel resistor (all units have a pull-up here)
; bit 1 - I aux freq sel resistor (all units have a pull-up here)
; bit 0 - O Test LED output

; SC1 pin (39) becomes IS3 in single-chip mode / not used
; SC2 pin (38) becomes OS3 in single-chip mode / 2D26148 (opt jpr), 7312C18 (pull-up)

TMCSR   EQU   8 ; Timer Control and Status register
; only bits of importance are 0 and 3 - the other 6 are read-only or left at 0

; Bit 7 Input Capture flag - read-only (not used)
; Bit 6 Output Compare Flag - read-only (not used)
; Bit 5 Timer Over flow flag - read-only (not used)
; Bit 4 Input Capture Interrupt enable (inhibited, always set to 0)
; Bit 3 IRQ2 enabled when OCF is set? / 0 inhibits, 1 enables
; Bit 2 Timer Overflow Interrupt enable (inhibited, always set to 0)
; Bit 1 Input Capture Register interrupt transition direction (0 to 1 - always set to 0)
; Bit 0 if this bit AND Port 2 DDR sets P2.0 = 1, OCF flag appears at port P2.1

COUNTR  EQU   9 ; (2 byte) - free-running 16-bit counter
; 16-bit free-running counter, incrementing each half-cycle of clock
; TOF bit in TMCSR is set each time the counter rolls over from FFFF to 0000
; OCF bit in TMCSR is set when the value matches that in OUTCMP

OUTCMP  EQU   11 ; (2 byte) - Output Compare Register
; Interval value is written into this register

IDLE1   EQU   15 ; not used but must be written to - Port 3 Control & Status register
IDLE2   EQU   16 ; not used but must be written to - SCI Rate / Mode Control
IDLE3   EQU   17 ; not used but must be written to - SCI Transmit / Receive register
IDLE4   EQU   18 ; not used but must be written to - SCI Receive Data Register

RAMCR   EQU   20 ; Bit 6 is the only one under user control, 1 = enables internal RAM

; RAM - $0080 - $00FF (on-chip)

; functions that have been determined

byteNow EQU   128 ; incoming bit stream from front-end stage
ctPuls  EQU   129 ; Bit counter to mark completion of incoming received byte
rlyMsk  EQU   130 ; if non-zero, relays C and D are masked out
bchSum  EQU   137 ; (2 byte) BCH checksum - if remains 0, message received OK
;                   Second byte is filler due to loading $B9 into index register
ctPhse  EQU   139 ; phase interval for demodulator code
tempBit EQU   142 ; Byte being evaluated for bit transition and direction
byteOld EQU   143 ; Previously evaluated byte, needed for one more comparison
valueIi EQU   144 ; Demodulator value - 0 degree
valueQi EQU   145 ; Demodulator value - 90 degree
phsTmp  EQU   148 ; temporary byte during phase value calculation
g1valI  EQU   149 ; quadrant table 1 (95-9C)
g1valQ  EQU   150
g2valI  EQU   157 ; quadrant table 2 (9D-A4)
g2valQ  EQU   158
g3valI  EQU   165 ; quadrant table 3 (A5-AC)
g3valQ  EQU   166
g4valI  EQU   173 ; quadrant table 4 (AD-B4)
g4valQ  EQU   174
relJump EQU   183 ; (2 byte) Value for relative jump after a demodulator cycle finishes
msgStrt EQU   185
INTV_W  EQU   186 ; (2 byte) interval timer - working value
actbyte EQU   188 ; relay table?
nmiCt   EQU   191 ; counter for use during NMI code cycle
msgTbl  EQU   192 ; Incoming message buffer (5 bytes / 40 bits)
rcvByt  EQU   196
ledFlg  EQU   198 ; Flag to determine whether Test LED is on or off
ldTimA  EQU   199 ; Load A timer
ldTimB  EQU   200 ; Load B timer
ldTimC  EQU   201 ; Load C timer
ldTimD  EQU   202 ; Load D timer
rlyBit  EQU   203 ; relay bits to write to Port 1 outputs
INTV_M  EQU   208 ; (2 byte) interval timer - master value? From frequency table
SMPCLK  EQU   210 ; (2 byte) sampling clock interval from frequency table
Stak2   EQU   211 ; alternate location for stack pointer?
Stack   EQU   255 ; end of internal RAM / main stack location

; still to be determined

ram_83  EQU   131
ram_84  EQU   132
ram_85  EQU   133
ram_86  EQU   134
ram_87  EQU   135
ram_88  EQU   136
ram_8C  EQU   140
ram_8D  EQU   141
ram_92  EQU   146
ram_93  EQU   147
ram_B5  EQU   181
ram_BD  EQU   189
ram_C5  EQU   197
ram_CC  EQU   204
ram_CD  EQU   205
ram_CE  EQU   206
ram_CF  EQU   207

; program (on-chip mask ROM space)

        ORG   $F800

; firmware number and revision

romFW   FCB   0,45,1

; This section is called when the Output Compare interrupt is invoked by the MPU

vec_tmr LDAA  TMCSR ; read and discard bits 5-7
        LDD   OUTCMP
        ADDD  SMPCLK
        STD   OUTCMP
        LDAA  #$80
        ORAA  PORT1 ; do an indirect fetch of incoming serial bit
        STAA  PORT1 ; send Deadman pulse
        RORA
        RORA
        ROL   byteNow ; roll serial bit into in incoming byte
        DEC   ctPuls
        LDAA  #$07
        ANDA  ctPuls 
        BEQ   tmr_01 ; exit unless 8 bits have been gathered

; Next line serves as a normal exit for above section AND a hook for unneeded vectors

vec_nul RTI

; Continuation of timer code

; Initialize ports, read settings, set variables

tmr_01  CLI
        LDD   #$40BC
        STAA  RAMCR ; $40 enables internal RAM
        STAB  P1DDR ; default state: 10111100
        LDD   #$0D79
        STAA  P2DDR ; default state: 00001101
        STAB  P4DDR ; default state: 01111001
        LDD   #$0009
        STAA  IDLE2 ; zero out otherwise unused registers
        STAA  IDLE3
        STAA  P3DDR
        STAA  IDLE1
        STAB  TMCSR ; default state: 00001001
        LDAA  PORT2
        ANDA  #$10 ; read primary frequency resistors
        LDAB  PORT4
        ANDB  #$06 ; read aux frequency resistors
        LSRA
        ABA
        ASLA
        LDX   #freqTbl
        TAB
        ABX
        LDD   $00,X ; fetch and save values from the table
        STD   SMPCLK
        LDD   $02,X
        STD   INTV_M
        LDAA  PORT1 ; get and save relay mask flag
        ANDA  #$40
        STAA  rlyMsk
        CLR   nmiCt ; reset NMI internal counter

        CLR   ram_CF
        LDAA  ram_86
        BNE   tmr_02
        LDAA  #$03
        BRA   tmr_03
tmr_02  DECA
        BNE   tmr_04
tmr_03  LDAB  PORT2
        EORB  #$01
        STAB  PORT2 ; flip state of P2.0
tmr_04  STAA  ram_86

; decrement interval timers

        LDAA  ram_CC
        BEQ   tmr_05
        DEC   ram_CC
tmr_05  LDX   INTV_W
        DEX
        STX   INTV_W
        BNE   tmr_08
        LDX   INTV_M
        STX   INTV_W
        LDX   #ldTimD ; set pointer to Load D
tmr_06  LDAA  $00,X
        BEQ   tmr_07
        DECA
        STAA  $00,X
tmr_07  DEX
        CPX   #ldTimA-1 ; finished all four loads?
        BPL   tmr_06

tmr_08  LDD   byteNow ; consecutive reads - ctPuls in accB
        STAA  tempBit
        ANDB  #$18
        STAB  ctPhse
        BNE   tmr_09
        JMP   tmr_26

tmr_09  LDAA  msgStrt
        BMI   bitTst
        LDAA  #$FB
        ANDA  PORT2
        STAA  PORT2 ; clear P2.2

;    Examine bit stream to find presence / direction of bit transitions
;    byteOld is previously examined byte, to compare its bit 0 to current bit 7
;    Previous bit 7 (or byteOld bit 0) is XOR'd with current bit 7 to find transitions
;    Sign bit comparison determines whether a transition occurred (0: no transition)
;    If a transition was found, Bit 7 is then used to determine direction
;    If 1, a positive transition (0->1) occurred, else it was negative (1->0)
;    Current bit position is then used to look up value and negated if needed
;    Cumulative values are saved to valueIi (0 degree) and valueQi (90 degree)

bitTst  CLRA
        CLRB
        STD   valueIi ; write to consecutive bytes - valueQi in AccB
        LDAA  byteOld
        LDX   #demod ; we are checking for presence and direction of bit transitions
tmr_11  TAB
        EORB  tempBit
        BPL   tmr_14
        LDAB  $00,X ; read 0-degree value
        TSTA
        BPL   tmr_12
        NEGB
tmr_12  ADDB  valueIi
        STAB  valueIi
        LDAB  $02,X ; read 90-degree value
        TSTA
        BPL   tmr_13
        NEGB
tmr_13  ADDB  valueQi
        STAB  valueQi
tmr_14  LDAA  tempBit
        ROL   tempBit
        DEX
        CPX   #demod-8
        BNE   tmr_11
        STAA  byteOld ; save tempBit for bit 7 / bit 0 comparison

; integration and first half of phase calculations

        LDX   #g1valI
        LDAB  ctPhse ; value will be 0, 8, 16, or 24
        ABX
        LDD   valueIi ; read of consecutive bytes - valueQi in AccB
        STD   $00,X
        LDAA  g1valI ; table 1 value
        LDAB  g2valI ; table 2 value
        ADDA  g3valI ; table 3 value
        ADDB  g4valI ; table 4 value
        ASRA
        ASRB
        ABA
        STAA  valueIi
        LDAB  $03,X
        JSR   tmr_66
        STAA  $02,X
        LDAA  g1valQ ; table 1 value
        LDAB  g2valQ ; table 2 value
        ADDA  g3valQ ; table 3 value
        ADDB  g4valQ ; table 4 value
        ASRA
        ASRB
        ABA
        STAA  valueQi
        LDAB  $05,X
        JSR   tmr_66
        ADDA  $02,X
        STAA  $02,X

; use sign bit left over from calculations to determine sign of
; valueIi, valueQi (and change if necessary)

        ASLA ; bump sign bit into carry
        LDD   valueIi ; read of consecutive bytes - valueQi in AccB
        BCC   tmr_15
        NEGA
        NEGB
tmr_15  STAB  valueQi

; now continue with phase calculations (AccA carried over)

        CLRB
        SUBD  $03,X
        ASRA
        RORB
        ASRA
        RORB
        ADDD  $03,X
        STD   $03,X
        BPL   tmr_16
        NEGA
tmr_16  STAA  $07,X
        LDAA  valueQi
        CLRB
        SUBD  $05,X
        ASRA
        RORB
        ASRA
        RORB
        ADDD  $05,X
        STD   $05,X
        BPL   tmr_17
        NEGA
tmr_17  LDAB  $07,X
        CBA
        BPL   tmr_18
        ASRA
        BRA   tmr_19

; demodulation table, where 244 = -12, 239 = -17

        FCB   17,12,0,244,239,244,0
demod   FCB   12,17,12

; Continuation of timer code

tmr_18  ASRB
tmr_19  ABA
        STAA  $07,X
        LDX   ram_B5
        LDAA  $07,X
        CMPA  #$20
        BGE   tmr_23
        LDX   #g1valI
        LDAA  $07,X
tmr_20  STX   ram_BD
tmr_21  LDAB  #$08
        ABX
        CPX   #ram_B5
        BGE   tmr_22
        LDAB  $07,X
        CBA
        BPL   tmr_21
        TBA
        BRA   tmr_20

tmr_22  LDX   ram_BD
        STX   ram_B5
        BRA   tmr_24

tmr_23  LDAA  $02,X
        STAA  ram_8C
        RTI

tmr_24  LDAA  #$03
        STAA  ram_8D
        CLRA
        STAA  msgStrt
        STAA  ram_8C
        RTI

; subroutine - called once by above code, once by main code

tmr_25  CLR   rcvByt
        CLR   ram_84
        LDAA  #$04
        STAA  ram_85
        LDX   #tmr_36
        STX   relJump
        LDAA  #$07
        ANDA  PORT4
        STAA  PORT4 ; clear bits 3-7 (not used anyway)
        RTS

; Continuation of timer code

tmr_26  LDD   ram_8C
        DECB
        BEQ   tmr_30
        BPL   tmr_29
        LDAB  #$FF
        STAB  msgStrt
        LDAB  ram_8C
        TST   ram_93
        BMI   tmr_27
        COMB
tmr_27  PSHA
        LDAA  PORT2 ; set P2.2, P2.3
        ORAA  #$0C
        TSTB
        BMI   tmr_28
        ANDA  #$F7
tmr_28  STAA  PORT2 ; clear P2.3
        PULA
        BRA   tmr_32

tmr_29  DECB
        BNE   tmr_31
        TAB
        EORB  ram_92
        BMI   tmr_31
        BRA   tmr_32

tmr_30  TAB
        EORB  ram_92
        BMI   tmr_32
        STAA  ram_93
tmr_31  DEC   ram_8D
tmr_32  STAA  ram_92
        LDAB  msgStrt
        BMI   tmr_34
        STAB  ram_83
tmr_33  JMP   tmr_52

tmr_34  LDAB  ram_83
        BNE   tmr_33
        LDAB  ram_93
        BMI   tmr_35

; we now have a bit - roll it in through the message table

        COMA
tmr_35  ROLA
        LDX   #msgTbl ; roll bit into place
        ROL   $04,X ; this is rcvByt
        ROL   $03,X
        ROL   $02,X
        ROL   $01,X
        ROL   $00,X
        LDAA  rcvByt
        JSR   tmr_70 ; perform BCH checksum calculation
        DEC   ram_85
        BNE   tmr_41
        LDX   relJump
        JMP   $00,X

; entry via relJump

tmr_36  SUBA  #$08
        BMI   tmr_42
        CMPA  #$04
        BGE   tmr_42
        ASRA
        STAA  ram_87
        LDX   #tmr_37
        LDAA  #$08
        BRA   tmr_40 ; code reuse

; entry via relJump

tmr_37  LDAB  ram_87
        BEQ   tmr_38
        INC   ram_84
        LDAA  #$87
        ANDA  PORT4
        ORAA  #$20 ; set P4.5 high (nothing there)
        STAA  PORT4
        LDX   #tmr_46
        LDAA  #$16
        BRA   tmr_40 ; code reuse

tmr_38  LDAA  #$87
        ANDA  PORT4
        ORAA  #$10 ; set P4.4 high (nothing there)
        STAA  PORT4
        LDX   #tmr_43
tmr_39  LDAA  #$06
tmr_40  STAA  ram_85
        STX   relJump
        CLR   rcvByt
tmr_41  JMP   bitTst

tmr_42  BRA   tmr_51

; entry via relJump

tmr_43  LDAB  #$02
        STAB  ram_88
        LDAB  PORT3 ; retrieve Silver address from address comb
        ANDB  #$3F
        CBA
        BEQ   tmr_45
        LDAB  PORT3 ; retrieve Gold address from address comb
        ROLB
        ROLB
        ROLB
        ORAB  #$3C
        ANDB  #$3F
        CBA
        BEQ   tmr_44
        INC   ram_84
        BRA   tmr_45

tmr_44  DEC   ram_88
tmr_45  LDX   #tmr_49
        BRA   tmr_39 ; code reuse

; entry via relJump

tmr_46  LDX   #tmr_47
        LDAA  #$0A
        BRA   tmr_40 ; code reuse

        BRA   tmr_51 ; vestige of older code?

; entry via relJump

tmr_47  LDX   #tmr_48
        LDAA  #$02
        BRA   tmr_40 ; code reuse

; entry via relJump

tmr_48  LDX   #tmr_50
        BRA   tmr_39 ; code reuse

; entry via relJump

tmr_49  TAB
        ANDA  #$30
        STAA  ram_CE
        ASRB
        ANDB  #$07
        LDX   #bytTbl
        ABX
        LDAB  $00,X
        STAB  actbyte
        BRA   tmr_48 ; code reuse

; command decode table? Or relay table? (also see: S00036 and S00175)

bytTbl  FCB   0,1,2,4,8,65,66,15

; Continuation of timer code

; entry via relJump

tmr_50  TST   bchSum
        BNE   tmr_51
        LDAA  #$FF
        STAA  ledFlg ; put LED flag in 'on' mode
        TST   ram_84
        BNE   tmr_52
        BRA   tmr_53

tmr_51  LDAA  #$01
        STAA  ram_83
tmr_52  CLRA
        STAA  ram_88
        STAA  bchSum
        JSR   tmr_25
        JMP   bitTst

tmr_53  LDAB  actbyte
        BEQ   tmr_54
        TBA
        LSRA
        LSRA
        LSRA
        LSRA
        BEQ   tmr_55
        SUBA  #$04
        BEQ   tmr_57
        BRA   tmr_51

tmr_54  LDAA  #$0F
        STAA  actbyte
        CLRB
        STAB  ledFlg ; put LED flag in 'off' mode
        BRA   tmr_56

tmr_55  LDAB  #$1E
tmr_56  JSR   tmr_59
        BRA   tmr_51

tmr_57  LDAA  #$06
        CMPB  #$41
        BEQ   tmr_58
        CMPB  #$42
        BNE   tmr_51
        DECA
tmr_58  STAA  ram_C5
        LDAA  #$FF
        STAA  ram_CC
        BRA   tmr_51

; subroutine (called by once by above code)

tmr_59  PSHX
        LDAA  ram_CE
        BEQ   tmr_61
tmr_60  ASLB
        SUBA  #$10
        BNE   tmr_60
tmr_61  STAB  ram_CE
        LDAB  ram_88
        STAB  ram_CD
        LDAB  actbyte
        TST   rlyMsk
        BEQ   tmr_62
        ANDB  #$03
tmr_62  LDX   #ldTimA
tmr_63  LSRB
        BCC   tmr_65
        LDAA  ram_CE
        BEQ   tmr_64
        LDAA  COUNTR
        LDAA  COUNTR+1
        ANDA  #$0F
        ADDA  ram_CE
tmr_64  STAA  $00,X
tmr_65  INX
        TSTB
        BNE   tmr_63
        PULX
        RTS

; Phase calculation subroutine (identical code in S00036, S00060, S00175)

tmr_66  STAA  phsTmp
        BPL   tmr_67
        NEGA
tmr_67  TSTB
        BPL   tmr_68
        NEGB
        NEG   phsTmp
tmr_68  MUL
        ASLD
        ASLD
        TST   phsTmp
        BPL   tmr_69
        NEGA
tmr_69  RTS

; BCH checksum calculation subroutine / if 0 is returned, data is good
; Similar to code in S00036, but S00060 has improved routine without carry bit usage

tmr_70  LDX   #bchSum
        LDAB  $00,X
        ASLB
        PSHA
        LSRA
        TBA
        EORA  #$43
        BITA  #$40
        BNE   tmr_71
        BCC   tmr_72
        CLC
tmr_71  BCS   tmr_72
        TBA
tmr_72  ANDA  #$3F
        STAA  $00,X
        PULA
        RTS

; Start of main application code

vec_rst LDS   #stack ; place stack at end of RAM
        LDAA  P2DDR ; fetch state of PC0-PC2
        ANDA  #$E0
        CMPA  #$E0 ; and ensure it is set up correctly

; Configuration incorrect - wait for deadman reset

app_01  BNE   app_01

; WHAT IS THIS DOING HERE???
; This code is present in S00036 as a hook for S00033 (when used in a DCT) Why???

        LDAA  $E000
        CMPA  #$55
        BNE   app_02
        JMP   $E001

; anyway, continue

app_02  CLI
        LDD   #$40BC
        STAA  RAMCR ; $40 enables internal RAM
        STAB  P1DDR ; default state: 10111100
        LDD   #$0D79
        STAA  P2DDR ; default state: 00001101
        STAB  P4DDR ; default state: 01111001
        LDD   #$0001
        STAA  IDLE2 ; zero out otherwise unused registers
        STAA  IDLE3
        STAA  P3DDR ; this port is ALL inputs (address comb)
        STAA  IDLE1
        STAB  TMCSR
        CLR   PORT4 ; turn test LED off
        LDAA  PORT3 ; read address comb
        INCA
        BNE   app_05

app_03  LDAB  #$3C
        LDAA  PORT1 ; check relay mask flag status
        ANDA  #$40
        BEQ   app_04
        ANDB  #$0C
app_04  LDAA  PORT1
        ANDA  #$80
        ABA
        STAA  PORT1 ; send Deadman pulse, set relay statuses
        BRA   app_07

; Is cold load pickup enabled? (it is by default, so P1.0 should be 1)
; This option causes all loads to be shed for 7.5 minutes after power-up or reset

app_05  LDAA  PORT1
        ANDA  #$01
        BEQ   app_06
        BRA   app_03

; Pull-up resistor is not present, so we write 0 to relay bits to restore loads instead
; bit 5 = Load D ... bit 2 = Load A

app_06  LDAA  #$C3
        ANDA  PORT1
        STAA  PORT1

; Now, we do a code integrity check before continuing

app_07  JMP   codeck

; Returning after successful check

app_08  LDAA  PORT1 ; Get P1.6 status, save in rlyMsk
        ANDA  #$40
        STAA  rlyMsk
        LDAA  PORT3 ; read address comb
        INCA
        BNE   app_09
        LDD   #$0202 ; starting values for relay timers
        STD   ldTimA ; save in Load A / B
        STD   ldTimC ; save in Load C / D
        TST   rlyMsk ; do we ignore Relays C / D?
        BEQ   app_10
        CLR   ldTimC ; forget Load C
        CLR   ldTimD ; forget Load D
        BRA   app_10

; Checking again for cold load pickup option

app_09  LDAA  PORT1
        ANDA  #$01
        BEQ   app_10

; Cold load pickup also includes a 4-minute random interval to prevent restoration surge
; Interval length is calculated by grabbing 4 random bits from serial input pin
; Resultant value (in AccB), divided by 4 = time between 15 seconds and 3.5 minutes
; 7.5 minute interval length * 4 = 30
; Similar code is present in S00036

        CLRB
        LDAA  PORT1
        RORA
        RORA
        ROLB
        LDAA  PORT1
        RORA
        RORA
        ROLB
        LDAA  PORT1
        RORA
        RORA
        ROLB
        LDAA  PORT1
        RORA
        RORA
        ROLB
        ADDB  #$1E

; Now write this into Load A / B timers (if flag is set accordingly, Loads C / D too)

        LDX   #ldTimA
        STAB  $00,X ; Load A
        STAB  $01,X ; Load B
        TST   rlyMsk
        BNE   app_10
        STAB  $02,X ; Load C
        STAB  $03,X ; Load D

; Read primary (and secondary) frequency resistors to determine place in lookup table
; Secondary resistors (on Port 4 pins) were for never-implemented frequencies
; Therefore, resistor attached to Port 2 pin is only one that changes

app_10  LDAA  PORT2
        ANDA  #$10
        LDAB  PORT4
        ANDB  #$06
        LSRA
        ABA
        ASLA
        LDX   #freqTbl
        TAB
        ABX
        LDD   $00,X ; fetch and save values from the table
        STD   SMPCLK
        LDD   $02,X
        STD   INTV_M
        STD   INTV_W

        LDAA  #$FE
        ANDA  PORT2
        STAA  PORT2 ; clear P2.0
        INC   ram_86
        JSR   tmr_25
        LDAA  #$05
        STAA  ram_8D
        LDD   #$0098
        STD   ram_B5
        LDD   COUNTR
        ADDD  SMPCLK
        STD   OUTCMP
        LDAA  TMCSR
        ORAA  #$08
        STAA  TMCSR
        BRA   app_11

; frequency table - resistor table is based on 2D26148Gxx designators
; Designators on 7312C21Gxx are R20/R16, R19/R15, R18/R14

freqTbl FDB   $01A9,$113C ; R3, R5, R16 = 425 and 4412
        FDB   $0190,$124F ; R2, R5, R16 = 400 and 4687
        FDB   $00FF,$1CB9 ; R3, R4, R16 = 255 and 7353
        FDB   $019A,$11DD ; R2, R4, R16 = 410 and 4573 = 12.5kHz
        FDB   $01CE,$0FDA ; R3, R5, R17 = 462 and 4058
        FDB   $018C,$127F ; R2, R5, R17 = 396 and 4735
        FDB   $0129,$18A9 ; R3, R4, R17 = 297 and 6313
        FDB   $01AD,$1113 ; R2, R4, R17 = 429 and 4371 = 9.615kHz

; Continuation of main code

app_11  LDX   #ldTimD
        TST   rlyMsk
        BEQ   app_12 ; skip Loads C / D
        DEX
        DEX
app_12  CLRA
        LDAB  ram_CF
        BEQ   app_13
        CMPB  ram_CD
        BNE   app_15
app_13  TST   $00,X ; store indicated relay timer
        BEQ   app_14
        SEC
app_14  ROLA
        DEX
        CPX   #ldTimA-1 ; are we done?
        BNE   app_13
app_15  ASLA
        ASLA
        TST   rlyMsk
        BEQ   app_20
        TST   ram_CC
        BEQ   app_18
        LDAB  #$06
        CMPB  ram_C5
        BNE   app_17
        LDAB  #$20
app_16  ABA
        STAA  rlyBit
        CLR   ram_C5
        BRA   app_21

app_17  DECB
        CMPB  ram_C5
        BNE   app_19
        LDAB  #$10
        BRA   app_16

app_18  ANDA  #$CF
        BRA   app_20

app_19  LDAB  rlyBit
        ANDB  #$30
        ABA
app_20  STAA  rlyBit

; Manipulate deadman bit, then update Test LED status
; Test LED lights when it sees any message, then remains lit for 64 minutes
; Test LED also goes out after responding to a 'restore load' message

app_21  LDAB  PORT1
        ANDB  #$80
        ADDB  rlyBit
        STAB  PORT1
        LDAA  PORT4
        ORAA  #$01 ; turn test LED on
        TST   ledFlg ; but first, check to see if it should be on!
        BNE   app_22
        ANDA  #$FE ; turn test LED off
app_22  STAA  PORT4 ; set Test LED status

        CLR   ram_CF
        LDAA  #$7F
        ANDA  PORT1
        STAA  PORT1 ; send Deadman pulse
        JMP   app_11

; Code integrity check
; stack pointer is used as a second index register, so it gets reset multiple times
; Deadman timer (P1.7) is also manipulated to keep unit from resetting prematurely

codeck  LDAB  PORT1
        LDS   #stack
        LDAA  #$FF
chk_01  EORB  #$80
        STAB  PORT1
        PSHA
        DECA
        CMPA  #$7F
        BNE   chk_01
        INCA
        TSX
        LDS   #stack
chk_02  EORB  #$80
        STAB  PORT1
        CMPA  $00,X
        BEQ   chk_03
        JMP   chk_fl ; failed

chk_03  INX
        INCA
        BNE   chk_02
        LDS   #stack
        TBA
        LDAB  #$FF
chk_04  EORA  #$80
        STAA  PORT1
        COMB
        PSHB
        COMB
        DECB
        CMPB  #$7F
        BNE   chk_04
        INCB
        TSX
        LDS   #stack
chk_05  EORA  #$80
        STAA  PORT1
        COMB
        CMPB  $00,X
        BNE   chk_fl ; failed
        COMB
        INX
        INCB
        BNE   chk_05
        LDS   #stak2 ; Stack is re-pointed to $D3 - in variable space!
        CLRB
chk_06  EORA  #$80
        STAA  PORT1
        PSHB
        TSX
        CPX   #byteNow ; start of RAM
        BNE   chk_06
        LDS   #stack
        LDX   #romFW ; start of ROM
        CLR   byteNow
        CLRB
chk_07  LDAA  PORT1
        EORA  #$80
        STAA  PORT1
        LDAA  byteNow
        ADDD  $00,X
        STAA  byteNow
        INX
        INX
        CPX   #appEnd ; checksum
        BNE   chk_07
        LDX   #vectors ; last 16 bytes of memory
chk_08  ADDD  $00,X
        INX
        INX
        BNE   chk_08
        SUBD  appEnd ; checksum
        BNE   chk_fl ; failed
        CLR   byteNow
        LDS   #stack
        LDAA  PORT3
        ANDA  #$3F
        CMPA  #$3E ; checking for special Silver address 62
        BEQ   chk_09 ; return to main loop without flashing Test LED

; Code check passed, blink Test LED 3 times before continuing with application code

        LDAA  #$02 ; slow flash
        JSR   ledFlsh
chk_09  JMP   app_08

; Code check failed, flash the Test LED 3 times and re-run the test
; This results in Test LED flashing continuously to indicate internal failure

chk_fl  LDS   #stack ; reset stack pointer
        LDAA  #$01 ; rapid flash
        JSR   ledFlsh
        JMP   codeck

; Code to blink Test LED 3 times
; Value brought in via AccA determines slow (value = 2) or rapid (value = 1) flash

ledFlsh LDX   #$0003
        LDAB  PORT4
app_35  ORAB  #$01 ; turn test LED on
        STAB  PORT4
        JSR   dlyLED
        ANDB  #$FE ; turn test LED off
        STAB  PORT4
        JSR   dlyLED
        DEX
        BNE   app_35
        RTS

; Delay routine for LED flasher, 1 = 1/4 second, 2 = 1/2 second

dlyLED  PSHA
        PSHB
        PSHX
        LDAB  PORT1
app_37  LDX   #$5161
app_38  EORB  #$80
        STAB  PORT1 ; send Deadman pulse
        DEX
        BNE   app_38
        DECA
        BNE   app_37
        PULX
        PULB
        PULA
        RTS

; end of main code

; Error handling routine
; Timer code sets nmiCt initial value to 0, check if it is still 0

vec_nmi TST   nmiCt
        BNE   nmi_01

; Now we check P4.7 - it is normally high, forcing an RTI and skipping rest of section

        LDAA  PORT4
        BITA  #$80
        BEQ   nmi_02
        RTI

; P4.7 is low, so now check nmiCt and redirect as necessary

nmi_01  LDAB  nmiCt
        CMPB  #$01
        BEQ   nmi_04
        CMPB  #$02
        BEQ   nmi_06
        RTI

; We start here and bump nmiCt to 1 before continuing

nmi_02  INC   nmiCt

; set all ports as output (except P1.1), then write a 1 to those pins

        LDAA  #$FD
        STAA  P1DDR
        LDAA  #$FF
        STAA  P3DDR
        STAA  P4DDR
        LDAA  #$1F
        STAA  P2DDR
        LDAA  #$FF
        STAA  PORT1
        STAA  PORT2
        STAA  PORT3
        STAA  PORT4

; Now wait for deadman reset

nmi_03  BRA   nmi_03

; when nmiCt is 1, we end up here instead and bump nmiCt again

nmi_04  INC   nmiCt

; write a 0 to all pins configured as output

        LDAA  #$00
        STAA  PORT1
        STAA  PORT2
        STAA  PORT3
        STAA  PORT4

; Now wait for deadman reset

nmi_05  BRA   nmi_05

; when nmiCt is 2, we end up here instead

nmi_06  INC   nmiCt

; Now we reset ports to normal configuration

        LDD   #$BC0D
        STAA  P1DDR ; default state: 10111100
        STAB  P2DDR ; default state: 00001101
        LDD   #$0079
        STAA  P3DDR ; default state: 00000000
        STAB  P4DDR ; default state: 01111001

; Check for unclipped or missing address comb

nmi_07  LDAA  PORT3
        BEQ   nmi_08
        CMPA  #$FF
        BEQ   nmi_09
        BRA   nmi_10 ; looks good, jump ahead

; Address comb was inserted with no clipped teeth

nmi_08  LDAA  PORT1 ; check P1.1 and P1.6
        BITA  #$42
        BNE   nmi_10
        LDAA  PORT2
        BITA  #$10 ; read primary frequency resistors
        BNE   nmi_10
        LDAA  PORT4 
        BITA  #$06 ; read aux frequency resistors
        BNE   nmi_10
        LDAA  #$04
        STAA   PORT2 ; set P2.2
        BRA   nmi_07

; Address comb is missing!

nmi_09  LDAA  PORT1 ; check P1.1 and P1.6
        COMA
        BITA  #$42
        BNE   nmi_10
        LDAA  PORT2
        COMA
        BITA  #$10 ; read primary frequency resistors
        BNE   nmi_10
        LDAA  PORT4 
        COMA
        BITA  #$06 ; read aux frequency resistors
        BNE   nmi_10
        LDAA  #$05
        STAA  PORT2 ; set P2.0, P2.2
        BRA   nmi_07

; We're done checking - initiate another NMI

nmi_10  CLR   PORT2 ; set all outputs on P2 to 0
        BRA   nmi_07

        FCB   $FF
appEnd  FDB   $A515 ; checksum?

; Next byte should be at address FE2A - confidence check when doing round-trip compile
; Space from FE2A to FFEF (454 bytes) is padded with 3F (SWI)

        SWI

; Vectors - addresses should match as a further confidence check

        ORG   $FFF0

vectors FDB   vec_nul ; F81F - idle SCI call
        FDB   vec_nul ; F81F - idle Timer Overflow
        FDB   vec_tmr ; F803 - Output Compare
        FDB   vec_nul ; F81F - idle Input Capture
        FDB   vec_nul ; F81F - idle IRQ
        FDB   vec_rst ; FB49 - Reset
        FDB   vec_nmi ; FD91 - NMI
        FDB   vec_rst ; FB49 - Reset
