From 5a4ecc5b201e317eacf34c4fec1493a21ff28797 Mon Sep 17 00:00:00 2001 From: scawful Date: Sun, 27 Aug 2023 15:24:27 -0400 Subject: [PATCH] PPU upgrades, move Memory to own dir --- src/app/core/emulator.cc | 1 + src/app/core/emulator.h | 1 + src/app/emu/audio/apu.cc | 2 +- src/app/emu/audio/apu.h | 2 +- src/app/emu/audio/dsp.cc | 2 +- src/app/emu/audio/dsp.h | 2 +- src/app/emu/cpu.h | 6 +- src/app/emu/memory/dma.h | 59 ++++ src/app/emu/{mem.h => memory/memory.h} | 59 ++-- src/app/emu/snes.cc | 2 +- src/app/emu/snes.h | 56 +--- src/app/emu/video/ppu.cc | 355 ++++++++++++------------- src/app/emu/video/ppu.h | 93 ++----- test/cpu_test.cc | 12 +- 14 files changed, 318 insertions(+), 334 deletions(-) create mode 100644 src/app/emu/memory/dma.h rename src/app/emu/{mem.h => memory/memory.h} (88%) diff --git a/src/app/core/emulator.cc b/src/app/core/emulator.cc index c5633a05..c7a7409d 100644 --- a/src/app/core/emulator.cc +++ b/src/app/core/emulator.cc @@ -53,6 +53,7 @@ void Emulator::RenderNavBar() { } if (ImGui::BeginMenu("Debug")) { + ImGui::MenuItem("PPU Register Viewer", nullptr, &show_ppu_reg_viewer_); MENU_ITEM("Debugger") {} MENU_ITEM("Memory Viewer") {} MENU_ITEM("Tile Viewer") {} diff --git a/src/app/core/emulator.h b/src/app/core/emulator.h index 1c4a96b9..2f2ca100 100644 --- a/src/app/core/emulator.h +++ b/src/app/core/emulator.h @@ -33,6 +33,7 @@ class Emulator : public SharedROM { emu::SNES snes_; bool running_ = false; + bool show_ppu_reg_viewer_ = false; }; } // namespace core diff --git a/src/app/emu/audio/apu.cc b/src/app/emu/audio/apu.cc index 71a9ce60..62f6d0b2 100644 --- a/src/app/emu/audio/apu.cc +++ b/src/app/emu/audio/apu.cc @@ -8,7 +8,7 @@ #include "app/emu/audio/dsp.h" #include "app/emu/audio/spc700.h" #include "app/emu/clock.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { diff --git a/src/app/emu/audio/apu.h b/src/app/emu/audio/apu.h index 4e74a7f3..3f1edf6b 100644 --- a/src/app/emu/audio/apu.h +++ b/src/app/emu/audio/apu.h @@ -8,7 +8,7 @@ #include "app/emu/audio/dsp.h" #include "app/emu/audio/spc700.h" #include "app/emu/clock.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { diff --git a/src/app/emu/audio/dsp.cc b/src/app/emu/audio/dsp.cc index 0ebb342e..ccc5b2c6 100644 --- a/src/app/emu/audio/dsp.cc +++ b/src/app/emu/audio/dsp.cc @@ -1,6 +1,6 @@ #include "app/emu/audio/dsp.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { diff --git a/src/app/emu/audio/dsp.h b/src/app/emu/audio/dsp.h index 0d9c5d40..15dc161e 100644 --- a/src/app/emu/audio/dsp.h +++ b/src/app/emu/audio/dsp.h @@ -5,7 +5,7 @@ #include #include -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index 61dc872d..587b45e8 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -8,7 +8,7 @@ #include "app/emu/clock.h" #include "app/emu/debug/log.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { @@ -683,6 +683,10 @@ class CPU : public Memory, public Loggable { bool GetFlag(uint8_t mask) const { return (status & mask) != 0; } // Appease the C++ Gods... + std::vector ReadByteVector(uint16_t address, + uint16_t size) const override { + return memory.ReadByteVector(address, size); + } void PushByte(uint8_t value) override { memory.PushByte(value); } void PushWord(uint16_t value) override { memory.PushWord(value); } uint8_t PopByte() override { return memory.PopByte(); } diff --git a/src/app/emu/memory/dma.h b/src/app/emu/memory/dma.h new file mode 100644 index 00000000..2566898d --- /dev/null +++ b/src/app/emu/memory/dma.h @@ -0,0 +1,59 @@ +#ifndef YAZE_APP_EMU_MEMORY_DMA_H +#define YAZE_APP_EMU_MEMORY_DMA_H + +#include + +namespace yaze { +namespace app { +namespace emu { + +// Direct Memory Address +class DMA { + public: + DMA() { + // Initialize DMA and HDMA channels + for (int i = 0; i < 8; ++i) { + channels[i].DMAPn = 0; + channels[i].BBADn = 0; + channels[i].UNUSEDn = 0; + channels[i].A1Tn = 0xFFFFFF; + channels[i].DASn = 0xFFFF; + channels[i].A2An = 0xFFFF; + channels[i].NLTRn = 0xFF; + } + } + + // DMA Transfer Modes + enum class DMA_TRANSFER_TYPE { + OAM, + PPUDATA, + CGDATA, + FILL_VRAM, + CLEAR_VRAM, + RESET_VRAM + }; + + // Functions for handling DMA and HDMA transfers + void StartDMATransfer(uint8_t channels); + void EnableHDMATransfers(uint8_t channels); + + // Structure for DMA and HDMA channel registers + struct Channel { + uint8_t DMAPn; // DMA/HDMA parameters + uint8_t BBADn; // B-bus address + uint8_t UNUSEDn; // Unused byte + uint32_t A1Tn; // DMA Current Address / HDMA Table Start Address + uint16_t DASn; // DMA Byte-Counter / HDMA indirect table address + uint16_t A2An; // HDMA Table Current Address + uint8_t NLTRn; // HDMA Line-Counter + }; + Channel channels[8]; + + uint8_t MDMAEN = 0; // Start DMA transfer + uint8_t HDMAEN = 0; // Enable HDMA transfers +}; +} // namespace emu +} // namespace app +} // namespace yaze + +#endif // YAZE_APP_EMU_MEMORY_DMA_H \ No newline at end of file diff --git a/src/app/emu/mem.h b/src/app/emu/memory/memory.h similarity index 88% rename from src/app/emu/mem.h rename to src/app/emu/memory/memory.h index 872ba5f1..9a986d2c 100644 --- a/src/app/emu/mem.h +++ b/src/app/emu/memory/memory.h @@ -24,26 +24,6 @@ // 2000-FFFF System RAM // 7F 0000-FFFF System RAM -// HiROM (Mode 21): - -// Banks Offset Purpose -// 00-3F 0000-1FFF LowRAM (shadowed from 7E) -// 2000-2FFF PPU1, APU -// 3000-3FFF SFX, DSP, etc. -// 4000-41FF Controller -// 4200-5FFF PPU2, DMA, etc. -// 6000-7FFF SRAM (256KB) -// 8000-FFFF 32k ROM Chunk -// 40-6F 0000-FFFF 64k ROM Chunk -// 70-77 0000-FFFF SRAM (256KB) -// 78-7D 0000-FFFF Never Used -// 7E 0000-1FFF LowRAM -// 2000-7FFF HighRAM -// 8000-FFFF Expanded RAM -// 7F 0000-FFFF More Expanded RAM -// 80-EF 0000-FFFF Mirror of 00-6F -// F0-FF 0000-FFFF 64k ROM Chunk - namespace yaze { namespace app { namespace emu { @@ -115,6 +95,15 @@ class Observer { virtual void Notify(uint32_t address, uint8_t data) = 0; }; +constexpr uint32_t kROMStart = 0xC00000; +constexpr uint32_t kROMSize = 0x400000; +constexpr uint32_t kRAMStart = 0x7E0000; +constexpr uint32_t kRAMSize = 0x20000; +constexpr uint32_t kVRAMStart = 0x210000; +constexpr uint32_t kVRAMSize = 0x10000; +constexpr uint32_t kOAMStart = 0x218000; +constexpr uint32_t kOAMSize = 0x220; + // memory.h class Memory { public: @@ -122,6 +111,8 @@ class Memory { virtual uint8_t ReadByte(uint16_t address) const = 0; virtual uint16_t ReadWord(uint16_t address) const = 0; virtual uint32_t ReadWordLong(uint16_t address) const = 0; + virtual std::vector ReadByteVector(uint16_t address, + uint16_t length) const = 0; virtual void WriteByte(uint32_t address, uint8_t value) = 0; virtual void WriteWord(uint32_t address, uint16_t value) = 0; @@ -154,8 +145,7 @@ class MemoryImpl : public Memory, public Loggable { const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB // Clear memory - memory_.clear(); - memory_.resize(0x1000000, 0); // 24-bit address space + std::fill(memory_.begin(), memory_.end(), 0); // Load ROM data into memory based on LoROM mapping size_t romSize = romData.size(); @@ -244,6 +234,13 @@ class MemoryImpl : public Memory, public Loggable { (static_cast(memory_.at(mapped_address + 1)) << 8) | (static_cast(memory_.at(mapped_address + 2)) << 16); } + std::vector ReadByteVector(uint16_t address, + uint16_t length) const override { + uint32_t mapped_address = GetMappedAddress(address); + NotifyObservers(mapped_address, /*data=*/0); + return std::vector(memory_.begin() + mapped_address, + memory_.begin() + mapped_address + length); + } void WriteByte(uint32_t address, uint8_t value) override { uint32_t mapped_address = GetMappedAddress(address); @@ -305,9 +302,10 @@ class MemoryImpl : public Memory, public Loggable { // Stack Pointer access. int16_t SP() const override { return SP_; } void SetSP(int16_t value) override { SP_ = value; } - - void SetMemory(const std::vector& data) override { memory_ = data; } - void ClearMemory() override { memory_.resize(64000, 0x00); } + void ClearMemory() override { std::fill(memory_.begin(), memory_.end(), 0); } + void SetMemory(const std::vector& data) override { + std::copy(data.begin(), data.end(), memory_.begin()); + } void LoadData(const std::vector& data) override { std::copy(data.begin(), data.end(), memory_.begin()); } @@ -357,15 +355,6 @@ class MemoryImpl : public Memory, public Loggable { return address; // Return the original address if no mapping is defined } - static const uint32_t kROMStart = 0xC00000; - static const uint32_t kROMSize = 0x400000; - static const uint32_t kRAMStart = 0x7E0000; - static const uint32_t kRAMSize = 0x20000; - static const uint32_t kVRAMStart = 0x210000; - static const uint32_t kVRAMSize = 0x10000; - static const uint32_t kOAMStart = 0x218000; - static const uint32_t kOAMSize = 0x220; - void NotifyObservers(uint32_t address, uint8_t data) const { for (auto observer : observers_) { observer->Notify(address, data); @@ -375,7 +364,7 @@ class MemoryImpl : public Memory, public Loggable { std::vector observers_; // Memory (64KB) - std::vector memory_; + std::array memory_; // Stack Pointer uint16_t SP_ = 0x01FF; diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index aa40cf70..313f48f9 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -12,7 +12,7 @@ #include "app/emu/clock.h" #include "app/emu/cpu.h" #include "app/emu/debug/debugger.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" #include "app/emu/video/ppu.h" #include "app/rom.h" diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index d6e09e00..81cc0e92 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -1,11 +1,17 @@ +#include + #include +#include #include +#include #include "app/emu/audio/apu.h" #include "app/emu/audio/spc700.h" #include "app/emu/clock.h" #include "app/emu/cpu.h" #include "app/emu/debug/debugger.h" +#include "app/emu/memory/dma.h" +#include "app/emu/memory/memory.h" #include "app/emu/video/ppu.h" #include "app/rom.h" @@ -13,52 +19,6 @@ namespace yaze { namespace app { namespace emu { -// Direct Memory Address -class DMA { - public: - DMA() { - // Initialize DMA and HDMA channels - for (int i = 0; i < 8; ++i) { - channels[i].DMAPn = 0; - channels[i].BBADn = 0; - channels[i].UNUSEDn = 0; - channels[i].A1Tn = 0xFFFFFF; - channels[i].DASn = 0xFFFF; - channels[i].A2An = 0xFFFF; - channels[i].NLTRn = 0xFF; - } - } - - // DMA Transfer Modes - enum class DMA_TRANSFER_TYPE { - OAM, - PPUDATA, - CGDATA, - FILL_VRAM, - CLEAR_VRAM, - RESET_VRAM - }; - - // Functions for handling DMA and HDMA transfers - void StartDMATransfer(uint8_t channels); - void EnableHDMATransfers(uint8_t channels); - - // Structure for DMA and HDMA channel registers - struct Channel { - uint8_t DMAPn; // DMA/HDMA parameters - uint8_t BBADn; // B-bus address - uint8_t UNUSEDn; // Unused byte - uint32_t A1Tn; // DMA Current Address / HDMA Table Start Address - uint16_t DASn; // DMA Byte-Counter / HDMA indirect table address - uint16_t A2An; // HDMA Table Current Address - uint8_t NLTRn; // HDMA Line-Counter - }; - Channel channels[8]; - - uint8_t MDMAEN = 0; // Start DMA transfer - uint8_t HDMAEN = 0; // Enable HDMA transfers -}; - class SNES : public DMA { public: SNES() = default; @@ -118,13 +78,13 @@ class SNES : public DMA { ROMInfo rom_info_; Debugger debugger; + // Currently loaded ROM std::vector rom_data; // Byte flag to indicate if the VBlank routine should be executed or not std::atomic v_blank_flag_; - // 32-bit counter to track the number of NMI interrupts (useful for clocks and - // timers) + // 32-bit counter to track the number of NMI interrupts std::atomic frame_counter_; // Other private member variables diff --git a/src/app/emu/video/ppu.cc b/src/app/emu/video/ppu.cc index 276eed6f..f92d0094 100644 --- a/src/app/emu/video/ppu.cc +++ b/src/app/emu/video/ppu.cc @@ -4,12 +4,45 @@ #include #include -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { namespace emu { +using namespace PPURegisters; + +void PPU::Update() { + auto cycles_to_run = clock_.GetCycleCount(); + + UpdateInternalState(cycles_to_run); + + // Render however many scanlines we're supposed to. + if (current_scanline_ < visibleScanlines) { + // Render the current scanline + RenderScanline(); + + // Increment the current scanline + current_scanline_++; + } +} + +void PPU::UpdateInternalState(int cycles) { + // Update the PPU's internal state based on the number of cycles + cycle_count_ += cycles; + + // Check if it's time to move to the next scanline + if (cycle_count_ >= cyclesPerScanline) { + current_scanline_++; + cycle_count_ -= cyclesPerScanline; + + // If we've reached the end of the frame, reset to the first scanline + if (current_scanline_ >= totalScanlines) { + current_scanline_ = 0; + } + } +} + void PPU::RenderScanline() { // Fetch the tile data from VRAM, tile map data from memory, and palette data // from CGRAM @@ -40,106 +73,65 @@ void PPU::RenderScanline() { // the frame buffer // Display the frame buffer on the screen - DisplayFrameBuffer(); // Sends the frame buffer to the display hardware - // (e.g., SDL2) + DisplayFrameBuffer(); } -void PPU::Update() { - auto cycles_to_run = clock_.GetCycleCount(); - - UpdateInternalState(cycles_to_run); - - // Render however many scanlines we're supposed to. - if (currentScanline < visibleScanlines) { - // Render the current scanline - // This involves fetching tile data, applying palette colors, handling - // sprite spriorities, etc. - RenderScanline(); - - // Increment the current scanline - currentScanline++; - } -} - -void PPU::UpdateInternalState(int cycles) { - // Update the PPU's internal state based on the number of cycles - cycleCount += cycles; - - // Check if it's time to move to the next scanline - if (cycleCount >= cyclesPerScanline) { - currentScanline++; - cycleCount -= cyclesPerScanline; - - // If we've reached the end of the frame, reset to the first scanline - if (currentScanline >= totalScanlines) { - currentScanline = 0; +void PPU::Notify(uint32_t address, uint8_t data) { + // Handle communication in the PPU. + if (address >= 0x2100 && address <= 0x213F) { + // Handle register notification + switch (address) { + case PPURegisters::INIDISP: + enable_forced_blanking_ = (data >> 7) & 0x01; + break; + case PPURegisters::BGMODE: + // Update the PPU mode settings + UpdateModeSettings(); + break; } } } -// Reads a byte from the specified PPU register -uint8_t PPU::ReadRegister(uint16_t address) { - switch (address) { - case 0x2102: // OAM Address Register (low byte) - return oam_address_ & 0xFF; - case 0x2103: // OAM Address Register (high byte) - return (oam_address_ >> 8) & 0xFF; - // ... handle other PPU registers - default: - // Invalid register address, return 0 - return 0; - } -} - -// Writes a byte to the specified PPU register -void PPU::WriteRegister(uint16_t address, uint8_t value) { - switch (address) { - case 0x2102: // OAM Address Register (low byte) - oam_address_ = (oam_address_ & 0xFF00) | value; - break; - case 0x2103: // OAM Address Register (high byte) - oam_address_ = (oam_address_ & 0x00FF) | (value << 8); - break; - // ... handle other PPU registers - default: - // Invalid register address, do nothing - break; - } -} - void PPU::UpdateModeSettings() { // Read the PPU mode settings from the PPU registers - uint8_t modeRegister = ReadRegister(PPURegisters::INIDISP); + uint8_t modeRegister = memory_.ReadByte(PPURegisters::INIDISP); - // Extract the PPU mode and other relevant settings from the register value - BackgroundMode mode = static_cast( - modeRegister & 0x07); // Mode is stored in the lower 3 bits - bool backgroundEnabled = - (modeRegister >> 7) & 0x01; // Background enabled flag is stored in bit 7 - bool spritesEnabled = - (modeRegister >> 6) & 0x01; // Sprites enabled flag is stored in bit 6 + // Mode is stored in the lower 3 bits + auto mode = static_cast(modeRegister & 0x07); - // Update the internal mode settings based on the extracted values - // modeSettings_.backgroundEnabled = backgroundEnabled; - // modeSettings_.spritesEnabled = spritesEnabled; - - // Update the tilemap, tile data, and palette settings based on the selected - // mode + // Update the tilemap, tile data, and palette settings switch (mode) { case BackgroundMode::Mode0: - // Mode 0: 4 background layers, all with 2bpp - // Update the tilemap, tile data, and palette settings accordingly - // ... + // Mode 0: 4 layers, each 2bpp (4 colors) break; case BackgroundMode::Mode1: - // Mode 1: 3 background layers (2 with 4bpp, 1 with 2bpp) - // Update the tilemap, tile data, and palette settings accordingly - // ... + // Mode 1: 2 layers, 4bpp (16 colors), 1 layer, 2bpp (4 colors) break; - // Handle other modes and update the settings accordingly - // ... + case BackgroundMode::Mode2: + // Mode 2: 2 layers, 4bpp (16 colors), 1 layer for offset-per-tile + break; + + case BackgroundMode::Mode3: + // Mode 3: 1 layer, 8bpp (256 colors), 1 layer, 4bpp (16 colors) + break; + + case BackgroundMode::Mode4: + // Mode 4: 1 layer, 8bpp (256 colors), 1 layer, 2bpp (4 colors) + break; + + case BackgroundMode::Mode5: + // Mode 5: 1 layer, 4bpp (16 colors), 1 layer, 2bpp (4 colors) hi-res + break; + + case BackgroundMode::Mode6: + // Mode 6: 1 layer, 4bpp (16 colors), 1 layer for offset-per-tile, hi-res + break; + + case BackgroundMode::Mode7: + // Mode 7: 1 layer, 8bpp (256 colors), rotation/scaling + break; default: // Invalid mode setting, handle the error or set default settings @@ -151,31 +143,108 @@ void PPU::UpdateModeSettings() { // Update tile data, tilemaps, sprites, and palette based on the mode settings UpdateTileData(); UpdatePaletteData(); - // ... } +// Internal methods to handle PPU rendering and operations +void PPU::UpdateTileData() { + // Fetch tile data from VRAM and store it in the internal buffer + for (uint16_t address = 0; address < tile_data_size_; ++address) { + tile_data_[address] = memory_.ReadByte(vram_base_address_ + address); + } + + // Update the tilemap entries based on the fetched tile data + for (uint16_t entryIndex = 0; entryIndex < tilemap_.entries.size(); + ++entryIndex) { + uint16_t tilemapAddress = + tilemap_base_address_ + entryIndex * sizeof(TilemapEntry); + // Assume ReadWord reads a 16-bit value from VRAM + uint16_t tileData = memory_.ReadWord(tilemapAddress); + + // Extract tilemap entry attributes from the tile data + TilemapEntry entry; + // Tile number is stored in the lower 10 bits + entry.tileNumber = tileData & 0x03FF; + + // Palette is stored in bits 10-12 + entry.palette = (tileData >> 10) & 0x07; + + // Priority is stored in bit 13 + entry.priority = (tileData >> 13) & 0x01; + + // Horizontal flip is stored in bit 14 + entry.hFlip = (tileData >> 14) & 0x01; + + // Vertical flip is stored in bit 15 + entry.vFlip = (tileData >> 15) & 0x01; + + tilemap_.entries[entryIndex] = entry; + } + + // Update the sprites based on the fetched tile data + for (uint16_t spriteIndex = 0; spriteIndex < sprites_.size(); ++spriteIndex) { + uint16_t spriteAddress = + oam_address_ + spriteIndex * sizeof(SpriteAttributes); + // Assume ReadWord reads a 16-bit value from VRAM + uint16_t spriteData = memory_.ReadWord(spriteAddress); + + // Extract sprite attributes from the sprite data + SpriteAttributes sprite; + + sprite.x = memory_.ReadByte(spriteAddress); + sprite.y = memory_.ReadByte(spriteAddress + 1); + + // Tile number is stored in the lower 9 + sprite.tile = spriteData & 0x01FF; + + // bits Palette is stored in bits 9-11 + sprite.palette = (spriteData >> 9) & 0x07; + + // Priority is stored in bits 12-13 + sprite.priority = (spriteData >> 12) & 0x03; + + // Horizontal flip is stored in bit 14 + sprite.hFlip = (spriteData >> 14) & 0x01; + + // Vertical flip is stored in bit 15 + sprite.vFlip = (spriteData >> 15) & 0x01; + + sprites_[spriteIndex] = sprite; + } +} + +void PPU::UpdateTileMapData() {} + void PPU::RenderBackground(int layer) { + auto bg1_tilemap_info = BGSC(0); + auto bg1_chr_data = BGNBA(0); + auto bg2_tilemap_info = BGSC(0); + auto bg2_chr_data = BGNBA(0); + auto bg3_tilemap_info = BGSC(0); + auto bg3_chr_data = BGNBA(0); + auto bg4_tilemap_info = BGSC(0); + auto bg4_chr_data = BGNBA(0); + switch (layer) { case 1: - // // Render the first background layer - // auto bg1_tilemap_info = - // PPURegisters::BGSC(ReadVRAM(PPURegisters::BG1SC)); auto bg1_chr_data = - // PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG12NBA)); break; - // case 2: - // // Render the second background layer - // auto bg2_tilemap_info = - // PPURegisters::BGSC(ReadVRAM(PPURegisters::BG2SC)); auto bg2_chr_data = - // PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG12NBA)); break; - // case 3: - // // Render the third background layer - // auto bg3_tilemap_info = - // PPURegisters::BGSC(ReadVRAM(PPURegisters::BG3SC)); auto bg3_chr_data = - // PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG34NBA)); break; - // case 4: - // // Render the fourth background layer - // auto bg4_tilemap_info = - // PPURegisters::BGSC(ReadVRAM(PPURegisters::BG4SC)); auto bg4_chr_data = - // PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG34NBA)); break; + // Render the first background layer + bg1_tilemap_info = BGSC(memory_.ReadByte(BG1SC)); + bg1_chr_data = BGNBA(memory_.ReadByte(BG12NBA)); + break; + case 2: + // Render the second background layer + bg2_tilemap_info = BGSC(memory_.ReadByte(BG2SC)); + bg2_chr_data = BGNBA(memory_.ReadByte(BG12NBA)); + break; + case 3: + // Render the third background layer + bg3_tilemap_info = BGSC(memory_.ReadByte(BG3SC)); + bg3_chr_data = BGNBA(memory_.ReadByte(BG34NBA)); + break; + case 4: + // Render the fourth background layer + bg4_tilemap_info = BGSC(memory_.ReadByte(BG4SC)); + bg4_chr_data = BGNBA(memory_.ReadByte(BG34NBA)); + break; default: // Invalid layer, do nothing break; @@ -186,83 +255,13 @@ void PPU::RenderSprites() { // ... } -uint32_t PPU::GetPaletteColor(uint8_t colorIndex) { - return memory_.ReadWordLong(colorIndex); -} +void PPU::UpdatePaletteData() {} -uint8_t PPU::ReadVRAM(uint16_t address) { - // ... -} +void PPU::ApplyEffects() {} -void PPU::WriteVRAM(uint16_t address, uint8_t value) { - // ... -} +void PPU::ComposeLayers() {} -uint8_t PPU::ReadOAM(uint16_t address) { return memory_.ReadByte(address); } - -void PPU::WriteOAM(uint16_t address, uint8_t value) { - // ... -} - -uint8_t PPU::ReadCGRAM(uint16_t address) { return memory_.ReadByte(address); } - -void PPU::WriteCGRAM(uint16_t address, uint8_t value) { - // ... -} - -// Internal methods to handle PPU rendering and operations -void PPU::UpdateTileData() { - // Fetch tile data from VRAM and store it in the internal buffer - for (uint16_t address = 0; address < tileDataSize_; ++address) { - tileData_[address] = memory_.ReadByte(vramBaseAddress_ + address); - } - - // Update the tilemap entries based on the fetched tile data - for (uint16_t entryIndex = 0; entryIndex < tilemap_.entries.size(); - ++entryIndex) { - uint16_t tilemapAddress = - tilemapBaseAddress_ + entryIndex * sizeof(TilemapEntry); - uint16_t tileData = memory_.ReadWord( - tilemapAddress); // Assume ReadWord reads a 16-bit value from VRAM - - // Extract tilemap entry attributes from the tile data - TilemapEntry entry; - entry.tileNumber = - tileData & 0x03FF; // Tile number is stored in the lower 10 bits - entry.palette = (tileData >> 10) & 0x07; // Palette is stored in bits 10-12 - entry.priority = (tileData >> 13) & 0x01; // Priority is stored in bit 13 - entry.hFlip = - (tileData >> 14) & 0x01; // Horizontal flip is stored in bit 14 - entry.vFlip = (tileData >> 15) & 0x01; // Vertical flip is stored in bit 15 - - tilemap_.entries[entryIndex] = entry; - } - - // Update the sprites based on the fetched tile data - for (uint16_t spriteIndex = 0; spriteIndex < sprites_.size(); ++spriteIndex) { - uint16_t spriteAddress = - oam_address_ + spriteIndex * sizeof(SpriteAttributes); - uint16_t spriteData = memory_.ReadWord( - spriteAddress); // Assume ReadWord reads a 16-bit value from VRAM - - // Extract sprite attributes from the sprite data - SpriteAttributes sprite; - sprite.x = memory_.ReadByte(spriteAddress); - sprite.y = memory_.ReadByte(spriteAddress + 1); - sprite.tile = - spriteData & 0x01FF; // Tile number is stored in the lower 9 bits - sprite.palette = - (spriteData >> 9) & 0x07; // Palette is stored in bits 9-11 - sprite.priority = - (spriteData >> 12) & 0x03; // Priority is stored in bits 12-13 - sprite.hFlip = - (spriteData >> 14) & 0x01; // Horizontal flip is stored in bit 14 - sprite.vFlip = - (spriteData >> 15) & 0x01; // Vertical flip is stored in bit 15 - - sprites_[spriteIndex] = sprite; - } -} +void PPU::DisplayFrameBuffer() {} } // namespace emu } // namespace app diff --git a/src/app/emu/video/ppu.h b/src/app/emu/video/ppu.h index e4ad214b..f6e378c2 100644 --- a/src/app/emu/video/ppu.h +++ b/src/app/emu/video/ppu.h @@ -6,7 +6,7 @@ #include #include "app/emu/clock.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { @@ -640,44 +640,37 @@ class PPU : public Observer { // Initializes the PPU with the necessary resources and dependencies PPU(Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {} + // Initialize the frame buffer void Init() { - // Initialize the frame buffer with a size that corresponds to the - // screen resolution clock_.SetFrequency(kPpuClockSpeed); frame_buffer_.resize(256 * 240, 0); } - void UpdateClock(double delta_time) { clock_.UpdateClock(delta_time); } - // Resets the PPU to its initial state void Reset() { std::fill(frame_buffer_.begin(), frame_buffer_.end(), 0); } // Runs the PPU for one frame. void Update(); + void UpdateClock(double delta_time) { clock_.UpdateClock(delta_time); } void UpdateInternalState(int cycles); - void Notify(uint32_t address, uint8_t data) override { - // Handle communication in the PPU. - } - - // Reads a byte from the specified PPU register - uint8_t ReadRegister(uint16_t address); - - // Writes a byte to the specified PPU register - void WriteRegister(uint16_t address, uint8_t value); - // Renders a scanline of the screen void RenderScanline(); + void Notify(uint32_t address, uint8_t data) override; + // Returns the pixel data for the current frame const std::vector& GetFrameBuffer() const { return frame_buffer_; } private: + // Updates internal state based on PPU register settings + void UpdateModeSettings(); + // Internal methods to handle PPU rendering and operations void UpdateTileData(); - // Updates internal state based on PPU register settings - void UpdateModeSettings(); + // Fetches the tile map data from memory and stores it in an internal buffer + void UpdateTileMapData(); // Renders a background layer void RenderBackground(int layer); @@ -685,73 +678,41 @@ class PPU : public Observer { // Renders sprites (also known as objects) void RenderSprites(); - void UpdateTileMapData() { - // Fetches the tile map data from memory and stores it in an internal - // buffer - } + // Fetches the palette data from CGRAM and stores it in an internal buffer + void UpdatePaletteData(); - void UpdatePaletteData() { - // Fetches the palette data from CGRAM and stores it in an internal - // buffer - } + // Applies effects to the layers based on the current mode and register + void ApplyEffects(); - void ApplyEffects() { - // Applies effects to the layers based on the current mode and register - // settings - } + // Combines the layers into a single image and stores it in the frame buffer + void ComposeLayers(); - void ComposeLayers() { - // Combines the layers into a single image and stores it in the frame - // buffer - } - - void DisplayFrameBuffer() { - // Sends the frame buffer to the display hardware (e.g., SDL2) - } - - // Retrieves a pixel color from the palette - uint32_t GetPaletteColor(uint8_t colorIndex); - - // Handles VRAM (Video RAM) reads and writes - uint8_t ReadVRAM(uint16_t address); - void WriteVRAM(uint16_t address, uint8_t value); - - // Handles OAM (Object Attribute Memory) reads and writes - uint8_t ReadOAM(uint16_t address); - void WriteOAM(uint16_t address, uint8_t value); - - // Handle CGRAM (Color Palette RAM) reads and writes - uint8_t ReadCGRAM(uint16_t address); - void WriteCGRAM(uint16_t address, uint8_t value); + // Sends the frame buffer to the display hardware (e.g., SDL2) + void DisplayFrameBuffer(); // =========================================================== // Member variables to store internal PPU state and resources Memory& memory_; Clock& clock_; - std::vector frame_buffer_; Tilemap tilemap_; BackgroundMode bg_mode_; std::array bg_layers_; std::vector sprites_; - std::vector tileData_; + std::vector tile_data_; + std::vector frame_buffer_; uint16_t oam_address_; - uint16_t tileDataSize_; - uint16_t vramBaseAddress_; - uint16_t tilemapBaseAddress_; + uint16_t tile_data_size_; + uint16_t vram_base_address_; + uint16_t tilemap_base_address_; - // The VRAM memory area holds tiles and tile maps. - std::array vram_; + uint16_t screen_brightness_ = 0x00; - // The OAM memory area holds sprite properties. - std::array oam_; + bool enable_forced_blanking_ = false; - // The CGRAM memory area holds the color palette data. - std::array cgram_; - - int cycleCount = 0; - int currentScanline = 0; + int cycle_count_ = 0; + int current_scanline_ = 0; const int cyclesPerScanline = 341; // SNES PPU has 341 cycles per scanline const int totalScanlines = 262; // SNES PPU has 262 scanlines per frame const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines diff --git a/test/cpu_test.cc b/test/cpu_test.cc index c8db4f0d..2e033edd 100644 --- a/test/cpu_test.cc +++ b/test/cpu_test.cc @@ -4,7 +4,7 @@ #include #include "app/emu/clock.h" -#include "app/emu/mem.h" +#include "app/emu/memory/memory.h" namespace yaze { namespace app { @@ -24,6 +24,8 @@ class MockMemory : public Memory { MOCK_CONST_METHOD1(ReadByte, uint8_t(uint16_t address)); MOCK_CONST_METHOD1(ReadWord, uint16_t(uint16_t address)); MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint16_t address)); + MOCK_METHOD(std::vector, ReadByteVector, + (uint16_t address, uint16_t length), (const, override)); MOCK_METHOD2(WriteByte, void(uint32_t address, uint8_t value)); MOCK_METHOD2(WriteWord, void(uint32_t address, uint16_t value)); @@ -84,6 +86,14 @@ class MockMemory : public Memory { (static_cast(memory_.at(address + 1)) << 8) | (static_cast(memory_.at(address + 2)) << 16); }); + ON_CALL(*this, ReadByteVector(::testing::_, ::testing::_)) + .WillByDefault([this](uint16_t address, uint16_t length) { + std::vector data; + for (int i = 0; i < length; i++) { + data.push_back(memory_.at(address + i)); + } + return data; + }); ON_CALL(*this, WriteByte(::testing::_, ::testing::_)) .WillByDefault([this](uint32_t address, uint8_t value) { memory_[address] = value;