	.Enable	lc
;
	.TITLE	TPC - TAPE/DISK COPY UTILITY
;
	.SBTTL	INTRODUCTION
;
        .IDENT  /V01.71/
;
	.MCALL	GCMLB$,GCML$,CSI$,CSI$1,CSI$2,CSI$SW,CSI$ND,DIR$,PUT$
        .MCALL  FDBDF$,FDAT$R,NMBLK$,WTSE$S,GET$,EXIT$S,FINIT$          
;JKN02
	.MCALL	QIOW$,OFNB$,CSI$SV,FSRSZ$
	.MCALL	ALUN$S,CLOSE$
	.MCALL	QIOW$S,ASTX$S,FDOP$R,QIO$S,CLEF$S,SETF$S
	.NLIST	BEX
;+
;
; TPC -- Fast Tape/Disk/Tape Copy Utility
;
; Version 01.51
;
; This routine will preserve any arbitrary format magtape in an image form
; on disk, or restore an image format file from disk to magtape.
;
; Its utility is its ability to drive the magtape at full speed.   This
; feature is useful when making many copies of DOS-11 magtapes.   The RSX-11
; Files-11 file is compatible with FCS variable length, block spanned
; record mode.
;
;
; Reid L Brown	27-Oct-77
;
; Modified by:
;
;	G H Cohen, 21-Aug-78
;	1) Insert a default value for file allocation size.
;	2) Use .TRNCL in place of .CLOSE.
;	3) Provide a means for file extension.
;	4) Attach to magtape unit and set density to 800BPI.
;
;+AF1	Alan E. Frisbie, 31-Jan-80
;	When moving error codes to MTERR or DSKERR, use MOVB instead of MOV,
;	followed by a MOVB #-1 if the result is negative.   This corrects the
;	problem of error numbers being printed as a positive value.
;
;+AF2	Alan E. Frisbie, 12-Mar-80
;	Enlarge buffer size to handle larrrrrrrge records (4200. bytes).
;	This allows TPC to copy DSC and BRU tapes.
;
; gce02 Glenn C. Everhart, 15-Dec-81
;	Add /ANsi switch to check for EOV as 1st 3 bytes in 80 byte
;	records seen. This should allow BIGTPC to copy ANSI tapes without
;	errors due to null files.
; 	Add /SC:nnnnnn switch to allow one to set tape characteristics
;	to nnnnnn (in RSX format) to handle really strange tape formats
;	Add processing to write 4 extra EOFs on tape at EOV and back-space
;	over all but 1 EOF to permit easy appending to FLX format tapes.
;	(The 4 EOFs will allow TPC input to stop fully when reading
;	the tapes in.)
;	Add /EB switch as subset of /ANsi switch, specifying EBCDIC
;	labels. Note that /AN will check for EITHER set
;	depending on this but not both.
;
; gce03 Glen C. Everhart, 3-Jan-83
;	Add /RT11 switch to handle RT11 variant ANSI format (512 byte labels).
;	Add /TR and /TW and associated logic for disk-disk copies in both
;	directions. Improve help message to give defaults. Change default
;	to /ANsi and make switches negatable.
;
; gce04 Glenn C. Everhart, (V01.40)
;	Add /FLX:NAME switch to allow creation of pseudo-FLX-type label
;	record in [1,1] on output tape.
;
; JKN01 Jim Neeland, (V01.44)
;	Add tape device "MF" to list to support TU78's.
; GCE04 Glenn Everhart
;	Conditional $VAX will define a "BIGGERTPC" to handle records up
;	to 11000. bytes long (works fine on VAX).
;
;+AF3   Alan E. Frisbie, 11-May-83  (V01.50)
;	Add /VE (Verify) and /CM (Compare) switches.
;	Make TPC serially re-usable when switching Tape/Disk directions.
;	Also do general cleanup of local symbols (using KED).
;
;JKN02  Jim Neeland, 3-Jun-83 (V01.51)
;       Add call to FINIT$ so files don't go to/from [0,0]!
;
;GCE05  Glenn Everhart - add support for passing eov/eot to double eof beyond
;	it (if any...)
;-
;
; LOCAL MACROS
;

	.MACRO	ERRMSG	MESSAG,WHERE,?A,?B,?C
	BR	C
A:	.ASCII	^MESSAG^
B:	.EVEN
C:	MOV	#A,QIO+Q.IOPL
	MOV	#B-A,QIO+Q.IOPL+2
	DIR$	#QIO
	.IIF	DF,WHERE	JMP	WHERE
	.ENDM	ERRMSG

	.MACRO	PAUSE
	CLEF$S	#16.
	WTSE$S	#16.
	.ENDM	PAUSE

	.MACRO	RESUME
	SETF$S	#16.
	.ENDM	RESUME

;
; ALLOCATE FSR AREA
;

	FSRSZ$	3			; ALLOCATE 3 BUFFERS
	.page
	.SBTTL	CSI & FCS DATA & BUFFERS
;
; FILE DESCRIPTOR BLOCKS & RELATED INFORMATION
;

FDBST:				; START OF FDB'S
FDBINP::FDBDF$			; DEFINE INPUT FDB
FDBOUT::FDBDF$			; DEFINE OUTPUT FDB
FDBEND:				; END ADDR OF FDB'S

GCMBLK:	GCMLB$	3,TPC,,TILUN	; COMMAND LINE CONTROL BLOCK

INDFN:	NMBLK$	TAPECOPY,TPC,,SY,0		; DEFAULT FILENAME
OUTDFN:	NMBLK$	TAPECOPY,TPC,,SY,0		; DEFAULT FILENAME

;
; LUNS
;

INLUN=	1			; INPUT FILE ON 1
OUTLUN=	2			; OUTPUT FILE ON 2
TILUN=	5			; GCML & QIO'S TO #5 (DEFAULT FROM TKB)

;
; COMMAND STRING INTERPRETER
;

CSIFLG:	.WORD	0		; FLAG WORD FOR COMMAND SWITCHES

HLPMSK=	1			; /HE - PRINT HELP MESSAGE
BLKMSK= 2			; /BL - ALLOCATE BLOCKS
SALMSK= 4			; /SA - SECONDARY ALLOCATION (BLOCKS)
CONMSK= 10			; /CO - CONTIGUOUS FILE
HDMSK=20			; /HD - 1600 BPI TAPE I/O
NRMSK=40			; /NR - NO REWIND BEFORE START
ERMSK=100			; /ER - IGNORE ERRORS (EXCEPT END-TAPE,EOV)
SCMSK=200			; /SC:NNNNNN SET CHARACTERISTICS TO NNNNNN (OCTAL)
EVMSK=400			; /EV = COPY TO EOV1/2, IGNORE IE.EOT/IE.EOV
ANSFLG:	.WORD	0	;FLAG NONZERO FOR ANSI TAPES
ANSCNT:	.WORD	0	;COUNT OF EOV1 OR EOV2 RECORDS SEEN
HDRLVL:	.WORD	0	;COUNT BUMPED UP BY HDR2 AND DOWN BY EOF2 OR EOV2

CSISW:	CSI$SW	HE,HLPMSK,CSIFLG
	CSI$SW	BL,BLKMSK,CSIFLG,SET,NEG,ALCTAB
	CSI$SW	SC,SCMSK,CSIFLG,SET,NEG,SCTAB
	CSI$SW	SA,SALMSK,CSIFLG,SET,NEG,SALTAB
	CSI$SW	CO,CONMSK,CSIFLG,SET,NEG
	CSI$SW	EV,EVMSK,CSIFLG,SET,NEG	;GCE05
	CSI$SW	HD,HDMSK,CSIFLG,SET,NEG
	CSI$SW	NR,NRMSK,CSIFLG,SET,NEG
	CSI$SW	ER,ERMSK,CSIFLG,SET,NEG
	CSI$SW	AN,1,ANSFLG,SET,NEG
	CSI$SW	EB,2,ANSFLG,SET,NEG	;/EBCDIC LABEL FLAG
	CSI$SW	RT,4,ANSFLG,SET,NEG	;/RT11 VERSION OF ANSI FORMAT
; NEW SWITCHES FOR COUNTS
; ALLOWS COPY OF DISK TO DISK
; /TR = TAPE IN READING TO FILE
; /TW = TAPE OUT WRITING TAPE FROM FILE
; EITHER ==> DISK-DISK
; /SZ:NNNNN:MMMMM=LENGTH (LOW:HIGH) OF COPY IF DISK-DISK
; /LO:NNNNN:MMMMM = START BLK (OCTAL, LO:HI) OF COPY ON NONFILE DISK
; /CT - ENABLE CONTROL ACCESSES (DENSITY, ETC.)
;
;gce04 new switches for image mode copy
; /IM:lo:hi:blkfct
;	where lo and hi are octal output blk # and blkfct is number of
; 512 byte blocks to buffer onto the output device (default 8.)
	CSI$SW	TR,1,D2DMSK,SET,NEG
	CSI$SW	TW,2,D2DMSK,SET,NEG
	CSI$SW	SZ,4,D2DMSK,SET,NEG,SZTAB
	CSI$SW	LO,10,D2DMSK,SET,NEG,LOTAB
	CSI$SW	FR,20,D2DMSK,SET,NEG	;/FR=REWIND AFTER END
	CSI$SW	CT,40,D2DMSK,SET,NEG
	CSI$SW	IM,100,D2DMSK,SET,NEG,IMTAB
	CSI$SW	FL,200,D2DMSK,SET,NEG,FLXNMB
	CSI$SW	NI,400,D2DMSK,SET,NEG	;/NI = INPUT FILE READ 512 BYTES AT
					;A TIME (NIBBLE IN)
	CSI$SW	NO,1000,D2DMSK,SET,NEG	;/NO = OUTPUT FILE WRITE 512 BYTES AT
					;A TIME (NIBBLE OUT). /NI AND /NO
					;WORK ONLY ON /IM COPIES.
	CSI$SW  MI,1,TFAKMK,SET,NEG	;/MI FOR MAGTAPE IN (OVERRIDE DVC PARSE)
	CSI$SW	MO,2,TFAKMK,SET,NEG	;/MO FOR MAGTAPE OUT (OVR DVC PARSE)
	CSI$SW	CM,1,VFYMSK,SET,NEG		;+AF3 /CM = Compare tape with existing file
	CSI$SW	VE,2,VFYMSK,SET,NEG		;+AF3 /VE = Verify Tape/Disk transfers
;
;
	CSI$ND


 .GLOBL LOLO,LOHI,SZLO,SZHI,START,D2DMSK,FDBINP,FDBOUT,INVEC
 .GLOBL OUTVEC,HELP,TPLHD,DSKLHD,DSKTTP,TPTDSK,MAGTST,WAIT
 .GLOBL ERROR,TAPEIN,DSKOUT,TPDKDQ,DSKIN,TAPOUT,DKTPDQ,NODADD,NODDEL
ALCTAB:	CSI$SV	DECIMAL,ALLOC,2
SALTAB:	CSI$SV	DECIMAL,SALOC,2
SCTAB:	CSI$SV	OCTAL,SCVAL,2
SZTAB:	CSI$SV	OCTAL,SZLO,2
	CSI$SV	OCTAL,SZHI,2
LOTAB:	CSI$SV	OCTAL,LOLO,2
	CSI$SV	OCTAL,LOHI,2
IMTAB:	CSI$SV	OCTAL,IMLO,2
	CSI$SV	OCTAL,IMHI,2
	CSI$SV	DECIMAL,IMBF,2
FLXNMB:	CSI$SV	ASCII,FLXNAM,6
	CSI$ND
FLXNAM:	.ASCII	/IMGFIL/
;
; FLX type label block (To write if image mode copy to tape)
;
LBLBK:	.RAD50	/IMG/
	.RAD50	/FIL/
	.RAD50	/OLB/	;PICK A FILE TYPE FLX TREATS IN IMAGE MODE
	.BYTE	1,1	;UIC [1,1]
	.WORD	233	;STANDARD DOS PROTECTION
	.WORD	1	;FORGET THE DATE OF CREATION ... DUNNO HOW TO DO EASILY
	.WORD	0	;SPARE IN DOS, LAST 3 NAME CHARS IN RSX FLX
;
; End of DOS label block
;
SCVAL:	.WORD	0		;CHARAC. VALUE.
ALLOC:	.WORD	-100.		; ALLOCATION VALUE [+/-]
SALOC:	.WORD	-50.		; SECONDARY ALLOCATION VALUE [+/-]
TFAKMK:	.WORD	0	; TAPE FAKE MASK
D2DMSK:	.WORD	0
SZHI:	.WORD	0
SZLO:	.WORD	0	;LENGTH HI,LO WORDS
LOHI:	.WORD	0	;LOW BLK # HI PART
LOLO:	.WORD	0	;AND LOW PART
ENDBK:	.WORD	0,0	;END LOGICAL BLOCK TO USE (SUM OF LO, SIZE)
ENDIO:	.WORD	0,0	;COUNT OF I/O DONES SEEN AT AST ENTRY
IMHI:	.WORD	0
IMLO:	.WORD	0
IMBF:	.WORD	8.	;BLOCKING FACTOR
VFYMSK:	.WORD	0	;+AF3 Verify and Compare switches
CMPPAS:	.WORD	0	;+AF3 0 = Normal copy pass,   -1 = Compare pass in progress
SWTDEV:	.WORD	0	;+AF3 0 = Nothing,   -1 = Switch devices for compare pass
	CSI$
CSI:	.BLKB	C.SIZE		; DEFINE THE CSI WORK AREA
	.EVEN
	.page
	.SBTTL	HELP MESSAGE & TEXT
;
; ERROR MESSAGE PROCESSING & HELP
;

QIO::	QIOW$	IO.WVB,TILUN,1,,IOSTAT,,<.-.,.-.,40,.-.,.-.,.-.>
IOSTAT::.WORD	0,0

ERRBUF:	.BLKB	80.

HLPMSG:
.ASCIZ	'TPC -	Tape / Disk utility program'
.ASCIZ	'	This program will copy magtapes to RSX-11 disk'
.ASCIZ	'	files and record them in a special image mode.'
.ASCIZ	'	This allows very fast multiple copies of tapes to be made'
.ASCIZ	'	from the disk image (much faster than FLX).'
	.IF	NDF,$VAX
.ASCIZ	'	Maximum block size is 4200. bytes. per tape block.'
	.IFF
.ASCIZ	'	Maximum block size is 11000. bytes. per tape block.'
	.ENDC
.ASCIZ	' '
.ASCIZ	'	The command format is standard RSX-11:'
.ASCIZ	' '
.ASCIZ	'		TPC>outfile=infile[/BL:nnnn][/SA:mmmm][/CO][/HE]'
.ASCIZ	'	Where:'
.ASCIZ	'		-One "file" must be a magtape device'
.ASCIZ	'		/BL:nnnn = An optional allocation specification'
.ASCIZ	'		 if disk is the output device.'
.ASCIZ	'		/SA:mmmm = An optional secondary allocation amount'
.ASCIZ	'		 if disk is the output device.'
.ASCIZ	'		/CO = Disk file to be contiguous'
.ASCIZ	'		/HE = This help text'
.ASCIZ	'		/NR = Do not rewind tape before use'
.ASCIZ	'		/HD = Use high density (1600 BPI) on tape'
.ASCIZ	'		/ER = Ignore input tape errors except EOT/EOV/EOF'
.ASCIZ	'		/EV = Ignore End-Volume or End-Tape errors'
.ASCIZ	'		/AN = ANSI format tape, use EOV1 and EOV2 for endtape'
.ASCIZ	'		/EB = EBCDIC labels (requires /AN switch)'
.ASCIZ	'		/SC:nnnn Sets tape characteristics to nnnn (octal)'
.ASCIZ	'		/TR = Read disk as if tape, to file on output'
.ASCIZ	'		/TW = Write file to disk as if tape output'
.ASCIZ	'		/SZ:low:high = Size in blks of disk to read on /TR'
.ASCIZ	'		/LO:low:high = Low blk number to start with on'
.ASCIZ	'			nonfile disk'
.ASCIZ	'		/FR = Rewind tape after writing'
.ASCIZ	'		/RT = RT11 version of ANSI format (Not 80 byte lbls)'
.ASCIZ	'		/VE = Verify tape/disk transfer after copy'
.ASCIZ	'		/CM = Compare tape and existing disk file'
.ASCIZ	'		/MI = Magtape In (overrides device name recog.)'
.ASCIZ	'		/MO = Magtape Out (overrides device name recog.)'
.ASCIZ	'		/IM:lo:hi:blkfct = Image copy, output blk no (lo:hi)'
.ASCIZ	'			and block factor args (blkfct=8 default)'
.ASCIZ	'	Image mode subswitches:'
.ASCIZ	'		/FL:filnam = Create FLX label first on tape of'
.ASCIZ	'			filnam.OLB[1,1]<233>'
.ASCIZ	'		/CT = Include control QIOS even if dsk-dsk'
.ASCIZ	'		/NI = "Nibble copy" In (read input in 1 blk a time)'
.ASCIZ	'		/NO = "Nibble copy" Out (wrt output 1 blk at a time)'
	.IF	NDF,$$$PRO
.ASCIZ	'	Defaults: /AN/-HD/-EB/BL:100/SA:50/-CO/SZ:494.:0/LO:0:0/-CT'
	.IFF
.ASCIZ	'	Defaults: /AN/-HD/-EB/BL:100/SA:50/-CO/SZ:800.:0/LO:0:0/-CT'
	.ENDC
HLPEND=	.
	.EVEN

DSKERM:	.ASCIZ	'TPC -- Disk I/O error.  Code=%D'
MTERM:	.ASCIZ	'TPC -- Magtape I/O error.  Code=%D'
CMPERM:	.ASCIZ	'TPC -- Compare error.   Files are different.'

	.EVEN
	.page
	.SBTTL	TAPE / DISK DATA & TABLES
;
; DISK & TAPE OPERATIONS ARE ENTIRELY AST DRIVEN, USING LINKED LISTS TO
; CONTROL THE SEQUENCE OF EVENTS TO BE FOLLOWED.  ALL BUFFERS FOR BOTH
; INPUT & OUTPUT OPERATIONS ARE KEPT IN LINKED LISTS BY TYPES (INPUT OR
; OUTPUT) AND ARE SCANNED AT EACH AST TO SEE WHAT WORK NEEDS TO BE DONE.
;
; THE GENERAL FORMAT FOR A LIST NODE IS:
;
N.FWD	= 0	; WD: 0		[ LINK TO NEXT NODE	]
N.BWD	= 2	; WD: 1		[ LINK TO PRIOR NODE	]
N.IOST	= 4	; WD: 2		[ IO STATUS WORD #0	]
		; WD: 3		[ IO STATUS WORD #1	]
N.WRK	= 10	; WD: 4		[ TEMPORARY WORK VAR.	]
N.BUF	= 12	; WD: 5		[ BUFFER ADDRESS	]
N.LEN	= 14	; WD: 6		[ BUFFER LENGTH		]
N.PTR	= 16	; WD: 7		[ CURRENT BUFFER POINTER]
N.BKH	= 20	; WD: 10	[ HIGH ORDER BLOCK NO.	]
N.BKL	= 22	; WD: 11	[ LOW ORDER BLOCK NO.	]
N.SIZE	= 24	; SIZE OF NODE
;

	.IF	NDF,$VAX
DSKBKS=	20.			;+AF2 NO. OF BLOCKS IN DISK BUFFER
	.IFF
DSKBKS=8.
	.ENDC
DSKBFS	= DSKBKS*512.		; DISK BUFFER SIZE = 16 BLOCKS
DSKBF0:	.BLKB	DSKBFS		; ALLOCATE 1ST BUFFER
DSKBF1:	.BLKB	DSKBFS		;   & 2ND BUFFER

	.IF	NDF,$VAX
TPBFS	= 4200.+2		;+AF2 TAPE BUFFER SIZE = 1 BLOCK + 1 WORD
	.IFF
TPBFS=11000.+2.
	.ENDC
TPBF0:	.BLKB	TPBFS		; TAPE BUFFER 1
TPBF1:	.BLKB	TPBFS		; TAPE BUFFER 2
TPBF2:	.BLKB	TPBFS		; TAPE BUFFER 3
	.IF	NDF,$VAX
TPBF3:	.BLKB	TPBFS		; TAPE BUFFER 4
IMBFMX=<2*DSKBKS>+<4*<TPBFS/1000>>
	.IFF
IMBFMX=<2*DSKBKS>+<3*<TPBFS/1000>>
	.ENDC
 .PRINT IMBFMX ;MAX BUFFER FOR/IM MODE
;
; CONTROL VARIABLES
;

DSKFCT:	.WORD	DSKBKS		; DISK BLOCKING FACTOR
INVEC:	.WORD	0		; CURRENT INPUT VECTOR
OUTVEC:	.WORD	0		; CURRENT OUTPUT VECTOR
OUTDVF:	.WORD	0		; OUTPUT DEVICE FLAG(-1 FOR MAGTAPE, 0 FOR DISK)
INDVF:	.WORD	0		; INPUT DEVICE FLAG
MAGDEV:	.WORD	"MM,"MS,"MT,"MF,"MU,"MA,0 ; MAGTAPE DEVICE LIST	;JKN01
IOST:	.WORD	0,0		; I/O STATUS BLOCK FOR SIMPLE QIO'S
FLAGS:	.WORD	0		; DISK & MAGTAPE FLAGS
MTERR:	.WORD	0		; MAGTAPE ERROR VALUE
DSKERR:	.WORD	0		; DISK ERROR VARIABLE
;
;LITERALS FOR LABEL STRING SEARCHES FOR /ANSI SWITCH (MODIFIED TO
;EBCDIC VALUES IF /EB SWITCH SEEN)
LITEO:	.ASCII	/EO/
LITF2:	.ASCII	/F2/
LITVV:	.ASCII	/VV/
LITHD:	.ASCII	/HD/
LITR2:	.ASCII	/R2/
;
;	FLAG VALUES FOR DISK & MAGTAPE FLAGS
;
EOV	= 1	; END OF VOLUME SEEN
EOF	= 2	; END OF FILE SEEN
ERR	= 4	; ERROR ENCOUNTERED
DONE	= 10	; TRANSFER DONE
CMPERR	= 20	; Compare error
;
; VFYMSK flags
;
CMPFLG	= 1	; Do a compare
VFYFLG	= 2	; Do a verify after this copy
;
	.page
;
; DISK NODE LIST
;

DSKLHD:	.WORD	DSKLHD		; DISK NODE LISTHEAD, FORWARD POINTER
	.WORD	DSKLHD		;  & BACKWARD POINTER
				; NOTE: LISTHEAD & LISTS MUST REMAIN TOGETHER

DSKLST:	.WORD	0,0,0,0,0,DSKBF0,DSKBFS,DSKBF0,0,0	; DISK NODE #0
	.WORD	0,0,0,0,0,DSKBF1,DSKBFS,DSKBF0,0,0	; DISK NODE #1
DSKLND=	.

;
; TAPE NODE LIST
;

TPLHD:	.WORD	TPLHD		; TAPE NODE LISTHEAD ADDRESS, FORWARD POINTER
	.WORD	TPLHD		;  & BACKWARD POINTER

TPLST:	.WORD	0,0,0,0,0,TPBF0,TPBFS,0,0,0		; TAPE BUFFER #0
	.WORD	0,0,0,0,0,TPBF1,TPBFS,0,0,0		; TAPE BUFFER #1
	.WORD	0,0,0,0,0,TPBF2,TPBFS,0,0,0		; TAPE BUFFER #2
	.IF	NDF,$VAX
	.WORD	0,0,0,0,0,TPBF3,TPBFS,0,0,0		; TAPE BUFFER #3
	.ENDC
TPLND=	.
	.page
	.SBTTL	PARSE INPUT SPECIFICATION & CHOOSE ACTION

;
; GET COMMAND LINE, PARSE IT & START ALL FILES
;

BEGIN:  FINIT$                  ;Once-only call to init FCS use         
;JKN02
START:
	CLR	VFYMSK		;+AF3 Clear VErify and CoMpare switches
	CLR	CMPPAS		;+AF3 Set normal copy pass operation
START2:
	CLR	SWTDEV		;+AF3 Clear switch-device flag
	BIT	#VFYFLG,VFYMSK	;+AF3 Second time thru, do a verify?
	BEQ	10$		;+AF3 No, skip
	MOV	#-1,CMPPAS	;+AF3 Yes, set compare-pass flag
	CLR	VFYMSK		;+AF3 Make sure we only do it once
	ERRMSG	<TPC -- Starting verify pass.>	;Not really an error...
10$:
	ALUN$S	#TILUN,#"TI,#0	; ENSURE TERMINAL ASSIGNED ON TILUN
	CALL	CLOSE		; INSURE ALL FILES ARE CLOSED (IGNORE ERRORS)
START3:
	MOV	#DSKLHD,DSKLHD
	MOV	#DSKLHD,DSKLHD+2	;SET UP QUEUES
	MOV	#TPLHD,TPLHD
	MOV	#TPLHD,TPLHD+2		;ANEW IN CASE SCREWED UP BEFORE
	MOV	#FDBST,R0	; POINT TO THE FIRST FDB
10$:	CLR	(R0)+		; CLEAR OUT THE FILE DESCRIPTOR BLOCKS
	CMP	R0,#FDBEND	; AT END YET?
	BLO	10$		; NO - CONTINUE
	MOV	#"EO,LITEO	;SET UP ANSI SEARCH LITERALS
	MOV	#"F2,LITF2
	MOV	#"VV,LITVV
	MOV	#"HD,LITHD
	MOV	#"R2,LITR2
	MOV	#1,ANSFLG	; SAY ANSI MODE	;GCE02
	CLR	ANSCNT		; AND ZERO ANSI EOVs SEEN ;GCE02
	CLR	HDRLVL		; ZERO HEADER LEVEL COUNTER
	CLR	CSIFLG		; RESET THE COMMAND STRING FLAGS
	CLR	INDVF		; RESET INPUT DEVICE TYPE
	CLR	OUTDVF		; RESET OUTPUT DEVICE TYPE
	MOV	#-100.,ALLOC	; RESET THE ALLOCATION AMOUNT
	MOV	#-50.,SALOC	; RESET THE SECONDARY ALLOCATION
	CLR	D2DMSK
	CLR	TFAKMK		;ZERO AUX FLAG WORDS
	.IF	NDF,$$$PRO
	MOV	#494.,SZLO	; DEFAULT RX01 SIZE FOR /SZ
	.IFF
	MOV	#800.,SZLO	; DEFAULT RX50 SIZE FOR /SZ (PRO 350)
	.ENDC
	CLR	SZHI
	CLR	LOLO
	CLR	LOHI		; START WITH BLK 0
	CLR	IMHI
	CLR	IMLO
.IF GE, IMBFMX-8.
IMMM=8.
.IFF
IMMM=IMBFMX
.ENDC
.PRINT IMMM ;DEFAULT /IM BLK FACTOR
	MOV	#IMMM,IMBF	; Set block factor default for /IM copy
	TST	CMPPAS		;+AF3 Is this a compare pass?
	BNE	30$		;+AF3 Yes, don't read a command line - use old one
	GCML$	#GCMBLK		; READ A COMMAND LINE
	BCC	30$		; CONTINUE IF NO ERRORS
	CMPB	#GE.EOF,G.ERR(R0) ; END OF COMMAND INPUT?
	BNE	20$		; NO - COMMAND INPUT ERROR
	EXIT$S

20$:	ERRMSG	<TPC -- Command line input error>
	JMP	START

30$:	CSI$1	#CSI,GCMBLK+G.CMLD+2,GCMBLK+G.CMLD ; PARSE COMMAND LINE
	BCC	50$		; CONTINUE IF NO ERRORS
40$:	ERRMSG	<TPC -- Command line syntax error>
	JMP	START
;
; Now that we have a valid command line, parse it to get any combination
; of switches on both input and output sides.
;
50$:
	Tst	CMPPAS		;+AF3 Are we already in a compare pass?
	BNE	58$		;+AF3 Yes -- skip this stuff
;
	CSI$2	R0,INPUT,#CSISW	;+AF3 Parse just for /CM & /VE switches
	CSI$2	R0,OUTPUT,#CSISW ;+AF3 Parse just for /CM & /VE switches
	CMP	#CMPFLG!VFYFLG,VFYMSK	;+AF3 Both compare and verify specified?
	BNE	52$		;+AF3 No, OK
	ERRMSG	<TPC -- /VE and /CM Not valid at same time.>,START
52$:
	BIT	#CMPFLG,VFYMSK	;+AF3 Does the user want to compare tape/file??
	BEQ	54$		;+AF3 NO, Skip
	MOV	#-1,CMPPAS	;+AF3 YES, Set compare pass flag
	CLR	VFYMSK		;+AF3 and remove switches
54$:
	CSI$1	#CSI,GCMBLK+G.CMLD+2,GCMBLK+G.CMLD ;+AF3 RE-PARSE COMMAND LINE
;
58$:
;
	TST	SWTDEV		;+AF3 Do we need to switch input & output for compare?
	BEQ	60$		;+AF3 NO, thank god
	CSI$2	R0,INPUT,#CSISW	;+AF3 For compare, magtape must be the input device
	BR	88$		;+AF3 Continue
60$:
	CSI$2	R0,OUTPUT,#CSISW ; PARSE OUTPUT FILESPEC & SWITCHES
88$:
	BCC	95$		; NO ERRORS - CONTINUE
	ERRMSG	<TPC -- Invalid switch>
	JMP	START

95$:	BITB	#<CS.WLD!CS.MOR>,C.STAT(R0)
	BEQ	98$		;+AF3 OK, skip
	Jmp	40$		;+AF3 Report error
;	BNE	40$
98$:
;
;SET UP /EBCDIC SUBLITERALS IF /EB SWITCH WAS SEEN
;
	BIT	#2,ANSFLG	;DID HE SET THE /EBCDIC SWITCH?
	BEQ	100$		;IF EQ NO
	BIS	#1,ANSFLG	;IF SO FORCE ON /ANSI SWITCH
	MOV	#153305,LITEO	;SET UP EBCDIC LITERALS FOR LABEL TEXT
	MOV	#171306,LITF2
	MOV	#162745,LITVV
	MOV	#142310,LITHD
	MOV	#171331,LITR2
100$:
	BIT	#4,ANSFLG	;/RT11 SWITCH SET?
	BEQ	110$	;IF EQ NO
	MOV	#"F1,LITF2
	MOV	#"R1,LITR2	;LOOK FOR HDR1, EOF1 IF SO
110$:
	BIT	#HLPMSK,CSIFLG	; DID THE USER WANT HELP?
	BEQ	120$		; NO - CONTINUE
	JMP	HELP		; GIVE HIM HELP

120$:
	BIT	#CONMSK,CSIFLG	; IS FILE TO BE CONTIGUOUS
	BNE	150$		; IF NE, YES
	TST	ALLOC		; IS ALLOC NEGATIVE
	BMI	160$		; IF YES SKIP
	NEG	ALLOC		; NEGATE IT
	BR	160$
150$:	TST	ALLOC		; IS ALLOC POSITIVE
	BPL	160$		; IF YES SKIP
	NEG	ALLOC		; NEGATE IT
160$:	BNE	170$		; (ALWAYS ALLOCATE SOMETHING!)
	MOV	#200.,ALLOC	;START AT 200 AT LEAST
170$:	TST	SALOC		; BUT NEVER LET SALOC REMAIN NEGATIVE
	BPL	180$		; IF POSITIVE, SKIP
	NEG	SALOC		; NEGATE IT
180$:	BNE	190$		;ALSO ALWAYS HAVE A SECONDARY ALLOC.
	MOV	#50.,SALOC
190$:	FDAT$R	#FDBOUT,#R.VAR,#FD.CR,,ALLOC,SALOC ; INIT THE FDB
	FDOP$R	R0,#OUTLUN
	MOV	R0,R1		; R1 = FILENAME BLOCK ADDRESS
	ADD	#F.FNB,R1	; ...
	MOV	#CSI+C.DSDS,R2	; R2 = DATASET DESCRIPTOR
	MOV	#OUTDFN,R3	; R3 = DEFAULT FILENAME ADDRESS
	BIT	#102,D2DMSK
	BEQ	200$		;NORMAL IF NOT DSK-DSK
	CALL	.PRSDV		;PARSE DEVICE INFO
	BR	210$
200$:	CALL	.GTDID	;GET DEFAULT DIRECTORY ;;;GCE03
	CALL	.PARSE		; PARSE THE FILENAME BLOCK INFO
210$:	BCS	250$		; ERROR ON PARSE
	BIT	#102,D2DMSK	; IS THIS WRITING A "TAPE" (REALLY DISK?)
	BNE	220$		; IF SO FORGET DEVICE NAME CHECK
	BIT	#2,TFAKMK	; IS OUTPUT TAPE VIA /MO SWITCH?
	BNE	220$		; IF NE YES, GO FLAG IT SO.
	CALL	MAGTST		; TEST FOR MAGTAPE DEVICE
	BCC	230$		; OUTPUT FILE IS DISK - OPEN IT
220$:	DEC	OUTDVF		; OUTPUT IS MAGTAPE - SO INDICATE
;
;
;+AF3     Here we do something really crazy.   Rather than write the compare
;+AF3   code for both tape-to-disk and disk-to-tape, I wrote it only for
;+AF3   tape-to-disk.    When a compare (verify) is called for when the
;+AF3   user specifies disk-to-tape, we simply switch the output and input
;+AF3   devices.
;+AF3
;+AF3     This is done by testing to see if the output device is a disk.
;+AF3   If not, a flag is set and the command line is re-parsed with CSI$2
;+AF3   called with "OUTPUT" and "INPUT" reversed.   Clever, huh?
;+AF3   I was afraid you would say that.   Oh well, at least it seems to work.
;
	TST	CMPPAS		;+AF3 Doing compare operation now?
	BEQ	260$		;+AF3 No, skip
	MOV	#-1,SWTDEV	;+AF3 Set Switch Device flag
	JMP	START3		;+AF3 and go parse the command line again
				;+AF3 with input and output devices switched
;
230$:
	TST	CMPPAS		;+AF3 Doing compare pass?
	BEQ	240$		;+AF3 NO, normal copy
	OFNB$	R0,#FO.RD,#OUTLUN,,,#FD.RWM	;+AF3 Open for read during compare
	BCC	270$		;+AF3 Skip if OK
	BR	250$		;+AF3 else error
240$:
	OFNB$	R0,#FO.WRT,#OUTLUN,,,#FD.RWM
	BCC	260$		; CONTINUE IF NO ERRORS
250$:	ERRMSG	<TPC -- Open error on output file>
	JMP START

260$:	MOV	#1002,F.RSIZ(R0) ; INIT THE RECORD SIZE IN FDB
	CLR	F.FFBY(R0)	; INDICATE NEXT BLOCK EMPTY
270$:
	TST	SWTDEV		;+AF3 Do we need to switch input & output?
	BEQ	280$		;+AF3 No, thank god
	CSI$2	#CSI,OUTPUT,#CSISW	;+AF3 For compare, disk must be output device
	BR	290$		;+AF3 Continue
280$:
	CSI$2	#CSI,INPUT,#CSISW ; PARSE INPUT FILE & SWITCHES
290$:
	BCC	300$		; NO ERROR IF CARRY CLEAR
	JMP	88$		; REPORT SWITCH ERROR
; TONY SCANDORA BUG REPORT... 88$ LOOKS RIGHT.
;	JMP	80$		; REPORT SWITCH ERROR
300$:	BITB	#<CS.MOR!CS.WLD>,C.STAT(R0) ; NO WILDCARDS, ETC ALLOWED
	BEQ	310$		; OK - CONTINUE
	JMP	40$		; REPORT IT AS ERROR
310$:	BIT	#HLPMSK,CSIFLG	; DID HE WANT HELP?
	BEQ	320$		; NO - CONTINUE
	JMP	HELP		; YES - GIVE HIM HELP

320$:	FDOP$R	#FDBINP,#INLUN,,#INDFN	; DECLARE LUN TO INPUT FDB
	MOV	R0,R1		; R1 = FNB ADDRESS
	ADD	#F.FNB,R1	; ...
	MOV	#CSI+C.DSDS,R2	; R2 = DATASET DESCRIPTOR
	MOV	#INDFN,R3	; R3 = DEFAULT FILENAME
	BIT	#101,D2DMSK
	BEQ	330$
	CALL	.PRSDV
	BR	340$
330$:	CALL	.GTDID		; GET DEFAULT DIRECTORY
	CALL	.PARSE		; PARSE THE FILENAME BLOCK DATA
340$:	BCS	360$		; ERROR ON PARSE
	BIT	#101,D2DMSK	; IS THE INPUT A DISK LOOKING LIKE A TAPE?
	BNE	350$		; IF SO, FLAG FOR LATER
	BIT	#1,TFAKMK	; /MI SWITCH FORCE US TO TREAT IN AS TAPE?
	BNE	350$		; IF NE YES, GO TREAT IN AS TAPE. SKIP TEST.
	CALL	MAGTST		; SEE IF THIS IS MAGTAPE
	BCC	370$		; NO - TRY TO OPEN THE FILE
350$:	DEC	INDVF		; INDICATE INPUT DEVICE IS MAGTAPE
	BR	380$		; CONTINUE
360$:	ERRMSG	<TPC -- Open error on input file>
	JMP	START

370$:	OFNB$	R0,#FO.RD,,#INLUN,,,#FD.RWM	; (If not magtape)
	BCS	360$		; ERROR ON OPEN

;
; DETERMINE WHETHER WE HAVE CORRECT COMBINATION OF DISK & MAGTAPE DEVICES:
; I.E. 1 DISK & 1 MAGTAPE
;

380$:	MOV	OUTDVF,R0	; R0 = OUTPUT DEVICE FLAG
	MOV	INDVF,R1	; R1 = INPUT DEVICE FLAG
	BIT	#100,D2DMSK
	BNE	390$		; /IM COPY IS BOTH DEVICES.
	XOR	R0,R1		; IF NOT -1, THEN WE HAVE 2 OF A KIND!
	BMI	390$		; GOOD! - CONTINUE
	ERRMSG	<TPC - Specify 1 file & 1 magtape device>,START

390$:
;
;SET UP ENDBK BLOCK
;
	MOV	LOLO,ENDBK+2
	MOV	LOHI,ENDBK
	ADD	SZLO,ENDBK+2
	ADC	ENDBK
	ADD	SZHI,ENDBK	;NOW HAVE ENDBK AS (HI,LO) AS QIO$ NEEDS.
	MOV	LOLO,ENDIO+2	;COPY START BLK # FOR COUNT OF DONE I/O'S
	MOV	LOHI,ENDIO
	BIT	#100,D2DMSK	;/IMAGE MODE?
	BNE	410$		;IF SO HANDLE SPECIALLY
	TST	CMPPAS		;+AF3 Compare pass?
	BNE	400$		;+AF3 Handled as special case of tape-to-disk
	TST	R0		; WHICH WAS IT?
	BPL	400$		; OUTPUT IS DISK
	JMP	DSKTTP		; OUTPUT IS MAGTAPE
400$:	JMP	TPTDSK		; TAPE-TO-DISK
410$:	JMP	IMGMOD		; IMAGE MODE COPY
	.page
;
; MAGTST - SUBROUTINE TO TEST FOR MAGTAPE AS THE REDIRECTED DEVICE WHICH
;	   WAS ASSIGNED IN THE FILENAME BLOCK.
; SKIPPED IF /MI OR /MO SWITCHES ARE GIVEN...

MAGTST:	MOV	#MAGDEV,R5	; POINT R5 TO DEVICE LIST

10$:	CMP	(R5)+,N.DVNM(R1); TEST DEVICE NAME FIELD OF FILENAME BLOCK
	BEQ	20$		; GOT IT! - ITS MAGTAPE (SET CARRY)
	TST	(R5)		; ANY MORE ENTRIES?
	BNE	10$		; YES - CONTINUE
	RETURN
20$:	SEC			; INDICATE MAGTAPE
	RETURN
;
; CLOSE - CLOSE FILES AT END OF RUN
;
CLOSE:
	TST	INDVF		; Input device a file?
	BNE	10$		; If NE, no - don't close it
	CLOSE$	#FDBINP		; Close input file
	BR	20$		; Check output file
10$:
	QIOW$S	#IO.KIL,#INLUN,#1	; Else kill I/O just in case
	QIOW$S	#IO.DET,#INLUN,#1	;+AF3 and Detach from the tape
20$:
	TST	OUTDVF		; Is output device a file?
	BNE	30$		; If NE no, don't close it
	MOV	#FDBOUT,R0	; Point to output FDB
	CALL	.TRNCL		; Truncate and close it
	BR	40$		; Exit
30$:
	QIOW$S	#IO.KIL,#OUTLUN,#1	; Else kill I/O just in case
	QIOW$S	#IO.DET,#OUTLUN,#1	;+AF3 and Detach from the tape
40$:
	RETURN
;
;
;
; HELP - LIST OUT THE HELP TEXT ON TI:
;

HELP:	MOV	#HLPMSG,R0	; R0 = HELP MESSAGE TEXT ADDRESS
	MOV	#40,QIO+Q.IOPL+4; SET THE CC-TYPE TO CR-LF
;
10$:	MOV	R0,QIO+Q.IOPL	; PUT THE ADDRESS IN THE DPB
	MOV	R0,R1		; SAVE THE BEGINNING ADDRESS IN R1
;
20$:	TSTB	(R0)+		; SCAN TO NULL AT END
	BNE	20$		; CONTINUE TILL NULL CHARACTER
	MOV	R0,R2		; R2 = ENDING LINE ADDRESS + 1
	DEC	R2		; BACKUP TO END OF LINE
	SUB	R1,R2		; R2 = LENGTH OF LINE
	MOV	R2,QIO+Q.IOPL+2	; PUT LENGTH IN QIO DPB
	DIR$	#QIO		; OUTPUT THE LINE
	CMP	R0,#HLPEND	; ANY MORE TEXT TO GO?
	BLO	10$		; YES - CONTINUE
	JMP	START
	.page
	.SBTTL	WAIT CODE & ERROR PROCESSING
;
; WAIT-	PROCESSING OF NORMAL CODE SUSPENDS ITSELF HERE AFTER INITIATING
;	THE TAPE COPY OPERATION.  ALL WORK IS DONE VIA AST ROUTINES, AND
;	IS COMPLETELY 'INTERRUPT' DRIVEN.  WHEN AN ERROR OCCURS, OR THE
;	TRANSFER IS FINISHED, THE MAIN PROGRAM IS 'RESUMED'.  IT MUST THEN
;	CHECK FOR ANY ERRORS ENCOUNTERED IN THE AST ROUTINES.
;
;
WAIT:	PAUSE			; WAIT FOR PROCESSING TO COMPLETE
	BIT	#ERR,FLAGS	; ANY ERRORS ?
	BNE	20$		; YES - PROCESS IT
	TST	CMPPAS		;+AF3 In compare pass?
	BEQ	10$		;+AF3 No - do restart
	ERRMSG	<TPC -- Compare OK.>	;+AF3 Not really an error message
	JMP	START		;+AF3 Just finished compare pass - do original start
10$:
	JMP	START2		;+AF3 Restart

20$:	MOV	#DSKERM,R1	; ASSUME A DISK ERROR
	MOV	DSKERR,-(SP)	; PUT ERROR CODE ON STACK
	BEQ	30$		; NO CODE - NOT DISK ERROR
	JMP	ERROR		; GO PROCESS ERROR

30$:	MOV	#MTERM,R1	; SETUP MAGTAPE ERROR MESSAGE
	MOV	MTERR,(SP)	; & ERROR CODE
	BEQ	40$		;+AF3 No code - not magtape error
	JMP	ERROR		; PROCESS THE ERROR
;
;
40$:
	MOV	#CMPERM,R1	;+AF3 Point to compare error message
	MOV	#0,(SP)		;+AF3 Null parameter
	JMP	ERROR		;+AF3 Process error message


;
; ERROR-PROCESS ERROR MESSAGES.  THIS ROUTINE OUTPUTS ERROR MESSAGES TO
;	THE USERS TERMINAL USING $EDMSG.  THE PATTERN STRING IS ASSUMED
;	TO BE POINTED TO BY R1, AND THE ERROR CODE ON THE STACK.  THIS
;	ROUTINE SHOULD BE PASSED CONTROL VIA	'JMP	ERROR'.
;

ERROR:	MOV	#ERRBUF,R0	; R0 = OUTPUT BUFFER POINTER
	MOV	SP,R2		; R2 = PARAMETER POINTER
	CALL	$EDMSG		; EDIT THE MESSAGE
	MOV	#ERRBUF,QIO+Q.IOPL	; SETUP THE QIO DPB
	MOV	R1,QIO+Q.IOPL+2		; ...
	TST	(SP)+		; POP THE PARAMETER FROM THE STACK
	DIR$	#QIO		; OUTPUT IT
	JMP	START		; RESTART
	.page
	.SBTTL	TAPE TO DISK OPERATIONS
;
; TAPE TO DISK
;
;	THIS FUNCTION INITIATES A TRANSFER SEQUENCE FROM THE TAPE DRIVE
;	TO THE DISK FILE.  THE TAPE IS REWOUND PRIOR TO THE START OF
;	THE COPY, AND ALL OPERATIONS ARE MULTI-BUFFERED.
;

TPTDSK:	CLR	FLAGS		; RESET ALL FLAGS
	CLR	DSKERR		; RESET DISK ERROR VALUE
	CLR	MTERR		; & MAGTAPE ERROR VALUE
	MOV	#TAPEIN,INVEC	; SETUP AST VECTORS
	MOV	#DSKOUT,OUTVEC	; ...
	BIT	#3,D2DMSK	;SEE IF CONTROL QIOS ARE CORRECT
	BEQ	10$		;IF SO LEAVE IN
	BIT	#40,D2DMSK	;SEE IF WANTED EVEN THO' DISK-DISK
	BEQ	50$		;IF NOT,SKIP 'EM
10$:
	QIOW$S	#IO.ATT,#INLUN,#1	; ATTACH TO TAPE UNIT
	BIT	#NRMSK,CSIFLG	;DID HE WANT TO SKIP THE REWIND?
	BNE	20$		;IF SO, SKIP REWIND
	QIOW$S	#IO.RWD,#INLUN,#1	; REWIND THE MAGTAPE
20$:	CLR	R0		;SET 800 BPI DENSITY INITIALLY
	BIT	#HDMSK,CSIFLG	;DID HE SAY HIGH DENSITY?
	BEQ	30$		;IF NOT LEAVE AT 800 BPI
	MOV	#4000,R0	;IF SO, SET 1600 BPI
30$:	BIT	#SCMSK,CSIFLG	;WAS THE /SC:NNNNNN SWITCH USED?
	BEQ	40$		;IF NOT JUST SET CHARACTS.
	BIS	SCVAL,R0	;ELSE OR IN CHARACTERISTICS WANTED
40$:
	QIOW$S	#IO.STC,#INLUN,#1,,,,<R0> ; SET TO 800BPI OR 1600 BPI
50$:
;
; INITIALIZE & PLACE ALL DISK BUFFERS IN THE QUEUE
;

	MOV	#DSKLHD,R0	; R0 = DISK BUFFER LISTHEAD
	MOV	R0,R4		; R4 = SAVED LISTHEAD ADDRESS
	MOV	R4,(R0)+	; SETUP A NULL LISTHEAD
	MOV	R4,(R0)+	; ...
				; NOTE: THE LISTHEAD & LISTS MUST BE TOGETHER!

60$:	MOV	R0,R5		; R5 = BUFFER NODE ADDRESS
	CALL	NODADD		; ADD IT TO DISK QUEUE
	MOV	N.BUF(R5),N.PTR(R5)	; UPDATE POINTER TO BUFFER START
	ADD	#N.SIZE,R0	; R0 = NEXT POINTER
	CMP	R0,#DSKLND	; AT END OF LIST YET?
	BLO	60$		; NO - CONTINUE

;
; INITIALIZE TAPE BUFFER LISTHEAD TO NULL, AND INITIATE I/O TO THE TAPE
; DRIVE FOR EVERY AVAILABLE BUFFER NODE IN ORDER TO START THE PROCESS GOING.
;

	MOV	#TPLHD,R0	; R0 = TAPE LISTHEAD ADDRESS
	MOV	R0,R1		; R1 = SAVED LISTHEAD ADDRESS
	MOV	R1,(R0)+	; INITIALIZE THE LISTHEAD TO NULL
	MOV	R1,(R0)+	; ...

80$:	MOV	R0,R1		; R1 = COPY OF NODE ADDRESS
	CLR	N.PTR(R0)	; RESET THE POINTER VALUE
	ADD	#N.IOST,R1	; R1 = IO STATUS ADDRESS
	MOV	R1,R2		; R2 = "    "	   "
	ADD	#N.BUF-N.IOST,R1; POINT R1 TO BUFFER ADDRESS CELL
	MOV	(R1)+,R3	; R3 = BUFFER ADDRESS
	TST	(R3)+		; ADVANCE BEYOND 1ST WORD
	MOV	(R1),R4		; & GET BUFFER SIZE
	SUB	#2,R4		; DECREASE BY TWO TO ACCOUNT FOR R3 OFFSET
	BIT	#1,D2DMSK	;READING DISK, NOT REAL TAPE?
	BEQ	85$		;IF EQ NO, NORMAL
	MOV	#512.,R4	;ELSE SET 1BLOCK SIZE OF BUFFER
85$:
	QIO$S	#IO.RLB,#INLUN,,,R2,INVEC,<R3,R4,,LOHI,LOLO>;READ THE BLOCK
	BIT	#3,D2DMSK
	BEQ	95$		;IF NOT DSK-DSK OMIT COUNT HERE
	ADD	#1,LOLO
	ADC	LOHI		;THEN COUNT UP THE BLOCK NUMBER
95$:	ADD	#N.SIZE,R0	; R0 = ADDRESS OF NEXT BUFFER NODE
	CMP	R0,#TPLND	; ANY MORE TAPE BUFFERS?
	BLO	80$		; YES - CONTINUE
	TST	CMPPAS		;+AF3 Doing compare pass?
	BNE	100$		;+AF3 Yes, skip to startup disk stuff
	JMP	WAIT		;+AF3 No - go wait for I/O Done
100$:
;
;
; HERE IS WHERE WE START UP THE DISK READS WHEN DOING A COMPARE OPERATION
;
;
	MOV	#DSKIN,OUTVEC	;+AF3 Setup AST vector

;
; INITATE DISK I/O OPERATIONS FOR ALL AVAILABLE DISK BUFFERS
;

	MOV	#DSKLHD,R0	; R0 = DISK LISTHEAD POINTER
	MOV	R0,R1		; R1 = SAVE LISTHEAD ADDR
	MOV	R1,(R0)+	; INITIATE THE LISTHEAD TO NULL
	MOV	R1,(R0)+	; ...

110$:	MOV	N.BUF(R0),R2	; R2 = BUFFER ADDRESS
	MOV	R2,N.PTR(R0)	; INIT THE BUFFER POINTER
	MOV	N.LEN(R0),R3	; R3 = LENGTH OF READ
	MOV	R0,R1		; R1 = IO STATUS BLOCK ADDR
	ADD	#N.IOST,R1	; ...
	MOV	#FDBOUT,R5	; R5 = INPUT FDB ADDRESS
	QIO$S	#IO.RVB,#OUTLUN,,,R1,OUTVEC,<R2,R3,,F.BKVB(R5),F.BKVB+2(R5)>
	ADD	DSKFCT,F.BKVB+2(R5) ; UPDATE THE NEXT BLOCK NO.
	ADC	F.BKVB(R5)	; ...
	ADD	#N.SIZE,R0	; ADVANCE TO NEXT BUFFER NODE
;????	CMP	R0,#DSKLND	;+AF3 Past end?
	BLO	110$		; CONTINUE IF IN RANGE
	JMP	WAIT		; ELSE GO WAIT FOR XFER TO COMPLETE
	.page
	.SBTTL	TAPEIN	- TAPE INPUT AST
;
; TAPEIN - HANDLE TAPE INPUT AST
;

	.GLOBL	TAPEIN,FLAGS
TAPEIN:	MOV	(SP)+,R5	; R5 = I/O STATUS ADDRESS
	BIT	#<EOV!ERR!DONE>,FLAGS	; ERROR OR END FLAGGED?
	BEQ	10$
	JMP	180$		; YES - GET OUT
10$:
	ADD	#1,ENDIO+2	;COUNT BLKS FINISHED
	ADC	ENDIO
	BIT	#1,D2DMSK	;SKIP UNLESS DSK-DSK
	BEQ	20$
	CMP	ENDIO,ENDBK	;HI PAST END?
	BHI	95$		;IF SO TREAT AS EOF
	BLO	20$		;IF LO ALLS WELL
	CMP	ENDIO+2,ENDBK+2	;LO BLK # PAST OR AT END?
	BHI	95$		;IF SO TREAT AS EOF
20$:
	MOV	N.BUF-N.IOST(R5),R4 ; R4 = BUFFER ADDRESS
	BIT	#ERMSK,CSIFLG	;IGNORING ERRORS?
	BEQ	30$		;IF NOT, LOOK AT ALL.
	CMPB	#IE.EOF,@R5	;WAS IT ENDFILE???
	BEQ	30$		;YES, LEAVE THAT 'ERROR'
;gce05 - if ignoring eov,eot errors just treat as ok...
	BIT	#EVMSK,CSIFLG	;IGNORING IE.EOT,IE.EOV?
	BNE	24$		;IF NE YES, SKIP TEST...
	CMPB	#IE.EOT,@R5
	BEQ	30$	;OR EOT
	CMPB	#IE.EOV,@R5
	BEQ	30$
24$:	CMPB	#IE.PRI,@R5
	BEQ	30$	;OR IF TAPE DISMOUNTED
;MAKE ALL OTHER ERRORS BE IGNORED...
	MOVB	#IS.SUC,@R5	; YES, SAY ALL WAS OK
30$:
ANST00==.
	TSTB	(R5)		; ERROR ON I/O?
	BMI	85$		; IF MI YES, GO HANDLE IT
; HERE WE CHECK FOR SPECIAL ANSI LABEL RECORDS.
; RECORD LENGTH MUST BE 80. BYTES ON INPUT AND TEXT OF FIRST
; 3 OR 4 CHARACTERS IN THE RECORD IS CHECKED.	;GCE02
ANST01==.
	BIT	#1,ANSFLG	; /ANSI SWITCH SEEN 	;GCE02
	BEQ	130$		; IF EQ NO, JUST PROCESS DATA
;SEE IF THIS WAS AN "EOV" RECORD. R4 POINTS TO DATA AREA.
; BE SURE RECORD LENGTH IS 80 BYTES FIRST.
	BIT	#4,ANSFLG	;/RT11 ANSI? (512 BYTE HEADERS)
	BNE	40$		; IF SO IGNORE LENGTH FOR CHECKS
	CMP	2(R5),#80.	; WAS THE RECORD AN 80 BYTE ONE?
	BNE	130$		; IF NOT CAN'T BE AN ANSI EOV LABEL
40$:	CMP	2(R4),LITEO	; CHECK FOR FIRST 2 CHARS
	BNE	50$		;IF NOT "EO", RESET ANSCNT
	CMPB	4(R4),LITVV	;SEE IF IT'S "EOV"
	BNE	50$		;IF NE, RESET ANSCNT
	INC	ANSCNT		;ELSE COUNT UP ANSCNT
	BR	130$
50$:	CLR	ANSCNT		;REQUIRE EOV1 AND EOV2 IN IMMEDIATE SUCCESSION
ANST02==.
; TEST FOR HDR2 OR EOF2 AND INCREMENT OR DECREMENT HDRLVL THEN
; THIS ALLOWS ONE TO HANDLE PARTIALLY FILLED ANSI TAPES.
	CMP	2(R4),LITHD	;HDR2
	BNE	60$		;IF NE NO, TRY EOF2
	CMP	4(R4),LITR2	;SEE IF STILL HDR2
	BNE	60$		;IF NOT CHECK EOF2
	MOV	#1,HDRLVL	;IF IT IS HDR2 SET LEVEL COUNTER UP TO 1
;				;(THIS PROTECTS AGAINST ACCIDENTAL SETS)
;	INC	HDRLVL		;IF IT IS HDR2 BUMP LEVEL COUNTER
	BR	130$		;THEN SCRAM
ANST03==.
60$:	CMP	2(R4),LITEO	;SEE IF IT'S EOF2
	BNE	130$		;IF NOT WE'RE THRU WITH IT
	CMP	4(R4),LITF2	;IF NOT THEN DONE
	BNE	130$
	DEC	HDRLVL		;COUNT DOWN FILE LEVEL
	BGE	130$		;IF 0 OR +, ALL'S WELL
	CLR	HDRLVL		;BUT CLAMP IT TO 0 IF WE MANAGE TO SCREW UP.
80$:	BR	130$
85$:
; ALLOW IE.EOT AND IE.EOV ERRORS TO PROPAGATE IN AN ATTEMPT TO HANDLE CASES
; WHERE THOSE ERRORS OCCUR ON READING THE TAPE. IF ANSI TAPE, THEY REALLY
; ARE NOT THE END OF DATA AND SHOULD BE BYPASSED.
ANST04==.
	BIT	#EVMSK,CSIFLG	;IGNORING EOV/EOT ETC...
	BEQ	87$		;IF EQ NO
	CMPB	#IE.EOT,(R5)
	BEQ	86$
	CMPB	#IE.EOV,(R5)
	BNE	87$
86$:	MOVB	#IS.SUC,(R5)	;FLAG ALL WELL
	BR	30$
87$:
	CMPB	#IE.EOT,(R5)	;EOT? IF SO ALWAYS MAKE EOV
	BEQ	110$
	CMPB	#IE.EOV,(R5)
	BEQ	110$
	CMPB	#IE.EOF,(R5)	; EOF?
	BNE	160$		; NO - ERROR
88$:
;
; GCE02 START
; ****** HERE ADD CODE TO SUPPORT /ANSI SWITCH IF SET
	BIT	#1,ANSFLG	; ANSI SWITCH SET?
	BEQ	95$		; IF EQ NO, JUST DO THE USUAL THINGS
; PROCESS ANSI MODE EOF RETURNS...
;
; ACTUALLY, NEED TO DUPLICATE EOF, BUT NEED EOV IF 2 ANSI EOVS THERE.
; IF ANSCNT IS 2 ALREADY, ALLOW NORMAL PROCESSING.
	.GLOBL	ANSCNT,HDRLVL,ANSFLG
	CMP	ANSCNT,#2	;SEEN AT LEAST 2 EOV RECORDS?
	BHIS	95$		;IF SO GO TEST FOR 2ND EOF AND WRITE THEM
	TST	HDRLVL		;ARE WE BETWEEN HDR2 AND EOF2?
	BNE	100$		;IF WE ARE (I.E. IF NE), JUST WRITE EOFS
;			;OTHERWISE JUST TREAT EOF MARKS AS END OF TAPE
95$:
; GCE02 END
	BIT	#EOF,FLAGS	; IS THIS 2ND EOF IN ROW? (EOV?)
	BNE	120$		; YES - END-OF-VOLUME
100$:	BIS	#EOF,FLAGS	; NO - SO SET IT TO INDICATE 1ST EOF
	MOV	#2,2(R5)	; YES - SET LENGTH FOR OUTPUT = 2 BYTES
	CLR	(R4)		; DATA VALUE = 0 (COUNT = 0)
	BR	140$		; CONTINUE
;GCE02 START
ANST05==.
110$:	BIT	#1,ANSFLG	;THIS AN ANSI TAPE? IF SO IGNORE THESE ERRS
	BEQ	120$		;IF NOT ANSI TAPE (EQ) JUST SET EOV
	TST	2(R5)		;ANY DATA THERE?
	BNE	130$		;IF ANSI TAPE (NE), THEN ACCEPT RECORD
	BR	150$		;IF NO DATA JUST CONTINUE
;GCE02 END
ANST06==.
120$:	BIS	#EOV,FLAGS	; INDICATE EOV SEEN
	MOV	#4,2(R5)	; YES - DATA LENGTH = 4 BYTES (2 NULLS)
	CLR	(R4)+		; RESET THE TWO 'NULL' RECORDS
	CLR	(R4)		; ...
	BR	140$		; CONTINUE

130$:	MOV	2(R5),(R4)	; SETUP 1ST BUFFER WORD WITH DATA LENGTH
	ADD	#2,2(R5)	; TOTAL LENGTH INCLUDES THE LENGTH WORD
	BIC	#EOF,FLAGS	; RESET THE EOF SEEN FLAG

140$:	SUB	#4,R5		; ADJUST R5 TO POINT TO START OF NODE
	MOV	N.BUF(R5),N.PTR(R5) ; SETUP THE POINTER TO START OF DATA
	MOV	TPLHD+2,R4	; R4 = PRIOR NODE ADDRESS FOR ADD OPERATION
	CALL	NODADD		; ADD NODE TO TAPE BUFFER LIST
150$:	JMP	TPDKDQ		; GO TRY TO DEQUEUE SOME WORK.

160$:	QIOW$S	#IO.KIL,#INLUN,#1,,IOST	; CANCEL ALL TAPE I/O'S IN PROG.
	BIS	#ERR,FLAGS	; FLAG THE ERROR
	MOVB	(R5),MTERR	;+AF1 COPY ERROR CODE FOR MAGTAPE
	BPL	170$		;+AF1 SKIP IF POSITIVE (NOT LIKELY)
	MOVB	#-1,MTERR+1	;+AF1 ELSE MAKE HI BYTE NEGATIVE ALSO
170$:
	RESUME			; RESUME THE MAIN TASK

180$:	ASTX$S			; EXIT THE AST
	.page
	.SBTTL	IMGMOD - DO IMAGE MODE COPIES
;THIS SECTION IS PRIMARILY FOR COPYING DISK TO DISK WITH NO FORMAT DEPENDENCE
;BUT IF THE /CT SWITCH IS SET WILL TRY TO DO SET-CHARACTERISTICS AND
;REWINDS, ETC., AS NEEDED FOR TAPE. ALSO WILL TRY TO WRITE A COUPLE EOFS
;AFTER THE WRITES.
;NOTE THE /FL:NAME SWITCH ALLOWS ONE TO WRITE PSEUDO-FLX TAPES WHICH CAN
;THEN CONTAIN OTHER (REAL) FLX PROGRAMS. FLX CAN'T HANDLE BIG BLOCK
;SIZES, BUT CAN SKIP FILES LIKE THIS SINCE LABELS ARE OK. CREATION DATES
;WILL BE FOULED UP BUT OTHERWISE OK. THE /IM AND /CT SWITCHES MUST BE
;USED TO PERMIT /FL TO HAVE ANY EFFECT.
IMGCT:	.WORD	0,0	;COUNT OF BLOCKS TO DO LEFT
IMGNM:	.WORD	0,0	;BLOCK NUMBERS TO USE
IMGIOS:	.WORD	0,0	;I/O STATUS BLK
IMGMOD:
	MOV	IMHI,IMGNM
	MOV	IMLO,IMGNM+2	;SET UP BLK #S TO USE
	MOV	SZLO,IMGCT+2
	MOV	SZHI,IMGCT	;SET UP COUNT TO DO
	CLR	FLAGS		; RESET ALL FLAGS
	CLR	DSKERR		; RESET DISK ERROR VALUE
	CLR	MTERR		; & MAGTAPE ERROR VALUE
	BIT	#40,D2DMSK	;SEE IF WANTED EVEN THO' DISK-DISK
	BNE	10$		;IF OK DO THE TAPE STUFF
	JMP	85$		;IF NOT, SKIP 'EM
10$:
	QIOW$S	#IO.ATT,#INLUN,#1	; ATTACH TO TAPE UNIT
	QIOW$S	#IO.ATT,#OUTLUN,#1	; ATTACH TO TAPE UNIT
	BIT	#NRMSK,CSIFLG	;DID HE WANT TO SKIP THE REWIND?
	BNE	20$		;IF SO, SKIP REWIND
	QIOW$S	#IO.RWD,#INLUN,#1	; REWIND THE MAGTAPE
	QIOW$S	#IO.RWD,#OUTLUN,#1	; REWIND TAPE
20$:	CLR	R0		;SET 800 BPI DENSITY INITIALLY
	BIT	#HDMSK,CSIFLG	;DID HE SAY HIGH DENSITY?
	BEQ	30$		;IF NOT LEAVE AT 800 BPI
	MOV	#4000,R0	;IF SO, SET 1600 BPI
30$:	BIT	#SCMSK,CSIFLG	;WAS THE /SC:NNNNNN SWITCH USED?
	BEQ	40$		;IF NOT JUST SET CHARACTS.
	BIS	SCVAL,R0	;ELSE OR IN CHARACTERISTICS WANTED
40$:
	QIOW$S	#IO.STC,#OUTLUN,#1,,,,<R0> ; SET TO 800BPI OR 1600BPI
	QIOW$S	#IO.STC,#OUTLUN,#1,,,,<R0> ; SET TO 800BPI OR 1600BPI
	BIT	#200,D2DMSK	;/FLX SWITCH SET UP A NAME?
	BEQ	85$		;IF NOT, SKIP WRITE OF LABEL.
	CMPB	FLXNAM,#'A	;LEGAL NAMES ARE THIS BIG OR MORE
	BLO	80$		;IF NOT, USE DEFAULT
	MOV	R0,-(SP)
	MOV	R1,-(SP)
	MOV	R2,-(SP)
;RADIX-50 PACK THE FILE NAME
	MOV	#FLXNAM,R0	;NAME IN
	CLR	R1		;NO PERIODS PLEASE
	JSR	PC,$CAT5	;CONVERT THE RAD50
	BCS	50$
	MOV	R1,LBLBK	;STORE OFF 1ST 3 CHARS
	CLR	R1
	JSR	PC,$CAT5	;CONVERT 2ND HALF
	MOV	R1,LBLBK+2	;STORE WHATEVER WE HAVE
	BR	60$
50$:	MOV	R1,LBLBK	;STORE 3 OR LESS CHAR NAME
	CLR	LBLBK+2		;ZERO 2ND HALF NAME
60$:
	MOV	(SP)+,R2
	MOV	(SP)+,R1
	MOV	(SP)+,R0
80$:	QIOW$S	#IO.WLB,#OUTLUN,#1,,,,<#LBLBK,#14.> ;WRITE THE FLX LABEL
85$:
;
;MAIN LOOP. USE ALL SPACE AVAILABLE AS A BIG BUFFER AND JUST SHUFFLE BACK
;AND FORTH TILL DONE.
.MCALL QIOW$S
IMGS:
	CMP	IMBF,#IMBFMX	;IS BLOCK FACTOR LEGAL?
	BLOS	10$		;IF LOS ALL'S WELL
	MOV	#IMBFMX,IMBF	;ELSE SET MAX AS BLOCK FACTOR
10$:
	MOV	IMBF,R3		;GET MAX BLK #
	ASH	#9.,R3		;SHIFT OVER 9 BITS FOR BUFFER SIZE
;DSKBF0 IS BUFFER ADDRESS
IMGLOP:
	BIT	#400,D2DMSK	;/NI SWITCH SET? (IF SO READ 512 AT A TIME)
	BNE	10$		;IF NE YES
	QIOW$S	#IO.RLB,#INLUN,#1,,#IMGIOS,,<#DSKBF0,R3,,LOHI,LOLO>
;THE ABOVE READS INPUT AT BLKS GIVEN.
	BR	50$		;SKIP 1 BLK AT A TIME READIN
10$:
;NIBBLE READIN (USE TO AVOID LOSS OF DATA DUE TO BAD BLOCKS)
	MOV	R0,-(SP)
	MOV	IMBF,R0
	MOV	R1,-(SP)
	MOV	#DSKBF0,R1	;DATA AREA
	MOV	R4,-(SP)
	MOV	R5,-(SP)
	MOV	LOHI,R4
	MOV	LOLO,R5
20$:	QIOW$S	#IO.RLB,#INLUN,#1,,#IMGIOS,,<R1,#1000,,R4,R5>
	ADD	#1,R5
	ADC	R4		;COUNT BLOCKS...
	ADD	#1000,R1	;BUMP ADDRESS POINTER
	TSTB	IMGIOS
	BPL	30$		;IF NO ERR, +; SKIP ERR MSG
	ERRMSG	<TPC -Warning- Input read error>
	TST	IMGIOS+2	;SEE IF 0 WORDS READ (==> DONE)
	BNE	30$		;IF NONZERO GO ON
	BIT	#ERMSK,CSIFLG	;IF /ER SPEC'D, USE BLK FACTOR ALWAYS
	BNE	30$
	MOV	R1,IMGIOS+2	;ELSE SAY HOW MANY WE GOT
	SUB	#DSKBF0+1000,IMGIOS+2	;MAKE IT A BYTE COUNT
	BR	40$
30$:	DEC	R0		;COUNT DOWN BLKS TO DO
	BGT	20$
	MOV	R3,IMGIOS+2	;COPY WC WE WOULD HAVE USED...
40$:	MOV	#1,IMGIOS
	MOV	(SP)+,R5
	MOV	(SP)+,R4
	MOV	(SP)+,R1
	MOV	(SP)+,R0
50$:
	TSTB	IMGIOS		;SEE IF ALL WENT OK
	BPL	60$
	ERRMSG	<TPC -Warning- IMG error on input disk>
60$:	MOV	IMGIOS+2,R4	;GET BYTES READ
	BNE	80$		;IF NONZERO WE KEEP GOING
	BIT	#ERMSK,CSIFLG	;IF /ER SPECIFIED DON'T TEST THIS
	BNE	80$
	JMP	IMGDUN		;ZERO READ MEANS ALL DONE.
80$:	BIT	#1000,D2DMSK	;/NO NUBBLE WRITE SPECIFIED?
	BNE	85$		;IF NE YES, DO IT 1 BLK AT A TIME
	QIOW$S	#IO.WLB,#OUTLUN,#1,,#IMGIOS,,<#DSKBF0,R4,,IMGNM,IMGNM+2>
;WRITE THE DATA OUT
	BR	110$		;SKIP NIBBLE WRITE
85$:
;NIBBLE WRITE (1 BLK AT A TIME TO AVOID LOSS OF DATA ON ERRORS)
	MOV	R0,-(SP)
	MOV	R1,-(SP)
	MOV	R2,-(SP)
	MOV	R3,-(SP)
	MOV	R4,R0
	ASH	#-9.,R0		;CONVERT TO A NUMBER OF BLOCKS TO DO
	MOV	#DSKBF0,R1	;BUFFER ADDRESS
	MOV	IMGNM,R2	;HI BLK NO
	MOV	IMGNM+2,R3	;LO BLK NO
95$:	QIOW$S	#IO.WLB,#OUTLUN,#1,,#IMGIOS,,<R1,#1000,,R2,R3>
	ADD	#1,R3
	ADC	R2		;BUMB BLK NO
	ADD	#1000,R1	;AND BUFF ADDR
	TSTB	IMGIOS		;CHECK ERRS
	BPL	100$		;IF PL ALL WELL
	ERRMSG	<TPC -Warning- IMG write error>
100$:
	DEC	R0		;COUNT DOWN BLKS TO DO
	BGT	95$		;AND LOOP TILL DONE
	MOV	R4,IMGIOS+2	;SAY WE WROTE ALL
	MOV	#1,IMGIOS	;WITH SUCCESS!!!!
	MOV	(SP)+,R3
	MOV	(SP)+,R2
	MOV	(SP)+,R1
	MOV	(SP)+,R0
110$:	TSTB	IMGIOS
	BPL	120$
	ERRMSG	<TPC -Warning- IMG error on output disk>
120$:	ASH	#-9.,R4		;GET NO. BLOCKS HANDLED LAST TIME
	BEQ	IMGDUN		;IF 0, EXIT
	SUB	R4,IMGCT+2	;COUNT DOWN BLOCKS TO DO
	SBC	IMGCT
	BMI	IMGDUN		;COUNT NEGATIVE MEANS DONE
	BNE	130$
	TST	IMGCT+2
	BEQ	IMGDUN		;END WHEN WE COUNT DOWN TO 0 LEFT
130$:	ADD	R4,LOLO
	ADC	LOHI		;ELSE BUMP BLOCK NUMBERS LEFT
	ADD	R4,IMGNM+2
	ADC	IMGNM		;IN BOTH INPUT AND OUTPUT
	;SEE HERE IF IMGCT+2 IS LESS THAN IMBF AND RESET THE
	;BUFFER SIZE IF SO TO DO MORE REASONABLE THINGS.
	TST	IMGCT		;IF H.O. COUNT IS NONZERO DONT DO THIS
	BNE	140$
	CMP	IMGCT+2,IMBF	;ENOUGH BLKS LEFT FOR FULL BUFFER?
	BHIS	140$		;IF HIS YES
	MOV	IMGCT+2,R3	;ELSE SET UP COUNT OF WHAT'S LEFT TO DO
	ASH	#9.,R3		;AND MAKE A VALID BYTE COUNT OF IT.
	BEQ	IMGDUN
140$:
	JMP	IMGLOP		;THEN GO TRY ANOTHER CHUNK
IMGDUN:
;ALL DONE... FINISH UP
	BIT	#40,D2DMSK	;/CT SPEC'D?
	BEQ	10$		;IF EQ NO
	QIO$S	#IO.EOF,#OUTLUN,,,R3
	QIO$S	#IO.EOF,#OUTLUN,,,R3
	QIO$S	#IO.EOF,#OUTLUN,,,R3
	QIO$S	#IO.EOF,#OUTLUN,,,R3
	QIO$S	#IO.EOF,#OUTLUN,,,R3
;NOW HAVE 5 EOF'S OUT TO DO. THUS WE NEED TO BACKSPACE OVER 4 RECORDS
;TO LEAVE TAPE IN POSITION AFTER THE FIRST EOF. DO IT THUS:
	.MCALL	QIOW$S
	QIOW$S	#IO.SPF,#OUTLUN,#20.,,R3,,#-4	;BACKSPACE OVER 4 EOFS
	BIT	#20,D2DMSK	;/FR FINAL REWIND WANTED?
	BEQ	10$		;IF NOT SKIP IT
	QIOW$S	#IO.RWD,#OUTLUN,#20.,,R3	;DO THE REWIND
10$:
	JMP	START		;DONE!
	.page
	.SBTTL	DSKOUT	- DISK OUTPUT AST
;
; DSKOUT - PROCESS DISK OUTPUT AST
;

DSKOUT:	MOV	(SP)+,R5	; R5 = AST ADDRESS
	BIT	#<ERR!DONE>,FLAGS	; ANY ERRORS OR OPERATION COMPLETE?
	BEQ	10$
	JMP	40$		; YES - JUST EXIT AST
10$:
;	BNE	40$		; YES - JUST EXIT AST
	TSTB	(R5)		; ANY ERRORS ON OPERATION?
	BMI	20$		; YES - ERROR

	SUB	#4,R5		; R5 = NODE ADDRESS (ADJUSTED)
	MOV	N.BUF(R5),N.PTR(R5)	; RESET THE BUFFER POINTER
	MOV	DSKLHD+2,R4	; R4 = PRIOR NODE ADDRESS FOR ADD
	CALL	NODADD		; ADD THIS TO DISK BUFFER QUEUE
	JMP	TPDKDQ		; TRY TO DEQUEUE SOME WORK

20$:	QIOW$S	#IO.KIL,#OUTLUN,#1,,#IOST	; CANCEL ALL OUTPUT I/O
	BIS	#ERR,FLAGS	; FLAG THE ERROR
	MOVB	(R5),DSKERR	;+AF1 SAVE THE ERROR CODE
	BPL	30$		;+AF1 SKIP IF POSITIVE (NOT LIKELY)
	MOVB	#-1,DSKERR+1	;+AF1 ELSE MAKE HI BYTE NEGATIVE ALSO
30$:
	RESUME			; CONTINUE THE MAIN TASK

40$:	ASTX$S
	.page
	.SBTTL	TPDKDQ	- TAPE TO DISK DEQUEUE ROUTINE
;
; TPDKDQ-
;
;	THIS ROUTINE PERFORMS THE MAJORITY OF WORK IN THE TRANSFER OPERATION.
;	ENTRIES IN THE TAPE INPUT QUEUE REPRESENT DATA INPUT FROM THE MAG-
;	TAPE DRIVE, WHICH NEEDS TO BE OUTPUT TO THE DISK.  THE ENTRIES IN THE
;	DISK QUEUE ARE EMPTY BUFFERS.  THIS ROUTINE COPIES MAGTAPE BUFFER
;	INFORMATION STARTING WITH THE 1ST BUFFER IN THE QUEUE TO THE DISK
;	BUFFERS, ONLY EXITING THE AST WHEN THERE ARE NO MORE BUFFERS AVAILABLE
;	AT ANY TIME TO CONTINUE THE OPERATION.
;

TPDKDQ:	MOV	#DSKLHD,R0	; R0 = DISK LISTHEAD ADDRESS
	CMP	R0,(R0)		; ANY OUTPUT BUFFERS AVAILABLE?
	BEQ	10$		; NO - JUST EXIT AST
	MOV	(R0),R0		; R0 = BUFFER NODE ADDRESS

	MOV	#TPLHD,R2	; R2 = TAPE LISTHEAD ADDRESS
	CMP	R2,(R2)		; ANY TAPE BUFFERS READY?
	BEQ	10$		; NO - WAIT FOR SOME TO COME
	MOV	(R2),R2		; R2 = BUFFER ADDRESS
	BR	20$		; CONTINUE

10$:	ASTX$S

;
; THERE IS AT LEAST ONE INPUT BUFFER READY & ONE OUTPUT BUFFER AVAILABLE.
; SETUP ALL POINTERS, CALCULATE THE NUMBER OF BYTES TO COPY & THE AMOUNT
; OF SPACE LEFT IN THE DISK BUFFER.  TRANSFER AS MUCH AS WILL FIT INTO
; THE DISK BUFFER.
;

20$:	MOV	N.PTR(R0),R1	; R1 = REMAINING LENGTH IN DISK BUFFER
	SUB	N.BUF(R0),R1	; ...
	NEG	R1		; (R1 = -AMOUNT USED)
	ADD	N.LEN(R0),R1	; ...
	BLE	DSKWRT		; NONE LEFT!
	MOV	N.IOST+2(R2),R3	; R3 = LENGTH OF TAPE BUFFER DATA
	BNE	30$
	JMP	QUEMTR		; NO TAPE DATA!
30$:
;	BEQ	QUEMTR		; NO TAPE DATA!

	CMP	R1,R3		; COMPARE REMAINING DISK TO TAPE BUFFER
	BHIS	40$		; DISK BUFFER WILL HOLD IT ALL - CONTINUE
	SUB	R1,R3		; R3 = AMOUNT WHICH WILL NOT FIT
	MOV	R3,N.IOST+2(R2)	; UPDATE THE AMOUNT WHICH REMAINS FOR TAPE
	MOV	R1,R3		; R3 = TRANSFER COUNT
	BR	50$		; CONTINUE

40$:	CLR	N.IOST+2(R2)	; XFER WILL FIT - NO DATA WILL BE LEFT

50$:	MOV	N.PTR(R2),R4	; R4 = FROM POINTER
	MOV	N.PTR(R0),R5	; R5 = TO POINTER
	ASR	R3		; R3 = XFER COUNT IN WORDS
	TST	CMPPAS		;+AF3 Is this a compare pass??
	BEQ	60$		;+AF3 No, go move data
	JMP	CMPBUF		;+AF3 If compare, go do compare operation

60$:	MOV	(R4)+,(R5)+	; COPY THE BUFFER
	SOB	R3,60$		; ...

	MOV	R5,N.PTR(R0)	; RESTORE NEW BUFFER POINTER VALUES
	MOV	R4,N.PTR(R2)	; ...

;
; WRITE OUT DISK BUFFER IF REQUIRED
;

	SUB	N.BUF(R0),R5	; R5 = AMOUNT OF BUFFER USED
	CMP	R5,N.LEN(R0)	; COMPARE TO BUFFER SIZE
	BLO	QUEMTR		; CONTINUE IF MORE ROOM LEFT

DSKWRT:
	TST	CMPPAS		;+AF3 In compare pass?
	BEQ	20$		;+AF3 No, skip to normal write
	BIT	#EOF,FLAGS	;+AF3 This should be END-OF-FILE time
	BNE	10$		;+AF3 It is, exit gracefully
	BIS	#CMPERR,FLAGS	;+AF3 Not EOF on disk file.   Flag error
10$:
	JMP	TDDONE		;+AF3 EXIT
20$:
	MOV	R0,R5		; R5 = NODE ADDRESS
	CALL	NODDEL		; DELETE THE NODE
	MOV	R2,-(SP)	; SAVE R2
	MOV	#FDBOUT,R4	; GET FDB ADDRESS
30$:	MOV	N.LEN(R0),R2	; COPY BUFFER LENGTH
	ASH	#-9.,R2		; CHANGE IT TO BLOCKS
	INC	R2		; AND ROUND UP
	CLR	R3		; CLEAR HIGH ORDER
	ADD	F.EFBK+2(R4),R2	; ADD LOW ORDER CURRENT BLOCK
	ADC	R3		; ADD CARRY INTO HIGH ORDER
	ADD	F.EFBK(R4),R3	; ADD HIGH ORDER CURRENT BLOCK
;CHANGE CMP TO CMPB BELOW TO AVOID TROUBLES...
;ONLY 8 BITS OF HIGH BLK ALLOCATED CAN REALLY BE THERE...
	CMPB	R3,F.HIBK(R4)	; COMPARE ULTIMATE HIGH ORDER TO
				;  AMOUT ALLOCATED ALREADY
	BLO	80$	;IF LO, KNOW ALL OK
	BHI	40$		; IF HIGH, MUST EXTEND
	CMP	R2,F.HIBK+2(R4)	; COMPARE ULTIMATE LOW ORDER
	BLO	80$		; IF LOW, THERE IS ROOM
40$:	MOV	R0,-(SP)	; PUSH NODE ADDRESS ON STACK
	MOV	R4,R0		; COPY FDB ADDRESS
	MOV	SALOC,R1	; GET SECONDARY ALLOCATION VALUE
	MOV	#203,R2		; SET CONTROL BITS FOR ENABLE, CONTIGUOUS
				; (BUT FILE IS NOW NON-CONTIGUOUS)
	BIT	#CONMSK,CSIFLG	; IS /CO SET?
	BNE	50$		; YES, HENCE WANTED CONTIGUOUS
	BIC	#7,R2		; CHANGE CONTROL INDICATORS TO NON-CONTIGUOUS
;
; THERE SEEMS TO BE A PROBLEM ABOVE 65536 BLOCKS ON DISK...
; CHANGE OF CMP TO CMPB ABOVE MAY OR MAY NOT FIX IT.
; GCE 11/5/82
;
50$:	CALL	.EXTND		; DO THE EXTEND
;.EXTND BETTER RESET F.HIBK SO THE THING DOESN'T JUST GOBBLE DISK...
	BCC	60$		; IF NO ERROR FROM EXTEND, GO ON
	MOV	#ERR,FLAGS	; FLAG AN ERROR
	TST	(SP)+		; POP STACK
	JMP	TDDONE		; GET OUT
60$:	MOV	(SP)+,R0	; RESTORE NODE ADDRESS IN R0
	BR	30$		; GO BACK TO CHECK THAT INCREMENT WAS ENOUGH
80$:	MOV	(SP)+,R2	; RESTORE R2
	MOV	R0,R1		; COPY NODE ADDRESS TO R1
	ADD	#N.IOST,R1	; R1 = IO STATUS BLOCK ADDRESS
	MOV	#FDBOUT,R3	; R3 = OUTPUT FDB POINTER
	MOV	N.BUF(R0),R4	; R4 = BUFFER ADDRESS
	MOV	N.LEN(R0),R5	; R5 = BUFFER LENGTH
	MOV	F.BKVB(R3),N.BKH(R0)	; MOVE BLOCK# INTO NODE
	MOV	F.BKVB+2(R3),N.BKL(R0)	; ...
	QIO$S	#IO.WVB,#OUTLUN,,,R1,OUTVEC,<R4,R5,#0,N.BKH(R0),N.BKL(R0)>
	ADD	DSKFCT,F.BKVB+2(R3)	; UPDATE BLOCK NUMBER TO NEXT WRITE
	ADC	F.BKVB(R3)		; ...
	MOV	F.BKVB(R3),F.EFBK(R3)	; UPDATE EOF BLOCK#
	MOV	F.BKVB+2(R3),F.EFBK+2(R3) ; ...

;
; QUEUE UP MAGTAPE FOR MORE INPUT IF BUFFER IS EMPTY
;

QUEMTR:	TST	N.IOST+2(R2)	; ANY MORE DATA TO XFER FROM THIS BUFFER?
	BEQ	10$		; NO
	JMP	TPDKDQ		; TRY TO DEQUEUE MORE WORK
10$:	BIT	#EOV,FLAGS	; DONE?
	BEQ	20$		; NO - CONTINUE
	BIS	#DONE,FLAGS	; YES - FLAG IT
	MOV	#DSKLHD,R0	; R0 = DISK LISTHEAD ADDRESS
	CMP	R0,(R0)		; ANY ENTRIES IN LIST?
	BEQ	TDDONE		; NO - DONE
	MOV	(R0),R0		; R0 = DISK NODE ADDRESS
	CMP	N.BUF(R0),N.PTR(R0) ; ANY DATA IN BUFFER?
	BNE	DSKWRT		; YES - GO WRITE IT OUT
	BEQ	TDDONE		; GO RESUME MAIN TASK & EXIT

20$:	MOV	R2,R5		; COPY NODE ADDR TO R5
	CALL	NODDEL		; DELETE THE NODE
	MOV	R2,R3		; COPY NODE ADDRESS TO R3
	ADD	#N.IOST,R3	; R3 = IO STATUS ADDR
	MOV	N.BUF(R2),R4	; R4 = BUFFER ADDRESS
	TST	(R4)+		; ADVANCE OVER 1ST WORD IN BUFFER
	MOV	N.LEN(R2),R5	; R5 = LENGTH (MAX) FOR READ
	SUB	#2,R5		; REDUCE BY AMOUNT BUFFER WAS ADVANCED
	BIT	#1,D2DMSK	;READING DISK AS TAPE?
	BEQ	30$		;IF EQ NO,NORMAL
	MOV	#512.,R5	;YES, SET 1 BLOCK
	CMP	LOHI,ENDBK	;ALL BLKS ALREADY SENT?
	BLO	30$		;IF LOWER THAN END, MORE TO DO
	CMP	LOLO,ENDBK+2	;ARE LOW ORDER BLKS SAME?
	BLO	30$		;IF LOWER ALL'S WELL
	SUB	#1,LOLO		;SINCE WE PASSED END, BACK UP 1
	SBC	LOHI
30$:
	QIO$S	#IO.RLB,#INLUN,,,R3,INVEC,<R4,R5,,LOHI,LOLO>
	BIT	#1,D2DMSK
	BEQ	40$		;SKIP INC UNLESS DSK-DSK
	ADD	#1,LOLO
	ADC	LOHI		;BUMP BLOCK NUMBER (IGNORED BY TAPE DRIVER)
40$:	JMP	TPDKDQ		; TRY TO DEQUEUE MORE WORK

50$:	ASTX$S			; EXIT THE AST

TDDONE:	RESUME			; RESUME THE MAIN TASK
	ASTX$S			; EXIT AST
	.page
;
; COMPARE TAPE BUFFER WITH DISK BUFFER
; READ NEW DISK BUFFERS AS NEEDED
;
CMPBUF:
	CMP	(R4)+,(R5)+	;+AF3 Same?
	BNE	BADCMP		;+AF3 No, give error message
	SOB	R3,CMPBUF	;+AF3 Test next word

	MOV	R5,N.PTR(R0)	;+AF3 Set next word pointer
	MOV	R4,N.PTR(R2)	;+AF3 ***

	SUB	N.BUF(R0),R5	;+AF3 Get length used
	CMP	R5,N.LEN(R0)	;+AF3 Compare with total available length
	BLO	10$		;+AF3 Some still left, go read more mag tape

	MOV	R0,R5		;+AF3 Save node pointer
	CALL	NODDEL		;+AF3 Delete disk node from list

	MOV	R0,R1		;+AF3 Node pointer
	ADD	#N.IOST,R1	;+AF3 Point at IOSB for this buffer
	MOV	N.BUF(R0),R4	;+AF3 Buffer address
	MOV	N.LEN(R0),R5	;+AF3 # of bytes to read
	MOV	#FDBOUT,R3	;+AF3 Get address of disk FDB
	QIO$S	#IO.RVB,#OUTLUN,,,R1,OUTVEC,<R4,R5,,F.BKVB(R3),F.BKVB+2(R3)>
	ADD	DSKFCT,F.BKVB+2(R3)	;+AF3 Update VBN
	ADC	F.BKVB(R3)	;+AF3 High order VBN
10$:
	JMP	QUEMTR		;+AF3 Do another mag tape read
;
;
;
BADCMP:
	BIS	#CMPERR!ERR,FLAGS	;+AF3 Flag error condition
	QIOW$S	#IO.KIL,#OUTLUN,#1,,#IOST	;+AF3 Kill all remaining disk reads
	QIOW$S	#IO.KIL,#INLUN,#1,,#IOST	;+AF3 Kill all remaining tape reads
	RESUME			;+AF3 Resume main program
	ASTX$S			;+AF3 and exit AST
	.page
	.SBTTL	DISK TO TAPE OPERATIONS
;
; DISK TO TAPE
;
;	THIS FUNCTION TRANSFERS DATA FROM THE DISK FILE TO MAGTAPE.  THE
;	MAGTAPE IS REWOUND PRIOR TO PROCESSING.  ALL READS FROM DISK ARE
; 	MULTI-BLOCK QIO'S (ALSO MULTI-BUFFERED) TO TAKE ADVANTAGE OF THE
;	HIGHER THROUGHPUT OF THE DISK.  MAGTAPE OPERATIONS ARE ALSO MULTI-
;	BUFFERED.
;

DSKTTP:	CLR	FLAGS		; RESET ALL FLAGS
	CLR	DSKERR		; RESET DISK ERROR VALUE
	CLR	MTERR		; RESET MAGTAPE ERROR
	MOV	#DSKIN,INVEC	; SETUP INPUT VECTOR
	MOV	#TAPOUT,OUTVEC	; & OUTPUT VECTOR
	BIT	#2,D2DMSK	;SKIP CONTROL QIO'S?
	BEQ	10$		;IF NOT LEAVE IN
	BIT	#40,D2DMSK
	BEQ	50$
10$:
	QIOW$S	#IO.ATT,#OUTLUN,#1	; ATTACH TO TAPE UNIT
	BIT	#NRMSK,CSIFLG
	BNE	20$		;DID HE SAY NO REWIND?
	QIOW$S	#IO.RWD,#OUTLUN,#1	; REWIND TAPE
20$:	CLR	R0		;800 BPI DEFAULT
	BIT	#HDMSK,CSIFLG	;1600 BPI TAPE?
	BEQ	30$		;IF NOT LEAVE 800
	MOV	#4000,R0	;IF SO SAY 1600BPI
30$:	BIT	#SCMSK,CSIFLG	;DID HE SET OTHER CHARACTERISTICS?
	BEQ	40$		;IF EQ NO, JUST SET CHARACS.
	BIS	SCVAL,R0	;ELSE OR IN OTHER BITS HE WANTS
40$:
	QIOW$S	#IO.STC,#OUTLUN,#1,,,,<R0> ; SET TO 800BPI OR 1600BPI
50$:
;
; INITIALIZE & PALACE ALL TAPE BUFFERS IN OUTPUT QUEUE
;

	MOV	#TPLHD,R0	; R0 = TAPE BUFFER LISTHEAD
	MOV	R0,R4		; R4 = SAVED LISTHEAD ADDR
	MOV	R4,(R0)+	; SET LISTHEAD TO NULL
	MOV	R4,(R0)+	; ...

60$:	MOV	R0,R5		; R5 = BUFFER NODE ADDRESS
	CALL	NODADD		; ADD TAPE BUFFER TO LIST
	MOV	N.BUF(R5),N.PTR(R5)	; INIT THE POINTER
	CLR	N.WRK(R5)	; RESET 'NUMBER REMAINING BYTES' COUNT
	ADD	#N.SIZE,R0	; ADVANCE TO NEXT BUFFER
	CMP	R0,#TPLND	; AT END OF TAPE BUFFERS?
	BLO	60$		; NO - CONTINUE

;
; INITATE DISK I/O OPERATIONS FOR ALL AVAILABLE DISK BUFFERS
;

	MOV	#DSKLHD,R0	; R0 = DISK LISTHEAD POINTER
	MOV	R0,R1		; R1 = SAVE LISTHEAD ADDR
	MOV	R1,(R0)+	; INITIATE THE LISTHEAD TO NULL
	MOV	R1,(R0)+	; ...

80$:	MOV	N.BUF(R0),R2	; R2 = BUFFER ADDRESS
	MOV	R2,N.PTR(R0)	; INIT THE BUFFER POINTER
	MOV	N.LEN(R0),R3	; R3 = LENGTH OF READ
;	BIT	#3,D2DMSK	; READ DISKS 1 BLK AT A TIME
;	BEQ	240$
;	MOV	#512.,R3	; I.E. 512 BYTES
;240$:
	MOV	R0,R1		; R1 = IO STATUS BLOCK ADDR
	ADD	#N.IOST,R1	; ...
	MOV	#FDBINP,R5	; R5 = INPUT FDB ADDRESS
	QIO$S	#IO.RVB,#INLUN,,,R1,INVEC,<R2,R3,,F.BKVB(R5),F.BKVB+2(R5)>
	ADD	DSKFCT,F.BKVB+2(R5) ; UPDATE THE NEXT BLOCK NO.
	ADC	F.BKVB(R5)	; ...
	ADD	#N.SIZE,R0	; ADVANCE TO NEXT BUFFER NODE
;????	CMP	R0,#DSKLND	;+AF3 Past end?
	BLO	80$		; CONTINUE IF IN RANGE
	JMP	WAIT		; ELSE GO WAIT FOR XFER TO COMPLETE
	.page
	.SBTTL	DSKIN	- DISK INPUT AST ROUTINE
;
; DSKIN - HANDLE DISK I/O DONE AST
;

DSKIN:	MOV	(SP)+,R5	; R5 = IO STATUS ADDRESS
	SUB	#4,R5		; POINT R5 TO NODE BEGINNING
	BIT	#<ERR!DONE>,FLAGS ; ANY ERRORS OR IS I/O COMPLETE?
	BNE	85$		; YES - JUST EXIT THE AST
	TSTB	N.IOST(R5)	; WAS THERE AN ERROR ON THIS XFER?
	BPL	20$		; NO - CONTINUE
	CMPB	#IE.EOF,N.IOST(R5)	; WAS IT EOF?
	BNE	40$		; NO - REAL ERROR
	BIT	#3,D2DMSK	; THIS A DSK-DISK XFER?
	BEQ	10$		; IF EQ NO
	CMP	ENDIO,ENDBK	; IF NE YES, BE SURE OUTPUT DONE
	BHI	10$		; SET EOF WHEN DONE
	BLO	20$
	CMP	ENDIO+2,ENDBK+2	; CHECK DBL PREC.
	BLO	20$		; IF NOT AT END IGNORE EOF INPUT
10$:
	BIS	#EOF,FLAGS	; FLAG EOF
	BR	85$		; & IGNORE AST

20$:	MOV	N.IOST+2(R5),N.WRK(R5)	; COPY THE # BYTES XFERRED
	ADD	N.BUF(R5),N.WRK(R5)	; POINT TO END BYTE + 1
	MOV	N.BUF(R5),N.PTR(R5)	; SETUP THE BEGINNING PTR
	MOV	DSKLHD+2,R4	; R4 = PRIOR (LAST) NODE IN LIST ADDR
	CALL	NODADD		; ADD NODE TO END OF QUEUE
;
;+AF3 Here we get sneeky.   If this is a compare operation, this AST
;+AF3 is being used by the tape-to-disk routine, but for read operations.
;
	TST	CMPPAS		;+AF3 Is this a compare pass?
	BEQ	30$		;+AF3 No, skip and do normal stuff
	JMP	TPDKDQ		;+AF3 Yes, go to tape-to-disk dequeue routine
30$:
	JMP	DKTPDQ		; GO TO MAIN DE-QUEUING ROUTINE

40$:	TST	CMPPAS		;+AF3 Doing compare?
	BEQ	50$			;+AF3 No,skip
	QIOW$S	#IO.KIL,#OUTLUN,#1,,#IOST ; CANCEL ALL OTHER DISK I/O'S
	BR	60$
50$:
	QIOW$S	#IO.KIL,#INLUN,#1,,#IOST ; CANCEL ALL OTHER DISK I/O'S
60$:
	BIS	#ERR,FLAGS	; FLAG THE ERROR
	MOVB	N.IOST(R5),DSKERR ;+AF1 SAVE DISK ERROR CODE
	BPL	80$		;+AF1 SKIP IF POSITIVE (NOT LIKELY)
	MOVB	#-1,DSKERR+1	;+AF1 ELSE MAKE HI BYTE NEGATIVE ALSO
80$:
	RESUME			; CONTINUE THE MAIN TASK

85$:	ASTX$S			; EXIT THE AST
	.page
	.SBTTL	TAPOUT	- TAPE OUTPUT I/O DONE AST ROUTINE
;
; TAPOUT - TAPE OUTPUT I/O DONE ROUTINE
;

TAPOUT:	MOV	(SP)+,R5	; R5 = I/O STATUS ADDRESS
	BIT	#<ERR!DONE>,FLAGS	; ERRORS OR DONE?
	BEQ	10$
	JMP	85$		; YES - JUST EXIT
10$:
	ADD	#1,ENDIO+2	;COUNT I/O DONES
	ADC	ENDIO
	BIT	#2,D2DMSK
	BEQ	20$
	CMP	ENDIO,ENDBK
	BLO	20$		;PAST END?
	BHI	40$
	CMP	ENDIO+2,ENDBK+2
	BHIS	40$		;TREAT AS EOF
20$:
	SUB	#N.IOST,R5	; ADJUST R5 TO POINT TO NODE
	BIT	#ERMSK,CSIFLG	;TRYING TO IGNORE TAPE ERRORS?
	BEQ	30$		;IF NOT, LOOK AT ALL
	CMPB	N.IOST(R5),#IE.PRI ;TAPE UNMOUNTED?
	BEQ	60$		;IF SO THEN DONE. IGNORE OTHER ERRORS
;NOTE THE /EVOR /ER SWITCHES CAN MAKE TAPE RUN OFF REEL. TOO BAD
; IF SO (USER OUGHT TO KNOW BETTER...)
	BIT	#EVMSK,CSIFLG
	BNE	24$
	CMPB	N.IOST(R5),#IE.EOT
	BEQ	60$
	CMPB	N.IOST(R5),#IE.EOV
	BEQ	60$		;ALLOW PRIV, EOT, OR EOV AS REAL.
24$:	BR	50$		;SKIP NORMAL TEST THEN
30$:	TSTB	N.IOST(R5)	; WAS THERE AN ERROR ON THE XFER?
	BMI	60$		; YES - SO INDICATE
	BR	50$
40$:	BIT	#40,D2DMSK	;UNLESS SPECIAL FLAG SET
	BNE	50$
	BIS	#DONE,FLAGS	;ON DSK-DSK, DONE WHEN FINISHED XFER
	MOV	N.BUF(R5),N.PTR(R5)	; SETUP THE BUFFER POINTER
	CLR	N.WRK(R5)	; RESET THE NO. BYTES LEFT TO XFER
	MOV	TPLHD+2,R4	; R4 = PRIOR (LAST) EXISTING NODE ADDR
	CALL	NODADD		; ADD THE NODE TO FREE TAPE BUFFER LIST
	QIOW$S	#IO.KIL,#OUTLUN,#1,,#IOST ; CANCEL ALL CURRENT I/O'S
	BR	80$
50$:
	MOV	N.BUF(R5),N.PTR(R5)	; SETUP THE BUFFER POINTER
	CLR	N.WRK(R5)	; RESET THE NO. BYTES LEFT TO XFER
	MOV	TPLHD+2,R4	; R4 = PRIOR (LAST) EXISTING NODE ADDR
	CALL	NODADD		; ADD THE NODE TO FREE TAPE BUFFER LIST
	JMP	DKTPDQ		; GO TO THE MAIN DE-QUEUING ROUTINE

60$:
	QIOW$S	#IO.KIL,#OUTLUN,#1,,#IOST ; CANCEL ALL CURRENT I/O'S
	BIS	#ERR,FLAGS	; FLAG THE ERROR
	MOVB	N.IOST(R5),MTERR ;+AF1 Save error code
	BPL	80$		;+AF1 Skip if positive (not likely)
	MOVB	#-1,MTERR+1	;+AF1 else make HI byte negative also
80$:
	RESUME			; CONTINUE EXECUTION OF MAIN TASK

85$:	ASTX$S
	.page
	.SBTTL	DKTPDQ	- DISK TO TAPE DEQUEUE ROUTINE
;
; DKTPDQ-
;
;	This routine interfaces the data coming in a continuous stream
;	from the disk (in multi-block buffers) to the magtape output. As
;	the tape is blocked 512., it is desirable to keep a constant no.
;	of I/O requests queued up to the tape drive, thereby allowing
;	this program to pay for itself.
;

DKTPDQ:	MOV	#DSKLHD,R0	; R0 = DISK LISTHEAD POINTER
	CMP	(R0),R0		; NULL LIST?
	BEQ	10$		; YES - WAIT FOR WORK
	MOV	(R0),R0		; R0 = THE 1ST NODE ADDR IN LIST

	MOV	#TPLHD,R2	; R2 = TAPE OUTPUT LISTHEAD ADDR
	CMP	(R2),R2		; ANY NODES FREE FOR OUTPUT?
	BEQ	10$		; NO - WAIT FOR SOME TO FREE UP
	MOV	(R2),R2		; R2 = ADDR OF 1ST FREE TAPE NODE
	BR	20$		; CONTINUE

10$:	ASTX$S			; EXIT AST

;
; Transfer data from the current disk buffer to the current tape buffer
; (note that this might be the resumption of a transfer begun earlier). When
; the tape buffer is filled (or the operation type determined if EOF), the
; the tape node is dequeued & the output initiated.  When a disk buffer is
; empty, the next one in the queue is serviced.
;

20$:	MOV	N.WRK(R2),R3	; R3 = REMAINING BYTES IF PARTIAL XFER
	BNE	30$		; WAS A PARTIAL - CONTINUE IT
	MOV	@N.PTR(R0),R3	; ELSE, START OF NEW RECORD
	ADD	#2,N.PTR(R0)	; ADVANCE OVER CONTROL WORD
	MOV	R3,N.WRK(R2)	; SAVE IT AS SIZE REQ'D
	BNE	30$
	JMP	160$		; ZERO - GO WRITE EOF
;	BEQ	160$		; ZERO - GO WRITE EOF

30$:	MOV	N.WRK(R0),R1	; R1 = ENDING DISK BUFFER ADDR
	SUB	N.PTR(R0),R1	; R1 = # BYTES LEFT IN BUFFER
	BNE	50$
40$:	JMP	210$		; NO BYTES LEFT IN BUFFER!
50$:
	CMP	R1,R3		; WILL THIS BE A PARTIAL COPY?
	BHIS	60$		; NO - ALL OF DATA IN DISK BUFFER
	SUB	R1,R3		; R3 = AMOUNT STILL REQ'D
	MOV	R3,N.WRK(R2)	; UPDATE TAPE BUFFER REMAINING COUNT
	MOV	R1,R3		; R3 = ACTUAL NUMBER OF BYTES TO XFER
	BR	80$		; CONTINUE

60$:	CLR	N.WRK(R2)	; INDICATE WHOLE XFER GOOD

80$:	MOV	N.PTR(R0),R4	; R4 = FROM POINTER
	MOV	N.PTR(R2),R5	; R5 = TO POINTER
	ASR	R3		; R3 = WORD XFER COUNT

85$:	MOV	(R4)+,(R5)+	; COPY THE DARA
	SOB	R3,85$		; ...

	MOV	R4,N.PTR(R0)	; UPDATE THE DISK POINTER
	MOV	R5,N.PTR(R2)	; & THE TAPE POINTER
	TST	N.WRK(R2)	; DO WE WRITE THE TAPE BUFFER?
	BNE	40$		; NO - SEE ABOUT DISK
	MOV	N.BUF(R2),R4	; R4 = TAPE BUFFER ADDRESS
	SUB	R4,R5		; R5 = NO. BYTES DATA IN BUFFER
	MOV	R2,R3		; R3 = THE IO STATUS ADDR
	ADD	#N.IOST,R3	; ...
	BIT	#3,D2DMSK
	BEQ	95$
	CMP	LOHI,ENDBK
	BHI	100$
	BLO	95$
	CMP	LOLO,ENDBK+2
	BHIS	100$
95$:
	QIO$S	#IO.WLB,#OUTLUN,,,R3,OUTVEC,<R4,R5,,LOHI,LOLO>
100$:
	BIT	#3,D2DMSK
	BEQ	110$
	ADD	#1,LOLO
	ADC	LOHI		;COUNT BLK # TO DO
110$:
	BIC	#EOV,FLAGS	; RESET THE END-OF-VOLUME TEST BIT
;
;GCE02 - For ANSI tape set HDRLVL or ANSCNT and don't set EOV status
; if inside ANSI area where 2 EOFS are * NOT * EOV.
;
	BIT	#1,ANSFLG	; /ANSI TAPE FLAG SEEN?
	BEQ	150$		; IF EQ NO, NORMAL
	BIT	#4,ANSFLG	;IS THIS /RT11 ANSI VERSION (NOT 80 BYTE LABELS)
	BNE	120$		;IF SO SKIP LENGTH CHECK
	CMP	R5,#80.		;WRITING 80 BYTES?
	BNE	150$		;IF NOT THIS ISN'T OF INTEREST
120$:	CMP	(R4),LITEO	; EOV OR EOF2?
	BNE	140$		; IF NOT CHECK FOR HDR2
;COULD BE AN EOV OR EOF2
	CMPB	2(R4),LITVV	; IS IT EOV?
	BNE	130$		;IF NE NO
; SAW EOV1 SO BUMP ANSCNT
	INC	ANSCNT		;COUNT EOV1
	BR	200$
130$:	CLR	ANSCNT		;2 EOV1 IN A ROW ARE REAL END
	CMP	2(R4),LITF2	; SEE IF EOF2 RECORD
	BNE	200$		; IF NE NO, JUST SKIP ANY SPECIAL STUFF
	DEC	HDRLVL		;ELSE COUNT DOWN HEADER LEVEL
	BGE	200$		;AND
	CLR	HDRLVL		;...CLAMP IT POSITIVE OR 0
	BR	200$
140$:	CMP	(R4),LITHD	;COULD THIS BE HDR2?
	BNE	200$		;IF NOT, JUST BYPASS
	CMP	2(R4),LITR2	;IF REALLY HDR2 THIS WILL BE EQ
	BNE	200$
	INC	HDRLVL		;SO COUNT UP LEVEL
150$:
; END GCE02
	BR	200$		; CONTINUE

160$:	MOV	R2,R3		; R3 = IO STATUS ADDR
	ADD	#N.IOST,R3	; ...
	BIT	#3,D2DMSK	;DSK-DSK?
	BEQ	170$
	BIT	#40,D2DMSK
	BNE	170$
	BR	180$
170$:
	QIO$S	#IO.EOF,#OUTLUN,,,R3,OUTVEC
180$:
	BIT	#EOV,FLAGS	; WAS EOV FLAG SET LAST TIME?
	BNE	230$		; YES - TWO EOF'S = EOV
	BIT	#1,ANSFLG	; /ANSI TAPE? 	;GCE02
	BEQ	190$		; IF EQ NO, JUST SET EOV
	CMP	ANSCNT,#2	; SAW EOV1 AND EOV2 RECORDS ALREADY?
	BHIS	190$		;IF SO SET EOV FLAG FOR NEXT EOF
	TST	HDRLVL		;BUT IF BETWEEN HDR2 AND EOF2 INSIDE VOL...
	BNE	200$		;...THEN DON'T SET EOV FLAG
190$:
;END GCE02
	BIS	#EOV,FLAGS	; NO - SO SET IT FOR THIS EOF

200$:	MOV	R2,R5		; R5 = NODE ADDRESS FOR DELETE
	CALL	NODDEL		; DELETE THE NODE FROM THE QUEUE

;
; DETERMINE WHETHER CURRENT DISK BUFFER IS EMPTY & INITIATE A NEW READ IF SO
;

210$:	CMP	N.WRK(R0),N.PTR(R0)	; BUFFER EMPTY?
	BLOS	220$
	JMP	DKTPDQ		; NO - CONTINUE DEQUEUEING
220$:
	MOV	R0,R5		; R5 = NODE ADDR
	CALL	NODDEL		; DELETE THE NODE FROM DISK QUEUE
	MOV	R0,R1		; R1 = IO STATUS ADDR
	ADD	#N.IOST,R1	; ...
	MOV	N.BUF(R0),R4	; R4 = BUFFER ADDR
	MOV	N.LEN(R0),R5	; R5 = NO. BYTES TO READ
;	BIT	#3,D2DMSK	; READ DISKS 1 BLK AT A TIME...
;	BEQ	382$
;	MOV	#512.,R5
;382$:
	MOV	#FDBINP,R3	; R3 = FDB ADDR
	QIO$S	#IO.RVB,#INLUN,,,R1,INVEC,<R4,R5,,F.BKVB(R3),F.BKVB+2(R3)>
	ADD	DSKFCT,F.BKVB+2(R3) ; UPDATE NEXT BLOCK#
	ADC	F.BKVB(R3)	; ...
	JMP	DKTPDQ		; CONTINUE

230$:	BIS	#DONE,FLAGS	; FLAG I/O DONE
	.IF	NDF,XXEOF
	BIT	#2,D2DMSK
	BEQ	240$
	BIT	#40,D2DMSK
	BEQ	250$
240$:
	QIO$S	#IO.EOF,#OUTLUN,,,R3,OUTVEC
	QIO$S	#IO.EOF,#OUTLUN,,,R3,OUTVEC
	QIO$S	#IO.EOF,#OUTLUN,,,R3,OUTVEC
;
;NOW HAVE 5 EOF'S OUT TO DO. THUS WE NEED TO BACKSPACE OVER 4 RECORDS
;TO LEAVE TAPE IN POSITION AFTER THE FIRST EOF. DO IT THUS:
;
	.MCALL	QIOW$S
	QIOW$S	#IO.SPF,#OUTLUN,#20.,,R3,OUTVEC,#-4	;BACKSPACE OVER 4 EOFS
	BIT	#20,D2DMSK	;/FR FINAL REWIND WANTED?
	BEQ	250$		;IF NOT SKIP IT
	QIOW$S	#IO.RWD,#OUTLUN,#20.,,R3,OUTVEC	;DO THE REWIND
250$:
;
; NOTE THE WAIT TO ENSURE WE GET DONE.
; THIS WILL PREVENT THE IO.KIL FROM CLOBBERINT THE EOF WRITES THAT ARE
; OUTSTANDING.
;
	.ENDC
	RESUME			; CONTINUE THE MAIN TASK
	ASTX$S			; EXIT AST
	.page
	.SBTTL	--SUBROUTINES--
	.SBTTL	NODADD	-  ADD NODE TO DEQUE
;
; NODADD-	ADD NODE TO DEQUE LIST
;
; INPUTS:	R5 = NODE ADDRESS
;		R4 = PRIOR NODE ADDRESS
;
;  OUTPUTS:	SAME
;

NODADD:	MOV	R4,2(R5)	; SETUP BACKWARD POINTER
	MOV	(R4),(R5)	; SETUP FORWARD POINTER
	MOV	R5,(R4)		; SETUP PRIOR FORWARD POINTER
	MOV	(R5),R5		; POINT R5 TO NEXT NODE
	MOV	(R4),2(R5)	; SETUP NEXT BACKWARD POINTER
	MOV	(R4),R5		; RESTORE R5
	RETURN


	.SBTTL	NODDEL	-  DELETE NODE FROM DEQUE
;
; NODDEL-	DELETE NODE FROM DEQUEUE LIST
;
;  INPUTS:	R5 = NODE ADDRESS
;
;  OUTPUTS:	SAME
;

NODDEL:	MOV	(R5),@2(R5)	; POINT PRIOR TO NEXT
	MOV	(R5),-(SP)	; SAVE NEXT ADDRESS
	ADD	#2,(SP)		; POINT TO NEXT BACWARD POINTER
	MOV	2(R5),@(SP)+	; POINT NEXT BACKWARD TO PRIOR
	RETURN



        .END    BEGIN                                                   
;JKN02
