:IF[WithTOR]; ************************************

TITLE[No.UTVFC.in.TOR];

:ELSE; *******************************************

Set[LFMonitor,1];
*Avoid error when inserting DisplayDefs.
INSERT[DisplayDefs];
*Defs file common to DisplayInit and Display

TITLE[DisplayInit];
*Ed Fiala 5 May 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 1088 bits forward scan and
352 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 25 percent of all cycles at 77 fields/sec compared to 20 percent at
65 fields/sec, which improves the emulator’s machine share from about 74
percent to 78 percent (= 6 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 1088 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, 1088 bits (= 420b nibbles) are in forward scan, 352
bits (= 130b 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
*Suspicious that the nibble counter is only cleared when AllowWU is false,
*so do this output twice.
vSLC ← 104400C, Call[vCRout];
vKeyboardHi ← 0C, Call[vCRout];
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];
vKeyboard ← HiA[KeyboardAddress];
vKeyboard ← (vKeyboard) or (LoA[KeyboardAddress]);
PStore4[vKeyboard,vDBuf0,0];**5 mi after the Output above
vSLC ← LoA[Task0NotifyLocA];
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];
vSLC ← Add[140000,15000]C;
vCSBHi ← 0C, GoTo[vSwiI];

%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 = 1088+352, where at most 1024 bits are visible, 352 must be
blanked with horizontal sync true, and the other 64 bits are blanked, divided
arbitrarily between the left and right parts of the display. The code below
sets up 256 nibbles data, 16 nibbles blanking, and 88 nibbles horizontal sync.
%
vLFInit:
vCSBHi ← 0C;
T ← 251C, Call[vOutNClk0];* 44 - 251 ← 0
vSLC ← 1C, Call[vOutNClk];*252 ← 1 (ML .eq. midline)
T ← 376C, Call[vOutNClk0];*253 - 376 ← 0
vSLC ← 10C, Call[vOutNClk];*377 ← 10 (SetC--start blanking)
T ← 12C, Call[vOutNClk0];* 0 - 12 ← 0
vSLC ← 4C;
T ← 135C, Call[vOutNClk];* 13 - 135 ← 4 (Hor sync)
T ← 145C, Call[vOutNClk0];*136 - 145 ← 0
vSLC ← 2C, Call[vOutNClk];*272 ← 2 (Switch--end blanking)

%HRam has just been initialized to 652 forward and 104 reverse = 756
bits/scanline for CSL monitors or to 1088 forward and 352 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, 1416
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 4 every 2560 cycles). The default value is 320,
representing 1 ticks/640 cycles at 40 mHz processor clock, equivalent to
1 tick/64 microseconds. Currently envisioned values are as follows:
64012802560cycles/tick
40 mHz320 160 80
44.5356 178 89
50400 200 100
The value in vCrystal can be read by the READR Misc opcode.
%

vSLC ← Add[140000,0]C;
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) - (210C), DblGoTo[v50mhz,v40x,ALU<0];*136d

*CSL thresholds are now 22d and -89d, respectively.
vCSLThresholds:
LU ← (vSLC) + (131C);*89d
LU ← (vSLC) - (26C), GoTo[v40x,ALU>=0];*22d
*Store 2 x processor crystal speed in vCrystal.
v50mhz:
vCrystal ← 310C, GoTo[vSetTimer];*50.0 mhz crystal (400d/2)

v40x:
vCrystal ← 262C, Skip[ALU<0];*44.5 mhz crystal (356/2)
vCrystal ← 240C, GoTo[vSetTimer];*40.0 mhz crystal (320/2)
vSetTimer:
vCrystal ← LSh[vCrystal,1];
T ← vDBuf0 ← 0C, Call[vQIni];
vCSB ← ZeroDataBuffer;*Using vCSB as temporary BR here
*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?)
PStore4[vCSB,vDBuf0,0];
PStore4[vCSB,vDBuf0,4];
PStore4[vCSB,vDBuf0,10];
PStore4[vCSB,vDBuf0,14];
T ← vMsgStatus ← 0C, Call[.+1];*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.
vCSB ← (vCSB) or (LoA[CSBAddress]), GoTo[vAddr7777];
*Wind up here with vMsgStatus .eq. 0
vCSB ← HiA[CSBAddress], Call[.-1];
*Set keyboard words 177034-177047 to -1
(177044-177047 unused by CSL keyboard)
T ← vDBuf0 ← (Zero) - 1, Call[vQIni];
vCR ← AllowWU;
PStore4[vKeyboard,vDBuf0,0];*Buttons at vKeyboard+3
PStore4[vKeyboard,vDBuf0,4];*KB0-KB3
PStore4[vKeyboard,vDBuf0,10];*KB4-KB7
*
vMouseDx ← T ← 0C;*Not initializing these seems harmless
*
vMouseDy ← 0C;
*
vMsg ← 0C;
T ← vMsgStatus ← 0C;
vKeyBuffer ← T, LoadPage[DisplayPage];
vButtons ← T, 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 vKeyboard quadword contains IncNC&Blank in
IOFetch4[vKeyboard,vfCReg,0], Return;*all four words here

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[vKeyboard,vfCReg,0], GoTo[vOutNClk];

vQIni:
vDBuf1 ← T;
vDBuf2 ← T, UseCTask;
vDBuf3 ← T, Return;

%
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
%

END[DisplayInit];
:ENDIF; ******************************************