diff --git a/Core/display.c b/Core/display.c index 392cd93..8072f37 100644 --- a/Core/display.c +++ b/Core/display.c @@ -459,10 +459,10 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) uint32_t *dest = NULL; if (!gb->sgb) { if (gb->border_mode != GB_BORDER_ALWAYS) { - dest = gb->screen + gb->position_in_line + gb->current_line * WIDTH; + dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH; } else { - dest = gb->screen + gb->position_in_line + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH; + dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH; } } @@ -476,7 +476,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) } if (gb->sgb) { if (gb->current_lcd_line < LINES) { - gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; + gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; } } else if (gb->model & GB_MODEL_NO_SFC_BIT) { @@ -500,7 +500,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) } if (gb->sgb) { if (gb->current_lcd_line < LINES) { - gb->sgb->screen_buffer[gb->position_in_line + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; + gb->sgb->screen_buffer[gb->lcd_x + gb->current_lcd_line * WIDTH] = gb->stopped? 0 : pixel; } } else if (gb->model & GB_MODEL_NO_SFC_BIT) { @@ -524,6 +524,7 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) } gb->position_in_line++; + gb->lcd_x++; gb->window_is_being_fetched = false; } @@ -937,6 +938,7 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) fifo_push_bg_row(&gb->bg_fifo, 0, 0, 0, false, false); /* Todo: find out actual access time of SCX */ gb->position_in_line = - (gb->io_registers[GB_IO_SCX] & 7) - 8; + gb->lcd_x = 0; gb->fetcher_x = 0; gb->extra_penalty_for_sprite_at_0 = (gb->io_registers[GB_IO_SCX] & 7); @@ -967,10 +969,19 @@ void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) } } else 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) || - (gb->io_registers[GB_IO_WX] == (uint8_t) (gb->position_in_line + 6) && !gb->wx_just_changed)) { + 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) { + 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 */ + if ((gb->model & GB_MODEL_FAMILY_MASK) == GB_MODEL_DMG_FAMILY && !GB_is_sgb(gb)) { + if (gb->lcd_x > 0) { + gb->lcd_x--; + } + } + } } if (should_activate_window) { @@ -1096,6 +1107,21 @@ abort_fetching_object: gb->cycles_for_line++; GB_SLEEP(gb, display, 21, 1); } + + while (gb->lcd_x != 160 && !gb->disable_rendering && gb->screen && !gb->sgb) { + /* Oh no! The PPU and LCD desynced! Fill the rest of the line whith white. */ + uint32_t *dest = NULL; + if (gb->border_mode != GB_BORDER_ALWAYS) { + dest = gb->screen + gb->lcd_x + gb->current_line * WIDTH; + } + else { + dest = gb->screen + gb->lcd_x + gb->current_line * BORDERED_WIDTH + (BORDERED_WIDTH - WIDTH) / 2 + (BORDERED_HEIGHT - LINES) / 2 * BORDERED_WIDTH; + } + *dest = gb->background_palettes_rgb[0]; + gb->lcd_x++; + + } + /* TODO: Verify timing */ if (!GB_is_cgb(gb) && gb->wy_triggered && (gb->io_registers[GB_IO_LCDC] & 0x20) && gb->io_registers[GB_IO_WX] == 166) { gb->wx166_glitch = true; diff --git a/Core/gb.h b/Core/gb.h index db99608..88e2991 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -523,6 +523,7 @@ struct GB_gameboy_internal_s { uint16_t object_low_line_address; bool wy_triggered; uint8_t window_tile_x; + uint8_t lcd_x; // The LCD can go out of sync since the push signal is skipped in some cases. ); /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ diff --git a/Core/save_state.c b/Core/save_state.c index 8f10152..a53ccba 100644 --- a/Core/save_state.c +++ b/Core/save_state.c @@ -180,6 +180,32 @@ static bool verify_state_compatibility(GB_gameboy_t *gb, GB_gameboy_t *save) return true; } +static void sanitize_state(GB_gameboy_t *gb) +{ + if (gb->cartridge_type->has_rumble && gb->rumble_callback) { + gb->rumble_callback(gb, gb->rumble_state); + } + + for (unsigned i = 0; i < 32; i++) { + GB_palette_changed(gb, false, i * 2); + GB_palette_changed(gb, true, i * 2); + } + + gb->bg_fifo.read_end &= 0xF; + gb->bg_fifo.write_end &= 0xF; + gb->oam_fifo.read_end &= 0xF; + gb->oam_fifo.write_end &= 0xF; + gb->object_low_line_address &= gb->vram_size & ~1; + gb->fetcher_x &= 0x1f; + if (gb->lcd_x > gb->position_in_line) { + gb->lcd_x = gb->position_in_line; + } + + if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) { + gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X; + } +} + #define READ_SECTION(gb, f, section) read_section(f, GB_GET_SECTION(gb, section), GB_SECTION_SIZE(section)) int GB_load_state(GB_gameboy_t *gb, const char *path) @@ -252,25 +278,7 @@ int GB_load_state(GB_gameboy_t *gb, const char *path) errno = 0; - if (gb->cartridge_type->has_rumble && gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, false, i * 2); - GB_palette_changed(gb, true, i * 2); - } - - gb->bg_fifo.read_end &= 0xF; - gb->bg_fifo.write_end &= 0xF; - gb->oam_fifo.read_end &= 0xF; - gb->oam_fifo.write_end &= 0xF; - gb->object_low_line_address &= gb->vram_size & ~1; - gb->fetcher_x &= 0x1f; - - if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) { - gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X; - } + sanitize_state(gb); error: fclose(f); @@ -363,25 +371,7 @@ int GB_load_state_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t le memcpy(gb, &save, sizeof(save)); - if (gb->cartridge_type->has_rumble && gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - - for (unsigned i = 0; i < 32; i++) { - GB_palette_changed(gb, false, i * 2); - GB_palette_changed(gb, true, i * 2); - } - - gb->bg_fifo.read_end &= 0xF; - gb->bg_fifo.write_end &= 0xF; - gb->oam_fifo.read_end &= 0xF; - gb->oam_fifo.write_end &= 0xF; - gb->object_low_line_address &= gb->vram_size & ~1; - gb->fetcher_x &= 0x1f; - - if (gb->object_priority == GB_OBJECT_PRIORITY_UNDEFINED) { - gb->object_priority = gb->cgb_mode? GB_OBJECT_PRIORITY_INDEX : GB_OBJECT_PRIORITY_X; - } + sanitize_state(gb); return 0; }