traces
This commit is contained in:
@@ -18,6 +18,7 @@ from .constants import (
|
||||
from .eeprom_image import write_eeprom_snapshot
|
||||
from .errors import UnsupportedInstruction
|
||||
from .memory import MemoryAccess
|
||||
from .panel import PanelAction, parse_panel_action, resolve_panel_input
|
||||
from .runner import H8536Emulator
|
||||
from .uart import UartTiming
|
||||
|
||||
@@ -33,6 +34,13 @@ CONNECT_LCD_FRAMES = (
|
||||
)
|
||||
|
||||
WATCH_PCS = {
|
||||
0x15E0: "main_panel_scanner",
|
||||
0x1BA0: "panel_f6d4_edge_dispatch",
|
||||
0x1BF8: "panel_f6db_edge_dispatch",
|
||||
0x1C0E: "panel_bit_dispatch",
|
||||
0x1F40: "cam_power_handler",
|
||||
0x20A1: "call_handler",
|
||||
0x3E54: "report_queue_enqueue",
|
||||
0xBB57: "sci1_eri_entry",
|
||||
0xBB67: "sci1_rxi_entry",
|
||||
0xBBD6: "rx_checksum_seed",
|
||||
@@ -56,6 +64,10 @@ WATCH_RANGES = (
|
||||
)
|
||||
|
||||
ACCESS_RANGES = (
|
||||
(0xF000, 0xF10F, "panel_external_bytes"),
|
||||
(0xF6D0, 0xF6DF, "panel_shadow_bytes"),
|
||||
(0xF6E0, 0xF6EF, "panel_previous_shadow_bytes"),
|
||||
(0xF6F0, 0xF6F3, "panel_dirty_bytes"),
|
||||
(0xF850, 0xF85D, "tx_staging_or_frame"),
|
||||
(0xF860, 0xF86D, "rx_validation_or_capture"),
|
||||
(0xF870, 0xF96F, "report_queue"),
|
||||
@@ -64,8 +76,12 @@ ACCESS_RANGES = (
|
||||
(0xFAA2, 0xFAA6, "serial_latches"),
|
||||
(0xFAF0, 0xFAFF, "lcd_line_buffer"),
|
||||
(0xE000, 0xE001, "primary_table_index_0000"),
|
||||
(0xE00E, 0xE00F, "primary_cam_power_index_0007"),
|
||||
(0xE02A, 0xE02B, "primary_call_index_0015"),
|
||||
(0xE400, 0xE401, "secondary_table_index_0000"),
|
||||
(0xE800, 0xE801, "current_table_index_0000"),
|
||||
(0xE80E, 0xE80F, "current_cam_power_index_0007"),
|
||||
(0xE82A, 0xE82B, "current_call_index_0015"),
|
||||
(0xEC00, 0xEC01, "flag_table_index_0000"),
|
||||
(0xE000, 0xE3FF, "primary_table_E000"),
|
||||
(0xE400, 0xE7FF, "secondary_table_E400"),
|
||||
@@ -77,6 +93,12 @@ ACCESS_RANGES = (
|
||||
)
|
||||
|
||||
STATE_BYTES = {
|
||||
0xF6D4: "panel_shadow_F6D4_cam_lane",
|
||||
0xF6DB: "panel_shadow_F6DB_call_lane",
|
||||
0xF6E4: "panel_previous_F6E4_cam_lane",
|
||||
0xF6EB: "panel_previous_F6EB_call_lane",
|
||||
0xF6F2: "panel_dirty_F6F2",
|
||||
0xF6F3: "panel_dirty_F6F3",
|
||||
0xF9B0: "queue_head",
|
||||
0xF9B5: "queue_tail",
|
||||
0xF9C0: "tx_gate",
|
||||
@@ -104,12 +126,14 @@ STATE_WORDS = {
|
||||
0xE000: "E000_index_0000_primary",
|
||||
0xE004: "E000_index_0002_primary",
|
||||
0xE008: "E000_index_0004_primary",
|
||||
0xE00E: "E000_index_0007_cam_power_primary",
|
||||
0xE024: "E000_index_0012_primary",
|
||||
0xE026: "E000_index_0013_primary",
|
||||
0xE02A: "E000_index_0015_primary",
|
||||
0xE104: "E000_index_0082_primary",
|
||||
0xE400: "E400_index_0000_secondary",
|
||||
0xE800: "E800_index_0000_current",
|
||||
0xE80E: "E800_index_0007_cam_power_current",
|
||||
0xE804: "E800_index_0002_current",
|
||||
0xE808: "E800_index_0004_current",
|
||||
0xE824: "E800_index_0012_current",
|
||||
@@ -189,6 +213,50 @@ class FrameResult:
|
||||
return lines
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelActionResult:
|
||||
action: PanelAction
|
||||
injection_summary: str
|
||||
steps: int
|
||||
stopped_reason: str
|
||||
new_tx_bytes: bytes
|
||||
new_tx_frames: list[bytes]
|
||||
state_before: dict[str, int | str]
|
||||
state_after: dict[str, int | str]
|
||||
accesses: list[MemoryAccess]
|
||||
context: RunContext
|
||||
|
||||
def lines(self, index: int) -> list[str]:
|
||||
lines = [
|
||||
f"panel_action[{index}]={self.action.label} input={self.action.panel_input.spec}",
|
||||
f" injection={self.injection_summary}",
|
||||
f" stopped={self.stopped_reason} steps={self.steps}",
|
||||
f" new_tx_bytes={format_frame(self.new_tx_bytes) if self.new_tx_bytes else 'none'}",
|
||||
]
|
||||
if self.new_tx_frames:
|
||||
lines.append(" new_tx_frames=" + " | ".join(format_frame(frame) for frame in self.new_tx_frames))
|
||||
else:
|
||||
lines.append(" new_tx_frames=none")
|
||||
lcd_display = self.state_after.get("lcd_display_ascii")
|
||||
if isinstance(lcd_display, str):
|
||||
lines.append(f" lcd_display={lcd_display!r}")
|
||||
state_changes = _state_change_lines(self.state_before, self.state_after)
|
||||
if state_changes:
|
||||
lines.append(" state_changes:")
|
||||
lines.extend(f" {line}" for line in state_changes)
|
||||
pc_lines = _pc_hit_lines(self.context)
|
||||
if pc_lines:
|
||||
lines.append(" pc_hits:")
|
||||
lines.extend(f" {line}" for line in pc_lines)
|
||||
access_lines = _access_lines(self.accesses)
|
||||
if access_lines:
|
||||
lines.append(" interesting_accesses:")
|
||||
lines.extend(f" {line}" for line in access_lines)
|
||||
if self.context.unsupported:
|
||||
lines.append(f" unsupported={self.context.unsupported}")
|
||||
return lines
|
||||
|
||||
|
||||
def parse_frame(text: str) -> bytes:
|
||||
normalized = text.strip().replace(",", " ").replace(":", " ").replace("-", " ").replace("_", " ")
|
||||
parts = normalized.split()
|
||||
@@ -209,6 +277,27 @@ def parse_frame(text: str) -> bytes:
|
||||
return bytes(values)
|
||||
|
||||
|
||||
def parse_panel_action_arg(text: str) -> PanelAction:
|
||||
try:
|
||||
return parse_panel_action(text)
|
||||
except ValueError as exc:
|
||||
raise argparse.ArgumentTypeError(str(exc)) from exc
|
||||
|
||||
|
||||
def parse_panel_press_arg(text: str) -> PanelAction:
|
||||
try:
|
||||
return PanelAction(resolve_panel_input(text), pressed=True, raw=text)
|
||||
except ValueError as exc:
|
||||
raise argparse.ArgumentTypeError(str(exc)) from exc
|
||||
|
||||
|
||||
def parse_panel_release_arg(text: str) -> PanelAction:
|
||||
try:
|
||||
return PanelAction(resolve_panel_input(text), pressed=False, raw=text)
|
||||
except ValueError as exc:
|
||||
raise argparse.ArgumentTypeError(str(exc)) from exc
|
||||
|
||||
|
||||
def frame_checksum(data: bytes) -> int:
|
||||
checksum = CHECKSUM_SEED
|
||||
for value in data[: FRAME_LENGTH - 1]:
|
||||
@@ -318,6 +407,11 @@ def build_arg_parser() -> argparse.ArgumentParser:
|
||||
parser.add_argument("--per-byte-steps", type=int, default=5_000, help="polite mode byte-consume limit, or UART mode step limit between byte arrivals")
|
||||
parser.add_argument("--post-frame-steps", type=int, default=80_000, help="maximum steps after a full injected frame")
|
||||
parser.add_argument("--post-frame-ms", type=int, help="run this many emulated milliseconds after each injected frame")
|
||||
parser.add_argument("--panel", action="append", type=parse_panel_action_arg, default=[], help="synthetic panel action, e.g. cam-power, call=release, or F6D4.3=press; preserves order across repeated --panel uses")
|
||||
parser.add_argument("--panel-press", action="append", type=parse_panel_press_arg, default=[], help="synthetic panel press alias/spec, e.g. cam-power, call, or F6D4.3")
|
||||
parser.add_argument("--panel-release", action="append", type=parse_panel_release_arg, default=[], help="synthetic panel release alias/spec, e.g. call or F6DB.5")
|
||||
parser.add_argument("--post-panel-steps", type=int, default=120_000, help="maximum steps after each synthetic panel action")
|
||||
parser.add_argument("--post-panel-ms", type=int, help="run this many emulated milliseconds after each synthetic panel action")
|
||||
parser.add_argument("--wait-heartbeats", type=int, default=0, help="wait for this many heartbeat frames before injecting the first host frame")
|
||||
parser.add_argument("--wait-heartbeat-steps", type=int, default=1_500_000, help="maximum steps while waiting for pre-injection heartbeat frames")
|
||||
parser.add_argument("--uart-timing", action="store_true", help="inject frame bytes at real UART inter-byte timing instead of waiting for RDRF consumption")
|
||||
@@ -347,8 +441,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
frames = list(args.frames)
|
||||
if args.preset == "connect-lcd":
|
||||
frames.extend(CONNECT_LCD_FRAMES)
|
||||
if not frames:
|
||||
raise SystemExit("pass at least one frame or use --preset connect-lcd")
|
||||
panel_actions = [*args.panel, *args.panel_press, *args.panel_release]
|
||||
if not frames and not panel_actions:
|
||||
raise SystemExit("pass at least one frame, use --preset connect-lcd, or add --panel/--panel-press")
|
||||
|
||||
rom_path, emulator, boot_summary, results = run_rx_probe(
|
||||
frames,
|
||||
@@ -384,6 +479,19 @@ def main(argv: list[str] | None = None) -> int:
|
||||
for index, result in enumerate(results):
|
||||
for line in result.lines(index):
|
||||
print(line)
|
||||
panel_results = [
|
||||
_run_panel_action(
|
||||
emulator,
|
||||
action,
|
||||
post_panel_steps=args.post_panel_steps,
|
||||
post_panel_ms=args.post_panel_ms,
|
||||
stop_after_tx_frame=not args.keep_listening,
|
||||
)
|
||||
for action in panel_actions
|
||||
]
|
||||
for index, result in enumerate(panel_results):
|
||||
for line in result.lines(index):
|
||||
print(line)
|
||||
print("total_tx_frames=" + " | ".join(format_frame(frame) for frame in emulator.sci1.tx_frames))
|
||||
eeprom_writes = emulator.memory.p9_bus.x24164_bus.write_log_lines(limit=80)
|
||||
if eeprom_writes:
|
||||
@@ -484,6 +592,49 @@ def _run_frame(
|
||||
)
|
||||
|
||||
|
||||
def _run_panel_action(
|
||||
emulator: H8536Emulator,
|
||||
action: PanelAction,
|
||||
*,
|
||||
post_panel_steps: int,
|
||||
post_panel_ms: int | None,
|
||||
stop_after_tx_frame: bool,
|
||||
) -> PanelActionResult:
|
||||
state_before = _state_snapshot(emulator)
|
||||
log_start = len(emulator.memory.access_log)
|
||||
tx_byte_start = len(emulator.sci1.tx_bytes)
|
||||
tx_frame_start = len(emulator.sci1.tx_frames)
|
||||
injection = emulator.inject_panel_input(action.panel_input, pressed=action.pressed)
|
||||
context = RunContext()
|
||||
|
||||
def post_predicate(inner: H8536Emulator) -> bool:
|
||||
if not stop_after_tx_frame:
|
||||
return False
|
||||
return any(frame != HEARTBEAT_FRAME for frame in inner.sci1.tx_frames[tx_frame_start:])
|
||||
|
||||
if post_panel_ms is not None:
|
||||
steps, reason = _run_cycles_for_ms(emulator, post_panel_ms, context)
|
||||
stopped_reason = reason
|
||||
else:
|
||||
steps, reason = _run_until(emulator, post_panel_steps, post_predicate, context)
|
||||
stopped_reason = "non_heartbeat_tx_frame" if reason == "predicate" and stop_after_tx_frame else reason
|
||||
|
||||
log_end = len(emulator.memory.access_log)
|
||||
state_after = _state_snapshot(emulator)
|
||||
return PanelActionResult(
|
||||
action=action,
|
||||
injection_summary=injection.summary(),
|
||||
steps=steps,
|
||||
stopped_reason=stopped_reason,
|
||||
new_tx_bytes=bytes(emulator.sci1.tx_bytes[tx_byte_start:]),
|
||||
new_tx_frames=list(emulator.sci1.tx_frames[tx_frame_start:]),
|
||||
state_before=state_before,
|
||||
state_after=state_after,
|
||||
accesses=emulator.memory.access_log[log_start:log_end],
|
||||
context=context,
|
||||
)
|
||||
|
||||
|
||||
def _inject_frame_uart_timed(
|
||||
emulator: H8536Emulator,
|
||||
frame: bytes,
|
||||
@@ -672,11 +823,15 @@ def _parse_byte(text: str) -> int:
|
||||
|
||||
__all__ = [
|
||||
"CONNECT_LCD_FRAMES",
|
||||
"PanelActionResult",
|
||||
"format_frame",
|
||||
"frame_checksum",
|
||||
"frame_checksum_ok",
|
||||
"main",
|
||||
"parse_frame",
|
||||
"parse_panel_action_arg",
|
||||
"parse_panel_press_arg",
|
||||
"parse_panel_release_arg",
|
||||
"run_rx_probe",
|
||||
"UartTiming",
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user