mirror of
https://github.com/kanjitalk755/macemu.git
synced 2025-01-05 14:32:15 +00:00
[Charles Srstka]
Attached is a set of patches to port the precise timer that is currently used in the Linux and BeOS builds of SheepShaver to Mac OS X (and any other Mach-based operating systems). Currently, the Linux build uses the clock_gettime() function to get nanosecond-precision time, and falls back on gettimeofday() if it is not present. Unfortunately, Mac OS X does not currently support clock_gettime(), and gettimeofday() has only microsecond granularity. The Mach kernel, however, has a clock_get_time() function that does very nearly the same thing as clock_gettime(). The patches to BasiliskII cause the timing functions such as timer_current_time() to use clock_get_time() instead of gettimeofday() on Mach-based systems that do not support clock_gettime(). The changes to SheepShaver involve the precise timer. The existing code for Linux uses pthreads and real-time signals to handle the timing. Mac OS X unfortunately does not seem to support real-time signals, so Mach calls are again used to suspend and resume the timer thread in order to attempt to duplicate the Linux and BeOS versions of the timer. The code is somewhat ugly right now, as I decided to leave alone the pre-existing style of the source file, which unfortunately involves #ifdefs scattered throughout the file and some duplication of code. A future patch may want to clean this up to separate out the OS-specific code and put it all together at the top of the file. However, for the time being, this seems to work. This has not been extensively tested, because I have not been able to get my hands on a good test-case app for the classic Mac OS that would run inside the emulator and try out the timer. However, performance does seem to be better than with the pre-existing code, and nothing seems to have blown up as far as I can tell. I did find a game via a Google search - Cap'n Magneto - that is known to have problems with Basilisk/SheepShaver's legacy 60 Hz timer, and the opening fade-to-color for this game appears to run much more smoothly with the precise timer code in place.
This commit is contained in:
parent
8ac0507a39
commit
5a8dfa1b36
@ -62,6 +62,9 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__MACH__)
|
||||
#include <mach/clock.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_NATIVE_M68K
|
||||
|
||||
@ -178,6 +181,8 @@ typedef char * caddr_t;
|
||||
/* Time data type for Time Manager emulation */
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
typedef struct timespec tm_time_t;
|
||||
#elif defined(__MACH__)
|
||||
typedef mach_timespec_t tm_time_t;
|
||||
#else
|
||||
typedef struct timeval tm_time_t;
|
||||
#endif
|
||||
|
@ -32,6 +32,23 @@
|
||||
#define CLOCK_REALTIME 0
|
||||
#endif
|
||||
|
||||
#if defined(__MACH__)
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/clock.h>
|
||||
|
||||
static clock_serv_t host_clock;
|
||||
static bool host_clock_inited = false;
|
||||
|
||||
static inline void mach_current_time(tm_time_t &t) {
|
||||
if(!host_clock_inited) {
|
||||
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &host_clock);
|
||||
host_clock_inited = true;
|
||||
}
|
||||
|
||||
clock_get_time(host_clock, &t);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Return microseconds since boot (64 bit)
|
||||
@ -40,10 +57,14 @@
|
||||
void Microseconds(uint32 &hi, uint32 &lo)
|
||||
{
|
||||
D(bug("Microseconds\n"));
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME)
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
uint64 tl = (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
|
||||
#elif defined(__MACH__)
|
||||
tm_time_t t;
|
||||
mach_current_time(t);
|
||||
uint64 tl = (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
|
||||
#else
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
@ -72,6 +93,8 @@ void timer_current_time(tm_time_t &t)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
#elif defined(__MACH__)
|
||||
mach_current_time(t);
|
||||
#else
|
||||
gettimeofday(&t, NULL);
|
||||
#endif
|
||||
@ -84,7 +107,7 @@ void timer_current_time(tm_time_t &t)
|
||||
|
||||
void timer_add_time(tm_time_t &res, tm_time_t a, tm_time_t b)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(__MACH__)
|
||||
res.tv_sec = a.tv_sec + b.tv_sec;
|
||||
res.tv_nsec = a.tv_nsec + b.tv_nsec;
|
||||
if (res.tv_nsec >= 1000000000) {
|
||||
@ -108,7 +131,7 @@ void timer_add_time(tm_time_t &res, tm_time_t a, tm_time_t b)
|
||||
|
||||
void timer_sub_time(tm_time_t &res, tm_time_t a, tm_time_t b)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(__MACH__)
|
||||
res.tv_sec = a.tv_sec - b.tv_sec;
|
||||
res.tv_nsec = a.tv_nsec - b.tv_nsec;
|
||||
if (res.tv_nsec < 0) {
|
||||
@ -132,7 +155,7 @@ void timer_sub_time(tm_time_t &res, tm_time_t a, tm_time_t b)
|
||||
|
||||
int timer_cmp_time(tm_time_t a, tm_time_t b)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(__MACH__)
|
||||
if (a.tv_sec == b.tv_sec)
|
||||
return a.tv_nsec - b.tv_nsec;
|
||||
else
|
||||
@ -152,7 +175,7 @@ int timer_cmp_time(tm_time_t a, tm_time_t b)
|
||||
|
||||
void timer_mac2host_time(tm_time_t &res, int32 mactime)
|
||||
{
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(__MACH__)
|
||||
if (mactime > 0) {
|
||||
// Time in milliseconds
|
||||
res.tv_sec = mactime / 1000;
|
||||
@ -187,7 +210,7 @@ int32 timer_host2mac_time(tm_time_t hosttime)
|
||||
if (hosttime.tv_sec < 0)
|
||||
return 0;
|
||||
else {
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
#if defined(HAVE_CLOCK_GETTIME) || defined(__MACH__)
|
||||
uint64 t = (uint64)hosttime.tv_sec * 1000000 + hosttime.tv_nsec / 1000;
|
||||
#else
|
||||
uint64 t = (uint64)hosttime.tv_sec * 1000000 + hosttime.tv_usec;
|
||||
@ -210,6 +233,10 @@ uint64 GetTicks_usec(void)
|
||||
struct timespec t;
|
||||
clock_gettime(CLOCK_REALTIME, &t);
|
||||
return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
|
||||
#elif defined(__MACH__)
|
||||
tm_time_t t;
|
||||
mach_current_time(t);
|
||||
return (uint64)t.tv_sec * 1000000 + t.tv_nsec / 1000;
|
||||
#else
|
||||
struct timeval t;
|
||||
gettimeofday(&t, NULL);
|
||||
|
@ -64,6 +64,10 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __MACH__
|
||||
#include <mach/mach_types.h>
|
||||
#endif
|
||||
|
||||
// Fix offsetof() on FreeBSD and GCC >= 3.4
|
||||
#if defined(__FreeBSD__) && defined(__cplusplus)
|
||||
#undef offsetof
|
||||
@ -400,6 +404,8 @@ static inline int spin_trylock(spinlock_t *lock)
|
||||
// Time data type for Time Manager emulation
|
||||
#ifdef HAVE_CLOCK_GETTIME
|
||||
typedef struct timespec tm_time_t;
|
||||
#elif defined(__MACH__)
|
||||
typedef mach_timespec_t tm_time_t;
|
||||
#else
|
||||
typedef struct timeval tm_time_t;
|
||||
#endif
|
||||
@ -416,6 +422,9 @@ typedef struct timeval tm_time_t;
|
||||
#if defined(HAVE_PTHREADS) && defined(HAVE_CLOCK_NANOSLEEP)
|
||||
#define PRECISE_TIMING 1
|
||||
#define PRECISE_TIMING_POSIX 1
|
||||
#elif defined(HAVE_PTHREADS) && defined(__MACH__)
|
||||
#define PRECISE_TIMING 1
|
||||
#define PRECISE_TIMING_MACH 1
|
||||
#endif
|
||||
|
||||
// Timing functions
|
||||
|
@ -29,6 +29,10 @@
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
#define DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
@ -73,6 +77,15 @@ static tm_time_t wakeup_time = wakeup_time_max;
|
||||
static pthread_mutex_t wakeup_time_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static void *timer_func(void *arg);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
static clock_serv_t system_clock;
|
||||
static thread_act_t timer_thread;
|
||||
static bool timer_thread_active = false;
|
||||
static tm_time_t wakeup_time_max = { 0x7fffffff, 999999999 };
|
||||
static tm_time_t wakeup_time = wakeup_time_max;
|
||||
static semaphore_t wakeup_time_sem;
|
||||
static void *timer_func(void *arg);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -268,6 +281,13 @@ void TimerInit(void)
|
||||
wakeup_time_sem = create_sem(1, "Wakeup Time");
|
||||
timer_thread = spawn_thread(timer_func, "Time Manager", B_REAL_TIME_PRIORITY, NULL);
|
||||
resume_thread(timer_thread);
|
||||
#elif PRECISE_TIMING_MACH
|
||||
pthread_t pthread;
|
||||
|
||||
host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &system_clock);
|
||||
semaphore_create(mach_task_self(), &wakeup_time_sem, SYNC_POLICY_FIFO, 1);
|
||||
|
||||
pthread_create(&pthread, NULL, &timer_func, NULL);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_POSIX
|
||||
timer_thread_active = timer_thread_init();
|
||||
@ -293,6 +313,10 @@ void TimerExit(void)
|
||||
wait_for_thread(timer_thread, &l);
|
||||
delete_sem(wakeup_time_sem);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
timer_thread_active = false;
|
||||
semaphore_destroy(mach_task_self(), wakeup_time_sem);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_POSIX
|
||||
timer_thread_kill();
|
||||
#endif
|
||||
@ -352,6 +376,10 @@ int16 RmvTime(uint32 tm)
|
||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||
suspend_thread(timer_thread);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
semaphore_wait(wakeup_time_sem);
|
||||
thread_suspend(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_POSIX
|
||||
timer_thread_suspend();
|
||||
pthread_mutex_lock(&wakeup_time_lock);
|
||||
@ -387,6 +415,11 @@ int16 RmvTime(uint32 tm)
|
||||
get_thread_info(timer_thread, &info);
|
||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
semaphore_signal(wakeup_time_sem);
|
||||
thread_abort(timer_thread);
|
||||
thread_resume(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_POSIX
|
||||
pthread_mutex_unlock(&wakeup_time_lock);
|
||||
timer_thread_resume();
|
||||
@ -461,6 +494,10 @@ int16 PrimeTime(uint32 tm, int32 time)
|
||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||
suspend_thread(timer_thread);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
semaphore_wait(wakeup_time_sem);
|
||||
thread_suspend(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_POSIX
|
||||
timer_thread_suspend();
|
||||
pthread_mutex_lock(&wakeup_time_lock);
|
||||
@ -483,6 +520,11 @@ int16 PrimeTime(uint32 tm, int32 time)
|
||||
get_thread_info(timer_thread, &info);
|
||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
semaphore_signal(wakeup_time_sem);
|
||||
thread_abort(timer_thread);
|
||||
thread_resume(timer_thread);
|
||||
#endif
|
||||
#ifdef PRECISE_TIMING_POSIX
|
||||
pthread_mutex_unlock(&wakeup_time_lock);
|
||||
timer_thread_resume();
|
||||
@ -519,11 +561,33 @@ static int32 timer_func(void *arg)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PRECISE_TIMING_MACH
|
||||
static void *timer_func(void *arg)
|
||||
{
|
||||
timer_thread = mach_thread_self();
|
||||
timer_thread_active = true;
|
||||
|
||||
while (timer_thread_active) {
|
||||
clock_sleep(system_clock, TIME_ABSOLUTE, wakeup_time, NULL);
|
||||
semaphore_wait(wakeup_time_sem);
|
||||
|
||||
tm_time_t system_time;
|
||||
|
||||
timer_current_time(system_time);
|
||||
if (timer_cmp_time(wakeup_time, system_time) < 0) {
|
||||
wakeup_time = wakeup_time_max;
|
||||
SetInterruptFlag(INTFLAG_TIMER);
|
||||
TriggerInterrupt();
|
||||
}
|
||||
semaphore_signal(wakeup_time_sem);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PRECISE_TIMING_POSIX
|
||||
static void *timer_func(void *arg)
|
||||
{
|
||||
while (!timer_thread_cancel) {
|
||||
|
||||
// Wait until time specified by wakeup_time
|
||||
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &wakeup_time, NULL);
|
||||
|
||||
@ -583,6 +647,10 @@ void TimerInterrupt(void)
|
||||
while (acquire_sem(wakeup_time_sem) == B_INTERRUPTED) ;
|
||||
suspend_thread(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_MACH
|
||||
semaphore_wait(wakeup_time_sem);
|
||||
thread_suspend(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_POSIX
|
||||
timer_thread_suspend();
|
||||
pthread_mutex_lock(&wakeup_time_lock);
|
||||
@ -601,6 +669,11 @@ void TimerInterrupt(void)
|
||||
get_thread_info(timer_thread, &info);
|
||||
} while (info.state == B_THREAD_SUSPENDED); // Sometimes, resume_thread() doesn't work (BeOS bug?)
|
||||
#endif
|
||||
#if PRECISE_TIMING_MACH
|
||||
semaphore_signal(wakeup_time_sem);
|
||||
thread_abort(timer_thread);
|
||||
thread_resume(timer_thread);
|
||||
#endif
|
||||
#if PRECISE_TIMING_POSIX
|
||||
pthread_mutex_unlock(&wakeup_time_lock);
|
||||
timer_thread_resume();
|
||||
|
Loading…
Reference in New Issue
Block a user