new learnigns
This commit is contained in:
@@ -385,6 +385,17 @@ def _semantics_lines(
|
||||
" * dispatch: command_low3 = RX_FRAME(0) & 0x07"
|
||||
+ (f"; observed {values}" if values else ""),
|
||||
)
|
||||
state_split = dispatch.get("state_split") or dispatch.get("dispatcher_split")
|
||||
if isinstance(state_split, dict):
|
||||
initial = ", ".join(str(item) for item in state_split.get("initial_idle_commands_hex", []) if item)
|
||||
continuation = ", ".join(str(item) for item in state_split.get("continuation_commands_hex", []) if item)
|
||||
lines.append(
|
||||
" * dispatcher split: FAA2 == 0 accepts initial/idle commands "
|
||||
f"{initial or 'none'}; FAA2 != 0 accepts continuation commands {continuation or 'none'}",
|
||||
)
|
||||
caveat = str(state_split.get("caveat") or "").strip()
|
||||
if caveat:
|
||||
lines.append(f" * dispatcher caveat: {_comment_text(caveat)}")
|
||||
if opts.include_evidence:
|
||||
lines.append(f" * dispatch evidence: {_hex_join(dispatch.get('evidence_addresses_hex'))}")
|
||||
|
||||
@@ -405,6 +416,11 @@ def _semantics_lines(
|
||||
handler = command.get("handler_start_hex") or "multiple"
|
||||
responses = ", ".join(str(item) for item in command.get("response_candidates", [])) or "none"
|
||||
lines.append(f" * - {value} {name}: {summary}; handler {handler}; responses {responses}")
|
||||
availability = _comment_text(str(command.get("availability_summary") or ""))
|
||||
if availability:
|
||||
lines.append(f" * availability: {availability}")
|
||||
for note in command.get("semantic_notes", []):
|
||||
lines.append(f" * note: {_comment_text(str(note))}")
|
||||
lines.extend(_command_effect_comment_lines(protocol.get("command_effects"), opts, prefix=" * "))
|
||||
lines.extend(_response_schema_comment_lines(_schema_list(protocol), opts, prefix=" * "))
|
||||
lines.extend(_table_map_comment_lines(_table_map_list(protocol), opts, prefix=" * "))
|
||||
@@ -458,9 +474,51 @@ def _semantics_lines(
|
||||
" u16 logical_index = sci1_rx_candidate_logical_index();",
|
||||
" u16 value = sci1_rx_candidate_value();",
|
||||
"",
|
||||
" switch (command) {",
|
||||
],
|
||||
)
|
||||
lines.extend(_command_dispatch_switch_lines(commands, opts))
|
||||
return lines
|
||||
|
||||
|
||||
def _command_dispatch_switch_lines(commands: list[JsonObject], opts: SerialPseudocodeOptions) -> list[str]:
|
||||
initial = [
|
||||
command for command in commands
|
||||
if _command_has_availability(command, "initial_idle_dispatch")
|
||||
]
|
||||
continuation = [
|
||||
command for command in commands
|
||||
if _command_has_availability(command, "continuation_dispatch")
|
||||
]
|
||||
if not initial and not continuation:
|
||||
return _flat_command_switch_lines(commands, opts, indent=" ") + ["}", ""]
|
||||
|
||||
lines = [
|
||||
" bool session_active = MEM8[0xFAA2u] != 0u;",
|
||||
"",
|
||||
" if (!session_active) {",
|
||||
" /* Initial/idle dispatcher: valid checksum/no RX error, FAA2 == 0. */",
|
||||
]
|
||||
lines.extend(_flat_command_switch_lines(initial, opts, indent=" "))
|
||||
lines.extend(
|
||||
[
|
||||
" return;",
|
||||
" }",
|
||||
"",
|
||||
" /* Continuation dispatcher: FAA2 != 0. */",
|
||||
],
|
||||
)
|
||||
lines.extend(_flat_command_switch_lines(continuation, opts, indent=" "))
|
||||
lines.extend(["}", ""])
|
||||
return lines
|
||||
|
||||
|
||||
def _flat_command_switch_lines(
|
||||
commands: list[JsonObject],
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
indent: str,
|
||||
) -> list[str]:
|
||||
lines = [f"{indent}switch (command) {{"]
|
||||
for command in commands:
|
||||
value = command.get("command_value")
|
||||
if not isinstance(value, int):
|
||||
@@ -468,28 +526,50 @@ def _semantics_lines(
|
||||
name = _safe_identifier(str(command.get("name_candidate") or f"command_{value:02X}"))
|
||||
summary = _comment_text(str(command.get("summary") or "candidate command semantics unknown"))
|
||||
evidence = _hex_join(command.get("evidence_addresses_hex"))
|
||||
lines.append(f" case 0x{value:02X}u:")
|
||||
lines.append(f" /* {name}: {summary}")
|
||||
lines.append(f"{indent}case 0x{value:02X}u:")
|
||||
lines.append(f"{indent} /* {name}: {summary}")
|
||||
availability = _comment_text(str(command.get("availability_summary") or ""))
|
||||
if availability:
|
||||
lines.append(f"{indent} * availability: {availability}")
|
||||
for note in command.get("semantic_notes", []):
|
||||
lines.append(f"{indent} * note: {_comment_text(str(note))}")
|
||||
for effect_line in _command_effect_switch_lines(command):
|
||||
lines.append(f" * {effect_line}")
|
||||
lines.append(f"{indent} * {effect_line}")
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f" * evidence: {evidence}")
|
||||
lines.append(" */")
|
||||
lines.append(f" candidate_{name}(logical_index, value);")
|
||||
lines.append(" break;")
|
||||
lines.append(f"{indent} * evidence: {evidence}")
|
||||
lines.append(f"{indent} */")
|
||||
if value == 0x01:
|
||||
lines.append(f"{indent} if ((RX_FRAME(1) & 0x80u) != 0u) {{")
|
||||
lines.append(f"{indent} candidate_unknown_command(command, logical_index, value);")
|
||||
lines.append(f"{indent} break;")
|
||||
lines.append(f"{indent} }}")
|
||||
lines.append(f"{indent} candidate_{name}(logical_index, value);")
|
||||
lines.append(f"{indent} break;")
|
||||
lines.extend(
|
||||
[
|
||||
" default:",
|
||||
" candidate_unknown_command(command, logical_index, value);",
|
||||
" break;",
|
||||
" }",
|
||||
"}",
|
||||
"",
|
||||
f"{indent}default:",
|
||||
f"{indent} candidate_unknown_command(command, logical_index, value);",
|
||||
f"{indent} break;",
|
||||
f"{indent}}}",
|
||||
],
|
||||
)
|
||||
return lines
|
||||
|
||||
|
||||
def _command_has_availability(command: JsonObject, availability: str) -> bool:
|
||||
value = command.get("availability")
|
||||
if value == availability:
|
||||
return True
|
||||
if isinstance(value, list) and availability in value:
|
||||
return True
|
||||
conditions = " ".join(str(item) for item in command.get("availability_conditions", []))
|
||||
if availability == "initial_idle_dispatch":
|
||||
return "FAA2 == 0" in conditions
|
||||
if availability == "continuation_dispatch":
|
||||
return "FAA2 != 0" in conditions
|
||||
return False
|
||||
|
||||
|
||||
def _command_effect_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
@@ -528,6 +608,8 @@ def _response_schema_comment_lines(
|
||||
response_id = schema.get("response_id") or schema.get("call_address_hex") or "candidate_response"
|
||||
byte_text = _response_schema_summary(schema)
|
||||
lines.append(f"{prefix}- {response_id}: {byte_text}")
|
||||
for note in schema.get("semantic_notes", []):
|
||||
lines.append(f"{prefix} note: {_comment_text(str(note))}")
|
||||
evidence = _hex_join(schema.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {evidence}")
|
||||
@@ -612,6 +694,13 @@ def _retry_error_comment_lines(
|
||||
threshold = retry.get("threshold_candidate", "?")
|
||||
summary = _comment_text(str(retry.get("summary") or "candidate retry path"))
|
||||
lines.append(f"{prefix}- retry path: counter {counter}, threshold {threshold}; {summary}")
|
||||
echo = retry.get("echo_response_candidate")
|
||||
if isinstance(echo, dict):
|
||||
staging = _comment_text(str(echo.get("staging_candidate") or "retry/error echo staging"))
|
||||
caveat = _comment_text(str(echo.get("observed_frame_caveat") or ""))
|
||||
lines.append(f"{prefix} echo path: {echo.get('entry_label', 'loc_BE4D')}: {staging}")
|
||||
if caveat:
|
||||
lines.append(f"{prefix} echo caveat: {caveat}")
|
||||
if isinstance(command_07, dict):
|
||||
summary = _comment_text(str(command_07.get("summary") or "candidate command 0x07 path"))
|
||||
lines.append(f"{prefix}- command 0x07 path: {summary}")
|
||||
@@ -987,6 +1076,8 @@ def _effect_summary(effect: JsonObject) -> str:
|
||||
operation = effect.get("operation_candidate")
|
||||
table = effect.get("table_base_hex")
|
||||
state = effect.get("state_address_hex")
|
||||
selector = effect.get("selector_without_response_hex")
|
||||
observed = effect.get("observed_frame_caveat")
|
||||
if target:
|
||||
parts.append(f"target {target}")
|
||||
if source:
|
||||
@@ -997,6 +1088,10 @@ def _effect_summary(effect: JsonObject) -> str:
|
||||
parts.append(f"table {table}")
|
||||
if state:
|
||||
parts.append(f"state {state}")
|
||||
if selector:
|
||||
parts.append(f"selector {selector} has no response")
|
||||
if observed:
|
||||
parts.append(str(observed))
|
||||
return _comment_text("; ".join(parts))
|
||||
|
||||
|
||||
|
||||
@@ -392,6 +392,9 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None:
|
||||
continue
|
||||
|
||||
comparisons = _dispatch_comparisons(ordered, index + 1, selector_reg)
|
||||
state_split = _dispatcher_state_split(ordered, index + 1, comparisons)
|
||||
if state_split:
|
||||
_annotate_dispatch_comparison_availability(comparisons, state_split)
|
||||
command_values = sorted({int(item["command_value"]) for item in comparisons})
|
||||
candidate = {
|
||||
"kind": "command_dispatch_candidate",
|
||||
@@ -413,6 +416,8 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None:
|
||||
"command_values": command_values,
|
||||
"command_values_hex": [_h16(value, width=2) for value in command_values],
|
||||
"comparisons": comparisons,
|
||||
"state_split": state_split,
|
||||
"dispatcher_split": state_split,
|
||||
"cases": [
|
||||
{
|
||||
"value": int(item["command_value"]),
|
||||
@@ -421,6 +426,8 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None:
|
||||
"target_hex": item["handler_start_hex"],
|
||||
"compare_address": item["compare_address"],
|
||||
"branch_address": item["branch_address"],
|
||||
"availability": item.get("availability"),
|
||||
"availability_conditions": item.get("availability_conditions", []),
|
||||
}
|
||||
for item in comparisons
|
||||
],
|
||||
@@ -446,6 +453,101 @@ def _find_command_dispatch(ordered: list[JsonObject]) -> JsonObject | None:
|
||||
return best
|
||||
|
||||
|
||||
def _dispatcher_state_split(
|
||||
ordered: list[JsonObject],
|
||||
start_index: int,
|
||||
comparisons: list[JsonObject],
|
||||
) -> JsonObject | None:
|
||||
if not comparisons:
|
||||
return None
|
||||
for index in range(start_index, min(len(ordered) - 1, start_index + 24)):
|
||||
ins = ordered[index]
|
||||
if not _has_ref_in_range(ins, 0xFAA2, 0xFAA2):
|
||||
continue
|
||||
if _mnemonic_root(str(ins.get("mnemonic", ""))) != "TST":
|
||||
continue
|
||||
branch = ordered[index + 1]
|
||||
if _mnemonic_root(str(branch.get("mnemonic", ""))) != "BNE":
|
||||
continue
|
||||
targets = _targets(branch)
|
||||
if not targets:
|
||||
continue
|
||||
continuation_start = int(targets[0])
|
||||
initial_values = sorted(
|
||||
{
|
||||
int(item["command_value"])
|
||||
for item in comparisons
|
||||
if int(item.get("compare_address", 0)) < continuation_start
|
||||
}
|
||||
)
|
||||
continuation_values = sorted(
|
||||
{
|
||||
int(item["command_value"])
|
||||
for item in comparisons
|
||||
if int(item.get("compare_address", 0)) >= continuation_start
|
||||
}
|
||||
)
|
||||
if not initial_values and not continuation_values:
|
||||
continue
|
||||
return {
|
||||
"kind": "serial_command_dispatch_state_split",
|
||||
"state_address": 0xFAA2,
|
||||
"state_address_hex": _h16(0xFAA2),
|
||||
"test_address": int(ins["address"]),
|
||||
"test_address_hex": _h16(int(ins["address"])),
|
||||
"branch_address": int(branch["address"]),
|
||||
"branch_address_hex": _h16(int(branch["address"])),
|
||||
"continuation_target": continuation_start,
|
||||
"continuation_target_hex": _h16(continuation_start),
|
||||
"initial_idle_commands": initial_values,
|
||||
"initial_idle_commands_hex": [_h16(value, width=2) for value in initial_values],
|
||||
"continuation_commands": continuation_values,
|
||||
"continuation_commands_hex": [_h16(value, width=2) for value in continuation_values],
|
||||
"summary": (
|
||||
"FAA2 == 0 takes the initial/idle dispatcher path; FAA2 != 0 takes "
|
||||
"the continuation dispatcher path."
|
||||
),
|
||||
"caveat": (
|
||||
"Initial dispatch follows checksum validation and RX error handling. Command 1 "
|
||||
"is only on the initial/idle path and is also gated by F861.bit7 == 0."
|
||||
),
|
||||
"evidence_addresses": [int(ins["address"]), int(branch["address"])],
|
||||
"evidence_addresses_hex": _hlist([int(ins["address"]), int(branch["address"])]),
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def _annotate_dispatch_comparison_availability(
|
||||
comparisons: list[JsonObject],
|
||||
state_split: Mapping[str, Any],
|
||||
) -> None:
|
||||
continuation_start = state_split.get("continuation_target")
|
||||
if not isinstance(continuation_start, int):
|
||||
return
|
||||
for item in comparisons:
|
||||
compare_address = item.get("compare_address")
|
||||
if not isinstance(compare_address, int):
|
||||
continue
|
||||
command_value = int(item.get("command_value", -1))
|
||||
if compare_address < continuation_start:
|
||||
availability = "initial_idle_dispatch"
|
||||
conditions = [
|
||||
"valid checksum/no RX physical error",
|
||||
"FAA2 == 0",
|
||||
]
|
||||
if command_value == 0x01:
|
||||
conditions.append("F861.bit7 == 0")
|
||||
else:
|
||||
availability = "continuation_dispatch"
|
||||
conditions = [
|
||||
"valid checksum/no RX physical error",
|
||||
"FAA2 != 0",
|
||||
]
|
||||
item["availability"] = availability
|
||||
item["availability_conditions"] = conditions
|
||||
item["availability_summary"] = " && ".join(conditions)
|
||||
|
||||
|
||||
def _dispatch_comparisons(
|
||||
ordered: list[JsonObject],
|
||||
start_index: int,
|
||||
@@ -537,6 +639,8 @@ def _command_candidates(
|
||||
"dispatch_compare_address_hex": comparison["compare_address_hex"],
|
||||
"dispatch_branch_address": comparison["branch_address"],
|
||||
"dispatch_branch_address_hex": comparison["branch_address_hex"],
|
||||
"availability": comparison.get("availability"),
|
||||
"availability_conditions": comparison.get("availability_conditions", []),
|
||||
}
|
||||
if alternative not in command["handler_alternatives"]:
|
||||
command["handler_alternatives"].append(alternative)
|
||||
@@ -555,6 +659,30 @@ def _command_candidates(
|
||||
command["handler_start_hex"] = _h16(starts_for_command[0]) if len(starts_for_command) == 1 else None
|
||||
command["handler_end"] = ends_for_command[0] if len(ends_for_command) == 1 else None
|
||||
command["handler_end_hex"] = _h16(ends_for_command[0]) if len(ends_for_command) == 1 else None
|
||||
availability_conditions = _dedupe_strings(
|
||||
str(condition)
|
||||
for alt in alternatives
|
||||
for condition in alt.get("availability_conditions", [])
|
||||
if condition
|
||||
)
|
||||
availability = _dedupe_strings(
|
||||
str(alt["availability"])
|
||||
for alt in alternatives
|
||||
if alt.get("availability")
|
||||
)
|
||||
availability_summaries = _dedupe_strings(
|
||||
" && ".join(str(condition) for condition in alt.get("availability_conditions", []) if condition)
|
||||
for alt in alternatives
|
||||
if alt.get("availability_conditions")
|
||||
)
|
||||
command["availability"] = availability[0] if len(availability) == 1 else availability
|
||||
command["availability_conditions"] = availability_conditions
|
||||
command["availability_summary"] = (
|
||||
availability_summaries[0]
|
||||
if len(availability_summaries) == 1
|
||||
else " OR ".join(availability_summaries)
|
||||
)
|
||||
command["semantic_notes"] = _command_semantic_notes(int(command["command_value"]))
|
||||
|
||||
ranges_for_command = [
|
||||
(alt["handler_start"], alt["handler_end"])
|
||||
@@ -657,26 +785,47 @@ def _command_name_candidate(value: int) -> str:
|
||||
def _command_summary(value: int) -> str:
|
||||
return {
|
||||
0x00: "candidate write of RX[3:4] into primary/current tables, followed by a response",
|
||||
0x01: "candidate read from the primary table, followed by a response carrying the value",
|
||||
0x01: "initial/idle-path primary table read only, followed by an odd response staging sequence",
|
||||
0x02: "candidate clear/abort path with no immediate response builder",
|
||||
0x04: "candidate write/update path that stores a value without an immediate serial response",
|
||||
0x05: "candidate pending/event acknowledgement path",
|
||||
0x05: "continuation-only conditional acknowledgement/session clear path",
|
||||
0x06: "candidate secondary-table value write path",
|
||||
0x07: "candidate retransmit/NAK-style path; error handling also builds command 0x07 responses",
|
||||
0x07: "candidate retransmit path; retry/error handling also builds a command 0x07 RX-payload echo",
|
||||
}.get(value, "candidate command semantics are unknown")
|
||||
|
||||
|
||||
def _command_semantic_notes(value: int) -> list[str]:
|
||||
return {
|
||||
0x01: [
|
||||
"Only accepted on the initial/idle dispatcher path: valid checksum/no RX error, FAA2 == 0, and F861.bit7 == 0.",
|
||||
"BCD7 stages F850=0x04, writes F851 from F861 and then overwrites F851 from F862.",
|
||||
"BCD7 reads the primary table word at E000 + 2*selector; F854 receives the low byte and F853 receives the high byte.",
|
||||
"F852 is not freshly written in the BCD7 handler, so do not describe the response as a fixed 04 00 QQ hi lo frame.",
|
||||
],
|
||||
0x05: [
|
||||
"Only accepted on the continuation dispatcher path when FAA2 != 0.",
|
||||
"For selector 0x0040, frame 05 00 40 00 00 1F performs no response staging.",
|
||||
"The handler clears FAA3/FAA2; F9B5 advances only when FAA2.bit3 was set from a queued report.",
|
||||
"If FAA2 == 0, command 5 falls through the initial dispatcher instead of doing acknowledgement work.",
|
||||
],
|
||||
0x07: [
|
||||
"loc_BE4D is a retry/error echo path: F850=0x07 and F861-F864 are copied into F851-F854 before loc_BA26.",
|
||||
"Observed frame 07 80 40 20 90 2D means RX bytes F861-F864 were 80 40 20 90; it is not a table value.",
|
||||
],
|
||||
}.get(value, [])
|
||||
|
||||
|
||||
def _command_effect_summary(value: int, effects: list[JsonObject]) -> str:
|
||||
if not effects:
|
||||
return "No structured command effects were inferred."
|
||||
return {
|
||||
0x00: "Candidate acknowledged set: writes value bytes to primary/current tables, flags the index, and stages an echo-style response.",
|
||||
0x01: "Candidate read: reads the primary table and stages a value response.",
|
||||
0x01: "Initial/idle candidate read: reads the primary table and stages an odd value response with F852 possibly stale.",
|
||||
0x02: "Candidate clear/abort: clears serial session state without an observed immediate response.",
|
||||
0x04: "Candidate deferred set: writes value bytes and flags the index without an observed immediate response.",
|
||||
0x05: "Candidate acknowledgement/clear: updates pending/event state without an observed immediate response.",
|
||||
0x05: "Continuation-only ACK/session clear: clears FAA3/FAA2 and only advances F9B5 when queued-report FAA2.bit3 was set; selector 0x0040 has no response.",
|
||||
0x06: "Candidate secondary set: writes value bytes to the secondary table and flags the index.",
|
||||
0x07: "Candidate retransmit/error reply: reuses prior TX bytes or builds an explicit 0x07 retry/error response.",
|
||||
0x07: "Candidate retransmit/error reply: reuses prior TX bytes or copies RX payload bytes behind an explicit 0x07 retry/error echo.",
|
||||
}.get(value, "Candidate effects are inferred from handler-local writes, reads, calls, and response staging.")
|
||||
|
||||
|
||||
@@ -740,9 +889,10 @@ def _command_effects(
|
||||
{
|
||||
"kind": "table_read_candidate",
|
||||
"target_candidate": "primary_value_table_candidate",
|
||||
"destination_candidate": "response value bytes",
|
||||
"destination_candidate": "response value bytes F854/F853, with F852 not freshly written by BCD7",
|
||||
"table_base": 0xE000,
|
||||
"table_base_hex": _h16(0xE000),
|
||||
"address_expression_candidate": "E000 + 2*selector",
|
||||
"evidence_addresses": _table_access_addresses(table_accesses, 0x2000, "read"),
|
||||
}
|
||||
)
|
||||
@@ -781,9 +931,15 @@ def _command_effects(
|
||||
elif value == 0x05:
|
||||
add(
|
||||
{
|
||||
"kind": "pending_acknowledgement_candidate",
|
||||
"kind": "conditional_ack_session_clear_candidate",
|
||||
"target_candidate": "selected event/pending state",
|
||||
"operation_candidate": "clear selected pending flags and then clear serial session state",
|
||||
"operation_candidate": (
|
||||
"when FAA2 != 0, clear FAA3/FAA2; advance F9B5 only if FAA2.bit3 was "
|
||||
"set from queued-report state; selector 0x0040 stages no response"
|
||||
),
|
||||
"selector_without_response_hex": _h16(0x0040),
|
||||
"requires": ["FAA2 != 0"],
|
||||
"fallthrough_when": "FAA2 == 0",
|
||||
"evidence_addresses": _dedupe_ints(
|
||||
_state_access_addresses(state_accesses, 0xFAA2)
|
||||
+ _state_access_addresses(state_accesses, 0xFAA3)
|
||||
@@ -822,6 +978,16 @@ def _command_effects(
|
||||
"evidence_addresses": _addresses_in_ranges(ordered, ranges, 0xBE05, 0xBE22),
|
||||
}
|
||||
)
|
||||
add(
|
||||
{
|
||||
"kind": "retry_error_echo_candidate",
|
||||
"source_candidate": "RX payload bytes F861-F864",
|
||||
"destination_candidate": "F850=0x07, F851-F854=F861-F864 before loc_BA26",
|
||||
"observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90; it is not a table value",
|
||||
"response_candidates": [item for item in response_ids if item == "response_at_BE6A"],
|
||||
"evidence_addresses": _addresses_in_ranges(ordered, ranges, 0xBE4D, 0xBE6A),
|
||||
}
|
||||
)
|
||||
|
||||
if response_ids:
|
||||
add(
|
||||
@@ -852,6 +1018,10 @@ def _command_effect_aliases(commands: list[JsonObject]) -> list[JsonObject]:
|
||||
"command_value_hex": command.get("command_value_hex"),
|
||||
"name_candidate": command.get("name_candidate"),
|
||||
"summary": command.get("effect_summary", command.get("summary")),
|
||||
"availability": command.get("availability"),
|
||||
"availability_conditions": command.get("availability_conditions", []),
|
||||
"availability_summary": command.get("availability_summary"),
|
||||
"semantic_notes": command.get("semantic_notes", []),
|
||||
"effects": command.get("effects", []),
|
||||
"response_candidates": command.get("response_candidates", []),
|
||||
"evidence_addresses": command.get("evidence_addresses", []),
|
||||
@@ -938,6 +1108,7 @@ def _response_candidates(ordered: list[JsonObject]) -> list[JsonObject]:
|
||||
}
|
||||
response["schema"] = _response_schema(response)
|
||||
response["byte_schema"] = response["schema"]["bytes"]
|
||||
response["semantic_notes"] = response["schema"].get("semantic_notes", [])
|
||||
responses.append(response)
|
||||
return responses
|
||||
|
||||
@@ -1004,6 +1175,7 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject:
|
||||
for addr in item.get("evidence_addresses", [])
|
||||
if isinstance(addr, int)
|
||||
)
|
||||
semantic_notes = _response_schema_semantic_notes(response, bytes_out)
|
||||
return {
|
||||
"kind": "response_schema_candidate",
|
||||
"response_id": response.get("id"),
|
||||
@@ -1014,6 +1186,7 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject:
|
||||
"buffer_end": TX_STAGING_END,
|
||||
"buffer_end_hex": _h16(TX_STAGING_END),
|
||||
"bytes": bytes_out,
|
||||
"semantic_notes": semantic_notes,
|
||||
"evidence_addresses": evidence_addresses,
|
||||
"evidence_addresses_hex": _hlist(evidence_addresses),
|
||||
"confidence": "candidate-medium" if evidence_addresses else "candidate-low",
|
||||
@@ -1024,6 +1197,30 @@ def _response_schema(response: Mapping[str, Any]) -> JsonObject:
|
||||
}
|
||||
|
||||
|
||||
def _response_schema_semantic_notes(
|
||||
response: Mapping[str, Any],
|
||||
bytes_out: list[JsonObject],
|
||||
) -> list[str]:
|
||||
call_address = response.get("call_address")
|
||||
if call_address == 0xBCFA:
|
||||
for item in bytes_out:
|
||||
if item.get("offset") == 2 and item.get("source_kind") == "unknown":
|
||||
item["source_kind"] = "stale_or_unchanged"
|
||||
item["source_expression"] = "stale/unchanged"
|
||||
item["caveat"] = "BCD7 does not freshly write F852 before loc_BA26."
|
||||
return [
|
||||
"Command 1 BCD7 staging is odd: F850=0x04; F851 is written from F861 then overwritten by F862.",
|
||||
"The primary table word is read from E000 + 2*selector; F854/F853 receive low/high value bytes.",
|
||||
"F852 may be stale or unchanged in this handler; avoid a fixed 04 00 QQ hi lo response shape.",
|
||||
]
|
||||
if call_address == 0xBE6A:
|
||||
return [
|
||||
"loc_BE4D retry/error echo stages F850=0x07 and copies F861-F864 into F851-F854 before loc_BA26.",
|
||||
"Observed 07 80 40 20 90 2D echoes RX payload bytes 80 40 20 90; it is not a table-derived value.",
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def _response_schemas(responses: list[JsonObject]) -> list[JsonObject]:
|
||||
return [
|
||||
response["schema"]
|
||||
@@ -1203,6 +1400,7 @@ def _response_builder_aliases(responses: list[JsonObject]) -> list[JsonObject]:
|
||||
"call_address": response.get("call_address"),
|
||||
"call_address_hex": response.get("call_address_hex"),
|
||||
"writes": writes,
|
||||
"semantic_notes": response.get("semantic_notes", []),
|
||||
"evidence_addresses": response.get("evidence_addresses", []),
|
||||
"evidence_addresses_hex": response.get("evidence_addresses_hex", []),
|
||||
"confidence": response.get("confidence", "medium"),
|
||||
@@ -1571,6 +1769,7 @@ def _retry_error_model(ordered: list[JsonObject], responses: list[JsonObject]) -
|
||||
retry_evidence = _dedupe_ints(
|
||||
[int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, 0xFAA2, 0xFAA6)]
|
||||
+ [int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, TX_STAGING_START, TX_STAGING_END)]
|
||||
+ [int(ins["address"]) for ins in retry_path if _has_ref_in_range(ins, RX_FRAME_START + 1, RX_FRAME_START + 4)]
|
||||
+ [int(ins["address"]) for ins in retry_path if _is_send_builder_call(ins)]
|
||||
+ _response_evidence_addresses(responses, checksum_error_response)
|
||||
)
|
||||
@@ -1606,8 +1805,16 @@ def _retry_error_model(ordered: list[JsonObject], responses: list[JsonObject]) -
|
||||
"response_candidates": checksum_error_response,
|
||||
"summary": (
|
||||
"Candidate retry path clears/consults serial flags, increments FAA6, compares it "
|
||||
"with 2, and when still below the apparent limit stages a command 0x07 response."
|
||||
"with 2, and when still below the apparent limit enters loc_BE4D to stage a "
|
||||
"command 0x07 retry/error echo of RX payload bytes F861-F864."
|
||||
),
|
||||
"echo_response_candidate": {
|
||||
"entry_label": "loc_BE4D",
|
||||
"entry_address": 0xBE4D,
|
||||
"entry_address_hex": _h16(0xBE4D),
|
||||
"staging_candidate": "F850=0x07; F851-F854=F861-F864",
|
||||
"observed_frame_caveat": "07 80 40 20 90 2D echoes RX payload 80 40 20 90 and is not a table value.",
|
||||
},
|
||||
"evidence_addresses": retry_evidence,
|
||||
"evidence_addresses_hex": _hlist(retry_evidence),
|
||||
"confidence": "candidate-medium" if retry_counter_evidence else "candidate-low",
|
||||
@@ -1646,7 +1853,7 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
command_ack_values = [
|
||||
int(command["command_value"])
|
||||
for command in commands
|
||||
if command.get("command_value") in {0x05, 0x06}
|
||||
if command.get("command_value") == 0x05
|
||||
]
|
||||
if not evidence and not command_ack_values:
|
||||
return None
|
||||
@@ -1774,13 +1981,18 @@ def _gate_queue_model(ordered: list[JsonObject], commands: list[JsonObject]) ->
|
||||
},
|
||||
{
|
||||
"name": "host_ack_can_advance_queue",
|
||||
"summary": "Commands 0x05/0x06 are modeled as acknowledgement paths that can clear pending state or advance F9B5.",
|
||||
"summary": (
|
||||
"Command 0x05 is a continuation-only ACK/session clear path: it clears "
|
||||
"FAA3/FAA2 and advances F9B5 only when queued-report FAA2.bit3 was set. "
|
||||
"Selector 0x0040 has no response; if FAA2 == 0 the command falls through "
|
||||
"instead of doing ACK work."
|
||||
),
|
||||
"command_values_hex": [_h16(value, width=2) for value in command_ack_values],
|
||||
"state_addresses_hex": [_h16(0xF9B5)],
|
||||
"state_addresses_hex": [_h16(0xFAA2), _h16(0xFAA3), _h16(0xF9B5)],
|
||||
"evidence_addresses": _dedupe_ints(
|
||||
addr
|
||||
for command in commands
|
||||
if command.get("command_value") in {0x05, 0x06}
|
||||
if command.get("command_value") == 0x05
|
||||
for addr in command.get("evidence_addresses", [])
|
||||
if isinstance(addr, int)
|
||||
),
|
||||
@@ -2283,9 +2495,26 @@ def _response_window(ordered: list[JsonObject], call_index: int) -> list[JsonObj
|
||||
for index in range(call_index - 1, max(-1, call_index - 48), -1):
|
||||
candidate = ordered[index]
|
||||
mnemonic = str(candidate.get("mnemonic", "")).upper()
|
||||
if mnemonic in {"RTS", "RTE"}:
|
||||
root = _mnemonic_root(mnemonic)
|
||||
if root in {"RTS", "RTE"}:
|
||||
break
|
||||
if candidate.get("kind") == "branch" and mnemonic != "BSR":
|
||||
if root in {
|
||||
"BRA",
|
||||
"BEQ",
|
||||
"BNE",
|
||||
"BCS",
|
||||
"BCC",
|
||||
"BHI",
|
||||
"BLS",
|
||||
"BGE",
|
||||
"BLT",
|
||||
"BGT",
|
||||
"BLE",
|
||||
"BMI",
|
||||
"BPL",
|
||||
"BVS",
|
||||
"BVC",
|
||||
}:
|
||||
break
|
||||
start = index
|
||||
return ordered[start:call_index]
|
||||
|
||||
Reference in New Issue
Block a user