diff --git a/src/addressSpace.cpp b/src/addressSpace.cpp index 36d9486..6eaa633 100644 --- a/src/addressSpace.cpp +++ b/src/addressSpace.cpp @@ -42,6 +42,6 @@ void AddressSpace::loadGame(const std::string& filename) { std::istream_iterator(rom), std::istream_iterator()); - memcpy(memoryLayout.romBank0, game.data(), ROM_BANK_SIZE); - memcpy(memoryLayout.romBankSwitch, game.data() + ROM_BANK_SIZE, ROM_BANK_SIZE); + memoryLayout.romBank0 = game.data(); + memoryLayout.romBankSwitch = game.data() + ROM_BANK_SIZE; } diff --git a/src/addressSpace.hpp b/src/addressSpace.hpp index c1a1413..e7470a4 100644 --- a/src/addressSpace.hpp +++ b/src/addressSpace.hpp @@ -12,34 +12,76 @@ class AddressSpace { bool bootromLoaded = true; Byte bootrom[BOOTROM_SIZE] = {0}; - -public: std::vector 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; } }; diff --git a/src/defines.hpp b/src/defines.hpp index 73e41f9..91587bc 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -32,6 +32,7 @@ #define SCREEN_BPP 3 #define ROM_BANK_SIZE 0x4000 +#define RAM_BANK_SIZE 0x2000 #define SCANLINES_PER_FRAME 154 #define SCANLINE_DURATION 456 diff --git a/src/extendedOpcodeResolver.cpp b/src/extendedOpcodeResolver.cpp index 1c35bf6..dbbc1ac 100644 --- a/src/extendedOpcodeResolver.cpp +++ b/src/extendedOpcodeResolver.cpp @@ -3,7 +3,7 @@ void GameBoy::extendedOpcodeResolver() { PC += 1; - switch (addressSpace[PC]) { + switch (readOnlyAddressSpace[PC]) { case 0x00: rlc(BC.hi); PC += 1; @@ -425,7 +425,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x46: - bit(0, addressSpace[HL.reg]); + bit(0, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -473,7 +473,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x4E: - bit(1, addressSpace[HL.reg]); + bit(1, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -521,7 +521,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x56: - bit(2, addressSpace[HL.reg]); + bit(2, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -569,7 +569,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x5E: - bit(3, addressSpace[HL.reg]); + bit(3, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -617,7 +617,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x66: - bit(4, addressSpace[HL.reg]); + bit(4, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -665,7 +665,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x6E: - bit(5, addressSpace[HL.reg]); + bit(5, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -713,7 +713,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x76: - bit(6, addressSpace[HL.reg]); + bit(6, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; @@ -761,7 +761,7 @@ void GameBoy::extendedOpcodeResolver() { break; case 0x7E: - bit(7, addressSpace[HL.reg]); + bit(7, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(12); break; diff --git a/src/gameboy.cpp b/src/gameboy.cpp index 2275f74..9ff5ce8 100644 --- a/src/gameboy.cpp +++ b/src/gameboy.cpp @@ -13,10 +13,11 @@ void GameBoy::start(std::string bootrom, std::string game) { addressSpace.loadBootrom(bootrom); addressSpace.loadGame(game); addressSpace.determineMBCInfo(); + addressSpace.createRamBank(); //init some registers that won't otherwise by set - (*JOYP) = 0xCF; - (*SC) = 0x7E; + addressSpace.memoryLayout.JOYP = 0xCF; + addressSpace.memoryLayout.SC = 0x7E; bool quit = false; @@ -28,29 +29,34 @@ void GameBoy::start(std::string bootrom, std::string game) { if (event.type == SDL_QUIT) { quit = true; } + if (event.type == SDL_KEYUP) { + display = true; + } } while (!rendered) { if (PC > 0xFF && addressSpace.getBootromState()) { addressSpace.unmapBootrom(); } - ppuEnabled = (*LCDC) & 0x80; + ppuEnabled = addressSpace.memoryLayout.LCDC & 0x80; - if (PC >= 0xe0) - display = true; - if (display) { - printf("Cycles: %lu, Opcode: 0x%.2x PPU cycles: %lu, PPMode: %d\n", cycles, addressSpace[PC], - cyclesSinceLastScanline(), currentMode); - printf("PC:0x%.2x, SP:0x%.2x\n", PC, SP); - printf("AF:0x%.4x, BC:0x%.4x\n", AF.reg, BC.reg); - printf("DE:0x%.4x, HL:0x%.4x\n", DE.reg, HL.reg); - printf("IME:%d IF:0x%.2x IE:0x%.2x\n", IME, (*IF), (*IE)); - printf("LCDC:%.2x STAT:0x%.2x LY:%d LYC:%d\n", (*LCDC), (*STAT), (*LY), (*LYC)); - printf("Cart type: 0x%.2x\n", addressSpace.game[0x147]); - printf("\n"); - } + // if (PC == 0x100) + // display = true; + // if (display) { + // printf("Cycles: %lu, Opcode: 0x%.2x PPU cycles: %lu, PPMode: %d\n", cycles, readOnlyAddressSpace[PC], + // cyclesSinceLastScanline(), currentMode); + // printf("AF:0x%.4x, BC:0x%.4x\n", AF.reg, BC.reg); + // printf("DE:0x%.4x, HL:0x%.4x\n", DE.reg, HL.reg); + // printf("IME:%d IF:0x%.2x IE:0x%.2x\n", IME, (*IF), (*IE)); + // printf("PC:0x%.4x, SP:0x%.4x\n", PC, SP); + // printf("LCDC:%.2x STAT:0x%.2x LY:%d LYC:%d\n", (*LCDC), (*STAT), (*LY), (*LYC)); + // printf("\n"); + // } + // if (PC >= 0xf000) + // exit(1); opcodeResolver(); + addressSpace.MBCUpdate(); interruptHandler(); timingHandler(); if (ppuEnabled) { @@ -60,8 +66,8 @@ void GameBoy::start(std::string bootrom, std::string game) { ppuCycles = 2; lastScanline = 0; lastRefresh = 0; - (*LY) = 0x00; - (*STAT) &= 0xfc; + addressSpace.memoryLayout.LY = 0x00; + addressSpace.memoryLayout.STAT &= 0xfc; } } rendered = false; diff --git a/src/gameboy.hpp b/src/gameboy.hpp index ec0cbd6..3a41bd6 100644 --- a/src/gameboy.hpp +++ b/src/gameboy.hpp @@ -46,61 +46,7 @@ class GameBoy { Word PC = 0x0000; //program counter AddressSpace addressSpace; - - //General purpose hardware registers - Byte* const JOYP = &addressSpace[0xFF00]; - Byte* const SB = &addressSpace[0xFF01]; - Byte* const SC = &addressSpace[0xFF02]; - Byte* const DIV = &addressSpace[0xFF04]; - - //Timer registers - Byte* const TIMA = &addressSpace[0xFF05]; - Byte* const TMA = &addressSpace[0xFF15]; //unused - Byte* const TAC = &addressSpace[0xFF16]; - - //interrupt flag and enable - Byte* const IF = &addressSpace[0xFF0F]; - Byte* const IE = &addressSpace[0xFFFF]; - - //Sound registers - Byte* const NR10 = &addressSpace[0xFF10]; - Byte* const NR11 = &addressSpace[0xFF11]; - Byte* const NR12 = &addressSpace[0xFF12]; - Byte* const NR13 = &addressSpace[0xFF13]; - Byte* const NR14 = &addressSpace[0xFF14]; - Byte* const NR20 = &addressSpace[0xFF15]; //unused - Byte* const NR21 = &addressSpace[0xFF16]; - Byte* const NR22 = &addressSpace[0xFF17]; - Byte* const NR23 = &addressSpace[0xFF18]; - Byte* const NR24 = &addressSpace[0xFF19]; - Byte* const NR30 = &addressSpace[0xFF1A]; - Byte* const NR31 = &addressSpace[0xFF1B]; - Byte* const NR32 = &addressSpace[0xFF1C]; - Byte* const NR33 = &addressSpace[0xFF1D]; - Byte* const NR34 = &addressSpace[0xFF1E]; - Byte* const NR40 = &addressSpace[0xFF1F]; //unused - Byte* const NR41 = &addressSpace[0xFF20]; - Byte* const NR42 = &addressSpace[0xFF21]; - Byte* const NR43 = &addressSpace[0xFF22]; - Byte* const NR44 = &addressSpace[0xFF23]; - Byte* const NR50 = &addressSpace[0xFF24]; - Byte* const NR51 = &addressSpace[0xFF25]; - Byte* const NR52 = &addressSpace[0xFF26]; - Byte* const waveRam = &addressSpace[0xFF30]; //WaveRam[0x10] - - //PPU registers - Byte* const LCDC = &addressSpace[0xFF40]; - Byte* const STAT = &addressSpace[0xFF41]; - Byte* const SCY = &addressSpace[0xFF42]; - Byte* const SCX = &addressSpace[0xFF43]; - Byte* const LY = &addressSpace[0xFF44]; - Byte* const LYC = &addressSpace[0xFF45]; - Byte* const DMA = &addressSpace[0xFF46]; - Byte* const BGP = &addressSpace[0xFF47]; - Byte* const OBP0 = &addressSpace[0xFF48]; - Byte* const OBP1 = &addressSpace[0xFF49]; - Byte* const WY = &addressSpace[0xFF4A]; - Byte* const WX = &addressSpace[0xFF4B]; + const AddressSpace& readOnlyAddressSpace = addressSpace; PPUMode currentMode = PPUMode::mode0; @@ -114,8 +60,6 @@ class GameBoy { uint32_t frameTime = 0; const int frameDelay = 1000 / V_SYNC; - bool testMBCWrite(const Byte& address); - void opcodeResolver(); void incLY(); void ppuUpdate(); @@ -131,7 +75,7 @@ class GameBoy { void interruptHandler(); bool testInterruptEnabled(Byte interrupt) const; - void resetInterrupt(Byte interrupt) const; + void resetInterrupt(Byte interrupt); void VBlankHandle(); void LCDStatHandle(); @@ -162,8 +106,8 @@ class GameBoy { void xorBitwise(T& dest, T src); void bit(Byte testBit, Byte reg); void extendedOpcodeResolver(); - static void set(const uint8_t testBit, uint8_t& reg); - static void res(const uint8_t testBit, uint8_t& reg); + static void set(uint8_t testBit, uint8_t& reg); + static void res(uint8_t testBit, uint8_t& reg); template void jp(T address); template diff --git a/src/interupts.cpp b/src/interupts.cpp index 5b1b33f..c4fa0ff 100644 --- a/src/interupts.cpp +++ b/src/interupts.cpp @@ -2,27 +2,32 @@ #include "gameboy.hpp" bool GameBoy::testInterruptEnabled(const Byte interrupt) const { - return (*IE) & static_cast(1 << interrupt); + return readOnlyAddressSpace.memoryLayout.IE & static_cast(1 << interrupt); } -void GameBoy::resetInterrupt(const Byte interrupt) const { - *IF &= ~(1 << interrupt); - *IF |= 0xE0; +void GameBoy::resetInterrupt(const Byte interrupt) { + addressSpace.memoryLayout.IF &= ~(1 << interrupt); + addressSpace.memoryLayout.IF |= 0xE0; } void GameBoy::interruptHandler() { if (!IME) return; - if (*IF & static_cast(1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT)) + if (readOnlyAddressSpace.memoryLayout.IF & static_cast(1 << VBLANK_INTERRUPT) && testInterruptEnabled( + VBLANK_INTERRUPT)) VBlankHandle(); - if (*IF & static_cast(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT)) + if (readOnlyAddressSpace.memoryLayout.IF & static_cast(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled( + LCD_STAT_INTERRUPT)) LCDStatHandle(); - if (*IF & static_cast(1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT)) + if (readOnlyAddressSpace.memoryLayout.IF & static_cast(1 << TIMER_INTERRUPT) && testInterruptEnabled( + TIMER_INTERRUPT)) timerHandle(); - if (*IF & static_cast(1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT)) + if (readOnlyAddressSpace.memoryLayout.IF & static_cast(1 << SERIAL_INTERRUPT) && testInterruptEnabled( + SERIAL_INTERRUPT)) serialHandle(); - if (*IF & static_cast(1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT)) + if (readOnlyAddressSpace.memoryLayout.IF & static_cast(1 << JOYPAD_INTERRUPT) && testInterruptEnabled( + JOYPAD_INTERRUPT)) joypadHandle(); } diff --git a/src/main.cpp b/src/main.cpp index a1919b7..d532d1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,7 @@ int main(int argc, char** argv) { auto* gb = new GameBoy(); gb->SDL2setup(); - gb->start("../dmg_boot.bin", "../roms/cpu_instrs.gb"); + gb->start("../dmg_boot.bin", "../roms/03-op_sp,hl.gb"); gb->SDL2destroy(); delete gb; diff --git a/src/mbc.cpp b/src/mbc.cpp index 8dfef3a..f8c82f4 100644 --- a/src/mbc.cpp +++ b/src/mbc.cpp @@ -1,10 +1,9 @@ #include "addressSpace.hpp" -#include "gameboy.hpp" void AddressSpace::determineMBCInfo() { MBC = static_cast(memoryLayout.romBank0[0x147]); romSize = 32768 * (1 << memoryLayout.romBank0[0x147]); - romBanks = (1 << (memoryLayout.romBank0[0x147] + 1)); + romBanks = 1 << (memoryLayout.romBank0[0x147] + 1); switch (memoryLayout.romBank0[0x0149]) { case 0x02: @@ -29,16 +28,90 @@ void AddressSpace::determineMBCInfo() { break; } - if (MBC == MBC2 || MBC2Battery) { + if (MBC == MBC2 || MBC == MBC2Battery) { //only the lower 4 bits are usable externalRamSize = 512; } } -bool GameBoy::testMBCWrite(const Byte& address) { - const Byte* ptr = &address; - if (ptr >= &addressSpace[0x0] && ptr <= &addressSpace[0x7FFF]) +bool AddressSpace::testMBCWrite(const Word address) { + if (address <= 0x7FFF) return true; return false; } +Byte* AddressSpace::MBCRead(const Word address) { + if (MBC == MBC1) { + if (address <= 0x1FFF) + return &ramEnable; + if (address <= 0x3FFF) + return &romBankRegister; + if (address <= 0x5FFF) + return &twoBitBankRegister; + if (address <= 0x7FFF) + return &romRamSelect; + } + if (MBC == MBC1Ram || MBC == MBC1RamBattery) { + if (address <= 0x1FFF) + return &ramEnable; + //bits 0-4 + if (address <= 0x3FFF) + return &romBankRegister; + if (address <= 0x5FFF) { + return &twoBitBankRegister; + } + if (address <= 0x7FFF) + return &romRamSelect; + } + return &dummyVal; +} + +void AddressSpace::MBCUpdate() { + if (MBC == MBC1) { + //TODO: multicart roms need to be able to switch the first rom bank as well + //see: https://gbdev.io/pandocs/MBC1.html + + //Selected ROM Bank = (Secondary Bank << 5) + ROM Bank + romBankRegister &= 0x1F; + twoBitBankRegister &= 0x3; + + //512 KiB can only have 8KiB of ram + if (romSize > 524288) { + if (romBankRegister == 0) + selectedRomBank = (twoBitBankRegister << 5) + 1; + selectedRomBank = (twoBitBankRegister << 5) + romBankRegister; + selectedExternalRamBank = 0; + } + else { + if (romBankRegister == 0) + selectedRomBank = 1; + else + selectedRomBank = romBankRegister; + selectedExternalRamBank = twoBitBankRegister; + } + loadRomBank(); + loadRamBank(); + } + if (MBC == MBC1Ram || MBC == MBC1RamBattery) {} +} + +void AddressSpace::loadRomBank() { + Byte* old = memoryLayout.romBankSwitch; + memoryLayout.romBankSwitch = game.data() + (ROM_BANK_SIZE * selectedRomBank); + if (old != memoryLayout.romBankSwitch) + printf("\n"); +} + +void AddressSpace::createRamBank() { + if (externalRamSize) + cartridgeRam = new Byte[externalRamSize]; +} + +void AddressSpace::loadRamBank() { + Byte* old = memoryLayout.externalRam; + if (cartridgeRam != nullptr) + memoryLayout.externalRam = cartridgeRam + (RAM_BANK_SIZE * selectedExternalRamBank); + if (old != memoryLayout.externalRam) + printf("\n"); +} + diff --git a/src/opcodeResolver.cpp b/src/opcodeResolver.cpp index 3e48396..613e68a 100644 --- a/src/opcodeResolver.cpp +++ b/src/opcodeResolver.cpp @@ -16,28 +16,28 @@ Word GameBoy::getWordPC() { RegisterPair word = {0}; //remember little endianness - word.lo = addressSpace[PC + 1]; - word.hi = addressSpace[PC + 2]; + word.lo = readOnlyAddressSpace[PC + 1]; + word.hi = readOnlyAddressSpace[PC + 2]; return word.reg; } Byte GameBoy::getBytePC() { - return addressSpace[PC + 1]; + return readOnlyAddressSpace[PC + 1]; } Word GameBoy::getWordSP() { RegisterPair word = {0}; //remember little endianness - word.lo = addressSpace[SP++]; - word.hi = addressSpace[SP++]; + word.lo = readOnlyAddressSpace[SP++]; + word.hi = readOnlyAddressSpace[SP++]; return word.reg; } Byte GameBoy::getByteSP() { - return addressSpace[SP++]; + return readOnlyAddressSpace[SP++]; } void GameBoy::ret() { @@ -47,8 +47,8 @@ void GameBoy::ret() { template void GameBoy::ld(T& dest, T src) { if constexpr (std::is_same_v) { - if (&dest == DIV) { - *DIV = 0x00; + if (&dest == &addressSpace.memoryLayout.DIV) { + addressSpace.memoryLayout.DIV = 0x00; lastDivUpdate = cycles; } else { @@ -90,7 +90,7 @@ void GameBoy::add(T& reg, T value) { else resetFlag(HALFCARRY_FLAG); //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ - if ((((value & 0xFFFF) + (reg & 0xFFFF)) & 0x10000) == 0x10000) + if ((static_cast(value) + static_cast(reg)) & 0x10000) setFlag(CARRY_FLAG); else resetFlag(CARRY_FLAG); @@ -98,10 +98,12 @@ void GameBoy::add(T& reg, T value) { reg += value; - if (reg == 0) - setFlag(ZERO_FLAG); - else - resetFlag(ZERO_FLAG); + if (sizeof(reg) == sizeof(Byte)) { + if (reg == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + } resetFlag(SUBTRACT_FLAG); } @@ -225,6 +227,8 @@ void GameBoy::orBitwise(T& dest, T src) { if (dest == 0) setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); resetFlag(SUBTRACT_FLAG); resetFlag(HALFCARRY_FLAG); @@ -237,6 +241,8 @@ void GameBoy::andBitwise(T& dest, T src) { if (dest == 0) setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); resetFlag(SUBTRACT_FLAG); setFlag(HALFCARRY_FLAG); @@ -249,6 +255,8 @@ void GameBoy::xorBitwise(T& dest, T src) { if (dest == 0) setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); resetFlag(SUBTRACT_FLAG); resetFlag(CARRY_FLAG); @@ -473,7 +481,7 @@ void GameBoy::rr(Byte& reg) { reg >>= 1; if (getFlag(CARRY_FLAG)) - AF.hi |= 0x80; + reg |= 0x80; if (lsb) setFlag(CARRY_FLAG); @@ -628,6 +636,7 @@ void GameBoy::srl(Byte& reg) { template void GameBoy::pop(T& reg) { reg = getWordSP(); + AF.reg &= 0xFFF0; } template @@ -636,9 +645,9 @@ void GameBoy::push(T reg) { RegisterPair temp = {0}; temp.lo = reg & 0xFF; temp.hi = reg >> 8; - SP--; + SP -= 1; addressSpace[SP] = temp.hi; - SP--; + SP -= 1; addressSpace[SP] = temp.lo; } @@ -678,9 +687,9 @@ void GameBoy::ccf() { void GameBoy::stop() {} void GameBoy::opcodeResolver() { - if (addressSpace[PC] != 0xCB) { + if (readOnlyAddressSpace[PC] != 0xCB) { bool jumped; - switch (addressSpace[PC]) { + switch (readOnlyAddressSpace[PC]) { case 0x00: //NOP PC += 1; @@ -742,7 +751,7 @@ void GameBoy::opcodeResolver() { break; case 0x0A: - ld(AF.hi, addressSpace[BC.reg]); + ld(AF.hi, readOnlyAddressSpace[BC.reg]); PC += 1; addCycles(8); break; @@ -790,13 +799,13 @@ void GameBoy::opcodeResolver() { break; case 0x12: - ld(addressSpace[BC.reg], AF.hi); + ld(addressSpace[DE.reg], AF.hi); PC += 1; addCycles(8); break; case 0x13: - DE.reg++; //no flags change no just inc it manually + DE.reg += 1; //no flags change no just inc it manually PC += 1; addCycles(8); break; @@ -837,7 +846,7 @@ void GameBoy::opcodeResolver() { break; case 0x1A: - ld(AF.hi, addressSpace[DE.reg]); + ld(AF.hi, readOnlyAddressSpace[DE.reg]); PC += 1; addCycles(8); break; @@ -891,7 +900,7 @@ void GameBoy::opcodeResolver() { case 0x22: ld(addressSpace[HL.reg], AF.hi); - HL.reg++; + HL.reg += 1; PC += 1; addCycles(8); break; @@ -944,7 +953,7 @@ void GameBoy::opcodeResolver() { break; case 0x2A: - ld(AF.hi, addressSpace[HL.reg]); + ld(AF.hi, readOnlyAddressSpace[HL.reg]); HL.reg += 1; PC += 1; addCycles(8); @@ -963,7 +972,7 @@ void GameBoy::opcodeResolver() { break; case 0x2D: - dec(HL.hi); + dec(HL.lo); PC += 1; addCycles(4); break; @@ -999,7 +1008,7 @@ void GameBoy::opcodeResolver() { case 0x32: ld(addressSpace[HL.reg], AF.hi); - HL.reg--; + HL.reg -= 1; PC += 1; addCycles(8); break; @@ -1052,14 +1061,14 @@ void GameBoy::opcodeResolver() { break; case 0x3A: - ld(AF.hi, addressSpace[HL.reg]); + ld(AF.hi, readOnlyAddressSpace[HL.reg]); HL.reg -= 1; PC += 1; addCycles(8); break; case 0x3B: - SP--; + SP -= 1; PC += 1; addCycles(8); break; @@ -1125,7 +1134,7 @@ void GameBoy::opcodeResolver() { break; case 0x46: - ld(BC.hi, addressSpace[HL.reg]); + ld(BC.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1173,7 +1182,7 @@ void GameBoy::opcodeResolver() { break; case 0x4E: - ld(BC.lo, addressSpace[HL.reg]); + ld(BC.lo, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1221,7 +1230,7 @@ void GameBoy::opcodeResolver() { break; case 0x56: - ld(DE.hi, addressSpace[HL.reg]); + ld(DE.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1269,7 +1278,7 @@ void GameBoy::opcodeResolver() { break; case 0x5E: - ld(DE.lo, addressSpace[HL.reg]); + ld(DE.lo, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1317,7 +1326,7 @@ void GameBoy::opcodeResolver() { break; case 0x66: - ld(HL.hi, addressSpace[HL.reg]); + ld(HL.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1365,7 +1374,7 @@ void GameBoy::opcodeResolver() { break; case 0x6E: - ld(HL.lo, addressSpace[HL.reg]); + ld(HL.lo, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1461,7 +1470,7 @@ void GameBoy::opcodeResolver() { break; case 0x7E: - ld(AF.hi, addressSpace[HL.reg]); + ld(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1509,7 +1518,7 @@ void GameBoy::opcodeResolver() { break; case 0x86: - add(AF.hi, addressSpace[HL.reg]); + add(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1557,7 +1566,7 @@ void GameBoy::opcodeResolver() { break; case 0x8E: - adc(AF.hi, addressSpace[HL.reg]); + adc(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1605,7 +1614,7 @@ void GameBoy::opcodeResolver() { break; case 0x96: - sub(addressSpace[HL.reg]); + sub(readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1653,7 +1662,7 @@ void GameBoy::opcodeResolver() { break; case 0x9E: - sbc(addressSpace[HL.reg]); + sbc(readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1701,7 +1710,7 @@ void GameBoy::opcodeResolver() { break; case 0xA6: - andBitwise(AF.hi, addressSpace[HL.reg]); + andBitwise(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1749,7 +1758,7 @@ void GameBoy::opcodeResolver() { break; case 0xAE: - xorBitwise(AF.hi, addressSpace[HL.reg]); + xorBitwise(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1797,7 +1806,7 @@ void GameBoy::opcodeResolver() { break; case 0xB6: - orBitwise(AF.hi, addressSpace[HL.reg]); + orBitwise(AF.hi, readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -1845,7 +1854,7 @@ void GameBoy::opcodeResolver() { break; case 0xBE: - cp(addressSpace[HL.reg]); + cp(readOnlyAddressSpace[HL.reg]); PC += 1; addCycles(8); break; @@ -2091,7 +2100,7 @@ void GameBoy::opcodeResolver() { break; case 0xE2: - ld(addressSpace[BC.lo + 0xFF00], AF.hi); + ld(addressSpace[0xFF00 + BC.lo], AF.hi); PC += 1; addCycles(8); break; @@ -2115,20 +2124,19 @@ void GameBoy::opcodeResolver() { case 0xE8: { - const auto immediate = static_cast(getBytePC()); - const Word result = SP + static_cast(immediate); + const int16_t immediate = static_cast(getBytePC()); - if (((SP ^ immediate ^ result) & 0x10) != 0) + if ((SP & 0xF) + (immediate & 0xF) > 0xF) setFlag(HALFCARRY_FLAG); else resetFlag(HALFCARRY_FLAG); - if (((SP ^ immediate ^ result) & 0x100) != 0) + if ((SP & 0xFF) + (immediate & 0xFF) > 0xFF) setFlag(CARRY_FLAG); else resetFlag(CARRY_FLAG); - SP = result; + SP += immediate; resetFlag(ZERO_FLAG); resetFlag(SUBTRACT_FLAG); @@ -2161,7 +2169,7 @@ void GameBoy::opcodeResolver() { break; case 0xF0: - ld(AF.hi, addressSpace[0xFF00 + getBytePC()]); + ld(AF.hi, readOnlyAddressSpace[0xFF00 + getBytePC()]); PC += 2; addCycles(12); break; @@ -2173,7 +2181,7 @@ void GameBoy::opcodeResolver() { break; case 0xF2: - ld(AF.hi, addressSpace[0xFF00 + BC.lo]); + ld(AF.hi, readOnlyAddressSpace[0xFF00 + BC.lo]); PC += 1; addCycles(12); break; @@ -2203,20 +2211,20 @@ void GameBoy::opcodeResolver() { case 0xF8: { - const auto n = static_cast(getBytePC()); - const Word result = SP + n; + const int16_t immediate = static_cast(getBytePC()); + HL.reg = SP + immediate; - //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ - if ((((result & 0xF) + (HL.reg & 0xF)) & 0x10) == 0x10) + if ((SP & 0xF) + (immediate & 0xF) > 0xF) setFlag(HALFCARRY_FLAG); else resetFlag(HALFCARRY_FLAG); - //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ - if ((((result & 0xFF) + (HL.reg & 0xFF)) & 0x100) == 0x100) + + + if ((SP & 0xFF) + (immediate & 0xFF) > 0xFF) setFlag(CARRY_FLAG); else resetFlag(CARRY_FLAG); - HL.reg = result; // Load the result into HL + resetFlag(ZERO_FLAG); resetFlag(SUBTRACT_FLAG); @@ -2233,7 +2241,7 @@ void GameBoy::opcodeResolver() { break; case 0xFA: - ld(AF.hi, addressSpace[getWordPC()]); + ld(AF.hi, readOnlyAddressSpace[getWordPC()]); PC += 3; addCycles(16); break; diff --git a/src/ppu.cpp b/src/ppu.cpp index c5d8297..79d36ee 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -8,25 +8,28 @@ void GameBoy::ppuUpdate() { //test for HBlank checkPPUMode(); - if ((*LY) == (*LYC) || (*STAT) & (1 << 6)) { + if (readOnlyAddressSpace.memoryLayout.LY == readOnlyAddressSpace.memoryLayout.LYC || readOnlyAddressSpace. + memoryLayout.STAT & (1 << 6)) { // Request STAT interrupt if LY matches LYC // bug on DMG models triggers a STAT interrupt anytime the STAT register is written // Road Rage and Zerd no Denetsu rely on this - (*STAT) |= (1 << 2); + addressSpace.memoryLayout.STAT |= (1 << 2); } else { - (*STAT) &= ~(1 << 2); + addressSpace.memoryLayout.STAT &= ~(1 << 2); } // Check for STAT interrupts and request if needed (e.g., when entering specific modes) - bool hBlankInterruptEnabled = (*STAT) & (1 << 3); - bool vBlankInterruptEnabled = (*STAT) & (1 << 4); /* Determine if VBlank interrupt is enabled */ - bool oamInterruptEnabled = (*STAT) & (1 << 5); /* Determine if OAM Search interrupt is enabled */ + bool hBlankInterruptEnabled = readOnlyAddressSpace.memoryLayout.STAT & (1 << 3); + bool vBlankInterruptEnabled = readOnlyAddressSpace.memoryLayout.STAT & (1 << 4); + /* Determine if VBlank interrupt is enabled */ + bool oamInterruptEnabled = readOnlyAddressSpace.memoryLayout.STAT & (1 << 5); + /* Determine if OAM Search interrupt is enabled */ if (currentMode == PPUMode::mode0 && hBlankInterruptEnabled || currentMode == PPUMode::mode1 && vBlankInterruptEnabled || currentMode == PPUMode::mode2 && oamInterruptEnabled) { - *IF |= 0x2; + addressSpace.memoryLayout.IF |= 0x2; } } @@ -58,16 +61,16 @@ void GameBoy::checkPPUMode() { } void GameBoy::incLY() { - (*LY)++; + addressSpace.memoryLayout.LY += 1; setPPUMode(PPUMode::mode2); - if ((*LY) > SCANLINES_PER_FRAME - 1) { - (*LY) = 0; + if (addressSpace.memoryLayout.LY > SCANLINES_PER_FRAME - 1) { + addressSpace.memoryLayout.LY = 0; } - else if ((*LY) == 144) { + else if (addressSpace.memoryLayout.LY == 144) { // VBlank Period SDL2present(); setPPUMode(PPUMode::mode1); - *IF |= 0x1; + addressSpace.memoryLayout.IF |= 0x1; } } @@ -84,31 +87,31 @@ uint64_t GameBoy::cyclesSinceLastRefresh() const { void GameBoy::setPPUMode(const PPUMode mode) { switch (mode) { case PPUMode::mode0: - (*STAT) &= ~0x03; + addressSpace.memoryLayout.STAT &= ~0x03; currentMode = PPUMode::mode0; break; case PPUMode::mode1: - (*STAT) &= ~0x03; - (*STAT) |= 0x01; + addressSpace.memoryLayout.STAT &= ~0x03; + addressSpace.memoryLayout.STAT |= 0x01; currentMode = PPUMode::mode1; break; case PPUMode::mode2: - (*STAT) &= ~0x03; - (*STAT) |= 0x02; + addressSpace.memoryLayout.STAT &= ~0x03; + addressSpace.memoryLayout.STAT |= 0x02; currentMode = PPUMode::mode2; break; case PPUMode::mode3: - (*STAT) &= ~0x03; - (*STAT) |= 0x03; + addressSpace.memoryLayout.STAT &= ~0x03; + addressSpace.memoryLayout.STAT |= 0x03; currentMode = PPUMode::mode3; break; } //7th bit is unused but always set - (*STAT) |= 0x80; + addressSpace.memoryLayout.STAT |= 0x80; } void GameBoy::drawLine() { - const uint8_t line = (*LY); + const uint8_t line = readOnlyAddressSpace.memoryLayout.LY; // Calculate the starting index of the current scanline in the framebuffer const uint32_t lineStartIndex = line * RESOLUTION_X; @@ -116,25 +119,27 @@ void GameBoy::drawLine() { // Pointer to the current line's pixel data in the framebuffer uint32_t* currentLinePixels = framebuffer + lineStartIndex; - if (!(*LCDC & 0x1)) { + if (!(readOnlyAddressSpace.memoryLayout.LCDC & 0x1)) { std::fill_n(currentLinePixels, RESOLUTION_X, 0xFFFFFFFF); // Fill line with white if BG display is off. return; } - const uint16_t backgroundMapAddr = (*LCDC & 0x08) ? 0x9C00 : 0x9800; - const uint16_t tileDataTableAddr = (*LCDC & 0x10) ? 0x8000 : 0x8800; - const bool signedIndex = !(*LCDC & 0x10); + const uint16_t backgroundMapAddr = (readOnlyAddressSpace.memoryLayout.LCDC & 0x08) ? 0x9C00 : 0x9800; + const uint16_t tileDataTableAddr = (readOnlyAddressSpace.memoryLayout.LCDC & 0x10) ? 0x8000 : 0x8800; + const bool signedIndex = !(readOnlyAddressSpace.memoryLayout.LCDC & 0x10); for (int pixel = 0; pixel < RESOLUTION_X; pixel++) { - const uint8_t xPos = (pixel + (*SCX)) % 256; // 256 pixels in total BG width - const uint8_t yPos = (line + (*SCY)) % 256; // 256 pixels in total BG height + const uint8_t xPos = (pixel + readOnlyAddressSpace.memoryLayout.SCX) % 256; // 256 pixels in total BG width + const uint8_t yPos = (line + readOnlyAddressSpace.memoryLayout.SCY) % 256; // 256 pixels in total BG height const uint16_t tileRow = (yPos / 8) * 32; const uint16_t tileCol = xPos / 8; const uint16_t tileIndex = tileRow + tileCol; const uint16_t tileAddr = backgroundMapAddr + tileIndex; - const int8_t tileID = signedIndex ? static_cast(addressSpace[tileAddr]) : addressSpace[tileAddr]; + const int8_t tileID = signedIndex + ? static_cast(readOnlyAddressSpace[tileAddr]) + : addressSpace[tileAddr]; uint16_t tileDataAddr; if (signedIndex) { @@ -145,14 +150,14 @@ void GameBoy::drawLine() { } const uint8_t lineOffset = yPos % 8; - const uint8_t tileRowData1 = addressSpace[tileDataAddr + (lineOffset * 2)]; - const uint8_t tileRowData2 = addressSpace[tileDataAddr + (lineOffset * 2) + 1]; + const uint8_t tileRowData1 = readOnlyAddressSpace[tileDataAddr + (lineOffset * 2)]; + const uint8_t tileRowData2 = readOnlyAddressSpace[tileDataAddr + (lineOffset * 2) + 1]; const uint8_t colourBit = 7 - (xPos % 8); const uint8_t colourNum = ((tileRowData2 >> colourBit) & 0x1) << 1 | ((tileRowData1 >> colourBit) & 0x1); // Apply the BGP register for palette mapping - uint8_t palette = (*BGP >> (colourNum * 2)) & 0x3; + uint8_t palette = (readOnlyAddressSpace.memoryLayout.BGP >> (colourNum * 2)) & 0x3; if (xPos == 0) palette = 0x3; switch (palette) { @@ -198,7 +203,6 @@ void GameBoy::SDL2present() { frameTime = SDL_GetTicks() - frameStart; - std::cout << SDL_GetTicks() << " " << frameTime << std::endl; if (frameDelay > frameTime) { SDL_Delay(frameDelay - frameTime); diff --git a/src/timing.cpp b/src/timing.cpp index 5b8d5d4..665c2c4 100644 --- a/src/timing.cpp +++ b/src/timing.cpp @@ -4,9 +4,7 @@ void GameBoy::timingHandler() { if (cycles - lastDivUpdate >= DIVIDER_REGISTER_FREQ) { const uint8_t increments = (cycles - lastDivUpdate) / DIVIDER_REGISTER_FREQ; - - (*DIV) += increments; - + addressSpace.memoryLayout.DIV += increments; lastDivUpdate += increments * DIVIDER_REGISTER_FREQ; } }