Close

20241106 -- ROM 4

A project log for ROM Disassembly - Cefucom-21

Peering into the soul of this obscure machine

ziggurat29ziggurat29 3 days ago0 Comments

I spent yesterday disassembling ROM 4 of the MCU2 board.  I got to 100% code coverage of that one.  That doesn't mean 100% understanding, it just means all the jigsaw puzzle pieces are now on the table.

It was interesting.  There are a lot of table-dispatched functions.

Dispatch Magic

I found some code which seems to be in a spin-wait for something to come into 8000h.

7874  loc_7874:
7874 E1           pop     hl              ; discard the return address
7875 ED 73 CE FF  ld      (word_FFCE), sp ; XXX stores SP during some Cefucom ROM4 stuff
7879  loop_7879:
7879 CD B9 78     call    sub_78B9        ; XXX some stuff with keys (as in buttons)
787C 2A C3 FF     ld      hl, (word_FFC3) ; XXX Cefu; a pointer into buffer @8000h
787F 11 00 80     ld      de, unk_8000
7882 B7           or      a
7883 ED 52        sbc     hl, de
7885 28 F2        jr      z, loop_7879    ; XXX nothing 'received'; spin
7887 21 00 00     ld      hl, 0
788A 22 C5 FF     ld      (word_FFC5), hl
788D CD E3 78     call    sub_78E3        ; XXX messes with DE, which will be a synthetic return address
7890 CD 7F 79     call    sub_797F
7893 01 99 78     ld      bc, sub_7899
7896 C5           push    bc              ; queue sub_7899 on the stack
7897 D5           push    de              ; queue the call sub_78E3 computed
7898 C9           ret                     ; (not really returning from here since we queued the above two)

And there is magicry at the end.

The code infers the availability of data by the difference between the start of buffer and end of buffer, so that end of buffer pointer must be atomically updated.  Cross referencing word_FFC5 I find that is indeed happening: 

7899  sub_7899:
7899 F3           di          ; critical section around these pointer updates
789A 2A C5 FF     ld  hl, (word_FFC5) ; XXX Cefu; an OFFSET into buffer @8000 while building
... move block into position at 8000h and computes end in DE and other stuff
78B2 ED 53 C3 FF  ld  (word_FFC3), de ; XXX Cefu; an end pointer into buffer @8000h
78B6 FB           ei          ; end critical section
...

so word_FFC5 seems to be used while transferring the block, and when it is completed then word_FFC3 is atomically updated with the final value.

The magicry at the end depends on sub_78E3 leaving a return address in DE, which eventually gets pushed to the stack prior to the ret, effectively synthesizing 'jp (de)'.

78E3   ; XXX lookup dispatch info
78E3   sub_78E3:
78E3 21 00 80     ld      hl, unk_8000
78E6 E5           push    hl
78E7 7E           ld      a, (hl)         ; get the code from buffer
78E8 ED 4B 1C 00  ld      bc, (off_1B+1)  ; XXX freaky as it is in the middle of a constant; val c7e0. bug?
78EC 21 0B 79     ld      hl, dispatchByCode_790B ; XXX dispatch 29 entries/116 by: (code, C, addr)
78EF   loop_78EF:
78EF ED A1        cpi
78F1 28 08        jr      z, leave_78FB   ; found it
78F3 E2 05 79     jp      po, loc_7905    ; finished; but not found
78F6 23           inc     hl              ; (HL already +1, so we only need +3 to get to next)
78F7 23           inc     hl
78F8 23           inc     hl
78F9 18 F4        jr      loop_78EF
78FB   leave_78FB:
78FB 4E           ld      c, (hl)
78FC 06 00        ld      b, 0
78FE 23           inc     hl
78FF 5E           ld      e, (hl)
7900 23           inc     hl
7901 56           ld      d, (hl)
7902 E1           pop     hl              ; (which will be 8000h)
7903 23           inc     hl
7904 C9           ret
7905   loc_7905:
7905 23           inc     hl
7906 23           inc     hl
7907 23           inc     hl
7908 23           inc     hl
7909 18 F0        jr      leave_78FB

The sub_78E3 basically looks up the servicing address from the code that is at the start of the block, and returns an additional associated parameter in C.  Here's the first entry: 

The sub_78E3 basically looks up the servicing address from the code that is at the start of the block, and returns an additional associated parameter in C.  Here's the first entry:

790B 21  dispatchByCode_790B:db 21h  ; code to match @8000h
790C 05     db 5          ; XXX goes in C
790D A6 79  dw sub_79A6   ; XXX goes in DE (and becomes a call address)
...

That table has 29 entries.

"State"

Rummaging through the references to PIO A code, there were sections like this:

7E96  sub_7E96:
7E96 3E 02     ld      a, 2
7E98 32 E4 FF  ld      (byte_FFE4), a
7E9B 3E 10     ld      a, 10h
7E9D D3 E0     out     (0E0h), a
7E9F 3A EA FF  ld      a, (byte_FFE8+2)
7EA2 D3 E2     out     (0E2h), a       ; PIO B data out
7EA4 C9        ret

Knowing that PIO A b2,1,0 are inputs, and that b5,4,3 are outputs, it occurred to me that those might be bitfields of a 3-bit number.  One expressed from MCU2 to PCU, and one expressed from PCU to MCU2.  I re-annotated that code throughout: 

7E96  sub_7E96:
7E96 3E 02     ld      a, 2        ; transition state 2
7E98 32 E4 FF  ld      (byte_FFE4), a  ; XXX PIO A data related; dispatch index
7E9B 3E 10     ld      a, 10h
7E9D D3 E0     out     (0E0h), a       ; PIO A set b5 low, b4 high, b3 low (send 2)
7E9F 3A EA FF  ld      a, (byte_FFE8+2)
7EA2 D3 E2     out     (0E2h), a       ; PIO B data out
7EA4 C9        ret

Things start to make a little more sense in that context.  So in sum PIO Port A is structured this way:

PIO A:
b7 - /FS from VDG
b6 - x (unused)
b5 - \
b4 -  +=> "MCU2 State (to ioboard)"
b3 - /
b2 - \
b1 -  +=< "PCU State (to main board)"
b0 - /

Port B has no bit structure, and seems to be used for bulk data transport between the boards. 

Because there is no 'strobe' between the boards to notify of state change, I am suspect that happens as a consequence of data being available on B.

Interboard Communications

Reviewing the ISR for Port B:

7E1C  isrPIOb_7E1C:
7E1C FB        ei              ; allow nesting this interrupt
7E1D F5        push    af
7E1E C5        push    bc
7E1F D5        push    de
7E20 E5        push    hl
7E21 CD 2F 7E  call    sub_7E2F
7E24 3E 0A     ld      a, 10
7E26 32 E3 FF  ld      (byte_FFE3), a
7E29 E1        pop     hl
7E2A D1        pop     de
7E2B C1        pop     bc
7E2C F1        pop     af
7E2D ED 4D     reti

There is a constant whacking of byte_FFE3 to the value of 10.  Cross referencing that, it can be found in the ISR for Port A: 

7F95  isrPIOaHelper_7F95:
7F95 F5        push    af
7F96 E5        push    hl
7F97 21 E3 FF  ld      hl, byte_FFE3
7F9A 35        dec     (hl)
7F9B 20 1A     jr      nz, leave_7FB7
...

So Port B whacks it to 10 ever time a byte comes over, and Port A decrements it each time a systick comes in, doing different things based on whether is reaches zero or not.  So I surmise this is a 'receive data timeout'.  Since I know the systick is 60 Hz, this means a timeout of 167 ms. 

I elided the code above, but the short story is that 'if it times out, the system is returned to state 0'.  So state 0 seems to be the quiescent state.

I should also point out that in these state processing, that Port B is turned around several times.  So in both systems it is configured in 'input mode' but during the operation is it changed to output as well.

So sub_7E2F is probably the 'stow a byte and maybe advance the state machine' function.

After I went though all the dispatch tables (there's something like 12 of them), I am now at 100% disassembly coverage of ROM4.  I don't know what all these things do, but it's still a milestone because code and data are now separated, so cross referencing can happen.  On the other hand, this state table design will possibly require my building of another document to keep track of the system.

I did take a peek at [Nigel]'s instruction trace.  At this point it's not exciting news because it is simply stuck in the 'memory test failed' loop on the PCU board.  He had configured the emulator for less than 32 KiB RAM and so it was failing there.  Easily fixed, and he got to a prompt, but still not working.  No big surprise, still so much more to do.

OK, with this understanding of PIO A and B, I'm going back to PCU board.

Discussions