From 20f83891f008279d595b2977f4683b2943abc7e2 Mon Sep 17 00:00:00 2001 From: Jonathan Campbell Date: Tue, 5 Sep 2023 16:54:07 -0700 Subject: [PATCH] VFRCRATE PITSYNC command --- CHANGELOG | 6 ++++++ src/hardware/timer.cpp | 16 ++++++++++++++++ src/hardware/vga.cpp | 12 ++++++++++++ src/hardware/vga_draw.cpp | 9 +++++++++ 4 files changed, 43 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 62bbfba018a..187bfd4cd4e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,12 @@ Next version: - Add "VRD" debugger command to force redraw of the VGA screen. - Add VGA debug set commands to force a video start address and another to clear all debug settings. + - Add "PITSYNC" option to VFRCRATE command. "VFRCRATE PITSYNC ON" + directs the system timer interrupt to synchronize tick rate with + vertical refresh rate of VGA emulation if the game or demo set + the timer tick rate to a value close enough to vertical refresh. + This is intended for games or demos that use the system timer for + a vsync interrupt. 2023.09.01 - Disable by default message confirmation after snapshot and AVI video diff --git a/src/hardware/timer.cpp b/src/hardware/timer.cpp index b40b815f274..e48bc55e977 100644 --- a/src/hardware/timer.cpp +++ b/src/hardware/timer.cpp @@ -29,6 +29,9 @@ #include "setup.h" #include "control.h" +extern bool VGA_PITsync; +extern double vga_fps; + // This is only set in PC-98 mode and only if emulating PC-9801. // There is at least one game (PC-98 port of Thexder) that depends on PC-9801 PIT 1 // behavior where the counter cycles at all times whether or not the PC speaker is @@ -308,6 +311,8 @@ static bool latched_timerstatus_locked; unsigned long PIT_TICK_RATE = PIT_TICK_RATE_IBM; +pic_tickindex_t VGA_PITSync_delay(void); + static void PIT0_Event(Bitu /*val*/) { PIC_ActivateIRQ(0); /* NTS: "Days of Thunder" leaves PIT 0 in mode 1 for some reason, which triggers once and then stops. "start" does not advance in that mode. @@ -315,6 +320,17 @@ static void PIT0_Event(Bitu /*val*/) { if (pit[0].mode == 2 || pit[0].mode == 3) { pit[0].track_time(PIC_FullIndex()); + /* If enabled option and VGA refresh rate is close to PIT 0 timer tick rate, + * make them line up so that demos that use PIT0 for vsync can run without + * shearing artifacts */ + if (VGA_PITsync) { + pic_tickindex_t vga_delay = 1000.0 / vga_fps; + if (fabs(vga_delay - pit[0].delay) < (vga_delay * 0.05)) { + PIC_AddEvent(PIT0_Event,VGA_PITSync_delay()); + return; + } + } + /* event timing error checking */ pic_tickindex_t err = PIC_GetCurrentEventTime() - pit[0].start; diff --git a/src/hardware/vga.cpp b/src/hardware/vga.cpp index e9e1809141b..2ca0556ab5d 100644 --- a/src/hardware/vga.cpp +++ b/src/hardware/vga.cpp @@ -170,6 +170,8 @@ void VGA_CaptureStartNextFrame(void); void VGA_CaptureMarkError(void); bool VGA_CaptureValidateCurrentFrame(void); +bool VGA_PITsync = false; + unsigned int vbe_window_granularity = 0; unsigned int vbe_window_size = 0; @@ -528,6 +530,13 @@ void VGA_SetCGA4Table(uint8_t val0,uint8_t val1,uint8_t val2,uint8_t val3) { } } +void SetPITSync(char *x) { + if (!strncasecmp(x,"off",3)) + VGA_PITsync = false; + else if (!strncasecmp(x,"on",3)) + VGA_PITsync = true; +} + void SetRate(char *x) { if (!strncasecmp(x,"off",3)) vga_force_refresh_rate = -1; @@ -574,11 +583,14 @@ class VFRCRATE : public Program { WriteOut(" SET rate Lock to integer frame rate, e.g. 15\n"); WriteOut(" SET rate Lock to decimal frame rate, e.g. 29.97\n"); WriteOut(" SET rate Lock to fractional frame rate, e.g. 60000/1001\n\n"); + WriteOut(" PITSYNC Make PIT timer tick at refresh rate if close enough\n\n"); WriteOut("Type VFRCRATE without a parameter to show the current status.\n"); return; } if (cmd->FindString("SET",temp_line,false)) SetRate((char *)temp_line.c_str()); + if (cmd->FindString("PITSYNC",temp_line,false)) + SetPITSync((char *)temp_line.c_str()); #if defined(USE_TTF) if (TTF_using()) PIC_AddEvent(&resetSize, 1); #endif diff --git a/src/hardware/vga_draw.cpp b/src/hardware/vga_draw.cpp index 37fcc832a6e..1e0a4ef1043 100644 --- a/src/hardware/vga_draw.cpp +++ b/src/hardware/vga_draw.cpp @@ -5428,6 +5428,15 @@ void FreeRawImage(void) { rawshot.free(); } +pic_tickindex_t VGA_PITSync_delay(void) { + pic_tickindex_t current_time = PIC_GetCurrentEventTime(); + pic_tickindex_t dt = current_time - vga.draw.delay.framestart; + pic_tickindex_t et = vga.draw.delay.vrstart - dt; + if (et < (vga.draw.delay.vtotal/2.0)) et += vga.draw.delay.vtotal; +// LOG_MSG("dt %.3f et %.3f\n",double(dt),double(et)); + return et; +} + static void VGA_VerticalTimer(Bitu /*val*/) { double current_time = PIC_GetCurrentEventTime();