1
0

non-volatile storage emulation

This commit is contained in:
Aiden
2026-05-25 23:16:41 +10:00
parent 0c241877eb
commit 0819701b22
12 changed files with 647 additions and 36 deletions

View File

@@ -55,7 +55,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 .peripherals import LCD, P9TraceEvent
from .peripherals import LCD, P9TraceEvent, X24164Bus, X24164Device, X24164TraceEvent
from .runner import H8536Emulator, RunReport
from .sci import SCI1, SciTxEvent
from .uart import UartTiming
@@ -124,6 +124,9 @@ __all__ = [
"VECTOR_SCI1_RXI",
"VECTOR_SCI1_TXI",
"WDT_TCSR_R",
"X24164Bus",
"X24164Device",
"X24164TraceEvent",
"build_arg_parser",
"describe_regions",
"discover_rom_path",

View File

@@ -368,7 +368,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--frt2-ocia-steps", type=int, default=ReplayConfig.frt2_ocia_steps)
parser.add_argument("--no-p9-fast-path", action="store_true", help="disable shortcut handling for known P9 routines")
parser.add_argument("--p9-fast-input", type=lambda text: int(text, 0), default=ReplayConfig.p9_fast_input)
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="make P9 fast-path wrapper calls succeed when no modeled P9 response is queued")
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("--assert-bench-parity", action="store_true", help="exit nonzero if emulator behavior diverges from the bench log")
parser.add_argument("--json", action="store_true", help="emit JSON")
return parser

View File

@@ -44,7 +44,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--frt2-ocia-steps", type=int, default=None, help="legacy step-period override for FRT2 OCIA")
parser.add_argument("--p9-fast-path", action="store_true", help="shortcut known P9 bit-banged transfer routines for exploration")
parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF, help="default byte returned by the P9 fast-path read routine")
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="make P9 fast-path wrapper calls succeed when no modeled P9 response is queued")
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")
return parser

View File

@@ -10,19 +10,22 @@ LOC_BFE0_TRANSFER_WRAPPER = 0xBFE0
LOC_BFFE_TRANSFER_WRAPPER = 0xBFFE
LOC_C08B_P9_WRITE_BYTE = 0xC08B
LOC_C0DB_P9_READ_BYTE = 0xC0DB
LOC_C10C_P9_MASTER_ACK = 0xC10C
LOC_C10C_P9_MARKER = 0xC10C
LOC_C121_P9_MARKER = 0xC121
LOC_C142_P9_MARKER = 0xC142
LOC_C121_P9_START = 0xC121
LOC_C121_P9_MARKER = LOC_C121_P9_START
LOC_C142_P9_STOP = 0xC142
LOC_C142_P9_MARKER = LOC_C142_P9_STOP
@dataclass(frozen=True)
class P9FastPathConfig:
"""Configuration for optional ROM P9 transfer shortcuts.
The helper assumes the CPU PC is exactly at a known routine entry. It
models the routine as if it completed successfully and returned via RTS.
Integration should keep this disabled unless the runner intentionally opts
into skipping these ROM routines.
The helper assumes the CPU PC is exactly at a known routine entry. It feeds
those byte/marker/wrapper operations into the modeled P9/X24164 bus and
returns via RTS. Integration should keep this disabled unless the runner
intentionally opts into skipping these ROM routines.
"""
enabled: bool = False
@@ -100,8 +103,7 @@ class P9FastPath:
elif pc == (self.config.read_byte_pc & 0xFFFF):
self._handle_read_byte(emulator)
elif pc in self.config.marker_pcs:
self.events.append(P9FastPathEvent("marker", pc))
self._return_from_subroutine(emulator)
self._handle_marker(emulator, pc)
elif pc in self.config.wrapper_pcs:
self._handle_wrapper(emulator)
else:
@@ -116,10 +118,12 @@ class P9FastPath:
pc = emulator.cpu.pc & 0xFFFF
value = emulator.cpu.regs[0] & 0xFF
self.output_bytes.append(value)
self.events.append(P9FastPathEvent("write_byte", pc, value))
success = emulator.memory.p9_bus.fast_write_byte(value)
self.events.append(P9FastPathEvent("write_byte", pc, value, source="x24164", success=success))
emulator.cpu.regs[0] = 1
self._set_logic_flags(emulator.cpu, 1, 1)
result = 1 if success else 0
emulator.cpu.regs[0] = result
self._set_logic_flags(emulator.cpu, result, 1)
self._return_from_subroutine(emulator)
def _handle_read_byte(self, emulator: Any) -> None:
@@ -127,6 +131,9 @@ class P9FastPath:
if self.input_bytes:
value = self.input_bytes.pop(0)
source = self.input_sources.pop(0) if self.input_sources else "queued"
elif (x24164_value := emulator.memory.p9_bus.fast_read_byte()) is not None:
value = x24164_value
source = "x24164"
else:
value = self.config.default_input_byte
source = "default_input_byte"
@@ -141,7 +148,27 @@ class P9FastPath:
def _handle_wrapper(self, emulator: Any) -> None:
pc = emulator.cpu.pc & 0xFFFF
success, source, queue_depth = emulator.memory.p9_bus.consume_wrapper_result()
success: bool
source: str
queue_depth: int | None
if pc == (LOC_BFE0_TRANSFER_WRAPPER & 0xFFFF):
address = emulator.cpu.regs[4] & 0x0FFF
value = emulator.cpu.regs[5] & 0xFFFF
write_success = emulator.memory.p9_bus.fast_write_word(address, value)
read_success, readback = emulator.memory.p9_bus.fast_read_word(address)
success = write_success and read_success and readback == value
source = "x24164_write_verify"
queue_depth = None
elif pc == (LOC_BFFE_TRANSFER_WRAPPER & 0xFFFF):
address = emulator.cpu.regs[4] & 0x0FFF
read_success, value = emulator.memory.p9_bus.fast_read_word(address)
if read_success:
emulator.cpu.regs[5] = value & 0xFFFF
success = read_success
source = "x24164_read_word"
queue_depth = None
else:
success, source, queue_depth = emulator.memory.p9_bus.consume_wrapper_result()
value = 1 if success else 0
self.events.append(
P9FastPathEvent(
@@ -163,6 +190,20 @@ class P9FastPath:
emulator.cpu.pc = emulator.memory.read16(sp) & 0xFFFF
emulator.cpu.regs[7] = (sp + 2) & 0xFFFF
def _handle_marker(self, emulator: Any, pc: int) -> None:
if pc == (LOC_C121_P9_START & 0xFFFF):
emulator.memory.p9_bus.fast_start()
self.events.append(P9FastPathEvent("start", pc, source="x24164"))
elif pc == (LOC_C10C_P9_MASTER_ACK & 0xFFFF):
emulator.memory.p9_bus.fast_master_ack(True)
self.events.append(P9FastPathEvent("master_ack", pc, source="x24164"))
elif pc == (LOC_C142_P9_STOP & 0xFFFF):
emulator.memory.p9_bus.fast_stop()
self.events.append(P9FastPathEvent("stop", pc, source="x24164"))
else:
self.events.append(P9FastPathEvent("marker", pc))
self._return_from_subroutine(emulator)
def _set_logic_flags(self, cpu: Any, value: int, size: int) -> None:
value &= mask(size)
cpu.z = value == 0

View File

@@ -2,6 +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
__all__ = [
"LCD_E_CLOCK_DATA",
@@ -13,4 +14,7 @@ __all__ = [
"P9Bus",
"P9StrobeEvent",
"P9TraceEvent",
"X24164Bus",
"X24164Device",
"X24164TraceEvent",
]

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
from dataclasses import dataclass
from typing import Iterable
from .x24164 import X24164Bus
P9_ACK_BIT = 0x80
P9_STROBE_BIT = 0x02
@@ -27,6 +29,7 @@ class P9TraceEvent:
source: str | None = None
success: bool | None = None
queue_depth: int | None = None
message: str | None = None
def line(self) -> str:
parts = [self.kind, f"ddr={self.ddr:02X}", f"dr={self.dr:02X}"]
@@ -40,11 +43,18 @@ class P9TraceEvent:
parts.append(f"source={self.source}")
if self.queue_depth is not None:
parts.append(f"queued={self.queue_depth}")
if self.message is not None:
parts.append(self.message)
return " ".join(parts)
class P9Bus:
"""Small model for the ROM's P9 bit-banged serial handshake."""
"""Small model for the ROM's P9 bit-banged serial handshake.
Board tracing ties P91/P97 to X24164 SCL/SDA. The legacy bit queue is
retained for tests and exploratory scripts, while the X24164 model now
drives SDA during recognized EEPROM transactions.
"""
def __init__(
self,
@@ -52,6 +62,7 @@ class P9Bus:
dr: int = 0x00,
input_bits: Iterable[int] = (),
wrapper_results: Iterable[bool] = (),
x24164_bus: X24164Bus | None = None,
) -> None:
self.ddr = ddr & 0xFF
self.dr_latch = dr & 0xFF
@@ -64,6 +75,8 @@ class P9Bus:
self.trace_events: list[P9TraceEvent] = []
self.transmitted_bits: list[int] = []
self.byte_candidates: list[int] = []
self.x24164_bus = x24164_bus if x24164_bus is not None else X24164Bus()
self._x24164_trace_index = 0
def write_ddr(self, value: int) -> int:
self.ddr = value & 0xFF
@@ -72,6 +85,7 @@ class P9Bus:
def write_dr(self, value: int) -> int:
previous = self.dr_latch
previous_ddr = self.ddr
self.dr_latch = value & 0xFF
self.trace_events.append(P9TraceEvent("write_dr", self.ddr, self.dr_latch, value=self.dr_latch))
@@ -86,6 +100,14 @@ class P9Bus:
if edge == "rising" and bit7_output:
self._record_transmitted_bit(data_bit)
self.x24164_bus.observe(
previous_scl=bool(previous & P9_STROBE_BIT),
previous_master_sda=bool(previous & P9_ACK_BIT) if previous_ddr & P9_ACK_BIT else True,
current_scl=bool(self.dr_latch & P9_STROBE_BIT),
current_master_sda=bool(self.dr_latch & P9_ACK_BIT) if self.ddr & P9_ACK_BIT else True,
master_sda_output=bool(self.ddr & P9_ACK_BIT),
)
self._append_x24164_trace()
return self.dr_latch
def read_ddr(self) -> int:
@@ -96,9 +118,13 @@ class P9Bus:
input_bit = None
source = None
if not (self.ddr & P9_ACK_BIT):
x24164_bit = self.x24164_bus.sda_bit()
if self.input_bits:
input_bit = self.input_bits.pop(0)
source = "queued_bit"
elif x24164_bit is not None:
input_bit = x24164_bit
source = "x24164"
else:
input_bit = self.default_input_bit
source = "default_bit"
@@ -145,6 +171,72 @@ class P9Bus:
events = self.trace_events if limit is None else self.trace_events[-limit:]
return [event.line() for event in events]
def fast_start(self) -> None:
self.x24164_bus.fast_start()
self._append_x24164_trace()
def fast_stop(self) -> None:
self.x24164_bus.fast_stop()
self._append_x24164_trace()
def fast_master_ack(self, ack: bool = True) -> None:
self.x24164_bus.fast_master_ack(ack)
self._append_x24164_trace()
def fast_write_byte(self, value: int) -> bool:
success = self.x24164_bus.fast_write_byte(value)
self.trace_events.append(
P9TraceEvent(
"fast_write_byte",
self.ddr,
self.dr_latch,
value=value & 0xFF,
success=success,
source="x24164",
)
)
self._append_x24164_trace()
return success
def fast_read_byte(self) -> int | None:
value = self.x24164_bus.fast_read_byte()
if value is not None:
self.trace_events.append(P9TraceEvent("fast_read_byte", self.ddr, self.dr_latch, value=value, source="x24164"))
self._append_x24164_trace()
return value
def fast_read_word(self, address: int) -> tuple[bool, int]:
success, value = self.x24164_bus.read_linear_word(address)
self.trace_events.append(
P9TraceEvent(
"fast_read_word",
self.ddr,
self.dr_latch,
value=(value >> 8) & 0xFF,
success=success,
source="x24164",
message=f"addr={address & 0x0FFF:03X} word={value & 0xFFFF:04X}",
)
)
self._append_x24164_trace()
return success, value
def fast_write_word(self, address: int, value: int) -> bool:
success = self.x24164_bus.write_linear_word(address, value)
self.trace_events.append(
P9TraceEvent(
"fast_write_word",
self.ddr,
self.dr_latch,
value=(value >> 8) & 0xFF,
success=success,
source="x24164",
message=f"addr={address & 0x0FFF:03X} word={value & 0xFFFF:04X}",
)
)
self._append_x24164_trace()
return success
def _record_transmitted_bit(self, bit: int) -> None:
self.transmitted_bits.append(bit)
self.trace_events.append(P9TraceEvent("tx_bit", self.ddr, self.dr_latch, bit=bit))
@@ -154,3 +246,9 @@ class P9Bus:
byte = (byte << 1) | data_bit
self.byte_candidates.append(byte)
self.trace_events.append(P9TraceEvent("tx_byte", self.ddr, self.dr_latch, value=byte))
def _append_x24164_trace(self) -> None:
new_events = self.x24164_bus.trace_events[self._x24164_trace_index :]
self._x24164_trace_index = len(self.x24164_bus.trace_events)
for event in new_events:
self.trace_events.append(P9TraceEvent("x24164", self.ddr, self.dr_latch, message=event.line()))

View File

@@ -0,0 +1,362 @@
from __future__ import annotations
from dataclasses import dataclass, field
X24164_SIZE = 2048
@dataclass
class X24164Device:
"""Small Xicor X24164 serial EEPROM model.
The ROM uses two control-byte families on the P91/P97 two-wire bus:
H'A0/H'A1 for the low logical half and H'E0/H'E1 for the high logical half.
X24164 has unusual device-select encoding compared with later 24Cxx parts,
so the emulator stores the accepted high-nibble control family directly.
"""
name: str
control_base: int
data: bytearray = field(default_factory=lambda: bytearray([0xFF] * X24164_SIZE))
def __post_init__(self) -> None:
self.control_base &= 0xF0
if len(self.data) < X24164_SIZE:
self.data.extend([0xFF] * (X24164_SIZE - len(self.data)))
elif len(self.data) > X24164_SIZE:
del self.data[X24164_SIZE:]
def matches_control(self, value: int) -> bool:
return (value & 0xF0) == self.control_base
def offset_from_control(self, value: int, word_address: int) -> int:
high_address = (value >> 1) & 0x07
return ((high_address << 8) | (word_address & 0xFF)) & (X24164_SIZE - 1)
def read(self, offset: int) -> int:
return self.data[offset & (X24164_SIZE - 1)]
def write(self, offset: int, value: int) -> None:
self.data[offset & (X24164_SIZE - 1)] = value & 0xFF
@dataclass(frozen=True)
class X24164TraceEvent:
kind: str
device: str | None = None
value: int | None = None
address: int | None = None
bit: int | None = None
ack: bool | None = None
message: str | None = None
def line(self) -> str:
parts = [self.kind]
if self.device is not None:
parts.append(f"device={self.device}")
if self.address is not None:
parts.append(f"addr={self.address:03X}")
if self.value is not None:
parts.append(f"value={self.value:02X}")
if self.bit is not None:
parts.append(f"bit={self.bit}")
if self.ack is not None:
parts.append(f"ack={int(self.ack)}")
if self.message is not None:
parts.append(self.message)
return " ".join(parts)
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.active = False
self.phase = "idle"
self.selected: X24164Device | None = None
self.control_byte = 0
self.address = 0
self._rx_bits: list[int] = []
self._ack_pending: bool | None = None
self._ack_armed_on_current_clock = False
self._read_byte = 0xFF
self._read_bit_index = 0
self._read_prepared_on_current_clock = False
self._awaiting_master_ack = False
def observe(
self,
*,
previous_scl: bool,
previous_master_sda: bool,
current_scl: bool,
current_master_sda: bool,
master_sda_output: bool,
) -> None:
if previous_scl and current_scl and previous_master_sda != current_master_sda:
if previous_master_sda and not current_master_sda:
self.start()
elif not previous_master_sda and current_master_sda:
self.stop()
if not previous_scl and current_scl:
self._scl_rising(current_master_sda, master_sda_output)
elif previous_scl and not current_scl:
self._scl_falling()
def start(self) -> None:
self.active = True
self.phase = "control"
self.selected = None
self._rx_bits.clear()
self._ack_pending = None
self._ack_armed_on_current_clock = False
self._read_prepared_on_current_clock = False
self._awaiting_master_ack = False
self.trace_events.append(X24164TraceEvent("x24164_start"))
def stop(self) -> None:
if self.active:
self.trace_events.append(X24164TraceEvent("x24164_stop", device=self._selected_name()))
self.active = False
self.phase = "idle"
self.selected = None
self._rx_bits.clear()
self._ack_pending = None
self._ack_armed_on_current_clock = False
self._read_prepared_on_current_clock = False
self._awaiting_master_ack = False
def sda_bit(self) -> int | None:
if not self.active:
return None
if self._ack_pending is not None:
return 0 if self._ack_pending else 1
if self.phase == "read_data" and not self._awaiting_master_ack:
return (self._read_byte >> (7 - self._read_bit_index)) & 1
return 1
def fast_start(self) -> None:
self.start()
def fast_stop(self) -> None:
self.stop()
def fast_write_byte(self, value: int) -> bool:
self._accept_byte(value & 0xFF)
ack = bool(self._ack_pending)
self.trace_events.append(
X24164TraceEvent("x24164_fast_write_byte", self._selected_name(), value=value & 0xFF, ack=ack)
)
self._ack_pending = None
self._ack_armed_on_current_clock = False
if self.phase == "read_data" and self.selected is not None:
self._prepare_read_byte()
return ack
def fast_read_byte(self) -> int | None:
if not self.active or self.phase != "read_data" or self.selected is None:
return None
value = self.selected.read(self.address)
self.trace_events.append(
X24164TraceEvent("x24164_fast_read_byte", self.selected.name, value=value, address=self.address)
)
self.address = (self.address + 1) & (X24164_SIZE - 1)
self._prepare_read_byte()
return value
def fast_master_ack(self, ack: bool = True) -> None:
if not self.active:
return
self.trace_events.append(X24164TraceEvent("x24164_fast_master_ack", self._selected_name(), ack=ack))
if ack and self.phase == "read_data" and self.selected is not None:
self._prepare_read_byte()
elif not ack:
self.phase = "idle"
def read_linear_word(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, 0xFFFF
offset = address & (X24164_SIZE - 1)
high = device.read(offset)
low = device.read((offset + 1) & (X24164_SIZE - 1))
value = (high << 8) | low
self.trace_events.append(
X24164TraceEvent("x24164_linear_read_word", device.name, value=high, address=offset, message=f"word={value:04X}")
)
return True, value
def write_linear_word(self, address: int, value: int) -> 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)
device.write(offset, (value >> 8) & 0xFF)
device.write((offset + 1) & (X24164_SIZE - 1), value & 0xFF)
self.trace_events.append(
X24164TraceEvent(
"x24164_linear_write_word",
device.name,
value=(value >> 8) & 0xFF,
address=offset,
message=f"word={value & 0xFFFF:04X}",
)
)
return True
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 _scl_rising(self, master_sda: bool, master_sda_output: bool) -> None:
if not self.active:
return
if self._ack_pending is not None:
self.trace_events.append(X24164TraceEvent("x24164_ack_bit", self._selected_name(), ack=self._ack_pending))
return
if self.phase == "read_data":
if self._awaiting_master_ack:
ack = not master_sda if master_sda_output else False
self.trace_events.append(X24164TraceEvent("x24164_master_ack", self._selected_name(), ack=ack))
self._awaiting_master_ack = False
if ack and self.selected is not None:
self._prepare_read_byte(skip_current_falling=True)
else:
self.phase = "idle"
return
if master_sda_output:
self._rx_bits.append(1 if master_sda else 0)
self.trace_events.append(X24164TraceEvent("x24164_rx_bit", self._selected_name(), bit=self._rx_bits[-1]))
if len(self._rx_bits) == 8:
value = 0
for bit in self._rx_bits:
value = (value << 1) | bit
self._rx_bits.clear()
self._accept_byte(value)
def _scl_falling(self) -> None:
if not self.active:
return
if self._ack_pending is not None:
if self._ack_armed_on_current_clock:
self._ack_armed_on_current_clock = False
return
self._ack_pending = None
if self.phase == "read_data" and self.selected is not None:
self._prepare_read_byte()
return
if self.phase == "read_data" and not self._awaiting_master_ack:
if self._read_prepared_on_current_clock:
self._read_prepared_on_current_clock = False
return
if self._read_bit_index < 7:
self._read_bit_index += 1
else:
self.trace_events.append(
X24164TraceEvent("x24164_tx_byte_done", self._selected_name(), value=self._read_byte)
)
if self.selected is not None:
self.address = (self.address + 1) & (X24164_SIZE - 1)
self._awaiting_master_ack = True
def _accept_byte(self, value: int) -> None:
value &= 0xFF
if self.phase == "control":
self.control_byte = value
self.selected = self._device_for_control(value)
read_mode = bool(value & 1)
self._ack_pending = self.selected is not None
self._ack_armed_on_current_clock = True
self.trace_events.append(
X24164TraceEvent(
"x24164_control",
self._selected_name(),
value=value,
ack=self._ack_pending,
message="read" if read_mode else "write",
)
)
if self.selected is None:
self.phase = "ignore"
elif read_mode:
self.phase = "read_data"
else:
self.phase = "word_address"
return
if self.phase == "word_address":
if self.selected is None:
self._ack_pending = False
self._ack_armed_on_current_clock = True
self.phase = "ignore"
return
self.address = self.selected.offset_from_control(self.control_byte, value)
self._ack_pending = True
self._ack_armed_on_current_clock = True
self.phase = "write_data"
self.trace_events.append(
X24164TraceEvent("x24164_word_address", self.selected.name, value=value, address=self.address, ack=True)
)
return
if self.phase == "write_data":
if self.selected is None:
self._ack_pending = False
self._ack_armed_on_current_clock = True
self.phase = "ignore"
return
self.selected.write(self.address, value)
self.trace_events.append(
X24164TraceEvent("x24164_write_data", self.selected.name, value=value, address=self.address, ack=True)
)
self.address = (self.address + 1) & (X24164_SIZE - 1)
self._ack_pending = True
self._ack_armed_on_current_clock = True
return
self._ack_pending = False
self._ack_armed_on_current_clock = True
self.trace_events.append(X24164TraceEvent("x24164_ignored_byte", self._selected_name(), value=value, ack=False))
def _prepare_read_byte(self, *, skip_current_falling: bool = False) -> None:
if self.selected is None:
self._read_byte = 0xFF
return
self._read_byte = self.selected.read(self.address)
self._read_bit_index = 0
self._read_prepared_on_current_clock = skip_current_falling
self._awaiting_master_ack = False
self.trace_events.append(
X24164TraceEvent("x24164_prepare_read", self.selected.name, value=self._read_byte, address=self.address)
)
def _device_for_control(self, value: int) -> X24164Device | None:
for device in self.devices:
if device.matches_control(value):
return device
return None
def _device_for_linear_address(self, address: int) -> X24164Device | None:
bank = (address >> 11) & 1
wanted_base = 0xA0 if bank == 0 else 0xE0
for device in self.devices:
if device.control_base == wanted_base:
return device
return None
def _selected_name(self) -> str | None:
return self.selected.name if self.selected is not None else None
def default_x24164_devices() -> list[X24164Device]:
return [
X24164Device("x24164_a0_lower_2k", 0xA0),
X24164Device("x24164_e0_upper_2k", 0xE0),
]

View File

@@ -1170,7 +1170,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--stop-on-tx", action="store_true", help="stop when SCI1 TDR emits the first byte")
parser.add_argument("--p9-fast-path", action="store_true", help="shortcut known P9 bit-banged transfer routines for exploration")
parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF)
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="make P9 fast-path wrapper calls succeed when no modeled P9 response is queued")
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("--p9-log-limit", type=int, default=80)
parser.add_argument("--sci-log-limit", type=int, default=32)
parser.add_argument("--hot-limit", type=int, default=12)

View File

@@ -270,7 +270,7 @@ def build_arg_parser() -> argparse.ArgumentParser:
parser.add_argument("--frt2-ocia-steps", type=int, default=None, help="legacy step-period override for FRT2 OCIA")
parser.add_argument("--no-p9-fast-path", action="store_true", help="disable shortcut handling for known P9 routines")
parser.add_argument("--p9-fast-input", type=parse_int, default=0xFF, help="default byte returned by the P9 fast-path read routine")
parser.add_argument("--p9-fast-optimistic-wrapper", action="store_true", help="make P9 fast-path wrapper calls succeed when no modeled P9 response is queued")
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")
return parser