Emulator learnings folded back into decompiler
This commit is contained in:
@@ -17,6 +17,7 @@ KEY_STATE_ADDRESSES: tuple[int, ...] = (
|
||||
0xF9B5,
|
||||
0xF9B9,
|
||||
0xF9C0,
|
||||
0xF9C1,
|
||||
0xF9C3,
|
||||
0xF9C5,
|
||||
0xF9C6,
|
||||
@@ -51,6 +52,7 @@ def analyze_serial_gate(payload: dict[str, Any]) -> JsonObject:
|
||||
"queue_send_gate_loc_BAF2": _queue_send_gate(by_address),
|
||||
"resend_gate_path": _resend_gate_path(by_address),
|
||||
"rx_session_maintenance": _rx_session_maintenance(by_address),
|
||||
"timer_tick_evidence": _timer_tick_evidence(payload, by_address),
|
||||
}
|
||||
access_summary = _state_access_summary(instructions, labels)
|
||||
|
||||
@@ -94,6 +96,11 @@ def format_text_report(analysis: dict[str, Any]) -> str:
|
||||
lines.append(f" {summary}")
|
||||
for item in section.get("items", []):
|
||||
lines.append(f" - {item['address_hex']}: {item['text']}")
|
||||
roles = section.get("candidate_timer_roles", [])
|
||||
if roles:
|
||||
lines.append(" Candidate timer roles:")
|
||||
for role in roles:
|
||||
lines.append(f" - {role['address_hex']}: {role['role']}")
|
||||
|
||||
lines.extend(["", "State address readers/writers:"])
|
||||
for entry in analysis.get("state_accesses", []):
|
||||
@@ -266,6 +273,53 @@ def _rx_session_maintenance(by_address: dict[int, JsonObject]) -> JsonObject:
|
||||
}
|
||||
|
||||
|
||||
def _timer_tick_evidence(payload: dict[str, Any], by_address: dict[int, JsonObject]) -> JsonObject:
|
||||
vector = _vector_entry(payload, 0x0062, "frt1_ocia")
|
||||
handler = _int_field(vector, "target") if vector else None
|
||||
if handler is None:
|
||||
handler = 0xBEEA
|
||||
addresses = [handler, 0xBEEE, 0xBEF4, 0xBEF8, 0xBEFE, 0xBF02, 0xBF08]
|
||||
has_vector = vector is not None and _int_field(vector, "target") == handler
|
||||
has_handler_clear = _instruction_mentions(by_address.get(handler), ("FRT1_TCSR", "#5"))
|
||||
decrement_addresses = (0xBEF4, 0xBEFE, 0xBF08)
|
||||
has_decrements = all(
|
||||
address in by_address and _access_kind(by_address[address], state_address) == "read_write"
|
||||
for address, state_address in zip(decrement_addresses, (0xF9C0, 0xF9C1, 0xF9C6))
|
||||
)
|
||||
return {
|
||||
"title": "FRT1 OCIA periodic tick countdowns",
|
||||
"present": bool((has_vector or has_handler_clear) and has_decrements),
|
||||
"summary": (
|
||||
"Static evidence links vector H'0062 to the FRT1 OCIA handler at H'BEEA; the handler "
|
||||
"clears FRT1_TCSR.OCFA and conditionally decrements H'F9C0, H'F9C1, and H'F9C6."
|
||||
),
|
||||
"vector_address_hex": h16(0x0062),
|
||||
"handler_address_hex": h16(handler),
|
||||
"vector_target_label": str(vector.get("target_label", "")) if vector else "",
|
||||
"items": _items(by_address, addresses),
|
||||
"candidate_timer_roles": [
|
||||
{
|
||||
"address": 0xF9C0,
|
||||
"address_hex": h16(0xF9C0),
|
||||
"role": "candidate post-TX/report delay countdown",
|
||||
"evidence_address_hex": h16(0xBEF4),
|
||||
},
|
||||
{
|
||||
"address": 0xF9C1,
|
||||
"address_hex": h16(0xF9C1),
|
||||
"role": "candidate secondary delay countdown",
|
||||
"evidence_address_hex": h16(0xBEFE),
|
||||
},
|
||||
{
|
||||
"address": 0xF9C6,
|
||||
"address_hex": h16(0xF9C6),
|
||||
"role": "candidate periodic report/heartbeat countdown",
|
||||
"evidence_address_hex": h16(0xBF08),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _state_access_summary(instructions: list[JsonObject], labels: dict[int, str]) -> list[JsonObject]:
|
||||
result: list[JsonObject] = []
|
||||
for state_address in KEY_STATE_ADDRESSES:
|
||||
@@ -333,6 +387,32 @@ def _has_all(by_address: dict[int, JsonObject], addresses: tuple[int, ...]) -> b
|
||||
return all(address in by_address for address in addresses)
|
||||
|
||||
|
||||
def _vector_entry(payload: dict[str, Any], address: int, name: str) -> JsonObject | None:
|
||||
vectors = payload.get("vectors", [])
|
||||
if not isinstance(vectors, list):
|
||||
return None
|
||||
for vector in vectors:
|
||||
if not isinstance(vector, dict):
|
||||
continue
|
||||
if _int_field(vector, "address") == address or str(vector.get("name", "")).lower() == name:
|
||||
return vector
|
||||
return None
|
||||
|
||||
|
||||
def _int_field(payload: JsonObject | None, key: str, default: int | None = None) -> int | None:
|
||||
if not isinstance(payload, dict):
|
||||
return default
|
||||
value = payload.get(key)
|
||||
return value if isinstance(value, int) else default
|
||||
|
||||
|
||||
def _instruction_mentions(ins: JsonObject | None, fragments: tuple[str, ...]) -> bool:
|
||||
if not isinstance(ins, dict):
|
||||
return False
|
||||
text = f"{ins.get('text', '')} {ins.get('operands', '')} {ins.get('comment', '')}".upper()
|
||||
return all(fragment.upper() in text for fragment in fragments)
|
||||
|
||||
|
||||
def _text(by_address: dict[int, JsonObject], address: int) -> str:
|
||||
return str(by_address.get(address, {}).get("text", "<missing>"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user