PDP-10 Update: TIM
Fix subtle errors in timer/timebase implementation. Document mechanisms. Reduce magic constants by using what the OS sets via wrint.
This commit is contained in:
parent
6243f9314f
commit
6e78905ecc
1 changed files with 200 additions and 60 deletions
|
@ -41,42 +41,102 @@
|
||||||
#include "pdp10_defs.h"
|
#include "pdp10_defs.h"
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
/* The KS timer works off a 4.100 MHz (243.9024 nsec) oscillator that
|
||||||
|
* is independent of all other system timing.
|
||||||
|
*
|
||||||
|
* Two pieces of timekeeping hardware are exposed to the OS.
|
||||||
|
* o The interval timer, which can interrupt at a programmed interval.
|
||||||
|
* o The timebase, which records time (71 bits).
|
||||||
|
*
|
||||||
|
* The clock is architecturally readable in units of 243.9024 nsec via
|
||||||
|
* the timebase. The implementation is somewhat different.
|
||||||
|
*
|
||||||
|
* The instructions that update the clocks specify time in these units.
|
||||||
|
*
|
||||||
|
* However, both timekeepers are incremented by the microcode when
|
||||||
|
* a 12 bit counter overflows; e.g. at a period of 999.0244 usec.
|
||||||
|
* Thus, the granularity of timer interrupts is approximately 1 msec.
|
||||||
|
*
|
||||||
|
* The OS programs the interval timer to interrupt as though the
|
||||||
|
* the 12 least significant bits mattered. Thus, for a (roughly)
|
||||||
|
* 1 msec interval, it would program 1 * 4096 into the interval timer.
|
||||||
|
* The sign bit is not used, so 35-12 = 23 bits for the maximum interval,
|
||||||
|
* which is 139.674 minutes. If any of the least significant bits
|
||||||
|
* are non-zero, the interval is extended by 1 * 4096 counts.
|
||||||
|
*
|
||||||
|
* The timer merely sets the INTERVAL DONE flag in the APR flags.
|
||||||
|
* Whether that actually causes an interrupt is controlled by the
|
||||||
|
* APR interrupt enable for the flag and by the PI system.
|
||||||
|
*
|
||||||
|
* The flag is readable as an APR condition by RDAPR, and CONSO/Z APR,.
|
||||||
|
* The flag is cleared by WRAPR 1b22!1b30 (clear, count done).
|
||||||
|
*
|
||||||
|
* The timebase is maintained with the 12 LSB zero in a workspace
|
||||||
|
* register. When read by the OS, the actual value of the 10 MSB of
|
||||||
|
* the hardware counter is inserted into those bits, providing increased
|
||||||
|
* resolution. Although the system reference manual says otherwise, the
|
||||||
|
* two LSB of the counter are read as zero by the microcode (DPM2), so
|
||||||
|
* bits <70:71> of the timebase are also read as zero by software.
|
||||||
|
*
|
||||||
|
* When the OS sets the timebase, the 12 LSB that it supplies are ignored.
|
||||||
|
*
|
||||||
|
* The timebase is typically used for accurate time of day and CPU runtime
|
||||||
|
* accounting. The simulator adjusts the equivalent of the 12-bit counter,
|
||||||
|
* so CPU time will reflect simulator wall clock, not simulated machine cycles.
|
||||||
|
* Since time of day must be accurate, this may result in the OS reporting
|
||||||
|
* CPU times that are unrealistically faster - or slower - than on the
|
||||||
|
* real hardware.
|
||||||
|
*
|
||||||
|
* This module also implements the TCU, a battery backed-up TOY clock
|
||||||
|
* that was supported by TOPS-10, but not sold by DEC.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Invariants */
|
/* Invariants */
|
||||||
|
|
||||||
#define TIM_HW_FREQ 4100000 /* 4.1Mhz */
|
#define TIM_HW_FREQ 4100000 /* 4.1Mhz */
|
||||||
#define TIM_HWRE_MASK 07777
|
#define TIM_HWRE_MASK 07777 /* Timer field of timebase */
|
||||||
|
#define TIM_BASE_RAZ 03 /* Timer bits read as zero by ucode */
|
||||||
#define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */
|
#define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */
|
||||||
#define UNIT_Y2K (1u << UNIT_V_Y2K)
|
#define UNIT_Y2K (1u << UNIT_V_Y2K)
|
||||||
|
|
||||||
|
#define TIM_TMXR_FREQ 60 /* Target frequency (HZ) for tmxr polls */
|
||||||
|
|
||||||
|
/* Estimate of simulator instructions/sec for initialization and fixed timing.
|
||||||
|
* This came from prior magic constant of 8000 at 60 tics/sec.
|
||||||
|
* The machine was marketed as ~ 300KIPs, which would imply 3 usec/instr.
|
||||||
|
* So 8,000 instructions should take ~24 msec. This would indicate that
|
||||||
|
* the simulator from which this came was ~1.4 x the speed of the real
|
||||||
|
* hardware. Current milage will vary.
|
||||||
|
*/
|
||||||
|
#define TIM_WAIT_IPS 480000
|
||||||
|
|
||||||
/* Clock mode TOPS-10/ITS */
|
/* Clock mode TOPS-10/ITS */
|
||||||
|
|
||||||
#define TIM_TPS_T10 60
|
#define TIM_TPS_T10 60 /* Initial frequency guess for TOPS-10 (close, not exact) */
|
||||||
#define TIM_WAIT_T10 8000
|
#define TIM_ITS_QUANT (TIM_HW_FREQ / TIM_TPS_T10) /* ITS PC sampling and user runtime interval */
|
||||||
#define TIM_MULT_T10 1
|
|
||||||
#define TIM_ITS_QUANT (TIM_HW_FREQ / TIM_TPS_T10)
|
|
||||||
|
|
||||||
/* Clock mode TOPS-20/KLAD */
|
/* Clock mode TOPS-20/KLAD */
|
||||||
|
|
||||||
#define TIM_TPS_T20 1001
|
#define TIM_TPS_T20 1000 /* Initial estimate for TOPS-20 - 1msec seems fast? */
|
||||||
#define TIM_WAIT_T20 500
|
|
||||||
#define TIM_MULT_T20 16
|
|
||||||
|
|
||||||
/* Probability function for TOPS-20 idlelock */
|
/* Probability function for TOPS-20 idlelock */
|
||||||
|
|
||||||
#define PROB(x) (((rand() * 100) / RAND_MAX) >= (x))
|
#define PROB(x) (((rand() * 100) / RAND_MAX) >= (x))
|
||||||
|
|
||||||
d10 tim_base[2] = { 0, 0 }; /* 71b timebase */
|
static d10 tim_base[2] = { 0, 0 }; /* 71b timebase */
|
||||||
d10 tim_ttg = 0; /* time to go */
|
static d10 tim_interval = 0; /* value programmed into the clock */
|
||||||
d10 tim_period = 0; /* period */
|
static d10 tim_period = 0; /* period in HW ticks adjusted for non-zero LSBs */
|
||||||
|
static d10 tim_new_period = 0; /* period for the next interval */
|
||||||
|
static int32 tim_mult; /* Multiple of interval timer period at which tmxr is polled */
|
||||||
|
|
||||||
d10 quant = 0; /* ITS quantum */
|
d10 quant = 0; /* ITS quantum */
|
||||||
int32 tim_mult = TIM_MULT_T10; /* tmxr poll mult */
|
static int32 tim_t20_prob = 33; /* TOPS-20 prob */
|
||||||
int32 tim_t20_prob = 33; /* TOPS-20 prob */
|
|
||||||
|
|
||||||
/* Exported variables */
|
/* Exported variables - initialized by set CPU model and reset */
|
||||||
|
|
||||||
int32 clk_tps = TIM_TPS_T10; /* clock ticks/sec */
|
int32 clk_tps; /* Interval clock ticks/sec */
|
||||||
int32 tmr_poll = TIM_WAIT_T10; /* clock poll */
|
int32 tmr_poll; /* SimH instructions/clock service */
|
||||||
int32 tmxr_poll = TIM_WAIT_T10 * TIM_MULT_T10; /* term mux poll */
|
int32 tmxr_poll; /* SimH instructions/term mux poll */
|
||||||
|
|
||||||
extern int32 apr_flg, pi_act;
|
extern int32 apr_flg, pi_act;
|
||||||
extern UNIT cpu_unit;
|
extern UNIT cpu_unit;
|
||||||
|
@ -85,10 +145,11 @@ extern a10 pager_PC;
|
||||||
extern int32 t20_idlelock;
|
extern int32 t20_idlelock;
|
||||||
|
|
||||||
DEVICE tim_dev;
|
DEVICE tim_dev;
|
||||||
t_stat tcu_rd (int32 *data, int32 PA, int32 access);
|
static t_stat tcu_rd (int32 *data, int32 PA, int32 access);
|
||||||
t_stat tim_svc (UNIT *uptr);
|
static t_stat tim_svc (UNIT *uptr);
|
||||||
t_stat tim_reset (DEVICE *dptr);
|
static t_stat tim_reset (DEVICE *dptr);
|
||||||
void tim_incr_base (d10 *base, d10 incr);
|
static t_bool update_interval (d10 new_interval);
|
||||||
|
static void tim_incr_base (d10 *base, d10 incr);
|
||||||
|
|
||||||
extern d10 Read (a10 ea, int32 prv);
|
extern d10 Read (a10 ea, int32 prv);
|
||||||
extern d10 ReadM (a10 ea, int32 prv);
|
extern d10 ReadM (a10 ea, int32 prv);
|
||||||
|
@ -106,11 +167,10 @@ extern t_stat wr_nop (int32 data, int32 PA, int32 access);
|
||||||
|
|
||||||
DIB tcu_dib = { IOBA_TCU, IOLN_TCU, &tcu_rd, &wr_nop, 0 };
|
DIB tcu_dib = { IOBA_TCU, IOLN_TCU, &tcu_rd, &wr_nop, 0 };
|
||||||
|
|
||||||
UNIT tim_unit = { UDATA (&tim_svc, UNIT_IDLE, 0), TIM_WAIT_T10 };
|
static UNIT tim_unit = { UDATA (&tim_svc, UNIT_IDLE, 0), 0 };
|
||||||
|
|
||||||
REG tim_reg[] = {
|
static REG tim_reg[] = {
|
||||||
{ BRDATA (TIMEBASE, tim_base, 8, 36, 2) },
|
{ BRDATA (TIMEBASE, tim_base, 8, 36, 2) },
|
||||||
{ ORDATA (TTG, tim_ttg, 36) },
|
|
||||||
{ ORDATA (PERIOD, tim_period, 36) },
|
{ ORDATA (PERIOD, tim_period, 36) },
|
||||||
{ ORDATA (QUANT, quant, 36) },
|
{ ORDATA (QUANT, quant, 36) },
|
||||||
{ DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT },
|
{ DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT },
|
||||||
|
@ -122,7 +182,7 @@ REG tim_reg[] = {
|
||||||
{ NULL }
|
{ NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
MTAB tim_mod[] = {
|
static MTAB tim_mod[] = {
|
||||||
{ UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL },
|
{ UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL },
|
||||||
{ UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL },
|
{ UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL },
|
||||||
{ MTAB_XTD|MTAB_VDV, 000, "ADDRESS", NULL,
|
{ MTAB_XTD|MTAB_VDV, 000, "ADDRESS", NULL,
|
||||||
|
@ -140,26 +200,53 @@ DEVICE tim_dev = {
|
||||||
|
|
||||||
/* Timer instructions */
|
/* Timer instructions */
|
||||||
|
|
||||||
/* Timer - if the timer is running at less than hardware frequency,
|
/* Timebase - the timer is always running at less than hardware frequency,
|
||||||
need to interpolate the value by calculating how much of the current
|
* need to interpolate the value by calculating how much of the current
|
||||||
clock tick has elapsed, and what that equates to in msec. */
|
* clock tick has elapsed, and what that equates to in sysfreq units.
|
||||||
|
*/
|
||||||
|
|
||||||
t_bool rdtim (a10 ea, int32 prv)
|
t_bool rdtim (a10 ea, int32 prv)
|
||||||
{
|
{
|
||||||
d10 tempbase[2];
|
d10 tempbase[2];
|
||||||
|
|
||||||
ReadM (INCA (ea), prv); /* check 2nd word */
|
|
||||||
tempbase[0] = tim_base[0]; /* copy time base */
|
|
||||||
tempbase[1] = tim_base[1];
|
|
||||||
if (tim_mult != TIM_MULT_T20) { /* interpolate? */
|
|
||||||
int32 used;
|
int32 used;
|
||||||
d10 incr;
|
d10 incr;
|
||||||
|
|
||||||
|
tempbase[0] = tim_base[0]; /* copy time base */
|
||||||
|
tempbase[1] = tim_base[1];
|
||||||
|
|
||||||
|
/* used = time remaining in this service interval (instructions)
|
||||||
|
* poll = instructions in the whole service interval.
|
||||||
|
* incr = fraction of interval consumed * HW ticks per interval.
|
||||||
|
* Thus, incr is approximate number of HW ticks to add to the timebase
|
||||||
|
* value returned. This does NOT update the timebase.
|
||||||
|
*/
|
||||||
used = tmr_poll - (sim_activate_time (&tim_unit) - 1);
|
used = tmr_poll - (sim_activate_time (&tim_unit) - 1);
|
||||||
incr = (d10) (((double) used * TIM_HW_FREQ) /
|
incr = (d10) (((double) used * TIM_HW_FREQ) /
|
||||||
((double) tmr_poll * (double) clk_tps));
|
((double) tmr_poll * (double) tim_period));
|
||||||
tim_incr_base (tempbase, incr);
|
tim_incr_base (tempbase, incr);
|
||||||
}
|
|
||||||
tempbase[0] = tempbase[0] & ~((d10) TIM_HWRE_MASK); /* clear low 12b */
|
/* Although the two LSB of the counter contribute carry to the
|
||||||
|
* value, they are read as zero by microcode, and thus cleared here.
|
||||||
|
*
|
||||||
|
* The reason that these bits are forced to zero in the hardware is
|
||||||
|
* that the counter is in a different clock domain from the microcode.
|
||||||
|
* To make the domain crossing, the microcode reads the counter
|
||||||
|
* until two consecutive values match.
|
||||||
|
*
|
||||||
|
* Since the microcode cycle time is 300 nsec, the LSBs of the
|
||||||
|
* counter run too fast (244 nsec) for the strategy to work.
|
||||||
|
* Ignoring the two LSB ensures that the value can't change any
|
||||||
|
* faster than ~976 nsec, which guarantees a stable value can be
|
||||||
|
* obtained in at most three attempts.
|
||||||
|
*/
|
||||||
|
tempbase[1] &= ~((d10) TIM_BASE_RAZ);
|
||||||
|
|
||||||
|
/* If the destination is arranged so that the first word is OK, but
|
||||||
|
* the second pagefaults, the value will be half-written. As we
|
||||||
|
* expect the PFH to restart the instruction, the both halves will
|
||||||
|
* be written the second time. We could read and write back both
|
||||||
|
* halves to avoid this, but the hardware doesn't seem to either.
|
||||||
|
*/
|
||||||
Write (ea, tempbase[0], prv);
|
Write (ea, tempbase[0], prv);
|
||||||
Write (INCA(ea), tempbase[1], prv);
|
Write (INCA(ea), tempbase[1], prv);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -168,35 +255,67 @@ return FALSE;
|
||||||
t_bool wrtim (a10 ea, int32 prv)
|
t_bool wrtim (a10 ea, int32 prv)
|
||||||
{
|
{
|
||||||
tim_base[0] = Read (ea, prv);
|
tim_base[0] = Read (ea, prv);
|
||||||
tim_base[1] = CLRS (Read (INCA (ea), prv));
|
tim_base[1] = CLRS (Read (INCA (ea), prv) & ~((d10) TIM_HWRE_MASK));
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
t_bool rdint (a10 ea, int32 prv)
|
t_bool rdint (a10 ea, int32 prv)
|
||||||
{
|
{
|
||||||
Write (ea, tim_period, prv);
|
Write (ea, tim_interval, prv);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* write a new interval timer period (in timer ticks).
|
||||||
|
* This does not clear the harware counter, so the first
|
||||||
|
* completion can come up to ~1 msc later than the new
|
||||||
|
* period.
|
||||||
|
*/
|
||||||
|
|
||||||
t_bool wrint (a10 ea, int32 prv)
|
t_bool wrint (a10 ea, int32 prv)
|
||||||
{
|
{
|
||||||
tim_period = Read (ea, prv);
|
tim_interval = CLRS (Read (ea, prv));
|
||||||
tim_ttg = tim_period;
|
return update_interval (tim_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static t_bool update_interval (d10 new_interval)
|
||||||
|
{
|
||||||
|
tim_new_period = CLRS (new_interval + ((new_interval & TIM_HWRE_MASK)? 010000 : 0));
|
||||||
|
if (!(tim_new_period & ~TIM_HWRE_MASK))
|
||||||
|
tim_new_period = 010000;
|
||||||
|
|
||||||
|
clk_tps = (int32) (0.5 + ( ((double)TIM_HW_FREQ) / (double)tim_new_period ));
|
||||||
|
|
||||||
|
/* tmxr is polled every tim_mult clks. Compute the divisor matching the target. */
|
||||||
|
|
||||||
|
tim_mult = (clk_tps <= TIM_TMXR_FREQ)? 1 : ((clk_tps + (TIM_TMXR_FREQ-1)) / TIM_TMXR_FREQ);
|
||||||
|
|
||||||
|
/* Estimate instructions/tick for fixed timing */
|
||||||
|
|
||||||
|
tim_unit.wait = TIM_WAIT_IPS / clk_tps;
|
||||||
|
|
||||||
|
tmxr_poll = tim_unit.wait * tim_mult;
|
||||||
|
|
||||||
|
/* The next tim_svc will update the activation time.
|
||||||
|
*
|
||||||
|
*/
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Timer service - the timer is only serviced when the 'ttg' register
|
/* Timer service - the timer is only serviced when the interval
|
||||||
has reached 0 based on the expected frequency of clock interrupts. */
|
* programmed in tim_period by wrint expires. If the interval
|
||||||
|
* changes, the timebase update is based on the previous interval.
|
||||||
|
* The interval calibration is based on what the new interval will be.
|
||||||
|
*/
|
||||||
|
|
||||||
t_stat tim_svc (UNIT *uptr)
|
static t_stat tim_svc (UNIT *uptr)
|
||||||
{
|
{
|
||||||
if (cpu_unit.flags & UNIT_KLAD) /* diags? */
|
if (cpu_unit.flags & UNIT_KLAD) /* diags? */
|
||||||
tmr_poll = uptr->wait; /* fixed clock */
|
tmr_poll = uptr->wait; /* fixed clock */
|
||||||
else tmr_poll = sim_rtc_calb (clk_tps); /* else calibrate */
|
else tmr_poll = sim_rtc_calb (clk_tps); /* else calibrate */
|
||||||
sim_activate (uptr, tmr_poll); /* reactivate unit */
|
sim_activate (uptr, tmr_poll); /* reactivate unit */
|
||||||
tmxr_poll = tmr_poll * tim_mult; /* set mux poll */
|
tmxr_poll = tmr_poll * tim_mult; /* set mux poll */
|
||||||
tim_incr_base (tim_base, tim_period); /* incr time base */
|
tim_incr_base (tim_base, tim_period); /* incr time base based on period of expired interval */
|
||||||
tim_ttg = tim_period; /* reload */
|
tim_period = tim_new_period; /* If interval has changed, update period */
|
||||||
apr_flg = apr_flg | APRF_TIM; /* request interrupt */
|
apr_flg = apr_flg | APRF_TIM; /* request interrupt */
|
||||||
if (Q_ITS) { /* ITS? */
|
if (Q_ITS) { /* ITS? */
|
||||||
if (pi_act == 0)
|
if (pi_act == 0)
|
||||||
|
@ -211,7 +330,7 @@ else if (t20_idlelock && PROB (100 - tim_t20_prob))
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tim_incr_base (d10 *base, d10 incr)
|
static void tim_incr_base (d10 *base, d10 incr)
|
||||||
{
|
{
|
||||||
base[1] = base[1] + incr; /* add on incr */
|
base[1] = base[1] + incr; /* add on incr */
|
||||||
base[0] = base[0] + (base[1] >> 35); /* carry to high */
|
base[0] = base[0] + (base[1] >> 35); /* carry to high */
|
||||||
|
@ -222,12 +341,28 @@ return;
|
||||||
|
|
||||||
/* Timer reset */
|
/* Timer reset */
|
||||||
|
|
||||||
t_stat tim_reset (DEVICE *dptr)
|
static t_stat tim_reset (DEVICE *dptr)
|
||||||
{
|
{
|
||||||
sim_register_clock_unit (&tim_unit); /* declare clock unit */
|
sim_register_clock_unit (&tim_unit); /* declare clock unit */
|
||||||
tim_period = 0; /* clear timer */
|
|
||||||
tim_ttg = 0;
|
tim_base[0] = tim_base[1] = 0; /* clear timebase (HW does) */
|
||||||
|
/* HW does not initialize the interval timer, so the rate at which the timer flag
|
||||||
|
* sets is random. No sensible user would enable interrupts or check the flag without
|
||||||
|
* setting an interval. The timebase is intialized to zero by microcode intialization.
|
||||||
|
* It increments based on the overflow, so it would be reasonable for a user to just
|
||||||
|
* read it twice and subtract the values to determine elapsed time.
|
||||||
|
*
|
||||||
|
* Simply to keep the simulator overhead down until the interval timer is initialized
|
||||||
|
* by the OS or diagnostic, we will set the internal interval to ~17 msec here.
|
||||||
|
* This allows the service routine to increment the timebase, and gives RDTIME an
|
||||||
|
* baseline for its interpolation.
|
||||||
|
*/
|
||||||
|
tim_interval = 0;
|
||||||
|
clk_tps = 60;
|
||||||
|
update_interval(17*4096);
|
||||||
|
|
||||||
apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */
|
apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */
|
||||||
|
|
||||||
tmr_poll = sim_rtc_init (tim_unit.wait); /* init timer */
|
tmr_poll = sim_rtc_init (tim_unit.wait); /* init timer */
|
||||||
sim_activate (&tim_unit, tmr_poll); /* activate unit */
|
sim_activate (&tim_unit, tmr_poll); /* activate unit */
|
||||||
tmxr_poll = tmr_poll * tim_mult; /* set mux poll */
|
tmxr_poll = tmr_poll * tim_mult; /* set mux poll */
|
||||||
|
@ -240,27 +375,32 @@ t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||||||
{
|
{
|
||||||
if (val & (UNIT_T20|UNIT_KLAD)) {
|
if (val & (UNIT_T20|UNIT_KLAD)) {
|
||||||
clk_tps = TIM_TPS_T20;
|
clk_tps = TIM_TPS_T20;
|
||||||
uptr->wait = TIM_WAIT_T20;
|
update_interval(((d10)(1000*4096))/clk_tps);
|
||||||
tmr_poll = TIM_WAIT_T20;
|
tmr_poll = tim_unit.wait;
|
||||||
tim_mult = TIM_MULT_T20;
|
|
||||||
uptr->flags = uptr->flags | UNIT_Y2K;
|
uptr->flags = uptr->flags | UNIT_Y2K;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
clk_tps = TIM_TPS_T10;
|
clk_tps = TIM_TPS_T10;
|
||||||
uptr->wait = TIM_WAIT_T10;
|
update_interval (((d10)(1000*4096))/clk_tps);
|
||||||
tmr_poll = TIM_WAIT_T10;
|
tmr_poll = tim_unit.wait;
|
||||||
tim_mult = TIM_MULT_T10;
|
|
||||||
if (Q_ITS)
|
if (Q_ITS)
|
||||||
uptr->flags = uptr->flags | UNIT_Y2K;
|
uptr->flags = uptr->flags | UNIT_Y2K;
|
||||||
else uptr->flags = uptr->flags & ~UNIT_Y2K;
|
else uptr->flags = uptr->flags & ~UNIT_Y2K;
|
||||||
}
|
}
|
||||||
tmxr_poll = tmr_poll * tim_mult;
|
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Time of year clock */
|
/* Time of year clock
|
||||||
|
*
|
||||||
|
* The hardware clock was never sold by DEC, but support for it exists
|
||||||
|
* in TOPS-10. Code was also available for RSX20F to read and report the
|
||||||
|
* to the OS via its20F's SETSPD task. This implements only the read functions.
|
||||||
|
*
|
||||||
|
* The manufacturer's manual can be found at
|
||||||
|
* http://bitsavers.trailing-edge.com/pdf/digitalPathways/tcu-150.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
t_stat tcu_rd (int32 *data, int32 PA, int32 access)
|
static t_stat tcu_rd (int32 *data, int32 PA, int32 access)
|
||||||
{
|
{
|
||||||
time_t curtim;
|
time_t curtim;
|
||||||
struct tm *tptr;
|
struct tm *tptr;
|
||||||
|
|
Loading…
Add table
Reference in a new issue