1
0

command advance sweep

This commit is contained in:
Aiden
2026-05-26 15:21:52 +10:00
parent 74a2e2fd2c
commit a48fa0ed18
14 changed files with 821 additions and 78 deletions

View File

@@ -24,6 +24,7 @@ from .uart import UartTiming
CHECKSUM_SEED = 0x5A
FRAME_LENGTH = 6
HEARTBEAT_FRAME = bytes.fromhex("0000000080DA")
CONNECT_LCD_FRAMES = (
bytes.fromhex("04000040001E"),
@@ -66,6 +67,12 @@ ACCESS_RANGES = (
(0xE400, 0xE401, "secondary_table_index_0000"),
(0xE800, 0xE801, "current_table_index_0000"),
(0xEC00, 0xEC01, "flag_table_index_0000"),
(0xE000, 0xE3FF, "primary_table_E000"),
(0xE400, 0xE7FF, "secondary_table_E400"),
(0xE800, 0xEBFF, "current_table_E800"),
(0xEC00, 0xEFFF, "flag_table_EC00"),
(0xF400, 0xF4FF, "eeprom_shadow_F400"),
(0xF7B0, 0xF82F, "persistent_record_ram"),
(0xF200, 0xF201, "lcd_ports"),
)
@@ -85,12 +92,30 @@ STATE_BYTES = {
0xFAA4: "rx_error_latch",
0xFAA5: "retry_or_gate_flags",
0xFAA6: "retry_counter",
0xEC02: "EC00_flag_index_0002",
0xEC04: "EC00_flag_index_0004",
0xEC12: "EC00_flag_index_0012",
0xEC13: "EC00_flag_index_0013",
0xEC15: "EC00_flag_index_0015",
0xEC82: "EC00_flag_index_0082",
}
STATE_WORDS = {
0xE000: "E000_index_0000_primary",
0xE004: "E000_index_0002_primary",
0xE008: "E000_index_0004_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",
0xE804: "E800_index_0002_current",
0xE808: "E800_index_0004_current",
0xE824: "E800_index_0012_current",
0xE826: "E800_index_0013_current",
0xE82A: "E800_index_0015_current",
0xE904: "E800_index_0082_current",
0xF860: "rx_frame_01",
0xF862: "rx_frame_23",
0xF864: "rx_frame_45",
@@ -206,8 +231,13 @@ def run_rx_probe(
boot_steps: int = 250_000,
per_byte_steps: int = 5_000,
post_frame_steps: int = 80_000,
post_frame_ms: int | None = None,
wait_heartbeats: int = 0,
wait_heartbeat_steps: int = 1_500_000,
uart_timing: bool = False,
uart_baud: int = 38_400,
uart_format: str = "8E1",
tx_wire_timing: bool = False,
interval_steps: int = 512,
frt1_ocia_steps: int | None = None,
frt2_ocia_steps: int | None = None,
@@ -232,6 +262,7 @@ def run_rx_probe(
p9_fast_default_wrapper_success=p9_fast_optimistic_wrapper,
p7_input=p7_input,
eeprom_seed=eeprom_seed,
sci1_tx_timing=UartTiming.from_format(uart_format, baud=uart_baud) if tx_wire_timing else None,
)
if eeprom_image is not None:
emulator.memory.load_eeprom_image(eeprom_image)
@@ -242,9 +273,24 @@ def run_rx_probe(
f"boot={boot_reason} steps={boot_steps_used} pc={h16(emulator.cpu.pc)} "
f"SCR={emulator.sci1.scr:02X} SSR={emulator.sci1.ssr:02X} "
f"rx_serviceable={int(_rx_ready(emulator))} "
f"clock_hz={emulator.clock_hz} "
f"clock_hz={emulator.clock_hz} uart_format={uart_format.upper()} "
f"tx_wire_timing={int(tx_wire_timing)} "
f"lcd_display={emulator.memory.lcd.display_text(lines=4)!r}"
)
if wait_heartbeats:
initial_heartbeats = sum(1 for frame in emulator.sci1.tx_frames if frame == HEARTBEAT_FRAME)
target_heartbeats = initial_heartbeats + wait_heartbeats
def heartbeat_predicate(inner: H8536Emulator) -> bool:
return sum(1 for frame in inner.sci1.tx_frames if frame == HEARTBEAT_FRAME) >= target_heartbeats
wait_context = RunContext()
wait_steps, wait_reason = _run_until(emulator, wait_heartbeat_steps, heartbeat_predicate, wait_context)
final_heartbeats = sum(1 for frame in emulator.sci1.tx_frames if frame == HEARTBEAT_FRAME)
boot_summary += (
f" wait_heartbeats={wait_heartbeats} wait_reason={wait_reason} "
f"wait_steps={wait_steps} heartbeat_count={final_heartbeats}"
)
results = [
_run_frame(
@@ -252,8 +298,10 @@ def run_rx_probe(
frame,
per_byte_steps=per_byte_steps,
post_frame_steps=post_frame_steps,
post_frame_ms=post_frame_ms,
uart_timing=uart_timing,
uart_baud=uart_baud,
uart_format=uart_format,
stop_after_tx_frame=stop_after_tx_frame,
)
for frame in frames
@@ -269,8 +317,13 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--boot-steps", type=int, default=250_000, help="maximum steps to boot until SCI1 RXI is serviceable")
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("--uart-timing", action="store_true", help="inject frame bytes at real 8N1 UART inter-byte timing instead of waiting for RDRF consumption")
parser.add_argument("--post-frame-ms", type=int, help="run this many emulated milliseconds after each injected frame")
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")
parser.add_argument("--uart-baud", type=parse_int, default=38_400, help="baud rate for --uart-timing; 38400 gives about 260 us per 8N1 byte")
parser.add_argument("--uart-format", default="8E1", help="UART character format for timed RX/TX modeling; real RCP link is 8E1")
parser.add_argument("--tx-wire-timing", action="store_true", help="delay SCI1 TDRE/TXI by one modeled UART character after each TDR write")
parser.add_argument("--keep-listening", action="store_true", help="use all post-frame steps instead of stopping at the first new TX frame")
parser.add_argument("--interval-steps", type=int, default=512, help="rough step period for the scaffolded interval timer interrupt")
parser.add_argument("--clock-hz", type=parse_int, default=10_000_000, help="CPU/phi clock in Hz for calibrated FRT timing")
@@ -303,8 +356,13 @@ def main(argv: list[str] | None = None) -> int:
boot_steps=args.boot_steps,
per_byte_steps=args.per_byte_steps,
post_frame_steps=args.post_frame_steps,
post_frame_ms=args.post_frame_ms,
wait_heartbeats=args.wait_heartbeats,
wait_heartbeat_steps=args.wait_heartbeat_steps,
uart_timing=args.uart_timing,
uart_baud=args.uart_baud,
uart_format=args.uart_format,
tx_wire_timing=args.tx_wire_timing,
interval_steps=args.interval_steps,
frt1_ocia_steps=args.frt1_ocia_steps,
frt2_ocia_steps=args.frt2_ocia_steps,
@@ -327,6 +385,13 @@ def main(argv: list[str] | None = None) -> int:
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:
print("eeprom_writes:")
for line in eeprom_writes:
print(f" {line}")
else:
print("eeprom_writes=none")
if args.eeprom_save:
args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
@@ -352,8 +417,10 @@ def _run_frame(
*,
per_byte_steps: int,
post_frame_steps: int,
post_frame_ms: int | None,
uart_timing: bool,
uart_baud: int,
uart_format: str,
stop_after_tx_frame: bool,
) -> FrameResult:
state_before = _state_snapshot(emulator)
@@ -363,7 +430,7 @@ def _run_frame(
context = RunContext()
stopped_reason = "post_frame_steps"
steps_total = 0
timing = UartTiming(baud=uart_baud)
timing = UartTiming.from_format(uart_format, baud=uart_baud)
if uart_timing:
steps_total, stopped_reason = _inject_frame_uart_timed(
@@ -391,9 +458,13 @@ def _run_frame(
def post_predicate(inner: H8536Emulator) -> bool:
return stop_after_tx_frame and len(inner.sci1.tx_frames) >= target_frame_count
steps, reason = _run_until(emulator, post_frame_steps, post_predicate, context)
if post_frame_ms is not None:
steps, reason = _run_cycles_for_ms(emulator, post_frame_ms, context)
stopped_reason = reason
else:
steps, reason = _run_until(emulator, post_frame_steps, post_predicate, context)
stopped_reason = "tx_frame" if reason == "predicate" and stop_after_tx_frame else reason
steps_total += steps
stopped_reason = "tx_frame" if reason == "predicate" and stop_after_tx_frame else reason
log_end = len(emulator.memory.access_log)
state_after = _state_snapshot(emulator)
@@ -475,6 +546,22 @@ def _run_until_cycle(
return max(0, max_steps), "max_steps"
def _run_cycles_for_ms(emulator: H8536Emulator, delta_ms: int, context: RunContext) -> tuple[int, str]:
target_delta_cycles = int((max(0, delta_ms) * max(1, emulator.clock_hz)) / 1000)
target_cycles = emulator.cpu.cycles + target_delta_cycles
completed = 0
while emulator.cpu.cycles < target_cycles:
pc = emulator.cpu.pc
context.record_pc(pc)
try:
emulator.step()
except UnsupportedInstruction as exc:
context.unsupported = str(exc)
return completed, "unsupported_instruction"
completed += 1
return completed, f"post_frame_ms_{delta_ms}"
def _rx_ready(emulator: H8536Emulator) -> bool:
if not (emulator.sci1.scr & SCI_SCR_RIE and emulator.sci1.scr & SCI_SCR_RE):
return False
@@ -546,7 +633,8 @@ def _access_lines(accesses: list[MemoryAccess]) -> list[str]:
lines = []
for access in interesting[:80]:
label = _access_label(access.address)
lines.append(f"{access.kind:<5} {h16(access.address)} {access.value:02X} {label}")
pc = f" pc={h16(access.pc)}" if access.pc is not None else ""
lines.append(f"{access.kind:<5} {h16(access.address)} {access.value:02X} {label}{pc}")
if len(interesting) > 80:
lines.append(f"... {len(interesting) - 80} more interesting accesses")
return lines