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
mark@faime.demon.co.uk