:TITLE[Timer]; %Edit by Ed Fiala 12 March 1982: Replaced 'Return' in timer dispatch by 'SetFault, GoTo[.]'; added With3MB, With10MB, WithTOR conditionals; added TimerGoLoc; change Refr init to preserve refresh sequence from Initial. Edit by Ed Fiala 24 August 1981: improve Refresh timer some more. Edit by Ed Fiala 5 May 1981: Add prTime init. Edit by Ed Fiala 29 April 1981: Remove manual init for mi at 0 and 1 and move the system initialization to Initialize.Mc; bum two mi out of Refresh timer code; improve initialization; add init for NWW; WithMidas conditional. Edit by Ed Fiala 3 March 1981: put in parameters for varying refresh period and clock rate; set values for 8 msec refresh period, improving performance about 3.9 percent. NOTES: 1) Save 1 mi if Midas resumes at MidasHaltLoc-1 rather than MidasHaltLoc+1. 2) Should automatically assemble Ethernet timers rather than using ugly conditionals. 3) Change ClockLo increment from 4 to 1 when Pilot has been changed to use vCrystal properly. 4) Be wary of timer overflow during DisplayInit.Mc's execution. 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. In Load/AddToTimer, 0:3 are new state, 4:11 new data, and 12:15 are slot number. When a timer wakeup is deferred too long, wakeup and AddToTimer don't work properly. If a timer is able to occupy the wakeup slot, then, no matter how long tasking is deferred, wakeup will happen as soon as tasking occurs. However, if some other timer occupies the slot, then 8192 cycles after it expired, the timer's data field will become positive and cease requesting a wakeup--8192 cycles after that it will go negative again, etc. So any deferral of 8193 to 16384 cycles will delay wakeup at least 16384 cycles. Even if an expiring timer immediately occupies the wakeup slot, AddToTimer may leave the data field with an 8192 cycle wait for next time, if tasking is defferred too long. Slot 17b is used for refresh and realtime clock update. Since timer periods are denominated in processor cycles, microcode can control both the number of cycles between timer wakeups and the constant added to the clock each wakeup. At the moment, 4 is added to ClockLo every 2560 cycles (because historically 1 was added every 640 cycles). Eventually software will use the READR opcode (see MesaX.Mc) to obtain the value in the vCrystal register and adjust automatically to both variable processor clock speed, and the addend to the realtime clock. Refresh must cycle fast enough to maintain storage. 16k RAMs are configured as 128 rows x 128 columns; one Dolphin refresh reference refreshes four rows, so 32 references are required per cycle; 64k RAMs are configured as 256 x 256 and would require 64 references per cycle. Both 16k and 64k RAMs specify a 2 ms cycle at maximum operating temperature (80 deg C). The primary leakage phenomenon necessitating refresh is exponential in 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 ~24 of every 640 cycles (~3.75 percent of all cycles) would be consumed by the refresh timer (Only 17 cycles are used, but the Refresh reference leaves MC1 busy for an additional 10 cycles, and the next task to run is expected to reference memory after 3 cycles, so 17+7 = 24 cycles). Therefor, refresh period has been increased to 8 ms, accepting less clock precision and a longer storage refresh period to make the emulator run faster. This surrenders about 10 degrees in temperature margin and possibly requires some marginal RAMs to be replaced, but the manufacturer's specification is believed very conservative and we do not intend to operate anywhere near the maximum operating temperature. Parameters for other periods are given in the table below. Initialization must be particularly careful about refresh. Initial uses a 640 cycle timer with the Refr register as a state variable for a period of 2 ms. After putting Pilot1 and its overlays in storage, Initial exits with LoadRAM. During LoadRAM, inline refresh is done slightly faster, but xfTemp1 is used as the Refresh base register, so an unrefreshed period of almost 4 ms might be experienced by some RAMs, if xfTemp1 and Refr are maximally out of sync--however, this is still less than the 8 ms refresh period used by Pilot. Pilot immediately starts a 2560 cycle refresh timer with Refr as the state variable; Initialize.Mc sets Refr to the value in xfTemp1 immediately before calling this code, so there is no significant refresh hickup after Initial's LoadRAM. Subsequent LoadRAM calls during Pilot init always allow normal refresh to avoid discontinuities in the refresh cycle. However, skipping the CSLKeybaord and CSLFKeyboard overlays goes about ? cycles without tasking and HRam init in DisplayInit.Mc then goes about 7500 more cycles without tasking. The following refresh periods are for a 100 ns cycle (values larger than 16 ms require a double timer): Refresh period RTimer value Timer period 2 ms 50257b 640 cycles 4 ms 50517b 1280 cycles 8 ms 51217b 2560 cycles 16 ms 52417b 5120 cycles % SetTask[TTask]; RTMP _ 100000C, At[TimerInitLoc]; ClrTimerS: *NOTE: Must have 7 mi between successive LoadTimer/AddToTimer operations and *may not have branch condition in same mi with LoadTimer/AddToTimer. LoadTimer[RTMP]; *Disable all Timers RTimer _ HiA[51217]; *Set up 256 us clock period RTimer _ (RTimer) or (LoA[51217]); *and 8 ms refresh period *prTime and NWW are emulator register in the RM 320-377 area. NWW _ T _ 0C; RSImage _ RS232 _ T; RTMP _ (RTMP) + 1, ResetMemErrs; *Clear pending memory errors LU _ (RTMP) and (17C); *there are 16d timers prTime _ 3C, GoTo[ClrTimerS,ALU#0]; LoadTimer[RTimer]; LoadPage[TimerPage]; LU _ Timer, GoToP[SetUpRef]; *Eat any (leftover) timer trying to go off OnPage[TimerPage]; SetUpRef: Call[TimerRet], At[TimerGoLoc]; TimerWakeup: *Timer wakeups come here T _ Dispatch[Timer,14,4]; *Since Refresh is the most intrusive timer, use this mi to do something *useful for it and not harmful to other timer wakeups. Refr _ (Refr) and not (1000C), Disp[.+1]; %NOTE: TimerTable is correct here only for those Timers used by Pilot1; it makes no provision for timers used by Pilot2 or by later overlays. Pilot2 timers should be handled as follows: (1) Remove the IMReserve in Mesa1Occupied for the TimerTable location to be overwritten; no change in Mesa2Occupied is required because the TimerTable location involved will harmlessly (in fact, usefully) be initialized by the code here. (2) Generally adjust IMReserves in both Mesa1Occupied and Mesa2Occupied so that there will be enough free locations on TimerPage for the new code in Pilot2. % :IF[WithTOR]; **************************************** Timers: Refresh[Refr], DblGoTo[ClkUpdate,EOMRefresh,ALU#0], At[TimerTable,00]; *slot 17 :ELSE; *********************************************** Timers: Refresh[Refr], GoTo[ClkUpdate], At[TimerTable,00]; *slot 17 :ENDIF; ********************************************** SetFault, GoTo[.], At[TimerTable,01]; *slot 16 (Jasmine overwrites) SetFault, GoTo[.], At[TimerTable,02]; *slot 15 (Jasmine overwrites) SetFault, GoTo[.], At[TimerTable,03]; *slot 14 (Jasmine overwrites) SetFault, GoTo[.], At[TimerTable,04]; *slot 13 (Jasmine overwrites) SetFault, GoTo[.], At[TimerTable,05]; *slot 12 SetFault, GoTo[.], At[TimerTable,06]; *slot 11 :IF[With3MB]; **************************************** *slot 10 (3mb Ethernet slot = xoTask) APCTask&APC _ xoNotify, GoTo[TimerRet], At[TimerTable,07]; *slot 06 (2nd 3mb Ethernet slot = xoTask2) APCTask&APC _ xoNotify2, GoTo[TimerRet], At[TimerTable,11]; :ELSE; *********************************************** SetFault, GoTo[.], At[TimerTable,07]; *slot 10 SetFault, GoTo[.], At[TimerTable,11]; *slot 06 :ENDIF; ********************************************** :IF[With10MB]; *************************************** *slots 04, 05, and 07 for 10 mb Ethernet, enxTask, enxTask2, and enxTask3. APCTask&APC _ enxNotify, GoTo[TimerRet], At[TimerTable,13]; APCTask&APC _ enxNotify2, GoTo[TimerRet], At[TimerTable,12]; APCTask&APC _ enxNotify3, GoTo[TimerRet], At[TimerTable,10]; :ELSE; *********************************************** SetFault, GoTo[.], At[TimerTable,13]; *slot 04 SetFault, GoTo[.], At[TimerTable,12]; *slot 05 SetFault, GoTo[.], At[TimerTable,10]; *slot 07 :ENDIF; ********************************************** SetFault, GoTo[.], At[TimerTable,14]; *slot 03 SetFault, GoTo[.], At[TimerTable,15]; *slot 02 (Audio overwrites) SetFault, GoTo[.], At[TimerTable,16]; *slot 01 SetFault, GoTo[.], At[TimerTable,17]; *slot 00 EOMRefresh: IOStrobe; ClkUpdate: AddToTimer[RTimer]; ClockLo _ (ClockLo) + (4C); *4C for 256 us clock period :IF[WithMidas]; ************************************** T _ (FFault) and (10000C), Skip[Carry']; ClockHi _ (ClockHi) + 1; T _ (Printer) and T; Refr _ (Refr) + (20C), Skip[ALU#0]; Return; *Midas resumes program at MidasHaltLoc+1 GoTo[.], SetFault, At[MidasHaltLoc]; *Midas halt request :ELSE; *********************************************** ClockHi _ (ClockHi) + 1, UseCOutAsCIn; Refr _ (Refr) + (20C); :ENDIF; ********************************************** TimerRet: Return, At[MidasHaltLoc,1]; :IF[With3MB]; **************************************** IFE[xoTask,10,,ER[Edit.for.3MB.Ethernet.task.change.needed]]; IFE[xoTask2,6,,ER[Edit.for.3MB.Ethernet.task.change.needed]]; :ENDIF; ********************************************** :IF[With10MB]; *************************************** IFE[enxTask,4,,ER[Edit.for.10MB.Ethernet.task.change.needed]]; IFE[enxTask2,5,,ER[Edit.for.10MB.Ethernet.task.change.needed]]; IFE[enxTask3,7,,ER[Edit.for.10MB.Ethernet.task.change.needed]]; :ENDIF; ********************************************** :END[Timer]; (1795)\f5