*----------------------------------------------------------- Title[LispDisplayMain.mc.....July 16, 1985 2:28 PM...Masinter]; * Main part of Dorado display microcode -- included with emulators, * but not included with Bootstrap/Initial microcode. *----------------------------------------------------------- *----------------------------------------------------------- * Terminal Word Task (TWT) and Display Word Task (DWT) microcode. * TWT runs as either DWT or AWT, depending on hardware configuration. * If TWT runs as AWT, then DWT may run independently on behalf of * a non-Alto display, using the same word task microcode. *----------------------------------------------------------- % This microcode is structured as if it used subtasks, because it may be shared by non-Alto terminal microcode using DWT. However, when running in Alto terminal emulation mode as AWT or DWT, it NEVER expects to get a B channel command or to run as the B subtask. Note that this microcode can run on behalf of the DispM terminal controller or of either A or B channel of the DispY controller. DispY and DispM use independent but parallel RBase, MemBase, and TIOA values, and the A and B channels are differentiated by SubTask. Thus, for example, references to ACount (below) apply equally well to BCount or TCount in the appropriate circumstances. This code uses subtasks, and must ALWAYS Branch to the same place when blocking, must never test Branch conditions across blocks, and must use task-specific RBase, RStk, and MemBase values correctly. Note that the controller is designed to prohibit wakeups while DWT is initiated (either running or preempted) and for at least n instructions after any BLOCK, where n is approximately 5. DWT microcode wakes up in response to flags set by the DHT microcode. DWT does not need to be specially awakened for initialization. DWT will initialize itself EXCEPT that AAddress, BAddress, ACount, and BCount must have been initialized explicitly by DHT, because you cannot guarantee which subtask will run first, and DWTInit is executed only once. Subtask.0 defines a block of 16 subtask specific registers, of which 5 per subtask are used by DWT: AAddress, ACount, ANextCount, ANextAddrLo, and ANextAddrHi are the subtask-specific registers for Channel A. There is a similar set for channel B. % Set[XTask, IP[DWT]]; *----------------------------------------------------------- TWTInitPC: * Terminal word task initialization: return appropriate task number. *----------------------------------------------------------- Subroutine; KnowRBase[THTRegion]; T_ AWT, DisplayConfig, Branch[.+2, R<0]; T_ DWT; CoReturn; TopLevel; * Terminal word task starts up here as either DWT or AWT. * Select TIOA that addresses the appropriate hardware. * All other initialization and main loop code is the same as DWT. MemBase_ TChannelBR; RBase_ RBase[THTRegion]; RBase_ RBase[TWTRegion], DisplayConfig, Branch[UseDWTFlag, R>=0]; T_ AWTFlag, Branch[SetDWTFlag]; *----------------------------------------------------------- DWTInitPC: * Display word task initialization, for non-standard displays. *----------------------------------------------------------- Subroutine; KnowRBase[TWTRegion]; T_ DWT, CoReturn; TopLevel; * Display word task starts up here iff it is not being used for * Alto terminal emulation. RBase_ RBase[AChannelRegion]; MemBase_ AChannelBR; UseDWTFlag: T_ DWTFlag; *same for both subtasks SetDWTFlag: TIOA_ T; T_ 20C, Branch[DWTCheck]; *same for both subtasks *----------------------------------------------------------- * Display word task main loop -- shared by terminal word task. * ACount >=0 here iff data remains to be output for the current scan line. *----------------------------------------------------------- DWTStart: ACount_ (ACount)-T, Branch[DWTCheck, R<0]; * Fetch the next munch. Output_ 20 signals to the display controller * that a new munch is on its way. AAddress_ (IOFetch_ AAddress)+(Output_ T), Block, Branch[DWTStart]; * AAddress will be even if we just exhausted a scan line. * AAddress will be odd if we have been awakened to start a new scan line. * In either case, isolate flag in AAddress[15] for use in adjusting * the WCB flags (below). DWTCheck: AAddress_ (AAddress) AND (1C), Branch[DWTAdjustWCBFlags, R even]; * Starting new scan line. Copy parameters left by DHT. DWTRefill: BrHi_ ANextAddrHi, Branch[DWTSpuriousWakeup, R<0]; BrLo_ ANextAddrLo; ACount_ ANextCount; * (# Munches to Fetch)-1 in [8..11] * Now adjust WCB flags, as follows: * If we just exhausted a scan line, AAddress = 0 now; execute Output_ 0 * to clear the CurWCB flag, and set AAddress to -1 for the next wakeup. * If we are starting a new scan line, AAddress = 1 now; execute Output_ 1 * to set the CurWCB flag and clear the NextWCB flag, and set AAddress to 0 * for the first IOFetch. DWTAdjustWCBFlags: AAddress_ (AAddress)-1, Output_ AAddress, Block, Branch[DWTStart]; * A bug in the hardware causes occasional spurious wakeups; just ignore them. * Note that we do nothing to dismiss the spurious wakeups -- the ones caused by * the known hardware bug are transient. DWTSpuriousWakeup: ACount_ T-T-1, Block, Branch[DWTStart]; *----------------------------------------------------------- * Alto Terminal Horizontal Task (THT) microcode. * This microcode runs as DHT or AHT and does everything * for the display except IOFetches. *----------------------------------------------------------- *----------------------------------------------------------- DHTInitPC: * Dummy Display Horizontal Task initialization. * The label DHTInitPC is redefined if real DHT microcode is included. *----------------------------------------------------------- Subroutine; T_ DHT, CoReturn; TopLevel; * DHT will be awakened once; just ignore the wakeup. Block, Branch[.]; *----------------------------------------------------------- THTInitPC: * Terminal Horizontal Task initialization: return appropriate task number. * Note: must awaken the appropriate task here, since the task number is * a variable. *----------------------------------------------------------- Subroutine; KnowRBase[THTRegion]; T_ AHT, DisplayConfig, Branch[THTInitAHT, R<0]; T_ DHT; Wakeup[DHT], CoReturn; TopLevel; * DHT starts here T_ Statics, Branch[THTInit1]; * Use DispY TIOA Subroutine; THTInitAHT: Wakeup[AHT], CoReturn; TopLevel; * AHT starts here T_ TStatics, Branch[THTInit1]; * Use DispM TIOA THTInit1: TIOA_ T; :If[AltoMode]; ********** Alto version ********** RBase_ RBase[EmuBRHiReg]; T_ EmuBRHiReg, RBase_ RBase[TWTRegion], Call[SetDisplayBRHi]; MaxWidthWords_ MaxWidthWordsAlto; :Else; ******** PrincOps version ******** T_ A0, RBase_ RBase[TWTRegion], Call[SetDisplayBRHi]; :EndIf; ********************************** T_ AllShutUp, Call[OutputGetsT]; * Disable WakeUps, Reset DDC TCount_ T-T-1, MemBase_ IOBR; TAddress_ T-T-1, TIOA[TNLCB]; * Set initial TWT WCB flag TSetNextWCB_ ANextWCBFlag; * Initialize both channels to maximum left margins, so they never turn on. TChannelReg_ A0, Call[SendMaxMarg]; * Reset various NLCB registers T_ AScan, Call[OutputGetsT]; T_ BScan, Call[OutputGetsT]; * Initialize B because polarity is ORed with A! T_ Modes, Call[OutputGetsT]; * THT initialization (cont'd) * Skip over HRam and MiniMixer initialization if DispM present. ResetDisplayConfig: RBase_ RBase[THTRegion]; PD_ NOT (DisplayConfig); TReg400C_ 400C, Branch[NoInitRams, ALU>=0]; * Initialize HRam and MiniMixer. Call[InitHRam]; Call[JLoadMiniMixer]; Nop; * Placement * Initialize vertical waveform and left margin constants according to * the appropriate hardware configuration and display mode, chosen from: * {Alto, LF (Alto mode), LF (full screen)} X {DispY, DispM}. * These are selected by, respectively, * (DisplayConfig[13:15] = {0, 1, 3}) X (DisplayConfig[0] = {0, 1}) * except that in PrincOps mode, DisplayConfig[13:15] = 1 selects LF (full screen) * and the value 3 does not occur. NoInitRams: T_ LCY[DisplayConfig, DisplayConfig, 1], Branch[.+3, R odd]; * Initialize vertical waveforms for an Alto monitor: VisibleLineConst_ LowByte[TopBorderCntAlto, VisibleLineCnt, BottomBorderCntAlto]; TopBorderConst_ TopBorderCntAlto, Branch[.+3]; * Initialize vertical waveforms for an LF monitor: VisibleLineConst_ LowByte[TopBorderCntLF, VisibleLineCnt, BottomBorderCntLF]; TopBorderConst_ TopBorderCntLF; VisibleLineConst_ (VisibleLineConst)+(400C); VSyncConst_ VSyncCnt; * Now compute the IM address of the margin constants. BootState_ A0, TaskingOff, Call[AfterMarginConsts]; * MargConst[LMarg, CursorX] assembles IM word with the LMarg NLCB constant in the * left half and the CursorX NLCB constant in the right half. M[MargConst, Set[T1@, Or[ALMarg!, And[Sub[0, #1], NLCBDataMask!]]] Set[T2@, Or[CursorX!, And[Sub[0, #2], NLCBDataMask!]]] Data[(Byt0[RShift[T1@, 10]] Byt1[And[T1@, 377]] Byt2[RShift[T2@, 10]] Byt3[And[T2@, 377]])]]; DispTable[IfE[AltoMode, 0, 4, 10]]; * Indexed by DisplayConfig[14:15],,DisplayConfig[0] MargConst[Dec[0,0,3,6], Dec[0,3,2,5]]; * Alto monitor, DispY board MargConst[Dec[0,0,0,0], Dec[0,3,2,0]]; * Alto monitor, DispM board :If[AltoMode]; ********** Alto version ********** * These constants include extra left margin to center an Alto-style picture on * an LF monitor. MargConst[Dec[0,5,3,0], Dec[0,8,1,9]]; * LF monitor, Alto width, DispY board MargConst[Dec[0,4,9,5], Dec[0,8,1,5]]; * LF monitor, Alto width, DispM board Nop; Nop; :EndIf; ********************************** * These constants are for a picture that fills the LF monitor. MargConst[Dec[0,3,2,2], Dec[0,6,1,1]]; * LF monitor, full width, DispY board MargConst[Dec[0,2,6,2], Dec[0,5,8,2]]; * LF monitor, full width, DispM board * THT initialization (cont'd) * T = table index, Link = table base. Read the table entry from IM. * Miscellaneous other initialization is interspersed with this. AfterMarginConsts: TTemp0_ T OR (Link), Call[LinkGetsTTemp0]; TVCWShadowReg_ A0, TIOA[TStatics], ReadIM[0]; T_ Link, Call[LinkGetsTTemp0]; T_ LCY[T, T, 10], ReadIM[1]; T_ T XOR (Link); LMargConst_ T, Call[LinkGetsTTemp0]; TerminalLo_ A0, ReadIM[2]; T_ Link, Call[LinkGetsTTemp0]; T_ LCY[T, T, 10], ReadIM[3]; CursorXConst_ T XOR (Link); TerminalHi_ T_ A0, TaskingOn, Call[OutputGetsT]; * Statics_ 0 to enable wakeups BootTimer_ T_ A0, Block, Branch[THTNewField]; *----------------------------------------------------------- LinkGetsTTemp0: *----------------------------------------------------------- Subroutine; Link_ TTemp0, Return; TopLevel; *----------------------------------------------------------- * Start of new field (defined to be beginning of VSync) *----------------------------------------------------------- KnowRBase[THTRegion]; THTNewField: TFieldAreaReg_ VSyncConst; TIOA[TNLCB], Call[SendMaxMarg]; *Initialize cursor to zero T_ CursorLo, Call[OutputGetsT]; T_ CursorHi, Call[OutputGetsT]; * Complement field type and set VSync=1, VBlank=1 TVCWShadowReg_ (TVCWShadowReg) XOR (Or[VSync!, VBlank!, 1]C); T_ BootTimer, Output_ T; * T for boot check below %*----------------------------------------------------------- Booting obeys the following protocol: 1. Ignore button pushes that are very short (less than 8 ms). 2. Wait 1.5 seconds after a single push to see if the the user is performing a multiple-push boot. 3. If the button is down more than 2.5 seconds, ignore the button push. The baseboard also sees the boot button, and will boot the machine itself if a multiple-push boot occurs. The ReadTerminal subroutine, called once every scan line (38 us), watches the boot button, and reports results in these registers: TerminalHi < 0 iff boot button is presently down; BootTimer = duration of most recent button push (units of 38 us); 0 => no boot has occurred; 177777B => push was > 2.5 seconds long. Note: ReadTerminal filters out too-short pushes (< 8 ms). Note: BootTimer is valid only if the boot button is now up (TerminalHi >= 0). This logic runs once per field (60 times/second) and decides what to do. The BootState register reflects the current state, as follows: BootState=0 if nothing is happening; otherwise: BootState[0:14] = number of fields remaining until action should be taken (if boot button stays up); BootState[15] = action: 1 = boot, 0 = ignore. %*----------------------------------------------------------- BootCheck: * T=BootTimer; TerminalHi[0]=1 if boot button now down % turn off boot check while in Lisp PD_ T, TerminalHi, Branch[EndBootCheck, R<0]; PD_ BootState, Branch[NewBoot, ALU#0]; * Boot button is now up and has not been down recently. BootState_ (BootState)-(2C), Branch[.+2, ALU#0]; BootState_ A0, Branch[EndBootCheck]; * Nothing is happening * Timing button-up interval. Timer expired? Branch[.+2, ALU<0]; Branch[EndBootCheck]; * Timer has expired. Reset state and take appropriate action. BootState, Branch[Boot, R odd]; BootState_ A0, Branch[EndBootCheck]; * Boot button is now up but has been down recently. Start button-up timer, and set * action = ignore if multiple pushes are in progress or the last push was too long. NewBoot: BootState_ MinimumWait, Branch[.+3, ALU#0]; * Branch if multiple push PD_ (BootTimer)+1; * Action_ boot iff BootTimer # -1 BootState_ (BootState)+1, XorSavedCarry; BootTimer_ A0, Branch[EndBootCheck]; % * end of no boot check *----------------------------------------------------------- * Vertical retrace activity *----------------------------------------------------------- KnowRBase[THTRegion]; EndBootCheck: T_ (TReg400C) OR (LowByte[CursorXCoord]); TTemp0_ (Fetch_ T)+1; * Fetch cursorX; TTemp0_ cursorY TTemp1_ MD, T_ APointer, Call[OutputGetsT]; * reset NLCB[ReaderPtr] to zero !! T_ BPointer; Output_ T, Call[ReadTerminal]; * Kills wakeup; decrements TFieldAreaReg T_ CursorXConst, Block; * Block here so we don't overrun the scan line T_ T-(TTemp1); * Add cursor position to base constant Fetch_ TTemp0, Output_ T; * Send CursorX to NLCB; fetch cursorY T_ (TVCWShadowReg) AND (1C); * Isolate fieldtype T_ T-(TopBorderConst); * Bias cursor past top border T_ T-(TopBorderConst); TCursorYReg_ T-MD; * Calculate CursorY count/index T_ (TReg400C) OR (LowByte[DAStart]); T_ (Fetch_ T)+1; TDCBChainReg_ MD; * Issue the vertical field interrupt. * Must not do this until we have picked up all page 1 locations that the * software might change during the interrupt routine (particularly DAStart * and the cursor coordinates), else there is the possibility of a race. Fetch_ T; * 421B = vertical field interrupt mask RBase_ RBase[NWW]; NWW_ (NWW) OR MD, Reschedule; RBase_ RBase[THTRegion]; * Loop here for remainder of vertical sync VSyncLoop: Output_ TVCWShadowReg; * Also kills wakeup Call[ReadTerminal]; T_ TopBorderConst, Block, Branch[VSyncLoop, ALU#0]; * Tests TFieldAreaReg * End of vertical sync. * If odd field, generate one line of blank before beginning visible field. * This is because in odd fields vertical sync does not end until the middle * of the next scan line. TVCWShadowReg_ (TVCWShadowReg) AND (Not[VSync!]C), * VSync_ 0, VBlank_ 1 Branch[EvenField, R even]; Output_ TVCWShadowReg, * Also kills wakeup Call[ReadTerminal]; T_ TopBorderConst, Block; EvenField: TSLCReg_ T-1; * Set top border count T_ TVCWShadowReg_ (TVCWShadowReg) AND (Not[Or[VSync!, VBlank!]]C), Call[OutputGetsT]; * VSync_ 0, VBlank_ 0 TFieldAreaReg_ VisibleLineConst; RBase_ RBase[TWTRegion]; TReaderPtrReg_ A0, * Init to 0, needed every new field Branch[NoDCB]; * Skip top border before processing DCBs *----------------------------------------------------------- * Visible portion of field (including borders). Begin processing next DCB. * Note: borders are considered part of the "visible" portion of the field so * that cursor processing will occur within the borders as well. *----------------------------------------------------------- KnowRBase[TWTRegion]; NewDCB: PD_ TDCBChainReg; * Follow chain Branch[NoDCB, ALU=0]; T_ (Fetch_ TDCBChainReg)+1; * fetch word 0 = next DCB link TDCBChainReg_ MD, T_ (Fetch_ T)+1; * fetch word 1 = htab and width TScratch_ MD, T_ (Fetch_ T)+1; * fetch word 2 = bit map address TNextAddrLo_ MD, T_ (Fetch_ T)+1; * fetch word 3 = scan line count TSLCReg_ MD; * Check for Dorado/D0 "Long DCB": * if bit 0 of word 3 = 1, ignore the normal (short) bit map address, * and instead interpret words 4 and 5 as a long pointer to the bit map. TSLCReg_ (TSLCReg)-1, Branch[LongDCB, R<0]; * TSLCReg_ count-1 TNextAddrHi_ TBRHiReg, Branch[GetScan]; * Short pointer (=MDS) * The regular (16-bit) address must contain a seal before we will believe long DCB LongDCB: PD_ (TNextAddrLo) XOR (LongDCBSeal); T_ (Fetch_ T)+1, Branch[InvalidLongDCB, ALU#0]; TNextAddrLo_ MD, Fetch_ T; TNextAddrHi_ MD; TSLCReg_ (TSLCReg) XOR (100000C), Branch[GetScan]; InvalidLongDCB: TDCBChainReg_ A0, Branch[NoDCB]; * Stop DCB processing for this field * Set up Scan control in NLCB based on Alto polarity bit. * Ignore Alto resolution bit -- always use full resolution. GetScan: PD_ (TScratch) AND (AltoPolarityMask); *look at LCWord T_ Or[FullRes!, Size1!]C, Branch[.+2, ALU=0]; T_ T OR (AltoPolarity); T_ T OR (AScan), Call[OutputGetsT]; * Send scan control to NLCB * check for zero width DCB. T_ (TScratch) AND (377C); * Width in words TNWrdsMinus1_ T-1, Branch[.+2, ALU#0]; Branch[NoDCB]; * Set up width and left margin in NLCB TScratch_ LDF[TScratch, 6, 10]; * TScratch_ HTAB T_ LSH[TScratch, 4]; * Convert words to pixels T_ (LMargConst)-T, Call[OutputGetsT]; * Offset from HWindow and negate :If[AltoMode]; ********** Alto version ********** * Ensure that HTAB+NWRDS does not extend beyond the right-hand margin. * There is a bug in DispY that causes the picture to tear if that is done. T_ MaxWidthWords; T_ T-(TScratch); * T_ max allowable width PD_ T-(TNWrdsMinus1)-1; * Compare to actual width T_ LSH[T, 4], Branch[.+2, Carry']; * Convert words to pixels T_ (TNWrdsMinus1)+1, Branch[.-1]; * Use actual width :Else; ******** PrincOps version ******** T_ (TNWrdsMinus1)+1; * Use actual width T_ LSH[T, 4]; * Convert words to pixels :EndIf; ********************************** T_ T+(Sub[WidthOffset!, 1]C); * WidthOffset is positive const T_ T XOR (AWidthDataMask), * Negate and insert register select Call[OutputGetsT]; * send width to NLCB * Compute initial bit map offset based on even vs odd field. RBase_ RBase[THTRegion]; T_ A0, TVCWShadowReg, RBase_ RBase[TWTRegion], Branch[.+2, R odd]; T_ A_ T, Branch[MoreDCB]; * Even field T_ (TNWrdsMinus1)+1, Branch[MoreDCB]; * Odd field *----------------------------------------------------------- * THT per-scan-line activity *----------------------------------------------------------- KnowRBase[TWTRegion]; * Continue current DCB (from CheckSLC, below). T = 2*NWrds. * ALU contains (TNWrdsMinus1)+1, i.e., Carry iff zero-width DCB. * Advance address to next scan line for this field. MoreDCB: TNextAddrLo_ (TNextAddrLo)+T, Branch[DoCursor, Carry]; TNextAddrHi_ A_ TNextAddrHi, XorSavedCarry, Call[JamPtrAndNextCnt]; * Enable TWT to run!! TIOA[AHTFlag]; T_ A0, Output_ TSetNextWCB, Branch[DoCursor]; * Here if no DCB or zero width -- do not start up TWT. NoDCB: TNWrdsMinus1_ T-T-1, Call[SendMaxMarg]; * returns T=0 * Cursor processing. T must have zero on entry here. DoCursor: T_ T+1, RBase_ RBase[THTRegion]; T_ TCursorYReg_ (TCursorYReg)+T+1, TIOA[TNLCB], Branch[NoCursor, R<0]; PD_ T-(22C); * T is >= 2 T_ (TReg400C)+T, Branch[BeyondCursor, ALU>=0]; T_ T+(LowByte[Sub[CursorBitMap!, 2]]); Fetch_ T; * T was in [2..21], now points to data TTemp0_ MD, Branch[SendCursor]; NoCursor: T_ CursorLo, Branch[CheckSLC]; BeyondCursor: TCursorYReg_ 100000C; * maximum negative value TTemp0_ A0; * Cursor data _ 0 SendCursor: T_ (TTemp0) AND (377C); * Have cursor data in TTemp0 now T_ T OR (CursorLo), Call[OutputGetsT]; T_ RSH[TTemp0, 10]; T_ T OR (CursorHi); CheckSLC: Output_ T, Call[ReadTerminal]; * Returns with ALU = TFieldAreaReg * Notice: Code MUST have done at least one Output to NLCB before reaching here T_ (TReg400C)-1, RBase_ RBase[TWTRegion], Branch[EndOfField, ALU=0]; TSLCReg_ (TSLCReg)-1; T_ ((TNWrdsMinus1)+1) LSH 1, Block, DblBranch[NewDCB, MoreDCB, ALU<0]; :If[AltoMode]; ********** Alto version ********** EndOfField: MaxWidthWords_ (MaxWidthWords) AND T, RBase_ RBase[THTRegion], Block, Branch[THTNewField, R>=0]; Branch[ResetDisplayConfig]; * Switching between narrow and wide modes :Else; ******** PrincOps version ******** EndOfField: RBase_ RBase[THTRegion], Block, Branch[THTNewField]; :EndIf; ********************************** % Note: when the DCB chain is exhausted, TSLCReg will continue to decrement on every line and the Branch to NewDCB will be taken. NewDCB will find the TDCBChainReg = 0, so will go to NoDCB, then to DoCursor, and back to CheckSLC until FieldArea is exhausted. Note: TSLCReg maintains the remaining number of scan lines minus one. % *----------------------------------------------------------- SendMaxMarg: * Send maximum margins to NLCB -- for blank scan lines. * Entry: TIOA[NLCB] or TIOA[TNLCB] * Exit: T = 0 *----------------------------------------------------------- Subroutine; DontKnowRBase; T_ AMaxMarg; Output_ T; T_ BMaxMarg; * Does nothing if talking to DispM T_ A0, Output_ T, Return; *----------------------------------------------------------- JamPtrAndNextCnt: * First, calculates the number of munches to send to the hardware, * loading ANextCount. The hardware reader pointer is updated and sent * to the hardware. Finally, the shadow reader pointer is updated for the * next entry into this subroutine. Runs on behalf of AChannelRegion, * BChannelRegion, or TChannelRegion, depending on RBase. * Sets AReaderPtrReg, ANextCount. Destroys T. * Invariant for entry: AReaderPtrReg contains munch pointer into the fifo * for the Next scan line, and nothing else!! *----------------------------------------------------------- Subroutine; KnowRBase[AChannelRegion]; * or BChannelRegion, or TChannelRegion * first calculate next munch count for transfer. Calculation is * NEW Word Pointer - 1 + NWRDS. * Result in [8..11] is number of munches to transfer -1. T_ (ANextAddrLo) AND (17C); * get NEW word pointer ANextCount_ T+(ANWrdsMinus1); * set by DCB processing * merge WORD offset into fifo pointer and send to hardware T_ (AReaderPtrReg)+T; * word offset into RP T_ T OR (APointer); T_ T OR (AChannelReg); * Select channel for NLCB command T_ ANextCount, Output_ T; * send reader pointer * now calculate update for reader pointer register (shadows hardware RP) AReaderPtrReg_ (AReaderPtrReg)+T, Carry20; AReaderPtrReg_ (AReaderPtrReg) AND (360C), Return; *----------------------------------------------------------- SetDisplayBRHi: * Sets the high part of the display BR for Alto terminal emulation * Entry: T = new BRHi value *----------------------------------------------------------- Subroutine; DontKnowRBase; TBRHiReg_ T, Return; *----------------------------------------------------------- JLoadMiniMixer: * Loads the mininmixer with the identity for Alto emulation. * This is done ONCE in the beginning of the world. Users must be friendly enough * to reload MiniMixer as they exit from user code to Alto emulation code. * Entry: RBase[THTRegion]. * Exit: TIOA[MiniMixer] * Clobbers T, TTemp0, TTemp1 *----------------------------------------------------------- Subroutine; KnowRBase[THTRegion]; * MiniMixer inputs are: * 0 Cursor * 1 AItem.0 * 2 AItem.1 * 3 AItem.2 OR Polarity * 4 AItem.3 * 5 BItem.0 * 6 AOn * 7 BOn * Desired output function is: * IF ((Cursor OR AItem.0) XOR Polarity) = 1 THEN black ELSE white * where black = 0, white = 17B. * Note that all AItem.i are zero when AOn = 0 or i >= item size. * Therefore, for Alto emulation we don't need to look at AOn at all; and * AItem.2 OR Polarity = Polarity. TTemp1_ A0, TIOA[MiniMixer]; * address in 0..7, start with 0 JamMMLoop: T_ (TTemp1) LSH 1; TTemp0_ (TTemp1) OR T; * TTemp0.0_ AItem.0 OR Cursor T_ LSH[TTemp1, 3]; * T.0_ Polarity PD_ (TTemp0) XOR T; T_ TTemp1, Branch[.+2, ALU<0]; * Branch if black T_ T OR (17C); * White = 17B TTemp1_ (TTemp1)+(400C); Output_ T, Branch[JamMMLoop, ALU#0]; Return; TopLevel; :If[AltoMode]; ********** Alto version ********** *----------------------------------------------------------- SetDisplayConfigA: * Alto Opcode 61032B * Set display mode to Alto emulation (AC0=0) or full screen (AC0=-1), and * return the number of words per scan line in AC0. *----------------------------------------------------------- TopLevel; Set[XTask, IP[EMU]]; Nop, At[SD400, 32]; RBase_ RBase[DisplayConfig]; T_ OR[177400, MaxWidthWordsAlto!]C, DisplayConfig, Branch[RetConfig, R even]; * Branch if Alto monitor Stack, Branch[.+2, R<0]; * LF monitor, decide what to do * Switch to emulating an Alto monitor: DisplayConfig_ (DisplayConfig) AND NOT (2C), Branch[.+3]; * Switch to using the entire width of the LF monitor: DisplayConfig_ (DisplayConfig) OR (2C); T_ Or[177400, MaxWidthWordsLF!]C; * T has the new words/scanline, with 377 in the LH to signal DHT to reinitialize. MaxWidthWords_ T; RetConfig: Stack_ T AND (377C), IFUJump[0]; :EndIf; ********************************** *----------------------------------------------------------- SetDisplayFieldRate: * Emulator operation to adjust the vertical field rate * Enter: Stack[StkP] = total visible line count, including top and bottom border * Stack[StkP-1] = top border * Stack[StkP-2] = vertical sync * All counts are number of scan lines in the even field. * Exit: StkP_ StkP-3 *----------------------------------------------------------- TopLevel; Set[XTask, IP[EMU]]; T_ Stack&-1, RBase_ RBase[VisibleLineConst]; VisibleLineConst_ T; TopBorderConst_ Stack&-1; VSyncConst_ Stack&-1, IFUJump[0];