From 7f6d3b80664e047e732a3f129044ed019ae784d1 Mon Sep 17 00:00:00 2001 From: Jorge Gorbe Moya Date: Wed, 2 Jul 2008 20:42:08 +0200 Subject: [PATCH] Core simulates instruction cycle cost. Optimizations. Now distortion.gb runs at ~66 FPS :D --- GBVideo.cc | 168 ++-- GBVideo.h | 4 +- gbcore.cc | 2091 ++++++++++++++++++++++++-------------------- gbcore.h | 4 + opcodes.h | 32 +- tests/test_core.cc | 37 +- 6 files changed, 1276 insertions(+), 1060 deletions(-) diff --git a/GBVideo.cc b/GBVideo.cc index af461fc..bf640cd 100644 --- a/GBVideo.cc +++ b/GBVideo.cc @@ -9,12 +9,14 @@ GBVideo::GBVideo(GameBoy *core): display(0), core(core), frames_rendered(0), - cycles_until_next_update(0), + t0(0), mode(2), display_mode(NORMAL) { SDL_Init(SDL_INIT_VIDEO); - display=SDL_SetVideoMode(320,288,32,SDL_HWSURFACE | SDL_DOUBLEBUF); + display=SDL_SetVideoMode(320,288,32,SDL_SWSURFACE | SDL_DOUBLEBUF); + + t0 = SDL_GetTicks(); colors[0] = SDL_MapRGB(display->format, 192,192,0); colors[1] = SDL_MapRGB(display->format, 139,139,21); @@ -88,7 +90,7 @@ void GBVideo::write_OAM (int addr, u8 value) #endif -void GBVideo::update() +u32 GBVideo::update() { //Mode 0 is present between 201-207 clks, 2 about 77-83 clks, and 3 //about 169-175 clks. A complete cycle through these states takes 456 @@ -106,103 +108,103 @@ void GBVideo::update() // mode 0 starts at 252, 708, 1164... // vblank starts at 65664 - if (cycles_until_next_update == 0) + int cycles_until_next_update=0; + int STAT = core->memory.high[GBMemory::I_STAT]; + int LYC = core->memory.high[GBMemory::I_LYC]; + int LY = core->memory.high[GBMemory::I_LY]; + + switch (mode) { - int STAT = core->memory.high[GBMemory::I_STAT]; - int LYC = core->memory.high[GBMemory::I_LYC]; - int LY = core->memory.high[GBMemory::I_LY]; + case 0: + // HBlank (preserve bits 2-6, mode = 0) + STAT = (STAT&0xFC); + if (check_bit(STAT, 3)) + { + logger.trace("Requesting IRQ_LCD_STAT -- HBLANK"); + core->irq(GameBoy::IRQ_LCD_STAT); + } + cycles_until_next_update = 204; + if (LY == 143) + mode = 1; + else + mode = 2; + break; + case 1: + if (LY == 144) + { + logger.trace("Requesting IRQ_VBLANK"); + core->irq(GameBoy::IRQ_VBLANK); - switch (mode) - { - case 0: - // HBlank (preserve bits 2-6, mode = 0) - STAT = (STAT&0xFC); - if (check_bit(STAT, 3)) + if (check_bit(STAT,4)) { - logger.trace("Requesting IRQ_LCD_STAT -- HBLANK"); + logger.trace("Requesting IRQ_LCD_STAT -- VBLANK"); core->irq(GameBoy::IRQ_LCD_STAT); } - cycles_until_next_update = 204; - if (LY == 143) - mode = 1; - else - mode = 2; - break; - case 1: - if (LY == 144) + SDL_Flip(display); + frames_rendered++; + u32 t1 = SDL_GetTicks(); + if (t1-t0 > 1000) { - logger.trace("Requesting IRQ_VBLANK"); - core->irq(GameBoy::IRQ_VBLANK); - - if (check_bit(STAT,4)) - { - logger.trace("Requesting IRQ_LCD_STAT -- VBLANK"); - core->irq(GameBoy::IRQ_LCD_STAT); - } - SDL_Flip(display); - frames_rendered++; - if (frames_rendered % 10 == 0) - { - char buf[50]; - sprintf(buf, "%d", frames_rendered); - SDL_WM_SetCaption(buf, 0); - } - - // preserve bits 3-6, set mode to 1 (VBlank) and coincidence to 0 - STAT = (STAT&0xF8) | 1; - } - cycles_until_next_update = 456; - if (LY == 153) - mode = 2; - else - mode = 1; - break; - case 2: { - if (LY == LYC) - { - STAT = set_bit(STAT, 2); // set coincidence flag - if (check_bit(STAT, 6)) - { - logger.trace("Requesting IRQ_LCD_STAT -- LY = LYC = ", LY, " EI=", int(core->memory.IE), - " IME =", int(core->IME)); - core->irq(GameBoy::IRQ_LCD_STAT); - } + char buf[50]; + sprintf(buf, "%f FPS", frames_rendered/(0.001f*(t1-t0))); + SDL_WM_SetCaption(buf, 0); + t0=t1; + frames_rendered=0; } - if (check_bit(STAT, 5)) + // preserve bits 3-6, set mode to 1 (VBlank) and coincidence to 0 + STAT = (STAT&0xF8) | 1; + } + cycles_until_next_update = 456; + if (LY == 153) + mode = 2; + else + mode = 1; + break; + case 2: { + if (LY == LYC) + { + STAT = set_bit(STAT, 2); // set coincidence flag + if (check_bit(STAT, 6)) { - logger.trace("Requesting IRQ_LCD_STAT -- Mode 2"); + logger.trace("Requesting IRQ_LCD_STAT -- LY = LYC = ", LY, " EI=", int(core->memory.IE), + " IME =", int(core->IME)); core->irq(GameBoy::IRQ_LCD_STAT); } + } - // preserve bits 2-6, set mode 2 - STAT = (STAT&0xFC) | 2; - cycles_until_next_update = 80; - mode = 3; - break; + if (check_bit(STAT, 5)) + { + logger.trace("Requesting IRQ_LCD_STAT -- Mode 2"); + core->irq(GameBoy::IRQ_LCD_STAT); } - case 3: - draw(); - // preserve bits 2-6, set mode 3 - STAT = (STAT&0xFC) | 3; - cycles_until_next_update = 172; - mode = 0; - break; - } - - if (mode == 1 || mode == 2) - { - LY = (LY+1)%154; - logger.trace(LY); - core->memory.high[GBMemory::I_LY] = LY; + + // preserve bits 2-6, set mode 2 + STAT = (STAT&0xFC) | 2; + cycles_until_next_update = 80; + mode = 3; + break; } + case 3: + draw(); + // preserve bits 2-6, set mode 3 + STAT = (STAT&0xFC) | 3; + cycles_until_next_update = 172; + mode = 0; + break; + } + + if (mode == 1 || mode == 2) + { + LY = (LY+1)%154; + logger.trace(LY); + core->memory.high[GBMemory::I_LY] = LY; + } - core->memory.high[GBMemory::I_STAT] = STAT; - } + core->memory.high[GBMemory::I_STAT] = STAT; - --cycles_until_next_update; - return; + return cycles_until_next_update; } void GBVideo::draw() diff --git a/GBVideo.h b/GBVideo.h index 31741eb..54cfbe6 100644 --- a/GBVideo.h +++ b/GBVideo.h @@ -16,8 +16,8 @@ class GBVideo u32 colors[4]; u32 frames_rendered; + u32 t0; - int cycles_until_next_update; int mode; public: @@ -47,7 +47,7 @@ class GBVideo // drawing control void draw(); - void update(); + u32 update(); void set_display_mode(DisplayMode mode) { display_mode = mode; } // event processing diff --git a/gbcore.cc b/gbcore.cc index 9be7705..d86d402 100644 --- a/gbcore.cc +++ b/gbcore.cc @@ -18,6 +18,8 @@ GameBoy::GameBoy(std::string rom_name, GameBoyType type): IME(1), HALT(0), cycle_count(0), + cycles_until_video_update(0), + cycles_until_next_instruction(0), breakpoints(), last_breakpoint_id(0) { @@ -33,6 +35,12 @@ GameBoy::GameBoy(std::string rom_name, GameBoyType type): void GameBoy::reset() { logger.info("GameBoy reset"); + IME = 1; + HALT = 0; + cycle_count = 0; + cycles_until_video_update = 0; + cycles_until_next_instruction = 0; + regs.PC = 0x0100; regs.AF = 0x01B0; regs.BC = 0x0013; @@ -40,6 +48,11 @@ void GameBoy::reset() regs.HL = 0x014D; regs.SP = 0xFFFE; + for (int i=0; i<0xFFFF; i++) + { + memory.write(i, 0); + } + memory.write(0xFF05, 0x00); // TIMA memory.write(0xFF06, 0x00); // TMA memory.write(0xFF07, 0x00); // TAC @@ -102,1083 +115,1235 @@ void GameBoy::disable_breakpoint(int id) GameBoy::run_status GameBoy::run_cycle() { - int opcode; - opcode = memory.read(regs.PC++); - - switch(opcode) - { - // LD n, nn - for_each_register(0x3E, 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, LD_reg_nn) + cycle_count += CYCLE_STEP; - // LD r1,r2 - for_each_register(0x7F, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, LD_A_reg) - for_each_register(0x47, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, LD_B_reg) - for_each_register(0x4F, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, LD_C_reg) - for_each_register(0x57, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, LD_D_reg) - for_each_register(0x5F, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, LD_E_reg) - for_each_register(0x67, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, LD_H_reg) - for_each_register(0x6F, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, LD_L_reg) - - // LD reg, (HL) - for_each_register(0x7E, 0x46, 0x4E, 0x56, 0x5E, 0x66, 0x6E, LD_reg__HL_) - - // LD (HL), reg - for_each_register(0x77, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, LD__HL__reg) - - case 0x36: // LD (HL), n - memory.write(regs.HL, memory.read(regs.PC++)); - break; - - // LD A, mem - case 0x0A: // LD A, (BC) - regs.A = memory.read(regs.BC); - break; - case 0x1A: // LD A, (DE) - regs.A = memory.read(regs.DE); - break; - case 0xFA: // LD A, (nn) - regs.A = memory.read(memory.read16(regs.PC)); - regs.PC+=2; - break; + if (cycles_until_next_instruction > 0) + { + if (cycles_until_video_update <= 0) + cycles_until_video_update = video.update(); - // LD mem, A - case 0x02: // LD (BC), A - memory.write(regs.BC, regs.A); - break; - case 0x12: // LD (DE), A - memory.write(regs.DE, regs.A); - break; - case 0xEA: // LD (nn), A - memory.write(memory.read16(regs.PC), regs.A); - regs.PC+=2; - break; - - // LD A, (C) - case 0xF2: - regs.A = memory.read(0xFF00 + regs.C); - break; - // LD (C), A - case 0xE2: - memory.write(0xFF00 + regs.C, regs.A); - break; - - // LD A, (HLD); LD A, (HL-); LDD A,(HL); - case 0x3A: - regs.A = memory.read(regs.HL); - --regs.HL; - break; - // LD (HLD), A; LD (HL-), A; LDD (HL), A; - case 0x32: - memory.write(regs.HL, regs.A); - --regs.HL; - break; - // LD A, (HLI); LD A, (HL+); LDI A, (HL); - case 0x2A: - regs.A = memory.read(regs.HL); - ++regs.HL; - break; - // LD (HLI), A; LD (HL+), A; LDI (HL), A; - case 0x22: - memory.write(regs.HL, regs.A); - ++regs.HL; - break; - - // LDH (n), A - case 0xE0: { - memory.high[memory.read(regs.PC++)] = regs.A; - break; - } - // LDH A, (n) - case 0xF0: - regs.A = memory.high[memory.read(regs.PC++)]; - break; - - // LD n, nn - case 0x01: // LD BC, nn - regs.BC = memory.read16(regs.PC); - regs.PC +=2; - break; - case 0x11: // LD DE, nn - regs.DE = memory.read16(regs.PC); - regs.PC +=2; - break; - case 0x21: // LD HL, nn - regs.HL = memory.read16(regs.PC); - regs.PC +=2; - break; - case 0x31: // LD SP, nn - regs.SP = memory.read16(regs.PC); - regs.PC +=2; - break; + cycles_until_video_update -= CYCLE_STEP; + cycles_until_next_instruction -= CYCLE_STEP; - // LD SP, HL - case 0xF9: - regs.SP = regs.HL; - break; - - // LD HL, SP+n - // LDHL SP, n - case 0xF8: { - s8 offset = memory.read(regs.PC++); - int res = regs.SP + offset; - - // TODO: Verificar si los flags van asi - set_flag_if (res > 0xFFFF, CARRY_FLAG); - - // TODO: hacer lo apropiado con el half-carry flag - reset_flag(ADD_SUB_FLAG); - reset_flag(ZERO_FLAG); + if (cycles_until_next_instruction > 0) return WAIT; + else return NORMAL; + } - regs.HL = static_cast(res & 0xFFFF); - break; + // Check for interrupts before opcode fetching + u8 IE=memory.high[GBMemory::I_IE]; + //logger.trace("IME=", int(IME), " IE=", int(IE)); + if (IME && IE) + { + u8 IF = memory.high[GBMemory::I_IF]; + //logger.trace("Dispatching interrupts: IE=", int(IE), " IF=", int(IF)); + if (IF) + { + if ((IF & IRQ_VBLANK) && (IE & IRQ_VBLANK)) + { + IME = 0; + IF &= (~IRQ_VBLANK); + do_call(0x40); + logger.trace("VBLANK IRQ"); } - - // LD (nn), SP - case 0x08: { - int addr = memory.read16(regs.PC); - regs.PC += 2; - memory.write(addr, regs.SP); - break; + else if ((IF & IRQ_LCD_STAT) && (IE & IRQ_LCD_STAT)) + { + IME = 0; + IF &= (~IRQ_LCD_STAT); + do_call(0x48); + logger.trace("LCD STAT IRQ"); + } + else if ((IF & IRQ_TIMER) && (IE & IRQ_TIMER)) + { + IME = 0; + IF &= (~IRQ_TIMER); + do_call(0x50); + logger.trace("TIMER IRQ"); + } + else if ((IF & IRQ_SERIAL) && (IE & IRQ_SERIAL)) + { + IME = 0; + IF &= (~IRQ_SERIAL); + do_call(0x58); + logger.trace("SERIAL IRQ"); + } + else if ((IF & IRQ_JOYPAD) && (IE & IRQ_JOYPAD)) + { + IME = 0; + IF &= (~IRQ_JOYPAD); + do_call(0x60); + logger.trace("JOYPAD IRQ"); } + } + memory.high[GBMemory::I_IF] = IF; + } - // PUSH nn - PUSH(0xF5, A, flags) - PUSH(0xC5, B, C) - PUSH(0xD5, D, E) - PUSH(0xE5, H, L) + /* + for(BreakpointMap::iterator i=breakpoints.begin(); + i != breakpoints.end(); + i++) + { + if (i->second.addr == regs.PC && i->second.enabled) + return BREAKPOINT; + } + */ + + int opcode; + opcode = memory.read(regs.PC++); - // POP nn - POP(0xF1, A, flags) - POP(0xC1, B, C) - POP(0xD1, D, E) - POP(0xE1, H, L) + switch(opcode & 0x80) + { + case 0x00: + switch(opcode) + { + // LD n, nn + for_each_register(0x3E, 0x06, 0x0E, 0x16, 0x1E, 0x26, 0x2E, LD_reg_nn) + + // LD r1,r2 + for_each_register(0x7F, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, LD_A_reg) + for_each_register(0x47, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, LD_B_reg) + for_each_register(0x4F, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, LD_C_reg) + for_each_register(0x57, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, LD_D_reg) + for_each_register(0x5F, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, LD_E_reg) + for_each_register(0x67, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, LD_H_reg) + for_each_register(0x6F, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, LD_L_reg) + + // LD reg, (HL) + for_each_register(0x7E, 0x46, 0x4E, 0x56, 0x5E, 0x66, 0x6E, LD_reg__HL_) - // 8-bit ALU - // ADD A,reg - for_each_register(0x87, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, ADD_A_reg) - - case 0x86: {// ADD A, (HL) - int res = regs.A + memory.read(regs.HL); - int half_res = (regs.A & 0x0F) + (memory.read(regs.HL) & 0x0F); - regs.A = static_cast(res); + // LD (HL), reg + for_each_register(0x77, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, LD__HL__reg) - reset_flag(ADD_SUB_FLAG); - set_flag_if (res > 0xFF, CARRY_FLAG); - set_flag_if (regs.A == 0, ZERO_FLAG); - set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); - break; - } - case 0xC6: {//ADD A, # - int inm = memory.read(regs.PC++); - int res = regs.A + inm; - int half_res = (regs.A & 0x0F) + (inm & 0x0F); - regs.A = static_cast(res); + case 0x36: // LD (HL), n + memory.write(regs.HL, memory.read(regs.PC++)); + cycles_until_next_instruction = 12; + break; - reset_flag(ADD_SUB_FLAG); - set_flag_if (res > 0xFF, CARRY_FLAG); - set_flag_if (regs.A == 0, ZERO_FLAG); - set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); - break; - } - - // ADC A, n - for_each_register(0x8F, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, ADC_A_reg) - - case 0x8E: {// ADC A, (HL) - int carry = (check_flag(CARRY_FLAG)? 1 : 0); - int res = regs.A + memory.read(regs.HL) + carry; - int half_res = (regs.A & 0x0F) + (memory.read(regs.HL) & 0x0F) + carry; - regs.A = static_cast(res); + // LD A, mem + case 0x0A: // LD A, (BC) + regs.A = memory.read(regs.BC); + cycles_until_next_instruction = 8; + break; + case 0x1A: // LD A, (DE) + regs.A = memory.read(regs.DE); + cycles_until_next_instruction = 8; + break; - reset_flag(ADD_SUB_FLAG); - set_flag_if (res > 0xFF, CARRY_FLAG); - set_flag_if (regs.A == 0, ZERO_FLAG); - set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); - break; - } - case 0xCE: {//ADC A, # - int carry = (check_flag(CARRY_FLAG)? 1 : 0); - int inm = memory.read(regs.PC++); - int res = regs.A + inm + carry; - int half_res = (regs.A & 0x0F) + (inm & 0x0F) + carry; - regs.A = static_cast(res); + // LD mem, A + case 0x02: // LD (BC), A + memory.write(regs.BC, regs.A); + cycles_until_next_instruction = 8; + break; + case 0x12: // LD (DE), A + memory.write(regs.DE, regs.A); + cycles_until_next_instruction = 8; + break; + + // LD A, (HLD); LD A, (HL-); LDD A,(HL); + case 0x3A: + regs.A = memory.read(regs.HL); + --regs.HL; + cycles_until_next_instruction = 8; + break; + // LD (HLD), A; LD (HL-), A; LDD (HL), A; + case 0x32: + memory.write(regs.HL, regs.A); + --regs.HL; + cycles_until_next_instruction = 8; + break; + // LD A, (HLI); LD A, (HL+); LDI A, (HL); + case 0x2A: + regs.A = memory.read(regs.HL); + ++regs.HL; + cycles_until_next_instruction = 8; + break; + // LD (HLI), A; LD (HL+), A; LDI (HL), A; + case 0x22: + memory.write(regs.HL, regs.A); + ++regs.HL; + cycles_until_next_instruction = 8; + break; + + // LD n, nn + case 0x01: // LD BC, nn + regs.BC = memory.read16(regs.PC); + regs.PC +=2; + cycles_until_next_instruction = 12; + break; + case 0x11: // LD DE, nn + regs.DE = memory.read16(regs.PC); + regs.PC +=2; + cycles_until_next_instruction = 12; + break; + case 0x21: // LD HL, nn + regs.HL = memory.read16(regs.PC); + regs.PC +=2; + cycles_until_next_instruction = 12; + break; + case 0x31: // LD SP, nn + regs.SP = memory.read16(regs.PC); + regs.PC +=2; + cycles_until_next_instruction = 12; + break; - reset_flag(ADD_SUB_FLAG); - set_flag_if (res > 0xFF, CARRY_FLAG); - set_flag_if (regs.A == 0, ZERO_FLAG); - set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); - break; - } - // SUB n - for_each_register(0x97, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, SUB_reg) + // LD (nn), SP + case 0x08: { + int addr = memory.read16(regs.PC); + regs.PC += 2; + memory.write(addr, regs.SP); + cycles_until_next_instruction = 20; + break; + } - case 0x96: {//SUB (HL) - int res = regs.A - memory.read(regs.HL); - int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F); - regs.A = static_cast(res); + // INC n + for_each_register(0x3C, 0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, INC_reg) + + case 0x34: {//INC (HL) + int half_res = (memory.read(regs.HL) & 0x0F) + 1; + memory.write(regs.HL, memory.read(regs.HL) - 1); + reset_flag(ADD_SUB_FLAG); + set_flag_if (memory.read(regs.HL) == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + cycles_until_next_instruction = 12; + break; + } + + // DEC n + for_each_register(0x3D, 0x05, 0x0D, 0x15, 0x1D, 0x25, 0x2D, DEC_reg) + + case 0x35: {//DEC (HL) + int half_res = (memory.read(regs.HL) & 0x0F) - 1; + memory.write(regs.HL, memory.read(regs.HL) - 1); + set_flag(ADD_SUB_FLAG); + set_flag_if (memory.read(regs.HL) == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 12; + break; + } - set_flag(ADD_SUB_FLAG); - set_flag_if (res < 0, CARRY_FLAG); - set_flag_if (res == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; - } - - case 0xD6: {//SUB # - int inm = memory.read(regs.PC++); - int res = regs.A - inm; - int half_res = (regs.A & 0x0F) - (inm & 0x0F); - regs.A = static_cast(res); - - set_flag(ADD_SUB_FLAG); - set_flag_if (res < 0, CARRY_FLAG); - set_flag_if (res == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; - } + // 16-bit ALU + // ADD HL, n + for_each_register16(0x09, 0x19, 0x29, 0x39, ADD_HL_reg16) - // SBC n - for_each_register(0x9F, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, SBC_reg) - - case 0x9E: {//SBC (HL) - int carry = (check_flag(CARRY_FLAG)? 1 : 0); - int res = regs.A - memory.read(regs.HL) - carry; - int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F) - carry; - regs.A = static_cast(res); - - set_flag(ADD_SUB_FLAG); - set_flag_if (res < 0, CARRY_FLAG); - set_flag_if (res == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; - } + // INC nn + for_each_register16(0x03, 0x13, 0x23, 0x33, INC_reg16) - // There is no SBC inm + // DEC nn + for_each_register16(0x0B, 0x1B, 0x2B, 0x3B, DEC_reg16) - // AND n - for_each_register(0xA7, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, AND_reg) - - case 0xA6: //AND (HL) - regs.A &= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; - case 0xE6: //AND inm - regs.A &= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; + // DAA http://www.worldofspectrum.org/faq/reference/z80reference.htm#DAA + case 0x27: { + u8 corr_factor = 0; + if (regs.A > 0x99 || check_flag(CARRY_FLAG)) { + corr_factor = 0x60; + set_flag(CARRY_FLAG); + } else { + reset_flag(CARRY_FLAG); + } - // OR n - for_each_register(0xB7, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, OR_reg) - - case 0xB6: //OR (HL) - regs.A |= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; + if (regs.A & 0x0F > 9 || check_flag(HALF_CARRY_FLAG)) { + corr_factor |= 0x06; + } - case 0xF6: //OR inm - regs.A |= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; + if (!check_flag(ADD_SUB_FLAG)) { + regs.A += corr_factor; + } else { + regs.A -= corr_factor; + } - // XOR n - for_each_register(0xAF, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, XOR_reg) - - case 0xAE: //XOR (HL) - regs.A ^= memory.read(regs.HL); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; + set_flag_if(regs.A==0, ZERO_FLAG); + reset_flag(HALF_CARRY_FLAG); // GBCPUman.pdf contradicts previous reference :P + cycles_until_next_instruction = 4; + break; + } - case 0xEE: //XOR inm - regs.A ^= memory.read(regs.PC++); - if (regs.A == 0) set_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(CARRY_FLAG); - break; - - // CP n - for_each_register(0xBF, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, CP_reg) + // CPL + case 0x2F: + regs.A = ~regs.A; + set_flag(HALF_CARRY_FLAG); + set_flag(ADD_SUB_FLAG); + cycles_until_next_instruction = 4; + break; + + // CCF + case 0x3F: + if (check_flag(CARRY_FLAG)) + reset_flag(CARRY_FLAG); + else + set_flag(CARRY_FLAG); - case 0xBE: {//CP (HL) - int res = regs.A - memory.read(regs.HL); - int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F); + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + cycles_until_next_instruction = 4; + break; - set_flag(ADD_SUB_FLAG); - set_flag_if (res < 0, CARRY_FLAG); - set_flag_if (res == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; + // SCF + case 0x37: + set_flag(CARRY_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + cycles_until_next_instruction = 4; + break; + + // NOP + case 0x00: + cycles_until_next_instruction = 4; + break; + + // HALT + case 0x76: + HALT = true; + cycles_until_next_instruction = 4; + break; + + // STOP + case 0x10: { + int sub_opcode = memory.read(regs.PC++); + if (sub_opcode == 0x00) { + HALT = true; + } else { + logger.critical("Unknown sub-opcode after 0x10"); + } + cycles_until_next_instruction = 4; + break; } - - case 0xFE: {//CP # - int inm = memory.read(regs.PC++); - int res = regs.A - inm; - int half_res = (regs.A & 0x0F) - (inm & 0x0F); - - set_flag(ADD_SUB_FLAG); - set_flag_if (res < 0, CARRY_FLAG); - set_flag_if (res == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; + + // Rotates and shifts + // RLCA + case 0x07: { + u8 bit7 = regs.A >>7; + regs.A = (regs.A << 1) | bit7; + set_flag_if(regs.A == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + // TODO: Check which of GBCPUman.pdf or + // worldofspectrum z80 reference is correct + // + //set_flag_if(bit7, CARRY_FLAG); + cycles_until_next_instruction = 4; + break; } - // INC n - for_each_register(0x3C, 0x04, 0x0C, 0x14, 0x1C, 0x24, 0x2C, INC_reg) - - case 0x34: {//INC (HL) - int half_res = (memory.read(regs.HL) & 0x0F) + 1; - memory.write(regs.HL, memory.read(regs.HL) - 1); - reset_flag(ADD_SUB_FLAG); - set_flag_if (memory.read(regs.HL) == 0, ZERO_FLAG); - set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); - break; - } + // RLA (through carry) + case 0x17: { + u8 bit7 = regs.A >> 7; + regs.A = (regs.A << 1) | check_flag(CARRY_FLAG); + set_flag_if(bit7, CARRY_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 4; + break; + } + + // RRCA + case 0x0F: { + u8 bit0 = regs.A & 1; + regs.A = (regs.A >> 1) | (bit0 << 7); + set_flag_if(regs.A == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + // TODO: Check which of GBCPUman.pdf or + // worldofspectrum z80 reference is correct + // + //set_flag_if(bit0, CARRY_FLAG); + cycles_until_next_instruction = 4; + break; + } - // DEC n - for_each_register(0x3D, 0x05, 0x0D, 0x15, 0x1D, 0x25, 0x2D, DEC_reg) - - case 0x35: {//DEC (HL) - int half_res = (memory.read(regs.HL) & 0x0F) - 1; - memory.write(regs.HL, memory.read(regs.HL) - 1); - set_flag(ADD_SUB_FLAG); - set_flag_if (memory.read(regs.HL) == 0, ZERO_FLAG); - set_flag_if (half_res < 0, HALF_CARRY_FLAG); - break; + // RRA (through carry) + case 0x1F: { + u8 bit0 = regs.A & 1; + regs.A = (regs.A >> 1) | (check_flag(CARRY_FLAG) << 7); + set_flag_if(bit0, CARRY_FLAG); + set_flag_if(regs.A == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 4; + break; } - // 16-bit ALU - // ADD HL, n - for_each_register16(0x09, 0x19, 0x29, 0x39, ADD_HL_reg16) + + // JR n + case 0x18: + // -1 because PC is now pointing past the opcode + regs.PC += static_cast(memory.read(regs.PC++)); + cycles_until_next_instruction = 8; + break; + + // JR cc, n + case 0x20: { // JR NZ, n + s8 offset = static_cast(memory.read(regs.PC++)); + if (!check_flag(ZERO_FLAG)) + regs.PC += offset; + cycles_until_next_instruction = 8; + break; + } - // ADD SP, # - case 0xE8: { - // FIXME: No se que hacer con el half carry, en 4 o en 11? - int n = static_cast(memory.read(regs.PC++)); - int res = regs.SP + n; - regs.SP = static_cast(res); - reset_flag(ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag_if(res > 0xFFFF, CARRY_FLAG); - break; - } + case 0x28: { // JR Z, n + s8 offset = static_cast(memory.read(regs.PC++)); + if (check_flag(ZERO_FLAG)) + regs.PC += offset; + cycles_until_next_instruction = 8; + break; + } - // INC nn - for_each_register16(0x03, 0x13, 0x23, 0x33, INC_reg16) + case 0x30: { // JR NC, n + s8 offset = static_cast(memory.read(regs.PC++)); + if (!check_flag(CARRY_FLAG)) + regs.PC += offset; + cycles_until_next_instruction = 8; + break; + } - // DEC nn - for_each_register16(0x0B, 0x1B, 0x2B, 0x3B, DEC_reg16) + case 0x38: { // JR C, n + s8 offset = static_cast(memory.read(regs.PC++)); + if (check_flag(CARRY_FLAG)) + regs.PC += offset; + cycles_until_next_instruction = 8; + break; + } - // Miscellaneous instructions - case 0xCB: { - int sub_opcode = memory.read(regs.PC++); - switch(sub_opcode) - { - // SWAP n - for_each_register(0x37, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, SWAP_reg) - // SWAP (HL) - case 0x36: { - u8 tmp = memory.read(regs.HL); - tmp = ((tmp & 0x0F) << 4) | ((tmp & 0xF0)>>4); - memory.write(regs.HL, tmp); - set_flag_if(tmp==0, ZERO_FLAG); - reset_flag(CARRY_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(ADD_SUB_FLAG); - break; - } + default: + std::ostringstream errmsg; + errmsg << "Unknown opcode 0x"; + errmsg << std::hex << std::setw(2) << std::setfill('0') << opcode; + errmsg << " at 0x" << std::hex << std::setw(4) << regs.PC-1; + errmsg << " (cycle count = " << std::dec << cycle_count << ")"; + logger.critical(errmsg.str()); + break; + + } // end switch + break; + case 0x80: + switch(opcode) + { + case 0xFA: // LD A, (nn) + regs.A = memory.read(memory.read16(regs.PC)); + regs.PC+=2; + cycles_until_next_instruction = 16; + break; + case 0xEA: // LD (nn), A + memory.write(memory.read16(regs.PC), regs.A); + regs.PC+=2; + cycles_until_next_instruction = 16; + break; + + // LD A, (C) + case 0xF2: + regs.A = memory.read(0xFF00 + regs.C); + cycles_until_next_instruction = 8; + break; + // LD (C), A + case 0xE2: + memory.write(0xFF00 + regs.C, regs.A); + cycles_until_next_instruction = 8; + break; + // LDH (n), A + case 0xE0: { + memory.high[memory.read(regs.PC++)] = regs.A; + cycles_until_next_instruction = 12; + break; + } + // LDH A, (n) + case 0xF0: + regs.A = memory.high[memory.read(regs.PC++)]; + cycles_until_next_instruction = 12; + break; + + // LD SP, HL + case 0xF9: + regs.SP = regs.HL; + cycles_until_next_instruction = 8; + break; + + // LD HL, SP+n + // LDHL SP, n + case 0xF8: { + s8 offset = memory.read(regs.PC++); + int res = regs.SP + offset; + + // TODO: Verificar si los flags van asi + set_flag_if (res > 0xFFFF, CARRY_FLAG); - // RLC n - for_each_register(0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, RLC_reg) + // TODO: hacer lo apropiado con el half-carry flag + reset_flag(ADD_SUB_FLAG); + reset_flag(ZERO_FLAG); - // RLC (HL) - case 0x06: { - u8 value = memory.read(regs.HL); - u8 bit7 = value >> 7; - value = (value << 1) | bit7; - memory.write(regs.HL, value); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; + regs.HL = static_cast(res & 0xFFFF); + cycles_until_next_instruction = 12; + break; } - // RL n (through carry) - for_each_register(0x17, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, RL_reg) - - // RL (HL) (through carry) - case 0x16: { - u8 value = memory.read(regs.HL); - u8 bit7 = value >> 7; - value = (value << 1) | check_flag(CARRY_FLAG); - memory.write(regs.HL, value); - set_flag_if(bit7, CARRY_FLAG); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; + // PUSH nn + PUSH(0xF5, A, flags) + PUSH(0xC5, B, C) + PUSH(0xD5, D, E) + PUSH(0xE5, H, L) + + // POP nn + POP(0xF1, A, flags) + POP(0xC1, B, C) + POP(0xD1, D, E) + POP(0xE1, H, L) + + // 8-bit ALU + // ADD A,reg + for_each_register(0x87, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, ADD_A_reg) + + case 0x86: {// ADD A, (HL) + int res = regs.A + memory.read(regs.HL); + int half_res = (regs.A & 0x0F) + (memory.read(regs.HL) & 0x0F); + regs.A = static_cast(res); + + reset_flag(ADD_SUB_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; } - - // RRC n - for_each_register(0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, RRC_reg) - - // RRC (HL) - case 0x0E: { - u8 value = memory.read(regs.HL); - u8 bit0 = value & 1; - value = (value >> 1) | (bit0 << 7); - memory.write(regs.HL, value); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; + case 0xC6: {//ADD A, # + int inm = memory.read(regs.PC++); + int res = regs.A + inm; + int half_res = (regs.A & 0x0F) + (inm & 0x0F); + regs.A = static_cast(res); + + reset_flag(ADD_SUB_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; } - // RR n (through carry) - for_each_register(0x1F, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, RR_reg) + // ADC A, n + for_each_register(0x8F, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, ADC_A_reg) - // RR (HL) (through carry) - case 0x1E: { - u8 value = memory.read(regs.HL); - u8 bit0 = value & 1; - value = (value >> 1) | (check_flag(CARRY_FLAG) << 7); - memory.write(regs.HL, value); - set_flag_if(bit0, CARRY_FLAG); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; - } + case 0x8E: {// ADC A, (HL) + int carry = (check_flag(CARRY_FLAG)? 1 : 0); + int res = regs.A + memory.read(regs.HL) + carry; + int half_res = (regs.A & 0x0F) + (memory.read(regs.HL) & 0x0F) + carry; + regs.A = static_cast(res); - // SLA n - for_each_register(0x27, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, SLA_reg) - - // SLA (HL) - case 0x26: { - u8 value = memory.read(regs.HL); - bool carry = (value & 0x80) != 0; - value <<= 1; - memory.write(regs.HL, value); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - set_flag_if(carry, CARRY_FLAG); - break; + reset_flag(ADD_SUB_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; } + case 0xCE: {//ADC A, # + int carry = (check_flag(CARRY_FLAG)? 1 : 0); + int inm = memory.read(regs.PC++); + int res = regs.A + inm + carry; + int half_res = (regs.A & 0x0F) + (inm & 0x0F) + carry; + regs.A = static_cast(res); - // SRA n - for_each_register(0x2F, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, SRA_reg) - - // SRA (HL) - case 0x2E: { - u8 value = memory.read(regs.HL); - bool carry = (value & 0x01) != 0; - u8 MSB = value & 0x80; - value = (value >> 1) | MSB; - memory.write(regs.HL, value); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - set_flag_if(carry, CARRY_FLAG); - break; + reset_flag(ADD_SUB_FLAG); + set_flag_if (res > 0xFF, CARRY_FLAG); + set_flag_if (regs.A == 0, ZERO_FLAG); + set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; } - // SRL n - for_each_register(0x3F, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, SRL_reg) + // SUB n + for_each_register(0x97, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, SUB_reg) - // SRL (HL) - case 0x3E: { - u8 value = memory.read(regs.HL); - bool carry = (value & 0x01) != 0; - value >>= 1; - memory.write(regs.HL, value); - set_flag_if(value == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - set_flag_if(carry, CARRY_FLAG); - break; - } + case 0x96: {//SUB (HL) + int res = regs.A - memory.read(regs.HL); + int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F); + regs.A = static_cast(res); - default: { - int bit_op = sub_opcode >> 6; - int reg = sub_opcode & 7; - int b = (sub_opcode >> 3) & 7; - bool res; - switch (bit_op) - { - case 1: // BIT - switch(reg) - { - case 0: - res = check_bit(regs.B, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 1: - res = check_bit(regs.C, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 2: - res = check_bit(regs.D, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 3: - res = check_bit(regs.E, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 4: - res = check_bit(regs.H, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 5: - res = check_bit(regs.L, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 6: - res = check_bit(memory.read(regs.HL), b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - case 7: - res = check_bit(regs.A, b); - set_flag_if(res == false, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - set_flag(HALF_CARRY_FLAG); - break; - } - break; - - case 2: // RES - switch(reg) - { - case 0: - regs.B = reset_bit(regs.B, b); - break; - case 1: - regs.C = reset_bit(regs.C, b); - break; - case 2: - regs.D = reset_bit(regs.D, b); - break; - case 3: - regs.E = reset_bit(regs.E, b); - break; - case 4: - regs.H = reset_bit(regs.H, b); - break; - case 5: - regs.L = reset_bit(regs.L, b); - break; - case 6: - memory.write(regs.HL, reset_bit(memory.read(regs.HL), b)); - break; - case 7: - regs.A = reset_bit(regs.A, b); - break; - } - break; - - case 3: // SET - switch(reg) - { - case 0: - regs.B = set_bit(regs.B, b); - break; - case 1: - regs.C = set_bit(regs.C, b); - break; - case 2: - regs.D = set_bit(regs.D, b); - break; - case 3: - regs.E = set_bit(regs.E, b); - break; - case 4: - regs.H = set_bit(regs.H, b); - break; - case 5: - regs.L = set_bit(regs.L, b); - break; - case 6: - memory.write(regs.HL, set_bit(memory.read(regs.HL), b)); - break; - case 7: - regs.A = set_bit(regs.A, b); - break; - } - break; - - default: - logger.critical("Unknown sub-opcode after 0xCB"); - break; - } + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + } + + case 0xD6: {//SUB # + int inm = memory.read(regs.PC++); + int res = regs.A - inm; + int half_res = (regs.A & 0x0F) - (inm & 0x0F); + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; } - - } - break; - } - - // DAA http://www.worldofspectrum.org/faq/reference/z80reference.htm#DAA - case 0x27: { - u8 corr_factor = 0; - if (regs.A > 0x99 || check_flag(CARRY_FLAG)) { - corr_factor = 0x60; - set_flag(CARRY_FLAG); - } else { - reset_flag(CARRY_FLAG); - } - - if (regs.A & 0x0F > 9 || check_flag(HALF_CARRY_FLAG)) { - corr_factor |= 0x06; - } - if (!check_flag(ADD_SUB_FLAG)) { - regs.A += corr_factor; - } else { - regs.A -= corr_factor; - } + // SBC n + for_each_register(0x9F, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, SBC_reg) + + case 0x9E: {//SBC (HL) + int carry = (check_flag(CARRY_FLAG)? 1 : 0); + int res = regs.A - memory.read(regs.HL) - carry; + int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F) - carry; + regs.A = static_cast(res); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + } - set_flag_if(regs.A==0, ZERO_FLAG); - reset_flag(HALF_CARRY_FLAG); // GBCPUman.pdf contradicts previous reference :P - break; - } + // There is no SBC inm - // CPL - case 0x2F: - regs.A = ~regs.A; - set_flag(HALF_CARRY_FLAG); - set_flag(ADD_SUB_FLAG); - break; + // AND n + for_each_register(0xA7, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, AND_reg) - // CCF - case 0x3F: - if (check_flag(CARRY_FLAG)) + case 0xA6: //AND (HL) + regs.A &= memory.read(regs.HL); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); reset_flag(CARRY_FLAG); - else - set_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + + case 0xE6: //AND inm + regs.A &= memory.read(regs.PC++); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; - reset_flag(HALF_CARRY_FLAG); - reset_flag(ADD_SUB_FLAG); - break; + // OR n + for_each_register(0xB7, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, OR_reg) - // SCF - case 0x37: - set_flag(CARRY_FLAG); - reset_flag(HALF_CARRY_FLAG); - reset_flag(ADD_SUB_FLAG); - break; + case 0xB6: //OR (HL) + regs.A |= memory.read(regs.HL); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + + case 0xF6: //OR inm + regs.A |= memory.read(regs.PC++); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; - // NOP - case 0x00: - break; + // XOR n + for_each_register(0xAF, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, XOR_reg) - // HALT - case 0x76: - HALT = true; - break; + case 0xAE: //XOR (HL) + regs.A ^= memory.read(regs.HL); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + + case 0xEE: //XOR inm + regs.A ^= memory.read(regs.PC++); + if (regs.A == 0) set_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + + // CP n + for_each_register(0xBF, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, CP_reg) + + case 0xBE: {//CP (HL) + int res = regs.A - memory.read(regs.HL); + int half_res = (regs.A & 0x0F) - (memory.read(regs.HL) & 0x0F); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + } + + case 0xFE: {//CP # + int inm = memory.read(regs.PC++); + int res = regs.A - inm; + int half_res = (regs.A & 0x0F) - (inm & 0x0F); + + set_flag(ADD_SUB_FLAG); + set_flag_if (res < 0, CARRY_FLAG); + set_flag_if (res == 0, ZERO_FLAG); + set_flag_if (half_res < 0, HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + } - // STOP - case 0x10: { - int sub_opcode = memory.read(regs.PC++); - if (sub_opcode == 0x00) { - HALT = true; - } else { - logger.critical("Unknown sub-opcode after 0x10"); + // ADD SP, # + case 0xE8: { + // FIXME: No se que hacer con el half carry, en 4 o en 11? + int n = static_cast(memory.read(regs.PC++)); + int res = regs.SP + n; + regs.SP = static_cast(res); + reset_flag(ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag_if(res > 0xFFFF, CARRY_FLAG); + cycles_until_next_instruction = 16; + break; } - break; - } - - // DI - case 0xF3: - IME = 0; - break; - - // EI - case 0xFB: - IME = 1; - break; - - // Rotates and shifts - // RLCA - case 0x07: { - u8 bit7 = regs.A >>7; - regs.A = (regs.A << 1) | bit7; - set_flag_if(regs.A == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - // TODO: Check which of GBCPUman.pdf or - // worldofspectrum z80 reference is correct - // - //set_flag_if(bit7, CARRY_FLAG); - break; - } - // RLA (through carry) - case 0x17: { - u8 bit7 = regs.A >> 7; - regs.A = (regs.A << 1) | check_flag(CARRY_FLAG); - set_flag_if(bit7, CARRY_FLAG); - set_flag_if(regs.A == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; - } - - // RRCA - case 0x0F: { - u8 bit0 = regs.A & 1; - regs.A = (regs.A >> 1) | (bit0 << 7); - set_flag_if(regs.A == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - // TODO: Check which of GBCPUman.pdf or - // worldofspectrum z80 reference is correct - // - //set_flag_if(bit0, CARRY_FLAG); - break; - } - - // RRA (through carry) - case 0x1F: { - u8 bit0 = regs.A & 1; - regs.A = (regs.A >> 1) | (check_flag(CARRY_FLAG) << 7); - set_flag_if(bit0, CARRY_FLAG); - set_flag_if(regs.A == 0, ZERO_FLAG); - reset_flag(ADD_SUB_FLAG); - reset_flag(HALF_CARRY_FLAG); - break; - } - - - // Jumps - // JP nn - case 0xC3: - regs.PC = memory.read16(regs.PC); - break; - - // JP cc, nn - case 0xC2: { // JP NZ, nn - u16 dst = memory.read16(regs.PC); - if (!check_flag(ZERO_FLAG)) - regs.PC = dst; - else - regs.PC += 2; // if !cc, skip 2 dst bytes - break; - } + // Miscellaneous instructions + case 0xCB: { + int sub_opcode = memory.read(regs.PC++); + switch(sub_opcode) + { + // SWAP n + for_each_register(0x37, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, SWAP_reg) + + // SWAP (HL) + case 0x36: { + u8 tmp = memory.read(regs.HL); + tmp = ((tmp & 0x0F) << 4) | ((tmp & 0xF0)>>4); + memory.write(regs.HL, tmp); + + set_flag_if(tmp==0, ZERO_FLAG); + reset_flag(CARRY_FLAG); + reset_flag(HALF_CARRY_FLAG); + reset_flag(ADD_SUB_FLAG); + cycles_until_next_instruction = 16; + break; + } - case 0xCA: { // JP Z, nn - u16 dst = memory.read16(regs.PC); - if (check_flag(ZERO_FLAG)) - regs.PC = dst; - else - regs.PC += 2; // if !cc, skip 2 dst bytes - break; - } + // RLC n + for_each_register(0x07, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, RLC_reg) + + // RLC (HL) + case 0x06: { + u8 value = memory.read(regs.HL); + u8 bit7 = value >> 7; + value = (value << 1) | bit7; + memory.write(regs.HL, value); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } - case 0xD2: { // JP NC, nn - u16 dst = memory.read16(regs.PC); - if (!check_flag(CARRY_FLAG)) - regs.PC = dst; - else - regs.PC += 2; // if !cc, skip 2 dst bytes - break; - } + // RL n (through carry) + for_each_register(0x17, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, RL_reg) + + // RL (HL) (through carry) + case 0x16: { + u8 value = memory.read(regs.HL); + u8 bit7 = value >> 7; + value = (value << 1) | check_flag(CARRY_FLAG); + memory.write(regs.HL, value); + set_flag_if(bit7, CARRY_FLAG); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } - case 0xDA: { // JP C, nn - u16 dst = memory.read16(regs.PC); - if (check_flag(CARRY_FLAG)) - regs.PC = dst; - else - regs.PC += 2; // if !cc, skip 2 dst bytes - break; - } + // RRC n + for_each_register(0x0F, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, RRC_reg) + + // RRC (HL) + case 0x0E: { + u8 value = memory.read(regs.HL); + u8 bit0 = value & 1; + value = (value >> 1) | (bit0 << 7); + memory.write(regs.HL, value); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } - // JP (HL) - case 0xE9: - regs.PC = regs.HL; - break; + // RR n (through carry) + for_each_register(0x1F, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, RR_reg) + + // RR (HL) (through carry) + case 0x1E: { + u8 value = memory.read(regs.HL); + u8 bit0 = value & 1; + value = (value >> 1) | (check_flag(CARRY_FLAG) << 7); + memory.write(regs.HL, value); + set_flag_if(bit0, CARRY_FLAG); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } + + // SLA n + for_each_register(0x27, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, SLA_reg) + + // SLA (HL) + case 0x26: { + u8 value = memory.read(regs.HL); + bool carry = (value & 0x80) != 0; + value <<= 1; + memory.write(regs.HL, value); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + set_flag_if(carry, CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } + + // SRA n + for_each_register(0x2F, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, SRA_reg) + + // SRA (HL) + case 0x2E: { + u8 value = memory.read(regs.HL); + bool carry = (value & 0x01) != 0; + u8 MSB = value & 0x80; + value = (value >> 1) | MSB; + memory.write(regs.HL, value); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + set_flag_if(carry, CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } - // JR n - case 0x18: - // -1 because PC is now pointing past the opcode - regs.PC += static_cast(memory.read(regs.PC++)); - break; + // SRL n + for_each_register(0x3F, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, SRL_reg) + + // SRL (HL) + case 0x3E: { + u8 value = memory.read(regs.HL); + bool carry = (value & 0x01) != 0; + value >>= 1; + memory.write(regs.HL, value); + set_flag_if(value == 0, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + reset_flag(HALF_CARRY_FLAG); + set_flag_if(carry, CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + } - // JR cc, n - case 0x20: { // JR NZ, n - s8 offset = static_cast(memory.read(regs.PC++)); - if (!check_flag(ZERO_FLAG)) - regs.PC += offset; - break; - } + default: { + int bit_op = sub_opcode >> 6; + int reg = sub_opcode & 7; + int b = (sub_opcode >> 3) & 7; + bool res; + switch (bit_op) + { + case 1: // BIT + switch(reg) + { + case 0: + res = check_bit(regs.B, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 1: + res = check_bit(regs.C, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 2: + res = check_bit(regs.D, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 3: + res = check_bit(regs.E, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 4: + res = check_bit(regs.H, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 5: + res = check_bit(regs.L, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + case 6: + res = check_bit(memory.read(regs.HL), b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 16; + break; + case 7: + res = check_bit(regs.A, b); + set_flag_if(res == false, ZERO_FLAG); + reset_flag(ADD_SUB_FLAG); + set_flag(HALF_CARRY_FLAG); + cycles_until_next_instruction = 8; + break; + } + break; + + case 2: // RES + switch(reg) + { + case 0: + regs.B = reset_bit(regs.B, b); + cycles_until_next_instruction = 8; + break; + case 1: + regs.C = reset_bit(regs.C, b); + cycles_until_next_instruction = 8; + break; + case 2: + regs.D = reset_bit(regs.D, b); + cycles_until_next_instruction = 8; + break; + case 3: + regs.E = reset_bit(regs.E, b); + cycles_until_next_instruction = 8; + break; + case 4: + regs.H = reset_bit(regs.H, b); + cycles_until_next_instruction = 8; + break; + case 5: + regs.L = reset_bit(regs.L, b); + cycles_until_next_instruction = 8; + break; + case 6: + memory.write(regs.HL, reset_bit(memory.read(regs.HL), b)); + cycles_until_next_instruction = 16; + break; + case 7: + regs.A = reset_bit(regs.A, b); + cycles_until_next_instruction = 8; + break; + } + break; + + case 3: // SET + switch(reg) + { + case 0: + regs.B = set_bit(regs.B, b); + cycles_until_next_instruction = 8; + break; + case 1: + regs.C = set_bit(regs.C, b); + cycles_until_next_instruction = 8; + break; + case 2: + regs.D = set_bit(regs.D, b); + cycles_until_next_instruction = 8; + break; + case 3: + regs.E = set_bit(regs.E, b); + cycles_until_next_instruction = 8; + break; + case 4: + regs.H = set_bit(regs.H, b); + cycles_until_next_instruction = 8; + break; + case 5: + regs.L = set_bit(regs.L, b); + cycles_until_next_instruction = 8; + break; + case 6: + memory.write(regs.HL, set_bit(memory.read(regs.HL), b)); + cycles_until_next_instruction = 16; + break; + case 7: + regs.A = set_bit(regs.A, b); + cycles_until_next_instruction = 8; + break; + } + break; + + default: + logger.critical("Unknown sub-opcode after 0xCB"); + break; + } + } + + } + break; + } + // DI + case 0xF3: + IME = 0; + cycles_until_next_instruction = 4; + break; + + // EI + case 0xFB: + IME = 1; + cycles_until_next_instruction = 4; + break; + + // Jumps + // JP nn + case 0xC3: + regs.PC = memory.read16(regs.PC); + cycles_until_next_instruction = 12; + break; + + // JP cc, nn + case 0xC2: { // JP NZ, nn + u16 dst = memory.read16(regs.PC); + if (!check_flag(ZERO_FLAG)) + regs.PC = dst; + else + regs.PC += 2; // if !cc, skip 2 dst bytes + cycles_until_next_instruction = 12; + break; + } - case 0x28: { // JR Z, n - s8 offset = static_cast(memory.read(regs.PC++)); - if (check_flag(ZERO_FLAG)) - regs.PC += offset; - break; - } + case 0xCA: { // JP Z, nn + u16 dst = memory.read16(regs.PC); + if (check_flag(ZERO_FLAG)) + regs.PC = dst; + else + regs.PC += 2; // if !cc, skip 2 dst bytes + cycles_until_next_instruction = 12; + break; + } - case 0x30: { // JR NC, n - s8 offset = static_cast(memory.read(regs.PC++)); - if (!check_flag(CARRY_FLAG)) - regs.PC += offset; - break; - } + case 0xD2: { // JP NC, nn + u16 dst = memory.read16(regs.PC); + if (!check_flag(CARRY_FLAG)) + regs.PC = dst; + else + regs.PC += 2; // if !cc, skip 2 dst bytes + cycles_until_next_instruction = 12; + break; + } - case 0x38: { // JR C, n - s8 offset = static_cast(memory.read(regs.PC++)); - if (check_flag(CARRY_FLAG)) - regs.PC += offset; - break; - } + case 0xDA: { // JP C, nn + u16 dst = memory.read16(regs.PC); + if (check_flag(CARRY_FLAG)) + regs.PC = dst; + else + regs.PC += 2; // if !cc, skip 2 dst bytes + cycles_until_next_instruction = 12; + break; + } - // Calls - // CALL nn - case 0xCD: { - u16 addr = memory.read16(regs.PC); - regs.PC += 2; - do_call(addr); - break; - } + // JP (HL) + case 0xE9: + regs.PC = regs.HL; + cycles_until_next_instruction = 4; + break; - // CALL cc, nn - case 0xC4: { // CALL NZ, nn - if (!check_flag(ZERO_FLAG)) { + // Calls + // CALL nn + case 0xCD: { u16 addr = memory.read16(regs.PC); regs.PC += 2; do_call(addr); - } else { - regs.PC += 2; // if !cc, skip 2 (nn) bytes + cycles_until_next_instruction = 12; + break; } - break; - } - case 0xCC: { // CALL Z, nn - if (check_flag(ZERO_FLAG)) { - u16 addr = memory.read16(regs.PC); - regs.PC += 2; - do_call(addr); - } else { - regs.PC += 2; // if !cc, skip 2 (nn) bytes + // CALL cc, nn + case 0xC4: { // CALL NZ, nn + if (!check_flag(ZERO_FLAG)) { + u16 addr = memory.read16(regs.PC); + regs.PC += 2; + do_call(addr); + } else { + regs.PC += 2; // if !cc, skip 2 (nn) bytes + } + cycles_until_next_instruction = 12; + break; } - break; - } - case 0xD4: { // CALL NC, nn - if (!check_flag(CARRY_FLAG)) { - u16 addr = memory.read16(regs.PC); - regs.PC += 2; - do_call(addr); - } else { - regs.PC += 2; // if !cc, skip 2 (nn) bytes + case 0xCC: { // CALL Z, nn + if (check_flag(ZERO_FLAG)) { + u16 addr = memory.read16(regs.PC); + regs.PC += 2; + do_call(addr); + } else { + regs.PC += 2; // if !cc, skip 2 (nn) bytes + } + cycles_until_next_instruction = 12; + break; } - break; - } - case 0xDC: { // CALL C, nn - if (check_flag(CARRY_FLAG)) { - u16 addr = memory.read16(regs.PC); - regs.PC += 2; - do_call(addr); - } else { - regs.PC += 2; // if !cc, skip 2 (nn) bytes + case 0xD4: { // CALL NC, nn + if (!check_flag(CARRY_FLAG)) { + u16 addr = memory.read16(regs.PC); + regs.PC += 2; + do_call(addr); + } else { + regs.PC += 2; // if !cc, skip 2 (nn) bytes + } + cycles_until_next_instruction = 12; + break; } - break; - } - - // Restarts - RST(0xC7, 0x00) - RST(0xCF, 0x08) - RST(0xD7, 0x10) - RST(0xDF, 0x18) - RST(0xE7, 0x20) - RST(0xEF, 0x28) - RST(0xF7, 0x30) - RST(0xFF, 0x38) - - // Returns - // RET - case 0xC9: { - u16 retaddr = memory.read16(regs.SP); - regs.SP += 2; - regs.PC = retaddr; - break; - } - // RET cc - case 0xC0: // RET NZ - if (!check_flag(ZERO_FLAG)) { - u16 retaddr = memory.read16(regs.SP); - regs.SP += 2; - regs.PC = retaddr; + case 0xDC: { // CALL C, nn + if (check_flag(CARRY_FLAG)) { + u16 addr = memory.read16(regs.PC); + regs.PC += 2; + do_call(addr); + } else { + regs.PC += 2; // if !cc, skip 2 (nn) bytes + } + cycles_until_next_instruction = 12; + break; } - break; - case 0xC8: // RET Z - if (check_flag(ZERO_FLAG)) { + // Restarts + RST(0xC7, 0x00) + RST(0xCF, 0x08) + RST(0xD7, 0x10) + RST(0xDF, 0x18) + RST(0xE7, 0x20) + RST(0xEF, 0x28) + RST(0xF7, 0x30) + RST(0xFF, 0x38) + + // Returns + // RET + case 0xC9: { u16 retaddr = memory.read16(regs.SP); regs.SP += 2; regs.PC = retaddr; + cycles_until_next_instruction = 8; + break; } - break; - case 0xD0: // RET NC - if (!check_flag(CARRY_FLAG)) { - u16 retaddr = memory.read16(regs.SP); - regs.SP += 2; - regs.PC = retaddr; - } - break; + // RET cc + case 0xC0: // RET NZ + if (!check_flag(ZERO_FLAG)) { + u16 retaddr = memory.read16(regs.SP); + regs.SP += 2; + regs.PC = retaddr; + } + cycles_until_next_instruction = 8; + break; + + case 0xC8: // RET Z + if (check_flag(ZERO_FLAG)) { + u16 retaddr = memory.read16(regs.SP); + regs.SP += 2; + regs.PC = retaddr; + } + cycles_until_next_instruction = 8; + break; + + case 0xD0: // RET NC + if (!check_flag(CARRY_FLAG)) { + u16 retaddr = memory.read16(regs.SP); + regs.SP += 2; + regs.PC = retaddr; + } + cycles_until_next_instruction = 8; + break; + + case 0xD8: // RET C + if (check_flag(CARRY_FLAG)) { + u16 retaddr = memory.read16(regs.SP); + regs.SP += 2; + regs.PC = retaddr; + } + cycles_until_next_instruction = 8; + break; - case 0xD8: // RET C - if (check_flag(CARRY_FLAG)) { + // RETI + case 0xD9: { + // RET && EI u16 retaddr = memory.read16(regs.SP); regs.SP += 2; regs.PC = retaddr; + IME=1; + cycles_until_next_instruction = 8; + break; } - break; - - // RETI - case 0xD9: { - // RET && EI - u16 retaddr = memory.read16(regs.SP); - regs.SP += 2; - regs.PC = retaddr; - IME=1; - break; + + default: + std::ostringstream errmsg; + errmsg << "Unknown opcode 0x"; + errmsg << std::hex << std::setw(2) << std::setfill('0') << opcode; + errmsg << " at 0x" << std::hex << std::setw(4) << regs.PC-1; + errmsg << " (cycle count = " << std::dec << cycle_count << ")"; + logger.critical(errmsg.str()); + break; } + break; + } - default: - std::ostringstream errmsg; - errmsg << "Unknown opcode 0x"; - errmsg << std::hex << std::setw(2) << std::setfill('0') << opcode; - errmsg << " at 0x" << std::hex << std::setw(4) << regs.PC-1; - errmsg << " (cycle count = " << std::dec << cycle_count << ")"; - logger.critical(errmsg.str()); - break; - - } // end switch - - ++cycle_count; + if (cycles_until_video_update <= 0) + cycles_until_video_update = video.update(); - video.update(); - - // Check for interrupts before opcode fetching - u8 IE=memory.high[GBMemory::I_IE]; - //logger.trace("IME=", int(IME), " IE=", int(IE)); - if (IME && IE) - { - u8 IF = memory.high[GBMemory::I_IF]; - //logger.trace("Dispatching interrupts: IE=", int(IE), " IF=", int(IF)); - if (IF) - { - if ((IF & IRQ_VBLANK) && (IE & IRQ_VBLANK)) - { - IME = 0; - IF &= (~IRQ_VBLANK); - do_call(0x40); - logger.trace("VBLANK IRQ"); - } - else if ((IF & IRQ_LCD_STAT) && (IE & IRQ_LCD_STAT)) - { - IME = 0; - IF &= (~IRQ_LCD_STAT); - do_call(0x48); - logger.trace("LCD STAT IRQ"); - } - else if ((IF & IRQ_TIMER) && (IE & IRQ_TIMER)) - { - IME = 0; - IF &= (~IRQ_TIMER); - do_call(0x50); - logger.trace("TIMER IRQ"); - } - else if ((IF & IRQ_SERIAL) && (IE & IRQ_SERIAL)) - { - IME = 0; - IF &= (~IRQ_SERIAL); - do_call(0x58); - logger.trace("SERIAL IRQ"); - } - else if ((IF & IRQ_JOYPAD) && (IE & IRQ_JOYPAD)) - { - IME = 0; - IF &= (~IRQ_JOYPAD); - do_call(0x60); - logger.trace("JOYPAD IRQ"); - } - } - memory.high[GBMemory::I_IF] = IF; - } - - /* - for(BreakpointMap::iterator i=breakpoints.begin(); - i != breakpoints.end(); - i++) - { - if (i->second.addr == regs.PC && i->second.enabled) - return BREAKPOINT; - } - */ + cycles_until_video_update -= CYCLE_STEP; + cycles_until_next_instruction -= CYCLE_STEP; - return NORMAL; + if (cycles_until_next_instruction > 0) return WAIT; + else return NORMAL; } GameBoy::run_status GameBoy::run() { + static const int CYCLES_PER_INPUT_CHECK = 40000; + static int c=0; SDL_Event ev; run_status status=NORMAL; - while (status == NORMAL) + while (status == NORMAL || status == WAIT) { - while (video.poll_event(&ev)) + ++c; + if (c==CYCLES_PER_INPUT_CHECK) { - switch(ev.type) + c=0; + while (video.poll_event(&ev)) { - case SDL_KEYDOWN: - switch(ev.key.keysym.sym) - { - case SDLK_ESCAPE: - return PAUSED; - case SDLK_q: - return QUIT; - default: - break; - } - break; - case SDL_QUIT: - return QUIT; + switch(ev.type) + { + case SDL_KEYDOWN: + switch(ev.key.keysym.sym) + { + case SDLK_ESCAPE: + return PAUSED; + case SDLK_q: + return QUIT; + default: + break; + } + break; + case SDL_QUIT: + return QUIT; + } } } status = run_cycle(); diff --git a/gbcore.h b/gbcore.h index 42bef04..495fa41 100644 --- a/gbcore.h +++ b/gbcore.h @@ -70,6 +70,9 @@ class GameBoy u8 HALT; // Is the CPU halted waiting for an interrupt? u32 cycle_count; + u32 cycles_until_video_update; + u32 cycles_until_next_instruction; + static const u32 CYCLE_STEP = 4; inline void do_call(u16 addr) { @@ -91,6 +94,7 @@ class GameBoy TRACEPOINT, PAUSED, QUIT, + WAIT, }; // Constructors diff --git a/opcodes.h b/opcodes.h index e872c6b..c1d82d2 100644 --- a/opcodes.h +++ b/opcodes.h @@ -9,8 +9,8 @@ #define for_each_register(opA, opB, opC, opD, opE, opH, opL, macro) \ macro(opA, A) \ macro(opB, B) \ - macro(opC, C) \ - macro(opD, D) \ + macro(opC, C) \ + macro(opD, D) \ macro(opE, E) \ macro(opH, H) \ macro(opL, L) @@ -24,11 +24,13 @@ #define LD_reg_nn(opcode, reg) \ case opcode: \ regs.reg = memory.read(regs.PC++); \ + cycles_until_next_instruction = 8; \ break; #define LD_reg_reg(opcode, reg1, reg2) \ case opcode: \ regs.reg1 = regs.reg2; \ + cycles_until_next_instruction = 4; \ break; #define LD_A_reg(opcode, reg2) LD_reg_reg(opcode, A, reg2) @@ -43,12 +45,14 @@ #define LD_reg__HL_(opcode, reg) \ case opcode: \ regs.reg = memory.read(regs.HL); \ + cycles_until_next_instruction = 8; \ break; // LD (HL), reg #define LD__HL__reg(opcode, reg) \ case opcode: \ memory.write(regs.HL, regs.reg); \ + cycles_until_next_instruction = 8; \ break; #define PUSH(opcode, regH, regL) \ @@ -56,6 +60,7 @@ memory.write(regs.SP-1, regs.regH); \ memory.write(regs.SP-2, regs.regL); \ regs.SP -= 2; \ + cycles_until_next_instruction = 16; \ break; #define POP(opcode, regH, regL) \ @@ -63,6 +68,7 @@ regs.regL = memory.read(regs.SP); \ regs.regH = memory.read(regs.SP+1); \ regs.SP += 2; \ + cycles_until_next_instruction = 12; \ break; #define ADD_A_reg(opcode, reg) \ @@ -75,6 +81,7 @@ set_flag_if (res > 0xFF, CARRY_FLAG); \ set_flag_if (regs.A == 0, ZERO_FLAG); \ set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -89,6 +96,7 @@ set_flag_if (res > 0xFF, CARRY_FLAG); \ set_flag_if (regs.A == 0, ZERO_FLAG); \ set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -101,6 +109,7 @@ set_flag_if (res < 0, CARRY_FLAG); \ set_flag_if (res == 0, ZERO_FLAG); \ set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -114,6 +123,7 @@ set_flag_if (res < 0, CARRY_FLAG); \ set_flag_if (res == 0, ZERO_FLAG); \ set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -124,6 +134,7 @@ reset_flag(ADD_SUB_FLAG); \ set_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -134,6 +145,7 @@ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -144,6 +156,7 @@ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ reset_flag(CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -155,6 +168,7 @@ set_flag_if (res < 0, CARRY_FLAG); \ set_flag_if (res == 0, ZERO_FLAG); \ set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -165,6 +179,7 @@ reset_flag(ADD_SUB_FLAG); \ set_flag_if (regs.reg == 0, ZERO_FLAG); \ set_flag_if (half_res > 0x0F, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -175,6 +190,7 @@ set_flag(ADD_SUB_FLAG); \ set_flag_if (regs.reg == 0, ZERO_FLAG); \ set_flag_if (half_res < 0, HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 4; \ break; \ } @@ -188,17 +204,20 @@ set_flag_if (res == 0, ZERO_FLAG); \ set_flag_if (half_res > 0xFFF, HALF_CARRY_FLAG); \ set_flag_if (res > 0xFFFF, CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } #define INC_reg16(opcode, reg16) \ case opcode: \ ++regs.reg16; \ + cycles_until_next_instruction = 8; \ break; #define DEC_reg16(opcode, reg16) \ case opcode: \ --regs.reg16; \ + cycles_until_next_instruction = 8; \ break; #define SWAP_reg(opcode, reg) \ @@ -208,11 +227,13 @@ reset_flag(CARRY_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ reset_flag(ADD_SUB_FLAG); \ + cycles_until_next_instruction = 8; \ break; #define RST(opcode, n) \ case opcode: \ do_call(n); \ + cycles_until_next_instruction = 32; \ break; @@ -227,6 +248,7 @@ set_flag_if(regs.reg == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -238,6 +260,7 @@ set_flag_if(regs.reg == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -252,6 +275,7 @@ set_flag_if(regs.reg == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -263,6 +287,7 @@ set_flag_if(regs.reg == 0, ZERO_FLAG); \ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -275,6 +300,7 @@ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ set_flag_if(carry, CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -287,6 +313,7 @@ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ set_flag_if(carry, CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } @@ -298,6 +325,7 @@ reset_flag(ADD_SUB_FLAG); \ reset_flag(HALF_CARRY_FLAG); \ set_flag_if(carry, CARRY_FLAG); \ + cycles_until_next_instruction = 8; \ break; \ } diff --git a/tests/test_core.cc b/tests/test_core.cc index e13056d..6dd0e95 100644 --- a/tests/test_core.cc +++ b/tests/test_core.cc @@ -60,7 +60,8 @@ int main(int argc, char **argv) if (command == "step" || command == "s") { - gb.run_cycle(); + while(gb.run_cycle() == GameBoy::WAIT) {} // do nothing + cout << gb.status_string() << endl; } else if (command == "run" || command == "r" || command == "cont") @@ -69,20 +70,32 @@ int main(int argc, char **argv) { gb.reset(); } - int status = gb.run(); - if (status == GameBoy::QUIT) + if (arguments.size() == 0) { - break; + int status = gb.run(); + if (status == GameBoy::QUIT) + { + break; + } + else if (status == GameBoy::BREAKPOINT) + { + cout << "Breakpoint hit at " << gb.regs.PC << endl; + cout << gb.status_string() << endl; + } + else + { + cout << "run returned with status " << status << endl; + } } - else if (status == GameBoy::BREAKPOINT) + else if (arguments.size() == 1) { - cout << "Breakpoint hit at " << gb.regs.PC << endl; + int cycles = str2int(arguments[0]); + + for (u32 i=0; i