1
0

EMualtor adjustments

This commit is contained in:
Aiden
2026-05-25 20:42:45 +10:00
parent d2e7609bbf
commit 3ab79648ff
17 changed files with 3047 additions and 83 deletions

View File

@@ -26,9 +26,14 @@ AUTONOMOUS_TX_REPORT_LABEL = "loc_BB43"
MAIN_REPORT_GATE_ENTRY = 0x3FD3
MAIN_REPORT_GATE_CALL = 0x3FEB
SESSION_GATE_ENTRY = 0x3FEF
IDLE_REPORT_GATE_ENTRY = 0x4046
IDLE_REPORT_QUEUE_WRITE = 0x4067
IDLE_REPORT_GATE_END = 0x4070
QUEUE_REPORT_ENTRY = 0xBAF2
RESEND_GATE_ENTRY = 0xBE9E
PERIODIC_RESEND_ENTRY = 0xBED5
FRT1_OCIA_ENTRY = 0xBEEA
FRT2_OCIA_ENTRY = 0xBF23
INDEX_DECODER_ADDRESS = 0x622B
INDEX_DECODER_LABEL = "loc_622B"
CHECKSUM_SEED = 0x5A
@@ -82,6 +87,8 @@ STATE_VARIABLES = {
0xF9B5: "event_queue_write_or_pending_cursor_candidate",
0xF9B9: "event_queue_base_or_current_slot_candidate",
0xF9C0: "serial_tx_busy_timer_candidate",
0xF9C4: "idle_heartbeat_gate_countdown_candidate",
0xF9C5: "rx_session_timeout_candidate",
0xF9C6: "autonomous_report_period_timer_candidate",
0xF9C8: "autonomous_report_resend_countdown_candidate",
}
@@ -132,6 +139,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
"gate_queue_model": None,
"tx_report_model": None,
"periodic_resend_model": None,
"timer_interrupt_model": None,
"confidence": "low",
"confidence_score": 0.0,
"caveat": "No protocol semantics are emitted without both RX and TX serial reconstruction candidates.",
@@ -148,6 +156,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
gate_queue_model = _gate_queue_model(ordered, commands)
tx_report_model = _tx_report_model(ordered, responses)
periodic_resend_model = _periodic_resend_model(ordered, responses)
timer_interrupt_model = _timer_interrupt_model(ordered)
evidence = _top_level_evidence(ordered, dispatch, responses, rx_candidate, tx_candidate)
confidence_score = _confidence_score(frame_supported, dispatch, responses, commands)
@@ -202,6 +211,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
"gate_queue_model": gate_queue_model,
"tx_report_model": tx_report_model,
"periodic_resend_model": periodic_resend_model,
"timer_interrupt_model": timer_interrupt_model,
"evidence": evidence,
}
return {
@@ -222,6 +232,7 @@ def analyze_serial_semantics(payload: Mapping[str, Any]) -> JsonObject:
"gate_queue_model": protocol["gate_queue_model"],
"tx_report_model": protocol["tx_report_model"],
"periodic_resend_model": protocol["periodic_resend_model"],
"timer_interrupt_model": protocol["timer_interrupt_model"],
"confidence": protocol["confidence"],
"confidence_score": protocol["confidence_score"],
"caveat": protocol["caveat"],
@@ -1308,10 +1319,17 @@ def _logical_table_map_candidates(ordered: list[JsonObject]) -> list[JsonObject]
def _state_variable_candidates(ordered: list[JsonObject]) -> list[JsonObject]:
candidates: list[JsonObject] = []
state_regions = [
(SERIAL_HANDLER_START, SERIAL_HANDLER_END),
(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END),
(0x40E0, 0x40E0),
(FRT1_OCIA_ENTRY, 0xBF08),
(FRT2_OCIA_ENTRY, 0xBF37),
]
serial_region = [
ins
for ins in ordered
if SERIAL_HANDLER_START <= int(ins.get("address", -1)) <= SERIAL_HANDLER_END
if any(start <= int(ins.get("address", -1)) <= end for start, end in state_regions)
]
if not any(
_has_ref_in_range(ins, min(STATE_VARIABLES), max(STATE_VARIABLES))
@@ -1366,8 +1384,8 @@ def _state_variable_candidates(ordered: list[JsonObject]) -> list[JsonObject]:
"evidence_addresses_hex": _hlist(evidence),
"confidence": "candidate-medium",
"caveat": (
"Role is inferred from references in the serial handler region and remains "
"a state-variable candidate."
"Role is inferred from references in serial handler, gate, and timer regions "
"and remains a state-variable candidate."
),
}
)
@@ -1621,6 +1639,7 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
evidence = _dedupe_ints(
_addresses_in_ranges(ordered, [(MAIN_REPORT_GATE_ENTRY, MAIN_REPORT_GATE_CALL)], MAIN_REPORT_GATE_ENTRY, MAIN_REPORT_GATE_CALL)
+ _addresses_in_ranges(ordered, [(SESSION_GATE_ENTRY, 0x4007)], SESSION_GATE_ENTRY, 0x4007)
+ _addresses_in_ranges(ordered, [(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)], IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)
+ _addresses_in_ranges(ordered, [(QUEUE_REPORT_ENTRY, AUTONOMOUS_TX_REPORT_CALL)], QUEUE_REPORT_ENTRY, AUTONOMOUS_TX_REPORT_CALL)
+ _addresses_in_ranges(ordered, [(RESEND_GATE_ENTRY, PERIODIC_RESEND_ENTRY)], RESEND_GATE_ENTRY, PERIODIC_RESEND_ENTRY)
)
@@ -1655,6 +1674,26 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
MAIN_REPORT_GATE_CALL,
),
},
{
"name": "idle_heartbeat_report_may_enqueue",
"entry_label": "loc_4046",
"target_label": "loc_4067",
"condition_candidate": (
"F9C4 == 0 && ((FAA5.bit7 == 0) || (F9C3 == 0)) && F9B0 == F9B5"
),
"summary": (
"Idle/default report gate; when the FRT2 countdown clears and the queue is "
"empty, loc_4046 can enqueue H'00FF for the later loc_BAF2 -> loc_BA26 send path."
),
"state_addresses_hex": [_h16(0xF9C4), _h16(0xFAA5), _h16(0xF9C3), _h16(0xF9B0), _h16(0xF9B5)],
"enqueued_report_candidate_hex": _h16(0x00FF),
"evidence_addresses": _addresses_in_ranges(
ordered,
[(IDLE_REPORT_GATE_ENTRY, IDLE_REPORT_GATE_END)],
IDLE_REPORT_GATE_ENTRY,
IDLE_REPORT_GATE_END,
),
},
{
"name": "queue_has_pending_report",
"entry_label": "loc_BAF2",
@@ -1705,6 +1744,20 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
0x4007,
),
},
{
"name": "idle_heartbeat_gate_initial_delay_loaded",
"summary": "Startup/init loads F9C4 with H'14 before the first idle/default report can be queued.",
"state_addresses_hex": [_h16(0xF9C4)],
"reload_value_hex": _h16(0x14, width=2),
"evidence_addresses": _state_immediate_evidence(ordered, 0xF9C4, 0x14),
},
{
"name": "idle_heartbeat_gate_post_send_delay_loaded",
"summary": "loc_BA26 reloads F9C4 with H'07 after each send, matching the observed heartbeat spacing.",
"state_addresses_hex": [_h16(0xF9C4)],
"reload_value_hex": _h16(0x07, width=2),
"evidence_addresses": _state_immediate_evidence(ordered, 0xF9C4, 0x07),
},
{
"name": "host_ack_can_advance_queue",
"summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.",
@@ -1729,6 +1782,111 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
}
def _timer_interrupt_model(ordered: list[JsonObject]) -> JsonObject | None:
present_addresses = {int(ins["address"]) for ins in ordered if isinstance(ins.get("address"), int)}
frt1_evidence = _dedupe_ints(
_addresses_in_ranges(ordered, [(FRT1_OCIA_ENTRY, 0xBF08)], FRT1_OCIA_ENTRY, 0xBF08)
)
frt2_evidence = _dedupe_ints(
_addresses_in_ranges(ordered, [(FRT2_OCIA_ENTRY, 0xBF37)], FRT2_OCIA_ENTRY, 0xBF37)
)
sources: list[JsonObject] = []
if frt1_evidence:
frt1_counters = [
counter
for counter in (
_timer_counter(0xF9C0, "tx_report_gate_counter_candidate", "candidate gate counter used before entering the report builder.", 0xBEF4),
_timer_counter(0xF9C1, "rx_interbyte_timeout_candidate", "candidate RX interbyte timeout counter.", 0xBEFE),
_timer_counter(0xF9C6, "periodic_resend_cadence_counter_candidate", "candidate periodic resend/heartbeat cadence counter.", 0xBF08),
)
if int(counter["evidence_address"]) in present_addresses
]
sources.append(
{
"source": "FRT1 OCIA",
"vector_address_hex": _h16(0x0062),
"handler_address": FRT1_OCIA_ENTRY,
"handler_address_hex": _h16(FRT1_OCIA_ENTRY),
"summary": "Candidate periodic tick ISR for serial busy, interbyte, and resend counters.",
"counters": frt1_counters,
"evidence_addresses": frt1_evidence,
"evidence_addresses_hex": _hlist(frt1_evidence),
}
)
if frt2_evidence:
frt2_counters = [
counter
for counter in (
_timer_counter(0xF9C4, "idle_heartbeat_gate_countdown_candidate", "candidate idle/default report enqueue countdown.", 0xBF2D),
_timer_counter(0xF9C5, "rx_session_timeout_candidate", "candidate RX/session maintenance timeout counter.", 0xBF37),
)
if int(counter["evidence_address"]) in present_addresses
]
sources.append(
{
"source": "FRT2 OCIA",
"vector_address_hex": _h16(0x006A),
"handler_address": FRT2_OCIA_ENTRY,
"handler_address_hex": _h16(FRT2_OCIA_ENTRY),
"summary": "Candidate periodic tick ISR for idle heartbeat/report and RX session counters.",
"clock_select": "CKS1=1 CKS0=0 => phi/32",
"ocra_value_hex": "H'7A12",
"manual_reference": "Manual/0900766b802125d0.md:12038 FRT CKS1/CKS0 clock select",
"counters": frt2_counters,
"evidence_addresses": frt2_evidence,
"evidence_addresses_hex": _hlist(frt2_evidence),
}
)
if not sources:
return None
counters = _dedupe_timer_counters(
counter
for source in sources
for counter in source.get("counters", [])
if isinstance(counter, dict)
)
evidence = _dedupe_ints(
addr
for source in sources
for addr in source.get("evidence_addresses", [])
if isinstance(addr, int)
)
return {
"kind": "timer_interrupt_model_candidate",
"source": " / ".join(str(source["source"]) for source in sources),
"summary": "FRT compare-match handlers decrement serial gate, timeout, and cadence counters.",
"sources": sources,
"counters": counters,
"evidence_addresses": evidence,
"evidence_addresses_hex": _hlist(evidence),
"confidence": "candidate-medium",
}
def _timer_counter(address: int, name: str, role: str, evidence_address: int) -> JsonObject:
return {
"address": address,
"address_hex": _h16(address),
"name_candidate": name,
"role": role,
"evidence_address": evidence_address,
"evidence_address_hex": _h16(evidence_address),
}
def _dedupe_timer_counters(counters: Iterable[JsonObject]) -> list[JsonObject]:
output = []
seen: set[int] = set()
for counter in counters:
address = counter.get("address")
if not isinstance(address, int) or address in seen:
continue
seen.add(address)
output.append(counter)
return output
def _tx_report_model(ordered: list[JsonObject], responses: list[JsonObject]) -> JsonObject | None:
report_responses = [
response for response in responses