Files

119 lines
3.5 KiB
Python
Raw Permalink Normal View History

2026-02-24 22:12:20 -07:00
from __future__ import annotations
from typing import Callable
from .debugger import Debugger, BreakContext
2026-02-25 17:26:59 -07:00
from .ppu import PPUState, RenderedFrame
2026-02-25 23:30:36 -07:00
from .cpu import ProcessorState
2026-02-24 22:12:20 -07:00
from .cart import Cartridge
from . import gbc_py as core
class GameBoyColor:
2026-03-11 16:06:04 -07:00
def __init__(self, cartridge: Cartridge = None, dbg_callback: Callable = None):
2026-02-24 22:12:20 -07:00
try:
self._gbc = (
core.GameBoyColor(lambda ctx: dbg_callback(self, BreakContext(ctx)))
2026-02-25 23:30:36 -07:00
if dbg_callback is not None
2026-02-24 22:12:20 -07:00
else core.GameBoyColor()
)
except Exception as e:
2026-03-11 16:06:04 -07:00
raise RuntimeError("Failed to initialize GameBoyColor core instance") from e
2026-02-24 22:12:20 -07:00
# Entirely optional, only check if provided
if cartridge is not None:
self.insert_cartridge(cartridge)
2026-03-11 16:06:04 -07:00
"""
2026-02-25 16:39:04 -07:00
Internal helpers for argument correctness checks
2026-03-11 16:06:04 -07:00
"""
2026-02-25 16:39:04 -07:00
def _check_bitwidth_byte(self, value: int):
if value > 0xFF or value < 0x00:
raise ValueError("`value` must be 8-bits")
def _check_bitwidth_addr(self, addr: int):
if addr > 0xFFFF or addr < 0x0000:
raise ValueError("`addr` must be 16-bits")
2026-03-11 16:06:04 -07:00
"""
2026-02-25 16:39:04 -07:00
Publicly exposed interface implementation
2026-03-11 16:06:04 -07:00
"""
2026-02-25 16:39:04 -07:00
@property
def cpu_state(self):
try:
cpu = self._gbc.get_cpu()
except Exception as e:
raise RuntimeError(
"Failed to initialize ProcessorState core instance"
) from e
return ProcessorState(cpu.get_state())
2026-02-25 17:26:59 -07:00
@property
def ppu_state(self):
try:
ppu = self._gbc.get_ppu()
except Exception as e:
2026-03-11 16:06:04 -07:00
raise RuntimeError("Failed to initialize PPUState core instance") from e
2026-02-25 17:26:59 -07:00
return PPUState(ppu.get_state())
2026-02-25 23:30:36 -07:00
@property
def debugger(self):
try:
debugger = self._gbc.get_debugger()
except Exception as e:
2026-03-11 16:06:04 -07:00
raise RuntimeError("Failed to initialize Debugger core instance") from e
2026-02-25 23:30:36 -07:00
return Debugger(debugger)
2026-02-25 17:26:59 -07:00
@property
def frame(self):
try:
frame = self._gbc.get_frame()
except Exception as e:
2026-03-11 16:06:04 -07:00
raise RuntimeError("Failed to acquire frame from core") from e
2026-02-25 17:26:59 -07:00
return RenderedFrame(frame)
2026-02-24 22:12:20 -07:00
def insert_cartridge(self, cartridge: Cartridge):
self._gbc.insert_cartridge(cartridge._raw)
def read_byte(self, addr: int, debug=False):
2026-02-25 16:39:04 -07:00
self._check_bitwidth_addr(addr)
addr_bus = self._gbc.get_bus()
return addr_bus.read_byte(addr, debug=debug)
2026-02-25 16:39:04 -07:00
def write_byte(self, addr: int, value: int, debug=False):
2026-02-25 16:39:04 -07:00
self._check_bitwidth_addr(addr)
self._check_bitwidth_byte(value)
addr_bus = self._gbc.get_bus()
return addr_bus.write_byte(addr, value)
2026-04-22 17:24:00 -07:00
def step_frame(self, big_step: bool = True):
2026-02-25 16:39:04 -07:00
cycles = 70224 # One frame worth of t-cycles
2026-04-22 17:24:00 -07:00
if big_step:
self.big_step(cycles=cycles)
else:
self.step(cycles=cycles)
2026-02-25 16:39:04 -07:00
2026-04-22 17:24:00 -07:00
def step_scanline(self, big_step: bool = True):
2026-02-25 16:39:04 -07:00
cycles = 456 # One scanline worth of t-cycles
2026-04-22 17:24:00 -07:00
if big_step:
self.big_step(cycles=cycles)
else:
self.step(cycles=cycles)
def big_step(self, cycles: int = None) -> int:
if cycles is not None:
return self._gbc.big_step_cycles(cycles)
else:
return self._gbc.big_step()
2026-02-25 16:39:04 -07:00
2026-02-24 22:12:20 -07:00
def step(self, cycles: int = None):
if cycles is not None:
self._gbc.step_cycles(cycles)
else: # Latent, not recommended
self._gbc.step()