#ifndef GBPP_SRC_GAMEBOY_HPP_ #define GBPP_SRC_GAMEBOY_HPP_ #include #include #include #include #include #include #include #include "defines.hpp" //two bits per colour enum Colour { black = 0b11, darkGray = 0b10, lightGray = 0b01, white = 0b00 }; 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. }; union RegisterPair { Word reg; //register.reg == (hi << 8) + lo. (hi is more significant than lo) struct { Byte lo; Byte hi; }; }; class AddressSpace { private: bool bootromLoaded = true; Byte bootrom[BOOTROM_SIZE]; std::ifstream 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]; 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 }; } memoryLayout; bool getBootromState(); void unmapBootrom(); void mapBootrom(); void loadBootrom(std::string filename); void loadGame(std::string filename); //overload [] for echo ram and bootrom support Byte operator[](uint32_t address) const { if(address >= 0xE000 && address < 0xFE00) return memoryLayout.echoRam[address - 0xE000]; if(address < 0x0100 && bootromLoaded) return bootrom[address]; else return memoryLayout.memory[address]; } Byte& operator[](uint32_t address) { if(address >= 0xE000 && address < 0xFE00) return memoryLayout.echoRam[address - 0xE000]; if(address < 0x0100 && bootromLoaded) return bootrom[address]; else 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; uint8_t IME = 0; //enables interupts //Accumulator and flags RegisterPair AF; //General purpose CPU registers RegisterPair BC; RegisterPair DE; RegisterPair HL; Word SP = 0xFFFE; //stack pointer Word PC = 0x0000; //program counter AddressSpace addressSpace; //General purpose hardware registers Byte* JOYP = &addressSpace[0xFF00]; Byte* SB = &addressSpace[0xFF01]; Byte* SC = &addressSpace[0xFF02]; Byte* DIV = &addressSpace[0xFF04]; //Timer registers Byte* TIMA = &addressSpace[0xFF05]; Byte* TMA = &addressSpace[0xFF15]; //unused Byte* TAC = &addressSpace[0xFF16]; //interrupt flag and enable Byte* IF = &addressSpace[0xFF0F]; Byte* 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] //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]; 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; SDL_Event event; void opcodeHandler(); void ppuUpdate(); void drawLine(); void SDL2present(); void checkPPUMode(); void setPPUMode(PPUMode mode); uint32_t cyclesSinceLastScanline(); uint32_t cyclesSinceLastRefresh(); void interruptHandler(); bool testInterruptEnabled(Byte interrupt); void resetInterrupt(Byte interrupt); void VBlankHandle(); void LCDStatHandle(); void timerHandle(); void serialHandle(); void joypadHandle(); void setFlag(Byte bit); void resetFlag(Byte bit); bool getFlag(Byte bit) const; Word getWordPC(); Byte getBytePC(); Word getWordSP(); Byte getByteSP(); void addCycles(Byte ticks); //OPCODE FUNCTIONS template void ld(T& dest, T src); template void orBitwise(T &dest, T src); template void andBitwise(T &dest, T src); template void xorBitwise(T& dest, T src); template void bit(T testBit, T reg); template void jp(T address); template bool jrNZ(T offset); template void inc(T& reg); template void call(T address); void halt(); void stop(); template void ldW(T dest, T src); template void cp(T value); template void dec(T& reg); template bool jrZ(T offset); template void sub(T value); template void jr(T OFFSET); template void push(T reg); template void rl(T& reg); template void pop(T& reg); template void rla(T& reg); template void rst(T address); void ret(); template void add(T& reg, T value); void cpl(); void ccf(); void swap(Byte &value); public: void start(std::string bootrom, std::string game); void SDL2setup(); void SDL2destroy(); }; #endif //GBPP_SRC_GAMEBOY_HPP_