From 4986930511f28ecfc902cabc9a0839291be75f02 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Tue, 27 Mar 2018 15:46:00 +0300 Subject: [PATCH] Mostly complete emulation of the OAM bug. Passes oam_bug-2. --- Core/display.c | 13 +++- Core/gb.c | 2 + Core/gb.h | 1 + Core/memory.c | 189 +++++++++++++++++++++++++++++++++++++++++++++---- Core/memory.h | 2 + Core/z80_cpu.c | 96 ++++++++++--------------- 6 files changed, 227 insertions(+), 76 deletions(-) diff --git a/Core/display.c b/Core/display.c index 36363fb..b614dca 100755 --- a/Core/display.c +++ b/Core/display.c @@ -288,7 +288,7 @@ void GB_lcd_off(GB_gameboy_t *gb) gb->current_line = 0; gb->ly_for_comparison = 0; - gb->oam_search_index = 0; + gb->accessed_oam_row = -1; } static void add_object_from_index(GB_gameboy_t *gb, unsigned index) @@ -478,6 +478,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) /* Lines 0 - 143 */ for (; gb->current_line < LINES; gb->current_line++) { gb->oam_write_blocked = gb->is_cgb; + gb->accessed_oam_row = 0; GB_SLEEP(gb, display, 6, 3); gb->io_registers[GB_IO_LY] = gb->current_line; gb->oam_read_blocked = true; @@ -504,8 +505,15 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->n_visible_objs = 0; for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) { - add_object_from_index(gb, gb->oam_search_index); + if (gb->is_cgb) { + add_object_from_index(gb, gb->oam_search_index); + /* The CGB does not care about the accessed OAM row as there's no OAM bug*/ + } GB_SLEEP(gb, display, 8, 2); + if (!gb->is_cgb) { + add_object_from_index(gb, gb->oam_search_index); + gb->accessed_oam_row = (gb->oam_search_index & ~1) * 4 + 8; + } if (gb->oam_search_index == 37) { gb->vram_read_blocked = !gb->is_cgb; gb->vram_write_blocked = false; @@ -513,6 +521,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) GB_STAT_update(gb); } } + gb->accessed_oam_row = -1; gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 3; diff --git a/Core/gb.c b/Core/gb.c index 01c049a..fe8fe5a 100755 --- a/Core/gb.c +++ b/Core/gb.c @@ -500,6 +500,8 @@ void GB_reset(GB_gameboy_t *gb) /* These are not deterministic, but 00 (CGB) and FF (DMG) are the most common initial values by far */ gb->io_registers[GB_IO_DMA] = gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = gb->is_cgb? 0x00 : 0xFF; + + gb->accessed_oam_row = -1; gb->magic = (uintptr_t)'SAME'; } diff --git a/Core/gb.h b/Core/gb.h index 3d0a919..bf836cc 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -427,6 +427,7 @@ struct GB_gameboy_internal_s { uint8_t n_visible_objs; bool fetching_objects; uint8_t oam_search_index; + uint8_t accessed_oam_row; ); /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ diff --git a/Core/memory.c b/Core/memory.c index 41345da..ecef4c8 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -29,6 +29,84 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) return GB_BUS_INTERNAL; } +static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c) +{ + return ((a ^ c) & (b ^ c)) ^ c; +} + +static uint8_t bitwise_glitch_read(uint8_t a, uint8_t b, uint8_t c) +{ + return b | (a & c); +} + +static uint8_t bitwise_glitch_pop(uint8_t a, uint8_t b, uint8_t c, uint8_t d) +{ + return (b & a) | (b & c) | (b & d) | (a & c & d); +} + +void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address) +{ + if (gb->is_cgb) return; + + if (address >= 0xFE00 && address < 0xFF00) { + if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) { + gb->oam[gb->accessed_oam_row] = bitwise_glitch(gb->oam[gb->accessed_oam_row], + gb->oam[gb->accessed_oam_row - 8], + gb->oam[gb->accessed_oam_row - 4]); + gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch(gb->oam[gb->accessed_oam_row + 1], + gb->oam[gb->accessed_oam_row - 7], + gb->oam[gb->accessed_oam_row - 3]); + for (unsigned i = 2; i < 8; i++) { + gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i]; + } + } + } +} + +void GB_trigger_oam_bug_read(GB_gameboy_t *gb, uint16_t address) +{ + if (gb->is_cgb) return; + + if (address >= 0xFE00 && address < 0xFF00) { + if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 8) { + gb->oam[gb->accessed_oam_row - 8] = + gb->oam[gb->accessed_oam_row] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row], + gb->oam[gb->accessed_oam_row - 8], + gb->oam[gb->accessed_oam_row - 4]); + gb->oam[gb->accessed_oam_row - 7] = + gb->oam[gb->accessed_oam_row + 1] = bitwise_glitch_read(gb->oam[gb->accessed_oam_row + 1], + gb->oam[gb->accessed_oam_row - 7], + gb->oam[gb->accessed_oam_row - 3]); + for (unsigned i = 2; i < 8; i++) { + gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 8 + i]; + } + } + } +} + +void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address) +{ + if (gb->is_cgb) return; + + if (address >= 0xFE00 && address < 0xFF00) { + if (gb->accessed_oam_row != 0xff && gb->accessed_oam_row >= 0x20 && gb->accessed_oam_row < 0x98) { + gb->oam[gb->accessed_oam_row - 0x8] = bitwise_glitch_pop(gb->oam[gb->accessed_oam_row - 0x10], + gb->oam[gb->accessed_oam_row - 0x08], + gb->oam[gb->accessed_oam_row ], + gb->oam[gb->accessed_oam_row - 0x04] + ); + gb->oam[gb->accessed_oam_row - 0x7] = bitwise_glitch_pop(gb->oam[gb->accessed_oam_row - 0x0f], + gb->oam[gb->accessed_oam_row - 0x07], + gb->oam[gb->accessed_oam_row + 0x01], + gb->oam[gb->accessed_oam_row - 0x03] + ); + for (unsigned i = 0; i < 8; i++) { + gb->oam[gb->accessed_oam_row + i] = gb->oam[gb->accessed_oam_row - 0x10 + i] = gb->oam[gb->accessed_oam_row - 0x08 + i]; + } + } + } +} + static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) { if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting) || addr >= 0xFE00) return false; @@ -118,14 +196,54 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) return gb->ram[addr & 0x0FFF]; } - if (addr < 0xFEA0) { - if (gb->oam_read_blocked || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { - return 0xFF; - } - return gb->oam[addr & 0xFF]; - } - if (addr < 0xFF00) { + if (gb->oam_write_blocked) { + GB_trigger_oam_bug_read(gb, addr); + return 0xff; + } + + if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + /* Todo: Does reading from OAM during DMA causes the OAM bug? */ + return 0xff; + } + + if (gb->oam_read_blocked) { + if (!gb->is_cgb) { + if (addr < 0xFEA0) { + if (gb->accessed_oam_row == 0) { + gb->oam[(addr & 0xf8)] = + gb->oam[0] = bitwise_glitch_read(gb->oam[0], + gb->oam[(addr & 0xf8)], + gb->oam[(addr & 0xfe)]); + gb->oam[(addr & 0xf8) + 1] = + gb->oam[1] = bitwise_glitch_read(gb->oam[1], + gb->oam[(addr & 0xf8) + 1], + gb->oam[(addr & 0xfe) | 1]); + for (unsigned i = 2; i < 8; i++) { + gb->oam[i] = gb->oam[(addr & 0xf8) + i]; + } + } + else if (gb->accessed_oam_row == 0xa0) { + gb->oam[0x9e] = bitwise_glitch_read(gb->oam[0x9c], + gb->oam[0x9e], + gb->oam[(addr & 0xf8) | 6]); + gb->oam[0x9f] = bitwise_glitch_read(gb->oam[0x9d], + gb->oam[0x9f], + gb->oam[(addr & 0xf8) | 7]); + + for (unsigned i = 0; i < 8; i++) { + gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i]; + } + } + } + } + return 0xff; + } + + if (addr < 0xFEA0) { + return gb->oam[addr & 0xFF]; + } + /* Unusable. CGB results are verified, but DMG results were tested on a SGB2 */ /* Also, writes to this area are not emulated */ if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { /* Seems to be disabled in Modes 2 and 3 */ @@ -134,6 +252,10 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) if (gb->is_cgb) { return (addr & 0xF0) | ((addr >> 4) & 0xF); } + } + + if (addr < 0xFF00) { + return 0; } @@ -398,16 +520,53 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) return; } - if (addr < 0xFEA0) { - if (gb->oam_write_blocked|| (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + if (addr < 0xFF00) { + if (gb->oam_write_blocked) { + GB_trigger_oam_bug(gb, addr); return; } - gb->oam[addr & 0xFF] = value; - return; - } - - if (addr < 0xFF00) { - GB_log(gb, "Wrote %02x to %04x (Unused)\n", value, addr); + + if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + /* Todo: Does writing to OAM during DMA causes the OAM bug? */ + return; + } + + if (gb->is_cgb) { + if (addr < 0xFEA0) { + gb->oam[addr & 0xFF] = value; + } + return; + } + + if (addr < 0xFEA0) { + if (gb->accessed_oam_row == 0xa0) { + for (unsigned i = 0; i < 8; i++) { + if ((i & 6) != (addr & 6)) { + gb->oam[(addr & 0xf8) + i] = gb->oam[0x98 + i]; + } + else { + gb->oam[(addr & 0xf8) + i] = bitwise_glitch(gb->oam[(addr & 0xf8) + i], gb->oam[0x9c], gb->oam[0x98 + i]); + } + } + } + + gb->oam[addr & 0xFF] = value; + + if (gb->accessed_oam_row == 0) { + gb->oam[0] = bitwise_glitch(gb->oam[0], + gb->oam[(addr & 0xf8)], + gb->oam[(addr & 0xfe)]); + gb->oam[1] = bitwise_glitch(gb->oam[1], + gb->oam[(addr & 0xf8) + 1], + gb->oam[(addr & 0xfe) | 1]); + for (unsigned i = 2; i < 8; i++) { + gb->oam[i] = gb->oam[(addr & 0xf8) + i]; + } + } + } + else if (gb->accessed_oam_row == 0) { + gb->oam[addr & 0x7] = value; + } return; } diff --git a/Core/memory.h b/Core/memory.h index 16b3b92..c000b7a 100644 --- a/Core/memory.h +++ b/Core/memory.h @@ -7,6 +7,8 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value); #ifdef GB_INTERNAL void GB_dma_run(GB_gameboy_t *gb); void GB_hdma_run(GB_gameboy_t *gb); +void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address); +void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address); #endif #endif /* memory_h */ diff --git a/Core/z80_cpu.c b/Core/z80_cpu.c index 12a3073..4db336c 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -22,31 +22,6 @@ typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode); This is equivalent to running the memory write 1 T-cycle before the memory read. */ - -static uint8_t bitwise_glitch(uint8_t a, uint8_t b, uint8_t c) -{ - return ((a ^ c) & (b ^ c)) ^ c; -} - -static void trigger_oam_bug(GB_gameboy_t *gb, uint8_t register_id) -{ - if (gb->is_cgb) return; - - if (gb->registers[register_id] >= 0xFE00 && gb->registers[register_id] < 0xFF00) { - if (gb->oam_search_index < 38 && gb->oam_search_index > 0) { - gb->oam[gb->oam_search_index * 4 + 4] = bitwise_glitch(gb->oam[gb->oam_search_index * 4 + 4], - gb->oam[gb->oam_search_index * 4 - 4], - gb->oam[gb->oam_search_index * 4]); - gb->oam[gb->oam_search_index * 4 + 5] = bitwise_glitch(gb->oam[gb->oam_search_index * 4 + 5], - gb->oam[gb->oam_search_index * 4 - 3], - gb->oam[gb->oam_search_index * 4 + 1]); - for (unsigned i = 2; i < 8; i++) { - gb->oam[gb->oam_search_index * 4 + 4 + i] = gb->oam[gb->oam_search_index * 4 - 4 + i]; - } - } - } -} - static void ill(GB_gameboy_t *gb, uint8_t opcode) { GB_log(gb, "Illegal Opcode. Halting.\n"); @@ -115,7 +90,7 @@ static void inc_rr(GB_gameboy_t *gb, uint8_t opcode) { uint8_t register_id = (opcode >> 4) + 1; GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, register_id); /* Todo: test T-cycle timing */ + GB_trigger_oam_bug(gb, gb->registers[register_id]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); gb->registers[register_id]++; } @@ -240,7 +215,7 @@ static void dec_rr(GB_gameboy_t *gb, uint8_t opcode) { uint8_t register_id = (opcode >> 4) + 1; GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, register_id); /* Todo: test T-cycle timing */ + GB_trigger_oam_bug(gb, gb->registers[register_id]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); gb->registers[register_id]--; } @@ -437,7 +412,6 @@ static void ccf(GB_gameboy_t *gb, uint8_t opcode) static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode) { GB_advance_cycles(gb, 3); - trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */ GB_write_memory(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8); GB_advance_cycles(gb, 5); } @@ -445,7 +419,6 @@ static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode) { GB_advance_cycles(gb, 3); - trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */ GB_write_memory(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8); GB_advance_cycles(gb, 5); } @@ -453,7 +426,7 @@ static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode) { GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */ + GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_HL]); /* Todo: test T-cycle timing */ gb->registers[GB_REGISTER_AF] &= 0xFF; gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8; GB_advance_cycles(gb, 4); @@ -461,8 +434,9 @@ static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode) static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode) { + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, GB_REGISTER_HL); /* Todo: test T-cycle timing */ + GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_HL]); /* Todo: test T-cycle timing */ gb->registers[GB_REGISTER_AF] &= 0xFF; gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8; GB_advance_cycles(gb, 4); @@ -748,15 +722,14 @@ static void halt(GB_gameboy_t *gb, uint8_t opcode) static void ret_cc(GB_gameboy_t *gb, uint8_t opcode) { - /* Todo: Verify timing */ if (condition_code(gb, opcode)) { GB_debugger_ret_hook(gb); GB_advance_cycles(gb, 8); - gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); + GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ + gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++); GB_advance_cycles(gb, 4); - gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8; + gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8; GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] += 2; } else { GB_advance_cycles(gb, 8); @@ -768,10 +741,9 @@ static void pop_rr(GB_gameboy_t *gb, uint8_t opcode) uint8_t register_id; GB_advance_cycles(gb, 4); register_id = ((opcode >> 4) + 1) & 3; - trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */ + GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ gb->registers[register_id] = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++); GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */ gb->registers[register_id] |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8; GB_advance_cycles(gb, 4); gb->registers[GB_REGISTER_AF] &= 0xFFF0; // Make sure we don't set impossible flags on F! See Blargg's PUSH AF test. @@ -808,14 +780,15 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) uint16_t call_addr = gb->pc - 1; if (condition_code(gb, opcode)) { GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_SP] -= 2; uint16_t addr = GB_read_memory(gb, gb->pc++); GB_advance_cycles(gb, 4); addr |= (GB_read_memory(gb, gb->pc++) << 8); - GB_advance_cycles(gb, 7); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); + GB_advance_cycles(gb, 3); + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); + GB_advance_cycles(gb, 4); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 5); gb->pc = addr; @@ -830,12 +803,12 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) static void push_rr(GB_gameboy_t *gb, uint8_t opcode) { uint8_t register_id; - GB_advance_cycles(gb, 7); + GB_advance_cycles(gb, 3); + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ + GB_advance_cycles(gb, 4); register_id = ((opcode >> 4) + 1) & 3; - trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */ GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) >> 8); GB_advance_cycles(gb, 4); - trigger_oam_bug(gb, GB_REGISTER_SP); /* Todo: test T-cycle timing */ GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF); GB_advance_cycles(gb, 5); } @@ -982,11 +955,12 @@ static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode) static void rst(GB_gameboy_t *gb, uint8_t opcode) { uint16_t call_addr = gb->pc - 1; - GB_advance_cycles(gb, 7); - gb->registers[GB_REGISTER_SP] -= 2; - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); + GB_advance_cycles(gb, 3); + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); + GB_advance_cycles(gb, 4); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 5); gb->pc = opcode ^ 0xC7; GB_debugger_call_hook(gb, call_addr); @@ -996,11 +970,11 @@ static void ret(GB_gameboy_t *gb, uint8_t opcode) { GB_debugger_ret_hook(gb); GB_advance_cycles(gb, 4); - gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); + GB_trigger_oam_bug_read_increase(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ + gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++); GB_advance_cycles(gb, 4); - gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8; + gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8; GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] += 2; } static void reti(GB_gameboy_t *gb, uint8_t opcode) @@ -1013,14 +987,15 @@ static void call_a16(GB_gameboy_t *gb, uint8_t opcode) { uint16_t call_addr = gb->pc - 1; GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_SP] -= 2; uint16_t addr = GB_read_memory(gb, gb->pc++); GB_advance_cycles(gb, 4); addr |= (GB_read_memory(gb, gb->pc++) << 8); - GB_advance_cycles(gb, 7); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); + GB_advance_cycles(gb, 3); + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); + GB_advance_cycles(gb, 4); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 5); gb->pc = addr; GB_debugger_call_hook(gb, call_addr); @@ -1419,16 +1394,19 @@ void GB_cpu_run(GB_gameboy_t *gb) if (gb->halted && !effecitve_ime && interrupt_queue) { gb->halted = false; } + /* Call interrupt */ else if (effecitve_ime && interrupt_queue) { gb->halted = false; uint16_t call_addr = gb->pc - 1; - GB_advance_cycles(gb, 11); - gb->registers[GB_REGISTER_SP] -= 2; - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); + + GB_advance_cycles(gb, 7); + GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */ + GB_advance_cycles(gb, 4); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) >> 8); interrupt_queue = gb->interrupt_enable; GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); + GB_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F; GB_advance_cycles(gb, 5);