:TITLE[Timer];*Last edited: 13 January 1981 by Fiala

%OTHER TASKS USING TIMERS MUST GUARANTEE 6 CYCLES FROM WAKEUP TO
LOADTIMER/ADDTOTIMER AND 8 CYCLES AFTER LOADTIMER/ADDTOTIMER BEFORE
TASKING FOR THE REASONS GIVEN BELOW.

Occurrences of LoadTimer and AddToTimer must be separated by 14 cycles,
and branch conditions are illegal with LoadToTimer and AddToTimer.
Here we fulfill the 14 cycle requirement by guaranteeing 6
cycles before LoadTimer/AddToTimer and 8 cycles after.

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. Setup the constant for a simple timer in slot 17b for
refresh and realtime clock update (clock constant must be even). Clock
period given below is for a cycle time of 100 ns (values larger than 16 ms
require a double timer).
Refresh periodClock constantRTimer valueClock period
2 ms 154b 50257b 64 us
4 ms 330b 50517b 128 us
8 ms 656b 51217b 256 us
16 ms 1534b 52417b 512 us
The clock constant above is chosen so that when we add it to RTCLOW every
tick, a carry will be produced at the same rate as the Alto clock.
For the 64 us timer round, a carry every 65536/108 = 606.8 iterations.
606.8 * 64 = 38836 us; equivalent Alto number is 39009.5 us, so the clock
will run slightly fast; for 256 us and slower periods, this is corrected to
about 39016 us, almost the same as the Alto.
%

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
RTCLOW ← 1C;*Make RTCLOW odd so that clock updating won’t
*start until after memory initialization.
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 scanner microcode**
SetFault, Goto[.], At[TimerTable,1]; *slot 16
SetFault, Goto[.], At[TimerTable,2]; *slot 15
SetFault, Goto[.], At[TimerTable,3]; *slot 14
SetFault, Goto[.], At[TimerTable,4]; *slot 13
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 ← Or[LShift[EOTask,14],And[7400,EOTimerDoneLoc]]C,
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 debugging
*
SetFault, Goto[.], At[TimerTable,15]; *slot 2
*
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 (And[377,EOTimerDoneLoc]C);
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 8 cycles after AddToTimer
RTCRet:
REFR ← (REFR) + (20C), Return;
:ENDIF; ***************************************
PFetch1[TimerBase,TimerTemp];*Increment VM 430
TimerTemp ← (TimerTemp) + 1;
PStore1[TimerBase,TimerTemp], Goto[RTCRet];


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


:END[Timer];