LCD decompile
This commit is contained in:
74
tests/test_board_profile.py
Normal file
74
tests/test_board_profile.py
Normal file
@@ -0,0 +1,74 @@
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from h8536.board_profile import analyze_board_profile, board_comment_for_instruction
|
||||
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)
|
||||
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()
|
||||
44
tests/test_lcd_driver.py
Normal file
44
tests/test_lcd_driver.py
Normal 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
55
tests/test_lcd_text.py
Normal 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
148
tests/test_sci_protocol.py
Normal 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()
|
||||
Reference in New Issue
Block a user