INSERT[DisplayDefs];*Defs file common to DisplayInit and Display

:TITLE[DisplayInit];
*Ed Fiala 3 June 1982

%Jump to DisplayInit from DeviceInit running as DisplayTask. Determine the
terminal configuration from bits 10..14b of the UTVFC ID register:
LF
Star keyboard/LF monitor = 3 (resident);
CSLF
CSL keyboard/LF monitor = 11b (1st overlay); and
CSL
CSL keyboard/CSL monitor = 5 (also 1st overlay).
Initialization for all of these is contained here. When started by Initial,
Star keyboard microcode is resident and CSL keyboard microcode resides in
the next overlay--it overwrites the resident if loaded. Initialization
begins by either loading the overlay into IMX or skipping LoadRAM’s pointers
past the overlay.
When running with Midas, no overlays are in core, and the
microcode is used as loaded. The Star keyboard driver is larger, requiring
a 40b-word table and about 32b more mi on lfKBPage than the CSL keyboard.

The CSLOverlay integer controls assembly of this source to produce the
microcode configuration required. This same initialization is used for
both LF and CSL keyboard drivers and monitors.
Both drivers are built from
Display.Mc which contains conditional assemblies for code specific to a
keyboard; assembled as is, Display.Mc produces code for a CSL keyboard
(either monitor); LFDisplay.Mc changes assembly switches and does
INSERT[Display] to make the Star keyboard configuration. The Star and CSL
keyboard drivers are entirely different, but the code for displaying a
scanline is independent of which monitor is used. Also, HRAM initialization
below is different.

Monitors require a certain time/scanline which 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, visible constant, and blanked area. The UTVFC limits image size of
the image+visible constant to 1024 bits or less. The visible constants would
be setup by initializing the pingpong buffers to some pattern, but this isn’t
done below (i.e., the size of the visible constant is 0).

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 systems
use 77 fields/sec except when videotaping computer sesssions. To obtain
60 Hz rate for videotaping, change lfVSStart to lvVSStart =
[1/28.8 us x 60 Hz] - [808/2] - lfSyncLength - lfBlankLength - 14 =
579 - 404 - 9 - 13 - 14 = 139d = 213b.

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, compatible with the CSL display, by framing 608 visible bits by
equal size blanked areas to the left and right, so the monitors must be
adjusted to be full-screen at 808 scanlines. Pilot also runs the monitors
at 808 scanlines for compatibility.

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=150b, ML should
appear at 150b+264b-number of blanked nibbles following horizontal sync, or
at AAR=434b-number of nibbles following horizontal sync.

Total vert blanking = vVSStart + vSyncLength + vBlankLength + 14d scanlines.
%
*DisplayInitLoc must be odd; DisplayInitLoc to DisplayInitloc+6 are used
**NOTE: These are known to MakeLoaderFile command files.
Set[DpNotifyLoc,Add[DisplayInitLoc,1]];
*7610b must be even
Set[DpContInitLoc,Add[DisplayInitLoc,4]];
*7613
Set[DpOvlSkipLoc,Add[DisplayInitLoc,5]];
*7614
Set[DpSLCountLoc,Add[DisplayInitLoc,6]];
*7615

SetTask[DpTask];

*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];*Start←nibble 44b, ForceAARLoad’←0
vMDS420 ← 177000C, Call[vCRout];
Output[vSLC,vBufStart];**5 mi after Output before PStore4
T ← vDBuf0 ← IncNC&Blank, Call[vQIni];
vMDS420 ← (vMDS420) or (30C);
PStore4[vMDS420,vDBuf0,0];
vMDS ← 0C;*vMDS = vZero, so both are initialized
Input[vTemp,0];*Controller ID code in bits 10:14b

:IF[CSLOverlay]; *******************************

*ID code in bits 10:14b is 3 for LF keyboard with LF display, 11 for CSL
*keyboard with LF display, or 5 for CSL keyboard with CSL display.
LU ← (vTemp) and (140C);
vCnt ← LoA[DpOvlSkipLoc], GoTo[.+3,ALU=0];
*Notify LoadRAM at its continue-loading address to read CSL keyboard overlay.
*LoadRAM reenters at DpNotifyLoc.
***RTemp1 (RM 53) has to be EVEN or be made even here, even though it lies
***within the device table manipulated by Initialize. xBuf to xBuf3 are also
***smashed by LoadRAM ????.
vCnt ← LoA[LRJContinue];
vCnt ← (vCnt) or (HiA[LRJContinue]), Skip;
vMPBr:
vCnt ← (vCnt) or (HiA[DpOvlSkipLoc]);
APCTask&APC ← vCnt;
vMPRet:
IncMPanel, Return;

%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.
%
SetTask[0];

vLPCarry:
Skip[Carry’];
LPhi ← (LPhi) + (400C) + 1;
vAddr7777:
T ← 170000C, Return;

T ← xfTemp, At[DpOvlSkipLoc];*Skip CSL keyboard overlay
LP ← (LP) + T;
vSkpOv:
PFetch1[LP,xfTemp,0];
LP ← (LP) + (3C), Call[vLPCarry];
xfTemp ← (LdF[xfTemp,0,14]) xnor T;*Check address .eq. 7777b
Skip[ALU=0];
GoTo[vSkpOv];

*CSL ucode has been skipped or loaded. Notify display task to continue init.
RTemp ← LoA[DpContInitLoc], At[DpNotifyLoc];
T ← RTemp ← (RTemp) or (HiA[DpContInitLoc,DpTask]);
T ← (APCTask&APC ← RTemp) - T, Call[vMPRet];
*Emulator will continue here, 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[DpSLCountLoc]), Skip[Carry];
RTemp ← HiA[DpSLCountLoc,DpTask], Return;
APCTask&APC ← RTemp, Call[vMPRet];
*Next time emulator runs, pass control back to DeviceInit.
LoadPage[InitPage];
T ← xCNT, GoToP[DI6];

SetTask[DpTask];
:ENDIF; ****************************************

vDBuf0 ← 40000C, At[DpContInitLoc];*to load AAR
vCR ← IncNC&Blank, Call[vNClk];*Increment NClk bit; generate NClk
***Don’t know why next mi is useful
Output[vZero,vHCRam], Call[vNClk];
Output[vDBuf0,vBufStart];*ForceAARLoad’←1, ForceIARLoad’←0
Output[vDBuf0,vLdIAR];
LU ← (vTemp) and (40C);*vTemp[10:14b] = 3 LF, 5 CSL, or 11 LF
vDBA ← 44C, GoTo[vLFInit,ALU=0];*Load HRam, starting at nibble 44

*CSL display 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)
vFieldFill ← cslVSStart, 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 (150b) 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 least 608 of the bits must be data bits
and 352 of bits must be blanked with horizontal sync true; the other 480
bits may be displayed as a constant value or blanked, as chosen, and the
blanked bits may be divided arbitrarily between the left and right parts
of the display. The code below shows 256 nibbles data, 16 nibbles blanking,
and 88 nibbles horizontal sync.
NOTE: Initializing HRam takes about 408*18 = 7344 cycles; during this time,
tasking doesn’t take place, so refresh will not take place.
%
vLFInit:
vFieldFill ← lfVSStart;*Only for LF monitor
:IF[IFG[WordsPerLine!,46,0,1]]; ****************
*This code has the computed parameters for a 50 mhz crystal (i.e.,
*1088-bit forward scan, 352-bit horizontal sync with 608 bits of data):
T ← 336C, Call[vOutNClk0];* 44 - 336 ← 0 (partly redundant)
*ML at 150 + (.5*(416+130)) - 74 = 337
vSLC ← 1C, Call[vOutNClk];*337 ← 1 (ML)
T ← 376C, Call[vOutNClk0];*340 - 376 ← 0
vSLC ← 10C, Call[vOutNClk];*377 ← 10 (SetC)
T ← 72C, Call[vOutNClk0];* 0 - 72 ← 0 (blanking--74b)
vSLC ← 4C;
T ← 222C, Call[vOutNClk];* 73 - 222 ← 4 (Hor. sync--130b)
T ← 316C, Call[vOutNClk0];*223 - 316 ← 0 (blanking--74b)
:ELSE; *****************************************
*This code has the computed parameters for a 50 mhz crystal (i.e.,
*1088-bit forward scan, 352-bit horizontal sync with 1024 bits of data):
T ← 251C, Call[vOutNClk0];* 44 - 251 ← 0 (partly redundant)
*ML at 0 + (.5*(416+130)) - 74 = 337
vSLC ← 1C, Call[vOutNClk];*251 ← 1 (ML)
T ← 376C, Call[vOutNClk0];*252 - 376 ← 0
vSLC ← 10C, Call[vOutNClk];*377 ← 10 (SetC)
T ← 12C, Call[vOutNClk0];* 0 - 12 ← 0 (blanking--13b)
vSLC ← 4C;
***This seems to be insufficient horizontal sync?
T ← 135C, Call[vOutNClk];* 13 - 135 ← 4 (Hor. sync--123b)
T ← 145C, Call[vOutNClk0];*136 - 145 ← 0 (blanking--9b)
:ENDIF; ****************************************
vSwiI:
vMDS177400 ← 177400C;
:IF[CSLOverlay]; *******************************
vDBuf0 ← Add[140000,vStart]C;*ForceAARLoad’←1, ForceIARLoad’←1
vSLC ← 2C, Call[vOutNClk];*317 ← 2 (Switch--end blanking)
*or 45

%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 100 ns units. 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.
%
Output[vDBuf0,vBufStart];
Output[vDbuf0,vLdIAR];**This mi probably useless
vSLC ← (Zero) - 1;
vCR ← AllowWU&Blank, Call[vCRout];
*Count scanlines here until stopped by the emulator.
vTemp1 ← IP[RConstantLo]C;*IOStrobe in 1st mi won’t work
T ← vSLC ← (vSLC) + 1, IOStrobe, GoTo[vIRet];

*Emulator restarts DpTask here
Input[vTemp,0], At[DpSLCountLoc];
T ← (SStkP&NStkP) xor (377C);
StkP ← vTemp1, vTemp1 ← T, NoRegILockOK;
LU ← (vTemp) 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];*136d

*CSL thresholds are now 22d and -89d, respectively.
vCSLThresholds:
vSLC ← (vSLC) + (131C);*69d
LU ← (vSLC) - (157C), GoTo[v40x,ALU>=0];*111d
*See Timer.Mc for the determination of these constants.
v50mhz:
Stack ← HiA[5330];
Stack ← (Stack) or (LoA[5330]);
Stack&+1 ← 130C, GoTo[vSetTimer];*50 mhz crystal (530b)

v40x:
Stack ← HiA[113274], GoTo[v44.5,ALU<0];*44.5 mhz crystal (602b)
Stack ← HiA[12172];
Stack ← (Stack) or (LoA[12172]);
Stack&+1 ← 256C, GoTo[vSetTimer];*40 mhz crystal (656b)

v44.5:
Stack ← (Stack) or (LoA[113274]);
Stack&+1 ← 202C, GoTo[vSetTimer];

vSetTimer:
T ← Stack ← (Stack) or (400C);
StkP ← vTemp1;
:ELSE; *****************************************
vSLC ← 2C, Call[vOutNClk];*272 ← 2 (Switch--end blanking)
vCR ← AllowWU&Blank, Call[vCRout];
:ENDIF; ****************************************

*Set keyboard words 177030-177043 to -1
(177040-177043 unused by CSL keyboard)
T ← vDBuf0 ← (Zero) - 1, Call[vQIni];
T ← vMsgStatus ← 0C;*Task sets up loop
PStore4[vMDS420,vDBuf0,0];
PStore4[vMDS420,vDBuf0,4];
PStore4[vMDS420,vDBuf0,10], Call[.+1];*Call sets up loop

%This code zeroes the two pingpong buffers (not needed unless a white area
*is shown to the right of the active display).
vSLC ← 140000C;
Output[vSLC,vBufStart];
T ← vMsgStatus ← 0C;
Output[vZero,vLdIAR], Call[vClrBf];*IAR ← Start
vButtons ← T, Call[vClrBf];*This call sets up loop
%
*Clear cursor memory (32 bits/scanline x 32 scan lines) because in Alto
*compatible mode, only 16x16 of the hardware’s 32x32 cursor is used.
**NOTE: Blanking must be on here. The final overlay must not overwrite this
**code until it has finished.
vSLC ← 6C;*8 nibbles/scan line
Output[vMsgStatus,vCursor0];
vMsgStatus ← (vMsgStatus) + (4C);
Output[vZero,vCursorMem0];
vSLC ← (vSLC) - 1, GoTo[.-3,R>=0];
vMsgStatus ← (vMsgStatus) + (4000C);
vMsgStatus ← (vMsgStatus) and (174000C), Skip[Carry];
vMDS420 ← (vMDS420) or (20C), Return;
*Wind up here with vMsgStatus .eq. 0
vMDS420 ← 400C, Call[.-1];
vCR ← AllowWU;
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;
IOFetch4[vMDS420,vfCReg,0], Return;*vMDS420 holds 177030b 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:
vDBA ← (vDBA) + 1;
LU ← (RHMask[vDBA]) xor T;
Output[vSLC,vHCRam], Skip[ALU#0];
T ← (Zero) + T + 1, GoTo[vNClk];
IOFetch4[vMDS420,vfCReg,0], GoTo[vOutNClk];

vQIni:
vDBuf1 ← T;
vTemp1 ← T, UseCTask;
vIRet:
vTemp ← T, Return;

%vClrBf:
IOFetch16[vMDS177400,vfBuf0,0];
IOStrobe;
IOFetch16[vMDS177400,vfBuf0,0];
IOFetch16[vMDS177400,vfBuf0,0];
IOFetch16[vMDS177400,vfBuf0,0], Return;
%

:END[DisplayInit];