traces
This commit is contained in:
@@ -57,6 +57,7 @@ from .cpu import CPUState
|
||||
from .errors import EmulatorError, UnsupportedInstruction
|
||||
from .fast_paths import P9FastPath, P9FastPathConfig, P9FastPathEvent
|
||||
from .memory import MemoryAccess, MemoryMap, describe_regions
|
||||
from .panel import PanelAction, PanelInjection, PanelInput, parse_panel_action, resolve_panel_input
|
||||
from .peripherals import LCD, P9TraceEvent, X24164Bus, X24164Device, X24164TraceEvent, factory_default_words_from_rom
|
||||
from .runner import H8536Emulator, RunReport
|
||||
from .sci import SCI1, SciTxEvent
|
||||
@@ -97,6 +98,9 @@ __all__ = [
|
||||
"P9FastPath",
|
||||
"P9FastPathConfig",
|
||||
"P9FastPathEvent",
|
||||
"PanelAction",
|
||||
"PanelInjection",
|
||||
"PanelInput",
|
||||
"P9TraceEvent",
|
||||
"RAMCR",
|
||||
"REGISTER_FIELD_END",
|
||||
@@ -137,4 +141,6 @@ __all__ = [
|
||||
"factory_default_words_from_rom",
|
||||
"load_rom",
|
||||
"main",
|
||||
"parse_panel_action",
|
||||
"resolve_panel_input",
|
||||
]
|
||||
|
||||
203
h8536/emulator/panel.py
Normal file
203
h8536/emulator/panel.py
Normal file
@@ -0,0 +1,203 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ..formatting import h16
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelInput:
|
||||
name: str
|
||||
source: int
|
||||
shadow: int
|
||||
previous: int
|
||||
bit: int
|
||||
dirty: int
|
||||
dirty_bit: int
|
||||
selector: int | None = None
|
||||
note: str = ""
|
||||
|
||||
@property
|
||||
def spec(self) -> str:
|
||||
return f"{h16(self.shadow)}.{self.bit}"
|
||||
|
||||
@property
|
||||
def dirty_spec(self) -> str:
|
||||
return f"{h16(self.dirty)}.{self.dirty_bit}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelAction:
|
||||
panel_input: PanelInput
|
||||
pressed: bool
|
||||
raw: str = ""
|
||||
|
||||
@property
|
||||
def label(self) -> str:
|
||||
state = "press" if self.pressed else "release"
|
||||
return f"{self.panel_input.name}:{state}"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PanelInjection:
|
||||
action: PanelAction
|
||||
source_before: int
|
||||
source_after: int
|
||||
shadow_before: int
|
||||
shadow_after: int
|
||||
previous_before: int
|
||||
previous_after: int
|
||||
dirty_before: int
|
||||
dirty_after: int
|
||||
|
||||
def summary(self) -> str:
|
||||
panel_input = self.action.panel_input
|
||||
selector = "" if panel_input.selector is None else f" selector=0x{panel_input.selector:04X}"
|
||||
return (
|
||||
f"{self.action.label} source={h16(panel_input.source)} "
|
||||
f"shadow={panel_input.spec} previous={h16(panel_input.previous)} "
|
||||
f"dirty={panel_input.dirty_spec}{selector}"
|
||||
)
|
||||
|
||||
|
||||
PANEL_LANES: tuple[tuple[int, int, int, int, int], ...] = (
|
||||
(0xF102, 0xF6D7, 0xF6E7, 0xF6F2, 7),
|
||||
(0xF103, 0xF6D6, 0xF6E6, 0xF6F2, 6),
|
||||
(0xF104, 0xF6D5, 0xF6E5, 0xF6F2, 5),
|
||||
(0xF105, 0xF6D4, 0xF6E4, 0xF6F2, 4),
|
||||
(0xF106, 0xF6D3, 0xF6E3, 0xF6F2, 3),
|
||||
(0xF107, 0xF6D2, 0xF6E2, 0xF6F2, 2),
|
||||
(0xF108, 0xF6D1, 0xF6E1, 0xF6F2, 1),
|
||||
(0xF109, 0xF6D0, 0xF6E0, 0xF6F2, 0),
|
||||
(0xF005, 0xF6DC, 0xF6EC, 0xF6F3, 4),
|
||||
(0xF006, 0xF6DB, 0xF6EB, 0xF6F3, 3),
|
||||
)
|
||||
|
||||
KNOWN_PANEL_INPUTS: dict[str, PanelInput] = {
|
||||
"cam-power": PanelInput(
|
||||
name="cam-power",
|
||||
source=0xF105,
|
||||
shadow=0xF6D4,
|
||||
previous=0xF6E4,
|
||||
bit=3,
|
||||
dirty=0xF6F2,
|
||||
dirty_bit=4,
|
||||
selector=0x0007,
|
||||
note="CAM POWER button; queues selector 0x0007 when gates allow",
|
||||
),
|
||||
"call": PanelInput(
|
||||
name="call",
|
||||
source=0xF006,
|
||||
shadow=0xF6DB,
|
||||
previous=0xF6EB,
|
||||
bit=5,
|
||||
dirty=0xF6F3,
|
||||
dirty_bit=3,
|
||||
selector=0x0015,
|
||||
note="CALL button; queues selector 0x0015 active/inactive reports",
|
||||
),
|
||||
}
|
||||
|
||||
PANEL_ALIASES: dict[str, str] = {
|
||||
"cam": "cam-power",
|
||||
"camera-power": "cam-power",
|
||||
"camera_power": "cam-power",
|
||||
"cam_power": "cam-power",
|
||||
"campower": "cam-power",
|
||||
"power": "cam-power",
|
||||
}
|
||||
|
||||
|
||||
def resolve_panel_input(text: str) -> PanelInput:
|
||||
token = _normalize_token(text)
|
||||
token = PANEL_ALIASES.get(token, token)
|
||||
if token in KNOWN_PANEL_INPUTS:
|
||||
return KNOWN_PANEL_INPUTS[token]
|
||||
if "." not in token:
|
||||
raise ValueError(f"unknown panel input {text!r}; use cam-power, call, or an address bit like F6D4.3")
|
||||
address_text, bit_text = token.split(".", 1)
|
||||
address = _parse_address(address_text)
|
||||
try:
|
||||
bit = int(bit_text, 0)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"invalid panel bit in {text!r}") from exc
|
||||
if not 0 <= bit <= 7:
|
||||
raise ValueError(f"panel bit out of range in {text!r}")
|
||||
for source, shadow, previous, dirty, dirty_bit in PANEL_LANES:
|
||||
if address in {source, shadow}:
|
||||
return PanelInput(
|
||||
name=f"{h16(shadow)}.{bit}",
|
||||
source=source,
|
||||
shadow=shadow,
|
||||
previous=previous,
|
||||
bit=bit,
|
||||
dirty=dirty,
|
||||
dirty_bit=dirty_bit,
|
||||
note="raw panel matrix input inferred from ROM shadow/dirty lane",
|
||||
)
|
||||
raise ValueError(f"{h16(address)} is not a known A8 panel byte shadow/source")
|
||||
|
||||
|
||||
def parse_panel_action(text: str, *, default_pressed: bool = True) -> PanelAction:
|
||||
raw = text.strip()
|
||||
spec = raw
|
||||
pressed = default_pressed
|
||||
for separator in ("=", ":"):
|
||||
if separator not in raw:
|
||||
continue
|
||||
left, right = raw.rsplit(separator, 1)
|
||||
state = _parse_state(right)
|
||||
if state is None:
|
||||
continue
|
||||
spec = left
|
||||
pressed = state
|
||||
break
|
||||
return PanelAction(resolve_panel_input(spec), pressed=pressed, raw=raw)
|
||||
|
||||
|
||||
def _parse_state(text: str) -> bool | None:
|
||||
token = _normalize_token(text)
|
||||
if token in {"press", "pressed", "on", "down", "1", "true", "active"}:
|
||||
return True
|
||||
if token in {"release", "released", "off", "up", "0", "false", "inactive"}:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
def _normalize_token(text: str) -> str:
|
||||
return text.strip().lower().replace("_", "-")
|
||||
|
||||
|
||||
def _parse_address(text: str) -> int:
|
||||
token = text.strip().upper()
|
||||
if token.startswith("H'"):
|
||||
token = token[2:]
|
||||
elif token.startswith("$"):
|
||||
token = token[1:]
|
||||
elif token.startswith("0X"):
|
||||
token = token[2:]
|
||||
try:
|
||||
value = int(token, 16)
|
||||
except ValueError as exc:
|
||||
raise ValueError(f"invalid panel address {text!r}") from exc
|
||||
if not 0 <= value <= 0xFFFF:
|
||||
raise ValueError(f"panel address out of range {text!r}")
|
||||
return value
|
||||
|
||||
|
||||
def set_bit(value: int, bit: int, enabled: bool) -> int:
|
||||
mask = 1 << bit
|
||||
return (value | mask) & 0xFF if enabled else (value & ~mask) & 0xFF
|
||||
|
||||
|
||||
__all__ = [
|
||||
"KNOWN_PANEL_INPUTS",
|
||||
"PANEL_ALIASES",
|
||||
"PANEL_LANES",
|
||||
"PanelAction",
|
||||
"PanelInjection",
|
||||
"PanelInput",
|
||||
"parse_panel_action",
|
||||
"resolve_panel_input",
|
||||
"set_bit",
|
||||
]
|
||||
@@ -39,6 +39,7 @@ from .cpu import CPUState, mask, s8, s16, sign_bit
|
||||
from .errors import EmulatorError, UnsupportedInstruction
|
||||
from .fast_paths import P9FastPath, P9FastPathConfig
|
||||
from .memory import MemoryMap
|
||||
from .panel import PanelAction, PanelInjection, PanelInput, resolve_panel_input, set_bit
|
||||
from .sci import SCI1
|
||||
from .timers import FrtOciaScheduler, FrtRegisters
|
||||
from .uart import UartTiming
|
||||
@@ -134,6 +135,38 @@ class H8536Emulator:
|
||||
def inject_sci1_rx_byte(self, value: int) -> None:
|
||||
self.memory.inject_sci1_rx_byte(value)
|
||||
|
||||
def inject_panel_input(self, panel_input: PanelInput | str, *, pressed: bool = True) -> PanelInjection:
|
||||
resolved = resolve_panel_input(panel_input) if isinstance(panel_input, str) else panel_input
|
||||
action = PanelAction(resolved, pressed=pressed)
|
||||
shadow_before = self.memory.read8(resolved.shadow)
|
||||
source_before = self.memory.external.get(resolved.source, shadow_before)
|
||||
previous_before = self.memory.read8(resolved.previous)
|
||||
dirty_before = self.memory.read8(resolved.dirty)
|
||||
|
||||
source_after = set_bit(source_before, resolved.bit, pressed)
|
||||
shadow_after = set_bit(shadow_before, resolved.bit, pressed)
|
||||
previous_after = set_bit(previous_before, resolved.bit, not pressed)
|
||||
dirty_after = dirty_before | (1 << resolved.dirty_bit)
|
||||
|
||||
self.memory.write8(resolved.source, source_after)
|
||||
self.memory.write8(resolved.shadow, shadow_after)
|
||||
# The ROM's loc_1Bxx dispatchers act on shadow XOR previous-shadow.
|
||||
# Force the previous sample opposite at this bit so the main loop sees one clean edge.
|
||||
self.memory.write8(resolved.previous, previous_after)
|
||||
self.memory.write8(resolved.dirty, dirty_after)
|
||||
|
||||
return PanelInjection(
|
||||
action=action,
|
||||
source_before=source_before,
|
||||
source_after=source_after,
|
||||
shadow_before=shadow_before,
|
||||
shadow_after=shadow_after,
|
||||
previous_before=previous_before,
|
||||
previous_after=previous_after,
|
||||
dirty_before=dirty_before,
|
||||
dirty_after=dirty_after,
|
||||
)
|
||||
|
||||
def step(self) -> str:
|
||||
pc = self.cpu.pc
|
||||
cycles_before = self.cpu.cycles
|
||||
|
||||
@@ -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