173 lines
6.6 KiB
Python
173 lines
6.6 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from .cycles import cycle_comment
|
|
from .formatting import h16, label_for
|
|
from .memory import MEMORY_REGIONS, region_for
|
|
from .model import Instruction
|
|
from .rom import Rom
|
|
from .tables import IO_REGISTERS
|
|
from .vectors import DtcVectorEntry
|
|
|
|
|
|
def _reference_comment(ins: Instruction) -> str:
|
|
parts: list[str] = []
|
|
for address in ins.references:
|
|
region = region_for(address)
|
|
name = IO_REGISTERS.get(address, h16(address))
|
|
parts.append(f"{name} in {region.name}")
|
|
return "refs " + ", ".join(parts) if parts else ""
|
|
|
|
|
|
def format_listing(
|
|
rom_path: Path,
|
|
rom: Rom,
|
|
instructions: dict[int, Instruction],
|
|
vectors: dict[int, tuple[str, int]],
|
|
labels: dict[int, str],
|
|
mode: str,
|
|
traced: bool,
|
|
dtc_vectors: dict[int, DtcVectorEntry] | None = None,
|
|
data_candidates: dict[str, list[dict[str, object]]] | None = None,
|
|
show_cycles: bool = False,
|
|
) -> str:
|
|
lines: list[str] = []
|
|
lines.append("; H8/536 ROM disassembly")
|
|
lines.append(f"; input: {rom_path}")
|
|
lines.append(f"; bytes: {len(rom.data)}")
|
|
lines.append(f"; vector mode: {mode}")
|
|
lines.append(f"; analysis: {'recursive trace from vectors' if traced else 'linear sweep'}")
|
|
lines.append(";")
|
|
lines.append("; Notes from the manual:")
|
|
lines.append("; - H8/536 uses the H8/500 CPU instruction set.")
|
|
lines.append("; - In minimum mode the reset vector at H'0000-H'0001 is a 16-bit PC.")
|
|
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.")
|
|
if show_cycles:
|
|
lines.append("; - Cycle counts use Appendix A tables A-7/A-8 for on-chip access with no external wait states.")
|
|
lines.append("")
|
|
lines.append("; Memory Map")
|
|
for region in MEMORY_REGIONS:
|
|
lines.append(f"; {h16(region.start)}-{h16(region.end)} {region.name:<18} {region.kind}")
|
|
lines.append("")
|
|
lines.append("; Vectors")
|
|
for vector_addr, (name, target) in sorted(vectors.items()):
|
|
target_name = labels.get(target, label_for(target))
|
|
lines.append(f"; {h16(vector_addr)} {name:<24} -> {target_name} ({h16(target)})")
|
|
lines.append("")
|
|
if dtc_vectors:
|
|
lines.append("; DTC Vectors")
|
|
for vector_addr, entry in sorted(dtc_vectors.items()):
|
|
target = entry["register_info_address"]
|
|
lines.append(f"; {h16(vector_addr)} {entry['source']:<24} -> {h16(target)}")
|
|
lines.append("")
|
|
|
|
if data_candidates:
|
|
strings = data_candidates.get("strings", [])
|
|
pointer_tables = data_candidates.get("pointer_tables", [])
|
|
if strings or pointer_tables:
|
|
lines.append("; Unreached Data Candidates")
|
|
for item in strings[:40]:
|
|
lines.append(
|
|
f"; string {h16(int(item['address'])):<8} len={item['length']:<3} {item['text']!r}",
|
|
)
|
|
for item in pointer_tables[:40]:
|
|
targets = ", ".join(h16(int(target)) for target in item["targets"][:8])
|
|
suffix = " ..." if int(item["count"]) > 8 else ""
|
|
lines.append(
|
|
f"; ptrtbl {h16(int(item['address'])):<8} count={item['count']:<3} -> {targets}{suffix}",
|
|
)
|
|
lines.append("")
|
|
|
|
for address in sorted(instructions):
|
|
ins = instructions[address]
|
|
if address in labels:
|
|
lines.append("")
|
|
lines.append(f"{labels[address]}:")
|
|
raw = " ".join(f"{byte:02X}" for byte in ins.raw)
|
|
padded_raw = raw.ljust(14)
|
|
comment_parts = [
|
|
part
|
|
for part in (
|
|
ins.comment,
|
|
_reference_comment(ins) if not ins.comment else "",
|
|
cycle_comment(ins.cycles) if show_cycles else "",
|
|
)
|
|
if part
|
|
]
|
|
comment = f" ; {'; '.join(comment_parts)}" if comment_parts else ""
|
|
lines.append(f"{address:04X}: {padded_raw} {ins.text}{comment}")
|
|
lines.append("")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def write_json(
|
|
path: Path,
|
|
instructions: dict[int, Instruction],
|
|
vectors: dict[int, tuple[str, int]],
|
|
labels: dict[int, str],
|
|
dtc_vectors: dict[int, DtcVectorEntry] | None = None,
|
|
data_candidates: dict[str, list[dict[str, object]]] | None = None,
|
|
call_graph: dict[str, object] | None = None,
|
|
) -> None:
|
|
payload = {
|
|
"vectors": [
|
|
{"address": addr, "name": name, "target": target, "target_label": labels.get(target)}
|
|
for addr, (name, target) in sorted(vectors.items())
|
|
],
|
|
"dtc_vectors": list((dtc_vectors or {}).values()),
|
|
"memory_regions": [
|
|
{
|
|
"name": region.name,
|
|
"start": region.start,
|
|
"end": region.end,
|
|
"kind": region.kind,
|
|
"manual": region.manual,
|
|
}
|
|
for region in MEMORY_REGIONS
|
|
],
|
|
"data_candidates": data_candidates or {"strings": [], "pointer_tables": []},
|
|
"call_graph": call_graph or {"nodes": [], "edges": []},
|
|
"instructions": [
|
|
{
|
|
"address": ins.address,
|
|
"address_region": region_for(ins.address).name,
|
|
"bytes": ins.raw.hex().upper(),
|
|
"text": ins.text,
|
|
"mnemonic": ins.mnemonic,
|
|
"operands": ins.operands,
|
|
"kind": ins.kind,
|
|
"targets": ins.targets,
|
|
"cycles": ins.cycles,
|
|
"references": [
|
|
{
|
|
"address": address,
|
|
"name": IO_REGISTERS.get(address),
|
|
"region": region_for(address).name,
|
|
"kind": region_for(address).kind,
|
|
}
|
|
for address in ins.references
|
|
],
|
|
"comment": ins.comment,
|
|
"valid": ins.valid,
|
|
}
|
|
for ins in (instructions[addr] for addr in sorted(instructions))
|
|
],
|
|
}
|
|
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
|
|
|
|
|
def format_callgraph_dot(call_graph: dict[str, object]) -> str:
|
|
lines = ["digraph callgraph {"]
|
|
lines.append(' graph [rankdir="LR"];')
|
|
for node in call_graph.get("nodes", []):
|
|
label = node["label"]
|
|
lines.append(f' "{label}" [label="{label}\\n{h16(int(node["start"]))}"];')
|
|
for edge in call_graph.get("edges", []):
|
|
lines.append(f' "{edge["from_label"]}" -> "{edge["to_label"]}" [label="{h16(int(edge["call_site"]))}"];')
|
|
lines.append("}")
|
|
lines.append("")
|
|
return "\n".join(lines)
|