Google
 

Trailing-Edge - PDP-10 Archives - BB-H506D-SM_1983 - sort/source/srtjss.mac
There are 16 other files named srtjss.mac in the archive. Click here to see a list.
SUBTTL	TOPS-20 SPECIFIC PART OF SORT/MERGE
SUBTTL	D.L. CAMPBELL/DZN/DMN/BRF	3-Jun-81


;	"JSYS SAVES"


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


IFE FTOPS20,<PRINTX ? SRTJSS should not be present in TOPS-10 SORT/MERGE.>
IFN FTPRINT,<PRINTX [Entering SRTJSS.MAC]]>
SUBTTL	TABLE OF CONTENTS FOR SRTJSS



;                    Table of Contents for SRTJSS
;
;
;                             Section                             Page
;
;   1  TOPS-20 SPECIFIC PART OF SORT/MERGE ......................   1
;   2  TABLE OF CONTENTS FOR SRTJSS .............................   2
;   3  DEFINITIONS
;        3.1  TOPS-20 Specific Parameters .......................   3
;        3.2  Impure Data .......................................   4
;   4  ENTRY POINTS
;        4.1  TOPS-20 Entry Vector ..............................   6
;   5  PSORT.
;        5.1  SETUPI - Set Up Input Files .......................   7
;   6  I/O ROUTINES
;        6.1  INIINP - Initialize Next Input File
;             6.1.1  Set Up .....................................   7
;             6.1.2  Disk .......................................   9
;             6.1.3  Magtape ....................................  11
;             6.1.4  Others .....................................  13
;        6.2  INIOUT - Initialize Next Output File
;             6.2.1  Set Up .....................................  17
;             6.2.2  Disk .......................................  18
;             6.2.3  Magtape ....................................  19
;        6.3  Magtape Utility Routines ..........................  20
;        6.4  File Utility Routines
;             6.4.1  Close Master Input/Output File .............  21
;             6.4.2  Delete a Temp File .........................  22
;             6.4.3  Unmap Buffer Pages For a File ..............  23
;             6.4.4  Initialize Output Temporary File ...........  26
;             6.4.5  Append to Temporary File ...................  27
;   7  TRY TO RENAME SINGLE TEMP FILE TO OUTPUT FILE ............  30
;   8  GETREC
;        8.1  GETBUF - Input 1 Physical Buffer
;             8.1.1  Set Up .....................................  33
;             8.1.2  Terminal ...................................  34
;             8.1.3  Disk .......................................  35
;             8.1.4  Magtape ....................................  39
;   9  PUTREC
;        9.1  PUTBUF - Output 1 Physical Buffer
;             9.1.1  Set Up .....................................  40
;             9.1.2  Disk .......................................  41
;             9.1.3  Magtape ....................................  43
;  10  PSORT.
;       10.1  Memory Management Routines for TOPS-20 ............  44
;  11  COLLATING SEQUENCE ROUTINES ..............................  54
;  12  ERROR MESSAGES ...........................................  55
SUBTTL	DEFINITIONS -- TOPS-20 Specific Parameters

OUTSIZ==PGSIZ*3			;DESIRABLE SIZE FOR SINGLE OUTPUT BUFFER
PPTBUF==2			;MINIMUM PAGES PER TEMP FILE BUFFER
SUBTTL	DEFINITIONS -- Impure Data

	SEGMENT IMPURE		;[C20]
ZJ.BEG:!			;[427] FIRST DATUM TO DELETE

CBPTR:	BLOCK	1		;CANONICAL BYTE POINTER
INLST:	BLOCK	2		;I/O COMMAND LIST FOR MTA INPUT
OUTLST:	BLOCK	2		;I/O COMMAND LIST FOR MTA OUTPUT
PGTAB:	BLOCK	<1000/^D36>+1	;[C13] BIT TABLE OF MAPPED PAGES
LEANUM:	BLOCK	1		;ARGUMENT TO /LEAVES SWITCH
OBUFSZ:	BLOCK	1		;(MINIMUM) SIZE OF OUTPUT BUFFER
DFMTRS:	BLOCK	1		;DEFAULT MTA RECORD SIZE
DFMTMD:	BLOCK	1		;[C03] DEFAULT MTA HARDWARE MODE
MTTEMP:	BLOCK	2		;[C12] TEMPORARY MTOPR% BLOCK
MOUNTR:	BLOCK	1		;[C12] MOUNTR AROUND FLAG, 0=NO, -1=YES
AZTEMP:	BLOCK	^D20		;[405] TEMP TO HOLD FILESPEC AT XGTJFN
SOURCE:	BLOCK	1		;[464] AREA TO SAVE ARGS FOR PMAP
DEST:	BLOCK	1		;[464] ...
ACCESS:	BLOCK	1		;[464] ...
PGSATM:	BLOCK	1		;[464] THE # OF PAGES ATTEMPTED TO PMAP
GTJARG:	BLOCK	1		;ARG FOR GETJI JSYS
ZJ.END==.-1			;[427] LAST DATUM TO DELETE

	SEGMENT HPURE			;[C20]
	BLOCK	1			;[427] NEXT IN LIST
	ZJ.BEG,,ZJ.END			;[427] DATA TO ZERO
	.LINK	S.LNK,.-2		;[427] TELL LINK WHAT TO DO
;DEFINE MNEMONICS FOR I/O MODES AND TABLE OF CORRESPONDING BYTE POINTERS

DEFINE GENMOD,<
  ..INDX==1
  X MODSIXBIT,<440600,,0>
  X MODASCII,<440700,,0>
  X MODEBCDIC,<441100,,0>
  X MODBINARY,<444400,,0>
 PURGE ..INDX
>

DEFINE X(NAME,BPTR),<
NAME==..INDX
	BPTR
..INDX==..INDX+1
>

BYTTAB:	GENMOD

IFE FTCOBOL,<
 DEFINE	ENDMODULE<
	$PURGE
	END	<ENTVLN,,ENTVEC>>

 DEFINE	COMPARE (R,J)<
	JSP	P4,@.CMPAR	;;[OK]
 >
>

IF1,<
DEFINE	$JRST$ <BLOCK 1>	;KEEP MACRO HAPPY
>
SUBTTL	ENTRY POINTS -- TOPS-20 Entry Vector


IFE FTCOBOL,<
	SEGMENT	HPURE		;[C20]

ENTVEC:	JRST	START		;MAIN ENTRY POINT
	JRST	START		;REENTER ENTRY POINT
	EXP	V%SORT		;VERSION NUMBER
	USRVLN,,USRVEC		;USER ENTRY VECTOR

	ENTVLN==.-ENTVEC

USRVEC:	JRST	FORENT		;FORTRAN ENTRY POINT

	USRVLN==.-USRVEC
SUBTTL	PSORT. -- SETUPI - Set Up Input Files

;SETUPI - PASS OVER INPUT FILES, REMEMBER LARGEST BUFFER SIZE  REQUIRED,
;SET UP SOME RANDOM STUFF

SETUPI:	HRRZ T1,IOMODE		;[C20] GET I/O MODE
	MOVE T1,BYTTAB-1(T1)	;[OK] GET PROPER CANONICAL BYTE POINTER
	MOVEM T1,CBPTR		;SAVE FOR LATER
	SETZM PGTAB		;CLEAR TABLE OF MAPPED PAGES
	MOVE T1,[PGTAB,,PGTAB+1] ;SET UP FOR BLT
	BLT T1,PGTAB+<1000/^D36>	;[C13] CLEAR THE TABLE
	MOVE T1,F.INZR		;GET PTR TO INPUT FILESPEC CHAIN
	CALL SETUP2		;GET SIZE OF LARGEST BUFFER IN CHAIN
	MOVEM T1,MXDVSZ		;SAVE IT
	RET			;RETURN

SETUPO:	MOVE T1,F.OUZR		;GET PTR TO OUTPUT FILESPEC
	CALL SETUP2		;GET SIZE OF LARGEST BUFFER IN CHAIN
	MOVEM T1,OBUFSZ		;REMEMBER
	RET

;FIND SIZE OF LARGEST BUFFER IN CHAIN
; CALL WITH T1 POINTING TO X. BLOCK CHAIN
; RETURN WITH T1/ SIZE OF BUFFER

SETUP2:	PUSH P,P1		;GET A REG FOR PTR TO X. BLOCK
	PUSH P,P2		;GET REG TO HOLD MAX BUFFER SIZE
	SETZ P2,		;INIT MAX BUFFER SIZE
	MOVE P1,T1		;COPY HEAD OF X. BLOCK CHAIN

SETUP1:	SETZ T1,
	LDB T2,[POINT 9,X.DVCH(P1),17]	;[OK] GET DEVICE TYPE
	CAXN T2,.DVDSK		;DISK?
	MOVEI T1,PGSIZ		;YES, BUFFER WANTS TO BE 1 PAGE
	CAXN T2,.DVMTA		;MTA?
	CALL	[MOVEI F,FCBORG		;[C02] YES, SETUP MINIMAL FCB
		MOVE T1,X.FLG(P1)	;[OK] [C02]   ..
		MOVEM T1,FILFLG(F)	;[C02]   ..
		CALL MTBFSZ		;[C02] COMPUTE BUFFER SIZE
		TRZE T1,PGMSK		;[365] ROUND UP TO PAGE BOUNDARY
		ADDI T1,PGSIZ		;[365]   BECAUSE MTA BUFFERS GO ON PG BOUNDARY
		LSH T1,POW2(2)		;DOUBLE BECAUSE MTA IS DOUBLE BUFFERED
		RET]
	SKIPN T1		;ALL ELSE USES
	MOVEI T1,^D100		; 100 WORD BUFFERS
	CAML T1,P2		;BIGGEST SO FAR?
	MOVEM T1,P2		;YES, SAVE THIS
	SKIPE P1,X.NXT(P1)	;[OK] ARE THERE MORE FILESPECS IN LIST?
	JRST SETUP1		;YES, PROCESS NEXT
	MOVE T1,P2		;RETURN RESULT IN T1
	POP P,P2		;NO, RESTORE P REGISTERS
	POP P,P1		; ..
	RET			;AND RETURN
SUBTTL	I/O ROUTINES -- INIINP - Initialize Next Input File -- Set Up

;CALL WITH:	MOVEI F,<ADDRESS OF FCB>
;		PUSHJ P,INIINP

INIINP:	PUSH P,P1		;GET A REGISTER
	MOVE P1,FILXBK(F)	;GET POINTER TO X. BLOCK IN P1
	HRRZ T1,X.JFN(P1)	;[OK] GET JFN
	HRLZM T1,FILPGN(F)	;STORE IN FCB
	MOVE T3,X.FLG(P1)	;[OK] GET FILE FLAGS
	MOVEM T3,FILFLG(F)	;SAVE IN FCB
	MOVE T1,X.BLKF(P1)	;[OK] [C06] FETCH BLOCKING FACTOR
	HRRZM T1,FILBLK(F)	;[C06] STORE AS AOBJN WORD (TO FAIL FIRST TIME)
	SETZM FILSIZ(F)
	SETZM FILEOF(F)
	SETZM FILCNT(F)
	LDB T1,[POINT 9,X.DVCH(P1),17] ;[OK] GET DEVICE TYPE
	CAILE T1,.DVNET		;RANGE CHECK
	JRST E$$NSD		;NO SUCH DEVICE
	JUMPL T1,E$$NSD		;CAN'T BE NEGATIVE
	CALL	@[IFIW INDSK	;[C20] 0 - DISK
		  IFIW E$$NSD	;[C20] 1 - NO SUCH DEVICE
		  IFIW INMTA	;[C20] 2 - MAGTAPE
		  REPEAT 4,<IFIW E$$NSD> ;[C20] 3-6 - NO SUCH DEVICE
		  IFIW E$$CDL	;[C20] 7 - LINE PRINTER
		  IFIW INCDR	;[C20] 10 - CARD READER
		  IFIW E$$FED	;[C20] 11 - FRONT-END DEVICE
		  IFIW INTTY	;[C20] 12 - TERMINAL
		  IFIW INPTY	;[C20] 13 - PSEUDO-TERMINAL
		  IFIW E$$NSD	;[C20] 14 - NO SUCH DEVICE
		  IFIW INNUL	;[C20] 15 - NULL DEVICE
		  IFIW E$$AND](T1)	;[C20] 16 - ARPANET DEVICE
	POP P,P1		;RESTORE P1
	RET			;AND RETURN
SUBTTL	I/O ROUTINES -- INIINP - Initialize Next Input File -- Disk


;COMPUTES:	FILEOF(F)/ SIZE OF FILE, IN BYTES
;		FILBPB(F)/ NUMBER OF BYTES PER BUFFER
;		FILBUF(F)/ (LH) NUMBER OF PAGES IN A BUFFER
;		FILBUF(F)/ (RH) FIRST PAGE OF BUFFER

INDSK:	MOVX T1,FI.DSK		;SET DISK BIT FOR LATER USE
	IORM T1,FILFLG(F)	; ..
	SKIPE X.BLKF(P1)	;[OK] [305] IF BLOCKED FILE,
	CALL BLKSET		;[305]   SET UP FOR IT
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVE T2,[2,,.FBBYV]	;GET BYTE SIZE AND FILE SIZE
	MOVEI T3,T3		;RETURN IN T3 AND T4
	GTFDB%			; ..
	LDB T1,[POINT 6,T3,11]	;GET BYTE SIZE ALONE
	JUMPE T1,INDSK2		;MAKE ZERO BYTE SIZE IMPLY 36-BIT BYTES
	CAIN T1,7		;7-BIT BYTES?
	JRST INDSK1		;YES, GO HANDLE
	CAIN T1,^D36		;36-BIT BYTES?
	JRST INDSK2		;YES
	CAIN T1,^D9		;[305] 9-BIT BYTES?
	JRST INDSK9		;[305] YES, HANDLE IT
	$ERROR (?,IBS,<Invalid byte size for >,+)
	HLRZ T2,FILPGN(F)
	$MORE (FILESPEC,T2)
	$DIE

INDSK9:	MOVX T1,MODEBCDIC	;[305] IF FILE HAS 9-BIT BYTES,
	CAME T1,IOMODE		;[305]   I/O MODE HAD BETTER BE EBCDIC
	JRST E$$FMC		;[305] FILE MODE CONFLICT
	MOVEM T4,FILEOF(F)	;[305] STUFF BYTE COUNT INTO FDB
	JRST INDSK3		;[305] REJOIN COMMON CODE

INDSK1:	MOVEI T1,MODASCII	;ARE WE SORTING ASCII RECORDS?
	CAME T1,IOMODE		; ..
	JRST E$$FMC		;[305] FILE'S MODE CONFLICTS WITH MODE SWITCH
	MOVE T1,T4		;YES, GET BYTE COUNT IN T1
	IDIVI T1,5		;ROUND TO WORD BOUNDARY
	SKIPE T2		;IF REMAINDER
	ADDI T1,1		; ONE MORE WORD
	IMULI T1,5		;CONVERT BACK TO BYTES
	MOVEM T1,FILEOF(F)	;SAVE IN FCB
	JRST INDSK3

;HERE IF FILE WRITTEN IN 36-BIT BYTES.  ANY I/O MODE IS LEGAL,
; SO MUST COMPUTE NUMBER OF N-BIT BYTES IN FILE GIVEN NUMBER
; OF 36-BIT BYTES

INDSK2:	MOVE T1,T4		;COPY WORD COUNT
	IMUL T1,IOBPW2		;[C03] MULTIPLY BY BYTES PER WORD
	MOVEM T1,FILEOF(F)	;SAVE AWAY
INDSK3:	MOVE T1,IBUFNO		;GET NO. OF PAGES PER INPUT BUFFER
	HRLM T1,FILBUF(F)	;STORE IT
	SKIPGE BUFALC		;HAS BUFFER BEEN ALLOCATED YET?
	JRST INDSK5		;YES
	LSH T1,POW2(PGSIZ)	;NO, CONVERT PAGES TO WORDS
	CALL ALCBPG		;ALLOCATE T1 WORDS ON PAGE BOUNDARY
	LSH T1,-<POW2(PGSIZ)>	;CONVERT ADDRESS TO PAGE NO.
	HRRM T1,FILBUF(F)	;STORE PAGE NO. OF BUFFER IN FCB
INDSK5:	CALL MRKPGS		;[326] MARK PAGES AS POSSIBLY MAPPED
	MOVE T1,FILBPK(F)	;[C17] GET BYTES PER BLOCK
	MOVEM T1,FILKCT(F)	;[C17] SAVE AS BLOCK BYTE COUNT
	HLRZ T1,FILBUF(F)	;[321] GET PAGES PER BUFFER
	LSH T1,POW2(PGSIZ)	;CONVERT TO WORDS
	IMUL T1,IOBPW2		;[C03] CONVERT TO APPROPRIATE NO. OF BYTES
	MOVEM T1,FILBPB(F)	;SAVE NO. OF BYTES PER BUFFER
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,OF%RD		;OPEN FOR READ
	OPENF%			;[335]   ..
	  ERJMP E$$OPN		;OOPS
	RET
SUBTTL	I/O ROUTINES -- INIINP - Initialize Next Input File -- Magtape


;COMPUTES:	FILEOF(F)/ NOT USED FOR MAGTAPE
;		FILBPB(F)/ BYTES PER BUFFER 
;		FILBUF(F)/ (LH) WORDS PER BUFFER (NOT PAGES)
;		FILBUF(F)/ (RH) ADDRESS OF BUFFER (WORD ADDRESS, NOT PG)

INMTA:	MOVX T1,FI.MTA		;REMEMBER THIS IS A MAGTAPE
	IORM T1,FILFLG(F)	; ..
	CALL MTBFSZ		;COMPUTE BUFFER SIZE, IN WORDS
	SKIPN FILBUF(F)		;[N26] IS THERE A BUFFER FROM A PREVIOUS FILE?
	JRST INMTA1		;[N26] NO, ITS THE FIRST TIME
	HLRZ T2,FILBUF(F)	;[N26] GET THE PREVIOUS SIZE
	CAMN T1,T2		;[N26] IS IT THE SAME SIZE AS LAST TIME?
	JRST INMTA2		;[N26] YES, USE PREVIOUS BUFFERS
	SUB T2,T1		;[N26] GET THE DIFFERENCE
	CAIL T2,PGSIZ		;[N26] LESS THAN 1 PAGE?
	JRST INMTA1		;[N26] NO, ALLOCATE NEW BUFFERS FOR NOW
	HRLM T1,FILBUF(F)	;[N26] RESET THE BUFFER SIZE
	HRLM T1,FILBF2(F)	;[N26] BUT NOT THE BUFFER LOCATION
	JRST INMTA2		;[N26] AND CONTINUE WITH BUFFERS ALLOCATED
INMTA1:	HRLZM T1,FILBUF(F)	;[N26] SAVE SIZE OF BUFFER
	IMUL T1,IOBPW2		;[C03] MULTIPLE BY BYTES PER WORD
	MOVEM T1,FILBPB(F)	;SAVE IN FCB
	HLRZ T1,FILBUF(F)	;RECOVER WORDS PER BUFFER
	CALL ALCBPG		;ALLOCATE BUFFER ON PAGE BOUNDARY
	HRRM T1,FILBUF(F)	;SAVE ITS ADDRESS
	HLRZ T1,FILBUF(F)	;GET WORDS PER BUFFER
	CALL ALCBPG		;ALLOCATE SECOND BUFFER ON PG BOUNDARY
	HRRM T1,FILBF2(F)	;[C02] SAVE ITS ADDRESS
INMTA2:	HLRZ T1,FILPGN(F)	;[N26] GET JFN
	MOVX T2,OF%RD!FLD(17,OF%MOD) ;OPEN FOR READ, DUMP MODE
	OPENF%			;[335]   ..
	  ERJMP E$$OPN
	CALL SMTLBS		;[C12] SET MOUNTR AND FI.ATO
	CALL POSITF		;[C11] POSITION AT FILE
	CALL SMTDEN		;[C01] SET DENSITY
	CALL SMTPAR		;[C01] SET PARITY
	CALL SMTMOD		;[C01] SET HARDWARE MODE
	CALL LABSET		;SET UP TAPE LABEL STUFF
	CALL STRTIO		;START INPUT ON FIRST BUFFER
	CALLRET CHKLBL		;CHECK HEADER LABELS AND RETURN
;START I/O ON THE FIRST MAGTAPE BUFFER.  THE FIRST CALL TO GETBUF
; WILL START I/O ON THE SECOND BUFFER, RETURNING WHEN ALL
; I/O TO THE FIRST BUFFER IS COMPLETE

STRTIO:	HLRZ T1,FILPGN(F)	;GET JFN FOR TAPE
	GDSTS%			;[335] GET CURRENT STATUS
	TXZ T2,MT%IRL		;CLEAR POSSIBLE LEFTOVER ERROR BITS
	SDSTS%			;[335]   ..
	MOVEI T1,INLST		;[C02] LOAD PARAMETER FOR SWTBUF
	CALL SWTBUF		;[C02] SET BYTE POINTER AND IOWD BLOCK

;START I/O ON FIRST BUFFER

	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,DM%NWT		;LIGHT "DO NOT WAIT" BIT
	HRRI T2,INLST		;ADDRESS OF COMMAND LIST
	DUMPI%			;[335] INITIATE I/O
	  ERJMP E$$DME
	RET
SUBTTL	I/O ROUTINES -- INIINP - Initialize Next Input File -- Others


INNUL:
INTTY:
INPTY:
INCDR:	CALL SETUP7		;SET UP FOR 7-BIT TRIVIAL DEVICE
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,OF%RD!FLD(7,OF%BSZ) ;OPEN FOR READ, 7-BIT BYTES
	OPENF%			;[335]   ..
	  ERJMP E$$OPN
	RET

;ROUTINES TO INITIALIZE SIMPLEMINDED DEVICES FOR OUTPUT

OUNUL:
OULPT:
OUTTY:
OUPTY:
OUCDP:	CALL SETUP7		;SET UP FOR SIMPLE 7-BIT DEVICE
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,OF%WR!FLD(7,OF%BSZ) ;OPEN FOR WRITE, 7-BIT BYTES
	OPENF%			;[335]
	  ERJMP E$$OPN
	MOVE T1,FILBPB(F)	;GET BYTES PER BUFFER
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER WAITING TO BE FILLED
	RET

;SETUP7 - SET UP FOR SIMPLEMINDED ASCII-ONLY DEVICES

SETUP7:	MOVE T1,IOMODE		;GET I/O MODE
	CAXE T1,MODASCII	;LEGAL?
	JRST E$$IDM		;NO, COMPLAIN
	MOVEI T1,^D100		;ARBITRARILY ALLOCATE 100-WD BUFFER
	HRLZM T1,FILBUF(F)	;SAVE WORDS PER BUFFER IN FCB
	CALL ALCBUF		;ALLOCATE BUFFER SPACE
	HRRM T1,FILBUF(F)	;SAVE ADDRESS OF BUFFER
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;SAVE IT
	MOVEI T1,^D100		;COMPUTE BYTES PER BUFFER
	IMUL T1,IOBPW2		;[C03]  ..
	MOVEM T1,FILBPB(F)	;SAVE IT
	RET
;COMPUTE BUFFER SIZE FOR MAGTAPE, ACCOUNTING FOR BLOCKING FACTOR

MTBFSZ:	CALL MTMODE		;[C03] DO MTA HARDWARE MODE CALCULATIONS
	SKIPN T3,X.BLKF(P1)	;[OK] BLOCKING FACTOR SPECIFIED?
	JRST MTBFS1		;UNBLOCKED, USE DEFAULT RECORD SIZE
	HRRZ T1,IOMODE		;[C20] DISPATCH ON I/O MODE
	JRST	@[IFIW MTB6BT	;[C20] SIXBIT
		  IFIW MTBASC	;[C20] ASCII
		  IFIW MTBBCD	;[C20] EBCDIC
		  IFIW MTBBIN]-1(T1)	;[C20] BINARY

MTB6BT:	MOVE T1,RECSIZ		;RECORD SIZE
	IMUL T1,T3		;TIMES BLOCKING FACTOR
	RET			;EQUALS BUFFER SIZE

MTBASC:	MOVE T1,RECORD		;RECORD SIZE IN BYTES
	ADDI T1,2		;PLUS CRILLIF
	IMUL T1,T3		;TIMES BLOCKING FACTOR
	IDIVI T1,5		;DIVIDED BY BYTES PER WORD
	SKIPE T2		;PLUS ONE
	ADDI T1,1		; IF ANY PARTIAL WORDS
	RET			;EQUALS BUFFER SIZE IN WORDS

MTBBCD:	MOVE T1,RECORD		;RECORD SIZE IN BYTES
	SKIPGE FILFLG(F)	;[C06] IF VARIABLE?
	ADDI T1,4		;[C06] ADD RECORD HEADER WORD
	IMUL T1,T3		;TIMES BLOCKING FACTOR
	IDIVI T1,4		;DIVIDED BY BYTES PER WORD
	SKIPE T2		;PLUS ONE
	ADDI T1,1		; IF ANY PARTIAL WORDS
	SKIPGE FILFLG(F)	;[C06] IF VARIABLE?
	ADDI T1,1		;[C06] ADD BLOCK HEADER WORD
	RET			;EQUALS BUFFER SIZE

MTBBIN:	MOVE T1,RECSIZ		;RECORD SIZE IN WORDS
	IMUL T1,T3		;TIMES BLOCKING FACTOR
	RET			;IS BUFFER SIZE


MTBFS1:	MOVE T1,DFMTRS		;GET DEFAULT MAGTAPE RECORD SIZE IN BYTES
	IDIV T1,FILHBW(F)	;[C06] COMPUTE NO. OF WORDS
	SKIPE T2		;[C06] COUNT PARTIAL WORDS
	ADDI T1,1		;[C06]  ..
	RET
;GET MAGTAPE HARDWARE MODE AND OTHER CALCULATIONS

MTMODE:	MOVE T2,FILFLG(F)	;[C03] A HARDWARE MODE REQUESTED?
	TXNE T2,FI.IND!FI.STA	;[C03]  ..
	JRST MTMOD1		;[C03] YES
	MOVE T3,DFMTMD		;[C03] NO, SET DEFAULT MODE
	CAIN T3,.SJDM8		;[C03] INDUSTRY COMPATIBLE?
	TXO T2,FI.IND		;[C03] YES, REMEMBER IT
	CAIN T3,.SJDMA		;[C03] ANSI-ASCII?
	TXO T2,FI.STA		;[C03] YES, REMEMBER IT
	MOVEM T2,FILFLG(F)	;[C03] SAVE NEW FLAGS
MTMOD1:	MOVEI T1,DFMTMD		;[C03] GET ACTUAL MODE
	TXNE T2,FI.IND		;[C03] INDUSTRY COMPATIBLE?
	MOVEI T1,.SJDM8		;[C03] YES, REMEMBER IT
	TXNE T2,FI.STA		;[C03] ANSI-ASCII?
	MOVEI T1,.SJDMA		;[C03] YES, REMEMBER IT
	MOVEI T2,1		;[C03] COMPUTE HARDWARE BYTES PER WORD
				;[C03] ASSUME 1-BYTE WORDS
	CAIN T1,.SJDM6		;[C03] SIXBIT MODE?
	MOVEI T2,6		;[C03] YES, 6-BYTE WORDS
	CAIN T1,.SJDMA		;[C03] ANSI-ASCII MODE?
	MOVEI T2,5		;[C03] YES, 5-BYTE WORDS
	CAIN T1,.SJDM8		;[C03] INDUSTRY COMPATIBLE MODE?
	MOVEI T2,4		;[C03] YES, 4-BYTE WORDS
	MOVEM T2,FILHBW(F)	;[C03] SAVE HARDWARE BYTES PER WORD
	RET			;[C03] DONE
;LABSET -- SET UP LABEL NAMES IN SIXBIT FOR LABEL PROCESSING


LABSET:	MOVE T1,X.LABL(P1)	;[OK] GET LABEL TYPE FOR THIS TAPE
	MOVE T2,FILFLG(F)	;[C12]  AND FLAGS
	CAXE T1,LABOMITTED	;ARE LABELS OMITTED?
	TXNE T2,FI.ATO		; OR IS PULSAR DOING LABELLING FOR US?
	RET			;YES TO EITHER, SKIP THIS
	PUSH P,P2		;GET A REG
	MOVEI T1,^D17		;ALLOCATE ENUF WORDS FOR FILENAME.EXT
	CALL GETSPC		; ..
	  JRST E$$NEC
	MOVE P2,T1		;SAVE ADDR OF STRING SPACE
	HRL T1,T1		;MAKE BLT POINTER
	HRRI T1,1(P2)		;[OK]  TO CLEAR STRING SPACE
	HRRZI T2,^D17-1(P2)	;[OK] END OF BLT
	SETZM 0(P2)		;[OK] CLEAR FIRST WORD
	BLT T1,0(T2)		;[OK] SPREAD IT AROUND
	SETZM X.RIB+.RBNAM(P1)	;[OK] CLEAR CELLS IN X. BLOCK
	SETZM X.RIB+.RBEXT(P1)	;[OK]  WHICH WILL RECEIVE SIXBIT NAME.EXT
	HRRO T1,P2		;CONSTRUCT STRING PTR TO SPACE
	HRRZ T2,X.JFN(P1)	;[OK] GET JFN OF THIS TAPE
	MOVX T3,<1B8+1B11+JS%PAF> ;GET FILENAME.EXT, WITH THE DOT
	JFNS%			;[335] GET THE NAME
	  ERJMP E$$NFS		;NO FILE NAME SPECIFIED FOR LABELED TAPE

;CONVERT THE ASCIZ STRING POINTED TO BY P2 (NAME.EXT) TO
; SIXBIT AND STUFF INTO X.RIB+.RBNAM AND X.RIB+.RBEXT

	HRLI P2,(POINT 7,)	;POINT TO ZEROTH BYTE
	MOVEI T4,6		;MAXIMUM CHARACTERS ALLOWED
	MOVE T3,[POINT 6,X.RIB+.RBNAM(P1)] ;[OK] WHERE TO PUT SIXBIT

LABST1:	ILDB T1,P2		;GET THE NEXT BYTE
	JUMPE T1,LABST2		;NULL TERMINATES FILENAME
	CAIN T1,"."		;CHECK FOR DOT
	JRST LABST3		;FOUND IT, GO ON TO TYPE
	SOJL T4,E$$FTL		;FILESPEC FIELD TOO LONG FOR LABELLED TAPE
	CAIL T1,"a"		;[C04] CONVERT LOWER CASE TO UPPER CASE
	CAILE T1,"z"		;[C04]  ..
	SKIPA			;[C04]  ..
	SUBI T1,"a"-"A"		;[C04]  ..
	SUBI T1,40		;CONVERT BYTE TO SIXBIT
	IDPB T1,T3		;PLUNK INTO X. BLOCK
	JRST LABST1		;ONCE MORE

LABST3:	MOVEI T4,3		;TYPE IS 3 CHARS MAX
	MOVE T3,[POINT 6,X.RIB+.RBEXT(P1)]	;[OK]
	JRST LABST1		;GO DO THE TYPE

LABST2:	MOVEI T1,^D17		;RETURN STRING SPACE TO FREE POOL
	CALL FRESPC		; ..
	POP P,P2		;RESTORE P2
	RET			;RETURN
SUBTTL	I/O ROUTINES -- INIOUT - Initialize Next Output File -- Set Up

;CALL:		PUSHJ P,INIOUT
;RETURNS:	+1/ ALWAYS

INIOUT:	MOVEI F,FCBORG		;OUTPUT FILE IS ALWAYS 1ST FCB
	PUSH P,P1		;GET A REGISTER
	MOVE P1,F.OUZR		;GET PTR TO X. BLOCK
	MOVEM P1,FILXBK(F)	;SAVE IN FCB
	SETZM FILSIZ(F)		;INIT FILE SIZE
	SETZM FILEOF(F)		; AND BYTE COUNT
	MOVE T1,X.FLG(P1)	;[OK] PUT FLAGS IN FCB
	TXO T1,FI.OUT		;[C06] REMEMBER THIS IS AN OUTPUT FILE
	MOVEM T1,FILFLG(F)	; ..
	MOVE T1,X.JFN(P1)	;[OK] GET JFN
	HRLZM T1,FILPGN(F)	;SAVE IT AND ZAP PAGE COUNT
	MOVE T1,X.BLKF(P1)	;[OK] [C06] FETCH BLOCKING FACTOR
	HRRZM T1,FILBLK(F)	;[C06] STORE AS AOBJN WORD (TO FAIL FIRST TIME)
	LDB T1,[POINT 9,X.DVCH(P1),17] ;[OK] GET DEVICE TYPE AND DISPATCH ON IT
	CAILE T1,.DVNET		;RANGE CHECK
	JRST E$$NSD		;NO SUCH DEVICE
	JUMPL T1,E$$NSD		;CAN'T BE NEGATIVE
	CALL	@[IFIW OUDSK	;[C20] 0 - DISK
		  IFIW E$$NSD	;[C20] 1 - NO SUCH DEVICE
		  IFIW OUMTA	;[C20] 2 - MAGTAPE
		  REPEAT 4,<IFIW E$$NSD> ;[C20] 3-6 - NO SUCH DEVICE
		  IFIW OULPT	;[C20] 7 - LPT
		  IFIW E$$CDC	;[C20] 10 - CDR
		  IFIW E$$FED	;[C20] 11 - FRONT-END DEVICE
		  IFIW OUTTY	;[C20] 12 - TERMINAL
		  IFIW OUPTY	;[C20] 13 - PSEUDO-TERMINAL
		  IFIW E$$NSD	;[C20] 14 - NO SUCH DEVICE
		  IFIW OUNUL	;[C20] 15 - NULL DEVICE
		  IFIW E$$AND](T1)	;[C20] 16 - ARPANET DEVICE
	POP P,P1		;RESTORE P1
	RET
SUBTTL	I/O ROUTINES -- INIOUT - Initialize Next Output File -- Disk


OUDSK:	MOVX T1,FI.DSK		;REMEMBER THIS IS A DISK FILE
	IORM T1,FILFLG(F)	; ..
	SKIPE X.BLKF(P1)	;[OK] [305] IF BLOCKED FILE,
	CALL BLKSET		;[305]   SET UP FOR IT
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,OF%RD!OF%WR	;OPEN FOR READ AND WRITE
	MOVE T3,IOMODE		;GET I/O MODE
	CAXN T3,MODASCII	;IS THIS TO BE AN ASCII FILE?
	IORX T2,FLD(7,OF%BSZ)	;YES, SET BYTE SIZE TO 7
	OPENF%			;[335]
	  ERJMP E$$OPN
	MOVE T1,OBUFNO		;GET PAGES PER OUTPUT BUFFER
	HRLM T1,FILBUF(F)	;SAVE IN FCB
	LSH T1,POW2(PGSIZ)	;COMPUTE WORDS PER BUFFER
	CALL ALCBPZ		;[371] ALLOC BUFFER ON PG BOUNDRY AND ZERO IT
	LSH T1,-<POW2(PGSIZ)>	;COMPUTE BUFFER PAGE NUMBER
	HRRM T1,FILBUF(F)	;SAVE
	LSH T1,POW2(PGSIZ)	;CONVERT BACK TO ADDRESS
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;SAVE IT
	MOVE T1,FILBPK(F)	;[C17] GET BYTES PER BLOCK
	MOVEM T1,FILKCT(F)	;[C17] SAVE AS BLOCK BYTE COUNT
	HLRZ T1,FILBUF(F)	;GET PAGES PER BUFFER
	LSH T1,POW2(PGSIZ)	;COMPUTE WORDS
	IMUL T1,IOBPW2		;[C03] COMPUTE BYTES
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER WAITING TO BE FILLED
	MOVEM T1,FILBPB(F)	;SAVE HOW MANY BYTES IN A BUFFER
	RET			;RETURN
SUBTTL	I/O ROUTINES -- INIOUT - Initialize Next Output File -- Magtape


OUMTA:	MOVX T1,FI.MTA		;REMEMBER THIS IS A MAGTAPE
	IORM T1,FILFLG(F)
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,OF%WR!FLD(17,OF%MOD) ;OPEN FOR WRITE, DUMP MODE
	OPENF%			;[335]   ..
	  ERJMP E$$OPN		;OPEN ERROR
	CALL SMTLBS		;[C12] SET MOUNTR AND FI.ATO
	CALL MTBFSZ		;COMPUTE BUFFER SIZE (IN WORDS)
	HRLZM T1,FILBUF(F)	;SAVE IT
	IMUL T1,IOBPW2		;[C03] COMPUTE BYTES PER BUFFER
	MOVEM T1,FILBPB(F)	;SAVE IT
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER EMPTY
	HLRZ T1,FILBUF(F)	;RECOVER WORDS PER BUFFER
	CALL ALCBPZ		;[371] ALLOCATE BUFFER ON A PAGE BOUNDARY AND ZERO IT
	HRRM T1,FILBUF(F)	;REMEMBER ITS ADDRESS
	HLRZ T1,FILBUF(F)	;GET BUFFER SIZE AGAIN, IN WORDS
	CALL ALCBPZ		;[371] ALLOCATE THE SECOND BUFFER
	HRRM T1,FILBF2(F)	;[C02] REMEMBER ITS ADDRESS
	CALL POSITF		;[C11] POSITION AT FILE
	CALL SMTDEN		;[C01] SET DENSITY
	CALL SMTPAR		;[C01] SET PARITY
	CALL SMTMOD		;[C01] SET HARDWARE MODE
	MOVEI T1,OUTLST		;[C02] LOAD PARAMETER FOR SWTBUF
	CALL SWTBUF		;[C02] SET BYTE POINTER AND IOWD BLOCK
	CALL LABSET		;SET UP LABEL PARAMETERS
	CALLRET WRTLBL		;WRITE HEADER LABEL AND RETURN
SUBTTL	I/O ROUTINES -- Magtape Utility Routines


;RWNDF - REWIND MAGTAPE POINTED TO BY F

RWNDF:	MOVX T1,FI.ATO		;[C12] NEED A VOLUME SWITCH?
	TDNN T1,FILFLG(F)	;[C12]  ..
	SKIPN MOUNTR		;[C12]  ..
	SKIPA			;[C12] NO
	CALL GMTVL1		;[C12] MAYBE
	MOVX T2,.MOREW		;[C12] FUNCTION TO REWIND TAPE
RWNDF1:	HLRZ T1,FILPGN(F)	;GET JFN
	MTOPR%			;[335] DO IT
	RET

;UNLDF - UNLOAD MAGTAPE POINTED TO BY F

UNLDF:	SKIPE MOUNTR		;[C12] MOUNTR AROUND?
	RET			;[C12] CAN'T DO UNLOAD
	MOVX T2,.MORUL		;[C12] GET FUNCTION CODE
	JRST RWNDF1		;JOIN COMMON CODE

;SKIPR - SKIP MAGTAPE ONE RECORD

SKIPR:	MOVX T2,.MOFWR		;LOAD FUNCTION CODE
	JRST RWNDF1		;JOIN COMMON CODE

;SKIPF - SKIP MAGTAPE ONE FILE

SKIPF:	MOVX T2,.MOFWF		;FUNCTION CODE
	JRST RWNDF1

;BKSPR - BACKSPACE MAGTAPE ONE RECORD

BKSPR:	MOVX T2,.MOBKR		;[C11] LOAD FUNCTION CODE
	JRST RWNDF1		;[C11] JOIN COMMON CODE

;BKSPF - BACKSPACE MAGTAPE ONE FILE

BKSPF:	MOVX T2,.MOBKF		;[C11] FUNCTION CODE
	JRST RWNDF1		;[C11] JOIN COMMON CODE

;WRTEOF - WRITE A TAPE MARK DURING LABEL PROCESSING

WRTEOF:	MOVX T2,.MOEOF		;[335] FUNCTION CODE FOR MTOPR%
	JRST RWNDF1		;DO IT

;SMTPAR - SET MAGTAPE PARITY POINTED TO BY F

SMTPAR:	MOVE T1,FILXBK(F)	;[C01] GET X. BLOCK
	SKIPGE T3,X.PAR(T1)	;[OK] [C01] GET PARITY PARAMETER FOR MTOPR%
	RET			;[C01] DON'T SET IF DEFAULT
	MOVX T2,.MOSPR		;[C01] FUNCTION CODE FOR MTOPR%
	JRST RWNDF1		;[C01] DO IT

;SMTDEN - SET MAGTAPE DENSITY POINTED TO BY F

SMTDEN:	MOVE T1,FILXBK(F)	;[C01] GET X. BLOCK
	SKIPGE T3,X.DEN(T1)	;[OK] [C12] [C01] GET DENSITY PARAMETER FOR MTOPR%
	RET			;[C12] DON'T SET IF DEFAULT
	MOVX T2,.MOSDN		;[C01] FUNCTION CODE FOR MTOPR%
	JRST RWNDF1		;[C01] DO IT

;SMTMOD - SET MAGTAPE HARDWARE MODE POINTED TO BY F

SMTMOD:	MOVE T1,FILFLG(F)	;[C01] GET FLAGS
	MOVX T2,.MOSDM		;[C01] FUNCTION CODE FOR MTOPR%
	TXNE T1,FI.IND		;[C01] INDUSTRY COMPATABLE?
	JRST   [MOVX T3,.SJDM8	;[C01] YES, GET PARAMETER FOR MTOPR%
		JRST RWNDF1]	;[C01] DO IT
	TXNE T1,FI.STA		;[C01] ANSI-ASCII?
	JRST   [MOVX T3,.SJDMA	;[C01] YES, GET PARAMETER FOR MTOPR%
		JRST RWNDF1]	;[C01] DO IT
	RET			;[C01] LEAVE AS DEFAULT

;ISITMT - CHECK TO SEE IF FILE POINTED TO BY F IS A MAGTAPE
;	  SKIP RETURN IF IT IS

ISITMT:	MOVX T1,FI.MTA
	TDNE T1,FILFLG(F)
	AOS (P)
	RET

;ISATBT - CHECK TO SEE IF MAGTAPE POINTED TO BY F IS AT BOT
;	  SKIP RETURN IF IT IS

ISATBT:	HLRZ T1,FILPGN(F)	;[C11] GET JFN
	GDSTS%			;[C11] GET DEVICE STATUS
	TXNE T2,MT%BOT		;[C11] BOT?
	AOS (P)			;[C11] YES, SKIP RETURN
	RET			;[C11]

;SMTLBS - SET MOUNTR AND FI.ATO (AUTO SYSTEM LABELING FLAGS)

SMTLBS:	HLRZ T1,FILPGN(F)	;[C12] GET JFN
	MOVX T2,.MORLI		;[C12] FUNCTION FOR LABEL TYPE
	MOVEI T3,MTTEMP		;[C12] GET MTOPR% BLOCK ADDRESS
	MOVEI T4,2		;[C12] SETUP MTOPR% BLOCK
	MOVEM T4,MTTEMP		;[C12]  ..
	MTOPR%			;[C12] DO IT
	  ERJMP [SETZM MOUNTR		;[C12] MOUNTR ISN'T AROUND
		RET]			;[C12]
	SETOM MOUNTR		;[C12] MOUNTR IS AROUND
	MOVX T1,FI.UNL		;[C12] UNLOAD REQUESTED?
	TDNE T1,FILFLG(F)	;[C12]  ..
	CALL [	ANDCAM T1,FILFLG(F)	;[C12] YES, CLEAR IT
		$ERROR (%,UIA,<Unloads illegal with MOUNTR.>) ;[C12] TELL USER
		RET]		;[C12] REJOIN MAIN FLOW
	MOVE T1,FILXBK(F)	;[C12] DENSITY REQUESTED?
	SKIPL X.DEN(T1)		;[OK] [C12]  ..
	CALL [	SETOM X.DEN(T1)	;[OK] [C12] YES, CLEAR IT
		$ERROR (%,DIA,<Setting DENSITY illegal with MOUNTR.>) ;[C12] TELL USER
		RET]		;[C12] REJOIN MAIN FLOW
	MOVE T1,MTTEMP+1	;[C12] GET LABEL TYPE
	CAXN T1,.LTUNL		;[C12] LABLED?
	RET			;[C12] NO
	MOVX T2,FI.ATO		;[C12] YES, SET FLAG
	IORM T2,FILFLG(F)	;[C12]  ..
	MOVE T2,FILXBK(F)	;[C22] NOT STANDARD LABELS REQUESTED?
	MOVX T3,LABSTANDARD	;[C22]  ..
	CAME T3,X.LABL(T2)	;[C22]  ..
	CALL [	MOVEM T3,X.LABL(T2)	;[C22] YES, FIX IT
		$ERROR (%,LIA,<Not standard labels illegal with MOUNTR.>) ;[C22] TELL USER
		RET]		;[C22] REJOIN MAIN FLOW
	MOVE T2,MODE		;[C12] GET MODE
	CAXN T1,.LTEBC		;[C12] EBCDIC LABELS?
	CAXE T2,MODEBCDIC	;[C12] AND NOT DEALING WITH EBCDIC?
	SKIPA			;[C12] NO
	JRST [$ERROR (?,ILT,<Inconsistent label type.>)] ;YES
	CAXN T1,.LTANS		;[C12] ANSI-ASCII LABELS?
	CAXE T2,MODASCII	;[C12] AND NOT DEALING WITH ASCII?
	SKIPA			;[C12] NO
	JRST E$$ILT		;[C12] YES
	RET			;[C12]

;GMTVOL - GET NEXT VOLUME FOR MAGTAPE POINTED TO BY F

GMTVOL:	MOVX T2,.MOVLS		;[C12] FUNCTION FOR VOLUME SWITCHING
	MOVEI T3,[	3		;[C12] GET ARGUMENT BLOCK, WORD COUNT
			.VSMRV		;[C12] RELATIVE VOLUME SUBFUNCTION
			1]		;[C12] NEXT RELATIVE REEL
	JRST RWNDF1		;[C12] JOIN COMMON CODE

;GMTVL1 - GET FIRST VOLUME FOR MAGTAPE POINTED TO BY F

GMTVL1:	MOVX T2,.MOVLS		;[C12] FUNCTION FOR VOLUME SWITCHING
	MOVEI T3,[	2		;[C12] GET ARGUMENT BLOCK, WORD COUNT
			.VSFST]		;[C12] FIRST VOLUME SUBFUNCTION
	JRST RWNDF1		;[C12] JOIN COMMON CODE

>;END IFE FTCOBOL
SUBTTL	I/O ROUTINES -- File Utility Routines -- Close Master Input/Output File

CLSMST:	TDZA T4,T4		;REMEMBER THIS IS A MASTER CLOSE
CLSFIL:	SETO T4,		;REMEMBER WE'RE CLOSING A TMP FILE
	MOVX T1,FI.OUT		;IS THIS AN OUTPUT FILE?
	TDNN T1,FILFLG(F)	; ..
	JRST CLSFL2		;NO, DON'T WRITE PARTIAL BUFFERS
	PUSH P,T4		;SAVE FLAG REG
	JSP T4,PUTBUF		;WRITE LAST PARTIAL BUFFER
	POP P,T4		;GET T4 BACK AGAIN
	MOVX T1,FI.DSK		;IS THIS A DISK FILE?
	TDNN T1,FILFLG(F)	; ..
	JRST CLSFL3		;NO, DON'T UPDATE FDB OR DO PMAPS
	JUMPN T4,CLSFL2		;[420] IF TEMP FILE CLOSE, DON'T UPDATE FDB
	MOVSI T1,.FBBYV(CF%NUD)	;SET FILE BYTE SIZE
	HLR T1,FILPGN(F)	;GET JFN
	MOVX T2,FB%BSZ		;MASK FOR BYTE SIZE FIELD
	MOVX T3,^D36B11		;DEFAULT IS 36-BIT BYTES
IFE FTCOBOL,<
	MOVE T0,IOMODE		;GET I/O MODE
	CAXN T0,MODASCII	;ASCII?
	MOVX T3,7B11		;YES, BYTES ARE 7 BITS LONG
	CAXN T0,MODEBCDIC	;EBCDIC?
	MOVX T3,9B11		;YES, BYTES ARE 9 BITS LONG
>
	CHFDB%			;[335] UPDATE FDB
	HRLI T1,.FBSIZ(CF%NUD)	;NOW UPDATE NO. OF BYTES IN FILE
	SETO T2,		;CHANGE ALL BITS
	MOVE T3,FILEOF(F)	;NO. OF BYTES WE WROTE
	CHFDB%			;[335]

CLSFL2:	MOVE T1,FILFLG(F)	;GET FILE FLAGS
	TXNE T1,FI.DSK		;DISK FILE?
	TXNE T1,FI.OUT		; AND INPUT?
	JRST CLSFL3		;NO TO EITHER, DON'T UNMAP BUFFER
	PUSH P,T4		;UNMAPF CALLS CLRBIT, WHICH TRASHES T4
	CALL UNMAPF		;UNMAP FILE PAGES
	POP P,T4

CLSFL3:	JUMPN T4,CPOPJ		;[420] TEMP FILE CLOSE?
	HLRZ T1,FILPGN(F)	;[420] GET JFN OF FILE
	CLOSF%			;[335] NO, CLOSE IT
	  ERJMP E$$CFF		;CLOSE FAILURE FOR FILE
	RET
SUBTTL	I/O ROUTINES -- File Utility Routines -- Delete a Temp File


;DELFIL - DELETE FILE POINTED TO BY F

DELFIL:	CALL UNMAPF		;FIRST UNMAP ANY BUFFER PAGES
	HLRZ T1,FILPGN(F)	;GET JFN
	TXO T1,CZ%ABT		;ABORT (I.E., EXPUNGE) FILE
	CLOSF%			;[335] CLOSE AND DELETE IT
	  ERJMP E$$CFF		;[357] CAN'T CLOSE FILE
	RET
SUBTTL	I/O ROUTINES -- File Utility Routines -- Unmap Buffer Pages For a File

UNMAPF:	SETO T1,		;PREPARE TO UNMAP BUFFER
	MOVSI T2,.FHSLF		;THIS FORK
	HRR T2,FILBUF(F)	;STARTING FROM THIS PAGE
	HLRZ T3,FILBUF(F)	;UNMAP THIS MANY PAGES
	IORX T3,PM%CNT		; ..
	PMAP%			;[335]
	PUSH P,P1		;GET A REG
	HLRZ P1,FILBUF(F)	;GET PAGES PER BUFFER
	MOVN P1,P1		;NEGATE
	HRLZ P1,P1		;CONSTRUCT AOBJN PTR
	HRR P1,FILBUF(F)	;PUT PG NO IN RH

UNMAP1:	HRRZ T1,P1		;GET BIT (PAGE) NUMBER
	MOVEI T2,PGTAB		;CLEAR BIT IN PGTAB TO INDICATE
	CALL CLRBIT		; THAT THIS PAGE IS NOW UNMAPPED
	AOBJN P1,UNMAP1		;LOOP FOR ALL PAGES
	POP P,P1		;RESTORE P1
	RET
;ERSET$ -  CLEAN UP THE MESS AFTER A FATAL ERROR; CALLED BY DIE
;RESET$ - DEALLOCATE CORE AND UNMAP BUFFER PAGES; CALLED BY ENDS.

RESET$:	TDZA P3,P3		;[335] INDICATE CALLED BY ENDS.
ERSET$:	SETO P3,		;[335] INDICATE CALLED BY DIE
	MOVSI P1,-<<1000/^D36>+1> ;[C13] SET UP AOBJN PTR TO PGTAB
	SETZ P2,		;INIT BIT NUMBER

RESET1:	HRRZ T1,P1		;[C20] GET NEXT WORD OF PGTAB
	MOVE T1,PGTAB(T1)	;[C20]   ..
	JFFO T1,RESET2		;ANY ONE BITS?
	ADDI P2,^D36		;NO, STEP BIT NUMBER
	AOBJN P1,RESET1		;KEEP LOOKING
	JRST RESETC		;NO MORE BITS, CLOSE FILES

RESET2:	ADD T2,P2		;COMPUTE BIT WITHIN TABLE
	MOVE T1,T2		;SET UP FOR CALL TO CLRBIT
	PUSH P,T2		;SAVE BIT NUMBER OVER CLRBIT
	MOVEI T2,PGTAB		; ..
	CALL CLRBIT		;CLEAR THIS BIT
	POP P,T2		;RECOVER BIT (PAGE) NUMBER
	HRLI T2,.FHSLF		; FROM THIS FORK
	SETO T1,		;INDICATE UNMAPPING
	SETZ T3,		;ONE PAGE ONLY
	PMAP%			;[335] UNMAP IT
	JRST RESET1

RESETC:	CALL RELSPC		;[C13] RELEASE ALL RETAINED SPACE
				;WERE WE CALLED BY DIE?
	JUMPE P3,CPOPJ		;NO, FILES SHOULD BE CLOSED ALREADY
	MOVEI F,FCBORG		;YES, CLOSE AND ABORT MASTER FILE
	HLRZ T1,FILPGN(F)	;GET JFN
	IORX T1,CZ%ABT		;ABORT IT
	CLOSF%			;[335]
	  ERJMP .+1
	MOVE P3,ACTTMP		;[C20] GET NO. OF ACTIVE TMP FILES
	MOVEI F,TMPFCB		;[C20] SETUP PTR TO TMP FCB BLOCKS

RESTC2:	HLRZ T1,FILPGN(F)	;GET JFN
	IORX T1,CZ%ABT		;ABORT FILE
	CLOSF%			;[335]
	  ERJMP .+1
	ADDI F,FCBLEN		;[C20] STEP TO NEXT FCB
	SOJG P3,RESTC2		; ..
	RET
;MRKPGS - MARK PAGES OF THIS FILE AS POSSIBLY BEING MAPPED
;	  SO ON A FATAL ERROR THEY WILL BE UNMAPPED BY RESET%

MRKPGS:	PUSH P,P1		;GET A REG
	HLRZ P1,FILBUF(F)	;GET PAGES PER BUFFER
	MOVN P1,P1		;NEGATE
	HRLZ P1,P1		;MAKE AOBJN PTR
	HRR P1,FILBUF(F)	;PUT PAGE NO. IN RH

MRKPG1:	HRRZ T1,P1		;GET PAGE (BIT) NUMBER
	MOVEI T2,PGTAB		;SET BIT IN PGTAB
	CALL SETBIT		; ..
	AOBJN P1,MRKPG1		;DO IT FOR ALL PAGES
	POP P,P1		;RESTORE P1
	RET

;SETBIT - SET A BIT IN A TABLE
;CALL WITH:	T1/ BIT NO.
;		T2/ TABLE ADDR.

SETBIT:	CALL BITSET		;COMPUTE WORD AND SET UP BIT IN T3
	TDNE T3,0(T1)		;[OK] IS BIT ALREADY SET?
	JRST E$$FPM		;YES
	IORM T3,0(T1)		;[OK] NO, SET IT AND RETURN
	RET

;CLRBIT - ANALOGOUS TO SETBIT

CLRBIT:	CALL BITSET		;COMPUTE WORD ADDR AND SET UP BIT
	TDNN T3,0(T1)		;[OK] ALREADY CLEAR?
	JRST E$$FPU		;YES
	ANDCAM T3,0(T1)		;[OK] NO, CLEAR IT
	RET

;BITSET - COMPUTE WORD IN T1 AND PLACE BIT IN T3

BITSET:	MOVE T4,T2		;COPY TABLE ADDR
	IDIVI T1,^D36		;COMPUTE WORD AND BIT WITHIN WORD
	MOVX T3,<1B0>		;GET ZEROTH BIT
	MOVN T2,T2		;NEGATE BIT NO. FOR LSH
	LSH T3,0(T2)		;[OK] PUT BIT IN RIGHT PLACE
	ADD T1,T4		;COMPUTE WORD WITHIN TABLE
	RET
SUBTTL	I/O ROUTINES -- File Utility Routines -- Initialize Output Temporary File


ENTFIL:	AOS T1,NUMTMP		;COUNT RUNS
	HRLM T1,FILRUN(F)	;FOR COMPAR = TEST
	AOS T1,NUMENT		;COUNT TEMP FILES
	CAMLE T1,MAXTMP		;WRAPPED AROUND YET?
	CALLRET APPFIL		;YES, APPEND TO THIS FILE
	MOVX T1,FI.DSK!FI.OUT!FI.TMP ;SET APPROPRIATE FLAGS
	IORM T1,FILFLG(F)	; ..
	CALL GENNAM		;GENERATE NEW TEMP FILE NAME
	HRRZ T2,TCBIDX		;GET INDEX TO TEMP FILE
	IDIV T2,STRNUM		;ROUND ROBIN DIR NUMBERS
	MOVE T2,STRNAM(T3)	;[OK] GET DIRECTORY NUMBER FOR TEMP FILE
	CALL XGTJFN		;MAKE ASCIZ FILESPEC AND DO GTJFN
	MOVX T2,OF%WR		;OPEN FOR WRITE
	OPENF%			;[335]
	  ERJMP E$$OPN
	HRLZM T1,FILPGN(F)	;SAVE JFN AND ZERO PAGE COUNTER
	SKIPGE BUFALC		;HAVE BUFFERS BEEN ALLOCATED YET?
	JRST	[HRRZ T1,FILBUF(F) ;YES, GET ADDRESS OF BUFFER
		 JRST ENTFL1]	;GO SET UP BYTE POINTER
	MOVE T1,TBUFNO		;GET PAGES PER TEMP FILE BUFFER
	HRLZM T1,FILBUF(F)	;SAVE IN FCB
	LSH T1,POW2(PGSIZ)	;COMPUTE WORDS IN BUFFER
	MOVEM T1,FILBPB(F)	;SAVE BYTES (=WORDS) PER BUFFER
	CALL ALCBPG		;ALLOCATE BUFFER ON PG BOUNDARY
	LSH T1,-<POW2(PGSIZ)>	;CONVERT ADDR OF BUFFER TO PAGE NO.
	HRRM T1,FILBUF(F)	;SAVE IN FCB

ENTFL1:	LSH T1,POW2(PGSIZ)	;CONVERT BACK TO ADDRESS
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;SAVE IT
	MOVE T1,FILBPB(F)	;GET BYTES PER BUFFER
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER WAITING TO BE FILLED
	SETZM FILEOF(F)		;INDICATE NOTHING'S BEEN WRITTEN YET
	RET			;RETURN

;SETTMP - PLUG OUR CONNECTED DIRECTORY NUMBER INTO STRNAM
;         AS A DEFAULT IF USER DIDN'T TYPE ANY /TEMP SWITCHES

IFE FTCOBOL,<
SETTMP:	SKIPE STRNUM		;DID USER TYPE ANY /TEMP SWITCHES?
	RET			;YES, WE'RE ALL SET
	SETZ T1,		;GET DIRECTORY ASSOCIATED WITH DSK:
	HRROI T2,[ASCIZ /DSK:/]	; TO BE CONSISTENT WITH MOST EXEC COMMANDS
	RCDIR%			;[335]
	  ERJMP NODISK		;MIGHT BE DEFINED TO BE A NON-DISK DEV
	MOVEM T3,STRNAM		;SAVE IN TABLE
	AOS STRNUM		;INDICATE ONLY ONE TEMP FILE AREA
	RET			;AND RETURN

NODISK:	GJINF%			;[335] NO, GET CONNECTED DIRNUM IN T2
	MOVEM T2,STRNAM		;SAVE IN TABLE
	AOS STRNUM		;INDICATE ONLY ONE TEMP FILE AREA
	RET			;AND RETURN
>
SUBTTL	I/O ROUTINES -- File Utility Routines -- Append to Temporary File

APPFIL:	HRRZ T1,TCBIDX		;GET TMP FILE INDEX
	IMULI T1,DFBLEN		;FIND OFFSET OF DFB BLOCK FOR FILE
	ADDI T1,DFBORG		;ADD BASE ADDRESS OF DFB BLOCKS
	HRLZ T1,T1		;CONSTRUCT BLT POINTER
	HRR T1,F		;COPY TO CURRENT FCB
	HRRZI T2,DFBLEN(F)	;[C20] BLT LIMIT
	HLRZ T3,FILRUN(F)	;PRESERVE RUN NUMBER ENTFIL COMPUTED
	BLT T1,-1(T2)		;[OK] RESTORE INFORMATION TO FCB
	HRLM T3,FILRUN(F)	;RESTORE RUN NUMBER ENTFIL COMPUTED
	MOVE T3,FILEOF(F)	;GET FILE SIZE
	IDIVI T3,PGSIZ		;GET PAGE NUMBER OF LAST PAGE
	HRRM T3,FILPGN(F)	;[335] REMEMBER FOR PMAP%
	HRRZ T1,FILBUF(F)	;GET 1ST PG OF BUFFER
	LSH T1,POW2(PGSIZ)	;COMPUTE ADDRESS
	ADD T1,T4		;[C20] COMPUTE 1ST UNUSED WORD
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;SAVE IT
	JUMPE T4,APPFL1		;IF FILE ENDS ON PG. BOUNDARY, NO
				; NEED TO READ PARTIAL PAGE IN
	MOVE T1,FILPGN(F)	;[335] GET PMAP% SOURCE IDENT
	MOVSI T2,.FHSLF		;CONSTRUCT IDENT TO BUFFER
	HRR T2,FILBUF(F)	; ..
	MOVX T3,PM%RD!PM%CPY!PM%PLD ;COPY-ON-WRITE ACCESS
	PMAP%			;[335] GET THE PARTIALLY WRITTEN PAGE
	MOVE T1,FILEOF(F)	;GET WORDS WRITTEN SO FAR
	SUB T1,T4		;SUBTRACT PARTIAL PAGE (BECAUSE
	MOVEM T1,FILEOF(F)	; PUTBUF WILL COUNT THEM AGAIN)

APPFL1:	MOVE T2,FILBPB(F)	;GET BYTES PER BUFFER
	SUB T2,T4		;COMPUTE BYTES LEFT IN BUFFER
	MOVEM T2,FILCNT(F)	;REMEMBER
	HRRZ T1,FILPTR(F)	;GET ADDR OF FIRST WORD TO BE USED
	HLRO T2,FILRUN(F)	;FAKE SIXBIT WORD COUNT FOR EOF MARKER
	MOVEM T2,0(T1)		;[OK] SET EOF MARKER
	AOS FILPTR(F)		;BUMP BYTE PTR PAST EOF MARKER
	SOSG FILCNT(F)		;ACCOUNT FOR THE WORD WE WROTE
	JSP T4,PUTBUF		;COUNT EXPIRED, GET FRESH BUFFER
	RET
;LKPFIL - SET UP TO START READING FROM BEGINNING OF TEMP FILE

LKPFIL:	MOVX T1,FI.DSK!FI.TMP	;REMEMBER THIS IS A TEMP FILE
	IORM T1,FILFLG(F)	; ..
	MOVX T1,FI.OUT		;REMEMBER WE'RE NO LONGER
	ANDCAM T1,FILFLG(F)	; DOING OUTPUT TO THIS FILE
	SKIPGE BUFALC		;HAVE BUFFERS BEEN ALLOCATED YET?
	JRST LKPFL1		;YES, DON'T DO IT AGAIN
	MOVE T1,TBUFNO		;GET PAGES PER TEMP FILE BUFFER
	HRLZM T1,FILBUF(F)	;SAVE SIZE OF BUFFER IN FCB
	LSH T1,POW2(PGSIZ)	;CONVERT PAGES TO WORDS
	MOVEM T1,FILBPB(F)	;SAVE WORDS (=BYTES) PER BUFFER
	CALL ALCBPG		;ALLOCATE BUFFER ON PAGE BOUNDARY
	LSH T1,-<POW2(PGSIZ)>	;CONVERT ADDRESS TO PAGE
	HRRM T1,FILBUF(F)	;SAVE ADDR OF BUFFER IN FCB

LKPFL1:	CALL MRKPGS		;MARK PAGES AS POSSIBLY MAPPED
	SETZM FILCNT(F)		;SO FIRST CALL TO GETREC CAUSES PMAP
	HLLZS FILPGN(F)		;RESET PG COUNTER FOR PMAP
	RET
;XGTJFN - CONVERT DIRNUM AND SIXBIT FILE NAME TO ASCIZ FILESPEC
;CALL WITH:
;	T1/ SIXBIT FILENAME
;	T2/ DIRECTORY NUMBER
;RETURNS:
;	+1: WITH JFN IN T1

XGTJFN:	PUSH P,P1		;GET SOME REGS
	PUSH P,P2		; ..
	DMOVE P1,T1
	HRROI T1,AZTEMP		;[405] CONVERT ADDRESS TO STRING PTR
	PUSH P,T1		;SAVE PTR TO START OF STRING
	DIRST%			;[335] MAKE A STRING
	  ERJMP E$$IDN		;INVALID DIRECTORY NUMBER

;NOW CONVERT THE SIXBIT STRING IN P1 TO ASCII, USING BIS.  NOTE
; THAT THIS CODE ASSUMES THAT P1 ALWAYS CONTAINS 6 CHARACTERS.

	MOVE P2,T1		;COPY CURRENT BYTE PTR TO FILESPEC
	MOVEI T0,6		;SOURCE STRING LENGTH
	MOVE T1,[POINT 6,P1]	;BYTE PTR TO 6-BIT SOURCE STRING
	MOVEI T3,6		;DESTINATION STRING LENGTH
	MOVE T4,P2		;COPY BYTE PTR TO FILESPEC
	EXTEND T0,[MOVSO 40]	;ADD 40 TO EACH BYTE TO PRODUCE ASCII
	JRST E$$BIS		;ERROR

;ADD TYPE AND ;T ATTRIBUTE TO FILESPEC

	MOVEI T0,7		;MOVE 7 CHARACTERS (INCLUDING THE NULL)
	MOVE T1,[POINT 7,[ASCIZ /.TMP;T/]]
	MOVEI T3,7		;7 CHARS IN DEST
	EXTEND T0,[MOVSLJ]	;T4 ALREADY HAS GOOD DEST PTR
	JRST E$$BIS		;ERROR
	MOVX T1,GJ%SHT		;SHORT FORM
	POP P,T2		;GET BYTE PTR TO FILESPEC
	GTJFN%			;[335] GET THE JFN
	  ERJMP E$$GFT		;FAILED
	POP P,P2		;RESTORE P REGS
	POP P,P1		; ..
	RET			;ALL SET, RETURN WITH T1=JFN
SUBTTL	TRY TO RENAME SINGLE TEMP FILE TO OUTPUT FILE

IFE FTCOBOL,<

;TSTDEV - CHECK TO SEE IF SINGLE TEMP FILE IS ON SAME STRUCTURE
;	 AS OUTPUT FILE.  IF SO, WE CAN RENAME INSTEAD OF COPY.

TSTDEV:	HRRZ T1,IOMODE		;GET I/O MODE INDEX
	CAXE T1,MODSIXBIT	;ONLY SIXBIT LOOKS LIKE TEMP FILE
	RET
	MOVE P1,F.OUZR		;[436] LOAD ADDRESS OF OUTPUT X.BLOCK
	MOVS T1,@EXTORG		;[OK] [C13] GET EXTRACT CODE
	CAIN T1,(JRST (P4))	;JUST A DUMMY?
	SKIPE X.BLKF(P1)	;[OK] CAN'T DO IF OUTPUT BLOCKED
	RET			;DO THE COPY
	LDB T1,[POINT 9,X.DVCH(P1),17] ;[OK] GET DEVICE TYPE OF OUTPUT FILE
	CAXE T1,.DVDSK		;DISK?
	RET			;NO, MUST COPY TO OUTPUT FILE
	PUSH P,P2		;GET A REGISTER
	MOVEI T1,2		;ALLOCATE ENOUGH WORDS TO BUILD
	CALL GETSPC		; DEVICE NAME STRING
	  JRST E$$NEC
	SETZM 0(T1)		;[OK] ZERO STRING SPACE
	SETZM 1(T1)		;[OK]  ..
	MOVE P2,T1		;SAVE STRING ADDRESS
	HRROS T1		;CONSTRUCT STRING PTR TO IT
	MOVEI T2,TMPFCB		;ONLY ONE TMP FILE, SO FCB IS AT TMPFCB
	HLRZ T2,FILPGN(T2)	;[OK] PICK UP JFN
	MOVX T3,<1B2>		;OUTPUT ONLY DEVICE FIELD
	JFNS%			;[335] GET IT
	  ERJMP E$$JFT		;FAILED
	MOVEI T1,2		;ALLOCATE SPACE FOR DEVICE NAME STRING
	CALL GETSPC		; OF OUTPUT FILE
	  JRST E$$NEC
	SETZM 0(T1)		;[OK] ZERO STRING SPACE
	SETZM 1(T1)		;[OK]  ..
	MOVE T4,T1		;SAVE ADDRESS
	HRROS T1		;CONSTRUCT STRING PTR
	HRRZ T2,X.JFN(P1)	;[OK] GET JFN OF OUTPUT FILE
	MOVX T3,<1B2>		;GET DEVICE FIELD ONLY
	JFNS%			;[335]   ..
	  ERJMP E$$JFO		;FAILED

; ..
; ..

;T4 NOW POINTS TO DEVICE STRING FOR OUTPUT FILE, P2 POINTS TO
; DEVICE STRING FOR TEMP FILE.  COMPARE USING BIS AND SET UP
; TO DO RENAME IF THEY ARE EQUAL.

	MOVEI T0,6		;SOURCE STRING LENGTH
	HRRZ T1,T4		;GET ADDR OF OUTPUT FILE DEVICE STRING
	HRLI T1,(POINT 7,)	;CONSTRUCT BYTE POINTER
	SETZ T2,		;NOT USED BY BIS
	MOVEI T3,6		;DEST STRING LENGTH
	HRRZ T4,P2		;GET ADDR OF TMP FILE DEVICE STRING
	POP P,P2		;RESTORE P2 NOW
	HRLI T4,(POINT 7,)	;CONSTRUCT BYTE POINTER
	EXTEND	T0,[CMPSE	;COMPARE STRINGS, SKIP IF EQUAL
		    0		; ZERO FILL IF EITHER
		    0]		; STRING RUNS OUT
	JRST	[MOVEI T1,4	;FREE STRING SPACE WE GRABBED
		 CALLRET FRESPC];NON-SKIP RETURN, MUST DO COPY
	MOVEI T1,4		;FREE STRING SPACE WE GRABBED
	CALL FRESPC		; ..
	MOVEI T1,RSTF		;ADDR OF ROUTINE TO RENAME OUTPUT FILE
	RETSKP			;SKIP RETURN TO INDICATE RENAME POSSIBLE
;RSTF - RENAME SOLITARY TEMP FILE TO BE SORT OUTPUT MASTER

RSTF:	MOVEI T3,TMPFCB		;ONLY ONE TEMP FILE, FCB LIVES AT TMPFCB
	HLRZ T1,FILPGN(T3)	;[OK] GET SOURCE JFN (OF TEMP FILE)
	IORX T1,CO%NRJ		;DON'T RELEASE JFN
	CLOSF%			;[335] CLOSE IT
	  ERJMP E$$CFR		;SOMETHING WRONG
	MOVE T4,F.OUZR		;GET ADDR OF X. BLOCK FOR OUTPUT FILE
	HRRZ T1,X.JFN(T4)	;[OK] GET JFN OF OUTPUT FILE
	IORX T1,CO%NRJ		;DON'T RELEASE JFN
	CLOSF%			;[335] CLOSE FILE
	  ERJMP E$$CFR		;OOPS
	HLRZ T1,FILPGN(T3)	;[OK] GET TEMP FILE JFN AGAIN
	HRRZ T2,X.JFN(T4)	;[OK] GET OUTPUT FILE JFN
	RNAMF%			;[335] DO THE RENAME
	  ERJMP E$$RFF		;FAILED
	MOVE T1,FILEOF(F)	;[437] TMPFCB #BYTES
	MOVEI F,FCBORG		;[437] CHANGE F TO WHAT IS EXPECTED
	MOVEM T1,FILEOF(F)	;[437] AND GIVE FCBORG CORRECT INFO
	MOVE T1,INPREC		;FAKE COPY OF FILE
	MOVEM T1,FILSIZ(F)	;[437] GIVE THIS INFO TOO FOR EOFOUT
	MOVE T1,T2(T4)		;[OK] [437] SET UP OPENF
	MOVE T2,[44B5+OF%RD+OF%WR];[437] BECAUSE CLSFIL
	OPENF%			;[437] NEEDS IT OPEN FOR CHFDB
	  ERJMP E$$OPN		;[437] FAILURE!
	CALLRET EOFOUT		;TOP LEVEL RETURN

E$$CFR:	$ERROR (?,CFR,<CLOSE failure at RSTF>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$RFF:	$ERROR (?,RFF,<RENAME failed at RSTF>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO
>;END IFE FTCOBOL
SUBTTL	GETREC -- GETBUF - Input 1 Physical Buffer -- Set Up

;CALL WITH:	JSP T4,GETBUF
;RETURNS:	+1/ END OF FILE
;		+2/ SUCCESS, T1 CONTAINS NO. OF BYTES READ

GETBUF:	PUSH P,T4		;PUSH RETURN ADDRESS
IFN FTCOBOL,<
	JRST	GETDSK		;[C03] ONLY GETDSK IN COBOL SORT
>
IFE FTCOBOL,<
	MOVE T1,FILFLG(F)	;[435] GET FILE FLAGS
	TXNE T1,FI.DSK		;[435] FILE ON DISK?
	JRST GETDSK		;YES
	TXNE T1,FI.MTA		;[435] MAGTAPE?
	JRST GETMTA		;YES
	MOVE T1,FILXBK(F)	;GET ADDR OF X. BLOCK FOR FILE
	LDB T1,[POINT 9,X.DVCH(T1),17] ;[OK] GET DEVICE TYPE
	CAXN T1,.DVTTY		;IS THIS A TTY?
	JRST GETTTY		;YES, HANDLE UNIQUELY
	HLRZ T1,FILPGN(F)	;GET JFN
	HRRO T2,FILBUF(F)	;STRING PTR TO BUFFER
	MOVE T3,FILBPB(F)	;LENGTH OF STRING
	SETZ T4,		;OR STOP ON NULL
	SIN%			;[335] GET IT
	  ERJMP [MOVX T1,.FHSLF	;[N15] [C02] GET MOST RECENT ERROR NUMBER
		GETER%		;[335]   ..
		HRRZ T1,T2	; ..
		CAXE T1,IOX4	;END OF FILE?
		JRST E$$BER	;NO, HARD INPUT ERROR
		CAMN T3,FILBPB(F) ;WAS BYTE COUNT ALTERED (I.E., WAS ANYTHING READ?)
		JRST GETEOF	;[C03] NO, GIVE EOF RETURN
		JRST .+1]	;[N15] [C03] YES, CONTINUE
	MOVE T1,FILBPB(F)	;GET BYTES PER BUFFER
	SUB T1,T3		;DEDUCT NO. OF BYTES NOT READ
	MOVEM T1,FILCNT(F)	;EQUALS NO. OF BYTES IN THIS BUFFERLOAD
	HRRZ T1,FILBUF(F)	;CONSTRUCT BYTE PTR TO BUFFER
	ADD T1,CBPTR		; ..
	MOVEM T1,FILPTR(F)	; ..
	MOVE T1,FILCNT(F)	;[C03] RETURN WITH BYTE COUNT IN T1
	RETSKP			;GIVE GOOD RETURN
SUBTTL	GETREC -- GETBUF - Input 1 Physical Buffer -- Terminal


GETTTY:	HRRZ T4,FILBUF(F)	;CONSTRUCT BYTE POINTER TO BUFFER
	ADD T4,CBPTR		; ..
	MOVEM T4,FILPTR(F)	;INIT BUFFER POINTER IN FCB
	SETZM FILCNT(F)		;INIT BYTE COUNTER
	MOVE T3,FILBPB(F)	;GET BYTES PER BUFFER FOR COUNTDOWN

GETTT1:	HLRZ T1,FILPGN(F)	;GET JFN
	BIN%			;[335] GET A BYTE
	CAXN T2,.CHCNZ		;CTRL-Z?
	JRST [	SKIPN FILCNT(F) ;[C03] WAS ANYTHING READ THIS TIME?
		JRST GETEOF	;[C03] NO, GIVE EOF RETURN
		BKJFN%		;[C03] YES, BACKUP OVER CTRL-Z
		  ERJMP E$$BER	;[C03] ERROR
		MOVE T1,FILCNT	;[C03] RETURN WITH BYTE COUNT IN T1
		RETSKP]		;AND GIVE GOOD RETURN
	AOS T1,FILCNT(F)	;COUNT DATA BYTES
	IDPB T2,T4		;PLUNK IT INTO THE BUFFER
	SOJG T3,GETTT1		;GET THE NEXT BYTE, UNLESS BUFFER FULL
	RETSKP			;RETURN WITH T1=NO. OF BYTES READ

>;END IFE FTCOBOL

GETEOF:	MOVE EF,PHYEOF		;[C03] GIVE EOF RETURN
	RET			;[C03]  ..
SUBTTL	GETREC -- GETBUF - Input 1 Physical Buffer -- Disk


GETDSK:	SKIPG T1,FILEOF(F)	;[C17] ANY BYTES LEFT?
	JRST GETEOF		;[C03] NO, GIVE EOF RETURN
	MOVE T2,FILBPB(F)	;GET NO. OF BYTES PER BUFFER
	CAMLE T1,T2		;MORE THAN 1 BUFFER'S WORTH LEFT?
	MOVE T1,T2		;YES, JUST READ ONE BUFFER'S WORTH
	MOVEM T1,FILCNT(F)	;SAVE NO. OF BYTES IN THIS BUFFERLOAD
	MOVE T3,FILEOF(F)	;GET NO. OF BYTES LEFT IN FILE
	SUB T3,T1		;SUBTRACT NO. WE'RE READING
	MOVEM T3,FILEOF(F)	;SAVE NEW NO. OF BYTES LEFT IN FILE
IFE FTCOBOL,<
	MOVN T2,T1		;[C17] DECRIMENT BLOCK BYTE COUNT
	ADDM T2,FILKCT(F)	;[C17]   ..
	MOVX T2,FI.TMP		;IS THIS A TEMP FILE?
	TDNE T2,FILFLG(F)	; IF SO, WORDS=BYTES
	JRST GETDS1		;YES, SKIP THE DIVIDE
	IDIV T1,IOBPW2		;[C03] COMPUTE NO. OF WORDS WE'RE READING
	SKIPE T2		;[C03] COUNT PARTIAL WORDS
	ADDI T1,1		;[C03]  ..
GETDS1:
>
	IDIVI T1,PGSIZ		;COMPUTE NO. OF PAGES WE'RE READING
	SKIPE T2		;COUNT PARTIAL PAGES
	ADDI T1,1		; ..
	MOVX T3,PM%CNT!PM%PLD	;[335] CONSTRUCT PAGE COUNT FOR PMAP%
	ADD T3,T1		; ..
	MOVE T1,FILPGN(F)	;GET JFN,,PAGE NO.
	HRLI T2,.FHSLF		;MAP TO THIS FORK
	HRR T2,FILBUF(F)	;PAGE INTO WHICH TO START MAPPING
	PMAP%			;[335] MAP THE FILE
IFE FTCOBOL,<			;[402] TEMP FILES DON'T HAVE HOLES
	  ERCAL FIXMAP		;[402] SEE WHICH SECTION PMAP% FAILED
  IF NOT A TEMP FILE
	MOVX T4,FI.TMP		;[402] LOAD TEMP FILE BIT
	TDNN T4,FILFLG(F)	;[402] TEMP FILE?
  THEN BEWARE OF HOLES IN INPUT FILES
	CALL FIXPGS		;[402] NO--MAY HAVE HOLES TO FIX
  FI;
> ;[402] END OF IFE FTCOBOL
	HLRZ T1,FILBUF(F)	;GET PAGES PER BUFFER
	HRRZ T2,FILPGN(F)	;GET CURRENT PAGE NUMBER
	ADD T1,T2		;COMPUTE NEXT PAGE NUMBER
	HRRM T1,FILPGN(F)	;SAVE IT

	HRRZ T1,FILBUF(F)	;GET 1ST PAGE OF BUFFER
	LSH T1,POW2(PGSIZ)	;TURN INTO ADDRESS
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;INITIALIZE BYTE PTR TO BUFFER
	MOVE T1,FILCNT(F)	;RETURN WITH T1=NO. OF BYTES READ
	RETSKP			;RETURN
IFE FTCOBOL,<

BEGIN
  PROCEDURE	(PUSHJ	P,FIXPGS)	;[402] MAKE HOLES INTO ZERO PAGES

;CERTAIN FILE FORMATS SUPPORTED BY SORT ARE ALLOWED TO CONTAIN HOLES, NAMELY,
;PAGES THAT DO NOT EXIST IN THE FILE. SINCE SORT IS ESSENTIALLY A SEQUENTIAL
;PROCESSOR, IT SHOULD TREAT THESE HOLES AS PAGES FULL OF ZEROS. THIS IS
;CONSISTENT WITH THE MONITOR'S HANDLING OF SIN% AND PRE-JSYSIZED VERSIONS OF
;SORT.
;
;SIMPLE HOLES ARE THOSE PAGES THAT DO NOT EXIST IN A FILE SECTION THAT DOES
;EXIST. IN THIS CASE, THE PMAP% JSYS THAT MAPS THE PAGES (ASSUMED FOR READ-ONLY
;FROM A FILE OPEN FOR READ-ONLY) WILL SUCCEED, BUT MEMORY REFERENCES INTO THE
;MAPPED PAGES WILL FAIL. THIS ROUTINE TESTS FOR THESE HOLES BY REFERENCING THE
;FIRST WORD IN EACH PAGE JUST MAPPED. ANY FAILURES (DETECTED BY AN ERJMP) THEN
;CAUSE THE OFFENDING PAGE TO BE UNMAPPED. THIS GIVES A ZERO-FILLED PAGE FOR SORT
;TO READ.
;
;CALL:
;	T1-T3/	ARGUMENTS GIVEN TO PMAP%
;	F/	POINTER TO THIS FILE'S FILE BLOCK
;RETURNS WITH T1-T4 DESTROYED, AND ALL HOLE PAGES UNMAPPED.

	PUSH	P,P1			;[C20] SAVE P1, NEED AN EXTRA AC
	HRRZ	T4,FILBUF(F)		;[C20] [402] BUILD POINTER FOR PAGES
	LSH	T4,POW2(PGSIZ)		;[402]   POINTING TO ADDRESS OF PAGE
	HRRZ	P1,T3			;[C20] [402] GET PAGE COUNT
	SETO	T1,			;[402] MAKE PMAP% ARGS SPECIFY UNMAPPING
	MOVX	T3,<PM%CNT+1>		;[402]   IN CASE WE HAVE TO
  WHILE THERE ARE PAGES TO CHECK
	BEGIN
		SKIP	(T4)		;[OK] [402] TOUCH THE PAGE
		  ERJMP	[PMAP%			;[402] FAILED--UNMAP IT
			 JRST .+1]		;[402]   AND CONTINUE LOOKING
		ADDI	T2,1		;[402] ADVANCE PMAP% ARGS
		ADDI	T4,PGSIZ	;[C20] [402] ADVANCE POINTER
		SOJG	P1,$B		;[C20] [402] LOOP UNTIL NO MORE PAGES
	END;
	POP	P,P1			;[C20] RESTORE P1
	RETURN				;[402] DONE
END;
;STILL IN IFE FTCOBOL

BEGIN
  PROCEDURE	(PUSHJ	P,FIXMAP)	;[402] MAKE ZERO PAGES WHEN PMAP% FAILS

;IF A PMAP% JSYS FAILS FOR AN INPUT FILE, THEN SOME OF THE REQUESTED PAGES WERE
;IN A FILE SECTION THAT HAS NO INDEX BLOCK. IN THIS CASE, THE MONITOR IS
;UNWILLING TO GIVE US ANYTHING, EVEN INFORMATION ON HOW MUCH OF THE REQUEST THE
;PMAP% JSYS WAS ABLE TO MAP BEFORE QUITTING. THEREFORE, WE MUST ANALYZE THE
;STATE OF THINGS OURSELVES TO PERFORM RECOVERY.
;
;THE METHOD USED IS IN TWO PARTS. IF THERE IS NO FILE SECTION BOUNDARY
;REPRESENTED IN THE PMAP% REQUEST, THEN ALL OF THE PAGES WERE FROM THE SAME NON-
;EXISTENT SECTION. IN THIS CASE, WE SIMPLY UNMAP THEM ALL. IF THERE IS A FILE
;SECTION BOUNDARY WITHIN THE PMAP% REQUEST, THEN EITHER PORTION OF THE REQUEST
;MAY HAVE CAUSED THE PMAP% TO FAIL. IN THIS CASE, WE MUST RETRY BOTH HALVES OF
;THE PMAP% REQUEST AGAIN (THOSE PAGES BEFORE THE SECTION BOUNDARY AND THOSE
;AFTER IT), BUT SEPARATELY. IF EITHER (OR BOTH) FAILS, THEN WE UNMAP THOSE
;PAGES. WE MAKE THE LIKELY ASSUMPTION THAT THERE IS AT MOST 1 SECTION BOUNDARY
;WITHIN THE REQUESTED GROUP OF PAGES.
;
;CALL:
;	T1-T3/	ARGUMENTS TO PMAP% THAT FAILED
;RETURNS WITH PAGES FROM NON-EXISTENT SECTIONS UNMAPPED, WITHOUT DESTROYING
;T1-T3.

	PUSH	P,P1			;[C20] SAVE P1
	PUSH	P,T1			;[402] SAVE T1-T3
	PUSH	P,T2			;[402]   ..
	PUSH	P,T3			;[402]   ..
  IF FAILING PMAP% REQUEST WAS CONTAINED IN 1 SECTION
	HRRZ	T4,T1			;[402] COMPUTE LAST PAGE OF
	IORX	T4,PGSIZ-1		;[402]   CURRENT FILE SECTION
	HRRZ	P1,T1			;[C20] COMPUTE # AGES TO THERE
	SUBI	T4,-1(P1)		;[C20] [402]   ..
	HRRZ	P1,T3			;[C20] MORE THAN WE ASKED FOR?
	CAMGE	T4,P1			;[C20] [402]   ..
	JRST	$T			;[402] NO-- REQUEST CROSSED SECTION BDY
  THEN ALL IN SAME NON-EXISTENT SECTION SO JUST UNMAP ALL PAGES
	PUSHJ	P,UNMAP			;[402] UNMAP MAPPED PAGES
	JRST	$F			;[402]
  ELSE EITHER OR BOTH SECTIONS MAY NOT EXIST SO TRY EACH ONE
	HRR	T3,T4			;[402] RETRY FIRST SECTION OF PMAP%
	PMAP%				;[402]   ..
	  ERCAL	UNMAP			;[402] DOESN'T EXIST--UNMAP IT
	ADD	T1,T4			;[402] TRY SECOND SECTION OF PMAP%
	ADD	T2,T4			;[402]   ..
	HRR	T3,0(P)			;[402]   ..
	SUB	T3,T4			;[402]   ..
	PMAP%				;[402]   ..
	  ERCAL	UNMAP			;[402] DOESN'T EXIST--UNMAP IT
  FI;
	POP	P,T3			;[402] RESTORE ACS FOR CALLER
	POP	P,T2			;[402]   ..
	POP	P,T1			;[402]   ..
	POP	P,P1			;[C20]   ..
	RETURN				;[402] DONE
END;
;STILL IN IFE FTCOBOL

BEGIN
  PROCEDURE	(PUSHJ	P,UNMAP)	;[402] UNMAP MAPPED PAGES

;UNMAP UNMAPPS THE PAGES SPECIFIED BY THE PMAP% ARGUMENTS IN T1-T3. DESTROYS NO ACS.

	PUSH	P,T1			;[402] SAVE SOURCE
	SETO	T1,			;[402]   SO WE CAN MAKE IT UNMAP ARG
	PMAP%				;[402] UNMAP THE PAGES
	POP	P,T1			;[402] RESTORE SOURCE
	RETURN				;[402] DONE
END;

> ;[402] END OF IFE FTCOBOL
SUBTTL	GETREC -- GETBUF - Input 1 Physical Buffer -- Magtape


IFE FTCOBOL,<
GETMTA:	MOVEI T1,INLST		;[C08] LOAD PARAMETER FOR SWTBUF
	CALL SWTBUF		;[C08] SWITCH BYTE POINTER AND IOWD BLOCK
	SETZM FILCNT(F)		;[C08] ZAP FILCNT

;START I/O ON NEXT BUFFER AND WAIT FOR I/O TO FINISH ON THIS ONE

	HLRZ T1,FILPGN(F)	;[C08] GET JFN
	MOVX T2,DM%NWT		;[C08] SET "DO NOT WAIT" BIT
	HRRI T2,INLST		;ADDR OF COMMAND LIST
	DUMPI%			;[335] DO IT
	  ERCAL [CAXN T1,IOX4		;[C08] END OF FILE?
		JRST [	HLRZ T1,FILPGN(F)	;YES, GET JFN
			GDSTS%			;[335] GET CURRENT STATUS
			TXZ T2,MT%EOF		;CLEAR EOF FLAG
			SDSTS%			;[335]   ..
			POP P,(P)		;[C03] FIX UP STACK
			JRST GETEOF]		;[C03] GIVE EOF RETURN
		CAXE T1,IOX5		;DEVICE OR DATA ERROR?
		JRST E$$DME		;NO, BUG SOMEWHERE
		HLRZ T1,FILPGN(F)	;GET JFN
		GDSTS%			;[335] GET DEVICE STATUS
		TXZN T2,MT%IRL		;RECORD LENGTH INCORRECT?
		JRST E$$DME		;NO, BUG SOMEWHERE
		SDSTS%			;[335] CLEAR MT%IRL
		HLRZ T3,T3		;YES, GET BYTES READ INTO RH(T3)
		IDIV T3,FILHBW(F)	;[C03] CONVERT HARDWARE BYTES TO WORDS
		SKIPE T4		;[C03] INCLUDING PARTIAL WORDS
		ADDI T3,1		;[C03]  ..
		IMUL T3,IOBPW2		;[C03] CONVERT TO BYTES
		MOVEM T3,FILCNT(F)	;SAVE IN FCB
		MOVX T2,DM%NWT		;[C08] RESTART I/O
		HRRI T2,INLST		;[C08]   ..
		DUMPI%			;[335]   ..
		  ERJMP E$$DME		;TROUBLE
		RET]			;[C03] REJOIN MAIN FLOW
	SKIPN T1,FILCNT(F)	;GET BYTES PER (FULL) BUFFER
	MOVE T1,FILBPB(F)	;[C03] UNLESS THE HAIRY LITERAL ABOVE
				; COMPUTED FILCNT FOR US,
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER HAS BEEN FILLED
	RETSKP			;GIVE GOOD RETURN

;SWTBUF - SWITCH MAGTAPE BUFFERS
;         CALL WITH C(T1) = IOWD BLOCK ADDRESS

SWTBUF:	MOVE T4,FILFLG(F)	;[C02] GET FLAGS
	TXC T4,FI.BF2		;[C02] SWITCH FLAG
	MOVEM T4,FILFLG(F)	;[C02]  ..
	HRRZ T2,FILBF2(F)	;[C02] GET ADDRESS FOR IOWD WORD
	HRRZ T3,FILBUF(F)	;[C02] GET ADDRESS FOR BYTE POINTER
	TXNE T4,FI.BF2		;[C02] BUFFERS SWAPPED?
	EXCH T2,T3		;[C02] YES, SWITCH ADDRESES
	HLRZ T4,FILBUF(F)	;[C02] SET IOWD WORD
	MOVN T4,T4		;[C02]  ..
	HRL T2,T4		;[C02]  ..
	SUBI T2,1		;[C20] [C02]  ..
	MOVEM T2,0(T1)		;[OK] [C02]  ..
	SETZM 1(T1)		;[OK] [C02] TIE OFF END OF IOWD BLOCK
	MOVE T4,IOMODE		;[C02] INDUSTRY COMPATIBLE EBCDIC MAGTAPE?
	CAXE T4,MODEBCDIC	;[C02]  ..
	JRST SWTBF1		;[C02]  ..
	MOVE T4,FILFLG(F)	;[C02]  ..
	TXNE T4,FI.IND		;[C02]  ..
	TXNN T4,FI.MTA		;[C02]  ..
	JRST SWTBF1		;[C02]  ..
	HRLI T3,(POINT 8,)	;[C02] YES, BUILD 8-BIT BYTE POINTER
	SKIPA			;[C02]
SWTBF1:	ADD T3,CBPTR		;[C02] NO, BUILD DEFAULT BYTE POINTER
	MOVEM T3,FILPTR(F)	;[C02] STORE BYTE POINTER
	RET			;[C02]

>;END IFE FTCOBOL
SUBTTL	PUTREC -- PUTBUF - Output 1 Physical Buffer -- Set Up


;CALL:		JSP T4,PUTBUF
;RETURNS:	+1/ ALWAYS, T1 CONTAINS NO. OF BYTES IN BUFFER

PUTBUF:	PUSH P,T4		;PUSH RETURN ADDR ON STACK
	MOVE T1,FILCNT(F)	;[C03] EMPTY BUFFER?
	CAML T1,FILBPB(F)	;[C03]   ..
	RET			;[C03] YES, RETURN WITH BYTE COUNT IN T1
IFE FTCOBOL,<			;FALL THROUGH TO PUTDSK IN COBOL SORT
	MOVE T1,FILFLG(F)	;[435] GET FILE FLAGS
	TXNE T1,FI.DSK		;[435]  FILE ON DISK?
	JRST PUTDSK		;YES
	TXNE T1,FI.MTA		;[435] MAGTAPE?
	JRST PUTMTA		;YES, HANDLE IT

	HLRZ T1,FILPGN(F)	;JFN
	HRRO T2,FILBUF(F)	;STRING PTR TO BUFFER
	MOVE T3,FILCNT(F)	;[444] BYTES NOT WRITTEN
	SUB T3,FILBPB(F)	;[444]  MINUS BYTES PER BUFFER
				;[444]  EQUALS NEGATIVE BYTES WRITTEN THIS TIME
	SOUT%			;[335] MOVE IT OUT
	MOVE T1,FILBPB(F)	;INDICATE BUFFER WAITING
	MOVEM T1,FILCNT(F)	; TO BE FILLED
	HRRZ T1,FILBUF(F)	;GET ADDRESS OF BUFFER
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER TO BUFFER
	MOVEM T1,FILPTR(F)	; ..
	MOVE T1,FILCNT(F)	;[C03] RETURN WITH BYE COUNT IN T1
	RET			;RETURN

>;END IFE FTCOBOL
SUBTTL	PUTREC -- PUTBUF - Output 1 Physical Buffer -- Disk


PUTDSK:	HRLZI T1,.FHSLF		;[C17] MAP FROM THIS PROCESS
	HRR T1,FILBUF(F)	;STARTING AT THIS PAGE
	MOVE T2,FILPGN(F)	;TO THIS FILE
	HLRZ T3,FILBUF(F)	;GET PAGES PER BUFFER
	SKIPLE T4,FILCNT(F)	;WRITING PARTIAL BUFFER?
	CALL FIXUP		;YES, COMPUTE T3=PAGE COUNT
	MOVEM T3,PGSATM		;[464] SAVE # OF PAGES TO PMAP%
	IORX T3,PM%CNT!PM%RWX	;ALLOW ALL TYPES OF ACCESS TO FILE PG.
	PMAP%			;[335] MOVE THEM OUT TO THE FILE
	ERCAL BADMAP		;[457]  GO TO BADMAP IF PMAP% FAILURE.
	HRRZS T3		;GET PAGE COUNT
	HRRZ T2,FILPGN(F)	;GET CURRENT PAGE NUMBER
	ADD T3,T2		;COMPUTE NEXT
	HRRM T3,FILPGN(F)	;SAVE IN FCB
	MOVE T1,FILBPB(F)	;GET NO. OF BYTES PER BUFFER
	MOVE T2,T1		;IN T2 ALSO
	EXCH T1,FILCNT(F)	;FLAG BUFFER FULL, GET # OF  UNUSED BYTES
	SKIPLE	T1		;[465] IF THE # OF UNUSED BYTES IS 0 OR -1,
				;[465]  T2 ALREADY = # OF BYTES WRITTEN.
	SUB T2,T1		;COMPUTE BYTES WRITTEN THIS TIME
	ADDM T2,FILEOF(F)	;UPDATE TOTAL NO. OF BYTES WRITTEN
IFE FTCOBOL,<
	MOVE T4,FILFLG(F)	;[C20] A TEMP FILE?
	TXNE T4,FI.TMP		;[C20]   ..
>
	ADDM T2,TMPTOT		;[C20] YES, UPDATE TOTAL PAGES USED
IFE FTCOBOL,<
	MOVN T1,T2		;[C17] DESCRIMENT BLOCK BYTE COUNT
	ADDM T1,FILKCT(F)	;[C17]   ..
>
	HRRZ T1,FILBUF(F)	;GET STARTING PAGE OF BUFFER
	LSH T1,POW2(PGSIZ)	;COMPUTE ADDRESS
	ADD T1,CBPTR		;CONSTRUCT BYTE POINTER
	MOVEM T1,FILPTR(F)	;SAVE IT
IFE FTCOBOL,<
	TXNN T4,FI.TMP		;[371] IS IT A TEMP FILE?
	SKIPG SEQNO		;[371] OR OUTPUT NOT SEQUENCED
	JRST PUTDS1		;[C03] [371] YES
	HRRZ T2,FILPTR(F)	;[371] YES, MUST CLEAR BUFFER IN CASE
	HLRZ T3,FILBUF(F)	;[371]  ANY BIT 35'S ARE LEFT O
	LSH T3,POW2(PGSIZ)	;[371] GET SIZE OF BUFFER IN WORDS
	ADD T3,T2		;[C20] [371] END+1
	SETZM (T2)		;[C20] CLEAR FIRST WORD
	HRL T2,T2		;[371] FORM BLT POINTER
	ADDI T2,1		;[371] EVENTUALLY
	BLT T2,-1(T3)		;[OK] [371]  AND THE REST
PUTDS1:				;[C03]
>;END IFE FTCOBOL
	MOVE T1,FILCNT(F)	;[C03] RETURN WITH BYTE COUNT IN T1
	RET			;RETURN
SUBTTL	BADMAP -- HANDLE PMAP% FAILURE IF DISK FULL OR QUOTA EXCEEDED


;BADMAP is called, using ERCAL, after a PMAP% failure. This routine
;was written for case two of PMAP%(i.e. Mapping Process Pages to a
;File), but should work equally well for any of the first three cases
;of PMAP%ing(i.e. Mapping not unmapping or deleting). See the Monitor
;Calls Reference Manual.
;An appropriate error message is typed to the tty. If the failure was
;due to the disk being full or exceeding the users quota, then SORT
;may be continued after more disk space is made available. 
;The AC's are saved since they have not been altered from their
;initial state so that upon returning, the calling routine will not
;be aware of the occurrence of the error. The first three AC's are
;also moved into the following memory locations for use in this error
;routine.
;	SOURCE	------	Process handle ,, Page number
;
;	DEST	------	JFN ,, Page number
;
;	ACCESS	------	Access bits ,, Repetition count
;
;This routine checks the pages that were attempted to be PMAP%ed by
;using the RPACS% JSYS. Upon encountering a non-existent page, the
;AC's are reset to reflect the number of pages and the page numbers
;that did not get mapped and attempts to map them. If successful,
;then the old AC values are poped and control is returned to the
;caller. If not successful, then control is passed to BADMP2 and
;a loop is performed as many times as the user desires and is 
;necessary.


BADMAP:	PUSH	P,T3		;[457] SAVE SOME REGISTERS.
	PUSH	P,T4		;[464]
BADMP2:	DMOVEM	T1,SOURCE	;[464] SAVE THE ARG'S FOR THE LATEST PMAP%
	MOVEM	T3,ACCESS	;[464] SAVE THE ACCESS BITS AND REPETITION COUNT
	MOVEI	T1,.FHSLF	;[457] GET PROCESS HANDLE FOR SELF
	GETER%			;[457] GET LAST ERROR OF PROCESS.
	HRRZ	T1,T2		;[457] GET ERROR NUMBER
	CAIE	T1,PMAPX6	;[457] WAS THIS THE LAST ERROR?
	CAIN	T1,IOX11	;[457] OR THIS?
	JRST	VLDERR		;[457] YES!
	HRROI	T1,CRLF		;START MESSAGE ON NEW LINE
	PSOUT%			; INCASE BATCH
	MOVEI	T1,"?"		;PREFIX MESSAGE WITH ?
	PBOUT%
	MOVEI	T1,.PRIOU	;[457] GET READY FOR ERSTR CALL.
	ERSTR%			;[457] PUT OUT THE ERROR MESSAGE. 
	  HALTF%		;[457] SHOULD NOT GET HERE!
	  JRST	.-1		;[457] OR HERE!
	HALTF%			;[457] DIE!
	JRST	.-1		;[457] DO NOT ALLOW TO CONTINUE.

VLDERR:	PUSH	P,T2		;SAVE THE GETER ARGS
	PUSH	P,T3		;...
	SETO	T1,		;CURRENT JOB
	HRROI	T2,GTJARG	;1 WORD
	MOVEI	T3,.JIBAT	;BATCH CONTROL WORD
	GETJI%
	  SETZM	GTJARG		;ASSUME NOT BATCH
	HRROI	T1,CRLF		;START MESSAGE ON NEW LINE
	PSOUT%			; INCASE BATCH
	MOVEI	T1,"$"		;PREFIX MESSAGE WITH $ FOR BATCH
	SKIPL	GTJARG		;IS IT REALLY A BATCH JOB?
	MOVEI	T1,"%"		;NO, JUST GIVE WARNING
	PBOUT%
	POP	P,T3		;RESTORE ORIGINAL ERROR #
	POP	P,T2		;...
	MOVEI	T1,.PRIOU	;[457] GET READY FOR ERSTR CALL.
	ERSTR%			;[457] PUT OUT THE ERROR MESSAGE. 
	  HALTF%		;[457] SHOULD NOT GET HERE!
	  JRST	.-1		;[457] OR HERE!
	HRROI	T1,QEMSG	;[457] SET UP POINTER TO MESSAGE.
	PSOUT%			;[457] PUT OUT MESSAGE.
	HALTF%			;[457] WAIT FOR USER TO FIND MORE SPACE.
				;[457] SORT WILL RESUME AT THIS
				;[457] POINT AFTER BEING CONTINUED.
	SETZ	T3,		;[464] INITIALIZE THE PAGE ADDER.
	MOVE	T1,DEST		;[464] GET DESTINATION OF THE ATTEMPTED PMAP%
	MOVE	T4,PGSATM	;[464] GET THE # OF PAGES ATTEMPTED TO PMAP%
CHKPAG:	HRRZ	T2,DEST		;[464] GET # OF FIRST PAGE ATTEMPTED TO PMAP%
	ADD	T2,T3		;[464] CALCULATE THE PAGE TO SEARCH FOR.
	HRR	T1,T2		;[464] INSERT PAGE # TO SEARCH FOR.
	RPACS%			;[464] GET ACCESSIBILITY OF THIS PAGE.
	TXNN	T2,PA%PEX	;[464] DOES THIS PAGE EXIST?
	JRST	NOPAGE		;[464] NO!
	AOS	T3		;[464] YES, INCREMENT THE PAGE ADDER.
	SOJG	T4,CHKPAG	;[464] CHECK THE NEXT PAGE.
	JRST	FIN		;[464] IF YOU GET HERE, SOMETHING IS FLAKY!

NOPAGE:	DMOVE	T1,SOURCE	;[464] GET THE SOURCE AND DESTINATION FOR THE PMAP%
	ADD	T1,T3		;[464] PAGE # OF SOURCE TO START MAPPING FROM.
	ADD	T2,T3		;[464] PAGE # OF DEST TO START MAPPING INTO.
	MOVE	T4,PGSATM	;[464] GET # OF PAGES ATTEMPTED TO PMAP%
	SUB	T4,T3		;[464] GET # OF PAGES THAT DIDN'T MAKE IT.
	MOVE	T3,ACCESS	;[464] GET ACCESS BITS AND REPETITION COUNT.
	HRR	T3,T4		;[464] UPDATE # OF PAGES TO PMAP%
	PMAP%			;[464] TRY TO MAP THE REMAINDER OF THE PAGES!
	  ERJMP	BADMP2		;[464] DIDN'T GET 'EM ALL, KEEP TRYING!
FIN:	POP	P,T4		;[464] RESTORE ORIGINAL DATA FOR RETURN.
	POP	P,T3		;[457] RESTORE ACCUMULATORS.
	POPJ	17,		;[464][457] RETURN TO CALLING ROUTINE.

CRLF:	ASCIZ	/
/
QEMSG:	ASCIZ /.
Type CONTINUE after expunging deleted files.
/				;[464][457] THAT'S ALL FOLKS.
;FIXUP COMPUTES A PAGE COUNT FOR PMAP% WHEN WE'RE PMAPPING A PARTIAL BUFFERLOAD.
;RETURNS PAGE COUNT IN T3.  MUSTN'T DISTURB T1 OR T2, AND CAN ONLY BE CALLED
;DURING A CLOSE.

FIXUP:	MOVE T3,FILBPB(F)	;[C06] GET BYTES PER (FULL) BUFFER
	SUB T3,T4		;SUBTRACT # OF UNWRITTEN BYTES
IFE FTCOBOL,<
  IF THIS IS NOT A TEMP FILE
	MOVX T4,FI.TMP		;IS THIS A TEMP FILE?
	TDNE T4,FILFLG(F)	;[400]   ..
	JRST $T			;[430] [400] YES IT'S A TEMP FILE
  THEN FIND BYTES PER WORD
	SKIPA T4,IOBPW2		;[C03] [366] T4 = BYTES/WORD
  ELSE BYTES PER WORD IS 1
	MOVX T4,1		;[366] T4 = 1 BYTE/WORD
  FI;
	IMULI T4,PGSIZ		;[366] T4 = BYTES/PAGE
	IDIV T3,T4		;[366] T3 = WHOLE PAGES, T4 = REMAINING WORDS
>
IFN FTCOBOL,<
	IDIVI T3,PGSIZ		;[366] T3 = WHOLE PAGES, T4 = REMAINING WORDS
>				;END IFE FTCOBOL 
	SKIPE T4		;ROUND UP TO WHOLE PAGES
	ADDI T3,1		; ..
	RET
SUBTTL	PUTREC -- PUTBUF - Output 1 Physical Buffer -- Magtape


IFE FTCOBOL,<
PUTMTA:	PUSH P,FILPTR(F)	;[C03] SAVE OLD BYTE POINTER
	MOVEI T1,OUTLST		;[C02] LOAD PARAMETER FOR SWTBUF
	CALL SWTBUF		;[C02] SWITCH BYTE POINTER AND IOWD BLOCK
	POP P,T1		;[C03] RESTORE BYTE POINTER
	MOVE T2,FILBPB(F)	;[C02] GET BUFFER LENGTH IN BYTES
	SUB T2,FILCNT(F)	;[C02] SUBTRACT NO. OF BYTES WE DIDN'T WRITE
	IDIV T2,IOBPW2		;[C03] CONVERT TO WORDS
	JUMPE T3,PUTMT2		;[C03] ROUND UP TO A WHOLE WORD
	ADDI T2,1		; ..
	SUB T3,IOBPW2		;[C03] CLEAR REST OF WORD
	SETZ T4,		;[C03]  ..
	IDPB T4,T1		;[C03]  ..
	AOJL T3,.-1		;[C03]  ..
PUTMT2:	MOVN T2,T2		;[C03] [C02] NEGATE WORD COUNT
	HRLM T2,OUTLST		;[C02] CORRECT IOWD WORD
	HLRZ T1,FILPGN(F)	;GET JFN
	MOVX T2,DM%NWT		;DON'T WAIT ON I/O
	HRRI T2,OUTLST		;ADDR OF CMD LIST
	DUMPO%			;[335] START I/O
	  ERCAL [CAXE T1,IOX5		;DEVICE OR DATA ERROR?
		JRST E$$DME		;NO, BUG
		HLRZ T1,FILPGN(F)	;GET JFN
		GDSTS%			;[335] GET TAPE STATUS BITS
		TXZN T2,MT%EOT		;END OF TAPE?
		JRST E$$DME		;NO, COMPLAIN
		SDSTS%			;[335] YES, CLEAR THE BIT
		MOVX T2,FI.EOT		;[C08] SET EOT BIT IN FILFLG
		IORM T2,FILFLG(F)	;[C08]  ..
		MOVX T2,DM%NWT		;[C08] RESTART I/O
		HRRI T2,OUTLST		;[C08]  ..
		DUMPO%			;[C08]  ..
		  ERJMP E$$DME		;[C08] ERROR
		RET]			;REJOIN MAIN FLOW
	MOVX T1,FI.EOT		;[C08] AT EOT?
	TDNE T1,FILFLG(F)	;[C08]  ..
	CALL   [HLRZ T1,FILPGN(F)	;[C08] YES, GET JFN
		MOVX T2,.MONOP		;[C08] GET MTOPR% FUNCTION FOR WAITING
		MTOPR%			;[C08] WAIT FOR I/O TO COMPLETE
		  SKIPA			;[C08] SHOULD ALWAYS FAIL, STILL EOT
		JRST E$$DME		;[C08] NO, BUG SOMEWHERE
		MOVEI T1,.FHSLF		;[C08] GET ERROR CODE
		GETER%			;[C08]  ..
		HRRZ T1,T2		;[C08]  ..
		CAXE T1,IOX5		;[C08] DEVICE OR DATA ERROR?
		JRST E$$DME		;[C08] NO, BUG
		HLRZ T1,FILPGN(F)	;[C08] GET JFN
		GDSTS%			;[C08] GET TAPE STATUS BITS
		TXZN T2,MT%EOT		;[C08] END OF TAPE?
		JRST E$$DME		;[C08] NO, COMPLAIN
		SDSTS%			;[C08] YES, CLEAR THE BIT
		RET]			;[C08] REJOIN MAIN FLOW
	MOVE T1,FILBPB(F)	;GET BYTES PER BUFFER
	MOVEM T1,FILCNT(F)	;INDICATE BUFFER BEGGING TO BE FILLED
	SKIPG SEQNO		;[371] OUTPUT NOT SEQUENCED
	JRST PUTMT3		;[C03] [371] YES
	HRRZ T2,FILPTR(F)	;[371] YES, MUST CLEAR BUFFER IN CASE
	HLRZ T3,FILBUF(F)	;[371]  ANY BIT 35'S ARE LEFT O
	ADD T3,T2		;[C20] [371] END+1
	SETZM (T2)		;[C20] CLEAR FIRST WORD
	HRL T2,T2		;[371] FORM BLT POINTER
	ADDI T2,1		;[371] EVENTUALLY
	BLT T2,-1(T3)		;[OK] [371]  AND THE REST
PUTMT3:	MOVE T1,FILCNT(F)	;[C03] RETURN WITH BYTE COUNT IN T1
	RET

>;END IFE FTCOBOL
SUBTTL	PSORT. -- Memory Management Routines for TOPS-20


;CHKCOR - CHECK FOR MEMORY SWITCHS AND INSURE ARGUMENTS ARE REASONABLE

CHKCOR:	PUSH P,P1		;GET SOME REGS
	PUSH P,P2		; ..
	CALL MINBUF		;COMPUTE MINIMUM ALLOWABLE BUFFER AREA
	MOVE P1,T1		;SAVE
	CALL MAXBUF		;COMPUTE MAXIMUM ALLOWABLE BUFFER AREA
	CAMGE T1,P1		;[C20] IS MAX .LT. MIN?
	JRST E$$NEC		;YES, DIE
	MOVE P2,T1		;COPY MAXIMUM BUFFER SPACE USEABLE
IFE FTCOBOL,<
	CALL USRBUF		;DID USER SPECIFY A BUFFER AREA SIZE?
	JRST CHKCR1		;NO, USE DEFAULT
	CAMGE T1,P1		;[C20] IS USER'S SIZE .GE. MINIMUM?
	JRST [	$ERROR (%,NBS,<Not enough buffer space specified>)
		MOVE T1,P1	;[C20] USE MINIMUM INSTEAD
		JRST CHKTRE]	;AND RETURN
	CAML T1,P2		;[C20] IS USER'S SIZE .LT. MAXIMUM?
	JRST [	$ERROR (%,TMS,<Too much buffer space specified>)
		MOVE T1,P2	;[C20] USE MAXIMUM INSTEAD
		JRST CHKTRE]	;AND RETURN
	JRST CHKTRE		;USER'S SIZE OK, RETURN IT
>;END IFE FTCOBOL

CHKCR1:	CALL DEFBUF		;COMPUTE DEFAULT BUFFER SIZE
	CAMGE T1,P1		;[C20] RANGE CHECK
	MOVE T1,P1		;[C20]  AGAINST MINIMUM
	CAML T1,P2		;[C20]  AND MAXIMUM
	MOVE T1,P2		;[C09]  ..
;HERE WITH C(T1) = SIZE OF I/O BUFFER AREA TO ALLOCATE

CHKTRE:	ADDI T1,PGSIZ		;[C13] IN CASE NOT ON A PAGE BOUNDARY
	MOVEM T1,BUFSZ		;[C13] SAVE SIZE OF BUFFER AREA
	SKIPLE MRGSW		;MERGE COMMAND?
	JRST CHKRT1		;YES, SETTMP COMPUTED NUMRCB FOR US
	CALL MINTRE		;COMPUTE MINIMUM SIZE OF TREE
	MOVE P1,T1		;[C20] SAVE
	CALL MAXTRE		;COMPUTE MAXIMUM SIZE OF TREE
	CAMGE T1,P1		;[C20] INSURE .GE. MINIMUM
	JRST E$$NEC		;OOPS
	MOVE P2,T1		;[C20] SAVE FOR MORE TESTS
IFE FTCOBOL,<
	CALL USRTRE		;DID USER SPECIFY A TREE SIZE?
	JRST CHKCR2		;NO, USE DEFAULT
	CAMGE T1,P1		;[C20] IS USER .GE. MINIMUM?
	JRST [	$ERROR (%,NLS,<Not enough leaves specified>)
		MOVE T1,P1	;[C20] USE MINIMUM
		JRST CHKRET]	;AND FINISH UP
	CAMLE T1,P2		;[C20] IS USER .LE. MAXIMUM?
	JRST [	$ERROR (%,TML,<Too many leaves specified>)
		MOVE T1,P2	;[C20] USE MAXIMUM
		JRST CHKRET]	;AND FINISH UP
	JRST CHKRET		;USER SIZE IS GOOD, FINISH UP
>;END IFE FTCOBOL

CHKCR2:	CALL DEFTRE		;COMPUTE DEFAULT TREE SIZE
	CAMGE T1,P1		;[C20] RANGE CHECK
	MOVE T1,P1		;[C20]  ..
	CAMLE T1,P2		;[C20]  ..
	MOVE T1,P2		;[C20]  ..

;HERE WITH C(T1) = NUMBER OF WORDS TO ALLOCATE TO TREE

CHKRET:	MOVE T2,REKSIZ		;GET RECORD SIZE
	ADDI T2,RN.LEN		;ADD OVERHEAD WORDS
	IDIV T1,T2		;COMPUTE HOW MANY RECORDS CAN FIT IN TREE
	CAIGE T1,^D16		;MUST HAVE AT LEAST 16
	JRST [$ERROR (?,NET,<Not enough core for tree>)]
	MOVEM T1,NUMRCB		;SAVE FOR INITRE
CHKRT1:	POP P,P2		;RESTORE REGS
	POP P,P1
	RET			;[C13]
;MINBUF - COMPUTE MINIMUM BUFFER SPACE NEEDED FOR SORT

MINBUF:	SKIPLE MRGSW		;MERGE COMMAND?
	JRST MINBF1		;YES, HANDLE DIFFERENTLY

IFN FTCOBOL,<			;FOR COBOL, ONLY NEED 1 TEMP FILE BUFFER
	MOVEI T1,PGSIZ		; WHICH IS ONE PAGE
	RET
>

IFE FTCOBOL,<			;OTHERWISE, NEED 1 INPUT/OUTPUT PG AND 1 TMP PG
	MOVEI T1,PGSIZ		;FOR TEMP FILE INPUT
	MOVE T2,MXDVSZ		;[C05] PLUS BIGGEST INPUT DEVICE BUFFER
	CAMGE T2,OBUFSZ		;[C05] OR BIGGEST OUTPUT DEVICE BUFFER
	MOVE T2,OBUFSZ		;[C05]  ..
	ADD T1,T2		;[C05]  ..
	JRST RNDRET		;ROUND UP TO A PAGE AND RETURN
>

MINBF1:
IFN FTCOBOL,<			;FOR COBOL MERGE, JUST 1 TMP BUFFER
	MOVEI T1,PGSIZ		; ..
	RET
>

IFE FTCOBOL,<			;FOR REGULAR, NEED TMP PLUS INPUT/OUTPUT
	HRRZ T1,ACTTMP		;NUMBER OF ACTIVE TEMP FILES
	IMUL T1,MXDVSZ		;TIMES LARGEST BUFFER (JUST IN CASE)
	MOVE T2,MXDVSZ		;[C05] PLUS BIGGEST INPUT DEVICE BUFFER
	CAMGE T2,OBUFSZ		;[C05] OR BIGGEST OUTPUT DEVICE BUFFER
	MOVE T2,OBUFSZ		;[C05]  ..
	ADD T1,T2		;[C05]  ..
	JRST RNDRET		;ROUND UP TO A PAGE AND RETURN
>
;MAXBUF COMPUTES MAXIMUM BUFFER SPACE ALLOWABLE

MAXBUF:	SKIPLE MRGSW		;MERGE COMMAND?
	JRST MAXBF1		;YES, HANDLE DIFFERENTLY
	MOVEI T1,PGSIZ*8	;8 PAGES FOR TEMP FILE BUFFER
IFN FTCOBOL,<
	RET
>

IFE FTCOBOL,<			;IF LIBOL ISN'T DOING I/O FOR US,
	MOVE T2,MXDVSZ		;GET LARGEST INPUT BUFFER REQUIRED
	LSH T2,POW2(4)		;4 TIMES LARGEST BUFFER
	CAMGE T2,OBUFSZ		;[C05] BUT MUST BE GREATER THAN OUTPUT BUFFER
	MOVE T2,OBUFSZ		;[C05]  ..
	ADD T1,T2		;JUST MAKE SOME ARBITRARY LARGE NUMBER
	JRST RNDRET		;ROUND UP AND RETURN
>

MAXBF1:
IFN FTCOBOL,<			;FOR COBOL MERGE VERB
	MOVEI T1,PGSIZ*^D32	;JUST ALLOW A LARGE OUTPUT (TMP) BUFFER
	RET
>

IFE FTCOBOL,<			;FOR STAND ALONE MERGE
	HRRZ T1,ACTTMP		;GET NUMBER OF INPUT FILES
	IMUL T1,MXDVSZ		;TIMES BIGGEST KNOWN BUFFER
	LSH T1,POW2(20)		;ALLOW VERY LARGE BUFFER
	ADD T1,OBUFSZ		;[C05] PLUS ONE FOR OUTPUT
	JRST RNDRET		;ROUND UP AND RETURN
>
;USRBUF RETURNS USER'S DESIRED BUFFER AREA SIZE

IFE FTCOBOL,<
USRBUF:	SKIPG T1,CORSIZ		;DID HE TYPE ONE?
	RET			;NO, ERROR RETURN
	LSH T1,POW2(PGSIZ)	;YES, CONVERT TO WORDS
	RETSKP
>

;USRTRE - RETURN USER'S DESIRED NO. OF LEAVES ON TREE (/LEAVES:n)

IFE FTCOBOL,<
USRTRE:	SKIPG T1,LEANUM		;IS THERE ONE?
	RET
	MOVE T2,REKSIZ		;GET RECORD SIZE, IN WORDS
	ADDI T2,RN.LEN		; PLUS OVERHEAD WORDS
	IMUL T1,T2		;[C20] COMPUTE SPACE NEEDED FOR TREE
	RETSKP
>

;DEFBUF - COMPUTE DEFAULT BUFFER SPACE TO USE

DEFBUF:	SKIPLE MRGSW		;MERGE COMMAND?
	JRST DEFBF1		;YES, HANDLE DIFFERENTLY
	MOVEI T1,PGSIZ*4	;4 PAGES FOR TEMP FILE BUFFER
IFE FTCOBOL,<
	MOVE T2,MXDVSZ		;[C05] PLUS BIGGEST INPUT DEVICE BUFFER
	CAMGE T2,OBUFSZ		;[C05] OR BIGGEST OUTPUT DEVICE BUFFER
	MOVE T2,OBUFSZ		;[C05]  ..
	ADD T1,T2		;[C05]  ..
>
	JRST RNDRET		;ROUND UP AND RETURN

DEFBF1:	MOVE T1,ACTTMP		;GET NO. OF INPUT FILES
	LSH T1,POW2(PGSIZ*2)	;ALLOW 2 PGS FOR INPUT
IFE FTCOBOL,<			;[C05] IF NOT COBOL MERGE, ADD
	ADD T1,OBUFSZ		;[C05]   ROOM FOR OUTPUT FILE BUFFER
>
;	JRST RNDRET		;ROUND UP AND RETURN

RNDRET:	TRZE T1,PGMSK		;[365] ON PAGE BOUNDARY?
	ADDI T1,PGSIZ		;[365] NO, ROUND UP
	RET			;AND RETURN
;MINTRE COMPUTES MINIMUM SPACE NEEDED FOR SORT TREE

MINTRE:	MOVE T1,REKSIZ		;GET RECORD SIZE
	ADDI T1,RN.LEN		;PLUS OVERHEAD WORDS
	LSH T1,POW2(^D16)	;NEED AT LEAST 16 NODES
	RET

;MAXTRE - COMPUTE MAXIMUM ALLOWABLE SPACE FOR TREE

MAXTRE:	MOVE T1,FRECOR		;[C13] GET FREE CORE
	SUB T1,BUFSZ		;[C13] MINUS BUFFER POOL SPACE
	SUBI T1,1000		;[C13] MINUS ODD BUFFERS (I.E. LABELS ETC.)
	RET

;DEFTRE - COMPUTE DEFAULT SIZE FOR TREE

DEFTRE:	MOVEI T1,RN.LEN		;GET LENGTH OF A NODE
	ADD T1,REKSIZ		;PLUS LENGTH OF A RECORD
	IMULI T1,NRECS		;TIMES NO. OF RECORDS WE WANT TO HAVE
				; IN CORE BY DEFAULT
	RET
;RFMBFP -- REFORMAT BUFFER POOL FOR MERGE PHASE OF SORT

RFMBFP:	MOVE T1,BUFSZ		;GET SIZE OF BUFFER POOL
	CALL FRESPC		;DROP IT LIKE A HOT POTATO
	MOVE T1,RCBSZ		;[C13] GET SIZE OF RECORD POOL
	CALL FRESPC		;[C13] DEALLOCATE IT
	MOVE T1,TRESZ		;[C13] GET SIZE OF TREE AREA
	CALL FRESPC		;[C13] DEALLOCATE IT
	MOVEI T1,RN.LEN		;[313] SIZE OF A TREE NODE
	IMUL T1,NUMRCB		;[313]   TIMES NUMBER OF RECORDS
	MOVEM T1,TRESZ		;[C13] SAVE SIZE OF TREE
	CALL GETSPC		;ALLOCATE SPACE FOR TREE
	  JRST E$$RBP		;CONFUSION
	MOVEM T1,TREORG		;[313] TREE ORIGIN
	MOVE T1,REKSIZ		;[C13] GET SIZE OF RECORD
	IMUL T1,NUMRCB		;[C13]   TIMES NUMBER OF RECORDS
	MOVEM T1,RCBSZ		;[C13] SAVE SIZE OF RECORD POOL
	CALL GETSPC		;[C13] ALLOCATE SPACE FOR RECORD POOL
	  JRST E$$RBP		;[C13] FAILED
	MOVEM T1,RCBORG		;[C13] RECORD POOL ORIGIN
	HRRZ T1,ACTTMP		;GET NUMBER OF TEMP FILES
	LSH T1,POW2(PGSIZ)	;TIMES SIZE OF ONE BUFFER (1 PAGE)

IFN FTCOBOL,<			;IF COBOL SORT,
	SETZ T2,		;[C13] ASSUME NO OUTPUT BUFFER
	SKIPE NUMLFT		;IF WE NEED TO DO A MERGE PASS
	MOVEI T2,OUTSIZ		;[C13] GET DESIRABLE SIZE FOR OUTPUT BUFFER
	MOVEM T2,OBUFSZ		;[C13] SAVE IT
>

	ADD T1,OBUFSZ		;[C13] ADD SIZE OF OUTPUT BUFFER

	IMULI T1,PPTBUF*2	;DOUBLE BUFFERING WOULD BE NICE
	ADDI T1,PGSIZ		;[C13] IN CASE NOT ON A PAGE BOUNDARY
	PUSH P,P2		;GET A REG FOR FLAG
	SETZ P2,		;ASSUME DOUBLE BUFFERING
	PUSH P,T1		;SAVE UNIT BUFFER-POOL SIZE
	PUSH P,P1		;GET A REG
	MOVE P1,T1		;SAVE WHAT WE WANT TO ALLOCATE
	CALL GETSPC		;TRY FOR IT
	  CALL [SUBI P1,PGSIZ		;[C13] CAN'T GET IT, TRY FOR A SINGLE BUFFER
		LSH P1,-<POW2(2)>	;[C13]  ..
		ADDI P1,PGSIZ		;[C13]  ..
		MOVE T1,P1		; ..
		CALL GETSPC		;ALLOCATE IT
		  JRST E$$ICM		;INSUFFICIENT CORE
		PUSH P,T1		;$ERROR TRASHES T1
		$ERROR(%,CDB,<Can't double buffer merge pass>)
		POP P,T1
		SETO P2,		;FLAG DOUBLE BUFFERING
		RET]			;REJOIN MAIN FLOW
	MOVEM T1,BUFORG		;[C13] SAVE START OF BUFFER POOL
	MOVEM T1,BUFPTR		;REMEMBER WHERE BUFFER POOL STARTS
	MOVEM P1,BUFSZ		;REMEMBER SIZE OF BUFFER POOL
	TRZE T1,PGMSK		;[C13] CALCULATE USEFUL BUFFER SPACE
	ADDI T1,PGSIZ		;[C13]   ..
	SUB T1,BUFORG		;[C13]   ..
	SUB P1,T1		;[C13]   ..
	MOVEM P1,UBUFSZ		;[C13] SAVE IT
	POP P,P1		;RESTORE P1
	POP P,T1		;RESTORE UNIT BUFFER-POOL SIZE
	IDIVI T1,PPTBUF*2	;RECOVER UNIT BUFFER SIZE
	SUB T1,OBUFSZ		;[C13]  FOR TEMP FILES
;	JRST RFMBP1
;HERE WHEN WE HAVE ENOUGH CORE FOR BUFFERS - DIVIDE WHAT
; WE'VE GOT AMONG TEMP FILE AND OUTPUT FILE BUFFERS

RFMBP1:	MOVE T2,UBUFSZ		;[C13] GET USEFUL SIZE OF BUFFER POOL
	SUB T2,OBUFSZ		;[C13] RESERVE SPACE FOR OUTPUT BUFFER
	SKIPN P2		;IF DOUBLE BUFFERED,
	SUB T2,OBUFSZ		;[C13]  RESERVE SPACE FOR 2 OUTPUT BUFFERS
	IDIV T2,T1		;[C20] DIVIDE POOL SIZE BY UNIT BUFFER-POOL
				; SIZE TO GET DEGREE OF BUFFERING
	JUMPE T2,E$$FER		;CAN'T ALLOCATE THEM
	MOVEM T2,TBUFNO		;PAGES PER TEMP FILE BUFFER
	MOVE T1,UBUFSZ		;[C13] GET USEFUL SIZE OF BUFFER POOL
	IMUL T2,ACTTMP		;COMPUTE SPACE OCCUPIED BY TEMP
	LSH T2,POW2(PGSIZ)	; FILE BUFFERS
	SUB T1,T2		;WHAT'S LEFT IS FOR OUTPUT FILE

IFE FTCOBOL,<			;IF NOT COBOL, WE HAVE REAL OUTPUT
	MOVE T2,OBUFSZ		;GET OUTPUT BUFFER SIZE
	CAIGE T2,PGSIZ		;USE LARGE OF TEMP OR OUTPUT
	MOVEI T2,PGSIZ		; ..
>

IFN FTCOBOL,<			;IF COBOL, JUST TEMP FILE OUTPUT
	MOVEI T2,PGSIZ		; ..
>

	IDIV T1,T2		;DIVIDE SPACE BY SIZE OF BUFFERS
	CAIGE T1,2		;NEED AT LEAST 2 BUFFERS
	JRST E$$NRO		;NO ROOM
	MOVEM T1,OBUFNO		;SAVE NUMBER OF BUFFERS
	POP P,P2		;RESTORE P2
	RET			;AND RETURN
;FMTBFP -- SETUP MEMORY POOLS

BEGIN
  PROCEDURE	(PUSHJ	P,FMTBFP)
$1%	MOVE	T1,NUMRCB	;GET NO. OF RECORDS IN TREE
	IMULI	T1,RN.LEN	;COMPUTE SIZE OF TREE
	MOVEM	T1,TRESZ	;[C13] SAVE TREE SIZE
	CALL	GETSPC		;[C13] ALLOCATE SPACE FOR TREE
	  JRST	$2		;[C13] FAILED
	MOVEM	T1,TREORG	;[C13] SAVE ADDR OF START OF TREE
	MOVE	T1,NUMRCB	;[C13] GET NO. OF RECORDS IN TREE
	IMUL	T1,REKSIZ	;[C13]  TIMES SIZE OF RECORDS
	MOVEM	T1,RCBSZ	;[C13] SAVE SIZE OF RECORD POOL
	CALL	GETSPC		;[C13] ALLOCATE SPACE FOR RECORDS
	  JRST [MOVE	T1,TRESZ	;[C13] FAILED, DEALLOCATE TREE SPACE
		CALL	FRESPC		;[C13]   ..
		JRST	$2]		;[C13]
	MOVEM	T1,RCBORG	;[C13] SAVE ADDR OF START OF RECORD POOL
	JRST	$3		;[C13]
IFN FTCOBOL,<
$2%	MOVE T1,NUMRCB		;[C20] NOT ENOUGH SPACE, TRY FOR SMALLER TREE
	LSH T1,-<POW2(2)>	; BY ONE HALF
	CAIGE T1,^D64		;MAKE SURE TREE DOESN'T TURN
	JRST E$$NEC		; INTO A SHRUBBERY
	MOVEM T1,NUMRCB		; (APOLOGIES TO M. PYTHON)
	JRST $1			;[C13] GO TRY AGAIN
>
IFE FTCOBOL,<
$2%	  JRST E$$NEC		;IF STANDALONE, NO SECOND CHANCES
>
$3%	MOVE	T1,BUFSZ	;[C13] ALLOCATE SEPARATE AREA FOR BUFFER POOL
	CALL	GETSPC		; ..
	  JRST E$$NEC
	MOVEM	T1,BUFPTR	;REMEMBER WHERE IT STARTS
	MOVEM	T1,BUFORG	;[C13] SAVE START OF BUFFER POOL
	MOVE	T2,BUFSZ	;[C13] CALCULATE USEFUL BUFFER SPACE
	TRZE	T1,PGMSK	;[C13]   ..
	ADDI	T1,PGSIZ	;[C13]   ..
	SUB	T1,BUFORG	;[C13]   ..
	SUB	T2,T1		;[C13]   ..
	MOVEM	T2,UBUFSZ	;[C13] SAVE IT
	RETURN			;[C13]
END;
;ALCBPG -- ALLOCATE BUFFER ON A PAGE BOUNDARY
;CALL WITH:	T1/ NO. OF WORDS DESIRED
;RETURNS+1 WITH T1/ ADDRESS OF BUFFER

ALCBPG:	MOVE T2,BUFPTR		;GET FREE POINTER
	TRZE T2,PGMSK		;[365] ON PAGE BOUNDARY?
	ADDI T2,PGSIZ		;[365] NO, ROUND UP TO NEXT PAGE
	MOVEM T2,BUFPTR		; ..
ALCBP1:	MOVE T3,BUFORG		;[C13] GET START OF BUFFER AREA
	ADD T3,BUFSZ		;[C13] CALCULATE END OF BUFFER AREA
	SUB T3,BUFPTR		;SUBTRACT BOTTOM
	CAMLE T1,T3		;IS WHAT'S WANTED .GT. WHAT'S THERE?
	JRST E$$BAF		;ALLOCATION FAILURE
	ADDM T1,BUFPTR		;UPDATE PTR
	MOVE T1,T2		;RETURN ADDR OF SPACE
	RET

;ALCBUF - ALLOCATE BUFFER SPACE, NOT NECESSARILY ON PAGE BOUNDARY

ALCBUF:	MOVE T2,BUFPTR		;GET PTR TO BUFFER AREA
	CALLRET ALCBP1		;JOIN COMMON CODE
;[371] ALCBPZ--ALLOCATE BUFFER ON A PAGE BOUNDARY AND MAKE SURE BUFFER AREA IS ZERO
ALCBPZ:	PUSH	P,T1		;[371] SAVE SIZE REQUIRED
	CALL	ALCBPG		;[371] GET BUFFER SPACE
	HRLZ	T2,T1		;[371] BUILD BLT POINTERS
	HRRI	T2,1(T1)	;[OK] [371] ...
	POP	P,T3		;[371] GET SIZE BACK
	ADD	T3,T1		;[C20] [371] END+1
	SETZM	(T1)		;[OK] [371] ZERO FIRST WORD
	BLT	T2,-1(T3)	;[OK] [371] CLEAR ALL OF DATA
	RET			+;[371]
SUBTTL	COLLATING SEQUENCE ROUTINES

IFE FTCOBOL,<

BEGIN
  PROCEDURE	(PUSHJ	P,COLTRX)
	MOVE	T1,COLJFN	;GET JFN
	MOVX	T2,OF%RD!FLD(7,OF%BSZ) ;OPEN FOR READ, 7-BIT BYTES
	OPENF%			;[335]   ..
	  ERJMP	E$$OPN
	MOVE	T1,[IFIW COLBUF]	;[C20] GET THE ALT SEQ TABLE
	MOVEM	T1,COLSW	;STORE THE ADDRESS OF THE TABLE
	MOVEI	T2,COLCHR	;ADDRESS OF THE INPUT ROUTINE
	PUSHJ	P,BLDCOL	;BUILD THE TABLE
	  JRST	E$$ICS		;ILLEGAL COLLATING SEQUENCE SPECIFIED
	MOVE	T1,COLJFN
	CLOSF%			;[335]
	  JFCL
	RETURN
END;

BEGIN
  PROCEDURE	(PUSHJ	P,COLCHR)
	SOSGE	COLPTR+1	;REDUCE THE BYTE COUNT
	JRST	$1		;GET A BUFFER
	HRRZ	T1,COLPTR	;[C20] GET WORD
	MOVE	T1,(T1)		;[C20]   ..
	TRNE	T1,1		;CHECK FOR SEQUENCE NUMBER
	JRST	[AOS	COLPTR		;IT IS
		MOVNI	T1,5
		ADDM	T1,COLPTR+1	;ACCOUNT FOR 5 BYTES
		JRST	COLCHR]		;LOOP BACK
	ILDB	T1,COLPTR	;GET A BYTE
	TXNN	P1,COL.QU	;[467] ARE WE IN A QUOTE?
	CAILE	T1," "		;[467] NO, IGNORE SPACE AND ALL CONTROL CHARACTERS
	CAIGE	T1," "		;[467] YES, IGNORE  ALL CONTROL CHARACTERS
	JRST	$B		;GET THE NEXT CHARACTER
	PJRST	CPOPJ1		;SKIP RETURN WITH CHAR IN T1

  $1%	MOVE	T1,COLJFN	;GET JFN
	HRROI	T2,COLITB	;USE LIT BUFFER
	MOVEI	T3,COLITS	;NO. OF CHARS
	SETZ	T4,
	SIN%			;[335] GET IT
	  ERJMP	[MOVX	T1,.FHSLF	;GET MOST RECENT ERROR NUMBER
		GETER%			;[335]   ..
		HRRZ	T1,T2		; ..
		CAXE	T1,IOX4		;END OF FILE?
		JRST	[PUSHJ	P,E$$BER
			$MORE	(FILESPEC,COLJFN)
			JRST	LASTER]		;[357] TRY TO GIVE MORE INFO
		CAIN	T3,COLITS	;WAS BYTE COUNT ALTERED (I.E., WAS ANYTHING READ?)
		RETURN			;NO, ASSUME EOF
		JRST	.+1]		;CONTINUE, GET EOF AGAIN NEXT TIME
	MOVEI	T1,COLITS	;GET BYTES PER BUFFER
	SUB	T1,T3		;DEDUCT NO. OF BYTES NOT READ
	MOVEM	T1,COLPTR+1	;EQUALS NO. OF BYTES IN THIS BUFFERLOAD
	MOVE	T1,[POINT 7,COLITB]
	MOVEM	T1,COLPTR	;STORE NEW BYTE POINTER
	JRST	$B		;GET THE NEXT CHARACTER
END;
>;END IFE FTCOBOL
SUBTTL	ERROR MESSAGES

;.TOLEB - PRINT "LOOKUP/ENTER" BLOCK (FILESPEC STRING) FOR
; JFN IN T2 (USED BY $MORE MACRO)

.TOLEB:	MOVX T1,.PRIOU		;TYPE ON PRIMARY OUTPUT JFN
	MOVX T3,<1B2+1B5+1B8+1B11+1B14+1B17+1B20>!JS%PAF ;TYPE ALL FIELDS
	JFNS%			;[335]   TYPE IT
	RET

E$$OPN:	$ERROR (?,OPN,<OPENF failure for file >,+)
	HLRZ T2,FILPGN(F)	;GET JFN OF FILE
	$MORE (FILESPEC,T2)
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$CFF:	$ERROR (?,CFF,<CLOSE failure for file >,+)
	HLRZ T1,FILPGN(F)	;GET JFN
	$MORE (FILESPEC,T1)
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$FPM:	$ERROR (?,FPM,<File page already mapped>)

E$$FPU:	$ERROR (?,FPU,<File page already unmapped>)

E$$IDN:	$ERROR (?,IDN,<Invalid directory number for temp file>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$BIS:	$ERROR (?,BIS,<Non-skip return from BIS>)

E$$GFT:	$ERROR (?,GFT,<GTJFN% failed for temp file>,+)	;[357]
LASTER:	$MORE	(TEXT,<
	>)
	MOVEI	T1,.PRIOU	;[357] PRINCIPAL OUPUT DEVICE
	HRLOI	T2,.FHSLF	;[357] CURRENT FORK,,LAST ERROR
	SETZB	T3,T4		;[357] NO LIMIT,,FULL MESSAGE
	ERSTR%			;[357] PRINT THE MESSAGE
	  JFCL			;[357] IGNORE UNDEFINED ERROR NUMBER
	  JFCL			;[357] IGNORE ERROR DURING EXECUTION OF ERSTR
	$DIE			;[357] GIVE UP

E$$RBP:	$ERROR (?,RBP,<Reformat of buffer pool failed>)

E$$ICM:	$ERROR (?,ICM,<Insufficient core for merge phase>)

E$$FER:	$ERROR (?,FER,<Can't allocate temp buffers at RFMBFP>)

E$$NRO:	$ERROR (?,NRO,<No room for output buffer in merge phase>)

E$$BAF:	$ERROR (?,BAF,<Buffer allocation failed>)

IFE FTCOBOL,<			;NON-COBOL ERROR MESSAGES
E$$CDL:	$ERROR (?,CDL,<Can't do input from the line printer>)

E$$CDC:	$ERROR (?,CDC,<Can't do output to the card reader>)

E$$AND:	$ERROR (?,AND,<ARPANET device illegal for SORT>)

E$$FED:	$ERROR (?,FED,<FRONT-END device illegal for SORT>)

E$$FMC:	$ERROR (?,FMC,<File's mode conflicts with mode switch for >,+)
	HRRZ T2,X.JFN(P1)	;[OK] [305] PRINT THE FILE SPEC
	$MORE (FILESPEC,T2)	;[305]   ..
	$DIE			;[305]

E$$IDM:	$ERROR (?,IDM,<Illegal data mode>)

E$$JFT:	$ERROR (?,JFT,<JFNS% failed for temp file>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFORMATION

E$$NSD:	$ERROR (?,NSD,<No such device>)

E$$DME:	$ERROR (?,DME,<Error in DUMP MODE I/O to magtape>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$NFS:	$ERROR (?,NFS,<No filename specified for labelled tape >,+)
	HRRZ T2,X.JFN(P1)	;[OK]
	$MORE (FILESPEC,T2)
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$FTL:	$ERROR (?,FTL,<Filespec field too long for labelled tape >,+)
	HRRZ T2,X.JFN(P1)	;[OK]
	$MORE (FILESPEC,T2)
	$DIE

E$$JFO:	$ERROR (?,JFO,<JFNS% failed for output file>,+)	;[357]
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

E$$BER:	$ERROR (?,BER,<Hard input error for device >,+)
	HLRZ T2,FILPGN(F)	;GET JFN
	$MORE (FILESPEC,T2)
	JRST	LASTER		;[357] TRY TO GIVE MORE INFO

>;END IFE FTCOBOL