LCD decompile
This commit is contained in:
120
h8536/render.py
120
h8536/render.py
@@ -8,6 +8,8 @@ from .dataflow import state_for_instruction
|
||||
from .dtc import DtcEndpointInfo, DtcRegisterInfo
|
||||
from .formatting import h16, label_for
|
||||
from .indirect import indirect_comment_for_instruction, indirect_metadata_for_instruction
|
||||
from .lcd_driver import lcd_comment_for_instruction, lcd_metadata_for_instruction
|
||||
from .lcd_text import lcd_text_comment_for_instruction
|
||||
from .memory import MEMORY_REGIONS, region_for
|
||||
from .model import Instruction
|
||||
from .peripheral_access import (
|
||||
@@ -118,6 +120,103 @@ def _dataflow_comment(analysis: dict[str, object] | None, address: int) -> str:
|
||||
return "dataflow " + ", ".join(parts[:4]) + suffix
|
||||
|
||||
|
||||
def _lcd_text_lines(lcd_text: dict[str, object] | None) -> list[str]:
|
||||
if not lcd_text:
|
||||
return []
|
||||
strings = lcd_text.get("strings", [])
|
||||
regions = lcd_text.get("regions", [])
|
||||
searches = lcd_text.get("searches", [])
|
||||
if not strings and not regions and not searches:
|
||||
return []
|
||||
|
||||
lines = ["; LCD/Text Scan"]
|
||||
for search in (searches if isinstance(searches, list) else []):
|
||||
if not isinstance(search, dict):
|
||||
continue
|
||||
hits = len(search.get("literal_hits", [])) + len(search.get("candidate_hits", []))
|
||||
status = "found" if hits else "not literal"
|
||||
lines.append(f"; search {search.get('term')!r}: {status}, hits={hits}")
|
||||
near = search.get("near_matches", [])
|
||||
if isinstance(near, list) and near:
|
||||
sample = ", ".join(f"{h16(int(item['address']))} {item['trimmed']!r}" for item in near[:4])
|
||||
lines.append(f"; near: {sample}")
|
||||
|
||||
if isinstance(regions, list) and regions:
|
||||
lines.append("; LCD text regions")
|
||||
for region in regions[:12]:
|
||||
if not isinstance(region, dict):
|
||||
continue
|
||||
samples = ", ".join(repr(sample) for sample in region.get("samples", [])[:4])
|
||||
lines.append(
|
||||
f"; region {h16(int(region['start']))}-{h16(int(region['end']))} "
|
||||
f"count={region['count']:<3} {samples}",
|
||||
)
|
||||
if len(regions) > 12:
|
||||
lines.append(f"; ... {len(regions) - 12} more LCD text regions")
|
||||
|
||||
if isinstance(strings, list) and strings:
|
||||
lines.append("; LCD text candidates")
|
||||
shown = 0
|
||||
for item in strings:
|
||||
if not isinstance(item, dict):
|
||||
continue
|
||||
if item.get("confidence") == "low" and not item.get("xref_count"):
|
||||
continue
|
||||
xrefs = f" xrefs={item['xref_count']}" if item.get("xref_count") else ""
|
||||
lines.append(
|
||||
f"; text {h16(int(item['address'])):<8} len={item['length']:<3} "
|
||||
f"{item['confidence']:<6} {str(item['trimmed'])!r}{xrefs}",
|
||||
)
|
||||
shown += 1
|
||||
if shown >= 48:
|
||||
break
|
||||
if len(strings) > shown:
|
||||
lines.append(f"; ... {len(strings) - shown} more LCD text candidates")
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def _lcd_driver_lines(lcd_driver: dict[str, object] | None) -> list[str]:
|
||||
if not lcd_driver:
|
||||
return []
|
||||
accesses = lcd_driver.get("accesses", [])
|
||||
loops = lcd_driver.get("polling_loops", [])
|
||||
routines = lcd_driver.get("routines", [])
|
||||
if not accesses and not loops and not routines:
|
||||
return []
|
||||
|
||||
lines = ["; LCD Driver Candidates"]
|
||||
for address_info in lcd_driver.get("addresses", []):
|
||||
if not isinstance(address_info, dict):
|
||||
continue
|
||||
lines.append(
|
||||
f"; {h16(int(address_info['address']))} {address_info['name']:<18} {address_info['role']}",
|
||||
)
|
||||
if isinstance(routines, list) and routines:
|
||||
lines.append("; LCD routines")
|
||||
for routine in routines[:16]:
|
||||
if not isinstance(routine, dict):
|
||||
continue
|
||||
roles = ", ".join(str(role) for role in routine.get("roles", []))
|
||||
lines.append(
|
||||
f"; routine {h16(int(routine['start']))}-{h16(int(routine['end']))} "
|
||||
f"{routine['role_hint']:<24} {roles}",
|
||||
)
|
||||
if len(routines) > 16:
|
||||
lines.append(f"; ... {len(routines) - 16} more LCD routines")
|
||||
if isinstance(loops, list) and loops:
|
||||
lines.append("; LCD busy loops")
|
||||
for loop in loops[:16]:
|
||||
if not isinstance(loop, dict):
|
||||
continue
|
||||
lines.append(
|
||||
f"; loop {h16(int(loop['read_address']))}->{h16(int(loop['branch_address']))} "
|
||||
f"{loop['summary']}",
|
||||
)
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def format_listing(
|
||||
rom_path: Path,
|
||||
rom: Rom,
|
||||
@@ -135,6 +234,8 @@ def format_listing(
|
||||
indirect_flow: dict[str, object] | None = None,
|
||||
dataflow: dict[str, object] | None = None,
|
||||
symbols: dict[str, object] | None = None,
|
||||
lcd_text: dict[str, object] | None = None,
|
||||
lcd_driver: dict[str, object] | None = None,
|
||||
) -> str:
|
||||
lines: list[str] = []
|
||||
lines.append("; H8/536 ROM disassembly")
|
||||
@@ -149,6 +250,7 @@ def format_listing(
|
||||
lines.append("; - The register field is H'FE80-H'FFFF; names below come from appendix B.")
|
||||
lines.append("; - @aa:8 short absolute operands use BR as the upper address byte.")
|
||||
lines.append("; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.")
|
||||
lines.append("; - LCD inference treats E-clock H'F200/H'F201 accesses as status/control and data candidates.")
|
||||
if sci_analysis and sci_analysis.get("clock_hz") is None:
|
||||
lines.append("; - Pass --clock-hz to convert SCI BRR settings into numeric baud rates.")
|
||||
if show_cycles:
|
||||
@@ -192,6 +294,8 @@ def format_listing(
|
||||
lines.append("")
|
||||
|
||||
lines.extend(_symbol_lines(symbols))
|
||||
lines.extend(_lcd_text_lines(lcd_text))
|
||||
lines.extend(_lcd_driver_lines(lcd_driver))
|
||||
|
||||
if timing_summary:
|
||||
lines.extend(format_timing_summary(timing_summary))
|
||||
@@ -210,6 +314,8 @@ def format_listing(
|
||||
sci_comment_for_instruction(sci_analysis, address),
|
||||
peripheral_comment_for_instruction(peripheral_access, address),
|
||||
indirect_comment_for_instruction(indirect_flow, address),
|
||||
lcd_text_comment_for_instruction(lcd_text, address),
|
||||
lcd_comment_for_instruction(lcd_driver, address),
|
||||
_dataflow_comment(dataflow, address),
|
||||
_reference_comment(ins, symbols) if not ins.comment else "",
|
||||
cycle_comment(ins.cycles) if show_cycles else "",
|
||||
@@ -236,6 +342,8 @@ def write_json(
|
||||
indirect_flow: dict[str, object] | None = None,
|
||||
dataflow: dict[str, object] | None = None,
|
||||
symbols: dict[str, object] | None = None,
|
||||
lcd_text: dict[str, object] | None = None,
|
||||
lcd_driver: dict[str, object] | None = None,
|
||||
) -> None:
|
||||
payload = {
|
||||
"vectors": [
|
||||
@@ -261,8 +369,10 @@ def write_json(
|
||||
"indirect_flow": indirect_flow or {"sites": []},
|
||||
"dataflow": _dataflow_json_payload(dataflow),
|
||||
"symbols": symbols or {"symbols": [], "by_address": {}},
|
||||
"lcd_text": lcd_text or {"strings": [], "regions": [], "searches": []},
|
||||
"lcd_driver": lcd_driver or {"accesses": [], "polling_loops": [], "routines": []},
|
||||
"instructions": [
|
||||
_instruction_payload(ins, sci_analysis, peripheral_access, indirect_flow, dataflow, symbols)
|
||||
_instruction_payload(ins, sci_analysis, peripheral_access, indirect_flow, dataflow, symbols, lcd_text, lcd_driver)
|
||||
for ins in (instructions[addr] for addr in sorted(instructions))
|
||||
],
|
||||
}
|
||||
@@ -319,6 +429,8 @@ def _instruction_payload(
|
||||
indirect_flow: dict[str, object] | None = None,
|
||||
dataflow: dict[str, object] | None = None,
|
||||
symbols: dict[str, object] | None = None,
|
||||
lcd_text: dict[str, object] | None = None,
|
||||
lcd_driver: dict[str, object] | None = None,
|
||||
) -> dict[str, object]:
|
||||
payload: dict[str, object] = {
|
||||
"address": ins.address,
|
||||
@@ -355,6 +467,12 @@ def _instruction_payload(
|
||||
dataflow_metadata = _dataflow_instruction_payload(dataflow, ins.address)
|
||||
if dataflow_metadata:
|
||||
payload["dataflow"] = dataflow_metadata
|
||||
lcd_text_comment = lcd_text_comment_for_instruction(lcd_text, ins.address)
|
||||
if lcd_text_comment:
|
||||
payload["lcd_text"] = {"comment": lcd_text_comment}
|
||||
lcd_driver_metadata = lcd_metadata_for_instruction(lcd_driver, ins.address)
|
||||
if lcd_driver_metadata:
|
||||
payload["lcd_driver"] = lcd_driver_metadata
|
||||
return payload
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user