1
0

EEPROM layout

This commit is contained in:
Aiden
2026-05-26 11:35:21 +10:00
parent 1ad03d5692
commit edb8ed78f3
19 changed files with 169583 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import argparse
from pathlib import Path
from ..formatting import h16, parse_int
from .eeprom_image import write_eeprom_snapshot
from .memory import describe_regions
from .runner import H8536Emulator
@@ -47,6 +48,11 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model")
parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF")
parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset")
parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before running")
parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after running")
parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after running")
parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after running")
parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports")
return parser
@@ -70,6 +76,9 @@ def main(argv: list[str] | None = None) -> int:
p7_input=args.p7_input,
eeprom_seed=args.eeprom_seed,
)
if args.eeprom_load:
emulator.memory.load_eeprom_image(args.eeprom_load.read_bytes())
print(f"eeprom_loaded={args.eeprom_load}")
print(f"rom={rom_path}")
print(f"reset_vector={h16(emulator.reset_vector())}")
if args.memory_map:
@@ -82,4 +91,20 @@ def main(argv: list[str] | None = None) -> int:
print(line)
if not report.heartbeat_seen:
print("heartbeat_status=not reached; no heartbeat is reported unless bytes are emitted via SCI1_TDR")
if args.eeprom_save:
args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
print(f"eeprom_saved={args.eeprom_save}")
if args.eeprom_report:
write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=rom_bytes)
print(f"eeprom_report={args.eeprom_report}")
if args.eeprom_report_json:
write_eeprom_snapshot(
emulator.memory,
args.eeprom_report_json,
rom_bytes=rom_bytes,
as_json=True,
include_image_hex=args.eeprom_report_include_image,
)
print(f"eeprom_report_json={args.eeprom_report_json}")
return 0

View File

@@ -0,0 +1,351 @@
from __future__ import annotations
import hashlib
import json
from collections import defaultdict
from collections.abc import Mapping
from pathlib import Path
from typing import Any
from ..formatting import h16
from .memory import MemoryMap
from .peripherals.x24164 import (
X24164_FACTORY_DEFAULT_BYTES,
X24164_LOGICAL_PAGE_COUNT,
X24164_LOGICAL_PAGE_SIZE,
X24164_LOGICAL_SIZE,
factory_default_words_from_rom,
)
JsonObject = dict[str, Any]
SELECTOR_MAP_BASE = 0xC564
SELECTOR_MAP_COUNT = 0x0200
RECORD_BYTES = 8
SHADOW_BASE = 0xF400
def build_eeprom_snapshot(
memory: MemoryMap,
*,
rom_bytes: bytes | None = None,
include_image_hex: bool = False,
) -> JsonObject:
image = memory.dump_eeprom_image()
selectors_by_offset = _selectors_by_offset(rom_bytes)
factory_image = _factory_image(rom_bytes)
write_events = _write_events(memory, selectors_by_offset)
write_words = _coalesced_write_words(memory, selectors_by_offset)
factory_diffs = _factory_diffs(image, factory_image, selectors_by_offset)
report: JsonObject = {
"kind": "emulator_eeprom_snapshot",
"summary": {
"logical_size": len(image),
"logical_size_hex": f"0x{len(image):04X}",
"sha256": hashlib.sha256(image).hexdigest(),
"write_byte_count": len(write_events),
"write_word_count": len(write_words),
"factory_diff_word_count": len(factory_diffs),
"record_count": X24164_LOGICAL_PAGE_COUNT,
},
"records": _records(image),
"write_events": write_events,
"write_word_events": write_words,
"factory_diffs": factory_diffs,
"shadow_f400": _shadow_summary(memory, rom_bytes, selectors_by_offset),
}
if include_image_hex:
report["image_hex"] = image.hex()
return report
def format_eeprom_snapshot(report: Mapping[str, Any], *, limit: int = 80) -> str:
summary = report["summary"]
lines = [
"Emulator EEPROM Snapshot",
"",
f"size={summary['logical_size_hex']} sha256={summary['sha256']}",
(
f"writes: bytes={summary['write_byte_count']} words={summary['write_word_count']} "
f"factory_diff_words={summary['factory_diff_word_count']}"
),
"",
"Persistent Records:",
]
for record in report.get("records", []):
lines.append(
f"- page {record['page_hex']} EEPROM {record['range_hex']} "
f"bytes={record['bytes_hex']} text={record['ascii']!r}"
)
lines.extend(["", "EEPROM Word Writes:"])
word_events = list(report.get("write_word_events", []))
if not word_events:
lines.append("- none since EEPROM setup/load")
for event in word_events[:limit]:
suffix = _event_suffix(event)
lines.append(
f"- {event['address_hex']} page={event['page_hex']} offset={event['offset_hex']} "
f"{event['old_word_hex']}->{event['new_word_hex']} source={event['source']}{suffix}"
)
if len(word_events) > limit:
lines.append(f"- ... {len(word_events) - limit} more word writes omitted")
lines.extend(["", "Factory Diffs:"])
diffs = list(report.get("factory_diffs", []))
if not diffs:
lines.append("- current EEPROM image matches ROM factory/default image")
for diff in diffs[:limit]:
suffix = _event_suffix(diff)
lines.append(
f"- {diff['address_hex']} page={diff['page_hex']} offset={diff['offset_hex']} "
f"expected={diff['expected_word_hex']} actual={diff['actual_word_hex']}{suffix}"
)
if len(diffs) > limit:
lines.append(f"- ... {len(diffs) - limit} more factory diffs omitted")
shadow = report.get("shadow_f400", {})
lines.extend(["", "F400 Shadow Diffs:"])
shadow_diffs = list(shadow.get("diffs", [])) if isinstance(shadow, Mapping) else []
if not shadow_diffs:
lines.append("- F400-F4FF shadow matches ROM factory words or no ROM factory baseline was supplied")
for diff in shadow_diffs[:limit]:
suffix = _event_suffix(diff)
lines.append(
f"- {diff['address_hex']} offset={diff['offset_hex']} "
f"expected={diff['expected_word_hex']} actual={diff['actual_word_hex']}{suffix}"
)
if len(shadow_diffs) > limit:
lines.append(f"- ... {len(shadow_diffs) - limit} more shadow diffs omitted")
return "\n".join(lines).rstrip() + "\n"
def write_eeprom_snapshot(
memory: MemoryMap,
output_path: Path,
*,
rom_bytes: bytes | None = None,
as_json: bool = False,
include_image_hex: bool = False,
) -> JsonObject:
report = build_eeprom_snapshot(memory, rom_bytes=rom_bytes, include_image_hex=include_image_hex)
output_path.parent.mkdir(parents=True, exist_ok=True)
if as_json:
output_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="utf-8")
else:
output_path.write_text(format_eeprom_snapshot(report), encoding="utf-8")
return report
def _write_events(memory: MemoryMap, selectors_by_offset: Mapping[int, list[int]]) -> list[JsonObject]:
events = []
for index, event in enumerate(memory.p9_bus.x24164_bus.write_events):
item = _address_info(event.logical_address, selectors_by_offset)
item.update(
{
"index": index,
"device": event.device,
"device_offset": event.device_offset,
"device_offset_hex": f"0x{event.device_offset:03X}",
"old_value": event.old_value,
"old_value_hex": f"0x{event.old_value & 0xFF:02X}",
"new_value": event.new_value,
"new_value_hex": f"0x{event.new_value & 0xFF:02X}",
"source": event.source,
}
)
events.append(item)
return events
def _coalesced_write_words(memory: MemoryMap, selectors_by_offset: Mapping[int, list[int]]) -> list[JsonObject]:
events = memory.p9_bus.x24164_bus.write_events
words: list[JsonObject] = []
index = 0
while index < len(events):
event = events[index]
next_event = events[index + 1] if index + 1 < len(events) else None
if (
next_event is not None
and (event.logical_address & 1) == 0
and next_event.logical_address == ((event.logical_address + 1) & 0x0FFF)
and next_event.device == event.device
and next_event.source == event.source
):
old_word = ((event.old_value & 0xFF) << 8) | (next_event.old_value & 0xFF)
new_word = ((event.new_value & 0xFF) << 8) | (next_event.new_value & 0xFF)
item = _address_info(event.logical_address, selectors_by_offset)
item.update(
{
"index": index,
"device": event.device,
"old_word": old_word,
"old_word_hex": f"0x{old_word:04X}",
"new_word": new_word,
"new_word_hex": f"0x{new_word:04X}",
"source": event.source,
}
)
words.append(item)
index += 2
else:
index += 1
return words
def _factory_diffs(
image: bytes,
factory_image: bytes | None,
selectors_by_offset: Mapping[int, list[int]],
) -> list[JsonObject]:
if factory_image is None:
return []
diffs = []
for address in range(0, min(len(image), len(factory_image)), 2):
expected = (factory_image[address] << 8) | factory_image[address + 1]
actual = (image[address] << 8) | image[address + 1]
if expected == actual:
continue
item = _address_info(address, selectors_by_offset)
item.update(
{
"expected_word": expected,
"expected_word_hex": f"0x{expected:04X}",
"actual_word": actual,
"actual_word_hex": f"0x{actual:04X}",
}
)
diffs.append(item)
return diffs
def _shadow_summary(
memory: MemoryMap,
rom_bytes: bytes | None,
selectors_by_offset: Mapping[int, list[int]],
) -> JsonObject:
if rom_bytes is None:
return {"diffs": [], "note": "no ROM factory baseline supplied"}
factory_words = dict(factory_default_words_from_rom(rom_bytes))
diffs = []
for offset in range(0, X24164_FACTORY_DEFAULT_BYTES, 2):
expected = factory_words[offset]
address = SHADOW_BASE + offset
high = memory.external.get(address & 0xFFFF, 0xFF)
low = memory.external.get((address + 1) & 0xFFFF, 0xFF)
actual = ((high & 0xFF) << 8) | (low & 0xFF)
if expected == actual:
continue
item = _address_info(offset, selectors_by_offset)
item.update(
{
"address": address,
"address_hex": h16(address),
"expected_word": expected,
"expected_word_hex": f"0x{expected:04X}",
"actual_word": actual,
"actual_word_hex": f"0x{actual:04X}",
}
)
diffs.append(item)
return {"diffs": diffs, "diff_count": len(diffs)}
def _records(image: bytes) -> list[JsonObject]:
records = []
for page in range(X24164_LOGICAL_PAGE_COUNT):
base = page * X24164_LOGICAL_PAGE_SIZE
data = image[base : base + RECORD_BYTES]
records.append(
{
"page": page,
"page_hex": f"0x{page:X}",
"address": base,
"address_hex": f"0x{base:03X}",
"range_hex": f"0x{base:03X}-0x{base + RECORD_BYTES - 1:03X}",
"bytes_hex": data.hex(" ").upper(),
"words_hex": [
f"0x{((data[index] << 8) | data[index + 1]):04X}"
for index in range(0, len(data), 2)
],
"ascii": _ascii(data),
"is_blank_spaces": data == (b" " * RECORD_BYTES),
}
)
return records
def _factory_image(rom_bytes: bytes | None) -> bytes | None:
if rom_bytes is None:
return None
image = bytearray([0xFF] * X24164_LOGICAL_SIZE)
for offset, word in factory_default_words_from_rom(rom_bytes):
for page in range(X24164_LOGICAL_PAGE_COUNT):
address = (page * X24164_LOGICAL_PAGE_SIZE) + offset
image[address] = (word >> 8) & 0xFF
image[address + 1] = word & 0xFF
for page in range(1, X24164_LOGICAL_PAGE_COUNT):
base = page * X24164_LOGICAL_PAGE_SIZE
for offset in range(0, RECORD_BYTES, 2):
image[base + offset] = 0x20
image[base + offset + 1] = 0x20
return bytes(image)
def _selectors_by_offset(rom_bytes: bytes | None) -> dict[int, list[int]]:
if rom_bytes is None:
return {}
result: dict[int, list[int]] = defaultdict(list)
for selector in range(SELECTOR_MAP_COUNT):
address = SELECTOR_MAP_BASE + selector * 2
if address + 1 >= len(rom_bytes):
break
low = rom_bytes[address + 1]
if low:
result[low & 0xFE].append(selector)
return dict(result)
def _address_info(address: int, selectors_by_offset: Mapping[int, list[int]]) -> JsonObject:
address &= 0x0FFF
page = (address // X24164_LOGICAL_PAGE_SIZE) & 0x0F
offset = address & 0xFF
aligned_offset = offset & 0xFE
selectors = selectors_by_offset.get(aligned_offset, [])
return {
"address": address,
"address_hex": f"0x{address:03X}",
"page": page,
"page_hex": f"0x{page:X}",
"offset": offset,
"offset_hex": f"0x{offset:02X}",
"aligned_offset": aligned_offset,
"aligned_offset_hex": f"0x{aligned_offset:02X}",
"record_byte": offset if offset < RECORD_BYTES else None,
"role": "record_header_or_label" if offset < RECORD_BYTES else "factory_shadow_offset",
"mapped_selectors": selectors[:24],
"mapped_selectors_hex": [f"0x{selector:03X}" for selector in selectors[:24]],
}
def _event_suffix(event: Mapping[str, Any]) -> str:
parts = []
if event.get("role"):
parts.append(str(event["role"]))
selectors = event.get("mapped_selectors_hex")
if isinstance(selectors, list) and selectors:
parts.append("selectors=" + ",".join(str(item) for item in selectors[:6]))
return f" ({'; '.join(parts)})" if parts else ""
def _ascii(data: bytes) -> str:
return "".join(chr(value) if 0x20 <= value <= 0x7E else "." for value in data)
__all__ = [
"build_eeprom_snapshot",
"format_eeprom_snapshot",
"write_eeprom_snapshot",
]

View File

@@ -161,6 +161,20 @@ class MemoryMap:
self.p9_bus.x24164_bus.seed_factory_defaults_from_rom(self.rom.data)
self.p9_bus.clear_x24164_trace()
def load_eeprom_image(self, data: bytes | bytearray, *, mirror_shadow: bool = True) -> None:
self.p9_bus.x24164_bus.load_linear(data)
if mirror_shadow:
image = self.p9_bus.x24164_bus.dump_linear()
for offset in range(min(0x0100, len(image))):
self.external[(0xF400 + offset) & 0xFFFF] = image[offset]
self.p9_bus.clear_x24164_trace()
def dump_eeprom_image(self) -> bytes:
return self.p9_bus.x24164_bus.dump_linear()
def clear_eeprom_write_log(self) -> None:
self.p9_bus.x24164_bus.clear_write_log()
def _set_register(self, address: int, value: int) -> None:
self.registers[address - REGISTER_FIELD_START] = value & 0xFF

View File

@@ -2,7 +2,7 @@ from __future__ import annotations
from .lcd import LCD, LCD_E_CLOCK_DATA, LCD_E_CLOCK_STATUS, LCD_LINE_WIDTH
from .p9_bus import P9_ACK_BIT, P9_STROBE_BIT, P9Bus, P9StrobeEvent, P9TraceEvent
from .x24164 import X24164Bus, X24164Device, X24164TraceEvent, factory_default_words_from_rom
from .x24164 import X24164Bus, X24164Device, X24164TraceEvent, X24164WriteEvent, factory_default_words_from_rom
__all__ = [
"LCD_E_CLOCK_DATA",
@@ -17,5 +17,6 @@ __all__ = [
"X24164Bus",
"X24164Device",
"X24164TraceEvent",
"X24164WriteEvent",
"factory_default_words_from_rom",
]

View File

@@ -4,6 +4,7 @@ from dataclasses import dataclass, field
X24164_SIZE = 2048
X24164_LOGICAL_SIZE = 4096
X24164_FACTORY_DEFAULT_BASE = 0xC964
X24164_FACTORY_DEFAULT_BYTES = 0x0100
X24164_LOGICAL_PAGE_SIZE = 0x0100
@@ -72,12 +73,30 @@ class X24164TraceEvent:
return " ".join(parts)
@dataclass(frozen=True)
class X24164WriteEvent:
logical_address: int
device: str
device_offset: int
old_value: int
new_value: int
source: str
def line(self) -> str:
return (
f"addr={self.logical_address & 0x0FFF:03X} device={self.device} "
f"offset={self.device_offset & (X24164_SIZE - 1):03X} "
f"{self.old_value & 0xFF:02X}->{self.new_value & 0xFF:02X} source={self.source}"
)
class X24164Bus:
"""Bit-level two-wire bus model for X24164 EEPROMs."""
def __init__(self, devices: list[X24164Device] | None = None) -> None:
self.devices = devices if devices is not None else default_x24164_devices()
self.trace_events: list[X24164TraceEvent] = []
self.write_events: list[X24164WriteEvent] = []
self.active = False
self.phase = "idle"
self.selected: X24164Device | None = None
@@ -197,6 +216,18 @@ class X24164Bus:
)
return True, value
def read_linear_byte(self, address: int) -> tuple[bool, int]:
device = self._device_for_linear_address(address)
if device is None:
self.trace_events.append(
X24164TraceEvent("x24164_linear_read_miss", address=address & 0x0FFF, message="no_mapped_device")
)
return False, 0xFF
offset = address & (X24164_SIZE - 1)
value = device.read(offset)
self.trace_events.append(X24164TraceEvent("x24164_linear_read_byte", device.name, value=value, address=offset))
return True, value
def write_linear_word(self, address: int, value: int) -> bool:
device = self._device_for_linear_address(address)
if device is None:
@@ -205,8 +236,8 @@ class X24164Bus:
)
return False
offset = address & (X24164_SIZE - 1)
device.write(offset, (value >> 8) & 0xFF)
device.write((offset + 1) & (X24164_SIZE - 1), value & 0xFF)
self._write_device_byte(device, offset, (value >> 8) & 0xFF, source="linear_word")
self._write_device_byte(device, (offset + 1) & (X24164_SIZE - 1), value & 0xFF, source="linear_word")
self.trace_events.append(
X24164TraceEvent(
"x24164_linear_write_word",
@@ -218,20 +249,61 @@ class X24164Bus:
)
return True
def write_linear_byte(self, address: int, value: int, *, source: str = "linear_byte") -> bool:
device = self._device_for_linear_address(address)
if device is None:
self.trace_events.append(
X24164TraceEvent("x24164_linear_write_miss", address=address & 0x0FFF, message="no_mapped_device")
)
return False
offset = address & (X24164_SIZE - 1)
self._write_device_byte(device, offset, value, source=source)
self.trace_events.append(X24164TraceEvent("x24164_linear_write_byte", device.name, value=value & 0xFF, address=offset))
return True
def dump_linear(self) -> bytes:
data = bytearray()
for address in range(X24164_LOGICAL_SIZE):
device = self._device_for_linear_address(address)
if device is None:
data.append(0xFF)
else:
data.append(device.read(address & (X24164_SIZE - 1)))
return bytes(data)
def load_linear(self, data: bytes | bytearray, *, fill: int = 0xFF) -> None:
if len(data) > X24164_LOGICAL_SIZE:
raise ValueError(f"EEPROM image is too large: {len(data)} > {X24164_LOGICAL_SIZE}")
padded = bytearray([fill & 0xFF] * X24164_LOGICAL_SIZE)
padded[: len(data)] = data
for address, value in enumerate(padded):
device = self._device_for_linear_address(address)
if device is not None:
device.write(address & (X24164_SIZE - 1), value)
self.clear_write_log()
def clear_write_log(self) -> None:
self.write_events.clear()
def seed_factory_defaults_from_rom(self, rom_bytes: bytes) -> None:
for offset, word in factory_default_words_from_rom(rom_bytes):
for page in range(X24164_LOGICAL_PAGE_COUNT):
self.write_linear_word((page * X24164_LOGICAL_PAGE_SIZE) + offset, word)
for page in range(X24164_LOGICAL_PAGE_COUNT):
for page in range(1, X24164_LOGICAL_PAGE_COUNT):
base = page * X24164_LOGICAL_PAGE_SIZE
for offset in range(0, 8, 2):
self.write_linear_word(base + offset, 0x2020)
self.clear_write_log()
def trace_lines(self, limit: int | None = None) -> list[str]:
events = self.trace_events if limit is None else self.trace_events[-limit:]
return [event.line() for event in events]
def write_log_lines(self, limit: int | None = None) -> list[str]:
events = self.write_events if limit is None else self.write_events[-limit:]
return [event.line() for event in events]
def _scl_rising(self, master_sda: bool, master_sda_output: bool) -> None:
if not self.active:
return
@@ -327,7 +399,7 @@ class X24164Bus:
self._ack_armed_on_current_clock = True
self.phase = "ignore"
return
self.selected.write(self.address, value)
self._write_device_byte(self.selected, self.address, value, source="bit_banged")
self.trace_events.append(
X24164TraceEvent("x24164_write_data", self.selected.name, value=value, address=self.address, ack=True)
)
@@ -365,6 +437,24 @@ class X24164Bus:
return device
return None
def _linear_base_for_device(self, device: X24164Device) -> int:
return 0x0800 if device.control_base == 0xE0 else 0x0000
def _write_device_byte(self, device: X24164Device, offset: int, value: int, *, source: str) -> None:
offset &= X24164_SIZE - 1
old_value = device.read(offset)
device.write(offset, value)
self.write_events.append(
X24164WriteEvent(
logical_address=(self._linear_base_for_device(device) + offset) & 0x0FFF,
device=device.name,
device_offset=offset,
old_value=old_value,
new_value=value & 0xFF,
source=source,
)
)
def _selected_name(self) -> str | None:
return self.selected.name if self.selected is not None else None

View File

@@ -15,6 +15,7 @@ from .constants import (
SCI_SSR_RDRF,
VECTOR_SCI1_RXI,
)
from .eeprom_image import write_eeprom_snapshot
from .errors import UnsupportedInstruction
from .memory import MemoryAccess
from .runner import H8536Emulator
@@ -216,6 +217,7 @@ def run_rx_probe(
p9_fast_optimistic_wrapper: bool = False,
p7_input: int = 0xFF,
eeprom_seed: str = "blank",
eeprom_image: bytes | None = None,
stop_after_tx_frame: bool = True,
) -> tuple[Path, H8536Emulator, str, list[FrameResult]]:
rom_bytes, discovered_rom_path = load_rom(rom_path)
@@ -231,6 +233,8 @@ def run_rx_probe(
p7_input=p7_input,
eeprom_seed=eeprom_seed,
)
if eeprom_image is not None:
emulator.memory.load_eeprom_image(eeprom_image)
boot_context = RunContext()
boot_steps_used, boot_reason = _run_until(emulator, boot_steps, _rx_ready, boot_context)
@@ -277,6 +281,11 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="legacy fallback for older wrapper experiments; known BFE0/BFFE wrappers use the X24164 model")
parser.add_argument("--p7-input", type=parse_int, default=0xFF, help="external P7 pin state for input bits; DIP-off board default is 0xFF")
parser.add_argument("--eeprom-seed", choices=("blank", "factory"), default="blank", help="initial X24164/shadow state before reset")
parser.add_argument("--eeprom-load", type=Path, help="load a 0x1000-byte logical EEPROM image before booting the ROM")
parser.add_argument("--eeprom-save", type=Path, help="save the final 0x1000-byte logical EEPROM image after probing")
parser.add_argument("--eeprom-report", type=Path, help="write a readable EEPROM snapshot report after probing")
parser.add_argument("--eeprom-report-json", type=Path, help="write a structured EEPROM snapshot report after probing")
parser.add_argument("--eeprom-report-include-image", action="store_true", help="include the full EEPROM image as hex in JSON reports")
return parser
@@ -305,16 +314,35 @@ def main(argv: list[str] | None = None) -> int:
p9_fast_optimistic_wrapper=args.p9_fast_optimistic_wrapper,
p7_input=args.p7_input,
eeprom_seed=args.eeprom_seed,
eeprom_image=args.eeprom_load.read_bytes() if args.eeprom_load else None,
stop_after_tx_frame=not args.keep_listening,
)
print(f"rom={rom_path}")
if args.eeprom_load:
print(f"eeprom_loaded={args.eeprom_load}")
print(f"reset_vector={h16(emulator.reset_vector())}")
print(boot_summary)
for index, result in enumerate(results):
for line in result.lines(index):
print(line)
print("total_tx_frames=" + " | ".join(format_frame(frame) for frame in emulator.sci1.tx_frames))
if args.eeprom_save:
args.eeprom_save.parent.mkdir(parents=True, exist_ok=True)
args.eeprom_save.write_bytes(emulator.memory.dump_eeprom_image())
print(f"eeprom_saved={args.eeprom_save}")
if args.eeprom_report:
write_eeprom_snapshot(emulator.memory, args.eeprom_report, rom_bytes=emulator.memory.rom.data)
print(f"eeprom_report={args.eeprom_report}")
if args.eeprom_report_json:
write_eeprom_snapshot(
emulator.memory,
args.eeprom_report_json,
rom_bytes=emulator.memory.rom.data,
as_json=True,
include_image_hex=args.eeprom_report_include_image,
)
print(f"eeprom_report_json={args.eeprom_report_json}")
return 0