;;; Copyright (c) 1999 Massachusetts Institute of Technology
;;; See the COPYING file at the top-level directory of this project.

;********************************
;**
;** TO BE DONE:
;**
;** RECORDS LONGER THAN 1K
;** AUTOMATIC DETERMINATION OF DENSITY AND RECORD LENGTH WHEN READING
;** ALLOW CLOSE WHEN TAPE HAS BEEN TAKEN OFF
;** BE MORE FORGIVING OF CONTROLLER LOSSAGE
;** PASS .MTAPE TO JOB DEVICE
;** HAVE AN IGNORE ERRORS SWITCH
;** THIS HORRIBLE BLETCHEROUS IOT CODE SHOULD BE REDESIGNED
;**
;********************************

;Do not be deceived by the TM10 conditionals. This file is only good
; for a TM03/RH11 on a KS10. See MTAPE for anything else. Barf.

IFE TM10A+TM10B+TM03S,.ERR NO MAGTAPE CONTROLLER TYPE SELECTED
IFN TM10A,INFORM IO BUSS MAG TAPE,1
IFN TM10B,INFORM DATA CHANNEL MAG TAPE,1
IFN TM03S,INFORM RH11/TM03 MAG TAPE,1

IFN TM10P,[
$INSRT TM10
]
IFN TM03S,[
$INSRT TM03S
]
$INSRT T20MAC

MSCBL==8			;Command buffer length
MGQDLL==10.			;PI level PDL length
IF2 IFN MNUMB-140600, .ERR IMPLICIT DEPENDENCY ON MNUMB
MSEOFP==210100			;High bit of MNUMB in MEMBLT is EOF flag
MSCHN==140500			;Other 5 bits of MNUMB is drive number

MTXP==410300,,			;BP to set/get transport number from IOCHNM
MUNITF==170300			;BP to set/get unit field in MTC CONI

IFN TM10P,[
;Tape controller commands
MNOPIN==10000			;NOP and interrupt when idle
MWRITE==4000			;Write data
MW3IN==14000			;Write data and long EOR gap
MSPRR==7000			;Space in reverse direction
MREAD==2000			;Read data
MSPFR==6000			;Space in forward direction
MSPFF==16000			;Space forward to EOF
MSPRF==17000			;Space reverse to EOF
MREWND==1000			;Rewind
MRWNDD==11000			;Rewind and unload
MWEOF==5000			;Write EOF mark
]

MGNRTY==10.			;Number of retries on read

;Modes in OPEN call
;
%MMOUT==1,,0			;Open for output, 0=>input
%MMBLK==2,,0			;Block mode, 0=>unit mode
%MMIMG==4,,0			;Image mode, 0=>Ascii
%MMCNK==10,,0			;"Chunk" mode, 0=>stream mode
%MMNSE==20,,0			;No skip to EOF on close, 0=>do skip
%MMEP==40,,0			;Even parity, 0=>odd
%MMDEN==300,,0			;Density select
				; 00=> "Default" - 1600 for KS/TU77
				; 01=> 800 BPI
				; 10=> 1600 BPI
				; 11=> 6250 BPI
%MM32B==400,,0			;"IBM" mode, write 8-bit bytes from LH 32 bits
				; 0=> core dump
%MMRSZ==7000,,0			;Record Size
				; Ranging binarily from 0=>1024. to 7=>8. bytes


EBLK

SUBTTL MAG TAPE STORAGE AREA

MGVTC:	0			;Scratch word for virgin tape check
MGJDTI:	0			;"Wait for job done" time out value
MGNWRD:	0			;Number of words read in a PI level read
MGTBZY:	-1			;Flag to get interrupt started
MGSFTI: 0			;Software interrupt in progress
MGUNIT:	-1			;Unit expecting interrupt
MGWCW:	0			;"Wait for control word written" flag
LMIOWD:	0			;Last MIOWD

MSRAC:	BLOCK NMTCS		;Transport software status flags
 %MA==1,,525252
 %MAEOF==400000			;4.9 EOF seen on read - must be 4.9
 %MAETR==200000			;4.8 EOT on internal read
 %MAETW==100000			;4.7 EOT on write
 %MAERR==040000			;4.6 PI level got an error
 %MARCE==020000			;4.5 Read compare error
 %MACTH==014000			;4.4 Core allocator says stop
				;4.3 "  (WHY 2 BITS?)
 %MASTP==002000			;4.2 Stop read ahead
 %MANWT==001000			;4.1 Nothing written on tape yet
 %MAEFA==000400			;3.9 EOF seen on read-ahead, user hasn't seen
 %MAESO==000200			;3.8 EOF read since open
 %MAMSO==000100			;3.7 Tape moved since first open
 %MAREW==000040			;3.6 "Tape is rewinding" flag
 %MARAC==000020			;3.5 Read active flag
 %MAMOL==000010			;3.4 Transport on line
 %MAWSW==000004			;3.3 Wants software (timing) interrupts
MGERRC:	BLOCK NMTCS		;Error count
MTCEFW:	BLOCK NMTCS		;Number of EOF's written at close
MGEOFR:	BLOCK NMTCS		;Number of EOF's since last read
MTPSAV:	BLOCK NMTCS		;Storage to restore P on EOF
MSCMDC:	REPEAT NMTCS,0		;Number of commands in command list
MGSPCD:	BLOCK NMTCS		;Spacing operation command
MGSCNT: BLOCK NMTCS		;Spacing operation count
MGEOTT:	BLOCK NMTCS		;Timeout for detecting virgin tape
MSBUFP:	REPEAT NMTCS,0		;Mag tape MP<->PI buffer queue  in,,out
MSNBOL:	BLOCK NMTCS		;Number of buffers on list
MSCRW:	BLOCK NMTCS		;R/W flag, -1=>output 0=>input
MSMPRC:	BLOCK NMTCS		;Words left in buffer
MSMPRP:	BLOCK NMTCS		;Next word in buffer
MTMDN:	REPEAT NMTCS,-1		;Block active at MP
MGCABN:	REPEAT NMTCS,-1		;Buffer active at PI level
MTUSE:	REPEAT NMTCS,-1		;Number of channels open on this transport
MTUSR:	REPEAT NMTCS,-1		;User index of transport user
MTBLKS:	BLOCK NMTCS		;If write, buffer size
				;If read, size of last record read
MTMFNC:	BLOCK NMTCS		;.MTAPE function storage
MTMCNT:	BLOCK NMTCS		;.MTAPE count
MTMTAP:	BLOCK NMTCS		;MTAPE call word

MGCMTS:	BLOCK NMTCS		;PI level status info (CONI MTS or similar)
MGCMTC:	BLOCK NMTCS		;PI level controller info (CONI MTC or similar)

 IFN TM10P,[
MTCONO:	REPEAT NMTCS,\.RPCNT_17+20 ;Prototype CONO word for each transport
 ]
 IFN TM03S,[
MTSELW: BLOCK NMTCS		;Select word (TC reg val) for each transport
 MTSELM==3777			;density/format/parity/unit bits mask
MTWTFC:	BLOCK NMTCS		;-10 word count to tape frame conversion factor
 ]

;Build table of QDL (PI level stack) pointers
MGQDLP:	REPEAT NMTCS,-MGQDLL,,CONC MGQD,\.RPCNT,-1

;Built storage for command lists and PI level stacks
REPEAT NMTCS,[
	CONC MSCB,\.RPCNT,:
		BLOCK MSCBL	;Space for command list
		MGRCV		;Fake "command", causes list to wrap around

		MGNCMD		;Function on botton of Q PDL, gets new command
	CONC MGQD,\.RPCNT,:
		BLOCK MGQDLL	;Space for PI level stack

	IFE .RPCNT,MSLCTB==.-MSCB0	;Length of each table
]

;Build table of pointers for adding commands to command list
MTCMBP:	REPEAT NMTCS,-MSCBL-1,,CONC MSCB,\.RPCNT,-1

;Build table of bytepointers for removing commands from command list
MGCMBP:	REPEAT NMTCS,4400,,CONC MSCB,\.RPCNT,-1

;It is depressing to discover that the DUMP program sucks these directly
; out of the running ITS. If you change what's in here fix DUMP too.

MGEMTC:	0			;CONI MTC, at last error
MGEMTS:	0			;CONI MTS, at last error

IFE TM10B,[
MIOWD: BLOCK 2			;Channel program
]
IFN TM10A,[
MGDBRK:	0
	CONO MTS,1
	JRST 12,@MGDBRK
];TM10A

BBLK


;Random Macros

;Enqueue and Dequeue buffers from the lists used to pass buffers between
; main program and PI level. Assumes W has a valid transport number...

DEFINE MTENQB REG,TEMP=TT
	SKIPG MSNBOL(W)		;;Any buffers on list already?
	IFSKP.
	 HLRZ TEMP,MSBUFP(W)	;;Yes, get old tail pointer
	 DPB REG,[MLO,,MEMBLT(TEMP)]	;;Add new one onto end of list
	ELSE.
	 HRRM REG,MSBUFP(W)	;;No, make new buffer head of list
	ENDIF.
	HRLM REG,MSBUFP(W)	;;New buffer is tail in any event
	SETZM TEMP
	DPB TEMP,[MLO,,MEMBLT(REG)]	;;Indicate end of list
	AOS MSNBOL(W)		;;Increment count of buffers on list
TERMIN

DEFINE MTDEQB REG,TEMP=TT
	HRRZ REG,MSBUFP(W)	;;Get head of buffer list
	HLRZ TEMP,MSBUFP(W)	;;Get tail
	CAMN REG,TEMP		;;Tail == head?
	IFSKP.
	 LDB TEMP,[MLO,,MEMBLT(REG)]	;;No, get next buffer on list
	 HRRM TEMP,MSBUFP(W)	;;Make it the new head of list
	ELSE.
	 SETZM MSBUFP(W)	;;Only one buffer, list is now empty
	ENDIF.
	SOS MSNBOL(W)		;;Decrement count of buffers on list
TERMIN

IFN TM03S,[
;Macros for testing various status bits as set up by MGGXPT and friends
IRP OP,,[NN,NE,NA,O,OE,ON,OA,Z,ZE,ZN,ZA,C,CE,CN,CA]
  DEFINE TT!OP REG,#MSK
    ...TSF==0
    IFSE REG,CS1,[
	TL!OP I,MSK
	...TSF==-1
	]
    IFSE REG,CS2,[
	TR!OP I,MSK
	...TSF==-1
	]
    IFSE REG,FS,[
	TL!OP J,MSK
	...TSF==-1
	]
    IFSE REG,ERR,[
	TR!OP J,MSK
	...TSF==-1
	]
    IFE ...TSF,.ERR Invalid register name REG given in TTxxx macro
  TERMIN
TERMIN
];IFN TM03S
SUBTTL MAG TAPE OPEN ROUTINE

;	C/	MODE,,DEV
;	D/	MODE (ROT 1) 4.9=1=>OUTPUT
;	I/	DEVICE NUMBER
;	R/	IOCHNM WORD POINTER

MAGTO:	SKIPL W,I		;Get device
	 CAIL W,NMTCS		;Too big?
	  JRST OPNL1		;No such device
	MOVSI T,%MAREW
	TDNE T,MSRAC(W)		;Rewinding?
	 PUSHJ P,UFLS		;Yes, wait for completion
	CONO PI,CLKOFF		;Don't want transport status changed under me
	SKIPL MTUSE(W)		;See if we are the first opener
	IFSKP.			;First opener. Initialize database for device
	 SETZM MSBUFP(W)	;Indicate no buffers on chain
	 SETZM MSNBOL(W)	;Another way of saying above
	 SETZM MSMPRC(W)	;Buffer words remaining
	 SETZM MSMPRP(W)	;Pointer to buffers
	 SETOM MTMDN(W)		;No buffer at MP level
	 SETOM MGCABN(W)	;No buffer owned by PI code
	 SETZM MSRAC(W)		;Initialize transport software status word
	 MOVEI A,2
	 MOVEM A,MTCEFW(W)	;Number of EOF's written
	 MOVSI A,%MANWT
	 IORM A,MSRAC(W)	;Indicate nothing written on tape
	ELSE.			;Not first opener. Check legality of this open
	 CAME U,MTUSR(W)	;Same user as previous open?
	  JRST OPNL10		;Different user, report error
	 MOVE B,D		;Check direction of open
	 EQV B,MSCRW(W)		;Same as previous direction?
	 SKIPL B		;Yes, OK
	  JRST OPNL2		;No, wrong direction
	ENDIF.
	MOVEM U,MTUSR(W)	;Store user
	SETZM MSCRW(W)		;Indicate read until found to be otherwise
	TLNE C,1		;Read or write?
	 SETOM MSCRW(W)		;Indicate write
	AOS MTUSE(W)		;Indicate 1 more user
	PUSHJ P,SOSSET
	    MTUSE(W)		;SOS if PCLSR'd
	CONO PI,CLKON		;Let um (em?) get me (who's em?)
	PUSHJ P,MTSTAT		;Get transport status in standard form
	MOVE A,MSRAC(W)
	TLNE A,%MAERR		;PI error means device not there
	 JRST MTODNP		;Go report error
IFN TM03S,[
	TTNN FS,%TMSFP		;Formatter present?
	 JRST MTODNP		;No, can't win
	TTNN FS,%TMSOL		;Slave on line?
	 JRST MTODOL		;No, lose
]
IFN TM10P,[
	TXNN J,%T1STH		;Transport hung, by chance?
	 JRST MTOW1		;No, proceed
	TXNN J,%T1SRW		;OK if rewinding
	 JRST OPNL7		;Device not working
]
MTOW1:	SKIPN MSCRW(W)		;Writing?
	 JRST MTOW2		;No...
IFN TM10P,TXNE J,%T1SWL		;Yes. Write locked?
IFN TM03S,TTNE FS,%TMSWL
	 JRST OPNL26		;Yes, device writelocked error
IFN TM10P, TXNN J,%T1SET	;At EOT?
IFN TM03S, TTNN FS,%TMSET
	  JRST MTOW2		;No
	MOVSI A,%MAETW+%MAERR	;If write and EOT, make .IOT give IOC error,
				;  but let open win
	IORM A,MSRAC(W)

MTOW2:	TXNE C,%MMDEN		;Density specified explicitly?
	 JRST MTOW3		;Yes, go check for validity
IFN TM10P,MOVEI A,1		;No, pick one. Use 800 for TM10
IFN TM03S,MOVEI A,2		;Use 1600 for TM03
	DPB A,[.BP %MMDEN,C]	;Set value
	JRST MTOW4		;Done and assumed correct

MTOW3:	LDB A,[.BP %MMDEN,C]	;Get density from open
IFN TM10P,[
	CAIE A,1		;800 is the only currently OK speed for TM10
	 JRST OPNL12
 IFN 0,[
;You need something like this if you arrange to allow non-800 TM10 speeds
	TXNN C,%MM32B		;32-bit compatible mode requested?
	 JRST MTOW4		;No, core dump
	CAIN A,1		;Density specified as 800?
	 JRST MTOW4		;Yes, we can do that
	JRST OPNL12		;32-bit and not 800BPI, controller can't do it
 ]
];IFN TM10P
IFN TM03S,[
	CAIN A,3		;Specified 6250?
	 JRST OPNL12		;Yep, loser. Else OK
]

;Here on successful open.
; Set up CONO or select word, IOCHNM word, blocksize
;
MTOW4:	PUSHJ P,LSWDEL		;Release MTUSE switch
IFN TM10P,[
	MOVEI A,MTCCHN_3	;Start building CONO word. Set control PI level
IFN TM10A,TRO A,1		;Add in data PI level for non-DF10 controller
	LDB B,[.BP %MMDEN,C]	;Get density from open
	CAIE B,1		;800 BPI
	 BUG INFO,[ILLEGAL TM10 TAPE SPEED SETTING, USING 800BPI]
	MOVEI B,%T1D80		; Yep.	
	DPB B,[.BP %T1CDS,A]	;Set density field of CONO word
	TXNN C,%MM32B		;32-bit mode specified?
	 TXO A,%T1CCD		;No, set core dump mode in CONO
	TXNN C,%MMEP		;User wants even parity?
	 TXO A,%T1COP		;No, set odd parity
	DPB W,[.BP %T1CNU,A]	;Set unit number field of CONO
	MOVEM A,MTCONO(W)	;Save assembled CONO word
];IFN TM10P
IFN TM03S,[
	MOVE A,W		;Get unit number being initialized
	TXNN C,%MM32B		;User asked for industry compatible?
	IFSKP.			;Yes...
	 MOVEI B,4		;Set word-to-frame conversion factor
	 MOVEM B,MTWTFC(W)
	 MOVEI B,%TMFIC		;Set mode in budding select word
	ELSE.			;Core dump...
	 MOVEI B,5		; takes 5 tape frames per word
	 MOVEM B,MTWTFC
	 MOVEI B,%TMFCD		;Set CD format in select word
	ENDIF.
	DPB B,[.BP %TMTFS,A]	;Set format in select word
	TXNE C,%MMEP		;User wants even parity?
	 TXO A,%TMTEP		;Yes, set even parity flag
	LDB B,[.BP %MMDEN,C]	;Get density from open	
	CAIE B,1		;800 BPI specified?
	 SKIPA B,[%TMD16]	;Anything else, use 16
	  MOVEI B,%TMD08	;800, use that
	DPB B,[.BP %TMTDS,A]	;Set density in select word
	MOVEM A,MTSELW(W)	;Set select word for this unit
];IFN TM03S
	LDB B,[.BP %MMRSZ,C]	;Get record size from open call
	TRC B,7			;Convert to power of two
	MOVEI A,10		;Minimum record size is 10 (8.) words 
	LSH A,(B)		;Blocksize = minimum * 2^power
	MOVEM A,MTBLKS(W)	;Save blocksize in words
	LDB A,[.BP <%MMBLK\%MMIMG>,C]
				;Use unit/block and ascii/image bits as index
	HLR C,MTOPTB(A)		;Get input dispatch routine from table
	TLNE D,400000		;Remember D? Skip if user opened in input mode
	 HRR C,MTOPTB(A)	;OK, get output dispatch routine instead
	MOVEM C,(R)		;Save routine in IOCHNM word
	DPB W,[MTXP(R)]		;Set transport number in IOCHNM word
	JRST POPJ1		;Skip return means success

;Here if device is not present
MTODNP:	SETZM MSRAC(W)	;Too bad. Clear status word and report error
	JRST OPNL1

;Here if device off line
MTODOL:	SETZM MSRAC(W)
	JRST OPNL7

;Table of I/O routines
;
MTOPTB:	MTUAIX,,MTUAOX		;Unit Ascii
	MTBIX,,MTBOX		;Block Ascii
	MTUIIX,,MTUIOX		;Unit Image
	MTBIX,,MTBOX		;Block Image



SUBTTL MAG TAPE CLOSE ROUTINES

;MTOCL - CLOSE TAPE OPEN FOR OUTPUT

MTOCL:	LDB W,[MTXP(R)]		;Set up transport number
	SOSL MTUSE(W)		;Decrement transport use counter
	 POPJ P,		;Not only channel open, all done
	AOS MTUSE(W)		;Consistant state in case PCLSR'd
	MOVSI A,%MAERR
	TDNE A,MSRAC(W)		;See if error has occurred
	 PUSHJ P,MTOCL3		;Yes, clean up a few things

	SKIPL MTMDN(W)		;Is there a partially processed buffer?
	 PUSHJ P,MTWBFD		;Yes, write it to tape

	MOVEI B,MGMEOT		;Queue a "Write EOT" command to PI level
	PUSHJ P,MTCMD
	SKIPLE MSCMDC(W)
	 PUSHJ P,UFLS		;Wait till done. 
	MOVEI A,1
	MOVEM A,MGEOFR(W)
	SKIPGE MTMDN(W)		;Active buffer at MP level?
	IFSKP.
	 MOVE A,MTMDN(W)
	 BUG PAUSE,[MT: MP BUF REMAINS AT CLOSE],OCT,A
	 PUSHJ P,MEMR
	ENDIF.
	SKIPGE MGCABN(W)		;Active buffer at PI level?
	IFSKP.
	 MOVE A,MGCABN(W)
	 BUG PAUSE,[MT: PI BUF REMAINS AT CLOSE],OCT,A
	 PUSHJ P,MEMR
	ENDIF.
	SKIPN MSNBOL(W)		;Any buffers on buffer queue?
	IFSKP.
	 BUG PAUSE,[MT:],DEC,MSNBOL(W),[BFRS QUEUED AT CLOSE]
	 PUSHJ P,MTCBFF
	ENDIF.
	SETOM MTUSE(W)		;Nobody is using this transport
	SETOM MTUSR(W)		;No user associated with this transport
	SETZM MSRAC(W)		;reset transport software status word
	POPJ P,

MTOCL3:	SKIPL A,MTMDN(W)	;Have a buffer active at MP level?
	 PUSHJ P,MEMR		;Flush it if so
	SETOM MTMDN(W)
	SETZM MSMPRC(W)		;Say no words left in current buffer
	POPJ P,

;Close routine for MT open for input

MTICL:	LDB W,[MTXP(R)]		;Set up channel data pointer
	SOSL MTUSE(W)		;Last user?
	 POPJ P,		;No, nothing to do
	AOS MTUSE(W)		;Yes, save state for possible PCLSR
	MOVEI T,1		;This crock is because we want to allow
	CAMGE T,MSCMDC(W)	; one outstanding command if rewinding
	 PUSHJ P,UFLS		;Wait till one or less outstanding command
	MOVE T,MSRAC(W)		;Get transport software status
	TLNE T,%MAREW		;Is it rewinding?
	IFSKP.
	 SKIPLE MSCMDC(W)	;No, wait till no outstanding commands
	  PUSHJ P,UFLS
	ENDIF.
	SKIPGE MGCABN(W)	;Any PI-level buffers left?
	IFSKP.
	 MOVE A,MGCABN(W)	 
	 BUG PAUSE,[MT: PI INPUT BUF LEFT AT CLOSE],OCT,A
	 PUSHJ P, MEMR
	ENDIF.
	PUSHJ P,MTRBD		;Release any MP-level buffers
	SETOM MTUSR(W)		;Reset user identifier
	PUSHJ P,MTCBFF		;Free some buffers ?whose+++
	SOS MTUSE(W)		;Decrement use count
	MOVE T,MSRAC(W)		;Get transport software status again
	TLNE T,%MAREW		;Are we rewinding?
	 JRST MTICL2		;Yes, don't have to skip to EOF
	MOVSI A,(%MMNSE)
	TDNE A,(R)		;Check if user wants skip to EOF
	 JRST MTICL2		;No, forget it
	MOVSI A,%MAMSO
	TDNN A,MSRAC(W)		;Has tape moved at all since open?
	 JRST MTICL2		;No...
	SKIPE MGEOFR(W)		;
	 JRST MTICL2		;Note that this disposes of a read-ahead EOF.
	PUSHJ P,MTSTAT
IFN TM10P,[
	MOVE B,MGCMTS(W)
	TXNE B,%T1SBT		;BOT?
	 JRST MTICL2
]
IFN TM03S,[
	TTNN FS,%TMSOL		;Still online?
	 JRST MTICL2		;No, can't very well skip to EOF
	TTNE FS,%TMSBT
	 JRST MTICL2
]
	MOVEI B,MGSPFF		;Space forward to end of file
	PUSHJ P,MTCMD
	SKIPLE MSCMDC(W)	;Wait till command done
	 PUSHJ P,UFLS

MTICL2:	MOVSI T,%MAREW
	ANDM T,MSRAC(W)		;Clear all software status except rewinding
	POPJ P,			;Done

;Free a chain of IO buffers pointed to by MSBUFP(W)
; Count of buffers is in MSNBOL(W)
;
MTCBFF:	SKIPN MSNBOL(W)		;Any buffers on chain?
	 POPJ P,		;No, fine
	DO.
	 HRRZ A,MSBUFP(W)	;Yes, get pointer
	 LDB T,[MLO,,MEMBLT(A)]	;Get pointer to next buffer
	 HRRM T,MSBUFP(W)	;Write that in chain header
	 PUSHJ P,MEMR		;Return buffer to IO pool
	 SOSLE MSNBOL(W)	;Any more to do?
	  JRST TOP.		;Yes, loop back for more
	ENDDO.
	SETZM MSBUFP(W)		;Zero buffer list pointer
	POPJ P,



SUBTTL MAG TAPE INPUT .IOT ROUTINES

	SKIPA B,[SIOKT]
MTUAI:	 MOVEI B,CHRKTI		;Unit ASCII input
	MOVE E,[440700,,5]
	JRST MTREAD

	SKIPA B,[SIOKT]
MTUII:	 MOVEI B,CHRKTI		;Unit image input
	MOVE E,[444400,,1]
	JRST MTREAD

MTBI:	MOVE E,[444400,,1]	;Block input
	MOVEI B,BLKT

MTREAD:	LDB W,[MTXP(R)]		;W <= Transport number
	PUSHJ P,MTIECK		;Check for tape errors first
	MOVEM P,MTPSAV(W)	;Save P for EOF return
	JSP B,(B)		;IO Routine. BLKT,CHRKT,SIOKT
	    MSMPRP(W)		;Pointer to next word
	    MSMPRC(W)		;Count of words remaining
	    SETZ MTRBG		;Get new buffer (SETZ for CHRKT return on EOF)
	    MTRBD		;Discard buffer
	    JRST 4,.		;Unused
	    PUSHJ P,MTRBFW	;Code to wait for new buffer from PI level

;Get a new buffer for I/O code.
; This routine assumes that there is a buffer ready to get
; (i.e. MTRBFW was called)
;
MTRBG:	SKIPG MSNBOL(W)		;Any buffers on list?
	 JRST MTRBG3		;No, must be error or EOF
	CONO PI,UTCOFF		;Stop I/O for a bit
	MTDEQB TT,A		;Dequeue buffer into TT using A as temp
	MOVEM TT,MTMDN(W)	;Remember buffer active at MP level
	CONO PI,UTCON		;Turn I/O back on
	LDB J,[MWC,,MEMBLT(TT)]	;Get word count from buffer
	MOVEM J,MTBLKS(W)	;Save for .MTAPE 13
	LSH TT,10.		;Convert buffer number into address
	JRST SIOBGX		;TT/address, J/word count /E unchanged

;Here if no buffer ready, error or EOF assumed
;
MTRBG3:	SKIPL MSRAC(W) .SEE %MAEOF	;EOF flag set in transport status?
	IFSKP.
	 MOVSI T,%MAEFA		;The EOF is now no longer read-ahead
	 ANDCAM T,MSRAC(W)	;So remember that fact
	 JRST POPJ2		;Double skip to tell I/O code about EOF
	ENDIF.
	PUSHJ P,MTIECK		;Not EOF. Check for error conditions
	BUG HALT,[TAPE READ BUFFER VANISHED] ;No error, shoulda been a buffer

;MAG TAPE READ BUFFER DISCARD ROUTINE
MTRBD:	SKIPGE A,MTMDN(W)	;Have an active MP buffer?
	 JRST MOVTWJ		;Set up T for buffer wait, return
	CONO PI,UTCOFF		;Stop IO for a bit
	SETOM MTMDN(W)		;No active MP buffer
	SETZM MSMPRC(W)		;No data in nonexistant buffer
	LDB TT,[MSEOFP,,MEMBLT(A)]	;Get EOF flag from PI level
	PUSHJ P,MGMEMR		;Return buffer, enable UTC
	JUMPN TT, [ MOVSI A,%MAEOF	;EOF seen?
		    IORM A,MSRAC(W)	;Record EOF in software status
		    JRST .+1 ]
MOVTWJ:	MOVE T,W		;T gets transport number for bfr wait UFLS
	POPJ P,			;Also MTRBG, MTRBFW rely on this setting T


;MAG TAPE READ WAIT FOR DATA ROUTINE
; Called under a UFLS by system IO code to wait for data to arrive.
; Note T, not W, has transport number. T must be set up by some
; previous routine
;
MTRBFW:	MOVE TT,MSRAC(T)	;Get software status of transport
	TLNN TT,%MAEOF\%MAERR	;PI code reports EOF or error?
	SKIPLE MSNBOL(T)	;Or are any buffers available for reading?
	 JRST POPJ1		;SOme interesting condition, unhang
	TLNE TT,%MARAC		;Have we asked for a tape read yet?
	 POPJ P,		;Yes, nothing to do but wait, wait, wait
	PUSH P,W		;This being done under a UFLS. only T good
	PUSH P,B
	MOVE W,T		;Set up transport number in usual place
	MOVEI B,MGREAD		;What we want the PI level to do
	MOVEI T,MSCBL		;Make sure that there is room in command list
	CAMG T,MSCMDC(W)	;If there is room,
	IFSKP.			; we will request a read operation
	 MOVSI TT,%MARAC
	 IORM TT,MSRAC(W)	;Read is active now, or will be shortly
	 PUSHJ P,MTCMD1		;Queue up command request for PI level
	ENDIF.			;If no room for command, all we can do is wait
	MOVE T,W		;Fix up T for UFLS
	POP P,B			;Clean up everything else
	POP P,W
	POPJ P,

SUBTTL MAG TAPE OUTPUT .IOT ROUTINES

	SKIPA B,[SIOKT]
MTUAO:	 MOVEI B,CHRKT		;Unit ASCII output
	MOVE E,[440700,,5]
	JRST MTWRIT

	SKIPA B,[SIOKT]
MTUIO:	 MOVEI B,CHRKT		;Unit image output
	MOVE E,[444400,,1]
	JRST MTWRIT

MTBO:	MOVE E,[444400,,1]	;Block output
	MOVEI B,BLKT

MTWRIT:	LDB W,[MTXP(R)]		;Get transport number
	PUSHJ P,MTIECK		;Check for tape errors first
	HRLZI A,%MAETW
	TDNE A,MSRAC(W)		;PI code think it hit EOT?
	 JRST IOCER9		;Yep, go lose
	JSP B,(B)
	    SETZ MSMPRP(W)	;Pointer to next word
	    MSMPRC(W)		;Count
	    SETZ MTWBFG		;Get new buffer
	    MTWBFD		;Write out buffer to tape
	    JRST 4,.
	    TRNA		;No wait for buffer routine needed

;MAG TAPE WRITE - BUFFER GET ROUTINE

MTWBFG:	PUSHJ P,MTIECK		;Check for tape errors
	PUSHJ P,TCALL		;Turn off UTC
	    JRST IOMQ		;Try to get buffer
	 POPJ P,		;Return noskip if no buffers available
	MOVEM A,MTMDN(W)	;Store active buffer number
	MOVEI T,MUMGB
	DPB T,[MUR,,MEMBLT(A)]	;Tell world that it is a mag tape buffer
	DPB W,[MSCHN,,MEMBLT(A)] ;Store channel number in buffer header
	SETZM TT
	DPB A,[121000,,TT]	;Convert block number in A to address in TT
	MOVE J,MTBLKS(W)	;Get block size of write
	JRST SIOBGX

;MAG TAPE WRITE - BUFFER FINISHED ROUTINE
; General IO code has filled a buffer or otherwise finished with it.
; Queue the buffer for writing if there is anything in it.

MTWBFD:	PUSHJ P,MTIECK		;Check for tape errors
	SKIPGE A,MTMDN(W)	;Have an active buffer?
	 POPJ P,		;No, nothing to do
	MOVEI T,MSCBL		;See if there is any room in command list
	CAMG T,MSCMDC(W)
	 PUSHJ P,UFLS		;Wait for room in command list
	MOVE T,MTBLKS(W)	;Get blocksize we are writing
	SUB T,MSMPRC(W)		;Subtract number of words remaining in buffer
	CONO PI,UTCOFF		;Lock up while frobbing buffer lists
	SETOM MTMDN(W)		;Clear active buffer
	SETZM MSMPRC(W)		;Clear free word in buffer count
	JUMPE T,MEMR		;Nothing in buffer, just return block
	DPB T,[MWC,,MEMBLT(A)]	;Store word count
	MTENQB A		;Queue buffer on to-PI list
	CONO PI,UTCON		;Finished mucking with buffer list
	PUSH P,B
	MOVEI B,MGWRIT		;Issue a write request to PI level
	PUSHJ P,MTCMD
	MOVSI B,%MANWT
	ANDCAM B,MSRAC(W)	;Note that we have written something to tape
	SETZM MTCEFW(W)		;Note no EOF's written at end of tape
	JRST POPBJ		;Restore B and return

SUBTTL MTCMD - GIVE COMMAND TO PI LEVEL

;PUSHJ P,MTCMD
	;RH(W) has transport number
	;RH(B) has address of PI level routine to execute

MTCMD:	MOVEI T,MSCBL		;Pending command list length
	CAMG T,MSCMDC(W)	;Count of commands currently in list
	 PUSHJ P,UFLS		;Wait for room in pending command list 
	CONO PI,UTCOFF		;Grab machine, keep count accurate
MTCMD1:	MOVE T,MTCMBP(W)	;Get pointer to next slot in command list
	AOBJN T,MTCMD2		;Increment slot and check for wraparound
	 SUB T,[MSCBL,,MSCBL]	;Reached end of list, ring it
MTCMD2:	MOVEM B,(T)		;Store new command
	MOVEM T,MTCMBP(W)	;Store new pointer
	AOS MSCMDC(W)		;Indicate one more command in list
	JRST MSTRTR		;Go start up PI routine

SUBTTL MTIECK - CHECK FOR IOC ERROR

;PUSHJ P,MTIECK
	;Returns +1 if no error
	;Gives IOC error to user if error - never returns.

MTIECK:	PUSH P,A
	MOVE A,MSRAC(W)		;Get transport software status
	TLNN A,%MAERR		;PI level report an error?
	 JRST POPAJ		;No, all is OK
	SKIPLE MSCMDC(W)	;Wait for PI level to finish cleaning up
	 PUSHJ P,UFLS		; before throwing away buffers
	SKIPL A,MTMDN(W)	;Have an active MP-level buffer?
	 PUSHJ P,MEMR		;Yes, throw it away
	SETOM MTMDN(W)		;No active MP buffer
	SETZM MSMPRP(W)		;No place to get/put next character
	SETZM MSMPRC(W)		;No room to do it anyway
	PUSHJ P,MTCBFF		;Free any buffers waiting for MP processing
	POP P,A
	MOVE T,MGCMTS(W)	;Get transport status data from PI level
IFN TM10P,[
	TRNE T,%T1STH\%T1SIO	;Transport hung or illegal operation?
	 JRST IOCER1		;Yes, report device error
	TRNE T,%T1SPE\%T1SRC\%T1SRL\%T1SDL\%T1SBT	;Gruesome errors
	 JRST IOCER3		;Report irrecoverable data error
	TRNE T,%T1SET		;(Real) End of Tape?
	 JRST IOCER9		;Device Full error
];IFN TM10P
IFN TM03S,[
	MOVE I,MGCMTC(W)	;Get controller transport status from PI level
	MOVE J,MGCMTS(W)
	TTNE CS1,%TM1MP		;Control bus parity error?
	 JRST IOCER1
	TTNN CS1,%TM1TE		;Transfer error? Could be TM03 or RH11
	IFSKP.
	 TTNE CS2,%TM2DL\%TM2UP\%TM2NF\%TM2NM\%TM2PE\%TM2MT\%TM2MP
	  JRST IOCER1		;Non-data errors
	 TTNN FS,%TMSES		;Formatter error?
	 IFSKP.
	  TTNE ERR,%TMEUS\%TMECT\%TMENX\%TMEMD\%TMEFS\%TMEMC\%TMERM\%TMEIR\%TMEIF
	   JRST IOCER1		;Fatal formatter errors
	  TTNE ERR,%TMECE\%TMECS\%TMEFC\%TMENG\%TMEFL\%TMEIC
	   JRST IOCER3		;Non-recoverable data errors
	 ENDIF.
	ENDIF.
	TTNE FS,%TMSET		;End of tape?
	 JRST IOCER9
];IFN TM03S
	MOVE A,MGEOFR(W)	;Check for logical EOT
	CAIL A,2		;Read two EOF's since last record?
	 JRST IOCER9		;Yes, report EOT
	JRST IOCER3		;No, give irrecoverable data error for now

SUBTTL MAG TAPE .STATUS ROUTINE
;Building a status word in D.
;Routine sets:
; 1.7-1.9 open modes
; 2.3 BOT
; 2.4 EOT
; 2.5 9 track (0 = 7 track)
; 2.6 IBM mode (0 = coredump)
; 2.7 Transport idle (no pending command)
; 2.8 EOF (last thing seen was a tape mark)
;
STAMTC:	
;Open modes
	LDB W,[MTXP(R)]		;Get transport number
	DPB A,[60300,,D]	;Open mode
	PUSHJ P,MTSTAT		;Get tape status from transport
;EOT?
	LDB A,[.BP %MAETW_22,MSRAC(W)]	;EOT on write
	LDB B,[.BP %MAETR_22,MSRAC(W)]	;EOT on read
	IOR A,B
	DPB B,[140100,,D]	;Tape at EOT?
IFN TM10P,[
;7 or 9 track drive?
	LDB A,[20100,,J]	;Get 7/9 track bit
	TRC A,1
	DPB A,[150100,,D]	;7 or 9 track drive
	.ERR Missing TM10 .STATUS code at STAMTC
]
IFN TM03S,[
;A bunch of new stuff. BOT?
	SETZ A,
	TTNE FS,%TMSBT		;Transport is at BOT?
	 SETO A,
	DPB A,[130100,,D]	;BOT.
;7 or 9 track drive?
	SETOM A			;TM03 is always 9-track
	DPB A,[150100,,D]	;7 or 9 track drive
;Core dump or 32 bit mode?
	MOVE A,MTWTFC(W)	;Get word to frame conversion. 4 or 5
	TRC A,1			;Flip last bit. Last bit now on iff 32bit mode
	DPB A,[160100,,D]	;Sorry.
;Formatter busy?
	SETO A,
	SKIPLE MSCMDC(W)	;Idle if no pending commands
	 SETZ A,
	DPB A,[170100,,D]
;EOF last thing seen?
	SETZ A,
	TTNE FS,%TMSTM		;Last thing seen was a tape mark?
	 SETO A,
	DPB A,[200100,,D]	;EOF. 
];IFN TM03S
	POPJ P,

;RCHST ROUTINE FOR MAG TAPE

RCHMGT:	HLRZ E,(R)		;Get open mode from IOCHNM word
	LDB J,[MTXP(R)]		;Get the mag tape drive number
	MOVSI J,'MT0(J)		;Return that drive's device name
	TRZ E,(.BM MTXP)	;Clear drive # field in OPEN modes
	JRST POPJ1		;Skip so that J overrides built-in device name


SUBTTL MAG TAPE PI LEVEL

;Request magtape interrupt from MP-level software
; Must be called with I/O interrupts off (CONO PI,UTCOFF)
; This is called every 1/2 second by the ITS slow clock routine
; 
MSTRTR:	SETZM MGTBZY		;No busy tapes, flag software interrupt
	CONO PI,MTCRQ		;Request interrupt on magtape channel
	CONO PI,UTCON		;Reenable IO interrupt system
	POPJ P,			;That is all

;Handle tape interrupt requested by software or other nonspecific cause
; Get here via non-vectored MTCCHN interrupt on the KS
;
MGSBRK:	AOS MGTBZY		;Note interrupt level is busy
	SETOM MGSFTI		;Note software interrupt in progress
	MOVEI B,NMTCS-1		;Loop over all transports
	SKIPG MSCMDC(B)		;Any commands in command list?
MGSBK1:	 SOJGE B,.-1		;No, try next transport
	SKIPGE B		;Found command or finished scan. Which?
	 JRST MGSBK2		;Finished scanning. Go check other things
	MOVE W,B		;Have a command. Set up transport number
	PUSH P,B		;Save B over call to command routine
	MOVSI B,%MACTH
	TDNN B,MSRAC(W)		;Core allocator saying go away?
	 PUSHJ P,MGXGO		;No, go start requested routine
	POP P,B			;Restore transport count
	JRST MGSBK1		;Loop back for another transport

MGSBK2: SETZM MGSFTI		;Clear software interrupt flag
	JRST MGEX		;That's all for now
;This probably should check for lost IE bits on TM03

;Handle interrupt caused by specific hardware conditions
; Get here via vectored interrupt on the KS
;

IFN TM10P,[
; A - result of CONI MTC,
; C - result of CONI MTS,
;
MGHBRK:	SKIPGE W,MGUNIT		;Get unit we are expecting interrupt on
	 JRST MGUBRK		;None? Probably MP level bailed out. Punt.
	LDB B,[MUNITF,,A]	;Get unit that controller is squacking about
	CAME W,B		;Same unit?
	 BUG HALT,[MT PI CODE LOST TRANSPORT]
	MOVE J,C
	PUSH P,[MGEX]		;Cause POPJ P, in handler to dismiss interrupt
IFN TM10B,[
	TXNE C,%T1SCP\%T1SNM\%T1SDP	;Check for DF10 errors
	 JRST MGERR		;Yes, go to error routine
];TM10B
	TXNE C,%T1SIO		;Illegal operation error?
	 JRST MGERR		;Yes, go to error routine
	JRST MGXGO		;No, go handle interrupt

;Here to unbreak on unexpected hardware interrupt
;
MGUBRK:	LDB W,[MUNITF,,A]	;Who's the loser, anyway
	MOVE B,MTCONO(W)	;Get prototype CONO word for that transport
	CONO MTC,(B)		;Select offending unit
	CONO MTS,31		;Clear interrupt, DF10, stop any op in progress
	JRST MGEX		;Go dismiss interrupt
];IFN TM10P

IFN TM03S,[
;Check for active unit. If none, go directly to inactive slave
;polling code. If active unit,check that controller is talking about
;this unit, then jump to its routine (MGXGO). When that finishes,
;see if controller is still showing attention, and check status
;of other transports if so.
;
;Well, almost.

	EBLK
MGHBRK:	0
	BBLK
	JSR UTCSAV		;Save AC,s set up interrupt P PDL
	AOS MGTBZY		;Note interrupt level is alive
	SKIPGE W,MGUNIT		;Some transport waiting for an interrupt?
	 JRST MGNOUW		;Nope, either Slave Status Change or lossage
	IORDI B,%TMTC		;Get transport the TM03 is thinking about
	ANDI B,7
	CAME B,W		;Same as the one we were waiting for?
	 BUG HALT,[MT PI CODE LOST TRANSPORT]
	PUSHJ P,MGXGO		;Go jump into this unit's handler

MGNOUW:
IFN NMTCS-1,[
.ERR Missing multi-transport code at MGNOUW!
If more than one unit, have to see if this is an interrupt caused by a
SSC (slave status change), and if so poll all the inactive units and
update their software status (rewinding, on/off line, etc.) With only
one transport we currently avoid this by keeping the unit active
(MGUNIT set) as long as it is doing *anything*, and giving it all the
hardware interrupts. See rewind code, too.
];IFN NMTCS-1
	JRST MGEX		;Go dismiss interrupt
];IFN TM03S

;Interrupt level control flow
;
; Each transport maintains the current state of its PI-level action
; routines on a seperate (per-transport) PDL with its SP in Q. This
; allows you to save the current state, dismiss the interrupt, and
; resume where you left off when the next interrupt for this transport
; arrives.
; 
; Subroutine linkage between routines handling a per-drive PI-level
; action is with PUSHJ Q,xxx and POPJ Q,
;
; The MP level queues commands for the PI level to process on a per-
; transport queue at MGSBx (x=unit number)
;
; Ths general interrupt code transfers to the per-drive actions by
; jumping to MGXGO, which will load Q from the saved state of the
; current drive and start up whatever it finds there. This may be
; the MGNCMD routine, which will start a new command, or it may be
; the middle of an in-progress PI-level command handler.
;
; PI-level action routines can relinquish control in several ways:
;
;  PUSHJ Q, CPOPJ will dismiss the current interrupt, arranging for
;  control to return to the instruction following the PUSHJ when the
;  next interrupt for this unit occurs.
;
;  JRST MGOVER will dismiss the current interrupt, arranging for the
;  currently executing subroutine to be restarted "from the top" when
;  the next interrupt for this unit occurs.
;
;  A simple POPJ P, will dismiss the current interrupt, arranging for
;  the routine that called this one to be started at the point immediately
;  after the call when the next interrupt comes in. This is generally not
;  useful except from first-level action routines, where a POPJ P, will
;  indicate that you have finished handling the current command and cause
;  the next interrupt to fetch and start execution of a new command from the
;  queued command list by transferring to MGNCMD. Got that?

;Transfer control to whatever the per-transport code wants to do.
;
MGXGO:	MOVE Q,MGQDLP(W)	;Getsaved Q PDL pointer for current transport
	PUSHJ P,QPOPJ		;"Return" to whatever is on top of Q PDL,
				; arranging for POPJ P, to return to here

	MOVEM Q,MGQDLP(W)	;Save current Q PDL for next time
	POPJ P,			;Return, dismissing this interrupt

;Dismiss interrupt, arranging for next interrupt to restart routine
; we are currently executing (rather than either continuing here or
; starting a new command)
;
MGOVER:
IFN TM10P,[
	CONSZ MTS,%T1STH\%T1SIO\%T1SCE	;Tape hung/ill op/DF10 error, 
	 JRST MGERR		; go handle error condition
]
	SOS (Q)			;Decrement address at top of Q PDL, now
				; points to PUSHJ Q, <us> instruction so
				; that we will get called again from MGXGO
				; at next interrupt dispatch
	POPJ P,			;Return to P PDL caller, dismissing interrupt


;This routine is always the last "command" in a transport command list
; Wraps the command list BP around the the beginning of the list,
; then gets and executes the next command.
;
MGRCV:	MOVNI A,MSCBL+1		;Get negative length of command list
	ADDM A,MGCMBP(W)	;Wrap command extraction pointer back to start
MGNCM1:	ILDB B,MGCMBP(W)	;Get new command from list
	JRST (B)		;And jump to it

;This code is always the first (bottom) thing in a transport's Q PDL
; It is therefore executed whenever MGXGO transfers control to the
; Q PDL and there is no command in progress.
;
MGNCMD:	AOBJN Q,MGNCM1		;Got here w/ POPJ Q,. Fake PUSHJ to restore Q,
				; then go get the next command
	
	BUG			;Q PDL pointer clobbered

QPOPJ1:	AOS (Q)
QPOPJ:	POPJ Q,


;WAIT FOR JOB DONE BIT TO SET
; Called from PI-level command routines to wait for end of command.
; Called with PUSHJ Q,MGWTJD
;
; Returns +1 to caller if unusual termination
;         +2 to caller if operation terminated normally
; 
;	I,J contain tape status:
;	 On TM10, I=CONI MTC,
;		  J=CONI MTS
;	 On TM03S, I=%TMCS1,,%TMCS2 (controller status),
;		   J=%TMFS,,%TMERR (formatter status)

IFN TM10P,[
MGWTJD:	CONI MTS,J		;Get transport status
	TXNE J,%T1STH\%T1SIO	;Transport hung or Illegal operation?
	 JRST MGERR		;Yes, job-done isn't ever going to get set
MGWJD1:	CONI MTS,J		;Get status again
	SKIPN MGJDTI		;Is there a time out set?
	IFSKP.
	 MOVE T,TIME		;Yes, check it. Get current time
	 CAML T,MGJDTI		;Smaller than specified timeout time?
	  JRST MGERR		;No, lose
	ENDIF.
	TXNN J,%T1SJD		;JOB-DONE bit set?
	 JRST MGOVER		;No, we want to wait for it
	CONI MTS,J		;Job Done. Get status
	CONI MTC,I		;Get CONI MTC
	MOVEM J,MGCMTS(W)	;Save for MP level
	MOVEM I,MGCMTC(W)
IFN TM10B,[
	SKIPE MGWCW		;Want to wait for control word?
	 PUSHJ Q,MGWCWC		;Check to see if it is written
	SETZM MGWCW		;Clear wait-for-control-word request flag
]
	CONO MTS,30		;Clear channel conditions
	MOVE B,MTCONO(W)
	CONO MTC,(B)		;Release mtc, clear job done bit
IFN TM10B,[
	TXNE J,%T1SCP\%T1SNM\%T1SDP	;Check for channel errors
	 POPJ Q,		;Channel error, return nonskip
]
	TXNE J,%T1STH\%T1SIO\%T1SPE\%T1SRC%\T1SRL\%T1SDL\%T1SBT
	 POPJ Q,		;Random other things, not necessarily errors
	JRST QPOPJ1		;Success
];IFN TM10P
IFN TM03S,[
MGWTJD:	
MGWJD1:	IORDI T,%TMCS1		;Get controller status
	TXNN T,%TM1GO		;Go bit still set?
	 JRST MGWJD2		;No, command is finished. Go check errors
	SKIPE T,MGJDTI		;Is there a time out set? Get it if so.
	 CAML T,TIME		;Past timeout time
	  JRST MGOVER		;No timeout or not timed out yet, go wait
	JRST MGERR		;Timed out. Go check transport

;Here if action has terminated. Set up status bits before returning
MGWJD2:	HRLZ I,T		;%TMCS1,,?
	IORDI T,%TMCS2
	HRR I,T			;I = %TMCS1,,%TMCS2
	IORDI J,%TMERR
	IORDI T,%TMFS
	HRL J,T			;J = %TMFS,,%TMERR - I,J now in standard form
	MOVEM J,MGCMTS(W)	;Save for MP level
	MOVEM I,MGCMTC(W)
	TTNE CS1,%TM1MP		;Massbus control parity error
	 JRST MGERR		;Who knows what the transport is doing
	TTNN CS1,%TM1TE		;Controller error?
	 TTNE FS,%TMSES		;Formatter error?
	  POPJ Q,		;Yes, return +1 for closer examination
	JRST QPOPJ1		;Success
];IFN TM03S

;Select the transport given by W
; Returns +1 with transport selected, status in I,J
; May dismiss and wait for a while first
;
MGGXPT:	
IFN TM10P,[
	CONSO MTS,%T1SNU	;Can controller select new unit right now?
	 JRST MGOVER		;No, wait for it
	MOVE T,TIME
	ADDI T,10.*30.		;Time out in 10. seconds
	MOVEM T,MGJDTI		;Set up job-done timeout
	MOVEM W,MGUNIT		;Note unit waiting for hardware interrupt
	MOVE B,MTCONO(W)	;Get CONO word for new unit
	CONO MTC,MNOPIN(B)	;NOP, but interrupt when new drive is ready
	PUSHJ Q,MGWJD1		;Wait for job done
	 JFCL			;Ignore any errors
	SETZM MGJDTI		;No more timeout
	SETOM MGUNIT		;Not waiting any more
	POPJ Q,			;Return to caller
];IFN TM10P
IFN TM03S,[
	IORDI T,%TMFS		;Get current formatter status
	TXNN T,%TMSES\%TMSSC	;Showing error or slave status change?
	IFSKP.			;Yes, clear errors before going ahead
	 IOWRI W,%TMTC		;Select slave
	 MOVEI A,10.		;Try 10 times to clear drive
	 MOVEI B,%TMCLR
	 DO.
	  IOWRI B,%TMCS1	;Write drive clear command
	  IORDI T,%TMFS		;Get status
	  TXNN T,%TMSES		;Still have error
	   JRST ENDLP.		;No, done
	  SOJG A,TOP.		;Retry up to 10 times
	  JRST MGERR		;Else give up
	 ENDDO.
	ENDIF.
	MOVE T,MTSELW(W)	;Get desired settings
	IOWRI T,%TMTC		;Tell TM03
	MOVEI T,%TMNOP		;You may need this to set status values
	IOWRI T,%TMCS1		; but I'm not really sure
	IORDI T,%TMCS1
	PUSHJ Q,MGWJD2		;Go set status registers appropriately
	 JFCL			;Maybe should check non-existant slave?
	POPJ Q,
];IFN TM03S

;MGERR - HANDLE TAPE ERROR
;
;Called from PI level command routines which detect a tape or controller error
; Flags error in software status word, records error status in MGExxx.
; Flushes command in progress by resetting Q PDL to base.
; Flushes commands in PI-level command queue by resetting queue pointers.
; Flushes any buffers which might be queued for writing.
; Returns to P PDL, to wait for new commands.

MGERR:
IFN TM10P,[
	CONI MTS,MGEMTS		;Get transport status for MP level analysis
	CONI MTC,MGEMTC
]
IFN TM03S,[
	IORDI T,%TMCS1		;Get controller and transport status into
	HRLM T,MGEMTC		; standard form
	IORDI T,%TMCS2
	HRRM T,MGEMTC
	IORDI T,%TMFS
	HRLM T,MGEMTS
	IORDI T,%TMERR
	HRRM T,MGEMTS
	MOVE I,MGEMTC		;Get status to standard place for macros
	MOVE J,MGEMTS
];IFN TM03S
	MOVSI T,%MARAC		;Read not active, for sure
	ANDCAM T,MSRAC(W)
	SETZM MSCMDC(W)		;No commands active
	SETOM MGUNIT		;Clear unit wait flag
	SETZM MGJDTI		;No job-done timeout
IFN TM10B,[
	TXNE J,%T1SCP\%T1SNM\%T1SDP	;Channel error?
	 BUG PAUSE,[MTAPE: CHANNEL ERROR, STATUS=],OCT,J,[MICWA+1=],OCT,MICWA+1,[MIOWD=],OCT,MIOWD
];TM10B
IFN TM03S,[
	.ERR UBA errors? More gentle cleanup? 
	TTNE CS1,%TM1MP\%TM1TE	;Cbus parity or transfer error?
	 TTNE FS,%TMSES		;And no formatter error?
	  SKIPA			;Yes and yes (no) (what?). Controller error
	   BUG INFO,[MTAPE: RH11 ERROR, STATUS=],OCT,MGEMTC,[TM STATUS=],OCT,MGEMTS
	TTNE FS,%TMSES		;Formatter error?
	 BUG INFO,[MTAPE: FORMATTER ERROR, STATUS=],OCT,MGEMTS
	PUSHJ P,TMINIC		;Go reinit controller
];IFN TM03S
	SKIPL A,MGCABN(W)	;Any buffers owned by PI routines?
	 PUSHJ P,IMEMR		;Yes, give them back to system
	SETOM MGCABN(W)		;No PI buffer any more
	HRLZI B,%MAERR		;Flag error detected by PI level
	IORM B,MSRAC(W)		;Store in transport status word
	MOVE Q,[-MGQDLL,,MGQD0-1];Reset Q PDL to base of this unit's stack,
	MOVE T,MSLCTB		; thus flushing whatever command is in progress
	IMUL T,W		;Find offset to base of Q PDL for this unit
	ADD Q,T			;Add to prototype unit 0 QDP pointer,
	PUSH P,Q		; and save it away
	MOVE Q,[-MSCBL-1,,MSCB0-1]	;Get command list input pointer
	ADD Q,T			;Offset it to this unit's command queue,
	MOVEM Q,MTCMBP(W)	;And save it as new MP (input) queue pointer
	MOVE Q,[4400,,MSCB0-1]	;Get new command list output pointer
	ADD Q,T			;Offset correctly for this unit,
	MOVEM Q,MGCMBP(W)	;And save as PI (output) command queue pointer
	MOVSI B,%MAREW		;Does initting xport stop TM03 rewind?
	ANDCAM B,MSRAC(W)	;Say not rewinding
	SKIPN MSCRW(W)		;If reading,
	 JRST POPQJ		; we're done. Restore Q and exit
	SKIPN MSNBOL(W)		;Writing. Any buffers on list?
	 JRST POPQJ		;No, restore Q and exit
	DO.			;Here to free buffers on IO list
	 HRRZ A,MSBUFP(W)	;Get buffer pointer from head of chain
	 LDB T,[MLO,,MEMBLT(A)]	;Get back pointer (next buffer)
	 HRRM T,MSBUFP(W)	;Make next buffer head of chain
	 PUSHJ P,IMEMR		;Free current buffer
	 SOSLE MSNBOL(W)	;Decrement count, check for more
	 JRST TOP.		;More, loop back
	ENDDO.
	SETZM MSBUFP(W)		;Mark list as empty
	JRST POPQJ		;Restore Q and return

IFN TM10B,[
;Wait for control word to get written
;
MGWCWC:	SKIPE MICWA+1		;Control word written?
	 POPJ Q,		;Yes, return to caller
	CONO MTS,4		;No, tell DF10 to write it
MGWCW1:	SKIPE MICWA+1		;Written yet?
	 JRST MGWCW2		;Good, done
	PUSHJ Q,CPOPJ		;Not written. Dismiss interrupt and wait
	JRST MGWCW1		;We're back. See if written yet.

MGWCW2:	MOVE I,MGCMTC(W)
	MOVE J,MGCMTS(W)	;Restore status
	TLO J,10		;Set control word written
	POPJ Q,			;Return to caller
];IFN TM10B

IFN TM10A,[
MGDCSI:	SKIPA A,[BLKI MTC,MIOWD]
MGDCSO:	 MOVE A,[BLKO MTC,MIOWD]
	MOVEM A,MAGLOC
	MOVE A,[JSR MGDBRK]
	MOVEM A,MAGLOC+1
	POPJ Q,
];IFN TM10A

IFN TM03S,[
;Note this one's called on P PDL
TMINIC:	IORDI A,%TMTC		;Get TC reg value
	ANDI A,MTSELM		;Keep interesting bits only
	IORDI B,%TMCS2		;Get CS2
	ANDI B,7		;Keep selected "drive" (TM03) number only
	MOVEI T,%TM2CC
	IOWRI T,%TMCS2		;Clear controller logic
	IOWRI B,%TMCS2		;Reselect TM03
	IOWRI A,%TMTC		;Reselect transport
	MOVX T,%TMCLR		;Do a formatter clear command
	IOWRI T,%TMCS1
	POPJ P,
];IFN TM03S


SUBTTL MAG TAPE PI LEVEL WRITE

MGWRIT:	PUSHJ Q,MGGXPT		;Select desired transport
IFN TM10P,[
	TXNE J,%T1STH\%T1SRW\%T1SWL	;Write locked, hung or rewinding?
	 JRST MGERR		;Not a good thing
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line?
	  JRST MGERR		;No or no, can't do operation
	TTNE FS,%TMSWL		;Transport write locked?
	 JRST MGERR		;Shouldn't happen, we checked at open.
]
	MOVSI A,%MACTH
	TDNE A,MSRAC(W)		;Core alloc want quit temporarly?
	 JRST [	PUSHJ Q,CPOPJ	;OK, wait a while
		JRST MGWRIT ]	;Then go back and try again
	MOVEM W,MGUNIT		;Set unit
	HRREI B,-MGNRTY		;Number of retries
	MOVEM B,MGERRC(W)	;Store error count
	MTDEQB A		;Get buffer to write off list
	MOVEM A,MGCABN(W)	;Note this buffer active at PI
	LDB B,[MWC,,MEMBLT(A)]	;Get word count from buffer
	MOVNS B			;B -> -COUNT
	HRLZS B			;B -> -COUNT,,0
IFN KL10P,LSH B,4		;Shift if KL10 data channel
IFN KL10P,MOVE R,A		;Save core page number for cache sweep
IFE TM03S,[			;TM03S IOWD is -COUNT,,ITS PAGE
	LSH A,10.		;Convert buffer number to memory address
	SUBI A,1		;Address - 1
]
	HRRM A,B		;B -> -COUNT,,ADDRESS-1; an IO word
IFN TM10B,DATAO MTS,[MICWA]	;Tell DF10 where IO channel program is
IFN TM10A,PUSHJ Q,MGDCSO	;
	MOVEM B,MIOWD		;Put IO word where controller looks
	MOVEM B,LMIOWD		;Remember what we're about to do
IFN TM10P,SETZM MIOWD+1		;Stop after one operation
IFN KL10P,[
	PUSHJ P,CSHSWP		;Unload buffer from cache into core
	  CAI
]
MGWRT2:
IFN TM10P,[
	MOVE B,MTCONO(W)	;Get prototype CONO word
	CONO MTC,MWRITE(B)	;Perform WRITE operation
]
IFN TM03S,[
	HLRE A,MIOWD		;Get PDP10 word count
	ASH A,1			; * 2
	IOWRI A,%TMWC		; = number of unibus words to transfer
	HLRE A,MIOWD		;Get PDP10 word count again
	IMUL A,MTWTFC(W)	;Convert to tape frame count
	IOWRI A,%TMFC		;Set tape frames to write
	HRRZ A,MIOWD		;Get ITS page to transfer to
	LSH A,1			;Convert ITS pg # to DEC pg #
	TXO A,%UQVAL		;Valid mapping, I should hope
	IOWRI A,UBAPAG+IUTPG_1	;Set up first half of UBA mapping
	TXO A,1			;Next DEC page number
	IOWRI A,UBAPAG+IUTPG_1+1	;Set second half of UBA mapping
	MOVEI A,IUTPG_14		;Unibus address to DMA to
	IOWRI A,%TMBA		;Tell controller
	MOVX A,%TM1IE\%TMWRT	;Write, enable interrupts
	IOWRI A,%TMCS1		;Start controller
]
	PUSHJ Q,MGWTJD		;Wait for job done
	 JRST MGWERR		;Error, go attempt to repair

;Here if tape written OK
MGWRT3:	MOVE A,MGCABN(W)	;Get buffer number
	HRLZI B,%MAETW+%MAERR	;End of tape check
IFN TM10P,TXNE J,%T1SET		;Controller says we're at EOT?
IFN TM03S,TTNE FS,%TMSET
	 IORM B,MSRAC(W)	;Tell MP
	LDB T,[MUR,,MEMBLT(A)]	;Get buffer flavor from buffer
	CAIE T,MUMGB		;Still a magtape buffer?
	 BUG HALT,[MT BUF CHANGED INTO SOME OTHER KIND??]
	SETOM MGCABN(W)		;No buffer active at PI anymore
	PUSHJ P,IMEMR		;Return buffer to system
	MOVSI A,%MAMSO		;Note tape has moved since open
	IORM A,MSRAC(W)
	;JRST MGCMDR ;Falls through

;General exit routine for most PI-level command handlers
; Check command queue for more commands.
;  If no further commands, dismiss interrupt.
;  If further commands queued, wait for this one to finish, then fetch
;   and begin processing of next command.
;
MGCMDR:	SOS MSCMDC(W)		;Decrement command count
	SETOM MGUNIT		;Reset active unit
	SKIPG MSCMDC(W)		;Any more commands?
	 JRST MGCMR1		;No. Go see about other units
IFN TM10P,[
	MOVE B,MTCONO(W)
	CONO MTC,MNOPIN(B)	;Tell controller to interupt when unit ready
	MOVEM W,MGUNIT		;Set up unit number again for MGWTJD
	PUSHJ Q,MGWTJD		;Go wait for unit to finish current command
	 JRST MGERR		;Oops
	SETOM MGUNIT		;No unit waiting any more
];IFN TM10P
	POPJ Q,			;Return on Q PDL. Most likely this will
				; transfer control to MGNCMD routine at
				; base of PDL, which will fetch and execute
				; next command.

MGCMR1:
IFN NMTCS-1,[
;Ought to check other transports for commands to start.
; For now, will get started by 1/2 second timeout, if nothing else.
	.ERR Unfinished multi-transport code at MGCMR1
];IFN NMTCS-1	
	POPJ P,			;No more, return on P to dismiss interrupt



;CONTROLLER DETECTED ERROR ON WRITE OPERATION
; Retry, or give up, or write some blank tape and then try again, 
; depending on the type of error.
;
MGWERR:
IFN TM10P,[
IFN TM10B,SETZM MIOWD		;Seems to do one record anyway
IFN TM10A,[	
	MOVE A,[-1,,MGVTC-1]
	MOVEM A,MIOWD		;Specify one record to space back cmd.
]
	TXNE J,%T1STH\%T1SRW\%T1SIO\%T1SET\%T1SWL	;Bad types of errors
	 JRST MGERR		;OK, give up
	AOSL MGERRC(W)		;Still retrying?
	 JRST MGERR		;No, give up
	MOVE B,MTCONO(W)
	CONO MTC,MSPRR(B)	;Space reverse one record
	PUSHJ Q,MGWTJD		;Wait for job done
	 JRST MGERR		;Error
	MOVE B,LMIOWD		;Get last operation's IOWD
	MOVEM B,MIOWD		;We're going to try it again
	MOVE B,MTCONO(W)	;Get prototype CONO
	CONO MTC,14000(B)	;Write with extended EOR gap
	PUSHJ Q,MGWTJD		;Wait for completion
	 JRST MGWBT		;Lost again, retry some more
	JRST MGWRT3		;Worked, return to normal write sequence
];IFN TM10P
IFN TM03S,[
	TTNE ERR,%TMERM\%TMEUS\%TMEFS\%TMEIR\%TMEIF\%TMEMC\%TMECT\%TMENX\%TMEOI
	 JRST MGERR		;Fatal errors
	TTNN FS,%TMSOL		;On line?
	 JRST MGERR		;This isn't so good either
	TTNE ERR,%TMEFC\%TMENG\%TMEMD\%TMEIC\%TMECE\%TMECS\%TMEFL
	 JRST MGWER1		;Error which retrying might fix

;Formatter seems happy. Check channel status before returning
; Currently this only catches the case where there was an RH11 error
; but no TM03 error, and thus should always end up jumping to MGERR.
; Formatter errors on which it might be OK to get here are %TMECE,
; %TMECS, %TMEFL, but someone needs to test this...
	TTNE CS1,%TM1TE		;Transfer error?
	 TTNE FS,%TMSES		;And not formatter error?
	  CAIA
	   JRST MGERR		;Just lose for now
	BUG INFO,[MT: ACCEPTING QUESTIONABLE WRITE OPERATION]
	JRST MGWRT3

;Retry errors
MGWER1:	AOSL MGERRC(W)		;Still retrying?
	 JRST MGERR		;No, give up
	BUG INFO,[MT: RETRYING WRITE OPERATION]
	PUSHJ P,TMINIC		;Hit controller with hammer
	MOVEI B,1
	MOVEM B,MGSPCD(W)	;1 record
	PUSHJ Q,MGSPCR		;Reselect transport space back a record
	TTNE FS,%TMSTM		;Stopped on EOF?
	 SOSGE MGEOFR(W)	;Yes, one less EOF in front of us
	  SETZM MGEOFR(W)	;But never less than zero
	PUSHJ P,TMINIC		;Hit controller with hammer again
	PUSHJ Q,MGGXPT		;Set up transport again
	MOVEI B,%TMER3\%TM1IE
	IOWRI B,%TMCS1		;Erase a few inches of tape
	PUSHJ Q,MGWTJD		;Wait for job done
	 JRST MGERR		;Can't get anything right...
	MOVE B,LMIOWD		;Get last operation's IOWD
	MOVEM B,MIOWD		;We're going to try it again
	JRST MGWRT2		;Go back to write code and retry operation
];IFN TM03S


SUBTTL MAG TAPE PI LEVEL READ

MGREAD:	PUSHJ Q,MGGXPT		;Select desired transport
MGRD0:	
IFN TM10P,[
	TXNE J,%T1STH\%T1SRW\%T1SET	;Unit hung, rewinding, or EOT
	 JRST MGERR		;Seems so, report error
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
]
	MOVE B,MGEOFR(W)	;EOF's seen since last read
	CAIL B,2		;Apparent logical EOT?
	 JRST MGERR		;Yes, can't read anything
	SKIPE MSCRW(W)		;Make sure open for reading
	 BUG HALT,[MT: CHN NOT OPEN FOR READING]
	MOVSI B,%MACTH		;Core allocator wants to wait?
	TDNN B,MSRAC(W)		;Check in status word
	 PUSHJ P,IOMQ		;No, get a buffer
	  JRST [PUSHJ Q,CPOPJ	;Wanted to wait some or failed,
		JRST MGREAD ]	; wait a while, then start over
	MOVEM A,MGCABN(W)	;Note active buffer at PI level
	MOVEI B,MUMGB
	DPB B,[MUR,,MEMBLT(A)]	;Note buffer belongs to mag tape code
	MOVEM W,MGUNIT		;Set active unit number
	HRREI B,-MGNRTY		;Number of retries
	MOVEM B,MGERRC(W)	;Store error count
IFN KL10P, MOVE R,A		;Get page for cache flush routine
IFE TM03S,[			;TM03S IOWD is -COUNT,,ITS PAGE
	LSH A,10.		;Convert buffer number to memory address
	SUBI A,1		;address-1
]
IFE KL10P,HRLI B,-2000		;Get count half of IOWD
IFN KL10P,HRLI B,-2000_4	;The KL is, of course, different
	HRR B,A			;-count,,address-1
	MOVEM B,LMIOWD		;Remember IOWD for possible retry
IFN TM10B,[
	DATAO MTS,[MICWA]	;Tell DF10 where the channel program is
	SETZM MICWA+1		;Stop after one operation
]
IFN TM10A,PUSHJ Q,MGDCSI
IFN TM10P,SETZM MIOWD+1
MGRD1:	MOVEM B,MIOWD		;Save IOWD where channel expects it
	MOVEI B,2000		;Assume for now that the record has 2K words
	MOVEM B,MGNWRD		;Set up word count
IFN KL10P,[
	PUSHJ P,CSHSWP		;Ensure no residue of this page in cache
	    CAIA
]
IFN KS10P,CLRCSH		;KS needs cache invalidated on read
IFN TM10P,[
	MOVE B,MTCONO(W)	;Get prototype CONO word
	CONO MTC,MREAD(B)	;Issue READ command
]
IFN TM03S,[
	HLRE A,MIOWD		;Get PDP10 word count
	ASH A,1			; * 2
	IOWRI A,%TMWC		; = number of unibus words to transfer
	SETZ A,			;Set FC to zero
	IOWRI A,%TMFC		;Set tape frames to write
	HRRZ A,MIOWD		;Get ITS page to transfer to
	LSH A,1			;Convert ITS pg # to DEC pg #
	TXO A,%UQVAL
	IOWRI A,UBAPAG+IUTPG_1	;Set up first half of UBA mapping
	TXO A,1			;Next DEC page number
	IOWRI A,UBAPAG+IUTPG_1+1	;Set second half of UBA mapping
	MOVEI A,IUTPG_14	;Unibus address for DMA
	IOWRI A,%TMBA		;Tell controller
	MOVX A,%TM1IE\%TMRDF	;Read forward, enable interrupts
	IOWRI A,%TMCS1		;Start controller
];IFN TM03S
IFN TM10B,SETOM MGWCW		;Tell MGWTJD to wait for control word written
	PUSHJ Q,MGWTJD		;Wait for command to complete
	 JRST MGRERR		;Error, go do something. May be retry, correct
				; MGNWRD if frame count error, or give up
IFN TM03S,[
;Get true record size for TM03, which doesn't give a FC error on READ
MGRD1A:	TTNE FS,%TMSTM		;Read a tape mark?
	 JRST [	SETZM MGNWRD	;Read a tape mark. No data, just EOF
		JRST MGRD2 ]
	IORDI A,%TMFC		;Get record size in tape frames
	ADD A,MTWTFC(W)		;Round up to PDP10 words.
	SUBI A,1		;Add frames_per_word - 1
	IDIV A,MTWTFC(W)	;Get record size in PDP10 words
	MOVEM A,MGNWRD
];IFN TM03S
;Here if OK or error handler has fixed things up
MGRD2:	MOVE A,MGCABN(W)	;Get buffer we just wrote
	SETOM MGCABN(W)		;No buffer active at PI level
	MTENQB A		;Queue buffer up for MP level
	MOVE B,MGNWRD		;Get number of words we read
	DPB B,[MWC,,MEMBLT(A)]	;Set number of words in buffer
IFN TM10P,TXNE J,%T1SEF		;Read an EOF?
IFN TM03S,TTNE FS,%TMSTM	; i.e. a tape mark?
	IFSKP.
	 SETZM MGEOFR(W)	;No. Note no EOFs read since last record
	ELSE.
	 MOVSI B,%MAESO+%MAEFA	;EOF seen since open, EOF read ahead
	 IORM B,MSRAC(W)	;Note this in transport status word
	 SETO B,
	 DPB B,[MSEOFP,,MEMBLT(A)]	;Set EOF flag in this buffer
	 AOS A,MGEOFR(W)	;Count EOFs
	 CAIL A,2		;Enough for EOT?
	  JRST MGRD4		;Logical EOT, space back over it
	ENDIF.
	MOVSI A,%MAMSO
	IORB A,MSRAC(W)		;Note tape movement
IFN TM10P,TXNE J,%T1SET		;Real EOT reached?
IFN TM03S,TTNE FS,%TMSET
	 JRST MGRD3
	MOVE C,MSNBOL(W)	;Get count of buffers on list
IFN TM10P,TXNN J,%T1SEF		;EOF?
IFN TM03S,TTNN FS,%TMSTM

;	 CAIL C,6		; or read ahead six records?
	JFCL			;Or just for the hell of it

	  JRST MGRD5		;Yes, stop reading
	TLNN A,%MASTP		;Somebody request we stop?
	 JRST MGRD0		;No, go back and read another record
				;Yes, fall through to terminate read
;Here to terminate read command
;
MGRD5:	MOVSI A,%MASTP+%MARAC	;Clear read active, any read stop request
	ANDCAM A,MSRAC(W)
	JRST MGCMDR		;Go to standard command return routine

;Controller saw a real EOT
;
MGRD3:	DPB B,[MSEOFP,,MEMBLT(A)]	;Fake EOF
	DPB B,[420100,,MSRAC(W)] .SEE %MAETR	;Indicate EOT on read
	JRST MGRD5		;Terminate read

;Here if logical EOT (two EOF's in a row)
; Back up till between the two EOF's
;
MGRD4:	MOVSI A,%MAESO+%MARAC+%MASTP	;SECOND EOF, LOGICAL EOT
	ANDCAM A,MSRAC(W)
	JRST MGSPRF		;Space reverse one file

;Error detected on read.
; Analyze error, retry read if it might help
;
MGRERR:
IFN TM10P,[
 IFN TM10B,[
	TXNE J,%T1SCP\%T1SNM\%T1SDP
	 JRST MGERR		;Channel error, forget it
 ]
	TXNE J,%T1STH\%T1SRW\%T1SIO\%T1SRC	;Hard error?
	 JRST MGERR		;Yes, abandon read
	TXNE J,%T1SPE\%T1SDL%T1SBT	;Parity, data-late, or bad tape?
	 JRST MGMRT		;Yes, a read retry might fix it
	TXNN J,%T1SRL		;Record length error?
	 JRST MGERR		;No, who knows what is going on

;Here if record length error. Adjust MGNWRD to reflect data actually read
;
IFN TM10B,HRRZ A,MICWA+1
IFN TM10A,HRRZ A,MIOWD		;Get final control word
	HRRZ B,LMIOWD		;Get original IOWD
	SUB A,B			;Calculate words read
IFN TM10B,SOS A			;Channel funnyness
	CAIE A,1		;Length of 1 may just be EOF indication
	IFSKP.
	 TXNE J,%T1SEF\%T1SET	;1 word, EOF? EOT?
	  SETZM A		;Yes, zero length record, just carries MSEOFP
	ENDIF.
	MOVEM A,MGNWRD		;Save new record length
	JRST MGRD2		;Go process as potentially correctly read data
];IFN TM10P
IFN TM03S,[
	TTNE ERR,%TMERM\%TMEUS\%TMEFS\%TMEIR\%TMEIF\%TMEMC\%TMECT\%TMENX
	 JRST MGERR		;Fatal errors
	TTNN FS,%TMSOL		;On line?
	 JRST MGERR		;This isn't so good either
	TTZ ERR,%TMEFC\%TMENG	;Ignore NSG, Frame count error
	TTNE FS,%TMSPE		;Phase encoded (1600BPI) mode?
	 TTZ ERR,%TMECE\%TMECS	;If so, ignore errors hardware already fixed
	TTZN ERR,%TMECE\%TMECS\%TMEFC\%TMENG\%TMEFL\%TMEMD\%TMEIC ;Retryable?
	 JRST MGRER1		;Nothing retrying will help
	TTNE FS,%TMSTM		;Tape mark?
	 JRST MGRER2		;Yes, no point in retrying
	JRST MGMRT		;OK, go retry read

;Formatter seems happy. Check channel status before returning
MGRER1:	TTNE ERR,177777		;Sanity check. Shouldn't be any errors left
	 JRST MGERR
	TTNE CS1,%TM1TE		;Transfer error?
	 TTNE FS,%TMSES		;And not formatter error?
	  CAIA
	   JRST MGERR		;Just lose for now
MGRER2:	JRST MGRD1A

];IFN TM03S


;Retry a read operation
;
MGMRT:	AOSL MGERRC(W)		;Still retrying?
	 JRST MGERR		;No, give up
IFN TM10P,[
 IFN TM10B,SETZM MIOWD
 IFN TM10A,[
	MOVE A,[-1,,MGVTC-1]
 ]
	MOVEM A,MIOWD		;One record
	PUSHJ Q,MGDCSO
	MOVE B,MTCONO(W)
	CONO MTC,MSPRR(B)	;Back up one record
	PUSHJ Q,MGWTJD		;Wait till done
	 JRST MGERR		;No errors allowed
 IFN TM10A,PUSHJ Q,MGDCSI	;Put back PI 1 BLKI
];IFN TM10P
IFN TM03S,[
;This could be more refined
	PUSHJ P,TMINIC		;Hit controller with hammer
	PUSHJ Q,MGGXPT		;Set up transport again
	SETO A,
	IOWRI A,%TMFC		;1 record
	MOVEI A,%TM1IE\%TMSPR	;Space backwards command
	IOWRI A,%TMCS1		;Do it
	PUSHJ Q,MGWTJD		;Wait till finished
	 JRST MGERR		;Give up if error
];IFN TM03S
	MOVE B,LMIOWD		;Get IOWD from last command
	JRST MGRD1		;Try operation again


SUBTTL MAG TAPE PI LEVEL SPACING COMMANDS
;Space forward and space reverse are completely under conditionals

IFN TM03S,[

;Space forward MGSPCD(W) records
MGSPCF:	PUSHJ Q,MGGXPT		;Acquire transport
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
	MOVEM W,MGUNIT		;Note active transport
	MOVN A,MGSPCD(W)	;Get -count
	IOWRI A,%TMFC		;Tell controller
	MOVEI A,%TMSPF		;Get command
	TXO A,%TM1IE		;Turn on interrupts
	IOWRI A,%TMCS1		;Start controller
	PUSHJ Q,MGWTJD		;Wait till command completes
	IFNSK.			;Error?
	 TTNE CS1,%TM1MP	;Control parity err?
	  JRST MGERR		;Yes, nothing else valid
	 TTNE FS,%TMSES		;Formatter errors?
	 IFSKP.
	  PUSHJ P, TMINIC	;No formatter errors, ignore RH errors
	 ELSE.
	  TTNN ERR,%TMEFC	;Frame count error?
	   JRST MGERR		;Nope, must be important
	  PUSHJ P,TMINIC
	 ENDIF.
	ENDIF.
	POPJ Q,

;Space forward record
MGSPFR:	MOVEI B,1		;Do one record
	MOVEM B,MGSPCD(W)	;Set count
	MOVSI B,%MAMSO		;Note this command causes tape movement
	IORM B,MSRAC(W)		;Set in transport software status
	PUSHJ Q,MGSPCF		;Do spacing op
	TTNN FS,%TMSTM
	 SETZB B,MGEOFR(W)	;Count EOFs since last record. None here
	TTNE FS,%TMSTM
	 AOS B,MGEOFR(W)	;Saw one.
	CAIL B,2		;Saw two?
	 JRST MGSPRR		;Back up over last one...
	JRST MGCMDR		;Clean up and leave

;Space forward file
MGSPFF:	MOVSI B,%MAESO		;See if tape has already read ahead to EOF
	TDZE B,MSRAC(W)
	 JRST MGCMDR		;EOF already read during the read, done
	MOVEI B,1		;Do one record
	MOVEM B,MGSPCD(W)	;Set count
	MOVSI B,%MAMSO		;Note this command causes tape movement
	IORM B,MSRAC(W)		;Set in transport software status
	PUSHJ Q,MGSPCF		;Space forward one record
	TTNN FS,%TMSTM
	IFSKP.			;Saw a TM
	 AOS B,MGEOFR(W)	;Saw one.
	 CAIL B,2		;Saw two?
	  JRST MGSPRR		;Yes, back up over last one, and leave
	 JRST MGCMDR		;No, OK, but we're done
	ENDIF.
	SETZB B,MGEOFR(W)	;No tape mark, reset count of EOFs seen
MGSPF1:	MOVEM B,MGSPCD(W)	;Set count to 0 (B zero from above)
	PUSHJ Q,MGSPCF		;Do it
	TTNE FS,%TMSTM		;Saw a EOF?
	IFSKP.			;No..
	 TTNE FS,%TMSET		;Saw an EOT?
	  JRST MGERR		;Really shouldn't happen
	 JRST MGSPF1		;Else go look for TM again
	ENDIF.
	AOS MGEOFR(W)		;Saw one.
	JRST MGCMDR		;EOF read. done

;Space reverse MGSPCD(W) records
MGSPCR:	PUSHJ Q,MGGXPT		;Set up transport and get status
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
	TTNE FS,%TMSBT		;At BOT?
	 POPJ Q,
	MOVEM W,MGUNIT		;Save selected transport
	MOVN A,MGSPCD(W)	;Get -count
	IOWRI A,%TMFC		;Tell controller
	MOVEI A,%TMSPR		;Space Reverse command
	TXO A,%TM1IE		;Turn on interrupts
	IOWRI A,%TMCS1		;Start controller
	PUSHJ Q,MGWTJD		;Wait till command completes
	IFNSK.			;Error?
	 TTNE CS1,%TM1MP	;Control parity err?
	  JRST MGERR		;Yes, nothing else valid
	 TTNE FS,%TMSES
	 IFSKP.
	  PUSHJ P,TMINIC
	 ELSE.
	  TTNN ERR,%TMEFC	;Frame count error?
	   JRST MGERR		;Nope, must be important
	  PUSHJ P,TMINIC
	 ENDIF.
	ENDIF.
	POPJ Q,

;Space reverse one record
MGSPRR:	MOVEI B,1
	MOVEM B,MGSPCD(W)	;1 record
	MOVSI B,%MAMSO		;Note this command causes tape movement
	IORM B,MSRAC(W)		;Set in transport software status
	PUSHJ Q,MGSPCR
	TTNE FS,%TMSTM		;Stopped on EOF?
	 SOSGE MGEOFR(W)	;Yes, one less EOF in front of us
	  SETZM MGEOFR(W)	;But never less than zero
	JRST MGCMDR		;Leave through general exit	

;Space reverse fill
MGSPRF:	MOVSI B,%MAESO		;See if tape has already read ahead to EOF
	TDNN B,MSRAC(W)
	 JRST MGSPR2		;No, we can just do command
MGSPR1:	MOVEI B,1
	MOVEM B,MGSPCD(W)
	PUSHJ Q,MGSPCR		;Skip back 1 record, should backup over EOF
	TTNN FS,%TMSTM		;Tape mark seen?
	 JRST MGSPR1		;Well, apparently not
	MOVSI B,%MAESO
	ANDCAM B,MSRAC(W)	;Turn off EOF-read-ahead flag
MGSPR2:	SETZ B,
	MOVEM B,MGSPCD(W)	;Space backwards the maximum number of records
	PUSHJ Q,MGSPCR		;Do it
	TTNN FS,%TMSBT		;BOT?
	 TTNE FS,%TMSTM		;Tape mark seen?
	  JRST MGCMDR		;Yes or Yes, all done
	JRST MGSPR2		;No, go back and try to find one.

];End TM03S way back

;Rewind commands
;
MGRWND:
IFN TM10P,MOVEI B,MREWND	;Normal rewind
IFN TM03S,MOVEI B,%TMREW
	CAIA
MGRWDM:
IFN TM10P, MOVEI B,MRWNDD	;Rewind and dismount
IFN TM03S, MOVEI B,%TMUNL
	MOVEM B,MGSPCD(W)	;Set up command
	PUSHJ Q,MGRWD1		;Call action routine
	JRST MGCMDR

;Action routine for rewind commands
MGRWD1:	PUSHJ Q,MGGXPT
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
]
	MOVEM W,MGUNIT
	MOVE B,MGSPCD(W)	;Get desired command
IFN TM10P,[
	ADD B,MTCONO(W)
	CONO MTC,(B)
]
IFN TM03S,[
	TXO B,%TM1IE
	IOWRI B,%TMCS1
]
	PUSHJ Q,MGWTJD		;Start controller and wait till done
	IFNSK.
	 TTNE FS,%TMSES
	  JRST MGERR		;Oops.
	ENDIF.
	SETZM MGEOFR(W)		;Seen no EOFs since last record

;Here after rewind command has started.
;  Two possibilities: Tape is rewound already, or controller is finished
;  but tape is still rewinding.
IFN TM10P,[
MGRWD2:	TXNN J,%T1SRW		;Slave still rewinding tape?
	 JRST MGRWD3		;No, at BOT
	PUSHJ Q,CPOPJ		;Wait some.
	CONI MTS,J		;Get status again
	MOVEM J,MGCMTS(W)	;Update in memory
	JRST MGRWD2		;Go see if we're done yet.

;Here when tape has rewound
MGRWD3:	MOVE T,TIME		;Wait 1 second more
	ADDI T,30.		;This is an attempt to fix a hardware bug
	MOVEM T,MGJDTI
MGRWD4:	MOVE T,TIME
	CAML T,MGJDTI		;Check time
	 JRST MGRWD5		;Finished waiting, go clean up
	PUSHJ Q,CPOPJ		;No, wait some more
	JRST MGRWD4

MGRWD5:	MOVSI A,%MAMSO+%MAREW
	ANDCAM A,MSRAC(W)	;Say not rewinding, hasn't moved
	SETZM MGJDTI		;Clear timeout
	POPJ Q,
];IFN TM10P
IFN TM03S,[
;This could be a lot smarter; should dismiss the command completely
; after rewind is started, and just scan for rewinding transports
; when a SSC interrupt is received. This change is also necessary
; to make multiple slaves work right; currently the rewinding unit
; holds active unit (MGUNIT) until the rewind is completed.
MGRWD2:	TTNN FS,%TMSPP		;Transport still rewinding?
	 JRST MGRWD3		;No, go finish up
	PUSHJ Q,CPOPJ		;Dismiss interrupt, wait a while
	PUSHJ Q,MGGXPT		;Get right slave, set up status
	JRST MGRWD2

MGRWD3:	MOVSI A,%MAMSO+%MAREW
	ANDCAM A,MSRAC(W)	;Say not rewinding, hasn't moved
	POPJ Q,
];IFN TM03S

;SKIP TO LOGICAL EOT
;
MGSEOT:	PUSHJ Q,MGGXPT		;Get transport
IFN TM10P,[
	TXNE J,%T1STH\%T1SRW	;Hung or rewinding?
	 JRST MGERR
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
]
	MOVEM W,MGUNIT		;Set active unit
IFN TM10P,TXNE J,%T1SBT		;BOT?
IFN TM03S,TTNE FS,%TMSBT
	 JRST MGVTCK		;Yep, do virgin tape check

;Not at BOT
; Do we have enough CONDITIONALS yet?
MGNVT:	
IFN TM10P,[
	MOVE B,MTCONO(W)	;Get CONO word
 IFN TM10A,[
	PUSHJ Q,MGDCSO
	MOVE A,[-1,,MGVTC-1]
	MOVEM A,MIOWD		;Tell it to move one record
 ]
 IFN TM10B,SETZM MIOWD
	CONO MTC,MSPRR(B)	;Space reverse first
];IFN TM10P
IFN TM03S,[
	SETO B,			;Space backwards one record
	IOWRI B,%TMFC
	MOVEI B,%TMSPR\%TM1IE
	IOWRI B,%TMCS1
];IFN TM03S
	PUSHJ Q,MGWTJD		;Wait till done
	IFNSK.
	 TTNE FS,%TMSES
	  JRST MGERR
	ENDIF.
	SETZM MGEOFR(W)
	AOS MGEOFR(W)		;Claim to have seen one EOF
MGEOT2:	
IFN TM10P,[
	MOVE B,MTCONO(W)
	CONO MTC,MSPFF(B)	;Skip forward to EOF
	PUSHJ Q,MGWTJD
	 JRST MGERR
]
IFN TM03S,[
	AOS MSCMDC(W)		;Kludge so MGSPFF will return here.
	PUSHJ Q,MGSPFF		;Space forward file
]
	MOVSI B,%MAETR		;See if we hit real EOT
IFN TM10P, TXNE J,%T1SET
IFN TM03S, TTNE FS,%TMSET
	 IORM B,MSRAC(W)	;Yep, tell MP
IFN TM10P,[
	MOVE B,MTCONO(W)
 IFN TM10A,[
	MOVE A,[-1,,MGVTC-1]
	MOVEM A,MIOWD		;One record
 ]
 IFN TM10B,SETZM MIOWD
	CONO MTC,MSPFR(B)	;Space forward one record
]
IFN TM03S,[
	SETO B,
	IOWRI B,%TMFC
	MOVEI B,%TMSPF\%TM1IE
	IOWRI B,%TMCS1
]
	PUSHJ Q,MGWTJD
	IFNSK.
	 TTNE FS,%TMSES
	 JRST MGERR
	ENDIF.
	MOVSI B,%MAETR		;Check for real EOT again
IFN TM10P,TXNE J,%T1SET
IFN TM03S,TTNE FS,%TMSET
	 IORM B,MSRAC(W)
IFN TM10P,TXNN J,%T1SEF		;Is this another EOF?
IFN TM03S,TTNN FS,%TMSTM	;(i.e. a tape mark)
	 JRST MGEOT2		;No, not at logical EOT. Go try again
IFN TM10P,[
	MOVE B,MTCONO(W)	;Yes, at logical EOT, Back up over 1 EOF
	CONO MTC,MSPRF(B)	;Space back one file
]
IFN TM03S,[
	SETO B,
	IOWRI B,%TMFC
	MOVEI B,%TMSPR\%TM1IE
	IOWRI B,%TMCS1
]
	PUSHJ Q,MGWTJD
	IFNSK.
	 TTNE FS,%TMSES
	 JRST MGERR
	ENDIF.
	MOVSI B,%MAEOF+%MAETR	;Tell MP at EOF, EOT
	IORM B,MSRAC(W)
	JRST MGCMDR

;Check for virgin tape
MGVTCK:	MOVE T,TIME
	ADDI T,60.		;Set a two second timeout
	MOVEM T,MGEOTT(W)	; before we must see an EOT
IFN TM10P,[
 IFN TM10B,[
	SETZM MIOWD
	DATAO MTS,[MICWA]
 ]
 IFN TM10A,[
	MOVE A,[-1,,MGVTC-1]
	MOVEM A,MIOWD
	PUSHJ Q,MGDCSO
 ]
	MOVE B,MTCONO(W)
	CONO MTC,MREAD(B)	;Read record. will time out if no EOF marks
MGVTC1:	CONI MTS,J
	MOVEM J,MGCMTS(W)
	TXNE J,%T1SJD		;Job done?
	 JRST MGNVT		;Yep,  not virgin tape
	TXNE J,%T1STH\%T1SIO	;Transport hung or illegal operation?
	 JRST MGERR		;Yes, give up
	MOVE T,MGEOTT(W)	;Get the timeout value
	CAMG T,TIME		;Time up?
	 JRST MGVT		;Yes, it's a new tape
	PUSHJ Q,CPOPJ		;Wait a while
	JRST MGVTC1		;Go back and check again
];IFN TM10P
IFN TM03S,[
	SETO B,
	IOWRI B,%TMFC		;Do one record
	MOVEI B,%TMSPF\%TM1IE	;Space forward command
	IOWRI B,%TMCS1

MGVTC1:	IORDI T,%TMCS1		;Get status
	TXNN T,%TM1GO		;Still going?
	IFSKP.			;Yep.
	 MOVE T,MGEOTT(W)	;Get the timeout value
	 CAMG T,TIME		;Time up?
	  JRST MGVT		;Yes, it's a new tape
	 PUSHJ Q,CPOPJ		;Wait a while
	 JRST MGVTC1		;Go back and check again
	ENDIF.
	TXNE T,%TM1TE\%TM1MP	;Controller error?
	 JRST MGERR		;Yes, bad
	IORDI T,%TMFS		;Get formatter status
	TXNE T,%TMSES		;Formatter error?
	 JRST MGNVT		;No error, not a new tape
	IORDI T,%TMERR		;Get formatter error register
	TXNE T,%TMEFC		;Frame Count error?
	 JRST MGNVT		;Yes, that is probably an OK tape
	JRST MGERR		;Not FC, must be a real problem
];IFN TM03S

;Virgin tape. Abort operation in progress, rewind tape
MGVT:	
IFN TM10P,[
	MOVE B,MTCONO(W)
	CONO MTS,31		;Clear controller
	CONO MTC,MNOPIN(B)	;Start new command to clear things out
	PUSHJ Q,MGWTJD
	 JFCL			;Ignore errors
]
IFN TM03S,[
	PUSHJ P,TMINIC
]
IFN TM10P,MOVEI B,MREWND		;Do a rewind
IFN TM03S,MOVEI B,%TMREW
	MOVEM B,MGSPCD(W)
	PUSHJ Q,MGRWD1	
	SETZM MGEOFR(W)		;No EOFs seen
	AOS MGEOFR(W)		;???
	JRST MGCMDR		;Done

;Write EOT mark (two EOF's) on tape
;
MGMEOT:	PUSHJ Q,MGGXPT
IFN TM10P,[
	TXNE J,%T1STH\%T1SRW\%T1SWL ;Hung, rewinding, or write locked...
	 JRST MGERR
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
	TTNE FS,%TMSWL		;Transport write locked?
	 JRST MGERR		;Shouldn't happen, we checked at open.
];IFN TM03S
	MOVEM W,MGUNIT		;Note active unit
	SKIPE MTCEFW(W)		;Check # of EOF's already written
	IFSKP.			;None. Need to write two
	 AOS MSCMDC(W)		;Kludge so MGWEOF will return here.
	 AOS MTCEFW(W)		;Increment EOF count
	 PUSHJ Q,MGWEOF		;Write an EOF
	ENDIF.
	MOVE A,MTCEFW(W)	;Get EOF count
	SOSLE A			;Check for at least two EOF's written
	IFSKP.			;No, need one more on tape
	 AOS MSCMDC(W)		;Again, force MGWEOF to return
	 AOS MTCEFW(W)		;Incr EOF count
	 PUSHJ Q,MGWEOF		;Write EOF
	ENDIF.
	MOVE A,MSRAC(W)		;Get transport software status
	TLNE A,%MANWT		;Are we supposed to back up over last EOF?
	IFSKP.
	 AOS MSCMDC(W)		;Yes. Force MGSPRF to return, then...
	 PUSHJ Q,MGSPRF		; ...do space reverse file command
	ENDIF.
	MOVSI A,%MANWT		;Note what we've done
	IORM A,MSRAC(W)		
	JRST MGCMDR		;Leave through general exit routine

;Write one EOF mark on tape
;
MGWEOF:	PUSHJ Q,MGGXPT		;Set up transport for IO
IFN TM10P,[	
	TXNE J,%T1STH\%T1SRW\%T1SWL ;Hung, rewinding, or write locked...
	 JRST MGERR		; ..lose
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
	TTNE FS,%TMSWL		;Transport write locked?
	 JRST MGERR		;Shouldn't happen, we checked at open.
]
	MOVEM W,MGUNIT		;Remember active unit
IFN TM10P,[
	MOVE B,MTCONO(W)	;Get CONO word for this unit
	CONO MTC,MWEOF(B)	;Execute Write EOF function
]
IFN TM03S,[
	MOVEI B,%TM1IE\%TMWTM	;Write tape mark, enable interrupts
	IOWRI B,%TMCS1		;Do it
]
	PUSHJ Q,MGWTJD		;Wait around till command is complete
	IFNSK.
	 TTNE FS,%TMSES
	 JRST MGERR
	ENDIF.
	MOVSI A,%MAMSO
	IORM A,MSRAC(W)		;Note tape movement in software status
	JRST MGCMDR		;Return through general exit routine

;Get transport and controller status
;
MGSTAT:	PUSHJ Q,MGGXPT
	JRST MGCMDR

;Write some blank space
;
MGW3IN:	PUSHJ Q,MGGXPT
IFN TM10P,[
	TXNE J,%T1STH\%T1SRW\%T1SWL ;Hung, rewinding, or write locked...
	 JRST MGERR
]
IFN TM03S,[
	TTNE FS,%TMSFR		;Formatter ready?
	 TTNN FS,%TMSOL		;Transport on line
	  JRST MGERR		;No or no, can't do operation
	TTNE FS,%TMSWL		;Transport write locked?
	 JRST MGERR		;Shouldn't happen, we checked at open.
];IFN TM03S
	MOVEM W,MGUNIT		;Note active transport
IFN TM10P,[			
;TM10 can't just write blank space, it has to write blank space followed
; by a data record
 IFN TM10B,[
	SETZM MIOWD		;Tell TM10 to write 1-word record
	DATAO MTS,[MICWA]
 ]
 IFN TM10A,[
	MOVE A,[-1,,MGVTC-1]	;1-word record
	MOVEM A,MIOWD
	PUSHJ Q,MGDCSO
 ]
	MOVE B,MTCONO(W)
	CONO MTC,MW3IN(B)	;Write date record preceeded by 3-in gap
];IFN TM10P
IFN TM03S,[
	MOVEI B,%TM1IE\%TMER3	;Erase 3 inches command, enable interrupts
	IOWRI B,%TMCS1		;Do it
];IFN TM03S
	PUSHJ Q,MGWTJD		;Wait...
	 JFCL			;Ignore errors (really should check kind)
IFN TM10P,JRST MGSPRR		;Now space record reverse over the bogus record
IFN TM03S,JRST MGCMDR		;TM03 does it right


SUBTTL .MTAPE UUO 

;.MTAPE AC,
;	AC/	CHNM,,COMMAND
;		COMMAND/	COUNT,,FUNCTION

;Called with	J/ AC field from UUO

AMTAPE:	XCTR XR,[HLRZ R,(J)]	;Get IO channel from user
	TRNE R,-NIOCHN		;Legal channel?
	 JRST ILUUO		;Lose
	HRRZM R,UUAC(U)		;Cause errors to report on the correct channel
	ADDI R,IOCHNM(U)	;Form IOCHNM pointer
	HRRZ A,(R)		; A: CLSTB/IOTTB index
	CAIL A,MTUAIX		; Min index for tape
	 CAILE A,MTBOX		; Max
	  POPJ P,		; Laconic error reportage.
	LDB W,[MTXP(R)]		;Get transport number
	CAME U,MTUSR(W)		;Same user?
	 POPJ P,		;No, give up
	UMOVE A,(J)		;Get Channel,,Command from user
	MOVEM A,MTMTAP(W)	;Save it
	HRRZS A			;Get user address of Count,,Function word
	PUSHJ P,MTIECK		;Check transport for error states
	UMOVE A,(A)		;Get Count,,Function from user
	HRRZM A,MTMFNC(W)	;Store function
	HLREM A,MTMCNT(W)	; and count
	SKIPN MTMCNT(W)		;Count of zero always means one, for
	 AOS MTMCNT(W)		; compatibility with the old code.
	HRRZS A			;Just Function
	CAIL A,NMTAPC		;Command in range?
	 POPJ P,		;No...
	AOS (P)			;Command OK, skip return
	JRST @MTAPDT(A)		;Go do command

MTAPDT:	MTHANG			;0 Hang till tape motion done
	MTRWND			;1 Rewind
	MTRWDM			;2 Rewind and dismount
	MTWEOR			;3 Write EOR if appropriate
	MTW3IN			;4 Write 3 inches of blank tape
	MTWEOF			;5 Write EOF
	MTSPR			;6 Space records
	MTSPF			;7 Space files
	MTSPEOT			;10 Space to EOT
	MTSTOP			;11 Abort all operations immediately
	MTSBKS			;12 Set block size (to COUNT)
	MTRBKS			;13 Read block size (to AC)
IFN 0,[
	MTRTSW			;14 Read Transport Information (to AC)
	MTRTEW			;15 Read Tape Error Word (to AC)
]
NMTAPC==.-MTAPDT

;Set block size for write
;
MTSBKS:	MOVE A,MTMCNT(W)	;Desired blocksize
	CAIL A,4		;Check reasonableness
	 CAILE A,2000
	  SOSA (P)		;Fail if too big or too small
	   MOVEM A,MTBLKS(W)	;OK, change write-buffer size
	POPJ P,

;Read current write blocksize
;
MTRBKS:	MOVE A,MTBLKS(W)	;Get current write block size
	JRST APTUAJ		;Return it

;Hang until all motion finished
;
MTHANG:	SKIPLE MSCMDC(W)	;Check count of queued commands
	 PUSHJ P,UFLS		;Wait if there are any
	JRST MTIECK		;Done, check for errors and return

;Rewind, Rewind and dismount
;
MTRWND:	SKIPA B,[MGRWND]	;Get "REWIND" PI level command
MTRWDM:	 MOVEI B,MGRWDM		; or "REWIND and DISMOUNT" if appropriate
	SKIPE MSCRW(W)		;Writing?
	 JRST MTSOSP		;Yes, error. Go undo skip rtn and exit
	MOVSI A,%MAREW		;Note rewinding in software status
	IORM A,MSRAC(W)
	JRST MTCMD		;Go queue command for PI level


;"Write End of Record" 
; This is really the FORCE system call, or should be.
;
MTWEOR:	SKIPG MTMDN(W)		;MP level have an active buffer?
	 POPJ P,		;No, nothing to do
	SKIPE MSCRW(W)		;Writing?
	 JRST MTWBFD		;Yes, go write buffer to tape
MTSOSP:	SOS (P)			;Not writing, error. Undo skip return
	POPJ P,			;Return

;Write EOF
;
MTWEOF:	SKIPN MSCRW(W)		;Writing?
	 JRST MTSOSP		;No, can't write EOF. Take error exit
	PUSHJ P,MTWEOR		;Flush current buffer, if any
	MOVEI B,MGWEOF		;Get PI level EOF routine
	AOS MTCEFW(W)		;Increment count of EOF's written
	MOVSI TT,%MANWT		;Flag something written on tape
	ANDCAM TT,MSRAC(W)
	JRST MTCMD		;Queue MGWEOF command for PI level

;Write blank tape
;
MTW3IN:	SKIPN MSCRW(W)		;Error if tape not open for write
	 JRST MTSOSP
	MOVEI B,MGW3IN		;Get PI level command
	SETZM MTCEFW(W)		;No EOF's since last record
	MOVSI TT,%MANWT
	ANDCAM TT,MSRAC(W)	;Note written something to tape
	JRST MTCMD		;Go queue command for PI

;Space records, +=forward, -=back
;
MTSPR:	SKIPE MSCRW(W)		;Not allowed if writing
	 JRST MTSOSP
	PUSHJ P,MTFLRA		;Flush read-ahead, find out how far off we are
	ADD B,C			;Get records tape is ahead of user (EOF=record)
	MOVNS B			;Subtract this from user's request
	ADDB B,MTMCNT(W)	;Adjust count
	JUMPE B,CPOPJ		;If count is now zero, we are done
MTSPR1:	PUSHJ P,MTCNTR		;Update user's copy in case PCLSR
	JUMPG B,MTSPFR		;Space forward
	MOVEI B,MGSPRR		;Get SPACE REVERSE command
	PUSHJ P,MTCMD		;Queue it
	AOSGE B,MTMCNT(W)	;Increment count, check if done
	 JRST MTSPR1		;More
	POPJ P,			;No more

MTSPFR:	MOVEI B,MGSPFR		;Get SPACE FORWARD command
	PUSHJ P,MTCMD		;Queue it
	SOSLE B,MTMCNT(W)	;Decr count, check if done
	 JRST MTSPR1		;More
	POPJ P,

;This routine flushes readahead.  Call before doing a spacing operation
; this doesn't actually undo the effect on the drive of the read-ahead.
; It does make sure that read-ahead's effect can't change, then returns
; in B the number of records ahead (non-negative) and in C the number of
; EOF's ahead (0 or 1).  If called twice it will return zero the second
; time.  Be sure to update your parameters.
;
MTFLRA:	PUSHJ P,MTCNTR		;Make sure user space is writable first
	MOVSI B,%MASTP
	MOVSI T,%MARAC
	IORM B,MSRAC(W)		;Tell PI-level read routine to stop reading
	TDNE T,MSRAC(W)		;Wait until PI level actually stops
	 PUSHJ P,UFLS
	ANDCAM B,MSRAC(W)	;Turn off %MASTP, things are now quiet
	SKIPLE MSCMDC(W)	;Make sure PI level is completely inactive
	 PUSHJ P,UFLS		;Wait if necessary
	MOVE B,MSNBOL(W)	;Get # bufs on read list (='s # records ahead)
	LDB C,[.BP (%MAEFA),MSRAC(W)] ;1 if EOF read ahead
	MOVSI T,%MAEFA		;Clear read-ahead EOF 
	ANDCAM T,MSRAC(W)
	JRST MTCBFF		;Go flush read-ahead buffers

;Space files, + = forward, - = back
MTSPF:	SKIPE MSCRW(W)		;Not allowed if writing
	 JRST MTSOSP
	PUSHJ P,MTFLRA		;Flush read-ahead
	MOVN B,C		;Number of files tape is ahead of user
	ADDB B,MTMCNT(W)	;Adjust desired count
	JUMPE B,CPOPJ		;If now zero we are done
MTSPF1:	PUSHJ P,MTCNTR		;Update user's count in case of PCLSR
	JUMPG B,MTSPFF		;Go do forward if needed
	MOVEI B,MGSPRF		;Get space reverse file command
	PUSHJ P,MTCMD		;Queue it
	AOSGE B,MTMCNT(W)	;Done?
	 JRST MTSPF1		;Nope
	POPJ P,

MTSPFF:	MOVEI B,MGSPFF		;Get space forward file command
	PUSHJ P,MTCMD		;Do it
	SOSLE B,MTMCNT(W)	;Count down
	 JRST MTSPF1		; till done
	POPJ P,

;Update user's FUNCTION,,COUNT word in case he's PCLSR'd
;
MTCNTR:	HRRZ A,MTMTAP(W)
	MOVE T,MTMFNC(W)
	HRL T,MTMCNT(W)
	XCTR XW,[MOVEM T,(A)]
	POPJ P,

;Get Controller status words to I,J
;
MTSTAT:	SKIPLE MSCMDC(W)	;Wait till commands queued to PI level are done
	 PUSHJ P,UFLS
	MOVEI B,MGSTAT		;GET STATUS command
	PUSHJ P,MTCMD		;Give to PI
	SKIPLE MSCMDC(W)	;Wait till done
	 PUSHJ P,UFLS
	MOVE I,MGCMTC(W)
	MOVE J,MGCMTS(W)
	POPJ P,

;Go to EOT
;
MTSPEOT:MOVEI B,MGSEOT		;Get SPACE-TO-LOGICAL-EOT command
	JRST MTCMD		;Queue it. Note read-ahead doesn't matter here

;Stop whatever we are doing right now
; Very brute-force - throws away all pending commands, read-ahead buffers, etc.
;
MTSTOP:	CONO PI,UTCOFF		;We are going to mess with buffer lists
	SETZM MSCMDC(W)		;Poof, no more commands
	MOVE Q,[-MGQDLL,,MGQD0-1] ;Reset Q PDL pointer to base of stack
	MOVE T,MSLCTB
	IMUL T,W
	ADD Q,T
	MOVEM Q,MGQDLP(W)
	MOVE Q,[-MSCBL-1,,MSCB0-1] ;Reset command queue input pointer
	ADD Q,T
	MOVEM Q,MTCMBP(W)
	MOVE Q,[4400,,MSCB0-1]	;Reset command queue output pointer
	ADD Q,T
	MOVEM Q,MGCMBP(W)
	SKIPL A,MTMDN(W)	;Buffer active at MP level?
	 PUSHJ P,IMEMR		;Yes, release it
	SETOM MTMDN(W)		;No buffer active at MP level
	SETZM MSMPRP(W)		;Zero MP pointer into current buffer
	SETZM MSMPRC(W)		;Zero count of available words in buffer
	PUSHJ P,MTCBFF		;Flush any buffers queued for PI level
	CAME W,MGUNIT		;Is interrupt level waiting for this unit?
	 JRST UTCONJ		;No, all set
	SETOM MGUNIT		;Yes, say no unit waiting for interrupt.
				;(Makes interrupt go away w/out doing anything)
IFN TM10P,[
	CONI MTC,I		;See what drive controller thinks it's using
	LDB B,[MUNITF,,I]
	CAME W,B		;Same as the drive we're clearing?
	 JRST UTCONJ		;If not same unit, don't do anything
	MOVE B,MTCONO(W)	;Yes, abort operation in progress
	CONO MTS,31		;Stop the tape
	CONO MTC,(B)		;Clear interupts
];IFN TM10P
IFN TM03S,[
	IORDI B,%TMTC
	ANDI B,7		;Get selected transport
	CAME W,B		;Same?
	 JRST UTCONJ
	PUSHJ P,TMINIC		;Yes, go reinit controller
];IFN TM03S
	SKIPGE A,MGCABN(W)	;Any buffers active at PI level?
	 JRST UTCONJ		;No
	PUSHJ P,IMEMR		;Yes, return buffer to system
	SETOM MGCABN(W)		;Say no buffer active at PI level
	JRST UTCONJ		;Leave




