*-----------------------------------------------------------
Title[Junk.mc...February 23, 1983  5:46 PM...Taft];
*-----------------------------------------------------------

%
Junk task and related subroutines.
This code presently maintains the real time clock and event counters,
and optionally samples the emulator task's PC and keeps a histogram.

The main loop does the following:
1. Acknowledge junk wakeup.
2. Maintain the real time clock.  Alto mode uses a DDA technique to
   approximate the effect of updating RTClock at ~38.09524 us intervals,
   though the Junk task actually wakes up every 32 us.  PrincOps mode
   just increments RTClock every 32 us.
3. Call UpdateCounters every EventUpdateInterval times (about once/ms).
4. Samples the emulator's PC, if sampling is enabled.
5. Generates interval timer wakeups (PrincOps only).

For Alto mode, the real time clock is represented as follows:
VM 430: high 16 bits of clock (Alto convention).
RTClock[0:9]: low 10 bits of clock (Alto-I convention).
RTClock[10:15]: high 6 bits of DDA fraction (bits undefined on Alto).
RTCFrac: low 16 bits of DDA fraction.
Hence the DDA fraction is 22 bits long, and the cumulative error introduced
by the approximation is guaranteed less than 1 / 2↑22, or about
1 second / 48 days.

For PrincOps mode, the clock is simply a 32-bit counter.  RTClock is
the low-order word, and RTC430 is the high-order word.

Caution: the RTClock cell must be updated once and only once per wakeup
of the Junk task, and must always assume a legal value
(B[0:9] monotonically increasing).  This is because RTClock is used for timing
by the Ethernet output task, which is higher-priority than Junk.

Derivation of RTCDelta:
Junk wakeup interval = 32 us.
RTClock interval = 224 / 5.88E6 = 38.09524 us.
Therefore Delta = 32 / 38.09524 = 0.84000
Multiplying by 2↑22 yields 3523215 = [65B ,, 141217B]
%

:If[AltoMode];		********** Alto version **********
MC[RTCDeltaHi, 65];
MC[RTCDeltaLoLeft, 141000];
MC[RTCDeltaLoRight, 217];		* Microcode depends on this being odd
:Else;			******** PrincOps version ********
MC[ITWakeupMask, 177422];		* Mask of wakeups to generate for interval timer
:EndIf;			**********************************

* Interval between calls to UpdateCounters
MC[EventUpdateInterval, 37];	* Times 32 us = ~1 ms.

Set[XTask, IP[JNK]];

*-----------------------------------------------------------
* Junk task
*-----------------------------------------------------------

Subroutine;
JNKInitPC: T← JNK, CoReturn;
TopLevel;

JunkTaskStart:
	T← A0, RBase← RBase[Events];
	TIOA← T;
	EventUpdateTimer← T-T-1, MemBase← IOBR;

:If[AltoMode];		********** Alto version **********
	RTCDeltaLo← RTCDeltaLoLeft;
	RTCDeltaLo← (RTCDeltaLo) OR (RTCDeltaLoRight), Branch[JunkTaskLoop];

* Main loop consumes 6 cycles per wakeup in the normal case
* (no RTClock overflow or counter update or PC sampling).
* RTCDeltaLo is normally odd (required for AckJunkTW←).
* RTCDeltaLow even => PC sampling enabled.
JunkUpdateCounters:
	Call[UpdateCounters];
JunkTaskLoop:
	T← RTCDeltaLo, Branch[SamplePC, R even];
JunkTaskCont:
	RTCFrac← (RTCFrac)+(AckJunkTW← T);
	T← RTCDeltaHi;
	RTClock← (RTClock)+T, XorSavedCarry;
	EventUpdateTimer← (EventUpdateTimer)+1, Branch[RTCCarry, Carry];
	Block, DblBranch[JunkUpdateCounters, JunkTaskLoop, ALU>=0];

* Here when RTClock carries out.  Increment VM 430.
RTCCarry:
	T← 400C;
	T← T OR (30C);
	Fetch← T;			* VM 430
	RTC430← MD+1;			* RTC430 is for Lisp performance stuff
	Store← T, DBuf← RTC430, Block, Branch[JunkUpdateCounters];

:Else;			******** PrincOps version ********
	RTCDeltaLo← 1C, Branch[JunkTaskLoop];

* Main loop consumes 4 cycles per wakeup in the normal case
* (no counter update or PC sampling).
* RTCDeltaLo normally contains 1 (required for AckJunkTW←).
* RTCDeltaLow=0 => PC sampling enabled.
JunkUpdateCounters:
	Call[UpdateCounters];
JunkTaskLoop:
	T← RTCDeltaLo, Branch[SamplePC, R even];
JunkTaskCont:
	RTClock← T← (RTClock)+(AckJunkTW← T);
	PD← (WakeupTime)#T;
	RTC430← A← RTC430, XorSavedCarry, Branch[RTCWakeup, ALU=0];
	EventUpdateTimer← (EventUpdateTimer)+1, Block,
		DblBranch[JunkUpdateCounters, JunkTaskLoop, R>=0];

* Here to generate interval timer wakeup
RTCWakeup:
	T← ITWakeupMask;
	Fetch← T;
	RBase← RBase[NWW];
	NWW← (NWW) OR MD, Reschedule, Block,
		Branch[JunkTaskStart];	* Reset RBase before resuming
:EndIf;			**********************************

KnowRBase[Events];

* Here to sample the emulator's PC and maintain a histogram.
* Histogram format is an array of LONG CARDINALs pointed to by PCHistBR.
SamplePC:
	T← A0, TaskingOff;
	RdTPC← T, TaskingOn;
	T← NOT (Link);			* TPC data is inverted
	T← T+T, MemBase← PCHistBR;	* 2 words per histogram slot
	Fetch← T;			* Increment histogram entry for PC
	JunkTemp← MD+1;
	T← (Store← T)+1, DBuf← JunkTemp, Branch[.-2, Carry];
	T← (RTCDeltaLo)+1, MemBase← IOBR, Branch[JunkTaskCont];

*-----------------------------------------------------------
UpdateCounters:
* Subroutine to update RM shadow of event counters.
* Called from Junk and Emulator tasks.  Emulator must call with TaskingOff.
* Entry: RBase=RBase[Events]
* Exit: Event*Lo, EventHi1, EventHi0 updated; T clobbered.
* Event*Prev remembers the previous value of EventCnt*', a non-resettable
* register.  Event*Lo maintains the count since the last restart.
*-----------------------------------------------------------
Subroutine;
KnowRBase[Events];

* Update event count A.
	T← (EventAPrev)-(EventCntA');
	EventALo← (EventALo)+T;
	EventAPrev← (EventAPrev)-T, Branch[.+3, Carry'];
	EventAHi1← (EventAHi1)+1;
	EventAHi0← A← EventAHi0, XorSavedCarry;

* Update event count B.
	T← (EventBPrev)-(EventCntB');
	EventBLo← (EventBLo)+T;
	EventBPrev← (EventBPrev)-T, Branch[.+3, Carry'];
	EventBHi1← (EventBHi1)+1;
	EventBHi0← A← EventBHi0, XorSavedCarry;

* Reset the timer for Junk task calls.
	EventUpdateTimer← NOT (EventUpdateInterval), Return;




* Subroutines called only from the Emulator.
Set[XTask, IP[EMU]];

*-----------------------------------------------------------
SetPCHistAddr:
* Subroutine to set the PC histogram address and enable/disable
* sampling of the emulator task's PC.
* Entry: T,,Q = LONG POINTER to table (or 0,,0 to disable)
*	RBase=Events
* Exit: RBase=Events
*	MemBase=PCHistBR
* Table is ARRAY [0..4095] OF LONG CARDINAL.
* The emulator's PC is used as an index into the array, and the appropriate
* word is incremented every 32 microseconds.
*-----------------------------------------------------------
Subroutine;
KnowRBase[Events];

	RTCDeltaLo← (RTCDeltaLo) OR (1C); * Disable sampling
	EventTemp0← Q, MemBase← PCHistBR;
	PD← (BRLo← EventTemp0) OR T;
	BRHi← T, Branch[.+2, ALU=0];
	RTCDeltaLo← (RTCDeltaLo)-1;	* Enable sampling
	Return;

*-----------------------------------------------------------
StartCounters:
* Subroutine to enable event counters and reset them to zero.
* Entry: T=event counter enable word (for InsSetOrEvent←), RBase=Events.
* Exit: event counters zeroed, T unchaged, RBase=Events.
*-----------------------------------------------------------
Subroutine;

* Enable hardware counters as specified in the call.
	InsSetOrEvent← T;

* Now reset the software counters.  Must do this with TaskingOff to prevent
* interference from the junk task, and must not read event counters until
* at least 3 cycles after InsSetOrEvent← or we might read trash.
	EventALo← A0, TaskingOff;
	EventAHi1← A0;
	EventAHi0← A0;
	EventAPrev← EventCntA';

	EventBLo← A0;
	EventBHi1← A0;
	EventBHi0← A0, TaskingOn;
	EventBPrev← EventCntB', Return;

*-----------------------------------------------------------
ReadCounters:
* Subroutine to read current values of counters into memory.
* Entry: T=pointer to CounterValues record, RBase=Events.
*  CounterValues: TYPE = MACHINE DEPENDENT RECORD [
*	eventALo, eventAHi1, eventAHi0: CARDINAL,	-- event counterA
*	eventBLo, eventBHi1, eventBHi0: CARDINAL];	-- event counterB
* Exit: T, Q, and EventTemp0 clobbered.
*-----------------------------------------------------------
Subroutine;

	Fetch← T, Q← T;			* Minimize waiting while TaskingOff
	T← MD, EventTemp0← Link;
TopLevel;
	TaskingOff, Call[UpdateCounters]; * Capture up-to-date state
	T← (Store← Q)+1, DBuf← EventALo;
	T← (Store← T)+1, DBuf← EventAHi1;
	T← (Store← T)+1, DBuf← EventAHi0;
	T← (Store← T)+1, DBuf← EventBLo;
	T← (Store← T)+1, DBuf← EventBHi1;
	T← (Store← T)+1, DBuf← EventBHi0;
	Link← EventTemp0;
Subroutine;
	TaskingOn, Return;
TopLevel;