From 86c9f9d89d51761e3c33c8c1dd2ab494b0735cee Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 17 Jun 2017 22:17:58 +0300 Subject: [PATCH] =?UTF-8?q?Updated=20SameBoy=20to=20pass=20Mooneye-GB?= =?UTF-8?q?=E2=80=99s=20lcdon=5Ftiming=20test=20(on=20a=20DMG),=20as=20wel?= =?UTF-8?q?l=20as=20refined=20related=20CBG=20behaviors.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/display.c | 60 +++++++++++++++++++++++++++++++++++++++++++++----- Core/gb.h | 3 +++ Core/memory.c | 11 ++++----- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/Core/display.c b/Core/display.c index 4d9f9c0..57b7178 100755 --- a/Core/display.c +++ b/Core/display.c @@ -266,6 +266,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) gb->hdma_steps_left = 0xff; } + gb->oam_blocked = false; + gb->vram_blocked = false; + /* Keep sending vblanks to user even if the screen is off */ gb->display_cycles += cycles; if (gb->display_cycles >= LCDC_PERIOD) { @@ -281,14 +284,25 @@ 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); - /* This is correct for DMG. Is it correct for the 3 CGB modes (DMG/single/double)?*/ + /* 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? */ + uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4; + uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4; for (; cycles; cycles -= atomic_increase) { + 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) { + gb->first_scanline = false; + gb->display_cycles += atomic_increase; + } bool should_compare_ly = true; uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH; + /* Handle cycle completion. STAT's initial value depends on model and mode */ if (gb->display_cycles == LCDC_PERIOD) { /* VBlank! */ @@ -304,6 +318,10 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) } ly_for_comparison = gb->io_registers[GB_IO_LY] = 0; + /* Todo: verify timing */ + gb->oam_blocked = true; + gb->vram_blocked = false; + /* Reset window rendering state */ gb->current_window_line = 0xFF; } @@ -333,9 +351,42 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) } } + /* Handle line 0 right after turning the LCD on */ + else if (gb->first_scanline) { + /* OAM and VRAM blocking is not delayed in the very first scanline */ + if (gb->display_cycles == atomic_increase) { + gb->io_registers[GB_IO_STAT] &= ~3; + gb->oam_blocked = false; + gb->vram_blocked = false; + } + else if (gb->display_cycles == MODE2_LENGTH) { + gb->io_registers[GB_IO_STAT] &= ~3; + gb->io_registers[GB_IO_STAT] |= 3; + gb->oam_blocked = true; + gb->vram_blocked = true; + } + else if (gb->display_cycles == MODE2_LENGTH + MODE3_LENGTH) { + gb->io_registers[GB_IO_STAT] &= ~3; + gb->oam_blocked = false; + gb->vram_blocked = false; + } + } + /* Handle STAT changes for lines 0-143 */ - else if (gb->display_cycles < LINES * LINE_LENGTH ) { + else if (gb->display_cycles < LINES * LINE_LENGTH) { unsigned position_in_line = gb->display_cycles % LINE_LENGTH; + + /* Handle OAM and VRAM blocking */ + if (position_in_line == stat_delay - oam_blocking_rush || + // In case stat_delay is 0 + (position_in_line == LINE_LENGTH + stat_delay - oam_blocking_rush && gb->io_registers[GB_IO_LY] != 143)) { + gb->oam_blocked = true; + } + else if (position_in_line == MODE2_LENGTH + stat_delay - vram_blocking_rush) { + gb->vram_blocked = true; + } + + /* Handle everything else */ if (position_in_line == stat_delay) { gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 2; @@ -360,6 +411,8 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) } else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) { gb->io_registers[GB_IO_STAT] &= ~3; + gb->vram_blocked = false; + gb->oam_blocked = false; if (gb->hdma_on_hblank) { gb->hdma_on = true; gb->hdma_cycles = 0; @@ -466,9 +519,6 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) gb->stat_interrupt_line = true; } } - - if (!gb->stat_interrupt_line) { - } } if (gb->stat_interrupt_line && !previous_stat_interrupt_line) { diff --git a/Core/gb.h b/Core/gb.h index 4ee6114..6eca5f7 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -375,6 +375,9 @@ struct GB_gameboy_internal_s { GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG GB_FRAMESKIP_SECOND_FRAME_RENDERED, } frame_skip_state; + bool first_scanline; // The very first scan line after turning the LCD behaves differently. + bool oam_blocked; + bool vram_blocked; ); /* 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 18c46eb..36903df 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -60,7 +60,7 @@ static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr) static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) == 3) { + if (gb->vram_blocked) { return 0xFF; } return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000]; @@ -115,7 +115,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) } if (addr < 0xFEA0) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + if (gb->oam_blocked || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { return 0xFF; } return gb->oam[addr & 0xFF]; @@ -338,7 +338,7 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) == 3) { + if (gb->vram_blocked) { //GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr); return; } @@ -385,7 +385,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) } if (addr < 0xFEA0) { - if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2 || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { + if (gb->oam_blocked|| (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { return; } gb->oam[addr & 0xFF] = value; @@ -438,7 +438,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) 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 = gb->cgb_double_speed? -2 : -4; + gb->display_cycles = 0; + gb->first_scanline = true; if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) { gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON; }