Adding some logic for MMC1 mapper

This commit is contained in:
Selim Mustafaev 2025-07-01 16:46:07 +03:00
parent f07633a69e
commit e6e8c71794
10 changed files with 138 additions and 11 deletions

View File

@ -28,7 +28,9 @@ add_executable(nes
examples/sdl/SdlKeyboardController.cpp examples/sdl/SdlKeyboardController.cpp
examples/sdl/SdlKeyboardController.h src/Dma.cpp src/Dma.h examples/sdl/SdlKeyboardController.h src/Dma.cpp src/Dma.h
src/Oam.cpp src/Oam.cpp
src/Oam.h) src/Oam.h
src/Mapper/Mapper1.cpp
src/Mapper/Mapper1.h)
find_package(SDL2 CONFIG REQUIRED) find_package(SDL2 CONFIG REQUIRED)
find_package(fmt REQUIRED) find_package(fmt REQUIRED)

View File

@ -11,7 +11,7 @@ int main() {
nes::System device; nes::System device;
nes::SdlWindow window(nes::Ppu::SCREEN_WIDTH, nes::Ppu::SCREEN_HEIGHT); 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; SDL_Event e;
bool frameRendered = false; bool frameRendered = false;
@ -22,7 +22,7 @@ int main() {
}); });
device.connect(std::make_shared<SdlKeyboardController>()); device.connect(std::make_shared<SdlKeyboardController>());
//device.insertCartridge("/home/selim/Downloads/dk.nes"); //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"); //device.insertCartridge("C:\\Users\\selim\\Documents\\nestest.nes");
auto frameStart = std::chrono::steady_clock::now(); auto frameStart = std::chrono::steady_clock::now();

View File

@ -3,9 +3,12 @@
// //
#include "Cartridge.h" #include "Cartridge.h"
#include "Mapper/Mapper0.h"
#include "Mapper/Mapper1.h"
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <cstddef>
namespace nes { namespace nes {
@ -21,13 +24,28 @@ namespace nes {
// when it become available // when it become available
_header = reinterpret_cast<RomHeader*>(_romData.get()); _header = reinterpret_cast<RomHeader*>(_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 prgSize = _header->prgChunks*PRG_CHUNK_SIZE;
const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE; const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE;
_prgRom = std::span<uint8_t>(_romData.get() + sizeof(RomHeader), prgSize); _prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize);
_chrRom = std::span<uint8_t>(_romData.get() + sizeof(RomHeader) + prgSize, chrSize); _chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize);
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks); switch (_header->flags.mapper) {
case 0:
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks);
case 1:
_mapper = std::make_unique<Mapper1>(_header->prgChunks, _header->chrChunks);
default:
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks);
}
if (const auto ramSize = _mapper->ramSize(); ramSize > 0) {
_ram = std::make_unique<uint8_t[]>(ramSize);
}
} }
uint8_t Cartridge::readPrg(uint16_t address) { uint8_t Cartridge::readPrg(uint16_t address) {
@ -40,6 +58,11 @@ namespace nes {
return _chrRom[mappedAddress]; return _chrRom[mappedAddress];
} }
uint8_t Cartridge::readRam(uint16_t address) {
uint32_t mappedAddress = _mapper->mapRam(address);
return _ram[mappedAddress];
}
Cartridge::Mirroring Cartridge::mirroring() const { Cartridge::Mirroring Cartridge::mirroring() const {
return _header->flags.mirroring; return _header->flags.mirroring;
} }

View File

@ -5,7 +5,7 @@
#ifndef NES_CARTRIDGE_H #ifndef NES_CARTRIDGE_H
#define NES_CARTRIDGE_H #define NES_CARTRIDGE_H
#include "Mapper/Mapper0.h" #include "Mapper/Mapper.h"
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
@ -15,13 +15,13 @@ namespace nes {
namespace fs = std::filesystem; 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 PRG_CHUNK_SIZE = 16*1024;
constexpr size_t CHR_CHUNK_SIZE = 8*1024; constexpr size_t CHR_CHUNK_SIZE = 8*1024;
class Cartridge { class Cartridge {
public: public:
enum Mirroring { enum Mirroring: uint8_t {
Horizontal = 0, Horizontal = 0,
Vertical = 1 Vertical = 1
}; };
@ -35,7 +35,7 @@ namespace nes {
}; };
struct RomHeader { struct RomHeader {
uint32_t magic; uint8_t magic[4];
uint8_t prgChunks; uint8_t prgChunks;
uint8_t chrChunks; uint8_t chrChunks;
Flags flags; Flags flags;
@ -48,6 +48,7 @@ namespace nes {
public: public:
uint8_t readPrg(uint16_t address); uint8_t readPrg(uint16_t address);
uint8_t readChr(uint16_t address); uint8_t readChr(uint16_t address);
uint8_t readRam(uint16_t address);
Mirroring mirroring() const; Mirroring mirroring() const;
private: private:
@ -56,6 +57,7 @@ namespace nes {
RomHeader* _header; RomHeader* _header;
std::span<uint8_t> _prgRom; std::span<uint8_t> _prgRom;
std::span<uint8_t> _chrRom; std::span<uint8_t> _chrRom;
std::unique_ptr<uint8_t[]> _ram;
}; };
} }

View File

@ -6,6 +6,7 @@
#define NES_MAPPER_H #define NES_MAPPER_H
#include <cstdint> #include <cstdint>
#include <cstddef>
namespace nes { namespace nes {
@ -17,6 +18,8 @@ namespace nes {
public: public:
virtual uint32_t mapPrg(uint16_t address) = 0; virtual uint32_t mapPrg(uint16_t address) = 0;
virtual uint32_t mapChr(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: protected:
uint8_t _prgBanks; uint8_t _prgBanks;

View File

@ -13,4 +13,12 @@ namespace nes {
uint32_t Mapper0::mapChr(uint16_t address) { uint32_t Mapper0::mapChr(uint16_t address) {
return address; return address;
} }
}
uint32_t Mapper0::mapRam(uint16_t address) {
return address;
}
size_t Mapper0::ramSize() {
return 0;
}
}

View File

@ -17,6 +17,8 @@ namespace nes {
public: public:
uint32_t mapPrg(uint16_t address) override; uint32_t mapPrg(uint16_t address) override;
uint32_t mapChr(uint16_t address) override; uint32_t mapChr(uint16_t address) override;
uint32_t mapRam(uint16_t address) override;
size_t ramSize() override;
}; };
} }

31
src/Mapper/Mapper1.cpp Normal file
View File

@ -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;
}
}

53
src/Mapper/Mapper1.h Normal file
View File

@ -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

View File

@ -92,6 +92,9 @@ namespace nes {
else if(address >= 0x2000 && address < 0x4000) { else if(address >= 0x2000 && address < 0x4000) {
return _ppu->read(address & 0x0007); return _ppu->read(address & 0x0007);
} }
else if (address >= 0x6000 && address < 0x8000) {
return _cartridge->readRam(address);
}
else if(address >= 0x8000) { else if(address >= 0x8000) {
return _cartridge->readPrg(address); return _cartridge->readPrg(address);
} }