From b205a11a54d117f830400f4978a7e4e18e74b280 Mon Sep 17 00:00:00 2001 From: Edd Mann Date: Tue, 11 Nov 2025 08:04:13 +0000 Subject: [PATCH] Revert "fix: enable Game Boy Color mode when loading CGB ROMs" --- src/Cpu/Cpu.php | 34 ------------- src/Cpu/InstructionSet.php | 6 +-- src/Emulator.php | 13 +---- src/Memory/Wram.php | 64 ++++--------------------- src/Ppu/ColorPalette.php | 11 +---- src/Ppu/Ppu.php | 62 +++++------------------- src/System/CgbController.php | 8 +--- tests/Unit/System/CgbControllerTest.php | 5 +- 8 files changed, 30 insertions(+), 173 deletions(-) diff --git a/src/Cpu/Cpu.php b/src/Cpu/Cpu.php index 408b821..059c0d1 100644 --- a/src/Cpu/Cpu.php +++ b/src/Cpu/Cpu.php @@ -8,7 +8,6 @@ use Gb\Cpu\Register\FlagRegister; use Gb\Cpu\Register\Register16; use Gb\Interrupts\InterruptController; -use Gb\System\CgbController; /** * LR35902 CPU (Sharp SM83) @@ -46,9 +45,6 @@ final class Cpu private int $pendingCycles = 0; private ?\Closure $cycleCallback = null; - // CGB controller for speed switching (optional, only needed for CGB) - private ?CgbController $cgbController = null; - /** * @param BusInterface $bus Memory bus for reading/writing memory * @param InterruptController $interruptController Interrupt controller @@ -458,36 +454,6 @@ public function setStopped(bool $stopped): void $this->stopped = $stopped; } - /** - * Set the CGB controller for speed switching support. - * - * @param CgbController $cgbController CGB controller instance - */ - public function setCgbController(CgbController $cgbController): void - { - $this->cgbController = $cgbController; - } - - /** - * Execute STOP instruction. - * In CGB mode with speed switch prepared, triggers speed switch. - * Otherwise, stops the CPU until button press. - */ - public function executeStop(): void - { - // Check if CGB speed switch is prepared - if ($this->cgbController !== null && $this->cgbController->isSpeedSwitchPrepared()) { - // Trigger speed switch (toggles between normal and double speed) - $this->cgbController->triggerSpeedSwitch(); - // Don't stop the CPU - execution continues at new speed - $this->stopped = false; - } else { - // Normal STOP behavior: halt and stop until button press - $this->halted = true; - $this->stopped = true; - } - } - /** * Get the Interrupt Master Enable flag. * diff --git a/src/Cpu/InstructionSet.php b/src/Cpu/InstructionSet.php index cb67e50..ce6ca07 100644 --- a/src/Cpu/InstructionSet.php +++ b/src/Cpu/InstructionSet.php @@ -371,7 +371,7 @@ private static function buildInstruction(int $opcode): Instruction }, ), - // 0x10: STOP - Stop CPU and LCD until button press (or trigger speed switch in CGB) + // 0x10: STOP - Stop CPU and LCD until button press 0x10 => new Instruction( opcode: 0x10, mnemonic: 'STOP', @@ -380,8 +380,8 @@ private static function buildInstruction(int $opcode): Instruction handler: static function (Cpu $cpu): int { // Read next byte (should be 0x00) self::readImm8($cpu); - // Execute STOP - handles both speed switching and normal stop - $cpu->executeStop(); + $cpu->setHalted(true); + $cpu->setStopped(true); return 4; }, ), diff --git a/src/Emulator.php b/src/Emulator.php index 3c542b8..9d4dac8 100644 --- a/src/Emulator.php +++ b/src/Emulator.php @@ -131,11 +131,6 @@ private function initializeSystem(): void $this->interruptController ); - // Enable CGB mode if cartridge supports it - if ($this->cartridge->getHeader()->isCgbSupported()) { - $this->ppu->enableCgbMode(true); - } - // Create APU $this->apu = new Apu($this->audioSink); @@ -182,11 +177,8 @@ private function initializeSystem(): void // HDMA registers: HDMA1-HDMA5 $this->bus->attachIoDevice($this->hdma, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55); - // Connect HDMA to PPU for H-Blank triggers - $this->ppu->setHdmaController($this->hdma); - // Create CGB controller - $this->cgb = new CgbController($vram, $wram); + $this->cgb = new CgbController($vram); // CGB registers: KEY1, VBK, RP, SVBK $this->bus->attachIoDevice($this->cgb, 0xFF4D, 0xFF4F, 0xFF56, 0xFF70); @@ -205,9 +197,6 @@ private function initializeSystem(): void // Create CPU $this->cpu = new Cpu($this->bus, $this->interruptController); - // Set CGB controller on CPU for speed switching support - $this->cpu->setCgbController($this->cgb); - // Optimization (Step 14): Pre-build all 512 instructions for faster dispatch // Expected: 1-2% performance gain by eliminating lazy initialization checks \Gb\Cpu\InstructionSet::warmCache(); diff --git a/src/Memory/Wram.php b/src/Memory/Wram.php index bea57a5..474764e 100644 --- a/src/Memory/Wram.php +++ b/src/Memory/Wram.php @@ -13,25 +13,17 @@ * In DMG mode: Single 8KB bank * In CGB mode: Bank 0 (0xC000-0xCFFF) + switchable banks 1-7 (0xD000-0xDFFF) * - * CGB WRAM: 32KB (8 banks of 4KB each) - * - Bank 0: Always at 0xC000-0xCFFF - * - Banks 1-7: Switchable at 0xD000-0xDFFF via SVBK register + * For now, implements DMG-style 8KB WRAM. CGB bank switching will be added later. */ final class Wram implements DeviceInterface { - /** @var array> Working RAM storage (8 banks × 4KB) */ - private array $banks; - - /** @var int Currently selected bank for 0xD000-0xDFFF (1-7, defaults to 1) */ - private int $currentBank = 1; + /** @var array Working RAM storage (8KB = 8192 bytes) */ + private array $ram; public function __construct() { - // Initialize 8 banks of 4KB each with 0x00 - $this->banks = []; - for ($bank = 0; $bank < 8; $bank++) { - $this->banks[$bank] = array_fill(0, 4096, 0x00); - } + // Initialize 8KB of RAM with 0x00 + $this->ram = array_fill(0, 8192, 0x00); } /** @@ -42,16 +34,8 @@ public function __construct() */ public function readByte(int $address): int { - $offset = $address & 0x1FFF; - - if ($offset < 0x1000) { - // 0xC000-0xCFFF: Always bank 0 - return $this->banks[0][$offset]; - } else { - // 0xD000-0xDFFF: Switchable bank (1-7) - $bankOffset = $offset - 0x1000; - return $this->banks[$this->currentBank][$bankOffset]; - } + $offset = $address & 0x1FFF; // Mask to 8KB + return $this->ram[$offset]; } /** @@ -62,37 +46,7 @@ public function readByte(int $address): int */ public function writeByte(int $address, int $value): void { - $offset = $address & 0x1FFF; - $value = $value & 0xFF; - - if ($offset < 0x1000) { - // 0xC000-0xCFFF: Always bank 0 - $this->banks[0][$offset] = $value; - } else { - // 0xD000-0xDFFF: Switchable bank (1-7) - $bankOffset = $offset - 0x1000; - $this->banks[$this->currentBank][$bankOffset] = $value; - } - } - - /** - * Set the current WRAM bank (CGB only, controlled by SVBK register). - * - * @param int $bank Bank number (0-7, where 0 is treated as 1) - */ - public function setBank(int $bank): void - { - // Bank 0 is treated as bank 1 (banks 1-7 are valid) - $this->currentBank = ($bank & 0x07) === 0 ? 1 : ($bank & 0x07); - } - - /** - * Get the current WRAM bank number. - * - * @return int Current bank (1-7) - */ - public function getBank(): int - { - return $this->currentBank; + $offset = $address & 0x1FFF; // Mask to 8KB + $this->ram[$offset] = $value & 0xFF; } } diff --git a/src/Ppu/ColorPalette.php b/src/Ppu/ColorPalette.php index 3b62639..06273bd 100644 --- a/src/Ppu/ColorPalette.php +++ b/src/Ppu/ColorPalette.php @@ -42,15 +42,8 @@ final class ColorPalette public function __construct() { // Initialize palettes with white (0x7FFF = all 1s in 15-bit RGB) - // Each color is 2 bytes: low byte (0xFF), high byte (0x7F) - $this->bgPalette = []; - $this->objPalette = []; - for ($i = 0; $i < 64; $i += 2) { - $this->bgPalette[$i] = 0xFF; // Low byte - $this->bgPalette[$i + 1] = 0x7F; // High byte - $this->objPalette[$i] = 0xFF; // Low byte - $this->objPalette[$i + 1] = 0x7F; // High byte - } + $this->bgPalette = array_fill(0, 64, 0xFF); + $this->objPalette = array_fill(0, 64, 0xFF); } /** diff --git a/src/Ppu/Ppu.php b/src/Ppu/Ppu.php index a5862de..f9c01e2 100644 --- a/src/Ppu/Ppu.php +++ b/src/Ppu/Ppu.php @@ -5,7 +5,6 @@ namespace Gb\Ppu; use Gb\Bus\DeviceInterface; -use Gb\Dma\HdmaController; use Gb\Interrupts\InterruptController; use Gb\Interrupts\InterruptType; use Gb\Memory\Vram; @@ -98,19 +97,12 @@ final class Ppu implements DeviceInterface /** @var array */ private array $bgColorBuffer = []; - // Background priority buffer for CGB priority rules (stores BG-to-OAM priority bit) - /** @var array */ - private array $bgPriorityBuffer = []; - /** @var ColorPalette Color palette system (CGB) */ private readonly ColorPalette $colorPalette; /** @var bool CGB mode enabled */ private bool $cgbMode = false; - /** @var HdmaController|null HDMA controller for H-Blank DMA transfers (CGB) */ - private ?HdmaController $hdmaController = null; - public function __construct( private readonly Vram $vram, private readonly Oam $oam, @@ -217,11 +209,6 @@ private function setMode(PpuMode $mode): void // Update STAT register mode bits $this->stat = ($this->stat & ~self::STAT_MODE_MASK) | $mode->getStatBits(); - // Trigger HDMA H-Blank transfer if entering H-Blank mode - if ($mode === PpuMode::HBlank && $this->hdmaController !== null) { - $this->hdmaController->onHBlank(); - } - // Trigger STAT interrupt if enabled for this mode $statInterrupt = match ($mode) { PpuMode::HBlank => ($this->stat & self::STAT_MODE0_INT) !== 0, @@ -254,7 +241,6 @@ private function renderScanline(): void // Initialize scanline buffer and BG color buffer $this->scanlineBuffer = array_fill(0, ArrayFramebuffer::WIDTH, Color::fromDmgShade(0)); $this->bgColorBuffer = array_fill(0, ArrayFramebuffer::WIDTH, 0); - $this->bgPriorityBuffer = array_fill(0, ArrayFramebuffer::WIDTH, false); // Render layers if (($this->lcdc & self::LCDC_BG_WINDOW_ENABLE) !== 0) { @@ -301,7 +287,6 @@ private function renderBackground(): void $vramBank = ($attributes & 0x08) !== 0 ? 1 : 0; // Bit 3: VRAM bank $xFlip = ($attributes & 0x20) !== 0; // Bit 5: horizontal flip $yFlip = ($attributes & 0x40) !== 0; // Bit 6: vertical flip - $bgPriority = ($attributes & 0x80) !== 0; // Bit 7: BG-to-OAM priority // Apply flips $finalTileY = $yFlip ? (7 - $tileY) : $tileY; @@ -314,9 +299,8 @@ private function renderBackground(): void // Get pixel color $color = $this->getTilePixel($vramData, $tileDataAddr, $finalTileX, $finalTileY); - // Store raw color and priority for sprite priority checking + // Store raw color for sprite priority checking $this->bgColorBuffer[$x] = $color; - $this->bgPriorityBuffer[$x] = $bgPriority; // Apply palette if ($this->cgbMode) { @@ -362,7 +346,6 @@ private function renderWindow(): void $vramBank = ($attributes & 0x08) !== 0 ? 1 : 0; // Bit 3: VRAM bank $xFlip = ($attributes & 0x20) !== 0; // Bit 5: horizontal flip $yFlip = ($attributes & 0x40) !== 0; // Bit 6: vertical flip - $bgPriority = ($attributes & 0x80) !== 0; // Bit 7: BG-to-OAM priority // Apply flips $finalTileY = $yFlip ? (7 - $tileY) : $tileY; @@ -374,9 +357,8 @@ private function renderWindow(): void $color = $this->getTilePixel($vramData, $tileDataAddr, $finalTileX, $finalTileY); - // Store raw color and priority for sprite priority checking + // Store raw color for sprite priority checking $this->bgColorBuffer[$x] = $color; - $this->bgPriorityBuffer[$x] = $bgPriority; // Apply palette if ($this->cgbMode) { @@ -484,17 +466,9 @@ private function renderSprite(array $sprite, int $spriteHeight, array $vramData) } // Check priority (behind BG) - $bgColor = $this->bgColorBuffer[$pixelX]; - if ($bgColor !== 0) { - // In CGB mode, check BG priority bit first - if ($this->cgbMode && $this->bgPriorityBuffer[$pixelX]) { - // BG priority bit is set - BG always wins - continue; - } - // Otherwise, use normal sprite priority (behindBg flag) - if ($behindBg) { - continue; - } + if ($behindBg && $this->bgColorBuffer[$pixelX] !== 0) { + // If BG pixel is not color 0, sprite is hidden behind BG + continue; } if ($this->cgbMode) { @@ -562,14 +536,6 @@ public function isCgbMode(): bool return $this->cgbMode; } - /** - * Set the HDMA controller for H-Blank DMA transfers. - */ - public function setHdmaController(HdmaController $hdmaController): void - { - $this->hdmaController = $hdmaController; - } - // DeviceInterface implementation for I/O registers public function readByte(int $address): int { @@ -585,11 +551,10 @@ public function readByte(int $address): int self::OBP1 => $this->obp1, self::WY => $this->wy, self::WX => $this->wx, - // CGB color palette registers - only accessible in CGB mode - self::BCPS => $this->cgbMode ? $this->colorPalette->readBgIndex() : 0xFF, - self::BCPD => $this->cgbMode ? $this->colorPalette->readBgData() : 0xFF, - self::OCPS => $this->cgbMode ? $this->colorPalette->readObjIndex() : 0xFF, - self::OCPD => $this->cgbMode ? $this->colorPalette->readObjData() : 0xFF, + self::BCPS => $this->colorPalette->readBgIndex(), + self::BCPD => $this->colorPalette->readBgData(), + self::OCPS => $this->colorPalette->readObjIndex(), + self::OCPD => $this->colorPalette->readObjData(), default => 0xFF, }; } @@ -608,11 +573,10 @@ public function writeByte(int $address, int $value): void self::OBP1 => $this->obp1 = $value, self::WY => $this->wy = $value, self::WX => $this->wx = $value, - // CGB color palette registers - only writable in CGB mode - self::BCPS => $this->cgbMode ? $this->colorPalette->writeBgIndex($value) : null, - self::BCPD => $this->cgbMode ? $this->colorPalette->writeBgData($value) : null, - self::OCPS => $this->cgbMode ? $this->colorPalette->writeObjIndex($value) : null, - self::OCPD => $this->cgbMode ? $this->colorPalette->writeObjData($value) : null, + self::BCPS => $this->colorPalette->writeBgIndex($value), + self::BCPD => $this->colorPalette->writeBgData($value), + self::OCPS => $this->colorPalette->writeObjIndex($value), + self::OCPD => $this->colorPalette->writeObjData($value), default => null, }; diff --git a/src/System/CgbController.php b/src/System/CgbController.php index 82fdbe9..eb40db1 100644 --- a/src/System/CgbController.php +++ b/src/System/CgbController.php @@ -6,7 +6,6 @@ use Gb\Bus\DeviceInterface; use Gb\Memory\Vram; -use Gb\Memory\Wram; /** * Game Boy Color Controller @@ -15,8 +14,7 @@ * - VBK (0xFF4F): VRAM bank select * - KEY1 (0xFF4D): Speed switch control * - RP (0xFF56): Infrared communications port (stub) - * - SVBK (0xFF70): WRAM bank select - * - HDMA1-5 (0xFF51-0xFF55): HDMA registers (handled by HdmaController) + * - HDMA1-5 (0xFF51-0xFF55): HDMA registers (future) * * Reference: Pan Docs - CGB Registers */ @@ -26,7 +24,6 @@ final class CgbController implements DeviceInterface private const KEY1 = 0xFF4D; // Speed switch private const VBK = 0xFF4F; // VRAM bank private const RP = 0xFF56; // Infrared port - private const SVBK = 0xFF70; // WRAM bank /** @var int KEY1 register: speed switch control */ private int $key1 = 0x00; @@ -36,7 +33,6 @@ final class CgbController implements DeviceInterface public function __construct( private readonly Vram $vram, - private readonly Wram $wram, ) { } @@ -46,7 +42,6 @@ public function readByte(int $address): int self::KEY1 => $this->readKey1(), self::VBK => $this->vram->getBank() | 0xFE, // Only bit 0 used, others return 1 self::RP => 0xFF, // Infrared stub: always return 0xFF - self::SVBK => $this->wram->getBank() | 0xF8, // Only bits 0-2 used, others return 1 default => 0xFF, }; } @@ -57,7 +52,6 @@ public function writeByte(int $address, int $value): void self::KEY1 => $this->writeKey1($value), self::VBK => $this->vram->setBank($value & 0x01), self::RP => null, // Infrared stub: ignore writes - self::SVBK => $this->wram->setBank($value & 0x07), default => null, }; } diff --git a/tests/Unit/System/CgbControllerTest.php b/tests/Unit/System/CgbControllerTest.php index 5244931..92c6213 100644 --- a/tests/Unit/System/CgbControllerTest.php +++ b/tests/Unit/System/CgbControllerTest.php @@ -5,7 +5,6 @@ namespace Tests\Unit\System; use Gb\Memory\Vram; -use Gb\Memory\Wram; use Gb\System\CgbController; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; @@ -14,13 +13,11 @@ final class CgbControllerTest extends TestCase { private CgbController $controller; private Vram $vram; - private Wram $wram; protected function setUp(): void { $this->vram = new Vram(); - $this->wram = new Wram(); - $this->controller = new CgbController($this->vram, $this->wram); + $this->controller = new CgbController($this->vram); } #[Test]