From bb836662dd0a6a2d4c2ff7c8f2ecf3b7e952e5e1 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 14 May 2022 01:14:41 +0300 Subject: [PATCH] More accurate emulation of Hblank skip, emulation of Mode 2 skip --- Core/display.c | 51 ++++++++++++++++++++++++++++++++++++++++--------- Core/gb.h | 2 ++ Core/sm83_cpu.c | 18 ++++++++++++++--- 3 files changed, 59 insertions(+), 12 deletions(-) diff --git a/Core/display.c b/Core/display.c index b5b50e5..cfe0376 100644 --- a/Core/display.c +++ b/Core/display.c @@ -1287,6 +1287,7 @@ object_buffer_pointer++\ static inline uint16_t mode3_batching_length(GB_gameboy_t *gb) { + if (gb->position_in_line != (uint8_t)-16) return 0; if (gb->model & GB_MODEL_NO_SFC_BIT) return 0; if (gb->hdma_on) return 0; if (gb->stopped) return 0; @@ -1328,9 +1329,20 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) unsigned first_batch = (LINE_LENGTH * 2 - gb->cycles_for_line * 2 + gb->display_cycles); GB_display_run(gb, first_batch, force); cycles -= first_batch; + if (gb->display_state == 22) { + gb->io_registers[GB_IO_STAT] &= ~3; + gb->mode_for_interrupt = 0; + GB_STAT_update(gb); + } gb->display_state = 9; gb->display_cycles = 0; } + if (unlikely(gb->delayed_glitch_hblank_interrupt && cycles && gb->current_line < LINES)) { + gb->delayed_glitch_hblank_interrupt = false; + gb->mode_for_interrupt = 0; + GB_STAT_update(gb); + gb->mode_for_interrupt = 3; + } gb->cycles_since_vblank_callback += cycles / 2; /* The PPU does not advance while in STOP mode on the DMG */ @@ -1367,7 +1379,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) GB_STATE(gb, display, 24); GB_STATE(gb, display, 26); GB_STATE(gb, display, 27); - // GB_STATE(gb, display, 28); + GB_STATE(gb, display, 28); GB_STATE(gb, display, 29); GB_STATE(gb, display, 30); GB_STATE(gb, display, 31); @@ -1406,6 +1418,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) gb->current_line = 0; gb->window_y = -1; gb->wy_triggered = false; + gb->position_in_line = -16; gb->ly_for_comparison = 0; gb->io_registers[GB_IO_STAT] &= ~3; @@ -1425,6 +1438,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) GB_SLEEP(gb, display, 34, 2); gb->n_visible_objs = 0; + gb->orig_n_visible_objs = 0; gb->cycles_for_line += 8; // Mode 0 is shorter on the first line 0, so we augment cycles_for_line by 8 extra cycles. gb->io_registers[GB_IO_STAT] &= ~3; @@ -1455,7 +1469,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) // Mode 3 abort, state 9 display9: { // TODO: Timing of things in this scenario is almost completely untested - if (gb->current_line < 144 && !GB_is_sgb(gb) && !gb->disable_rendering) { + if (gb->current_line < LINES && !GB_is_sgb(gb) && !gb->disable_rendering) { GB_log(gb, "The ROM is preventing line %d from fully rendering, this could damage a real device's LCD display.\n", gb->current_line); uint32_t *dest = NULL; if (gb->border_mode != GB_BORDER_ALWAYS) { @@ -1470,12 +1484,26 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) gb->lcd_x++; } } + gb->n_visible_objs = gb->orig_n_visible_objs; gb->current_line++; - gb->vram_read_blocked = false; - gb->vram_write_blocked = false; - gb->wx_triggered = false; - gb->wx166_glitch = false; gb->cycles_for_line = 0; + if (gb->current_line != LINES) { + gb->cycles_for_line = 2; + GB_SLEEP(gb, display, 28, 2); + gb->io_registers[GB_IO_LY] = gb->current_line; + if (gb->position_in_line >= 156 && gb->position_in_line < (uint8_t)-16) { + gb->delayed_glitch_hblank_interrupt = true; + } + GB_STAT_update(gb); + gb->position_in_line = -15; + goto mode_3_start; + } + else { + if (gb->position_in_line >= 156 && gb->position_in_line < (uint8_t)-16) { + gb->delayed_glitch_hblank_interrupt = true; + } + gb->position_in_line = -16; + } } while (true) { @@ -1519,6 +1547,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) gb->mode_for_interrupt = -1; GB_STAT_update(gb); gb->n_visible_objs = 0; + gb->orig_n_visible_objs = 0; if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) { GB_BATCHPOINT(gb, display, 5, 80); @@ -1541,7 +1570,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) } } gb->cycles_for_line = MODE2_LENGTH + 4; - + gb->orig_n_visible_objs = gb->n_visible_objs; gb->accessed_oam_row = -1; gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 3; @@ -1576,7 +1605,6 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) fifo_clear(&gb->oam_fifo); /* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */ fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false); - gb->position_in_line = -16; gb->lcd_x = 0; /* The actual rendering cycle */ @@ -1761,7 +1789,8 @@ abort_fetching_object: GB_SLEEP(gb, display, 21, 1); } skip_slow_mode_3: - + gb->position_in_line = -16; + /* TODO: This seems incorrect (glitches Tesserae), verify further */ /* if (gb->fetcher_state == 4 || gb->fetcher_state == 5) { @@ -1881,6 +1910,10 @@ skip_slow_mode_3: gb->io_registers[GB_IO_IF] |= 2; } GB_SLEEP(gb, display, 12, 2); + if (gb->delayed_glitch_hblank_interrupt) { + gb->delayed_glitch_hblank_interrupt = false; + gb->mode_for_interrupt = 0; + } gb->ly_for_comparison = gb->current_line; GB_STAT_update(gb); GB_SLEEP(gb, display, 24, 1); diff --git a/Core/gb.h b/Core/gb.h index 16ddb1a..04d7f27 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -621,6 +621,7 @@ struct GB_gameboy_internal_s { uint8_t object_flags; }; uint8_t n_visible_objs; + uint8_t orig_n_visible_objs; uint8_t oam_search_index; uint8_t accessed_oam_row; uint8_t mode_for_interrupt; @@ -642,6 +643,7 @@ struct GB_gameboy_internal_s { uint16_t last_tile_index_address; bool cgb_repeated_a_frame; uint8_t data_for_sel_glitch; + bool delayed_glitch_hblank_interrupt; ) /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index 5485eff..1623106 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -23,6 +23,7 @@ typedef enum { GB_CONFLICT_WX, GB_CONFLICT_CGB_LCDC, GB_CONFLICT_NR10, + GB_CONFLICT_CGB_SCX, } conflict_t; /* Todo: How does double speed mode affect these? */ @@ -35,9 +36,7 @@ static const conflict_t cgb_conflict_map[0x80] = { [GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB, [GB_IO_NR10] = GB_CONFLICT_NR10, - [GB_IO_SCX] = GB_CONFLICT_WRITE_CPU, // TODO: Similar to BGP, there's some time travelling involved - - /* Todo: most values not verified, and probably differ between revisions */ + [GB_IO_SCX] = GB_CONFLICT_CGB_SCX, }; /* Todo: verify on an MGB */ @@ -291,6 +290,19 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) GB_write_memory(gb, addr, value); gb->pending_cycles = 4; break; + + case GB_CONFLICT_CGB_SCX: + if (gb->cgb_double_speed) { + GB_advance_cycles(gb, gb->pending_cycles - 2); + GB_write_memory(gb, addr, value); + gb->pending_cycles = 6; + } + else { + GB_advance_cycles(gb, gb->pending_cycles); + GB_write_memory(gb, addr, value); + gb->pending_cycles = 4; + } + break; } gb->address_bus = addr; }