commit d08fbfe1aa735935a7ae4a85761b34287043a87c Author: panzone Date: Mon Nov 24 13:37:49 2025 +0100 public release of panzgb diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..03920b6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +build/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0442578 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.7) +project(panzgb) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED True) + +# Make static library with pangb core +ADD_LIBRARY( panzgb-core STATIC + lib/gb-memory.c + lib/gb-opcodes.c + lib/gb-opcodes-impl.c + lib/gb-sound.c + lib/gb-video.c + lib/gc-imp.c + lib/gc-interrupts.c + lib/mbc1.c + lib/mbc3.c ) +# PC clients +find_package(SDL2 REQUIRED) +include_directories(panzgb ${SDL2_INCLUDE_DIRS}) +add_executable(panzgb panzgb.c) +target_link_libraries(panzgb ${SDL2_LIBRARIES} panzgb-core) diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..362af8c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2025 panzone + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100755 index 0000000..3b15716 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# panzgb + +panzgb is an emulator for the Gameboy system. + +## Build + +The PC client requires SDL2 installed on the system. Then panzgb uses cmake for building +``` +mkdir build && cd build +cmake .. +make +``` + +## Run + +``` +./panzgb ROM-FILE +``` + +## What is working + +- Basic support for running games +- Support for MBC1 and MBC3 games + +Most notably, the sound system is currently not working. Also, this is a pure Gameboy emulator and it doesn't support Gameboy Color games. diff --git a/lib/gb-impl.h b/lib/gb-impl.h new file mode 100755 index 0000000..40c803e --- /dev/null +++ b/lib/gb-impl.h @@ -0,0 +1,180 @@ +#ifndef GB_IMPL_H +#define GB_IMPL_H +#include "panzgb.h" + +#define NAME_CART 0x134 + +#define CARTRIDGE_SIZE 0x200000 +#define MEMORY_SIZE 0x10000 + +#define KEYMAP_ADDR 0xFF00 + +#define DIVIDER_TIMER 0xFF04 + +#define TIMER_ADDR 0xFF05 +#define TIMER_DEFAULT 0xFF06 +#define TIMER_CONTROLLER 0xFF07 + +#define INTERRUPT_ENABLED_ADDR 0xFFFF +#define INTERRUPT_REQUEST_ADDR 0xFF0F + +#define LCD_SCANLINE_ADRR 0xFF44 +#define LCD_REG_CONTROL 0xFF40 +#define LCD_REG_STATUS 0xFF41 + +#define DMA_ADRR 0xFF46 + +#define SCROLLX 0xFF43 +#define SCROLLY 0xFF42 + +#define WINDOWY 0xFF4A +#define WINDOWX 0xFF4B + +#define getAF(cpu) ((cpu->A << 8) | (cpu->F)) +#define getBC(cpu) ((cpu->B << 8) | (cpu->C)) +#define getDE(cpu) ((cpu->D << 8) | (cpu->E)) +#define getHL(cpu) ((cpu->H << 8) | (cpu->L)) + +#define SET_ZFLAG(cpu) (cpu->F |= 0x80) +#define SET_NFLAG(cpu) (cpu->F |= 0x40) +#define SET_HFLAG(cpu) (cpu->F |= 0x20) +#define SET_CFLAG(cpu) (cpu->F |= 0x10) + +#define RESET_ZFLAG(cpu) (cpu->F &= ~(0x80)) +#define RESET_NFLAG(cpu) (cpu->F &= ~(0x40)) +#define RESET_HFLAG(cpu) (cpu->F &= ~(0x20)) +#define RESET_CFLAG(cpu) (cpu->F &= ~(0x10)) + +#define PUSH(cpu, base, l) \ + do { \ + writeMemory(cpu, base - 2, l & 0xFF); \ + writeMemory(cpu, base - 1, (l >> 8) & 0xFF); \ + base -= 2; \ + } while (0); + +#define POP(cpu, base, l) \ + do { \ + BYTE _low = readMemory(cpu, base); \ + l = (readMemory(cpu, base + 1) << 8) | _low; \ + base += 2; \ + } while (0); + +#define GET_BYTE_PC(cpu, l) \ + do { \ + l = readMemory(cpu, cpu->progCounter); \ + cpu->progCounter++; \ + } while (0); + +#define GET_WORD_PC(cpu, l) \ + do { \ + l = readMemory(cpu, cpu->progCounter); \ + cpu->progCounter++; \ + BYTE _hi = readMemory(cpu, cpu->progCounter); \ + cpu->progCounter++; \ + l = (_hi << 8) | l; \ + } while (0); + +struct audio_channel { + int pos; + int period_clock; + int length_timer; + int env_vol; + int env_dir; + int env_sweep_pace; +}; + +struct gameboy { + BYTE cartridge[CARTRIDGE_SIZE]; + BYTE memory[MEMORY_SIZE]; + + // Now the CPU registers + + WORD progCounter; + WORD stack; + + BYTE A; + BYTE B; + BYTE C; + BYTE D; + BYTE E; + BYTE F; + BYTE H; + BYTE L; + + /*This part represent the memory banking system*/ + + BYTE ROMType; /*This variable represents the ROM type MBCx*/ + WORD currentROMBank; + BYTE ROMBankType; + + BYTE RAMBank[0x10000]; /*This should contains all the RAM banks*/ + BYTE currentRAMBank; + BYTE isRAMEnable; + + /*This part helps handling the timers*/ + + int clockBeforeTimer; + int clockBeforeDividerTimer; + + /*This is the master interrupt switch*/ + + BYTE master_interr_switch; + BYTE whenDisableInterrupts; + BYTE enable_interr; + + /*This area is for handle the LCD timing*/ + + int clockScanline; + + /*This is the screen*/ + + BYTE screenData[144][160]; + + BYTE cpuHalted; + + BYTE keymap; + + void (*changeBank)(gb *, WORD, BYTE); + + struct audio_channel channel_1; + struct audio_channel channel_2; + struct audio_channel channel_3; + struct audio_channel channel_4; + + BYTE soundData[2048]; + WORD numerOfBytes; + + SIGNED_WORD leftSoundBuffer, rightSoundBuffer; + int soundMasterClock; + int soundFrameClock; + int soundFramePos; +}; + +BYTE readMemory(gb *cpu, WORD addr); +void writeMemory(gb *cpu, WORD addr, BYTE data); + +void increaseTimer(gb *cpu, BYTE clocks); +void setTimerFreq(gb *cpu); +void raiseInterrupt(gb *cpu, BYTE code); +void handleInterrupts(gb *cpu); + +void handleGraphic(gb *cpu, BYTE cycles); + +void DMATransfert(gb *cpu, BYTE data); + +void mbc1_changeBank(gb *cpu, WORD addr, BYTE data); +void mbc3_changeBank(gb *cpu, WORD addr, BYTE data); + +BYTE getKeypad(gb *cpu); + +BYTE executeOpcode(gb *cpu, BYTE opcode); +unsigned int extendedOpcodes(gb *cpu, BYTE opcode); + +void writeSaveRam(gb *cpu); +void loadSaveRam(gb *cpu); + +void handleSound(gb *cpu, BYTE clocks); + +void writeSoundRegistry(gb *cpu, WORD addr, BYTE data); + +#endif diff --git a/lib/gb-memory.c b/lib/gb-memory.c new file mode 100755 index 0000000..c361fed --- /dev/null +++ b/lib/gb-memory.c @@ -0,0 +1,84 @@ +#include "gb-impl.h" + +/*This function assumes that the ROM is already loaded*/ + +void setGbBanking(gb *cpu) { + switch (cpu->cartridge[0x147]) { + case 1: + case 2: + case 3: + cpu->ROMType = 1; + cpu->currentROMBank = 1; + cpu->changeBank = mbc1_changeBank; + break; + + case 0x13: + cpu->ROMType = 3; + cpu->currentROMBank = 1; + cpu->changeBank = mbc3_changeBank; + break; + } + cpu->currentRAMBank = 0; +} + +BYTE readMemory(gb *cpu, WORD addr) { + if ((addr >= 0x0000) && (addr <= 0x3FFF)) { + return cpu->cartridge[addr]; + } + if ((addr >= 0x4000) && (addr <= 0x7FFF)) { + WORD t = addr - 0x4000; + return cpu->cartridge[t + (cpu->currentROMBank * 0x4000)]; + } + + else if ((addr >= 0xA000) && (addr <= 0xBFFF)) { + WORD t = addr - 0xA000; + return cpu->RAMBank[t + (cpu->currentRAMBank * 0x2000)]; + } else if (addr == 0xFF00) + return getKeypad(cpu); + + return cpu->memory[addr]; +} + +/*This function is necessary for replicate the ECHO (E000-FDFF) area*/ + +void writeMemory(gb *cpu, WORD addr, BYTE data) { + /*This part is mapped on the rom, so read-only*/ + if (addr < 0x8000) { + cpu->changeBank(cpu, addr, data); + } + + else if ((addr >= 0xA000) && (addr < 0xC000)) { + if (cpu->isRAMEnable != 0) { + WORD t = addr - 0xA000; + cpu->RAMBank[t + (cpu->currentRAMBank * 0x2000)] = data; + } + } + + else if ((addr >= 0xE000) && (addr < 0xFE00)) { + cpu->memory[addr] = data; + writeMemory(cpu, addr - 0x2000, data); + } + + /*Not usable */ + else if ((addr >= 0xFEA0) && (addr < 0xFEFF)) { + } + + else if (addr == TIMER_CONTROLLER) { + BYTE freq = readMemory(cpu, TIMER_CONTROLLER) & 0x3; + cpu->memory[TIMER_CONTROLLER] = data; + BYTE newfreq = readMemory(cpu, TIMER_CONTROLLER) & 0x3; + + if (freq != newfreq) + setTimerFreq(cpu); + } else if (addr == DIVIDER_TIMER) + cpu->memory[DIVIDER_TIMER] = 0; + else if (addr == LCD_SCANLINE_ADRR) + cpu->memory[LCD_SCANLINE_ADRR] = 0; + else if (addr == DMA_ADRR) + DMATransfert(cpu, data); + else if((addr >= 0xFF10) && (addr <= 0xFF3F)){ + writeSoundRegistry(cpu, addr, data); + } + else + cpu->memory[addr] = data; +} diff --git a/lib/gb-opcode.h b/lib/gb-opcode.h new file mode 100755 index 0000000..6e8b14f --- /dev/null +++ b/lib/gb-opcode.h @@ -0,0 +1,47 @@ +#ifndef GBOPCODE_H +#define GBOPCODE_H + +void LOAD_8BIT(BYTE *dest, BYTE src); +void LOAD_16BIT(BYTE *destA, BYTE *destB, WORD src); + +void LDHL(gb *cpu); + +void ADD_8BIT(gb *cpu, BYTE *regA, BYTE regB); +void ADDC_8BIT(gb *cpu, BYTE *regA, BYTE regB); + +void SUB_8BIT(gb *cpu, BYTE *regA, BYTE regB); +void SUBC_8BIT(gb *cpu, BYTE *regA, BYTE regB); + +void AND_8BIT(gb *cpu, BYTE *regA, BYTE regB); +void OR_8BIT(gb *cpu, BYTE *regA, BYTE regB); +void XOR_8BIT(gb *cpu, BYTE *regA, BYTE regB); + +void CP_8BIT(gb *cpu, BYTE regB); + +void INC_8BIT(gb *cpu, BYTE *reg); +void DEC_8BIT(gb *cpu, BYTE *reg); + +void ADD_16BIT(gb *cpu, BYTE *regA, BYTE *regB, WORD src); + +void INC_16BIT(BYTE *regA, BYTE *regB); +void DEC_16BIT(BYTE *regA, BYTE *regB); + +void JMP(gb *cpu, WORD addr); + +void ROTATE_LEFT(gb *cpu, BYTE *reg); +void ROTATE_RIGHT(gb *cpu, BYTE *reg); +void ROTATE_LEFT_CARRY(gb *cpu, BYTE *reg); +void ROTATE_RIGHT_CARRY(gb *cpu, BYTE *reg); + +void SHIFT_LEFT(gb *cpu, BYTE *reg); +void SHIFT_RIGHT_ARITH(gb *cpu, BYTE *reg); +void SHIFT_RIGHT(gb *cpu, BYTE *reg); + +void SWAP_NIBBLES(gb *cpu, BYTE *reg); +void TEST_BIT(gb *cpu, BYTE val, BYTE numBit); +void RESET_BIT(gb *cpu, BYTE *val, BYTE numBit); +void SET_BIT(gb *cpu, BYTE *val, BYTE numBit); + +void DAA(gb *cpu); + +#endif diff --git a/lib/gb-opcodes-impl.c b/lib/gb-opcodes-impl.c new file mode 100755 index 0000000..bc78350 --- /dev/null +++ b/lib/gb-opcodes-impl.c @@ -0,0 +1,448 @@ +#include "gb-impl.h" +#include "gb-opcode.h" +#include + +#define ROTATE_FLAG(cpu, bit, val) \ + { \ + if (bit == 0) \ + RESET_CFLAG(cpu); \ + else \ + SET_CFLAG(cpu); \ + RESET_NFLAG(cpu); \ + RESET_HFLAG(cpu); \ + if (val == 0) \ + SET_ZFLAG(cpu); \ + else \ + RESET_ZFLAG(cpu); \ + } \ + while (0) + +void LOAD_8BIT(BYTE *dest, BYTE src) { + *dest = src; +} + +void LOAD_16BIT(BYTE *destA, BYTE *destB, WORD src) { + *destB = src & 0xFF; + *destA = (src >> 8) & 0xFF; +} + +void LDHL(gb *cpu) { + uint32_t val; + SIGNED_BYTE n; + n = (SIGNED_BYTE)readMemory(cpu, cpu->progCounter); + cpu->progCounter++; + + // GET_BYTE_PC(cpu, n); + val = (SIGNED_WORD)cpu->stack + (SIGNED_WORD)n; + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + + if (val > 0xFFFF) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + WORD h = (val & 0x8FF); + h += (cpu->stack & 0x8FF); + + if (h > 0x8FF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + + /*TODO check the flags*/ + cpu->L = val & 0xFF; + cpu->H = (val >> 8) & 0xFF; +} + +void JMP(gb *cpu, WORD addr) { + cpu->progCounter = addr; +} + +void ADD_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + WORD value = (*regA) + regB; + + if (value > 255) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + value &= 0xFF; + RESET_NFLAG(cpu); + if (value == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + + WORD h = ((*regA) & 0xF); + h += (regB & 0xF); + + if (h > 0xF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + *regA = value & 0xFF; +} + +void ADDC_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + ADD_8BIT(cpu, regA, (BYTE)(regB + ((cpu->F & 0x10) ? 1 : 0))); + /* BYTE ret = regB + ((cpu->F &0x10)?1:0); + WORD value = (*regA) + ret; + //printf("(%x) %x +(%x) %x + %x (%x) = %x\n",i,*regA, (cpu->A),regB, + ((cpu->F &0x10)?1:0), cpu->F, value&0xff ); + + if(value > 0xff) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + value &= 0xFF; + RESET_NFLAG(cpu); + if(value == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + + WORD h = ((*(regA)) & 0xF) ; + h += (ret & 0xF) ; + + if (h > 0xF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + *regA = value & 0xFF;*/ +} + +void SUB_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + BYTE value = (*regA) - regB; + SET_NFLAG(cpu); + + if ((*regA) < regB) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + value &= 0xFF; + if (value == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + + SIGNED_WORD h = ((*regA) & 0xF); + h -= (regB & 0xF); + + if (h < 0) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + *regA = (BYTE)value; +} + +void SUBC_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + SUB_8BIT(cpu, regA, (BYTE)(regB + ((cpu->F & 0x10) ? 1 : 0))); + /*BYTE ret = regB + (SIGNED_BYTE)((cpu->F &0x10)?1:0); + int32_t value = (*regA) - ret; + + // printf("%x - %x - %x = %x\n",*regA, regB, ((cpu->F &0x10)?1:0), value ); + + SET_NFLAG(cpu); + + if(value <0 ){ + SET_CFLAG(cpu); + value+=256; + } + else + RESET_CFLAG(cpu); + + value &=0xFF; + if(value == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + + SIGNED_WORD h = (*regA & 0x0F) ; + h -= (ret & 0x0F) ; + + if ((ret & 0x0F) > (*regA & 0x0F) ) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + *regA = value&0xff;*/ +} + +void AND_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + BYTE val = *regA & regB; + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + SET_HFLAG(cpu); + RESET_CFLAG(cpu); + *regA = val; +} + +void OR_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + BYTE val = (*regA) | regB; + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + RESET_HFLAG(cpu); + RESET_CFLAG(cpu); + *regA = val; +} + +void XOR_8BIT(gb *cpu, BYTE *regA, BYTE regB) { + BYTE val = (*regA) ^ regB; + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + RESET_HFLAG(cpu); + RESET_CFLAG(cpu); + *regA = val; +} + +void CP_8BIT(gb *cpu, BYTE regB) { + BYTE temp = cpu->A; + SUB_8BIT(cpu, &(cpu->A), regB); + cpu->A = temp; +} + +void INC_8BIT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + val++; + if (((*reg) & 0xF) == 0) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + RESET_NFLAG(cpu); + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + *reg = val; +} + +void DEC_8BIT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + val--; + if (((*reg) & 0xF) == 0) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + SET_NFLAG(cpu); + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + *reg = val; +} + +void INC_16BIT(BYTE *regA, BYTE *regB) { + WORD value = ((*regA) << 8) | (*regB); + value++; + *regA = (value >> 8) & 0xFF; + *regB = value & 0xFF; +} + +void DEC_16BIT(BYTE *regA, BYTE *regB) { + WORD value = ((*regA) << 8) | (*regB); + value--; + *regA = (value >> 8) & 0xFF; + *regB = value & 0xFF; +} + +void SWAP_NIBBLES(gb *cpu, BYTE *reg) { + BYTE val = *reg; + val = ((val << 4) & 0xF0) | ((val >> 4) & 0xF); + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + RESET_HFLAG(cpu); + RESET_CFLAG(cpu); + *reg = val; +} + +void TEST_BIT(gb *cpu, BYTE val, BYTE numBit) { + BYTE val2 = (val >> numBit) & 0x1; + if (val2 == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + SET_HFLAG(cpu); +} + +void RESET_BIT(gb *cpu, BYTE *val, BYTE numBit) { + BYTE v = (*val) & (~(0x1 << numBit)); + *val = v; +} + +void SET_BIT(gb *cpu, BYTE *val, BYTE numBit) { + BYTE v = (*val) | (0x1 << numBit); + *val = v; +} + +void ADD_16BIT(gb *cpu, BYTE *regA, BYTE *regB, WORD src) { + uint32_t val = (((*regA) << 8)) | ((*regB) & 0xFF); + // uint32_t val = (((*regA)<<8)) | ((*regB) &0xFF); + val += src; + RESET_NFLAG(cpu); + + if (val > 0xFFFF) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + WORD h = (((((*regA) << 8)) | ((*regB) & 0xFF)) & 0x8FF); + h += (src & 0x8FF); + + if (h > 0x8FF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + + *regA = (val >> 8) & 0xFF; + *regB = (val & 0xFF); +} + +void ROTATE_LEFT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE msb = (val >> 7) & 0x1; + val <<= 1; + val |= msb; + cpu->F = 0; + if (msb) { + SET_CFLAG(cpu); + } else { + RESET_CFLAG(cpu); + } + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + + *reg = val; +} + +void ROTATE_RIGHT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE msb = (val & 0x1); + cpu->F = 0; + val >>= 1; + if (msb) { + val |= 0x80; + SET_CFLAG(cpu); + } else { + val &= 0x7f; + RESET_CFLAG(cpu); + } + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + *reg = val; +} + +void ROTATE_LEFT_CARRY(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE msb = (val >> 7) & 0x1; + val <<= 1; + if (cpu->F & 0x10) + val |= 0x1; + if (msb) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + *reg = val; +} + +void ROTATE_RIGHT_CARRY(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE msb = val & 0x1; + val >>= 1; + if ((cpu->F & 0x10) != 0) + val |= 0x80; + else + val &= 0x7f; + if (msb) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + if (val == 0) + SET_ZFLAG(cpu); + else + RESET_ZFLAG(cpu); + *reg = val; +} + +void SHIFT_LEFT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE msb = (val >> 7) & 0x1; + + val <<= 1; + + ROTATE_FLAG(cpu, msb, val); + *reg = val; +} + +void SHIFT_RIGHT_ARITH(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE lsb = (val)&0x1; + BYTE msb = (val >> 7) & 0x1; + + val >>= 1; + val |= ((msb << 7)); + + ROTATE_FLAG(cpu, lsb, val); + *reg = val; +} + +void SHIFT_RIGHT(gb *cpu, BYTE *reg) { + BYTE val = *reg; + BYTE lsb = (val)&0x1; + + val >>= 1; + + ROTATE_FLAG(cpu, lsb, val); + *reg = val; +} + +void DAA(gb *cpu) { + uint32_t a = cpu->A; + + if (!(cpu->F & 0x40)) { + if ((cpu->F & 0x20) || (a & 0xF) > 9) + a += 0x06; + if ((cpu->F & 0x10) || a > 0x9F) + a += 0x60; + } else { + if ((cpu->F & 0x20) != 0) + a -= 0x06; + + if ((cpu->F & 0x10) != 0) + a -= 0x60; + } + + cpu->F &= ~(0x20 | 0x80); + if ((a & 0x100) == 0x100) + SET_CFLAG(cpu); + + a &= 0xFF; + + if (a == 0) + SET_ZFLAG(cpu); + cpu->A = (BYTE)a; +} diff --git a/lib/gb-opcodes.c b/lib/gb-opcodes.c new file mode 100755 index 0000000..8a17f04 --- /dev/null +++ b/lib/gb-opcodes.c @@ -0,0 +1,1804 @@ +#include +#include "gb-impl.h" +#include "gb-opcode.h" + +BYTE executeOpcode(gb *cpu, BYTE opcode) { + BYTE val = 0; + SIGNED_BYTE s_val = 0; + WORD val_16 = 0; + switch (opcode) { + case 0x00: + return 4; + case 0x76: + cpu->cpuHalted = 0; + return 4; + case 0x06: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->B), val); + return 8; + case 0x0E: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->C), val); + return 8; + case 0x16: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->D), val); + return 8; + case 0x1E: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->E), val); + return 8; + case 0x26: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->H), val); + return 8; + case 0x2E: + GET_BYTE_PC(cpu, val) + LOAD_8BIT(&(cpu->L), val); + return 8; + + case 0x7F: + LOAD_8BIT(&(cpu->A), cpu->A); + return 4; + case 0x78: + LOAD_8BIT(&(cpu->A), cpu->B); + return 4; + case 0x79: + LOAD_8BIT(&(cpu->A), cpu->C); + return 4; + case 0x7A: + LOAD_8BIT(&(cpu->A), cpu->D); + return 4; + case 0x7B: + LOAD_8BIT(&(cpu->A), cpu->E); + return 4; + case 0x7C: + LOAD_8BIT(&(cpu->A), cpu->H); + return 4; + case 0x7D: + LOAD_8BIT(&(cpu->A), cpu->L); + return 4; + case 0x7E: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->A), val); + return 8; + case 0x40: + LOAD_8BIT(&(cpu->B), cpu->B); + return 4; + case 0x41: + LOAD_8BIT(&(cpu->B), cpu->C); + return 4; + case 0x42: + LOAD_8BIT(&(cpu->B), cpu->D); + return 4; + case 0x43: + LOAD_8BIT(&(cpu->B), cpu->E); + return 4; + case 0x44: + LOAD_8BIT(&(cpu->B), cpu->H); + return 4; + case 0x45: + LOAD_8BIT(&(cpu->B), cpu->L); + return 4; + case 0x46: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->B), val); + return 8; + case 0x48: + LOAD_8BIT(&(cpu->C), cpu->B); + return 4; + case 0x49: + LOAD_8BIT(&(cpu->C), cpu->C); + return 4; + case 0x4A: + LOAD_8BIT(&(cpu->C), cpu->D); + return 4; + case 0x4B: + LOAD_8BIT(&(cpu->C), cpu->E); + return 4; + case 0x4C: + LOAD_8BIT(&(cpu->C), cpu->H); + return 4; + case 0x4D: + LOAD_8BIT(&(cpu->C), cpu->L); + return 4; + case 0x4E: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->C), val); + return 8; + case 0x50: + LOAD_8BIT(&(cpu->D), cpu->B); + return 4; + case 0x51: + LOAD_8BIT(&(cpu->D), cpu->C); + return 4; + case 0x52: + LOAD_8BIT(&(cpu->D), cpu->D); + return 4; + case 0x53: + LOAD_8BIT(&(cpu->D), cpu->E); + return 4; + case 0x54: + LOAD_8BIT(&(cpu->D), cpu->H); + return 4; + case 0x55: + LOAD_8BIT(&(cpu->D), cpu->L); + return 4; + case 0x56: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->D), val); + return 8; + case 0x58: + LOAD_8BIT(&(cpu->E), cpu->B); + return 4; + case 0x59: + LOAD_8BIT(&(cpu->E), cpu->C); + return 4; + case 0x5A: + LOAD_8BIT(&(cpu->E), cpu->D); + return 4; + case 0x5B: + LOAD_8BIT(&(cpu->E), cpu->E); + return 4; + case 0x5C: + LOAD_8BIT(&(cpu->E), cpu->H); + return 4; + case 0x5D: + LOAD_8BIT(&(cpu->E), cpu->L); + return 4; + case 0x5E: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->E), val); + return 8; + case 0x60: + LOAD_8BIT(&(cpu->H), cpu->B); + return 4; + case 0x61: + LOAD_8BIT(&(cpu->H), cpu->C); + return 4; + case 0x62: + LOAD_8BIT(&(cpu->H), cpu->D); + return 4; + case 0x63: + LOAD_8BIT(&(cpu->H), cpu->E); + return 4; + case 0x64: + LOAD_8BIT(&(cpu->H), cpu->H); + return 4; + case 0x65: + LOAD_8BIT(&(cpu->H), cpu->L); + return 4; + case 0x66: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->H), val); + return 8; + case 0x68: + LOAD_8BIT(&(cpu->L), cpu->B); + return 4; + case 0x69: + LOAD_8BIT(&(cpu->L), cpu->C); + return 4; + case 0x6A: + LOAD_8BIT(&(cpu->L), cpu->D); + return 4; + case 0x6B: + LOAD_8BIT(&(cpu->L), cpu->E); + return 4; + case 0x6C: + LOAD_8BIT(&(cpu->L), cpu->H); + return 4; + case 0x6D: + LOAD_8BIT(&(cpu->L), cpu->L); + return 4; + case 0x6E: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->L), val); + return 8; + case 0x70: + writeMemory(cpu, getHL(cpu), cpu->B); + return 8; + case 0x71: + writeMemory(cpu, getHL(cpu), cpu->C); + return 8; + case 0x72: + writeMemory(cpu, getHL(cpu), cpu->D); + return 8; + case 0x73: + writeMemory(cpu, getHL(cpu), cpu->E); + return 8; + case 0x74: + writeMemory(cpu, getHL(cpu), cpu->H); + return 8; + case 0x75: + writeMemory(cpu, getHL(cpu), cpu->L); + return 8; + case 0x36: + GET_BYTE_PC(cpu, val); + writeMemory(cpu, getHL(cpu), val); + return 12; + + case 0x0A: + val = readMemory(cpu, getBC(cpu)); + LOAD_8BIT(&(cpu->A), val); + return 8; + case 0x1A: + val = readMemory(cpu, getDE(cpu)); + cpu->A = val; + return 8; + case 0xFA: + GET_WORD_PC(cpu, val_16); + val = readMemory(cpu, val_16); + LOAD_8BIT(&(cpu->A), val); + return 16; + case 0x3E: + GET_BYTE_PC(cpu, val); + LOAD_8BIT(&(cpu->A), val); + return 8; + + case 0x47: + LOAD_8BIT(&(cpu->B), cpu->A); + return 4; + case 0x4F: + LOAD_8BIT(&(cpu->C), cpu->A); + return 4; + case 0x57: + LOAD_8BIT(&(cpu->D), cpu->A); + return 4; + case 0x5F: + LOAD_8BIT(&(cpu->E), cpu->A); + return 4; + case 0x67: + LOAD_8BIT(&(cpu->H), cpu->A); + return 4; + case 0x6F: + LOAD_8BIT(&(cpu->L), cpu->A); + return 4; + case 0x02: + writeMemory(cpu, getBC(cpu), cpu->A); + return 8; + case 0x12: + writeMemory(cpu, getDE(cpu), cpu->A); + return 8; + case 0x77: + writeMemory(cpu, getHL(cpu), cpu->A); + return 8; + case 0xEA: + GET_WORD_PC(cpu, val_16); + writeMemory(cpu, val_16, cpu->A); + return 16; + + case 0xF2: + val = readMemory(cpu, 0xFF00 + cpu->C); + LOAD_8BIT(&(cpu->A), val); + return 8; + case 0xE2: + writeMemory(cpu, 0xFF00 + cpu->C, cpu->A); + return 8; + + case 0x3A: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->A), val); + DEC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0x32: + writeMemory(cpu, getHL(cpu), cpu->A); + DEC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0x2A: + val = readMemory(cpu, getHL(cpu)); + LOAD_8BIT(&(cpu->A), val); + INC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0x22: + writeMemory(cpu, getHL(cpu), cpu->A); + INC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0xE0: + GET_BYTE_PC(cpu, val); + writeMemory(cpu, 0xFF00 + val, cpu->A); + return 12; + case 0xF0: + GET_BYTE_PC(cpu, val); + val = readMemory(cpu, 0xFF00 + val); + LOAD_8BIT(&(cpu->A), val); + return 12; + + case 0x01: + GET_WORD_PC(cpu, val_16); + LOAD_16BIT(&(cpu->B), &(cpu->C), val_16); + return 12; + case 0x11: + GET_WORD_PC(cpu, val_16); + LOAD_16BIT(&(cpu->D), &(cpu->E), val_16); + return 12; + case 0x21: + GET_WORD_PC(cpu, val_16); + LOAD_16BIT(&(cpu->H), &(cpu->L), val_16); + return 12; + case 0x31: + GET_WORD_PC(cpu, val_16); + cpu->stack = val_16; + return 12; + case 0xF9: + cpu->stack = getHL(cpu); + return 8; + case 0xF8: + GET_BYTE_PC(cpu, s_val); + val_16 = cpu->stack + s_val; + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + + if ((uint32_t)(cpu->stack + s_val) > 0xFFFF) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + if ((uint32_t)(cpu->stack + s_val) > 0x8FF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + cpu->L = val_16 & 0xFF; + cpu->H = ((val_16 >> 8) & 0xFF); + + return 12; + case 0x08: + GET_WORD_PC(cpu, val_16); + writeMemory(cpu, val_16, cpu->stack & 0xFF); + writeMemory(cpu, val_16 + 1, (cpu->stack >> 8) & 0xFF); + return 20; + case 0xF5: + PUSH(cpu, cpu->stack, getAF(cpu)); + return 16; + case 0xC5: + PUSH(cpu, cpu->stack, getBC(cpu)); + return 16; + case 0xD5: + PUSH(cpu, cpu->stack, getDE(cpu)); + return 16; + case 0xE5: + PUSH(cpu, cpu->stack, getHL(cpu)); + return 16; + case 0xF1: + POP(cpu, cpu->stack, val_16); + cpu->A = (val_16 >> 8) & 0xFF; + cpu->F = val_16 & 0xFF; + cpu->F &= ~0xF; + return 12; + case 0xC1: + POP(cpu, cpu->stack, val_16); + cpu->B = (val_16 >> 8) & 0xFF; + cpu->C = val_16 & 0xFF; + return 12; + case 0xD1: + POP(cpu, cpu->stack, val_16); + cpu->D = (val_16 >> 8) & 0xFF; + cpu->E = val_16 & 0xFF; + return 12; + case 0xE1: + POP(cpu, cpu->stack, val_16); + cpu->H = (val_16 >> 8) & 0xFF; + cpu->L = val_16 & 0xFF; + return 12; + + case 0x87: + ADD_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0x80: + ADD_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0x81: + ADD_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0x82: + ADD_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0x83: + ADD_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0x84: + ADD_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0x85: + ADD_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0x86: + val = readMemory(cpu, getHL(cpu)); + ADD_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xC6: + GET_BYTE_PC(cpu, val); + ADD_8BIT(cpu, &(cpu->A), val); + return 8; + + case 0x8F: + ADDC_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0x88: + ADDC_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0x89: + ADDC_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0x8A: + ADDC_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0x8B: + ADDC_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0x8C: + ADDC_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0x8D: + ADDC_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0x8E: + val = readMemory(cpu, getHL(cpu)); + ADDC_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xCE: + GET_BYTE_PC(cpu, val); + ADDC_8BIT(cpu, &(cpu->A), val); + return 8; + case 0x97: + SUB_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0x90: + SUB_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0x91: + SUB_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0x92: + SUB_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0x93: + SUB_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0x94: + SUB_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0x95: + SUB_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0x96: + val = readMemory(cpu, getHL(cpu)); + SUB_8BIT(cpu, &(cpu->A), val); + return 8; + case 0x9F: + SUBC_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0x98: + SUBC_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0x99: + SUBC_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0x9A: + SUBC_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0x9B: + SUBC_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0x9C: + SUBC_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0x9D: + SUBC_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0x9E: + val = readMemory(cpu, getHL(cpu)); + SUBC_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xD6: + GET_BYTE_PC(cpu, val); + SUB_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xDE: + GET_BYTE_PC(cpu, val); + SUBC_8BIT(cpu, &(cpu->A), val); + return 8; + + case 0xA7: + AND_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0xA0: + AND_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0xA1: + AND_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0xA2: + AND_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0xA3: + AND_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0xA4: + AND_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0xA5: + AND_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0xA6: + val = readMemory(cpu, getHL(cpu)); + AND_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xE6: + GET_BYTE_PC(cpu, val); + AND_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xB7: + OR_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0xB0: + OR_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0xB1: + OR_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0xB2: + OR_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0xB3: + OR_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0xB4: + OR_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0xB5: + OR_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0xB6: + val = readMemory(cpu, getHL(cpu)); + OR_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xF6: + val = readMemory(cpu, cpu->progCounter); + cpu->progCounter++; + OR_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xAF: + XOR_8BIT(cpu, &(cpu->A), cpu->A); + return 4; + case 0xA8: + XOR_8BIT(cpu, &(cpu->A), cpu->B); + return 4; + case 0xA9: + XOR_8BIT(cpu, &(cpu->A), cpu->C); + return 4; + case 0xAA: + XOR_8BIT(cpu, &(cpu->A), cpu->D); + return 4; + case 0xAB: + XOR_8BIT(cpu, &(cpu->A), cpu->E); + return 4; + case 0xAC: + XOR_8BIT(cpu, &(cpu->A), cpu->H); + return 4; + case 0xAD: + XOR_8BIT(cpu, &(cpu->A), cpu->L); + return 4; + case 0xAE: + val = readMemory(cpu, getHL(cpu)); + XOR_8BIT(cpu, &(cpu->A), val); + return 8; + case 0xEE: + val = readMemory(cpu, cpu->progCounter); + cpu->progCounter++; + XOR_8BIT(cpu, &(cpu->A), val); + return 8; + + case 0xBF: + CP_8BIT(cpu, cpu->A); + return 4; + case 0xB8: + CP_8BIT(cpu, cpu->B); + return 4; + case 0xB9: + CP_8BIT(cpu, cpu->C); + return 4; + case 0xBA: + CP_8BIT(cpu, cpu->D); + return 4; + case 0xBB: + CP_8BIT(cpu, cpu->E); + return 4; + case 0xBC: + CP_8BIT(cpu, cpu->H); + return 4; + case 0xBD: + CP_8BIT(cpu, cpu->L); + return 4; + case 0xBE: + val = readMemory(cpu, getHL(cpu)); + CP_8BIT(cpu, val); + return 8; + case 0xFE: + GET_BYTE_PC(cpu, val) + CP_8BIT(cpu, val); + return 8; + + case 0x3C: + INC_8BIT(cpu, &(cpu->A)); + return 4; + case 0x04: + INC_8BIT(cpu, &(cpu->B)); + return 4; + case 0x0C: + INC_8BIT(cpu, &(cpu->C)); + return 4; + case 0x14: + INC_8BIT(cpu, &(cpu->D)); + return 4; + case 0x1C: + INC_8BIT(cpu, &(cpu->E)); + return 4; + case 0x24: + INC_8BIT(cpu, &(cpu->H)); + return 4; + case 0x2C: + INC_8BIT(cpu, &(cpu->L)); + return 4; + case 0x34: + val = readMemory(cpu, getHL(cpu)); + INC_8BIT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 12; + + case 0x3D: + DEC_8BIT(cpu, &(cpu->A)); + return 4; + case 0x05: + DEC_8BIT(cpu, &(cpu->B)); + return 4; + case 0x0D: + DEC_8BIT(cpu, &(cpu->C)); + return 4; + case 0x15: + DEC_8BIT(cpu, &(cpu->D)); + return 4; + case 0x1D: + DEC_8BIT(cpu, &(cpu->E)); + return 4; + case 0x25: + DEC_8BIT(cpu, &(cpu->H)); + return 4; + case 0x2D: + DEC_8BIT(cpu, &(cpu->L)); + return 4; + case 0x35: + val = readMemory(cpu, getHL(cpu)); + DEC_8BIT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 12; + + /*16BIT operations*/ + + case 0x09: + ADD_16BIT(cpu, &(cpu->H), &(cpu->L), getBC(cpu)); + return 8; + case 0x19: + ADD_16BIT(cpu, &(cpu->H), &(cpu->L), getDE(cpu)); + return 8; + case 0x29: + ADD_16BIT(cpu, &(cpu->H), &(cpu->L), getHL(cpu)); + return 8; + case 0x39: + ADD_16BIT(cpu, &(cpu->H), &(cpu->L), cpu->stack); + return 8; + + case 0x03: + INC_16BIT(&(cpu->B), &(cpu->C)); + return 8; + case 0x13: + INC_16BIT(&(cpu->D), &(cpu->E)); + return 8; + case 0x23: + INC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0x33: + cpu->stack++; + return 8; + case 0x0B: + DEC_16BIT(&(cpu->B), &(cpu->C)); + return 8; + case 0x1B: + DEC_16BIT(&(cpu->D), &(cpu->E)); + return 8; + case 0x2B: + DEC_16BIT(&(cpu->H), &(cpu->L)); + return 8; + case 0x3B: + cpu->stack--; + return 8; + + case 0xE8: + GET_BYTE_PC(cpu, s_val); + val_16 = cpu->stack + s_val; + RESET_ZFLAG(cpu); + RESET_NFLAG(cpu); + + if ((uint32_t)(cpu->stack + s_val) > 0xFFFF) + SET_CFLAG(cpu); + else + RESET_CFLAG(cpu); + + if (((cpu->stack) & 0x8ff + (s_val)) > 0x8FF) + SET_HFLAG(cpu); + else + RESET_HFLAG(cpu); + cpu->stack = val_16; + return 16; + + case 0xCB: + GET_BYTE_PC(cpu, val); + return extendedOpcodes(cpu, val); + + case 0x27: + DAA(cpu); + return 4; + + case 0x2F: + SET_NFLAG(cpu); + SET_HFLAG(cpu); + cpu->A = ~cpu->A; + return 4; + case 0x3F: + RESET_NFLAG(cpu); + RESET_HFLAG(cpu); + val = (cpu->F & 0x10) >> 4; + if (val == 0) + cpu->F |= 0x10; + else + cpu->F &= ~(0x10); + return 4; + case 0x37: + RESET_NFLAG(cpu); + RESET_HFLAG(cpu); + SET_CFLAG(cpu); + return 4; + + case 0x07: + ROTATE_LEFT(cpu, &(cpu->A)); + SET_ZFLAG(cpu); + return 4; + case 0x0F: + ROTATE_RIGHT(cpu, &(cpu->A)); + SET_ZFLAG(cpu); + return 4; + case 0x17: + ROTATE_LEFT_CARRY(cpu, &(cpu->A)); + SET_ZFLAG(cpu); + return 4; + case 0x1f: + ROTATE_RIGHT_CARRY(cpu, &(cpu->A)); + SET_ZFLAG(cpu); + return 4; + + case 0xF3: + cpu->whenDisableInterrupts = 2; + cpu->enable_interr = 0; + return 4; + case 0xFB: + cpu->whenDisableInterrupts = 2; + cpu->enable_interr = 1; + return 4; + + case 0xC3: + GET_WORD_PC(cpu, val_16); + JMP(cpu, val_16); + return 12; + case 0xC2: + val = cpu->F & 0x80; + GET_WORD_PC(cpu, val_16); + if (val == 0) + JMP(cpu, val_16); + return 12; + case 0xCA: + val = cpu->F & 0x80; + GET_WORD_PC(cpu, val_16); + if (val != 0) + JMP(cpu, val_16); + return 12; + case 0xD2: + val = cpu->F & 0x10; + GET_WORD_PC(cpu, val_16); + if (val == 0) + JMP(cpu, val_16); + return 12; + case 0xDA: + val = cpu->F & 0x10; + GET_WORD_PC(cpu, val_16); + if (val != 0) + JMP(cpu, val_16); + return 12; + case 0xE9: + cpu->progCounter = getHL(cpu); + return 4; + case 0x18: + GET_BYTE_PC(cpu, s_val); + cpu->progCounter += s_val; + return 8; + + case 0x20: + GET_BYTE_PC(cpu, s_val); + val = cpu->F & 0x80; + if (val == 0) + cpu->progCounter += s_val; + return 8; + case 0x28: + GET_BYTE_PC(cpu, s_val); + val = cpu->F & 0x80; + if (val != 0) { + cpu->progCounter += s_val; + } + return 8; + case 0x30: + GET_BYTE_PC(cpu, s_val); + val_16 = cpu->F & 0x10; + if (val_16 == 0) + cpu->progCounter += s_val; + return 8; + case 0x38: + GET_BYTE_PC(cpu, s_val); + val_16 = cpu->F & 0x10; + if (val_16 != 0) + cpu->progCounter += s_val; + return 8; + + case 0xCD: + GET_WORD_PC(cpu, val_16); + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = val_16; + return 24; + case 0xC4: + GET_WORD_PC(cpu, val_16); + val = cpu->F & 0x80; + if (val == 0) { + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = val_16; + } + return 12; + case 0xCC: + GET_WORD_PC(cpu, val_16); + val = cpu->F & 0x80; + if (val != 0) { + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = val_16; + } + return 12; + case 0xD4: + GET_WORD_PC(cpu, val_16); + val = cpu->F & 0x10; + if (val == 0) { + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = val_16; + } + return 12; + case 0xDC: + GET_WORD_PC(cpu, val_16); + val = cpu->F & 0x10; + if (val != 0) { + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = val_16; + } + return 12; + case 0xC7: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x0; + return 32; + case 0xCF: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x8; + return 32; + case 0xD7: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x10; + return 32; + case 0xDF: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x18; + return 32; + case 0xE7: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x20; + return 32; + case 0xEF: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x28; + return 32; + case 0xF7: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x30; + return 32; + case 0xFF: + PUSH(cpu, cpu->stack, cpu->progCounter); + cpu->progCounter = 0x38; + return 32; + case 0xC9: + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + return 8; + case 0xC0: + val = cpu->F & 0x80; + if (val == 0) { + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + } + return 8; + case 0xC8: + val = cpu->F & 0x80; + if (val != 0) { + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + } + return 8; + case 0xD0: + val = cpu->F & 0x10; + if (val == 0) { + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + } + return 8; + case 0xD8: + val = cpu->F & 0x10; + if (val != 0) { + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + } + return 8; + case 0xD9: + POP(cpu, cpu->stack, val_16); + cpu->progCounter = val_16; + cpu->master_interr_switch = 1; + return 8; + default: + fprintf(stderr, "Can't decode %x opcode (PC = %x)\n", opcode, + cpu->progCounter); + return 1; + } + return 1; +} + +unsigned int extendedOpcodes(gb *cpu, BYTE opcode) { + BYTE val = 0; + switch (opcode) { + /*TODO You should check this*/ + case 0x00: + ROTATE_LEFT(cpu, &(cpu->B)); + return 8; + case 0x01: + ROTATE_LEFT(cpu, &(cpu->C)); + return 8; + case 0x02: + ROTATE_LEFT(cpu, &(cpu->D)); + return 8; + case 0x03: + ROTATE_LEFT(cpu, &(cpu->E)); + return 8; + case 0x04: + ROTATE_LEFT(cpu, &(cpu->H)); + return 8; + case 0x05: + ROTATE_LEFT(cpu, &(cpu->L)); + return 8; + case 0x06: + val = readMemory(cpu, getHL(cpu)); + ROTATE_LEFT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x07: + ROTATE_LEFT(cpu, &(cpu->A)); + return 8; + + case 0x08: + ROTATE_RIGHT(cpu, &(cpu->B)); + return 8; + case 0x09: + ROTATE_RIGHT(cpu, &(cpu->C)); + return 8; + case 0x0A: + ROTATE_RIGHT(cpu, &(cpu->D)); + return 8; + case 0x0B: + ROTATE_RIGHT(cpu, &(cpu->E)); + return 8; + case 0x0C: + ROTATE_RIGHT(cpu, &(cpu->H)); + return 8; + case 0x0D: + ROTATE_RIGHT(cpu, &(cpu->L)); + return 8; + case 0x0E: + val = readMemory(cpu, getHL(cpu)); + ROTATE_RIGHT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x0F: + ROTATE_LEFT(cpu, &(cpu->A)); + return 8; + + case 0x10: + ROTATE_LEFT_CARRY(cpu, &(cpu->B)); + return 8; + case 0x11: + ROTATE_LEFT_CARRY(cpu, &(cpu->C)); + return 8; + case 0x12: + ROTATE_LEFT_CARRY(cpu, &(cpu->D)); + return 8; + case 0x13: + ROTATE_LEFT_CARRY(cpu, &(cpu->E)); + return 8; + case 0x14: + ROTATE_LEFT_CARRY(cpu, &(cpu->H)); + return 8; + case 0x15: + ROTATE_LEFT_CARRY(cpu, &(cpu->L)); + return 8; + case 0x16: + val = readMemory(cpu, getHL(cpu)); + ROTATE_LEFT_CARRY(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x17: + ROTATE_LEFT_CARRY(cpu, &(cpu->A)); + return 8; + case 0x18: + ROTATE_RIGHT_CARRY(cpu, &(cpu->B)); + return 8; + case 0x19: + ROTATE_RIGHT_CARRY(cpu, &(cpu->C)); + return 8; + case 0x1A: + ROTATE_RIGHT_CARRY(cpu, &(cpu->D)); + return 8; + case 0x1B: + ROTATE_RIGHT_CARRY(cpu, &(cpu->E)); + return 8; + case 0x1C: + ROTATE_RIGHT_CARRY(cpu, &(cpu->H)); + return 8; + case 0x1D: + ROTATE_RIGHT_CARRY(cpu, &(cpu->L)); + return 8; + case 0x1E: + val = readMemory(cpu, getHL(cpu)); + ROTATE_RIGHT_CARRY(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x1F: + ROTATE_RIGHT_CARRY(cpu, &(cpu->A)); + return 8; + + case 0x20: + SHIFT_LEFT(cpu, &(cpu->B)); + return 8; + case 0x21: + SHIFT_LEFT(cpu, &(cpu->C)); + return 8; + case 0x22: + SHIFT_LEFT(cpu, &(cpu->D)); + return 8; + case 0x23: + SHIFT_LEFT(cpu, &(cpu->E)); + return 8; + case 0x24: + SHIFT_LEFT(cpu, &(cpu->H)); + return 8; + case 0x25: + SHIFT_LEFT(cpu, &(cpu->L)); + return 8; + case 0x26: + val = readMemory(cpu, getHL(cpu)); + SHIFT_LEFT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 8; + case 0x27: + SHIFT_LEFT(cpu, &(cpu->A)); + return 16; + case 0x28: + SHIFT_RIGHT_ARITH(cpu, &(cpu->B)); + return 8; + case 0x29: + SHIFT_RIGHT_ARITH(cpu, &(cpu->C)); + return 8; + case 0x2A: + SHIFT_RIGHT_ARITH(cpu, &(cpu->D)); + return 8; + case 0x2B: + SHIFT_RIGHT_ARITH(cpu, &(cpu->E)); + return 8; + case 0x2C: + SHIFT_RIGHT_ARITH(cpu, &(cpu->H)); + return 8; + case 0x2D: + SHIFT_RIGHT_ARITH(cpu, &(cpu->L)); + return 8; + case 0x2E: + val = readMemory(cpu, getHL(cpu)); + SHIFT_RIGHT_ARITH(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 8; + case 0x2F: + SHIFT_RIGHT_ARITH(cpu, &(cpu->A)); + return 16; + + case 0x30: + SWAP_NIBBLES(cpu, &(cpu->B)); + return 8; + case 0x31: + SWAP_NIBBLES(cpu, &(cpu->C)); + return 8; + case 0x32: + SWAP_NIBBLES(cpu, &(cpu->D)); + return 8; + case 0x33: + SWAP_NIBBLES(cpu, &(cpu->E)); + return 8; + case 0x34: + SWAP_NIBBLES(cpu, &(cpu->H)); + return 8; + case 0x35: + SWAP_NIBBLES(cpu, &(cpu->L)); + return 8; + case 0x36: + val = readMemory(cpu, getHL(cpu)); + SWAP_NIBBLES(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x37: + SWAP_NIBBLES(cpu, &(cpu->A)); + return 8; + case 0x38: + SHIFT_RIGHT(cpu, &(cpu->B)); + return 8; + case 0x39: + SHIFT_RIGHT(cpu, &(cpu->C)); + return 8; + case 0x3A: + SHIFT_RIGHT(cpu, &(cpu->D)); + return 8; + case 0x3B: + SHIFT_RIGHT(cpu, &(cpu->E)); + return 8; + case 0x3C: + SHIFT_RIGHT(cpu, &(cpu->H)); + return 8; + case 0x3D: + SHIFT_RIGHT(cpu, &(cpu->L)); + return 8; + case 0x3E: + val = readMemory(cpu, getHL(cpu)); + SHIFT_RIGHT(cpu, &val); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x3F: + SHIFT_RIGHT(cpu, &(cpu->A)); + return 8; + + case 0x40: + TEST_BIT(cpu, cpu->B, 0); + return 8; + case 0x41: + TEST_BIT(cpu, cpu->C, 0); + return 8; + case 0x42: + TEST_BIT(cpu, cpu->D, 0); + return 8; + case 0x43: + TEST_BIT(cpu, cpu->E, 0); + return 8; + case 0x44: + TEST_BIT(cpu, cpu->H, 0); + return 8; + case 0x45: + TEST_BIT(cpu, cpu->L, 0); + return 8; + case 0x46: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 0); + return 16; + case 0x47: + TEST_BIT(cpu, cpu->A, 0); + return 8; + case 0x48: + TEST_BIT(cpu, cpu->B, 1); + return 8; + case 0x49: + TEST_BIT(cpu, cpu->C, 1); + return 8; + case 0x4A: + TEST_BIT(cpu, cpu->D, 1); + return 8; + case 0x4B: + TEST_BIT(cpu, cpu->E, 1); + return 8; + case 0x4C: + TEST_BIT(cpu, cpu->H, 1); + return 8; + case 0x4D: + TEST_BIT(cpu, cpu->L, 1); + return 8; + case 0x4E: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 1); + return 16; + case 0x4F: + TEST_BIT(cpu, cpu->A, 1); + return 8; + case 0x50: + TEST_BIT(cpu, cpu->B, 2); + return 8; + case 0x51: + TEST_BIT(cpu, cpu->C, 2); + return 8; + case 0x52: + TEST_BIT(cpu, cpu->D, 2); + return 8; + case 0x53: + TEST_BIT(cpu, cpu->E, 2); + return 8; + case 0x54: + TEST_BIT(cpu, cpu->H, 2); + return 8; + case 0x55: + TEST_BIT(cpu, cpu->L, 2); + return 8; + case 0x56: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 2); + return 16; + case 0x57: + TEST_BIT(cpu, cpu->A, 2); + return 8; + case 0x58: + TEST_BIT(cpu, cpu->B, 3); + return 8; + case 0x59: + TEST_BIT(cpu, cpu->C, 3); + return 8; + case 0x5A: + TEST_BIT(cpu, cpu->D, 3); + return 8; + case 0x5B: + TEST_BIT(cpu, cpu->E, 3); + return 8; + case 0x5C: + TEST_BIT(cpu, cpu->H, 3); + return 8; + case 0x5D: + TEST_BIT(cpu, cpu->L, 3); + return 8; + case 0x5E: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 3); + return 16; + case 0x5F: + TEST_BIT(cpu, cpu->A, 3); + return 8; + case 0x60: + TEST_BIT(cpu, cpu->B, 4); + return 8; + case 0x61: + TEST_BIT(cpu, cpu->C, 4); + return 8; + case 0x62: + TEST_BIT(cpu, cpu->D, 4); + return 8; + case 0x63: + TEST_BIT(cpu, cpu->E, 4); + return 8; + case 0x64: + TEST_BIT(cpu, cpu->H, 4); + return 8; + case 0x65: + TEST_BIT(cpu, cpu->L, 4); + return 8; + case 0x66: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 4); + return 16; + case 0x67: + TEST_BIT(cpu, cpu->A, 4); + return 8; + case 0x68: + TEST_BIT(cpu, cpu->B, 5); + return 8; + case 0x69: + TEST_BIT(cpu, cpu->C, 5); + return 8; + case 0x6A: + TEST_BIT(cpu, cpu->D, 5); + return 8; + case 0x6B: + TEST_BIT(cpu, cpu->E, 5); + return 8; + case 0x6C: + TEST_BIT(cpu, cpu->H, 5); + return 8; + case 0x6D: + TEST_BIT(cpu, cpu->L, 5); + return 8; + case 0x6E: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 5); + return 16; + case 0x6F: + TEST_BIT(cpu, cpu->A, 5); + return 8; + case 0x70: + TEST_BIT(cpu, cpu->B, 6); + return 8; + case 0x71: + TEST_BIT(cpu, cpu->C, 6); + return 8; + case 0x72: + TEST_BIT(cpu, cpu->D, 6); + return 8; + case 0x73: + TEST_BIT(cpu, cpu->E, 6); + return 8; + case 0x74: + TEST_BIT(cpu, cpu->H, 6); + return 8; + case 0x75: + TEST_BIT(cpu, cpu->L, 6); + return 8; + case 0x76: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 6); + return 16; + case 0x77: + TEST_BIT(cpu, cpu->A, 6); + return 8; + case 0x78: + TEST_BIT(cpu, cpu->B, 7); + return 8; + case 0x79: + TEST_BIT(cpu, cpu->C, 7); + return 8; + case 0x7A: + TEST_BIT(cpu, cpu->D, 7); + return 8; + case 0x7B: + TEST_BIT(cpu, cpu->E, 7); + return 8; + case 0x7C: + TEST_BIT(cpu, cpu->H, 7); + return 8; + case 0x7D: + TEST_BIT(cpu, cpu->L, 7); + return 8; + case 0x7E: + val = readMemory(cpu, getHL(cpu)); + TEST_BIT(cpu, val, 7); + return 16; + case 0x7F: + TEST_BIT(cpu, cpu->A, 7); + return 8; + + case 0x80: + RESET_BIT(cpu, &(cpu->B), 0); + return 8; + case 0x81: + RESET_BIT(cpu, &(cpu->C), 0); + return 8; + case 0x82: + RESET_BIT(cpu, &(cpu->D), 0); + return 8; + case 0x83: + RESET_BIT(cpu, &(cpu->E), 0); + return 8; + case 0x84: + RESET_BIT(cpu, &(cpu->H), 0); + return 8; + case 0x85: + RESET_BIT(cpu, &(cpu->L), 0); + return 8; + case 0x86: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 0); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x87: + RESET_BIT(cpu, &(cpu->A), 0); + return 8; + case 0x88: + RESET_BIT(cpu, &(cpu->B), 1); + return 8; + case 0x89: + RESET_BIT(cpu, &(cpu->C), 1); + return 8; + case 0x8A: + RESET_BIT(cpu, &(cpu->D), 1); + return 8; + case 0x8B: + RESET_BIT(cpu, &(cpu->E), 1); + return 8; + case 0x8C: + RESET_BIT(cpu, &(cpu->H), 1); + return 8; + case 0x8D: + RESET_BIT(cpu, &(cpu->L), 1); + return 8; + case 0x8E: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 1); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x8F: + RESET_BIT(cpu, &(cpu->A), 1); + return 8; + case 0x90: + RESET_BIT(cpu, &(cpu->B), 2); + return 8; + case 0x91: + RESET_BIT(cpu, &(cpu->C), 2); + return 8; + case 0x92: + RESET_BIT(cpu, &(cpu->D), 2); + return 8; + case 0x93: + RESET_BIT(cpu, &(cpu->E), 2); + return 8; + case 0x94: + RESET_BIT(cpu, &(cpu->H), 2); + return 8; + case 0x95: + RESET_BIT(cpu, &(cpu->L), 2); + return 8; + case 0x96: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 2); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x97: + RESET_BIT(cpu, &(cpu->A), 2); + return 8; + case 0x98: + RESET_BIT(cpu, &(cpu->B), 3); + return 8; + case 0x99: + RESET_BIT(cpu, &(cpu->C), 3); + return 8; + case 0x9A: + RESET_BIT(cpu, &(cpu->D), 3); + return 8; + case 0x9B: + RESET_BIT(cpu, &(cpu->E), 3); + return 8; + case 0x9C: + RESET_BIT(cpu, &(cpu->H), 3); + return 8; + case 0x9D: + RESET_BIT(cpu, &(cpu->L), 3); + return 8; + case 0x9E: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 3); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0x9F: + RESET_BIT(cpu, &(cpu->A), 3); + return 8; + case 0xA0: + RESET_BIT(cpu, &(cpu->B), 4); + return 8; + case 0xA1: + RESET_BIT(cpu, &(cpu->C), 4); + return 8; + case 0xA2: + RESET_BIT(cpu, &(cpu->D), 4); + return 8; + case 0xA3: + RESET_BIT(cpu, &(cpu->E), 4); + return 8; + case 0xA4: + RESET_BIT(cpu, &(cpu->H), 4); + return 8; + case 0xA5: + RESET_BIT(cpu, &(cpu->L), 4); + return 8; + case 0xA6: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 4); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xA7: + RESET_BIT(cpu, &(cpu->A), 4); + return 8; + case 0xA8: + RESET_BIT(cpu, &(cpu->B), 5); + return 8; + case 0xA9: + RESET_BIT(cpu, &(cpu->C), 5); + return 8; + case 0xAA: + RESET_BIT(cpu, &(cpu->D), 5); + return 8; + case 0xAB: + RESET_BIT(cpu, &(cpu->E), 5); + return 8; + case 0xAC: + RESET_BIT(cpu, &(cpu->H), 5); + return 8; + case 0xAD: + RESET_BIT(cpu, &(cpu->L), 5); + return 8; + case 0xAE: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 5); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xAF: + RESET_BIT(cpu, &(cpu->A), 5); + return 8; + case 0xB0: + RESET_BIT(cpu, &(cpu->B), 6); + return 8; + case 0xB1: + RESET_BIT(cpu, &(cpu->C), 6); + return 8; + case 0xB2: + RESET_BIT(cpu, &(cpu->D), 6); + return 8; + case 0xB3: + RESET_BIT(cpu, &(cpu->E), 6); + return 8; + case 0xB4: + RESET_BIT(cpu, &(cpu->H), 6); + return 8; + case 0xB5: + RESET_BIT(cpu, &(cpu->L), 6); + return 8; + case 0xB6: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 6); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xB7: + RESET_BIT(cpu, &(cpu->A), 6); + return 8; + case 0xB8: + RESET_BIT(cpu, &(cpu->B), 7); + return 8; + case 0xB9: + RESET_BIT(cpu, &(cpu->C), 7); + return 8; + case 0xBA: + RESET_BIT(cpu, &(cpu->D), 7); + return 8; + case 0xBB: + RESET_BIT(cpu, &(cpu->E), 7); + return 8; + case 0xBC: + RESET_BIT(cpu, &(cpu->H), 7); + return 8; + case 0xBD: + RESET_BIT(cpu, &(cpu->L), 7); + return 8; + case 0xBE: + val = readMemory(cpu, getHL(cpu)); + RESET_BIT(cpu, &val, 7); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xBF: + RESET_BIT(cpu, &(cpu->A), 7); + return 8; + + case 0xC0: + SET_BIT(cpu, &(cpu->B), 0); + return 8; + case 0xC1: + SET_BIT(cpu, &(cpu->C), 0); + return 8; + case 0xC2: + SET_BIT(cpu, &(cpu->D), 0); + return 8; + case 0xC3: + SET_BIT(cpu, &(cpu->E), 0); + return 8; + case 0xC4: + SET_BIT(cpu, &(cpu->H), 0); + return 8; + case 0xC5: + SET_BIT(cpu, &(cpu->L), 0); + return 8; + case 0xC6: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 0); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xC7: + SET_BIT(cpu, &(cpu->A), 0); + return 8; + case 0xC8: + SET_BIT(cpu, &(cpu->B), 1); + return 8; + case 0xC9: + SET_BIT(cpu, &(cpu->C), 1); + return 8; + case 0xCA: + SET_BIT(cpu, &(cpu->D), 1); + return 8; + case 0xCB: + SET_BIT(cpu, &(cpu->E), 1); + return 8; + case 0xCC: + SET_BIT(cpu, &(cpu->H), 1); + return 8; + case 0xCD: + SET_BIT(cpu, &(cpu->L), 1); + return 8; + case 0xCE: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 1); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xCF: + SET_BIT(cpu, &(cpu->A), 1); + return 8; + case 0xD0: + SET_BIT(cpu, &(cpu->B), 2); + return 8; + case 0xD1: + SET_BIT(cpu, &(cpu->C), 2); + return 8; + case 0xD2: + SET_BIT(cpu, &(cpu->D), 2); + return 8; + case 0xD3: + SET_BIT(cpu, &(cpu->E), 2); + return 8; + case 0xD4: + SET_BIT(cpu, &(cpu->H), 2); + return 8; + case 0xD5: + SET_BIT(cpu, &(cpu->L), 2); + return 8; + case 0xD6: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 2); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xD7: + SET_BIT(cpu, &(cpu->A), 2); + return 8; + case 0xD8: + SET_BIT(cpu, &(cpu->B), 3); + return 8; + case 0xD9: + SET_BIT(cpu, &(cpu->C), 3); + return 8; + case 0xDA: + SET_BIT(cpu, &(cpu->D), 3); + return 8; + case 0xDB: + SET_BIT(cpu, &(cpu->E), 3); + return 8; + case 0xDC: + SET_BIT(cpu, &(cpu->H), 3); + return 8; + case 0xDD: + SET_BIT(cpu, &(cpu->L), 3); + return 8; + case 0xDE: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 3); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xDF: + SET_BIT(cpu, &(cpu->A), 3); + return 8; + case 0xE0: + SET_BIT(cpu, &(cpu->B), 4); + return 8; + case 0xE1: + SET_BIT(cpu, &(cpu->C), 4); + return 8; + case 0xE2: + SET_BIT(cpu, &(cpu->D), 4); + return 8; + case 0xE3: + SET_BIT(cpu, &(cpu->E), 4); + return 8; + case 0xE4: + SET_BIT(cpu, &(cpu->H), 4); + return 8; + case 0xE5: + SET_BIT(cpu, &(cpu->L), 4); + return 8; + case 0xE6: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 4); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xE7: + SET_BIT(cpu, &(cpu->A), 4); + return 8; + case 0xE8: + SET_BIT(cpu, &(cpu->B), 5); + return 8; + case 0xE9: + SET_BIT(cpu, &(cpu->C), 5); + return 8; + case 0xEA: + SET_BIT(cpu, &(cpu->D), 5); + return 8; + case 0xEB: + SET_BIT(cpu, &(cpu->E), 5); + return 8; + case 0xEC: + SET_BIT(cpu, &(cpu->H), 5); + return 8; + case 0xED: + SET_BIT(cpu, &(cpu->L), 5); + return 8; + case 0xEE: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 5); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xEF: + SET_BIT(cpu, &(cpu->A), 5); + return 8; + case 0xF0: + SET_BIT(cpu, &(cpu->B), 6); + return 8; + case 0xF1: + SET_BIT(cpu, &(cpu->C), 6); + return 8; + case 0xF2: + SET_BIT(cpu, &(cpu->D), 6); + return 8; + case 0xF3: + SET_BIT(cpu, &(cpu->E), 6); + return 8; + case 0xF4: + SET_BIT(cpu, &(cpu->H), 6); + return 8; + case 0xF5: + SET_BIT(cpu, &(cpu->L), 6); + return 8; + case 0xF6: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 6); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xF7: + SET_BIT(cpu, &(cpu->A), 6); + return 8; + case 0xF8: + SET_BIT(cpu, &(cpu->B), 7); + return 8; + case 0xF9: + SET_BIT(cpu, &(cpu->C), 7); + return 8; + case 0xFA: + SET_BIT(cpu, &(cpu->D), 7); + return 8; + case 0xFB: + SET_BIT(cpu, &(cpu->E), 7); + return 8; + case 0xFC: + SET_BIT(cpu, &(cpu->H), 7); + return 8; + case 0xFD: + SET_BIT(cpu, &(cpu->L), 7); + return 8; + case 0xFE: + val = readMemory(cpu, getHL(cpu)); + SET_BIT(cpu, &val, 7); + writeMemory(cpu, getHL(cpu), val); + return 16; + case 0xFF: + SET_BIT(cpu, &(cpu->A), 7); + return 8; + + default: + fprintf(stderr, "Can't decode CB %x opcode (PC = %x)\n", opcode, + cpu->progCounter); + return 1; + } +} diff --git a/lib/gb-sound.c b/lib/gb-sound.c new file mode 100755 index 0000000..939ccc8 --- /dev/null +++ b/lib/gb-sound.c @@ -0,0 +1,277 @@ +#include "gb-impl.h" +#include "gb-sound.h" + +void increase_sound_timers(gb *cpu) { + if (CHANNEL_1_LENGTH_ENABLED != 0) { + cpu->channel_1.length_timer++; + if (cpu->channel_1.length_timer >= 64) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFE; + } + } + if(CHANNEL_2_LENGTH_ENABLED != 0){ + cpu->channel_2.length_timer++; + if(cpu->channel_2.length_timer >= 64) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFD; + } + } + if(CHANNEL_3_LENGTH_ENABLED != 0){ + cpu->channel_3.length_timer++; + if(cpu->channel_3.length_timer >= 256) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFB; + } + } + if(CHANNEL_4_LENGTH_ENABLED != 0){ + cpu->channel_4.length_timer++; + if(cpu->channel_4.length_timer >= 64) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xF7; + } + } +} + +void increase_vol_envelope(gb *cpu){ + if (cpu->channel_1.env_sweep_pace != 0) { + cpu->channel_1.env_vol += (cpu->channel_1.env_sweep_pace == 0) ? -1 : 1; + if (cpu->channel_1.env_vol < 0) cpu->channel_1.env_vol = 0; + if (cpu->channel_1.env_vol > 15) cpu->channel_1.env_vol = 15; + } + if(cpu->channel_2.env_sweep_pace !=0) { + cpu->channel_2.env_vol += (cpu->channel_2.env_dir == 0) ? -1 : 1; + if (cpu->channel_2.env_vol < 0) cpu->channel_2.env_vol = 0; + if (cpu->channel_2.env_vol > 15) cpu->channel_2.env_vol = 15; + } + if(cpu->channel_4.env_sweep_pace !=0) { + cpu->channel_4.env_vol += (cpu->channel_4.env_dir == 0) ? -1 : 1; + if (cpu->channel_4.env_vol < 0) cpu->channel_4.env_vol = 0; + if (cpu->channel_4.env_vol > 15) cpu->channel_4.env_vol = 15; + } +} + +void increase_sweep(gb *cpu){ + if(CHANNEL_1_SWEEP_PACE !=0) { + WORD newPeriod = CHANNEL_1_PERIOD_CLOCK; + newPeriod = CHANNEL_1_SWEEP_DIR != 0? newPeriod - (CHANNEL_1_PERIOD_CLOCK >> CHANNEL_1_SWEEP_STEP) : newPeriod + (CHANNEL_1_PERIOD_CLOCK >> CHANNEL_1_SWEEP_STEP); + NR13 = newPeriod & 0xFF; + NR14 = (NR14 & 0xF8) | (newPeriod >> 8); + } +} + +void handleSound(gb *cpu, BYTE clocks) { + + BYTE s = 0; + + if (AUDIO_OFF) + return; + + cpu->channel_1.period_clock -= clocks; + if (cpu->channel_1.period_clock < 0) { + s = squareWave[CHANNEL_1_WAVE][cpu->channel_1.pos & 7]; + cpu->channel_1.pos++; + cpu->channel_1.period_clock += (2048 - CHANNEL_1_PERIOD_CLOCK) *4; + + s *= cpu->channel_1.env_vol; + + if (CHANNEL_1_ON != 0 && CHANNEL_1_RIGHT_ACTIVE != 0) cpu->rightSoundBuffer += s; + if (CHANNEL_1_ON != 0 && CHANNEL_1_LEFT_ACTIVE != 0) cpu->leftSoundBuffer += s; + } + + cpu->channel_2.period_clock -= clocks; + if(cpu->channel_2.period_clock < 0) { + s = squareWave[CHANNEL_2_WAVE][cpu->channel_2.pos & 7]; + cpu->channel_2.pos++; + cpu->channel_2.period_clock += (2048 - CHANNEL_2_PERIOD_CLOCK) *4; + + s *= cpu->channel_2.env_vol; + if (CHANNEL_2_ON != 0 && CHANNEL_2_RIGHT_ACTIVE != 0) cpu->rightSoundBuffer += s; + if (CHANNEL_2_ON != 0 && CHANNEL_2_LEFT_ACTIVE !=0) cpu->leftSoundBuffer += s; + } + + cpu->channel_3.period_clock -= clocks; + if(cpu->channel_3.period_clock < 0) { + s = CHANNEL_3_WAVE[cpu->channel_3.pos / 2] &0xFF; + //If pos is even, we need the upper nibble, otherwise the lower one + s = cpu->channel_3.pos % 2 == 0 ? s >> 4 : s & 0xF; + cpu-> channel_3.pos = cpu->channel_3.pos++ &0x1F; + cpu->channel_3.period_clock += (2048 - CHANNEL_3_PERIOD_CLOCK) *2; + if(CHANNEL_3_OUTPUT_LEVEL == 0) { + s = 0; + } else { + s >>= CHANNEL_3_OUTPUT_LEVEL -1; + } + + if (CHANNEL_3_ON != 0 && CHANNEL_3_RIGHT_ACTIVE != 0) cpu->rightSoundBuffer += s; + if (CHANNEL_3_ON != 0 && CHANNEL_3_LEFT_ACTIVE !=0) cpu->leftSoundBuffer += s; + } + + cpu->channel_4.period_clock -= clocks; + if(cpu->channel_4.period_clock <= 0) { + s = noise[cpu->channel_4.pos & 0xF] & 0x1; + s *= cpu->channel_4.env_vol; + cpu->channel_4.pos++; + cpu->channel_4.period_clock += CHANNEL_4_FREQ_MULTIPLIER; + + if (CHANNEL_4_ON != 0 && CHANNEL_4_RIGHT_ACTIVE != 0) cpu->rightSoundBuffer += s; + if (CHANNEL_4_ON != 0 && CHANNEL_4_LEFT_ACTIVE != 0) cpu->leftSoundBuffer += s; + } + + cpu->soundFrameClock -= clocks; + if(cpu->soundFrameClock <= 0){ + cpu->soundFrameClock += 8192; //512 hz + switch (cpu->soundFramePos) { + case 0: + case 4: + increase_sound_timers(cpu); + break; + case 2: + case 6: + increase_sound_timers(cpu); + //increase sweep + break; + case 7: + increase_vol_envelope(cpu); + break; + default: + break; + } + cpu->soundFramePos = cpu->soundFramePos++ & 0x7; + } + + cpu->soundMasterClock -= clocks; + if (cpu->soundMasterClock <= 0) { + cpu->soundMasterClock += GB_CLOCK / 44100; + cpu->leftSoundBuffer *= VOLUME_LEFT; + cpu->rightSoundBuffer *= VOLUME_RIGHT; + if (cpu->leftSoundBuffer > 127) cpu->leftSoundBuffer = 127; + else if (cpu->leftSoundBuffer < -128) cpu->leftSoundBuffer = -128; + if (cpu->rightSoundBuffer > 127) cpu->rightSoundBuffer = 127; + else if (cpu->rightSoundBuffer < -128) cpu->rightSoundBuffer = -128; + + cpu->soundData[cpu->numerOfBytes++] = cpu->leftSoundBuffer + 128; + cpu->soundData[cpu->numerOfBytes++] = cpu->rightSoundBuffer + 128; + cpu->leftSoundBuffer = 0; + cpu->rightSoundBuffer = 0; + } +} + +void writeSoundRegistry(gb *cpu, WORD addr, BYTE data){ + + if(addr == 0xFF11) { + BYTE lengthTimer = data &0x3F; + cpu->channel_1.length_timer = lengthTimer; + cpu->memory[addr] = data; + } + else if(addr == 0xFF12) { + cpu->memory[addr] = data; + if((data & 0xF8) == 0){ + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFE; + } + } + else if(addr == 0xFF13) { + cpu->memory[addr] = data; + } + else if(addr == 0xFF14) { + BYTE triggering = data &0x80; + if(triggering != 0) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] | 0x1; + cpu->channel_1.pos = 0; + cpu->channel_1.period_clock = (2048 - CHANNEL_1_PERIOD_CLOCK) *4; + cpu->channel_1.length_timer = NR11 & 0x3F; + cpu->channel_1.env_vol = NR12 >> 4; + cpu->channel_1.env_dir = (NR12 & 0x8) >> 4; + cpu->channel_1.env_sweep_pace = NR12 & 0x7; + } + cpu->memory[addr] = data; + } + + else if(addr == 0xFF16) { + BYTE lengthTimer = data &0x3F; + cpu->channel_2.length_timer = lengthTimer; + cpu->memory[addr] = data; + } + else if(addr == 0xFF17) { + cpu->memory[addr] = data; + if((data & 0xF8) == 0){ + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFD; + } + } + else if(addr == 0xFF18) { + cpu->memory[addr] = data; + } + else if(addr == 0xFF19) { + BYTE triggering = data &0x80; + if(triggering != 0) { + cpu->channel_2.pos = 0; + cpu->channel_2.period_clock = (2048 - CHANNEL_2_PERIOD_CLOCK) *4; + cpu->channel_2.length_timer = NR21 & 0x3F; + cpu->channel_2.env_vol = NR22 >> 4; + cpu->channel_2.env_dir = (NR22 & 0x8) >> 4; + cpu->channel_2.env_sweep_pace = NR22 & 0x7; + cpu->memory[0xFF26] = cpu->memory[0xFF26] | 0x2; + } + cpu->memory[addr] = data; + } + else if(addr == 0xFF1A) { + BYTE on = data & 0x80; + if(on == 0) { + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xFB; + } + cpu->memory[addr] = data; + } + else if(addr == 0xFF1B) { + cpu->channel_3.length_timer = data; + cpu->memory[addr] = data; + } + else if(addr == 0xFF1C) { + cpu->memory[addr] = data; + } + else if(addr == 0xFF1D) { + cpu->memory[addr] = data; + } + else if(addr == 0xFF1E) { + BYTE triggering = data &0x80; + if(triggering != 0) { + cpu->channel_3.pos = 1; + cpu->channel_3.period_clock = (2048 - CHANNEL_3_PERIOD_CLOCK) *2; + cpu->channel_3.length_timer = NR32; + cpu->memory[0xFF26] = cpu->memory[0xFF26] | 0x4; + } + cpu->memory[addr] = data; + } + + + else if(addr == 0xFF41) { + BYTE lengthTimer = data &0x3F; + cpu->channel_4.length_timer = lengthTimer; + cpu->memory[addr] = data; + } + else if(addr == 0xFF42) { + cpu->memory[addr] = data; + if((data & 0xF8) == 0){ + cpu->memory[0xFF26] = cpu->memory[0xFF26] & 0xF7; + } + } + else if(addr == 0xFF43) { + cpu->memory[addr] = data; + } + else if(addr == 0xFF44) { + BYTE triggering = data &0x80; + if(triggering != 0) { + cpu->channel_4.env_vol = NR42 >> 4; + cpu->channel_4.length_timer = NR41 & 0x3F; + cpu->channel_4.pos = 0; + cpu->channel_4.env_dir = (NR42 & 0x8) >> 4; + cpu->channel_4.env_sweep_pace = NR42 & 0x7; + cpu->channel_4.period_clock = 0; + cpu->memory[0xFF26] = cpu->memory[0xFF26] | 0x8; + } + cpu->memory[addr] = data; + } + + else if(addr == 0xFF26) { + BYTE val = data & 0x80; + cpu->memory[addr] = val | cpu->memory[addr]; + } + else { + cpu->memory[addr] = data; + } +} + diff --git a/lib/gb-sound.h b/lib/gb-sound.h new file mode 100755 index 0000000..e9582e2 --- /dev/null +++ b/lib/gb-sound.h @@ -0,0 +1,86 @@ +#ifndef __GB_SOUND_H__ +#define __GB_SOUND_H__ + +#include "gb-impl.h" + +const static BYTE squareWave[4][8] ={ + { 0, 0,0, 0, 0, 0, 0, 1 }, + { 1,0, 0, 0, 0, 0, 0, 1 }, + { 1,0,0,0, 0, 1, 1, 1 }, + { 0,1,1,1,1,1, 1, 0 } +}; + +const static BYTE noise[] = { + 0xfb,0xe7,0xae,0x1b,0xa6,0x2b,0x05,0xe3, + 0xb6,0x4a,0x42,0x72,0xd1,0x19,0xaa,0x03, +}; + +#define NR50 cpu->memory[0xFF24] +#define NR51 cpu->memory[0xFF25] +#define NR52 cpu->memory[0xFF26] + +#define NR10 cpu->memory[0xFF10] +#define NR11 cpu->memory[0xFF11] +#define NR12 cpu->memory[0xFF12] +#define NR13 cpu->memory[0xFF13] +#define NR14 cpu->memory[0xFF14] + +#define NR21 cpu->memory[0xFF16] +#define NR22 cpu->memory[0xFF17] +#define NR23 cpu->memory[0xFF18] +#define NR24 cpu->memory[0xFF19] + +#define NR32 cpu->memory[0xFF1C] +#define NR33 cpu->memory[0xFF1D] +#define NR34 cpu->memory[0xFF1E] + +#define NR41 cpu->memory[0xFF20] +#define NR42 cpu->memory[0xFF21] +#define NR43 cpu->memory[0xFF22] +#define NR44 cpu->memory[0xFF23] + +#define AUDIO_OFF ((NR52 & 0x80) == 0) +#define VOLUME_LEFT ((NR50 & 0x70 )>> 4 ) +#define VOLUME_RIGHT (NR50 & 0x7 ) + + +#define CHANNEL_1_ON (NR52 & 0x1) +#define CHANNEL_1_PERIOD_CLOCK (((NR14 & 0x7) << 8) | NR13) +#define CHANNEL_1_WAVE ((NR11 & 0xC0) >> 6) +#define CHANNEL_1_LENGTH_ENABLED (NR14 & 0x40) +#define CHANNEL_1_ENVELOP_DIR (NR12 & 0x8) +#define CHANNEL_1_SWEEP_PACE ((NR11 & 0x70) >> 4) +#define CHANNEL_1_SWEEP_DIR ((NR11 & 0x08) >> 3) +#define CHANNEL_1_SWEEP_STEP (NR11 & 0x07) + +#define CHANNEL_1_LEFT_ACTIVE (NR51 & 0x10 ) +#define CHANNEL_1_RIGHT_ACTIVE (NR51 & 0x1 ) + +#define CHANNEL_2_ON (NR52 & 0x2) +#define CHANNEL_2_PERIOD_CLOCK (((NR24 & 0x7) << 8) | NR23) +#define CHANNEL_2_WAVE ((NR21 & 0xC0) >> 6) +#define CHANNEL_2_LENGTH_ENABLED (NR24 & 0x40) +#define CHANNEL_2_SWEEP_PACE (NR22 & 0x7) +#define CHANNEL_2_ENVELOP_DIR (NR22 & 0x8) +#define CHANNEL_2_LEFT_ACTIVE (NR51 & 0x20 ) +#define CHANNEL_2_RIGHT_ACTIVE (NR51 & 0x2 ) + +#define CHANNEL_3_ON (NR52 & 0x4) +#define CHANNEL_3_OUTPUT_LEVEL ((NR32 >> 5) & 0x3) +#define CHANNEL_3_LENGTH_ENABLED (NR34 & 0x40) +#define CHANNEL_3_WAVE (cpu->memory + 0xFF30) +#define CHANNEL_3_PERIOD_CLOCK (((NR34 & 0x7) << 8) | NR33) + +#define CHANNEL_3_LEFT_ACTIVE (NR51 & 0x40 ) +#define CHANNEL_3_RIGHT_ACTIVE (NR51 & 0x4 ) + +#define CHANNEL_4_ON (NR52 & 0x4) +#define CHANNEL_4_LFSR (NR43 & 0x8) +#define CHANNEL_4_FREQ_MULTIPLIER ((NR43 & 0x7) << (NR43 >> 4)) +#define CHANNEL_4_LENGTH_ENABLED (NR44 & 0x40) +#define CHANNEL_4_SWEEP_PACE (NR42 & 0x7) +#define CHANNEL_4_ENVELOP_DIR (NR42 & 0x8) +#define CHANNEL_4_LEFT_ACTIVE (NR51 & 0x80 ) +#define CHANNEL_4_RIGHT_ACTIVE (NR51 & 0x8 ) + +#endif diff --git a/lib/gb-video.c b/lib/gb-video.c new file mode 100755 index 0000000..69bb7fa --- /dev/null +++ b/lib/gb-video.c @@ -0,0 +1,328 @@ +#include "gb-impl.h" + + BYTE getColour(BYTE colourNum, BYTE palette) { + // use the palette to get the colour + return (palette >> (colourNum * 2)) & 0x3; +} + +void renderTiles(gb *cpu) { + WORD tileData = 0; + WORD backgroundMemory = 0; + BYTE unsig = 1; + + BYTE lcd_control = readMemory(cpu, LCD_REG_CONTROL); + + BYTE scrollY = readMemory(cpu, SCROLLY); + BYTE scrollX = readMemory(cpu, SCROLLX); + BYTE windowY = readMemory(cpu, WINDOWY); + BYTE windowX = readMemory(cpu, WINDOWX); + + if (windowX < 7) { + windowX = 7; + } + windowX -= 7; + + BYTE currentLine = readMemory(cpu, LCD_SCANLINE_ADRR); + BYTE usingWindow = 0; + + /*The window is enabled and visible?*/ + if ((lcd_control & 0x20) != 0 && windowY <= currentLine) + usingWindow = 1; + + // tile area ? + if ((lcd_control & 0x10) != 0) + tileData = 0x8000; + else { + // This memory region uses signed bytes + tileData = 0x8800; + unsig = 0; + } + + BYTE yPos = 0; + + // yPos is used to calculate which of 32 vertical tiles the + // current scanline is drawing + if (usingWindow == 0) + yPos = scrollY + currentLine; + else { + yPos = currentLine - windowY; + } + + // which line of the tile is being rendered ? + WORD tileRow = (yPos / 8) * 32; + + BYTE palette = readMemory(cpu, 0xFF47); + BYTE colorMap[4] = {getColour(0, palette), getColour(1, palette), + getColour(2, palette), getColour(3, palette)}; + + // Now I have to render the line, pixel by pixel + for (BYTE pixel = 0; pixel < 160;) { + BYTE xPos = pixel + scrollX; + + if ((lcd_control & 0x08) != 0) + backgroundMemory = 0x9C00; + else + backgroundMemory = 0x9800; + + // translate the current x pos to window space if necessary + if (usingWindow == 1) { + if (pixel >= windowX) { + // This means I'm in the window + xPos = pixel - windowX; + if ((lcd_control & 0x40) != 0) + backgroundMemory = 0x9c00; + else + backgroundMemory = 0x9800; + } + } + + // which of the 32 horizontal tiles does this xPos fall within? + WORD tileCol = (xPos / 8); + WORD tileNum; + + WORD tileAddrss = backgroundMemory + tileRow + tileCol; + if (unsig == 1) + tileNum = (BYTE)readMemory(cpu, tileAddrss); + else { + tileNum = + ((SIGNED_WORD)readMemory(cpu, tileAddrss) + (SIGNED_WORD)128) & + 0xff; + } + + // deduce where this tile identifier is in memory. + WORD tileLocation = tileData + (tileNum * 16); + + BYTE line = yPos % 8; + line *= 2; // each line takes two bytes + BYTE data1 = readMemory(cpu, tileLocation + line); + BYTE data2 = readMemory(cpu, tileLocation + line + 1); + + SIGNED_BYTE colourBit = (xPos & 0x7); + colourBit -= 7; + colourBit *= -1; + + for(; colourBit >= 0; colourBit--) { + BYTE colourNum = (data2 >> (colourBit)) & 0x1; + colourNum <<= 1; + colourNum |= ((data1 >> (colourBit)) & 0x1); + + BYTE col = colorMap[colourNum]; + + cpu->screenData[currentLine][pixel] = col; + pixel++; + if(pixel >= 160) { + break; + } + } + } +} + +void renderSprites(gb *cpu) { + BYTE ysize = 8; + BYTE currentLine = readMemory(cpu, 0xFF44); + BYTE lcd_control = readMemory(cpu, LCD_REG_CONTROL); + if ((lcd_control & 0x4) != 0) + ysize = 16; + + /*Explore all the sprite table*/ + /*Each sprite is formed by 4 bytes: + * 0 : y pos - 16 + * 1 : x pos - 8 + * 2 : data location in memory + * 3 : several flags + */ + for (BYTE index = 0; index < 40 * 4; index += 4) { + BYTE yPos = readMemory(cpu, 0xFE00 + index) - 16; + + if ((currentLine < yPos) || (currentLine >= (yPos + ysize))) + continue; + + BYTE xPos = readMemory(cpu, 0xFE00 + index + 1) - 8; + + if (xPos > 160) + continue; + + BYTE tileLocation = readMemory(cpu, 0xFE00 + index + 2); + BYTE attributes = readMemory(cpu, 0xFE00 + index + 3); + + BYTE yFlip = attributes & 0x40; + BYTE xFlip = attributes & 0x20; + + WORD colourAddress; + if ((attributes & 0x10) != 0) + colourAddress = 0xFF49; + else + colourAddress = 0xFF48; + + // does this sprite intercept with the scanline? + BYTE line = currentLine - yPos; + + // read the sprite in backwards in the y axis + if (yFlip != 0) { + line -= ysize; + line *= -1; + } + + line *= 2; // same as for tiles + WORD dataAddress = (0x8000 + (tileLocation * 16)) + line; + BYTE data1 = readMemory(cpu, dataAddress); + BYTE data2 = readMemory(cpu, dataAddress + 1); + + BYTE palette = readMemory(cpu, colourAddress); + + // its easier to read in from right to left as pixel 0 is + // bit 7 in the colour data, pixel 1 is bit 6 etc... + for (SIGNED_BYTE tilePixel = 7; tilePixel >= 0; tilePixel--) { + SIGNED_BYTE xPix = 0 - tilePixel; + xPix += 7; + BYTE pixel = xPos + (BYTE)xPix; + + if (pixel > 160 || currentLine > 144) + continue; + + // check if pixel is hidden behind background + if ((attributes & 0x80) != 0 && + cpu->screenData[currentLine][pixel] != 0) { + continue; + } + + /*Should do some refactoring for avoiding this signed pixel*/ + SIGNED_BYTE colourbit = tilePixel; + // read the sprite in backwards for the x axis + if (xFlip != 0) { + colourbit -= 7; + colourbit *= -1; + } + + BYTE colourNum = (data2 >> (colourbit)) & 0x1; + colourNum <<= 1; + colourNum |= ((data1 >> (colourbit)) & 0x1); + + /*Must ignore the alpha color*/ + if (colourNum == 0) + continue; + + BYTE col = getColour(colourNum, palette); + + /*I can draw the pixel*/ + cpu->screenData[currentLine][pixel] = col; + } + } +} + +void drawScanline(gb *cpu) { + BYTE control = readMemory(cpu, LCD_REG_CONTROL); + if ((control & 0x1) != 0) + renderTiles(cpu); + + if ((control & 0x2) != 0) + renderSprites(cpu); +} + +void updateLCD(gb *cpu) { + BYTE status = readMemory(cpu, LCD_REG_STATUS); + BYTE lcd_control = readMemory(cpu, LCD_REG_CONTROL); + + /*LCD is disabled*/ + if ((lcd_control & 0x80) == 0) { + // set the mode to 1 during lcd disabled and reset scanline + cpu->clockScanline = 456; + cpu->memory[0xFF44] = 0; + status &= 252; + status |= 0x1; + writeMemory(cpu, LCD_REG_STATUS, status); + return; + } + + BYTE currentline = readMemory(cpu, LCD_SCANLINE_ADRR); + BYTE mode = status & 0x3; + + BYTE nextMode = 0; + BYTE reqInt = 0; + + // in vblank so set mode to 1 + if (currentline >= 144) { + nextMode = 1; + status &= ~(0x3); + status |= 0x1; + reqInt = status & 0x10; + } else { + // mode 2 + if (cpu->clockScanline >= 376) { + nextMode = 2; + status &= ~(0x3); + status |= 0x2; + reqInt = status & 0x20; + } + // mode 3 + else if (cpu->clockScanline >= 204) { + nextMode = 3; + status &= ~(0x3); + status |= 0x3; + } + // mode 0 + else { + nextMode = 0; + status &= ~(0x3); + reqInt = status & 0x8; + } + } + + if ((reqInt != 0) && (mode != nextMode)) + raiseInterrupt(cpu, 1); + + // check the conincidence flag + if (readMemory(cpu, 0xFF44) == readMemory(cpu, 0xFF45)) { + status |= 0x4; + if ((status & 0x40) != 0) + raiseInterrupt(cpu, 1); + } else { + status &= ~(0x4); + } + writeMemory(cpu, LCD_REG_STATUS, status); +} + +void handleGraphic(gb *cpu, BYTE cycles) { + updateLCD(cpu); + BYTE lcd_control = cpu->memory[LCD_REG_CONTROL]; + + if ((lcd_control & 0x80) == 0) { + return; + } + cpu->clockScanline -= cycles; + if (cpu->clockScanline <= 0) { + BYTE currentline = cpu->memory[LCD_SCANLINE_ADRR]; + cpu->clockScanline += 456; + + /*V-blank interrupt*/ + if (currentline == 144) + raiseInterrupt(cpu, 0); + + else if (currentline > 153) + cpu->memory[LCD_SCANLINE_ADRR] = 0; + + else if (currentline < 144) + drawScanline(cpu); + cpu->memory[LCD_SCANLINE_ADRR]++; + } +} + +BYTE getPixelColor(gb *cpu, BYTE x, BYTE y) { + BYTE col = cpu->screenData[y][x]; + BYTE color = 0; + switch (col) { + case 0: + color = BLACK_COL; + break; + case 1: + color = GRAY_COL; + break; + case 2: + color = LIGHTGRAY_COL; + break; + case 3: + color = WHITE_COL; + break; + } + return color; +} diff --git a/lib/gc-imp.c b/lib/gc-imp.c new file mode 100755 index 0000000..627d62d --- /dev/null +++ b/lib/gc-imp.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include "gb-impl.h" +#include "gb-opcode.h" + +void loadROM(gb *cpu, char *rom) { + FILE *f = fopen(rom, "rb"); + if (!f) { + fprintf(stderr, "Can't load ROM\n"); + exit(2); + } + fread(cpu->cartridge, 1, CARTRIDGE_SIZE, f); + fclose(f); + + switch (cpu->cartridge[0x147]) { + case 0: + case 1: + case 2: + case 3: + case 0x13: + break; + + default: + printf("Can't execute this rom (ROM code: %x)\n", + cpu->cartridge[0x147]); + exit(EXIT_FAILURE); + } +} + +void no_changeBank(gb *cpu, WORD addr, BYTE data) { +} + +void initGameBoy(gb *cpu) { + cpu->changeBank = no_changeBank; + cpu->progCounter = 0x100; + cpu->whenDisableInterrupts = 0; + cpu->enable_interr = 0; + cpu->stack = 0xFFFE; + + memset(cpu->memory, 0, MEMORY_SIZE); + + cpu->master_interr_switch = 0; + + cpu->clockScanline = 456; + cpu->clockBeforeTimer = 1024; + cpu->clockBeforeDividerTimer = 256; + cpu->ROMType = 0; + cpu->currentROMBank = 1; + cpu->cpuHalted = 1; + cpu->whenDisableInterrupts = 0; + cpu->keymap = 0xFF; + cpu->soundMasterClock = GB_CLOCK / 44100; + + setGbBanking(cpu); +} + +void DMATransfert(gb *cpu, BYTE data) { + WORD addr = data * 0x100; + for (int i = 0; i < 0xA0; i++) { + writeMemory(cpu, 0xFE00 + i, readMemory(cpu, addr + i)); + } +} + +BYTE getKeypad(gb *cpu) { + BYTE mem = cpu->memory[0xFF00]; + + mem ^= 0xFF; + if ((mem & 0x10) == 0) { + BYTE currentPad = cpu->keymap >> 4; + currentPad |= 0xF0; + mem &= currentPad; + mem ^= 0x30; + } else if ((mem & 0x20) == 0) { + BYTE currentPad = cpu->keymap & 0xF; + currentPad |= 0xF0; + mem &= currentPad; + mem ^= 0x30; + } + + return mem; +} + +void keyReleased(gb *cpu, int key) { + cpu->keymap |= (0x1 << key); +} + +void keyPressed(gb *cpu, int key) { + BYTE previouslyUnset = 0; + + // if setting from 1 to 0 we may have to request an interupt + if (((cpu->keymap >> key) & 0x1) == 0) + previouslyUnset = 1; + + // a keypressed is 0 not 1 + cpu->keymap &= ~(0x1 << key); + + // button pressed + BYTE button = 1; + + // is this a standard button or a directional button? + if (key > 3) + button = 1; + else + button = 0; + + BYTE keyReq = cpu->memory[0xFF00]; + BYTE requestInterupt = 0; + + // only request interupt if the button just pressed is + // the style of button the game is interested in + if (button && (((keyReq >> 5) & 0x1) == 0)) + requestInterupt = 1; + + // same as above but for directional button + else if (button && (((keyReq >> 4) & 0x1) == 0)) + requestInterupt = 1; + + // request interupt + if (requestInterupt && (previouslyUnset == 0)) + raiseInterrupt(cpu, 4); +} + +void changeKeyState(gb *cpu, BYTE key, BYTE state) { + if (state == GB_KEY_PRESSED) { + keyPressed(cpu, key); + } else + keyReleased(cpu, key); +} + +BYTE executeGameBoy(gb *cpu) { + BYTE opcode; + BYTE numClock = 4; + + if (cpu->cpuHalted != 0) { + GET_BYTE_PC(cpu, opcode); + numClock = executeOpcode(cpu, opcode); + + if (cpu->whenDisableInterrupts > 0) { + cpu->whenDisableInterrupts--; + if (cpu->whenDisableInterrupts == 0) + cpu->master_interr_switch = cpu->enable_interr; + } + } + + increaseTimer(cpu, numClock); + handleInterrupts(cpu); + handleGraphic(cpu, numClock); + //handleSound(cpu, numClock); + + return numClock; +} + +gb *newGameboy(char *rom) { + gb *game = malloc(sizeof(gb)); + if (!game) { + return NULL; + } + loadROM(game, rom); + initGameBoy(game); + return game; +} + +char *getGameName(gb *cpu) { + return (char *)cpu->cartridge + NAME_CART; +} + +BYTE *getSoundData(gb *cpu, unsigned int *len) { + *len = cpu->numerOfBytes; + cpu->numerOfBytes = 0; + return cpu->soundData; +} diff --git a/lib/gc-interrupts.c b/lib/gc-interrupts.c new file mode 100755 index 0000000..b4e8e20 --- /dev/null +++ b/lib/gc-interrupts.c @@ -0,0 +1,76 @@ +#include "gb-impl.h" + +void setTimerFreq(gb *cpu) { + BYTE freq = readMemory(cpu, TIMER_CONTROLLER) & 0x3; + switch (freq) { + case 0: + cpu->clockBeforeTimer = 1024; + break; // freq 4096 + case 1: + cpu->clockBeforeTimer = 16; + break; // freq 262144 + case 2: + cpu->clockBeforeTimer = 64; + break; // freq 65536 + case 3: + cpu->clockBeforeTimer = 256; + break; // freq 16382 + } +} + +void increaseTimer(gb *cpu, BYTE clocks) { + //Handle divider timer + cpu->clockBeforeDividerTimer -= clocks; + if (cpu->clockBeforeDividerTimer <= 0) { + cpu->clockBeforeDividerTimer += 255; + cpu->memory[DIVIDER_TIMER]++; + } + /*Timer is enabled ?*/ + if ((readMemory(cpu, TIMER_CONTROLLER) & 0x4) != 0) { + cpu->clockBeforeTimer -= clocks; + if (cpu->clockBeforeTimer <= 0) { + BYTE currentTimer = readMemory(cpu, TIMER_ADDR); + + setTimerFreq(cpu); + writeMemory(cpu, TIMER_ADDR, currentTimer + 1); + /*The timer should overflow ?*/ + if (currentTimer == 255) { + writeMemory(cpu, TIMER_ADDR, readMemory(cpu, TIMER_DEFAULT)); + raiseInterrupt(cpu, 2); + } + } + } +} + +void raiseInterrupt(gb *cpu, BYTE code) { + BYTE interruptRequest = readMemory(cpu, INTERRUPT_REQUEST_ADDR); + interruptRequest |= (0x1 << code); + writeMemory(cpu, INTERRUPT_REQUEST_ADDR, interruptRequest); + cpu->cpuHalted = 1; +} + +void handleInterrupts(gb *cpu) { + BYTE req = readMemory(cpu, INTERRUPT_REQUEST_ADDR); + BYTE enabled = readMemory(cpu, INTERRUPT_ENABLED_ADDR); + + BYTE mask = req & enabled; + + if (cpu->master_interr_switch != 0 && mask != 0) { + /*Interrupts are enabled and an interrupt is set, let's raise it */ + for (BYTE i = 0; i < 5; i++) { + if ((mask & (0x1 << i)) != 0) { + //Disable other interrupts + cpu->master_interr_switch = 0; + //Mark interrupt as handled + req &= ~(0x1 << i); + writeMemory(cpu, INTERRUPT_REQUEST_ADDR, req); + + WORD currentPC = cpu->progCounter; + PUSH(cpu, cpu->stack, currentPC); + //Which routine should we use? + cpu->progCounter = 0x40 + (i<<3); + return; + } + } + } +} diff --git a/lib/mbc1.c b/lib/mbc1.c new file mode 100755 index 0000000..67251ee --- /dev/null +++ b/lib/mbc1.c @@ -0,0 +1,45 @@ +#include "gb-impl.h" + +void mbc1_changeBank(gb *cpu, WORD addr, BYTE data) { + // RAM enabling + if (addr < 0x2000) { + BYTE testData = data & 0xF; + if (testData == 0x0) { + cpu->isRAMEnable = 0; + } else { + cpu->isRAMEnable = 1; + } + } + + /*Change ROM bank*/ + else if ((addr >= 0x2000) && (addr < 0x4000)) { + BYTE lower5 = data & 31; + cpu->currentROMBank &= 224; // turn off the lower 5 + cpu->currentROMBank |= lower5; + if (cpu->currentROMBank == 0 || cpu->currentROMBank == 0x20 || + cpu->currentROMBank == 0x40 || cpu->currentROMBank == 0x60) + cpu->currentROMBank++; + } + + /*Depending from the MBC1 type, I have to change something*/ + else if ((addr >= 0x4000) && (addr < 0x6000)) { + if (cpu->ROMBankType == 1) { + cpu->currentROMBank &= 31; + data &= 0x3; + data <<= 5; + cpu->currentROMBank |= data; + if (cpu->currentROMBank == 0 || cpu->currentROMBank == 0x20 || + cpu->currentROMBank == 0x40 || cpu->currentROMBank == 0x60) + cpu->currentROMBank++; + } else + cpu->currentRAMBank = data & 0x3; + } + + /*Determine which type of MBC1 is*/ + else if ((addr >= 0x6000) && (addr < 0x8000)) { + BYTE newData = data & 0x1; + cpu->ROMBankType = (newData == 0) ? 1 : 0; + if (cpu->ROMBankType == 1) + cpu->currentRAMBank = 0; + } +} diff --git a/lib/mbc3.c b/lib/mbc3.c new file mode 100755 index 0000000..2470f41 --- /dev/null +++ b/lib/mbc3.c @@ -0,0 +1,30 @@ +#include "gb-impl.h" + +void mbc3_changeBank(gb *cpu, WORD addr, BYTE data) { + // RAM enabling + if (addr < 0x2000) { + BYTE testData = data & 0xF; + if (testData == 0x0) { + cpu->isRAMEnable = 0; + } else + cpu->isRAMEnable = 1; + } + + /*Change ROM bank*/ + else if ((addr >= 0x2000) && (addr < 0x4000)) { + BYTE lower7 = data & 0x7F; + cpu->currentROMBank = lower7; + if (cpu->currentROMBank == 0x00) + cpu->currentROMBank = 1; + + } + + else if ((addr >= 0x4000) && (addr < 0x6000)) { + if (data <= 0x3) + cpu->currentRAMBank = data & 0x3; + } + + /*Should handle the timer*/ + else if ((addr >= 0x6000) && (addr < 0x8000)) { + } +} diff --git a/lib/panzgb.h b/lib/panzgb.h new file mode 100755 index 0000000..f2de2d5 --- /dev/null +++ b/lib/panzgb.h @@ -0,0 +1,53 @@ +#ifndef PANZGB_H +#define PANZGB_H +/* + * BYTE must always be an unsigned, 8 bit value + * WORD must always be an unsigned, 16 bit value + * + * Must be changed, depending the architecture (if the compiler + * doesn't support C99 types) + */ + +#include + +typedef uint8_t BYTE; +typedef int8_t SIGNED_BYTE; +typedef uint16_t WORD; +typedef int16_t SIGNED_WORD; + +#define GB_CLOCK 4194304 +#define GB_SCREEN_REFRESH_RATE 59.7 +typedef struct gameboy gb; + +gb *newGameboy(char *rom); +char *getGameName(gb *cpu); + +BYTE executeGameBoy(gb *cpu); + +/* Those macros are for handling the gameboy keymap*/ + +#define GB_K_RIGHT 0x0 +#define GB_K_LEFT 0x1 +#define GB_K_UP 0x2 +#define GB_K_DOWN 0x3 +#define GB_K_A 0x4 +#define GB_K_B 0x5 +#define GB_K_SELECT 0x6 +#define GB_K_START 0x7 + +#define GB_KEY_PRESSED 0x0 +#define GB_KEY_RELEASED 0x1 + +void changeKeyState(gb *cpu, BYTE key, BYTE state); + +#define WHITE_COL 0x00 +#define LIGHTGRAY_COL 0x77//0x55//0x77 +#define GRAY_COL 0xcc//0xaa +#define BLACK_COL 0xff + +BYTE getPixelColor(gb *cpu, BYTE x, BYTE y); + +void setGbBanking(gb *cpu); + +BYTE* getSoundData(gb *cpu, unsigned int* size); +#endif diff --git a/panzgb.c b/panzgb.c new file mode 100755 index 0000000..2c8edb5 --- /dev/null +++ b/panzgb.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include "lib/panzgb.h" + +#define NUM_OP_60HZ (GB_CLOCK / GB_SCREEN_REFRESH_RATE) + +#define SCALE 4 + +void doScreenshoot(SDL_Renderer *renderer) { + time_t name; + name = time(NULL); + SDL_Surface *sshot = + SDL_CreateRGBSurface(0, 160 * SCALE, 144 * SCALE, 32, 0x00ff0000, + 0x0000ff00, 0x000000ff, 0xff000000); + SDL_RenderReadPixels(renderer, NULL, SDL_PIXELFORMAT_ARGB8888, + sshot->pixels, sshot->pitch); + SDL_SaveBMP(sshot, ctime(&name)); + SDL_FreeSurface(sshot); +} + +void renderScreen(gb *cpu, SDL_Renderer *rend) { + int x, y; + int j; + SDL_Rect rectangle; + rectangle.h = SCALE; + SDL_SetRenderDrawColor(rend, 0, 0, 0, SDL_ALPHA_OPAQUE); + SDL_RenderClear(rend); + for (y = 0; y < 144; y++) { + for (x = 0; x < 160; x++) { + BYTE color = getPixelColor(cpu, x, y); + for (j = x + 1; color == getPixelColor(cpu, j, y) && j < 160; j++) + ; + rectangle.w = SCALE * (j - x); + rectangle.x = x * SCALE; + rectangle.y = y * SCALE; + x = j - 1; + SDL_SetRenderDrawColor(rend, color, color, color, SDL_ALPHA_OPAQUE); + SDL_RenderFillRect(rend, &rectangle); + } + } + SDL_RenderPresent(rend); +} + +void getInput(gb *cpu, SDL_Renderer *rend) { + SDL_Event event; + SIGNED_BYTE key = 0; + + while (SDL_PollEvent(&event)) { + if (event.type == SDL_QUIT) { + exit(EXIT_SUCCESS); + } + if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP) { + switch (event.key.keysym.sym) { + case SDLK_z: + key = GB_K_A; + break; + case SDLK_x: + key = GB_K_B; + break; + case SDLK_RETURN: + key = GB_K_START; + break; + case SDLK_SPACE: + key = GB_K_SELECT; + break; + case SDLK_UP: + key = GB_K_UP; + break; + case SDLK_LEFT: + key = GB_K_LEFT; + break; + case SDLK_RIGHT: + key = GB_K_RIGHT; + break; + case SDLK_DOWN: + key = GB_K_DOWN; + break; + + case SDLK_f: + doScreenshoot(rend); + return; + + default: + return; + } + if (event.type == SDL_KEYUP) { + changeKeyState(cpu, key, GB_KEY_RELEASED); + } else { + changeKeyState(cpu, key, GB_KEY_PRESSED); + } + } + } +} + +gb *gameboy; + +int main(int argc, char **argv) { + SDL_Init(SDL_INIT_EVERYTHING); + SDL_Window *window = SDL_CreateWindow("panz-gb", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 160 * SCALE, + 144 * SCALE, 0); + SDL_Renderer *renderer = + SDL_CreateRenderer(window, -1, 0); + + gameboy = newGameboy(argv[1]); + if (!gameboy) { + fprintf(stderr, "Error on memory allocation\n"); + exit(EXIT_FAILURE); + } + + unsigned int numOperation = 0; + while (1) { + unsigned int timeStartFrame = SDL_GetTicks(); + getInput(gameboy, renderer); + while (numOperation < NUM_OP_60HZ) + numOperation += executeGameBoy(gameboy); + numOperation -= NUM_OP_60HZ; + renderScreen(gameboy, renderer); + + float deltaT = + (float)1000 / (59.7) - (float)(SDL_GetTicks() - timeStartFrame); + if (deltaT > 0) + SDL_Delay((unsigned int)deltaT); + } +}