:TITLE[MesaIO];		*Last edited 1 June 1981 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) and the
CCA board (for Thacker/Petit).  These each use MISC 14b for the driver.
Misc 14 used to be XferL (deimplemented).
%

Set[ccaKludge,0];
Set[fpKludge,1];

: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/LPhi			point at the tester data;
	LPDest/LPDesthi		point at the step-control data;
	zBuf2/zBuf3		point at the temporary quadword.
	xBuf-xBuf3		for quadwords of tester data being output and
				for results which are first IOStore4'ed in
				storage and then PFetch4'ed;
	yBuf-yBuf3		expected data; mask;
	zBuf			current word of step-control info;
	zBuf1			counts steps remaining in the current word;
	Stack			step count.
%

Set[ccTask,0];
Set[ccOut,Add[LShift[ccTask,4],1]];
Set[ccIn,Add[LShift[ccTask,4],1]];
Set[ccPage,15];

@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.
%
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]];

@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[opPage0];	*T ← quadword argument count
	RTemp ← T, IOStrobe, GoToP[.+1];	*Zero FP board address register
OnPage[opPage0];
	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];

:ELSE; *****************************************
	T ← sUnimplemented, GoTo[MiscTrap], At[MiscDisp1,14];
END[MesaIO];
:ENDIF; ****************************************