From 1031509e8a2b6ff6f04ec1258479dfa6745a6576 Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 15 May 2025 22:53:37 -0400 Subject: [PATCH] Refactor Emulator and Snes classes for improved memory access - Updated Emulator class to remove inheritance from SharedRom and streamline ROM handling. - Refactored memory access methods in Emulator and Snes classes to use consistent naming conventions. - Enhanced DungeonObjectRenderer to utilize the updated Snes class for CPU and memory operations, improving clarity and maintainability. - Cleaned up unnecessary comments and improved code formatting for better readability. --- src/app/emu/emulator.cc | 8 +-- src/app/emu/emulator.h | 7 ++- src/app/emu/memory/memory.h | 58 +++++++++++----------- src/app/emu/snes.h | 2 +- src/app/zelda3/dungeon/object_renderer.cc | 59 +++++++++------------- src/app/zelda3/dungeon/object_renderer.h | 60 ++++++----------------- 6 files changed, 78 insertions(+), 116 deletions(-) diff --git a/src/app/emu/emulator.cc b/src/app/emu/emulator.cc index cc0efbbc..8744fea3 100644 --- a/src/app/emu/emulator.cc +++ b/src/app/emu/emulator.cc @@ -58,8 +58,8 @@ void Emulator::Run() { } rom_data_ = rom()->vector(); snes_.Init(rom_data_); - wanted_frames_ = 1.0 / (snes_.Memory().pal_timing() ? 50.0 : 60.0); - wanted_samples_ = 48000 / (snes_.Memory().pal_timing() ? 50 : 60); + wanted_frames_ = 1.0 / (snes_.memory().pal_timing() ? 50.0 : 60.0); + wanted_samples_ = 48000 / (snes_.memory().pal_timing() ? 50 : 60); loaded = true; count_frequency = SDL_GetPerformanceFrequency(); @@ -491,8 +491,8 @@ void Emulator::RenderMemoryViewer() { ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse)) { - mem_edit.DrawContents((void*)snes_.Memory().rom_.data(), - snes_.Memory().rom_.size()); + mem_edit.DrawContents((void*)snes_.memory().rom_.data(), + snes_.memory().rom_.size()); ImGui::EndChild(); } diff --git a/src/app/emu/emulator.h b/src/app/emu/emulator.h index 642883fc..53a5d431 100644 --- a/src/app/emu/emulator.h +++ b/src/app/emu/emulator.h @@ -36,7 +36,7 @@ struct EmulatorKeybindings { * @class Emulator * @brief A class for emulating and debugging SNES games. */ -class Emulator : public SharedRom { +class Emulator { public: Emulator() { std::string emulator_layout = R"( @@ -92,7 +92,7 @@ class Emulator : public SharedRom { {"cpu.PC", &snes_.cpu().PC}, {"cpu.status", &snes_.cpu().status}, {"snes.cycle_count", &snes_.mutable_cycles()}, - {"cpu.SP", &snes_.Memory().mutable_sp()}, + {"cpu.SP", &snes_.memory().mutable_sp()}, {"spc.A", &snes_.apu().spc700().A}, {"spc.X", &snes_.apu().spc700().X}, {"spc.Y", &snes_.apu().spc700().Y}, @@ -115,6 +115,8 @@ class Emulator : public SharedRom { audio_device_ = audio_device; } auto wanted_samples() const -> int { return wanted_samples_; } + auto rom() { return rom_; } + auto mutable_rom() { return rom_; } private: void RenderNavBar(); @@ -153,6 +155,7 @@ class Emulator : public SharedRom { int16_t* audio_buffer_; SDL_AudioDeviceID audio_device_; + Rom* rom_; Snes snes_; SDL_Texture* ppu_texture_; diff --git a/src/app/emu/memory/memory.h b/src/app/emu/memory/memory.h index 097e0049..96b53ccd 100644 --- a/src/app/emu/memory/memory.h +++ b/src/app/emu/memory/memory.h @@ -30,27 +30,27 @@ typedef struct DmaChannel { uint8_t b_addr; uint16_t a_addr; uint8_t a_bank; - uint16_t size; // also indirect hdma adr - uint8_t ind_bank; // hdma - uint16_t table_addr; // hdma - uint8_t rep_count; // hdma + uint16_t size; // also indirect hdma adr + uint8_t ind_bank; // hdma + uint16_t table_addr; // hdma + uint8_t rep_count; // hdma uint8_t unusedByte; bool dma_active; bool hdma_active; uint8_t mode; bool fixed; bool decrement; - bool indirect; // hdma + bool indirect; // hdma bool from_b; bool unusedBit; - bool do_transfer; // hdma - bool terminated; // hdma + bool do_transfer; // hdma + bool terminated; // hdma } DmaChannel; typedef struct CpuCallbacks { - std::function read_byte; - std::function write_byte; - std::function idle; + std::function read_byte = nullptr; + std::function write_byte = nullptr; + std::function idle = nullptr; } CpuCallbacks; constexpr uint32_t kROMStart = 0x008000; @@ -62,7 +62,7 @@ constexpr uint32_t kRAMSize = 0x20000; * @brief Memory interface */ class Memory { -public: + public: virtual ~Memory() = default; virtual uint8_t ReadByte(uint32_t address) const = 0; virtual uint16_t ReadWord(uint32_t address) const = 0; @@ -116,25 +116,25 @@ public: * */ class MemoryImpl : public Memory { -public: + public: void Initialize(const std::vector &romData, bool verbose = false); uint16_t GetHeaderOffset() { uint16_t offset; switch (memory_[(0x00 << 16) + 0xFFD5] & 0x07) { - case 0: // LoROM - offset = 0x7FC0; - break; - case 1: // HiROM - offset = 0xFFC0; - break; - case 5: // ExHiROM - offset = 0x40; - break; - default: - throw std::invalid_argument( - "Unable to locate supported ROM mapping mode in the provided ROM " - "file. Please try another ROM file."); + case 0: // LoROM + offset = 0x7FC0; + break; + case 1: // HiROM + offset = 0xFFC0; + break; + case 5: // ExHiROM + offset = 0x40; + break; + default: + throw std::invalid_argument( + "Unable to locate supported ROM mapping mode in the provided ROM " + "file. Please try another ROM file."); } return offset; @@ -285,7 +285,7 @@ public: std::vector rom_; std::vector ram_; -private: + private: uint32_t GetMappedAddress(uint32_t address) const; bool verbose_ = false; @@ -323,7 +323,7 @@ private: std::vector memory_; }; -} // namespace emu -} // namespace yaze +} // namespace emu +} // namespace yaze -#endif // YAZE_APP_EMU_MEMORY_H +#endif // YAZE_APP_EMU_MEMORY_H diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index b173d932..e6981cd5 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -57,7 +57,7 @@ class Snes { auto cpu() -> Cpu& { return cpu_; } auto ppu() -> Ppu& { return ppu_; } auto apu() -> Apu& { return apu_; } - auto Memory() -> MemoryImpl& { return memory_; } + auto memory() -> MemoryImpl& { return memory_; } auto get_ram() -> uint8_t* { return ram; } auto mutable_cycles() -> uint64_t& { return cycles_; } diff --git a/src/app/zelda3/dungeon/object_renderer.cc b/src/app/zelda3/dungeon/object_renderer.cc index 0ac29e93..88b5c3a0 100644 --- a/src/app/zelda3/dungeon/object_renderer.cc +++ b/src/app/zelda3/dungeon/object_renderer.cc @@ -1,5 +1,7 @@ #include "app/zelda3/dungeon/object_renderer.h" +#include "app/gfx/arena.h" + namespace yaze { namespace zelda3 { @@ -8,8 +10,7 @@ void DungeonObjectRenderer::LoadObject(uint32_t routine_ptr, vram_.sheets = sheet_ids; rom_data_ = rom()->vector(); - // Prepare the CPU and memory environment - memory_.Initialize(rom_data_); + snes_.memory().Initialize(rom_data_); // Configure the object based on the fetched information ConfigureObject(); @@ -19,16 +20,16 @@ void DungeonObjectRenderer::LoadObject(uint32_t routine_ptr, } void DungeonObjectRenderer::ConfigureObject() { - cpu.A = 0x03D8; - cpu.X = 0x03D8; - cpu.DB = 0x7E; + snes_.cpu().A = 0x03D8; + snes_.cpu().X = 0x03D8; + snes_.cpu().DB = 0x7E; // VRAM target destinations - cpu.WriteLong(0xBF, 0x7E2000); - cpu.WriteLong(0xCB, 0x7E2080); - cpu.WriteLong(0xC2, 0x7E2002); - cpu.WriteLong(0xCE, 0x7E2082); - cpu.SetAccumulatorSize(false); - cpu.SetIndexSize(false); + snes_.cpu().WriteLong(0xBF, 0x7E2000); + snes_.cpu().WriteLong(0xCB, 0x7E2080); + snes_.cpu().WriteLong(0xC2, 0x7E2002); + snes_.cpu().WriteLong(0xCE, 0x7E2082); + snes_.cpu().SetAccumulatorSize(false); + snes_.cpu().SetIndexSize(false); } /** @@ -59,15 +60,15 @@ void DungeonObjectRenderer::ConfigureObject() { #_0198AD: RTS */ void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) { - cpu.PB = 0x01; - cpu.PC = routine_ptr; + snes_.cpu().PB = 0x01; + snes_.cpu().PC = routine_ptr; // Set up initial state for object drawing - cpu.Y = 0; // Start at the beginning of the tilemap - cpu.D = 0x7E; // Direct page register for memory access + snes_.cpu().Y = 0; // Start at the beginning of the tilemap + snes_.cpu().D = 0x7E; // Direct page register for memory access // Push return address to stack - cpu.PushLong(0x01 << 16 | 0xFFFF); // Push a dummy return address + snes_.cpu().PushLong(0x01 << 16 | 0xFFFF); // Push a dummy return address // Set up a maximum instruction count to prevent infinite loops const int MAX_INSTRUCTIONS = 10000; @@ -75,17 +76,18 @@ void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) { // Execute instructions until we hit a return instruction or max count while (instruction_count < MAX_INSTRUCTIONS) { - uint8_t opcode = cpu.ReadByte(cpu.PB << 16 | cpu.PC); + uint8_t opcode = + snes_.cpu().ReadByte(snes_.cpu().PB << 16 | snes_.cpu().PC); // Check for RTS (Return from Subroutine) instruction if (opcode == 0x60) { // Execute the RTS instruction - cpu.ExecuteInstruction(opcode); + snes_.cpu().ExecuteInstruction(opcode); break; // Exit the loop after RTS } // Execute the instruction - cpu.ExecuteInstruction(opcode); + snes_.cpu().ExecuteInstruction(opcode); instruction_count++; } @@ -108,7 +110,7 @@ void DungeonObjectRenderer::UpdateObjectBitmap() { // Iterate over tilemap in memory to read tile IDs for (int tile_index = 0; tile_index < 512; tile_index++) { // Read the tile ID from memory - uint16_t tile_id = memory_.ReadWord(0x7E2000 + tile_index * 2); + uint16_t tile_id = snes_.memory().ReadWord(0x7E2000 + tile_index * 2); // Skip empty tiles (0x0000) if (tile_id == 0) continue; @@ -128,8 +130,8 @@ void DungeonObjectRenderer::UpdateObjectBitmap() { int tile_y = (tile_index / 32) * 8; // Get the graphics sheet - auto& sheet = GraphicsSheetManager::GetInstance().mutable_gfx_sheets()->at( - vram_.sheets[sheet_number]); + auto& sheet = + gfx::Arena::Get().mutable_gfx_sheets()->at(vram_.sheets[sheet_number]); // Calculate the offset in the tilemap int tilemap_offset = tile_y * 256 + tile_x; @@ -137,19 +139,6 @@ void DungeonObjectRenderer::UpdateObjectBitmap() { // Copy the tile from the graphics sheet to the tilemap sheet.Get8x8Tile(tile_id % 32, 0, 0, tilemap_, tilemap_offset); } - - // Create the bitmap from the tilemap - bitmap_.Create(256, 256, 8, tilemap_); -} - -void DungeonObjectRenderer::SetPalette(const gfx::SnesPalette& palette, - size_t transparent_index) { - // Apply the palette to the bitmap - bitmap_.SetPaletteWithTransparent(palette, transparent_index); - - // Store the palette in the VRAM structure for future reference - vram_.palettes.clear(); - vram_.palettes.push_back(palette); } } // namespace zelda3 diff --git a/src/app/zelda3/dungeon/object_renderer.h b/src/app/zelda3/dungeon/object_renderer.h index c8a8eb6a..2290534f 100644 --- a/src/app/zelda3/dungeon/object_renderer.h +++ b/src/app/zelda3/dungeon/object_renderer.h @@ -1,9 +1,7 @@ #include #include -#include "app/emu/cpu/cpu.h" -#include "app/emu/memory/memory.h" -#include "app/emu/video/ppu.h" +#include "app/emu/snes.h" #include "app/gfx/bitmap.h" #include "app/gfx/snes_palette.h" #include "app/rom.h" @@ -14,80 +12,55 @@ namespace zelda3 { /** * @struct PseudoVram * @brief Simulates the SNES VRAM for object rendering - * + * * This structure holds the sheet IDs and palettes needed for rendering * dungeon objects in Link to the Past. */ struct PseudoVram { - std::array sheets = { 0 }; + std::array sheets = {0}; std::vector palettes; }; /** * @class DungeonObjectRenderer * @brief Renders dungeon objects from Link to the Past - * + * * This class uses the emulator subsystem to simulate the SNES CPU * drawing routines for dungeon objects. It captures the tile data * written to memory and renders it to a bitmap. */ -class DungeonObjectRenderer : public SharedRom { +class DungeonObjectRenderer { public: DungeonObjectRenderer() = default; /** * @brief Loads and renders a dungeon object - * + * * @param routine_ptr Pointer to the drawing routine in ROM * @param sheet_ids Array of graphics sheet IDs used by the object */ void LoadObject(uint32_t routine_ptr, std::array& sheet_ids); - + /** * @brief Configures the CPU state for object rendering */ void ConfigureObject(); - + /** * @brief Executes the object drawing routine - * + * * @param routine_ptr Pointer to the drawing routine in ROM */ void RenderObject(uint32_t routine_ptr); - + /** * @brief Updates the bitmap with the rendered object */ void UpdateObjectBitmap(); - - /** - * @brief Sets the palette for the rendered object - * - * @param palette The palette to use for the object - * @param transparent_index Index of the transparent color (default: 0) - */ - void SetPalette(const gfx::SnesPalette& palette, size_t transparent_index = 0); - /** - * @brief Gets the rendered bitmap - * - * @return gfx::Bitmap* Pointer to the bitmap - */ - gfx::Bitmap* bitmap() { return &bitmap_; } - - /** - * @brief Gets the memory implementation - * - * @return Memory implementation - */ - auto memory() { return memory_; } - - /** - * @brief Gets a mutable pointer to the memory implementation - * - * @return Mutable pointer to the memory implementation - */ - auto mutable_memory() { return &memory_; } + auto mutable_memory() { return &tilemap_; } + auto rom() { return rom_; } + auto mutable_rom() { return &rom_; } private: std::vector tilemap_; @@ -95,11 +68,8 @@ class DungeonObjectRenderer : public SharedRom { PseudoVram vram_; - emu::MemoryImpl memory_; - emu::CpuCallbacks cpu_callbacks_; - emu::Ppu ppu{memory_}; - emu::Cpu cpu{memory_, cpu_callbacks_}; - + Rom* rom_; + emu::Snes snes_; gfx::Bitmap bitmap_; };