:IF[WithUTVFC]; *********************************************** Set[LFMonitor,1]; *Avoid error when inserting DisplayDefs. INSERT[DisplayDefs]; *Defs file common to DisplayInit and Display TITLE[DisplayInit]; *Ed Fiala 15 October 1982 %Jump to DisplayInit from DeviceInit running as DisplayTask. Determine the terminal configuration from bits 10..14b of the UTVFC ID register: LF Dallas keyboard/LF monitor = 3 (resident); CSLF CSL keyboard/LF monitor = 11b (1st overlay); and CSL CSL keyboard/CSL monitor = 5 (2nd overlay). Initialization for all of these is contained here (overwritten by a subsequent overlay, so code size is not a major issue). The LF microcode is resident; drivers for other configurations are in overlays which overwrite the resident if they are loaded. Initialization begins by loading the appropriate overlay into IMX and moving LoadRAM's pointer past the last overlay. Monitors require a certain time/scanline, and this time must be divided in a specified ratio between forward scan and horizontal sync--hardware adjustments cannot compensate for much variation and for a particular hardware adjustment, all microcode systems should use the same operating parameters. With a 50 mhz oscillator, the LF monitor requires 1080 bits forward scan and 360 bits horizontal sync totalling 28.8 us/scanline. With a 20 mhz oscillator, the CSL monitor requires 652 bits forward and 104 bits horizontal sync totalling 38 us/scanline. The forward scan consists of blanked area, image, and blanked area. The UTVFC limits image size to 1024 bits. For the LF monitor, it was empirically determined that hardware adjustments could compensate for substantial sweep rate variations by adding (invisible) scanlines at the end of each field before vertical sync. In DisplayDefs.Mc, the vVSStart parameter may be varied between about 11b and 300b to control field rate. At 11b (77 fields/sec), no flicker is noticeable; at 140b (65 fields/sec), flicker is just barely noticeable; at 300b (55 fields/sec) flicker definitely degrades the picture. Increasing vVSStart without hardware adjustments spreads the picture vertically, so not too much software flexibility is permissible. The ability to operate at less than maximum field rate is potentially of interest because the display task averages roughly 20 percent of all cycles at 77 fields/sec compared to 16 percent at 65 fields/sec, which improves the emulator's machine share from about 79 percent to 83 percent (= 5 percent faster). However, at present all microcode systems use 77 fields/sec. For the CSL monitor, very little variation in the vertical parameters is possible; the current choice results in 60 fields/second. A CSL monitor is 608 visible bits/scanline x 808 visible scanlines; the LF monitor is 1080 x 861, but this microcode reduces its capability to 1024 x 808--the reduction to 808 is desirable because the monitors will also be used by Alto compatible microcode, so it is desirable to adjust them to be full-screen at 808 scanlines. All terminal drivers are built from Display.Mc which contains conditional assemblies for code specific to a keyboard or monitor; CSLDisplay.Mc, LFDisplay.Mc, and CSLFDisplay.Mc set the switches to an appropriate value and INSERT[Display]. Differences among the drivers appear in the code for putting out data for each scanline. The Dallas and CSL keyboard drivers are entirely different. Also, HRAM initialization below is different. For the CSL monitor, vBufStart = 150b, so 230b nibbles (=608 bits =38 words) of data are supplied by the task per scanline; an additional 5b nibbles of forward scan occur while blanking just before and just after horizontal sync; and there are 32b nibbles of horizonal sync. The midline bit in HRam appears 1/2 x (230b+12b+32b) = 136b nibbles after the end of horizontal sync. Since data begins at AAR=150b, ML should appear at 150b+136b-5b = nibble 301b. For the LF monitor, 1080 bits (= 416b nibbles) are in forward scan, 360 bits (= 132b nibbles) are horizontal sync; ML appears 1/2 * 550b nibbles after ending horizontal sync. Since data begins at AAR=0, ML should appear at 0b+264b-number of blanked nibbles following horizontal sync. Total vert blanking = vVSStart + vSyncLength + vBlankLength + 21d scanlines. % SetTask[DisplayTask]; *Get here from DeviceInit running at the Display's task level. DisplayInit: vCR _ ClrNC&Blank, At[DisplayInitLoc]; *Clear Nibble counter vCSBHi _ 0C, Call[vCRout]; *Suspicious that the nibble counter is only cleared when AllowWU is false, *so do this output twice. vCSB _ ZeroDataBuffer, Call[vCRout]; *Using vCSB as temporary BR here vSLC _ 104400C; T _ vDBuf0 _ 0C, Call[vQIni]; *Zero the 20b-word storage buffer used for IOFetch4/16 when display is off. *(?Suspicious that Initial doesn't zero core, so have to do it here?) **5 mi after Output above. PStore4[vCSB,vDBuf0,0]; PStore4[vCSB,vDBuf0,4]; PStore4[vCSB,vDBuf0,10]; PStore4[vCSB,vDBuf0,14]; Output[vSLC,vBufStart]; *Start_nibble 44b, ForceAARLoad'_0 *Setup a quadword with IncNC&Blank in each word so that an IOFetch4 can be *used instead of four Output's to generate NClk's needed when loading HRAM. *Since tasking does not occur during HRAM initialization, refresh is delayed *by only 0.4 msec with IOFetch4 instead of 1.0 msec with four Output's. *This could conceivably be important. T _ vDBuf0 _ IncNC&Blank, Call[vQIni]; vCSB _ HiA[CSBAddress]; vCSB _ (vCSB) or (LoA[CSBAddress]); vSLC _ LoA[Task0NotifyLocA]; PStore4[vCSB,vDBuf0,10]; **5 mi after the Output above Input[vDBuf3,0]; *Controller ID code in bits 10:14b LU _ (vDBuf3) and (140C); *Test for LF monitor/LF keyboard LU _ (vDBuf3) and (100C), GoTo[vLoadOverlay,ALU#0]; *Jump if CSL keyboard *For LF/LF terminal, skip the two overlays in storage so that LRJ's registers *will point at the overlay after that. Must switch to task 0 to access LRJ's *registers. vNotify: vSLC _ (vSLC) or (HiA[Task0NotifyLocA]); vNotify1: APCTask&APC _ vSLC, GoTo[vAddr7777]; *Here if CSL keyboard; continue LoadRAM if LF monitor, else skip one more *overlay and then continue LoadRAM. LoadRAM continues at Task0NotifyLocC or *at vContinueInitLoc as specified to MakeLoaderFile in Pilot.Mlf. vLoadOverlay: vSLC _ LoA[LRJContinue], Skip[ALU#0]; vSLC _ LoA[Task0NotifyLocC], GoTo[vNotify]; *LF monitor, CSL kyboard vSLC _ (vSLC) or (HiA[LRJContinue]), GoTo[vNotify1]; SetTask[0]; %The code for skipping overlays must be compatible with both old and new LoadRAM (which Pilot inherits from Initial), so it accepts the word displacement in xfTemp+LP but leaves xfTemp .eq. 0 and the modified displacement only in LP. % vLPCarry: Skip[Carry']; LPhi _ (LPhi) + (400C) + 1; vAddr7777: T _ 170000C, Return; vSkipOverlay: T _ APCTask&APC; RTemp _ T; T _ xfTemp; LP _ (LP) + T, Call[vLPCarry]; vSkipOvLp: PFetch1[LP,xfTemp,0]; LP _ (LP) + (3C), Call[vLPCarry]; xfTemp _ (LdF[xfTemp,0,14]) xnor T; APCTask&APC _ RTemp, Skip[ALU=0]; GoTo[vSkipOvLp]; vIRet: Return; *For the Star keyboard/LF monitor configuration, skip both overlays. *LP+xfTemp points to next word of memory to be loaded into IM. *The 1st overlay skipped is for the Star keyboard/CSL monitor. UseCTask, Call[vSkipOverlay], At[Task0NotifyLocA]; *LoadRAM continues here after loading the LF monitor/CSL keyboard overlay. *Skip the 2nd display overlay (for the CSL keyboard/CSL monitor). UseCTask, Call[vSkipOverlay], At[Task0NotifyLocB]; *The LoadRAM pointer (LP+xfTemp) is now advanced over both terminal overlays, *and the appropriate microcode has been loaded onto the display page. *The 2nd overlay specifies vOverlayLoadedLoc as its starting address. *Notify DisplayTask at vContinueInit. RTemp _ LoA[vContinueInitLoc], At[vOverlayLoadedLoc]; T _ RTemp _ (RTemp) or (HiA[vContinueInitLoc,DisplayTask]); vNotifyBack: T _ (APCTask&APC _ RTemp) - T, Call[vIRet]; *Task 0 will resume here when it next runs, but not until after HRAM init, *when clock speed determination for the time-of-day clock and other timers *will be carried out. The long comment later explains how this works. T _ (Zero) + T + 1; RTemp _ (RTemp) or (LoA[vSLCountLoc]), Skip[Carry]; RTemp _ (HiA[vSLCountLoc,DisplayTask]), Return; APCTask&APC _ RTemp, Call[vIRet]; *Next time task 0 runs, pass control back to DeviceInit. LoadPage[InitPage]; GoToP[DI5a]; *Here to skip 1st overlay and load the 2nd (CSL keyboard and monitor). UseCTask, Call[vSkipOverlay], At[Task0NotifyLocC]; *Now continue LoadRAM to load the 2nd overlay. RTemp _ LoA[LRJContinue]; RTemp _ (RTemp) or (HiA[LRJContinue]), GoTo[vNotifyBack]; SetTask[DisplayTask]; vContinueInit: vDBuf0 _ 40000C, At[vContinueInitLoc]; *to load AAR vCnt _ 0C; vCR _ IncNC&Blank, Call[vCRout]; *Increment NClk bit Output[vCnt,vHCRam], Call[vNClk]; *NClk to load AAR_nibble 44b Output[vDBuf0,vBufStart]; *ForceAARLoad'_1, ForceIARLoad'_0 Output[vDBuf0,vLdIAR]; *Load IAR from Start LU _ (vDBuf3) and (40C); *Only vDBuf3[10:14b] .eq. 5 has *the 40b bit set, so this tests *for CSL monitor vCnt _ 44C, GoTo[vLFInit,ALU=0]; *Load HRam, starting at nibble 44 *CSL monitor HRAM init T _ 277C, Call[vOutNClk0]; * 44 - 277 _ 0 (150 up data) *ML at 150 + (1/2 * (230+46)) - 7 = 300 vSLC _ 1C, Call[vOutNClk]; *300 _ 1 (ML) T _ 376C, Call[vOutNClk0]; *301 - 376 _ 0 (data) vSLC _ 10C, Call[vOutNClk]; *377 _ 10 (SetC--start blanking) *46b nibbles of blanking must have 32b words of horizontal sync; the other *14b nibbles may be blanked either before or after horizontal sync; this *positions the active image left or right on the screen. T _ 4C, Call[vOutNClk0]; * 0 - 4 _ 0 (blank, no sync) vSLC _ 4C; T _ 36C, Call[vOutNClk]; * 5 - 36 _ 4 (blank, hor. sync) T _ 44C, Call[vOutNClk0]; * 37 - 44 _ 0 (blank) vSLC _ 2C, Call[vOutNClk]; * 45 _ 2 (Switch--end blanking) vSLC _ Add[140000,15000]C, GoTo[vSwiI]; *CSL monitor vStart %Since HRam is only 256 bits long, it is necessary to take advantage of the fact that the Switch bit is ignored unless blanking is in force. On each scanline, AAR is initialized to vBufSt and then cycles until SetC is turned on to start blanking and then until the Switch bit occurs. Hence, initialization must setup a total line time such that data bits+ blanking bits = 1080+360, where at most 1024 bits are visible, 360 must be blanked with horizontal sync true, and the other 56 bits are blanked, divided arbitrarily between the left and right parts of the display. The code below sets up 256 nibbles data, 14 nibbles blanking, and 90 nibbles horizontal sync. % vLFInit: Nop; T _ 254C, Call[vOutNClk0]; * 44 - 254 _ 0 vSLC _ 1C, Call[vOutNClk]; *255 _ 1 (ML .eq. midline) T _ 376C, Call[vOutNClk0]; *256 - 376 _ 0 vSLC _ 10C, Call[vOutNClk]; *377 _ 10 (SetC--start blanking) T _ 6C, Call[vOutNClk0]; * 0 - 6 _ 0 vSLC _ 4C; T _ 140C, Call[vOutNClk]; * 7 - 140 _ 4 (Hor sync) T _ 147C, Call[vOutNClk0]; *141 - 147 _ 0 vSLC _ 2C, Call[vOutNClk]; *150 _ 2 (Switch--end blanking) vSLC _ Add[140000,0]C; *LF monitor vStart %Tried this code, which looks identical to the above but executes faster, but it didn't work all the time for some reason. It would be desirable to get this variation working because storage refresh is off for a longer time with the code above than with this code. % % vSLC _ 4C; T _ 140C, Call[vOutNClk]; * 44 - 140 _ 4 (Hor. sync) T _ 147C, Call[vOutNClk0]; *141 - 147 _ 0 vSLC _ 2C, Call[vOutNClk]; *150 _ 2 (Switch--end blanking) T _ 254C, Call[vOutNClk0]; *151 - 254 _ 0 vSLC _ 1C, Call[vOutNClk]; *255 _ 1 (ML .eq. midline) T _ 376C, Call [vOutNClk0]; *256 - 376 _ 0 vSLC _ 10C, Call[vOutNClk]; *377 _ 10 (SetC--start blanking) T _ 6C, Call[vOutNClk0]; * 0 - 6 _ 0 vSLC _ 4C; T _ 44C, Call[vOutNClk]; * 7 - 44 _ 4 (Hor. sync) vSLC _ Add[140000,0]C; *LF monitor vStart % %HRam has just been initialized to 652 forward and 104 reverse = 756 bits/scanline for CSL monitors or to 1080 forward and 360 reverse = 1440 bits/scanline for LF monitors. This establishes a scanline time of 28.800 us (LF) or 37.800 us (CSL). We want to utilize these times to determine the unknown processor clock period by counting cycles/scanline. To discriminate 40 mhz (100 ns/cycle), 44.5 mhz (89.886 ns/cycle), and 50 mhz (80 ns/cycle) processor clocks, DisplayTask counts scanlines in a six-cycle loop while the emulator executes a six-cycle loop 65536d times. The predicted scanline count is 6*65536*C/(L-(6*C)-X), where L is time/scanline, C is processor clock period, and X is other task overhead/scanline in cycles. The computation below ignores all components of X except the refresh timer at L*(18/2560). So the computations are (393216*C)/(288-6*C-2) for LF monitors or (393216*C)/(378-6*C-3) for CSL monitors. Predicted Predicted CSL LF CSL LF Scanlines Scanlines Threshold Threshold 40.0 mHz 1066 1404 .g. 1046 .g. 1375 44.5 mHz 955 1258 .g. 935 .g. 1230 50.0 mHz 850 1119 Observed counts with 40.0 mhz processor clock 1066 on a CSL monitor, ? with an LF monitor. In addition, the code below will setup the vCrystal register with a value indicative of the rate at which the high resolution timer in Timer.Mc is counting (presently 1 every RefreshPeriod cycles). The default value is 80, representing 1 ticks/2560 cycles at 40 mHz processor clock, equivalent to 1 tick/256 microseconds. Currently envisioned values are as follows: 640 1280 2560 cycles/tick 40 mHz 320 160 80 44.5 356 178 89 50 400 200 100 In other words, the 32-bit high resolution timer counts in units of 20480d/vCrystal microseconds. vCrystal can be read by the READR Esc opcode. % vSwiI: Output[vSLC,vBufStart]; Output[vSLC,vLdIAR]; *IAR _ Start vSLC _ (Zero) - 1; vCR _ AllowWU&Blank, Call[vCRout]; *Call sets up loop *Count scanlines here until stopped by the emulator. Nop; *IOStrobe in 1st mi won't work T _ vSLC _ (vSLC) + 1, IOStrobe, GoTo[vIRet]; *Emulator restarts DisplayTask here Input[vDBuf0,0], At[vSLCountLoc]; LU _ (vDBuf0) and (40C); vSLC _ (vSLC) - (2000C), GoTo[vCSLThresholds,ALU#0]; *LF thresholds are now 351d and 206d, respectively. vSLC _ (vSLC) - (316C); *206d LU _ (vSLC) - (221C), DblGoTo[v50mhz,v40x,ALU<0]; *145d *CSL thresholds are now 22d and -89d, respectively. vCSLThresholds: vSLC _ (vSLC) + (131C); *89d LU _ (vSLC) - (157C), GoTo[v40x,ALU>=0]; *111d *Store 2 x processor crystal speed in vCrystal. v50mhz: vCrystal _ 144C, GoTo[vSetTimer]; *50.0 mhz crystal (100d) v40x: vCrystal _ 131C, GoTo[vSetTimer,ALU<0]; *44.5 mhz crystal (89d) vCrystal _ 120C; *40.0 mhz crystal (80d) :IF[IFE[RefreshPeriod,5000,0,1]]; ***************** vSetTimer: Set[RefPerShift,IFE[RefreshPeriod,2400,1,IFE[RefreshPeriod,1200,2]]]; vCrystal _ LSh[vCrystal,RefPerShift]; T _ vDBuf0 _ 0C, Call[vQIni]; :ELSE; ******************************************** vSetTimer: T _ vDBuf0 _ 0C, Call[vQIni]; :ENDIF; ******************************************* T _ vMsgStatus _ 0C, Call[.+1]; *Call sets up loop vCR _ AllowWU&Blank; T _ vMsgStatus _ 0C, Call[vCRout]; *Call sets up loop *Zero cursor memory (32 bits/scanline x 32 scanlines); must zero (most of) *the cursor memory because only 16 x 16 of it will be used later. **NOTE: Blanking must be on when loading the cursor memory. **NOTE: This code must finish execution before the page on which it resides **is overwritten by the Pilot2 code. vCurIni: vSLC _ 6C; *8-2 nibbles/scanline Output[vMsgStatus,vCursor0]; vMsgStatus _ (vMsgStatus) + (4C); Output[vDBuf0,vCursorMem0]; vSLC _ (vSLC) - 1, GoTo[.-3,R>=0]; vMsgStatus _ (vMsgStatus) + (4000C); vMsgStatus _ (vMsgStatus) and (174000C), Skip[Carry]; *GoTo here allows 5 mi after Output-Output, avoiding hardware problem. GoTo[vIRet]; *Wind up here with vMsgStatus .eq. 0 Call[vIRet]; *Set keyboard words 177710-177717 to -1 (177714-177717 unused by CSL keyboard, *177717 unused by LF keyboard) T _ vDBuf0 _ (Zero) - 1, Call[vQIni]; vCR _ AllowWU; PStore4[vCSB,vDBuf0,10]; *KB0-KB3 PStore4[vCSB,vDBuf0,14]; *KB4-KB7 * vMouseDxy _ T _ 0C; *Not initializing these seems harmless * vMsg _ 0C; T _ vButtons _ 0C; vKeyBuffer _ T, LoadPage[DisplayPage]; vMsgStatus _ 75C, GoToP[vFDone]; *End init **NOTE: An NClk is produced with an IOFetch4 rather than four Output's so **that display initialization doesn't defer memory refresh too long; as **coded, initialization runs about 0.4 msec before tasking. *SUBROUTINE vNClk generates one NClk. vNClk: UseCTask; *The quadword contains IncNC&Blank in all 4 words IOFetch4[vCSB,vfCReg,10], Return; vCRout: UseCTask; Output[vCR,vCReg], Return; *SUBROUTINE vOutNClk sends the pattern in vSLC to the HCRam until the *address = T; does T_T+1 prior to Return for caller. vOutNClk0: vSLC _ 0C; vOutNClk: vCnt _ (vCnt) + 1; LU _ (RHMask[vCnt]) xor T; Output[vSLC,vHCRam], Skip[ALU#0]; T _ (Zero) + T + 1, GoTo[vNClk]; IOFetch4[vCSB,vfCReg,10], GoTo[vOutNClk]; vQIni: vDBuf1 _ T; vDBuf2 _ T, UseCTask; vDBuf3 _ T, Return; END[DisplayInit]; :ELSE; ******************************************************** TITLE[No.UTVFC.microcode]; :ENDIF; ******************************************************* %vClrBf: IOFetch16[vCSB,vfBuf0,0]; IOStrobe; IOFetch16[vCSB,vfBuf0,0]; IOFetch16[vCSB,vfBuf0,0]; IOFetch16[vCSB,vfBuf0,0], Return; *Clear the two ping-pong buffers and block (not needed unless a white area *is to be shown to the right of the active display) vSLC _ 140000C; Output[vSLC,vBufStart]; *ForceIARLoad'_ForceAARLoad'_Start_0 T _ vMsgStatus _ 0C; Output[vSLC,vLdIAR], Call[vClrBf]; *IAR _ Start vButtons _ T, Call[vClrBf]; *Call sets up loop % (1792)\f5 6444f0 1181f5 282f0 438f5 36f0 123f5 2549f0 12f5 1f0 30f5 758f0 12f5 1f0 1645f5 34f0 1f5 55f0 147f5 6f0 1353f5