diff --git a/CMakeLists.txt b/CMakeLists.txt index c59e851..adf73a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,9 @@ add_executable(nes examples/sdl/SdlKeyboardController.cpp examples/sdl/SdlKeyboardController.h src/Dma.cpp src/Dma.h src/Oam.cpp - src/Oam.h) + src/Oam.h + src/Mapper/Mapper1.cpp + src/Mapper/Mapper1.h) find_package(SDL2 CONFIG REQUIRED) find_package(fmt REQUIRED) diff --git a/examples/sdl/main.cpp b/examples/sdl/main.cpp index 63b0164..2ccabf9 100644 --- a/examples/sdl/main.cpp +++ b/examples/sdl/main.cpp @@ -11,7 +11,7 @@ int main() { nes::System device; nes::SdlWindow window(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT); - window.setSize(nes::Ppu::SCREEN_WIDTH * 4, nes::Ppu::SCREEN_HEIGHT * 4); + window.setSize(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT); SDL_Event e; bool frameRendered = false; @@ -22,7 +22,7 @@ int main() { }); device.connect(std::make_shared()); //device.insertCartridge("/home/selim/Downloads/dk.nes"); - device.insertCartridge("/Users/selim/Documents/ff.nes"); + device.insertCartridge("/Users/selim/Documents/smb.nes"); //device.insertCartridge("C:\\Users\\selim\\Documents\\nestest.nes"); auto frameStart = std::chrono::steady_clock::now(); diff --git a/src/Cartridge.cpp b/src/Cartridge.cpp index 9fd5209..2132ce5 100644 --- a/src/Cartridge.cpp +++ b/src/Cartridge.cpp @@ -3,9 +3,12 @@ // #include "Cartridge.h" +#include "Mapper/Mapper0.h" +#include "Mapper/Mapper1.h" #include #include #include +#include namespace nes { @@ -21,13 +24,28 @@ namespace nes { // when it become available _header = reinterpret_cast(_romData.get()); + if (memcmp(_header->magic, ROM_MAGIC, sizeof(ROM_MAGIC)) != 0) { + throw std::runtime_error("Wrong ROM magic"); + } + const size_t prgSize = _header->prgChunks*PRG_CHUNK_SIZE; const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE; - _prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize); - _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); + _prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize); + _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); - _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + switch (_header->flags.mapper) { + case 0: + _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + case 1: + _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + default: + _mapper = std::make_unique(_header->prgChunks, _header->chrChunks); + } + + if (const auto ramSize = _mapper->ramSize(); ramSize > 0) { + _ram = std::make_unique(ramSize); + } } uint8_t Cartridge::readPrg(uint16_t address) { @@ -40,6 +58,11 @@ namespace nes { return _chrRom[mappedAddress]; } + uint8_t Cartridge::readRam(uint16_t address) { + uint32_t mappedAddress = _mapper->mapRam(address); + return _ram[mappedAddress]; + } + Cartridge::Mirroring Cartridge::mirroring() const { return _header->flags.mirroring; } diff --git a/src/Cartridge.h b/src/Cartridge.h index 5ab4acb..1b0f11c 100644 --- a/src/Cartridge.h +++ b/src/Cartridge.h @@ -5,7 +5,7 @@ #ifndef NES_CARTRIDGE_H #define NES_CARTRIDGE_H -#include "Mapper/Mapper0.h" +#include "Mapper/Mapper.h" #include #include @@ -15,13 +15,13 @@ namespace nes { namespace fs = std::filesystem; - constexpr uint32_t ROM_MAGIC = 0x4E45531A; + constexpr uint8_t ROM_MAGIC[4] = { 'N', 'E', 'S', 0x1a }; constexpr size_t PRG_CHUNK_SIZE = 16*1024; constexpr size_t CHR_CHUNK_SIZE = 8*1024; class Cartridge { public: - enum Mirroring { + enum Mirroring: uint8_t { Horizontal = 0, Vertical = 1 }; @@ -35,7 +35,7 @@ namespace nes { }; struct RomHeader { - uint32_t magic; + uint8_t magic[4]; uint8_t prgChunks; uint8_t chrChunks; Flags flags; @@ -48,6 +48,7 @@ namespace nes { public: uint8_t readPrg(uint16_t address); uint8_t readChr(uint16_t address); + uint8_t readRam(uint16_t address); Mirroring mirroring() const; private: @@ -56,6 +57,7 @@ namespace nes { RomHeader* _header; std::span _prgRom; std::span _chrRom; + std::unique_ptr _ram; }; } diff --git a/src/Mapper/Mapper.h b/src/Mapper/Mapper.h index 7443235..4471833 100644 --- a/src/Mapper/Mapper.h +++ b/src/Mapper/Mapper.h @@ -6,6 +6,7 @@ #define NES_MAPPER_H #include +#include namespace nes { @@ -17,6 +18,8 @@ namespace nes { public: virtual uint32_t mapPrg(uint16_t address) = 0; virtual uint32_t mapChr(uint16_t address) = 0; + virtual uint32_t mapRam(uint16_t address) = 0; + virtual size_t ramSize() = 0; protected: uint8_t _prgBanks; diff --git a/src/Mapper/Mapper0.cpp b/src/Mapper/Mapper0.cpp index 09b16f3..88a6481 100644 --- a/src/Mapper/Mapper0.cpp +++ b/src/Mapper/Mapper0.cpp @@ -13,4 +13,12 @@ namespace nes { uint32_t Mapper0::mapChr(uint16_t address) { return address; } -} \ No newline at end of file + + uint32_t Mapper0::mapRam(uint16_t address) { + return address; + } + + size_t Mapper0::ramSize() { + return 0; + } +} diff --git a/src/Mapper/Mapper0.h b/src/Mapper/Mapper0.h index c9d234f..2a40d45 100644 --- a/src/Mapper/Mapper0.h +++ b/src/Mapper/Mapper0.h @@ -17,6 +17,8 @@ namespace nes { public: uint32_t mapPrg(uint16_t address) override; uint32_t mapChr(uint16_t address) override; + uint32_t mapRam(uint16_t address) override; + size_t ramSize() override; }; } diff --git a/src/Mapper/Mapper1.cpp b/src/Mapper/Mapper1.cpp new file mode 100644 index 0000000..57c5e1c --- /dev/null +++ b/src/Mapper/Mapper1.cpp @@ -0,0 +1,31 @@ +// +// Created by Selim Mustafaev on 27.06.2025. +// + +#include "Mapper1.h" + +namespace nes { + + uint32_t Mapper1::mapPrg(uint16_t address) { + + if(_controlReg.prgBankMode == FixFirst16 || _controlReg.prgBankMode == FixLast16) { + + } else { + + } + + return address; + } + + uint32_t Mapper1::mapChr(uint16_t address) { + return address; + } + + uint32_t Mapper1::mapRam(uint16_t address) { + return address & 0x1FFF; + } + + size_t Mapper1::ramSize() { + return 32*1024; + } +} diff --git a/src/Mapper/Mapper1.h b/src/Mapper/Mapper1.h new file mode 100644 index 0000000..cb4b2f9 --- /dev/null +++ b/src/Mapper/Mapper1.h @@ -0,0 +1,53 @@ +// +// Created by Selim Mustafaev on 27.06.2025. +// + +#ifndef MAPPER1_H +#define MAPPER1_H + +#include "Mapper.h" + +namespace nes { + + class Mapper1 final : public Mapper { + public: + enum PrgBankMode: uint8_t { + FixFirst32 = 0, + FixLast32 = 1, + FixFirst16 = 2, + FixLast16 = 3, + }; + + enum ChrBankMode: uint8_t { + Single8 = 0, + Double4 = 1 + }; + + union ControlRegister { + struct { + uint8_t reserved: 2; + PrgBankMode prgBankMode: 2; + ChrBankMode chrBankMode: 1; + }; + uint8_t value; + }; + + public: + using Mapper::Mapper; + ~Mapper1() override = default; + + public: + uint32_t mapPrg(uint16_t address) override; + uint32_t mapChr(uint16_t address) override; + uint32_t mapRam(uint16_t address) override; + size_t ramSize() override; + + private: + ControlRegister _controlReg = {}; + uint8_t _shiftReg = 0; + uint8_t _writes = 0; + }; + +} + +#endif //MAPPER1_H diff --git a/src/System.cpp b/src/System.cpp index af691c7..0687bb0 100644 --- a/src/System.cpp +++ b/src/System.cpp @@ -92,6 +92,9 @@ namespace nes { else if(address >= 0x2000 && address < 0x4000) { return _ppu->read(address & 0x0007); } + else if (address >= 0x6000 && address < 0x8000) { + return _cartridge->readRam(address); + } else if(address >= 0x8000) { return _cartridge->readPrg(address); }