More accurate emulation of Hblank skip, emulation of Mode 2 skip

This commit is contained in:
Lior Halphon 2022-05-14 01:14:41 +03:00
parent 87fdf91e0c
commit bb836662dd
3 changed files with 59 additions and 12 deletions

View File

@ -1287,6 +1287,7 @@ object_buffer_pointer++\
static inline uint16_t mode3_batching_length(GB_gameboy_t *gb) 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->model & GB_MODEL_NO_SFC_BIT) return 0;
if (gb->hdma_on) return 0; if (gb->hdma_on) return 0;
if (gb->stopped) 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); unsigned first_batch = (LINE_LENGTH * 2 - gb->cycles_for_line * 2 + gb->display_cycles);
GB_display_run(gb, first_batch, force); GB_display_run(gb, first_batch, force);
cycles -= first_batch; 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_state = 9;
gb->display_cycles = 0; 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; gb->cycles_since_vblank_callback += cycles / 2;
/* The PPU does not advance while in STOP mode on the DMG */ /* 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, 24);
GB_STATE(gb, display, 26); GB_STATE(gb, display, 26);
GB_STATE(gb, display, 27); GB_STATE(gb, display, 27);
// GB_STATE(gb, display, 28); GB_STATE(gb, display, 28);
GB_STATE(gb, display, 29); GB_STATE(gb, display, 29);
GB_STATE(gb, display, 30); GB_STATE(gb, display, 30);
GB_STATE(gb, display, 31); 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->current_line = 0;
gb->window_y = -1; gb->window_y = -1;
gb->wy_triggered = false; gb->wy_triggered = false;
gb->position_in_line = -16;
gb->ly_for_comparison = 0; gb->ly_for_comparison = 0;
gb->io_registers[GB_IO_STAT] &= ~3; 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_SLEEP(gb, display, 34, 2);
gb->n_visible_objs = 0; 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->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; 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 // Mode 3 abort, state 9
display9: { display9: {
// TODO: Timing of things in this scenario is almost completely untested // 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); 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; uint32_t *dest = NULL;
if (gb->border_mode != GB_BORDER_ALWAYS) { 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->lcd_x++;
} }
} }
gb->n_visible_objs = gb->orig_n_visible_objs;
gb->current_line++; 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; 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) { while (true) {
@ -1519,6 +1547,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
gb->mode_for_interrupt = -1; gb->mode_for_interrupt = -1;
GB_STAT_update(gb); GB_STAT_update(gb);
gb->n_visible_objs = 0; gb->n_visible_objs = 0;
gb->orig_n_visible_objs = 0;
if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) { if (!GB_is_dma_active(gb) && !gb->oam_ppu_blocked) {
GB_BATCHPOINT(gb, display, 5, 80); 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->cycles_for_line = MODE2_LENGTH + 4;
gb->orig_n_visible_objs = gb->n_visible_objs;
gb->accessed_oam_row = -1; 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;
@ -1576,7 +1605,6 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
fifo_clear(&gb->oam_fifo); fifo_clear(&gb->oam_fifo);
/* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */ /* 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); fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false);
gb->position_in_line = -16;
gb->lcd_x = 0; gb->lcd_x = 0;
/* The actual rendering cycle */ /* The actual rendering cycle */
@ -1761,6 +1789,7 @@ abort_fetching_object:
GB_SLEEP(gb, display, 21, 1); GB_SLEEP(gb, display, 21, 1);
} }
skip_slow_mode_3: skip_slow_mode_3:
gb->position_in_line = -16;
/* TODO: This seems incorrect (glitches Tesserae), verify further */ /* TODO: This seems incorrect (glitches Tesserae), verify further */
/* /*
@ -1881,6 +1910,10 @@ skip_slow_mode_3:
gb->io_registers[GB_IO_IF] |= 2; gb->io_registers[GB_IO_IF] |= 2;
} }
GB_SLEEP(gb, display, 12, 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->ly_for_comparison = gb->current_line;
GB_STAT_update(gb); GB_STAT_update(gb);
GB_SLEEP(gb, display, 24, 1); GB_SLEEP(gb, display, 24, 1);

View File

@ -621,6 +621,7 @@ struct GB_gameboy_internal_s {
uint8_t object_flags; uint8_t object_flags;
}; };
uint8_t n_visible_objs; uint8_t n_visible_objs;
uint8_t orig_n_visible_objs;
uint8_t oam_search_index; uint8_t oam_search_index;
uint8_t accessed_oam_row; uint8_t accessed_oam_row;
uint8_t mode_for_interrupt; uint8_t mode_for_interrupt;
@ -642,6 +643,7 @@ struct GB_gameboy_internal_s {
uint16_t last_tile_index_address; uint16_t last_tile_index_address;
bool cgb_repeated_a_frame; bool cgb_repeated_a_frame;
uint8_t data_for_sel_glitch; 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 */ /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */

View File

@ -23,6 +23,7 @@ typedef enum {
GB_CONFLICT_WX, GB_CONFLICT_WX,
GB_CONFLICT_CGB_LCDC, GB_CONFLICT_CGB_LCDC,
GB_CONFLICT_NR10, GB_CONFLICT_NR10,
GB_CONFLICT_CGB_SCX,
} conflict_t; } conflict_t;
/* Todo: How does double speed mode affect these? */ /* 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_OBP0] = GB_CONFLICT_PALETTE_CGB,
[GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB,
[GB_IO_NR10] = GB_CONFLICT_NR10, [GB_IO_NR10] = GB_CONFLICT_NR10,
[GB_IO_SCX] = GB_CONFLICT_WRITE_CPU, // TODO: Similar to BGP, there's some time travelling involved [GB_IO_SCX] = GB_CONFLICT_CGB_SCX,
/* Todo: most values not verified, and probably differ between revisions */
}; };
/* Todo: verify on an MGB */ /* 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_write_memory(gb, addr, value);
gb->pending_cycles = 4; gb->pending_cycles = 4;
break; 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; gb->address_bus = addr;
} }