diff --git a/src/defines.hpp b/src/defines.hpp index 2ab5e4e..30e63c2 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -9,7 +9,7 @@ // 6 n - - Add/Sub-Flag (BCD) // 5 h - - Half Carry Flag (BCD) // 4 cy C NC Carry Flag -// 3-0 - - - Not used (always zero) +// 3-0 - - - Not used #define CARRY_FLAG 4 //'C' #define HALFCARRY_FLAG 5 //'H' #define SUBTRACT_FLAG 6 //'N' @@ -21,7 +21,7 @@ #define SERIAL_INTERRUPT 3 #define JOYPAD_INTERRUPT 4 -#define T_CLOCK_FREQ 4194304 +#define T_CLOCK_FREQ 4194304 //2^22 #define DIVIDER_REGISTER_FREQ 16384 @@ -39,13 +39,13 @@ #define MODE2_DURATION 80 #define MODE3_MIN_DURATION 172 #define MODE0_3_DURATION 376 //mode3 is 172 to 289, mode0 87 to 204 -#define MODE1_DURATION 4560 #define H_SYNC 9198 #define V_SYNC 59.73 -#define HBlank_DURATION 204 //GPU_MODE 0 -#define SCANLINE_OAM_FREQ 80 //GPU_MODE 2 -#define SCANLINE_VRAM_FREQ 80 //GPU_MODE 3 +#define HBLANK_DURATION 204 //PPU_MODE 0 +#define VBLANK_DURATION 4560 +#define SCANLINE_OAM_FREQ 80 //PPU_MODE 2 +#define SCANLINE_VRAM_FREQ 80 //PPU_MODE 3 #endif diff --git a/src/gameboy.cpp b/src/gameboy.cpp index 20b5333..15190d3 100644 --- a/src/gameboy.cpp +++ b/src/gameboy.cpp @@ -1,60 +1,50 @@ #include #include "gameboy.hpp" -bool AddressSpace::getBootromState() -{ +bool AddressSpace::getBootromState() { return bootromLoaded; } -void AddressSpace::unmapBootrom() -{ +void AddressSpace::unmapBootrom() { bootromLoaded = false; } -void AddressSpace::mapBootrom() -{ +void AddressSpace::mapBootrom() { bootromLoaded = true; } -void AddressSpace::loadBootrom(std::string filename) -{ +void AddressSpace::loadBootrom(std::string filename) { std::ifstream file; int size = std::filesystem::file_size(filename); - if(size != 256) - { + if (size != 256) { std::cerr << "Bootrom was an unexpected size!\nQuitting!\n" << std::endl; exit(1); } file.open(filename, std::ios::binary); - file.read(reinterpret_cast(bootrom), BOOTROM_SIZE); + file.read(reinterpret_cast(bootrom), BOOTROM_SIZE); } -void AddressSpace::loadGame(std::string filename) -{ +void AddressSpace::loadGame(std::string filename) { game.open(filename, std::ios::binary); - if(!game.is_open()) - { + if (!game.is_open()) { std::cerr << "Game was not found!\nQuitting!\n" << std::endl; exit(1); } - game.read(reinterpret_cast(memoryLayout.romBank1), ROM_BANK_SIZE*2); + game.read(reinterpret_cast(memoryLayout.romBank1), ROM_BANK_SIZE * 2); } -void GameBoy::addCycles(uint8_t ticks) -{ - cycles = (cycles+ticks) % T_CLOCK_FREQ; +void GameBoy::addCycles(uint8_t ticks) { + cycles = (cycles + ticks) % T_CLOCK_FREQ; lastOpTicks = ticks; } -void GameBoy::start(std::string bootrom, std::string game) -{ +void GameBoy::start(std::string bootrom, std::string game) { addressSpace.loadBootrom(bootrom); addressSpace.loadGame(game); bool quit = false; - while(!quit) - { + while (!quit) { // Event loop: Check and handle SDL events // if(SDL_PollEvent(&event)) // { @@ -68,16 +58,13 @@ void GameBoy::start(std::string bootrom, std::string game) interruptHandler(); //timing(); ppuUpdate(); - if(PC > 0xFF && addressSpace.getBootromState()) - { + if (PC > 0xFF && addressSpace.getBootromState()) { addressSpace.unmapBootrom(); } int cyclesSince = cyclesSinceLastRefresh(); - if(cyclesSince > FRAME_DURATION) - { + if (cyclesSince > FRAME_DURATION) { lastRefresh = cycles; SDL2present(); } } } - diff --git a/src/gameboy.hpp b/src/gameboy.hpp index 6774e9a..2526394 100644 --- a/src/gameboy.hpp +++ b/src/gameboy.hpp @@ -11,66 +11,59 @@ #include "defines.hpp" //two bits per colour -enum Colour -{ +enum Colour { black = 0b11, darkGray = 0b10, lightGray = 0b01, white = 0b00 }; -enum PPUMode -{ +enum PPUMode { mode0, // Horizontal Blank (Mode 0): No access to video RAM, occurs during horizontal blanking period. mode1, // Vertical Blank (Mode 1): No access to video RAM, occurs during vertical blanking period. mode2, // OAM Search (Mode 2): Access to OAM (Object Attribute Memory) only, sprite evaluation. - mode3 // Pixel Transfer (Mode 3): Access to both OAM and video RAM, actual pixel transfer to the screen. + mode3 // Pixel Transfer (Mode 3): Access to both OAM and video RAM, actual pixel transfer to the screen. }; -union RegisterPair -{ +union RegisterPair { Word reg; //register.reg == (hi << 8) + lo. (hi is more significant than lo) - struct - { + struct { Byte lo; Byte hi; }; }; -class AddressSpace -{ - private: +class AddressSpace { +private: bool bootromLoaded = true; Byte bootrom[BOOTROM_SIZE]; std::ifstream game; - public: - AddressSpace() - { +public: + AddressSpace() { // Initialize the memory to zero memoryLayout = {}; std::memset(memoryLayout.memory, 0, sizeof(memoryLayout.memory)); } // Nested union for the memory layout - union MemoryLayout - { + union MemoryLayout { Byte memory[0x10000]; - struct - { - Byte romBank1[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) + + struct { + Byte romBank1[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 + 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 }; } memoryLayout; @@ -81,20 +74,19 @@ class AddressSpace void loadGame(std::string filename); //overload [] for echo ram and bootrom support - Byte operator[](uint32_t address) const - { - if(address >= 0xE000 && address < 0xFE00) + Byte operator[](uint32_t address) const { + if (address >= 0xE000 && address < 0xFE00) return memoryLayout.echoRam[address - 0xE000]; - if(address < 0x0100 && bootromLoaded) + if (address < 0x0100 && bootromLoaded) return bootrom[address]; else return memoryLayout.memory[address]; } - Byte& operator[](uint32_t address) - { - if(address >= 0xE000 && address < 0xFE00) + + Byte& operator[](uint32_t address) { + if (address >= 0xE000 && address < 0xFE00) return memoryLayout.echoRam[address - 0xE000]; - if(address < 0x0100 && bootromLoaded) + if (address < 0x0100 && bootromLoaded) return bootrom[address]; else return memoryLayout.memory[address]; @@ -102,7 +94,7 @@ class AddressSpace }; class GameBoy { - private: +private: uint32_t cycles = 0; uint32_t lastOpTicks = 0; uint32_t lastRefresh = 0; @@ -181,10 +173,10 @@ class GameBoy { PPUMode currentMode; //3 colour channels - uint32_t* framebuffer = new uint32_t[RESOLUTION_X*RESOLUTION_Y*SCREEN_BPP]; - SDL_Window *screen; - SDL_Renderer *renderer; - SDL_Texture *texture; + uint32_t* framebuffer = new uint32_t[RESOLUTION_X * RESOLUTION_Y * SCREEN_BPP]; + SDL_Window* screen; + SDL_Renderer* renderer; + SDL_Texture* texture; SDL_Event event; void opcodeHandler(); @@ -220,56 +212,56 @@ class GameBoy { void addCycles(Byte ticks); //OPCODE FUNCTIONS - template + template void ld(T& dest, T src); - template - void orBitwise(T &dest, T src); - template - void andBitwise(T &dest, T src); - template + template + void orBitwise(T& dest, T src); + template + void andBitwise(T& dest, T src); + template void xorBitwise(T& dest, T src); - template + template void bit(T testBit, T reg); - template + template void jp(T address); - template + template bool jrNZ(T offset); - template + template void inc(T& reg); - template + template void call(T address); void halt(); void stop(); - template + template void ldW(T dest, T src); - template + template void cp(T value); - template + template void dec(T& reg); - template + template bool jrZ(T offset); - template + template void sub(T value); - template + template void jr(T OFFSET); - template + template void push(T reg); - template + template void rl(T& reg); - template + template void pop(T& reg); - template + template void rla(T& reg); - template + template void rst(T address); void ret(); - template + template void add(T& reg, T value); void cpl(); void ccf(); - void swap(Byte &value); + void swap(Byte& value); - public: +public: void start(std::string bootrom, std::string game); void SDL2setup(); void SDL2destroy(); diff --git a/src/interupts.cpp b/src/interupts.cpp index 6696afc..2b1a2f1 100644 --- a/src/interupts.cpp +++ b/src/interupts.cpp @@ -1,35 +1,31 @@ #include "defines.hpp" #include "gameboy.hpp" -bool GameBoy::testInterruptEnabled(Byte interrupt) -{ - return (*IE) & (Byte) (1 << interrupt); +bool GameBoy::testInterruptEnabled(Byte interrupt) { + return (*IE) & (Byte)(1 << interrupt); } -void GameBoy::resetInterrupt(Byte interrupt) -{ +void GameBoy::resetInterrupt(Byte interrupt) { *IF &= ~(1 << interrupt); } -void GameBoy::interruptHandler() -{ - if(!IME) +void GameBoy::interruptHandler() { + if (!IME) return; - if(*IF & (Byte) (1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT)) + if (*IF & (Byte)(1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT)) VBlankHandle(); - if(*IF & (Byte) (1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT)) + if (*IF & (Byte)(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT)) LCDStatHandle(); - if(*IF & (Byte) (1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT)) + if (*IF & (Byte)(1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT)) timerHandle(); - if(*IF & (Byte) (1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT)) + if (*IF & (Byte)(1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT)) serialHandle(); - if(*IF & (Byte) (1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT)) + if (*IF & (Byte)(1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT)) joypadHandle(); } -void GameBoy::VBlankHandle() -{ +void GameBoy::VBlankHandle() { printf("VBlank interrupt"); IME = 0; push(PC); @@ -37,8 +33,7 @@ void GameBoy::VBlankHandle() resetInterrupt(VBLANK_INTERRUPT); } -void GameBoy::LCDStatHandle() -{ +void GameBoy::LCDStatHandle() { printf("LCD stat interrupt"); IME = 0; push(PC); @@ -47,8 +42,7 @@ void GameBoy::LCDStatHandle() resetInterrupt(LCD_STAT_INTERRUPT); } -void GameBoy::timerHandle() -{ +void GameBoy::timerHandle() { printf("timer interrupt"); IME = 0; push(PC); @@ -57,8 +51,7 @@ void GameBoy::timerHandle() resetInterrupt(TIMER_INTERRUPT); } -void GameBoy::serialHandle() -{ +void GameBoy::serialHandle() { printf("serial interrupt"); IME = 0; push(PC); @@ -67,12 +60,11 @@ void GameBoy::serialHandle() resetInterrupt(SERIAL_INTERRUPT); } -void GameBoy::joypadHandle() -{ +void GameBoy::joypadHandle() { printf("joypad interrupt"); IME = 0; push(PC); addCycles(16); PC = 0x60; resetInterrupt(JOYPAD_INTERRUPT); -} \ No newline at end of file +} diff --git a/src/main.cpp b/src/main.cpp index a2082c6..b1a90de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,7 @@ #include #include "gameboy.hpp" -int main(int argc, char** argv) -{ +int main(int argc, char** argv) { auto* gb = new GameBoy(); gb->SDL2setup(); gb->start("../bootrom.bin", "../roms/DrMario.gb"); diff --git a/src/ppu.cpp b/src/ppu.cpp index 6bcd9f8..1622ddc 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -4,7 +4,7 @@ #include void GameBoy::ppuUpdate() { - //test HBlank + //test for HBlank checkPPUMode(); if (cyclesToStayInHblank != -1) { @@ -53,7 +53,8 @@ void GameBoy::ppuUpdate() { // 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); - } else { + } + else { (*STAT) &= ~(1 << 2); } @@ -96,7 +97,8 @@ void GameBoy::checkPPUMode() { setPPUMode(PPUMode::mode3); else setPPUMode(PPUMode::mode0); - } else { + } + else { // VBlank Period setPPUMode(PPUMode::mode1); }