From a68f749c3a778f94c310547d69cb51d3a3d4eed9 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Mon, 9 May 2022 01:01:24 +0300 Subject: [PATCH] Initial emulation of "SCX banging" to prolong mode 3 --- Core/display.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++--- Core/memory.c | 1 + 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Core/display.c b/Core/display.c index bb59ff8..a5ebcb4 100644 --- a/Core/display.c +++ b/Core/display.c @@ -433,6 +433,7 @@ void GB_STAT_update(GB_gameboy_t *gb) void GB_lcd_off(GB_gameboy_t *gb) { + gb->cycles_for_line = 0; gb->display_state = 0; gb->display_cycles = 0; /* When the LCD is disabled, state is constant */ @@ -566,6 +567,17 @@ static void render_pixel_if_possible(GB_gameboy_t *gb) bg_priority |= oam_fifo_item->bg_priority; } } + + // (gb->position_in_line + 16 < 8) is (gb->position_in_line < -8) in unsigned logic + if (((uint8_t)(gb->position_in_line + 16) < 8)) { + if ((gb->position_in_line & 7) == (gb->io_registers[GB_IO_SCX] & 7)) { + gb->position_in_line = -8; + } + else if (gb->position_in_line == (uint8_t) -9) { + gb->position_in_line = -16; + return; + } + } /* Drop pixels for scrollings */ if (gb->position_in_line >= 160 || (gb->disable_rendering && !gb->sgb)) { @@ -771,6 +783,9 @@ static void advance_fetcher_state_machine(GB_gameboy_t *gb, unsigned *cycles) if (gb->wx_triggered) { x = gb->window_tile_x; } + else if ((uint8_t)(gb->position_in_line + 16) < 8) { + x = gb->io_registers[GB_IO_SCX] >> 3; + } else { /* TODO: There is some CGB timing error around here. Adjusting SCX by 7 or less shouldn't have an effect on a CGB, @@ -1295,6 +1310,13 @@ static inline uint16_t mode3_batching_length(GB_gameboy_t *gb) */ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) { + if (unlikely((gb->io_registers[GB_IO_LCDC] & 0x80) && (signed)(gb->cycles_for_line * 2 + cycles + gb->display_cycles) > LINE_LENGTH * 2)) { + unsigned first_batch = (LINE_LENGTH * 2 - gb->cycles_for_line * 2 + gb->display_cycles); + GB_display_run(gb, first_batch, force); + cycles -= first_batch; + gb->display_state = 9; + gb->display_cycles = 0; + } gb->cycles_since_vblank_callback += cycles / 2; /* The PPU does not advance while in STOP mode on the DMG */ @@ -1314,7 +1336,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) GB_STATE(gb, display, 6); GB_STATE(gb, display, 7); GB_STATE(gb, display, 8); - // GB_STATE(gb, display, 9); + GB_STATE(gb, display, 9); GB_STATE(gb, display, 10); GB_STATE(gb, display, 11); GB_STATE(gb, display, 12); @@ -1346,6 +1368,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) GB_STATE(gb, display, 40); GB_STATE(gb, display, 41); GB_STATE(gb, display, 42); + GB_STATE(gb, display, 43); } if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { @@ -1414,7 +1437,33 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) gb->wx_triggered = false; gb->wx166_glitch = false; goto mode_3_start; - + + // Mode 3 abort, state 9 + display9: { + // TODO: Timing of things in this scenario is almost completely untested + if (gb->current_line < 144 && !GB_is_sgb(gb) && !gb->disable_rendering) { + GB_log(gb, "The ROM is preventing line %d from fully rendering, this could damage a real device's LCD display.\n", gb->current_line); + 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; + } + uint32_t color = GB_is_cgb(gb)? GB_convert_rgb15(gb, 0x7FFF, false) : gb->background_palettes_rgb[4]; + while (gb->lcd_x < 160) { + *(dest++) = color; + gb->lcd_x++; + } + } + gb->current_line++; + gb->vram_read_blocked = false; + gb->vram_write_blocked = false; + gb->wx_triggered = false; + gb->wx166_glitch = false; + gb->cycles_for_line = 0; + } + while (true) { /* Lines 0 - 143 */ gb->window_y = -1; @@ -1514,7 +1563,7 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) /* Fill the FIFO with 8 pixels of "junk", it's going to be dropped anyway. */ 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->position_in_line = -16; gb->lcd_x = 0; gb->extra_penalty_for_object_at_0 = MIN((gb->io_registers[GB_IO_SCX] & 7), 5); @@ -1785,7 +1834,17 @@ skip_slow_mode_3: GB_SLEEP(gb, display, 36, 2); gb->cgb_palettes_blocked = false; - GB_SLEEP(gb, display, 11, LINE_LENGTH - gb->cycles_for_line - 2); + if (gb->cycles_for_line > LINE_LENGTH - 2) { + gb->cycles_for_line = 0; + GB_SLEEP(gb, display, 43, LINE_LENGTH - gb->cycles_for_line); + goto display9; + } + + { + uint16_t cycles_for_line = gb->cycles_for_line; + gb->cycles_for_line = 0; + GB_SLEEP(gb, display, 11, LINE_LENGTH - cycles_for_line - 2); + } /* TODO: Verify double speed timing TODO: Timing differs on a DMG @@ -1794,6 +1853,7 @@ skip_slow_mode_3: (gb->io_registers[GB_IO_WY] == gb->current_line)) { gb->wy_triggered = true; } + gb->cycles_for_line = 0; GB_SLEEP(gb, display, 31, 2); if (gb->current_line != LINES - 1) { gb->mode_for_interrupt = 2; diff --git a/Core/memory.c b/Core/memory.c index 9d6d452..9c9048b 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -1432,6 +1432,7 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->display_cycles = 0; gb->display_state = 0; gb->double_speed_alignment = 0; + gb->cycles_for_line = 0; if (GB_is_sgb(gb)) { gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; }