MMC1 mapper
This commit is contained in:
parent
c0ac8bfb58
commit
f114104e8e
@ -32,7 +32,8 @@ add_executable(nes
|
|||||||
src/Mapper/Mapper1.cpp
|
src/Mapper/Mapper1.cpp
|
||||||
src/Mapper/Mapper1.h
|
src/Mapper/Mapper1.h
|
||||||
src/Bus.cpp
|
src/Bus.cpp
|
||||||
src/Bus.h)
|
src/Bus.h
|
||||||
|
src/Utils.h)
|
||||||
|
|
||||||
find_package(SDL2 CONFIG REQUIRED)
|
find_package(SDL2 CONFIG REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
|
|||||||
@ -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, nes::Ppu::SCREEN_HEIGHT);
|
window.setSize(nes::Ppu::SCREEN_WIDTH*2, nes::Ppu::SCREEN_HEIGHT*2);
|
||||||
|
|
||||||
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/smb.nes");
|
device.insertCartridge("/Users/selim/Documents/drm.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();
|
||||||
|
|||||||
25
src/Bus.cpp
25
src/Bus.cpp
@ -5,6 +5,8 @@
|
|||||||
#include "Bus.h"
|
#include "Bus.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "../../../../../../opt/homebrew/Cellar/fmt/11.2.0/include/fmt/format.h"
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
|
||||||
Bus::Bus(uint8_t *ram,
|
Bus::Bus(uint8_t *ram,
|
||||||
@ -53,12 +55,6 @@ 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) {
|
|
||||||
return _cartridge->readPrg(address);
|
|
||||||
}
|
|
||||||
else if(address == 0x4016) {
|
else if(address == 0x4016) {
|
||||||
return _controller1->read();
|
return _controller1->read();
|
||||||
}
|
}
|
||||||
@ -67,6 +63,12 @@ namespace nes {
|
|||||||
return _controller2->read();
|
return _controller2->read();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (address >= 0x6000 && address < 0x8000) {
|
||||||
|
return _cartridge->readRam(address);
|
||||||
|
}
|
||||||
|
else if(address >= 0x8000) {
|
||||||
|
return _cartridge->readPrg(address);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -78,9 +80,6 @@ namespace nes {
|
|||||||
else if(address >= 0x2000 && address < 0x4000) {
|
else if(address >= 0x2000 && address < 0x4000) {
|
||||||
_ppu->write(address & 0x0007, value);
|
_ppu->write(address & 0x0007, value);
|
||||||
}
|
}
|
||||||
else if(address >= 0x8000) {
|
|
||||||
std::cout << "Cartridge (PRG) write at address: " << address << std::endl;
|
|
||||||
}
|
|
||||||
else if(address == 0x4014) {
|
else if(address == 0x4014) {
|
||||||
_dma->start(value);
|
_dma->start(value);
|
||||||
}
|
}
|
||||||
@ -92,6 +91,14 @@ namespace nes {
|
|||||||
_controller2->poll();
|
_controller2->poll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (address >= 0x6000 && address < 0x8000) {
|
||||||
|
_cartridge->writeRam(address, value);
|
||||||
|
}
|
||||||
|
else if(address >= 0x8000) {
|
||||||
|
_cartridge->writePrg(address, value);
|
||||||
|
} else {
|
||||||
|
//fmt::println("unknown bus write at address: {:#06x}, value: {}", address, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -19,9 +19,6 @@ namespace nes {
|
|||||||
std::ifstream rom(path, std::ios::binary);
|
std::ifstream rom(path, std::ios::binary);
|
||||||
rom.read(reinterpret_cast<char*>(_romData.get()), static_cast<std::streamsize>(romSize));
|
rom.read(reinterpret_cast<char*>(_romData.get()), static_cast<std::streamsize>(romSize));
|
||||||
|
|
||||||
// UB here
|
|
||||||
// Should be std::start_lifetime_as<RomHeader>(_romData);
|
|
||||||
// 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) {
|
if (memcmp(_header->magic, ROM_MAGIC, sizeof(ROM_MAGIC)) != 0) {
|
||||||
@ -32,15 +29,25 @@ namespace nes {
|
|||||||
const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE;
|
const size_t chrSize = _header->chrChunks*CHR_CHUNK_SIZE;
|
||||||
|
|
||||||
_prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize);
|
_prgRom = std::span(_romData.get() + sizeof(RomHeader), prgSize);
|
||||||
_chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize);
|
|
||||||
|
|
||||||
switch (_header->flags.mapper) {
|
if (chrSize == 0) {
|
||||||
|
_chrRam = std::make_unique<uint8_t[]>(CHR_CHUNK_SIZE);
|
||||||
|
_chrRom = std::span(_chrRam.get(), CHR_CHUNK_SIZE);
|
||||||
|
} else {
|
||||||
|
_chrRom = std::span(_romData.get() + sizeof(RomHeader) + prgSize, chrSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t mapper = _header->flags.mapper | (_header->flags2.upperMapper << 4);
|
||||||
|
|
||||||
|
switch (mapper) {
|
||||||
case 0:
|
case 0:
|
||||||
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks);
|
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks);
|
||||||
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
_mapper = std::make_unique<Mapper1>(_header->prgChunks, _header->chrChunks);
|
_mapper = std::make_unique<Mapper1>(_header->prgChunks, _header->chrChunks);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
_mapper = std::make_unique<Mapper0>(_header->prgChunks, _header->chrChunks);
|
throw std::runtime_error("unknown mapper");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto ramSize = _mapper->ramSize(); ramSize > 0) {
|
if (const auto ramSize = _mapper->ramSize(); ramSize > 0) {
|
||||||
@ -53,16 +60,29 @@ namespace nes {
|
|||||||
return _prgRom[mappedAddress];
|
return _prgRom[mappedAddress];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cartridge::writePrg(uint16_t address, uint8_t value) {
|
||||||
|
_mapper->write(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t Cartridge::readChr(uint16_t address) {
|
uint8_t Cartridge::readChr(uint16_t address) {
|
||||||
uint32_t mappedAddress = _mapper->mapChr(address);
|
uint32_t mappedAddress = _mapper->mapChr(address);
|
||||||
return _chrRom[mappedAddress];
|
return _chrRom[mappedAddress];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cartridge::writeChr(uint16_t address, uint8_t value) {
|
||||||
|
_chrRom[address] = value;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t Cartridge::readRam(uint16_t address) {
|
uint8_t Cartridge::readRam(uint16_t address) {
|
||||||
uint32_t mappedAddress = _mapper->mapRam(address);
|
uint32_t mappedAddress = _mapper->mapRam(address);
|
||||||
return _ram[mappedAddress];
|
return _ram[mappedAddress];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Cartridge::writeRam(uint16_t address, uint8_t value) {
|
||||||
|
uint32_t mappedAddress = _mapper->mapRam(address);
|
||||||
|
_ram[mappedAddress] = value;
|
||||||
|
}
|
||||||
|
|
||||||
Cartridge::Mirroring Cartridge::mirroring() const {
|
Cartridge::Mirroring Cartridge::mirroring() const {
|
||||||
return _header->flags.mirroring;
|
return _header->flags.mirroring;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,12 +34,20 @@ namespace nes {
|
|||||||
uint8_t mapper: 4;
|
uint8_t mapper: 4;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Flags2 {
|
||||||
|
uint8_t reserved: 2;
|
||||||
|
uint8_t version: 2;
|
||||||
|
uint8_t upperMapper: 4;
|
||||||
|
};
|
||||||
|
|
||||||
struct RomHeader {
|
struct RomHeader {
|
||||||
uint8_t magic[4];
|
uint8_t magic[4];
|
||||||
uint8_t prgChunks;
|
uint8_t prgChunks;
|
||||||
uint8_t chrChunks;
|
uint8_t chrChunks;
|
||||||
Flags flags;
|
Flags flags;
|
||||||
uint8_t reserved[9];
|
Flags2 flags2;
|
||||||
|
uint8_t prgRamSize;
|
||||||
|
uint8_t reserved[7];
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -47,8 +55,11 @@ namespace nes {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
uint8_t readPrg(uint16_t address);
|
uint8_t readPrg(uint16_t address);
|
||||||
|
void writePrg(uint16_t address, uint8_t value);
|
||||||
uint8_t readChr(uint16_t address);
|
uint8_t readChr(uint16_t address);
|
||||||
|
void writeChr(uint16_t address, uint8_t value);
|
||||||
uint8_t readRam(uint16_t address);
|
uint8_t readRam(uint16_t address);
|
||||||
|
void writeRam(uint16_t address, uint8_t value);
|
||||||
Mirroring mirroring() const;
|
Mirroring mirroring() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -58,6 +69,7 @@ namespace nes {
|
|||||||
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;
|
std::unique_ptr<uint8_t[]> _ram;
|
||||||
|
std::unique_ptr<uint8_t[]> _chrRam;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ namespace nes {
|
|||||||
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 uint32_t mapRam(uint16_t address) = 0;
|
||||||
virtual size_t ramSize() = 0;
|
virtual size_t ramSize() = 0;
|
||||||
|
virtual void write(uint16_t address, uint8_t value) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t _prgBanks;
|
uint8_t _prgBanks;
|
||||||
|
|||||||
@ -21,4 +21,8 @@ namespace nes {
|
|||||||
size_t Mapper0::ramSize() {
|
size_t Mapper0::ramSize() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mapper0::write(uint16_t address, uint8_t value) {
|
||||||
|
// not used
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,7 @@ namespace nes {
|
|||||||
uint32_t mapChr(uint16_t address) override;
|
uint32_t mapChr(uint16_t address) override;
|
||||||
uint32_t mapRam(uint16_t address) override;
|
uint32_t mapRam(uint16_t address) override;
|
||||||
size_t ramSize() override;
|
size_t ramSize() override;
|
||||||
|
void write(uint16_t address, uint8_t value) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,25 +2,52 @@
|
|||||||
// Created by Selim Mustafaev on 27.06.2025.
|
// Created by Selim Mustafaev on 27.06.2025.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "../Utils.h"
|
||||||
#include "Mapper1.h"
|
#include "Mapper1.h"
|
||||||
|
|
||||||
namespace nes {
|
namespace nes {
|
||||||
|
Mapper1::Mapper1(uint8_t prgBanks, uint8_t chrBanks): Mapper(prgBanks, chrBanks) {
|
||||||
|
|
||||||
|
_controlReg.prgBankMode = FixLast16;
|
||||||
|
_controlReg.chrBankMode = Double4;
|
||||||
|
_prgBank16Hi = _prgBanks - 1;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Mapper1::mapPrg(uint16_t address) {
|
uint32_t Mapper1::mapPrg(uint16_t address) {
|
||||||
|
|
||||||
if(_controlReg.prgBankMode == FixFirst16 || _controlReg.prgBankMode == FixLast16) {
|
if(_controlReg.prgBankMode == FixFirst16 || _controlReg.prgBankMode == FixLast16) {
|
||||||
|
if (address >= PRG_FIRST_BANK && address < PRG_SECOND_BANK) {
|
||||||
|
return _prgBank16Lo*PRG_BANK16_SIZE + clampBits<14>(address);
|
||||||
|
} else if (address >= PRG_SECOND_BANK && address < PRG_END) {
|
||||||
|
return _prgBank16Hi*PRG_BANK16_SIZE + clampBits<14>(address);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
return _prgBank32*PRG_BANK32_SIZE + clampBits<15>(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t Mapper1::mapChr(uint16_t address) {
|
uint32_t Mapper1::mapChr(uint16_t address) {
|
||||||
|
|
||||||
|
if (_chrBanks == 0) {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (_controlReg.chrBankMode) {
|
||||||
|
case Single8:
|
||||||
|
return _chrBank8*CHR_BANK8_SIZE + clampBits<13>(address);
|
||||||
|
case Double4:
|
||||||
|
if (address >= CHR_FIRST_BANK && address < CHR_SECOND_BANK) {
|
||||||
|
return _chrBank4Lo*CHR_BANK4_SIZE + clampBits<12>(address);
|
||||||
|
} else if (address < CHR_END) {
|
||||||
|
return _chrBank4Hi*CHR_BANK4_SIZE + clampBits<12>(address);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t Mapper1::mapRam(uint16_t address) {
|
uint32_t Mapper1::mapRam(uint16_t address) {
|
||||||
return address & 0x1FFF;
|
return address & 0x1FFF;
|
||||||
}
|
}
|
||||||
@ -28,4 +55,60 @@ namespace nes {
|
|||||||
size_t Mapper1::ramSize() {
|
size_t Mapper1::ramSize() {
|
||||||
return 32*1024;
|
return 32*1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mapper1::write(uint16_t address, uint8_t value) {
|
||||||
|
|
||||||
|
// Reset shift register
|
||||||
|
if (bitIsSet<7>(value)) {
|
||||||
|
_shiftReg = 0;
|
||||||
|
_controlReg.prgBankMode = FixLast16;
|
||||||
|
_prgBank16Hi = _prgBanks - 1;
|
||||||
|
_writes = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_writes++;
|
||||||
|
_shiftReg = (_shiftReg >> 1) | ((value & 0x1) << 4);
|
||||||
|
|
||||||
|
if (_writes == 5) {
|
||||||
|
auto command = static_cast<Command>(getBits<13,2>(address));
|
||||||
|
switch (command) {
|
||||||
|
case SetControlRegister:
|
||||||
|
_controlReg.value = clampBits<5>(_shiftReg);
|
||||||
|
break;
|
||||||
|
case SetChrBankLo:
|
||||||
|
if (_controlReg.chrBankMode == Double4) {
|
||||||
|
_chrBank4Lo = clampBits<5>(_shiftReg);
|
||||||
|
} else {
|
||||||
|
_chrBank8 = maskBits<1,4>(_shiftReg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SetChrBankHi:
|
||||||
|
if (_controlReg.chrBankMode == Double4) {
|
||||||
|
_chrBank4Hi = clampBits<5>(_shiftReg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ConfigurePrgBanks:
|
||||||
|
switch (_controlReg.prgBankMode) {
|
||||||
|
case FixFirst32:
|
||||||
|
case FixLast32:
|
||||||
|
_prgBank32 = getBits<1,3>(_shiftReg);
|
||||||
|
break;
|
||||||
|
case FixFirst16:
|
||||||
|
_prgBank16Lo = 0;
|
||||||
|
_prgBank16Hi = clampBits<4>(_shiftReg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FixLast16:
|
||||||
|
_prgBank16Lo = clampBits<4>(_shiftReg);
|
||||||
|
_prgBank16Hi = _prgBanks - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_writes = 0;
|
||||||
|
_shiftReg = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,19 @@ namespace nes {
|
|||||||
|
|
||||||
class Mapper1 final : public Mapper {
|
class Mapper1 final : public Mapper {
|
||||||
public:
|
public:
|
||||||
|
const uint16_t PRG_FIRST_BANK = 0x8000;
|
||||||
|
const uint16_t PRG_SECOND_BANK = 0xBFFF;
|
||||||
|
const uint16_t PRG_END = 0xFFFF;
|
||||||
|
|
||||||
|
const uint16_t CHR_FIRST_BANK = 0x0000;
|
||||||
|
const uint16_t CHR_SECOND_BANK = 0x0FFF;
|
||||||
|
const uint16_t CHR_END = 0x1FFF;
|
||||||
|
|
||||||
|
const size_t PRG_BANK16_SIZE = 16*1024;
|
||||||
|
const size_t PRG_BANK32_SIZE = 32*1024;
|
||||||
|
const size_t CHR_BANK4_SIZE = 4*1024;
|
||||||
|
const size_t CHR_BANK8_SIZE = 8*1024;
|
||||||
|
|
||||||
enum PrgBankMode: uint8_t {
|
enum PrgBankMode: uint8_t {
|
||||||
FixFirst32 = 0,
|
FixFirst32 = 0,
|
||||||
FixLast32 = 1,
|
FixLast32 = 1,
|
||||||
@ -23,9 +36,23 @@ namespace nes {
|
|||||||
Double4 = 1
|
Double4 = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum Command: uint8_t {
|
||||||
|
SetControlRegister = 0,
|
||||||
|
SetChrBankLo = 1,
|
||||||
|
SetChrBankHi = 2,
|
||||||
|
ConfigurePrgBanks = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Mirroring: uint8_t {
|
||||||
|
OneScreenLowerBank = 0,
|
||||||
|
OneScreenUpperBank = 1,
|
||||||
|
Vertical = 2,
|
||||||
|
Horizontal = 3
|
||||||
|
};
|
||||||
|
|
||||||
union ControlRegister {
|
union ControlRegister {
|
||||||
struct {
|
struct {
|
||||||
uint8_t reserved: 2;
|
Mirroring mirroring: 2;
|
||||||
PrgBankMode prgBankMode: 2;
|
PrgBankMode prgBankMode: 2;
|
||||||
ChrBankMode chrBankMode: 1;
|
ChrBankMode chrBankMode: 1;
|
||||||
};
|
};
|
||||||
@ -34,6 +61,7 @@ namespace nes {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
using Mapper::Mapper;
|
using Mapper::Mapper;
|
||||||
|
Mapper1(uint8_t prgBanks, uint8_t chrBanks);
|
||||||
~Mapper1() override = default;
|
~Mapper1() override = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -41,11 +69,20 @@ namespace nes {
|
|||||||
uint32_t mapChr(uint16_t address) override;
|
uint32_t mapChr(uint16_t address) override;
|
||||||
uint32_t mapRam(uint16_t address) override;
|
uint32_t mapRam(uint16_t address) override;
|
||||||
size_t ramSize() override;
|
size_t ramSize() override;
|
||||||
|
void write(uint16_t address, uint8_t value) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ControlRegister _controlReg = {};
|
ControlRegister _controlReg = {};
|
||||||
uint8_t _shiftReg = 0;
|
uint8_t _shiftReg = 0;
|
||||||
uint8_t _writes = 0;
|
uint8_t _writes = 0;
|
||||||
|
|
||||||
|
uint8_t _chrBank4Lo = 0;
|
||||||
|
uint8_t _chrBank4Hi = 0;
|
||||||
|
uint8_t _chrBank8 = 0;
|
||||||
|
|
||||||
|
uint8_t _prgBank16Lo = 0;
|
||||||
|
uint8_t _prgBank16Hi = 0;
|
||||||
|
uint8_t _prgBank32 = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "Ppu.h"
|
#include "Ppu.h"
|
||||||
|
|
||||||
|
#include "../../../../../../opt/homebrew/Cellar/fmt/11.2.0/include/fmt/base.h"
|
||||||
|
|
||||||
#ifdef NES_LOGGING
|
#ifdef NES_LOGGING
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#endif
|
#endif
|
||||||
@ -228,6 +230,7 @@ namespace nes {
|
|||||||
_oam->write(value);
|
_oam->write(value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
fmt::println("unknown ppu write");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
src/Utils.h
Normal file
44
src/Utils.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
//
|
||||||
|
// Created by Selim Mustafaev on 02.07.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
|
||||||
|
namespace nes {
|
||||||
|
|
||||||
|
template<size_t pos, size_t len, std::integral T>
|
||||||
|
consteval T createBitMask() {
|
||||||
|
T hi = (1 << (pos + len)) - 1;
|
||||||
|
T lo = (1 << pos) - 1;
|
||||||
|
return hi - lo;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N, std::integral T>
|
||||||
|
bool bitIsSet(T value) {
|
||||||
|
return value & (1 << N);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t pos, size_t len, std::integral T>
|
||||||
|
T getBits(T value) {
|
||||||
|
static_assert(sizeof(T)*8 >= (pos + len - 1));
|
||||||
|
return (value >> pos) & ((1 << len) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t pos, size_t len, std::integral T>
|
||||||
|
T maskBits(T value) {
|
||||||
|
static_assert(sizeof(T)*8 >= (pos + len - 1));
|
||||||
|
return value & createBitMask<pos, len, T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N, std::integral T>
|
||||||
|
T clampBits(T value) {
|
||||||
|
static_assert(sizeof(T)*8 >= N);
|
||||||
|
return value & ((1 << N) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //UTILS_H
|
||||||
Loading…
Reference in New Issue
Block a user