From 4cf3b3c9481f83a0cfc08fb0bd2d531b5f51ca44 Mon Sep 17 00:00:00 2001 From: Lior Halphon Date: Sat, 13 Jan 2024 15:50:07 +0200 Subject: [PATCH] Accurate emulation of frame parity --- Core/display.c | 28 +++++++++++++++++++++------- Core/gb.c | 2 ++ Core/gb.h | 1 + Core/memory.c | 4 ++++ Shaders/MasterShader.fsh | 2 +- Shaders/MasterShader.metal | 2 +- 6 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Core/display.c b/Core/display.c index 9e9e8b9..e2a70d0 100644 --- a/Core/display.c +++ b/Core/display.c @@ -97,8 +97,7 @@ static void fifo_overlay_object_row(GB_fifo_t *fifo, uint8_t lower, uint8_t uppe #define WIDTH (160) #define BORDERED_WIDTH 256 #define BORDERED_HEIGHT 224 -#define FRAME_LENGTH (LCDC_PERIOD) -#define VIRTUAL_LINES (FRAME_LENGTH / LINE_LENGTH) // = 154 +#define VIRTUAL_LINES (LCDC_PERIOD / LINE_LENGTH) // = 154 typedef struct __attribute__((packed)) { uint8_t y; @@ -1380,12 +1379,28 @@ static inline uint8_t x_for_object_match(GB_gameboy_t *gb) return ret; } +static void update_frame_parity(GB_gameboy_t *gb) +{ + if (gb->model <= GB_MODEL_CGB_E) { + gb->is_odd_frame ^= true; + } + else { + // Faster than division, it's normally only once + while (gb->frame_parity_ticks > LCDC_PERIOD * 2) { + gb->frame_parity_ticks -= LCDC_PERIOD * 2; + gb->is_odd_frame ^= true; + } + } +} + /* TODO: It seems that the STAT register's mode bits are always "late" by 4 T-cycles. The PPU logic can be greatly simplified if that delay is simply emulated. */ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) { + gb->frame_parity_ticks += cycles; + if (unlikely((gb->io_registers[GB_IO_LCDC] & GB_LCDC_ENABLE) && (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); @@ -1470,13 +1485,12 @@ void GB_display_run(GB_gameboy_t *gb, unsigned cycles, bool force) if (gb->cycles_since_vblank_callback < LCDC_PERIOD) { GB_SLEEP(gb, display, 1, LCDC_PERIOD - gb->cycles_since_vblank_callback); } + update_frame_parity(gb); // TODO: test actual timing GB_display_vblank(gb, GB_VBLANK_TYPE_LCD_OFF); } return; } - - gb->is_odd_frame = false; - + if (!GB_is_cgb(gb)) { GB_SLEEP(gb, display, 23, 1); } @@ -2001,7 +2015,7 @@ skip_slow_mode_3: } else { if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) { - gb->is_odd_frame ^= true; + update_frame_parity(gb); // TODO: test actual timing GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME); } gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_RENDERED; @@ -2009,7 +2023,7 @@ skip_slow_mode_3: } else { if (!GB_is_sgb(gb) || gb->current_lcd_line < LINES) { - gb->is_odd_frame ^= true; + update_frame_parity(gb); // TODO: test actual timing GB_display_vblank(gb, GB_VBLANK_TYPE_NORMAL_FRAME); } } diff --git a/Core/gb.c b/Core/gb.c index ff29536..b2fbb45 100644 --- a/Core/gb.c +++ b/Core/gb.c @@ -1762,6 +1762,8 @@ static void GB_reset_internal(GB_gameboy_t *gb, bool quick) } GB_set_internal_div_counter(gb, 8); + /* TODO: AGS-101 is inverted in comparison to AGS-001 and AGB */ + gb->is_odd_frame = gb->model > GB_MODEL_CGB_E; #ifndef GB_DISABLE_DEBUGGER if (gb->nontrivial_jump_state) { diff --git a/Core/gb.h b/Core/gb.h index 92b33a8..3dfbbca 100644 --- a/Core/gb.h +++ b/Core/gb.h @@ -644,6 +644,7 @@ struct GB_gameboy_internal_s { bool disable_window_pixel_insertion_glitch; bool insert_bg_pixel; uint8_t cpu_vram_bus; + uint32_t frame_parity_ticks; ) GB_SECTION(accessory, diff --git a/Core/memory.c b/Core/memory.c index 4c64186..d9e5a06 100644 --- a/Core/memory.c +++ b/Core/memory.c @@ -1504,6 +1504,10 @@ static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) gb->lcd_status_callback(gb, false); } gb->double_speed_alignment = 0; + if (gb->model <= GB_MODEL_CGB_E) { + /* TODO: Verify this, it's a bit... odd */ + gb->is_odd_frame ^= true; + } GB_timing_sync(gb); GB_lcd_off(gb); } diff --git a/Shaders/MasterShader.fsh b/Shaders/MasterShader.fsh index 24bba3c..220bac7 100644 --- a/Shaders/MasterShader.fsh +++ b/Shaders/MasterShader.fsh @@ -30,7 +30,7 @@ vec4 texture_relative(sampler2D t, vec2 pos, vec2 offset) {filter} -#define BLEND_BIAS (2.0/5.0) +#define BLEND_BIAS (1.0/3.0) #define DISABLED 0 #define SIMPLE 1 diff --git a/Shaders/MasterShader.metal b/Shaders/MasterShader.metal index 7ba04dd..aaa84d0 100644 --- a/Shaders/MasterShader.metal +++ b/Shaders/MasterShader.metal @@ -50,7 +50,7 @@ __attribute__((unused)) static inline float4 texture_relative(texture2d t, #line 1 {filter} -#define BLEND_BIAS (2.0/5.0) +#define BLEND_BIAS (1.0/3.0) enum frame_blending_mode { DISABLED,