1
0

LCD decompile

This commit is contained in:
Aiden
2026-05-25 15:10:32 +10:00
parent 1d7f00e59c
commit cdfb811c28
15 changed files with 8836 additions and 19 deletions

521
h8536/board_profile.py Normal file
View File

@@ -0,0 +1,521 @@
from __future__ import annotations
from collections.abc import Mapping
from .formatting import parse_int
from .model import Instruction
SCI_REGISTERS: dict[str, dict[str, int]] = {
"SCI1": {
"SMR": 0xFED8,
"BRR": 0xFED9,
"SCR": 0xFEDA,
"TDR": 0xFEDB,
"SSR": 0xFEDC,
"RDR": 0xFEDD,
},
"SCI2": {
"SMR": 0xFEF0,
"BRR": 0xFEF1,
"SCR": 0xFEF2,
"TDR": 0xFEF3,
"SSR": 0xFEF4,
"RDR": 0xFEF5,
},
}
SCI_REGISTER_BY_ADDRESS = {
address: (channel, register)
for channel, registers in SCI_REGISTERS.items()
for register, address in registers.items()
}
SYSCR2_ADDRESS = 0xFEFD
SYSCR2_INITIAL = 0x80
P9SCI2E_BIT = 0
SCR_INITIAL = 0x0C
MANUAL_REFERENCES = [
"Manual/0900766b802125d0.md:2417 FP-80 H8/536 pin 66 is P95/TXD",
"Manual/0900766b802125d0.md:2418 FP-80 H8/536 pin 67 is P96/RXD",
"Manual/0900766b802125d0.md:11192 Port 9 carries SCI1 and SCI2 serial signals",
"Manual/0900766b802125d0.md:11201 P96 is RXD1 input",
"Manual/0900766b802125d0.md:11202 P95 is TXD1 output",
"Manual/0900766b802125d0.md:15725 SCI1 RXD input pin",
"Manual/0900766b802125d0.md:15726 SCI1 TXD output pin",
"Manual/0900766b802125d0.md:15750 SCI register table starts with SCI1 RDR/TDR/SMR/SCR/SSR/BRR",
"Manual/0900766b802125d0.md:15758 SCI register table lists SCI2 RDR/TDR/SMR/SCR/SSR/BRR",
"Manual/0900766b802125d0.md:15794 RDR receive data register",
"Manual/0900766b802125d0.md:15823 TDR transmit data register",
"Manual/0900766b802125d0.md:15969 SCR enables and disables SCI functions",
"Manual/0900766b802125d0.md:16009 SCR.TE makes the TXD pin output",
"Manual/0900766b802125d0.md:16029 SCR.RE makes the RXD pin input",
"Manual/0900766b802125d0.md:16090 SSR contains transmit/receive status flags",
"Manual/0900766b802125d0.md:10560 SYSCR2 controls port 9 pin functions",
"Manual/0900766b802125d0.md:10631 SYSCR2.P9SCI2E controls the SCI2 functions of P92-P94",
]
BOARD_PROFILES = {
"sony_rcp_tx7": {
"name": "Sony RCP-TX7",
"summary": "Board trace ties the H8/536 SCI1 pins to a MAX202 RS232 transceiver.",
"manual_references": MANUAL_REFERENCES,
"traces": [
{
"channel": "SCI1",
"signal": "TXD",
"h8_pin": 66,
"h8_pin_name": "P95/TXD",
"h8_function": "TXD1",
"max202_pin": 11,
"evidence": "MAX202 pin 11 traces to H8 pin 66",
},
{
"channel": "SCI1",
"signal": "RXD",
"h8_pin": 67,
"h8_pin_name": "P96/RXD",
"h8_function": "RXD1",
"max202_pin": 12,
"evidence": "MAX202 pin 12 traces to H8 pin 67",
},
],
},
}
_READ_ONLY_ROOTS = {"BTST", "CMP", "CMP:E", "CMP:G", "CMP:I", "MOVFPE", "TST"}
_BIT_WRITE_ROOTS = {"BCLR", "BNOT", "BSET"}
def analyze_board_profile(
instructions: Mapping[int, Instruction],
board: str = "sony_rcp_tx7",
) -> dict[str, object]:
"""Annotate board-specific serial-path accesses without changing disassembly output."""
if board not in BOARD_PROFILES:
raise ValueError(f"unsupported board profile: {board}")
profile = BOARD_PROFILES[board]
annotations: dict[int, str] = {}
instruction_metadata: dict[int, dict[str, object]] = {}
channels = _initial_channel_payload(profile)
register_values: dict[int, int | None] = {
SYSCR2_ADDRESS: SYSCR2_INITIAL,
SCI_REGISTERS["SCI1"]["SCR"]: SCR_INITIAL,
SCI_REGISTERS["SCI2"]["SCR"]: SCR_INITIAL,
}
for address in sorted(instructions):
ins = instructions[address]
access_kind = _access_kind(ins)
access_records: list[dict[str, object]] = []
comments: list[str] = []
if SYSCR2_ADDRESS in _expanded_references(ins, access_kind):
value = _write_value(ins, SYSCR2_ADDRESS, register_values.get(SYSCR2_ADDRESS), 1, 0)
if access_kind == "write":
register_values[SYSCR2_ADDRESS] = value
record = _syscr2_access(ins, access_kind, register_values.get(SYSCR2_ADDRESS))
access_records.append(record)
comments.append(str(record["comment"]))
for target in _sci_targets(ins, access_kind):
register_address = int(target["register_address"])
value = _write_value(
ins,
register_address,
register_values.get(register_address),
int(target["width"]),
int(target["index"]),
)
if access_kind == "write":
register_values[register_address] = value
channel = str(target["channel"])
register = str(target["register"])
p9sci2e = _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT)
record = _sci_access(
ins,
channel,
register,
register_address,
access_kind,
value,
register_values.get(register_address),
p9sci2e,
)
access_records.append(record)
comments.append(str(record["comment"]))
channel_payload = channels[channel]
channel_accesses = channel_payload["accesses"]
if isinstance(channel_accesses, list):
channel_accesses.append(record)
if register == "SCR":
channel_payload["scr"] = _scr_metadata(register_values.get(register_address))
if access_records:
comment = "; ".join(_dedupe(comments))
annotations[ins.address] = comment
instruction_metadata[ins.address] = {
"accesses": access_records,
"comment": comment,
}
channels["SCI2"]["p9sci2e"] = _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT)
return {
"board": board,
"name": profile["name"],
"summary": profile["summary"],
"manual_references": list(profile["manual_references"]),
"traces": [dict(trace) for trace in profile["traces"]],
"channels": channels,
"annotations": annotations,
"instructions": instruction_metadata,
"state": {
"SYSCR2": _register_state(register_values.get(SYSCR2_ADDRESS)),
"P9SCI2E": _bit_state(register_values.get(SYSCR2_ADDRESS), P9SCI2E_BIT),
},
}
def board_comment_for_instruction(analysis: Mapping[str, object] | None, address: int) -> str:
if not analysis:
return ""
annotations = analysis.get("annotations")
if not isinstance(annotations, Mapping):
return ""
comment = annotations.get(address)
if comment is None:
comment = annotations.get(str(address))
return str(comment) if comment else ""
def board_metadata_for_instruction(
analysis: Mapping[str, object] | None,
address: int,
) -> dict[str, object] | None:
if not analysis:
return None
instructions = analysis.get("instructions")
if not isinstance(instructions, Mapping):
return None
metadata = instructions.get(address)
if metadata is None:
metadata = instructions.get(str(address))
return metadata if isinstance(metadata, dict) else None
def _initial_channel_payload(profile: Mapping[str, object]) -> dict[str, dict[str, object]]:
traces = [dict(trace) for trace in profile["traces"] if isinstance(trace, Mapping)]
return {
"SCI1": {
"traced_to_max202": True,
"path": "RS232/MAX202",
"pins": traces,
"scr": _scr_metadata(SCR_INITIAL),
"accesses": [],
},
"SCI2": {
"traced_to_max202": False,
"path": None,
"note": "Sony RCP-TX7 MAX202 board traces are on SCI1 P95/P96, not SCI2 P92/P93.",
"p9sci2e": False,
"scr": _scr_metadata(SCR_INITIAL),
"accesses": [],
},
}
def _syscr2_access(ins: Instruction, access: str, value: int | None) -> dict[str, object]:
p9sci2e = _bit_state(value, P9SCI2E_BIT)
comment = _syscr2_comment(access, p9sci2e)
record: dict[str, object] = {
"address": ins.address,
"instruction": ins.text,
"register": "SYSCR2",
"register_address": SYSCR2_ADDRESS,
"access": access,
"p9sci2e": p9sci2e,
"comment": comment,
}
if value is not None:
record["value"] = value
record["value_hex"] = _hex8(value)
return record
def _sci_access(
ins: Instruction,
channel: str,
register: str,
register_address: int,
access: str,
value: int | None,
state_value: int | None,
p9sci2e: bool | None,
) -> dict[str, object]:
comment = _sci_comment(channel, register, access, value if value is not None else state_value, p9sci2e)
record: dict[str, object] = {
"address": ins.address,
"instruction": ins.text,
"channel": channel,
"register": register,
"register_address": register_address,
"access": access,
"traced_to_max202": channel == "SCI1",
"comment": comment,
}
if value is not None:
record["value"] = value
record["value_hex"] = _hex8(value)
if register == "SCR":
record["scr"] = _scr_metadata(value if value is not None else state_value)
if channel == "SCI2":
record["p9sci2e"] = p9sci2e
return record
def _sci_comment(
channel: str,
register: str,
access: str,
value: int | None,
p9sci2e: bool | None,
) -> str:
if channel == "SCI1":
return _sci1_comment(register, access, value)
return _sci2_comment(register, access, p9sci2e)
def _sci1_comment(register: str, access: str, value: int | None) -> str:
if register in {"SMR", "BRR"}:
return (
f"SCI1 {register} serial init for traced RS232/MAX202 path "
"(H8 pin 66 P95/TXD to MAX202 pin 11; MAX202 pin 12 to H8 pin 67 P96/RXD)"
)
if register == "SCR":
bits = _scr_bits_text(value)
suffix = f" {bits}" if bits else ""
return (
f"SCI1 SCR {access}{suffix}; TE/RE select the traced RS232/MAX202 pins "
"(P95/TXD pin 66 to MAX202 pin 11, P96/RXD pin 67 to MAX202 pin 12)"
)
if register == "TDR":
verb = "transmits" if access == "write" else "accesses transmit buffer"
return (
f"SCI1 TDR {access} {verb} on traced RS232/MAX202 path: "
"H8 pin 66 P95/TXD -> MAX202 pin 11"
)
if register == "RDR":
verb = "receives" if access == "read" else "accesses receive buffer"
return (
f"SCI1 RDR {access} {verb} from traced RS232/MAX202 path: "
"MAX202 pin 12 -> H8 pin 67 P96/RXD"
)
if register == "SSR":
return "SCI1 SSR status for traced RS232/MAX202 path; TDRE/RDRF/error flags gate TDR/RDR use"
return f"SCI1 {register} {access} on traced RS232/MAX202 path"
def _sci2_comment(register: str, access: str, p9sci2e: bool | None) -> str:
prefix = f"SCI2 {register} {access}; not the traced MAX202 path"
if p9sci2e is False:
return (
f"{prefix}; P9SCI2E=0 disables SCI2 pins P92/P93/P94, "
"while the board trace is SCI1 P95/P96"
)
if p9sci2e is True:
return f"{prefix}; P9SCI2E=1 may route SCI2 pins, but the board trace is SCI1 P95/P96"
return f"{prefix}; P9SCI2E state unknown, and the board trace is SCI1 P95/P96"
def _syscr2_comment(access: str, p9sci2e: bool | None) -> str:
if p9sci2e is False:
return (
f"SYSCR2 {access} leaves P9SCI2E=0; SCI2 pins are disabled, "
"so SCI2 is not the traced MAX202 path; traced RS232/MAX202 remains SCI1 P95/P96"
)
if p9sci2e is True:
return (
f"SYSCR2 {access} sets P9SCI2E=1; SCI2 pins may be enabled, "
"but Sony RCP-TX7 MAX202 traces are SCI1 P95/P96"
)
return f"SYSCR2 {access}; P9SCI2E unknown, board trace remains SCI1 P95/P96"
def _sci_targets(ins: Instruction, access: str) -> list[dict[str, object]]:
width = _mnemonic_width(ins.mnemonic)
targets: list[dict[str, object]] = []
seen: set[int] = set()
for base_address in ins.references:
target_width = width if _operand_references_memory(ins, access) else 1
for index in range(target_width):
register_address = base_address + index
if register_address in seen:
continue
register_info = SCI_REGISTER_BY_ADDRESS.get(register_address)
if register_info is None:
continue
seen.add(register_address)
channel, register = register_info
targets.append(
{
"channel": channel,
"register": register,
"register_address": register_address,
"width": target_width,
"index": index,
},
)
return targets
def _expanded_references(ins: Instruction, access: str) -> set[int]:
width = _mnemonic_width(ins.mnemonic)
target_width = width if _operand_references_memory(ins, access) else 1
addresses: set[int] = set()
for base_address in ins.references:
for index in range(target_width):
addresses.add(base_address + index)
return addresses
def _operand_references_memory(ins: Instruction, access: str) -> bool:
operand = _destination_operand(ins.operands) if access == "write" else _source_operand(ins.operands)
return bool(operand and operand.startswith("@"))
def _access_kind(ins: Instruction) -> str:
root = _mnemonic_root(ins.mnemonic)
if root in _READ_ONLY_ROOTS:
return "read"
destination = _destination_operand(ins.operands)
if destination and destination.startswith("@"):
return "write"
return "read"
def _write_value(
ins: Instruction,
register_address: int,
current_value: int | None,
width: int,
index: int,
) -> int | None:
if _access_kind(ins) != "write":
return None
root = _mnemonic_root(ins.mnemonic)
if root == "CLR":
return 0
if root in _BIT_WRITE_ROOTS:
bit = _immediate_bit(ins.operands)
if bit is None or current_value is None:
return None
if root == "BSET":
return (current_value | (1 << bit)) & 0xFF
if root == "BCLR":
return (current_value & ~(1 << bit)) & 0xFF
return (current_value ^ (1 << bit)) & 0xFF
value = _immediate_source_value(ins.operands)
if value is None:
return None
return _byte_for_target(value, width, index)
def _destination_operand(operands: str) -> str | None:
operands = operands.strip()
if not operands:
return None
if "," not in operands:
return operands
return operands.rsplit(",", 1)[1].strip()
def _source_operand(operands: str) -> str | None:
operands = operands.strip()
if not operands:
return None
if "," not in operands:
return operands
return operands.rsplit(",", 1)[0].strip()
def _immediate_source_value(operands: str) -> int | None:
source = _source_operand(operands) or ""
if not source.startswith("#"):
return None
try:
return parse_int(source[1:])
except ValueError:
return None
def _immediate_bit(operands: str) -> int | None:
value = _immediate_source_value(operands)
if value is None:
return None
return value if 0 <= value <= 7 else None
def _mnemonic_root(mnemonic: str) -> str:
return mnemonic.split(".", 1)[0]
def _mnemonic_width(mnemonic: str) -> int:
return 2 if mnemonic.endswith(".W") else 1
def _byte_for_target(value: int, width: int, index: int) -> int:
shift = 8 * (width - index - 1)
return (value >> shift) & 0xFF
def _scr_metadata(value: int | None) -> dict[str, object]:
metadata = _register_state(value)
metadata.update(
{
"tie": None if value is None else bool(value & 0x80),
"rie": None if value is None else bool(value & 0x40),
"tx_enabled": None if value is None else bool(value & 0x20),
"rx_enabled": None if value is None else bool(value & 0x10),
},
)
return metadata
def _register_state(value: int | None) -> dict[str, object]:
return {
"value": value,
"value_hex": None if value is None else _hex8(value),
}
def _scr_bits_text(value: int | None) -> str:
if value is None:
return ""
return f"TE={1 if value & 0x20 else 0} RE={1 if value & 0x10 else 0}"
def _bit_state(value: int | None, bit: int) -> bool | None:
if value is None:
return None
return bool(value & (1 << bit))
def _dedupe(items: list[str]) -> list[str]:
seen: set[str] = set()
output: list[str] = []
for item in items:
if item in seen:
continue
seen.add(item)
output.append(item)
return output
def _hex8(value: int) -> str:
return f"H'{value & 0xFF:02X}"