From 9e7670c922aeea098133028af417b676ab9c8b41 Mon Sep 17 00:00:00 2001 From: gbeauche <> Date: Thu, 22 May 2003 22:12:05 +0000 Subject: [PATCH] Import VOSF from Basilisk II for faster and more accurate video refresh. There may be some bugs left though. Rework sigsegv_handler() a little to accomodate VOSF way of life. TODO: merge video drivers infrastructure from B2. --- SheepShaver/Makefile | 3 +- SheepShaver/src/Unix/Makefile.in | 3 +- SheepShaver/src/Unix/configure.in | 15 ++ SheepShaver/src/Unix/main_unix.cpp | 51 ++--- SheepShaver/src/Unix/sysdeps.h | 3 + SheepShaver/src/Unix/user_strings_unix.cpp | 1 + SheepShaver/src/Unix/user_strings_unix.h | 3 +- SheepShaver/src/Unix/video_x.cpp | 240 +++++++++++++++++++-- 8 files changed, 264 insertions(+), 55 deletions(-) diff --git a/SheepShaver/Makefile b/SheepShaver/Makefile index 97075a3c..2495704b 100644 --- a/SheepShaver/Makefile +++ b/SheepShaver/Makefile @@ -58,7 +58,8 @@ links: Unix/sshpty.h Unix/sshpty.c Unix/strlcpy.h Unix/strlcpy.c \ Unix/sys_unix.cpp Unix/timer_unix.cpp Unix/xpram_unix.cpp \ Unix/sigsegv.h Unix/sigsegv.cpp Unix/vm_alloc.h Unix/vm_alloc.cpp \ - Unix/posix_sem.cpp \ + Unix/posix_sem.cpp Unix/video_vosf.h Unix/video_blit.h \ + Unix/video_blit.cpp \ Unix/Linux/scsi_linux.cpp Unix/Linux/NetDriver'; \ PREFIX="`pwd`/"; case $(B2_TOPDIR) in /*) PREFIX="";; esac; \ for i in $$list; do \ diff --git a/SheepShaver/src/Unix/Makefile.in b/SheepShaver/src/Unix/Makefile.in index c0cdd308..8ed7a9d4 100644 --- a/SheepShaver/src/Unix/Makefile.in +++ b/SheepShaver/src/Unix/Makefile.in @@ -29,7 +29,8 @@ SRCS = main_unix.cpp ../prefs.cpp ../prefs_items.cpp prefs_unix.cpp sys_unix.cpp ../rom_patches.cpp ../rsrc_patches.cpp ../emul_op.cpp ../name_registry.cpp \ ../macos_util.cpp ../timer.cpp timer_unix.cpp ../xpram.cpp xpram_unix.cpp \ ../adb.cpp clip_unix.cpp ../sony.cpp ../disk.cpp ../cdrom.cpp ../scsi.cpp \ - Linux/scsi_linux.cpp ../video.cpp video_x.cpp ../audio.cpp audio_oss_esd.cpp ../ether.cpp \ + Linux/scsi_linux.cpp ../video.cpp video_blit.cpp video_x.cpp \ + ../audio.cpp audio_oss_esd.cpp ../ether.cpp \ Linux/ether_linux.cpp ../serial.cpp serial_unix.cpp ../extfs.cpp extfs_unix.cpp \ about_window_unix.cpp ../user_strings.cpp user_strings_unix.cpp \ vm_alloc.cpp sigsegv.cpp \ diff --git a/SheepShaver/src/Unix/configure.in b/SheepShaver/src/Unix/configure.in index b99f0e9b..e11fa11e 100644 --- a/SheepShaver/src/Unix/configure.in +++ b/SheepShaver/src/Unix/configure.in @@ -8,6 +8,7 @@ AC_CONFIG_HEADER(config.h) dnl Options. AC_ARG_ENABLE(xf86-dga, [ --enable-xf86-dga use the XFree86 DGA extension [default=yes]], [WANT_XF86_DGA=$enableval], [WANT_XF86_DGA=yes]) AC_ARG_ENABLE(xf86-vidmode, [ --enable-xf86-vidmode use the XFree86 VidMode extension [default=yes]], [WANT_XF86_VIDMODE=$enableval], [WANT_XF86_VIDMODE=yes]) +AC_ARG_ENABLE(vosf, [ --enable-vosf enable video on SEGV signals [default=yes]], [WANT_VOSF=$enableval], [WANT_VOSF=yes]) AC_ARG_WITH(esd, [ --with-esd support ESD for sound under Linux/FreeBSD [default=yes]], [WANT_ESD=$withval], [WANT_ESD=yes]) AC_ARG_WITH(gtk, [ --with-gtk use GTK user interface [default=yes]], [WANT_GTK=$withval], [WANT_GTK=yes]) AC_ARG_WITH(mon, [ --with-mon use mon as debugger [default=yes]], [WANT_MON=$withval], [WANT_MON=yes]) @@ -477,6 +478,19 @@ AC_CACHE_CHECK([whether we can skip instruction in SIGSEGV handler], AC_TRANSLATE_DEFINE(HAVE_SIGSEGV_SKIP_INSTRUCTION, "$ac_cv_have_skip_instruction", [Define if we can ignore the fault (instruction skipping in SIGSEGV handler).]) +dnl Can we do Video on SEGV Signals ? +CAN_VOSF=no +if [[ "$ac_cv_have_extended_signals" = "yes" -o "$ac_cv_have_sigcontext_hack" = "yes" ]]; then + CAN_VOSF=yes +fi + +dnl Enable VOSF screen updates with this feature is requested and feasible +if [[ "x$WANT_VOSF" = "xyes" -a "x$CAN_VOSF" = "xyes" ]]; then + AC_DEFINE(ENABLE_VOSF, 1, [Define if using video enabled on SEGV signals.]) +else + WANT_VOSF=no +fi + dnl Generate Makefile. AC_SUBST(SYSSRCS) AC_OUTPUT(Makefile) @@ -487,6 +501,7 @@ echo SheepShaver configuration summary: echo echo XFree86 DGA support .............. : $WANT_XF86_DGA echo XFree86 VidMode support .......... : $WANT_XF86_VIDMODE +echo Enable video on SEGV signals ..... : $WANT_VOSF echo ESD sound support ................ : $WANT_ESD echo GTK user interface ............... : $WANT_GTK echo mon debugger support ............. : $WANT_MON diff --git a/SheepShaver/src/Unix/main_unix.cpp b/SheepShaver/src/Unix/main_unix.cpp index ea1b3c63..f4b13cc5 100644 --- a/SheepShaver/src/Unix/main_unix.cpp +++ b/SheepShaver/src/Unix/main_unix.cpp @@ -108,6 +108,7 @@ #include "rom_patches.h" #include "user_strings.h" #include "vm_alloc.h" +#include "sigsegv.h" #define DEBUG 0 #include "debug.h" @@ -1282,21 +1283,23 @@ static void sigusr2_handler(int sig, sigcontext_struct *sc) static void sigsegv_handler(int sig, sigcontext_struct *sc) { pt_regs *r = sc->regs; + + // Get effective address + uint32 addr = r->dar; + +#if ENABLE_VOSF + // Handle screen fault. + extern bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction); + if (Screen_fault_handler((sigsegv_address_t)addr, (sigsegv_address_t)r->nip)) + return; +#endif + num_segv++; // Fault in Mac ROM or RAM? bool mac_fault = (r->nip >= ROM_BASE) && (r->nip < (ROM_BASE + ROM_AREA_SIZE)) || (r->nip >= RAMBase) && (r->nip < (RAMBase + RAMSize)); if (mac_fault) { - // Get opcode and divide into fields - uint32 opcode = *((uint32 *)r->nip); - uint32 primop = opcode >> 26; - uint32 exop = (opcode >> 1) & 0x3ff; - uint32 ra = (opcode >> 16) & 0x1f; - uint32 rb = (opcode >> 11) & 0x1f; - uint32 rd = (opcode >> 21) & 0x1f; - int32 imm = (int16)(opcode & 0xffff); - // "VM settings" during MacOS 8 installation if (r->nip == ROM_BASE + 0x488160 && r->gpr[20] == 0xf8000000) { r->nip += 4; @@ -1324,6 +1327,15 @@ static void sigsegv_handler(int sig, sigcontext_struct *sc) return; } + // Get opcode and divide into fields + uint32 opcode = *((uint32 *)r->nip); + uint32 primop = opcode >> 26; + uint32 exop = (opcode >> 1) & 0x3ff; + uint32 ra = (opcode >> 16) & 0x1f; + uint32 rb = (opcode >> 11) & 0x1f; + uint32 rd = (opcode >> 21) & 0x1f; + int32 imm = (int16)(opcode & 0xffff); + // Analyze opcode enum { TYPE_UNKNOWN, @@ -1407,27 +1419,6 @@ static void sigsegv_handler(int sig, sigcontext_struct *sc) transfer_type = TYPE_STORE; transfer_size = SIZE_HALFWORD; addr_mode = MODE_U; break; } - // Calculate effective address - uint32 addr = 0; - switch (addr_mode) { - case MODE_X: - case MODE_UX: - if (ra == 0) - addr = r->gpr[rb]; - else - addr = r->gpr[ra] + r->gpr[rb]; - break; - case MODE_NORM: - case MODE_U: - if (ra == 0) - addr = (int32)(int16)imm; - else - addr = r->gpr[ra] + (int32)(int16)imm; - break; - default: - break; - } - // Ignore ROM writes if (transfer_type == TYPE_STORE && addr >= ROM_BASE && addr < ROM_BASE + ROM_SIZE) { // D(bug("WARNING: %s write access to ROM at %08lx, pc %08lx\n", transfer_size == SIZE_BYTE ? "Byte" : transfer_size == SIZE_HALFWORD ? "Halfword" : "Word", addr, r->nip)); diff --git a/SheepShaver/src/Unix/sysdeps.h b/SheepShaver/src/Unix/sysdeps.h index c1d10fac..798593f0 100644 --- a/SheepShaver/src/Unix/sysdeps.h +++ b/SheepShaver/src/Unix/sysdeps.h @@ -59,6 +59,9 @@ # endif #endif +// Mac and host address space are the same +#define REAL_ADDRESSING 1 + // Are we using a PPC emulator or the real thing? #ifdef __powerpc__ #define EMULATED_PPC 0 diff --git a/SheepShaver/src/Unix/user_strings_unix.cpp b/SheepShaver/src/Unix/user_strings_unix.cpp index b6011d2f..660b4847 100644 --- a/SheepShaver/src/Unix/user_strings_unix.cpp +++ b/SheepShaver/src/Unix/user_strings_unix.cpp @@ -62,6 +62,7 @@ user_string_def platform_strings[] = { {STR_HELP_MENU_GTK, "/_Help"}, {STR_HELP_ITEM_ABOUT_GTK, "/Help/_About SheepShaver"}, {STR_SUSPEND_WINDOW_TITLE, "SheepShaver suspended. Press Space to reactivate."}, + {STR_VOSF_INIT_ERR, "Cannot initialize Video on SEGV signals."}, {-1, NULL} // End marker }; diff --git a/SheepShaver/src/Unix/user_strings_unix.h b/SheepShaver/src/Unix/user_strings_unix.h index 73196a18..9e8d1864 100644 --- a/SheepShaver/src/Unix/user_strings_unix.h +++ b/SheepShaver/src/Unix/user_strings_unix.h @@ -52,7 +52,8 @@ enum { STR_PREFS_ITEM_QUIT_GTK, STR_HELP_MENU_GTK, STR_HELP_ITEM_ABOUT_GTK, - STR_SUSPEND_WINDOW_TITLE + STR_SUSPEND_WINDOW_TITLE, + STR_VOSF_INIT_ERR }; #endif diff --git a/SheepShaver/src/Unix/video_x.cpp b/SheepShaver/src/Unix/video_x.cpp index 4935326e..79a239e5 100644 --- a/SheepShaver/src/Unix/video_x.cpp +++ b/SheepShaver/src/Unix/video_x.cpp @@ -58,6 +58,12 @@ static volatile bool thread_stop_ack = false; // Acknowledge for thread_stop_req static bool has_dga = false; // Flag: Video DGA capable static bool has_vidmode = false; // Flag: VidMode extension available +#ifdef ENABLE_VOSF +static bool use_vosf = true; // Flag: VOSF enabled +#else +static const bool use_vosf = false; // VOSF not possible +#endif + static bool palette_changed = false; // Flag: Palette changed, redraw thread must update palette static bool ctrl_down = false; // Flag: Ctrl key pressed static bool quit_full_screen = false; // Flag: DGA close requested from redraw thread @@ -92,8 +98,9 @@ static Cursor mac_cursor; static GC cursor_gc, cursor_mask_gc; static bool cursor_changed = false; // Flag: Cursor changed, window_func must update cursor static bool have_shm = false; // Flag: SHM present and usable -static uint8 *the_buffer; // Pointer to Mac frame buffer +static uint8 *the_buffer = NULL; // Pointer to Mac frame buffer static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer +static uint32 the_buffer_size; // Size of allocated the_buffer // Variables for DGA mode static char *dga_screen_base; @@ -118,6 +125,12 @@ extern Display *x_display; extern void SysMountFirstFloppy(void); +// Video acceleration through SIGSEGV +#ifdef ENABLE_VOSF +# include "video_vosf.h" +#endif + + /* * Open display (window or fullscreen) */ @@ -138,6 +151,9 @@ static int error_handler(Display *d, XErrorEvent *e) // Open window static bool open_window(int width, int height) { + int aligned_width = (width + 15) & ~15; + int aligned_height = (height + 15) & ~15; + // Set absolute mouse mode ADBSetRelMouseMode(false); @@ -186,9 +202,8 @@ static bool open_window(int width, int height) // Create SHM image ("height + 2" for safety) img = XShmCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, &shminfo, width, height); shminfo.shmid = shmget(IPC_PRIVATE, (height + 2) * img->bytes_per_line, IPC_CREAT | 0777); - screen_base = (uint32)shmat(shminfo.shmid, 0, 0); - the_buffer = (uint8 *)screen_base; - shminfo.shmaddr = img->data = (char *)screen_base; + the_buffer_copy = (uint8 *)shmat(shminfo.shmid, 0, 0); + shminfo.shmaddr = img->data = (char *)the_buffer_copy; shminfo.readOnly = False; // Try to attach SHM image, catching errors @@ -209,7 +224,7 @@ static bool open_window(int width, int height) // Create normal X image if SHM doesn't work ("height + 2" for safety) if (!have_shm) { - int bytes_per_row = width; + int bytes_per_row = aligned_width; switch (depth) { case 1: bytes_per_row /= 8; @@ -223,9 +238,8 @@ static bool open_window(int width, int height) bytes_per_row *= 4; break; } - screen_base = (uint32)malloc((height + 2) * bytes_per_row); - the_buffer = (uint8 *)screen_base; - img = XCreateImage(x_display, vis, depth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)screen_base, width, height, 32, bytes_per_row); + the_buffer_copy = (uint8 *)malloc((aligned_height + 2) * bytes_per_row); + img = XCreateImage(x_display, vis, depth == 1 ? 1 : xdepth, depth == 1 ? XYBitmap : ZPixmap, 0, (char *)the_buffer_copy, aligned_width, aligned_height, 32, bytes_per_row); } // 1-Bit mode is big-endian @@ -234,8 +248,20 @@ static bool open_window(int width, int height) img->bitmap_bit_order = MSBFirst; } - // Allocate memory for frame buffer copy - the_buffer_copy = (uint8 *)malloc((height + 2) * img->bytes_per_line); +#ifdef ENABLE_VOSF + use_vosf = true; + // Allocate memory for frame buffer (SIZE is extended to page-boundary) + the_host_buffer = the_buffer_copy; + the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line); + the_buffer = (uint8 *)vm_acquire(the_buffer_size); + the_buffer_copy = (uint8 *)malloc(the_buffer_size); + D(bug("the_buffer = %p, the_buffer_copy = %p, the_host_buffer = %p\n", the_buffer, the_buffer_copy, the_host_buffer)); +#else + // Allocate memory for frame buffer + the_buffer = (uint8 *)malloc((aligned_height + 2) * img->bytes_per_line); + D(bug("the_buffer = %p, the_buffer_copy = %p\n", the_buffer, the_buffer_copy)); +#endif + screen_base = (uint32)the_buffer; // Create GC the_gc = XCreateGC(x_display, the_win, 0, 0); @@ -255,6 +281,17 @@ static bool open_window(int width, int height) mac_cursor = XCreatePixmapCursor(x_display, cursor_map, cursor_mask_map, &black, &white, 0, 0); cursor_changed = false; + // Init blitting routines + bool native_byte_order; +#ifdef WORDS_BIGENDIAN + native_byte_order = (XImageByteOrder(x_display) == MSBFirst); +#else + native_byte_order = (XImageByteOrder(x_display) == LSBFirst); +#endif +#ifdef ENABLE_VOSF + Screen_blitter_init(&visualInfo, native_byte_order, depth); +#endif + // Set bytes per row VModes[cur_mode].viRowBytes = img->bytes_per_line; XSync(x_display, false); @@ -289,7 +326,6 @@ static bool open_dga(int width, int height) XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse); XF86DGASetViewPort(x_display, screen, 0, 0); XF86DGASetVidPage(x_display, screen, 0); - screen_base = (uint32)dga_screen_base; // Set colormap if (depth == 8) @@ -307,6 +343,33 @@ static bool open_dga(int width, int height) bytes_per_row *= 4; break; } + +#if ENABLE_VOSF + bool native_byte_order; +#ifdef WORDS_BIGENDIAN + native_byte_order = (XImageByteOrder(x_display) == MSBFirst); +#else + native_byte_order = (XImageByteOrder(x_display) == LSBFirst); +#endif +#if REAL_ADDRESSING || DIRECT_ADDRESSING + // Screen_blitter_init() returns TRUE if VOSF is mandatory + // i.e. the framebuffer update function is not Blit_Copy_Raw + use_vosf = Screen_blitter_init(&visualInfo, native_byte_order, depth); + + if (use_vosf) { + // Allocate memory for frame buffer (SIZE is extended to page-boundary) + the_host_buffer = the_buffer; + the_buffer_size = page_extend((height + 2) * bytes_per_row); + the_buffer_copy = (uint8 *)malloc(the_buffer_size); + the_buffer = (uint8 *)vm_acquire(the_buffer_size); + } +#else + use_vosf = false; + the_buffer = dga_screen_base; +#endif +#endif + screen_base = (uint32)the_buffer; + VModes[cur_mode].viRowBytes = bytes_per_row; XSync(x_display, false); return true; @@ -339,12 +402,24 @@ static bool open_display(void) depth = 32; break; } + + bool display_open = false; if (display_type == DIS_SCREEN) - return open_dga(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize); + display_open = open_dga(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize); else if (display_type == DIS_WINDOW) - return open_window(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize); - else - return false; + display_open = open_window(VModes[cur_mode].viXsize, VModes[cur_mode].viYsize); + +#ifdef ENABLE_VOSF + if (use_vosf) { + // Initialize the VOSF system + if (!video_vosf_init()) { + ErrorAlert(GetString(STR_VOSF_INIT_ERR)); + return false; + } + } +#endif + + return display_open; } @@ -355,14 +430,28 @@ static bool open_display(void) // Close window static void close_window(void) { + if (have_shm) { + XShmDetach(x_display, &shminfo); +#ifdef ENABLE_VOSF + the_host_buffer = NULL; // don't free() in driver_base dtor +#else + the_buffer_copy = NULL; // don't free() in driver_base dtor +#endif + } + if (img) { + if (!have_shm) + img->data = NULL; + XDestroyImage(img); + } + if (have_shm) { + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + } + if (the_gc) + XFreeGC(x_display, the_gc); + // Close window XDestroyWindow(x_display, the_win); - - // Close frame buffer copy - if (the_buffer_copy) { - free(the_buffer_copy); - the_buffer_copy = NULL; - } } // Close DGA mode @@ -378,6 +467,17 @@ static void close_dga(void) if (has_vidmode) XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]); #endif + + if (!use_vosf) { + // don't free() the screen buffer in driver_base dtor + the_buffer = NULL; + } +#ifdef ENABLE_VOSF + else { + // don't free() the screen buffer in driver_base dtor + the_host_buffer = NULL; + } +#endif } static void close_display(void) @@ -386,6 +486,41 @@ static void close_display(void) close_dga(); else if (display_type == DIS_WINDOW) close_window(); + +#ifdef ENABLE_VOSF + if (use_vosf) { + // Deinitialize VOSF + video_vosf_exit(); + } +#endif + + // Free frame buffer(s) + if (!use_vosf) { + if (the_buffer_copy) { + free(the_buffer_copy); + the_buffer_copy = NULL; + } + } +#ifdef ENABLE_VOSF + else { + // the_buffer shall always be mapped through vm_acquire() so that we can vm_protect() it at will + if (the_buffer != VM_MAP_FAILED) { + D(bug(" releasing the_buffer at %p (%d bytes)\n", the_buffer, the_buffer_size)); + vm_release(the_buffer, the_buffer_size); + the_buffer = NULL; + } + if (the_host_buffer) { + D(bug(" freeing the_host_buffer at %p\n", the_host_buffer)); + free(the_host_buffer); + the_host_buffer = NULL; + } + if (the_buffer_copy) { + D(bug(" freeing the_buffer_copy at %p\n", the_buffer_copy)); + free(the_buffer_copy); + the_buffer_copy = NULL; + } + } +#endif } @@ -456,6 +591,12 @@ static bool has_mode(int x, int y) bool VideoInit(void) { +#ifdef ENABLE_VOSF + // Zero the mainBuffer structure + mainBuffer.dirtyPages = NULL; + mainBuffer.pageInfo = NULL; +#endif + // Init variables private_data = NULL; cur_mode = 0; // Window 640x480 @@ -652,6 +793,13 @@ void VideoExit(void) redraw_thread_active = false; } +#ifdef ENABLE_VOSF + if (use_vosf) { + // Deinitialize VOSF + video_vosf_exit(); + } +#endif + // Close window and server connection if (x_display != NULL) { XSync(x_display, false); @@ -729,8 +877,24 @@ static void resume_emul(void) XF86DGASetViewPort(x_display, screen, 0, 0); XSync(x_display, false); + // the_buffer already contains the data to restore. i.e. since a temporary + // frame buffer is used when VOSF is actually used, fb_save is therefore + // not necessary. +#ifdef ENABLE_VOSF + if (use_vosf) { + LOCK_VOSF; + PFLAG_SET_ALL; + UNLOCK_VOSF; + memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize); + } +#endif + // Restore frame buffer if (fb_save) { +#ifdef ENABLE_VOSF + // Don't copy fb_save to the temporary frame buffer in VOSF mode + if (!use_vosf) +#endif memcpy((void *)screen_base, fb_save, VModes[cur_mode].viYsize * VModes[cur_mode].viRowBytes); free(fb_save); fb_save = NULL; @@ -979,6 +1143,13 @@ static void handle_events(void) // Hidden parts exposed, force complete refresh case Expose: +#ifdef ENABLE_VOSF + if (use_vosf) { // VOSF refresh + LOCK_VOSF; + PFLAG_SET_ALL; + UNLOCK_VOSF; + } +#endif memset(the_buffer_copy, 0, VModes[cur_mode].viRowBytes * VModes[cur_mode].viYsize); break; } @@ -1376,7 +1547,18 @@ static void *redraw_func(void *arg) tick_counter = 0; // Update display - update_display(); +#ifdef ENABLE_VOSF + if (use_vosf) { + if (mainBuffer.dirty) { + LOCK_VOSF; + update_display_window_vosf(); + UNLOCK_VOSF; + XSync(x_display, false); // Let the server catch up + } + } + else +#endif + update_display(); // Set new cursor image if it was changed if (cursor_changed) { @@ -1391,6 +1573,20 @@ static void *redraw_func(void *arg) } } } +#ifdef ENABLE_VOSF + else if (use_vosf) { + // Update display (VOSF variant) + static int tick_counter = 0; + if (++tick_counter >= frame_skip) { + tick_counter = 0; + if (mainBuffer.dirty) { + LOCK_VOSF; + update_display_dga_vosf(); + UNLOCK_VOSF; + } + } + } +#endif // Set new palette if it was changed if (palette_changed && !emul_suspended) {