diff --git a/Core/display.c b/Core/display.c index c0f385fc..38897177 100755 --- a/Core/display.c +++ b/Core/display.c @@ -305,6 +305,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4; for (; cycles; cycles -= atomic_increase) { + gb->io_registers[GB_IO_IF] |= gb->future_interrupts & 3; + gb->future_interrupts &= ~3; + bool previous_stat_interrupt_line = gb->stat_interrupt_line; gb->stat_interrupt_line = false; @@ -351,7 +354,12 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) else if (gb->display_cycles == LINES * LINE_LENGTH + stat_delay) { gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 1; - gb->io_registers[GB_IO_IF] |= 1; + if (gb->is_cgb) { + gb->future_interrupts |= 1; + } + else { + gb->io_registers[GB_IO_IF] |= 1; + } /* Entering VBlank state triggers the OAM interrupt. In CGB, it happens 4 cycles earlier */ if (gb->io_registers[GB_IO_STAT] & 0x20 && !gb->is_cgb) { @@ -573,7 +581,12 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) } if (gb->stat_interrupt_line && !previous_stat_interrupt_line) { - gb->io_registers[GB_IO_IF] |= 2; + if (gb->is_cgb) { + gb->future_interrupts |= 2; + } + else { + gb->io_registers[GB_IO_IF] |= 2; + } } } diff --git a/Core/gb.h b/Core/gb.h index c18bf304..37dada46 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -332,9 +332,8 @@ struct GB_gameboy_internal_s { GB_PADDING(uint16_t, serial_cycles); uint16_t serial_cycles; /* This field changed its meaning in v0.10 */ uint16_t serial_length; - bool dont_delay_timer_interrupt; /* If the timer glitch causes a TIMA overflow, it causes the timer to overflow - with different timing, so the triggered interrupt is not delayed. - Todo: needs test ROM. */ + uint8_t future_interrupts; /* Interrupts can occur in any T-cycle. Some timings result in different interrupt + timing when the CPU is in halt mode, and might also affect the DI instruction. */ ); /* APU */ diff --git a/Core/memory.c b/Core/memory.c index d8f7df7e..4fbd1f1d 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -136,7 +136,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) if (addr < 0xFF80) { switch (addr & 0xFF) { case GB_IO_IF: - return gb->io_registers[GB_IO_IF] | 0xE0; + return gb->io_registers[GB_IO_IF] | 0xE0 | gb->future_interrupts; case GB_IO_TAC: return gb->io_registers[GB_IO_TAC] | 0xF8; case GB_IO_STAT: @@ -416,8 +416,9 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case GB_IO_WX: GB_window_related_write(gb, addr & 0xFF, value); break; - case GB_IO_SCX: case GB_IO_IF: + gb->future_interrupts = 0; + case GB_IO_SCX: case GB_IO_SCY: case GB_IO_LYC: case GB_IO_BGP: diff --git a/Core/timing.c b/Core/timing.c index e5ac7882..9a96f595 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -82,14 +82,13 @@ static void GB_ir_run(GB_gameboy_t *gb) static void advance_tima_state_machine(GB_gameboy_t *gb) { + gb->io_registers[GB_IO_IF] |= gb->future_interrupts & 4; + gb->future_interrupts &= ~4; if (gb->tima_reload_state == GB_TIMA_RELOADED) { gb->tima_reload_state = GB_TIMA_RUNNING; } else if (gb->tima_reload_state == GB_TIMA_RELOADING) { - gb->io_registers[GB_IO_IF] |= 4; - if (!gb->dont_delay_timer_interrupt) { - // Todo - } + gb->future_interrupts |= 4; gb->tima_reload_state = GB_TIMA_RELOADED; } } @@ -157,7 +156,6 @@ static void increase_tima(GB_gameboy_t *gb) { gb->io_registers[GB_IO_TIMA]++; if (gb->io_registers[GB_IO_TIMA] == 0) { - gb->dont_delay_timer_interrupt = false; gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA]; gb->tima_reload_state = GB_TIMA_RELOADING; } @@ -197,7 +195,6 @@ void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac) /* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */ if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) { increase_tima(gb); - gb->dont_delay_timer_interrupt = true; } } } diff --git a/Core/z80_cpu.c b/Core/z80_cpu.c index ce30d551..878358da 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -1338,6 +1338,10 @@ void GB_cpu_run(GB_gameboy_t *gb) { gb->vblank_just_occured = false; uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; + if (!gb->halted) { + interrupt_queue |= gb->future_interrupts & gb->interrupt_enable; + } + if (interrupt_queue) { gb->halted = false;