import unittest from h8536.indirect import analyze_indirect_flow, indirect_comment_for_instruction from h8536.model import Instruction from h8536.render import format_listing, write_json from h8536.rom import Rom import json import tempfile from pathlib import Path class IndirectFlowTest(unittest.TestCase): def test_detects_indexed_pointer_table_before_indirect_jump(self): data = bytearray([0xFF] * 0x240) data[0x0200:0x0206] = bytes.fromhex("01200300FFFF") instructions = { 0x0100: Instruction(0x0100, b"", "MOV:G.W", "@(H'0200,R4), R1"), 0x0104: Instruction(0x0104, b"", "JMP", "@R1", kind="jump", fallthrough=False), 0x0120: Instruction(0x0120, b"\x19", "RTS", kind="return", fallthrough=False), 0x0300: Instruction(0x0300, b"\x19", "RTS", kind="return", fallthrough=False), } analysis = analyze_indirect_flow(Rom(bytes(data)), instructions, {0x0120: "loc_0120"}) site = analysis["sites"][0] self.assertEqual(site["address"], 0x0104) self.assertEqual(site["target_register"], "R1") self.assertEqual(site["table"]["base"], 0x0200) self.assertEqual(site["table"]["entry_count"], 2) self.assertEqual(site["table"]["decoded_target_count"], 2) self.assertIn("pointer table H'0200", indirect_comment_for_instruction(analysis, 0x0104)) def test_records_unknown_indirect_call_without_prior_table_load(self): instructions = { 0x0100: Instruction(0x0100, b"", "JSR", "@R0", kind="call"), } analysis = analyze_indirect_flow(Rom(bytes([0xFF] * 0x200)), instructions) self.assertEqual(analysis["sites"][0]["confidence"], "unknown") self.assertIn("target not resolved", analysis["sites"][0]["summary"]) def test_listing_and_json_include_indirect_flow_metadata(self): instructions = { 0x0100: Instruction(0x0100, b"", "JSR", "@R0", kind="call"), } analysis = analyze_indirect_flow(Rom(bytes([0xFF] * 0x200)), instructions) listing = format_listing( Path("rom.bin"), Rom(bytes([0xFF] * 0x200)), instructions, {}, {}, "min", traced=True, indirect_flow=analysis, ) self.assertIn("target not resolved", listing) with tempfile.TemporaryDirectory() as tmp: path = Path(tmp) / "out.json" write_json(path, instructions, {}, {}, indirect_flow=analysis) payload = json.loads(path.read_text(encoding="utf-8")) self.assertEqual(payload["indirect_flow"]["sites"][0]["address"], 0x0100) self.assertEqual(payload["instructions"][0]["indirect_flow"]["confidence"], "unknown") if __name__ == "__main__": unittest.main()