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.
This commit is contained in:
scawful
2025-05-15 22:53:37 -04:00
parent 86d72fe0ca
commit 1031509e8a
6 changed files with 78 additions and 116 deletions

View File

@@ -58,8 +58,8 @@ void Emulator::Run() {
} }
rom_data_ = rom()->vector(); rom_data_ = rom()->vector();
snes_.Init(rom_data_); snes_.Init(rom_data_);
wanted_frames_ = 1.0 / (snes_.Memory().pal_timing() ? 50.0 : 60.0); wanted_frames_ = 1.0 / (snes_.memory().pal_timing() ? 50.0 : 60.0);
wanted_samples_ = 48000 / (snes_.Memory().pal_timing() ? 50 : 60); wanted_samples_ = 48000 / (snes_.memory().pal_timing() ? 50 : 60);
loaded = true; loaded = true;
count_frequency = SDL_GetPerformanceFrequency(); count_frequency = SDL_GetPerformanceFrequency();
@@ -491,8 +491,8 @@ void Emulator::RenderMemoryViewer() {
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse)) { ImGuiWindowFlags_NoScrollWithMouse)) {
mem_edit.DrawContents((void*)snes_.Memory().rom_.data(), mem_edit.DrawContents((void*)snes_.memory().rom_.data(),
snes_.Memory().rom_.size()); snes_.memory().rom_.size());
ImGui::EndChild(); ImGui::EndChild();
} }

View File

@@ -36,7 +36,7 @@ struct EmulatorKeybindings {
* @class Emulator * @class Emulator
* @brief A class for emulating and debugging SNES games. * @brief A class for emulating and debugging SNES games.
*/ */
class Emulator : public SharedRom { class Emulator {
public: public:
Emulator() { Emulator() {
std::string emulator_layout = R"( std::string emulator_layout = R"(
@@ -92,7 +92,7 @@ class Emulator : public SharedRom {
{"cpu.PC", &snes_.cpu().PC}, {"cpu.PC", &snes_.cpu().PC},
{"cpu.status", &snes_.cpu().status}, {"cpu.status", &snes_.cpu().status},
{"snes.cycle_count", &snes_.mutable_cycles()}, {"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.A", &snes_.apu().spc700().A},
{"spc.X", &snes_.apu().spc700().X}, {"spc.X", &snes_.apu().spc700().X},
{"spc.Y", &snes_.apu().spc700().Y}, {"spc.Y", &snes_.apu().spc700().Y},
@@ -115,6 +115,8 @@ class Emulator : public SharedRom {
audio_device_ = audio_device; audio_device_ = audio_device;
} }
auto wanted_samples() const -> int { return wanted_samples_; } auto wanted_samples() const -> int { return wanted_samples_; }
auto rom() { return rom_; }
auto mutable_rom() { return rom_; }
private: private:
void RenderNavBar(); void RenderNavBar();
@@ -153,6 +155,7 @@ class Emulator : public SharedRom {
int16_t* audio_buffer_; int16_t* audio_buffer_;
SDL_AudioDeviceID audio_device_; SDL_AudioDeviceID audio_device_;
Rom* rom_;
Snes snes_; Snes snes_;
SDL_Texture* ppu_texture_; SDL_Texture* ppu_texture_;

View File

@@ -30,27 +30,27 @@ typedef struct DmaChannel {
uint8_t b_addr; uint8_t b_addr;
uint16_t a_addr; uint16_t a_addr;
uint8_t a_bank; uint8_t a_bank;
uint16_t size; // also indirect hdma adr uint16_t size; // also indirect hdma adr
uint8_t ind_bank; // hdma uint8_t ind_bank; // hdma
uint16_t table_addr; // hdma uint16_t table_addr; // hdma
uint8_t rep_count; // hdma uint8_t rep_count; // hdma
uint8_t unusedByte; uint8_t unusedByte;
bool dma_active; bool dma_active;
bool hdma_active; bool hdma_active;
uint8_t mode; uint8_t mode;
bool fixed; bool fixed;
bool decrement; bool decrement;
bool indirect; // hdma bool indirect; // hdma
bool from_b; bool from_b;
bool unusedBit; bool unusedBit;
bool do_transfer; // hdma bool do_transfer; // hdma
bool terminated; // hdma bool terminated; // hdma
} DmaChannel; } DmaChannel;
typedef struct CpuCallbacks { typedef struct CpuCallbacks {
std::function<uint8_t(uint32_t)> read_byte; std::function<uint8_t(uint32_t)> read_byte = nullptr;
std::function<void(uint32_t, uint8_t)> write_byte; std::function<void(uint32_t, uint8_t)> write_byte = nullptr;
std::function<void(bool waiting)> idle; std::function<void(bool waiting)> idle = nullptr;
} CpuCallbacks; } CpuCallbacks;
constexpr uint32_t kROMStart = 0x008000; constexpr uint32_t kROMStart = 0x008000;
@@ -62,7 +62,7 @@ constexpr uint32_t kRAMSize = 0x20000;
* @brief Memory interface * @brief Memory interface
*/ */
class Memory { class Memory {
public: public:
virtual ~Memory() = default; virtual ~Memory() = default;
virtual uint8_t ReadByte(uint32_t address) const = 0; virtual uint8_t ReadByte(uint32_t address) const = 0;
virtual uint16_t ReadWord(uint32_t address) const = 0; virtual uint16_t ReadWord(uint32_t address) const = 0;
@@ -116,25 +116,25 @@ public:
* *
*/ */
class MemoryImpl : public Memory { class MemoryImpl : public Memory {
public: public:
void Initialize(const std::vector<uint8_t> &romData, bool verbose = false); void Initialize(const std::vector<uint8_t> &romData, bool verbose = false);
uint16_t GetHeaderOffset() { uint16_t GetHeaderOffset() {
uint16_t offset; uint16_t offset;
switch (memory_[(0x00 << 16) + 0xFFD5] & 0x07) { switch (memory_[(0x00 << 16) + 0xFFD5] & 0x07) {
case 0: // LoROM case 0: // LoROM
offset = 0x7FC0; offset = 0x7FC0;
break; break;
case 1: // HiROM case 1: // HiROM
offset = 0xFFC0; offset = 0xFFC0;
break; break;
case 5: // ExHiROM case 5: // ExHiROM
offset = 0x40; offset = 0x40;
break; break;
default: default:
throw std::invalid_argument( throw std::invalid_argument(
"Unable to locate supported ROM mapping mode in the provided ROM " "Unable to locate supported ROM mapping mode in the provided ROM "
"file. Please try another ROM file."); "file. Please try another ROM file.");
} }
return offset; return offset;
@@ -285,7 +285,7 @@ public:
std::vector<uint8_t> rom_; std::vector<uint8_t> rom_;
std::vector<uint8_t> ram_; std::vector<uint8_t> ram_;
private: private:
uint32_t GetMappedAddress(uint32_t address) const; uint32_t GetMappedAddress(uint32_t address) const;
bool verbose_ = false; bool verbose_ = false;
@@ -323,7 +323,7 @@ private:
std::vector<uint8_t> memory_; std::vector<uint8_t> memory_;
}; };
} // namespace emu } // namespace emu
} // namespace yaze } // namespace yaze
#endif // YAZE_APP_EMU_MEMORY_H #endif // YAZE_APP_EMU_MEMORY_H

View File

@@ -57,7 +57,7 @@ class Snes {
auto cpu() -> Cpu& { return cpu_; } auto cpu() -> Cpu& { return cpu_; }
auto ppu() -> Ppu& { return ppu_; } auto ppu() -> Ppu& { return ppu_; }
auto apu() -> Apu& { return apu_; } auto apu() -> Apu& { return apu_; }
auto Memory() -> MemoryImpl& { return memory_; } auto memory() -> MemoryImpl& { return memory_; }
auto get_ram() -> uint8_t* { return ram; } auto get_ram() -> uint8_t* { return ram; }
auto mutable_cycles() -> uint64_t& { return cycles_; } auto mutable_cycles() -> uint64_t& { return cycles_; }

View File

@@ -1,5 +1,7 @@
#include "app/zelda3/dungeon/object_renderer.h" #include "app/zelda3/dungeon/object_renderer.h"
#include "app/gfx/arena.h"
namespace yaze { namespace yaze {
namespace zelda3 { namespace zelda3 {
@@ -8,8 +10,7 @@ void DungeonObjectRenderer::LoadObject(uint32_t routine_ptr,
vram_.sheets = sheet_ids; vram_.sheets = sheet_ids;
rom_data_ = rom()->vector(); rom_data_ = rom()->vector();
// Prepare the CPU and memory environment snes_.memory().Initialize(rom_data_);
memory_.Initialize(rom_data_);
// Configure the object based on the fetched information // Configure the object based on the fetched information
ConfigureObject(); ConfigureObject();
@@ -19,16 +20,16 @@ void DungeonObjectRenderer::LoadObject(uint32_t routine_ptr,
} }
void DungeonObjectRenderer::ConfigureObject() { void DungeonObjectRenderer::ConfigureObject() {
cpu.A = 0x03D8; snes_.cpu().A = 0x03D8;
cpu.X = 0x03D8; snes_.cpu().X = 0x03D8;
cpu.DB = 0x7E; snes_.cpu().DB = 0x7E;
// VRAM target destinations // VRAM target destinations
cpu.WriteLong(0xBF, 0x7E2000); snes_.cpu().WriteLong(0xBF, 0x7E2000);
cpu.WriteLong(0xCB, 0x7E2080); snes_.cpu().WriteLong(0xCB, 0x7E2080);
cpu.WriteLong(0xC2, 0x7E2002); snes_.cpu().WriteLong(0xC2, 0x7E2002);
cpu.WriteLong(0xCE, 0x7E2082); snes_.cpu().WriteLong(0xCE, 0x7E2082);
cpu.SetAccumulatorSize(false); snes_.cpu().SetAccumulatorSize(false);
cpu.SetIndexSize(false); snes_.cpu().SetIndexSize(false);
} }
/** /**
@@ -59,15 +60,15 @@ void DungeonObjectRenderer::ConfigureObject() {
#_0198AD: RTS #_0198AD: RTS
*/ */
void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) { void DungeonObjectRenderer::RenderObject(uint32_t routine_ptr) {
cpu.PB = 0x01; snes_.cpu().PB = 0x01;
cpu.PC = routine_ptr; snes_.cpu().PC = routine_ptr;
// Set up initial state for object drawing // Set up initial state for object drawing
cpu.Y = 0; // Start at the beginning of the tilemap snes_.cpu().Y = 0; // Start at the beginning of the tilemap
cpu.D = 0x7E; // Direct page register for memory access snes_.cpu().D = 0x7E; // Direct page register for memory access
// Push return address to stack // 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 // Set up a maximum instruction count to prevent infinite loops
const int MAX_INSTRUCTIONS = 10000; 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 // Execute instructions until we hit a return instruction or max count
while (instruction_count < MAX_INSTRUCTIONS) { 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 // Check for RTS (Return from Subroutine) instruction
if (opcode == 0x60) { if (opcode == 0x60) {
// Execute the RTS instruction // Execute the RTS instruction
cpu.ExecuteInstruction(opcode); snes_.cpu().ExecuteInstruction(opcode);
break; // Exit the loop after RTS break; // Exit the loop after RTS
} }
// Execute the instruction // Execute the instruction
cpu.ExecuteInstruction(opcode); snes_.cpu().ExecuteInstruction(opcode);
instruction_count++; instruction_count++;
} }
@@ -108,7 +110,7 @@ void DungeonObjectRenderer::UpdateObjectBitmap() {
// Iterate over tilemap in memory to read tile IDs // Iterate over tilemap in memory to read tile IDs
for (int tile_index = 0; tile_index < 512; tile_index++) { for (int tile_index = 0; tile_index < 512; tile_index++) {
// Read the tile ID from memory // 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) // Skip empty tiles (0x0000)
if (tile_id == 0) continue; if (tile_id == 0) continue;
@@ -128,8 +130,8 @@ void DungeonObjectRenderer::UpdateObjectBitmap() {
int tile_y = (tile_index / 32) * 8; int tile_y = (tile_index / 32) * 8;
// Get the graphics sheet // Get the graphics sheet
auto& sheet = GraphicsSheetManager::GetInstance().mutable_gfx_sheets()->at( auto& sheet =
vram_.sheets[sheet_number]); gfx::Arena::Get().mutable_gfx_sheets()->at(vram_.sheets[sheet_number]);
// Calculate the offset in the tilemap // Calculate the offset in the tilemap
int tilemap_offset = tile_y * 256 + tile_x; 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 // Copy the tile from the graphics sheet to the tilemap
sheet.Get8x8Tile(tile_id % 32, 0, 0, tilemap_, tilemap_offset); 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 } // namespace zelda3

View File

@@ -1,9 +1,7 @@
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include "app/emu/cpu/cpu.h" #include "app/emu/snes.h"
#include "app/emu/memory/memory.h"
#include "app/emu/video/ppu.h"
#include "app/gfx/bitmap.h" #include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h" #include "app/gfx/snes_palette.h"
#include "app/rom.h" #include "app/rom.h"
@@ -19,7 +17,7 @@ namespace zelda3 {
* dungeon objects in Link to the Past. * dungeon objects in Link to the Past.
*/ */
struct PseudoVram { struct PseudoVram {
std::array<uint8_t, 16> sheets = { 0 }; std::array<uint8_t, 16> sheets = {0};
std::vector<gfx::SnesPalette> palettes; std::vector<gfx::SnesPalette> palettes;
}; };
@@ -31,7 +29,7 @@ struct PseudoVram {
* drawing routines for dungeon objects. It captures the tile data * drawing routines for dungeon objects. It captures the tile data
* written to memory and renders it to a bitmap. * written to memory and renders it to a bitmap.
*/ */
class DungeonObjectRenderer : public SharedRom { class DungeonObjectRenderer {
public: public:
DungeonObjectRenderer() = default; DungeonObjectRenderer() = default;
@@ -60,34 +58,9 @@ class DungeonObjectRenderer : public SharedRom {
*/ */
void UpdateObjectBitmap(); void UpdateObjectBitmap();
/** auto mutable_memory() { return &tilemap_; }
* @brief Sets the palette for the rendered object auto rom() { return rom_; }
* auto mutable_rom() { return &rom_; }
* @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_; }
private: private:
std::vector<uint8_t> tilemap_; std::vector<uint8_t> tilemap_;
@@ -95,11 +68,8 @@ class DungeonObjectRenderer : public SharedRom {
PseudoVram vram_; PseudoVram vram_;
emu::MemoryImpl memory_; Rom* rom_;
emu::CpuCallbacks cpu_callbacks_; emu::Snes snes_;
emu::Ppu ppu{memory_};
emu::Cpu cpu{memory_, cpu_callbacks_};
gfx::Bitmap bitmap_; gfx::Bitmap bitmap_;
}; };