Updated SameBoy to pass Mooneye-GB’s lcdon_timing test (on a DMG), as well as refined related CBG behaviors.
This commit is contained in:
parent
bbd2ca8ddf
commit
86c9f9d89d
@ -266,6 +266,9 @@ static void update_display_state(GB_gameboy_t *gb, uint8_t cycles)
|
|||||||
gb->hdma_steps_left = 0xff;
|
gb->hdma_steps_left = 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb->oam_blocked = false;
|
||||||
|
gb->vram_blocked = false;
|
||||||
|
|
||||||
/* Keep sending vblanks to user even if the screen is off */
|
/* Keep sending vblanks to user even if the screen is off */
|
||||||
gb->display_cycles += cycles;
|
gb->display_cycles += cycles;
|
||||||
if (gb->display_cycles >= LCDC_PERIOD) {
|
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 atomic_increase = gb->cgb_double_speed? 2 : 4;
|
||||||
uint8_t stat_delay = gb->cgb_double_speed? 2 : (gb->cgb_mode? 0 : 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);
|
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) {
|
for (; cycles; cycles -= atomic_increase) {
|
||||||
|
|
||||||
gb->display_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;
|
bool should_compare_ly = true;
|
||||||
uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH;
|
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 */
|
/* Handle cycle completion. STAT's initial value depends on model and mode */
|
||||||
if (gb->display_cycles == LCDC_PERIOD) {
|
if (gb->display_cycles == LCDC_PERIOD) {
|
||||||
/* VBlank! */
|
/* 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;
|
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 */
|
/* Reset window rendering state */
|
||||||
gb->current_window_line = 0xFF;
|
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 */
|
/* 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;
|
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) {
|
if (position_in_line == stat_delay) {
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
gb->io_registers[GB_IO_STAT] |= 2;
|
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) {
|
else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) {
|
||||||
gb->io_registers[GB_IO_STAT] &= ~3;
|
gb->io_registers[GB_IO_STAT] &= ~3;
|
||||||
|
gb->vram_blocked = false;
|
||||||
|
gb->oam_blocked = false;
|
||||||
if (gb->hdma_on_hblank) {
|
if (gb->hdma_on_hblank) {
|
||||||
gb->hdma_on = true;
|
gb->hdma_on = true;
|
||||||
gb->hdma_cycles = 0;
|
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;
|
gb->stat_interrupt_line = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gb->stat_interrupt_line) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->stat_interrupt_line && !previous_stat_interrupt_line) {
|
if (gb->stat_interrupt_line && !previous_stat_interrupt_line) {
|
||||||
|
@ -375,6 +375,9 @@ struct GB_gameboy_internal_s {
|
|||||||
GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG
|
GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG
|
||||||
GB_FRAMESKIP_SECOND_FRAME_RENDERED,
|
GB_FRAMESKIP_SECOND_FRAME_RENDERED,
|
||||||
} frame_skip_state;
|
} 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 */
|
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||||
|
@ -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)
|
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 0xFF;
|
||||||
}
|
}
|
||||||
return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000];
|
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 (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 0xFF;
|
||||||
}
|
}
|
||||||
return gb->oam[addr & 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)
|
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);
|
//GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -385,7 +385,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (addr < 0xFEA0) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
gb->oam[addr & 0xFF] = value;
|
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)) {
|
if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) {
|
||||||
/* It appears that there's a slight delay after enabling the screen? */
|
/* It appears that there's a slight delay after enabling the screen? */
|
||||||
/* Todo: verify this. */
|
/* 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) {
|
if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) {
|
||||||
gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON;
|
gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user