From 855ffb490af34b5407443d769be8129685f343c3 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Fri, 25 May 2018 23:42:36 +0300 Subject: [PATCH] =?UTF-8?q?A=20HBlank=20interrupt=20cannot=20occur=20in=20?= =?UTF-8?q?the=20last=20M-cycle=20of=20HBlank.=20Correct=20emulation=20of?= =?UTF-8?q?=20STAT=20access=20conflicts=20on=20the=20CGB=20(Test:=20CPU-E,?= =?UTF-8?q?=20single=20speed=20only).=20Fixes=20a=20minor=20graphical=20gl?= =?UTF-8?q?itch=20in=20Pok=C3=A9mon=20Puzzle=20Challenge.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Core/display.c | 13 +++++++++---- Core/gb.h | 2 +- Core/memory.c | 3 ++- Core/z80_cpu.c | 14 +++++++++++++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/Core/display.c b/Core/display.c index c82a2f0..3ebf48d 100644 --- a/Core/display.c +++ b/Core/display.c @@ -84,7 +84,7 @@ static void fifo_overlay_object_row(GB_fifo_t *fifo, uint8_t lower, uint8_t uppe /* - Each line is 456 cycles. Without scrolling, sprites or a window:: + Each line is 456 cycles. Without scrolling, sprites or a window: Mode 2 - 80 cycles / OAM Transfer Mode 3 - 172 cycles / Rendering Mode 0 - 204 cycles / HBlank @@ -260,7 +260,7 @@ void GB_STAT_update(GB_gameboy_t *gb) } switch (gb->io_registers[GB_IO_STAT] & 3) { - case 0: gb->stat_interrupt_line = (gb->io_registers[GB_IO_STAT] & 8) && !gb->is_first_line_mode2; break; + case 0: gb->stat_interrupt_line = (gb->io_registers[GB_IO_STAT] & 8) && !gb->mode_0_interrupt_disable; break; case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break; /* The OAM interrupt is handled differently, it reads the writable flags from STAT less frequenctly, and is not based on the mode bits of STAT. */ @@ -582,8 +582,9 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) GB_SLEEP(gb, display, 23, 1); } + /* Todo: Merge this with the normal line routine */ /* Handle the very first line 0 */ - gb->is_first_line_mode2 = true; + gb->mode_0_interrupt_disable = true; gb->current_line = 0; gb->ly_for_comparison = 0; gb->io_registers[GB_IO_STAT] &= ~3; @@ -601,7 +602,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->vram_read_blocked = !gb->is_cgb; gb->oam_write_blocked = true; gb->vram_write_blocked = !gb->is_cgb; - gb->is_first_line_mode2 = false; + gb->mode_0_interrupt_disable = false; GB_STAT_update(gb); gb->cycles_for_line += 2; @@ -632,6 +633,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) /* Mode 0 is shorter in the very first line */ GB_SLEEP(gb, display, 5, LINE_LENGTH - gb->cycles_for_line - 8); + gb->mode_0_interrupt_disable = true; gb->current_line = 1; while (true) { /* Lines 0 - 143 */ @@ -656,6 +658,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) GB_SLEEP(gb, display, 7, 1); + gb->mode_0_interrupt_disable = false; gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 2; gb->oam_write_blocked = true; @@ -820,6 +823,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->hdma_starting = true; } GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line); + gb->mode_0_interrupt_disable = true; } /* Lines 144 - 152 */ @@ -842,6 +846,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) gb->io_registers[GB_IO_STAT] &= ~3; gb->io_registers[GB_IO_STAT] |= 1; gb->io_registers[GB_IO_IF] |= 1; + gb->mode_0_interrupt_disable = false; trigger_oam_interrupt(gb); GB_STAT_update(gb); gb->oam_interrupt_line = false; diff --git a/Core/gb.h b/Core/gb.h index be2c68b..cce9120 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -422,7 +422,7 @@ struct GB_gameboy_internal_s { uint8_t oam_search_index; uint8_t accessed_oam_row; uint8_t extra_penalty_for_sprite_at_0; - bool is_first_line_mode2; + bool mode_0_interrupt_disable; bool oam_interrupt_line; bool lyc_interrupt_line; ); diff --git a/Core/memory.c b/Core/memory.c index 1fbd331..254417c 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -665,7 +665,8 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) case GB_IO_STAT: /* A DMG bug: http://www.devrs.com/gb/files/faqs.html#GBBugs */ - if (!gb->is_cgb && !gb->stat_interrupt_line && !gb->is_first_line_mode2 && + /* TODO: Confirm gb->mode_0_interrupt_disable usage */ + if (!gb->is_cgb && !gb->stat_interrupt_line && !gb->mode_0_interrupt_disable && (gb->io_registers[GB_IO_STAT] & 0x3) < 2 && (gb->io_registers[GB_IO_LCDC] & 0x80)) { gb->io_registers[GB_IO_IF] |= 2; } diff --git a/Core/z80_cpu.c b/Core/z80_cpu.c index 6f17917..5119ff7 100644 --- a/Core/z80_cpu.c +++ b/Core/z80_cpu.c @@ -15,12 +15,15 @@ typedef enum { GB_CONFLICT_READ_OR, /* If the CPU and another component write at the same time, the CPU's value "wins" */ GB_CONFLICT_WRITE_CPU, + /* Register specific values */ + GB_CONFLICT_STAT_CGB, } GB_conflict_t; /* Todo: How does double speed mode affect these? */ static const GB_conflict_t cgb_conflict_map[0x80] = { [GB_IO_IF] = GB_CONFLICT_WRITE_CPU, [GB_IO_LYC] = GB_CONFLICT_WRITE_CPU, + [GB_IO_STAT] = GB_CONFLICT_STAT_CGB, /* Todo: most values not verified, and probably differ between revisions */ }; @@ -92,14 +95,23 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) GB_write_memory(gb, addr, value); gb->pending_cycles = 5; return; + } case GB_CONFLICT_WRITE_CPU: GB_advance_cycles(gb, gb->pending_cycles + 1); GB_write_memory(gb, addr, value); gb->pending_cycles = 3; return; + + case GB_CONFLICT_STAT_CGB: { + /* The LYC bit behaves differently */ + uint8_t old_value = GB_read_memory(gb, addr); + GB_advance_cycles(gb, gb->pending_cycles); + GB_write_memory(gb, addr, (old_value & 0x40) | (value & ~0x40)); + GB_advance_cycles(gb, 1); + GB_write_memory(gb, addr, value); + gb->pending_cycles = 3; } - } }