110 lines
3.1 KiB
Python
110 lines
3.1 KiB
Python
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",
|
|
]
|