aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2018-10-03 23:31:21 -0700
committerH. Peter Anvin <hpa@zytor.com>2018-10-03 23:31:21 -0700
commit30273a6d5dded9db146aa618db34cbdc801df871 (patch)
tree92c47a73f260ce73e84879802673b453d2d9255a
parent5e562c1f3540840e776aaa18b43cbcc9c717d2fd (diff)
downloadabc80sim-30273a6d5dded9db146aa618db34cbdc801df871.tar.gz
abc80sim-30273a6d5dded9db146aa618db34cbdc801df871.tar.xz
abc80sim-30273a6d5dded9db146aa618db34cbdc801df871.zip
clock: for ABC800, interpolate CTC based on virtual time
If we are running in speed-accurate mode (limit_speed == true), then calculate interpolated CTC values based on virtual time (TSTATEs) since the last timer event on that channel. This makes it look to the virtual CPU as the counter is moving smoothly.
-rw-r--r--abc80.c4
-rw-r--r--clock.c42
-rw-r--r--clock.h1
3 files changed, 28 insertions, 19 deletions
diff --git a/abc80.c b/abc80.c
index 662669f..82e3cb5 100644
--- a/abc80.c
+++ b/abc80.c
@@ -13,7 +13,8 @@
#include <SDL_main.h>
-double ns_per_tstate = 1000.0/3.0; /* Nanosection per tstate (clock cycle) */
+double ns_per_tstate = 1000.0/3.0; /* Nanoseconds per tstate (clock cycle) */
+double tstate_per_ns = 3.0/1000.0; /* Inverse of the above = freq in GHz */
bool limit_speed = true;
static const char version_string[] = VERSION;
@@ -208,6 +209,7 @@ static void set_speed(const char *arg)
} else {
limit_speed = true;
ns_per_tstate = 1000.0/mhz;
+ tstate_per_ns = mhz/1000.0;
}
}
diff --git a/clock.c b/clock.c
index 4afed65..70e9dab 100644
--- a/clock.c
+++ b/clock.c
@@ -12,8 +12,9 @@ static struct abctimer *ctc_timer[4];
* Initialize the time for next event
*/
struct abctimer {
- uint64_t last;
- uint64_t period;
+ uint64_t period; /* Period in ns */
+ uint64_t last; /* Last checkpoint in ns */
+ uint64_t ltst; /* TSTATE value for last checkpoint */
void (*func)(void);
};
@@ -35,6 +36,7 @@ static struct abctimer *create_timer(uint64_t period, void (*func)(void))
t->period = period;
t->func = func;
t->last = nstime();
+ t->ltst = TSTATE;
return t;
}
@@ -120,6 +122,8 @@ void z80_poll_external(void)
/* Missed tick(s), advance to skip missed */
t->last = now - (now - t->last) % t->period;
}
+ /* 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 */
} else if (next > tnext) {
@@ -181,22 +185,24 @@ void abc800_ctc_out(uint8_t port, uint8_t v)
uint8_t abc800_ctc_in(uint8_t port)
{
- uint8_t v;
-
- switch (port & 3) {
- case 0:
- case 1:
- case 2:
- v = 0xff;
- break;
-
- case 3:
- {
- uint64_t now = nstime();
- v = ((ctc_timer[3]->last + ctc_timer[3]->period - now) * ctc_div[3])
- / ctc_timer[3]->period;
- break;
- }
+ uint8_t v, div;
+ const struct abctimer *t;
+
+ port &= 3;
+ t = ctc_timer[port];
+ div = ctc_div[port];
+
+ if (!t)
+ return -1;
+
+ if (limit_speed) {
+ /* Interpolate based on TSTATEs (virtual time) */
+ v = ((int64_t)t->period -
+ (int64_t)(((TSTATE - t->ltst)*ns_per_tstate)) * div) /
+ t->period;
+ } else {
+ /* Interpolate based on nanoseconds (real time) */
+ v = ((t->period - (nstime() - t->last)) * div) / t->period;
}
return v;
diff --git a/clock.h b/clock.h
index 78eedf2..b143c6f 100644
--- a/clock.h
+++ b/clock.h
@@ -6,6 +6,7 @@ extern void timer_poll(void);
extern void vsync_screen(void);
extern double ns_per_tstate;
+extern double tstate_per_ns;
extern bool limit_speed;
#endif /* CLOCK_H */