Insert[DisplayDefs]; *Defs file common to DisplayInit and Display :IF[LFKeyBoard]; *Ed Fiala 10 September 1982 TITLE[LFDisplay]; :ELSE; TITLE[Display]; :ENDIF; Loca[IZTab0,DisplayPage,20]; *1st Dispatch for initial zeroes Loca[IZTab4,DisplayPage,40]; *2nd Dispatch for initial zeroes Loca[dSendMem,DisplayPage,60]; *2-way Dispatch avoiding branch burp Loca[FZTab0,DisplayPage,100]; *1st Dispatch for final zeroes Loca[FZTab4,DisplayPage,120]; *2nd Dispatch for final zeroes Loca[dNL16,DisplayPage,140]; *Dispatch for final data IOFetch4's SetTask[0]; %Alto.Mc jumps here on opcode 61027b. T contains AC0, the new frame-fill parameter. The old frame-fill parameter is returned in AC0. The values normally used are 11b (normal 77 Hz.) and 213b (videotaping 60 Hz.). Only assemble this code for the resident to avoid overwriting xoPage in the overlay. % :IF[IFE[CSLOverlay,1,LFKeyboard,1]]; ********** OnPage[xoPage]; vFrameRate: RTemp ← IP[vFieldFill]C; StkP ← RTemp; MNBR ← Stack, Stack ← T, NoRegILockOK; T ← MNBR, GoTo[TtoAC0CSR]; *In Alto.Mc :ENDIF; *************************************** %Alto.Mc jumps here on opcode 61033b. AC0 is -1 to make the full monitor width available, 0 to revert to Alto-size picture. The resulting maximum display width is returned in AC0. This code presently always returns 38d (i.e., the Alto-compatible words/scanline) as the result even on LF monitors because huge extra overhead would result unless the horizontal RAM is reloaded when changing the width. The implementation is put here rather than on xoPage to allow variant implementations for Lisp and Smalltalk, if desired. % OnPage[DisplayPage]; dpDspWid: AC0 ← WordsPerLine, Return, At[dpWidthLoc]; SetTask[DpTask]; *Initialization and end-of-field come to vFDone. vFDone must be absolutely *placed at an odd location so that the exit from display initialization *to here will be the same for both LF and CSL keyboard microcode. :IF[LFKeyBoard]; ****************************** Macro[KBCheck,GoTo[vChkMsg]]; vFDone: T ← LdF[vCR,15,1], At[FldDoneLoc]; *Set up to load cursor memory--mask Field bit. vCursorY ← (Zero) xnor T, GoTo[.+3,ALU#0]; *Test field vCursorControl ← 0C; *Even scan lines next T ← (vFieldFill) - 1, GoTo[.+3]; vCursorControl ← 4000C; *Odd scan lines next T ← vFieldFill; vCnt ← T; vNWrds ← lfSyncLength; *vLink←lfBlankLength done below. *Complement even/odd field bit and turn on blanking vCR ← (vCR) xor (OddFld&Blank), Call[vKBOut]; *Two extra scanlines for LF keyboard to post results *1st scanline posts mouse x,y and buttons; vMouseXY contains two signed *7-bit numbers in bits 0:6 and 9:15 added to VM 424 and 425, respectively. PFetch4[vMDS420,vDBuf0,4]; *Mouse X,Y, --, -- T ← LdF[vMouseXY,0,7]; vMouseXY ← LCy[vMouseXY,11], Skip[R>=0]; vDBuf0 ← (vDBuf0) - (200C); vDBuf0 ← (vDBuf0) + T; T ← LdF[vMouseXY,0,7], Skip[R>=0]; vDBuf1 ← (vDBuf1) - (200C); vDBuf1 ← (vDBuf1) + T; vMouseXY ← 0C; PStore4[vMDS420,vDBuf0,4]; *Mouse buttons; vDBuf0 and vTemp stored into 177030 and 177033 by PStore4. *Here the mouse buttons are in vButtons[11:13d]; power supply normal, VS, *and video are in vButtons[8:10] but not used. T ← (vButtons) and (4C); T ← (LdF[vButtons,13,2]) or T; T ← vDBuf0 ← (Zero) xnor T; vTemp ← T; T ← (vZero) - (350C); *177030-177400 PStore4[vMDS177400,vDBuf0], Call[vKBIn]; *Check input & task *Have 0 to 2 characters represented in vKeyBuffer; determine the bit and *word position in the bit table in VM 177034 - 177043 by lookup in IM *KeyTable; set/clear that bit according to key down/up. *Smashes vTemp, vDBA (= vSize), and vBase; initializes vLink. @vVB0: LU ← LHMask[vKeyBuffer], Call[vPostKey]; :ELSE; **************************************** Macro[KBCheck,DblGoTo[vM1,vM0,IOAtten']]; Set[DisplayPage2,DisplayPage]; Set[DisplayPage3,DisplayPage]; vFDone: Input[vTemp,0,vTemp], At[FldDoneLoc]; *Controller ID in bits 10:14b T ← LdF[vCR,15,1]; vCursorY ← (Zero) xnor T, Skip[ALU#0]; *Test field vCursorControl ← 0C, Skip; *Even scan lines next vCursorControl ← 4000C; *Odd scan lines next T ← (vFieldFill) + T; LU ← (vTemp) and (40C); *value 3 or 11 for LF, 5 for CSL display vCnt ← T, GoTo[vCSLMon,ALU#0]; *Here if LF (Ball) monitor vNWrds ← lfSyncLength; vLink ← lfBlankLength, GoTo[vCSLKB]; vCSLMon: vNWrds ← cslSyncLength; vLink ← cslBlankLength; vCSLKB: vCR ← (vCR) xor (OddFld&Blank), Call[vKBOut]; *Start blanking :ENDIF; *************************************** *Repeat VSStart+1 or +2 times (-1 if LF KB) for blank lines at end of field vVB1: Input[vTemp,0,vCnt], GoTo[vKBIOS,R>=0]; *Start VSync PFetch2[vMDS420,vDBA,6]; *Fetch Cursor X and Y from 426b-427b vCR ← (vCR) or (VS); vCnt ← 7C, Call[vKBOut]; @vVS0: T ← vSLC, Skip[R Even]; *Cursor Y coordinate *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; vDBA ← (vDBA) xnor (0C), Call[.+1]; *vDBA ← cursor X' *Continue VSync; load 1st 8 words of cursor memory in 8 scanlines @vVS1: T ← 11C, A ← vCnt, NoRegILockOK, GoTo[vLdCur,R>=0]; vDBA ← (vDBA) + (4C), Call[.+1]; *increment in nibble number *Continue VSync; repeats vSyncLength+1 scanlines @vVS2: Input[vTemp,0,vNWrds], Skip[R<0]; vNWrds ← (vNWrds) - 1, IOStrobe, KBCheck; vNWrds ← Add[40000,vStart]C; Output[vNWrds,vBufStart]; *Start←vBufSt, ForceIARLoad'←0 vCR ← (vCR) and not (VS); Output[vZero,vLdIAR]; *IAR←Start (resynchronize) vCurrentY ← vMaxYh; *End VSync vCurrentY ← (vCurrentY) + (vMaxYl), Call[vKBOut]; *Repeat vBlankLength+1 blank scanlines at top of field @vVB2: Input[vTemp,0,vLink], Skip[R<0]; vLink ← (vLink) - 1, IOStrobe, KBCheck; *Start frame @vStF: vCR ← (vCR) and not (Blank); *Fetch chain head (vLink) & interrupt word (vNWrds). Here IOAtten is setup *by the two low address bits of vLink being 00. PFetch2[vMDS420,vLink,0]; T ← (vDBA) and not (176000C), Call[vSF1]; *save low ten bits vTemp ← Add[140000,vStart]C; *ReSync IAR Output[vTemp,vBufStart]; *Start←vBufSt, ForceIARLoad'←1 **NOTE: Must check IOAtten for the backchannel prior to posting the interrupt **request with DoInt--these output to RS232 device disturbing IOAtten. *Field interrupt bits in T; DoInt tasks, smashes vDBuf0 and vDBuf1. LoadPageExternal[DoIntPage]; T ← vNWrds, IOStrobe, CallExternal[DoIntLoc]; *Set CEnable bit; set cursor word number to largest line, so that it will *count to 0/1 when cursor is first shown. vCursorControl ← (vCursorControl) or (172000C), GoTo[vNextDCB]; *Add X to initial Y. vSF1: vCursorControl ← (LHMask[vCursorControl]) + T, KBCheck; vKBOut: Output[vCR,vCReg]; *Avoid Output-Output-PStore4 problem by Nop; *waiting here for 5 mi IOStrobe, GoTo[vChkMsg]; *SUBROUTINE vLdCur loads one word of the cursor memory, then goes to vChkMsg. vLdCur: T ← (LdF[vCursorControl,1,4]) + T; PFetch1[vMDS420,vDBuf1]; *Fetch word to load (VM 431+vSLC.1..4) vCursorControl ← LHMask[vCursorControl]; vTemp ← 2C, Skip; *Loop 4 times (one nibble each time) vLdCLp: vCursorControl ← (vCursorControl) + (4C); *vTemp as base reg. prevents abort Output[vCursorControl,vCursor0,vTemp]; vDBuf1 ← LCy[vDBuf1,4]; Output[vDBuf1,vCursorMem0,vTemp], Skip[R<0]; vTemp ← (vTemp) - 1, GoTo[vLdCLp]; vCursorControl ← (vCursorControl) + (10000C); vKBIn: Input[vTemp,0]; *Set up IAddr.6..7 = 0 to make IOAtten reference *channel 0 for vChkMsg vKBIOS: vCnt ← (vCnt) - 1, IOStrobe, KBCheck; *Here when DCB chain is exhausted but not done with field yet. vFrameDone: T ← vCurrentY, Call[vCyg0]; *For zero-width DCBs send two scan lines of zeroes (to fill the two ping-pong *buffers), then do only cursor and message checking until the next DCB. vWidZ: Output[vCR,vCReg]; Call[vFZ38]; *Fill 1st buffer (Avoid Output here for vCR write) Call[vFZ38]; *Fill 2nd buffer *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** vFZ38x: vCursorY ← (vCursorY) - (2C), GoTo[NoCursor,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], Skip[ALU>=0]; *Set vCursorY to count to a large positive number after one more iteration. vCursorY ← 100000C; vSLC, DblGoTo[Cursn,Cursm,R<0]; NoCursor: Input[vTemp,0,vSLC], Skip[R<0]; *Skip if DCB or field exhausted Cursm: vSLC ← (vSLC) - 1, IOStrobe, KBCheck; Cursn: vSLC ← (vSLC) - 1, IOStrobe, Call[vChkMsg]; %Worst timing and wakeup requirements for a scanline begin here: a long DCB coincident with final cursor line, mouse XY end-of-message on backchannel, unfavorable data alignment so no IOFetch16's can be used, 5-word tab, and 3 final zero words/scanline. DCB setup and the 1st scanline must complete in about 29 us (LF) or 38 us (CSL), and worst timing for any one lower and all higher priority tasks must also be allowed for. The emulator is presently restricted to about 44 cycles between tasks, and the timer task to about 38 cycles; disk and ethernet controllers unfortunately have some code that runs even longer without tasking. On one Bravo display, it was observed that tasking once after setting up the scanline loop worked for CSL monitors but seemed solidly too slow for LF monitor/keyboards; since the cursor was not being shown at the same time, nor the slow mouse XY end-of-message code for the CSL monitor being executed, we can infer cases worse than the one observed--removing the task after setting up the scanline fixed this problem. After this observation, the DCB setup microcode has been improved about 10 cycles and worst cases in end-of-message code about 8 cycles; however, it does not seem desirable to put the task after DCB setup back into the code unless additional improvements are made in worst case timing. NOTE RESTRICTIONS: 1) No bitmap is permitted to cross a 64k boundary; 2) DCB's all must be located in MDS. DCB format (Check this out): word 0 Link to next DCB (Zero terminates DCB chain) word 1 Bit 1 is 0 for white background, 1 for black background Bits 3:7 are tab count (nzeroes sent before memory data). Bits 8:15 are the no. data words/scanline; must be even. word 2 Pointer to DCB if scan line count sign is 0 else a password (177423b) enabling the long pointer; if password is wrong, DCB chain terminates. word 3 scan line count (sign bit 1 means "long") words 4-5 Long pointer to DCB if scan line count sign is 1 % vNextDCB:vCurrentY ← (vCurrentY) - 1; vDoDCB: T ← (vLink) + (2C), GoTo[vFDone,ALU<0]; PFetch2[vMDS,vDBA]; *Fetch DBA and SLC (spurious fetch from *location 2 if Link=0) :IF[AltoXMMode]; ************************** vBase ← T, LoadPage[DisplayPage2]; *Bypass kludge to get Link+2 T ← 351C; *Add bits 14:15b of 177751 to vMDShi OnPage[DisplayPage2]; PFetch1[vMDS177400,vTemp]; T ← vLink, LoadPage[DisplayPage]; PFetch2[vMDS,vLink]; *Fetch Link & NWrds OnPage[DisplayPage]; :ELSE; ************************************ vBase ← T; *Bypass kludge to get Link+2 T ← vLink; PFetch2[vMDS,vLink]; *Fetch Link & NWrds :ENDIF; *********************************** T ← vMDShi, Skip[ALU#0]; *Skip if Link .ne. 0 GoTo[vFrameDone]; vBasehi ← T; *Bias the scan line count, clear and branch on the long DCB flag vSLC ← (LdF[vSLC,1,17]) - 1, GoTo[LongDCB,R<0]; :IF[SingleDCBMode]; *********************** vSingleDCBFlag ← 0C; :ENDIF; *********************************** :IF[AltoXMMode]; ************************** T ← vDBA, LoadPage[DisplayPage3]; vBase ← T, LoadPage[DisplayPage]; OnPage[DisplayPage3]; vTemp ← T ← RSh[vTemp,2]; OnPage[DisplayPage]; T ← (LSh[vTemp,10]) or T; vBasehi ← (vBasehi) + T; :ELSE; ************************************ T ← vDBA; vBase ← T; :ENDIF; *********************************** *Timing = 25 cycles (short pointer) or 39 cycles (long pointer) to here. *vSLC ← Min(vCurrentY,vSLC)-1; vCurrentY ← vCurrentY-vSLC+1 ChkBkgnd: vSLC ← T ← (vSLC) - 1; vCurrentY ← (vCurrentY) - T - 1; LU ← Dispatch[vNWrds,1,1], GoTo[ChkBk1,ALU>=0]; T ← (vCurrentY) + T, Call[vCyg0]; LU ← Dispatch[vNWrds,1,1]; ChkBk1: T ← RHMask[vNWrds], Disp[.+1]; *Field bit vCR ← (vCR) and not (BlackBackground), DblGoTo[vWidNZ,vWidZ,ALU#0], DispTable[2]; vCR ← (vCR) or (BlackBackground), DblGoTo[vWidNZ,vWidZ,ALU#0]; vCyg0: vSLC ← T, UseCTask; vCurrentY ← Zero, Return; LongDCB: PFetch2[vBase,vBase,2]; *Fetch long pointer at Link+4 :IF[SingleDCBMode]; *********************** vSingleDCBFlag ← 1C; :ENDIF; *********************************** LU ← (vDBA) xnor (354C); *do the dcb only if DCB[3] = 177423b vBasehi ← LSh[vBasehi,10], DblGoTo[vFrameDone,ChkBkgnd,ALU#0]; **Since 64k-boundary crossings aren't handled correctly in the code which **follows, this initialization is pointless. * vBasehi ← T ← LSh[vBasehi,10]; * T ← vBasehi ← (RSh[vBasehi,10]) + T + 1; *Fix up long pointer * vBasehi ← (FixVA[vBasehi]) or T, GoTo[ChkBkgnd]; :IF[IFG[WordsPerLine!,46,1,0]]; *********** vFZ38: IOFetch16[vMDS177400,vfBuf0,0]; IOFetch16[vMDS177400,vfBuf0,0]; IOFetch16[vMDS177400,vfBuf0,0]; IOFetch16[vMDS177400,vfBuf0,0], GoTo[vFZ38x]; :ELSE; ************************************ vFZ38: Output[vZero,vBuf0]; Output[vZero,vBuf0]; IOFetch4[vMDS177400,vfBuf0,0]; IOFetch16[vMDS177400,vfBuf0,0]; IOFetch16[vMDS177400,vfBuf0,0], GoTo[vFZ38x]; :ENDIF; *********************************** %Original vNWrds contains the following: vNWrds: [0] unused [1] BlackBackground [2] unused [3:7] TabCount nzeroes to send before sending memory data [8:15] WordCount No. data words/scanline. Unused if DCB.Width = 0; might exceed 46b; must be EVEN because each bitmap must start on even word. Compute values for vNWrds and vSize as follows: vNWrds: [0:2] unused [3:7] TabCount no. of initial zero words [8:15] BaseAdjust nwords to add to vBase to get start of next scanline's bitmap. Because of interleaving this will be 2*WordCount - words transmitted which will be EVEN. vSize: [0] FinalFillIsZero means FinalFill is uninteresting [1] unused [2:7] FinalFill number of zeros to fill out the scanline [8] unused [9:15] DataCount nwords to send to controller from bitmap; will be EVEN NOTES: (1) Since each bitmap starts on an even word, interleaved scanlines will be spaced by 4*n words, so the decision about whether a scanline is quadaligned could be done during DCB setup rather than on each scanline. % *DCB setup timing is 36 to 50 cycles to here (+9 if last DCB overflows field) *vCR[0] = complement of vCR[15b] so that ALU<0 test can be used here. vWidNZ: Dispatch[vCR,15,1]; vSize ← WordsPerLine, Disp[.+1]; vBase ← (vBase) + T, DispTable[2]; *Odd field--offset by WordCount LU ← LdF[vNWrds,3,5], Call[vWidthSetup]; *Even field--no offset *DCB setup timing is 53 to 78 cycles to here (+9 if last DCB overflows field) :IF[SingleDCBMode]; *********************** Output[vCR,vCReg], Call[.+1]; :ENDIF; *********************************** *Wakeup here once/scanline in the DCB when there are no leading 0's **Note: every cycle here degrades emulator performance about 0.3%. Dispatch[vBase,16,1], GoTo[vDSend]; :IF[SingleDCBMode]; *********************** *Subtract desired number of words from 46b to get number of trailing 0's vWidthSetup: vSize ← (vSize) - T, GoTo[vTabNZ,ALU#0]; :ELSE; ************************************ vWidthSetup: Output[vCR,vCReg], GoTo[vTabNZ,ALU#0]; *Subtract desired number of words from 46b to get number of trailing 0's vSize ← (vSize) - T; :ENDIF; *********************************** LU ← vSize, GoTo[vEndSz,ALU>=0]; *Go if it fits *Specified size is too big--truncate to 38d words vNWrds ← (vNWrds) + T; T ← vSize ← WordsPerLine; *size = 46b = 38d *vNWrds[10:17] ← 2 x vNWrds - WordsPerLine vNWrds ← (vNWrds) - T, GoTo[vEndSa]; *DCB setup timing is 60 to 82 cycles to here. :IF[SingleDCBMode]; *********************** vTabNZ: T ← LdF[vNWrds,3,5], Call[vScanI]; Output[vCR,vCReg], Call[.+1]; :ELSE; ************************************ vTabNZ: T ← (LdF[vNWrds,3,5]) + T, Call[vScanI]; :ENDIF; *********************************** *Wakeup here once/scanline in the DCB when there are leading 0's Dispatch[vNWrds,6,2], GoTo[vIZSnd]; *DCB setup timing is 48 to 64 cycles to here vScanI: vSize ← (vSize) - T; *WordsPerLine - WordCount - TabCount T ← RHMask[vNWrds], Skip[ALU<0]; *check for fit LU ← vSize, GoTo[vEndSz]; *it fits *Scanline too big--truncate to WordsPerLine words; one final zero if TabCount *odd. BaseAdjust is 2*WordCount - [WordsPerLine - TC] = *2*nwords desired sent - nwords sent, where TC is TabCount+1 if TabCount odd. vNWrds ← (vNWrds) + T + 1; *TabCount,,2*WordCount+1 T ← vSize ← (vSize) + T, Skip[R Odd]; *WordsPerLine-TabCount vNWrds ← (vNWrds) - T - 1, GoTo[vEndSa]; *2*WordCount-(WordsPerLine-TabCount) vSize ← (vSize) + (400C); *Final zero if TabCount odd vNWrds ← (vNWrds) - T, GoTo[vEndSb]; *2*WordCount-(WordsPerLine-TabCount-1) vEndSz: vSize ← (LSh[vSize,10]) or T, Skip[ALU#0]; vEndSa: vSize ← (vSize) or (100000C); *Set FinalFillIsZero :IF[SingleDCBMode]; *********************** vEndSb: Input[vTemp,0,vSingleDCBFlag], Skip[R Odd]; vCnt ← T, UseCTask, GoTo[vNoMsg]; vCnt ← T, IOStrobe, KBCheck; :ELSE; ************************************ vEndSb: vCnt ← T, UseCTask, GoTo[vNoMsg]; :ENDIF; *********************************** *Dispatch Table for initial zero memory references. vIZSnd: Dispatch[vNWrds,3,3], Disp[.+1]; *Tab if necessary Output[vZero,vBuf0], At[IZTab0,3]; Output[vZero,vBuf0], At[IZTab0,2]; Dispatch[vNWrds,3,3]; Output[vZero,vBuf0], Disp[.+2], At[IZTab0,1]; Disp[.+1], At[IZTab0,0]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,7]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,6]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,5]; IOFetch16[vMDS177400,vfBuf0,0], At[IZTab4,4], GoTo[vIZ00]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,3]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,2]; IOFetch4[vMDS177400,vfBuf0,0], At[IZTab4,1]; vIZ00: Dispatch[vBase,16,1], At[IZTab4,0]; *Have 170b in T; know that vCnt .gr. 0 words will be sent (must be EVEN) vDSend: vCnt ← (vCnt) - (20C), Disp[.+1]; PFetch2[vBase,vDBuf0,0], At[dSendMem,1]; vBase ← (vBase) + (2C); vCnt ← (vCnt) - (2C); **NOTE: vCnt as the base register below prevents a 6 cycle abort. Output[vDBuf0,vBuf0,vCnt], Skip[ALU>=0]; Output[vDBuf1,vBuf0,vCnt], GoTo[vNL16]; Output[vDBuf1,vBuf0,vZero]; *Now vBase is quadaligned; do IOFetch16's so long as vBase[10:17b] .le. 360b *(HW manual indicates a hex-aligned restriction, but it is wrong) and there *are 20b or more words left to transfer. vNLQ3: LU ← (LdF[vBase,10,7]) - T - 1, GoTo[vNL16,ALU<0], At[dSendMem,0]; vNLQ4: vCnt ← (vCnt) - (4C), GoTo[vNLQ2,Carry']; IOFetch4[vBase,vfBuf0,0], Skip[ALU>=0]; vBase ← (vBase) + (4C), GoTo[vNL16]; vBase ← (vBase) + (4C); LU ← (LdF[vBase,10,7]) - T - 1, GoTo[vNLQ4]; vNLQ2: IOFetch16[vBase,vfBuf0,0]; vBase ← (vBase) + (20C); vCnt ← (vCnt) - (14C), GoTo[vNLQ3]; *Usually get here following an IOFetch16, when MC1 is busy for 16d cycles, *so do the cursor check here. Following an IOFetch4, showing the cursor *adds 11 cycles; following IOFetch16, it adds 11 or 14 cycles. vNL16: vCursorY ← (vCursorY) - (2C), GoTo[vNL16b,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[vNL16c,vNL16d,R<0]; vNL16b: vSLC ← (vSLC) - 1, IOStrobe, GoTo[vNL16d,R>=0]; *Last scanline of DCB; change TPC to initiate DCB setup. vNL16c: T ← LdF[vSize,11,7], Call[vNL16e]; vCurrentY ← (vCurrentY) - 1, GoTo[vDoDCB]; vNL16d: T ← LdF[vSize,11,7]; vNL16e: vCnt ← T, Dispatch[vCnt,14,3], NoRegILockOK; T ← RHMask[vNWrds], Disp[.+1]; *In the common case, 17 cycles intervene between the preceding IOFetch16 *and the first IOFetch4 in this dispatch table, and MC1 has been busy for *16 of the 17 cycles. IOFetch4[vBase,vfBuf0,0], At[dNL16,7]; vBase ← (vBase) + (4C); IOFetch4[vBase,vfBuf0,0], At[dNL16,5]; vBase ← (vBase) + (4C); IOFetch4[vBase,vfBuf0,0], At[dNL16,3]; vBase ← (vBase) + (4C); PFetch2[vBase,vDBuf0,0], At[dNL16,1]; vBase ← (vBase) + (2C); **NOTE: vCnt as the base register below prevents a 6 cycle abort. **Careful to ensure 5 mi after final Output to avoid Gotcha Output[vDBuf0,vBuf0,vCnt]; Dispatch[vSize,6,2]; Output[vDBuf1,vBuf0,vCnt], Disp[vFin0s]; IOFetch4[vBase,vfBuf0,0], At[dNL16,6]; vBase ← (vBase) + (4C); IOFetch4[vBase,vfBuf0,0], At[dNL16,4]; vBase ← (vBase) + (4C); IOFetch4[vBase,vfBuf0,0], At[dNL16,2]; vBase ← (vBase) + (4C); *Dispatch on 2 low bits of final-zero count and the FinalFillIsZero bit Dispatch[vSize,6,2], Skip[R>=0], At[dNL16,0]; vBase ← (vBase) + T, KBCheck; *No final fill Disp[vFin0s]; *Dispatch Table for final zero memory references. vFin0s: Output[vZero,vBuf0], At[FZTab0,3]; *Careful to ensure 5 mi after Output, Output to the Return, avoiding *Output-Output-PStore4 problem Output[vZero,vBuf0], At[FZTab0,2]; Output[vZero,vBuf0], At[FZTab0,1]; Dispatch[vSize,2,4], At[FZTab0,0]; vBase ← (vBase) + T, Disp[.+1]; *Entries 12 to 17 in this table are not needed since max. count is 36d IFG[WordsPerLine!,100,ER[WordsPerLine.Too.Large]]; :IF[IFG[WordsPerLine!,46,1,0]]; *********** IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,17]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,16]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,15]; IOFetch16[vMDS177400,vfBuf0,0], At[FZTab4,14], GoTo[vFZ32]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,13]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,12]; :ENDIF; *********************************** IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,11]; vFZ32: IOFetch16[vMDS177400,vfBuf0,0], At[FZTab4,10], GoTo[vFZ16]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,7]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,6]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,5]; vFZ16: IOFetch16[vMDS177400,vfBuf0,0], At[FZTab4,4], KBCheck; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,3]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,2]; IOFetch4[vMDS177400,vfBuf0,0], At[FZTab4,1], KBCheck; vChkMsg: LU ← vMsgStatus, DblGoTo[vM1,vM0,IOAtten'], At[FZTab4,0]; :IF[LFKeyBoard]; ****************************** %LF KEYBOARD SUBROUTINE vChkMsg: vMsgStatus holds keyboard process state information. The count field is initialized to minus number of bits in the field and incremented as each bit comes in. When count reaches zero, the carry-out of the count increments the state. The field sizes and their associated state are: state size 0 idle 1 4 x mouse delta, twos complement 2 4 y mouse delta 3 3 mouse buttons (and status are lumped together in single field) 4 status (video, VS, power supply normal, key data follows) 4 10 key data (7 bits of key, 1 bit of up/down) 5 post key data 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. Note that this uses 7 bit fields. Timing = 4 cycles when idle, 6 cycles for the 1st bit in a message, 12 or 14 cycles for non-transition bits of the message, 15 or 17 cycles for key data, 19 to 24 cycles for mouse X or Y, and 17 to 20 cycles for buttons and status. % MC[keyStart,75]; *initial state, count=-4+1, state=1 vM1: vMsg ← RSh[vMsg,1], GoTo[vMsgContinue,ALU#0]; *Zeroes here vNoMsg: T ← 170C, Return; vM0: vMsg ← RSh[vMsg,1], Skip[ALU#0]; *Ones here vMsgStatus ← keyStart, GoTo[vNoMsg]; vMsg ← (vMsg) + (100000C); vMsgContinue: LU ← LdF[vMsgStatus,13,5]; Dispatch[vMsgStatus,10,3], Skip[ALU=0]; *Dispatch on state bits vMsgStatus ← (vMsgStatus) + 1, GoTo[vNoMsg]; T ← RSh[vMsg,10], Disp[.+1]; vMsgStatus ← (vMsgStatus) or (35C), GoTo[keyX], DispTable[4,17,2]; *X vMsgStatus ← (vMsgStatus) or (32C), GoTo[keyY]; *Y T ← RSh[vMsg,7], DblGoTo[keyBS,keyBSI,R<0]; *Buttons/status vKeyBuffer ← (LSh[vKeyBuffer,10]) or T; *key data vMsgStatus ← 0C, GoTo[vNoMsg]; keyX: vMsg ← RSh[vMsg,14], Skip[R>=0]; vMsg ← (vMsg) or (170C); *neg., extend sign to seven bits T ← LSh[vMsg,11], GoTo[keyX1]; keyY: T ← vMsg ← RSh[vMsg,14], Skip[R>=0]; T ← vMsg ← (vMsg) or (170C); *neg., extend sign to seven bits vMouseXY ← (vMouseXY) and not (400C); keyX1: vMouseXY ← (vMouseXY) + T, GoTo[vNoMsg]; keyBS: vMsgStatus ← (vMsgStatus) or (31C), Skip; keyBSI: vMsgStatus ← 0C; *Message ends here vButtons ← T, GoTo[vNoMsg]; *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,150,105]; * D1, T10(\), T9, T8. Byte[075,171,065,163]; * T7, R6(BW), T6, L12(FL4). Byte[172,160,055,045]; * L9(FL3), L6(LF), T5, T4. Byte[035,012,025,177]; * T3, T2, T1(esc), . *10: Byte[164,177,173,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,161,155]; * R7, R10, R11, R8. Byte[014,153,113,042]; * R9(FR4), R12(swat), A7(space), L11(CTL). Byte[114,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,015,177,177]; * 1, 48, , . OnPage[DisplayPage]; vPostKey: vTemp ← HiA[KeyTable], GoTo[vPKey2,ALU#0]; *if no data, Return right away vKeyBuffer ← LSh[vKeyBuffer,10]; *shift to other KB char vTemp ← (vTemp) or (LoA[KeyTable]), GoTo[vPKey3,ALU#0]; vPKey1: vLink ← lfBlankLength, GoTo[vVB1]; vPKey2: vTemp ← (vTemp) or (LoA[KeyTable]); vPKey3: T ← LdF[vKeyBuffer,1,5]; *Get word number (4 bytes per word) vTemp ← (vTemp) + T; *Form final address T ← LdF[vKeyBuffer,6,1]; *Set h2 to high/low word APCTask&APC ← vTemp; *Address to read in Control Store ReadCS; *Get the word T ← CSData, DispTable[1,1,0]; *Even loc after ReadCS vLink ← T, LoadPage[lfKBPage]; LU ← LdF[vKeyBuffer,7,1]; *low or high byte OnPage[lfKBPage]; T ← (vZero) - (344C), Skip[ALU#0]; *T ← 177034 - 177400 vLink ← RSh[vLink,10]; *Need upper byte T ← (LdF[vLink,15,3]) + T; *Get word number PFetch1[vMDS177400,vDBA]; *vDBA is a temp--fetch Alto kbd word *T ← vTemp ← 100000b rshift vLink[11b:14b]. Dispatch[vLink,13,2]; vLink ← T, Dispatch[vLink,11,2], NoRegILockOK, Disp[.+1]; T ← vTemp ← 100000C, Disp[vPK1], DispTable[4]; T ← vTemp ← 40000C, Disp[vPK1]; T ← vTemp ← 20000C, Disp[vPK1]; T ← vTemp ← 10000C, Disp[vPK1]; *Test for key down (0) or up (1). vPK1: vKeyBuffer ← RHMask[vKeyBuffer], DblGoTo[vPKNZ,vPKZ,R<0], DispTable[4]; T ← vTemp ← RSh[vTemp,4], GoTo[vPK1]; T ← vTemp ← RSh[vTemp,10], GoTo[vPK1]; T ← vTemp ← RSh[vTemp,14], GoTo[vPK1]; vPKZ: vDBA ← (vDBA) and not T, Skip; *key down, clear bit vPKNZ: vDBA ← (vDBA) or T; *key up, set bit T ← vLink, LoadPage[DisplayPage]; PStore1[vMDS177400,vDBA], GoToP[vPKey1]; *store word OnPage[DisplayPage]; :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; 32 on KB0 to KB3 end-of-msg; 29 on KSMS; or 43 on Mouse XY end. % vM0: vMsg ← LSh[vMsg,1], DblGoTo[vMsgA,vNoMsg,R<0]; vM1: vMsg ← (LSh[vMsg,1]) + 1, DblGoTo[vMsgA,vNoMsg,R<0]; vNoMsg: T ← 170C, 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]; *Dispatch on type bits, setup for next message. vMsgStatus ← Dispatch[vMsgStatus,4,3]; vDBuf0 ← T, Disp[.+1]; *Timing from VM0/VM1 to here = 13 cycles *Reject type 0 (possibly noise) vMsg ← 0C, GoTo[vNoMsg], DispTable[10]; T ← (vZero) - (344C), GoTo[vKBn]; *KB0 (VM 177034) T ← (vZero) - (343C), GoTo[vKBn]; *KB1 (VM 177035) T ← (vZero) - (342C), GoTo[vKBn]; *KB2 (VM 177036) T ← (vZero) - (341C), GoTo[vKBn]; *KB3 (VM 177037) vTemp ← T, GoTo[vKSMS]; *KSMS (keyset/mouse switches) PFetch4[vMDS420,vDBuf0,4], GoTo[vMXY]; *MouseX,Y from 424b,425b *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 T ← (vZero) - (344C), Skip[ALU=0]; vEOMs: vMsg ← 0C, GoTo[vNoMsg]; PFetch1[vMDS177400,vMsg]; *Fetch KB0 as parameter for Boot vTemp ← And[BootSV,377]C; vTemp ← (vTemp) or (Or[LShift[BootTask,14],And[BootSV,7400]]C); APCTask&APC ← vTemp; LU ← MNBR ← vMsg, Return; *Continue in BootTask at SuckADuck *Stores KSMS in 177030 and 177033, junk in other 2 regs. vKSMS: T ← (vZero) - (350C); PStore4[vMDS177400,vDBuf0], GoTo[vEOMs]; vKBn: PStore1[vMDS177400,vDBuf0], GoTo[vEOMs]; vMXY: T ← 200C; T ← (RHMask[vMsg]) - T; *delta Y (sent as excess 128d) vMsg ← RSh[vMsg,10]; vDBuf1 ← (vDBuf1) + T; T ← (vMsg) - (200C); *delta X (sent as excess 128d) vDBuf0 ← (vDBuf0) + T; vMsg ← 0C; PStore4[vMDS420,vDBuf0,4], GoTo[vNoMsg]; *Store updated X,Y 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; *************************************** :END[Display];