from __future__ import annotations from dataclasses import dataclass from .constants import FRT_TCR_OCIEA, FRT_TCSR_CCLRA, FRT_TCSR_OCFA from .memory import MemoryMap FRT_INTERNAL_PRESCALERS = { 0b00: 4, 0b01: 8, 0b10: 32, } @dataclass(frozen=True) class FrtRegisters: name: str tcr: int tcsr: int frc_h: int ocra_h: int @dataclass class FrtOciaScheduler: registers: FrtRegisters cycle_accumulator: int = 0 last_config: tuple[int, int] | None = None compare_matches: int = 0 def tick(self, memory: MemoryMap, cycle_delta: int) -> None: cycle_delta = max(0, cycle_delta) tcr = memory.register8(self.registers.tcr) prescaler = frt_internal_prescaler(tcr) if prescaler is None: self.cycle_accumulator = 0 self.last_config = None return config = self._config(memory) if config != self.last_config: self.cycle_accumulator = 0 self.last_config = config self.cycle_accumulator += cycle_delta ticks = self.cycle_accumulator // prescaler self.cycle_accumulator %= prescaler if ticks <= 0: return frc = memory.register16(self.registers.frc_h) ocra = memory.register16(self.registers.ocra_h) tcsr = memory.register8(self.registers.tcsr) clear_on_a = bool(tcsr & FRT_TCSR_CCLRA) matched = False for _ in range(ticks): frc = (frc + 1) & 0xFFFF if frc == ocra: matched = True self.compare_matches += 1 if clear_on_a: frc = 0 memory.set_register16(self.registers.frc_h, frc) if matched: memory.set_register8(self.registers.tcsr, tcsr | FRT_TCSR_OCFA) def period_cycles(self, memory: MemoryMap) -> int | None: tcr = memory.register8(self.registers.tcr) prescaler = frt_internal_prescaler(tcr) if prescaler is None: return None ocra = memory.register16(self.registers.ocra_h) return frt_ocia_period_cycles(tcr, ocra) def pending(self, memory: MemoryMap) -> bool: return bool(memory.register8(self.registers.tcr) & FRT_TCR_OCIEA and memory.register8(self.registers.tcsr) & FRT_TCSR_OCFA) def period_ms(self, memory: MemoryMap, clock_hz: int) -> float | None: period = self.period_cycles(memory) if period is None or clock_hz <= 0: return None return 1000.0 * period / clock_hz def _config(self, memory: MemoryMap) -> tuple[int, int]: return memory.register8(self.registers.tcr) & 0x03, memory.register16(self.registers.ocra_h) def frt_internal_prescaler(tcr: int) -> int | None: return FRT_INTERNAL_PRESCALERS.get(tcr & 0x03) def frt_ocia_period_cycles(tcr: int, ocra: int) -> int | None: prescaler = frt_internal_prescaler(tcr) if prescaler is None: return None compare_ticks = (ocra & 0xFFFF) or 0x10000 return compare_ticks * prescaler __all__ = [ "FRT_INTERNAL_PRESCALERS", "FrtOciaScheduler", "FrtRegisters", "frt_internal_prescaler", "frt_ocia_period_cycles", ]