*----------------------------------------------------------- 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; (1800)