*-----------------------------------------------------------
Title[TJunk.mc...September 20, 1982 10:27 AM...WSH];
*-----------------------------------------------------------

%
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, using 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.
3. Call UpdateCounters every EventUpdateInterval times (about once/ms).
4. Samples the emulator’s PC, if sampling is enabled.

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.

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]
%

MC[RTCDeltaHi, 65];
MC[RTCDeltaLoLeft, 141000];
MC[RTCDeltaLoRight, 217];
* Microcode depends on this being odd

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

Set[XTask, IP[JNK]];

*-----------------------------------------------------------
* Junk task
*-----------------------------------------------------------
** simulate Tablet for Smalltalk - take out event counters!!!!
rme[TState, EventUpdateTimer];
rme[TByte, EventAHi0];
* Event counter shadow registers
rme[PenUP, EventAHi1];
rme[TabletAddr, EventALo];
rme[AlwaysOne, EventAPrev];
rme[TJunkLink, EventBHi0];
rme[TJunkCounter, EventBHi1];
* rme[EventBLo];

mc[byteAvailable, 100];
mc[F0, 4];
mc[SetByteReceived, 100];
mc[SetNextByte, 200];
mc[firstByte, 200];

Subroutine;
JNKInitPC: T← JNK, CoReturn;
TopLevel;
T← A0, RBase← RBase[Events];
TIOA← T;
TState← T-T, MemBase← IOBR; * start in TState 0
TabletAddr← 176400C;
TabletAddr← (TabletAddr) + (100C); *Tablet X (rel to 400C)
AlwaysOne← 400C;* handshake with Tablet
T← EventCntB← AlwaysOne;* reset
T← T or (setNextByte);
TJunkCounter← 177777C;
EventCntB← T;
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.
JunkUpdateTablet:
Call[doTByte];
JunkTaskLoop:
T← RTCDeltaLo, Branch[SamplePC, R even];
JunkTaskCont:
RTCFrac← (RTCFrac)+(AckJunkTW← T);
T← RTCDeltaHi;
RTClock← (RTClock)+T, XorSavedCarry;
Branch[RTCCarry, Carry];
Block, branch[JunkUpdateTablet];

* Here when RTClock carries out. Increment VM 430.
RTCCarry:
T← 30C;
Fetch← T;* VM 430
RTC430← MD+1;* RTC430 is for Lisp performance stuff
Store← T, DBuf← RTC430, Block, Branch[JunkUpdateTablet];

* 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;


*-----------------------------------------------------------
doTByte:
* updates the tablet coordinates from GenIn and does the
* handshaking via GenOut
* if TState is even, we wait for byteAvailable; when it is,
* we do something with the byte
* if TSTate is odd, and wait for byteAvailable to go away
*-----------------------------------------------------------
KnowRBase[Events];

branch[TStateOdd, R odd], TState;
TStateEven:
T← NOT (EventCntA’); *GenIn
pd← T and (byteAvailable);
branch[te1, alu#0];
TJunkCounter← (TJunkCounter) - 1;
branch[.+2, alu#0];
** no byteAvailable in 65K wakeups
** TabletAddr is 177100 at this point - set it to zero; counter will just turn over
store← TabletAddr, dbuf← 0C, return;
return;

te1: TJunkCounter← 177777C;
TJunkLink← Link;
TopLevel;
BigBDispatch← TState;
branch[TFirstByte], EventCntB← AlwaysOne; *GenOut[resetNextByte]


** dispTable was too hard to figure out!!!
TFirstByte: TByte ← NOT (EventCnta’), AT[3521];

* wait to synch up with tablet
pd← (TByte) and (firstByte);
branch[txx, alu#0];
TState← 177776C; * not seen yet

txx: T← (AlwaysOne) or (setByteReceived), branch[txx1];
txLow: T← (TByte) and (F0), at[3523];
PenUP← LSH [T, 15]; * penBit is bit 15 (8)
TByte← NOT (EventCntA’), branch[txx];

txHi: T← NOT (EventCntA’), at[3525];
T← T and (77C);
T← LSH [T, 6];
TByte← (TByte) and (77C);
T← T or (TByte);
T← T or (PenUP);
TabletAddr← (store← TabletAddr) + 1, dbuf← T, branch[txx];


tyLow: TByte← NOT (EventCntA’), branch[txx], at[3527];

tyHi: TState← 177776C, at[3531];
T← NOT(EventCntA’);
T← T and (77C);
T← LSH [T, 6];
TByte← (TByte) and (77C);
T← TByte← T or (TByte);
T← 4000C;
T← T + (230C); * inversion by 4230C
T← T - (TByte);
TabletAddr← (store← TabletAddr) - 1, dbuf← T, branch[txx];

txx1: Tstate← (TState) + 1;
Link← TJunkLink;
subroutine;
EventCntB← T, return; *GenOut[SetByteReceived]

TopLevel;



subroutine;

TStateOdd: * wait for byteAvailable to go away
T← NOT (EventCntA’);
pd← T and (byteAvailable);
branch[.+2, alu=0];
return;
TState← (TState) + 1;
T← EventCntB← AlwaysOne; *GenOut[resetByteReceived]
T← T or (setNextByte);
EventCntB← T, 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= MACHINE DEPENDENT RECORD
*
[
*
eventALo, eventAHi1, eventAHi0: CARDINAL,-- event counterA
*
eventBLo, eventBHi1, eventBHi0: CARDINAL,-- event counterB
*
]
* Exit: T and Q 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;