Mostly complete emulation of the OAM bug. Passes oam_bug-2.

This commit is contained in:
Lior Halphon 2018-03-27 15:46:00 +03:00
parent 9093f22293
commit 4986930511
6 changed files with 227 additions and 76 deletions

View File

@ -288,7 +288,7 @@ void GB_lcd_off(GB_gameboy_t *gb)
gb->current_line = 0; gb->current_line = 0;
gb->ly_for_comparison = 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) 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 */ /* Lines 0 - 143 */
for (; gb->current_line < LINES; gb->current_line++) { for (; gb->current_line < LINES; gb->current_line++) {
gb->oam_write_blocked = gb->is_cgb; gb->oam_write_blocked = gb->is_cgb;
gb->accessed_oam_row = 0;
GB_SLEEP(gb, display, 6, 3); GB_SLEEP(gb, display, 6, 3);
gb->io_registers[GB_IO_LY] = gb->current_line; gb->io_registers[GB_IO_LY] = gb->current_line;
gb->oam_read_blocked = true; 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; gb->n_visible_objs = 0;
for (gb->oam_search_index = 0; gb->oam_search_index < 40; gb->oam_search_index++) { 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); 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) { if (gb->oam_search_index == 37) {
gb->vram_read_blocked = !gb->is_cgb; gb->vram_read_blocked = !gb->is_cgb;
gb->vram_write_blocked = false; 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_STAT_update(gb);
} }
} }
gb->accessed_oam_row = -1;
gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] &= ~3;
gb->io_registers[GB_IO_STAT] |= 3; gb->io_registers[GB_IO_STAT] |= 3;

View File

@ -501,6 +501,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 */ /* 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->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'; gb->magic = (uintptr_t)'SAME';
} }

View File

@ -427,6 +427,7 @@ struct GB_gameboy_internal_s {
uint8_t n_visible_objs; uint8_t n_visible_objs;
bool fetching_objects; bool fetching_objects;
uint8_t oam_search_index; 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 */ /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */

View File

@ -29,6 +29,84 @@ static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr)
return GB_BUS_INTERNAL; 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) 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; 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]; 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 (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 */ /* Unusable. CGB results are verified, but DMG results were tested on a SGB2 */
/* Also, writes to this area are not emulated */ /* 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 */ 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) { if (gb->is_cgb) {
return (addr & 0xF0) | ((addr >> 4) & 0xF); return (addr & 0xF0) | ((addr >> 4) & 0xF);
} }
}
if (addr < 0xFF00) {
return 0; return 0;
} }
@ -398,16 +520,53 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
return; return;
} }
if (addr < 0xFEA0) { if (addr < 0xFF00) {
if (gb->oam_write_blocked|| (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { if (gb->oam_write_blocked) {
GB_trigger_oam_bug(gb, addr);
return; return;
} }
gb->oam[addr & 0xFF] = value;
return;
}
if (addr < 0xFF00) { if ((gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) {
GB_log(gb, "Wrote %02x to %04x (Unused)\n", value, addr); /* 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; return;
} }

View File

@ -7,6 +7,8 @@ void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
#ifdef GB_INTERNAL #ifdef GB_INTERNAL
void GB_dma_run(GB_gameboy_t *gb); void GB_dma_run(GB_gameboy_t *gb);
void GB_hdma_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
#endif /* memory_h */ #endif /* memory_h */

View File

@ -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. 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) static void ill(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_log(gb, "Illegal Opcode. Halting.\n"); 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; uint8_t register_id = (opcode >> 4) + 1;
GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 4);
gb->registers[register_id]++; 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; uint8_t register_id = (opcode >> 4) + 1;
GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 4);
gb->registers[register_id]--; 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) static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 3); 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_write_memory(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 5); 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) static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 3); 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_write_memory(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8);
GB_advance_cycles(gb, 5); 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) static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode)
{ {
GB_advance_cycles(gb, 4); 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] &= 0xFF;
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8; gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8;
GB_advance_cycles(gb, 4); 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) 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); 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] &= 0xFF;
gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8; gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8;
GB_advance_cycles(gb, 4); 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) static void ret_cc(GB_gameboy_t *gb, uint8_t opcode)
{ {
/* Todo: Verify timing */
if (condition_code(gb, opcode)) { if (condition_code(gb, opcode)) {
GB_debugger_ret_hook(gb); GB_debugger_ret_hook(gb);
GB_advance_cycles(gb, 8); 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_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_advance_cycles(gb, 8);
gb->registers[GB_REGISTER_SP] += 2;
} }
else { else {
GB_advance_cycles(gb, 8); GB_advance_cycles(gb, 8);
@ -768,10 +741,9 @@ static void pop_rr(GB_gameboy_t *gb, uint8_t opcode)
uint8_t register_id; uint8_t register_id;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
register_id = ((opcode >> 4) + 1) & 3; 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->registers[register_id] = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++);
GB_advance_cycles(gb, 4); 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->registers[register_id] |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP]++) << 8;
GB_advance_cycles(gb, 4); 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. 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; uint16_t call_addr = gb->pc - 1;
if (condition_code(gb, opcode)) { if (condition_code(gb, opcode)) {
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->registers[GB_REGISTER_SP] -= 2;
uint16_t addr = GB_read_memory(gb, gb->pc++); uint16_t addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= (GB_read_memory(gb, gb->pc++) << 8); addr |= (GB_read_memory(gb, gb->pc++) << 8);
GB_advance_cycles(gb, 7); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 5);
gb->pc = addr; 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) static void push_rr(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint8_t register_id; 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; 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_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) >> 8);
GB_advance_cycles(gb, 4); 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_write_memory(gb, --gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF);
GB_advance_cycles(gb, 5); 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) static void rst(GB_gameboy_t *gb, uint8_t opcode)
{ {
uint16_t call_addr = gb->pc - 1; uint16_t call_addr = gb->pc - 1;
GB_advance_cycles(gb, 7); GB_advance_cycles(gb, 3);
gb->registers[GB_REGISTER_SP] -= 2; GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8);
GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 5);
gb->pc = opcode ^ 0xC7; gb->pc = opcode ^ 0xC7;
GB_debugger_call_hook(gb, call_addr); 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_debugger_ret_hook(gb);
GB_advance_cycles(gb, 4); 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_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_advance_cycles(gb, 8);
gb->registers[GB_REGISTER_SP] += 2;
} }
static void reti(GB_gameboy_t *gb, uint8_t opcode) 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; uint16_t call_addr = gb->pc - 1;
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
gb->registers[GB_REGISTER_SP] -= 2;
uint16_t addr = GB_read_memory(gb, gb->pc++); uint16_t addr = GB_read_memory(gb, gb->pc++);
GB_advance_cycles(gb, 4); GB_advance_cycles(gb, 4);
addr |= (GB_read_memory(gb, gb->pc++) << 8); addr |= (GB_read_memory(gb, gb->pc++) << 8);
GB_advance_cycles(gb, 7); GB_advance_cycles(gb, 3);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); GB_trigger_oam_bug(gb, gb->registers[GB_REGISTER_SP]); /* Todo: test T-cycle timing */
GB_advance_cycles(gb, 4); 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_advance_cycles(gb, 5);
gb->pc = addr; gb->pc = addr;
GB_debugger_call_hook(gb, call_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) { if (gb->halted && !effecitve_ime && interrupt_queue) {
gb->halted = false; gb->halted = false;
} }
/* Call interrupt */ /* Call interrupt */
else if (effecitve_ime && interrupt_queue) { else if (effecitve_ime && interrupt_queue) {
gb->halted = false; gb->halted = false;
uint16_t call_addr = gb->pc - 1; uint16_t call_addr = gb->pc - 1;
GB_advance_cycles(gb, 11);
gb->registers[GB_REGISTER_SP] -= 2; GB_advance_cycles(gb, 7);
GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); 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; interrupt_queue = gb->interrupt_enable;
GB_advance_cycles(gb, 4); 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; interrupt_queue &= (gb->io_registers[GB_IO_IF]) & 0x1F;
GB_advance_cycles(gb, 5); GB_advance_cycles(gb, 5);