: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:
64012802560cycles/tick

40 mHz320 160 80
44.5356 178 89
50400 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
%