more serial improvements
This commit is contained in:
@@ -40,6 +40,7 @@ def generate_serial_pseudocode(
|
||||
lines.extend(_board_comment_lines(payload))
|
||||
if opts.include_manual:
|
||||
lines.extend(_manual_reference_lines(payload))
|
||||
lines.extend(_sci_link_lines(payload))
|
||||
lines.extend(_declarations(tx_candidate, rx_candidate))
|
||||
if opts.include_semantics:
|
||||
lines.extend(_semantics_lines(serial_semantics, opts))
|
||||
@@ -196,6 +197,72 @@ def _manual_reference_lines(payload: JsonObject) -> list[str]:
|
||||
return lines
|
||||
|
||||
|
||||
def _sci_link_lines(payload: JsonObject) -> list[str]:
|
||||
config = _first_sci1_configuration(payload)
|
||||
dtc_note = _dtc_sci_note(payload)
|
||||
if not config and not dtc_note:
|
||||
return []
|
||||
|
||||
lines = ["/* SCI1 link layer:"]
|
||||
if config:
|
||||
mode_summary = str(config.get("mode_summary") or config.get("mode") or "SCI mode")
|
||||
char_format = "8E1" if "8-bit even parity 1 stop" in mode_summary else mode_summary
|
||||
smr = config.get("smr_hex") or _optional_hex(config.get("smr"), width=2)
|
||||
brr = config.get("brr_hex") or _optional_hex(config.get("brr"), width=2)
|
||||
scr = config.get("scr_hex") or _optional_hex(config.get("scr"), width=2)
|
||||
clock_source = config.get("clock_source") or "clock source unknown"
|
||||
lines.append(
|
||||
" * - "
|
||||
+ _comment_text(
|
||||
f"{char_format} SCI characters carry the 6 protocol bytes; "
|
||||
f"{mode_summary}, clock {clock_source}, SMR={smr}, BRR={brr}, SCR={scr}",
|
||||
),
|
||||
)
|
||||
baud = config.get("baud_bps")
|
||||
if isinstance(baud, (int, float)):
|
||||
lines.append(f" * - candidate baud: {_comment_text(str(baud))} bps")
|
||||
else:
|
||||
lines.append(" * - baud is clock-dependent; pass --clock-hz on the decompiler for bps.")
|
||||
if dtc_note:
|
||||
lines.append(f" * - {_comment_text(dtc_note)}")
|
||||
lines.append(" */")
|
||||
lines.append("")
|
||||
return lines
|
||||
|
||||
|
||||
def _first_sci1_configuration(payload: JsonObject) -> JsonObject | None:
|
||||
sci = payload.get("sci")
|
||||
if not isinstance(sci, dict):
|
||||
return None
|
||||
channels = sci.get("channels")
|
||||
if not isinstance(channels, dict):
|
||||
return None
|
||||
sci1 = channels.get("SCI1")
|
||||
if not isinstance(sci1, dict):
|
||||
return None
|
||||
configurations = sci1.get("configurations")
|
||||
if not isinstance(configurations, list):
|
||||
return None
|
||||
for item in configurations:
|
||||
if isinstance(item, dict):
|
||||
return item
|
||||
return None
|
||||
|
||||
|
||||
def _dtc_sci_note(payload: JsonObject) -> str:
|
||||
vectors = payload.get("dtc_vectors")
|
||||
if not isinstance(vectors, list):
|
||||
return ""
|
||||
sources = {
|
||||
str(item.get("source", "")).lower()
|
||||
for item in vectors
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
if "sci1_rxi" in sources or "sci1_txi" in sources:
|
||||
return "SCI1 DTC vector entries are present; review DTC metadata before treating RX/TX as CPU-only."
|
||||
return "No SCI1 RXI/TXI DTC vector entries are present in JSON; RX/TX are modeled as CPU ISR paths."
|
||||
|
||||
|
||||
def _declarations(tx_candidate: JsonObject | None, rx_candidate: JsonObject | None) -> list[str]:
|
||||
candidate = tx_candidate or rx_candidate or {}
|
||||
channel = str(candidate.get("channel") or "SCI1")
|
||||
@@ -250,6 +317,10 @@ def _declarations(tx_candidate: JsonObject | None, rx_candidate: JsonObject | No
|
||||
timeout = _int_field(rx_candidate, "interbyte_timeout_address", 0xF9C1)
|
||||
complete = _int_field(rx_candidate, "complete_timer_address", 0xF9C5)
|
||||
length = _int_field(rx_candidate, "frame_length", 6)
|
||||
error_handling = rx_candidate.get("rx_error_handling")
|
||||
error_latch = 0xFAA4
|
||||
if isinstance(error_handling, dict):
|
||||
error_latch = _int_field(error_handling, "error_latch_address", 0xFAA4)
|
||||
lines.extend(
|
||||
[
|
||||
f"#define RX_FRAME_LENGTH {length}u",
|
||||
@@ -258,6 +329,8 @@ def _declarations(tx_candidate: JsonObject | None, rx_candidate: JsonObject | No
|
||||
f"#define RX_INDEX MEM8[{_c_hex(rx_index)}]",
|
||||
f"#define RX_INTERBYTE_TIMEOUT MEM8[{_c_hex(timeout)}]",
|
||||
f"#define RX_COMPLETE_TIMER MEM8[{_c_hex(complete)}]",
|
||||
f"#define RX_ERROR_LATCH MEM8[{_c_hex(error_latch)}]",
|
||||
"#define RX_ERROR_LATCH_PHYSICAL_ERROR 0x80u",
|
||||
"",
|
||||
],
|
||||
)
|
||||
@@ -326,6 +399,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}")
|
||||
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=" * "))
|
||||
lines.extend(_state_variable_comment_lines(protocol.get("state_variable_candidates"), opts, prefix=" * "))
|
||||
lines.extend(_retry_error_comment_lines(protocol.get("retry_error_model"), opts, prefix=" * "))
|
||||
lines.append(" */")
|
||||
lines.append("")
|
||||
|
||||
@@ -376,6 +454,8 @@ def _semantics_lines(
|
||||
evidence = _hex_join(command.get("evidence_addresses_hex"))
|
||||
lines.append(f" case 0x{value:02X}u:")
|
||||
lines.append(f" /* {name}: {summary}")
|
||||
for effect_line in _command_effect_switch_lines(command):
|
||||
lines.append(f" * {effect_line}")
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f" * evidence: {evidence}")
|
||||
lines.append(" */")
|
||||
@@ -394,6 +474,203 @@ def _semantics_lines(
|
||||
return lines
|
||||
|
||||
|
||||
def _command_effect_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
effects = [item for item in _object_list(value) if item.get("effects") or item.get("summary")]
|
||||
if not effects:
|
||||
return []
|
||||
lines = [f"{prefix}command effects:"]
|
||||
for item in effects:
|
||||
command = item.get("command_value_hex") or _command_hex(item.get("command_value"))
|
||||
name = item.get("name_candidate") or "unknown_command"
|
||||
summary = _comment_text(str(item.get("summary") or "candidate effects"))
|
||||
lines.append(f"{prefix}- {command} {name}: {summary}")
|
||||
for effect in _object_list(item.get("effects"))[:3]:
|
||||
effect_text = _effect_summary(effect)
|
||||
if effect_text:
|
||||
lines.append(f"{prefix} effect: {effect_text}")
|
||||
evidence = _hex_join(item.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {evidence}")
|
||||
return lines
|
||||
|
||||
|
||||
def _response_schema_comment_lines(
|
||||
schemas: list[JsonObject],
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
if not schemas:
|
||||
return []
|
||||
lines = [f"{prefix}response schemas:"]
|
||||
for schema in schemas[:4]:
|
||||
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}")
|
||||
evidence = _hex_join(schema.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {evidence}")
|
||||
if len(schemas) > 4:
|
||||
lines.append(f"{prefix}- ... {len(schemas) - 4} more candidate response schemas")
|
||||
return lines
|
||||
|
||||
|
||||
def _table_map_comment_lines(
|
||||
tables: list[JsonObject],
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
if not tables:
|
||||
return []
|
||||
lines = [f"{prefix}table map candidates:"]
|
||||
for table in tables[:6]:
|
||||
name = table.get("name_candidate") or "unnamed_table_candidate"
|
||||
address = table.get("logical_base_address_hex") or table.get("address_hex") or "?"
|
||||
element = table.get("element_candidate")
|
||||
accesses = ", ".join(str(item) for item in table.get("observed_accesses", []) if item) or "access?"
|
||||
detail = f"{name} at {address}"
|
||||
if element:
|
||||
detail += f" ({element})"
|
||||
lines.append(f"{prefix}- {_comment_text(detail)}; observed {accesses}")
|
||||
evidence = _hex_join(table.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {evidence}")
|
||||
if len(tables) > 6:
|
||||
lines.append(f"{prefix}- ... {len(tables) - 6} more table candidates")
|
||||
return lines
|
||||
|
||||
|
||||
def _state_variable_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
states = sorted(
|
||||
_object_list(value),
|
||||
key=lambda item: item.get("address") if isinstance(item.get("address"), int) else 0x10000,
|
||||
)
|
||||
if not states:
|
||||
return []
|
||||
lines = [f"{prefix}state variable candidates:"]
|
||||
for state in states[:6]:
|
||||
name = state.get("name_candidate") or "unnamed_state_candidate"
|
||||
address = state.get("address_hex") or _command_hex(state.get("address"))
|
||||
reads = state.get("read_count", "?")
|
||||
writes = state.get("write_count", "?")
|
||||
bits = ", ".join(str(item) for item in state.get("bit_candidates", []))
|
||||
suffix = f"; bits {bits}" if bits else ""
|
||||
lines.append(f"{prefix}- {_comment_text(str(name))} {address}: reads {reads}, writes {writes}{suffix}")
|
||||
evidence = _hex_join(state.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix} evidence: {evidence}")
|
||||
if len(states) > 6:
|
||||
lines.append(f"{prefix}- ... {len(states) - 6} more state-variable candidates")
|
||||
return lines
|
||||
|
||||
|
||||
def _retry_error_comment_lines(
|
||||
value: object,
|
||||
opts: SerialPseudocodeOptions,
|
||||
*,
|
||||
prefix: str,
|
||||
) -> list[str]:
|
||||
if not isinstance(value, dict):
|
||||
return []
|
||||
checksum = value.get("checksum_failure_path")
|
||||
retry = value.get("retry_path")
|
||||
command_07 = value.get("command_0x07_path")
|
||||
lines = [f"{prefix}retry/error model candidate:"]
|
||||
if isinstance(checksum, dict):
|
||||
condition = _comment_text(str(checksum.get("condition_candidate") or "checksum failure"))
|
||||
target = checksum.get("error_target") or checksum.get("error_target_address_hex") or "error target"
|
||||
lines.append(f"{prefix}- checksum path: {condition} -> {target}")
|
||||
if isinstance(retry, dict):
|
||||
counter = retry.get("counter_address_hex") or "counter?"
|
||||
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}")
|
||||
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}")
|
||||
evidence = _hex_join(value.get("evidence_addresses_hex"))
|
||||
if opts.include_evidence and evidence:
|
||||
lines.append(f"{prefix}- evidence: {evidence}")
|
||||
return lines
|
||||
|
||||
|
||||
def _command_effect_switch_lines(command: JsonObject) -> list[str]:
|
||||
effects = _object_list(command.get("effects"))[:3]
|
||||
lines = []
|
||||
for effect in effects:
|
||||
text = _effect_summary(effect)
|
||||
if text:
|
||||
lines.append(f"candidate effect: {text}")
|
||||
return lines
|
||||
|
||||
|
||||
def _effect_summary(effect: JsonObject) -> str:
|
||||
kind = str(effect.get("kind") or "effect_candidate")
|
||||
parts = [kind]
|
||||
target = effect.get("target_candidate") or effect.get("destination_candidate")
|
||||
source = effect.get("source_candidate")
|
||||
operation = effect.get("operation_candidate")
|
||||
table = effect.get("table_base_hex")
|
||||
state = effect.get("state_address_hex")
|
||||
if target:
|
||||
parts.append(f"target {target}")
|
||||
if source:
|
||||
parts.append(f"source {source}")
|
||||
if operation:
|
||||
parts.append(str(operation))
|
||||
if table:
|
||||
parts.append(f"table {table}")
|
||||
if state:
|
||||
parts.append(f"state {state}")
|
||||
return _comment_text("; ".join(parts))
|
||||
|
||||
|
||||
def _schema_list(protocol: JsonObject) -> list[JsonObject]:
|
||||
schemas = _object_list(protocol.get("response_schemas"))
|
||||
if schemas:
|
||||
return schemas
|
||||
return _object_list(protocol.get("response_schema"))
|
||||
|
||||
|
||||
def _table_map_list(protocol: JsonObject) -> list[JsonObject]:
|
||||
tables = _object_list(protocol.get("logical_table_map_candidates"))
|
||||
if tables:
|
||||
return tables
|
||||
return _object_list(protocol.get("table_map_candidates"))
|
||||
|
||||
|
||||
def _response_schema_summary(schema: JsonObject) -> str:
|
||||
parts = []
|
||||
for item in _object_list(schema.get("bytes")):
|
||||
label = item.get("byte") or f"byte{item.get('offset', '?')}"
|
||||
source = item.get("source_expression") or item.get("source_kind") or "unknown"
|
||||
parts.append(f"{label}={source}")
|
||||
return _comment_text("; ".join(parts) if parts else "candidate byte sources unknown")
|
||||
|
||||
|
||||
def _object_list(value: object) -> list[JsonObject]:
|
||||
if not isinstance(value, list):
|
||||
return []
|
||||
return [item for item in value if isinstance(item, dict)]
|
||||
|
||||
|
||||
def _command_hex(value: object) -> str:
|
||||
if isinstance(value, int):
|
||||
return f"0x{value:02X}"
|
||||
return "?"
|
||||
|
||||
|
||||
def _tx_functions(candidate: JsonObject, opts: SerialPseudocodeOptions) -> list[str]:
|
||||
length = _int_field(candidate, "frame_length", 6)
|
||||
seed = _int_field(candidate, "checksum_seed", 0x5A)
|
||||
@@ -511,8 +788,9 @@ def _rx_functions(candidate: JsonObject, opts: SerialPseudocodeOptions) -> list[
|
||||
"",
|
||||
"void sci1_rx_error_candidate_isr(void)",
|
||||
"{",
|
||||
" RX_ERROR_LATCH |= RX_ERROR_LATCH_PHYSICAL_ERROR;",
|
||||
" SCI1_SSR &= (u8)~(SCI_SSR_ORER | SCI_SSR_FER | SCI_SSR_PER);",
|
||||
" RX_INDEX = 0u;",
|
||||
" sci1_rx_byte_received_candidate_isr();",
|
||||
"}",
|
||||
"",
|
||||
],
|
||||
@@ -532,6 +810,14 @@ def _candidate_comment_block(
|
||||
formula = str(candidate.get("checksum_formula") or "").strip()
|
||||
if formula:
|
||||
lines.append(f" * checksum formula: {_comment_text(formula)}")
|
||||
error_handling = candidate.get("rx_error_handling")
|
||||
if isinstance(error_handling, dict):
|
||||
summary = str(error_handling.get("summary") or "").strip()
|
||||
if summary:
|
||||
lines.append(f" * RX error handling: {_comment_text(summary)}")
|
||||
caveat = str(error_handling.get("manual_caveat") or "").strip()
|
||||
if caveat:
|
||||
lines.append(f" * RX error caveat: {_comment_text(caveat)}")
|
||||
if opts.include_evidence:
|
||||
evidence = candidate.get("evidence_addresses_hex")
|
||||
if isinstance(evidence, dict):
|
||||
@@ -596,6 +882,12 @@ def _c_hex(value: int, *, width: int = 4) -> str:
|
||||
return f"0x{value & 0xFFFF:0{width}X}u"
|
||||
|
||||
|
||||
def _optional_hex(value: object, *, width: int = 4) -> str:
|
||||
if isinstance(value, int):
|
||||
return f"H'{value & 0xFFFF:0{width}X}"
|
||||
return "unknown"
|
||||
|
||||
|
||||
def _h(value: int) -> str:
|
||||
return f"H'{value & 0xFFFF:04X}"
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ from .model import Instruction
|
||||
|
||||
|
||||
SCI1_TDR_ADDRESS = 0xFEDB
|
||||
SCI1_SSR_ADDRESS = 0xFEDC
|
||||
SCI1_RDR_ADDRESS = 0xFEDD
|
||||
TX_BUFFER_START = 0xF858
|
||||
TX_CHECKSUM_ADDRESS = 0xF85D
|
||||
@@ -24,6 +25,7 @@ RX_INDEX_ADDRESS = 0xF9C3
|
||||
RX_INTERBYTE_TIMEOUT_ADDRESS = 0xF9C1
|
||||
RX_COMPLETE_TIMER_ADDRESS = 0xF9C5
|
||||
RX_FRAME_LENGTH = 6
|
||||
RX_ERROR_LATCH_ADDRESS = 0xFAA4
|
||||
|
||||
_BUFFER_DATA_END = TX_CHECKSUM_ADDRESS - 1
|
||||
_MIN_BUFFER_REFERENCES = 3
|
||||
@@ -265,6 +267,41 @@ def _collect_rx_evidence(ordered: list[Instruction]) -> list[dict[str, object]]:
|
||||
),
|
||||
)
|
||||
|
||||
rdrf_before_rdr = _rx_rdrf_clear_before_rdr_read(ordered)
|
||||
if rdrf_before_rdr:
|
||||
evidence.append(
|
||||
_evidence(
|
||||
"rx_rdrf_clear_before_rdr_read",
|
||||
rdrf_before_rdr,
|
||||
summary=(
|
||||
"ROM clears SCI1 SSR.RDRF before reading SCI1_RDR; preserve this observed "
|
||||
"ordering even though the manual describes the canonical RDR-read then "
|
||||
"RDRF-clear sequence"
|
||||
),
|
||||
manual_references=[
|
||||
"Manual/0900766b802125d0.md:16652 RDRF clear sequence reads RDR before clearing RDRF",
|
||||
"Manual/0900766b802125d0.md:16926 canonical receive flag clear sequence",
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
eri_fallthrough = _rx_eri_fallthrough_sequence(ordered)
|
||||
if eri_fallthrough:
|
||||
evidence.append(
|
||||
_evidence(
|
||||
"rx_eri_falls_through_to_rxi",
|
||||
eri_fallthrough,
|
||||
summary=(
|
||||
"SCI1 ERI latches FAA4.bit7, clears ORER/FER/PER, then falls through into "
|
||||
"the same RXI byte-capture path"
|
||||
),
|
||||
manual_references=[
|
||||
"Manual/0900766b802125d0.md:16703 FER/PER transfer errored data to RDR; ORER does not",
|
||||
"Manual/0900766b802125d0.md:16936 ERI is requested on ORER, FER, or PER",
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
indexed_stores = [ins for ins in ordered if _is_indexed_capture_store(ins)]
|
||||
if indexed_stores:
|
||||
evidence.append(
|
||||
@@ -435,6 +472,17 @@ def _rx_candidate_from_evidence(evidence: list[dict[str, object]]) -> dict[str,
|
||||
key: list(evidence_by_key[key]["addresses"])
|
||||
for key in _RX_REQUIRED_EVIDENCE
|
||||
}
|
||||
optional_evidence_keys = [
|
||||
key
|
||||
for key in (
|
||||
"rx_rdrf_clear_before_rdr_read",
|
||||
"rx_eri_falls_through_to_rxi",
|
||||
)
|
||||
if key in evidence_by_key
|
||||
]
|
||||
for key in optional_evidence_keys:
|
||||
evidence_addresses[key] = list(evidence_by_key[key]["addresses"])
|
||||
|
||||
return {
|
||||
"id": "sci1_rx_frame_f868_len6_candidate",
|
||||
"kind": "candidate_sci1_rx_frame",
|
||||
@@ -469,13 +517,18 @@ def _rx_candidate_from_evidence(evidence: list[dict[str, object]]) -> dict[str,
|
||||
"caveat": "candidate frame means six consecutive bytes within the observed RX timing/state machine, not a proven delimited packet",
|
||||
"required_evidence_count": len(_RX_REQUIRED_EVIDENCE),
|
||||
"observed_evidence_count": len(_RX_REQUIRED_EVIDENCE),
|
||||
"optional_evidence_count": len(optional_evidence_keys),
|
||||
"missing_evidence": [],
|
||||
"evidence_addresses": evidence_addresses,
|
||||
"evidence_addresses_hex": {
|
||||
key: [h16(address) for address in addresses]
|
||||
for key, addresses in evidence_addresses.items()
|
||||
},
|
||||
"evidence": [evidence_by_key[key] for key in _RX_REQUIRED_EVIDENCE],
|
||||
"evidence": [
|
||||
evidence_by_key[key]
|
||||
for key in [*_RX_REQUIRED_EVIDENCE, *optional_evidence_keys]
|
||||
],
|
||||
"rx_error_handling": _rx_error_handling_candidate(evidence_by_key),
|
||||
"short_comment": (
|
||||
f"candidate/evidence-supported SCI1 {RX_FRAME_LENGTH}-byte RX frame; "
|
||||
f"capture {h16(RX_CAPTURE_START)}-{h16(RX_CAPTURE_END)}, validate "
|
||||
@@ -708,6 +761,100 @@ def _rx_xor_checksum_validation(ordered: list[Instruction]) -> list[Instruction]
|
||||
return []
|
||||
|
||||
|
||||
def _rx_rdrf_clear_before_rdr_read(ordered: list[Instruction]) -> list[Instruction]:
|
||||
for index, ins in enumerate(ordered):
|
||||
if not _is_bclr_bit(ins, SCI1_SSR_ADDRESS, 6):
|
||||
continue
|
||||
window = ordered[index + 1:index + 5]
|
||||
for candidate in window:
|
||||
if _mnemonic_root(candidate.mnemonic) in {"RTE", "RTS"}:
|
||||
break
|
||||
if _is_read_from_address(candidate, SCI1_RDR_ADDRESS):
|
||||
return [ins, candidate]
|
||||
return []
|
||||
|
||||
|
||||
def _rx_eri_fallthrough_sequence(ordered: list[Instruction]) -> list[Instruction]:
|
||||
for index, ins in enumerate(ordered):
|
||||
if not _is_bset_bit(ins, RX_ERROR_LATCH_ADDRESS, 7):
|
||||
continue
|
||||
window = ordered[index:index + 18]
|
||||
if any(_mnemonic_root(candidate.mnemonic) in {"RTE", "RTS"} for candidate in window[:6]):
|
||||
continue
|
||||
error_clears: list[Instruction] = []
|
||||
for bit in (5, 4, 3):
|
||||
clear = next(
|
||||
(
|
||||
candidate for candidate in window
|
||||
if _is_bclr_bit(candidate, SCI1_SSR_ADDRESS, bit)
|
||||
),
|
||||
None,
|
||||
)
|
||||
if clear is not None:
|
||||
error_clears.append(clear)
|
||||
if len(error_clears) != 3:
|
||||
continue
|
||||
after_error = [
|
||||
candidate for candidate in window
|
||||
if candidate.address > max(clear.address for clear in error_clears)
|
||||
]
|
||||
byte_path = _rx_rdrf_clear_before_rdr_read(after_error)
|
||||
if byte_path:
|
||||
return _dedupe_instructions([ins, *error_clears, *byte_path])
|
||||
return []
|
||||
|
||||
|
||||
def _rx_error_handling_candidate(evidence_by_key: Mapping[str, dict[str, object]]) -> dict[str, object] | None:
|
||||
fallthrough = evidence_by_key.get("rx_eri_falls_through_to_rxi")
|
||||
clear_order = evidence_by_key.get("rx_rdrf_clear_before_rdr_read")
|
||||
if fallthrough is None and clear_order is None:
|
||||
return None
|
||||
evidence_items = [
|
||||
item for item in (fallthrough, clear_order) if isinstance(item, Mapping)
|
||||
]
|
||||
evidence_addresses = _dedupe_ints(
|
||||
int(address)
|
||||
for item in evidence_items
|
||||
for address in item.get("addresses", [])
|
||||
if isinstance(address, int)
|
||||
)
|
||||
return {
|
||||
"kind": "sci1_rx_error_handling_candidate",
|
||||
"error_latch_address": RX_ERROR_LATCH_ADDRESS,
|
||||
"error_latch_address_hex": h16(RX_ERROR_LATCH_ADDRESS),
|
||||
"error_latch_bit": 7,
|
||||
"fallthrough_to_rx_byte_path": fallthrough is not None,
|
||||
"rdrf_clear_before_rdr_read": clear_order is not None,
|
||||
"summary": (
|
||||
"SCI1 ERI appears to mark a physical receive error and continue into the RXI "
|
||||
"byte-capture path; the RXI path clears RDRF before reading RDR in the ROM order."
|
||||
),
|
||||
"manual_caveat": (
|
||||
"Manual text distinguishes ORER from FER/PER data transfer into RDR and describes "
|
||||
"the normal RDR-read then RDRF-clear ordering; this output preserves the observed ROM order."
|
||||
),
|
||||
"evidence_addresses": evidence_addresses,
|
||||
"evidence_addresses_hex": [h16(address) for address in evidence_addresses],
|
||||
"confidence": "candidate-medium" if fallthrough else "candidate-low",
|
||||
}
|
||||
|
||||
|
||||
def _is_bclr_bit(ins: Instruction, address: int, bit: int) -> bool:
|
||||
return (
|
||||
_mnemonic_root(ins.mnemonic) == "BCLR"
|
||||
and _is_write_to_address(ins, address)
|
||||
and _immediate_source_value(ins.operands) == bit
|
||||
)
|
||||
|
||||
|
||||
def _is_bset_bit(ins: Instruction, address: int, bit: int) -> bool:
|
||||
return (
|
||||
_mnemonic_root(ins.mnemonic) == "BSET"
|
||||
and _is_write_to_address(ins, address)
|
||||
and _immediate_source_value(ins.operands) == bit
|
||||
)
|
||||
|
||||
|
||||
def _is_read_from_address(ins: Instruction, address: int) -> bool:
|
||||
source, destination = _source_destination_operands(ins.operands)
|
||||
if _operand_mentions_address(source, address):
|
||||
@@ -792,6 +939,17 @@ def _dedupe_instructions(instructions: list[Instruction]) -> list[Instruction]:
|
||||
return output
|
||||
|
||||
|
||||
def _dedupe_ints(values: Iterable[int]) -> list[int]:
|
||||
output: list[int] = []
|
||||
seen: set[int] = set()
|
||||
for value in values:
|
||||
if value in seen:
|
||||
continue
|
||||
seen.add(value)
|
||||
output.append(value)
|
||||
return output
|
||||
|
||||
|
||||
def _instruction_sequence(
|
||||
instructions: Mapping[int, Instruction] | Iterable[Instruction],
|
||||
) -> list[Instruction]:
|
||||
@@ -837,9 +995,12 @@ def _operand_mentions_address(operand: str, address: int) -> bool:
|
||||
operand_upper = operand.upper().replace(" ", "")
|
||||
names = {
|
||||
SCI1_TDR_ADDRESS: ("SCI1_TDR",),
|
||||
SCI1_SSR_ADDRESS: ("SCI1_SSR",),
|
||||
SCI1_RDR_ADDRESS: ("SCI1_RDR",),
|
||||
TX_BUFFER_START: ("TX_BUFFER",),
|
||||
TX_CHECKSUM_ADDRESS: ("TX_CHECKSUM",),
|
||||
TX_INDEX_ADDRESS: ("TX_INDEX",),
|
||||
RX_ERROR_LATCH_ADDRESS: ("RX_ERROR_LATCH",),
|
||||
}
|
||||
if any(name in operand_upper for name in names.get(address, ())):
|
||||
return True
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user