diff --git a/CMakeLists.txt b/CMakeLists.txt index d63be30..3a19583 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,8 @@ target_compile_options(unit_tests PUBLIC -fprofile-arcs -ftest-coverage) # make app add_executable(ComSquare main.cpp + sources/SNES.cpp + sources/SNES.hpp sources/Memory/MemoryBus.cpp sources/Memory/MemoryBus.hpp sources/Memory/IMemory.hpp @@ -29,11 +31,17 @@ add_executable(ComSquare sources/PPU/PPU.hpp sources/CPU/CPU.cpp sources/CPU/CPU.hpp - sources/SNES.cpp - sources/SNES.hpp sources/Cartridge/Cartridge.cpp sources/Cartridge/Cartridge.hpp sources/Exceptions/NotImplementedException.hpp sources/APU/APU.hpp sources/APU/APU.cpp - sources/Exceptions/InvalidAddress.hpp sources/Exceptions/InvalidRom.hpp sources/Models/Ints.hpp sources/Models/Ints.hpp sources/Ram/Ram.cpp sources/Ram/Ram.hpp sources/Memory/MemoryShadow.cpp sources/Memory/MemoryShadow.hpp) + sources/Exceptions/InvalidAddress.hpp + sources/Exceptions/InvalidRom.hpp + sources/Models/Ints.hpp + sources/Models/Ints.hpp + sources/Ram/Ram.cpp + sources/Ram/Ram.hpp + sources/Memory/MemoryShadow.cpp + sources/Memory/MemoryShadow.hpp +) diff --git a/main.cpp b/main.cpp index 7544138..b93334c 100644 --- a/main.cpp +++ b/main.cpp @@ -14,5 +14,6 @@ int main(int argc, char **argv) } ComSquare::MemoryBus bus; ComSquare::SNES snes(std::make_shared(bus), argv[1]); + bus.mapComponents(snes); return 0; } \ No newline at end of file diff --git a/sources/APU/APU.cpp b/sources/APU/APU.cpp index 50b8750..3a2066e 100644 --- a/sources/APU/APU.cpp +++ b/sources/APU/APU.cpp @@ -8,9 +8,9 @@ namespace ComSquare::APU { - APU::APU(std::shared_ptr dsp) - : _dsp(std::move(dsp)) - { } + APU::APU() + { + } uint8_t APU::read(uint24_t addr) { diff --git a/sources/APU/APU.hpp b/sources/APU/APU.hpp index 0fa7f48..8088257 100644 --- a/sources/APU/APU.hpp +++ b/sources/APU/APU.hpp @@ -105,12 +105,12 @@ namespace ComSquare::APU class DSP { }; - class APU : IMemory { + class APU : public IMemory { private: Registers _registers; InternalRegisters _internalRegisters{}; public: - explicit APU(std::shared_ptr dsp); + explicit APU(); //! @brief The DSP component used to produce sound std::shared_ptr _dsp; diff --git a/sources/CPU/CPU.hpp b/sources/CPU/CPU.hpp index 51b9b53..f399295 100644 --- a/sources/CPU/CPU.hpp +++ b/sources/CPU/CPU.hpp @@ -174,12 +174,12 @@ namespace ComSquare::CPU }; //! @brief The main CPU - class CPU : IMemory { + class CPU : public IMemory { private: //! @brief All the registers of the CPU Registers _registers{}; //! @brief Is the CPU running in emulation mode (in 8bits) - bool _isEmulationMode{}; + bool _isEmulationMode = true; //! @brief Internal registers of the CPU (accessible from the bus via addr $4200 to $421F). InternalRegisters _internalRegisters{}; //! @brief The memory bus to use for read/write. diff --git a/sources/Memory/MemoryBus.cpp b/sources/Memory/MemoryBus.cpp index bf9f1dc..837146d 100644 --- a/sources/Memory/MemoryBus.cpp +++ b/sources/Memory/MemoryBus.cpp @@ -5,6 +5,8 @@ #include #include #include "MemoryBus.hpp" +#include "../SNES.hpp" +#include "MemoryShadow.hpp" namespace ComSquare { @@ -42,4 +44,37 @@ namespace ComSquare } handler->write(addr - handler->getStart(), data); } + + void MemoryBus::_mirrorComponents(SNES &console, int i) + { + this->_memoryAccessors.push_back(Memory::MemoryShadow::createShadow(console.wram, i, i + 0x2000)); + this->_memoryAccessors.push_back(Memory::MemoryShadow::createShadow(console.cpu, i + 0x4200, i + 0x4220)); + } + + void MemoryBus::mapComponents(SNES &console) + { + // The WRam is always mapped in the bank 7E and 7F, no matter the memory mapping mode. + console.wram->setMemoryRegion(0x7E0000, 0x7FFFFF); + this->_memoryAccessors.push_back(console.wram); + + console.ppu->setMemoryRegion(0x2100, 0x2140); + this->_memoryAccessors.push_back(console.ppu); + + console.apu->setMemoryRegion(0x2140, 0x2144); + this->_memoryAccessors.push_back(console.apu); + + console.cpu->setMemoryRegion(0x4200, 0x4220); + this->_memoryAccessors.push_back(console.cpu); + + // TODO implement DMA & HDMA (4220 to 4300) + + //Mirror the first $2000 bits of the WRam to all banks of the Q1 & Q3. + for (uint24_t i = 0; i < 0x400000; i += 0x10000) { + this->_mirrorComponents(console, i); + } + for (uint24_t i = 0x800000; i < 0xC00000; i += 0x10000) { + this->_memoryAccessors.push_back(Memory::MemoryShadow::createShadow(console.wram, i, i + 0x2000)); + this->_memoryAccessors.push_back(Memory::MemoryShadow::createShadow(console.cpu, i + 0x4200, i + 0x4220)); + } + } } \ No newline at end of file diff --git a/sources/Memory/MemoryBus.hpp b/sources/Memory/MemoryBus.hpp index fa5b097..3ad6c01 100644 --- a/sources/Memory/MemoryBus.hpp +++ b/sources/Memory/MemoryBus.hpp @@ -23,6 +23,10 @@ namespace ComSquare std::shared_ptr getAccessor(uint24_t addr); //! @brief The last value read via the memory bus. uint8_t _openbus = 0; + //! @brief Mirror components to other banks. (Used by the mapComponents method). + //! @param console All the components. + //! @param i Base address for the mirrors. + inline void _mirrorComponents(struct SNES &console, int i); public: //! @brief Read data at a global address. //! @param addr The address to read from. @@ -32,6 +36,9 @@ namespace ComSquare //! @param addr The address to write to. //! @param data The data to write. void write(uint24_t addr, uint8_t data); + //! @brief Map components to the address space using the currently loaded cartridge to set the right mapping mode. + //! @param console All the components. + void mapComponents(struct SNES &console); }; } diff --git a/sources/Memory/MemoryShadow.cpp b/sources/Memory/MemoryShadow.cpp index b98e513..c9c14d0 100644 --- a/sources/Memory/MemoryShadow.cpp +++ b/sources/Memory/MemoryShadow.cpp @@ -8,9 +8,16 @@ namespace ComSquare::Memory { - MemoryShadow::MemoryShadow(std::shared_ptr initial) + MemoryShadow::MemoryShadow(std::shared_ptr initial, uint24_t start, uint24_t end) : _initial(std::move(initial)) - { } + { + this->setMemoryRegion(start, end); + } + + std::shared_ptr MemoryShadow::createShadow(std::shared_ptr initial, uint24_t start, uint24_t end) + { + return static_cast>(new MemoryShadow(std::move(initial), start, end)); + } uint8_t MemoryShadow::read(uint24_t addr) { diff --git a/sources/Memory/MemoryShadow.hpp b/sources/Memory/MemoryShadow.hpp index 8bfb284..e848a3e 100644 --- a/sources/Memory/MemoryShadow.hpp +++ b/sources/Memory/MemoryShadow.hpp @@ -10,13 +10,15 @@ namespace ComSquare::Memory { - class MemoryShadow : IMemory { + class MemoryShadow : public IMemory { private: //! @brief Memory to shadow from. std::shared_ptr _initial; public: //! @brief Create a shadow for the memory given as parameter. - explicit MemoryShadow(std::shared_ptr initial); + explicit MemoryShadow(std::shared_ptr initial, uint24_t start, uint24_t end); + + static std::shared_ptr createShadow(std::shared_ptr initial, uint24_t start, uint24_t end); //! @brief Read from the initial IMemory given. //! @param addr The address to read from. The address 0x0 should refer to the first byte of the initial IMemory. //! @throw InvalidAddress will be thrown if the address is more than the size of the initial IMemory. diff --git a/sources/Ram/Ram.hpp b/sources/Ram/Ram.hpp index 93b2b43..98b73cf 100644 --- a/sources/Ram/Ram.hpp +++ b/sources/Ram/Ram.hpp @@ -9,7 +9,7 @@ namespace ComSquare::Ram { - class Ram : IMemory { + class Ram : public IMemory { private: //! @brief The ram. (Can be used for WRam, SRam, VRam etc) uint8_t *_data; diff --git a/sources/SNES.cpp b/sources/SNES.cpp index 9f15064..022a1aa 100644 --- a/sources/SNES.cpp +++ b/sources/SNES.cpp @@ -6,7 +6,11 @@ namespace ComSquare { - SNES::SNES(const std::shared_ptr &bus, const std::string &romPath) - : _cpu(bus), _cartridge(romPath) + SNES::SNES(const std::shared_ptr &bus, const std::string &romPath) : + cpu(new CPU::CPU(bus)), + ppu(new PPU::PPU()), + apu(new APU::APU()), + cartridge(new Cartridge::Cartridge(romPath)), + wram(new Ram::Ram(16384)) { } } diff --git a/sources/SNES.hpp b/sources/SNES.hpp index 946b604..8adc2f1 100644 --- a/sources/SNES.hpp +++ b/sources/SNES.hpp @@ -8,15 +8,20 @@ #include "Memory/MemoryBus.hpp" #include "CPU/CPU.hpp" #include "Cartridge/Cartridge.hpp" +#include "Ram/Ram.hpp" +#include "PPU/PPU.hpp" +#include "APU/APU.hpp" namespace ComSquare { //! @brief Container of all the components of the SNES. - class SNES { - private: - CPU::CPU _cpu; - Cartridge::Cartridge _cartridge; + struct SNES { public: + std::shared_ptr cpu; + std::shared_ptr ppu; + std::shared_ptr apu; + std::shared_ptr cartridge; + std::shared_ptr wram; //! @brief Create all the components using a common memory bus for all of them. SNES(const std::shared_ptr &bus, const std::string &ramPath); };