	.TITLE	GETPUT - RECORD I/O FOR TECOIO

	.IDENT	"X0203"

;
; COPYRIGHT (C) 1976 BY DIGITAL EQUIPMENT CORPORATION,
; MAYNARD, MASSACHUSETTS
;
; THIS SOFTWARE IS FURNISHED UNDER A LICENSE FOR USE ONLY ON A
; SINGLE  COMPUTER  SYSTEM AND MAY BE COPIED ONLY WITH THE IN-
; CLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS  SOFTWARE,  OR
; ANY  OTHER  COPIES THEREOF, MAY NOT BE PROVIDED OR OTHERWISE
; MADE AVAILABLE TO ANY OTHER PERSON EXCEPT FOR  USE  ON  SUCH
; SYSTEM  AND TO ONE WHO AGREES TO THESE LICENSE TERMS.  TITLE
; TO AND OWNERSHIP OF THE SOFTWARE SHALL AT ALL  TIMES  REMAIN
; IN DIGITAL.
;
; THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE  WITH-
; OUT  NOTICE  AND  SHOULD NOT BE CONSTRUED AS A COMMITMENT BY
; DIGITAL EQUIPMENT CORPORATION.
;
; DIGITAL EQUIPMENT CORPORATION ASSUMES NO RESPONSIBILITY  FOR
; THE USE OR RELIABILITY OF ITS SOFTWARE ON EQUIPMENT WHICH IS
; NOT SUPPLIED BY DIGITAL.
;
; ANDREW C. GOLDSTEIN  12-FEB-78  22:30
; MARK H. BRAMHALL     29-MAR-79  13:35

	.MCALL	GET$,PUT$

.SBTTL	GETBUF -- GET BUFFER FROM INPUT

;+
;
; *** - GETBUF	GET BUFFER FROM INPUT
;
; THIS ROUTINE READS THE INPUT FILE INTO THE TEXT BUFFER. WHEN CALLED,
; IT READS UNTIL EITHER:
;
;	1) THE BUFFER IS NEARLY FULL.
;	2) A FORM FEED IS ENCOUNTERED.
;	3) END OF FILE IS ENCOUNTERED.
;
; INPUTS:
;
;	R0 =	POINTER TO AREA TO READ INTO
;	R1 =	BUFFER SPACE AVAILABLE (BYTES)
;	R2 =	AMOUNT OF SPACE TO LEAVE FREE
;
; OUTPUTS:
;
;	EOFLAG(R5) = -1 IF END OF FILE WAS REACHED
;		   =  0 OTHERWISE
;	FFFLAG(R5) = -1 IF READ TERMINATED WITH A FORM FEED
;		   =  0 OTHERWISE
;	ZZ(R5)     = UPDATED END OF BUFFER
;
; GETBUF READS STANDARD RSX VARIABLE LENGTH RECORDS FROM THE INPUT
; FILE. IF THE FILE HAS EITHER IMPLIED OR FORTRAN CARRIAGE CONTROL,
; CARRIAGE RETURN AND LINE FEED ARE INSERTED INTO THE BUFFER AFTER
; EACH RECORD PROVIDED THE RECORD DOESN'T END WITH ESCAPE, LINE FEED,
; VERTICAL TAB, OR FORM FEED. IF THE RECORD ENDS WITH FORM FEED, IT IS NOT
; ENTERED INTO THE BUFFER AND THE READ IS TERMINATED.
; AFTER EACH RECORD HAS BEEN READ, IF THERE ARE
; FEWER THAN R2 CHARACTERS IN THE BUFFER, THE READ IS ALSO TERMINATED.
;
;-

	.PSECT	PURE,RO,D

GETDSP:	.WORD	NOCRLF-NOCRLF	; ESCAPE
	.WORD	ADCRLF-NOCRLF	; CR
	.WORD	GOTFF-NOCRLF	; FF
	.WORD	NOCRLF-NOCRLF	; VT

	.PSECT	CODE,RO,I

PRNREC:	ADD	#2,R4		; DON'T COUNT ANY CR/LF
	MOVB	F.SEQN(R0),R1	; GET THE "PREFIX"
	BEQ	60$		; NONE
	BPL	10$		; ONE, A LF COUNT
	MOV	R1,R2		; COPY THE CTRL CHAR
	BIC	#^C<37>,R2	; MAKE IT A CTRL CHAR
	MOV	#1,R1		; SET COUNT AS 1
	BR	20$		; AND GO DO IT

10$:	TSTB	CHRFLG		; FIRST TIME?
	BNE	20$		; NO
	DEC	R1		; YES, DO ONE LESS LF INITIALLY
	BEQ	60$		; NOTHING DO TO
20$:	CMP	R1,R4		; ENOUGH SPACE LEFT?
	BHI	60$		; NOPE, MUST PUNT...
	SUB	R1,R4		; YEP, BUT LESS NOW
	MOV	R1,-(SP)	; SAVE COUNT
	MOV	R2,-(SP)	; SAVE CTRL CHAR
	MOV	F.NRBD(R0),R2	; GET SIZE OF RECORD
	BEQ	40$		; NULL...
	ADD	R3,R1		; BUILD NEW EOR POINTER
30$:	MOVB	-(R3),-(R1)	; MOVE RECORD UP
	SOB	R2,30$		; LOOP FOR WHOLE RECORD
40$:	MOV	(SP)+,R2	; RESTORE CTRL CHAR
	MOV	(SP)+,R1	; RESTORE COUNT
50$:	MOVB	R2,(R3)+	; STORE CTRL CHAR
	SOB	R1,50$		; ENOUGH TIMES
	ADD	F.NRBD(R0),R3	; FORM CORRECT NEW EOR POINTER
60$:	MOVB	#1,CHRFLG	; NOT THE FIRST TIME ANYMORE
	MOV	#LF,R2		; ASSUME LF COUNT AGAIN
	MOVB	F.SEQN+1(R0),R1	; GET THE "POSTFIX"
	BEQ	80$		; NONE, DONE
	BPL	70$		; ONE, A LF COUNT
	MOV	R1,R2		; COPY THE CTRL CHAR
	BIC	#^C<37>,R2	; MAKE IT A CTRL CHAR
	MOV	#1,R1		; SET COUNT AS 1
70$:	TST	R4		; ROOM?
	BEQ	80$		; NOPE, PUNT...
	DEC	R4		; YEP, BUT LESS
	MOVB	R2,(R3)+	; MOVE CTRL CHAR
	SOB	R1,70$		; AND LOOP
	CMP	R2,#CR		; DID WE INSERT A TRAILING CR?
	BNE	80$		; NOPE, DONE
	NEGB	CHRFLG		; YEP, NEED LF IF EOF COMES UP
80$:	JMP	IBFCHK		; CONTINUE

	.ENABL	LSB

GETBUF::
	SAVE
	TST	INFDB+F.BDB	; TEST FOR OPEN FILE AVAILABLE.
	BNE	10$
	ERROR	NFI,<"No file for input">

10$:	CLR	FFFLAG(R5)	; CLEAR FORM FEED FLAG FOR RETURN
	TST	EOFLAG(R5)	; ALREADY AT END-OF-FILE?
	BEQ	20$		; NO
	RETURN			; YES, JUST EXIT

20$:	MOV	R1,R4		; COPY AVAILABLE SPACE SIZE
	MOV	#INFDB,R0	; GET FDB POINTER
	CALL	.MARK		; SAVE INPUT FILE POSITION
	MOV	R3,-(SP)	; STASH AWAY R3
	MOV	SR0+2(SP),R3	; GET THE WORKING BUFFER POINTER

	.DSABL	LSB

; HERE TO GET NEXT INPUT RECORD MOVED INTO TECO'S TEXT BUFFER.

NEXREC:	SUB	#2,R4		; LEAVE ROOM FOR CR/LF
	GET$	R0,R3,R4	; GET THE RECORD
	BCS	INERR		; SOME INPUT ERROR.
	ADD	F.NRBD(R0),R3	; INCREMENT THE CHARACTER POINTER
	SUB	F.NRBD(R0),R4	; DECREMENT SPACE COUNT
	MOV	R1,-(SP)	; SAVE R1
	MOV	R2,-(SP)	; AND SAVE R2
	MOV	#LF,R2		; PRE-LOAD A LINE FEED INTO HERE
	BITB	F.RATT(R0),#FD.PRN ; PRINT FORMAT?
	BNE	PRNREC		; YEP
	TST	F.NRBD(R0)	; CHECK SIZE OF RECORD
	BEQ	ADCRLF		; NULL...
	TSTB	INBP2		; BP2 MODE?
	BEQ	30$		; NOPE
	CMPB	-1(R3),#'&	; TRAILING "&"?
	BNE	30$		; NOPE
10$:	DEC	R3		; YEP, BACK UP THE POINTER
20$:	DEC	F.NRBD(R0)	; 1 LESS IN RECORD NOW
	BEQ	ADCRLF		; RECORD IS NOW NULL...
	CMPB	-(R3),#SPACE	; TRAILING SPACE?
	BEQ	20$		; YEP, DELETE IT
	CMPB	(R3)+,#TAB	; TRAILING TAB?
	BEQ	10$		; YEP, DELETE THAT ALSO
30$:	MOVB	-1(R3),R1	; GET THE LAST CHAR OF RECORD
	CMP	R1,R2		; IS IT LINE FEED?
	BLT	ADCRLF		; NO, TOO LOW OR NEGATIVE
	BEQ	NOCRLF		; YES
	MOV	#GETABL,R2	; GET THE TERMINATOR LIST (AM, CR, FF, VT)
40$:	CMP	R1,(R2)+	; IN THE LIST?
	BLO	40$		; NO, AND KEEP LOOKING...
	BEQ	GETTRM		; YES; ELSE TOO HIGH, STOP LOOKING
ADCRLF:	BITB	F.RATT(R0),#FD.CR!FD.FTN ; CHECK FOR IMPLIED OR FORTRAN CCL
	BEQ	NOCRLF		; NEITHER
	MOVB	#CR,(R3)+	; INSERT CARRIAGE RETURN
	MOVB	#LF,(R3)+	; AND LINE FEED
	BR	IBFCHK		; CONTINUE

GETTRM:	ADD	GETDSP-<GETABL+2>(R2),PC ; YES, DISPATCH ON IT
NOCRLF:	ADD	#2,R4		; CR-LF ALREADY PRESENT - PUT BACK THE SPACE
IBFCHK:	MOV	(SP)+,R2	; RESTORE R2
	MOV	(SP)+,R1	; AND RESTORE R1
	CMP	R4,SR2+2(SP)	; SEE HOW MUCH SPACE IS LEFT
	BLO	IBFDON		; IF LESS THAN THRESHOLD, QUIT
	BR	NEXREC		; GET NEXT RECORD.

; FORM FEED ENCOUNTERED IN INPUT

GOTFF:	DEC	FFFLAG(R5)	; SET FF FLAG
	DEC	R3		; BUT DON'T ENTER FF IN BUFFER
	CMP	(SP)+,(SP)+	; POP SAVED R1/R2

; END OF INPUT: EITHER WE HIT A FORM FEED OR END OF FILE, OR THE BUFFER
; IS FULL. COMPUTE NUMBER OF CHARACTERS READ AND RETURN.

IBFDON:	SUB	TXSTOR(R5),R3	; COMPUTE TOTAL BYTE COUNT IN BUFFER
	MOV	R3,ZZ(R5)	; SET NEW END OF BUFFER
	TST	(SP)+		; CLEAN THE STACK
	RETURN			; AND OUT

; TO HERE ON RANDOM INPUT ERRORS.

INERR:	MOV	F.ERR(R0),R4	; PICK UP ERROR CODE
	CMPB	R4,#IE.EOF	; SEE IF WE HIT END OF FILE
	BNE	20$		; IF NOT, GET OUT
10$:	MOV	#-1,EOFLAG(R5)	; YES - SET END OF FILE FLAG
	TSTB	CHRFLG		; NEED TRAILING LF ON EOF?
	BPL	IBFDON		; NOPE, JUST EXIT
	MOVB	#LF,(R3)+	; YEP, SO DO IT
	BR	IBFDON		; AND EXIT

20$:	BITB	#FA.SHR,F.FACC(R0) ; SEE IF FILE IS OPEN SHARED
	BNE	10$		; BRANCH IF YES - IGNORE ALL ERRORS
	MOV	(SP)+,R3	; RESTORE R4 FROM MARK
	CALL	.POINT		; RESTORE FILE POSITION TO ALLOW RECOVERY
	MOV	R4,F.ERR(R0)	; RESTORE ERROR CODE
	JMP	FDBERR		; AND REPORT ERROR

.SBTTL	PUTBUF -- PUT BUFFER IN OUTPUT FILE

;+
;
; *** - PUTBUF	PUT BUFFER IN OUTPUT FILE
;
; THIS ROUTINE OUTPUTS THE DESIGNATED PORTION OF THE BUFFER TO
; THE OUTPUT FILE CURRENTLY OPEN. THE BUFFER IS SEARCHED FOR
; RECORD DELIMITERS WHICH ARE LINE FEED, VERTICAL TAB, FORM FEED,
; AND ESCAPE. IF THE FILE HAS NULL CARRIAGE CONTROL, THE DELIMITER
; IS OUTPUT WITH THE RECORD; OTHERWISE IF THE DELIMINTER IS LINE
; FEED AND IT IS PRECEEDED BY A CARRIAGE RETURN, THEN THE CR-LF
; PAIR IS NOT OUTPUT.
;
; INPUTS:
;
;	R0 =	POINTER TO START OF BUFFER
;	R1 =	NUMBER OF CHARACTERS TO OUTPUT
;	R2 = -1	IF A FORM FEED IS TO BE APPENDED TO THE OUTPUT
;	      0	IF OUTPUT IS TO BE DONE AS IS
;
; OUTPUTS:
;
;	NONE
;
; ALL REGISTERS ARE PRESERVED.
;
;-

	.ENABL	LSB

PUTBUF::
	SAVE
	TST	OUTFDB+F.BDB	; IS THERE OPEN  OUTPUT FILE?
	BNE	10$		; IF YES, PROCEED
	ERROR	NFO,<"No file for output">

10$:	MOV	R0,R2		; USE FOR POINTER TO GET CHARACTERS FROM BUFFER
	MOV	R5,-(SP)	; ALSO SAVE R5
	MOV	#OUTFDB,R0	; SET UP FDB ADDRESS

	.DSABL	LSB

CHRSCN:	CLR	R3		; ZERO PREVIOUS CHARACTER SEEN
	CLR	-(SP)		; ZERO # OF CHARACTERS TO REMOVE INITIALLY
	MOV	R2,R4		; SET TO START OF NEXT RECORD
10$:	MOV	R3,R5		; SAVE LAST CHARACTER SEEN
	DEC	R1		; COUNT OFF A CHARACTER
	BLT	OBFDON		; NO -- OUTPUT THE LAST THEN
	MOVB	(R2)+,R3	; GET NEXT CHARACTER FROM CALLER'S BUFFER
	CMPB	R3,#AM		; HIGHER THAN ESCAPE?
	BHI	10$		; YES, KEEP SCANNING
	BEQ	20$		; NO, ESCAPE, TERMINATE
	CMPB	R3,#FF		; HIGHER THAN FF?
	BHI	10$		; YES, KEEP SCANNING
	CMPB	R3,#LF		; LOWER THAN LF?
	BLO	10$		; YES, KEEP SCANNING
	BNE	20$		; TERMINATED WITH VT OR FF
	CMPB	R5,#CR		; LF TERMINATOR, IS PREVIOUS A CR?
	BNE	20$		; NOPE, DON'T REMOVE ANYTHING
	BITB	F.RATT(R0),#FD.CR!FD.FTN ; CHECK FOR IMPLIED OR FORTRAN CCL
	BEQ	20$		; NEITHER
	MOV	#2,(SP)		; SET LINE LENGTH ADJUSTMENT
20$:	CLR	R5		; GUESS AT NORMAL OUTPUT
	TSTB	OUBP2		; BP2 MODE?
	BEQ	40$		; NOPE
	TST	R1		; YEP, MORE TO COME?
	BLE	50$		; NO
	CMPB	(R2),#'0	; YES, NEXT A DIGIT?
	BLO	30$		; NO
	CMPB	(R2),#'9	; MIGHT BE...
	BLOS	40$		; AND IT IS TOO
30$:	MOV	(PC)+,R5	; SET FOR TRAILING SPACE/"&"
	 .BYTE	40,'&
40$:	SUB	(SP),R2		; REMOVE CR-LF WHEN APPROPRIATE
	CALL	PUTOUT		; OUTPUT THE RECORD FROM R4 TO R2
	ADD	(SP)+,R2	; FIX END OF RECORD POINTER
	BR	CHRSCN		; GO LOOK AT NEXT RECORD.

50$:	TST	SR2+4(SP)	; WILL A FORM FEED FOLLOW?
	BEQ	40$		; NOPE
	BR	30$		; YEP

; HERE WHEN CALLER'S BUFFER IS COMPLETE

OBFDON:	CLR	R5		; GUESS AT NO FINAL FF
	TST	SR2+4(SP)	; REALLY DO A FINAL FF?
	BNE	10$		; YES
	CMP	R2,R4		; ANY CHARACTERS IN CURRENT OUTPUT RECORD?
	BEQ	30$		; NO -- DO NOT OUTPUT THEN
	BR	20$		; YES -- SO OUTPUT THEM

10$:	MOV	#FF,R5		; YES, SET TO DO A FINAL FF
20$:	CALL	PUTOUT		; DO THE LAST PUT
30$:	TST	(SP)+		; DUMP A STACK ITEM
	MOV	(SP)+,R5	; RESTORE R5 NOW
PUTRET:	RETURN			; RETURN

; SUBROUTINE TO DO ALL PUTS

PUTOUT:	MOVB	(R2),-(SP)	; SAVE REAL LAST & POINTER TO REAL LAST
	MOV	R2,-(SP)
	MOVB	R5,(R2)+	; SET NEW LAST
	BNE	10$		; IT EXISTS
	DEC	R2		; ELSE REMOVE IT
10$:	SWAB	R5		; SWITCH FOR NEXT NEW LAST
	MOV	R2,-(SP)	; SAVE POINTER TO NEW LAST & NEW LAST
	MOVB	(R2),-(SP)
	MOVB	R5,(R2)+	; SET NEW LAST
	BNE	20$		; IT EXISTS
	DEC	R2		; ELSE REMOVE IT
20$:	MOV	R2,R3		; GET POINTER PAST END OF RECORD
	SUB	R4,R3		; THIS IS THE SIZE OF THE RECORD
	PUT$	R0,R4,R3	; PUT IT OUT
	MOVB	(SP)+,@(SP)+	; RESTORE THE NEW LAST
	MOV	(SP)+,R2	; RESTORE THE POINTER TO REAL LAST & REAL LAST
	MOVB	(SP)+,(R2)
	BCC	PUTRET		; O.K., RETURN
	JMP	FDBERR		; REPORT ERROR AND EXIT



	.END

