INSERT[DisplayDefs]; *Defs file common to DisplayInit and Display :IF[LFKeyBoard]; TITLE[LFDisplay]; :ELSEIF[LFMonitor]; TITLE[CSLFDisplay]; :ELSE; TITLE[CSLDisplay]; :ENDIF; % Ed Fiala 26 May 1983: Increase cycles after Output from 4 to 5 at vCursm. Ed Fiala 29 June 1982: Fix black background bug with display off. Ed Fiala 20 May 1982: Add FourWakeups switch. Ed Fiala 18 May 1982: cosmetic edits; save cycles at @vTVB1; improve average main loop time by 2 cycles for CSLF terminal, 4 cycles for LF terminal; saved 6 cycles on worst case for LF terminal; eliminated vBadCnt and replaced vMouseDx and vMouseDy by a single register vMouseDxy; move IOStrobe to 3rd mi at @vVB0; improve LF keyboard keystroke posting code. Jim Sandman March 11, 1982 change KTable Ed Fiala 22 February 1982 This source assembles a driver for any of the three monitor/keyboard configurations with appropriate setting of LFMonitor and LFKeyboard switches. During vertical blanking, the vCnt register is used to count the appropriate number of times through the blanked scanlines (a) at end-of-field; (b) during vertical sync; and (c) before the beginning of the next field. During these blanked lines, all work necessary to clean up after the last visible field and to setup for the next visible field will take place. Cleanup for the last field consists of posting accumulated keyboard characters (LF keyboard only), mouse position changes, and mouse buttons. These updates could have been completed at end-of-message, but that would have worsened DisplayTask timing requirements significantly, so the information is buffered until vertical blanking and then posted. Setup for the next field includes loading even or odd scanline cursor memory words as appropriate for the next field, posting the vertical interrupt, resyncing IAR, setting up the cursor control and the DCB for the next field. On each scanline, a memory reference that sets IAddr[6:7] = 0 must be made prior to testing the backchannel with IOAtten. IAddr[6:7] are loaded from the low two bits of H2 on Input or Output or from the low two bits of SrcDest on all other references. During vertical blanking, Input[--,0] or Output[--,x] where x = vCReg or vCursor0 are usually used; during vertical scan, Output[--,vBuf0] or IOFetchN[--,vfBuf0] are used. However, it is possible to use a PFetch/StoreN[--,RMaddr] where RMaddr (i.e., the SrcDest field of the mi) has two low zeroes. Timing considerations are as follows: (1) LF monitors require scanline service every 28.8 microseconds, CSL monitors every 37.8 microseconds. Estimated average service time is 68 cycles (6.8 microseconds) for LF monitors or 58 cycles (5.8 microseconds) for CSL monitors, so the display task will consume 22 percent of all cycles for LF and 15 percent for CSL monitors. (2) To count cycles across tasking in the presence of memory references: a. Charge all storage transport to the task initiating a reference, so a PFetch1, 2, or 4 or a PStore1, 2, or 4 are charged 1, 2, or 4 cycles for transport, as appropriate, and Input and Output are charged 1 cycle each; this may result in a double charge if the transport occurs while another task is in MC1/2 wait. b. When waking up, assume MC1 is busy for 3 cycles, so a reference in either of the first two mi counts as 5 cycles. This may be pessimistic; the predecessor is usually the emulator; MC1 will be idle after prototypical PFetch1/NextInst/NIRet and busy for 10 cycles after PStore1/NextInst/NIRet. c. References initiated just prior to tasking are charged for MC1/MC2 wait incurred by the successor task (normally the emulator); for simplicity, again assume that the successor task will do non-memory mi for 4 cycles and then be held in MC1/MC2 wait as long as necessary. I have empirically determined that the average number of non-memory cycles in the successor task is 4.1 by timing programs with the OnlyTwoWakeups and FourWakeups switches in different states. (3) Although DisplayTask is of high priority, each time it returns, another task will run even if lower priority; but since DisplayTask reasserts its wakeup request, it will run next barring higher priority requests. As suggested in the timing model in (2) above, tasking before mi otherwise suspended in memory wait improves emulator throughput about 2.0 percent since the next task to run completes a few mi, on the average, before suspension in MC1/MC2 wait, but, with too many returns, DisplayTask may not finish scanline service within its 28.8 or 37.8 microsecond window. A service failure might garbage a backchannel message and always displaces the screen image downward two scanlines (two rather than one because of interleaving) from the point of failure onward. To allow tasking more frequently, lower priority tasks must not run too long without tasking; at present, the emulator has no known sequences longer than 45 cycles without tasking. A tiny amount of screen jumping is perhaps acceptable, but erroneous backchannel messages are a bigger problem. For the CSL keyboard, bad messages are detected and discarded, and the status of the four keyboard words and mouse buttons is rebroadcast every five (?) scanlines, or 15.4 times/second, so occasional missed messages produce no errors except almost imperceptible non-movements in the mouse position. However, errors are not detected for the LF keyboard, so a data-late will distort any message in progress. Also, keyboard "strokes" are transmitted by the keyboard microcomputer, so if one of these messages is missed, confusion can be magnified. This problem is so bad that only very rare data lates can be tolerated with an LF keyboard. (4) The current code has no bad timing cases during vertical blanking (it was necessary to load cursor memory words in two scanlines each rather than in one scanline to make the timing for that case less critical than the visible field.). The worst case occurs on the first scanline of the visible field (which requires an extra Output) when an end-of-message event occurs on the backchannel and the cursor is shown. Assembly switches allow tasking either 2, 3 or 4 times per scanline; the display seems solid at 4 tasks per scanline, and since the emulator runs 2.0 percent faster, it is desirable to leave the switches set for 4 wakeups unless there are an objectionable number of data lates. The discussion here assumes 4 tasks/scanline. When showing the cursor, only 3 tasks occur, so that the worst case is transformed to occur on the first scanline, not showing the cursor, and servicing an end-of-message event on the backchannel. The first two scanlines for a zero-width DCB are the same timing as regular scanlines; after that less critical. For these reasons, timing for the regular visible scanline is the only case examined. The sequence is as follows: 1st Wakeup Output 9 cycles 1st scanline of field only ------- IO16 3+18-4 cycles Every scanline x 3+20 cycles worst for LF keyboard vM1/vM0 3+33 cycles worst for CSL keyboard ------- 2nd Wakeup IO16 3+18-4 cycles Every scanline ------- 3rd Wakeup IO16 3+18-4 cycles Every scanline ------- 4th Wakeup IO16 3+18-4 cycles Every scanline This happens every 28.8 microseconds and must happen within about 28.1 microseconds because the final IOFetch16 must finish in MC2 before the next scanline. The average CPU requirement is about 68 cycles out of 288 cycles (40 mHz processor crystal) or 22 percent of all cycles. Consequently, bad cases are as follows: on 8 of the 404+44 scanlines, showing the cursor adds 9 cycles, but only 3 wakeups occur in this case; on the first visible scanline, an extra Output adds 9 cycles; on message boundaries, up to 5 extra cycles may be needed (LF keyboard). Hence, the typical case is that 4 wakeups and 68 cycles are needed in 281 cycles; if the only higher priority activity is the refresh timer (26 cycles every 2560), then 281-68-26 implies that 4 consecutive lower priority executions should not exceed 187 cycles (= 47 cycles between tasks) in the absence of page faults, which is comfortable. However, the bad timing cases all occur when a page, write protect, or MOB fault occurs during a scanline. In this case, the fault handler will preempt as many as 79 cycles (emulator was running) or 101 cycles (io task was running)--these are the worst cases; see Fault.Mc. After preemption, the fault handler continues in the emulator with an 11 cycle task and a 4 cycle task still in Fault.Mc. If the emulator was running, then the 79 cycles is not additive to other execution time because the emulator would otherwise have continued to its tasking point, so preemption adds no more than 77 cycles. If any io task was running, however, the fault handler will resume it, so time available to the display task is 101 cycles less than without the fault. Half of the faults take 8 cycles less than this time. 3/4 of the faults will happen before or during the first three display wakeups, when the emulator will have short sequences in Fault.Mc after the long preemption. However, in the worst case, the display task will be running on its final wakeup (or some other io task after the display task has finished its 3rd wakeup) when the fault handler intervenes, and in this case the effect of the fault will be to add 101 cycles to the worst case. In this case, there is still no data late unless the four preceding non-display wakeups averaged more than 21 cycles. A truly worst case view would also allow for the unusual 9+5 cycles needed by the display task, and in this case only 18 cycles between tasks is permissible. However, the frequency of bad cases should be low, so data lates should be infrequent. If FourWakeups is 0, the LF monitor main loop will wakeup 3 times per scanline (even when it shows the cursor). If OnlyTwoWakeups is 1, the LF monitor main loop will wakeup only twice/scanline. For the reasons discussed above, it may be necessary (particularly with LF keyboards) to have only 3 wakeups/scanline, but avoid if possible. % Set[OnlyTwoWakeups,0]; Set[FourWakeups,1]; Loca[vEvFet,DisplayPage,40]; *Dispatch table for CSL monitor only Loca[vOdFet,DisplayPage,60]; *Dispatch table for CSL monitor only Loca[KeyTable,DisplayPage,40]; *40b mi for LF keyboard only Macro[KBCheck,DblGoTo[vM1,vM0,IOAtten']]; SetTask[DisplayTask]; OnPage[DisplayPage]; ***Eliminate this mi by moving vEndOfFieldLoc. *vDBuf0/1 _ MouseX/Y vFDone: T _ LdF[vCR,15,1], At[vEndOfFieldLoc]; *End-of-field comes to vFDon1. vFDon1: PFetch2[vCSB,vDBuf0,MouseX]; *Complement even/odd field bit and turn on blanking vCR _ (vCR) xor (OddFld&Blank); *Set up to load cursor memory--mask Field bit. vCursorY _ (Zero) xnor T; *Excess 200b counts are kept in left and right halves of vMouseDxy. :IF[LFKeyboard]; ****************************** *Store mouse buttons in vButtons[11:13d] into 177707; power supply normal, *VS, and video are in vButtons[8:10], but not currently reported. T _ (vButtons) and (4C); T _ (LdF[vButtons,13,2]) or T; vDBuf3 _ (Zero) xnor T; *X is in right-half, Y in left-half. T _ 200C; T _ (LdF[vMouseDxy,10,10]) - T; vDBuf0 _ (vDBuf0) + T; *Update MouseX T _ 200C; T _ (LdF[vMouseDxy,0,10]) - T; vDBuf1 _ (vDBuf1) + T; *Update MouseY :ELSE; **************************************** *Mouse buttons already in position. T _ vButtons; vDBuf3 _ T; *X is in left-half, Y in right-half. T _ 200C; T _ (LdF[vMouseDxy,0,10]) - T; vDBuf0 _ (vDBuf0) + T; T _ 200C; T _ (LdF[vMouseDxy,10,10]) - T; vDBuf1 _ (vDBuf1) + T; :ENDIF; *************************************** *Post mouse x,y by adding mouse dx/dy to MouseX/Y in the CSB; store buttons *in vDBuf3; vDBuf2 smashes the word between MouseY and Buttons, but that's ok. PStore4[vCSB,vDBuf0,MouseX]; *Reset register to 0+200b (excess 200b); this avoids overflows because the *maximum mouse movement in one field time is less than +/- 200b. vMouseDxy _ 100000C; *43 or 44 (+2 if LF) cycles to vM1 vMouseDxy _ (vMouseDxy) or (200C), Call[vSF0]; *Fetch CursorX/Y into vDBuf2/3; setup cursor position @vVB0: PFetch2[vCSB,vDBuf2,CursorX]; *Setup for vertical sync below. vCR _ (vCR) or (VS); **8 cycle abort here. T _ vDBuf3, Skip[R Even]; *Y coord. *Y odd => invert earlier decision on which scanline of the cursor goes first. vCursorControl _ (vCursorControl) xor (4000C); *vCursorY init to Ycoord - (1 if B,2 if A); counts down by 2/scanline *until negative; shows cursor for 8 scan-lines; finally, sets vCursorY *to large pos. number to disable until end-of-field. vCursorY _ (vCursorY) + T; vDBuf2 _ (vDBuf2) xnor (0C); *Increment nibble number vDBuf2 _ (vDBuf2) + (4C); T _ (vDBuf2) and not (176000C); *Save low ten bits :IF[LFKeyboard]; ****************************** *Add X to initial Y vCursorControl _ (vCursorControl) + T; vBitmapHi _ HiA[KeyTable]; *Setup for vPostKey *37 or 40 cycles to vM1. vBitmapHi _ (vBitmapHi) or (LoA[KeyTable]), Call[vKBIn]; *A user typing 70 words/min would average 420 chars/min = 840 keystrokes/min *= 14 keystrokes/sec and this code is sampling vKeyBuffer at the field rate, *77 times/second; for this algorithm to fall behind and drop characters, *the user will have to produce 3 keystrokes in 2 field times. *~13 cycles to vM1 if no keystroke posted, else 62 to 65 cycles to vM1 with *12 cycles free during the dead time of the PStore1. @vVB1: LU _ LHMask[vKeyBuffer], Call[vPostKey]; :ELSE; **************************************** *Increment nibble number *Add X to initial Y; 33 or 36 cycles to vM1 vCursorControl _ (vCursorControl) + T, Call[vKBIn]; :ENDIF; *************************************** *Repeat VSStart or +1 times for blank lines at end of field @vVB2: Input[vDBuf0,0,vCnt], GoTo[vKBIOS,R>=0]; vCnt _ 10C; Output[vCR,vCReg]; *Start vertical sync *Load half of the cursor memory shown on this field in 16d scanlines. *Every two scanlines four nibbles are loaded. vVS0: T _ CursorBitmap; T _ (LdF[vSLC,1,4]) + T, Task; PFetch1[vCSB,vDBuf0]; *Fetch word to load (CSB+11b+vSLC.1..4) vCnt _ (vCnt) - 1, Call[vLdCur]; *~30 cycles to vM1 @vVS1: Output[vDBuf0,vCursorMem0]; vSLC _ (vSLC) + (4C), Call[vLdCur]; *~36 cycles to vM1 @vVS2: Output[vDBuf0,vCursorMem0,vCnt], GoTo[.+3,R<0]; vSLC _ LHMask[vSLC]; vSLC _ (vSLC) + (10000C), GoTo[vVS0]; vCnt _ vSyncLength; vSLC _ HiA[Sub[MaxScanLines,2]], Call[.+1]; *Continue VSync; repeats vSyncLength+1 scanlines @vVS3: Input[vDBuf0,0,vCnt], Skip[R<0]; vCnt _ (vCnt) - 1, IOStrobe, KBCheck; *7 cycles to vM1 loop vDBuf1 _ Add[40000,vStart]C; Output[vDBuf1,vBufStart]; *Start_vBufSt, ForceIARLoad'_0 vCR _ (vCR) and not (VS); *The RM register vDBuf1 is a "don't care" in the next mi Output[vDBuf1,vLdIAR]; *IAR_Start (resynchronize) *End VSync vCnt _ vBlankLength, Call[vKBOut]; *32 cycles to vM1 *NOTE: vBitmap/vBitmapHi coincident with vDBuf2/3; @vTVB0: PFetch2[vCSB,vDBuf2,DCBptr]; *DCBptr & int. mask. vDBuf1 _ Add[140000,vStart]C, Call[.+1]; *ReSync IAR *Repeat vBlankLength+1 blank scanlines at top of field @vTVB1: Input[vDBuf0,0,vCnt], Skip[R<0]; vCnt _ (vCnt) - 1, IOStrobe, KBCheck; *7 cycles to vM1 vCR _ (vCR) and not (Blank), Call[vChkMsg]; *10 cycles to vM1 *Continue on same scanline after tasking. Output[vDBuf1,vBufStart]; *Start_vBufSt, ForceIARLoad'_1 *Field interrupt T _ vDBuf3, LoadPageExternal[NotifyInterruptPage]; vDBuf0 _ T, IOStrobe, CallExternal[NotifyInterruptLoc]; @vTVB2: T _ vCSBHi; vBitmapHi _ T; LU _ vBitmap, Call[vDCB0]; *DCBptr fetched at @vTVB0+3 *Here on same scanline after tasking LU _ (vDBuf0) and (DCBBackground); T _ RHMask[vDBuf0], Skip[ALU=0]; vCR _ (vCR) or (BlackBackground), DblGoTo[vZeroWidthDCB,.+2,ALU=0]; vCR _ (vCR) and not (BlackBackground), GoTo[vZeroWidthDCB,ALU=0]; PFetch2[vBitmap,vBitMap,DCBBitmapPtr]; vSLC _ (vSLC) or (LoA[Sub[MaxScanLines,2]]); vBitMapHi _ T _ (vBitMapHi) and (77C); vBitMapHi _ (LSh[vBitMapHi,10]) + T + 1, GoTo[vWidNZ]; *Set CEnable bit; set cursor word number to largest line, so that it will *count to 0/1 when cursor is first shown. vDCB0: vCursorControl _ (vCursorControl) or (172000C), Skip[ALU=0]; **Here use SrcDest=vDBuf0=0 to setup IOAtten for vChkMsg. PFetch1[vBitmap,vDBuf0,DCBFlags], GoTo[vChkMsg]; Input[vCnt,0]; vDBuf0 _ 0C, KBCheck; *14 cycles to vM1 *vCReg=0 sets IAddr[6:7] to 0 for IOAtten test vSF0: Output[vCR,vCReg,vCursorY], GoTo[.+3,R Even]; *Test field bit vCursorControl _ T _ 0C; *Even scan lines next vCnt _ vVSStart, GoTo[.+3]; vCursorControl _ T _ 4000C; *Odd scan lines next vCnt _ Add[vVSStart!,1]C; vSLC _ T, IOStrobe, KBCheck; vLdCur: Output[vSLC,vCursor0,vDBuf2]; vDBuf0 _ LCy[vDBuf0,4]; Output[vDBuf0,vCursorMem0]; vSLC _ (vSLC) + (4C); Output[vSLC,vCursor0,vDBuf2]; vDBuf0 _ LCy[vDBuf0,4], GoTo[vKBO1]; vKBOut: Output[vCR,vCReg]; *Avoid Output-Output-PStore4 problem by Nop; *waiting here for 5 mi vKBO1: IOStrobe, GoTo[vChkMsg]; vKBIn: Input[vDBuf0,0]; *Set up IAddr.6..7 = 0 to make IOAtten reference *channel 0 for vChkMsg vKBIOS: vCnt _ (vCnt) - 1, IOStrobe, KBCheck; vKBInx: Input[vDBuf0,0]; *Same as vKBIn without IOStrobe vKBCnt: vCnt _ (vCnt) - 1, KBCheck; *For zero-width DCBs send two scanlines of zeroes (to fill the two ping-pong *buffers), then do only cursor and message checking until the next DCB. vZeroWidthDCB: IOStrobe; vBitmap _ ZeroDataBuffer, Task; *19 cycles to vM1 vSLC _ (vSLC) or (LoA[Sub[MaxScanLines,4]]); *31 cycles to vM1 (LF monitor); 47 cycles to task (CSL monitor) Output[vCR,vCReg]; :IF[LFMonitor]; ******************************* IOFetch16[vBitMap,vfBuf0,0], Call[vZMsg]; *Fill 1st buffer *45 cycles to task in worst case (showing cursor) IOFetch16[vBitMap,vfBuf0,0], Call[vZCur]; IOFetch16[vBitMap,vfBuf0,0], Call[vZMsg]; *Fill 2nd buffer IOFetch16[vBitMap,vfBuf0,0], Call[vZCur]; :ELSE; **************************************** vCnt _ 0C; IOFetch4[vBitMap,vfBuf0,0], Call[vZMsg]; *Fill 1st buffer *45 cycles to task in worst case (showing cursor) Output[vCnt,vBuf0], Call[vZCur]; IOFetch4[vBitMap,vfBuf0,0], Call[vZMsg]; *Fill 2nd buffer Output[vCnt,vBuf0], Call[vZCur]; :ENDIF; *************************************** *Loop checking for messages & cursor. Since only 16x16 of the hardware's *32x32 cursor is used, it is shown 8 scanlines/field and then disabled on *the following scanline. **Should figure a way to avoid checking both vCursorY and vSLC here** vCursorY _ (vCursorY) - (2C), Skip[R<0]; Input[vDBuf0,0,vSLC], DblGoTo[vCursn,vCursm,R<0]; vCursorControl _ (vCursorControl) + (10000C); LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; *Last was final cursor line--disable cursor now. vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; *Set vCursorY to count to a large positive number after one more iteration. vCursorY _ 100000C; vSLC, Skip[R<0]; *Skip on last scanline of field vCursm: vSLC _ (vSLC) - 1, IOStrobe, GoTo[vChkMsg]; vCursn: IOStrobe, Call[vChkMsg]; T _ LdF[vCR,15,1], GoTo[vFDon1]; vZMsg: IOFetch16[vBitMap,vfBuf0,0], KBCheck; vZCur: IOStrobe; :UNLESS[LFMonitor]; *************************** Output[vCnt,vBuf0]; :ENDIF; *************************************** vCursorY _ (vCursorY) - (2C), Skip[R<0]; vZCur0: IOFetch16[vBitMap,vfBuf0,0], Return; vCursorControl _ (vCursorControl) + (10000C); LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; *Last was final cursor line--disable cursor now. vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; *Set vCursorY to count to a large positive number after one more iteration. vCursorY _ 100000C; GoTo[vZCur0]; :IF[LFMonitor]; ******************************* vWidNZ1: IOStrobe, Disp[.+1]; *Odd field--offset by WordCount T _ vCnt _ WordsPerLine, Return, DispTable[2]; *Even field--no offset T _ vCnt _ 0C, Return; vWidNZ: Dispatch[vCR,15,1], Call[vWidNZ1]; %Begin visible field. Worst case execution time for a scanline (showing the cursor and longest backchannel end-of-message case) is about 86 cycles; normal time is about 66 cycles. **Code here relies on another task running after the T _ vCnt _ X, Return; **above to avoid the bypass kludge. % Output[vCR,vCReg], GoTo[vLFSL0]; vChkCall: T _ vCnt _ (vCnt) + (20C), KBCheck; vLastL: T _ vCnt _ (vCnt) + (20C), Return; :IF[OnlyTwoWakeups]; **************************** *Not tasking here costs ~1.5 percent in machine utilization, so avoid if *possible. vLFSL1: UseCTask, Call[vLastL]; :ELSE; ****************************************** vLFSL1: Call[vLastL]; :ENDIF; ***************************************** *3rd wakeup/scanline (2nd if OnlyTwoWakeups=1) IOFetch16[vBitmap,vfBuf0], Call[vLFSL2]; :IF[FourWakeups]; ******************************* *4th wakeup/scanline IOFetch16[vBitmap,vfBuf0], Call[vLFSL3]; *1st wakeup/scanline **Note: every cycle here degrades emulator performance about 0.5 percent. vLFSL0: IOFetch16[vBitmap,vfBuf0], Call[vChkCall]; *2nd wakeup/scanline IOFetch16[vBitmap,vfBuf0], GoTo[vLFSL1]; vLFSL3: vSLC _ (vSLC) - 1, IOStrobe, Skip[R<0]; vNextSL: T _ vCnt _ (vCnt) + (Add[WordsPerLine!,20]C), Return; Call[vNextSL]; T _ LdF[vCR,15,1], GoTo[vFDon1]; vLFSL2: vCursorY _ (vCursorY) - (2C), Skip[R<0]; T _ vCnt _ (vCnt) + (20C), Return; *Only three wakeups when showing the cursor to avoid slow timing. vCursorControl _ (vCursorControl) + (10000C), Call[vShowC]; *1st wakeup/scanline **Note: every cycle here degrades emulator performance about 0.5 percent. IOFetch16[vBitmap,vfBuf0], Call[vChkCall]; *2nd wakeup/scanline IOFetch16[vBitmap,vfBuf0], GoTo[vLFSL1]; vShowC: LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; vCursorY _ 100000C; *Next scanline, disable cursor T _ vCnt _ (vCnt) + (20C); vSLC _ (vSLC) - 1, IOStrobe, GoTo[.+3,R>=0]; *Last scanline of DCB; change TPC to initiate DCB setup. IOFetch16[vBitmap,vfBuf0], Call[vNextSL]; T _ LdF[vCR,15,1], GoTo[vFDon1]; IOFetch16[vBitmap,vfBuf0], GoTo[vNextSL]; :ELSE; ****************************************** *1st wakeup/scanline **Note: every cycle here degrades emulator performance about 0.5 percent. vLFSL0: IOFetch16[vBitmap,vfBuf0], Call[vChkCall]; *2nd wakeup/scanline IOFetch16[vBitmap,vfBuf0], GoTo[vLFSL1]; vLFSL2: vCursorY _ (vCursorY) - (2C), Skip[R<0]; T _ vCnt _ (vCnt) + (20C), GoTo[vNoCurs]; vCursorControl _ (vCursorControl) + (10000C); LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; vCursorY _ 100000C; *Next scanline, disable cursor T _ vCnt _ (vCnt) + (20C); vNoCurs: vSLC _ (vSLC) - 1, IOStrobe, GoTo[vLast16,R>=0]; *Last scanline of DCB; change TPC to initiate DCB setup. IOFetch16[vBitmap,vfBuf0], Call[vLastL]; T _ LdF[vCR,15,1], GoTo[vFDon1]; vLast16: IOFetch16[vBitmap,vfBuf0]; **This mi requires another task to run before the display task runs again. T _ vCnt _ (vCnt) + (Add[WordsPerLine!,20]C), Return; :ENDIF; ***************************************** :ELSE; **************************************** %NOTES: Since each bitmap starts on an even word, interleaved scanlines will be spaced by 4*n words, so the first word of each scanline has the same alignment within a quadword. % vWidNZ: Dispatch[vCR,15,1], Call[vWNZ2]; *Begin visible field with quadwords beginning each scanline Output[vCR,vCReg], GoTo[vWNE1]; vWNZ2: IOStrobe, Disp[.+1]; *Odd field--offset by WordsPerLine. vBitmap _ (vBitmap) + (WordsPerLine), DispTable[2]; *Even field--no offset. vWNZ0: Dispatch[vBitmap,16,1], Skip[Carry']; vBitmapHi _ (vBitmapHi) + (400C) + 1, GoTo[.-1]; T _ 44C, Disp[.+1]; PFetch2[vBitmap,vDBuf0], Return, DispTable[2]; T _ vCnt _ 0C, Call[vNoMsg]; *vCnt is returned in T *Begin visible field with non-quadaligned word pair beginning each scanline **Require another task to intervene between the previous mi and DisplayTask. Output[vCR,vCReg], GoTo[vWNO1]; v16a444: IOFetch16[vBitmap,vfBuf0,14], KBCheck; v16a4: T _ vCnt _ 24C; IOFetch16[vBitmap,vfBuf0,4], KBCheck; vIO4Ck: IOFetch4[vBitmap,vfBuf0,4], KBCheck; v4a4164: IOFetch4[vBitmap,vfBuf0]; T _ 34C; IOFetch4[vBitmap,vfBuf0], Return; *This page has code for fields which begin with a non-quadaligned word pair. *Scanlines begin at vWNO1 or at v4+2; at these places, vBitmap+T must point *at the first word and vCnt must contain the same value as T. v444416: Call[vIO4Ck]; IOFetch4[vBitmap,vfBuf0,10]; IOFetch4[vBitmap,vfBuf0,14]; IOFetch4[vBitmap,vfBuf0], GoTo[v16a]; v444: T _ 30C, Call[v4a4164]; v4: T _ 40C; IOFetch4[vBitmap,vfBuf0], Call[vCurs]; PFetch2[vBitmap,vDBuf0], GoTo[vWNOLp]; vCurs: vCursorY _ (vCursorY) - (2C), GoTo[vCursb,R>=0]; vCursorControl _ (vCursorControl) + (10000C); LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; vCursorY _ 100000C; *Next scanline, disable cursor vSLC _ (vSLC) - 1, IOStrobe, DblGoTo[vCursc,vCursd,R<0]; vCursb: vSLC _ (vSLC) - 1, IOStrobe, GoTo[vCursd,R>=0]; *Last scanline of DCB; change TPC to initiate DCB setup. vCursc: Call[vCurse]; T _ LdF[vCR,15,1], GoTo[vFDon1]; vCursd: vCnt _ T _ Add[WordsPerLine!,44]C; vCurse: vCursorControl _ vCursorControl, Return; *Interlock vWNOLp: T _ (vCnt) + (2C); vBitmap _ (vBitmap) + T; *NOTE: supplying some base register here is needed to avoid waiting for *the PFetch2 to finish (because BR defaults to vDBuf0). Output[vDBuf0,vBuf0,vCnt], Skip[Carry']; vBitmapHi _ (vBitmapHi) + (400C) + 1; Output[vDBuf1,vBuf0,vCnt]; T _ 67C; *Can do IOFetch16's so long as addr[8:15] .le. 360b (HW manual indicates a *hex-aligned restriction, but it is wrong). If addr[10:17b] .ls. 340b, *when normal IOFetch4-16-16 works LU _ (LdF[vBitmap,10,6]) - T - 1, Call[v36d]; v16a: T _ 24C; IOFetch16[vBitmap,vfBuf0], Call[vCurs]; *Next scanline continues here with vCnt in T vWNO1: PFetch2[vBitmap,vDBuf0], GoTo[vWNOLp]; *23 cycles from wakeup to here v36d: Dispatch[vBitmap,13,4], Skip[Carry]; *35 cycles from wakeup to vM1 IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4]; *2-4-16-16 T _ vCnt _ 20C, Disp[.+1]; *30 cycles from wakeup to vM1 IOFetch16[vBitmap,vfBuf0,0], Call[vChkMsg], At[vOdFet,0]; *2-16-16-4 IOFetch16[vBitmap,vfBuf0], GoTo[v4]; *36 cycles from wakeup to vM1 (65 cycles from wakeup to task) IOFetch4[vBitmap,vfBuf0,0], Call[v16a4], At[vOdFet,2]; *2-4-16-4-4-4-4 IOFetch4[vBitmap,vfBuf0], GoTo[v444]; *36 cycles from wakeup to vM1 IOFetch4[vBitmap,vfBuf0,0], Call[v16a4], At[vOdFet,4]; *2-4-16-4-4-4-4 IOFetch4[vBitmap,vfBuf0], GoTo[v444]; *36 cycles from wakeup to vM1 (65 cycles from wakeup to task) IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4], At[vOdFet,6]; *2-4-16-16 *30 cycles from wakeup to vM1 IOFetch16[vBitmap,vfBuf0,0], Call[vChkMsg], At[vOdFet,10]; *2-16-16-4 IOFetch16[vBitmap,vfBuf0], GoTo[v4]; *33 cycles from wakeup to vM1 IOFetch4[vBitmap,vfBuf0,0], GoTo[v444416], At[vOdFet,12]; *2-4-4-4-4-4-16 *33 cycles from wakeup to vM1 IOFetch4[vBitmap,vfBuf0,0], GoTo[v444416], At[vOdFet,14]; *2-4-4-4-4-4-16 *36 cycles from wakeup to vM1 (65 cycles from wakeup to task) IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4], At[vOdFet,16]; *2-4-16-16 *This page has code for fields which have quadaligned scanlines. Scanlines *may begin at vWNE1 or at vWNEgo+2; at these places the vBitmap base register *must point at the first word of the scanline, vCnt is "don't care", and *the last two words for the next scanline at vBitmap+44b must have been *PFetch'ed into vDBuf0/1. vCur2: Output[vDBuf0,vBuf0,vCnt]; vCursorY _ (vCursorY) - (2C), GoTo[vCur2b,R>=0]; vCursorControl _ (vCursorControl) + (10000C); LU _ (vCursorControl) + (10000C), Skip[ALU>=0]; vCursorControl _ (vCursorControl) and not (102000C); Output[vCursorControl,vCursor0,vCnt], Skip[ALU>=0]; vCursorY _ 100000C; *Next scanline, disable cursor vSLC _ (vSLC) - 1, IOStrobe, DblGoTo[vCur2d,vCur2c,R>=0]; vCur2b: vSLC _ (vSLC) - 1, IOStrobe, GoTo[vCur2d,R>=0]; *Last scanline of DCB; change TPC to initiate DCB setup. vCur2c: Output[vDBuf1,vBuf0,vCnt], Call[.+2]; T _ LdF[vCR,15,1], GoTo[vFDon1]; vDBuf1 _ vDBuf1, Return; vCur2d: vBitmap _ (vBitmap) + (LShift[WordsPerLine!,1]C); Output[vDBuf1,vBuf0,vCnt], Skip[Carry']; vBitmapHi _ (vBitmapHi) + (400C) + 1; *Fetch the end two words for next time unless last line T _ 44C; PFetch2[vBitmap,vDBuf0], Return; v4442: T _ 30C, Call[v4a4164]; v42: T _ 40C; IOFetch4[vBitmap,vfBuf0], Call[vCur2]; vWNE1: T _ 67C, GoTo[vWNEgo]; v4416442: IOFetch4[vBitmap,vfBuf0,4]; IOFetch4[vBitmap,vfBuf0,10]; T _ vCnt _ 34C, Call[v16a444]; IOFetch4[vBitmap,vfBuf0], GoTo[v42]; *Next scanline continues here with 67b in T *Can do IOFetch16's so long as address[8:15] .le. 360b *(HW manual indicates a hex-aligned restriction, but it is wrong) vWNEgo: LU _ (LdF[vBitmap,10,6]) - T - 1, Call[vWNELp]; *Return here for the normal IOFetch4-16-16-2 case IOFetch16[vBitmap,vfBuf0], Call[vCur2]; T _ 67C, GoTo[vWNEgo]; vWNELp: Dispatch[vBitmap,13,4], Skip[Carry]; IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4]; T _ vCnt _ 20C, Disp[.+1]; *This dispatch table is entered when vBitmap points into the last 40b words *of a 400b-word group. The code uses two IOFetch16's if possible, where the *constraint is that an IOFetch16 can't cross the 400b word boundary. IOFetch16[vBitmap,vfBuf0,0], Call[vChkMsg], At[vEvFet,0]; *16-16-4-2 IOFetch16[vBitmap,vfBuf0], GoTo[v42]; IOFetch4[vBitmap,vfBuf0,0], Call[v16a4], At[vEvFet,2]; *4-16-4-4-4-4-2 IOFetch4[vBitmap,vfBuf0], GoTo[v4442]; IOFetch4[vBitmap,vfBuf0,0], Call[v16a4], At[vEvFet,4]; *4-16-4-4-4-4-2 IOFetch4[vBitmap,vfBuf0], GoTo[v4442]; IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4], At[vEvFet,6]; *4-16-16-2 IOFetch16[vBitmap,vfBuf0,0], Call[vChkMsg], At[vEvFet,10]; *16-16-4-2 IOFetch16[vBitmap,vfBuf0], GoTo[v42]; IOFetch4[vBitmap,vfBuf0,0], GoTo[v4416442], At[vEvFet,12]; *4-4-4-16-4-4-2 IOFetch4[vBitmap,vfBuf0,0], GoTo[v4416442], At[vEvFet,14]; *4-4-4-16-4-4-2 IOFetch4[vBitmap,vfBuf0,0], GoTo[v16a4], At[vEvFet,16]; *4-16-16-2 :ENDIF; *************************************** vChkMsg: DblGoTo[vM1,vM0,IOAtten']; :IF[LFKeyBoard]; ****************************** %The keyboard microcomputer delivers messages to the UTVFC over the backchannel one bit at-a-time, with a format as follows: 0 SSSBBB YYYY XXXX 1 -or- KKKKKKKK 1 SSSBBB YYYY XXXX 1 where the right-most bit is the first to arrive. XXXX and YYYY are two's complement mouse delta-X and delta-Y, respectively; SSSBBB are mouse buttons (in what order?) and status bits (video, VS, and power supply normal); a 0 after this terminates the message; a 1 is followed by 8 bits of key data, which encode key strokes (i.e., transitions of keyboard characters up or down); the vPostKey subroutine uses KeyTable (next page) to translate KKKKKKKK into bits to be set/cleared in the storage table at vCSB+10. The mouse resolves 200 pixels/inch. 35 inches/sec is the maximum speed required for tracking the mouse. We update the mouse position once every field, ~77 times a second. Therefore tracking the mouse at maximum velocity requires a field large enough hold 35*200/77 = 91d counts. Adding 1 bit for sign mandates an 8 bit field for x and another 8 bit field for y. vMsgStatus remains 75b until a backchannel 1 (IOAtten) begins a message. Bit 0 indicates a message in progress; vMsgStatus[10:12b] is "state" and vMsgStatus[13:17b] is "count". Carry-out of count increments state: state size 0 -- idle, terminated by a 1 bit 1 3 Build 1st 3 bits of delta X. 2 4 Collect 4th bit of X and post; build 1st 3 bits of delta Y. 3 7 Collect 4th bit of Y and post; build all 6 bits of BS. 4 1 Post buttons and status; 0 in 1st bit terminates message. 8 if 1 in 1st bit, continue and collect 1st 7 bits of key data. 5 1 Collect 8th bit of K and post; reset to state 0. Must preserve T or reload it from vCnt for main loop. Timing is 4 cycles when idle, 7 for the 1st message bit, 10 for non-transition message bits, 15 or 16 on mouse delta X or delta Y, 15 or 16 for key data, and 15 or 14 for buttons and status. Results are preserved in RM until the next vertical blanking period, then posted. % vM1: LU _ LdF[vMsgStatus,13,5], Skip[R<0]; *0's (IOAtten') here vMsgRet: vMsg _ RSh[vMsg,1], Return; *Idle Dispatch[vMsgStatus,10,3], Skip[ALU=0]; *Dispatch on state bits vMsgStatus _ (vMsgStatus) + 1, GoTo[.-2]; *Count .ne. 0 vMsg _ RSh[vMsg,1], Disp[.+1]; *Reach the dispatch table after 7 cycles. *States 0-1 never get here; states 6-7 impossible. *State 2 .eq. X; set count to 4-1; clear high bits of vMsg for vKY later. vKX: T _ vMsg _ RSh[vMsg,14], GoTo[vKeyX], DispTable[4,17,2]; *State 3 .eq. Y; set count to 7-1 vKY: T _ vMsg _ RSh[vMsg,4], GoTo[vKeyY]; *State 4 .eq. Buttons; change to state 0 (no char follows) or 5, count 10b-1 T _ vMsg _ RSh[vMsg,7], GoTo[vKeyBI]; *State 5: post key data; reset to state 0. vKB: T _ vMsg _ RSh[vMsg,10]; vKeyBuffer _ (LSh[vKeyBuffer,10]) or T; vMsgStatus _ 75C; vMSU: T _ vCnt, Return; vKeyY: vMsgStatus _ (vMsgStatus) or (32C), Skip; vKeyX: vMsgStatus _ (vMsgStatus) or (35C); vMouseDxy _ (vMouseDxy) + T, GoTo[vMSU]; *State 4; set count to 10b-1 if key data follows. vKeyBS: vMsgStatus _ (vMsgStatus) or (31C), Skip; vKeyBI: vMsgStatus _ 75C; *Directly here if no key data follows vButtons _ T, GoTo[vMSU]; vKCount: vMsgStatus _ (vMsgStatus) + 1, Return; *Dispatch on state bits and high bit of count. vM0: LU _ Dispatch[vMsgStatus,10,4], Skip[R<0]; *1's (IOAtten) here vMsgStatus _ (vMsgStatus) or (100000C), GoTo[vMsgRet]; vMsg _ RSh[vMsg,1], Disp[.+1]; *Dispatch on state bits *Timing to the dispatch table is 6 cycles. vMsg _ (vMsg) or (100000C), GoTo[vKCount], DispTable[10,17,3]; vMouseDxy _ (vMouseDxy) - (10C), GoTo[vKX]; vMsg _ (vMsg) or (100000C), GoTo[vKCount]; vMouseDxy _ (vMouseDxy) + (174000C), GoTo[vKY]; vMsg _ (vMsg) or (100000C), GoTo[vKCount]; T _ vMsg _ RSh[vMsg,7], GoTo[vKeyBS]; vMsg _ (vMsg) or (100000C), GoTo[vKCount]; vMsg _ (vMsg) or (100000C), GoTo[vKB]; %Have 0 to 2 characters represented in vKeyBuffer; determine the bit and word position in the vCSB+10 bit table by lookup in IM KeyTable; set/clear that bit according to key down/up. Have to manipulate the vKeyBuffer register in such a way that if vChkMsg shifts in another event, nothing is lost; also, don't want to process events twice. Algorithm is: If LH of vKeyBuffer is non-zero, post the event, zero the LH, and done; else left shift vKeyBuffer 10b and conditionally post. Both of these tests must be made on one scanline. % vPostKey: T _ LdF[vKeyBuffer,1,5], GoTo[vPKGo,ALU#0]; *If no data, vKeyBuffer _ LSh[vKeyBuffer,10]; *shift to other event T _ LdF[vKeyBuffer,1,5], Skip[ALU=0]; vBitmapHi _ (vBitmapHi) + T, GoTo[vPKGo1]; Input[vDBuf0,0], GoTo[vKBIOS]; vPKGo: vBitmapHi _ (vBitmapHi) + T; vPKGo1: T _ LdF[vKeyBuffer,6,1]; *Set H2 to high/low word *Now set vBitmapHi[0:7] to 0 for later while reading IM. APCTask&APC _ vBitmapHi, vBitmapHi _ T, NoRegILockOK; ReadCS; *Get the word LU _ LdF[vKeyBuffer,7,1], DispTable[1,1,0]; *Even loc after ReadCS *low or high byte T _ 10C, GoTo[vPKeyL,ALU=0]; *vCSB+10 = 1st KB word *Low byte of IM word. T _ (LdF[CSData,15,3]) + T; *Get word number PFetch1[vCSB,vDBuf0]; *Fetch KB word vBitmap _ T; *Bypass kludge to remember address Dispatch[CSData,11,2]; *Dispatch on top two bits of bit number *Dispatch on bottom two bits of bit number Dispatch[CSData,13,2], Disp[.+1]; vPKeyB: vDBuf1 _ T _ 100000C, Disp[vPKey0], DispTable[4]; vDBuf1 _ T _ 4000C, Disp[vPKey0]; vDBuf1 _ T _ 200C, Disp[vPKey0]; vDBuf1 _ T _ 10C, Disp[vPKey0]; *Shift possible 2nd character into bits 0:7 and test for key up/down vPKey0: vKeyBuffer _ RHMask[vKeyBuffer], DblGoTo[vPKUp,vPKDwn,R<0], DispTable[4]; T _ RSh[vDBuf1,1], GoTo[vPKey0]; T _ RSh[vDBuf1,2], GoTo[vPKey0]; T _ RSh[vDBuf1,3], GoTo[vPKey0]; vPKUp: vDBuf0 _ (vDBuf0) or T, IOStrobe, Skip; *Key up--set bit vPKDwn: vDBuf0 _ (vDBuf0) and not T, IOStrobe; *Key down--clear bit *Use SrcDest=0 to setup IOAtten here. PStore1[vBitmap,vDBuf0,0], GoTo[vKBIOS]; *Store word *High byte of IM word. vPKeyL: T _ (LdF[CSData,5,3]) + T; PFetch1[vCSB,vDBuf0]; vBitmap _ T; Dispatch[CSData,1,2]; Dispatch[CSData,3,2], Disp[vPKeyB]; *KeyBoard Translation Table from Level III to Alto *Entry in the table is bitnumber*8+wordnumber Set[ByteLoc,KeyTable]; Macro[Byte,IMData[LH[LShift[#1,10],#2] RH[LShift[#3,10],#4] At[ByteLoc]] Set[ByteLoc,ADD[ByteLoc,1]]]; KTable: Byte[177,177,177,177]; *00 Byte[177,177,177,177]; Byte[177,177,177,177]; Byte[177,177,177,177]; *04: Byte[005,115,155,105]; * D1, T10(\), T9, T8. Byte[075,173,065,042]; * T7, R6(BW), T6, L12(FL4). Byte[163,114,055,045]; * L9(FL3), L6(LF), T5, T4. Byte[035,015,025,177]; * T3, T2, T1(esc), . *10: Byte[164,177,172,144]; * R4, , R1, R2. Byte[004,125,177,024]; * R5, R3(FR5), , L10. Byte[034,044,054,177]; * L7, L4, L1. Byte[177,064,177,177]; * , A9, , . *14: Byte[154,074,171,150]; * R7, R10, R11, R8. Byte[014,153,113,161]; * R9(FR4), R12(swat), A7(space), L11(CTL). Byte[160,124,134,162]; * L8, L5, L2, L3(DEL). Byte[104,165,177,177]; * A8, A11, , . *20: Byte[177,175,143,140]; * , A12, A6(shift-R), /. Byte[122,131,073,063]; * ., (comma), m, n. Byte[072,070,052,101]; * b, v, c, x. Byte[102,177,135,177]; * z, , 47, . *24: Byte[142,152,141,132]; * A4(Return), 46(_), (quote), :. Byte[121,110,062,043]; * l, k, j, h. Byte[023,032,050,041]; * g, f, d, s. Byte[051,177,103,112]; * a, , A3(lock), A5(shift-L). *30: Byte[145,151,123,130]; * A10, 45(]) , 42([), p. Byte[111,071,060,033]; * o, i, u, y. Byte[013,003,030,021]; * t, r, e, w. Byte[031,022,177,174]; * q, A1(tab), , D2. *34: Byte[170,133,120,100]; * A2(bs), =, -, 0 Byte[061,053,040,020]; * 9, 8, 7, 6. Byte[000,010,001,011]; * 5, 4, 3, 2. Byte[002,012,177,177]; * 1, 48, , . :ELSE; **************************************** %CSL KEYBOARD vChkMsg collects and posts incoming messages from keyboard microcomputer. Form of message is 1xxxxtttmmmmmmmmmmmmmmmm1, where t = type bit, m = message bit, x = unused, and 1's represent the leading and trailing flags. IOAtten encodes the bits, delivered one/scanline. At message start, vMsgStatus>=0 and the leading 1 has just been shifted out of vMsg; in this case vMsg is saved in vMsgStatus with a sign bit of 1. At message end, vMsgStatus<0 and the trailing 1 has just been shifted into the low bit of vMsg. Timing here to return = 4 cycles usually; 13 on message start; 30 on KB0 to KB3 (includes 10 cycles MC1 wait by next task to run); 25 on Mouse XY end; and 19 on KSMS end. % vM0: vMsg _ LSh[vMsg,1], DblGoTo[vMsgA,vNoMsg,R<0]; vM1: vMsg _ (LSh[vMsg,1]) + 1, DblGoTo[vMsgA,vNoMsg,R<0]; vNoMsg: T _ vCnt, Return; vMsgA: T _ LSh[vMsgStatus,7], GoTo[vMsgEnd,R<0]; *Just collected 1st word of message; save it in vMsgStatus with sign of 1 vMsgBg: T _ (vMsg) or (100000C); vMsgStatus _ T; vMsg _ 400C, GoTo[vNoMsg]; vMsgEnd: *End-of-message--rebuild message body T _ vMsg _ (RSh[vMsg,1]) or T, Skip[R Odd]; vMsgStatus _ 0C, GoTo[vEOMs]; *Bad end flag--reject message *Dispatch on type bits, setup for next message. vMsgStatus _ Dispatch[vMsgStatus,4,3]; vKeyBuffer _ T, Disp[.+1]; *Timing from VM0/VM1 to here = 13 cycles *Reject type 0 (possibly noise) vMsg _ 0C, GoTo[vNoMsg], DispTable[10]; PStore1[vCSB,vKeyBuffer,10], GoTo[vEOMs]; *KB0 PStore1[vCSB,vKeyBuffer,11], GoTo[vEOMs]; *KB1 PStore1[vCSB,vKeyBuffer,12], GoTo[vEOMs]; *KB2 PStore1[vCSB,vKeyBuffer,13], GoTo[vEOMs]; *KB3 *Stores KSMS in 177033; junk in 177030-177032 would be ok. vButtons _ T, GoTo[vEOMs]; *KSMS (keyset/mouse switches) vKeyBuffer _ 200C, GoTo[vMXY]; *Delta X and Y for mouse *Depressing the boot button causes an endless stream of IOAtten' which *manifests as an endless stream of 1's here; for a message in which both *the body and type are all 1's (to eliminate noise) boot the machine. *On keyboard boot, KB0 is 177776b for BS or 177577b for 0; KB0 is passed *through the activity of Initial to the microcode being booted in BootTask's *T register, which is not smashed. vMsg _ (vMsg) + 1; *Boot button Skip[ALU=0]; vEOMs: vMsg _ 0C, GoTo[vNoMsg]; PFetch1[vCSB,vMsg,10]; *Fetch KB0 as parameter for Boot vKeyBuffer _ LoA[BootSV]; vKeyBuffer _ (vKeyBuffer) or (HiA[BootSV,BootTask]); APCTask&APC _ vKeyBuffer; LU _ MNBR _ vMsg, Return; *Continue in BootTask at SuckADuck *Delta X and delta Y are sent excess 128d; the value in vMouseDxy is *initialized to 128d and the mouse cannot move more than about +/- 91d *in one field time, so overflow of the 8 bit fields is impossible. vMXY: T _ (vKeyBuffer) or (100000C); T _ (vMsg) - T; vMouseDxy _ (vMouseDxy) + T, GoTo[vEOMs]; SetTask[BootTask]; *Only the T-registers of unused tasks (Possibly MNBR or SALUF also) will *survive across the boot, so we tuck away KB0 so that microcode being booted *can decide what kind of boot to do. SuckADuck: T _ MNBR, At[BootSV]; Boot, GoTo[.]; :ENDIF; *************************************** :IF[LFKeyBoard]; END[LFDisplay]; :ELSEIF[LFMonitor]; END[CSLFDisplay]; :ELSE; END[CSLDisplay]; :ENDIF;(2048)\f5