diff --git a/.gitignore b/.gitignore index 478cd55..4d02245 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build/ cmake-build-debug/ .idea/ src/.vscode/ -src/dmg_boot.gb \ No newline at end of file +src/dmg_boot.gb +tests/* \ No newline at end of file diff --git a/src/audio.cpp b/src/audio.cpp index 9a95636..f19e4e9 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -17,6 +17,8 @@ APU::APU() volumeLeft = 0; volumeRight = 0; + mMap = nullptr; + channel1 = new PulseChannel(CH1); channel2 = new PulseChannel(CH2); channel3 = new WaveChannel(); @@ -50,6 +52,26 @@ bool APU::init() return true; } +// Set MemoryMap pointer +void APU::setMemoryMap(MemoryMap* mMap) +{ + this->mMap = mMap; + // initialize Handlers + initializeReadWriteHandlers(); +} + +// Initializes the read-write handlers of MemoryMap +void APU::initializeReadWriteHandlers() +{ + if (!mMap) + { + throw std::runtime_error("MemoryMap not set in APU"); + return; + } + + mMap->setAudioReadHandler([this](Word address) { return this->readByte(address); }); + mMap->setAudioWriteHandler([this](Word address, Byte value) { this->writeByte(address, value); }); +} void APU::test() { printf("APU test\n"); @@ -728,4 +750,4 @@ void NoiseChannel::trigger() { LFSR = 0x7FFF; enabled = dacEnabled; -} \ No newline at end of file +} diff --git a/src/audio.h b/src/audio.h index 1255f41..dd956e2 100644 --- a/src/audio.h +++ b/src/audio.h @@ -2,6 +2,8 @@ #include "types.h" #include #include +#include +#include "mmap.h" enum Channel { @@ -15,9 +17,9 @@ class PulseChannel { private: Channel channel; - bool enabled; - bool dacEnabled; - int frameSequencer; + bool enabled; + bool dacEnabled; + int frameSequencer; Byte sweepPeriod; bool sweepNegate; @@ -26,7 +28,7 @@ class PulseChannel // NRx1 Byte waveDuty; int lengthTimer; - int maxLengthTimer = 64; + int maxLengthTimer = 64; Byte envelopeInitialVolume; bool envelopeIncrease; @@ -41,25 +43,24 @@ class PulseChannel void test(); void writeByte(Word address, Byte value); Byte readByte(Word address); - bool isEnabled(); - void powerOff(); - void run(); - void set_NRx4(Byte value); - void setFrameSequencer(int frameSequencer); - void trigger(); + bool isEnabled(); + void powerOff(); + void run(); + void set_NRx4(Byte value); + void setFrameSequencer(int frameSequencer); + void trigger(); }; class WaveChannel { private: - Byte waveRAM[16]; bool dacEnabled; bool enabled; int lengthTimer; int maxLengthTimer = 256; - int frameSequencer; + int frameSequencer; Byte outputLevel; @@ -73,22 +74,22 @@ class WaveChannel void writeByte(Word address, Byte value); Byte readByte(Word address); void trigger(); - bool isEnabled(); - void powerOff(); - void set_NRx4(Byte value); - void run(); - void setFrameSequencer(int frameSequencer); + bool isEnabled(); + void powerOff(); + void set_NRx4(Byte value); + void run(); + void setFrameSequencer(int frameSequencer); }; class NoiseChannel { private: - bool enabled; - bool dacEnabled; + bool enabled; + bool dacEnabled; int lengthTimer; int maxLengthTimer = 64; - int frameSequencer; + int frameSequencer; Byte envelopeInitialVolume; bool envelopeIncrease; @@ -112,11 +113,11 @@ class NoiseChannel void writeByte(Word address, Byte value); Byte readByte(Word address); void trigger(); - bool isEnabled(); - void powerOff(); - void set_NRx4(Byte value); - void run(); - void setFrameSequencer(int frameSequencer); + bool isEnabled(); + void powerOff(); + void set_NRx4(Byte value); + void run(); + void setFrameSequencer(int frameSequencer); }; class APU @@ -160,12 +161,17 @@ class APU WaveChannel* channel3; NoiseChannel* channel4; + // Pointer to MemoryMap + MemoryMap* mMap; + public: APU(); + void setMemoryMap(MemoryMap* mMap); void test(); - bool init(); + bool init(); void writeByte(Word address, Byte value); Byte readByte(Word address); void stepAPU(int cycles); void clearRegisters(); + void initializeReadWriteHandlers(); }; \ No newline at end of file diff --git a/src/gameBoy.cpp b/src/gameBoy.cpp index 9f25b3e..0af8569 100644 --- a/src/gameBoy.cpp +++ b/src/gameBoy.cpp @@ -16,7 +16,7 @@ GBE::GBE() gbe_graphics = new PPU(); // audio = new Audio(); - // APU = new APU(); + gbe_audio = new APU(); // Unify the CPU and MemoryMap gbe_cpu->setMemory(gbe_mMap); @@ -24,9 +24,12 @@ GBE::GBE() // Unify the CPU and PPU gbe_cpu->setPPU(gbe_graphics); - // Unify the PPU and MmeoryMap + // Unify the PPU and MemoryMap gbe_graphics->setMemoryMap(gbe_mMap); + // Unify the APU and MemoryMap + gbe_audio->setMemoryMap(gbe_mMap); + gbe_graphics->init(); // Open the Boot ROM @@ -38,7 +41,7 @@ GBE::GBE() // printf("game rom file not opened"); // Open the Game ROM - if ((gameROM = fopen("../tests/dmg_sound/rom_singles/03-trigger.gb", "rb")) == NULL) + if ((gameROM = fopen("../tests/dmg_sound/rom_singles/02-len ctr.gb", "rb")) == NULL) printf("game rom file not opened"); // Set the Boot ROM @@ -122,7 +125,7 @@ void GBE::update() // update the DIV and TIMA timers gbe_cpu->updateTimers(s_Cycles); gbe_graphics->executePPU(s_Cycles); - gbe_mMap->audio->stepAPU(s_Cycles); + gbe_audio->stepAPU(s_Cycles); s_Cycles = 0; s_Cycles += gbe_cpu->performInterrupt(); gbe_graphics->pollEvents(); diff --git a/src/gameBoy.h b/src/gameBoy.h index 35c22f8..20ce470 100644 --- a/src/gameBoy.h +++ b/src/gameBoy.h @@ -3,6 +3,7 @@ #include "cpu.h" #include "mmap.h" #include "graphics.h" +#include "audio.h" // GBE stands for GameBoyEmulator @@ -34,6 +35,9 @@ class GBE // File pointer for game ROM FILE* gameROM; + // Pointer to Audio + APU* gbe_audio; + // Update function of the GBE // Will be called every frame // GB has 59.73 frames per second diff --git a/src/mmap.cpp b/src/mmap.cpp index 6d71288..5b085e0 100644 --- a/src/mmap.cpp +++ b/src/mmap.cpp @@ -104,11 +104,24 @@ MemoryMap::MemoryMap() bootRomFile = nullptr; romFile = nullptr; - audio = new APU(); - mbcMode = 0x0; } +// MemoryMap Destructor +MemoryMap::~MemoryMap() +{ + delete romBank0; + delete romBank1; + delete videoRam; + delete externalRam; + delete workRam; + delete oamTable; + delete ioPorts; + delete highRam; + delete interruptEnableRegister; + delete joyPadState; +} + // Write to memory // TODO: Make emulation memory secure bool MemoryMap::writeMemory(Word address, Byte value) @@ -176,8 +189,8 @@ bool MemoryMap::writeMemory(Word address, Byte value) } // Write to Audio Registers else if (address >= 0xFF10 && address <= 0xFF3F) - { - audio->writeByte(address, value); + { + audioWriteHandler(address, value); } else ioPorts[address - 0xFF00] = value; @@ -254,7 +267,7 @@ Byte MemoryMap::readMemory(Word address) // Read from Audio Registers if (address >= 0xFF10 && address <= 0xFF3F) { - return audio->readByte(address); + return audioReadHandler(address); } // Read from I/O Ports else diff --git a/src/mmap.h b/src/mmap.h index 7756c80..ddcc4b3 100644 --- a/src/mmap.h +++ b/src/mmap.h @@ -1,7 +1,7 @@ #pragma once #include "types.h" #include -#include "audio.h" +#include // The Memory Map for GBE // Pulled from https://gbdev.io/pandocs/Memory_Map.html @@ -138,12 +138,12 @@ class MemoryMap // Stays in the I/O Ports at 0xFF4B Byte* reg_WX; + // Write to audio are being handled by this without passing the APU pointer to MemoryMap + std::function audioWriteHandler; + // Read from audio handled by this + std::function audioReadHandler; + public: - // Audio Unit - // I know this is not the best way to do it - // But I am not sure how to do it better - APU* audio; - Byte* joyPadState; // Constructor MemoryMap(); @@ -270,4 +270,10 @@ class MemoryMap // sets the ROM file void setRomFile(FILE* file) { romFile = file; } + + // set audioWriteHandler + void setAudioWriteHandler(const std::function& function) { audioWriteHandler = function; } + + // set audioReadHandler + void setAudioReadHandler(const std::function& function) { audioReadHandler = function; } }; \ No newline at end of file