.;
.;
.;		B R U . C M D
.;
.;	Conduct a standard BRU run.
.;	This command file can be invoked either interactively by
.;		>@BRU
.;	Or as an MCR command by
.;		>@BRU input output
.;	    or
.;		>@BRU output=input
.;	Input and output devices will be mounted (foreign) as necessary.
.;	An output disk mounted foreign WILL be initialized.
.;
.;	The following switches are legal, and can appear on either input
.;	or output device:
.;		Switch	Default	Meaning
.;		/BA	/-BA	Run bad before copy (foreign disks only).
.;		/BK:set	/BK	Backup set name.
.;		/DE:dns	varies	Tape density.
.;		/EX	/-EX	Exclude the named files from the copy.
.;		/IM	/-IM	Disk is "image mode".
.;		/IN		Initial index file size.
.;		/MX		Maximum index file size.
.;		/PR	/PR	Preserve file IDs.
.;		/RW	/RW	Rewind tape before copy. No effect for disk.
.;		/UF	/UF	Create UFDs as needed.
.;		/VE	/-VE	Verify volume after copy.
.;
.;	To add another switch, make the necessary modifications at all
.;	points identified by the string ".;//".
.;

	.ENABLE SUBSTITUTION
	.ENABLE ESCAPE
	.SETS S$PROC <FILSPC>
	.SETS S$SPAC "                                        "

.;
.;	At this point, the user is invited to insert here a set of
.;	identification strings for the systems he may wish to run
.;	this command file on. The strings are defined by
.;		.SETS sysnam "system identification"
.;	where <sysnam> is the DECnet node name, or the system name
.;	assigned at SYSGEN. The appropriate identification string
.;	will be selected at run time based on the contents of <NETNOD>.
.;
.;	If the system you are executing on supports extended logical
.;	names, logical name SYSTEM_IDENTIFICATION will be translated,
.;	(if it exists) and the translation will be used instead of the
.;	system identification generated as above.
.;
	.SETS FENNY "PDP-11/84"
	.SETS MARVIN "PDP-11/03"
	.SETS ZAPHOD "PDP-11/74"

.;
.;	Now, translate the above table (or logical name
.;	SYSTEM_IDENTIFICATION if it exists) into a string to display
.;	if we need it.
.;
	.SETS S$SNAM ""
	.IFDF <VERSN>	.TRANSLATE SYSTEM_IDENTIFICATION
	.IFDF <VERSN>	.SETS S$SNAM <EXSTRI>
	.IF S$SNAM = ""	.IFDF '<NETNOD>'	.SETS S$SNAM '<NETNOD>'
	.IF S$SNAM <> ""	.SETS S$SNAM " - "+S$SNAM
	.SETS S$SNAM <NETNOD>+"::"+S$SNAM
	.TEST S$PROC
	.SETS S$PROT S$SPAC[1:36.-(<STRLEN>/2)]+S$PROC
	.TEST S$SNAM
	.SETS S$SNAT S$SPAC[1:36.-(<STRLEN>/2)]+S$SNAM

.;
.;	Determine if we are interactive.
.;
	.SETF L$INTR
	.IF P1 = "" .IF P2 = ""	.SETT L$INTR

.;
.;	Display the title lines.
.;
.;.;.;	.IFF L$INTR	.GOTO NOTITL
	;
	;'S$PROT'
	;
	;                       Perform a backup on system
	;'S$SNAT'
	;
.NOTITL:.;

.;
.;	If the command line is in MCR format, convert to DCL.
.;
	.IF P2 = ""	.PARSE P1 "=" P2 P1

.;
.;	Create all switches and their defaults.
.;
	.SETF V$BA		! Do not run BAD first.
	.SETS V$BK ""		! Backup set name (null = next one).
	.SETN V$DE 0.		! Tape density.
	.SETF V$EX		! Do not exclude the named files.
	.SETF V$IM		! Disk is not image mode.
	.SETN V$IN 0.		! Default initial index size.
	.SETN V$MX 0.		! Default maximum index size.
	.SETT V$PR		! Preserve file IDs.
	.SETT V$RW		! Rewind tape before copy.
	.SETT V$UF		! Create UFDs as needed.
	.SETF V$VE		! Do not verify volume after copy.
.;//
.;//	To add another switch, define its corresponding symbol
.;//	here. This will cause the switch parser in subroutine DEV
.;//	to recognise and parse it.
.;//

.;
.;	Create the table of legal tape densities. The first density
.;	given will be the default for output. Make entries in this
.;	table ONLY for tapes that support more than one density, as
.;	an entry here enables the /DEN switch in the BRU command line
.;	(harmless), and the interactive query for density, even though
.;	there is only one legal entry (annoying). The first entry in
.;	the list for each device will be the default density for output.
.;
	.SETS DNS$MM "1600,800"		! Legal density for MM:
.;.;	.SETS DNS$MT "800"		! Density for MT: intentionally omitted.
	.SETS DNS$MS "1600,800"		! Legal density for MS:
	.SETS DNS$MF "1600,6250"	! Legal density for MF:

.;
.;	Set up the MCR command prefix, so we will work under DCL.
.;
	.SETS S$MCR ""
	.IF <CLI> <> "MCR"	.SETS S$MCR "MCR "

.;
.;	Create U.CW1 mask words to recognize devices of interest.
.;
	.SETN O$DUCW 140010	! UCW1 value for a disk.
	.SETN O$DDNC 001700	! UCW1 "dont care" mask for a disk.
	.SETN O$TUCW 000140	! UCW1 value for a tape.
	.SETN O$TDNC 140621	! UCW1 "dont care" mask for a tape.

.;
.;	Define various properties of the index file.
.;
	.SETN N$LIMI 65500.	! Absolute maximum filess on a volume.
	.SETN N$1HDR 25600.-7.	! Number of files with 1 header index.
	.SETN N$2HDR 51712.-13.	! Number of files with 2 header index.

.;
.;	Define the opposite of "IN" (which is "OU"), and
.;		the opposite of "OU" (which is "IN").
.;
	.SETS IN$OPP "OU"
	.SETS OU$OPP "IN"

.;
.;	Make assumptions about the "other" device suitable for the
.;		first pass through "DEV".
.;
	.SETT OU$DSK		! Assume (for the moment) output to disk
	.SETF OU$IMG		! Assume (for the moment) not image mode
	.SETT IN$FOK		! Filespecs OK on input volume.
	.SETF OU$FOK		! Filespecs not OK on output volume.

.;
.;	Ask for the input device. Any switches which appear
.;		on the line will be parsed, also.
.;
	.GOSUB DEV 'P1'|IN|Enter input device

.;
.;	Reset any switches that are position dependant.
.;//	If you add a switch that distinguishes between input and
.;//	output devices, you may need to reset or otherwise process
.;//	it here.
.;
	.SETF V$IM		! Disk is not image mode.

.;
.;	Ask for the output device. Any switches which appear
.;		on the line will be parsed, also.
.;
	.GOSUB DEV 'P2'|OU|Enter output device

.;
.;	Force switch settings consistent with the device types specified.
.;//	If you add a switch that only applies to certain device types,
.;//	you may want to reset it here for devices it does not apply to.
.;//	alternatively, you may wish to have the DEV subroutine reject it.
.;
	.IFT OU$F11	.SETF V$BA	! If output is F-11, ignore /BA.
	.IFF OU$DSK	.SETF V$BA	! If output tape, ignore /BA.
	.IFT OU$F11	.SETT V$PR	! If output is F-11, ignore /-PR.
	.IFT OU$SER	.SETT V$PR	! If output image or tape, no /-PR.
	.IFT OU$DSK .IFT IN$DSK .SETF V$RW	! Can not rewind a disk.
	.IFF OU$F11	.SETF V$UF	! If output not F-11, ignore /UF.
	.IFT OU$SER	.SETF V$UF	! If output image or tape, no /UF.
	.IF IN$FIL = ""	.SETF V$EX	! If no input files, ignore /EX.
	.IFT OU$F11	.SETN V$IN 0.	! If output is F-11, ignore /IN.
	.IFT OU$SER	.SETN V$IN 0.	! If output image or tape, no /IN.
	.IFT OU$F11	.SETN V$MX 0.	! If output is F-11, ignore /MX.
	.IFT OU$SER	.SETN V$MX 0.	! If output image or tape, no /MX.
	.IFF IN$DSK	.SETF OU$IMG	! If input is tape, output not img.
	.IFF OU$DSK	.SETF IN$IMG	! If output is tape, input not img.
	.SETL L$IMGP IN$DSK&OU$DSK	! Image possible only disk-to-disk.

.;
.;	Set the tape density based on what we know so far:
.;		If the output device supports it,
.;			replace 0 with proper default
.;		If neither device supports it,
.;			force it to zero.
.;
	.SETS S$DRVR OU$DEV[1:2]
	.SETS S$DENS "0"
	.IFDF DNS$'S$DRVR' .PARSE DNS$'S$DRVR' "," S$DENS S$JUNK
	.IFDF DNS$'S$DRVR' .IF V$DE = 0	.SETN V$DE 'S$DENS'.
	.IFNDF DNS$'S$DRVR'	.SETS S$DRVR IN$DEV[1:2]
	.IFNDF DNS$'S$DRVR'	.SETN V$DE 0.	! Ignore /DE if not supported.

.;
.;	Supply dummy initial index file limit values.
.;
	.SETS S$IXLM ""			! Must be defined. Value set later.
	.SETS S$IXMX ""			! Must be defined. Value set later.
	.SETN O$IXLM 0			! Index limit not computed yet.

.;
.;	If this is not an interactive run, we have all the information we
.;	need. Go execute the necessary commands.
.;
	.IFF L$INTR	.GOTO DOIT

.;
.;	If we are interactive, tell the user what we're about to do.
.;//	If you add a switch, add an appropriate message for it here.
.;
	;
	; By default, the BRU run will be done as follows:
	;

	.;
	.;	Running BAD is a possibility only if the output is to
	.;	a disk that is not mounted FILES-11.
	.;
	.IFT OU$DSK .IFF OU$F11	.IFF V$BA ;	BAD will not be run on the output disk.
	.IFT OU$DSK .IFF OU$F11	.IFT V$BA ;	BAD will be run on the output disk.

	.;
	.;	BRU image input is only a possibility if both input and
	.;	output devices are disk.
	.;
	.IFT L$IMGP .IFF IN$IMG		;	The input disk is in FILES-11 (not BRU backup set) format.
	.IFT L$IMGP .IFT IN$IMG		;	The input disk is in BRU backup set format.

	.;
	.;	BRU image output is only a possibility if both input and
	.;	output devices are disk.
	.;
	.IFT L$IMGP .IFF OU$IMG		;	The output disk is in FILES-11 (not BRU backup set) format.
	.IFT L$IMGP .IFT OU$IMG		;	The output disk is in BRU backup set format.

	.;
	.;	A backup set name is legal only if either the input or the
	.;	output device is serial (meaning either tape or BRU image).
	.;
	.SETL L$Q IN$SER!OU$SER
	.IFT L$Q .IF V$BK = ""	;	A backup set name was not specified.
	.IFT L$Q .IF V$BK <> ""	;	Backup set "'V$BK'" will be restored.

	.;
	.;	Specifying exclusion of the named input files only makes
	.;	sense if input files were specified.
	.;
	.IF IN$FIL <> "" .IFF V$EX	;	Only the named files will be copied.
	.IF IN$FIL <> "" .IFT V$EX	;	All except the named files will be copied.

	.;
	.;	Preservation of file IDs is a possibility only if output
	.;	is not to serial media (tape or BRU image) and not to
	.;	a disk mounted FILES-11.
	.;
	.IFF OU$SER .IFF OU$F11 .IFF V$PR ;	File IDs will not be preserved.
	.IFF OU$SER .IFF OU$F11 .IFT V$PR ;	File IDs will be preserved.

	.;
	.;	Specifying the maximum number of file IDs is only possible
	.;	if output is not to serial media (tape or BRU image) and
	.;	not to a disk mounted FILES-11, and file IDs are not being
	.;	preserved.
	.;
	.IFF OU$SER .IFF OU$F11 .IFF V$PR	.GOSUB MAX V$MX O$IXLM 'OU$DEV'
	.IFF OU$SER .IFF OU$F11 .IFF V$PR ;	The volume will hold a maximum of 'V$MX%D'. files.

	.;
	.;	Specifying the initial number of file IDs is only possible
	.;	if output is not to serial media (tape or BRU image) and
	.;	not to a disk mounted FILES-11, and file IDs are not being
	.;	preserved. If no value was specified, a default must be
	.;	supplied. The value must be clamped to the allowed limits
	.;	in any case.
	.;
	.SETN N$INVL V$IN
	.IFF OU$SER .IFF OU$F11 .IFF V$PR .IF V$IN = 0	.GOSUB INI V$IN 'V$MX%D'.
	.IF V$IN > V$MX	.SETN V$IN V$MX
	.IF V$IN < 16.	.SETN V$IN 16.
	.IFF OU$SER .IFF OU$F11 .IFF V$PR		;	Initially, the index file will hold 'V$IN%D'. files.

	.;
	.;	Rewinding the tape is only a possibility if either
	.;	input or output is not to disk.
	.;
	.SETL L$Q IN$DSK&OU$DSK
	.IFF L$Q .IFF V$RW		;	The tape will not be rewound first.
	.IFF L$Q .IFT V$RW		;	The tape will be rewound first.

	.;
	.;	Specifying the media density is optional of the input medium
	.;	is tape, required if output medium is tape, impossible
	.;	otherwise.
	.;
	.SETS S$DRVR IN$DEV[1:2]
	.IFNDF DNS$'S$DRVR'	.SETS S$DRVR OU$DEV[1:2]
	.IFDF DNS$'S$DRVR' .IF V$DE <> 0	;	The tape density is 'V$DE%D' BPI.
	.IFDF DNS$'S$DRVR' .IF V$DE = 0	;	The tape density is defaulted.

	.;
	.;	Creating new directories is only possible on a mounted
	.;	output disk that is not being used to create a BRU image.
	.;
	.IFF OU$SER .IFT OU$F11 .IFF V$UF		;	BRU will not create directories on the output disk.
	.IFF OU$SER .IFT OU$F11 .IFT V$UF		;	BRU will create directories on the output disk.

	.;
	.;	The output volume can ALWAYS be verified.
	.;
	.IFF V$VE			;	The output volume will not be verified.
	.IFT V$VE			;	The output volume will be verified.

.;
.;	Now, give the user a chance to change the defaults. If he passes
.;	it up, go execute the required commands.
.;
	;
	.SETF L$OP
.INQIOP:.SETL L$Q L$OP
	.ASK [L$OP] L$OP Do you wish to change these defaults
	.IFT <ESCAPE>	.GOTO HELPOP
	.IFF L$OP	.GOTO DOIT

.;
.;	The user has decided he does not want to take our thoughtful
.;	recommendations on how to do the BRU run. The payment for his
.;	arrogance is to answer each of the questions that follow.
.;//	If you add a switch, it might be polite to query the user about
.;//	it here if he elects to run this command file interactively.
.;
	;
	.DISABLE LOWERCASE

	.;
	.;	Running BAD is a possibility only if the output is to
	.;	a disk that is not mounted FILES-11.
	.;
.INQIBA:.SETL L$Q	V$BA
	.IFT OU$DSK .IFF OU$F11	.ASK [V$BA] V$BA Shall I run BAD on the output disk
	.IFT <ESCAPE>	.GOTO HELPBA

	.;
	.;	BRU image input is only a possibility if both input and
	.;	output devices are disk.
	.;
.INQIII:.SETL L$Q IN$IMG
	.IFT L$IMGP .ASK [IN$IMG] IN$IMG Is the input disk in BRU image format
	.IFT <ESCAPE>	.GOTO HELPII

	.;
	.;	BRU image output is only a possibility if both input and
	.;	output devices are disk, and the input is not in image
	.;	mode.
	.;
.INQIIO:.SETL L$Q OU$IMG
	.IFT IN$IMG	.SETF OU$IMG
	.IFF IN$IMG .IFT L$IMGP .ASK [OU$IMG] OU$IMG Is the output disk in BRU image format
	.IFT <ESCAPE>	.GOTO HELPIO

	.;
	.;	Recompute whether we are serial media, as the user may have
	.;	selected BRU backup format for either input or output disk.
	.;
	.SETL IN$SER IN$IMG!#IN$DSK
	.SETL OU$SER OU$IMG!#OU$DSK

	.;
	.;	Also tinker with any switch defaults that depend on
	.;	whether we are serial media.
	.;
	.IFT OU$SER	.SETT V$PR	! If output image or tape, no /-PR.
	.IFT OU$SER	.SETF V$UF	! If output image or tape, no /UF.
	.IFT OU$SER	.SETN V$IN 0.	! If output image or tape, no /IN.
	.IFT OU$SER	.SETN V$MX 0.	! If output image or tape, no /MX.

	.;
	.;	A backup set name is legal only if either the input or the
	.;	output device is serial (meaning either tape or BRU image).
	.;
.INQIBK:.SETL L$Q IN$SER!OU$SER
	.SETS S$Q V$BK
	.IFT L$Q	.ASKS [0:12.:V$BK] V$BK What backup set name
	.IFT <ESCAPE>	.GOTO HELPBK

	.;
	.;	Specifying exclusion of the named input files only makes
	.;	sense if input files were specified.
	.;
.INQIEX:.SETL L$Q V$EX
	.IF IN$FIL <> "" .ASK [V$EX] V$EX Shall I exclude the named files
	.IFT <ESCAPE>	.GOTO HELPEX

	.;
	.;	Preservation of file IDs is a possibility only if output
	.;	is not to serial media (tape or BRU image) and not to
	.;	a disk mounted FILES-11.
	.;
.INQIPR:.SETL L$Q V$PR
	.IFF OU$F11 .IFF OU$SER	.ASK [V$PR] V$PR Shall I preserve file IDs
	.IFT <ESCAPE>	.GOTO HELPPR

	.;
	.;	As the user may have changed the default on the above
	.;	question, recompute the index file limit if needed.
	.;
	.IFF V$PR .IF O$IXLM = 0 .GOSUB MAX V$MX O$IXLM 'OU$DEV'

	.;
	.;	Specifying the maximum number of file IDs is only possible
	.;	if output is not to serial media (tape or BRU image) and
	.;	not to a disk mounted FILES-11, and file IDs are not being
	.;	preserved.
	.;
.INQIMX:.SETN N$Q V$MX
	.IFF OU$SER .IFF OU$F11 .IFF V$PR .ASKN [16.:O$IXLM:V$MX] V$MX Enter the maximum files on this volume
	.IFT <ESCAPE>	.GOTO HELPMX

	.;
	.;	Specifying the initial number of file IDs is only possible
	.;	if output is not to serial media (tape or BRU image) and
	.;	not to a disk mounted FILES-11, and file IDs are not being
	.;	preserved. If no value was specified, a default must be
	.;	supplied. The value must be clamped to the allowed limits
	.;	in any case.
	.;
	.SETN V$IN N$INVL
	.IFT OU$DSK .IFF V$PR	.IF V$IN = 0	.GOSUB INI V$IN 'V$MX%D'.
	.IF V$IN > V$MX	.SETN V$IN V$MX
	.IF V$IN < 16.	.SETN V$IN 16.
.INQIIN:.SETN N$Q V$IN
	.IFT OU$DSK .IFF V$PR	.ASKN [16.:V$MX:V$IN] V$IN Enter the initial index file allocation
	.IFT <ESCAPE>	.GOTO HELPIN

	.;
	.;	Rewinding the tape is only a possibility if either
	.;	input or output is not to disk.
	.;
.INQIRW:.SETL L$Q V$RW
	.IFF IN$DSK .OR .IFF OU$DSK	.ASK [V$RW] V$RW Shall I rewind the tape
	.IFT <ESCAPE>	.GOTO HELPRW

	.;
	.;	Specifying the media density is optional of the input medium
	.;	is tape, required if output medium is tape, impossible
	.;	otherwise.
	.;
.INQIDE:.SETN N$Q V$DE
	.SETS S$DRVR IN$DEV[1:2]
	.IFNDF DNS$'S$DRVR'	.SETS S$DRVR OU$DEV[1:2]
	.IFNDF DNS$'S$DRVR'	.GOTO NOINDN
	.PARSE DNS$'S$DRVR' "," S$DENS S$JUNK
	.IF S$DRVR = OU$DEV[1:2]	.IF V$DE = 0	.SETN V$DE 'S$DENS'.
	.ASKN [::V$DE] V$DE What tape density
	.SETS S$DENS DNS$'S$DRVR'
	.IF S$DRVR = IN$DEV[1:2]	.SETS S$DENS S$DENS+",0"
	.IFT <ESCAPE>	.GOTO HELPDE
	.TEST ",'S$DENS'," ",'V$DE%D',"
	.IF <STRLEN> <> 0	.GOTO NOINDN
	.SETN V$DE N$Q
	.DISABLE DISPLAY
	;
	; Error - Illegal density for an 'S$DRVR': device.
	;         You must select from the list 'S$DENS'.
	;
	.ENABLE DISPLAY
	.GOTO INQIDE
.NOINDN:.;

	.;
	.;	Creating new directories is only possible on a mounted
	.;	output disk that is not being used to create a BRU image.
	.;
.INQIUF:.SETL L$Q V$UF
	.IFF OU$SER .IFT OU$F11 .ASK [V$UF] V$UF Shall I create new directories where needed
	.IFT <ESCAPE>	.GOTO HELPUF

	.;
	.;	The output volume can ALWAYS be verified.
	.;
.INQIVE:.SETL L$Q V$VE
	.ASK [V$VE] V$VE Shall I verify the output volume(s)
	.IFT <ESCAPE>	.GOTO HELPVE

	.ENABLE LOWERCASE

.;
.;	Now that we know what we are supposed to do, do it.
.;//	If you add a switch that modifies the command lines generated,
.;//	impliment it here.
.;
.DOIT:	;

	.;
	.;	If needed, compute the initial and maximum index file entries.
	.;
	.IFT OU$DSK .IFF V$PR	.IF V$MX = 0	.GOSUB MAX V$MX O$IXLM 'OU$DEV'
	.IFT OU$DSK .IFF V$PR	.IF V$IN = 0	.GOSUB INI V$IN 'V$MX%D'.

	.;
	.;	Build the BRU command line;
	.;
	.SETS S$BRU "BRU"
	.IFF IN$DSK .IFT V$RW		.SETS S$BRU S$BRU+"/REW"
	.IFF IN$DSK .IF V$DE <> 0	.SETS S$BRU S$BRU+"/DEN:'V$DE%D'"
	.IFT IN$DSK .IFT IN$F11		.SETS S$BRU S$BRU+"/MOU"
	.IFT IN$IMG			.SETS S$BRU S$BRU+"/IM:R"
	.IFT IN$SER .IF V$BK <> ""	.SETS S$BRU S$BRU+"/BAC:"+V$BK
	.IFT V$EX			.SETS S$BRU S$BRU+"/EXC"
	.IFF OU$DSK .IFT V$RW		.SETS S$BRU S$BRU+"/REW"
	.IFF OU$DSK .IF V$DE <> 0	.SETS S$BRU S$BRU+"/DEN:'V$DE%D'"
	.IFF OU$DSK .IFF V$RW		.SETS S$BRU S$BRU+"/APP"
	.IFT OU$DSK .IFF OU$F11		.SETS S$BRU S$BRU+"/INI"
	.IFT OU$DSK .IFT OU$F11		.SETS S$BRU S$BRU+"/NOINI"
	.IFT OU$DSK .IFF V$PR		.SETS S$BRU S$BRU+"/NOPRE"
	.IFT OU$DSK .IFF V$PR		.SETS S$BRU S$BRU+"/HEA:'V$IN'/MAX:'V$MX'"
	.IFT OU$IMG			.SETS S$BRU S$BRU+"/IM:S"
	.IFT OU$SER .IF V$BK <> ""	.SETS S$BRU S$BRU+"/BAC:"+V$BK
	.IFT OU$DSK .IFT OU$F11 .IFT V$UF	.SETS S$BRU S$BRU+"/UFD"
	.IFT V$VE			.SETS S$BRU S$BRU+"/VER"

.;
.;	Pick up the start time for the BRU run.
.;
	.PARSE <TIME> ":" S$HR S$MI S$SC
	.SETN N$SHR 'S$HR'.
	.SETN N$SMI 'S$MI'.
	.SETN N$SSC 'S$SC'.
;
;		  Start time: 'N$SHR%DR2':'N$SMI%DR2Z':'N$SSC%DR2Z'
;

.;
.;	Execute command lines as appropriate.
.;
	.IFF IN$PUB	.IFF IN$MOU	'S$MCR'ALL 'IN$DEV'
	.IFF OU$PUB	.IFF OU$MOU	'S$MCR'ALL 'OU$DEV'
	.IFF IN$ONL			'S$MCR'CON ONLINE 'IN$DEV'
	.IFF OU$ONL			'S$MCR'CON ONLINE 'OU$DEV'
	.IFF IN$MOU			'S$MCR'MOU 'IN$DEV'/FOR
	.IFF OU$MOU			'S$MCR'MOU 'OU$DEV'/FOR
	.IFT V$BA			'S$MCR'BAD 'OU$DEV'/LI
					'S$MCR''S$BRU' 'IN$DEV''IN$FIL' 'OU$DEV'
	.IFF IN$MOU	.IFF IN$PUB	'S$MCR'DMO 'IN$DEV'
	.IFF IN$MOU	.IFT IN$PUB	'S$MCR'DMO 'IN$DEV'/DEV
	.IFF OU$MOU	.IFF OU$PUB	'S$MCR'DMO 'OU$DEV'
	.IFF OU$MOU	.IFT OU$PUB	'S$MCR'DMO 'OU$DEV'/DEV
.;.;	.IFF IN$ONL			'S$MCR'CON OFFLINE 'IN$DEV'
.;.;	.IFF OU$ONL			'S$MCR'CON OFFLINE 'OU$DEV'
	.IFF IN$PUB	.IFF IN$MOU	'S$MCR'DEA 'IN$DEV'
	.IFF OU$PUB	.IFF OU$MOU	'S$MCR'DEA 'OU$DEV'

.;
.;	Pick up the end time for the BRU run.
.;
	.PARSE <TIME> ":" S$HR S$MI S$SC
	.SETN N$EHR 'S$HR'.
	.SETN N$EMI 'S$MI'.
	.SETN N$ESC 'S$SC'.

.;
.;	Calculate the elapsed time for the run.
.;
	.SETN N$DSC N$ESC+60.-N$SSC
	.SETN N$DMI N$EMI+59.-N$SMI+(N$DSC/60.)
	.SETN N$DHR N$EHR+59.-N$SHR+(N$DMI/60.)
	.SETN N$DSC N$DSC-((N$DSC/60.)*60.)
	.SETN N$DMI N$DMI-((N$DMI/60.)*60.)
	.SETN N$DHR N$DHR-((N$DHR/60.)*60.)

;
;		    End time: 'N$EHR%DR2':'N$EMI%DR2Z':'N$ESC%DR2Z'
;		  Start time: 'N$SHR%DR2':'N$SMI%DR2Z':'N$SSC%DR2Z'
;		Elapsed time: 'N$DHR%DR2':'N$DMI%DR2Z':'N$DSC%DR2Z'
;
	.EXIT

.;
.;
.;
.;		DEV -- Check for legal device.
.;
.;	Calling sequence:
.;		.GOSUB DEV dev/swits|dir|prompt
.;	    Where:
.;		dev/swits = device spec
.;		dir = "IN" or "OU"
.;		prompt = prompt string.
.;	Returns:
.;		dir$DEV = device name
.;		dir$FIL = filespecs
.;		dir$PUB = <TRUE> if device public
.;		dir$ONL = <TRUE> if online
.;		dir$MOU = <TRUE> if mounted
.;		dir$DSK = <TRUE> if a disk
.;		dir$F11 = <TRUE> if mounted FILES-11
.;		dir$IMG = <TRUE> if image disk.
.;		dir$SER = <TRUE> if serial media (tape or image)
.;
.;		Also, all switches are parsed, and their values
.;			returned.
.;
.DEV:

.;
.;	Parse the calling sequence. First, the command line needs to
.;	be broken up into device spec, intended direction (IN or OU),
.;	and thhe desired prompt text. Then, the switches, if any, are
.;	separated from the device spec.
.;
	.PARSE COMMAN "|" SB$DEV SB$DIR SB$PMP SB$JNK
	.PARSE SB$DEV "/" SB$DEV SB$SWS
	.SETS SB$OPP 'SB$DIR'$OPP	! Get our opposite direction.

.;
.;	If we have a device spec already, this run is not interactive.
.;	Go directly to process the device spec and switches.
.;
	.SETF SB$INT
	.IF SB$DEV <> ""	.GOTO DEVCHK
	.SETT SB$INT

.;
.;	Prompt the user for a device specification. Switches may also be
.;	used here, and will become the defaults for the options Q&A.
.;
.DEVASK:.DISABLE DISPLAY
	.DISABLE LOWERCASE
	;
	.ENABLE DISPLAY
	.ASKS SB$DEV 'SB$PMP'
	.ENABLE LOWERCASE
	.IFF <ESCAPE>	.GOTO DEVCHK

.;
.;	Give the poor sucker some help on what is meant by the
.;	question.
.;
	.DISABLE DISPLAY
	;
	;You must specify the name of a mass-storage device (disk or
	;tape) in the usual RSX format. Logical names are OK, but they
	;must resolve to a legal device name. For the input device ONLY,
	;you may also give one or more filespecs.
	;
	;This device will brought online if necessary, and left online
	;after the BRU operation.
	.ENABLE DISPLAY
	.GOTO DEVASK

.;
.;	Check out the device specification.
.;
.DEVCHK:.;

	.;
	.;	Parse the switches off the device spec.
	.;
	.PARSE SB$DEV "/" SB$DEV SB$SWT

	.;
	.;	Parse off the file specifications from the device name.
	.;	If file specs are not allowed in this direction,
	.;	error out.
	.;
	.SETS S$ERR "File specifications are not allowed on this volume"
	.PARSE SB$DEV ":" SB$DEV 'SB$DIR'$FIL
	.IFF 'SB$DIR'$FOK .IF 'SB$DIR'$FIL <> ""	.GOTO DEVERR

	.;
	.;	If we have support for extended logical names, attempt
	.;	translation of the device name as a logical name.
	.;	If translation succeeds, reparse into device name and
	.;	file name.
	.;
	.IFNDF <VERSN>	.GOTO DEVLNM
	.TRANSLATE 'SB$DEV'
	.IF <EXSTRI> = ""	.GOTO DEVLNM
	.PARSE <EXSTRI> ":" SB$DEV SB$JNK
	.IF SB$JNK <> ""	.SETS 'SB$DIR'$FIL SB$JNK+","+'SB$DIR'$FIL

	.;
	.;	See if the device exists. If not, error out.
	.;
.DEVLNM:.;
	.SETS S$ERR "Device 'SB$DEV': not in system"
	.TESTDEVICE 'SB$DEV':
	.TEST <EXSTRI> "NSD"
	.IF <STRLEN> > 0	.GOTO DEVERR

	.;
	.;	Get the device physical name, and its characteristics.
	.;
	.PARSE <EXSTRI> "," SB$DEV SB$UC1 SB$UC2 SB$UC3 SB$UC4 SB$FGS
	.SETS 'SB$DIR'$DEV SB$DEV

	.;
	.;	Check the device type. If disk or tape, set the appropriate
	.;	indicators -- otherwise, error out.
	.;
	.SETF 'SB$DIR'$DSK
	.SETS S$ERR "Device 'SB$DEV' is neither disk nor tape"
	.IF O$DUCW = 'SB$UC1'&#O$DDNC	.SETT 'SB$DIR'$DSK
	.IFF 'SB$DIR'$DSK	.IF O$TUCW <> 'SB$UC1'&#O$TDNC	.GOTO DEVERR
	.SETS S$ERR "Since 'IN$DEV' is a tape, 'SB$DEV' must be a disk"
	.IFF IN$DSK .IFF OU$DSK	.GOTO DEVERR

	.;
	.;	See if the device is allocated. If so, error out unless
	.;	MCR is disabled.
	.;
	.SETS S$ERR "'SB$DEV' is allocated to another user"
	.TEST <EXSTRI> "ALO"
	.IF <STRLEN> > 0	.IFENABLED MCR	.GOTO DEVERR
	.IF <STRLEN> > 0	; Warning -- 'S$ERR'.
	.IF <STRLEN> > 0	;            Since MCR is disabled, I will proceed.

	.;
	.;	Determine whether the device is:
	.;		- Online,
	.;		- Mounted,
	.;		- Public.
	.;
	.SETT 'SB$DIR'$ONL
	.SETT 'SB$DIR'$MOU
	.SETF 'SB$DIR'$PUB
	.TEST <EXSTRI> "PUB"
	.IF <STRLEN> > 0	.SETT 'SB$DIR'$PUB
	.TEST <EXSTRI> "OFL"
	.IF <STRLEN> > 0	.IF <SYSTEM> = 6	.SETF 'SB$DIR'$ONL
	.TEST <EXSTRI> "NMT"
	.IF <STRLEN> > 0	.SETF 'SB$DIR'$MOU

	.;
	.;	Determine whether the device is mounted as a FILES-11
	.;	device.
	.;
	.SETL 'SB$DIR'$F11 'SB$DIR'$MOU
	.TEST <EXSTRI> "FOR"
	.IF <STRLEN> > 0	.SETF 'SB$DIR'$F11
	.IF <SYSTEM> <> 6	.SETT 'SB$DIR'$MOU
	.SETS SB$SOV ""

	.;
	.;	Get the default switches, and append the explicit
	.;	switches.
	.;
	.SETS SB$SWL SB$SWS			! Get default switches
	.IF SB$SWT <> ""	.SETS SB$SWL SB$SWL+"/"+SB$SWT

	.;
	.;	Now, parse the next switch from the input line. If
	.;	there is none, go return.
	.;
.DEVSWL:
	.IF SB$SWL = ""	.GOTO DEVRTN
	.PARSE SB$SWL "/" SB$SWT SB$SWL
	.PARSE SB$SWT ":" SB$SWT SB$SWV

	.;
	.;	Find out whether the switch specification is asserted or
	.;	negated. Switches may be negated by preceding them with
	.;	either "-" or "NO". The negation indicator (if any) is
	.;	stripped off before further processing.
	.;
	.SETF LB$ASR
	.SETS SB$JNK "-"
	.IF SB$JNK = SB$SWT[1:1]	.GOTO DEVSWS
	.SETS SB$JNK "NO"
	.IF SB$JNK = SB$SWT[1:2]	.GOTO DEVSWS
	.SETS SB$JNK ""
	.SETT LB$ASR
.DEVSWS:.TEST SB$JNK
	.SETS SB$SWT SB$SWT[<STRLEN>+1:*]

	.;
	.;	Find out whether this is a legal switch. It is legal if
	.;	the first two characters:
	.;		are alphanumeric
	.;		form the name of a defined symbol when "V$"
	.;			is prefixed.
	.;	None others need apply.
	.;
	.SETS SB$SWT SB$SWT[1:2]
	.IF SB$SWT = ""	.GOTO DEVSWL
	.SETS S$ERR "Switch /'SB$SWT' is not defined"
	.TEST SB$SWT
	.IFF <ALPHAN>		.GOTO DEVERR
	.IFNDF V$'SB$SWT'	.GOTO DEVERR

	.;
	.;	Handle the switch according to the symbol type of the
	.;	symbol that defines it (V$sw).
	.;
	.TEST V$'SB$SWT'
	.GOTO DEVSW'<SYMTYP>'

	.;
	.;	The switch is defined by a logical symbol. Set V$sw
	.;	<TRUE> or <FALSE> depending on whether the switch was
	.;	asserted.True/false switches may not have an argument.
	.;
.DEVSW0:.SETS S$ERR "Switch /'SB$SWT' may not have an argument"
	.IF SB$SWV <> ""	.GOTO DEVERR
	.SETL LB$JNK V$'SB$SWT'
	.SETS SB$SOV SB$SWT+":'LB$JNK'/"+SB$SOV
	.SET'LB$ASR' V$'SB$SWT'
	.GOTO DEVSWL

	.;
	.;	The switch is defined by a numeric symbol. Set V$sw
	.;	to the value of the switch argument (0 if none). The argument
	.;	must be numeric, and is assumed to be octal.
	.;	The switch may not be negated.
	.;
.DEVSW2:.IFF <OCTAL>	.SETS SB$SWV SB$SWV+"."
	.SETS S$ERR "Switch /'SB$SWT' may not be negated"
	.IFF LB$ASR	.GOTO DEVERR
	.SETS S$ERR "Switch /'SB$SWT' requires a numeric argument"
	.SETS SB$SWV "0"+SB$SWV
	.SETN NB$JNK V$'SB$SWT'
	.TEST SB$SWV
	.IFF <NUMBER>	.GOTO DEVERR
	.TEST V$'SB$SWT'
	.IFT <OCTAL>	.SETS SB$SOV SB$SWT+":'NB$JNK%O'/"+SB$SOV
	.IFF <OCTAL>	.SETS SB$SOV SB$SWT+":'NB$JNK%D'./"+SB$SOV
	.SETN V$'SB$SWT' 'SB$SWV'
	.GOTO DEVSWL

	.;
	.;	The switch is defined by a string symbol. Set V$sw to the
	.;	value of the switch argument. The switch may not be negated.
	.;
.DEVSW4:.SETS S$ERR "You may not pass an argument if /'SB$SWT' is negated"
	.IFF LB$ASR .IF SB$SWV <> ""	.GOTO DEVERR
	.SETS SB$JNK V$'SB$SWT'
	.SETS SB$SOV SB$SWT+":"+SB$JNK+"/"+SB$SOV
	.SETS V$'SB$SWT' SB$SWV
	.GOTO DEVSWL

.;
.;	Do some final validation:
.;//	If you add a switch, and wish to make it illegal on certain
.;//	types of device, do so here.
.;
.DEVRTN:.IFNDF V$IM .SETF V$IM

	.;
	.;		Both input and output disks may not be in
	.;		image format. The /IM switch is forced off
	.;		for tape.
	.;
	.IFF 'SB$DIR'$DSK	.SETF V$IM
	.SETL 'SB$DIR'$IMG V$IM
	.SETS S$ERR "Both disks may not be in image format"
	.IFT IN$IMG .IFT OU$IMG	.GOTO DEVERR

	.;
	.;		You may not copy from tape to image disk, or
	.;		vice versa.
	.;
	.SETS S$ERR "You cannot copy between tape and image disk"
	.IFT IN$IMG .IFF OU$DSK	.GOTO DEVERR
	.IFT OU$IMG .IFF IN$DSK	.GOTO DEVERR

	.;
	.;	Set the "Serial media" flag if the device is tape or
	.;	image disk.
	.;
	.SETL 'SB$DIR'$SER 'SB$DIR'$IMG!#'SB$DIR'$DSK

	.;
	.;	Attempt to validate the tape density. This will catch
	.;	errors ONLY when both the tape driver name and the
	.;	tape density are available - so it may fail oddly if
	.;	the /DE switch is not applied to the proper device -
	.;	but it's the best I can do.
	.;
	.SETS SB$DNS ""
	.SETS SB$DRV 'SB$DIR'$DEV[1:2]
	.IFNDF DNS$'SB$DRV' .IFDF 'SB$OPP'$DEV	.SETS SB$DRV 'SB$OPP'$DEV[1:2]
	.IFDF DNS$'SB$DRV'	.SETS SB$DNS DNS$'SB$DRV'
	.SETS S$ERR "'SB$DRV': devices do not support 'V$DE%D' bpi"
	.IF SB$DNS <> "" .IF V$DE <> 0	.TEST ",'SB$DNS'," ",'V$DE%D',"
	.IF SB$DNS <> "" .IF V$DE <> 0	.IF <STRLEN> = 0 .GOTO DEVERR
	.RETURN

.;
.;	Validation error handling:
.;
.;		Display an error message as appropriate.
.;
.DEVERR:.DISABLE DISPLAY
	;
	;Error - 'S$ERR'.
	;
	.ENABLE DISPLAY

	.;
	.;	If invoked in "command line" mode, just exit.
	.;
	.IFF SB$INT	.EXIT

	.;
	.;	Loop through the affected switches, restoring each
	.;	to the value it had before the entry containing
	.;	the screw-up.
	.;
.DEVERS:
	.IF SB$SOV = ""	.GOTO DEVASK
	.PARSE SB$SOV "/" SB$SWT SB$SOV
	.PARSE SB$SWT ":" SB$SWT SB$SWV
	.TEST V$'SB$SWT'
	.GOTO DEVER'<SYMTYP>'
.DEVER0:.SET'SB$SWV' V$'SB$SWT'
	.GOTO DEVERS
.DEVER2:.SETN V$'SB$SWT' 'SB$SWV'
	.GOTO DEVERS
.DEVER4:.SETS V$'SB$SWT' SB$SWV
	.GOTO DEVERS

.;
.;======================================================================
.;
.;	Help code.
.;
.;	By convention, enter at HELPsw to get help on switch "/sw".
.;	after help is displayed, return to "INQIsw". There are specific
.;	exceptions to this for the IM switch (which is slightly dif-
.;	ferent on input than on output).
.;

.HELPBA:.SETL V$BA L$Q
	.DISABLE DISPLAY
	;
	;	This option determines whether the bad block locator (BAD)
	;	is run on the output volume before you BRU to it. Normally
	;	you do not need to do this unless this is the very first
	;	time you have used the volume, or you have reason to suspect
	;	the integrity of the volume.
	;
	.IFF V$BA	;	The default is not to run BAD on the output volume.
	.IFT V$BA	;	The default is to run BAD on the output volume.
	;
	.ENABLE DISPLAY
	.GOTO INQIBA

.HELPBK:.SETS V$BK S$Q
	.DISABLE DISPLAY
	;
	;	This option specifies the name of the BRU backup set to
	;	create or restore. On input, a null backup set name means
	;	to take the next backup set on the medium. On output, a
	;	null backup set name means to use the name of the input
	;	volume for the backup set name.
	;
	.IF V$BK = ""	;	The default is to take the next backup set on input, or
	.IF V$BK = ""	;	to create a backup set named after the output volume on
	.IF V$BK = ""	;	output.
	.IF V$BK <> ""	;	The default is to use backup set "'V$BK'"
	;
	.ENABLE DISPLAY
	.GOTO INQIBK

.HELPEX:.SETL V$EX L$Q
	.DISABLE DISPLAY
	;
	;	If this option is asserted, all files on the input volume
	;	except those named in the input specification are copied
	;	to the output volume. If it is negated, only those files
	;	named in the input specification are copied to the output
	;	volume.
	;
	.IFF V$EX	;	The default is to copy only the named files.
	.IFT V$EX	;	The default is to copy all but the named files.
	;
	.ENABLE DISPLAY
	.GOTO INQIEX

.HELPII:.SETL IN$IMG L$Q
	.DISABLE DISPLAY
	;
	;	If this option is asserted, the input disk is treated as
	;	a BRU image disk, not a standard FILES-11 disk. You may
	;	not select this option for both input and output disks.
	;
	.IFT IN$IMG	;	By default, this disk is in BRU image format.
	.IFF IN$IMG	;	By default, this disk is in FILES-11 format.
	;
	.ENABLE DISPLAY
	.GOTO INQIII

.HELPIO:.SETL OU$IMG L$Q
	.DISABLE DISPLAY
	;
	;	If this option is asserted, the output disk is treated as
	;	a BRU image disk, not a standard FILES-11 disk. You may
	;	not select this option for both input and output disks.
	;
	.IFT OU$IMG	;	By default, this disk is in BRU image format.
	.IFF OU$IMG	;	By default, this disk is in FILES-11 format.
	;
	.ENABLE DISPLAY
	.GOTO INQIIO

.HELPIN:.SETN V$IN N$Q
	.SETD V$IN
	.DISABLE DISPLAY
	;
	;	This option determines the initial size of the index file
	;	to create on the output disk. This is the number of files
	;	that can be placed on the disk before the index file is
	;	extended.
	;
	;	The default is an initial index file to hold 'V$IN'. files.
	;
	.ENABLE DISPLAY
	.GOTO INQIIN

.HELPMX:.SETN V$MX N$Q
	.SETD V$MX
	.DISABLE DISPLAY
	;
	;	This option determines the maximum number of files that
	;	can be placed on the output disk.
	;
	;	The default is a maximum of 'V$MX'. files.
	;
	.ENABLE DISPLAY
	.GOTO INQIMX


.HELPOP:.SETL L$OP L$Q
	.DISABLE DISPLAY
	;
	;	This option must be asserted if you want to change the
	;	run parameters that have been selected for you.
	;
	.IFF L$OP	;	The default is to accept the selected run parameters.
	.IFT L$OP	;	The default is to allow you to change the selected run parameters.
	;
	.ENABLE DISPLAY
	.GOTO INQIOP

.HELPPR:.SETL V$PR L$Q
	.DISABLE DISPLAY
	;
	;	This option must be asserted if you wish to preserve the
	;	File ID and Sequence numbers on the output disk. If you
	;	do not preserve these, the output disk will not be bootable.
	;
	.IFF V$PR	;	The default is not to preserve the File ID and sequence numbers.
	.IFT V$PR	;	The default is to preserve the File ID and sequence numbers.
	;
	.ENABLE DISPLAY
	.GOTO INQIPR

.HELPRW:.SETL V$RW L$Q
	.DISABLE DISPLAY
	;
	;	If this option is asserted, the tape will be rewound
	;	before BRU runs. If it is not asserted, input will
	;	be from the current tape position, and output will be
	;	appended to the tape.
	;
	.IFF V$RW	;	The default is not to rewind the tape.
	.IFT V$RW	;	The default is to rewind the tape.
	;
	.ENABLE DISPLAY
	.GOTO INQIRW

.HELPDE:.SETN V$DE N$Q
	.SETD V$DE
	.DISABLE DISPLAY
	;
	;	This option specifies the density of the magtape.
	;	This must be selected from the list
	;		'S$DENS'
	.IF S$DRVR = IN$DEV[1:2]	;	where "0" signifies "use the input tape density".
	;
	.IF V$DE = 0	;	The default is the actual input tape density
	.IF V$DE <> 0	;	The default is 'V$DE%D' bpi
	;
	.ENABLE DISPLAY
	.GOTO INQIDE

.HELPUF:.SETL V$UF L$Q
	.DISABLE DISPLAY
	;
	;	If you assert this option, BRU will create directories on
	;	the output device as necessary to hold the files copied.
	;	If you do not assert this option, files cannot be copied
	;	unless their directories already exist on the output disk.
	;
	.IFF V$UF	;	The default is not to create directories.
	.IFT V$UF	;	The default is to create directories.
	;
	.ENABLE DISPLAY
	.GOTO INQIUF

.;	.SETF V$VE		! Do not verify volume after copy.

.HELPVE:.SETL V$VE L$Q
	.DISABLE DISPLAY
	;
	;	If you assert this option, a verify pass will be made
	;	to insure that the input and output media contain
	;	identical data. Note that the verify pass may fail
	;	if one of the devices is a disk mounted for FILES-11
	;	access, and available to be written on. The probability
	;	of failure ranges from low to certain, depending on the
	;	nature of the BRU operation and the disk activity.
	;
	.IFF V$VE	;	The default is not to make a verify pass.
	.IFT V$VE	;	The default is to make a verify pass.
	;
	.ENABLE DISPLAY
	.GOTO INQIVE

.;
.;======================================================================
.;
.;	The following are general purpose subroutines to calculate the
.;	absolute maximum file headers on a volume, and to calculate the
.;	default initial index file allocation for a given maximum number
.;	of headers. All this was lifted from INIPAR.MAC
.;
.;
.;
.;	MAX - Calculate the absolute and default maximum headers.
.;
.;		.GOSUB MAX var lim dev
.;		    Where -
.;			var returns the default (if not previously
.;				defined, or if set to 0)
.;			lim returns the absolute limit
.;			dev is the device name (with trailing colon).
.;
.;		The curious reader is referred to INIPAR.MAC
.;		for the validity of this algorithm.
.;
.MAX:	.PARSE COMMAN " " ST$X ST$Y ST$Z SB$JNK
	.TESTDEVICE 'ST$Z'
	.PARSE <EXSTRI> "," SB$JNK SB$UC1 SB$UC2 SB$UC3 SB$UC4 SB$JNK
	.SETN OB$IL1 'SB$UC2'&377
	.SETN OB$IL0 'SB$UC3'
	.GOSUB VLOD OB$TM OB$IL
	.GOSUB CADD OB$TM 7777
	.GOSUB CDIV OB$TM 10000
	.GOSUB CADD OB$TM 11
	.GOSUB VSUB OB$IL OB$TM
	.GOSUB CMUL OB$IL 177
	.GOSUB CDIV OB$IL 402
	.SETN 'ST$Y' N$LIMI
	.IF OB$IL1 = 0 .IF OB$IL0 < N$LIMI	.SETN 'ST$Y' OB$IL0
	.IFNDF 'ST$X'	.SETN 'ST$X' 0.
	.SETN OB$ID1 OB$IL1/10
	.SETN OB$ID0 OB$IL0/10+(OB$IL1&7*20000)
	.IF OB$ID1 <> 0 .OR .IF OB$ID0 > 'ST$Y'	.SETN OB$ID0 'ST$Y'
	.IF 'ST$X' = 0		.SETN 'ST$X' OB$ID0
	.IF 'ST$X' < 16.	.SETN 'ST$X' 16.
	.SETD 'ST$X'
	.SETD 'ST$Y'
	.RETURN

.;
.;	INI - Calculate the default initial number of headers
.;
.;		.GOSUB INI var max
.;		    Where -
.;			var is the name of the symbol to return the result in
.;			max is the maximum number of headers
.;
.;		The curious reader is referred to INIPAR.MAC
.;		for the validity of this algorithm.
.;
.INI:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B
	.SETN OT$B0 'ST$B'
	.SETN OT$A0 OT$B0/2
	.IF OT$A0 < 100.	.GOTO INISML
	.IF OT$B0 <= N$1HDR	.GOTO INIDSP
	.SETN OT$C0 N$2HDR
	.IF OT$B0 > N$2HDR	.GOTO INIBIG
	.SETN OT$C0 N$1HDR
	.IF OT$A0 >= N$1HDR	.GOTO INIDSP
.INIBIG:.SETN OT$A0 OT$C0
	.SETN OT$C0 OT$B0-OT$A0
	.IF OT$C0 < 5	.SETN OT$A0 OT$A0+5-OT$C0
	.GOTO INIDSP
.INISML:.SETN OT$A0 16.
.INIDSP:.SETN 'ST$A' OT$A0
	.RETURN

.;
.;======================================================================
.;
.;	The following are a set of general-purpose double-precision
.;	arithmetic routines. All arithmetic is unsigned. Double
.;	precision variable "xxxxx" (5 characters maximum) is represented
.;	by the pair of single-precision numeric symbols "xxxxx0" and
.;	"xxxxx1". Constants are represented by nnnnnn (octal or decimal,
.;	16 bits max) if the constant has a single-precision value, or
.;	mmmmmm,nnnnnn (each part either octal or decimal, and 16 bits
.;	max) for a double precision value, with "mmmmmm" being the most
.;	significant part, and "nnnnnn" the least significant part. The
.;	enterprising person should be able to extend the precision
.;	arbitrarily, but with a corresponding decrease in performance.
.;
.;
.;
.;	D2V - Convert a decimal number into a variable.
.;
.;		.GOSUB D2V variable number
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			number is a decimal number (no decimal point).
.;
.D2V:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$X ST$Y
	.SETN 'ST$X'0 0
	.SETN 'ST$X'1 0
.D2VLP:
	.GOSUB CMUL 'ST$X' 10.
	.SETS ST$JNK ST$Y[1:1]
	.SETS ST$Y ST$Y[2:*]
	.GOSUB CADD 'ST$X' 'ST$JNK'.
	.IF ST$Y <> ""	.GOTO D2VLP
	.SETO 'ST$X'0
	.SETO 'ST$X'1
	.RETURN

.;
.;	V2D - Convert a variable to a decimal number.
.;
.;		.GOSUB V2D variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;		The result comes back in <EXSTRI>, without a decimal
.;			point.
.;
.V2D:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$X ST$Y
	.SETN OT$X0 'ST$X'0
	.SETN OT$X1 'ST$X'1
	.SETS ST$Y ""
.V2DLP:
	.GOSUB CDIV OT$X|OT$Y 10.
	.SETS ST$Y "'OT$Y0%D'"+ST$Y
	.IF OT$X0 <> 0	.GOTO V2DLP
	.IF OT$X1 <> 0	.GOTO V2DLP
	.SETS <EXSTRI> ST$Y
	.RETURN

.;
.;	CLOD - Load a variable from a constant.
.;
.;		.GOSUB CLOD variable constant
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			constant is a double-precision constant of the
.;				form [high part,]low part
.;
.CLOD:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.PARSE ST$B "," ST$B ST$C ST$JNK
	.SETN 'ST$A'0 'ST$B'
	.SETN 'ST$A'1 0'ST$C'
	.RETURN

.;
.;	VLOD - Load a variable from another variable
.;
.;		.GOSUB VLOD variable variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable
.;
.VLOD:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B
	.SETN 'ST$A'0 0
	.SETN 'ST$A'1 0
	.IFDF 'ST$B'0	.SETN 'ST$A'0 'ST$B'0
	.IFDF 'ST$B'1	.SETN 'ST$A'1 'ST$B'1
	.RETURN

.;
.;	VADD - Add a variable to another variable.
.;
.;		.GOSUB VADD variable variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable
.;
.VADD:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.SETN OT$B0 'ST$B'0
	.SETN OT$B1 'ST$B'1

	.GOTO XADD

.;
.;	CADD - Add a constant to a variable.
.;
.;		.GOSUB CADD variable constant
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			constant is a double-precision constant of the
.;				form [high part,]low part
.;
.CADD:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B
	.PARSE ST$B "," ST$B ST$C ST$JNK

	.SETN OT$B0 'ST$B'
	.SETN OT$B1 0'ST$C'

	.GOTO XADD

.XADD:					.; Common code for addition.

	.; Separate the first addend into its constituent bytes.

	.SETN OT$A0 'ST$A'0&377
	.SETN OT$A1 'ST$A'0/400&377
	.SETN OT$A2 'ST$A'1&377
	.SETN OT$A3 'ST$A'1/400&377

	.; Separate the second addend into its constituent bytes.

	.SETN OT$B3 OT$B1/400&377
	.SETN OT$B2 OT$B1&377
	.SETN OT$B1 OT$B0/400&377
	.SETN OT$B0 OT$B0&377

	.; Add the corresponding bytes of the two addends, with carry.

	.SETN OT$C0 OT$A0+OT$B0
	.SETN OT$C1 OT$A1+OT$B1+(OT$C0/400&377)
	.SETN OT$C2 OT$A2+OT$B2+(OT$C1/400&377)
	.SETN OT$C3 OT$A3+OT$B3+(OT$C2/400&377)

	.; Strip out the carry bits.

	.SETN OT$C0 OT$C0&377
	.SETN OT$C1 OT$C1&377
	.SETN OT$C2 OT$C2&377
	.SETN OT$C3 OT$C3&377

	.; Reconstitute the sum.

	.SETN 'ST$A'0 OT$C1*400+OT$C0
	.SETN 'ST$A'1 OT$C3*400+OT$C2

	.RETURN

.;
.;	VSUB - Subtract a variable from another variable.
.;
.;		.GOSUB VSUB variable variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable
.;
.VSUB:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.SETN OT$B0 'ST$B'0
	.SETN OT$B1 'ST$B'1

	.GOTO XSUB

.;
.;	CSUB - Subtract a constant from a variable.
.;
.;		.GOSUB CSUB variable constant
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			constant is a double-precision constant of the
.;				form [high part,]low part
.;
.CSUB:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.SETN OT$B0 'ST$B'
	.SETN OT$B1 0'ST$C'

	.GOTO XSUB

.XSUB:					.; Common code for subtraction.

	.; Separate the subtrahend into its constituent bytes.

	.SETN OT$A0 'ST$A'0&377
	.SETN OT$A1 'ST$A'0/400&377
	.SETN OT$A2 'ST$A'1&377
	.SETN OT$A3 'ST$A'1/400&377

	.; Separate the subtractor into its constituent bytes.

	.SETN OT$B3 OT$B1/400&377
	.SETN OT$B2 OT$B1&377
	.SETN OT$B1 OT$B0/400&377
	.SETN OT$B0 OT$B0&377

	.; Subtract the subtractor from the subtrahend, with borrow.

	.SETN OT$C0 OT$A0+400-OT$B0
	.SETN OT$C1 OT$A1+377-OT$B1+(OT$C0/400&377)
	.SETN OT$C2 OT$A2+377-OT$B2+(OT$C1/400&377)
	.SETN OT$C3 OT$A3+377-OT$B3+(OT$C2/400&377)

	.; Strip off the borrowed bits.

	.SETN OT$C0 OT$C0&377
	.SETN OT$C1 OT$C1&377
	.SETN OT$C2 OT$C2&377
	.SETN OT$C3 OT$C3&377

	.; Reconstitute the difference.

	.SETN 'ST$A'0 OT$C1*400+OT$C0
	.SETN 'ST$A'1 OT$C3*400+OT$C2

	.RETURN

.;
.;	VMUL - multiply a variable by another variable.
.;
.;		.GOSUB VMUL variable variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable
.;
.VMUL:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.SETN OT$B0 'ST$B'0
	.SETN OT$B1 'ST$B'1

	.GOTO XMUL

.;
.;	CMUL - Multiply a constant by a variable.
.;
.;		.GOSUB CMUL variable constant
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			constant is a double-precision constant of the
.;				form [high part,]low part
.;
.CMUL:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B
	.PARSE ST$B "," ST$B ST$C ST$JNK

	.SETN OT$B0 'ST$B'
	.SETN OT$B1 0'ST$C'

	.GOTO XMUL

.XMUL:					.; Common code for multiplication.

	.; Separate the multiplicand into its constituent bytes.

	.SETN OT$A0 'ST$A'0&377
	.SETN OT$A1 'ST$A'0/400&377
	.SETN OT$A2 'ST$A'1&377
	.SETN OT$A3 'ST$A'1/400&377

	.; Separate the multiplier into its constituent bytes.

	.SETN OT$B3 OT$B1/400&377
	.SETN OT$B2 OT$B1&377
	.SETN OT$B1 OT$B0/400&377
	.SETN OT$B0 OT$B0&377

	.; Form the cross products that make up the first byte of the
	.; product (and contribute to the second).

	.SETN OT$C99 OT$A0*OT$B0
	.SETN OT$C0 OT$C99&377
	.SETN OT$C1 OT$C99/400&377

	.; Form the cross products that make up the second byte of the
	.; product (and contribute to the third).

	.SETN OT$C99 OT$A1*OT$B0
	.SETN OT$C1 OT$C99&377+OT$C1
	.SETN OT$C2 OT$C99/400&377
	.SETN OT$C99 OT$A0*OT$B1
	.SETN OT$C1 OT$C99&377+OT$C1
	.SETN OT$C2 OT$C99/400&377+OT$C2+(OT$C1/400&377)
	.SETN OT$C1 OT$C1&377

	.; Form the cross products that make up the third byte of the
	.; product (and contribute to the fourth).

	.SETN OT$C99 OT$A2*OT$B0
	.SETN OT$C2 OT$C99&377+OT$C2
	.SETN OT$C3 OT$C99/400&377
	.SETN OT$C99 OT$A1*OT$B1
	.SETN OT$C2 OT$C99&377+OT$C2
	.SETN OT$C3 OT$C99/400&377+OT$C3
	.SETN OT$C99 OT$A0*OT$B2
	.SETN OT$C2 OT$C99&377+OT$C2
	.SETN OT$C3 OT$C99/400&377+OT$C3+(OT$C2/400&377)
	.SETN OT$C2 OT$C2&377

	.; Form the cross products that make up the fourth byte of the
	.; product (and would contribute to the fifth, if we were
	.; carrying the operation so far).

	.SETN OT$C99 OT$A3*OT$B0
	.SETN OT$C3 OT$C99&377+OT$C3
.;	.SETN OT$C4 OT$C99/400&377
	.SETN OT$C99 OT$A2*OT$B1
	.SETN OT$C3 OT$C99&377+OT$C3
.;	.SETN OT$C4 OT$C99/400&377+OT$C4
	.SETN OT$C99 OT$A1*OT$B2
	.SETN OT$C3 OT$C99&377+OT$C3
.;	.SETN OT$C4 OT$C99/400&377+OT$C4
	.SETN OT$C99 OT$A0*OT$B3
	.SETN OT$C3 OT$C99&377+OT$C3
.;	.SETN OT$C4 OT$C99/400&377+OT$C4+(OT$C3/400&377)
	.SETN OT$C3 OT$C3&377

	.; Reconstitute the product.

	.SETN 'ST$A'0 OT$C1*400+OT$C0
	.SETN 'ST$A'1 OT$C3*400+OT$C2

	.RETURN

.;
.;	VDIV - Divide a variable into another variable.
.;
.;		.GOSUB VDIV variable variable
.;		    Where
.;			variable is the name of a double-precision
.;				variable
.;
.VDIV:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B

	.SETN OT$B0 'ST$B'0
	.SETN OT$B1 'ST$B'1

	.GOTO XDIV

.;
.;	CDIV - Divide a constant into a variable.
.;
.;		.GOSUB CDIV variable constant
.;		    Where
.;			variable is the name of a double-precision
.;				variable, and
.;			constant is a double-precision constant of the
.;				form [high part,]low part
.;
.CDIV:
	.SETS COMMAN "'COMMAN%C'"
	.PARSE COMMAN " " ST$A ST$B
	.PARSE ST$B "," ST$B ST$C ST$JNK

	.SETN OT$B0 'ST$B'
	.SETN OT$B1 0'ST$C'

	.GOTO XDIV

.XDIV:					.; Common code for Division.

	.; Isolate the variable name that is to hold the remainder.

	.PARSE ST$A "|" ST$A ST$C

	.; Separate the dividend into its constituent bytes.

	.SETN OT$A0 'ST$A'0&377
	.SETN OT$A1 'ST$A'0/400&377
	.SETN OT$A2 'ST$A'1&377
	.SETN OT$A3 'ST$A'1/400&377

	.; Separate the divisor into its constituent bytes.

	.SETN OT$B3 OT$B1/400&377
	.SETN OT$B2 OT$B1&377
	.SETN OT$B1 OT$B0/400&377
	.SETN OT$B0 OT$B0&377

	.; Clear the remainder, and the shift counter.

	.SETN OT$C0 0
	.SETN OT$C1 0
	.SETN OT$C2 0
	.SETN OT$C3 0
	.SETN OT$C99 0

.CDIVL:
	.; Compare the dividend to the divisor.

	.IF OT$B3 > OT$A3	.GOTO CDIVLX
	.IF OT$B3 < OT$A3	.GOTO CDIVLS
	.IF OT$B2 > OT$A2	.GOTO CDIVLX
	.IF OT$B2 < OT$A2	.GOTO CDIVLS
	.IF OT$B1 > OT$A1	.GOTO CDIVLX
	.IF OT$B1 < OT$A1	.GOTO CDIVLS
	.IF OT$B0 > OT$A0	.GOTO CDIVLX

.CDIVLS:.;

	.; Increment the shift counter.

	.INC OT$C99

	.; Shift the divisor one bit to the left.

	.SETN OT$B0 (OT$B0*2)
	.SETN OT$B1 (OT$B1*2)+(OT$B0/400&377)
	.SETN OT$B2 (OT$B2*2)+(OT$B1/400&377)
	.SETN OT$B3 (OT$B3*2)+(OT$B2/400&377)

	.SETN OT$B0 OT$B0&377
	.SETN OT$B1 OT$B1&377
	.SETN OT$B2 OT$B2&377
	.SETN OT$B3 OT$B3&377

	.GOTO CDIVL

.CDIVLX:.IF OT$C99 = 0	.GOTO CDIVXX

.CDIVR:
	.; Shift the quotient one bit to the left.

	.SETN OT$C0 (OT$C0*2)
	.SETN OT$C1 (OT$C1*2)+(OT$C0/400&377)
	.SETN OT$C2 (OT$C2*2)+(OT$C1/400&377)
	.SETN OT$C3 (OT$C3*2)+(OT$C2/400&377)

	.SETN OT$C0 OT$C0&377
	.SETN OT$C1 OT$C1&377
	.SETN OT$C2 OT$C2&377
	.SETN OT$C3 OT$C3&377

	.; Shift the divisor one bit to the right.

	.SETN OT$B0 (OT$B0/2)+(OT$B1&1*200)
	.SETN OT$B1 (OT$B1/2)+(OT$B2&1*200)
	.SETN OT$B2 (OT$B2/2)+(OT$B3&1*200)
	.SETN OT$B3 (OT$B3/2)

	.; Compare the divisor to the dividend.

	.IF OT$B3 > OT$A3	.GOTO CDIVRX
	.IF OT$B3 < OT$A3	.GOTO CDIVRS
	.IF OT$B2 > OT$A2	.GOTO CDIVRX
	.IF OT$B2 < OT$A2	.GOTO CDIVRS
	.IF OT$B1 > OT$A1	.GOTO CDIVRX
	.IF OT$B1 < OT$A1	.GOTO CDIVRS
	.IF OT$B0 > OT$A0	.GOTO CDIVRX

	.; If the divisor is smaller than the dividend,
	.; Increment the quotient, and

.CDIVRS:.SETN OT$C0 OT$C0+1

	.; Subtract the divisor from the dividend.

	.SETN OT$A0 OT$A0+400-OT$B0
	.SETN OT$A1 OT$A1+377-OT$B1+(OT$A0/400&377)
	.SETN OT$A2 OT$A2+377-OT$B2+(OT$A1/400&377)
	.SETN OT$A3 OT$A3+377-OT$B3+(OT$A2/400&377)

	.SETN OT$A0 OT$A0&377
	.SETN OT$A1 OT$A1&377
	.SETN OT$A2 OT$A2&377
	.SETN OT$A3 OT$A3&377

	.; Loop until we have shifted the divisor back to its 
	.; original position.

.CDIVRX:.DEC OT$C99
	.IF OT$C99 > 0	.GOTO CDIVR

.CDIVXX:.;

	.; Reconstitute the quotient.

	.SETN 'ST$A'0 OT$C1*400+OT$C0
	.SETN 'ST$A'1 OT$C3*400+OT$C2

	.IF ST$C = ""	.RETURN

	.; If desired, reconstitute the remainder.

	.SETN 'ST$C'0 OT$A1*400+OT$A0
	.SETN 'ST$C'1 OT$A3*400+OT$A2

	.RETURN
