More ccu based mining
This commit is contained in:
118
tests/test_ccu_seed_hints.py
Normal file
118
tests/test_ccu_seed_hints.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from h8536.ccu_seed_hints import (
|
||||
analyze_ccu_seed_hints,
|
||||
checksum,
|
||||
encode_host_frame,
|
||||
frame_hex,
|
||||
main,
|
||||
selector_bytes,
|
||||
write_ccu_seed_hints,
|
||||
)
|
||||
|
||||
|
||||
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(0xC000, "MOV:G.W", "#H'0006, R3"),
|
||||
instruction(0xC004, "MOV:G.W", "@(-H'2000,R3), R0"),
|
||||
instruction(0xC008, "MOV:G.W", "R1, @H'E1EC", [0xE1EC]),
|
||||
],
|
||||
"call_graph": {
|
||||
"nodes": [
|
||||
{"start": 0xC000, "end": 0xC0FF, "label": "loc_C000"},
|
||||
],
|
||||
},
|
||||
"indirect_flow": {
|
||||
"sites": [
|
||||
{
|
||||
"table": {
|
||||
"base": 0x28A6,
|
||||
"entries": [
|
||||
{"index": 0, "entry_address": 0x28A6, "target": 0x2CB9, "target_label": "loc_2CB9"},
|
||||
{"index": 1, "entry_address": 0x28A8, "target": 0x1234, "target_label": "loc_1234"},
|
||||
{"index": 2, "entry_address": 0x28AA, "target": 0x1234, "target_label": "loc_1234"},
|
||||
],
|
||||
},
|
||||
}
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class CcuSeedHintsTest(unittest.TestCase):
|
||||
def test_selector_encoding_matches_loc_622b_ranges(self):
|
||||
self.assertEqual(selector_bytes(0x000), (0x00, 0x00))
|
||||
self.assertEqual(selector_bytes(0x07F), (0x00, 0x7F))
|
||||
self.assertEqual(selector_bytes(0x080), (0x01, 0x00))
|
||||
self.assertEqual(selector_bytes(0x17F), (0x01, 0xFF))
|
||||
self.assertEqual(selector_bytes(0x180), (0x02, 0x00))
|
||||
self.assertEqual(selector_bytes(0x1FF), (0x02, 0x7F))
|
||||
|
||||
def test_frame_encoding_uses_xor_seed(self):
|
||||
frame = encode_host_frame(0x00, 0x000, 0x8080)
|
||||
|
||||
self.assertEqual(frame, [0x00, 0x00, 0x00, 0x80, 0x80, 0x5A])
|
||||
self.assertEqual(checksum(frame[:5]), frame[5])
|
||||
self.assertEqual(frame_hex(frame), "00 00 00 80 80 5A")
|
||||
|
||||
def test_analysis_emits_seed_plan_and_selector_reasons(self):
|
||||
analysis = analyze_ccu_seed_hints(payload(), rom_path=None)
|
||||
by_selector = {
|
||||
int(item["selector"]): item
|
||||
for item in analysis["selector_candidates"]
|
||||
}
|
||||
|
||||
self.assertEqual(analysis["kind"], "ccu_seed_hints")
|
||||
self.assertIn(0x000, by_selector)
|
||||
self.assertIn(0x0F6, by_selector)
|
||||
self.assertIn("00 00 00 80 80 5A", [step["frame"] for step in analysis["seed_plan"]["steps"]])
|
||||
self.assertIn("01 01 76 00 00 2C", [step["readback_frame"] for step in analysis["seed_plan"]["steps"]])
|
||||
|
||||
def test_write_json_output(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "hints.json"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
|
||||
write_ccu_seed_hints(input_path, output_path, rom_path=None, as_json=True)
|
||||
|
||||
written = json.loads(output_path.read_text(encoding="utf-8"))
|
||||
self.assertEqual(written["kind"], "ccu_seed_hints")
|
||||
self.assertGreaterEqual(written["summary"]["candidate_count"], 1)
|
||||
|
||||
def test_cli_writes_text_report(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
input_path = Path(tmp) / "rom.json"
|
||||
output_path = Path(tmp) / "hints.txt"
|
||||
input_path.write_text(json.dumps(payload()), encoding="utf-8")
|
||||
|
||||
stdout = io.StringIO()
|
||||
rc = main([str(input_path), "--rom", str(Path(tmp) / "missing.bin"), "--out", str(output_path)], stdout=stdout)
|
||||
|
||||
self.assertEqual(rc, 0)
|
||||
self.assertIn("wrote", stdout.getvalue())
|
||||
self.assertIn("Candidate Fake-CCU Seed Plan", output_path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user