*----------------------------------------------------------- Title[ColorDisplay.mc...September 23, 1981 3:26 PM...Taft]; * Microcode for Dorado Color display. * Uses the general purpose display data structures * for the Dorado Display controller. *----------------------------------------------------------- % This microcode may be included with any emulator. If included, it must be loaded by MicroD AFTER DisplayMain and DisplayAux, thereby overriding the dummy DHTInitPC entry point that is in DisplayMain. The DHT microcode in this module will be executed only if a DispM board is installed. If there is no DispM board, the DispY board will be taken over for Alto Terminal emulation, and this code will never be reached. The DWT microcode and certain subroutines are shared with the Alto Terminal microcode, and are defined in DisplayMain and DisplayAux. % *----------------------------------------------------------- * Local R-register definitions *----------------------------------------------------------- SetRMRegion[AChannelRegion]; Reserve[nStandardDWTRegs]; * The standard ones defined in DisplayDefs RVN[AVCWShadowReg]; * Copy of VCWShadowReg RVN[AChanCtrlBlkPtr]; * pointer to channel control block RVN[AScanLineCount]; SetRMRegion[BChannelRegion]; Reserve[nStandardDWTRegs]; * The standard ones defined in DisplayDefs RVN[BVCWShadowReg]; * Copy of VCWShadowReg RVN[BChanCtrlBlkPtr]; * pointer to channel control block RVN[BScanLineCount]; SetRMRegion[DHTRegion]; Reserve[nStandardDHTRegs]; * The standard ones defined in DisplayDefs RVN[HTemp4]; * yet another temp RVN[ColorCtrlBlkPtr]; * pointer to color control block RVN[MCBFlags]; * infrequent operation flags RVN[VBLeadVSTrail]; * vertical control constants RVN[VStoVB]; * vertical control constants RVN[VisibleLines]; * vertical control constants *----------------------------------------------------------- * General interface definitions. *----------------------------------------------------------- MC[pMonitorCtrlBlk, IfE[AltoMode, 0, 177414, 414]]; MC[MCBSeal, 177456]; MC[MMixFlag, 100]; MC[PClkFlag, 40]; MC[HCtrlFlag, 20]; MC[VCtrlFlag, 10]; MC[ATableFlag, 4]; MC[BTableFlag, 2]; MC[CTableFlag, 1]; MC[AllMCBFlags, 177]; * Pixel clock constants for 10.06 MHz, a reasonable value * but NOT EXACTLY the 525 line monitor (which is 2614=11.95MHz)!! MC[VCOMul, 2400]; MC[VCODiv, 13]; *VCOMul OR VCODiv = 2413 = (120 lsh 4) OR 13 Set[XTask, IP[DHT]]; *----------------------------------------------------------- DHTInitPC: * Display Horizontal Task initialization *----------------------------------------------------------- Subroutine; T_ DHT, CoReturn; TopLevel; * DHT starts here * initialize B Channel registers RBase_ RBase[BChannelRegion]; BChannelReg_ 100000C; BSetNextWCB_ BNextWCBFlag; BCount_ T-T-1, MemBase_ IOBR; BAddress_ T-T-1, RBase_ RBase[AChannelRegion]; * initialize A Channel registers ASetNextWCB_ ANextWCBFlag; AScratch_ T_ Statics; AAddress_ T-T-1, TIOA_ T; T_ AllShutUp, Call[OutputGetsT]; * Disable WakeUps, Reset ctrler * Initialize both channels to maximum left margins, so they never turn on. AChannelReg_ A0, TIOA[NLCB]; ACount_ T-T-1, Call[SendMaxMarg]; * Set the VCO to 11.950 MHz = 83.57 nsec/pixel for 525 line monitor * the pixel clock has to have some reasonable value to generate wakeups T_ VCOClock; TIOA_ T; T_ VCOMul; T_ T OR (VCODiv), Call[OutputGetsT]; TIOA_ AScratch; * Needed for restoring high bits!! * Set constants RBase_ RBase[DHTRegion]; Reg400C_ 400C; VisibleLines_ T-T-1; * No visible lines flag * Initialize all of HRam to zeroes, removing any garbage horizontal waveforms. RME[CHRamLengthReg, HTemp1]; * JamHRam argument conventions RME[CHRamDataReg, HTemp2]; CHRamLengthReg_ HRamSize; * HRam[0..1777B] _ 0 CHRamDataReg_ T_ A0, TIOA[HRam], Call[JamHRam]; * Now load HRam with a dummy HBlank waveform, needed to generate wakeups T_ HTemp0_ A0; * HRam[0..47] _ Blank CHRamLengthReg_ 60C; CHRamDataReg_ HBlank, Call[JamHRam]; * Release HRam, start processing bitmaps and colormaps T_ ReleaseRam, Call[OutputGetsT]; * Clear out pMonitorCtrlBlk^ :If[AltoMode]; ********** Alto version ********** T_ (Reg400C) OR (And[pMonitorCtrlBlk!, 377]C); :Else; ******** PrincOps version ******** T_ pMonitorCtrlBlk; :EndIf; ********************************** Store_ T, DBuf_ HTemp0; * Clear 414 *----------------------------------------------------------- * Start of new field *----------------------------------------------------------- KnowRBase[DHTRegion]; * Initialization -- also branched to after monitor reinitialization * (VCtrl, HCtrl, or PClk). DHTNewFieldInit: * Scan initialization TIOA[NLCB]; T_ (AScan), Call[OutputGetsT]; * clear AScan to NLCB T_ (BScan), Call[OutputGetsT]; * clear BScan to NLCB * Enable DHT and DWT wakeups, and await first DHT wakeup. VCWShadowReg_ T_ A0, TIOA[Statics], Call[OutputGetsT]; Block, Branch[DHTNewField]; * Normal start of new field. DHTNewField: TIOA[NLCB]; Call[SendMaxMarg]; :If[AltoMode]; ********** Alto version ********** T_ (Reg400C) OR (And[pMonitorCtrlBlk!, 377]C); :Else; ******** PrincOps version ******** T_ pMonitorCtrlBlk; :EndIf; ********************************** Fetch_ T; T_ PD_ MD, RBase_ RBase[BChannelRegion]; * T_ ptr to MCBlock BScanLineCount_ T-T-1, Branch[NoMCB, ALU=0]; * Now fetch all pointers, flags, and MCBSeal. T_ (Fetch_ T)+(4C); * Fetch seal BScratch_ MD, T_ (Fetch_ T)-1; * Fetch color control block pointer PD_ (BScratch)#(MCBSeal); * Check seal ColorCtrlBlkPtr_ MD, Branch[BadMCBSeal, ALU#0]; T_ (Fetch_ T)-1; * Fetch BChanCtrlBlkPtr T_ (Fetch_ T)-1, BChanCtrlBlkPtr_ MD; * Fetch AChanCtrlBlkPtr BReaderPtrReg_ A0, RBase_ RBase[AChannelRegion]; AScanLineCount_ T-T-1; * Force new ChanCtrlBlk to be used Fetch_ T, AChanCtrlBlkPtr_ MD; * Fetch flags AReaderPtrReg_ A0, RBase_ RBase[DHTRegion]; MCBFlags_ (MCBFlags) OR MD; * Merge in new flags Store_ T, DBuf_ 0C, Branch[ColorVBToVSInit]; * Clear flags in MCB * No MCB present. Disable all MCB and ChanCtrlBlk processing. KnowRBase[BChannelRegion]; NoMCB: Nop; BadMCBSeal: AChanCtrlBlkPtr_ A0; BChanCtrlBlkPtr_ A0, RBase_ RBase[DHTRegion]; MCBFlags_ A0, Branch[ColorVBToVSInit]; *----------------------------------------------------------- * Vertical retrace activity. Know TIOA = NLCB here. *----------------------------------------------------------- * Begin blanking and wait one scan line before processing the MCB flags. * This is required because of the double-buffering in the controller. ColorVBToVSInit: T_ RSH[VBLeadVSTrail, 10]; * VB to VS count FieldAreaReg_ T; VCWShadowReg_ (VCWShadowReg) OR (VBlank); *0,0,0,FieldType Output_ VCWShadowReg, Call[DHTTimingCrock]; * First, if any of the MCBFlags are set, fire up AUT to process one of them. * If any of PClk, HCtrl, or VCtrl is set, then start over at DHTNewField, * since it's not reasonable to process DCBs when those are uninitialized. MCBFlags_ (MCBFlags) AND (AllMCBFlags), Block; VBToVS1: Branch[NoMCBFlags, ALU=0]; T_ MCBFlags; HTemp0_ T AND (Or[PClkFlag!, HCtrlFlag!, VCtrlFlag!]C), Call[.+2]; Branch[ProcessMCBFlags]; T_ Link, Call[RequestAUT]; RBase_ RBase[DHTRegion]; * RequestAUT clobbers RBase !! PD_ HTemp0; PD_ A0, Branch[VBToVS1, ALU=0]; Branch[DHTNewFieldInit]; NoMCBFlags: T_ BPointer, Call[OutputGetsT]; *reset NLCB[ReaderPtr] to zero !! T_ APointer, Call[OutputGetsT]; *reset NLCB[ReaderPtr] to zero !! VisibleLines, Branch[DHTNewFieldInit, R<0]; Nop; * Note: in the following loops, must do at least one Output to NLCB to * kill the wakeup, and must not block until third cycle after Output. * This is mostly what the DHTTimingCrock subroutine is for. ColorVBToVSLoop: Output_ VCWShadowReg, Call[DHTTimingCrock]; T_ VBLeadVSTrail, Block, Branch[ColorVBToVSLoop, ALU<0]; ColorVSToVSInit: * Complement field type and set VSync=1 (VBlank=1 already) VCWShadowReg_ (VCWShadowReg) XOR (Or[VSync!, 1]C); FieldAreaReg_ T AND (377C); *T_ VSToVS count ColorVSToVSLoop: Output_ VCWShadowReg, Call[DHTTimingCrock]; T_ (VCWShadowReg) AND (1C), Block, Branch[ColorVSToVSLoop, ALU<0]; ColorVSToVBInit: VCWShadowReg_ (VCWShadowReg) AND (Not[VSync!]C); FieldAreaReg_ T+(VStoVB); * T = field type; add to VBlank count ColorVSToVBLoop: Output_ VCWShadowReg, Call[DHTTimingCrock]; Block, Branch[ColorVSToVBLoop, ALU<0]; ColorVBDone: T_ VCWShadowReg_ (VCWShadowReg) AND (Not[Or[VSync!, VBlank!]]C); AVCWShadowReg_ T; BVCWShadowReg_ T, Call[OutputGetsT]; FieldAreaReg_ VisibleLines, Branch[NewScanLineA]; *----------------------------------------------------------- DHTTimingCrock: * Consumes 2 cycles to fill up the minimum wait between Output and Block. * Decrements FieldAreaReg and returns ALU = -(FieldAreaReg); * this eases placement of caller's conditional branches! *----------------------------------------------------------- Subroutine; FieldAreaReg_ (FieldAreaReg)-1; PD_ (0S)-(FieldAreaReg), Return; TopLevel; *----------------------------------------------------------- * DHT per-scan-line activity. * The following microcode is SHARED by the two channels and is executed TWICE * per line; once with RBase=AChannelRegion and again with RBase=BChannelRegion *----------------------------------------------------------- NewScanLineB: RBase_ RBase[BChannelRegion], Branch[.+2]; NewScanLineA: RBase_ RBase[AChannelRegion]; AScanLineCount_ (AScanLineCount)-1, TIOA[NLCB], Branch[.+2, R<0]; ANextAddrHi, DblBranch[ColorBitMapNil, MoreColorBitMap, R<0]; * Initialize channel the first time around or follow ChanCtrlBlk chain. PD_ AChanCtrlBlkPtr; Branch[.+2, ALU#0]; AScanLineCount_ 77777C, Branch[NilControlBlock]; * End of chain T_ (Fetch_ AChanCtrlBlkPtr)+1; AChanCtrlBlkPtr_ MD, T_ (Fetch_ T)+1; * Fetch nwords/line ANWrdsMinus1_ MD, T_ (Fetch_ T)+1; * Fetch MSH long pointer ANextAddrLo_ MD, T_ (Fetch_ T)+1; * Fetch LSH long pointer ANextAddrHi_ MD, T_ (Fetch_ T)+1; * Fetch scanlines/field T_ MD, AScratch_ (Fetch_ T)+1; * Fetch pixels/line ("width") ANWrdsMinus1_ (ANWrdsMinus1)-1; AScanLineCount_ T-(2C), Branch[NonNilControlBlock, ALU>=0]; NilControlBlock: * Zero pixels/line or end of chain T_ (AChannelReg) OR (AMaxMarg), Call[OutputGetsT]; ANextAddrHi_ T-T-1, Branch[ColorBitMapNil]; * Nil bitmap NonNilControlBlock: T_ (0S)-MD; * Negate pixels/line AScratch_ (Fetch_ AScratch)+1; * Fetch left margin T _ T AND (AWidthDataMask), Call[NLCBGetsT]; * send width to NLCB T_ (0S)-MD; * Negate left margin Fetch_ AScratch; * Fetch scan control word AScratch_ MD, T_ T AND (ALMargDataMask), Call[NLCBGetsT]; T_ (AScratch) OR (AScan); T_ T OR (AChannelReg), Call[NLCBGetsT]; * send scan ctrl to NLCB T_ RSH[AScratch, 6]; T_ T OR (Modes), Call[OutputGetsT]; * send mode ctrl to NLCB T_ A0, AVCWShadowReg, Branch[AdvanceColorBitMap, R even]; T_ (ANWrdsMinus1)+1, Branch[AdvanceColorBitMap]; * Odd field * Advance pointer to start of next scan line MoreColorBitMap: T_ ((ANWrdsMinus1)+1) LSH 1; * T_ 2 * line length AdvanceColorBitMap: ANextAddrLo_ (ANextAddrLo)+T; ANextAddrHi_ A_ ANextAddrHi, XorSavedCarry; Call[JamPtrAndNextCnt]; * Enable DWT to run for this channel T_ ASetNextWCB, TIOA[DHTFlag], Branch[ColorBitMapOutput]; * Here if bit map is nil. But we must do at least one Output to NLCB * in order to kill the wakeup, so do an innocuous one here. ColorBitMapNil: T_ CursorLo; * Output to either DHTFlag or NLCB here, depending on whether or not there is * a bit map. Then, if just did the A channel, switch to the B channel and * go around again. ColorBitMapOutput: Output_ T, AChannelReg, Branch[NewScanLineB, R>=0]; * Notice: Code MUST have done at least one OUTPUT to NLCB before reaching here RBase_ RBase[DHTRegion]; FieldAreaReg_ (FieldAreaReg)-1; Block, DblBranch[NewScanLineA, DHTNewField, ALU#0]; *----------------------------------------------------------- NLCBGetsT: * Output from T to NLCB for appropriate channel. * Clobbers T. *----------------------------------------------------------- Subroutine; KnowRBase[AChannelRegion]; T_ T OR (AChannelReg), TIOA[NLCB]; Output_ T, Return; TopLevel; *----------------------------------------------------------- ProcessMCBFlags: * Process MCB flags. The actual work is done by the Asynchronous Utility * Task. We process only one flag per call so as not to run for too long -- * this code shares some registers with DHT (HTemp0-4) and DWT (AChannelBR), * which are free only during vertical retrace. It is assumed that we will * be finished long before the end of vertical retrace! *----------------------------------------------------------- RBase_ RBase[DHTRegion]; HTemp0_ T-T-1, MemBase_ IOBR; * Init counter T_ VCOClock; * Nearly all TIOAs of interest are TIOA_ T; * in this block (360-367) T_ MMixFlag; * Leftmost flag * Test the MCBFlags left-to-right. PD_ (MCBFlags) AND T; HTemp0_ (HTemp0)+1, Branch[.+2, ALU#0]; T_ T RSH 1, Branch[.-2]; * Dispatch and turn off the flag. T_ NOT T, BDispatch_ HTemp0; MCBFlags_ (MCBFlags) AND T; *----------------------------------------------------------- MCBFunctionTable: DispTable[7]; Branch[ProcessMCBDone]; * MiniMixer -- unimplemented T_ (ColorCtrlBlkPtr)+(10C), Branch[ProcessVCtrl]; T_ (ColorCtrlBlkPtr)+(13C), Branch[ProcessHCtrl]; T_ (ColorCtrlBlkPtr)+(16C), Branch[ProcessPClk]; T_ (ColorCtrlBlkPtr)+(0C), Branch[ProcessAMap]; T_ (ColorCtrlBlkPtr)+(2C), Branch[ProcessBMap]; T_ (ColorCtrlBlkPtr)+(4C), Branch[ProcessCMap]; *----------------------------------------------------------- *----------------------------------------------------------- ProcessVCtrl: * Set vertical control parameters from color control block. * T = ColorCtrlBlkPtr+10. *----------------------------------------------------------- T_ (Fetch_ T)+1; VBLeadVSTrail_ MD, T_ (Fetch_ T)+1; VStoVB_ MD, Fetch_ T; VisibleLines _ MD, Branch[ProcessMCBDone]; *----------------------------------------------------------- ProcessHCtrl: * Load HRam from color control block. T = ColorCtrlBlkPtr+13. *----------------------------------------------------------- * Three words are fetched X = maximum address in HRam * from color control block: W,,A = W is HB to HS leading edge * A is address of HS trailing edge * B-A = HS trail to HB trail RME[CHRamMaxAddr, HTemp0]; * JamHRam arguments are HTemp1 and HTemp2. RME[CHBlankLeadReg, HTemp3]; RME[CHBlankTrailReg, HTemp4]; T_ (Fetch_ T)+1; CHRamMaxAddr_ MD, T_ (Fetch_ T)+1; CHBlankLeadReg_ MD, Fetch_ T; T_ HRam, CHBlankTrailReg _ MD; TIOA_ T; CHRamLengthReg_ HRamSize; * HRam[0..1777] _ 0 CHRamDataReg_ T_ A0, Call[JamHRam]; * Now load HRam with Color specific waveforms T_ LDF[CHBlankLeadReg, 10, 10]; * HRam[(X-W)..(X-1)] _ Blank CHRamLengthReg_ T; CHRamDataReg_ HBlank; T_ (CHRamMaxAddr)-T, Call[JamHRam]; CHBlankLeadReg_ T_ LDF[CHBlankLeadReg, 10, 0]; * HRam[A..B-1] _ Blank CHRamLengthReg_ CHBlankTrailReg, Call[JamHRam]; CHRamDataReg_ Or[HSync!, HBlank!]C; * HRam[X..X] _ Sync&Blank CHRamLengthReg_ 1C; T_ CHRamMaxAddr, Call[JamHRam]; CHRamLengthReg_ CHBlankLeadReg; * HRam[0..A-1] _ Sync&Blank T_ A0, Call[JamHRam]; T_ (CHRamMaxAddr) RSH 1; * HRam[(X/2)..(X/2)+(A-1)] _ HalfLine CHRamDataReg_ HalfLine, Call[JamHRam]; ReleaseSomeRam: T_ ReleaseRam, Call[OutputGetsT]; ProcessMCBDone: Branch[AUTStart]; *----------------------------------------------------------- JamHRam: * Enter: T = starting HRam address * CHRamLengthReg = number of consecutive HRam locations to load * CHRamDataReg = HRam data to load * TIOA[HRam], RBase[DHTRegion] * Clobbers T *----------------------------------------------------------- Subroutine; T_ T OR (LoadAddress); T_ (CHRamLengthReg)-1, Output_ T; * Send initial address T_ T-1, Output_ CHRamDataReg, Branch[., ALU#0]; Return; TopLevel; *----------------------------------------------------------- ProcessPClk: * Set pixel clock rate. T = ColorCtrlBlkPtr+16. TIOA = VCOClock already. *----------------------------------------------------------- Fetch_ T; Output_ MD, Branch[ProcessMCBDone]; *----------------------------------------------------------- * Load one of the color tables (AMap, BMap, or CMap) from the color * control block. T = address of low word of appropriate long pointer. *----------------------------------------------------------- ProcessAMap: HTemp0_ 3777C; * length of ATable -1 HTemp0_ (HTemp0)-1, TIOA[Mixer], Branch[DoSomeTable]; * = AMap ProcessBMap: TIOA[BMap], Branch[.+2]; ProcessCMap: TIOA[CMap]; HTemp0_ 376C; * length of B or CTable -2 * HTemp0 = (length of table)-2 DoSomeTable: T_ (Fetch_ T)+1; * Fetch the long pointer HTemp1_ MD, Fetch_ T, T_ LoadAddress, * Keep, DontWrite, LoadAddr = 0 Call[OutputGetsT]; * Initial LoadAddr just to grab the Ram MemBase_ AChannelBR; * Borrow DWT's BR HTemp1_ MD, BRLo_ HTemp1; BRHi_ HTemp1; HTemp1_ Cnt; * Must preserve Cnt register HTemp0_ A0, Cnt_ HTemp0; * Init address and count * Note: this loop consumes about 130 microseconds doing useful work, * and another 230 microseconds holding due to cache misses! * This could be improved by PreFetching the data, but loading tables * is so infrequent that it's probably not worthwhile to do so. * Note: first Output_ T sends LoadAddress; subsequent ones send data. HTemp0_ (Fetch_ HTemp0)+1; * Prime the pipeline HTemp0_ (Fetch_ HTemp0)+1, T_ MD, Output_ T, Branch[., Cnt#0&-1]; T_ MD, Output_ T, Call[OutputGetsT]; * Output last 2 words from pipeline Cnt_ HTemp1, Branch[ReleaseSomeRam]; (1552)