Google
 

Trailing-Edge - PDP-10 Archives - integ_tools_tops20_v7_30-apr-86_dumper - tools/10backup_v2_1/bio.mar
There are 5 other files named bio.mar in the archive. Click here to see a list.
	.title	BIO	10BACKUP IO Routines
	.ident	'BIO v2.1'
;
;
; This module is part of 10BACKUP - a program to read DECsystem-10
; BACKUP tapes in INTERCHANGE mode on a VAX.
;
; The routines in this module do the tape input and the file output
; under control of the other modules.
;
; The source modules that make up the 10BACKUP program are:-
;
;	10BACKUP.BAS	the main line program.
;	BIO.MAR		contains tape and file IO routines.
;	BUR.MAR		is a set of macro utility routines.
;	C36.MAR		contains 36 bit conversion routines.
;	BMS.MSG		contains the error message definitions.
;	10BACKUP.RNH	Runoff input to build the help library.
;
;
; The first BIO routines are for tape input:-
;
; On tape initialization the tape device is checked to see if
; it is a foreign mounted tape. If it is the QIO's are used for
; all I/O otherwise RMS is called. This enables the program to
; work efficiently with tape devices and yet be able to take its
; input from a file if necessary.
;
; When QIO's are used a number of buffers are set up to allow the
; program to buffer its input. This seems to work OK although the
; program tends to be CPU bound on our overloaded system.
;
; The routines for tape input are:-
;
;	bio_tape_init		To initialize tape access.
;	bio_tape_skip		To skip over tape marks (end of tape files).
;	bio_tape_rewind		To rewind the tape to the beginning.
;	bio_tape_close		To finish accessing the tape.
;	bio_tape_free_buff	To free current buffer for further read aheads.
;	bio_tape_read		To read a tape block.
;
; It is possible that some of these routines may be useful to someone
; else setting up another program which has to read from tapes as well.
;
;
;
	$dvidef
;
buffers = 4				;Number of buffers to use.
max_reclen = 2720			;Maximum block length.
;
;
	.psect	buffer,abs,quad ;Layout of a single buffer.
;
bufque:	.blkq	1		;Buffer que entry.
bufsts:	.blkl	1		;Buffer status code.
buflen:	.blkl	1		;Buffer data length.
bufdat:	.blkb	max_reclen	;Buffer data area.
	.align	quad
bufsiz:
;
;
;
	.psect	data_ro,pic,shr,nowrt,noexe,long
;
tape_itmlst:
	.word	4,dvi$_devclass		;Item code and length.
	.address tape_devclass		;Item buffer address.
	.long	0			;No return length address.
	.word	4,dvi$_devchar		;Item code and length.
	.address tape_devchar		;Item buffer address.
	.long	0			;No return length address.
	.long	0			;End of item list.
;
;
;
	.psect	data,rd,wrt,quad
;
	.align	quad
tape_que:	.address tape_que,tape_que	;Input queue header.
proc_que:	.address proc_que,proc_que	;Process queue header.
tape_mode:	.long	0		;Mode of input.
tape_buffer:	.long	0		;Current process buffer.
tape_enable:	.long	0		;Flag indicating reads may continue.
tape_working:	.long	0		;Flag indicating read in operation.
tape_mark:	.long	0		;Tape mark seen flag.
tape_devclass:	.long	0		;Class of input device.
tape_devchar:	.long	0		;Characteristics of input device.
tape_chan:	.blkw	1		;Input QIO channel.
tape_iosb:	.blkq	1		;Input QIO status block.
tape_qio:	$qio	efn=5, -
			chan=0, -
			func=io$_readlblk, -
			iosb=tape_iosb, -
			astadr=tape_ast, -
			p1=0, -
			p2=max_reclen		;QIO parameters for block read.
;
;
	.align	quad
tape_buffers:	.blkb	buffers * bufsiz	;Allocate QIO buffer pool.
;
;
tape_fab:	$fab				;FAB for RMS input.
tape_rab:	$rab	fab=tape_fab, -
			rop=rah, -
			usz=max_reclen, -
			ubf=tape_buffers	;RAB for RMS input.
;
;
;
;
;
; Initialize tape device ready for access.
; Check whether to use RMS or QIO's and then open the file
; or assign a channel to the device and get everything ready
; for reads.
;
; Called via:  status_code = BIO_TAPE_INIT( DEVICE )
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_init,^M<R2,R3,R4>

	movl	4(AP),R2		;Get address device name.
	$getdviw_s efn=#1, -
		devnam=(R2), -
		itmlst=tape_itmlst, -
		iosb=tape_iosb		;See if device name is valid.
	blbc	R0,10$			;No, assume we use RMS.
	blbc	tape_iosb,10$		;If error in info assume RMS.
	cmpl	tape_devclass,#dc$_tape ;Is the device a tape?
	bneq	10$			;No, go use RMS.
	bbc	#dev$v_for,tape_devchar,10$ ;Use RMS unless mounted Foreign.
	brb	50$			;Alright, go use QIOs.
;
;
10$:	movb	#1,tape_mode		;Remember we are using RMS I/O.
	movb	(R2),fab$b_fns+tape_fab ;Set file name length.
	movl	4(R2),fab$l_fna+tape_fab ;Set file name address.
	$open	fab=tape_fab		;Open the input file.
	blbc	R0,20$			;Check for errors.
	$connect rab=tape_rab		;Connect input record stream.
20$:	brw	90$			;All finished.
;
;
50$:	clrb	tape_mode		;Remember we are using QIO's.
	$assign_s devnam=(R2), -
		chan=tape_chan		;Assign a channel to the device.
	blbc	R0,90$			;Check for errors.
	movzwl	tape_chan,tape_qio+qio$_chan ;Put channel in QIO block.
	movab	proc_que,proc_que	;Initialise process queue.
	movab	proc_que,proc_que+4
	movab	tape_que,tape_que	;Initialise input queue.
	movab	tape_que,tape_que+4
	movzbl	#buffers,R4		;Set up buffer counter.
	movab	tape_buffers,R3		;Set up buffer address.

60$:	insque	(R3),tape_que		;Put buffer into read queue.
	addl	#bufsiz,R3		;Point to next buffer.
	sobgtr	R4,60$			;Do all buffers.
	clrl	tape_buffer		;There is no current buffer.
	clrl	tape_mark		;No tape marks seen.
	clrl	tape_working		;Nothing happening yet.
	movzbl	#1,tape_enable		;Enable tape reads.
;
;
90$:	ret				;Return to caller with status.
;
;
;
;
;
; Skip over tape tape marks.
;
; Called via:  status_code = BIO_TAPE_SKIP( COUNT )
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_skip,^M<R2,R3,R4>

	movzbl	#ss$_normal,R0		;Assume normal status.
	movl	@4(AP),R3		;Get skip count.
	bneq	10$			;Skip unless zero.
	brw	90$			;Assume we are finished if zero.
10$:	blbc	tape_mode,50$		;Go use QIOs if appropriate.
;
;
20$:	$get	rab=tape_rab		;Get a record.
	blbc	R0,30$			;Finished if errors.
	tstw	rab$w_rsz+tape_rab	;Does record have a length?
	bgtr	20$			;Yes, go get another.
	sobgtr	R3,20$			;Skip each file.
30$:	brb	90$			;All finished.
;
;
50$:	bsbw	tape_stop		;Ensure all I/O has stopped.
60$:	remque	@proc_que,R4		;Get address of buffer.
	bvs	70$			;Got a buffer?
	insque	(R4),tape_que		;Yes, put into input queue.
	movl	bufsts(R4),R0		;Get error status.
	blbc	R0,70$			;Check for errors.
	tstl	buflen(R4)		;Tape mark?
	bneq	60$			;No, go look in next buffer.
	decl	R3			;Decrement skip count.
	bneq	60$			;Have we finished skipping?
	brb	90$			;Already skipped to the right place.
;
70$:	$qiow_s efn=#1, -
		chan=tape_chan, -
		func=#io$_skipfile, -
		iosb=tape_iosb, -
		p1=(R3)			;Ask the driver to do the skip.
	cmpl	R0,#ss$_endofvolume	;Did driver hit end of volume.
	bneq	75$			;No, go test skip direction.
	movl	#rms$_eof,R0		;Indicate end of volume.
	brb	90$			;All finished.
;
75$:	clrl	tape_mark		;Assume we skipped to before a tape mark.
	tstl	R3			;Check direction of skip.
	blss	80$			;Was a skip backwards.
	movb	#1,tape_mark		;Remember positioned after tape mark.
80$:	movb	#1,tape_enable		;Enable further tape reads.
;
;
90$:	ret				;Return to caller with status.
;
;
;
;
;
;
; Rewind tape.
;
; Called via:  status_code = BIO_TAPE_REWIND
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_rewind,^M<R2,R3,R4>

	blbc	tape_mode,50$		;Go use QIOs if appropriate.
;
;
	$rewind	rab=tape_rab		;Rewind the record stream.
	blbc	R0,90$			;Finish if errors.
	movl	rab$l_sts+tape_rab,R0	;Get status.
	brb	90$			;All finished.
;
;
50$:	bsbw	tape_abort		;Abort current I/O stream.
	$qiow_s efn=#1, -
		chan=tape_chan, -
		func=#io$_rewind!io$m_nowait, -
		iosb=tape_iosb		;Rewind the tape.
	bsbw	chkerr			;Check for errors.
	movb	#1,tape_enable		;Enable further tape reads.
	clrb	tape_mark		;No tape mark (yet).
;
;
90$:	ret				;Return to caller with status.
;
;
;
;
;
;
;
; Finish with the tape.
;
; Called via:  status_code = BIO_TAPE_CLOSE
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_close,^M<R2,R3,R4>

	blbc	tape_mode,50$		;Go use QIOs if appropriate.
;
;
	$close	fab=tape_fab		;Close the tape file.
	brb	90$			;All finished.
;
;
50$:	bsbw	tape_abort		;Abort current I/O stream.
	$dassgn_s chan=tape_chan	;Deassign tape channel.
;
;
90$:	ret				;Return to caller with status.
;
;
;
;
;
;
; Release current QIO buffer for further read aheads.
;   If there is no current buffer then no problem.
;   If we don't get called then still no problem
;	(the next read will get release the buffer anyway).
;
; Called via:  CALL BIO_TAPE_FREE_BUFF
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_free_buff,^M<>

	blbs	tape_mode,50$		;Does not apply while using RMS.
	movl	tape_buffer,R1		;Get address of current buffer.
	beql	50$			;If none then skip.
	insque	(R1),@tape_que		;Put buffer into input que.
	clrl	tape_buffer		;No current buffer now.
;
50$:	ret				;Return to caller.
;
;
;
;
;
;
; Read a block from the tape.
;
; Called via:  status_code = BIO_TAPE_READ( BLOCK_LENGTH, BLOCK_ADDRESS )
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_tape_read,^M<R2,R3,R4>

	blbc	tape_mode,50$		;Go use QIOs if appropriate.
;
;
	$get	rab=tape_rab		;Get a record from the input stream.
	blbc	R0,10$			;Finish if errors.
	movl	fab$l_sts+tape_fab,R0	;Get final status.
	movzwl	rab$w_rsz+tape_rab,@4(AP) ;Pass back record length.
	movl	rab$l_ubf+tape_rab,@8(AP) ;Pass back data address.
10$:	brb	90$			;All finished.
;
;
50$:	movl	tape_buffer,R1		;Get address of current buffer.
	beql	60$			;If none then skip.
	insque	(R1),@tape_que		;Put buffer into input que.
	clrl	tape_buffer		;No current buffer now.
60$:	blbs	tape_working,70$	;Is tape already working?
	blbs	tape_enable,65$		;Is the tape enabled for reads.
	movab	proc_que,R0		;Get address of process queue.
	cmpl	proc_que,R0		;Is the process queue empty?
	bneq	70$			;No, do not restart reads yet.
	movb	#1,tape_enable		;Enable further reads.
65$:	movb	#1,tape_working		;Mark tape as working.
	bsbw	tape_start		;Initialise tape reads.
70$:	remque	@proc_que,R4		;Get address of buffer to process.
	bvc	80$			;Got a buffer?
	$waitfr_s efn=#5		;Wait for a tape read to finish.
	bsbw	chkerr			;Check for errors.
	brb	70$			;Try getting a buffer now.
;
80$:	movl	R4,tape_buffer		;Remember the buffer address.
	movl	bufsts(R4),R0		;Pass back status code.
	movl	buflen(R4),@4(AP)	;Pass back record length.
	movab	bufdat(R4),@8(AP)	;Pass back data address.
;
;
90$:	ret				;Return to caller with status.
;
;
;
;
; This is the QIO ast routine which is called at the completion
; of every QIO. If possible it sets another QIO going to read
; into the next input buffer before it finishes.
;
	.entry	tape_ast,^M<R4>	;Input AST routine.

	movzwl	tape_iosb,R0		;Get QIO status.
	movzwl	tape_iosb+2,R1		;Get QIO length.
	blbs	R0,20$			;Skip on if no errors.
	cmpw	R0,#ss$_endoffile	;Tape mark detected?
	beql	10$			;Yes, go deal with it.
	cmpw	R0,#ss$_endoftape	;Block read ok past tape end?
	bneq	20$			;No, some other problem.
	movzbl	#ss$_normal,R0		;Ignore being past tape marker.
	brb	20$			;Continue business as usual.
10$:	clrl	R1			;Zero bytes for tape mark.
	movl	#rms$_eof,R0		;Assume end of tape.
	blbs	tape_mark,30$		;Done if second tape mark.
	movzbl	#ss$_normal,R0		;Only first tape mark.
	movb	#1,tape_mark		;Remember the tape mark.
	brb	30$			;Have handled the tape mark.
20$:	clrb	tape_mark		;No tape mark this read.
;
30$:	remque	@tape_que,R4		;Remove entry from read queue.
	movl	R0,bufsts(R4)		;Save status.
	movl	R1,buflen(R4)		;Save data length.
	insque	(R4),@proc_que+4	;Put buffer into process queue.
;
	blbc	R0,40$			;Error on last read?
	blbc	tape_enable,50$		;Someone wants us to stop?
	bsbb	tape_start		;Initialise another read.
	brb	70$			;All done.
;
40$:	clrb	tape_enable		;Disable further reads.
50$:	clrb	tape_working		;Remember we have stopped.
;
70$:	ret				;Resume whatever.
;
;
;
; This is an internal subroutine to start a QIO if an input
; buffer is available for it.
;
tape_start:
	movl	tape_que,R1		;Find first free input buffer.
	movab	tape_que,R0		;Get header address.
	cmpl	R1,R0			;Is input queue empty?
	beql	40$			;Yes, give up.
	movab	bufdat(R1),tape_qio+qio$_p1;Set up address of buffer.
	$qio_g	tape_qio		;Issue an input QIO.
	bsbw	chkerr			;Check for errors.
	brb	50$			;All finished.
40$:	clrb	tape_working		;Cannot do a read.
50$:	rsb				;Return to caller.
;
;
;
; This is an internal routine to abort the current I/O stream.
; It stops current I/O and then releases all the process buffers.
;
tape_abort:
	bsbb	tape_stop		;Ensure I/O is stopped.
10$:	remque	@proc_que,R4		;Get address of a process buffer.
	bvs	20$			;Got a buffer?
	insque	(R4),tape_que		;Put all buffers into input queue.
	brb	10$			;Release all buffers.
20$:	rsb				;Return to caller.
;
;
;
; This is an internal routine to stop tape I/O. It disables the
; read enable flag and then waits for I/O to stop.
;
tape_stop:
	clrb	tape_enable		;Turn off tape I/O.
10$:	blbc	tape_working,20$	;Tape still reading?
	$waitfr_s efn=#5		;Wait for a tape read to finish.
	bsbw	chkerr			;Check for errors.
	brb	10$			;Try again.
20$:	movl	tape_buffer,R4		;Get address of current buffer.
	beql	30$			;Skip if none.
	insque	(R4),tape_que		;Put buffer into input queue.
	clrl	tape_buffer		;No current buffer now.
30$:	rsb				;Return to caller.
;
;
;
; This is our old faithful error checking routine. It checks the
; status code in R0 for errors and if found reports it by calling
; BUR_CHKERR.
;
chkerr:
	blbc	R0,10$			;Check error status.
	rsb				;All OK, simply exit.
10$:	pushl	R0			;Error status.
	pushal	(SP)			;Point to the status.
	calls	#1,G^BUR_CHKERR		;Deal with the error.
	movl	(SP)+,R0		;Recover the error status.
	rsb				;Exit after the error.
;
;
;
.page
;
;
;
; This is the code to get the next tape volume ready for access. If
; the job is a batch job accessing a tape device then we ask the
; operator via OPCOM to load the next tape volume into the same
; device. For other jobs we call LIB$GET_INPUT to find out what to
; use as the next device.
;
;
;
; The NEXT_VOLUME routine asks the user or operator to ready the
; next tape volume for reading.
;
; Called via:  status_code = BIO_NEXT_VOLUME( TAPE_STATUS, OPERATOR_REPLY )
;
; The possible values for tape_status are:-
;	   -1		tape device is not open
;	    0		tape device is ready for normal access
;	    1		reserved
;	    2		retryable read error detected
;	    3		fatal (or unknown) error detected
;	    4		reached end of tape
;
;
;
;
	$opcdef
	$jpidef
;
;
	.psect	data_ro,pic,shr,nowrt,noexe,long
;
tape_nvl_prompt:.ascid	'Please ready the next 10BACKUP tape volume: '
tape_req_head:	.byte	opc$l_ms_text	;Header length.
		.long	<<opc$m_nm_cluster!opc$m_nm_tapes>@8>!opc$_rq_rqst
		.long	0		;Request header.
tape_request:	.ascid	'!ACPlease ready the next 10BACKUP tape volume on !AD for read access'
;
	.psect	data,rd,wrt,quad
;
tape_modlst:
	.word	4,jpi$_mode		;Item code and length.
	.long	0			;Item buffer address (unknown).
	.long	0			;Return length address (none).
	.long	0			;End of item list.
;
tape_namlst:
	.word	64,dvi$_devnam		;Item code and length.
	.long	0			;Item buffer address (unknown).
	.long	0			;Return length address (unknown).
	.long	0			;End of item list.
;

	.psect	nvl_local,abs	;Local storage on stack.
;
nvl_mode:	.blkl	1		;Process mode.
nvl_devtxt:	.blkb	64		;Device name text.
nvl_devlen:	.blkl	1		;Length of device text.
nvl_reqtxt:	.blkb	255		;Request text.
nvl_reqbuf:	.blkq	1		;Descriptor for request.
nvl_mbxchn:	.blkw	1		;Reply mailbox channel.
nvl_iosb:	.blkq	1		;Reply mailbox IOSB.
nvl_reptxt:	.blkb	255		;Reply text.
nvl_loclen:
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_next_volume,^M<R2,R3,R4,R5,R6,R9>

	subl	#nvl_loclen,SP		;Make local storage.
	movl	SP,R9			;Remember where it is.
;
	blbs	tape_mode,10$		;If RMS IO ask user for next volume.
;
	clrb	tape_mark		;Set no tape mark.
	movab	nvl_mode(R9),tape_modlst+4 ;Set up buffer pointer.
	$getjpiw_s efn=#1, -
		itmlst=tape_modlst	;Find out what mode we are in.
	bsbw	chkerr			;Check for errors.
	cmpl	nvl_mode(R9),#jpi$k_interactive ;Is this job interactive?
	bneq	50$			;No, go and ask operator for next volume.
;
;
; Since this is an interactive job or we are using RMS I/O then
; we assume that the name of the next volume can be gotten from
; the user or the command procedure via lib$get_input.
;
10$:	calls	#0,bio_tape_rewind	;First we rewind the present volume.
	blbc	R0,40$			;Give up if errors.
;
	movl	8(AP),R2		;Get address of operator reply string.
;
20$:	pushab	tape_nvl_prompt		;Prompt to use.
	pushl	R2			;Where to put reply.
	calls	#2,G^LIB$GET_INPUT	;Get reply from user.
	blbc	R0,40$			;Give up if errors.
;
	tstw	(R2)			;Check if we were given data.
	bneq	25$			;Yes, go open new volume.
;
	tstl	@4(AP)			;Check if there is a current volume.
	blss	20$			;No, reask user for volume name.
	brb	40$			;Assume we will use the current volume.
;
25$:	tstl	@4(AP)			;Check if we still have a current volume.
	blss	30$			;No, just go open new volume.
	calls	#0,G^bio_tape_close	;Close old volume.
	mnegl	#1,@4(AP)		;Remember it is closed.
;
30$:	pushl	R2			;Name for next volume.
	calls	#1,g^bio_tape_init	;Open up the next volume.
	blbs	R0,40$			;If OK go ahead and use it.
;
	pushl	R0			;Put status code on stack.
	pushab	(SP)			;Point to it.
	calls	#1,g^bur_wrtmsg		;Write an error message.
	movl	(SP)+,R0		;Get status code back.
	brb	20$			;Go try again.
;
40$:	brw	90$			;Have finished setting up next volume.
;
;
; We come here to send a request to the operator via OPCOM so that
; he will mount the next volume in the tape drive for our BATCH job.
;
50$:	$qiow_s	efn=#1, -
		chan=tape_chan, -
		func=#io$_rewindoff!io$m_nowait, -
		iosb=tape_iosb		;Unload the tape.
	blbc	R0,40$			;Check for errors.
	movb	#1,tape_enable		;Enable further tape reads.
;
	movab	nvl_devtxt(R9),tape_namlst+4 ;Set up buffer pointer.
	movab	nvl_devlen(R9),tape_namlst+8 ;Set up return length.
	$getdviw_s efn=#1, -
		chan=tape_chan, -
		itmlst=tape_namlst	;Find device name.
	bsbw	chkerr			;Check for errors.
;
	movl	#255,nvl_reqbuf(R9)	;Set initial request length.
	movab	nvl_reqtxt(R9),nvl_reqbuf+4(R9) ;Set request address.
;
	pushab	nvl_devtxt(R9)		;Device text arg.
	movzwl	nvl_devlen(R9),-(SP)	;Device len arg.
	pushab	tape_req_head		;Header arg.
	pushaq	nvl_reqbuf(R9)		;Output descriptor arg.
	pushal	nvl_reqbuf(R9)		;Where to put output length.
	pushaq	tape_request		;Control string arg.
	calls	#6,G^sys$fao		;Format the request buffer.
	bsbw	chkerr			;Check for errors.
;
	$crembx_s chan=nvl_mbxchn(R9), -
		maxmsg=#255, -
		promsk=#^XFF00		;Make a mailbox for the reply.
	bsbw	chkerr			;Check for errors.
	$sndopr_s msgbuf=nvl_reqbuf(R9), -
		chan=nvl_mbxchn(R9)	;Send the request to operator.
	bsbw	chkerr			;Check for errors.
	$qiow_s efn=#1, -
		chan=nvl_mbxchn(R9), -
		func=#io$_readlblk, -
		iosb=nvl_iosb(R9), -
		p1=nvl_reptxt(R9), -
		p2=#255			;Get reply from operator.
	bsbw	chkerr			;Check for errors.
	movzwl	nvl_iosb(R9),R0		;Get reply IO status.
	bsbw	chkerr			;Check for errors.
	$dassgn_s chan=nvl_mbxchn(R9)	;Deassign mailbox channel.
	bsbw	chkerr			;Check for errors.
;
	movzwl	nvl_iosb+2(R9),R0	;Get length of reply.
	subl	#8,R0			;Adjust for reply text only.
	bgeq	60$			;Check if length is OK.
	clrl	R0			;Don't let string length be negative.
60$:	movab	nvl_reptxt+8(R9),R1	;Point to reply text.
	movl	8(AP),R2		;Destination descriptor.
	jsb	G^LIB$SCOPY_R_DX6	;Pass back string.
	bsbw	chkerr			;Check for errors.
	movzwl	nvl_reptxt+opc$w_ms_status(R9),R0 ;Get operator status.
;
;
90$:	ret				;Return with status.
;
;
;
.page
;
;
; The rest of the routines in this module are for calling RMS to
; write out the output files being restored.
;
; The three routines are:-
;	bio_file_init	To open up the output file.
;	bio_file_write	To write the current buffer to the output file.
;	bio_file_close	To close the output file.
;
;
;
	.psect	data,rd,wrt,quad
;
	.align	quad
bio_file_maxlen == 32763			;Maximum size of file record.
bio_file_buflen::	.long	0		;Length of file record.
bio_file_buffer::	.blkb	bio_file_maxlen	;Allocate buffer storage.
;
	.align quad
file_fab:	$fab	rat=<CR>		;FAB for RMS file.
file_rab:	$rab	fab=file_fab, -
			rbf=bio_file_buffer, -
			rop=wbh			;RAB for RMS file.
;
;
; The following are the routines to write the restored files.
;
; Initialize output file for writing.
;
; Called via:  CALL BIO_FILE_INIT( FILE_NAME, DEFAULT_NAME )
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_file_init,^M<>

	movl	4(AP),R1		;Get address of file name.
	movb	(R1),fab$b_fns+file_fab ;Set file name length.
	movl	4(R1),fab$l_fna+file_fab ;Set file name address.
	movl	8(AP),R1		;Get address of default name.
	movb	(R1),fab$b_dns+file_fab ;Set default name length.
	movl	4(R1),fab$l_dna+file_fab ;Set default name address.
	$create	fab=file_fab		;Open the file file.
	bsbw	chkerr			;Check for errors.
	$connect rab=file_rab		;Connect file record stream.
	bsbw	chkerr			;Check for errors.
	clrl	bio_file_buflen		;No data in buffer yet.
	ret				;Return to caller with status.
;
;
; Write a file record.
;
; Called via:  CALL BIO_FILE_WRITE
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_file_write,^M<>

	movw	bio_file_buflen,rab$w_rsz+file_rab ;Set length of file record.
	$put	rab=file_rab		;Write a record.
	bsbw	chkerr			;Check for errors.
	clrl	bio_file_buflen		;Reset the file length.
	ret				;Return to caller.
;
;
; Close the output file.
;
; Called via:  CALL BIO_FILE_CLOSE
;
	.psect	$code,pic,shr,nowrt,long
;
	.entry	bio_file_close,^M<>

	tstl	bio_file_buflen		;Is there still data in buffer?
	beql	10$			;No, don't need write.
	calls	#0,G^bio_file_write	;Write the data then.
10$:	$close	fab=file_fab		;Close the data file.
	bsbw	chkerr			;Check for errors.
	ret				;Return to caller.

;
;
;
	.end