emulator improvements
This commit is contained in:
110
h8536/emulator/sci.py
Normal file
110
h8536/emulator/sci.py
Normal file
@@ -0,0 +1,110 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .constants import (
|
||||
HEARTBEAT_FRAME,
|
||||
SCI1_BRR,
|
||||
SCI1_RDR,
|
||||
SCI1_SCR,
|
||||
SCI1_SMR,
|
||||
SCI1_SSR,
|
||||
SCI1_TDR,
|
||||
SCI_SCR_TE,
|
||||
SCI_SSR_RDRF,
|
||||
SCI_SSR_TDRE,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SciTxEvent:
|
||||
address: int
|
||||
value: int
|
||||
scr: int
|
||||
ssr: int
|
||||
emitted: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
class SCI1:
|
||||
"""Small SCI1 model for the H8/536 serial path.
|
||||
|
||||
Manual anchors:
|
||||
- RDR/TDR/SCR/SSR live at H'FEDD/H'FEDB/H'FEDA/H'FEDC for SCI1.
|
||||
- SCR bit 7 TIE, bit 6 RIE, bit 5 TE, bit 4 RE.
|
||||
- SSR bit 7 TDRE, bit 6 RDRF, bits 5..3 ORER/FER/PER.
|
||||
- Software normally writes TDR after TDRE=1, then clears SSR.TDRE.
|
||||
"""
|
||||
|
||||
smr: int = 0x00
|
||||
brr: int = 0xFF
|
||||
scr: int = 0x0C
|
||||
tdr: int = 0xFF
|
||||
ssr: int = 0x87
|
||||
rdr: int = 0x00
|
||||
tx_bytes: list[int] = field(default_factory=list)
|
||||
tx_events: list[SciTxEvent] = field(default_factory=list)
|
||||
tx_frames: list[bytes] = field(default_factory=list)
|
||||
_frame_buffer: bytearray = field(default_factory=bytearray)
|
||||
tx_ready_delay: int = 0
|
||||
|
||||
def read(self, address: int) -> int:
|
||||
if address == SCI1_SMR:
|
||||
return self.smr
|
||||
if address == SCI1_BRR:
|
||||
return self.brr
|
||||
if address == SCI1_SCR:
|
||||
return self.scr
|
||||
if address == SCI1_TDR:
|
||||
return self.tdr
|
||||
if address == SCI1_SSR:
|
||||
return self.ssr
|
||||
if address == SCI1_RDR:
|
||||
return self.rdr
|
||||
raise KeyError(address)
|
||||
|
||||
def write(self, address: int, value: int) -> None:
|
||||
value &= 0xFF
|
||||
if address == SCI1_SMR:
|
||||
self.smr = value
|
||||
elif address == SCI1_BRR:
|
||||
self.brr = value
|
||||
elif address == SCI1_SCR:
|
||||
self.scr = value
|
||||
elif address == SCI1_TDR:
|
||||
self.tdr = value
|
||||
self._write_tdr(value)
|
||||
elif address == SCI1_SSR:
|
||||
# The real SSR is R/(W)*: writable zeroes clear latched flags after
|
||||
# the required read sequence. This scaffold applies zero bits
|
||||
# directly so ROM BCLR/BSET style accesses can be modeled.
|
||||
self.ssr = value
|
||||
elif address == SCI1_RDR:
|
||||
self.rdr = value
|
||||
else:
|
||||
raise KeyError(address)
|
||||
|
||||
def _write_tdr(self, value: int) -> None:
|
||||
emitted = bool(self.scr & SCI_SCR_TE)
|
||||
if emitted:
|
||||
self.tx_bytes.append(value)
|
||||
self._frame_buffer.append(value)
|
||||
if len(self._frame_buffer) == len(HEARTBEAT_FRAME):
|
||||
self.tx_frames.append(bytes(self._frame_buffer))
|
||||
self._frame_buffer.clear()
|
||||
self.ssr |= SCI_SSR_TDRE
|
||||
self.tx_ready_delay = 2
|
||||
self.tx_events.append(SciTxEvent(SCI1_TDR, value, self.scr, self.ssr, emitted))
|
||||
|
||||
def inject_rx(self, value: int) -> None:
|
||||
self.rdr = value & 0xFF
|
||||
self.ssr |= SCI_SSR_RDRF
|
||||
|
||||
def saw_heartbeat(self) -> bool:
|
||||
return HEARTBEAT_FRAME in self.tx_frames
|
||||
|
||||
def tick(self) -> None:
|
||||
if self.tx_ready_delay:
|
||||
self.tx_ready_delay -= 1
|
||||
if self.tx_ready_delay == 0:
|
||||
self.ssr |= SCI_SSR_TDRE
|
||||
Reference in New Issue
Block a user