Updates
This commit is contained in:
@@ -4,11 +4,22 @@ import json
|
||||
from pathlib import Path
|
||||
|
||||
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,
|
||||
@@ -18,6 +29,7 @@ def format_listing(
|
||||
mode: str,
|
||||
traced: bool,
|
||||
dtc_vectors: dict[int, DtcVectorEntry] | None = None,
|
||||
data_candidates: dict[str, list[dict[str, object]]] | None = None,
|
||||
) -> str:
|
||||
lines: list[str] = []
|
||||
lines.append("; H8/536 ROM disassembly")
|
||||
@@ -30,6 +42,11 @@ def format_listing(
|
||||
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.")
|
||||
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()):
|
||||
@@ -43,6 +60,23 @@ def format_listing(
|
||||
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:
|
||||
@@ -50,7 +84,8 @@ def format_listing(
|
||||
lines.append(f"{labels[address]}:")
|
||||
raw = " ".join(f"{byte:02X}" for byte in ins.raw)
|
||||
padded_raw = raw.ljust(14)
|
||||
comment = f" ; {ins.comment}" if ins.comment else ""
|
||||
comment_parts = [part for part in (ins.comment, _reference_comment(ins) if not ins.comment 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)
|
||||
@@ -62,6 +97,8 @@ def write_json(
|
||||
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": [
|
||||
@@ -69,15 +106,37 @@ def write_json(
|
||||
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,
|
||||
"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,
|
||||
}
|
||||
@@ -85,3 +144,16 @@ def write_json(
|
||||
],
|
||||
}
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user