Created an interface for mappers

Also implemented NROM or "no mapper"
This commit is contained in:
Amish Naidu 2016-09-28 23:59:09 +05:30
parent c1af387390
commit cdf9a4b44c
12 changed files with 205 additions and 81 deletions

View file

@ -23,7 +23,7 @@ namespace sn
std::vector<Byte> m_PRG_ROM;
std::vector<Byte> m_CHR_ROM;
Byte m_nameTableMirroring;
Byte m_mapper;
Byte m_mapperNumber;
bool m_extendedRAM;
bool m_chrRAM;
};

View file

@ -31,6 +31,7 @@ namespace sn
CPU m_cpu;
PPU m_ppu;
Cartridge m_cartridge;
std::unique_ptr<Mapper> m_mapper;
Controller m_controller1;

View file

@ -3,7 +3,9 @@
#include <vector>
#include <map>
#include <functional>
#include <memory>
#include "Cartridge.h"
#include "Mapper.h"
namespace sn
{
@ -28,20 +30,17 @@ namespace sn
MainBus();
Byte read(Address addr);
void write(Address addr, Byte value);
bool loadCartridge(Cartridge *cart);
bool setMapper(Mapper* mapper);
bool setWriteCallback(IORegisters reg, std::function<void(Byte)> callback);
bool setReadCallback(IORegisters reg, std::function<Byte(void)> callback);
const Byte* getPagePtr(Byte page);
private:
std::vector<Byte> m_RAM;
std::vector<Byte> m_extRAM;
Cartridge* m_cartride;
Byte m_mapper;
Mapper* m_mapper;
std::map<IORegisters, std::function<void(Byte)>> m_writeCallbacks;
std::map<IORegisters, std::function<Byte(void)>> m_readCallbacks;
//temporary only
bool one_bank;
std::map<IORegisters, std::function<Byte(void)>> m_readCallbacks;;
};
};

52
include/Mapper.h Normal file
View file

@ -0,0 +1,52 @@
#ifndef MAPPER_H
#define MAPPER_H
#include "Cartridge.h"
#include <memory>
namespace sn
{
enum NameTableMirroring
{
Horizontal = 0,
Vertical = 1,
FourScreen = 8,
};
class Mapper
{
public:
enum Type
{
NROM = 0,
MMC1 = 1,
UxROM = 2,
};
Mapper(Cartridge& cart, Type t) : m_cartridge(cart), m_type(t) {};
virtual void writePRG (Address addr, Byte value) = 0;
virtual Byte readPRG (Address addr) = 0;
virtual const Byte* getPagePtr (Address addr) = 0; //for DMAs
virtual Byte readCHR (Address addr) = 0;
virtual void writeCHR (Address addr, Byte value) = 0;
bool inline hasExtendedRAM()
{
return m_cartridge.hasExtendedRAM();
}
NameTableMirroring inline getNameTableMirroring()
{
return static_cast<NameTableMirroring>(m_cartridge.getNameTableMirroring());
}
static std::unique_ptr<Mapper> createMapper (Type mapper_t, Cartridge& cart);
protected:
Cartridge& m_cartridge;
Type m_type;
};
}
#endif //MAPPER_H

25
include/MapperNROM.h Normal file
View file

@ -0,0 +1,25 @@
#ifndef MAPPERNROM_H
#define MAPPERNROM_H
#include "Mapper.h"
namespace sn
{
class MapperNROM : public Mapper
{
public:
MapperNROM(Cartridge& cart);
void writePRG (Address addr, Byte value);
Byte readPRG (Address addr);
const Byte* getPagePtr(Address addr);
Byte readCHR (Address addr);
void writeCHR (Address addr, Byte value);
private:
bool m_oneBank;
bool m_usesCharacterRAM;
std::vector<Byte> m_characterRAM;
};
}
#endif // MAPPERNROM_H

View file

@ -2,16 +2,10 @@
#define PICTUREBUS_H
#include <vector>
#include "Cartridge.h"
#include "Mapper.h"
namespace sn
{
enum NameTableMirroring
{
Horizontal = 0,
Vertical = 1,
FourScreen = 8,
};
class PictureBus
{
public:
@ -19,18 +13,15 @@ namespace sn
Byte read(Address addr);
void write(Address addr, Byte value);
bool loadCartridge(Cartridge *cart);
bool setMapper(Mapper *mapper);
Byte readPalette(Byte paletteAddr);
private:
std::vector<Byte> m_RAM;
NameTableMirroring m_mirroring;
std::size_t NameTable0, NameTable1, NameTable2, NameTable3; //indices where they start in RAM vector
bool m_usesCharacterRAM;
std::vector<Byte> m_characterRAM;
std::vector<Byte> m_palette;
Cartridge* m_cartride;
Mapper* m_mapper;
};
}
#endif // PICTUREBUS_H

View file

@ -7,7 +7,7 @@ namespace sn
{
Cartridge::Cartridge() :
m_nameTableMirroring(0),
m_mapper(0),
m_mapperNumber(0),
m_extendedRAM(false)
{
@ -24,7 +24,7 @@ namespace sn
Byte Cartridge::getMapper()
{
return m_mapper;
return m_mapperNumber;
}
Byte Cartridge::getNameTableMirroring()
@ -42,7 +42,7 @@ namespace sn
std::ifstream romFile (path, std::ios_base::binary | std::ios_base::in);
if (!romFile)
{
LOG(Error) << "Could not open ROM file" << std::endl;
LOG(Error) << "Could not open ROM file from path: " << path << std::endl;
return false;
}
@ -81,8 +81,8 @@ namespace sn
m_nameTableMirroring = header[6] & 0xB;
LOG(Info) << "Name Table Mirroring: " << +m_nameTableMirroring << std::endl;
m_mapper = ((header[6] >> 4) & 0xf) | (header[7] & 0xf0);
LOG(Info) << "Mapper #: " << +m_mapper << std::endl;
m_mapperNumber = ((header[6] >> 4) & 0xf) | (header[7] & 0xf0);
LOG(Info) << "Mapper #: " << +m_mapperNumber << std::endl;
m_extendedRAM = header[6] & 0x2;
LOG(Info) << "Extended (CPU) RAM: " << std::boolalpha << m_extendedRAM << std::endl;

View file

@ -43,8 +43,15 @@ namespace sn
if (!m_cartridge.loadFromFile(rom_path))
return;
if (!m_bus.loadCartridge(&m_cartridge) ||
!m_pictureBus.loadCartridge(&m_cartridge))
m_mapper = Mapper::createMapper(static_cast<Mapper::Type>(m_cartridge.getMapper()), m_cartridge);
if (!m_mapper)
{
LOG(Error) << "Creating Mapper failed. Probably unsupported." << std::endl;
return;
}
if (!m_bus.setMapper(m_mapper.get()) ||
!m_pictureBus.setMapper(m_mapper.get()))
return;
m_cpu.reset();
@ -92,10 +99,11 @@ namespace sn
while (m_elapsedTime > m_cpuCycleDuration)
{
//PPU
m_ppu.step();
m_ppu.step();
m_ppu.step();
//CPU
m_cpu.step();
m_elapsedTime -= m_cpuCycleDuration;

View file

@ -6,7 +6,7 @@ namespace sn
{
MainBus::MainBus() :
m_RAM(0x800, 0),
m_cartride(nullptr)
m_mapper(nullptr)
{
}
@ -45,17 +45,14 @@ namespace sn
}
else if (addr < 0x8000)
{
if (m_cartride->hasExtendedRAM())
if (m_mapper->hasExtendedRAM())
{
return m_extRAM[addr - 0x6000];
}
}
else
{
if (!one_bank)
return m_cartride->getROM()[addr - 0x8000];
else //mirrored
return m_cartride->getROM()[(addr - 0x8000) & 0x3fff];
return m_mapper->readPRG(addr);
}
return 0;
}
@ -95,14 +92,14 @@ namespace sn
}
else if (addr < 0x8000)
{
if (m_cartride->hasExtendedRAM())
if (m_mapper->hasExtendedRAM())
{
m_extRAM[addr - 0x8000] = value;
}
}
else
{
LOG(InfoVerbose) << "ROM memory write attempt\n" << std::endl;
m_mapper->writePRG(addr, value);
}
}
@ -121,44 +118,30 @@ namespace sn
}
else if (addr < 0x8000)
{
if (m_cartride->hasExtendedRAM())
if (m_mapper->hasExtendedRAM())
{
return &m_extRAM[addr - 0x8000];
}
}
else
{
if (!one_bank)
return &m_cartride->getROM()[addr - 0x8000];
else //mirrored
return &m_cartride->getROM()[(addr - 0x8000) & 0x3fff];
}
return nullptr;
}
bool MainBus::loadCartridge(Cartridge* cart)
bool MainBus::setMapper(Mapper* mapper)
{
m_cartride = cart;
m_mapper = cart->getMapper();
auto rom = cart->getROM();
m_mapper = mapper;
if (m_mapper != 0)
if (!mapper)
{
LOG(Error) << "Mapper not supported" << std::endl;
LOG(Error) << "Mapper pointer is nullptr" << std::endl;
return false;
}
if (cart->hasExtendedRAM())
if (mapper->hasExtendedRAM())
m_extRAM.resize(0x2000);
if (rom.size() == 0x4000) //1 bank
{
one_bank = true;
}
else //2 banks
{
one_bank = false;
}
return true;
}

19
src/Mapper.cpp Normal file
View file

@ -0,0 +1,19 @@
#include "Mapper.h"
#include "MapperNROM.h"
namespace sn
{
std::unique_ptr<Mapper> Mapper::createMapper(Mapper::Type mapper_t, sn::Cartridge& cart)
{
std::unique_ptr<Mapper> ret;
switch (mapper_t)
{
case NROM:
ret.reset(new MapperNROM(cart));
break;
default:
ret.reset(nullptr);
}
return ret;
}
}

64
src/MapperNROM.cpp Normal file
View file

@ -0,0 +1,64 @@
#include "MapperNROM.h"
#include "Log.h"
namespace sn
{
MapperNROM::MapperNROM(Cartridge &cart) :
Mapper(cart, Mapper::NROM)
{
if (cart.getROM().size() == 0x4000) //1 bank
{
m_oneBank = true;
}
else //2 banks
{
m_oneBank = false;
}
if (cart.getVROM().size() == 0)
{
m_usesCharacterRAM = true;
m_characterRAM.resize(0x2000);
LOG(Info) << "Uses character RAM" << std::endl;
}
else
m_usesCharacterRAM = false;
}
Byte MapperNROM::readPRG(Address addr)
{
if (!m_oneBank)
return m_cartridge.getROM()[addr - 0x8000];
else //mirrored
return m_cartridge.getROM()[(addr - 0x8000) & 0x3fff];
}
void MapperNROM::writePRG(Address addr, Byte value)
{
LOG(InfoVerbose) << "ROM memory write attempt at " << +addr << " to set " << +value << std::endl;
}
const Byte* MapperNROM::getPagePtr(Address addr)
{
if (!m_oneBank)
return &m_cartridge.getROM()[addr - 0x8000];
else //mirrored
return &m_cartridge.getROM()[(addr - 0x8000) & 0x3fff];
}
Byte MapperNROM::readCHR(Address addr)
{
if (m_usesCharacterRAM)
return m_characterRAM[addr];
else
return m_cartridge.getVROM()[addr];
}
void MapperNROM::writeCHR(Address addr, Byte value)
{
if (m_usesCharacterRAM)
m_characterRAM[addr] = value;
else
LOG(Info) << "Read-only CHR memory write attempt at " << std::hex << addr << std::endl;
}
}

View file

@ -7,17 +7,14 @@ namespace sn
PictureBus::PictureBus() :
m_RAM(0x800),
m_palette(0x20),
m_cartride(nullptr)
m_mapper(nullptr)
{}
Byte PictureBus::read(Address addr)
{
if (addr < 0x2000)
{
if (m_usesCharacterRAM)
return m_characterRAM[addr];
else
return m_cartride->getVROM()[addr];
return m_mapper->readCHR(addr);
}
else if (addr < 0x3eff) //Name tables upto 0x3000, then mirrored upto 3eff
{
@ -47,10 +44,7 @@ namespace sn
{
if (addr < 0x2000)
{
if (m_usesCharacterRAM)
m_characterRAM[addr] = value;
else
LOG(Info) << "Read-only memory write attempt at " << std::hex << addr << std::endl;
m_mapper->writeCHR(addr, value);
}
else if (addr < 0x3eff) //Name tables upto 0x3000, then mirrored upto 3eff
{
@ -73,20 +67,17 @@ namespace sn
}
}
bool PictureBus::loadCartridge(Cartridge* cart)
bool PictureBus::setMapper(Mapper *mapper)
{
if (!cart)
if (!mapper)
{
LOG(Error) << "Mapper argument is nullptr" << std::endl;
return false;
LOG(Error) << "Cartride argument is nullptr" << std::endl;
}
m_cartride = cart;
m_mapper = mapper;
auto mirroring = cart->getNameTableMirroring();
// mirroring = (mirroring & FourScreen) ? FourScreen : mirroring;
m_mirroring = static_cast<NameTableMirroring>(mirroring);
switch (m_mirroring)
switch (m_mapper->getNameTableMirroring())
{
case Horizontal:
NameTable0 = NameTable1 = 0;
@ -103,15 +94,6 @@ namespace sn
return false;
}
if (cart->getVROM().size() == 0)
{
m_usesCharacterRAM = true;
m_characterRAM.resize(0x2000);
LOG(Info) << "Uses character RAM" << std::endl;
}
else
m_usesCharacterRAM = false;
return true;
}