DTC and SCI improvements
This commit is contained in:
123
h8536/render.py
123
h8536/render.py
@@ -4,14 +4,57 @@ import json
|
||||
from pathlib import Path
|
||||
|
||||
from .cycles import cycle_comment
|
||||
from .dtc import DtcEndpointInfo, DtcRegisterInfo
|
||||
from .formatting import h16, label_for
|
||||
from .memory import MEMORY_REGIONS, region_for
|
||||
from .model import Instruction
|
||||
from .peripheral_access import (
|
||||
peripheral_comment_for_instruction,
|
||||
peripheral_json_payload,
|
||||
peripheral_metadata_for_instruction,
|
||||
)
|
||||
from .rom import Rom
|
||||
from .sci import sci_comment_for_instruction, sci_json_payload, sci_metadata_for_instruction
|
||||
from .tables import IO_REGISTERS
|
||||
from .timing import format_timing_summary
|
||||
from .vectors import DtcVectorEntry
|
||||
|
||||
|
||||
def _dtc_endpoint_text(endpoint: DtcEndpointInfo) -> str:
|
||||
address = endpoint["address"]
|
||||
text = endpoint["text"]
|
||||
return f"{text} ({h16(address)})" if text != h16(address) else text
|
||||
|
||||
|
||||
def _dtc_register_lines(vector_addr: int, entry: DtcVectorEntry, info: DtcRegisterInfo) -> list[str]:
|
||||
target = entry["register_info_address"]
|
||||
if not info.get("valid"):
|
||||
error = info.get("error", "register information unavailable")
|
||||
return [f"; {h16(vector_addr)} {entry['source']:<24} {h16(target)} unavailable: {error}"]
|
||||
|
||||
mode = info["mode"]
|
||||
source = info["source"]
|
||||
destination = info["destination"]
|
||||
count = info["count"]
|
||||
lines = [
|
||||
(
|
||||
f"; {h16(vector_addr)} {entry['source']:<24} {h16(target)} "
|
||||
f"{mode['size']} x{count['transfers']} ({count['bytes']} bytes): "
|
||||
f"{_dtc_endpoint_text(source)} -> {_dtc_endpoint_text(destination)} "
|
||||
f"[src+={mode['source_increment_step']}, dst+={mode['destination_increment_step']}]"
|
||||
),
|
||||
(
|
||||
f"; DTMR={h16(info['dtmr'])} DTSR={h16(info['dtsr'])} "
|
||||
f"DTDR={h16(info['dtdr'])} DTCR={h16(info['dtcr'])}"
|
||||
),
|
||||
]
|
||||
if mode["reserved_set"]:
|
||||
lines.append(f"; warning: DTMR reserved bits set ({h16(mode['reserved'])})")
|
||||
if count["zero_means_65536"]:
|
||||
lines.append("; DTCR raw zero means an initial transfer count of 65536")
|
||||
return lines
|
||||
|
||||
|
||||
def _reference_comment(ins: Instruction) -> str:
|
||||
parts: list[str] = []
|
||||
for address in ins.references:
|
||||
@@ -31,7 +74,10 @@ def format_listing(
|
||||
traced: bool,
|
||||
dtc_vectors: dict[int, DtcVectorEntry] | None = None,
|
||||
data_candidates: dict[str, list[dict[str, object]]] | None = None,
|
||||
timing_summary: dict[str, list[dict[str, object]]] | None = None,
|
||||
show_cycles: bool = False,
|
||||
sci_analysis: dict[str, object] | None = None,
|
||||
peripheral_access: dict[str, object] | None = None,
|
||||
) -> str:
|
||||
lines: list[str] = []
|
||||
lines.append("; H8/536 ROM disassembly")
|
||||
@@ -45,6 +91,9 @@ def format_listing(
|
||||
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("; - SCI baud inference uses section 14.2.8 BRR formulas when SMR/BRR are known.")
|
||||
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:
|
||||
lines.append("; - Cycle counts use Appendix A tables A-7/A-8 for on-chip access with no external wait states.")
|
||||
lines.append("")
|
||||
@@ -63,6 +112,10 @@ def format_listing(
|
||||
target = entry["register_info_address"]
|
||||
lines.append(f"; {h16(vector_addr)} {entry['source']:<24} -> {h16(target)}")
|
||||
lines.append("")
|
||||
lines.append("; DTC Register Information")
|
||||
for vector_addr, entry in sorted(dtc_vectors.items()):
|
||||
lines.extend(_dtc_register_lines(vector_addr, entry, entry["register_info"]))
|
||||
lines.append("")
|
||||
|
||||
if data_candidates:
|
||||
strings = data_candidates.get("strings", [])
|
||||
@@ -81,6 +134,9 @@ def format_listing(
|
||||
)
|
||||
lines.append("")
|
||||
|
||||
if timing_summary:
|
||||
lines.extend(format_timing_summary(timing_summary))
|
||||
|
||||
for address in sorted(instructions):
|
||||
ins = instructions[address]
|
||||
if address in labels:
|
||||
@@ -92,6 +148,8 @@ def format_listing(
|
||||
part
|
||||
for part in (
|
||||
ins.comment,
|
||||
sci_comment_for_instruction(sci_analysis, address),
|
||||
peripheral_comment_for_instruction(peripheral_access, address),
|
||||
_reference_comment(ins) if not ins.comment else "",
|
||||
cycle_comment(ins.cycles) if show_cycles else "",
|
||||
)
|
||||
@@ -111,6 +169,9 @@ def write_json(
|
||||
dtc_vectors: dict[int, DtcVectorEntry] | None = None,
|
||||
data_candidates: dict[str, list[dict[str, object]]] | None = None,
|
||||
call_graph: dict[str, object] | None = None,
|
||||
timing_summary: dict[str, list[dict[str, object]]] | None = None,
|
||||
sci_analysis: dict[str, object] | None = None,
|
||||
peripheral_access: dict[str, object] | None = None,
|
||||
) -> None:
|
||||
payload = {
|
||||
"vectors": [
|
||||
@@ -130,35 +191,53 @@ def write_json(
|
||||
],
|
||||
"data_candidates": data_candidates or {"strings": [], "pointer_tables": []},
|
||||
"call_graph": call_graph or {"nodes": [], "edges": []},
|
||||
"timing_summary": timing_summary or {"blocks": [], "loops": []},
|
||||
"sci": sci_json_payload(sci_analysis),
|
||||
"peripheral_access": peripheral_json_payload(peripheral_access),
|
||||
"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,
|
||||
}
|
||||
_instruction_payload(ins, sci_analysis, peripheral_access)
|
||||
for ins in (instructions[addr] for addr in sorted(instructions))
|
||||
],
|
||||
}
|
||||
path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
|
||||
|
||||
|
||||
def _instruction_payload(
|
||||
ins: Instruction,
|
||||
sci_analysis: dict[str, object] | None = None,
|
||||
peripheral_access: dict[str, object] | None = None,
|
||||
) -> dict[str, object]:
|
||||
payload: dict[str, object] = {
|
||||
"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,
|
||||
}
|
||||
sci_metadata = sci_metadata_for_instruction(sci_analysis, ins.address)
|
||||
if sci_metadata:
|
||||
payload["sci"] = sci_metadata
|
||||
peripheral_metadata = peripheral_metadata_for_instruction(peripheral_access, ins.address)
|
||||
if peripheral_metadata:
|
||||
payload["peripheral_access"] = peripheral_metadata
|
||||
return payload
|
||||
|
||||
|
||||
def format_callgraph_dot(call_graph: dict[str, object]) -> str:
|
||||
lines = ["digraph callgraph {"]
|
||||
lines.append(' graph [rankdir="LR"];')
|
||||
|
||||
Reference in New Issue
Block a user