6801 assembly: Interrupt code interpretation

Thread Starter

metermannd

Joined Oct 25, 2020
343
Here's a bit of code I need to get my mind around - understanding it is key to whether I can rewrite it for more modern languages (i.e., C++ or ARM assembly).

What I need help with most is seeing where the received data goes into memory.
The main portion of RAM is between $0080 and $00FF, with an additional range at $0C00 to $0C7F (but only the first 3 bytes are relevant as far as this code goes)

It seems as long as $C9 is NOT set to '1', message reception is basically continuous


On the hardware side of this code, the relevant bits of PIA Port A are:

bit 0 - Receive signal (serial)
bit 1 - Transmit signal (serial)
bit 2 - Transmitter enable
bit 3 - Transmit / receive relay
(In the unit I'm copying, the trace from PA3 is cut, and PA2 drives both the transmitter enable line and the transmit / receive relay)

I've noted that part of the code pertaining to PA3, and would like to see how much can safely be cleaned out.
It'd be that much less code to work with.


What I know about the transmitter side - the process of sending a message:

Data string is written into the buffer ($A0 to $B5)

Specified values (likely counters) to $B6 and $B8

Let's use the shortest string from the main code as an example for analysis:

$A0: AA B8 FF 00 E0 00
$B6: 2B
$B8: 01
$0C00: 0006

A 1 is then written to $B7 and $C9

A timer is then set and allowed to run down

Meanwhile, the interrupt code moves the output bit stream into PA1, and sets PA2 to put the unit into transmit mode.

When the timer ends, $B7 is then read to ensure it is no longer 1 (BLE) - if it is still 1, then a 'no transmit' error flag is set and the main code falls through to the error / abort routine

$C9 is then cleared to tell the interrupt that the transmitter is to be turned off (PA1 and PA2 are eventually cleared within the interrupt code)


Coming back to the beginning of the post, the main thing I need to see is where into RAM the received data is written.
Code:
;NMI routine (driven by PTM output 2)
; Software shift register:
; Capture data from PIA A bit 0 and save in Present Si byte
    LDA A $0C
    ROR A
    ROL $0082

; Check if transmit flag is set (1)
    TST $00C9
    BNE nmi_01
    JMP nmi_31

; Check to see if we have counted off 8 bits
nmi_01
    DEC $00D2
    LDA A #$07
    AND A $D2
    BEQ nmi_02
    RTI

; We now have 8 bits - Present SI byte is copied to Last Si byte
nmi_02
    LDA A $82
    STA A $83

; PTM registers are manipulated
    LDA B #$52
    STA B $01
    LDA A #$12
    TST $00B7
    BLE nmi_03
    LDA A #$92
nmi_03
    STA A $00
    LDA B #$53
    STA B $01

; Check to see if 4 'phases' are counted
    DEC $00D3
    LDA A #$03
    AND A $D3
    BNE nmi_04
    JMP nmi_43

; Prepare for I / Q table lookups
nmi_04
    LDA A $84    ; Previous Last Si byte
    LDA B $83    ; Last Si byte
    STA B $93
    CLR $0094    ; $94 - $97 is scratchpad
    CLR $0095
    CLR $0096
    CLR $0097
    LDX #$FD87    ; Lookup table pointer
    ROR $0093    ; Roll byte into position

; Evaluate Last / Previous Last Si bytes based on bit 7 (as sign bit)
nmi_05
    ROL $0093
    TAB
    EOR B $93
    BPL nmi_08    ; we only care about low / high transitions

; Process 'I' value
    LDA B $00,X
    TST A
    BPL nmi_06
    NEG B
nmi_06
    ADD B $94
    STA B $94

; Process 'Q' value
    LDA B $02,X
    TST A
    BPL nmi_07
    NEG B
nmi_07
    ADD B $95
    STA B $95

; Process 'I2' value
    LDA B $96
    ADD B $0A,X
    STA B $96

; Process 'Q2' value
    LDA B $97
    ADD B $0B,X
    STA B $97

nmi_08
    LDA A $93    ; Reload Last Si byte for next pass
    DEX
    CPX #$FD7F    ; Have we reached the last 45 degree interval?
    BNE nmi_05
    BRA nmi08a

; Table: I / Q lookup
byte    20 17 00 E9 E0 E9 00 17 20 17
; Table: I2 / Q2 lookup
byte    01 00 FF 00 01 00 FF 00 01

; save values after table lookup
nmi08a
    STA A $84    ; New Previous Last Si byte value
    LDA A $94
    STA A $85    ; Ii value
    LDA A $95
    STA A $86    ; Qi value
    LDA A $96
    STA A $87    ; I2i value
    LDA A $97
    STA A $88    ; Q2i value

; Integrator - calculate I2avg, reduce to 15/16 previous
    LDA A $89
    LDA B $8B
    ASR A    ; simulate 'ASR D' operation x 4
    ROR B
    ASR A
    ROR B
    ASR A
    ROR B
    ASR A
    ROR B
    STA D $93    ; Store 1/16 value
    LDA A $89    ; Reload I2avg value
    LDA B $8B
    SUB D $93    ; Subtraction
    ADD A $87    ; Add I2i value
    STA A $89    ; Store updated I2avg value
    STA B $8B

; Integrator - calculate Q2avg, reduce to 15/16 previous
    LDA A $8A
    LDA B $8C
    ASR A     ; Simulate 'ASR D' operation x 4
    ROR B
    ASR A
    ROR B
    ASR A
    ROR B
    ASR A
    ROR B
    STA D $93    ; Store 1/16 value
    LDA A $8A    ; Reload Q2avg value
    LDA B $8C
    SUB D $93    ; Subtraction
    ADD A $88    ; Add Q2i value
    STA A $8A    ; Store updated Q2avg value
    STA B $8C


    TST $0089
    BPL nmi_09
    TAB
    EOR B $8D
    BPL nmi_09
    COM $0090
nmi_09
    STA A $8D
    LDA A $89
    BPL nmi_10
    NEG A
nmi_10
    LDA B $8A
    BPL nmi_11
    NEG B
nmi_11
    CBA
    BPL nmi_12
    ASR A
    BRA nmi_14
nmi_12
    ASR B
nmi_14
    ABA
    STA A $91    ; value stored for debugging only
    CMP A #$09    ; has carrier detect threshold been met?
    BPL nmi_15
    JMP nmi_29
nmi_15
    LDA A $89
    LDA B $8A
    CBA
    BPL nmi_16
    NEG A
    CBA
    BMI nmi_17
    TST B
    BPL nmi_19
    BRA nmi_18
nmi_16
    NEG B
    CBA
    BMI nmi_20
    STA A $8E
    NEG B
    ASR B
    STA B $8F
    BRA nmi_21
nmi_17
    ASR A
    NEG A
    PSH A
    ABA
    STA A $8E
    PUL A
    SBA
    NEG A
    STA A $8F
    BRA nmi_21
nmi_18
    NEG A
    NEG B
nmi_19
    STA A $8F
    ASR B
    STA B $8E
    BRA nmi_21
nmi_20
    ASR A
    PSH A
    ABA
    STA A $8E
    PUL A
    SBA
    STA A $8F

; Vector calculation
nmi_21
    LDA A $85
    LDA B $8E
    STA A $93
    STA B $94
    EOR A $94
    STA A $94
    LDA A $93
    BPL nmi_22
    NEG A
nmi_22
    TST B
    BPL nmi_23
    NEG B
nmi_23
    MUL
    ASL D
    ASL D
    TST $0094
    BPL nmi_24
    NEG A

nmi_24
    STA A $92

; Vector calculation 2
    LDA A $86
    LDA B $8F
    STA A $93
    STA B $94
    EOR A $94
    STA A $94
    LDA A $93
    BPL nmi_25
    NEG A
nmi_25
    TST B
    BPL nmi_26
    NEG B
nmi_26
    MUL
    ASL D
    ASL D
    TST $0094
    BPL nmi_27
    NEG A

nmi_27
    ADD A $92
    TST $0090
    BPL nmi_28
    COM A
nmi_28
    STA A $92
    INC $00CC
    BRA nmi_30
nmi_29
    CLR $0092
nmi_30
    TST $00CC
    BNE nmi_33
nmi_31
    CLR $00CE
    LDA A #$05
    STA A $D4
    CLR $00CB
    CLR $00DA
    LDX #$0008
nmi_32
    CLR $97,X
    DEX
    BNE nmi_32
    RTI

nmi_33
    LDA A #$00
    LDA B $92
    ASR B
    ABA
    STA A $CD    ; value stored for debugging only
    TST $00D4
    BEQ nmi_35
nmi_34
    TAB
    EOR B $92
    BPL nmi_35
    COM $0090
nmi_35
    LDA B #$02
    CMP B $CC
    BEQ nmi_36
    BPL nmi_37
    ASR A
nmi_36
    ASR A
nmi_37
    CLR $00CC
    LDX $D6
    ASR A
    STA A $00,X
    LDA A #$00
    LDX #$009B    ; reset table pointer
nmi_38
    ADD A $00,X
    DEX
    CPX #$0097    ; have we reached the end of the table?
    BNE nmi_38
    STA A $DC
    TST A
    BPL nmi_39
    NEG A
nmi_39
    LDX $D6
    ASR A
    ADD A $04,X
    STA A $04,X
    DEX
    CPX #$0097    ; have we reached the end of the table?
    BNE nmi_40
    LDX #$009B    ; reset table pointer
nmi_40
    STX $D6
    INC $00D5
    TST $00CB
    BMI nmi_41
    CMP A $CB
    BMI nmi_41
    STA A $CB
    CLR $00D5
nmi_41
    LDA A #$03
    AND A $D5
    BNE nmi_42
    LDA A $DC
    STA A $DA
nmi_42
    RTI

nmi_43
    DEC $00B7
    BEQ nmi_45
    BPL nmi_44
    JMP nmi_56
nmi_44
    RTI

nmi_45
    INC $00B7
    DEC $00B6
    BEQ nmi_54
    LDA A $B6
    LDA B $BB
    CMP B #$01
    BEQ nmi_53
    CMP A #$34
    BNE nmi_47
nmi_46
    CLR $0080
nmi_47
    CMP A #$06
    BNE nmi_48
    LDA A $80
    ASL A
    ASL A
    ADD A $A0
    STA A $A0
nmi_48
    LDX $0C00
nmi_49
    ROL $9F,X
    DEX
    BNE nmi_49
    ROL A

; BCH checksum
    ASL $0080
    PSH A
    BIT A #$01
    BEQ nmi_50
    LDA A $80
    BIT A #$40
    BNE nmi_52
    BRA nmi_51
nmi_50
    LDA A $80
    BIT A #$40
    BEQ nmi_52
nmi_51
    EOR A #$03
nmi_52
    AND A #$3F
    STA A $80
    PUL A

; Transmit bit through PIA Port A
    AND A #$01    ; clear AccA except bit 0
    ASL A        ; move bit into place for transmission
    ADD A #$04    ; set bit 2 (transmit relay 'on')
    LDA B $0C    ; read PIA Port A
    AND B #$F9    ; clear bits 1 / 2
    ABA            ; add new bits 1 / 2
    STA A $0C    ; write PIA Port A
    RTI

nmi_53
    CMP A #$1E
    BNE nmi_47
    CLR $0080
    JMP nmi_47
nmi_54
    DEC $00B8
    BEQ nmi_55
    LDA A #$34
    STA A $B6
    BRA nmi_46

; End of transmission cleanup
nmi_55
    LDA A $0C    ; read PIA Port A
    AND A #$F9    ; clear bits 1 / 2 (end transmission)
    STA A $0C    ; write PIA Port A
    DEC $00B7
    INC $0C02
    CLR $00CE
    LDA A #$05    ; reset data bit counter
    STA A $D4
    RTI

nmi_56
    INC $00B7
    DEC $00D4
    BEQ nmi_60
    BMI nmi_57
    LDA B #$00
    LDA A #$01
    CMP A $D4
    BNE nmi_59
    COM A
    STA A $CB
    BRA nmi_59
nmi_57
    INC $00D4
    LDA B $DA
    TST $00CA
    BMI nmi_58
    COM B
nmi_58
    LDA A #$FF
    STA A $CE

; This section is not needed (PA3 is unused)
; Need to see how much can be cleaned out
nmi_59
    ROR B    ; move bit 7 to bit 3 (not used)
    ROR B
    ROR B
    ROR B
    AND B #$08
    LDA A $0C    ; PIA Port A
    AND A #$F7    ; clear bit 3 (not used)
    ABA            ; add in new bit 3
    STA A $0C    ; write to PIA Port A
    BRA nmi_61

nmi_60
    INC $00D4
    LDA A $DA
    TAB
    EOR A $DB
    BMI nmi_61
    STA B $CA
    DEC $00D4
nmi_61
    LDA A $DA
    STA A $DB
    LDA B $CE
    TST $00CA
    BMI nmi_62
    COM A
nmi_62
    LDX $0C00
    TST B
    BMI nmi_64
    CLR $0C02
    LDA A $B8
    STA A $D1
    CLR $0080
    CLR $00B6
nmi_63
    JMP nmi_04
nmi_64
    TST $0C02
    BNE nmi_63
    ROL A
nmi_65
    ROL $9F,X
    DEX
    BNE nmi_65
    INC $00B6
    LDX $0C00
    LDA A $9F,X
    LDA B $B6

; BCH checksum
    ASL $0080
    PSH A
    BIT A #$01
    BEQ nmi_66
    LDA A $80
    BIT A #$40
    BNE nmi_68
    BRA nmi_67
nmi_66
    LDA A $80
    BIT A #$40
    BEQ nmi_68
nmi_67
    EOR A #$03
nmi_68
    AND A #$3F
    STA A $80
    PUL A

    CMP B #$34
    BLT nmi_70
    DEC $00D1
    BNE nmi_70
    LDA B #$01
    STA B $0C02
nmi_70
    JMP nmi_04
Moderator edit: Code inserted in code box using [code]...text...[/code]
 

Attachments

MrChips

Joined Oct 2, 2009
30,802
This is a good example of poor programming style.

1) Do not use absolute hex addresses. Use a labelled name instead, one that the reader can understand.
2) Use comments, not to explain what the line of code does but what is the purpose of a block of code.
3) State code specifications, i.e. what are the inputs, what are the outputs.
4) Address such as nmi_18 conveys no meaning to the reader. It should be something such as "negate_D".
5) Use hex values only for bit masking operations, for example. Use decimal values when referring to a number value, for example,
Instead of
LDA A #$0A
use
LDA A #10

Why is the code using NMI instead of normal IRQ?

Bottom line: Make your code readable and easy to understand by you and any other person 25 years from now.
 

Thread Starter

metermannd

Joined Oct 25, 2020
343
Unfortunately I don't have a proper assembler as I'm on a M1 Mac (I upgraded too soon and don't currently have a viable option for running Windows).

Even this code is better than what I originally copied from the original unit's EPROM - I did a fair bit of cleanup to get it into this state.

I've never had to share code with others, so I don't know the rules of formatting source code.

As for why it's NMI versus IRQ, that's the way it was wired up in the original unit.

Nothing I do is ever good enough it seems.
 

MrChips

Joined Oct 2, 2009
30,802
Unfortunately I don't have a proper assembler as I'm on a M1 Mac (I upgraded too soon and don't currently have a viable option for running Windows).

Even this code is better than what I originally copied from the original unit's EPROM - I did a fair bit of cleanup to get it into this state.

I've never had to share code with others, so I don't know the rules of formatting source code.

As for why it's NMI versus IRQ, that's the way it was wired up in the original unit.

Nothing I do is ever good enough it seems.
Don't take it too hard, just that there's room for improvement.

I am going to assume that this is not your code. I will assume that it was reassembled from code sitting in the EPROM.
It would be worth your while to sit down and try to reverse engineer the code. Try to figure out what each SRAM data location is being used for and give it some reasonable label.
Do the same for subroutines/functions and give it a name.
Then do the same for branch locations and name them too.

If you need help to reverse engineer MC6800 code I could lend a hand, time allowing.
Also if you need to run code through an assembler, whether it be 6800/6805/6809/68000/HC11, we could likely find you an editor/assembler to work with. I have worked with ASM for all of the above.
 

Thread Starter

metermannd

Joined Oct 25, 2020
343
Correct that it started as an eprom dump that was run through a disassembler (f9dasm to be precise), cleaned up for formatting, then run through with a fine-tooth comb to clean it up (trust me, the original version is horrid).

I did figure out a good number of these locations (and have them documented in a separate sheet), I just haven't defined suitable 'source' mnemonics for these addresses... yet.

This is the only section of code that I really need help with - most of the remaining code I've figured out (and have optimized already) or at least have a fairly good sense of how it flows. Those sections I don't quite get should start falling into place once I have this straightened out.

For the purposes of analysis, this would be HC11 code (the original MCU was an automotive variant of the 6801).
 

Thread Starter

metermannd

Joined Oct 25, 2020
343
Incidentally, I do need to thank you for the push to determine labels for the various locations.

Looking at that code in relation to a few other sections gave me a few much-needed insights that helped me determine the function of most every RAM location in the code, save for most of the interrupt section and one other section I removed.

I'm now going back and applying those labels to a fresh dump of the EPROM to make into a proper source dump.
 
Top