PDP10: Fix rdtim to properly compute the value

The correct solution adds the elapsed interval rather than the remaining
interval value.

As reported in #699
This commit is contained in:
Mark Pizzolato 2019-06-04 00:02:17 -07:00
parent 9514ea5a58
commit 53ad66f57e

View file

@ -136,6 +136,7 @@ d10 quant = 0; /* ITS quantum */
/* Exported variables - initialized by set CPU model and reset */
int32 clk_tps; /* Interval clock ticks/sec */
int32 tick_in_usecs; /* Interval tick size in usecs */
int32 tmr_poll; /* SimH instructions/clock service */
int32 tmxr_poll; /* SimH instructions/term mux poll */
@ -213,26 +214,25 @@ DEVICE tim_dev = {
/* Timebase - the timer is always running at less than hardware frequency,
* need to interpolate the value by calculating how much of the current
* clock tick has elapsed, and what that equates to in sysfreq units.
*
* Read the contents of the time base registers, add the current contents of the
* millisecond counter to the doubleword read, and place the result in location
* E,E+1.
*/
t_bool rdtim (a10 ea, int32 prv)
{
double fract; /* Fraction of current interval completed */
d10 tempbase[2]; /* Local copy of tempbase to interpolate */
int32 used; /* Used part of curr intv, in hw ticks */
d10 incr; /* Interpolated increment for timebase */
tempbase[0] = tim_base[0]; /* copy time base */
tempbase[1] = tim_base[1];
used = tmr_poll - (sim_activate_time (&tim_unit) - 1);
fract = ((double)used) / ((double)tmr_poll);
/*
* incr is approximate number of HW ticks to add to the timebase
* value returned. This does NOT update the timebase.
*/
incr = (d10)(fract * (double)tim_period);
incr = (d10)((((double)TIM_HW_FREQ) / 1000000.0) * (tick_in_usecs - sim_activate_time_usecs (&tim_unit)));
tim_incr_base (tempbase, incr);
/* Although the two LSB of the counter contribute carry to the
@ -263,6 +263,11 @@ sim_debug (DEB_RRD, &tim_dev, "rdtim() = %012" LL_FMT "o %012" LL_FMT "o\n", tem
return FALSE;
}
/*
* Read the contents of location E,E+l, clear the right twelve bits of the low
* order word read (the part corresponding to the hardware millisecond
* counter), and place the result in the time base registers in the workspace.
*/
t_bool wrtim (a10 ea, int32 prv)
{
tim_base[0] = Read (ea, prv);
@ -271,6 +276,10 @@ sim_debug (DEB_RWR, &tim_dev, "wrtim(%012" LL_FMT "o, %012" LL_FMT "o)\n", tim_b
return FALSE;
}
/*
* Read the contents of the interval register into location E. The period read is
* the same as that supplied by WRINT.
*/
t_bool rdint (a10 ea, int32 prv)
{
Write (ea, tim_interval, prv);
@ -282,6 +291,9 @@ return FALSE;
* This does not clear the harware counter, so the first
* completion can come up to ~1 msc later than the new
* period.
*
* Load the contents of location E into the interval register in
* the workspace.
*/
t_bool wrint (a10 ea, int32 prv)
@ -294,21 +306,57 @@ return update_interval (tim_interval);
static t_bool update_interval (d10 new_interval)
{
int32 old_clk_tps = clk_tps;
int32 old_tick_in_usecs = tick_in_usecs;
/*
* The value provided is in hardware clicks. For a frequency of 4.1
* MHz, that means that dividing by 4096 (shifting 12 to the right) we get
* the aproximate value in milliseconds. If any of the rightmost bits is
* one, we add one unit (4096 ticks ). Reference:
* AA-H391A-TK_DECsystem-10_DECSYSTEM-20_Processor_Reference_Jun1982.pdf
* (page 4-37)
* (page 4-37):
*
* The timer includes a 12-bit hardware millisecond counter, a doubleword
* time base kept from it, and an interval register for timed interrupts. The
* millisecond counter runs continuously at 4.1 MHz and represents an
* elapsed time of just under 1 ms at each overflow. Whenever the counter is
* read, its two least significant bits are ignored, so its contents effectively
* represent a count in lllicroseconds (1/1025th ms).
* The time base is a double length number kept in a pair of registers in
* the workspace. It is a 71-bit unsigned quantity in which the entire first
* word comprises the high order thirty-six bits, and the low order thirty-five
* are in bits 1-35 of the second word. In this doubleword, the hardware
* counter corresponds to the right twelve bits of the low order word. The
* program can initialize the time base as a number of milliseconds (the low
* order twelve bits are ignored), and every time the counter overflows the
* microcode adds 4096 (2**12) to the base.
*
* The interval register (in the workspace) holds a period that is specified
* by the program and corresponds in magnitude to the low order word of the
* time base. This allows a maximum interval of 223 ms, which is almost 140
* minutes. At the end of each interval, the :microcode sets Interval Done
* (RDAPR bit 30), requesting an interrupt on the level assigned to the system
* flags (§4.8). In a separate workspace register, the microcode starts with
* the given period, decrements it by 4096 (2**12) every time the millisecond
* counter overflows, and sets the flag when the contents of this "time to go"
* register reach zero or less. Hence the countdown is by milliseconds, and
* any nonzero quantity in the low order twelve bits of the given period adds
* a whole millisecond to the count. (However, following specification of an
* interval by the program, the first downcount occurs at the first counter
* overflow regardless of when the register was loaded.)
*/
tim_new_period = new_interval & ~TIM_HWRE_MASK;
if (new_interval & TIM_HWRE_MASK) tim_new_period += 010000;
if (new_interval & TIM_HWRE_MASK)
tim_new_period += 010000;
if (tim_new_period == 0) {
sim_debug (DEB_TPS, &tim_dev, "update_interval() - ignoring 0 value interval\n");
return FALSE;
}
tick_in_usecs = (int32)(((double)new_interval)/(((double)TIM_HW_FREQ)/1000000.0));
if (tick_in_usecs != old_tick_in_usecs)
sim_debug (DEB_TPS, &tim_dev, "update_interval() - tick_in_usecs changed from %d to %d\n", old_tick_in_usecs, tick_in_usecs);
/* clk_tps is the new number of clocks ticks per second */
clk_tps = (int32) ceil(((double)TIM_HW_FREQ /(double)tim_new_period) - 0.5);
if (clk_tps != old_clk_tps)
@ -340,7 +388,7 @@ if (cpu_unit.flags & UNIT_KLAD) { /* diags? */
sim_activate (uptr, tmr_poll); /* reactivate unit */
}
else {
sim_activate_after (uptr, 1000000/clk_tps); /* reactivate unit */
sim_activate_after (uptr, tick_in_usecs); /* reactivate unit */
tmr_poll = sim_activate_time (uptr) - 1;
}
tmxr_poll = (int32)(sim_timer_inst_per_sec () / clk_tps);/* set mux poll */