Commodore 1541 DOS ROM Disassembly

Revision 03 (901229-03)

Title:		Commodore 1541 ROM Disassembly (Text)
Compiled By:	mark@faime.demon.co.uk
Updated:	07/08/04

		-----------------------------------------
		Turn on drive-active LED:
SETLDA	$C100	Set bit 3 of DSKCNT ($1000) to turn on
		LED for the current drive (DRVNUM; $7F).
		-----------------------------------------
		Turn on drive-active LED:
LEDSON	$C118	Set bit 3 of DSKCNT ($1C00) to turn on
		drive active LED for drive O.
		-----------------------------------------
		Turn off error LED:
ERROFF	$C123	Store $00 in ERWORD ($026C) and in ERLED
		($026D) to clear any error status and
		turn off drive-active/error LED.
		-----------------------------------------
		Turn on error LED:
ERRON	$C12C	Store $80 in ERWORD ($026C) to ensure
		LED will continue to flash and set bit
		3 of DSKCNT to turn the LED on using
		the LED mask from LEDMSK ($FECA).
		-----------------------------------------
		Parse string in command buffer:
PARSXQ	$C146	Clear the "don't write BAM" flag, WBAM
    		($02F9) and move the drive number of the
		last successful job from LSTDRV ($028E)
		($028E) to DRVNUM ($7F). This makes the
		last used drive the default drive.
	$C150	JSR to OKERR ($E6BC) to clear any errors
		and move the OK error message into the
		error buffer.
	$C153	Check if the command's secondary address
		(ORGSA; $84) was $0F (command channel).
	$C15D	If it was not $0F, exit with a JMP to
		OPEN ($D7B4).
PS5	$C160	If the secondary address was $0F, JSR to
		CMDSET ($C2B3) to interpret the command
		and set up the necessary variables and
		registers (on return .Y=O).
	$C163	Move first character of command from the
		command buffer ($0200) to CHAR ($0275).
PS10	$C16A	Search the command table (CMDTBL; $FE89)
    		for this character. If not found, exit
		by loading .A with a #$31 (BAD COMMAND)
		and jumping to the command level error
		handler (CMDERR; $C1C8).
PS20	$C17A	If found, store the command's position
		in the table (the command number) into
		CMDNUM ($022A). Check if this command
		must be parsed by comparing the command
		number with $09. If parsing is required
		(NEW, RENAME, SCRATCH, COPY, & LOAD),
	$C181	JSR to TAGCMD ($C1EE) to set tables,
		pointers and patterns.
PS30	$C184	Move the address of the appropriate ROM
		routine from the tables, CJUMPL ($FE95)
		and CJUMPH ($FEA1) into $6F/$70 (TEMP).
		Exit with an indirect JMP to the routine
		via the vector at TEMP ($6F).
		-----------------------------------------
		Terminate command successfully:
ENDCMD	$C194	Clear the "don't write BAM" flag, WBAM
		($02F9). Load .A with the error status
		from ERWORD ($026C). If non-zero, an
		error has occurred so exit with a JMP
		to CMDERR ($C1C8).
SCREND	$C1A3	If command completed with no errors, set
		TRACK ($80), SECTOR ($81), and the
		pointer into the command buffer, CB($A3)
		to $00. JSR to ERRMSG ($E6C7) and ERROFF
		($C123) to clear any error status.
SCREN1	$C1AD	Move current drive number from DRVNUM
		($7F) to last used drive number, LSTDRV
		($028E). Set the drive-busy flag, NODRV
		($FF) to $00 to indicate that the drive
		is inactive. JSR to CLRCB ($C1BD) to
		zero the command buffer. JMP to FREICH
		($D4DA) to clear the internal channel.
		-----------------------------------------
		Clear the command buffer ($0200-$0228):
CLRCB	$C1BD	Erase any old command information by
		overwriting the old command with $00.
		-----------------------------------------
		Command level error handling:
CMDERR	$C1C8	Set TRACK ($80) and SECTOR ($81) to $00
		and JMP to CMDER2 ($E645).
		-----------------------------------------
		Simple parser:
SIMPRS	$C1D1	Initialize .X and the file table pointer
		FILTBL ($027A) to $00. Load .A with a
		$3A (:) and JSR to PARSE ($C268) to scan
		the command string for a colon.
	$C1DB	On return Z=1 if ":" found and .Y points
		to its position in the command. If not
		found, leave FILTAB=$00 and exit. If ":"
		was found, set FILTAB= (":" position - 1)
		and exit. All exits are with a JMP to
		SETANY ($C368) to set the drive number.
		-----------------------------------------
		Find colon (:) in command string:
PRSCLN	$C1E5	Load .X and .Y with $00 and .A with $3A
		(:) and JMP to PARSE ($C268).
		-----------------------------------------
		Tag command string, set up CMD structure
		and file stream pointers:
		-----------------------------------------
		COMMAND STRUCTURE (Bit mapped)
		-----------------
		The disk commands, RENAME, SCRATCH, NEW,
		and LOAD, are analyzed by this routine
		to determine the command structure. As
		the command is parsed, bits in IMAGE
		($028B) are set or cleared to indicate
		the presence or absence of various parts
		of the command. Once the command has
		been analyzed, its structure image is
		checked against the correct structure
		for that command given in STRUCT($FEA5+)
		-----------------------------------------
		Bit Name Meaning
		--- --- -------------------------------
		7   P1  Wild cards present (Y=l)
		6   G1  More than one file implied (Y=1)
		5   Dl  Drive # specified (not default)
		4   N1  Filenamel given
		3   P2  Wild cards present (Y=1)
		2   G2  More than one file implied (Y=1)
		1   D2  Drive # specified (not default)
		0   N2  Filename2 given

		NOTE: Bits 7-4 refer to file #1
		      Bits 3-0 refer to file #2
		-----------------------------------------
TAGCMD	$C1EE	JSR to PRSCLN ($C1E5) to locate the
		position of the colon (:) that is a
		necessary part of all these commands.
		e.g. RO:NEWNAME=OLDNAME (Rename)
TC25	$C1F3	If no colon was found, load .A with $34
		to indicate a bad command and exit with
		a JMP to CMDERR ($C1C8).
TC30	$C1F8	If a colon was found, set FILTAB to the
		colon position - 1.
	$C1FD	Check if a comma was found before the
		colon (.X > 0 on return from PARSE).
	$C1FE	If a comma was found, the syntax is bad
		so exit via TC25 ($C1F3).
TC35	$C200	Load .A with $3D (=) and JSR to PARSE
		($C268). On return .X=0 indicates that
		no wild-card characters (? or *) were
		found. If any were found, set bit 6 (G1)
		of IMAGE ($028B) to indicate that the
		command applies to more than one file.
TC40	$C20A	In all cases, set bit 5 (Dl) of IMAGE
		to indicate that a drive # is present
		and set bit 0 (N2) to indicate that a
		second file name is given (fixed later)
		-----------------------------------------
	$C20F	Increment .X and use it to set the
		lengths of filenames 1 and 2, F1CNT and
		F2CNT ($0277/8). Filename 2 will default
		to the same length as filename 1.
	$C216	Check if PARSE found any wild cards by
		loading PATFLG ($028A). If any found,
		set bit 7 (P1) of IMAGE ($028B).
	$C223	Set pattern flag, PATFLG ($028A) to $00
		to prepare for parsing the rest of the
		command.
TC50	$C228	Check if there is any command left to
		parse by checking the value of .Y set by
		PARSE. If .Y=O, nothing left so branch
		to TC75 ($C254) to check structure.
	$C22B	Store value from .Y in filetable, FILTBL
		($027A),X. Set the pointer to the start
		of filename #2, F2PNT ($0279) from the
		current value of F1CNT ($0277).
	$C234	Load .A with $8D (shifted CR) and JSR to
		PARSE ($C268) to parse the rest of the
		command. On return increment .X so it
		points to the end of the string and put
		the value into F2CNT ($0278). Decrement
		the value of .X to restore its former
		value.
	$C23E	Check if any wild cards were found by
		PARSE in filename 2 by checking the
		pattern flag, PATFLG ($028A). If any
		were found, set 3 (P2) of IMAGE ($028B).
TC60	$C245	Check if there was a second filename by
		checking if .X = F1CNT. If second file
		name is only 1 chr long, branch to TC70.
	$C24A	Set bit 2 to indicate that the command
		implies more than one second file name.
TC70	$C24C	Set bit 1 to indicate that a second
		drive is specified and hit 0 to indicate
		that a second file name is given. FOR
		this with IMAGE (clears bit 0) and store
		the result back into IMAGE ($028B).
TC75	$C254	Check IMAGE against the entry for that
		command (CMD number from CMDNUM, $022A)
		in the structure table, STRUCT ($FEA5+)
		If match, syntax is OK; exit with an RTS
TC80	$C260	Store IMAGE in ERWORD ($026C). Load .A
		with a $30 to indicate a bad syntax and
		exit with a JMP to CMDERR ($C1C8).
		-----------------------------------------
		Parse string:
		On entry, .A contains the character to
		be found in the string, .Y points to the
		the character in the string where the
		scan is to start, and .X points into the
		file table, FILTAB,X.
		The routine scans the string for special
		characters "*", "?", and "," as well as
		the desired character. In scanning the
		string .Y is used as a pointer to the
		character in the command string being
		examined and .X is a pointer into the
		file table, FILTAB ($027B+) for storing
		the positions (.Y value) of the start &
		end of file names that are found. When a
		wild card (* or ?) is found, the pattern
		flag PATFLG ($028A) is incremented. When
		a comma is found, its position is noted
		in the file table, FILTAB and a check is
		made to ensure that not too many file
		names are present.
		When the special character is found or
		the end of the command is reached, the
		routine ends. If no wild cards have been
		found, the pattern type, PATTYP,X is set
		to $80. Otherwise it is left unchanged.
		On exit, .Y=0 and the Z flag =0 if the
		desired character has not been found. If
		it has been found, .Y = the position of
		the character and the Z flag is set.

PARSE	$C268	Store the desired character in CHAR
		($0275).
PR10	$C26B	Start of loop using .Y as a counter to
		scan the command string. If .Y is
		greater than or equal to the length of
		the command string, CMDSIZE ($0274),
		branch to PR30 ($C29E).
	$C270	Load command string character into .A
		and increment .Y counter. Check if it is
		the desired character. If it is, branch
		to PR35 ($C2A0).
	$C278	Check if it is a wild card ("*" or "?").
		If not, branch to PR25 ($C283).
PR20	$C280	Increment the pattern flag, PATFLG
		($028A) to count the if of wild cards.
PR25	$C283	Check if it is a comma (","). If not,
		branch back to PR10 to get next command
		string character.
	$C287	Transfer character count from .Y to .A
		and store in the file table, FILTAB+1,X
		($027B,X) to indicate where the file
		name ends. Load .A with the pattern flag
		PATFLG and AND it with $7F. If the
		result is zero (no wild cards found),
		branch to PR28.
	$C292	Wild cards were present, so store $80
		in PATTYP,X ($E7,X) to indicate this.
		Also store $80 into PATFLG to zero the
		count of wild cards but indicate that
		there are wild cards in the string.
PR28	$C299	Increment .X (counts number of files &
		points into FILTAB) and compare it to
		$04 (the maximum number of file names
		allowed in a command string). If the
		maximum has not been exceeded, branch
		back to PR10 to continue the scan.
PR30	$C29E	Load .Y with $00 to indicate that the
		desired character was not found.
PR35	$C2A0	Store a copy of the command size, CMDSIZ
		($0274) into the file table, FILTAB+I,X
		($027B,X). Load the pattern flag, PATFLG
		and AND it with $7F. If the result is 0,
		no wild cards have been found so branch
		to PR40.
	$C2AD	Wild cards were present, so store $80
		in PATTYP,X ($E7,X) to indicate this.
PR40	$C2B1	Transfer character count from .Y to .A.
		This sets the Z flag if the desired
		character has not been found.
		-----------------------------------------
		Initialize command tables & pointers
		Find length of command string and zero
		all variables and pointers.
CMDSET	$C2B3	Load .Y from BUFTAB+CBPTR ($A3). This is
		the length of the command that was sent
		from the computer. If .Y=O, branch to
		CS08 ($C2CB).
	$C2B7	Decrement .Y and if .Y=O, branch to CS07
		($C2CA).
	$C2BA	Load .A with the character from the
		command buffer, CMDBUF,Y ($0200,Y) and
		see if it is a carriage return ($0D). If
		it is, branch to CS08 ($C2CB).
	$C2C1	Decrement .Y and load the next character
		from the command buffer. If this is a
		carriage return ($0D), branch to CS08
		($C2CB). If not, increment .Y
CS07	$C2CA	Increment .Y pointer into command buffer
CS08	$C2CB	Store length of command (.Y) in CMDSIZ
	$C2D4	($027B). Compare length (.Y) with the
		maximum allowable length ($2A) to set
		the carry flag. Load .Y with $FF. If
		command length was OK, branch to CMDRST.
		Command over-size so set command number
		($022A) to $FF, load .A with $32 to
		indicate a TOO LONG ERROR and exit with
		a JMP to CMDERR ($C1C8).
CMDRST	$C2DC	-----------------------------------------
		Zero all important variables & pointers:
		BUFTAB+CBPTR ($A3) REC    ($0258)
		FILTBL ($027A-7F)  TYPE   ($024A)
		ENTSEC ($00D8-DC)  TYPFLG ($0296)
		ENTIND ($00DD-E1)  F1PTR  ($00D3)
		FILDRV ($00E2-E6)  F2PTR  ($0279)
		PATTYP ($00E7-EB)  PATFLG ($028A)
		FILTRK ($0280-84)  ERWORD ($026C)
		FILSEC ($0285-89)
		-----------------------------------------
		Set first drive & table pointers:
ONEDRV	$C312	Change pointer to end of the first file
		name (F1CNT; $0277) to point to the end
		of the second file name (use value from
		F2CNT; $0278). Store $01 in F2CNT and in
		F2PTR ($0279) to clear these variables
		-----------------------------------------
		Set up all drives from F2CNT:
ALLDRS	$C320	Load .Y with last drive used from LSTDRV
		($028E) and .X with $00.
AD10	$C325	Save .X into F1PTR ($D3). Load .A from
		FILTAB,X ($027A,X) so it points to the
		start of the Xth file specified in the
		command string.
	$C32A	JSR to SETDRV ($C33C) to set drive #.
		On return .Y contains the drive number
		specified in the command or the default.
		NOTE: Bits represent drives (If bit 7
		set, use default. Bit 0 = drive #0/1)
	$C32D	Recover .X pointer from F1PTR. Store .A
		in FILTAB,X ($027A,X). Move drive # from
		.Y to .A and store in FILDRV,X ($027A,X)
	$C335	Increment .X pointer and compare it to
		F2CNT ($0278) to see if any more files
		were specified. If more, branch back to
		AD10 to do the next one. If not, RTS
		-----------------------------------------
		Set drive # from text or default to 0
		On entry and exit .A is an index into
		the command buffer.
SETDRV	$C33C	On entry .Y is the default drive #. On
		exit it is the drive specified or the
		default drive.
	$C33C	Move pointer into command buffer from
		.A to .X
	$C33D	Load .Y with $00 to ensure that the
		1541's default drive is ALWAYS DRIVE #0
	$C33F	Load .A with $3A (:) to prepare to hunt
		for a colon (drive # is just before :).
	$C341	Check for colon in command string at
		CMDBUF+1,X ($0201,X). Picks up syntax:
		X#:FILENAME as in SO:JUNK
		If found, branch to SD40.
	$C346	Check for colon in command string at
		CMDBUF,X ($0200,X). Picks up default
		drive syntax as in S:JUNK
		If colon NOT found, branch to SD40.
	$C34B	Colon found so increment pointer (.X) so
		it points to the first character in the
		filename.
SD20	$C34C	Transfer .Y to .A to set up the default
		drive
SD22	$C34D	AND .A with $01 to ensure drive number
		in ASCII form ($30 or $31) is converted
		to $00 or $01.
SD24	$C34F	Transfer .A to .Y to restore drive if.
		Transfer .X to .A to restore index into
		command string and exit with an RTS.
		-----------------------------------------
		Set drive # from command string with the
		syntax: X#:FILENAME. On entry .X points
		to the # in the command string.
SD40	$C352	Load .A with the drive number (in ASCII)
		from CMDBUF,X ($0200,X).
	$C355	Increment .X twice so it points to the
		first character in the file name.
	$C357	Compare .A (drive number) to $30 (dr#0).
		If equal, branch back to SD22 ($C34D)
	$C35B	Compare .A (drive number) to $31 (dr#1).
		If equal, branch back to SD22 ($C34D)
		If be default drive
		not equal, must so
		branch back to SD20 ($C34C).
		-----------------------------------------
		Set drive # from command string with the
		syntax: X#,FILE or xx=FILE.
SD50	$C361	Transfer the drive number from .Y to .A.
	$C362	OR .A with $80 to set the default drive
		bit and then AND the result with $81 to
		mask off any odd bits. Branch back to
		SD24 ($C34F) to terminate routine.
		-----------------------------------------
		Set drive # from any configuration:
SETANY	$C368	Set IMAGE ($028B) to $00.
	$C36D	Load .Y from FILTBL ($027A).
SA05	$C370	Load .A with the (CB),Y character from
		the command string and JSR to TSTOV1 to
		test for a "0" or "1".
	$C371	On return .A contains $00 or $01 if the
		drive was specified. If not specified,
		.A is $80 or $81. If the drive number
		was given, branch to SA20 ($C388).
	$C377	Increment the pointer into the command
		string (.Y). Compare the pointer value
		to the command length (CMDSIZ; $0274)
		to see if we are at the end. If we are,
		branch to SA10 ($C383).
	$C37D	If not " 0" or "1", set the pointer (.Y)
		to the end of the command less one (so
		it points to the last character before
		the RETURN to pick up things like VO)
		and loop back to SA05 ($C370).
SA10	$C383	Decrement IMAGE (becomes $FF) to flag a
		default drive status and load .A with a
		$00 to ensure default to 0 on the 1541.
SA20	$C388	AND the drive number in .A with $01, and
		store the result in the current drive
		number, DRVNUM ($7F).
	$C38C	Exit with a JMP to SETLDS ($C100) to
		turn on the drive active light.
		-----------------------------------------
		Toggle drive number:
TOGDRV	$C38F	Load .A with current drive number from
		DRVNUM ($7F). FOR it with $01 to flip
		bit #0, AND it with $01 to mask off the
		bits 1-7, and store the result back in
		DRVNUM ($7F).
		-----------------------------------------
		Set pointers to one file stream and
FSISET	$C398	check type:
		Zero .Y and load .A with the pointer to
	$C39D	the end of file name 1 (F1CNT; $0277).
		Compare .A to the pointer to the end of
		file name 2 (F2CNT; $0278). If equal,
		there is no second file so branch to
		FS15 ($C3B8).
	$C3A2	Decrement F2CNT and load .Y with its
		value. Load .A with the pointer to the
		filetype in the command string from
		FILTAB,Y ($027A,Y). Transfer this value
		to .Y and use it to load the file type
		into .A from the command string (CB),Y.
FS10	$C3AC	Load .Y with $04 (the number of file
		types less 1).
	$C3B0	Loop to compare the file type in .A to
		the list of possible file types,TYPLST,Y
		When a match occurs, branch to FS15
		($C3B8). If no match found this time,
		decrement .Y and, if there are any file
		types left, loop back to FS10. NOTE: if
		no match occurs, file assumed to be DEL.
FS15	$C3B8	Transfer file type from .Y to .A and
		store in TYPFLG ($0296).
		-----------------------------------------
TSTOV1	$C3BD	Test if character in .A is ASCII 0 or 1:
		Compare .A to ASCII "0" ($30) and then
		to ASCII "1" ($31). If a match in either
		case, branch to TOV1.
		OR .A with $80 to set bit 7 to indicate
		no match was found.
TOV1	$C3C7	AND .A with $81 to convert ASCII to HEX
		and preserve bit 7.
		-----------------------------------------
		Determine optimal search for LOOKUP and
		FINFIL:
OPTSCH	$C3CA	Zero TEMP ($6F) and DRVFLG ($028D) and
		push $00 onto the stack. Load .X with
		value from F2CNT ($0278). Note: TEMP is
		the drive mask.
OS10	$C3D5	Pull .A from the stack and OR it with
		the value in TEMP ($6F). Push the result
		back onto the stack. Load .A with $01
		and store this value in TEMP. Decrement
		.X (pointer into file table). If no
		files left (.X=$FF), branch to $0S30.
	$C3E0	Load .A with the drive for the file from
		FILDRV,X ($E2,X). If this file uses the
		default drive (bit 7 set), branch to
		OS15. Do two ASL ' s on TEMP ($6F).
OS15	$C3E8	Do one LSR on .A. If drive number in .A
		was 1, the carry bit is set so branch
		back to OS10.
	$C3EB	Since drive number was 0, do one ASL on
		TEMP ($6F) and branch back to OS10.
OS30	$C3EF	Pull .A from the stack and transfer this
		value to .X. Use this value as an index
		and load .A with a value from the search
		table, SCHTBL-1,X ($C43F,X). Push this
		value onto the stack, AND it with $03,
		and store the result in DRVCNT ($028C).
		Pull the original value off the stack
		and do an ASL. If bit 7 is not set,
		branch to OS40.
	$C3FE	If bit 7 was set, load A. with the value
		from FILDRV ($E2).
OS35	$C400	AND .A with $01 and store the result in
		DRVNUM ($7F). Load .A with DRVCNT($028C)
		and if $00, only one drive is addressed
		so branch to OS60.
	$C409	JSR to AUTOI ($C63D) to check the drive
		status and initialize it if necessary.
		On return, branch to OS70 if the drive
		is ready (.A=O).
OS45	$C41B	Drive is not ready so load .A with $74
		to indicate the drive is not ready and
		JSR to CMDERR ($C1C8).
OS50	$C420	JSR to TOGDRV ($C38F) to switch drives
		and JSR to AUTOI ($C63D) to check this
		drive's status and init it if necessary.
		On return, save the processor status on
		the stack. JSR to TOGDRV to switch back
		to the first drive. On return, pull the
		status back off the stack. If the second
		drive is active, branch to OS70.
	$C42D	Since second drive is not active, set
		DRVCNT ($020C) to $00 to indicate only
		one drive addressed and branch to OS70.
OS60	$C434	JSR to AUTOI ($C63D) to check the drive
		status and initialize it if necessary.
		On return, branch to OS45 if the drive
		is NOT ready (.A<>0).
OS70	$C439	Teminate routine with a JMP to SETLDS
		($C100) to turn on the drive active LEDs
OS45	$C43C	Do a ROL on the value in .A and JMP to
		OS35 ($C400).
		-----------------------------------------
SCHTBL	$C440	Search Table
		BYTES $00, $80, $41
		BYTES $01, $01, $01, $01
		BYTES $81, $81, $81, $81
		BYTES $42, $42, $42, $42
		-----------------------------------------
LOOKUP	$C44F	Look up all files in command string in
		the directory and fill tables with info.
		JSR to OPTSCH to find optimal search
		pattern and turn on drive active LEDs.
LK05	$C452	Store $00 in DELIND ($0292), to indicate
		that we are NOT looking for a deleted or
		unused directory entry. But, for one or
		more specific file names. JSR to SRCHST
		($C5AC) to start the search process.
	$C45A	On return, branch to LK25 if a valid
		file name was found (Z flag =0)
LK10	$C45C	Since no file name was found, decrement
		DRVCNT ($028C), the number of drive
		searches to be made. If any more left
		(DRVCNT >= 0), branch to LK15.
	$C461	Since there are no more drive searches
		to be done, exit with an RTS.
LK15	$C462	Store $01 in DRVFLG ($028D) and JSR to
		TOGDRV ($C38F) to switch drives. JSR to
		SETLDS ($C100) to turn on the other LED.
		Then JMP back to LK05 to begin the
		search on the other drive.
LK20	$C470	JSR to SEARCH ($C617) to read the next
		valid file name in the directory.
	$C473	On return, branch to LK30 to abandon the
		search if a valid file name was NOT
		found (Z flag = 1).
LK25	$C475	JSR to COMPAR ($C4D8) to compare the
		list of files found with list of those
		required. On return, FOUND ($028F) is 0
		if all files have NOT been found.
	$C478	Load .A with the value from FOUND. If
		not all the files have been found yet,
		branch to LK26 to continue the search.
	$C47D	All files have been found so exit from
		the routine with an RTS.
LK26	$C47E	Load .A with the value from ENTFND
		($0253) to check if the most recent
		compare found a match. If not (.A=$FF),
		branch to LK20 to search directory for
		another valid file name. If a match was
		found, branch back to LK25 to try again.
LK30	$C485	Load .A with the value from FOUND. If
		not all the files have been found yet,
		branch to LK10 to continue the search.
	$C48A	All files found so exit with an RTS.
		-----------------------------------------
		Find next file name matching any file
		in stream & return with entry stuffed
		into tables:
FFRE	$C48B	JSR to SRRE ($C604) to set up and read
		in the next block of directory entries.
	$C48E	If no files found, branch to FF10.
	$C490	If files were found, branch to FF25.
		-----------------------------------------
FF15	$C492	Store $01 in DRVFLG ($028D) and JSR to
		TOGDRV ($C38F) to switch to the other
		drive. JSR to SETLDS ($C100) to turn on
		the new drive active light.
		Find starting entry in the directory:
FFST	$C49D	Store $00 in DELIND ($0292), to indicate
		that we are NOT looking for a deleted or
		unused directory entry. But, for one or
		more specific file names. JSR to SRCHST
		($C5AC) to start the search process.
	$C4A5	On return, branch to FF25 if a valid
		file name was found (Z flag =0)
	$C4A7	Store .A value in FOUND ($028F).
FF10	$C4AA	Load .A from FOUND ($028F). If non-zero,
		all files found so branch to FF40 & exit
	$C4AF	Since there is nothing more on this
		drive, decrement DRVCNT by 1. If any
		more drives left, branch to FF15 to try
		the other drive. If none left, do an RTS
		-----------------------------------------
		Continue scan of directory:
FNDFIL	$C4B5	JSR to SEARCH ($C617) to retrieve the
		next valid file name from the directory.
	$C4B8	On return, branch to FF10 if no more
		entries available on this drive.
FF25	$C4BA	JSR to COMPAR ($C4D8) to see if any of
		the names found match the ones needed.
	$C4BD	On return, load .X from ENTFND ($0253).
		If a match on a name was found (.X<128),
		branch to FF30 to check the file type.
		If no match found (.X>127), load .A with
		the value from FOUND($028F) to check if
		all files have been found. If not(.A=O),
		branch back to FNDFIL to load another
		name from the directory.
	$C4C7	If .A<>0, all files have been found so
		branch to FF40 and exit with an RTS.
FF30	$C4C9	Check the file type flag, TYPFLG($0296).
		If it is $00, there is no file type
		restriction so branch to FF40 and exit.
	$C4CE	Load the file pattern type from PATTYP,X
		($E7,X), AND it with the file type mask
		#$07, and compare it to the value in
		TYPFLG ($0296). If the file types do not
		match, branch back to FNDFIL to continue
		the search.
FF40	$C4D7	Terminate the routine with an RTS.
		-----------------------------------------
		Compare all file names in command list
		with each valid entry in directory.
		Any matches are tabulated.
COMPAR	$C4D8	Set the found-entry flag, ENTFND ($0253)
		to $FF and zero the pattern flag PATFLG
		($028A). JSR to CMPCHK ($C589) to check
		the file table for unfound files. If
		there are unfound files (Z flag = 1),
		branch to CP10 to begin comparing.
CP02	$C4E6	Terminate routine with an RTS.
CP05	$C4E7	JSR to CC10 ($C594) to set F2PTR ($0279)
		to point to the next file needed on this
		drive. On return, branch to CP02 to exit
		if no more files needed on this drive.
CP10	$C4EC	Load .A with the current drive number
		from DRVNUM ($7F) and FOR it with the
		drive number specified for the file,
		FILDRV,X ($E2,X). LSR the result. If the
		carry flag is clear, the drive number is
		correct for this file so branch to CP20
		to find the name in the directory list.
	$C4F3	AND the value in .A with $40 to check if
		we are to use the default drive (NOTE:
		$40 rather than $80 because of the LSR).
		If we can not use the default drive,
		branch back to CP05 to set up the next
		file name on our list of files needed.
	$C4F7	Compare DRVCNT ($028C) with $02. If
		equal, don't use default drive so branch
		back to CP05.
CP20	$C4FE	At this point we have a match on the
		drive numbers so check the directory
		entries to see if we can match a name.
		Load .A with the pointer to the position
		of the required file name from FILTBL,X
		($027A,X) and transfer this value to .X.
	$C502	JSR to FNDLMT to find the end of the
		command string. On return, load the
		pointer into the directory buffer (.Y)
		with $03 (so it points past the file
		type, track and sector) and JMP to CP33.
CP30	$C50A	Compare the .Xth character in the
		command string (the required filename)
		with the .Yth character in the directory
		buffer (the directory entry). If equal,
		branch to CP32 to set up for the next
		character.
	$C511	No exact match so check if the command
		buffer character is a "?" which will
		match any character. If not, branch to
		to CP05 to try the next file name.
	$C515	Compare the character we just used from
		the directory buffer with SAO to see if
		we've reached the end of the name. If
		we have, branch to CP05 to try the next
		file name.
CP32	$C51B	Increment .X and .Y
CP33	$C51D	Compare .X with the length of the
		command string, LIMIT ($02.76). If we are
		at the end, branch to CP34.
	$C522	Check if the new character in the file
		name, CMDBUF,X ($0200,X) is a "*". If it
		is, it matches everything so branch to
		CP40 to tabulate this match.
	$C529	If not a "*", branch to CP30 to keep on
		matching.
CP34	$C52B	Compare .Y to $13 to see if we are at
		the end of the name in the directory.
		If we are, branch to CP40 to tabulate.
	$C52F	If not at the limit, check the character
		in the directory entry name. If it isn't
		an $A0, we did not get to the end of the
		name so branch back to CP05 to try again
CP40	$C535	The filenames match so keep track of it
		by storing the pointer to the entry from
		F2PNT ($0279) into ENTFND ($0253).
	$C53B	Get the file type pattern ($80,$81,etc)
		from PATTYP,X ($E7,X), AND it with $80,
		and store it in PSTFLG.
	$C542	Get the pointer to the directory entry
		from INDEX ($0294) and store it in the
		entry index, ENTIND,X ($DD,X).
	$C547	Get the sector on track 18 on which the
		entry is stored from SECTOR ($81) and
		store it in, ENTSEC,X ($D8,X).
	$C54B	Zero .Y and load .A with the file type
		of this directory entry from (DIRBUF),Y
		($94),Y. Increment .Y. Save the type on
		the stack. AND the type with $40 to see
		if this is a locked file type, and store
		the result in TEMP ($6F). Pull the file
		type off the stack and AND it with $DF
		($FF-$20). If the result is > 127 (the
		replacement bit not set), branch to CP42
	$C55A	OR the result with $20.
CP42	$C55C	AND the result with $27 and OR it with
		the value stored in TEMP ($6F) and store
		the final result back in TEMP.
	$C562	Load .A with $80, AND .A with the file
		pattern type from PATTYP,X ($E7,X), OR
		the result with the value in TEMP ($6F),
		and store the final result back in
		PATTYP,X.
	$C56A	Load .A with the file's drive number
		from FILDRV,X ($E2,X). AND it with $80
		to preserve the default drive bit, OR it
		with the current drive number, DRVNUM
		($7F) and store the result back into
		FILDRV,X ($E2,X).
	$C572	Move the file's first track link from
		(DIRBUF),Y(.Y=1) to FILTRK,X ($0280) and
		increment .Y.
	$C578	Move the file's first sector link from
		(DIRBUF),Y(.Y=2) to FILSEC,X ($0285).
	$C57D	Check the current record length, REC
		($0258). If NOT $00, branch to CMPCHK.
	$C582	Set .Y to $15 and move the file entry's
		record size from (DIRBUF),Y to REC.
		----------------------------------------
CMPCHK	$C589	Check table for unfound files
		Set all-files-found flag, FOUND ($028F)
		to $FF. Move the number of files to test
		from F2CNT ($0278) to F2PTR ($0279).
CC10	$C594	Decrement the file count, F2PTR ($0279).
		If any files left, branch to CC15.
		If none left, exit with an RTS.
CC15	$C59A	Load .X with the number of the file to
		test from F2PTR. Load .A with the file's
		pattern type from PATTYP,X ($E7,X). If
		file has not been found yet (bit 7 is
		still set) abort search by branching to
		CC20.
		Load .A with the file's first track link
		from FILTRK,X ($0280,X). If non-zero,
		the file has been found, so branch back
		to CC10 to test the next file.
CC20	$C5A6	Load .A with $00 and store it in the
		all-files-found flag, FOUND ($028F) to
		indicate that all files have NOT been
		found and exit with an RTS.
		----------------------------------------
		Initiate search of directory:
SRCHST	$C5AC	Returns with valid entry (DELIND=0) or
		with the first deleted entry (DELIND=1)
		Load .Y with $00 and store it in DELSEC.
		($0291). Decrement .Y to $FF and store
		it in the found-an-entry flag, ENTFND
		($0253).
	$C5B5	To start search at the beginning of the
		directory, set TRACK ($80) to $12 (#18)
		(from $FE79) and SECTOR ($81) to $01.
		Also store $01 in last-sector-in-file
		flag, LSTBUF ($0293).
	$C5C1	JSR to OPNIRD ($D475) to open the
		internal channel (SA=16) for a read and
		to read in the first one or two sectors
		in the file whose T/S link is given in
		TRACK ($80) and SECTOR ($81).
SR10	$C5C4	Test LSTBUF ($0293) to see if we have
		exhausted the last sector in the
		directory file. If not (LSTBUF <> $00),
		branch to SR15.
	$C5C9	Exit with an RTS.
SR15	$C5CA	Set the file count, FILCNT ($0295) to
		$07 to indicate that there are 8 entries
		(0-7) left to examine in the buffer.
	$C5CF	Load .A with $00 and JSR to DRDBYT to
		read the first byte in the sector (the
		track link). On return store this value
		into LSTBUF ($0293). This sets LSTBUF to
		$00 if there are no more blocks left in
		in the directory file.
SR20	$C5D7	JSR to GETPNT ($D4E8) to set the
		directory pointer, DIRBUF ($94/5) to the
		data that was just read into the active
		buffer, BUFTAB,X ($99/A,X).
		----------------------------------------
		NOTE: DIRBUF does NOT point to the start of the data
		buffer ($0300, $0400,...). It points to the first
		data byte ($0302, $0402,...). As the entries are
		examined, it is update to point to the start of
		the entry ($0x02, $0x22, $0x42,...).
	$C5DA	Decrement the entry count, FILCNT and
	$C5DF	load .Y with $00 to begin examination of
	$C5E3	the first directory entry.
		Test the entry ' s file type in (DIRBUF),Y
		If non-zero, this is NOT a deleted or
		blank entry so branch to SR30.
		Process a scratched or blank entry
	$C5E8	Test DELSEC ($0291) to see if a deleted
		entry has already been found. If it has
		(DELSEC <> $00), branch to SEARCH($C617)
		This is first deleted entry so JSR to
	$C5F0	CURBLK ($DE3B) to set up the current
	$C5F8	sector in SECTOR ($81). Save the sector
		number in DELSEC ($0291).
		Load .A with the low byte of the pointer
		to the start of this entry (its position
		in the data buffer) from DIRBUF ($94).
		Load .X with the current value of DELIND
		($0292). This sets the Z flag to 1 if
		only valid entries are desired.
		Store the pointer in .A into DELIND.
		If the Z flag is set, we need valid
SR30	$C5FA	entries, not deleted ones, so branch to
	$C5FB	SEARCH to continue the search.
		We wanted a deleted entry and we found
		one so terminate routine with an RTS.
		We have found a valid entry. Check if we
	$C602	are looking for one by comparing DELIND
		($0292) to $01. If not equal, we want a
		valid entry so branch to SR50.
		If DELIND = 1, we want a deleted entry,
		not a valid one, so branch to SEARCH to
		continue the quest!
		-----------------------------------------
SRRE	$C604	Re-enter the directory search:
		Set TRACK ($80) to $12 (#18) from $FE85
	$C60E	Set SECTOR ($81) from the last directory
		sector used, DIRSEC ($0290).
		JSR to OPNIRD ($D475) to open the
	$C611	internal channel (SA=16) for a read and
		to read in the first one or two sectors
		in the file whose T/S link is given in
		TRACK ($80) and SECTOR ($81).
		Load .A with the pointer INDEX ($0294)
		that points to the start of the last
		entry we were examining and JSR to
		SETPNT ($D4C8) to set the DIRPNT ($94/5)
		to point to the start of the entry.
		-----------------------------------------
SEARCH	$C617	Continue search of entries:
		Set found-entry flag, ENTFND ($0253) to
SR40	$C621	$FF. Load .A with number of entries left
	$C629	in the buffer from FILCNT ($0295). If
		none left, branch to SR40 to get the
		next buffer of directory entries.
		There is at least one more entry left in
		this buffer so load .A with $20 (the #
		of characters in each entry) and JSR to
		INCPTR ($D1C6) to set DIRPTR ($94/5) to
		point to the start of the next entry.
		JMP to SR20 ($C5D7) to process it.
		Get next buffer of entries:
SR50	$C62F	JSR to NXTBUF ($D44D) to read in the
		next directory sector and JMP to SR10
		to begin processing it.
		We have found a valid entry so save
	$C634	how far we got and return.
	$C637	Save low byte of the pointer to the
		entry, from DIRBUF($94) in INDEX($0294).
		JSR to CURBLK ($DE3B) to store the
		sector we are checking in SECTOR ($81).
		Save the current sector number from
		SECTOR ($81) in DIRSEC ($0290) and RTS.
		-----------------------------------------
AUTOI	$C63C	Check drive for active diskette, init
		if needed. Return no drive status.
		Test auto-initialization flag, AUTOFG
	$C641	($68). If AUTOFG <> 0, auto-init is
	$C647	disabled so branch to AUTO2 ($C669).
	$C64C	Load .X with the current drive number
	$C64F	from DRVNUM ($7F). Test whether the
	$C651	diskette has been changed by doing an
	$C655	LSR on the write-protect-change flag for
	$C659	the current drive, WPSW,X ($1C/D). If
	$C65D	the carry flag, C, is clear, the disk
		has not been changed so branch to AUTO2.
		Load .A with $FF. Store this value as
		the job return code in JOBRTN ($0298).
		JSR to ITRIAL ($D00E) to do a SEEK to
		the current drive to determine if a
		diskette is present.
		Load .Y with $FF (default to true).
		Compare the value in return job code in
		.A with $02. If equal, NO SYNC was found
		so branch to AUTO1 to abort.
		Compare the value in return job code in
		.A with $03. If equal, NO HEADER was
		found so branch to AUTO1 to abort.
		Compare the value in return job code in
		.A with $0F. If equal, NO DRIVE was
		found so branch to AUTO1 to abort.
		Seems OK so load .Y with $00.
AUTO1	$C65F	Load .X with the current drive number
		DRVNUM ($7F). Transfer the value of .Y
		into .A ($00 if OK;$FF if BAD) and store
		in the current drive status, NODRV,X
		($FF,X). If status is bad (not $00),
		branch to AUTO2 to abort.
	$C666	JSR to INITDR ($D042) to initialize the
		current drive.
AUTO2	$C669	Load .A with the current no-drive status
		and terminate routine with an RTS.
		NOTE: Z flag set if all is OK.
		-----------------------------------------
		Transfer filename from CMD to buffer:
		On entry, .A = string size; .X = starting
		index in command string; .Y=buffer #
TRNAME	$C66E	Save .A (string size) on the stack.
	$C66F	JSR to FNDLMT ($C6A6) to find the limit
		of the string in the command buffer that
		is pointed to by .X.
	$C672	JSR to TRCMBF ($C688) to transfer the
		command buffer contents from .X to LIMIT
		to the data buffer whose number is in .Y
	$C675	Restore the string size into .A from the
		stack. Set the carry flag and subtract
		the maximum string size, STRSIZ ($024B).
	$C67A	Transfer the result from .A to .X. If
		the result is 0 or negative, the string
		does not need padding so branch to TN20.
	$C67F	String is short and needs to be padded
		so load .A with $A0.
TN10	$C681	Loop to pad the string in the directory
		buffer with .X $A0's.
TN20	$C687	Terminate routine with an RTS.
		-----------------------------------------
		Transfer CMD buffer to another buffer:
		.X=index to first chr in command buffer
		LIMIT=index to last chr+1 in CMD buffer
		.Y=buffer#. Uses current buffer pointer.
TRCMBF	$C688	Multiply .Y by 2 (TYA;ASL;TAY).
	$C68B	Use current buffer pointers, BUFTAB,Y
		($99/A,Y) to set the directory buffer
		pointers, DIRBUF ($94/5).
		Zero .Y (index into directory buffer)
TR10	$C697	Move character from CMDBUF,X ($0200,X)
		to (DIRBUF),Y ;($94),Y.
	$C69C	Increment .Y. If .Y equals $00, branch
		to TR20 to abort.
	$C69F	Increment .X. If .X < LIMIT ($0276)
		branch back to TR10 to do next character
TR20	$C6A5	Terminate routine with an RTS.
FNDLMT	$C6A6	Find the limit(end) of the string in the
		command buffer that is pointed to by X
		Zero the string size, STRSIZ ($024B).
FL05	$C6AD	Transfer the starting pointer from .X
		to .A and save it on the stack.
		Load .A with the Xth command string
FL10	$C6B0	character, CMDBUF,X ($0200,X).
	$C6B4	Compare the character to a ",". If they
	$C6B8	match, we're at the end. Branch to FL10.
	$C6BC	Compare the character to a "=". If they
	$C6C3	match, we're at the end. Branch to FL10.
	$C6C8	Increment STRSIZ ($024B) and .X
		Check if the string size, STRSIZ, has
		reached the maximum size of $0F (#15).
		If it has, branch to FL10 to quit.
		Compare .X to the pointer to the end of
		the command string, CMDSIZ ($0274). If
		we're NOT at the end. Branch to FL05.
		Store the .X value (the last character
	$C6CB	plus 1) into LIMIT ($0276).
	$C6CD	Pull the original .X value off the stack
		into .A and transfer it to .X
		Terminate routine with an RTS.
		-----------------------------------------
GETNAM	$C6CE	Get file entry from directory:
		(called by STDIR and GETDIR)
		Save secondary address, SA ($83) on the
	$C6D1	stack.
	$C6D4	Save the current channel#, LINDX ($82)
		on the stack.
		JSR to GNSUB ($C6DE) to get a directory
		entry using the internal read channel
		SA=$11(#17).
	$C6D7	Pull the original SA and LINDX values
	$C6DD	from the stack and reset these variables
		Terminate the routine with an RTS.
		-----------------------------------------
GNSUB	$C6DE	Get file entry subroutine:
		Set current secondary address, SA ($83)
	$C6E2	to $11 (internal read secondary address)
	$C6E5	JSR to FNDRCH ($D0EB) to find an unused
	$C6E8	read channel.
	$C6ED	JSR to GETPNT ($D4E8) to set the
		directory buffer pointer, DIRBUF ($94/5)
		from the pointer to the currently active
		buffer using values from BUFTAB ($30/1).
		Test the found entry flag, ENTFLG($0253)
		to see if there are more files. If there
		are more (ENTFLG > 127), branch to GN05.
		No more entries so test DRVFLG ($028D)
		to see if we have the other drive to do.
		If DRVFLG <> 0, branch to GN050 to do
		the other drive.
GN05	$C6F2	JSR to MSGFRE ($C806) to send the BLOCKS
GN050	$C6F5	FREE message. an RTS.
	$C6F7	Clear carry bit and exit with
		-----------------------------------------
		Test drive flag, DRVFLG ($028D). If $00,
		branch to GN10.
	$C6FC	Decrement drive flag, DRVFLG ($028D). If
		not $00, branch to GN051 to do a new
GN051	$C701	directory.
GN10	$C704	
		Decrement drive flag, DRVFLG ($028D).
	$C707	JSR to TOGDRV ($C38F) to switch drives.
		JSR to MSGFRE ($C806) to send the BLOCKS
		FREE
		message.
	$C70A	Set the carry flag and exit with a JMP
		to TOGDRV ($C38F) to switch drives.
	$C70E	Load .A with $00 and zero the hi byte of
		the number of blocks counter, NBTEMP+1
		($0273) and the drive flag DRVFLG($028D)
	$C716	JSR to NEWDIR ($C7B7) to begin a new
		directory listing.
	$C719	Set the carry flag
		and exit with an RTS.
	$C71B	Load .X with $18 (#24), the length of an
		entry in a directory listing
		e.g. 114  " PROGRAM FILENAME "  PRG
	$C71D	Load .Y with $1D, the position of the
		hi byte of the # of blocks in the file.
	$C71F	Load .A with the hi byte of the # of
		blocks in the file. Store this into the
		hi byte of the block counter, NBTEMP+1
	$C726	($0273). If zero, branch to GN12.
		Load .X with $16 (#22) the directory
GN12	$C728	length less 2.
		Decrement Y so it points to the position
	$C729	of the lo byte of the # of blocks in
		the file.
		Load .A with the lo byte of the # of
		blocks in the file. Store this into the
		lo byte of the block counter, NBTEMP
		($0272).
	$C72E	Compare .X to $16 (#22) the directory
	$C732	length less 2. If they are equal, branch
		to GN14.
		Compare .A (the lo byte of the blocks)
	$C736	with $0A (#10). If .A<10 branch to GN14
		Decrement .X (we will need less padding
	$C737	since # of blocks is at least 2 digits.
		Compare .A (the lo byte of the blocks)
	$C73B	with $64 (#100). If A<100 branch to GN14
		Decrement .X (we will need less padding
		since # of blocks is at least 3 digits.
GN14	$C73C	JSR to BLKNB ($C7AC) to clear the name
	$C73F	buffer for the next entry. On return Y=0
	$C742	Load .A with the file type from the
	$C743	directory buffer (DIRBUF),Y and save the
		file type onto the stack.
		Do an ASL of the value in .A to set the
		carry bit if this is a valid file that
		has not been closed. (see BCS $C764)
		If .A<128, branch to GN15.
		-----------------------------------------
		NOTE: The branch at $C742 and the code following is what
		produces the PRG<, SEQ<, etc. file types. Note that
		these file types are LOCKED and can't be SCRATCHED!
		The locking and unlocking of files is NOT supported
		by any Commodore DOS. To lock a file, change its
		file type in its directory entry from $80, $81, etc
		to $C0, $Cl, etc. Reverse the process for unlocking
		-----------------------------------------
	$C745	Load .A with a $3C (a "<").
	$C747	Store this value into the name buffer
		NAMBUF+1,X ($02B1,X).
		-----------------------------------------
GN15	$C74A	Pull the file type off the stack and AND
		it with $0F to mask off the higher bits.
		Transfer it to .Y to use as an index.
	$C74E	Move last character in file type name
		from TP2LST,Y ($FEC5,Y) to the name
		buffer, NAMBUF,X ($02B1,X).
	$C754	Decrement .X
	$C755	Move middle character in file type name
		from TPILST,Y ($FEC0,Y) to the name
		buffer, NAMBUF,X ($02B1,X).
	$C75B	Decrement .X
	$C75C	Move first character in file type name
		from TYPLST,Y ($FEBB,Y) to the name
		buffer, NAMBUF,X ($02B1,X).
	$C762	Decrement .X twice
	$C764	If carry bit is set (indicates valid
		entry; see $C742) branch to GN20.
	$C766	Load .A with $2A (a "*") to indicate an
		improperly closed file.
	$C768	Store the "*" in NAMBUF+1,X ($02B1,X).
GN20	$C76B	Store a shifted space, SAO in the buffer
		(between name & type) and decrement .X
	$C771	Load .Y with $12 (#18) so it points to
		the end of the name in the dir buffer.
GN22	$C773	Loop to transfer the 16 characters in
		the file name from the directory buffer
		to the name buffer.
	$C77E	Load .A with $22 (a "")
	$C780	Store quotation mark before the name.
GN30	$C783	Loop to scan up the name looking for a
		quote mark($22) or a shifted space($A0).
		When either character is found or the
		end of the name is reached, store a $22
		(quote mark) at that location. Then AND
		any remaining characters in the name
		with $7F to clear bit 7 for each one.
GN40	$C7A7	JSR to FNDFIL ($C4B5) to find the next
		entry. On return, set the carry bit.
GN45	$C7AB	Terminate the routine with an RTS.
		-----------------------------------------
		Blank the name buffer:
BLKNB	$C7AC	Load .Y with $1B, the length of the name
		buffer, and .A with $20, a space.
BLKNB1	$C7B0	Loop to store $20's in all locations in
		the name buffer, NAMBUF ($02B1-CB)
	$C7B6	Terminate the routine with an RTS.
		-----------------------------------------
		New directory in listing
NEWDIR	$C7B7	JSR to BAM2X ($F119) to set BAM pointer
		in buffer 0/1 tables and leave in .X
	$C7BA	JSR to REDBAM ($F0DF) to read in the BAM
		to $0700-FF if not already present.
	$C7BD	JSR to BLKNB ($C7AC) to blank the name
		buffer, NAMBUF ($02B1-CB).
	$C7C0	Set TEMP ($6F) to $FF
	$C7C4	Set NBTEMP ($0272) to the current drive
		number from DRVNUM ($7F)
	$C7C9	Set NBTEMP+1 ($0273) to $00
	$C7CE	Load .X with the position of the read
		BAM job in the queue from JOBNUM ($F9).
	$C7D0	Set high byte of the pointer to the
		directory buffer, DIRBUF ($94/5) using a
		value (3,4,5,6,7,7) from BUFIND,X($FEE0)
	$C7D5	Set low byte of the pointer to the
		directory buffer, DIRBUF ($94/5) using
		the value ($90) from DSKNAM ($FE88).
		DIRBUF now points to the start of the
		disk name in the BAM buffer ($0x90)
	$C7DA	Load .Y with $16 (#22), the name length.
ND10	$C7DC	Load .A with character, (DIRBUF),Y and
		test if it is a shifted blank (SAO).
		If not, branch to ND2O.
	$C7E2	Since it is not a shifted blank, load .A
		with a $31 (ASCII "1") for version #1.
	$C7E4	BYTE $2C here causes branch to ND20.
ND15	$C7E5	Load .A with character, (DIRBUF),Y and
		test if it is a shifted blank (SAO).
		If not, branch to ND20.
	$C7EB	Since it is not a shifted blank, load .A
		with a $20 (ASCII space).
ND20	$C7ED	Store the character in .A into the name
		buffer, NAMBUF+2,Y ($02B3,Y).
	$C7F1	If more characters left (.Y>=0) branch
		back to ND15.
	$C7F3	Store a $12 (RVS on) in NAMBUF ($02B1)
	$C7F8	Store a $22 (quote) in NAMBUF+1 ($02B2)
	$C7FD	Store a $22 (quote) in NAMBUF+18($02C3)
	$C800	Store a $20 (space) in NAMBUF+19($02C4)
	$C805	Terminate routine with an RTS.
		-----------------------------------------
		Set up message "BLOCKS FREE"
MSGFRE	$C806	JSR to BLKNB ($C7AC) to clear the name
		buffer.
	$C809	Load .Y with $0B (message length -1).
	$C80B	Loop using .Y as index to move message
		from FREMSG,Y ($C817,Y) to NAMBUF,Y
		($02B1,Y).
	$C814	Terminate routine with a JMP to NUMFRE
		($EF4D) to calculate the number free.
		-----------------------------------------
FREMSG	$C817	Message "BLOCKS FREE"
		-----------------------------------------
		- * - * - SCRATCH ONE OR MORE FILES - * - * -
		-----------------------------------------
SCRTCH	$C823	JSR to FSISET ($C398) to set up for one
		file stream.
	$C826	JSR to ALLDRS ($C320) to all drives
		needed based on F2CNT.
	$C829	JSR to OPTSCH ($C3CA) to determine best
		sequence of drives to use.
	$C82C	Zero file counter, RO ($86)
	$C830	JSR to FFST ($C49D) to find the first
		directory entry. If not successful,
		branch to SC30.
		--------------------------------------
		NOTE: THE FOLLOWING CODE PREVENTS FREEING THE SECTORS
		OF AN UNCLOSED FILE.
		--------------------------------------
SC15	$C835	JSR to TSTCHN ($DDB7) to test for active
		files from index table.
	$C838	If file active (carry clear), branch to
		SC25.
		--------------------------------------
		NOTE: THE FOLLOWING CODE PREVENTS THE SCRATCHING OF
		A LOCKED FILE (BIT 6 OF THE FILE TYPE SET).
		--------------------------------------
	$C83A	Load .Y with $00.
	$C83C	Load .A with file type from (DIRBUF),Y
		($94,Y).
	$C83E	AND the file type with $40 to test if it
		is a locked file (bit 6 of filetype set)
	$C840	If a locked file, branch to SC25.
		-----------------------------------------
	$C842	JSR to DELDIR ($C8B6) to delete the
		directory entry.  Stores $00 as the file
		type and rewrite the sector on disk.
		-----------------------------------------
	$C845	Load .Y with $13 (#19).
	$C847	Test whether this is a relative file by
		loading .A with 19th character of the
		entry (the track of the side-sector
		pointer for a REL file) from (DIRBUF),Y
	$C849	If $00, not a REL file so branch to SC17
	$C84B	Store track pointer into TRACK ($80).
	$C84D	Increment .Y and move sector pointer
		from (DIRBUF),Y into SECTOR ($81).
	$C852	JSR to DELFIL ($C87D) to free the side
		sectors by updating and writing the BAM
		----------------------------------------------------------
		NOTE: THE FOLLOWING CODE PREVENTS FREEING THE SECTORS
		OF A FILE IF ITS REPLACEMENT WAS INCOMPLETE (BIT 5 SET).
		----------------------------------------------------------
SC17	$C855	Load .X with the directory entry counter
		ENTFND ($0253) and .A with $20.
	$C85A	AND .A with the file pattern type in
		PATTYP,X ($E7,X) to check if this is an
		opened but unclosed file.
	$C85C	If unclosed file, branch to SC20.
		-----------------------------------------
	$C85E	Move initial track link from FILTRK,X
		($0280,X) into TRACK ($80).
	$C863	Move initial sector link from FILSEC,X
		($0285,X) into SECTOR ($81).
	$C868	JSR to DELFIL ($C87D) to free the file
		blocks by updating and writing the BAM
SC20	$C86B	Increment the file counter, RO ($86).
SC25	$C86D	JSR to FFRE ($C48B) to match the next
		filename in the command string.
	$C870	If a match found, branch to SC15
SC30	$C872	All done. Store number of files that
		have been scratched, RO ($86) into
		TRACK ($80)
	$C876	Load .A with $01 and .Y with $00
	$C87A	Exit with a JMP to SCREND ($CIA3)
		-----------------------------------------
		Delete file by links:
DELFIL	$C87D	JSR to FRETS ($EF5F) to mark the first
		file block as free in the BAM.
	$C880	JSR to OPNIRD ($D475) to open the
		internal read channel (SA=17) and read
		in the first one or two blocks.
	$C883	JSR to BAM2X ($F119) to set BAM pointers
		in the buffer tables.
	$C886	Load .A from BUFO,X ($A7,X) and compare
		it to $FF to see if buffer inactive.
		If inactive (.A=$FF), branch to CEL2
		Load write BAM flag, WBAM ($02F9), OR it
		with $40 to set bit 6 and store it back
		in WBAM to indicate both buffers active.
DEL2	$C894	Zero .A and JSR to SETPNT($D4C8) to set
	$C899	pointers to the currently active buffer.
		JSR to RDBYT ($D156) to direct read one
	$C89C	byte (the track link from the buffer)
		Store track link into TRACK ($80)
	$C89E	JSR to RDBYT ($D156) to direct read one
	$C8A1	byte (the sector link from the buffer)
		Store sector link into SECTOR ($81)
	$C8A3	Test track link. If not $00 (not final
	$C8A7	sector in this file), branch to DELI
		JSR to MAPOUT ($EEF4) write out the BAM.
	$C8AA	Exit with a JMP to FRECHN ($D227) to
		free the internal read channel.
		-----------------------------------------
DEL1	$C8AD	JSR to FRETS($EF5F) to de-allocate(free)
	$C8B0	specified in TRACK ($80) & SECTOR ($81)
	$C8B3	in the BAM.
		JSR to NXTBUF ($D44D) to read in the
		next block in the file (use T/S link).
		JMP to DEL2 to de-allocate the new block
		-----------------------------------------
DELDIR	$C8B6	Delete the directory entry:
		Load .Y with $00 (will point to the 0th
	$C8B8	character in the entry; the file type).
		Set the file type, (DIRBUF),Y; ($94),Y
	$C8BB	to $00 to indicate a scratched file.
	$C8BE	JSR to WRTOUT ($DE5E) to write cut the
		directory block.
		Exit with a JMP to WATJOB ($D599) to
		wait for the write job to be completed.
		----------------------------------------------------------
		* DUPLICATE DISK * NOT AVAILABLE ON THE 1541
		----------------------------------------------------------
	$C8C1	Load .A with a $31 to indicate a bad
		command and JMP to CMDERR ($C1C8).
		------------------------------------------------------------
		- * - * - FORMAT DISKETTE ROUTINE - * - * -
		----------------------------------------------------------
		This routine sets up a jump instruction in buffer 0
		that points to the code used by the disk controller
		to do the formatting. It then puts an exectute job
		code in the job queue. The routine then waits while
		the disk controller actually does the formatting.
		----------------------------------------------------------
FORMAT	$C8C6	Store JMP $FABB ($4C,$BB,$FA) at the
    		start of buffer 0 ($0600/1/2).
	$C8D5	Load .A with $03 and JSR to SETH ($D6D3)
		to set up header of active buffer to the
		values in TRACK ($80) and SECTOR ($81).
	$C8DA	Load drive number, DRVNUM ($7F), FOR it
		with $E0 (execute job code) and store
		the result in the job queue ($0003).
FMT105	$C8E0	Load .A from the job queue ($0003). If
		.A > 127, the job has not been finished
		yet so branch back to FMT105.
	$C8E4	Compare .A with $02. if .A < 2, the job
		was completed OK so branch to FMT110.
	$C8E8	Error code returned by disk controller
		indicates a problem so load .A with $03
		and .X with $00 and exit with a JMP to
		ERROR ($E60A).
FMT110	$C8EF	Job completed satisfactorily so exit
		with an RTS.
		----------------------------------------------------------
		- * - * - COPY DISK FILES ROUTINE - * - * -
		----------------------------------------------------------
DSKCPY	$C8F0	Store $E0 in BUFUSE ($024F) to kill the
		BAM buffer.
	$C8F5	JSR to CLNBAM ($F0D1) to set track and
		sector links in BAM to $00.
	$C8F8	JSR to BAM2X ($F119) to return the BAM
		LINDX in .X.
	$C8FB	Store $FF in BUFO,X ($A7,X) to mark the
		BAM as out-of-memory.
	$C8FF	Store $0F in LINUSE ($0256) to free all
		LINDXs.
	$C904	JSR to PRSCLN ($C1E5) to parse the
		command string and find the colon.
	$C907	If colon found (Z flag =0), branch to
		DX0000.
	$C909	Colon not found in command string so
		command must be CX=Y. This command is
		not supported on the 1541 so exit with
		a JMP to DUPLCT ($C8C1).
		-----------------------------------------
DX0000	$C90C	JSR to TC30 ($C1F8) to parse the command
		string.
DX0005	$C90F	JSR to ALLDRS ($C320) to put the drive
		numbers into the file table.
	$C912	Load .A with the command pattern image
		as determined by the parser from IMAGE
		($028B). AND the image with %01010101
		($55). If the result is not $00, the
		command must be a concatenate or normal
		copy so branch to DX0020.
	$C919	Check for pattern matching in the name
		(as in cl:game=0:*) by loading .X from
		FILTBL ($027A) and then loading .A from
		the command string, CMDBUF,X ($0200,X).
	$C91F	The value in .A is compared to $2A ("*")
		If there is no match, there is no wild
		so branch to DX0020.
DX0010	$C923	Load .A with the $30 to indicate a
		syntax error and JMP to CMDERR ($C1C8).
DX0020	$C928	Load .A with the command pattern image
		as determined by the parser from IMAGE
		($028B). AND the image with %11011001
		($D9). If the result is not $00, the
		syntax is bad so branch to DX0010 and
		abort.
	$C92F	JMP to COPY ($C952) to do the file copy.
		syntax error and JMP to CMDERR ($C1C8).
		-----------------------------------------
PUPS1	$C932	Subroutine used to set up for copying
		entire disk (C1=0). Not used on 1541.
		-----------------------------------------
		Copy file(s) to one file:
COPY	$C952	JSR to LOOKUP ($C44F) to look up the
		file(s) listed in the command string in
		the directory.
	$C955	Load .A with the number of filenames in
		the command string from F2CNT($0278) and
		compare it with $03. If fewer than three
		files, this is not a concatenate so
		branch to COP10 ($C9A1).
	$C95C	Load .A with the first file drive number
		from FILDRV ($E2) and compare it to the
		second drive number in FILDRV+1 ($E3).
		If not equal, this is not a concatenate
		so branch to COP10 ($C9A1).
	$C962	Load .A with the index to the first file
		entry from ENTIND ($DD) and compare it
		to the second file's index in ENTIND+1
		($DE). If not equal, this is not a
		concatenate so branch to COP10 ($C9A1).
	$C968	Load .A with the first file's sector
		link from ENTSEC ($D8) and compare it
		to the second file's link in ENTSEC+1
		($D9). If not equal, this is not a
		concatenate so branch to COP10 ($C9A1).
		-----------------------------------------
		CONCATENATE FILES
		-----------------------------------------
	$C96E	JSR to CHKIN ($CACC) to check if input
		file exists.
	$C971	Set F2PTR ($0279) to $01 and JSR to
		OPIRFL ($C9FA) to open the internal read
		channel, read in the directory file, and!
		locate the named file.
	$C979	JSR to TYPFIL ($D125) to determine the
		file type. If $00, a scratched file so
		branch to COPO1 (file type mismatch).
	$C97E	Compare the file type to $02. If not
		equal, it is not a deleted program file
		so branch to COPO5 to continue.
COP01	$C982	Bad file name. Load .A with $64 to
		indicate a file type mismatch and JSR
		to CMDERR ($C1C8).
COPO5	$C987	Set secondary address, SA ($83) to $12
		(#18, the internal write channel)
	$C98B	Move the active buffer pointer from
		LINTAB+IRSA ($023C) to LINTAB+IWSA
		($023D).
	$C991	Deactivate the internal read channel by
		storing $FF in LINTAB+IRSA ($023C).
	$C996	JSR to APPEND ($DA2A) to copy first file
	$C999	Load .X with $02 and JSR to CY10 ($C9B9)
    		to copy second file behind the first.
	$C99E	Exit routine with a JMP to ENDCMD ($C194)
		-----------------------------------------
		COPY FILE
		-----------------------------------------
COP10	$C9A1	JSR to CY ($C9A7) to do copy.
	$C9A3	Exit routine with a JMP to ENDCMD ($C194)
		-----------------------------------------
CY	$C9A7	JSR to CHKIO ($CAE7) to check if file
	$C9AA	exists.
		($E2), AND
		bit), and
		Get drive number from FILDRV
		it with $01 (mask off default
		store it in DRVNUM ($7F).
	$C9B0	JSR to OPNIWR ($D486) to open internal
		write channel.
	$C9B3	JSR to ADDFIL ($D6E4) to add the new
	$C9B6	file name to the directory and rewrite
		the directory.
		Load .X with pointer from FICNT ($0277).
CY10	$C9B9	Store .X in F2CNT ($0278).
	$C9BC	JSR to OPIRFL ($C9FA) to open internal
	$C9BF	read channel and read in one or two
	$C9C3	blocks of the directory.
		Set secondary address, SA ($83) to $11,
		to set up the internal read channel.
		JSR to FNDRCH ($D0EB) to find an unused
	$C9C6	read channel.
		JSR to TYPFIL ($D125) to determine if
	$C9C9	the file is a relative file.
		If not a relative file (Z flag not set
CY10A	$C9CB	on return), branch to CY10A. the
		JSR to CYEXT ($CA53) to open copy
		relative file records.
	$C9CE	Store $08 (EOI signal)
		into EOIFLG($F8).
	$C9D2	JMP to CY20.
CY15	$C9D5	JSR to PIBYTE ($CF9B) to write out last
CY20	$C9D8	byte to disk. ($CA35) to byte from
		JSR to GIBYTE
		get a
	$C9DB	the internal read channel. record flag)
		Load .A with $80 (the last
		and JSR to TSTFLG ($DDA6) to see if this
		is the last record.
CY30	$C9E0	On return if Z flag is set (test failed;
	$C9E5	this is not the last record) branch to
	$C9E7	CY15 to do some more.
	$C9EA	Last record done so JSR to TYPFIL($D125)
	$C9F3	to get file type.
		On return if Z flag is set branch to
		CY30 to do some more.
		JSR to PIBYTE ($CF9B) to write out last
		byte to disk.
		Check if there are more files to copy
		by loading .X from F2PTR ($0279),
		incrementing it by 1, and comparing it
		to F2CNT ($0278). If the carry bit is
		clear, there are more files to copy so
		branch back to CY10.
		Since no more files to copy, set the SA
		($83) to $12 (internal write channel)
		and JMP to CLSCHN ($DB02) to close the
		copy channel and file.
OPIRFL	$C9FA	-----------------------------------------
		Open internal read channel to read file:
		Load .X with the file pointer F2PTR
		($0279) and use this as an index to load
		.A with the drive number of the file to
		be read from FILDRV,X ($E2,X). AND this
		drive number with $01 to mask off the
		default drive bit, and store the value
		in DRVNUM ($7F) to set the drive number.
	$CA03	Set the current TRACK ($80) to 18 ($12),
		the directory track.
	$CA08	Set the current SECTOR($81) to the
	$CA0C	sector containing the directory entry
	$CA0F	for this file from ENTSEC,X ($D8,X).
		the directory track.
		JSR to OPNIRD ($D475) to open the
		internal read channel to read the
		directory.
		Load .X with the file pointer F2PTR
		($0279) and use this as an index to load
		.A with the pointer to the start of the
		entry from ENTIND,X ($DD,X).
	$CA14	JSR to SETPNT ($D4C8) to set the track
		sector pointers from the entry.
	$CA17	Load .X with the file pointer F2PTR
		($0279) and use this as an index to load
		.A with the file's pattern mask from
		PATTYP,X ($E7,X). AND this value with
		$07 (the file type mask) and use it to
		set the file type in TYPE ($024A).
	$CA21	Set the record length, REC ($0258) to
		$00 since this is not a relative file.
	$CA26	JSR to OPREAD ($D9A0) to open a read
		channel.
OPIR10	$CA29	Load .Y with $01 and JSR to TYPFIL
	$CA2E	($D125) to get the file type.
	$CA30	If Z flag set on return (indicates that
	$CA31	this is not a relative file) branch to
	$CA32	OPIR10.
		Increment .Y by 1.
		Transfer the value in .Y into .A
		Exit with a JMP to SETPNT ($D4C8) to set
		the track & sector pointers from the
		directory entry.
		-----------------------------------------
GIBYTE	$CA35	Get byte from internal read channel:
		Set the secondary address, SA ($83) to
		$11 (#17) the internal read channel.
		-----------------------------------------
GCBYTE	$CA39	Get byte from any channel:
		JSR to GBYTE ($D39B) to get the next
	$CA3C	byte from the read channel.
	$CA3E	Store the byte in DATA ($85).
	$CA42	Load .X with the logical file index
		LINDX ($82) and use this as an index to
		load .A with the channel status flag,
		CHNRDY,X
		FOR .A with $08, the not EOI send code
	$CA46	and store the result in EOIFLG ($F8).
		If .A <> $00 (EOI was sent!), branch to
	$CA48	GIB20 and exit.
		JSR to TYPFIL ($D125) to get the file
GIB20	$CA4D	type. If Z flag set on return (indicates
	$CA52	this is not a relative file), branch to
		GIB20 and exit.
		Load .A with $80 (the last record flag)
		and JSR to SETFLG ($DD97).
		Terminate routine with an RTS.
		-----------------------------------------
CYEXT	$CA53	Copy relative records:
		JSR to SETDRN ($D1D3) to set drive #.
	$CA56	JSR to SSEND ($E1CB) to position side
	$CA59	sector and BUFTAB to the end of the
		last record.
		Save side sector index, SSIND ($D6) and
	$CA5F	the side sector number, SSNUM ($D5) onto
	$CA63	the stack.
		Set the secondary address, SA ($83) to
		$12, the internal write channel.
		JSR to FNDWCH ($D107) to find an unused
	$CA66	write channel.
		JSR to SETDRN ($D1D3) to set drive #.
	$CA69	JSR to SSEND ($E1CB) to position side
		sector and BUFTAB to the end of the
		last record.
	$CA6C	JSR to POSBUF ($E2C9) to position the
	$CA6F	proper data blocks into the buffers.
	$CA73	Set R1 ($87) to the current value of
	$CA77	the side sector index, SSIND ($D6).
	$CA7F	Set RO ($86) to the current value of
	$CA85	the side sector number, SSNUM ($D5).
		Zero R2 ($88) and the low bytes of the
		record pointer RECPTR ($D4) and the
		relative file pointer ($D7).
		Restore the original values of the side
		side sector number, SSNUM ($D5) and the
		sector index, SSIND ($D6) from the stack
		Terminate the routine with a JMP to
		ADDR1 ($E33B).
		RENAME FILE IN THE DIRECTORY
		----------------------------------------------------------
RENAME	$CA88	JSR to ALLDRS ($C320) to set up all the
	$CA8B	drives given in the command string.
		Load .A with the drive specified for the
		second file from FILDRV+i ($E3), AND it
		with $01 to mask off the default drive
		bit, and store the result back in
	$CA91	FILDRV+1 ($E3).
		Compare the second drive number (in .A)
	$CA95	with the first one in FILDRV ($E2). If
		equal, branch to RN10.
		OR the drive number in .A with $80 to
RN10	$CA97	set bit 7. This will force a search of
		both drives for the named file.
		Store the value in .A into FILDRV ($E2)
	$CA99	JSR to LOOKUP ($C44F) to look up both
	$CA9C	file names in the directory.
	$CA9F	JSR to CHKIO ($CAE7) to check for the
	$CAA5	existance of the files named.
	$CAA9	Load the value from FILDRV+1 ($E3), AND
	$CAAC	it with $01 to mask off the default
	$CAAF	drive bit, and use the result to set the
		currently active drive, DRVNUM ($7F).
		Set the active sector number, SECTOR
		($81) using the directory sector in
		which the second file name was found
		(from ENTSEC+1; $D9).
		JSR to RDAB ($DE57) to read the
		directory sector specified in TRACK($80)
		and SECTOR ($81).
		JSR to WATJOB ($D599) to wait for the
		job to be completed.
		Load .A with the pointer to the entry in
		the buffer from ENTIND+1 ($DE), add $03
		(so it points to the first character in
		the file name), and JSR to SETPNT($D4C8)
		to set the pointers to the file name.
	$CAB7	JSR to GETACT ($DF93) to store the
	$CABA	active buffer number in .A.
		Transfer the buffer number to .Y, load
		.X from the file table FILTBL ($027A),
		.A with $10 (the number of characters
		in a file name) and JSR to TRNAME($C66E)
		to transfer the file name from the
	$CAC3	command string to the buffer containing
	$CAC6	the file entry.
	$CAC9	JSR to WRTOUT ($DE5E) to write out the
		revised directory sector.
		JSR to WATJOB ($D599) to wait for the
		job to be completed.
		Terminate the routine with a JMP to
		ENDCMD ($C194).
		-----------------------------------------
CHKIN	$CACC	Check existance of input file:
		Load .A with the first file type from
		PATTYP+1 ($E8), AND it with the file
		type mask ($07) and store it in TYPE
		($024A).
CK10	$CAD3	Load .X from F2CNT ($0278).
CK20	$CAD6	
		Decrement .X by 1 and compare it with
		the value of F1CNT ($0277).
	$CADA	If the carry is clear, the file has been
		found so branch to CK10.
	$CADC	Load .A with the file's track link from
		FILTRK,X ($0280,X). If link is NOT $00,
		branch to CK10.
	$CAE1	Since the file has not been found, load
		.A with $62 and exit with a JMP to
		CMDERR ($C1C8).
	$CAE6	Terminate routine with an RTS.
		-----------------------------------------
CHKIO	$CAE7	Check existance of I/O file: for the
CK25	$CAEA	JSR to CHKIN ($CACC) to check link from
CK30	$CAEF	existance of the input file.
	$CAF4	Load .A with the file's track
		FILTRK,X ($0280,X). If link equals $00,
		branch to CK30.
		The file already exists so load .A with
		$62 and exit with a JMP to CMDERR($C1C8)
		Decrement .X (file counter). If more
		files exist, branch back to CK25.
		CMDERR ($C1C8).
	$CAF7	Terminate routine with an RTS.
		--------------------------------------
		MEMORY ACCESS COMMANDS (M-R, M-W, AND M-E)
		--------------------------------------
MEM	$CAF8	Check that the second character in the
		command is a "-" by: loading .A with
		the character from CMDBUF+1 ($0201),
		and comparing it with $2D ("-"). If not
		equal, branch to MEMERR ($CB4B).
	$CAFF	Set up address specified in command by
		moving the characters from CMDBUF+3
		($0202) and CMDBUF+4 ($0203) to TEMP
		($6F) and TEMP+1 ($70).
	$CB09	Set .Y to $00.
	$CB0B	Load .A with the third character of the
		command (R,W,E) from CMDBUF+2 ($0202).
	$CB0E	Compare .A with "R". If equal, branch
		to MEMRD ($CB20).
	$CB12	JSR to KILLP ($F258) to kill protection.
		NOTE: this does nothing on the 1541!
	$CB15	Compare .A with "W". If equal, branch
		to MEMWRT ($CB50).
	$CB19	Compare .A with  " E " .  If NOT equal,
		branch to MEMERR ($CB4B).
		-----------------------------------------
MEMEX	$CB1D	Do indirect jump using the pointer set
		up in TEMP ($006F).
		-----------------------------------------
MEMRD	$CB20	Load .A with the contents of (TEMP),Y
		($6F),Y and store the value in DATA($85)
	$CB24	Compare the command string length,CMDSIZ
		($0274), with $06. If it is less than or
		equal to 6 (normally 5), branch to M30.
		--------------------------------------
		NOTE: PREVIOUSLY UNDOCUMENTED COMMAND!!
		--------------------------------------
		PRINT#15,"M-R";CHR$(LO);CHR$(HI);CHR$(HOW MANY)
		--------------------------------------
		Multi-byte memory read:
MRMULT	$CB2B	Load .X with the 6th character in the
		command string from CMDBUF+5 ($0205).
	$CB2E	Decrement .X (now $00 if only one to
		read).
	$CB2F	If the result is $00, all done so branch
		to M30.
	$CB31	Transfer the value in .X to .A and clear
		the carry flag.
	$CB33	Add the lo byte of the memory pointer
		in TEMP ($6F). This value is the lo
		byte of the last character to be sent.
	$CB35	Increment the lo byte pointer in TEMP
		($6F) so it points to the second memory
		location to be read.
	$CB37	Store the value in .A into LSTCHR+ERRCHN
		($0249).
	$CB3A	Load .A with the current value of TEMP
		($6A), the lo byte of the second memory
		location to be read and store this value
		in CB+2 ($A5).
	$CB3E	Load .A with the current value of TEMP+l
		($70), the hi byte of the second memory
		location to be read and store this value
		in CB+3 ($A6).
	$CB42	Continue memory read with a JMP to GE20
		($D443).
		-----------------------------------------
M30	$CB45	JSR to FNDRCH ($D0EB) to find an unused
		read channel.
	$CB48	Terminate memory read with a JMP to
		GE15 ($D43A).
		-----------------------------------------
MEMERR	$CB4B	Load .A with $31 to indicate a bad
		command and JMP to CMDERR ($C1C8).
		-----------------------------------------
MEMWRT	$CB50	Move byte from CMDBUF+6,Y ($0206,Y) to
		memory at TEMP,Y ($BF,Y).
	$CB55	Increment .Y and compare .Y with the
		number of bytes to do, CMDBUF+5 ($0205).
	$CB59	If more to do, branch back to M10.
	$CB5B	Terminate memory write with an RTS.
		-----------------------------------------
		USER COMMANDS NOTE: U0 restores pointer to JMP table
		-----------------------------------------
		User jump commands:
USER	$CB5C	Load .Y with the second byte of the
		command string from CMDBUF+l ($0201).
	$CB5F	Compare .Y to $30. If not equal, this
		is NOT a U0 command so branch to US10.
USRINT	$CB63	Restore normal user jump address ($FFEA)
		storing $EA in USRJMP ($6B) and $FF in
		USRJMP+l ($6C).
	$CB6B	Terminate routine with an RTS.
US10	$CB6C	JSR to USREXC ($CB72) to execute the
		code according to the jump table.
	$CB6F	Terminate routine with a JMP to ENDCMD
		($C194).
USREXC	$CB72	Decrement .Y, transfer the value to .A,
		AND it with $0F to convert it to hex,
		multiply it by two (ASL), and transfer
		the result back into .Y.
	$CB78	Transfer the lo byte of the user jump
		address from the table at (USRJMP),Y
		to IP ($75).
	$CB7C	Increment .Y by 1.
	$CB7D	Transfer the hi byte of the user jump
		address from the table at (USRJMP),Y
		to IP+l ($76).
	$CB81	Do an indirect jump to the user code
		through the vector at IP ($0076).
		-----------------------------------------
		Open direct access buffer in response
		to an OPEN "#" command:
OPNBLK	$CB84	Use the previous drive number, LSTDRV
		($028E) to set the current drive number
		DRVNUM ($7F).
	$CB89	Save the current secondary address, SA
		($83) on the stack.
	$CB8C	JSR to AUTOI ($C63D) to initialize the
		disk. This is necessary for proper
		channel assignment.
	$CB8F	Restore the original secondary address,
		SA ($83) by pulling it off the stack.
	$CB92	Load .X with the command string length
		CMDSIZ ($0274). Decrement .X by 1.
	$CB96	If .X not equal to zero, a specific
		buffer number has been requested(e.g.#l)
		so branch to OB10.
	$CB98	No specific buffer requested so get any
		available buffer by loading .A with $01
		and doing a JSR to GETRCH ($D1E2).
	$CB9D	On return, JMP to OB30.
OB05	$CBA0	Load .A with $70 to indicate that no
		channel is available and JMP to CMDERR
		($C1C8).
OB10	$CBA5	Specific buffer requested so load .Y
		with $01 and JSR to BP05 ($CC7C) to
		check the block parameters. buffer
		$CBB8
OB15	$CBAA	Load .X with the number of the
		requested from FILSEC ($0285) and check
		it against $05 (the highest numbered
		buffer available). If too large, branch
		to OB05 and abort the command.
	$CBB1	Set TEMP ($6F) and TEMP+1 ($70) to $00
		and set the carry flag.
		Loop to shift a 1 into the bit position
		in TEMP or TEMP+l that corresponds to
		the buffer requested. For example:
		TEMP+1(00000000) TEMP(0000001)=buffer 0
		TEMP+1(00000000) TEMP(0000100)=buffer 2
		TEMP+1(00000001) TEMP(0000000)=buffer 8
	$CBBF	Load .A with the value in TEMP ($6F)
		and AND it with the value in BUFUSE
		($024F) which indicates which buffers
		are already in use. If the result is
		NOT $00, the buffer requested is already
		in use so branch to OB05 to abort.
	$CBC6	Load .A with the value in TEMP+1 ($70)
		and AND it with the value in BUFUSE+l
		($0250) which indicates which buffers
		are already in use. If the result is
		NOT $00, the buffer requested is already
		in use so branch to OB05 to abort.
	$CBCD	Mark the buffer requested as in use by
		ORing the value in TEMP with the value
		in BUFUSE and the value in TEMP+1 with
		the value in BUFUSE+l.
	$CBDD	Set up the channel by loading .A with
		$00 and doing a JSR to GETRCH ($DIE2)
		to find an unused read channel.
	$CBE2	Load .X with the current channel# from
		LINDX ($82).
	$CBE4	Use .X as an index to move the sector
		link from FILSEC($0285) to BUFO,X($A7,X)
	$CBE9	Transfer the sector link from .A to .X.
	$CBEA	Use .X as an index to move the current
		drive number from DRVNUM($7F) to JOBS,X
		($00,X) and to LSTJOB,X ($025B,X).
OB30	$CBF1	Load .X with the current secondary
		address, SA ($83).
	$CBF3	Load .A with the current value from the
		logical index table, LINTAB,X ($022B,X).
		OR this value with $40 to indicate that
		it is read/write mode and store the
		result back in LINTAB,X.
	$CBFB	Load .Y with the current channel#, LINDX
		($82).
	$CBFD	Load .A with $FF and store this value
		as the channel's last character pointer
		LSTCHR,Y ($0244,Y).
	$CC0D	
	$CC02	Load .A with $89 and store this value
		in CHNRDY,Y ($00F2,Y) to indicate that
		the channel is a random access one and
		is ready.
	$CC07	Load .A with the channel number from
		BUFO,Y ($00A7,Y) and store it in
		CHNDAT,Y($023E,Y) as the first character
		Multiply the sector value in .A by 2
		and transfer the result into .X
	$CC0F	Set the buffer table value BUFTAB,X
		($99,X) to $01.
	$CC13	Set the file type value FILTYP,Y ($EC,Y)
		to $0E to indicate a direct access file
		type.
	$CC18	Terminate routine with a JMP to ENDCMD
		($C1C4).
		----------------------------------------------------------
		BLOCK COMMANDS (B-A;B-F;B-R;B-W;B-E;B-P)
		----------------------------------------------------------
		Block commands:
BLOCK	$CC1B	Zero .X and .Y. Load .A with $2D ("-")
		and JSR to PARSE ($C268) to locate the
		sub-command (separated from the command
		with a "-").
	$CC24	On return branch to BLK40 if Z flag is
		not set ("-" was found).
BLK10	$CC26	Load .A with $31 to indicate a bad
		command and JMP to CMDERR ($C1C8).
BLK30	$CC2B	Load .A with $30 to indicate a bad
		syntax and JMP to CMDERR ($C1C8).
BLK40	$CC30	Transfer the value in .X to .A. If not
		$00, branch to BLK30.
	$CC33	Load .X with $05 (the number of block
		commands - 1).
	$CC35	Load .A with the first character in the
		sub-command from CMDBUF,Y ($0200,Y).
BLK50	$CC38	Loop to compare the first character in
		the sub-command with the characters in
		the command table BCTAB,X ($CCSD,X). If
		a match is found, branch to BLK60. If NO
		MATCH is found, branch to BLK10.
BLK60	$CC42	Transfer the pointer to the command in
		the command table from .X to .A. OR this
		value with $80 and store it as the
		command number in CMDNUM ($022A).
	$CC48	JSR to BLKPAR ($CC6F) to parse the
		block parameters.
	$CC4B	Load .A with the command number from
		CMDNUM ($022A), multiply it by 2 (ASL),
		and transfer the result into .X.
	$CC50	Use .X as an index into the jump table
		BCJMP,X ($CC63) to set up a jump vector
		to the ROM routine at TEMP ($6F/70).
	$CC5A	Do an indirect JMP to the appropriate
		ROM routine via the vector at TEMP($6F).
		-----------------------------------------
	$CC5D	Block sub-command table ($CC5D-$CC62)
		.BYTE "AFRWEP"
		-----------------------------------------
	$CC63	Block jump table ($CC63-$CC6E)
		$CC63/4 $03,$CD BLOCK-ALLOCATE $CD03
		$CC65/6 $F5,$CC BLOCK-FREE     $CCF5
		$CC67/8 $56,$CD BLOCK-READ     $CD56
		$CC69/A $73,$CD BLOCK-WRITE    $CD73
		$CC6B/C $A3,$CD BLOCK-EXECUTE  $CDA3
		$CC6D/E $BD,$CD BLOCK-POINTER  $CDBD
		-----------------------------------------
		Parse the block parameters:
BLKPAR	$CC6F	Zero .X and .Y. Load .A with $3A (":")
		and JSR to PARSE ($C268) to find the
		colon, if any.
	$CC78	On return branch to BP05 if Z flag is
    		not set (":" found; .Y= " : " -position+l)
	$CC7A	Load .Y with $03 (start of parameters)
BP05	$CC7C	Load .A with the .Yth character from
		the command string.
	$CC7F	Compare the character in .A with $20,
		(a space). If equal, branch to BP10.
	$CC83	Compare the character in .A with $29,
		(a skip chr). If equal, branch to BP10.
	$CC87	Compare the character in .A with $2C,
		(a comma). If NOT equal, branch to BP20.
BP10	$CC8B	Increment .Y. Compare .Y to the length
		of the command string in CMDSIZ ($0274).
		If more left, branch back to BP05.
	$CC91	If no more, exit with an RTS.
BP20	$CC92	JSR to ASCHEX ($CCAl) to convert ASCII
		values into hex and store the results
		in tables.
	$CC95	Increment the number of parameters
		processed F1CNT ($0277).
	$CC98	Load .Y with the value in F2PTR ($0279)
	$CC9B	Compare the value in .X (the original
		value of F1CNT ($0277) to $04 (the
		maximun number of files - 1). If the
		value in .X $04, branch to BP10.
	$CC9F	If .X was > $04, the syntax is bad so
		branch to BLK30 ($CC2B).
		-----------------------------------------
		Convert ASCII to HEX and store the
		converted values in the FILTRK ($0280)
		and FILSEC ($0285) tables:
		On entry: .Y = pointer into CMD buffer
ASCHEX	$CCA1	Zero TEMP($6F), TEMP+l($70), and TEMP+3
		($72) as a work area.
	$CCA9	Load .X with $FF.
AH10	$CCAB	Load .A with the command string byte
		from CMDBUF,Y.
	$CCAE	Test if the character in .A is numeric
		by comparing it to $40. If non-numeric,
		branch to AH2O.
	$CCB2	Test if the character in .A is ASCII
		by comparing it to $30. If it is not an
		ASCII digit, branch to AH2O.
	$CCB6	AND the ASCII digit with $0F to mask
		off the higher order bits and save this
		new value on the stack.
	$CCB9	Shift the values already in the table
		one position (TEMP+1 goes into TEMP+2;
		TEMP goes into TEMP+1).
	$CCC1	Pull the new value off the stack and
		store it in TEMP.
	$CCC4	Increment .Y and compare it to the
		command length stored in CMDSIZ ($0274).
		If more command left, branch back to
		AH10.
		Convert the values in the TEMP table
		into a single hex byte:
AH2O	$CCCA	Save the .Y pointer to the command
		string into F2PTR ($0279), clear the
		the carry flag, and load .A with $00.
AH30	$CCD0	Increment .X by 1 (index into TEMP).
	$CCD1	Compare .X to $03 to see if we're done
		yet. If done, branch to AH40.
	$CCD5	Load .Y from TEMP,Y ($6F,Y).
AH35	$CCD7	Decrement .Y by 1. If Y<0 branch to AH30
	$CCDA	Add (with carry) the value from DECTAB,X
		($CCF2,X) to .A. This adds 1, 10 or 100.
		If there is no carry, branch to AH35.
	$CCDF	Since there is a carry, clear the carry,
		increment TEMP+3, and branch back to
		AH35.
AH40	$CCE4	Save the contents of .A (the hex number)
		onto the stack.
	$CCE5	Load .X with the command segment counter
    		from F1CNT ($0277).
	$CCE8	Load .A with the carry bit (thousands)
		from TEMP+3 ($72) and store it in the
		table, FILTRK,X ($0280,X).
	$CCED	Pull the hex number off the stack and
		store it in the table, FILSEC,X($0285,X)
	$CCF1	Terminate routine with an RTS.
		-----------------------------------------
DECTAB		The decimal conversion table:
	$CCF2	Byte $01 = 1
	$CCF3	Byte $0A = 10
	$CCF4	Byte $64 = 100
		-----------------------------------------
		Free (de-allocate) block in the BAM:
BLKFRE	$CCF5	JSR to BLKTST ($CDF5) to test for legal
		block and set up track & sector.
	$CCF8	JSR to FRETS ($EF5F) to free the block
		in the BAM and mark the BAM as changed.
	$CCFB	Terminate routine with a JMP to ENDCMD
		($C194).
		-----------------------------------------
	$CCFE	Unused code: LDA #$01 / STA WBAM($02F9)
		-----------------------------------------
		Allocate a sector (block) in the BAM:
BLKALC	$CD03	JSR to BLKTST ($CDF5) to test for legal
		block and set up track & sector.
	$CD06	Load .A with the current sector pointer,
		SECTOR ($81) and save this on the stack.
	$CD09	JSR to GETSEC ($FIFA) to set the BAM and
		find the next available sector on this
		track.
	$CD0C	If Z flag is set on return to indicate
		that the desired sector is in use and
		there is no greater sector available on
		this track, branch to BA15.
	$CD0E	Pull the requested sector from the stack
		and compare it to the current contents
		of SECTOR ($81). If not equal, the
		requested sector is already in use so
		branch to BA30.
	$CD13	Requested sector is available so JSR to
		WUSED ($EF90) to allocate the sector in
		the BAM and terminate the command with
		a JMP to ENDCMD ($C194).
BA15	$CD19	Pull the desired sector off the stack.
		It is of no further use since that
		sector is already in use.
BA20	$CD1A	Set the desired sector, SECTOR ($81) to
		$00, increment the desired track, TRACK
		($80) by 1, and check if we have reached
		the maximum track count of 35 (taken
		from MAXTRK $FECB). If we have gone all
		the way, branch to BA40.
	$CD27	JSR to GETSEC ($F1FA) to set the BAM and
		find the next available sector on this
		track.
	$CD2A	If Z flag is set on return, no greater
		sector is available on this track so
		branch back to BA20 to try another track
BA30	$CD2C	Requested block is not available so load
		.A with $65 to indicate NO BLOCK ERROR
		and JMP to CMDER2 ($E645).
BA40	$CD31	No free sectors are available so load
		.A with $65 to indicate NO BLOCK ERROR
		and JMP to CMDERR ($C1C8).
		-----------------------------------------
		B-R Sub to test parameters:
BLKRD2	$CD36	JSR to BKOTST ($CDF2) to test block
		parameters and set track & sector.
	$CD39	JMP to DRTRD ($D460) to read block
		-----------------------------------------
		B-R Sub to get byte w/o increment:
GETSIM	$CD3C	JSR to GETPRE ($D12F) set parameters.
	$CD3F	Load .A with the value in (BUFTAB,X),
		($99,X).
	$CD41	Terminate routine with an RTS.
		-----------------------------------------
		B-R Sub to do read:
BLKRD3	$CD42	JSR to BLKRD2 ($CD36) to test parameters
	$CD45	Zero .A and JSR to SETPNT ($D4C8) to set
		the track and sector pointers.
	$CD4A	JSR to GETSIM ($CD3C) to read block. On
		return .Y is the LINDX.
	$CD4D	Store the byte in .A into LSTCHR,Y
		($0244,Y) as the last character.
	$CD50	Store $89 in CHNRDT,Y($F2,Y) to indicate
		that it is a random access channel and
		is now ready.
	$CD55	Exit routine with an RTS.
		-----------------------------------------
		Block read a sector:
BLKRD	$CD56	JSR to BLKRD3 ($CD42) to set up to read
		the requested sector.
	$CD59	JSR to RNGET1 ($D3EC) to read in the
		sector.
	$CD5C	Terminate routine with a JMP to ENDCMD
		($C194).
		-----------------------------------------
		U1: Block read of a sector:
		-----------------------------------------
		NOTE: The only real difference between
		a B-R command and a Ul (preferred) is
		that the U1 command move the last byte
		into the data buffer and stores $FF as
		the last byte read.
		-----------------------------------------
UBLKRD	$CD5F	JSR to BLKPAR ($CC6F) to parse the block
		parameters.
	$CD62	JSR to BLKRD3 ($CD42) to set up to read
		the requested sector.
	$CD65	Move the last character read from
		LSTCHR,Y ($0244,Y) to CHNDAT,Y ($023E,Y)
	$CD6B	Store $FF in LSTCHR,Y ($0244,Y) as the
		last character to be read.
	$CD70	Terminate routine with a JMP to ENDCMD
		($C194) which ends with an RTS.
		-----------------------------------------
		Block-write of a sector:
BLKWT	$CD73	JSR to BKOTST ($CDF2) to test the buffer
		and block parameters and set up the
		drive, track, and sector pointers.
	$CD76	JSR to GETPNT ($D4E8) to read the active
		buffer pointers. On exit, .A points into
		the buffer.
	$CD79	Transfer .A to .Y and decrement .Y.
	$CD7B	If the value in .A is greater than $02,
		branch to BW10
	$CD7F	Load .Y with $01.
BW10	$CD81	Load .A with $00.
	$CD83	JSR to SETPNT ($D4C8) to set the buffer
		pointers.
	$CD86	Transfer the value in .Y to .A and JSR
		to PUTBYT ($CFFl) to put the byte in .A
		into the active buffer of LINDX.
	$CD8A	Transfer the value of .X to .A and save
		it on the stack.
BW20	$CD8C	JSR to DRTWRT ($D464) to write out the
		block.
	$CD8F	Pop the value off the stack and transfer
		it back into .X.
	$CD91	JSR to RNGET2 ($D3EE) to set the channel
		ready status and last character.
	$CD94	Terminate routine with a JMP to ENDCMD
		($C194) which ends with an RTS.
		-----------------------------------------
		U2: Block write of a sector:
UBLKWT	$CD97	JSR to BLKPAR ($CC6F) to parse the block
		parameters.
	$CD9A	JSR to BKOTST ($CDF2) to test the buffer
		and block parameters and set up the
		drive, track, and sector pointers.
	$CD9D	JSR to DRTWRT ($D464) to write out the
		block.
	$CDA0	Terminate routine with a JMP to ENDCMD
		($C194) which ends with an RTS.
		-----------------------------------------
		Block execute a sector:
BLKEXC	$CDA3	JSR to KILLP ($F258) to kill the disk
		protection. Does nothing on the 1541!
	$CDA6	JSR to BLKRD2 ($CC6F) to read the sector
	$CDA9	Store $00 in TEMP ($6F) as the lo byte
		of the JMP address)
	$CDAD	Load .X from JOBNUM ($F9) and use it as
		an index to load the hi byte of the JMP
		address from BUFIND,X ($FEE0,X) and
		store it in TEMP+l ($70).
	$CDB4	JSR to BE10 ($CDBA) to execute the
		block.
	$CDB7	Terminate routine with a JMP to ENDCMD
		($C194) which ends with an RTS.
		-----------------------------------------
BE10	$CDBA	JMP (TEMP) Used by block execute.
		-----------------------------------------
		Set the buffer pointer: for
BLKPTR	$CDBD	JSR to BUFTST ($CDD2) to test
		allocated buffer.
	$CDC0	Load the buffer number of the channel
		requested from JUBNUM ($F9), multiply
		it by two (ASL), and transfer the result
		into .X. Load .A with the new buffer
		pointer value from FILSEC+1 ($0286) and
		store it in the buffer table BUFTAB,X
		($99,X).
	$CDC9	JSR to GETPRE ($D12F) to set up pointers
	$CDCC	JSR to RNGET2 ($D3EE) to ready the
		channel for I/O.
	$CDCF	Terminate routine with a JMP to ENDCMD
		($C194) which ends with an RTS.
		-----------------------------------------
		Test whether a buffer has been allocated
		for the secondary address given in SA.
BUFTST	$CDD2	Load .X with the file stream 1 pointer,
		F1PTR ($D3) and then increment the
		original pointer F1PTR ($D3).
	$CDD6	Load .A with that file's secondary
		address from FILSEC,X ($02.85,X).
	$CDD9	Transfer the secondary address to .Y.
		Decrement it by 2 (to eliminate the
		reserved secondary addresses 0 and 1)
		and compare the result with $0C (#12).
		If the original SA was between 2 and 14,
		it passes the test so branch to BT20.
BT15	$CDE0	Load .A with $70 to indicate no channel
		is available and JMP to CMDERR ($C1C8).
BT20	$CDE5	Store the original secondary address
		(in .A) into SA ($83) as the active SA.
	$CDE7	JSR to FNDRCH ($D0EB) to find an unused
		read channel. If none available, branch
		to BT15.
	$CDEC	JSR to GETACT ($DF93) to get the active
		buffer number. On return, store the
		active buffer number in JOBNUM ($F9).
		read channel. If none available, branch
	$CDF1	Terminate routine with an RTS.
		-----------------------------------------
		Test all block parameters: buffer
		allocated and legal block. If OK, set up
		drive, track, and sector values.
BKOTST	$CDF2	JSR to BUFTST($CDD2) to test if buffer
		is allocated for this secondary address.
		-----------------------------------------
		Set the drive number, track, and sector
		values requested for a block operation
		and test to see that these are valid.
BLKTST	$CDF5	Load .X with the channel number from
		F1PTR ($D3)
	$CDF7	Load .A with the drive number desired
		from FILSEC,X($0285,X), AND it with $01
		to mask off the default drive bit, and
		store the result as the current drive
		number, DRVNUM ($7F).
	$CDFE	Move the desired sector from FILSEC+2,X
		($0287,X) to SECTOR ($81).
	$CE03	Move the desired track from FILSEC+I,X
		($0286,X) to TRACK ($80).
	$CE08	JSR to TSCHK ($D55F) to test whether the
		track and sector values are legal.
	$CE0B	JMP to SETLDS to turn on drive active
		LED. Do RTS from there.
		-----------------------------------------
		FIND RELATIVE FILE
		-----------------------------------------
		INPUTS: (ALL 1 BYTE)           OUTPUTS: (ALL 1 BYTE)
		RECL   - record # (lo byte)    SSNUM  - side sector #
		RECH   - record # (hi byte)    SSIND  - index into SS
		RS     - record size           RECPTR - pointer into
		RELPTR - pointer into record            sector
		-----------------------------------------
FNDREL	$CE0E	JSR to MULPLY($CE2C) to find total bytes
		TOTAL = REC# x RS + RECPTR
	$CE11	JSR to DIV254 to divide by 254. The
		result is the record's location (in
		sectors) from the start of the file.
	$CE14	Save the remainder (in .A) into RELPTR
		($D7). This points into the last sector.
	$CE18	JSR to DIV120 to divide by 120. The
		result points into the side sector file.
	$CE1B	Increment the pointer into the sector,
		RELPTR ($D7) by two to bypass the two
		link bytes at the start of the sector.
	$CE1F	Move the quotient of the division by 120
		from RESULT ($8B) to SSNUM ($D5).
	$CE23	Load .A with the remainder of the
		division from ACCUM+1 ($90), multiply
		it by two (ASL) because each side sector
		pointer occupies two bytes (t & s), add
		$10 (#16) to skip the initial link table
		in the sector, and store the resulting
		side sector index (points into the
		sector holding the side sectors) into
		SSIND ($D6).
	$CE2B	Terminate routine with an RTS.
		-----------------------------------------
		Calculate a record's location in bytes.
		TOTAL = REC# x RS + RECPTR
MULPLY	$CE2C	JSR to ZERRES ($CED9) to zero the RESULT
		area ($8B-$8D).
	$CE31	Zero ACCUM+3 ($92).
	$CE33	Load .X with the LINDX ($82) and use it
		to move the lo byte of the record number
		from RECL,X ($B5) to ACCUM+1 ($90).
	$CE37	Move the hi byte of the record number
		from RECH,X ($BB) to ACCUM+2 ($91).
	$CE3B	If the hi byte of the record number is
		not $00, branch to MUL25.
	$CE3D	If the lo byte of the record number is
		$00, branch to MUL50 to adjust for
		record #0 (the first record).
MUL25	$CE41	Load .A with the lo byte of the record
		size from ACCUM+1 ($90), set the carry
		flag, subtract $01, and store the result
		back in ACCUM+1. If the carry flag is
		still set, branch to MULT50.
	$CE4A	Decrement the hi byte of the record size
		in ACCUM+2 ($91).
MUL50	$CE4C	Copy the record size from RS,X ($C7,X)
		to TEMP ($6F).
MUL100	$CE50	Do an LSR on TEMP ($6F). If the carry
		flag is clear, branch to MUL200 (no add
		this time).
	$CE54	JSR to ADDRES ($CEED) to add.
		RESULT = RESULT + ACCUM+1,2,3
MUL200	$CE57	JSR to ACCX2 ($CEE5) to multiply the
		ACCUM+1,2,3 by two.
	$CE5A	Test TEMP to see if done, if not branch
		back to MUL100.
	$CE5E	Add the byte pointer to the result.
MUL400	$CE6D	Terminate routine with an RTS.
		--------------------------------------
		DIVIDE ROUTINE:
		RESULT ($8B) = QUOTIENT ACCUM+1 ($90) = REMAINDER
		--------------------------------------
    		Divide by 254 entry point:
DIV254	$CE6E	Load .A with $FE (#254)
	$CE70	Byte $2C (skip over next instruction)
		Divide by 120 entry point:
DIV120	$CE71	Load .A with $78 (#120)
	$CE73	Store divisor into TEMP ($6F).
	$CE75	Swap ACCUM+1,2,3 with RESULT,1,2
	$CE84	JSR to ZERRES ($CED9) to zero RESULT,1,2
DIV150	$CE87	Zero .X
DIV200	$CE89	Divide by 256 by moving the value in
		ACCUM+1,X ($90,X) to ACCUM,X ($8F,X).
	$CE8D	Increment .X. If .X is not 4 yet, branch
		back to DIV200.
	$CE92	Zero the hi byte, ACCUM+3 ($92).
		Check if this is a divide by 120 by
		testing bit 7 of TEMP. If it is a divide
		by 254, branch to DIV300.
	$CE9A	Do an ASL of ACCUM ($8F) to set the
		carry flag if ACCUM > 127. Push the
		processor status on the stack to save
		the carry flag. Do an LSR on ACCUM to
		restore its original value. Pull the
		processor status back off the stack and
		JSR to ACC200 ($CEE6) to multiply the
		value in the ACCUM,1,2 by two so that
		we have, in effect, divided by 128.
		X/128 = 2 * X/256
DIV300	$CEA3	JSR to ADDRES ($CEED) to add the ACCUM
		to the RESULT.
	$CEA6	JSR to ACCX2 ($CEE5) to multiply the
		ACCUM by two.
	$CEA9	Check if this is a divide by 120 by
		testing bit 7 of TEMP. If it is a divide
		by 254, branch to DIV400.
	$CEAD	JSR to ACCX4 ($CEE2) to multiply the
		ACCUM by four. A= 4 * (2 * A) = 8 * A
DIV400	$CEB0	Add in the remainder from ACCUM ($8F)
		to ACCUM+1. If a carry is produced,
		increment ACCUM+2 and, if necessary,
		ACCUM+3.
DIV500	$CEBF	Test if remainder is less than 256 by
		ORing ACCUM+3 and ACCUM+2. If the result
		is not zero, the remainder is too large
		so branch to DIV to crunch some more.
	$CEC5	Test if remainder is less than divisor
		subtracting the divisor, TEMP ($6F) from
		the remainder in ACCUM+1 ($90). If the
		remainder is smaller, branch to DIV600.
	$CED0	Since the remainder is too large, add 1
		to the RESULT.
DIV600	$CED6	Store the new, smaller remainder in
		ACCUM+l ($90).
	$CED8	Terminate routine with an RTS.
		-----------------------------------------
		Zero the RESULT area:
ZERRES	$CED9	Load .A with $00 and store in RESULT
		($8B), RESULT+1($8C), and RESULT+2($8D).
	$CEE1	Terminate routine with an RTS.
		-----------------------------------------
    		Multiply ACCUM by 4:
ACCX4	$CEE2	JSR ACCX2 ($CEE5)
		Multiply ACCUM by 2:
ACCX2	$CEE5	Clear the carry flag.
ACC200	$CEE6	Do a ROL on ACCUM+1($90), ACCUM+2($91),
		and ACCUM+2($92).
	$CEEC	Terminate routine with an RTS.
		-----------------------------------------
		Add ACCUM, to RESULT:
ADDRES	$CEED	Load .X with $FD.
ADD100	$CEF0	Add RESULT+3,X ($8E,X) and ACCUM+4,X
		($93) and store the result in RESULT+3.
	$CEF6	Increment .X. If not $00 yet, branch
		back to ADD100.
	$CEF9	Terminate routine with an RTS.
		-----------------------------------------
		Initialize LRU (least recently used)
		table:
LRUINT	$CEFA	Load .X with $00.
LRUILP	$CEFC	Transfer .X to .A. Store the value in
		.A into LRUTBL,X ($FA,X).
	$CEFF	Increment .X and compare it to $04, the
		command channel number. If not yet
		equal, branch back to LRUILP.
	$CF04	Load .A with $06, the BAM logical index
		for the floating BAM, and store this
		value into LRUTBL,X ($FA,X).
	$CF08	Terminate routine with an RTS.
		-----------------------------------------
		Update LRU (least recently used) table:
LRUUPD	$CF09	Load .Y with $04, the command channel
		number. Load .X from LINDX ($82) the
		current channel number.
LRULP1	$CF0D	Load .A with the value from LRUTBL,Y
		($00FA,Y). Store the current channel
		number (from .X) into LRUTBL,Y.
	$CF12	Compare the value in .A with the current
		channel number in LINDX ($82). If they
		are equal, branch to LRUEXT to exit.
	$CF16	Decrement .Y the channel counter. If no
		more channels to do (Y<0) branch to
		LRUINT ($CEFA) since no match was found.
	$CF19	Transfer .A to .X and JMP to LRULP1
		.A into LRUTBL,X ($FA,X).
LRUEXT	$CF1D	Terminate routine with an RTS.
		-----------------------------------------
		Double buffer: Switch the active and
		inactive buffers.
DBLBUF	$CF1E	JSR to LRUUPD ($CF09) to update the LRU
		(least recently used) table.
	$CF21	JSR to GETINA ($DFB7) to get the LINDX
		channel's inactive buffer number (in .A)
	$CF24	On return, if there is an inactive
		buffer, branch to DBL15.
	$CF26	There is no inactive buffer so make one!
		JSR to SETDRN ($D1D3) to set the drive
		number to the one in LSTJOB.
	$CF29	JSR to GETBUF ($D28E) to get. a free
		buffer number. If no buffers available,
		branch to DBL30 and abort.
	$CF2E	JSR to PUTINA ($DFC2) to store the new
		buffer number as the inactive buffer.
	$CF31	Save the current values of TRACK ($80)
		and SECTOR ($81) on the stack.
	$CF37	Load .A with $01 and JSR to DRDBYT
    		($D4F6) to direct read .A bytes. Store
		the byte read as the current SECTOR($81)
	$CF3E	Load .A with $00 and JSR to DRDBYT
		($D4F6) to direct read .A bytes. Store
		the byte read as the current TRACK($80).
	$CF45	If the TRACK byte was $00 (last sector
		in the file), branch to DBL10.
	$CF47	JSR to TYPFIL ($D125) to determine the
		file type we are working on. If it is a
		relative file, branch to DBL05.
	$CF4C	JSR to TSTWRT ($DDAB) to see if we are
		writing this file or just reading it.
		If just reading, branch to DBL05 to read
		ahead.
	$CF51	We are writing so JSR to TGLBUF ($CF8C)
		to toggle the buffers. On return, JMP
		to DBL08.
DBL05	$CF57	JSR to TGLBUF ($CF8C) to toggle the
		inactive and inactive buffers.
	$CF5A	JSR to RDAB ($DE57) to read in the next
		sector of the file (into active buffer).
DBL08	$CF5D	Pull the old SECTOR($81) and TRACK($80)
		values from the stack and restore them.
	$CF63	JMP to DBL20.
DBL10	$CF66	Pull the old SECTOR($81) and TRACK($80)
		values from the stack and restore them.
DBL15	$CF6C	JSR to TGLBUF ($CF8C) to toggle the
		inactive and active buffers.
DBL20	$CF6F	JSR to GETACT ($DF93) to get the active
		buffer number (in .A). Transfer the
		active buffer number into .X and JMP to
		WATJOB ($D599) to wait until job is done
		-----------------------------------------
DBL30	$CF76	No buffers to steal so load .A with $70
		to indicate a NO CHANNEL error and JMP
		to CMDERR ($C1C8).
		-----------------------------------------
DBSET	$CF7B	Set up double buffering:
		JSR to LRUUPD ($CF09) to update the LRU
		(least recently used) table.
	$CF7E	JSR to GETINA ($DFB7) to get the number
		of the inactive buffer (in .A).
	$CF81	If there is an inactive buffer, branch
		to DBS10 to exit.
	$CF83	JSR to GETBUF ($DF93) to find an unused
		buffer. If no buffers available, branch
		to DBL30 ($CF76) to abort.
DBS10	$CF88	JSR to PUTINA ($DFC2) to set the buffer
		found as the inactive buffer.
	$CF8B	Terminate routine with an RTS.
		-----------------------------------------
TGLBUF	$CF8C	Toggle the inactive & active buffers:
		Input: LINDX = current channel #
		Load .X with the channel number from
		LINDX ($82) and use it as an index to
		load .A with the buffer number from
		BUFO,X ($A7). EOR this number with $80
		to change its active/inactive state and
		store the modified value back in BUFO,X.
	$CF94	Load .A with the buffer number from
		BUF1,X ($AE). EOR this number with $80
		to change its active/inactive state and
		store the modified value back in BUF1,X.
	$CF9A	Terminate routine with an RTS.
		-----------------------------------------
		Write byte to internal write channel:
PIBYTE	$CF9B	Load .X with $12 (#18) the secondary
		address of the internal write channel
		and use it to set the current secondary
		address SA ($83).
	$CF9F	JSR to FNDWCH ($D107) to find an unused
		write channel.
	$CFA2	JSR to SETLED ($C100) to turn on the
		drive active LED.
	$CFA5	JSR to TYPFIL ($D125) to determine the
		current file type. If NOT a relative
		file, branch to PBYTE ($CFAF).
	$CFAA	Load .A with $20 (the overflow flag bit)
		and JSR to CLRFLG ($DD9D) to clear the
		overflow flag.
		-----------------------------------------
		Write byte to any channel:
PBYTE	$CFAF	Load .A with the current secondary
		address from SA ($83). Compare the SA
		with $0F (#15) to see if we are using is
		the command channel. If SA=$0F, this
		the command channel so branch to L42
		($CFD8). If not, branch to L40 ($CFBF).
		--------------------------------------
PUT	$CFB7	Main routine to write to a channel:
		Check if this is the command channel or
		a data channel by loading the original
		secondary address from ORGSA ($84),
L40	$CFBF	ANDing it with $8F, and comparing the
		result with $0F (#15). If less than 15,
		this is a data channel so branch to L42.
		JSR to TYPFIL ($D125) to determine the
L41	$CFC4	file type. If we are NOT working on a
	$CFC9	sequential file, branch to L41.
		Since this is a sequential file, load
		.A with the data byte from DATA ($85)
		and JMP to WRTBYT ($D19D) to write the
		byte to the channel.
		If Z flag not set, we are writing to a
L46	$CFCB	true random access file (USR) so branch
	$CFCE	to L46.
		We are writing to a relative (REL) file
		so JMP to WRTREL ($E0AB).
		Since this is a USR file, load .A with
L42	$CFD3	the data byte from DATA ($85) and JSR
	$CFD8	to PUTBYT ($CFF1) to write it to the
		channel.
		To prepare to write the next byte: load
		.Y with the channel number from LINDX
		($82) and JMP to RNGET2 ($D3EE).
		Since this is the command channel, set
L50	$CFDC	LINDX ($82) to $04 (the command channel
	$CFE3	number).
	$CFE8	Test if command buffer is full by doing
		a JSR to GETPNT ($D4E8) to get the
		position of the last byte written and
		comparing it to $2A. If they are equal,
		the buffer is full so branch to L50.
		Since there is space, load .A with the
		command message byte from DATA ($85) and
		JSR to PUTBYT ($CFF1) to write it to the
		command channel.
		Test if this is the last byte of the
L45	$CFEC	message by checking the EOIFLG ($F8).
	$CFED	If it is zero, this is the last byte so
		branch to L45.
		Terminate command with an RTS.
		Increment CMDWAT ($0255) to set the
	$CFF0	command-waiting flag.
		Terminate command with an RTS.
		-----------------------------------------
PUTBYT	$CFF1	Put byte in .A into the active buffer
	$CFF2	of the channel in LINDX:
		Save byte in .A onto the stack.
		JSR to GETACT ($DF93) to get the active
		buffer number (in .A). If there is an
		active buffer, branch to PUTB1.
PUTB1	$CFF7	No active buffer so pull the data byte
	$CFFD	off the stack, load .A with $61 to
	$CFFF	indicate a FILE NOT OPEN error, and JMP
	$D002	to CMDERR ($C1C8).
	$D004	Multiply the buffer number by 2 (ASL)
		and transfer this value to .X
		Pull the data byte off the stack and
		store it in the buffer at (BUFTAB,X)
		($99,X).
		Increment the buffer pointer BUFTAB,X
		NOTE: Z flag is set if this data byte
		was stored in the last position
		in the buffer!
		Terminate routine with an RTS.
		----------------------------------------------------------
		INITIALIZE DRIVE(S)
		----------------------------------------------------------
		Initialize drive(s): (Disk command)
INTDRV	$D005	JSR to SIMPRS ($C1D1) to parse the
		disk command.
	$D008	JSR to INITDR ($D042) to initialize the
		drive(s).
ID20	$D00B	Terminate command with a JMP to ENDCMD
		($C194).
		-----------------------------------------
		Initialize drive given in DRVNUM:
ITRIAL	$D00E	JSR to BAM2A ($F10F) to get the current
	$D011	BAM pointer in .A. and use
		LINDX
		Transfer the BAM pointer to .Y
		it as an
		index to load the BAM
		from BUFO,Y ($A7,Y) into .X. If there
		is a valid buffer number for the BAM
		(not $FF), branch to IT30.
	$D018	No buffer so we had better get one!
		Save the BAM pointer in .A on the stack
		and JSR to GETBUF ($D28E) to find an
		unused buffer. If a buffer is available,
		branch to IT20.
	$D01F	No buffer available so load .A with $70
		to indicate a NO CHANNEL error and JSR
		to CMDER3 ($E648).
IT20	$D024	Pull the BAM pointer from the stack and
		transfer it to .Y. Transfer the new
		buffer number from .X to .A, OR it with
		$80 (to indicate an inactive status),
		and store the result in BUFO,Y ($00A7,Y)
		to allocate the buffer.
IT30	$D02C	Transfer the buffer number from .X to
		.A, AND it with $0F to mask off the
		inactive status bit, and store it in
		JOBNUM ($F9).
	$D031	Set SECTOR ($81) to $00 and TRACK ($80)
		to $12 (#18) to prepare to read the BAM.
	$D03A	JSR to SETH ($D6D3) to set up the seek
	$D03D	image of the BAM header.
		Load .A with $B0 (the job code for a
		SEEK) and JMP to DOJOB ($D58C) to do the
		seek to track 18. Does an RTS when done.
		-----------------------------------------
INITDR	$D042	Initialize drive:
		JSR to CLNBAM ($F0D1) to zero the track
	$D045	numbers for the BAM.
		JSR to CLDCHN ($D313) to allocate a
	$D048	channel for the BAM.
		JSR to ITRIAL ($D00E) to allocate a
	$D04D	buffer for the BAM and seek track 18.
		Store $00 in MDIRTY,X ($0251) to
	$D052	indicate that the BAM for drive .X is
		NOT DIRTY (BAM in memory matches BAM on
		the diskette).
		Set the master ID for the diskette in
	$D05D	DSKID,X ($12/3 for drive 0) from the
		track 18 header values ($16/17) read
		during the seek to track 18.
		JSR to DOREAD ($D586) to read the BAM
	$D060	into the buffer.
		Load the disk version(#65 for 4040/1541)
	$D06F	from the $0X02 position in the BAM and
		store it in DSKVER,X($0101,drive number)
		Zero WPSW,X ($1C,X) to clear the write
NFCALC	$D075	protect switch and NODRV,X ($FF,X) to
		clear the drive-not-active flag.
		Count the number of free blocks in BAM
	$D078	JSR to SETBPT ($EF3A) to set the bit map
		pointer and read in the BAM if necessary
		Initialize .Y to $04 and zero .A and .X
NUMF1	$D07D	(.X will be the hi byte of the count).
		Clear carry and add (BMPNT),Y; ($6D),Y
	$D082	to the value in .A. If no carry, branch
		to NUMF2.
		Increment .X (the hi byte of the count).
NUMF2	$D083	Increment .Y four times so it points to
	$D08B	the start of the next track byte in the
		BAM. Compare .Y to $48 (the directory
		track location). If .Y=$48, branch to
		NUMF2 to skip the directory track.
		Compare .Y to $90 to see if we are done.
	$D08F	If there is more to do, branch to NUMF1.
		All done. Save the lo byte of the count
		on the stack and transfer the hi byte
		from .X to .A. Load .X with the current
		drive number from DRVNUM ($7F) and store
		the hi byte of the count (in .A) into
		NDBH,X ($02FC,X). Pull the lo byte of
		the count off the stack and save it in
		NDBL,X ($02FA,X).
	$D09A	Terminate routine with an RTS.
		-----------------------------------------
STRRD	$D09B	Start reading ahead:
		Use the values in TRACK and SECTOR to
	$D09E	read a data block. Use the track and
	$D0A1	sector pointers to set up the next one.
	$D0A4	JSR to SETHDR ($D6D0) to set up the
	$D0A9	header image using TRACK ($80) and
	$D0AE	SECTOR ($81) values.
		JSR to RDBUF ($D0C3) to read the first
		block into the data buffer.
		JSR to WATJOB ($D599) to wait for the
		read job to be completed.
		JSR to GETBYT ($D137) to get the first
		byte from the data buffer (track link)
		and store it in TRACK ($80).
		JSR to GETBYT ($D137) to get the second
		byte from the data buffer (sector link)
		and store it in SECTOR ($81).
		Terminate routine with an RTS.
		-----------------------------------------
STRDBL	$D0AF	Start double buffering: (reading ahead)
		JSR to STRRD ($D09B) to read in a data
STR1	$D0B2	block and set up the next one.
	$D0B6	Check the current TRACK ($80) value. If
	$D0B7	not $00, we are not at the end of the
		file so branch to STR1.
		Terminate routine with an RTS.
		JSR to DBLBUF ($CF1E) to set up buffers
	$D0BA	and pointers for double buffering and
	$D0BD	set TRACK and SECTOR for the next. block.
	$D0C0	JSR to SETHDR ($D6D0) to set up the
		header image using TRACK ($80) and
		SECTOR ($81) values.
		JSR to RDBUF ($D0C3) to read the next
		block into the data buffer.
		JMP to DBLBUF ($CF1E) to set up buffers
		and pointers for double buffering and
		set TRACK and SECTOR for the next block.
		-----------------------------------------
RDBUF	$D0C3	Start a read job of TRACK and SECTOR
		Load .A with $80, the job code for a
		read, and branch to STRTIT ($D0C9).
		-----------------------------------------
WRTBUF	$D0C7	Start a write job of TRACK and SECTOR
		Load .A with $90, the job code for a
STRTIT	$D0C9	write.
		Store command desired (in .A) as the
	$D0CC	current command in CMD ($024D).
		JSR to GETACT ($DF93) to get the active
		buffer number (in .A). Transfer the
		active buffer number into .X.
	$D0D0	JSR to SETLJB ($D506) to set up drive
		number (from the last job), check for
		legal track & sector, and, if all OK,
		do the job. On return .A=job number and
	$D0D3	.X=buffer number.
		Transfer buffer number from .X to .A and
		save it on the stack. Multiply the
		buffer number by two (ASL) and transfer
		the result into .X and use it as an
		index to store $00 in the buffer table
		pointer BUFTAB,X ($99,X)
WRTC1	$D0DB	JSR to TYPFIL ($D125) to get the file
	$D0E2	type. Compare the file type to $04. If
	$D0E8	this is not a sequential file, branch to
		WRTC1.
		Since this is a sequential file,
		increment the lo byte of the block count
		in NBKL,X ($B5,X) and, if necessary, the
		hi byte in NBKH,X ($BB,X).
		Pull the original buffer number off the
	$D0EA	stack and transfer it back into .X.
		Terminate routine with an RTS.
		-----------------------------------------
FNDRCH	$D0EB	Find the assigned read channel:
		Compare the current secondary address
FNDC20	$D0F1	from SA ($83) with $13 (#19) the
		$D0F3
		highest allowable secondary address+l.
		If too large, branch to FNDC20.
		AND the secondary address with $0F
		NOTE: This masks off the high order bits
		of the internal channel sec adr's:
		Internal read $11 (17) -> $01
		Internal write $12 (18) -> $02
		Compare the sec addr in .A with $0F(15),
FNDC25	$D0F7	the command channel sec addr. If they
		$D0F9
		are not equal, branch to FNDC25.
		Load .A with $10, the sec addr error
		value.
		Transfer the sec addr from .A to .X,
	$D100	set the carry flag, and load the
		channel number from LINTAB,X ($022B,X).
		If bit 7 is set, no channel has been
		assigned for this sec addr, so branch to
		FNDC30 to exit (with carry bit set).
		AND the current channel number with $0F
		and store the result as the current.
FNDC30	$D106	
		channel number in LINDX ($82). Transfer
		the channel number into .X and clear the
		carry bit.
		Terminate routine with an RTS.
		----------------------------------------
FNDWCH	$D107	Find the assigned write channel:
		Compare the current secondary address
	$D10D	from SA ($83) with $13 (#19) the
		highest allowable secondary address+l.
		If too large, branch to FNDW13.
		AND the secondary address with $0F
		NOTE: This masks off the high order bits
		of the internal channel sec adr's:
FNDW13	$D10F	Internal read $11 (17) -> $01
		Internal write $12 (18) -> $02
		Transfer the sec addr from .A to .X,
	$D113	and load the channel number assigned to
		this sec addr
		Transfer this
		from LINTAB,X ($022B,X).
		channel .Y.
		number to
	$D114	Do an ASL of the channel number in .A.
	$D115	If a channel has been assigned for this
	$D117	sec addr (bit 7 of LINTAB,X is not set)
		branch to FNDW15.
		If no channel assigned has been assigned
FNDW10	$D119	for this secondary address (bit 6 also
		set), branch to FNDW20 and abort.
		addr from .Y
		Transfer the original sec
FNDW15	$D121	to .A, AND it with $0F to mask off any
		high order bits, and store it in LINDX
		($82) as the currently active channel.
		Transfer the channel number to .X, clear
		the carry flag, and terminate with RTS.
		If bit 6 of LINTAB,X is set (indicates
FNDW20	$D123	an inactive channel), branch to FNDW10.
		Abort by setting the carry flag and
		terminate the routine with an RTS.
		-----------------------------------------
TYPFIL	$D125	Get current file type:
		Load .X with the current channel number
	$D127	from LINDX ($82).
		Load .A with the file type from the
	$D129	file type table, FILTYP,X ($EC,X).
		Divide the file type by 2 (LSR), AND
	$D12E	it with $07 to mask off higher order
		bits, and compare the result with $04
		'(set the Z flag if it is a REL file!).
		Terminate the routine with an RTS.
		-----------------------------------------
GETPRE	$D12F	Set buffer pointers:
		JSR to GETACT ($DF93) to get the active
	$D132	buffer number (in .A).
		Multiply the buffer number by 2 (ASL)
	$D134	and transfer the result into .X.
		Load .Y with the current channel number
	$D136	from LINDX ($82).
		Terminate the routine with an RTS.
		-----------------------------------------
GETBYT	$D137	Read one byte from the active buffer:
		If last data byte in buffer, set Z flag.
	$D13A	JSR to GETPRE to set buffer pointers.
		Load .A with the pointer to the last
	$D13D	character read from LSTCHR,Y ($0244,Y).
		If pointer is zero, branch to GETB1.
	$D13F	Load the data byte from (BUFTAB,X)
	$D142	($99,X) and save it on the stack.
		Load the pointer from BUFTAB,X ($99,X)
	$D149	and compare it to the pointer to the
		last character read in LSTCHR,Y. If the
		pointers are not equal, branch to GETB2.
		Store $FF in BUFTAB,X ($99,X)
GETB2	$D14D	Pull the data byte off the stack and
	$D150	increment BUFTAB,X ($99,X). This will
		set the Z flag if this is the last byte.
		Terminate routine with an RTS.
GETB1	$D151	Load the data byte from (BUFTAB,X)
		($99,X).
	$D153	Increment BUFTAB,X ($99,X).
	$D155	Terminate routine with an RTS.
		-----------------------------------------
RDBYT	$D156	Read byte from file:
		The next file will be read if necessary
		and CHNRDY($F2) will be set to EOI if
		we have read the last character in file.
		JSR to GETBYT to read a byte from the
	$D162	active buffer. On return, if Z flag is
		not set, we did not read the last byte
		in the buffer so branch to RD3 and RTS.
		We read the last byte so load .A with
RD01	$D164	$80, the EOI flag.
		Store the channel status (in .A) into
	$D167	CHNRDY,Y ($00F2,Y).
		Load .A with the byte from DATA ($85).
	$D169	Exit from routine with an RTS.
RD1	$D16A	JSR to DBLBUF ($CF1E) to begin double
	$D16D	buffering.
		Load .A with $00 and JSR to SETPNT
	$D172	($D4C8) to set up the buffer pointers
		JSR to GETBYT ($D137) to read the first
	$D175	byte from the active buffer (track link)
		Compare the track link to $00. If it is
		$D179 $00, there is no next block so branch
		to RD4.
		There is another block in this file so
	$D17B	store the track link in TRACK ($80).
		JSR to GETBYT ($D137) to read the next
	$D180	byte from the active buffer(sector link)
		and store it in SECTOR ($81).
		JSR to DBLBUF ($CF1E) to begin double
		buffering.
	$D183	JSR to SETDRN ($D1D3) to set up the
		drive number.
	$D186	JSR to SETHDR ($D6D0) to set up the
		next header image.
	$D189	JSR to RDBUF ($D0C3) to read in the next
		block in the file.
	$D18C	JSR to DBLBUF ($CF1E) to toggle the
		active & inactive buffers & read ahead.
	$D18F	Load .A with the byte from DATA ($85).
RD3	$D191	Exit from routine with an RTS.
RD4	$D192	JSR to GETBYTE ($D137) to get the next
		byte.
	$D195	Load .Y with the current channel number
		from LINDX ($82) and store the new
		character as the pointer to the last
		character read from the data buffer
		LSTCHR,Y ($0244,Y).
	$D19A	Load .A with the byte from DATA ($85).
	$D19C	Exit from routine with an RTS.
		-----------------------------------------
		Write character to the active channel:
		If this fills the buffer, write the
		data buffer out to disk.
WRTBYT	$D19D	JSR to PUTBYT ($CFFl) to write the byte
		to the active channel.
	$D1A0	If Z flag is set on return, the buffer
		is full so branch to WRTO.
	$D1A2	Exit from routine with an RTS.
WRTO	$D1A3	JSR to SETDRN ($D1D3) to set the current
		drive number from the one in LSTJOB.
	$D1A6	JSR to NXTTS ($F11E) to get the next
		available track and sector.
	$D1A9	Load .A with $00 and JSR to SETPNT
		($D4C8) to set up the buffer pointers.
	$D1AB	Load .A with the next available track
		from TRACK ($80) and JSR to PUTBYT
		($CFF1) to store the track link.
	$D1B3	Load .A with the next available sector
		from SECTOR ($81) and JSR to PUTBYT
		($CFF1) to store the sector link.
	$D1B8	JSR to WRTBUF ($D0C7) to write out the
		buffer to disk.
	$D1BB	JSR to DBLBUF ($CF1E) to toggle the
		active and inactive buffers and set up
		the next inactive buffer.
	$D1BE	JSR to SETHDR ($D6D0) to set up the
		header image for the next block.
	$D1C1	Load .A with $02 (to bypass the track
		and sector link) and JMP to SETPNT to
		set up the pointers to the next buffer.
		-----------------------------------------
		Increment the pointer of the active
		buffer by .A
INCPTR	$D1C6	Store the value from .A in TEMP ($6F).
	$D1C8	JSR to GETPNT ($D4E8) to get the active
		buffer pointer (in .A).
	$D1CB	Clear the carry flag and add the value
		from TEMP ($6F). Store the result into
		BUFTAB,X ($99,X) and into DIRBUF ($94).
	$D1D2	Terminate routine with an RTS.
		-----------------------------------------
		Set drive number:
SETDRN	$D1D3	Sets DRVNUM to the same drive as was
		used on the last job for the active
		buffer.
		JSR to GETACT ($D4E8) to get the active
		buffer number (in .A).
	$D1D6	Transfer the buffer number to .X and use
		it as an index to load the last job
		number from LSTJOB,X ($025B) into .A.
	$D1DA	AND the job number with $01 to mask off
		all but the drive number bit and store
		the result as the current drive number
		in DRVNUM ($7F).
	$D1DE	Terminate routine with an RTS.
		-----------------------------------------
		Open a new write channel:
		.A = number of buffers needed
		The routine allocates a buffer number
		and sets the logical file index, LINDX.
GETWCH	$D1DF	Set the carry flag to indicate that we
		want a write channel.
	$D1E0	Branch to GETR2.
		-----------------------------------------
		Open a new read channel:
		.A = number of buffers needed
		The routine allocates a buffer number
		and sets the channel#, LINDX.
GETRCH	$D1E2	Clear the carry flag to indicate that we
		want a read channel.
GETR2	$D1E3	Save the processor status (the carry
		flag) onto the stack.
	$D1E4	Save the number of buffer needed (in .A)
		into TEMP ($6F).
	$D1E6	JSR to FRECHN ($D227) to free any
		channels associated with this secondary
		address.
	$D1E9	JSR to FNDLNX ($D37F) to find the next
		free logical index (channel) to use and
		allocate it.
	$D1EC	Store the new channel number in LINDX
		as the current channel number.
	$D1EE	Load .X with the current secondary
		address from SA ($83).
	$D1F0	Pull the processor status off the stack
		and if carry flag is clear (read),
		branch to GETR55.
GETR52	$D1F3	OR the channel number in .A with $80 to
		set bit 7 to indicate a write file.
GETR55	$D1F5	Store the channel number (in .A) into
		the logical index table, LINTAB,X
		($022B,X).
		NOTE: Bit 7 set for a write channel
	$D1F8	AND the channel number in .A with $3F
		to mask off the write channel bit and
		transfer the result to .Y.
	$D1FB	De-allocate any buffers associated with
		this channel by storing $FF in BUFO,Y
		($00A7,Y), in BUF1,Y ($00AE,Y), and in
		SS,Y ($000D,Y).
GETR3	$D206	Decrement the value in TEMP ($6F). This
		is the number of buffers to allocate.
		If there are no more to allocate ($FF),
		branch to GETR4 and exit.
	$D20A	JSR to GETBUF ($D28E) to allocate a new
		buffer. If a buffer was allocated,
		branch to GETR5.
GETERR	$D20F	No buffers available, so JSR to RELBUF
		($D25A) to release any buffers allocated
	$D212	Load .A with $70 to indicate a NO
		CHANNEL error and JMP to CMDERR ($C1C8).
GETR5	$D217	Store the buffer number (in .A) into
		BUFO,Y ($00A7,Y).
	$D21A	Decrement the value in TEMP ($6F). This
		is the number of buffers to allocate.
		If there are no more to allocate ($FF),
		branch to GETR4 and exit.
	$D21E	JSR to GETBUF ($D28E) to allocate a new
		buffer. If a buffer was NOT allocated,
		branch to GETERR and abort.
	$D223	Store the buffer number (in .A) into
		BUF1,Y ($00AE,Y).
GETR4	$D226	Terminate routine with an RTS.
		-----------------------------------------
		Free channel associated with SA
		Read and write channels are freed. The
		command channel is not freed.
FRECHN	$D227	Load .A with the secondary address from
		SA ($83). Compare it with $0F (#15), the
		command channel secondary address. If
		the secondary address is not $0F, branch
		to FRECO.
	$D22D	Since we are not to free the command
		channel, simply exit with an RTS.
		-----------------------------------------
		Free data channel associated with SA:
FRECO	$D22E	Load .X with the secondary address from
		SA ($83).
	$D230	Load .A with the channel number
		associated with this secondary address
		from LINTAB,X ($022B,X). If it is $FF,
		there is no associated channel so branch
		to FRE25 and exit.
	$D237	AND the channel number with $3F to mask
		off the higher order bits and store the
		result as the current channel in LINDX
		($82).
	$D23B	Free the channel by storing $FF into
		LINTAB,X ($022B,X).
	$D240	Load .X with the channel number from
		LINDX ($82) and store $00 as the channel
		status (free) in CHNRDY,X ($F2,Y).
	$D246	JSR to RELBUF ($D25A) to release buffers
RELINX	$D249	Load .X with the channel number from
		LINDX ($82) and .A with $01.
REL15	$D24D	Decrement .X, the channel number. If
		it is $FF (no lower channel numbers),
		branch to REL10.
	$D250	Do an ASL on the value in .A. Note that
		the bit set shifts left one position
		each time through the loop.
	$D251	If .A <> 0, branch to REL15 (always).
REL10	$D253	OR the value in the accumulator with
		LINUSE ($0256) to free the channel
		(bit = 1 for free; bit = 0 for used).
		Store the resulting value back in
		LINUSE ($0256).
FRE25	$D259	Terminate routine with an RTS.
		-----------------------------------------
		Release buffers associated with channel:
RELBUF	$D25A	Load .X with the channel number from
		LINDX ($82).
	$D25C	Load .A with the buffer number for this
		channel from BUFO,X ($A7,X). Compare the
		buffer number with $FF (free). If it is
		already free, branch to REL1.
	$D262	Save the buffer number on the stack and
		store $FF into BUFO,X ($A7,X) to free
		this buffer.
	$D267	Pull the buffer number off the stack and
		JSR to FREBUF ($D2F3) to free the buffer
REL1	$D26B	Load .X with the channel number from
		LINDX ($82).
	$D26D	Load .A with the buffer number for this
		channel from BUF1,X ($AE,X). Compare the
		buffer number with $FF (free). If it is
		already free, branch to REL2.
	$D273	Save the buffer number on the stack and
		store $FF into BUF1,X ($AE,X) to free
		this buffer.
	$D278	Pull the buffer number off the stack and
		JSR to FREBUF ($D2F3) to free the buffer
REL2	$D27C	Load .X with the channel number from
		LINDX ($82).
	$D27E	Load .A with the side sector for this
		channel from SS,X ($CD,X). Compare the
		side sector with $FF (free). If it is
		already free, branch to REL3.
	$D284	Save the side sector on the stack and
		store $FF into SS,X ($CD,X) to free the
		side sector pointer.
	$D289	Pull the side sector off the stack and
		JSR to FREBUF ($D2F3) to free any buffer
REL3	$D28D	Terminate routine with an RTS.
		-----------------------------------------
GETBUF	$D28E	Get a free buffer number: .Y=channel #
		If successful, initialize JOBS & LSTJOB
		and return with buffer number in .A.
		If not successful, .A = $FF; N flag set.
	$D28E	Save channel number by transferring it
		from .Y to .A and pushing it on the
		stack.
	$D290	Load .Y with $01 and JSR to FNDBUF
		($D2BA) to find a free buffer (# in .X).
		If one is found, branch to GBF1.
	$D297	Decrement .Y and JSR to FNDBUF ($D2BA)
		to find a free buffer (# in .X). If one
		found, branch to GBF1.
	$D29D	Can't find a free one so let's try to
		steal one! JSR to STLBUF ($D339) to try
		to steal an inactive one. On return,
		buffer # in .A so transfer it to .X. If
		we didn't get one, branch to GBF2.
GBF1	$D2A3	Wait till any job using JOBS,X ($00,X)
		is completed.
	$D2A7	Clear the job queue by setting JOBS,X
	$D2AE	($00,X) and LSTJOB,X ($025B,X) to the
		current drive number using the value
		from DRVNUM ($7F).
		Transfer the buffer number from .X to .A
		multiply it by two (ASL), and transfer
		the result to .Y.
	$D2B1	Store a $02 on BUFTAB,Y ($0099,Y) so the
		pointer points beyond the track and
		sector link.
GBF2	$D2B6	Restore the original .Y value from the
		stack.
	$D2B8	Transfer the buffer number from .X to .A
		to set the N flag if not successful.
	$D2B9	Terminate routine with an RTS.
		-----------------------------------------
		Find a free buffer and set BUFUSE:
		On entry: .Y = index into BUFUSE
		Y=0 buffers 0-7; Y=1 buffers 8-15
		If successful, .X = buffer number
		If not successful, .X = $FF; N flag set
FNDBUF	$D2BA	Load .X with $07 (for bit test)
FBI	$D2BC	Load .A with BUFUSE,Y ($024F,X). Each
		bit indicates whether a buffer is free
		(1) or in use (0). AND this value in .A
		with the bit mask, BMASK,X ($EFE9,X).
		Each of these masks has just one bit
		set. If the result of the AND is $00,
		we have found a free buffer so branch
		to FB2.
	$D2C4	Decrement .X to try next buffer. If any
		left, branch back to FBI.
	$D2C7	No more buffers to try (.X=$FF) so exit
		with an RTS.
FB2	$D2C8	Found a free buffer so let's grab it!
		Load .A with the value in BUFUSE,Y
		($024F,Y), FOR it with the bit map for
		the free buffer, BMASK,X ($EFE9,X), and
		store the result back in BUFUSE,Y.
	$D2D1	Transfer the buffer number from .X to
		.A and if .Y is $00, branch to FB3.
	$D2D5	Since .Y is $01 (never happens on the
		1541), we have to add 8 to the buffer
		number. So: Clear the carry flag and add
		$08 to the buffer number in .A.
FB3	$D2D8	Transfer the buffer number from .A to .X
FRI20	$D2D9	Terminate routine with an RTS.
		-----------------------------------------
		Free the inactive buffer:
FREIAC	$D2DA	Load .X with the current channel number
		from LINDX ($82).
	$D2DC	Load .A with the buffer number from
		BUFO,X ($A7,X). If bit 7 is set, branch
		to FRI10.
	$D2E0	Transfer the channel number from .X to
		.A, clear the carry flag, add $07 (the
		maximum number of channels +1), and
		transfer the result back into .X. This
		is the alternate buffer for this channel
	$D2E5	Load .A with the buffer number from
		BUFO,X ($A7,X). If bit 7 is NOT set,
		this buffer is active too so exit to
		FRI20 (above).
FRI10	$D2E9	Compare the buffer number to $FF. If it
		is $FF, the buffer is free already so
		exit to FRI20 (above).
	$D2ED	Save the buffer number on the stack.
	$D2EE	Free the buffer by storing $FF into
		BUFO,X ($A7,X).
	$D2F2	Pull the buffer number off the stack.
FREBUF	$D2F3	Free buffer in BUFUSE:
		AND the buffer number with $0F to mask
		off any higher order bits, transfer the
		result into .Y and increment .Y by 1.
	$D2F7	Load .X with $10 (#16) 2 * 8 bits
FREB1	$D2F9	Loop to ROR BUFUSE+1 ($0250) and BUFUSE
		($024F) 16 times. Use .Y to count down
		to O. When .Y is zero, the bit that
		corresponds to the buffer we want is
		in the carry flag so we clear the carry
		bit to free that buffer. We then keep
		looping until .X has counted down all
		the way from $10 to $FF. When .X reaches
		$FF, the bits are all back in the right
		places, so exit with an RTS.
		-----------------------------------------
		Clear all channels except the CMD one:
CLRCHN	$D307	Set the current secondary address in SA
		($83) to $0E (#14)
CLRC1	$D30B	JSR to FRECHN ($D227) to free the
		channel whose secondary address is SA
	$D30E	Decrement the value in SA ($83). If it
		is not $00, branch back to CLRC1.
	$D312	Terminate routine with an RTS.
		-----------------------------------------
		Close all channels except the CMD one:
CLDCHN	$D313	Set the current secondary address in SA
		($83) to $0E (#14)
CLSD	$D317	Load .X with the secondary address from
		SA ($83) and use it as an index to load
		.A with the channel number from LINTAB,X
		($022B,X). Compare the channel number
		with $FF; if equal, no channel has been
		assigned so branch to CLD2.
	$D320	AND the channel number with $3F to mask
		off the higher order bits and store the
		result in LINDX ($82) as the current
		channel number.
	$D324	JSR to GETACT to get the active buffer
		number for this channel (returned in .A)
	$D327	Transfer the buffer number to .X and
		use it load .A with the last job number
		for this buffer from LSTJOB,X ($025B,X).
	$D32B	AND the last job number with $01 and
		compare it with the current drive number
		in DRVNUM ($7F). If not equal, branch to
		CLD2.
	$D331	JSR to FRECHN ($D227) to free this
		channel.
	$D334	Decrement the secondary address in SA
		($83) and if there are more to do (not
		$FF yet), branch back to CLSD
	$D338	Terminate routine with an RTS.
		--------------------------------------
		Steal an inactive buffer:
		Scan the least recently used table and
		steal the first inactive buffer found.
		Returns the stolen buffer number in .A
STLBUF	$D339	Save the value in TO ($6F) on the stack
		and zero .Y (the index to LRUTBL).
STL05	$D33E	Load .X (the channel index) with the
		value from LRUTBL,Y ($FA,Y).
	$D340	Load .A with the buffer status for this
		channel from BUFO,X ($A7,X). If this
		buffer is active (status < 128), branch
		to STL10.
	$D344	Compare the status to $FF (unused). If
		not equal, it's inactive so branch to
		STL30 to steal it!
STL10	$D348	Transfer the channel number from .X to
		.A, clear the carry flag, add $07 (the
		maximum number of channels +1), and
		transfer the result back into .X. Note
		.X now points to the alternative buffer
		for this channel.
	$D34D	Load .A with the buffer status for this
		channel from BUFO,X ($A7,X). If this
		buffer is active (status < 128), branch
		to STL30.
STL20	$D355	Increment .Y and compare the new value
		with #$05 (the maximum number of
		channels + 1). If there are still some
		channels left to check, branch to STL05
	$D35A	No luck stealing a buffer so load .X
		with $FF (indicates failure) and branch
		to STL60 to exit.
STL30	$D35E	Store the channel number (in .X) into
		T0 ($6F) temporarily.
	$D360	AND the buffer number in .A with $3F to
		mask off any higher order bits and
		transfer the result to .X.
STL40	$D363	Check if the buffer is being used for
		a job currently underway by loading .A
		with the job queue byte for the buffer
		from JOBS,X ($00,X). If bit 7 is set,
		a job is in progress so branch back to
		STL40 to wait for completion.
	$D367	Compare the job queue value with $02 to
		see if any errors occurred. If there
		were no errors (job queue was $01),
		branch to STL50 to steal the buffer.
	$D36B	No luck so load .X with the value we
		save into TO ($6F) and compare it to
		$07 (the maximum number of channels+l).
	$D36F	If .X < $07 we still need to check the
		alternative buffer for this channel so
		branch to STL10.
	$D371	If .X >= $07, we were checking the
		alternative channel so branch back to
		STL20 to check the next channel.
STL50	$D373	We've found an inactive buffer, now to
		steal it!
		Load .Y with the channel number from TO
		($6F) and store $FF into BUFO,Y ($A7,Y)
		to steal it.
STL60	$D37A	Pull the original value of TO off the
		stack and restore it. Transfer the
		buffer number from .X to .A (sets the
		N flag if not successful) and terminate
		routine with an RTS.
		-----------------------------------------
FNDLDX	$D37F	Find free LINDX and allocate in LINUSE
		Load .Y with $00 and .A with $01.
FND10	$D383	Test whether the same bit is set in
		LINUSE ($0256) and the accumulator. If a
		bit is set in LINUSE, the corresponding
		channel is free. If the test indicates a
		free channel, branch to FND30.
	$D388	Increment .Y (the counter) and do an ASL
		on the value in the accumulator to shift
		the test bit one place left. If more
		tests are needed, branch to FND10.
	$D38E	No channel found so load .A with $70 to
		to indicate a NO CHANNEL error and JMP
		to CMDERR ($C1C8).
FND30	$D391	FOR the bit mask (in .A) with $FF to
		flip the bits, AND the flipped mask with
		LINUSE to clear the appropriate bit, and
		store the result back in LINUSE ($0256).
	$D399	Transfer the channel number (LINDX) from
		.Y to .A and exit with an RTS.
		-----------------------------------------
		Get next byte from a channel:
GBYTE	$D39B	JSR to FNDRCH ($D0EB) to find an unused
		read channel.
	$D39E	JSR to SETLDS ($C100) to turn on the
		drive active light.
	$D3A1	JSR to GET ($D3AA) to get one byte from
		any type of file.
	$D3A4	Load .X with the current channel number
		from LINDX ($82) and load .A with the
		data byte from CHNDAT,X ($023E).
	$D3A9	Terminate routine with an RTS.
		-----------------------------------------
		Get next byte from any type of file:
GET	$D3AA	Load .X with the current channel number
		from LINDX ($82) JSR to TYPFIL ($D125)
		to determine the file type. If Z flag
		not set on return, this is not a
		relative file so branch to GET00.
	$D3B1	It is a relative file so JMP to RDREL
		($E120) to do this type.
GET00	$D3B4	Test if the current secondary address
	$D3BA	from SA ($83) is $0F (the CMD channel).
		If it is, branch to GETERC ($D414).
		Test if the last character we sent on
		this channel was an EOI by checking if
		the channel status in CHNRDY,X ($F2,X)
	$D3C0	is $08. If the last character was NOT
		an EOI, branch to GET1.
		Last character was EOI so JSR to TYPFIL
		($D125) to determine the file type.
	$D3C3	If the file type is NOT $07, a random
		access file, branch to GETO.
	$D3C7	This is a direct access file so we will
		leave it active. Store an $89 (random
		access file ready) as the channel status
		in CHNRDY,X ($F2,X) and exit with a
		JMP to RNDGET ($D3DE) to get the next
		character ready.
GETO	$D3CE	Last character sent was EOI so set the
		channel status as NOT READY by storing
		a $00 in CHNRDY,X ($F2,X).
	$D3D2	Terminate routine with an RTS.
GET1	$D3D3	Test if this is a LOAD by testing if
		the secondary address in SA ($83) is a
		$00. If it is a LOAD, branch to GET6.
GET2	$D3D7	It's not a LOAD. Maybe it's a random
		access file. JSR to TYPFIL ($D125) to
		determine the file type. If the file
		type is less than $04, it is NOT a
		random access file, so branch to SEQGET.
RNDGET	$D3DE	It is a random access file so JSR to
		GETPRE ($D12F) to set up the right
		pointers in .X and .Y.
	$D3E1	Load the pointer to the data byte into
		.A from BUFTAB,X ($99,X). Compare this
		value to the pointer to the last
		character pointer in LSTCHR,Y ($0244,Y)
		to see if we are up to the last one yet.
		If not, branch to RNGET1.
	$D3E8	We're at the last character so wrap the
		pointer around to the start again by
		storing $00 in BUFTAB,X ($99,X).
RNGET1	$D3EC	Increment BUFTAB,X ($99,X) to point to
		the next character.
RNGET2	$D3EE	Load .A with the data byte from
		BUFTAB,X ($99,X).
RNGET4	$D3F0	Save the data byte in CHNDAT,Y ($023E,Y)
	$D3F3	Load the pointer from EUFTAB,X and
		compare it to the value in LSTCHR,Y
		($0244,Y) to see if this is the last
		character we're supposed to get. If NOT,
		branch to RNGET3.
RNGET3	$D3FA	Since this is the last character, set
SEQGET	$D3FF	the channel status in CHNRDY,Y to $00
	$D400	to indicate an EOI (end of information).
		Terminate routine with an RTS.
		JSR to RDBYT ($D156) to read the next
GET3	$D403	data byte.
		Load .X with the channel number from
	$D408	LINDX ($82) and store the data byte in
		CHNDAT,X ($00F2,X).
		Terminate routine with an RTS.
GET6	$D409	Seems to be a LOAD. Test if it is a
	$D40E	directory listing by seeing if DIRLST
		($0254) is a $00. If it is, this is not
		a directory listing so branch to SEQGET.
		It is a directory listing so JSR to
		GETDIR ($ED67) to get a byte from the
		directory and then JMP to GET3.
		-----------------------------------------
		Get byte from the error channel:
GETERC	$D414	JSR to CETPNT ($D4E8) to read the active
		buffer pointer. If the buffer number is
		NOT $D4, lo byte of the pointer to one
		byte below error buffer, branch to GE10.
	$D41B	Check if DIRBUF+1 ($95) equals $02, the
		hi byte of the pointer to the error
		buffer. If not, branch to GE10.
	$D421	Store a $0D (#13; RETURN) in DATA ($85)
		and JSR to ERROFF ($C123) to turn off
		the error LED.
	$D428	Load .A with $00 and JSR to ERRTSO
		($E6C1) to transfer the error message
		to the error buffer.
	$D42D	Decrement CB+2 ($A5) so this pointer
		points to the start of the message,
		load .A with $80 (EOI out status), and
		branch (always!) to GE30.
GE1O	$D433	JSR to GETBYT ($D137) to read a byte
		of the error message. Store the byte in
		DATA ($85) and, if not $00, branch to
		GE20.
GE15	$D43A	Load .A with $D4, the lo byte cf the
		pointer to one byte below the error
		buffer and JSR to SETPNT ($D4C8) to set
		the pointers to the error buffer.
	$D43F	Store the hi byte of the pointer to the
		error buffer ($02) into BUFTAB+1,X
		($9A,X).
GE20	$D443	Load .A with $88, the channel status
		byte for ready-to-talk.
GE30	$D445	Store the value in .A as the error
		channel status in CHNRDY+ERRCHN ($F7).
	$D447	Load .A with the byte from DATA ($85)
		and store it as the channel data byte
		for the error channel in CHNDAT+ERRCHN
		($0243).
	$D44C	Terminate routine with an RTS.
		-----------------------------------------
		Read in the next block of a file by
		following the track and sector link.
		Set an EOF (end of file) indicator if
		the track link (first byte) is $00.
NXTBUF	$D44D	JSR to GETACT ($DF93) to get the active
		buffer number (in .A). Multiply the
		buffer number by 2 (ASL) and transfer it
		to .X.
	$D452	Store a $00 in BUFTAB,X ($99,X) to set
		the buffer pointer to the first byte.
	$D456	Check first byte (track link) in the
		buffer, (BUFTAB,X). If it is zero, there
		are no more blocks to get so branch to
		NXTB1.
	$D45A	Decrement the buffer pointer, BUFTAB,X
		($99,X) by 1 so it is $FF and JSR to
		RDBYT ($D156). This forces a read of the
		next sector because we set the pointer
		to the end of the current buffer.
NXTB1	$D45F	Terminate routine with an RTS.
		-----------------------------------------
		Direct block read:
DRTRD	$D460	Load .A with $80, the job code for read
		and branch tc DRT.
		-----------------------------------------
		Direct block write:
DRTWRT	$D464	Load .A with $90, the job code for write
DRT	$D466	OR the job code in .A with the current
		drive number in DRVNUM
		($7F) and store
		the result in CMD ($024D).
	$D46B	Load .A with the number of the buffer
		to use for the job from JOBNUM ($F9) and
		JSR to SETH ($D6D3) to set up the header
		image for the job.
	$D470	Load .X with the number of the buffer
		to use for the job from JOBNUM ($F9) and
		JMP to DOIT2 ($D593) to do the job.
		-----------------------------------------
		Open internal read channel: (SA=17) files.
		Use this entry point for PRG
OPNIRD	$D475	Load .A with $01 (program file type)
		-----------------------------------------
		Open internal read channel (.A=any type)
		Use this entry point for any file type.
OPNTYP	$D477	Store file type (.A) into TYPE ($024A)
	$D47A	Store $11 (#17) as the current secondary
		address in SA ($83).
	$D47E	JSR to OPNRCH ($DC46) to open a read
		channel.
	$D481	Set .A to $02 and JMP to SETPNT ($D4C8)
		to set the buffer pointer to point past
		the track and sector link.
		-----------------------------------------
OPNIWR	$D486	Open internal write channel (SA=18)
		Store $12 (#18) as the current secondary
	$D48A	address in SA ($83).
		JMP to OPNWCH ($DCDA) to open the write
		channel.
		-----------------------------------------
		Allocate the next directory block:
NXDRBK	$D48D	JSR to CURBLK ($DE3B) set the TRACK($80)
		and SECTOR ($81) values from the current
		header.
	$D490	Set TEMP ($6F) to $01 and save the
		current value of SECINC ($69), the
		sector increment used for sequential
		files, on the stack.
	$D497	Set the sector increment, SECINC ($69)
		to $03, the increment used for the
		directory track.
	$D49B	JSF to NXTDS ($F12D) to determine the
		next available track and sector.
	$D49E	Restore the original sector increment
    		in SECINC ($69) from the stack.
	$D4A1	Set .A to $00 and JSR to SETPNT ($D4C8)
		to set the pointer to the first byte in
		the active buffer (track byte).
	$D4A6	Load .A with the next track from TRACK
		($80) and JSR to PUTBYT ($CFF1) to
		store the track link in the buffer.
	$D4AB	Load .A with the next sector from SECTOR
		($81) and JSR to PUTBYT ($CFF1) to
		store the sector link in the buffer.
	$D4B0	JSR to WRTBUF ($D0C7) to write the
		buffer out to disk.
	$D4B3	JSR to WATJOB ($D599) to wait until the
    		write job is complete.
	$D4B6	Set .A to $00 and JSR to SETPNT ($D4C8)
		to set the pointer to the first byte in
		the active buffer (track byte).
NXDB1	$D4BB	Loop to zero the entire buffer.
	$D4C0	JSR to PUTBYT ($CFF1) to store $00 as
		the next track link.
	$D4C3	Load .A with $FF and JMP to PUTBYT
		($CFFl) to store $FF as the sector link.
		-----------------------------------------
		Set up pointer into active data buffer
		On entry: .A contains new pointer value
SETPNT	$D4C8	Save the new pointer (in .A) into TEMP
		($6F) and JSR to GETACT ($DF93) to find
		the active buffer number (in .A).
	$D4CD	Multiply the buffer number by 2 (ASL)
		and transfer the result into .X. pointer
	$D4CF	Move the high byte of the buffer
		from BUFTAB+1,X ($9A,X) to DIRBUF+1($95)
	$D4D3	Load the new buffer pointer value from
		TEMP ($6F) into .A. Store this new value
		into BUFTAB,X ($99,X) and DIRBUF ($94).
	$D4D9	Terminate routine with an RTS.
		-----------------------------------------
		Free both internal channels: (SA=17&18)
FREICH	$D4DA	Set SA ($83) to $11 (#17) the internal
		read channel and JSR to FRECHN ($D227)
		to free the internal read channel.
	$D4E1	Set SA ($83) to $12 (#18) the internal
		write channel and JMP to FRECHN ($D227)
		to free the internal write channel.
		-----------------------------------------
GETPNT	$D4E8	Get the active buffer pointer:
		JSR to GETACT ($DF93) to get the active
		buffer number (in .A).
SETDIR	$D4EB	Multiply the buffer number by two (ASL)
		and transfer the result into .X.
	$D4ED	Move the hi byte of the buffer pointer
		from BUFTAB+1,X ($9A,X) into the hi
		byte of the directory buffer pointer
		DIRBUF+1 ($95).
	$D4F1	Move the lo byte of the buffer pointer
		from BUFTAB,X ($99,X) into the lo byte
		of the directory buffer pointer DIRBUF
		($94). (.A = lo byte of the pointer)
	$D4F5	Terminate routine with an RTS.
		-----------------------------------------
		Direct read of a byte: (.A = position)
		On entry:.A = position of byte in buffer
		On exit:.A = data byte desired
DRDBYT	$D4F6	Store lo byte of pointer to desired byte
		(in .A) into TEMP+2 ($71).
	$D4F8	JSR to GETACT ($DF93) to get the active
		buffer number (in .A).
	$D4FB	Transfer buffer number into .X and load
		.A with the hi byte of the active buffer
		pointer from BUFIND,X ($FEE0,X). Store
		this value into TEMP+3 ($72).  This
		creates a pointer to the byte in $71/72.
	$D501	Zero .Y and load .A with the desired
		byte from (TEMP+2),Y; ($71),Y.
	$D505	Terminate routine with an RTS.
		-----------------------------------------
		Set up job using last job's drive:
		NOTE: For this entry, job code is in CMD
		and .X is buffer number (job #)
SETLJB	$D506	Load .A with previous job number from
		LSTJOB,X ($025B,X), AND the job number
		with $01 to leave just the drive number
		bits, and OR the result with the new
		job code on CMD ($024D). The resulting
		new job code is in .A.
		Set up new job:
		NOTE: For this entry, job code is in .A
		and .X is buffer number (job #)
SETJOB	$D50E	Save new job code on the stack and store
		the number of the buffer to use (.X) in
	$D511	
		JOBNUM ($F9).
		Transfer the buffer number from .X to
		.A, multiply it by 2 (ASL) and transfer
		it back into .X.
	$D514	Move the desired sector from HDRS+I,X
		($07,X) into CMD ($024D).
	$D519	Load .A with the desired track from
		HDRS,X ($06,X). If it is $00, branch to
		TSERR ($D54A).
	$D51D	Compare the desired track (in .A) with
		the maximum track number from MAXTRK
		($FED7). If it is too large, branch to
		TSERR ($D54A).
	$D522	Transfer the desired track number from
		.A to .X.
	$D523	Pull the job code off the stack and
		immediately push it back onto the stack.
	$D525	AND the job code in .A with $F0 to mask
		off the drive bits and compare it to $90
		(the job code for a write). If this is
		not a write job, branch to SJB1.
	$D52B	Pull the job code off the stack and
		immediately push it back onto the stack.
	$D52D	Do an LSR on the job code in .A to
		find the drive to use. If_ it is drive 1,
		branch to SJB2.
	$D530	Use drive 0 so load DOS version from
		DSKVER ($0101) and branch to SJB3.
SJB2	$D535	Use drive 1 so load DOS version from
		DSKVER+1 ($0102).
SJB3	$D538	If DOS version is $00 (no number), it is
		OK, so branch to SJB4.
		-----------------------------------------
		NOTE: On the 1541 the DOS version code
		(normally 65) is stored in ROM,
		not in RAM as on the 4040. This
		means you can not soft set a
		DOS version number on the 1541!
		However, a DOS version number of
		$00 is OK.
		-----------------------------------------
	$D53A	Compare the DOS version number with the
		1541 DOS version number ($65) from
		VERNUM ($FED5). If the version numbers
		do not match, branch to VNERR ($D572).
SJB4	$D53F	Transfer the desired track number from
		.X to .A and JSR to MAXSEX ($F24B) to
		calculate the maximum sector number+l
		for this track (returned in .A). Compare
		this value with the desired sector
		number in CMD. If the desired sector
		number is legal, branch to SJB].
TSERR	$D54A	Track and/or sector number is illegal so
		JSR to HED2TS ($D552) to store the
		values in TRACK ($80) and SECTOR ($81).
TSER1	$D54D	Load .A with $66 to indicate a bad track
		and sector and JMP to CMDER2 ($E645).
		-----------------------------------------
		Set desired track and sector values:
HED2TS	$D552	Load .A with the number of the buffer to
    		use for this job from JOBNUM ($F9).
		Multiply the buffer number by 2 (ASL)
		and transfer it to .X.
	$F556	Move the desired track number from
		HDRS,X ($06,X) to TRACK ($80).
	$F55A	Move the desired sector number from
		HDRS+I,X ($07,X) to SECTOR ($81).
	$F55E	Terminate routine with an RTS.
		-----------------------------------------
		Check for bad track and sector values:
TSCHK	$D55F	Load .A from TRACK ($80). If the track
		is $00, branch back to TSER1 ($D54D).
	$D563	Compare the track to the maximum track
		number allowed, MAXTRK ($FED7). If too
		large, branch back to TSER1.
	$D568	JSR to MAXSEC ($F24B) to calculate the
		maximum sector number allowed on this
		track. If too large, branch to TSER1.
	$D571	Terminate routine with an RTS.
		-----------------------------------------
		Bad DOS version number:
VNERR	$D572	JSR to HED2TS ($D552) to store the
		values in TRACK ($80) and SECTOR ($81).
	$D575	Load .A with $73 to indicate a bad DOS
		version number and JMP to CMDER2 ($E645)
		-----------------------------------------
		Conclude job set up:
SJB1	$D57A	Load .X with the number of the buffer to
		use for the job from JOBNUM ($F9).
	$D57C	Pull the job code off the stack.
	$D57D	Store the job code as the current
		command in CMD ($024D), in the job queue
		at JOBS,X ($00,X) to activate the disk
		controller, and in LSTJOB,X.
	$D585	Terminate routine with an RTS.
		-----------------------------------------
		Do a read job; return when done OK:
DOREAD	$D586	Load .A with $80, the read job code and
		branch to DOJOB.
		-----------------------------------------
		Do a write job; return when done OK:
DOWRIT	$D58A	Load .A with $90, the write job ccde.
DOJOB	$D58C	OR the job code with the current drive
		number in DF'VNUM ($7F).
	$D58E	Load .X with the number of the buffer to
		use for the job from JOBNUM ($F9).
DOIT	$D590	Store complete job code in CMD ($024D).
DOIT2	$D593	Load .A with job code from CMD ($024D).
	$D596	JSR to SETJOB ($D50E) to start job.
		-----------------------------------------
		Wait until job is completed:
WATJOB	$D599	JSR to TSTJOB ($D5A6) to check if job
		is done yet (error code returned in .A).
	$D59C	If job not done yet, branch to WATJOB.
	$D59E	Save error ccde on the stack.
	$D59F	Set job completed flag, JOBRTN ($0298),
		to $00.
	$D5A4	Recover error code from stack (in .A).
	$D5A5	Terminate routine with an RTS.
		-----------------------------------------
		Test if job done yet:
		If not done, return. If done OK, then
		return. If not OK, redo the job.
TSTJOB	$D5A6	Load .A with value from the job queue,
		JOBS,X ($00,X).
	$D5A8	If .A > 127, job not done yet so branch
		to NOTYET to exit with carry flag set.
	$D5AA	If .A < 2, job was completed with no
		errors so branch to OK to exit with the
		carry flag clear.
	$D5AE	Compare the error code to $08. If it is
		$08, a fatal write protect error has
		occured so branch to TJ10 and abort.
	$D5B2	Compare the error code to $0B. If it is
		$0B, a fatal ID mismatch error has
		occured so branch to TJ10 and abort.
	$D5B6	Compare the error code to $0F. If it is
		NOT $0F, a non-fatal error has occured
		so branch to RECOV and try again.
		NOTE: an error code of $0F means a fatal
		drive-not-available error has occured.
TJ10	$D5BA	Test bit 7 of the job return flag,
		JOBRTN ($0298). If it is set, the disk
		has been initialized and this is the
		first attempt to carry out the job, so
		branch to OK to return with the carry
		flag clear.
	$D5BF	JMP to QUIT2 ($D63F) to try to recover.
OK	$D5C2	Clear the carry flag and terminate the
NOTYET	$D5C4	routine with an RTS.
		Set the carry flag and terminate the
RECOV	$D5C6	routine with an RTS.
		Save .Y value and the current drive
	$D5CB	number from DRVNUM ($7F) on the stack.
		Load the job code for the last job from
		LSTJOB,X ($025B,X), AND it with $01 to
		mask off the non-drive bits, and store
		the result as the current drive number
		in DRVNUM ($7F).
	$D5D2	Transfer the drive number from .A to .Y
	$D5D9	and move the LED error mask from
		LEDMSK,Y ($FECA,Y) to ERLED ($026D)
		JSR to DOREC ($D6A6) to do last job
	$D5E0	recovery. On return, if the error code
		(in .A) is $01, it worked so branch to
		REC01.
		Retry didn't work, JMP to REC95 ($D66D)
REC01	$D5E3	Load .A with the original job code from
	$D5E9	LSTJOB,X ($025B,X), AND it with $F0 to
		mask off the drive number bits, and save
		it on the stack.
		Check if the job code was $90 (a write
	$D5ED	job). If not, branch to REC0.
REC0	$D5F4	This is a write job. OR the current.
		drive number from DRVNUM ($7F) with $B8
		(the job code for a sector seek) and
		store the result in LSTJOB,X ($025B,X).
		This replaces the original write job
		with a seek job during recovery.
		See if the head is on track by checking
REC1	$D600	bit 6 of REVCNT (6A). If this bit is
		set, the head is on track so branch to
		REC5.
		Head nct on track so zero the offset
		table pointer, EPTR ($0299) and the
		total offset TOFF ($029A).
		Load .Y with the offset table pointer
	$D606	EPTR ($0299) and .A with the total
		offset TOFF ($029A).
		Set the carry flag and subtract the
	$D60D	offset OFFSET,Y ($FEDB) from the total
		offset in .A. Store the result as the
		new total offset in TOFF ($029A).
		Load .A with the head offset from
	$D613	OFFSET,Y and JSR to HEDOFF ($D676) to
		move the head so it is on track.
		Increment the cffset table pointer and
		JSR to DOREC ($D6A6) to attempt to
		recover. On return, if the error code
		in .A < $02, the recovery worked so
		branch to REC3.
	$D61D	That try at recovery did not work so
		increment the offset table pointer by 1
		and load .A with the offset from
REC3	$D625	OFFSET,Y ($FEDB,Y). If the value loaded
		is not $00, branch to REC1 to try again.
		One more try on the offset. Load .A with
REC5	$D631	the total offset from TOFF ($029A) and
		JSR to HEDOFF ($D676). If no error on
		return, branch to REC9.
		Check bit 7 of the error recover count
QUIT	$D635	REVCNT ($6A). If this bit is clear,
		branch to REC7 to do a bump to track 1.
		job code off the
		Pull the original
	$D63A	stack. If it is NOT $90 (a write job)
		branch to QUIT2. the job code in
		For write jobs only, OR
QUIT2	$D63F	.A with the drive number from DRVNUM
		and put the result in LSTJOB,X ($025B,X)
		to restore the original value. JOBS,X
		Load .A with the error code from
		($00,X) and abort with a JSR to ERROR
		($E60A).
REC7	$D644	Pull the job code off the stack (in .A).
REC5	$D645	Check bit 7 of the job return flag
	$D64B	JOBRTN ($0298). If this bit is set,
		branch to REC95 to exit with job error.
		Push the job code back onto the stack.
		Do a bump to track 1 by loading .A with
REC8	$D651	$C0 (BUMP job code), ORing it with the
		current drive number from DRVNUM ($7F),
		and storing the result in the job queue
		at JOBS,X ($00,X).
		Wait for current job to be completed.
	$D655	JSR to DOREC ($D6A6) to try one more
REC9	$D65C	time. On return, code (.A)
		if the error
		is not $01 (no error), give up in
		disgust and branch to QUIT.
		Pull the original job code off the stack
	$D661	and compare it to $90 (the job code for
		a write job). If this isn't a write job,
		branch to REC95.
		OR the job code (in .A) with the drive
	$D666	number from DRVNUM ($7F) and store the
		value in LSTJOB,X.
		JSR to DOREC ($D6A6) to try one last
REC95	$D66D	time. On return, if the error code (.A)
		is not $01 (no error.), give up in
		disgust and branch to QUIT2.
		Pull the original drive number off the
	$D670	stack and store it in DRVNUM ($7F). stack
		Pull the original .Y value off the
		and restore .Y.
	$D672	Load .A with the error code from JOBS,X
		($00,X), clear the carry flag, and exit
		with an RTS.
		-----------------------------------------
HEDOFF	$D676	Adjust head offset:
		On entry: .A = OFFSET
		If .A=O, no offset required so branch
HOFI	$D67A	to HOF3.
	$D67C	If .A > 127, head needs to be stepped
		inward so branch to HOF2.
		We want to move head outward 1 track so:
HOF2	$D681	load .Y with $01 and JSR to MOVHED
	$D686	($D693) to move the head.
	$D688	On return, set the carry flag and
		subtract $01 from the value in .A. If
		the result is not $00, the head has not
		finished so branch back to HOF1.
		If the head is finished moving, branch
		to HOF3.
		We want to move head inward 1 track so:
HOF3	$D68D	load .Y with $FF and JSR to MOVHED
	$D692	($D693) to move the head.
		On return, clear the carry flag and
		add $01 to the value in .A. If the
		result is not $00, the head has not
		finished so branch back to HOF2.
		Terminate routine with an RTS.
		-----------------------------------------
MCVHED	$D693	Step head inward or outward 1 track:
		Save the value in .A onto the stack.
MH10	$D694	Transfer the number of steps to move
	$D695	(phase) from .Y into .A.
	$D697	Load .Y with the current drive number
	$D69A	from DRVNUM ($7F).
		Store the phase into PHASE,Y ($02FE,Y).
		Compare the phase in .A with the value
	$D69F	in PHASE,Y ($02FE,Y). If they are equal,
	$D6A4	the controller hrs not yet moved the
	$D6A5	head so branch back to MH10.
		Store $00 in PHASE,Y ($02FE,Y) so head
		won't move any more.
		Pull original value of .A off the stack.
		Terminate routine with an RTS.
		-----------------------------------------
DOREC	$D6A6	Load .A with the retry counter, REVCNT
DOREC1	$D6AB	($6A), AND it with $3F to mask off the
		high order bits, and transfer the result
		into .Y.
		Load .A with the error LED mask from
		ERLED ($026D), FOR it with the disk
		controller port B, DSKCNT ($1000) and
		store it back in DSKCNT ($1C00) to turn
		the drive light OFF.
DOREC2	$D6B4	Restart the last job by moving the job
	$D6B9	code from LSTJOB,X ($025B,X) to the job
		queue at JOBS,X ($00,X).
		Loop to wait until the value in the job
		queue at JOBS,X ($00,X) is less than 127
DOREC3	$D6C1	(indicates job has been completed).
	$D6C4	Test to see if the error code returned
	$D6C5	is $01 (successful). If everything was
		OK, branch to DOREC3.
		It didn't work. Decrement the error
		counter in .Y and, if .Y has not counted
		down to $00 yet, branch to DOREC1 and
		keep trying.
		Save the error code onto the stack.
		Load .A with the error LED mask from
		ERLED ($026D), OR it with the disk
		controller port B, DSKCNT ($1C00) and
		store it back in DSKCNT ($1C00) to turn
	$D6CE	the drive light back ON.
	$D6CF	Pull the error code back off the stack.
		Terminate routine with an RTS.
		-----------------------------------------
SETHDR	$D6D0	Set up the header for the active buffer:
	$D6D3	Uses values in TRACK, SECTOR, & DSKID.
		JSR to GETACT ($DF93) to get the number
		of the active buffer (returned in .A).
		Multiply the number of the active buffer
	$D6D5	(in .A) by 2 (ASL) and transfer the
		result into .Y.
		Move the track number from TRACK ($80)
	$D6DA	to HDRS,Y ($0006,Y).
	$D6DF	Move the sector number from SECTOR ($81)
	$D6E3	to HDRS+l,Y ($0007,Y).
		Load .A with the current drive number
		from DRVNUM ($7F), multiply it by 2(ASL)
		and transfer the result to .X.
		NOTE: this last bunch of code really
		does nothing. On the 4040 it is
		done in preparation for moving
		the ID characters. However, this
		is not done here on the 1541!
		Terminate routine with an RTS.
		-----------------------------------------
ADDFIL	$D6E4	Add new filename to the directory:
		Save the following variables onto the
	$D6F0	stack: SA ($83), LINDX ($82), SECTOR
	$D6F4	($81), and TRACK ($80).
		Set the current secondary address, SA
		($83) to $11 (#17), the internal read
		channel.
		JSR to CURBLK ($DE3B) to find a read
		channel and set TRACK ($80) and SECTOR
		($81) from the most recently read header
	$D6F7	Save the file type, TYPE ($024A) of the
	$D6FB	file to be added onto the stack.
		Load .A with the drive number for the
		new file, and it with $01, and store the
		result as the current drive, DRVNUM($7F)
	$D701	Load .X with the last job number frcm
		JOBNUM ($F9).
	$D706	FOR the drive number in .A with the
		last job code from LSTJOB,X ($025B,X),
		divide the result by 2 (LSR), and check
		if the carry flag is
		the new file uses the
		last job so there is
		the drive and we can
		clear. If it is,
		same drive as the
		no need to change
		branch to AF08.
	$D709	Store $01 in DELIND ($0292) to indicate
		that we are searching for a deleted
		entry and JSR to SRCHST ($C5AC). On
		return, if .A=O, all directory sectors
		are full so branch to AF15 to start a
		new sector. If .A<>0, we have found a
		spot to put the new entry so branch to
		AF20.
AF08	$D715	Since we have used this drive before,
		some of the directory information is
		in memory. Check if DELSEC ($0291) a
		is $00. If it is, we didn't locate in
		deleted entry the last time we read
		the directory so branch to AF10.
	$D71A	Since DELSEC is not $00, it is the
		number of the sector containing the
		first available directory entry. See if
		this sector is currently in memory by
		comparing this sector number with the
		one in SECTOR ($81). If they are equal,
		the sector is in memory so branch to
		AF20.
	$D71E	Since the desired sector is not in
		memory, set SECTOR ($81) to the desired
		sector number and JSR to DRTRD ($D460)
		to read in the sector. Now branch to
		AF20.
AF10	$D726	Store $01 in DELIND ($0292) to indicate
		that we are looking for a deleted entry
		and JSR to SEARCH ($C617) to find the
		first deleted or empty directory entry.
	$D72E	On return, if .A is not equal to $00, a
		deleted or empty entry was found so
		branch to AF20.
AF15	$D730	No empty entries so we have to start a
		new sector so JSR to NXDRBK ($D48D) to
		find us the next available sector.
	$D733	Move the new sector number from SECTOR
		($81) to DELSEC ($0291) and set DELIND
		($0292) to $02.
AF20	$D73D	Load .A with the pointer that points to
		first character in the directory entry,
		DELIND($0292), and JSR to SETPNT($D4C8)
		to set the pointers to this entry.
	$D743	Pull the file type off the stack and
		store it back in TYPE ($024A).
	$D747	Compare the file type to $04 (REL type).
		If this is not a relative file, branch
		to AF25.
	$D74B	Since it is a REL file, OR the file type
		(in .A) with $80 to set. bit 7.
AF25	$D74D	JSR to PUTBYT ($CFFl) to store the file
		type (in .A) into the buffer.
	$D750	Pull the file's track link off the
		stack, store it in FILTRK ($0280), and
		JSR to PUTBYT ($CFF1) to store the
		track link in the buffer.
	$D757	Pull the file's sector link off the
		stack, store it in FILSEC ($0285), and
		JSR to PUTBYT ($CFFI) to store the
		sector link in the buffer.
	$D75E	JSR to GETACT ($DF93) to get the active
		buffer number (in .A) and transfer the
		value to .Y
	$D762	Load .X with the file table pointer
		from FILTAB ($027A).
	$D766	Load .A with $10 (#16) and JSR to TRNAME
		($C66E) to transfer the file name to
		the buffer.
	$D76D	Loop to fill directory entry with $00's
		from (DIRBUF),16 to (DIRBUF),27.
	$D776	Check the value in TYPE ($024A) to see
		if this is a relative file. If not,
		branch to AF50.
	$D77D	For REL files only: Load .Y with $10.
	$D77F	Move the side-sector track number from
		TRKSS ($0259) to (DIRBUF),Y. Increment Y
	$D785	Move the side-sector sector number from
		SECSS ($025A) to (DIRBUF),Y. Increment Y
	$D78B	Move the record length from REC ($0258)
		to (DIRBUF),Y.
AF50	$D790	JSR to DRTWRT ($D464) to write out the
		directory sector.
	$D793	Pull the original value of LINDX off the
		stack, store it. back in LINDX ($82), and
		transfer the value into .X.
	$D797	Pull the original value of SA off the
		stack, store it back in SA ($83).
	$D79A	Load .A with the number of the directory
		sector containing the new entry from
		DELSEC ($0291) and store it in ENTSEC
		($D8) and in DSEC,X ($0260,X).
	$D7A5	Load .A with the pointer to the start
		of the new entry from DELIND ($0292)
		and store it in DIND,X ($0266,X).
	$D7AA	Load .A with the file type of the new
		entry from TYPE ($024A) and store it in
		PATTYP ($E7).
	$D7AF	Load .A with the current drive number
		from DRVNUM ($7F) and store it in
		FILDRV ($E2).
	$D7B3	Terminate routine with an RTS.
		-----------------------------------------
		Open a channel from serial bus:
		The open, load, or save command is
		parsed. A channel is allocated and the
		directory is searched for the filename
		specified in the command.
OPEN	$D7B4	Move the current. secondary address from
		SA ($83) to TEMPSA ($024C).
	$D7B9	JSR to CMDSET ($C2B3) to set the command
		string pointers. On return, store the .X
		value in CMDNUM ($022A).
	$D7BC	Load .X with the first character in the
		command string CMDBUF ($0200). Load .A
		with the secondary address from TEMPSA
		($024C). If the secondary address is not
		$00, this is not a load so branch to
		OP021.
	$D7C7	Compare the value in .X with $2A ("*")
		to check if the command is "load the
		last referenced program". If not $2A,
		branch to OP021.
	$D7CB	Appears to be "load last". Check by
		loading .A with the last. program's track
		link from PRGTRK ($7E). If .A=O, there
		is no last program so branch to OP0415
		to initialize drive O.
OP02	$D7CF	Seems OK, let's load last program.
		Store the program's track link (in .A)
		into TRACK ($80).
	$D7D1	Move the program's drive number from
		PRGDRV ($026E) to DRVNUM ($7F).
	$D7D8	Store $82 (program) as the file ty p e in
		PATTYP ($E7).
	$D7DC	Move the program's sector link from
		PRGSEC ($026F) into SECTOR ($81).
	$D7E1	JSR to SETLDS ($C100) to turn on the
		drive active LED.
	$D7E4	JSR to OPNRCH ($DC46) to open a read
		channel.
	$D7E7	Load .A with $04 (2 * program type), OR
		it with the drive number in DRVNUM ($7F)
ENDRD	$D7EB	Load .X with the number of the active
		buffer from LINDX ($82).
	$D7ED	Store the value in .A as the file type
		in FILTYP,Y ($00EC,Y).
	$D7F0	Terminate routine with a JMP to ENDCMD
		($C194).
OP021	$D7F3	Compare the byte in .X (the first in the
		command string) with $24 ("$") to check
		if we are to load the directory. If it
		is NOT "$", branch to OP041.
	$D7F7	We want the directory. But, should we
		load it or just open it as a SEQ file?
		Check the secondary address in TEMPSA
		(024C). If it is not $00, branch to
		OP04 to open it as a SEQ file.
	$D7FC	JMP to LOADIR ($DA55) to load the
		directory.
OP04	$D7FF	Open the directory as a SEQ file.
		JSR to SIMPRS ($C1D1) to parse the
		command string.
	$D802	Move the directory's track link from
		DIRTRK ($FE85) into TRACK ($80).
	$D807	Zero the desired sector, SECTOR ($81)
	$D80B	JSR to OPNRCH ($DC46) to open the read
		channel.
	$D80E	Load .A with the current drive number
		from DRVNUM ($7F) and OR it with $02
		(2 * the SEQ file type).
	$D812	Terminate routine with a JMP to ENDRD
		($D7EB).
OP041	$D815	Compare the byte in .X (the first in the
		command string) with $23 ("#") to check
		if this is to be a direct access channel
		If it is NOT "#", branch to OP042.
	$D819	Continue routine with a JMP to OPNBLK
		($CB84).
OP0415	$D81C	Set the file type flag TYPFLG ($0296)
		to $02 (prcgram file).
	$D821	Zero the current drive number DRVNUM
		($7F) and the last job drive number
		LSTDRV ($028E).
	$D828	JSR to INITDR ($D042) to initialize
		drive #0.
OP042	$D82B	JSR to PRSCLN ($C1E5) to parse the
		command string to find the colon.
	$D82E	If none found, branch to OP049
	$D830	Zero .X and branch to OP20 (always).
OP049	$D834	Transfer the byte in .X to .A. If the
OP05	$D837	byte is $00, branch to OP10.
		Oops, trouble! Load .A with $30 to
OP10	$D83C	indicate a BAD SYNTAX error and JMP to
		CMDERR ($C1C8).
		Decrement .Y so it points to the ":"
	$D83F	If .Y=O, first character is a ":" so
		branch to OP20.
		Decrement .Y so it points to the byte
OP20	$D840	just before the ":".
		Store the pointer to the file name
	$D843	(in .Y) into FILTBL ($027A).
		Load .A with $8D (shifted return) and
	$D848	JSR to PARSE ($C268) to parse the rest
		of the command string.
		Increment .X (file count) and store the
	$D84C	result into F2CNT ($0278).
		JSR to ONEDRV ($C312) to set up one
	$D84F	drive and the necessary pointers.
		JSR to OPTSCH ($C3CA) to determine the
		optimal search pattern.
	$D852	JSR to FFST ($C49D) to search the disk
	$D857	directory for the file entry.
		Zero the record length, REC ($0258),
	$D861	MODE ($0297) (read mode), and the file
		type, TYPE ($024A) (deleted file).
		Test the value of F1CNT ($0277). If it
	$D866	is $00, there are NO wild cards in the
		filename so branch to OP40.
		JSR to CKTM ($DA09) to set the file
	$D869	type and mode.
		Test the value of F1CNT ($0277). If it
	$D86F	is $01, there is only one wild card in
		the filename so branch to OP40.
		Compare .Y to $04. If .Y=$04, this is a
	$D873	relative file so branch to OP60 to set
		the record size.
		JSR to CKTM ($DA09) to set the file
OP40	$D876	type and mode.
		Restore the original secondary address
	$D87B	into SA ($83) using the value from
		TEMPSA ($024C).
		Test the secondary address, if it is
	$D87F	greater or equal to $02, this is not a
		load or save so branch to OP45.
		This is a load or save. Set MODE ($0297)
	$D882	(0=read; 1=write) using the secondary
		address (0=load; 1=save).
		Set the write BAM flag, WBAM ($02F9) to
		$40 to flag that BAM is dirty.
	$D887	Load .A with the file type, TYPE ($024A)
	$D88C	If it is not $00 (deleted file type),
		branch to OP50. NOTE: load & save of
		files have TYPE set to $00 in $D857.
		Set file type, TYPE ($024A) to $02
OP45	$D891	(program file type).
		Load .A with the file type, TYPE ($024A)
	$D896	If it is not $00 (scratched file type),
		branch to OP50.
		Load the file type as given in the
	$D89D	directory from PATTYP ($E7), AND it with
		$07 (file type mask), and store the
		result as the file type in TYPE ($024A)
		Test the file's first track link in
	$D8A2	FILTRK ($0280). If it is not $00, the
		file exists so branch to OP50.
		The file doesn't exist, set TYPE ($024A)
OP50	$D8A7	to $01 (the default value; a SEQ file).
		Check MODE ($0297). If it is $01, it is
OP60	$D8AE	write mode so branch to OP75 to write.
	$D8B1	JMP to OP90 ($D940) to open to read or
		load.
		Handle relative file:
	$D8B4	Load .Y with the pointer from FILTBL,X.
		Load .A with the file's record size as
OP75	$D8BA	given in the directory from CMDBUF,Y and
	$D8BF	store it in REC ($0258).
	$D8C6	Test if the file's track link in FILTRK
		($0280) is $00. If it is NOT $00, the
		file is present so branch to OP40 to
		read it.
		Set the MODE ($0297) to $01 (write mode)
		and branch to OP40 (always).
		Load .A with the file's type as given in
OP77	$D8CD	the directory from PATTYP ($E7), AND it
		with $80 to determine if it is a deleted
		file, and transfer the result to .X. If
		it is not a deleted file, branch to OP81
		Open to write. Load .A with $20 and test
		if any bits in .A and the file type in
		PATTYP ($E7) match. If not, branch to
		OP80.
	$D8D3	JSR to DELDIR ($C8B6) to delete the
	$D8D6	directory entry and write out the
		revised sector.
		JMP to OPWRIT ($D9E3) to open the
OP80	$D8D9	channel to write.
		Load .A with the entry's track link from
		FILTRK ($0280). If it is not $00, there
		is an existing file so branch to OP81.
0P81	$D8DE	File not found but that's OK. JMP to
	$D8E1	OPWRIT ($D9E3) to open a write channel.
		Load .A with CMDBUF ($0200), the first
	$D8E8	byte of the command string. If it equals
		$40 ("@"), branch to 0P82. NOTE: THIS IS
		WHERE REPLACE FILE COMMAND IS DETECTED!
		Transfer .X value into .A. If it is not
0P815	$D8EB	$00, branch to 0P815.
	$D8F0	Load .A with $63 to indicate a FILE
		EXISTS ERROR and JMP to CMDERR ($C1C8).
		Load .A with $33 to indicate a bad
0P82	$D8F5	filename and JMP to CMDERR ($C1C8).
		REPLACE FILE ROUTINE * MAY HAVE BUG!
		Load the file type of the directory
	$D8FE	entry from PATTYP ($E7), AND it with
	$D902	the file type mask $07, and compare the
		result with the command string file type
		in TYPE ($024A). If the file types do
		not match, branch to 0P115 to abort.
		Compare the file type (in .A) with $04.
		If it is $04, this is a relative file
		so branch to 0P115 to abort.
		JSR to OPNWCH ($DCDA) to open the write
	$D905	channel.
		Move the active buffer number from
	$D90A	LINDX ($82) to WLINDX ($0270).
		Set the secondary address, SA ($83) to
		$D90E $11 (#17) the internal read channel.
		JSR to FNDRCH ($D0EB) to find an unused
	$D911	read channel.
		Load .A with the current value of the
	$D917	pointer into the directory buffer, INDEX
		($0294) and JSR to SETPNT ($D4C8) to set
		the buffer pointers to point to the
		INDEXth byte. NOTE: at this point INDEX
		points to the first byte in the entry,
		the file type.
		Zero .Y. Then load .A with the file type
	$D91F	from (DIRBUF),Y; ($94),Y, OR the file
		type with $20 (set the replace bit), and
		store the result back in (DIRBUF),Y.
		Load .Y with $1A (#26) and move the new
	$D925	track link from TRACK($80) to (DIRBUF),Y
		Increment .Y and move the new sector
	$D92A	link from SECTOR ($81) to (DIRBUF),Y.
		Load .X with the active buffer number
	$D92D	from WLINDX ($0270).
		Load .A with the sector of the directory
		entry ENTSEC ($D8) and copy it into
		DSEC,X ($0260,X).
	$D932	Load .A with the pointer to the start. of
		the directory entry ENTIND ($DD) and
		copy it into DIND,X ($0266,X).
	$D937	JSR to CURBLK ($DE3B) to set TRACK ($80)
		and SECTOR ($81) from header of most
		recently read header.
	$D93A	JSR to DRTWRT ($D464) to do direct block
		write of directory block to disk.
	$D93D	JMP to OPFIN ($D9EF) to finish opening
		the file.
OP90	$D940	Test the directory entry's track link
		in FILTRK ($0280). If it is NOT $00,
		the file exists so branch to OP100.
OP95	$D945	Load .A with $62 to indicate a FILE NOT
		FOUND error and JMP to CMDERR ($C1C8).
OP100	$D94A	Compare the value in MODE ($0297) to
		$03 (open to modify). If MODE=$03 branch
		to OP110.
	$D951	Check bit 5 of the directory entry's
		file type. If this bit is set, it flags
		a file that is already opened (or not
		closed properly). If the bit is NOT SET,
		branch to OP110 and carry on.
	$D957	Load .A with $60 to indicate a FILE OPEN
		error and JMP to CMDERR ($C1C8).
OP110	$D95C	Load .A with the directory entry's file
		type from PATTYP ($E7), AND it. with $07
		to mask off higher order bits, and
		compare it with the file type specified
		in the command string from TYPE ($024A).
		If the file types match, branch to OP120
OPUS	$D965	Load .A with $64 to indicate a FILE TYPE
		MISMATCH error and JMP to CMDERR ($C1C8)
OP120	$D96A	Load .Y with $00 and use it to zero
		F2PTR ($0279).
	$D96F	Load .X with the mode from MODE ($0297)
		If MODE is not $02 (open to append),
		branch to OP125.
	$D976	Compare the file type (in .A) with $04.
		If it is $04, this is a relative file
		so branch to OP115.
	$D97A	This applies only to opening to append.
		Load .A with the file type from
		(DIRBUF),Y ;($94),Y, AND it with $4F,
		and store it back in (DIRBUF),Y.
	$D980	Save the secondary address from SA ($83)
		onto the stack and set SA ($83) to $11
		(#17, the internal read channel).
	$D987	JSR to CURBLK ($DE3B) to set TRACK ($80)
		and SECTOR ($81) from header of most
		recently read header.
	$D98A	JSR to DRTWRT ($D464) to do direct block
		write of directory block to disk.
	$D98D	Pull original secondary address off the
		stack and restore it in SA ($83).
OP125	$D990	JSR to OPREAD ($D9A0) to open the file
		for a read.
	$D993	Check if MODE ($0297) is $02 (append).
		If it is n ' t $02, branch to OPFIN ($D9EF)
	$D99A	JSR to APPEND ($DA2A) to read to the end
		of the file.
	$D99D	JMP to ENDCMD ($C194) to terminate.
		-----------------------------------------
		Open a file to read:
OPREAD	$D9A0	Copy the relative file values from the
	$D9AE	directory entry (DIRBUF),Y; ($94),Y into
		their RAM variable locations:
		Track for side sector to TRKSS ($0259)
		Sector for side sector to SECSS ($025A)
		Load .A with the record size from the
		directory entry. Load .X with the size
		from the command string, REC ($0258).
	$D9B3	Store the value in .A into REC ($0258).
	$D9B6	Transfer the value from .X into .A. If
		the command string size is $00, branch
		to OP130 (defaults to entry size).
	$D9B9	Compare the two record lengths. If they
	$D9BE	are equal, branch to OP130.
		Record lengths do not match, load .A
		with $50 to indicate a READ PAST END OF
		FILE error and JSR to CMDERR ($C1C8).
		-----------------------------------------
OP130	$D9C3	Load .X with the pointer F2PTR ($0279).
	$D9C6	Copy the track link from FILTRK,X
	$D9CB	($0280,X) to TRACK ($80).
	$D9D0	Copy the sector link from FILSEC,X
		($0285,X) to SECTOR ($81).
		JSR to OPNRCH ($DC46) to open a read
		channel.
	$D9D3	Load .Y with the active buffer number
		from LINDX ($82).
	$D9D5	Load .X with the pointer F2PTR ($0279).
	$D9D8	Copy the directory sector containing the
		entry from ENTSEC,X ($D8,X) to DSEC,Y
	$D9DF	($0260,Y).
		Copy the pointer to the entry in the
		directory sector from ENTIND,X ($DD,X)
		to DIND,Y ($0266,Y).
	$D9E2	Terminate the routine with an RTS.
		-----------------------------------------
OPWRIT	$D9E3	Open a file to write:
		Load .A with the drive number for the
		file from FILDRV ($E2), AND it with $01
		to mask off non-drive bits, and store
		the result as the current drive in
	$D9E9	DRVNUM ($7F). write
		JSR to OPNWCH ($DCDA) to
		open a
OPFIN	$D9EC	channel. entry
	$D9EF	JSR to ADDFIL ($D6E4) to
		add the
		is
		to the directory. greater than $01,
		If the secondary address
	$D9F5	it is a not a program file so
		branch to OPF1.
		JSR to GETHDR ($DE3E) to set up TRACK
	$D9F8	and SECTOR values from the last header
		read. TRACK ($80) to
		Ccpy the track link from
	$D9FC	PRGTRK ($7E). DRVNUM ($7F) to
	$DA01	Copy the file drive from
		PRGDRV ($026E).
		Copy the sector link from SECTOR ($81)
OPF1	$DA06	to PRGSEC ($026F). JMP to ENDSAV
		Terminate routine with a
		($C199).
		-----------------------------------------
CKTM	$DA09	Check mode or file type:
		Load .Y with the pointer from FILTBL,X.
CKM1	$DA11	Load .A with the mode or file type from
		the command string, CMDBUF,Y.
		Load .Y with $04, the number of modes.
		Loop to compare mode requested with the
		table of modes, MODLST,Y ($FEB2,Y). If
		no match is found, branch to CKM2. If a
	$DA19	match is found, fall through.
		VALID MODES: 0 = R (READ)
		1 = W (WRITE)
		2 = A (APPEND)
		3 = M (MODIFY)
		($0297)
		Store .Y counter (0-3) in MODE
CKM2	$DA1C	Loop to compare type requested with the
		$DA26
		table of types, TPLST,Y ($FEB6,Y). If
		no match is found, branch to CKT2. If a
		match is found, fall through.
		VALID TYPES: 0 = D (DELETED)
		1 = S (SEQUENTIAL)
		2 = P (PROGRAM)
		3 = U (USER)
		4 = R (RELATIVE)
		Store .Y counter (0-3) in TYPE ($024A)
CKT2	$DA29	Terminate the routine with an RTS.
		--------------------------------------
APPEND	$DA2A	Append information to the end of a file
		Reads through old file to end.
		JSR to GCBYTE ($CA39) tc get a byte from
	$DA2D	the data channel.
	$DA34	Test if we are at the end of file. If
		not, loop back to APPEND.
		JSR to RDLNK ($DE95) to set TRACK ($80)
	$DA37	and SECTOR ($81) from the track and
		sector links in the last block. NOTE:
		TRACK will be $00 and SECTOR will be a
		pointer to the end of the file.
		Load .X with the end of file pointer
AP3O	$DA3D	from SECTOR ($81), increment it by 1,
	$DA42	and transfer the result to .A. If the
		new value of the pointer is not $00,
		there is space left at the end of this
		sector so branch to AP30.
		No space left in this sector so JSR to
		WRTO ($D1A3) to get the next sector.
		Load .A with $02 so it points to the
		start of the data area for this new
		sector.
		JSR to SETPNT ($D4C8) to set the active
	$DA45	buffer pointers.
		Load .X with the active buffer number
	$DA4B	from LINDX ($82) and store $01 (channel
	$DA54	ready at the end of file) in the channel
		status flag CHNRDY,X ($F2,X).
		Load .X with the sec. address SA ($83).
		Load .A with $80, OR it with the active
		buffer number in LINDX ($82), and store
		the result in LINTAB,X ($022B,X) to
		indicate that this is now a write file.
		Terminate the routine with an RTS.
		-----------------------------------------
LOADIR	$DA55	Load the directory ($):
		Store $0C (load) as the command code in
	$DA5A	CMDNUM ($022A).
	$DA5C	Load .A with $00 (load only drive #0)
	$DA62	Load .X with the command length from
		CMDSIZ ($0274) and decrement the length
		in .X by 1. If the result is $00, branch
		to LD02 to load complete directory for
		drive O.
		Decrement the length in .X by 1. If the
		LD01
	$DA65	result is still not $00, this must be a
		selective load by name so branch to LD03
		Load .A with the second character in the
	$DA6D	command string from CMDBUF+1 ($0201) and
		JSR to TSTOV1 ($C3BD) to test if the
		character is an ASCII " 0 " or "1 " . If
		not, branch to LD03 to load by name.
		Store the drive number desired (in .A)
		LD02
		into FILDRV ($E2).
LD03	$DA6F	Increment F1CNT ($0277), F2CNT ($0278), I
	$DA78	and FILTBL ($027A).
	$DA7C	Store $80 in PATTYP ($E7) to represent
	$DA84	the file type.
		I $DA86 Store $2A ("*") as the first two bytes
		in the command string CMDBUF ($0200) and
		CMDBUF+1 ($0201)
		Branch always to LD10.
		I JSR to PRSCLN ($C2DC) to find the colon
LD05	$DA8E	in the command string. If no colon is
	$DA8E	found, branch to LD05.
	$DA90	Colon found so JSR to CMDRST ($C2DC) to
		zero all command string variables.
		Lcad .Y with $03.
		Decrement .Y twice and store the result
LD10	$DA95	in FILTBL ($027A).
		JSR to TC35 ($C200) to parse and set
	$DA98	up the tables.
	$DA9B	JSF to FSISET ($C398) to set pointers to!
		file name and check type.
		JSR to ALLDRS ($C320) to set up all
		drives required.
	$DA9E	JSR to OPTSCH ($C3CA) to determine the
LD20	$DAA1	best drive search pattern.
	$DAA4	JSR to NEWDIR ($C7B7) to read in BAM and
	$DAA7	set up disk name, ID, etc as first line
	$DAAA	in directory.
	$DAAD	I JSR to FFST ($C49D) to find file start
	$DAAF	I entry.
	$DAB2	I JSR to STDIR ($EC9E) to start the
	$DAB7	I directory loading function.
	$DABB	JSR to GETBYT ($D137) to read first byte
	$DABF	from the buffer.
		Load .X with the active buffer number
		from LINDX ($82).
		Store the first byte (in .A) into
		CHNDAT,X ($023E,X).
		Load .A with the current drive number
		from DRVNUM ($7F) and use this value to
		set the last job drive LSTDRV ($028E).
		OR the drive number in .A with $04 and
		store the result as the file type in
		FILTYP,X ($EC,X).
		Zero BUFTAB+CBPTR ($A3). Note: CBPTR is
		the command buffer pointer ($0A).
		Terminate the routine with an RTS.
		-----------------------------------------
CLOSE	$DAC0	Close the file related to the specified
	$DAC5	secondary address:
		Zero the write BAM flag, WBAM ($02F9).
		If secondary address, SA ($83) is not
		zero (directory load), branch to CLS10
CLS05	$DAC9	Close directory: Zero the directory
CLS10	$DAD1	listing flag DIRLST ($0254) and JSR to
CLS15	$DAD4	FRECHN ($D227) to free the channel.
	$DAD8	JMP to FREICH ($D4DA) to free the
	$DADB	internal channel and terminate routine.
	$DAE1	If secondary address (in .A) is $0F(#15)
	$DAE6	branch to CLSALL to close all files.
	$DAE9	JSR to CLSCHN ($DB02) to close channel.
		If secondary address in SA ($83) is $01
		(save), branch to CLS05 to close the
		internal channel and exit..
		Check the error status in ERWORD ($026C)
		If status is not $00, the last command
		produced an error so branch to CLS15.
		JMP to ENDCMD ($C194) to end command.
		Error so JMP to SCREN1 ($C1AD)
		-----------------------------------------
CLSALL	$DAEC	Close all files: (when CMD closed)
CLS20	$DAF0	Set secondary address, SA ($83) to $0E.
CLS25	$DAF3	JSR to CLSCHN ($DB02) to close channel.
	$DAF7	Decrement SA ($83). If more secondary
	$DAFC	addresses to do (SA>=0) loop to CLS20.
	$DAFF	Check the error status in ERWORD ($026C)
		If status is nct $00, the last command
		produced an error so branch to CLS25.
		JMP to ENDCMD ($C194) to end command.
		Error so JMP to SCREN1 ($C1AD)
		-----------------------------------------
CLSCHN	$DB02	Close file with specified sec. address
		Load .X with the secondary address from
		SA ($83).
	$DB04	Load .A with the channel status from
	$DB09	LINTAB,X ($022B,X). If the status is
		not $FF (closed), branch to CLSC28.
		Channel already closed so terminate
CLSC28	$DB0C	routine with an RTS.
	$DB10	AND the channel status (in .A) with $0F
		to leave only the buffer number and
		store the result in LINDX ($82).
		JSR to TYPFIL ($D125) to determine the
	$DB13	file type (returned in .A).
		If file type is $07 (direct channel)
	$DB17	branch to CLSC30.
		If file type is $04 (relative file)
	$DB1B	branch to CLSREL.
	$DB20	JSR to FNDWCH ($D107) to find an unused
		write channel. If none found, branch to
		CLSC31
		JSR to CLSWRT ($DB62) to close off
	$DB23	sequential write.
		JSR to CLSDIR ($DBA5) to close directory
CLSC30	$DB26	JSR to MAPOUT ($EEF4) to write out BAM.
CLSC31	$DB29	JMP to FRECHN ($D227) to free channel
		and terminate the command.
CLSREL	$DB2C	-----------------------------------------
		$DB2F
		Sub to close relative file: out BAM
		JSR to SCRUB ($DDFl) to write
		if it is dirty (RAM version modified).
		JSR to DBLBUF ($CF1E) to set up double
		buffering and read ahead.
	$DB32	JSR to SSEND ($E1CB) to position side
		sector & buffer table pointer to the end
		of the last record.
	$DB35	Load .X with the side sector number from
	$DB3B	SSNUM ($D5), store this byte in T4($73),
		and increment T4 by 1.
		Zero T1 ($70) and T2 ($71).
	$DB41	Load .A with the pointer to the side
		sector value in the directory buffer
		from SSIND ($D6), set the carry flag,
		subtract $0E (the side sector offset-2),
		and store the result in T3 ($72).
	$DB48	JSR to SSCALC ($DF51) to calculate the
		$DB4B
		$DB4D
		number of side sector blocks needed.
		Load .X with the active buffer number
		from LINDX ($82). of side
		Move the lo byte of the number
		sector blocks from T1 ($70) to NBKL,X
		($B5,X) and the hi byte from T2 ($71) to
		NBKH,X ($BB,X).
CLSR1	$DB55	Load .A with $40 (the dirty flag for a
	$DB5C	relative record flag) and JSR to TSTFLG
	$DB5F	($DDA6) to test if relative record must
		be written out. If not, branch to CLSR1.
		JSR to CLSDIR ($DBA5) to close the
		directory file.
		JMP to FRECHN ($D227) to clear the
		channel and terminate routine.
		-----------------------------------------
		Close a sequential file write channel:
CLSWRT	$DB62	Lcad .X with the active buffer number
		from LINDX ($82).
	$DB64	Load .A with the number of bytes written
		in this sector from NBKL,X ($B5,X) and
		OR .A with the number of data blocks
		written from NBKL,X ($B5,X).
		If the result is not $00, at least one
		block of the file has been written so
		branch
	$DB6A	to CLSW10.
		No blocks have been written so JSR to
		GETPNT ($D4E8) to get the pointer into
		the data buffer (returned in .A). If
		this value is greater than two, at least
		one byte has been written so branch to
		CLSW10.
	$DB71	No bytes have been written so load .A
		with $0D (carriage return) and JSR to
		PUTBYT ($CFF1) to write it out to the
		data buffer.
CLSW10	$DB76	JSR to GETPNT ($D4E8) to get the pointer
	$DB7D	into the data buffer (returned in .A).
		If the pointer value is not $02, the
		buffer is not empty so branch to CLSW20.
		Since we have an empty buffer, JSR to
		DBLBUF ($CF1E) to switch buffers.
	$DB80	Load .X with the active buffer number
		from LINDX ($82).
	$DB82	Load .A with the number of bytes written
		in this sector from NBKL,X ($B5,X). If
		this value is not equal to $00, branch
		to CLSW15.
	$DB86	Decrement the number of data blocks
		written in NBKH,X ($BB,X) by 1.
CLSW15	$DB88	Decrement the number of bytes written
		$DB8A
		in this sector, NBKL,X ($B5,X) by 1.
		Load .A with $00.
CLSW20	$DB8C	Set the carry flag, subtract $01 from
		the number of bytes written in this
		sector (.A), and save the result on the
		stack.
	$DB90	Load .A with $00 and JSR to SETPNT
		($D4C8) to set the buffer pointers to
		the first byte in the data buffer (the
		track link).
	$DB95	JSR to PUTBYT ($CFFl) to write $00 out
		as the track link.
	$DB98	Pull the bytes written from the stack.
	$DB99	JSR to PUTBYT ($CFFl) to write out the
		$DB9C
		$DB9F
		$DBA2
		bytes in this sector as the sector link.
		JSR to WRTBUF ($D0C7) to write the data
		buffer out to disk.
		to wait for the
		JSR to WATJOB ($D599)
		write job to be completed. that
		JMP to DBLBUF ($CF1E) to make sure
		both buffers are OK.
		-----------------------------------------
		Close directory after writing file:
CLSDIR	$DBA5	Load .X with the active buffer number
	$DBAA	from LINDX ($82). Save this value into
		$DBAD
		$DBB2
		WLINDX ($0270).
		Save the current secondary address from
		SA ($83) onto the stack.
		Copy the sector of the directory entry
		for the file from DSEC,X ($0260,X) into
		SECTOR ($81).
		Copy the pointer to the directory entry
		for the file from DIND,X ($0266,X) into
		INDEX ($0294).
	$DBB8	Load .A with the file type from FILTYP,X
	$DBEE	($EC,X), AND it with $01 to mask off the
	$DBC3	non-drive bits, and store the result as
	$DBC6	the current drive number in DRVNUM ($7F)
	$DBC9	Ccpy the directory track number (#18)
	$DBCC	from DIRTRK ($FE85) into TRACK ($80).
	$DBCE	JSR to GETACT ($DF93) to get the active
	$DBD3	buffer number (returned in .A).
	$DBD8	Save the active buffer number onto the
		stack and into JOBNUM ($F9).
		JSR to DIRTRD ($D460) to read in the
		directory sector containing the entry.
		Load .Y with $00.
		Load .A with the hi byte of the pointer
		to the active buffer from BUFIND,X
		($FEE:O,X) and store it in R0+1 ($87).
		Complete the pointer into the directory
		buffer by copying the lo byte of the
		pointer from INDEX ($0294) to RO ($86).
		Load .A with the file type from the
		directory entry (RO),Y, AND it with $20,
		and checking if the result is $00. If
		it is $00, this is NOT a replace so
		branch to CLSD5.
		-----------------------------------------
		NOTE: Here is where we do the directory
		entry when a file is replaced.
		- * - * - Possible bugs! - * - * -
		-----------------------------------------
	$DBDE	JSR to TYPFIL ($D125) to determine the
	$DBE1	file type (returned in .A).
	$DBF5	If file type is $04 (a relative file)
	$DBEB	branch to CLSD6.
	$DBEC	Load .A with the file type from R0,Y,
	$DBF0	AND it with $8F to mask off the replace
	$DBF2	bit, and store the result back in R0,Y.
	$DBF4	Increment .Y. The pointer at (RO),Y now
	$DBF7	points to the old track link.
	$DBF8	Copy the old track link from (RO),Y to
	$DBFC	into TRACK ($80).
		Store the .Y value into TEMP+2 ($71).
		Load .Y with $1B (#27). The pointer at
		(RO),Y now points to the replacement
		sector link.
		Load .A with the replacement sector link
		from (RO),Y and save it on the stack.
		Decrement .Y. The pointer at (RO),Y now
		points to the replacement track link.
		Load .A with the replacement track link.
		If this link is NOT $00, branch to CLSD4
		Trouble! Replacement track link should
		never be $00. So put replacement track
		link in TRACK ($80).
	$DBFE	Pull replacement sector link off the
	$DC01	stack and put it in SECTOR ($81).
		Load .A with $67 to indicate a SYSTEM
		TRACK OR SECTOR error and JMP to CMDER2
		($E645).
		-----------------------------------------
CLSD4	$DC06	Push the replacement track link onto
	$DC07	the stack.
		Load .A with $00. Zero the replacement
	$DC0B	track link in the entry (RO),Y.
	$DC0C	Increment .Y.
	$DC0E	Zero replacement sector link in (RO),Y.
	$DC0F	Pull the replacement track link off
	$DC11	the stack.
		Load .Y with the original pointer value
		from TEMP+2 ($71). Note: pointer at
		(RO),Y now points to the second byte of
		the entry, the track link.
		Store the replacement track link as the
	$DC13	final track link in (RO),Y.
		Increment .Y. Note: the pointer at
	$DC14	(RO),Y now points to the third byte of
		the entry, the sector link.
		Move the old sector link from (RO),Y to
	$DC18	SECTOR ($81).
		Pull the replacement sector link off the
CLSD5	$DC1B	stack and store it as the final sector
	$DC1E	link in (RO),Y.
	$DC21	JSR to DEIFIL ($C87D) to delete the old
		file from the BAM by following the track
		and sector links.
		JSR to CLSD6 ($DC29) to finish closing.
		Load .A with the file type from (RO),Y,
CLSD6	$DC29	AND it with $0F to mask off any high
		order bits, OR it with $80 to set the
		closed bit, and store the result back
		in (RO),Y.
		Load .X with the active buffer number
	$DC2C	that was saved into WLINDX ($0270).
	$DC2E	Load .Y with $1B (#27). The pointer at
	$DC32	(RO),Y now points to the low byte of the
		number of blocks in the file.
		Copy the lo byte of the number of blocks
		from NBKL,X ($B5,X) to (R0),Y.
		Increment .Y.
	$DC33	Copy the hi byte of the number of blocks
	$DC37	from NBKH,X ($BFs,X) to (R0),Y.
		Pull the original buffer number off the
	$DC39	stack and transfer it into .X.
		Load .A with $90 (write job code) and OR
	$DC3D	it with the drive number in DRVNUM($7F).
		JSR to COIT ($D590) to write out the
		revised directory sector.
	$DC40	Pull the original secondary address off
		the stack and transfer it into SA ($83).
	$DC43	JMP to FNDWCH ($D107) to exit.
		-----------------------------------------
		Open read channel with two buffers:
		Sets secondary address in LINTAB and
		initializes all pointers, including the
		ones for a relative file.
OPNRCH	$DC46	Load .A with $01 and JSR to GETRCH
		$DC4B
		$DC4E
		($D1E2) to set up one read channel
		JSF to INITP ($DCB6) to clear pointers.
		Load .A with the file type and save this
		value on the stack.
	$DC52	Multiply the file type in .A by 2 (ASL),
		OR it with the current drive in DRVNUM
		($7F) and store it in FILTYP,X to set
		the file type.
	$DC57	JSR to STRRD ($D09B) to read the first
	$DC5A	one or two blocks in the
	$DC5C	Load
		file.
		buffer number
		.X with the active
		from LINDX ($82). track number
		Load .A with the current track number
		from
		TRACK ($80). If the
		is not $00 (not the last block in the
		file), branch to CR10.
	$DC60	Load .A with the current sector number
		from SECTOR ($81). Since TRACK=$00, this
		is the pointer to the last character in
		the file. Store this value in LSTCHR,X
		($0244,X).
OR10	$DC65	Pull the original file type cff the
	$DC6A	stack. If this is not a relative file,
		branch to OR30.
		Load .Y with the secondary address from
		SA ($83). Load the channel type from
		LINTAB,Y ($022B,Y), OR it with $40 to
		mark it as a READ/WRITE file, and store
		the channel type back in LINTAB,Y.
	$DC74	Copy the record size from REC ($0258)
		into RS,X ($C7,X).
	$DC79	JSR to GETBUF ($D28E) to set up a buffer
	$DC7E	for the side sectors. If a buffer is
		available, branch to OR20.
		Since no buffer is available for the
		side sectors, abort with a JMP to GBERR
		($D20F).
OR20	$DC81	Load .X with the active buffer number
		(side
		sector buffer) from LINDX ($82).
	$DC83	Store the side sector buffer number in
		SS,X ($CD,X).
	$DC85	Copy the side sector track link from
		TRKSS
		($0259) into TRACK ($80).
	$DC8A	Copy the side sector sector link from
	$DC8F	side
	$DC92	side
		SECSS ($025A) into SECTOR ($81).
		JSR to SETH ($D6D3) to set up the
		sector header image. the
		JSR to RDSS ($DE73) to
		read in
	$DC95	sector block.
		JSR to WATJOB ($D599) to wait for the
OROW	$DC98	job to be completed.
		Load .X with the active buffer number
OR30	$DC9A	(side sector buffer) from LINDX ($82).
	$DCA0	Set the next record pointer in the side
	$DCA3	sector buffer NR,X ($C1,X) to $02.
	$DCA6	Load .A with $00 and JSR to SETPNT
		$DCA9
		($D4C8) to set the buffer  p ointers to
		the start of the side sector buffer.
		JSR to RD4O ($E153) to set up the first
		record. the
		JMP to GETHDR ($DE3E) to restore
		track and sector pointers and exit.
		JSR to RDBYT ($D156) to read a byte.
	$DCAC	Load .X with the active buffer number
	$DCAE	(side sector buffer) from LINDX ($82).
	$DCB1	Store the data byte (in .A) into
		$DCB5
		CHNDAT,X ($023E,X).
		Store $88 (ready to talk) as the channel
		status in CHNRDY,X ($F2,X).
		Terminate routine with an RTS.
		-----------------------------------------
INITP	$DCB6	Initialize variables for open channel:
		Load .X with the active buffer number
	$DCB8	from LINDX ($82).
	$DCBC	Load buffer number from BUFO,X ($A7,X),
		$DCC1
		$DCC7
		$DCC9
		$DCCE
		$DCD4
		multiply it by two (ASL), and transfer
		the result into
		.Y.
		Store $02 into the buffer pointer
		BUFTAB,Y ($0099,Y) so it points to the
		first data byte in the buffer.
		Load .A with the alternative-buffer
		number from BUF1,X ($AE,X), OR it with
		$80 to set the buffer-inactive bit, and
		store the result back in BUF1,X.
		Multiply the buffer number (in .A) by
		two (ASL) and transfer the result to .Y.
		Store $02 into the buffer pointer
		BUFTAB,Y ($0099,Y) so it points to the
		first data byte in the buffer.
		Zero the lo and hi bytes of the number
		of blocks written, NBKL,X ($B5,X) and
		NBKH,X ($BB,X).
		Zero the last data byte LSTCHR,X
		($0244),X.
	$DCD9	Terminate routine with an RTS.
		-----------------------------------------
OPNWCH	$DCDA	Open write channel with two buffers:
OW10	$DCDD	JSR to INTTS ($F1A9) to get the first
	$DCE2	track and sector.
	$DCE5	Load .A with $01 and JSR to GETWCH
	$DCE8	($D1DF) to get one buffer for writing.
	$DCEA	JSR to SETHDR ($D6D0) to set up header
	$DCEE	image.
	$DCF3	JSR to INITP ($DCB6) to set. up pointers.
	$DCF8	Load .X with the active buffer number
	$DCFC	from LINDX ($82).
	$DCFD	Load .A with the file type from TYPE
		($024A) and save it onto the stack.
		Multiply the file type in .A by two
		(ASL), OR it with the drive number from
		DRVNUM ($7F), and store the result as
		the file type in FILTYP,X ($EC,X).
		Pull the original file type off the
		stack and if this is a relative file
		(type = $04), branch to OW10.
		Since this is not a relative file, set
		channel status, CHNRDY,X ($F2,X) to $01
		(active listener).
		Terminate routine with an RTS.
		Load .Y with the secondary address from
		SA ($83).
	$DCFF	Load .A with the buffer type from
	$DD09	LINTAB,Y ($022B,Y), AND it with $3F to
		mask off higher order bits, OR it with
		$40 to flag this as a READ/WRITE file,
		and store the result back in LINTAB,Y.
		Copy record size from REC ($0258) into
	$DD0E	RS,X ($C7,X).
	$DD13	JSR to GETBUF ($D28E) to get a new
		buffer for storing the side sectors.
		If a buffer is available, branch to OW20
		No buffer available so abort with a
OW20	$DD16	JMP to GBERR ($D20F).
		Load .X with the active buffer number
	$DD18	from LINDX ($82).
		Store the new side sector buffer number
	$DD1A	into SS,X ($CD,X).
	$DD1D	JSR to CLRBUF ($DEC1) to clear the
	$DD20	side sector buffer.
		JSR to NXTTS ($F11E) to find the next
		available track and sector.
		Copy the new track link from TRACK ($80)
	$DD25	to TRKSS ($0259).
		Copy the new sector link from SECTOR
	$DD2A	($81) to SECSS ($025A).
		Load .X with the active buffer number
		from LINDX ($82).
	$DD2C	Load .A with the side sector buffer
	$DD2E	number from SS,X ($CD,X).
	$DD31	JSR to SETH ($D6D3) to set up the header
		Load .A with $00 and JSR to SETSSP
	$DD36	($DEE9) to set the buffer pointers
		using the current SS pointer (in .A)
		Load .A with $00 and JSR to PUTSS
	$DD3B	($DD8D) to set a null side sector link.
	$DD40	Load .A with $11 (the side sector offset
		plus 1) and JSR to PUTSS ($DD8D) to set
		the last character.
		Load .A with $00 and JSR to PUTSS
	$DD45	($DD8D) to set this side sector number.
		Load .A with the record size from REC
	$DD4B	($0258) and JSR to PUTSS ($DD8D) to set
	$DD50	the record size.
		Load .A with the file track link from
		TRACK ($80) and JSR to PUTSS ($DD8D) to
		set the track link.
		Load .A with the file sector link from
	$DD55	SECTOR ($81) and JSR to PUTSS ($DD8D) to
		set the sector link.
		Load .A with the side sector offset
	$DD5A	($10) and JSR to PUTSS ($DD8D) to set
	$DD5D	the side sector offset.
	$DD62	JSR to GETHDR ($DE3E) to get the track
		and sector of the first side sector.
		Load .A with the SS track link from
		TRACK ($80) and JSR to PUTSS ($DD8D) to
		set the SS track link.
		Load .A with the SS sector link from
	$DD67	SECTOR ($81) and JSR to PUTSS ($DD8D) to
		set the SS sector link.
		JSR to WRTSS ($DE6C) to write out the
	$DD6A	side sector block.
	$DD6D	JSR to WATJOB ($D599) to wait for the
	$DD72	write job to be completed.
		Load .A with $02 and JSR to SETPNT
		($D4C8) to set the pointer into the
		data buffer to the start of the data.
		Load .X with the active buffer number
	$DD74	from LINDX ($82).
		Set the carry flag, load .A with $00,
	$DD7B	subtract the record size from RS,X
	$DD7E	($C7,X), and store the result in NR,X
	$DD81	($C1,X) to set NR for a null buffer.
		JSR to NULBUF ($E2E2) to set null
		records in the active buffer.
		JSR to NULLNK ($DE19) to set track link
		to $00 and sector link to last non-zero
		character.
		JSR to WRTOUT ($DE5E) to write out the
		null record block.
	$DD84	JSR to WATJOB ($D599) to wait for the
		write job to be completed.
	$DD87	JSR to MAPOUT ($EEF4) to write out the
	$DD8A	BAM.
		JMP to OROW ($DC98) finish opening the
		channel.
PUTSS	$DD8D	-----------------------------------------
	$DD8E	Put byte into the side sector:
		Push byte in .A cnto the stack.
		Load .X with the active buffer number
		from LINDX ($82).
	$DD90	Load .A with the side sector buffer
		number from SS,X ($CD,X).
	$DD92	JMP to PUTB1 ($CFFD).
		-----------------------------------------
		Set/Clear flag:
SCFLG	$DD95	If carry flag clear, branch to CLRFLG
		Set flag:
SETFLG	$DD97	Load .X with the active buffer number
		from LINDX ($82).
CLRFLG	$DD99	OR the byte in .A with the file type in
CLRF10	$DD9B	FILTYP,X ($EC,X).
	$DD9D	If result is not $00, branch to CLRF10.
	$DD9F	Clear flag:
	$DDA1	Load .X with the active buffer number
	$DDA3	from LINDX ($82).
	$DDA5	FOR the byte in .A with $FF to flip all
		the bits.
		AND the byte in .A with the file type in
		FILTYP,X ($EC,X).
		Store the result in .A, as the new file
		type in FILTYP,X ($EC,X).
		Terminate routine with an RTS.
TSTFLG	$DDA6	-----------------------------------------
	$DDA8	Test flag:
	$DDAA	Load .X with the active buffer number
		from LINDX ($82).
		AND the byte in .A with the file type
		in FILTYP,X ($EC,X).
		Terminate routine with an RTS.
TSTWRT	$DDAB	-----------------------------------------
	$DDAE	Test if this is a write job:
	$DDAF	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
		Transfer the buffer number to .X.
		Load .A with the last job code from
		LSTJOB,X ($025B), AND the job code with
		$F0 to mask off the drive bits, and
		compare the result with $90 (write job
		code). This sets the Z flag if this is
		a write job.
	$DDB6	Terminate routine with an RTS.
		-----------------------------------------
TSTCHN	$DDB7	Test for active files in LINDX tables:
TSTC20	$DDB9	C=0 if file active X=ENTFND; Y = LINDX
TSTC30	$DDBB	C=l if file inactive X=18
TSTRTS	$DDC2	Load .X with $00 (secondary address)
TSTC40	$DDC9	Save .X value into TEMP+2 ($71).
	$DDCA	Load .A with the buffer number for this
	$DDCC	secondary address from LINTAB,X (022B,X)
	$DDCF	If the buffer number is NOT $FF, branch
	$DDD6	to TSTC40 for further testing.
	$DDD9	Restore .X value from TEMP+2 ($71) and
	$DDE1	increment it by 1. If the resulting .X
	$DDE8	value is less than $10 (the maximum
	$DDEF	sec. address - 2), loop tack to TSTC20.
	$DDF0	Terminate routine with an RTS.
		Save .X value into TEMP+2 ($71).
		AND the buffer number in .A with $3F to
		mask off the higher order bits and
		transfer the result into .Y.
		Load .A with the file type for this
		secondary address from FILTYP,Y ($EC,Y),
		AND it with $01 to mask off the
		non-drive bits, and store the result in
		TEMP+l ($70).
		Load .X with the index entry found
		from ENTFND ($0253).
		Load .A with the drive number for this
		secondary address from FILDRV,X ($E2,X),
		AND it with $01 to mask off the
		non-drive bits, and compare the result
		with the drive number in TEMP+l ($70).
		If the drives do not match, branch to
		TSTC30.
		Drive numbers match, now check if the
		directory entries match by comparing
		the entry sector in DSEC,Y($026C,Y) with
		the one in ENTSEC,X ($D8,X). If they do
		not match, branch to TSTC30.
		Drive numbers are match, now check if
		the directory entries match by comparing
		the entry index in DIND,Y ($0266,Y) with
		the one in ENTIND,X ($DD,X). If they do
		not match, branch to TSTC30.
		Clear the carry flag to indicate that
		all tests passed and active file found.
		Terminate routine with an RTS.
		-----------------------------------------
SCRUB	$DDF1	Write out buffer if dirty:
		NOTE: a buffer is dirty if the copy in
		RAM has been modified so it does
		not match the copy on disk.
		JSR to GAFLGS ($DF9E) to get active
		buffer number and set in LBUSED.
SCR1	$DDF4	If V flag not set, buffer is not dirty
	$DDF6	so branch to SCR1.
	$DDF9	JSR to WRTOUT ($DE5E) to write out the
	$DDFC	buffer to disk.
		JSR to WATJOB ($D599) to wait for the
		job to be completed.
		Terminate routine with an RTS.
		-----------------------------------------
SETLNK	$DDFD	Put TRACK and SECTOR into header:
	$DE02	JSR to SET00 ($DE2B) to set up pointer
		to header.
		Move desired track from TRACK ($80) to
	$DE05	(DIRBUF),Y; ($94),Y. Increment .Y
		Move desired sector from SECTOR ($81) to
	$DE09	(DIRBUF),Y; ($94),Y.
		Terminate routine with a JMP to SDIRTY
		($E105) to flag the buffer as dirty.
		-----------------------------------------
GETLNK	$DE0C	Set TRACK & SECTOR from link in buffer:
	$DE0F	JSR to SET00 ($DE2B) to set up pointer
	$DE14	to header.
		Move track link from (DIRBUF),Y;($94),Y
		to TRACK ($80). Increment .Y.
		Move sector link from (DIRBUF),Y
	$DE18	($94),Y to SECTOR ($80).
		Terminate routine with an RTS.
		-----------------------------------------
NULLNK	$DE19	Set track link to $00 and sector link to
		the last non-zero character in buffer.
		JSR to SET00 ($DE2B) to set up pointer
	$DE1C	to header.
	$DE21	Store $00 as track link in (DIRBUF),Y
		($94),Y. Increment .Y.
		Load .X with the active buffer number
	$DE23	from LINDX ($82).
		Load .A with the pointer into the data
	$DE2A	buffer from NR,X ($C1,X), decrement it
		by 1, and store the result as the sector
		link in (DIRBUF),Y; ($94),Y.
		Terminate routine with an RTS.
		-----------------------------------------
SETOO	$DE2B	Set up pointer to active buffer:
		JSR to GETACT ($DF93) to get the active
	$DE2E	buffer number (returned in .A).
	$DE30	Multiply the buffer number (in .A) by
		two (ASL) and transfer the result to .X.
		Move the hi byte of the buffer pointer
	$DE34	from BUFTAB+1,X ($9A,X) to DIRBUF+l($95)
		Store $00 as the lo byte of the buffer
	$DE38	pointer in DIRBUF ($94).
		Zero .Y and exit routine with an RTS.
		-----------------------------------------
CURBLK	$DE3B	Set TRACK & SECTOR from header:
GETHDR	$DE3E	JSR to FNDRCH ($D0EB) to find an unused
	$DE41	read channel.
		JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
		Store the buffer number in JOBNUM ($F9)
	$DE43	Multiply the buffer number (in .A) by
	$DE45	two (ASL) and transfer the result to .Y.
		Move the track number from the header
	$DE4A	table, HDRS,X ($0006,Y) to TRACK ($80).
	$DE4F	Move the sector number from the header
		table, HDRS+I,X($0007,Y) to SECTOR($81).
		Terminate routine with an RTS.
		-----------------------------------------
WRTAB	$DE50	Do read and write jobs:
		Store $90(write job code) in CMD($024D)
RDAB	$DE57	and branch to SJ10 (always).
		Store $80(read job code) in CMD($024D)
WRTOUT	$DE5E	and branch to SJ10 (always).
RDIN	$DE65	Store $90(write job code) in CMD($024D)
		and branch to SJ20 (always).
		Store $80(read job code) in CMD($024D)
WRTSS	$DE6C	and branch to SJ20 (always).
RDSS	$DE73	Store $90(write job code) in CMD($024D)
		and branch to RDS5 (always).
		Load .A with $80(read job code)
RDS5	$DE75	Store job code (in .A) into CMD($024D).
	$DE78	Load .X with the active buffer number
SJ10	$DE7A	from LINDX ($82).
	$DE7F	Load .A with the side sector buffer
		number from SS,X ($CD,X) and tranfer it
		to .X. If the SS buffer number < 127,
		branch to SJ30.
		JSR to SETHDR ($D6D0) to set header from
	$DE82	TRACK and SECTOR.
		JSR to GETACT to get the active buffer
	$DE85	number (returned in .A).
		Transfer the buffer number to .X.
	$DE86	Copy the drive number from DRVNUM ($7F)
SJ20	$DE8B	to LSTJOB,X ($025B,X).
		JSR to CDIRTY ($E115) to clear the
	$DE8E	dirty buffer flag.
	$DE91	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
		Transfer the buffer number to .X.
	$DE92	Continue routine with JMP to SETLJB
		($D506) to set last used buffer.
		-----------------------------------------
RDLNK	$DE95	Set TRACK & SECTOR from link in buffer:
		Load .A with $00 and JSR to SETPNT
		($D4C8) to set the buffer pointer to the
		first byte in the buffer (track link).
	$DE9A	JSR to GETBYT ($D137) to read the track
	$DE9F	TRACK ($80).
	$DEA4	read the sector
		SECTOR ($81).
		link. Store the link in
		JSR to GETBYT ($D137) to
		link. Store the link in
		Terminate routine with an RTS.
		-----------------------------------------
		Move bytes from one buffer to another:
BOTOBO	$DEA5	On entry: .A = number of bytes to move
		.Y = from buffer #
		.X
		Save number
		= to buffer # (in .A)
		of bytes to move
B02	$DEA6	onto the stack.
		$DEAC
		$DEB1
		$DEB6
		$DEB9
		and TEMP+2 ($71).
		cf the from buffer
		Zero TEMP ($6F)
		Move the hi byte
		pointer from BUFIND,Y ($FEE0,Y) to
		TEMP+1 ($70).
		Move the hi byte of the to buffer
		pointer from BUFIND,X ($FEE0,X) to
		TEMP+3 ($72).
		Pull the number-of-bytes-to-move from
		the stack, transfer it into .Y, and
		decrement .Y by 1 (0th byte is #1).
		Loop using .Y as a count-down index to
	$DEC0	transfer bytes from (TEMP)Y to (TEMP+2)Y
		Terminate routine with an RTS.
		-----------------------------------------
CLRBUF	$DEC1	Clear buffer: (buffer # in .A)
		Transfer buffer number from .A to .Y.
CB10	$DEC2	Move the hi byte of the from buffer
	$DEC7	pointer from BUFIND,Y ($FEE0,Y) to
	$DECC	TEMP+1 ($70).
		Zero TEMP ($6F) and .Y
		Loop to fill buffer
		with $00's.
	$DED1	Terminate routine with an RTS.
		-----------------------------------------
SSSET	$DED2	Set side sector pointer to $00:
		Zero .A and JSR
		to SSDIR ($DEDC) to set
	$DED7	DIRBUF with current SS pointer. the side
		Load .Y with $02. Load .A with
		sector pointer from (DIRBUF),Y;
		($94),Y.
	$DEDB	Terminate routine with an RTS.
		-----------------------------------------
SSDIR	$DEDC	Use SS pointer to set DIRBUF:
		On entry: .A = lo byte
		Store lo byte (in .A) into DIRBUF ($94).
	$DEDE	Load .X with the active buffer number
		$DEE0
		$DEE2
		$DEE3
		$DEE8
		from LINDX ($82). buffer
		.X.
		Load .A with the side sector
		number from SS,X ($CD,X).
		Transfer SS buffer number to
		Copy hi byte
		of buffer pointer from
		BUFIND ($FEE0) to DIRBUF+1 ($95).
		Terminate routine with an RTS.
		-----------------------------------------
SETSSP	$DEE9	Use SS pointer to set DIRBUF & BUFTAB:
	$DEEA	On entry: .A = lo byte
	$DEED	Save lo byte (in .A) onto the stack.
	$DEEE	JSR to SSDIR ($DEDC) to set DIRBUF from
	$DEF1	current SS pointer.
	$DEF4	On return, .A contains the hi byte of
	$DEF7	the SS buffer pointer. Save the hi byte
		onto the stack.
		Transfer the SS buffer number from .X
		to .A, multiply it by two (ASL), and
		transfer it back into .X.
		Pull hi byte of SS buffer pointer off
		the stack and store it in BUFTAB+1,X
		($9A,X).
		Pull lo byte of SS buffer pointer off
		the stack and store it in BUFTAB,X
		($99,X).
		Terminate routine with an RTS.
		-----------------------------------------
SSPOS	$DEF8	Use SSNUM & SSIND to set SS & BUFTAB:
		On return V = 0 all OK
		V = 1 out of range
		JSR to SSTEST ($DF66) to test if SSNUM &
	$DEFB	SSIND are resident and within range.
	$DEFD	If N flag set, out of range so branch
	$DEFF	to SSP10.
	$DF01	If V flag clear, it is in residence so
		branch to SSP20.
		Since V flag set, maybe in range and
		maybe not. Do another test:
		Load .X with the active buffer number
		from LINDX ($82).
		Load .A with the side sector buffer
	$DF03	number from SS,X ($CD,X).
		JSR to IBRD ($DF1B) to read in the SS.
	$DF06	JSR to SSTEST ($DF66) to test again.
	$DF09	If N flag clear, it is in range so
SSP10	$DF0B	branch to SSP20.
		Out of range so JSR to SSEND ($E1CB) to
SSP20	$DF0E	set SS & BUFTAB to end of last record.
	$DF12	BIT with ER1 ($FECE) to set flags and
		terminate routine with an RTS.
		Load .A with the SS pointer from SSIND
		($D6).
	$DF14	JSR to SETSSP ($DEE9) to set DIRBUF and
	$DF17	BUFTAB.
		BIT with ERO ($FECD) to set flags and
		terminate routine with an RTS.
		-----------------------------------------
IBRD	$DF1B	Indirect block read/write:
		On entry: .A = buffer number for R/W
		.X = active buffer (LINDX)
		(DIRBUF),Y points to T&S to be R/W
		Store buffer number (.A) in JOBNUM ($F9)
IBWT	$DF1D	Load .A with $80 (read job code) and ($F9)
IBOP	$DF21	branch to IBOP.
		$DF23
		$DF25
		$DF26
		$DF2C
		$DF32
		$DF37
		$DF3B
		$DF40
		Store buffer number (.A) in JOBNUM
		Load .A with $90 (write job code)
		Push the job code onto the stack.
		Load .A with the file's drive number
		from FILTYP,X ($EC,X), AND it with $01
		to mask off the non-drive bits, and use
		it to set the drive, DRVNUM ($7F)
		Pull the job code off the stack, OR it
		with the drive number in DRVNUM ($7F),
		and store the result in CMD ($024D).
		Move the track number from (DIRBUF),Y
		($94),Y to TRACK ($80). Increment .Y
		Move the sector number from (DIRBUF),Y
		($94),Y to SECTOR ($81).
		Load .A with the buffer number from
		JOBNUM ($F9) and JSR to SETH ($D6D3) to
		set up the header.
		Load .X with the buffer number from
		JOBNUM ($F9) and JMP to DOIT2 ($D593)
		to do the job.
		-----------------------------------------
GSSPNT	$DF45	Get side sector pointers:
		Load .X with the active buffer number
	$DF47	from
		LINDX ($82). buffer
		Load .A with the side sector
	$DF49	number from SS,X ($CD,X)
		JMP to SETDIR($D4EB) to set the DIRBUF
		pointers.
		-----------------------------------------
SCAL1	$DF4C	Calculate side sectors:
		Load .A with $78, the number of side
SSCALC	$DF4E	sector pointers in a buffer.
		$DF51
		JSR to ADDT12 ($DF5C) to add the number
		of side sectors needed * 120.
		Decrement .X. If .X $00, branch to
	$DF54	SCAL1.
		Load .A with the number of SS indices
	$DF57	needed from T3 ($72) and multiply it
		by 2 (ASL) since two bytes (track & sec)
		are needed for each index.
		JSR
		to ADDT12 to add .A to T1 & T2.
ADDT12	$DF5A	Load .A with the number of SS blocks
		$DF5C
		$DF5D
		needed from T4 ($73) ($70) to the
		Clear the carry flag.
		Add the contents of T1
		contents of the accumulator and store
		the result back in T1 ($70).
	$DF61	If carry is clear, branch to ADDRTS.
	$DF63	Increment the value in T2 ($71).
ADDRTS	$DF65	Terminate routine with an RTS.
		-----------------------------------------
SSTEST	$DF66	Test SSNUM & SSIND for range & residence
		Flag meanings on exit:
		N Range V Residence
		0 OK 0 YES ERO
		0 MAYBE 1 NO ER1
		1 BAD 0 YES ER2
		1 BAD 1 NO ER3
		JSR to SSSET ($DED2) to set the pointer
	$DF69	to $00 and get the SS number (in .A).
		Compare the SS number in .A with the
	$DF6D	one in SSNUM ($D5). If they are not
	$DF6F	equal, branch to ST20.
	$DF73	Load .Y with the pointer into the SS
		buffer from SSIND ($D6)
		Load .A from (DIRBUF),Y; ($94),Y. If
		this value is $00, the proper side
		sector is not present so branch to ST10.
		BIT ERO ($FECD) to clear the N and V
ST10	$DF77	flags. All OK so exit with an RTS.
		Definitely out of range so BIT with E2
ST20	$DF7B	($FECF) and exit with an RTS.
		Load .A with the SS number from SSNUM
	$DF81	($D5) and compare it with $06, the
		number of side sector links. If the
		value in SSNUM > $06, branch to ST30.
		Multiply the SS number in .A by 2 (ASL)
	$DF83	and transfer the result into .Y.
		Load .A with $04, and store this value
	$DF87	in DIRBUF ($94), lo byte of the pointer.
		Load .A with the value from (DIRBUF),Y
ST30	$DF8B	($94),Y. If this value is not $00,
		branch to ST40.
		Way out of range so BIT with E3 ($FED0)
ST40	$DF8F	and exit with an RTS.
		Not in residence and range is unknown
		so BIT with El ($FECE) and exit with RTS
		-----------------------------------------
GETACT	$DF93	Get active buffer number:
		On exit: .A = active buffer number
		.X = LINDX
		Flag N = 1 if no active buffer.
		Load .X with the current buffer number
	$DF95	from LINDX ($82).
		Load .A with the buffer number from
		BUFO,X ($A7,X). If bit. 7 is not set,
		this buffer is active so branch to GAl.
GA1	$DF99	Load .A with the buffer number from
	$DF9B	BUF1,X ($AE,X).
	$DF9D	AND the buffer number with $BF to strip
		the dirty bit.
		Terminate routine with an RTS.
		-----------------------------------------
		Get active buffer & set LBUSED:
		On exit: .A = active buffer number
		.X = LINDX
		Flag N = 1 if no active buffer
		Flag V = 1 if buffer is dirty
GAFLGS	$DF9E	Load .X with the current buffer number
		from LINDX ($82).
GA2	$DFA0	Save buffer number into LBUSED ($0257).
	$DFA3	Load .A with the buffer number from
	$DFA7	BUFO,X ($A7,X). If bit 7 is not set,
	$DFAE	this buffer is active so branch to GA3.
		Transfer the buffer number from .X to
		.A, clear the carry flag, add $07 (the
		maximum number of channels + 1), and
		store the result in LBUSED ($0257).
		Load .A with the buffer number from
		BUF1,X ($AE,X).
GA3	$DFB0	Store the buffer number in T1 ($70).
	$DFB2	AND the buffer number with $1F and BIT
	$DFB6	the result with T1 ($70) to set the
		N and V flags.
		Terminate routine with an RTS.
		-----------------------------------------
		Get a channel's inactive buffer number:
		On entry: LINDX = channel number
		On exit: .A = buffer # or $FF if none
GETINA	$DFB7	Load .X with the channel number from
	$DFB9	LINDX ($82).
	$DFBD	Load .A with the buffer number from
		BUFO,X ($A7,X). If bit 7 is set, this
		buffer is inactive so branch to GI10.
		Load .A with the buffer number from
		BUF1,X ($AE,X).
GI10	$DFBF	Compare the buffer number with $FF to
	$DFC1	set the Z flag if inactive buffer found.
		Terminate routine with an RTS.
		-----------------------------------------
		Set the inactive buffer's buffer number:
		On entry: .A = buffer number
PUTINA	$DFC2	Load .X with the channel number from
	$DFC4	LINDX ($82).
	$DFC6	OR the buffer number in .A with $80 to
		set the inactive buffer bit.
		Load .Y with the buffer number from
		BUFO,X ($A7,X). If bit 7 is clear, the
		other buffer is the inactive one so
		branch to PI1.
PI1	$DFCA	This buffer is inactive so store new
	$DFCC	buffer number in BUFO,X ($A7,X).
	$DFCD	Exit with an RTS.
	$DFCF	This buffer is inactive so store new
		buffer number in BUFO,X ($AE,X).
		Exit with an RTS.
		-----------------------------------------
NXTREC	$DFD0	Set up next relative record:
NXTR15	$DFD5	Load .A with $20 (overflow flag) and
NXTR20	$DFDC	JSR to CLRFLG ($DD9D) to clear the
	$DFDE	record overflow flag.
	$DFE2	Load .A with $80 (last record flag) and
	$DFE4	JSR to TSTFLG ($DDA6) to test if we are
	$DFE6	out beyond the last record. If not,
	$DFE8	branch to NXTR40.
	$DFEA	Load .X with the current channel number
	$DFED	from LINDX ($82).
	$DFEF	Increment the lo byte of the record
	$DFF3	counter in RECL,X ($B5,X). If the result
	$DFF6	is not $00, branch to NXTR15.
	$DFF8	Increment the hi byte of the record
	$DFFA	counter in RECH,X ($BB,X).
	$DFFD	Load .X with the current channel number
	$DFFF	from LINDX ($82).
	$E001	Load .A with the pointer to the next
		record from NR,X ($Cl,X).
		If the next record pointer is $00, there
		is no next record so branch to NXTR45.
		JSR to GETPNT ($D4E8) to get the buffer
		pointer.
		Load .X with the current channel number
		from LINDX ($82).
		Compare the buffer pointer in .A with
		the pointer in NR,X ($Cl,X). If BT $00, branch to P2.
	$E286	Trouble! JMP to BREAK ($E202).
P2	$E289	Clear the carry flag and add the pointer
		is no carry,
		in RELPNT ($D7). If there
		branch to P30.
		set the carry flag.
	$E28E	Add another $01 and
P30	$E291	JSR to NXOUT ($E009) to set up the next
		record.
	$E294	JMP to RD15 ($E138) to complete set up.
		-----------------------------------------
		- * - * - UNUSED CODE - * - *
	$E297	Load .A with $51 (record overflow) and
		JSR to CMDERR ($C1C8).
		-----------------------------------------
		Position proper data blocks into buffers
POSBUF	$E29C	Save the lo byte of the DIRBUF ($94/5)
		pointer into R3 ($89).
	$E2A0	Save the hi byte of the DIRBUF ($94/5)
		pointer into R4 ($8A).
	$E2A4	JSR to BHERE ($E2D0) to check if desired
		branch
		block is in the buffer. If not,
		to P10 to read it in.
	$E2A9	Terminate routine with an RTS.
P10	$E2AA	JSR to SCRUB ($DDFl) to clean the buffer
	$E2AD	JSR to GETLNK ($DE0C) to set TRACK and
	$E2B0	SECTOR from link.
		the
		If TRACK ($80) is $00, there is no next
		track branch
		so to P80.
	$E2B4	JSR to BHERE ($E2D0) to check if desired
		block is in the buffer. If not, branch
		to P75 to read it in.
	$E2B9	JSR to DBLBUF ($CF1E) to toggle the
		$E2BC
		active and inactive buffers.
		JMP to FREIAC ($D2DA) to free the
		inactive buffer.
P75	$E2BF	JSR to FREIAC ($D2DA) to free the
		inactive buffer.
P80	$E2C2	Load .Y with $00.
	$E2C4	Move the desired track from (R3),Y
		($89),Y into TRACK ($80). Increment .Y
	$E2C9	Move the desired sector from (R3),Y
	$E2CD	($89),Y into SECTOR ($81).
		JMP to STRDBL ($D0AF) to read in the
		desired block and the next one too.
BHERE	$E2D0	-----------------------------------------
		Check if desired block is in buffer:
		JSR to GETHDR ($DE3E) to set TRACK and
		SECTOR from the header.
BHERE2	$E2D3	Load .Y with $00
	$E2D5	Compare the desired track from (R3),Y
		($89),Y with the value in TRACK ($80).
		If they are equal, branch to BH10 to
		compare the sectors.
	$E2D9	No match (Z=0) so exit with an RTS
BH10	$E2DC	Increment .Y. (R3),Y
	$E2DD	Compare the desired sector from
		($89),Y with the value in SECTOR ($81).
		This sets Z=1 if they are equal.
	$E2E1	Terminate routine with an RTS.
		--------------------------------------
NULBUF	$E2E2	Set null records in active buffer:
		JSR to SET00 ($DE2B) to set pointers to
	$E2E5	start of data buffer.
		Loop to fill data buffer with $00's
NB20	$E2EE	from $xx02 to $xxFF.
	$E2F1	JSR to ADDNR ($E304) to calculate the
		position of the next record (in .A).
		Store the in
		new pointer value NR,X
	$E2F3	($Cl,X).
		Transfer the next record pointer to .Y.
	$E2F4	Store $FF as the first character in the
		$E2F8
		next record at. (DIRBUF),Y; ($94),Y.
		JSR to ADDNR ($E304) to calculate the
NB30	$E2FB	position of the next record (in .A).
		$E2FD
		$E2FF
		$E303
		If carry flag is clear, we haven't done
		all the records in this block yet so
		branch to NB20.
		If the Z flag is not set, branch to NB30
		Store $00 into NR,X ($Cl,X) to flag the
		last record.
		Terminate routine with an RTS.
		-----------------------------------------
ADDNR	$E304	Add record size & next record pointer:
		On exit: C=1 if crossed buffer boundary
		Load .X with the channel number from
	$E306	LINDX ($82).
		Load .A with the next record pointer
	$E309	from NR,X ($Cl,X) and set the carry flag
		If NR pointer is $00 branch to AN05.
	$E30B	Clear the carry flag and add the record
	$E30E	size from RS,X ($C7,X).
		If carry clear, branch to AN10.
	$E310	If result is not $00, branch to AN05.
	$E312	Load .A with $02 (bypass link)
	$E314	BIT with ER00 ($FECC) to set flags.
	$E317	Terminate routine with an RTS
AN05	$E318	Add $01 to the contents of .A to adjust
AN10	$E31B	for the link and set the carry flag.
		Terminate routine with an RTS
		-----------------------------------------
ADDREL	$E31C	Add blocks to a relative file:
		JSR to SETDRN ($D1D3) to set drive #.
	$E31F	JSR to SSEND ($E1CB) to set up end of
	$E322	file.
		JSR to POSBUF ($E29C) to position the
	$E325	proper data blocks into the buffers.
		JSR to DBSET ($CF7C) to set up double
	$E328	buffering.
		Copy side sector index from SSIND ($D6)
	$E32C	into R1 ($87).
		Copy side sector number from SSNUM ($D5)
		into RO ($86).
	$E330	Set R2 ($88) to $00 to clear
		fcr one block. the flag
	$E334	Set RECPTR ($D4) to $00 to clear this
		for calculations.
	$E338	JSR to FNDREL ($CE0E) to calculate the
		side sector pointers.
ADDR1	$E33B	JSR to NUMFRE ($EF4D) to calculate the
		number of blocks free.
	$E33E	Load .Y with the channel number from
		LINDX ($82).
	$E340	Load .X with the record size from RS,Y
		($C7,Y), decrement the size by 1, and
		transfer the result into .A.
	$E344	Clear the carry flag and add the record
		pointer, RELPTR ($D7) to the record size
		in .A.
	$E347	If no carry results, there is no span
		to the next block so branch to AR10.
	$E349	Increment the SS pointer, SSIND ($D6)
		twice. If the result is not zero, branch
		to AR10.
	$E34F	Increment the side sector number, SSNUM
		(D5) by 1 and store $10 (the side sector
		offset) into SSIND ($D6) since we are
		starting a new block.
AR10	$E355	Load .A with the SS index from R1, clear
		the carry flag,
		add $02, and JSR to
		SETSSP ($DEE9) to set DIRBUF & BUFTAB.
	$E35D	Load the side sector number from SSNUM
		($D5) and compare it with $06, the
		number of side sector links. If SSNUM
		is less than or equal to $06, the range
		is valid so branch to AR25.
AR20	$E363	Load .A with $52 to indicate a TOO BIG
		RELATIVE FILE error and JSR to CMDERR
		($C1C8).
AR25	$E368	Load .A with the side sector index from
		SSIND ($D6) and set the carry flag.
	$E36B	Subtract. the SS index from R1 ($87). If
		the result is positive, branch to AR30.
	$E36F	Subtract $0F (the side sector index
		offset less 1) and clear the carry flag.
AR30	$E372	Store the number of side sector indicies
		(in .A) into T3 ($72).
	$E374	Load .A with the SS number from SSNUM
		($D5). Subtract. the SS number from RO
		($86) to find the number of side sectors
		needed. Store the number needed into
		T4 ($73).
	$E37A	Zero T1 ($70) and T2 ($71) to serve as
		a results accumulator.
	$E380	Transfer the number of side sectors
	$E384	needed from .A to .X and JSR to SSCALC
		($DF51) to calculate the number of
		blocks needed.
		Load .A with the hi byte of the number
	$E388	needed from T2 ($71). If the hi byte is
		not $00, branch to AR35.
		Load .X with the lo byte of the number
	$E38D	needed from T1 ($70). Decrement .X by 1.
		If the result is not $00, branch to AR35
		Increment R2 ($88) by 1.
AR35	$E38F	Check if there are enough blocks left:
	$E396	Compare the hi byte of the number of
		blocks needed (in .A) with the hi byte
		of the number of blocks free in NBTEMP+1
		($0273). If there are more than enough,
		branch to AR40. If there are NOT enough,
		branch to AR20. If we have just enough,
		we had better check the lo byte.
		Load .A with the lo byte of the number
AR40	$E39D	free from NBTEMP ($0272) and compare it
		with the lo byte of the number needed in
		T1 ($70). If there are not enough,
		branch to AR20 to abort.
		Load .A with $01 and JSR to DRDBYT
	$E3A2	($D4F6) to read the sector link.
		Clear the carry flag and add $01 to .A
	$E3A5	to give the NR.
		Load .X with the channel number from
	$E3A7	LINDX ($82).
		Store the NR value (in .A) into NR,X
		$E'A9 ($Cl,X).
	$E3AC	JSR to NXTTS ($F11E) to get the next
	$E3AF	available track and sector.
	$E3B3	JSR to SETLNK ($DDFD) to set the track
		and sector link in the current block.
		Load .A with the add-l-block flag from
		R2 ($88). If the flag is set, branch
		to AR50.
		JSR to WRTOUT ($DE5E) to write the
AR45	$E3B6	current block to disk.
		JSR to DBLBUF ($CF1E) to switch
		buffers.
	$E3B9	JSR to SETHDR ($D6D0) to set header from
	$E3BC	TRACK SECTOR.
		$E3BF ~ and
		$E3C2
		JSR to NXTTS ($F11E) to get the next.
		track
		available and sector.
		JSR to SETLNK ($DDFD) to set the track
		and sector link in the current block.
		JSR to NULBUF ($E2E2) to clean out the
	$E3C5	buffer
		JMP to AR55 ($E3D4).
AR50	$E3C8	JSR to DBLBUF ($CF1E) to switch buffers.
	$E3CB	JSR to SETHDR ($D6D0) to set header from
	$E3CE	TRACK and SECTOR.
	$E3D1	JSR to NULBUF ($E2E2) to
		buffer
		JSR to NULLNK ($DE19) to
		clean out the
		set link for
AR55	$E3D4	the last block. write the
		JSR to WRTOUT ($DE5E) to
	$E3D7	current block to disk. set TRACK and
		JSR to GETLNK ($DE0C) to
	$E3DA	SECTOR from the track & sector link.
		$E3E0
		Save the value of TRACK ($80) and SECTOR
		and
		($81) onto the stack.
		JSR to GETHDR ($DE3E) to set TRACK
	$E3E3	SECTOR from the last sector read. SECTOR
		Save the value of TRACK ($80) and
	$E3E9	($81) onto the stack.
		JSR to GSSPNT ($DF45) to calculate the
	$E3EC	side sector pointer (returned in .A)
	$E3EF	Transfer the pointer in .A to .X. If the
	$E3F2	pointer value is not $00, we don't need
		another side sector so branch to AR60.
		JSR to NEWSS ($E44E) to get another side
		sector.
		Load .A with $10, side sector offset,
	$E3F7	and JSR to SETSSP ($DEE9) to set the
		count in RO
		side sector pointer.
		Increment the side sector
		($86) by 1.
AR60	$E3F9	Pull this sector's track off the stack
	$E3FD	and JSR to PUTSS ($DD8D) to write it
	$E401	into the side sector buffer.
		Pull this sector's sector off the stack
		and JSR to PUTSS ($DD8D) to write it
		into the side sector buffer.
		Pull this sector's sector link off the
	$E404	stack and store it in SECTOR ($81).
		Pull this sector's track link off the
	$E407	stack and store it in TRACK ($80).
		If track link is $00, there are no more
	$E409	blocks in this file so branch to AR65
		Compare the side sector counter in RO
	$E40F	($86) with the end count in SSNUM ($D5).
		If they are not equal, we haven't done
		enough new blocks yet so branch to AR45.
		Almost done so JSR to GSSPNT ($DF45) to
	$E412	get the side sector pointer.
		Compare the pointer in .A with the end
		pointer in SSIND($D6). If SSIND>.A, we
		are almost done so branch to AR45. If
		SSIND=.A there is one more block left so
		branch to AR50.
AR65	$E418	All done. JSR to GSSPNT ($DF45) to get
	$E41C	the side sector pointer. Save it onto
	$E421	the stack.
	$E427	Load .A with a $00 and JSR to SSDIR
	$E42D	($DEDC) to set DIRBUF with the current
	$E430	SS pointer.
	$E433	Zero .A and .Y. Zero the track link of
	$E436	the side-sector sector in (DIRBUF),Y
	$E439	($94),Y. Increment .Y.
	$E43C	Pull the pointer into this sector off
	$E43F	the stack, subtract $01, and store the
	$E441	result as the sector link of the side-
		sector sector in (DIRBUF),Y; ($94),Y.
		JSR to WRTSS ($DE6C) to write out the
		current block of side sectors to disk.
		JSR to WATJOB ($D599) to wait for the
		write job to be completed.
		JSR to MAPOUT ($EEF4) to write the BAM.
		JSR to FNDREL ($CE0E) to find the
		relative file and calculate SSNUM and
		SSIND for the desired record.
		JSR to DBLBUF ($CF1E) to get back to
		the leading buffer.
		JSR to SSPOS ($DEF8) to position SS and
		BUFTAB to SSNUM and SSIND.
		On return, if V flag is set, the record
		is still beyond the end of the relative
		file so branch to AR70.
		All OK so exit from routine with a JMP
		to POSITN ($E275) to position to the
		record.
AR70	$E444	Still beyond end of file so: load .A
		with $80 (the last record flag), JSR to
		SETFLG ($DD97) to set the flag, load .A
		with $50 (no record error) and exit with
		a JSR to CMDERR ($C1C8).
		-----------------------------------------
		Create a new side sector and change the
		old side sectors to reflect it.
NEWSS	$E44E	JSR to NXTTS ($F11E) to find the next
	$E451	available track and sector.
	$E454	JSR to DBLBUF ($CF1E) to toggle to the
	$E457	inactive buffer.
	$E45B	JSR to SCRUB ($DDFl) to write out the
	$E45E	buffer if it is dirty (doesn't match
		copy on disk).
		JSR to GETACT ($DF93) to determine the
		active buffer number (returned in .A).
		Save the buffer number onto the stack.
		JSR to CLRBUF ($DEC1) to zero the buffer
		Load .X with the channel number from
		LINDX ($82).
	$E460	Load .A with the number of the buffer
		containing the side sectors from SS,X
		($CD,X) and transfer this value into .Y.
	$E463	Pull the active buffer number off the
		stack and transfer it into .X.
	$E465	Load .A with $10, the side sector offset
	$E467	JSR to BOTOBO ($DEA5) to move $10 (.A)
		bytes from buffer #(.X) to buffer #(.Y).
	$E46A	Load .A with $00 and JSR to SSDIR($DEDC)
		to set the pointer at DIRBUF ($94) to
		point to the start of the old SS buffer.
	$E46F	Load .Y with $02, and load .A with the
		side sector number from (DIRBUF),Y and
		save it onto the stack.
	$E474	Zero .A and JSR to SETPNT ($D4C8) to set
		the pointer at DIRBUF ($94) to point to
		the start of the new SS buffer.
	$E479	Pull the SS number off the stack, add 1,
		and store the result. in the new side
		sector table at (DIRBUF),Y.
	$E47F	Multiply the SS number in .A by 2 (ASL),
		add 4, store the result. (points to the
		new SS value in the buffer) in R3 ($89),
		and transfer this value into .Y.
	$E485	Subtract $02 from the result and store
		this pointer in R2 ($88).
	$E48A	Copy the current value of TRACK ($80)
		into Rl ($87) for use in SS update and
		into the new SS buffer at (DIRBUF),Y
	$E490	Increment .Y
	$E491	Copy the current value of SECTOR ($81)
		into R2 ($88) for use in SS update and
		into the new SS buffer at. (DIRBUF),Y
	$E497	Set the track link at the start of the
		new SS block to $00.
	$E49C	Set the sector link at the start of the
		new SS block to $11 to indicate that the
		last. non-zero character in the buffer is
		the one following the SS offset.
	$E4A1	Load .A with $10 (the SS offset) and JSR
		to SETPNT ($D4C8) to set the pointer
		to the SS block.
		new
	$E4A6	JSR to WRTAB ($DE50) to write out. the
		new side sector block to disk.
	$E4A9	JSR to WATJOB ($D599) to wait for the
		write job to be completed.
		-----------------------------------------
		Note: Finished creating new block. Now,
		revise old SS to reflect the new.
		-----------------------------------------
NS20	$E4AC	Load .X with the channel number from
	$E4AE	LINDX ($82).
		Load .A with the side sector buffer
		number from SS,X ($CD,X) and save this
		number onto the stack.
	$E4B1	JSR to GAFLGS ($DF9E) to get active
		buffer number and set flags.
	$E4B4	Load .X with the new channel number from
		LINDX ($82).
	$E4B6	Store the side sector buffer number from
		.A into SS,X ($CD,X). Note: this swaps
		the active buffer and the SS buffer.
	$E4B8	Pull the old side sector buffer number
	$E4BE	off the stack, load .X with the last
		buffer used from LBUSED ($0257), and
		store the old SS buffer # (in .A) into
		BUFO,X ($A7,X).
		Zero .A and JSR to SETPNT ($D4C8) to set
		the buffer pointer to the start of the
		buffer.
	$E4C3	Zero .Y and set the track link to point
	$E4CA	to the new SS block using the value from
		TRACK ($80). Increment .Y.
		Set the sector link to point to the new
		SS block using the value from SECTOR
	$E4CE	($81).
		JMP to NS50 ($E4DE).
NS40	$E4D1	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
	$E4D4	Load .X with the channel number from
		LINDX ($82).
	$E4D6	JSR to IBRD ($DF1B) to read the next SS.
	$E4DB	buffer number (returned in .A).
		Zero .A and JSR to SETPNT ($D4C8) to set
		the buffer pointer to the start of the
		buffer.
NS50	$E4DE	Decrement the pointer in R4 ($8A) twice.
	$E4E2	Load .Y with the pointer into the buffer
		from R3 ($89).
	$E4E4	Load .A with the new SS track pointer
		from R1 ($87) and store this value into
		the data buffer at (DIRBUF),Y.
	$E4E8	Increment .Y.
	$E4E9	Load .A with the new SS sector pointer
	$E4ED	from R2 ($88) and store this value into
	$E4F0	the data buffer at (DIRBUF),Y.
		JSR to WRTOUT ($DE5E) to write out the
		revised side sector block.
		JSR to WATJOB ($D599) to wait for the
		write job to be completed.
	$E4F3	Load .Y with the pointer from $R4 ($8A)
	$E4F9	and compare it to $03. If .Y>$03, there
		are more side sectors to update so
		branch back to NS40.
		Terminate routine with a JMP to DBLBUF
		($CF1E) to reset the active buffer.
		-----------------------------------------
		ERROR MESSAGE TABLE $E4FC - $E5D4
		-----------------------------------------
		Each entry consists of the applicable error numbers
		followed by the message test with the first and last
		characters OR'ed with $80. The key words in the text
		are tokenized (values $80 - $8F). The tokenized word
		list follows the main error message table.
		-----------------------------------------
		Address Error numbers Error Message
		-----------------------------------------
	$E4FC	$00                     OK
	$E500	$20,$21,$22,$23,$24,$27 READ ERROR
	$E50B	$52                     FILE TOO LARGE
	$E517	$50                     RECORD NOT PRESENT
	$E522	$51                     OVERFLOW IN RECORD
	$E52F	$25,$28                 WRITE ERROR
	$E533	$26                     WRITE PROTECT ON
	$E540	$29                     DISK ID MISMATCH
	$E546	$30,$31,$32,$33,$34     SYNTAX ERROR
	$E552	$60                     WRITE FILE OPEN
	$E556	$63                     FILE EXISTS
	$E55F	$64                     FILE TYPE MISMATCH
	$E567	$65                     NO BLOCK
	$E570	$66,$67                 ILLEGAL TRACK OR SECTOR
	$E589	$61                     FILE NOT OPEN
	$E58D	$39                     FILE NOT FOUND
	$E592	$01                     FILES SCRATCHED
	$E59F	$70                     NO CHANNEL
	$E5AA	$71                     DIR ERROR
	$E5AF	$72                     DISK FULL
	$E5B6	$73                     CBM DOS V2.6 4030
	$E5C8	$74                     DRIVE NOT READY
		-----------------------------------------
		TABLE OF TOKENIZED WORDS $E5D5 - $E609
		-----------------------------------------
	$E5D5	$09 ERROR        $E5F4 $06 NOT
	$E5DB	$0A WRITE        $E5F8 $07 FOUND
	$E5E1	$03 FILE         $E5FE $08 DISK
	$E5E6	$04 OPEN         $E603 $0B RECORD
	$E5EB	$05 MISMATCH
		-----------------------------------------
ERROR	$E60A	Handle errors reported by controller:
		On entry: .A = error code number
		          .X = job number
		Save the error code onto the stack.
	$E60B	Store the job number into JOBNUM ($F9).
	$E60D	Transfer job number (from .X) to .A,
		multiply it by 2 (ASL), and transfer the
		result back into .X.
ERR1	$E610	Set TRACK ($80) and SECTOR ($81) using
	$E618	the values from the last header read in
	$E619	HDRS,X ($06,X) and HDRS+1,X ($07,X).
	$E61D	Pull the disk controller error code off
	$E621	the stack and convert it into a DOS
	$E625	error code by:
		AND the error code in .A with $0F. If
		the result is $00, branch to ERR1 to
		handle error codes $10 - $14.
		Compare the result to $0F (no drive).
		If the code is NOT $0F, branch to ERR2.
		Load .A with $74 (DOS no drive code)
		and branch to ERR3 (always).
		Load .A with $06.
ERR2	$E627	OR the code in .A with $20 and subtract
ERR3	$E62D	2 from the result.
		Save the DOS error code onto the stack.
ERR4	$E62E	Compare the command number from CMDNUM
	$E635	($022A) with $00 to see if this was a
	$E63A	VALIDATE command. If not, branch to ERR4
	$E63E	Set CMDNUM ($022A) to $FF.
	$E641	Pull the DOS error code off the stack
	$E644	and JSR to ERRMSG ($E6C7) to transfer
		the error message to the error buffer.
		JSR to INITDR ($D042) to initialize the
		drive and eliminate the bad BAM in RAM.
		JMP to CMDER3 ($E648) to complete the
		error handling.
		Pull the DOS error code off the stack.
CMDER2	$E645	JSR to ERRMSG ($E6C7) to transfer the
CMDER3	$E648	the error message to the error buffer.
		JSR to CLRCB ($C1BD) to clear out the
	$E64B	command buffer.
	$E650	Clear the write-BAM flag, WBAM ($02F9)
	$E653	so a bad copy of the BAM will not be
	$E656	written to disk.
	$E65A	JSR to ERRON ($C12C) to start the error
		LED flashing.
		JSR to FREICH ($D4DA) to free the
		internal read or write channel.
		Zero BUFTAB+CBPTR ($A3) to clear the
		pointers.
		Load .X with $45 (#TOPWRT) and transfer
		this value to the STACK POINTER to purge
		the stack
	$E65D	Load .A with the original secondary
		address from ORGSA ($84), AND it with
		$0F, and store the result as the current
		secondary address in SA ($83).
	$E663	Compare the secondary address (in .A)
	$E667	with $0F. If it is $0F (the command
	$E668	channel), branch to ERR10.
	$E66C	Set the interrupt flag to prevent any
	$E670	interrupts!
	$E672	If the listener active flag in LSNACT
	$E679	($79) is not $00, we are an active
		listener so branch to LSNERR.
		If the talker active flag in TLKACT
		($7A) is not $00, we are an active
		talker so branch to TLKERR.
		Load .X with the current secondary
		address from SA ($83).
		Load .A with the active channel number
		from LINTAB,X ($022B,X). If this channel
		number is $FF, the channel is inactive
		so branch to ERR10.
		AND the channel number (in .A) with $0F,
		store it as the current channel number
		in LINDX ($82) and JMP to TLERR ($E68E).
		-----------------------------------------
TLKERR	$E680	Talker error recovery:
		Release all bus lines and go idle.
		JSR to FNDRCH ($D0EB) to find an unused
LSNERR	$E683	read channel.
	$E688	JSR to ITERR ($EA4E) to release all bus
		lines and JMP to IDLE ($EBE7).
		Listener error recovery:
		Release all bus lines and go idle.
		JSR to FNDRCH ($D0EB) to find an unused
TLERR	$E68B	read channel.
	$E68E	JSR to ITERR ($EA4E) to release all bus
		lines and JMP to IDLE ($EBE7).
		Unused on the 1541
ERR10	$E698	Terminate routine with a JMP to IDLE
		($EBE7).
		-----------------------------------------
HEXDEC	$E69B	Convert hex to BCD:
		On entry: .A contains hex number
		On exit: .A contains BCD number
		Transfer hex from .A to .X.
HEXO	$E69C	Zero .A and set decimal mode (SED).
	$E69F	Compare .X value to $00. If equal,
HEX5	$E6A3	branch to HEX5 to exit.
	$E6AA	Clear carry flag, add 1 to value in .A,
		decrement .X, and JMP back to HEXO.
		Clear decimal mode (CLD).
		Convert BCD to ASCII decimal digit.
		On exit: .X contains BCD number
		(CB+2)Y contains ASCII
BCDDEC	$E6AB	Transfer BCD from .A to .X.
	$E6AC	Divide BCD value in .X by 16 (4 x LSR)
	$E6B0	JSR to BCD2 ($E6B4) to convert the most
	$E6B3	significant digit to ASCII.
		Transfer original BCD byte from .X to .A
BCD2	$E6B4	AND the BCD value in .A with $0F to mask
	$E6BA	off the higher order nybble, OR the
	$E6BB	result with $30 (convert to ASCII), and
		store the ASCII value in (CB+2)Y; ($A5)Y
		Increment .Y
		Terminate routine with an RTS.
		-----------------------------------------
OKERR	$E6BC	Transfer error message to error buffer:
ERRTSO	$E6BF	JSR to ERROFF ($C123) to turn off error
	$E6C1	LED.
		Load .A with $00 (no error).
		Set TRACK ($80) and SECTOR ($81) to $00.
ERRMSG	$E6C7	Load .Y with $00.
	$E6C9	Set pointer at CB+2/3 ($A5/6) to point
	$E6D1	to the error buffer. ($02D5).
		JSR to BCDDEC ($E6AB) to convert the
	$E6D4	BCD number in .A to ASCII and store it
		at the start of the error buffer.
		Store $2C "," after the error code in
	$E6D8	the error buffer (CB+2),Y; ($A5),Y.
		Increment .Y (points into error buffer).
	$E6D9	Copy the first character of the error
	$E6DF	buffer from ERRBUF ($02D5) into the
	$E6E3	channel data area CHNDAT+ERRCHN ($0243).
		Transfer the error number from .X to .A
		and JSR to ERMOVE ($E706) to move the
		error message into the error buffer.
		Store $2C "," after the error message in
	$E6E7	the error buffer (CB+2),Y; ($A5),Y.
		Increment .Y (points into error buffer).
	$E6E8	Load .A with the track number from
	$E6EA	TRACK ($80).
	$E6ED	JSR to BCDDEC ($E6AB) to convert the
	$E6F1	track number in .A to ASCII and store
		it in the error buffer.
		Store $2C "," after the track number in
		the error buffer (CB+2),Y; ($A5),Y.
		Increment .Y (points into error buffer).
	$E6F2	Load .A with the sector number from
	$E6F4	SECTOR ($81).
		JSR to BCDDEC ($E6AB) to convert the
		sector number in .A to ASCII and store
		it in the error buffer.
	$E6F7	Decrement the .Y pointer by 1, transfer
		the result to .A, clear the carry flag,
		add $D5 (the start of the error buffer),
		and store the final result (points to
		the last character) into LSTCHR+ERRCHN
		($0249).
	$E6FF	Increment the lo byte of the pointer
		in CB+2 ($A5) by 1 so it points to the
		second character of the message (we put
	$E701	the first character into the channel
		data area already.
		Set error channel status CHNRDY+ERRCHN
	$E705	($F7) to $88 to indicate that it is
		ready-to-talk.
		Terminate routine with an RTS.
		-----------------------------------------
ERMOVE	$E706	Move the error message from the error
		table to the error buffer. The tokens
		in the table are converted to words.
		Transfer the error message number from
	$E707	.A to .X.
		Save the current values of RO ($86) and
	$E70D	R0+1 ($87) onto the stack so we can use
		this as a temporary pointer.
		Set up a pointer in R0/R0+1 to point. to
	$E715	the error message table in ROM ($E4FC).
		Transfer the error number back into .A.
	$E716	Zero .X to use as an indirect pointer.
E10	$E718	Compare the error number (in .A) with
		the error number in the table (RO,X)
		($86,X). If a match is found, branch
		to E50.
	$E71C	Save error number onto the stack.
	$E71D	JSR to EADV2 ($E775) to advance the
	$E720	pointer to the error table.
		If carry flag is clear, there are more
E20	$E722	messages to check so branch to E30
		No more messages so JSR to EADV2 ($E775)
	$E725	to advance the pointer.
		If carry flag is clear, we are not done
E30	$E727	with the message yet so branch to E20.
		Compare the hi byte of the pointer in
	$E72F	R0+1 ($87) to $E6. If the pointer is
		less than $E6, there is more table left
		so branch to E40. If the pointer is
		greater than $E6, we are past the end of
		the table so branch to E45.
		The hi bytes match so compare the lo
E40	$E735	bytes of the pointer in RO ($86) with
		$0A (the end of the table). If we are
		past the end, branch to E45.
		Pull the error number off the stack and
		JMP to ElO to continue checking.
E45	$E739	Can't find error number in table so
E50	$E73D	pop the error number off the stack and
		JMP to E90 ($E74D) to quit.
		The error number has been located so
	$E740	JSR to EADV1 ($E767) to advance past.
		the other error numbers.
		If carry flag is clear, we have not
E55	$E742	advanced far enough so branch to E50.
		JSR to E60 ($E754) to check for token
	$E745	and put character(s) into buffer.
		JSR to EADV1 ($E767) to advance pointer.
	$E748	If carry flag is clear, there is more to
	$E74A	do so branch back to E55.
		JSR to E60 ($E754) to check for token
E90	$E74D	or last word.
		All done! Pull original RO and RO+l
	$E753	values off the stack and replace them.
		Terminate routine with an RTS.
		-----------------------------------------
E60	$E754	Sub to check for token or word and put
		it into the buffer.
		Compare the character in .A with $20
	$E758	(the maximum token number +1). If .A is
		greater, this is not a token so branch
		to E70.
		Save token (in .A) into .X.
	$E759	Store $20 (implied leading space) into
	$E75D	the buffer at (CB+2),Y; ($A5),Y.
		Increment .Y.
	$E75E	Move the token from .X back into .A.
	$E75F	JSR to ERMOVE ($E706) to add the token
	$E762	word to the message.
		Terminate routine with an RTS.
E70	$E763	Store character (in .A) into the buffer
	$E765	at (CB+2),Y; ($A5),Y.
		Increment .Y pointer into error buffer.
	$E766	Terminate routine with an RTS.
		-----------------------------------------
EADV1	$E767	Sub: Advance error pointer before move:
		Increment the lo byte of the pointer in
	$E76B	RO ($86). If the new value is not $00,
		branch to EA10.
		Increment the hi byte of the pointer in
		R0+l ($87).
EA10	$E76D	Load .A with the next character from
	$E76F	the error message table (RO,X); ($Al,X).
		Shift the byte in .A left to set the
	$E770	carry flag if this is the first or last
		character in the message.
		Load .A with the next character from
		the error message table (RO,X); ($Al,X).
	$E772	AND the character in .A with $7F to mask
		off bit 7.
	$E774	Terminate routine with an RTS.
		----------------------------------------
		Sub: Advance error pointer after move:
EADV2	$E775	JSR to EA10 ($E76D) to get the next
		byte from the error message table.
	$E778	Increment the lo byte of the pointer in
		RO ($86). If the new value is not $00,
		branch to EA20.
	$E77C	Increment the hi byte of the pointer in
		R0+1 ($87).
EA20	$E77E	Terminate routine with an RTS.
		---------------------------------------------------------
		UTILITY LOADER PROGRAM
		----------------------------------------------------------
		This utility is used to load and execute user programs
		or system utilities from disk.
		This utility may be used in two ways:
		a) On power-up:
		If the data and clock lines are grounded at power up,
		the routine is entered. It waits until the ground clip
		is removed and then loads the first file found in the
		directory into disk RAM using the first. two bytes of
		the file as the load address. Once the file is loaded,
		it is executed starting at the first byte.
		b) Normal entry:
		The disk command "&:filename" will load and execute
		the file whose filename is specified. For example:
		PRINT#15,"&O:DISK TASK"
		----------------------------------------------------------
		File structure:
		The utility or program must be of the following form.
		File type: USR
		Bytes 1/2: Load address in disk RAM (lo/hi).
		Byte 3:    Lo byte of the length of the routine
		Bytes 4/N: Disk routine machine code.
		Byte N+1:  Checksum.  Note that the checksum includes
		           all bytes including the load address.
		           formula: CHECKSUM = CHECKSUM + BYTE + CARRY
		----------------------------------------------------------
		NOTE: Routines may be longer than 256 bytes. However,
		there MUST be a valid checksum byte after the
		number of bytes specified in byte #3 and after
		each subsequent 256 bytes!
		--------------------------------------
BOOT2	$E77F	Exit routine with an RTS.
		----------------------------------------------------------
BOOT	$E780	Load .A with input port data from PB
		($1800). Transfer data from .A to .X.
	$E784	AND the data byte (in .A) with $04 to
		see if clock is grounded. If not, branch
		to BOOT2 to exit.
	$E788	Transfer data byte from .X to .A.
	$E789	AND the data byte (in .A) with $01 to
		see if data line is grounded. If not,
		branch to BOOT2 to exit.
	$E78D	Clear interrupt flag so that background
		routines will run.

		BOOT CLIP MUST BE ON!
BOOT3	$E78F	Load .A with input port data from PB
		($1800).
	$E791	AND the data byte (in .A) with $05 to
	$E795	see if clip has been removed. If not,
		branch to BOOT3 to wait until it is.
		Set the number of files to $01 by
	$E798	incrementing F2CNT ($0278).
		Set the command string length to $01 by
	$E79B	incrementing CMDSIZ ($0274).
		Set the first character in the command
	$E7A0	buffer, CMDBUF ($0200), to $2A ("*") to
		match any file name.
		JMP to BOOT4 ($E7A8) to continue.
UTLODR	$E7A3	NORMAL ENTRY POINT
		Load .A with $8D and JSR to PARSE
BOOT4	$E7A8	($C268) to parse the command string.
		JSR to KILLP ($F258) to kill protect.
	$E7AB	Does nothing on the 1541!
	$E7AF	Load .A with the file count from F2CNT
	$E7B4	($0278) and save it on the stack.
		Set file count in F2CNT ($0278) to $01.
		Set first-byte flag in RO ($86) to $FF.
	$E7B8	JSR to LOOKUP ($C44F) to locate the file
	$E7BB	name on the disk.
	$E7C0	Check the track link for the file found
	$E7C5	in FILTRK ($0280). If it is $00, the
		file was not found so branch to UTLD00.
		Load .A with $39 to indicate a FILE NOT
		FOUND error and JSR to CMDERR ($C1C8) to
		exit.
		Pull original file count off the stack
		UTLD00
	$E7C9	and restore it into F2CNT ($0278).
		Set TRACK ($80) from the track link
	$E7CE	for the file from FILTRK ($0280).
	$E7D3	Set SECTOR ($81) from the sector link
		for the file from FILSEC ($0285).
		Load .A with $03 (USER FILE TYPE) and
UTLD10	$E7D8	JSR to OPNTYP ($D477) to open the file.
		Load .A with $00 and store it in Rl($87)
	$E7DC	to initialize the checksum.
	$E7DF	JSR to GTABYT ($E839) to get the first
		byte from the file (lo of load address).
		Store the lo byte of the load address
		in R2 ($88).
	$E7E1	JSR to ADDSUM ($E84B) to add the byte
	$E7E4	into the checksum.
		JSR to GTABYT ($E839) to get the second
	$E7E7	byte from the file (hi of load address).
		Store the hi byte of the load address
		in R3 ($89).
	$E7E9	JSR to ADDSUM ($E84B) to add the byte
	$E7EC	into the checksum.
	$E7F0	Load .A with the flag from RO ($86). If
	$E7F3	the flag is $00, this is not the load
		address so branch to UTLD20.
		Load lo byte of load address from R2
		($88) and save it onto the stack.
		Load hi byte of load address from R3
	$E7F6	($89) and save it onto the stack.
		Set first-byte flag in RO ($86) to $00.
UTLD20	$E7FA	JSR to GTABYT ($E839) to get the data
UTLD30	$E7FD	byte count from the file.
	$E7FF	Store the data byte count in R4 ($8A).
	$E802	JSR to ADDSUM ($E84B) to add the byte
		into the checksum.
		JSR to GTABYT ($E839) to get a data byte
	$E805	from the file.
		Zero .Y and store the data byte (in .A)
	$E809	at. desired address, (R2),Y; ($88),Y.
		JSR to ADDSUM ($E84B) to add the byte
	$E80C	into the checksum.
		Increment the lo byte of the pointer in
	$E813	R2 ($88) by $01. If the result is not
		$00, branch to UTLD35.
		Increment the hi byte of the pointer in
		R3 ($89) by $01.
UTLD35	$E817	Decrement the byte counter in R4 ($8A).
	$E81B	If the result is not $00, there are more
		bytes to get so branch back to UTLD30.
		JSR to GIBYTE ($CA35) to get a data byte
	$E81E	from the file without an EOI check.
		Load .A with the checksum from DATA($85)
	$E824	and compare it with the computed check-
		sum in R1 ($87), If they match, all is
		OK so branch to UTLD50.
		Bad checksum so JSR to GETHDR ($DE3E) to
	$E827	set TRACK and SECTOR from the header.
		Load .A with $50 to indicate a NO RECORD
UTLD50	$E82C	error and JSR to CMDER2 ($E645).
		Load .A with the EOI flag from EIOFLG
		($F8). If the flag is NOT $00, we are
		not done yet so branch back to UTLD10
		to do another 256 bytes.
	$E830	Routine all loaded so pull load address
		off the stack (lo/hi), set up a jump
		vector in F2/3 ($88/9), and do an
		indirect JMP to the routine via (R2).
		-----------------------------------------
		Subroutines for UTLODR
		-----------------------------------------
GTABYT	$E839	Get a byte from the file opened using
		the internal read channel. There is an
		end-of-file check done. If EOI occurs,
		a #51 DOS error is reported.
		JSR to GIBYTE ($CA35) to fetch a byte
	$E83C	and store it in DATA ($85).
		Test the end of information flag, EOIFLG
	$E840	($F8). If NOT $00, we have not come to
		the end so branch to GTABYE.
		We have an EOI condition. JSR to GETHDR
	$E843	($DE3E) to set TRACK and SECTOR from
		the header.
		Load .A with $51 to indicate a RECORD
GTABYE	$E848	SIZE error and JSR to CMDER2 ($E645).
		Load .A with the byte from DATA ($85)
	$E84A	Terminate routine with an RTS.
		-----------------------------------------
ADDSUM	$E84B	Compute the running checksum in R1:
		On entry: .A = new byte to add
		Clear the carry flag.
	$E84C	Add the byte in R1 ($87) to the byte in
	$E850	.A and then add $00 to the result to
		add in the carry bit.
		Store the new checksum into R1.
	$E852	Terminate routine with an RTS.
		-----------------------------------------
		SERIAL BUS COMMUNICATION ROUTINES
		-----------------------------------------
		ENTRY POINT FOR IRQ ROUTINE TO SERVICE
		ATTENTION (ATN) SIGNALS FROM THE C-64.
		-----------------------------------------
ATNIRQ	$E853	Load .A with the contents of PA1 ($1801)
		to clear the interrupt (IRQ) flag (CA1).
	$E856	Store $01 in ATNPND ($7C) to indicate
		that an ATN request is pending.
	$E85A	Terminate routine with an RTS.
		-----------------------------------------
		Service the attention request from the
		C-64.
ATNSRV	$E85B	Set the interrupt flag (SEI) to prevent
		any interrupts.
	$E85C	Store $00 in ATNPND ($7C) to indicate
		that no ATN request is pending.
	$E860	Zero the listener and talker active
		flags LSNACT ($79) and TLKACT ($7A).
	$E864	Load .X with $45 and transfer this value
    		to the stack pointer to reset the stack.
	$E867	Store $80 in the EOI flag, EOIFLG ($F8)
		to indicate a non-EOI state.
	$E86B	Store $80 in the ATN mode flag, ATNMOD
		($7D) to set ATN mode for ACPT routine
	$E86D	JSR to CLKHI ($E9B7) to wait for the
		clock line go high.
	$E870	JSR to DATLOW ($E9A5) to set the data
		line low as a response.
	$E873	To get hardware control of the data line
		acknowledge the attention signal by:
		loading .A with the contents of port B,
		PB ($1800), OR the byte with $10 to set.
		the ACK ATN bit, and store the result
		back into port B, PB ($1800).
ATNS15	$E87B	Check to see if the ATN signal is still
		present by: loading .A with the contents
		of port B, PB ($1800). If bit 7 is not
		set, the ATN signal is gone so branch
		to ATNS20 ($E8D7).
	$E880	AND the contents of .A with $04 to see
		if the clock line is still low. If bit
		2 is set (result of AND is not $00), the
		clock line is still low so branch back
		to ATNS15 to wait.

		Clock line went high so there is a
		command byte waiting for us.
	$E884	JSR to ACPTR ($E9C9) to get the command
		byte.
	$E887	Compare the command byte (in .A) with
		$3F (unlisten). If this is not an
		unlisten command, branch to ATN35.

		General unlisten command received.
	$E88B	Zero the listener active flag, LSNACT
		($7A) and branch to ATN122 ($E902).

ATNS35	$E891	Compare the command byte (in .A) with
		$5F (untalk). If this is not an untalk
		command, branch to ATN40.

		General untalk command received.
	$E895	Zero the talker active flag, TLKACT
		($7A) and branch to ATN122 ($E902).

ATN40	$E89B	Compare the command byte (in .A) with
		our talk address in TLKADR ($78). If
		this is not our talk address, branch to
		ATN45.

		Talk command for us.
	$E89F	Set the talker active flag, TLKACT ($7A)
		to $01, the listener active flag, LSNACT
		($79) to $00, and branch to ATN95.

ATN45	$E8A9	Compare the command byte (in .A) with
		our listen address in LSNADR ($77). If
		this is not our listen address, branch
		to ATN50.

		Listen command for us.
	$E8AD	Set the listener active flag, LSNACT
		($79) to $01, the talker active flag,
		TLKACT ($7A) to $00, and branch to ATN95

ATN50	$E8B7	Save the command byte by transferring it
		from .A to .X.
	$E8B8	Test if the command byte is a secondary
		address by AND'ing it with $60. If the
		result is not $60, this is not a
		secondary address so branch to ATN120.
		NOTE: SA = $60 + N

		A secondary address for the drive.
	$E8BE	Transfer the original command byte from
		.X back into .A.
	$E8BF	Store the original secondary address
		byte into ORGSA ($84).
	$E8C1	AND the secondary address (in .A) with
		$0F to strip off any junk and store the
		result as the current secondary address
		in SA ($83).

		Test if this is a CLOSE command for this
		secondary address.
	$E8C5	Load .A with the original secondary
		address from ORGSA ($84). AND this value
		with $F0 to mask off the low nybble. If
		the result is not $E0, this is not a
		CLOSE command so branch to ATN122.

		CLOSE the file with this SA.
	$E8CD	Clear the interrupt flag (CLI) to enable
		interrupts.
	$E8CE	JSR to CLOSE ($DAC0) to close the file.
		-----------------------------------------
		WARNING: CLOSE routine does not return
		in time to be handled by ATN122
		-----------------------------------------
	$E8D1	Set the interrupt flag (SEI) to prevent
		any interrupts.
ATN95	$E8D2	Test if the ATN signal is still present.
		If it is, branch back to ATN30.

		ATN SIGNAL GONE - CARRY OUT COMMAND
ATSN20	$E8D7	Store $00 in ATNMOD ($7D) to clear the
		attention mode.
	$E8DB	Release the ATN ACK line by loading the
		byte from port B, PB ($1800), AND'ing
		it with $EF ($FF-ATNA), and storing the
		result back into port B ($1800).
	$E8E3	Test the listener active flag, LSNACT
		($79) to se if we are supposed to be a
		listener. If flag is $00, branch to
		ATN100.

		BE AN ACTIVE TALKER.
	$E8E7	JSR to DATHI ($E99C) to free data line.
		serial bus.
	$E8EA	JMP to IDLE ($EBE7).

ATN100	$E8ED	Test the talker active flag, TLKACT($7A)
		to see if we are supposed to talk. If
		flag is $00, branch to ATN110.

		BE AN ACTIVE TALKER.
	$E8F1	JSR to DATHI ($E99C) to free data line.
	$E8F4	JSR to CLKLOW ($E9AE) to pull clock low.
	$E8F7	JSR to TALK ($E909) to talk on the bus.
ATN110	$E8FA	JMP to ILERR ($EA4E) to release all the
		lines and shift to idle mode.

		FIX SO DEVICE NOT PRESENT IS REPORTED
ATN120	$E8FD	Store $10 in PB ($1800) to kill all the
		lines except ATN ACK (ATN ACKnowledge).
ATN122	$E902	Test if ATN signal is still present (bit
		7 of PB set). If gone, branch to ATNS20.
		If still present, loop to ATN122.
		-----------------------------------------
		SERIAL BUS TALK ROUTINES
		-----------------------------------------
TALK	$E909	Set the interrupt flag (SEI) to prevent
		any interrupts.
	$E90A	JSR to FNDRCH ($D0EB) to find an unused
		read channel. If no channel is available
		branch to NOTLK to exit.
TALK1	$E90F	Load .X with the current channel number
		from LINDX ($82).
	$E911	Load .A with the channel status from
		CHNRDY,X ($F2,X). If bit 7 is set, the
		status is OK so branch to TLK05.

NOTLK	$E905	Terminate routine with an RTS.

		NOTE: CODE ADDED TO FIX VERIFY ERROR
TLK05	$E906	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E909	JSR to DEBNC ($E9C0) to test if the
		clock signal is gone. NOTE: this must be
		80 microseconds or more from JMP TALK1.
	$E91C	AND the data byte in .A with $01 and
		save it on the stack.
	$E91F	JSR to CLKHI ($E9B7) to set the clock
		line high.
	$E922	Pull the test byte off the stack. If it
		is $00, this is a VERIFY ERROR so branch
		to TLK02 to send an EOI.
TALK2	$E925	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E928	JSR to DEBNC ($E9C0) to test if the
		data line has been set low.
	$E92B	AND the test byte (in .A) with $01.
		If the result is not $00, the line has
		not been set hi (no response) so branch
		back to TALK2 to wait for response.

	$E92F	Load .X with the current channel number
		from LINDX ($82).
	$E931	Load .A with the channel status from
		CHNRDY,X ($F2,X), and AND it with $08 to
		test if we have an EOI condition. If the
		result is not $00, we do not have an
		EOI so branch to NOEOI ($E94B).

		Send an EOI signal to the C-64 by:
TLK02	$E937	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E93A	JSR to DEBNC ($E9C0) to send an EOI and
		test if the data line has been set.
	$E93D	AND the test byte (in .A) with $01.
		If the result is not $00, the line has
		not been set hi (no response) so branch
		back to TLK02 to wait for hi response.
TLK03	$E941	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E944	JSR to DEBNC ($E9C0) to test if the
		data line has been set.
	$E947	AND the test byte (in .A) with $01.
		If the result equals $00, the line has
		not been set lo (no response) so branch
		back to TLK02 to wait for lo response.

NOEOI	$E94B	JSR to CLKLOW ($E9AE) to set the clock
		line low.
	$E94E	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E951	JSR to DEBNC ($E9C0) to test if the
		data line has been set.
	$E954	AND the test byte (in .A) with $01.
		If the result is not $00, the line has
		not been set hi (no response) so branch
		back to NOEOI to wait for hi response.
	$E958	Store $08 in CONT ($98) to set up the
		bit counter.
ISR01	$E95C	JSR to DEBNC ($E9C0) to let the port
		settle.
	$E95F	AND the test byte (in .A) with $01 to
		be sure the line is hi before we send.
		If the result is not $00, the line has
		not been set hi (no response) so branch
		to FRMFRX($E999) to wait for hi response
ISR02	$E963	Load .X with the current channel number
		from LINDX ($82).
	$E965	Load .A with the channel data byte from
		CHNDAT,X ($F2,X). Rotate the status byte
		one bit right (ROR) and store the result
		back into CHNDAT,X ($F2,X).
	$E96C	If the carry bit is set, branch to ISRHI
		to send a 1.
	$E96E	JSR to DATLOW ($E9A5) to send a O.
	$E971	Branch to ISRCLK to clock it.

ISRHI	$E973	JSR to DATHI ($E99C) to send a 1.

ISRCLK	$E976	JSR to CLKHI ($E9B7) to set. the clock
		line hi. (rising edge).
	$E979	Load .A with the speed flag from
		DRVTRK+1 ($23). If the flag is not $00,
		no slow down is required so branch to
		ISR03.
	$E97B	JSR to SLOWD ($FEF3) to slow down the
		data transmission.
ISR03	$E980	JSR to CLKDAT ($FEFB) to pull the clock
		low and release the data.
	$E983	Decrement the bit count in CONT ($98).
		If the count is not $00, there are more
		bits to send from this byte so branch
		back to ISR01.
ISR04	$E987	JSR to DEBNC ($E9C0) to test if the
		data line has been set.
	$E98A	AND the test byte (in .A) with $01.
		If the result equals $00, the line has
		not been set lo (no response) so branch
		back to ISR04 to wait for lo response.
	$E991	Clear the interrupt flag (CLI) to
		allow interrupts in preparation for
		sending the next byte.
	$E992	JSR to GET ($D3AA) to get the next
		data byte to send.
	$E995	Set the interrupt flag (SEI) to prevent
		any interrupts.
	$E996	JMP to TALK1 to keep on talking.
		-----------------------------------------
		TALK SUBROUTINES:
		-----------------------------------------
FRMERX	$E999	JMP to FRMERR ($EA4E) to release all
		lines and go to idle mode.
		-----------------------------------------
		Set data out line high.
DATHI	$E99C	Load .A with the byte from port B, PB
		($1800), AND it with $FD ($FF-DATOUT),
		and store the result back in PB ($1800).
	$E9A4	Terminate routine with an RTS.
		-----------------------------------------
		Set data out line lo.
DATLOW	$E9A5	Load .A with the byte from port B, PB
		($1800), OR it with $02 (DATOUT), and
		store the result back in PB ($1800).
	$E9AD	Terminate routine with an RTS.
		-----------------------------------------
		Set clock line lo.
CLKLOW	$E9AE	Load .A with the byte from port B, PB
		($1800), OR it with $08 (CLKOUT), and
		store the result back in PB ($1800).
	$E9B6	Terminate routine with an RTS.
		-----------------------------------------
		Set clock line hi.
CLKHI	$E9B7	Load .A with the byte from port B, PB
		($1800), AND it with $F7 ($FF-CLKOUT),
		and store the result back in PB ($1800).
	$E9BF	Terminate routine with an RTS.
		-----------------------------------------
		Wait for response on bus.
DEBNC	$E9C0	Load .A with the byte from port. B, PB
		($1800). Compare the old port value (.A)
		with the current value of PB ($1800). If
		there is no change, branch to DEBNC.
	$E9C8	Terminate routine with an RTS.
		-----------------------------------------
		SERIAL BUS LISTEN ROUTINES
		-----------------------------------------
ACPTR	$E9C9	Store $08 in CONT ($98) to set up the
		bit counter.
ACP00A	$E9CD	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E9D0	JSR to DEBNC ($E9C0) to test if the
		clock line has been set.
	$E9D3	AND the test byte (in .A) with $04.
		If the result is not $00, the line has
		not been set hi (no response) so branch
		back to ACP00A to wait for hi response.
	$E9D7	JSR to DATHI ($E99C) to make data line
		high.
	$E9DA	Store $01 in T1HC1 ($1805) to set up
		for a 255 microsecond delay.
ACP00	$E9DF	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$E9E2	Load .A with the interrupt flag register
		from IFR1 ($180D) and AND the test byte
		with $40. If the result is NOT $00, the
		time has run out so it MUST be an EOI.
		Since it is an EOI, branch to ACP00B.
	$E9E9	JSR to DEBNC ($E9C0) to test if the
		clock line has been set.
	$E9EC	AND the test byte (in .A) with $04.
		If the result is $00, the clock line has
		not been set lo (no response) so branch
		back to ACP00 to wait for lo response.
		If the result is not $00, the line has
		been set lo so branch to ACP01 to go on.

ACP00B	$E9F2	JSR to DATLOW ($E9A5) to set data line
		low as a response.
	$E9F5	Load .X with $0A, and loop to count .X
		down to $00 to delay for talker turn
		around time.
	$E9FA	JSR to DATHI ($E99C) to make data line
		high.
ACP02A	$E9FD	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$EA00	JSR to DEBNC ($E9C0) to test if the
		clock line has been set.
	$EA03	AND the test byte (in .A) with $04.
		If the result is $00, the clock line has
		not been set lo (no response) so branch
		back to ACP02A to wait for lo response.
	$EA07	Store $00 in EOIFLG ($F8) to indicate
		that an EOI has been received.
ACP01
ACP03	$EA0B	Load .A with the data byte from port B,
		PB ($1800), EOR it with $01 to find the
		complement of the data bit, shift the
		data bit into the carry flag (LSR). AND
		the result in .A with $02 to test if the
		clock line has been set high to indicate
		valid data. If the result is NOT $00,
		the clock line has not been set hi yet
		so branch back to ACP03 and try again.
	$EA15	Three $EA (NOP) bytes to fill space
		left by speed-up to fix VC20 901229-02
		ROM's.
	$EA18	We have valid data bit in the carry so
		do a rotate right (ROR) on DATA ($85) to
		store the bit into the data byte.
ACP03A	$EA1A	JSR to TSTATN ($EA59) to test for an
		ATN signal.
	$EA1D	JSR to DEBNC ($E9C0) to test if the
		clock line has been set.
	$EA20	AND the test byte (in .A) with $04.
		If the result is $00, the clock line has
		not been set. lo (no response) so branch
		back to ACP03A to wait for lo response.
	$EA24	Decrement the bit counter in CONT ($98).
		If the count is not $00, there are more
		bits to get so branch back to ACP03.
	$EA28	JSR to DATLOW ($E9A5) to set data line
		low as a response.
	$EA2B	Load .A with the data byte from DATA
		($85).
	$EA2D	Terminate routine with an RTS.
		-----------------------------------------
		MAIN LISTEN ROUTINE
		-----------------------------------------
LISTEN	$EA2E	Set interrupt mask (SEI) to prevent any
		interrupts.
	$EA2F	JSR to FNDWCH ($D107) to find an unused
		write channel.  If none available, branch
		to LSN15.
	$EA34	Load .A with the write channel status
		from CHNRDY,X ($F2,X).
	$EA36	Rotate the status byte right (ROR). If
		the carry bit is set, the write channel
		is inactive so branch to LSN30.
LSN15	$EA39	Test if this is an OPEN command by:
		loading .A with the original secondary
		address from ORGSA ($84) and AND'ing it
		with $F0. If the result is $F0, it is
		an OPEN command so branch to LSN30.
	$EA41	Not an active channel so JMP to ILERR
		($EA4E) to abort.
LSN30	$EA44	JSR to ACPTR ($E9C9) to get a data byte.
	$EA47	Clear interrupt mask (CLI) to allow
		interrupts.
	$EA48	JSR to PUT ($CFB7) to put the data byte
		into its proper place (DATA, EOI, SA).
	$EA4B	JMP to LISTEN ($EA2E) to keep on
		listening.
		--------------------------------------
FRMERR		Release all bus lines and go idle:
ITERR	$EA4E	Store $00 into port B, PB ($1800) and
ILERR		JMP to IDLE ($EBE7).
		--------------------------------------
		LISTEN SUBROUTINES
		--------------------------------------
ATNLOW	$EA56	JMP to ATNSRV ($E85B) to service ATN
		request.
		--------------------------------------
		Test if in ATN mode:
TSTATN	$EA59	Load .A with the attention mode flag
		from ATNMOD ($7D). If $00, we are not
		in attention mode so branch to TSTA50.
	$EA5D	We are in attention mode. Load .A with
		the byte from port B, PB ($1800). If
		bit 7 of this byte is clear, the ATN
		signal is gone so branch to TATN20 to
		do what we were told. 
TSTRTN	$EA62	The ATN signal hasn't gone away yet so
		exit with an RTS.

TSTA50	$EA63	We are not in attention mode now. Load
		.A with the byte from port B, PB ($1800)
		If bit 7 of this byte is clear, there is
		no ATN signal present so branch to
		TSTRTN to exit.
		If bit 7 of this byte is set, there is
		an ATN signal present so JMP to ATNSRV
		($E85B) to service the ATN request.

TATN20	$EA6B	JMP to ATNS20 ($E8D7) to carry out the
		attention command.
		--------------------------------------
		FLASH LED TO SIGNAL ERROR
		--------------------------------------
		No-error status:
PEZRO	$EA6E	Load .X with $00.
	$EA70	.BYTE $2C skips next two bytes.

		Error status: 
PERR	$EA71	Load .X with the error number from TEMP
		($6F).
	$EA73	Transfer the error number from .X into
		the stack pointer to use the stack as a
		storage register.
PE20	$EA74	Transfer the value of the stack pointer
		(the error number) into .X
PE30	$EA75	Load .A with $08 (the LED mask), OR it
    		with the data port controlling the LED's
		LEDPRT ($1000). and JMP to PEA7A ($FEEA)
		to turn on LED. NOTE: this is a patch to
		be sure the data direction register for
		the LED line is set to output.
REA7D	$EA7D	Transfer the byte in .Y to .A
PD10	$EA7E	Clear the carry flag.
PD20	$EA7F	Add $01 to the contents of .A. If the
		result is not $00, branch to PD20.
	$EA83	Decrement .Y (the hi byte of the timer).
		If value of .Y is not $00, branch to
		PD10.

		Turn off LED(s).
	$EA86	Load .A with the byte from the data port
		controlling the LED, LEDPRT ($1000). AND
		the byte with $F7 ($FF - LED mask) and
		store the result back into LEDPRT($1C00)
		to turn OFF the LED.
PE40	$EA8E	Transfer the byte in .Y to .A
PD11	$EA8F	Clear the carry flag.
PD21	$EA90	Add $01 to the contents of .A. If the
		result is not $00, branch to PD21.
	$EA94	Decrement .Y (the hi byte of the timer).
		If value of .Y is not $00, branch to
		PD11.
	$EA97	Decrement the count in .X. If the result
		is greater than or equal to $00, branch
		to PE30 to flash again.
	$EA9A	Compare .X to $FC to see if we have
		waited long enough between groups of
		flashes. If .X <> $FC branch to PE40 to
		wait some more. If .X = $FC, branch to
		PE20 to repeat the sequence.
		--------------------------------------
		INITIALIZATION OF DISK
		--------------------------------------
DSKINT	$EAA0	Set the interrupt flag (SEI) to prevent
		interrupts.
	$EAA1	Clear the decimal mode flag (CLD).
	$EAA2	Store $FF into the data direction
		register DDRA1 ($1803).
	$EAA7	Load .X and .Y with $00.

		Fill zero page with ascending pattern
PV10	$EAAC	Transfer the byte from .X into .A.
	$EAAD	Store the byte from .A into $00,X.
	$EAAF	Increment .X. If .X is not $00, branch
		back to PV10.

		Check zero page bits.
PV20	$EAB2	Transfer the byte from .X into .A.
	$EAB3	Compare the byte in .A with $00,X.
		If no match, branch to PEZRO ($EA6E).
PV30	$EAB7	Increment the contents of $00,X by 1.
	$EAB9	Increment .Y. If .Y is not $00, branch
		back to PV30.
	$EABC	Check if $00,X equals byte in .A. If no
		match, something is wrong so branch to
		PEZRO ($EA6E).
	$EAC0	Store the $00 byte from .Y into $00,X.
	$EAC2	Check if $00,X equals $00. If it does
		not, something is wrong so branch to
		PEZRO ($EA6E).
	$EAC6	Increment the counter in .X. If the
		result is not $00, we have more of zero
		page to check so branch back to PV20.

		Test the two 64K bit ROM's.
RM10	$EAC9	Increment TEMP ($6F) to set the next
		error number ($01=$E/F;$02=$C/D ROM).
	$EACB	Store .X value (page number) into IP+l
		($76) as the hi byte of the pointer.
	$EACF	Set lo byte of pointer, IP ($75) to $00.
	$EAD1	Set .Y to $00 and .X to $20 (32 pages).
	$EAD4	Clear the carry flag.
RT10	$EAD5	Decrement the hi byte of the pointer in
		IP+l ($76) and we'll do it backwards.
RT20	$EAD7	Add the ROM value from (IP),Y to the
		contents of .A, increment the Y pointer,
		and if .Y is not $00, branch back to
		RT20 to do another byte from this page.
	$EADC	Decrement .X (page count). If the page
		count is not zero, branch to RT10 to do
		the next page of the ROM.
	$EADF	Add $00 to .A to add in the last carry.
	$EAE1	Transfer the checksum from .A to .X.
	$EAE2	Compare the checksum in .A with the
		hi byte of the count in IP+1 ($76). If
		the bytes do not match, branch to PERR2
		($EB1F). $E/F ROM: checksum = $E0
		$C/D ROM: checksum = $C0
	$EAE6	Compare checksum in .X with $C0 to check
		if we are done. If not, branch to RM10.

		Test the disk RAM.
CR20	$EAEA	Load .A with $01 (start of first block).
CR30	$EAEC	Save contents of .A (page number) into
		IP+1 ($76) as hi byte of pointer.
	$EAEE	Increment TEMP ($6F) to bump the error
		number ($03=RAM problem)
RAMTST	$EAF0	Load .X with $07 (number of RAM pages).
RA10	$EAF2	Transfer .Y value to .A and clear carry.
	$EAF4	Add the hi byte of the pointer, IP+l
		($76) to the accumulator and store the
		result in (IP,Y).
	$EAF8	Increment. .Y and if .Y is not $00,
		branch to RA10 to fill RAM page.
	$EAFB	Increment the hi byte of the pointer in
		IP+1 ($76) and decrement the page count
		in .X. If .X is not $00, we have more
		pages to do so branch back to RA10.
	$EB00	Load .X with $07 (number of RAM pages).
RA30	$EB02	Decrement the hi byte of the pointer in
		IP+l ($76). We'll check backwards.
RA40	$EB04	Decrement .Y, transfer the .Y value into
		.A and clear the carry.
	$EB07	Add the hi byte of the pointer, IP+1
		($76) to the accumulator and compare the
		result with (IP,Y). If they don't match,
		branch to PERR2 to report the error.
	$EB0D	EOR the contents of .A with $FF to flip
		the bits and store the result into the
		RAM at (IP),Y.
	$EB11	EOR the contents of .A with (IP),Y and
		store the result (should be $00) back
		into (IP),Y. If the result. is not $00,
		branch to PERR2 to report the error.
	$EB17	Transfer the contents of .Y into .A. If
		.Y is not $00, we have more to do on
		this page so branch back to RA40.
	$EB1A	Decrement the page count in .X. If there
		are more pages to do, branch to RA30.
	$EB1D	Branch to DIAGOK.

PERR2	$EB1F	JMP to PERR ($EA71) to report error.

DIAGOK	$EB22	Load .X with $45 and transfer this value
		to the stack pointer to reset the stack.
	$EB25	Load .A with the byte from the LED
		control port, LEDPRT ($1C00), AND it
		with $F7 ($FF-LED mask) and store the
		result back in LEDPRT to turn off LED.
	$EB2D	Store $01 in PCR1 ($180C) to cause
		interrupt. on the negative edge of ATN.
	$EB32	Store $82 (10000010) in IFR1 ($180D)
		and IER1 ($180E).
		-----------------------------------------
		COMPUTE DEVICE # FROM BITS 5/6 OF PORT B
		-----------------------------------------
	$EB3A	Load .A with the data byte from Port B,
		PB ($1800). AND the byte with $60
		(%01100000). Do one ASL and three ROL's
		to convert from bits 6/5 to bits 1/0.
		NOTE: OXX00000 becomes 000000XX

	$EB43	OR .A with $48 (the talk address) and
		store the result in TLKADR ($78).
	$EB43	EOR .A with $60 (the listen address) and
		store the result in LSNADR ($77).
		-----------------------------------------
		Initialize buffer pointer table
INTTAB	$EB4B	Zero .X and .Y
INTT1	$EB4F	Zero .A and store the $00 byte in .A in
		the buffer table at BUFTAB,X ($99,X).
	$EB53	Increment .X and load .A with the hi
		byte of the pointer to the buffer from
		BUFIND,Y ($FEE0) and store it into the
		buffer table at BUFTAB,X ($99,X).
	$EB59	Increment .X and .Y and compare the new
		value of .Y with $05 (the number of
		buffers). If there are more buffers to
		do, branch to INTT1.

	$EB5F	Store the lo byte of the pointer to the
		command buffer ($00) into the buffer
		table at BUFTAB,X ($99,X). Increment .X.
	$EB64	Store the hi byte of the pointer to the
		into the buffer
		command buffer ($02)
		table at BUFTAB,X ($99,X). Increment .X.
	$EB69	Store the to byte of the pointer to the
		error buffer ($D5) into the buffer table
		table at BUFTAB,X ($99,X). Increment .X.
	$EB6E	Store the hi byte of the pointer to the
		error buffer ($02) into the buffer table
		table at. BUFTAB,X ($99,X). Increment .X.
	$EB72	Load .A with $FF (inactive SA) and .X
		with $12 (the maximum secondary address)
DSKIN1	$EB76	Loop to set all LINTAB,X ($022B,X)
		values to $FF to indicate inactive.
	$EB7C	Load .X with $05 (the maximum number of
		channels - 1).
DSKIN2	$EB7E	Loop to set all BUFO,X ($A7,X), BUF1,X
		($AE,X) and SS,X (CD,X) values to $FF to
		indicate that these buffers are unused.

	$EB87	Store $05 (the buffer count) into
		BUFO+CMDCHN ($AB)
	$EB8B	Store $05 (the buffer count + 1) into
		BUFO+ERRCHN ($AC)
	$EB8F	Store $FF into BUFO+BLINDX ($AD)
	$EB93	Store $FF into BUFI+BLINDX ($B4)
	$EB95	Store $05 (the error channel #) into
		LINTAB+ERRSA ($023B).
	$EB9A	Store $84 ($80 + the command channel #)
		into LINTAB+CMDSA ($023A).
	$EB9F	Store $0F (LINDX 0 to 5 free) into
		LINUSE ($0256).
	$EBA4	Store $01 (ready to listen) into
		CHNRDY+CMDCHN ($F6).
	$EBA8	Store $01 (ready to talk) into
		CHNRDY+ERRCHN ($F7).
	$EBAC	Store $E0 into BUFUSE ($024F) and $FF
		into BUFUSE+1 ($0250).
	$EBB6	Store $01 into WPSW ($1C) and WPSW+1
		($1D) to set up the write protect status
	$EBBC	JSR to USRINT ($CB63) to initialize the
		user jump table.
	$EBBF	JSR to LRUINT ($CEFA) to initialize the
		least recently used table.
	$EBC2	JSR to CNTINT ($F259) to initialize the
		disk controller.
	$EBC5	Set up the indirect NMI vector at VNMI
		($65/6) to point to the diagnostic
		routine, DIAGOK ($EB22).
	$EBCD	Store $0A into SECINC ($69) as the
		normal next sector increment. as the
	$EBD1	Store $05 into REVCNT ($6A)
		normal recovery counter.
SETERR	$EBD5	Load .A with $73 and JSR to ERRTSO
		($E6C1) to set up power-on error message
		73 CBM DOS V2.6 1541 0 0
	$EBDA	Load .A with $1A (%00011010) and store
		it in the data direction register DDRB1
		($1802). ATNA,CLKOUT,DATOUT are outputs.
	$EBDF	Store $00 in data port B, PB ($1800) to
		CLOCK, & ATNA lines high.
		set DATA,
	$EBE4	JSR to BOOT ($E780) to see if we need
		to boot a systems routine.

		-----------------------------------------
		IDLE LOOP. WAIT FOR SOMETHING TO DO.
		-----------------------------------------
IDLE	$EBE7	Clear interrupt mask (CLI) to allow
		interrupts. 
		Release all the bus lines:
	$EBE8	Load .A with the byte from port B, PB
		($1800), AND it with $E5 to set CLOCK,
		DATA, and ATNA lines high, and store the
		result back in PB ($1800). 
	$EBF0	Check the value of CMDWAT ($0255) to see
		if there is a command waiting. If it is
		$00, there is none waiting so branch to
		IDL1.
	$EBF5	Store $00 in CMDWAT ($0255) to clear the
		command waiting flag.
	$EBFA	Store $00 in NMIFLG ($67) to clear the
		debounce. then
	$EBFC	JSR to PARSXQ ($C146) to parse and
		execute the command.
IDL1	$EBFF	Clear interrupt mask (CLI) to allow
		interrupts.
	$EC00	Check the value of ATNPND ($0255) to see
		if there is an attention pending. If it
		is $00, there is nothing pending (such
		as the drive running or an open file)
		so branch to IDL01.
	$EC04	JMP to ATNSRV ($E85B) to service the
		attention request.
IDL01	$EC07	Clear interrupt mask (CLI) to allow
		interrupts.
	$EC08	Store $0E (#14), the maximum secondary
		address for files in TEMP+3 ($72).
	$EC0C	Zero TEMP ($6F) and TEMP+l ($70).
IDL02	$EC12	Load .X with the secondary address
		counter from TEMP+3 ($72).
	$EC14	Load .A with the channel number for this
		secondary address from LINTAB,X($022B,X)
		If it is $FF, there is no active file
		for this SA so branch to IDL3.
	$EC1B	We've found an active file so AND the
		channel number with $3F and store the
		result as the current channel number in
		LINDX ($82).
	$EC1F	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
	$EC22	Transfer the buffer number from .A to .X
	$EC23	Determine which drive is to be used by
		loading the old job number from LSTJOB,X
		($025B,X), AND'ing it with $01, and
		transferring the result into .X.
	$EC29	Increment the count of the number of
		active files on drive X in TEMP,X($6F,X)
IDL3	$EC2B	Decrement the SA count in TEMP+3 ($72).
		If there are more secondary addresses
		left to check, branch back to IDL2.
	$EC2F	Load .Y with $04 (the number of buffers
		less 1).
IDL4	$EC31	Load .A with the current job code for
		this buffer from the job queue, JOBS,Y
		($00,Y). If bit 7 is not set, no job is
		in progress so branch to IDL5.
	$EC36	There is a job in progress so AND the
		job code in .A with $01 to mask off the
		non-drive bits and transfer the result
		to .X.
	$EC39	Increment the count of the number of
		active files on drive X in TEMP,X($6F,X)
IDL5	$EC3B	Decrement the buffer counter in .Y. If
		there are more buffers to check, branch
		to IDL4.
	$EC3E	Set the interrupt mask (SEI) to prevent
		interrupts while reading LEDPRT ($1000).
	$EC3F	Load .A with the data byte from the
		port controlling the LED, AND the byte
		with $F7 ($FF - LED mask), and save the
		result onto the stack.
	$EC45	Load .A with the current drive number
		from DRVNUM ($7F) and save it in RO($86)
	$EC49	Zero DRVNUM ($7F).
	$EC4D	Test the active file count for drive 0
		in TEMP ($6F). If $00, branch to IDL7.
	$EC51	Load the write protect switch byte from
		WPSW ($1C). If it is $00 branch to IDL6.
	$EC55	JSR to CLDCHN ($D313) to close all files
IDL6	$EC58	Pull the LED data byte off the stack,
		OR it with $08 (LED mask) to turn on the
		LED since drive 0 is active, and save
		the byte back onto the stack.
IDL7	$EC5C	Increment the DRVNUM ($7F). (to $01)
	$EC5E	Test the active file count for drive 1
		in TEMP+1 ($70). If $00, branch to IDL9.
	$EC62	Load the write protect switch byte from
		WPSW ($1C). If it is $00 branch to IDL8.
	$EC66	JSR to CLDCHN ($D313) to close all files
IDL8	$EC69	Pull the LED data byte off the stack,
		OR it with $00 (LED mask) to turn on the
		LED since drive 1 is active, and save
		the byte back onto the stack.
IDL9	$EC6D	Copy the original drive number from
		RO ($86) back into DRVNUM ($7F).
	$EC71	Pull the LED data byte off the stack.
	$EC72	Load .X with the error status from
		ERWORD ($026C). If it is $00, the LED
		is not flashing so branch to IDL12.

		Error light is flashing:
	$EC77	Load .A with the LED data byte from
		LEDPRT ($1C00)
	$EC7A	Compare the error status in .X with $80.
		If it is not $80, this is not the first
		time we have seen this error so branch
		to IDL 10.
	$EC7E	We have just encountered a new error
		status so JMP to IDL11.

IDL10	$EC81	Load .X with the value of TIMER1 ($1805)
		If bit 7 is set, we are still timing so
		branch to IDL12.
	$EC86	Store $A0 into TIMER1 ($1805) to set the
		timer to a new 8 millisecond cycle.
IDL11	$EC8B	Decrement the count of 8 millisecond
		cycles in ERWORD ($026C). If the count
		is not $00 yet, branch to IDL12
	$EC90	Time is up. FOR the LED status in .A
		with the LED mask in ERLED ($026D) to
		toggle the LED.
	$EC93	Store $10 in ERWORD ($026C) to start a
		new timing cycle.
IDL12	$EC98	Store the current LED status (in .A)
		into the LED port, LEDPRT ($1000).
	$EC9B	JMP to IDL1 ($EBFF) the top of the loop.
		-----------------------------------------
		Start loading the directory:
STDIR	$EC9E	Set current secondary address, SA ($83)
		to $00.
	$ECA2	Load .A with $01 and JSR to GETRCH
		($D1E2) to allocate a channel and one
		buffer.
	$ECA7	Zero .A and JSR to SETPNT ($D4C8) to
		set the buffer pointer to the start of
		the buffer.
	$ECAC	Load .X with the channel number from
		LINDX ($82).
	$ECAE	Store $00 as the last character for this
		channel in LSTCHR,X ($0244).
	$ECB3	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
	$ECB6	Transfer the buffer number into .X
	$ECB7	Load .A with the current drive number
		from DRVNUM ($7F) and store this number
		as the last job number for this buffer
		in LSTJOB,X ($025B).
	$ECBC	Load .A with $01 and JSR to PUTBYT
		($CFFl) to put the lo byte of the load
		address ($0401) into the buffer.
	$ECC1	Load .A with $04 and JSR to PUTBYT
		($CFF1) to put the hi byte of the load
		address ($0401) into the buffer.
	$ECC6	Load .A with $01 and JSR to PUTBYT
		($CFFl) twice to put a phony program
		line link ($0101) into the buffer.
	$ECCE	Load .A with the drive number for the
		directory from NBTEMP ($0272) and JSR to
		PUTBYT ($CFF1) to put this to the buffer
		as the lo byte of the first line number.
	$ECD4	Load .A with $00 and JSR to PUTBYT
		($CFF1) to store this as the hi byte of
		the line number.
	$ECD9	JSR to MOVBUF ($ED59) to move the disk
		name into the buffer.
	$ECDC	JSR to GETACT ($DF93) to get the active
		buffer number (returned in .A).
	$ECDF	Multiply the buffer number by 2 (ASL)
		and transfer it into .X.
	$ECE1	Decrement the lo byte of the pointer in
		BUFTAB,X ($$99,X) twice.
	$ECE5	Load .A with $00 and JSR to PUTBYT
		($CFFl) to store this as the end of
		program line null byte.
DIR1	$ECEA	Load .A with $01 and JSR to PUTBYT
		($CFF1) twice to put a phony program
		line link ($0101) into the buffer.
	$ECF2	JSR to GETNAM ($C6CE) to get the buffer
		number and file name. If the carry flag
		is clear on return, this is the last
		entry so branch to DIR3.
	$ECF7	Load .A with the lo byte of the block
		count from NBTEMP ($0272) and JSR to
		PUTBYT ($CFF1) to put this to the buffer
		as the lo byte of the line number.
	$ECFD	Load .A with the hi byte of the block
		count from NBTEMP+1 ($0273) and JSR to
		PUTBYT ($CFF1) to put this to the buffer
		as the hi byte of the line number.
	$ED03	JSR to MOVBUF ($ED59) to move the file
		name and file type into the buffer.
	$ED06	Load .A with $00 and JSR to PUTBYT
		($CFF1) to store this as the end of
		program line null byte.
	$ED0B	If the Z flag is not set on return, the
		buffer is not full so branch to DIR1 to
		do the next file entry.
DIR10	$ED0D	JSR to GETACT ($DF93) to get the active
    		buffer number (returned in .A).
	$ED10	Multiply the buffer number by 2 (ASL)
		and transfer it into .X.
	$ED12	Store $00 as the lo byte of the pointer
		in BUFTAB,X ($$99,X).
	$ED16	Load .A with $88 (ready-to-talk).
	$ED18	Load .Y with the channel number from
		LINDX ($82).
	$ED1A	Store $88 (in .A) into the directory
		list flag DIRLST ($0254) to indicate
		that the directory list is full.
	$ED1D	Store $88 (in .A) as  the channel status
		in CHNRDY,Y ($00F2,Y).
	$ED20	Load .A with the byte from DATA ($85).
	$ED22	Terminate routine with an RTS.
		-----------------------------------------
		End directory loading:
DIR3	$ED23	Load .A with the lo byte of the block
		count from NBTEMP ($0272) and JSR to
		PUTBYT ($CFFl) to put this to the buffer
		as the lo byte of the line number.
	$ED29	Load .A with the hi byte of the block
		count from NBTEMP+1 ($0273) and JSR to
		PUTBYT ($CFF1) to put this to the buffer
		as the hi byte of the line number.
	$ED2F	JSR to MOVBUF ($ED59) to move the file
		name and file type into the buffer.
	$ED32	JSR to GETACT ($DF93) to get the active
    		buffer number (returned in .A).
	$ED35	Multiply the buffer number by 2 (ASL)
		and transfer it into .X.
	$ED37	Decrement the lo byte of the pointer in
		BUFTAB,X ($$99,X) twice.
	$ED3B	Load .A with $00 and JSR to PUTBYT
		($CFFl) three times to store the three
		null bytes at the end of a program.
	$ED46	JSR to GETACT ($DF93) to get the active
    		buffer number (returned in .A).
	$ED49	Multiply the buffer number by 2 (ASL)
		and transfer it into .Y.
	$ED4B	Load .A with the lo byte of the pointer
		into the buffer from BUFTAB,Y ($0099,Y).
	$ED4E	Load .Y with the channel number from
		LINDX ($82).
	$ED50	Store the lo byte of the pointer (in .A)
		into the lo byte of the pointer to the
		last non-zero character in the buffer
		LSTCHR,X ($0244,X).
	$ED53	Decrement the pointer in LSTCHR,X
		($0244,X) by 1 so it does actually point
		to the last character in the buffer.
	$ED56	JMP to DIR10 ($ED0D) to set the channel
		status and flags and exit.
		-----------------------------------------
		Transfer file name to listing buffer
MOVBUF	$ED59	Zero .Y
MOVB1	$ED5B	Load .A with the character from NAMBUF,Y
		($02B1,Y) and JSR to PUTBYT ($CFFl) to
		store it in the listing buffer.
	$ED61	Increment .Y. If .Y is not $1B (#27)
		yet, branch to MOVB1.
	$ED66	Terminate routine with an RTS.
		-----------------------------------------
		Get character for directory load
GETDIR	$ED67	JSR to GETBYT ($D137) to get a byte from
		the data buffer (loads next block if
		necessary).
	$ED6A	On return, if the Z flag is set, we are
		at the end-of-file so branch to GETD3.
	$ED6C	Terminate routine with an RTS.

GETD3	$ED6D	Store the byte (in .A) into DATA ($85).
	$ED6F	Load .Y with the channel number from
		LINDX ($82).
	$ED71	Load .A with the lo byte of the pointer
		into the directory buffer from LSTCHR,Y
		($0244,Y)
	$ED74	If the lo byte of the pointer is $00, we
		have exhausted the current buffer so
		branch to GD1.
	$ED76	We must be at the end-of-file so load
		.A with $80 (EOI) and store it as the
		channel status in CHNRDY,Y ($00F2,Y).
	$ED7B	Load .A with the byte from DATA ($85).
	$ED7D	with an RTS.
		Terminate routine
GD1	$ED7E	Save the null byte in .A onto the stack.
	$ED7F	JSR to DIR1 ($ECEA) to create pseudo
		program listing in the listing buffer.
	$ED82	Pull the null data byte off the stack.
	$ED83	Terminate routine with an RTS.
		-----------------------------------------
		VALIDATE (COLLECT) DISK COMMAND
		-----------------------------------------
		Create a new BAM to match the sectors
VALDAT		used by the current directory entries.
VERDIR	$ED84	JSR to SIMPRS ($C1D1) to parse the
		command string and extract the drive #.
	$ED87	JSR to INITDR ($D042) to initialize the
		drive specified.
	$ED8A	Store $40 in WBAM ($02F9) to mark BAM
		as dirty (needs to be written out).
	$ED8F	JSR to NEWMAP ($EEB7) to build a new
	$ED92	blank BAM in RAM.
		Store $00 in DELIND ($0292) to force
		a search for a valid directory entry
		and JSR to SRCHST ($C5AC) to search the
		directory for the first valid entry.
	$ED9A	If an entry is found (Z flag not set),
		branch to VD25 to process it.

		No more entries so finish up.
VD10	$ED9C	Set SECTOR ($81) to $00.
	$EDA0	Set TRACK ($80) with the value $12 (#18)
		from DIRTRK ($FE85).
	$EDA5	JSR to VMKBAM ($EDE5) to trace through
		the directory sectors and mark those in
		use in the BAM.
	$EDA8	Store $00 in WBAM ($02F9) to mark BAM as
		clean (BAM in RAM matches BAM on disk).
	$EDAD	JSR to SCRBAM ($EEFF) to write BAM out
		to disk.
	$EDB0	Terminate command with a JMP to ENDCMD
		($C194).

    		Process directory entry for BAM
VD15	$EDB3	Increment. .Y (points to entry in buffer)
	$EDB4	Load the track link for the entry from
		(DIRBUF),Y; ($94),Y and save it onto
		the stack.
	$EDB7	Increment .Y (points to entry in buffer)
	$EDB8	Load the sector link for the entry from
		(DIRBUF),Y; ($94),Y and save it onto
		the stack.
	$EDBB	Load .Y with $13 so it points to the
		side sector track link of the entry.
	$EDBD	Load the SS track link for the entry
		from (DIRBUF),Y; ($94),Y. If the SS
		track link is $00, this isn't a relative
		file so branch to VD17.
	$EDC1	Store the SS track link in TRACK ($80).
	$EDC3	Increment .Y (points to entry in buffer)
	$EDC4	Load the SS sector link for the entry
		from (DIRBUF),Y; ($94),Y. Store the SS
		sector link in SECTOR ($81).
	$EDC8	JSR to VMKBAM ($EDE5) to trace through
		the SS file and mark the sectors used
		in the BAM.
VD17	$EDCB	Pull the main file's sector link off the
		stack and store it in SECTOR ($81).
	$EDCE	Pull the main file's track link off the
		stack and store it in TRACK ($80).
	$EDD1	JSR to VMKBAM ($EDE5) to trace through
		the main file and mark the sectors used
		in the BAM.
VD20	$EDD4	JSR to SRRE ($C604) to search for the
		next valid directory entry.
	$EDD7	If another entry is not found (Z flag
		is set) branch to VD10 to finish up.

		Check if entry found is properly closed
VD25	$EDD9	Zero .Y so it points to the first
		character in the entry, the file type.
	$EDDB	Load .A with the file type byte from
		(DIRBUF),Y; ($99),Y. If bit 7 is set,
		the file has been properly closed so
		branch to VD15 to process it.

	$EDDF	File was not properly closed so JSR to
		DELDIR ($C8B6) to delete it from the
		directory.
	$EDE2	JMP to VD20 ($EDD4) to find next entry.
		-----------------------------------------
		Trace file by links and mark BAM
VMKBAM	$EDE5	JSR to TSCHK ($D55F) to check that the
		TRACK and SECTOR values are legal.
	$EDE8	JSR to WUSED ($EF90) to mark the sector
		pointed to by TRACK and SECTOR as IN USE
		in the BAM.
	$EDEB	JSR to OPNIRD ($D475) to open the
		internal read channel and read in the
		first one or two file blocks.
MRK2	$EDEE	Load .A with $00 and JSR to SETPNT
		($D4C8) to set the pointers to the first
		byte in the buffer (the track link).
	$EDF3	JSR to GETBYT ($D137) to read the track
		link (in .A). Store it into TRACK ($80).
	$EDF8	JSR to GETBYT ($D137) to read the sector
		link (in .A). Store it into SECTOR ($81)
	$EDFD	Load .A with the track link from TRACK
		($80). If it is not $00, branch to MRK1.
	$EE01	Track link is $00. This must be the last
		block in the file so JMP to FRECHN
		($D227) to free the channel and return.
MRK1	$EE04	JSR to WUSED ($EF90) to mark the sector
		pointed to by TRACK and SECTOR as IN USE
		in the BAM.
	$EE07	JSR to NXTBUF ($D44D) to read in the
		next block of the file.
	$EE0A	JMP to MRK2 ($EDEE) to do next block.
		-----------------------------------------
		NEW (FORMAT) DISK COMMAND
		-----------------------------------------
		A full, or long NEW marks off the tracks
		and sectors on a diskette, writes null
		data blocks in all sectors, and creates
		a new BAM and directory on track 18.
		A short NEW merely creates a new BAM and
		directory on track 18.
NEW	$EE0D	JSR to ONEDRV ($C312) to set up drive
		and table pointers.
	$EE10	Load the number of the drive that was
		set up from FILDRV ($E2). If bit 7 is
		not set, a legal drive number was
		specified so branch to N101 to continue.

	$EE14	Load .A with $33 to indicate a BAD DRIVE
		NUMBER and JMP to CMDERR ($C1C8).

N101	$EE19	AND the drive number (in .A) with $01 to
		mask off the non drive bits and store
		the result as the current drive in
		DRVNUM ($7F).
	$EE1D	JSR to SETLDS ($C100) to turn on the
		drive active LED.
	$EE20	Load .A with the drive number from
		DRVNUM ($7F), multiply it by 2 (ASL),
		and transfer it into .X.
	$EE24	Load .Y with the pointer to the start
		of the new disk ID in the command buffer
		from FILTBL+1 ($027B).
	$EE27	Compare the ID pointer in .Y with the
		length of the command string in CMDSIZ
		($0274). If these values are equal,
		there is no new disk ID. Therefore this
		must be a short new so branch to N108.

	$EE2C	Transfer new disk ID from the command
		buffer CMDBUF,Y ($0200,Y) and CMDBUF+1,Y
		($0201,Y) to the master disk ID area
		DSKID,X ($12,X) and DSKID+1,X ($13,X).

	$EE36	JSR to CLRCHN ($D307) to clear all
		channels while formatting.
	$EE39	Store $01 into TRACK ($80) as first
		track to do.
	$EE3D	JSR to FORMAT ($C8C6) to set up JMP
		command in buffer that points to the
		formatting routine to be used by the
		disk controller.
	$EE40	JSR to CLRBAM ($F005) to clear the BAM.
	$EE43	JMP to N110 ($EE56) to continue.

		Clear directory only.
N108	$EE46	JSR to INITDR ($D042) to init. the drive
	$EE49	Load .X with the drive number from
		DRVNUM ($7F).
	$EE4B	Load .A with the DOS version number
		as given in the BAM, DSKVER,X ($0101,X)
		and compare it with the 1541 DOS version
		number ($41) from VERNUM ($FED5). If the
		version numbers match, branch to N110.
	$EE53	DOS versions do not match so JMP to
		VNERR ($D572) to abort.

N110	$EE56	JSR to NEWMAP ($EEB7) to create a new
		BAM.
	$EE59	Load .A with the current job code from
    		JOBNUM ($F9) and transfer it to .Y.
	$EE5C	Multiply the job code in .A by 2 (ASL)
		and transfer the result to .X.
	$EE5E	Load .A with $90, the offset of the disk
		name in the BAM from DSKNAM ($FE88) and
		store this pointer in BUFTAB,X ($99,X).
	$EE63	Load .X with the buffer number from
		FILTBL ($027A), load .Y with $27 (the
		name length) and JSR to TRNAME ($C66E)
		to transfer the new disk name from the
		command buffer into the BAM area.
	$EE6B	Load .Y with $12 (position of disk ID).
	$EE6D	Load .X with the drive number from
		DRVNUM ($7F) and copy the DOS version
		number ($41) from VERNUM ($FED5) into
		DSKVER,X ($0101,X).
	$EE75	Transfer the drive number from .X to .A,
		multiply it by 2 (ASL), and transfer the
		result back into .X.
	$EE78	Transfer the first disk ID character
		from DSKID,X ($12,X) into (DIRBUF),Y
		($94),Y. Increment .Y.
	$EE7D	Transfer the second disk ID character
		from DSKID+1,X ($13,X) into (DIRBUF),Y
		($94),Y. Increment .Y twice.
	$EE83	Store the directory DOS version ($32;
		ASCII 2) into (DIRBUF),Y; ($94),Y.
	$EE87	Increment .Y.
	$EE88	Transfer the format type ($41; ASCII A)
		from VERNUM ($FED5) into (DIRBUF),Y
	$EE8D	($94),Y.
		Load .Y with $02 so it points to the
		third byte in the BAM and store the
		format type ($41; in .A) into the BAM
		at (BMPNT),Y; ($6D),Y.
	$EE91	Transfer the directory track number, $12
		from DIRTRK ($FE85) into TRACK ($80).
	$EE96	JSR to USEDTS ($EF93) to mark track 18
		sector 0 as used in the BAM.
	$EE99	Set SECTOR ($81) to $01.
	$EE9D	JSR to USEDTS ($EF93) to mark track 18
		sector 1 as used in the BAM.
	$EEA0	JSR to SCRBAM ($EEFF) to write out the
		new BAM to disk.
	$EEA3	JSR to CLRBAM ($F005) to set all of BAM
		area to $00.
	$EEA6	Load .Y with $01 and store $FF as the
		first directory block's sector link in
		(BMPNT),Y; ($6D),Y.
	$EEAC	JSR to DRTWRT ($D464) to write out the
		new directory block to disk.
	$EEAF	Decrement the sector number (from $01
		$00) in SECTOR ($81) and JSR to DRTRD
		($D460) to read the BAM back into RAM.
	$EEB4	Terminate command with a JMP to ENDCMD
		($C194).
		-----------------------------------------
NEWMPV		Create a new BAM map:
NEWMAP	$EEB7	JSR to CLNBAM ($F0D1) to set entire BAM
    		area to $00's.
	$EEBA	Using .Y as a pointer, store $12 (#18)
		and $01 as the track and sector link in
		(BMPNT),Y; ($6D),Y; as the first two
		bytes of the new BAM.
	$EEC4	Increment .Y until it is $04.
NM10	$EEC7	Zero the area to be used to manipulate
		the BAM map bits, TO ($6F), T1 ($70),
		and T2 ($71).
	$EECF	Transfer the byte from .Y into .A and
		divide it by 4 (2 * LSR) to find the
		track number.
	$EED2	JSR to MAXSEC ($F24B) to calculate the
		maximum sector number for this track
		and store this value as the number of
		sectors free on this track in (BMPNT),Y
		($6D),Y.
	$EED7	Increment .Y. Transfer the maximum
		sector number from .A into .X.
NM20	$EED9	Set the carry flag (this 1 bit will
		indicate that this sector is free) and
		rotate this bit from the carry into
		the bit map area (TO/1/2) using ROL T0,
		ROL T1, and ROL T2.
		       T2 ($71)  T1 ($70)  TO ($6F)  C
		before 00000000  11111111  11111111  1
		after  00000001<-11111111<-11111111<-0
	$EEE0	Decrement the sector count in .X. If the
		resulting .X value is not $00, there are
		more to do so branch back to NM20.
NM30	$EEE3	Transfer the bit map for this track from
		TO,X ($6F,X) to the BAM area (BMPNT),Y;
		($6D,Y). Increment .Y and .X. If the
		new .X value is not $03, we have more
		to transfer so branch back to NM30.
	$EEED	Compare the .Y value to $90. If it is
		less than $90, we have more tracks to
		do so branch back to NM10.
		-----------------------------------------
	$EEF1	JMP to NFCALC ($D075) to calculate the
		number of blocks free.
		-----------------------------------------
		Write out BAM to the drive specified in
		LSTJOB.
MAPOUT	$EEF4	JSR to GETACT ($DF93) to find the active
		buffer number (returned in .A).
	$EEF7	Transfer the buffer number to .X. last
	$EEF8	Load .A with the job code for the it with
		job from LSTJOB,X ($025B,X), AND and
		$01 to mask off the non-drive bits,
		store the result in DRVNUM ($7F).

		Write out BAM to the drive specified in
		DRVNUM.
SCRBAM	$EEFF	Load .Y with the drive number from
		DRVNUM ($7F).
	$EF01	Load .A with the BAM-dirty flag from
		MDIRTY,Y ($0251,Y). If the flag is not
		$00, the BAN, is dirty (the copy in RAM
		does NOT match the copy on disk) so
		branch to SB10 to write it out to disk.
	$EF06	BAM is clean so there is no reason to
		write it out. Terminate routine with
		an RTS.

SB10	$EF07	Zero the BAM-dirty flag in MDIRTY,Y
		($0251,Y).
	$EF0C	JSR to SETBPT ($EF3A) to set up the
		pointer to the BAM.
	$EF0F	Load .A with the drive number from
		DRVNUM ($7F), multiply it by 2 (ASL),
		and save the result onto the stack.
	$EF13	JSR to PUTBAM ($F0A5) to put the memory
    		images to the BAM.
	$EF16	Pull the (drive number x 2) off the
		stack, clear the carry flag, add $01,
		and JSR to PUTBAM ($F0A5) to put the
		memory images to the BAM.

		Verify that the block count for the
		track matches the bit map for the track.
	$EF1D	Load .A from TRACK ($80) and push the
		track number onto the stack.
	$EF20	Load .A with $01 and store it in TRACK.
SB20	$EF24	Multiply the track number in .A by 4
		(2 x ASL) and store the result as the
		lo byte of the buffer pointer in BMPNT
		($6D).
	$EF28	JSR to AVCK ($F220) to check that the
		blocks free for the track agrees with
		the bit map.
	$EF2B	Increment the track count in TRACK ($80)
		If the new count is less than the
		the maximum track number (#36), branch
		back to SB20 to check the next track.
	$EF34	Pull the original track number off the
		stack and restore it into TRACK ($80).
	$EF37	JMP to DOWRIT ($D58A) to write out the
		BAM to disk.
		-----------------------------------------
		Read in the BAM, if not already in RAM,
		and set the pointers to the BAM
SETBPT	$EF3A	JSR to BAM2A ($F10F) to get the BAM
		channel number in .A (dr0 = 6). Transfer
		the channel number into .X.
	$EF3E	JSR to REDBAM ($F0DF) to read in the BAM
		if not already in memory.
	$EF41	Load .X with the buffer number used for
		the read from JOBNUM ($F9).
	$EF43	Set the hi byte of the pointer to the
		BAM in BMPNT+1 ($6E) using the hi byte
		pointer value for the buffer from
		BUFIND,X ($FEE0,X).
	$EF48	Set the lo byte of the pointer to the
		BAM in BMPNT ($6D) to $00.
	$EF4C	Terminate routine with an RTS.
		-----------------------------------------
		Get the number of blocks free on the
		drive specified in DRVNUM:
NUMFRE	$EF4D	Load .X with the drive number from
		DRVNUM ($7F).
	$EF4F	Transfer the lo byte of the number of
		blocks free from NDBL,X ($02FA,X) into
		NBTEMP ($0272).
	$EF55	Transfer the hi byte of the number of
		blocks free from NDBH,X ($02FC,X) into
		NBTEMP+1 ($0273).
	$EF5B	Terminate routine with an RTS.
		-----------------------------------------
		Free the block specified in TRACK and the
		SECTOR as free in the BAM:
WFREE	$EF5C	JSR to FIXBAM ($EFF1) to write out
		BAM the value in WBAM indicates that it
		is needed. 
FRETS	$EF5F	JSR to FREUSE ($EFCF) to calculate the
		index to the BAM entry and that contains
		the desired TRACK SECTOR. On return
		.Y points to the entry and .X points to
		the bit within the entry.
FRETS2	$EF62	Set the carry flag (the flag for no
		action required).
	$EF63	If Z flag is NOT set, the desired TRACK
		and SECTOR is already free in the BAM
		so branch to FRERTS to exit.
	$EF65	Load .A with BAM entry from (BMPNT),Y
		($6D),Y, OR it with the bit map mask
		from BMASK,X ($EFE9,X) to turn on (free)
		the bit that corresponds to the desired
		block, and store the result back into
		(BMPNT),Y; ($6D),Y.
	$ED6C	JSR to DTYBAM ($EF88) to set the dirty
		BAM flag (BAM in RAM and BAM on disk
		do not match).
	$ED6F	Load .Y with the pointer to the number
		of blocks free for the track from TEMP
		($6F) and clear the carry flag.
	$EF72	Load .A with the blocks free for the
		track from (BMPNT),Y; ($6D),Y, add 1,
		and store the result back into (BMPNT),Y
	$EF78	Load .A with the TRACK ($80) number of
		the block we just freed. If it is on the
		directory track (#18), branch to USE10
		($EFBA).
	$EF7F	Increment the lo byte of the count of
		the total number of blocks free on the
		disk, NDBL,X ($02FA,X) by 1. If the
		result is NOT $00, branch to FRERTS
	$EF84	Increment the hi byte of the count of
		the total number of blocks free on the
		disk, NDBH,X ($02FC,X) by 1.
FRERTS	$EF87	Terminate routine with an RTS.
		-----------------------------------------
		Set dirty-BAM flag:
		Indicates that the copy of the BAM in
		disk RAM does not match the disk copy.
DTYBAM	$EF88	Load .X with the current drive number
		from DRVNUM ($7F).
	$EF8A	Store a $01 into the dirty BAM flag in
		MDIRTY,X ($0251).
	$EF8F	Terminate routine with an RTS.
		-----------------------------------------
		Mark the block specified in TRACK and
		SECTOR as USED in the BAM:
WUSED	$EF90	JSR to FIXBAM ($EFF1) to write out the
		BAM the value in WBAM indicates that it
		is needed.
USEDTS	$EF93	JSR to FREUSE ($EFCF) to calculate the
		index to the BAM entry that contains
		the desired TRACK and SECTOR. On return
		.Y points to the entry and .X points to
		the bit within the entry.
	$EF96	If Z flag is set, the desired TRACK and
		SECTOR is already marked as USED in the
		BAM so branch to USERTS to exit.
	$EF98	Load .A with BAM entry from (BMPNT),Y
		($6D),Y, FOR it with the bit map mask
		from BMASK,X ($EFE9,X) to zero (in use)
		the bit that corresponds to the desired
		block, and store the result back into
		(BMPNT),Y; ($6D),Y.
	$ED9F	JSR to DTYBAM ($EF88) to set the dirty
		BAM flag (BAM in RAM and BAM on disk
		do not match).
	$EDA2	Load .Y with the pointer to the number
		of blocks free for the track from TEMP
		($6F).
	$EFA4	Load .A with the blocks free for the
		track from (BMPNT),Y; ($6D),Y, set the
		carry flag, subtract $01, and store the
		result back into (BMPNT),Y.
	$EFAB	Load .A with the TRACK ($80) number of
		the block we just freed. If it is on the
		directory track (#18), branch to USE20
		($EFBD).
	$EFB2	Load .A with the lo byte of the count of
		the total number of blocks free on the
		disk, NDBL,X ($02FA,X). If the lo byte
		is NOT $00, branch to USE10.
	$EFB7	Decrement the hi byte of the count of
		the total number of blocks free on the
		disk, NDBH,X ($02FC,X) by 1.
USE10	$EFBA	Decrement the lo byte of the count of
		the total number of blocks free on the
		disk, NDBL,X ($02FA,X) by 1.
USE20	$EFBD	Load .A with the hi byte of the count of
		the total number of blocks free on the
		disk, NDBH,X ($02FC,X). If the hi byte
		is NOT $00, branch to USERTS.
	$EFC2	Load .A with the lo byte of the count of
		the total number of blocks free on the
		disk, NDBL,X ($02FA,X). If the lo byte
		is greater than 2, branch to USERTS.
	$EFC9	Load .A with $72 to indicate a DISK FULL
		error and JSR to ERRMSG ($E6C7).
USERTS	$EFCE	Terminate routine with an RTS.
		-----------------------------------------
		Calculate index into the BAM for
		FRETS and USEDTS.
		On exit: Z flag = 1 if used in BAM
		         Z flag = 0 if free in BAM
FREUSE	$EFCF	JSR to SETBAM ($F011) to set BAM image
		in memory. On return .Y contains a
		pointer to the start of the bit map for
		the desired track.
	$EFD2	Transfer the pointer from .Y to .A.
FREUS2	$EFD3	Store the pointer from .A into TEMP
		($6F).
FREUS3	$EFD5	Load .A with the desired sector number
		from SECTOR ($81) and do three LSR's to
		divide the sector number by 8 to find
		out which of the three bytes for this
		track the sector is in.
	$EFDA	Set the carry flag, add the pointer to
		the start of the track from TEMP ($6F)
		to the sector index (0/1/2) in .A, and
		transfer the result to .Y.
	$EFDE	Load .A with the desired sector number
		from SECTOR ($81), AND the sector number
		with $07 to find the bit position that
		corresponds to that sector, and transfer
		the result into .X.
	$EFE3	Load .A with the BAM byte that contains
		the bit for the desired block from
		(BMPNT),Y; ($6D),Y, and AND it with the
		bit map for the appropriate bit from
		BMASK,X ($EFE9,X) to set the Z flag.
	$EFE8	Terminate routine with an RTS.
		-----------------------------------------
		Bit mask table $EFE9-EFF0
BMASK	$EFE9	.BYTE $01 1
	$EFEA	.BYTE $02 2
	$EFEB	.BYTE $04 4
	$EFEC	.BYTE $08 8
	$EFED	.BYTE $10 16
	$EFEE	.BYTE $20 32
	$EFEF	.BYTE $40 64
	$EFF0	.BYTE $80 128
		-----------------------------------------
		Write out BAM to disk if value in WBAM
		indicates that it is necessary.
FIXBAM	$EFF1	Load .A with $FF and BIT this value with
		the value in WBAM ($02F9).
	$EFF6	If Z flag set (WBAM was $00) branch to
		FBAM10 to exit.
	$EFF8	If N flag clear (bit 7 of of WBAM was 0)
		branch to FBAM10 to exit.
	$EFFA	If V flag set (bit 6 of WBAM was 0)
		branch to FBAM10 to exit.
	$EFFC	Set WBAM ($02F9) to $00 and JSR to
		DOWRIT ($D58A) to write BAM to disk.
FBAM10	$F004	Terminate routine with an RTS.
		-----------------------------------------
		Zero the BAM area:
CLRBAM	$F005	JSR to SETBPT ($EF3A) to set the
		pointers to the BAM.
	$F008	Zero .Y and .A.
CLB1	$F00B	Loop, using .Y as an index, to store
		$00's in all 256 locations in the BAM
		buffer.
	$F010	Terminate routine an RTS.
		-----------------------------------------
		Set BAM image in memory:
SETBAM	$F011	Save the values of TO ($6F) and T1 ($70)
		onto the stack so we can use this as a
		work area.
	$F017	Load .X with the current drive number
		from DRVNUM ($7F). Load .A with the
		drive status for this drive from NODRV,X
		($FF,X). If the drive status is $00, we
		have a functioning drive so branch to
		SBM10 to continue.

	$F019	Load .A with $74 to indicate a DRIVE NOT
		READY error and JSR to CMDER3 ($E648).
SBM10	$F022	JSR to BAM2A ($F10F) to load .A with the
		channel number and .X with the drive #.
	$F025	Transfer the channel number (in .A) into
		T0 ($6F).
	$F027	Transfer the drive number from .X into
		.A, multiply it by 2 (ASL), store the
		result in T1 ($70) and in .X.
	$F02E	Load .A with the current track number
		from TRACK ($80) and compare it with
		the track value given in the BAM track
		table, TBAM,X ($029D,X). If the values
		match, the BAM is in the correct area
		of memory so branch to SBM30.

	$F033	Increment .X by 1 and store the result
		in T1 ($70). Note that .X now points to
		the alternate BAM channel.
	$F036	Compare the current track value (in .A)
		with the contents of the BAM track table
		TBAM,X ($029D,X) for the alternate BAM
		location. If the value match, the BAM
		is in an appropriate location so branch
		to SBM30.

	$F03B	JSR to SWAP ($F05B) to read in the BAM
		if necessary and move it to the correct
		area of the disk RAM.

SBM30	$F03E	Load .A with the BAM channel number from
		T1 ($70).
	$F040	Load .X with the current drive number
		from DRVNUM ($7F).
	$F042	Store the channel number (in .A) into
		UBAM,X ($029B,X) to set the last channel
		used pointer.
	$F045	Multiply the channel number (in .A) by
		four (2 x ASL), clear the carry, and
		add $Al, the lo byte of the pointer, to
		the start of the BAM ($02A1). Store the
		result into the lo byte of the BAM
		pointer, BMPNT ($6D).
	$F04C	Load .A with $02, the hi byte of the
		pointer to the start of the BAM, add $00
		to add in the carry (if any) from the
		previous addition, and store the result
		as the hi byte of the BAM pointer,
		BMPNT+1 ($6E).
	$F052	Zero .Y.
	$F054	Pull the original values of T1 ($70) and
		T0 ($6F) off the stack and store them
		back in their original locations.
	$F05A	Terminate routine with an RTS.
		-----------------------------------------
		Swap images of the BAM:
SWAP	$F05B	Load .X with the index into the buffer
		from T0 ($6F) and JSR to REDBAM ($F0DF)
		to read the BAM if not already in RAM.
	$F060	Load .A with the current drive number
		from DRVNUM ($7F) and transfer the drive
		number into.X.
	$F063	Multiply the drive number in .A by two
		(ASL), OR it with the least used BAM
		pointer in UBAM,X ($029B,X), FOR it with
		$01, and AND it with $03. Store the
		result into T1 ($70) and JSR to PUTBAM
		($F0A5) to put the memory image into the
		BAM.
	$F070	Load .A with the buffer number from
		JOBNUM ($F9), multiply it by two (ASL),
		and transfer the result into .X.
	$F074	Load .A with the track number from TRACK
		($80), multiply it by four (2 x ASL),
		and store the result as the lo byte of
		the pointer in BUFTAB,X ($99,X).
	$F07A	Load .A with the value from T1 ($70),
		multiply it by four (2 x ASL), and
		transfer the result into .Y.
SWAP3	$F07F	Transfer one byte of the BAM from its
		position in RAM, (BUFTAB,X) ($99,X), to
		its proper position BAM,Y ($02A1,Y).
	$F084	Zero the memory location that held the
		BAM byte (BUFTAB,X); ($99,X).
	$F088	Increment the lo byte of the pointer to
		the original BAM image BUFTAB,X ($99,X).
	$F08A	Increment .Y, the pointer to the new BAM
		image. Transfer this value into .A, AND
		it with $03 to mask off the high order
		bits, and if the result is not $00,
		branch back to SWAP3 to move the next
		byte.
	$F090	Load .X with the drive number from T1
		($70). Load .A with the current track
		number from TRACK ($80) and store the
		track number into TBAM,X ($029D,X) to
		set the track number for the image.
	$F097	Load .A with the write-BAM flag from
		WBAM ($02F9). If the flag is non-zero,
		branch to SWAP4 so we don't write out
		the BAM now.
	$F09C	JMP to DOWRIT ($D58A) to write out the
		BAM to disk and terminate the routine.

SWAP4	$F09F	OR the write-BAM flag (in .A) with $80
		to indicate that a write of the BAM is
		pending and store the result back into
		WBAM ($02F9).
	$F0A4	Terminate routine with an RTS.
		-----------------------------------------
		Transfer memory image of BAM into the
		correct position in disk RAM:
PUTBAM	$F0A5	Transfer the pointer in .A into .Y.
	$F0A6	Load .A with the track number of the BAM
		from TBAM,Y ($029D,Y). If the track
		number is $00, there is no BAM image
		in RAM so branch to SWAP2.
	$F0AB	Save the track number onto the stack.
	$F0AC	Zero the track flag in TBAM,Y ($029D,Y).
	$F0B1	Load .A with the buffer number from
		JOBNUM ($F9), multiply it by two (ASL),
		and transfer the result into .X.
	$F0B5	Pull the track number off the stack,
		multiply it by four (2 x ASL), and store
		the result as the lo byte of the pointer
		in BUFTAB,X ($99,X).
	$F0BA	Transfer the pointer in .Y into .A,
		multiply it by four (2 x ASL), and
		transfer the result back into .Y.
SWAP1	$F0BE	Transfer one byte of the BAM image from
		BAM,Y ($02A1) to (BUFTAB,X); ($99,X).
	$F0C3	Zero the memory location that held the
		BAM byte BAM,X ($02A1,X).
	$F0C8	Increment the lo byte of the pointer to
		the original BAM image BUFTAB,X ($99,X).
	$F0CA	Increment .Y, the pointer to the new BAM
		image. Transfer this value into .A, AND
		it with $03 to mask off the high order
		bits, and if the result is not $00,
		branch back to SWAP1 to move the next
		byte.
SWAP2	$F0D0	Terminate the routine with an RTS.
		-----------------------------------------
		Zero the track number for BAM images:
CLNBAM	$F0D1	Load .A with the drive number from TRACK
		($80), multiply it by two (ASL), and
		transfer the result into .X.
	$F0D5	Zero .A and store $00 as the track #
		for the BAM image in TBAM,X ($029D,X).
	$F0DA	Increment .X and store $00 as the track
		# for the BAM image in TBAM,X ($029D,X).
	$F0DE	Terminate the routine with an RTS.
		-----------------------------------------
		Read BAM from disk if not already in RAM
REDBAM	$F0DF	Load .A with the value from BUFO,X and
		compare it with $FF. If it is not $FF,
		the BAM is in memory so branch to RBM20.
	$F0E5	Transfer the channel number from .X into
		.A and save it onto the stack.
	$F0E7	JSR to GETBUF ($D28E) to find a free
		buffer. On return transfer the buffer
		number from .A into .X.
	$F0EB	If a buffer was found (bit 7 of buffer
		number not set), branch to RBM10.
	$F0ED	Load .A with $70 to indicate a NO
		CHANNEL ERROR and JSR to CMDERR ($C1C8).
RBM10	$F0F2	Store the buffer number assigned (in .X)
		into JOBNUM ($F9).
	$F0F4	Pull the channel number off the stack
		and transfer it into .Y.
	$F0F6	Transfer the buffer number from .X to
		.A, OR it with $80 to set it as inactive
		for stealing, and store the result into
		BUFO,Y ($00A7,Y).
	$F0FC	Multiply the buffer number (in .A) by
		two (ASL) and transfer the result into
		.X.
	$F0FE	Load .A with the directory track number
		(#18) from DIRTRK ($FE85) and store it
		in the header table at HDRS,X ($06,X).
	$F103	Store $00 as the BAM sector number in
		the header table at HDRS+I,X ($07,X).
	$F107	JMP to DOREAD ($D586) to read in the
		BAM and terminate routine.

RBM20	$F10A	AND the channel number (in .A) with $0F
		and store the result in JOBNUM ($F9) to
		set the RAM's job number.
	$F10E	Terminate routine with an RTS.
		-----------------------------------------
		Load .A with the channel # for the BAM
BAM2A	$F10F	Load .A with $06, the BAM ' s channel #
B2X10	$F111	Load .X with the current drive number
		from DRVNUM ($7F). If the drive number
		is not $00, branch to B2X10.
	$F115	Clear the carry flag and add $07 to find
		the BAM channel number for drive #1.
	$F118	Terminate routine with an RTS.
		-----------------------------------------
		Load .X with the channel # for the BAM
BAAM2X	$F119	JSR TO BAM2A ($F10F) to load .A with the
		BAM's channel number.
	$F11C	Transfer the channel # from .A to .X.
	$F11D	Terminate routine with an RTS.
		-----------------------------------------
		Next available track and sector:
		Given current track and sector, this
		routine returns the next available track
		and sector.
NXTTS	$F11E	JSR to GETHDR ($DE3E) to set TRACK and
NXTDS	$F121	SECTOR from the most recent header.
	$F125	Store $03 into TEMP ($6F).
	$F12D	Load .A with $01, OR it with the value
		of the write-BAM flag, WBAM ($02F9), and
		store the result back into WBAM to
		prevent a write of the BAM.
		Load .A with the value from TEMP ($6F)
		NXT1 and save it onto the stack.
	$F130	JSR to SETBAM ($F011) to set the BAM
	$F133	image into memory.
	$F136	Pull the original value of TEMP off the
		stack and store it back in TEMP ($6F).
		Load .A with the BAM value from
		(BMPNT),Y; ($6D,Y). If the value is not
		$00 (no sectors free), branch to FNDNXT
		($F173).
NXTERR	$F13A	Load .A with the current track number
	$F141	from TRACK ($80). If the track number
	$F143	is #18 (directory track), branch to
	$F145	NXTERR to abort.
	$F14C	If the current track is less than #18,
	$F14F	branch to NXT2.
	$F150	Increment the track number in TRACK($80)
	$F152	Compare the value of TRACK to $24 (#36),
	$F156	the maximum track value. If they are not
	$F158	equal, branch to NXT1 to check out this
	$F15A	track.
		Load .X with $12 (#18), the directory
		track number from DIRTRK ($FE85).
		Decrement the track number in .X.
		Store the track number (in .X) into
		TRACK ($80).
		Store $00 as the sector number into
		SECTOR ($81).
		Decrement the counter in TEMP ($6F).
		If the count is not $00 yet, branch to
		NXT1.
		Load .A with $72 to indicate a DISK FULL
NXT2	$F15F	error and JSR to CMDERR ($C1C8).
		Decrement the track number in TRACK($80)
	$F161	If the value in TRACK is not $00, branch
	$F163	to NXT1 to check out this track.
	$F166	Load .X with $12 (#18), the directory
		track number from DIRTRK ($FE85).
		Increment the track number in .X.
	$F167	Store the track number (in .X) into
		TRACK ($80).
	$F169	Store $00 as the sector number into
		SECTOR ($81).
	$F16D	Decrement the counter in TEMP ($6F).
	$F16F	If the count is not $00 yet, branch to
		NXT1.
	$F171	If the count is $00, branch to NXTERR.
		-----------------------------------------
		Find the optimum next sector on this
		track. Next sector=Current+change (#10)
FNDNXT	$F173	Load .A with the sector number from
		SECTOR ($81).
	$F175	Clear the carry flag and add the sector
		increment from SECINC ($69). The normal
		increment is $0A (#10). It is $03 for
		the directory track.
	$F178	Store the new sector number into SECTOR.
	$F17A	Load .A with the current track number
		from TRACK ($80) and JSR to MAXSEC
		($F24B) to find the maximum sector
		number on this track (returned in .A).
	$F17F	Store the maximum sector number into
		LSTSEC ($024E) and CMD ($024D).
	$F185	Compare the maximum sector number (in
		.A) with the new sector value in SECTOR
		($81). If the new sector value is less
		than the maximum, branch to FNDNO.
		New sector number too big so subtract.
		away
		the maximum sector number on track.
	$F189	Set the carry flag.
	$F18A	Load .A with the new sector number from
		SECTOR ($80).
	$F18C	Subtract the maximum sector number on
		and store
		this track from LSTSEC ($024E)
		the result into SECTOR ($81).
	$F191	If the revised sector number is $00,
		branch to FNDNO.
	$F193	Decrement the revised sector number in
		SECTOR ($81) by 1.
FNDNO	$F195	JSR to GETSEC ($F1FA) to set the BAM
		into memory and find the first available
		sector following the revised sector #.
	$F198	If no sector is available on this track
		(Z flag = 1), branch to FNDN2.
FNDN1	$F19A	Exit with a JMP to WUSED ($EF90) to set
		this new sector as in use.
FNDN2	$F19D	Set the sector number in SECTOR ($81)
		to $00.
	$F1A1	JSR to GETSEC ($F1FA) to set the BAM
		into memory and find the first available
		sector following the revised sector #.
	$F198	If a sector is available on this track
		(Z flag = 0), branch to FNDN1.
	$F1A6	JMP to DERR ($F1F5) to abort.
		-----------------------------------------
		Find optimum initial track and sector:
INTTS	$F1A9	Load .A with $01, OR it with the write-
	$F1B1	BAM flag, WBAM ($02F9), and store the
		result back in WBAM to indicate a write
		of BAM is pending.
		Load .A with the value from RO ($86) and
		save it onto the stack.
	$F1B4	Store $01 into RO ($86).
		NOTE: TRACK = DIRECTORY TRACK - RO
	$F1B8	Load .A with the directory track number
		$FlBB
		($12) from DIRTRK ($FE85).
		Set the carry flag, subtract the counter
		in RO and store the result into TRACK
	$F1C0	($80).
		If the value in TRACK is less than or
		equal to 0, branch to ITS2.
		Do tracks 17 -> 1
	$F1C4	JSR to SETBAM ($F011) to set the pointer
		to the BAM.
	$F1C7	Load .A with the number of blocks free
		on this track from (BMPNT),Y; ($6D,Y).
	$F1C9	If some sectors are free on this track
		(Z flag not set), branch to FNDSEC
		($F1E6).
		None free on lower track so try a higher
		one:
ITS2	$F1CB	Load .A with the directory track number
		$F1CE
		($12) from DIRTRK ($FE85).
		Clear the carry flag, add the counter in
		RO and store the result into TRACK ($80)
	$F1D3	Increment the track counter in RO ($86).
	$F1D5	If the value in TRACK is greater than or
	$F1DA	equal to the maximum track number (#36),
		branch to ITS3.
		Load .A with $67 to indicate a SYSTEM
		TRACK & SECTOR error and JSR to CMDER2
		($E645).
		Do tracks 19 -> 35
ITS3	$F1DF	JSR to SETBAM ($F011) to set the pointer
		to the BAM.
	$F1E2	Load .A with the number of blocks free
		on this track from (BMPNT),Y; ($6D,Y).
	$F1E4	If no sectors are free on this track
		(Z flag is set), branch to ITS1 to try
		a lower numbered track.
FNDSEC	$F1E6	Pull the original value of RO off the
	$F1E9	stack and store it back in RO ($86).
		Store $00 as the sector number in
	$F1ED	SECTOR ($81).
	$F1F0	JSR to GETSEC ($F1FA) to set the BAM and
	$F1F2	find first available sector.
		If no sector available, branch to DERR.
		Terminate routine with a JMP to WUSED
		($EF90) to mark sector as used in BAM.
		-------------------------------------
DERR	$F1F5	Error in BAM:
		Load .A with $71 to indicate an error
		in the BAM and JSR to CMDER2 ($E645).
		-----------------------------------------
GETSEC	$F1FA	Set the BAM and find the first available
		sector starting at SECTOR:
		JSR to SETBAM ($F011) to set the pointer
		to the BAM.
	$F1FD	Transfer the .Y value into .A and save
	$F1FF	it onto the stack.
	$F202	JSR to AVCK
		validity.
		Load .A with
		($F220) to check the bit map
		the current track number
	$F20A	from TRACK ($80) and JSR to MAXSEC
		($F24B) to find the maximum sector
		number allowed on this track. On return,
		into store
		Pull the maximum sector number (in .A)
		LSTSEC ($024E). off the stack
		the original .Y value
GS10	$F20D	and store it in TEMP ($6F).
		Compare the current sector number from
		SECTOR ($81) with the maximum sector
		GS20. count
		in LSTSEC ($024E). If the current
		sector
		number is too large, branch to
	$F214	JSR to FREUS3 ($EFD5) to calculate index
	$F219	into the BAM. On return, if the Z flag
		is not set, the sector is free so branch
		to GS30.
		Sector was not free:
		Increment the sector number in SECTOR
GS20	$F21D	($81) and branch (always) to GS10.
		Load .A with $00. Note
		that this sets
GS30	$F21F	the Z flag to indicate that a free
		sector was not found.
		Terminate routine with an RTS.
		-----------------------------------------
AVCK	$F220	Check the validity of the bit map:
		Load .A with the value of TEMP ($6F) and
		save it onto the stack.
	$F223	Store $00 into TEMP ($6F).
	$F227	Load .Y with $04, the number of bytes
	$F22A	per track in the BAM from BAMSIZ ($FE86)
		Decrement .Y by 1 (now $03).
AC10	$F22B	Load .X with $07 (bit counter).
AC20	$F22D	Load .A with the BAM byte for this track
		from (BMPNT),Y; ($6D,Y), and AND the BAM
		byte with the bit. mask from BMASK,X
		($EFE9,X) to isolate the bit for this
		sector. If the result is $00, the sector
		is allocated so branch to AC30.
	$F234	Since the sector is free, increment the
AC30	$F236	count of free sectors in TEMP ($6F).
		Decrement the bit counter (1 bit/sector)
	$F239	in .X. If_ the count is greater than or
		equal to $00, branch to AC20.
		Decrement the byte counter (8 sectors/
	$F23C	byte) in .Y. If the count is not $00,
		branch to AC10.
		Compare the number of bytes free on the
	$F242	track as given in the BAM at (BMPNT),Y
		($6D,Y) with the count we did in TEMP
		($6F). If the counts DO NOT MATCH, the
		branch to AC40 to abort.
		Pull the original value of TEMP off
		stack and restore it into TEMP ($6F).
	$F245	Terminate routine with an RTS.
		-----------------------------------------
AC40	$F246	Error in BAM: an error
		Load .A with $71 to indicate
		in the BAM and JSR to CMDER2 ($E645).
		-----------------------------------------
MAXSEC	$F24B	Returns the number of sectors allowed
		on this track. Track number in .A.
		Load .X with the number of zones ($04)
MAXI	$F24E	from
		NZONES ($FED6).
		Compare the track number (in .A) with
	$F251	the zone boundary value from TRKNUM-1,X
		($FED6,X).
		Decrement the zone count in .X.
	$F252	If the track number in .A is less than
	$F254	the boundary value, branch to MAXI.
		Load .A with the number of sectors/track
	$F257	for this zone from NUMSEC,X ($FED1,X).
		Terminate routine with an RTS.
		-----------------------------------------
		Kill protection: Does NOTHING on 1541!
KILLP	$F258	Terminate routine with an RTS.
		DISK CONTOLLER ROUTINES
		-----------------------
		-----------------------
CNTINT	$F259	Controller initialization
	$F25B	Store $01101111 in DDRB2 ($1C02) to set
	$F25E	the data direction for Port B.
		Store %01100000 in DSKCNT ($1C00) to
	$F26C	turn off the motor & LED and set phase A
		Set the peripheral control register
	$F27B	($1C0C) for neg edge latch mode, CA2 hi
		to disable the SO line to the 6502, CB1
		is input, and CB2 is R/W mode control.
		set T1HL2($lC07) to $3A and T1LL2($1C06)
	$F281	to $00 so there is 20ms between IRQ's
		store $7F in IER2 ($1C0E) to clear all
	$F286	IRQ sources.
		store $C0 in IFR2 ($1C0D) to clear the
	$F28E	bit and then into IER2 ($1C0E) to enable
		the timer IRQ.
		store $FF as the current drive, CDRIVE
	$F294	($3E) and as init flag, FTNUM ($51).
		set header block ID, HBID ($39) to $08
	$F298	set data block ID, DBID ($47) to $07
	$F29C	set NXTST ($62/3) to point to INACT
		($FA05).
	$F2A4	set MINSTP ($64) to 200 to indicate the
	$F2A8	minimum number of steps required to
		invoke the fast stepping mode.
		store 4 into AS ($5E) to indicate the
LCC	$F2AC	number of steps needed to accelerate
		and decelerate the head.
		store 4 into AF ($5F) as the
		acceleration/deceleration factor.
		Main controller loop:
	$F2B0	Scans the job queue for job requests
	$F2B3	Finds job on current track if it exists
		Save stack pointer in SAVSP ($49).
		reset IRQ flag
	$F2B6	set bits 3,2,& 1 of PCR2 ($1000) to
TOP	$F2BE	enable S.O. to 6502, hi output
CONT10	$F2C3	top of loop to scan job queue. Load .Y
		with #$05 as pointer to top of queue.
		Load .A with byte from queue, JOBS,Y
	$F2C5	($0000,Y). Test if bit 7 is set. If not,
		branch to CONT20 since no job here.
		Check if job is a jump code ($D0).
CONT30	$F2CA	If not, branch to CONT30.
	$F2CD	Transfer queue position from .Y to .A
	$F2D1	and JMP to EX2 ($F370) to do jump job.
		AND job code with $01. If result is 0,
		the drive # is valid so branch to CONT35
		Load .A with $0F to indicate a bad drive
		number and JMP to ERRR ($F969)
CONT35	$F2D8	Store job drive # in DRIVE ($3D).
	$F2DB	Compare job drive # with current drive
	$F2DF	number in CDRIVE ($3E). (CDRIVE is $FF
		if the drive is not turned on.) If they
		are equal, branch to CONT40
		JSR to TURNON ($F97E) to turn on drive.
	$F2E2	Set CDRIVE to job drive # and exit for
		now with a JMP to END ($F99C).
CONT40	$F2E9	Check the value in DRVST ($20) to see if
	$F2ED	the drive is up to speed. If bit 7 is
		set, it isn ' t so JMP to END ($F99C).
		Check if the head is stepping. If it is,
		exit with a JMP to END ($F99C). If it is
		not stepping, branch to QUE.
CONT20	$F2F3	Decrement .Y pointer into queue. If more
		locations in queue, branch back to
		CONT10. If none left JMP to END ($F99C).
QUE	$F2F9	Store $20 in DRVST ($20) to set drive
	$F2FD	status to running.
		Check if head needs to be stepped for
		this job. If not, branch to QUE20.
QUE05	$F306	Check other jobs to see if one for this
		track. If not, calculate steps needed.
	$F315	Store $60 in DRVST ($20) to set drive
		status to stepping, store destination
		track in DRVTRK ($22) and exit for now
		with a JMP to END ($F99C).
QUE20	$F320	check if job is on current drive. If
		not, branch back to QUE05.
	$F32A	calculate distance to track
	$F32D	are we on track already? if so, branch
		to GOTU.
	$F32F	store number of steps to the desired
		track in NXTRK ($42)
	$F339	JMP back to QUE05 to check if another
		job is closer.
GOTU	$F33C	Calculate zone (1-4) of the desired
		track and store the number of sectors
		on the track in SECTR ($43).
	$F34D	Calculate recording density and set the
		divide by N counter by storing a value
		in DSKCNT ($1000).
	$F35F	Load .x with drive number and .A with
		the job code.
	$F363	Compare job code with $40. If equal,
		branch to BMP to do bump job.
EXE	$F367	Compare job code with $60. If equal,
		branch to EX to do execute job.
	$F36B	Not Bump or Execute, JMP to SEAK ($F3B1)
		-----------------------------------------
		EX Do an execute job
	$F367	set pointer to buffer in BUFPNT ($30/1)
	$F379	do indirect JMP via BUFPNT to the code
		that starts at the start of the buffer.
		-----------------------------------------
BMP	$F37C	Do a bump to track #1
SETJB	$F380	Store $60 as the drive status, DRVST
SEAK	$F388	(20) to indicate head is stepping.
	$F38C	Set track phase to phase A
	$F390	Store -45 ($A4) as the number of tracks
	$F393	to move head in STEPS ($4A).
	$F3B1	Set. DRVTRK ($22) to 1 as new track#
		Job done so JMP to ERRR ($F969).
		Sub to set pointer to buffer, BUFPNT
		($30/31) and into header table, HDRPNT
		($32) for this position in job queue.
		-----------------------------------------
		Search for a valid header block on this
		track. Up to 90 header and data blocks
		are scanned while looking for a valid
		header block before this routine gives
		up. A valid header block must have:
		1) a SYNC mark
		2) a header block ID ($08)
		3) a valid checksum (EOR of sector,
		track, ID1, and ID2)
		4) the sector number
		5) the track number
		6) the second disk ID character given
		when the disk was formatted
		7) the first disk ID character given
		when the disk was formatted
		NOTE: The actual order of these bytes
		is as given above. Not as listed
		in the 1541 manual!
		-----------------------------------------
SEAK	$F3B1	Store $5A (90) in TMP ($4B) as the sync
	$F3B7	mark counter (quit if counts down to 0)
	$F3BB	Store $52 into STAB ($24) as the header
	$F3BE	block ID code to wait for (GCR for $08).
	$F3C4	JSR to SYNC ($F556) to wait for sync
	$F3C6	Read first character after sync
		Compare it to character in STAB ($24)
		If no match, this is not a header block
		so branch to SEEK20.
SEEK15	$F3C8	Loop to read in the next 7 characters
	$F3D5	and store in STAB+1,X ($25,X).
		JSR to CNVBIN ($F497) to convert the
		header bytes from GCR form to normal.
SEEK30	$F3D8	Loop to compute checksum of header read
	$F3E2	EOR checksum, sector, track, ID1 & ID2.
	$F3E6	If computed checksum is not 0, branch
	$F3EC	to CSERR ($F41E) to report error.
		Update current track from header data
		Compare job code in JOB ($45) with $30
		to see if it is a seek job. If it is,
		branch to ESEEK ($F410) to do it.
	$F3F2	Compare master disk ID in $12/13 to the
		disk ID from the header in $16/17. If
		they don't match, branch to BADID($F41B)
		to report a disk ID mismatch error.
	$F404	JMP to WSWCT ($F423) to find the best
		sector on this track to service (usually
		the current sector + 2)
SEEK20	$F407	Decrement SYNC counter in TMP($4B) by 1
		to see if we should check more syncs. If
		not 0 yet, branch back to SEEK10. If 0,
		load .A with a $02 (to indicate header
		block not found) and JMP to ERRR ($F969)
ESEEK	$F410	Change master disk ID in $12/$13 to
		match the ID read in from $16/17
DONE	$F418	Load .A with a $01 (to indicate job
		completed OK) and exit to error handler
		-----------------------------------------
BADID	$F41B	Load .A with a $0B (to indicate disk ID
		mismatch) and exit to error handler
CSERR	$F41E	Load .A with a $09 (to indicate a bad
		checksum) and exit to error handler
		-----------------------------------------
		WSECT Determine best sector on this track to
		service (optimum is current sector + 2)
	$F423	Store $7F as the current sector in $4C
	$F427	Load .A with the sector number from the
		header just read from HEADER+3 ($19).
	$F429	Add 2
	$F42C	Compare sum to the number of sectors on
		this track in SECTR ($43). If sum is too
		big, subtract the number of sectors.
L460	$F432	Store sum as next sector to be serviced
		in NEXTS ($4D).
L480	$F43A	JSR to SETJB ($F393) to set pointers.
	$F443	Check to be sure job is for this drive.
		If not, branch to L470 ($F483).
	$F447	Check to be sure job is for this track.
		If not, branch to L470 ($F483).
	$F44F	Compare job code in JOB ($45) with $60
		to see if it is an execute job. If it
		is, branch to L465.
	$F455	Load .A with job ' s sector, (HDRPNT),Y
		and subtract the upcoming sector from
		NEXTS ($4D). If result is positive,
		branch to L465 since sector coming up.
	$F45E	Add value from NEXTS ($4D) back in.
L465	$F461	Compare to distance to other sector
		request. If further away, branch to L470
		since other job is closer.
	$F465	Save distance to sector on the stack.
		Check job code in JOB ($45). If a read
		job, branch to TSTRDJ.
DOITT	$F46A	This is a write job. Pull distance to
TSTRDJ	$F473	sector off the stack. Since a write job
L470	$F47E	requires set up time, if sector is less
	$F483	than 9 ahead or more than 12 ahead, we
	$F487	are better off doing another job so
		branch to L470.
		This job is closer than others so set up
		by storing distance in CSECT ($4C) and
		setting BUFPNT to point to the buffer.
		Branch always to L470
		-----------------------------------------
		This is a read job. Pull distance to
		sector off the stack. Since a read job
		doesn't need much set up time, if sector
		is less than 6 ahead, we better do it
		so branch to DOITT.
		Decrement queue position in JOBN ($3F)
		by 1. If more to check branch to L480.
		No more to check. Test if any jobs were
		found. If none, JMP to END ($F99C). If
		yes, set up job and JMP to REED ($F4CA)
		_________________________________________
		Convert GCR image of header into the
		normal 8 bit binary and~ move the values
		into $16/7/8/9/A. The characters
		decoded include:
		-Header block ID code (usually $08)
		-Hdr block checksum (EOR of T/S/ID1/ID2)
		-Sector number
		-Track number
		-ID2 (2nd ID chr given when formatted)
		-ID1 (1st ID chr given when formatted)
		-The remaining characters are junk!
		-----------------------------------------
REED	$F4CA	Read in the track and sector that is
		specified in the header table
		Check if this is a read job. If not,
		JMP to WRIGHT ($F4CE)
READO1	$F4D1	JSR to DSTRT ($F50A) find header and set
		up to the start of the data block
READ11	$F4D4	Loop to read first 256 data bytes and
		store them in the data buffer.
READ20	$F4DF	Loop to read the last 70 data bytes and
	$F4ED	store them in the overflow buffer from
		$F4F0 $01BA to $01FF.
		JSR to GCRBIN ($F8E0) to convert the 326
		GCR data bytes into 256 normal bytes
		Compare the first byte in the data block
		from BID ($38) with the header block ID
		character (normally $07) in HDIB ($47)
		to check if this is a legal data block.
	$F4F4	If they match, branch to READ28.
READ28	$F5F6	No match, so load .A with a 4 to flag a
DSTRT	$F4FB	DATA BLOCK NOT FOUND error and JMP to
	$F4FE	ERRR ($F969).
	$F502	JSR to CHKBLK ($F5E9) to compute the
	$F504	checksum for the data block by EORing
	$F5F6	all the 256 data bytes.
	$F507	Compare the computed checksum in .A with
	$F50A	with the checksum read from the disk in
		CHKSUM ($3A). If equal, branch to READ40
		No match, so load .A with a 5 to flag a
		DATA BLOCK CHECKSUM error good read
		Byte $2C to skip over next LDA
		Load .A with a
		1 to indicate a
		JMP to ERRR ($F969).
		JSR to SRCH ($F510) to find the desired
		header block.
		JMP to SYNC ($F556) to wait for the
		data block sync character.
		-----------------------------------------
		SRCH Find a specific header. The track and
		sector desired must be stored in the
		header table
	$F510	Use values from the header table and
		the master disk ID ($12/3) to set up an
		image of the desired header $16-$19
	$F529	FOR the track, sector, and ID characters
		to calculate the header checksum and
		store it in $1A.
	$F533	JSR to CONHDR ($F934) to convert the
		header image into its GCR image.
	$F536	Load .X with $5A as a counter of the
		number of sync marks checked.
SRCH2O	$F538	JSR to SYNC ($F556) to wait for the
		next sync mark.
SRCH25	$F53D	Loop to scan the 8 bytes following the
		sync mark to attempt to find a match to
		the GCR image of the desired header. If
		any character does not match the image,
		branch to SRCH30.
		exit with an RTS
	$F54D	All characters match so
		-----------------------------------------
SRCH30	$F54E	Decrement the sync mark counter in .X
		If counter is not 0 yet, branch back to
		SRCH2O to wait for next sync.
	$F551	No match, so load .A with a 2 to flag a
		BLOCK HEADER NOT FOUND error.
ERR	$F553	JMP to ERRR ($F969).
		-----------------------------------------
SYNC	$F556	Wait for SYNC mark
		A SYNC mark is 10 or more consecutive
		1's bits written onto the disk. It is
		used to identify the start of a block
		of information recorded on disk. The
		first character following a SYNC mark
		is used to determine whether this is
		a header block ($08) or a data block
		($07).
SYNC10	$F556	Store $D0 in TIMER1 ($1805) to allow a
	$F55B	maximum wait of 20 milliseconds for a
	$F55D	sync before timing out..
		Load .A with $03 (the error code for a
		NO SYNC FOUND error)
		Test bit 7 of TIMER1 ($1805) to check
	$F562	for a time-out. If time is up, branch
	$F567	to ERR ($F553) to exit.
		Test bit 7 of DSKCNT ($1C00) to check
		for a sync. If no sync, branch back to
		SYNC10 to wait some more.
		Load .A from DATA2 to reset the PA latch
		clear the 6502's overflow flag, and RTS
		-----------------------------------------
		WRIGHT Write contents of data buffer to disk
	$F56E	Compare job code in .A with $10 to check
	$F575	if this is write job. If not, JMP to
		$F5 7A VERIFY ($F691).
		JSR to CH!BLK ($F5E9) to compute the
		checksum for the data block. Store the
		checksum in CHKSUM ($3A).
		Load .A from DSKCNT and AND it with $10
		to check for write protect. tab. If the
		result is not $00, OK to write so branch
		to WRT10.
		Load .A with $08 to flag a WRITE PROTECT
		error and JMP to ERRR ($F969)
		___________ ____________________________
WRT10	$F586	JSR to BINGCR ($F78F) to convert data
	$F589	in the buffer into GCR form.
	$F58C	JSR to SRCH ($F510) to find the correct
		header block
		Wait for 8 more bytes to go by. This is
		the header gap.
		-----------------------------------------
		NOTE: The header gap on the 1541 is 8
		bytes long. The gap on the 4040 is 9
		bytes long. This is the main reason why
		the drives are write incompatible!
		----------------------------------------
WRTSNC	$F594	Store $FF in DDRA2 ($1C03) to make Port
	$F599	A an output port
	$F5A3	Load .A from PCR2 ($1000), AND the value
	$F5AF	with $1F, OR it with $C0, and store the
		result in PCR2 to turn on write mode.
		Store $FF in DATA2 ($1C01) as the SYNC
		mark character
		Loop to write out. 5 consecutive $FF
		bytes (5x8 = 40 1's).
	$F5B1	Load .Y with $BB to point into the
		overflow buffer ($01BB-01FF).
WRT30	$F5B3	Load .A with byte from overflow buffer,
		wait till last byte is out, store new
		byte into DATA2 ($1C01), increment .Y
		pointer, and if more characters to do,
		branch back to WRT30.
WRT40	$F5BF	Load .A with byte from data buffer,
	$F5CA	wait till last byte is out, store new
	$F5CC	byte into DATA2 ($1C01), increment .Y
		pointer, and if more characters to do,
		branch back to WRT40.
		Wait for final byte to clear
		Load .A from PCR2 ($1C0C), OR the value
		with $E0, and store the result back in
		PCR2 to shift to read mode.
	$F5D4	Store $00 in data direction register
		DDRA2 to make port A an input port.
	$F5D9	JSR to WTOBIN ($F5F2) to convert GCR
	$F5DC	data in buffer back into its normal 8
		bit form to prepare to verify it.
		Convert the write job number in the job
		queue into a verify job.
	$F5E6	JMP to SEAK ($F3B1) to scan the queue
		for the next job.
		-----------------------------------------
CHKBLK	$F5E9	Calculate data block checksum
		FOR the 256 data bytes. Return with the
		checksum in .A
		-----------------------------------------
WTOBIN	$F5F2	Convert the 10 bit image of the data to
		normal 8 bit binary. Since 5 encoded
		bytes (40 bits) are converted into 4
		normal bytes (32 bits), the encoded
		form of 256 data bytes takes up 320
		bytes. At the start of this routine
		the first 64 encoded bytes that were
		read are stored in the overflow buffer
		($01BA-FF) and the remaining 256 bytes
		are in the normal data buffer. At the
		end of the routine the decoded bytes
		are stored in the normal data buffer.
	$F5F2	Set up pointers to the buffers
	$F5FE	Do the overflow buffer ($01BA-FF) first.
	$F604	Store $BB in GCRPNT ($34) so it points
		to the first byte in the overflow buffer
		($01BB) that is to be processed by the
		routine GET4GB.
	$F608	Store $BB in BYTCNT ($52) so it points
		to the location where the first decoded
		data byte is to be stored.
	$F60A	JSR to GET4GB ($F7E6) to convert the
		first five GCR bytes into 4 normal bytes
		(the data block ID + 3 data bytes). The
WTOB14	$F60D	decoded bytes appear in $52-5
	$F611	Store data block ID chr in BID ($38).
	$F624	Move decoded data bites from $53-$55 to
		the buffer ($01BB-D). Note that the
		decoded bytes are put back into the
		overflow buffer.
		JSR to GET4BG ($F7E6) to convert the
WTOB56	$F629	next 5 GCR bytes to 4 normal bytes and
	$F641	store them in $52-5.
	$F643	Move decoded data bytes from $53-$55 to
		the buffer ($01BB-D). Note that the
		decoded bytes are put back into the
		overflow buffer.
		If more in overflow, branch to WTOB14
		Move last two data bytes into buffer
WTOB53	$F64F	Loop to convert the 256 bytes in data
	$F629	bLffer. JSR to GET4BG ($F7E6) to convert
		the next 5 GCR bytes to 4 normal bytes
		and store them in $52-5.
		Move decoded data bytes from $53-$55 tc
		the data buffer. Note that the decoded
		bytes are put back in the data buffer.
		----------------------------------------
		At this point the data bytes have all
		been decoded. Some bytes are in the
		overflow buffer and some are in the
		lower part of the data buffer. The
		following routines shift the bytes in
		the buffer up and then fill -H e lower
		part of the buffer with the bytes from
		the overflow buffer.
		-----------------------------------------
WTOB52	$F66E	Move decoded bytes in lower part of the
WTOB57	$F683	data buffer up into their proper places
		in the buffer.
		Move decoded bytes from the overflow
	$F68E	buffer to the bottom of the data buffer.
	$F690	Set GCRFLG ($50) to 0 to indicate that
		the data in buffer is in normal form.
		Exit with an RTS.
		-----------------------------------------
		VRFY Verify a data block
		This routine converts the data in the
		data buffer into its 10 bit encoded
		form (GCR). It then compares the GCR
		image with what is recorded on the
		disk. The encoded data is then changed
		back into normal 8 bit binary form.
		----------------------------------------
	$F691	Compare job code in .A with $20 to check
		that this is a verify job. If not, JMP
		to SECTSK (F6CA) to do a sector seek.
	$F698	JSR to CHKBLK ($F5E9) to compute the
		checksum for the data block. Store the
		checksum in CHKSUM ($3A).
	$F69D	JSR to BINGCR ($F78F) tc convert the
		data to its GCR image.
	$F6A0	JSR to DSTRT ($F50A) to find the right
		sector and wait for data.
VRF15	$F6A3	Loop to read 64 data bytes from disk and
		compare them to those in the overflow
		buffer. If any bytes do not match,
		branch to VRF20 to report error.
VRF30	$F6B3	Loop to read 254 data bytes from disk
		and compare them to those in the data
		buffer. If any bytes do not match,
		branch to VRF20 to report.error.
	$F6C2	All bytes match so JMP to DONE ($F418)
		-----------------------------------------
VRF20	$F6C5	Bad byte, so load .A with $07 to flag a
		WRITE-VERIFY error & JMP to ERRR ($F969)
		-----------------------------------------
SECTSK	$F6CA	JSR to SRCH to do a sector search
		JMP to DONE ($F418)
		-----------------------------------------
PUT4GB	$F6D0	Convert binary to GCR
		This routine is used to convert. 4 normal
		8 bit bytes into the 10 bit encoded form
		used for recording onto disk. Encoding
		involves breaking up each 8 bit normal
		byte into two 4-bit nybbles. The 5-bit
		equivalent for each nybble is found by
		looking in a table. The 10 bits that
		result are stored in two consecutive
		memory locations. When four 8-bit bytes
		are encoded, the resulting 40 bits are
		stored like this:
		-------------------------------------------------
		Four normal 8 bit bytes stored in $52/3/4/5
		AAAABBBB CCCCDDDD EEEEFFFF GGGGHHHH
		-------------------------------------------------
		Four 10 bit encoded bytes stored in buffer
		aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh
		-------------------------------------------------
	$F6D0	Clear critical areas of the buffer where
		the encoded bytes are to be stored.
		GTAB to GTAB+4 ($56-5A)
	$F6D8	Load first 8-bit byte ($52), AND it with
		$F0 (11110000) to mask off the low
		nybble (AAAA0000), do four LSR's to
		convert the hi nybble to a low nybble
		(0000AAAA), look up the corresponding
		five bit GCR value (000aaaaa) in BGTAB
		BGTAB ($F77F+), do three ASL's on it
		(aaaaa000), and store it in the first
		position in the encoded data area ($56)
	$F6E9	Load first 8-bit byte ($52), AND it with
		$0F (00001111) to mask off the high
		nybble (0000BBBB), find the five bit GCR
		equivalent (000bbbbb) in BGTAB ($F77F+),
		do twc ROR's on it alternated with ROR's
		on $57 .A=(0000Cbbb) $57=bb000000, AND
		the value in .A with $07 (00000111), OR
		the value in .A with the value in $52
		(aaaaa000), and store the result
		(aaaaabbb) in the first position of the
		GCR data buffer (BUFPNT),Y ($30,Y).
	$F6FE	Load second 8-bit byte ($53), AND it
		with $F0 (11110000) to mask off the low
		nybble (CCOO0000), do four LSR's to
		convert the hi nybble to a low nybble
		(0000CCCC), lock up the five bit GCR
		equivalent (000ccccc) in BGTAB ($F77F+),
		do one ASL on it (00ccccc0), OR it with
		the contents of $57 (bb000000), and put
		the result (bbccccc0) in $57.
	$F70F	Load second 8-bit byte ($53), AND it
		with $0F (00001111) to mask off the high
		nybble (0000DDDD), find the five bit GCR
		equivalent (000ddddd) in BGTAB ($F77F+),
		do four ROL's on it (dddd0000 C=d),
		store it in $58(dddd0000), do one more
		ROL (ddd0000d C=d), AND it with $01, OR
		it with the value in $57(bbccccc0) and
		store the result (bbcccccd) into the
		second byte of the GCR buffer
	$F725	Load third 8-bit byte ($54), AND it with
		$F0 (11110000) to mask off the low
		nybble (EEEE0000), do four LSR's to
		convert the hi nybble to a low nybble
		(0000EEEE), look up the five bit GCR
		equivalent (000eeeee) in BGTAB ($F77F+),
		do one FOR on it (0000eeee C=e), OR it
		with the contents of $58 (dddd0000),
		store the result (ddddeeee) in the third
		byte of the GCR buffer, do another ROR
		(e0000eee)C=e, AND it with $80(10000000)
		and store the result (e0000000) in $59.
	$F73D	Load third 8-bit byte ($54), AND it with
		$0F (00001111) to mask off the high
		nybble (0000FFFF), find the five bit GCR
		equivalent (000fffff) in BGTAB ($F77F+),
		do two ASL's on it (Offfff00), AND it
		with $7C (01111100), OR it with the
		value in $59 (e0000000), and store the
		result (efffff00) in $59
	$F74D	Load the fourth 8-bit byte ($55), AND it
		with $F0 (11110000) to mask off the low
		nybble (GGGG0000), do four LSR's to
		convert the hi nybble to a low nybble
		(0000GGGG), look up the five bit GCR
		equivalent (000ggggg) in BGTAB ($F77F+),
		do three ROR's on .A alternated with
		ROR's on $5A .A=(00000gg) $5A=(ggg00000)
		AND .A with $03 (00000011), OR .A with
		the ccntents of $59 (efffff00), & store
		result (efffffgg) in the fourth byte of
	$F76F	the GCR buffer.
		Load the fourth 8-bit byte ($55), AND it
		with $0F (00001111) to mask off the high
		nybble (0000HHHH), find the five bit GCR
		equivalent (000hhhhh) in BGTAB ($F77F+),
		OR it with the value in $59 (ggg00000),
		and store the result (ggghhhhh) in the
		fifth position of the GCR buffer.
BGTAB	$F77F	Table of 5 bit GCR equivalents
		4 bit nybble 5 bit GCR code
		$00 0000 $0A 01010
		$01 0001 $0B 01011
		$02 0010 $12 10010
		$03 0011 $13 10011
		$04 0100 $0E 01110
		$05 0101 $0F 01111
		$06 0110 $16 10110
		$07 0111 $17 10111
		$08 1000 $09 01001
		$09 1001 $19 11001
		$0A 1010 $1A 11010
		$0B 1011 $1B 11011
		$0C 1100 $0D 01101
		$0D 1101 $1D 11101
		$0E 1110 $1E 11110
		$0F 1111 $15 10101
		Ncte:
		5 bits are used to ensure that
		not more than 2 consecutive 0's
		are recorded on disk.
		-----------------------------------------
		BINGCR Create write image of data
		This routine converts 260 normal 8-bit
		bytes into their 10-bit equivalents to
		produce an image for writing to disk. A
		total of 325 GCR bytes are produced.
		-----------------------------------------
		The original 8-bit bytes are:
		256 1 data block ID character ($07)
		data bytes (stored in buffer X)
		1 data checksum
		2 off bytes ($00)
		260 8-bit binary bytes
		-----------------------------------------
		The first 69 GCR bytes are stored in the
		overflow buffer ($10BB-FF). The rest of
		the GCR bytes are stored in buffer X and
		replace the original data bytes
		-----------------------------------------
	$F78F	Initialize pointers to buffers
	$F797	Set pointer to start of overflow $0lbb
	$F7A5	Move data block ID code from DBID ($47)
		and first 3 data characters into a work
		area
		($52/3/4/5) for input by the PUT4GB
BING07	$F7BA	routine ($F6D0) byte to convert
		Store pointer to next
	$F7BC	(in .Y) into BYTCNT ($36).
	$F7BF	JSR to PUT4GB ($F6D0) to convert the
		four
		GCR
		the
		the
		Move
		bytes in $52/3/4/5 into their five
		equivalents and store in buffer. Use
		overflow buffer first and then use
		data buffer. into the work area
		next four bytes
		($52/3/4/5).
	$F7D7	If more bytes to convert (.Y is count)
		branch back to BING07.
	$F7D9	Move data block checksum from DBID ($3A)
	$F7E3	and two off bytes ($00) into the work
		area ($53/4/5) NOTE: THE LAST DATA BYTE
		IS IN $52.
		JSR to PUT4GB ($F6D0) to convert the
		four bytes in $52/3/4/5 into their five
		GCR equivalents and store in buffer.
		-----------------------------------------
		GET4GB Convert GCR to binary
		This routine is used to decode 5 GCR
		bytes
		(used for recording on disk) into
		4 normal 8-bit binary bytes. Decoding
		involves extracting 5 bits from one or
		two GCR bytes. The 4-bit nybble that is
		equivalent to it is found by looking in
		a table. The pattern of 5-bit segments
		in the 5 GCR bytes and the equivalent
		4-bit nybbles in the four binary bytes
		are indicated below:
		--------------------------------------
		Four 10 bit encoded bytes stored in buffer
		aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh
		--------------------------------------
		Four AAAABBBB
		normal 8 bit bytes stored in $56/7/8/9
		CCCCDDDD EEEEFFFF GGGGHHHH
		-------------------------------------------------
	$F7E6	Load the first GCR byte (aaaaabbb) from
		(BUFPNT),Y, AND it with $F8 (11111000)
		to mask off the low bits (aaaa000), do
		three LSR's and store the result
	$F7F1	(000aaaaa) in GTAB ($56)
	$F7F9	Load the first GCR byte (aaaaabbb) from
	$F802	(BUFPNT),Y, AND it with $07 (00000111)
	$F80D	to mask off the high bits (00000bbb), do
		two ASL's and store the result(000bbb00)
		in $57.
		Increment Y and check if Y=0. If so,
		change BUFPNT so it points to the data
		buffer rather than the overflow buffer.
		Load the second GCR byte (bbcccccd) from
		(BUFPNT),Y, AND it with $C0 (11000000)
		to mask off the low bits (bb000000), do
		three ROL's (000000bb), OR it with the
		value in $57 (000bbb00), and store the
		result (000bbbbb) back in $57.
		Load the second GCR byte(bbcccccd) from
		(BUFPNT),Y, AND it with $3E (00111110)
	$F814	to mask off unwanted bits (00ccccc0), do
	$F81F	one LSR and store the result (000ccccc)
		in $58.
		Load the second GCR byte (bbcccccd) from
		(BUFPNT),Y, AND it with $01 (00000001)
		to mask off unwanted bits (0000000d),
		do four ASL's and store the result
		(000d0000) in $58.
		Load the third GCR byte (ddddeeee) from
		(BUFPNT),Y, AND it with $F0 (11110000)
		to mask off the low bits (dddd0000), do
	$F82B	four LSR's (0000dddd), OR it with the
		value in $59 (000d0000), and store the
		result (000ddddd) back in $59.
		Load the third GCR byte (ddddeeee) from
	$F833	(BUFPNT),Y, AND it with $0F(00001111) to
		mask off hi bits (0000eeee), do one ASL
		and store the result (000eeee0) in $5A.
		Load the fourth GCR byte (efffffgg) from
	$F840	(BUFPNT),Y, AND it with $80 (10000000)
		to mask off the low bits (e0000000), do
		two ROL's (000e0000), OR it with the
		value in $5A (0000eeee), and store the
		result (000eeeee) back in $5A.
		Load the fourth GCR byte (efffffgg) from
		(BUFPNT),Y, AND it with $7C (01111100)
		to mask off unwanted bits (Offfff00), do
		two LSR's and store the result(000fffff)
		in $5B.
	$F848	Load the fourth GCR byte (efffffgg) from
		(BUFPNT),Y, AND it with $03 (00000011)
		to mask off unwanted bits (000000gg),
		do three LSR's and store the result
		(000gg000) in $5C.
	$F854	Increment Y. If Y=0 change BUFPNT to
		point to the next buffer.
	$F85A	Load the fifth GCR byte (ggghhhhh) from
		(BUFPNT),Y, AND it with $E0 (11100000)
		to mask off the low bits (ggg00000), do
		four ROL's (00000ggg), OR it with the
		value in $5C (000gg000), and store the
		result (000ggggg) back in $5C.
	$F866	Load the fifth GCR byte(ggghhhhh) from
		(BUFPNT),Y, AND it with $1F (00011111)
		to mask off the high bits (000hhhhh),
		and store in $5D
		-----------------------------------------
		At this point the 40 bits that made up
		the 5 GCR bytes have been separated into
		eight 5-bit values that correspond to
		the eight 4-bit nybbles that will make
		up the four normal binary bytes. The 8
		5-bit values are stored in $56-D. The
		following routines look up the 4-bit
		hi nybbles in GCRHI ($F8A0) and the low
		nybbles in GCRLO (starts at $F8C0)
		-----------------------------------------
	$F86D	Load .X with the first 5-bit value from
		$56, load .A with 4-bit high nybble
		from GCRHI,X, load X with a second five
		bit value from $57, OR .A with the four
		bit low nybble from GCRLO,X, and store
		the result in $52.
	$F87B	Load X with the third 5-bit value from
		$58, load .A with 4-bit high nybble from
		GCRHI,X, load X with the fourth 5-bit
		value from $59, OR .A with the 4-bit low
		nybble from GCRLO,X and store the result
		in $53.
	$F887	Load X with the fifth 5-bit value from
		$5A, load .A with 4-bit high nybble from
		from GCRHI,X, load X with the second
		five bit value from $5B, OR .A with the
		four bit low nybble from GCRLO,X, and
		store the result in $54.
	$F893	Load .X with the seventh 5 value from
		$5C, load .A with 4-bit high nybble from
		GCRHI,X, load X with the second 5-bit
		value from $5D, OR .A with the four bit
		low nybble from GCRLO,X, and store the
		result in $55.
		-----------------------------------------
		NOTE: The five bit to four bit tables below have many $FF
		entries. These are the five bit codes that are not used.
		If one of these is found, it causes a byte decoding error
		---------------------------------------------------------
		GCRHI($F8A0) & GCRLO($F8C0) Tables of 5 bit GCR to binary
		--------------------------------------
		5 bit GCR code High nybble ($F8A0+) Low nybble ($F8C0+)
		$00 00000 $FF 11111111 ERROR $FF 11111111 ERROR
		$01 00001 $FF 11111111 ERROR $FF 11111111 ERROR
		$02 00010 $FF 11111111 ERROR $FF 11111111 ERROR
		$03 00011 $FF 11111111 ERROR $FF 11111111 ERROR
		$04 00100 $FF 11111111 ERROR $FF 11111111 ERROR
		$05 00101 $FF 11111111 ERROR $FF 11111111 ERROR
		$06 00110 $FF 11111111 ERROR $FF 11111111 ERROR
		$07 00111 $FF 11111111 ERROR $FF 11111111 ERROR
		$08 01000 $FF 11111111 ERROR $FF 11111111 ERROR
		$09 01001 $80 1000---- $08 ----1000
		$0A 01010 $00   $00 ----0000
		$0B 01011 $10 0001---- $01 ----0001
		$0C 01100 $FF 11111111 ERROR $FF 11111111 ERROR
		$0D 01101 $C0 1100----  $0C ----1100
		$0E 01110 $40 0100---- $04 ----0100
		$0F 01111 $50 0101---- $05 ----0101
		$10 10000 $FF 11111111 ERROR $FF 11111111 ERROR
		$11 10001 $FF 11111111 ERROR $FF 11111111 ERROR
		$12 10010 $20 0010---- $02 ----0010
		$13 10011 $30 0011---- $03 ----0011
		$14 10100 $FF 11111111 ERROR $FF 11111111 ERROR
		$15 10101 $F0 1111---- $0F ----1111
		$16 10110 $60 0110---- $06 ----0110
		$17 10111 $70 0111---- $07 ----0111
		$18 11000 $FF 11111111 ERROR $FF 11111111 ERROR
		$19 11001 $90 1001---- $09 ----1001
		$1A 11010 SAO 1010---- $0A ----1010
		$1B 11011 $B0 1011---- $0B ----1011
		$1C 11100 $FF 11111111 ERROR $FF 11111111 ERROR
		$1D 11101 $D0 1101---- $0D ----1101
		$lE 11110 $E0 1110---- $0E ----1110
		$1F 11111 $FF 11111111 ERROR $FF 11111111 ERROR
		--------------------------------------
		GCRBIN Decode GCR data image
		This routine decoded the 69 GCR bytes
		stored in the overflow buffer ($10BB-FF)
		into normal 8-bit bytes. The decoded
		bytes are stored in a data buffer.
		-----------------------------------------
	$F8E0	Zero byte counter & lo bit of pointers
	$F8E8	Set
		lo byte of pointer, NXTBF ($4E) to
		$BA and set the hi byte NXTPNT ($4F) to
		$01 so they point to the first byte of
		the GCR image in the overflow buffer.
	$F8F0	Set SAVPNT+1 ($2F) to point to the data
		buffer where the 8-bit bytes are to be
		stored.
	$F8F4	JSR to GET4GB ($F7E6) to convert the
		first five GCR bytes into binary, the
		header block ID, the header checksum,
		the sector #, and the track #. The
		decoded bytes appear in $52-5.
	$F8F7	Store header block ID code in BID ($38)
	$F8FB	Move the three decoded bytes from $53-55
		into the buffer. Note that these bytes
		are NOT stored in the overflow buffer
		where the GCR image is stored.
GCRB10	$F90C	Transfer byte pointer from .Y into
		BYTCNT ($36).
	$F90E	JSR to GET4GB ($F7E6) to convert the
		next five GCR bytes to normal and store
		them in $52-5.
	$F913	Move decoded data byte from $52 into the
		data buffer.
	$F918	Test .Y to see if entire overflow buffer
		has been done. If done, branch to GCRB20
	$F91A	Move decoded data bytes from $53-5 into
		the data buffer.
	$F929	If .Y is not $00, there is more to do
		so branch back to GCRB10.
GCRB20	$F92B	Move header block checksum from $53
		to CHKSUM ($3A)
	$F92F	Restore buffer pointer and RTS.
		-----------------------------------------
		CONHDR Convert header to write image
		This routine creates a GCR image of a
		header block. It uses the header block
		ID code from HBID ($39) and the header
		information stored in $1A (checksum),
		$19 (sector), $18 (track), $17 (ID2),
		and $16 (ID1). A final $00 byte is used
		as a final off byte. Four of the binary
		bytes are moved into a staging area and
		the subroutine PUT4GB ($F6D0) is used to
		convert these bytes to their GCR image
		and store them in the STAB buffer($24-D)
		-----------------------------------------
	$F934	Save current value of the buffer pointer
		BUFPNT+1 ($31) in SAVPNT+1 ($2F).
	$F938	Make BUFPNT+1 ($31) point to >STAB ($00)
	$F93C	Make GCRPNT ($34) point to 127), find the absolute value using
		the 2's complement.
		Compare the number of steps to the value
		(usually $C8)in MINSTP ($64) to see if
		the distance is big enough to use the
		fast stepping mode. If the distance is
		large enough, branch to INA20 ($FA1C).
		Not big enough so set up the pointer in
INAC20	$FA1C	NXTST ($62/3) to point to the short step
DOSTEP	$FA2E	routine, SHORT ($FA3B) and branch to
STPOUT	$FA32	DOSTEP ($FA2E).
		Calculate the number of steps to do in
		fast stepping mode by subtracting the
		value in AS ($5E) from .A twice (for
		acceleration and deceleration). Store
		the result in RSTEPS ($61). Then move
		the number of steps needed for the head
		to accelerate from AS ($5E) to ACLSTP
		($60). Finally set pointer in NXTST
		($62/3) to point to the acceleration
		mode routine SSACL ($FA7B)
		Load value from STEPS ($4A). If positive
		(<127), branch to STPIN ($FA63) to step
		the head inwards.
		Increment STEPS ($4A) to reduce number
		left to do by 1, load .X with the value
		from DSKCNT ($1C00) decrement it by 1,
		and branch to STP ($FA69).
SHORT	$FA3B	Short distance head stepping.
		Load the number of steps left to do from
		STEPS ($4A). If any left, branch to
		DOSTEP ($FA2E). If not, set NXTST
		pointer($62/3) to point to the settle
		head routine SETLE ($FA4E) and store $05
SETLE	$FA4E	in ACLSTP ($60) to set the settle time.
		Branch to END33 ($FABE) to end IRQ.
		Settle head routine. Decrement ACLSTP
		($60) and if non-zero, brach to END33
		($FABE) to end IRQ. If zero, set drive
		status, DRVST ($20), to indicate that
		the drive is available for use by
		clearing bit 6. Set NXTST pointer($62/3)
		to point to the head inactive routine
		($FA05) and branch to END33 ($FABE).
STPIN	$FA63	Decrement STEPS ($4A) to reduce number
		left to do by 1, load .X with the value
		from DSKCNT($1C00) and increment it by 1
STP	$FA69	Transfer the value in .X to .A (this is
		DSKCNT+1 for a step in and DSKCNT-1 for
		a step out), AND the value with $03, and
		store it in TMP ($4B). Load DSKCNT, AND
		it with $FC to mask off bits 0 & 1, OR
		it with TMP to set the new values for
		these bits, and store the result back in
		DSKCNT. JMP to END33 ($FABE) to end IRQ.
		-----------------------------------------
		NOTE: cycling bits 0 & 1 of DSKCNT
		($1000) will move the head.
		00/01/10/11/00 will move head in
		00/11/10/01/00 will move head out
		-----------------------------------------
SSACL	$FA7B	Accelerate head routine.
		Set carry flag, load the 6522 Timerl hi
		latch T1HL2 ($lC07), subtract the value
		in AF ($5F; acceleration factor), and
		store the result in T1HC2 ($1C05; timerl
		hi counter.). Decrement the number of
		acceleration steps left in ACLSTP ($60)
		and if any steps left, branch to SSA10.
	$FA88	No steps left, so reset the number of
		acceleration steps left ACLSTP ($60)
		using the value in AS ($5E) and set the
		NXTST pointer ($62/3) to point to the
		fast stepping routine, SSRUN ($FA97).
SSA10	$FA94	JMP to DOSTEP ($FA2E)
		-----------------------------------------
SSRUN	$FA97	Fast stepping mode routine.
		Decrement number of steps left to do in
		RSTEPS ($61). If any left, branch to
		DOSTEP ($FA2E). Since none left, set the
		NXTST pointer ($62/3) to point to the
SSDEC	$FAA5	decelerate routine SSDEC ($FAA5) and
		branch to DOSTEP ($FA2E).
		-----------------------------------------
		Decelerate head routine.
		Load .A from the 6522 Timerl hi latch
		T1HL2 ($1C07), clear the carry fla g ,
		add the acceleration factor AF ($5F),
		and store the result in T1HC2 ($1C05;
		timerl hi counter). Decrement the number
		of deceleration steps left ACLSTP ($60)
		and if any steps left, branch to SSA10.
		Since no steps left, set the NXTST
		pointer ($62/3) to point to the settle
		routine, SETLE ($FA4E). Set the number
END33	$FABE	of acceleration steps left to $03 to
		allow settling time.
		Terminate the motor and stepper control
		routine by clearing bit 1 of the 6522's
		peripheral control register, PCR2($1000)
		This force CA2 low which disables the
		SO line to the 6502. Finally, do an RTS
		to transfer control back to the main
		IRQ routine at $FE7C.
		-----------------------------------------
FORMT	$FAC7	This routine is used to format (NEW) a
		diskette. The code is executed in place
		(rather than moved into RAM and then
		executed as in the 4040). The IP FORMAT
		routine ($C8C6) sets up a JMP $FAC7 at
		the start of buffer #0, puts an EXECUTE
		($E0) job into the job queue at $03, and
		then waits for the job to be completed.
FORMT	$FAC7	-----------------------------------------
		Load .A from FTNUM ($51) to check if
		formatting has begun. If FTNUM>0, the
		formatting has begun so branch to L213
		($FAF5). If not, begin formatting by:
		Setting DRVST($20) to $60 (head is now
		stepping), storing $01 into DRVTRK ($22)
		to set the current track and into FTNUM
		($51; format begun flag).
	$FAD7	Do BUMP to track 1 by stepping head out
		46 tracks. Store -92 (256-2*46) into
		STEPS ($4A) and clear bits 0 & 1 of
		DSKCNT ($1000) to set head phase to 00.
	$FAE3	Set CNT ($0620) to $0A to allow up to
		10 errors before abort.
	$FAE8	Set NUM($0621/2) to 4000 ($0FA0) as a
		first guess at number of bytes that can
		be recorded on half a track.
		Exit with a JMP to END ($F99C)
L213	$FAF5	On re-entry .A holds the track number
L214	$FB00	(loaded from FTNUM). Compare it to the
		track in HDRPNT($32). If they match, we
		are on the correct track so branch to
		L214 ($FB00). If different, put the .A
		value (track we want) into HDRPNT ($32)
		and exit with a JMP to END ($F99C).
		Test bit 4 of DSKCNT ($1C00) to see if
TOPP	$FB0C	write protect is on. If 1, protect is
	$FB0F	not on so branch to TOPP ($FB0C). If 0,
	$FB12	load .A with $08 to indicate a WRITE
		PROTECT error & JMP to FMTERR ($FDD3).
		JSR to SYNCLR ($FDA3) to erase the track
		by writing 28*256 SYNC marks.
		JSR to WRTNUM ($FDC3) to write out  NUM
		($0621/22; value = 4000) SYNC marks.
		Store a non-sync character ($55) into
		the output port DATA2 ($1C01) and JSR to
		WRTNUM ($FDC3) to write NUM ($0621/2;
		value = 4000) non-sync bytes.
		-----------------------------------------
		At this point the track will have one
		area that contains SYNC and another area
		that has non-sync characters like this:
		1111111100110011001100110011001111111
		SYNC 4000 non-sync bytes SYNC
		The following routines time the SYNC and
		non-sync segments to determine how many
		characters can be written on the track.
		This is used to calculate the length of
		the gap between sectors (inter-sector).
		-----------------------------------------
	$FB1A	JSR to KILL ($FE00) to kill write mode.
	$FB1D	JSR to SYNC ($F556) to wait for the
	$FB20	start of the SYNC section.
		Set bit 6 of the 6522's ACR1 ($180B) to
	$FB35	set it up as a free running 100 micro-
		second timer.
		Set .X and .Y to $00. They will hold the
FWAIT	$FB39	timer count. .X=least significant byte
		.Y=most significant bit
		Loop to wait for SYNC area
FWAIT2	$FB3E	Loop to wait for not-sync area
	$FB43	Reset interrupt flags to start the timer
		F000
F001	$FB46	Loop to time the non-sync area.
		Check if SYNC here yet. If here, branch
		to F005 ($FB5C). If no SYNC yet, check
		IFR1 ($1804) to see if timer has timed
		out. If time not up yet, branch back to
		F001 ($FB46). If time is up, increment
		.X by 1 (and .Y if .X=0) and branch back
		to F000 ($FB43) to reset the timer. If
		.Y is 0, we have a count of 65535 which
F005	$FB5C	means we can't find a sync mark so abort
		by loading .A with $02 and JMP to FMTERR
		Found a SYNC so store the non-sync times
F006	$FB64	in T2 ($71/2). Reset .X and .Y to $00
		and begin timing the SYNC area.
		Reset interrupt flags to start the timer
F007	$FB67	Loop to time the SYNC area:
F009	$FB7D	Check if not-sync here yet. If here, go
		to F009 ($FB7D). If still have a SYNC,
		check IFR1 ($1804) to see if timer has
		timed out. If not time yet, branch back
		to F007 ($FB67). If time up, increment
		.X by 1 (and .Y if .X=0) and loop back
		to F006 ($FB64) to reset the timer. If
		.Y is 0, we have a count of 65535 which
		means we can't find no-SYNC. So abort:
		load .A with a $02 and JMP to FMTERR
		Found non-sync. Calculate the difference
COUNT	$FBB6	between the SYNC and non-sync times. If
		the difference is less than 4, branch to
		COUNT ($FBB6). If the difference is more
		than 4, make NUM ($0261/2) the average
		of the two times and branch to TOPP
		($FB0C) to try again.
		Set .X and .Y to $00 to prepare to count
CNT10	$FBBB	the number of characters in the non-sync
		area.
		Test bit 7 of DSKCNT ($1C00) to see if
CNT20	$FBCE	SYNC is here yet. If SYNC here, branch
		to CNT20 ($FBCE). If not, test the timer
		If not time, branch back to CNT10. If
		time for one character is up, increment
		.X (and .Y if needed), clear the timer
		flag (.V) and branch back to CNT10. If
		.Y=0 we have a count of 65535 so abort:
		load .A with $03 & JMP to FMTERR ($FDD3)
		Store the byte count (count*2) in TRAL
DS08	$FBE0	($0624/5) and turn off the 6522's timer
		Calculate the total number of bytes we
	$FC36	need to record on this track:
		(282 chr/sect x 5/4 x *sect)
		Subtract this from the total we found
		and divide by the number of sectors to
		get the size of the gap between sectors.
		If the calculated gap is less than 4, it
		is too small so load .A with $05 and JMP
		to FMTERR ($FDD3). If it is big enough,
		store inter-sector gap in DTRCK ($0626).
		Set sector counter SECT ($0628) to $00.
MAK10	$FC3F	Loop to create sector header images in
		buffer 0 ($0300+) .Y is the pointer
		into the buffer (0 for sect #1).
	$FC3F	Move sector ID code from HBID ($39) to
		$0300+Y ($0300 for #1).
	$FC44	----------------------------------------------------------
	$FC4C	Increment .Y twice to skip the checksum
	$FC52	and move sector number from SECT ($0628)
	$FC58	to $0300+Y ($0302 for sector #1).
	$FC5E	Increment .Y and move the track number
	$FC64	from FTNUM ($51) to $0300+Y ($0303 for
	$FC68	sector #1)
	$FC7A	Increment .Y and move ID2 from DSKID+1
	$FC84	($13) to $0300+Y ($0304 for sector #1).
		Increment .Y and move ID1 from DSKID
		($12) to $0300+Y ($0305 for sector #1).
		Increment .Y and store $0F in $0300+Y
		($0306 for #1) as off byte.
		Increment .Y and store $0F in $0300+Y
		($0307 for #1) as off byte.
		Increment .Y, calculate the header blk
		checksum and store it in $02F9+Y
		($0302 for sector #1)
		Increment SECT ($0628) and compare it to
		number of sectors on track SECTR ($43)
		If done all images, save the number of
		sectors on this track onto the stack.
		Increment .X (becomes $01) and transfer
		it to .A (dummy data character).
		----------------------------------------------------------
		NOTE: .X should really be $00. Since it
		is $01, all the data blocks on a
		diskette formatted on a 1541 drive
		have 1 garbage character followed
		by 255 $01's rather than 256 $00's
CRTDAT	$FC86	Loop to put 255 dummy data bytes ($01's)
	$FC8E	into data buffer #2 ($0500+)
	$FC95	Set the buffer pointer BUFPNT ($30/1) to
		point to the header block images ($0300)
		and JSR to FBTOG($FE30) to convert the
		header images to a GCR write image with
		no header block ID code.
		Pull # of sectors from stack, transfer
	$FC9E	the value to .Y, and JSR to MOVUP($FDE5)
		to move the GCR header image stored in
		in buffer #0 69 bytes up in memory. Then
		JSR to MOVOVR ($FDF5) to move the 69
		header image bytes from the overflow
		buffer into the low end of buffer #0.
		Set the buffer pointer BUFPNT ($30/1) to
		point to the dummy data block, JSR to
		CHKBLK($F5E9) to calculate the data blk
		checksum, store it in CHKSUM, and JSR to
		BINGCR($F78F) to convert the dummy data
		block into its GCR write image.
		-----------------------------------------
		Begin formatting the track now!
		-----------------------------------------
WRTSYN	$FCAA	Set the pointer to the header GCR image
		$FCAE
		$FCB1
		HDRPNT ($32) to $00 so it points to the
		start of the first header image.
		JSR to CLEAR ($FE0E) to wipe the track.
		Store $FF in be ready
		PORT2 ($1C01) to
WRTS10	$FCB8	to write a sync character. Load .X with
WRTS20	$FCBE	
		$FCC2
		$05 (5 SYNC's coming up!)
		Write out 5 sync marks
		Initialize .X to $0A (output 10 bytes)
		and set .Y with the value from HDRPNT
		($32) so it points to the start of the
		header GCR image.
		Write out the 10 header characters
WRTS30	$FCCF	Load .X with $08 (HARD SET VALUE!)
	$FCD1	NOTE: This means you can not easily
		change the header gap size!
		Loop to output eight $55 bytes to form
DBSYNC	$FCDC	the header gap (gapl).
		$FCE0
		Store $FF in PORT2 ($1C01) to be ready
		to write a sync mark. Load .X with $05
		(5 SYNC ' s coming up!)
		Write out 5 sync marks
WRTS40	$FCE9	Initialize .X to $BB to point to the
	$FCEB	first byte of the overflow buffer (the
		start of the dummy data block)
		Loop to write out the 69 GCR bytes in
WRTS50	$FCF9	the overflow buffer.
		Loop to write out the 256 GCR bytes in
	$FD04	data buffer #2 ($0500+)
		Load .A with $55 and .X with the tail
		(inter-sector) gap from DTRCK ($0626)
WGP2	$FD09	Loop to write .X $55 characters to
	$FD12	form the tail (inter-sector) gap.
		Advance the header pointer HDRPNT($32/3)
	$FD19	by 10 so it points to the start of the
		next header image.
		SECT($0628)
		Decrement the sector counter
		by 1 and test to see if any more sectors
		to do. If more, branch back to WRTSYN
		to do the next sector. If no more, wait
		for the last byte to be written out and
		then JSR to KILL ($FE00) to switch to
		read mode.
		-----------------------------------------
		Formatting done. Verify it!
		-----------------------------------------
	$FD27	Set TRYS ($0623) to $C8 to limit the
COMP	$FD2C	number of attempts to verify to 200.
		Set BUFPNT ($30/1) to point to the start
		of the headers in buffer #0 ($0300) and
		set SECT ($0628) with the # of sectors
		on this track from SECTR ($43).
CMPR10	$FD39	JSR to SYNC($F556) to wait for a SYNC
		mark. Once found, set .X to $0A (there
		are 9 header characters to read) and .Y
		$00 (point to character in header image)
CMPR15	$FD40	Loop to read header bytes and compare
	$FD4E	them to the image in the buffer. If any
		byte doesn't match, branch to CMPR20.
		Header reads back OK so add 10 to BUFPNT
		($30) so it points to next header image.
	$FD55	JMP to TSTDAT ($FD62)
		-----------------------------------------
CMPR20	$FD58	Bad verify. Decrement TRYS ($0623). If
		more attempts left, branch back to COMP
		($FD2C) to try again. If we have tried
		200 times, abort: load .A with $06 and
		JMP to FMTERR ($FDD3)
		-----------------------------------------
TSTDAT	$FD62	Header OK so check the data block.
		JSR to SYNC ($F556) to wait for the data
		block SYNC mark. Once found, set .Y to
		$BB to point to the start of the data
		block image in the overflow buffer.
TST05	$FD67	Loop to read and verify the 69 GCR bytes
		in the overflow buffer. If no match,
		branch to CMPR20 ($FD58) and try again.
	$FD75	Overflow buffer OK so set .X to $FC
		(255-3; don't bother checking the OFF
		bytes at the end).
TST10	$FD77	Loop to read and verify the 253 GCR
		bytes in data buffer #3. If no match,
		branch to CMPR20 ($FD58) and try again.
	$FD86	Decrement the sector counter in SECT
		($0628) by 1 and test to see if any more
		to do. If more, branch back to CMPR10 to
		do next sector. If no more, increment
		the track counter FTNUM ($51) and test
		if there are any more tracks to do. If
		all done, branch to FMTEND ($FD96). If
		more to do, JMP to END ($F99C) to step
		the head to the next track.
		-----------------------------------------
FMTEND	$FD96	Set the track counter, FTNUM ($51) to
		$FF and the GCRFLG ($50) to O. To flag
		a successful completion load .A with $01
		and JMP to ERRR ($F969).
		-----------------------------------------
		Formatting and Verification Completed!
		-----------------------------------------
		Formatting Subroutines
		-----------------------------------------
SYNCLR	$FDA3	Wipe track by writing 40*256 SYNC marks
		Set bits 6 & 7 of the 6522's peripheral
		control register PCR2 ($1C0C). This
		latches the signal on the CB2 line.
SYC10	$FDAD	Store $FF in the data direction register
	$FDB5	DDRA2 ($1C03) to make PORT A an output
	$FDB9	port and put $FF in the data port DATA2
		($lC01) to produce SYNC characters.
		Initialize .X to $28 (hi counter) and
		.Y to $00 (lo counter).
		Loop to write out 40*256 SYNC marks
	$FDC2	using .X & .Y as counters
		RTS -*- WARNING WRITE MODE LEFT ON -*-
		-----------------------------------------
		WRTNUM Write out. NUM ($0621/2) bytes
WRTN10	$FDC3	Load .X with the LSB and .Y with the
	$FDC9	MSB of NUM ($0621/2).
		Loop to write out what ever is in the
	$FDD2	data port DATA2. ($1C03) NUM times using
		.X and .Y as counters
		RTS
		-----------------------------------------
		FMTERR Handles format errors
FMTE10	$FDD3	Decrement the retry counter CNT ($0620)
	$FDDB	and, if no tries left, branch to FMTE10.
		If any left, JMP to END($F99C) to do any
		stepping required and try again.
		Set the track counter FTNUM ($51) to $FF
		and the GCRFLG ($50) to 0 and JMP to
		ERRR ($F969).
		-----------------------------------------
		MOVUP Move .Y bytes in buffer #0 up 69 bytes
MOVOVR	$FDE5	Loop to move .Y characters in buffer #0
	$FDEE	($0300+) up 69 memory locations in RAM.
		Move byte from $0300 to $0345. RTS
		Move 69 bytes from overflow buffer into
	$FDF5	the bottom of the data buffer pointed
	$FDF7	to by BUFPNT ($30/1)
		Load .Y with $44 (68)
		Loop to move 69 bytes from $01BB+ into
		the data buffer. RTS
		-----------------------------------------
		KILL Disable write mode
	$FE00	Set bits 5, 6 and 7 of the 6522's PCR2
		($1C0C) to set CB2 high. Store 0 in the
		data direction register. DDRA2 ($lC03)
		to make PORT A an input port. RTS
		-----------------------------------------
		CLEAR Wipe track with non-sync characters
	$FE0E	Clear (zero) bit 5 of the 6522's PCR2
	$FE18	($1C0C). This forces CB2 low.
		Store $FF in the data direction register
	$FE22	DDRA2 ($lC03) to set output mode and put
		$55 in the data port DATA2 ($lC01) to
		write non-sync characters.
		Initialize .X to $28 (hi counter) and
		.Y to $00 (lo counter).
CLER10	$FE26	Loop to write out 40*256 non-sync
	$FE2F	characters using .X & .Y as counters.
		RTS -*- WARNING WRITE MODE LEFT ON -*-
		-----------------------------------------
FBTOG	$FE30	----------------------------------------------------------
FBG10	$FE30	Convert header images in buffer #0 into
	$FE38	GCR form without the header ID code.
	$FE3C	Zero the low byte of the buffer pointers
	$FE44	pointers BUFPNT($31) and SAVPNT ($2E)
		and the byte counter BYTCNT ($36).
		Set the GCR pointer GCRPNT ($34) to $BB
		so it points to the first character in
		the overflow buffer ($01BB+).
		Save the hi byte of the buffer pointer
		BUFPNT ($31) into SAVPNT ($2F) and then
		set BUFPNT to $01 to point to the over-
		flow buffer.
		Loop to move 4 bytes at a time into the
		staging area $52-55 and then do a JSR
		to PUT4BG ($F6D0) to convert them into
		five GCR bytes and store them in the
		overflow or data buffer. Terminate the
		routine with a JMP to PUT4BG to convert
		and store the last four.
		--------------------------------------
		MAIN SYSTEM IRQ ROUTINE (IRQ VECTOR POINTS HERE)
		--------------------------------------
SYSIRQ	$FE67	IRQ's are generated in two ways:
		1) by an ATN signal from the VIC-20
		or the C-64 on the serial bus, or
		2) by a time out of the 6522's timer
		This happens every 10 milliseconds
		This routine tests for the source of the
		IRQ signal and branches to the correct
		ROM routine.
		-----------------------------------------
	$FE67	Save .A, .X, and .Y on the stack
	$FE6C	Test if IRQ caused by an ATN signal on
		the serial bus by checking bit 1 of the
		interrupt flag register of the 6522 that
		handles the bus IFR1 ($180D). If this
		bit is not set (1), there was no ATN
		signal so branch to IRQ10 ($FE76). If it
		is set, JMP to the bus handling routine
		ATNIRQ ($E85F).
IRQ10	$FE76	Test if the 6522 timer has timed out by
		testing bit 7 of the interrupt flag
		register of the 6522 that serves as a
		disk controller IFR2 ($1C0D). If the bit
		is not set, branch to IRQ20 ($FE7F). If
		it is set, do a JSR to the floppy disk
		controller routines, LCC($F2B0).
IRQ20	$FE7F	Pull .A, .X, and .Y from the stack and
		do an RTI.
		--------------------------------------
		MISCELLANEOUS CONSTANTS & TABLES IN ROM
		--------------------------------------
	$FE85	$12 Directory track number (18)
	$FE86	$04 Number of bytes/track in BAM
	$FE87	$04 Offset of BAM in the sector
	$FE88	$90 Offset of disk name in BAM sector
		--------------------------------------
		Command Search Table
		--------------------------------------
	$FE89	$56 V = Validate or collect disk
	$FE8A	$49 I = Initialize BAM & directory
	$FE8B	$44 D = Duplicate or backup disk (N.A.)
	$FE8C	$4D M = Memory operation (M-R,M-W,M-E)
	$FE8D	$42 B = Block operation (B-R,B-A,B-W,etc)
	$FE8E	$55 U = User jump commands (except U+ & U-)
	$FE8F	$50 P = Position (for REL files)
	$FE90	$26 & = Utility loader
	$FE91	$43 C = Copy file (copy disk N.A. on 1541)
	$FE92	$52 R = Rename file
	$FE93	$53 S = Scratch file
	$FE94	$4E N = New or format a diskette
		--------------------------------------
		(Lo Byte) (Hi Byte) Command Jump Table
		--------------------------------------
	$FE95	$84 $FEA1 $ED V = Validate
	$FE96	$05 $FEA2 $D0 I = Initialize BAM
	$FE97	$C1 $FEA3 $C8 D = Duplicate (N.A.)
	$FE98	$F8 $FEA4 $CA M = Memory operation
	$FE99	$1B $FEA5 $CC B = Block operation
	$FE9A	$5C $FEA6 $CB U = User jump commands
	$FE9B	$07 $FEA7 $E2 P = Position (for REL)
	$FE9C	$A3 $FEA8 $E7 & = Utility loader.
	$FE9D	$F0 $FEA9 $C8 C = Copy file
	$FE9E	$88 $FEAA $CA R = Rename file
	$FE9F	$23 $FEAB $C8 S = Scratch file
	$FEA0	$0D $FEAC $EE N = New a diskette
		--------------------------------------
		STRUCTURE IMAGES FOR COMMANDS
		--------------------------------------
	$FEAD	$51 %01010001 disk copy
	$FEAE	$DD %11011101 rename a file (not parsed)
	$FEAF	$1C %00011100 scratch a file (not parsed)
	$FEB0	$9E %10011110 new a diskette (not parsed)
	$FEB1	$1C %00011100 load a file
		     --------
		     PGDRPGDR Not greater than one file
		     FS1 FS2  Not default drive(s)
		              Required filename
		--------------------------------------
		MODE TABLE (R/W/A/M)
		--------------------------------------
	$FEB2	$52 R = Read mode
	$FEB3	$57 W = Write mode
	$FEB4	$41 A = Append
	$FEB5	$4D M = Modify (read improperly closed file)
		--------------------------------------
		(1st Byte) (Hi Byte) File type table
		--------------------------------------
	$FEB6	$44 D $FEBB $44 D $FEC0 $45 E $FEC5 $4C L DEL
	$FEB7	$53 S $FEBC $53 S $FEC1 $45 E $FEC6 $51 Q SEQ
	$FEB8	$50 P $FEBD $50 P $FEC2 $52 R $FEC7 $47 G PRG
	$FEB9	$55 U $FEBE $55 U $FEC3 $53 S $FEC8 $52 R USR
	$FEBA	$4C L $FEBF $52 R $FEC4 $45 E $FEC9 $4C L REL
		--------------------------------------
	$FECA	$08 LED mask for drive 0
	$FECB	$00 LED mask for drive 1 (N.A. on 1541)
		--------------------------------------
		ERROR FLAG VARIABLES FOR USE BY BIT
		--------------------------------------
	$FECC	$00 ER00
	$FECD	$3F ERO
	$FECE	$7F ER1
	$FECF	$BF ER2
	$FED0	$FF ER3
		--------------------------------------
		NUMBER OF SECTORS/TRACK IN EACH ZONE
		--------------------------------------
	$FED1	$11 17 sectors/track in zone 4 (31-35)
	$FED2	$12 18 sectors/track in zone 3 (25-30)
	$FED3	$13 19 sectors/track in zone 2 (18-24)
	$FED4	$15 21 sectors/track in zone 1 (01-17)
		--------------------------------------
	$FED5	$41 DOS version number (65)
	$FED6	$04 Number of different zones
		--------------------------------------
		ZONE BOUNDARIES (HIGHEST TRACK # + 1)
		--------------------------------------
	$FED7	$24 Track #36 - end of zone 4 (31-35)
	$FEDB	$1F Track #31 - end of zone 3 (25-30)
	$FED9	$19 Track #25 - end of zone 2 (18-24)
	$FEDA	$12 Track #18 - end of zone 1 (01-17)
		--------------------------------------
		OFFSETS FOR ERROR RECOVERY
		--------------------------------------
	$FEDB	$01
	$FEDC	$FF
	$FEDD	$FF
	$FEDE	$01
	$FEDF	$00
		--------------------------------------
		HI BYTE OF POINTERS TO DATA BUFFERS
		--------------------------------------
	$FEE0	$03 Data buffer #0 ($0300-03FF)
	$FEE1	$04 Data buffer #1 ($0400-04FF)
	$FEE2	$05 Data buffer #2 ($0500-05FF)
	$FEE3	$06 Data buffer #3 ($0600-06FF)
	$FEE4	$07 Data buffer #4 ($0700-07FF)
	$FEE5	$07 Data buffer #5 ($0700-07FF)
		--------------------------------------
	$FEE6	$FD Checksum for $E and $F ROMs
		--------------------------------------
		NMI VECTOR POINTS HERE
		--------------------------------------
NMI	$FEE7	Do indirect jump to the address stored
		in VNMI ($0065). This vector points to
		XXXXXX ($XXXX)
		--------------------------------------
		PATCH FOR POWER-ON ERRORS
		--------------------------------------
PEA7A	$FEEA	Store the value that is in .A on entry
		into the 6522's data port 2, LEDPRT
		($1000; also called DSKCNT) and in the
		data direction register, LEDOUT ($lC02;
		also called DDRB2). Exit with a JMP to
		REA7D ($EA7D) to return to the LED blink
		routine.
		--------------------------------------
		PATCH FOR 1541 DISK WITH SLOW SERIAL RECEIVE
		--------------------------------------
SLOWD	$FEF3	Produce a 40 microseconds delay with a
		loop that counts .X down from 5 to 1.
		Exit with an RTS.
		--------------------------------------
	$FEFB	JSR $E9AE unused junk
	$FEFE	JMP $E99C unused junk
		--------------------------------------
		PATCH TO NMI ROUTINE TO CHECK FOR U+ AND U- COMMANDS
		--------------------------------------
NNMI	$FF01	Load .A with the second character in the
		command buffer CMDBUF+2 ($0202). Compare
		it with "-" and, if equal, branch to
		NNMI10 ($FF0D). If not a "-", subtract
		a "+" from it. If not zero, command must
		be a real UI command so branch back to
		NMI ($FEE7) to do normal NMI.
NNMI10	$FF0D	Store .A (contains zero or a "-") into
		DRVTRK+1 ($23) and do an RTS to continue
		--------------------------------------
	$FF10	- $FFE6 UNUSED GARBAGE
		--------------------------------------
		TABLE OF JUMP VECTORS TO ROUTINES (LO BYTE/HI BYTE)
		--------------------------------------
	$FFE6	$C6/$C8 FORMAT ROM routine $C8C6
	$FFE8	$8F/$F9 TRNOFF ROM routine $F98F
	$FFEA	$5F/$CD UBLKRD ROM routine $CD5F
	$FFEC	$97/$CD UBLKWT ROM routine $CD97
	$FFEE	$00/$05 Link to buffer #2 $0500
	$FFF0	$03/$05 Link to buffer #2 $0503
	$FFF2	$06/$05 Link to buffer #2 $0506
	$FFF4	$09/$05 Link to buffer #2 $0509
	$FFF6	$0C/$05 Link to buffer #2 $050C
	$FFF8	$0F/$05 Link to buffer #2 $050F
	$FFFA	$01/$FF NNMI ROM routine $FF01
	$FFFC	$A0/$EA DSKINT ROM routine $EAA0
	$FFFE	$67/$FE SYSIRQ ROM routine $FE67
		--------------------------------------

Note: This file was OCR'd from a PDF version of Inside Commodore DOS (by Richard Immers and Gerald G Neufeld). Beware that as part of the conversion process, some labels and address prefixes may not align correcly with the corresponding comment fields. I've fixed most of them, but please send me corrections for any others.

Redisplay page as HTML or redisplay page as Plain Text


Mailmark@faime.demon.co.uk