VAX730, VAX750, VAX8600: Update Interval Timer and TODR to align with VAX780

This commit is contained in:
Mark Pizzolato 2016-12-16 18:32:26 -08:00
parent 5fc4744d8a
commit 76103cf760
4 changed files with 384 additions and 374 deletions

View file

@ -100,6 +100,18 @@
#define CLK_DELAY 5000 /* 100 Hz */ #define CLK_DELAY 5000 /* 100 Hz */
#define TMXR_MULT 1 /* 100 Hz */ #define TMXR_MULT 1 /* 100 Hz */
static BITFIELD tmr_iccs_bits [] = {
BIT(RUN), /* Run */
BITNCF(3), /* unused */
BIT(XFR), /* Transfer */
BIT(SGL), /* Single */
BIT(IE), /* Interrupt Enable */
BIT(DON), /* Done */
BITNCF(23), /* unused */
BIT(ERR), /* Error */
ENDBITS
};
/* TU58 definitions */ /* TU58 definitions */
#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ #define UNIT_V_WLK (UNIT_V_UF) /* write locked */
@ -173,13 +185,13 @@ uint32 tmr_nicr = 0; /* next interval */
uint32 tmr_inc = 0; /* timer increment */ uint32 tmr_inc = 0; /* timer increment */
int32 tmr_sav = 0; /* timer save */ int32 tmr_sav = 0; /* timer save */
int32 tmr_int = 0; /* interrupt */ int32 tmr_int = 0; /* interrupt */
int32 tmr_use_100hz = 1; /* use 100Hz for timer */
int32 clk_tps = 100; /* ticks/second */ int32 clk_tps = 100; /* ticks/second */
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
struct todr_battery_info { struct todr_battery_info {
uint32 toy_gmtbase; /* GMT base of set value */ uint32 toy_gmtbase; /* GMT base of set value */
uint32 toy_gmtbasemsec; /* The milliseconds of the set value */ uint32 toy_gmtbasemsec; /* The milliseconds of the set value */
uint32 toy_endian_plus2; /* 2 -> Big Endian, 3 -> Little Endian, invalid otherwise */
}; };
typedef struct todr_battery_info TOY; typedef struct todr_battery_info TOY;
@ -187,7 +199,6 @@ int32 td_regval; /* temp location used in
t_stat tti_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr);
t_stat clk_svc (UNIT *uptr);
t_stat tmr_svc (UNIT *uptr); t_stat tmr_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr);
@ -204,9 +215,8 @@ t_stat clk_attach (UNIT *uptr, CONST char *cptr);
t_stat clk_detach (UNIT *uptr); t_stat clk_detach (UNIT *uptr);
t_stat tmr_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr);
t_stat td_reset (DEVICE *dptr); t_stat td_reset (DEVICE *dptr);
int32 icr_rd (t_bool interp); int32 icr_rd (void);
void tmr_incr (uint32 inc); void tmr_sched (uint32 incr);
void tmr_sched (void);
t_stat todr_resync (void); t_stat todr_resync (void);
t_stat txdb_misc_wr (int32 data); t_stat txdb_misc_wr (int32 data);
@ -283,7 +293,7 @@ DEVICE tto_dev = {
/* TODR and TMR data structures */ /* TODR and TMR data structures */
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ UNIT clk_unit = { UDATA (NULL, UNIT_IDLE+UNIT_FIX, sizeof(TOY))};
REG clk_reg[] = { REG clk_reg[] = {
{ DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT }, { DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT },
@ -315,16 +325,31 @@ REG tmr_reg[] = {
{ FLDATAD (INT, tmr_int, 0, "interrupt request") }, { FLDATAD (INT, tmr_int, 0, "interrupt request") },
{ HRDATA (INCR, tmr_inc, 32), REG_HIDDEN }, { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN },
{ HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN }, { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN },
{ FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN },
{ NULL } { NULL }
}; };
#define TMR_DB_REG 0x01 /* Register Access */
#define TMR_DB_TICK 0x02 /* Ticks */
#define TMR_DB_SCHED 0x04 /* Scheduling */
#define TMR_DB_INT 0x08 /* Interrupts */
#define TMR_DB_TODR 0x10 /* TODR */
DEBTAB tmr_deb[] = {
{ "REG", TMR_DB_REG, "Register Access"},
{ "TICK", TMR_DB_TICK, "Ticks"},
{ "SCHED", TMR_DB_SCHED, "Ticks"},
{ "INT", TMR_DB_INT, "Interrupts"},
{ "TODR", TMR_DB_TODR, "TODR activities"},
{ NULL, 0 }
};
DEVICE tmr_dev = { DEVICE tmr_dev = {
"TMR", &tmr_unit, tmr_reg, NULL, "TMR", &tmr_unit, tmr_reg, NULL,
1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
NULL, NULL, &tmr_reset, NULL, NULL, &tmr_reset,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_DEBUG, 0,
tmr_deb, NULL, NULL, NULL, NULL, NULL,
&tmr_description &tmr_description
}; };
@ -464,10 +489,11 @@ void rxcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tti_int = 0; tti_int = 0;
else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tti_int = 1; if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tti_int = 1;
}
tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR);
return;
} }
int32 rxdb_rd (void) int32 rxdb_rd (void)
@ -492,10 +518,11 @@ void txcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tto_int = 0; tto_int = 0;
else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tto_int = 1; if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tto_int = 1;
}
tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR); tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR);
return;
} }
void txdb_wr (int32 data) void txdb_wr (int32 data)
@ -506,7 +533,6 @@ tto_int = 0; /* clear int */
if (tto_buf & TXDB_SEL) /* console mailbox? */ if (tto_buf & TXDB_SEL) /* console mailbox? */
txdb_misc_wr (tto_buf); txdb_misc_wr (tto_buf);
sim_activate (&tto_unit, tto_unit.wait); /* no, console */ sim_activate (&tto_unit, tto_unit.wait); /* no, console */
return;
} }
/* Terminal input service (poll for character) */ /* Terminal input service (poll for character) */
@ -524,7 +550,8 @@ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */
return c; return c;
if (c & SCPE_BREAK) /* break? */ if (c & SCPE_BREAK) /* break? */
tti_buf = RXDB_ERR; tti_buf = RXDB_ERR;
else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); else
tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags));
tti_buftime = sim_os_msec (); tti_buftime = sim_os_msec ();
uptr->pos = uptr->pos + 1; uptr->pos = uptr->pos + 1;
tti_csr = tti_csr | CSR_DONE; tti_csr = tti_csr | CSR_DONE;
@ -541,7 +568,7 @@ tmxr_set_console_units (&tti_unit, &tto_unit);
tti_buf = 0; tti_buf = 0;
tti_csr = 0; tti_csr = 0;
tti_int = 0; tti_int = 0;
sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); sim_activate (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll));
return SCPE_OK; return SCPE_OK;
} }
@ -612,162 +639,143 @@ return "console terminal output";
The architected VAX timer, which increments at 1Mhz, cannot be The architected VAX timer, which increments at 1Mhz, cannot be
accurately simulated due to the overhead that would be required accurately simulated due to the overhead that would be required
for 1M clock events per second. Instead, a hidden calibrated for 1M clock events per second. Instead 1Mhz intervals are
100Hz timer is run (because that's what VMS expects), and a derived from the calibrated instruction execution rate.
hack is used for the interval timer.
When the timer is started, the timer interval is inspected.
if the interval is >= 10msec, then the 100Hz timer drives the
next interval
if the interval is < 10mec, then count instructions
If the interval register is read, then its value between events If the interval register is read, then its value between events
is interpolated using the current instruction count versus the is interpolated relative to the elapsed instruction count.
count when the most recent event started, the result is scaled
to the calibrated system clock, unless the interval being timed
is less than a calibrated system clock tick (or the calibrated
clock is running very slowly) at which time the result will be
the elapsed instruction count.
*/ */
int32 iccs_rd (void) int32 iccs_rd (void)
{ {
sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_rd()", tmr_iccs_bits, tmr_iccs, tmr_iccs, TRUE);
return tmr_iccs & TMR_CSR_RD; return tmr_iccs & TMR_CSR_RD;
} }
void iccs_wr (int32 val) void iccs_wr (int32 val)
{ {
sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_wr()", tmr_iccs_bits, tmr_iccs, val, TRUE);
if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
tmr_use_100hz = 0; if (tmr_iccs & TMR_CSR_RUN) { /* run 1 -> 0? */
if (tmr_iccs & TMR_CSR_RUN) /* run 1 -> 0? */ tmr_icr = icr_rd (); /* update itr */
tmr_icr = icr_rd (TRUE); /* update itr */ sim_rtcn_calb (0, TMR_CLK); /* stop timer */
}
} }
if (val & CSR_DONE) /* Interrupt Acked? */ if (val & CSR_DONE) /* Interrupt Acked? */
sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */ sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */
tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */ tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */
tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */ tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */
(val & TMR_CSR_WR); (val & TMR_CSR_WR);
if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr; /* xfr set? */ if (val & TMR_CSR_XFR) /* xfr set? */
tmr_icr = tmr_nicr;
if (val & TMR_CSR_RUN) { /* run? */ if (val & TMR_CSR_RUN) { /* run? */
if (val & TMR_CSR_XFR) /* new tir? */ if (val & TMR_CSR_XFR) /* new tir? */
sim_cancel (&tmr_unit); /* stop prev */ sim_cancel (&tmr_unit); /* stop prev */
if (!sim_is_active (&tmr_unit)) /* not running? */ if (!sim_is_active (&tmr_unit)) { /* not running? */
tmr_sched (); /* activate */ sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmr_sched (tmr_icr); /* activate */
}
} }
else if (val & TMR_CSR_SGL) { /* single step? */ else {
tmr_incr (1); /* incr tmr */ if (val & TMR_CSR_XFR) /* xfr set? */
if (tmr_icr == 0) /* if ovflo, */ tmr_icr = tmr_nicr;
tmr_icr = tmr_nicr; /* reload tir */ if (val & TMR_CSR_SGL) { /* single step? */
tmr_icr = tmr_icr + 1; /* incr tmr */
if (tmr_icr == 0) { /* if ovflo, */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_IE) { /* ie? */
tmr_int = 1; /* set int req */
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_incr() - INT=1\n");
}
tmr_icr = tmr_nicr; /* reload tir */
}
}
} }
if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */
(TMR_CSR_DON | TMR_CSR_IE)) (TMR_CSR_DON | TMR_CSR_IE)) {
tmr_int = 0; if (tmr_int) {
return; tmr_int = 0;
sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n");
}
}
} }
int32 icr_rd (t_bool interp) int32 icr_rd (void)
{ {
uint32 delta; int32 result;
if (interp || (tmr_iccs & TMR_CSR_RUN)) { /* interp, running? */ if (tmr_iccs & TMR_CSR_RUN) { /* running? */
delta = sim_grtime () - tmr_sav; /* delta inst */ uint32 delta = sim_grtime() - tmr_sav;
if (tmr_use_100hz && (tmr_poll > TMR_INC)) /* scale large int */ result = (int32)(tmr_nicr + (uint32)((1000000.0 * delta) / sim_timer_inst_per_sec ()));
delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll);
if (delta >= tmr_inc)
delta = tmr_inc - 1;
return tmr_icr + delta;
} }
return tmr_icr; else
result = (int32)tmr_icr;
sim_debug (TMR_DB_REG, &tmr_dev, "icr_rd() = 0x%08X%s\n", result, (tmr_iccs & TMR_CSR_RUN) ? " - interpolated" : "");
return result;
} }
int32 nicr_rd (void) int32 nicr_rd (void)
{ {
sim_debug (TMR_DB_REG, &tmr_dev, "nicr_rd() = 0x%08X\n", tmr_nicr);
return tmr_nicr; return tmr_nicr;
} }
void nicr_wr (int32 val) void nicr_wr (int32 val)
{ {
sim_debug (TMR_DB_REG, &tmr_dev, "nicr_wr(0x%08X)\n", val);
tmr_nicr = val; tmr_nicr = val;
} }
/* 100Hz base clock unit service */
t_stat clk_svc (UNIT *uptr)
{
tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate_after (uptr, 1000000/clk_tps); /* reactivate unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */
tmr_incr (TMR_INC); /* do timer service */
return SCPE_OK;
}
/* Interval timer unit service */ /* Interval timer unit service */
t_stat tmr_svc (UNIT *uptr) t_stat tmr_svc (UNIT *uptr)
{ {
tmr_incr (tmr_inc); /* incr timer */ sim_debug (TMR_DB_TICK, &tmr_dev, "tmr_svc()\n");
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) /* run? */
tmr_sched (tmr_nicr); /* reactivate */
if (tmr_iccs & TMR_CSR_IE) { /* ie? set int req */
tmr_int = 1;
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_svc() - INT=1\n");
}
else
tmr_int = 0;
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
return SCPE_OK; return SCPE_OK;
} }
/* Timer increment */
void tmr_incr (uint32 inc)
{
uint32 new_icr = (tmr_icr + inc) & LMASK; /* add incr */
if (new_icr < tmr_icr) { /* ovflo? */
tmr_icr = 0; /* now 0 */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) { /* run? */
tmr_icr = tmr_nicr; /* reload */
tmr_sched (); /* reactivate */
}
if (tmr_iccs & TMR_CSR_IE) /* ie? set int req */
tmr_int = 1;
else tmr_int = 0;
}
else {
tmr_icr = new_icr; /* no, update icr */
if (tmr_iccs & TMR_CSR_RUN) /* still running? */
tmr_sched (); /* reactivate */
}
return;
}
/* Timer scheduling */ /* Timer scheduling */
void tmr_sched (void) void tmr_sched (uint32 nicr)
{ {
tmr_sav = sim_grtime (); /* save intvl base */ uint32 usecs = (nicr) ? (~nicr + 1) : 0xFFFFFFFF;
tmr_inc = (~tmr_icr + 1); /* inc = interval */
if (tmr_inc == 0) tmr_inc = 1; clk_tps = 1000000 / usecs;
if (tmr_inc < TMR_INC) { /* 100Hz multiple? */
sim_activate (&tmr_unit, tmr_inc); /* schedule timer */ sim_debug (TMR_DB_SCHED, &tmr_dev, "tmr_sched(nicr=0x%08X-usecs=0x%08X) - tps=%d\n", nicr, usecs, clk_tps);
tmr_use_100hz = 0; tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK);
} if (SCPE_OK == sim_activate_after (&tmr_unit, usecs))
else tmr_use_100hz = 1; /* let clk handle */ tmr_sav = sim_grtime(); /* Save interval base time */
return;
} }
/* 100Hz clock reset */ /* 100Hz TODR reset */
t_stat clk_reset (DEVICE *dptr) t_stat clk_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&clk_unit, clk_unit.wait, TMR_CLK);/* init 100Hz timer */
sim_activate_after (&clk_unit, 1000000/clk_tps); /* activate 100Hz unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1); clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL) if (clk_unit.filebuf == NULL)
return SCPE_MEM; return SCPE_MEM;
todr_resync ();
} }
todr_resync ();
return SCPE_OK; return SCPE_OK;
} }
@ -807,6 +815,20 @@ const char *clk_description (DEVICE *dptr)
return "time of year clock"; return "time of year clock";
} }
static uint32 sim_byteswap32 (uint32 data)
{
uint8 *bdata = (uint8 *)&data;
uint8 tmp;
tmp = bdata[0];
bdata[0] = bdata[3];
bdata[3] = tmp;
tmp = bdata[1];
bdata[1] = bdata[2];
bdata[2] = tmp;
return data;
}
/* CLK attach */ /* CLK attach */
t_stat clk_attach (UNIT *uptr, CONST char *cptr) t_stat clk_attach (UNIT *uptr, CONST char *cptr)
@ -818,8 +840,21 @@ memset (uptr->filebuf, 0, (size_t)uptr->capac);
r = attach_unit (uptr, cptr); r = attach_unit (uptr, cptr);
if (r != SCPE_OK) if (r != SCPE_OK)
uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE);
else else {
TOY *toy = (TOY *)uptr->filebuf;
uptr->hwmark = (uint32) uptr->capac; uptr->hwmark = (uint32) uptr->capac;
if ((toy->toy_endian_plus2 < 2) || (toy->toy_endian_plus2 > 3))
memset (uptr->filebuf, 0, (size_t)uptr->capac);
else {
if (toy->toy_endian_plus2 != sim_end + 2) { /* wrong endian? */
toy->toy_gmtbase = sim_byteswap32 (toy->toy_gmtbase);
toy->toy_gmtbasemsec = sim_byteswap32 (toy->toy_gmtbasemsec);
}
}
toy->toy_endian_plus2 = sim_end + 2;
todr_resync ();
}
return r; return r;
} }
@ -839,13 +874,12 @@ return r;
t_stat tmr_reset (DEVICE *dptr) t_stat tmr_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
tmr_iccs = 0; tmr_iccs = 0;
tmr_icr = 0;
tmr_nicr = 0; tmr_nicr = 0;
tmr_int = 0; tmr_int = 0;
tmr_use_100hz = 1;
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
todr_resync (); /* resync TODR */
return SCPE_OK; return SCPE_OK;
} }
@ -865,6 +899,7 @@ sim_rtcn_get_time(&now, TMR_CLK); /* get curr time */
base.tv_sec = toy->toy_gmtbase; base.tv_sec = toy->toy_gmtbase;
base.tv_nsec = toy->toy_gmtbasemsec * 1000000; base.tv_nsec = toy->toy_gmtbasemsec * 1000000;
sim_timespec_diff (&val, &now, &base); sim_timespec_diff (&val, &now, &base);
sim_debug (TMR_DB_TODR, &tmr_dev, "todr_rd() - TODR=0x%X\n", (int32)(val.tv_sec*100 + val.tv_nsec/10000000));
return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */ return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */
} }
@ -882,6 +917,7 @@ val.tv_nsec = (((uint32)data) % 100) * 10000000;
sim_timespec_diff (&base, &now, &val); /* base = now - data */ sim_timespec_diff (&base, &now, &val); /* base = now - data */
toy->toy_gmtbase = (uint32)base.tv_sec; toy->toy_gmtbase = (uint32)base.tv_sec;
toy->toy_gmtbasemsec = base.tv_nsec/1000000; toy->toy_gmtbasemsec = base.tv_nsec/1000000;
sim_debug (TMR_DB_TODR, &tmr_dev, "todr_wr(0x%X)\n", data);
} }
t_stat todr_resync (void) t_stat todr_resync (void)
@ -904,9 +940,9 @@ else { /* Not-Attached means */
if (ctm == NULL) /* error? */ if (ctm == NULL) /* error? */
return SCPE_NOFNC; return SCPE_NOFNC;
base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */
ctm->tm_hour) * 60) + ctm->tm_hour) * 60) +
ctm->tm_min) * 60) + ctm->tm_min) * 60) +
ctm->tm_sec; ctm->tm_sec;
todr_wr ((base * 100) + 0x10000000); /* use VMS form */ todr_wr ((base * 100) + 0x10000000); /* use VMS form */
} }
return SCPE_OK; return SCPE_OK;

View file

@ -175,7 +175,6 @@ uint32 tmr_nicr = 0; /* next interval */
uint32 tmr_inc = 0; /* timer increment */ uint32 tmr_inc = 0; /* timer increment */
int32 tmr_sav = 0; /* timer save */ int32 tmr_sav = 0; /* timer save */
int32 tmr_int = 0; /* interrupt */ int32 tmr_int = 0; /* interrupt */
int32 tmr_use_100hz = 1; /* use 100Hz for timer */
int32 clk_tps = 100; /* ticks/second */ int32 clk_tps = 100; /* ticks/second */
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
@ -190,7 +189,6 @@ int32 td_regval; /* temp location used in
t_stat tti_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr);
t_stat clk_svc (UNIT *uptr);
t_stat tmr_svc (UNIT *uptr); t_stat tmr_svc (UNIT *uptr);
t_stat tti_reset (DEVICE *dptr); t_stat tti_reset (DEVICE *dptr);
t_stat tto_reset (DEVICE *dptr); t_stat tto_reset (DEVICE *dptr);
@ -207,9 +205,9 @@ t_stat tmr_reset (DEVICE *dptr);
const char *tmr_description (DEVICE *dptr); const char *tmr_description (DEVICE *dptr);
t_stat td_reset (DEVICE *dptr); t_stat td_reset (DEVICE *dptr);
const char *td_description (DEVICE *dptr); const char *td_description (DEVICE *dptr);
int32 icr_rd (t_bool interp); int32 icr_rd (void);
void tmr_incr (uint32 inc); void tmr_incr (uint32 inc);
void tmr_sched (void); void tmr_sched (uint32 incr);
t_stat todr_resync (void); t_stat todr_resync (void);
t_stat txdb_misc_wr (int32 data); t_stat txdb_misc_wr (int32 data);
@ -288,12 +286,12 @@ DEVICE tto_dev = {
/* TODR and TMR data structures */ /* TODR and TMR data structures */
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ UNIT clk_unit = { UDATA (NULL, UNIT_IDLE+UNIT_FIX, sizeof(TOY))};
REG clk_reg[] = { REG clk_reg[] = {
{ DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT }, { DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT },
{ DRDATAD (POLL, tmr_poll, 24, "calibrated poll interval"), REG_NZ + PV_LEFT + REG_HRO }, { DRDATAD (POLL, tmr_poll, 24, "calibrated poll interval"), REG_NZ + PV_LEFT + REG_HRO },
{ DRDATAD (TPS, clk_tps, 8, "ticks per second (100)"), REG_NZ + PV_LEFT }, { DRDATAD (TPS, clk_tps, 8, "ticks per second"), REG_NZ + PV_LEFT },
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
{ DRDATAD (ASYNCH, sim_asynch_enabled, 1, "asynch I/O enabled flag"), PV_LEFT }, { DRDATAD (ASYNCH, sim_asynch_enabled, 1, "asynch I/O enabled flag"), PV_LEFT },
{ DRDATAD (LATENCY, sim_asynch_latency, 32, "desired asynch interrupt latency"), PV_LEFT }, { DRDATAD (LATENCY, sim_asynch_latency, 32, "desired asynch interrupt latency"), PV_LEFT },
@ -320,7 +318,6 @@ REG tmr_reg[] = {
{ FLDATAD (INT, tmr_int, 0, "interrupt request") }, { FLDATAD (INT, tmr_int, 0, "interrupt request") },
{ HRDATA (INCR, tmr_inc, 32), REG_HIDDEN }, { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN },
{ HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN }, { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN },
{ FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN },
{ NULL } { NULL }
}; };
@ -328,12 +325,14 @@ REG tmr_reg[] = {
#define TMR_DB_TICK 0x02 /* Ticks */ #define TMR_DB_TICK 0x02 /* Ticks */
#define TMR_DB_SCHED 0x04 /* Scheduling */ #define TMR_DB_SCHED 0x04 /* Scheduling */
#define TMR_DB_INT 0x08 /* Interrupts */ #define TMR_DB_INT 0x08 /* Interrupts */
#define TMR_DB_TODR 0x10 /* TODR */
DEBTAB tmr_deb[] = { DEBTAB tmr_deb[] = {
{ "REG", TMR_DB_REG, "Register Access"}, { "REG", TMR_DB_REG, "Register Access"},
{ "TICK", TMR_DB_TICK, "Ticks"}, { "TICK", TMR_DB_TICK, "Ticks"},
{ "SCHED", TMR_DB_SCHED, "Ticks"}, { "SCHED", TMR_DB_SCHED, "Ticks"},
{ "INT", TMR_DB_INT, "Interrupts"}, { "INT", TMR_DB_INT, "Interrupts"},
{ "TODR", TMR_DB_TODR, "TODR activities"},
{ NULL, 0 } { NULL, 0 }
}; };
@ -484,10 +483,11 @@ void rxcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tti_int = 0; tti_int = 0;
else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tti_int = 1; if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tti_int = 1;
}
tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR);
return;
} }
int32 rxdb_rd (void) int32 rxdb_rd (void)
@ -512,10 +512,11 @@ void txcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tto_int = 0; tto_int = 0;
else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tto_int = 1; if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tto_int = 1;
}
tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR); tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR);
return;
} }
void txdb_wr (int32 data) void txdb_wr (int32 data)
@ -525,8 +526,8 @@ tto_csr = tto_csr & ~CSR_DONE; /* clear flag */
tto_int = 0; /* clear int */ tto_int = 0; /* clear int */
if (tto_buf & TXDB_SEL) /* console? */ if (tto_buf & TXDB_SEL) /* console? */
txdb_misc_wr (tto_buf); txdb_misc_wr (tto_buf);
else sim_activate (&tto_unit, tto_unit.wait); /* no, console terminal */ else
return; sim_activate (&tto_unit, tto_unit.wait); /* no, console terminal */
} }
/* Terminal input service (poll for character) */ /* Terminal input service (poll for character) */
@ -535,7 +536,7 @@ t_stat tti_svc (UNIT *uptr)
{ {
int32 c; int32 c;
sim_clock_coschedule_tmr (uptr, TMR_CLK, TMXR_MULT); /* continue poll */ sim_clock_coschedule (uptr, TMXR_MULT); /* continue poll */
if ((tti_csr & CSR_DONE) && /* input still pending and < 500ms? */ if ((tti_csr & CSR_DONE) && /* input still pending and < 500ms? */
((sim_os_msec () - tti_buftime) < 500)) ((sim_os_msec () - tti_buftime) < 500))
@ -544,7 +545,8 @@ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */
return c; return c;
if (c & SCPE_BREAK) /* break? */ if (c & SCPE_BREAK) /* break? */
tti_buf = RXDB_ERR | RXDB_FRM; tti_buf = RXDB_ERR | RXDB_FRM;
else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); else
tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags));
tti_buftime = sim_os_msec (); tti_buftime = sim_os_msec ();
uptr->pos = uptr->pos + 1; uptr->pos = uptr->pos + 1;
tti_csr = tti_csr | CSR_DONE; tti_csr = tti_csr | CSR_DONE;
@ -561,7 +563,7 @@ tmxr_set_console_units (&tti_unit, &tto_unit);
tti_buf = 0; tti_buf = 0;
tti_csr = 0; tti_csr = 0;
tti_int = 0; tti_int = 0;
sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); sim_activate (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll));
return SCPE_OK; return SCPE_OK;
} }
@ -633,23 +635,11 @@ return "console terminal output";
The architected VAX timer, which increments at 1Mhz, cannot be The architected VAX timer, which increments at 1Mhz, cannot be
accurately simulated due to the overhead that would be required accurately simulated due to the overhead that would be required
for 1M clock events per second. Instead, a hidden calibrated for 1M clock events per second. Instead 1Mhz intervals are
100Hz timer is run (because that's what VMS expects), and a derived from the calibrated instruction execution rate.
hack is used for the interval timer.
When the timer is started, the timer interval is inspected.
if the interval is >= 10msec, then the 100Hz timer drives the
next interval
if the interval is < 10mec, then count instructions
If the interval register is read, then its value between events If the interval register is read, then its value between events
is interpolated using the current instruction count versus the is interpolated relative to the elapsed instruction count.
count when the most recent event started, the result is scaled
to the calibrated system clock, unless the interval being timed
is less than a calibrated system clock tick (or the calibrated
clock is running very slowly) at which time the result will be
the elapsed instruction count.
*/ */
int32 iccs_rd (void) int32 iccs_rd (void)
@ -660,29 +650,46 @@ return tmr_iccs & TMR_CSR_RD;
void iccs_wr (int32 val) void iccs_wr (int32 val)
{ {
sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_wr()", tmr_iccs_bits, val, val, TRUE); sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_wr()", tmr_iccs_bits, tmr_iccs, val, TRUE);
if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
tmr_use_100hz = 0; if (tmr_iccs & TMR_CSR_RUN) { /* run 1 -> 0? */
if (tmr_iccs & TMR_CSR_RUN) /* run 1 -> 0? */ tmr_icr = icr_rd (); /* update itr */
tmr_icr = icr_rd (TRUE); /* update itr */ sim_rtcn_calb (0, TMR_CLK); /* stop timer */
}
} }
if (val & CSR_DONE) /* Interrupt Acked? */ if (val & CSR_DONE) /* Interrupt Acked? */
sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */ sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */
tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */ tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */
tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */ tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */
(val & TMR_CSR_WR); (val & TMR_CSR_WR);
if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr; /* xfr set? */ if (val & TMR_CSR_XFR) /* xfr set? */
tmr_icr = tmr_nicr;
if (val & TMR_CSR_RUN) { /* run? */ if (val & TMR_CSR_RUN) { /* run? */
if (val & TMR_CSR_XFR) /* new tir? */ if (val & TMR_CSR_XFR) /* new tir? */
sim_cancel (&tmr_unit); /* stop prev */ sim_cancel (&tmr_unit); /* stop prev */
if (!sim_is_active (&tmr_unit)) /* not running? */ if (!sim_is_active (&tmr_unit)) { /* not running? */
tmr_sched (); /* activate */ sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmr_sched (tmr_icr); /* activate */
}
} }
else if (val & TMR_CSR_SGL) { /* single step? */ else {
tmr_incr (1); /* incr tmr */ if (val & TMR_CSR_XFR) /* xfr set? */
if (tmr_icr == 0) /* if ovflo, */ tmr_icr = tmr_nicr;
tmr_icr = tmr_nicr; /* reload tir */ if (val & TMR_CSR_SGL) { /* single step? */
tmr_icr = tmr_icr + 1; /* incr tmr */
if (tmr_icr == 0) { /* if ovflo, */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_IE) { /* ie? */
tmr_int = 1; /* set int req */
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_incr() - INT=1\n");
}
tmr_icr = tmr_nicr; /* reload tir */
}
}
} }
if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */
(TMR_CSR_DON | TMR_CSR_IE)) { (TMR_CSR_DON | TMR_CSR_IE)) {
@ -691,24 +698,20 @@ if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */
sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n"); sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n");
} }
} }
return;
} }
int32 icr_rd (t_bool interp) int32 icr_rd (void)
{ {
uint32 delta; int32 result;
if (interp || (tmr_iccs & TMR_CSR_RUN)) { /* interp, running? */ if (tmr_iccs & TMR_CSR_RUN) { /* running? */
delta = sim_grtime () - tmr_sav; /* delta inst */ uint32 delta = sim_grtime() - tmr_sav;
if (tmr_use_100hz && (tmr_poll > TMR_INC)) /* scale large int */ result = (int32)(tmr_nicr + (uint32)((1000000.0 * delta) / sim_timer_inst_per_sec ()));
delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll);
if (delta >= tmr_inc)
delta = tmr_inc - 1;
sim_debug (TMR_DB_REG, &tmr_dev, "icr_rd() = 0x%08X\n", tmr_icr + delta);
return tmr_icr + delta;
} }
sim_debug (TMR_DB_REG, &tmr_dev, "icr_rd() = 0x%08X\n", tmr_icr); else
return tmr_icr; result = (int32)tmr_icr;
sim_debug (TMR_DB_REG, &tmr_dev, "icr_rd() = 0x%08X%s\n", result, (tmr_iccs & TMR_CSR_RUN) ? " - interpolated" : "");
return result;
} }
int32 nicr_rd (void) int32 nicr_rd (void)
@ -723,87 +726,52 @@ sim_debug (TMR_DB_REG, &tmr_dev, "nicr_wr(0x%08X)\n", val);
tmr_nicr = val; tmr_nicr = val;
} }
/* 100Hz base clock unit service */
t_stat clk_svc (UNIT *uptr)
{
sim_debug (TMR_DB_TICK, &tmr_dev, "clk_svc()\n");
tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate_after (uptr, 1000000/clk_tps); /* reactivate unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */
tmr_incr (TMR_INC); /* do timer service */
return SCPE_OK;
}
/* Interval timer unit service */ /* Interval timer unit service */
t_stat tmr_svc (UNIT *uptr) t_stat tmr_svc (UNIT *uptr)
{ {
sim_debug (TMR_DB_TICK, &tmr_dev, "tmr_svc()\n"); sim_debug (TMR_DB_TICK, &tmr_dev, "tmr_svc()\n");
tmr_incr (tmr_inc); /* incr timer */ tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) /* run? */
tmr_sched (tmr_nicr); /* reactivate */
if (tmr_iccs & TMR_CSR_IE) { /* ie? set int req */
tmr_int = 1;
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_svc() - INT=1\n");
}
else
tmr_int = 0;
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
return SCPE_OK; return SCPE_OK;
} }
/* Timer increment */
void tmr_incr (uint32 inc)
{
uint32 new_icr = (tmr_icr + inc) & LMASK; /* add incr */
if (new_icr < tmr_icr) { /* ovflo? */
tmr_icr = 0; /* now 0 */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) { /* run? */
tmr_icr = tmr_nicr; /* reload */
tmr_sched (); /* reactivate */
}
if (tmr_iccs & TMR_CSR_IE) { /* ie? set int req */
tmr_int = 1;
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_incr() - INT=1\n");
}
else tmr_int = 0;
}
else {
tmr_icr = new_icr; /* no, update icr */
if (tmr_iccs & TMR_CSR_RUN) /* still running? */
tmr_sched (); /* reactivate */
}
return;
}
/* Timer scheduling */ /* Timer scheduling */
void tmr_sched (void) void tmr_sched (uint32 nicr)
{ {
tmr_sav = sim_grtime (); /* save intvl base */ uint32 usecs = (nicr) ? (~nicr + 1) : 0xFFFFFFFF;
tmr_inc = (~tmr_icr + 1); /* inc = interval */
if (tmr_inc == 0) tmr_inc = 1; clk_tps = 1000000 / usecs;
sim_debug (TMR_DB_SCHED, &tmr_dev, "tmr_sched(0x%08X)\n", tmr_inc);
if (tmr_inc < TMR_INC) { /* 100Hz multiple? */ sim_debug (TMR_DB_SCHED, &tmr_dev, "tmr_sched(nicr=0x%08X-usecs=0x%08X) - tps=%d\n", nicr, usecs, clk_tps);
sim_activate (&tmr_unit, tmr_inc); /* schedule timer */ tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK);
tmr_use_100hz = 0; if (SCPE_OK == sim_activate_after (&tmr_unit, usecs))
} tmr_sav = sim_grtime(); /* Save interval base time */
else tmr_use_100hz = 1; /* let clk handle */
return;
} }
/* 100Hz clock reset */ /* 100Hz TODR reset */
t_stat clk_reset (DEVICE *dptr) t_stat clk_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&clk_unit, clk_unit.wait, TMR_CLK);/* init 100Hz timer */
sim_activate_after (&clk_unit, 1000000/clk_tps); /* activate 100Hz unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1); clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL) if (clk_unit.filebuf == NULL)
return SCPE_MEM; return SCPE_MEM;
todr_resync ();
} }
todr_resync ();
return SCPE_OK; return SCPE_OK;
} }
@ -903,13 +871,12 @@ return r;
t_stat tmr_reset (DEVICE *dptr) t_stat tmr_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
tmr_iccs = 0; tmr_iccs = 0;
tmr_icr = 0;
tmr_nicr = 0; tmr_nicr = 0;
tmr_int = 0; tmr_int = 0;
tmr_use_100hz = 1;
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
todr_resync (); /* resync TODR */
return SCPE_OK; return SCPE_OK;
} }
@ -936,7 +903,7 @@ sim_timespec_diff (&val, &now, &base);
if ((val.tv_sec >= TOY_MAX_SECS) || (!toy->toy_gmtbase))/* todr overflowed? */ if ((val.tv_sec >= TOY_MAX_SECS) || (!toy->toy_gmtbase))/* todr overflowed? */
return 0; /* stop counting */ return 0; /* stop counting */
sim_debug (TMR_DB_TODR, &tmr_dev, "todr_rd() - TODR=0x%X\n", (int32)(val.tv_sec*100 + val.tv_nsec/10000000));
return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */ return (int32)(val.tv_sec*100 + val.tv_nsec/10000000); /* 100hz Clock Ticks */
} }
@ -946,6 +913,7 @@ void todr_wr (int32 data)
TOY *toy = (TOY *)clk_unit.filebuf; TOY *toy = (TOY *)clk_unit.filebuf;
struct timespec now, val, base; struct timespec now, val, base;
sim_debug (TMR_DB_TODR, &tmr_dev, "todr_wr(0x%X)\n", data);
if (data) { if (data) {
/* Save the GMT time when set value was not 0 to record the base for /* Save the GMT time when set value was not 0 to record the base for
future read operations in "battery backed-up" state */ future read operations in "battery backed-up" state */

View file

@ -251,7 +251,7 @@ t_stat clk_detach (UNIT *uptr);
t_stat tmr_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr);
t_stat fl_svc (UNIT *uptr); t_stat fl_svc (UNIT *uptr);
t_stat fl_reset (DEVICE *dptr); t_stat fl_reset (DEVICE *dptr);
int32 icr_rd (); int32 icr_rd (void);
void tmr_sched (uint32 incr); void tmr_sched (uint32 incr);
t_stat todr_resync (void); t_stat todr_resync (void);
t_stat fl_wr_txdb (int32 data); t_stat fl_wr_txdb (int32 data);
@ -453,10 +453,11 @@ void rxcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tti_int = 0; tti_int = 0;
else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tti_int = 1; if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tti_int = 1;
}
tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR);
return;
} }
int32 rxdb_rd (void) int32 rxdb_rd (void)
@ -481,10 +482,11 @@ void txcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tto_int = 0; tto_int = 0;
else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tto_int = 1; if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tto_int = 1;
}
tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR); tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR);
return;
} }
void txdb_wr (int32 data) void txdb_wr (int32 data)
@ -494,8 +496,8 @@ tto_csr = tto_csr & ~CSR_DONE; /* clear flag */
tto_int = 0; /* clear int */ tto_int = 0; /* clear int */
if (tto_buf & TXDB_SEL) /* floppy? */ if (tto_buf & TXDB_SEL) /* floppy? */
fl_wr_txdb (tto_buf); fl_wr_txdb (tto_buf);
else sim_activate (&tto_unit, tto_unit.wait); /* no, console */ else
return; sim_activate (&tto_unit, tto_unit.wait); /* no, console */
} }
/* Terminal input service (poll for character) */ /* Terminal input service (poll for character) */
@ -513,7 +515,8 @@ if ((c = sim_poll_kbd ()) < SCPE_KFLAG) /* no char or error? */
return c; return c;
if (c & SCPE_BREAK) /* break? */ if (c & SCPE_BREAK) /* break? */
tti_buf = RXDB_ERR | RXDB_FRM; tti_buf = RXDB_ERR | RXDB_FRM;
else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); else
tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags));
tti_buftime = sim_os_msec (); tti_buftime = sim_os_msec ();
uptr->pos = uptr->pos + 1; uptr->pos = uptr->pos + 1;
tti_csr = tti_csr | CSR_DONE; tti_csr = tti_csr | CSR_DONE;
@ -602,10 +605,8 @@ return "console terminal output";
The architected VAX timer, which increments at 1Mhz, cannot be The architected VAX timer, which increments at 1Mhz, cannot be
accurately simulated due to the overhead that would be required accurately simulated due to the overhead that would be required
for 1M clock events per second. Instead, a hidden calibrated for 1M clock events per second. Instead 1Mhz intervals are
100Hz timer is run (because that's what VMS expects), and 1Mhz derived from the calibrated instruction execution rate.
intervals are derived from the calibrated instruction execution
rate.
If the interval register is read, then its value between events If the interval register is read, then its value between events
is interpolated relative to the elapsed instruction count. is interpolated relative to the elapsed instruction count.
@ -667,10 +668,9 @@ if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */
sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n"); sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n");
} }
} }
return;
} }
int32 icr_rd () int32 icr_rd (void)
{ {
int32 result; int32 result;
@ -732,7 +732,7 @@ if (SCPE_OK == sim_activate_after (&tmr_unit, usecs))
tmr_sav = sim_grtime(); /* Save interval base time */ tmr_sav = sim_grtime(); /* Save interval base time */
} }
/* 100Hz clock reset */ /* 100Hz TODR reset */
t_stat clk_reset (DEVICE *dptr) t_stat clk_reset (DEVICE *dptr)
{ {
@ -1072,7 +1072,8 @@ switch (fl_state) { /* case on state */
tti_int = 1; tti_int = 1;
fl_state = FL_EMPTY; /* go empty */ fl_state = FL_EMPTY; /* go empty */
} }
else fl_state = FL_DONE; /* error? cmd done */ else
fl_state = FL_DONE; /* error? cmd done */
sim_activate (uptr, fl_xwait); /* schedule next */ sim_activate (uptr, fl_xwait); /* schedule next */
break; break;
@ -1167,7 +1168,6 @@ if ((tti_csr & CSR_DONE) == 0) { /* input idle? */
} }
tti_buf = FL_CPROT; /* status */ tti_buf = FL_CPROT; /* status */
fl_state = FL_IDLE; /* floppy idle */ fl_state = FL_IDLE; /* floppy idle */
return;
} }
/* Reset */ /* Reset */

View file

@ -82,6 +82,18 @@
#define CLK_DELAY 5000 /* 100 Hz */ #define CLK_DELAY 5000 /* 100 Hz */
#define TMXR_MULT 1 /* 100 Hz */ #define TMXR_MULT 1 /* 100 Hz */
static BITFIELD tmr_iccs_bits [] = {
BIT(RUN), /* Run */
BITNCF(3), /* unused */
BIT(XFR), /* Transfer */
BIT(SGL), /* Single */
BIT(IE), /* Interrupt Enable */
BIT(DON), /* Done */
BITNCF(23), /* unused */
BIT(ERR), /* Error */
ENDBITS
};
/* Logical console definitions */ /* Logical console definitions */
#define LC_NUMBY 128 /* response buffer size */ #define LC_NUMBY 128 /* response buffer size */
@ -205,7 +217,6 @@ uint32 tmr_nicr = 0; /* next interval */
uint32 tmr_inc = 0; /* timer increment */ uint32 tmr_inc = 0; /* timer increment */
int32 tmr_sav = 0; /* timer save */ int32 tmr_sav = 0; /* timer save */
int32 tmr_int = 0; /* interrupt */ int32 tmr_int = 0; /* interrupt */
int32 tmr_use_100hz = 1; /* use 100Hz for timer */
int32 clk_tps = 100; /* ticks/second */ int32 clk_tps = 100; /* ticks/second */
int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */
int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ int32 tmr_poll = CLK_DELAY; /* pgm timer poll */
@ -237,7 +248,6 @@ uint16 *rlcs_buf = NULL;
t_stat tti_svc (UNIT *uptr); t_stat tti_svc (UNIT *uptr);
t_stat tto_svc (UNIT *uptr); t_stat tto_svc (UNIT *uptr);
t_stat clk_svc (UNIT *uptr);
t_stat tmr_svc (UNIT *uptr); t_stat tmr_svc (UNIT *uptr);
t_stat lc_svc (UNIT *uptr); t_stat lc_svc (UNIT *uptr);
t_stat rlcs_svc (UNIT *uptr); t_stat rlcs_svc (UNIT *uptr);
@ -257,9 +267,8 @@ t_stat clk_detach (UNIT *uptr);
t_stat tmr_reset (DEVICE *dptr); t_stat tmr_reset (DEVICE *dptr);
t_stat rlcs_reset (DEVICE *dptr); t_stat rlcs_reset (DEVICE *dptr);
t_stat rlcs_attach (UNIT *uptr, CONST char *cptr); t_stat rlcs_attach (UNIT *uptr, CONST char *cptr);
int32 icr_rd (t_bool interp); int32 icr_rd (void);
void tmr_incr (uint32 inc); void tmr_sched (uint32 incr);
void tmr_sched (void);
t_stat todr_resync (void); t_stat todr_resync (void);
t_stat lc_wr_txdb (int32 data); t_stat lc_wr_txdb (int32 data);
@ -349,12 +358,12 @@ DEVICE tto_dev = {
/* TODR and TMR data structures */ /* TODR and TMR data structures */
UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE+UNIT_FIX, sizeof(TOY)), CLK_DELAY };/* 100Hz */ UNIT clk_unit = { UDATA (NULL, UNIT_FIX, sizeof(TOY))};
REG clk_reg[] = { REG clk_reg[] = {
{ DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT }, { DRDATAD (TIME, clk_unit.wait, 24, "initial poll interval"), REG_NZ + PV_LEFT },
{ DRDATAD (POLL, tmr_poll, 24, "calibrated poll interval"), REG_NZ + PV_LEFT + REG_HRO }, { DRDATAD (POLL, tmr_poll, 24, "calibrated poll interval"), REG_NZ + PV_LEFT + REG_HRO },
{ DRDATAD (TPS, clk_tps, 8, "ticks per second (100)"), REG_NZ + PV_LEFT }, { DRDATAD (TPS, clk_tps, 8, "ticks per second"), REG_NZ + PV_LEFT },
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
{ DRDATAD (ASYNCH, sim_asynch_enabled, 1, "asynch I/O enabled flag"), PV_LEFT }, { DRDATAD (ASYNCH, sim_asynch_enabled, 1, "asynch I/O enabled flag"), PV_LEFT },
{ DRDATAD (LATENCY, sim_asynch_latency, 32, "desired asynch interrupt latency"), PV_LEFT }, { DRDATAD (LATENCY, sim_asynch_latency, 32, "desired asynch interrupt latency"), PV_LEFT },
@ -381,16 +390,31 @@ REG tmr_reg[] = {
{ FLDATAD (INT, tmr_int, 0, "interrupt request") }, { FLDATAD (INT, tmr_int, 0, "interrupt request") },
{ HRDATA (INCR, tmr_inc, 32), REG_HIDDEN }, { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN },
{ HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN }, { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN },
{ FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN },
{ NULL } { NULL }
}; };
#define TMR_DB_REG 0x01 /* Register Access */
#define TMR_DB_TICK 0x02 /* Ticks */
#define TMR_DB_SCHED 0x04 /* Scheduling */
#define TMR_DB_INT 0x08 /* Interrupts */
#define TMR_DB_TODR 0x10 /* TODR */
DEBTAB tmr_deb[] = {
{ "REG", TMR_DB_REG, "Register Access"},
{ "TICK", TMR_DB_TICK, "Ticks"},
{ "SCHED", TMR_DB_SCHED, "Ticks"},
{ "INT", TMR_DB_INT, "Interrupts"},
{ "TODR", TMR_DB_TODR, "TODR activities"},
{ NULL, 0 }
};
DEVICE tmr_dev = { DEVICE tmr_dev = {
"TMR", &tmr_unit, tmr_reg, NULL, "TMR", &tmr_unit, tmr_reg, NULL,
1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
NULL, NULL, &tmr_reset, NULL, NULL, &tmr_reset,
NULL, NULL, NULL, NULL, NULL, NULL,
NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_DEBUG, 0,
tmr_deb, NULL, NULL, NULL, NULL, NULL,
&tmr_description &tmr_description
}; };
@ -444,10 +468,11 @@ void rxcs_wr (int32 data)
{ {
if ((data & CSR_IE) == 0) if ((data & CSR_IE) == 0)
tti_int = 0; tti_int = 0;
else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) else {
tti_int = 1; if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE)
tti_int = 1;
}
tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR);
return;
} }
int32 rxdb_rd (void) int32 rxdb_rd (void)
@ -500,10 +525,10 @@ if (data & TXCS_WMN) { /* Updating enable m
} }
if ((tto_csr & CSR_IE) == 0) if ((tto_csr & CSR_IE) == 0)
tto_int = 0; tto_int = 0;
else else {
if ((tto_csr & CSR_DONE) == CSR_DONE) if ((tto_csr & CSR_DONE) == CSR_DONE)
tto_int = 1; tto_int = 1;
return; }
} }
void txdb_wr (int32 data) void txdb_wr (int32 data)
@ -517,7 +542,6 @@ if ((dest >= ID_CT) && (dest <= ID_LC)) { /* valid line? */
sim_activate (&tto_unit[dest], sim_activate (&tto_unit[dest],
((dest == ID_LC) && (data == LC_FNCBT)) ? 0 : tto_unit[dest].wait);/* activate unit */ ((dest == ID_LC) && (data == LC_FNCBT)) ? 0 : tto_unit[dest].wait);/* activate unit */
} }
return;
} }
int32 stxcs_rd (void) int32 stxcs_rd (void)
@ -591,7 +615,7 @@ int32 line = uptr - tti_dev.units;
switch (line) { switch (line) {
case ID_CT: /* console terminal */ case ID_CT: /* console terminal */
sim_clock_coschedule_tmr (uptr, TMR_CLK, TMXR_MULT);/* continue poll */ sim_clock_coschedule (uptr, TMXR_MULT); /* continue poll */
if ((tti_csr & CSR_DONE) && /* input still pending and < 500ms? */ if ((tti_csr & CSR_DONE) && /* input still pending and < 500ms? */
((sim_os_msec () - tti_buftime) < 500)) ((sim_os_msec () - tti_buftime) < 500))
return SCPE_OK; return SCPE_OK;
@ -631,7 +655,7 @@ tmxr_set_console_units (tti_unit, tto_unit);
tti_buf = 0; tti_buf = 0;
tti_csr = 0; tti_csr = 0;
tti_int = 0; tti_int = 0;
sim_activate_abs (&tti_unit[ID_CT], KBD_WAIT (tti_unit[ID_CT].wait, tmr_poll)); sim_activate (&tti_unit[ID_CT], KBD_WAIT (tti_unit[ID_CT].wait, tmr_poll));
return SCPE_OK; return SCPE_OK;
} }
@ -716,162 +740,143 @@ return "console terminal output";
The architected VAX timer, which increments at 1Mhz, cannot be The architected VAX timer, which increments at 1Mhz, cannot be
accurately simulated due to the overhead that would be required accurately simulated due to the overhead that would be required
for 1M clock events per second. Instead, a hidden calibrated for 1M clock events per second. Instead 1Mhz intervals are
100Hz timer is run (because that's what VMS expects), and a derived from the calibrated instruction execution rate.
hack is used for the interval timer.
When the timer is started, the timer interval is inspected.
if the interval is >= 10msec, then the 100Hz timer drives the
next interval
if the interval is < 10mec, then count instructions
If the interval register is read, then its value between events If the interval register is read, then its value between events
is interpolated using the current instruction count versus the is interpolated relative to the elapsed instruction count.
count when the most recent event started, the result is scaled
to the calibrated system clock, unless the interval being timed
is less than a calibrated system clock tick (or the calibrated
clock is running very slowly) at which time the result will be
the elapsed instruction count.
*/ */
int32 iccs_rd (void) int32 iccs_rd (void)
{ {
sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_rd()", tmr_iccs_bits, tmr_iccs, tmr_iccs, TRUE);
return tmr_iccs & TMR_CSR_RD; return tmr_iccs & TMR_CSR_RD;
} }
void iccs_wr (int32 val) void iccs_wr (int32 val)
{ {
sim_debug_bits_hdr (TMR_DB_REG, &tmr_dev, "iccs_wr()", tmr_iccs_bits, tmr_iccs, val, TRUE);
if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
tmr_use_100hz = 0; if (tmr_iccs & TMR_CSR_RUN) { /* run 1 -> 0? */
if (tmr_iccs & TMR_CSR_RUN) /* run 1 -> 0? */ tmr_icr = icr_rd (); /* update itr */
tmr_icr = icr_rd (TRUE); /* update itr */ sim_rtcn_calb (0, TMR_CLK); /* stop timer */
}
} }
if (val & CSR_DONE) /* Interrupt Acked? */ if (val & CSR_DONE) /* Interrupt Acked? */
sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */ sim_rtcn_tick_ack (20, TMR_CLK); /* Let timers know */
tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */ tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */
tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */ tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */
(val & TMR_CSR_WR); (val & TMR_CSR_WR);
if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr; /* xfr set? */ if (val & TMR_CSR_XFR) /* xfr set? */
tmr_icr = tmr_nicr;
if (val & TMR_CSR_RUN) { /* run? */ if (val & TMR_CSR_RUN) { /* run? */
if (val & TMR_CSR_XFR) /* new tir? */ if (val & TMR_CSR_XFR) /* new tir? */
sim_cancel (&tmr_unit); /* stop prev */ sim_cancel (&tmr_unit); /* stop prev */
if (!sim_is_active (&tmr_unit)) /* not running? */ if (!sim_is_active (&tmr_unit)) { /* not running? */
tmr_sched (); /* activate */ sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmr_sched (tmr_icr); /* activate */
}
} }
else if (val & TMR_CSR_SGL) { /* single step? */ else {
tmr_incr (1); /* incr tmr */ if (val & TMR_CSR_XFR) /* xfr set? */
if (tmr_icr == 0) /* if ovflo, */ tmr_icr = tmr_nicr;
tmr_icr = tmr_nicr; /* reload tir */ if (val & TMR_CSR_SGL) { /* single step? */
tmr_icr = tmr_icr + 1; /* incr tmr */
if (tmr_icr == 0) { /* if ovflo, */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_IE) { /* ie? */
tmr_int = 1; /* set int req */
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_incr() - INT=1\n");
}
tmr_icr = tmr_nicr; /* reload tir */
}
}
} }
if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */
(TMR_CSR_DON | TMR_CSR_IE)) (TMR_CSR_DON | TMR_CSR_IE)) {
tmr_int = 0; if (tmr_int) {
return; tmr_int = 0;
sim_debug (TMR_DB_INT, &tmr_dev, "iccs_wr() - INT=0\n");
}
}
} }
int32 icr_rd (t_bool interp) int32 icr_rd (void)
{ {
uint32 delta; int32 result;
if (interp || (tmr_iccs & TMR_CSR_RUN)) { /* interp, running? */ if (tmr_iccs & TMR_CSR_RUN) { /* running? */
delta = sim_grtime () - tmr_sav; /* delta inst */ uint32 delta = sim_grtime() - tmr_sav;
if (tmr_use_100hz && (tmr_poll > TMR_INC)) /* scale large int */ result = (int32)(tmr_nicr + (uint32)((1000000.0 * delta) / sim_timer_inst_per_sec ()));
delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll);
if (delta >= tmr_inc)
delta = tmr_inc - 1;
return tmr_icr + delta;
} }
return tmr_icr; else
result = (int32)tmr_icr;
sim_debug (TMR_DB_REG, &tmr_dev, "icr_rd() = 0x%08X%s\n", result, (tmr_iccs & TMR_CSR_RUN) ? " - interpolated" : "");
return result;
} }
int32 nicr_rd (void) int32 nicr_rd (void)
{ {
sim_debug (TMR_DB_REG, &tmr_dev, "nicr_rd() = 0x%08X\n", tmr_nicr);
return tmr_nicr; return tmr_nicr;
} }
void nicr_wr (int32 val) void nicr_wr (int32 val)
{ {
sim_debug (TMR_DB_REG, &tmr_dev, "nicr_wr(0x%08X)\n", val);
tmr_nicr = val; tmr_nicr = val;
} }
/* 100Hz base clock unit service */
t_stat clk_svc (UNIT *uptr)
{
tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */
sim_activate_after (&clk_unit, 1000000/clk_tps); /* reactivate unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */
tmr_incr (TMR_INC); /* do timer service */
return SCPE_OK;
}
/* Interval timer unit service */ /* Interval timer unit service */
t_stat tmr_svc (UNIT *uptr) t_stat tmr_svc (UNIT *uptr)
{ {
tmr_incr (tmr_inc); /* incr timer */ sim_debug (TMR_DB_TICK, &tmr_dev, "tmr_svc()\n");
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else
tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) /* run? */
tmr_sched (tmr_nicr); /* reactivate */
if (tmr_iccs & TMR_CSR_IE) { /* ie? set int req */
tmr_int = 1;
sim_debug (TMR_DB_INT, &tmr_dev, "tmr_svc() - INT=1\n");
}
else
tmr_int = 0;
AIO_SET_INTERRUPT_LATENCY(tmr_poll*clk_tps); /* set interrrupt latency */
return SCPE_OK; return SCPE_OK;
} }
/* Timer increment */
void tmr_incr (uint32 inc)
{
uint32 new_icr = (tmr_icr + inc) & LMASK; /* add incr */
if (new_icr < tmr_icr) { /* ovflo? */
tmr_icr = 0; /* now 0 */
if (tmr_iccs & TMR_CSR_DON) /* done? set err */
tmr_iccs = tmr_iccs | TMR_CSR_ERR;
else tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */
if (tmr_iccs & TMR_CSR_RUN) { /* run? */
tmr_icr = tmr_nicr; /* reload */
tmr_sched (); /* reactivate */
}
if (tmr_iccs & TMR_CSR_IE) /* ie? set int req */
tmr_int = 1;
else tmr_int = 0;
}
else {
tmr_icr = new_icr; /* no, update icr */
if (tmr_iccs & TMR_CSR_RUN) /* still running? */
tmr_sched (); /* reactivate */
}
return;
}
/* Timer scheduling */ /* Timer scheduling */
void tmr_sched (void) void tmr_sched (uint32 nicr)
{ {
tmr_sav = sim_grtime (); /* save intvl base */ uint32 usecs = (nicr) ? (~nicr + 1) : 0xFFFFFFFF;
tmr_inc = (~tmr_icr + 1); /* inc = interval */
if (tmr_inc == 0) tmr_inc = 1; clk_tps = 1000000 / usecs;
if (tmr_inc < TMR_INC) { /* 100Hz multiple? */
sim_activate (&tmr_unit, tmr_inc); /* schedule timer */ sim_debug (TMR_DB_SCHED, &tmr_dev, "tmr_sched(nicr=0x%08X-usecs=0x%08X) - tps=%d\n", nicr, usecs, clk_tps);
tmr_use_100hz = 0; tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK);
} if (SCPE_OK == sim_activate_after (&tmr_unit, usecs))
else tmr_use_100hz = 1; /* let clk handle */ tmr_sav = sim_grtime(); /* Save interval base time */
return;
} }
/* 100Hz clock reset */ /* 100Hz TODR reset */
t_stat clk_reset (DEVICE *dptr) t_stat clk_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&clk_unit, clk_unit.wait, TMR_CLK);/* init 100Hz timer */
sim_activate_after (&clk_unit, 1000000/clk_tps); /* activate 100Hz unit */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */ if (clk_unit.filebuf == NULL) { /* make sure the TODR is initialized */
clk_unit.filebuf = calloc(sizeof(TOY), 1); clk_unit.filebuf = calloc(sizeof(TOY), 1);
if (clk_unit.filebuf == NULL) if (clk_unit.filebuf == NULL)
return SCPE_MEM; return SCPE_MEM;
todr_resync ();
} }
todr_resync ();
return SCPE_OK; return SCPE_OK;
} }
@ -970,13 +975,12 @@ return r;
t_stat tmr_reset (DEVICE *dptr) t_stat tmr_reset (DEVICE *dptr)
{ {
tmr_poll = sim_rtcn_init_unit (&tmr_unit, CLK_DELAY, TMR_CLK); /* init timer */
tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */
tmr_iccs = 0; tmr_iccs = 0;
tmr_icr = 0;
tmr_nicr = 0; tmr_nicr = 0;
tmr_int = 0; tmr_int = 0;
tmr_use_100hz = 1;
sim_cancel (&tmr_unit); /* cancel timer */ sim_cancel (&tmr_unit); /* cancel timer */
todr_resync (); /* resync TODR */
return SCPE_OK; return SCPE_OK;
} }
@ -1220,7 +1224,8 @@ switch (rlcs_state) {
rlcs_csr = rlcs_csr | RLCS_ERR; /* set master error bit */ rlcs_csr = rlcs_csr | RLCS_ERR; /* set master error bit */
if (rlcs_bcnt > 0) /* transfer in progress? */ if (rlcs_bcnt > 0) /* transfer in progress? */
rlcs_csr = rlcs_csr & ~RLCS_DRDY; rlcs_csr = rlcs_csr & ~RLCS_DRDY;
else rlcs_csr = rlcs_csr | RLCS_DRDY; else
rlcs_csr = rlcs_csr | RLCS_DRDY;
cso_buf = rlcs_csr; cso_buf = rlcs_csr;
rlcs_sts_reg = RL_MP; /* MP on next read */ rlcs_sts_reg = RL_MP; /* MP on next read */
break; break;
@ -1228,7 +1233,8 @@ switch (rlcs_state) {
case RL_MP: case RL_MP:
if ((uptr->flags & UNIT_ATT) == 0) /* update status */ if ((uptr->flags & UNIT_ATT) == 0) /* update status */
rlcs_mp = RLDS_UNATT; rlcs_mp = RLDS_UNATT;
else rlcs_mp = RLDS_ATT; else
rlcs_mp = RLDS_ATT;
cso_buf = rlcs_mp; cso_buf = rlcs_mp;
rlcs_sts_reg = RL_CSR; /* MP on next read */ rlcs_sts_reg = RL_CSR; /* MP on next read */
break; break;