1
0
Files
h8-536-decoder/tests/test_table_xrefs.py
2026-05-27 21:37:50 +10:00

158 lines
6.0 KiB
Python

import json
import tempfile
import unittest
from pathlib import Path
from h8536.table_xrefs import analyze_table_xrefs, generate_table_xref_report, write_table_xrefs
def reference(address: int) -> dict:
return {"address": address}
def instruction(
address: int,
mnemonic: str,
operands: str = "",
references: list[int] | None = None,
text: str | None = None,
) -> dict:
return {
"address": address,
"mnemonic": mnemonic,
"operands": operands,
"text": text or f"{mnemonic} {operands}".strip(),
"references": [reference(item) for item in (references or [])],
"targets": [],
}
def payload() -> dict:
return {
"call_graph": {
"nodes": [
{"start": 0xC000, "end": 0xC0FF, "label": "loc_C000"},
{"start": 0xD000, "end": 0xD0FF, "label": "loc_D000"},
],
},
"serial_semantics": {
"table_map_candidates": [
{
"kind": "logical_table_map_candidate",
"name_candidate": "primary_value_table_candidate",
"confidence": "candidate-medium",
"accesses": [{"instruction_address": 0xC004}],
}
],
},
"lcd_text": {
"strings": [
{
"address": 0x77F4,
"text": "COMM LINK ITEM-1",
"trimmed": "COMM LINK ITEM-1",
"confidence": "high",
"xrefs": [
{
"address": 0x7804,
"following_bsr": {"address": 0x7807, "target": 0x5A91},
}
],
"xref_count": 1,
}
],
},
"lcd_driver": {
"routines": [
{
"start": 0x3F40,
"end": 0x3F74,
"role_hint": "lcd_wait_and_transfer",
"roles": ["lcd_data_write"],
}
],
},
"instructions": [
instruction(0xC000, "MOV:G.W", "#H'0006, R3"),
instruction(0xC004, "MOV:G.W", "@(-H'2000,R3), R0"),
instruction(0xC008, "MOV:G.W", "R1, @(-H'1800,R4)"),
instruction(0xD000, "MOV:G.W", "@H'F900, R2", [0xF900]),
instruction(0xD004, "BSET.B", "#7, @(-H'1400,R5)"),
instruction(0xD008, "CMP:G.W", "@H'E124, R0", [0xE124]),
instruction(0xD00C, "MOV:G.W", "R3, @H'F922", [0xF922]),
],
}
class TableXrefsTest(unittest.TestCase):
def test_reports_logical_direct_static_and_dynamic_accesses(self):
analysis = analyze_table_xrefs(payload())
tables = {table["name"]: table for table in analysis["tables"]}
primary = tables["primary_value_table_candidate"]
self.assertEqual(primary["access_count"], 3)
self.assertEqual(primary["read_count"], 3)
self.assertEqual(primary["static_offsets"], [0, 6, 0x124])
static_access = primary["accesses"][0]
self.assertEqual(static_access["index"], 6)
self.assertEqual(static_access["logical_address"], 0xE006)
self.assertEqual(static_access["function_label"], "loc_C000")
self.assertEqual(static_access["semantic_candidates"][0]["confidence"], "candidate-medium")
self.assertEqual(primary["accesses"][2]["kind"], "direct_logical_address_access")
self.assertEqual(primary["accesses"][2]["logical_address"], 0xE124)
current = tables["current_value_table_candidate"]
self.assertEqual(current["access_count"], 2)
self.assertEqual(current["dynamic_index_count"], 1)
self.assertEqual(current["accesses"][0]["index"], "dynamic")
self.assertEqual(current["accesses"][0]["index_register"], "R4")
self.assertEqual(current["accesses"][1]["direct_address"], 0xF922)
self.assertEqual(current["accesses"][1]["offset"], 2)
self.assertEqual(current["write_count"], 2)
flags = tables["flag_table_candidate"]
self.assertEqual(flags["write_count"], 1)
self.assertEqual(flags["dynamic_index_count"], 1)
lcd_terms = {
item["term"]: item
for item in analysis["lcd_correlation"]["term_hits"]
}
self.assertEqual(lcd_terms["CONNECT"]["hit_count"], 0)
self.assertEqual(lcd_terms["COMM LINK"]["hit_count"], 1)
self.assertEqual(
analysis["lcd_correlation"]["display_builder_targets"][0]["target"],
0x5A91,
)
def test_text_report_names_dynamic_registers_and_functions(self):
text = generate_table_xref_report(payload(), source_name="sample.json")
self.assertIn("Table/Index Cross-Reference Report for sample.json", text)
self.assertIn("primary_value_table_candidate H'E000", text)
self.assertIn("offset H'0006 selector 0x003 -> H'E006", text)
self.assertIn("offset H'0124 selector 0x092 -> H'E124", text)
self.assertIn("offset H'0002 selector 0x001 at H'F922", text)
self.assertIn("index dynamic via R4", text)
self.assertIn("term 'CONNECT': no LCD/text candidate hits", text)
self.assertIn("term 'COMM LINK': 1 candidate", text)
self.assertIn("display builder xrefs: H'5A91:1", text)
self.assertIn("loc_C000", text)
def test_write_json_output(self):
with tempfile.TemporaryDirectory() as tmp:
input_path = Path(tmp) / "rom.json"
output_path = Path(tmp) / "xrefs.json"
input_path.write_text(json.dumps(payload()), encoding="utf-8")
write_table_xrefs(input_path, output_path, as_json=True)
written = json.loads(output_path.read_text(encoding="utf-8"))
self.assertEqual(written["kind"], "table_xrefs")
self.assertEqual(written["summary"]["access_count"], 6)
self.assertEqual(written["source"], str(input_path))
if __name__ == "__main__":
unittest.main()