Fixed and accurate emulation of object at x=0 timings

This commit is contained in:
Lior Halphon 2022-05-11 02:15:56 +03:00
parent a68f749c3a
commit 4521bb4767
2 changed files with 19 additions and 22 deletions

View File

@ -554,6 +554,13 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
const GB_fifo_item_t *oam_fifo_item = NULL; const GB_fifo_item_t *oam_fifo_item = NULL;
bool draw_oam = false; bool draw_oam = false;
bool bg_enabled = true, bg_priority = false; bool bg_enabled = true, bg_priority = false;
// Rendering (including scrolling adjustment) does not occur as long as an object at x=0 is pending
if (gb->n_visible_objs != 0 &&
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
gb->objects_x[gb->n_visible_objs - 1] == 0) {
return;
}
if (unlikely(gb->wx_triggered && !fifo_size(&gb->bg_fifo))) return; if (unlikely(gb->wx_triggered && !fifo_size(&gb->bg_fifo))) return;
@ -1304,6 +1311,13 @@ static inline uint16_t mode3_batching_length(GB_gameboy_t *gb)
return 0; return 0;
} }
static inline uint8_t x_for_object_match(GB_gameboy_t *gb)
{
uint8_t ret = gb->position_in_line + 8;
if (ret > (uint8_t)-16) return 0;
return ret;
}
/* /*
TODO: It seems that the STAT register's mode bits are always "late" by 4 T-cycles. TODO: It seems that the STAT register's mode bits are always "late" by 4 T-cycles.
The PPU logic can be greatly simplified if that delay is simply emulated. The PPU logic can be greatly simplified if that delay is simply emulated.
@ -1326,7 +1340,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
} }
return; return;
} }
GB_BATCHABLE_STATE_MACHINE(gb, display, cycles, 2, !force) { GB_BATCHABLE_STATE_MACHINE(gb, display, cycles, 2, !force) {
GB_STATE(gb, display, 1); GB_STATE(gb, display, 1);
GB_STATE(gb, display, 2); GB_STATE(gb, display, 2);
@ -1353,7 +1367,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);
@ -1562,11 +1576,8 @@ 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);
/* Todo: find out actual access time of SCX */
gb->position_in_line = -16; gb->position_in_line = -16;
gb->lcd_x = 0; gb->lcd_x = 0;
gb->extra_penalty_for_object_at_0 = MIN((gb->io_registers[GB_IO_SCX] & 7), 5);
/* The actual rendering cycle */ /* The actual rendering cycle */
gb->fetcher_state = 0; gb->fetcher_state = 0;
@ -1655,17 +1666,16 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
/* Handle objects */ /* Handle objects */
/* When the object enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB. /* When the object enabled bit is off, this proccess is skipped entirely on the DMG, but not on the CGB.
On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */ On the CGB, this bit is checked only when the pixel is actually popped from the FIFO. */
while (gb->n_visible_objs != 0 && while (gb->n_visible_objs != 0 &&
(gb->position_in_line < 160 || gb->position_in_line >= (uint8_t)(-8)) && gb->objects_x[gb->n_visible_objs - 1] < x_for_object_match(gb)) {
gb->objects_x[gb->n_visible_objs - 1] < (uint8_t)(gb->position_in_line + 8)) {
gb->n_visible_objs--; gb->n_visible_objs--;
} }
gb->during_object_fetch = true; gb->during_object_fetch = true;
while (gb->n_visible_objs != 0 && while (gb->n_visible_objs != 0 &&
(gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) && (gb->io_registers[GB_IO_LCDC] & 2 || GB_is_cgb(gb)) &&
gb->objects_x[gb->n_visible_objs - 1] == (uint8_t)(gb->position_in_line + 8)) { gb->objects_x[gb->n_visible_objs - 1] == x_for_object_match(gb)) {
while (gb->fetcher_state < 5 || fifo_size(&gb->bg_fifo) == 0) { while (gb->fetcher_state < 5 || fifo_size(&gb->bg_fifo) == 0) {
advance_fetcher_state_machine(gb, &cycles); advance_fetcher_state_machine(gb, &cycles);
@ -1676,18 +1686,6 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
} }
} }
/* Todo: Measure if penalty occurs before or after waiting for the fetcher. */
if (gb->extra_penalty_for_object_at_0 != 0) {
if (gb->objects_x[gb->n_visible_objs - 1] == 0) {
gb->cycles_for_line += gb->extra_penalty_for_object_at_0;
GB_SLEEP(gb, display, 28, gb->extra_penalty_for_object_at_0);
gb->extra_penalty_for_object_at_0 = 0;
if (gb->object_fetch_aborted) {
goto abort_fetching_object;
}
}
}
/* TODO: Can this be deleted? { */ /* TODO: Can this be deleted? { */
advance_fetcher_state_machine(gb, &cycles); advance_fetcher_state_machine(gb, &cycles);
gb->cycles_for_line++; gb->cycles_for_line++;

View File

@ -623,7 +623,6 @@ struct GB_gameboy_internal_s {
uint8_t n_visible_objs; uint8_t 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 extra_penalty_for_object_at_0;
uint8_t mode_for_interrupt; uint8_t mode_for_interrupt;
bool lyc_interrupt_line; bool lyc_interrupt_line;
bool cgb_palettes_blocked; bool cgb_palettes_blocked;