Google
 

Trailing-Edge - PDP-10 Archives - AP-4178E-RM - swskit-sources/magtap.mac
There are 58 other files named magtap.mac in the archive. Click here to see a list.
;<3A.MONITOR>MAGTAP.MAC.10, 19-Jul-78 01:36:41, Edit by MCLEAN
;I DID AN IOPIOF IN SWAPCD -- DEFINATELY A NO NO
;<3A.MONITOR>MAGTAP.MAC.9,  6-Jun-78 15:10:47, EDIT BY MILLER
;CHECK FOR NEGATIVE ENTRY IN MAGTAP MTOPR
;<3A-NEW>MAGTAP.MAC.8, 25-May-78 21:54:13, Edit by FORTMILLER
;ADD DX20 SUPPORT
;<3A.MONITOR>MAGTAP.MAC.7,  4-May-78 05:52:42, Edit by MCLEAN
;TEMP FIX TO REMOVE .MTT16
;<3A.MONITOR>MAGTAP.MAC.6,  2-May-78 23:05:53, Edit by MCLEAN
;PERMIT READ BACKWARDS FOR KS10
;<2BOSACK>MAGTAP.MAC.1, 24-Feb-78 01:41:48, EDIT BY BOSACK
;CHANGE TO NEW DEVICE/DATA ERROR BITS
;<3.SM10-RELEASE-3>MAGTAP.MAC.4,  9-Jan-78 16:29:10, EDIT BY ENGEL
;TCO #1890 FIX INCORRECT RECORD COUNT IN MTOPR .MOINF (REVERSE WORDS 6 AND 7)
;<3.SM10-RELEASE-3>MAGTAP.MAC.2, 13-Nov-77 14:41:03, Edit by MCLEAN
;READ BACKWARDS ILLEGAL ON KS10
;<3-MONITOR>MAGTAP.MAC.163,  7-Nov-77 13:03:47, EDIT BY KIRSCHEN
;MORE COPYRIGHT UPDATING...
;<3-MONITOR>MAGTAP.MAC.162,  5-Nov-77 00:13:11, EDIT BY CROSSLAND
;FIX MTOPR SO YOU CAN GET NUMBER OF HARD ERRORS.
;<3-MONITOR>MAGTAP.MAC.161, 12-Oct-77 13:56:52, EDIT BY KIRSCHEN
;UPDATE COPYRIGHT FOR RELEASE 3
;<3-MONITOR>MAGTAP.MAC.160,  5-Jul-77 14:24:29, EDIT BY HURLEY
;MOVE CHECKING OF EXISTENT DRIVES TO DEVICE
;<3-MONITOR>MAGTAP.MAC.159,  6-May-77 11:46:49, EDIT BY HURLEY
;ADD SET INPUT/OUTPUT AND ATTRIBUTE CHECK ENTRIES IN DISPATCH TABLE
;<3-MONITOR>MAGTAP.MAC.158,  3-May-77 13:20:02, Edit by HESS
;TCO 1736 - TAPE PROCESSING ADDED
;<3-MONITOR>MAGTAP.MAC.155, 24-Jan-77 10:46:02, EDIT BY HURLEY
;TCO 1719 - GET CORRECT NUMBER OF PAGES FOR BUFFERS WHEN USING INDUSTRY COMPATIBLE MODE
;<3-MONITOR>MAGTAP.MAC.154, 15-Jan-77 17:53:05, Edit by MCLEAN
;<3-MONITOR>MAGTAP.MAC.153, 15-Jan-77 17:25:39, Edit by MCLEAN
;<3-MONITOR>MAGTAP.MAC.152, 12-Jan-77 15:47:26, Edit by MCLEAN
;<3-MONITOR>MAGTAP.MAC.151, 11-Jan-77 15:51:45, Edit by MCLEAN
;<3-MONITOR>MAGTAP.MAC.150, 27-Dec-76 17:34:11, EDIT BY HURLEY
;<2-MONITOR>MAGTAP.MAC.149, 29-Nov-76 17:48:06, EDIT BY BOSACK
;<2-MONITOR>MAGTAP.MAC.148, 29-Nov-76 17:19:46, EDIT BY BOSACK
;<2-MONITOR>MAGTAP.MAC.147, 29-Nov-76 17:12:45, EDIT BY BOSACK
;TCO 1672 - FIX MTINDX BUG
;<2-MONITOR>MAGTAP.MAC.146, 14-Oct-76 19:27:26, EDIT BY HURLEY
;TCO 1598 - ADD OF%OFL BIT TO OPENF
;<2-MONITOR>MAGTAP.MAC.145, 11-Oct-76 09:21:33, EDIT BY HURLEY
;TCO 1583 - MAKE MAGTAPE WAITS BE DONE WITH HDISMS
;<2-MONITOR>MAGTAP.MAC.144,  6-Oct-76 09:09:44, EDIT BY HURLEY
;TCO 1568 - DONT CLEAR ABORTF UNLESS USER PROGRAM HAS SEEN THE ERROR
;<2-MONITOR>MAGTAP.MAC.143,  2-Sep-76 17:12:26, EDIT BY HALL
;MADE SYSERR ENTRY FOR DIRECTORY NUMBER CONTAIN USER NUMBER FROM JOBDIR
;<1B-MONITOR>MAGTAP.MAC.142, 10-JUN-76 20:01:13, EDIT BY BOSACK
;<1B-MONITOR>MAGTAP.MAC.141, 10-JUN-76 11:17:43, EDIT BY JMCCARTHY
;TCO 1370 - "ASSIGN MTA3" WORKS, EVEN WHEN THERE'S NO MTA3
;<1B-MONITOR>MAGTAP.MAC.140,  9-JUN-76 16:50:49, EDIT BY BOSACK
;<1B-MONITOR>MAGTAP.MAC.139,  9-JUN-76 16:09:01, EDIT BY BOSACK
;<1B-MONITOR>MAGTAP.MAC.138,  9-JUN-76 15:20:53, EDIT BY BOSACK
;TCO 1378 - FIX BUGCHKS AFTER REWIND WITHOUT CLOSE
;<1A-MONITOR>MAGTAP.MAC.137,  6-APR-76 15:23:54, EDIT BY HURLEY
;TCO 1216 - REVISITED
;<1A-MONITOR>MAGTAP.MAC.136,  1-APR-76 13:51:41, EDIT BY HURLEY
;TCO # 1230 - FIX SPELLING ERRORS IN BUGCHK MESSAGES
;<1MONITOR>MAGTAP.MAC.135, 23-MAR-76 18:14:46, EDIT BY HURLEY
;<1MONITOR>MAGTAP.MAC.134, 23-MAR-76 17:04:06, EDIT BY HURLEY
;TCO 1216 - MAKE ALL UNSUPPORTED DISPATCH ENTRIES RETURN AN ERROR CODE
;<2MONITOR>MAGTAP.MAC.133, 16-JAN-76 17:51:12, EDIT BY MURPHY
;<2MONITOR>MAGTAP.MAC.132,  8-JAN-76 18:35:11, EDIT BY HURLEY


;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
;  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.
;
;COPYRIGHT (C) 1976, 1977, 1978 BY DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

	SEARCH PROLOG
	SEARCH PHYPAR
	TTITLE MAGTAP

;SPECIAL AC DEFINITIONS USED HEREIN

DEFAC (U,Q1)			;UNIT NUMBER
DEFAC (IOS,Q2)			;STATUS BITS FROM MTASTS
DEFAC (STS,P1)			;DEVICE STATUS
DEFAC (JFN,P2)			;JFN
DEFAC (DEV,P4)			;DEVICE DISPATCH ADR


;DEFAULT VALUES FOR MAGTAPES

	MTSPCN==5		;MAX # OF SPACING OPERATIONS AT 1 TIME
	MINREC==4		;MINIMUM # OF WORDS FOR OUTPUT RECORD


;BIT DEFINITIONS FOR LH OF MTASTS

	OPN==1B0		;UNIT HAS BEEN OPENED
	OPND==:1B2		;UNIT WAS OPENED FOR DUMP MODE
	DMPWT==1B3		;WAITING FOR A DUMP MODE OP TO FINISH
	LTERR==1B4		;ERROR OCCURED ON LAST DUMP MODE OPER.
	BUFA==1B5		;BUFFERS HAVE BEEN ASSIGNED
	CLOF==1B6		;CLOSF IN PROGRESS
	MTOWT==1B7		;MTOPR IN PROGRESS
	MTIEL==1B8		;INHIBIT ERROR LOGGING


;BITS IN RH OF MTASTS (DEFINED IN MONSYM)
;DATA STRUCTURE DEFINITIONS

DEFSTR (MTRS,MTANR1(U),35,18)	;RECORD SIZE IN HARDWARE BYTES
DEFSTR (MTDM,MTANR1(U),17,3)	;DATA MODE
DEFSTR (MTDN,MTANR1(U),14,4)	;DENSITY
DEFSTR (MTSTC,MTANR1(U),10,3)	;CLOSF FUNCTION COUNTER
DEFSTR (MTRBF,MTANR1(U),7,1)	;READING BACKWARDS FLAG
DEFSTR (MTPAR,MTANR1(U),6,1)	;PARITY
DEFSTR (MTFCN,MTANR1(U),5,4)	;LAST FUNCTION PERFORMED
DEFSTR (MTNTM,MTANR1(U),1,2)	;COUNT OF EOF'S WRITTEN
DEFSTR (MTBYT,MTANR2(U),17,18)	;INITIAL LH OF FILBYT
DEFSTR (MTBUF,MTANR2(U),35,18)	;POINTER TO START OF LIST OF BUF PAGES
DEFSTR (MTCUB,MTANR3(U),23,6)	;CURRENT USER BUFFER
DEFSTR (MTCSB,MTANR3(U),17,6)	;CURRENT SERVICE ROUTINE BUFFER
DEFSTR (MTUBW,MTANR3(U),11,6)	;USER BYTES PER WORD
DEFSTR (MTHBW,MTANR3(U),5,6)	;HARDWARE BYTES PER WORD
DEFSTR (MTCUP,MTANR4(U),35,18)	;CURRENT USER PAGE
DEFSTR (MTCIRB,MTANR4(U),17,18)	;CURRENT IORB IN USE
DEFSTR (MTUBP,MTANR5(U),35,18)	;USER BYTES PER PAGE
DEFSTR (MTUBB,MTANR5(U),17,18)	;USER BYTES PER BUFFER
DEFSTR (MTLIRB,MTANR6(U),35,18)	;LAST IORB ADR FOR DUMP MODE
DEFSTR (MTALTC,MTANR6(U),17,18)	;LAST TRANSFER COUNT

DEFSTR (MTNIR,MTARS1(U),35,6)	;# OF IORB'S QUEUED UP
DEFSTR (MTANIR,MTARS1,35,6)	;ABSOULTE VERSION OF MTNIR
DEFSTR (MTPPB,MTARS1(U),29,9)	;NUMBER OF PAGES PER BUFFER
DEFSTR (ABORTF,MTARS1,20,1)	;AN ERROR OCCURED AND IORB'S ABORTED

DEFSTR (IRFLG,MTIRSD,8,9)	;FLAGS FOR USE BY HIGH LEVEL ROUTINE
	MSKSTR (IRBFR,MTIRSD,1B0)	;BUFFER READY FOR USER
	MSKSTR (IRBFQ,MTIRSD,1B1)	;CURRENT BUFFER FLAG
	MSKSTR (IRBFA,MTIRSD,1B2)	;ACTIVE FLAG, IORB BEING FILLED
					; OR EMPTIED BY SERVICE ROUTINE
	MSKSTR (IRBAB,MTIRSD,1B3)	;IORB ABORTED DUE TO AN ERROR
	MSKSTR (IRBFF,MTIRSD,1B4)	;IORB IS FREE
DEFSTR (IRBUN,MTIRSD,17,9)	;UNIT NUMBER
DEFSTR (IRBPB,MTIRSD,35,18)	;POINTER TO BUFFER OF PAGE POINTERS
DEFSTR (IRBOC,MTIRSD+1,35,18)	;ORIGINAL COUNT


DEFINE MTERET (X) <
	IFNB <X>,<
	JRST [	MOVEI A,X
		MOVX IOS,MT%DVE
		IORB IOS,MTASTS(U)
		TQO <ERRF>
		RET]>
	IFB <X>,<
	JRST [	MOVX IOS,MT%DVE
		IORB IOS,MTASTS(U)
		TQO <ERRF>
		RET]>>
;MAGTAPE DISPATCH TABLE

	SWAPCD

MTADTB::DTBDSP (MTASET)		;SET DIRECTORY
	DTBDSP (MTANAM)		;LOOKUP NAME
	DTBDSP (MTAEXT)		;LOOKUP EXTENTION
	DTBDSP (MTAVER)		;LOOKUP VERSION
	DTBBAD (DESX9)		;PROTECTION INSERTION
	DTBBAD (DESX9)		;ACCOUNT INSERTION
	DTBBAD (DESX9)		;STATUS INSERTION
	DTBDSP (MTAOPN)		;OPENF
	DTBDSP (MTASQI)		;BIN/SIN
	DTBDSP (MTASQO)		;BOUT/SOUT
	DTBDSP (MTACLZ)		;CLOSF
	DTBDSP (MTAREN)		;RENAME
	DTBDSP (MTADEL)		;DELETE
	DTBDSP (MTDMPI)		;DUMPI
	DTBDSP (MTDMPO)		;DUMPO
	DTBDSP (MTAMNT)		;MOUNT
	DTBDSP (MTADSM)		;DISMOUNT
	DTBDSP (MTINID)		;INIT DIRECTORY
	DTBDSP (MTMTAP)		;MTOPR
	DTBDSP (MTGTS)		;GETSTS
	DTBDSP (MTSTS)		;SETSTS
	DTBDSP (MTRECO)		;RECORD OUT
	DTBDSP (RFTADN)		;READ TAD
	DTBDSP (SFTADN)		;SET TAD
	DTBDSP (BIOINP)		;SET JFN FOR INPUT
	DTBDSP (BIOOUT)		;SET JFN FOR OUTPUT
	DTBBAD (GJFX49)		;CHECK ATTRIBUTE

	DTBLEN==:.-MTADTB	;GLOBAL LENGTH OF DISPATCH TABLE
;ROUTINE TO INITIALIZE THE MAGTAP DATA STRUCTURES

	SWAPCD

MTAINI::MOVSI A,-MTAN		;SET UP TO INIT UNITS
	MOVEI B,-1		;NEED THIS CONSTANT
MTINI2:	SETOM MTALCK(A)		;INIT LOCK WORD
	SETZM MTASTS(A)		;ZERO STATUS WORD ALSO
	HRRZM B,TLABL0(A)	;DISCONNECT MT FROM MTA
	SETZM TLABL1(A)		;CLEAR MT STATUS
	AOBJN A,MTINI2		;LOOP BACK FOR ALL UNITS
	RET			;ALL DONE

;ROUTINE TO MOUNT A MAGTAPE UNIT

MTAMNT:	RETSKP


;ROUTINE TO DISMOUNT A MAGTAPE UNIT

MTADSM:	RETSKP


;ROUTINE TO INITIALIZE THE DIRECTORY OF A MAGTAPE

MTINID:	RETBAD (DESX9)


;ROUTINE TO SET THE DIRECTORY

MTASET:	RETBAD (GJFX32)	;NO FILES IN DIR


;ROUTINE TO LOOKUP A NAME ON A MAGTAPE UNIT

MTANAM:	RETBAD (DESX9)


;ROUTINE TO FIND AN EXTENTION

MTAEXT:	RETBAD (DESX9)


;ROUTINE TO FIND A VERSION

MTAVER:	MOVEI A,0
	RETSKP


;ROUITNE TO DO RENAME ON A MAGTAPE 

MTAREN:	RETBAD (DESX9)


;ROUTINE TO DELETE

MTADEL:	RETBAD (DESX9)
;ROUTINE TO GET MAGTAP STATUS BITS

MTGTS::	HLRZ U,DEV		;SET UP UNIT NUMBER
	CALL UPDSTS		;UPDATE THE STATUS
	 RET			;NEED TO BLOCK
	HRRZ A,MTASTS(U)	;GET CURRENT STATUS BITS
	LOAD B,MTALTC		;GET LAST TRANSFER COUNT
	XCTU [HRLM B,3]		;GIVE IT TO USER
	LOAD B,MTDN		;GET DENSITY
	DPB B,[POINTR A,MT%DEN]	;DOESNT HANDLE 1600 BPI TOO WELL
	LOAD B,MTPAR		;GET PARITY
	DPB B,[POINTR A,MT%EVP]
	RET			;RETURN WITH STATUS IN A


;ROUTINE TO SET THE MAGTAP STATUS BITS

MTSTS::	STKVAR <MTSTSA,MTSTSE>
	SETZM MTSTSE		;ZERO THE ERROR BIT
	MOVEM A,MTSTSA		;SAVE NEW STATUS SETTING
	HLRZ U,DEV		;SET UP UNIT NUMBER
	CALL UPDSTS		;UPDATE THE STATUS
	 RET			;NEED TO BLOCK
	MOVE A,MTASTS(U)	;GET CURRENT STATUS
	TXNE A,MT%DAE!MT%DVE!MT%IRL!MT%EOF!MT%EOT
	SETOM MTSTSE		;THERE WAS AN ERROR AT START OF THIS
	MOVE A,MTSTSA		;GET BACK NEW SETTING
	ANDI A,MT%DAE!MT%DVE!MT%IRL!MT%EOT!MT%EOF!MT%SER!MT%EVP!MT%DEN
	MOVX B,MT%DAE!MT%DVE!MT%IRL!MT%EOT!MT%EOF!MT%SER!MT%EVP!MT%DEN
	ANDCAM B,MTASTS(U)	;CLEAR BITS BEING SPECIFIED BY USER
	IORB A,MTASTS(U)	;SET BITS FROM USER
	LDB B,[POINTR A,MT%EVP]	;GET PARITY
	STOR B,MTPAR
	HRRZ B,MTCUTB(U)	;GET UDB ADDRESS
	MOVE B,UDBCHR(B)	;GET THE CHARACTERISTICS WORD
	TXNE B,UC.625		;IS DRIVE CAPABLE OF 6250?
	JRST MTSTS2		;YES, DON'T STORE INTO DENSITY FIELD OF UDB
	LDB B,[POINTR A,MT%DEN]	;AND DENSITY (NOT GOOD FOR 1600 BPI)
	SKIPN B			;DEFAULT WANTED
	JRST [	LOAD B,JSMTD	;YES, GET DEFAULT DENSITY
		SKIPN B
		MOVEI B,MTDFDN	;IF NO JOB DEFAULT, USE SYSTEM DEFAULT
		JRST .+1]
	STOR B,MTDN
MTSTS2:	SKIPE MTSTSE		;ANY ERRORS AT START?
	TXNE A,MT%DAE!MT%DVE!MT%IRL!MT%EOT!MT%EOF
	RET			;IF THERE ARE STILL SOME ERRORS, RETURN
	TQZ <ERRF,EOFF>		;CLEAR ERROR FLAGS
	JE ABORTF,(U),R		;IF ABORTF NOT ON ALREADY, DONT CLEAR IT
	SETZRO ABORTF,(U)	;ZERO THE ABORT FLAG
	RET			;AND EXIT
;ROUTINE TO UPDATE MTASTS
;	CALL UPDSTS
;RETURNS +1:	NEEDS TO BLOCK
;	 +2:	OK

UPDSTS:	CALL SETWLK		;GO SET WRITE LOCK BIT CORRECTLY
	LOAD A,MTFCN		;GET FUNCTION CODE OF LAST OPERATION
	CAIE A,IRFRED		;READ?
	CAIN A,IRFRDR		;OR READ BACKWARDS?
	SKIPA IOS,MTASTS(U)	;YES
	JRST UPDST1		;NO, GO WAIT FOR ACTIVITY TO FINISH
	TXNN IOS,OPND		;OPENED FOR SEQUENTIAL READ?
	RETSKP			;YES, STATUS GETS UPDATED LATER
UPDST1:	CALL MTACLW		;WAIT FOR ACTIVITY TO STOP
	 RET			;NEED TO BLOCK
	CALL MTFLSH		;GO UPDATE STATUS FROM EACH IORB
	 TQZ <BLKF>		;DONT WANT TO BLOCK
	CALL SETBOT		;SET BOT BIT IN STATUS WORD
	RETSKP			;ALL DONE



;ROUTINE TO SET THE WRITE LOCK BIT CORRECTLY IN MTASTS

SETWLK:	HRRZ A,MTCUTB(U)	;GET UDB ADR
	MOVE A,UDBSTS(A)	;GET STATUS OF DRIVE
	MOVX B,MT%ILW		;GET WRITE LOCK BIT IN STATUS WORD
	ANDCAM B,MTASTS(U)	;START BY CLEARING IT
	TXNE A,US.WLK		;IS THIS DRIVE WRITE LOCKED?
	IORM B,MTASTS(U)	;YES, SET THE BIT IN THE STATUS WORD
	RET			;ALL DONE


;ROUTINE TO SET UP MT%BOT

SETBOT:	HRRZ A,MTCUTB(U)	;GET UDB ADR
	MOVE A,UDBSTS(A)	;GET STATUS BITS
	MOVX B,MT%BOT		;FIRST CLEAR THE BIT
	ANDCAM B,MTASTS(U)
	TXNE A,US.BOT		;AT BOT?
	IORM B,MTASTS(U)	;YES, SET THE BIT
	RET
;ROUTINE TO OPEN A MAG TAPE UNIT FOR READING OR WRITING
;CALLED BY OPENF

MTAOPN::HLRZ U,DEV		;SET UP UNIT NUMBER
	TXNE STS,READF!WRTF	;MUST BE OPENED FOR AT LEAST ONE
	TXNE STS,XCTF!RNDF!NONXF
	RETBAD (OPNX15)		;ILLEGAL MODE FOR OPENING
	SKIPN A,MTCUTB(U)	;GET UDB ADR
	JRST MTAOFL		;IF NO UNIT, PRETEND IT IS OFF-LINE
	HRRZS A			;GET ADDRESS ONLY
	JN USOFL,(A),[CALL CHKOFL ;SEE IF OPENS ALLOWED IF OFFLINE
		 JRST MTAOFL	;NO
		JRST .+1]	;YES
	LDB A,[POINT 4,STS,35]	;GET DATA MODE
	CAIE A,17		;MUST BE 0 OR 17
	JUMPN A,MTAILM		;ILLEGAL MODE IF NOT 0 OR 17
	LOCK MTALCK(U),<CALL LCKTST>	;PREVENT MULTIPLE OPENS
	MOVX IOS,OPN		;SEE IF UNIT IS ALREADY OPENED
	TDNE IOS,MTASTS(U)	;...
	JRST BUSY		;YES, DONT OPEN IT TWICE
	CAIN A,17		;DUMP MODE?
	TXO IOS,OPND		;YES, REMEMBER THAT FOR CLOSE
	TXC STS,WRTF!READF	;SEE IF OPEN FOR BOTH READ AND WRITE
	TXCN STS,WRTF!READF	;ONLY ALLOWED IF DUMP MODE
	CAIN A,17		;OPEN FOR BOTH, IS THIS DUMP MODE?
	SKIPA			;YES, OK
	JRST MTAISM		;ILLEGAL SEQUENTIAL I/O MODE
	MOVEM IOS,MTASTS(U)	;MARK THAT UNIT IS NOW OPENED
	CALL SETWLK		;SET THE WRITE LOCK BIT CORRECTLY
	MOVE IOS,MTASTS(U)	;GET STATUS AGAIN
	TXNE IOS,MT%ILW		;IS DRIVE WRITE LOCKED?
	TQNN <WRTF>		;YES, IS THIS A WRITE OPENF?
	SKIPA			;NO, THEN DONT COMPLAIN ABOUT WRITE LOCK
	JRST MTAWLK		;GO COMPLAIN THAT DRIVE IS WRITE LOCKED
MTOPNX:	SETZM MTANR1(U)		;INITIALIZE PER UNIT STORAGE
	SETZM MTANR2(U)
	SETZM MTANR3(U)
	SETZM MTANR4(U)
	SETZM MTANR5(U)
	SETZM MTANR6(U)
	SETZM MTARS1(U)
	LOAD A,JSMTR		;GET DEFAULT RECORD SIZE
	SKIPG A			;IS THE DEFAULT SET?
	MOVEI A,MTDFRS		;NO, USE SYSTEM DEFAULT VALUE
	STOR A,MTRS		;USE THIS UNLESS CHANGED BEFORE BIN 1
	LOAD A,JSMTM		;GET DATA MODE
	SKIPG A			;IS IT SET YET?
	MOVEI A,MTDFDM		;NO, USE SYSTEM DEFAULT VALUE
	STOR A,MTDM
	;..
	;..
	CALL SETBW		;SET UP BYTES PER WORD, ETC.
	 JRST MTOPNF		;FAILED
	LOAD A,JSMTD		;AND ALSO GET THE DEFAULT DENSITY
	SKIPG A			;SPECIFIED?
	MOVEI A,MTDFDN		;NO, USE SYSTEM DEFAULT VALUE
	STOR A,MTDN
	LOAD A,JSMTP		;GET DEFAULT PARITY
	STOR A,MTPAR		;STORE PARITY
	SETZM FILCNT(JFN)	;INITIALIZE COUNT OF BYTES IN BUFFER
	SETZM FILLEN(JFN)	;INITIALIZE LENGTH OF CURRENT BUFFER
	SETZM FILBYN(JFN)	;AND BYTE POSITION
	MOVEI A,^D36		;SET UP BYTE POSITION
	DPB A,PBYTPO		;STORE IN FILBYT(JFN)
	HLRZ A,FILBYT(JFN)	;GET INITIAL BYTE POINTER
	STOR A,MTBYT		;SAVE FOR LATER
	SETZRO MTNIR		;INIT COUNT OF IORB'S IN QUEUE
	MOVEI A,MTBUFN*MTIRBL	;NOW INITIALIZE THE IORB'S
	IMULI A,(U)		;GET START OF IORB AREA
	ADDI A,MTIRBF		;GET ABS ADR OF IORB
	MOVSI B,-MTBUFN		;SET UP COUNT
MTAOP1:	MOVSI C,(A)		;ZERO IORB
	HRRI C,1(A)
	SETZM 0(A)
	BLT C,MTIRBL-1(A)	;INITIALIZED TO ALL ZEROS
	SETONE IRBFF,(A)	;YES, MARK ALL BUFFERS AS FREE 
	STOR U,IRBUN,(A)	;SAVE UNIT NUMBER FOR INTERRUPT LEVEL
	ADDI A,MTIRBL		;STEP TO NEXT IORB
	AOBJN B,MTAOP1		;LOOP BACK FOR ALL IORB'S
	MOVEI A,MAXPPB*MTBUFN	;GET ADDRESS OF START OF LIST
	IMULI A,(U)		;  FOR THIS UNIT
	ADDI A,MTAPBF		;ADD IN BASE ADR OF PAGE BUFFER AREA
	STOR A,MTBUF		;REMEMBER ADR OF LIST
	STOR A,MTCUP		;SET UP CURRENT P TO BE EMPTIED
	SETZRO MTCUB		;START AT BUFFER 0
	SETZRO MTCSB		;FOR BOTH USER AND SERVICE ROUTINE BUF'S
	MOVE A,JOBNO		;GET USER PROGRAM NAME FOR SYSERR
	MOVE B,JOBPNM(A)
	HRRZ C,MTCUTB(U)	;GET UDB ADR
	MOVEM B,UDBPNM(C)	;STORE PROGRAM NAME IN UDB
	HRR B,JOBDIR(A)		;GET LOGGED-IN DIRECTORY
	HRLI B,USRLH		;MAKE IT A USER NUMBER
	MOVEM B,UDBUDR(C)	;STORE IT IN UDB ALSO
	UNLOCK MTALCK(U)	;UNLOCK THE LOCK ON THIS UNIT
	RETSKP			;SUCCESSFULLY OPENED
BUSY:	SKIPA A,[OPNX9]		;UNIT ALREADY OPENED
MTOPNF:	SETZM MTASTS(U)		;CLEAR STATUS WORD
	UNLOCK MTALCK(U)	;UNLOCK THIS UNIT
	RET			;NOT OPENED!

MTAILM:	MOVEI A,OPNX14		;ILLEGAL MODE
	RET

MTAOFL:	MOVEI A,OPNX8		;DEVICE IS OFF-LINE
	RET

MTAWLK:	MOVEI A,OPNX25		;RETURN WRITE LOCKED ERROR CODE
	JRST MTOPNF		;OPENF FAILED

MTAISM:	MOVEI A,OPNX15		;ILLEGAL SEQUENTIAL I/O MODE
	JRST MTOPNF		;GO UNLOCK AND BOMB OUT
;ROUTINE TO SET UP MTUBW, MTHBW, MTUBB, AND MTUBP

SETBW:	LOAD A,MTDM		;GET DATA MODE
	CAILE A,MTMXDM		;LEGAL MODE?
	RETBAD (MTOX5)		;NO
	MOVE A,HBWTB(A)		;GET BYTES PER WORD
	STOR A,MTHBW		;SAVE HARDWARE BYTES PER WORD
	LDB A,PBYTSZ		;GET SIZE OF USER BYTES
	MOVEI C,^D36		;GET USER BYTES PER WORD
	SKIPN A			;SIZE = 0?
	MOVEI A,^D36		;YES, PRETEND IT IS WORD MODE
	IDIV C,A		;GET BYTES PER WORD
	STOR C,MTUBW		;USER BYTES PER WORD
	LOAD A,MTRS		;NOW CALCULATE USER BYTES PER BUFFER
	IMUL A,C		;RECORD SIZE * USER BYTES PER WORD
	OPSTR <IDIV A,>,MTHBW	;NOW HAVE DESIRED VALUE
	SKIPE B			;ANY REMAINING BITS IN RECORD?
	AOS A			;YES, INCR USER BYTES PER BUFFER
	STOR A,MTUBB		;SAVE COUNT OF USER BYTES PER BUFFER
	IMULI C,PGSIZ		;GET USER BYTES PER PAGE
	STOR C,MTUBP		;SAVE USER BYTES PER PAGE
	IDIV A,C		;SEE IF RECORD SIZE TOO LARGE
	SKIPE B			;GET NUMBER OF PAGES PER BUFFER
	AOS A			;PARTIAL PAGE
	CAILE A,MAXPPB		;WITHIN LEGAL RANGE?
	RETBAD (MTOX4)		;NO
	RETSKP			;EXIT OK


HBWTB:	1			;TABLE OF HARDWARE BYTES PER WORD
	1			;36 BIT BYTES
	6			;6 BIT BYTES
	5			;7 BIT BYTES
	4			;8 BIT BYTES
	1			;36 BIT BYTES (HI-DENSITY)
MTMXDM==:.-HBWTB-1
;ROUTINE TO CLOSE A MAGTAPE UNIT
;CALLED BY CLOSF

MTACLZ::HLRZ U,DEV		;SET UP UNIT NUMBER
	MOVX IOS,MTOWT!DMPWT	;CLEAR WAIT FLAGS
	ANDCAB IOS,MTASTS(U)
	TXNN IOS,CLOF		;ALREADY STARTED THE CLOSE?
	JRST [	SETZRO MTSTC	;NO, INITIALIZE CLOSE FUNCTION COUNT
		MOVX IOS,CLOF	;AND MARK THAT CLOSE IS IN PROGRESS
		IORB IOS,MTASTS(U)
		TXNN IOS,OPND	;DUMP MODE?
		JRST .+1	;NO
		INCR MTSTC	;YES, DONT FORCE OUT LAST BUFFER
		JRST .+1]
	TXNE A,CZ%ABT		;AN ABORT TYPE CLOSE?
	JRST MTACLA		;YES, GO ABORT
	CALL MTACLW		;WAIT FOR ACTIVITY TO CEASE
	 RET			;NEED TO BLOCK
	MOVE IOS,MTASTS(U)	;GET STATUS
	TXNN IOS,BUFA!OPND	;ANY BUFFERS?
	JRST MTACLR		;NO, NOTHING TO DO
	TQNE <WRTF>		;WRITING?
	JRST MTACL1		;YES, GO CLOSE THE WRITE
	TXNN IOS,OPND		;DUMP MODE
	TQNN <READF>		;NO, READING?
	JRST MTACLR		;NOTHING TO DO
	SETZRO ABORTF,(U)	;CLEAR ERROR ABORT BIT
	CALL MTPOS0		;SEQUENTIAL READ, GO LEAVE TAPE IN
	 RET			; CORRECT POSITION
	CALL MTACLW		;WAIT FOR ACTIVITY TO STOP
	 RET			;NEED TO BLOCK
	JRST MTACLR
MTACL1:	CALL MTACLW		;WAIT FOR ALL ACTIVITY TO STOP
	 RET			;NEED TO BLOCK
	MOVE IOS,MTASTS(U)	;GET STATUS FLAGS
	TXNE IOS,MT%DAE!MT%DVE	;ERROR IN PROGRESS?
	RETBAD (IOX5)		;YES, GIVE ERROR RETURN
	LOAD A,MTSTC		;GET CLOSE FUNCTION COUNT
	CAIL A,MTACLN		;FINISHED YET?
	JRST MTACLR		;YES, GO RELEASE PAGES
	XCT MTACLT(A)		;PERFORM THE NEXT FUNCTION IN LIST
	 RET			;NEED TO BLOCK
	INCR MTSTC		;STEP TO NEXT FUNCTION TO BE DONE
	JRST MTACL1		;LOOP BACK TIL CLOSE IS COMPLETE

MTACLT:	CALL MTRECO		;FORCE OUT THE LAST PARTIAL BUFFER
	CALL MTCHKE		;CHECK IF EOF'S NEED WRITING
	CALL MTABKE		;THEN BACKSPACE OVER SECOND EOF
	CALL MTFLSH		;FLUSH OUT ALL REMAINING IORBS
MTACLN==.-MTACLT		;NUMBER OF FUNCTIONS IN TABLE

MTACLA:	NOSKED			;DONT ALLOW SCHEDULING
	CALL MTAKIL		;KILL ALL REQUESTS IN QUEUE
	OKSKED
MTCLA1:	LOAD A,MTNIR		;GET NUMBER OF IORBS STILL ACTIVE
	JUMPE A,MTACLR		;IF NONE, CLOSE OUT
	MOVEI A,MTAWAT		;WAIT FOR IT TO FINISH
	HRL A,U
	MDISMS
	JRST MTCLA1		;LOOP BACK TO CHECK IF DONE
;ROUTINE TO CHECK IF EOF'S SHOULD BE WRITTEN
;	CALL MTCHKE
;RETURNS +1:	NEED TO BLOCK
;	 +2:	EOF'S WRITTEN OR NOT NEEDED

MTCHKE:	LOAD A,MTFCN		;GET LAST FUNCTION PERFORMED
	CAIE A,IRFWRT		;A WRITE?
	CAIN A,IRFWTM		;OR WRITE TAPE MARK?
	SKIPA A,[2]		;YES, GO MAKE SURE TWO EOF'S ARE WRITTEN
	RETSKP			;NO NEED FOR ANY EOF'S
	CALLRET MTAWTM		;GO WRITE TAPE MARKS


;ROUTINE TO BACKSPACE OVER SECOND TAPE MARK

MTABKE:	LOAD A,MTFCN		;GET LAST FUNCTION
	CAIE A,IRFWTM		;MUST HAVE BEEN WRITE TAPE MARK
	RETSKP			;NOTHING TO BE DONE IF NOT
	LOAD A,MTNTM		;GET # OF TAPE MARKS WRITTEN
	CAIE A,2		;EXACTLY 2?
	RETSKP			;NO, ALL DONE
	CALL GETIRB		;GET AN IORB TO USE
	 RET			;MUST WAIT FOR ONE
	MOVEI B,IRFBSB		;GET FUNCTION CODE OF BACKSPACE BLOCK
	MOVEI C,1		;DO ONE
	CALL MTPIRQ		;QUEUE IT UP
	RETSKP			;ALL DONE
;ROUTINE TO WAIT FOR ALL IORB'S TO BE FINISHED
;	CALL MTACLW
;RETURNS +1:	NEEDS TO BLOCK
;	 +2:	NO IORB'S LEFT IN QUEUE

MTACLW:	LOAD A,MTNIR		;GET NUMBER OF IORB'S IN QUEUE
	JUMPE A,RSKP		;IF NONE, RETURN
	MOVEI A,MTAWAT		;SET UP TO WAIT FOR REQUESTS TO FINISH
	HRL A,U			;FOR THIS UNIT
	TXO STS,BLKF		;SET WAIT FLAG FOR IO
	RET			;AND GIVE NON-SKIP RETURN

MTACLR:	CALL MTARLB		;RELEASE PAGES (IF ANY)
	SETZM MTASTS(U)		;INITIALIZE STATUS
	RETSKP			;CLOSE IS COMPLETE


;ROUTINE TO WAIT FOR ALL REQUESTS TO BE FINISHED

	RESCD

MTAWAT::LOAD B,MTANIR,(A)	;GET NUMBER OF IORB'S OUTSTANDING
	JUMPLE B,1(4)		;IF 0, RETURN +2
	JRST 0(4)		;OTHERWISE CONTINUE WAITING

	SWAPCD


;ROUTINE TO RETURN ALL IORBS TO THE FREE STATE AND CHECK FOR I/O ERRORS
;	CALL MTFLSH
;RETURNS +1:	NEED TO BLOCK
;	 +2:	OK

MTFLSH:	SAVEQ
	CALL MTACLW		;WAIT FOR ALL QUIET
	 RET			;NEED TO BLOCK
	MOVEI Q3,MTBUFN		;SET UP COUNT OF IORBS
MTFL0:	CALL GETIRB		;GET NEXT IORB
	 JRST MTFL1		;IGNORE ERRORS, (THEY WERE ADDED TO MTASTS)
	CALL IRBDON		;YES, MARK IT DONE
	 JFCL			;IGNORE ERRORS
MTFL1:	LOAD B,MTCSB		;STEP TO NEXT IORB
	AOS B
	IDIVI B,MTBUFN
	STOR C,MTCSB
	SOJG Q3,MTFL0		;LOOP BACK FOR ALL IORBS
	TQZ <BLKF>		;MAKE SURE WAIT FLAG IS NOT SET
	RETSKP			;ALL FLUSHED OUT
;ROUTINE TO DO SEQUENTIAL INPUT FROM MAGTAPE

MTASQI::HLRZ U,DEV		;SET UP UNIT NUMBER
	CALL MTAASB		;GO ASSIGN BUFFER SPACE (IF NEEDED)
	 MTERET ()		;NO ROOM IN JSB FOR BUFFERS
	MOVX IOS,MTOWT		;CLEAR MTOPR WAIT FLAG
	ANDCAB IOS,MTASTS(U)
	LOAD A,MTFCN		;GET LAST FUNCTION DONE
	CAIE A,IRFRED		;LAST ONE A READ?
	CAIN A,IRFRDR		;OR READ REVERSE?
	JRST MTSQI6		;YES, DONT NEED TO UPDATE STATUS
	CALL UPDSTS		;UPDATE STATUS
	 RET			;NEED TO BLOCK
MTSQI6:	CALL CHKERI		;SEE IF ANY ERRORS
	 RET			;YES, DONT GO ANY FURTHER
	MOVE A,FILBYN(JFN)	;SEE IF THE BUFFER IS EMPTY YET
	CAMGE A,FILLEN(JFN)	;...
	JRST MTSQI1		;NO, JUST STEP TO NEXT PAGE IN BUFFER
	JUMPE A,MTSQI5		;IF NO BUFFER YET, DONT CALL IRBDON
MTSQI0:	CALL CLRINP		;CLEAR FILLEN ETC
	 MTERET ()		;ERROR
MTSQI5:	LOAD A,MTRS		;GET RECORD SIZE
	MOVEI B,IRFRED		;MARK THAT A READ IS BEING DONE
	LOAD C,MTRBF		;GET READ BACKWARDS FLAG
	SKIPE C			;READING BACKWARDS?
	MOVEI B,IRFRDR		;YES, GET CORRECT FUNCTION CODE
	CALL MTIRQI		;QUEUE UP ANY FREE IORBS FOR FILLING
	 RET			;ERROR OCCURED
	CALL GETUBF		;GET NEXT IORB TO BE EMPTIED
	 RET			;MUST WAIT FOR ONE TO FILL UP
	JUMPE B,MTSQI0		;IF NO CHARACTERS IN IORB, GET ANOTHER
	OPSTR <IMUL B,>,MTUBW	;CALCULATE USER BYTES IN BUFFER
	OPSTR <IDIV B,>,MTHBW	; HDW BYTES * USER BPW / HDW BPW
	SKIPE C			;PARTIAL BYTE?
	AOS B			;YES, COUNT TOTAL BYTES BY ONE
	LOAD C,MTUBB		;GET MAX USER BYTES PER BUFFER
	CAMLE B,C		;LIMIT TO MAX BYTES
	MOVE B,C		;...
	MOVEM B,FILLEN(JFN)	;THIS = # OF BYTES TO BE BIN'D OR SIN'D
	SETZM FILBYN(JFN)	;SET BYTE NUMBER TO ZERO
	SETZM FILCNT(JFN)	;ZERO COUNT TOO
	JE MTRBF,,MTSQI1	;READING BACKWARDS?
	IDIVI B,PGSIZ		;YES, GET NUMBER OF PAGES OF DATA
	SKIPE C			;PARTIAL PAGES INCLUDED
	AOS B
	MOVEI B,-1(B)		;PAGES GO FROM 0 TO N-1
	OPSTRM <ADDM B,>,MTCUP	;START AT END OF LIST OF PAGES
	;..
	;..
MTSQI1:	SKIPLE FILCNT(JFN)	;ARE THERE ANY MORE BYTES IN BUFFER
	JRST MTSQI2		;YES
	LOAD A,MTCUP		;GET NEXT BUFFER PAGE TO BE EMPTIED
	MOVE A,(A)		;GET VIRTUAL ADDRESS OF THE PAGE
	OPSTR <HRL A,>,MTBYT	;SET UP BYTE POINTER
	MOVEM A,FILBYT(JFN)	;SAVE BYTE POINTER FOR BIN/SIN
	MOVE A,FILLEN(JFN)	;GET LENGTH OF BUFFER
	SUB A,FILBYN(JFN)	;GET NUMBER OF BYTES LEFT IN BUFFER
	LOAD B,MTUBP		;GET NUMBER OF USER BYTES PER PAGE
	CAMLE A,B		;IS THERE MORE THAN 1 PAGE OF BYTES
	MOVE A,B		;YES, LIMIT COUNT TO 1 PAGE ONLY
	MOVEM A,FILCNT(JFN)	;SAVE COUNT OF BYTES IN THIS PAGE
	JN MTRBF,,MTSQI3	;IF READING BACKWARDS, GO FIX UP FILBYT
	INCR MTCUP		;STEP TO NEXT PAGE IN BUFFER
MTSQI2:	SOS FILCNT(JFN)		;NOW GET THE NEXT BYTE
	AOS FILBYN(JFN)		;DECREMENT COUNT AND INCREMENT POSITION
	ILDB A,FILBYT(JFN)	;GET BYTE FROM BUFFER
	RET			;GIVE SUCCESSFUL RETURN


MTSQI3:	DECR MTCUP		;STEP TO NEXT USER PAGE
	MOVE C,FILLEN(JFN)	;GET NUMBER OF BYTES ON THIS PAGE
	SUB C,FILBYN(JFN)	;GET NUMBER OF BYTES LEFT IN RECORD
	IDIV C,B		;DIVIDE BY USER BYTES PER PAGE
	JUMPE D,MTSQI2		;IF ON A PAGE BOUNDRY, USE FULL COUNT
	MOVEM D,FILCNT(JFN)	;IF FIRST TIME THRU, USE THIS COUNT
	SUB B,D			;GET NUMBER OF BYTES TO SKIP
	OPSTR <IDIV B,>,MTUBW	;SPLIT INTO WORDS AND BYTES
	ADDM B,FILBYT(JFN)	;STEP OVER THE FULL WORDS
MTSQI4:	SOJL C,MTSQI2		;THEN STEP OVER BYTES (IF ANY)
	IBP FILBYT(JFN)
	JRST MTSQI4		;LOOP BACK TIL ALL BYTES SKIPPED
;ROUTINE TO WAIT FOR AN IORB TO GO INACTIVE
;ACCEPTS IN A/	IORB ADR
;	JRST MTAWTR
;RETURNS +1:	ALWAYS, WITH BLKF SET AND A SET UP FOR MDISMS

MTAWTR:	HRLI A,MTARWT		;SET UP FOR DISMIS
	MOVSS A
	TQO <BLKF>		;MARK THAT ROUTINE WANTS TO BLOCK
	RET			;GO BLOCK


;ROUTINE TO WAIT FOR AN IORB TO BECOME READY

	RESCD

MTARWT::JN IRBFA,(A),0(4)	;IF STILL ACTIVE, TAKE NON-SKIP RET
	JRST 1(4)		;NO LONGER ACTIVE

	SWAPCD
;ROUTINE TO DO SEQUENTIAL OUTPUT
;CALLED FROM IO

MTASQO::STKVAR <MTSQOB>
	MOVEM A,MTSQOB		;SAVE THE BYTE
	HLRZ U,DEV		;SET UP UNIT
	MOVX IOS,MTOWT		;CLEAR MTOPR WAIT FLAG
	ANDCAB IOS,MTASTS(U)
	CALL CHKERO		;CHECK FOR ERRORS
	 RET			;THERE ARE SOME, EXIT NOW
	CALL MTAASB		;ASSIGN BUFFERS IF NEEDED
	 MTERET ()		;NO ROOM IN JSB FOR BUFFERS
	SKIPG FILLEN(JFN)	;IS THIS THE VERY FIRST TIME THROUGH?
	JRST [	CALL GETIRB	;GET NEXT IORB TO USE
		 RET		;WAIT FOR AN IORB
		JRST MTSQO2]	;GO USE THIS BUFFER FOR FILLING
	MOVE A,FILBYN(JFN)	;GET BYTE COUNT
	CAMGE A,FILLEN(JFN)	;ANY BYTES LEFT IN THIS BUFFER?
	JRST MTSQO1		;YES, GO STEP TO NEXT PAGE
	CALL MTSQO0		;GO OUTPUT THE BUFFER
	CALL GETIRB		;GET NEXT BUFFER FOR USER TO FILL
	 RET			;NONE AVAILABLE, GO WAIT FOR A BUFFER
MTSQO2:	CALL CHKERO		;CHECK FOR ERRORS
	 RET			;ERRORS EXIST
	LOAD A,MTUBB		;GET USER BYTES PER BUFFER
	MOVEM A,FILLEN(JFN)	;SET UP NUMBER OF BYTES TO BE BOUT'ED
	SETZM FILBYN(JFN)	;START AT BYTE 0
	SETZM FILCNT(JFN)	;GUARANTEE TO SET UP FILCNT
MTSQO1:	SKIPLE FILCNT(JFN)	;ANY BYTES LEFT ON THIS PAGE?
	JRST MTSQO3		;YES, GO FINISH OUT THIS PAGE
	LOAD A,MTCUP		;GET POINTER TO CURRENT PAGE
	INCR MTCUP		;STEP TO NEXT PAGE
	MOVE A,(A)		;GET ADDRESS OF BUFFER PAGE
	OPSTR <HRL A,>,MTBYT	;SET UP FILBYT
	MOVEM A,FILBYT(JFN)
	HRLI B,0(A)		;ZERO BUFFER
	HRRI B,1(A)
	SETZM 0(A)
	BLT B,PGSIZ-1(A)
	MOVE B,FILLEN(JFN)	;GET LENGTH OF BUFFER
	SUB B,FILBYN(JFN)	;GET REMAINING BYTES IN BUFFER
	LOAD A,MTUBP		;GET USER BYTES PER PAGE
	CAML A,B		;ENOUGH BYTES LEFT IN BUFFER?
	MOVE A,B		;NO, SET FILCNT CORRECTLY
	MOVEM A,FILCNT(JFN)	;SET UP COUNT OF BYTES TO BE BOUT'ED
MTSQO3:	SOS FILCNT(JFN)		;NOW PUT IN THE ORIGINAL BYTE
	AOS FILBYN(JFN)		;KEEP THE COUNTS RIGHT
	MOVE A,MTSQOB		;GET BACK THE BYTE
	IDPB A,FILBYT(JFN)	;PUT THE BYTE INTO THE BUFFER
	RET			;AND GIVE SUCCESSFUL RETURN
;ROUTINE TO WRITE OUT A PARTIAL BUFFER
;	CALL MTRECO
;RETURNS +1:	ERROR
;	 +2:	DONE

MTRECO::HLRZ U,DEV		;SET UP UNIT NUMBER
	MOVE IOS,MTASTS(U)	;GET STATUS BITS
	TQNN <OPNF>		;CHECK OPENED
	RETBAD (IOX2)		;NOT (CAN GET HERE ON MTOPR)
	TQNE <WRTF>		;OPENED FOR WRITE?
	TXNE IOS,OPND		;AND NOT IN DUMP MODE?
	RETBAD (IOX9)		;NO, RECORD OUT LEGAL FOR SEQUENTIAL WRITE ONLY
	SKIPLE FILLEN(JFN)	;ANY BUFFERS SET UP YET?
	SKIPG FILBYN(JFN)	;YES, ANY THING IN THEM YET?
	RETSKP			;NO, NOTHING TO DO
	CALL MTSQO0		;OUTPUT THE BUFFER
	RETSKP			;ALL DONE



;ROUTINE TO OUTPUT A BUFFER AS SPECIFIED BY FILBYN, FILCNT, AND FILLEN
;	CALL MTSQO0
;RETURNS +1:	NEEDS TO BLOCK
;	 +2:	BUFFER HAS BEEN QUEUED

MTSQO0:	CALL GETCSB		;GET CURRENT IORB BEING USED
	LOAD B,MTHBW		;GET RECORD SIZE FOR OUTPUT
	LOAD D,MTUBW		;GET USER BYTES PER WORD
	IMUL B,FILBYN(JFN)	;BYN * HBW / UBW
	IDIV B,D
	SKIPE C			;ANY PARTIAL BYTES?
	AOS B			;YES, WRITE OUT PARTIAL BYTE
	LOAD D,MTHBW		;NOW GET MIN # OF HDW BYTES PER RECORD
	IMULI D,MINREC		;GET # OF BYTES IN MIN RECORD
	CAMGE B,D		;IS THIS RECORD TOO SMALL?
	MOVE B,D		;YES, MAKE IT THE MINIMUM SIZE
	MOVEI C,IRFWRT		;FUNCTION IS WRITE
	CALL MTAIRQ		;QUEUE UP THIS IORB
	SETZM FILLEN(JFN)	;MARK THAT BUFFER WAS QUEUED UP
	SETZM FILBYN(JFN)
	SETZM FILCNT(JFN)
	MOVX C,MT%EOT		;CHECK FOR EOT
	TDNN C,MTASTS(U)	;...
	RET			;NOT AT EOT
	TQO <ERRF>		;SET ERROR FLAG
	RET			;WILL BE NOTICED LATER
;ROUTINE TO ASSIGN BUFFER SPACE IN THE JSB
;THIS ROUTINE SETS UP MTBUF, MTCUB, AND MTNBF
;ACCEPTS U/	UNIT #
;	CALL MTAASB
;RETURNS +1:	NO ROOM IN JSB FOR BUFFERS
;	 +2:	BUFFERS ASSIGNED

MTAASB:	MOVX A,BUFA		;BUFFERS ALREADY ASSIGNED?
	TDNE A,MTASTS(U)	;...
	RETSKP			;YES, NO NEED TO GO ANY FURTHER
	SAVEP			;SAVE PERMANENT ACS
	LOAD B,MTRS		;GET RECORD SIZE
	OPSTR <IDIV B,>,MTHBW	;GET NUMBER OF WORDS PER RECORD
	ADDI B,PGSIZ-1		;CALCULATE NUMBER OF PAGES REQUIRED
	IDIVI B,PGSIZ		;...
	CAILE B,MAXPPB		;WITHIN BOUNDS?
	RETBAD (MTOX4)		;NO, RECORD SIZE TOO LARGE
	STOR B,MTPPB		;SAVE PAGES PER BUFFER
	LOAD P5,MTBUF		;SET UP BASE ADR OF LIST
	LOAD P3,MTPPB		;SET UP COUNTER
	IMULI P3,MTBUFN
MTAAS1:	CALL ASGPAG		;GET A PAGE FROM JSB
	 JRST MTAAS2		;NO MORE LEFT, GO RELEASE SPACE
	HRRZM A,0(P5)		;STORE ADDRESS IN LIST
	AOS P5			;STEP POINTER TO LIST
	SOJG P3,MTAAS1		;LOOP BACK TIL ALL PAGES ASSIGNED
	MOVX A,BUFA		;MARK THAT BUFFERS ARE NOW ASSIGNED
	IORM A,MTASTS(U)	;...
	RETSKP			;BUFFERS ASSIGNED

MTAAS2:	LOAD A,MTPPB		;GET MAX NUMBER OF ASSIGNABLE PAGES 
	IMULI A,MTBUFN		;...
	SUB P3,A		;GET NEGATIVE COUNT OF PAGES ASSIGNED
MTAAS3:	AOJG P3,MTAAS4		;ANY MORE PAGES TO BE RELEASED?
	MOVE A,-1(P5)		;YES, GET ADDRESS
	CALL RELPAG		;AND RELEASE IT
	SOJA P5,MTAAS3		;LOOP BACK TIL ALL PAGES RELEASED

MTAAS4:	MOVEI A,IOX7		;NO ROOM IN JSB FOR BUFFERS
	RETBAD			;GIVE FAIL RETURN
;ROUTINE TO RELEASE ALL OF THE BUFFER PAGES
;	CALL MTARLB
;RETURNS +1:	ALWAYS

MTARLB:	MOVX A,BUFA		;ARE THERE ANY BUFFERS ASSIGNED?
	TDNN A,MTASTS(U)	;...
	RET			;NO, DONT RELEASE ANY
	SAVEP
	LOAD P3,MTPPB		;GET COUNT OF NUMBER OF PAGES
	IMULI P3,MTBUFN		;...
	LOAD P5,MTBUF		;GET STARTING ADR OF LIST OF PAGES
MTARL1:	HRRZ A,0(P5)		;GET NEXT PAGE TO BE RELEASED
	CALL RELPAG		;RELEASE IT
	AOS P5			;STEP POINTER TO LIST
	SOJG P3,MTARL1		;LOOP BACK TIL ALL PAGES RELEASED
	MOVX A,BUFA		;NOW MARK THAT THERE ARE NO BUFFERS
	ANDCAM A,MTASTS(U)	;...
	RET			;AND RETURN
;ROUTINE TO GET AN IORB TO USE AND STEP TO NEXT IORB
;	CALL GETIRB
;RETURNS +1:	NEED TO WAIT OR ERROR
;	 +2:	A/	IORB ADR

GETIRB:	CALL GETCSB		;GET CURRENT IORB ADR
	JN IRBFA,(A),MTAWTR	;IF ACTIVE STILL, GO WAIT
	JN IRBFF,(A),GETIR1	;IF FREE, DONT CALL IRBDON
	CALL SETSTS		;CHECK FOR ERRORS IN IORB
	CALL IRBDON		;MARK IORB AS DONE
	 RET			;ERROR
GETIR1:	LOAD B,MTCSB		;GET BUFFER NUMBER AGAIN
	OPSTR <IMUL B,>,MTPPB	;GET OFFSET INTO LIST OF PAGES
	OPSTR <ADD B,>,MTBUF	;ADD START ADR OF BUFFER
	STOR B,MTCUP		;SAVE CURRENT PAGE BEING EMPTIED
	SETONE IRBFQ,(A)	;MARK IT AS QUEUED UP
	SETZRO <IRBAB,IRBFF,IRBFR,IRBFA>,(A)
	RETSKP			;GIVE OK RETURN
;ROUTINE TO GET THE NEXT BUFFER TO BE GIVEN TO USER ON INPUT
;	CALL GETUBF
;RETURNS +1:	WAIT OR ERROR
;	 +2:	A/	IORB ADR
;		B/	# OF CHARACTERS IN BUFFER

GETUBF:	CALL GTUBFA		;GET NEXT USER IORB
	 JRST GTUBF1		;NONE AVAILABLE
	CALL SETSTS		;SEE IF ANY ERRORS IN THIS IORB
	CALL CHKERI		;SEE IF ANY ERRORS OR EOF
	 JRST [	PUSH P,A	;SAVE ERROR CODE
		CALL GETCUB	;GET CURRENT IORB
		CALL IRBDON	;RELEASE IT
		 JFCL
		JRST PA1]	;GIVE ERROR RETURN
	MOVE B,IRBCNT(A)	;GET # OF BYTES IN RECORD
	RETSKP			;GOT ONE

GTUBF1:	JUMPE A,[CALL GETCUB	;GET BACK THE IORB ADR
		JRST MTAWTR]	;GO WAIT FOR ACTIVE BIT TO GO OFF
	SKIPL A			;NONE QUEUED UP?
	RETBAD ()		;NO, JUST PLAIN ERROR
	BUG(CHK,MTANOI,<GETUBF: NO QUEUED IORB'S FOR INPUT>)
	JRST MTASIC		;GO BOMB


;ROUTINE TO GET A BUFFER FOR INPUTING
;	CALL GTUBFA
;RETURNS +1:	A = 0		;NEED TO WAIT FOR ACTIVITY TO FINISH
;		A = -1		;NO IORB'S QUEUED UP FOR INPUT
;		A POSITIVE	;ERROR CODE
;	 +2:	A/	IORB ADR

GTUBFA:	CALL GETCUB		;GET IORB FOR USER TO EMPTY
	JN IRBFA,(A),RETZ	;GO WAIT FOR AN IORB
	LOAD B,MTCUB		;GET BUFFER NUMBER AGAIN
	OPSTR <IMUL B,>,MTPPB	;GET OFFSET INTO LIST OF PAGES
	OPSTR <ADD B,>,MTBUF	;ADD START ADR OF BUFFER
	STOR B,MTCUP		;SAVE CURRENT PAGE BEING EMPTIED
	JN <IRBFF,IRBAB>,(A),GETUB1
	LOAD C,ISFCN,(A)	;GET FUNCTION CODE
	CAIE C,IRFRDR		;READ BACKWARDS?
	CAIN C,IRFRED		;OR READ?
	RETSKP			;YES, GIVE THIS BUFFER TO USER
	CALL IRBDON		;MARK THIS BUFFER AS DONE
	 RET			;ERROR OCCURED
GETUB1:	JRST RETO		;NONE QUEUED UP
;ROUTINE TO GET CURRENT USER IORB ADR
;	CALL GETCUB
;RETURNS +1:	ALWAYS, A/	IORB ADR

GETCUB:	MOVEI A,MTBUFN
	IMULI A,(U)		;GET BUFFER NUMBER FOR THIS UNIT
	OPSTR <ADD A,>,MTCUB	;GET RELATIVE BUFFER NUMBER
	IMULI A,MTIRBL
	ADDI A,MTIRBF		;GET ACTUAL ADR
	RET			;AND RETURN


;ROUTINE TO GET CURRENT IORB ADR
;	CALL GETCSB
;RETURNS +1:	ALWAYS, A/	IORB ADR

GETCSB:	MOVEI A,MTBUFN
	IMULI A,(U)		;GET BUFFER NUMBER FOR THIS UNIT
	OPSTR <ADD A,>,MTCSB	;GET RELATIVE BUFFER NUMBER
	IMULI A,MTIRBL
	ADDI A,MTIRBF		;GET ACTUAL ADR
	RET			;AND RETURN


;ROUTINE TO STEP TO NEXT USER BUFFER
;	CALL STPCUB
;RETURNS +1:	ALWAYS

STPCUB:	LOAD B,MTCUB		;STEP USER BUFFER
	AOS B
	IDIVI B,MTBUFN		;MODULO NUMBER OF IORBS
	STOR C,MTCUB		;SAVE NEW BUFFER NUMBER
	RET


;ROUTINE TO CLEAR FILLEN, FILBYN, FILCNT AND AN IORB

CLRINP:	SETZM FILLEN(JFN)	;MARK THAT THERE IS NO MORE BUFFER
	SETZM FILBYN(JFN)	;  IN CASE WE NEED TO BLOCK
	SETZM FILCNT(JFN)
	CALL GETCUB		;GET ADR OF THE BUFFER BEING USED
	CALL IRBDON		;MARK IT DONE
	 RET			;SOME SORT OF ERROR OCCURED
	CALL STPCUB		;STEP TO NEXT USER BUFFER
	RETSKP
;ROUTINE TO MARK AN IORB AS DONE
;ACCEPTS IN A/	IORB ADR
;	CALL IRBDON
;RETURNS +1:	ERROR
;	 +2:	A/	IORB ADR

IRBDON:	JE IRBFQ,(A),IRBDN1	;IF NOT QUEUED UP, ERROR
	JN IRBFA,(A),IRBDN2	;IF STILL ACTIVE, ERROR
	SETZM IRBSTS(A)		;INIT THE IORB
	SETZM IRBMOD(A)
	SETZM IRBXFL(A)		;CLEAR OUT POINTER TO TRANSFER LIST
	SETZRO <IRBFQ,IRBFR,IRBAB,IRBFA>,(A)
	SETONE IRBFF,(A)	;MARK IT AS FREE
	RETSKP			;FINISHED

IRBDN1:	BUG(CHK,MTANOQ,<IRBDN1: IRBDON CALLED FOR NON-QUEUED UP IORB>)
	JRST MTASIC

IRBDN2:	BUG(CHK,MTANOA,<IRBDN2: IRBDON CALLED FOR AN ACTIVE IORB>)
MTASIC:	MOVX A,MT%DVE		;MAKE THIS BE A HARD ERROR
	IORM A,MTASTS(U)
	MTERET (IOX8)		;SOFTWARE CONFUSION
;ROUTINE TO QUEUE UP AN IORB
;ACCEPTS IN A/	IORB ADR
;	CALL QUEIRB
;RETURNS +1:	ALWAYS

QUEIRB:	MOVE IOS,MTASTS(U)	;GET STATUS BITS
	MOVX C,IS.IER		;SEE IF USER DOESNT WANT ERROR RECOVERY
	TXNE IOS,MT%SER		;...
	IORM C,IRBSTS(A)	;DONT DO ANY ERROR RECOVERY ACTION
	MOVX C,IS.IEL		;USER WANT TO IGNORE ERROR LOGGING?
	TXNE IOS,MTIEL		;...
	IORM C,IRBSTS(A)	;YES
	LOAD B,MTCSB		;GET CURRENT IORB NUMBER
	SETONE IRBFA,(A)	;MARK THIS IORB AS ACTIVE
	LOAD C,ISFCN,(A)	;GET FUNCTION
	LOAD D,MTFCN		;GET LAST FUNCTION
	CAIE C,IRFRED		;READING?
	JRST [	CAIE C,IRFRDR	;OR READ REVERSE?
		JRST QUEIR1	;NO, UPDATE MTCUB
		CAIE D,IRFRDR	;YES, WAS LAST ONE A READ REVERSE TOO?
		JRST QUEIR1	;NO, SET POINTER TO BUFFER
		JRST QUEIR2]	;YES, DONT RESET BUFFER POINTER
	CAIE D,IRFRED		;LAST ONE A READ?
QUEIR1:	STOR B,MTCUB		;NO, SET USER BUFFER POINTER HERE
QUEIR2:	STOR C,MTFCN		;SET LAST FUNCTION
	AOS B			;NOW STEP IORB NUMBER
	IDIVI B,MTBUFN		;MODULO NUMBER OF IORBS
	STOR C,MTCSB
	INCR MTNIR		;COUNT UP NUMBER OF QUEUED IORBS
	MOVE B,MTCUTB(U)	;GET CDB,,UDB
	NOSKED			;PHYSIO REQUIRES NOSKED
	CALL PHYSIO		;QUEUE UP REQUEST
	OKSKED
	RET			;RETURN
;ROUTINE TO CHECK AN IORB FOR ERRORS
;	CALL CHKERI	OR	CALL CHKERO
;RETURNS +1:	ERROR
;	 +2:	OK, A-D UNCHANGED

CHKERI:	MOVE IOS,MTASTS(U)	;GET STATUS BITS
	TXNN IOS,MT%EOF!MT%DAE!MT%DVE!MT%IRL
	RETSKP			;NO ERRORS OR EOF
	JRST CHKER1

CHKERO:	MOVE IOS,MTASTS(U)	;GET STATUS BITS
	TXNN IOS,MT%DAE!MT%DVE!MT%IRL
	RETSKP			;NO ERRORS
CHKER1:	MOVEI A,IOX4		;SET UP FOR EOF ERROR
	TXNE IOS,MT%DAE!MT%DVE!MT%IRL	;ANY ERRORS?
	SKIPA A,[IOX5]		;YES, GET ERROR CODE
	TQOA <EOFF>		;MARK EOF FLAG
	TQO <ERRF>		;OR ERROR FLAG
	RET			;AND GIVE ERROR RETURN
;ROUTINE TO SET UP THE STATUS WORD
;ACCEPTS IN A/	IORB ADR
;	CALL SETSTS
;RETURNS +1:	ALWAYS

SETSTS:	JN <IRBAB,IRBFF>,(A),R ;ABORTED OR FREE ARE OK
	JE IRBFR,(A),R		;IF NEVER QUEUED UP, DONT SET STATUS
	MOVE B,IRBSTS(A)	;GET STATUS BITS
	MOVX C,MT%EOT!MT%BOT!MT%EOF ;SET UP TO TEST CONDITIONS
	ANDCAM C,MTASTS(U)	;CLEAR THESE BITS FIRST
	TXNN B,IS.TPM		;EOF SET?
	TXZ C,MT%EOF		;NO
	TXNN B,IS.EOT		;AT END OF TAPE?
	TXZ C,MT%EOT		;NO
	TXNN B,IS.BOT		;OR AT BEGINING OF TAPE
	TXZ C,MT%BOT		;NO
	TXNE B,IS.DTE		;DATA ERROR?
	TXO C,MT%DAE		;YES
	TXNE B,IS.DVE		;DEVICE (HARDWARE) ERROR?
	TXO C,MT%DVE		;YES
	TXNE B,IS.RTL		;RECORD TOO LONG?
	TXO C,MT%IRL		;YES
	IORB C,MTASTS(U)	;SET BITS IN STATUS WORD
	LOAD D,ISFCN,(A)	;GET THE FUNCTION
	CAIN D,IRFRDR		;READ BACKWARDS?
	JRST SETST1		;YES, CHECK RECORD LENGTH
	CAIE D,IRFRED		;READ?
	CAIN D,IRFWRT		;OR WRITE?
SETST1:	SKIPA D,IRBCNT(A)	;YES, CHECK RECORD LENGTH
	JRST SETST2		;NO, DONT SET MT%IRL
	STOR D,MTALTC		;REMEMBER THIS FOR MTGTS
	OPSTR <SUB D,>,IRBOC,(A) ;SUBTRACT ORIGINAL COUNT
	TXNE C,OPND		;DUMP MODE?
	SKIPN D			;YES, MUST BE 0 ELSE ERROR
	JRST SETST2		;NO RECORD LENGTH ERROR
	MOVX C,MT%IRL		;SET UP FOR RECORD LENGTH ERROR
	TXNN B,IS.TPM		;SAW A TAPE MARK THIS TIME?
	IORB C,MTASTS(U)	;NO, SET MT%IRL IN MTASTS
SETST2:	RET			;ALL DONE
;ROUTINE TO CHECK IF AN IORB SHOULD BE ABORTED
;ACCEPTS IN A/	IORB ADR
;	CALL MTCHKA		;CALLED FROM PHYTM2
;RTURNS +1:	ABORT THE IORB
;	 +2:	OK

	RESCD

MTCHKA:	LOAD B,IRBUN,(A)	;GET UNIT NUMBER
	JE ABORTF,(B),RSKP	;IF ABORT FLAG = 0, GIVE OK RETURN
	SETONE IRBAB,(A)	;MARK THAT THE IORB WAS ABORTED
	RET			;LET PHYSIO MARK THIS AS DONE

	SWAPCD



;ROUTINE TO QUEUE UP IORB'S FOR FILLING
;ACCEPTS IN A/	RECORD SIZE
;	    B/	FUNCTION CODE
;	CALL MTIRQI
;RETURNS +1:	ERROR
;	 +2:	SUCCESSFUL

MTIRQI:	STKVAR <MTIRQR,MTIRQF>
	MOVEM A,MTIRQR		;SAVE THE RECORD SIZE
	MOVEM B,MTIRQF		;SAVE FUNCTION CODE
MTIRQ0:	CALL GETCSB		;GET ADR OF THIS IORB
	JN IRBFA,(A),RSKP	;IF IORB IS STILL ACTIVE, ALL DONE
	JN IRBAB,(A),MTIRQ1	;IF ABORTED, USE THIS IORB
	LOAD B,IRBFQ,(A)	;GET QUEUED FLAG
	LOAD C,ISFCN,(A)	;GET FUNCTION CODE OF THIS IORB
	CAIE C,IRFRDR		;READ BACKWARDS
	CAIN C,IRFRED		;OR READ AHEAD?
	JUMPN B,RSKP		;YES, CAUGHT UP WITH USER
MTIRQ1:	CALL GETIRB		;GET NEXT IORB (SHOULD NOT BLOCK)
	 RET			;AN ERROR OCCURED
	MOVE B,MTIRQR		;GET RECORD SIZE
	MOVE C,MTIRQF		;AND FUNCTION CODE
	CALL MTAIRQ		;QUEUE UP THIS IORB FOR READING
	JRST MTIRQ0		;LOOP BACK TILL CAUGHT UP WITH EMPTIER
;ROUTINE TO QUEUE 1 IORB FOR FILLING
;ACCEPTS IN A/	ADR OF IORB TO BE USED
;	    B/	RECORD SIZE
;	    C/	FUNCTION CODE
;	CALL MTIRQ1
;RETURNS +1:	ALWAYS

MTAIRQ:	STKVAR <MTIRQA,MTIRQB,MTIRQC,MTIRQP,MTIRQT,MTIRQF>
	MOVEM A,MTIRQA		;SAVE ADDRESS OF IORB
	MOVEM B,MTIRQT		;SAVE SIZE OF RECORD
	MOVEM B,IRBCNT(A)	;STORE CHARACTER COUNT
	STOR B,IRBOC,(A)	;SAVE ORIGINAL COUNT FOR CHKERR
	MOVEM C,MTIRQF		;SAVE FUNCTION FOR LATER
	STOR C,ISFCN,(A)	;STORE FUNCTION CODE
	ADDI A,MTIRTL		;GET POINTER TO IO LIST AREA
	MOVEM A,MTIRQC		;SAVE FOR STORING LIST LATER
	LOAD B,MTPPB		;GET PAGES PER BUFFER
	MOVEM B,MTIRQP		;SAVE THIS COUNT
	OPSTR <IMUL B,>,MTCSB	;THEN GET START OF AREA FOR THIS BUFFER
	OPSTR <ADD B,>,MTBUF	;GET ADR OF PAGE TO BE FILLED
	MOVEM B,MTIRQB
	MOVE A,MTIRQA		;GET IORB ADR AGAIN
	STOR B,IRBPB,(A)	;SAVE ADR OF LIST OF PAGES FOR MTAINT
MTIRQ2:	HRRZS A,(B)		;ZERO PHYSICAL PAGE IN CASE NOT LOCKED
	SKIPG MTIRQT		;ANY MORE BYTES TO BE TRANSFERED?
	JRST [	MOVE C,MTIRQC	;NO, GO END TRANSFER LIST
		JRST MTIRQ4]
	CALL MLKMA		;LOCK DOWN THIS PAGE
	MOVE B,MTIRQB		;GET INDEX INTO PAGE LIST AGAIN
	HRLM A,(B)		;STORE PHYSICAL PAGE ADDRESS
	MOVE B,MTIRQF		;GET FUNCTION
	CAIE B,IRFRED		;READING?
	CAIN B,IRFRDR		;OR READ REVERSE?
	CALL MRKMPG		;YES, MARK THAT PAGE IS MODIFIED
	MOVE B,MTIRQB		;GET PAGE NUMBER AGAIN
	HLRZ B,(B)		;...
	LSH B,PGSFT		;GET ADDRESS FROM PAGE NUMBER
	LOAD A,MTHBW		;GET HARDWARE BYTES PER WORD
	IMULI A,PGSIZ		;ASK FOR A PAGE TO BE FILLED
	CAMLE A,MTIRQT		;ENOUGH BYTES FOR A FULL PAGE?
	MOVE A,MTIRQT		;NO, JUST DO THE REMAINDER OF BYTES
	SUBM A,MTIRQT		;DECREMENT THE COUNT OF BYTES LEFT
	MOVNS MTIRQT		;SUBM DOESNT DO WHAT WAS DESIRED
	OPSTR <HRL A,>,MTDM	;GET MODE OF TRANSFER
	;..
	;..
	MOVE D,MTIRQF		;GET FUNCTION AGAIN
	CAIE D,IRFRED		;READING?
	CAIN D,IRFRDR		;OR READING BACKWARDS
	SKIPA			;YES
	JRST MTIRQ3		;NO
	JE MTRBF,,MTIRQ3	;READING BACKWARDS?
	TRO B,777		;START AT END OF THIS PAGE
	TXO A,1B0		;YES, TELL PHYSIO THAT
MTIRQ3:	HLRZ C,MTCUTB(U)	;GET CDB FOR THIS UNIT
	CALL PHYCCW		;GET IOWD FOR TRANSFER LIST
	MOVEM A,@MTIRQC		;SAVE IOWD IN TRANSFER LIST
	AOS B,MTIRQB		;STEP TO NEXT PAGE
	AOS C,MTIRQC		;STEP POINTER TO IORB TRANSFER LIST
	SOSLE MTIRQP		;ANY MORE PAGES TO BE DONE?
	JRST MTIRQ2		;LOOP BACK FOR ALL IOWD'S
MTIRQ4:	SKIPLE MTIRQP		;IF USED ALL PAGES IN LIST, DONT SETZM
	SETZM 0(C)		;END TRANSFER LIST WITH A ZERO
	MOVE A,MTIRQA		;GET POINTER TO IORB
	STOR C,IRBTL,(A)	;SAVE POINTER TO TAIL
	MOVEI C,MTIRTL(A)	;GET POINTER TO TRANSFER LIST HEAD
	STOR C,IRBHD,(A)	;STORE HEAD POINTER
	CALL SETMOD		;SET UP MODE WORD
	MOVE D,[MTCHKA,,MTAINT]	;NOW SET UP INTERRUPT LEVEL HANDLER ADR
	MOVEM D,IRBIVA(A)	;IN IORB
	CALLRET QUEIRB		;GO QUEUE UP THE IORB


;ROUTINE TO SET UP MODE WORD IN IORB
;ACCEPTS IN A/	IORB ADR
;	CALL SETMOD
;RETURNS +1:	ALWAYS - IORB ADR STILL IN A

SETMOD:	LOAD D,MTDM		;SET UP DATA MODE
	STOR D,IRBDM,(A)
	LOAD D,MTDN		;SET UP DENSITY
	STOR D,IRBDN,(A)
	LOAD D,MTPAR		;AND SET UP PARITY
	STOR D,IRBPAR,(A)
	RET
;ROUTINE TO MARK THAT IORB IS READY AND UNLOCK BUFFER PAGES
;CALLED AT INTERRUPT LEVEL BY PHYSIO
;ASSUMES IN A/	ADR OF IORB
;	CALL MTAINT
;RETURNS +1:	ALWAYS

	RESCD

MTAINT:	LOAD B,IRBFA,(A)	;MAKE SURE THIS IORB IS REALLY ACTIVE
	JUMPE B,NOACTV		;IF 0, A FATAL STATE OF CONFUSION
	MOVE B,IRBSTS(A)	;GET STATUS BITS FROM IORB
	TXNE B,IS.ERR!IS.TPM	;ERROR OR EOF?
	JRST MTAINE		;YES, GO FLUSH ALL QUEUED REQUESTS
MTAIN0:	CALLRET MTAIND		;NO, GO UNLOCK PAGES AND MARK DONE

MTAINE:	TXNN B,IS.TPM!IS.DTE!IS.DVE!IS.WGU!IS.NRT
	JRST MTAIN0		;DONT ABORT FOR NO GOOD REASON
	SAVEQ			;GET SOME PERMANENT ACS
	LOAD U,IRBUN,(A)	;GET UNIT NUMBER
	CALL MTAIND		;UNLOCK PAGES
	CALLRET MTAKIL		;KILL ALL IORBS LEFT ON QUEUE


NOACTV:	BUG(HLT,MTARIN,<MTAINT: INTERRUPT RECEIVED FOR NONACTIVE IORB>)

	SWAPCD


;ROUTINE TO KILL ALL QUEUED UP REQUESTS

	RESCD

MTAKIL:	STKVAR <MTAKLA>
	SETONE ABORTF,(U)	;MARK THAT IORB'S SHOULD BE ABORTED
	MOVE A,MTCUTB(U)	;SET UP CDB ,, UDB FOR PHYKIL
	CALL PHYKIL		;DEQUEUE ALL IORB'S
MTAKL1:	HRRZM A,MTAKLA		;SAVE ADR OF NEXT IORB
	JUMPE A,R		;IF NONE, RETURN
	HRRZS A			;CLEAR HIGH ORDER BITS
	SETONE IRBAB,(A)	;MARK IORB AS BEING ABORTED
	HRRZ B,IRBIVA(A)	;GET INTERRUPT LEVEL ROUTINE TO  CALL
	CALL 0(B)		;CLEAN UP IORB
	HRRZ A,@MTAKLA		;GET NEXT IORB
	JRST MTAKL1		;LOOP BACK TIL ALL IORB'S DEQUEUED

	SWAPCD
;ROUTINE TO DEQUEUE ONE IORB

	RESCD

MTAIND:	SAVEQ			;SAVE ALL PERMANENT ACS USED
	MOVX B,IRBFR		;MARK THAT BUFFER IS READY
	IORM B,MTIRSD(A)
	MOVX B,IRBFA		;AND CLEAR ACTIVE BIT
	ANDCAM B,MTIRSD(A)
	LOAD U,IRBUN,(A)	;GET UNIT NUMBER
	LOAD B,MTPPB		;GET COUNT OF PAGES THAT NEED UNLOCKING
	MOVNS B			;SET UP FOR AOBJN POINTER
	LOAD Q3,IRBPB,(A)	;GET ADDRESS OF LIST OF LOCKED PAGES
	HRL Q3,B		;Q3 = -MTPPB ,, ADR OF LOCKED PAGES
MTAIN1:	HLRZ A,(Q3)		;GET PHYSICAL ADR OF LOCKED PAGE
	JUMPE A,MTAIN2		;IF AT END OF LOCKED PAGES, STOP
	CALL MULKCR		;UNLOCK IT
	AOBJN Q3,MTAIN1		;LOOP BACK TILL ALL PAGES UNLOCKED
MTAIN2:	DECR MTNIR		;DECREMENT NUMBER OF REQUESTS PENDING
	RET			;AND RETURN

	SWAPCD
;JSYS TO SET UP LOGICAL TO PHYSICAL CORRESPONDENCE. ;INPUT IS:
;	USER A/ SLAVE,,UNIT NUMBER
;	USER B/ SERIAL NUMBER

.MTALN::MCENT
	MOVX B,SC%WHL!SC%OPR
	TDNN B,CAPENB		;IS THIS A PRIVILEGED JOB?
	ITERR (WHELX1)		;NO. COMPLAIN
	HRLI A,600002		;MAGTAPE TYPE
	CALL CHKDES		;MAKE SURE IS A VALID UNIT
	 ITERR()		;CAN'T GET IT
	HLRZ U,DEV		;GET UNIT
	XCTU [SKIPN B,B]	;A VALID SERIAL #
	ITERR ()		;NO
	XCTU [HLRZ P5,A]	;GET DEVICE TYPE
	SKIPN P5		;IS THERE ONE?
	MOVEI P5,.MTT45		;NO ASSUME 45
	JRST MTALN1		;GO TO RESIDENT CODE

	RESCD
MTALN1:	IOPIOF			;DISABLE INTERRUPTS
	MOVSI P3,-MTAN		;SET UP TO SEARCH ALL UNITS
MTLUN1:	SKIPN C,MTCUTB(P3)	;GET UDB ADDRESS FOR THIS UNIT
	JRST MTLUN2		;NONE THERE. FORGET IT
	HRRZ D,C		;GET ADDRESS ONLY
	CAME B,UDBDSN(D)	;IS THIS THE ONE?
	JRST MTLUN2		;NO. KEEP LOOKING
	LOAD P6,USTYP,(D)	;GET TYPE OF SLAVE
	CAIE P6,0(P5)		;MATCH?
	JRST MTLUN2		;NOPE TRY NEXT ENTRY
	CAIN U,0(P3)		;IS THIS ALREADY CORRECT?
	JRST MTALNX		;YES. JUST RETURN GOOD
	MOVX B,OPN		;SEE IF UNIT NOW ASSIGNED
	TDNN B,MTASTS(U)	;IS THIS ONE BEING USED?
	TDNE B,MTASTS(P3)	;IS IT NOW BEING USED?
	JRST [	IOPION	;TURN DISKS BACK ON
		ITERR (OPNX7)]	;UNAVAILABLE
	EXCH C,MTCUTB(U)	;SWITCH ASSIGNMENTS
	MOVEM C,MTCUTB(P3)	;DITTO
MTALNX:	IOPION			;ENABLE I/O
	JRST MRETN		;GOOD

MTLUN2:	AOBJN P3,MTLUN1		;DO ALL UNITS
	IOPION			;TURN DISKS ON AGAIN
	ITERR()			;NONE FOUND
	SWAPCD			;SET SWAP CODE
;MTOPR JSYS
;ACCEPTS IN B/	OPERATION
;	CALL MTMTAP
;RETURNS +1:	ROUTINE WANTS TO BLOCK (BLKF SET) OR ERROR
;	 +2:	DONE

MTMTAP::STKVAR <MTMTAO,MTMTAE>
	MOVEM B,MTMTAO		;SAVE OPERATION CODE
	HLRZ U,DEV		;SET UP UNIT NUMBER
	MOVX IOS,DMPWT		;CLEAR DUMP MODE WAIT FLAG
	ANDCAB IOS,MTASTS(U)
	SKIPL B			;LEGAL OPERATION?
	CAIL B,MTMTBL		;LEGAL OPERATION?
	RETBAD (MTOX1)		;NO, ERROR
	TXNN IOS,MTOWT		;SKIP INTI IF NOT FIRST TIME
	TQNE <OPNF>		;OPENED?
	SKIPA			;YES - SKIP INIT
	JRST [	CALL MTNOPN	;NO - INIT STORAGE
		 RET		;PASS ERROR UP
		JRST .+1]
	CALL MTACLW		;ALWAYS WAIT FOR ACTIVITY TO STOP
	 RET			;NEED TO BLOCK
	CALL UPDSTS		;UPDATE STATUS
	 RET			;NEED TO BLOCK
	MOVE B,MTMTAO		;GET BACK OPERATION CODE
	ROT B,-1		;GET INDEX INTO OPERATION TABLE
	HLRZ A,MTMTAB(B)	;GET EVEN ENTRY
	SKIPGE B		;ODD ENTRY DESIRED?
	HRRZ A,MTMTAB(B)	;YES, GET RIGHT HALF WORD
	JUMPE A,[MOVEI A,MTOX1	;ILLEGAL FUNCTION IF 0
		JRST MTMTPE]
	CALL 0(A)		;DO THE OPERATION
MTMTPE:	 TDZA B,B		;ERROR OR WAIT
	SETO B,			;SUCCESSFUL
	MOVX C,MTOWT		;ALWAYS CLEAR WAIT BIT
	ANDCAM C,MTASTS(U)
	JUMPN B,RSKP		;IF SUCCESSFUL, RETURN NOW
	TQNE <BLKF>		;BLOCKING?
	IORM C,MTASTS(U)	;YES, REMEMBER THIS FOR NEXT PASS
	RET


;ROUTINE TO INIT DRIVE STORAGE IF IT IS NOT OPENED

MTNOPN:	LOCK MTALCK(U),<CALL LCKTST>
	CALL MTOPNX		;INIT DATA BASE
	 RET			;ERROR , PASS ON
	RETSKP			;OK - RETURN
;TABLE OF MTOPR FUNCTIONS

MTMTAB:	MTACLE,,MTAREW		;(0,1) CLEAR ERRORS ,, REWIND
	MTARBK,,MTATPM		;(2,3) SET READ BACKWARDS ,, EOF
	MTASDM,,MTASRS		;(4,5) SET DATA MODE ,, SET RECORD SIZE
	MTAFRC,,MTABRC		;(6,7) FORWARD RECORD ,, BACKWARD RECORD
	MTAEOT,,MTARWU		;(10,11) SKIP TO EOT ,, REWIND  & UNLOAD
	MTARDN,,MTAERT		;(12,13) READ DENSITY ,, ERASE TAPE
	MTARDM,,MTARRS		;(14,15) GET DATA MODE ,, GET REC SIZE
	MTAFFL,,MTABFL		;(16,17) FORWARD FILE ,, BACKWARD FILE
	MTASPA,,MTARPA		;(20,21) SET PARITY ,, READ PARITY
	MTANRB,,MTRECO		;(22,23) GET # OF BYTES LEFT IN RECORD ,, OUTPUT RECORD
	MTASDN,,MTAINF		;(24,25) SET DENSITY ,, GET TAPE INFO
	MTARDR,,MTASID		;(26,27) READ DIRECTION ,, SET REEL ID
	MTAIEL,,RSKP		;(30,31) INHIBIT ERROR LOGGING,,WAIT NOP
	MTALOC,,MTTLS		;(32,33) ATTACH MT TO MTA,,TAPE LABEL SET
	MTTLG,,MTULR		;(34,35) TAPE LABEL GET,,USER LABEL READ
	MTULW,,0		;(36,37) USER LABEL WRITE,,ERROR
MTMTBL==<.-MTMTAB>*2		;MAXIMUM LEGAL FUNCTION CODE
;ROUTINE TO CLEAR ERRORS

MTACLE:	MOVX A,MT%DVE!MT%DAE!MT%IRL!MT%EOT!MT%EOF
	TDNN A,MTASTS(U)	;ANY ERRORS SHOWING?
	RETSKP			;NO, DONT CLEAR ABORTF
	ANDCAM A,MTASTS(U)
	SETZRO ABORTF,(U)
	TQZ <ERRF,EOFF>		;CLEAR STS ALSO
	RETSKP			;EXIT SUCCESSFULLY


;ROUTINE TO SET OR CLEAR THE READ BACKWARDS FLAG

MTARBK:	CALL MTPOS0		;POSITION TAPE TO WHERE USER EXPECTS IT
	 RET			;NEED TO BLOCK
	UMOVE T1,3		;GET NEW VALUE
	STOR T1,MTRBF		;SAVE NEW SETTING OF FLAG
	RETSKP			;EXIT


;ROUTINE TO POSITION TAPE TO WHERE USER THINKS IT SHOULD BE
;	CALL MTPOS0
;RETURNS +1:	NEEDS TO BLOCK
;	 +2:	DONE

MTPOS0:	MOVEI A,0		;SET UP TO POSITION HEAD CORRECTLY
	JE MTRBF,,MTPOS1	;READING BACKWARDS?
	CALLRET MTFRC0		;NO, MOVE TAPE FORWARD OVER READ AHEAD

MTPOS1:	CALLRET MTBRC0		;BACK UP OVER READ AHEAD


;ROUTINE TO READ THE DIRECTION OF READ TRANSFER

MTARDR:	LOAD T1,MTRBF		;GET READ BACKWARDS FLAG
	UMOVEM T1,3		;GIVE IT TO USER
	RETSKP


;ROUTINE TO SET DATA MODE

MTASDM:	SAVEQ
	XCTU [HRRZ Q3,3]	;GET NEW DATA MODE
	CAILE Q3,MTMXDM		;LEGAL?
	RETBAD (MTOX5)		;NO, ILLEGAL DATA MODE
	CALL MTAPBO		;OUTPUT PARTIAL BUFFER IF ANY
	 RET			;NEEDS TO BLOCK
	SKIPN Q3		;IS THIS 0?
	LOAD Q3,JSMTM		;YES, GET JOB DEFAULT
	SKIPN Q3		;IS THAT NOT SET YET?
	MOVEI Q3,MTDFDM		;YES, USE SYSTEM DEFAULT
	STOR Q3,MTDM		;STORE NEW DATA MODE
	CALL MTARLB		;RELEASE ANY BUFFERS
	SETZM FILLEN(JFN)	;THROW AWAY ANY DATA IN BUFFERS
	SETZM FILBYN(JFN)
	SETZM FILCNT(JFN)
	CALLRET SETBW		;SET UP OTHER PARAMETERS BASED ON MODE


;ROUTINE TO READ DATA MODE

MTARDM:	LOAD T1,MTDM		;GET DATA MODE
	UMOVEM T1,3		;RETURN IT TO USER
	RETSKP			;AND GIVE SUCCESSFUL RETURN
;ROUTINE TO SET RECORD SIZE

MTASRS:	MOVE IOS,MTASTS(U)	;GET STATUS
	TXNE IOS,OPND		;DUMP MODE IN EFFECT?
	RETBAD (MTOX3)		;YES, ERROR
	TXNE IOS,BUFA		;BUFFERS ASSIGNED YET?
	RETBAD(MTOX2)		;YES, RECORD SIZE MUST BE SET BEFORE I/O IS DONE
	XCTU [HRRZ T1,3]	;GET NEW SIZE
	STOR T1,MTRS		;STORE NEW RECORD SIZE
	CALLRET SETBW		;GO COMPUTE NEW PARAMETERS


;ROUTINE TO READ RECORD SIZE

MTARRS:	LOAD T1,MTRS		;GET RECORD SIZE
	UMOVEM T1,3		;RETURN IT TO USER
	RETSKP			;AND GIVE SUCCESSFUL RETURN



;ROUTINE TO SET REEL I.D.

MTASID:	MOVE T1,CAPENB		;CHECK CAPABILITIES
	TRNN T1,SC%WHL!SC%OPR	;WHEEL OR OPERATOR?
	RETBAD (MTOX7)		;NO, NOT PRIVILEGED TO DO THIS FUNCTION
	UMOVE T1,3		;GET I.D.
	HRRZ T2,MTCUTB(U)	;GET UDB ADR
	MOVEM T1,UDBVID(T2)	;STORE NEW I.D.
	RETSKP


;ROUTINE TO SET ERROR LOGGING FLAG

MTAIEL:	MOVX T1,MTIEL		;GET FLAG
	ANDCAM T1,MTASTS(U)	;CLEAR IT
	XCTU [SKIPE 3]		;USER WANT TO SET IT?
	IORM T1,MTASTS(U)	;YES
	RETSKP
;ROUTINE TO SET DENSITY

MTASDN:	XCTU [HRRZ T1,3]	;GET NEW DENSITY
	CAILE T1,MTMXDN		;LEGAL?
	RETBAD (MTOX6)		;NO, ILLEGAL DENSITY
	SKIPN T1		;WANT DEFAULT?
	LOAD T1,JSMTD		;YES, GET JOB DEFAULT
	SKIPN T1		;GOT ONE YET?
	MOVEI T1,MTDFDN		;NO, USE SYSTEM DEFAULT
	STOR T1,MTDN		;STORE IT
	RETSKP


;ROUTINE TO READ THE DENSITY

MTARDN:	LOAD T1,MTDN		;GET THE DENSITY
	UMOVEM T1,3		;RETURN IT TO USER
	RETSKP			;AND GIVE SUCCESSFUL RETURN



;ROUTINE TO SET PARITY

MTASPA:	UMOVE T1,3		;GET NEW PARITY SETTING
	STOR T1,MTPAR		;SAVE IT
	RETSKP


;ROUTINE TO READ PARITY SETTING

MTARPA:	LOAD T1,MTPAR		;GET PARITY SETTING
	UMOVEM T1,3		;RETURN IT TO USER
	RETSKP			;AND GIVE SUCCESSFUL RETURN



;ROUTINE TO GET THE NUMBER OF BYTES LEFT IN BUFFER

MTANRB:	MOVE IOS,MTASTS(U)	;GET STATUS
	TXNE IOS,OPND		;MUST BE SEQUENTIAL I/O MODE
	RETBAD (MTOX3)		;THIS FUNCTION NOT LEGAL FOR DUMP MODE
	SKIPE T1,FILLEN(JFN)	;ANY BUFFER SET UP
	SUB T1,FILBYN(JFN)	;YES, GET NUMBER OF BYTES REMAINING
	UMOVEM T1,3		;RETURN IT TO USER
	RETSKP			;AND GIVE SUCCESSFUL RETURN
;ROUTINE TO RETURN INFORMATION ABOUT A TAPE

MTAINF:	SAVEPQ			;SAVE PERMANENT ACS
	UMOVE P3,3		;GET POINTER TO ARGUMENT BLOCK
	HRRZ Q3,MTCUTB(U)	;GET POINTER TO UDB
	XCTU [HRRZ P5,0(P3)]	;GET COUNT OF WORDS TO BE RETURNED
	CAILE P5,MTIFTL		;TOO MANY?
	RETBAD (MTOX8)		;YES, ARGUMENT BLOCK TOO LONG
	MOVNS P5
	HRLZS P5		;SET UP AOBJN POINTER
MTAIFL:	AOS P3			;STEP POINTER TO ARGUMENT BLOCK
	XCT MTINFT(P5)		;GET NEXT WORD
	UMOVEM T1,0(P3)		;STORE IT IN USER'S BLOCK
	AOBJN P5,MTAIFL		;LOOP BACK UNTIL DONE
	RETSKP

MTINFT:	MOVEI T1,1		;TAPE TYPE CODE
	MOVE T1,UDBVID(Q3)	;TAPE ID
	CALL MTASER		;DRIVE SERIAL NUMBER
	MOVE T1,UDBRED(Q3)	;FRAMES READ
	MOVE T1,UDBWRT(Q3)	;FRAMES WRITTEN
	MOVE T1,UDBPS2(Q3)	;RECORD # WITHIN CURRENT FILE
	MOVE T1,UDBPS1(Q3)	;FILE # ON TAPE
	MOVE T1,UDBSRE(Q3)	;SOFT READ ERRORS
	MOVE T1,UDBSWE(Q3)	;SOFT WRITE ERRORS
	MOVE T1,UDBHRE(Q3)	;HARD READ ERRORS
	MOVE T1,UDBHWE(Q3)	;HARD WRITE ERRORS
MTIFTL==.-MTINFT

;ROUTINE TO GET SERIAL # OF UNIT

MTASER:	HRRZ B,UDBDSN(Q3)	;GET DRIVE SERIAL #
	MOVE C,udbslv(Q3)	;GET SLAVE NUMBER
	DPB C,[POINT 3,B,17]
	MOVE C,UDBADR(Q3)	;GET TM02 NUMBER
	DPB C,[POINT 3,B,14]
	MOVE A,UDBCDB(Q3)	;GET CHANNEL #
	MOVE C,CDBADR(A)
	DPB C,[POINT 3,B,11]
	MOVE A,B		;RETURN CHAN,CONTRL,UNIT ,, SERIAL #
	RET
;ROUTINE TO REWIND THE TAPE

MTAREW:	CALL MTARWX		;FINISH OUTPUT/CLEAR INPUT
	 RET			;NEEDS TO BLOCK
	CALL GETIRB		;GET AN IORB TO USE
	 RET			;NEED TO WAIT FOR ONE TO BE FREE
	MOVEI B,IRFREW		;GET REWIND FUNCTION CODE
MTARW1:	MOVEI C,1		;DO ONE OPERATION
	CALL MTPIRQ		;QUEUE UP THIS REQUEST
	RETSKP			;ALL DONE


;ROUTINE TO REWIND AND UNLOAD

MTARWU:	CALL MTARWX		;FINISH OUTPUT/CLEAR INPUT
	 RET			;NEEDS TO BLOCK
	CALL GETIRB		;GET AN IORB
	 RET
	MOVEI B,IRFRUN		;SET UP OPERATION CODE
	JRST MTARW1		;GO DO THE OPERATION

;COMMON ROUTINE FOR REWINDS TO FINISH OUTPUT OR CLEAR INPUT

MTARWX:	TQNE <OPNF>		;IF NOT OPENED
	TXNE IOS,OPND		;DUMP MODE?
	RETSKP			;YES - DO NOTHING
	TQNN <WRTF>		;WRITING?
	JRST MTRWXR		;NO
	CALLRET MTAPBO		;YES - DO PARTIAL BUFFER OUTPUT

MTRWXR:	SKIPN FILBYN(JFN)	;BUFFER EXIST?
	RETSKP			;NO
	CALL CLRINP		;YES - CLEAR IT
	 MTERET()		;ERROR
	RETSKP

;ROUTINE TO ERASE GAP

MTAERT:	TQNE <OPNF>		;MUST BE OPENED
	TQNN <WRTF>		;MUST BE OPENED FOR WRITE
	RETBAD (IOX2)		;ILLEGAL WRITE ATTEMPT
	CALL MTAPBO		;OUTPUT PARTIAL BUFFER IF ANY
	 RET			;NEEDS TO BLOCK
	CALL GETIRB		;GET AN IORB TO USE
	 RET
	MOVEI B,IRFERG		;GET OPERATION CODE
	JRST MTARW1		;GO DO OPERATION
;ROUTINE TO FORCE OUT ANY PARTIAL BUFFER
;	CALL MTAPBO
;RETURNS +1:	NEED TO BLOCK
;	 +2:	OK

MTAPBO:	CALL MTRECO		;OUTPUT RECORD
	 JFCL			;NOT DOING SEQUENTIAL OUTPUT
	CALLRET UPDSTS		;WAIT FOR IT TO STOP WRITING


;ROUTINE TO SKIP TO END OF TAPE

MTAEOT:	MOVE IOS,MTASTS(U)	;GET FLAGS
	TXNE IOS,MTOWT		;FIRST TIME THRU?