RastMain.mc
Copyright © 1986, 1987, 1988, 1989 by Xerox Corporation. All rights reserved.
Dave Rumph, May 23, 1989 2:37:19 pm PDT
Title[RastMain.mc.....May 23, 1989 2:30:30 pm PDT...Rumph];
Main part of Dorado raster controller microcode -- included with emulators, but not included with Bootstrap/Initial microcode.
Word Task (WT) microcode.
This microcode is structured to use subtasks, and can run on behalf of
any of A, B, C or D channels of the Raster Controller board set. The
channels are differentiated by SubTask. Thus, for example, references to
aCount (below) apply to aCount, bCount, cCount or dCount, depending on
the what the hardware puts on SubTask.
Because this code uses subtasks, it must ALWAYS Branch to the same place when
blocking, must never test Branch conditions across blocks, and must use
task-specific RBase, RStk, and MemBase values correctly. Note that the
controller is designed to prohibit wakeups while WT is initiated (either
running or preempted) and for at least n instructions after any BLOCK,
where n is approximately 5.
WT microcode wakes up in response to hardware flags set by the LT microcode.
WT does not need to be specially awakened for initialization.
WT will initialize itself EXCEPT that aAddress, bAddress, aCount,
bCount, etc. must have been initialized explicitly by LT, because one cannot
guarantee which subtask will run first, and WTInit is executed only once.
Subtask.0 defines a block of 16 subtask specific registers, of which
5 per subtask are used by WT: aAddress, aCount, aNextCount, aNextAddrLo,
and aNextAddrHi are the subtask-specific registers for Channel A. The other
11 are used by LT in a channel-specific way. There is a similar set for each
other channel. Because the RM is limited and must be channel-specific, the
WT code uses a few tricks to conserve RM. The discussion that follows refers
to, for example, aAddress, to represent all four channels' address register (as does
the actual WT code).
In addition to holding the incremental munch address beyond the beginning of
the scanline, aAddress encodes the channel id (3=A, 2=B, 1=C, 0=D) in the bottom
two bits, which are ignored by IOFetch. Care is taken to avoid clearing those two
bits at the end of a scanline. In addition, the high bit stores state. When it's on,
WT has finished processing that channel's scanline shut off the hardware's wake
request. It can begin processing the next line when it wakes up next.
The WT microcode checks for a NIL pointer to the scanline data, and alerts the
line task (LT) that an unexpected wakeup has occurred. In order to do this, WT
must set one of four bits in an RM location in the LTRegion. Because the SubTask
logic ORs into the address of this write, the LTRegion's bottom two address bits must
already be on to avoid this nasty (in this case) effect.
Set[XTask, IP[WT]];
WTInitPC:
Raster word task initialization.
Subroutine;
DontKnowRBase;
T← WT, CoReturn;
TopLevel;
RBase← RBase[RastRMForA];
MemBase← RastBRForA;
T ← TaskCmd;
TIOA← T;
T← 20C, Block;   * same for all subtasks
WTStart:
Raster word task main loop -- shared by terminal word task.
aCount >=0 here iff data remains to be output for the current scan line.
aCount← (aCount)-1, Branch[WTCheck, R<0];
aAddress← (IOFetch← aAddress)+T, Block, Branch[WTStart];
Fetch the next munch.
WTCheck:
aAddress >= 0 if we just exhausted a scan line.
aAddress < 0 if we have been awakened to start a new scan line.
aAddress← (aAddress) OR (WTBeginLineFlag), Branch[WTEndLine, R>=0];
WTBeginLine:
Starting new scan line. Check for bad wakeup, then copy parameters left by LT. The copy of aCount← aNextCount comes before the bad wake test to prevent aCount (tested at WTStart) from beciming positive by wrapping around.
aCount← aNextCount; * (# Munches to Fetch)-1
T← aNextAddrHi;
PD← T OR (aNextAddrLo);
T← 20C, Branch[WTBadWake, ALU=0];
aAddress← (aAddress) AND (AddressMask); * mask off just channel id
BrHi← aNextAddrHi;
BrLo← aNextAddrLo, Branch[WTStart];
WTEndLine:
T← 1C;
Output← T;
WTEndLineEnd:
T← 20C, Block, Branch[WTStart];
WTBadWake:
BDispatch← aAddress;
RBase← RBase[LTRegion]; * the Dispatch modifies this instruction's next address
T← LTStatus.DBadWake, Branch[WTFinishBadWake], DispTable[4];
T← LTStatus.CBadWake, Branch[WTFinishBadWake];
T← LTStatus.BBadWake, Branch[WTFinishBadWake];
T← LTStatus.ABadWake;
WTFinishBadWake:
Because SubTask is modifying RM addresses on the following write, LTRegion MOD 4 must = 3.
LTStatus← T OR (LTStatus);
T← (WTShutUp), Call[OutputGetsT];
Since the wrong channel is waking up, the normal loop of the WT microcode will never terminate the real requested channel. We need to shut up WT so that LT and the emulator can get back in to report the bad wake.
RBase← RBase[RastRMForA], Branch[WTEndLineEnd];
Line Task (LT) microcode
This microcode handles the full functionality needed for printer and display
applications, but not FIn startup. Nor are requests to AUT to load tables
yet handled.
The Line Task (LT) is responsible for setting parameters in the hardware that
change on a line-by-line basis. LT keeps track, for each of the four channels,
of various data needed to transfer the bitmap, passing some of that information
on the the Word Task (WT) as necessary.
The control information neede by LT is passed to it through a data structure
that begins with a Master Raster Block (MRB) which contains a seal and pointers
to a State Transition Block (STB) chain, three Raster Channel Block (RCB) chains
and a Control Channel Block (CCB) chain. The CCB and RCB's are nearly identical
so for the remainder of this discussion, references to RCB's hold for the CCB
as well. See RasterControllerFace.mesa and RasterControllerHeadDorado.mesa for
the exact details about these data structures. RastLTMicrocode.mesa contains a
Mesa-like implementation of this microcode for documentation purposes.
Because of the design of the hardware and the interaction with the software, LT
must be aware of three lines, and hence potentially three RCB's, during the same
scanline. Consider the case where all scanlines are pointed to by different
RCB's at an LT wakeup during the display of scanline n:
The data for scanline n has finished being transferred to the hardware by WT, so
the software can by told that the bitmap for scanline n is no longer needed.
The scanline being transfered by WT during this LT wakeup will be displayed next
line - it is scanline n+1. LT needs to load the Next Line Control Block (NLCB)
with certain parameters for that line: where to start reading out of the hardware
buffer RAM, whether there is data on the line, etc.
LT must also pass to WT the parameters it needs to transfer line n+2, if
necessary, as well as set the bit in the NLCB that wakes WT to transfer the next
line.
So LT needs to be aware of some aspects of line N, N+1 and N+2 during any one
wakeup. During the following wakeup, LT will process more information for line
N+1 and N+2 and begin N+3. To minimize the amount of data stored across that
block, the order of processing is important. The microcode finishes the
bookeeping for line N, sets up the parameters for line N+1 in the NLCB, leaving
the control information in the RM aCCRTemp, then leaves the WT parameters in RM,
sets the ChannelWantsWT bit as needed in aCCRTemp and sends it to the NLCB.
It is also important to keep track, for each channel, of the state of the RCB
chain. This state, the "lineState", is encoded as four bits per channel in the
LTLineState word in RM. The low three bits encode the state itself, and are the
index of an 8-way (nominally) BDispatch. The high bit is used as a flag to cause
LT to store the status, completing the handshake with the software.
The LTLineState word is right-cycled 4 bits to refer to the state information of a
different channel. Since each channel uses a different RMRegion, LTLineState is
stored in Q temporarily during that portion of LT's wakeup. A picture of
LTLineState:
---------------------------------------------------------------------------------
| A flg | D state | D flg | C state | C flg | B state | B flg | A state |
| (1 bit) | (3 bits) | (1 bit) | (3 bits) | (1 bit) | (3 bits) | (1 bit) | (3 bits) |
---------------------------------------------------------------------------------
This arrangement is used because the Dorado allows for easy dispatching on the
low three bits and easy testing of the high bit.
Set[XTask, IP[LT]];
LTInitPC:
Raster line task initialization
Subroutine;
DontKnowRBase;
T← LT, CoReturn;
TopLevel;
Nop;  * for placement
RBase← RBase[LTRegion];
MemBase← LTBR;
Zero the NLCB to prevent bad WT wakes
T← SelCmd;
T← A0, TIOA← T, Call[OutputGetsT];
TIOA[DataCmd];
LTSavedCnt← Cnt;
T← 20C;
T�, Cnt← T;
Output← T, Branch[., Cnt#0&-1];
Shut up the hardware wakeups
TIOA[TaskCmd];
T← RastAllShutUp, Call[OutputGetsT]; * clears SubTask
LTIndexToStore← T← A0, Call[OutputGetsT]; * reenable the task wakeups
LTStatus← A0;        * initial state
LTLineState← AllFirstTime;    * first time, don't store status
LTSTBLines← T-T-1;
Begin processing the MRB
T← T+1;          * already checked the seal
T← (Fetch← T)+1;
LTNextSTBAddrLo← MD, T← (Fetch← T)+1;
LTNextSTBAddrHi← MD, T← (Fetch← T)+1;
LTScratch← nChannels;
Cnt← LTScratch;
LTScratch← AChannelRBase;
InitChannelLoop:
Initialize some parameters for this channel
RBase← LTScratch;       * loaded from [14:17]
KnowRBase[RastRMForA];     * but really each region in parallel
Initialize for WT (which can't do it itself for all four channels)
aAddress← Cnt;
aAddress← (aAddress) OR (WTBeginLineFlag);
aCount← T-T-1;
aNextCount← T-T-1;
aNextAddrHi← A0;
aNextAddrLo← A0;
aNextRCBAddrLo← MD, T← (Fetch← T)+1;
aNextRCBAddrHi← MD, T← (Fetch← T)+1;
Initialize some LT parameters for this channel
aCCRTemp← InitialCCR;     * 17000C
aLineRepeatCount← T-T-1;
aRCBLineRepeatCount← T-T-1;
aLines← T-T-1;
aStatus← (BadStatus);
RBase← RBase[LTRegion];
LTScratch← (LTScratch)+1, Branch[InitChannelLoop, Cnt#0&-1];
Initialize the Word Task (WT)
LTFlags← MD, T← (LTStatusAddr);
Store← T, DBuf← (0C);       * reset the LT error status
TaskingOff, Call[WTInitPC];
LdTPC←T, Wakeup[WT];
TaskingOn, Branch[.+2];
LTStartLine:
LT wakes up here for each scanline
LTSavedCnt← Cnt;
LTSavedQ← Q;
T← (LTLineState) AND (StateMask);   * check D Channel for New RCB
PD← T XOR (NewRCB);
TIOA[DataCmd], Branch[LTStartLine1, ALU#0];
Output← LTCursorCtl;       * NLCB Address should be set correctly
Output← LTModeCtl;       * from the last time through the scanline
Output← LTHalftoneCtl;      * loop.
Output← LTSelCtl;
LTStartLine1:
PD← LTSTBLines;        * any lines left in this STB?
T← LTNextSTBAddrHi, Branch[LTStartLine2, ALU>=0];
PD← (LTNextSTBAddrLo) OR (T);
T← A0, Branch[LTStartLine3, ALU=0];
BRHi← LTNextSTBAddrHi;      * need the next STB element
BRLo← LTNextSTBAddrLo;
T← (Fetch← T)+1;
LTScratch← (GoodSTBSeal);
PD← (LTScratch) XOR (MD);
T← (Fetch← T)+1, Branch[BadSTBSeal, ALU#0];
LTSTBLines← MD, T← (Fetch← T)+1;
TIOA[SelCmd];         * prepare to transfer 20b STB states to NLCB
LTScratch← (STBStateNLCB);
Output← LTScratch;
TIOA[DataCmd];
LTScratch← nStates;
Cnt← LTScratch;
STBStatesLoop:
Output← MD;
T← (Fetch← T)+1, Branch[STBStatesLoop, Cnt#0&-1];
TIOA[SelCmd];         * prepare to transfer 20b STB counts to NLCB
LTScratch← (STBCountNLCB);
Output← LTScratch;
TIOA[DataCmd];
LTScratch← nStates;
Cnt← LTScratch;
STBCountsLoop:
Output← MD;
T← (Fetch← T)+1, Branch[STBCountsLoop, Cnt#0&-1];
LTNextSTBAddrLo← MD, Fetch← T;
LTNextSTBAddrHi← MD;
BRHi← LTMRBAddrHi;
BRLo← LTMRBAddrLo;
LTStartLine2:
LTSTBLines← (LTSTBLines)-1, Branch[LTStartLine3];
BadSTBSeal:
LTStatus← (LTStatus) OR (LTStatus.badSTBSeal);
BRHi← LTMRBAddrHi;
BRLo← LTMRBAddrLo;
LTStartLine3:
LTScratch← nChannels;
Cnt← LTScratch;
T← A0, TIOA[SelCmd], Call[OutputGetsT]; * Kill the LT wakeup
TIOA[DataCmd];
LTScratch← AChannelRBase;
LTChannelLoop:
LTLineState← RCy[LTLineState, LTLineState, 4]; * Get the next channel's data
Q← LTLineState;
RBase← LTScratch;          * loaded from [14:17]
KnowRBase[RastRMForA];        * but really parallel regions
BRLo← aNextRCBAddrLo;
BRHi← aNextRCBAddrHi;
PD← Q;
Branch[LTThisLine, ALU>=0];
Finish off line N by storing the status if necessary
T← (aStatusToStore) OR (RCBComplete);
aStatusToStore← StatusAddr;       * Trick: reuse RM as temp
DBuf← T, Store← aStatusToStore;
T← protoFinishedRCB, Call[LTPrepareNotify];
T← ClearStoreStatus;
T← (Q) AND (T);
Q← T;
LTThisLine:
Do the right thing depending on the line state for this channel -- dispatch
on linestate (stored temporarily in Q)
T← aIndexIntoBuffer, BDispatch← Q;
LTIndexToStore← T;* Address for BDispatch to modify
Branch[LTNextLine], DispTable[5];     * case FirstTime
aStatusToStore← aStatus, Branch[LTNewLine];  * case NewRCB
PD← aStatus, Branch[LTNewLine];     * case NewLine
T← DontSwapBuffers, Branch[LTRepeatingLine]; * case RepeatingLine
aCCRTemp← aCCR, Branch[LTNextOutput];   * case InactiveChannel
LTNewLine:
aCCRTemp← aCCR, DblBranch[LTNextLine, LTTurnItemOn, ALU>=0];
LTRepeatingLine:
aCCRTemp← (aCCR) OR (T);
(aStatus), Branch[LTNextLine, R>=0];
LTTurnItemOn:
aCCRTemp← (aCCRTemp) OR (ItemOnThisLine);
LTNextLine:
Start working on line N+1
T← ClearStateMask;
T← (Q) AND (T);
Q← T, PD← A← aLineRepeatCount;
Branch[LTNextNew, ALU<0];
T← (T) OR (RepeatingLine);
Q← T, aLineRepeatCount← (aLineRepeatCount)-1;
Output← aCCRTemp, Branch[LTEndScanLine];
LTNextNew:
PD← aLines;
T← (T) OR (NewLine), Branch[LTEndRCB, ALU<0];
Q← T;
aLineRepeatCount← aRCBLineRepeatCount;
aLines← (aLines)-1;
PD← aStatus;
T← (aCCRTemp) OR (ChanWantsWT), Branch[LTNextOutput, ALU>=0];
Output← T, T← A← aWordsPerLine;
ANextAddrLo← (ANextAddrLo) + (T), Branch[LTNextNewBack, ALU<0];
ANextAddrHi← A← ANextAddrHi, XorSavedCarry, Branch[LTEndScanline];
LTNextNewBack:
ANextAddrHi← (ANextAddrHi)-1, XorSavedCarry, Branch[LTEndScanline];
LTEndRCB:
T← (NewRCB);
T← (Q) OR (T);
Q← T;
PD← (aStatus) XOR (BadStatus);
T← (NextRCBAddrAddr), Branch[LTNextNewRCB, ALU=0];
aNextRCBAddrLo← SetStoreStatus;
aNextRCBAddrLo← (Q) OR (aNextRCBAddrLo);   * Using RM as a temp here
Q← aNextRCBAddrLo, T← (Fetch← T)+1;     * since it's being loaded next
aNextRCBAddrLo← MD, Fetch← T;
aNextRCBAddrHi← MD;
LTNextNewRCB:
T← aNextRCBAddrLo;
PD← (aNextRCBAddrHi) OR T;
T← ClearStateMask, Branch[LTMakeInactive, ALU=0];
BRLo← aNextRCBAddrLo;
T← A0, BRHi← aNextRCBAddrHi;       * RCBSealAddr is 0
T← (Fetch← T)+1;
aLines← GoodRCBSeal;          * Trick: use RM as temp
PD← (MD) XOR (aLines);
Branch[BadRCBSeal, ALU#0];
T← (Fetch← T)+1;
PD← MD;
T← (Fetch← T)+1, Branch[LTNextNewRCB1a, ALU#0];
PD← MD;
Branch[LTNextNewRCB1, ALU#0];
T← ClearStateMask, Branch[LTMakeInactive];   * for MicroD placement
BadRCBSeal:
T← protoBadRCBSeal;         * for MicroD placement
Call[LTPrepareNotify];
T← ClearStateMask;
LTMakeInactive:
aNextAddrHi← A0;           * so WT can check for bad wakes
aNextAddrLo← A0;
T← (Q) AND (T);
T← (T) OR (InactiveChannel);
Q← T, Branch[LTNextOutput];
LTNextNewRCB1:
nop;
LTNextNewRCB1a:
T← (Fetch← T)+1;
aLines← MD, T← (Fetch← T)+1;
aRCBLineRepeatCount← MD, T← (Fetch← T)+1;
aLineRepeatCount ← aRCBLineRepeatCount;
aCCR← MD, T← (Fetch← T)+1;
PD← aStatus← MD;
Branch[.+2, ALU<0];          * buffer pointer valid?
Branch[LTNextOutput];         * for MicroD placement
T← (Fetch← T)+1;
aNextAddrLo← MD, T← (Fetch← T)+1;
aNextAddrHi← MD, T← (Fetch← T)+1;
aNextCount← MD, T← (Fetch← T)+1;
aWordsPerLine← MD, T← (Fetch← T)+1;
aIndexIntoBuffer← MD;
aCCRTemp← (aCCRTemp) OR (ChanWantsWT);
LTNextOutput:
Output← aCCRTemp;
LTEndScanline:
RBase← RBase[LTRegion];
Output← LTIndexToStore;
LTLineState← Q;
LTScratch← (LTScratch)+1, Branch[LTChannelLoop, Cnt#0&-1];
If NewRCB, process CCB
LTScratch← LTLineState;
LTScratch← (LTScratch) AND (StateMask);
PD← (LTScratch) XOR (NewRCB);
T← CursorCtlAddr, Branch[LTEnd, ALU#0];
T← (Fetch← T)+1;
LTCursorCtl← MD, T← (Fetch← T)+1;
LTModeCtl← MD, T← (Fetch← T)+1;
LTHalftoneCtl← MD, Fetch← T;
LTSelCtl← MD;
LTEnd:
BRHi← LTMRBAddrHi;
BRLo← LTMRBAddrLo;
T← LTStatusAddr;
T← (Fetch← T)-1;
PD← (LTStatus) XOR MD; * see if the status changed
T← (Fetch← T)+1, Branch[LTEnd1, ALU=0]; * get the interrupt mask, preparing to notify
Store← T, DBuf← LTStatus;
RBase← RBase[NWW];
NWW← (NWW) OR MD, Reschedule;
RBase← RBase[LTRegion];
LTEnd1:
Cnt← LTSavedCnt;
Q← LTSavedQ, Block, Branch[LTStartLine];
LTPrepareNotify:
We need to XOR the bit in T, shifted left according to LTScratch[16:17], into LTStatus, without touching anything else. We XOR so that each transition can be noticed without the need for a handshake back from the software that the previous notify was caught. Note, however, that if the software is slow enough in responding that the microcode can change the same bit twice, the software won't see it at all. For that reason, WT ORs into the badWake bits, rather than XORs, since bad wakeups indicate hardware failure and are less well behaved in when they occur, it is much more important that the software see at least one than it know how many occurred.
Subroutine;
RBase← RBase[LTRegion];
LTScratch← Cnt;
Branch[.+2, Cnt=0&-1];
T← LSh[T, 3], Branch[.-1];
LTStatus← (LTStatus) XOR (T);
Cnt← LTScratch;
LTScratch← (LTScratch) XOR (AddressXOR);
RBase← LTScratch, Return;