:TITLE[MesaIO];*Last edit 16 April 1982 by Fiala

%This file assembles a driver for one of the io kludges. At present the
choice lies between the floating point board io kludge (for Petit), CCA
board (for Thacker/Petit), and DES board (for Gifford). These each use MISC
14b for the driver.
%

:IF[AltoMode]; *********************************
Set[ccaKludge,0];
Set[fpKludge,1];
Set[desKludge,0];
**Not implemented yet
:ELSEIF[CedarMode]; ****************************
Set[ccaKludge,1];
Set[fpKludge,0];
Set[desKludge,0];
**Not implemented yet
:ELSE; *****************************************
Set[ccaKludge,0];
Set[fpKludge,0];
Set[desKludge,0];
**Not implemented yet
:ENDIF; ****************************************

:IF[ccaKludge]; ********************************
%At entry to the opcode, there are starting at TOS:
Long pointer to the blocks of tester data (Table starts at word 8--
the first two quadwords are used by the opcode for temp storage);
Long pointer to step control info.
Cardinal count of number of steps to execute;

The cardinal count should be non-zero originally; it is kept in a register
decremented after each successful step. If the step includes data comparison,
and if the comparison fails, the count is not decremented and the opcode
terminates with that count as its result. Interrupts are NOT allowed between
steps.

Step-control info is a packed array with 1 nibble/step where the left-most
two bits of each nibble are the count minus one of quadwords to output for
the current step, the third bit is 1 if the second input quadword should be
checked, and the fourth bit is 1 if the first input quadword should be
checked. This array must begin on a quadaligned storage boundary.

Tester data for each step consists of 1 to 4 output quadwords followed by
0 (no input checking), 2 (1 input quadword checked) or 4 (both input quadwords
checked) input quadwords. The first of each pair of input quadwords is the
correct value for the data and the second is a comparison mask.

Input checking is carried out as follows: IOStore4 stores the results which
are then PFetch4’ed into RM; then the expected quadword is read and xor’ed;
finally, the xor’ed result is anded with the mask quadword. If the result is
non-zero, the instruction exits with the step number at which the comparison
was non-zero at TOS.

Register assignments:
LP/LPhipoint at the tester data;
LPDest/LPDesthipoint at the step-control data;
zBuf2/zBuf3point at the temporary quadword.
xBuf-xBuf3for quadwords of tester data being output and
for results which are first IOStore4’ed in
storage and then PFetch4’ed;
yBuf-yBuf3expected data; mask;
zBufcurrent word of step-control info;
zBuf1counts steps remaining in the current word;
Stackstep count.
%

Set[ccTask,0];
Set[ccOut,Add[LShift[ccTask,4],1]];
Set[ccIn,Add[LShift[ccTask,4],1]];
:IF[AltoMode]; *********************************
Set[ccPage,15];
*126b mi (also 4b mi on xfPage1)
:ELSE; *****************************************
Set[ccPage,17];
*126b mi (also 4b mi on xfPage1)
:ENDIF; ****************************************

@ccOpr:
T ← Stack&-1, LoadPage[opPage1], At[MiscDisp1,14];
LPhi ← T, LoadPage[opPage0], CallP[StackLPy];
*StackLPy returns after LP,,LPhi ← TOS-1,,TOS with stack popped twice,
*LPhi bounds-checked and in base register format, LP in T.
zBuf2 ← T, LoadPage[ccPage];*Base reg pointing at temp storage
T ← LPhi;
OnPage[ccPage];
zBuf3 ← T;
LP ← (LP) + (10C);*Advance LP past temp storage
*Next do the same thing for the LPDest,,LPDesthi base register (but no
*subroutine is available for this one).
T ← Stack&-1;
LPDestHi ← T;
LU ← LdF[LPDestHi,0,12];*Test for out-of-bounds
LPDestHi ← (LSh[LPDestHi,10]) + T + 1, Skip[ALU=0];
LPDestHi ← (Zero) - 1;*Cause map-out-of-bounds
T ← Stack&-1;*Point StkP at step-count
LPDest ← T;
cc4Step:
PFetch1[LPDest,zBuf,0];*Fetch next step control word.
zBuf1 ← 3C;
cc1Step:
IOFetch4[LP,ccOut,0], Call[ccAdLP];
LU ← (zBuf) and (140000C);*Test for another output block
zBuf ← (zBuf) - (40000C), GoTo[cc1Step,ALU#0];
LU ← (zBuf) and (30000C); *check for any check/read required
LU ← (zBuf) and (10000C), Skip[ALU#0]; *check for first block
Stack ← (Stack)-1, GoTo[ccNoChk];
PFetch4[LP,yBuf,0], GoTo[SecondOnly,ALU=0]; *some block not first => second
LP ← (LP) + (4C), Call[ccAdLP1]; *we are checking some block(s)
IOStore4[zBuf2,ccIn,0]; *tester to core, first block
LU ← (zBuf) and (20000C);
PFetch4[zBuf2, xBuf,0], GoTo[FirstOnly,ALU=0]; *core to R, first block

*Here, we know we are to check both blocks of tester data. The first block’s
*tester data is in xBuf, and the desired result is in yBuf.
T ← yBuf, Call[ccChk1A];
IOStore4[zBuf2, ccIn, 4]; *tester to core, second block
T ← xBuf;
yBuf ← (yBuf) and T;
T ← xBuf1, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
yBuf1 ← (yBuf1) and T;
T ← xBuf2, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
yBuf2 ← (yBuf2) and T;
T ← yBuf3, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
PFetch4[LP,yBuf,0]; *second block desired results
LP ← (LP) + (4C), Call[ccAdLP1];
xBuf3 ← (xBuf3) and T;
PFetch4[zBuf2,xBuf,4], Skip[ALU=0]; *core to R, second block
LoadPage[opPage3], GoTo[ccExit];
T ← yBuf;
xBuf ← (xBuf) xor T, Call[ccChk1A1];
T ← yBuf, goto[FOx];

SecondOnly:
LP ← (LP) + (4C); *increment LP
IOStore4[zBuf2, ccIn,0],Skip[Carry’]; *tester to core, first (useless) block
LPHi ← (LPHi) + (400C) + 1;
IOStore4[zBuf2, ccIn,4]; *tester to core, second block
PFetch4[zBuf2, xBuf,4]; *core to R, second block
FirstOnly:
T ← yBuf;
xBuf ← (xBuf) xor T, Call[ccChk1A1];
T ← yBuf;
FOx:
xBuf ← (xBuf) and T;
T ← yBuf1, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
xBuf1 ← (xBuf1) and T;
T ← yBuf2, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
xBuf2 ← (xBuf2) and T;
T ← yBuf3, Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
xBuf3 ← (xBuf3) and T;
Skip[ALU=0];
LoadPage[opPage3], GoTo[ccExit];
Stack ← (Stack)-1;
ccNoChk: zBuf1 ← (zBuf1)-1, Skip[ALU#0]; *check for count exhausted
LoadPage[opPage3], GoTo[ccExit];
LU ← (LPDest) + 1, Skip[ALU<0]; *check for step control word exhausted
zBuf ← LSh[zBuf,4], GoTo[cc1Step];*Another step in this word
LPDest ← (LPDest) + 1, GoTo[cc4Step,Carry’];
LPDestHi ← (LPDestHi) + (400C) + 1;
GoTo[cc4Step];*1 mi required after LPDestHi← before ref.


ccChk1A:
xBuf ← (xBuf) xor T;
ccChk1A1:
T ← yBuf1;
xBuf1 ← (xBuf1) xor T;
T ← yBuf2;
xBuf2 ← (xBuf2) xor T;
T ← yBuf3;
PFetch4[LP,yBuf,0];*Retrieve mask
LP ← (LP) + (4C);
xBuf3 ← (xBuf3) xor T, Skip[Carry’];
LPHi ← (LPHi) + (400C) + 1, Return;
Return;

ccAdLP:
LP ← (LP) + (4C);
ccAdLP1:
Skip[Carry’];
LPHi ← (LPHi) + (400C) + 1, Return;
Return;

ccExit:
GoToP[P7Tail];

END[MesaIO-CCA];

:ELSEIF[fpKludge]; *****************************

%This is a general purpose opcode for driving the floating point board, which
must have been initialized to run on behalf of task 0.

TOS,,TOS-1
Long pointer to quadword for arguments
TOS-2
Count of quadwords to be output (.ge. 1)
TOS-3
Count of quadwords to be input (.ge. 1)
Leaves stack cleared.

Approximate execution time = 79.5 + 10/extra IOFetch4 or IOStore4 cycles.
%
Set[fpTask,0];
*Has to be 0 for IOStrobe to go to correct device (?)
Set[fpBOut,Add[LShift[fpTask,4],0]];
Set[fpBIn,Add[LShift[fpTask,4],3]];
:IF[AltoMode]; *********************************
Set[fpbPage0,4];
*23b mi--must be page with buffer refill at 377b
:ELSE; *****************************************
Set[fpbPage0,10];
*23b mi--must be page with buffer refill at 377b
:ENDIF; ****************************************

@FPOPR:
T ← Stack&-1, LoadPage[opPage1], At[MiscDisp1,14];
LPhi ← T, LoadPage[opPage0], CallP[StackLPy];
*StackLPy returns after LP,,LPhi ← TOS-1,,TOS with stack popped twice,
*LPhi bounds-checked and in base register format.
T ← (Stack&-1) - 1, LoadPage[fpbPage0];*T ← quadword argument count
RTemp ← T, IOStrobe, GoToP[.+1];*Zero FP board address register
OnPage[fpbPage0];
IOFetch4[LP,fpBOut,0];*Output arguments
LP ← (LP) + (4C), Call[AdvLP4];
RTemp, GoTo[.-2,R>=0];
***
T ← Stack&-1, LoadPage[opPage1];
***
CallP[StackLPx];
*The IOStrobe must not occur sooner than the 2nd mi after the task wakeup
T ← (Stack&-1) - 1, Call[Kill2];
*This IOStrobe must not occur until the last word from the previous IOFetch4
*has been received by the FP board; this seems to imply that the IOStrobe
*must occur 17 cycles after the IOFetch4 starts plus at most 6 cycles for
*additional delay until the IOFetch4 has started on MC2 (??).
Nop;
Call[Kill2]; Call[Kill2];
Nop;
RTemp ← T, IOStrobe, Call[.+1];*Start FP board running
*Must not test IOAtten sooner than the 2nd mi after tasking.
Nop;
Skip[IOAtten];*Wait for it to finish
Kill2:
Return;
Nop;
IOStore4[LP,fpBIn,0];*Read back results
LP ← (LP) + (4C), Call[AdvLP4];
RTemp, GoTo[.-2,R>=0];
LU ← NextInst[IBuf], Call[P4Tailx];

AdvLP4:
RTemp ← (RTemp) - 1, Skip[Carry’];
LPhi ← (LPhi) + (400C) + 1, Return;*64k boundary crossing
Return;
END[MesaIO-FP];

:ELSEIF[desKludge]; ****************************
ER[No.code.for.desKludge];
END[MesaIO-DES];
:ELSEIF[AltoMode]; *****************************
T ← sUnimplemented, GoTo[MiscTrap], At[MiscDisp1,14];
END[MesaIO];
:ELSE; *****************************************
TrapParm ← 0C, GoTo[MiscUnimp], At[MiscDisp1,14];
END[MesaIO];
:ENDIF; ****************************************