1
0
Files
h8-536-decoder/h8536/emulator/timers.py
2026-05-25 22:17:36 +10:00

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",
]