{File name Display.mc Author: AEF AEF (with a lot stolen from Jim JXF 's previous version) Created: April 27, 1982 12:05 PM Last Edited: Dennis DEG , 1-Sep-84 19:33:16: Add copyright notice. Last Edited: AEF AEF , 13-Sep-83 16:32:38: Move IOPage for larger VM Last Edited: AEF AEF , 23-Nov-82 14:18:05: Fix bug when cursor is at [0,0] (at StartField) Last Edited: AEF AEF , September 7, 1982 2:09 PM: Change CANCELBR[$,0] to CANCELBR[$,2] for cursor buffer write Last Edited: AEF AEF , August 4, 1982 1:55 PM: Reduce by one instruction Last Edited: AEF AEF , June 30, 1982 11:33 AM: } { Copyright (C) 1982, 1983 by Xerox Corporation. All rights reserved.} SetTask[1]; StartAddress[DisplayStart]; {CONSTANT DEFINITIONS} {Values for Display Control Register} Set[StartVSync, 23]; Set[EndVSync, 3]; {Returns from Sleep} Set[EndVSync1, 4]; Set[EndVSync2, 0D]; Set[TopBorder1, 3]; Set[Hibernate, 7]; Set[TopBorder2, 8]; Set[TopBorder4, 0A]; {must be 0A} Set[TopBorder3, 0]; Set[TopBorder5, 0B]; Set[StartField, 9]; Set[NextFieldLine, 0C]; {Returns from FetchParameters} Set[ReturnFromScroll, 0D]; Set[FetchParams, 0F]; {Returns from Cycle} Set[Cycle6, 0A]; {must be same as TopBorder4} Set[Cycle, 2]; {Snore times} Set[VSyncTime, 16'd]; {10 hex - odd frame is 17'd} Set[BottomBorderTime, 12'd]; {0C hex} Set[CopyCursorTime, 8]; {number of lines needed to copy the cursor} Set[FetchParamsTime, 2]; {Return Macros} MacroDef[HibernateRet, at[#1, 10, HibernateRet]]; MacroDef[SleepRet, at[#1, 10, SleepRet]]; MacroDef[CycleRet, at[#1, 10, CycleRet]]; MacroDef[FetchParamsRet, at[#1, 10, FetchParamsRet]]; {-------------------------------------------------------------------------------------------- Start Vertical Sync ---------------------------------------------------------------------------------------------} {We assume upon entry that the following registers have been initialized: uIOPage = start of IOPage uInitCurMapAddr = start of cursor map in CSB uInitBorder = 8822 uInitCurBufAddr = start of cursor buffer uPatchCount = 0 uPatchLine = FFFF hex u3F0 = 3F0 hex u3FF = 3FF hex uInitPicLength = number of lines in the picture area of display uClockBits = 0/1 uNormalFifo = FC00 hex dY = 1} {LINE 1 OF START VERTICAL SYNC (4 clicks)} HibernateRet[BottomBorderTime] DisplayStart: {Set the low order bit of uClockBits to 0 or 1 depending on odd or even field.} dZ ← uClockBits, Refresh, CANCELBR[$, 0F], c1; uClockBits ← dZ ← dZ xor dY{uClockBits xor 1}, c2; uOddField ← dY ← dY and dZ, c3; dZ ← VSyncTime + dY {OddField}, rhdZ ← VSyncTime, c1; dY ← dY{OddField} LRot4 {Prepare for DCtl register}, c2; {Set DCtl to start vertical sync. The odd/even bit in DCtl is bit 11. Go to Sleep for 17 lines if even field, 18 lines if odd field.} dY ← dY or StartVSync, L5 ← Hibernate, c3; DCtl ← dY LRot0, CALL[SnoreC2], c1; {LINES 2-17/18 OF START VERTICAL SYNC (2 clicks each)} {At Sleep} {------------------------------------------------------------------------------------------- End Vertical Sync --------------------------------------------------------------------------------------------} {LINE 1 OF END VERTICAL SYNC (2 clicks)} HibernateRet[And[VSyncTime, 0F]] {Set DCtl to end vertical sync. Set up the address for reading the CSB.} rhdZ ← IOPageHigh, Refresh, c1; dZ ← uIOPage, c2; dZ ← dZ + DisplayCSBOffset, L5 ← EndVSync1, c3; DCtl ← EndVSync, CALL[GoToGoToSleep], c1; {LINE 2 OF END VERTICAL SYNC - 11 CLICKS} SleepRet[EndVSync1] {Load the parameters from the display CSB (memory addresses 140E7-140EF) into registers: U B8 - loaded with junk the first time through U B9 - uCurrentIOCBLow U BA - uCurrentIOCBHi U BB - uFieldCmd U BC - uWakeupMask U BD - uSyncCmd U BE - uCursorX U BF - uCursorY U B0 - uBorder dY - uCursorMask} FetchCSBparameters: MAR ← [rhdZ, dZ + 0], L5 ← EndVSync2, c1; CSBLoop:dZ ← dZ + 1, AltUaddr, NibCarryBr, c2; DisplayParmBlock ← dY, dY ← MD, BRANCH[FetchCSBparameters, $], c3; uCursorMask ← dY, Refresh, GOTO[GoToGoToSleep], c1; {------------------------------------------------------------------------------------------- Top Border --------------------------------------------------------------------------------------------} {LINE 1 OF TOP BORDER - 2/4 CLICKS} {If the display is not initialized, then set the border to the standard dotted border. Initialize the scrolling wakeup mask and uPatchLine and go to Sleep for 2 lines.} SleepRet[EndVSync2] Xbus ← uFieldCmd, XDisp, L5 ← TopBorder1, c1; DCtl ← uSyncCmd, BRANCH[DisplayNotInitialized, DisplayInitialized, 0B], c2; DisplayNotInitialized: uScrollWakeupMask ← 0, c3; uPatchLine ← dZ xor ~dZ, Refresh, c1; DBorder ← uInitBorder, c2; dZ ← rhdZ ← FetchParamsTime, CALL[Snore], c3; {If the display is initialized, then set the border to the border parameter in the CSB. We go to Sleep and wake up at FetchIOCB to load the IOCB into U registers.} DisplayInitialized: DBorder ← uBorder, CALL[Sleep], c3; {LINE 2 OF TOP BORDER - 2/4 CLICKS} {If the display is not initialized, we are still at Snore for this line. If the display is initialized, we wake up from Sleep at Fetch Parameters to load the IOCB parameters into U registers. When we return here, uCopyCount has been reset to 7 and we have: U 93 - uScrollWakeupMask U 94 - uPatchFlags U 95 - uPatchCount U 96 - uFifoA U 97 - uFifoB U 98 - uPatchSize U 99 - uPatchLine } FetchParamsRet[FetchParams] dY ← uClockBits, L5 ← TopBorder2, Refresh, CALL[Sleepc2], c1; {LINE 3 OF TOP BORDER - 3 CLICKS} {Set the low order bit of the picture length depending on odd or even field.} HibernateRet[FetchParamsTime] dY ← uOddField, Refresh, GOTO[SetLength], c1; SleepRet[TopBorder2] dY ← uOddField, Refresh, c1; SetLength: dZ ← uInitPicLength, c2; uPicLength ← dY or dZ, c3; {Set the low order bit of the cursor map address depending on odd or even field.} dY ← uPatchOffset, Refresh, c1; dZ ← uInitCurMapAddr, L5 ← TopBorder3, c2; uCurMapAddr ← dY or dZ, CALL[Sleep], c3; {LINE 4 OF TOP BORDER - 7 CLICKS} {Get the high order bits of the bitmap address where the cursor is located by shifting uPatchLine left by 6 bits.} SleepRet[TopBorder3] dY ← uPatchLine, Refresh, L5 ← TopBorder4, CALL[Cycle6], c1; {The 4 low order bits in uCursorX specify the bit offset from a word boundary of the cursor. Get the low order bits of the bitmap address where the cursor is located by eliminating these 4 bits of uCursorX.} CycleRet[Cycle6] dZ ← uCursorX, c2; dZ ← dZ and u3F0, c3; dZ ← dZ LRot12, c1; uCurVisAddr ← dZ or dY, c2; {If this patch is on an odd line, increment the cursor buffer address by 40 hex words (one display scan line).} dY ← uInitCurBufAddr, c3; Xbus ← uPatchOffset, XDisp, c1; rhdY ← uCursorX, BRANCH[EvenPatch, OddPatch, 0E], c2; EvenPatch: GOTO[AnyPatch], c3; OddPatch: dY ← dY + 40, GOTO[AnyPatch], c3; {If smooth scrolling is not on, then test to see if the display is off. If it is off, we must not reference display memory, so we go to Sleep for 8 lines.} AnyPatch: Xbus ← uPatchFlags, XDisp, c1; uCurBufAddr ← dZ or dY, BRANCH[ScrollingOff, NoCursor, 0E], c2; ScrollingOff: Xbus ← uFieldCmd, XDisp, c3; rhdZ ← IOPageHigh, BRANCH[DisplayOff, CopyCursor, 0B], c1; {If smooth scrolling is on, then go to Sleep for 8 lines.} NoCursor: rhdZ ← dZ ← CopyCursorTime, CALL[Snore], c3; DisplayOff: GOTO[NoCursor], c2; {The display is on. Go to Sleep for one line.} CopyCursor: dZ ← uCurMapAddr, CALL[GoToSleep], c2; {LINES 5-12 OF TOP BORDER - 12/2 CLICKS} {We arrive here when the display is on and smooth scrolling is off. This is where we get the cursor onto the screen. We go through this loop 8 times for each field, to make a total of 16 lines for the cursor. As the cursor may cross word boundaries, two words of the visible region are involved and two words must be stored into the cursor buffer. First we read one word of the cursor out of the cursor map and shift it according to the bit offset in uCursorX (rhdY).} SleepRet[TopBorder4] MAR ← [rhdZ, dZ], dZ ← dZ + 2, L5 ← Cycle, c1; Xbus ← rhdY, XDisp, CANCELBR[$, 2], c2; uCurMapAddr ← dZ, dY ← MD, DISP4[Cycle], c3; CycleRet[Cycle] rhdZ ← 0 {display bank}, c1; uCursorShifted ← dY, c2; {Read the first word out of the visible region where the cursor is located, mask out the section we aren't interested in, and OR it with the shifted cursor pattern obtained above. The result is stored into the cursor buffer where, at the cursor position, the hardware will display it instead of the bitmap.} dZ ← uCurVisAddr, c3; MAR ← [rhdZ, dZ + 0], c1; dY ← dY and uCursorMask, c2; dY ← MD or dY, c3; dZ ← uCurBufAddr, Refresh, c1; Noop, c2; Noop, c3; MAR ← [rhdZ, dZ + 0], c1; MDR ← dY, dZ ← uCurVisAddr, c2; dY ← uCursorShifted, c3; {Now do the same thing for the second word of the cursor. Each time through this loop, we must increment the address of the cursor buffer and of the visible region by 80 hex, the number of words in 2 lines. If uCurBufAddr ends in FF hex, this write will be cancelled.} MAR ← [rhdZ, dZ + 1], c1; dY ← dY and ~uCursorMask, CANCELBR[$, 2], c2; dY ← MD or dY, c3; dZ ← dZ + 80, c1; uCurVisAddr ← dZ, c2; dZ ← uCurBufAddr, L5 ← TopBorder4, c3; MAR ← [rhdZ, dZ + 1], c1; MDR ← dY, LOOPHOLE[wok], CANCELBR[$, 2], c2; dY ← dZ + 80, c3; {uCopyCount starts out at 7 and gets decremented. If it is not negative now, branch to the beginning of the copy loop.} dZ ← uCopyCount - 1, LOOPHOLE[niblTiming], c1; uCopyCount ← dZ, YDisp, c2; uCurBufAddr ← dY, BRANCH[$, CopyDone, 7], c3; rhdZ ← IOPageHigh, GOTO[CopyCursor], c1; {We are finished copying the cursor} CopyDone: L5 ← TopBorder5, CALL[GoToGoToSleep], c1; {LINE 13 OF TOP BORDER - 12/6 CLICKS} {Now we must take care of housekeeping. Send a Mesa interrupt and update the clocks. The three clock registers must all be loaded in the same click.} HibernateRet[CopyCursorTime] dY ← uWP, GOTO[DoWakeup], c1; SleepRet[TopBorder5] dY ← uWP, c1; DoWakeup: dY ← dY or uWakeupMask, MesaIntRq, c2; uWP ← dY, c3; Xbus ← uOddField, XDisp, c1; dY ← uClockLow, BRANCH[Even, Odd, 0E], L5 ← 0E, c2; Even: dZ ← RShift1 uClockBits, GOTO[IncClock], c3; Odd: dZ ← RShift1 uClockBits, c3; IncClock: dY ← dY + dZ, CarryBr, c1; dZ ← uClockHigh, BRANCH[$, UpClockHi], c2; L5Disp, GOTO[SetClock], c3; UpClockHi: dZ ← dZ + 1, L5Disp, c3; SetClock: uClockLow ← dY, dY ← 0, BRANCH[SetEven, SetOdd, 0E], c1; SetEven: uClockHigh ← dZ, L5 ← StartField, GOTO[ClockBits], c2; SetOdd: uClockHigh ← dZ, dY ← dY + 1, L5 ← StartField, c2; ClockBits: uClockBits ← dZ ← dY, c3; {------------------------------------------------------------------------------------------- Start Field --------------------------------------------------------------------------------------------} {LINES 1-404 OF PICTURE FIELD (2-14 clicks)} {This entire Start Field section is a giant loop which we run through 404 times for the odd or even lines in the main picture. We load the DCtlFifo according to whether there is a patch on the line or not and where the patch is located. The Fifos are loaded one line previous to the line we are displaying, thus we don't set DCtl to start the field until the second time around. The only time that the code uses all 14 clicks is during smooth scrolling.} {Set DCtl to start the visible field.} NextFieldLine: dY ← ~u3FF, Refresh, BRANCH[TestForPatch, ExitField], c1; StartField: DCtl ← uFieldCmd, CANCELBR[$, 0F], c1; L5 ← NextFieldLine, c2; [] ← dZ xor uPicLength, ZeroBr, GOTO[NextFieldLine], c3; {If this line does not have a patch on it (no cursor or smooth scrolling), then we insert 111111 | line number 0 5 6 15 into the DCtlFifo.} TestForPatch: [] ← dZ xor uPatchLine, ZeroBr, c2; uClockBits ← dZ, BRANCH[NoPatch, Patch], c3; NoPatch: DCtlFifo ← dY or dZ, Refresh, c1; EndOfLine: dZ ← dZ + 2, L5Disp, ClrDPRq, c2; [] ← dZ xor uPicLength, ZeroBr, BRANCH[NextFieldLine, StartField, 0E], c3; {If there is a patch on this line, then determine where the patch is located. FifoA and FifoB are provided by the head. The 11 value of XHDisp is used for smooth scrolling when the top line is at the top of the window. XHDisp Location DCtlFifo ← 0 Middle FifoA,,line number FifoB + patch offset(0/1) 111111,,line number 1 Left edge FifoA + patch offset (0/1) 111111,,line number 2 Right edge FifoA,,line number FifoB + patch offset (0/1) 3 Scrolling 111111,,line number } Patch: Xbus ← uPatchFlags, XHDisp, c1; dY ← uFifoB, DISP2[SendFifos], c2; DCtlFifo ← dZ or uFifoA, GOTO[NextFifo], c3, at[0, 4, SendFifos]; DCtlFifo ← dZ or uFifoA, GOTO[NextFifo2], c3, at[2, 4, SendFifos]; dY ← uFifoA, GOTO[NextFifo], c3, at[1, 4, SendFifos]; dY ← uClockBits, GOTO[NoMorePatch], c3, at[3, 4, SendFifos]; NextFifo: dZ ← uPatchOffset, Refresh, c1; DCtlFifo ← dZ + dY, c2; dY ← uClockBits, GOTO[NormalFifo], c3; NextFifo2: dZ ← uPatchOffset, Refresh, c1; DCtlFifo ← dZ + dY, c2; dY ← uClockBits, GOTO[SendDone], c3; NormalFifo: DCtlFifo ← dY or uNormalFifo, GOTO[TestForEnd], c1; {The Fifos have been loaded. If this line is not the last line of the patch, then increment the patch line and the patch offset by 2.} SendDone: Noop, c1; TestForEnd: [] ← uLastPatchLine xor dY, ZeroBr, c2; dZ ← uPatchCount, ZeroBr, BRANCH[ContinuePatch, EndPatch], c3; ContinuePatch: dZ ← dY + 2, CANCELBR[$, 1], c1; uPatchLine ← dZ, c2; dZ ← uPatchOffset, c3; dZ ← dZ + 2, c1; uPatchOffset ← dZ, GOTO[GoToFinish], c2; {If we have finished the last line of this patch, then see if there are more patches. If so, we must go load the next patch parameters: U 96 - uFifoA U 97 - uFifoB U 98 - uPatchSize U 99 - uPatchLine This will only occur when smooth scrolling.} EndPatch: dY ← uPatchPtr, BRANCH[FetchScrollParams, LastPatch], c1; NoMorePatch: DCtlFifo ← dY or uNormalFifo, c1; {If this is the last patch, then return to the beginning for the next line. If we are smooth scrolling, do a Mesa interrupt.} LastPatch: Xbus ← uPatchFlags, XDisp, CANCELBR[$, 0F], c2; GoToFinish: Noop, BRANCH[FinishLine, Scroll, 0E], c3; Scroll: dZ ← uWP, c1; dZ ← dZ or uScrollWakeupMask, MesaIntRq, c2; uWP ← dZ, c3; FetchParamsRet[ReturnFromScroll] dZ ← uClockBits, Refresh, GOTO[EndOfLine], c1; FinishLine: dZ ← uClockBits, Refresh, GOTO[EndOfLine], c1; {We have finished the 404 lines of the visible field.} ExitField: dZ ← dZ + 2, ClrDPRq, c2; uClockBits ← dZ, c3; {------------------------------------------------------------------------------------------- Bottom Border --------------------------------------------------------------------------------------------} {LINES 1-12 OF BOTTOM BORDER (2 clicks)} {Set DCtl to start the bottom border. Go to sleep for 12 lines and wake up at DisplayStart.} BotBorder: dZ ← rhdZ ← BottomBorderTime, Refresh, c1; dY ← uSyncCmd, CALL[Hibernate], c2; {-------------------------------------------------------------------------------------------- Fetch IOCB Parameters --------------------------------------------------------------------------------------------} {Load the IOCB parameters into U registers: U 93 - uScrollWakeupMask U 94 - uPatchFlags U 95 - uPatchCount U 96 - uFifoA U 97 - uFifoB U 98 - uPatchSize U 99 - uPatchLine The IOCB starts on a 16-word boundary + 1 (low 4 bits = 0001), so by adding 1 to the address, we begin loading at register U 92. This is a good chance to reset uCopyCount to 7 the first time through the loop.} SleepRet[TopBorder1] FetchParameters: rhdY ← uCurrentIOCBHi, c1; dZ ← 7, {reset uCopyCount} c2; dY ← uCurrentIOCBLow, c3; IOCBLoop: MAR ← [rhdY, dY + 0], c1; dY ← dY + 1, AltUaddr, YDisp, c2; DisplayIOCBblock ← dZ, dZ ← MD, BRANCH[IOCBLoop, SavePointer, 7], c3; {We come here only if we are smooth scrolling. We reload the four scrolling parameters: U 96 - uFifoA U 97 - uFifoB U 98 - uPatchSize U 99 - uPatchLine} FetchScrollParams: dZ ← dZ - 1, c2; rhdY ← uCurrentIOCBHi, c3; MAR ← [rhdY, dY], dY ← dY + 1, c1; Noop, CANCELBR[$, 2], c2; uPatchCount ← dZ, dZ ← MD, c3; MAR ← [rhdY, dY], dY ← dY + 1, c1; Noop, CANCELBR[$, 2], c2; uFifoA ← dZ, dZ ← MD, c3; MAR ← [rhdY, dY], dY ← dY + 1, c1; Noop, CANCELBR[$, 2], c2; uFifoB ← dZ, dZ ← MD, c3; MAR ← [rhdY, dY], dY ← dY + 1, c1; Noop, CANCELBR[$, 2], c2; uPatchSize ← dZ, dZ ← MD, c3; {Save the pointer to the next patch parameters.} SavePointer: uPatchPtr ← dY, Refresh, c1; {Set the patch offset according to odd or even field. Adjust the patch line accordingly.} dY ← dZ xor uOddField, {PatchLine xor uOddField} c2; dY ← dY and 1, c3; uPatchOffset ← dY, c1; dY ← dZ + dY, {PatchLine + PatchOffset} c2; uPatchLine ← dY, c3; {Calculate the last line of the patch and return.} dY ← uPatchSize, c1; dZ ← dZ + dY, c2; dY ← dZ xor uOddField, c3; dY ← dY and 1, c1; dZ ← dZ - dY, L5Disp, c2; uLastPatchLine ← dZ, RET[FetchParamsRet, 0D], c3; {-------------------------------------------------------------------------------------------- Sleep Routines --------------------------------------------------------------------------------------------} {We come here at the end of a line to update the clock bits and clear wakeups until the next line. For times when we do not need to do anything useful for several lines in a row (bottom border), Snore provides a count to keep track of the number of times we have called Sleep.} Hibernate: DCtl ← dY LRot0, c3; Snore: SleepRet[Hibernate] dZ ← dZ - 1, NegBr, Refresh, L5 ← Hibernate, c1; SnoreC2:Xbus ← rhdZ, XDisp, BRANCH[Winter, Spring], c2; Winter: Noop, CANCELBR[Sleep, 0F], c3; Spring: rhdY ← dY ← 1 {see StartVSync}, RET[HibernateRet], c3; GoToGoToSleep: Noop, c2; GoToSleep: Noop, c3; Sleep: dY ← uClockBits, Refresh, c1; Sleepc2: dY ← dY + 2, pRet5, c2; uClockBits ← dY, ClrDPRq, RET[SleepRet], c3; {------------------------------------------------------------------------------------------ Cycle -------------------------------------------------------------------------------------------} {Takes parameter in dY. Rotates dY left by 0-15 bits.} Cycle: dY ← dY, Xbus ← 0, XDisp, GOTO[NoRot], c*, at[0,10,Cycle]; dY ← LRot1 dY, Xbus ← 0, XDisp, GOTO[NoRot], c*, at[0F,10,Cycle]; dY ← LRot1 dY, Xbus ← 0, XDisp, GOTO[DRot1], c*, at[0E,10,Cycle]; dY ← RRot1 dY, Xbus ← 1, XDisp, GOTO[NoRot], c*, at[0D,10,Cycle]; dY ← dY, Xbus ← 1, XDisp, GOTO[NoRot], c*, at[0C,10,Cycle]; dY ← LRot1 dY, Xbus ← 1, XDisp, GOTO[NoRot], c*, at[0B,10,Cycle]; Cycle6: dY ← LRot1 dY, Xbus ← 1, XDisp, GOTO[DRot1], c*, at[0A,10,Cycle]; dY ← RRot1 dY, Xbus ← 2, XDisp, GOTO[NoRot], c*, at[9,10,Cycle]; dY ← dY, Xbus ← 2, XDisp, GOTO[NoRot], c*, at[8,10,Cycle]; dY ← LRot1 dY, Xbus ← 2, XDisp, GOTO[NoRot], c*, at[7,10,Cycle]; dY ← LRot1 dY, Xbus ← 2, XDisp, GOTO[DRot1], c*, at[6,10,Cycle]; dY ← RRot1 dY, Xbus ← 3, XDisp, GOTO[NoRot], c*, at[5,10,Cycle]; dY ← dY, Xbus ← 3, XDisp, GOTO[NoRot], c*, at[4,10,Cycle]; dY ← LRot1 dY, Xbus ← 3, XDisp, GOTO[NoRot], c*, at[3,10,Cycle]; dY ← LRot1 dY, Xbus ← 3, XDisp, GOTO[DRot1], c*, at[2,10,Cycle]; dY ← RRot1 dY, Xbus ← 0, XDisp, GOTO[NoRot], c*, at[1,10,Cycle]; NoRot: pRet5, DISP2[DLRot], c*; DRot1: dY ← LRot1 dY, pRet5, DISP2[DLRot], c*; DLRot: RET[CycleRet], c*, at[0,4,DLRot]; dY ← dY LRot4, RET[CycleRet], c*, at[1,4,DLRot]; dY ← dY LRot8, RET[CycleRet], c*, at[2,4,DLRot]; dY ← dY LRot12, RET[CycleRet], c*, at[3,4,DLRot];