Google
 

Trailing-Edge - PDP-10 Archives - BB-D480C-SB_1981 - forio.mac
There are 25 other files named forio.mac in the archive. Click here to see a list.
	SEARCH	FORPRM
	TV	FORIO	I/O ROUTINES,6(2033)

;COPYRIGHT (C) 1981  BY  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS.

;THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED
;ONLY  IN  ACCORDANCE  WITH  THE  TERMS  OF  SUCH LICENSE AND WITH THE
;INCLUSION OF THE ABOVE COPYRIGHT NOTICE.  THIS SOFTWARE OR ANY  OTHER
;COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY
;OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF THE  SOFTWARE  IS  HEREBY
;TRANSFERRED.

;THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT  NOTICE
;AND  SHOULD  NOT  BE  CONSTRUED  AS A COMMITMENT BY DIGITAL EQUIPMENT
;CORPORATION.

;DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY  OF  ITS
;SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.

COMMENT \

***** Begin Revision History *****

1100	CKS	5-Jun-79
	New

1263	JLC	09-Feb-81	QAR 10-05487
	Fix -10 backspace. SOJLE should be SOJGE.

1272	DAW	19-Feb-81
	A few low-risk changes to support extended addressing.

1303	JLC	25-Feb-81
	Folded -10 code for %irec into the -20 code for DIREC, made
	it call %SAVE4 instead of %SAVE3 as it was clobbering P4.
	Save line sequence number in the DDB.

1306	DAW	26-Feb-81
	New arg-list format from %SAVE.

1310	DAW	26-Feb-81
	Full words in DDB for END=, ERR=, IOST=, AVAR= addresses.

1316	JLC	5-Mar-81
	Major changes for magtape handling, switching i/o direction.

1325	JLC	9-Mar-81
	Yet more changes to magtape I/O on the -10

1332	JLC	11-Mar-81
	Installed dump mode I/O for the -10.

1333	JLC	11-Mar-81
	Fix to backspace over eof.

1336	JLC	12-Mar-81
	Fix to dump mode I/O, removed extraneous saves of pntr/count
	for the -10, added one where crucial (just before FILOP in
	binary I/O).

1341	JLC	13-Mar-81
	Add code to handle rewind of file that hasn't been opened
	yet for the -10.

1343	DAW	16-Mar-81
	A few changes for extended addressing.

1346	JLC	16-Mar-81
	Fixes to -10 backspace file, skip file, and rewind.

1353	JLC	18-Mar-81
	More fixes for -10 magtape ops. BAKEOF was not backspacing
	over the last block of the file.

1357	JLC	19-Mar-81
	More fixes to magtape ops. BAKEOF was not handling null
	files correctly. Installed code to prevent creation of
	null file for rewind, skip file, backfile, or unload with
	no OPEN.

1360	EDS	19-Mar-81	Q10-05866
	Range check UNIT numbers used in I/O statements.

1361	JLC	20-Mar-81
	Fix some typos in code to prevent null file creation.

1363	JLC	24-Mar-81
	Minor fixes to magtape and error typout,
	added missing global (%iset) called from foropn.

1365	JLC	25-Mar-81
	Typo in WAIT FILOP.

1366	JLC	26-Mar-81
	Still more typos, plus BAKEOF bug, plus END FILE was not
	incrementing the block #.

1374	JLC	31-Mar-81
	Replace code to turn off D%END for terminals. Previous code
	was wiping T1, which contained valuable data.

1376	JLC	31-Mar-81
	Fix -10 backspace code to eliminate cache-sweep bugs for
	SMP (removed clearing of use-bits).

1377	JLC	01-Apr-81
	Change load/store FLGS to move/movem FLAGS, since it was
	a full word. Minor fix to -10 backspace. Minor changes
	to UNFO, moved check for empty window from end of loop
	to beginning of BLT code.

1401	JLC	30-Apr-81
	Put back code to clear use-bits, was merely masking another
	bug.

1402	JLC	06-Apr-81
	Transplant input record initialization to where it belongs,
	new subroutine called %IRINI. Move setting of CRLF suppression
	to %IREC.

1406	JLC	06-Apr-81
	Minor bug in backspace for -10, backspace beyond block 1
	sometimes would not work.

1410	JLC	07-Apr-81
	Move record buffer setup to %IRINI and %ORINI in preparation
	for separation of record buffer. Modify and separate EXPRB
	for same preparation.

1411	DAW	08-Apr-81
	Use IJFN and OJFN instead of JFN.

1412	JLC	09-Apr-81
	Fix minor problem reading fixed-length record files. Fix
	backspace for the -20 for fixed-length record files.

1413	DAW	10-Apr-81
	Get rid of flag D%MTOP. FOROTS doesn't need to check
	whether or not its doing a magtape operation on every IO
	statement.

1414	DAW	10-Apr-81
	MTOP operations were ignoring ERR=.

1416	JLC	10-Apr-81
	Separate record buffers. Install DTA rewind and unload.

1422	JLC	13-Apr-81
	Typo in separate record buffers.

1423	DAW	13-Apr-81
	Put %SETD in FORIO (was in FOROPN).

1424	JLC	14-Apr-81
	Typo in %IRINI made DECODE non-existent.

1427	JLC	15-Apr-81
	Changed RSIZ to be a word in the DDB. Make FORIO
	ignore MODE=DUMP if ACCESS=RANDOM.

1430	JLC	15-Apr-81
	Typo in -20 backspace broke it.

1432	JLC	16-Apr-81
	Was trashing returned AC T3 in DIREC. Changed code to return
	result in IRCNT(D) instead.

1433	JLC/CKS	16-Apr-81
	Fix for binary backspace.

1435	CKS	16-Apr-81
	More binary backspace fixes.

1436	JLC	16-Apr-81
	More of edit 1432. Return result in IRCNT for DECODE also.

1443	JLC	17-Apr-81
	Make EOFN(D) represent fixed number of bytes in file. EOF
	detected by comparing BYTN with EOFN.

1444	JLC	21-Apr-81
	Fix bug caused by edit 1443; it was smashing T1.

1445	DAW	21-Apr-81
	Rework code around UNFSW to make it more understandable.

1450	JLC	22-Apr-81
	Fix DECODE new record code.

1451	JLC	23-Apr-81
	Special code for dump mode I/O in mtops.

1453	JLC	24-Apr-81
	Make dump mode backspace and skiprecord work for magtape.
	Insert if20 end after EOFN setup code.

1454	JLC	24-Apr-81	QAR 20-01364
	Change EOFN if we switch from formatted to unformatted.

1455	JLC	27-Apr-81
	Fix bug from edit 1452. Must not set D%LIN/D%LOUT on the way
	out of magtape operations.

1460	JLC	28-Apr-81
	Fix typo in edit 1453. It thought most files were dump mode.

1463	JLC	7-May-81
	Many major changes. See FOROTS.MAC revhist.

1464	DAW	21-May-81
	Error messages.

1465	JLC	15-May-81
	Major changes to -20 I/O.

1474	JLC	22-May-81
	Bug in %PTOF, thought WSIZ was in words, was in bytes.

1476	JLC	26-May-81
	Bug in unformatted I/O, was looking at EOFN for non-disk files.

1501	JLC	27-May-81
	More bugs, this time in random I/O, caused by changed calling
	sequence for MAPW.

1502	JLC	28-May-81
	Install defensive WAIT operations in magtape code.

1505	JLC	01-Jun-81
	Many bug fixes in disk and magtape backspace operations.
	Turn off EOF and initialize things for BACKFILE and
	SKIPFILE.

1506	CKS	2-Jun-81
	Add SLST77 and ELST77, temporarily equated to F-66 equivalents,
	SLIST and ELIST.

1511	JLC	5-Jun-81
	More edits to magtape code, for SKIPFILE and BACKFILE.

1516	JLC	10-Jun-81
	Yet another bug, this time in disk backspace. WSIZ is not
	in words! Fix end-of-record handling for unformatted I/O.

1532	DAW	14-Jul-81
	OPEN rewrite: Base level 1

1535	JLC	14-Jul-81
	EOF handling correction, both to zero arrays correctly
	and to handle IOSTAT correctly.

1536	JLC	15-Jul-81
	Minor edits.

1542	JLC	17-Jul-81
	Fix ERR and END processing and %MVBLK.

1546	JLC	20-Jul-81
	Fix DEC% to call DECINI. Fix TIREC for -20 to allocate
	record buffer if none there yet.

1547	DAW	20-Jul-81
	Replacement for old %CHKDR routine.

1550	JLC	20-Jul-81
	Fix DECODE, it had off-by-one error. Fix X format, it referenced
	stuff in DDB without D. Fix setup of record buffers - make sure
	it happens in %ORINI or %IRINI.

1553	JLC	23-Jul-81
	Fix ENCODE and DECODE again. Setup IRPTR properly in TIREC.
	Eliminate useless routine ENCINX.

1560	DAW	28-Jul-81
	OPEN rewrite: Base level 2

1567	JLC	30-Jul-81
	More fixes to ENCODE/DECODE, output buffer setup, prompting.

1572	JLC	31-Jul-81
	ENCODE - setup entire string with blanks for initialization.

1574	BL	3-Aug-81
	Missing IF20 conditional around G.PRP

1575	JLC	05-Aug-81
	Fix record zeroing again.

1577	DAW	11-Aug-81
	Create "ENDNUL" routine to make the "drop a null at EOR"
	hack work correctly.

1601	DAW	12-Aug-81
	ENDFILE to a disk file was trying to open it for input on the -20.

1604	DAW	12-Aug-81
	More of 1601-type stuff, for DIVERT.

1607	DAW	13-Aug-81
	Fix bug in FIND code.

1613	JLC	19-Aug-81	Q10-6390
	Use a character count, not a word count, in backspace of
	ASCII files.

1614	JLC	19-Aug-81
	Move setting of D%MOD into DOREC. Remove END= branching
	for end-of-record for files with no crlf at end, thus
	EOF becomes a valid record terminator, and the program
	will get END= at the next read.

1622	JLC	21-Aug-81
	Rewrite ENCODE/DECODE again, rework record positioning
	subroutines for output, so that X, T format reaaly work.

1625	DAW	21-Aug-81
	Get rid of "DF".

1627	DAW	24-Aug-81
	On TOPS-20, UNLOAD and REWIND no longer need a filename

1630	JLC	24-Aug-81
	Make illegal operations on magtape a fatal error.

1631	DAW	24-Aug-81
	Set D%MOD in UNFO.

1632	JLC	24-Aug-81
	Fixed OPAD to output spaces instead of random trash for X and
	T format.

1633	JLC	25-Aug-81
	On TOPS-20, SKIPFILE and BACKFILE no longer need a filename.

1634	JLC	25-Aug-81
	ORPOS was left set at FIXREC, caused records to be too large.

1635	JLC	25-Aug-81
	Fix for edit 1633, plus ENDFILE can't work that way.

1637	JLC	26-Aug-81
	DECODE bug. IRCNT was not getting set up properly.

1642	JLC	27-Aug-81
	Replace %FILOP calls with FILOPs.

1643	JLC	27-Aug-81
	More code for faster EOL handling. Modify IRBUF/ORBUF to be full
	words so ENCODE/DECODE will work with extended addressing.

1645	DAW	28-Aug-81
	Column 1 before errors in DIVERT'ed file.

1646	DAW	28-Aug-81
	DTA REWIND and UNLOAD used wrong channel.

1647	DAW	28-Aug-81
	DTA REWIND and UNLOAD to not require an existing file.

1652	DAW	1-Sep-81
	Fix DUMP mode I/O on TOPS-10; make "IOE" a "?" error.

1653	JLC	1-Sep-81
	Return -1 (illegal LSN) for non-LINED files and LINED files
	with no LSN.

1663	JLC	8-Sep-81
	Fixed ill mem read for non-existent pages in read-only file.
	Added code to record top page number, so unused pages can be
	unmapped.

1665	DAW	8-Sep-81
	Make a D.TTY hack to get error messages right; delete refs to U.TTY.

1676	DAW	9-Sep-81
	%OCRLF to always output a CRLF, and not use "U".

1702	JLC	10-Sep-81
	More fix to non-existent page stuff, unmapping unused pages.
	Add code to prevent expansion of random files on -10 by
	merely touching the page (not possible on -20).

1703	DAW	11-Sep-81
	Fix printing of too many CRLF's in errors when a TTY file is open.

1704	JLC	11-Sep-81
	Fix SETPOS not to pad a blank when we are at desired position.
	Also typo in RDW for -10 in edit 1702.

1705	JLC	11-Sep-81
	Fix more serious T-format bug. T1 was not working on output,
	as it got stored as position 0. Now ORPOS contains desired
	position of NEXT character.

1706	DAW	11-Sep-81
	Lots of changes to errors.

1707	JLC	14-Sep-81
	Edit 1705 broke %IBACK.

1712	JLC	15-Sep-81
	Fixed more bugs in t-format, created IRPOS.
	Eliminated D%ERR!

1716	JLC	16-Sep-81
	Changed the names of ISPOS, OSPOS, etc., to make things less
	confusing. Fixed typo due to confusion.

1722	JLC	16-Sep-81
	Code for IRPOS more complicated than originally envisaged.

1730	JLC	18-Sep-81
	More fixes for T-format.

1735	DAW	22-Sep-81
	-20 DISK APPEND files now get EOF for READ.

1737	DAW	23-Sep-81
	Fix processing of REREAD error "RBR".

1740	DAW	23-Sep-81
	More REREAD code.

1745	JLC	24-Sep-81
	Made IRBLN, ORBLN, and IRLEN full words. Removed all refs
	to IRPOS, now unnecessary.

1761	JLC	5-Oct-81
	Fixed ENDFILE on disk, did not open file for output before.

1774	DAW	8-Oct-81
	Avoid "?Unexpected TAPOP. error" for typical errors.

1775	JLC	9-Oct-81
	Fix ^Z handling.

1777	DAW	9-Oct-81
	FILOP. CLOSE before RELEASE where appropriate.

2005	JLC	15-Oct-81
	Fix unmapping of unused pages so it does it for random files.
	On -10, update .RBSIZ so we don't return zeroes for data
	that's there.

2006	JLC	15-Oct-81
	Control-Z change broke DECODE by meddling with IRCNT, which
	should be inviolate before the "device-dependent" call.

2010	JLC	19-Oct-81
	Make EOFN and BYTN live for the -10.

2016	JLC	20-Oct-81
	Fix SLISTs and ELISTs to differentiate between -66 and -77
	programs and give 1-trip (i.e., 1 entry) for zero-trip
	lists.

2030	JLC	27-Oct-81
	Fix SLISTs and ELISTs to substitute 1 for zero or negative
	supplied counts.

2033	DAW	19-Nov-81
	Change symbol "LTYPE" to "%LTYPE" to avoid conflict with
	user symbol.
	Give error if user tries to do random I/O without an OPEN
	statement with a RECORDSIZE specifier.
	Pay attention to ERR= and IOSTAT= for ENCODE and DECODE.
	Fix dollar format to make T and X format have some effect
	at end of record.

***** End Revision History *****

\

	ENTRY	IN%,OUT%,RTB%,WTB%,NLI%,NLO%,ENC%,DEC%,FIND%,MTOP%
	ENTRY	IOLST%,FIN%

	INTERN	%IBYTE,%OBYTE,%IBYTC
	INTERN	%IREC,%OREC,%EOREC,%OCRLF,%ORECS
	INTERN	%IBACK,%OVNUM,%GETIO,%MAPW,%SETAV
	INTERN	%RPOS,%SPOS
IF10,<	INTERN	%RANWR,%BACKB,%CLRBC,%BAKEF,%ISET  >
	INTERN	%SETD
	INTERN	%UDBAD

	EXTERN	%POPJ,%POPJ1,%SAVE1,%SAVE2,%SAVE3,%SAVE4,%SAVE,%PUSHT,%POPT,%JPOPT
	EXTERN	%FORMT,%LDI,%LDO,%NLI,%NLO
	EXTERN	%IOERR,%ABORT,%IONAM
	EXTERN	%SETIN,%SETOUT,%CHKNR,%CRLF
	EXTERN	%GTBLK,%MVBLK
	EXTERN	%ISAVE,%FSAVE,%SIZTB,%DDBTA
	EXTERN	%EDDB,U.RERD,U.ERR,D.TTY
IF20,<	EXTERN	G.PRP,G.PRMPT,%OCCOC,%CCMSK,%CLSOP >
IF10,<	EXTERN	%ST10B,%CALOF,%CLSER,%FREBLK   >
	EXTERN	%LTYPE
	EXTERN	%OPENX,%LSTBF
	EXTERN	%TERR,%TIOS
	EXTERN	%ALCHN,%DECHN

	SEGMENT	CODE
	SUBTTL	I/O SETUP

;Formatted read -- READ (u,1)
	SIXBIT	/IN./
IN%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,FMTCNV	;CONVERT ARG LIST
	MOVX	P1,0		;SET FORMATTED INPUT
	JRST	INGO		;GO DO I/O

;Formatted write -- WRITE (u,1)
	SIXBIT	/OUT./
OUT%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,FMTCNV	;CONVERT ARG LIST
	MOVX	P1,D%IO		;SET FORMATTED OUTPUT
	JRST	OUTGO		;GO DO I/O

;Unformatted read
	SIXBIT	/RTB./
RTB%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,UNFCNV	;CONVERT ARG LIST
	MOVX	P1,D%UNF	;SET UNFORMATTED INPUT
	JRST	INGO		;GO DO I/O

;Unformatted write
	SIXBIT	/WTB./
WTB%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,UNFCNV	;CONVERT ARG LIST
	MOVX	P1,D%UNF+D%IO	;SET UNFORMATTED OUTPUT
	JRST	OUTGO		;GO DO I/O

;Namelist input
	SIXBIT	/NLI./
NLI%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,NMLCNV	;CONVERT ARG LIST
	MOVX	P1,D%NML	;SET NAMELIST INPUT
	JRST	INGO		;GO DO I/O

;Namelist output
	SIXBIT	/NLO./
NLO%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,NMLCNV	;CONVERT ARG LIST
	MOVX	P1,D%NML+D%IO	;SET NAMELIST OUTPUT
	JRST	OUTGO		;GO DO I/O

;DECODE
	SIXBIT	/DEC./
DEC%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,ENCCNV	;CONVERT ARG LIST
	XMOVEI	T1,[ASCIZ /DECODE/]
	MOVEM	T1,%IONAM	;Set statement name
	PUSHJ	P,IOARG		;Move args to A.XXX
	PUSHJ	P,SETDE		;SET UP DDB POINTING TO STRING
	MOVX	T1,D%ENC	;Set ENCODE/DECODE input
	MOVEM	T1,FLAGS(D)
	MOVEM	U,%UDBAD	;SAVE DDB ADDRESS FOR IOLST
	PUSHJ	P,DECINI	;INIT BUFFER PNTR
	PUSHJ	P,%IREC		;Read record
	MOVE	T1,[%FORMT,,%FORMT] ;Set for formatted I/O
	JRST	%SIO		;Start I/O

;ENCODE
	SIXBIT	/ENC./
ENC%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,ENCCNV	;CONVERT ARG LIST
	XMOVEI	T1,[ASCIZ /ENCODE/]
	MOVEM	T1,%IONAM	;Set statement name
	PUSHJ	P,IOARG		;Move args to A.XXX
	PUSHJ	P,SETDE		;SET UP DDB POINTING TO STRING
	MOVX	T1,D%ENC+D%IO	;Set ENCODE/DECODE output
	MOVEM	T1,FLAGS(D)
	MOVEM	U,%UDBAD	;SAVE DDB ADDRESS FOR IOLST
	PUSHJ	P,ENCINI	;Init for ENCODE
	MOVE	T1,[%FORMT,,%FORMT] ;Set for formatted I/O
	JRST	%SIO		;Start I/O

	SIXBIT	/MTOP./
MTOP%:	PUSHJ	P,%SAVE		;SAVE ACS
	PUSHJ	P,MTCNV		;CONVERT ARG LIST
	XMOVEI	T1,[0]		;DON'T KNOW STATEMENT NAME YET
	MOVEM	T1,%IONAM
	PUSHJ	P,IOARG		;Move args to A.XXX
	PUSHJ	P,CHKUNT	;Check for unit number in range
				;(Goes to ABORT% if unit is bad)
	PJRST	MTOP		;OK, Go do it and return.
IOARG:	SETZM	A.UNIT		;CLEAR BLOCK SO UNSPECIFIED ARGS ARE 0
	MOVE	T1,[A.UNIT,,A.UNIT+1]
	BLT	T1,IOARGS+MAXKWD-1

ARGLP:	LDB	T1,[POINTR @%LTYPE,ARGKWD]	;GET KWD OF ARGUMENT
	JUMPE	T1,ARGN		;POSITIONAL ARG NOT SPECIFIED, SKIP IT
	XMOVEI	T2,@0(L)	;Get arg address
	CAIE	T1,IK.UNIT	;VALUE-TYPE ARG?
	CAIN	T1,IK.FMS
	  JRST	ARGV		;YES, GO GET VALUE
	CAIE	T1,IK.MTOP
	CAIN	T1,IK.HSL
	  JRST	ARGV
	JRST	ARGS		;NOT VALUE ARG, GO STORE TYPE & ADDRESS

ARGV:	HRRZ	T2,(L)		;Get value
	MOVE	T3,@%LTYPE	;Get arg bits.
	TXNE	T3,ARGTYP	;IMMEDIATE MODE?
	  MOVE	T2,@(L)		;NO, GET VALUE
ARGS:	MOVEM	T2,IOARGS(T1)	;STORE ARG IN BLOCK
	LDB	T2,[POINTR @%LTYPE,ARGTYP] ;GET TYPE
	MOVEM	T2,IOTYPS(T1)	;STORE TYPE
ARGN:	AOBJN	L,ARGLP		;TRANSFER WHOLE ARG BLOCK

	SKIPE	T1,A.IOS	;IOSTAT VARIABLE?
	 SETZM	(T1)		;YES. CLEAR IT
	POPJ	P,		;DONE
	SEGMENT DATA

;COPIED ARGS, MUST BE CONSECUTIVE, IN ORDER ON KEYWORD NUMBER (IK.XXX)

IOARGS==.-1			;ARGS START AT UNIT
A.UNIT:: BLOCK	1		;UNIT=		[VALUE]
A.FMT::	 BLOCK	1		;FMT=		[ADDRESS]
A.FMS::	 BLOCK	1		;FORMAT SIZE	[VALUE]
A.END::	 BLOCK	1		;END=		[ADDRESS]
A.ERR::	 BLOCK	1		;ERR=		[ADDRESS]
A.IOS::	 BLOCK	1		;IOSTAT=	[ADDRESS]
A.REC::	 BLOCK	1		;REC=		[ADDRESS]
A.NML::	 BLOCK	1		;NAMELIST ADDRESS [ADDRESS]
A.MTOP:: BLOCK	1		;MTA OP CODE	[VALUE]
A.HSA::	 BLOCK	1		;ENCODE/DECODE HOL STRING ADDRESS [ADDRESS]
A.HSL::	 BLOCK	1		;ENCODE/DECODE HOL STRING LENGTH [VALUE]

MAXKWD==.-IOARGS		;MAX LEGAL IO ARG KWD NUMBER

;NOW FOR THE DATA TYPES

IOTYPS=.-1
T.UNIT:: BLOCK	1		;UNIT=		[VALUE]
T.FMT::	 BLOCK	1		;FMT=		[ADDRESS]
T.FMS::	 BLOCK	1		;FORMAT SIZE	[VALUE]
T.END::	 BLOCK	1		;END=		[ADDRESS]
T.ERR::	 BLOCK	1		;ERR=		[ADDRESS]
T.IOS::	 BLOCK	1		;IOSTAT=	[ADDRESS]
T.REC::	 BLOCK	1		;REC=		[ADDRESS]
T.NML::	 BLOCK	1		;NAMELIST ADDRESS [ADDRESS]
T.MTOP:: BLOCK	1		;MTA OP CODE	[VALUE]
T.HSA::	 BLOCK	1		;ENCODE/DECODE HOL STRING ADDRESS [ADDRESS]
T.HSL::	 BLOCK	1		;ENCODE/DECODE HOL STRING LENGTH [VALUE]

	SEGMENT CODE
OUTGO:	XMOVEI	T1,[ASCIZ /WRITE/] ;Set statement name
	MOVEM	T1,%IONAM
	PUSHJ	P,STIO		;Do common start functions
	PUSHJ	P,%SETD		;Setup D and U, do implicit
					; OPEN if necessary.
	PUSHJ	P,%SETOUT	;Get file opened for output.
	JRST	EFIO1C		;Go to common code

INGO:	XMOVEI	T1,[ASCIZ /READ/]
	MOVEM	T1,%IONAM
	PUSHJ	P,STIO		;Do common start functions
				; (returns t1= unit number)
	CAME	T1,[-6]		;REREAD?
	 JRST	INGO1		;No
	SKIPN	T1,U.RERD	;GET REREAD UNIT
	 JRST	DORBR		;?REREAD not preceeded by READ
	TXO	F,F%NINP	;SUPPRESS FIRST INPUT
INGO1:	PUSHJ	P,%SETD		;Do implicit OPEN if necessary
	PUSHJ	P,%SETIN	;Get file opened for input.
	JRST	EFIO1C		;Go rejoin common code

;Process error "REREAD not preceeded by READ"

SEGMENT ERR
DORBR:	PUSHJ	P,SETERI	;Setup %TERR and %TIOS
	$ECALL	RBR,%ABORT

SETERI:	MOVE	T1,A.ERR	;Setup ERR= if specified
	MOVEM	T1,%TERR
	MOVE	T1,A.IOS	; and IOSTAT=
	MOVEM	T1,%TIOS
	POPJ	P,		;Return

SEGMENT CODE

;Common READ/WRITE I-O start-up functions
;Copy IO args to A.xxx
;Check unit number in range.
;If REC= specified, sets D%RAN in P1
;Returns T1= unit number

STIO:	PUSHJ	P,IOARG		;MOVE ARGS TO A.XXX
	PUSHJ	P,CHKUNT	;Check unit number in range
				; (Goes to ABORT% or ERR= if not).
	SKIPE	A.REC		;REC= SPECIFIED?
	  TXO	P1,D%RAN	;YES, IMPLIES RANDOM I/O

	HRRE	T1,A.UNIT	;GET UNIT
	POPJ	P,		;Return

EFIO1C:	XOR	P1,OLDFLG	;CHECK THAT I/O STATEMENT MATCHES FILE
	TXNN	P1,D%RAN+D%UNF
	  JRST	IOGO		;MATCHES, FINE

	TXNE	P1,D%RAN	;MISMATCH ON RANDOM/SEQUENTIAL?
	 PUSHJ	P,RNSMIS	;Yes, print error and return
				; (we might also have UNF/FORM error)
	TXNN	P1,D%UNF	;Mismatch on UNF?
	 JRST	%ABORT		;No, just abort because of the RAN error.

;This mismatch can only occur if the file had previously been open
;assuming FORMATTED I/O and the user just did an UNFORMATTED operation.
;
;If no I/O has been done (only the OPEN statement), and the /MODE was not
; specified, this is ok-- the file is changed to be UNFORMATTED. Otherwise,
; it is a fatal error, and the program is aborted.

	LOAD	T1,XMODE(D)	;GET 1 IFF WE DIDN'T SEE /MODE IN OPEN
	SKIPN	BLKN(D) 	;AT START OF FILE?
	  JUMPN	T1,CUNFSW	;YES, CAN SWITCH IF NO /MODE IN OPEN
	JRST	MISFUF		;Fatal mismatch of FORMATTED/UNFORMATTED

;Here if it is possible for us to switch FORMATTED to UNFORMATTED
CUNFSW:	TXNE	P1,D%RAN	;But did we also get a mismatch of
				; RANDOM and SEQUENTIAL?
	  JRST	%ABORT		;Yeah, just quit.
	PUSHJ	P,UNFSW		;Otherwise-- switch file to UNFORMATTED.
	JRST	IOGO		; and go on.
;Come here if there is a fatal mismatch of FORMATTED/UNFORMATTED
;I/O. Type error message and abort the program.

MISFUF:	DMOVE	T2,[EXP [ASCIZ /formatted/],[ASCIZ /unformatted/]]
	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNN	T1,D%UNF	;DID HE TRY UNFORMATTED I/O?
	  EXCH	T2,T3		;YES
	PUSHJ	P,CDI		;TELL HIM HE CAN'T DO IT

	PJRST	%ABORT		;GO DIE

CDI:	;IOERR	(CDI,31,315,?,Can't do $A I/O to $A file,<T2,T3>,%POPJ)
	$ECALL	CDI,%POPJ

;Routine to print fatal error message for user because
; he tried to mix and match random and sequential access.
; After the error is typed, it returns so FOROTS can type
;still more errors if necessary.
RNSMIS:	DMOVE	T2,[EXP [ASCIZ /direct/],[ASCIZ /sequential/]]
	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNE	T1,D%RAN	;DID USER TRY RANDOM I/O?
	  EXCH	T2,T3		;NO, HE TRIED SEQUENTIAL
	PJRST	CDI		;Tell user he can't do it; return
;Routine to switch file from FORMATTED to UNFORMATTED.
;POPJ's when done.
UNFSW:	MOVX	T1,D%UNF!D%BIN	;Set UNFORMATTED and BINARY
	IORM	T1,FLAGS(D)
	MOVEI	T2,^D36		;Set byte size to 36
IF10,<	STORE	T2,IBSIZ(D)
	STORE	T2,OBSIZ(D) >
IF20,<	STORE	T2,BSIZ(D)  >

IF20,<
	LOAD	T1,INDX(D)	;NONDISK?
	CAIN	T1,DI.DSK
	  JRST	UNSW1		;NO
	LOAD	T1,IJFN(D)	;YES, RESET MONITOR BYTE SIZE ALSO
	SFBSZ%
	  PUSHJ	P,SETBSZ	;EASY WAY FAILED, REOPEN FILE
UNSW1:	LOAD	T2,BPW(D)	;GET BYTES/WORD
	MOVE	T1,WSIZ(D)	;GET WINDOW SIZE
	ADDI	T1,-1(T2)	;ROUND UP
	IDIVI	T1,(T2)		;GET WINDOW SIZE IN WORDS
	MOVEM	T1,WSIZ(D)	;STORE IT

> ;IF20

	MOVE	T1,EOFN(D)	;GET FILE SIZE IN BYTES
	CAMGE	T1,[377777,,777774] ;PREVENT OVERFLOW
	  ADDI	T1,4		;CONVERT TO WORDS
	IDIVI	T1,5
	MOVEM	T1,EOFN(D)

	MOVEI	T1,MD.BIN	;Set /MODE to binary
	STORE	T1,MODE(D)

IF10,<
	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNE	T1,D%RAN	;Is this a RANDOM file?
	  JRST	NODM		;YES, DON'T CHANGE DATA MODE
	MOVEI	T1,.IOBIN	;Binary
	STORE	T1,DMODE(D)
	PUSHJ	P,%CHKNR	;CHECK THAT MODE IS LEGAL FOR THIS DEVICE
	  JRST	%ABORT		;NOT LEGAL, MESSAGE ALREADY TYPED; GO DIE

	HLLZ	T2,CHAN(D)	;DO SETSTS FILOP TO TELL MONITOR ABOUT NEW MODE
	HRRI	T2,.FOSET
	LOAD	T3,DMODE(D)
	MOVE	T1,[2,,T2]
	FILOP.	T1,
	 $SNH			;SETSTS shouldn't fail
NODM:
> ;IF10

	MOVEI	T1,1		;SET BYTES PER WORD
	STORE	T1,BPW(D)
	POPJ	P,		;Return

IF20,<
;This routine gets called for DISK files
; when the SFBSZ% JSYS has failed.
;If the file has not been opened yet, the routine just POPJ's since
;the byte size will be set correctly when the OPENF is done.
;
;If the OPENF has already been done, the routine tries to do a CLOSF
; and another OPENF with the correct byte size. If this fails the
; program is aborted.
;
;Inputs:
;	T2/ byte size we want.
;	D/ pointer to the DDB.
;Call:
;	PUSHJ	P,SETBSZ
;	<return here if ok>	;Goes to %ABORT if something is wrong
;Smashes ac T1 only

SETBSZ:	PUSH	P,T2		;Save the byte size we want
	LOAD	T1,IJFN(D)	;GET FILE STATUS
	GTSTS%
	JUMPGE	T2,T2POPJ	;FILE NOT OPEN YET, REAL OPEN WILL BE OK
	SETZ	T1,		;INIT OPENF BITS
	TXNE	T2,GS%RDF	;FILE OPEN FOR READ?
	  TXO	T1,OF%RD	;YES, RE-OPEN FOR READ
	TXNE	T2,GS%WRF	;WRITE?
	  TXO	T1,OF%WR
	POP	P,T2		;Restore byte size
	LSH	T2,^D30		;POSITION BYTE SIZE FOR OPENF
	IOR	T2,T1		;Put more OPENF% bits into the arg.

	LOAD	T1,IJFN(D)	;CLOSE FILE
	HRLI	T1,(CO%NRJ)	;KEEP JFN
	CLOSF%			; (This does not affect T2)
;	  IOERR	(UFS,34,,?,$J,,%ABORT)
	 $ECALL	UFS,%ABORT
	LOAD	T1,IJFN(D)	;REOPEN FILE WITH MODIFIED BYTE SIZE
	OPENF%
;	  IOERR	(UFS,30,,?,$J,%ABORT) ;CAN'T
	 $ECALL	UFS,%ABORT
	POPJ	P,		;NEW BYTE SIZE SET

;Restore T2 and POPJ.
T2POPJ:	POP	P,T2
	POPJ	P,		;Return
> ;IF20
;Routine to check UNIT= to see if unit number is in range
;Call:
;	PUSHJ	P,CHKUNT
;	<return here if ok>
;If unit is out of range and ERR= or IOSTAT= was specified,
;  the program returns to the appropriate place.
;Otherwise, the error message is typed and the program is aborted.
;Uses T1,T2

CHKUNT:	HRRE	T2,A.UNIT	;GET UNIT
	CAML	T2,[-7]		;RANGE CHECK.  NEGATIVE ARGS ILLEGAL
	CAILE	T2,MAXUNIT	; OR UNIT BEYOND RANGE OF DDBTAB
	  JRST	.+2		;Out of range.
	POPJ	P,		;Ok, return

;Prepare to call error routine
;T2= unit number (IUN error uses it)
	PUSHJ	P,SETERI	;Set %TERR and %TIOS
	$ECALL	IUN,%ABORT	;Give error
IOGO:	MOVEM	U,%UDBAD	;SAVE DDB ADDRESS FOR IOLST
	MOVE	T2,FLAGS(D)	;T2:= DDB flags

IF10,<
	LOAD	T1,MODE(D)	;GET THE /MODE
	TXNN	T2,D%RAN	;RANDOM IS SPECIAL DUMP
	CAIE	T1,MD.DMP	;DUMP?
	 JRST	NOTDMP		;NO
	MOVE	T1,[DMPIN,,DMPOUT] ;YES.
	JRST	%SIO		;GO DO IT
NOTDMP:

>;END IF10

	TXNE	T2,D%RAN	;RANDOM I/O?
	  PUSHJ	P,RMAPW		;YES, SET TO START OF RECORD
	MOVE	T2,FLAGS(D)	;Reget flags
	TXNE	T2,D%UNF	;Unformatted?
	  JRST [MOVE T1,[UNFI,,UNFO] ;Yes
		JRST %SIO]

	TXNN	T2,D%IO		;GET A RECORD IF INPUT
	 PUSHJ	P,%IREC
	MOVE	T1,[%FORMT,,%FORMT] ;ASSUME FORMATTED
	SKIPE	T2,A.FMT	;GET FORMAT ADDR
	CAMN	T2,[IK.FMT_^D27] ;Compiler kludge-- keyword+0 address
	  MOVE	T1,[%LDI,,%LDO]	;NO, ASSUME LIST-DIRECTED
	SKIPE	A.NML		;UNLESS NAMELIST SPECIFIED
	  MOVE	T1,[%NLI,,%NLO]	;THEN SET NAMELIST ADDRESSES
	JRST	%SIO		;GO DO I/O

;HERE FROM IOLST% OR FIN% WHEN I/O IS COMPLETE

%FIO:	SETZM	%UDBAD		;CLEAR THE UDB PNTR
	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNE	T1,D%IO		;OUTPUT?
	 TXNE	T1,D%UNF	;YES, FORMATTED?
	  JRST	%SETAV		;NO
	PUSHJ	P,%OREC		;OUTPUT RECORD

%SETAV:	MOVE	T1,NREC(U)	;GET CURRENT RECORD NUMBER
	SKIPE	T2,AVAR(U)	;Get address of ASSOCIATE VARIABLE
	  MOVEM	T1,(T2)		;There is one, store record number
	SETZM	ENDAD(U)	;Clear END=,
	SETZM	ERRAD(U)	; ERR=,
	SETZM	IOSAD(U)	; IOSTAT=.
	POPJ	P,		;DONE
;ROUTINE TO SET UP A DDB FOR ENCODE/DECODE

SETDE:	SETZ	P1,		;No funny IO flags.
	SKIPE	U,%EDDB		;DDB ALREADY CREATED?
	  JRST	GOTD		;Yes, use it.

	MOVEI	T1,ULEN		;Get length of unit block
	PUSHJ	P,%GTBLK
	MOVE	U,T1		;Point U to it
	MOVEM	U,%EDDB		;Save for use on next ENCODE/DECODE
	MOVEI	T1,DLEN		;GET LENGTH OF DDB
	PUSHJ	P,%GTBLK	;GET AN EMPTY DDB
	MOVEI	D,(T1)		;POINT D TO IT
	MOVEM	D,DDBAD(U)	;Remember it in the unit block

	MOVEI	T1,DI.INT	;SET "DEVICE" TYPE TO INTERNAL FILE
	STORE	T1,INDX(D)	;STORE IN DDB

	MOVEI	T1," "		;PAD WITH SPACES
	STORE	T1,PADCH(U)

	JRST	GOTD		;Done

;ROUTINES TO CONVERT POSITIONAL ARG BLOCKS TO KEYWORD ARG BLOCKS
	
FMTCNV:	LDB	T1,[POINTR @%LTYPE,ARGKWD] ;GET KWD FIELD OF FIRST ARG
	JUMPN	T1,%POPJ	;NONZERO, NEW-STYLE CALL

	CAMLE	L,[-4,,-1]	;AT LEAST 4 ARGS?
	  JRST	IOCNV1		;NO, SKIP /FMT
	PUSH	P,L
	ADD	L,[3,,3]
	MOVEI	T1,IK.FMT	;GET KWD NUMBER FOR /FMT
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

	CAMLE	L,[-5,,-1]	;AT LEAST 5 ARGS?
	  JRST	IOCNV1		;NO, SKIP FORMAT SIZE
	PUSH	P,L
	ADD	L,[4,,4]
	MOVEI	T1,IK.FMS	;GET KWD NUMBER FOR FORMAT SIZE
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

IOCNV1:	CAMLE	L,[-6,,-1]	;AT LEAST 6 ARGS?
	  JRST	IOCNV2		;NO, SKIP /REC
	PUSH	P,L
	ADD	L,[5,,5]
	MOVEI	T1,IK.REC	;GET KWD NUMBER FOR /REC
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

IOCNV2:	MOVEI	T1,IK.UNIT	;GET KWD NUMBER FOR /UNIT
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST

IOCNV3:	CAMLE	L,[-2,,-1]	;AT LEAST 2 ARGS?
	  POPJ	P,		;NO, DONE
	MOVEI	T1,IK.END	;GET KWD NUMBER FOR /END
	PUSH	P,L
	ADDI	L,1
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

	CAMLE	L,[-3,,-1]	;AT LEAST 3 ARGS?
	  POPJ	P,		;NO, DONE
	PUSH	P,L
	ADDI	L,2
	MOVEI	T1,IK.ERR	;GET KWD NUMBER FOR /ERR
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

	POPJ	P,		;DONE


UNFCNV:	LDB	T1,[POINTR @%LTYPE,ARGKWD] ;GET KWD FIELD OF FIRST ARG
	JUMPN	T1,%POPJ	;NONZERO, NEW-STYLE CALL

	JRST	IOCNV1


NMLCNV:	LDB	T1,[POINTR @%LTYPE,ARGKWD] ;GET KWD FIELD OF FIRST ARG
	JUMPN	T1,%POPJ	;NONZERO, NEW-STYLE CALL

	CAMLE	L,[-4,,-1]	;AT LEAST 4 ARGS?
	  JRST	IOCNV2		;NO, NO NAMELIST ADDRESS
	PUSH	P,L
	MOVEI	T1,IK.NML	;GET KWD NUMBER FOR NAMELIST
	ADDI	L,3
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L
	JRST	IOCNV2		;GO DO STANDARD ARGS
ENCCNV:	LDB	T1,[POINTR @%LTYPE,ARGKWD] ;GET KWD FIELD OF FIRST ARG
	JUMPN	T1,%POPJ	;NONZERO, NEW-STYLE CALL

	MOVEI	T1,IK.HSL	;GET KWD NUMBER FOR STRING LENGTH
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST

	CAMLE	L,[-4,,-1]	;AT LEAST 4 ARGS?
	  JRST	IOCNV3		;NO, SKIP /FMT
	PUSH	P,L
	ADDI	L,3
	MOVEI	T1,IK.FMT	;GET KWD NUMBER FOR /FMT
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

	CAMLE	L,[-5,,-1]	;AT LEAST 5 ARGS?
	  JRST	IOCNV3		;NO, SKIP FORMAT SIZE
	PUSH	P,L
	ADDI	L,4
	MOVEI	T1,IK.FMS	;GET KWD NUMBER FOR FORMAT SIZE
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L

	CAMLE	L,[-6,,-1]	;AT LEAST 6 ARGS?
	  JRST	IOCNV3		;NO, SKIP STRING ADDRESS
	PUSH	P,L
	ADDI	L,5
	MOVEI	T1,IK.HSA	;GET KWD NUMBER FOR STRING ADDRESS
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L
	JRST	IOCNV3		;GO DO STANDARD ARGS


MTCNV:	LDB	T1,[POINTR @%LTYPE,ARGKWD]	;GET KWD FIELD OF FIRST ARG
	JUMPN	T1,%POPJ	;NONZERO, NEW-STYLE CALL

	CAMLE	L,[-4,,-1]	;AT LEAST 4 ARGS?
	  JRST	IOCNV2		;NO
	PUSH	P,L
	ADDI	L,3
	MOVEI	T1,IK.MTOP	;GET KWD NUMBER FOR MT OP CODE
	DPB	T1,[POINTR @%LTYPE,ARGKWD] ;STORE IN LOCAL ARG LIST
	POP	P,L
	JRST	IOCNV2		;GO CONVERT UNIT, ERR, END
;SETD IS CALLED TO SET UP D FOR ALL I/O STATEMENTS.
;OPENS UNIT IF NECESSARY
;CHECKS RANDOM VS. SEQUENTIAL, FORMATTED VS. UNFORMATTED
;ARGS:	 T1 = UNIT NUMBER
;	 P1 = DF BITS D%RAN AND D%UNF
;	      USED FOR DEFAULT OPEN IF NECESSARY
;RETURN: D = DDB ADDRESS

%SETD:	MOVE	U,%DDBTAB(T1)	;Get address of unit block
	JUMPN	U,GOTD		;ALREADY OPEN, GO CHECK STUFF

	TXNE	P1,D%RAN	;Trying to do RANDOM I/O?
	 JRST	RIOERR		;?Have to use OPEN statement to specify
				; RECORDSIZE.
	PUSH	P,T1		;SAVE UNIT NUMBER
	MOVEI	T1,DLEN		;GET LENGTH OF DDB
	PUSHJ	P,%GTBLK	;ALLOCATE IT
	MOVE	D,T1		;POINT TO EMPTY DDB
	MOVEI	T1,ULEN		;Get length of unit block
	PUSHJ	P,%GTBLK	;Allocate it
	MOVE	U,T1
	HRRZM	P1,FLAGS(D)	;Clear LH flags, set default RH ones
	MOVE	T1,(P)		;GET UNIT BACK
	STORE	T1,UNUM(U)	;SAVE UNIT NUMBER

	SKIPE	T1,A.ERR	;ERR=
	 MOVEM	T1,ERRAD(U)	; Set in Unit block
	SKIPE	T1,A.IOS	;IOSTAT=
	 MOVEM	T1,IOSAD(U)	; Set in Unit block

	MOVEI	T1,FM.UNF	;GET /FORM:UNFORMATTED
	TXNE	P1,D%UNF	;UNFORMATTED I/O STATEMENT?
	  STORE	T1,FORM(D)	;YES, OPEN FILE FOR UNFORMATTED I/O

	PUSHJ	P,%OPENX	;OPEN THE DDB
	POP	P,T1		;GET UNIT NUMBER BACK
	MOVEM	U,%DDBTAB(T1)	;Store address of unit block

;Here with U pointing to the unit block and DDBAD(U) is the
; DDB block address. P1 contains flags pertinent to the IO statement.

GOTD:	PUSHJ	P,STREEI	;Store ERR=, END=, IOSTAT= in the unit block.
	MOVE	D,DDBAD(U)	;Get DDB address
	MOVE	T1,FLAGS(D)	;PUT IN PERM FLGS
	MOVEM	T1,OLDFLG	;Save old flags for UNFSW test
	TXZ	T1,D%CLR	;Clear temp flags
	TDO	T1,P1		;Set beginning flags for statement
	MOVEM	T1,FLAGS(D)	;Store updated flags
	POPJ	P,

SEGMENT	ERR

;Here if RANDOM I/O was attempted before an OPEN statement was done.
; This is a fatal error since RECORDSIZE parameter is needed
;for random I/O.

RIOERR:	PUSHJ	P,SETERI	;Take note of ERR=, IOSTAT=.
	$ECALL	RR1,%ABORT	; Go give error, abort or jump.

SEGMENT	CODE

SEGMENT	DATA
OLDFLG:	BLOCK	1
SEGMENT CODE
;Routine to store END=, ERR=, and IOSTAT= args in the unit block.
;The args will stay in the DDB until IO returns via %SETAV.
;
;Inputs:
;	U/ address of unit block
;	A.ERR/ <ERR= arg address>
;	A.END/ <END= arg address>
;	A.IOS/ <IOSTAT= arg address>
;Call:
;	PUSHJ	P,STREEI
;	<return here always>

STREEI:	MOVE	T1,A.END	;Get END=
	MOVEM	T1,ENDAD(U)	;Store in unit block for later
	MOVE	T1,A.ERR	;Get ERR=
	MOVEM	T1,ERRAD(U)	;Store in unit block for later
	MOVE	T1,A.IOS	;Get IOSTAT=
	MOVEM	T1,IOSAD(U)	;Store in unit block for later
	POPJ	P,		;Return
	SUBTTL	BYTE I/O

	COMMENT	&

IBYTE and OBYTE are the basic routines for formatted I/O; they read or write
one byte in the current record.  RPOS returns the current position (column
number) within a record.  SPOS sets the current position.  IREC reads the next
record from the file.  OREC writes a record into the file.

Each open file has a record buffer which holds the current record.  (This makes
T format work and makes REREAD easier.)  There is one record buffer per open
file since function calls can cause simultaneous I/O on multiple units.

Record buffer format:

Input:

	IRBUF			  IRPTR
	 v			   v
   --------------------------------------------------------------------
   !    !///////////////////////////!///////////////!                 !
   --------------------------------------------------------------------
				     <--- IRCNT ---->
         <---------------- IRLEN ------------------->
         <-------------------------- IRBLN -------------------------->


Output:


	ORBUF			  ORPTR
	 v			   v
   --------------------------------------------------------------------
   !    !///////////////////////////!///////////////!                 !
   --------------------------------------------------------------------
				     <------------ ORCNT ------------>
         <---------------- ORLEN* ------------------>
         <-------------------------- ORBLN -------------------------->

(*note:  on output, ORLEN is not kept up to date by OBYTE, since normally
ORPTR is at the end of the record so ORLEN changes every character.  ORLEN
is correct immediately after a leftward T format.)

The -1 word of the record buffer is used for carriage control.  The record
is altered by replacing the first character (the carriage control character)
with 0 to 4 characters.

	&
;ROUTINE TO READ SINGLE BYTE
;RETURN: T1 = NEXT BYTE FROM FILE
;DESTROYS NO ACS EXCEPT T1
;NOTE: IRCNT GOING NEGATIVE IS A LEGAL CONDITION. IT MERELY
;MEANS THAT WE ARE BEYOND THE END OF THE RECORD. T-FORMAT AND
;X-FORMAT WILL SET IT NEGATIVE IF THEY GO BEYOND THE END OF
;THE RECORD.

%IBYTE:	SOSGE	IRCNT(D)	;DECREMENT BYTE COUNT
	  JRST	EOR		;NONE LEFT, END OF BUFFER
	ILDB	T1,IRPTR(D)	;GET BYTE FROM BUFFER
	POPJ	P,		;DONE

EOR:	MOVX	T1,D%EOR	;Tell caller record has ended (if it cares)
	IORM	T1,FLAGS(D)	; . .
	MOVEI	T1," "		;EXTEND SHORT RECORDS WITH TRAILING SPACES
	POPJ	P,		;RETURN


;ROUTINE TO REREAD CURRENT BYTE
;RETURN: T1 = BYTE THAT IBYTE RETURNED ON MOST RECENT CALL
;DESTROYS NO ACS EXCEPT T1

%IBYTC:	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNE	T1,D%EOR	;Has record ended?
	  SKIPA	T1,[" "]	;YES, RETURN SPACE
	LDB	T1,IRPTR(D)	;NO, RETURN CHAR UNDER BYTE POINTER
	POPJ	P,		;RETURN



;ROUTINE TO BACK UP INPUT BYTE POINTER
;NO ARGS
;ON RETURN, IBYTE WILL BE BACKSPACED ONE CHARACTER
;CAN BE CALLED REPEATEDLY

%IBACK:	PUSHJ	P,RIPOS		;GET CURRENT COLUMN NUMBER
	SOJA	T1,SIPOS	;SET TO THAT COLUMN
;ROUTINE TO PUT SINGLE BYTE IN FILE
;ARGS:	 T1 = BYTE
;DESTROYS NO ACS

%OBYTE:	SKIPE	ORPOS(D)	;VIRTUAL POSITION SET?
	 PUSHJ	P,SETPOS	;YES. SET NEW POSITION BEFORE DEPOSIT
LOBYTE:	SOSGE	ORCNT(D)	;DECREMENT BYTE COUNT
	  JRST	OEXP		;BUFFER FULL, GO EXPAND IT
	IDPB	T1,ORPTR(D)	;STORE BYTE IN BUFFER
	POPJ	P,		;DONE

OEXP:	MOVE	T0,FLAGS(D)	;Get flags
	TXNE	T0,D%ENC	;ENCODE/DECODE?
	  JRST	TRUNC		;YES, TRUNCATE RECORD INSTEAD OF EXPANDING
	PUSHJ	P,%PUSHT	;SAVE T0-T5
	PUSHJ	P,EXPORB	;EXPAND RECORD BUFFER
	PUSHJ	P,%POPT		;RESTORE T0-T5
	JRST	LOBYTE		;GO STORE BYTE IN EXPANDED BUFFER

TRUNC:	TXOE	T0,D%TRNC	;Only say this once
	  POPJ	P,
	IORM	T0,FLAGS(D)	;(Remember D%TRUC was set)
	AOSN	ORCNT(D)	;ONLY COMPLAIN ON FIRST CHAR (CNT = -1)
;	  IOERR	(ETL,60,509,%,Record length exceeds string length)
	 $ECALL	ETL
	SOS	ORCNT(D)	;RESTORE COUNT
	POPJ	P,



;ROUTINE TO EXPAND THE RECORD BUFFER
;RETURN: T1 = BYTE POINTER TO START OF MOVED RECORD BUFFER
;	 T2 = BYTE POINTER TO FIRST FREE BYTE IN MOVED RECORD BUFFER
;	 T3 = NUMBER OF NEW BYTES IN MOVED RECORD BUFFER
;	 RBUF, RBLN, RPTR SET UP FOR NEW BUFFER

EXPIRB:	MOVE	T2,IRBLN(D)	;GET OLD LENGTH IN BYTES
	HRRZ	T1,IRBUF(D)	;AND OLD BUFFER ADDR
	SETZ	T3,		;NO MINIMUM SIZE
	PUSHJ	P,EXPRB		;EXPAND AND MOVE
	MOVEM	T1,IRBUF(D)	;STORE NEW BUFFER ADDR
	MOVEM	T1,IRPTR(D)	;STORE PNTR TO NEW BUFFER
	MOVE	T4,IRBLN(D)	;GET OLD SIZE AGAIN
	MOVEM	T3,IRBLN(D)	;STORE NEW SIZE
	SUBI	T3,(T4)		;RETURN SIZE LEFT IN T3
	POPJ	P,

EXPORB:	MOVE	T2,ORBLN(D)	;GET OLD LENGTH IN BYTES
	HRRZ	T1,ORBUF(D)	;AND OLD BUFFER ADDR
	MOVE	T3,ORPOS(D)	;VIRTUAL POSITION AS MINIMUM
	PUSHJ	P,EXPRB		;EXPAND AND MOVE
	MOVEM	T1,ORBUF(D)	;STORE NEW BUFFER ADDR
	MOVE	T4,ORBLN(D)	;GET OLD SIZE AGAIN
	MOVEM	T3,ORBLN(D)	;STORE NEW SIZE
	SUBI	T3,(T4)		;RETURN SIZE LEFT IN T3
	DMOVEM	T2,ORPTR(D)	;STORE PNTR/COUNT TO MIDDLE OF NEW BUFFER
	POPJ	P,

EXPRB:	JUMPE	T1,GETRB	;IF NONE YET, GET ONE
	MOVEI	T4,(T2)		;COPY OLD SIZE
	LSH	T4,1		;DOUBLE IT
	ADDI	T4,(T3)		;ADD MINIMUM SIZE
	IDIVI	T2,5		;GET # WORDS IN OLD BUFFER
	MOVEI	T3,(T4)		;COPY NEW SIZE
	IDIVI	T3,5		;GET # WORDS IN NEW BUFFER
	ADDI	T3,1		;ACCOUNT FOR CARRIAGE CONTROL WORD
	ADDI	T2,1		; JUST BEFORE REC BUFFER
	SUBI	T1,1
	PUSHJ	P,%MVBLK	;MOVE TO BIGGER BUFFER
	ADDI	T1,1		;PUT CARRIAGE CONTROL WORD BACK
	SUBI	T3,1		;DON'T INCLUDE IN RECSIZ
	IMULI	T3,5		;CONVERT TO CHARS
	HRLI	T1,(POINT 7)	;MAKE BYTE PNTR TO BEG BUFFER
	HRLI	T2,(POINT 7,)	;MAKE BYTE POINTER TO END OF OLD STRING
	POPJ	P,		;RETURN

GETRB:	ADDI	T3,LRECBF*5	;MINIMUM SIZE + STANDARD SIZE
	ADDI	T3,4		;ROUND UP
	IDIVI	T3,5		;GET # WORDS
	MOVEI	T1,(T3)		;COPY IT
	IMULI	T3,5		;GET EXACT # CHARS
	ADDI	T1,1		;ADD 1 FOR CARRIAGE WORD
	PUSH	P,T3		;DESTROYED BY %GTBLK
	PUSHJ	P,%GTBLK
	POP	P,T3		;GET THE NUMBER OF CHARS BACK
	ADDI	T1,1		;POINT PAST CARRIAGE WORD
	HRLI	T1,(POINT 7)	;CREATE PNTR
	MOVE	T2,T1		;OLD AND NEW PNTR ARE THE SAME
	POPJ	P,

;Routine to drop a null at EOR
; Does not affect the byte count
;Uses T1

ENDNUL:	SETZ	T1,		;Get a null char.
	PUSHJ	P,%OBYTE	; Put in record
	AOS	ORCNT(D)	;Don't count it as a character.
	POPJ	P,		;Return
	SUBTTL	INPUT

%IREC:	TXZE	F,F%NINP	;REREAD?
	 JRST	REREAD		;YES. GO DO SETUP ONLY

	SETOM	LSNUM(D)	;SET UP ILLEGAL LINE SEQUENCE NUMBER
	SKIPN	IRBUF(D)	;ANY BUFFER YET?
	PUSHJ	P,EXPIRB	;NO. ALLOCATE THE BUFFER

	AOS	NREC(U)		;INCREMENT RECORD NUMBER

	LOAD	T1,INDX(D)	;GET DEV INDEX
	PUSHJ	P,@IDSP(T1)	;DO DEVICE-DEPENDENT INPUT

	LOAD	T1,UNUM(U)	;Get unit number
	LOAD	T2,FLAGS(D)	;Get flags
	TXNN	T2,D%ENC	;Unless this is DECODE..
	 HRREM	T1,U.RERD	;Store REREAD unit

	MOVE	T1,IRBUF(D)	;GET RECORD BUFFER PNTR
	MOVEM	T1,IRPTR(D)	;STORE INITIALIZED BYTE PTR

	MOVE	T1,IRBLN(D)	;GET NUMBER OF BYTES IN RECORD BUFFER
	SUB	T1,IRCNT(D)	;SUBTRACT NUMBER OF REMAINING BYTES
	MOVEM	T1,IRCNT(D)	;STORE COUNT OF BYTES READ IN
	MOVEM	T1,IRLEN(D)
	MOVE	T0,FLAGS(D)	;Get current DDB flags
	TXZ	T0,D%EOR	;RECORD HAS NOT ENDED YET
	TXO	T0,D%SICR+D%SILF	;SUPPRESS NEXT CRLF
	MOVEM	T0,FLAGS(D)	;Store updated DDB flags
	JUMPN	T1,%POPJ	;NO MORE CHECKING IF CHARS IN RECORD
	TXNN	T0,D%END	;ZERO CHARS. EOF ALSO?
	 POPJ	P,		;NO. JUST A NULL RECORD

IREOF:	SETZM	IRCNT(D)	;EOF, RETURN A ZERO-CHARACTER RECORD
;	 IOERR	(EOF,899,404,?,End of file,,%ABORT) 
	 $ECALL	EOF,%ABORT	;HANDLE EOF RETURN IN IOERR
	POPJ	P,

REREAD:	MOVE	T1,IRLEN(D)	;REREAD. SETUP PNTR/COUNT WITH OLD DATA
	MOVEM	T1,IRCNT(D)
	MOVE	T1,IRBUF(D)
	MOVEM	T1,IRPTR(D)
	MOVX	T1,D%EOR
	ANDCAM	T1,FLAGS(D)	;Clear "end of record" flag
	POPJ	P,		;Return


;ALL DEVICE-DEPENDENT INPUT ROUTINES HAVE THE SAME CALLING SEQUENCE:
;ARGS:	 IRPTR = BYTE POINTER TO START OF RECORD BUFFER
;	 IRBLN = NUMBER OF BYTES IN RECORD BUFFER
;RETURN: NEXT RECORD FROM FILE READ INTO RECORD BUFFER
;	 T3 = NUMBER OF UNUSED BYTES LEFT IN RECORD BUFFER

IDSP:	IFIW	TIREC
	IFIW	DIREC
	IFIW	XIREC
	IFIW	XIREC
	IFIW	DECODE
IF20,<

;TTY

TIREC:	MOVE	T0,FLAGS(D)
	TXZ	T0,D%END	;CLEAR EOF FOR TTY'S
	MOVEM	T0,FLAGS(D)
	TXNN	T0,D%SICR+D%SILF	;SUPPRESS CR OR LF?
	 PUSHJ	P,%OCRLF		;NO. OUTPUT CRLF

	MOVEI	T1,.RDBRK	;SET TEXTI BLOCK LENGTH
	MOVEM	T1,TXIBLK+.RDCWB

	MOVX	T1,RD%CRF+RD%JFN+RD%BBG ;SUPPRESS CR, READ FROM JFNS, BFP GIVEN
	MOVEM	T1,TXIBLK+.RDFLG ;STORE FLAGS

	LOAD	T1,IJFN(D)	;GET JFN
	HRLI	T1,(T1)		;IN BOTH HALVES
	MOVEM	T1,TXIBLK+.RDIOJ ;STORE IT

	MOVE	T1,IRBUF(D)	;GET RECORD BUFFER PNTR
	MOVEM	T1,TXIBLK+.RDDBP ;STORE DEST BYTE POINTER
	MOVEM	T1,TXIBLK+.RDBFP ;AND BEGINNING-OF-BUFFER POINTER

	MOVE	T1,IRBLN(D)	;GET RECORD BUFFER LENGTH
	MOVEM	T1,TXIBLK+.RDDBC ;STORE DEST BYTE COUNT

	SETZM	TXIBLK+.RDBFP	;NO WAKEUP ON ^U, ^W EDITING
	MOVE	T1,G.PRP	;SET POINTER TO PROMPT STRING
	MOVEM	T1,TXIBLK+.RDRTY

	MOVEI	T1,TXIBRK	;POINT TO BREAK MASK
	MOVEM	T1,TXIBLK+.RDBRK ;STORE IT

	LOAD	T1,IJFN(D)	;SET CCOC WORDS FOR INPUT
	DMOVE	T2,CCOC(D)
	SFCOC%

TCONT:	MOVEI	T1,TXIBLK	;POINT TO BLOCK
	TEXTI%			;READ A LINE
	  JSHALT		;SHOULD NOT FAIL

	MOVE	T1,TXIBLK+.RDFLG ;GET TEXTI FLAGS
	TXNN	T1,RD%BTM	;INPUT TERMINATED BY BREAK CHAR?
	  JRST	TEXP		;NO, EXPAND BUFFER AND CONTINUE

	MOVX	T0,D%END	;Get flag to set if CTRL-Z seen.
	LDB	T1,TXIBLK+.RDDBP ;GET TERMINATING CHAR
	CAIN	T1,32		;^Z?
	  IORM	T0,FLAGS(D)	;Yes, set end-of-file

	LOAD	T1,IJFN(D)	;SET CCOC WORDS FOR OUTPUT
	MOVE	T2,CCOC(D)	;SET ALL CHARS TO SEND LITERALLY
	AND	T2,%CCMSK	; EXCEPT ^I AND ^L, WHICH BEHAVE THE SAME
	OR	T2,%OCCOC	; AS ON INPUT
	MOVE	T3,%OCCOC+1
	SFCOC%

	SETZM	G.PRP		;CLEAR PROMPT STRING FOR NEXT TIME
	AOS	T3,TXIBLK+.RDDBC ;RETURN COUNT OF LEFTOVER BYTES IN BUFFER
	MOVEM	T3,IRCNT(D)	;  DISCARDING TERMINATING BREAK CHAR

	POPJ	P,		;DONE


TEXP:	PUSHJ	P,EXPIRB	;EXPAND RECORD BUFFER
	MOVEM	T1,TXIBLK+.RDBFP ;SET NEW POINTER TO START OF BUFFER
	MOVEM	T2,TXIBLK+.RDDBP ;SET POINTER TO DEST STRING
	MOVEM	T3,TXIBLK+.RDDBC ;SET BYTE COUNT OF DEST STRING
	JRST	TCONT		;DO ANOTHER TEXTI TO CONTINUE INPUT
;STILL IF20

;TEXTI BREAK TABLE FOR STANDARD FORTRAN CHAR SET

TXIBRK:	1B<^O12>+1B<^O13>+1B<^O14>+1B<^O32> ;BREAK ON LF, VT, FF, ^Z
	0			;AND NOTHING ELSE
	0
	0


	SEGMENT	DATA

TXIBLK:	BLOCK	1+.RDBRK	;TEXTI ARG BLOCK

	SEGMENT	CODE

;STILL IF20

;DISK

DIREC:
XIREC:

>;END IF20

IF10,<

TIREC:	MOVE	T0,FLAGS(D)	;Get current DDB flags
	TXZ	T0,D%END	;CLEAR EOF FOR TTY'S
	MOVEM	T0,FLAGS(D)	;Store updated flags
	TXNN	T0,D%SICR+D%SILF ;SUPPRESS CR OR LF?
	 PUSHJ	P,%OCRLF	;NO. OUTPUT CRLF

DIREC:
XIREC:

>;END IF10

	MOVE	T1,IRBLN(D)	;GET # BYTES IN RECORD BUFFER
	MOVEM	T1,IRCNT(D)	;SETUP INITIAL COUNT
	MOVE	T1,FLAGS(D)	;ARE WE AT EOF?
	TXNE	T1,D%END
	 POPJ	P,		;YES. GO NO FURTHER

	PUSHJ	P,%SAVE4	;SAVE SOME P ACS

	DMOVE	P1,IPTR(D)	;GET FILE WINDOW BYTE POINTER/COUNT
	MOVE	P3,RSIZE(D)	;GET RECORD SIZE, 0 IF NONE

	MOVE	T2,IRBUF(D)	;GET RECORD BUFFER PNTR
	MOVE	T3,IRBLN(D)	;GET REC BUFFER LENGTH

	LOAD	T1,MODE(D)	;GET MODE OF FILE
	CAIE	T1,MD.ASL	;LINE-SEQUENCE ASCII?
	 JRST	DLP		;NO
ILNLP:	SOJGE	P2,ILNOK	;DECR COUNT
	PUSHJ	P,IMAP		;COUNT RAN OUT. GET ANOTHER BUF
	 JRST	ILNLP		;AND TRY AGAIN
	JRST	DIEOR		;GOT EOF

ILNOK:	ILDB	P4,P1		;GET A CHAR
	JUMPE	P4,ILNLP	;SKIP IT IF NULL
	MOVEI	T1,(P1)		;GET A PROPER INDEX, (In-section address)
				; Note: Don't use XMOVEI above--
				; (P1 is a byte ptr)
	MOVE	T0,(T1)		;GET THE WORD CHAR CAME FROM
	TRNN	T0,1		;LOW BIT ON?
	 JRST	INOLN		;NO. NOT A LINE #
	MOVEM	T0,LSNUM(D)	;SAVE THE LSN
	ADDI	P1,1		;YES. PROCEED TO NEXT WORD
	SUBI	P2,5		;DECR CHAR COUNT FOR WORD
	LDB	P4,P1		;GET THE BYTE AFTER WORD
	CAIE	P4,11		;IS IT A TAB?
	 JRST	INOLN		;NO. USE THE CHAR


DLP:	SOJGE	P2,DLPX2	;IF OUT OF MAPPED BYTES, GO GET MORE
	PUSHJ	P,IMAP
	 JRST	DLP		;KEEP IN SYNCH!
	JRST	DIEOR		;GOT EOF

DLPX2:	ILDB	P4,P1		;GET A BYTE
INOLN:	CAIGE	P4,40		;CHECK FOR SPECIAL BYTE
	 JRST	CHKEOL		;SPECIAL
DDPB:	SOJGE	T3,DDPB2	;DECR RECORD BUFFER COUNT, EXPAND IF NEEDED
	PUSHJ	P,EXPIRB	;USES T1
	JRST	DDPB		;KEEP IN SYNCH

DDPB2:	IDPB	P4,T2		;DEPOSIT BYTE IN RECORD BUFFER
DDPB3:	SOJL	P3,DLP		;DECR THE RECSIZ COUNT
	JUMPG	P3,DLP		;OK IF NEG
	JRST	DIEOR		;END OF RECORD IF ZERO

CHKEOL:	JUMPE	P4,DDPB3	;SKIP NULLS
	CAIN	P4,15		;CARRIAGE RETURN?
	 JRST	GOTCR		;YES
	CAIG	P4,14		;STANDARD EOF CHARS ARE 12-14 (LF,VT,FF)
	 CAIGE	P4,12		;EOL CHAR?
	  JRST	NOTEOL		;NO. CHECK FOR TTY CONTROL-Z
	SOJA	P3,DIEOR	;YES. DECR COUNT AND END IT ALL

NOTEOL:	CAIE	P4,32		;^Z?
	 JRST	DDPB		;NO. PASS IT THROUGH
	LOAD	T1,INDX(D)	;YES. GET DEVICE INDEX
	CAIE	T1,DI.TTY	;TTY?
	 JRST	DDPB		;NO. PASS IT THROUGH
	JRST	DDPB3		;YES. SKIP IT

GOTCR:
CRLP:	DMOVEM	P1,IPTR(D)	;SAVE AWAY THE CURRENT PNTR/COUNT
	SOJGE	P2,CRX2		;ANY CHARS IN BUFFER?
	PUSHJ	P,IMAP		;NO. GET A BUFFERFUL
	 JRST	CRLP		;KEEP IN SYNCH
	JRST	DIEOR		;GOT EOF

CRX2:	ILDB	P4,P1		;GET A CHAR
	JUMPE	P4,CRX3		;SKIP NULLS
	CAIN	P4,15		;ANOTHER CARRIAGE RETURN?
	 JRST	CRX3		;YES. IGNORE IT
	CAIG	P4,14		;VERT MOTION CHAR?
	 CAIGE	P4,12
	  JRST	CRONLY		;NO. DATA
	SOJA	P3,DIEOR	;YES. REAL END OF LINE

CRX3:	SOJG	P3,CRLP		;DECR RECSIZ COUNT
	JUMPL	P3,CRLP		;IF NEG, VARIABLE SIZE
	JRST	DIEOR		;IF ZERO, EOL

CRONLY:	DMOVE	P1,IPTR(D)	;GET PNTR/COUNT BEFORE NEW DATA
DIEOR:	MOVEM	T3,IRCNT(D)	;SAVE THE REDUCED REC COUNT
	SKIPE	RSIZE(D)	;RECORDSIZE?
	 PUSHJ	P,IALIGN	;YES. ALIGN THE PNTR/COUNT
	DMOVEM	P1,IPTR(D)	;STORE UPDATED FILE BYTE POINTER

	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM I/O?
	CAME	T2,IRBUF(D)	;YES, DID WE FIND A NONZERO BYTE?
	  POPJ	P,		;YES, DONE
	JRST	RNRERR		;NO, REC NOT WRITTEN

IALIGN:	MOVE	T3,RSIZE(D)	;GET THE RECORDSIZE
	ADDI	T3,6		;2 FOR CRLF, 4 FOR ROUNDING
	IDIVI	T3,5		;GET # WORDS
	IMULI	T3,5		;AND # CHARS AGAIN
	SUB	T3,RSIZE(D)	;GET DIFF
	ADDI	P3,(T3)		;ADD TO # CHARS TO SKIP
	JUMPE	P3,IRNOSK	;NO CHARS TO SKIP
IALGLP:	PUSHJ	P,GETBYT	;SKIP THE CRLF
	SOJG	P3,IALGLP
IRNOSK:	HRLI	P1,(POINT 7,0,34) ;POINT TO END OF WORD
	IDIVI	P2,5		;CORRECT COUNT
	IMULI	P2,5
	POPJ	P,

GETBYT:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;DID WE GET EOF ALREADY?
	 POPJ	P,		;YES. DON'T TRY AGAIN
	SOJGE	P2,GOTBYT	;STILL SOME THERE
	PUSHJ	P,IMAP		;NO
	 JRST	GETBYT		;Keep in synch
	POPJ	P,		;EOF

GOTBYT:	ILDB	P4,P1		;GET A CHAR
	POPJ	P,
IF20,<

IMAP:	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIE	T1,DI.DSK	;DISK?
	 JRST	XSIN		;NO. MUST DO SINR'S

	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM?
	 JRST	FNXTW		;YES. DO SEPARATELY

	MOVE	T1,BYTN(D)	;GET DESIRED BYTE #
	CAML	T1,EOFN(D)	;PAST EOF?
	  JRST	DPEOF		;YES. GO SET EOF FLAG

IMAPX:	PUSHJ	P,FNXTW		;MAP NEXT WINDOW
	MOVEM	P2,WCNT(D)	;SAVE ACTIVE BYTE COUNT

	MOVE	T1,BYTN(D)	;GET BYTE # OF NEXT WINDOW
	CAMG	T1,EOFN(D)	;PAST EOF?
	 POPJ	P,		;NO
	SUB	T1,EOFN(D)	;GET DIFF
	SUBI	P2,(T1)		;REDUCE # BYTES AVAIL
	MOVEM	P2,WCNT(D)	;SAVE ACTIVE BYTE COUNT
	POPJ	P,

DPEOF:	AOS	(P)		;SKIP RETURN MEANS EOF
	MOVX	T0,D%END	;Set EOF flag
	IORM	T0,FLAGS(D)
	POPJ	P,
>;END IF20
IF10,<

;GENERAL-PURPOSE IO ERROR MESSAGE
;	IOERR	(IOE,899,400,%,$I,<T1>)

IMAP:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM?
	  JRST	FNXTW		;YES

	DMOVEM	P1,IPTR(D)	;STORE POINTER, COUNT FOR MONITOR
	PUSH	P,T2
	PUSH	P,T3

	AOS	T1,BLKN(D)	;INCREMENT BLOCK #
	IMULI	T1,1200		;GET NEXT BLOCK FIRST BYTE #
	MOVEM	T1,BYTN(D)	;SAVE FOR EOFN CALC
	HLLZ	T2,CHAN(D)	;GET CHANNEL NUMBER FOR FILOP
	HRRI	T2,.FOINP	;SET FILOP FUNCTION
	MOVE	T1,[1,,T2]	;SET LENGTH,,ADDRESS
	FILOP.	T1,		;INPUT NEXT BUFFER
	  PUSHJ	P,TEOFCHK	;ERROR, COULD BE EOF

	POP	P,T3
	POP	P,T2
	DMOVE	P1,IPTR(D)	;GET NEW BYTE POINTER, COUNT
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;IF FILE ENDED,
	 AOS	(P)		;SKIP RETURN
	POPJ	P,		;ELSE DONE


TEOFCHK:
	LOAD	T2,INDX(D)	;GET DEV INDEX
	CAIN	T2,DI.TTY	;TERMINAL?
	TRNN	T1,IO.EOF	;YES, EOF?
	  JRST	EOFCHK		;NO, NOTHING SPECIAL

	MOVE	T2,[1,,T3]	;SET UP FOR CLOSE
	MOVEI	T3,.FOCLS
	HLL	T3,CHAN(D)
	FILOP.	T2,		;CLEAR EOF BIT, LEAVE TTY OPEN
	 $ECALL	IOE,%ABORT

EOFCHK:	MOVX	T0,D%END	;Get EOF flag
	TRNE	T1,IO.EOF	;EOF?
	 IORM	T0,FLAGS(D)	;Yes, this file is ended

	TRNE	T1,IO.EOF	;EOF OFF?
	TRNE	T1,IO.ERR+IO.EOT ;NO, ANY REAL ERR BITS?
	 $ECALL	IOE,%ABORT

	POPJ	P,		;RETURN FROM XIREC WITH T3 SET

>;END IF10
IF20,<


XSIN:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;EOF ALREADY FOUND?
	 JRST	%POPJ1		;YES. SKIP RETURN
	LOAD	T0,DVTYP(D)	;Is this a DISK append file?
	CAIN	T0,.DVDSK	; (If DVTYP is disk and we are here,
	 JRST	EOFGET		;it is, so get EOF).

	PUSH	P,T2		;SAVE CRITICAL ACS
	PUSH	P,T3

	AOS	BLKN(D)		;INCR BLOCK #
	LOAD	T1,IJFN(D)	;GET FILE JFN
	HRRO	T2,WADR(D)	;GET POINTER TO BUFFER
	MOVN	T3,WSIZ(D)	;GET WINDOW SIZE IN BYTES

	SINR%			;READ STRING
	  ERCAL	EOFCHK		;ERROR, POSSIBLE EOF

	MOVE	P1,WADR(D)	;MAKE BYTE POINTER TO BUFFER
	SUBI	P1,1		;POINT TO BEG-1
	HRLI	P1,(POINT 7,0,34)
	MOVE	P2,WSIZ(D)	;GET FULL WINDOW SIZE
	ADD	P2,T3		;GET # BYTES WE GOT
	MOVEM	P2,WCNT(D)	;SAVE FOR DIRECTION SWITCHING

	POP	P,T3		;RESTORE T ACS
	POP	P,T2
	JUMPG	P2,%POPJ	;RETURN TO COPY LOOP
	AOS	(P)		;OTHERWISE EOF
	POPJ	P,

;Here to return EOF because DISK APPEND file wanted to READ.

EOFGET:	MOVX	T0,D%END	;EOF
	IORM	T0,FLAGS(D)
	JRST	%POPJ1		;Skip return for EOF

;Routine to get ERROR or set D%END if EOF.
;Returns .+1 unless ERR= branch taken.

EOFCHK:	GTSTS%			;GET FILE STATUS
	TXNN	T2,GS%EOF	;EOF?  [*** err+eof??]
	  JRST	INPERR		;NO, INPUT ERROR
	MOVX	T0,D%END	;EOF, tell caller
	IORM	T0,FLAGS(D)
	POPJ	P,		;CONTINUE


INPERR:	AOS	T2,ERRN(D)	;BUMP ERROR COUNT
	CAILE	T2,^D10		;TOO MANY?
;	  IOERR	(INX,899,401,?,$J,,%ABORT) ;YES
;	IOERR	(INP,899,401,%,$J,,%POPJ) ;NO
	  $ECALL INX,%ABORT	;Yes
	$ECALL	INP,%POPJ	;No

> ;END IF20

RNRER1:	MOVE	T1,NREC(D)	;GET RECORD NUMBER
	$ECALL	RNR,%ABORT	;HAVEN'T INCR REC # YET

RNRERR:	MOVE	T1,NREC(U)	;GET RECORD NUMBER
	SUBI	T1,1		;POINT BACK TO CURRENT RECORD
;	IOERR	(RNR,25,510,?,Record $D has not been written,<T1>,%ABORT)
	$ECALL	RNR,%ABORT
;DECODE			

DECINI:	MOVE	T1,A.HSA	;GET STRING ADDR
	$BLDBP	T1		;Make byte ptr to start of string
	MOVEM	T1,IRBUF(D)	;STORE RECORD BUFFER POINTER

	SKIPGE	T1,A.HSL	;GET STRING LENGTH
	  SETZ	T1,		;NEGATIVE, SET TO 0
	MOVEM	T1,IRBLN(D)	;STORE AS RECORD BUFFER LENGTH
	MOVEM	T1,IRCNT(D)	;RETURN ZERO EMPTY CHARS IN REC BUFFER
	POPJ	P,		;RETURN


DECODE:	MOVE	T1,IRBLN(D)	;GET STRING LENGTH
	SKIPLE	IRCNT(D)	;IF 0 OR NEG, WE'RE AT END OF STRING
	 SUB	T1,IRCNT(D)	;GET CURRENT CHAR POSITION
	ADDI	T1,4		;GET # WORDS TAKEN
	IDIVI	T1,5
	ADDM	T1,IRBUF(D)	;SAVE NEW BUFFER PNTR
	IMULI	T1,5		;GET # CHARS USED UP
	MOVE	T2,IRBLN(D)	;DECR # CHARS IN STRING
	SUBI	T2,(T1)
	MOVEM	T2,IRBLN(D)	;SAVE NEW COUNT
	SETZM	IRCNT(D)	;SETUP FOR REST OF IREC
	POPJ	P,
	SUBTTL	OUTPUT

;%OREC IS CALLED FROM INSIDE FORIO ONLY, AT THE FIN CALL AT THE END
;OF ALL FORMATTED I/O WRITES.
;%ORECS IS CALLED FROM FORFMT (FOR "/" FORMAT) AND NMLST/LDIO (TO
;OUTPUT A RECORD). IT CALLS %OREC AND THEN SETS
;UP THE OUTPUT RECORD BUFFER AGAIN EXCEPT FOR ENCODE/DECODE

%ORECS:
%OREC:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%ENC	;ENCODE?
	 PUSHJ	P,FIXREC	;NO. DO CC SUBSTITUTION, /RECORDSIZE PADDING
ORECX:	LOAD	T1,INDX(D)	;GET DEV INDEX
	PUSHJ	P,@ODSP(T1)	;OUTPUT THE RECORD, AS APPROPRIATE FOR DEV
	AOS	NREC(U)		;COUNT RECORD

ORINI:	MOVE	T1,ORBUF(D)	;RESET BYTE POINTER
	MOVEM	T1,ORPTR(D)
	MOVE	T1,ORBLN(D)	;RESET BYTE COUNT
	MOVEM	T1,ORCNT(D)
	SETZM	ORLEN(D)	;CLEAR RECORD LENGTH
	SETZM	ORPOS(D)	;CLEAR VIRTUAL POS
	POPJ	P,		;DONE, READY FOR NEXT OUTPUT


ODSP:	IFIW	TOREC
	IFIW	DOREC
	IFIW	XOREC
	IFIW	XOREC
	IFIW	ENCODE

;ERROR MESSAGE OUTPUT
;ARGS:	 T1 = ADDRESS OF ASCIZ MESSAGE STRING

%EOREC:	PUSH	P,D
	PUSH	P,U
	SKIPN	U,U.ERR		;POINT TO ERR DDB
	  JRST	ETTY		;NONE, USE PSOUT
	
	PUSH	P,U.ERR		;RECURSIVE ERRS GO TO TTY
	SETZM	U.ERR
	MOVE	D,DDBAD(U)	;Set up D

	STKVAR	<ERPTR>		;ALLOCATE STACK VARS
	HRLI	T1,(POINT 7,)	;STORE POINTER TO MSG
	MOVEM	T1,ERPTR

	PUSHJ	P,%SETOUT	;Set file open for output

	PUSHJ	P,ROPOS		;Get output position
	CAIE	T1,1		;If not at column 1,
	 PUSHJ	P,%OCRLF	;Get to column 1

	MOVEI	T1," "		;GET A SPACE
	LOAD	T2,CC(U)	;GET /CC
	CAIN	T2,CC.FORT	;/CC:FORTRAN?
	  PUSHJ	P,%OBYTE	;YES, SPACE CARRIAGE CONTROL FOR ERRORS

EOLP:	ILDB	T1,ERPTR	;GET BYTE OF MSG
	JUMPE	T1,EOEND	;QUIT WHEN NULL
	PUSHJ	P,%OBYTE	;TYPE CHAR
	JRST	EOLP

EOEND:	PUSHJ	P,%OREC		;WRITE MESSAGE
	MOVX	T1,D%MOD	;Remember file modified
	IORM	T1,FLAGS(D)

	UNSTK			;CLEAN UP AND RETURN
	POP	P,U.ERR
	POP	P,U
	POP	P,D
	POPJ	P,

EOCRLF:	MOVEI	T1,15		;CRLF
	PUSHJ	P,%OBYTE
	MOVEI	T1,12
	PJRST	%OBYTE
ETTY:	SKIPN	D,D.TTY		;POINT TO TTY DDB IF OPEN
	  JRST	EPSOUT		;NO, NO COMPLICATIONS
	MOVE	T0,FLAGS(D)	;GET FLAGS
	TXNN	T0,D%OUT	;DOING OUTPUT?
	  JRST	EPSOUT		;NO, FINE
	PUSH	P,T1		;SAVE MESSAGE ADDRESS
	PUSHJ	P,ROPOS		;Get column position
	CAIE	T1,1		;Is last line out yet?
	 PUSHJ	P,%OREC		;No, write it
	MOVE	T0,FLAGS(D)	;Re-get flags
	TXNN	T0,D%SICR+D%SILF ;Suppress CRLF?
	 PUSHJ	P,%OCRLF	;No, get to column 1
	POP	P,T1		;RESTORE MSG ADDRESS

EPSOUT:	POP	P,U		;Restore pushed ACs
;	POP	P,DF
	POP	P,D
IF10,<	OUTSTR	(T1)		;TYPE MESSAGE
	OUTSTR	%CRLF## >
IF20,<
	HRROI	T1,(T1)
	PSOUT%
	HRROI	T1,%CRLF##
	PSOUT%
>
	POPJ	P,		;DONE
;OUTPUT CARRIAGE CONTROL
;IF CARRIAGE CONTROL IS BEING DONE, SUBSTITUTES FOR FIRST CHAR
;IF FIXED-LENGTH RECORDS, PADS OR TRUNCATES RECORD TO CORRECT LENGTH
;
;RETURN: ORPTR = BYTE POINTER TO FIRST CHAR OF RECORD
;	 ORCNT = NUMBER OF BYTES IN RECORD

FIXREC:	MOVE	T0,FLAGS(D)	;GET DDB FLAGS
	TXNN	T0,D%STCR	;DOLLAR FORMAT?
	 JRST	NODOL		;NO
	SKIPE	ORPOS(D)	;ANY VIRTUAL POSITION?
	 PUSHJ	P,SETPOS	;YES. SET IT UP
NODOL:	SETZM	ORPOS(D)	;DON'T LET TRAILING TABBING FOOL US!
	SKIPN	ORPTR(D)	;ANY RECORD BUFFER YET?
	 PUSHJ	P,EXPORB	;NO. SET ONE UP!
	MOVE	T1,ORBLN(D)	;GET BUFFER LENGTH
	SUB	T1,ORCNT(D)	;GET CURRENT POSITION
	CAML	T1,ORLEN(D)	;ARE WE SOMEWHERE INSIDE RECORD?
	 JRST	NSPOS		;NO. ALREADY AT END
	MOVE	T1,ORLEN(D)	;GET IT AGAIN
	PUSHJ	P,SPOS1		;SET TO END OF RECORD
	JRST	GOTPOS		;ORLEN OK AS IS

NSPOS:	MOVEM	T1,ORLEN(D)	;SAVE NEW LENGTH
GOTPOS:	SKIPE	T2,RSIZE(D)	;FIXED-LENGTH RECORDS?
	 CAMN	T2,ORLEN(D)	;YES. PERFECT FIT?
	  JRST	NPAD		;VARIABLE LENGTH OR PERFECT FIT. SKIP PADDING
	SUB	T2,ORLEN(D)	;CHECK AGAINST CHARS ALREADY WRITTEN
	JUMPG	T2,NTRUNC	;RECORD SHORTER THAN RECORDSIZE, FINE
	MOVE	T1,RSIZE(D)	;GET RECORDSIZE
	PUSHJ	P,SPOS1		;TRUNCATE RECORD TO ITS MAXIMUM SIZE
	JRST	NPAD		;DON'T PAD

NTRUNC:	PUSHJ	P,CHRPAD	;APPEND PAD CHARS TO GET RECORD TO RIGHT SIZE

NPAD:	LOAD	T1,CC(U)	;GET /CARRIAGECONTROL
	CAIN	T1,CC.FORT	;/CC:FORTRAN?
	  JRST	FIXCC		;YES, GO HANDLE
;HERE WHEN CC=LIST, IE DO NOT SUBSTITUTE CARRIAGE CONTROL CHAR

;APPEND TRAILING CRLF TO RECORD UNLESS $ FORMAT, IN WHICH CASE DON'T APPEND
;ANYTHING.  IF WRITING FIXED-LENGTH RECORDS, SKIP TO WORD BOUNDARY SO DISK
;FILES LOOK THE SAME AS V5, WITH ALL RECORDS STARTING IN A NEW WORD.

	MOVE	T0,FLAGS(D)
	TXZ	T0,D%SICR+D%SILF ;Turn off "suppress CRLF" flags
	MOVEM	T0,FLAGS(D)	;Make sure flags are in reasonable state
	TXZE	T0,D%STCR	;$ format in this record?
	 JRST	NCRLF		;Yes, skip CRLF

	MOVEI	T1,15		;CRLF
	PUSHJ	P,%OBYTE
	MOVEI	T1,12
	PUSHJ	P,%OBYTE
	MOVX	T1,D%SICR+D%SILF ;Set flags that say we already got a CRLF
	IORM	T1,FLAGS(D)
	JRST	NLPAD		;GO APPEND NULLS IF NECESSARY

;Updated DDB flags are in T0.

NCRLF:	TXO	T0,D%SICR	;SET FLAG SO TTY INPUT WILL FIND PROMPT
	MOVEM	T0,FLAGS(D)	;Store updated flags
	SKIPN	RSIZE(D)	;FIXED-LENGTH RECORDS?
	  JRST	NLPAD		;NO, SKIP
	MOVEI	T1,0		;PUT IN 2 NULLS TO ALIGN AS IF CRLF WERE THERE
	PUSHJ	P,%OBYTE
	PUSHJ	P,%OBYTE

NLPAD:	PUSHJ	P,NULPAD	;PAD TO WORD BOUNDARY IF NECESSARY

	MOVE	T1,ORBLN(D)	;SET RCNT TO NUMBER OF BYTES IN RECORD
	SUBB	T1,ORCNT(D)

	MOVE	T1,ORBUF(D)	;GET REC BUFFER ADDRESS
	MOVEM	T1,ORPTR(D)	;STORE IT
IF20,<	MOVEM	T1,G.PRP   >	;SAVE POINTER TO PROMPT STRING, MIGHT NEED IT
	POPJ	P,
;HERE WHEN CC=FORTRAN, IE SUBSTITUTE CARRIAGE CONTROL CHAR

;REPLACE FIRST CHAR WITH CARRIAGE CONTROL SEQUENCE. THE CARRIAGE CONTROL
;SEQUENCE STARTS WITH <CR> UNLESS THE PREVIOUS LINE ENDED WITH $ FORMAT.
;THEN COME 0-3 CHARACTERS, DEPENDING ON THE CARRIAGE CONTROL CHAR.  IF
;THIS LINE HAS A $ FORMAT, SET A FLAG TO PREVENT THE <CR> NEXT TIME.

FIXCC:	PUSHJ	P,ENDNUL	;Drop a null at EOR
	MOVE	T2,ORBLN(D)	;SET RCNT TO LENGTH OF RECORD NOW,
	SUBB	T2,ORCNT(D)	; BEFORE CC SUBSTITUTION

	MOVE	T1,ORBUF(D)	;POINT TO REC BUFFER
	JUMPE	T2,[AOS ORCNT(D)	;IF ZERO-LENGTH RECORD,
		    MOVSI T2,(" "B6) ;MAKE INTO A RECORD CONTAINING ONE SPACE
		    MOVEM T2,(T1)
		    JRST .+1]
	HRLI	T1,(POINT 7,,6)	;MAKE POINTER TO FIRST CHAR OF RECORD
	MOVEM	T1,ORPTR(D)	;STORE POINTER
IF20,<	MOVEM	T1,G.PRP   >	;STORE POINTER TO PROMPT STRING, MIGHT NEED IT
	MOVX	T0,D%STCR	;Flag to set (maybe)
	LDB	T1,T1		;GET CC CHARACTER
	CAIN	T1,"$"		;DOLLAR CARRIAGE CONTROL?
	 IORM	T0,FLAGS(D)	;Yes, same as $ FORMAT and space CC

	CAIL	T1,"*"		;RANGE CHECK CC CHAR
	CAILE	T1,"3"
	  MOVEI	T1,"*"-1	;OUT OF RANGE, TREAT AS SPACE
	HLRZ	T2,CCTAB-"*"(T1) ;GET REPEAT COUNT
	HRRZ	T1,CCTAB-"*"(T1) ;GET CONTROL CHAR WHICH DOES THE CC

	MOVE	T4,FLAGS(D)	;T4:= DDB flags
	TXZN	T4,D%SILF	;SUPPRESS INITIAL LF?
	  JRST	NSUP		;NO
	CAIN	T1,12		;YES, DO WE HAVE A LF TO SUPPRESS?
	  SUBI	T2,1		;YES, DECREMENT LF COUNT
NSUP:	JUMPLE	T2,NLF		;IF NO CC CHARS LEFT, SKIP
	PUSHJ	P,DPBD		;STORE CHAR AND DECREMENT RPTR
	SOJG	T2,.-1		;REPEAT UNTIL DONE

NLF:	MOVEI	T1,15		;GET CR
	TXZN	T4,D%SICR	;SUPPRESS INITIAL CR?
	  PUSHJ	P,DPBD		;NO, APPEND CR TO FRONT

	SOS	ORCNT(D)		;CORRECT COUNT, CC CHAR IS NOW GONE
	TXZE	T4,D%STCR	;SUPPRESS TRAILING CR ON THIS LINE?
	  TXO	T4,D%SICR+D%SILF ;YES, SUPPRESS INITIAL CRLF OF NEXT LINE
	MOVEM	T4,FLAGS(D)	;Store updated DDB flags
	POPJ	P,		;ALL DONE
;ROUTINE TO NORMALIZE CRLF POSITION, BY TYPING PENDING CRLF, IF ANY
;
;WHEN WRITING A FILE WITH CC=FORTRAN, THE CRLFS COME BEFORE THE
;RECORDS INSTEAD OF AFTER THEM.  THE REST OF THE WORLD PUTS CRLFS
;AFTER THEIR RECORDS.  THIS ROUTINE IS CALLED TO GET IN SYNC WITH THE
;OUTSIDE WORLD WHEN NECESSARY.
;
;CALLED:
;	BEFORE AN ERROR MESSAGE IS TYPED
;	WHEN SWITCHING FROM OUTPUT TO INPUT ON TTY

%OCRLF:	MOVX	T0,D%SILF+D%SICR ;Suppress next CRLF
	IORM	T0,FLAGS(D)

	MOVEI	T1,2		;SET BYTE COUNT, PTR
	MOVEM	T1,ORCNT(D)
	MOVE	T1,ORBUF(D)
	MOVEM	T1,ORPTR(D)

	MOVE	T2,%CRLF	;GET A CRLF
	MOVEM	T2,(T1)		;PUT IT IN THE RECORD BUFFER

IF20,<	SETZM	G.PRP	>	;SET NO PROMPT STRING AVAILABLE
	PJRST	ORECX		;FORCE THE CRLF OUT

;DEPOSIT BYTE AND DECREMENT  (THE OPPOSITE OF ILDB)
;T1 = BYTE, RPTR = 7-BIT BYTE POINTER
;Uses T1 and T3 only

DPBD:	AOS	ORCNT(D)	;BUMP BYTE COUNT
	DPB	T1,ORPTR(D)	;DEPOSIT BYTE
	MOVSI	T3,(47B5)	;DECREMENT RPTR
	ADD	T3,ORPTR(D)
	TLCN	T3,(1B0)
	  SUB	T3,[430000,,1]
	MOVEM	T3,ORPTR(D)
	POPJ	P,


;ROUTINES TO PAD RECORD
;NULPAD APPENDS 0-4 NULLS TO RECORD TO BRING IT TO WORD BOUNDARY
;CHRPAD APPENDS (T2) PAD CHARACTERS TO RECORD

NULPAD:	SKIPN	RSIZE(D)	;/REC SIZE?
	  POPJ	P,		;NO, NO NULL PADDING IS NECESSARY
	MOVE	T1,ORBLN(D)	;GET REC BUF SIZE
	SUB	T1,ORCNT(D)	;GET # CHARS WRITTEN
	MOVNI	T1,(T1)		;GET -SIZE
	IDIVI	T1,5		;GET T2 = -SIZE MOD 5 = NUMBER OF NULLS
	JUMPE	T2,%POPJ	;LEAVE ZERO REMAINDER ALONE
	  ADDI	T2,5		;OTHERWISE CONVERT TO POSITIVE
	TDZA	T1,T1		;GET A NULL AND SKIP INTO LOOP
CHRPAD:	LOAD	T1,PADCH(U)	;GET PAD CHAR
	PUSHJ	P,%OBYTE	;OUTPUT A PAD CHAR
	SOJG	T2,.-1		;KEEP PADDING UNTIL RECORD IS RIGHT SIZE
	POPJ	P,		;Return when done



;CARRIAGE CONTROL TABLE
;(LH) REPEAT COUNT, (RH) CONTROL CHAR TO SUBSTITUTE

				;CHAR	       CC SEQ	ACTION ON PRINTER

	XWD	1,12		;SPACE		<LF>	NEXT LINE
CCTAB:	XWD	1,23		;*		<^S>	NEXT LINE, NO PAGE SKIP
	XWD	0,		;+			OVERWRITE CURRENT LINE
	XWD	1,21		;,		<^Q>	NEXT EVEN LINE
	XWD	3,12		;-	<LF><LF><LF>	SKIP 2 LINES
	XWD	1,22		;.		<^R>	NEXT THIRD LINE
	XWD	1,24		;/		<^T>	NEXT 10TH LINE
	XWD	2,12		;0	    <LF><LF>	SKIP 1 LINE
	XWD	1,14		;1		<FF>	PAGE SKIP
	XWD	1,20		;2		<^P>	NEXT 30TH LINE
	XWD	1,13		;3		<VT>	NEXT 20TH LINE
;DISK OUTPUT

IF20,<
DOREC:	MOVX	T0,D%MOD	;Set file modified
	IORM	T0,FLAGS(D)
XOREC:
>;END IF20

IF10,<
;ARBITRARY DEVICE

DOREC:
TOREC:
XOREC:
>;END IF10

	PUSHJ	P,%SAVE2	;Save P1 and P2
	DMOVE	P1,OPTR(D)	;GET FILE POINTER/COUNT
	DMOVE	T2,ORPTR(D)	;GET RECORD BYTE POINTER AND RECORD LENGTH
	JUMPE	T3,DOFIN	;IF NO BYTES LEFT IN RECORD, DONE

DOLP:	SOJL	P2,DOFULL	;IF FILE WINDOW FULL, GO MOVE WINDOW
	ILDB	T1,T2		;GET BYTE FROM RECORD
	IDPB	T1,P1		;STORE BYTE IN FILE
	SOJG	T3,DOLP		;COPY WHOLE RECORD
				;ALL DONE, FALL INTO DOFIN

DOFIN:	DMOVEM	P1,OPTR(D)	;STORE UPDATED POINTER/COUNT

IF10,<
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%INT	;INTERACTIVE DEVICE?
	 PUSHJ	P,NOTRAN	;YES. OUTPUT BUFFER
>;END IF10

	MOVE	T1,BYTN(D)	;GET BYTE # OF NEXT WINDOW
	SUBI	T1,(P2)		;GET LAST BYTE IN USE
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM FILE?
	 CAMLE	T1,EOFN(D)	;YES. ONLY STORE LARGER EOFN
	  MOVEM	T1,EOFN(D)	;SAVE AS EOF PNTR

	POPJ	P,		;DONE

DOFULL:	PUSHJ	P,OMAP		;OUTPUT A WINDOWFUL
	JRST	DOLP		;STAY IN SYNCH
IF20,<

OMAP:	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIN	T1,DI.DSK	;DISK?
	 JRST	FNXTW		;YES. JUST MAP NEXT WINDOW
;	JRST	XSOUT		;NO. OTHER

;OUTPUT ROUTINES

;ARBITRARY DEVICE

XSOUT:	JUMPE	P1,XSKP		;IF NO CHARS, JUST PREPARE WINDOW
	PUSHJ	P,%PUSHT	;SAVE T ACS
	LOAD	T1,OJFN(D)	;GET JFN
	HRRO	T2,WADR(D)	;GET WINDOW ADDR
	MOVN	T3,WSIZ(D)	;GET WINDOW SIZE IN BYTES
	JUMPLE	P2,.+2		;IF IN MIDDLE OF WINDOW
	 ADD	T3,P2		;DECREMENT ACTIVE BYTE COUNT
	SOUTR%			;OUTPUT THE STRING
	  ERJMP	OUTERR		;ERROR, GO TELL USER
	PUSHJ	P,%POPT		;RESTORE T ACS

XSKP:	AOS	BLKN(D)		;INCR BLOCK #
	MOVE	P1,WADR(D)	;SETUP BYTE PNTR
	SUBI	P1,1		;POINT TO BEG-1
	HRLI	P1,(POINT 7,0,34)
	MOVE	P2,WSIZ(D)	;FULL WINDOW AVAILABLE
	POPJ	P,		;DONE

OUTERR:	AOS	T2,ERRN(D)	;BUMP ERROR COUNT
	CAIG	T2,^D10		;TOO MANY?
;	  IOERR	(OUX,899,401,?,$J,,%ABORT) ;YES
;	IOERR	(OUT,899,401,%,$J,,%POPJ) ;NO
	  $ECALL OUX,%ABORT	;Yes
	$ECALL	OUT,%POPJ	;No


;TTY OUTPUT
TOREC:	LOAD	T1,OJFN(D)	;GET JFN
	MOVE	T2,ORPTR(D)	;GET POINTER TO START OF RECORD
	MOVN	T3,ORCNT(D)	;GET NEGATIVE OF BYTE COUNT
	JUMPGE	T3,%POPJ	;LEAVE NOW IF 0-BYTE STRING
	SOUTR%			;OUTPUT THE STRING
	  ERJMP	OUTERR		;ERROR, GO TELL USER
	POPJ	P,		;DONE
>;END IF20
IF10,<

OMAP:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM?
	 JRST	FNXTW		;WRITE RANDOM BUFFER

NOTRAN:	PUSHJ	P,%PUSHT
	DMOVEM	P1,OPTR(D)	;STORE POINTER, COUNT FOR MONITOR
	AOS	T1,BLKN(D)	;INCR BLOCK #
	IMULI	T1,1200		;GET BYTE # OF NEXT BLOCK
	MOVEM	T1,BYTN(D)	;SAVE FOR EOFN CALC
	HLLZ	T2,CHAN(D)	;GET CHANNEL NUMBER FOR FILOP
	HRRI	T2,.FOOUT	;SET FILOP FUNCTION
	MOVE	T1,[1,,T2]	;SET LENGTH,,ADDRESS
	FILOP.	T1,		;OUTPUT A BUFFER
	 $ECALL	IOE,%ABORT

	DMOVE	P1,OPTR(D)	;GET NEW BYTE POINTER, COUNT
	PJRST	%JPOPT

>;END IF10
;ENCODE

ENCINI:	MOVE	T1,A.HSA	;GET STRING ADDR
	$BLDBP	T1		;Build 7-bit byte ptr.
	MOVEM	T1,ORPTR(D)
	MOVEM	T1,ORBUF(D)
	SKIPG	T2,A.HSL	;GET STRING LENGTH
	 SETZB	T2,A.HSL	;IF ZERO OR NEG, SET TO ZERO
	JUMPE	T2,NENCFL	;NO FILL IF NO CHARS
	MOVEI	T3," "		;SETUP WITH BLANKS
ENCLP:	IDPB	T3,T1
	SOJG	T2,ENCLP
NENCFL:	MOVE	T1,A.HSL	;RESET BYTE COUNT
	MOVEM	T1,ORCNT(D)
	MOVEM	T1,RSIZE(D)	;AND RECORD SIZE
	MOVEM	T1,ORBLN(D)	;AND REC BUFFER LENGTH
	SETZM	T1,ORLEN(D)	;AND RECORD LENGTH
	POPJ	P,		;DONE, READY FOR OUTPUT

ENCODE:	MOVE	T1,ORBLN(D)	;GET # CHARS IN STRING
	SUB	T1,ORCNT(D)	;GET CURRENT CHAR POS
	SKIPE	ORPOS(D)	;WAITING ON POSITIONING FORMAT?
	 MOVE	T1,ORPOS(D)	;YES. SUBSTITUTE IT
	ADDI	T1,4		;GET # WORDS
	IDIVI	T1,5
	ADDM	T1,ORBUF(D)	;SAVE NEW BUFFER ADDR
	IMULI	T1,5		;GET # CHARS IN THOSE WORDS
	MOVE	T2,ORBLN(D)	;GET # CHARS IN STRING
	SUBI	T2,(T1)		;DECR # CHARS
	MOVEM	T2,ORBLN(D)	;SAVE IT
	POPJ	P,
	SUBTTL	T FORMAT

;ROUTINE TO READ RECORD POSITION
;RETURN: T1 = BYTE NUMBER OF NEXT BYTE TO/FROM RECORD
;	      I.E., NUMBER OF BYTES ALREADY READ FROM RECORD OR STORED IN IT
;PRESERVES T2-T5

%RPOS:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%IO		;INPUT OR OUTPUT?
	  JRST	ROPOS		;OUTPUT, GO HANDLE

RIPOS:	MOVE	T1,IRLEN(D)	;GET RECORD LENGTH
	SUB	T1,IRCNT(D)	;SUBTRACT # CHARS LEFT IN IT
	ADDI	T1,1		;BEG OF RECORD IS COL 1
	POPJ	P,

ROPOS:	MOVE	T1,ORBLN(D)	;GET RECORD BUFFER LENGTH
	SUB	T1,ORCNT(D)	;SUBTRACT EMPTY SPACE
	ADDI	T1,1		;BEG OF RECORD IS COL 1
	SKIPE	ORPOS(D)	;IS THERE A VIRTUAL POSITION?
	 MOVE	T1,ORPOS(D)	;YES. USE IT AS CURRENT POSITION
	POPJ	P,		;RETURN WITH BYTE NUMBER
;ROUTINE TO SET RECORD POSITION
;ARG:	 T1 = BYTE NUMBER
;SETS SO THAT NEXT IBYTE/OBYTE CALL GETS OR STORES THE GIVEN BYTE

%SPOS:	JUMPG	T1,.+2		;OK IF .GT. 0
	 MOVEI	T1,1		;SET TO 1 IF NOT
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%IO		;INPUT?
	  JRST	SOPOS		;NO, OUTPUT

SIPOS:	SUBI	T1,1		;SET POSITION TO ONE BEFORE IT
	MOVE	T2,IRLEN(D)	;GET LENGTH OF RECORD
	SUBI	T2,(T1)		;COMPUTE NEW EMPTY-BYTE COUNT
	MOVEM	T2,IRCNT(D)	;SET TO DESIRED COLUMN
	JUMPL	T2,ISEOR	;NO BP SETUP IF BEYOND RECORD

	IDIVI	T1,5		;BREAK BYTE # INTO WORD NUMBER AND BYTE OFFSET
	LDB	T3,[POINT 6,IRBUF(D),5] ;Get "P"-field of byte ptr.
	CAILE	T3,44		;Skip if a local byte ptr.
	 JRST	SIPOS1		;No
	MOVE	T2,BPTAB(T2)	;GET LH OF BYTE POINTER
	HRR	T2,IRBUF(D)	;PUT IN RH POINTING TO RECORD BUFFER
	ADDI	T2,(T1)		;ADD IN WORD OFFSET
	MOVEM	T2,IRPTR(D)	;STORE NEW BYTE POINTER
	POPJ	P,

;The ENCODE/DECODE extended addressing version.

SIPOS1:	MOVE	T3,IRBUF(D)	;Get starting BP
	TXZ	T3,77B5		;Clear "P" field
	TDO	T3,BPTABE(T2)	;Get new "P" field
	ADDI	T3,(T1)		;Add in word offset
	MOVEM	T3,IRPTR(D)	;Store new byte ptr.
	POPJ	P,		;Return

ISEOR:	MOVX	T0,D%EOR	;Set END-OF-RECORD flag
	IORM	T0,FLAGS(D)
	POPJ	P,		;DONE

SOPOS:	MOVEM	T1,ORPOS(D)	;STORE NEXT CHARACTER POSITION
	POPJ	P,

;SETPOS - SET OUTPUT POSITION ROUTINE. T FORMAT AND X FORMAT
;JUST SET ORPOS(D), THE VIRTUAL POSITION. SETPOS IS CALLED IF
;A CHARACTER IS ACTUALLY OUTPUT IN THAT POSITION. FIRST, THE
;CURRENT POSITION IS CHECKED AGAINST THE LAST RECORDED LENGTH.
;IF THE CURRENT POSITION IS GREATER THAN THE LENGTH, A NEW
;LENGTH IS RECORDED. IF ORPOS (THE DESIRED POSITION) IS
;WITHIN THE NEW LENGTH, WE JUST GO AND SETUP THE PNTR/COUNT
;APPROPRIATELY. IF IT IS NOT, WE PAD THE CURRENT RECORD WITH
;BLANKS. IF THE CURRENT POSITION WAS LESS THAN THE LAST
;RECORDED LENGTH, THEN WE JUST CHECK THIS LENGTH AGAINST
;ORPOS. IF ORPOS IS WITHIN THE LENGTH, WE GO AND SETUP THE
;PNTR/COUNT APPROPRIATELY. OTHERWISE, WE SETUP THE PNTR/COUNT
;TO THE OLD LENGTH AND PAD THE RECORD WITH BLANKS TO ORPOS.

SETPOS:	PUSHJ	P,%PUSHT	;SAVE T ACS
	SOS	ORPOS(D)	;DESIRED POSITION IS ONE LESS THAN SPECIFIED
	SKIPN	ORBUF(D)	;ANY BUFFER ALLOCATED YET?
	 PUSHJ	P,EXPORB	;NO. ALLOCATE ONE
	MOVE	T1,ORBLN(D)	;GET RECORD BUFFER LENGTH
	SUB	T1,ORCNT(D)	;GET CURRENT CHARACTER POSITION
	CAMGE	T1,ORLEN(D)	;NEW ONE BIGGER OR SAME?
	 JRST	LENGOK		;NO. SETUP WITHIN RECORD
	MOVEM	T1,ORLEN(D)	;YES. STORE NEW ONE
	CAMN	T1,ORPOS(D)	;ARE WE WHERE WE WANT TO BE PRECISELY?
	 JRST	CLRVIR		;YES. TIME TO DO NOTHING
	CAMLE	T1,ORPOS(D)	;SMALLER THAN DESIRED POSITION?
	 JRST	SPOSV		;NO. JUST SETUP PNTR/COUNT
	JRST	OPAD		;YES. PAD WITH SPACES

LENGOK:	MOVE	T1,ORLEN(D)	;GET BUFFER LENGTH AGAIN
	CAML	T1,ORPOS(D)	;IS LENGTH SMALLER THAN DESIRED POSITION?
	 JRST	SPOSV		;NO. JUST SETUP PNTR/COUNT
	PUSHJ	P,SPOS1		;YES. SET CURRENT POSITION TO END OF RECORD
				;AND PAD WITH BLANKS
OPAD:	MOVE	T1,ORBLN(D)	;GET BUFFER LENGTH
	SUB	T1,ORCNT(D)	;GET CURRENT POSITION
	MOVE	T2,ORPOS(D)	;GET DESIRED POSITION
	SUBI	T2,(T1)		;GET # BLANKS TO PAD
OPADLP:	MOVEI	T1," "		;GET A BLANK
	PUSHJ	P,LOBYTE	;DON'T HAVE TO CHECK ORPOS EACH TIME
	SOJG	T2,OPADLP
CLRVIR:	SETZM	ORPOS(D)	;PREVENT REPEAT OF ABOVE
	PUSHJ	P,%POPT		;RESTORE T ACS
	POPJ	P,

SPOSV:	MOVE	T1,ORPOS(D)	;GET DESIRED POSITION
	SETZM	ORPOS(D)	;PREVENT REPEAT OF ABOVE
	PUSHJ	P,SPOS1		;SET THE POSITION
	PUSHJ	P,%POPT		;RESTORE T ACS
	POPJ	P,

SPOS1:	MOVE	T2,ORBLN(D)	;GET BUFFER LENGTH
	SUBI	T2,(T1)		;GET NEW EMPTY-BYTE COUNT
	MOVEM	T2,ORCNT(D)	;SET NEW COUNT
	IDIVI	T1,5		;BREAK BYTE # INTO WORD NUMBER AND BYTE OFFSET
	LDB	T3,[POINT 6,ORBUF(D),5] ;Get P-field of byte ptr.
	CAILE	T3,44		;Skip if regular, local byte ptr.
	 JRST	SPOS2		;No, use other BP table
	MOVE	T2,BPTAB(T2)	;Get LH of byte ptr.
	HRR	T2,ORBUF(D)	;Put in RH of byte ptr.
	ADDI	T2,(T1)		;Add in word offset.
	MOVEM	T2,ORPTR(D)	;STORE NEW BYTE POINTER
	POPJ	P,		;DONE

;The ENCODE/DECODE extended addressing version.

SPOS2:	MOVE	T3,ORBUF(D)	;Get starting BP of buffer
	TXZ	T3,77B5		;Clear BP bits
	TDO	T3,BPTABE(T2)	;Get new BP bits.
	ADDI	T3,(T1)		;Add in word offset
	MOVEM	T3,ORPTR(D)	;Store new byte pointer.
	POPJ	P,		;Done

BPTAB:	POINT	7,		;BYTE 0
	POINT	7,,6		;BYTE 1
	POINT	7,,13		;BYTE 2
	POINT	7,,20		;BYTE 3
	POINT	7,,27		;BYTE 4

BPTABE:	610000,,0		;BYTE 0
	620000,,0		;BYTE 1
	630000,,0		;BYTE 2
	640000,,0		;BYTE 3
	650000,,0		;BYTE 4
	SUBTTL	UNFORMATTED I/O

UNFI:	AOS	NREC(U)		;UPDATE RECORD COUNT
	DMOVE	P1,IPTR(D)	;GET FILE PNTR/COUNT
	SKIPN	P4,RSIZE(D)	;REC SIZE SPECIFIED?
	  HRLOI	P4,377777	;NO, USE BIG RECORDS

	PUSHJ	P,ILSCW1	;READ START LSCW
	 $ECALL EOF,%ABORT	;EOF. GO START READING THE I/O LIST
				;WHICH WILL FLUSH ITSELF OUT

	SETZ	T3,		;SET NO ARRAY IN PROGRESS YET

UILP:	JUMPG	T3,UIWIN	;IF WE HAVE AN ARRAY ADDRESS, CONTINUE WITH IT
	PUSHJ	P,%GTIOX	;GET NEXT ARRAY FROM IO LIST
	JUMPE	T3,UIEND	;IF NONE, GO FINISH UP

UIWIN:	JUMPLE	P4,UIZERO	;RECORDSIZE WORDS READ, GO SKIP TO END LSCW
	JUMPG	P2,UISEG	;IF DATA LEFT IN WINDOW, CONTINUE WITH IT
	PUSHJ	P,UINXTW	;READ NEXT WINDOW
	 JRST	UIEND		;GO CHECK IF EOF OK

UISEG:	JUMPG	P3,UIBLT	;IF DATA LEFT IN SEGMENT, CONTINUE WITH IT
UISEGX:	PUSHJ	P,ILSCWX	;READ LSCW OF NEXT SEGMENT
	JUMPLE	P3,UIZERO	;FOUND NULL TYPE 2 OR TYPE 3, GO STORE ZEROES

UIBLT:	MOVEI	T2,(T3)		;GET MIN OF ARRAY LENGTH
	CAILE	T2,(P2)		; AND WINDOW LENGTH
	  MOVEI	T2,(P2)
	CAILE	T2,(P3)		; AND SEGMENT LENGTH
	  MOVEI	T2,(P3)
	CAILE	T2,(P4)		; AND RECORD LENGTH
	  MOVEI	T2,(P4)
	JUMPE	T2,UILP		;DON'T LOSE ON BOUNDARY

;T1/ address of user's array
;P1/ local FOROTS address of data
;T2/ number of words to copy

IF20,<
	TLNN	T1,-1		;Extended addressing?
	 JRST	UIBLT1		;No, normal BLT

;Use XBLT
	PUSH	P,T2		;Save acs
	PUSH	P,T3		; Used by XBLT
				;T2/ # words to copy
	XMOVEI	T3,1(P1)	;T3/ "From" -- get FOROTS address of data
	MOVE	T4,T1		;T4/ "To"-- user's array.
	EXTEND	T2,[XBLT]	;** Copy the data **
	POP	P,T3		;Restore acs
	POP	P,T2
	ADDI	T1,(T2)		;Point to END+1 of BLT.
	JRST	UIBLT2		;Skip normal BLT

>;END IF20
UIBLT1:	MOVSI	T4,1(P1)	;GET BLT-FROM ADDRESS
	HRRI	T4,(T1)		;AND BLT-TO ADDRESS
	ADDI	T1,(T2)		;POINT TO END+1 OF BLT
	BLT	T4,-1(T1)	;MOVE DATA INTO ARRAY

UIBLT2:	ADDI	P1,(T2)		;INCREMENT ADDRESS OF DATA IN WINDOW
	SUBI	P2,(T2)		;DECREMENT COUNT OF DATA LEFT IN WINDOW
	SUBI	P3,(T2)		;DECREMENT COUNT OF DATA LEFT IN SEGMENT
	SUBI	P4,(T2)		;DECREMENT COUNT OF DATA LEFT IN RECORD
	SUBI	T3,(T2)		;DECREMENT COUNT OF DATA LEFT IN ARRAY
	JRST	UILP		;CONTINUE

UIZERO:	SETZM	(T1)		;CLEAR FIRST WORD
	CAIG	T3,1		;MORE THAN 1 WORD?
	 JRST	UZSKP2		;NO

IF20,<
	TLNN	T1,-1		;Extended addressing?
	 JRST	UZSKP1		;No, normal BLT

;Use XBLT
	PUSH	P,T2		;Save acs
	PUSH	P,T3
	PUSH	P,T4
	MOVE	T2,T3		;T2/ # words to copy
	MOVE	T3,T1		;t3/ "from" the array
	XMOVEI	T4,1(T1)	;T4/ "to" array+1
	EXTEND	T2,[XBLT]	;** Zero array **
	POP	P,T4		;Restore saved acs
	POP	P,T3
	POP	P,T2
	JRST	UZSKP2
>;END IF20

UZSKP1:	MOVSI	T4,(T1)		;SET BLT-FROM ADDRESS
	HRRI	T4,1(T1)	;AND BLT-TO ADDRESS
	ADDI	T1,(T3)		;POINT TO END+1 OF BLT
	  BLT	T4,-1(T1)	;CLEAR WHOLE ARRAY

;Here when BLT (or XBLT) has been done to clear array
UZSKP2:	PUSHJ	P,%GTIOX	;GET NEXT ARRAY FROM IO LIST
	JUMPN	T3,UIZERO	;IF THERE IS ONE, ZERO IT TOO

UIEND:	PUSHJ	P,ILSCW3	;SKIP TO END LSCW
UIRET:	DMOVEM	P1,IPTR(D)	;STORE FILE POINTER/COUNT
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%END	;REACH EOF?
	 POPJ	P,		;NO
	TXNE	T0,D%BIN	;BINARY FILE?
	 $ECALL	BBF,%ABORT	;YES. BAD FORMAT
	$ECALL	EOF,%ABORT	;NO. JUST EOF


IF10,<
UINXTW:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;AT EOF?
	 JRST	UIEOF		;YES. REPORT OR JRST AWAY
	PUSHJ	P,UNXTW		;MAP NEXT WINDOW
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END	;SEQUENTIAL FILE AND EOF?
	 JRST	UIEOF		;YES. REPORT OR JRST AWAY
	JRST	%POPJ1		;NO
> ;IF10

IF20,<
UINXTW:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;IN FILE?
	 JRST	UIEOF		;NO. REPORT OR JRST AWAY
	TXNE	T0,D%RAN	;RANDOM?
	 JRST	UIRAN		;YES. HANDLE SEPARATELY
	LOAD	T0,INDX(D)	;GET DEVICE INDEX
	CAIE	T0,DI.DSK	;DISK?
	 JRST	UTNXTW		;NO. DON'T PROCESS EOFN
	MOVE	T0,BYTN(D)	;GET DESIRED WORD #
	CAML	T0,EOFN(D)	;WITHIN FILE?
	 JRST	UIEOF		;NO. EOF RETURN
	PUSHJ	P,UNXTW		;READ NEXT WINDOW
	MOVE	T0,BYTN(D)	;GET WORD # OF NEXT WINDOW
	CAMG	T0,EOFN(D)	;PAST EOF?
	 JRST	UINEOF		;NO
	SUB	T0,EOFN(D)	;GET DIFF
	SUB	P2,T0		;SET COUNT TO WHAT'S THERE
UINEOF:	MOVEM	P2,WCNT(D)	;SAVE ACTIVE WORD COUNT
	JRST	%POPJ1

UTNXTW:	PUSHJ	P,UNXTW		;JUST GET A NEW WINDOW
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%END	;EOF?
	 AOS	(P)		;NO. SKIP RETURN
	POPJ	P,

UIRAN:	PUSHJ	P,UNXTW		;GET NEXT WINDOW
	JRST	%POPJ1
> ;IF20

UIEOF:	MOVX	T0,D%END	;Set EOF flag
	IORM	T0,FLAGS(D)
	POPJ	P,

;HERE AT START OF RECORD
;READ START LSCW
;0 MEANS RANDOM RECORD WAS NEVER WRITTEN

ILSCW1:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%BIN	;BINARY MODE?
	  JRST	IMG1		;NO, IMAGE, NO LSCWS
	PUSHJ	P,IWORD		;GET WORD FROM BINARY FILE
	  POPJ	P,		;EOF, MEANS END OF WHOLE FILE
	JUMPE	T1,RNRERR	;ZERO, RECORD WASN'T WRITTEN
	LDB	T2,[POINT 9,T1,8] ;GET SEGMENT CONTROL WORD
	CAIE	T2,1		;START LSCW?
;	  IOERR	(BBF,25,302,?,Bad format binary file,,%ABORT)
	 $ECALL	BBF,%ABORT	;?Bad format binary file
	MOVEI	P3,(T1)		;GET SEGMENT LENGTH FROM RH
	SOJA	P3,%POPJ1	;REMOVE LSCW FROM COUNT, RETURN

IMG1:	MOVEI	P3,-1		;SET LARGE SEGMENT
	JRST	%POPJ1		;DONE

;HERE WHEN START OR CONTINUE SEGMENT ENDS
;MUST SEE CONTINUATION OR END LSCW

ILSCWX:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%BIN	;BINARY MODE?
	  JRST	IMG2		;NO, IMAGE, FAKE A CONTINUATION LSCW
	PUSHJ	P,%PUSHT	;SAVE T ACS
ILX1:	PUSHJ	P,IWORD		;GET WORD FROM BINARY FILE
	 $ECALL	BBF,%ABORT	;IOERR: ?"Bad format binary file"
	SETO	P3,		;ASSUME 0 SEGMENT LENGTH
	LDB	T2,[POINT 9,T1,8] ;GET SEGMENT CONTROL WORD
	CAIN	T2,3		;END LSCW?
	  JRST	ILXEND		;YES, END OF LOGICAL RECORD
	CAIE	T2,2		;CONTINUATION LSCW?
	 $ECALL	BBF,%ABORT	;NO. BAD LSCW, BAD BINARY FILE
	MOVEI	P3,(T1)		;GET SEGMENT LENGTH FROM RH
	SUBI	P3,1		;REMOVE LSCW FROM COUNT
ILXEND:	PJRST	%JPOPT		;RESTORE T ACS AND RETURN

IMG2:	MOVEI	P3,-1		;SET LARGE SEGMENT
	POPJ	P,


;HERE AT END OF IO LIST
;POSITION FILE JUST AFTER END LSCW
;NUMBER OF WORDS TO DISCARD IS .GE. 0 IN P3

ILSCW3:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%BIN	;BINARY?
	  JRST	IMG3		;NO, NO LSCW
	JUMPL	P3,%POPJ	;IF ALREADY READ TYPE 3, LEAVE
	PUSHJ	P,IWORD		;GET WORD FROM BINARY FILE
	 $ECALL	BBF,%ABORT	;EOF. FILE IN ERROR
	SOJGE	P3,.-2		;SKIP TILL LSCW
	LDB	T2,[POINT 9,T1,8] ;GET SEGMENT CONTROL WORD
	CAIN	T2,2		;CONTINUE LSCW?
	  JRST	[MOVEI P3,(T1)	;YES, GET SEGMENT LENGTH
		 SOJA P3,ILSCW3] ;CONTINUE
	CAIE	T2,3		;END LSCW?
	 $ECALL	BBF,%ABORT	;No, file in error.
	POPJ	P,		;DONE

IMG3:	SKIPN	RSIZE(D)	;RECORD SIZE SPECIFIED?
	  POPJ	P,		;NO - WE HAVE NO CLEANUP
	JUMPLE	P4,%POPJ		;SKIP TO END OF RECORD
	PUSHJ	P,IWORD		;READ A WORD
	  SETZ	P4,		;EOF, OK
	SOJG	P4,.-2		;LOOP BACK
IMG4:	POPJ	P,		;RETURN


INXT:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%END	;SKIP IF AT END
	PUSHJ	P,UINXTW	;NOTHING LEFT, GO MAP NEXT WINDOW
	 POPJ	P,		;EOF
;	JRST	IWORD		;DONE

IWORD:	SOJL	P2,INXT		;IF NO WORDS, GET SOME
	ADDI	P1,1		;INCREMENT POINTER
	MOVE	T1,(P1)		;GET WORD
	PJRST	%POPJ1		;SKIP RETURN
;UNFORMATTED SKIP RECORD

UNFSKP:	AOS	NREC(U)		;UPDATE RECORD COUNT
	DMOVE	P1,IPTR(D)	;GET FILE PNTR/COUNT
	MOVE	P4,RSIZE(D)	;GET RECORD SIZE

	PUSHJ	P,ILSCW1	;READ START LSCW, P3 = SEGMENT LENGTH
	 POPJ	P,		;EOF. JUST RETURN
	SETZ	T3,		;NO ARRAY IN PROGRESS
	JRST	UIEND		;GO SKIP TO END OF RECORD
UNFO:	MOVX	T0,D%MOD	;Set "file modified"
	IORM	T0,FLAGS(D)
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%RAN	;RANDOM?
	  PUSHJ	P,RMAPW		;YES, MAP CORRECT WINDOW
	AOS	NREC(U)		;UPDATE RECORD COUNT
	DMOVE	P1,OPTR(D)	;GET FILE POINTER/COUNT
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%BIN	;BINARY?
	  PUSHJ	P,OLSCW1	;YES, OUTPUT START LSCW
	SKIPN	P3,RSIZE(D)	;RECORD SIZE SPECIFIED?
	  HRLOI	P3,377777	;NO, SET BIG RECORDS

UOLP:	PUSHJ	P,%GTIOX	;GET ADDRESS AND LENGTH OF AN ARRAY TO OUTPUT
	JUMPE	T3,UOEND	;END OF IOLST, DONE

UOBLT:	JUMPG	P2,WINDOK	;OK IF WE HAVE ROOM IN WINDOW
	PUSHJ	P,%PUSHT	;GET NEW ONE IF WE DON'T
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%BIN	;BINARY?
	 PUSHJ	P,OLSCWX	;YES. FINISH START OR CONTINUE LSCW
	PUSHJ	P,UNXTW		;OUTPUT CURRENT WINDOW, GET NEXT
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%BIN	;BINARY
	 PUSHJ	P,OLSCW2	;YES. OUTPUT TYPE 2 LSCW
	PUSHJ	P,%POPT

WINDOK:	JUMPLE	P3,UOTRNC	;IF NONE, GO TRUNCATE RECORD
	MOVEI	T2,(T3)		;GET ARRAY LENGTH
	CAILE	T2,(P2)		;UNLESS LESS THAN WINDOW LENGTH
	  MOVEI	T2,(P2)		;GET MIN
	CAMLE	T2,P3		;UNLESS LESS THAN RECORD SIZE
	  MOVE	T2,P3		;GET MIN
	JUMPE	T2,UOSKP	;AVOID 0-WORD BLT

;T1/ addr. of user's array.
;p1/ local FOROTS address of data.
;T2/ # words to copy
IF20,<
	TLNN	T1,-1		;User's array in a non-zero section?
	 JRST	UOBLT2		;No, normal BLT

;Use XBLT
	PUSH	P,T2		;Save acs
	PUSH	P,T3
				;T2/ # words to copy
	MOVE	T3,T1		;T3/ "from" -- user's array
	XMOVEI	T4,1(P1)	;T4/ "to"-- get Global FOROTS' address.
	EXTEND	T2,[XBLT]	;** COPY array **
	POP	P,T3		;Restore T3
	POP	P,T2		; and T2
	ADDI	P1,(T2)		;Point to end+1 of XBLT
	JRST	UOSKP
>;END IF20

;Use BLT
UOBLT2:	MOVSI	T4,(T1)		;GET BLT-FROM ADDRESS
	HRRI	T4,1(P1)	;AND BLT-TO ADDRESS
	ADDI	P1,(T2)		;POINT TO END+1 OF BLT
	BLT	T4,(P1)		;MOVE DATA INTO WINDOW

UOSKP:	SUBI	P3,(T2)		;DECREMENT COUNT OF WORDS LEFT IN RECORD
	SUBI	P2,(T2)		;DECREMENT COUNT OF EMPTY SPACE IN WINDOW
	SUBI	T3,(T2)		;DECREMENT COUNT OF WORDS LEFT IN ARRAY
	JUMPLE	T3,UOLP		;IF ARRAY DONE, GO DO NEXT ARRAY
	ADDI	T1,(T2)		;BUMP ARRAY ADDRESS
	JRST	UOBLT		;GO DO NEXT PIECE

UOTRNC:	PUSHJ	P,%GTIOX
	JUMPN	T3,UOTRNC

UOEND:	SKIPN	RSIZE(D)	;RECORD SIZE?
	  JRST	UOXYZ		;NO, FINE
	JUMPLE	P3,UOXYZ	;NO ZEROS NECESSARY, FINE
	SETZ	T1,		;GET A ZERO
	PUSHJ	P,OWORD		;PUT IN FILE
	SOJG	P3,.-1		;PAD WHOLE RECORD

UOXYZ:	MOVE	T0,FLAGS(D)
	TXNE	T0,D%BIN	;BINARY?
	  PUSHJ	P,OLSCW3	;YES, OUTPUT END LSCW

	DMOVEM	P1,OPTR(D)	;STORE FILE POINTER/COUNT

	MOVE	T1,BYTN(D)	;GET BYTE # OF NEXT WINDOW
	SUBI	T1,(P2)		;GET LAST BYTE IN USE
	MOVE	T0,FLAGS(D)	;Fetch DDB flags
	TXNE	T0,D%RAN	;RANDOM FILE?
	 CAMLE	T1,EOFN(D)	;YES. ONLY STORE LARGER EOFN
	  MOVEM	T1,EOFN(D)	;SAVE AS EOF PNTR

	POPJ	P,		;DONE
;LSCW ROUTINES
;FORMAT OF BINARY RECORD:  (FORMAT OF BINARY RECORD)
;THERE IS NO NECESSARY RELATIONSHIP BETWEEN SEGMENT SIZE AND BUFFER SIZE

OLSCW1:	SKIPE	RSIZE(D)	;IS RECORD SIZE SPECIFIED?
	  JRST	O1FIX		;YES - SET TYPE 1 LSCW NOW
	SETZM	SEGCNT		;CLEAR WORD COUNT OF SEGMENTS ALREADY IN FILE
	MOVSI	T1,(1B8)	;GET START LSCW
	JRST	O2FIX		;SKIP TYPE 2 PROCESSING
OLSCW2:	SKIPE	RSIZE(D)	;IS RECORD SIZE SPECIFIED?
	  POPJ	P,		;YES - NO NEED FOR TYPE 2 LSCW
	MOVSI	T1,(2B8)	;GET CONTINUE LSCW
O2FIX:	PUSHJ	P,OWORD		;PUT WORD INTO FILE WINDOW
	MOVEM	P1,CWADR	;STORE IN-CORE ADDRESS OF CONTROL WORD
	POPJ	P,

O1FIX:	MOVE	T1,RSIZE(D)	;GET RECORD SIZE
	ADD	T1,[1B8+1]	;SET START LSCW
	SETZM	CWADR		;REMEMBER WE'VE ALREADY FILLED IN START LSCW
	PJRST	OWORD		;AND PUT WORD INTO FILE WINDOW

OLSCWX:	SKIPE	RSIZE(D)	;WAS RECORD SIZE SPECIFIED?
	  POPJ	P,		;YES - START LSCW WAS ALREADY FILLED IN
	SKIPN	T2,CWADR	;GET ADDRESS OF TYPE 1 OR 2 CONTROL WORD
	 $SNH			;Already out in file, bug
	MOVEI	T1,1(P1)	;POINT TO END+1 OF WINDOW
	SUBI	T1,(T2)		;GET DISTANCE FROM CONTROL WORD = SEG LENGTH
	HRRM	T1,(T2)		;STORE LENGTH IN CONTROL WORD
	ADDM	T1,SEGCNT	;ADD INTO TOTAL RECORD LENGTH
	SETZM	CWADR		;NOW NO CONTROL WORD WAITING TO BE FINISHED
	POPJ	P,		;DONE

OLSCW3:	SKIPE	RSIZE(D)	;WAS RECORD SIZE SPECIFIED?
	  JRST	O3FIX		;YES - START LSCW ALL DONE - DO TYPE 3 ONLY
	SKIPN	T2,CWADR	;GET ADDRESS OF TYPE 1 OR 2 CONTROL WORD
	 $SNH			;Already out in file, bug
	MOVEI	T1,1(P1)	;POINT TO END+1 OF RECORD
	SUBI	T1,(T2)		;GET DISTANCE FROM CONTROL WORD = SEG LENGTH
	HRRM	T1,(T2)		;STORE LENGTH IN START CONTROL WORD
	ADD	T1,SEGCNT	;ADD IN WORDS FROM OTHER SEGMENTS
	ADDI	T1,1		;ADD IN END LSCW TOO
	HRLI	T1,(3B8)	;PUT IN TYPE-3 LSCW HEADER
	SETZM	CWADR		;NOW NO CONTROL WORD WAITING TO BE FINISHED
	PJRST	OWORD		;PUT INTO FILE

O3FIX:	MOVE	T1,RSIZE(D)	;GET USER SPECIFIED RECORD SIZE
	ADD	T1,[3B8+2]	;SET UP END LSCW
;	PJRST	OWORD		;PUT INTO FILE

OWORD:	SOJL	P2,OWFULL	;IF NO ROOM LEFT, GET SOME
	ADDI	P1,1		;INCREMENT POINTER
	MOVEM	T1,(P1)		;STORE WORD
	POPJ	P,

OWFULL:	PUSHJ	P,UNXTW		;NO ROOM, MAP NEXT WINDOW
	JRST	OWORD		;KEEP IN SYNCH

	SEGMENT	DATA
CWADR:	BLOCK	1		;ADDRESS OF START LSCW
SEGCNT:	BLOCK	1		;COUNT OF WORDS OUT IN FILE IN PREVIOUS SEGMENTS
	SEGMENT	CODE
	SUBTTL	DUMP MODE I/O

IF10,<

DMPIN:	PUSHJ	P,DMPSET	;SETUP FOR THE DUMP MODE I/O
	MOVEI	T2,.FOINP	;SET FOR INPUT
	HLL	T2,FBLK(D)	;Get channel stuff
	MOVEI	T3,DMPLST
	MOVE	T1,[2,,T2]
	FILOP.	T1,		;Do the INPUT
	 PUSHJ	P,EOFCHK	;Set D%END if EOF; else give error
	MOVE	T1,FLAGS(D)	;Get flags
	TXNE	T1,D%END	;End of file?
	 $ECALL	EOF,%ABORT	;Yes, give error
	POPJ	P,		;No, return

DMPOUT:	PUSHJ	P,DMPSET	;SETUP FOR THE DUMP MODE I/O
	MOVEI	T2,.FOOUT	;SET FOR OUTPUT
	HLL	T2,FBLK(D)	;GET CHANNEL STUFF
	MOVEI	T3,DMPLST
	MOVE	T1,[2,,T2]
	FILOP.	T1,		;DO THE INPUT
	 $ECALL	IOE,%ABORT	;Error
	POPJ	P,

DMPSET:	PUSHJ	P,%SAVE2	;SAVE P1 & P2
	XMOVEI	P1,DMPLST	;SETUP DUMP MODE LIST PNTR
DMPLP:	PUSHJ	P,%GTIOX	;GET AN I/O LIST ITEM
	JUMPE	T1,DMPDON	;DONE WITH SETUP IF NO ADDR
	SUBI	T1,1		;GET ADDR-1
	MOVNI	T2,(T3)		;GET NEG # WORDS
	HRLI	T1,(T2)		;IN LEFT HALF
	MOVEM	T1,(P1)		;SAVE IN DUMP MODE LIST
	ADDI	T3,177		;GET # BLOCKS
	IDIVI	T3,200		;ROUNDED UP
	ADDM	T3,BLKN(D)	;ADD TO BLOCK COUNT
	AOJA	P1,DMPLP
DMPDON:	SETZM	(P1)		;CLEAR LAST WORD
	POPJ	P,

>;END IF10

	SUBTTL	DISK POSITIONING

	COMMENT &

TOPS-20 sequential disk files are read with PMAPs by moving a n-page window
through the file.  The file window always starts on a n-page boundary and is
moved only when the desired byte is not in the window.  The first process page
number of the window is in WTAB(D).  (The window size can be set to something
besides 4 pages with BUFFERCOUNT=).

TOPS-20 random files are similar, but there are n independent one-page windows.
If references to the file are well localized, the windows will often contain
the desired records.  For random files WTAB contains an AOBJN pointer to a
n-word table, with each word giving the process page number and corresponding
file page number of a window, process page in the left half and file page in
the right half.  The number of windows can be set with BUFFERCOUNT=.

TOPS-10 random files are the same, but the windows are a block long instead of
a page.  WTAB uses the sign bit to remember if a block has been modified and
needs to be written back.  The BUFFERCOUNT parameter is rounded up to a
multiple of 4 blocks so that an integral number of process pages are used.

TOPS-10 sequential disk files are like any other TOPS-10 sequential file.

	&


;ROUTINE TO MAP NEXT WINDOW OF FILE
;ARGS:	 BYTN = FILE BYTE NUMBER OF START OF WINDOW
;RETURN: P1 = BYTE POINTER TO FIRST MAPPED BYTE
;	 P2 = COUNT OF BYTES IN WINDOW
;	 BYTN = FILE BYTE NUMBER+1 OF END OF WINDOW
;		 I.E., STARTING BYTE OF FOLLOWING WINDOW

FNXTW:	PUSHJ	P,%PUSHT	;SAVE T ACS
	MOVE	T1,BYTN(D)	;GET BYTE NUMBER
	IDIVI	T1,5		;CONVERT TO WORD NUMBER
	CAIE	T2,0		;BYTE # MUST BE MULT OF 5
	 $SNH			;If not, halt.
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%RAN	;RANDOM?
	 PUSHJ	P,MAPW		;MAP THAT BYTE INTO CORE
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%RAN	;CHECK AGAIN
	 PUSHJ	P,SMAPW		;IT'S SEQUENTIAL

	IMULI	P2,5		;CONVERT WORD COUNT TO BYTE COUNT
	ADDM	P2,BYTN(D)	;INCREMENT BYTE NUMBER TO AFTER THIS WINDOW
	HRLI	P1,(POINT 7,0,34) ;MAKE POINTER TO FIRST MAPPED BYTE
	PJRST	%JPOPT		;RESTORE T ACS AND RETURN

UNXTW:	PUSHJ	P,%PUSHT	;SAVE T ACS

	MOVE	T1,BYTN(D)	;DISK, GET WORD NUMBER
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%RAN	;RANDOM?
	 PUSHJ	P,MAPW		;MAP THAT WORD INTO CORE
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%RAN	;CHECK AGAIN
	 PUSHJ	P,SMAPW		;IT'S SEQUENTIAL
	ADDM	P2,BYTN(D)	;INCREMENT WORD NUMBER FOR NEXT CALL
	PJRST	%JPOPT		;RESTORE T ACS AND RETURN
;ROUTINE TO GET NEXT BUFFER OF NON-DISK FILE

IF20,<

TNXTW:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%END	;END FILE ALREADY?
	 POPJ	P,		;YES. NOTHING TO DO
	LOAD	T1,IJFN(D)	;GET JFN
	MOVE	T2,WADR(D)	;GET ADDRESS OF BUFFER
	HRLI	T2,(POINT 36,)	;POINT TO FIRST WORD
	MOVN	T3,WSIZ(D)	;GET WINDOW SIZE IN WORDS

	TXNE	T0,D%IO		;INPUT OR OUTPUT?
	  JRST	TONXTW		;OUTPUT

	AOS	BLKN(D)		;INCR BLOCK #
	SINR%			;READ
	  ERJMP	UEOFCHK		;EOF OR ERROR
UEOFRET:
	MOVE	P1,WADR(D)	;POINT TO DATA
	SUBI	P1,1		;POINT TO WORD-1
	MOVE	P2,WSIZ(D)	;GET FULL WINDOW SIZE
	ADD	P2,T3		;GET # WORDS WE ACTUALLY GOT
	MOVEM	P2,WCNT(D)	;SAVE FOR DIRECTION SWITCH
	POPJ	P,		;DONE

UEOFCHK:
	PUSH	P,T2		;SAVE POINTER TO END OF DATA
	GTSTS%			;GET FILE STATUS
	TXNN	T2,GS%EOF	;EOF?
	  JRST	UINERR		;NO, INPUT ERROR
	MOVX	T0,D%END	;EOF, tell caller
	IORM	T0,FLAGS(D)
	POP	P,T2		;RESTORE T2
	JRST	UEOFRET		;CONTINUE

UINERR:	POP	P,T2		;RESTORE T2
;	IOERR	(INY,899,401,?,$J,%ABORT) ;TYPE MESSAGE AND ABORT STATEMENT
	$ECALL	INY,%ABORT


TONXTW:	JUMPE	P1,TOSKP	;IF FIRST BUFFER, DON'T WRITE IT
	JUMPL	P2,.+2		;IF MIDDLE OF WINDOW
	 ADD	T3,P2		;WRITE PARTIAL WINDOW
	SOUTR%			;WRITE BUFFER
	  ERJMP	UOUTERR		;ERROR, TYPE MESSAGE

TOSKP:	AOS	BLKN(D)		;INCR BLOCK #
	MOVE	P1,WADR(D)	;POINT TO EMPTY BUFFER
	SUBI	P1,1		;POINT TO WORD-1
	MOVE	P2,WSIZ(D)	;GET FULL WINDOW SIZE
	POPJ	P,		;DONE

UOUTERR:
;	IOERR	(OUY,899,401,?,$J,%ABORT) ;ERROR, TYPE MONITOR MESSAGE
	$ECALL	OUY,%ABORT

> ;IF20
;ROUTINE TO MAP WINDOW CONTAINING FIRST BYTE OF RANDOM RECORD
;THINGS ARE LEFT SET UP FOR NXTW IN CASE RECORD SPANS WINDOWS
;ARGS:	 A.REC = RECORD NUMBER TO SET TO
;RETURN: IPTR/OPTR = POINTER TO FIRST BYTE OF RECORD
;	 ICNT/OCNT = BYTES IN WINDOW
;	 BYTN = NUMBER OF FIRST BYTE IN FOLLOWING WINDOW

RMAPW:	PUSHJ	P,%SAVE2	;SAVE P ACS
	SKIPG	T3,@A.REC	;GET RECORD NUMBER
;	  IOERR	(IRN,25,512,?,Illegal record number $D,<T3>,%ABORT)
	 $ECALL	IRN,%ABORT
	MOVEM	T3,NREC(U)	;STORE IN UNIT BLOCK

IRMAP:	MOVE	T1,RSIZE(D)	;GET RECORD SIZE, BYTES
	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNE	T0,D%UNF	;UNFORMATTED?
	  JRST	URMAP		;YES, GO DO THAT

FRMAP:	ADDI	T1,2+4		;ADD 2 FOR CRLF, 4 TO ROUND UP TO WORD
	IDIVI	T1,5		;GET RECORD SIZE IN WORDS
	IMULI	T1,-1(T3)	;GET WORD NUMBER OF START OF RECORD

	MOVEI	T2,5		;GET BYTES PER WORD
	IMUL	T2,T1		;GET BYTE NUMBER OF RECORD START
	MOVEM	T2,BYTN(D)	;STORE IT

	PUSHJ	P,MAPW		;MAP RECORD INTO CORE

	HRLI	P1,(POINT 7,0,34) ;POINT TO FIRST BYTE OF RECORD
	IMULI	P2,5		;CONVERT WINDOW LENGTH TO BYTES
	JRST	RMAPX		;GO FINISH UP


URMAP:	MOVE	T0,FLAGS(D)
	TXNE	T0,D%BIN	;BINARY FILE?
	  ADDI	T1,2		;YES, ADD 2 LSCWS
	IMULI	T1,-1(T3)	;GET WORD NUMBER OF RECORD START
	MOVEM	T1,BYTN(D)	;STORE CURRENT WORD NUMBER

	PUSHJ	P,MAPW		;MAP RECORD START INTO CORE

RMAPX:	ADDM	P2,BYTN(D)	;INCREMENT WORD NUMBER FOR NXTW
	DMOVEM	P1,IPTR(D) 	;STORE BYTE POINTER AND COUNT
	DMOVEM	P1,OPTR(D)	;IN BOTH PLACES
	POPJ	P,		;DONE

;ROUTINE TO MAP A FILE WINDOW
;ARGS:	 P1 = FILE ADDRESS
;RETURN: P1 = PROCESS ADDRESS
;	 P2 = NUMBER OF WORDS MAPPED IN WINDOW

IF10,<LWSIZ==7>			;ON 10, WINDOW SIZE IS 2**7
IF20,<LWSIZ==9>			;ON 20, WINDOW SIZE IS 2**9
PSIZ==1_LWSIZ

%MAPW:
MAPW:	MOVE	P1,T1		;GET WORD ADDRESS IN FILE
	LSHC	P1,-LWSIZ	;GET PAGE NUMBER, SAVE OFFSET WITHIN PAGE
	CAMLE	P1,TPAGE(D)	;GREATER THAN ANY PAGE REFERENCED BEFORE?
	 MOVEM	P1,TPAGE(D)	;YES. SAVE IT
	MOVE	T1,WTAB(D)	;GET AOBJN POINTER TO WINDOW TABLE

FINDW:	HRRZ	T2,(T1)		;GET PAGE NUMBER OF A MAPPED PAGE
	CAIE	T2,(P1)		;IS IT THE ONE WE WANT?
	  AOBJN	T1,FINDW	;NO, LOOK ON
	JUMPL	T1,PTRBMP	;IN CORE
	PUSHJ	P,RDW		;NOT IN CORE

PTRBMP:	HLRZ	P1,(T1)		;GET IN-CORE PAGE NUMBER OF FILE PAGE
	LSHC	P1,LWSIZ	;COMBINE WITH WITHIN-PAGE OFFSET

	PUSHJ	P,PAGCHK	;MACHINE-DEPENDENT PAGE (BLOCK) CODE

	MOVE	T2,WPTR(D)	;GET REFILL POINTER TO WINDOW TABLE
	CAIE	T2,(T1)		;IS IT POINTING TO PAGE WE JUST USED?
	  JRST	MAPRET		;NO, LEAVE IT WHERE IT IS
	SUBI	T2,1		;YES, POINT IT SOMEPLACE ELSE
	MOVE	T3,WTAB(D)	;GET POINTER TO START OF TABLE
	CAIL	T2,(T3)		;DID WE PASS BEGINNING OF TABLE?
	  JRST	PTRRET		;NO, FINE
	HLRE	T3,T3		;YES, GET -TABLE LENGTH
	SUB	T2,T3		;RESET POINTER TO TOP OF TABLE
PTRRET:	MOVEM	T2,WPTR(D)	;RESET REFILL POINTER

MAPRET:	SETCM	P2,P1		;GET 777777777777 - OFFSET INTO WINDOW
	ANDI	P2,PSIZ-1	;GET WSIZ - 1 - OFFSET INTO WINDOW
	ADDI	P2,1		;GET WSIZ - OFFSET = WORDS MAPPED IN WINDOW
	SOJA	P1,%POPJ	;RETURN CORRECT ADDR-1

IF10,<
PAGCHK:	MOVSI	T2,(1B0)	;GET PAGE-WRITTEN BIT
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%IO		;ARE WE WRITING?
	  IORM	T2,(T1)		;YES, FLAG PAGE AS MODIFIED
	POPJ	P,
>;END IF10


IF20,<
PAGCHK:	LOAD	T3,ACC(D)	;GET ACCESS
	CAIE	T3,AC.RIN	;RANDIN?
	 POPJ	P,		;NO. RANDOM. LEAVE PAGE ALONE
	SKIP	(P1)		;REFERENCE A WORD IN THE PAGE
	 ERJMP	UNMAPR		;UNMAP THE PAGE IF NON-EXISTENT
	POPJ	P,

UNMAPR:	PUSHJ	P,%PUSHT	;SAVE T ACS
	MOVEI	T2,(P1)		;GET CORE ADDR
	LSH	T2,-LWSIZ	;MAKE IT A PAGE AGAIN
	HRLI	T2,.FHSLF	;THIS FORK
	SETO	T1,		;SETUP UNMAP FUNCTION
	SETZ	T3,		;WITH NO REPEAT COUNT
	PMAP%			;UNMAP IT, SO IT WILL BE 0
	PUSHJ	P,%POPT		;RESTORE T ACS
	POPJ	P,
>;END IF20
IF20,<
RDW:	MOVE	T1,WPTR(D)	;GET POINTER TO PAGE TO BOOT FROM CORE
	HRRM	P1,(T1)		;STORE NEW FILE PAGE NUMBER

	HLRZ	T2,(T1)		;GET PROCESS PAGE NUMBER
	HRLI	T2,.FHSLF	;FORK HANDLE
	LOAD	T1,IJFN(D)	;JFN
	MOVSI	T1,(T1)
	HRRI	T1,(P1)		;FILE PAGE NUMBER
	MOVSI	T3,(PM%PLD+PM%RD+PM%WR)	;ACCESS BITS
	PMAP%			;MAP PAGE IN

	MOVE	T1,WPTR(D)	;POINT TO WINDOW TABLE ENTRY
	POPJ	P,		;GO MOVE WINDOW POINTER TO SOMEPLACE ELSE
>;END IF20
IF10,<
RDW:	PUSHJ	P,WRTPG		;WRITE PAGE BACK IF MODIFIED

	MOVE	T1,LKPB+.RBSIZ(D) ;GET REAL FILE SIZE
	ADDI	T1,PSIZ-1	;ROUND UP TO GET # BLOCKS
	LSH	T1,-LWSIZ
	CAIGE	T1,1(P1)	;BLOCK EXIST?
	 JRST	CLRPAG		;NO. CREATE ZEROS, ACT AS IF IT DID
	HLLZ	T2,CHAN(D)	;SET CHANNEL NUMBER
	HRRI	T2,.FOUSI	;SET USETI FUNCTION
	MOVEI	T3,1(P1)	;GET PAGE NUMBER TO READ IN
	MOVE	T1,[2,,T2]	;SET TO DESIRED BLOCK
	FILOP.	T1,
	 $ECALL	IOE,%ABORT

	HLLZ	T2,CHAN(D)	;SET CHANNEL NUMBER
	HRRI	T2,.FOINP	;SET INPUT FUNCTION
	MOVEI	T3,T4		;SET ADDRESS OF COMMAND LIST
	MOVE	T1,WPTR(D)	;POINT TO WTAB ENTRY
	HLRZ	T4,(T1)		;GET CORE "PAGE NUMBER" TO READ INTO
	LSH	T4,LWSIZ	;CONVERT TO ADDRESS
	SUBI	T4,1		;-1 FOR IOWD
	HRLI	T4,-PSIZ	;SET LENGTH
	SETZ	T5,		;ZERO TO END COMMAND LIST
	MOVE	T1,[2,,T2]	;SET ARG BLOCK POINTER
	FILOP.	T1,		;DO FILOP
	 $ECALL	IOE,%ABORT

	MOVE	T1,WPTR(D)	;RELOAD WTAB POINTER
	HRRM	P1,(T1)		;STORE NEW FILE PAGE NUMBER
	POPJ	P,		;GO BUMP POINTER TO SOMEPLACE ELSE


WRTPG:	MOVE	T1,WPTR(D)	;GET POINTER TO PAGE TO BOOT FROM CORE
	SKIPL	T3,(T1)		;GET FILE PAGE NUMBER AND SKIP IF MODIFIED
	  POPJ	P,		;NOT MODIFIED, NO NEED TO WRITE

	HLLZ	T2,CHAN(D)	;GET CHANNEL NUMBER
	HRRI	T2,.FOUSO	;SET USETO FUNCTION
	MOVEI	T3,1(T3)	;GET JUST BLOCK NUMBER
	MOVE	T1,[2,,T2]	;SET ARG BLOCK POINTER
	FILOP.	T1,		;SET TO DESIRED BLOCK
	 $ECALL	IOE,%ABORT

	IMULI	T3,PSIZ		;GET # WORDS TO END OF THIS PAGE
	CAMLE	T3,LKPB+.RBSIZ(D) ;LARGER THAN WHAT'S THERE?
	 MOVEM	T3,LKPB+.RBSIZ(D) ;YES. UPDATE IT
	HLLZ	T2,CHAN(D)	;GET CHANNEL NUMBER AGAIN
	HRRI	T2,.FOOUT	;SET OUTPUT FUNCTION
	MOVEI	T3,T4		;SET ADDRESS OF COMMAND LIST
	MOVE	T1,WPTR(D)	;POINT TO WTAB ENTRY AGAIN
	HLRZ	T4,(T1)		;GET "PAGE NUMBER" OF WINDOW
	LSH	T4,LWSIZ	;MAKE INTO ADDRESS
	SUBI	T4,1		;-1 FOR IOWD
	HRLI	T4,-PSIZ	;PUT COUNT IN LH
	SETZ	T5,		;ZERO TO END COMMAND LIST
	MOVE	T1,[2,,T2]	;SET ARG BLOCK POINTER
	FILOP.	T1,		;DO FILOP OR OUT UUO
	 $ECALL	IOE,%ABORT
	POPJ	P,		;DONE
REPEAT 0,<
EOFOK:	MOVEI	T2,(T1)		;COPY BITS
	ANDI	T2,760000	;CLEAR ALL BUT ERR BITS
	CAIE	T2,IO.EOF	;JUST EOF?
	 $ECALL	IOE,%ABORT	;NO, TYPE MESSAGE
	MOVEI	T3,(T1)		;COPY BITS AGAIN
	TRZ	T3,760000	;CLEAR ERR BITS
	HLLZ	T2,CHAN(D)	;SETSTS
	HRRI	T2,.FOSET
	MOVE	T1,[2,,T2]
	FILOP.	T1,
	 $SNH
>;END REPEAT 0

CLRPAG:	MOVE	T1,WPTR(D)	;CLEAR BLOCK
	HLRZ	T2,(T1)
	LSH	T2,LWSIZ
	SETZM	(T2)
	MOVSI	T3,(T2)
	HRRI	T3,1(T2)
	BLT	T3,PSIZ-1(T2)

	HRRM	P1,(T1)		;STORE NEW FILE PAGE NUMBER

	POPJ	P,		;RETURN AS IF IT HAD RETURNED ZEROS


;HERE AT CLOSE TO WRITE MODIFIED PAGES

%RANWR:	PUSHJ	P,%SAVE1	;SAVE P ACS
	MOVE	P1,WTAB(D)	;GET AOBJN POINTER TO TABLE
	
RWLP:	MOVEM	P1,WPTR(D)	;POINT TO A PAGE
	PUSHJ	P,WRTPG		;WRITE IT IF MODIFIED
	AOBJN	P1,RWLP		;DO ALL PAGES

	POPJ	P,		;DONE

> ;IF10
;SEQUENTIAL CASE, ONE N-PAGE WINDOW
;ARGS:	 T1 = WORD NUMBER IN FILE
;	 BUFCT = LENGTH OF WINDOW, PAGES

IF20,<

SMAPW:	LOAD	T2,INDX(D)	;GET DEVICE INDEX
	CAIE	T2,DI.DSK	;DISK?
	 JRST	TNXTW		;NO.
	MOVE	P1,T1		;GET WORD # IN FILE
	LSHC	P1,-LWSIZ	;CALC PAGE #
	LOAD	T2,BUFCT(D)	;GET # PAGES WE ARE READING/WRITING
	ADDI	T2,(P1)		;GET HIGHEST PAGE #
	CAMLE	T2,TPAGE(D)	;HIGHER PAGE THAN RECORDED?
	 MOVEM	T2,TPAGE(D)	;YES. RECORD IT
	AOS	BLKN(D)		;INCREMENT "BLOCK" NUMBER
	MOVE	T1,WTAB(D)	;GET PAGE ADDR OF BUFFER
	MOVSI	T2,.FHSLF	;THIS FORK
	HRRI	T2,(T1)		;PAGE NUMBER IN FORK
	EXCH	T1,P1		;PAGE NUMBER IN FILE
	LOAD	T3,IJFN(D)	;JFN
	HRLI	T1,(T3)
	LOAD	T3,BUFCT(D)	;PAGE COUNT
	HRLI	T3,(PM%CNT+PM%PLD+PM%RD+PM%WR) ;ACCESS BITS, READ PAGES NOW
	PMAP%			;MAP WINDOW INTO FILE

	MOVE	T1,FLAGS(D)	;CHECK IF INPUT OPERATION
	TXNN	T1,D%IN		;IS IT?
	 JRST	NOPCH2		;NO. DON'T CHECK PAGE EXISTENCE
	LOAD	T4,BUFCT(D)	;GET BUFFER COUNT
	MOVNI	T4,(T4)		;NEGATIVE
	MOVSI	T4,(T4)		;IN LEFT HALF
	HRR	T4,WTAB(D)	;GET PAGE # OF BOTTOM PAGE
CHPLP:	MOVEI	T1,(T4)		;GET CORE ADDR
	LSH	T1,LWSIZ
	SKIP	(T1)
	ERJMP	UNMPG		;IF NOT THERE, GO UNMAP
	AOBJN	T4,CHPLP	;BACK FOR MORE
	JRST	NOPCH2		;DONE

UNMPG:	SETO	T1,		;SET TO UNMAP IT
	MOVSI	T2,.FHSLF	;THIS FORK
	HRRI	T2,(T4)		;GET THE CORRECT PAGE TO TOSS
	SETZ	T3,		;NO REPEAT COUNT
	PMAP%			;UNMAP THE PAGE
	AOBJN	T4,CHPLP	;BACK FOR MORE

NOPCH2:	LSHC	P1,LWSIZ	;COMBINE PAGE WITH WITHIN-PAGE OFFSET
	MOVE	T1,P1		;GET WITHIN-PAGE OFFSET
	ANDI	T1,PSIZ-1
	LOAD	P2,BUFCT(D)	;GET WINDOW SIZE IN PAGES
	LSH	P2,9		;CONVERT TO WORDS
	SUBI	P2,(T1)		;GET WORDS LEFT IN WINDOW
	SOJA	P1,%POPJ	;POINT TO CORRECT ADDR-1

> ;END IF20

IF10,<
SMAPW:	MOVE	T0,FLAGS(D)	;Get DDB flags
	TXNN	T0,D%IO		;INPUT OPERATION?
	 JRST	SMWIN		;YES. GO DO IT
	AOS	T1,BLKN(D)	;INCR BLOCK #
	IMULI	T1,200		;GET BYTE # OF NEXT BLOCK
	MOVEM	T1,BYTN(D)	;STORE FOR EOFN CALC
	DMOVEM	P1,OPTR(D)	;OUTPUT. SETUP OUTPUT PNTR/COUNT
	MOVE	T2,CHAN(D)	;WRITE CURRENT BLOCK
	HRRI	T2,.FOOUT
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	  PUSHJ	P,EOFCHK
	DMOVE	P1,OPTR(D)	;GET POINTER TO NEXT BUFFER
	POPJ	P,		;DONE

SMWIN:	AOS	T1,BLKN(D)	;INCR BLOCK #
	IMULI	T1,200		;GET BYTE # OF NEXT BLOCK
	MOVEM	T1,BYTN(D)	;SAVE FOR EOFN CALC
	DMOVEM	P1,IPTR(D)	;SETUP INPUT PNTR/COUNT
	MOVE	T2,CHAN(D)	;GET CHANNEL STUFF
	HRRI	T2,.FOINP	;READ NEXT BLOCK
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	 PUSHJ	P,EOFCHK
	DMOVE	P1,IPTR(D)	;GET PNTR/COUNT OF BUFFER JUST READ
	POPJ	P,

> ;IF10
	SUBTTL	TAPE POSITIONING

;Come here from MTOP% after the unit number has been checked.
;IO args have been set up in A.xxx
;A POPJ will return from the MTOP% call.

MTOP:	SKIPL	T1,A.MTOP	;GET OPERATION CODE
	CAILE	T1,MOPMAX	;NEGATIVE OR TOO BIG?
	  POPJ	P,		;YES, NOP
	XMOVEI	T2,.		;Current section number in LH
	HRR	T2,MOPNAM(T1)	;Get global address of ASCIZ name
	MOVEM	T2,%IONAM	;SET STATEMENT NAME FOR ERROR MESSAGES
	MOVE	T1,A.UNIT	;GET UNIT NUMBER
	SETZ	P1,		;Clear D%UNF and D%RAN for default OPEN
	PUSHJ	P,%SETD		;SET UP D

	DMOVE	T2,[EXP [ASCIZ /direct/],[ASCIZ /sequential/]]
	MOVE	T1,FLAGS(D)	;GET FLAGS
	TXNE	T1,D%RAN	;RANDOM FILE?
	 $ECALL	CDI		;CAN'T DO THIS UNTIL VERSION 7

	MOVE	T1,A.MTOP	;Get back MTOP number
	PUSHJ	P,@MOPDSP(T1)	;GO DO OPERATION
	PJRST	%SETAV		;RETURN (possibly doing ERR=, etc.)

MOPNAM:	[ASCIZ /REWIND/]	;(0)
	[ASCIZ /UNLOAD/]	;(1)
	[ASCIZ /BACKSPACE/]	;(2)
	[ASCIZ /BACK FILE/]	;(3)
	[ASCIZ /ENDFILE/]	;(4)
	[ASCIZ /SKIP RECORD/]	;(5)
	[0]			;(6)
	[ASCIZ /SKIP FILE/]	;(7)
MOPMAX==.-MOPNAM

MOPDSP:	IFIW	MOPREW
	IFIW	MOPUNL
	IFIW	MOPBSR
	IFIW	MOPBSF
	IFIW	MOPEND
	IFIW	MOPSKR
	IFIW	%POPJ
	IFIW	MOPSKF
IF20,<

;REWIND

MOPREW:
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END	;EOF?
	 PUSHJ	P,BAKEOF	;YES. CLEAR IT
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;AND BLOCK #
	MOVX	T0,D%END	;File is now not at end
	ANDCAM	T0,FLAGS(D)
	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIN	T1,DI.DSK	;DISK?
	  JRST	DSKREW		;CAN DO
	CAIN	T1,DI.MTA	;TAPE?
	  JRST	MTAREW		;CAN DO
	POPJ	P,		;ELSE NOP

DSKREW:	MOVE	T0,FLAGS(D)	;Get DDB flags for this file
	TXNE	T0,D%OUT	;WAS IT OPEN FOR OUTPUT?
	 PUSHJ	P,%SETIN	;Yes. Switch to input
	SETZM	IPTR(D)		;PRETEND NO I/O DONE
	SETZM	ICNT(D)		;NO BYTES IN BUFFER
	SETZM	BYTN(D)		;SET CURRENT BYTE NUMBER TO 0
	POPJ	P,		;DONE

MTAREW:	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNN	T1,D%IN+D%OUT	;If not open,
	 JRST	JSTREW		;Don't call %SETIN
	PUSHJ	P,%SETIN	;Get file opened for INPUT
	SETZM	IPTR(D)		;PRETEND NO I/O DONE
	SETZM	ICNT(D)		;NO BYTES IN BUFFER
	MOVEI	T2,.MOREW	;SET TO REWIND TAPE
	JRST	DOMTOP		;GO DO MTOPR

JSTREW:	PUSHJ	P,MTAOJF	;Open JFN, aborts if fails
	MOVEI	T2,.MOREW	;Get function
	PUSHJ	P,DOMTP1	;Do it
	PJRST	MTACJF		;Close file, release JFN, return.

;Routine to create a JFN to be used for magtape operations
;Returns .+1 if ok, JFN in "RWJFN"
;The JFN is opened for input.
;If fails, goes to %ABORT.

MTAOJF:	MOVE	T1,[POINT 7,TMDEV] ;Get device name with ":"
	MOVEI	T2,DEV(D)	;From the DDB
	HRLI	T2,(POINT 7,)
MTAOJ1:	ILDB	T3,T2		;Get a byte
	JUMPE	T3,MTAOJ2	;Null, done
	IDPB	T3,T1		;Store
	JRST	MTAOJ1		;Loop until null found
MTAOJ2:	MOVEI	T3,":"		;Append a colon
	IDPB	T3,T1		;Now have DEV: in "TMDEV"

;Do our own GTJFN.

	MOVX	T1,GJ%SHT
	HRROI	T2,TMDEV
	GTJFN%
	 ERJMP	E..SNH		;?Dev must exist: OPENX was done!
	HRRZM	T1,RWJFN	;Save JFN

;Have to OPENF the file to do a TAPOP.

	MOVX	T2,OF%RD	;Read ACCESS, nothing else.
	OPENF%			;Get READ access to file
	 ERJMP	MTARWO		;?OPENF failed, give error
	POPJ	P,		;OK, return

;Here if OPENF failed

MTARWO:	MOVE	T1,RWJFN	;Release JFN
	RLJFN%
	 ERJMP	.+1		;?Too bad
	$ECALL	OPE,%ABORT	;Give JSYS error and abort program

SEGMENT DATA
TMDEV:	BLOCK	20		;Device name with ":"
RWJFN:	BLOCK	1		;Temp JFN used for REWIND, UNLOAD
SEGMENT CODE

;Routine to close and release JFN gotten by MTAOJF

MTACJF:	MOVE	T1,RWJFN	;Get saved JFN
	CLOSF%
	 $ECALL	CLF,%ABORT	;?CLOSF failed, abort program
	POPJ	P,		;All worked, return


;BACKSPACE

MOPBSR:	PUSHJ	P,%SETIN	;Switch to input if necessary
	MOVE	T1,NREC(U)	;GET RECORD #
	SOJLE	T1,%POPJ	;CAN'T GO BEFORE BEG FILE
	MOVEM	T1,NREC(U)	;SAVE NEW RECORD NUMBER

	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END	;FILE AT END?
	 JRST	BAKEOF		;YES. JUST BACK OVER IT

	TXNE	T0,D%UNF	;FORMATTED?
	  JRST	UNFBSR		;NO, UNFORMATTED

	DMOVE	P1,IPTR(D)	;GET POINTER
	JUMPE	P1,%POPJ	;IF NO I/O YET, WE'RE DONE

	MOVEI	T1,(P1)		;GET JUST ADDR
	CAML	T1,WADR(D)	;BEG OF WINDOW?
	 JRST	BSROK		;NO
	PUSHJ	P,SUBP1X	;YES. GET PREVIOUS WINDOW
	 POPJ	P,		;NONE. JUST RETURN

BSROK:	PUSHJ	P,FBTST		;FIND CURRENT EOL
	 JRST	NORSZ		;GOT TO BEG FILE
	PUSHJ	P,FBSLP		;AND PREVIOUS ONE
	 JRST	NORSZ		;GOT TO BEG FILE
	SKIPN	RSIZE(D)	;FIXED-LENGTH RECORDS?
	 JRST	NORSZ		;NO
	HRLI	P1,(POINT 7,0,34) ;YES. POINT TO END OF WORD
	IDIVI	P2,5		;AND CORRECT COUNT
	IMULI	P2,5
NORSZ:	DMOVEM	P1,IPTR(D)	;STORE POINTER/COUNT
	POPJ	P,

FBTST:	LDB	T1,P1		;GET BYTE
	CAIL	T1,12		;LF, VT, FF?
	CAILE	T1,14
	  JRST	FBSLP		;NO
	JRST	%POPJ1		;DONE. SKIP RETURN
FBSLP:	ADDI	P2,1		;ADJUST COUNT
	ADD	P1,[47B5]	;DECREMENT P1
	TLCE	P1,(1B0)
	 JRST	FBTST		;PNTR IS OK

SUBP1:	SUB	P1,[430000,,1]	;DECREMENT TO PREV WORD
	MOVEI	T1,(P1)		;GET JUST ADDR
	CAML	T1,WADR(D)	;AT BEG OF WINDOW?
	 JRST	FBTST		;NO. BACK TO TEST
	PUSHJ	P,SUBP1X	;YES. GET PREVIOUS WINDOW
	 POPJ	P,		;NONE THERE
	JRST	FBTST		;GOT IT. BACK TO GET CHARS

SUBP1X:	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIN	T1,DI.MTA	;TAPE?
	  JRST	MTABSA		;YES

	MOVE	T1,BYTN(D)	;GET BYTE # OF NEXT WINDOW
	SUB	T1,WSIZ(D)	;GET BYTE # OF THIS WINDOW
	JUMPLE	T1,%POPJ	;BEG OF FILE. NON-SKIP RETURN
	SUB	T1,WSIZ(D)	;GET BYTE # OF PREVIOUS WINDOW
	MOVEM	T1,BYTN(D)	;SAVE IT

	PUSHJ	P,FNXTW		;GO MAP THE WINDOW

	MOVE	T1,WSIZ(D)	;GET WINDOW SIZE IN BYTES
	LOAD	T2,BPW(D)	;GET BYTES/WORD
	IDIVI	T1,(T2)		;GET WINDOW SIZE IN WORDS
	ADD	P1,T1		;POINT TO END OF WINDOW
	HRLI	P1,(POINT 7,,34)
	SETZ	P2,		;AND CLEAR COUNT
	JRST	%POPJ1		;SKIP RETURN

MTABSA:	MOVE	T1,BLKN(D)	;GET CURRENT BLOCK #
	SOJLE	T1,%POPJ	;LEAVE IF NONE THERE AFTER DECR
	MOVEM	T1,BLKN(D)	;SAVE DECREMENTED AMOUNT
	MOVEI	T2,.MOBKR	;BACKSPACE RECORD
	PUSHJ	P,DOMTOP	;BACK UP TO BEGINNING OF THIS RECORD
	PUSHJ	P,BACKA		;BACKSPACE, READ A BLOCK
	JRST	%POPJ1		;AND SKIP RETURN

BACKA:	MOVEI	T2,.MOBKR
	PUSHJ	P,DOMTOP	;BACK UP TO BEGINNING OF PREV RECORD

	LOAD	T1,IJFN(D)	;READ THE RECORD
	MOVE	T2,WADR(D)	;POINT TO BUFFER
	SUBI	T2,1		;POINT TO LAST BYTE IN PREV WORD
	HRLI	T2,(POINT 7,0,34)
	MOVN	T3,WSIZ(D)	;GET LENGTH OF WINDOW
	SINR%			;READ STRING
	  ERCAL	EOFCHK		;ERROR, GO TYPE MESSAGE

	MOVE	T1,WSIZ(D)	;GET WINDOW SIZE
	ADD	T1,T3		;GET # BYTES ACTUALLY READ
	MOVEM	T1,WCNT(D)	;SAVE FOR DIRECTION SWITCH

	MOVE	P1,T2		;POINT AT LAST BYTE READ
	SETZ	P2,		;CLEAR COUNT
	DMOVEM	P1,IPTR(D)	;SAVE PNTR/COUNT
	POPJ	P,

UNFBSR:	LOAD	T1,INDX(D)	;CHECK DEVICE
	CAIN	T1,DI.MTA	;TAPE?
	  JRST	MTABSU		;YES

	DMOVE	P1,IPTR(D)	;GET POINTER
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%BIN	;BINARY?
	  JRST	IBSLP		;NO, IMAGE

UBSLP:	MOVEI	T1,(P1)		;GET ADDR ONLY
	CAML	T1,WADR(D)	;AT BEG OF WINDOW?
	 JRST	BINOK		;NO
	MOVE	T1,BYTN(D)	;YES. GET WORD # OF NEXT WINDOW
	SUB	T1,WSIZ(D)	;GET WORD # OF THIS WINDOW
	JUMPLE	T1,%POPJ	;IF BEG OF THIS WINDOW IS 0, LEAVE
	SUB	T1,WSIZ(D)	;GET WORD # OF PREVIOUS WINDOW
	MOVEM	T1,BYTN(D)	;SAVE IT
	PUSHJ	P,UNXTW		;GET PREVIOUS WINDOW
	ADD	P1,WSIZ(D)	;POINT TO END OF WINDOW
	SETZ	P2,		;AND CLEAR COUNT
BINOK:	HRRZ	T1,(P1)		;GET LENGTH FROM END LSCW
	SUBI	P1,(T1)		;GET ADDR OF BEG OF RECORD
	ADDI	P2,(T1)		;AND INCR WORD COUNT LEFT
	MOVEI	T1,(P1)		;GET ADDR ONLY
	SUB	T1,WADR(D)	;GET WINDOW OFFSET
	CAML	T1,[-1]		;BEFORE BEG OF WINDOW?
	 JRST	UBSRET		;NO. WE'RE DONE
	ADDI	P1,1		;POINT TO WORD WE WANT TO MAP
	PUSHJ	P,%PTOF		;GET THE FILE POSITION
	JUMPGE	P1,BINOK2	;OK IF ZERO
	 SETZ	P1,		;ELSE MAP PAGE 0
BINOK2:	PUSHJ	P,%FTOP		;MAP THE BEG OF RECORD
	MOVE	T1,BYTN(D)	;GET BYTE # OF NEXT WINDOW
	CAMG	T1,EOFN(D)	;PAST EOF?
	 JRST	UBSRET		;NO
	SUB	T1,EOFN(D)	;YES. GET DIFF
	SUBI	P2,(T1)		;SUBTRACT FROM AVAILABLE CHARS
	MOVE	T2,WSIZ(D)	;GET WINDOW SIZE
	SUBI	T2,(T1)		;GET # ACTIVE CHARS
	MOVEM	T2,WCNT(D)	;SAVE IT
UBSRET:	DMOVEM	P1,IPTR(D)	;STORE POINTER
	POPJ	P,		;DONE

IBSLP:	SKIPN	T1,RSIZE(D)	;RECORDSIZE SPECIFIED?
;	  IOERR	(CBI,25,536,?,Can't backspace image file with no RECORDSIZE,,%ABORT)
	 $ECALL	CBI,%ABORT
	SUBI	P1,(T1)		;DECR PNTR
	ADDI	P2,(T1)		;INCR WORD COUNT
	MOVEI	T1,(P1)		;GET ADDR ONLY
	SUB	T1,WADR(D)	;GET WINDOW OFFSET
	CAML	T1,[-1]		;BEFORE BEG WINDOW?
	 JRST	UBSRET		;NO. WE'RE DONE
	ADDI	P1,1		;POINT TO WORD WE REALLY WANT
	PUSHJ	P,%PTOF		;CONVERT P1 FROM CORE ADDRESS TO FILE ADDRESS
	JUMPG	P1,IMGOK2	;OK IF .GT. 0
	 SETZ	P1,		;ELSE MAP PAGE 0
IMGOK2:	JRST	BINOK2		;GO MAP PAGE, SETUP PNTR/COUNT


MTABSU:	DMOVE	P1,IPTR(D)	;GET BUFFER POINTER/COUNT
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%BIN	;BINARY?
	  JRST	MTABSB		;YES
	SKIPN	T1,RSIZE(D)	;NO, IMAGE; MUST HAVE RECORD SIZE
	 $ECALL	CBI,%ABORT
	JRST	MTABSI		;GO BACK OVER THAT MANY WORDS
MTABSB:	MOVEI	T1,(P1)		;GET PNTR
	CAML	T1,WADR(D)	;AT BEG OF BUFFER?
	 JRST	BSUOK		;NO. CAN JUST GRAB PREV RECSIZ
	MOVE	T1,BLKN(D)	;GET BLOCK
	SOJLE	T1,%POPJ	;BEG OF FILE. BACKSPACE IS NOP
	MOVEM	T1,BLKN(D)	;STORE AS NEW BLOCK #
	MOVEI	T2,.MOBKR	;BACKSPACE TO BEG OF CURRENT BLOCK
	PUSHJ	P,DOMTOP
	PUSHJ	P,BACKU		;BACKSPACE 1 RECORD, READ 1
BSUOK:	HRRZ	T1,(P1)		;GET RECSIZ FROM TYPE 3 LSCW

MTABSI:	SUBI	P1,(T1)		;DECR PNTR
	ADDI	P2,(T1)		;INCR COUNT
	MOVEI	P3,(P1)		;GET NEW PNTR
	SUB	P3,WADR(D)	;GET WINDOW OFFSET
	CAML	P3,[-1]		;BEFORE BEG OF WINDOW?
	 JRST	GOTBF		;NO. WE'RE OK
	MOVE	T1,BLKN(D)	;GET BLOCK #
	SOJLE	T1,BEGBF	;IF BLOCK 1, JUST SET TO BEG BUFFER
	MOVEM	T1,BLKN(D)	;NOT. STORE NEW BLOCK #
	MOVEI	T2,.MOBKR	;BACKSPACE TO BEG OF CURRENT BLOCK
	PUSHJ	P,DOMTOP
	PUSHJ	P,BACKU		;BACKSPACE 1 REC, READ 1
	MOVM	T1,P3		;GET NEW DECREMENT
	SOJA	T1,MTABSI	;BACKU POINTS 1 BACK, SO DECR THE DECR
				;LOOP UNTIL DONE OR BEG FILE

BEGBF:	MOVE	P1,WADR(D)	;SET PNTR/COUNT TO BEG BUFFER
	SUBI	P1,1
	MOVE	P2,WCNT(D)
GOTBF:	DMOVEM	P1,IPTR(D)	;STORE PNTR/COUNT
	POPJ	P,		;DONE

BACKU:	MOVEI	T2,.MOBKR	;BACKSPACE RECORD
	PUSHJ	P,DOMTOP

	LOAD	T1,IJFN(D)	;READ THE RECORD
	MOVE	T2,WADR(D)	;POINT TO BUFFER
	SUBI	T2,1		;POINT TO LAST BYTE OF WORD-1
	HRLI	T2,(POINT 36,0,35)
	MOVN	T3,WSIZ(D)
	SINR%
	  ERCAL	EOFCHK		;ERROR, GO TYPE MESSAGE
	MOVEI	P1,(T2)		;GET PNTR TO END OF ACTIVE BYTES
	SETZ	P2,
	DMOVEM	P1,IPTR(D)	;SAVE PNTR/COUNT
	MOVE	T1,WSIZ(D)	;GET WINDOW SIZE
	ADD	T1,T3		;GET # ACTIVE BYTES
	MOVEM	T1,WCNT(D)	;SAVE IT
	POPJ	P,

BAKEOF:	MOVX	T0,D%END	;Clear EOF bit
	ANDCAM	T0,FLAGS(D)
	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIE	T1,DI.MTA	;MAGTAPE?
	 POPJ	P,		;NO

	PUSHJ	P,%CLSOP	;CLOSE, OPEN FILE
	SOS	BLKN(D)		;EOF MARK IS A BLOCK
	MOVEI	T2,.MOBKR	;BACK OVER THE EOF MARK
	PUSHJ	P,DOMTOP
	SKIPN	BLKN(D)		;ANY BLOCKS LEFT?
	 POPJ	P,		;NO. JUST LEAVE
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;YES. UNFORMMATED?
	 JRST	BACKU		;YES. BACKSPACE, UNFORMATTED READ
	JRST	BACKA		;NO. BACKSPACE, FORMATTED READ

;ROUTINES TO CONVERT BETWEEN FILE ADDRESSES AND PROCESS ADDRESSES
;
;%PTOF - CONVERT PROCESS ADDRESS TO FILE ADDRESS
;ARGS:	 P1 = ADDRESS, MUST BE IN THE MEMORY MAPPED TO THE FILE OPEN
;	      ON THE DDB POINTED TO BY D
;RETURN: P1 = CORRESPONDING WORD NUMBER IN THE FILE
;
;%FTOP - CONVERT FILE ADDRESS TO PROCESS ADDRESS
;ARGS:	 P1 = FILE ADDRESS
;RETURN: P1 = PROCESS ADDRESS WITH THAT WORD OF FILE MAPPED TO IT
;	 P2 = COUNT OF WORDS FOLLOWING MAPPED WORD IN WINDOW

%PTOF::	MOVEI	P1,(P1)		;DISCARD LH(P1)
	JUMPE	P1,%POPJ	;ADDRESS=0 MEANS FILE PAGE 0
	SUB	P1,WADR(D)	;GET OFFSET IN WINDOW
	MOVE	T2,BYTN(D)	;GET FILE OFFSET OF NEXT WINDOW
	SUB	T2,WSIZ(D)	;GET FILE OFFSET OF CURRENT WINDOW
	LOAD	T4,BPW(D)	;GET BYTES/WORD
	IDIVI	T2,(T4)		;CONVERT BYTES TO WORDS
	ADD	P1,T2		;ADD TO OFFSET IN THIS WINDOW
	POPJ	P,

%FTOP::	MOVEM	P1,BYTN(D)	;STORE BYTE NUMBER
	MOVE	T1,P1		;ARG IS IN T1
	PUSHJ	P,SMAPW		;GO MAP THE FILE, RETURN PROCESS ADDRESS
	ADDM	P2,BYTN(D)	;SET BYTN UP FOR NXTW
	POPJ	P,


;UNLOAD

MOPUNL:	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIE	T1,DI.MTA	;TAPE?
	  JRST	MOPREW		;NO, UNLOAD IS REWIND
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVX	T0,D%END	;File is now not at end
	ANDCAM	T0,FLAGS(D)
	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNN	T1,D%IN+D%OUT	;If not opened yet,
	 JRST	JSTUNL		;Don't call "SETIN"
	PUSHJ	P,%SETIN	;Get file opened for input.
	MOVEI	T2,.MORUL	;SET FOR UNLOAD OPR
	JRST	DOMTOP		;GO DO IT

JSTUNL:	PUSHJ	P,MTAOJF	;Get a JFN with no filename
	MOVEI	T2,.MORUL	;UNLOAD it
	PUSHJ	P,DOMTP1
	PJRST	MTACJF		;Close, release JFN and return


;TOPS-20 BACKFILE

MOPBSF:	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIE	T1,DI.MTA	;TAPE?
	  POPJ	P,		;NO, BACKFILE IS NOP
	PUSHJ	P,%SETIN	;Make sure we're open for input
	PUSHJ	P,ENDOUT	;SETUP PROPERLY
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVX	T0,D%END	;File is now not at end
	ANDCAM	T0,FLAGS(D)
	MOVEI	T2,.MOBKF	;SET FOR BACKSPACE FILE
	PUSHJ	P,DOMTOP	;GO DO IT
	MOVEI	T2,.MOBKF	;AND A 2ND TIME
	PUSHJ	P,DOMTOP
	LOAD	T1,IJFN(D)	;GET JFN
	GDSTS%			;GET STATUS
	TXNN	T2,MT%BOT	;UNLESS BEG TAPE
	 PUSHJ	P,FORWF
	PJRST	%CLSOP		;MAKE SURE NO STUPID EOF STATUS

;END FILE

MOPEND:	AOS	NREC(D)		;INCR REC #
	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIN	T1,DI.DSK	;DISK?
	  JRST	DSKEND		;YES
	CAIE	T1,DI.MTA	;TAPE?
	 POPJ	P,		;NO. ENDFILE IS A NOP

	PUSHJ	P,%SETOUT	;Set to output
	PUSHJ	P,%LSTBF	;OUTPUT LAST BUFFER, IF ANY
	PUSHJ	P,%CLSOP	;CLOSE FILE, OPEN FOR INPUT AGAIN
ENDOUT:	SETZM	IPTR(D)		;CLEAR THE PNTR/COUNT
	SETZM	ICNT(D)
	MOVE	T1,FLAGS(D)
	TXO	T1,D%IN+D%END	;WE ARE OPEN FOR INPUT, AT EOF
	TXZ	T1,D%OUT	;NO LONGER DOING OUTPUT
	MOVEM	T1,FLAGS(D)
	AOS	BLKN(D)		;INCR BLOCK #
	POPJ	P,

DSKEND:	PUSHJ	P,%SETOUT	;SET TO OUTPUT
	PUSHJ	P,%SETIN	;AND THEN TO INPUT AGAIN
	MOVX	T1,D%END+D%MOD	;AT EOF
	IORM	T1,FLAGS(D)
	POPJ	P,		;DONE

;TOPS-20 SKIP RECORD

MOPSKR:	PUSHJ	P,%SETIN	;Switch to input
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%UNF	;READ AND IGNORE 1 RECORD
	  JRST	%IREC
	JRST	UNFSKP


;SKIP FILE

MOPSKF:	LOAD	T1,INDX(D)	;GET DEV INDEX
	CAIE	T1,DI.MTA	;TAPE?
	  POPJ	P,		;NO, SKIP IS NOP
	MOVE	T0,FLAGS(D)	;GET FLAGS
	TXNN	T0,D%IN+D%OUT	;FILE OPEN?
	 JRST	JSTSKF		;NO. JUST SKIP A FILE
	PUSHJ	P,%SETIN	;Make sure file is open for input
	PUSHJ	P,ENDOUT	;SETUP PROPERLY
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVX	T0,D%END	;Clear EOF bit
	ANDCAM	T0,FLAGS(D)
	MOVEI	T2,.MOFWF	;SET FOR SKIP FILE
	PUSHJ	P,DOMTOP	;BUT IF WE WERE, DON'T GO ANYWHERE
	PJRST	%CLSOP		;MAKE SURE NO STUPID EOF BIT LEFT ON

JSTSKF:	PUSHJ	P,MTAOJF	;GET A JFN, OPEN MTA
	MOVEI	T2,.MOFWF	;DO A SKIP FILE MTOPR
	PUSHJ	P,DOMTP1
	PJRST	MTACJF		;GO CLOSE FILE, RELEASE JFN, LEAVE

FORWF:	MOVEI	T2,.MOFWF	;SKIP FILE


;DOMTOP - Routine to do the MTOP specified in T2. (does appropriate
;  WAIT's etc.)

DOMTOP:	LOAD	T1,IJFN(D)	;GET JFN

;Enter at DOMTP1 if you want to use the JFN in T1.

DOMTP1:	PUSH	P,T2		;SAVE THE OPERATION TO DO

	MOVEI	T2,.MONOP	;DO A WAIT
	MTOPR%
	 ERJMP	MTOPER

	POP	P,T2		;GET THE OPERATION
	MTOPR%			;DO OPERATION
	  ERJMP	MTOPER

	MOVEI	T2,.MONOP	;AND DO A WAIT
	MTOPR%
	 ERJMP	MTOPER
	POPJ	P,		;DONE

MTOPER: ;IOERR	(ILM,23,,?,$J,,%POPJ)
	$ECALL	ILM,%ABORT

> ;IF20
IF10,<

MOPREW:	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;AND BLOCK #
	MOVX	T0,D%END	;Clear EOF bit
	ANDCAM	T0,FLAGS(D)
	LOAD	T1,DVTYP(D)	;GET DEVICE INDEX
	CAIN	T1,.TYDTA	;DECTAPE?
	 JRST	DTAREW		;YES
	CAIN	T1,.TYDSK	;DISK?
	 JRST	DSKREW		;Yes
	CAIN	T1,.TYMTA	;Magtape?
	 JRST	MTAREW		;Yes
	POPJ	P,		;OTHERWISE IT'S A NOP

.FOMTP==30
DTAREW:	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNN	T1,D%IN+D%OUT	;Is the DECTAPE open?
	 JRST	RWDEVO		;Yes, don't use filename
	PUSHJ	P,%SETIN	;OPEN for input.
	SETZM	BLKN(D)		;CLEAR BLOCK NUMBER
	MOVE	T2,CHAN(D)	;LH= chan #
	HRRI	T2,.FOMTP	;MTAPE FILOP
	MOVX	T3,MTREW.	;REWIND
	MOVE	T1,[2,,T2]
	FILOP.	T1,
	 $ECALL	IOE,%ABORT
	POPJ	P,

DSKREW:	MOVE	T1,FLAGS(D)	;Is file really OPEN?
	TXNE	T1,D%IN+D%OUT
	 PUSHJ	P,CLSOPN	;Yes, CLOSE the file, open for input
	POPJ	P,		;Return

MTAREW:	MOVE	T1,FLAGS(D)	;Get flags
	TXNN	T1,D%IN+D%OUT	;Is file really OPEN
	 JRST	RWDEVO		;No
	PUSHJ	P,CLSOPN	;CLOSE THE FILE, OPEN FOR INPUT
	MOVEI	T2,.TFREW	;Go do REWIND
	PJRST	DOMTOP

;Here to REWIND a non-directory device that is not opened yet.
; Can't use FILOP.'s because you need a filename for them.

RWDEVO:	PUSHJ	P,OPDEVO	;Open the device only
	MOVE	T1,ASCHN	;Get channel #
	LSH	T1,^D23		;Shift to ac field
	IOR	T1,[MTREW.]	;Make instruction
	XCT	T1		;** REWIND the device **
	PJRST	CLDEVO		;Close device and return

;Routine to OPEN the device only, (on a low channel).
; FILOP. is not done, because no file can be specified.
;The assigned channel is stored in ASCHN.
;Returns .+1 or takes ERR= or goes to %ABORT (if errors)

OPDEVO:	SETZ	T1,		;Get a free channel
	PUSHJ	P,%ALCHN	;Get a channel
	 $ECALL	NFC,%ABORT	;?Too many OPEN units
	MOVEM	T1,ASCHN	;Save it
	LSH	T1,^D23		;Shift into AC position
	IOR	T1,[OPEN T2]	;Get instruction to XCT
	MOVEI	T2,.IODMP	;Set dump mode
	SETZ	T4,		;No buffers
	MOVE	T3,DEV(D)	;Get device
	XCT	T1		;** OPEN the device **
	 JRST	OPDVFL		;?Failed
	POPJ	P,		;OK, return

;The OPEN UUO failed. Either "No such device"
;or "Assigned to another job".

OPDVFL:	MOVE	T1,DEV(D)	;See if this device exists
	DEVTYP	T1,
	 JRST	OPDVNS		;?no such device
	JUMPE	T1,OPDVNS	;Or if 0 returned.
	SKIPA	T1,[ERDAJ%]	;"Device allocated to another job"
OPDVNS:	MOVEI	T1,ERNSD%	;"No such device"
	$ECALL	OPN,%ABORT	;Give error, abort if no ERR=

SEGMENT DATA
ASCHN:	BLOCK	1		;Assigned channel for non-FILOP. I/O
SEGMENT CODE

;Routine to CLOSE the device OPEN'ed by OPDEVO.
;Returns .+1 always

CLDEVO:	MOVE	T1,ASCHN	;Get assigned channel #
	LSH	T1,^D23		;Shift into ac position
	IOR	T1,[RELEAS 0]	;Get a RELEASE instruction
	XCT	T1		;Do it
	MOVE	T1,ASCHN	;Get channel #
	PUSHJ	P,%DECHN	;Deallocate it
	 $SNH			;?Not assigned, "can't happen"
	POPJ	P,		;Ok, return


;Still IF10

MOPBSR:	PUSHJ	P,%SETIN		;Get file open for input
	LOAD	T1,INDX(D)		;GET DEVICE INDEX
	CAIE	T1,DI.DSK		;DISK?
	CAIN	T1,DI.MTA		;OR MAGTAPE?
	  JRST	BSROK			;YES
	POPJ	P,			;NO. BSR IS NOP
BSROK:	SKIPG	BLKN(D)			;HAVE WE READ ANYTHING?
	 POPJ	P,			;NO. BACKSPACE IS A NOP
	MOVE	T1,NREC(U)		;GET RECORD NUMBER
	SOJLE	T1,%POPJ		;CAN'T GO BACK
	MOVEM	T1,NREC(U)		;STORE UPDATED ONE
	LOAD	T1,MODE(D)		;GET FILE MODE
	CAIN	T1,MD.DMP		;DUMP MODE?
	 JRST	BSRDMP			;YES. VERY SPECIAL
	CAIN	T1,MD.BIN		;IS IT BINARY?
	 JRST	BSRBIN			;YES
	CAIE	T1,MD.ASC		;IS IT ASCII?
	 CAIN	T1,MD.ASL
	  JRST	BSRASC			;YES. GO LOOK BACKWARDS FOR LF
	SKIPE	RSIZE(D)		;NO. FIXED-LENGTH RECORDS?
	 JRST	IMGFIX			;YES
	JRST	NOBSR			;NO. DO NOTHING!
BSRASC:	SKIPE	RSIZE(D)		;FIXED-LENGTH RECORDS?
	 JRST	ASCFIX			;YES. EASY TREATMENT
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%END		;ARE WE AT EOF?
	 JRST	ASCNEF			;NO
	PUSHJ	P,BAKEOF		;YES. BACK UP AND GET PREVIOUS BLOCK
	SKIPN	BLKN(D)			;NULL FILE?
	 POPJ	P,			;YES. WE'RE DONE
	MOVE	T1,IBCB(D)		;GET BUFFER HEADER ADDR
	MOVE	P2,1(T1)		;RETURN THE WORD COUNT OF THE BUFFER
	IMULI	P2,5			;TURN INTO CHARS
	HRRZ	P1,IPTR(D)		;GET END BUFFER PNTR
	MOVE	T3,ICNT(D)		;AND COUNT (SHOULD BE ZERO)
	MOVEI	T2,4			;SET INDEX TO END OF WORD
	JRST	BIDXOK			;GO BACK TO PREVIOUS EOL CHAR

ASCNEF:	MOVE	P1,IBCB(D)		;GET ADDR OF BUFFER
	HRRZ	P2,1(P1)		;GET WORD COUNT
	IMULI	P2,5			;GET CHAR COUNT OF BUFFER
	HRRZ	P1,IPTR(D)		;GET CURRENT WORD PNTR
	LDB	T2,[POINT 6,IPTR(D),5]	;GET BYTE OFFSET
	IDIVI	T2,7			;CALCULATE INDEX
	SUBI	T2,4			;SUBTRACT FROM 4
	MOVM	T2,T2			;TO GET INDEX IN RIGHT DIRECTION
	MOVE	T3,ICNT(D)		;GET CURRENT COUNT
	PUSHJ	P,BACKUP		;BACKUP TO LAST LF
	ADDI	T3,1			;NOW TO PREVIOUS CHAR
	SOJGE	T2,BIDXOK
	MOVEI	T2,4
	SUBI	P1,1
BIDXOK:	PUSHJ	P,BACKUP		;AND TO PREVIOUS LF
	MOVE	T2,PNTABL(T2)		;CREATE PNTR TO IT
	TLZ	T2,17			;THROW OUT INDEX
	HRRI	T2,(P1)
	MOVEM	T2,IPTR(D)		;SAVE IT
	MOVEM	T3,ICNT(D)		;AND COUNT
NOBSR:	POPJ	P,

%BAKEF:
BAKEOF:	SOS	BLKN(D)			;DECR BLOCK FOR THE EOF
%ISET:	PUSHJ	P,CLSOPN		;CLEAR THE EOF STATUS
	LOAD	T1,INDX(D)		;GET DEVICE INDEX
	CAIN	T1,DI.DSK		;DISK?
	 JRST	DSKEOF			;YES. GO DO USETI
	CAIE	T1,DI.MTA		;MAGTAPE?
	 POPJ	P,			;NO. CAN'T DO ANYTHING ELSE
	MOVEI	T2,.TFBSB		;YES. BACKSPACE A FILEMARK
	PUSHJ	P,DOMTOP
	LOAD	T1,MODE(D)		;GET DATA MODE
	CAIE	T1,MD.DMP		;DUMP?
	 SKIPG	BLKN(D)			;NO. ANY DATA?
	  POPJ	P,			;LEAVE IF NO DATA OR DUMP MODE
	MOVEI	T2,.TFBSB		;BACK OVER THE RECORD WE WANT
	PUSHJ	P,DOMTOP
	JRST	COMEOF			;JOIN COMMON CODE

DSKEOF:	MOVE	T2,FBLK(D)		;GET CHANNEL STUFF
	HRRI	T2,.FOUSI		;DO USETI
	SKIPG	T3,BLKN(D)		;TO CURRENT BLOCK
	 POPJ	P,			;LEAVE IF NULL FILE
	MOVE	T1,[2,,T2]
	FILOP.	T1,
	 $ECALL	IOE,%ABORT
	LOAD	T1,MODE(D)		;GET DATA MODE
	CAIN	T1,MD.DMP		;DUMP?
	 POPJ	P,			;YES. DON'T READ ANYTHING

COMEOF:	MOVE	T2,FBLK(D)		;GET CHANNEL STUFF
	HRRI	T2,.FOINP		;READ THE BLOCK
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	 $ECALL	IOE,%ABORT		;Should not fail with "EOF"
	MOVE	T1,IBCB(D)		;GET BUFFER HEADER ADDR
	HRRZ	T1,1(T1)		;GET THE WORD COUNT
	ADDM	T1,IPTR(D)		;POINT TO END OF BUFFER
	SETZM	ICNT(D)			;CLEAR COUNT
	POPJ	P,

BSRDMP:	SKIPN	T1,BLKN(D)		;GET BLOCK #
	 POPJ	P,			;HAVEN'T DONE ANY INPUT YET
	SOS	BLKN(D)			;DECR THE BLOCK #
	LOAD	T2,INDX(D)		;GET DEVICE INDEX
	CAIE	T2,DI.DSK		;DISK?
	 JRST	%BACKB			;NO. BACKSPACE AN MTA BLOCK
	PJRST	USET			;SET NEXT BLOCK TO CURRENT ONE

;HERE IF BINARY RECORD
BSRBIN:	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END		;ARE WE AFTER AN EOF?
	 PJRST	BAKEOF			;YES. GET PREV BLOCK, POINT TO END
	MOVE	P1,IBCB(D)		;GET ADDR OF BUFFER
	HRRZ	P2,1(P1)		;GET WORD COUNT
	MOVEI	P3,(P2)			;COPY IT
	CAMN	P2,ICNT(D)		;ARE WE POINTING TO BEG BLK?
	 JRST	BBACK			;YES. MUST GET PREV BUFFER
	MOVE	P1,IPTR(D)		;GET THE CURRENT PNTR
	MOVE	T1,ICNT(D)		;GET COUNT LEFT
	MOVEM	T1,BSRCNT		;SAVE FOR BACKSPACE
	HRRZ	T1,(P1)			;GET THE WORD COUNT OF LAST REC
	JRST	GTBPTR			;NOW GO BACK UP THE FILE

BBACK:	PUSHJ	P,PRVBUF		;GET THE PREVIOUS BUFFER
	 JRST	BBOK			;NON-SKIP MEANS NOT BEG OF FILE
	POPJ	P,			;SKIP MEANS BEGINNING OF FILE

BBOK:	MOVEM	T3,BSRCNT		;SAVE ALSO FOR BACKSPACE
	MOVEI	P3,(P2)			;COPY THE WORD COUNT
	HRRZ	T1,(P1)			;GET THE LAST REC WORD SIZE
GTBPTR:	PUSHJ	P,BCOM			;DO THE BACKSPACE
	MOVE	T1,BSRCNT		;RESTORE NEW COUNT
	MOVEM	T1,ICNT(D)
	POPJ	P,

;HERE FOR ASCII OR IMAGE FIXED-LENGTH RECORDS

ASCFIX:	MOVE	T0,FLAGS(D)		;Get DDB flags
	TXNE	T0,D%END		;ARE WE AFTER AN EOF?
	 PJRST	BAKEOF			;YES. GET PREV BLOCK, POINT TO END
	MOVE	T1,ICNT(D)		;GET COUNT
	IDIVI	T1,5			;GET # WORDS
	MOVEM	T1,BSRCNT		;SAVE WORD COUNT
	PUSHJ	P,BSRFIX		;GO BACK
	MOVE	T1,BSRCNT		;GET WORDS AGAIN
	IMULI	T1,5			;CONVERT TO CHARS
	MOVEM	T1,ICNT(D)		;SAVE IT
	HRLOI	T1,7777			;AND RIGHT JUSTIFY THE PNTR
	ANDM	T1,IPTR(D)		;BY CLEARING THE POSITION
	POPJ	P,

IMGFIX:	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END		;ARE WE AFTER AN EOF?
	 PJRST	BAKEOF			;YES. GET PREV BLOCK, POINT TO END
	MOVE	T1,ICNT(D)		;GET WORD COUNT
	MOVEM	T1,BSRCNT		;SAVE IT
	PUSHJ	P,BSRFIX		;GO BACK
	MOVE	T1,BSRCNT		;GET WORDS AGAIN
	MOVEM	T1,ICNT(D)		;SAVE IT
	POPJ	P,

BSRFIX:	MOVE	P1,IBCB(D)		;GET THE BUFFER ADDR
	HRRZ	P2,1(P1)		;GET THE WORD COUNT
	MOVEI	P3,(P2)			;COPY IT
	LOAD	T1,RSIZW(D)		;GET # WORDS IN RECORD
BCOM:	SUB	P3,BSRCNT		;GET # WORDS USED
	SUBI	P3,(T1)
	JUMPL	P3,BPREV		;HAVE TO GO TO PREV BUFFER
	SUBI	P2,(P3)			;GET NEW # WORDS
	MOVEM	P2,BSRCNT		;SAVE NEW COUNT
	MOVNI	T1,(T1)			;GET NEG REC SIZE
	ADDM	T1,IPTR(D)		;DECR THE PNTR
	POPJ	P,			;AND LEAVE

BPREV:	PUSHJ	P,PRVBUF		;GET PREV BUFFER
	 JRST	NOTBEG			;NOT AT BEG OF FILE
	MOVE	P1,IBCB(D)		;BEG FILE. GET ADDR OF BUFFER
	MOVE	P2,1(P1)		;GET # WORDS IN BUFFER
	MOVEM	P2,BSRCNT		;BUFFER IS FULL
	MOVEI	T1,1(P1)		;SETUP BEG PNTR
	HRRM	T1,IPTR(D)
	POPJ	P,

NOTBEG:	MOVM	T1,P3			;FIND POS DIFF NECESSARY
	CAIG	T1,(P2)			;.GT. BUFFER SIZE?
	 JRST	BINOK			;NO
	ADD	P3,P2			;YES. DECREASE THE NEG DIFF
	JRST	BPREV			;AND GO GET ANOTHER BUFFER
BINOK:	ADD	P1,P3			;DECR THE PNTR
	HRRM	P1,IPTR(D)
	MOVEM	T1,BSRCNT		;AND SETUP THE COUNT
	POPJ	P,

AFTPRV:	IMULI	P2,5			;GET BYTE COUNT
RCLP0:	MOVEI	T2,4			;LOAD MAX INDEX
BACKUP:
RCLP1:	CAIL	T3,(P2)			;COUNT AT BUFFER MAX?
	JRST	BEGBUF			;YES. GO GET PREVIOUS BLOCK
	LDB	T1,PNTABL(T2)		;GET A CHAR
	CAIG	T1,14			;EOL CHAR?
	 CAIGE	T1,12
	  JRST	NEOL			;NO
	POPJ	P,			;YES. WE'RE DONE

NEOL:	ADDI	T3,1			;INCR BUFFER COUNT
	SOJGE	T2,RCLP1		;LOOP FOR 5 CHARS/WORD
	SOJA	P1,RCLP0		;THEN DECR THE WORD PNTR

PNTABL:	POINT	7,(P1),6
	POINT	7,(P1),13
	POINT	7,(P1),20
	POINT	7,(P1),27
	POINT	7,(P1),34

;
;HERE WE MUST DIVERT THE POINTER TO THE PREVIOUS BLOCK
;AND RESET THE CHAR COUNT

BEGBUF:	PUSHJ	P,PRVBUF		;GET PREVIOUS BLOCK
	  JRST	AFTPRV			;NON-SKIP MEANS WE'RE OK
	MOVEI	T2,4			;RIGHT JUSTIFY THE INDEX, BECAUSE
	POPJ	P,			;OTHERWISE WE'RE AT FILE START

PRVBUF:	MOVE	T1,BLKN(D)		;GET CURRENT BLOCK #
	SOJLE	T1,%POPJ1		;CAN'T GO BACKWARDS
	MOVEM	T1,BLKN(D)		;SAVE IT BACK
	PUSHJ	P,REDBLK		;READ THE BLOCK
	MOVEI	P2,(T1)			;RETURN THE WORD COUNT OF THE BUFFER
	HRRZ	P1,IPTR(D)		;GET THE WORD PNTR
	MOVE	T3,ICNT(D)		;AND THE COUNT
	POPJ	P,

REDBLK:	LOAD	T2,INDX(D)		;GET DEVICE INDEX
	CAIE	T2,DI.MTA		;MAGTAPE?
	  JRST	DSKRED			;NO. DISK
	PUSHJ	P,CLRBCB		;COUNT ACTIVES
	PUSH	P,P4			;Get a spare perm ac
	MOVEI	P4,1(T1)		;Must back over current one too
MTABLP:	MOVEI	T2,.TFBSB		;SETUP FOR BACKSPACE
	PUSHJ	P,DOMTOP		;DO IT
	SOJG	P4,MTABLP		;BACKSPACE FOR ALL ACTIVES
	POP	P,P4			;Restore P4
	JRST	GOREAD			;GO READ THE BLOCK

DSKRED:	PUSHJ	P,USET			;POINT TO PREVIOUS BLOCK
	PUSHJ	P,CLRBCB		;CLEAR THE USE BITS
GOREAD:	MOVSI	T1,(BF.VBR)		;TURN ON VIRGIN BUFFER RING
	IORM	T1,IBCB(D)		;IN THE BUFFER HEADER
	MOVE	T2,CHAN(D)		;GET CHANNEL
	HRRI	T2,.FOINP		;SETUP FOR INPUT
	HRRZ	T3,IBCB(D)		;POINT TO CURRENT BUFFER
	MOVE	T1,[2,,T2]		;2-WORD FILOP
	FILOP.	T1,
	 $ECALL	IOE,%ABORT		;Should not fail with EOF
	MOVE	T1,IBCB(D)		;GET BUFFER ADDR
	HRRZ	T1,1(T1)		;GET WORD COUNT
	ADDM	T1,IPTR(D)		;POINT TO END OF BUFFER
	SETZM	ICNT(D)			;AND CLEAR COUNT
	POPJ	P,

USET:	MOVE	T2,FBLK(D)		;GET FILOP WORD 0
	HRRI	T2,.FOUSI		;GET USETI CODE
	MOVEI	T3,(T1)			;GET THE BLOCK #
	MOVE	T1,[2,,T2]		;DO THE USETI
	FILOP.	T1,
	 $ECALL	IOE,%ABORT
	POPJ	P,

;Routine to clear the "use" bits of all active buffers, and
; return how many there were in T1.

%CLRBC:
CLRBCB:	SETZM	ICNT(D)			;CLEAR BUFFER CONTROL BLOCK
	HRLOI	T1,7700			;EXCEPT BYTE SIZE IN PNTR
	ANDM	T1,IPTR(D)
	MOVE	T2,CHAN(D)		;GET CHANNEL
	HRRI	T2,.FOWAT		;SETUP FOR WAIT
	MOVE	T1,[1,,T2]		;DO FILOP
	FILOP.	T1,
	 $ECALL	IOE,%ABORT

	SETZ	T1,			;CLEAR ACTIVE BUFFER COUNT
	HRRZ	T3,IBCB(D)		;GET PNTR TO BUFFER
	MOVEI	T2,(T3)			;COPY IT
FNDUSE:	MOVE	T4,-1(T3)		;GET STATUS WORD
	TLNE	T4,40			;TAPE EOF?
	 AOJA	T1,USEDON		;YES. WE'RE DONE
	MOVE	T4,(T3)			;GET THE USE WORD
	TLZE	T4,(1B0)		;TURN OFF. WAS IT ON?
	  ADDI	T1,1			;YES. ADD TO ACTIVE COUNTER
	MOVEM	T4,(T3)			;PUT IT BACK
	HRRZ	T3,(T3)			;GET WHAT IT POINTS TO
	CAIN	T2,(T3)			;POINTING TO CURRENT BUFFER?
	  POPJ	P,			;YES. WE'VE DONE IT
	JRST	FNDUSE			;AND TRY AGAIN
USEDON:	HRLOI	T4,377777		;TURN OFF USE BIT JUST IN CASE
	ANDM	T4,(T3)
	POPJ	P,

;UNLOAD

MOPUNL:	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVX	T0,D%END	;File is now not at end
	ANDCAM	T0,FLAGS(D)
	LOAD	T1,DVTYP(D)	;GET DEVICE TYPE
	CAIN	T1,.TYDTA	;DECTAPE?
	 JRST	DTAUNL		;YES
	CAIN	T1,.TYDSK	;DISK
	 JRST	DSKUNL
	CAIN	T1,.TYMTA	;Or magtape
	 JRST	MTAUNL
	POPJ	P,		;OTHERWISE IT'S A NOP

DSKUNL:	SKIPN	FBLK(D)		;IS FILE REALLY OPEN?
	 POPJ	P,		;No, no-op.
	PJRST	CLSOPN		;Close file, leave OPEN for input.

MTAUNL:	SKIPN	FBLK(D)		;Is file really OPEN?
	  JRST	ULDEVO		;No, just UNLOAD.
	PUSHJ	P,CLSOPN	;Close file, leav OPEN for input.
	MOVEI	T2,.TFUNL	;Setup for UNLOAD
	JRST	DOMTOP		;Go do it

DTAUNL:	MOVE	T1,FLAGS(D)	;Get DDB flags
	TXNN	T1,D%IN+D%OUT	;Is the DECTAPE open?
	 JRST	ULDEVO		;Yes, don't use filename
	PUSHJ	P,%SETIN	;Open the dectape
	MOVE	T2,CHAN(D)	;LH= chann #
	HRRI	T2,.FOMTP	;MTAPE FILOP
	MOVX	T3,MTUNL.	;UNLOAD
	MOVE	T1,[2,,T2]
	FILOP.	T1,
	 $ECALL	IOE,%ABORT
	POPJ	P,

;Here to UNLOAD a DECtape or magtape that is not opened yet.
; Can't use FILOP.'s because you need a filename for them.

ULDEVO:	PUSHJ	P,OPDEVO	;Open the device only
	MOVE	T1,ASCHN	;Get channel #
	LSH	T1,^D23		;Shift to ac field
	IOR	T1,[MTUNL.]	;Make instruction
	XCT	T1		;** UNLOAD the device **
	PJRST	CLDEVO		;Close device and return

;TOPS-10 BACKFILE

MOPBSF:	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIE	T1,DI.MTA	;MAGTAPE?
	 POPJ	P,		;OTHERWISE IT'S A NOP
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%END+D%OUT	;EOF OR DOING OUTPUT?
	 PUSHJ	P,CLREOF	;YES. CLOSE/OPEN THE FILE
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%IN+D%OUT	;Is file not open?
	 PJRST	DOBKFU		;OPEN already, just do the UUO's and return.

;The MTA is not open.

	PUSHJ	P,OPDEVO	;OPEN the device
	PUSHJ	P,DOBKFU	;Do the BACKFILE UUO's.
	PJRST	CLDEVO		;Close device and return.

;Subroutine to do the UUO's necessary for BACKFILE.
;The device is OPEN.

DOBKFU:	PUSHJ	P,BACKF		;NOW BACKSPACE OVER 2 EOF MARKS
	PUSHJ	P,BACKF
	MOVEI	T2,.TFSTS	;GET STATUS OF TAPE UNIT
	PUSHJ	P,DOMTOP
	TXNE	T1,TF.BOT	;BEG TAPE?
	 POPJ	P,		;YES. JUST LEAVE
	MOVEI	T2,.TFFSF	;NO. MUST FORWARD AGAIN
	PJRST	DOMTOP

;TOPS-10 ENDFILE

MOPEND:	AOS	NREC(D)		;INCR REC #
	PUSHJ	P,%SETOUT	;Get file opened for output
	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIE	T1,DI.DSK	;DISK
	 CAIN	T1,DI.MTA	;OR MTA
	  JRST	.+2		;YES
	POPJ	P,		;NO. END FILE IS NOP
	PUSHJ	P,CLSOPN	;CLOSE, THEN OPEN FOR INPUT
	MOVX	T0,D%END	;Set fake end if necessary
	IORM	T0,FLAGS(D)
	AOS	BLKN(D)		;SIMULATE READING THE EOF RECORD
	POPJ	P,

;TOPS-10 SKIP RECORD

MOPSKR:	PUSHJ	P,%SETIN		;Set file open for input
	LOAD	T1,MODE(D)
	CAIN	T1,MD.DMP		;DUMP MODE?
	 JRST	SKRDMP			;YES. VERY SPECIAL
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF		;UNFORMATTED?
	 JRST	UNFSKP			;YES. DO UNFORMATTED SKIP
	JRST	%IREC			;TO SKIP RECORD, JUST READ AND IGNORE

SKRDMP:	AOS	T3,BLKN(D)		;GET THE INCREMENTED BLOCK #
	ADDI	T3,1			;WANT THE NEXT ONE
	LOAD	T1,INDX(D)		;GET DEVICE INDEX
	CAIE	T1,DI.DSK		;DISK?
	 JRST	%SKIPB			;NO. SKIP AN MTA BLOCK
	MOVE	T2,FBLK(D)		;GET FILOP WORD 0
	HRRI	T2,.FOUSO		;GET USETO CODE
	MOVE	T1,[2,,T2]		;DO THE USETI
	FILOP.	T1,
	 PUSHJ	P,%CLSER		;JUST RETURN ON EOF
	POPJ	P,

;TOPS-10 SKIP FILE

MOPSKF:	LOAD	T1,INDX(D)	;GET DEVICE INDEX
	CAIE	T1,DI.MTA	;MAGTAPE?
	  POPJ	P,		;NO. SKF IS NOP
	MOVEI	T1,1		;SET NEXT RECORD TO 1
	MOVEM	T1,NREC(U)
	SETZM	BLKN(D)		;CLEAR BLOCK #
	MOVE	T0,FLAGS(D)
	TXNN	T0,D%OUT	;WERE WE DOING OUTPUT?
	 JRST	SKFIN		;NO
	PUSHJ	P,%SETIN	;Yes. Close file, open again
	JRST	SKFCOM

;File not opened for output.

SKFIN:	MOVE	T1,FLAGS(D)
	TXNE	T1,D%IN		;Is file OPEN?
	 JRST	SKFINN		;Yes
	PUSHJ	P,OPDEVO	;OPEN device
	MOVEI	T2,.TFFSF	;Skip a file
	PUSHJ	P,DOMTOP
	PJRST	CLDEVO		;Close again, and return.

SKFINN:	PUSHJ	P,CLREOF	;Clear EOF for input file
SKFCOM:	MOVEI	T2,.TFFSF	;SKIP A FILE
	JRST	DOMTOP

%SKIPB:	MOVEI	T2,.TFFSB	;SKIP A BLOCK
	PJRST	DOMTOP

%BACKB:	MOVEI	T2,.TFBSB	;BACKSPACE BLOCK
	PJRST	DOMTOP

BACKF:	MOVEI	T2,.TFBSF	;BACKSPACE FILE

;DOMTOP - DOES MAGTAPE OP, RETURNS FLAGS IN T1
DOMTOP:	MOVE	T3,DEV(D)	;GET DEVICE NAME
	MOVE	T1,[2,,T2]	;DO TAPOP
	TAPOP.	T1,
	 $ECALL	UTE,%ABORT	;?Unexpected TAPOP error $O,<T1>
	MOVEI	T2,.TFWAT	;THEN A WAIT
	MOVE	T4,[2,,T2]
	TAPOP.	T4,
	 $ECALL	UTE,%ABORT
	POPJ	P,

CHKEF:	MOVE	T1,DEV(D)	;GET THE DEVICE NAME
	MOVEM	T1,MTCBLK	;SETUP FOR MTCHR
	MOVE	T1,[MTCLEN,,MTCBLK]
	MTCHR.	T1,		;GET CHARACTERISTICS
;	  IOERR	(UME,,,?,Unexpected MTCHR error $O,<T1>,%ABORT)
	 $ECALL	UME,%ABORT
	SKIPE	MTCBLK+.MTREC	;ANY RECS AFTER LAST EOF?
	  AOS	(P)		;YES. NOT AT EOF THEN. SKIP RETURN
	POPJ	P,


;Clear EOF by CLOSE'ing and re-OPENing the file.
;If it was opened for output, leave it that way.
; If it was opened for input, leave it that way.

CLREOF:	MOVE	T2,FBLK(D)	;GET THE CHANNEL STUFF
	HRRI	T2,.FOREL	;CLOSE THE FILE
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	 PUSHJ	P,%CLSER
	MOVSI	T1,(FO.PRV+FO.ASC)
	HLLM	T1,FBLK(D)	;AND NOW REOPEN IT
	MOVX	T5,D%IN		;Get flag to set
	MOVE	T0,FLAGS(D)	;Get current DDB flags
	TXNE	T0,D%OUT	;If file is now OPEN for output,
	 MOVX	T5,D%OUT	;Leave it that way
	TXZ	T0,D%IN+D%OUT+D%END	;Clear current flags
	MOVEM	T0,FLAGS(D)	;Store new DDB flags
	PUSH	P,T5		;Save flags
	PUSHJ	P,%ST10B	;Setup .FOBRH, .FONBF
	POP	P,T5		;Restore flags to set on OPEN
	PUSHJ	P,%CALOF	;Try re-opening the file
	 JRST	%ABORT		;?Failed
	POPJ	P,		;Worked, return

	SEGMENT	DATA

BSRCNT:	BLOCK	1		;# WORDS FOR BACKSPACE
DMPLST:	BLOCK	MAXARG+1	;DUMP I/O LIST
	BLOCK	1		;THE ZERO WORD (JUST IN CASE)

	MTCLEN==20
MTCBLK:	BLOCK	MTCLEN

	SEGMENT	CODE

;Routine to CLOSE and then re-OPEN a file for input.
;This will have the effect of clearing the EOF status if set.

CLSOPN:	MOVE	T1,FLAGS(D)	;GET FLAGS
	TXNE	T1,D%OUT	;FILE OPEN FOR OUTPUT?
	 PUSHJ	P,%LSTBF	;YES. OUTPUT LAST BUFFER IF MTA
	MOVE	T2,FBLK(D)	;GET CHANNEL STUFF
	HRRI	T2,.FOCLS	;CLOSE THE FILE
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	 PUSHJ	P,%CLSER
	MOVE	T2,FBLK(D)
	HRRI	T2,.FOREL	;RELEASE THE CHANNEL
	MOVE	T1,[1,,T2]
	FILOP.	T1,
	 PUSHJ	P,%CLSER
	MOVE	T1,[FO.PRV+FO.ASC+.FORED] ;OPEN IT FOR INPUT
	MOVEM	T1,FBLK(D)
	SETZM	LKPB+.RBALC(D)	;PREVENT TRUNCATION
	MOVX	T0,D%END+D%IO+D%OUT
	ANDCAM	T0,FLAGS(D)	;Clear flags
	MOVX	T5,D%IN		;Set this flag if OPEN works
	PUSHJ	P,%ST10B	;Setup .FOBRH, .FONBF
	MOVX	T5,D%IN		;Get flag again
	PUSHJ	P,%CALOF	;Try re-opening the file
	 JRST	%ABORT		;Failed
	POPJ	P,		;Done, return

> ;IF10
	SUBTTL	FIND

;FIND STATEMENT
;
;POSITIONS A RANDOM-ACCESS DISK FILE SO THAT SUBSEQUENT I/O WILL TAKE LESS TIME
;IF SUFFICIENT COMPUTATION INTERVENES BETWEEN THE FIND AND THE I/O.
;
;10:  IF THE UNIT IS IDLE, NOT TRANSFERRING DATA FOR THIS JOB OR ANY
;     OTHER JOB, POSITIONS THE ACCESS ARMS TO THE CORRECT CYLINDER
;
;20:  CAN'T BE DONE
;
;THIS STATEMENT IS ALMOST ENTIRELY WORTHLESS.

	SIXBIT	/FIND./
FIND%:	PUSHJ	P,%SAVE		;SAVE USER'S ACS
	PUSHJ	P,FMTCNV	;CONVERT OLD-STYLE ARG LIST
	XMOVEI	T1,[ASCIZ /FIND/] ;SET STATEMENT NAME FOR ERROR MESSAGES
	MOVEM	T1,%IONAM
	SETZ	P1,		;No special flags to set

	PUSHJ	P,STIO		;Get args, set D%RAN in P1 (hopefully)
	PUSHJ	P,%SETD		;SET D AND U, DO IMPLICIT OPEN IF NECESSARY
	PUSHJ	P,%SETIN	;Get file opened for input
	MOVE	T1,@A.REC	;GET RECORD NUMBER
	MOVEM	T1,NREC(U)	;STORE IN DDB FOR ASSOCIATE VARIABLE

IF10,<
	MOVE	T3,RSIZE(D)	;GET RECORD SIZE
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;FORMATTED FILE?
	  JRST	FNDUNF		;UNFORMATTED, SLIGHTLY DIFFERENT

	ADDI	T3,2+4		;ADD 2 BYTES FOR CRLF, 4 TO ROUND UP TO WORD
	IDIVI	T3,5		;GET RECORD SIZE, WORDS
	JRST	FIND1		;GO DO THE SEEK

;DDB flags are in T0.

FNDUNF:	TXNE	T0,D%BIN	;BINARY?
	  ADDI	T3,2		;YES, ADD 2 LSCWS

FIND1:	LSH	T3,-7		;CONVERT TO BLOCK NUMBER
	ADDI	T3,1
	HLLZ	T2,CHAN(D)	;GET CHANNEL NUMBER
	HRRI	T2,.FOSEK	;SET SEEK FUNCTION
	MOVE	T1,[2,,T2]	;POINT TO FILOP BLOCK
	FILOP.	T1,		;DO THE "SEEK" FILOP
	 $ECALL	IOE,%ABORT
>;END IF10

	PJRST	%SETAV		;GO SET ASSOCIATE VARIABLE AND RETURN
	SUBTTL	IOLST

	SIXBIT	/IOLST./
IOLST%:	SKIPN	%UDBAD		;DO WE HAVE A UDB?
	 JRST	[POP P,1	;No. Error or EOF occured, get AC1
		 POPJ P,]	; and go back to user pgm.
	PUSHJ	P,%ISAVE	;SAVE ACS
	MOVE	U,%UDBAD	;RESTORE DDB ADDRESS
	MOVE	D,DDBAD(U)

IOLP:	MOVE	T1,(L)		;GET NEXT I/O LIST ENTRY
	SKIPN	T2,@%LTYPE	;Anything there?
	  POPJ	P,		;END OF LIST, RETURN TO USER PROGRAM
	LDB	T2,[POINTR T2,ARGKWD] ;GET TYPE OF ENTRY
	CAILE	T2,6		;IN RANGE?
	  SETZ	T2,		;NO, ILLEGAL
	PUSHJ	P,@[
		IFIW ILL
		IFIW DATA
		IFIW SLIST
		IFIW ELIST
		IFIW FIN
		IFIW SLST77
		IFIW ELST77 ](T2) ;Process it
	JRST	IOLP		;CONTINUE UNTIL END OF LIST


ILL:	ADDI	L,1		;INCREMENT PAST ARG
	TDNN	T1,[377777777777] ;ONLY LEGAL ARG IS ALL 0
	JRST	ILLOK		;AN OK BAD ARGUMENT...
	EXCH	P,OTHERP	;STUPID COROUTINE NEEDS THE RIGHT STACK!
	 $ECALL	IOL,%ABORT	;BAD I/O LIST
ILLOK:	ADJSP	P,-1		;DISCARD RETURN ADDRESS
	POPJ	P,		;RETURN FROM IOLST%


DATA:	LDB	T2,[POINTR @%LTYPE,ARGTYP]	;GET DATATYPE
	MOVEI	T3,1		;1 ENTRY
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;BUT IF UNFORMATTED
	 MOVE	T3,%SIZTB(T2)	;SET "ARRAY" LENGTH TO DATUM SIZE
	SETZ	T4,		;CLEAR INCREMENT
	AOJA	L,%DOIO		;GO PROCESS SINGLE DATUM


;EXPLICIT FIN CALL
FIN%:	SKIPN	%UDBAD		;ANY UDB PNTR?
	 JRST	[POP P,1	;No. Restore AC1
		 POPJ P,]	; & return (Must have gotten END or ERR).
	PUSHJ	P,%FSAVE	;SAVE ACS
	MOVE	U,%UDBAD
	MOVE	D,DDBAD(U)
	JRST	FIN1

FIN:	ADJSP	P,-1		;FIN CONSTITUTES END OF LIST, SO DISCARD
				; RETURN ADDRESS

FIN1:	SETZB	T1,T3		;FLAG END OF I/O LIST
	SETZ	T2,		;ALSO CLEAR DATA TYPE REG
	PJRST	%DOIO		;GO FINISH UP FORMAT PROCESSING
SLST77:	SETOM	SWTCH7		;SET 77 SWITCH
	JRST	SLCOM		;JOIN COMMON CODE


SLIST:	SETZM	SWTCH7		;CLEAR 77 SWITCH
SLCOM:	STKVAR	<CNT,INC,OFFS,SAVEL> ;ALLOCATE LOCAL VARIABLES
	MOVE	T3,@%LTYPE	;Get arg type bits
	TXNE	T3,ARGTYP	;IMMEDIATE MODE (TYPE=0)?
	 JRST	SLNIM		;NO. GO GET VALUE
	HRRZI	T1,(T1)		;CHOP EXTRANEOUS BITS OFF
	JRST	SLCOM2
SLNIM:	HRRE	T1,(T1)		;GET COUNT
SLCOM2:	JUMPG	T1,SLNZ		;POSITIVE COUNT OK
	SKIPN	SWTCH7		;77 PROGRAM?
	 MOVEI	T1,1		;NO. 1-TRIP DO COUNT
SLNZ:	MOVEM	T1,CNT		;STORE COUNT

	ADDI	L,1		;Point to increment
	MOVE	T1,(L)		;Get value
	MOVE	T3,@%LTYPE	;Get arg type bits
	TXNE	T3,ARGTYP	;IMMEDIATE MODE?
	  MOVE	T1,(T1)		;NO, GET VALUE
	HRREM	T1,INC		;STORE INCREMENT
	HRRE	T4,T1		;SAVE FOR RETURN
	SUBI	L,1		;Point back to SLIST base.

	MOVE	T0,FLAGS(D)
	TXNN	T0,D%UNF	;UNFORMATTED?
	 JRST	NOTUNF		;NO
	TRNE	T1,-2		;YES, IS INCREMENT +1?
	  JRST	SLP0		;NO, DO ONE-BY-ONE ALGORITHM
NOTUNF:	PUSH	P,L
	ADDI	L,3		;Word after array address
	MOVE	T3,@%LTYPE	;Get KWD and TYPE bits
	POP	P,L
	MOVE	T1,3(L)		;GET WORD AFTER ARRAY ADDRESS
	TXNN	T3,ARGKWD	;IS IT ANOTHER ARRAY?
	  JUMPN	T1,SLP0		;YES, MUST DO ONE-BY-ONE THING

	MOVE	T1,2(L)		;SINGLE ARRAY WITH INC +1, GET ADDRESS
	PUSH	P,L
	ADDI	L,2
	LDB	T2,[POINTR @%LTYPE,ARGTYP]	;AND DATATYPE
	POP	P,L
	MOVE	T5,%SIZTB(T2)	;GET THE ELEMENT SIZE
	MOVE	T3,CNT		;AND NUMBER OF ELEMENTS
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;UNFORMATTED I/O
	 ASH	T3,-1(T5)	;YES. TURN ELEMENTS INTO WORD COUNT
	ASH	T4,-1(T5)	;GET INCR IN WORDS
	UNSTK			;DISCARD STACK VARIABLES
	ADDI	L,3		;SKIP OVER SLIST
	PJRST	%DOIO		;GO DO WHOLE ARRAY

SLP0:	SETZM	OFFS		;INITIALIZE OFFSET
	XMOVEI	L,2(L)		;POINT TO FIRST ARRAY ADDRESS
	MOVEM	L,SAVEL		;SAVE FOR LOOP THROUGH ALL ARRAYS

SLP1:	MOVE	L,SAVEL		;RESET L TO START OF SLIST
SLP:	MOVE	T1,(L)		;GET AN ARRAY BASE ADDRESS
	MOVE	T3,@%LTYPE
	TXNE	T3,ARGKWD	;IS IT AN ARRAY ADDRESS?
	  JRST	SLPE		;NO, END OF LOOP
	JUMPE	T1,SLPE		;ZERO IS END OF LIST, NOT VALID ADDRESS
	LDB	T2,[POINTR @%LTYPE,ARGTYP] ;GET DATA TYPE OF ARRAY
	MOVE	T3,OFFS		;GET OFFSET INTO ARRAY
	IMUL	T3,%SIZTB(T2)	;TURN ELEMENTS INTO WORDS
	ADDI	T1,(T3)		;ADD OFFSET TO BASE ADDRESS
	MOVEI	T3,1		;SET # ELEMENTS TO 1
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;UNFORMATTED I/O
	 MOVE	T3,%SIZTB(T2)	;YES. SET TO LENGTH
	PUSHJ	P,%DOIO		;I/O THE ARRAY ELEMENT

	AOJA	L,SLP		;BUMP TO NEXT ARRAY ADDRESS, CONTINUE

SLPE:	MOVE	T1,INC		;GET INCREMENT
	ADDM	T1,OFFS		;BUMP OFFSET
	SOSLE	CNT		;DECREMENT COUNT
	  JRST	SLP1		;NOT YET ZERO, CONTINUE I/O

	UNSTK			;GET RID OF LOCAL STORAGE
	POPJ	P,		;END OF SLIST
ELST77:	SETOM	SWTCH7		;SET 77 SWITCH
	JRST	ELCOM		;JOIN COMMON CODE

ELIST:	SETZM	SWTCH7		;CLEAR 77 SWITCH
ELCOM:	STKVAR	<CNT,INC,OFFS,SAVEL> ;ALLOCATE LOCAL VARIABLES
	MOVE	T3,@%LTYPE
	TXNE	T3,ARGTYP	;IMMEDIATE MODE?
	 JRST	ELNIM		;NO. GO GET VALUE
	HRRZI	T1,(T1)		;TOSS OTHER BITS
	JRST	ELCOM2
ELNIM:	HRRE	T1,(T1)		;GET VALUE
ELCOM2:	JUMPG	T1,ELNZ		;POSITIVE COUNT
	SKIPN	SWTCH7		;77 PROGRAM?
	 MOVEI	T1,1		;NO. 1-TRIP DO COUNT
ELNZ:	MOVEM	T1,CNT		;STORE COUNT

	SETZ	T4,		;CLEAR INCR WORD FOR FORMATTED I/O
	SETZM	OFFS		;CLEAR OFFSET
	XMOVEI	L,1(L)		;POINT TO FIRST INCREMENT/ADDRESS PAIR
	MOVEM	L,SAVEL		;SAVE FOR LOOP

ELP1:	MOVE	L,SAVEL		;RESET L
ELP:	MOVE	T1,(L)		;GET AN INCREMENT
	MOVE	T3,@%LTYPE	;Get arg type bits
	TXNE	T3,ARGKWD	;CHECK FOR 0 KEYWORD FIELD
	  JRST	ELPE		;NONZERO KEYWORD, END OF LOOP
	JUMPE	T1,ELPE		;ZERO IS END OF LIST
	TXNE	T3,ARGTYP	;IMMEDIATE MODE CONSTANT?
	  MOVE	T1,(T1)		;NO, GET VALUE
	IMUL	T1,OFFS		;GET OFFSET INTO ARRAY

	PUSH	P,L
	ADDI	L,1
	LDB	T2,[POINTR @%LTYPE,ARGTYP]	;GET ARG TYPE
	POP	P,L
	IMUL	T1,%SIZTB(T2)	;MULTIPLY OFFSET BY ELEMENT SIZE
	ADD	T1,1(L)		;ADD BASE ADDRESS TO OFFSET
	MOVEI	T3,1		;1 ENTRY
	MOVE	T0,FLAGS(D)
	TXNE	T0,D%UNF	;BUT IF UNFORMATTED
	 MOVE	T3,%SIZTB(T2)	;SET ARRAY LENGTH
	PUSHJ	P,%DOIO		;I/O THE ELEMENT

	ADDI	L,2		;BUMP TO NEXT INCREMENT/ADDRESS PAIR, CONTINUE
	JRST	ELP

ELPE:	AOS	OFFS		;INCREMENT OFFSET
	SOSLE	CNT		;DECREMENT COUNT
	  JRST	ELP1		;IF NOT YET ZERO, CONTINUE

	UNSTK
	POPJ	P,		;END OF ELIST
;COROUTINE TO GET THE NEXT ITEM IN THE I/O LIST
;ARGS:	 NONE
;RETURN: T1 = ADDRESS
;	 T2 = DATATYPE
;	 T3 = LENGTH, WORDS
;REQUIRES L UNCHANGED SINCE LAST CALL

%GTIOX:	PUSHJ	P,%SAVE4	;SAVE P1-P4
%GETIO:	EXCH	P,OTHERP
	POPJ	P,


;COROUTINE TO PROCESS THE NEXT ITEM IN THE I/O LIST
;ARG:	 T1 = (30-BIT) ADDRESS
;	 T2 = DATATYPE
;	 T3 = LENGTH, WORDS

%DOIO:	EXCH	P,OTHERP
	POPJ	P,
;ROUTINE TO INITIALIZE COROUTINES
;ARG: T1 = INPUT ROUTINE,,OUTPUT ROUTINE

%SIO:	MOVEM	P,OTHERP	;SAVE STACK PNTR
	MOVE	P,[IOWD LIOPDL,IOPDL]
	SETZM	IO.ADR##	;CLEAR SPECIAL CROCK FLAG
	MOVE	T0,FLAGS(D)
	TXZ	T0,D%EOI	;AND OTHER SPECIAL CROCK FLAG
	TXNN	T0,D%IO		;OUTPUT?
	  HLRZ	T1,T1		;NO, GET INPUT ROUTINE ADDRESS
	MOVEM	T0,FLAGS(D)	;Store (possibly updated) DDB flags
	TLZ	T1,-1		;Make sure this is a local address
	PUSHJ	P,(T1)		;DO I/O
	EXCH	P,OTHERP
	PJRST	%FIO		;GO FINISH UP I/O

	SEGMENT	DATA
GETADR:	BLOCK	1
DOADR:	BLOCK	1
%UDBAD: BLOCK	1		;DDB ADDRESS "STACK"
SWTCH7:	BLOCK	1		;THE ANSI-77 SWITCH

OTHERP:	BLOCK	1		;OTHER STACK

LIOPDL==100
IOPDL:	BLOCK	LIOPDL


	SEGMENT	CODE
	SUBTTL	OVNUM

;ROUTINE TO FIND LINK NUMBER GIVEN AN ADDRESS
;ARGS:	 T1 = ADDR
;RETURN: T1 = LINK NUMBER,,ADDR
; Unless extended addressing: Then, T1 will not be changed.

;ASSUMPTIONS:
;THE CONTROL SECTION IS THE LAST THING IN EACH LINK.
;LINKS ARE DISJOINT AND ARE STRUNG TOGETHER IN INCREASING ORDER OF ADDRESS.
;CODE AND DATA ARE LOADED CONTIGUOUSLY WITHIN A LINK, SEPARATE FROM OTHER
;LINKS.

;CONTROL SECTION OFFSETS (FROM OVRLAY.MAC)
	CS.NUM==2		;LINK NUMBER
	CS.FPT==4		;FORWARD POINTER TO NEXT CONTROL SECTION

%OVNUM:	SKIPN	T2,.JBOVL	;GET ROOT LINK CONTROL SECTION ADDRESS
	  POPJ	P,		;NONE, LINK NUMBER IS 0

;Note: At this point, we can assume that FOROTS is running in section 0
;      because LINK is not supposed to allow overlays in extended sections.
;    Thus the address in T1 is only 18 bits.

OVLP:	HRL	T1,CS.NUM(T2)	;PC IS IN THIS LINK OR SOME FOLLOWING ONE
	CAIE	T2,0		;IF NO FOLLOWING LINK, DONE
	CAIL	T2,(T1)		;DOES LINK START BEFOE SEARCH ADDRESS?
	  POPJ	P,		;YES, LINK NUMBER IS IN T1, DONE
	HRRZ	T2,CS.FPT(T2)	;GET POINTER TO FOLLOWING LINK
	JRST	OVLP		;SEARCH ON
	PURGE	$SEG$
	END