first tests now pass
This commit is contained in:
@@ -12,34 +12,76 @@
|
||||
class AddressSpace {
|
||||
bool bootromLoaded = true;
|
||||
Byte bootrom[BOOTROM_SIZE] = {0};
|
||||
|
||||
public:
|
||||
std::vector<Byte> game;
|
||||
|
||||
public:
|
||||
AddressSpace() {
|
||||
// Initialize the memory to zero
|
||||
memoryLayout = {};
|
||||
std::memset(memoryLayout.memory, 0, sizeof(memoryLayout.memory));
|
||||
}
|
||||
|
||||
// Nested union for the memory layout
|
||||
union MemoryLayout {
|
||||
Byte memory[0x10000];
|
||||
Byte* cartridgeRam = nullptr;
|
||||
|
||||
struct {
|
||||
Byte romBank0[ROM_BANK_SIZE]; // Mapped to 0x0000
|
||||
Byte romBankSwitch[ROM_BANK_SIZE]; // Mapped to 0x4000
|
||||
Byte vram[0x2000]; // Mapped to 0x8000
|
||||
Byte externalRam[0x2000]; // Mapped to 0xA000
|
||||
Byte memoryBank1[0x1000]; // Mapped to 0xC000
|
||||
Byte memoryBank2[0x1000]; // Mapped to 0xD000
|
||||
Byte echoRam[0x1E00]; // Mapped to 0xE000 (Echo RAM, mirrors 0xC000 to 0xDFFF)
|
||||
Byte spriteAttributeTable[0xA0]; // Mapped to 0xFE00
|
||||
Byte notUsable[0x60]; // Mapped to 0xFEA0
|
||||
Byte io[0x80]; // Mapped to 0xFF00, 0xFF0F is interrupt flag
|
||||
Byte specialRam[0x7F]; // Mapped to 0xFF80
|
||||
Byte interuptEnableReg; // Mapped to 0xFFFF
|
||||
};
|
||||
struct {
|
||||
Byte* romBank0; //[ROM_BANK_SIZE] Mapped to 0x0000
|
||||
Byte* romBankSwitch; //[ROM_BANK_SIZE] Mapped to 0x4000
|
||||
Byte vram[0x2000]; //Mapped to 0x8000
|
||||
Byte* externalRam; //[0x2000]; Mapped to 0xA000
|
||||
Byte memoryBank1[0x1000]; //Mapped to 0xC000
|
||||
Byte memoryBank2[0x1000]; //Mapped to 0xD000
|
||||
Byte oam[0xA0]; //Mapped to 0xFE00
|
||||
Byte notUsable[0x60]; //Mapped to 0xFEA0
|
||||
//General purpose hardware registers
|
||||
Byte JOYP;
|
||||
Byte SB;
|
||||
Byte SC;
|
||||
Byte DIV;
|
||||
//Timer registers
|
||||
Byte TIMA;
|
||||
Byte TMA;
|
||||
Byte TAC;
|
||||
//interrupt flag and enable
|
||||
Byte IF;
|
||||
//Sound registers
|
||||
Byte NR10;
|
||||
Byte NR11;
|
||||
Byte NR12;
|
||||
Byte NR13;
|
||||
Byte NR14;
|
||||
Byte NR20; //not used
|
||||
Byte NR21;
|
||||
Byte NR22;
|
||||
Byte NR23;
|
||||
Byte NR24;
|
||||
Byte NR30;
|
||||
Byte NR31;
|
||||
Byte NR32;
|
||||
Byte NR33;
|
||||
Byte NR34;
|
||||
Byte NR40; //unused
|
||||
Byte NR41;
|
||||
Byte NR42;
|
||||
Byte NR43;
|
||||
Byte NR44;
|
||||
Byte NR50;
|
||||
Byte NR51;
|
||||
Byte NR52;
|
||||
Byte waveRam[0x10];
|
||||
//PPU registers
|
||||
Byte LCDC;
|
||||
Byte STAT;
|
||||
Byte SCY;
|
||||
Byte SCX;
|
||||
Byte LY;
|
||||
Byte LYC;
|
||||
Byte DMA;
|
||||
Byte BGP;
|
||||
Byte OBP0;
|
||||
Byte OBP1;
|
||||
Byte WY;
|
||||
Byte WX;
|
||||
Byte specialRam[0x7F]; //Mapped to 0xFF80
|
||||
Byte IE; // Mapped to 0xFFFF
|
||||
} memoryLayout{};
|
||||
|
||||
void unmapBootrom();
|
||||
@@ -49,35 +91,281 @@ public:
|
||||
void loadGame(const std::string& filename);
|
||||
|
||||
void determineMBCInfo();
|
||||
static bool testMBCWrite(Word address);
|
||||
Byte* MBCRead(Word address);
|
||||
//prevents seg faults when programs with no MBC try to write to ROM
|
||||
Byte dummyVal = 0;
|
||||
void MBCUpdate();
|
||||
void loadRomBank();
|
||||
void createRamBank();
|
||||
void loadRamBank();
|
||||
MBCType MBC = {};
|
||||
uint32_t romSize = 0;
|
||||
uint32_t romBanks = 0;
|
||||
uint32_t externalRamSize = 0;
|
||||
uint32_t externalRamBanks = 0;
|
||||
Byte romBankRegister = 0x01;
|
||||
Byte ramBankRegister = 0x00;
|
||||
|
||||
//Selected ROM Bank = (Secondary Bank << 5) + ROM Bank
|
||||
Byte selectedRomBank = 0;
|
||||
Byte romBankRegister = 0x00;
|
||||
//2 bit register acts as secondary rom bank register or ram bank number
|
||||
Byte twoBitBankRegister = 0x0;
|
||||
Byte selectedExternalRamBank = 0;
|
||||
Byte romRamSelect = 0x00;
|
||||
Byte ramEnable = 0x00;
|
||||
//MBC3
|
||||
Byte latchClockData = 0x00;
|
||||
Byte ramBankRTCRegister = 0x00;
|
||||
|
||||
//overload [] for echo ram and bootrom support
|
||||
Byte operator[](const uint32_t address) const {
|
||||
if (address >= 0xE000 && address < 0xFE00)
|
||||
return memoryLayout.echoRam[address - 0x2000];
|
||||
//read
|
||||
Byte operator[](const Word address) const {
|
||||
if (address < 0x0100 && bootromLoaded)
|
||||
return bootrom[address];
|
||||
if (address < 0x4000)
|
||||
return memoryLayout.romBank0[address];
|
||||
if (address < 0x8000)
|
||||
return memoryLayout.romBankSwitch[address - 0x4000];
|
||||
if (address < 0xA000)
|
||||
return memoryLayout.vram[address - 0x8000];
|
||||
if (address < 0xC000) {
|
||||
if (externalRamSize == 0)
|
||||
return 0xFF;
|
||||
return memoryLayout.externalRam[address - 0xA000];
|
||||
}
|
||||
if (address < 0xD000)
|
||||
return memoryLayout.memoryBank1[address - 0xC000];
|
||||
if (address < 0xE000)
|
||||
return memoryLayout.memoryBank2[address - 0xD000];
|
||||
if (address < 0xFE00)
|
||||
return memoryLayout.memoryBank1[address - 0xE000];
|
||||
if (address < 0xFEA0)
|
||||
return memoryLayout.oam[address - 0xFE00];
|
||||
if (address < 0xFF00) {
|
||||
if ((memoryLayout.STAT & 0x03) == 2 || (memoryLayout.STAT & 0x03) == 3)
|
||||
return 0xFF;
|
||||
return 0x00;
|
||||
}
|
||||
if (address < 0xFF80)
|
||||
switch (address) {
|
||||
case 0xFF00:
|
||||
return memoryLayout.JOYP;
|
||||
case 0xFF01:
|
||||
return memoryLayout.SB;
|
||||
case 0xFF02:
|
||||
return memoryLayout.SC;
|
||||
// Timer registers
|
||||
case 0xFF04:
|
||||
return memoryLayout.DIV;
|
||||
case 0xFF05:
|
||||
return memoryLayout.TIMA;
|
||||
case 0xFF06:
|
||||
return memoryLayout.TMA;
|
||||
case 0xFF07:
|
||||
return memoryLayout.TAC;
|
||||
// Interrupt flag
|
||||
case 0xFF0F:
|
||||
return memoryLayout.IF;
|
||||
// Sound registers
|
||||
case 0xFF10:
|
||||
return memoryLayout.NR10;
|
||||
case 0xFF11:
|
||||
return memoryLayout.NR11;
|
||||
case 0xFF12:
|
||||
return memoryLayout.NR12;
|
||||
case 0xFF13:
|
||||
return memoryLayout.NR13;
|
||||
case 0xFF14:
|
||||
return memoryLayout.NR14;
|
||||
case 0xFF16:
|
||||
return memoryLayout.NR21;
|
||||
case 0xFF17:
|
||||
return memoryLayout.NR22;
|
||||
case 0xFF18:
|
||||
return memoryLayout.NR23;
|
||||
case 0xFF19:
|
||||
return memoryLayout.NR24;
|
||||
case 0xFF1A:
|
||||
return memoryLayout.NR30;
|
||||
case 0xFF1B:
|
||||
return memoryLayout.NR31;
|
||||
case 0xFF1C:
|
||||
return memoryLayout.NR32;
|
||||
case 0xFF1D:
|
||||
return memoryLayout.NR33;
|
||||
case 0xFF1E:
|
||||
return memoryLayout.NR34;
|
||||
case 0xFF20:
|
||||
return memoryLayout.NR41;
|
||||
case 0xFF21:
|
||||
return memoryLayout.NR42;
|
||||
case 0xFF22:
|
||||
return memoryLayout.NR43;
|
||||
case 0xFF23:
|
||||
return memoryLayout.NR44;
|
||||
case 0xFF24:
|
||||
return memoryLayout.NR50;
|
||||
case 0xFF25:
|
||||
return memoryLayout.NR51;
|
||||
case 0xFF26:
|
||||
return memoryLayout.NR52;
|
||||
// PPU registers
|
||||
case 0xFF40:
|
||||
return memoryLayout.LCDC;
|
||||
case 0xFF41:
|
||||
return memoryLayout.STAT;
|
||||
case 0xFF42:
|
||||
return memoryLayout.SCY;
|
||||
case 0xFF43:
|
||||
return memoryLayout.SCX;
|
||||
case 0xFF44:
|
||||
return memoryLayout.LY;
|
||||
case 0xFF45:
|
||||
return memoryLayout.LYC;
|
||||
case 0xFF46:
|
||||
return memoryLayout.DMA;
|
||||
case 0xFF47:
|
||||
return memoryLayout.BGP;
|
||||
case 0xFF48:
|
||||
return memoryLayout.OBP0;
|
||||
case 0xFF49:
|
||||
return memoryLayout.OBP1;
|
||||
case 0xFF4A:
|
||||
return memoryLayout.WY;
|
||||
case 0xFF4B:
|
||||
return memoryLayout.WX;
|
||||
|
||||
return memoryLayout.memory[address];
|
||||
default:
|
||||
if (address >= 0xFF30 && address <= 0xFF3F) {
|
||||
return memoryLayout.waveRam[address - 0xFF30];
|
||||
}
|
||||
return 0xFF;
|
||||
}
|
||||
if (address < 0xFFFF)
|
||||
return memoryLayout.specialRam[address - 0xFF80];
|
||||
//0xFFFF
|
||||
return memoryLayout.IE;
|
||||
}
|
||||
|
||||
Byte& operator[](const uint32_t address) {
|
||||
if (address >= 0xE000 && address < 0xFE00)
|
||||
return memoryLayout.echoRam[address - 0x2000];
|
||||
//write
|
||||
Byte& operator[](const Word address) {
|
||||
if (address < 0x0100 && bootromLoaded)
|
||||
return bootrom[address];
|
||||
if (address < 0x8000)
|
||||
return (*MBCRead(address));
|
||||
if (address < 0xA000)
|
||||
return memoryLayout.vram[address - 0x8000];
|
||||
if (address < 0xC000)
|
||||
return memoryLayout.externalRam[address - 0xA000];
|
||||
if (address < 0xD000)
|
||||
return memoryLayout.memoryBank1[address - 0xC000];
|
||||
if (address < 0xE000)
|
||||
return memoryLayout.memoryBank2[address - 0xD000];
|
||||
if (address < 0xFE00)
|
||||
return memoryLayout.memoryBank1[address - 0xE000];
|
||||
if (address < 0xFEA0)
|
||||
return memoryLayout.oam[address - 0xFE00];
|
||||
if (address < 0xFF00)
|
||||
return memoryLayout.notUsable[address - 0xFEA0];
|
||||
if (address < 0xFF80)
|
||||
switch (address) {
|
||||
case 0xFF00:
|
||||
return memoryLayout.JOYP;
|
||||
case 0xFF01:
|
||||
return memoryLayout.SB;
|
||||
case 0xFF02:
|
||||
return memoryLayout.SC;
|
||||
case 0xFF04:
|
||||
return memoryLayout.DIV;
|
||||
// Timer registers
|
||||
case 0xFF05:
|
||||
return memoryLayout.TIMA;
|
||||
// The address 0xFF15 is mentioned as unused, possibly a mistake? Original has TMA = 0xFF06 typically.
|
||||
case 0xFF06:
|
||||
return memoryLayout.TMA;
|
||||
case 0xFF07:
|
||||
return memoryLayout.TAC;
|
||||
// Interrupt flag
|
||||
case 0xFF0F:
|
||||
return memoryLayout.IF;
|
||||
// Sound registers
|
||||
case 0xFF10:
|
||||
return memoryLayout.NR10;
|
||||
case 0xFF11:
|
||||
return memoryLayout.NR11;
|
||||
case 0xFF12:
|
||||
return memoryLayout.NR12;
|
||||
case 0xFF13:
|
||||
return memoryLayout.NR13;
|
||||
case 0xFF14:
|
||||
return memoryLayout.NR14;
|
||||
case 0xFF16:
|
||||
return memoryLayout.NR21;
|
||||
case 0xFF17:
|
||||
return memoryLayout.NR22;
|
||||
case 0xFF18:
|
||||
return memoryLayout.NR23;
|
||||
case 0xFF19:
|
||||
return memoryLayout.NR24;
|
||||
case 0xFF1A:
|
||||
return memoryLayout.NR30;
|
||||
case 0xFF1B:
|
||||
return memoryLayout.NR31;
|
||||
case 0xFF1C:
|
||||
return memoryLayout.NR32;
|
||||
case 0xFF1D:
|
||||
return memoryLayout.NR33;
|
||||
case 0xFF1E:
|
||||
return memoryLayout.NR34;
|
||||
case 0xFF20:
|
||||
return memoryLayout.NR41;
|
||||
case 0xFF21:
|
||||
return memoryLayout.NR42;
|
||||
case 0xFF22:
|
||||
return memoryLayout.NR43;
|
||||
case 0xFF23:
|
||||
return memoryLayout.NR44;
|
||||
case 0xFF24:
|
||||
return memoryLayout.NR50;
|
||||
case 0xFF25:
|
||||
return memoryLayout.NR51;
|
||||
case 0xFF26:
|
||||
return memoryLayout.NR52;
|
||||
// PPU registers
|
||||
case 0xFF40:
|
||||
return memoryLayout.LCDC;
|
||||
case 0xFF41:
|
||||
return memoryLayout.STAT;
|
||||
case 0xFF42:
|
||||
return memoryLayout.SCY;
|
||||
case 0xFF43:
|
||||
return memoryLayout.SCX;
|
||||
case 0xFF44:
|
||||
return memoryLayout.LY;
|
||||
case 0xFF45:
|
||||
return memoryLayout.LYC;
|
||||
case 0xFF46:
|
||||
return memoryLayout.DMA;
|
||||
case 0xFF47:
|
||||
return memoryLayout.BGP;
|
||||
case 0xFF48:
|
||||
return memoryLayout.OBP0;
|
||||
case 0xFF49:
|
||||
return memoryLayout.OBP1;
|
||||
case 0xFF4A:
|
||||
return memoryLayout.WY;
|
||||
case 0xFF4B:
|
||||
return memoryLayout.WX;
|
||||
|
||||
return memoryLayout.memory[address];
|
||||
default:
|
||||
if (address >= 0xFF30 && address <= 0xFF3F) {
|
||||
return memoryLayout.waveRam[address - 0xFF30];
|
||||
}
|
||||
return dummyVal;
|
||||
}
|
||||
if (address < 0xFFFF)
|
||||
return memoryLayout.specialRam[address - 0xFF80];
|
||||
//0xFFFF
|
||||
return memoryLayout.IE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user