Google
 

Trailing-Edge - PDP-10 Archives - steco_19840320_1er_E35 - 10,5676/teco/newsrc/tecfil.mac
There are 3 other files named tecfil.mac in the archive. Click here to see a list.
	SUBTTL	Introduction

; Copyright (c) 1980 Stevens Institute of Technology, Hoboken, New Jersey
; 07030.

; This software may be used and copied provided that this copyright notice
;is included, and provided that copies of all modifications are sent to:
;
;	TECO Project
;	Computer Center
;	Stevens Institute of Technology
;	Castle Point Station
;	Hoboken, New Jersey    07030
;
;
; The information in this software is subject to change without notice
; and should not be construed as a commitment by Stevens Institute of
; Technology.

  ; Search needed universals

	SEARCH	TECUNV		; TECO universal file

  ; Generate the prologue


	TECVER==200		; Major version number
	TECMIN==1		; Minor version number
	TECEDT==1126		; Edit level
	TECWHO==0		; Last editor


	PROLOGUE(FIL,<TECO File system interface>)	; Generate the TITLE and other stuff
	SUBTTL	Table of Contents

;+
;.pag.lit

;		Table of Contents for TECFIL - File system interface
;
;
;			   Section			      Page
;   1. Introduction . . . . . . . . . . . . . . . . . . . . .    1
;   2. Table of Contents. . . . . . . . . . . . . . . . . . .    2
;   3. Revision History . . . . . . . . . . . . . . . . . . .    3
;   4. Macro definitions
;        4.1.   $RETE . . . . . . . . . . . . . . . . . . . .    4
;   5. Misc definitions for TECFIL. . . . . . . . . . . . . .    5
;   6. F$INIT - File system initialization. . . . . . . . . .    6
;   7. F$SETI - Set the input routine to be the standard. . .    7
;   8. Default switch table . . . . . . . . . . . . . . . . .    8
;   9. F$PARS - Parse a file specificaton . . . . . . . . . .    9
;  10. Switch processing
;       10.3.   F$SXBT - Get a sixbit item. . . . . . . . . .   18
;       10.4.   F$PROT - Input a file protection. . . . . . .   19
;       10.5.   F$KEY - Input a keyword . . . . . . . . . . .   19
;       10.6.   F$CORE - Input an amount of core. . . . . . .   20
;       10.7.   F$VERS - Input a version number . . . . . . .   21
;  11. F$DFLT - Default a FDB from another. . . . . . . . . .   26
;  12. F$EB - Do Edit backup function . . . . . . . . . . . .   27
;  13. EB subroutines
;       13.1.   EBFAIL. . . . . . . . . . . . . . . . . . . .   30
;  14. F$ENQ - ENQ a file . . . . . . . . . . . . . . . . . .   31
;  15. F$DEQ - DEQ a file . . . . . . . . . . . . . . . . . .   32
;  16. ENQ/DEQ Subroutines
;       16.1.   WRTCHR - Write a character. . . . . . . . . .   33
;  17. F$OPEN - Open a file . . . . . . . . . . . . . . . . .   34
;  18. Byte mode routines
;       18.1.   F$WRIT - Output a byte. . . . . . . . . . . .   42
;       18.2.   F$READ - Read a byte. . . . . . . . . . . . .   43
;       18.3.   Sixbit input/output . . . . . . . . . . . . .   44
;       18.4.   ASCII mode input/output . . . . . . . . . . .   46
;       18.5.   LSA input/output. . . . . . . . . . . . . . .   47
;  19. F$IBYT - Input a byte. . . . . . . . . . . . . . . . .   49
;  20. F$OBYT - Write a byte to a file. . . . . . . . . . . .   50
;  21. F$COPY - Copy from one file to another . . . . . . . .   51
;  22. F$COPY
;       22.1.   Subroutines
;            22.1.1.     DBFGEN - General dump buffer routine   52
;            22.1.2.     DBFASC - ASCII dump buffer routine .   53
;            22.1.3.     FIXBRH - Fix a buffer header . . . .   54
;            22.1.4.     CPYOUT - Output a buffer . . . . . .   55
;            22.1.5.     CPYGEN - General copy routine. . . .   56
;            22.1.6.     CPYGEN - General copy routine. . . .   57
;  23. F$CLOS - This routine will close a file. . . . . . . .   58
;  24. F$RSET - This routine will reset an I/O channel. . . .   59
;  25. F$USET - Do a USETI/USETO FILOP. . . . . . . . . . . .   60
;  26. F$RENM - Rename a file . . . . . . . . . . . . . . . .   61
;  27. FIXDEV - Routine to fix up a device name and path. . .   62
;  28. Subroutines
;       28.1.   RENSUB. . . . . . . . . . . . . . . . . . . .   63
;  29. F$ERR - Error processing for the file errors . . . . .   64
;  30. CHKFFI - Check if file is found in a different area. .   65
;  31. Low segment for TECFIL . . . . . . . . . . . . . . . .   66
;  32. End of TECFIL. . . . . . . . . . . . . . . . . . . . .   67

;.end lit.pag
;-
	SUBTTL	Revision History
COMMENT	|

1000	Start of this version

1001	By: Nick Bush		On: 15-July-1980
	Fix table entry to allow EH commands without an argument.
	Avoid %TECSUP messages when doing an EB/INPLACE.
	Modules: TECTBL,TECFIL

1014	By: Nick Bush		On: 11-August-1980
	1) Don't do the AUTO-BUFFER command if we had a error on the last command.
	   This avoids losing info for the error messages.
	2) Make an EB/ER/EX sequence end up renaming the correct files, not
	   trying to rename the ER'ed file to the EB'ed file.BAK, ...
	3) Make TEC file=file work the way it is supposed to.
	Modules: TECPRS,TECFIL,TECECM,TECINI

1023	By: Robert McQueen		On: 16-August-1980
	Cause the path to be printed for the E< commands if the file that is edited
	is in the default path.
	Modules: TECFIL

1027	By: Nick Bush		On: 21-August-1980
	Shorten FILOP. block to not include the PPN word. This avoids random
	protection failures.
	Modules: TECFIL

1030	By: Robert McQueen		On: 21-August-1980
	- Clean up defaulting routines
	- Prevent EB files from always seeming to be /INPLACE.
	Modules: TECFIL,TECECM

1031	By: Robert McQueen		On: 22-August-1980
	Attempt to speed up reading characters by not saving T2 to T4 in F$READ and
	rearrange the checks in the routine so they are faster.
	Modules: TECFIL,TECUUO

1033	By: Nick Bush		On: 22-August-1980
	Fix F$ENQ to turn on the bypass level numbers bit for the ENQ.,
	and to not allow the file to be edited if it gets the ENQDR% error.
	Modules: TECFIL

1035	By: Nick Bush		On: 25-August-1980
	Fix CHKFFI to handle ersatz devices correctly. Use the path
	returned by the monitor as the assumed path where the file
	should exist. If it didn't exist there, give the FFI message
	correctly. Under either case, if it is not the default path,
	do the correct thing with EB'ed files.
	Modules: TECFIL

1036	By: Nick Bush		On: 26-August-1980
	Restore .JBFF before calling CHKFFI in POSREA and POSAPP. This
	avoids problems with memory management if the FFI message is to
	be given.
	Modules: TECFIL

1037	By: Robert McQueen		On: 26-August-1980
	Fix edit 1030 for defaulting.
	Modules: TECUNV,TECFIL

1043	By: Robert McQueen		On: 1-October-1980
	?Illegal memory reference at PC .... (M$XPND+some).  Stuffing the
	warning message into the QRG which expands, but .JBFF is wrong.
	Modules: TECFIL

1054	By: Nick Bush		On: 13-November-1980
	Fix yet another case of .JBFF being screwed up while a warning message
	is being given.
	Modules: TECFIL

1055	By: Robert McQueen		On: 18-November-1980
	Add two new :^T commands to put the terminal into packed image mode and to
	take it out again.
		:-3^T - Put into packed image mode
		:-4^T - Take it out of packed image mode
	Modules: TECUNV,TECFIL,TECCMD,TECMVM

1071	By: Nick Bush		On: 10-January-1981
	Add F$RBUF and F$WBUF routines to improve I/O performance.
	Also add /MODE:DUMP for all file commands.
	Modules: TECUNV,TECFIL,TECCMD,TECECM

1072	By: Robert McQueen		On: 15-January-1981
	Random errors from TECIOE to Ill mem refs.  Clear LASFDB in F$WBUF before
	returning to the caller.
	Modules: TECFIL

1075	By: Robert McQueen		On: 28-January-1981
	Files that were edited with /MODE:DUMP had the habit of having junk at the
	end of the file or the last few characters trashed.  Fix the code that
	clears the characters after the end of the editing buffer to do it
	right.
	Modules: TECFIL

1076	By: Nick Bush		On: 30-January-1981
	Add a default mode to be mode type 0. This will allow /MODE:ASCII
	to really be such with LSA files. Also make /MODE: properly default
	from ER/EW to EB and vice versa.
	Modules: TECUNV,TECCMD,TECFIL,TECECM

1101	By: Robert McQueen		On: 13-Febuary-1981
	If the file was 'magic' in size don't blow it away.  (Last editing buffer
	fit in the last disk buffer, so LASFDB was never cleared, because no 
	output was done.)
	Modules: TECFIL

1105	By: Robert McQueen		On: 18-March-1981
	Null files did not work with dump mode input.
	Modules: TECFIL

1114	By: Robert McQueen		On: 17-July-1981
	Fix a defaulting problem with sub-file directories.
	Modules: TECFIL

1121	By: Nick Bush		On: 17-August-1981
	Control-U's in three line command buffers did not work.  More
	checks were needed in DISPLY.
	
	Fix defaulting of file spec paths to work correctly.  A previous edit
	had broken it.
	Modules: TECVID,TECFIL

Start of Version 200A(1126)
|
	SUBTTL	Macro definitions -- $RETE

;+
;.HL1 $RETE
;This is a helper macro to return the error code to the caller
;.literal
;
; Usage:
;	$RETE	(XXX)
;.end literal
;-

DEFINE	$RETE(XXX),<
	JRST	[MOVX	T1,$FE'XXX
		PJRST	EATALT]
	>; End of $RETE macro definition

	SUBTTL	Misc definitions for TECFIL

; The following are misc definitions for the I/O blocks

	.FOLEN==.FOPAT+1	; Length of a FILOP. block
	.RBLEN==.RBALC		; Length of a LOOKUP block
	SUBTTL	F$INIT - File system initialization

	$CODE			; Start of the code

;+
;.HL1 F$INIT
;This routine will initialize the file system interface module.
;This routine should be called once during the initialization of this
;editor.
;.literal
;
; Usage:
;	PUSHJ	P,F$INIT	; Initialize the file system
;	(Return)		; Always return
;.end literal
;-

F$INIT:	STORE	T1,F$BZER,F$EZER,0 ; Clear the low segment
	MOVX	T1,.PTFRD	; Read the default path
	MOVEM	T1,MYPATH+.PTFCN ; Store in the function word
	MOVE	T2,[XWD .PTMAX,MYPATH] ; Set up to read the default path
	MOVE	T1,MYPPN	; Get the default for the path
	PATH.	T2,		; Get the default path from the operating system
	 MOVEM	T1,MYPATH+.PTPPN ; Failed - Use the PPN
	MOVEI	T3,.GTLOC	; Get the location of job 0
	GETTAB	T3,		; from the monitor
	  JRST	F$IN.0		; Assuem default
	MOVE	T1,[XWD .NDRNN,T2] ; Get the argument pointer
	MOVEI	T2,2		; Two arguments
	NODE.	T1,		; Get the node name
F$IN.0:	 MOVX	T1,<SIXBIT /LOCAL/>	; Assume this
	MOVEM	T1,MYNODE	; Save this
	MOVX	T1,F%FDAE&LH.ALF!.GTFET ; See if FILDAE is there
	GETTAB	T1,		; Get it from the monitor
	  SETZ	T1,		; Assume not
	SETZM	FDAEM		; Ditto
	TXNN	T1,F%FDAE&RH.ALF ; Well?
	  POPJ	P,		; Return
	SETOM	FDAEM		; Yes
	MOVX	T1,%SIFDA	; See if FILDAE is running
	GETTAB	T1,		;  .  .  .
	  SETZ	T1,		; Assume not there
	JUMPE	T1,.POPJ	; Have one?
	SETOM	FDAEM		; No, not running
	POPJ	P,		; Return to the caller
	SUBTTL	F$SETI - Set the input routine to be the standard

;+
;.HL1 F$SETI
;This routine will set the input routine for the F$PARS and the F$SXBT and
;other routines to be the normal input routines.
;.literal
;
; Usage:
;	MOVEI	T1,Error.address
;	PUSHJ	P,F$SETI
;	(Return)
;.end literal
;-

F$SETI:	MOVEM	T1,ERRRTN	; Store the error return
	MOVEI	T1,CMDCHR	; Get the input routine address
	MOVEM	T1,RTN		; Store the routine address
	POPJ	P,		; Return to the caller


; The following is the input routine for the input of a character
; It is set up by the call to F$SETI
	BITMSK(IGNCHR,.CH,<CRT,FFD,LFD,VTB>) ; Characters to ignore


CMDCHR:	$SAVE	<T1>		; Save T1
CMDC.0:	PUSHJ	P,SKRCH		; Get the next character
	  JRST	@ERRRTN		; Failed
	MOVX	T1,IGNCHR	; Get the mask of characters to ignore
	LSH	T1,(CH)		; Shift it
	JUMPL	T1,CMDC.0	; If we don't want it, get the next
	CAXN	CH,.CHESC	; Is this an escape ?
	 SETZ	CH,		; No - Return
	POPJ	P,		; Return to the caller
	SUBTTL	Default switch table

; The following are the switches that are always applied to a file specification
DEFINE	DEFSWT,<
SW	DEFAULT,<POINTR(<.FDFLG(P1)>,FD.DEF)>,,1,
SW	MODE,<POINTR(<.FDMOD(P1)>,FD.MOD)>,F$KEY,MODPTR,SW.VRQ!SW.KEY
SW	NODEFAULT,<POINTR(<.FDFLG(P1)>,FD.NDF)>,,1,
SW	PROTECTION,<POINTR(<.FDPRO(P1)>,FD.PRO)>,F$PROT,,SW.VRQ
SW	VERSION,<POINTR(<.FDVER(P1)>,FD.VER)>,F$VERS,,SW.VRQ
> ; End of DEFSWT macro definition

	DOSWTCH(DEF,DEFSWT)	; Expand the table

; Expand the switch pointer macro

DEFPTR:	SWTPTR	(DEF)		; Make the argument block
	SUBTTL	F$PARS - Parse a file specificaton

;+
;.HL1 F$PARS
;This routine will parse a file specificaion, and store the results into
;an FD block.  It will also parse any file switches that are present on
;this file specification
;.literal
;
; Usage:
;	MOVEI	T1,FDB.address
;	MOVE	T2,Switch.block.pointer
;	PUSHJ	P,F$PARS
;	 (Failed)
;	(File spec parsed)
;.end literal
;-


F$PARS:	$SAVE	<P1,P2,P3>	; Save a few registers
	MOVEM	P,PRSSTK	; Store the pointer
	DMOVE	P1,T1		; Copy the FDB and switch block addresses
	MOVEM	P1,LASFDB	; Store the last FDB
	HRLI	T1,1(T1)	; Clear the FDB
	MOVSS	T1		; with a BLT
	SETZM	-1(T1)		; Clear the first word
	BLT	T1,.FDLEN-1(P1)	; . . .
	MOVEI	T1,[ERROR E.UFS] ; Get the error return
	PUSHJ	P,F$SETI	; Setup the input routine

; The following is the main loop for inputting items in the file specification

PARS.1:	PUSHJ	P,F$SXBT	; Get a sixbit item

	CAXE	CH,":"		; Delimiter a colon ?
	 JRST	PARS.2		; No - Try for the others
	PUSHJ	P,@RTN		; Get the next character
	CAXE	CH,":"		; Is this also a colon ?
	  JRST	PARS.0		; No - Must have a device name
	CFXE.	T2,FDBNOD,(P1),0 ; Have a node name already ?
	 $RETE	(DNN)		; ++ Double node name illegal
	STOR.	T1,FDBNOD,(P1)	; Store the node name
	JRST	PARS.1		; Get the next item
; Here to store a device name

PARS.0:	PUSHJ	P,F$REAT	; Reeat the character
	CFXE.	T2,FDBDEV,(P1),0 ; Have a node name already ?
	  $RETE	(DDN)		; ++ Double device name illegal
	STOR.	T1,FDBDEV,(P1)	; Store the device name
	JRST	PARS.1		; Loop

; Here if we have something as a delimiter besides a colon

PARS.2:	CAXE	CH,"."		; Extension coming next ?
	  JRST	PARS.4		; No - Check for path specification or switch
	JSP	T2,PARS.N	; Store the file name if present
	PUSHJ	P,F$SXBT	; Get the extension
	TXNE	T1,RH.ALF	; Make sure it is not too long
	 $RETE	(ELS)		; ++ Extension longer than six characters
	MOVX	T2,FD.HEX	; Get the extension flag
	TDNE	T2,.FDFLG(P1)	; Make sure we don't have one already
	 $RETE	(DEI)		; ++ Double extension not allowed
	IORM	T2,.FDFLG(P1)	; Light the flag
	STORS.	T1,FDBEXT,(P1)	; And store the extension
	PUSHJ	P,F$REAT	; Reeat the character
	JRST	PARS.1		; And continue

; Here to check to see if we have a path specification

PARS.4:	CAXE	CH,"["		; Start of the path specification ?
	  JRST	PARS.S		; No - Try for a switch

	JSP	T2,PARS.N	; Store the file name if we have one
	PUSHJ	P,.IOCTW	; Get an octal number
	JUMPN	T1,[CAXE CH,","		; Make sure a comma delimits
		$RETE	(IPS)		; ++ Illegal path specification
		JRST	PARS.6]		; Continue processint

	CAXE	CH,"-"		; Was the delimiter a dash ?
	  JRST	PARS.5		; No - Try for [,
	PUSHJ	P,@RTN		; Get the next character
	JUMPE	CH,PARS.3	; Allow [-$
	CAXE	CH,"]"		; Is it the closing bracket ?
	  $RETE	(IPS)		; ++ Illegal path specification
	JRST	PARS.3		; Finish up
; Here to see if the delimiter for the null project number was a comma

PARS.5:	CAXE	CH,","		; Is this corrrect
	  JRST	[PUSHJ	P,F$REAT	; Reeat the character
		PUSHJ	P,F$SXBT	; Try for a sixbit PPN
		JUMPN	T1,PARS.7	; If we have one jump
		$RETE	(IPS)]		; ++ Illegal path specification

	HLRZ	T1,MYPPN	; Get the default project number
PARS.6:	TXNE	T1,LH.ALF	; Make sure the number is good
	 $RETE	(IPS)		; ++ Illegal path specification
	HRLM	T1,.FDPPN(P1)	; Store in the PPN word
	PUSHJ	P,.IOCTW	; Get the second half
	SKIPN	T1		; Something input ?
	 HRRZ	T1,MYPPN	; No - Use the default
	TXNE	T1,LH.ALF	; Check for a right half value only
	 $RETE	(IPS)		; ++ Illegal path specification
	HRRM	T1,.FDPPN(P1)	; Store the programmer number
	SKIPA			; Skip and enter the SFD scanning

PARS.7:	STOR.	T1,FDBPPN,(P1)	; Store the PPN
	MOVSI	T3,-<.PTMAX-.PTSFD> ; Get minus the length
	HRRI	T3,.FDSFD(P1)	; Get the address

PARS.8:	CAXE	CH,","		; Is this the delimiter ?
	  JRST	PARS.9		; No - Check for a ']'
	PUSHJ	P,F$SXBT	; Get the sixbit SFD
	MOVEM	T1,(T3)		; Store the SFD
	JUMPE	T1,[$RETE (NSS)] ; ++ Null SFD specified
	AOBJN	T3,PARS.8	; Loop for all possible SFDs
	$RETE	(SNT)		; ++ SFDs nested too deeply

PARS.9:	JUMPE	CH,PARS.3	; If done - just exit
	CAXE	CH,"]"		; Is this the end of the path spec ?
	 $RETE	(IPS)		; ++ Illegal path specification
PARS.3:	BITON	T1,FD.PTH,.FDFLG(P1) ; Flag we have a path specified
	SETZ	T1,		; Flag we have no token
	JUMPE	CH,PARS.A	; If we got an altmode all is done
	JRST	PARS.1		; Yes - Continue

; Here to process a switch

PARS.S:	CAXE	CH,"/"		; Is this a switch delimiter ?
	  JRST	PARS.A		; Yes - Continue
	JSP	T2,PARS.N	; Store any file name
	MOVE	T2,(P2)		; Get the pointer
	MOVEM	T2,LASSWP	; Save the switch pointer
	PUSHJ	P,F$SXBT	; Get a sixbit token
	JUMPE	P2,PARS.C	; If no switch table skip this
	PUSHJ	P,PAR.SW	; Parse the switch
	  JRST	[JUMPE	T2,PARS.C	; Continue if unknown
		$RETE	(AMS)]		; ++ Ambig
	JRST	PARS.B		; Continue
PARS.C:	PUSH	P,P2		; Save P2
	MOVEI	P2,DEFPTR	; Get the pointer to the default switches
	PUSHJ	P,PAR.SW	; Parse the switch again
	  JRST	[POP	P,P2		; Restore P2
		SKIPE	T2		; If unknown skip
		 $RETE	(AMS)		; Return the error
		$RETE	(UNS)]		; Unknown switch
	POP	P,P2		; Restore the old pointer
PARS.B:	PUSHJ	P,F$REAT	; Reat the delimiter
	JRST	PARS.1		; Continue processing

; Here to do the main line processing of the switch

PAR.SW:	MOVEM	T1,LASSWT	; Save the last switch in case of error
	LOAD.	T2,SPBNAM,(P2)	; Get the pointer to the names
	PUSHJ	P,LOKNAM	; Look for the name in the table
	  POPJ	P,		; Return to the caller
	LOAD.	T1,SPBNAM,(P2)	; Get the pointer again
	SUBI	T2,(T1)		; Make this an index
	MOVE	P3,T2		; Copy it to a safe place
	LOAD.	T2,SPBRTN,(P2)	; Get the routine to call
	ADDI	T2,(P3)		; Point to the correct item
	MOVE	T2,(T2)		; Get the value
	TXNE	T2,SW.VRQ	; A value required ?
	  JRST	[CAXN	CH,":"		; Delimiter a colon ?
		  JRST	PARS.T		; Yes - Attempt to get the value
		MOVE	P,PRSSTK	; Restore the stack pointer
		$RETE	(SRV)]		; Issue the error
	CAXN	CH,":"		; Is this a value delimiter ?
	 TXNN	T2,LH.ALF	; Is there a routine address ?
	  JRST	PARS.V		; No value presetn -- keep on trucking

PARS.T:	MOVS	T2,T2		; Put the routine address in the correct half
	PUSHJ	P,(T2)		; Call the rotine
	JRST	PARS.U		; Continue

PARS.V:	LOAD.	T2,SPBVAL,(P2)	; Get the value to store
	ADDI	T2,(P3)		; Add in the offset
	MOVE	T1,(T2)		; Get the value to store

PARS.U:	LOAD.	T2,SPBPTR,(P2)	; Get the address f the pointer
	ADDI	T2,(P3)		; Point  the correct word
	MOVE	T2,(T2)		; Get the byte pointer
	TXNN	T2,LH.ALF	; Does this half a left half ?
	  JRST	[MOVEM	T1,(T2)		; Store the value
		JRST	F$GRET]		; Give a good return
	DPB	T1,T2		; Store the byte
F$GRET:	SETZM	LASFDB		; Clear the last FDB address since no errors
	JRST	.POPJ1		; And give a skip return


; Here to store a file name

PARS.N:	JUMPE	T1,(T2)		; Return if zero
	CFXE.	,FDBNAM,(P1),0	; Have a name already ?
	  $RETE	(DFN)		; ++ Double file name illegal
	STOR.	T1,FDBNAM,(P1)	; Store the file name
	JRST	(T2)		; Return
; Here to check for the end of the file specification

PARS.A:	SKIPE	CH		; End with an altmode ?
	 $RETE	(IVC)		; No - Invalid character
	JUMPE	T1,.+2		; Have a file name to store?
	 JSP	T2,PARS.N	; Yes, store it first
	STORE	T1,PTH,PTH+.PTMAX,0 ; Clear the PATH block
	LOAD.	T1,FDBDEV,(P1)	; Get the device
	JUMPE	T1,F$GRET	; Return if no device
	MOVEM	T1,PTH+.PTSTR	; Store in the PATH block
	MOVEI	T1,PTH		; Get the address of the block
	PATH.	T1,		; Get the path for the device
	 JRST	F$GRET		; Probably not a disk device
	LOAD	T1,PTH+.PTSWT,PT.IPP ; Implied PPN ?
	JMPF	T1,F$GRET	; Jump if false
	MOVX	T1,FD.PTH	; Have a path already ?
	TDNE	T1,.FDFLG(P1)	; Have it ?
	 $RETE	(DIP)		; ++ Device implies a PATH, but one was given
	PJRST	F$GRET		; All is okay, return
	SUBTTL	Switch processing -- F$SXBT - Get a sixbit item

;+
;.HL2 F$SXBT
;This routine will input a sixbit token.
;-

F$SXBT:	$SAVE	<T2,T3>		; Save T2 and T3
	SETZ	T1,		; Clear the accumulated number
				; Input a sixbit thingy
	MOVE	T2,[POINT 6,T1]	; Set up the byte pointer
	PUSHJ	P,GNBCHR	; Get the first non-blank character
	SKIPA			; And enter the routine

F$SX.0:	PUSHJ	P,@RTN		; Input an item
	MOVE	T3,CHRFLG(CH)	; Get the character flags
	TXNE	T3,CF.LC	; Is this lower case ?
	 ANDI	CH,137		; Yes - Convert to upper case
	TXNN	T3,CF.NUM!CF.ALP ; Alphabetic or numeric?
	 JRST	F$SX.1		; No - Clean up and return to the caller
	SUBI	CH," "-' '	; Convert to SIXBIT
	TXNN	T1,77		; Have the word filled ?
	 IDPB	CH,T2		; No - Store the character
	JRST	F$SX.0		; And loop for the next character

; Here to store the last sixbit item so that error processing will work

F$SX.1:	MOVEM	T1,LASSXB	; Store this as the last sixbit item
	POPJ	P,		; Return to the caller
	SUBTTL	Switch processing -- F$PROT - Input a file protection

;+
;.HL2 F$PROT
;This routine will input a file protection.
;-

F$PROT:	PUSHJ	P,.IOCTW	; Get the octal protection
	BITON	T2,FD.HPR,.FDFLG(P1) ; Light the flag
	CAIGE	T1,^O1000	; Is this too big ?
	 POPJ	P,		; No - Give a good return
	MOVE	P,PRSSTK	; Get the stack pointer
	$RETE	(PRO)		; Protection is bad



	SUBTTL	Switch processing -- F$KEY - Input a keyword

F$KEY:	PUSHJ	P,F$SXBT	; Get the sixbit item
	LOAD.	T2,SPBVAL,(P2)	; Get the pointer to the values
	ADDI	T2,(P3)		; Point to the correct entry
	MOVE	T2,(T2)		; Get the entry
	PUSH	P,T2		; Save the pointer
	PUSHJ	P,LOKNAM	; Look for the keyword
	  JRST	[MOVE	P,PRSSTK	; Get the stack to pointer
		SKIPE	T2		; Unknown ?
		$RETE	(AKW)		; Ambig keyword
		$RETE	(UKW)]		; Unknown keyword
	MOVEI	T1,(T2)		; Get the address it found the keyword at
	POP	P,T2		; Restore the pointer
	SUBI	T1,(T2)		; Make this an index
	POPJ	P,		; Return to the caller

	SUBTTL	Switch processing -- F$CORE - Input an amount of core

;+
;.HL2 F$CORE
; This routine will input an amount of core. The valid formats for
;the input are nP, where n is the number of pages, nK, where n is the
;number of kilo-words, nW where n is the number of words, nB, where
;n is the number of blocks, or n, where n is the number of K.
;-

F$CORE:	PUSHJ	P,.IDECW	; Get a decimal number
	MOVEI	T2,^D10		; Get the default amount to shift
	CAIL	CH,"a"		; Check for lower  case
	 CAILE	CH,"z"		;  .  .  .
	  SKIPA			; Upper case - skip
	SUBI	CH,"a"-"A"	; Convert to upper
	CAIN	CH,"P"		; Did he give a P?
	 MOVEI	T2,^D9		; Yes, shift by correct amount
	CAIN	CH,"W"		; Give number of words?
	 SETZ	T2,		; Yes, don't shift at all
	CAIN	CH,"B"		; Or was it the number of blocks
	 MOVEI	T2,^D7		; Yes, get the amount
	LSH	T1,(T2)		; Multiply by correct amount
	JUMPN	T2,@RTN		; Input the next character and return
	POPJ	P,		; And return the value
	SUBTTL	Switch processing -- F$VERS - Input a version number

;+
;.HL2 F$VERS
;This routine will input a version number.
;-

F$VERS:	STORE	T1,VERBEG,VEREND,0 ; Clear the storage
	PUSHJ	P,.IOCTW	; Input the first part of the version number
	CAXLE	T1,777		; Is this within range ?
	 JRST	VERS.0		; No - Error
	MOVEM	T1,VERMAJ	; Save it

VERS.2:	CAIN	CH,"("		; Have a minor version ?
	  JRST	VERS.1		; No - or done with it
	MOVEI	T1,^D26		; get the number of multiply by
	IMULM	T1,VERMIN	; . . .
	MOVE	T1,CHRFLG(CH)	; Get the flags
	TXNE	T1,CF.LC	; Is this lower case ?
	 ANDI	CH,137		; Convert to just upper case
	CAIG	CH,"Z"		; Is this a valid character
	 CAIGE	CH,"A"		; . . .

	  JRST	VERS.0		; No - Give an error return
	SUBI	CH,"A"-1	; Convert to an octal number
	ADDB	CH,VERMIN	; Update the version number
	CAXLE	CH,77		; Within reange still ?
	  JRST	VERS.0		; No - Give an error return
	PUSHJ	P,@RTN		; Get the next character
	JRST	VERS.2		; Try again for the minor version number

; Here for the edit level

VERS.1:	PUSHJ	P,.IOCTW	; Input the edit level
	TXNN	T1,LH.ALF	; Is this small enough ?
	CAIE	CH,")"		; Proper delimiter ?
	 JRST	VERS.0		; No - ERrror
	MOVEM	T1,VEREDT	; Save the edit level
	PUSHJ	P,@RTN		; Get the next character
	CAXE	CH,"-"		; Is this the start of a minor version ?
	 JRST	VERS.3		; No - Clean up
	PUSHJ	P,.IOCTW	; Input the who
	CAXLE	T1,7		; Better be less than
	  JRST	VERS.0		; Error
	MOVEM	T1,VERWHO	; Store it
VERS.3:	MOVE	T1,VERMAJ	; Get the major version number
	LSH	T1,<ALIGN. (VR.VER)> ; Align it
	MOVE	T2,VERMIN	; Get the minor version number
	STOR	T2,T1,VR.MIN	; Store the minor version number
	MOVE	T2,VEREDT	; Get the edit level
	STOR	T2,T1,VR.EDT	; Store the edit level
	MOVE	T2,VERWHO	; Get who last dited
	STOR	T2,T1,VR.WHO	; Store it
	POPJ	P,		; Return to the caller

; Here incase there was an error

VERS.0:	MOVE	P,PRSSTK	; Get the stack pointer
	$RETE	(IVN)		; ++ Invalid version number
; Helper routines

; Routine to eat to an altmode

EATALT:	$SAVE	<CH>		; Save CH
EATA.0:	JUMPE	CH,.POPJ	; Return if zero
	PUSHJ	P,@RTN		; Get the next character
	JRST	EATA.0		; Loop

; Routine to input a non-blank character

GNBCHR:	PUSHJ	P,@RTN		; Get a character
	CAXE	CH," "		; Is this a space
	 CAXN	CH,.CHTAB	; or a tab ?
	  JRST	GNBCHR		; Loop to get the next
	POPJ	P,		; Return

; Here to F$REAT the last character input

F$REAT:	$SAVE	<T1>		; Save T1
	MOVEI	T1,RSTRTN	; Get the next routine
	EXCH	T1,RTN		; Exchange
	MOVEM	T1,SAVRTN	; Save the old one
	POPJ	P,		; Return

RSTRTN:	$SAVE	<T1>		; Save T1
	MOVE	T1,SAVRTN	; Get the old routine address
	MOVEM	T1,RTN		; Restore it
	POPJ	P,		; Return to the caller
	SUBTTL	F$DFLT - Default a FDB from another

;+
;.HL1 F$DFLT
; This routine will default the items from one FDB to another.
;The updated FDBs are returned in the calling registers.
;.literal
;
; Usage:
;	MOVEI	T1,FDB.given
;	MOVEI	T2,FDB.to.default.from
;	PUSHJ	P,F$DFLT
;	(Return)
;.end literal
;-

DEFINE	DEFFDB(XXX,YYY),<
	LOAD.	T4,FDB'XXX',(T1)	;; Get the given value
	LSTOF.	X			;; Turn the listing off
	SKIPN	T4			;; Zero value
	  LOAD.	T4,FDB'XXX',(T2)	;; Get the default value
IFNB <YYY>,<TXNN T3,FD.'YYY>		;; If we should not store
	STOR.	T4,FDB'XXX',(T1)	;; Store the default value
	STOR.	T4,FDB'XXX',(T2)	;; Store the new default value
IFNB <YYY>,<
	MOVX	T4,FD.'YYY		;; Get the bit to store
	TDNE	T4,.FDFLG(T2)		;; Is this on ?
	 IORM	T4,.FDFLG(T1)		;; Light the flag
	TXNE	T3,FD.'YYY		;; Was it on already?
	 IORM	T4,.FDFLG(T2)		;; Yes, turn on in default block
>;; End of IFNB <YYY>
	LSTON.				;; Turn the listing back on
> ; End of DEFFDB macro definition

F$EBDF:	TXO	F,F.EB		; Flag doing an EB default
F$DFLT:	MOVSI	T4,(T3)		; Yes, set up the BLT pointer
	HRRI	T4,(T2)		;  .  .  .
	LOAD.	T3,FDBFLG,(T1)	; Get the flags for the routine
	TXNE	T3,FD.DEF	; /DEFAULT given?
	 BLT	T4,.FDLEN-1(T2)	; Yes, copy the permanent defaults
	TXNE	T3,FD.NDF	; No storing of defaults ?
	  POPJ	P,		; Yes, Just return
	DEFFDB	NOD		; Default the node name
	DEFFDB	DEV		; Default the device nmae
	DEFFDB	NAM		; Default the file name
	DEFFDB	EXT,HEX		; Default the extension
	DEFFDB	PRO,HPR		; Default the protection
	DEFFDB	MOD		; Default the mode
	DEFFDB	VER		; Default the version number

	TXZE	F,F.EB		; Doing EB default ?
	 POPJ	P,		; Yes, return now
	CFXN.	,FDBDEV,(T1),0	; Have a device ?
	 JRST	F$DF.1		; No, skip this
	STORE	T4,PTH,PTH+.PTMAX-1,0 ; Clear the path block
	LOAD.	T4,FDBDEV,(T1)	; Get the device name again
	MOVEM	T4,PTH+.PTSTR	; Store into the PATH block
	MOVE	T4,[XWD .PTMAX,PTH] ; Get the argument block pointer
	PATH.	T4,		; Do it
	 JRST	F$DF.1		; Failed, ignore it
	LOAD	T4,PTH+.PTSWT,PT.IPP ; Get the implied PPN
	JMPT	T4,.POPJ	; Return if true

F$DF.1:	MOVX	T3,FD.PTH	; Get the bit to check
	TDNN	T3,.FDFLG(T1)	; Specified for the file?
	  JRST	F$DF.2		; No, Check the default
	IORM	T3,.FDFLG(T2)	; Flag we now have a default
DEFINE	CPYFLD(LIST,FROM,TO)<
    IRP <LIST><
	LOAD.	T3,FDB'LIST',('FROM')	;; Get the item
	STOR.	T3,FDB'LIST',('TO')	;; Move it
    >;; End of IRP <LIST>
>; End of macro definition CPYFLD
	CPYFLD	(<PPN,SFD,SF2,SF3,SF4,SF5>,T1,T2)
	POPJ	P,		; Return to the caller with the FDBs updated

; Here to determine if the path was specified in the default, if so copy it
; to the file.

F$DF.2:	TDNN	T3,.FDFLG(T2)	; Specified in the default?
	  POPJ	P,		; No, Just return
	IORM	T3,.FDFLG(T1)	; Flag we have a directory
	CPYFLD	(<PPN,SFD,SF2,SF3,SF4,SF5>,T2,T1)
	POPJ	P,		; Return after the items are updated
	SUBTTL	F$EB - Do Edit backup function

;+
;.HL1 F$EB
;This routine will do the edit backup functions for the file processing.
;.literal
;
; Usage:
;	MOVEI	T1,FDB.address
;	PUSHJ	P,F$OPEN
;	 (failed)
;	(Won)
; Returns:
;	T1 - Temporary FDB (.TMP file)
;	T2 - Rename FDB
;.end literal
;-


F$EB:	$SAVE	<P1,P2,P3>	; Save a few registers
	MOVE	P1,T1		; Copy the FDB
	MOVEM	P1,LASFDB	; Store this
	LOAD.	T1,FDBEXT,(P1)	; Get the extension
	CAIN	T1,'BAK'	; Is this a BAK file ?
	 ERROR	E.EBF		; ++ Can not edit a backup file
	LOAD.	T1,FDBDEV,(P1)	; Get the device
	DEVCHR	T1,		; Get the device characteristics
	TXNN	T1,DV.DSK	; Is this a disk ?
	  JRST	[MOVX	T1,$FENAD	; Get the error code
		POPJ	P,]		; Return to the caller
; Here to open the input file

	MOVX	T1,$IOREA	; Get the function
	MOVE	T2,P1		; Get the FDB
	PUSHJ	P,F$OPEN	; Open the file for reading
	  POPJ	P,		; Failed ?
	MOVX	T1,.FDLEN	; Get the length
	MOVX	T2,.BTFDB	; And the block type
	PUSHJ	P,M$ZBLK	; Allocate a zero'ed block
	MOVE	P2,T1		; Copy the address
	MOVE	T2,P1		; Default from the source file
	PUSHJ	P,F$EBDF	;  .  .  .
	SETZ	P3,		; Clear for rename FDB
	SKIPGE	T1,SWTOMD	; Get the output mode
	 LOAD.	T1,FDBMOD,(P1)	; None given, use the same as input
	STOR.	T1,FDBMOD,(P2)	; Store the mode
	MOVE	T1,.FDFLG(P1)	; Check if /INP
	TXNE	T1,FD.INP	;  .  .  .
	  JRST	F$EB.I		; /INPLACE given, go handle it
	TXNN	T1,FD.NDP	; In default path?
	 JRST	F$EB.2		; Yes, go handle it
	ZERO.	,FDBPPN,(P2)	; Clear the PPN
	ZERO.	,FDBSFD,(P2)	; And the first SFD
	STORI.	<SIXBIT |DSK|>,T1,FDBDEV,(P2) ; Write into default search list
	ZERO.	,FDBNOD,(P2)	; On current node
	JRST	F$EB.0		; Go continue on

; Here for /INPLACE.  Copy all the information from the input file for the
;output file.

	DEFINE CPYFLD(NAME)<
IRP NAME,<	LOAD.	T1,FDB'NAME,(P1) ;; Get the field
	STOR.	T1,FDB'NAME,(P2) ;; Store it>>

F$EB.I:	CPYFLD(<NOD,DEV,NAM,EXT,PRO,VER,PPN,SFD,SF2,SF3,SF4,SF5>)
	BITON	T1,FD.PTH!FD.HEX,+.FDFLG(P2) ; Flag that we have a path and an extension
	SKIPA	T1,[EXP $IOWRI]	; Get the write, don't care if supercede function
	FALL	F$EB.0		; And continue on
; Here if the output is to be the same file name and extension as the input.
;For this case we do not need a rename FDB, nor do we need to flag this as
;an EB command opened file.

F$EB.0:	MOVX	T1,$IOWRS	; Get the write/no supercede
	MOVE	T2,P2		; And the FDB address
	PUSHJ	P,F$OPEN	; And open for output
	 JRST	EBFAIL		; Can't, give up
	MOVE	T1,P1		; Get the FDB address to lock up
	PUSHJ	P,F$ENQ		; ENQ. the file name
	 JRST	F$EB.9		; Couldn't, go give the error
	DMOVE	T1,P2		; Get the FDB address and the zero
	PJRST	F$GRET		; And return

F$EB.9:	MOVE	T1,P1		; Get the input file FDB
	PUSHJ	P,F$RSET	; And reset it
	MOVE	T1,P1		; Get it again
	PUSHJ	P,M$RBLK	; Return the block
	MOVEM	P2,LASFDB	; Save the block address
	MOVX	T1,$FEFAE	; Get the file already being edited code
	POPJ	P,		; And give the error return

F$EB.2:	MOVX	T1,.FDLEN	; Get the length
	MOVX	T2,.BTFDB	; And the block type
	PUSHJ	P,M$ZBLK	; Allocate a zero'ed block
	MOVE	P3,P2		; Save the other one
	MOVE	P2,T1		; And get the address of the new one
	LOAD.	T1,FDBMOD,(P3)	; Get the output mode
	STOR.	T1,FDBMOD,(P2)	; Save it for the .TMP file
	LOAD.	T1,FDBDEV,(P1)	; Get the device the file came from
	STOR.	T1,FDBDEV,(P2)	; And save it
	STORI.	<'TMP'>,T1,FDBEXT,(P2) ; With .TMP extension
	BITON	T1,FD.HEX,.FDFLG(P2) ; Flag that we have an extension
	LOAD.	T1,FDBCHN,(P1)	; Get the channel number
	LSHC	T1,-5		; Insert a bit in the middle
	LSH	T1,1		;  .  .  .
	LSHC	T1,5		;  .  .  .
	TXO	T1,'TAA'	; Make it readable
	HLL	T1,SIXJOB	; Get the sixbit job number
	STOR.	T1,FDBNAM,(P2)	; Save it
	MOVX	T1,$IOWRI	; Get the write function
	MOVE	T2,P2		; And the FDB address
	PUSHJ	P,F$OPEN	; And set up the output file
	 PJRST	EBFAIL		; Couldn't
	MOVE	T1,P3		; And check that we can handle the source file
	MOVE	T2,P3		; by renaming it to itself
	PUSHJ	P,F$RENM	;  . . .
	 PJRST	EBFAIL		; Couldn't
	STORI.	<'BAK'>,T1,FDBEXT,(P3) ; Change for .BAK file
	MOVE	T1,P3		; And check if we can rename that
	MOVE	T2,P3		;  .  .  .
	PUSHJ	P,F$RENM	;  .  .  .
	 JRST	[CAXE	T1,ERFNF%	; File not found?
		  JRST	EBFAIL		; No, give up
		JRST	.+1]		; Yes, all is okay
	LOAD.	T1,FDBEXT,(P1)	; Get the extension back
	STOR.	T1,FDBEXT,(P3)	; And reset the rename FDB
	MOVX	T1,FD.EB	; Get the flag to turn on
	IORM	T1,.FDFLG(P1)	; Flag it here
	IORM	T1,.FDFLG(P2)	; And here
	IORM	T1,.FDFLG(P3)	; And here that this FDB is for an EB'ed file
	MOVE	T1,P1		; Get the FDB address
	PUSHJ	P,F$ENQ		; And ENQ. it
	 JRST	[MOVX	T1,$FEFAE	; Get the error code
		PJRST	EBFAIL]		; And reset the world
	DMOVE	T1,P2		; Get the FDB addresses
	JRST	F$GRET		; And return happy

; Here if the open of the output file failed.  See if superceeding


	SUBTTL	EB subroutines -- EBFAIL

;+
;.HL2 EBFAIL
; Here if the EB function has failed.  This routine will clean up the random
;files that have been opened and return the blocks for all except the EB FDB.
;-

EBFAIL:	$SAVE	<T1>		; Save the error code
	MOVE	T1,LASFDB	; Get the last FDB
	LOAD.	T1,FDBERR,(T1)	; And get the error code from it
	STOR.	T1,FDBERR,(P1)	; Save it for later
	MOVEM	P1,LASFDB	; And reset the last FDB
	MOVE	T1,P1		; Reset the input file
	PUSHJ	P,F$RSET	; . . .
	SKIPE	T1,P3		; Copy this one
	 PUSHJ	P,F$RSET	; Kill off this channel
	SKIPE	T1,P3		; Get the block to return
	 PUSHJ	P,M$RBLK	; Return this block
	MOVE	T1,P2		; Get the other block to return
	PUSHJ	P,F$RSET	; Reset this channel if any
	MOVE	T1,P2		; Get it again
	PJRST	M$RBLK		; Return it if we can
	SUBTTL	F$ENQ - ENQ a file

;+
;.HL1 F$ENQ
; This routine will set the ENQ./DEQ. lock for the resource which is a file
;name.  The lock is set for the string which is the complete file specification.
;It takes as arguments the addresses of the open block and of the lookup/enter
;block.
;This routine has two returns.  The error (non-skip) return is taken if
;the lock cannot currently be granted.  The normal return is taken if the lock
;has been granted.
;.literal
;
; Usage:
;	MOVE	T1,FDB
;	PUSHJ	P,F$ENQ		; ENQ the file
;	(Failed)
;	(Good return)
;.end literal
;-

F$ENQ:	$SAVE	<P1>		; Save P1
	MOVE	P1,T1		; Copy the address of the FDB
	STORE	T1,ENQBEG,ENQEND,0 ; Clear the blocks
	MOVX	T1,<INSVL.(1,EQ.LNL)+INSVL.(.ENQPS+.ENQRI+2,EQ.LLB)>
	MOVEM	T1,.ENQLL+ENQHDR ; Save the lengths word
	LOAD.	T1,FDBCHN,(P1)	; Get the channel number
	MOVEM	T1,.ENQRI+ENQHDR ; Also save as the request ID
	TXO	T1,EQ.FBL	; Bypass level numbers
	MOVEM	T1,.ENQFL+ENQBLK ; Save it
	MOVE	T1,[POINT 7,STRING] ; Get the byte pointer
	MOVEM	T1,.ENQBP+ENQBLK ; Save it
	LOADS.	T4,FDBEXT,(P1)	; Get the extension for later
	MOVEI	T3,.FDPPN-.PTPPN(P1) ; Get the address of the path block
	MOVEI	T1,[$STRING(<^X/WRTCHR/^W/.FDDEV(P1)/:^W/.FDNAM(P1)/.^W/T4/^P/T3/^0>)]
	PUSHJ	P,T$TYPE	; Make the string to lock
	MOVE	T1,[POINT 7,STRING] ; Get the byte pointer back
	MOVEM	T1,.ENQBP+ENQBLK ; And save it for the monitor

	MOVE	T1,[XWD .ENQAA,ENQHDR] ; Get the pointer
	ENQ.	T1,		; And try for the lock
	  JRST	ENQFAI		; ENQ. failed - see why
	BITON	T1,FD.ENQ,.FDFLG(P1) ; Light the flag
	JRST	.POPJ1		; Give a good return

; Here if the ENQ. failed

ENQFAI:
IFN FTDEBUG,WARN E.EQF		; ENQ. failed
	CAXE	T1,ENQRU%	; Is it currently locked ?
	 CAXN	T1,ENQDR%	; Duplicate request? (we must be already editing it)
	  POPJ	P,		; Give an FAE message
	PJRST	.POPJ1		; Some other error, ignore it
	SUBTTL	F$DEQ - DEQ a file

;+
;.hl1 F$DEQ
; This routine will release all current locks.
;-

F$DEQ:	LOAD.	T2,FDBCHN,(T1)	; Get the channel number
	HRLI	T2,.DEQID	; Get the function
	DEQ.	T2,		; And DEQ. the file
	 JFCL			; Can't do much
	BITOFF	T2,FD.ENQ,.FDFLG(T1) ; Turn off the bit
	POPJ	P,		; Return
	SUBTTL	ENQ/DEQ Subroutines -- WRTCHR - Write a character

WRTCHR:	IDPB	CH,.ENQBP+ENQBLK ; Store the character
	POPJ	P,		; And return
	SUBTTL	F$OPEN - Open a file

;+
;.HL1 F$OPEN
;This routine will open a file for I/O.  It is called with the function
;in T1.
;.literal
;
; Usage:
;	MOVEI	T1,I/O function
;	MOVEI	T2,FD.block	; File descriptor
;	PUSHJ	P,F$OPEN	; Open the file
;	 (Failed)		
;	(Won)
;
; Returns:
;	On error: T1 contains the file system error code
;	Good return: T1 contains the address of a file access block
;.end literal
;-

F$OPEN:	$SAVE	<P1,P2,P3>	; Save P1 and P2
	MOVEM	T2,LASFDB	; Store the last FDB
	DMOVE	P1,T1		; Copy the arguments

OPEN.0:	STORE	T1,OPNBEG,OPNEND,0 ; Clear the OPEN area
	LOAD.	T2,FDBDEV,(P2)	; Get the device name
	JUMPE	T2,[MOVX T1,$FENXD	; Get the error code to return
		POPJ	P,]		; And return it
	LOAD.	T1,FDBNOD,(P2)	; Get the node name
	JUMPE	T1,OPEN.A	; Have the name?
	CAMN	T1,MYNODE	; Are we here?
	 JRST	OPEN.A		; All is okay
	MOVX	T1,$FENNS	; no network support yet
	POPJ	P,		; Return the error
OPEN.A:	LOAD.	T1,FDBMOD,(P2)	; Get the mode
	CAXE	T2,<SIXBIT |TTY|> ; Is this the terminal?
	 JRST	OPEN.D		; No, skip this
	CAXE	T1,$FMTRM	; Terminal mode?
	 CAXN	T1,$FMPIM	; Packed image mode ?
	  SKIPA			; Yes keep going
	   JRST	OPEN.D		; No, skip this
	MOVE	T3,T2		; Yes, get the name
	DEVCHR	T3,		; And get the DEVCHR bits
	MOVX	T4,.FALSE	; Get false
	TXNN	T3,DV.TTA	; Is this terminal controlling a job?
	 MOVX	T4,.TRUE	; No, get the value to store in the flag
	STOR	T4,.FDFLG(P2),FD.NTT ; Flag this isn't the controlling terminal
	MOVE	T1,IOMTBL(T1)	; Get the correct I/O mode
	JMPNS	OPEN.F		; Screen mode?
	TXOA	T1,IO.SUP	; Yes, flag we want echoing suppressed
OPEN.D:  MOVE	T1,IOMTBL(T1)	; Get the monitor I/O mode
OPEN.F:	DEVCHR	T2,		; Get the device characteristics
	JUMPE	T2,OPEN.G	; Does it exist?
	LOAD	T3,T1,IO.MOD	; Get the mode
	MOVX	T4,DV.M0	; Get the mode 0 bit
	LSH	T4,(T3)		; And shift to correct place
	TDNN	T2,T4		; Is this mode legal?
	 JRST	[MOVX	T1,$FEIMD	; No, get the error
		POPJ	P,]		; And return
	CAXLE	T3,.IOBIN	; Is this a binary mode?
	 TXNE	T2,DV.DSK	; Yes, must be a DSK device or we can't hack it
	  JRST	.+2		; Allow it
	   JRST	[MOVX T1,$FEIMD		; No, get the error
		POPJ	P,]		; And return
	MOVEI	T3,T1		; Get the address of the argument block
	LOAD.	T2,FDBDEV,(P2)	; Get the device name back
	DEVSIZ	T3,		; Determine the size of the buffers
	 JRST	.+2		; Skip if an error occured

	JUMPGE	T3,OPEN.1	; If this was not an error - Jump
	CAXE	T3,DVSNX%	; No such device?
	 STOPCD	(UDE,<Unknown DEVSIZ error>)

OPEN.G:	LOAD.	T2,FDBDEV,(P2)	; Get the device name back
	CAXE	T2,<SIXBIT /TMP/> ; Is this the temp device ?
	 JRST	[MOVX	T1,$FENXD	; Get the error code
		POPJ	P,]		; Give an error return
	CFXE.	T1,FDBEXT,(P2),0 ; Make sure this is a good TMP: file spec
	 JRST	[MOVX	T1,$FEITS	; ++ Illegal TMP: file specification
		POPJ	P,]		; Return

	LOAD.	T1,FDBNAM,(P2)	; Get the file name
	TXNE	T1,RH.ALF	; Check to make sure only left half
	 JRST	[MOVX	T1,$FEITS	; ++ Illegal TMP: file specification
		POPJ	P,]		; Return
	SETZ	T2,		; Clear the next word
	JRST	@TMPTBL(P1)	; Jump and do the function
; The following is the dispatch table for the TMP: functions

TABDEF	TMP,$IO,TMPILL		; Generate the table
 TABENT	DEL,TMPDEL		; Delete a file
 TABENT	WRI,TMPWRI		; Write a file
 TABENT	WRS,TMPWRS		; Write and no-super
 TABENT REA,TMPREA		; Read a file
TABEND				; End of the dispatch table


; Here for an illegal TMP: function

TMPILL:	MOVX	T1,$FEITF	; ++ Illegal TMP: function
	POPJ	P,		; Give an error return

; Here to do a delete of a TMP: file

TMPDEL:	MOVE	T3,[XWD .TCRDF,T1] ; Set up the argument pointer
	TMPCOR	T3,		; Do it
	  JRST	TMPDSK		; Failed - Try disk
	JRST	F$GRET		; Give a good return

; Here to write with no super

TMPWRI:
TMPWRS:	MOVE	T3,[XWD .TCRRF,T1] ; Get the function argument pointer
	TMPCOR	T3,		; Is this file there ?
	 JRST	TMPW.1		; No, continue on
	CAXN	P1,$IOWRS	; Do we need the message?
	 WARN	E.SUP		; Superceeding existing file

	MOVX	T2,<XWD .TCRFS,0> ; Get the amount left free
	TMPCOR	T2,		;  .  .  .
	 SETZ	T2,		; Assume none
	ADD	T3,T2		; Get the total length

TMPW.1:	JUMPN	T3,TMPW.2	; Have any to write into?
	LOADS.	T1,FDBNAM,(P2)	; No, get the file name
	HLL	T1,SIXJOB	; And make the nnnFOO
	STOR.	T1,FDBNAM,(P2)	; Store the name
	MOVX	T1,'TMP'	; Get the extension
	STOR.	T1,FDBEXT,(P2)	; Store it
	MOVX	T1,<SIXBIT |DSK|> ; Get the device
	STOR.	T1,FDBDEV,(P2)	; Store it
	BITON	T1,FD.TMD!FD.HEX,.FDFLG(P2) ; Flag this is a TMP file on disk
	JRST	OPEN.0		; And go try to write on disk

TMPW.2:	MOVE	T1,T3		; Get the number of words we can use
	MOVX	T2,.BTBUF	; Make it a buffer
	PUSH	P,T3		; Save the size
	PUSHJ	P,M$ZBLK	; Get a block
	POP	P,T3		; Get the size back
	STOR.	T1,FDBBUF,(P2)	; Store the buffer address
	HRLI	T1,(POINT 7,)	; Get the byte pointer
	MOVEM	T1,.FDBRH+.BFPTR(P2) ; Store it
	MOVN	T2,T3		; Get the number of words
	HRLI	T1,(T2)		; Make an IOWD
	SOJ	T1,		;  .  .  .
	MOVEM	T1,.FDBRH+.BFADR(P2) ; Store for later
	IMULI	T3,5		; Make the number of chars we can fit
	MOVEM	T3,.FDBRH+.BFCTR(P2) ; Store it
	BITON	T1,FD.TMP!FD.OPN!FD.OUT,.FDFLG(P2) ; Flag this is to go to TMPCOR
	PJRST	F$GRET		; Give the good return
; Here to read a TMP: file

TMPREA:	MOVE	T3,[XWD .TCRRF,T1] ; Make sure the file is around
	TMPCOR	T3,		; . . .
	  JRST	TMPDSK		; Failed - Try disk

	PUSH	P,T3		; Save the number of words
	MOVE	T1,(P)		; Get the number of words back again
	MOVX	T2,.BTBUF	; Allocate a buffer
	PUSHJ	P,M$GBLK	; From memory management
	STOR.	T1,FDBBUF,(P2)	; Store this
	HRLI	T1,(POINT 7)	; Build a byte pointer
	STOR.	T1,FDBPTR,(P2)	; Store in the buffer header
	POP	P,T2		; Get the number of words back again
	MOVEI	T3,5		; Get the number of characters per word
	IMUL	T3,T2		; Comput the character count
	STOR.	T3,FDBCTR,(P2)	; Store in the file access block
	STOR.	T3,FDBSIZ,(P2)	; Store as the file size too
	LOAD.	T4,FDBMOD,(P2)	; Get the mode of the file
	CAXN	T4,$FMDMP	; /MODE:DUMP?
	 STOR.	T2,FDBSIZ,(P2)	; For DUMP mode we need the word count
	MOVN	T2,T2		; Build the IOWD for reading TMP:
	HRLZ	T2,T2		; . . .
	HRRI	T2,-1(T1)	; . . .
	LOAD.	T1,FDBNAM,(P2)	; Get the name back again
	MOVE	T3,[XWD .TCRRF,T1] ; Get the function and argument pointer
	TMPCOR	T3,		; Get the file
	 STOPCD	(TFD,<TMP: file disappeared>)
	BITON	T1,FD.OPN!FD.TMP!FD.IN,.FDFLG(P2) ; Light a few flags
	MOVE	T1,P2		; Return the FDB
	JRST	F$GRET		; Return to the caller
; Here to try the disk for a TMP: file

TMPDSK:	MOVX	T1,<SIXBIT /DSK/> ; Get the device name
	STOR.	T1,FDBDEV,(P2)	; Store in the FDB
	LOADS.	T1,FDBNAM,(P2)	; Get the file name again
	HLL	T1,SIXJOB	; Get the sixbit job number
	STOR.	T1,FDBNAM,(P2)	; Store it back
	MOVX	T1,'TMP'	; Get the extension
	STOR.	T1,FDBEXT,(P2)	; Store it
	BITON	T1,FD.HEX,.FDFLG(P2) ; Flag we have an extension
	JRST	OPEN.0		; Try again from the top
; Here for disk or other type files

OPEN.1:	MOVEM	T1,FLP+.FOIOS	; Save the I/O status
	MOVEM	T2,FLP+.FODEV	; Save the device name
	PUSH	P,T3		; Save T3
	MOVE	T1,FLPTBL(P1)	; Get the FILOP. function
	MOVEM	T1,FLP+.FOFNC	; Store it
	EXCH	P1,P2		; Put this in the correct place
	MOVEI	T3,ELB		; Get the LOOKUP/ENTER block address
	MOVEI	T4,PTH		; Get the path block address
	PUSHJ	P,RENSUB	; Call the subroutine to move things
	EXCH	P1,P2		; Put the things back into the correct places
	MOVEI	T1,ELB		; Get the LOOKUP/ENTER block address
	MOVEM	T1,FLP+.FOLEB	; And store item
	MOVX	T1,<XWD .PTMAX,FPTH> ; Get the pointer to the found path area
	MOVEM	T1,FLP+.FOPAT	; Store it
	POP	P,T3		; Restore the buffer size and number
	PUSHJ	P,@PRETBL(P1)	; Do the preprocessing
OPEN.2:	MOVE	T1,[XWD .FOLEN,FLP] ; Get the argument block pointer
	MOVE	T2,ELB+.RBPPN	; Get the PPN incase of ERAEF% and $IOWRS
	FILOP.	T1,		; Do the FILOP.
	  JRST	OPENER		; Failed - See why
	PUSHJ	P,@POSTBL(P1)	; Do the post processing
	JRST	F$GRET		; Return to the caller

; Here if the FILOP. failed

OPENER:	CAXE	T1,ERAEF%	; Already existing file error?
	 JRST	OPNE.1		; No, go handle it
	CAXE	P1,$IOWRS	; Yes, write/no super function?
	 JRST	OPNE.1		; No, give up
	MOVX	T1,.FOWRT	; Yes, get the other function
	STOR	T1,FLP+.FOFNC,FO.FNC ; Store it in
	BITON	T1,FO.PRV,FLP+.FOFNC ; Flag we want to use any privs we have
	MOVEM	T2,ELB+.RBPPN	; Store the PPN
	MOVE	T1,OJBFF	; Get the old .JBFF
	MOVEM	T1,.JBFF	; And reset it over the warning
	WARN	E.SUP		; Give superceding existing file message
	LOAD.	T1,FDBBUF,(P2)	; Get the buffer address
	JUMPE	T1,OPEN.2	; Have a real buffer?
	MOVEM	T1,.JBFF	; And reset for the monitor
	JRST	OPEN.2		; And go write it

OPNE.1:	PUSH	P,T1		; Save T1
	PUSHJ	P,@ERRTBL(P1)	; Do any error processing
	JRST	.T1PJ		; Restore T1 and return

	; Routines to do preprocessing be for a FILOP.

; PREREA - Before a READ function

PREREA:	TDZA	T1,T1		; Flag reading a file

; PREAPP - Before an APPEND function
; PREWRI - Write a file
; PREWRS - Write a file (Create a new file)

PREAPP:				; Same as PREWRI
PREWRS:				; Same as PREWRI
PREWRI:	SETO	T1,		; Flag writing

; The following routine will allocate buffers for the files, it will
; allocate and store the FDB and buffer headeres

	PUSH	P,T1		; Save the flag
	JUMPE	T3,[MOVE T1,.JBFF	; If dump mode, just get .JBFF
		ZERO.	T2,FDBBUF,(P2)	; Clear the buffer address
		JRST	PRER.1]		; And go remember it
	HLRZ	T1,T3		; Get the number of buffers
	MOVE	T2,FLP+.FODEV	; Get the device name
	DEVCHR	T2,		; Get the device characteristics
	CAXGE	T1,D.NBUF	; Default number larger?
	 TXNN	T2,DV.DSK	; Is this a disk ?
	  JRST	.+2		; Skip
	   MOVX	T1,D.NBUF	; Yes, use the default number of buffers
	CFXN.	T2,FDBMOD,(P2),$FMERR ; Is this error mode?
	 MOVEI	T1,1		; Yes, use only one buffer
	SKIPE	(P)		; Reading or writing
	 MOVSS	T1		; Output - Move the number into the other half
	MOVEM	T1,FLP+.FONBF	; Store the number of buffers
	IMULI	T1,(T3)		; Calculate the number of words for the buffers
	SKIPE	(P)		; Is this output ?
	 MOVSS	T1		; Yes - Move to the other half
	MOVE	T3,T1		; Move into a safer place
	MOVEI	T2,.FDBRH(P2)	; Get the address of the buffer header
	SKIPE	(P)		; Output?
	 MOVS	T2,T2		; Yes, switch halves
	MOVEM	T2,FLP+.FOBRH	; Store in the FILOP. block
	MOVE	T1,T3		; Get the number of words for the buffers
	MOVX	T2,.BTBUF	; Flag getting buffers
	PUSHJ	P,M$GBLK	; Get a block from memory management
	STOR.	T1,FDBBUF,(P2)	; Store the buffer address
	EXCH	T1,.JBFF	; Make .JBFF point to it
PRER.1:	MOVEM	T1,OJBFF	; Save so we can restore .JBFF
	MOVX	T2,FD.IN	; Flag that this is input or output
	SKIPE	(P)		; Output ?
	 MOVX	T2,FD.OUT	; Yes - Get the other flag
	IORM	T2,.FDFLG(P2)	; Light the flag
	POP	P,(P)		; Pop off the flag
	POPJ	P,		; Return to the caller

; PREDEL - Preprocessing for a DELETE

PREDEL:	MOVEI	T1,REN		; Get the address of the rename block
	HRLM	T1,FLP+.FOLEB	; Store it
	MOVX	T1,.RBLEN	; Get the lenth of a RIB block
	MOVEM	T1,REN+.RBCNT	; Store it
	POPJ	P,		; Return to the caller
	; Here to do the post processing

; POSREA - After doing a read function
; POSAPP - After doing an append function
; POSWRI - After doing a write
; POSWRS - After doing a non-super write
;
; All these routines are the same.  They just flag that the channel is open
; and then store the channel allocated in the FDB and restore .JBFF

POSREA:
	MOVE	T1,OJBFF	; Restore .JBFF
	MOVEM	T1,.JBFF	; Must be done before CHKFFI is called
	SETZM	OJBFF		; Clear so we don't smash it later
	PUSHJ	P,CHKFFI	; Check if we need file found in message
	LOAD.	T1,FDBMOD,(P2)	; Get the mode
	SKIPN	T2,.RBSIZ+ELB	; Get the file size
	 SKIPE	.RBALC+ELB	; No file size, is this really something we have it with?
	  JRST	.+2		; Yes, all is well
	   MOVX	T2,.INFIN	; No, assume very big file
	JFCL	17,.+1		; Clear the flags
	MOVX	T3,^D36		; Get the size of a word
	IDIV	T3,BYTPTBL(T1)	; Get the number of bytes/word
	IMUL	T2,T3		; Get the total number of bytes
	JOV	[MOVX T2,.INFIN		; If it overflows, use the max
		JRST	.+1]		; And continue
	STOR.	T2,FDBSIZ,(P2)	; Remember the size
	CAXE	T1,$FMDFT	; Ascii mode ?
	  JRST	POSA.0		; No, continue
	LOAD	T1,FLP+.FOFNC,FO.CHN ; Get the channel number
	STOR.	T1,FDBCHN,(P2)	; Store in the FDB block
	MOVE	T1,P2		; Get the FDB address
	PUSHJ	P,F$IBUF	; Input the first buffer
	 POPJ	P,		; Couldn't, return the error
	LOAD.	T1,FDBBRH,(P2)	; Get the address of the buffers
	MOVE	T1,.BFCNT-1+1(T1) ; Get the first word of data
				; -1 because the symbols are defined silly
				; +1 because the data is the first word after
				;    the count
	TXNN	T1,1B35		; Is this an LSA file ?
	 JRST	POSA.0		; No, skip this
	MOVX	T1,$FMLSA	; Turn this into an LSA file
	STOR.	T1,FDBMOD,(P2)	; Store it back into the FDB
POSA.0:

; Here to do the post processing for write and write/no superceeding

POSAPP:
POSWRI:
POSWRS:
	LOAD.	T1,FDBMOD,(P2)	; Get the file mode
	MOVE	T2,BYTPTBL(T1)	; And get the correct byte pointer
	STOR	T2,.FDBRH+.BFPTR(P2),BP.SFL ; Store the byte size
	CAXE	T1,$FMLSA	; LSA file?
	 TDZA	T1,T1		; Clear the ac
	  MOVX	T1,<ASCII |00000|>+1 ; Get the initial LSN
	STOR.	T1,FDBLSN,(P2)	; Store it
	LOAD	T1,FLP+.FOFNC,FO.CHN ; Get the channel number
	STOR.	T1,FDBCHN,(P2)	; Store in the FDB block
	BITON	T1,FD.NLS!FD.OPN,.FDFLG(P2) ; Flag that the channel is open
	MOVE	T1,ELB+.RBVER	; Get the version number of the file
	CFXN.	,FDBVER,(P2),0	; Is this zero ?
	 STOR.	T1,FDBVER,(P2)	; Yes - Store the new version number
	LOAD	T1,ELB+.RBPRV,RB.PRV ; Get the priv word
	CFXN.	T2,FDBPRO,(P2),0 ; Do we have one already ?
	 STOR.	T1,FDBPRO,(P2)	; No - Store the new version
	SKIPE	T1,OJBFF	; Restore .JBFF
	 MOVEM	T1,.JBFF	; . . .
	POPJ	P,		; Return


; POSDEL - After delete processing

POSDEL:	LOAD	T1,FLP+.FOFNC,FO.CHN ; Get the channel number
	RESDV.	T1,		; And get rid of the channel
	 JFCL			; Ignore the error
	POPJ	P,		; Return
	; Here to do the error post processing

; ERRREA - After doing a read function
; ERRAPP - After doing an append function
; ERRWRI - After doing a write function
; ERRWRS - After doing a create new file function

; All of these routines are the same.  They restore .JBFF, call memory managment
; to dealocate the buffer and the FDB

ERRREA:ERRAPP:ERRWRI:ERRWRS:
	MOVE	T2,OJBFF	; Get the old value for .JBFF
	MOVEM	T2,.JBFF	; Restore it
	LOAD.	T1,FDBBUF,(P2)	; Get the address of the buffer
	JUMPE	T1,.+2		; Have something to return?
	 PUSHJ	P,M$RBLK	; Return the block to free core
	ZERO.	,FDBBUF,(P2)	; Clear the buffer address
	FALL	ERRDEL		; And join delete processing

; ERRDEL - Error processing for delete function

ERRDEL:	LOAD	T1,FLP+.FOFNC,FO.CHN ; Get the channel
	STOR.	T1,FDBCHN,(P2)	; And store it in case it is still open
	POPJ	P,		; Return

TABDEF	PRE,$IO,0		; Table for the preprocessing
	TABENT	DEL,PREDEL		; Delete preprocessing
	TABENT	REA,PREREA		; Read preprocessing
	TABENT	WRI,PREWRI		; Write preprocessing
	TABENT	WRS,PREWRS		; Write no-super preprocessing
	TABENT	APP,PREAPP		; Append preprocessing
TABEND

; Post processing table

TABDEF	POS,$IO,0
	TABENT	DEL,POSDEL		; Delete post processing
	TABENT	REA,POSREA		; Read post processing
	TABENT	WRI,POSWRI		; Write post processing
	TABENT	WRS,POSWRS		; Write no-super post processing
	TABENT	APP,POSAPP		; Append post processing
TABEND

; I/O mode table

TABDEF	IOM,$FM,0
	TABENT	TRM,.IOASC!IO.LEM	; Terminal
	TABENT	PIM,.IOPIM		; Packed image mode
	TABENT	LSA,.IOASC		; LSA mode ==> ascii
	TABENT	ASC,.IOASC		; Ascii mode ==> ascii
	TABENT	DFT,.IOASC		; Default is ascii
	TABENT	SXB,.IOBIN		; Sixbit mode ==> binary
	TABENT	BIN,.IOBIN		; Binary mode ==> binary
	TABENT	ERR,.IOASC		; Error file mode ==> ASCII
	TABENT	DMP,.IODMP		; Dump mode ==> dump
TABEND

; Table of byte pointers

DEFINE FK(A,B,SIZ)<EXP SIZ>
BYTPTBL:
	FM$KEY			; Generate the table entries

; Error processing table

TABDEF	ERR,$IO,0
	TABENT	DEL,ERRDEL		; Delete error processing
	TABENT	APP,ERRAPP		; Append error processing
	TABENT	REA,ERRREA		; Read error processing
	TABENT	WRI,ERRWRI		; Write error processing
	TABENT	WRS,ERRWRS		; Write no-super error processing
TABEND


; Table of FILOP. functons to do

TABDEF	FLP,$IO,0
	TABENT	APP,.FOAPP!FO.ASC!FO.PRV ; Append to a file
	TABENT	REA,.FORED!FO.ASC!FO.PRV ; Read a file
	TABENT	WRI,.FOWRT!FO.ASC!FO.PRV ; Write a file
	TABENT	WRS,.FOCRE!FO.ASC!FO.PRV ; Create a new file
	TABENT	DEL,.FODLT!FO.ASC!FO.PRV ; Delete a file
TABEND
	SUBTTL	Byte mode routines -- F$WRIT - Output a byte

;+
;.HL1 F$WRIT
; This routine is called to output a byte to a file.  This routine will
;the routine that must be called.  It will then call the mode dependent routine
;to do the output.
;.literal
;
; Usage:
;	MOVEI	T1,FDB
;	MOVEI	CH,Byte
;	PUSHJ	P,F$WRITE
;	 (Failed)
;	(Good return)
;.end literal
;-

F$WRIT:	MOVEM	T1,LASFDB	; Store the last FDB
	$SAVE	<T2,T3,T4>	; Save a few registers
	LOAD.	T2,FDBMOD,(T1)	; Get the mode
	PUSHJ	P,@OBYTBL(T2)	; Call the routine to output the byte
	 POPJ	P,		; Return the error
	PJRST	F$GRET		; Give the good return

; Dispatch table

TABDEF	OBY,$FM,WRTERR
 TABENT	PIM,F$OBYT		; Packed image mode
 TABENT	BIN,F$OBYT		; Binary mode
 TABENT	TRM,F$OBYT		; Terminal
 TABENT	ASC,F$OBYT		; ASCII mode
 TABENT	DFT,F$OBYT		; ASCII mode
 TABENT	SXB,O$SXBT		; SIXBIT mode
 TABENT	LSA,O$LSA		; LSA mode
 TABENT	DMP,DMPERR		; DUMP mode
TABEND

WRTERR:	STOPCD	WRT,<Illegal F$WRIT mode>

DMPERR:	MOVX	T1,$FEIOD	; Get the error code
	POPJ	P,		; And return
	SUBTTL	Byte mode routines -- F$READ - Read a byte

;+
;.HL1 F$READ
; This routine is called to read a byte from the input file.  This routine will
;call the mode dependent routine to input the byte.
;.literal
;
; Usage:
;	MOVEI	T1,FDB		; FDB to use
;	PUSHJ	P,F$READ	; Input a byte
;	 (Failed)
;	(Good return CH contains the character)
;.end literal
;-

F$READ:	MOVEM	T1,LASFDB	; Store the last FDB
	LOAD	T2,.FDFLG(T1),FD.EOF ; Check if already at EOF
	JMPT	T2,[MOVX T1,$FEEOF	; Yes, get the error code
		PJRST	F$RE.1]		; And exit
F$RE.0:	LOAD.	T2,FDBMOD,(T1)	; Get the mode
	PUSHJ	P,@IBYTBL(T2)	; Call the routine that does the work
	 JRST	F$RE.1		; Check if end of file
	JUMPE	CH,F$RE.2	; If null check further
	CAXN	CH,.CHFFD	; Is this a form
	 INCR.	,FDBFFC,(T1)	; Increment the form feed count
	JRST	F$GRET		; Give a good return to the caller

F$RE.1:	CAXN	T1,$FEEOF	; End of file?
	 SETZM	LASFDB		; Yes, clear the loc so channel stays open
	POPJ	P,		; And give the error return

F$RE.2:	LOAD.	T2,FDBMOD,(T1)	; Get the mode again
	CAXN	T2,$FMERR	; Skip this if the error file
	 JRST	F$GRET		; Give a good return
	JRST	F$RE.0		; Get the next byte

; Dispatch table

TABDEF	IBY,$FM
 TABENT	DMP,DMPERR		; Dump mode
 TABENT	PIM,F$IBYT		; Packed image mode
 TABENT	ERR,F$IBYT		; Error file
 TABENT	TRM,F$IBYT		; Terminal mode
 TABENT	ASC,F$IBYT		; ASCII mode
 TABENT	DFT,F$IBYT		; ASCII mode
 TABENT	SXB,I$SXBT		; SIXBIT mode
 TABENT	LSA,I$LSA		; LSA mode
TABEND
	SUBTTL	Byte mode routines -- Sixbit input/output

; The following are used for the conversion of bytes from sixbit to ascii

I$SXBT:	SOSL	T2,.FDRCN(T1)	; COunt down the number of chars in this record
	 JRST	ISXB.1		; Still more to go
	MOVX	T3,FD.NLS	; Get the intial call flag
	TDNE	T3,.FDFLG(T1)	; Is this the first call
	 JRST	[ANDCAM	T3,.FDFLG(T1)	; Yes, clear the flag
		JRST	ISXB.0]		; And get the first record count
	CAXN	T2,-1		; This the first time the count ran out?
	 MOVX	CH,.CHCRT	; Yes, get a carriage return
	CAXN	T2,-2		; Or is this the second time?
	 MOVX	CH,.CHLFD	; Yes, this should be a line feed
	CAXLE	T2,-3		; Time to get the next record?
	 PJRST	.POPJ1		; No, return the character

ISXB.0:	SKIPN	T2,.FDBRH+.BFCTR(T1) ; Get the byte count left
	 JRST	ISXB.2		; None left in buffer, go get a new buffer full
	IDIVI	T2,6		; And get the byte/word count
	JUMPE	T3,ISXB.3	; If on a word boundary already, go get the new count
	JUMPE	T2,ISXB.2	; If no full words left, get next buffer
	MOVN	T3,T3		; Otherwise fix up the byte count
	ADDM	T3,.FDBRH+.BFCTR(T1) ;  .  .  .
	MOVX	T2,(POINT 6,,0)	; Fix the byte pointer
	HLLM	T2,.FDBRH+.BFPTR(T1) ;  .  .  .
	JRST	ISXB.3		; And go get the word

ISXB.2:	PUSHJ	P,F$IBUF	; Get a buffer full
	 POPJ	P,		; Couldn't, return now

ISXB.3:	AOS	T2,.FDBRH+.BFPTR(T1) ; Get the byte pointer
	LOAD	T3,T2,BP.PFL	; Get the position field
	CAXL	T3,^D36		; Before the start of the word?
	 SOJ	T2,		; Yes, back up one
	MOVE	T2,(T2)		; Get the record count
	MOVEM	T2,.FDRCN(T1)	; Save the record count
	MOVX	T2,-6		; Get the amount to fix the pointer
	ADDM	T2,.FDBRH+.BFCTR(T1) ; And fix the counter
	JRST	I$SXBT		; And go get a byte

; Here to get a character from the file and convert it to ascii

ISXB.1:	PUSHJ	P,F$IBYT	; Input a byte
	  POPJ	P,		; Pass errors on
	MOVEI	CH,"0"-'0'(CH)	; Convert to ascii
	PJRST	.POPJ1		; Give a skip return
O$SXBT:	SKIPE	T2,.FDRCN(T1)	; Have the text buffer for building the record yet?
	 JRST	OSXB.1		; Yes, go store the character
	PUSHJ	P,CKEOL		; Check for EOL
	 JRST	OSXB.0		; Not an EOL, output the character
	PJRST	OSXBCN		; Output the record count

OSXB.0:	PUSH	P,T1		; Save T1
	MOVEI	T1,5		; Get 5 chars
	PUSHJ	P,M$GTXT	; Get a text buffer
	MOVE	T2,(P)		; Get the FDB address
	MOVEI	T2,.FDRCN(T2)	; Get the pointer address
	PUSHJ	P,M$USEB	; Set up the pointer
	MOVE	T2,T1		; Get the buffer address
	POP	P,T1		; Get the restore the FDB address
	JRST	OSXB.2		; Skip the EOL check

OSXB.1:	PUSHJ	P,CKEOL		; Check if this is an EOL
	 JRST	OSXB.2		; No, write the character
	$SAVE	<P1,P2>		; Save P1 and P2
	MOVE	P1,T1		; Get the FDB address
	MOVE	P2,T2		; And get the buffer address
	LOAD.	T2,BLKEND,(P2)	; Get the number of characters to write
	PUSHJ	P,OSXBCN	; Write out the count
	 POPJ	P,		; Couldn't
	SETZ	T2,		; Start from the start of the buffer
	MOVE	T1,P2		; Get the buffer address
	PUSHJ	P,SETINC	; Set up for GETINC
	 JRST	OSXB.4		; None, all done

OSXB.3:	MOVE	T1,P2		; Get the buffer address
	PUSHJ	P,GETINC	; Get a character
	 JRST	OSXB.4		; All done
	MOVEI	CH,'0'-"0"(CH)	; Convert to sixbit
	MOVE	T1,P1		; Get the FDB address
	PUSHJ	P,F$OBYT	; Write a character
	 POPJ	P,		; Give up
	JRST	OSXB.3		; Go for the next

OSXB.4:	MOVEI	T1,.FDRCN(P1)	; Get the pointer address
	PUSHJ	P,M$RELB	; Release the block
	PJRST	.POPJ1		; And return

; Here to write a character into the buffer to count it for later

OSXB.2:	CAXGE	CH," "		; Control character?
	 PJRST	.POPJ1		; Yes, ignore it
	CAXL	CH,"`"		; Lower case range?
	 MOVEI	CH,"a"-"A"(CH)	; Yes, convert to upper
	PUSH	P,T1		; Save T1
	LOAD.	T3,TPTADR,+.FDRCN(T1) ; Get the buffer address
	MOVX	T1,<POINTR(CH,^O<177_7>)> ; Get the byte pointer
	MOVEI	T2,1		; And the number of chars
	SETZ	T4,		; Clear the pointer address
	PUSHJ	P,M$INSS	; Insert the string
	MOVE	T2,(P)		; Get the FDB address
	LOAD.	T2,TPTADR,+.FDRCN(T2) ; Get the address of the buffer
	STOR.	T1,BLKPT,(T2)	; Store the new pointer
	PJRST	.T1PJ1		; Restore T1 and return

; Here to write the record count into the file. First advance to the
;next full word.

OSXBCN:	PUSH	P,T2		; Save the count
	SKIPN	T2,.FDBRH+.BFCTR(T1) ; Any room left here?
	 JRST	OSXB.5		; No, output the buffer
	IDIVI	T2,6		; Find out how many extra bytes
	JUMPE	T3,OSXB.6	; Are we at a word boundary?
	MOVN	T3,T3		; Get the amount we are skipping
	ADDM	T3,.FDBRH+.BFCTR(T1) ; And fix it
	MOVX	T2,<POINT 6,,0>	; Get the byte pointer to put in
	HLLM	T2,.FDBRH+.BFPTR(T1) ; Fix up the byte pointer
	JRST	OSXB.6		; And continue on

; Here to dump the buffer to make room for the count

OSXB.5:	PUSHJ	P,F$OBUF	; Dump it
	 PJRST	.T2PJ		; Restore T2 and return

OSXB.6:	AOS	T2,.FDBRH+.BFPTR(T1) ; Fix the byte pointer
	LOAD	T3,T2,BP.PFL	; Get the position
	CAXL	T3,^D36		; Pointing before the word?
	 SOJ	T2,		; Yes, back up one word then
	POP	P,(T2)		; Stuff in the record count
	PJRST	.POPJ1		; And return
	SUBTTL	Byte mode routines -- ASCII mode input/output

; The following are used for the output and input of ascii bytes

	I$ASCI==F$IBYT		; Input a byte
	O$ASCI==F$OBYT		; Write a byte
	SUBTTL	Byte mode routines -- LSA input/output

; The following are the routines used to input and output LSA bytes

O$LSA:	$SAVE	<T2,T3,T4>	; Save a few registers
	CAXN	CH,.CHFFD	; Form feed?
	 PJRST	WRTPAG		; Yes, write a page mark
	LOAD	T2,.FDFLG(T1),FD.NLS ; Check if we need a line number
	JMPF	T2,O$LS.1	; No, skip this
	LOAD.	T2,FDBLSN,(T1)	; Yes, get the last number
	ADDX	T2,<BYTE(7)0,0,1,0,0> ; Bump to next number
	TXNE	T2,<BYTE(7)0,0,<"8"&^-"0">,0,0> ; Is this over 8?
	 TXNN	T2,<BYTE(7)0,0,1,0,0> ; Yes, is this over 9?
	  JRST	O$LS.2		; Value is still ok
	TXZ	T2,<BYTE(7)0,0,^-"0",0,0> ; Clear the digit
	ADDX	T2,<BYTE(7)0,1,0,0,0> ; And bump the next
	TXNE	T2,<BYTE(7)0,<"8"&^-"0">,0,0,0> ; Did it overflow?
	 TXNN	T2,<BYTE(7)0,1,0,0,0> ; Maybe, check the other bit
	  JRST	O$LS.2		; No, use this value
	TXZ	T2,<BYTE(7)0,^-"0",0,0,0> ; Clear except the zero
	ADDX	T2,<BYTE(7)1,0,0,0,0> ; Bump the next digit
	TXNE	T2,<BYTE(7)<"8"&^-"0">,0,0,0,0> ; Check if it overflowed
	 TXNN	T2,<BYTE(7)1,0,0,0,0> ;  .  .  .
	  JRST	O$LS.2		; No, go use this value
	PUSHJ	P,WRTPAG	; Write a page mark
	 POPJ	P,		; Pass on the error return
	MOVX	T2,<ASCII |00100|>+1 ; Get the LSN
O$LS.2:	STOR.	T2,FDBLSN,(T1)	; Store the LSN
	PUSHJ	P,WRTWRD	; Write a word
	 POPJ	P,		; Couldn't
	PUSH	P,CH		; Save CH
	MOVX	CH,.CHTAB	; Get a tab
	PUSHJ	P,F$OBYT	; Write it
	 JRST	[POP	P,CH		; Restore the character
		POPJ	P,]		; And return
	POP	P,CH		; Get the character back
O$LS.1:	MOVX	T2,FD.NLS	; Get the bit to turn on (or off)
	ANDCAM	T2,.FDFLG(T1)	; Assume it should be off
	CAXN	CH,.CHLFD	; Is this a line feed?
	 IORM	T2,.FDFLG(T1)	; Yes, flag we will need the LSN
	PJRST	F$OBYT		; And write it


WRTPAG:	MOVX	T2,<ASCII |     |>+1 ; Get the second word
	PUSHJ	P,WRTWRD	; Write the word
	 POPJ	P,		; Pass on the error
	MOVX	T2,<BYTE(7).CHCRT,.CHFFD,0,0,0(1)1> ; Get the page mark
	PUSHJ	P,WRTWRD	; Write it
	 POPJ	P,		; Pass on the error
	MOVX	T2,<ASCII |00000|>+1 ; Get the initial LSN
	STOR.	T2,FDBLSN,(T1)	; Store the LSN
	BITON	T2,FD.NLS,.FDFLG(T1) ; And flag we need an LSN
	PJRST	.POPJ1		; Give a good return

WRTWRD:	$SAVE	<CH,P1>		; Save CH
	MOVE	P1,T2		; Get the word to write
	MOVE	T2,.FDBRH+.BFCTR(T1) ; Get the buffer counter
	IDIVI	T2,5		; And see if we need to skip to the end of a word
	JUMPE	T3,WRTW.0	; If not just go write the word
	SETZ	CH,		; Pad with nulls
WRTW.1:	PUSHJ	P,F$OBYT	; Write the byte
	 POPJ	P,		; Give up
	SOJG	T3,WRTW.1	; loop until at a word boundary

WRTW.0:	MOVE	T2,.FDBRH+.BFCTR(T1) ; Get the counter again
	JUMPN	T2,WRTW.2	; Any room left?
	PUSHJ	P,F$OBUF	; No, output the buffer
	 POPJ	P,		; Pass on the error
WRTW.2:	MOVX	T2,-5		; Get the number of chars we will write
	ADDM	T2,.FDBRH+.BFCTR(T1) ;  .  .  .
	AOS	T2,.FDBRH+.BFPTR(T1) ; Get the byte pointer
	IBP	T2		; Bump the pointer so it in the correct place
	MOVEM	P1,-1(T2)	; Store the word
	PJRST	.POPJ1		; Give the good return


; Here to input an LSA file

I$LSA:	$SAVE	<T2,T3,T4>	; Input a byte from the LSA files
	LOAD	T2,.FDFLG(T1),FD.NLS ; Looking for an LSA?
	JMPF	T2,I$LS.1	; If not skip the check
	MOVE	T2,.FDBRH+.BFCTR(T1) ; Get the counter
	IDIVI	T2,5		; Check if at a word boundary
	JUMPN	T3,F$IBYT	; If not, just get a byte
	JUMPN	T2,I$LS.0	; Have anything left to check?
	PUSHJ	P,F$IBUF	; No, input a buffer
	 POPJ	P,		; Couldn't, return
I$LS.0:	MOVE	T2,.FDBRH+.BFPTR(T1) ; Get the buffer pointer
	IBP	T2		; Make sure it is at the correct word
	MOVE	T3,(T2)		; Get the word to check
	TXNN	T3,1		; Is the LSN bit on?
	 JRST	F$IBYT		; No, just get the next char
	CAXE	T3,<ASCII |     |>+1 ; Is this a prefix to a page mark?
	 JRST	I$LS.2		; No, just store the LSN
	MOVX	T3,-5		; Yes, count the characters
	ADDB	T3,.FDBRH+.BFCTR(T1) ;  .  .  .
	JUMPN	T3,I$LS.3	; Is there still another word left?
	PUSHJ	P,F$IBUF	; No, get a buffer
	 POPJ	P,		; Couldn't
	SKIPA	T2,.FDBRH+.BFPTR(T1) ; Get the byte pointer
I$LS.3:	 AOS	T2,.FDBRH+.BFPTR(T1) ; Advance the byte pointer
	IBP	T2		; Make sure we are at the correct word
	MOVE	T3,(T2)		; Get the next word
	TXZ	T3,1		; Clear the LSN bit (in case it was on)
	CAXE	T3,<BYTE(7).CHCRT,.CHFFD,0,0,0(1)0> ; Is this a page mark?
	 PJRST	F$IBYT		; No, just return a character
	AOS	(P)		; Give the good return
	MOVX	CH,.CHFFD	; Of a form feed

I$LS.4:	AOS	.FDBRH+.BFPTR(T1) ; Bump the pointer past the page mark
	MOVX	T2,-5		; And fix the counter
	ADDM	T2,.FDBRH+.BFCTR(T1) ;  .  .  .
	POPJ	P,		; Return

I$LS.2:	STOR.	T3,FDBLSN,(T1)	; Store the LSN
	BITOFF	T2,FD.NLS,.FDFLG(T1) ; Flag we don't need an LSN anymore
	PUSHJ	P,I$LS.4	; Fix the buffer header
	PUSHJ	P,F$IBYT	; Get the next character
	 POPJ	P,		; Pass on the error
	CAXE	CH,.CHTAB	; Is it a tab?
	 JRST	I$LS.5		; No, check if a line feed

I$LS.1:	PUSHJ	P,F$IBYT	; Get a byte
	 POPJ	P,		; Couldn't
I$LS.5:	CAXE	CH,.CHLFD	; Is it a line feed?
	 PJRST	.POPJ1		; No, just return
	BITON	T2,FD.NLS,.FDFLG(T1) ; Yes, remember we need an LSN
	PJRST	.POPJ1		; And return
	SUBTTL	F$IBYT - Input a byte

;+
;.HL1 F$IBYT
;This routine will input a byte from the file if possible.  If the file is
;not open for reading it will give an error.
;.literal
;
; Usage:
;	MOVE	T1,FDB		; File access block
;	PUSHJ	P,F$IBYT	; Input the byte
;	(Error return)		; Failed -- Message already given
;	(Normal return)
;
; Error return:
;	T1 - Error reason
;
; Normal return:
;	CH - Byte input
;.end literal
;-

F$IBYT:	SOSGE	.FDBRH+.BFCNT(T1) ; Decrement the buffer count
	  JRST	F$IB.1		; Get the next buffer
	ILDB	CH,.FDBRH+.BFPTR(T1) ; Get the character
	JRST	.POPJ1		; Return to the caller

F$IB.1:	PUSHJ	P,F$IBUF	; Get the next buffer
	 POPJ	P,		; Couldn't
	JRST	F$IBYT		; And try again

F$IBUF:	MOVEM	T1,LASFDB	; Store this FDB incase of error
	MOVX	T2,FD.TMP	; Check if the file is a TMPCOR
	TDNE	T2,.FDFLG(T1)	;  .  .  .
	 JRST	F$IB.3		; Yes, go flag EOF
	MOVX	T3,.FOINP	; Get the function
	LOAD.	T2,FDBCHN,(T1)	; Get the channel
	STOR	T2,T3+.FOFNC,FO.CHN ; Store the channel number
	MOVE	T2,[XWD 1,T3]	; Get the argument block pointer
	FILOP.	T2,		; Get the next buffer
	  JRST	F$IB.2		; Process the errors
	STOR.	T2,FDBIOS,(T1)	; Update the I/O status
	INCR.	,FDBBLK,(T1)	; Bump the block number
	PJRST	.POPJ1		; Give the good return

F$IB.2:	STOR.	T2,FDBIOS,(T1)	; Store the I/O status
	TXNN	T2,IO.EOF	; End of file ?
	  JRST	[MOVX	T1,$FEMON	; ++ Monitor error
		POPJ	P,]		; Return to the caller
F$IB.3:	BITON	T2,FD.EOF,.FDFLG(T1) ; Store the end of file flag
	MOVX	T2,FD.EB	; Check if this file is EB'ed
	TDNE	T2,.FDFLG(T1)	;  .  .  .
	 JRST	F$IB.4		; Yes, just return eof
	PUSHJ	P,F$CLOS	; Close the input file
	 POPJ	P,		; Pass the error on

F$IB.4:	MOVX	T1,$FEEOF	; Return the end of file
	POPJ	P,		; Return to the caller
	SUBTTL	F$OBYT - Write a byte to a file

;+
;.Hl1 F$OBYT
;This routine will write a byte to an output file.  It will give an error
;if the write fails.
;.literal
;
; Usage:
;	MOVE	T1,FDB
;	MOVEI	CH,Byte to write
;	PUSHJ	P,F$OBYT
;	(Error return)
;	(Normal return)
;
; Error return:
;	T1 - Error reason
;
; Normal return:
;	Returns nothing.
;.end literal
;-

F$OBYT:	SOSGE	.FDBRH+.BFCTR(T1) ; Decrement the buffer count
	  JRST	F$OB.0		; Output the next buffer
	IDPB	CH,.FDBRH+.BFPTR(T1) ; Store te byte
	JRST	.POPJ1		; Give a good return

F$OB.0:	PUSHJ	P,F$OBUF	; Output the buffer
	 POPJ	P,		; Can't
	JRST	F$OBYT		; And go try the character again

F$OBUF:	MOVEM	T1,LASFDB	; Store the FDB incase of an error
	LOAD	T2,.FDFLG(T1),FD.TMP ; Check if going to TMPCOR
	JMPT	T2,F$OB.T	; If so, we must now convert to using disk
	MOVX	T3,.FOOUT	; Get the FILOP function
	LOAD.	T2,FDBCHN,(T1)	; Get the channel
	STOR	T2,T3+.FOFNC,FO.CHN ; Store the channel in the block
	MOVE	T2,[XWD 1,T3]	; Get the arugment block address
	FILOP.	T2,		; Do the FILOP
	 JRST	[STOR.	T2,FDBIOS,(T1)	; Store the I/O status
		MOVX	T1,$FEMON	; Flag the monitor is form the monitor
		POPJ	P,]		; Return
	STOR.	T2,FDBIOS,(T1)	; Store the I/O status
	INCR.	,FDBBLK,(T1)	; Bump the block number
	JRST	.POPJ1		; Loop


; Here if TMP: file will not fit in TMPCOR. We must now open the file
;on disk and write out the data we have so far.

F$OB.T:	$SAVE	<T1,P1,P2,P3,P4,CH> ; Save some room
	MOVE	P1,T1		; Get the FDB address
	MOVE	P2,.FDBRH+.BFADR(P1) ; And get the IOWD
	LOAD.	P4,FDBBUF,(P1)	; Get the buffer address
	ZERO.	T1,FDBBUF,(P1)	; Clear the buffer address
	LOADS.	T1,FDBNAM,(P1)	; Get the name into the other half
	HLL	T1,SIXJOB	; And make the nnnFOO file
	STOR.	T1,FDBNAM,(P1)	; Store the new name
	MOVX	T1,'TMP'	; Get the extension
	STOR.	T1,FDBEXT,(P1)	; Store it
	BITON	T1,FD.HEX!FD.TMD,.FDFLG(P1) ; Flag we have the extension
	MOVX	T1,<SIXBIT |DSK|> ; Get the device
	STOR.	T1,FDBDEV,(P1)	; Store it
	MOVX	T1,$IOWRI	; Get the function
	MOVE	T2,P1		; And the FDB address
	PUSHJ	P,F$OPEN	; And open the file
	 PJRST	F$ERR		; Couldn't, give up
	BITOFF	T1,FD.TMP,.FDFLG(P1) ; Flag not going to TMPCOR
	JUMPE	P4,.POPJ1	; If no buffer, skip this
	HLRE	T1,P2		; Get the number of words
	MOVN	T1,T1		; Make positive
	IMULI	T1,5		; Make it the number of characters
	MOVE	P3,T1		; And put in a safe place
	MOVEI	P2,1(P2)	; Get the address of the buffer
	HRLI	P2,(POINT 7,)	; Make it a byte pointer

F$OT.1:	ILDB	CH,P2		; Get a character
	MOVE	T1,P1		; And the FDB address
	PUSHJ	P,F$OBYT	; Output the character
	 PJRST	F$ERR		; Couldn't
	SOJG	P3,F$OT.1	; Loop for all the characters
	MOVE	T1,P4		; Get the buffer address
	PUSHJ	P,M$RBLK	; And return it
	PJRST	.POPJ1		; And return
	SUBTTL	F$RBUF - Read into a text buffer

;+
;.HL1 F$RBUF
; This routine will read data from an input file into the text buffer.
;.b.literal
; Usage:
;	MOVEI	T1,TPT address
;	MOVE	T2,Number of line feeds to allow
;	MOVE	T3,Number of characters to allow
;	MOVE	T4,Number of form feeds to allow
;	PUSHJ	P,F$RBUF
;-

F$RBUF:	$SAVE	<P1,P2,P3,P4>	; Save some ac's
	MOVE	P4,T1		; Get the TPT addrees
	MOVEM	T2,LFCNT	; Save the line feed count
	MOVEM	T3,CHRCNT	; And the character count
	MOVEM	T4,FFCNT	; And the form feed count
	LOAD.	T1,TPTADR,(P4)	; Get the text buffer address
	PUSHJ	P,GETFDI	; And find the FDB
	 ERROR	E.NFI		; No file for input
	MOVE	P1,T1		; Get the address of the FDB
	CFXN.	T2,FDBMOD,(P1),$FMDMP ; Is this dump mode?
	 JRST	FRDUMP		; Yes, go read it
	CFXE.	T2,FDBFFC,(P1),0 ; Is this the first read?
	 JRST	FRBU.7		; No, skip this
	INCR.	T2,FDBFFC,(P1)	; Bump the counter
FRBU.7:	MOVX	T1,FD.EOF	; Check if file is at end of file
	TDNE	T1,.FDFLG(P1)	;  .  .  .
	 POPJ	P,		; Yes, don't bother doing anything
	LOAD.	P3,TPTADR,(P4)	; Get text buffer address back
	LOAD.	T2,BLKEND,(P3)	; Get the end point
	IDIVI	T2,5		; Get the word and character index
	ADDI	T2,.BKTLN(P3)	;  .  .  .
	HLL	T2,BTAB-1(T3)	; And make the byte pointer
	STOR.	T2,BLKPTR,(P3)	; Store it
	LOAD.	T1,FDBSIZ,(P1)	; Get the size
	CAMGE	T1,CHRCNT	; More chars requested that are in the file?
	 MOVEM	T1,CHRCNT	; Save the number of chars from file
	MOVEI	T1,(P3)		; Get the address of the buffer
	MOVE	T2,CHRCNT	; And the amount to expand
	LOAD.	T3,BLKEND,(T1)	; And where to do it
	MOVE	P3,T3		; Remember it
	PUSHJ	P,M$XPND	; Expand the buffer
	LOAD.	T2,BLKEND,(T1)	; Get the new end
	STOR.	P3,BLKEND,(T1)	; And reset the end
	SUB	T2,P3		; Get the extra free
	ADDM	T2,.BKFRE(T1)	; Update the amount free
	MOVE	P3,T1		; And reset P3
	MOVE	T1,CHRCNT	; Get the number of characters
	MOVEM	T1,ENDYNK	; Save as ending point
	IMULI	T1,3		; Get the two-thirds point
	LSH	T1,-1		; Divide by 2
	MOVEM	T1,STPYNK	; Save the end point
	FRAME.	<XCTCHR>	; Make room for a get a character instruction
	LOAD.	T1,FDBMOD,(P1)	; Get the mode
	MOVE	T1,IBYTBL(T1)	; Get the address of the routine to fetch chars
	HRLI	T1,(PUSHJ P,)	; And get the instruction
	MOVEM	T1,XCTCHR	; Save it
; Loop reading into the buffer until an ending condition is met

FRBU.1:	SOS	STPYNK		; Count for ultimate stop
	SOSLE	ENDYNK		; Hit the two-thirds point yet?
	  JRST	FRBU.2		; No, keep getting chars
	SKIPL	STPYNK		; Are we almost full?
	 CAXN	CH,.CHLFD	; No, was the last char a line feed?
	  JRST	FRBU.4		; Last char a line feed or almost full, stop reading

FRBU.2:	MOVE	T1,P1		; Get the FDB address
	XCT	XCTCHR		; Get a character
	 JRST	FRBU.5		; Go check for end of file
	JUMPE	CH,FRBU.2	; Skip nulls
	CAXN	CH,.CHFFD	; Form feed?
	 JRST	FRBU.3		; Yes, terminate the reading
FRBU.9:	SOSL	.BKFRE(P3)	; Decrement the count of free characters
	 JRST	FRBU.8		; Still room to store this one
	AOS	.BKFRE(P3)	; Rest to 0
	MOVEI	T1,(P3)		; Get te buffer address
	MOVX	T2,D.TXTS	; And the size
	LOAD.	T3,BLKEND,(T1)	; And the end address
	MOVE	P3,T3		; Remember the old end
	PUSHJ	P,M$XPND	; Expand the buffer some more
	SOS	.BKFRE(T1)	; Fix the free count
	LOAD.	T2,BLKEND,(T1)	; Get the new end
	STOR.	P3,BLKEND,(T1)	; Fix it back
	SUB	T2,P3		; Get the amount free
	ADDM	T2,.BKFRE(T1)	; Update it
	MOVE	P3,T1		; Get the address back


FRBU.8:	INCR.	,BLKEND,(P3)	; Bump the end point
	IDPB	CH,.BKPTR(P3)	; Store it
	CAXE	CH,.CHLFD	; Line feed?
	 AOJA	P4,FRBU.1	; No, count it and try again
	SOSLE	LFCNT		; Have enough line feeds yet?
	 AOJA	P4,FRBU.1	; No, keep reading
	AOJA	P4,FRBU.4	; Yes, stop the read

FRBU.3:	INCR.	,FDBFFC,(P1)	; Increment the form feed count
	SOSLE	FFCNT		; Are we done?
	 JRST	FRBU.9		; No, continue
	BITON	T1,TF.FFD,.BKTFL(P3) ; Flag that we ended with a form feed
	JRST	FRBU.6		; And join the common code

FRBU.4:	BITOFF	T1,TF.FFD,.BKTFL(P3) ; Flag no form feed
FRBU.6:	SETZM	LASFDB		; No last FDB
	POPJ	P,		; And return

FRBU.5:	CAXN	T1,$FEEOF	; End of file?
	 JRST	FRBU.4		; Yes, go fix up the end
	MOVEM	P1,LASFDB	; Save the error FDB
	PJRST	F$ERR		; No, go give up
; Here to read the file in dump mode.

FRDUMP:	MOVX	T1,FD.EOF	; Get the EOF bit
	TDNE	T1,.FDFLG(P1)	; Are we already at EOF?
	 POPJ	P,		; Yes, all done
	LOAD.	T1,TPTADR,+$QRTPT(P4) ; Get the buffer address
	LOAD.	T2,FDBSIZ,(P1)	; And get the size of the file
	JUMPE	T2,FRDU.0	; Null file?
	IMULX	T2,5		; Make it characters
	LOAD.	T3,BLKEND,(T1)	; Get the end pointer
	JUMPN	T3,IODERR	; Only allowed if at start of buffer
	PUSHJ	P,M$XPND	; Make the room for the buffer
	MOVX	T3,FD.TMP	; Check if this is a TMP: file
	TDNE	T3,.FDFLG(P1)	;  .  .  .
	 JRST	FRDTMP		; Yes, it already is in core
	LOAD.	P2,FDBSIZ,(P1)	; Get the size back
	MOVN	P2,P2		; Negate it
	MOVSI	P2,(P2)		; Into left half
	HRRI	P2,.BKTLN-1(T1)	; Set up the IOWD
	SETZ	P3,		; Clear for the end of the command list
	LOAD.	T1,FDBCHN,(P1)	; Get the channel
	MOVX	T2,<INSVL.(.FOINP,FO.FNC)> ; Get the function
	STOR	T1,T2,FO.CHN	; Store the channel
	MOVEI	T3,P2		; Get the address of the command list
	MOVX	T1,<XWD 2,T2>	; Get the pointer
	FILOP.	T1,		; Input the data
	 TXNN	T1,IO.ERR	; Any errors?
	  JRST	FRDU.1		; Continue processing
	STOR.	T1,FDBIOS,(P1)	; Store the status
	SKIPA	P3,[EXP $FEMON]	; Get the error code to return

FRDU.1:	 SETO	P3,		; Flag no error
	LOAD.	P2,TPTADR,(P4)	; Get the buffer address
	LOAD.	T1,FDBSIZ,(P1)	; Get the number of words we read
	ADDI	T1,.BKTLN-1(P2)	; Point to the last word of the data
	MOVX	T2,<BYTE(7)0,0,0,0,177> ; Get a mask to check for nulls
	SETZ	T3,		; Set up a counter

FRDU.2:	TDNE	T2,(T1)		; Is this a null?
	 JRST	FRDU.3		; No, all done
	AOJ	T3,		; Bump the count
	ROT	T2,7		; Shift the mask
	TRNN	T2,1		; do five chars?
	 JRST	FRDU.2		; No, continue
	ROT	T2,1		; Skip the extra bit
	CAILE	T1,.BKTLN(P2)	; Done with all of the buffer?
	 SOJA	T1,FRDU.2	; And try again

; Here when we find a non-null character. The number of nulls is in
;T3.

FRDU.3:	ADDM	T3,.BKFRE(P2)	; Increase the free count
	MOVN	T3,T3		; Negate the null count
	ADDM	T3,.BKEND(P2)	; And back off the end pointer
	JUMPGE	P3,FRDU.E	; Error?
FRDU.0:	BITON	T1,FD.EOF,.FDFLG(P1) ; No, flag eof
	LOAD	T1,.FDFLG(P1),FD.EB ; Get the EB flag
	JMPT	T1,.POPJ	; Just return if EB'ed
	MOVE	T1,P1		; Otherwise close the file
	PUSHJ	P,F$CLOS	;  .  .  .
	 PJRST	F$ERR		; Error?
	POPJ	P,		; All done


; Here if an error occured on the FILOP.

FRDU.E:	MOVE	T1,P3		; Get the error code
	PJRST	F$ERR		; And give the error

; Here to read a TMP: file in dump mode.

FRDTMP:	LOAD.	T1,TPTADR,+$QRTPT(P4) ; Get the pointer address
	ADDX	T1,.BKTLN	; Point to first data word
	LOAD.	T2,FDBBUF,(P1)	; Get the buffer address
	HRLI	T1,(T2)		; And set up the BLT pointer
	LOAD.	T2,FDBSIZ,(P1)	; Get the file size (in characters)
	ADDI	T2,(T1)		; Get the final address
	BLT	T1,-1(T2)	; And move the text
	JRST	FRDU.1		; And go back to normal loop
	SUBTTL	F$WBUF - Write out a buffer to a file

;+
;.HL1 F$WBUF
; This routine will write out data from the given buffer into a file.
;.b.literal
; Usage:
;	MOVEI	T1,Text buffer address
;	MOVEI	T2,True= no form feed, False= maybe
;	MOVEI	T3,False= Force form feed, True= FF only if TF.FFD on
;	MOVEI	T4,FDB address
;	MOVE	A2,Start of text to write
;	MOVE	A1,End of text to write
;	PUSHJ	P,F$WBUF
;	 (return here)
;
;.end literal
;-

F$WBUF:	$SAVE	<P1,P2,P3,P4>	; Save some ac's
	DMOVE	P3,T2		; Get the flags
	MOVE	P1,T1		; Get the text buffer address
	MOVE	P2,T4		; Save the FDB address

	LOAD.	T1,FDBMOD,(P2)	; Get the mode
	PUSHJ	P,@DBFTBL(T1)	; And dump out the buffer
	JMPT	P3,FWBU.0	; All done if no form feed ever
	MOVX	T1,TF.FFD	; Get the flag bit
	TDNN	T1,.BKTFL(P1)	; Need a form feed from reading it?
	 JMPT	P4,FWBU.0	; No, only if forced from the call
	MOVX	CH,.CHFFD	; Get the character
	MOVE	T1,P2		; And the FDB address
	PUSHJ	P,F$WRIT	; Write the character
	 PJRST	F$ERR		; Couldn't, punt
FWBU.0:	SETZM	LASFDB		; Clear the last FDB that was used
	POPJ	P,		; All done
	SUBTTL	F$COPY - Copy from one file to another

;+
;.HL1 F$COPY
; This routine will perform the exit type function for a text
;buffer which has two files open. First it will copy the text from
;the text buffer into the output file. Next it will copy data directly
;from the input file to the output file. Any necessary conversion of
;data will be done during these operations.
;.b.literal
;
; Usage:
;	MOVE	T1,Text.buffer.address
;	MOVE	T2, (0 if REENTER command should abort, -1 if it should not)
;	PUSHJ	P,F$COPY
;	 (aborted due to REENTER)
;	(all data copied, files still open)
;
;.end lit
;If a REENTER command is given and the routine is to abort, it will abort
;at the first line terminator character it sees after the reenter is seen.
;The upper level is responsible for re-filling the text buffer after that
;point.
;-

F$COPY:	$SAVE	<P1,P2,P3,P4>	; Save some working room
	$SAVE	<A1,A2>		; Save the arg registers also
	MOVEM	T2,REEABT	; Save the flag
	MOVE	P1,T1		; Get the buffer address
	LOAD.	P2,BLKFDO,(P1)	; Get the output FDB address
	LOAD.	P3,BLKFDI,(P1)	; And the input FDB address
	LOAD.	T1,FDBMOD,(P2)	; Get the output mode
	SETZ	A2,		; Clear the first position
	LOAD.	A1,BLKEND,(P1)	; And get the final one
	PUSHJ	P,@DBFTBL(T1)	; Call correct dump buffer routine
	LOAD	T1,.BKTFL(P1),TF.FFD ; Get the form feed flag
	JMPF	T1,FCOP.1	; If no form feed, all done
	MOVX	CH,.CHFFD	; Need a form feed, get the character
	MOVE	T1,P2		; Get the FDB address
	PUSHJ	P,F$WRIT	; Write the character
	 PJRST	F$ERR		; Couldn't
FCOP.1:	LOAD	T1,.BKTFL(P1),TF.OPI ; Get the open for input flag
	JMPF	T1,FCOP.2	; No, give a good return
	MOVE	T1,.FDFLG(P3)	; Get the flags
	TXNE	T1,FD.OPN	; File still open?
	 TXNE	T1,FD.EOF	; Yes, at eof?
	  PJRST	FCOP.2		; Either closed or at eof
	LOAD.	T1,FDBMOD,(P2)	; Get the output mode
	LOAD.	T2,FDBMOD,(P3)	; And the input mode
	PUSHJ	P,@CPYTBL(T1)	; And copy the files
	 SKIPA			; Re-enter typed
FCOP.2:	AOS	(P)		; Give a skip return
	SETZM	LASFDB		; Clear the last FDB, since there was no err
	POPJ	P,		; Return to the caller

; Table of routines to copy data from text buffer to output file

TABDEF	DBF,$FM			; Initialize the table
 TABENT	ASC,DBFASC		; Plain ASCII data
 TABENT	DFT,DBFASC		; Plain ASCII data
 TABENT	LSA,DBFGEN		; Line sequenced ASCII
 TABENT	SXB,DBFGEN		; SIXBIT data
 TABENT	DMP,DBFDMP		; Dump mode
TABEND				; End of table

; Table of routines to copy data from input file to output file

TABDEF	CPY,$FM			; Table to be index by output mode
 TABENT	ASC,<Z @ASCTBL(T2)>	; ASCII
 TABENT	DFT,<Z @ASCTBL(T2)>	; ASCII
 TABENT	LSA,<Z @LSATBL(T2)>	; Line sequenced
 TABENT	SXB,<Z @SXBTBL(T2)>	; SIXBIT
 TABENT	DMP,IODERR	; Not allowed for dump mode
TABEND

TABDEF	ASC,$FM			; Subtable for ASCII output
 TABENT	ASC,CPYA2A		; Direct copy
 TABENT	DFT,CPYA2A		; Direct copy
 TABENT	LSA,CPYGEN		; LSA input uses general copy routine
 TABENT	SXB,CPYGEN		; SIXBIT input uses general copy routine
TABEND

SXBTBL:
TABDEF	LSA,$FM			; Subtable for LSA output
 TABENT	ASC,CPYGEN		; General routine for ASCII
 TABENT	DFT,CPYGEN		; General routine for ASCII
 TABENT	LSA,CPYGEN		; General routine for LSA
 TABENT	SXB,CPYGEN		; And for SIXBIT
TABEND
	SUBTTL	F$COPY -- Subroutines -- DBFGEN - General dump buffer routine

; This routine will write out the text from the text buffer into the output
;file. It assumes the ac's have been set up as:
;
;	P1/	Text buffer address
;	P2/	Output file FDB address
;

DBFGEN:	MOVE	T1,A2		; Get the first pos
	IDIVI	T1,5		; Get the word/char offsets
	SUB	A1,A2		; Get the number of characters to write
	MOVEI	A2,.BKTLN(P1)	; Get the base address
	ADDI	A2,(T1)		; Point to correct place
	HLL	A2,BTAB-1(T2)	; And set up the byte pointer

DBFG.1:	SOJL	A1,.POPJ	; Done yet?
	ILDB	CH,A2		; No, get a character
	MOVE	T1,P2		; Get the FDB address
	PUSHJ	P,F$WRIT	; Write it out
	 PJRST	F$ERR		; Couldn't
	JRST	DBFG.1		; Loop for the next
	SUBTTL	F$COPY -- Subroutines -- DBFDMP - Dump mode buffer routine

; This routine will write out the text from the text buffer into the output
;file. It assumes the ac's have been set up as:
;
;	P1/	Text buffer address
;	P2/	Output file FDB address
;

DBFDMP:	JUMPN	A2,IODERR	; This allowed?
	CFME.	,BLKEND,(P1),A1	; Must be entire buffer
	 JRST	IODERR		; Give the error
	SKPOPN	0(P2)		; File open?
	 JRST	IODERR		; No, punt
	JUMPE	A1,[MOVE T1,P2		; If nothing to output
		PUSHJ	P,F$CLOS	; just close the file
		 PJRST	F$ERR		; Punt on errors
		POPJ	P,]		; Pass back good return
	IDIVI	A1,5		; Get the number of words
	JUMPE	A2,DBFD.1	; If no extra character positions, skip this
	MOVEI	T1,(A1)		; Get the final address
	ADDI	T1,.BKTLN(P1)	; And make it absolute
	SETO	T3,		; Get a mask
	IMULX	A2,-^D7		; Get the amount to shift it
	SETZ	T2,		; Clear T2
	LSHC	T2,(A2)		; Shift in the mask
	ANDCAM	T3,(T1)		; Clear out the extra positions
	AOJ	A1,		; Count it
DBFD.1:	MOVN	A1,A1		; Negate the count
	HRLI	A1,(A1)		; Put in left half
	HRRI	A1,.BKTLN-1(P1)	; And set up IOWD
	SETZ	A2,		; End the list
	MOVX	T1,FD.TMP	; Check if really a TMPCOR file
	TDNE	T1,.FDFLG(P2)	;  .  .  .
	 JRST	DBFD.T		; Yes, go handle it
DBFD.2:	MOVX	T2,<INSVL.(.FOOUT,FO.FNC)> ; Get the function code
	LOAD.	T1,FDBCHN,(P2)	; Get the channel number
	STOR	T1,T2,FO.CHN	; Store it
	MOVEI	T3,A1		; Get the control list address
	MOVX	T1,<XWD 2,T2>	; Get the pointer
	FILOP.	T1,		; And output the buffer
	 JRST	.+2		; Couldn't
	  JRST	[MOVE	T1,P2		; Get the FDB address
		PUSHJ	P,F$CLOS	; Close the file
		 PJRST	F$ERR		; Couldn't
		POPJ	P,]		; Return
	STOR.	T1,FDBIOS,(P2)	; Store the status
	SKIPA	T1,[EXP $FEMON]	; Get the error code
IODERR:	 MOVX	T1,$FEIOD	; Get the error code
	PJRST	F$ERR		; And go handle the error

; Here if the file wants to go to TMPCOR.
; At this pointer, A1 contains the IOWD.

DBFD.T:	MOVX	T1,<XWD .TCRWF,T2> ; Get the pointer
	LOAD.	T2,FDBNAM,(P2)	; And the file name
	MOVE	T3,A1		; And the IOWD
	TMPCOR	T1,		; Try to write it directly
	 JRST	DBFD.D		; Can't, switch it to DSK
	POPJ	P,		; All done, return

DBFD.D:	PUSHJ	P,F$OB.T	; Switch to DSK:
	 PJRST	F$ERR		; Can't
	JRST	DBFD.2		; And go write things out
	SUBTTL	F$COPY -- Subroutines -- DBFASC - ASCII dump buffer routine

; This routine will write out the text from the text buffer into the output
;file. It assumes the ac's have been set up as:
;
;	P1/	Text buffer address
;	P2/	Output file FDB address
;	P3/	Input file FDB address
;
; This routine will use the M$MSTR routine to copy the text into
;the output buffer.

DBFASC:	$SAVE	<P1,P2,P3,P4>	; Save some room
	LOAD	T1,.FDFLG(P1),FD.TMP ; Check if going to TMPCOR
	JMPT	T1,DBFGEN	; Yes, do it normal

DBFA.0:	JUMPLE	A1,DBFA.D	; Yes, go finish up
	MOVE	T3,.BFCTR+.FDBRH(P2) ; Get the counter
	CAMLE	T3,A1		; Will all the chars from the buffer fit?
	 MOVE	T3,A1		; Yes, write them all
	MOVE	P4,T3		; Get the number of chars for this pass
	MOVE	T1,A2		; Get the current pointer
	IDIVI	T1,5		; And convert to word/char address
	HLL	T1,BTAB-1(T2)	; Get the byte pointer
	ADDI	T1,.BKTLN(P1)	; And the address
	MOVE	T2,.BFPTR+.FDBRH(P2) ; Get the buffer pointer
	PUSHJ	P,M$MSTR	; Move the text
	MOVE	T1,P4		; Get the character count
	MOVE	T2,P2		; And the FDB to fix
	PUSHJ	P,FIXBRH	; And fix up the buffer headers
	SKIPN	.BFCTR+.FDBRH(P2) ; Buffer full?
	 PUSHJ	P,CPYOUT	; Yes, output it
	ADD	A2,P4		; Increment the position
	SUB	A1,P4		; And decrement the count
	JRST	DBFA.0		; Loop for next pass

DBFA.D:	POPJ	P,		; All done
	SUBTTL	F$COPY -- Subroutines -- FIXBRH - Fix a buffer header

; This routine will fix a buffer header for the number of characters
;written into the buffer without uses the counter/pointer in the header.

; Usage:
;	MOVE	T1,Number of characters
;	MOVE	T2,FDB address
;	PUSHJ	P,FIXBRH

FIXBRH:	MOVN	T3,T1		; Get the count
	ADDM	T3,.BFCTR+.FDBRH(T2) ; And fix up the counter
	MOVE	T3,T1		; Get another copy
	IDIVI	T3,5		; Make the number of words plus extra bytes
	ADDM	T3,.BFPTR+.FDBRH(T2) ; Bump the pointer
	JUMPE	T4,.POPJ	; If no extra bytes, return
	IBP	.BFPTR+.FDBRH(T2) ; Bump the pointer
	SOJG	T4,.-1		; Do enough increments
	POPJ	P,		; And return
	SUBTTL	F$COPY -- Subroutines -- CPYOUT - Output a buffer

; This routine will output a buffer of the output file. The ac's are
;assumed to be set up as:
;	P2/	Output FDB address

CPYOUT:	MOVE	T1,P2		; Get the FDB address
	PUSHJ	P,F$OBUF	; Output the buffer
	 PJRST	F$ERR		; Couldn't
	POPJ	P,		; Did it, return
	SUBTTL	F$COPY -- Subroutines -- CPYGEN - General copy routine


; Here to copy from input file to output file. This routine assumes
;that the ac's are set up as follows:
;	P1/	Text buffer address
;	P2/	Output file FDB address
;	P3/	Input file FDB address
;

CPYGEN:	JUMPE	P3,.POPJ1	; If no input file, just return
CPYG.2:	MOVE	T1,P3		; Get the FDB address
	PUSHJ	P,F$READ	; And read a character
	 JRST	[CAXE	T1,$FEEOF	; Get end of file?
		  PJRST	F$ERR		; No, error
		PJRST	.POPJ1]		; Yes, all done
	MOVE	T1,P2		; Got a character, get the ouput FDB address
	PUSHJ	P,F$WRIT	; Write the character
	 PJRST	F$ERR		; Couldn't
	SKIPE	REEABT		; Supposed to abort now?
	 JRST	CPYG.2		; No, don't even bother checking
	SKIPN	XCTING		; Get a re-enter?
	 PUSHJ	P,CKEOL		; Yes, only stop if end of a line
	  JRST	CPYG.2		; No reenter or not end of line, continue
	POPJ	P,		; Return now
	SUBTTL	F$COPY -- Subroutines -- CPYGEN - General copy routine


; Here to copy from input file to output file. This routine assumes
;that the ac's are set up as follows:
;	P1/	Text buffer address
;	P2/	Output file FDB address
;	P3/	Input file FDB address
;

CPYA2A:	MOVE	T3,.BFCTR+.FDBRH(P2) ; Get the number of chars left in the output buffer
	CAMLE	T3,.BFCTR+.FDBRH(P3) ; Fewer chars in the input buffer?
	 MOVE	T3,.BFCTR+.FDBRH(P3) ; Yes, get that count
	MOVE	T1,.BFPTR+.FDBRH(P3) ; get the source pointer
	MOVE	T2,.BFPTR+.FDBRH(P2) ; And the destination
	MOVE	P4,T3		; Remeber how many chars
	PUSHJ	P,M$MSTR	; Move the text
	MOVE	T1,P4		; Get the char count
	MOVE	T2,P2		; And the FDB address
	PUSHJ	P,FIXBRH	; Fix the buffer header
	MOVE	T1,P4		; Get the count back
	MOVE	T2,P3		; And the address
	PUSHJ	P,FIXBRH	; And fix the input header
	SKIPN	.BFCTR+.FDBRH(P2) ; Need to output the buffer?
	 PUSHJ	P,CPYOUT	; Yes, do it
	SKIPE	.BFCTR+.FDBRH(P3) ; Anything in the input buffer?
	 JRST	CPYA.1		; Yes, go for another pass
	MOVE	T1,P3		; Get the FDB address
	PUSHJ	P,F$IBUF	; Input a buffer
	 JRST	.+2		; Error, check for EOF
	  JRST	CPYA.1		; Go loop
	CAXE	T1,$FEEOF	; End of file?
	 PJRST	F$ERR		; No, give up
	PJRST	.POPJ1		; Yes, all done

; Here to check for re-enter if necessary

CPYA.1:	SKIPN	REEABT		; Checking re-enters?
	 SKIPE	XCTING		; Yes, re-enter typed?
	  JRST	CPYA2A		; Continue copying

; Here if a re-enter was typed. Now copy chars until we copy an
;end of line character. At that point we can return and let
;top level handle it.

CPYA.2:	MOVE	T1,P3		; Get the input FDB
	PUSHJ	P,F$READ	; Read a character
	 JRST	[CAXE	T1,$FEEOF	; Check for end of file
		  PJRST	F$ERR		; No, error
		PJRST	.POPJ1]		; Yes, give skip return
	MOVE	T1,P2		; Get the output FDB
	PUSHJ	P,F$WRIT	; Write the character
	 PJRST	F$ERR		; Couldn't
	PUSHJ	P,CKEOL		; Line terminator?
	 JRST	CPYA.2		; No, keep copying
	POPJ	P,		; Yes, return
	SUBTTL	F$CHKP - Checkpoint a file

;+
;.hl1 F$CHKP
; This routine is used to checkpoint an output file to avoid losing the
;file in the event of a system crash.
;.lit
;
; Usage:
;	MOVEI	T1,FDB
;	PUSHJ	P,F$CHKP
;	 (return here always)
;
;.end lit
;-

F$CHKP:	SKPOPN	0(T1)		; If the file open?
	 POPJ	P,		; No, just return
	$SAVE	<P1,P2>		; Save some ac's
	MOVX	P1,FD.OUT	; Check if this is an output file
	TDNN	P1,.FDFLG(T1)	; Is it?
	 POPJ	P,		; No, just return now
	MOVX	P2,<INSVL.(.FOURB,FO.FNC)> ; Get the function code
	LOAD.	P1,FDBCHN,(T1)	; Get the channel number
	STOR	P1,P2,FO.CHN	; Store it
	MOVX	P1,<XWD 1,P2>	; Get the FILOP. args
	FILOP.	P1,		; And attempt to checkpoint the file
	 JFCL			; Ignore the error
	POPJ	P,		; And return
	SUBTTL	F$CLOS - This routine will close a file

;+
;.HL1 F$CLOS
;This routine will close a file.
;.literal
;
; Usage:
;	MOVEI	T1,FDB
;	PUSHJ	P,F$CLOS
;	(Normal return)
;
; Normal return:
;	Nothing
;.end literal
;-

F$CLOS:	MOVEM	T1,LASFDB	; Store the last FDB
	MOVE	T2,.FDFLG(T1)	; Get the flags
	TXNN	T2,FD.OPN	; File open?
	 PJRST	F$GRET		; No, give a good return
	TXNE	T2,FD.ENQ	; File ENQ.ed?
	 PUSHJ	P,F$DEQ		; It is, DEQ. it first
	TXNE	T2,FD.TMP	; File from TMPCOR?
	 JRST	F$CL.1		; Yes, don't really need to close anything
	MOVX	T3,.FOREL	; Release the channel
	LOAD.	T2,FDBCHN,(T1)	; Get the channel
	STOR	T2,T3+.FOFNC,FO.CHN ; Store the channel
	MOVE	T2,[XWD 1,T3]	; Get the arugment pointer
	FILOP.	T2,		; Do it
	 JRST	F$CL.E		; Can't do it
F$CL.2:	BITOFF	T2,FD.OPN,.FDFLG(T1) ; Clear the file is opened bit
	PUSH	P,T1		; Save T1
	LOAD.	T1,FDBBUF,(T1)	; Get the buffer address
	JUMPE	T1,.+2		; No buffer?
	 PUSHJ	P,M$RBLK	; Return the block
	POP	P,T1		; Restore T1
	LOAD	T2,.FDFLG(T1),FD.TMD ; Check if TMP file being written to disk
	JMPF	T2,F$GRET	; And give a skip return
	PUSH	P,T1		; Yes, save T1
	LOADS.	T1,FDBNAM,(T1)	; Get the name swapped
	ANDX	T1,LH.ALF	; Keep only the left half
	SETZ	T2,		; Clear the IOWD
	MOVX	T3,<XWD .TCRDF,T1> ; Get the function
	TMPCOR	T3,		; And delete the TMPCOR file
	 JFCL			; Ignore the error
	POP	P,T1		; Restore T1
	PJRST	F$GRET		; And return

F$CL.E:	STOR.	T2,FDBIOS,(T1)	; Store the iostat
	MOVX	T1,$FEMON	; Get the error code
	POPJ	P,		; Return

F$CL.1:	TXNN	T2,FD.OUT	; Output file to TMP:?
	 JRST	F$CL.2		; No, just return the buffer
	LOAD.	T2,FDBMOD,(T1)	; Get the I/O mode
	CAXN	T2,$FMDMP	; /MODE:DUMP?
	 JRST	F$CL.2		; Yes, file is already written
	PUSH	P,T1		; Yes, save T1
	LOAD.	T2,FDBBUF,(T1)	; Get the buffer address
	PUSH	P,T2		; Save the address
	SUBI	T2,@.FDBRH+.BFPTR(T1) ; And make the number of words used
	SOJ	T2,		; Plus one
	HRLI	T2,(T2)		; Put in the left half
	HRR	T2,(P)		; Get the address back
	SOJ	T2,		; Fix to an iowd
	POP	P,(P)		; Remove from the stack
	LOAD.	T1,FDBNAM,(T1)	; Get the file name
	MOVX	T3,<XWD .TCRWF,T1> ; Get the function
	TMPCOR	T3,		; And write the file
	 JRST	F$CL.3		; Couldn't, write it on disk
	POP	P,T1		; Restore the FDB address
	JRST	F$CL.2		; Go return the buffer

F$CL.3:	POP	P,T1		; Get the FDB address back
	PUSHJ	P,F$OB.T	; Change the file to being written to disk
	 PJRST	F$ERR		; Couldn't
	JRST	F$CLOS		; And start over again
	SUBTTL	F$RSET - This routine will reset an I/O channel

;+
;.hl1 F$RSET
; This routine will reset the I/O channel associated with the given
;FDB. It will cause the output file to be deleted, leaving the previous
;copy if there is one.
;.b.literal
; Usage:
;	MOVE	T1,FDB.address
;	PUSHJ	P,F$RSET
;	(normal return)
;
;.end literal
;-

F$RSET:	MOVX	T2,FD.ENQ	; Check if file
	TDNE	T2,.FDFLG(T1)	; was ENQ.'ed
	 PUSHJ	P,F$DEQ		; Yes, DEQ. it
	LOAD.	T2,FDBCHN,(T1)	; Get the channel
	JUMPE	T2,.POPJ	; Just return if zero
	RESDV.	T2,		; Clear the channel
	 JFCL			; Ignore the error
	BITOFF	T2,FD.OPN,.FDFLG(T1) ; Clear the open bit
	LOAD.	T2,FDBBUF,(T1)	; Get the address if one
	JUMPE	T2,.POPJ	; Return if no buffers
	ZERO.	,FDBBUF,(T1)	; Clear the address of the buffers
	MOVE	T1,T2		; Copy to the correct place
	PJRST	M$RBLK		; Return the block to the free core list
	SUBTTL	F$USET - Do a USETI/USETO FILOP

;+
;.HL1 F$USET
;This routine will do a USET? FILOP function.
;.literal
;
; Usage:
;	MOVEI	T1,FDB.address
;	MOVE	T2,Block number
;	PUSHJ	P,F$USET
;	(Error return)
;	(Normal return)
;.end literal
;-

F$USET:	MOVEM	T1,LASFDB	; Store the last FDB
	MOVEM	T2,T3+.FOIOS	; Store the block number in the IOS word
	CFME.	T3,FDBBLK,(T1),T2 ; At the right block
	 JRST	F$US.2		; Yes, go set the buffer header
	CFML.	T3,FDBBLK,(T1),T2 ; Are we before the correct block?
	 JRST	F$US.1		; No, go do the USETx

; Here to set the buffer header

F$US.2:
F$US.1:	MOVE	T2,.FDFLG(T1)	; Get the flags
	TXNN	T2,FD.OPN	; Is this file opened ?
	  JRST	[MOVX	T1,$FEOPN	; Get the error code
		POPJ	P,]		; Return
	MOVX	T3,.FOUSI	; Assume USETI function
	TXNE	T2,FD.OUT	; Open for output
	 MOVX	T3,.FOUSO	; Yes - Make it a USETO
	LOAD.	T2,FDBCHN,(T1)	; Get the channel
	STOR	T2,T3+.FOFNC,FO.CHN ; Store the channel
	MOVE	T2,[XWD 2,T3]	; Get the argument block pointer
	FILOP.	T2,		; Do the FILOP.
	 JRST	F$US.E		; Error
	CFXN.	T2,FDBMOD,(T1),$FMDMP ; /MODE:DUMP?
	 PJRST	F$GRET		; Yes, all done
	MOVX	T2,.FOWAT	; Get the function
	STOR	T2,T3+.FOFNC,FO.FNC ; Store it
	MOVE	T2,[XWD 1,T3]	; Get the pointer
	FILOP.	T2,		; Wait for I/O to stop
	 JRST	F$US.E		; Couldn't
	MOVX	T2,BF.IOU	; Get the use bit
	MOVE	T4,.FDBRH+.BFADR(T1) ; Get the address of the first
	TXNE	T4,BF.VBR	; Is this a virgin ring ?
	 JRST	F$US.4		; No, 
F$US.3:	ANDCAM	T2,.BFHDR-1(T4)	; Clear the bit
	LOAD	T4,.BFHDR-1(T4),BF.NBA ; Get the address of the next buffer
				;  address
	CAME	T4,.FDBRH+.BFADR(T1) ; At the end ?
	  JRST	F$US.3		; No - Advance to the next buffer
	BITON	T2,BF.VBR,.FDBRH+.BFADR(T1) ; Magic a new virgin ring
F$US.4:	MOVX	T2,.FOINP	; Get the function
	STOR	T2,T3+.FOFNC,FO.FNC ; Store the functon
	MOVE	T2,[XWD 1,T3]	; Get the argument block addrss
	FILOP.	T2,		; Do the FILOP
	  JRST	F$US.E		; Failed
	JRST	F$GRET		; Return
F$US.E:	STOR.	T2,FDBIOS,(T1)	; Store the I/O status
	MOVX	T1,$FEMON	; Make this a monitor error
	POPJ	P,		; Return to the caller
	SUBTTL	F$RENM - Rename a file

;+
;.HL1 F$RENM
;This routine will rename one file to another name.
;.literal
;
; Usage:
;	MOVEI	T1,FD.block	; From file decsriptor
;	MOVEI	T2,FD.block	; To file decsriptor
;	PUSHJ	P,F$RENM
;	(Error return)
;	(Normal return)
;
; Error return:
;	T1 -  Error code
; Normal return:
;	Returns nothing.
;.end lteral
;-

F$RENM:	MOVEM	T1,LASFDB	; Store the last FDB
	$SAVE	<P1,P2>		; Save P1 and P2
	DMOVE	P1,T1		; Copy the arguments
	STORE	T1,OPNBEG,OPNEND,0 ; Clear the OPEN areas
	MOVX	T1,FO.ASC!FO.PRV!.FORNM ; Get the function and flags
	CFXN.	,FDBNAM,(P2),0	; Really a rename?
	 MOVX	T1,FO.ASC!FO.PRV!.FODLT ; No, delete the file
	MOVEM	T1,FLP+.FOFNC	; Store it
	MOVX	T1,.IOASC	; Do this in ASCII mode
	MOVEM	T1,FLP+.FOIOS	; Store it
	MOVE	T1,P1		; Get the "from" FDB address
	PUSHJ	P,FIXDEV	; Fix up the name and path
	MOVE	T1,P2		; Get the other FDB
	PUSHJ	P,FIXDEV	; And fix it
	LOAD.	T1,FDBDEV,(P1)	; Get the device name
	CFXN.	,FDBNAM,(P2),0	; Delete instead?
	 STOR.	T1,FDBDEV,(P2)	; Yes, use the same device
	CFME.	T2,FDBDEV,(P2),T1 ; See if the devices are the same
	  JRST	[MOVX	T1,$FECRS	; ++ Can not rename across strs
		POPJ	P,]		; Return to the caller
	MOVEM	T1,FLP+.FODEV	; Store the device name
	MOVE	T1,[XWD REN,ELB] ; Get the RENAME/LOOKUP block
	MOVEM	T1,FLP+.FOLEB	; Store in the FILOP. block
	MOVEI	T4,PTH		; Get the address of the path block
	MOVEI	T3,ELB		; Get the LOOKUP/ENTER block address
	PUSHJ	P,RENSUB	; Fill in the block
	EXCH	P1,P2		; Move the blocks around
	MOVEI	T4,RPTH		; Get the rename path
	MOVEI	T3,REN		; Get the RENAME block address
	PUSHJ	P,RENSUB	; Fill in the block
	MOVE	T1,[XWD .FOLEN,FLP] ; Get the argument pointer
	FILOP.	T1,		; Rename the file
	 JRST	F$RN.1		; Couldn't
	LOAD	T1,FLP+.FOFNC,FO.CHN ; Get the channel number
	RESDV.	T1,		; And clear the channel
	 JFCL			; Couldn't
	JRST	F$GRET		; Return

F$RN.1:	LOAD	T2,FLP+.FOFNC,FO.CHN ; Get the channel number
	RESDV.	T2,		; Reset the channel
	 JFCL			; Ignore it
	POPJ	P,		; Give the error return
	SUBTTL	FIXDEV - Routine to fix up a device name and path

;+
;.hl1 FIXDEV
; This routine is used to fix up the device name and path in an
;FDB to be a device name which can be checked for a rename.
;.b.literal
; Usage:
;	MOVEI	T1,FDB.addres
;	PUSHJ	P,FIXDEV
;	 (return here)
;
;.end literal
;-

FIXDEV:	STORE	T2,PTH,PTH+.PTMAX,0 ; Clear the PATH block
	LOAD.	T2,FDBDEV,(T1)	; Get the device
	JUMPE	T2,.POPJ	; Return if no device
	MOVEM	T2,PTH+.PTSTR	; Store in the PATH block
	MOVEI	T2,PTH		; Get the address of the block
	PATH.	T2,		; Get the path for the device
	 POPJ	P,		; Return
	LOAD	T2,PTH+.PTSWT,PT.IPP ; Implied PPN ?
	JMPF	T2,.POPJ	; Jump if false
	BITON	T2,FD.PTH,.FDFLG(P1) ; Flag we have a path
	$SAVE	<P1>		; Save P1
	MOVE	P1,[XWD -.PTMAX-.PTPPN,PTH+.PTPPN] ; Get the AOBJN pointer
	HRRI	T2,.FDPPN(T1)	; Get the address to store into
	MOVE	T3,(P1)		; Copy the items
	MOVEM	T3,(T2)		; . . .
	ADDI	T2,1		; Point to next entry in the FDB
	AOBJN	P1,.-2		; Loop

	LOAD	T2,PTH+.PTSWT,PT.SLT ; Get the search list
	MOVE	T2,SLTBL(T2)	; Get the search list name
	STOR.	T2,FDBDEV,(T1)	; Store it
	POPJ	P,		; Return

; Here is the table of search list names

	.PTSLMAX==<1_WID(PT.SLT)>-1
TABDEF	SL,.PTSL,<SIXBIT /DSK/>
	TABENT	J,<SIXBIT /DSK/> ; Job
	TABENT	A,<SIXBIT /ALL/> ; ALL:
	TABENT	S,<SIXBIT /SSL/> ; SSL:
TABEND
	SUBTTL	Subroutines -- RENSUB

;+
;.HL1 RENSUB
;This routine will fill in a path, and lookup block from a FDB block.
;.literal
;
; Usage:
;	MOVEI	T3,ELB
;	MOVEI	T4,PTH
;	MOVEI	P1,FDB
;	PUSHJ	P,RENSUB
;	(Return)
;-

RENSUB:	LOAD	T1,.FDFLG(P1),FD.PTH ; Get the flag
	JMPF	T1,RENS.2	; If not path then skip this
	MOVEM	T4,.RBPPN(T3)	; Store the pointer
	MOVSI	T1,-.PTMAX-.PTPPN ; Get the number of times to loop
	HRRI	T1,.FDPPN(P1)	; Get the address
	MOVEI	T2,.PTPPN-1(T4)	; Get the first address
RENS.1:	PUSH	T2,(T1)		; Move the item
	AOBJN	T1,RENS.1	; Loop for all the items
	MOVX	T2,.PTSCY	; Assume /SCAN
	MOVX	T1,FD.SCN	; Check the flag
	TDNN	T1,.FDFLG(P1)	;  .  .  .
	 SETZ	T2,		; No, let it as is
	STOR	T2,.PTSWT(T4),PT.SCN ; store the switch
RENS.2:	LOAD.	T1,FDBNAM,(P1)	; Get the file name
	MOVEM	T1,.RBNAM(T3)	; Store ite
	LOADS.	T1,FDBEXT,(P1)	; Get the extensions
	MOVEM	T1,.RBEXT(T3)	; Store it
	LOAD.	T1,FDBPRO,(P1)	; Get the protection
	STOR	T1,.RBPRV(T3),RB.PRV ; Store in
	LOAD.	T1,FDBVER,(P1)	; Get the version number
	MOVEM	T1,.RBVER(T3)	; Store it
	MOVX	T1,.RBLEN	; Get the length of the block
	MOVEM	T1,.RBCNT(T3)	; Store it
	POPJ	P,		; Return to the caller
	SUBTTL	F$ERR - Error processing for the file errors

;+
;.HL1 F$ERR
;This routine will issue the reerror message for any file errors.
;It will output the FDB of the error message if it is required.
;-

F$ERR:	$SAVE	P1		; Save P1
	MOVE	P1,T1		; Move the error code into P1
	CAXGE	P1,$FEMIN	; Is this greater than the min ?
	  JRST	F$ER.0		; Yes - Skip this
	CAXLE	P1,$FEMAX	; Less then or equal to the max ?
	 STOPCD	(ETB,<Error code too big>)
	SKIPE	FERTBL-$FEMIN(P1) ; Have an error message ?
	 JRST	FERTBL-$FEMIN(P1) ; Yes - Go issue it
	CAXE	P1,$FEMON	; Is this a monitor error
	 STOPCD	(UEC,<Unknown error code>)


; Here to process I/O errors

	MOVE	P1,LASFDB	; Get the address of the ast FDB
	LOAD.	P1,FDBIOS,(P1)	; Get the I/O status from the FDB
	ERROR	E.IOE		; Give an I/O error

; Here to handle he LOOKUP/ENTER errors

F$ER.0:	CAXGE	P1,ERMAX%	; Is this less than the max ?
	 JRST	LERTBL(P1)	; Dispath to the table
	ERROR	E.UME		; ++ Unknown monitor error

; Define the table of errors for the error code

DEFINE	FER(XXX,YYY),<IFB <YYY>,<ERROR	E.'XXX>
			IFNB <YYY>,<EXP 0>>

FERTBL:	FERRORS			; Define the errors

; LOOKUP/ERROR table
	DEFINE ENTERR<
ERR$(0,FNF,<File not found>)
ERR$(1,IPP,<No UFD for Project-Programmer Number>)
ERR$(2,PRT,<Protection Failure>)
ERR$(3,FBM,<File being modified>)
ERR$(4,AEF,<File already exists>)
ERR$(5,ISU,<Illegal sequence of UUO's>)
ERR$(6,TRN,<Transmission error>)
ERR$(7,NSF,<Not a SAVE file>)
ERR$(10,NEC,<Not enough core>)
ERR$(11,DNA,<Device not available>)
ERR$(12,NSD,<No such device>)
ERR$(13,ILU,<Illegal UUO.  No two-register relocation>)
ERR$(14,NRM,<No room or quota exceeded on this file structure>)
ERR$(15,WLK,<File structure is write-locked>)
ERR$(16,NET,<Not enough monitor table space>)
ERR$(17,POA,<Partial allocation>)
ERR$(20,BNF,<Block not free>)
ERR$(21,CSD,<Cannot supersede a directory>)
ERR$(22,DNE,<CAnnot delete a non-empty directory>)
ERR$(23,SNF,<SFD not found>)
ERR$(24,SLE,<Search list empty>)
ERR$(25,LVL,<SFD nested too deeply>)
ERR$(26,NCE,<No create bit on on all structures>)
ERR$(27,SNS,<Segment not on swapping space>)
ERR$(30,FCU,<Cannot update file>)
ERR$(31,LOH,<Low segment overlaps high segment>)
ERR$(32,NLI,<CAnnot run program when not logged in>)
ERR$(33,ENQ,<File still has outstanding locks set>)
ERR$(34,BED,<Bad .EXE directory>)
ERR$(35,BEE,<Bad extension for .EXE file>)
ERR$(36,DTB,<Directory too big for .EXE file>)
ERR$(37,ENC,<TSK - Exceeded network capacity>)
ERR$(40,TNA,<TSK - Task not available>)
ERR$(41,UNN,<TSK - Undefined network node>)
>;end of enterr macro

DEFINE	ERR$(NN,XXX,TEXT),<ERROR E.'XXX>

LERTBL:	ENTERR
ERMAX%==.-LERTBL		; Define the max
	SUBTTL	CHKFFI - Check if file is found in a different area

;+
;.hl1 CHKFFI
; This routine will check if the file was found in a PATH different
;area than expected.
;If it is not, it will issue a TECFFI warning message.
;-


	F..FFI==1B0		; File found in message issued
	F..NDP==1B1		; File is not in default path


CHKFFI:	SKIPN	FPTH+.PTSTR	; Get the structure name we found the file on
	 PJRST	FFIOFF		; None, must not be a disk
	LOAD.	T1,FDBDEV,(P2)	; Store the device name
	MOVEM	T1,RPTH+.PTSTR	; Save the device name
	MOVE	T1,[XWD .PTMAX,RPTH] ; Get the pointer
	PATH.	T1,		; Get the info for the device
	 JFCL			; Don't worry about it
	MOVE	T1,FPTH+.PTSTR	; Get the device where we found this thing
	STOR.	T1,FDBDEV,(P2)	; Store for typeout
	LOAD	T1,RPTH+.PTSWT,PT.IPP ; Check if implied PPN
	MOVX	T2,FD.PTH	; Check if path given
	TDNN	T2,.FDFLG(P2)	;  .  .  .
	 JMPT	T1,CHKF.5	; No, if implied PPN, must check against assumed path
CHKF.0:	MOVEI	T3,.FDPPN(P2)	; Get the address of the path block
	MOVX	T1,<XWD -<.PTMAX-.PTPPN>,.PTPPN> ; Get the pointer for the blocks
	TXZ	P2,F..FFI!F..NDP ; Clear the two flags

CHKF.1:	MOVE	T2,FPTH(T1)	; Get the path where the file was
	CAME	T2,PTH(T1)	; Where we wanted it?
	 TXO	P2,F..FFI	; No, flag we need the message
	CAME	T2,MYPATH(T1)	; In default path yet?
	 TXO	P2,F..NDP	; No, remember that for later
	ADDI	T3,1		; Bump the pointer
	JUMPE	T2,.+2		; Skip if we hit the last one
	 AOBJN	T1,CHKF.1	; Loop for all SFD's

	HRRZM	P2,FFIFDB	; Save the FDB address
	SKIPN	PTH+.PTPPN	; Using default path for lookup?
	 TXNE	P2,F..NDP	; Yes, is the place we found the file the default path?
	  TXNN	P2,F..FFI	; Is the file found flag on ?
	   JRST	CHKF.4		; Don't need the message
	TXNN	F,F.SFFI	; Suppressing this ?
	 WARN	E.FFI		; ++ File found in ....
CHKF.4:	MOVX	T1,FD.NDP	; Get the not in default path flag
	TXNE	P2,F..NDP	; Is it there?
	 IORM	T1,.FDFLG(P2)	; No, flag the fact for the caller

	BITON	T1,FD.PTH,.FDFLG(P2) ; Light the path flag

CHKF.2:	TXZ	P2,F..FFI!F..NDP ; Clear the flags
	MOVEI	T2,.FDPPN(P2)	; Get the path block again
	MOVX	T1,<XWD -<.PTMAX-.PTPPN>,.PTPPN> ; And the pointer

CHKF.3:	MOVE	T3,FPTH(T1)	; Get the found path
	MOVEM	T3,(T2)		; Store it
	AOJ	T2,		; Bump this one
	JUMPE	T3,FFIOFF	; Done?
	AOBJN	T1,CHKF.3	; Loop for the rest
FFIOFF:	TXZ	F,F.SFFI	; Clear the FFI flag
	POPJ	P,		; And return

; Here if the a device name with an implied path was given. Move the
;info so the check is if the file was realy found where expected.

CHKF.5:	MOVE	T1,[XWD RPTH,PTH] ; Get the BLT pointer
	BLT	T1,PTH+.PTMAX-1	; And move the info
	JRST	CHKF.0		; And go do the checks

FFIPTH:	Z	FPTH		; Point to the path block
	SUBTTL	Low segment for TECFIL

; The following is the low segment for the common routines

	$IMPURE			; Data segment
	LOWVER(FIL,2)		; Low segment version number

F$BZER:!			; Beginning of the area to clear

OPNBEG:!			; Start of the place to clear on an OPEN

ELB:	BLOCK	.RBLEN+1	; ENTER/LOOKUP block
FLP:	BLOCK	.FOLEN		; FILOP block

PTH:	BLOCK	.PTMAX		; Path block for F$OPEN
REN:	BLOCK	.RBLEN+1	; Rename block
RPTH:	BLOCK	.PTMAX		; Rename path block
FPTH:	BLOCK	.PTMAX		; Path the file was found in

OPNEND==.-1			; End of the open block area

; Storage for F$RBUF

LFCNT:	BLOCK	1		; Max number of line feeds to read
FFCNT:	BLOCK	1		; Max number of form feeds to read
CHRCNT:	BLOCK	1		; Number of characters to expand buffer by
				; (Read up to 2/3 this many chars)
ENDYNK:	BLOCK	1		; Place to terminate read if a line feed is seen
STPYNK:	BLOCK	1		; Place to terminate read regardless.

MYPPN:	BLOCK	1		; This job's PPN
MYNODE:	BLOCK	1		; The node this job is on
MYPATH:	BLOCK	.PTMAX		; Default path area
MYJOB:	BLOCK	1		; This job's job number
REEABT:	BLOCK	1		; Abort on re-renter flag for F$COPY

PRSSTK:	BLOCK	1		; Stack pointer on call to F$PARS
ERRRTN:	BLOCK	1		; Address of the error routine
RTN:	BLOCK	1		; Input routine
SAVRTN:	BLOCK	1		; Save location
FDAEM:	BLOCK	1		; FILDAE is running flag
OJBFF:	BLOCK	1		; Saved .JBFF over opening the file

; Last items for error message printing.

LASFDB:	BLOCK	1		; Address of the last FDB
LASSWT:	BLOCK	1		; Last switch seen
LASSWP:	BLOCK	1		; Pointer to last switch table used
LASSXB:	BLOCK	1		; Last sixbit item input
LASOCT:	BLOCK	1		; Last octal number input

FFIFDB:	BLOCK	1		; FDB address for FFI message

; Switch processing information

SWTIMD:	BLOCK	1		; Input mode
SWTOMD:	BLOCK	1		; Output mode

VERBEG:!
VERMAJ:	BLOCK	1		; Major version number
VERMIN:	BLOCK	1		; Minor version number
VEREDT:	BLOCK	1		; Edit level
VERWHO:	BLOCK	1		; Who version number
VEREND==.-1			; End of version number storage

; ENQ/DEQ storage

ENQBEG:!			; Start of the ENQ/DEQ area
ENQHDR:	BLOCK	.ENQRI+1+.ENQPS+1 ; ENQ/DEQ header
ENQBLK=ENQHDR+.ENQRI+1	; ENQ/DEQ block
STRING:	BLOCK	20		; Area for the building of the string
ENQEND==.-1			; End of the ENQ/DEQ area

F$EZER==.-1			; End of the area to clear
	SUBTTL	End of TECFIL

	END			; End of TECFIL