:TITLE[Display];

*Definitions for Alto-compatible UTVFC
*Registers and constants used by UTVFC task
SETTASK[DpTask];

*Output Registers
SET[vCReg,0]; *Control register
SET[vBufStart,1]; *Buffer Starting Address
SET[vHCRam,2]; *Horizontal control ram
SET[vLdIAR,3]; *IAR[0:5] ← Start[0:5] (data is ignored)
SET[vCursor0,4]; *Channel 0 Cursor control register
SET[vCursor1,5]; *Channel 1 Cursor control register
SET[vCursorMem0,6]; *Channel 0 Cursor Memory
SET[vCursorMem1,7]; *Channel 1 Cursor Memory
SET[vBuf0,10]; *Channel 0 data buffer - use with Output
SET[vfBuf0,ADD[LSHIFT[DpTask,4],10]]; *Channel 0 data buffer - use with IOFetch
SET[vBuf1,11]; *Channel 1 data buffer
SET[vfBuf1,ADD[LSHIFT[DpTask,4],11]];
SET[vBuf2,12]; *Channel 2 data buffer
SET[vfBuf2,ADD[LSHIFT[DpTask,4],12]];
SET[vBuf3,13]; *Channel 3 data buffer
SET[vfBuf3,ADD[LSHIFT[DpTask,4],13]];

SET[vRB,LSHIFT[AND[DpTask,3],4]]; *enforces register allocation conventions

RV[vDBuf0,ADD[vRB,0]]; *doubleword data buffer
RV[vDBuf1,ADD[vRB,1]];

RV[vDBA,ADD[vRB,2]]; *used when picking up a DCB
RV[vSize, ADD[vRB,2]]; *used when transmitting a DCB

RV[vSLC,ADD[vRB,3]];
RV[vBase,ADD[vRB,4]];
RV[vBase1,ADD[vRB,5]];
RV[VCR,ADD[vRB,6]]; *Copy of hardware control register
RV[vCursorY,ADD[vRB,7]];
RV[vTemp,ADD[vRB,10]];
RV[vCurrentY,ADD[vRB,11]]; *Current display Y
RV[vCursorControl,ADD[vRB,12]];
RV[vMsg,ADD[vRB,13]];
RV[vLink,ADD[vRB,14]]; *holds DCB link word
RV[vNWrds,ADD[vRB,15]];
RV[vCnt,ADD[vRB,16]]; *NOTE: because this register must be different from vMDS,
*the display cannot run at Task 3 mod 4.
RV[vZero,ADD[vRB,17]]; *Register containing zero

RV[vMDS,76]; *base register containing 400b
RV[vMDShi,77]; *base register containing MDShi

MC[vMaxYh, 400]; *number of visible scan lines per field/2 -1
MC[vMaxYl, 223];

MC[vBlankLength,104]; * 2* vertical blanking time in scan lines (field A’s time is increased by 1)
MC[fAVSStart,77];
MC[fBVSStart,100];
MC[vSyncLength,40];

SET[msgTable,ADD[lshift[DisplayPage,10],20]];
SET[IZTab,ADD[lshift[DisplayPage,10],40]]; *dispatch table for initial zeros
SET[FZTab,ADD[lshift[DisplayPage,10],60]]; *dispatch table for final zeros
SET[FZTab2,ADD[lshift[DisplayPage,10],100]]; *secondary dispatch table for final zeros

*Initialization for UTVFC

SETTASK[DpTask];
ONPAGE[DisplayInitPage];

*Get here from DeviceInit. We are running at the display’s task level.

displayinit: vMDS ← 177000c, AT[DisplayInitLoc];
vCR ← 0c;
Output[vCR,vCursor0];*Disable the cursor
vMDShi ← 0c; *is this done somewhere else? if so, it can be removed
vMDS ← (vMDS) or (31c);*set 177037-177030 to -1
vSLC ← T ← 6c;
vCR ← (zero) -1;
dlcloop:
nop;
PStore1[vMDS,vCR];
vSLC ← T ← (vSLC) -1, goto[dlcloop,R>=0];
vCR ← 1c;
Output[vCR,vCReg];*Clear the Nibble counter
vCR ← 200c;*Increment NClk bit
vSLC ← 104400c;*Start ← 9d (nibble 36d), ForceAARLoad’ ← 0
Output[vSLC,vBufStart], call[NClk];*Generate NClk to load AAR
vSLC ← 0c;
Output[vSLC,vHCRam], call[NClk];*HCRam[36d] ← 0. NClk does HCReg ← 0, AAR←9d.
vSLC ← 55000c;
vMDS ← 400c; *Initialization
Output[vSLC,vBufStart]; *Clear ForceAARLoad, set ForceIARLoad, Start ← 26d (= 64 - 38)
Output[vSLC,vLdIAR];*initialize IAR
vSLC ← 155000c;
Output[vSLC,vBufStart];*Clear ForceAARLoad, ForceIARLoad
vDBA ← 44c;*Now we can load the entire ram, starting at location 36d.
vSLC ← 0c;
T ← 272c, call[OutNClk];*Load pattern in vSLC until address = value in T
vSLC ← 1c;
T ← 273c, call[OutNClk];*172d
vSLC ← 0c;
T ← 376c, call[OutNClk];*254d
vSLC ← 10c;
T ← 377c, call[OutNClk];*255d
vSLC ← 0c;
T ← 4c, call[OutNClk];
vSLC ← 4c;
T ← 36c, call[OutNClk];
vSLC ← 0c;
T ← 44c, call[OutNClk];*36d
vSLC ← 2c;
T ← 45c, call[OutNClk];*37d

vSLC ← 0c;*set up to clear Cursor memory
vCR ← 22c;*Allow display wakeups, set blanking
Output[vCR,vCReg];
vZero ← 0c, call[vRet];

InitCLoad: *First wakeup comes here. Clear the cursor memory (requires 32 scan line times, 8 nibbles/line).
vTemp ← 6c;
ICLoop:
Output[vSLC,vCursor0];*Send address
vSLC ← (vSLC) + (4c);
Output[vZero, vCursorMem0];*Send data (zero)
vTemp ← (vTemp) - 1,IOStrobe, goto[ICLoop, R>=0];
vSLC ← (vSLC) + (4000c);
vSLC ← (vSLC) and (174000c), goto[.+2, carry];
vRet:
return;
loadpage[DisplayPage];
vCurrentY ← 1c,gotop[vFirstWakeup];

*SUBROUTINE NClk generates one NClk.
NClk:
nop;*let the caller’s R write occur
Output[vCR,vCReg];*four outputs generate one NClk
Output[vCR,vCReg];
Output[vCR,vCReg];
Output[vCR,vCReg];
usectask, goto[Vret];

*SUBROUTINE OutNClk sends the pattern in vSLC to the HCRam until the address = T.
OutNClk:
Output[vSLC, vHCRam];
Output[vCR,vCReg];
Output[vCR,vCReg];
Output[vCR,vCReg];
Output[vCR,vCReg];
vDBA ← (vDBA)+1;
vDBA ← (vDBA) and (377c);
lu ← (vDBA) xor (T);
goto[OutNClk, ALU#0], usectask;
return;

*Task microcode for Alto-compatible UTVFC
SETTASK[DpTask];
ONPAGE[DisplayPage];


*Get here when the current DCB is exhausted. A branch on vCurrentY is pending.
*vChkMsg has been called, and IOStrobe has been sent to the controller.
GetNextDCB:
T ←(vLink) - (376c), goto[FieldDoneA, ALU=0]; *Testing vCurrentY
*Enter here from StartFrame
GetNextDCBx:
PFetch2[vMDS,vDBA];*spurious fetch from location 2 if vLink=0
lu ← vLink;
T ← (vLink) - (400c), goto[GotAnotherDCB, ALU#0];
vSLC ← vSLC; *interlock
vSLC ← (10000c), call[vMsgRet];
goto[WidthZero];

GotAnotherDCB:
PFetch2[vMDS, vLink];
vSLC ← (vSLC) - 1, goto[LongDCB, R<0]; *bias the scan line count
T ← vDBA;
vBase ← T, TASK;
T ← vMDShi;*Set up high base register
vBase1 ← T;
ChkBkgnd:
lu ← ldf[vNWrds,1,1];
lu ← ldf[vCR,15,1],dblgoto[vBB,vWB,ALU#0]; *Field bit

LongDCB:
T ← (RSH[vMDS,6]) + (T);*Point at long pointer (-400), T← 4+T
PFetch2[vMDS,vBase], TASK;*Fetch long ptr
vSLC ← (vSLC) XOR (100000c);*Turn off long DCB flag
T ← (vBase1) AND (377c);*Fix up long pointer
T ← vBase1← (LSH[vBase1,10]) + (T) + 1;
vBase1 ← (FixVa[vBase1]) OR (T) , goto[ChkBkgnd];

vBB:
vCR ← (vCR) or (100c), dblgoto[OddField,EvenField,ALU#0];
vWB:
vCR ← (vCR) and not (100c), dblgoto[OddField,EvenField,ALU#0];

OddField:
lu ← ldf[vNWrds,10,10], goto[EvF1]; *Data is sent from the base of the block

EvenField:
T ← ldf[vNWrds,10,10]; *Data is sent starting NWrds into the block
*<Do not do FreezeResult until UTVFC is fixed>
vBase ← (vBase) + (T), FreezeResult;
vBase ← (vBase) + (T), goto[OddField]; *previous instruction is replaced with this one..

EvF1:
T ← ldf[vNWrds,3,5], goto[WidthNonZero,ALU#0]; *check tab count

%
Here, we will calculate some quantities that will be useful to the main loop. They are:
vNWrds: TYPE = MACHINE DEPENDENT RECORD [
TabCountIsZero: [0..1], --means TabCount is uninteresting
fill: [0..3], --unused bits
TabCount: [0..37B], --number of zeros to send before sending memory data
BaseAdjust: [0..377B], --number of words to add to vBase to get to the start
of the next scan line’s bitmap. Not used if DCB.Width = 0. This quantity will usually
be DCB.Width, unless the total number of words specified by the user exceeds 38d, in
which case it will be reduced.
];

vSize: TYPE = MACHINE DEPENDENT RECORD [
FinalFillIsZero: [0..1], --means FinalFill is uninteresting
fill: [0..1], --unused
FinalFill: [0..77B], --number of zeros to fill out the scan line
DataCount: [0..177B], --number of doublewords to send to the controller from bitmap
ZeroWidthDCB: [0..1], --flag bit indicating that no data is to be sent from memory. See
below
];

A zero-width DCB is a special case. We set ZeroWidthDCB ← 1, DataCount ← 3, FinalFill ← 46B.
Also, TabCountIsZero ← 1, TabCount ← 0, BaseAdjust ← 0.
The main loop will send two scan lines of zeros (to fill the two ping-pong buffers),then
stop sending data to the controller until the next DCB.
%

WidthZero:
vSize ← 3c;
vSize ← (vSize) or (23000c);
Output[vCR,vCReg]; *Load the control register
vNWrds ← 100000c, goto[PreDS];

WidthNonZero: vSize ← 46c, goto[TabNonZero,ALU#0];
TabZero:
vNWrds ← (vNWrds) or (100000c); *Set the TabZero flag
T ← rhmask[vNWrds], goto[SetUpSize]; *Want to send this many words

TabNonZero:
T ← (rhmask[vNWrds]) + (T); *Want to send this many words
vNWrds ← (vNWrds) and not (100000c); *clear bit 0 in case the user said half resolution
SetUpSize: vSize ← (vSize) - (T); *subtract desired number of words from 46b
T ← rhmask[vNWrds], goto[ScanOvf, ALU<0]; *check for fit
vSize ← (lsh[vSize,10]) or (T), goto[EndSize]; *it fits

ScanOvf:
vSize ← (vSize) + (T), goto[vSizeEven, REven]; *vSize ← (46b - TabCount)
vSize ← (vSize) + (400c); *One final zero if TabCount is odd
vSize ← (vSize) and not (1c); *force vSize to be even
vSizeEven:
vNWrds ← (vNWrds) + (T); *vNWrds[10:17] ← 2*NWrds
T ← (vSize) and (376c);
vNWrds ← (vNWrds) - (T), goto[EndSize];

*At this point, vNWrds has TabCount in bits 3-7 , bit 0 is set if TabCount = 0.
*vNWrds[10:17] contains the amount that must be added to vBase at the end of
*a scan line to get to the start of the next scan’s data. If no overflow
*occurred, this is just the original value. On overflow, it is
*NWrds + (Nwrds - (46b - TabCount)), i.e.
*NWrds + (Amount we should have sent - (Amount actually sent))

EndSize:
lu ← lhmask[vSize]; *check for final fill count = 0
Output[vCR,vCReg], skip[ALU#0]; *load the control register
vSize ← (vSize) or (100000c); *Set FinalFillIsZero
T ← 177000c, call[vMsgRet]; *displacement for Initial zero memory references
dispatch[vNWrds,4,4], dblgoto[LineWake2,LineWake1,R<0];


*Get here from end of scan line processing.
NoCursor: vSLC ← (vSLC) - 1;
NoCursor1: IOStrobe, vCurrentY ← (vCurrentY) - 1, goto[GetNextDCB, alu<0];
T ← rhmask[vNWrds], goto[FieldDone, alu=0];
vBase ← (vBase) + (T), call[CheckBankCross];

*Wake up here at the start of all scan lines of a DCB after the first.
LineWake:
dispatch[vNWrds,4,4], skip[R<0];
LineWake1:
disp[IZ00]; *Tab if necessary
LineWake2:
T ← (vSize) and (376c), goto[SendFromMem,Reven];
ZeroWidthLine: vSize ← (vSize) - (2c), skip[ALU=0];
PreDS:
T ← 177000c, goto[DataSent]; *send zeros to the controller
vSize ← 1c, goto[CallChkMsg]; *The two scan lines of zeros have been sent

*Dispatch Table for initial zero memory references.
IZ00: IOFetch16[vMDS,vfBuf0], goto[LineWake2], AT[IZTab,00];
IZ01: Output[vZero,vBuf0], goto[ICheck16], AT[IZTab,01];
IZ02: Output[vZero,vBuf0], goto[IZ01], AT[IZTab,02];
IZ03: Output[vZero,vBuf0], goto[IZ02], AT[IZTab,03];
IZ04: IOFetch4[vMDS,vfBuf0], goto[ICheck16], AT[IZTab,04];
IZ05: IOFetch4[vMDS,vfBuf0], goto[IZ01], AT[IZTab,05];
IZ06: IOFetch4[vMDS,vfBuf0], goto[IZ02], AT[IZTab,06];
IZ07: IOFetch4[vMDS,vfBuf0], goto[IZ03], AT[IZTab,07];
IZ10: IOFetch4[vMDS,vfBuf0], goto[IZ04], AT[IZTab,10];
IZ11: IOFetch4[vMDS,vfBuf0], goto[IZ05], AT[IZTab,11];
IZ12: IOFetch4[vMDS,vfBuf0], goto[IZ06], AT[IZTab,12];
IZ13: IOFetch4[vMDS,vfBuf0], goto[IZ07], AT[IZTab,13];
IZ14: IOFetch4[vMDS,vfBuf0], goto[IZ10], AT[IZTab,14];
IZ15: IOFetch4[vMDS,vfBuf0], goto[IZ11], AT[IZTab,15];
IZ16: IOFetch4[vMDS,vfBuf0], goto[IZ12], AT[IZTab,16];
IZ17: IOFetch4[vMDS,vfBuf0], goto[IZ13], AT[IZTab,17];

ICheck16: lu ← ldf[vNWrds,3,1]; *there may be one 16-word block remaining
skip[alu=0];
IOFetch16[vMDS, vfBuf0];
T ← (vSize) and (376c), goto[SendFromMem];


*Start of transmission of data from memory.
*T has the number of words to send (known to be even and nonzero).
SendFromMem: vCnt ← T;
lu ← ldf[vBase,16,1]; *check for doubleword required
lu ← ldf[vBase,14,2], goto[NLQuads, alu=0];
PFetch2[vBase,vDBuf0,0];
vBase ← (vBase) + (2c);
vCnt ← (vCnt) - (2c);
Output[vDBuf0, vBuf0];
Output[vDBuf1, vBuf0];
NLQ1:
lu ← ldf[vBase,14,2];

NLQuads:
vCnt ← (vCnt) - (4c), goto[NLHex, alu=0];
skip[alu>=0];
PFetch2[vBase,vDBuf0,0], goto[NL4]; *0 or 2 words to go
IOFetch4[vBase,vfBuf0,0];
vBase ← (vBase) + (4c), goto[NLQ1];

*At NLHex, vCnt has been predecremented by 4
NLHex:
vCnt ← (vCnt) - (14c), skip[R>=0];
PFetch2[vBase,vDBuf0,0], goto[NL4]; *0 or 2 words to go
vCnt ← (vCnt) - (4c), skip[alu>=0];
NLHx:
IOFetch4[vBase,vfBuf0,0], goto[NL16]; *4 - 14d words to go
IOFetch16[vBase,vfBuf0,0];
*
lu ← (vSLC) - 1; *Test for last line of DCB, task if not.
*
skip[ALU<0];
*
call[vMsgRet];
vBase ← (vBase) + (20c), goto[NLHex];

NL4:
nop; *wait for vCnt write
lu ← ldf[vCnt,16,1]; *test for doubleword required
NL4x:
T ← 177000c, skip[ALU#0];
dispatch[vSize,4,4], dblgoto[CallChkMsg, SendFinalFill, R<0];
Output[vDBuf0, vBuf0];
vBase ← (vBase) + (2c);
Output[vDBuf1, vBuf0], goto[DataSent];

*Less than 16 words to go. The first quadword is on the way, vCnt is correct.
NL16:
vBase ← (vBase) + (4c);
lu ← ldf[vCnt,14,2];
vCnt ← (vCnt) - (4c), skip[ALU#0];
PFetch2[vBase, vDBuf0,0], goto[NL4];
IOFetch4[vBase, vfBuf0, 0], goto[NL16];

DataSent:
dispatch[vSize,4,4], skip[R<0]; *dispatch on low 4 bits of FinalFill
SendFinalFill: dispatch[vSize,2,2], disp[FZ00]; *Send final scan line fill, dispatch on hexes.
CallChkMsg: Input[vTemp,0], call[vChkMsg];
vCursorY ← (vCursorY) + (2c), dblgoto[NoCursor, ShowCursor,R<0];

*Dispatch Table for final zero memory references.
FZ00: disp[FZH0], AT[FZTab,00];
FZ01: Output[vZero,vBuf0], goto[FZCheck16], AT[FZTab,01];
FZ02: Output[vZero,vBuf0], goto[FZ01], AT[FZTab,02];
FZ03: Output[vZero,vBuf0], goto[FZ02], AT[FZTab,03];
FZ04: IOFetch4[vMDS,vfBuf0], goto[FZCheck16], AT[FZTab,04];
FZ05: IOFetch4[vMDS,vfBuf0], goto[FZ01], AT[FZTab,05];
FZ06: IOFetch4[vMDS,vfBuf0], goto[FZ02], AT[FZTab,06];
FZ07: IOFetch4[vMDS,vfBuf0], goto[FZ03], AT[FZTab,07];
FZ10: IOFetch4[vMDS,vfBuf0], goto[FZ04], AT[FZTab,10];
FZ11: IOFetch4[vMDS,vfBuf0], goto[FZ05], AT[FZTab,11];
FZ12: IOFetch4[vMDS,vfBuf0], goto[FZ06], AT[FZTab,12];
FZ13: IOFetch4[vMDS,vfBuf0], goto[FZ07], AT[FZTab,13];
FZ14: IOFetch4[vMDS,vfBuf0], goto[FZ10], AT[FZTab,14];
FZ15: IOFetch4[vMDS,vfBuf0], goto[FZ11], AT[FZTab,15];
FZ16: IOFetch4[vMDS,vfBuf0], goto[FZ12], AT[FZTab,16];
FZ17: IOFetch4[vMDS,vfBuf0], goto[FZ13], AT[FZTab,17];

FZCheck16: dispatch[vSize,2,2], goto[FZ00];

FZH0:
goto[CallChkMsg], AT[FZTab2,0];
FZH1:
IOFetch16[vMDS,vfBuf0], goto[CallChkMsg], AT[FZTab2,1];
FZH2:
IOFetch16[vMDS,vfBuf0], goto[FZH1], AT[FZTab2,2];

ShowCursor: Output[vCursorControl, vCursor0]; *Output previously calculated control word to CReg.
T ← (vCursorControl) + (10000c); *set up control word for next scan line.
goto[CursorDone, Carry], lu ← (vCursorControl) and (2000c); *Check CEnable bit
vCursorControl ← T, goto[NoCursor];

*Get here when the control word for the last cursor scan line has been sent, and also during the
*following scan line. First, turn off the enable bit, then fix Y so that the test for cursor active
*will fail for the rest of the field.
CursorDone: vCursorControl ← (vCursorControl) and not (2000c), skip[ALU#0];
vCursorY ← 100000c;*Y will remain negative until EOF
vSLC ← (vSLC) -1, goto[NoCursor1];


FieldDone:
vSLC ← 104000c, goto[FDAx];*set up to load cursor memory
*The constant 104000 is chosen with fiendish cleverness. The first time through
*LdCursMr, the load portion will be skipped. Subsequent calls will do the load
*until the vSLC becomes less than 4000. We must skip the first one, because it
*is not possible to load the cursor memory until pBlank is 1.

FieldDoneA:
vSLC ← 104000c;
FDAx:
T ← (vCR) and (4c);*OddField bit
vCR ← (vCR) xor (4c), dblgoto[FieldA, FieldB, ALU#0]; *Complement it, test field.

FieldA:
vCurrentY ← (vCurrentY) - (vBlankLength) -1; *about to do odd scan lines
TASK, vCursorY ← 2c;
vCursorControl ← 4000c;
vDBA ← fAVSStart, goto[vStartVBlank];

FieldB:
vCurrentY ← (vCurrentY) - (vBlankLength); *about to do even scan lines
TASK, vCursorY ← 1c;
vCursorControl ← 0c;
vDBA ← fBVSStart;
vStartVBlank:
vCR ← (vCR) or (20c), call[LdCursMr]; *ppBlank bit

VSyncWait:
vCurrentY ← T ← (vCurrentY) + (2c);*Wait here for time to start vsync
lu ← (vDBA) + (T);
*set up to resync IAR (returns to vSyncWait)
vNWrds ← 55000c, dblgoto[StartVsync,LdCursM,ALU=0];

StartVsync:
vCR ← (vCR) or (10c); *ppVS bit
vDBA ← (vDBA) - (vSyncLength), call[LdCursMr];

InVSync:
vCurrentY ← T ← (vCurrentY) + (2c);*Wait here to end vSync
lu ← (vDBA) + (T);
Output[vNWrds,vBufStart], goto[EndVSync,ALU=0]; *Load 55000 into START (many times)
Output[vZero,vLdIAR], goto[LdCursM]; *Load IAR (returns to InVSync)

EndVSync:
vCR ← (vCR) and not (10c);
vNWrds ← 155000c, call[LdCursMr]; *Turn off ForceIARLoad

BlankWait:
vCurrentY ← (vCurrentY) + (2c);*Wait here to end vBlank
Output[vNWrds,vBufStart], goto[StartFrame, ALU>=0]; *Load START again (over and over)
goto[LdCursM]; *returns to BlankWait

StartFrame:
vCurrentY ← vMaxYh;
vCurrentY ← (vCurrentY) + (vMaxYl);
vFirstWakeup:
SetUpCursor:
T ← 26c;
PFetch2[vMDS,vDBuf0];*Fetch Cursor x and y from 426b and 427b
T ← (20c);
vCR ← (vCR) and not (20c); *clear ppblank
PFetch2[vMDS,vLink];*Fetch chain head and interruput word.

T ← vDBuf1, skip[Reven]; *Y coordinate
*Y odd => invert earlier decision on which scan line of the cursor goes first.
vCursorControl ← (vCursorControl) xor (4000c);
vCursorY ← (vCursorY) -(T) -1;
Input[vTemp,0]; *input to set up IAddr.6..7 for vChkMsg
vDBuf0 ← (vDBuf0) xnor (0c);*vDBuf0 ← X’
vDBuf0 ← (vDBuf0) + (4c);*increment in nibble number
T ← (vDBuf0) and not (176000c);*save low ten bits
vCursorControl ← (vCursorControl) + (T), call[vChkMsg];*add X to initial Y
vCursorControl ← (vCursorControl) or (2000c); *Set CEnable bit
LoadPage[0];
IOStrobe, T ← vNWrds, callp[DoTIntNT]; *cause field interrupt (bits are in T, no tasking)
T ← (vLink) - (376c), goto[GetNextDCBx];

*SUBROUTINE LdCursM loads one word of the cursor memory, then goes to vChkMsg.
LdCursMr:
usectask;
T ← APC&APCTask;
vBase ← T; *save return link
LdCursM:
vSLC ← (vSLC) - (4000c);
vSLC ← (vSLC) and (174000c), skip[ALU>=0];
Output[vCR,vCReg], goto[vLDnA];
T ← (31c);
T ← (ldf[vSLC,1,4]) + (T);*select the word to load
Pfetch1[vMDS,vDBuf0];*fetch it
vTemp ← 2c;
LoadLp:
Output[vSLC,vCursor0];*output the address (nibble 0 first)
vDBuf0 ← lcy[vDBuf0,4], call[vMsgRet];
Output[vDBuf0,vCursorMem0];*output the data
vSLC ← (vSLC) + (4c);
vTemp ← (vTemp) - 1, goto[LoadLp,R>=0];
vLoadDone:
Output[vCR,vCReg]; *set IAddr.6,7
vLDnA:
IOStrobe, call[vChkMsg];
APC&APCTask ← vBase, goto[vMsgRet];


*SUBROUTINE vChkMsg checks for and posts incoming messages.
vChkMsg:
vDBuf0 ← 176400c, dblgoto[vM1,vM0,NoAtten];

vM0:
T ← vMsg ← (lsh[vMsg,1]), dblgoto[msgAct,vMsgRet, R<0];
vM1:
vCR ← (vCR) + (4000c); *increment the message’s 1’s count.
T ← vMsg ← (lsh[vMsg,1]) + 1, dblgoto[msgAct,vMsgRet, R<0];

vMsgRet:
return;

msgAct:
dispatch[vCR,5,3];*dispatch on message type bits
vTemp ← T, disp[msgType0];

msgType0:
T ← (ldf[vMsg,0,3]) + 1, AT[msgTable,0]; *vMsg contains the type in bits 0-3 at this time
vCR ← (lcy[vCR,10]) or (T);*vCR[5..7] ← type bits
vCR ← rcy[vCR,10];
vMsg ← (vMsg) and not (176000c);*clear type bits in vMsg
vMsg ← (vMsg) or (1000c), return;*or in new flag bit

msgType1:
T ← (vDBuf0) + (34c), goto[StoreKBn], AT[msgTable,1]; *KB0
msgType2:
T ← (vDBuf0) + (35c), goto[StoreKBn], AT[msgTable,2]; *KB1
msgType3:
T ← (vDBuf0) + (36c), goto[StoreKBn], AT[msgTable,3]; *KB2
msgType4:
T ← (vDBuf0) + (37c), goto[StoreKBn], AT[msgTable,4]; *KB3
msgType5:
T ← (vDBuf0) + (30c), goto[StoreKB4], AT[msgTable,5]; *KSMS (keyset/mouse switches)

StoreKB4:
PStore4[vMDS,vTemp], goto[StoreKBnx]; *Stores KSMS in 177030 and 177033, junk in other 2 regs.

StoreKBn:
*ignore the message if it contains fewer than eight 1’s.
lu ← (vCR) + (140000c); *carries if count >= 8d
vCR ← (vCR) and not (177400c), goto[MsgXit,NoCarry];
PStore1[vMDS,vTemp], goto[MsgXit];

StoreKBnx:
vCR ← (vCR) and not (177400c); *clear type and count bits in vCR
MsgXit:
vMsg ← 0c, return; *This will cause a long abort when KSMS is stored.

msgType6:
T ← (24c), AT[msgTable,6];
PFetch2[vMDS,vDBuf0]; *MouseX,Y from 424b, 425b
vTemp ← rcy[vTemp,10]; *setup for later extraction of deltaY
vMsg ← T ← rsh[vMsg,10], goto[.+2, R>=0]; *extract deltaX
T ← (vMsg) or (177400c); *sign extend
vDBuf0 ← (vDBuf0) + (T); *add to current X
vTemp ← T ← (rsh[vTemp,10]), goto[.+2, R>=0]; *extract deltaY
T ← (vTemp) or (177400c); *sign extend
vDBuf1 ← (vDBuf1) + (T); *add to current Y
T ← (24c);
PStore2[vMDS,vDBuf0], goto[StoreKBnx]; *restore X,Y

msgType7:
goto[StoreKBnx], AT[msgTable,7]; *no message of this type


*SUBROUTINE CheckBankCross increments vBase1 if a bank boundary is crossed.
*called only from LineWake-1.
CheckBankCross: T ← 177000c, skip[Carry]; *displacement for Initial Zero memory refs.
return;
vBase1 ← (vBase1) + (400c) + 1, return;

:END[Display];