From 5868a40a37602bbb3197165df660a587a301c691 Mon Sep 17 00:00:00 2001 From: cebix <> Date: Mon, 9 Jul 2001 11:22:01 +0000 Subject: [PATCH] - ADB has its own interrupt flag, INTFLAG_ADB - ADBMouseMoved(), ADBMouseDown/Up() and ADBKeyDown/Up() trigger the ADB interrupt - ADB mutex is only used for mouse movement (the only input state where it matters) - adb.cpp: toggling relative mouse mode resets mouse_x/y - PrimeTime(0) schedules a timer task with 0 delay time; this is still not the correct implementation, but it makes MacSyndicate work... - Unix: pthreads are preferred to POSIX.4 timers for 60Hz ticks because the timers drift badly under Linux and the thread can compensate for drifting well enough - Unix: moved GetTicks_usec() and Delay_usec() to timer_unix.cpp - video_x.cpp: X mouse acceleration is disabled in relative mouse mode because MacOS does its own acceleration - video_x.cpp: palette[].pixel and palette[].flags are always preset - video_x.cpp: decoupled X event handling from 60Hz video refresh cycle by using select() with a timeout on the X fd --- BasiliskII/ChangeLog | 7 +- BasiliskII/README | 7 +- BasiliskII/src/Unix/main_unix.cpp | 155 ++++++----------------------- BasiliskII/src/Unix/timer_unix.cpp | 100 +++++++++++++++++++ BasiliskII/src/Unix/video_x.cpp | 142 ++++++++++++++++---------- BasiliskII/src/adb.cpp | 27 +++-- BasiliskII/src/emul_op.cpp | 11 +- BasiliskII/src/include/main.h | 3 +- BasiliskII/src/timer.cpp | 6 +- BasiliskII/src/video.cpp | 2 + 10 files changed, 266 insertions(+), 194 deletions(-) diff --git a/BasiliskII/ChangeLog b/BasiliskII/ChangeLog index 21e9cfe1..05e7c055 100644 --- a/BasiliskII/ChangeLog +++ b/BasiliskII/ChangeLog @@ -7,10 +7,15 @@ V1.0 (snapshot) - thread-safe mouse handling - the TIME_OFFSET constant has been replaced by a (portable) function TimeToMacTime(); file dates in ExtFS should now be correct + - ADBInterrupt() is no longer called from the 60Hz interrupt but has + its own interrupt flag, potentially increasing the smoothness of + mouse movement - Unix: windowed display mode supports different resolutions and color - depth, which can be switched on-the-fly + depths, which can be switched on-the-fly - Unix: Ctrl-F5 grabs mouse in windowed mode (enhanced compatibility with games like flight simulators) + - Unix: X11 events are handled as soon as they arrive, outside of the + 60Hz video refresh raster - Unix: audio sample rate, bit depth and channel count are adjustable in the MacOS "Sound" control panel diff --git a/BasiliskII/README b/BasiliskII/README index c08d69c2..32d78ac7 100644 --- a/BasiliskII/README +++ b/BasiliskII/README @@ -616,9 +616,14 @@ Keyboard: On PC-style keyboards, "Alt" is the Mac "Command" key, while the "Windows" key is the Mac "Option" key. +Mouse: + Under Unix, press Ctrl-F5 while the Basilisk II window is active will grab + the mouse. This is needed for compatibility with some MacOS programs, + especially games. Press Ctrl-F5 again to return to normal mouse operation. + Floppy: Basilisk II can only handle 1.44MB MFM floppies. Depending on your platform, - flopyy disk changes might not be detected automatically. Under Linux, press + floppy disk changes might not be detected automatically. Under Unix, press Ctrl-F1 to mount a floppy. Under BeOS, select the appropriate "Mount" menu item or press Ctrl-F1 to mount a floppy. Under Windows, press Ctrl-Shift-F11. diff --git a/BasiliskII/src/Unix/main_unix.cpp b/BasiliskII/src/Unix/main_unix.cpp index ba9a1d90..ca25a592 100644 --- a/BasiliskII/src/Unix/main_unix.cpp +++ b/BasiliskII/src/Unix/main_unix.cpp @@ -475,7 +475,28 @@ int main(int argc, char **argv) sigaction(SIGINT, &sigint_sa, NULL); #endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) +#if defined(HAVE_PTHREADS) + + // POSIX threads available, start 60Hz thread + pthread_attr_init(&tick_thread_attr); +#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) + if (geteuid() == 0) { + pthread_attr_setinheritsched(&tick_thread_attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&tick_thread_attr, SCHED_FIFO); + struct sched_param fifo_param; + fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2; + pthread_attr_setschedparam(&tick_thread_attr, &fifo_param); + } +#endif + tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0); + if (!tick_thread_active) { + sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno)); + ErrorAlert(str); + QuitEmulator(); + } + D(bug("60Hz thread started\n")); + +#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) // POSIX.4 timers and real-time signals available, start 60Hz timer sigemptyset(&timer_sa.sa_mask); @@ -506,27 +527,6 @@ int main(int argc, char **argv) } D(bug("60Hz timer started\n")); -#elif defined(HAVE_PTHREADS) - - // POSIX threads available, start 60Hz thread - pthread_attr_init(&tick_thread_attr); -#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) - if (geteuid() == 0) { - pthread_attr_setinheritsched(&tick_thread_attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedpolicy(&tick_thread_attr, SCHED_FIFO); - struct sched_param fifo_param; - fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2; - pthread_attr_setschedparam(&tick_thread_attr, &fifo_param); - } -#endif - tick_thread_active = (pthread_create(&tick_thread, &tick_thread_attr, tick_func, NULL) == 0); - if (!tick_thread_active) { - sprintf(str, GetString(STR_TICK_THREAD_ERR), strerror(errno)); - ErrorAlert(str); - QuitEmulator(); - } - D(bug("60Hz thread started\n")); - #else // Start 60Hz timer @@ -575,10 +575,7 @@ void QuitEmulator(void) Exit680x0(); #endif -#if defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) - // Stop 60Hz timer - timer_delete(timer); -#elif defined(HAVE_PTHREADS) +#if defined(HAVE_PTHREADS) // Stop 60Hz thread if (tick_thread_active) { tick_thread_cancel = true; @@ -587,6 +584,9 @@ void QuitEmulator(void) #endif pthread_join(tick_thread, NULL); } +#elif defined(HAVE_TIMER_CREATE) && defined(_POSIX_REALTIME_SIGNALS) + // Stop 60Hz timer + timer_delete(timer); #else struct itimerval req; req.it_interval.tv_sec = req.it_value.tv_sec = 0; @@ -849,6 +849,8 @@ static void one_tick(...) #ifdef HAVE_PTHREADS static void *tick_func(void *arg) { + uint64 start = GetTicks_usec(); + int64 ticks = 0; uint64 next = GetTicks_usec(); while (!tick_thread_cancel) { one_tick(); @@ -858,110 +860,15 @@ static void *tick_func(void *arg) Delay_usec(delay); else if (delay < -16625) next = GetTicks_usec(); + ticks++; } + uint64 end = GetTicks_usec(); + D(bug("%Ld ticks in %Ld usec = %f ticks/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); return NULL; } #endif -/* - * Get current value of microsecond timer - */ - -uint64 GetTicks_usec(void) -{ -#ifdef HAVE_CLOCK_GETTIME - struct timespec t; - clock_gettime(CLOCK_REALTIME, &t); - return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000; -#else - struct timeval t; - gettimeofday(&t, NULL); - return (uint64)t.tv_sec * 1000000 + t.tv_usec; -#endif -} - - -/* - * Delay by specified number of microseconds (<1 second) - * (adapted from SDL_Delay() source; this function is designed to provide - * the highest accuracy possible) - */ - -#if defined(linux) -// Linux select() changes its timeout parameter upon return to contain -// the remaining time. Most other unixen leave it unchanged or undefined. -#define SELECT_SETS_REMAINING -#elif defined(__FreeBSD__) || defined(__sun__) -#define USE_NANOSLEEP -#elif defined(HAVE_PTHREADS) && defined(sgi) -// SGI pthreads has a bug when using pthreads+signals+nanosleep, -// so instead of using nanosleep, wait on a CV which is never signalled. -#define USE_COND_TIMEDWAIT -#endif - -void Delay_usec(uint32 usec) -{ - int was_error; - -#if defined(USE_NANOSLEEP) - struct timespec elapsed, tv; -#elif defined(USE_COND_TIMEDWAIT) - // Use a local mutex and cv, so threads remain independent - pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; - pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; - struct timespec elapsed; - uint64 future; -#else - struct timeval tv; -#ifndef SELECT_SETS_REMAINING - uint64 then, now, elapsed; -#endif -#endif - - // Set the timeout interval - Linux only needs to do this once -#if defined(SELECT_SETS_REMAINING) - tv.tv_sec = 0; - tv.tv_usec = usec; -#elif defined(USE_NANOSLEEP) - elapsed.tv_sec = 0; - elapsed.tv_nsec = usec * 1000; -#elif defined(USE_COND_TIMEDWAIT) - future = GetTicks_usec() + usec; - elapsed.tv_sec = future / 1000000; - elapsed.tv_nsec = (future % 1000000) * 1000; -#else - then = GetTicks_usec(); -#endif - - do { - errno = 0; -#if defined(USE_NANOSLEEP) - tv.tv_sec = elapsed.tv_sec; - tv.tv_nsec = elapsed.tv_nsec; - was_error = nanosleep(&tv, &elapsed); -#elif defined(USE_COND_TIMEDWAIT) - was_error = pthread_mutex_lock(&delay_mutex); - was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); - was_error = pthread_mutex_unlock(&delay_mutex); -#else -#ifndef SELECT_SETS_REMAINING - // Calculate the time interval left (in case of interrupt) - now = GetTicks_usec(); - elapsed = now - then; - then = now; - if (elapsed >= usec) - break; - usec -= elapsed; - tv.tv_sec = 0; - tv.tv_usec = usec; -#endif - was_error = select(0, NULL, NULL, NULL, &tv); -#endif - } while (was_error && (errno == EINTR)); -} - - #if !EMULATED_68K /* * Virtual 68k interrupt handler diff --git a/BasiliskII/src/Unix/timer_unix.cpp b/BasiliskII/src/Unix/timer_unix.cpp index bf3c06a8..59b0a401 100644 --- a/BasiliskII/src/Unix/timer_unix.cpp +++ b/BasiliskII/src/Unix/timer_unix.cpp @@ -22,6 +22,8 @@ #include "macos_util.h" #include "timer.h" +#include + #define DEBUG 0 #include "debug.h" @@ -196,3 +198,101 @@ int32 timer_host2mac_time(tm_time_t hosttime) return -t; // Time in negative microseconds } } + + +/* + * Get current value of microsecond timer + */ + +uint64 GetTicks_usec(void) +{ +#ifdef HAVE_CLOCK_GETTIME + struct timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000; +#else + struct timeval t; + gettimeofday(&t, NULL); + return (uint64)t.tv_sec * 1000000 + t.tv_usec; +#endif +} + + +/* + * Delay by specified number of microseconds (<1 second) + * (adapted from SDL_Delay() source; this function is designed to provide + * the highest accuracy possible) + */ + +#if defined(linux) +// Linux select() changes its timeout parameter upon return to contain +// the remaining time. Most other unixen leave it unchanged or undefined. +#define SELECT_SETS_REMAINING +#elif defined(__FreeBSD__) || defined(__sun__) +#define USE_NANOSLEEP +#elif defined(HAVE_PTHREADS) && defined(sgi) +// SGI pthreads has a bug when using pthreads+signals+nanosleep, +// so instead of using nanosleep, wait on a CV which is never signalled. +#define USE_COND_TIMEDWAIT +#endif + +void Delay_usec(uint32 usec) +{ + int was_error; + +#if defined(USE_NANOSLEEP) + struct timespec elapsed, tv; +#elif defined(USE_COND_TIMEDWAIT) + // Use a local mutex and cv, so threads remain independent + pthread_cond_t delay_cond = PTHREAD_COND_INITIALIZER; + pthread_mutex_t delay_mutex = PTHREAD_MUTEX_INITIALIZER; + struct timespec elapsed; + uint64 future; +#else + struct timeval tv; +#ifndef SELECT_SETS_REMAINING + uint64 then, now, elapsed; +#endif +#endif + + // Set the timeout interval - Linux only needs to do this once +#if defined(SELECT_SETS_REMAINING) + tv.tv_sec = 0; + tv.tv_usec = usec; +#elif defined(USE_NANOSLEEP) + elapsed.tv_sec = 0; + elapsed.tv_nsec = usec * 1000; +#elif defined(USE_COND_TIMEDWAIT) + future = GetTicks_usec() + usec; + elapsed.tv_sec = future / 1000000; + elapsed.tv_nsec = (future % 1000000) * 1000; +#else + then = GetTicks_usec(); +#endif + + do { + errno = 0; +#if defined(USE_NANOSLEEP) + tv.tv_sec = elapsed.tv_sec; + tv.tv_nsec = elapsed.tv_nsec; + was_error = nanosleep(&tv, &elapsed); +#elif defined(USE_COND_TIMEDWAIT) + was_error = pthread_mutex_lock(&delay_mutex); + was_error = pthread_cond_timedwait(&delay_cond, &delay_mutex, &elapsed); + was_error = pthread_mutex_unlock(&delay_mutex); +#else +#ifndef SELECT_SETS_REMAINING + // Calculate the time interval left (in case of interrupt) + now = GetTicks_usec(); + elapsed = now - then; + then = now; + if (elapsed >= usec) + break; + usec -= elapsed; + tv.tv_sec = 0; + tv.tv_usec = usec; +#endif + was_error = select(0, NULL, NULL, NULL, &tv); +#endif + } while (was_error && (errno == EINTR)); +} diff --git a/BasiliskII/src/Unix/video_x.cpp b/BasiliskII/src/Unix/video_x.cpp index d6b29388..04663912 100644 --- a/BasiliskII/src/Unix/video_x.cpp +++ b/BasiliskII/src/Unix/video_x.cpp @@ -80,7 +80,7 @@ enum { // Constants const char KEYCODE_FILE_NAME[] = DATADIR "/keycodes"; -static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | StructureNotifyMask; +static const int win_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | ExposureMask | StructureNotifyMask; static const int dga_eventmask = KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | StructureNotifyMask; @@ -429,12 +429,17 @@ public: virtual void toggle_mouse_grab(void) {} virtual void mouse_moved(int x, int y) { ADBMouseMoved(x, y); } + void disable_mouse_accel(void); + void restore_mouse_accel(void); + virtual void grab_mouse(void) {} virtual void ungrab_mouse(void) {} public: bool init_ok; // Initialization succeeded (we can't use exceptions because of -fomit-frame-pointer) Window w; // The window we draw into + + int orig_accel_numer, orig_accel_denom, orig_threshold; // Original mouse acceleration }; class driver_window; @@ -478,11 +483,13 @@ driver_base::driver_base() { the_buffer = NULL; the_buffer_copy = NULL; + XGetPointerControl(x_display, &orig_accel_numer, &orig_accel_denom, &orig_threshold); } driver_base::~driver_base() { ungrab_mouse(); + restore_mouse_accel(); if (w) { XUnmapWindow(x_display, w); @@ -535,6 +542,18 @@ void driver_base::update_palette(void) XSync(x_display, false); } +// Disable mouse acceleration +void driver_base::disable_mouse_accel(void) +{ + XChangePointerControl(x_display, True, False, 1, 1, 0); +} + +// Restore mouse acceleration to original value +void driver_base::restore_mouse_accel(void) +{ + XChangePointerControl(x_display, True, True, orig_accel_numer, orig_accel_denom, orig_threshold); +} + /* * Windowed display driver @@ -728,9 +747,9 @@ void driver_window::grab_mouse(void) Delay_usec(100000); } if (result == GrabSuccess) { - ADBSetRelMouseMode(mouse_grabbed = true); XStoreName(x_display, w, GetString(STR_WINDOW_TITLE_GRABBED)); - XSync(x_display, false); + ADBSetRelMouseMode(mouse_grabbed = true); + disable_mouse_accel(); } } @@ -741,6 +760,7 @@ void driver_window::ungrab_mouse(void) XUngrabPointer(x_display, CurrentTime); XStoreName(x_display, w, GetString(STR_WINDOW_TITLE)); ADBSetRelMouseMode(mouse_grabbed = false); + restore_mouse_accel(); } } @@ -836,6 +856,7 @@ void driver_dga::suspend(void) #endif XUngrabPointer(x_display, CurrentTime); XUngrabKeyboard(x_display, CurrentTime); + restore_mouse_accel(); XUnmapWindow(x_display, w); wait_unmapped(w); @@ -865,6 +886,7 @@ void driver_dga::resume(void) XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0); XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + disable_mouse_accel(); #ifdef ENABLE_XF86_DGA XF86DGADirectVideo(x_display, screen, XF86DGADirectGraphics | XF86DGADirectKeyb | XF86DGADirectMouse); XF86DGASetViewPort(x_display, screen, 0, 0); @@ -1011,6 +1033,7 @@ driver_fbdev::driver_fbdev(const video_mode &mode) XGrabPointer(x_display, w, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, w, None, CurrentTime); + disable_mouse_accel(); // Calculate bytes per row int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth); @@ -1150,6 +1173,7 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode) XWarpPointer(x_display, None, rootwin, 0, 0, 0, 0, 0, 0); XGrabKeyboard(x_display, rootwin, True, GrabModeAsync, GrabModeAsync, CurrentTime); XGrabPointer(x_display, rootwin, True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); + disable_mouse_accel(); int v_width, v_bank, v_size; XF86DGAGetVideo(x_display, screen, (char **)&the_buffer, &v_width, &v_bank, &v_size); @@ -1344,12 +1368,18 @@ static bool video_open(const video_mode &mode) --bloss; } - // Preset palette pixel values for gamma table + // Preset palette pixel values for CLUT or gamma table if (color_class == DirectColor) { int num = vis->map_entries; for (int i=0; i16/32 expand map - if (!IsDirectMode(mode) && (color_class == TrueColor || color_class == DirectColor)) + if (!IsDirectMode(mode) && xdepth > 8) for (int i=0; i<256; i++) ExpandMap[i] = map_rgb(i, i, i); #endif @@ -1722,15 +1749,12 @@ void video_set_palette(uint8 *pal, int num_in) p->red = pal[c*3 + 0] * 0x0101; p->green = pal[c*3 + 1] * 0x0101; p->blue = pal[c*3 + 2] * 0x0101; - if (color_class == PseudoColor) - p->pixel = i; - p->flags = DoRed | DoGreen | DoBlue; p++; } #ifdef ENABLE_VOSF // Recalculate pixel color expansion map - if (!IsDirectMode(VideoMonitor.mode) && (color_class == TrueColor || color_class == DirectColor)) { + if (!IsDirectMode(VideoMonitor.mode) && xdepth > 8) { for (int i=0; i<256; i++) { int c = i & (num_in-1); // If there are less than 256 colors, we repeat the first entries (this makes color expansion easier) ExpandMap[i] = map_rgb(pal[c*3+0], pal[c*3+1], pal[c*3+2]); @@ -1943,6 +1967,7 @@ static void handle_events(void) XNextEvent(x_display, &event); switch (event.type) { + // Mouse button case ButtonPress: { unsigned int button = event.xbutton.button; @@ -1975,6 +2000,12 @@ static void handle_events(void) drv->mouse_moved(event.xmotion.x, event.xmotion.y); break; + // Mouse entered window + case EnterNotify: + if (event.xcrossing.mode != NotifyGrab && event.xcrossing.mode != NotifyUngrab) + drv->mouse_moved(event.xmotion.x, event.xmotion.y); + break; + // Keyboard case KeyPress: { int code = -1; @@ -2311,12 +2342,6 @@ static void video_refresh_dga(void) { // Quit DGA mode if requested possibly_quit_dga_mode(); - - // Handle X events - handle_events(); - - // Handle palette changes - handle_palette_changes(); } #ifdef ENABLE_VOSF @@ -2326,12 +2351,6 @@ static void video_refresh_dga_vosf(void) // Quit DGA mode if requested possibly_quit_dga_mode(); - // Handle X events - handle_events(); - - // Handle palette changes - handle_palette_changes(); - // Update display (VOSF variant) static int tick_counter = 0; if (++tick_counter >= frame_skip) { @@ -2350,12 +2369,6 @@ static void video_refresh_window_vosf(void) // Ungrab mouse if requested possibly_ungrab_mouse(); - // Handle X events - handle_events(); - - // Handle palette changes - handle_palette_changes(); - // Update display (VOSF variant) static int tick_counter = 0; if (++tick_counter >= frame_skip) { @@ -2375,12 +2388,6 @@ static void video_refresh_window_static(void) // Ungrab mouse if requested possibly_ungrab_mouse(); - // Handle X events - handle_events(); - - // Handle_palette changes - handle_palette_changes(); - // Update display (static variant) static int tick_counter = 0; if (++tick_counter >= frame_skip) { @@ -2394,12 +2401,6 @@ static void video_refresh_window_dynamic(void) // Ungrab mouse if requested possibly_ungrab_mouse(); - // Handle X events - handle_events(); - - // Handle_palette changes - handle_palette_changes(); - // Update display (dynamic variant) static int tick_counter = 0; tick_counter++; @@ -2435,32 +2436,69 @@ static void VideoRefreshInit(void) } } +// This function is called on non-threaded platforms from a timer interrupt void VideoRefresh(void) { // We need to check redraw_thread_active to inhibit refreshed during // mode changes on non-threaded platforms - if (redraw_thread_active) - video_refresh(); + if (!redraw_thread_active) + return; + + // Handle X events + handle_events(); + + // Handle palette changes + handle_palette_changes(); + + // Update display + video_refresh(); } +const int VIDEO_REFRESH_HZ = 60; +const int VIDEO_REFRESH_DELAY = 1000000 / VIDEO_REFRESH_HZ; + #ifdef HAVE_PTHREADS static void *redraw_func(void *arg) { + int fd = ConnectionNumber(x_display); + uint64 start = GetTicks_usec(); int64 ticks = 0; - uint64 next = GetTicks_usec(); + uint64 next = GetTicks_usec() + VIDEO_REFRESH_DELAY; + while (!redraw_thread_cancel) { - video_refresh(); - next += 16667; + int64 delay = next - GetTicks_usec(); - if (delay > 0) - Delay_usec(delay); - else if (delay < -16667) + if (delay < -VIDEO_REFRESH_DELAY) { + + // We are lagging far behind, so we reset the delay mechanism next = GetTicks_usec(); - ticks++; + + } else if (delay <= 0) { + + // Delay expired, refresh display + handle_events(); + handle_palette_changes(); + video_refresh(); + next += VIDEO_REFRESH_DELAY; + ticks++; + + } else { + + // No display refresh pending, check for X events + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = delay; + if (select(fd+1, &readfds, NULL, NULL, &timeout) > 0) + handle_events(); + } } + uint64 end = GetTicks_usec(); - // printf("%Ld ticks in %Ld usec = %Ld ticks/sec\n", ticks, end - start, ticks * 1000000 / (end - start)); + D(bug("%Ld refreshes in %Ld usec = %f refreshes/sec\n", ticks, end - start, ticks * 1000000.0 / (end - start))); return NULL; } #endif diff --git a/BasiliskII/src/adb.cpp b/BasiliskII/src/adb.cpp index ba7ab147..915064df 100644 --- a/BasiliskII/src/adb.cpp +++ b/BasiliskII/src/adb.cpp @@ -57,7 +57,7 @@ static uint8 mouse_reg_3[2] = {0x63, 0x01}; // Mouse ADB register 3 static uint8 key_reg_2[2] = {0xff, 0xff}; // Keyboard ADB register 2 static uint8 key_reg_3[2] = {0x62, 0x05}; // Keyboard ADB register 3 -// ADB mouse input state lock (for platforms that use separate input thread) +// ADB mouse motion lock (for platforms that use separate input thread) static B2_mutex *mouse_lock; @@ -231,6 +231,8 @@ void ADBMouseMoved(int x, int y) mouse_x = x; mouse_y = y; } B2_unlock_mutex(mouse_lock); + SetInterruptFlag(INTFLAG_ADB); + TriggerInterrupt(); } @@ -240,21 +242,21 @@ void ADBMouseMoved(int x, int y) void ADBMouseDown(int button) { - B2_lock_mutex(mouse_lock); mouse_button[button] = true; - B2_unlock_mutex(mouse_lock); + SetInterruptFlag(INTFLAG_ADB); + TriggerInterrupt(); } /* - * First mouse button released + * Mouse button released */ void ADBMouseUp(int button) { - B2_lock_mutex(mouse_lock); mouse_button[button] = false; - B2_unlock_mutex(mouse_lock); + SetInterruptFlag(INTFLAG_ADB); + TriggerInterrupt(); } @@ -264,7 +266,10 @@ void ADBMouseUp(int button) void ADBSetRelMouseMode(bool relative) { - relative_mouse = relative; + if (relative_mouse != relative) { + relative_mouse = relative; + mouse_x = mouse_y = 0; + } } @@ -280,6 +285,10 @@ void ADBKeyDown(int code) // Set key in matrix key_states[code >> 3] |= (1 << (~code & 7)); + + // Trigger interrupt + SetInterruptFlag(INTFLAG_ADB); + TriggerInterrupt(); } @@ -295,6 +304,10 @@ void ADBKeyUp(int code) // Clear key in matrix key_states[code >> 3] &= ~(1 << (~code & 7)); + + // Trigger interrupt + SetInterruptFlag(INTFLAG_ADB); + TriggerInterrupt(); } diff --git a/BasiliskII/src/emul_op.cpp b/BasiliskII/src/emul_op.cpp index cb5603f0..31830a07 100644 --- a/BasiliskII/src/emul_op.cpp +++ b/BasiliskII/src/emul_op.cpp @@ -440,7 +440,6 @@ void EmulOp(uint16 opcode, M68kRegisters *r) if (HasMacStarted()) { // Mac has started, execute all 60Hz interrupt functions - ADBInterrupt(); TimerInterrupt(); VideoInterrupt(); @@ -457,7 +456,6 @@ void EmulOp(uint16 opcode, M68kRegisters *r) if (InterruptFlags & INTFLAG_1HZ) { ClearInterruptFlag(INTFLAG_1HZ); - if (HasMacStarted()) { SonyInterrupt(); DiskInterrupt(); @@ -480,11 +478,16 @@ void EmulOp(uint16 opcode, M68kRegisters *r) AudioInterrupt(); } + if (InterruptFlags & INTFLAG_ADB) { + ClearInterruptFlag(INTFLAG_ADB); + if (HasMacStarted()) + ADBInterrupt(); + } + if (InterruptFlags & INTFLAG_NMI) { ClearInterruptFlag(INTFLAG_NMI); - if (HasMacStarted()) { + if (HasMacStarted()) TriggerNMI(); - } } break; diff --git a/BasiliskII/src/include/main.h b/BasiliskII/src/include/main.h index 93df6638..d58791ed 100644 --- a/BasiliskII/src/include/main.h +++ b/BasiliskII/src/include/main.h @@ -66,7 +66,8 @@ enum { INTFLAG_ETHER = 8, // Ethernet driver INTFLAG_AUDIO = 16, // Audio block read INTFLAG_TIMER = 32, // Time Manager - INTFLAG_NMI = 64 // NMI + INTFLAG_ADB = 64, // ADB + INTFLAG_NMI = 128 // NMI }; extern uint32 InterruptFlags; // Currently pending interrupts diff --git a/BasiliskII/src/timer.cpp b/BasiliskII/src/timer.cpp index f787a33f..f88a1c61 100644 --- a/BasiliskII/src/timer.cpp +++ b/BasiliskII/src/timer.cpp @@ -249,10 +249,8 @@ int16 PrimeTime(uint32 tm, int32 time) //!! PrimeTime(0) means continue previous delay // (save wakeup time in RmvTime?) - if (time == 0) { - printf("FATAL: Unsupported PrimeTime(0)\n"); - return 0; - } + if (time == 0) + printf("WARNING: Unsupported PrimeTime(0)\n"); // Yes, calculate wakeup time relative to last scheduled time tm_time_t wakeup; diff --git a/BasiliskII/src/video.cpp b/BasiliskII/src/video.cpp index 97a31c63..d60baac3 100644 --- a/BasiliskII/src/video.cpp +++ b/BasiliskII/src/video.cpp @@ -23,6 +23,8 @@ * SEE ALSO * Inside Macintosh: Devices, chapter 1 "Device Manager" * Designing Cards and Drivers for the Macintosh Family, Second Edition + * Designing PCI Cards and Drivers for Power Macintosh Computers + * Display Device Driver Guide */ #include