Accurate PPU access timings

This commit is contained in:
Lior Halphon 2019-01-19 19:32:26 +02:00
parent 4536581a6e
commit 73a54049d2
4 changed files with 53 additions and 17 deletions

View File

@ -1524,7 +1524,7 @@ static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
else if (gb->display_state == 7 || gb->display_state == 8) { else if (gb->display_state == 7 || gb->display_state == 8) {
GB_log(gb, "Reading OAM data (%d/40)\n", gb->display_state == 8? gb->oam_search_index : 0); GB_log(gb, "Reading OAM data (%d/40)\n", gb->display_state == 8? gb->oam_search_index : 0);
} }
else if (gb->display_state <= 3 || gb->display_state == 24) { else if (gb->display_state <= 3 || gb->display_state == 24 || gb->display_state == 31) {
GB_log(gb, "Glitched line 0 (%d cycles to next event)\n", -gb->display_cycles / 2); GB_log(gb, "Glitched line 0 (%d cycles to next event)\n", -gb->display_cycles / 2);
} }
else if (gb->mode_for_interrupt == 3) { else if (gb->mode_for_interrupt == 3) {

View File

@ -304,6 +304,7 @@ void GB_lcd_off(GB_gameboy_t *gb)
gb->vram_read_blocked = false; gb->vram_read_blocked = false;
gb->oam_write_blocked = false; gb->oam_write_blocked = false;
gb->vram_write_blocked = false; gb->vram_write_blocked = false;
gb->cgb_palettes_blocked = false;
/* Reset window rendering state */ /* Reset window rendering state */
gb->wy_diff = 0; gb->wy_diff = 0;
@ -586,6 +587,13 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
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, 32);
GB_STATE(gb, display, 33);
GB_STATE(gb, display, 34);
GB_STATE(gb, display, 35);
GB_STATE(gb, display, 36);
} }
if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) {
@ -610,23 +618,30 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->vram_read_blocked = false; gb->vram_read_blocked = false;
gb->oam_write_blocked = false; gb->oam_write_blocked = false;
gb->vram_write_blocked = false; gb->vram_write_blocked = false;
gb->cycles_for_line = MODE2_LENGTH - 2; gb->cgb_palettes_blocked = false;
gb->cycles_for_line = MODE2_LENGTH - 4;
GB_STAT_update(gb); GB_STAT_update(gb);
GB_SLEEP(gb, display, 2, MODE2_LENGTH - 2); GB_SLEEP(gb, display, 2, MODE2_LENGTH - 4);
gb->oam_write_blocked = true;
gb->cycles_for_line += 2;
GB_STAT_update(gb);
GB_SLEEP(gb, display, 34, 2);
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;
gb->mode_for_interrupt = 3; gb->mode_for_interrupt = 3;
gb->oam_read_blocked = true; gb->oam_read_blocked = true;
gb->vram_read_blocked = !GB_is_cgb(gb); gb->vram_read_blocked = !GB_is_cgb(gb) || gb->cgb_double_speed;
gb->oam_write_blocked = true; gb->vram_write_blocked = !GB_is_cgb(gb) || gb->cgb_double_speed;
gb->vram_write_blocked = !GB_is_cgb(gb); gb->cgb_palettes_blocked = !GB_is_cgb(gb);
GB_STAT_update(gb); GB_STAT_update(gb);
gb->cycles_for_line += 2; gb->cycles_for_line += 2;
GB_SLEEP(gb, display, 24, 2); GB_SLEEP(gb, display, 24, 2);
gb->vram_read_blocked = true; gb->vram_read_blocked = true;
gb->vram_write_blocked = true; gb->vram_write_blocked = true;
gb->cgb_palettes_blocked = true;
/* TODO: How does the window affect this line? */ /* TODO: How does the window affect this line? */
gb->cycles_for_line += MODE3_LENGTH + (gb->io_registers[GB_IO_SCX] & 7) - 2; gb->cycles_for_line += MODE3_LENGTH + (gb->io_registers[GB_IO_SCX] & 7) - 2;
@ -650,6 +665,10 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->oam_write_blocked = false; gb->oam_write_blocked = false;
gb->vram_write_blocked = false; gb->vram_write_blocked = false;
gb->cycles_for_line += 2;
GB_SLEEP(gb, display, 31, 2);
gb->cgb_palettes_blocked = false;
GB_STAT_update(gb); GB_STAT_update(gb);
/* Mode 0 is shorter in the very first line */ /* Mode 0 is shorter in the very first line */
GB_SLEEP(gb, display, 5, LINE_LENGTH - gb->cycles_for_line - 8); GB_SLEEP(gb, display, 5, LINE_LENGTH - gb->cycles_for_line - 8);
@ -659,9 +678,13 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
while (true) { while (true) {
/* Lines 0 - 143 */ /* Lines 0 - 143 */
for (; gb->current_line < LINES; gb->current_line++) { for (; gb->current_line < LINES; gb->current_line++) {
gb->oam_write_blocked = GB_is_cgb(gb); gb->oam_write_blocked = GB_is_cgb(gb) && !gb->cgb_double_speed;
gb->accessed_oam_row = 0; gb->accessed_oam_row = 0;
GB_SLEEP(gb, display, 6, 3);
GB_SLEEP(gb, display, 35, 2);
gb->oam_write_blocked = GB_is_cgb(gb);
GB_SLEEP(gb, display, 6, 1);
gb->io_registers[GB_IO_LY] = gb->current_line; gb->io_registers[GB_IO_LY] = gb->current_line;
gb->oam_read_blocked = true; gb->oam_read_blocked = true;
gb->ly_for_comparison = gb->current_line? -1 : 0; gb->ly_for_comparison = gb->current_line? -1 : 0;
@ -702,6 +725,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
if (gb->oam_search_index == 37) { if (gb->oam_search_index == 37) {
gb->vram_read_blocked = !GB_is_cgb(gb); gb->vram_read_blocked = !GB_is_cgb(gb);
gb->vram_write_blocked = false; gb->vram_write_blocked = false;
gb->cgb_palettes_blocked = false;
gb->oam_write_blocked = GB_is_cgb(gb); gb->oam_write_blocked = GB_is_cgb(gb);
GB_STAT_update(gb); GB_STAT_update(gb);
} }
@ -712,6 +736,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->mode_for_interrupt = 3; gb->mode_for_interrupt = 3;
gb->vram_read_blocked = true; gb->vram_read_blocked = true;
gb->vram_write_blocked = true; gb->vram_write_blocked = true;
gb->cgb_palettes_blocked = false;
gb->oam_write_blocked = true; gb->oam_write_blocked = true;
GB_STAT_update(gb); GB_STAT_update(gb);
@ -725,13 +750,18 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f; gb->fetcher_x = ((gb->io_registers[GB_IO_SCX]) / 8) & 0x1f;
gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7); gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7);
uint8_t idle_cycles = 5; uint8_t idle_cycles = 3;
if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) { if (GB_is_cgb(gb) && gb->model <= GB_MODEL_CGB_C) {
idle_cycles = 4; idle_cycles = 2;
} }
gb->cycles_for_line += idle_cycles; gb->cycles_for_line += idle_cycles;
GB_SLEEP(gb, display, 10, idle_cycles); GB_SLEEP(gb, display, 10, idle_cycles);
gb->cgb_palettes_blocked = true;
gb->cycles_for_line += 2;
GB_SLEEP(gb, display, 32, 2);
/* The actual rendering cycle */ /* The actual rendering cycle */
gb->fetcher_state = 0; gb->fetcher_state = 0;
gb->bg_fifo_paused = false; gb->bg_fifo_paused = false;
@ -848,9 +878,16 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles)
GB_STAT_update(gb); GB_STAT_update(gb);
/* Todo: Measure this value */ /* Todo: Measure this value */
gb->cycles_for_line += 2;
GB_SLEEP(gb, display, 33, 2);
gb->cgb_palettes_blocked = !gb->cgb_double_speed;
gb->cycles_for_line += 12; gb->cycles_for_line += 2;
GB_SLEEP(gb, display, 25, 12); GB_SLEEP(gb, display, 36, 2);
gb->cgb_palettes_blocked = false;
gb->cycles_for_line += 8;
GB_SLEEP(gb, display, 25, 8);
if (gb->hdma_on_hblank) { if (gb->hdma_on_hblank) {
gb->hdma_starting = true; gb->hdma_starting = true;

View File

@ -465,6 +465,7 @@ struct GB_gameboy_internal_s {
uint8_t extra_penalty_for_sprite_at_0; uint8_t extra_penalty_for_sprite_at_0;
uint8_t mode_for_interrupt; uint8_t mode_for_interrupt;
bool lyc_interrupt_line; bool lyc_interrupt_line;
bool cgb_palettes_blocked;
); );
/* 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

@ -197,7 +197,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
} }
if (addr < 0xFF00) { if (addr < 0xFF00) {
if (gb->oam_write_blocked) { if (gb->oam_write_blocked && !GB_is_cgb(gb)) {
GB_trigger_oam_bug_read(gb, addr); GB_trigger_oam_bug_read(gb, addr);
return 0xff; return 0xff;
} }
@ -357,8 +357,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
if (!gb->cgb_mode && gb->boot_rom_finished) { if (!gb->cgb_mode && gb->boot_rom_finished) {
return 0xFF; return 0xFF;
} }
/* TODO: Verify actual access timing */ if (gb->cgb_palettes_blocked) {
if (gb->vram_read_blocked) {
return 0xFF; return 0xFF;
} }
uint8_t index_reg = (addr & 0xFF) - 1; uint8_t index_reg = (addr & 0xFF) - 1;
@ -793,8 +792,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
is required. */ is required. */
return; return;
} }
/* TODO: Verify actual access timing */ if (gb->cgb_palettes_blocked) {
if (gb->vram_write_blocked) {
return; return;
} }
uint8_t index_reg = (addr & 0xFF) - 1; uint8_t index_reg = (addr & 0xFF) - 1;