diff --git a/Core/display.c b/Core/display.c index 416dc8212..a27aca684 100644 --- a/Core/display.c +++ b/Core/display.c @@ -791,6 +791,7 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) { switch ((fetcher_step_t)gb->fetcher_state) { case GB_FETCHER_GET_TILE_T1: { + gb->cgb_wx_glitch = GB_is_cgb(gb) && (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == gb->io_registers[GB_IO_WX]; uint16_t map = 0x1800; if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) { @@ -824,6 +825,10 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) gb->fetcher_state++; break; case GB_FETCHER_GET_TILE_T2: { + if (gb->cgb_wx_glitch) { + gb->fetcher_state++; + break; + } dma_sync(gb, cycles); gb->current_tile = vram_read(gb, gb->last_tile_index_address); if (GB_is_cgb(gb)) { @@ -836,6 +841,8 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) break; case GB_FETCHER_GET_TILE_DATA_LOWER_T1: { + gb->cgb_wx_glitch = GB_is_cgb(gb) && (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == gb->io_registers[GB_IO_WX]; + uint8_t y_flip = 0; uint16_t tile_address = 0; uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb); @@ -859,6 +866,11 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) break; case GB_FETCHER_GET_TILE_DATA_LOWER_T2: { + if (gb->cgb_wx_glitch) { + gb->current_tile_data[0] = gb->current_tile_data[1]; + gb->fetcher_state++; + break; + } dma_sync(gb, cycles); bool use_glitched = false; bool cgb_d_glitch = false; @@ -880,6 +892,8 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) break; case GB_FETCHER_GET_TILE_DATA_HIGH_T1: { + gb->cgb_wx_glitch = GB_is_cgb(gb) && (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == gb->io_registers[GB_IO_WX]; + uint16_t tile_address = 0; uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb); @@ -903,6 +917,15 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) break; case GB_FETCHER_GET_TILE_DATA_HIGH_T2: { + if (gb->cgb_wx_glitch) { + gb->current_tile_data[1] = gb->current_tile_data[0]; + gb->fetcher_state++; + if (gb->wx_triggered) { + gb->window_tile_x++; + gb->window_tile_x &= 0x1F; + } + break; + } dma_sync(gb, cycles); bool use_glitched = false; bool cgb_d_glitch = false; @@ -1707,7 +1730,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) { should_activate_window = true; } - else if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 6) && !gb->wx_just_changed) { + else if (!GB_is_cgb(gb) && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 6) && !gb->wx_just_changed) { should_activate_window = true; /* LCD-PPU horizontal desync! It only appears to happen on DMGs, but not all of them. This doesn't seem to be CPU revision dependent, but most revisions */ diff --git a/Core/gb.h b/Core/gb.h index d32caf86b..7655c690d 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -648,6 +648,7 @@ struct GB_gameboy_internal_s { uint8_t cpu_vram_bus; uint32_t frame_parity_ticks; bool last_tileset; + bool cgb_wx_glitch; ) GB_SECTION(accessory, diff --git a/Core/memory.c b/Core/memory.c index 8a1a0432f..607267593 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -1399,6 +1399,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->wy_triggered = true; } case GB_IO_WX: + gb->cgb_wx_glitch = GB_is_cgb(gb) && (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == value; case GB_IO_IF: case GB_IO_SCX: case GB_IO_SCY: diff --git a/Core/sm83_cpu.c b/Core/sm83_cpu.c index fa5f63235..44638a28c 100644 --- a/Core/sm83_cpu.c +++ b/Core/sm83_cpu.c @@ -20,7 +20,7 @@ typedef enum { GB_CONFLICT_PALETTE_CGB, GB_CONFLICT_DMG_LCDC, GB_CONFLICT_SGB_LCDC, - GB_CONFLICT_WX, + GB_CONFLICT_WX_DMG, GB_CONFLICT_LCDC_CGB, GB_CONFLICT_LCDC_CGB_DOUBLE, GB_CONFLICT_STAT_CGB_DOUBLE, @@ -37,6 +37,7 @@ static const conflict_t cgb_conflict_map[0x80] = { [GB_IO_OBP0] = GB_CONFLICT_PALETTE_CGB, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_CGB, [GB_IO_SCX] = GB_CONFLICT_READ_OLD, + [GB_IO_WX] = GB_CONFLICT_WRITE_CPU, }; static const conflict_t cgb_double_conflict_map[0x80] = { @@ -46,6 +47,7 @@ static const conflict_t cgb_double_conflict_map[0x80] = { [GB_IO_STAT] = GB_CONFLICT_STAT_CGB_DOUBLE, [GB_IO_NR10] = GB_CONFLICT_NR10_CGB_DOUBLE, [GB_IO_SCX] = GB_CONFLICT_SCX_CGB_DOUBLE, + [GB_IO_WX] = GB_CONFLICT_READ_OLD, }; /* Todo: verify on an MGB */ @@ -60,7 +62,7 @@ static const conflict_t dmg_conflict_map[0x80] = { [GB_IO_OBP0] = GB_CONFLICT_PALETTE_DMG, [GB_IO_OBP1] = GB_CONFLICT_PALETTE_DMG, [GB_IO_WY] = GB_CONFLICT_READ_OLD, - [GB_IO_WX] = GB_CONFLICT_WX, + [GB_IO_WX] = GB_CONFLICT_WX_DMG, /* Todo: these were not verified at all */ [GB_IO_SCX] = GB_CONFLICT_READ_NEW, @@ -78,7 +80,7 @@ static const conflict_t sgb_conflict_map[0x80] = { [GB_IO_OBP0] = GB_CONFLICT_READ_NEW, [GB_IO_OBP1] = GB_CONFLICT_READ_NEW, [GB_IO_WY] = GB_CONFLICT_READ_OLD, - [GB_IO_WX] = GB_CONFLICT_WX, + [GB_IO_WX] = GB_CONFLICT_WX_DMG, /* Todo: these were not verified at all */ [GB_IO_SCX] = GB_CONFLICT_READ_NEW, @@ -258,7 +260,7 @@ static void cycle_write(GB_gameboy_t *gb, uint16_t addr, uint8_t value) break; } - case GB_CONFLICT_WX: + case GB_CONFLICT_WX_DMG: GB_advance_cycles(gb, gb->pending_cycles); GB_write_memory(gb, addr, value); gb->wx_just_changed = true;