:TITLE[Timer];*Last edited: 30 April 1981 by Fiala

%Occurrences of LoadTimer and AddToTimer must be separated by 7 mi, and branch
conditions are illegal with LoadToTimer and AddToTimer. Here the 7 mi
requirement is fulfilled with 3 mi before LoadTimer/AddToTimer and 4 mi after.
OTHER MODULES MUST GUARANTEE 3 MI FROM WAKEUP TO LOADTIMER/ADDTOTIMER AND 4 MI
AFTER LOADTIMER/ADDTOTIMER BEFORE TASKING TO BE CONSISTENT.

Refresh must cycle fast enough to maintain storage. 16k ic’s specification
is a refresh period of 2 ms at maximum operating temperature (80 deg C).
The leakage phenomenon which necessitates refresh is exponential in absolute
temperature, about exp(-44*(300/T)) so required refresh rate goes up by a
factor of 2 for every increase of 5 degrees C. Refresh at a 2 ms period
is undesirable because about 4 percent of all cycles would be consumed by
the timer task; the code below presently implements a refresh period of 8 ms,
which gives up about 10 degrees in temperature margin, possibly requiring
a few more marginal ic’s to be replaced.

In LoadTimer/AddToTimer, 0:3 are the new state, 4:11 new data, and 12:15
are the slot number.

Slot 17b is used for refresh and realtime clock update. The clock constant
ideally has a value such that carries out of RTCLOW will occur at the same
rate as the Alto clock. To achieve this, the clock constant should have
a value for a 64 us clock period of (65536*64)/39009.5 = 107.52d. Also, the
implementation below requires the clock constant to be even. The table
below shows values for a 40 mhz (100 ns cycle) main system clock; constants
are also given for 44.5 mhz (89.886 ns cycle) and 50 mhz (80 ns cycle) clocks.
Since timers run off the system clock, both refresh and clock periods scale
with the main system clock. Note that clock periods larger than 512 us
would require a double timer. Equivalent numbers for the three frequencies
are 107.52d (40 mhz), 96.645d (44.5 mhz), and 86.016d (50 mhz).

Refresh
Clock RTimer 40 mhz 44.5 mhz 50 mhz
Period
Period Value Constant Constant Constant
2 ms
64 us 50257b 154b141b 126b
4 ms
128 us 50517b 327b301b 254b
8 ms
256 us 51217b 656b603b 530b
16 ms
512 us 52417b 1534b 1405b 1260b
%
SetTask[0];

OnPage[XMiscPage];

*Common subroutine for Mesa/Alto ReadClock. Takes 430b in T, returns
*(VM 430) in RTemp, RTCLOW in T.
MXRClk:
PFetch1[MDS,RTemp];
RTemp1 ← IP[RTCLOW]C;
T ← (SStkP&NStkP) xor (377C);
StkP ← RTemp1, RTemp1 ← T, NoRegILockOK;
T ← (Stack) and not (77C);*Return Most Significant 10d bits
RestSP:
StkP ← RTemp1, Return;*Restore StkP

SetTask[TTask];

*Initialize Notifies here to set up timers (this is throwaway code)
InitTimers:
TimerTemp ← 100000C, At[TimerInitLoc];
ClrTimers:
TimerTemp ← (LoadTimer[TimerTemp]) + 1;
RSImage ← T ← 0C;
REFR ← RS232 ← T;*Zap RS232, setup refresh pointer
TimerBasehi ← T, ResetMemErrs;*Clear any pending memory errors
*initialize high base register
***Might be better to eliminate the RTCLOW even/odd kludge, which halves the
***clock preceision, and instead zero RConstant here, then correct its
***value after storage is initialized.
RTCLOW ← 1C;*Make RTCLOW odd so that clock updating won’t
*start until after memory initialization.
*Clock error is about .00019 or 16.2 sec/day for this 40 mhz value.
RConstant ← 256C;
RConstant ← (RConstant) or (400C);
RTimer ← 51000C;*Set up the Refresh time
LU ← (TimerTemp) and (17C);*clear 20b timers
RTimer ← (RTimer) or (217C), GoTo[ClrTimers,ALU#0];

LU ← Timer, LoadPage[TimerPage];*read to clear pending wakeups
LoadTimer[RTimer], GoToP[TimerSetTPC];*load refresh timer

OnPage[TimerPage];

TimerSetTPC:
TimerBase ← 400C, Call[TimerRet];*initialize low base register
TimerWakeup:
*Timer wakeups come here
Dispatch[Timer,14,4];
*Since Refresh timer has the greatest effect on performance, do something
*useful for it in this mi.
T ← RConstant, Disp[.+1];
*Timer dispatch table for task 16 (not all entries are used)
Refresh[REFR], GoTo[RefreshNext], At[TimerTable,0];*slot 17
**NOTE: TimerTable 1, 3, 5, 6, 7, 10, 20, and 21 are overwritten by
**Jasmine.Mc, 15 is Audio.Mc timer, 16 and 17 overwritten by Audio.Mc
SetFault, GoTo[.], At[TimerTable,1]; *slot 16 Jasmine
SetFault, GoTo[.], At[TimerTable,2]; *slot 15 Jasmine
SetFault, GoTo[.], At[TimerTable,3]; *slot 14 Jasmine
SetFault, GoTo[.], At[TimerTable,4]; *slot 13 Jasmine
SetFault, GoTo[.], At[TimerTable,5]; *slot 12
SetFault, GoTo[.], At[TimerTable,6]; *slot 11
SetFault, GoTo[.], At[TimerTable,7]; *slot 10
SetFault, GoTo[.], At[TimerTable,10]; *slot 7
*Ethernet Output Notify slot (Add copies with EOTask1, EOTask2 ... if
*multiple controllers).
TimerTemp ← HiA[EOTimerDoneLoc,EOTask], GoTo[TEONotify], At[TimerTable,11];
SetFault, GoTo[.], At[TimerTable,12]; *slot 5
SetFault, GoTo[.], At[TimerTable,13]; *slot 4
SetFault, GoTo[.], At[TimerTable,14]; *slot 3
**Temporarily commented out for Audio.Mc
*
SetFault, GoTo[.], At[TimerTable,15]; *slot 2 Audio
*
SetFault, GoTo[.], At[TimerTable,16]; *slot 1
*
SetFault, GoTo[.], At[TimerTable,17]; *slot 0

*Reserve locations that can be overwritten by non-resident overlays which
*use timers (reserve TimerTable 20 and 21 here; since 22 to 24 are used,
*25 is the next one available if more are needed).
IMReserve[0,Add[TimerTable,20],2];

*Notify for Ethernet output (Only one copy required)
TEONotify:
*Notify Ethernet output task, don’t restart timer
TimerTemp ← (TimerTemp) or (LoA[EOTimerDoneLoc]);
APCTask&APC ← TimerTemp;
TimerRet:
Return, At[TimerTable,24]; *Must be MouseHalt+1 for proper restarting.

RefreshNext:
AddToTimer[RTimer];
RTCLOW ← (RTCLOW) + T, Skip[R Even];
RTCLOW ← (Zero) + 1;*RTC has not been started yet.
:IF[WithMidas]; *******************************
T ← (FFault) and (10000C), GoTo[RTCUpd,Carry];
LU ← (Printer) and T;
Skip[ALU#0];*Skip if Midas present & mouse halt
RTCRet:
REFR ← (REFR) + (20C), Return;*increment the refresh address
*Midas mouse halt is a task 16 breakpoint that was not set by the user.
*Midas continues by going to location 7004 (which contains a return).
MouseHalt:
SetFault, GoTo[.], At[TimerTable,23];

*Note: MC1 is busy for 20 cycles after Refresh, so the PFetch1 below will be
*held for 11 cycles; when we take this path, we postpone checking for
*Midas present and mouse halt until the next iteration.
RTCUpd:
T ← 30C;
:ELSE; ****************************************
T ← 30C, GoTo[.+3,Carry];
Nop;*Ensure 4 mi after AddToTimer
RTCRet:
REFR ← (REFR) + (20C), Return;
:ENDIF; ***************************************
PFetch1[TimerBase,TimerTemp];*Increment VM 430
TimerTemp ← (TimerTemp) + 1;
PStore1[TimerBase,TimerTemp], GoTo[RTCRet];

:END[Timer];