diff --git a/PDP10/pdp10_tim.c b/PDP10/pdp10_tim.c index 4b585145..5e6b7e58 100644 --- a/PDP10/pdp10_tim.c +++ b/PDP10/pdp10_tim.c @@ -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 */