LCD decompile
This commit is contained in:
521
h8536/board_profile.py
Normal file
521
h8536/board_profile.py
Normal 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}"
|
||||
Reference in New Issue
Block a user