*-----------------------------------------------------------
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];