from __future__ import annotations from dataclasses import dataclass from typing import Iterable P9_ACK_BIT = 0x80 P9_STROBE_BIT = 0x02 @dataclass(frozen=True) class P9StrobeEvent: edge: str ddr: int dr: int data_bit: int bit7_output: bool class P9Bus: """Small model for the ROM's P9 bit-banged serial handshake.""" def __init__(self, ddr: int = 0x00, dr: int = 0x00, input_bits: Iterable[int] = ()) -> None: self.ddr = ddr & 0xFF self.dr_latch = dr & 0xFF self.input_bits: list[int] = [1 if bit else 0 for bit in input_bits] self.default_input_bit = 0 self.strobe_edges: list[P9StrobeEvent] = [] self.transmitted_bits: list[int] = [] self.byte_candidates: list[int] = [] def write_ddr(self, value: int) -> int: self.ddr = value & 0xFF return self.ddr def write_dr(self, value: int) -> int: previous = self.dr_latch self.dr_latch = value & 0xFF previous_strobe = bool(previous & P9_STROBE_BIT) current_strobe = bool(self.dr_latch & P9_STROBE_BIT) if previous_strobe != current_strobe: edge = "rising" if current_strobe else "falling" data_bit = 1 if self.dr_latch & P9_ACK_BIT else 0 bit7_output = bool(self.ddr & P9_ACK_BIT) self.strobe_edges.append(P9StrobeEvent(edge, self.ddr, self.dr_latch, data_bit, bit7_output)) if edge == "rising" and bit7_output: self._record_transmitted_bit(data_bit) return self.dr_latch def read_ddr(self) -> int: return self.ddr def read_dr(self) -> int: value = self.dr_latch if not (self.ddr & P9_ACK_BIT): if self.input_bits: input_bit = self.input_bits.pop(0) else: input_bit = self.default_input_bit if input_bit: value |= P9_ACK_BIT else: value &= ~P9_ACK_BIT return value & 0xFF def queue_input_bits(self, bits: Iterable[int]) -> None: self.input_bits.extend(1 if bit else 0 for bit in bits) def set_default_input_bit(self, bit: int) -> None: self.default_input_bit = 1 if bit else 0 def _record_transmitted_bit(self, bit: int) -> None: self.transmitted_bits.append(bit) if len(self.transmitted_bits) % 8 == 0: byte = 0 for data_bit in self.transmitted_bits[-8:]: byte = (byte << 1) | data_bit self.byte_candidates.append(byte)