From f653517026912f937cc1321c082ccb8e66dd8a96 Mon Sep 17 00:00:00 2001 From: scawful Date: Sat, 13 Apr 2024 23:56:41 -0500 Subject: [PATCH] add memory namespace, update comments --- src/app/emu/audio/apu.h | 28 ++++++++++---- src/app/emu/audio/dsp.h | 29 +++++++-------- src/app/emu/audio/spc700.h | 17 +++++++++ src/app/emu/cpu/cpu.h | 4 +- src/app/emu/memory/dma.cc | 11 ++++-- src/app/emu/memory/dma.h | 8 ++-- src/app/emu/memory/memory.cc | 2 + src/app/emu/memory/memory.h | 47 +++++++++++++++++++----- src/app/emu/memory/mock_memory.h | 37 ++++++++++++++++--- src/app/emu/snes.cc | 12 +++--- src/app/emu/snes.h | 8 ++-- src/app/emu/video/ppu.h | 3 +- src/app/zelda3/dungeon/object_renderer.h | 2 +- test/emu/cpu_test.cc | 6 +-- 14 files changed, 153 insertions(+), 61 deletions(-) diff --git a/src/app/emu/audio/apu.h b/src/app/emu/audio/apu.h index f656aa4f..adea4552 100644 --- a/src/app/emu/audio/apu.h +++ b/src/app/emu/audio/apu.h @@ -15,12 +15,32 @@ namespace app { namespace emu { namespace audio { +using namespace memory; + /** * + + * + */ + +const int kApuClockSpeed = 1024000; // 1.024 MHz +const int apuSampleRate = 32000; // 32 KHz +const int apuClocksPerSample = 64; // 64 clocks per sample + +/** + * @class Apu + * @brief The Apu class represents the Audio Processing Unit (APU) of a system. + * + * The Apu class is responsible for generating audio samples and managing the + * APU state. It interacts with the Memory, AudioRam, and Clock classes to + * read/write data and update the clock. The class also implements the Observer + * interface to receive notifications from the system. + * + * @par IPL ROM Info * 64 kilobytes of RAM are mapped across the 16-bit memory space of the SPC-700. * Some regions of this space are overlaid with special hardware functions. * - * Range Note + * @par Range Note * $0000-00EF Zero Page RAM * $00F0-00FF Sound CPU Registers * $0100-01FF Stack Page RAM @@ -31,13 +51,7 @@ namespace audio { * underlying RAM can always be written to, and the high bit of the Control * register $F1 can be cleared to unmap the IPL ROM and allow read access to * this RAM. - * */ - -const int kApuClockSpeed = 1024000; // 1.024 MHz -const int apuSampleRate = 32000; // 32 KHz -const int apuClocksPerSample = 64; // 64 clocks per sample - class Apu : public Observer { public: Apu(MemoryImpl &memory, AudioRam &aram, Clock &clock) diff --git a/src/app/emu/audio/dsp.h b/src/app/emu/audio/dsp.h index 37e23707..21dbfe7c 100644 --- a/src/app/emu/audio/dsp.h +++ b/src/app/emu/audio/dsp.h @@ -16,7 +16,6 @@ using SampleFetcher = std::function; using SamplePusher = std::function; /** - * * The S-DSP is a digital signal processor generating the sound data. * * A DSP register can be selected with $F2, after which it can be read or @@ -35,21 +34,19 @@ using SamplePusher = std::function; * There are 8 voices, numbered 0 to 7. * Each voice X has 10 registers in the range $X0-$X9. * - * Name Address Bits Notes - * VOL (L) $X0 SVVV VVVV Left channel volume, signed. - * VOL (R) $X1 SVVV VVVV Right channel volume, signed. - * P (L) $X2 LLLL LLLL Low 8 bits of sample pitch. - * P (H) $X3 --HH HHHH High 6 bits of sample pitch. - * SCRN $X4 SSSS SSSS Selects a sample source entry from the - * directory ADSR (1) $X5 EDDD AAAA ADSR enable (E), decay rate (D), - * attack rate (A). - * ADSR (2) $X6 SSSR RRRR Sustain level (S), release rate (R). - * GAIN $X7 0VVV VVVV 1MMV VVVV Mode (M), value (V). - * ENVX $X8 0VVV VVVV Reads current 7-bit value of ADSR/GAIN - * envelope. - * OUTX $X9 SVVV VVVV Reads signed 8-bit value of current - * sample wave multiplied by ENVX, before applying VOL. - * + * | Name | Address | Bits | Notes | + * |---------|---------|-----------|--------------------------------------------------------| + * | VOL (L) | $X0 | SVVV VVVV | Left channel volume, signed. | + * | VOL (R) | $X1 | SVVV VVVV | Right channel volume, signed. | + * | P (L) | $X2 | LLLL LLLL | Low 8 bits of sample pitch. | + * | P (H) | $X3 | --HH HHHH | High 6 bits of sample pitch. | + * | SCRN | $X4 | SSSS SSSS | Selects a sample source entry from the directory. | + * | ADSR (1)| $X5 | EDDD AAAA | ADSR enable (E), decay rate (D), attack rate (A). | + * | ADSR (2)| $X6 | SSSR RRRR | Sustain level (S), release rate (R). | + * | GAIN | $X7 | 0VVV VVVV 1MMV VVVV | Mode (M), value (V). | + * | ENVX | $X8 | 0VVV VVVV | Reads current 7-bit value of ADSR/GAIN envelope. | + * | OUTX | $X9 | SVVV VVVV | Reads signed 8-bit value of current sample wave | + * | | | | multiplied by ENVX, before applying VOL. | */ class DigitalSignalProcessor { diff --git a/src/app/emu/audio/spc700.h b/src/app/emu/audio/spc700.h index 8733de69..8a8d7b88 100644 --- a/src/app/emu/audio/spc700.h +++ b/src/app/emu/audio/spc700.h @@ -11,6 +11,9 @@ namespace app { namespace emu { namespace audio { +/** + * @brief AudioRam is an interface for the Audio RAM used by the SPC700. + */ class AudioRam { public: virtual ~AudioRam() = default; @@ -20,6 +23,9 @@ class AudioRam { virtual void write(uint16_t address, uint8_t value) = 0; }; +/** + * @brief AudioRamImpl is an implementation of the AudioRam interface. + */ class AudioRamImpl : public AudioRam { static const int ARAM_SIZE = 0x10000; std::vector ram = std::vector(ARAM_SIZE, 0); @@ -41,6 +47,17 @@ class AudioRamImpl : public AudioRam { } }; +/** + * @class Spc700 + * @brief The Spc700 class represents the SPC700 processor. + * + * The Spc700 class provides the functionality to execute instructions, read and + * write memory, and handle various addressing modes. It also contains registers + * and flags specific to the SPC700. + * + * @note This class assumes the existence of an `AudioRam` object for memory + * access. + */ class Spc700 { private: AudioRam& aram_; diff --git a/src/app/emu/cpu/cpu.h b/src/app/emu/cpu/cpu.h index 13723888..d5af069e 100644 --- a/src/app/emu/cpu/cpu.h +++ b/src/app/emu/cpu/cpu.h @@ -38,7 +38,9 @@ class InstructionEntry { const int kCpuClockSpeed = 21477272; // 21.477272 MHz -class Cpu : public Memory, public Loggable, public core::ExperimentFlags { +class Cpu : public memory::Memory, + public Loggable, + public core::ExperimentFlags { public: explicit Cpu(Memory& mem, Clock& vclock) : memory(mem), clock(vclock) {} enum class UpdateMode { Run, Step, Pause }; diff --git a/src/app/emu/memory/dma.cc b/src/app/emu/memory/dma.cc index 1f334f46..bd5c8b68 100644 --- a/src/app/emu/memory/dma.cc +++ b/src/app/emu/memory/dma.cc @@ -5,8 +5,9 @@ namespace yaze { namespace app { namespace emu { +namespace memory { -void DMA::StartDMATransfer(uint8_t channelMask) { +void DirectMemoryAccess::StartDMATransfer(uint8_t channelMask) { for (int i = 0; i < 8; ++i) { if ((channelMask & (1 << i)) != 0) { Channel& ch = channels[i]; @@ -20,8 +21,9 @@ void DMA::StartDMATransfer(uint8_t channelMask) { // Determine the transfer size based on the DMAPn register bool transferTwoBytes = (ch.DMAPn & 0x40) != 0; - // Perform the DMA transfer based on the channel parameters - std::cout << "Starting DMA transfer for channel " << i << std::endl; + // Perform the DirectMemoryAccess transfer based on the channel parameters + std::cout << "Starting DirectMemoryAccess transfer for channel " << i + << std::endl; for (uint16_t j = 0; j < ch.DASn; ++j) { // Read a byte or two bytes from memory based on the transfer size @@ -46,7 +48,7 @@ void DMA::StartDMATransfer(uint8_t channelMask) { MDMAEN = channelMask; // Set the MDMAEN register to the channel mask } -void DMA::EnableHDMATransfers(uint8_t channelMask) { +void DirectMemoryAccess::EnableHDMATransfers(uint8_t channelMask) { for (int i = 0; i < 8; ++i) { if ((channelMask & (1 << i)) != 0) { Channel& ch = channels[i]; @@ -70,6 +72,7 @@ void DMA::EnableHDMATransfers(uint8_t channelMask) { HDMAEN = channelMask; // Set the HDMAEN register to the channel mask } +} // namespace memory } // namespace emu } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/memory/dma.h b/src/app/emu/memory/dma.h index 2566898d..e200faaf 100644 --- a/src/app/emu/memory/dma.h +++ b/src/app/emu/memory/dma.h @@ -6,11 +6,11 @@ namespace yaze { namespace app { namespace emu { +namespace memory { -// Direct Memory Address -class DMA { +class DirectMemoryAccess { public: - DMA() { + DirectMemoryAccess() { // Initialize DMA and HDMA channels for (int i = 0; i < 8; ++i) { channels[i].DMAPn = 0; @@ -52,6 +52,8 @@ class DMA { uint8_t MDMAEN = 0; // Start DMA transfer uint8_t HDMAEN = 0; // Enable HDMA transfers }; + +} // namespace memory } // namespace emu } // namespace app } // namespace yaze diff --git a/src/app/emu/memory/memory.cc b/src/app/emu/memory/memory.cc index fa2cbb6b..a92dd7b6 100644 --- a/src/app/emu/memory/memory.cc +++ b/src/app/emu/memory/memory.cc @@ -12,6 +12,7 @@ namespace yaze { namespace app { namespace emu { +namespace memory { void DrawSnesMemoryMapping(const MemoryImpl& memory) { // Using those as a base value to create width/height that are factor of the @@ -77,6 +78,7 @@ void DrawSnesMemoryMapping(const MemoryImpl& memory) { } } +} // namespace memory } // namespace emu } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/memory/memory.h b/src/app/emu/memory/memory.h index ed91a36f..9a2c79f3 100644 --- a/src/app/emu/memory/memory.h +++ b/src/app/emu/memory/memory.h @@ -28,12 +28,13 @@ namespace yaze { namespace app { namespace emu { +namespace memory { -enum ROMSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 }; +enum RomSpeed { SLOW_ROM = 0x00, FAST_ROM = 0x07 }; enum BankSize { LOW_ROM = 0x00, HI_ROM = 0x01 }; -enum ROMType { +enum RomType { ROM_DEFAULT = 0x00, ROM_RAM = 0x01, ROM_SRAM = 0x02, @@ -43,7 +44,7 @@ enum ROMType { FX = 0x06 }; -enum ROMSize { +enum RomSize { SIZE_2_MBIT = 0x08, SIZE_4_MBIT = 0x09, SIZE_8_MBIT = 0x0A, @@ -51,7 +52,7 @@ enum ROMSize { SIZE_32_MBIT = 0x0C }; -enum SRAMSize { +enum SramSize { NO_SRAM = 0x00, SRAM_16_KBIT = 0x01, SRAM_32_KBIT = 0x02, @@ -73,14 +74,14 @@ enum License { // ... and other licenses }; -class ROMInfo { +class RomInfo { public: std::string title; - ROMSpeed romSpeed; + RomSpeed romSpeed; BankSize bankSize; - ROMType romType; - ROMSize romSize; - SRAMSize sramSize; + RomType romType; + RomSize romSize; + SramSize sramSize; CountryCode countryCode; License license; uint8_t version; @@ -105,7 +106,9 @@ constexpr uint32_t kVRAMSize = 0x10000; constexpr uint32_t kOAMStart = 0x218000; constexpr uint32_t kOAMSize = 0x220; -// memory.h +/** + * @brief Memory interface + */ class Memory { public: virtual ~Memory() = default; @@ -137,6 +140,29 @@ class Memory { enum class MemoryMapping { SNES_LOROM = 0, PC_ADDRESS = 1 }; +/** + * @class MemoryImpl + * @brief Implementation of the Memory interface for emulating memory in a SNES + * system. + * + * The MemoryImpl class provides methods for initializing and accessing memory + * in a SNES system. It implements the Memory interface and inherits from the + * Loggable class. + * + * The class supports different memory mappings, including LoROM and PC_ADDRESS + * mappings. It provides methods for reading and writing bytes, words, and longs + * from/to memory. It also supports stack operations for pushing and popping + * values. + * + * The class maintains separate vectors for ROM, RAM, VRAM, and OAM memory + * regions. It provides methods for accessing these memory regions and + * retrieving their sizes. + * + * The class also allows adding observers to be notified when memory is read or + * written. + * + * @note This class assumes a 16-bit address space. + */ class MemoryImpl : public Memory, public Loggable { public: void Initialize(const std::vector& romData, bool verbose = false, @@ -396,6 +422,7 @@ class MemoryImpl : public Memory, public Loggable { void DrawSnesMemoryMapping(const MemoryImpl& memory); +} // namespace memory } // namespace emu } // namespace app } // namespace yaze diff --git a/src/app/emu/memory/mock_memory.h b/src/app/emu/memory/mock_memory.h index c025ba2d..6a3f68f6 100644 --- a/src/app/emu/memory/mock_memory.h +++ b/src/app/emu/memory/mock_memory.h @@ -8,10 +8,14 @@ #include "app/emu/cpu/cpu.h" #include "app/emu/memory/memory.h" -using yaze::app::emu::Clock; -using yaze::app::emu::Cpu; -using yaze::app::emu::Memory; +namespace yaze { +namespace app { +namespace emu { +namespace memory { +/** + * @brief Mock CPU class for testing + */ class MockClock : public Clock { public: MOCK_METHOD(void, UpdateClock, (double delta), (override)); @@ -21,9 +25,23 @@ class MockClock : public Clock { MOCK_METHOD(float, GetFrequency, (), (const, override)); }; -// 0x1000000 is 16 MB, simplifying the memory layout for testing -// 2 MB is = 0x200000 - +/** + * @class MockMemory + * @brief A mock implementation of the Memory class. + * + * This class is used for testing purposes and provides a mock implementation of + * the Memory class. It allows for reading and writing bytes, words, and longs + * to memory, as well as pushing and popping values onto and from the stack. It + * also provides methods for setting and getting the stack pointer, initializing + * memory with ROM data, and clearing memory. + * + * The mock memory is represented by a vector of uint8_t values, where each + * element represents a byte of memory. The memory can be accessed using the [] + * operator, and its contents can be set using the SetMemoryContents() method. + * + * @note This class is intended for testing purposes only and should not be used + * in production code. + */ class MockMemory : public Memory { public: MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address)); @@ -85,6 +103,8 @@ class MockMemory : public Memory { } } + // 16MB = 0x1000000 + // 02MB = 0x200000 void Initialize(const std::vector& romData) { // 16 MB, simplifying the memory layout for testing memory_.resize(0x1000000); @@ -186,4 +206,9 @@ class MockMemory : public Memory { uint16_t SP_ = 0x01FF; }; +} // namespace memory +} // namespace emu +} // namespace app +} // namespace yaze + #endif // YAZE_TEST_MOCK_MOCK_MEMORY_H \ No newline at end of file diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index b54bfa07..5e747a2c 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -57,8 +57,8 @@ void audio_callback(void* userdata, uint8_t* stream, int len) { } // namespace -ROMInfo SNES::ReadRomHeader(uint32_t offset) { - ROMInfo romInfo; +RomInfo SNES::ReadRomHeader(uint32_t offset) { + RomInfo romInfo; // Read cartridge title char title[22]; @@ -70,17 +70,17 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) { // Read ROM speed and memory map mode uint8_t romSpeedAndMapMode = cpu_.ReadByte(offset + 0x15); - romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07); + romInfo.romSpeed = (RomSpeed)(romSpeedAndMapMode & 0x07); romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01); // Read ROM type - romInfo.romType = (ROMType)cpu_.ReadByte(offset + 0x16); + romInfo.romType = (RomType)cpu_.ReadByte(offset + 0x16); // Read ROM size - romInfo.romSize = (ROMSize)cpu_.ReadByte(offset + 0x17); + romInfo.romSize = (RomSize)cpu_.ReadByte(offset + 0x17); // Read RAM size - romInfo.sramSize = (SRAMSize)cpu_.ReadByte(offset + 0x18); + romInfo.sramSize = (SramSize)cpu_.ReadByte(offset + 0x18); // Read country code romInfo.countryCode = (CountryCode)cpu_.ReadByte(offset + 0x19); diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index c7dafd2d..74ff2b68 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -20,12 +20,14 @@ namespace yaze { namespace app { namespace emu { -class SNES : public DMA { +using namespace memory; + +class SNES : public DirectMemoryAccess { public: SNES() = default; ~SNES() = default; - ROMInfo ReadRomHeader(uint32_t offset); + RomInfo ReadRomHeader(uint32_t offset); // Initialization void Init(ROM& rom); @@ -95,7 +97,7 @@ class SNES : public DMA { audio::Apu apu_{memory_, audio_ram_, clock_}; // Helper classes - ROMInfo rom_info_; + RomInfo rom_info_; Debugger debugger; // Currently loaded ROM diff --git a/src/app/emu/video/ppu.h b/src/app/emu/video/ppu.h index f10ccac0..55956a18 100644 --- a/src/app/emu/video/ppu.h +++ b/src/app/emu/video/ppu.h @@ -16,6 +16,7 @@ namespace emu { namespace video { using namespace PpuRegisters; +using namespace memory; class PpuInterface { public: @@ -267,7 +268,7 @@ const int kPpuClockSpeed = 5369318; // 5.369318 MHz class Ppu : public Observer, public SharedROM { public: // Initializes the PPU with the necessary resources and dependencies - Ppu(Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {} + Ppu(memory::Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {} // Initialize the frame buffer void Init() { diff --git a/src/app/zelda3/dungeon/object_renderer.h b/src/app/zelda3/dungeon/object_renderer.h index 79146050..e469658b 100644 --- a/src/app/zelda3/dungeon/object_renderer.h +++ b/src/app/zelda3/dungeon/object_renderer.h @@ -46,7 +46,7 @@ class DungeonObjectRenderer : public SharedROM { std::vector tilemap_; uint16_t pc_with_rts_; std::vector rom_data_; - emu::MemoryImpl memory_; + emu::memory::MemoryImpl memory_; emu::ClockImpl clock_; emu::Cpu cpu{memory_, clock_}; emu::video::Ppu ppu{memory_, clock_}; diff --git a/test/emu/cpu_test.cc b/test/emu/cpu_test.cc index 041e51d0..d7c49759 100644 --- a/test/emu/cpu_test.cc +++ b/test/emu/cpu_test.cc @@ -26,8 +26,8 @@ class CpuTest : public ::testing::Test { } AsmParser asm_parser; - MockMemory mock_memory; - MockClock mock_clock; + memory::MockMemory mock_memory; + memory::MockClock mock_clock; Cpu cpu{mock_memory, mock_clock}; }; @@ -39,7 +39,7 @@ using ::testing::Return; // ============================================================================ TEST_F(CpuTest, CheckMemoryContents) { - MockMemory memory; + memory::MockMemory memory; std::vector data = {0x00, 0x01, 0x02, 0x03, 0x04}; memory.SetMemoryContents(data);