summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2018-10-26 22:03:11 -0700
committerH. Peter Anvin <hpa@zytor.com>2018-10-26 22:03:11 -0700
commitbca4b46d14516121d11afa109b7b5b6f86c165f1 (patch)
tree8dd1133a8aca9b82ab26e016c5ee439a70905ed6
parent04ec87e861b1463c4d4888200c48c901e99c1dd2 (diff)
downloadabc80sim-use_timer.tar.gz
abc80sim-use_timer.tar.xz
abc80sim-use_timer.zip
Use an asynchronous timer instead of polling nstime()use_timer
-rw-r--r--clock.c81
-rw-r--r--clock.h19
-rw-r--r--compiler.h3
-rw-r--r--configure.ac9
-rw-r--r--nstime.c117
-rw-r--r--nstime.h1
-rw-r--r--z80.c3
-rw-r--r--z80.h1
8 files changed, 162 insertions, 72 deletions
diff --git a/clock.c b/clock.c
index 80defd0..b7a6b2b 100644
--- a/clock.c
+++ b/clock.c
@@ -35,24 +35,17 @@ static struct abctimer *create_timer(uint64_t period, void (*func)(void))
t->period = period;
t->func = func;
- t->last = nstime();
+ t->last = 0 /* nstime() */;
t->ltst = TSTATE;
+ timer_ctr++; /* Force a timer poll */
return t;
}
-static unsigned int poll_tstate_period;
-#define MAX_TSTATE_PERIOD 512
-
void timer_init(void)
{
nstime_init();
- /* Limit polling to once every μs simulated time */
- poll_tstate_period = 1000*ns_per_tstate;
- if (!limit_speed || poll_tstate_period > MAX_TSTATE_PERIOD)
- poll_tstate_period = MAX_TSTATE_PERIOD;
-
switch (model) {
case MODEL_ABC80:
/* 20 ms = 50 Hz */
@@ -68,32 +61,26 @@ void timer_init(void)
}
}
-static inline bool trigger(uint64_t now, struct abctimer *tmr)
-{
- /* This expression: a) will overflow safely, b) will never trigger for 0 */
- if (likely((now - tmr->last) <= (tmr->period - 1)))
- return false;
-
- tmr->last += tmr->period;
- if (unlikely((now - tmr->last) >= tmr->period)) {
- /* Missed tick(s), advance clock to skip missed */
- tmr->last = now - ((now - tmr->last) % tmr->period);
- }
-
- return true;
-}
+volatile unsigned int timer_ctr;
+unsigned int timer_ctr_last;
+uint64_t timer_poll_tstate = ~UINT64_C(0);
/* See if it is time to slow down a bit */
-static void consider_napping(uint64_t now, uint64_t next)
+static void consider_napping(uint64_t next, uint64_t now)
{
uint64_t when;
int64_t ahead, behind;
static uint64_t ref_time, ref_tstate;
+ if (!limit_speed)
+ return;
+
if (unlikely(now <= ref_time || TSTATE <= ref_tstate))
goto weird;
+ timer_poll_tstate = ref_tstate + (next - now) * tstate_per_ns;
when = ref_time + (TSTATE - ref_tstate) * ns_per_tstate;
+
behind = now - when;
ahead = when - next;
@@ -101,41 +88,38 @@ static void consider_napping(uint64_t now, uint64_t next)
if (unlikely(behind >= MS(250) || ahead >= MS(100)))
goto weird;
- /* If we are ahead of the next event, hold off and wait for it */
+ /*
+ * If we are ahead of the next event, hold off and wait for it
+ * This should be interrrupted by a signal, but just in case put
+ * a reasonable limit on it...
+ */
if (ahead >= 0)
- mynssleep(next, now);
+ mynssleep(next + MS(10), now);
return;
weird:
/* We fell too far behind, got suspended, or the clock jumped... */
ref_time = now;
- ref_tstate = TSTATE;
+ timer_poll_tstate = ref_tstate + (next - now) * tstate_per_ns;
}
-/*
- * Even on systems where it is highly optimized, nstime() can take
- * quite a while to run. Therefore, only check wall time after
- * a certain number of simulated T-states.
- */
-#define CHECK_FREQUENCY 64
-
/* Poll for timers - these the only external event we look for */
-void z80_poll_external(void)
+void do_timer_poll(void)
{
uint64_t now;
- static uint64_t next = 0;
+ static uint64_t next = 0, last = 0;
+ static uint64_t timer_set = 0;
int i;
- bool sleepy = limit_speed;
- static uint64_t next_check_tstate;
- if (likely(TSTATE < next_check_tstate))
- return;
-
- next_check_tstate = TSTATE + poll_tstate_period;
+ while (1) {
+ now = nstime();
+ if (unlikely(now < last))
+ next = now;
+ last = now;
- now = nstime();
+ if (now < next)
+ break;
- if (unlikely(now >= next)) {
next = UINT64_MAX;
for (i = 0; i < ntimers; i++) {
@@ -152,7 +136,6 @@ void z80_poll_external(void)
/* TSTATE value corresponding to t->last */
t->ltst = TSTATE - (now - t->last)*tstate_per_ns;
t->func();
- sleepy = false; /* Just in case "now" is too far behind now */
}
if (next > tnext)
next = tnext; /* The next event is closer than you thought */
@@ -160,8 +143,12 @@ void z80_poll_external(void)
}
}
- if (sleepy)
- consider_napping(now, next);
+ /* Trigger a poll for the next event */
+ if (next != timer_set)
+ wakeusup(timer_set = next, now);
+
+ /* Moderate speed */
+ consider_napping(next, now);
}
/*
diff --git a/clock.h b/clock.h
index b143c6f..b56791a 100644
--- a/clock.h
+++ b/clock.h
@@ -1,12 +1,29 @@
#ifndef CLOCK_H
#define CLOCK_H
+#include "z80.h"
+
extern void timer_init(void);
-extern void timer_poll(void);
extern void vsync_screen(void);
extern double ns_per_tstate;
extern double tstate_per_ns;
extern bool limit_speed;
+extern void do_timer_poll(void);
+extern volatile unsigned int timer_ctr;
+extern unsigned int timer_ctr_last;
+extern uint64_t timer_poll_tstate;
+
+static inline void z80_poll_external(void)
+{
+ unsigned int timer_ctr_now = timer_ctr;
+ if (likely(timer_ctr_now == timer_ctr_last) &&
+ likely(TSTATE < timer_poll_tstate))
+ return;
+
+ timer_ctr_last = timer_ctr_now;
+ do_timer_poll();
+}
+
#endif /* CLOCK_H */
diff --git a/compiler.h b/compiler.h
index 9507a5d..d9409fd 100644
--- a/compiler.h
+++ b/compiler.h
@@ -43,6 +43,9 @@
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#endif
diff --git a/configure.ac b/configure.ac
index d3a0f58..5dc35f2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -70,10 +70,11 @@ AS_IF(["$SDL_CONFIG" --cflags >/dev/null 2>&1],
AC_CHECK_HEADERS(SDL.h)
AC_CHECK_HEADERS(png.h)
+PA_ADD_HEADERS([time.h sys/time.h])
AC_CHECK_HEADERS(dirent.h)
AC_CHECK_HEADERS(sys/types.h)
AC_CHECK_HEADERS(sys/stat.h)
-AC_CHECK_HEADERS(sys/time.h)
+AC_CHECK_HEADERS(sys/select.h)
AC_CHECK_HEADERS(unistd.h)
AC_CHECK_HEADERS(fcntl.h)
AC_CHECK_HEADERS(paths.h)
@@ -92,6 +93,9 @@ AC_CHECK_TYPES([struct stat])
AC_CHECK_FUNCS([stat _stat64])
AC_CHECK_FUNCS([fstat _fstat64])
+dnl Time types
+AC_CHECK_TYPES([struct timespec, struct timeval])
+
dnl Newer versions of POSIX have this
AC_CHECK_TYPES(mode_t)
@@ -110,6 +114,8 @@ AC_CHECK_FUNCS(clock_gettime)
AC_CHECK_FUNCS(clock_nanosleep)
AC_CHECK_FUNCS(nanosleep)
AC_CHECK_FUNCS(gettimeofday)
+AC_CHECK_FUNCS(pselect)
+AC_CHECK_FUNCS(select)
dnl Console handling
AC_CHECK_FUNCS(setsid)
@@ -119,6 +125,7 @@ dnl Checks for library functions.
AC_SEARCH_LIBS([deflate], [z])
AC_SEARCH_LIBS([png_write_png], [png])
AC_SEARCH_LIBS([clock_gettime], [rt])
+AC_SEARCH_LIBS([timer_settime], [rt])
dnl
dnl Check for supported gcc attributes; some compilers (e.g. Sun CC)
diff --git a/nstime.c b/nstime.c
index 06d7428..604637b 100644
--- a/nstime.c
+++ b/nstime.c
@@ -7,14 +7,61 @@
#include "compiler.h"
#include "nstime.h"
+#include "clock.h"
+
+#ifdef _POSIX_TIMERS
+
+#include <signal.h>
+
+#if defined(HAVE_STRUCT_TIMESPEC) || defined(HAVE_STRUCT_TIMEVAL)
+
+static time_t tv_sec_zero;
+
+/* The base value for the from/to values */
+#define ABS tv_sec_zero
+#define REL 0
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
#endif
-#include <SDL.h>
+#ifdef HAVE_STRUCT_TIMESPEC
-#ifdef _POSIX_TIMERS
+static inline uint64_t fromtimespec(struct timespec ts, time_t base)
+{
+ return ((ts.tv_sec - base) * UINT64_C(1000000000)) + ts.tv_nsec;
+}
+
+static inline struct timespec totimespec(uint64_t t, time_t base)
+{
+ struct timespec ts;
+
+ ts.tv_sec = t / UINT64_C(1000000000) + base;
+ ts.tv_nsec = t % UINT64_C(1000000000);
+
+ return ts;
+}
+
+#endif /* HAVE_STRUCT_TIMESPEC */
+
+#ifdef HAVE_STRUCT_TIMEVAL
+
+static inline uint64_t fromtimeval(struct timeval tv, time_t base)
+{
+ return ((tv.tv_sec - base) * UINT64_C(1000000) + tv.tv_usec) * 1000;
+}
+
+static inline struct timeval totimeval(uint64_t t, time_t base)
+{
+ struct timeval tv;
+
+ t = (t + base)/1000;
+
+ tv.tv_sec = t / UINT64_C(1000000);
+ tv.tv_usec = t % UINT64_C(1000000);
+
+ return tv;
+}
+
+#endif /* HAVE_STRUCT_TIMEVAL */
#ifdef _POSIX_MONOTONIC_CLOCK
# define WHICHCLOCK CLOCK_MONOTONIC
@@ -22,20 +69,45 @@
# define WHICHCLOCK CLOCK_REALTIME
#endif
-static time_t tv_sec_zero;
-
uint64_t nstime(void)
{
struct timespec ts;
clock_gettime(WHICHCLOCK, &ts);
- return ((ts.tv_sec - tv_sec_zero) * UINT64_C(1000000000)) + ts.tv_nsec;
+ return fromtimespec(ts, ABS);
+}
+
+static timer_t wakeuptimer;
+
+static void sigalrm_handler(int sig)
+{
+ (void)sig;
+ timer_ctr++;
}
void nstime_init(void)
{
struct timespec ts;
+ struct sigaction sa;
+
clock_gettime(WHICHCLOCK, &ts);
tv_sec_zero = ts.tv_sec;
+ timer_create(WHICHCLOCK, NULL, &wakeuptimer);
+
+ memset(&sa, 0, sizeof sa);
+ sa.sa_handler = sigalrm_handler;
+
+ sigaction(SIGALRM, &sa, NULL);
+}
+
+void wakeusup(uint64_t until, uint64_t since)
+{
+ struct itimerspec it;
+
+ (void)since;
+
+ memset(&it, 0, sizeof it);
+ it.it_value = totimespec(until, ABS);
+ timer_settime(&wakeuptimer, TIMER_ABSTIME, &it, NULL);
}
#elif defined(__WIN32__)
@@ -94,21 +166,16 @@ void mynssleep(uint64_t until, uint64_t since)
#elif defined(HAVE_GETTIMEOFDAY)
-static time_t tv_sec_zero;
-
uint64_t nstime(void)
{
struct timeval tv;
-
gettimeofday(&tv, NULL);
- return ((tv.tv_sec - tv_sec_zero) * UINT64_C(1000000000))
- + (tv.tv_usec * UINT64_C(1000));
+ return fromtimeval(tv, ABS);
}
void nstime_init(void)
{
struct timeval tv;
-
gettimeofday(&tv, NULL);
tv_sec_zero = tv.tv_sec;
}
@@ -124,9 +191,7 @@ void mynssleep(uint64_t until, uint64_t since)
(void)since;
struct timespec req;
- req.tv_sec = until / UINT64_C(1000000000) + tv_sec_zero;
- req.tv_nsec = until % UINT64_C(1000000000);
-
+ req = totimespec(until, ABS);
clock_nanosleep(WHICHCLOCK, TIMER_ABSTIME, &req, NULL);
}
@@ -136,12 +201,24 @@ void mynssleep(uint64_t until, uint64_t since)
{
struct timespec req;
- until -= since;
+ req = totimespec(until - since, REL);
+ nanosleep(&req, NULL);
+}
- req.tv_sec = until / UINT64_C(1000000000);
- req.tv_nsec = until % UINT64_C(1000000000);
+#elif defined(HAVE_PSELECT)
- nanosleep(&req, NULL);
+void mynssleep(uint64_t until, uint64_t since)
+{
+ struct timespec ts = totimespec(until - since, REL);
+ pselect(0, NULL, NULL, NULL, &ts, NULL);
+}
+
+#elif defined(HAVE_SELECT)
+
+void mynssleep(uint64_t until, uint64_t since)
+{
+ struct timeval tv = totimeval(until - since, REL);
+ select(0, NULL, NULL, NULL, &tv);
}
#elif !defined(__WIN32__)
diff --git a/nstime.h b/nstime.h
index c2f7134..8d3d5ee 100644
--- a/nstime.h
+++ b/nstime.h
@@ -6,5 +6,6 @@
extern uint64_t nstime(void);
extern void nstime_init(void);
extern void mynssleep(uint64_t until, uint64_t since);
+extern void wakeusup(uint64_t when, uint64_t now);
#endif /* NSTIME_H */
diff --git a/z80.c b/z80.c
index 3b31cb4..f791443 100644
--- a/z80.c
+++ b/z80.c
@@ -27,8 +27,7 @@
*/
#include "z80.h"
#include "screen.h"
-
-#include <setjmp.h>
+#include "clock.h"
/*
* The state of our Z-80 registers is kept in this structure:
diff --git a/z80.h b/z80.h
index 71af95e..4ddff0d 100644
--- a/z80.h
+++ b/z80.h
@@ -190,7 +190,6 @@ extern void z80_out(int, uint8_t);
extern int z80_in(int);
extern int disassemble(int);
extern int DAsm(uint16_t pc, char *T, int *target);
-extern void z80_poll_external(void);
extern uint8_t ram[]; /* Array for plain RAM */