PART II -- INTEL 8035 FIRMWARE DISASSEMBLY
Sega SP-400 Plotter / Printer — part of the SE3K documentation library, also readable inside the emulator.
PART II -- INTEL 8035 FIRMWARE DISASSEMBLY ------------------------------------------------------------------------------ File : sp400_8035.bin Chip : Intel 8035 (MCS-48 family, no internal ROM) Device : SEGA SP-400 -- serial front-end controller ROM size : 4096 bytes (4 KB, upper half is mirror of lower) Assembler : MCS-48 / Intel 8035 mnemonics ------------------------------------------------------------------------------ ; ════════════════════════════════════════════════════════════════════════════ ; FILE : sp400_8035.bin ; CPU : Intel 8035 (MCS-48 family — external ROM variant of the 8048) ; ROM : 4 096 bytes; second half (0x800–0xFFF) is an exact mirror ; of the first half — only the first 2 KB is listed below. ; ; ── 8035 Architecture Quick Reference ─────────────────────────────────────── ; • 8-bit accumulator (A) ; • 8 working registers R0–R7 in two banks (RB0 / RB1) ; • 64 bytes internal RAM; 256 bytes data-memory via MOVX ; • 12-bit program counter (4 KB address space) ; • 1 × 8-bit programmable timer/event counter ; • Two 8-bit I/O ports (P1, P2) + an 8-bit bidirectional BUS port ; • Two testable input pins T0, T1 ; • Interrupt vector: external INT → $003, timer → $007 ; ; ── Special Address Map ────────────────────────────────────────────────────── ; $000 Reset / power-on entry point ; $003 External interrupt vector (unused in this ROM) ; $007 Timer interrupt vector (unused in this ROM) ; $100 Main program (reached via JMP at $000) ; ; ── P2 Pin Assignment (inferred) ───────────────────────────────────────────── ; P2.5 (bit mask 0x20) – CLOCK output (driven LOW / HIGH by firmware) ; P2.6 (bit mask 0x40) – DATA_IN from host (polled for serial receive) ; ; ── Firmware Summary ───────────────────────────────────────────────────────── ; This firmware implements a bit-banged serial receiver. ; It reads a stream of bytes from P2.6 (DATA_IN), building each byte ; MSB-first by sampling the line 8 times (with inter-bit delays) and ; rotating each bit into R5 via the carry flag. After all 37 (0x25) bytes ; have been collected they are stored inverted (XOR 0xFF) in internal RAM ; starting at 0x10. The CLOCK line (P2.5) is toggled to produce ACK pulses. ; Three small busy-wait subroutines ($19E, $1A6, $1AD) provide timing. ; ════════════════════════════════════════════════════════════════════════════ ; ════════════════════════════════════════════════════════════════ ; RESET VECTOR ($000) ; The 8035 starts execution here on power-on / reset. ; The entire first page ($000–$0FF) is otherwise blank, so the ; firmware immediately jumps to the actual code at $100. ; ════════════════════════════════════════════════════════════════ $000: 24 00 JMP $100 ; reset vector → jump to main code ; ──────────────────────────────────────────────────────────── ; $002: [0xFF × 254] (blank / erased ROM) ; ──────────────────────────────────────────────────────────── ; ════════════════════════════════════════════════════════════════ ; INITIALISATION ($100–$105) ; ; Set up the three key variables: ; R2 = 0x00 – byte counter (counts received bytes, 0..37) ; R0 = 0x10 – RAM write pointer (starts at internal RAM 0x10) ; R1 = 0x10 – secondary pointer / bit-shift counter ; ════════════════════════════════════════════════════════════════ $100: BA 00 MOV R2,#00H ; init: byte counter = 0 $102: B8 10 MOV R0,#10H ; init: RAM write ptr = 0x10 $104: B9 10 MOV R1,#10H ; init: R1 (shift counter) = 0x10 ; ════════════════════════════════════════════════════════════════ ; SERIAL SYNC LOOP ($106–$129) ; ; This block implements the synchronisation phase of a bit-banged ; serial receive protocol on port P2. ; ; P2 pin map (inferred from masks): ; P2.5 (mask 0x20) – CLOCK line (output, driven by this CPU) ; P2.6 (mask 0x40) – DATA_IN / READY line (input from host) ; P2.3 (mask 0x08) – used to detect a new frame start in R1 ; ; Protocol flow: ; 1. Pull CLOCK low (ANL P2, #DFH → clear bit 5). ; 2. Poll DATA_IN (P2.6); if high → abort to error handler $12B. ; 3. If this is the very first byte (R2==0), wait until ; DATA_IN is low (start-bit / SYNC detection). ; 4. Load R1 register value and check its bit 3. ; Bit3=1 → R1 is already set; Bit3=0 → re-initialise R1=0x10. ; 5. Read DATA_IN again (abort if high) then output R1 to P1 ; (sends the current bit-shift value to the display / latch). ; 6. Drive CLOCK low then high (ANL→ORL on P2.6) → one clock pulse. ; 7. Decrement R2 via DJNZ R2 and repeat from step 4 until ; R2 reaches 0, then fall through to read the next byte group. ; ════════════════════════════════════════════════════════════════ $106: 9A DF ANL P2,#DFH ; pull P2.5 low → CLOCK line LOW $108: 0A IN A,P2 ; sample port P2 $109: 92 2B JB6 $12B ; if P2.6 (DATA_IN) is HIGH → abort/error $10B: FA MOV A,R2 ; check byte counter $10C: C6 06 JNZ $106 ; if not first byte, loop back and wait $10E: 0A IN A,P2 ; sample P2 again $10F: F2 08 JB5 $108 ; wait while P2.5 is HIGH (sync: wait for start bit) $111: 0A IN A,P2 ; sample P2 $112: 92 2B JB6 $12B ; DATA_IN high again → abort $114: F9 MOV A,R1 ; check current R1 shift value $115: D2 19 JB3 $119 ; if R1.bit3 set, pointer still valid; skip re-init $117: 24 1B JMP $11B ; jump over re-init $119: B9 10 MOV R1,#10H ; reset R1 to 0x10 $11B: 0A IN A,P2 ; sample P2 $11C: 92 2B JB6 $12B ; DATA_IN high → abort $11E: F1 MOV A,@R1 ; load byte at address R1 (look-up / shift reg read) $11F: 39 OUTL P1,A ; send current shift value out on P1 (data latch / display) $120: 0A IN A,P2 ; sample P2 $121: 92 2B JB6 $12B ; DATA_IN high → abort $123: 9A BF ANL P2,#BFH ; clear P2.6 (clock low before pulse) $125: 8A 40 ORL P2,#40H ; set P2.6 high → rising edge = one clock pulse to latch $127: CA 19 DJNZ R2, $119 ; repeat R2 times (shift-out loop) $129: 24 08 JMP $108 ; restart sync from CLOCK-low step ; ════════════════════════════════════════════════════════════════ ; MAIN RECEIVE LOOP ($12B–$198) ; ; Reads 8 bytes of serial data from P2.6 (DATA_IN), sampling each ; bit after a short inter-bit delay, and stores them in internal ; RAM starting at the address held in R0 (initially 0x10). ; ; For each bit of each byte: ; • IN A,P2 – sample the port ; • RLC A × 4 – shift bit 6 up into the carry flag ; (P2.6 is the data line; 4 left-rotates ; move bit 6 → bit 2 → ... → carry) ; • MOV A,R5 / RRC A / MOV R5,A ; – shift carry into R5 from the MSB side ; (builds the received byte MSB-first) ; • CALL $19E – inter-bit delay (~39 CPU cycles) ; ; After 8 bits (the 8 CALL $19E blocks $13C–$184): ; • ADD A,#DBH – equivalent to subtracting 0x25; tests ; whether byte count R2 has reached 0x25 ; (37 decimal); carry set → done ; • MOV @R0,A – write received byte to RAM[R0] ; • ORL P2,#20H – assert CLOCK high (ACK pulse to host) ; • INC R0 – advance RAM write pointer ; • JB3 $197 – if R0 bit3 still set, pointer is valid; ; otherwise reset R0 to 0x10 (wrap-around) ; ; Loop exit: when carry is set after ADD A,#DBH, jump to $199 ; (wait for host to de-assert DATA_IN then restart from $108). ; ════════════════════════════════════════════════════════════════ $12B: 34 AD CALL $1AD ; short delay before reading byte stream $12D: 0A IN A,P2 ; sample P2 $12E: 92 32 JB6 $132 ; DATA_IN high → skip to receive loop $130: 24 99 JMP $199 ; jump into receive loop $132: 34 A6 CALL $1A6 ; longer delay before first bit sample $134: 0A IN A,P2 ; sample P2 – bit 7 $135: F7 RLC A ; shift P2.6 toward carry: RLC ×4 $136: F7 RLC A ; ... $137: F7 RLC A ; ... $138: F7 RLC A ; .. carry now holds bit 7 $139: FD MOV A,R5 ; load accumulator for shift-in $13A: 67 RRC A ; rotate carry into MSB of R5 (build byte MSB-first) $13B: AD MOV R5,A ; save shifted byte $13C: 34 9E CALL $19E ; inter-bit delay $13E: 0A IN A,P2 ; sample P2 – bit 6 $13F: F7 RLC A ; shift P2.6 → carry ×4 $140: F7 RLC A $141: F7 RLC A $142: F7 RLC A $143: FD MOV A,R5 ; load partial byte $144: 67 RRC A ; shift carry into R5 $145: AD MOV R5,A ; save $146: 00 NOP ; padding NOP $147: 34 9E CALL $19E ; inter-bit delay $149: 0A IN A,P2 ; sample P2 – bit 5 $14A: F7 RLC A $14B: F7 RLC A $14C: F7 RLC A $14D: F7 RLC A $14E: FD MOV A,R5 ; load partial byte $14F: 67 RRC A ; shift carry into R5 $150: AD MOV R5,A ; save $151: 34 9E CALL $19E ; inter-bit delay $153: 0A IN A,P2 ; sample P2 – bit 4 $154: F7 RLC A $155: F7 RLC A $156: F7 RLC A $157: F7 RLC A $158: FD MOV A,R5 ; load partial byte $159: 67 RRC A ; shift carry into R5 $15A: AD MOV R5,A ; save $15B: 00 NOP ; padding NOP $15C: 34 9E CALL $19E ; inter-bit delay $15E: 0A IN A,P2 ; sample P2 – bit 3 $15F: F7 RLC A $160: F7 RLC A $161: F7 RLC A $162: F7 RLC A $163: FD MOV A,R5 ; load partial byte $164: 67 RRC A ; shift carry into R5 $165: AD MOV R5,A ; save $166: 34 9E CALL $19E ; inter-bit delay $168: 0A IN A,P2 ; sample P2 – bit 2 $169: F7 RLC A $16A: F7 RLC A $16B: F7 RLC A $16C: F7 RLC A $16D: FD MOV A,R5 ; load partial byte $16E: 67 RRC A ; shift carry into R5 $16F: AD MOV R5,A ; save $170: 00 NOP ; padding NOP $171: 34 9E CALL $19E ; inter-bit delay $173: 0A IN A,P2 ; sample P2 – bit 1 $174: F7 RLC A $175: F7 RLC A $176: F7 RLC A $177: F7 RLC A $178: FD MOV A,R5 ; load partial byte $179: 67 RRC A ; shift carry into R5 $17A: AD MOV R5,A ; save $17B: 34 9E CALL $19E ; inter-bit delay $17D: 0A IN A,P2 ; sample P2 – bit 0 $17E: F7 RLC A $17F: F7 RLC A $180: F7 RLC A $181: F7 RLC A $182: FD MOV A,R5 ; load partial byte $183: 67 RRC A ; shift carry into R5 → byte complete $184: AD MOV R5,A ; save completed byte $185: FA MOV A,R2 ; load byte counter $186: 03 DB ADD A,#DBH ; add 0xDB = subtract 0x25 (check if 37 bytes received) $188: F6 99 JC $199 ; carry set → all bytes done → end-of-transfer wait $18A: 1A INC R2 ; advance byte counter $18B: FD MOV A,R5 ; load completed byte for XOR $18C: D3 FF XRL A,#FFH ; XOR with 0xFF → invert all bits (data transformation) $18E: A0 MOV @R0,A ; store inverted byte to RAM at R0 $18F: 8A 20 ORL P2,#20H ; assert P2.5 (CLOCK/ACK) high → ACK pulse to host $191: 18 INC R0 ; advance RAM write pointer $192: F8 MOV A,R0 ; test pointer R0.bit3 $193: D2 97 JB3 $197 ; bit3 set → pointer still in valid range $195: 24 99 JMP $199 ; jump to end-of-transfer wait (buffer full) $197: B8 10 MOV R0,#10H ; reset pointer to 0x10 (wrap RAM buffer) ; ════════════════════════════════════════════════════════════════ ; END-OF-TRANSFER WAIT ($199–$19C) ; ; After all bytes are received (or on any abort), wait here until ; the host de-asserts DATA_IN (P2.6 goes LOW), then restart the ; whole receive sequence from $108. ; ════════════════════════════════════════════════════════════════ $199: 0A IN A,P2 ; sample P2 $19A: 92 99 JB6 $199 ; spin until DATA_IN goes LOW (host de-asserts) $19C: 24 08 JMP $108 ; restart serial receive from step 2 ; ════════════════════════════════════════════════════════════════ ; SUBROUTINE: DELAY_MEDIUM ($19E–$1A5) ; ; Busy-wait loop. Counts down from 13 (0x0D) to 0 using DEC A. ; Each loop iteration: MOV A,#0DH (1 cy) + DEC A (1 cy) + JNZ (2 cy) ; Total: 1 + 13 × 3 = ~40 machine cycles. ; Used as the inter-bit sampling delay in the serial receive loop. ; ════════════════════════════════════════════════════════════════ $19E: 23 0D MOV A,#0DH ; load counter = 13 $1A0: 07 DEC A ; decrement counter $1A1: 96 A0 JNZ $1A0 ; loop until zero $1A3: 00 NOP ; NOP (delay padding) $1A4: 00 NOP ; NOP (delay padding) $1A5: 83 RET ; return to caller ; ════════════════════════════════════════════════════════════════ ; SUBROUTINE: DELAY_LONG ($1A6–$1AC) ; ; Same structure; counts from 15 (0x0F). ; Total: ~46 machine cycles. ; Called once before reading the data byte stream. ; ════════════════════════════════════════════════════════════════ $1A6: 23 0F MOV A,#0FH ; load counter = 15 $1A8: 07 DEC A ; decrement counter $1A9: 96 A8 JNZ $1A8 ; loop until zero $1AB: 00 NOP ; NOP (delay padding) $1AC: 83 RET ; return to caller ; ════════════════════════════════════════════════════════════════ ; SUBROUTINE: DELAY_SHORT ($1AD–$1B2) ; ; Counts from 7 (0x07). ; Total: ~22 machine cycles. ; Called before the main data receive phase. ; ════════════════════════════════════════════════════════════════ $1AD: 23 07 MOV A,#07H ; load counter = 7 $1AF: 07 DEC A ; decrement counter $1B0: 96 AF JNZ $1AF ; loop until zero $1B2: 83 RET ; return to caller ; ──────────────────────────────────────────────────────────── ; $1B3: [0xFF × 1613] (blank / erased ROM) ; ──────────────────────────────────────────────────────────── ==============================================================================
HITACHI HD6805V1 ROM DISASSEMBLY
HITACHI HD6805V1 ROM DISASSEMBLY ==============================================================================