1
0
Files
h8-536-decoder/tests/test_eeprom_layout.py
2026-05-26 11:35:21 +10:00

121 lines
4.4 KiB
Python

import io
import json
import tempfile
import unittest
from pathlib import Path
from h8536.eeprom_layout import (
analyze_eeprom_layout,
format_text_report,
main,
write_eeprom_layout,
)
def reference(address: int) -> dict:
return {"address": address}
def instruction(address: int, mnemonic: str, operands: str = "", refs: list[int] | None = None) -> dict:
return {
"address": address,
"mnemonic": mnemonic,
"operands": operands,
"text": f"{mnemonic} {operands}".strip(),
"references": [reference(item) for item in (refs or [])],
"targets": [],
}
def payload() -> dict:
return {
"instructions": [
instruction(0x410C, "MOV:G.W", "R5, @(-H'0C00,R0)"),
instruction(0x41E8, "MOV:G.W", "R5, @(-H'0850,R1)"),
instruction(0x40FA, "CMP:G.W", "#H'6B6F, @H'F402", [0xF402]),
instruction(0xBD3D, "MOV:G.B", "@(-H'3A9B,R4), R1"),
instruction(0xBD45, "MOV:G.W", "R0, @(-H'0C00,R1)"),
instruction(0xBD49, "BTST.B", "#7, @H'F76E", [0xF76E]),
],
"call_graph": {
"nodes": [
{"start": 0x4000, "end": 0x4216, "label": "loc_4000"},
{"start": 0xBD0E, "end": 0xBD67, "label": "loc_BD0E"},
],
},
}
def rom_bytes() -> bytes:
rom = bytearray([0xFF] * 0xCB00)
rom[0xC564 : 0xC564 + 0x400] = b"\x00" * 0x400
rom[0xC564 + 0x015 * 2 : 0xC564 + 0x015 * 2 + 2] = b"\x40\xAA"
rom[0xC966:0xC968] = b"\x6B\x6F"
rom[0xC968:0xC96A] = b"\xFE\x00"
rom[0xCA0E:0xCA10] = b"\x80\x00"
return bytes(rom)
class EepromLayoutTest(unittest.TestCase):
def test_analysis_extracts_factory_defaults_records_and_selector_mapping(self):
with tempfile.TemporaryDirectory() as tmp:
rom_path = Path(tmp) / "rom.bin"
rom_path.write_bytes(rom_bytes())
analysis = analyze_eeprom_layout(payload(), rom_path=rom_path)
self.assertEqual(analysis["kind"], "eeprom_layout")
self.assertEqual(analysis["factory_defaults"]["entries"][1]["factory_word_hex"], "0x6B6F")
self.assertEqual(analysis["persistent_records"][3]["ram_base_hex"], "H'F7C8")
self.assertEqual(analysis["persistent_records"][3]["eeprom_base_hex"], "0x300")
mapped = analysis["serial_persistence_mapping"]["entries"][0]
self.assertEqual(mapped["selector_hex"], "0x015")
self.assertEqual(mapped["shadow_address_hex"], "H'F4AA")
self.assertEqual(mapped["eeprom_word_offset_hex"], "0xAA")
self.assertEqual(mapped["factory_word_hex"], "0x8000")
def test_text_report_mentions_core_layout(self):
with tempfile.TemporaryDirectory() as tmp:
rom_path = Path(tmp) / "rom.bin"
rom_path.write_bytes(rom_bytes())
analysis = analyze_eeprom_layout(payload(), rom_path=rom_path)
text = format_text_report(analysis)
self.assertIn("Persistent 8-Byte Records", text)
self.assertIn("selector 0x015", text)
self.assertIn("F76E", text)
def test_write_json_output(self):
with tempfile.TemporaryDirectory() as tmp:
input_path = Path(tmp) / "rom.json"
output_path = Path(tmp) / "eeprom.json"
rom_path = Path(tmp) / "rom.bin"
input_path.write_text(json.dumps(payload()), encoding="utf-8")
rom_path.write_bytes(rom_bytes())
write_eeprom_layout(input_path, output_path, rom_path=rom_path, as_json=True)
written = json.loads(output_path.read_text(encoding="utf-8"))
self.assertEqual(written["kind"], "eeprom_layout")
self.assertEqual(written["summary"]["persistent_record_count"], 16)
def test_cli_writes_text_report(self):
with tempfile.TemporaryDirectory() as tmp:
input_path = Path(tmp) / "rom.json"
output_path = Path(tmp) / "eeprom.txt"
rom_path = Path(tmp) / "rom.bin"
input_path.write_text(json.dumps(payload()), encoding="utf-8")
rom_path.write_bytes(rom_bytes())
stdout = io.StringIO()
rc = main([str(input_path), "--rom", str(rom_path), "--out", str(output_path)], stdout=stdout)
self.assertEqual(rc, 0)
self.assertIn("wrote", stdout.getvalue())
self.assertIn("EEPROM Layout Report", output_path.read_text(encoding="utf-8"))
if __name__ == "__main__":
unittest.main()