1
0

Compare commits

...

2 Commits

Author SHA1 Message Date
Aiden
a82f3f6628 Pin additons 2026-05-25 15:18:47 +10:00
Aiden
cdfb811c28 LCD decompile 2026-05-25 15:10:32 +10:00
16 changed files with 12332 additions and 67 deletions

View File

@@ -1,5 +1,7 @@
# H8/536 ROM Decompiler
The ROM used is from a SONY RCP-TX7 Camera Panel. Some of the code in this repo may be bias to the functions of that particular use case with the H8/536.
This repo now includes a standalone Python helper for the H8/536 ROM image:
```powershell
@@ -31,8 +33,11 @@ To turn the structured decompile output into conservative C-like pseudocode:
- Emits memory-region metadata for vector, DTC, RAM, register-field, and mode-dependent program/external space.
- Parses the DTC vector table described by the manual and decodes DTC register-information blocks.
- Tracks SCI setup writes and can infer baud rates from SMR/BRR when `--clock-hz` is supplied.
- Annotates SCI protocol actions such as TDRE waits, TDR writes, RDR reads, RX/TX interrupt enables, and receive-error clears.
- Adds a Sony RCP-TX7 board profile that ties H8/536 pin 66 `P95/TXD` and pin 67 `P96/RXD` to the MAX202 RS232 transceiver.
- Flags/manual-annotates TEMP-register access ordering for FRT and A/D 16-bit peripheral registers.
- Scans unreached ROM ranges for ASCII strings and pointer-table candidates.
- Scans likely LCD/menu text records, groups display-text regions, and reports literal/near matches for terms such as `CONNECT`.
- Emits function summaries and a direct-call graph in JSON, with optional Graphviz DOT output.
- Tracks conservative per-basic-block register/control-register dataflow in JSON and comments known value changes.
- Discovers RAM/external/global symbols from memory references and pointer tables, including read/write counts and xrefs.
@@ -40,6 +45,7 @@ To turn the structured decompile output into conservative C-like pseudocode:
- Adds Appendix A cycle estimates to JSON and can append them to ASM comments.
- Summarizes straight-line block timing and backward-branch loop timing when requested.
- Handles the E-clock transfer instructions `MOVFPE` and `MOVTPE`.
- Recognizes likely LCD E-clock access routines at `H'F200`/`H'F201`, including busy-flag polling and data/control writes.
- Generates a separate C-like pseudocode view from the JSON, preserving labels, calls, branches, register names, inferred symbols, metadata comments, optional cycle notes, and simple structured `if`/`do while` patterns.
The generated listing is written to:
@@ -66,6 +72,7 @@ python h8536_decompiler.py --help
- `--start H'1000 --end H'D100`: constrain the decode range.
- `--br H'FE`: resolve short absolute `@aa:8` operands through a known base-register value.
- `--clock-hz 16000000`: infer SCI baud rates from manual BRR formulas.
- `--board-profile sony_rcp_tx7|none`: include or suppress known board-trace annotations.
- `--cycles`: append Appendix A cycle estimates to assembly comments.
- `--timing`: include straight-line block and backward-branch loop timing summaries.
- `--callgraph-dot build\callgraph.dot`: write a Graphviz DOT call graph.
@@ -97,8 +104,12 @@ python h8536_pseudocode.py --help
- `h8536/dataflow.py`: conservative register/control-register value tracking.
- `h8536/symbols.py`: RAM/external/global symbol discovery from references and data tables.
- `h8536/indirect.py`: indirect call/jump and pointer-table dispatch hints.
- `h8536/lcd_text.py`: LCD/menu text record scanning, fuzzy search, and text xrefs.
- `h8536/lcd_driver.py`: LCD E-clock access and busy-poll recognizer.
- `h8536/timing.py`: block and loop cycle summaries.
- `h8536/sci.py`: SCI setup tracking and baud inference.
- `h8536/sci_protocol.py`: SCI transmit/receive/status semantic annotations.
- `h8536/board_profile.py`: Sony RCP-TX7 board-trace annotations, including the MAX202 RS232 path.
- `h8536/peripheral_access.py`: FRT/A-D TEMP-register access analysis.
- `h8536/pseudocode.py`: JSON-to-C-like pseudocode generation.
- `h8536/render.py`: assembly and JSON output.

View File

@@ -10,6 +10,7 @@
; - The register field is H'FE80-H'FFFF; names below come from appendix B.
; - @aa:8 short absolute operands use BR as the upper address byte.
; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.
; - LCD inference treats E-clock H'F200/H'F201 accesses as status/control and data candidates.
; - Pass --clock-hz to convert SCI BRR settings into numeric baud rates.
; - Cycle counts use Appendix A tables A-7/A-8 for on-chip access with no external wait states.
@@ -223,6 +224,88 @@
; mem_F10E H'F10E program_or_external memory r=0 w=1 width=byte
; ... 206 more symbols omitted from listing header
; Board Profile
; Board trace ties the H8/536 SCI1 pins to a MAX202 RS232 transceiver.
; H8 pin 66 P95/TXD (TXD) -> MAX202 pin 11
; H8 pin 67 P96/RXD (RXD) -> MAX202 pin 12
; SCI2 pin routing is disabled by SYSCR2.P9SCI2E=0 in the observed setup.
; LCD/Text Scan
; search 'CONNECT': not literal, hits=0
; near: H'A025 'COMPLETED', H'8E79 'ON CONT1 OFF~X', H'8F55 'ON CONT2 OFF~X', H'94A9 'ON'
; LCD text regions
; region H'63D7-H'6758 count=15 'OPERATION', 'PAINT', 'OPERATION', 'IRIS/M.BLK'
; region H'67E0-H'6831 count=2 'TLCS Xg', 'AGC GAIN AE Xh'
; region H'6A4F-H'6C47 count=8 'AUTO FUNC XjO', 'A.IRIS MODE Xj', 'AI BACK.L~Xj', 'AUTO FUNC Xk='
; region H'6F84-H'6FC0 count=2 'OTHERS Xo', 'SHUTTER Xo'
; region H'7052-H'7477 count=15 'SET RCP', 'MASTER', 'OTHERS Xp', 'COPY TO SLAVES~Xp'
; region H'757A-H'7824 count=14 'BARS TYPE Xuz', 'SMPTE Xu', 'SPLIT Xu', 'FULLFIELD 75% Xu'
; region H'78B5-H'792F count=4 'OTHERS Xx', 'WHITE BLACK~Xx', 'COMM LINK ITEM-2Xx', 'FLARE Xy'
; region H'819C-H'87A9 count=28 'SHADING X', 'WHITE~X', 'SHADING AUTO SETX', 'BLACK~X'
; region H'883D-H'8959 count=7 'MATRIX X', 'STD FL~X', 'PRESET MATRIX X', 'H.SAT SPCL~X'
; region H'8A0C-H'8BAC count=7 'MATRIX X', 'ON SKIN OFF~X', 'SAT HUE X', 'MATRIX X'
; region H'8CB7-H'8CFD count=2 'FILTER X', '1 2 3 4 X'
; region H'8E57-H'8EA7 count=3 'LENS X', 'ON CONT1 OFF~X', 'FOCUS ZOOM X'
; ... 23 more LCD text regions
; LCD text candidates
; text H'41B0 len=35 medium '01020304050607080910111213141516X'
; text H'5B55 len=10 high '0123456789'
; text H'60F6 len=16 high '0123456789ABCDEF'
; text H'63D7 len=10 high 'OPERATION' xrefs=1
; text H'63F5 len=10 high 'PAINT' xrefs=1
; text H'6443 len=10 high 'OPERATION' xrefs=1
; text H'6461 len=10 high 'IRIS/M.BLK' xrefs=1
; text H'6490 len=10 high 'OPERATION' xrefs=1
; text H'64AE len=10 high 'LOCK' xrefs=1
; text H'652F len=19 high 'DYNA LATITUDE Xe/' xrefs=1
; text H'6551 len=18 medium 'HIGH LOW~XeP' xrefs=1
; text H'65C9 len=18 medium 'BLACK STR Xe' xrefs=1
; text H'6644 len=19 medium 'BLACK STR XfD' xrefs=1
; text H'6665 len=19 medium 'STRETCH LEVEL Xfe' xrefs=1
; text H'6683 len=18 high 'POINT1 POINT2Xf' xrefs=1
; text H'6706 len=18 medium 'BLACK STR Xg' xrefs=1
; text H'6727 len=19 medium "COMPRESS LEVEL Xg'" xrefs=1
; text H'6745 len=19 high 'POINT1 POINT2XgE' xrefs=1
; text H'67E0 len=18 medium 'TLCS Xg' xrefs=1
; text H'681F len=18 medium 'AGC GAIN AE Xh' xrefs=1
; text H'693B len=19 medium 'AUTO FUNC Xi;' xrefs=1
; text H'6A4F len=19 medium 'AUTO FUNC XjO' xrefs=1
; text H'6A8E len=18 medium 'A.IRIS MODE Xj' xrefs=1
; text H'6AAD len=17 medium 'AI BACK.L~Xj' xrefs=1
; text H'6B3D len=19 medium 'AUTO FUNC Xk=' xrefs=1
; text H'6B5E len=19 medium 'AUTO FOCUS Xk^' xrefs=1
; text H'6BEF len=18 medium 'DIAG Xk' xrefs=1
; text H'6C16 len=18 medium 'DIAG DATA Xl' xrefs=1
; text H'6C35 len=18 medium 'RESET REQ~Xl4' xrefs=1
; text H'6F84 len=18 medium 'OTHERS Xo' xrefs=1
; text H'6FAE len=18 medium 'SHUTTER Xo' xrefs=1
; text H'7052 len=14 medium 'SET RCP' xrefs=1
; text H'706F len=14 medium 'MASTER' xrefs=1
; text H'709F len=18 medium 'OTHERS Xp' xrefs=1
; text H'70C0 len=18 medium 'COPY TO SLAVES~Xp' xrefs=1
; text H'7144 len=19 medium 'CAM ID SET~XqD' xrefs=1
; text H'71C9 len=18 medium 'OTHERS Xq' xrefs=1
; text H'71F9 len=18 medium 'CAM ID IND Xq' xrefs=1
; text H'7213 len=18 medium 'TITLE IND Xr' xrefs=1
; text H'72A5 len=18 medium 'OTHERS Xr' xrefs=1
; text H'72C7 len=17 medium 'CAM BARS~Xr' xrefs=1
; text H'72E4 len=18 medium 'CLOCK IND Xr' xrefs=1
; text H'7369 len=19 medium 'OTHERS Xsi' xrefs=1
; text H'7393 len=18 high 'CENTER MARKER Xs' xrefs=1
; text H'7425 len=19 medium 'OTHERS Xt%' xrefs=1
; text H'7464 len=19 medium 'SAFETY ZONE Xtd' xrefs=1
; text H'757A len=19 medium 'BARS TYPE Xuz' xrefs=1
; text H'75A4 len=18 medium 'SMPTE Xu' xrefs=1
; ... 192 more LCD text candidates
; LCD Driver Candidates
; H'F200 lcd_status_control status/control register inferred from busy polling and command writes
; H'F201 lcd_data data register inferred from paired data reads/writes
; LCD routines
; routine H'3F40-H'3F74 lcd_wait_and_transfer lcd_command_or_address_write, lcd_data_read, lcd_data_write, lcd_status_read
; LCD busy loops
; loop H'3F4A->H'3F51 LCD busy-flag poll: read H'F200, test bit 7, branch until clear
; Timing Summary
; Straight-line blocks
; block H'1000-H'10CB vec_reset_1000 ins=42 cycles=371 unknown=0
@@ -322,7 +405,7 @@ vec_reset_1000:
1025: 15 FE FE 06 93 MOV:G.B #H'93, @P9DDR ; P9DDR = H'93; cycles=9
102A: 15 FE FF 06 00 MOV:G.B #H'00, @P9DR ; P9DR = H'00; cycles=9
102F: 15 FE FC 06 87 MOV:G.B #H'87, @SYSCR1 ; SYSCR1 = H'87 (IRQ1E=0 IRQ0E=0 NMIEG=0 BRLE=0; P12/P13 are I/O, IRQ0 disabled, IRQ1 disabled); cycles=9
1034: 15 FE FD 06 84 MOV:G.B #H'84, @SYSCR2 ; SYSCR2 = H'84 (IRQ5E=0 IRQ4E=0 IRQ3E=0 IRQ2E=0 P6PWME=1 P9PWME=0 P9SCI2E=0; enabled P6 PWM); cycles=9
1034: 15 FE FD 06 84 MOV:G.B #H'84, @SYSCR2 ; SYSCR2 = H'84 (IRQ5E=0 IRQ4E=0 IRQ3E=0 IRQ2E=0 P6PWME=1 P9PWME=0 P9SCI2E=0; enabled P6 PWM); SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; cycles=9
1039: 15 FE 90 06 02 MOV:G.B #H'02, @FRT1_TCR ; FRT1_TCR = H'02 (ICIE=0 OCIEB=0 OCIEA=0 OVIE=0 OEB=0 OEA=0 CKS1=1 CKS0=0); cycles=9
103E: 15 FE 91 06 01 MOV:G.B #H'01, @FRT1_TCSR ; FRT1_TCSR = H'01 (ICF=0 OCFB=0 OCFA=0 OVF=0 OLVLB=0 OLVLA=0 IEDG=0 CCLRA=1); cycles=9
1043: 1D FE 92 06 00 MOV:G.W #H'00, @FRT1_FRC_H ; FRT1_FRC_H = H'00; FRT1_FRC word write; TEMP byte-order hazard avoided; cycles=9
@@ -341,12 +424,12 @@ vec_reset_1000:
1086: 15 FE C5 06 FF MOV:G.B #H'FF, @PWM2_DTR ; PWM2_DTR = H'FF; cycles=9
108B: 15 FE C8 06 3B MOV:G.B #H'3B, @PWM3_TCR ; PWM3_TCR = H'3B (OE=0 OS=0 CKS2=0 CKS1=1 CKS0=1); cycles=9
1090: 15 FE C9 06 7D MOV:G.B #H'7D, @PWM3_DTR ; PWM3_DTR = H'7D; cycles=9
1095: 15 FE D8 06 24 MOV:G.B #H'24, @SCI1_SMR ; SCI1_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); cycles=9
109A: 15 FE DA 06 3C MOV:G.B #H'3C, @SCI1_SCR ; SCI1_SCR = H'3C (TIE=0 RIE=0 TE=1 RE=1 CKE1=0 CKE0=0; SCI enables TX,RX, internal clock); cycles=9
109F: 15 FE D9 06 07 MOV:G.B #H'07, @SCI1_BRR ; SCI1_BRR = H'07; SCI1 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; cycles=9
10A4: 15 FE F0 06 24 MOV:G.B #H'24, @SCI2_SMR ; SCI2_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); cycles=9
10A9: 15 FE F2 06 0C MOV:G.B #H'0C, @SCI2_SCR ; SCI2_SCR = H'0C (TIE=0 RIE=0 TE=0 RE=0 CKE1=0 CKE0=0; SCI enables none, internal clock); cycles=9
10AE: 15 FE F1 06 07 MOV:G.B #H'07, @SCI2_BRR ; SCI2_BRR = H'07; SCI2 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; cycles=9
1095: 15 FE D8 06 24 MOV:G.B #H'24, @SCI1_SMR ; SCI1_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); SCI1 SMR serial init for traced RS232/MAX202 path (H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD); cycles=9
109A: 15 FE DA 06 3C MOV:G.B #H'3C, @SCI1_SCR ; SCI1_SCR = H'3C (TIE=0 RIE=0 TE=1 RE=1 CKE1=0 CKE0=0; SCI enables TX,RX, internal clock); disable SCI1 TX interrupt (TIE); disable SCI1 receive and receive-error interrupts (RIE); enable SCI1 transmitter (TE); enable SCI1 receiver (RE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); cycles=9
109F: 15 FE D9 06 07 MOV:G.B #H'07, @SCI1_BRR ; SCI1_BRR = H'07; SCI1 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; SCI1 BRR serial init for traced RS232/MAX202 path (H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD); cycles=9
10A4: 15 FE F0 06 24 MOV:G.B #H'24, @SCI2_SMR ; SCI2_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); SCI2 SMR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; cycles=9
10A9: 15 FE F2 06 0C MOV:G.B #H'0C, @SCI2_SCR ; SCI2_SCR = H'0C (TIE=0 RIE=0 TE=0 RE=0 CKE1=0 CKE0=0; SCI enables none, internal clock); disable SCI2 TX interrupt (TIE); disable SCI2 receive and receive-error interrupts (RIE); disable SCI2 transmitter (TE); disable SCI2 receiver (RE); SCI2 SCR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; cycles=9
10AE: 15 FE F1 06 07 MOV:G.B #H'07, @SCI2_BRR ; SCI2_BRR = H'07; SCI2 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; SCI2 BRR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; cycles=9
10B3: 15 FE E8 06 19 MOV:G.B #H'19, @ADCSR ; ADCSR = H'19 (ADF=0 ADIE=0 ADST=0 SCAN=1 CKS=1 CH2=0 CH1=0 CH0=1; A/D halt, scan AN0-AN1, 138-state max, ADI disabled); cycles=9
10B8: 15 FE E9 06 7F MOV:G.B #H'7F, @H'FEE9 ; refs H'FEE9 in register_field; cycles=9
10BD: 15 FF 10 06 F0 MOV:G.B #H'F0, @WCR ; WCR = H'F0 (WMS1=0 WMS0=0 WC1=0 WC0=0; programmable wait, 0 waits); cycles=9
@@ -2135,23 +2218,23 @@ loc_3F40:
3F46: 0C 06 00 48 ORC.W #H'0600, SR ; cycles=4
loc_3F4A:
3F4A: 15 F2 00 00 80 MOVFPE.B @H'F200, R0 ; refs mem_F200 in program_or_external; cycles=13
3F4F: A0 F7 BTST.B #7, R0 ; cycles=2
3F51: 26 F7 BNE loc_3F4A ; cycles=3/8 nt/t
3F4A: 15 F2 00 00 80 MOVFPE.B @H'F200, R0 ; LCD status read from E-clock H'F200; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; refs mem_F200 in program_or_external; cycles=13
3F4F: A0 F7 BTST.B #7, R0 ; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; cycles=2
3F51: 26 F7 BNE loc_3F4A ; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; cycles=3/8 nt/t
3F53: AC F8 BTST.W #8, R4 ; cycles=3
3F55: 26 16 BNE loc_3F6D ; cycles=3/8 nt/t
3F57: AC F9 BTST.W #9, R4 ; cycles=3
3F59: 26 07 BNE loc_3F62 ; cycles=3/8 nt/t
3F5B: 15 F2 00 00 94 MOVTPE.B R4, @H'F200 ; refs mem_F200 in program_or_external; cycles=13
3F5B: 15 F2 00 00 94 MOVTPE.B R4, @H'F200 ; LCD command/address write to E-clock H'F200; refs mem_F200 in program_or_external; cycles=13
3F60: 20 10 BRA loc_3F72 ; cycles=7
loc_3F62:
3F62: 15 F2 01 00 94 MOVTPE.B R4, @H'F201 ; refs mem_F201 in program_or_external; cycles=13
3F62: 15 F2 01 00 94 MOVTPE.B R4, @H'F201 ; LCD data write to E-clock H'F201; refs mem_F201 in program_or_external; cycles=13
3F67: 1D FB 00 08 ADD:Q.W #1, @H'FB00 ; refs ram_FB00 in on_chip_ram; cycles=8
3F6B: 20 05 BRA loc_3F72 ; cycles=8
loc_3F6D:
3F6D: 15 F2 01 00 84 MOVFPE.B @H'F201, R4 ; refs mem_F201 in program_or_external; cycles=13
3F6D: 15 F2 01 00 84 MOVFPE.B @H'F201, R4 ; LCD data read from E-clock H'F201; refs mem_F201 in program_or_external; cycles=13
loc_3F72:
3F72: CF 88 LDC.W @R7+, SR ; cycles=7
@@ -2499,12 +2582,12 @@ loc_434C:
435B: 15 FF 03 06 00 MOV:G.B #H'00, @IPRD ; IPRD = H'00 (FRT3 priority=0; 8-bit timer priority=0); cycles=9
4360: 15 FF 04 06 50 MOV:G.B #H'50, @IPRE ; IPRE = H'50 (SCI1 priority=5; SCI2 priority=0); cycles=9
4365: 15 FF 05 06 40 MOV:G.B #H'40, @IPRF ; IPRF = H'40 (A/D priority=4); cycles=9
436A: 15 FE DA C6 BSET.B #6, @SCI1_SCR ; set RIE (bit 6) of SCI1_SCR; cycles=9
436A: 15 FE DA C6 BSET.B #6, @SCI1_SCR ; set RIE (bit 6) of SCI1_SCR; enable SCI1 receive and receive-error interrupts (RIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); cycles=9
436E: 15 FE 90 C5 BSET.B #5, @FRT1_TCR ; set OCIEA (bit 5) of FRT1_TCR; cycles=9
4372: 15 FE A0 C5 BSET.B #5, @FRT2_TCR ; set OCIEA (bit 5) of FRT2_TCR; cycles=9
4376: 15 FE E8 C6 BSET.B #6, @ADCSR ; set ADIE (bit 6) of ADCSR; cycles=9
437A: 15 FE FD C4 BSET.B #4, @SYSCR2 ; set IRQ3E (bit 4) of SYSCR2; cycles=9
437E: 15 FE FD C5 BSET.B #5, @SYSCR2 ; set IRQ4E (bit 5) of SYSCR2; cycles=9
437A: 15 FE FD C4 BSET.B #4, @SYSCR2 ; set IRQ3E (bit 4) of SYSCR2; SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; cycles=9
437E: 15 FE FD C5 BSET.B #5, @SYSCR2 ; set IRQ4E (bit 5) of SYSCR2; SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; cycles=9
4382: 15 FE 8E F6 BTST.B #6, @P7DR ; refs P7DR in register_field; cycles=7
4386: 27 06 BEQ loc_438E ; cycles=3/7 nt/t
4388: 1D FE EC 07 A5 3F MOV:G.W #H'A53F, @WDT_TCSR_R ; WDT_TCSR_R = H'A53F (OVF=0 WT/IT=0 TME=1 CKS2=1 CKS1=1 CKS0=1; TCSR password H'A5, WDT enabled, interval IRQ0, clock phi/4096); cycles=11
@@ -2948,13 +3031,13 @@ BA60: 15 F8 5C 60 XOR.B @H'F85C, R0 ; refs ram_F85C in on_chip_ram; cycles=7
BA64: 15 F8 5D 90 MOV:G.B R0, @H'F85D ; refs ram_F85D in on_chip_ram; cycles=7
loc_BA68:
BA68: 15 FE DC F7 BTST.B #7, @SCI1_SSR ; refs SCI1_SSR in register_field; cycles=7
BA6C: 27 FA BEQ loc_BA68 ; cycles=3/7 nt/t
BA68: 15 FE DC F7 BTST.B #7, @SCI1_SSR ; wait for SCI1 transmit data register empty (TDRE=1); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR in register_field; cycles=7
BA6C: 27 FA BEQ loc_BA68 ; repeat SCI1 transmit-empty wait while TDRE=0; cycles=3/7 nt/t
BA6E: 15 F8 58 80 MOV:G.B @H'F858, R0 ; refs ram_F858 in on_chip_ram; cycles=7
BA72: 15 FE DB 90 MOV:G.B R0, @SCI1_TDR ; SCI1_TDR; cycles=7
BA72: 15 FE DB 90 MOV:G.B R0, @SCI1_TDR ; SCI1_TDR; write RS232/SCI byte to SCI1 TDR for transmission; SCI1 TDR write transmits on traced RS232/MAX202 path: H8 pin 66 P95/TXD -> MAX202 pin 11; cycles=7
BA76: 15 F9 C2 06 01 MOV:G.B #H'01, @H'F9C2 ; refs ram_F9C2 in on_chip_ram; cycles=9
BA7B: 15 FE DC D7 BCLR.B #7, @SCI1_SSR ; clear TDRE (bit 7) of SCI1_SSR; cycles=8
BA7F: 15 FE DA C7 BSET.B #7, @SCI1_SCR ; set TIE (bit 7) of SCI1_SCR; cycles=8
BA7B: 15 FE DC D7 BCLR.B #7, @SCI1_SSR ; clear TDRE (bit 7) of SCI1_SSR; clear SCI1 transmit data register empty flag (TDRE); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
BA7F: 15 FE DA C7 BSET.B #7, @SCI1_SCR ; set TIE (bit 7) of SCI1_SCR; enable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); cycles=8
BA83: 19 RTS ; cycles=13
vec_sci1_txi_BA84:
@@ -2966,7 +3049,7 @@ BA90: 15 F9 C3 16 TST.B @H'F9C3 ; refs ram_F9C3 in on_chip_ram; cycles=7
BA94: 27 13 BEQ loc_BAA9 ; cycles=3/7 nt/t
BA96: 15 FA A2 D3 BCLR.B #3, @H'FAA2 ; refs ram_FAA2 in on_chip_ram; cycles=9
BA9A: 15 FA A3 13 CLR.B @H'FAA3 ; refs ram_FAA3 in on_chip_ram; cycles=9
BA9E: 15 FE DA D7 BCLR.B #7, @SCI1_SCR ; clear TIE (bit 7) of SCI1_SCR; cycles=9
BA9E: 15 FE DA D7 BCLR.B #7, @SCI1_SCR ; clear TIE (bit 7) of SCI1_SCR; disable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); cycles=9
BAA2: 15 F9 C0 06 1F MOV:G.B #H'1F, @H'F9C0 ; refs ram_F9C0 in on_chip_ram; cycles=9
BAA7: 20 48 BRA loc_BAF1 ; cycles=8
@@ -2975,13 +3058,13 @@ BAA9: BF 90 MOV:G.W R0, @-R7 ; cycles=5
BAAB: 15 F9 C2 80 MOV:G.B @H'F9C2, R0 ; refs ram_F9C2 in on_chip_ram; cycles=6
BAAF: A0 12 EXTU.B R0 ; cycles=3
BAB1: F0 F8 58 80 MOV:G.B @(-H'07A8,R0), R0 ; cycles=6
BAB5: 15 FE DB 90 MOV:G.B R0, @SCI1_TDR ; SCI1_TDR; cycles=6
BAB5: 15 FE DB 90 MOV:G.B R0, @SCI1_TDR ; SCI1_TDR; write RS232/SCI byte to SCI1 TDR for transmission; SCI1 TDR write transmits on traced RS232/MAX202 path: H8 pin 66 P95/TXD -> MAX202 pin 11; cycles=6
BAB9: CF 80 MOV:G.W @R7+, R0 ; cycles=6
BABB: 15 FE DC D7 BCLR.B #7, @SCI1_SSR ; clear TDRE (bit 7) of SCI1_SSR; cycles=8
BABB: 15 FE DC D7 BCLR.B #7, @SCI1_SSR ; clear TDRE (bit 7) of SCI1_SSR; clear SCI1 transmit data register empty flag (TDRE); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
BABF: 15 F9 C2 08 ADD:Q.B #1, @H'F9C2 ; refs ram_F9C2 in on_chip_ram; cycles=8
BAC3: 15 F9 C2 04 06 CMP:G.B #H'06, @H'F9C2 ; refs ram_F9C2 in on_chip_ram; cycles=6
BAC8: 26 27 BNE loc_BAF1 ; cycles=3/7 nt/t
BACA: 15 FE DA D7 BCLR.B #7, @SCI1_SCR ; clear TIE (bit 7) of SCI1_SCR; cycles=9
BACA: 15 FE DA D7 BCLR.B #7, @SCI1_SCR ; clear TIE (bit 7) of SCI1_SCR; disable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); cycles=9
BACE: 15 F7 95 F6 BTST.B #6, @H'F795 ; refs ram_F795 in on_chip_ram; cycles=7
BAD2: 26 14 BNE loc_BAE8 ; cycles=3/7 nt/t
BAD4: 15 F7 91 F7 BTST.B #7, @H'F791 ; refs ram_F791 in on_chip_ram; cycles=7
@@ -3043,14 +3126,14 @@ BB56: 19 RTS ; cycles=12
vec_sci1_eri_BB57:
BB57: 15 FA A4 C7 BSET.B #7, @H'FAA4 ; refs ram_FAA4 in on_chip_ram; cycles=8
BB5B: 15 FE DC D5 BCLR.B #5, @SCI1_SSR ; clear ORER (bit 5) of SCI1_SSR; cycles=8
BB5F: 15 FE DC D4 BCLR.B #4, @SCI1_SSR ; clear FER (bit 4) of SCI1_SSR; cycles=8
BB63: 15 FE DC D3 BCLR.B #3, @SCI1_SSR ; clear PER (bit 3) of SCI1_SSR; cycles=8
BB5B: 15 FE DC D5 BCLR.B #5, @SCI1_SSR ; clear ORER (bit 5) of SCI1_SSR; clear SCI1 overrun error flag (ORER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
BB5F: 15 FE DC D4 BCLR.B #4, @SCI1_SSR ; clear FER (bit 4) of SCI1_SSR; clear SCI1 framing error flag (FER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
BB63: 15 FE DC D3 BCLR.B #3, @SCI1_SSR ; clear PER (bit 3) of SCI1_SSR; clear SCI1 parity error flag (PER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
vec_sci1_rxi_BB67:
BB67: 12 03 STM.W {R0,R1}, @-SP ; cycles=12
BB69: 15 FE DC D6 BCLR.B #6, @SCI1_SSR ; clear RDRF (bit 6) of SCI1_SSR; cycles=8
BB6D: 15 FE DD 80 MOV:G.B @SCI1_RDR, R0 ; refs SCI1_RDR in register_field; cycles=6
BB69: 15 FE DC D6 BCLR.B #6, @SCI1_SSR ; clear RDRF (bit 6) of SCI1_SSR; clear SCI1 receive-data-full flag (RDRF); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; cycles=8
BB6D: 15 FE DD 80 MOV:G.B @SCI1_RDR, R0 ; read SCI1 received byte from RDR; SCI1 RDR read receives from traced RS232/MAX202 path: MAX202 pin 12 -> H8 pin 67 P96/RXD; refs SCI1_RDR in register_field; cycles=6
BB71: 15 F9 C1 16 TST.B @H'F9C1 ; refs ram_F9C1 in on_chip_ram; cycles=6
BB75: 26 06 BNE loc_BB7D ; cycles=3/8 nt/t
BB77: 15 F9 C3 13 CLR.B @H'F9C3 ; refs ram_F9C3 in on_chip_ram; cycles=8

File diff suppressed because it is too large Load Diff

View File

@@ -465,7 +465,7 @@ void vec_reset_1000(void)
P9DDR = (uint8_t)(0x93); /* 1025; MOV:G.B #H'93, @P9DDR; P9DDR = H'93; refs P9DDR; cycles=9 */
P9DR = (uint8_t)(0x00); /* 102A; MOV:G.B #H'00, @P9DR; P9DR = H'00; refs P9DR; cycles=9 */
SYSCR1 = (uint8_t)(0x87); /* 102F; MOV:G.B #H'87, @SYSCR1; SYSCR1 = H'87 (IRQ1E=0 IRQ0E=0 NMIEG=0 BRLE=0; P12/P13 are I/O, IRQ0 disabled, IRQ1 disabled); refs SYSCR1; cycles=9 */
SYSCR2 = (uint8_t)(0x84); /* 1034; MOV:G.B #H'84, @SYSCR2; SYSCR2 = H'84 (IRQ5E=0 IRQ4E=0 IRQ3E=0 IRQ2E=0 P6PWME=1 P9PWME=0 P9SCI2E=0; enabled P6 PWM); refs SYSCR2; cycles=9 */
SYSCR2 = (uint8_t)(0x84); /* 1034; MOV:G.B #H'84, @SYSCR2; SYSCR2 = H'84 (IRQ5E=0 IRQ4E=0 IRQ3E=0 IRQ2E=0 P6PWME=1 P9PWME=0 P9SCI2E=0; enabled P6 PWM); SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; refs SYSCR2; cycles=9 */
FRT1_TCR = (uint8_t)(0x02); /* 1039; MOV:G.B #H'02, @FRT1_TCR; FRT1_TCR = H'02 (ICIE=0 OCIEB=0 OCIEA=0 OVIE=0 OEB=0 OEA=0 CKS1=1 CKS0=0); refs FRT1_TCR; cycles=9 */
FRT1_TCSR = (uint8_t)(0x01); /* 103E; MOV:G.B #H'01, @FRT1_TCSR; FRT1_TCSR = H'01 (ICF=0 OCFB=0 OCFA=0 OVF=0 OLVLB=0 OLVLA=0 IEDG=0 CCLRA=1); refs FRT1_TCSR; cycles=9 */
FRT1_FRC_H = (uint16_t)(0x00); /* 1043; MOV:G.W #H'00, @FRT1_FRC_H; FRT1_FRC_H = H'00; refs FRT1_FRC_H; FRT1_FRC W write high TEMP access; cycles=9 */
@@ -484,12 +484,12 @@ void vec_reset_1000(void)
PWM2_DTR = (uint8_t)(0xFF); /* 1086; MOV:G.B #H'FF, @PWM2_DTR; PWM2_DTR = H'FF; refs PWM2_DTR; cycles=9 */
PWM3_TCR = (uint8_t)(0x3B); /* 108B; MOV:G.B #H'3B, @PWM3_TCR; PWM3_TCR = H'3B (OE=0 OS=0 CKS2=0 CKS1=1 CKS0=1); refs PWM3_TCR; cycles=9 */
PWM3_DTR = (uint8_t)(0x7D); /* 1090; MOV:G.B #H'7D, @PWM3_DTR; PWM3_DTR = H'7D; refs PWM3_DTR; cycles=9 */
SCI1_SMR = (uint8_t)(0x24); /* 1095; MOV:G.B #H'24, @SCI1_SMR; SCI1_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); refs SCI1_SMR; cycles=9 */
SCI1_SCR = (uint8_t)(0x3C); /* 109A; MOV:G.B #H'3C, @SCI1_SCR; SCI1_SCR = H'3C (TIE=0 RIE=0 TE=1 RE=1 CKE1=0 CKE0=0; SCI enables TX,RX, internal clock); refs SCI1_SCR; cycles=9 */
SCI1_BRR = (uint8_t)(0x07); /* 109F; MOV:G.B #H'07, @SCI1_BRR; SCI1_BRR = H'07; SCI1 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; refs SCI1_BRR; cycles=9 */
SCI2_SMR = (uint8_t)(0x24); /* 10A4; MOV:G.B #H'24, @SCI2_SMR; SCI2_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); refs SCI2_SMR; cycles=9 */
SCI2_SCR = (uint8_t)(0x0C); /* 10A9; MOV:G.B #H'0C, @SCI2_SCR; SCI2_SCR = H'0C (TIE=0 RIE=0 TE=0 RE=0 CKE1=0 CKE0=0; SCI enables none, internal clock); refs SCI2_SCR; cycles=9 */
SCI2_BRR = (uint8_t)(0x07); /* 10AE; MOV:G.B #H'07, @SCI2_BRR; SCI2_BRR = H'07; SCI2 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; refs SCI2_BRR; cycles=9 */
SCI1_SMR = (uint8_t)(0x24); /* 1095; MOV:G.B #H'24, @SCI1_SMR; SCI1_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); SCI1 SMR serial init for traced RS232/MAX202 path (H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD); refs SCI1_SMR; cycles=9 */
SCI1_SCR = (uint8_t)(0x3C); /* 109A; MOV:G.B #H'3C, @SCI1_SCR; SCI1_SCR = H'3C (TIE=0 RIE=0 TE=1 RE=1 CKE1=0 CKE0=0; SCI enables TX,RX, internal clock); disable SCI1 TX interrupt (TIE); disable SCI1 receive and receive-error interrupts (RIE); enable SCI1 transmitter (TE); enable SCI1 receiver (RE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); refs SCI1_SCR; cycles=9 */
SCI1_BRR = (uint8_t)(0x07); /* 109F; MOV:G.B #H'07, @SCI1_BRR; SCI1_BRR = H'07; SCI1 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; SCI1 BRR serial init for traced RS232/MAX202 path (H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD); refs SCI1_BRR; cycles=9 */
SCI2_SMR = (uint8_t)(0x24); /* 10A4; MOV:G.B #H'24, @SCI2_SMR; SCI2_SMR = H'24 (C/A=0 CHR=0 PE=1 O/E=0 STOP=0 CKS1=0 CKS0=0; SCI async, 8-bit, even parity, 1 stop, clock phi); SCI2 SMR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; refs SCI2_SMR; cycles=9 */
SCI2_SCR = (uint8_t)(0x0C); /* 10A9; MOV:G.B #H'0C, @SCI2_SCR; SCI2_SCR = H'0C (TIE=0 RIE=0 TE=0 RE=0 CKE1=0 CKE0=0; SCI enables none, internal clock); disable SCI2 TX interrupt (TIE); disable SCI2 receive and receive-error interrupts (RIE); disable SCI2 transmitter (TE); disable SCI2 receiver (RE); SCI2 SCR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; refs SCI2_SCR; cycles=9 */
SCI2_BRR = (uint8_t)(0x07); /* 10AE; MOV:G.B #H'07, @SCI2_BRR; SCI2_BRR = H'07; SCI2 async 8-bit even parity 1 stop BRR N=7 CKS n=0; baud needs --clock-hz; SCI2 BRR write; not the traced MAX202 path; P9SCI2E=0 disables SCI2 pins P92/P93/P94, while the board trace is SCI1 P95/P96; refs SCI2_BRR; cycles=9 */
ADCSR = (uint8_t)(0x19); /* 10B3; MOV:G.B #H'19, @ADCSR; ADCSR = H'19 (ADF=0 ADIE=0 ADST=0 SCAN=1 CKS=1 CH2=0 CH1=0 CH0=1; A/D halt, scan AN0-AN1, 138-state max, ADI disabled); refs ADCSR; cycles=9 */
MEM8[0xFEE9] = (uint8_t)(0x7F); /* 10B8; MOV:G.B #H'7F, @H'FEE9; cycles=9 */
WCR = (uint8_t)(0xF0); /* 10BD; MOV:G.B #H'F0, @WCR; WCR = H'F0 (WMS1=0 WMS0=0 WC1=0 WC0=0; programmable wait, 0 waits); refs WCR; cycles=9 */
@@ -2116,21 +2116,21 @@ void loc_3F40(void)
SR &= (uint16_t)(0x00FF); /* 3F42; ANDC.W #H'00FF, SR; cycles=4 */
SR |= (uint16_t)(0x0600); /* 3F46; ORC.W #H'0600, SR; cycles=4 */
do {
R0 = read_eclock(MEM8[0xF200]); /* 3F4A; MOVFPE.B @H'F200, R0; refs mem_F200; cycles=13 */
set_flags_btst(R0, 7); /* 3F4F; BTST.B #7, R0; cycles=2 */
} while (!Z); /* 3F51; BNE loc_3F4A; cycles=3/8 nt/t */
R0 = read_eclock(MEM8[0xF200]); /* 3F4A; MOVFPE.B @H'F200, R0; LCD status read from E-clock H'F200; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; refs mem_F200; cycles=13 */
set_flags_btst(R0, 7); /* 3F4F; BTST.B #7, R0; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; cycles=2 */
} while (!Z); /* 3F51; BNE loc_3F4A; LCD busy-flag poll: read H'F200, test bit 7, branch until clear; cycles=3/8 nt/t */
set_flags_btst(R4, 8); /* 3F53; BTST.W #8, R4; cycles=3 */
if (!Z) goto loc_3F6D; /* 3F55; BNE loc_3F6D; cycles=3/8 nt/t */
set_flags_btst(R4, 9); /* 3F57; BTST.W #9, R4; cycles=3 */
if (!Z) goto loc_3F62; /* 3F59; BNE loc_3F62; cycles=3/8 nt/t */
write_eclock(MEM8[0xF200], R4); /* 3F5B; MOVTPE.B R4, @H'F200; refs mem_F200; cycles=13 */
write_eclock(MEM8[0xF200], R4); /* 3F5B; MOVTPE.B R4, @H'F200; LCD command/address write to E-clock H'F200; refs mem_F200; cycles=13 */
goto loc_3F72; /* 3F60; BRA loc_3F72; cycles=7 */
loc_3F62:
write_eclock(MEM8[0xF201], R4); /* 3F62; MOVTPE.B R4, @H'F201; refs mem_F201; cycles=13 */
write_eclock(MEM8[0xF201], R4); /* 3F62; MOVTPE.B R4, @H'F201; LCD data write to E-clock H'F201; refs mem_F201; cycles=13 */
MEM16[0xFB00] += (uint16_t)(1); /* 3F67; ADD:Q.W #1, @H'FB00; refs ram_FB00; cycles=8 */
goto loc_3F72; /* 3F6B; BRA loc_3F72; cycles=8 */
loc_3F6D:
R4 = read_eclock(MEM8[0xF201]); /* 3F6D; MOVFPE.B @H'F201, R4; refs mem_F201; cycles=13 */
R4 = read_eclock(MEM8[0xF201]); /* 3F6D; MOVFPE.B @H'F201, R4; LCD data read from E-clock H'F201; refs mem_F201; cycles=13 */
loc_3F72:
SR = (uint16_t)(MEM16[R7++]); /* 3F72; LDC.W @R7+, SR; cycles=7 */
return; /* 3F74; RTS; cycles=12 */
@@ -2432,12 +2432,12 @@ void loc_434C(void)
IPRD = (uint8_t)(0x00); /* 435B; MOV:G.B #H'00, @IPRD; IPRD = H'00 (FRT3 priority=0; 8-bit timer priority=0); refs IPRD; cycles=9 */
IPRE = (uint8_t)(0x50); /* 4360; MOV:G.B #H'50, @IPRE; IPRE = H'50 (SCI1 priority=5; SCI2 priority=0); refs IPRE; cycles=9 */
IPRF = (uint8_t)(0x40); /* 4365; MOV:G.B #H'40, @IPRF; IPRF = H'40 (A/D priority=4); refs IPRF; cycles=9 */
SCI1_SCR |= BIT(6); /* 436A; BSET.B #6, @SCI1_SCR; set RIE (bit 6) of SCI1_SCR; refs SCI1_SCR; cycles=9 */
SCI1_SCR |= BIT(6); /* 436A; BSET.B #6, @SCI1_SCR; set RIE (bit 6) of SCI1_SCR; enable SCI1 receive and receive-error interrupts (RIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); refs SCI1_SCR; cycles=9 */
FRT1_TCR |= BIT(5); /* 436E; BSET.B #5, @FRT1_TCR; set OCIEA (bit 5) of FRT1_TCR; refs FRT1_TCR; cycles=9 */
FRT2_TCR |= BIT(5); /* 4372; BSET.B #5, @FRT2_TCR; set OCIEA (bit 5) of FRT2_TCR; refs FRT2_TCR; cycles=9 */
ADCSR |= BIT(6); /* 4376; BSET.B #6, @ADCSR; set ADIE (bit 6) of ADCSR; refs ADCSR; cycles=9 */
SYSCR2 |= BIT(4); /* 437A; BSET.B #4, @SYSCR2; set IRQ3E (bit 4) of SYSCR2; refs SYSCR2; cycles=9 */
SYSCR2 |= BIT(5); /* 437E; BSET.B #5, @SYSCR2; set IRQ4E (bit 5) of SYSCR2; refs SYSCR2; cycles=9 */
SYSCR2 |= BIT(4); /* 437A; BSET.B #4, @SYSCR2; set IRQ3E (bit 4) of SYSCR2; SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; refs SYSCR2; cycles=9 */
SYSCR2 |= BIT(5); /* 437E; BSET.B #5, @SYSCR2; set IRQ4E (bit 5) of SYSCR2; SYSCR2 write leaves P9SCI2E=0; SCI2 pins are disabled, so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96; refs SYSCR2; cycles=9 */
set_flags_btst(P7DR, 6); /* 4382; BTST.B #6, @P7DR; refs P7DR; cycles=7 */
if (!Z) { /* 4386; BEQ loc_438E; cycles=3/7 nt/t */
WDT_TCSR_R = (uint16_t)(0xA53F); /* 4388; MOV:G.W #H'A53F, @WDT_TCSR_R; WDT_TCSR_R = H'A53F (OVF=0 WT/IT=0 TME=1 CKS2=1 CKS1=1 CKS0=1; TCSR password H'A5, WDT enabled, interval IRQ0, clock phi/4096); refs WDT_TCSR_R; cycles=11 */
@@ -2849,13 +2849,13 @@ void loc_BA26(void)
R0 ^= (uint8_t)(MEM8[0xF85C]); /* BA60; XOR.B @H'F85C, R0; refs ram_F85C; cycles=7 */
MEM8[0xF85D] = (uint8_t)(R0); /* BA64; MOV:G.B R0, @H'F85D; refs ram_F85D; cycles=7 */
do {
set_flags_btst(SCI1_SSR, 7); /* BA68; BTST.B #7, @SCI1_SSR; refs SCI1_SSR; cycles=7 */
} while (Z); /* BA6C; BEQ loc_BA68; cycles=3/7 nt/t */
set_flags_btst(SCI1_SSR, 7); /* BA68; BTST.B #7, @SCI1_SSR; wait for SCI1 transmit data register empty (TDRE=1); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=7 */
} while (Z); /* BA6C; BEQ loc_BA68; repeat SCI1 transmit-empty wait while TDRE=0; cycles=3/7 nt/t */
R0 = (uint8_t)(MEM8[0xF858]); /* BA6E; MOV:G.B @H'F858, R0; refs ram_F858; cycles=7 */
SCI1_TDR = (uint8_t)(R0); /* BA72; MOV:G.B R0, @SCI1_TDR; SCI1_TDR; refs SCI1_TDR; cycles=7 */
SCI1_TDR = (uint8_t)(R0); /* BA72; MOV:G.B R0, @SCI1_TDR; SCI1_TDR; write RS232/SCI byte to SCI1 TDR for transmission; SCI1 TDR write transmits on traced RS232/MAX202 path: H8 pin 66 P95/TXD -> MAX202 pin 11; refs SCI1_TDR; cycles=7 */
MEM8[0xF9C2] = (uint8_t)(0x01); /* BA76; MOV:G.B #H'01, @H'F9C2; refs ram_F9C2; cycles=9 */
SCI1_SSR &= ~BIT(7); /* BA7B; BCLR.B #7, @SCI1_SSR; clear TDRE (bit 7) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
SCI1_SCR |= BIT(7); /* BA7F; BSET.B #7, @SCI1_SCR; set TIE (bit 7) of SCI1_SCR; refs SCI1_SCR; cycles=8 */
SCI1_SSR &= ~BIT(7); /* BA7B; BCLR.B #7, @SCI1_SSR; clear TDRE (bit 7) of SCI1_SSR; clear SCI1 transmit data register empty flag (TDRE); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
SCI1_SCR |= BIT(7); /* BA7F; BSET.B #7, @SCI1_SCR; set TIE (bit 7) of SCI1_SCR; enable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); refs SCI1_SCR; cycles=8 */
return; /* BA83; RTS; cycles=13 */
}
@@ -2870,7 +2870,7 @@ void vec_sci1_txi_BA84(void)
if (Z) goto loc_BAA9; /* BA94; BEQ loc_BAA9; cycles=3/7 nt/t */
MEM8[0xFAA2] &= ~BIT(3); /* BA96; BCLR.B #3, @H'FAA2; refs ram_FAA2; cycles=9 */
MEM8[0xFAA3] = 0; /* BA9A; CLR.B @H'FAA3; refs ram_FAA3; cycles=9 */
SCI1_SCR &= ~BIT(7); /* BA9E; BCLR.B #7, @SCI1_SCR; clear TIE (bit 7) of SCI1_SCR; refs SCI1_SCR; cycles=9 */
SCI1_SCR &= ~BIT(7); /* BA9E; BCLR.B #7, @SCI1_SCR; clear TIE (bit 7) of SCI1_SCR; disable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); refs SCI1_SCR; cycles=9 */
MEM8[0xF9C0] = (uint8_t)(0x1F); /* BAA2; MOV:G.B #H'1F, @H'F9C0; refs ram_F9C0; cycles=9 */
goto loc_BAF1; /* BAA7; BRA loc_BAF1; cycles=8 */
loc_BAA9:
@@ -2878,13 +2878,13 @@ loc_BAA9:
R0 = (uint8_t)(MEM8[0xF9C2]); /* BAAB; MOV:G.B @H'F9C2, R0; refs ram_F9C2; cycles=6 */
R0 = zero_extend8(R0); /* BAAF; EXTU.B R0; cycles=3 */
R0 = (uint8_t)(MEM8[R0 - 0x07A8]); /* BAB1; MOV:G.B @(-H'07A8,R0), R0; cycles=6 */
SCI1_TDR = (uint8_t)(R0); /* BAB5; MOV:G.B R0, @SCI1_TDR; SCI1_TDR; refs SCI1_TDR; cycles=6 */
SCI1_TDR = (uint8_t)(R0); /* BAB5; MOV:G.B R0, @SCI1_TDR; SCI1_TDR; write RS232/SCI byte to SCI1 TDR for transmission; SCI1 TDR write transmits on traced RS232/MAX202 path: H8 pin 66 P95/TXD -> MAX202 pin 11; refs SCI1_TDR; cycles=6 */
R0 = (uint16_t)(MEM16[R7++]); /* BAB9; MOV:G.W @R7+, R0; cycles=6 */
SCI1_SSR &= ~BIT(7); /* BABB; BCLR.B #7, @SCI1_SSR; clear TDRE (bit 7) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(7); /* BABB; BCLR.B #7, @SCI1_SSR; clear TDRE (bit 7) of SCI1_SSR; clear SCI1 transmit data register empty flag (TDRE); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
MEM8[0xF9C2] += (uint8_t)(1); /* BABF; ADD:Q.B #1, @H'F9C2; refs ram_F9C2; cycles=8 */
set_flags_cmp8(MEM8[0xF9C2], 0x06); /* BAC3; CMP:G.B #H'06, @H'F9C2; refs ram_F9C2; cycles=6 */
if (!Z) goto loc_BAF1; /* BAC8; BNE loc_BAF1; cycles=3/7 nt/t */
SCI1_SCR &= ~BIT(7); /* BACA; BCLR.B #7, @SCI1_SCR; clear TIE (bit 7) of SCI1_SCR; refs SCI1_SCR; cycles=9 */
SCI1_SCR &= ~BIT(7); /* BACA; BCLR.B #7, @SCI1_SCR; clear TIE (bit 7) of SCI1_SCR; disable SCI1 TX interrupt (TIE); SCI1 SCR write TE=1 RE=1; TE/RE select the traced RS232/MAX202 pins (P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12); refs SCI1_SCR; cycles=9 */
set_flags_btst(MEM8[0xF795], 6); /* BACE; BTST.B #6, @H'F795; refs ram_F795; cycles=7 */
if (!Z) goto loc_BAE8; /* BAD2; BNE loc_BAE8; cycles=3/7 nt/t */
set_flags_btst(MEM8[0xF791], 7); /* BAD4; BTST.B #7, @H'F791; refs ram_F791; cycles=7 */
@@ -2945,17 +2945,17 @@ void vec_sci1_eri_BB57(void)
{
/* vector sources: sci1_eri */
MEM8[0xFAA4] |= BIT(7); /* BB57; BSET.B #7, @H'FAA4; refs ram_FAA4; cycles=8 */
SCI1_SSR &= ~BIT(5); /* BB5B; BCLR.B #5, @SCI1_SSR; clear ORER (bit 5) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(4); /* BB5F; BCLR.B #4, @SCI1_SSR; clear FER (bit 4) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(3); /* BB63; BCLR.B #3, @SCI1_SSR; clear PER (bit 3) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(5); /* BB5B; BCLR.B #5, @SCI1_SSR; clear ORER (bit 5) of SCI1_SSR; clear SCI1 overrun error flag (ORER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(4); /* BB5F; BCLR.B #4, @SCI1_SSR; clear FER (bit 4) of SCI1_SSR; clear SCI1 framing error flag (FER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
SCI1_SSR &= ~BIT(3); /* BB63; BCLR.B #3, @SCI1_SSR; clear PER (bit 3) of SCI1_SSR; clear SCI1 parity error flag (PER); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
}
void vec_sci1_rxi_BB67(void)
{
/* vector sources: sci1_rxi */
push_registers(R0, R1); /* BB67; STM.W {R0,R1}, @-SP; cycles=12 */
SCI1_SSR &= ~BIT(6); /* BB69; BCLR.B #6, @SCI1_SSR; clear RDRF (bit 6) of SCI1_SSR; refs SCI1_SSR; cycles=8 */
R0 = (uint8_t)(SCI1_RDR); /* BB6D; MOV:G.B @SCI1_RDR, R0; refs SCI1_RDR; cycles=6 */
SCI1_SSR &= ~BIT(6); /* BB69; BCLR.B #6, @SCI1_SSR; clear RDRF (bit 6) of SCI1_SSR; clear SCI1 receive-data-full flag (RDRF); SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use; refs SCI1_SSR; cycles=8 */
R0 = (uint8_t)(SCI1_RDR); /* BB6D; MOV:G.B @SCI1_RDR, R0; read SCI1 received byte from RDR; SCI1 RDR read receives from traced RS232/MAX202 path: MAX202 pin 12 -> H8 pin 67 P96/RXD; refs SCI1_RDR; cycles=6 */
set_flags_tst8(MEM8[0xF9C1]); /* BB71; TST.B @H'F9C1; refs ram_F9C1; cycles=6 */
if (!Z) goto loc_BB7D; /* BB75; BNE loc_BB7D; cycles=3/8 nt/t */
MEM8[0xF9C3] = 0; /* BB77; CLR.B @H'F9C3; refs ram_F9C3; cycles=8 */

544
h8536/board_profile.py Normal file
View File

@@ -0,0 +1,544 @@
from __future__ import annotations
from collections.abc import Mapping
from .formatting import parse_int
from .model import Instruction
SCI_REGISTERS: dict[str, dict[str, int]] = {
"SCI1": {
"SMR": 0xFED8,
"BRR": 0xFED9,
"SCR": 0xFEDA,
"TDR": 0xFEDB,
"SSR": 0xFEDC,
"RDR": 0xFEDD,
},
"SCI2": {
"SMR": 0xFEF0,
"BRR": 0xFEF1,
"SCR": 0xFEF2,
"TDR": 0xFEF3,
"SSR": 0xFEF4,
"RDR": 0xFEF5,
},
}
SCI_REGISTER_BY_ADDRESS = {
address: (channel, register)
for channel, registers in SCI_REGISTERS.items()
for register, address in registers.items()
}
SYSCR2_ADDRESS = 0xFEFD
SYSCR2_INITIAL = 0x80
P9SCI2E_BIT = 0
SCR_INITIAL = 0x0C
MANUAL_REFERENCES = [
"Manual/0900766b802125d0.md:2417 FP-80 H8/536 pin 66 is P95/TXD",
"Manual/0900766b802125d0.md:2418 FP-80 H8/536 pin 67 is P96/RXD",
"Manual/0900766b802125d0.md:11192 Port 9 carries SCI1 and SCI2 serial signals",
"Manual/0900766b802125d0.md:11201 P96 is RXD1 input",
"Manual/0900766b802125d0.md:11202 P95 is TXD1 output",
"Manual/0900766b802125d0.md:15725 SCI1 RXD input pin",
"Manual/0900766b802125d0.md:15726 SCI1 TXD output pin",
"Manual/0900766b802125d0.md:15750 SCI register table starts with SCI1 RDR/TDR/SMR/SCR/SSR/BRR",
"Manual/0900766b802125d0.md:15758 SCI register table lists SCI2 RDR/TDR/SMR/SCR/SSR/BRR",
"Manual/0900766b802125d0.md:15794 RDR receive data register",
"Manual/0900766b802125d0.md:15823 TDR transmit data register",
"Manual/0900766b802125d0.md:15969 SCR enables and disables SCI functions",
"Manual/0900766b802125d0.md:16009 SCR.TE makes the TXD pin output",
"Manual/0900766b802125d0.md:16029 SCR.RE makes the RXD pin input",
"Manual/0900766b802125d0.md:16090 SSR contains transmit/receive status flags",
"Manual/0900766b802125d0.md:10560 SYSCR2 controls port 9 pin functions",
"Manual/0900766b802125d0.md:10631 SYSCR2.P9SCI2E controls the SCI2 functions of P92-P94",
]
BOARD_PROFILES = {
"sony_rcp_tx7": {
"name": "Sony RCP-TX7",
"summary": "Board trace ties the H8/536 SCI1 pins to a MAX202 RS232 transceiver.",
"manual_references": MANUAL_REFERENCES,
"traces": [
{
"channel": "SCI1",
"signal": "TXD",
"h8_pin": 66,
"h8_pin_name": "P95/TXD",
"h8_function": "TXD1",
"max202_pin": 11,
"evidence": "MAX202 pin 11 traces to H8 pin 66",
},
{
"channel": "SCI1",
"signal": "RXD",
"h8_pin": 67,
"h8_pin_name": "P96/RXD",
"h8_function": "RXD1",
"max202_pin": 12,
"evidence": "MAX202 pin 12 traces to H8 pin 67",
},
],
},
}
_READ_ONLY_ROOTS = {"BTST", "CMP", "CMP:E", "CMP:G", "CMP:I", "MOVFPE", "TST"}
_BIT_WRITE_ROOTS = {"BCLR", "BNOT", "BSET"}
def analyze_board_profile(
instructions: Mapping[int, Instruction],
board: str = "sony_rcp_tx7",
) -> dict[str, object]:
"""Annotate board-specific serial-path accesses without changing disassembly output."""
if board not in BOARD_PROFILES:
raise ValueError(f"unsupported board profile: {board}")
profile = BOARD_PROFILES[board]
annotations: dict[int, str] = {}
instruction_metadata: dict[int, dict[str, object]] = {}
channels = _initial_channel_payload(profile)
register_values: dict[int, int | None] = {
SYSCR2_ADDRESS: SYSCR2_INITIAL,
SCI_REGISTERS["SCI1"]["SCR"]: SCR_INITIAL,
SCI_REGISTERS["SCI2"]["SCR"]: SCR_INITIAL,
}
for address in sorted(instructions):
ins = instructions[address]
access_kind = _access_kind(ins)
access_records: list[dict[str, object]] = []
comments: list[str] = []
if SYSCR2_ADDRESS in _expanded_references(ins, access_kind):
value = _write_value(ins, SYSCR2_ADDRESS, register_values.get(SYSCR2_ADDRESS), 1, 0)
if access_kind == "write":
register_values[SYSCR2_ADDRESS] = value
record = _syscr2_access(ins, access_kind, register_values.get(SYSCR2_ADDRESS))
access_records.append(record)
comments.append(str(record["comment"]))
for target in _sci_targets(ins, access_kind):
register_address = int(target["register_address"])
value = _write_value(
ins,
register_address,
register_values.get(register_address),
int(target["width"]),
int(target["index"]),
)
if access_kind == "write":
register_values[register_address] = value
channel = str(target["channel"])
register = str(target["register"])
p9sci2e = _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT)
record = _sci_access(
ins,
channel,
register,
register_address,
access_kind,
value,
register_values.get(register_address),
p9sci2e,
)
access_records.append(record)
comments.append(str(record["comment"]))
channel_payload = channels[channel]
channel_accesses = channel_payload["accesses"]
if isinstance(channel_accesses, list):
channel_accesses.append(record)
if register == "SCR":
channel_payload["scr"] = _scr_metadata(register_values.get(register_address))
if access_records:
comment = "; ".join(_dedupe(comments))
annotations[ins.address] = comment
instruction_metadata[ins.address] = {
"accesses": access_records,
"comment": comment,
}
channels["SCI2"]["p9sci2e"] = _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT)
return {
"board": board,
"name": profile["name"],
"summary": profile["summary"],
"manual_references": list(profile["manual_references"]),
"traces": [dict(trace) for trace in profile["traces"]],
"channels": channels,
"annotations": annotations,
"instructions": instruction_metadata,
"state": {
"SYSCR2": _register_state(register_values.get(SYSCR2_ADDRESS)),
"P9SCI2E": _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT),
},
}
def board_comment_for_instruction(analysis: Mapping[str, object] | None, address: int) -> str:
if not analysis:
return ""
annotations = analysis.get("annotations")
if not isinstance(annotations, Mapping):
return ""
comment = annotations.get(address)
if comment is None:
comment = annotations.get(str(address))
return str(comment) if comment else ""
def board_metadata_for_instruction(
analysis: Mapping[str, object] | None,
address: int,
) -> dict[str, object] | None:
if not analysis:
return None
instructions = analysis.get("instructions")
if not isinstance(instructions, Mapping):
return None
metadata = instructions.get(address)
if metadata is None:
metadata = instructions.get(str(address))
return metadata if isinstance(metadata, dict) else None
def board_json_payload(analysis: Mapping[str, object] | None) -> dict[str, object]:
if not analysis:
return {
"board": None,
"name": None,
"summary": None,
"manual_references": [],
"traces": [],
"channels": {},
"instructions": {},
}
return {
"board": analysis.get("board"),
"name": analysis.get("name"),
"summary": analysis.get("summary"),
"manual_references": analysis.get("manual_references", []),
"traces": analysis.get("traces", []),
"channels": analysis.get("channels", {}),
"instructions": analysis.get("instructions", {}),
"state": analysis.get("state", {}),
}
def _initial_channel_payload(profile: Mapping[str, object]) -> dict[str, dict[str, object]]:
traces = [dict(trace) for trace in profile["traces"] if isinstance(trace, Mapping)]
return {
"SCI1": {
"traced_to_max202": True,
"path": "RS232/MAX202",
"pins": traces,
"scr": _scr_metadata(SCR_INITIAL),
"accesses": [],
},
"SCI2": {
"traced_to_max202": False,
"path": None,
"note": "Sony RCP-TX7 MAX202 board traces are on SCI1 P95/P96, not SCI2 P92/P93.",
"p9sci2e": False,
"scr": _scr_metadata(SCR_INITIAL),
"accesses": [],
},
}
def _syscr2_access(ins: Instruction, access: str, value: int | None) -> dict[str, object]:
p9sci2e = _bit_state(value, P9SCI2E_BIT)
comment = _syscr2_comment(access, p9sci2e)
record: dict[str, object] = {
"address": ins.address,
"instruction": ins.text,
"register": "SYSCR2",
"register_address": SYSCR2_ADDRESS,
"access": access,
"p9sci2e": p9sci2e,
"comment": comment,
}
if value is not None:
record["value"] = value
record["value_hex"] = _hex8(value)
return record
def _sci_access(
ins: Instruction,
channel: str,
register: str,
register_address: int,
access: str,
value: int | None,
state_value: int | None,
p9sci2e: bool | None,
) -> dict[str, object]:
comment = _sci_comment(channel, register, access, value if value is not None else state_value, p9sci2e)
record: dict[str, object] = {
"address": ins.address,
"instruction": ins.text,
"channel": channel,
"register": register,
"register_address": register_address,
"access": access,
"traced_to_max202": channel == "SCI1",
"comment": comment,
}
if value is not None:
record["value"] = value
record["value_hex"] = _hex8(value)
if register == "SCR":
record["scr"] = _scr_metadata(value if value is not None else state_value)
if channel == "SCI2":
record["p9sci2e"] = p9sci2e
return record
def _sci_comment(
channel: str,
register: str,
access: str,
value: int | None,
p9sci2e: bool | None,
) -> str:
if channel == "SCI1":
return _sci1_comment(register, access, value)
return _sci2_comment(register, access, p9sci2e)
def _sci1_comment(register: str, access: str, value: int | None) -> str:
if register in {"SMR", "BRR"}:
return (
f"SCI1 {register} serial init for traced RS232/MAX202 path "
"(H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD)"
)
if register == "SCR":
bits = _scr_bits_text(value)
suffix = f" {bits}" if bits else ""
return (
f"SCI1 SCR {access}{suffix}; TE/RE select the traced RS232/MAX202 pins "
"(P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12)"
)
if register == "TDR":
verb = "transmits" if access == "write" else "accesses transmit buffer"
return (
f"SCI1 TDR {access} {verb} on traced RS232/MAX202 path: "
"H8 pin 66 P95/TXD -> MAX202 pin 11"
)
if register == "RDR":
verb = "receives" if access == "read" else "accesses receive buffer"
return (
f"SCI1 RDR {access} {verb} from traced RS232/MAX202 path: "
"MAX202 pin 12 -> H8 pin 67 P96/RXD"
)
if register == "SSR":
return "SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use"
return f"SCI1 {register} {access} on traced RS232/MAX202 path"
def _sci2_comment(register: str, access: str, p9sci2e: bool | None) -> str:
prefix = f"SCI2 {register} {access}; not the traced MAX202 path"
if p9sci2e is False:
return (
f"{prefix}; P9SCI2E=0 disables SCI2 pins P92/P93/P94, "
"while the board trace is SCI1 P95/P96"
)
if p9sci2e is True:
return f"{prefix}; P9SCI2E=1 may route SCI2 pins, but the board trace is SCI1 P95/P96"
return f"{prefix}; P9SCI2E state unknown, and the board trace is SCI1 P95/P96"
def _syscr2_comment(access: str, p9sci2e: bool | None) -> str:
if p9sci2e is False:
return (
f"SYSCR2 {access} leaves P9SCI2E=0; SCI2 pins are disabled, "
"so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96"
)
if p9sci2e is True:
return (
f"SYSCR2 {access} sets P9SCI2E=1; SCI2 pins may be enabled, "
"but Sony RCP-TX7 MAX202 traces are SCI1 P95/P96"
)
return f"SYSCR2 {access}; P9SCI2E unknown, board trace remains SCI1 P95/P96"
def _sci_targets(ins: Instruction, access: str) -> list[dict[str, object]]:
width = _mnemonic_width(ins.mnemonic)
targets: list[dict[str, object]] = []
seen: set[int] = set()
for base_address in ins.references:
target_width = width if _operand_references_memory(ins, access) else 1
for index in range(target_width):
register_address = base_address + index
if register_address in seen:
continue
register_info = SCI_REGISTER_BY_ADDRESS.get(register_address)
if register_info is None:
continue
seen.add(register_address)
channel, register = register_info
targets.append(
{
"channel": channel,
"register": register,
"register_address": register_address,
"width": target_width,
"index": index,
},
)
return targets
def _expanded_references(ins: Instruction, access: str) -> set[int]:
width = _mnemonic_width(ins.mnemonic)
target_width = width if _operand_references_memory(ins, access) else 1
addresses: set[int] = set()
for base_address in ins.references:
for index in range(target_width):
addresses.add(base_address + index)
return addresses
def _operand_references_memory(ins: Instruction, access: str) -> bool:
operand = _destination_operand(ins.operands) if access == "write" else _source_operand(ins.operands)
return bool(operand and operand.startswith("@"))
def _access_kind(ins: Instruction) -> str:
root = _mnemonic_root(ins.mnemonic)
if root in _READ_ONLY_ROOTS:
return "read"
destination = _destination_operand(ins.operands)
if destination and destination.startswith("@"):
return "write"
return "read"
def _write_value(
ins: Instruction,
register_address: int,
current_value: int | None,
width: int,
index: int,
) -> int | None:
if _access_kind(ins) != "write":
return None
root = _mnemonic_root(ins.mnemonic)
if root == "CLR":
return 0
if root in _BIT_WRITE_ROOTS:
bit = _immediate_bit(ins.operands)
if bit is None or current_value is None:
return None
if root == "BSET":
return (current_value | (1 << bit)) & 0xFF
if root == "BCLR":
return (current_value & ~(1 << bit)) & 0xFF
return (current_value ^ (1 << bit)) & 0xFF
value = _immediate_source_value(ins.operands)
if value is None:
return None
return _byte_for_target(value, width, index)
def _destination_operand(operands: str) -> str | None:
operands = operands.strip()
if not operands:
return None
if "," not in operands:
return operands
return operands.rsplit(",", 1)[1].strip()
def _source_operand(operands: str) -> str | None:
operands = operands.strip()
if not operands:
return None
if "," not in operands:
return operands
return operands.rsplit(",", 1)[0].strip()
def _immediate_source_value(operands: str) -> int | None:
source = _source_operand(operands) or ""
if not source.startswith("#"):
return None
try:
return parse_int(source[1:])
except ValueError:
return None
def _immediate_bit(operands: str) -> int | None:
value = _immediate_source_value(operands)
if value is None:
return None
return value if 0 <= value <= 7 else None
def _mnemonic_root(mnemonic: str) -> str:
return mnemonic.split(".", 1)[0]
def _mnemonic_width(mnemonic: str) -> int:
return 2 if mnemonic.endswith(".W") else 1
def _byte_for_target(value: int, width: int, index: int) -> int:
shift = 8 * (width - index - 1)
return (value >> shift) & 0xFF
def _scr_metadata(value: int | None) -> dict[str, object]:
metadata = _register_state(value)
metadata.update(
{
"tie": None if value is None else bool(value & 0x80),
"rie": None if value is None else bool(value & 0x40),
"tx_enabled": None if value is None else bool(value & 0x20),
"rx_enabled": None if value is None else bool(value & 0x10),
},
)
return metadata
def _register_state(value: int | None) -> dict[str, object]:
return {
"value": value,
"value_hex": None if value is None else _hex8(value),
}
def _scr_bits_text(value: int | None) -> str:
if value is None:
return ""
return f"TE={1 if value & 0x20 else 0} RE={1 if value & 0x10 else 0}"
def _bit_state(value: int | None, bit: int) -> bool | None:
if value is None:
return None
return bool(value & (1 << bit))
def _dedupe(items: list[str]) -> list[str]:
seen: set[str] = set()
output: list[str] = []
for item in items:
if item in seen:
continue
seen.add(item)
output.append(item)
return output
def _hex8(value: int) -> str:
return f"H'{value & 0xFF:02X}"

View File

@@ -4,16 +4,20 @@ import argparse
from pathlib import Path
from .analysis import build_call_graph, collect_labels, linear_sweep, trace
from .board_profile import analyze_board_profile
from .cycles import annotate_cycles
from .data_analysis import analyze_unreached_data
from .dataflow import analyze_dataflow
from .decoder import H8536Decoder
from .formatting import parse_int
from .indirect import analyze_indirect_flow
from .lcd_driver import analyze_lcd_driver
from .lcd_text import analyze_lcd_text
from .peripheral_access import analyze_peripheral_access
from .render import format_callgraph_dot, format_listing, write_json
from .rom import Rom
from .sci import analyze_sci
from .sci_protocol import analyze_sci_protocol
from .symbols import discover_symbols
from .timing import summarize_timing
from .vectors import read_dtc_vectors_max, read_dtc_vectors_min, read_vectors_max, read_vectors_min
@@ -38,6 +42,12 @@ def main() -> int:
parser.add_argument("--entry", type=parse_int, action="append", default=[], help="extra entry point to trace")
parser.add_argument("--br", type=parse_int, default=None, help="optional BR value for @aa:8 short absolute operands")
parser.add_argument("--clock-hz", type=parse_int, default=None, help="oscillator clock in Hz for SCI baud inference")
parser.add_argument(
"--board-profile",
choices=("sony_rcp_tx7", "none"),
default="sony_rcp_tx7",
help="emit board-specific annotations for known physical traces",
)
parser.add_argument("--linear", action="store_true", help="linear-sweep the selected range instead of tracing from vectors")
parser.add_argument("--cycles", action="store_true", help="append Appendix A cycle estimates to assembly comments")
parser.add_argument("--timing", action="store_true", help="include straight-line block and loop cycle summaries")
@@ -79,8 +89,16 @@ def main() -> int:
symbols = discover_symbols(instructions, data_candidates=data_candidates)
timing_summary = summarize_timing(instructions, labels, call_graph) if args.timing else None
sci_analysis = analyze_sci(instructions, clock_hz=args.clock_hz)
sci_protocol = analyze_sci_protocol(instructions)
board_profile = (
None
if args.board_profile == "none"
else analyze_board_profile(instructions, board=args.board_profile)
)
peripheral_access = analyze_peripheral_access(instructions)
indirect_flow = analyze_indirect_flow(rom, instructions, labels)
lcd_text = analyze_lcd_text(rom, instructions, start=args.start, end=end)
lcd_driver = analyze_lcd_driver(instructions)
args.out.parent.mkdir(parents=True, exist_ok=True)
args.out.write_text(
@@ -97,10 +115,14 @@ def main() -> int:
timing_summary=timing_summary,
show_cycles=args.cycles,
sci_analysis=sci_analysis,
sci_protocol=sci_protocol,
board_profile=board_profile,
peripheral_access=peripheral_access,
indirect_flow=indirect_flow,
dataflow=dataflow,
symbols=symbols,
lcd_text=lcd_text,
lcd_driver=lcd_driver,
),
encoding="utf-8",
)
@@ -116,10 +138,14 @@ def main() -> int:
call_graph=call_graph,
timing_summary=timing_summary,
sci_analysis=sci_analysis,
sci_protocol=sci_protocol,
board_profile=board_profile,
peripheral_access=peripheral_access,
indirect_flow=indirect_flow,
dataflow=dataflow,
symbols=symbols,
lcd_text=lcd_text,
lcd_driver=lcd_driver,
)
if args.callgraph_dot:
args.callgraph_dot.parent.mkdir(parents=True, exist_ok=True)

311
h8536/lcd_driver.py Normal file
View File

@@ -0,0 +1,311 @@
from __future__ import annotations
import re
from collections.abc import Iterable, Mapping
from .formatting import h16
from .model import Instruction
LCD_STATUS_CONTROL = 0xF200
LCD_DATA = 0xF201
LCD_ADDRESSES = {
LCD_STATUS_CONTROL: "lcd_status_control",
LCD_DATA: "lcd_data",
}
ADDRESS_RE = re.compile(r"H'(?P<hex>[0-9A-Fa-f]{4})|0x(?P<c_hex>[0-9A-Fa-f]{4})")
REGISTER_RE = re.compile(r"\b(?P<reg>R[0-7])\b")
def analyze_lcd_driver(instructions: Mapping[int, Instruction] | Iterable[Instruction]) -> dict[str, object]:
ordered = _instruction_sequence(instructions)
by_address = {ins.address: ins for ins in ordered}
accesses = [_access_for_instruction(ins) for ins in ordered]
accesses = [access for access in accesses if access]
instruction_metadata: dict[int, list[dict[str, object]]] = {}
for access in accesses:
instruction_metadata.setdefault(int(access["address"]), []).append(access)
polling_loops = _find_polling_loops(ordered, by_address)
for loop in polling_loops:
for address, role in (
(int(loop["read_address"]), "lcd_busy_status_read"),
(int(loop["test_address"]), "lcd_busy_flag_test"),
(int(loop["branch_address"]), "lcd_busy_wait_branch"),
):
instruction_metadata.setdefault(address, []).append(
{
"address": address,
"kind": role,
"summary": loop["summary"],
"loop_start": loop["read_address"],
},
)
routines = _routine_candidates(ordered, accesses, polling_loops)
return {
"addresses": [
{"address": address, "name": name, "role": _address_role(address)}
for address, name in LCD_ADDRESSES.items()
],
"accesses": accesses,
"polling_loops": polling_loops,
"routines": routines,
"instructions": instruction_metadata,
}
def lcd_comment_for_instruction(analysis: Mapping[str, object] | None, address: int) -> str:
metadata = lcd_metadata_for_instruction(analysis, address)
if not metadata:
return ""
summaries = []
for item in metadata:
if isinstance(item, Mapping) and item.get("summary"):
summary = str(item["summary"])
if summary not in summaries:
summaries.append(summary)
return "; ".join(summaries)
def lcd_metadata_for_instruction(
analysis: Mapping[str, object] | None,
address: int,
) -> list[dict[str, object]]:
if not analysis:
return []
instructions = analysis.get("instructions")
if not isinstance(instructions, Mapping):
return []
metadata = instructions.get(address)
return list(metadata) if isinstance(metadata, list) else []
def _instruction_sequence(instructions: Mapping[int, Instruction] | Iterable[Instruction]) -> list[Instruction]:
values = instructions.values() if isinstance(instructions, Mapping) else instructions
return sorted(values, key=lambda ins: ins.address)
def _access_for_instruction(ins: Instruction) -> dict[str, object] | None:
address = _lcd_address_for_instruction(ins)
if address is None:
return None
direction = _direction_for_instruction(ins, address)
role = _access_role(ins, address, direction)
register = _last_register(ins.operands)
return {
"address": ins.address,
"instruction": ins.text,
"lcd_address": address,
"lcd_name": LCD_ADDRESSES[address],
"direction": direction,
"role": role,
"register": register,
"summary": _access_summary(address, direction, role),
}
def _lcd_address_for_instruction(ins: Instruction) -> int | None:
for address in ins.references:
if address in LCD_ADDRESSES:
return address
for match in ADDRESS_RE.finditer(ins.operands):
value = int(match.group("hex") or match.group("c_hex"), 16)
if value in LCD_ADDRESSES:
return value
return None
def _direction_for_instruction(ins: Instruction, lcd_address: int) -> str:
mnemonic = ins.mnemonic.upper()
operands = [part.strip() for part in ins.operands.split(",")]
if mnemonic.startswith("MOVFPE"):
return "read"
if mnemonic.startswith("MOVTPE"):
return "write"
if len(operands) >= 2:
source_has_lcd = _operand_has_address(operands[0], lcd_address)
dest_has_lcd = _operand_has_address(operands[-1], lcd_address)
if source_has_lcd and not dest_has_lcd:
return "read"
if dest_has_lcd and not source_has_lcd:
return "write"
return "unknown"
def _operand_has_address(operand: str, address: int) -> bool:
return f"H'{address:04X}" in operand.upper() or f"0X{address:04X}" in operand.upper()
def _access_role(ins: Instruction, address: int, direction: str) -> str:
if address == LCD_STATUS_CONTROL and direction == "read":
return "lcd_status_read"
if address == LCD_STATUS_CONTROL and direction == "write":
return "lcd_command_or_address_write"
if address == LCD_DATA and direction == "read":
return "lcd_data_read"
if address == LCD_DATA and direction == "write":
return "lcd_data_write"
return "lcd_access"
def _access_summary(address: int, direction: str, role: str) -> str:
if role == "lcd_status_read":
return f"LCD status read from E-clock {h16(address)}"
if role == "lcd_command_or_address_write":
return f"LCD command/address write to E-clock {h16(address)}"
if role == "lcd_data_read":
return f"LCD data read from E-clock {h16(address)}"
if role == "lcd_data_write":
return f"LCD data write to E-clock {h16(address)}"
return f"LCD {direction} at E-clock {h16(address)}"
def _find_polling_loops(
ordered: list[Instruction],
by_address: Mapping[int, Instruction],
) -> list[dict[str, object]]:
loops: list[dict[str, object]] = []
for index, ins in enumerate(ordered):
access = _access_for_instruction(ins)
if not access or access["role"] != "lcd_status_read":
continue
register = access.get("register")
if not isinstance(register, str):
continue
test = _next_register_bit_test(ordered, index, register)
if test is None:
continue
branch = _next_back_branch_to(ordered, by_address, ordered.index(test), ins.address, test.address)
if branch is None:
continue
loops.append(
{
"read_address": ins.address,
"test_address": test.address,
"branch_address": branch.address,
"register": register,
"bit": 7,
"summary": f"LCD busy-flag poll: read {h16(LCD_STATUS_CONTROL)}, test bit 7, branch until clear",
},
)
return loops
def _next_register_bit_test(ordered: list[Instruction], index: int, register: str) -> Instruction | None:
for candidate in ordered[index + 1 : index + 5]:
if not candidate.mnemonic.upper().startswith("BTST"):
continue
if "#7" not in candidate.operands:
continue
if re.search(rf"\b{re.escape(register)}\b", candidate.operands):
return candidate
return None
def _next_back_branch_to(
ordered: list[Instruction],
by_address: Mapping[int, Instruction],
index: int,
read_address: int,
test_address: int,
) -> Instruction | None:
_ = by_address
for candidate in ordered[index + 1 : index + 5]:
if candidate.kind != "branch":
continue
targets = [int(target) for target in candidate.targets]
if read_address in targets or test_address in targets:
return candidate
return None
def _routine_candidates(
ordered: list[Instruction],
accesses: list[dict[str, object]],
polling_loops: list[dict[str, object]],
) -> list[dict[str, object]]:
if not accesses:
return []
address_to_access = {int(access["address"]): access for access in accesses}
loop_addresses = {int(loop["read_address"]) for loop in polling_loops}
routines: dict[tuple[int, int], dict[str, object]] = {}
for address in sorted(address_to_access):
start, end = _routine_span(ordered, address)
key = (start, end)
routine = routines.setdefault(
key,
{
"start": start,
"end": end,
"accesses": [],
"roles": [],
"role_hint": "lcd_access",
},
)
routine["accesses"].append(address_to_access[address])
for routine in routines.values():
roles = sorted({str(access["role"]) for access in routine["accesses"]})
routine["roles"] = roles
routine["role_hint"] = _routine_role_hint(routine, loop_addresses)
return sorted(routines.values(), key=lambda item: int(item["start"]))
def _routine_span(ordered: list[Instruction], address: int) -> tuple[int, int]:
addresses = [ins.address for ins in ordered]
index = addresses.index(address)
start = ordered[index].address
cursor = index - 1
while cursor >= 0:
previous = ordered[cursor]
if previous.kind in {"return", "rte"}:
break
if previous.address + max(previous.size, 1) != start:
break
start = previous.address
cursor -= 1
end = ordered[index].address
cursor = index
while cursor < len(ordered):
current = ordered[cursor]
end = current.address
if current.kind in {"return", "rte"}:
break
next_index = cursor + 1
if next_index >= len(ordered):
break
next_address = ordered[next_index].address
if current.address + max(current.size, 1) != next_address:
break
cursor = next_index
return start, end
def _routine_role_hint(routine: Mapping[str, object], loop_addresses: set[int]) -> str:
access_addresses = {int(access["address"]) for access in routine.get("accesses", []) if isinstance(access, Mapping)}
roles = {str(role) for role in routine.get("roles", [])}
if access_addresses & loop_addresses and "lcd_data_write" in roles and "lcd_command_or_address_write" in roles:
return "lcd_wait_and_transfer"
if access_addresses & loop_addresses:
return "lcd_wait_ready"
if "lcd_data_write" in roles:
return "lcd_write_data"
if "lcd_command_or_address_write" in roles:
return "lcd_write_command_or_address"
if "lcd_data_read" in roles:
return "lcd_read_data"
return "lcd_access"
def _last_register(operands: str) -> str | None:
matches = list(REGISTER_RE.finditer(operands))
return matches[-1].group("reg") if matches else None
def _address_role(address: int) -> str:
if address == LCD_STATUS_CONTROL:
return "status/control register inferred from busy polling and command writes"
if address == LCD_DATA:
return "data register inferred from paired data reads/writes"
return "unknown"

386
h8536/lcd_text.py Normal file
View File

@@ -0,0 +1,386 @@
from __future__ import annotations
from collections.abc import Iterable, Mapping
from difflib import SequenceMatcher
from .formatting import h16
from .model import Instruction
from .rom import Rom
DISPLAY_PRINTABLE = set(range(0x20, 0x7F))
DISPLAY_PUNCTUATION = set(b" ./:-+,%()[]")
DEFAULT_SEARCH_TERMS = ("CONNECT",)
MOV_IW_FIRST_OPCODE = 0x58
MOV_IW_LAST_OPCODE = 0x5F
def analyze_lcd_text(
rom: Rom,
instructions: Mapping[int, Instruction] | Iterable[Instruction] | None = None,
*,
start: int | None = None,
end: int | None = None,
search_terms: Iterable[str] = DEFAULT_SEARCH_TERMS,
max_candidates: int = 240,
) -> dict[str, object]:
"""Find likely fixed-width LCD/menu strings and their nearby raw xrefs.
The firmware stores some display text as inline menu/script records rather
than as plain null-terminated strings. This pass scans the ROM bytes
directly, then correlates likely text fields with decoded and raw immediate
address loads such as ``MOV:I.W #H'63D4, R0``.
"""
lower = 0 if start is None else max(0, start)
upper = rom.end if end is None else min(rom.end, end)
candidates = _unique_candidates(
[
*_ff_terminated_candidates(rom, lower, upper),
*_printable_run_candidates(rom, lower, upper),
],
)
candidates = sorted(candidates, key=lambda item: (-float(item["score"]), int(item["address"])))[:max_candidates]
candidates.sort(key=lambda item: int(item["address"]))
instruction_list = _instruction_sequence(instructions)
xrefs_by_address = _xref_map(rom, candidates, instruction_list)
for candidate in candidates:
xrefs = xrefs_by_address.get(int(candidate["address"]), [])
if xrefs:
candidate["xrefs"] = xrefs
candidate["xref_count"] = len(xrefs)
regions = _group_regions(candidates)
searches = [_search_term(rom, candidates, term) for term in search_terms]
return {
"strings": candidates,
"regions": regions,
"searches": searches,
"notes": [
"LCD text scan is byte-oriented and conservative; strings may be inline script fields.",
"Raw xrefs include MOV:I.W immediates to the string address and nearby record prefixes.",
],
}
def lcd_text_comment_for_instruction(analysis: Mapping[str, object] | None, address: int) -> str:
if not analysis:
return ""
for candidate in analysis.get("strings", []):
if not isinstance(candidate, Mapping):
continue
for xref in candidate.get("xrefs", []):
if isinstance(xref, Mapping) and int(xref.get("address", -1)) == address:
text = str(candidate.get("trimmed") or candidate.get("text") or "").strip()
return f"LCD text xref {h16(int(candidate['address']))} {text!r}"
return ""
def _instruction_sequence(
instructions: Mapping[int, Instruction] | Iterable[Instruction] | None,
) -> list[Instruction]:
if instructions is None:
return []
values = instructions.values() if isinstance(instructions, Mapping) else instructions
return sorted(values, key=lambda ins: ins.address)
def _ff_terminated_candidates(rom: Rom, start: int, end: int) -> list[dict[str, object]]:
candidates: list[dict[str, object]] = []
address = start
while address < end:
if rom.u8(address) not in DISPLAY_PRINTABLE:
address += 1
continue
text_start = address
raw = bytearray()
while address < end and rom.u8(address) in DISPLAY_PRINTABLE:
raw.append(rom.u8(address))
address += 1
ff_count = 0
cursor = address
while cursor < end and rom.u8(cursor) == 0xFF:
ff_count += 1
cursor += 1
if ff_count and _looks_like_lcd_text(raw):
candidates.append(_candidate(text_start, raw, "ff_terminated", ff_count=ff_count))
address = max(cursor, address + 1)
return candidates
def _printable_run_candidates(rom: Rom, start: int, end: int) -> list[dict[str, object]]:
candidates: list[dict[str, object]] = []
address = start
while address < end:
if rom.u8(address) not in DISPLAY_PRINTABLE:
address += 1
continue
text_start = address
raw = bytearray()
while address < end and rom.u8(address) in DISPLAY_PRINTABLE:
raw.append(rom.u8(address))
address += 1
if _looks_like_lcd_text(raw, allow_long=True):
candidates.append(_candidate(text_start, raw, "printable_run", ff_count=0))
address += 1
return candidates
def _candidate(address: int, raw: bytearray, kind: str, *, ff_count: int) -> dict[str, object]:
text = raw.decode("ascii", errors="replace")
trimmed = text.strip()
score = _display_score(raw, kind, ff_count)
payload: dict[str, object] = {
"address": address,
"length": len(raw),
"text": text,
"trimmed": trimmed,
"kind": kind,
"score": round(score, 3),
"confidence": _confidence(score),
}
if ff_count:
payload["ff_terminators"] = ff_count
if len(raw) > 32:
payload["segments"] = _fixed_width_segments(text)
return payload
def _unique_candidates(candidates: list[dict[str, object]]) -> list[dict[str, object]]:
by_key: dict[tuple[int, int], dict[str, object]] = {}
for candidate in candidates:
key = (int(candidate["address"]), int(candidate["length"]))
existing = by_key.get(key)
if existing is None or float(candidate["score"]) > float(existing["score"]):
by_key[key] = candidate
return list(by_key.values())
def _looks_like_lcd_text(raw: bytearray, *, allow_long: bool = False) -> bool:
if len(raw) < 4:
return False
if len(raw) > 80 and not allow_long:
return False
trimmed = bytes(raw).strip()
if len(trimmed) < 2:
return False
good = sum(1 for value in trimmed if _display_char_score(value) > 0)
return good / max(len(trimmed), 1) >= 0.78
def _display_char_score(value: int) -> float:
if 0x41 <= value <= 0x5A or 0x30 <= value <= 0x39:
return 1.0
if value == 0x20:
return 0.8
if value in DISPLAY_PUNCTUATION:
return 0.7
if 0x61 <= value <= 0x7A:
return 0.35
return 0.0
def _display_score(raw: bytearray, kind: str, ff_count: int) -> float:
trimmed = bytes(raw).strip()
if not trimmed:
return 0.0
char_score = sum(_display_char_score(value) for value in trimmed) / len(trimmed)
length_bonus = 0.15 if len(raw) in {8, 10, 16, 17, 18, 19, 20} else 0.0
terminator_bonus = min(ff_count, 3) * 0.08
kind_bonus = 0.08 if kind == "ff_terminated" else 0.0
long_penalty = 0.2 if len(raw) > 40 else 0.0
return max(0.0, char_score + length_bonus + terminator_bonus + kind_bonus - long_penalty)
def _confidence(score: float) -> str:
if score >= 1.05:
return "high"
if score >= 0.82:
return "medium"
return "low"
def _fixed_width_segments(text: str) -> list[dict[str, object]]:
segments: list[dict[str, object]] = []
for width in (10, 16, 18, 20):
if len(text) < width * 2:
continue
chunks = [text[index : index + width] for index in range(0, len(text), width)]
useful = [chunk.strip() for chunk in chunks if len(chunk.strip()) >= 2]
if len(useful) >= 2:
segments.append({"width": width, "chunks": chunks})
return segments[:3]
def _xref_map(
rom: Rom,
candidates: list[dict[str, object]],
instructions: list[Instruction],
) -> dict[int, list[dict[str, object]]]:
addresses = [int(candidate["address"]) for candidate in candidates]
target_to_address: dict[int, int] = {}
for address in addresses:
for delta in range(-4, 5):
target = address + delta
if 0 <= target <= 0xFFFF:
target_to_address.setdefault(target, address)
xrefs: dict[int, list[dict[str, object]]] = {address: [] for address in addresses}
_add_decoded_xrefs(xrefs, target_to_address, instructions)
_add_raw_mov_iw_xrefs(xrefs, target_to_address, rom)
return {address: refs for address, refs in xrefs.items() if refs}
def _add_decoded_xrefs(
xrefs: dict[int, list[dict[str, object]]],
target_to_address: Mapping[int, int],
instructions: list[Instruction],
) -> None:
for ins in instructions:
for target, candidate_address in target_to_address.items():
needle = f"H'{target:04X}"
if needle in ins.operands:
xrefs[candidate_address].append(
{
"address": ins.address,
"kind": "decoded_operand",
"target": target,
"delta": target - candidate_address,
"instruction": ins.text,
},
)
def _add_raw_mov_iw_xrefs(
xrefs: dict[int, list[dict[str, object]]],
target_to_address: Mapping[int, int],
rom: Rom,
) -> None:
data = rom.data
for address in range(0, max(len(data) - 2, 0)):
opcode = data[address]
if not MOV_IW_FIRST_OPCODE <= opcode <= MOV_IW_LAST_OPCODE:
continue
target = (data[address + 1] << 8) | data[address + 2]
candidate_address = target_to_address.get(target)
if candidate_address is None:
continue
register = f"R{opcode - MOV_IW_FIRST_OPCODE}"
xref: dict[str, object] = {
"address": address,
"kind": "raw_mov_iw",
"target": target,
"delta": target - candidate_address,
"register": register,
"instruction": f"MOV:I.W #{h16(target)}, {register}",
}
bsr = _following_bsr(data, address + 3)
if bsr:
xref["following_bsr"] = bsr
xrefs[candidate_address].append(xref)
def _following_bsr(data: bytes, address: int) -> dict[str, object] | None:
if address + 2 >= len(data) or data[address] != 0x1E:
return None
displacement = (data[address + 1] << 8) | data[address + 2]
if displacement & 0x8000:
displacement -= 0x10000
target = (address + 3 + displacement) & 0xFFFF
return {"address": address, "target": target, "instruction": f"BSR {h16(target)}"}
def _group_regions(candidates: list[dict[str, object]]) -> list[dict[str, object]]:
regions: list[dict[str, object]] = []
current: list[dict[str, object]] = []
previous_end: int | None = None
for candidate in candidates:
address = int(candidate["address"])
length = int(candidate["length"])
if current and previous_end is not None and address - previous_end > 0x80:
_append_region(regions, current)
current = []
current.append(candidate)
previous_end = address + length
if current:
_append_region(regions, current)
return regions
def _append_region(regions: list[dict[str, object]], candidates: list[dict[str, object]]) -> None:
if len(candidates) < 2:
return
start = int(candidates[0]["address"])
end = max(int(item["address"]) + int(item["length"]) for item in candidates)
regions.append(
{
"start": start,
"end": end,
"count": len(candidates),
"samples": [str(item.get("trimmed") or item.get("text")) for item in candidates[:8]],
},
)
def _search_term(rom: Rom, candidates: list[dict[str, object]], term: str) -> dict[str, object]:
raw = term.encode("ascii", errors="ignore")
literal_hits = _literal_hits(rom.data, raw)
folded = term.upper()
candidate_hits = [
{
"address": int(candidate["address"]),
"text": candidate["text"],
"trimmed": candidate["trimmed"],
}
for candidate in candidates
if folded in str(candidate.get("text", "")).upper()
]
near_matches = _near_matches(candidates, term)
return {
"term": term,
"literal_hits": literal_hits,
"candidate_hits": candidate_hits,
"near_matches": near_matches,
"status": "found" if literal_hits or candidate_hits else "not_found",
}
def _literal_hits(data: bytes, needle: bytes) -> list[int]:
if not needle:
return []
hits: list[int] = []
start = 0
upper_data = data.upper()
upper_needle = needle.upper()
while True:
index = upper_data.find(upper_needle, start)
if index < 0:
return hits
hits.append(index)
start = index + 1
def _near_matches(candidates: list[dict[str, object]], term: str) -> list[dict[str, object]]:
normalized_term = _normalize_text(term)
matches: list[dict[str, object]] = []
for candidate in candidates:
normalized = _normalize_text(str(candidate.get("trimmed") or candidate.get("text") or ""))
if not normalized:
continue
ratio = SequenceMatcher(None, normalized_term, normalized).ratio()
if ratio >= 0.34:
matches.append(
{
"address": int(candidate["address"]),
"text": candidate["text"],
"trimmed": candidate["trimmed"],
"score": round(ratio, 3),
},
)
matches.sort(key=lambda item: (-float(item["score"]), int(item["address"])))
return matches[:12]
def _normalize_text(text: str) -> str:
return "".join(char for char in text.upper() if "A" <= char <= "Z" or "0" <= char <= "9")

View File

@@ -900,10 +900,26 @@ def _metadata_comments(ins: JsonObject) -> list[str]:
if isinstance(inference, dict) and inference.get("comment"):
comments.append(str(inference["comment"]))
for event in ins.get("sci_protocol", []):
if isinstance(event, dict) and event.get("comment"):
comments.append(str(event["comment"]))
board_profile = ins.get("board_profile")
if isinstance(board_profile, dict) and board_profile.get("comment"):
comments.append(str(board_profile["comment"]))
indirect = ins.get("indirect_flow")
if isinstance(indirect, dict) and indirect.get("summary"):
comments.append(str(indirect["summary"]))
lcd_text = ins.get("lcd_text")
if isinstance(lcd_text, dict) and lcd_text.get("comment"):
comments.append(str(lcd_text["comment"]))
for lcd_item in ins.get("lcd_driver", []):
if isinstance(lcd_item, dict) and lcd_item.get("summary"):
comments.append(str(lcd_item["summary"]))
dataflow = ins.get("dataflow")
if isinstance(dataflow, dict):
changes = dataflow.get("changes")

View File

@@ -3,11 +3,14 @@ from __future__ import annotations
import json
from pathlib import Path
from .board_profile import board_comment_for_instruction, board_json_payload, board_metadata_for_instruction
from .cycles import cycle_comment
from .dataflow import state_for_instruction
from .dtc import DtcEndpointInfo, DtcRegisterInfo
from .formatting import h16, label_for
from .indirect import indirect_comment_for_instruction, indirect_metadata_for_instruction
from .lcd_driver import lcd_comment_for_instruction, lcd_metadata_for_instruction
from .lcd_text import lcd_text_comment_for_instruction
from .memory import MEMORY_REGIONS, region_for
from .model import Instruction
from .peripheral_access import (
@@ -17,6 +20,11 @@ from .peripheral_access import (
)
from .rom import Rom
from .sci import sci_comment_for_instruction, sci_json_payload, sci_metadata_for_instruction
from .sci_protocol import (
sci_protocol_comment_for_instruction,
sci_protocol_json_payload,
sci_protocol_metadata_for_instruction,
)
from .symbols import symbol_for_address
from .tables import IO_REGISTERS
from .timing import format_timing_summary
@@ -118,6 +126,128 @@ def _dataflow_comment(analysis: dict[str, object] | None, address: int) -> str:
return "dataflow " + ", ".join(parts[:4]) + suffix
def _lcd_text_lines(lcd_text: dict[str, object] | None) -> list[str]:
if not lcd_text:
return []
strings = lcd_text.get("strings", [])
regions = lcd_text.get("regions", [])
searches = lcd_text.get("searches", [])
if not strings and not regions and not searches:
return []
lines = ["; LCD/Text Scan"]
for search in (searches if isinstance(searches, list) else []):
if not isinstance(search, dict):
continue
hits = len(search.get("literal_hits", [])) + len(search.get("candidate_hits", []))
status = "found" if hits else "not literal"
lines.append(f"; search {search.get('term')!r}: {status}, hits={hits}")
near = search.get("near_matches", [])
if isinstance(near, list) and near:
sample = ", ".join(f"{h16(int(item['address']))} {item['trimmed']!r}" for item in near[:4])
lines.append(f"; near: {sample}")
if isinstance(regions, list) and regions:
lines.append("; LCD text regions")
for region in regions[:12]:
if not isinstance(region, dict):
continue
samples = ", ".join(repr(sample) for sample in region.get("samples", [])[:4])
lines.append(
f"; region {h16(int(region['start']))}-{h16(int(region['end']))} "
f"count={region['count']:<3} {samples}",
)
if len(regions) > 12:
lines.append(f"; ... {len(regions) - 12} more LCD text regions")
if isinstance(strings, list) and strings:
lines.append("; LCD text candidates")
shown = 0
for item in strings:
if not isinstance(item, dict):
continue
if item.get("confidence") == "low" and not item.get("xref_count"):
continue
xrefs = f" xrefs={item['xref_count']}" if item.get("xref_count") else ""
lines.append(
f"; text {h16(int(item['address'])):<8} len={item['length']:<3} "
f"{item['confidence']:<6} {str(item['trimmed'])!r}{xrefs}",
)
shown += 1
if shown >= 48:
break
if len(strings) > shown:
lines.append(f"; ... {len(strings) - shown} more LCD text candidates")
lines.append("")
return lines
def _lcd_driver_lines(lcd_driver: dict[str, object] | None) -> list[str]:
if not lcd_driver:
return []
accesses = lcd_driver.get("accesses", [])
loops = lcd_driver.get("polling_loops", [])
routines = lcd_driver.get("routines", [])
if not accesses and not loops and not routines:
return []
lines = ["; LCD Driver Candidates"]
for address_info in lcd_driver.get("addresses", []):
if not isinstance(address_info, dict):
continue
lines.append(
f"; {h16(int(address_info['address']))} {address_info['name']:<18} {address_info['role']}",
)
if isinstance(routines, list) and routines:
lines.append("; LCD routines")
for routine in routines[:16]:
if not isinstance(routine, dict):
continue
roles = ", ".join(str(role) for role in routine.get("roles", []))
lines.append(
f"; routine {h16(int(routine['start']))}-{h16(int(routine['end']))} "
f"{routine['role_hint']:<24} {roles}",
)
if len(routines) > 16:
lines.append(f"; ... {len(routines) - 16} more LCD routines")
if isinstance(loops, list) and loops:
lines.append("; LCD busy loops")
for loop in loops[:16]:
if not isinstance(loop, dict):
continue
lines.append(
f"; loop {h16(int(loop['read_address']))}->{h16(int(loop['branch_address']))} "
f"{loop['summary']}",
)
lines.append("")
return lines
def _board_profile_lines(board_profile: dict[str, object] | None) -> list[str]:
if not board_profile:
return []
traces = board_profile.get("traces", [])
if not isinstance(traces, list) or not traces:
return []
lines = ["; Board Profile"]
summary = board_profile.get("summary")
if summary:
lines.append(f"; {summary}")
for trace in traces:
if not isinstance(trace, dict):
continue
lines.append(
f"; H8 pin {trace['h8_pin']} {trace['h8_pin_name']} "
f"({trace['signal']}) -> MAX202 pin {trace['max202_pin']}",
)
state = board_profile.get("state")
if isinstance(state, dict) and state.get("P9SCI2E") is False:
lines.append("; SCI2 pin routing is disabled by SYSCR2.P9SCI2E=0 in the observed setup.")
lines.append("")
return lines
def format_listing(
rom_path: Path,
rom: Rom,
@@ -131,10 +261,14 @@ def format_listing(
timing_summary: dict[str, list[dict[str, object]]] | None = None,
show_cycles: bool = False,
sci_analysis: dict[str, object] | None = None,
sci_protocol: dict[str, object] | None = None,
board_profile: dict[str, object] | None = None,
peripheral_access: dict[str, object] | None = None,
indirect_flow: dict[str, object] | None = None,
dataflow: dict[str, object] | None = None,
symbols: dict[str, object] | None = None,
lcd_text: dict[str, object] | None = None,
lcd_driver: dict[str, object] | None = None,
) -> str:
lines: list[str] = []
lines.append("; H8/536 ROM disassembly")
@@ -149,6 +283,7 @@ def format_listing(
lines.append("; - The register field is H'FE80-H'FFFF; names below come from appendix B.")
lines.append("; - @aa:8 short absolute operands use BR as the upper address byte.")
lines.append("; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.")
lines.append("; - LCD inference treats E-clock H'F200/H'F201 accesses as status/control and data candidates.")
if sci_analysis and sci_analysis.get("clock_hz") is None:
lines.append("; - Pass --clock-hz to convert SCI BRR settings into numeric baud rates.")
if show_cycles:
@@ -192,6 +327,9 @@ def format_listing(
lines.append("")
lines.extend(_symbol_lines(symbols))
lines.extend(_board_profile_lines(board_profile))
lines.extend(_lcd_text_lines(lcd_text))
lines.extend(_lcd_driver_lines(lcd_driver))
if timing_summary:
lines.extend(format_timing_summary(timing_summary))
@@ -208,8 +346,12 @@ def format_listing(
for part in (
ins.comment,
sci_comment_for_instruction(sci_analysis, address),
sci_protocol_comment_for_instruction(sci_protocol, address),
board_comment_for_instruction(board_profile, address),
peripheral_comment_for_instruction(peripheral_access, address),
indirect_comment_for_instruction(indirect_flow, address),
lcd_text_comment_for_instruction(lcd_text, address),
lcd_comment_for_instruction(lcd_driver, address),
_dataflow_comment(dataflow, address),
_reference_comment(ins, symbols) if not ins.comment else "",
cycle_comment(ins.cycles) if show_cycles else "",
@@ -232,10 +374,14 @@ def write_json(
call_graph: dict[str, object] | None = None,
timing_summary: dict[str, list[dict[str, object]]] | None = None,
sci_analysis: dict[str, object] | None = None,
sci_protocol: dict[str, object] | None = None,
board_profile: dict[str, object] | None = None,
peripheral_access: dict[str, object] | None = None,
indirect_flow: dict[str, object] | None = None,
dataflow: dict[str, object] | None = None,
symbols: dict[str, object] | None = None,
lcd_text: dict[str, object] | None = None,
lcd_driver: dict[str, object] | None = None,
) -> None:
payload = {
"vectors": [
@@ -257,12 +403,27 @@ def write_json(
"call_graph": call_graph or {"nodes": [], "edges": []},
"timing_summary": timing_summary or {"blocks": [], "loops": []},
"sci": sci_json_payload(sci_analysis),
"sci_protocol": sci_protocol_json_payload(sci_protocol),
"board_profile": board_json_payload(board_profile),
"peripheral_access": peripheral_json_payload(peripheral_access),
"indirect_flow": indirect_flow or {"sites": []},
"dataflow": _dataflow_json_payload(dataflow),
"symbols": symbols or {"symbols": [], "by_address": {}},
"lcd_text": lcd_text or {"strings": [], "regions": [], "searches": []},
"lcd_driver": lcd_driver or {"accesses": [], "polling_loops": [], "routines": []},
"instructions": [
_instruction_payload(ins, sci_analysis, peripheral_access, indirect_flow, dataflow, symbols)
_instruction_payload(
ins,
sci_analysis,
sci_protocol,
board_profile,
peripheral_access,
indirect_flow,
dataflow,
symbols,
lcd_text,
lcd_driver,
)
for ins in (instructions[addr] for addr in sorted(instructions))
],
}
@@ -315,10 +476,14 @@ def _dataflow_instruction_payload(dataflow: dict[str, object] | None, address: i
def _instruction_payload(
ins: Instruction,
sci_analysis: dict[str, object] | None = None,
sci_protocol: dict[str, object] | None = None,
board_profile: dict[str, object] | None = None,
peripheral_access: dict[str, object] | None = None,
indirect_flow: dict[str, object] | None = None,
dataflow: dict[str, object] | None = None,
symbols: dict[str, object] | None = None,
lcd_text: dict[str, object] | None = None,
lcd_driver: dict[str, object] | None = None,
) -> dict[str, object]:
payload: dict[str, object] = {
"address": ins.address,
@@ -346,6 +511,12 @@ def _instruction_payload(
sci_metadata = sci_metadata_for_instruction(sci_analysis, ins.address)
if sci_metadata:
payload["sci"] = sci_metadata
sci_protocol_metadata = sci_protocol_metadata_for_instruction(sci_protocol, ins.address)
if sci_protocol_metadata:
payload["sci_protocol"] = sci_protocol_metadata
board_metadata = board_metadata_for_instruction(board_profile, ins.address)
if board_metadata:
payload["board_profile"] = board_metadata
peripheral_metadata = peripheral_metadata_for_instruction(peripheral_access, ins.address)
if peripheral_metadata:
payload["peripheral_access"] = peripheral_metadata
@@ -355,6 +526,12 @@ def _instruction_payload(
dataflow_metadata = _dataflow_instruction_payload(dataflow, ins.address)
if dataflow_metadata:
payload["dataflow"] = dataflow_metadata
lcd_text_comment = lcd_text_comment_for_instruction(lcd_text, ins.address)
if lcd_text_comment:
payload["lcd_text"] = {"comment": lcd_text_comment}
lcd_driver_metadata = lcd_metadata_for_instruction(lcd_driver, ins.address)
if lcd_driver_metadata:
payload["lcd_driver"] = lcd_driver_metadata
return payload

446
h8536/sci_protocol.py Normal file
View File

@@ -0,0 +1,446 @@
from __future__ import annotations
from collections.abc import Iterable, Mapping
from dataclasses import dataclass
from .formatting import h16, parse_int
from .model import Instruction
MANUAL_REFERENCES = [
"Manual/0900766b802125d0.md:15748 SCI register map for RDR/TDR/SCR/SSR",
"Manual/0900766b802125d0.md:15794 RDR stores received data and is CPU-readable",
"Manual/0900766b802125d0.md:15823 TDR holds the next byte to transmit",
"Manual/0900766b802125d0.md:15976 SCR.TIE enables/disables TXI on TDRE",
"Manual/0900766b802125d0.md:15993 SCR.RIE enables RXI and ERI",
"Manual/0900766b802125d0.md:16008 SCR.TE enables the transmitter",
"Manual/0900766b802125d0.md:16028 SCR.RE enables the receiver",
"Manual/0900766b802125d0.md:16090 SSR flags are cleared by writing zero",
"Manual/0900766b802125d0.md:16100 SSR.TDRE means TDR can accept the next byte",
"Manual/0900766b802125d0.md:16116 SSR.RDRF means received data reached RDR",
"Manual/0900766b802125d0.md:16127 SSR.ORER reports receive overrun",
"Manual/0900766b802125d0.md:16140 SSR.FER reports framing errors",
"Manual/0900766b802125d0.md:16147 SSR.PER reports parity errors",
]
@dataclass(frozen=True)
class SciRegister:
channel: str
name: str
address: int
SCI_REGISTERS: tuple[SciRegister, ...] = (
SciRegister("SCI1", "SCR", 0xFEDA),
SciRegister("SCI1", "TDR", 0xFEDB),
SciRegister("SCI1", "SSR", 0xFEDC),
SciRegister("SCI1", "RDR", 0xFEDD),
SciRegister("SCI2", "SCR", 0xFEF2),
SciRegister("SCI2", "TDR", 0xFEF3),
SciRegister("SCI2", "SSR", 0xFEF4),
SciRegister("SCI2", "RDR", 0xFEF5),
)
SCI_REGISTER_BY_ADDRESS = {register.address: register for register in SCI_REGISTERS}
SCI_CHANNELS = ("SCI1", "SCI2")
SCR_CONTROL_BITS = {
7: ("TIE", "TX interrupt", "TXI"),
6: ("RIE", "RX/ERI interrupts", "RXI and ERI"),
5: ("TE", "transmitter", "TXD output"),
4: ("RE", "receiver", "RXD input"),
}
SSR_FLAGS = {
7: ("TDRE", "transmit data register empty"),
6: ("RDRF", "receive-data-full"),
5: ("ORER", "overrun error"),
4: ("FER", "framing error"),
3: ("PER", "parity error"),
}
SSR_CLEAR_ACTIONS = {
7: ("clear_tdre", "clear {channel} transmit data register empty flag (TDRE)"),
6: ("clear_rdrf", "clear {channel} receive-data-full flag (RDRF)"),
5: ("clear_orer", "clear {channel} overrun error flag (ORER)"),
4: ("clear_fer", "clear {channel} framing error flag (FER)"),
3: ("clear_per", "clear {channel} parity error flag (PER)"),
}
def analyze_sci_protocol(
instructions: Mapping[int, Instruction] | Iterable[Instruction],
) -> dict[str, object]:
"""Identify SCI protocol-level reads, writes, clears, and polling waits."""
ordered = _instruction_sequence(instructions)
annotations: dict[int, list[str]] = {}
instruction_metadata: dict[int, list[dict[str, object]]] = {}
channels: dict[str, dict[str, list[dict[str, object]]]] = {
channel: {"events": []} for channel in SCI_CHANNELS
}
events: list[dict[str, object]] = []
def record(event: dict[str, object]) -> None:
public = dict(event)
events.append(public)
channel = str(public["channel"])
channels[channel]["events"].append(public)
instruction_metadata.setdefault(int(public["address"]), []).append(public)
comment = public.get("comment")
if comment:
parts = annotations.setdefault(int(public["address"]), [])
text = str(comment)
if text not in parts:
parts.append(text)
tdre_wait_events = _tdre_wait_events_by_address(ordered)
for ins in ordered:
for event in _instruction_events(ins):
record(event)
for event in tdre_wait_events.get(ins.address, []):
record(event)
return {
"manual_references": MANUAL_REFERENCES,
"channels": channels,
"events": events,
"annotations": {
address: "; ".join(parts)
for address, parts in sorted(annotations.items())
},
"instructions": instruction_metadata,
}
def sci_protocol_comment_for_instruction(analysis: Mapping[str, object] | None, address: int) -> str:
if not analysis:
return ""
annotations = analysis.get("annotations")
if not isinstance(annotations, Mapping):
return ""
comment = annotations.get(address)
return str(comment) if comment else ""
def sci_protocol_metadata_for_instruction(
analysis: Mapping[str, object] | None,
address: int,
) -> list[dict[str, object]]:
if not analysis:
return []
instructions = analysis.get("instructions")
if not isinstance(instructions, Mapping):
return []
metadata = instructions.get(address)
return list(metadata) if isinstance(metadata, list) else []
def sci_protocol_json_payload(analysis: Mapping[str, object] | None) -> dict[str, object]:
if not analysis:
return {
"manual_references": MANUAL_REFERENCES,
"channels": {channel: {"events": []} for channel in SCI_CHANNELS},
"events": [],
}
return {
"manual_references": analysis.get("manual_references", MANUAL_REFERENCES),
"channels": analysis.get("channels", {}),
"events": analysis.get("events", []),
}
def _instruction_events(ins: Instruction) -> list[dict[str, object]]:
events: list[dict[str, object]] = []
for register in _referenced_sci_registers(ins):
if register.name == "TDR":
events.extend(_tdr_events(ins, register))
elif register.name == "RDR":
events.extend(_rdr_events(ins, register))
elif register.name == "SSR":
events.extend(_ssr_events(ins, register))
elif register.name == "SCR":
events.extend(_scr_events(ins, register))
return events
def _tdr_events(ins: Instruction, register: SciRegister) -> list[dict[str, object]]:
if _access_direction(ins, register.address) != "write":
return []
return [
_event(
ins,
register,
action="write_tdr",
comment=f"write RS232/SCI byte to {register.channel} TDR for transmission",
),
]
def _rdr_events(ins: Instruction, register: SciRegister) -> list[dict[str, object]]:
if _access_direction(ins, register.address) != "read":
return []
return [
_event(
ins,
register,
action="read_rdr",
comment=f"read {register.channel} received byte from RDR",
),
]
def _ssr_events(ins: Instruction, register: SciRegister) -> list[dict[str, object]]:
if _access_direction(ins, register.address) != "write":
return []
bit = _immediate_bit(ins.operands)
if _mnemonic_root(ins.mnemonic) == "BCLR" and bit in SSR_CLEAR_ACTIONS:
action, comment_template = SSR_CLEAR_ACTIONS[bit]
flag, description = SSR_FLAGS[bit]
return [
_event(
ins,
register,
action=action,
bit=bit,
flag=flag,
description=description,
comment=comment_template.format(channel=register.channel),
),
]
value = _written_immediate_value(ins)
if value is None:
return []
events: list[dict[str, object]] = []
for clear_bit, (action, comment_template) in SSR_CLEAR_ACTIONS.items():
if value & (1 << clear_bit):
continue
flag, description = SSR_FLAGS[clear_bit]
events.append(
_event(
ins,
register,
action=action,
bit=clear_bit,
flag=flag,
description=description,
value=value,
comment=comment_template.format(channel=register.channel),
),
)
return events
def _scr_events(ins: Instruction, register: SciRegister) -> list[dict[str, object]]:
if _access_direction(ins, register.address) != "write":
return []
bit = _immediate_bit(ins.operands)
root = _mnemonic_root(ins.mnemonic)
if root in {"BCLR", "BSET"} and bit in SCR_CONTROL_BITS:
return [_scr_bit_event(ins, register, bit, enabled=root == "BSET")]
value = _written_immediate_value(ins)
if value is None:
return []
return [
_scr_bit_event(ins, register, bit_number, enabled=bool(value & (1 << bit_number)), value=value)
for bit_number in SCR_CONTROL_BITS
]
def _scr_bit_event(
ins: Instruction,
register: SciRegister,
bit: int,
enabled: bool,
value: int | None = None,
) -> dict[str, object]:
bit_name, noun, interrupt_source = SCR_CONTROL_BITS[bit]
verb = "enable" if enabled else "disable"
action_noun = noun.replace("/", "_").replace(" ", "_").lower()
if bit == 7:
comment = f"{verb} {register.channel} TX interrupt (TIE)"
elif bit == 6:
comment = f"{verb} {register.channel} receive and receive-error interrupts (RIE)"
elif bit == 5:
comment = f"{verb} {register.channel} transmitter (TE)"
else:
comment = f"{verb} {register.channel} receiver (RE)"
return _event(
ins,
register,
action=f"{verb}_{action_noun}",
bit=bit,
bit_name=bit_name,
enabled=enabled,
interrupt_source=interrupt_source,
value=value,
comment=comment,
)
def _tdre_wait_events_by_address(ordered: list[Instruction]) -> dict[int, list[dict[str, object]]]:
events_by_address: dict[int, list[dict[str, object]]] = {}
for index, ins in enumerate(ordered):
register = _single_sci_register(ins)
if register is None or register.name != "SSR":
continue
if _mnemonic_root(ins.mnemonic) != "BTST" or _immediate_bit(ins.operands) != 7:
continue
branch = _next_beq_back_to(ordered, index, ins.address)
if branch is None:
continue
events_by_address.setdefault(ins.address, []).append(
_event(
ins,
register,
action="wait_for_tdre",
bit=7,
flag="TDRE",
branch_address=branch.address,
branch_target=ins.address,
comment=f"wait for {register.channel} transmit data register empty (TDRE=1)",
),
)
events_by_address.setdefault(branch.address, []).append(
_event(
branch,
register,
action="tdre_wait_branch",
bit=7,
flag="TDRE",
test_address=ins.address,
branch_target=ins.address,
comment=f"repeat {register.channel} transmit-empty wait while TDRE=0",
),
)
return events_by_address
def _next_beq_back_to(ordered: list[Instruction], index: int, target: int) -> Instruction | None:
for candidate in ordered[index + 1 : index + 5]:
if _mnemonic_root(candidate.mnemonic) != "BEQ":
continue
if target in candidate.targets:
return candidate
return None
def _event(
ins: Instruction,
register: SciRegister,
*,
action: str,
comment: str,
**extra: object,
) -> dict[str, object]:
event: dict[str, object] = {
"address": ins.address,
"instruction": ins.text,
"action": action,
"channel": register.channel,
"register": register.name,
"register_address": register.address,
"register_address_hex": h16(register.address),
"comment": comment,
"manual": MANUAL_REFERENCES,
}
event.update({key: value for key, value in extra.items() if value is not None})
return event
def _instruction_sequence(
instructions: Mapping[int, Instruction] | Iterable[Instruction],
) -> list[Instruction]:
values = instructions.values() if isinstance(instructions, Mapping) else instructions
return sorted(values, key=lambda ins: ins.address)
def _referenced_sci_registers(ins: Instruction) -> list[SciRegister]:
registers: list[SciRegister] = []
seen: set[int] = set()
for address in ins.references:
register = SCI_REGISTER_BY_ADDRESS.get(address)
if register is None or register.address in seen:
continue
seen.add(register.address)
registers.append(register)
return registers
def _single_sci_register(ins: Instruction) -> SciRegister | None:
registers = _referenced_sci_registers(ins)
return registers[0] if len(registers) == 1 else None
def _access_direction(ins: Instruction, address: int) -> str | None:
root = _mnemonic_root(ins.mnemonic)
if root in {"BTST", "CMP:E", "CMP:G", "CMP:I", "MOVFPE", "TST"}:
return "read"
if root in {"BCLR", "BNOT", "BSET", "CLR", "NEG", "NOT"}:
return "write"
if root in {"ADD:Q", "ADD:G", "ADDX", "AND", "OR", "SUB", "SUBS", "SUBX", "XOR"}:
return "write"
if root in {"MOV:G", "MOV:S", "MOVTPE"}:
source, destination = _source_destination_operands(ins.operands)
if _operand_mentions_address_or_register(destination, address):
return "write"
if _operand_mentions_address_or_register(source, address):
return "read"
if root in {"MOV:L", "MOV:F"}:
return "read"
if root == "STC":
return "write"
if root == "LDC":
return "read"
return None
def _source_destination_operands(operands: str) -> tuple[str, str]:
if "," not in operands:
operand = operands.strip()
return "", operand
source, destination = operands.rsplit(",", 1)
return source.strip(), destination.strip()
def _operand_mentions_address_or_register(operand: str, address: int) -> bool:
operand_upper = operand.upper()
register = SCI_REGISTER_BY_ADDRESS.get(address)
if register and f"{register.channel}_{register.name}" in operand_upper:
return True
return f"H'{address:04X}" in operand_upper or f"0X{address:04X}" in operand_upper
def _immediate_bit(operands: str) -> int | None:
source, _destination = _source_destination_operands(operands)
if not source.startswith("#"):
return None
try:
bit = parse_int(source[1:])
except ValueError:
return None
return bit if 0 <= bit <= 7 else None
def _written_immediate_value(ins: Instruction) -> int | None:
root = _mnemonic_root(ins.mnemonic)
if root == "CLR":
return 0
if root not in {"MOV:G", "MOV:S"}:
return None
source, _destination = _source_destination_operands(ins.operands)
if not source.startswith("#"):
return None
try:
return parse_int(source[1:]) & 0xFF
except ValueError:
return None
def _mnemonic_root(mnemonic: str) -> str:
return mnemonic.rsplit(".", 1)[0].upper()

View File

@@ -0,0 +1,75 @@
import json
import unittest
from h8536.board_profile import analyze_board_profile, board_comment_for_instruction, board_json_payload
from h8536.model import Instruction
def ins(address: int, mnemonic: str, operands: str, references: list[int]) -> Instruction:
return Instruction(address, b"\x00", mnemonic, operands, references=references)
class BoardProfileTest(unittest.TestCase):
def test_profile_records_manual_pin_table_and_board_traces(self):
analysis = analyze_board_profile({})
self.assertEqual(analysis["board"], "sony_rcp_tx7")
self.assertIn("Manual/0900766b802125d0.md:2417", " ".join(analysis["manual_references"]))
self.assertIn("Manual/0900766b802125d0.md:2418", " ".join(analysis["manual_references"]))
traces = analysis["traces"]
self.assertEqual(traces[0]["h8_pin"], 66)
self.assertEqual(traces[0]["max202_pin"], 11)
self.assertEqual(traces[1]["h8_pin"], 67)
self.assertEqual(traces[1]["max202_pin"], 12)
self.assertEqual(board_json_payload(analysis)["board"], "sony_rcp_tx7")
json.dumps(analysis)
def test_sci1_init_and_scr_comments_identify_rs232_max202_path(self):
instructions = {
0x1000: ins(0x1000, "MOV:G.W", "#H'0407, @SCI1_SMR", [0xFED8]),
0x1004: ins(0x1004, "MOV:G.B", "#H'30, @SCI1_SCR", [0xFEDA]),
}
analysis = analyze_board_profile(instructions)
self.assertIn("SCI1 SMR serial init for traced RS232/MAX202 path", board_comment_for_instruction(analysis, 0x1000))
self.assertIn("SCI1 BRR serial init for traced RS232/MAX202 path", board_comment_for_instruction(analysis, 0x1000))
scr_comment = board_comment_for_instruction(analysis, 0x1004)
self.assertIn("SCI1 SCR write TE=1 RE=1", scr_comment)
self.assertIn("P95/TXD pin 66 to MAX202 pin 11", scr_comment)
self.assertIn("P96/RXD pin 67 to MAX202 pin 12", scr_comment)
def test_sci1_data_and_status_registers_annotate_traced_path(self):
instructions = {
0x1010: ins(0x1010, "BTST.B", "#7, @SCI1_SSR", [0xFEDC]),
0x1012: ins(0x1012, "MOV:G.B", "R0L, @SCI1_TDR", [0xFEDB]),
0x1014: ins(0x1014, "MOV:G.B", "@SCI1_RDR, R0L", [0xFEDD]),
}
analysis = analyze_board_profile(instructions)
self.assertIn("SCI1 SSR status for traced RS232/MAX202 path", board_comment_for_instruction(analysis, 0x1010))
self.assertIn("H8 pin 66 P95/TXD -> MAX202 pin 11", board_comment_for_instruction(analysis, 0x1012))
self.assertIn("MAX202 pin 12 -> H8 pin 67 P96/RXD", board_comment_for_instruction(analysis, 0x1014))
def test_sci2_disabled_comments_say_it_is_not_traced_path(self):
instructions = {
0x2000: ins(0x2000, "MOV:G.B", "#H'80, @SYSCR2", [0xFEFD]),
0x2004: ins(0x2004, "MOV:G.B", "#H'30, @SCI2_SCR", [0xFEF2]),
}
analysis = analyze_board_profile(instructions)
syscr2_comment = board_comment_for_instruction(analysis, 0x2000)
self.assertIn("P9SCI2E=0", syscr2_comment)
self.assertIn("SCI2 is not the traced MAX202 path", syscr2_comment)
sci2_comment = board_comment_for_instruction(analysis, 0x2004)
self.assertIn("SCI2 SCR write; not the traced MAX202 path", sci2_comment)
self.assertIn("P9SCI2E=0 disables SCI2 pins P92/P93/P94", sci2_comment)
self.assertFalse(analysis["channels"]["SCI2"]["traced_to_max202"])
self.assertFalse(analysis["channels"]["SCI2"]["p9sci2e"])
if __name__ == "__main__":
unittest.main()

View File

@@ -0,0 +1,80 @@
import json
import tempfile
import unittest
from pathlib import Path
from h8536.board_profile import analyze_board_profile
from h8536.model import Instruction
from h8536.pseudocode import PseudocodeOptions, generate_pseudocode
from h8536.render import format_listing, write_json
from h8536.rom import Rom
from h8536.sci_protocol import analyze_sci_protocol
class BoardSciIntegrationTest(unittest.TestCase):
def test_listing_json_and_pseudocode_include_rs232_path_comments(self):
instructions = {
0x1000: Instruction(
0x1000,
b"\x15\xFE\xDA\x06\x3C",
"MOV:G.B",
"#H'3C, @SCI1_SCR",
references=[0xFEDA],
comment="SCI1_SCR = H'3C",
),
0x1005: Instruction(
0x1005,
b"\x15\xFE\xDB\x90",
"MOV:G.B",
"R0, @SCI1_TDR",
references=[0xFEDB],
),
0x1009: Instruction(
0x1009,
b"\x15\xFE\xDD\x80",
"MOV:G.B",
"@SCI1_RDR, R0",
references=[0xFEDD],
),
}
sci_protocol = analyze_sci_protocol(instructions)
board_profile = analyze_board_profile(instructions)
listing = format_listing(
Path("rom.bin"),
Rom(b"\xFF" * 0x20),
instructions,
{},
{},
"min",
traced=False,
sci_protocol=sci_protocol,
board_profile=board_profile,
)
self.assertIn("; Board Profile", listing)
self.assertIn("H8 pin 66 P95/TXD", listing)
self.assertIn("write RS232/SCI byte to SCI1 TDR", listing)
self.assertIn("MAX202 pin 12 -> H8 pin 67 P96/RXD", listing)
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "out.json"
write_json(path, instructions, {}, {}, sci_protocol=sci_protocol, board_profile=board_profile)
payload = json.loads(path.read_text(encoding="utf-8"))
self.assertEqual(payload["board_profile"]["board"], "sony_rcp_tx7")
self.assertEqual(payload["board_profile"]["traces"][0]["max202_pin"], 11)
tdr_instruction = next(item for item in payload["instructions"] if item["address"] == 0x1005)
self.assertEqual(tdr_instruction["sci_protocol"][0]["action"], "write_tdr")
self.assertIn("board_profile", tdr_instruction)
pseudocode = generate_pseudocode(
payload,
options=PseudocodeOptions(include_addresses=False, include_asm=False, structured=False),
)
self.assertIn("write RS232/SCI byte to SCI1 TDR", pseudocode)
self.assertIn("H8 pin 66 P95/TXD -> MAX202 pin 11", pseudocode)
if __name__ == "__main__":
unittest.main()

44
tests/test_lcd_driver.py Normal file
View File

@@ -0,0 +1,44 @@
import unittest
from h8536.lcd_driver import analyze_lcd_driver, lcd_comment_for_instruction
from h8536.model import Instruction
class LcdDriverTest(unittest.TestCase):
def test_detects_e_clock_lcd_accesses_and_busy_poll_loop(self):
instructions = {
0x3F4A: Instruction(
0x3F4A,
b"\x15\xF2\x00\x00\x80",
"MOVFPE.B",
"@H'F200, R0",
references=[0xF200],
),
0x3F4F: Instruction(0x3F4F, b"\xA0\xF7", "BTST.B", "#7, R0"),
0x3F51: Instruction(0x3F51, b"\x26\xF7", "BNE", "loc_3F4A", kind="branch", targets=[0x3F4A]),
0x3F53: Instruction(0x3F53, b"\x15\xF2\x00\x00\x94", "MOVTPE.B", "R4, @H'F200", references=[0xF200]),
0x3F58: Instruction(0x3F58, b"\x15\xF2\x01\x00\x94", "MOVTPE.B", "R4, @H'F201", references=[0xF201]),
0x3F5D: Instruction(0x3F5D, b"\x19", "RTS", kind="return", fallthrough=False),
}
analysis = analyze_lcd_driver(instructions)
self.assertEqual(len(analysis["accesses"]), 3)
self.assertEqual(analysis["polling_loops"][0]["read_address"], 0x3F4A)
self.assertEqual(analysis["routines"][0]["role_hint"], "lcd_wait_and_transfer")
self.assertIn("LCD busy-flag poll", lcd_comment_for_instruction(analysis, 0x3F51))
self.assertIn("LCD data write", lcd_comment_for_instruction(analysis, 0x3F58))
def test_regex_fallback_detects_operands_without_references(self):
instructions = {
0x0100: Instruction(0x0100, b"", "MOVTPE.B", "R1, @H'F201"),
}
analysis = analyze_lcd_driver(instructions)
self.assertEqual(analysis["accesses"][0]["lcd_address"], 0xF201)
self.assertEqual(analysis["accesses"][0]["role"], "lcd_data_write")
if __name__ == "__main__":
unittest.main()

55
tests/test_lcd_text.py Normal file
View File

@@ -0,0 +1,55 @@
import unittest
from h8536.lcd_text import analyze_lcd_text, lcd_text_comment_for_instruction
from h8536.model import Instruction
from h8536.rom import Rom
class LcdTextTest(unittest.TestCase):
def test_finds_ff_terminated_menu_strings_and_raw_mov_xrefs(self):
data = bytearray([0x00] * 0x2200)
data[0x1000:0x100D] = b"OPERATION " + b"\xFF\xFF\xFF"
data[0x100D:0x1013] = bytes.fromhex("58 0F FD 1E 00 1D")
data[0x1030:0x103D] = b" PAINT " + b"\xFF\xFF\xFF"
data[0x103D:0x1040] = bytes.fromhex("58 10 30")
analysis = analyze_lcd_text(Rom(bytes(data)), search_terms=("CONNECT",))
by_text = {item["trimmed"]: item for item in analysis["strings"]}
self.assertIn("OPERATION", by_text)
self.assertIn("PAINT", by_text)
operation = by_text["OPERATION"]
self.assertEqual(operation["kind"], "ff_terminated")
self.assertEqual(operation["ff_terminators"], 3)
self.assertEqual(operation["xrefs"][0]["kind"], "raw_mov_iw")
self.assertEqual(operation["xrefs"][0]["target"], 0x0FFD)
self.assertEqual(operation["xrefs"][0]["delta"], -3)
self.assertEqual(operation["xrefs"][0]["following_bsr"]["target"], 0x1030)
self.assertEqual(analysis["searches"][0]["status"], "not_found")
def test_groups_nearby_strings_into_regions(self):
data = bytearray([0x00] * 0x1400)
data[0x1200:0x120D] = b" LOCK " + b"\xFF\xFF\xFF"
data[0x1240:0x124D] = b"IRIS/M.BLK" + b"\xFF\xFF\xFF"
analysis = analyze_lcd_text(Rom(bytes(data)))
self.assertEqual(len(analysis["regions"]), 1)
self.assertEqual(analysis["regions"][0]["count"], 2)
self.assertIn("LOCK", analysis["regions"][0]["samples"])
def test_decoded_operand_xref_comment(self):
data = bytearray([0x00] * 0x1300)
data[0x1100:0x110D] = b"CONNECT? " + b"\xFF\xFF\xFF"
instructions = {
0x0100: Instruction(0x0100, b"\x58\x11\x00", "MOV:I.W", "#H'1100, R0"),
}
analysis = analyze_lcd_text(Rom(bytes(data)), instructions, search_terms=("CONNECT",))
self.assertEqual(analysis["searches"][0]["status"], "found")
self.assertIn("LCD text xref", lcd_text_comment_for_instruction(analysis, 0x0100))
if __name__ == "__main__":
unittest.main()

148
tests/test_sci_protocol.py Normal file
View File

@@ -0,0 +1,148 @@
import unittest
from h8536.model import Instruction
from h8536.sci_protocol import (
analyze_sci_protocol,
sci_protocol_comment_for_instruction,
sci_protocol_json_payload,
sci_protocol_metadata_for_instruction,
)
def ins(
address: int,
mnemonic: str,
operands: str = "",
references: list[int] | None = None,
kind: str = "normal",
targets: list[int] | None = None,
) -> Instruction:
return Instruction(
address,
b"",
mnemonic,
operands,
kind=kind,
targets=targets or [],
references=references or [],
)
class SciProtocolTest(unittest.TestCase):
def test_tdre_polling_loop_is_wait_for_transmit_data_register_empty(self):
instructions = {
0x2000: ins(0x2000, "BTST.B", "#7, @SCI1_SSR", references=[0xFEDC]),
0x2004: ins(0x2004, "BEQ", "loc_2000", kind="branch", targets=[0x2000]),
}
analysis = analyze_sci_protocol(instructions)
self.assertIn(
"wait for SCI1 transmit data register empty (TDRE=1)",
sci_protocol_comment_for_instruction(analysis, 0x2000),
)
self.assertIn(
"repeat SCI1 transmit-empty wait while TDRE=0",
sci_protocol_comment_for_instruction(analysis, 0x2004),
)
metadata = sci_protocol_metadata_for_instruction(analysis, 0x2000)[0]
self.assertEqual(metadata["action"], "wait_for_tdre")
self.assertEqual(metadata["branch_address"], 0x2004)
def test_write_to_tdr_is_rs232_sci_transmit_byte(self):
instructions = {
0x2100: ins(0x2100, "MOV:G.B", "R0, @SCI1_TDR", references=[0xFEDB]),
}
analysis = analyze_sci_protocol(instructions)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2100),
"write RS232/SCI byte to SCI1 TDR for transmission",
)
self.assertEqual(analysis["channels"]["SCI1"]["events"][0]["register"], "TDR")
def test_scr_tie_bit_set_and_clear_enable_disable_tx_interrupt(self):
instructions = {
0x2200: ins(0x2200, "BSET.B", "#7, @SCI1_SCR", references=[0xFEDA]),
0x2204: ins(0x2204, "BCLR.B", "#7, @SCI1_SCR", references=[0xFEDA]),
}
analysis = analyze_sci_protocol(instructions)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2200),
"enable SCI1 TX interrupt (TIE)",
)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2204),
"disable SCI1 TX interrupt (TIE)",
)
def test_receive_path_clears_rdrf_then_reads_received_byte(self):
instructions = {
0x2300: ins(0x2300, "BCLR.B", "#6, @SCI1_SSR", references=[0xFEDC]),
0x2304: ins(0x2304, "MOV:G.B", "@SCI1_RDR, R0", references=[0xFEDD]),
}
analysis = analyze_sci_protocol(instructions)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2300),
"clear SCI1 receive-data-full flag (RDRF)",
)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2304),
"read SCI1 received byte from RDR",
)
def test_receive_error_handler_clears_overrun_framing_and_parity_errors(self):
instructions = {
0x2400: ins(0x2400, "BCLR.B", "#5, @SCI1_SSR", references=[0xFEDC]),
0x2404: ins(0x2404, "BCLR.B", "#4, @SCI1_SSR", references=[0xFEDC]),
0x2408: ins(0x2408, "BCLR.B", "#3, @SCI1_SSR", references=[0xFEDC]),
}
analysis = analyze_sci_protocol(instructions)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2400),
"clear SCI1 overrun error flag (ORER)",
)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2404),
"clear SCI1 framing error flag (FER)",
)
self.assertEqual(
sci_protocol_comment_for_instruction(analysis, 0x2408),
"clear SCI1 parity error flag (PER)",
)
def test_immediate_scr_write_reports_protocol_control_bits(self):
instructions = {
0x2500: ins(0x2500, "MOV:G.B", "#H'B0, @SCI2_SCR", references=[0xFEF2]),
}
analysis = analyze_sci_protocol(instructions)
comment = sci_protocol_comment_for_instruction(analysis, 0x2500)
self.assertIn("enable SCI2 TX interrupt (TIE)", comment)
self.assertIn("disable SCI2 receive and receive-error interrupts (RIE)", comment)
self.assertIn("enable SCI2 transmitter (TE)", comment)
self.assertIn("enable SCI2 receiver (RE)", comment)
def test_json_payload_keeps_events_without_instruction_index(self):
instructions = {
0x2600: ins(0x2600, "MOV:G.B", "R0, @SCI2_TDR", references=[0xFEF3]),
}
analysis = analyze_sci_protocol(instructions)
payload = sci_protocol_json_payload(analysis)
self.assertIn("manual_references", payload)
self.assertEqual(payload["events"][0]["action"], "write_tdr")
self.assertEqual(payload["channels"]["SCI2"]["events"][0]["register"], "TDR")
if __name__ == "__main__":
unittest.main()