Skip to content

Commit

Permalink
Improve various edge cases of window emulation, improve the emulation…
Browse files Browse the repository at this point in the history
… of the last quirk
  • Loading branch information
LIJI32 committed Jul 25, 2024
1 parent d34579e commit b1ea179
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 26 deletions.
78 changes: 53 additions & 25 deletions Core/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,9 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
gb->position_in_line = -16;
return;
}
else {
gb->line_has_fractional_scrolling = true;
}
}

/* Drop pixels for scrollings */
Expand Down Expand Up @@ -787,11 +790,27 @@ static uint8_t data_for_tile_sel_glitch(GB_gameboy_t *gb, bool *should_use, bool
return gb->data_for_sel_glitch;
}

internal void GB_update_wx_glitch(GB_gameboy_t *gb)
{
if (!GB_is_cgb(gb)) return;
if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) || !gb->wy_triggered) {
gb->cgb_wx_glitch = false;
return;
}
if (unlikely(gb->io_registers[GB_IO_WX] == 0)) {
// (gb->position_in_line + 16 <= 8) is (gb->position_in_line <= -8) in unsigned
gb->cgb_wx_glitch = ((uint8_t)(gb->position_in_line + 16) <= 8 ||
(gb->position_in_line == (uint8_t)-7 && gb->line_has_fractional_scrolling));
return;
}
gb->cgb_wx_glitch = (uint8_t)(gb->position_in_line + 7 + gb->window_is_being_fetched) == gb->io_registers[GB_IO_WX];
}

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];
GB_update_wx_glitch(gb);
uint16_t map = 0x1800;

if (!(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) {
Expand Down Expand Up @@ -841,8 +860,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];
GB_update_wx_glitch(gb);

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);
Expand Down Expand Up @@ -879,7 +898,6 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles)
}
if (!use_glitched) {
gb->current_tile_data[0] = vram_read(gb, gb->last_tile_data_address);

}
if (gb->last_tileset && gb->tile_sel_glitch) {
gb->data_for_sel_glitch = vram_read(gb, gb->last_tile_data_address);
Expand All @@ -892,8 +910,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];
GB_update_wx_glitch(gb);

uint16_t tile_address = 0;
uint8_t y = gb->model > GB_MODEL_CGB_C ? gb->fetcher_y : fetcher_y(gb);

Expand Down Expand Up @@ -955,9 +973,7 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles)
// fallthrough
default:
case GB_FETCHER_PUSH: {
if (gb->fetcher_state < 7) {
gb->fetcher_state++;
}
gb->fetcher_state = GB_FETCHER_PUSH;
if (fifo_size(&gb->bg_fifo) > 0) break;

if (unlikely(gb->wy_triggered && !(gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE) && !GB_is_cgb(gb) && !gb->disable_window_pixel_insertion_glitch)) {
Expand All @@ -977,7 +993,7 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles)

fifo_push_bg_row(&gb->bg_fifo, gb->current_tile_data[0], gb->current_tile_data[1],
gb->current_tile_attributes & 7, gb->current_tile_attributes & 0x80, gb->current_tile_attributes & 0x20);
gb->fetcher_state = 0;
gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
}
break;
}
Expand Down Expand Up @@ -1509,6 +1525,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
gb->window_y = -1;
gb->wy_triggered = false;
gb->position_in_line = -16;
gb->line_has_fractional_scrolling = false;

gb->ly_for_comparison = 0;
gb->io_registers[GB_IO_STAT] &= ~3;
Expand Down Expand Up @@ -1592,6 +1609,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
gb->delayed_glitch_hblank_interrupt = true;
}
gb->position_in_line = -16;
gb->line_has_fractional_scrolling = false;
}
}

Expand Down Expand Up @@ -1693,7 +1711,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
gb->lcd_x = 0;

/* The actual rendering cycle */
gb->fetcher_state = 0;
gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
if ((gb->mode3_batching_length = mode3_batching_length(gb))) {
GB_BATCHPOINT(gb, display, 3, gb->mode3_batching_length);
if (GB_BATCHED_CYCLES(gb, display) >= gb->mode3_batching_length) {
Expand All @@ -1710,6 +1728,15 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
goto skip_slow_mode_3;
}
}
if (!gb->wx_triggered && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) {
if (gb->io_registers[GB_IO_WX] == 0) {
fifo_clear(&gb->bg_fifo);
fifo_clear(&gb->oam_fifo);
gb->wx_triggered = true;
gb->window_y++;
gb->window_tile_x = 0;
}
}
while (true) {
/* Handle window */
/* TODO: It appears that WX checks if the window begins *next* pixel, not *this* pixel. For this reason,
Expand All @@ -1720,13 +1747,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)

if (!gb->wx_triggered && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & GB_LCDC_WIN_ENABLE)) {
bool should_activate_window = false;
if (gb->io_registers[GB_IO_WX] == 0) {
static const uint8_t scx_to_wx0_comparisons[] = {-7, -1, -2, -3, -4, -5, -6, -6};
if (gb->position_in_line == scx_to_wx0_comparisons[gb->io_registers[GB_IO_SCX] & 7]) {
should_activate_window = true;
}
}
else if (gb->io_registers[GB_IO_WX] < 166 + GB_is_cgb(gb)) {
if (gb->io_registers[GB_IO_WX] < 166 + GB_is_cgb(gb)) {
if (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
should_activate_window = true;
}
Expand All @@ -1752,7 +1773,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)
GB_SLEEP(gb, display, 42, 1);
}
gb->wx_triggered = true;
gb->fetcher_state = 0;
gb->fetcher_state = GB_FETCHER_GET_TILE_T1;
gb->window_is_being_fetched = true;
}
else if (!GB_is_cgb(gb) && gb->io_registers[GB_IO_WX] == 166 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7)) {
Expand All @@ -1762,7 +1783,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)

/* TODO: What happens when WX=0?*/
if (!GB_is_cgb(gb) && gb->wx_triggered && !gb->window_is_being_fetched &&
gb->fetcher_state == 0 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7) && gb->bg_fifo.size == 8) {
gb->fetcher_state == GB_FETCHER_GET_TILE_T1 && gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 7) && gb->bg_fifo.size == 8) {
// Insert a pixel right at the FIFO's end
gb->insert_bg_pixel = true;
}
Expand Down Expand Up @@ -1859,19 +1880,26 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force)

render_pixel_if_possible(gb);
advance_fetcher_state_machine(gb, &cycles);

if (gb->position_in_line == 160) break;

gb->cycles_for_line++;
GB_SLEEP(gb, display, 21, 1);
}
skip_slow_mode_3:
gb->position_in_line = -16;
gb->line_has_fractional_scrolling = false;


/* TODO: This seems incorrect (glitches Tesserae), verify further */
/*
if (gb->fetcher_state == 4 || gb->fetcher_state == 5) {
gb->data_for_sel_glitch = gb->current_tile_data[0];
/* TODO: Commented code seems incorrect (glitches Tesserae), verify further */

if (gb->fetcher_state == GB_FETCHER_GET_TILE_DATA_HIGH_T1 ||
gb->fetcher_state == GB_FETCHER_GET_TILE_DATA_HIGH_T2) {
// Make sure current_tile_data[1] holds the last tile data byte read
gb->current_tile_data[1] = gb->current_tile_data[0];

//gb->data_for_sel_glitch = gb->current_tile_data[0];
}
/*
else {
gb->data_for_sel_glitch = gb->current_tile_data[1];
}
Expand Down
1 change: 1 addition & 0 deletions Core/display.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ internal void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint
internal void GB_STAT_update(GB_gameboy_t *gb);
internal void GB_lcd_off(GB_gameboy_t *gb);
internal void GB_display_vblank(GB_gameboy_t *gb, GB_vblank_type_t type);
internal void GB_update_wx_glitch(GB_gameboy_t *gb);
#define GB_display_sync(gb) GB_display_run(gb, 0, true)

enum {
Expand Down
1 change: 1 addition & 0 deletions Core/gb.h
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ struct GB_gameboy_internal_s {
uint32_t frame_parity_ticks;
bool last_tileset;
bool cgb_wx_glitch;
bool line_has_fractional_scrolling;
)

GB_SECTION(accessory,
Expand Down
2 changes: 1 addition & 1 deletion Core/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +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;
GB_update_wx_glitch(gb);
case GB_IO_IF:
case GB_IO_SCX:
case GB_IO_SCY:
Expand Down

0 comments on commit b1ea179

Please sign in to comment.