diff --git a/.gitignore b/.gitignore index aa6cf58..77724da 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ build/ .idea roms/ -bootrom.bin +dmg_boot.bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d32882..ab855ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,12 @@ set(CMAKE_CXX_STANDARD 23) find_package(SDL2 REQUIRED) include_directories(${SDL2_INCLUDE_DIRS}) -add_executable(GBpp src/main.cpp src/gameboy.cpp src/opcode.cpp - src/interupts.cpp src/ppu.cpp) +add_executable(GBpp src/main.cpp + src/gameboy.cpp + src/opcodeResolver.cpp + src/interupts.cpp + src/ppu.cpp + src/timing.cpp + src/extendedOpcodeResolver.cpp +) target_link_libraries(GBpp ${SDL2_LIBRARIES}) \ No newline at end of file diff --git a/src/defines.hpp b/src/defines.hpp index 30e63c2..85b0934 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -23,7 +23,7 @@ #define T_CLOCK_FREQ 4194304 //2^22 -#define DIVIDER_REGISTER_FREQ 16384 +#define DIVIDER_REGISTER_FREQ (4194304/16384) #define BOOTROM_SIZE 0x100 @@ -38,7 +38,6 @@ #define FRAME_DURATION 70224 #define MODE2_DURATION 80 #define MODE3_MIN_DURATION 172 -#define MODE0_3_DURATION 376 //mode3 is 172 to 289, mode0 87 to 204 #define H_SYNC 9198 #define V_SYNC 59.73 diff --git a/src/extendedOpcodeResolver.cpp b/src/extendedOpcodeResolver.cpp new file mode 100644 index 0000000..1c35bf6 --- /dev/null +++ b/src/extendedOpcodeResolver.cpp @@ -0,0 +1,1547 @@ +#include "gameboy.hpp" + +void GameBoy::extendedOpcodeResolver() { + PC += 1; + + switch (addressSpace[PC]) { + case 0x00: + rlc(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x01: + rlc(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x02: + rlc(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x03: + rlc(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x04: + rlc(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x05: + rlc(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x06: + rlc(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x07: + rlc(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x08: + rrc(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x09: + rrc(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x0A: + rrc(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x0B: + rrc(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x0C: + rrc(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x0D: + rrc(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x0E: + rrc(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x0F: + rrc(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x10: + rl(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x11: + rl(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x12: + rl(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x13: + rl(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x14: + rl(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x15: + rl(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x16: + rl(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x17: + rl(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x18: + rr(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x19: + rr(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x1A: + rr(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x1B: + rr(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x1C: + rr(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x1D: + rr(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x1E: + rr(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x1F: + rr(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x20: + sla(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x21: + sla(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x22: + sla(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x23: + sla(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x24: + sla(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x25: + sla(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x26: + sla(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x27: + sla(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x28: + sra(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x29: + sra(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x2A: + sra(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x2B: + sra(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x2C: + sra(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x2D: + sra(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x2E: + sra(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x2F: + sra(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x30: + swap(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x31: + swap(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x32: + swap(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x33: + swap(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x34: + swap(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x35: + swap(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x36: + swap(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x37: + swap(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x38: + srl(BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x39: + srl(BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x3A: + srl(DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x3B: + srl(DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x3C: + srl(HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x3D: + srl(HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x3E: + srl(addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x3F: + srl(AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x40: + bit(0, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x41: + bit(0, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x42: + bit(0, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x43: + bit(0, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x44: + bit(0, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x45: + bit(0, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x46: + bit(0, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x47: + bit(0, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x48: + bit(1, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x49: + bit(1, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x4A: + bit(1, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x4B: + bit(1, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x4C: + bit(1, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x4D: + bit(1, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x4E: + bit(1, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x4F: + bit(1, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x50: + bit(2, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x51: + bit(2, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x52: + bit(2, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x53: + bit(2, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x54: + bit(2, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x55: + bit(2, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x56: + bit(2, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x57: + bit(2, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x58: + bit(3, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x59: + bit(3, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x5A: + bit(3, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x5B: + bit(3, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x5C: + bit(3, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x5D: + bit(3, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x5E: + bit(3, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x5F: + bit(3, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x60: + bit(4, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x61: + bit(4, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x62: + bit(4, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x63: + bit(4, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x64: + bit(4, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x65: + bit(4, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x66: + bit(4, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x67: + bit(4, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x68: + bit(5, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x69: + bit(5, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x6A: + bit(5, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x6B: + bit(5, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x6C: + bit(5, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x6D: + bit(5, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x6E: + bit(5, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x6F: + bit(5, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x70: + bit(6, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x71: + bit(6, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x72: + bit(6, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x73: + bit(6, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x74: + bit(6, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x75: + bit(6, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x76: + bit(6, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x77: + bit(6, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x78: + bit(7, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x79: + bit(7, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x7A: + bit(7, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x7B: + bit(7, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x7C: + bit(7, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x7D: + bit(7, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x7E: + bit(7, addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x7F: + bit(3, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x80: + res(0, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x81: + res(0, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x82: + res(0, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x83: + res(0, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x84: + res(0, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x85: + res(0, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x86: + res(0, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x87: + res(0, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x88: + res(1, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x89: + res(1, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x8A: + res(1, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x8B: + res(1, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x8C: + res(1, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x8D: + res(1, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x8E: + res(1, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x8F: + res(1, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x90: + res(2, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x91: + res(2, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x92: + res(2, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x93: + res(2, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x94: + res(2, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x95: + res(2, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x96: + res(2, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x97: + res(2, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x98: + res(3, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x99: + res(3, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x9A: + res(3, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x9B: + res(3, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x9C: + res(3, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x9D: + res(3, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x9E: + res(3, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0x9F: + res(3, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xA0: + res(4, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xA1: + res(4, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xA2: + res(4, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xA3: + res(4, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xA4: + res(4, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xA5: + res(4, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xA6: + res(4, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xA7: + res(4, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xA8: + res(5, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xA9: + res(5, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xAA: + res(5, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xAB: + res(5, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xAC: + res(5, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xAD: + res(5, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xAE: + res(5, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xAF: + res(5, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xB0: + res(6, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xB1: + res(6, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xB2: + res(6, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xB3: + res(6, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xB4: + res(6, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xB5: + res(6, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xB6: + res(6, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xB7: + res(6, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xB8: + res(7, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xB9: + res(7, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xBA: + res(7, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xBB: + res(7, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xBC: + res(7, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xBD: + res(7, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xBE: + res(7, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xBF: + res(7, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xC0: + set(0, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xC1: + set(0, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xC2: + set(0, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xC3: + set(0, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xC4: + set(0, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xC5: + set(0, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xC6: + set(0, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xC7: + set(0, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xC8: + set(1, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xC9: + set(1, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xCA: + set(1, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xCB: + set(1, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xCC: + set(1, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xCD: + set(1, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xCE: + set(1, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xCF: + set(1, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xD0: + set(2, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xD1: + set(2, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xD2: + set(2, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xD3: + set(2, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xD4: + set(2, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xD5: + set(2, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xD6: + set(2, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xD7: + set(2, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xD8: + set(3, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xD9: + set(3, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xDA: + set(3, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xDB: + set(3, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xDC: + set(3, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xDD: + set(3, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xDE: + set(3, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xDF: + set(3, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xE0: + set(4, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xE1: + set(4, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xE2: + set(4, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xE3: + set(4, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xE4: + set(4, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xE5: + set(4, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xE6: + set(4, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xE7: + set(4, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xE8: + set(5, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xE9: + set(5, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xEA: + set(5, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xEB: + set(5, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xEC: + set(5, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xED: + set(5, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xEE: + set(5, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xEF: + set(5, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xF0: + set(6, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xF1: + set(6, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xF2: + set(6, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xF3: + set(6, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xF4: + set(6, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xF5: + set(6, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xF6: + set(6, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xF7: + set(6, AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xF8: + set(7, BC.hi); + PC += 1; + addCycles(8); + break; + + case 0xF9: + set(7, BC.lo); + PC += 1; + addCycles(8); + break; + + case 0xFA: + set(7, DE.hi); + PC += 1; + addCycles(8); + break; + + case 0xFB: + set(7, DE.lo); + PC += 1; + addCycles(8); + break; + + case 0xFC: + set(7, HL.hi); + PC += 1; + addCycles(8); + break; + + case 0xFD: + set(7, HL.lo); + PC += 1; + addCycles(8); + break; + + case 0xFE: + set(7, addressSpace[HL.reg]); + PC += 1; + addCycles(16); + break; + + case 0xFF: + set(7, AF.hi); + PC += 1; + addCycles(8); + break; + + default: + printf("Unsupported extended opcode found: PC:0x%.2x, Opcode:0xcb%.2x\n", PC, addressSpace[PC]); + exit(1); + } +} diff --git a/src/gameboy.cpp b/src/gameboy.cpp index 9c92479..a835277 100644 --- a/src/gameboy.cpp +++ b/src/gameboy.cpp @@ -1,7 +1,7 @@ #include #include "gameboy.hpp" -bool AddressSpace::getBootromState() { +bool AddressSpace::getBootromState() const { return bootromLoaded; } @@ -13,10 +13,9 @@ void AddressSpace::mapBootrom() { bootromLoaded = true; } -void AddressSpace::loadBootrom(std::string filename) { +void AddressSpace::loadBootrom(const std::string& filename) { std::ifstream file; - int size = std::filesystem::file_size(filename); - if (size != 256) { + if (const uintmax_t size = std::filesystem::file_size(filename); size != 256) { std::cerr << "Bootrom was an unexpected size!\nQuitting!\n" << std::endl; exit(1); } @@ -24,7 +23,7 @@ void AddressSpace::loadBootrom(std::string filename) { file.read(reinterpret_cast(bootrom), BOOTROM_SIZE); } -void AddressSpace::loadGame(std::string filename) { +void AddressSpace::loadGame(const std::string& filename) { game.open(filename, std::ios::binary); if (!game.is_open()) { @@ -34,8 +33,11 @@ void AddressSpace::loadGame(std::string filename) { 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(const uint8_t ticks) { + cycles += ticks; + if (ppuEnabled) { + ppuCycles += ticks; + } lastOpTicks = ticks; } @@ -43,28 +45,52 @@ void GameBoy::start(std::string bootrom, std::string game) { addressSpace.loadBootrom(bootrom); addressSpace.loadGame(game); + //init some registers that won't otherwise by set + (*JOYP) = 0xCF; + (*SC) = 0x7E; + bool quit = false; - uint32_t cyclesSince = 0; + + bool display = false; while (!quit) { // Event loop: Check and handle SDL events - if (SDL_PollEvent(&event)) { + while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; // Set the quit flag when the close button is hit } } - opcodeHandler(); - interruptHandler(); - //timing(); - ppuUpdate(); if (PC > 0xFF && addressSpace.getBootromState()) { addressSpace.unmapBootrom(); } - cyclesSince = cyclesSinceLastRefresh(); - if (cyclesSince > FRAME_DURATION) { - lastRefresh = cycles; - SDL2present(); + ppuEnabled = (*LCDC) & 0x80; + + // if (PC == 0x100) + // 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("\n"); + // } + + opcodeResolver(); + interruptHandler(); + timingHandler(); + if (ppuEnabled) { + ppuUpdate(); + } + else { + ppuCycles = 2; + lastScanline = 0; + lastRefresh = 0; + (*LY) = 0x00; + (*STAT) &= 0xfc; } } } diff --git a/src/gameboy.hpp b/src/gameboy.hpp index 2526394..e00d9d0 100644 --- a/src/gameboy.hpp +++ b/src/gameboy.hpp @@ -35,9 +35,8 @@ union RegisterPair { }; class AddressSpace { -private: bool bootromLoaded = true; - Byte bootrom[BOOTROM_SIZE]; + Byte bootrom[BOOTROM_SIZE] = {0}; std::ifstream game; public: @@ -65,50 +64,54 @@ public: Byte specialRam[0x7F]; // Mapped to 0xFF80 Byte interuptEnableReg; // Mapped to 0xFFFF }; - } memoryLayout; + } memoryLayout{}; - bool getBootromState(); void unmapBootrom(); void mapBootrom(); - void loadBootrom(std::string filename); - void loadGame(std::string filename); + bool getBootromState() const; + void loadBootrom(const std::string& filename); + void loadGame(const std::string& filename); //overload [] for echo ram and bootrom support - Byte operator[](uint32_t address) const { + Byte operator[](const uint32_t address) const { if (address >= 0xE000 && address < 0xFE00) - return memoryLayout.echoRam[address - 0xE000]; + return memoryLayout.echoRam[address - 0x2000]; if (address < 0x0100 && bootromLoaded) return bootrom[address]; - else - return memoryLayout.memory[address]; + + return memoryLayout.memory[address]; } - Byte& operator[](uint32_t address) { + Byte& operator[](const uint32_t address) { if (address >= 0xE000 && address < 0xFE00) - return memoryLayout.echoRam[address - 0xE000]; + return memoryLayout.echoRam[address - 0x2000]; if (address < 0x0100 && bootromLoaded) return bootrom[address]; - else - return memoryLayout.memory[address]; + + return memoryLayout.memory[address]; } }; class GameBoy { -private: - uint32_t cycles = 0; - uint32_t lastOpTicks = 0; - uint32_t lastRefresh = 0; - uint32_t lastScanline = 0; - uint32_t cyclesToStayInHblank = -1; + //T-cycles not M-cycles (4 T-cycles = 1 M-cycle) + uint64_t cycles = 0; + //Start at 2 T-cycles https://github.com/Gekkio/mooneye-test-suite/blob/main/acceptance/ppu/lcdon_timing-GS.s + uint64_t ppuCycles = 2; + bool ppuEnabled = false; + uint64_t lastOpTicks = 0; + uint64_t lastRefresh = 0; + uint64_t lastScanline = 0; + uint64_t cyclesToStayInHblank = -1; + uint64_t lastDivUpdate = 0; uint8_t IME = 0; //enables interupts //Accumulator and flags - RegisterPair AF; + RegisterPair AF = {0}; //General purpose CPU registers - RegisterPair BC; - RegisterPair DE; - RegisterPair HL; + RegisterPair BC = {0}; + RegisterPair DE = {0}; + RegisterPair HL = {0}; Word SP = 0xFFFE; //stack pointer Word PC = 0x0000; //program counter @@ -116,70 +119,74 @@ private: AddressSpace addressSpace; //General purpose hardware registers - Byte* JOYP = &addressSpace[0xFF00]; - Byte* SB = &addressSpace[0xFF01]; - Byte* SC = &addressSpace[0xFF02]; - Byte* DIV = &addressSpace[0xFF04]; + Byte* const JOYP = &addressSpace[0xFF00]; + + Byte* const SB = &addressSpace[0xFF01]; + Byte* const SC = &addressSpace[0xFF02]; + Byte* const DIV = &addressSpace[0xFF04]; //Timer registers - Byte* TIMA = &addressSpace[0xFF05]; - Byte* TMA = &addressSpace[0xFF15]; //unused - Byte* TAC = &addressSpace[0xFF16]; + Byte* const TIMA = &addressSpace[0xFF05]; + Byte* const TMA = &addressSpace[0xFF15]; //unused + Byte* const TAC = &addressSpace[0xFF16]; //interrupt flag and enable - Byte* IF = &addressSpace[0xFF0F]; - Byte* IE = &addressSpace[0xFFFF]; + Byte* const IF = &addressSpace[0xFF0F]; + Byte* const IE = &addressSpace[0xFFFF]; //Sound registers - Byte* NR10 = &addressSpace[0xFF10]; - Byte* NR11 = &addressSpace[0xFF11]; - Byte* NR12 = &addressSpace[0xFF12]; - Byte* NR13 = &addressSpace[0xFF13]; - Byte* NR14 = &addressSpace[0xFF14]; - Byte* NR20 = &addressSpace[0xFF15]; //unused - Byte* NR21 = &addressSpace[0xFF16]; - Byte* NR22 = &addressSpace[0xFF17]; - Byte* NR23 = &addressSpace[0xFF18]; - Byte* NR24 = &addressSpace[0xFF19]; - Byte* NR30 = &addressSpace[0xFF1A]; - Byte* NR31 = &addressSpace[0xFF1B]; - Byte* NR32 = &addressSpace[0xFF1C]; - Byte* NR33 = &addressSpace[0xFF1D]; - Byte* NR34 = &addressSpace[0xFF1E]; - Byte* NR40 = &addressSpace[0xFF1F]; //unused - Byte* NR41 = &addressSpace[0xFF20]; - Byte* NR42 = &addressSpace[0xFF21]; - Byte* NR43 = &addressSpace[0xFF22]; - Byte* NR44 = &addressSpace[0xFF23]; - Byte* NR50 = &addressSpace[0xFF24]; - Byte* NR51 = &addressSpace[0xFF25]; - Byte* NR52 = &addressSpace[0xFF26]; - Byte* waveRam = &addressSpace[0xFF30]; //WaveRam[0x10] + 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* LCDC = &addressSpace[0xFF40]; - Byte* STAT = &addressSpace[0xFF41]; - Byte* SCY = &addressSpace[0xFF42]; - Byte* SCX = &addressSpace[0xFF43]; - Byte* LY = &addressSpace[0xFF44]; - Byte* LYC = &addressSpace[0xFF45]; - Byte* DMA = &addressSpace[0xFF46]; - Byte* BGP = &addressSpace[0xFF47]; - Byte* OBP0 = &addressSpace[0xFF48]; - Byte* OBP1 = &addressSpace[0xFF49]; - Byte* WY = &addressSpace[0xFF4A]; - Byte* WX = &addressSpace[0xFF4B]; + 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]; - PPUMode currentMode; + PPUMode currentMode = PPUMode::mode0; //3 colour channels uint32_t* framebuffer = new uint32_t[RESOLUTION_X * RESOLUTION_Y * SCREEN_BPP]; - SDL_Window* screen; - SDL_Renderer* renderer; - SDL_Texture* texture; - SDL_Event event; + SDL_Window* screen = nullptr; + SDL_Renderer* renderer = nullptr; + SDL_Texture* texture = nullptr; + SDL_Event event = {0}; + uint32_t frameStart = 0; + uint32_t frameTime = 0; + const int frameDelay = 1000 / V_SYNC; - void opcodeHandler(); + void opcodeResolver(); void incLY(); void ppuUpdate(); void drawLine(); @@ -187,12 +194,14 @@ private: void checkPPUMode(); void setPPUMode(PPUMode mode); - uint32_t cyclesSinceLastScanline(); - uint32_t cyclesSinceLastRefresh(); + uint64_t cyclesSinceLastScanline() const; + uint64_t cyclesSinceLastRefresh() const; + + void timingHandler(); void interruptHandler(); - bool testInterruptEnabled(Byte interrupt); - void resetInterrupt(Byte interrupt); + bool testInterruptEnabled(Byte interrupt) const; + void resetInterrupt(Byte interrupt) const; void VBlankHandle(); void LCDStatHandle(); @@ -220,17 +229,24 @@ private: void andBitwise(T& dest, T src); template void xorBitwise(T& dest, T src); - template - void bit(T testBit, T reg); + 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); template void jp(T address); template bool jrNZ(T offset); + template + bool jrNC(T offset); + template + bool jrC(T offset); template void inc(T& reg); template void call(T address); void halt(); + void daa(); void stop(); template void ldW(T dest, T src); @@ -242,29 +258,41 @@ private: bool jrZ(T offset); template void sub(T value); + template + void sbc(T value); template void jr(T OFFSET); template void push(T reg); - template - void rl(T& reg); + void rl(Byte& reg); + void sla(Byte& reg); + void sra(uint8_t& reg); + void srl(uint8_t& reg); + void rrc(Byte& reg); + void rrca(); + void rra(); + void rr(Byte& reg); + void rlc(Byte& reg); + void rlca(); + void rla(); template void pop(T& reg); template - void rla(T& reg); - template void rst(T address); void ret(); template void add(T& reg, T value); + template + void adc(T& reg, T value); void cpl(); + void scf(); void ccf(); void swap(Byte& value); public: void start(std::string bootrom, std::string game); void SDL2setup(); - void SDL2destroy(); + void SDL2destroy() const; }; #endif //GBPP_SRC_GAMEBOY_HPP_ diff --git a/src/interupts.cpp b/src/interupts.cpp index 2b1a2f1..5b1b33f 100644 --- a/src/interupts.cpp +++ b/src/interupts.cpp @@ -1,32 +1,33 @@ #include "defines.hpp" #include "gameboy.hpp" -bool GameBoy::testInterruptEnabled(Byte interrupt) { - return (*IE) & (Byte)(1 << interrupt); +bool GameBoy::testInterruptEnabled(const Byte interrupt) const { + return (*IE) & static_cast(1 << interrupt); } -void GameBoy::resetInterrupt(Byte interrupt) { +void GameBoy::resetInterrupt(const Byte interrupt) const { *IF &= ~(1 << interrupt); + *IF |= 0xE0; } void GameBoy::interruptHandler() { if (!IME) return; - if (*IF & (Byte)(1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT)) + if (*IF & static_cast(1 << VBLANK_INTERRUPT) && testInterruptEnabled(VBLANK_INTERRUPT)) VBlankHandle(); - if (*IF & (Byte)(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT)) + if (*IF & static_cast(1 << LCD_STAT_INTERRUPT) && testInterruptEnabled(LCD_STAT_INTERRUPT)) LCDStatHandle(); - if (*IF & (Byte)(1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT)) + if (*IF & static_cast(1 << TIMER_INTERRUPT) && testInterruptEnabled(TIMER_INTERRUPT)) timerHandle(); - if (*IF & (Byte)(1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT)) + if (*IF & static_cast(1 << SERIAL_INTERRUPT) && testInterruptEnabled(SERIAL_INTERRUPT)) serialHandle(); - if (*IF & (Byte)(1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT)) + if (*IF & static_cast(1 << JOYPAD_INTERRUPT) && testInterruptEnabled(JOYPAD_INTERRUPT)) joypadHandle(); } void GameBoy::VBlankHandle() { - printf("VBlank interrupt"); + //printf("VBlank interrupt\n"); IME = 0; push(PC); PC = 0x40; @@ -34,7 +35,7 @@ void GameBoy::VBlankHandle() { } void GameBoy::LCDStatHandle() { - printf("LCD stat interrupt"); + //printf("LCD stat interrupt\n"); IME = 0; push(PC); addCycles(16); @@ -43,7 +44,7 @@ void GameBoy::LCDStatHandle() { } void GameBoy::timerHandle() { - printf("timer interrupt"); + //printf("timer interrupt\n"); IME = 0; push(PC); addCycles(16); @@ -52,7 +53,7 @@ void GameBoy::timerHandle() { } void GameBoy::serialHandle() { - printf("serial interrupt"); + //printf("serial interrupt\n"); IME = 0; push(PC); addCycles(16); @@ -61,7 +62,7 @@ void GameBoy::serialHandle() { } void GameBoy::joypadHandle() { - printf("joypad interrupt"); + printf("joypad interrupt\n"); IME = 0; push(PC); addCycles(16); diff --git a/src/main.cpp b/src/main.cpp index b1a90de..a1919b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,8 +4,9 @@ int main(int argc, char** argv) { auto* gb = new GameBoy(); gb->SDL2setup(); - gb->start("../bootrom.bin", "../roms/DrMario.gb"); + gb->start("../dmg_boot.bin", "../roms/cpu_instrs.gb"); gb->SDL2destroy(); delete gb; + return 0; } diff --git a/src/opcodeResolver.cpp b/src/opcodeResolver.cpp new file mode 100644 index 0000000..7f131c8 --- /dev/null +++ b/src/opcodeResolver.cpp @@ -0,0 +1,2271 @@ +#include "gameboy.hpp" + +void GameBoy::setFlag(const Byte bit) { + AF.lo |= (1 << bit); +} + +void GameBoy::resetFlag(const Byte bit) { + AF.lo &= ~(1 << bit); +} + +bool GameBoy::getFlag(const Byte bit) const { + return (AF.lo >> bit) & 1; +} + +Word GameBoy::getWordPC() { + RegisterPair word = {0}; + + //remember little endianness + word.lo = addressSpace[PC + 1]; + word.hi = addressSpace[PC + 2]; + + return word.reg; +} + +Byte GameBoy::getBytePC() { + return addressSpace[PC + 1]; +} + +Word GameBoy::getWordSP() { + RegisterPair word = {0}; + + //remember little endianness + word.lo = addressSpace[SP++]; + word.hi = addressSpace[SP++]; + + return word.reg; +} + +Byte GameBoy::getByteSP() { + return addressSpace[SP++]; +} + +void GameBoy::ret() { + pop(PC); +} + +template +void GameBoy::ld(T& dest, T src) { + if constexpr (std::is_same_v) { + if (&dest == DIV) + *DIV = 0x00; + lastDivUpdate = cycles; + } + else { + dest = src; + } +} + + +template +void GameBoy::ldW(T dest, T src) { + if (sizeof(src) == sizeof(Word)) { + addressSpace[dest] = static_cast(src & 0xFF00) >> 8; + addressSpace[dest + 1] = static_cast(src & 0xFF); + } +} + +template +void GameBoy::add(T& reg, T value) { + if (sizeof(reg) == sizeof(Byte)) { + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xF) + (reg & 0xF)) & 0x10) == 0x10) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFF) + (reg & 0xFF)) & 0x100) == 0x100) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + } + + if (sizeof(reg) == sizeof(Word)) { + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFFF) + (reg & 0xFFF)) & 0x1000) == 0x1000) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFFFF) + (reg & 0xFFFF)) & 0x10000) == 0x10000) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + } + + reg += value; + + if (reg == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); +} + +template +void GameBoy::adc(T& reg, T value) { + T carry = getFlag(CARRY_FLAG) ? 1 : 0; + + if (sizeof(reg) == sizeof(Byte)) { + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xF) + (reg & 0xF)) & 0x10) == 0x10) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFF) + (reg & 0xFF)) & 0x100) == 0x100) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + } + + if (sizeof(reg) == sizeof(Word)) { + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFFF) + (reg & 0xFFF)) & 0x1000) == 0x1000) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((value & 0xFFFF) + (reg & 0xFFFF)) & 0x10000) == 0x10000) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + } + + reg += value + carry; + + if (reg == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); +} + +template +void GameBoy::sub(T value) { + if (AF.hi < value) { + setFlag(CARRY_FLAG); + resetFlag(ZERO_FLAG); + } + else if (AF.hi == value) { + setFlag(ZERO_FLAG); + resetFlag(CARRY_FLAG); + } + + AF.hi -= value; + + setFlag(SUBTRACT_FLAG); + //halfcarry test https://www.reddit.com/r/EmuDev/comments/4clh23/trouble_with_halfcarrycarry_flag/ + if (0 > (((AF.hi) & 0xf) - (value & 0xf))) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); +} + +template +void GameBoy::sbc(T value) { + T carry = getFlag(CARRY_FLAG) ? 1 : 0; + T result = AF.hi - value - carry; + + if (AF.hi < value + carry) { + setFlag(CARRY_FLAG); + } + else { + resetFlag(CARRY_FLAG); + } + + if (result == 0) { + setFlag(ZERO_FLAG); + } + else { + resetFlag(ZERO_FLAG); + } + + setFlag(SUBTRACT_FLAG); + + if ((AF.hi & 0xF) < (value & 0xF) + carry) { + setFlag(HALFCARRY_FLAG); + } + else { + resetFlag(HALFCARRY_FLAG); + } + + AF.hi = result; +} + +//https://gbdev.gg8.se/wiki/articles/DAA +void GameBoy::daa() { + if (getFlag(SUBTRACT_FLAG)) { + if (getFlag(CARRY_FLAG)) { + AF.hi -= 0x60; + } + if (getFlag(HALFCARRY_FLAG)) { + AF.hi -= 0x06; + } + } + else { + if (getFlag(CARRY_FLAG) || (AF.hi & 0xFF) > 0x99) { + AF.hi += 0x60; + setFlag(CARRY_FLAG); + } + if (getFlag(HALFCARRY_FLAG) || (AF.hi & 0x0F) > 0x09) { + AF.hi += 0x06; + } + } + + if (AF.hi == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +template +void GameBoy::orBitwise(T& dest, T src) { + dest |= src; + + if (dest == 0) + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); + resetFlag(CARRY_FLAG); +} + +template +void GameBoy::andBitwise(T& dest, T src) { + dest &= src; + + if (dest == 0) + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + setFlag(HALFCARRY_FLAG); + resetFlag(CARRY_FLAG); +} + +template +void GameBoy::xorBitwise(T& dest, T src) { + dest ^= src; + + if (dest == 0) + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(CARRY_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::bit(Byte testBit, Byte reg) { + if (const Byte result = reg & (1 << testBit); result == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + setFlag(HALFCARRY_FLAG); +} + +void GameBoy::set(const Byte testBit, Byte& reg) { + reg |= (1 << testBit); +} + +void GameBoy::res(const Byte testBit, Byte& reg) { + reg &= ~(1 << testBit); +} + + +template +void GameBoy::jr(T offset) { + PC += static_cast(offset) + 2; //PC moves 2 from original instruction +} + +template +bool GameBoy::jrNZ(T offset) { + bool jumped = false; + if (!getFlag(ZERO_FLAG)) //if not set + { + PC += static_cast(offset) + 2; //PC moves 2 from the original instruction + jumped = true; + } + + return jumped; +} + +template +bool GameBoy::jrZ(T offset) { + bool jumped = false; + if (getFlag(ZERO_FLAG)) //if not set + { + PC += static_cast(offset) + 2; //PC moves 2 from the original instruction + jumped = true; + } + + return jumped; +} + +template +bool GameBoy::jrNC(T offset) { + bool jumped = false; + if (!getFlag(CARRY_FLAG)) //if not set + { + PC += static_cast(offset) + 2; //PC moves 2 from the original instruction + jumped = true; + } + + return jumped; +} + +template +bool GameBoy::jrC(T offset) { + bool jumped = false; + if (getFlag(CARRY_FLAG)) //if not set + { + PC += static_cast(offset) + 2; //PC moves 2 from the original instruction + jumped = true; + } + + return jumped; +} + +template +void GameBoy::inc(T& reg) { + reg += 1; + + if (sizeof(reg) == sizeof(Byte)) { + if (reg == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((reg & 0x0F) == 0) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + } +} + +template +void GameBoy::call(T address) { + push(PC + 3); + PC = address; +} + +template +void GameBoy::cp(T value) //compare +{ + if (AF.hi < value) { + setFlag(CARRY_FLAG); + resetFlag(ZERO_FLAG); + } + else if (AF.hi == value) { + setFlag(ZERO_FLAG); + resetFlag(CARRY_FLAG); + } + + setFlag(SUBTRACT_FLAG); + + //halfcarry test https://www.reddit.com/r/EmuDev/comments/4clh23/trouble_with_halfcarrycarry_flag/ + if (0 > (((AF.hi) & 0xf) - (value & 0xf))) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); +} + +template +void GameBoy::dec(T& reg) { + reg -= 1; + + if (sizeof(reg) == sizeof(Byte)) { + if (reg == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + setFlag(SUBTRACT_FLAG); + + //halfcarry test https://www.reddit.com/r/EmuDev/comments/4clh23/trouble_with_halfcarrycarry_flag/ + if (0 > (((reg + 1) & 0xf) - (reg & 0xf))) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + } +} + +void GameBoy::swap(Byte& value) { + // Extract the lower and upper nibbles of the register + const Byte lowerNibble = value & 0x0F; + const Byte upperNibble = (value >> 4) & 0x0F; + + // Swap the lower and upper nibbles + value = (lowerNibble << 4) | upperNibble; + if (value == 0) + setFlag(ZERO_FLAG); + else + resetFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); + resetFlag(CARRY_FLAG); +} + +void GameBoy::halt() {} + +void GameBoy::rrc(Byte& reg) { + const Byte lsb = reg & 0x01; + reg >>= 1; + + if (lsb) + reg |= 0x80; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rrca() { + const Byte lsb = AF.hi & 0x01; + AF.hi >>= 1; + + if (lsb) + AF.hi |= 0x80; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rra() { + const Byte lsb = AF.hi & 0x01; + AF.hi >>= 1; + + if (getFlag(CARRY_FLAG)) + AF.hi |= 0x80; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rr(Byte& reg) { + const Byte lsb = reg & 0x01; + + reg >>= 1; + + if (getFlag(CARRY_FLAG)) + AF.hi |= 0x80; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rlc(Byte& reg) { + const Byte msb = (reg & 0x80) >> 7; + reg <<= 1; + + reg |= msb; + + if (msb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rlca() { + const Byte msb = (AF.hi & 0x80) >> 7; + AF.hi <<= 1; + + AF.hi |= msb; + + if (msb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rla() { + const Byte msb = (AF.hi & 0x80) >> 7; + AF.hi <<= 1; + + if (getFlag(CARRY_FLAG)) + AF.hi |= 0x01; + + if (msb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::rl(Byte& reg) { + const Byte msb = (reg & 0x80) >> 7; + + reg <<= 1; + + if (getFlag(CARRY_FLAG)) + reg |= 1; + + if (msb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::sla(Byte& reg) { + const Byte msb = (reg & 0x80) >> 7; + + reg <<= 1; + + if (msb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::sra(Byte& reg) { + const Byte msb = (reg & 0x80) >> 7; + const Byte lsb = reg & 0x1; + + reg >>= 1; + + if (msb) + reg |= 0x80; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::srl(Byte& reg) { + const Byte lsb = reg & 0x1; + + reg >>= 1; + + if (lsb) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + if (reg) + resetFlag(ZERO_FLAG); + else + setFlag(ZERO_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +template +void GameBoy::pop(T& reg) { + reg = getWordSP(); +} + +template +void GameBoy::push(T reg) { + //little endian + RegisterPair temp = {0}; + temp.lo = reg & 0xFF; + temp.hi = reg >> 8; + SP--; + addressSpace[SP] = temp.hi; + SP--; + addressSpace[SP] = temp.lo; +} + +template +void GameBoy::jp(T address) { + PC = address; +} + +template +void GameBoy::rst(T address) { + push(PC); + PC = address; +} + +void GameBoy::cpl() { + AF.hi = ~AF.hi; + setFlag(SUBTRACT_FLAG); + setFlag(HALFCARRY_FLAG); +} + +void GameBoy::scf() { + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); + setFlag(CARRY_FLAG); +} + +void GameBoy::ccf() { + if (getFlag(CARRY_FLAG)) + resetFlag(CARRY_FLAG); + else + setFlag(CARRY_FLAG); + + resetFlag(SUBTRACT_FLAG); + resetFlag(HALFCARRY_FLAG); +} + +void GameBoy::stop() {} + +void GameBoy::opcodeResolver() { + if (addressSpace[PC] != 0xCB) { + bool jumped; + switch (addressSpace[PC]) { + case 0x00: + //NOP + PC += 1; + addCycles(4); + break; + + case 0x01: + ld(BC.reg, getWordPC()); + PC += 3; + addCycles(12); + break; + + case 0x02: + ld(addressSpace[BC.reg], AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x03: + BC.reg += 1; + PC += 1; + addCycles(8); + break; + + case 0x04: + inc(BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x05: + dec(BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x06: + ld(BC.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x07: + rlca(); + PC += 1; + addCycles(4); + break; + + case 0x08: + ldW(getWordPC(), SP); + PC += 3; + addCycles(20); + break; + + case 0x09: + add(HL.reg, BC.reg); + PC += 1; + addCycles(8); + break; + + case 0x0A: + ld(AF.hi, addressSpace[BC.reg]); + PC += 1; + addCycles(8); + break; + + case 0x0B: + BC.reg -= 1; + PC += 1; + addCycles(8); + break; + + case 0x0C: + inc(BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x0D: + dec(BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x0E: + ld(BC.lo, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x0F: + rrca(); + PC += 1; + addCycles(4); + break; + + case 0x10: + stop(); + PC += 2; + addCycles(4); + break; + + case 0x11: + ld(DE.reg, getWordPC()); + PC += 3; + addCycles(12); + break; + + case 0x12: + ld(addressSpace[BC.reg], AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x13: + DE.reg++; //no flags change no just inc it manually + PC += 1; + addCycles(8); + break; + + case 0x14: + inc(DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x15: + dec(DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x16: + ld(DE.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x17: + rla(); + PC += 1; + addCycles(4); + break; + + case 0x18: + jr(getBytePC()); + addCycles(12); + break; + + case 0x19: + add(HL.reg, BC.reg); + PC += 1; + addCycles(8); + break; + + case 0x1A: + ld(AF.hi, addressSpace[DE.reg]); + PC += 1; + addCycles(8); + break; + + case 0x1B: + DE.reg -= 1; + PC += 1; + addCycles(8); + break; + + case 0x1C: + inc(DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x1D: + dec(DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x1E: + ld(DE.lo, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x1F: + rra(); + PC += 1; + addCycles(4); + break; + + case 0x20: + jumped = jrNZ(getBytePC()); + if (jumped) { + addCycles(12); + } + else { + PC += 2; + addCycles(8); + } + break; + + case 0x21: + ld(HL.reg, getWordPC()); + PC += 3; + addCycles(12); + break; + + case 0x22: + ld(addressSpace[HL.reg], AF.hi); + HL.reg++; + PC += 1; + addCycles(8); + break; + + case 0x23: + inc(HL.reg); + PC += 1; + addCycles(8); + break; + + case 0x24: + inc(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x25: + dec(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x26: + ld(HL.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x27: + daa(); + PC += 1; + addCycles(4); + break; + + case 0x28: + jumped = jrZ(getBytePC()); + if (jumped) { + addCycles(12); + } + else { + PC += 2; + addCycles(8); + } + break; + + case 0x29: + add(HL.reg, HL.reg); + PC += 1; + addCycles(8); + break; + + case 0x2A: + ld(AF.hi, addressSpace[HL.reg]); + HL.reg += 1; + PC += 1; + addCycles(8); + break; + + case 0x2B: + dec(HL.reg); + PC += 1; + addCycles(8); + break; + + case 0x2C: + inc(HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x2D: + dec(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x2E: + ld(HL.lo, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x2F: + cpl(); + PC += 1; + addCycles(4); + break; + + case 0x30: + jumped = jrNC(getBytePC()); + if (jumped) { + addCycles(12); + } + else { + PC += 2; + addCycles(8); + } + break; + + case 0x31: + ld(SP, getWordPC()); + PC += 3; + addCycles(12); + break; + + case 0x32: + ld(addressSpace[HL.reg], AF.hi); + HL.reg--; + PC += 1; + addCycles(8); + break; + + case 0x33: + SP += 1; + PC += 1; + addCycles(8); + break; + + case 0x34: + inc(addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x35: + dec(addressSpace[HL.reg]); + PC += 1; + addCycles(12); + break; + + case 0x36: + ld(addressSpace[HL.reg], getBytePC()); + PC += 2; + addCycles(12); + break; + + case 0x37: + scf(); + PC += 1; + addCycles(4); + break; + + case 0x38: + jumped = jrC(getBytePC()); + if (jumped) { + addCycles(12); + } + else { + PC += 2; + addCycles(8); + } + break; + + case 0x39: + add(HL.reg, SP); + PC += 1; + addCycles(8); + break; + + case 0x3A: + ld(AF.hi, addressSpace[HL.reg]); + HL.reg -= 1; + PC += 1; + addCycles(8); + break; + + case 0x3B: + SP--; + PC += 1; + addCycles(8); + break; + + case 0x3C: + inc(AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x3D: + dec(AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x3E: + ld(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0x3F: + ccf(); + PC += 1; + addCycles(4); + break; + + case 0x40: + ld(BC.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x41: + ld(BC.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x42: + ld(BC.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x43: + ld(BC.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x44: + ld(BC.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x45: + ld(BC.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x46: + ld(BC.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x47: + ld(BC.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x48: + ld(BC.lo, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x49: + ld(BC.lo, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x4A: + ld(BC.lo, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x4B: + ld(BC.lo, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x4C: + ld(BC.lo, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x4D: + ld(BC.lo, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x4E: + ld(BC.lo, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x4F: + ld(BC.lo, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x50: + ld(DE.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x51: + ld(DE.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x52: + ld(DE.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x53: + ld(DE.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x54: + ld(DE.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x55: + ld(DE.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x56: + ld(DE.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x57: + ld(DE.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x58: + ld(DE.lo, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x59: + ld(DE.lo, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x5A: + ld(DE.lo, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x5B: + ld(DE.lo, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x5C: + ld(DE.lo, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x5D: + ld(DE.lo, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x5E: + ld(DE.lo, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x5F: + ld(DE.lo, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x60: + ld(HL.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x61: + ld(HL.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x62: + ld(HL.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x63: + ld(HL.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x64: + ld(HL.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x65: + ld(HL.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x66: + ld(HL.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x67: + ld(HL.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x68: + ld(HL.lo, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x69: + ld(HL.lo, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x6A: + ld(HL.lo, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x6B: + ld(HL.lo, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x6C: + ld(HL.lo, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x6D: + ld(HL.lo, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x6E: + ld(HL.lo, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x6F: + ld(HL.lo, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x70: + ld(addressSpace[HL.reg], BC.hi); + PC += 1; + addCycles(8); + break; + + case 0x71: + ld(addressSpace[HL.reg], BC.lo); + PC += 1; + addCycles(8); + break; + + case 0x72: + ld(addressSpace[HL.reg], DE.hi); + PC += 1; + addCycles(8); + break; + + case 0x73: + ld(addressSpace[HL.reg], DE.lo); + PC += 1; + addCycles(8); + break; + + case 0x74: + ld(addressSpace[HL.reg], HL.hi); + PC += 1; + addCycles(8); + break; + + case 0x75: + ld(addressSpace[HL.reg], HL.lo); + PC += 1; + addCycles(8); + break; + + case 0x76: + halt(); + PC += 1; + addCycles(4); + break; + + case 0x77: + ld(addressSpace[HL.reg], AF.hi); + PC += 1; + addCycles(8); + break; + + case 0x78: + ld(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x79: + ld(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x7A: + ld(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x7B: + ld(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x7C: + ld(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x7D: + ld(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x7E: + ld(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x7F: + ld(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x80: + add(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x81: + add(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x82: + add(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x83: + add(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x84: + add(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x85: + add(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x86: + add(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x87: + add(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x88: + adc(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x89: + adc(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x8A: + adc(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x8B: + adc(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x8C: + adc(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x8D: + adc(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x8E: + adc(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x8F: + adc(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x90: + sub(BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x91: + sub(BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x92: + sub(DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x93: + sub(DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x94: + sub(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x95: + sub(HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x96: + sub(addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x97: + sub(AF.hi); + PC += 1; + addCycles(4); + break; + + case 0x98: + sbc(BC.hi); + PC += 1; + addCycles(4); + break; + + case 0x99: + sbc(BC.lo); + PC += 1; + addCycles(4); + break; + + case 0x9A: + sbc(DE.hi); + PC += 1; + addCycles(4); + break; + + case 0x9B: + sbc(DE.lo); + PC += 1; + addCycles(4); + break; + + case 0x9C: + sbc(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0x9D: + sbc(HL.lo); + PC += 1; + addCycles(4); + break; + + case 0x9E: + sbc(addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0x9F: + sbc(AF.hi); + PC += 1; + addCycles(4); + break; + + case 0xA0: + andBitwise(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0xA1: + andBitwise(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0xA2: + andBitwise(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0xA3: + andBitwise(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0xA4: + andBitwise(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0xA5: + andBitwise(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0xA6: + andBitwise(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0xA7: + andBitwise(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0xA8: + xorBitwise(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0xA9: + xorBitwise(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0xAA: + xorBitwise(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0xAB: + xorBitwise(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0xAC: + xorBitwise(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0xAD: + xorBitwise(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0xAE: + xorBitwise(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0xAF: + xorBitwise(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0xB0: + orBitwise(AF.hi, BC.hi); + PC += 1; + addCycles(4); + break; + + case 0xB1: + orBitwise(AF.hi, BC.lo); + PC += 1; + addCycles(4); + break; + + case 0xB2: + orBitwise(AF.hi, DE.hi); + PC += 1; + addCycles(4); + break; + + case 0xB3: + orBitwise(AF.hi, DE.lo); + PC += 1; + addCycles(4); + break; + + case 0xB4: + orBitwise(AF.hi, HL.hi); + PC += 1; + addCycles(4); + break; + + case 0xB5: + orBitwise(AF.hi, HL.lo); + PC += 1; + addCycles(4); + break; + + case 0xB6: + orBitwise(AF.hi, addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0xB7: + orBitwise(AF.hi, AF.hi); + PC += 1; + addCycles(4); + break; + + case 0xB8: + cp(BC.hi); + PC += 1; + addCycles(4); + break; + + case 0xB9: + cp(BC.lo); + PC += 1; + addCycles(4); + break; + + case 0xBA: + cp(DE.hi); + PC += 1; + addCycles(4); + break; + + case 0xBB: + cp(DE.lo); + PC += 1; + addCycles(4); + break; + + case 0xBC: + cp(HL.hi); + PC += 1; + addCycles(4); + break; + + case 0xBD: + cp(HL.lo); + PC += 1; + addCycles(4); + break; + + case 0xBE: + cp(addressSpace[HL.reg]); + PC += 1; + addCycles(8); + break; + + case 0xBF: + cp(AF.hi); + PC += 1; + addCycles(4); + break; + + case 0xC0: //RET NZ + if (!getFlag(ZERO_FLAG)) { + ret(); + addCycles(20); + } + else { + addCycles(8); + PC += 1; + } + break; + + case 0xC1: + pop(BC.reg); + PC += 1; + addCycles(12); + break; + + case 0xC2: + if (!getFlag(ZERO_FLAG)) { + jp(getWordPC()); + addCycles(16); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xC3: + jp(getWordPC()); + addCycles(16); + break; + + case 0xC4: + if (!getFlag(ZERO_FLAG)) { + call(getWordPC()); + addCycles(24); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xC5: + push(BC.reg); + PC += 1; + addCycles(16); + break; + + case 0xC6: + add(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xC7: + rst(0x0000); + addCycles(16); + break; + + case 0xC8: + if (getFlag(ZERO_FLAG)) { + ret(); + addCycles(20); + } + else { + addCycles(8); + PC += 1; + } + break; + + case 0xC9: + ret(); + addCycles(16); + break; + + case 0xCA: + if (getFlag(ZERO_FLAG)) { + jp(getWordPC()); + addCycles(16); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xCC: + if (getFlag(ZERO_FLAG)) { + call(getWordPC()); + addCycles(24); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xCD: + call(getWordPC()); + addCycles(24); + break; + + case 0xCE: + adc(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xCF: + rst(0x08); + addCycles(16); + break; + + case 0xD0: //RET NC + if (!getFlag(CARRY_FLAG)) { + ret(); + addCycles(20); + } + else { + addCycles(8); + PC += 3; + } + break; + + case 0xD1: + pop(DE.reg); + PC += 1; + addCycles(12); + break; + + case 0xD2: + if (!getFlag(CARRY_FLAG)) { + jp(getWordPC()); + addCycles(24); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xD4: + if (!getFlag(CARRY_FLAG)) { + call(getWordPC()); + addCycles(24); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xD5: + push(DE.reg); + PC += 1; + addCycles(16); + break; + + case 0xD6: + sub(getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xD7: + rst(0x0010); + addCycles(16); + break; + + case 0xD8: + if (getFlag(CARRY_FLAG)) { + ret(); + addCycles(20); + } + else { + addCycles(8); + PC += 1; + } + break; + + //reti + case 0xD9: + IME = 1; + ret(); + addCycles(16); + break; + + case 0xDA: + if (getFlag(CARRY_FLAG)) { + jp(getWordPC()); + addCycles(16); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xDC: + if (getFlag(CARRY_FLAG)) { + call(getWordPC()); + addCycles(24); + } + else { + addCycles(12); + PC += 3; + } + break; + + case 0xDE: + sbc(getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xDF: + rst(0x18); + addCycles(16); + break; + + case 0xE0: + ld(addressSpace[0xFF00 + getBytePC()], AF.hi); + PC += 2; + addCycles(12); + break; + + case 0xE1: + pop(HL.reg); + PC += 1; + addCycles(12); + break; + + case 0xE2: + ld(addressSpace[BC.lo + 0xFF00], AF.hi); + PC += 1; + addCycles(8); + break; + + case 0xE5: + push(HL.reg); + PC += 1; + addCycles(16); + break; + + case 0xE6: + andBitwise(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xE7: + rst(0x0020); + addCycles(16); + break; + + case 0xE8: + { + const auto immediate = static_cast(getBytePC()); + const Word result = SP + static_cast(immediate); + + if (((SP ^ immediate ^ result) & 0x10) != 0) + setFlag(HALFCARRY_FLAG); + else + resetFlag(HALFCARRY_FLAG); + + if (((SP ^ immediate ^ result) & 0x100) != 0) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + + SP = result; + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + + PC += 2; + addCycles(16); + } + break; + + case 0xE9: + jp(HL.reg); + addCycles(16); + break; + + case 0xEA: + ld(addressSpace[getWordPC()], AF.hi); + PC += 3; + addCycles(16); + break; + + case 0xEE: + xorBitwise(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xEF: + rst(0x28); + addCycles(16); + break; + + case 0xF0: + ld(AF.hi, addressSpace[0xFF00 + getBytePC()]); + PC += 2; + addCycles(12); + break; + + case 0xF1: + pop(AF.reg); + PC += 1; + addCycles(12); + break; + + case 0xF2: + ld(AF.hi, addressSpace[0xFF00 + BC.lo]); + PC += 1; + addCycles(12); + break; + + case 0xF3: + IME = 0; + PC += 1; + addCycles(4); + break; + + case 0xF5: + push(AF.reg); + PC += 1; + addCycles(16); + break; + + case 0xF6: + orBitwise(AF.hi, getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xF7: + rst(0x0030); + addCycles(16); + break; + + case 0xF8: + { + const auto n = static_cast(getBytePC()); + const Word result = SP + n; + + //halfcarry test https://robdor.com/2016/08/10/gameboy-emulator-half-carry-flag/ + if ((((result & 0xF) + (HL.reg & 0xF)) & 0x10) == 0x10) + 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) + setFlag(CARRY_FLAG); + else + resetFlag(CARRY_FLAG); + HL.reg = result; // Load the result into HL + + resetFlag(ZERO_FLAG); + resetFlag(SUBTRACT_FLAG); + + PC += 2; + addCycles(12); + } + break; + + case 0xF9: + ld(SP, HL.reg); + PC += 1; + addCycles(8); + break; + + case 0xFA: + ldW(AF.hi, addressSpace[getWordPC()]); + PC += 3; + addCycles(16); + break; + + //should not actually enable until the next opcode + //EI (0xFB) then DI (0xF3) does not allow interrupts to happen + case 0xFB: + IME = 1; + PC += 1; + addCycles(4); + break; + + case 0xFE: + cp(getBytePC()); + PC += 2; + addCycles(8); + break; + + case 0xFF: + rst(0x38); + addCycles(16); + break; + + default: + printf("Unsupported opcode found: PC:0x%.2x, Opcode:0x%.2x\n", PC, addressSpace[PC]); + exit(1); + } + } + else + extendedOpcodeResolver(); +} diff --git a/src/ppu.cpp b/src/ppu.cpp index 1622ddc..eafbcf8 100644 --- a/src/ppu.cpp +++ b/src/ppu.cpp @@ -2,52 +2,12 @@ #include "defines.hpp" #include #include +#include void GameBoy::ppuUpdate() { //test for HBlank checkPPUMode(); - if (cyclesToStayInHblank != -1) { - if (cyclesToStayInHblank < cyclesSinceLastScanline()) { - return; - } - if (cyclesToStayInHblank >= cyclesSinceLastScanline()) { - lastScanline = cycles; - cyclesToStayInHblank = -1; - } - } - // Check the PPU mode (HBlank, VBlank, OAM Search, or Pixel Transfer) - Byte mode = (*STAT) & 0x03; - switch (mode) { - case 0: - if (cyclesSinceLastScanline() > MODE2_DURATION + MODE3_MIN_DURATION) { - drawLine(); - cyclesToStayInHblank = SCANLINE_DURATION - cyclesSinceLastScanline(); - lastScanline = cycles; - incLY(); - } - currentMode = PPUMode::mode0; - break; - - //vblank - case 1: - if (currentMode != PPUMode::mode1) { - setPPUMode(PPUMode::mode1); - *IF |= 0x1; - } - if (cyclesSinceLastScanline() > SCANLINE_DURATION) { - lastScanline = cycles; - incLY(); - } - currentMode = PPUMode::mode1; - break; - case 2: - currentMode = PPUMode::mode2; - break; - case 3: - currentMode = PPUMode::mode3; - break; - } if ((*LY) == (*LYC) || (*STAT) & (1 << 6)) { // Request STAT interrupt if LY matches LYC // bug on DMG models triggers a STAT interrupt anytime the STAT register is written @@ -70,56 +30,77 @@ void GameBoy::ppuUpdate() { } } +void GameBoy::checkPPUMode() { + // Check the PPU mode (HBlank, VBlank, OAM Search, or Pixel Transfer) + const uint64_t cyclesSinceScanline = cyclesSinceLastScanline(); + + switch (currentMode) { + //hblank and vblank + case 0: + case 1: + if (cyclesSinceScanline > SCANLINE_DURATION) { + lastScanline = ppuCycles - (cyclesSinceScanline - SCANLINE_DURATION); + incLY(); + } + break; + case 2: + if (cyclesSinceScanline > MODE2_DURATION) { + setPPUMode(PPUMode::mode3); + } + break; + case 3: + if (cyclesSinceScanline > MODE2_DURATION + MODE3_MIN_DURATION) { + drawLine(); + setPPUMode(PPUMode::mode0); + } + break; + } +} + void GameBoy::incLY() { (*LY)++; - if ((*LY) > 153) + setPPUMode(PPUMode::mode2); + if ((*LY) > SCANLINES_PER_FRAME - 1) { (*LY) = 0; -} - -uint32_t GameBoy::cyclesSinceLastScanline() { - const uint32_t difference = cycles - lastScanline; - return difference; -} - -uint32_t GameBoy::cyclesSinceLastRefresh() { - const uint32_t difference = cycles - lastRefresh; - return difference; -} - -void GameBoy::checkPPUMode() { - uint32_t oamFetchTime = 0; - if ((*LY) < 144) { - const uint32_t currentDuration = cyclesSinceLastScanline(); - // Active Display Period (HBlank, OAM Search, and Pixel Transfer) - if (currentDuration < MODE2_DURATION) - setPPUMode(PPUMode::mode2); - else if (currentDuration < MODE2_DURATION + MODE3_MIN_DURATION + oamFetchTime) - setPPUMode(PPUMode::mode3); - else - setPPUMode(PPUMode::mode0); } - else { + else if ((*LY) == 144) { // VBlank Period + SDL2present(); setPPUMode(PPUMode::mode1); + *IF |= 0x1; } } -void GameBoy::setPPUMode(PPUMode mode) { +uint64_t GameBoy::cyclesSinceLastScanline() const { + const uint64_t difference = ppuCycles - lastScanline; + return difference; +} + +uint64_t GameBoy::cyclesSinceLastRefresh() const { + const uint64_t difference = ppuCycles - lastRefresh; + return difference; +} + +void GameBoy::setPPUMode(const PPUMode mode) { switch (mode) { case PPUMode::mode0: (*STAT) &= ~0x03; + currentMode = PPUMode::mode0; break; case PPUMode::mode1: (*STAT) &= ~0x03; (*STAT) |= 0x01; + currentMode = PPUMode::mode1; break; case PPUMode::mode2: (*STAT) &= ~0x03; (*STAT) |= 0x02; + currentMode = PPUMode::mode2; break; case PPUMode::mode3: (*STAT) &= ~0x03; (*STAT) |= 0x03; + currentMode = PPUMode::mode3; break; } //7th bit is unused but always set @@ -127,10 +108,10 @@ void GameBoy::setPPUMode(PPUMode mode) { } void GameBoy::drawLine() { - uint8_t line = (*LY); + const uint8_t line = (*LY); // Calculate the starting index of the current scanline in the framebuffer - uint32_t lineStartIndex = line * RESOLUTION_X; + const uint32_t lineStartIndex = line * RESOLUTION_X; // Pointer to the current line's pixel data in the framebuffer uint32_t* currentLinePixels = framebuffer + lineStartIndex; @@ -140,20 +121,20 @@ void GameBoy::drawLine() { return; } - uint16_t backgroundMapAddr = (*LCDC & 0x08) ? 0x9C00 : 0x9800; - uint16_t tileDataTableAddr = (*LCDC & 0x10) ? 0x8000 : 0x8800; - bool signedIndex = !(*LCDC & 0x10); + const uint16_t backgroundMapAddr = (*LCDC & 0x08) ? 0x9C00 : 0x9800; + const uint16_t tileDataTableAddr = (*LCDC & 0x10) ? 0x8000 : 0x8800; + const bool signedIndex = !(*LCDC & 0x10); for (int pixel = 0; pixel < RESOLUTION_X; pixel++) { - uint8_t xPos = (pixel + (*SCX)) % 256; // 256 pixels in total BG width - uint8_t yPos = (line + (*SCY)) % 256; // 256 pixels in total BG height + 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 - uint16_t tileRow = (yPos / 8) * 32; - uint16_t tileCol = xPos / 8; - uint16_t tileIndex = tileRow + tileCol; + const uint16_t tileRow = (yPos / 8) * 32; + const uint16_t tileCol = xPos / 8; + const uint16_t tileIndex = tileRow + tileCol; - uint16_t tileAddr = backgroundMapAddr + tileIndex; - int8_t tileID = signedIndex ? static_cast(addressSpace[tileAddr]) : addressSpace[tileAddr]; + const uint16_t tileAddr = backgroundMapAddr + tileIndex; + const int8_t tileID = signedIndex ? static_cast(addressSpace[tileAddr]) : addressSpace[tileAddr]; uint16_t tileDataAddr; if (signedIndex) { @@ -163,12 +144,12 @@ void GameBoy::drawLine() { tileDataAddr = tileDataTableAddr + (tileID * 16); } - uint8_t lineOffset = yPos % 8; - uint8_t tileRowData1 = addressSpace[tileDataAddr + (lineOffset * 2)]; - uint8_t tileRowData2 = addressSpace[tileDataAddr + (lineOffset * 2) + 1]; + const uint8_t lineOffset = yPos % 8; + const uint8_t tileRowData1 = addressSpace[tileDataAddr + (lineOffset * 2)]; + const uint8_t tileRowData2 = addressSpace[tileDataAddr + (lineOffset * 2) + 1]; - uint8_t colourBit = 7 - (xPos % 8); - uint8_t colourNum = ((tileRowData2 >> colourBit) & 0x1) << 1 | ((tileRowData1 >> colourBit) & 0x1); + 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; @@ -196,14 +177,14 @@ void GameBoy::SDL2setup() { SDL_WINDOW_OPENGL); // Create an SDL renderer to draw on the window - renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_ACCELERATED); + renderer = SDL_CreateRenderer(screen, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); // Create an SDL texture to hold the framebuffer data texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, RESOLUTION_X, RESOLUTION_Y); } -void GameBoy::SDL2destroy() { +void GameBoy::SDL2destroy() const { SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(screen); @@ -211,13 +192,18 @@ void GameBoy::SDL2destroy() { } void GameBoy::SDL2present() { - // Update the SDL texture with the framebuffer data - SDL_UpdateTexture(texture, NULL, framebuffer, RESOLUTION_X * sizeof(uint32_t)); - - // Clear the renderer and render the texture + SDL_UpdateTexture(texture, nullptr, framebuffer, RESOLUTION_X * sizeof(uint32_t)); SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderCopy(renderer, texture, nullptr, nullptr); + + + frameTime = SDL_GetTicks() - frameStart; + std::cout << SDL_GetTicks() << " " << frameTime << std::endl; + + if (frameDelay > frameTime) { + SDL_Delay(frameDelay - frameTime); + } + frameStart = SDL_GetTicks(); - // Present the renderer on the screen SDL_RenderPresent(renderer); } diff --git a/src/timing.cpp b/src/timing.cpp new file mode 100644 index 0000000..5b8d5d4 --- /dev/null +++ b/src/timing.cpp @@ -0,0 +1,12 @@ +#include "gameboy.hpp" + +//handles most of the behavoir as described here: https://gbdev.io/pandocs/Timer_and_Divider_Registers.html#ff04--div-divider-register +void GameBoy::timingHandler() { + if (cycles - lastDivUpdate >= DIVIDER_REGISTER_FREQ) { + const uint8_t increments = (cycles - lastDivUpdate) / DIVIDER_REGISTER_FREQ; + + (*DIV) += increments; + + lastDivUpdate += increments * DIVIDER_REGISTER_FREQ; + } +}