diff --git a/Core/display.c b/Core/display.c index aa6e681..4def6c8 100755 --- a/Core/display.c +++ b/Core/display.c @@ -133,7 +133,7 @@ static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y) } else { x += gb->effective_scx; - y += gb->io_registers[GB_IO_SCY]; + y += gb->effective_scy; } if (gb->io_registers[GB_IO_LCDC] & 0x08 && !in_window) { map = 0x1C00; @@ -289,7 +289,10 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) } uint8_t atomic_increase = gb->cgb_double_speed? 2 : 4; - uint8_t stat_delay = gb->cgb_double_speed? 2 : (gb->cgb_mode? 0 : 4); + /* According to AntonioND's docs this value should be 0 in CGB mode, but tests I ran on my CGB seem to contradict + these findings. + Todo: Investigate what causes the difference between our findings */ + uint8_t stat_delay = gb->cgb_double_speed? 2 : 4; //(gb->cgb_mode? 0 : 4); /* Todo: This is correct for DMG. Is it correct for the 3 CGB modes (DMG/single/double)?*/ uint8_t scx_delay = ((gb->effective_scx & 7) + atomic_increase - 1) & ~(atomic_increase - 1); /* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */ @@ -297,13 +300,13 @@ 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->delayed_interrupts &= ~3; gb->display_cycles += atomic_increase; - /* The very first line is 2 (4 from the CPU's perseptive) clocks shorter when the LCD turns on. - Todo: Verify on the 3 CGB modes, especially double speed mode. */ - if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - atomic_increase) { + /* The very first line is 4 clocks shorter when the LCD turns on. Verified on SGB2, CGB in CGB mode and + CGB in double speed mode. */ + if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - 4) { gb->first_scanline = false; - gb->display_cycles += atomic_increase; + gb->display_cycles += 4; } bool should_compare_ly = true; uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH; @@ -341,6 +344,10 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) 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) { + /* See comment on STAT interrupt at the end of the loop */ + gb->delayed_interrupts |= 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) { @@ -556,10 +563,18 @@ 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) { + /* On CGB, the STAT interrupt is not aligned to a T-Cycle, therefore it is only effective the next T-Cycle + Todo: verify on DMG mode CGB. This was only tested on LYC STAT interrupts, should be tested on others + as well. */ + gb->delayed_interrupts |= 2; + } } +#if 0 /* The value of LY is glitched in the last cycle of every line in CGB mode CGB in single speed - This is based on GiiBiiAdvance's docs */ + This is based on AntonioND's docs, however I could not reproduce these findings on my CGB. + Todo: Find out why my tests contradict these docs */ if (gb->cgb_mode && !gb->cgb_double_speed && gb->display_cycles % LINE_LENGTH == LINE_LENGTH - 4) { uint8_t glitch_pattern[] = {0, 0, 2, 0, 4, 4, 6, 0, 8}; @@ -570,6 +585,7 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) gb->io_registers[GB_IO_LY] = glitch_pattern[gb->io_registers[GB_IO_LY] & 7] | (gb->io_registers[GB_IO_LY] & 0xF8); } } +#endif } void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) @@ -603,13 +619,17 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) /* Render */ - /* Todo: it appears that the actual rendering starts 4 cycles after mode 3 starts. Is this correct? */ - int16_t current_lcdc_x = gb->display_cycles % LINE_LENGTH - MODE2_LENGTH - (gb->effective_scx & 0x7) - 4; + int16_t current_lcdc_x = gb->display_cycles % LINE_LENGTH - MODE2_LENGTH - (gb->effective_scx & 0x7) - 7; for (;gb->previous_lcdc_x < current_lcdc_x; gb->previous_lcdc_x++) { if (gb->previous_lcdc_x >= WIDTH) { continue; } + + if (((gb->previous_lcdc_x + gb->effective_scx) & 7) == 0) { + gb->effective_scy = gb->io_registers[GB_IO_SCY]; + } + if (gb->previous_lcdc_x < 0) { continue; } diff --git a/Core/gb.h b/Core/gb.h index 660186a..58a885a 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -331,6 +331,10 @@ 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; + uint8_t delayed_interrupts; /* When an interrupt occurs while not aligned to a T-cycle, it must be "delayed" */ + 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. */ ); /* APU */ @@ -383,6 +387,7 @@ struct GB_gameboy_internal_s { bool oam_write_blocked; bool vram_write_blocked; bool window_disabled_while_active; + uint8_t effective_scy; // SCY is latched when starting to draw a tile ); /* 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 31354b3..154ca1d 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -452,8 +452,6 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case GB_IO_LCDC: if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) { - /* It appears that there's a slight delay after enabling the screen? */ - /* Todo: verify this. */ gb->display_cycles = 0; gb->first_scanline = true; if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) { diff --git a/Core/timing.c b/Core/timing.c index bc233c1..8192648 100644 --- a/Core/timing.c +++ b/Core/timing.c @@ -82,10 +82,15 @@ static void GB_ir_run(GB_gameboy_t *gb) static void advance_tima_state_machine(GB_gameboy_t *gb) { + gb->delayed_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) { + gb->delayed_interrupts |= 4; // Timer interrupt is not aligned to a T-cycle and therefore is effective only the next one. + } gb->tima_reload_state = GB_TIMA_RELOADED; } } @@ -152,8 +157,8 @@ 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->io_registers[GB_IO_IF] |= 4; gb->tima_reload_state = GB_TIMA_RELOADING; } } @@ -196,6 +201,7 @@ 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 6ea6a38..b6dde8a 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -1337,9 +1337,9 @@ static GB_opcode_t *opcodes[256] = { void GB_cpu_run(GB_gameboy_t *gb) { gb->vblank_just_occured = false; - bool interrupt = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; + uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F & ~gb->delayed_interrupts; - if (interrupt) { + if (interrupt_queue) { gb->halted = false; } @@ -1354,9 +1354,8 @@ void GB_cpu_run(GB_gameboy_t *gb) gb->ime_toggle = false; } - if (effecitve_ime && interrupt) { + if (effecitve_ime && interrupt_queue) { uint8_t interrupt_bit = 0; - uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; while (!(interrupt_queue & 1)) { interrupt_queue >>= 1; interrupt_bit++;