--- name: disk-io-1541 description: > Use this skill for all 1541 disk drive programming on the Commodore 64. Covers DOS commands, file types (sequential, relative, program), direct access, error handling, the BAM, directory structure, and disk utilities. Sources: The Anatomy of the 1541 Disk Drive (Revised Edition), COMPUTE!'s Mapping the C64. --- # 1541 Disk Drive Programming ## Overview The **VIC-1541** disk drive is a single-sided, single-density (SS/SD) 5.25" floppy drive connected to the C64 via the **serial bus** (IEC bus). It contains its own 6502 CPU and 2KB of RAM. - **Device number**: Default 8 (configurable 8-11) - **Disk capacity**: 170KB (664 blocks free on a formatted disk) - **Tracks**: 35 (numbered 1-35) - **Sectors per track**: 17-21 (varies by zone) - **Block size**: 254 usable bytes per sector (2 bytes used as track/sector link) - **Communication**: Via Kernal's serial bus routines ### Track/Sector Layout | Tracks | Sectors/Track | Cumulative Sectors | |--------|---------------|--------------------| | 1-17 | 21 | 0-356 | | 18-24 | 19 | 357-490 | | 25-30 | 18 | 491-598 | | 31-35 | 17 | 599-682 | - **Directory track**: Track 18 (always) - **BAM**: Track 18, Sector 0 --- ## Logical File Model Every disk operation goes through **logical files**. The steps are: 1. `SETLFS` — assign logical file number, device, secondary address 2. `SETNAM` — assign filename 3. `OPEN` — open the file 4. `CHKIN`/`CHKOUT` — redirect input/output to the file 5. `BASIN`/`BSOUT` — read/write data 6. `CLOSE` — close the file From BASIC, use: `OPEN LFN, DEV, SA, "FILENAME"` --- ## File Types | Type | SA Range | Description | |------|----------|-------------| | Program | 0-1 | Machine language or BASIC programs | | Sequential | 2 | Sequential data files (read/write, not both at once) | | Relative | any+SA | Fixed-length record random-access files | | Command | 15 | Command channel (send DOS commands, read error) | --- ## BASIC Disk Operations ### Load and Save Programs ```basic LOAD "FILENAME",8 ' Load BASIC program LOAD "FILENAME",8,1 ' Load at original address (ML programs) SAVE "FILENAME",8 ' Save BASIC program SAVE "@:FILENAME",8 ' Save and overwrite existing file VERIFY "FILENAME",8 ' Verify saved file matches memory ``` ### Directory ```basic LOAD "$",8 ' Load directory as BASIC program LIST ' Display directory ``` ### Sending DOS Commands ```basic OPEN 15,8,15,"COMMAND" ' Open command channel with command CLOSE 15 ' Close command channel ``` ### Reading the Error Channel ```basic 10 OPEN 15,8,15 : REM Open command channel 20 INPUT#15,EN,EM$,ET,ES : REM Read error: number,message,track,sector 30 PRINT EN;EM$;ET;ES 40 CLOSE 15 ``` The error channel should be read after every disk operation. --- ## DOS Commands ### Formatting ```basic OPEN 15,8,15,"N0:DISKNAME,ID" : CLOSE 15 ' N0: = NEW on drive 0; ID = 2-char disk ID (e.g., "A1") ' WARNING: Destroys all data on disk ``` ### Scratching (Deleting) Files ```basic OPEN 15,8,15,"S0:FILENAME" : CLOSE 15 ' Wildcards supported: S0:TEST* deletes all files starting with TEST ``` ### Renaming Files ```basic OPEN 15,8,15,"R0:NEWNAME=OLDNAME" : CLOSE 15 ``` ### Copying Files ```basic OPEN 15,8,15,"C0:DEST=0:SOURCE" : CLOSE 15 ``` ### Validating Disk (clean up) ```basic OPEN 15,8,15,"V0:" : CLOSE 15 ' Reclaims space from improperly closed files ``` ### Initializing Disk ```basic OPEN 15,8,15,"I0:" : CLOSE 15 ' Re-reads BAM; use after swapping disks ``` ### Wildcards - `*` — matches any sequence of characters - `?` — matches any single character ```basic OPEN 15,8,15,"S0:TEST*" : CLOSE 15 ' scratch all TEST files ``` --- ## Sequential File Access Sequential files are read or written **in order only** (not random access). ### Writing a Sequential File ```basic 10 OPEN 1,8,2,"MYDATA,S,W" ' LFN=1, DEV=8, SA=2, write sequential 20 PRINT#1,"LINE 1 DATA" 30 PRINT#1,"LINE 2 DATA" 40 PRINT#1,NUMBER ' Numbers also work 50 CLOSE 1 ``` ### Reading a Sequential File ```basic 10 OPEN 1,8,2,"MYDATA,S,R" ' SA=2, read sequential 20 IF ST<>0 THEN 60 ' ST = status byte; non-zero = EOF/error 30 INPUT#1,LINE$ 40 PRINT LINE$ 50 GOTO 20 60 CLOSE 1 ``` ### Appending to a Sequential File ```basic OPEN 1,8,2,"MYDATA,S,A" ' SA=2 with ,A flag appends ``` --- ## Relative File Access Relative files provide **random access** to fixed-length records. ### Creating a Relative File ```basic 10 OPEN 2,8,2,"MYREL,L," + CHR$(RECLEN) ' RECLEN = record length in bytes (1-254) ``` ### Positioning the Record Pointer ```basic 20 OPEN 15,8,15 30 RN = 5 ' desired record number (1-based) 40 LO = (RN-1) AND 255 50 HI = INT((RN-1)/256) 60 PRINT#15,"P" CHR$(2) CHR$(LO) CHR$(HI) CHR$(0) ' Channel 2 (the data file's SA), then record# low, high, byte offset ``` ### Reading/Writing Records ```basic ' After positioning: PRINT#2, FIELD1$, FIELD2$ ' write INPUT#2, FIELD1$, FIELD2$ ' read (re-position first) ``` --- ## Error Codes | Code | Message | Meaning | |------|---------|---------| | 00 | OK | No error | | 01 | FILES SCRATCHED | Normal after SCRATCH | | 20 | READ ERROR | Block header not found | | 21 | READ ERROR | No sync character found | | 22 | READ ERROR | Data block not found | | 23 | READ ERROR | Checksum error in data | | 24 | READ ERROR | Byte decoding error | | 25 | WRITE ERROR | Write verify error | | 26 | WRITE PROTECT ON | Write-protect tab covered | | 27 | READ ERROR | Checksum error in header | | 28 | WRITE ERROR | Long data block | | 29 | DISK ID MISMATCH | Wrong disk in drive | | 30 | SYNTAX ERROR | DOS command syntax error | | 31 | SYNTAX ERROR | Invalid command | | 32 | SYNTAX ERROR | Long line | | 33 | SYNTAX ERROR | Invalid filename | | 34 | SYNTAX ERROR | No file given | | 39 | SYNTAX ERROR | Invalid command | | 50 | RECORD NOT PRESENT | Past end of relative file | | 51 | OVERFLOW IN RECORD | Wrote past end of record | | 52 | FILE TOO LARGE | Relative file too large | | 60 | WRITE FILE OPEN | File still open for writing | | 61 | FILE NOT OPEN | File not open | | 62 | FILE NOT FOUND | File does not exist | | 63 | FILE EXISTS | File already exists (overwrite with @) | | 64 | FILE TYPE MISMATCH | Wrong file type for operation | | 65 | NO BLOCK | Block already allocated | | 66 | ILLEGAL TRACK OR SECTOR | Bad T/S reference | | 67 | ILLEGAL SYSTEM T OR S | Bad system track/sector | | 70 | NO CHANNEL | No free logical channel | | 71 | DIR ERROR | Directory error (use VALIDATE) | | 72 | DISK FULL | No free blocks | | 73 | CBM DOS Vxx.x | Power-on/INITIALIZE message | | 74 | DRIVE NOT READY | No disk or drive error | --- ## Direct Block Access The 1541 allows reading/writing individual sectors directly. ### Block-Read ```basic OPEN 15,8,15 OPEN 2,8,2,"#" ' open direct-access channel PRINT#15,"B-R:2 0 " TRACK " " SECTOR ' Read into drive buffer; then read bytes via GETIN or INPUT# ``` ### Block-Write ```basic ' Write buffer to disk after filling via BSOUT: PRINT#15,"B-W:2 0 " TRACK " " SECTOR ``` ### Block-Pointer (set position within buffer) ```basic PRINT#15,"B-P:2 " POS ' set byte position in buffer (0-255) ``` --- ## Directory Structure on Disk **Track 18, Sector 0 = BAM (Block Availability Map)** BAM format: - Bytes 0-1: Track/sector of first directory block (18/1) - Byte 2: DOS version ('A') - Byte 3: unused - Bytes 4-143: BAM entries for each track (4 bytes per track × 35 tracks): - Byte 0: free sector count for this track - Bytes 1-3: bit map of free sectors (1=free, 0=used) - Bytes 144-161: Disk name (padded with $A0) - Bytes 162-163: Disk ID - Byte 164: $A0 - Bytes 165-166: DOS version bytes ("2A") **Track 18, Sectors 1-18 = Directory** Each directory sector holds **8 directory entries** (32 bytes each): - Byte 0: Track link to next directory sector (0 if none) - Byte 1: Sector link to next directory sector - For each of 8 entries starting at byte 2 (or 0 for entry 0): - Byte 0: File type ($80=DEL, $81=SEQ, $82=PRG, $83=USR, $84=REL; OR $40=locked, $60=opened) - Bytes 1-2: Starting track/sector - Bytes 3-18: Filename (padded with $A0) - Bytes 19-20: Side-sector block track/sector (relative files only) - Byte 21: Record length (relative files only) - Bytes 22-25: Unused - Bytes 26-27: Track/sector of replacement file (during @-SAVE) - Bytes 28-29: File size in blocks (low, high) --- ## Assembly Language Disk I/O ```asm ; Open logical file 1 to disk drive 8, secondary address 2, filename "DATA" LDA #1 ; logical file number LDX #8 ; device (disk) LDY #2 ; secondary address JSR $FFBA ; SETLFS LDA #4 ; filename length LDX #FNAME JSR $FFBD ; SETNAM JSR $FFC0 ; OPEN BCS error ; carry set = error LDX #1 ; logical file number JSR $FFC9 ; CHKOUT - redirect output to file LDA #65 ; character 'A' JSR $FFD2 ; BSOUT - write to file JSR $FFCC ; CLRCH - restore default channels LDA #1 JSR $FFC3 ; CLOSE FNAME .TEXT "DATA" ```