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