ER[DSKD];

%	A DISK DIAGNOSTIC ROUTINE

	last modified January 10, 1977  1:39 PM


TO ASSEMBLE:	@MI@ DSKD
LOAD:		DSKD
%
INSERT[DBEG];
*
*
*	OUTGOING COMMAND BITS FROM MAXC TO DISK AND CONTROLLER
*
	TARGET[ILC];

	MP[UDSEL,13];	* DESELECT THE UNIT
	MP[RSC,7];	* RESET SECTOR CONDITION
	MP[RPDL,10];	* RESET PROCESSOR DATA LATE
	MP[RCDL,11];	* RESET CONTROLLER DATA LATE
	MP[RSO,12];	* RESET SECTOR OVERFLOW
	MP[I1EN,0];	* ENABLE SECTOR INTERRUPTS
	MP[I2EN,4];	* ENABLE TYPE 1 WORD INTERRUPTS
	MP[I3EN,5];	* ENABLE TYPE 2 WORD INTERRUPTS
	MP[I4EN,6];	* ENABLE TYPE 3 WORD INTERRUPTS
	MP[LCR,1];	* LOAD CYLINDER ADDRESS REGISTER
	MP[LHR,2];	* LOAD HEAD ADDRESS REGISTER
	MP[COM,3];	* INTERPRET THIS AS A COMMAND
	MP[WRGAT,20];	* TURN ON WRITING
	MP[RDGAT,21];	* TURN ON READING
	MP[ERGAT,24];	* TURN ON ERASING
	MP[SEEK,22];	* INITIATE SEEK TO C.A.R.
	MP[RHR,23];	* RESET HEAD ADDRESS REGISTER TO 0
	MP[HDS,25];	* ACTIVATE R/W HEAD IN H.A.R.
	MP[RESTOR,26];	* SEEK TO CYLINDER 0
	SP[RESET,RSC,RPDL,RCDL,RSO];  * RESETS CONTROLLER ERRORS
	PM[ALL1CYL,777 0000];
	F[HDADR,23,27];	* HEAD ADDRESS FIELD
	F[CYLADR,17,27];* CYLINDER ADDRESS FIELD
*
*	INCOMING STATUS FROM DISK AND CONTROLLER
*
	MP[IC,0];	* INDEX CONDITION
	MP[UUNS,1];	* UNIT UNSAFE
	MP[UOFF,2];	* UNIT OFF LINE
	MP[UNR,3];	* UNIT NOT READY (NORMALLY = SEEKING)
	MP[SKFAIL,4];	* SEEK HAS FAILED
	MP[URDONLY,5];	* UNIT CANNOT BE WRITTEN ON
	MP[CNR,6];	* CONTROLLER IS NOT READY
	MP[SC,7];	* SECTOR CONDITION
	MP[PDL,10];	* PROCESSOR DATA LATE CONDITION
	MP[CDL,11];	* CONTROLLER DATA LATE CONDITION
	MP[SO,12];	* SECTOR OVERFLOW CONDITION


*	REPEATEDLY SELECT A NON-EXISTENT DISK UNIT AND CONTROLLER
*
	PM[UFOUR,4];
	MC[UNIT4,UFOUR];	* MAKE A CONSTANT FOR UNIT 4
RSELU4:	KUNIT←UNIT4, GOTO[.];


*	REPEATEDLY READ OUT THE KUNIT REGISTER
*
RKUNIT:	P←KUNIT, GOTO[.];


*	REPEATEDLY SELECT A NON-EXISTENT DISK UNIT AND CONTROLLER AND
*	THEN READ THE KUNIT REGISTER
SELLOOP:	KUNIT←UNIT4;
	P←NULL;
	P←KUNIT, GOTO[SELLOOP];

*	REPEATEDLY READ OUT THE KSTAT REGISTER
*
RKSTAT:	P←KSTAT, GOTO[.];

*
*	SELECT A NON-EXISTENT DISK UNIT AND CONTROLLER
*
SELU4:	KUNIT←UNIT4;
	P←NULL;
	P←KUNIT;
	P←KSTAT;
*
*	SELECT A REAL DISK UNIT AND CONTROLLER
*
	PM[UZERO,0];
	MC[UNIT,UZERO];	* MAKE A CONSTANT FOR UNIT 0
	PM[IDLP,0];
	MC[IDLE,IDLP];
	MC[DISCONN,UDSEL,RESET];
	MC[TIMES];
	SV[RANGEN,0];

SELUNIT: Q←7S;	* Reset and turn off interrupts on all controllers

SU1:	KUNIT←Q;
	P←AQ;
	KSET←DISCONN,KNEWCOMM;
	P←P-1;
	Q←P,GOTO[SU1,ALU>=0];

	KUNIT←UNIT;
	P←TIMES;
	Q←P+1;
	TIMES←Q;
	KSET←IDLE,KNEWCOMM;
	P←KUNIT;
	P←KSTAT;
	P←RANGEN;
	2P;
	CALL[STORLM,ALU=0];
	RETURN;
*
*	ROUTINE TO SEND A SET CYLINDER REGISTER COMMAND FOLLOWED BY
*	A SEEK COMMAND REPEATEDLY TO A TURNED-OFF DISK UNIT.
*
	MC[CYLVAL,LCR,RESET,ALL1CYL];
	MC[PAUSMSK,CNR];
	MC[SKVAL,COM,RESET,SEEK];
CRLP:	KUNIT←UNIT;
	P←NULL;
	KSET←CYLVAL,KNEWCOMM;
	Q←PAUSMSK;
	P←KSTAT;
	P AND Q;
	GOTO[.-2,ALU#0];
	KSET←SKVAL,KNEWCOMM;
	Q←PAUSMSK;
	P←KSTAT;
	P AND Q;
	GOTO[.-2,ALU#0];
	GOTO[CRLP];
*
*	ROUTINES TO REPEATEDLY STORE AND READ THE KATA REGISTERS
*
	PM[TDP,0];
	MC[DATWD,TDP];
	MC[DATEXT,TDP];
KWDATL:	CALL[SELUNIT];
	KMDR←DATWD;
	KMDRL←DATEXT;
	P←KMDR;
	Q←KMDRL;
	KWDATA,GOTO[.];
KRDATL:	CALL[SELUNIT];
	P←KRDATA;
	Q←KMDRL,GOTO[.-1];
*
*	WAIT UNTIL DISK UNIT AND CONTROLLER BOTH READY
*
	MC[ALLRDY,UOFF,SKFAIL,UNR,CNR];
WAITRDY:Q←ALLRDY;
	P←KSTAT;
	P←P AND Q;		*LOOP UNTIL READY
	GOTO[.-2, ALU#0];
	RETURN;
*
*	INTERRUPT HANDLERS FOR SECTOR AND WORD INTERRUPTS
*
	IM[SILOC,5];
	SILOC[(CALL[SIHAND])];
	IM[WILOC,11];
	WILOC[(CALL[WIHAND])];
	MC[SECRDY];
	MC[WDRDY];
	MC[RSCC,RSC,I1EN];
	MC[KDO];
	MC[KDOL];
	MC[KDI];
	MC[KDIL];
	MC[IUNIT];
	MC[STAAFT];
SIHAND:	P←AQ, Q←1S;
	SECRDY←Q, P←P;
	KCSET←RSCC, P←P;
	P←P;
	P←P;
	Q←KUNIT, P←P;
	IUNIT←Q, P←P;
	Q←KSTAT, P←P;
	STAAFT←Q, P←P;
	Q←P, P←P;
	RETURN,P←P1,IRET;
WIHAND:	P←AQ, Q←1S;
	WDRDY←Q, P←P;
	KMDR←KDO, P←P;
	KMDRL←KDOL, P←P;
	KWDATA, P←P;
	Q←KRDATA, P←P;
	KDI←Q, P←P;
	KDIL←KMDRL, Q←P, P←P;
	RETURN,P←P1,IRET;
*
*	WAIT UNTIL THE BEGINNING OF NEXT INDEX SECTOR
*
	NMC[NISC,IC,SC];
WAITIDX:SECRDY←NULL;
WIX1:	P←SECRDY;
	P;
	GOTO[WIX1,ALU=0];
	P←KSTAT;
	P←(P) U (NISC);
	P←P+1; GOTO[WAITIDX,ALU#0];
	RETURN;
*
*	WAIT UNTIL READY FOR NEXT WORD. INTERRUPT 4 REQUEST MUST NOT
*	BE BACK-PANEL JUMPERED.
*
	SP[BADUNIT,UUNS,UOFF,UNR,SKFAIL,URDONLY];
	MC[OOPSMSK,BADUNIT,PDL,CDL,SO];
WAITWD:	Q←OOPSMSK;
	P←NULL;
	P←KSTAT;
	P AND Q; GOTO[ANALYZE,ALU#0];
	P←WDRDY;
	P;
	P←NULL, GOTO[WAITWD,ALU=0];
	WDRDY←NULL,P, RETURN; * RETURN WITH ALU=0 MEANS ALL WENT OK
*
*	ANALYZE WHY WAITWD FAILED
*
	SV[CONDMSK,7777 7777 7777];
ANALYZE:Q←P AND Q, P←CONDMSK;
	P AND Q;
	GOTO[.+2,ALU=0];
STOP1:	BRKP[1], GOTO[.];
	AQ, RETURN;  * SET THE ALU OUTPUT NONZERO, REQUESTING RECOVERY
*
*	SEEK TO CYLINDER SPECIFIED IN CURCYL[17-27].
*
	MC[CURCYL,UZERO];
	MC[CYLCOM,UZERO];
	MC[SCRCOM,LCR,RESET];
	MC[SEECOM,COM,RESET,SEEK];
SEEKSUB:CALL[WAITRDY];
	Q←CURCYL;
	P←SCRCOM;
	Q← P OR Q;
	CYLCOM←Q;
	KSET←CYLCOM,KNEWCOMM;
	CALL[WAITRDY];
	KSET←SEECOM,KNEWCOMM;
	CALL[WAITRDY];
	RETURN;
*
*	GET A DATA WORD AND PUT IT IN DWD. IF SWITCH=0, GENERATE A RANDOM
*	DATA WORD. IF SWITCH#0, USE PWORD AS THE DATA WORD. THE ROUTINE
*	ASSUMES THAT X, L0-L7, AND DWD HAVE BEEN PRESERVED FROM THE
*	LAST CALL.
*
	MC[SWITCH,UZERO];
	MC[PWORD,UZERO];
	MC[WDSLFT];
	MC[DWD];
	LV[LDWDL];
	SV[X0,7654321];
	SV[X1,76543210];
	SV[X2,765432100];
	SV[X3,7654321000];
	SV[X4,76543210000];
	SV[X5,765432100000];
	SV[X6,654321000007];
	SV[X7,543210000076];
	SV[ZAP,0];
	SV[@X7,IP[X7]];
	SV[NXTIX,0];
GETDWD:	P←SWITCH;
	2P;
	Q←PWORD,GOTO[STDWD,ALU#0];
	Q←DWD,GOTO[.+2,X>=0];
	X←7S;
	P←LX;
	LX←Q←P+Q,DECX;
STDWD:	DWD←Q,RETURN;
*
*	PICK UP RANDOM NUMBER PARAMETERS FROM SCRATCHPAD.
*
LOADLM:	X←7S;
	Y←@X7;
	Q←ZAP;
	DWD←Q;
LOADLMP:P←SY,DECY,GOTO[GETX,X<0];
	LX←P,DECX,GOTO[LOADLMP];
GETX:	X←NXTIX,RETURN;
*
*	STORE UPDATED RANDOM NUMBER PARAMETERS IN SCRATCHPAD.
*
STORLM:	NXTIX←X;
	X←7S;
	Y←@X7;
	Q←DWD;
	ZAP←Q;
STORLMP:Q←LX,DECX,RETURN[X<0];
	SY←Q,DECY,GOTO[STORLMP];
*
*	SEEK FORWARD ONE CYLINDER
*
	SLC[LOCYL: CYLADR[0]];
	SLC[HICYL: CYLADR[626]];
	SLC[ONECYL: CYLADR[1]];
SEEKFWD:P←CURCYL;
	Q←ONECYL;
	P←P+Q;
	Q←LOCYL;
	P-Q; GOTO[.+2,ALU>0];
	P←Q;
	Q←HICYL;
	P-Q; GOTO[.+2,ALU<0];
	P←LOCYL;
	Q←P;
	CURCYL←Q;
	CALL[SEEKSUB];
	RETURN;
*
*	WRITE A MULTIPLE WORD RECORD ON THE INDEX SECTOR OF HEAD 0
*	OF CYLINDER CURCYL.
*
	PM[DZEROP,0];
	MC[DZERO,DZEROP];
	PM[DSYNCP,17];
	MC[DSYNC,DSYNCP];
	PM[WDMAXP,1100];
	MC[WDMAXW,WDMAXP];
	MC[RESETHD,RESET,COM,RHR,I1EN];
	MC[SETHD,LHR,I1EN];
	MC[SHCOM];
	MC[HDSEL,RESET,COM,HDS,I4EN,I1EN];
	MC[WRITEC,COM,WRGAT,ERGAT,HDS,I4EN,I1EN];
	MC[ERASE,COM,ERGAT,HDS,I4EN,I1EN];
	MC[STATUSW];
SETCHD:	CALL[SEEKSUB];
	KSET←RESETHD,KNEWCOMM,CALL[WAITRDY];
	P←CURCYL;
	Q←37 0000S;  * MASK CYLINDER TO GET HEAD NUMBER
	P←P AND Q;
	Q←23 0000S;  * MAKE SURE HEAD IS IN [0-23]
	Q←P-Q;
	GOTO[.+2,ALU<0];
	P←AQ;
	Q←SETHD;
	Q←P OR Q;
	SHCOM←Q;
	KSET←SHCOM,KNEWCOMM,CALL[WAITRDY];
	KMDR←DZERO; KMDRL←DZERO; KWDATA;
	Q←DZERO;
	KDO←Q;
	KDOL←Q;
	WDRDY←NULL;
	RETURN;
WRITE:	CALL[SETCHD];
WRITER:	CALL[WAITIDX];
	CALL[LOADLM];
	Q←0S;
	WDSLFT←Q;
	Q←KSTAT;
	STATUSW←Q;
	KSET←HDSEL,KNEWCOMM,CALL[WAITWD];
	KSET←WRITEC,CALL[WAITWD];
	CALL[WAITWD];
	CALL[WAITWD];
	CALL[WAITWD];
	CALL[WAITWD];
	CALL[WAITWD];
	Q←DSYNC;
	KDO←Q;
	KDOL←Q;
	CALL[WAITWD];
WDLP:	P←WDSLFT;
	Q←P+1;
	WDSLFT←Q;
	P←WDMAXW;
	P-Q;
	GOTO[WEXIT,ALU<0];
	CALL[GETDWD];
	Q←DWD;
	KDO←Q, Q←LDWDL;
	KDOL←Q;
	CALL[WAITWD];
	GOTO[WDLP];
WEXIT:	Q←DZERO;
	KDO←Q;
	KDOL←Q;
	CALL[WAITWD];
	CALL[WAITWD];
	KSET←ERASE;
	CALL[WAITWD];
	KSET←IDLE;
	KMDR←DZERO; KMDRL←DZERO; KWDATA;
	RETURN;
*
*	READ A MULTIPLE WORD RECORD FROM THE INDEX SECTOR OF HEAD 0
*	ON CYLINDER CURCYL AND CHECK IT WORD FOR WORD. IF LOOKAT>0, DISPLAY
*	WORD[LOOKAT] IN P AND Q REGISTERS.
*
	MC[WDMAXR,WDMAXP];
	MC[READC,COM,RDGAT,HDS,I4EN,I1EN];
	MC[STATUSR];
	MC[LOOKAT,DZEROP];
	PM[IERMSK,777777777777];
	MC[ERRMSK,IERMSK];
	PM[IERMSKL,17];
	MC[ERRMSKL,IERMSKL];
READ:	CALL[SETCHD];
READR:	CALL[WAITIDX];
	CALL[LOADLM];
	Q←0S;
	WDSLFT←Q;
	Q←KSTAT;
	STATUSR←Q;
	KSET←HDSEL,KNEWCOMM,CALL[WAITWD];
	CALL[WAITWD];
	CALL[WAITWD];
	KSET←READC,CALL[WAITWD];
	CALL[WAITWD];	* SKIP FIRST GARBAGE WORD
RDLP:	P←WDSLFT;
	Q←P+1;
	WDSLFT←Q;
	P←WDMAXR;
	P-Q;
	GOTO[REXIT,ALU<0];
	CALL[GETDWD];
	P←LOOKAT;
	2P;
	GOTO[CHECK,ALU<=0];
	Q←WDSLFT;
	P-Q,P←KDI;
	GOTO[OKAY,ALU#0];
	Q←KDIL; GOTO[OKAY];
CHECK:	Q←DWD;
	P←KRDATA;
	Q←P#Q, P←ERRMSK;
	P AND Q;
	GOTO[HANG,ALU#0];
	Q←DWD;
	P←KMDRL;
	Q←P#Q, P←ERRMSKL;
	P AND Q; GOTO[OKAY,ALU=0];
HANG:	P←KMDRL;BRKP[1];
OKAY:	CALL[WAITWD];
	GOTO[RDLP,ALU=0]; * CHECK THAT WAITWD WENT OK
REXIT:	KSET←IDLE,KNEWCOMM;
	P←KRDATA;
	RETURN;
*
*	CALLS ON THE VARIOUS ROUTINES
*
	MC[IENABLE,IENABLE];
CLRTIMES:ARM←2100S,INHINT, Q←A0;
	SETF[IENABLE],INHINT;
	IRET, INHINT, TIMES←Q, GOTO[LOADLM];
*
ST:	CALL[CLRTIMES],INHINT;
SEEKTST:CALL[SELUNIT];
	CALL[SEEKFWD];
	GOTO[SEEKTST];
*
WT:	CALL[CLRTIMES],INHINT;
WCONT:	CALL[SELUNIT];
	CALL[WRITE];
	GOTO[WCONT];
*
RT:	CALL[CLRTIMES],INHINT;
RCONT:	CALL[SELUNIT];
	CALL[READ];
	GOTO[RCONT];
*
WRT:	CALL[CLRTIMES],INHINT;
WRCONT:	CALL[SELUNIT];
	CALL[WRITE];
	CALL[READ];
	GOTO[WRCONT];
*
SWRT:	CALL[CLRTIMES],INHINT;
SWRCONT:CALL[SELUNIT];
	CALL[SEEKFWD];
	CALL[WRITE];
	CALL[READ];
	GOTO[SWRCONT];