aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2018-09-25 10:21:35 -0700
committerH. Peter Anvin <hpa@zytor.com>2018-09-25 10:21:35 -0700
commitb08c4b8ae65eee5ed6ba2b653f53ba488c8f7167 (patch)
treef3aa81f8da93b9ad047502bf304a5039087658a8
parentb4cffb6dfb3d11aef0f13a0a2564b743e6adf903 (diff)
downloadabc80sim-b08c4b8ae65eee5ed6ba2b653f53ba488c8f7167.tar.gz
abc80sim-b08c4b8ae65eee5ed6ba2b653f53ba488c8f7167.tar.xz
abc80sim-b08c4b8ae65eee5ed6ba2b653f53ba488c8f7167.zip
Only call SDL_PollEvent() from the vsync interrupt
SDL_PolLEvent() might be expensive on some platforms. We could either move it into a separate thread and use SDL_WaitEvent() instead, but for now at least, just poll it only in the vsync interrupt.
-rw-r--r--abcio.h2
-rw-r--r--clock.c56
-rw-r--r--sdlscrn.c83
-rw-r--r--z80.c15
-rw-r--r--z80.h1
5 files changed, 100 insertions, 57 deletions
diff --git a/abcio.h b/abcio.h
index 82d1a24..a091a68 100644
--- a/abcio.h
+++ b/abcio.h
@@ -31,8 +31,6 @@ extern void printer_reset(void);
extern void printer_out(int sel, int port, int value);
extern int printer_in(int sel, int port);
-extern void check_event(void);
-
extern void keyboard_down(int sym);
extern void keyboard_up(void);
diff --git a/clock.c b/clock.c
index 322bb42..bc35e36 100644
--- a/clock.c
+++ b/clock.c
@@ -6,6 +6,7 @@
static void abc80_clock_tick(void);
static void abc800_clock_tick(void);
+static struct abctimer *ctc_timer[4];
/*
* Initialize the time for next event
@@ -13,25 +14,44 @@ static void abc800_clock_tick(void);
struct abctimer {
uint64_t last;
uint64_t period;
+ void (*func)(void);
};
-static struct abctimer clock_timer, vsync_timer;
-static void (*clock_tick)(void);
+
+#define MAX_TIMERS 2
+static struct abctimer timers[MAX_TIMERS];
+static int ntimers;
+
+static struct abctimer *create_timer(uint64_t period, void (*func)(void))
+{
+ struct abctimer *t;
+
+ if (ntimers >= MAX_TIMERS)
+ abort();
+
+ t = &timers[ntimers++];
+
+ t->period = period;
+ t->func = func;
+ t->last = nstime();
+
+ return t;
+}
void timer_init(void)
{
switch (model) {
case MODEL_ABC80:
- clock_timer.period = 20000000; /* 20 ms */
- clock_tick = abc80_clock_tick;
+ /* 20 ms = 50 Hz */
+ create_timer(20000000, abc80_clock_tick);
break;
case MODEL_ABC802:
- clock_timer.period = 10666667; /* 10.67 ms = 93.75 Hz */
- clock_tick = abc800_clock_tick;
- vsync_timer.period = 20000000;
+ /* 10.67 ms = 93.75 Hz */
+ ctc_timer[3] = create_timer(10666667, abc800_clock_tick);
+
+ /* 20 ms = 50 Hz */
+ create_timer(20000000, abc802_vsync);
break;
}
-
- clock_timer.last = vsync_timer.last = nstime();
}
static inline bool trigger(uint64_t now, struct abctimer *tmr)
@@ -49,15 +69,19 @@ static inline bool trigger(uint64_t now, struct abctimer *tmr)
return true;
}
-void timer_poll(void)
+/* Poll for timers - these the only external event we look for */
+void z80_poll_external(void)
{
uint64_t now = nstime();
+ struct abctimer *t = timers;
+ int i;
- if (trigger(now, &clock_timer))
- clock_tick();
+ for (i = 0; i < ntimers; i++) {
+ if (trigger(now, t))
+ t->func();
- if (trigger(now, &vsync_timer))
- abc802_vsync();
+ t++;
+ }
}
/*
@@ -121,8 +145,8 @@ uint8_t abc800_ctc_in(uint8_t port)
case 3:
{
uint64_t now = nstime();
- v = ((clock_timer.last + clock_timer.period - now) * ctc_div[3])
- / clock_timer.period;
+ v = ((ctc_timer[3]->last + ctc_timer[3]->period - now) * ctc_div[3])
+ / ctc_timer[3]->period;
break;
}
}
diff --git a/sdlscrn.c b/sdlscrn.c
index ffcd228..7862ad5 100644
--- a/sdlscrn.c
+++ b/sdlscrn.c
@@ -32,6 +32,8 @@
extern const unsigned char abc_font[256][FONT_YSIZE];
+static void check_event(void);
+
#define NCOLORS 8
static struct argb { uint8_t a, r, g, b; } rgbcolors[NCOLORS] = {
@@ -141,12 +143,22 @@ static inline uint8_t screendata(uint8_t y, uint8_t x)
}
/*
- * Prepare screen for modification
+ * Prevent/allow screen refresh
*/
static void lock_screen(struct surface *s)
{
- if (s->lock_count++ == 0)
- SDL_LockSurface(s->surf);
+ if (!s->lock_count++)
+ SDL_LockSurface(s->surf);
+}
+
+static void unlock_screen(struct surface *s)
+{
+ if (s->lock_count > 0)
+ SDL_UnlockSurface(s->surf);
+ else if (s->lock_count < 0)
+ abort(); /* SHOULD NEVER HAPPEN */
+
+ s->lock_count--;
}
/*
@@ -237,21 +249,17 @@ put_screen(struct surface *s, unsigned int tx, unsigned int ty, bool blink)
*/
#define MIN_UPDATE_INTERVAL (10000000) /* 10 ms */
-static void update_screen(struct surface *s, bool force)
+static void update_screen(struct surface *s)
{
uint64_t now;
- if (--s->lock_count > 0)
+ if (s->lock_count > 0)
return;
- SDL_UnlockSurface(s->surf);
-
if (s->upd_x0 == UINT_MAX)
- return;
+ return; /* Screen unchanged */
now = nstime();
- if (!force && (now - s->updated) < MIN_UPDATE_INTERVAL)
- return;
if (s->surf->flags & SDL_DOUBLEBUF)
SDL_Flip(s->surf);
@@ -267,15 +275,21 @@ static void update_screen(struct surface *s, bool force)
s->upd_x1 = s->upd_y1 = 0;
}
+/* Mark the whole screen dirty */
+static void screen_dirty(struct surface *s)
+{
+ s->upd_x0 = s->upd_y0 = 0;
+ s->upd_x1 = (TS_WIDTH-1) >> mode40;
+ s->upd_y1 = TS_HEIGHT-1;
+}
+
/* Refresh the entire screen or recreate the screen on another surface */
static void refresh_screen(struct surface *s, bool blink)
{
unsigned int x, y;
unsigned int width = TS_WIDTH >> mode40;
- s->upd_x0 = s->upd_y0 = 0;
- s->upd_x1 = width-1;
- s->upd_y1 = TS_HEIGHT-1;
+ screen_dirty(s);
lock_screen(s);
@@ -283,7 +297,8 @@ static void refresh_screen(struct surface *s, bool blink)
for (x = 0; x < width; x++)
put_screen(s, x, y, blink);
- update_screen(s, true);
+ unlock_screen(s);
+ update_screen(s);
}
/*
@@ -315,7 +330,7 @@ void write_screen(uint8_t *p, uint8_t v)
put_screen(&rscreen, xy.x, xy.y, blink_on);
}
- update_screen(&rscreen, false);
+ unlock_screen(&rscreen);
}
static void do_set_mode40(bool m)
@@ -369,11 +384,27 @@ static bool set_blink(bool to_what)
break;
}
- update_screen(&rscreen, true);
+ unlock_screen(&rscreen);
return !to_what; /* We just flipped it... */
}
+/* Called from the timer that corresponds to the simulated vsync */
+void vsync_screen(void)
+{
+ const int blink_rate = 400/20; /* 400 ms/20 ms = 2.5 Hz */
+ static int blink_ctr;
+
+ check_event(); /* Poll for an SDL event */
+
+ if (!blink_ctr--) {
+ set_blink(!blink_on);
+ blink_ctr += blink_rate;
+ }
+
+ update_screen(&rscreen);
+}
+
/*
* Wrap an SDL_Surface in our local stuff
*/
@@ -395,7 +426,7 @@ static struct surface *init_surface(struct surface *s)
rgbcolors[i].r, rgbcolors[i].g, rgbcolors[i].b);
}
- /* Surface is not locked */
+ /* Surface is unlocked */
s->lock_count = 0;
return s;
@@ -413,8 +444,8 @@ static void abc_screenshot(void)
if (!init_surface(&s))
return;
refresh_screen(&s, true); /* Always snapshot with blink on */
- screenshot(s.surf);
+ screenshot(s.surf);
SDL_FreeSurface(s.surf);
}
@@ -436,10 +467,8 @@ void screen_init(bool width40)
rscreen.surf = SDL_SetVideoMode(PX_WIDTH, PX_HEIGHT, 32,
SDL_HWSURFACE | SDL_DOUBLEBUF |
(window ? 0 : SDL_FULLSCREEN));
- if (!init_surface(&rscreen))
- return;
- /* No mouse cursor, please */
+ /* No mouse cursor in full screen mode */
if ( !window )
SDL_ShowCursor(SDL_DISABLE);
@@ -471,6 +500,9 @@ void screen_init(bool width40)
/* Enable keyboard repeat */
SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
+ if (!init_surface(&rscreen))
+ return;
+
/* Forcibly set the screen width and load the appropriate BASIC */
do_set_mode40(width40);
}
@@ -484,11 +516,12 @@ void screen_reset(void)
}
/*
- * Handle events
+ * Handle events. This is called from vsync_screen(), because
+ * SDL_PollEvent() might be expensive on some platforms.
*/
-int keyboard_code; /* Keyboard code exported to PIO */
+int keyboard_code; /* Keyboard code exported to PIO/DART */
-void check_event(void)
+static void check_event(void)
{
SDL_Event event;
static int keyboard_scan = -1; /* No key currently down */
@@ -498,8 +531,6 @@ void check_event(void)
KSH_ALT = 4
} kshift;
- set_blink(timer_poll()); /* Poll timer, change blink if needed */
-
while ( SDL_PollEvent(&event) ) {
switch ( event.type ) {
case SDL_KEYDOWN:
diff --git a/z80.c b/z80.c
index fb025cb..133ccee 100644
--- a/z80.c
+++ b/z80.c
@@ -2524,20 +2524,9 @@ int z80_run(bool continuous, bool halted)
/* loop to do a z80 instruction */
do {
- /*
- * Special hack for ABC80
- */
-
- extern volatile int event_pending;
- void check_event(void);
-
do {
- if ( event_pending )
- check_event();
-
- /*
- * End of special hack.
- */
+ /* Poll for external event */
+ z80_poll_external();
/* Check for an interrupt */
if (z80_state.nminterrupt) {
diff --git a/z80.h b/z80.h
index fc7e0ab..3cc6349 100644
--- a/z80.h
+++ b/z80.h
@@ -187,6 +187,7 @@ 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 unsigned int tracing;
extern FILE *tracef;