*----------------------------------------------------------- Title[DisplayMain.mc.....February 27, 1983 12:41 PM...Taft]; * 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,2,2], Dec[0,3,4,2]]; * 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,8,6], Dec[0,6,0,6]]; * 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 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]; *----------------------------------------------------------- * 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];