1
0

EMualtor im

This commit is contained in:
Aiden
2026-05-25 18:43:36 +10:00
parent 81f5d7a150
commit 05e1237acc
18 changed files with 993 additions and 20 deletions

View File

@@ -0,0 +1,124 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
from .cpu import mask, sign_bit
LOC_BFE0_TRANSFER_WRAPPER = 0xBFE0
LOC_BFFE_TRANSFER_WRAPPER = 0xBFFE
LOC_C08B_P9_WRITE_BYTE = 0xC08B
LOC_C0DB_P9_READ_BYTE = 0xC0DB
LOC_C10C_P9_MARKER = 0xC10C
LOC_C121_P9_MARKER = 0xC121
LOC_C142_P9_MARKER = 0xC142
@dataclass(frozen=True)
class P9FastPathConfig:
"""Configuration for optional ROM P9 transfer shortcuts.
The helper assumes the CPU PC is exactly at a known routine entry. It
models the routine as if it completed successfully and returned via RTS.
Integration should keep this disabled unless the runner intentionally opts
into skipping these ROM routines.
"""
enabled: bool = False
write_byte_pc: int = LOC_C08B_P9_WRITE_BYTE
read_byte_pc: int = LOC_C0DB_P9_READ_BYTE
marker_pcs: frozenset[int] = frozenset(
{
LOC_C10C_P9_MARKER,
LOC_C121_P9_MARKER,
LOC_C142_P9_MARKER,
}
)
wrapper_pcs: frozenset[int] = frozenset(
{
LOC_BFE0_TRANSFER_WRAPPER,
LOC_BFFE_TRANSFER_WRAPPER,
}
)
default_input_byte: int = 0xFF
account_step: bool = True
cycles_per_hit: int = 0
@dataclass(frozen=True)
class P9FastPathEvent:
kind: str
pc: int
value: int | None = None
@dataclass
class P9FastPath:
"""Optional fast-path scaffold for ROM P9 bit-transfer routines."""
config: P9FastPathConfig = field(default_factory=P9FastPathConfig)
input_bytes: list[int] = field(default_factory=list)
output_bytes: list[int] = field(default_factory=list)
events: list[P9FastPathEvent] = field(default_factory=list)
def queue_input(self, *values: int) -> None:
self.input_bytes.extend(value & 0xFF for value in values)
def try_handle(self, emulator: Any) -> bool:
if not self.config.enabled:
return False
pc = emulator.cpu.pc & 0xFFFF
if pc == (self.config.write_byte_pc & 0xFFFF):
self._handle_write_byte(emulator)
elif pc == (self.config.read_byte_pc & 0xFFFF):
self._handle_read_byte(emulator)
elif pc in self.config.marker_pcs:
self.events.append(P9FastPathEvent("marker", pc))
self._return_from_subroutine(emulator)
elif pc in self.config.wrapper_pcs:
self.events.append(P9FastPathEvent("wrapper_success", pc))
emulator.cpu.regs[0] = 1
self._set_logic_flags(emulator.cpu, 1, 1)
self._return_from_subroutine(emulator)
else:
return False
if self.config.account_step:
emulator.cpu.steps += 1
emulator.cpu.cycles += self.config.cycles_per_hit
return True
def _handle_write_byte(self, emulator: Any) -> None:
pc = emulator.cpu.pc & 0xFFFF
value = emulator.cpu.regs[0] & 0xFF
self.output_bytes.append(value)
self.events.append(P9FastPathEvent("write_byte", pc, value))
emulator.cpu.regs[0] = 1
self._set_logic_flags(emulator.cpu, 1, 1)
self._return_from_subroutine(emulator)
def _handle_read_byte(self, emulator: Any) -> None:
pc = emulator.cpu.pc & 0xFFFF
value = self.input_bytes.pop(0) if self.input_bytes else self.config.default_input_byte
value &= 0xFF
self.events.append(P9FastPathEvent("read_byte", pc, value))
# The ROM-side read routine yields a byte in R5. Model that as a byte
# register write so the existing high byte is not accidentally clobbered.
emulator.cpu.regs[5] = (emulator.cpu.regs[5] & 0xFF00) | value
self._set_logic_flags(emulator.cpu, value, 1)
self._return_from_subroutine(emulator)
def _return_from_subroutine(self, emulator: Any) -> None:
sp = emulator.cpu.regs[7] & 0xFFFF
emulator.cpu.pc = emulator.memory.read16(sp) & 0xFFFF
emulator.cpu.regs[7] = (sp + 2) & 0xFFFF
def _set_logic_flags(self, cpu: Any, value: int, size: int) -> None:
value &= mask(size)
cpu.z = value == 0
cpu.n = bool(value & sign_bit(size))
cpu.v = False