This manual describes Commodore DOS on FAT32, aka CMDR-DOS.
Commander X16 duplicates and extends the programming interface used by Commodore's line of disk drives, including the famous (or infamous) VIC-1541. CMDR-DOS uses the industry-standard FAT-32 format. Partitions can be 32MB up to (in theory) 2TB and supports CMD-style partitions, subdirectories, timestamps and filenames up to 255 characters. It is the DOS built into the Commander X16.
There are three basic interfaces for CMDR-DOS: the binary interface (LOAD, SAVE, etc.), the data file interface (OPEN, PRINT#, INPUT#, GET#), and the command interface. We will give a brief summary of BASIC commands here, but please refer to the BASIC chapter for full syntax of each command.
If you are familiar with the SD2IEC or the CMD hard drive, navigating partitions and subdirectories is similar, with "CD", "MD", and "RD" commands to navigate directories.
The primary use of the binary interface is loading and saving program files and loading binary files into RAM.
Your binary commands are LOAD, SAVE, BLOAD, VLOAD, BVLOAD, VERIFY, and BVERIFY.
This is a brief summary of the LOAD and SAVE commands. For full documentation, refer to Chapter 3: BASIC Programming.
LOAD <filename> [,device][,secondary_address][,start_address]
This reads a program file from disk. The first two bytes of the file are the memory location to which the file will be loaded, with the low byte first. BASIC programs will start with $01 $08, which translates to $0801, the start of BASIC memory. The device number should be 8 for reading from the SD card.
secondary_address has multiple meanings:
0 or not present: load the data to address $0801, regardless of the address header.
1: load to the address specified in the file's header
2: load into VERA RAM bank 0 (at the 16-bit address in the file)
3: load into VERA RAM bank 1 (at the 16-bit address in the file)
start_address is the location to read your data into. If you need to relocate your data to banked RAM, for example, you will want to set the address to $A000 or higher.
Examples:
LOAD "ROBOTS.PRG",8,1
loads the program "ROBOTS.PRG" into memory at the address encoded in the file.
LOAD "HELLO",8
loads a program to the start of BASIC at $0801.
LOAD "*",8,1
loads the first program on the current directory. See the section below on wildcards for more
information about using * and ? to access files of a certain type or with unprintable characters.
LOAD "DATA.BIN",8,1,$A000
loads a file into banked RAM, starting at $A000. Don't forget to set the bank first: bank
0 is used by the operating system, so
SAVE <filename>[,device]
Save a file from the computer to the SD card. SAVE always reads from the beginning of BASIC memory at $0801, up to the end of the BASIC program. Device is optional and defaults to 8 (the SD card, or an IEC disk drive, if one is plugged in.)
One word of caution: CMDR-DOS will not let you overwrite a file by default. To overwrite a file, you need to prefix the filename with @:, like this:
SAVE "@:DEMO.PRG"
You may need to save arbitrary binary data from other locations. To do this, use the S command in the MONITOR: Chapter 6: Machine Language Monitor.
S "filename",8,<start_address>,<end_address>
Where
It is also a good idea to run the DOS command after a save. The Commodore model does not report certain failures back to BASIC, so you should double-check the result after a write operation.
DOS
00, OK,00,00
READY.
An OK reply means the file saved correctly. Any other result is an error that should be addressed:
DOS
63,FILE EXISTS,00,00
CMDR-DOS does not allow files to be overwritten without special handling. If you get FILE EXISTS, either change your file's name or save it with the @: prefix, like this:
SAVE "@:HELLO"
BLOAD loads a file without an address header to an arbitrary location in memory. Usage is similar to LOAD. However, BLOAD does not require or use the 2-byte header. The first byte in the file is the first byte loaded into memory.
BLOAD "filename",8,<bank>,<start_address>
Read binary data into VERA. VLOAD skips the 2-byte address header and starts reading at the third byte of the file.
VLOAD "filename",8,<bank>,<start_address>
Read binary data into VERA without a header. This works like BLOAD, but into VERA RAM.
VLOAD "filename",8,<bank>,<start_address>
Sequential files have two basic modes: read and write. The OPEN command opens a file for reading or writing. The PRINT# command writes to a file, and the GET# and INPUT# commands read from the file.
todo: examples
The command channel allows you to send commands to the CMDR-DOS interface. You can open and write to the command channel using the OPEN command, or you can use the DOS command to issue commands and read the status. While DOS can be used in immediate mode or in a program, only the combination of OPEN/INPUT# can read the command response back into a variable for later processing.
In either case, the ST psuedo-variable will allow you to quickly check the status. A status of 64 is "okay", and any other value should be checked by reading the error channel (shown below.)
To open the command channel, you can use the OPEN command with secondary address 15.
10 OPEN 15,8,15
If you want to issue a command immediately, add your command string at the end of the OPEN statement:
10 OPEN 15,8,15, "CD:/"
This example changes to the root directory of your SD card.
Now you can check your status by reading ST:
20 IF ST=64 THEN PRINT "OK": GOTO 50
To actually read the error channel and clear the error status, you need to read four values:
30 INPUT#15,A,B$,C,D
A is the error number. B$ is the error message. C and D are unused in CMDR-DOS, but will return the track and sector when used with a disk drive on the IEC connector.
40 PRINT A;B$;C;D
50 CLOSE 15
So the entire program looks like:
10 OPEN 15,8,15, "CD:/"
20 IF ST=64 THEN PRINT "OK": GOTO 50
30 INPUT#15,A,B$,C,D
40 PRINT A;B$;C;D
50 CLOSE 15
You can also use the DOS command to send a command to CMDR-DOS. Entering DOS by itself will print the drive's status on the screen. Entering a command in quotes or a string variable will execute the command. We will talk more about the status variable and DOS status message in the next section.
DOS
00, 0K, 00, 00
READY.
DOS "CD:/"
The special case of DOS "{{markdown}}quot;
will print a directory listing.
DOS "{{markdown}}quot;
You can also read the name of the current directory with DOS"$=C"
DOS "$=C"
This is the base features set compared to other Commodore DOS devices:
Feature | 1541 | 1571/1581 | CMD HD/FD | SD2IEC | CMDR-DOS |
---|---|---|---|---|---|
Sequential files | yes | yes | yes | yes | yes |
Relative files | yes | yes | yes | yes | not yet |
Block access | yes | yes | yes | yes | not yet |
Code execution | yes | yes | yes | no | yes |
Burst commands | no | yes | yes | no | no |
Timestamps | no | no | yes | yes | yes |
Time API | no | no | yes | yes | not yet |
Partitions | no | no | yes | yes | yes |
Subdirectories | no | no | yes | yes | yes |
It consists of the following components:
main.s
: TALK/LISTEN dispatchingparser.s
: filename/path parsingcmdch.s
: command channel parsing, status messagesfile.s
: file read/writematch.s
: FAT32 character set conversion, wildcard matchingdir.s
: FAT32 directory listingfunction.s
: command implementations for FAT32fat32/*
: FAT32 for 65c02 libraryAll currently unsupported commands are decoded in cmdch.s
anyway, but hooked into 31,SYNTAX ERROR,00,00
, so adding features should be as easy as adding the implementation.
CMDR-DOS implements the TALK/LISTEN layer (Commodore Peripheral Bus layer 3), it can therefore be directly hooked up to the Commodore IEEE KERNAL API (talk
, tksa
, untlk
, listn
, secnd
, unlsn
, acptr
, ciout
) and be used as a computer-based DOS, like on the C65 and the X16.
CMDR-DOS does not contain a layer 2 implementation, i.e. IEEE-488 (PET) or Commodore Serial (C64, C128, ...). By adding a Commodore Serial (aka "IEC") implementation, CMDR-DOS could be adapted for use as the system software of a standalone 65c02-based Serial device for Commodore computers, similar to an sd2iec device.
The Commodore DOS side and the FAT32 side are well separated, so a lot of code could be reused for a DOS that uses a different filesystem.
Or the core feature set, these are the supported functions:
Feature | Syntax | Supported | Comment |
---|---|---|---|
Reading | ,?,R |
yes | |
Writing | ,?,W |
yes | |
Appending | ,?,A |
not yet | |
Recovery | ,?,M |
no | not useful on FAT32 |
Types | ,S /,P /,U /,L |
yes | ignored on FAT32 |
Overwriting | @: |
yes | |
Magic channels 0/1 | yes | ||
Channel 15 command | command: args... |
yes | |
Channel 15 status | code, string, a, b |
yes | |
CMD partition syntax | 0: /1: /... |
yes | |
CMD subdirectory syntax | //DIR/: //DIR/: |
yes | |
Directory listing | $ |
yes | |
Dir with name filtering | $:FIL* |
yes | |
Dir with type filtering | $:*=P /$:*=D /$:*=A |
yes | |
Dir with timestamps | $=T |
yes | but with ISO syntax |
Dir with time filtering | $=T< /$=T< |
not yet | |
Partition listing | $=P |
yes | |
Partition filtering | $:NAME*=P |
no | |
List Current Directory | $=C |
yes |
And this table shows which of the standard commands are supported:
Name | Syntax | Description | Supported |
---|---|---|---|
BLOCK-ALLOCATE | B-A medium medium track sector |
Allocate a block in the BAM | no1 |
BLOCK-EXECUTE | B-E channel medium track sector |
Load and execute a block | not yet |
BLOCK-FREE | B-F medium medium track sector |
Free a block in the BAM | no1 |
BLOCK-READ | B-R channel medium track sector |
Read block | no1 |
BLOCK-STATUS | B-S channel medium track sector |
Check if block is allocated | no1 |
BLOCK-WRITE | B-W channel medium track sector |
Write block | no1 |
BUFFER-POINTER | B-P channel index |
Set r/w pointer within buffer | not yet |
CHANGE DIRECTORY | CD [path]: name |
Change the current sub-directory | yes |
CHANGE DIRECTORY | CD [medium]:← |
Change sub-directory up | yes |
CHANGE PARTITION | CP num |
Make a partition the default | yes |
COPY | C [path_a]: target_name= [path_b]: source_name[, ...] |
Copy/concatenate files | yes |
COPY | C dst_medium= src_medium |
Copy all files between disk | no1 |
DUPLICATE | D: dst_medium= src_medium |
Duplicate disk | no1 |
FILE LOCK | F-L [path]: name[, ...] |
Enable file write-protect | yes |
FILE RESTORE | F-R [path]: name[, ...] |
Restore a deleted file | not yet |
FILE UNLOCK | F-U [path]: name[, ...] |
Disable file write-protect | yes |
GET DISKCHANGE | G-D |
Query disk change | yes |
GET PARTITION | G-P num |
Get information about partition | yes |
INITIALIZE | I [medium] |
Re-mount filesystem | yes |
LOCK | L [path]: name |
Toggle file write protect | yes |
MAKE DIRECTORY | MD [path]: name |
Create a sub-directory | yes |
MEMORY-EXECUTE | M-E addr_lo addr_hi |
Execute code | yes |
MEMORY-READ | M-R addr_lo addr_hi [count] |
Read RAM | yes |
MEMORY-WRITE | M-W addr_lo addr_hi count data |
Write RAM | yes |
NEW | N [medium]: name, id,FAT32 |
File system creation | yes3 |
PARTITION | / [medium][: name] |
Select 1581 partition | no |
PARTITION | / [medium]: name, track sector count_lo count_hi ,C |
Create 1581 partition | no |
POSITION | P channel record_lo record_hi offset |
Set record index in REL file | not yet |
REMOVE DIRECTORY | RD [path]: name |
Delete a sub-directory | yes |
RENAME | R [path]: new_name= old_name |
Rename file | yes |
RENAME-HEADER | R-H [medium]: new_name |
Rename a filesystem | yes |
RENAME-PARTITION | R-P: new_name= old_name |
Rename a partition | no1 |
SCRATCH | S [path]: pattern[, ...] |
Delete files | yes |
SWAP | S- {8 |9 |D } |
Change primary address | yes |
TIME READ ASCII | T-RA |
Read Time/Date (ASCII) | no4 |
TIME READ BCD | T-RB |
Read Time/Date (BCD) | no4 |
TIME READ DECIMAL | T-RD |
Read Time/Date (Decimal) | no4 |
TIME READ ISO | T-RI |
Read Time/Date (ISO) | no4 |
TIME WRITE ASCII | T-WA dow mo/ da/ yr hr: mi: se ampm |
Write Time/Date (ASCII) | no4 |
TIME WRITE BCD | T-WB b0 b1 b2 b3 b4 b5 b6 b7 b8 |
Write Time/Date (BCD) | no4 |
TIME WRITE DECIMAL | T-WD b0 b1 b2 b3 b4 b5 b6 b7 |
Write Time/Date (Decimal) | no4 |
TIME WRITE ISO | T-WI yyyy- mm- ddT hh: mm: ss dow |
Write Time/Date (ISO) | no4 |
U1/UA | U1 channel medium track sector |
Raw read of a block | not yet |
U2/UB | U2 channel medium track sector |
Raw write of a block | not yet |
U3-U8/UC-UH | U3 - U8 |
Execute in user buffer | not yet |
U9/UI | UI |
Soft RESET | yes |
U:/UJ | UJ |
Hard RESET | yes |
USER | U0> pa |
Set unit primary address | yes |
USER | U0>B flag |
Enable/disable Fast Serial | no |
USER | U0>D val |
Set directory sector interleave | no1 |
USER | U0>H number |
Select head 0/1 | no1 |
USER | U0>L flag |
Large REL file support on/off | no |
USER | U0>M flag |
Enable/disable 1541 emulation mode | no1 |
USER | U0>R num |
Set number fo retries | no1 |
USER | U0>S val |
Set sector interleave | no1 |
USER | U0>T |
Test ROM checksum | no5 |
USER | U0>V flag |
Enable/disable verify | no1 |
USER | U0> pa |
Set unit primary address | yes |
USER | UI {+ |- } |
Use C64/VIC-20 Serial protocol | no1 |
UTILITY LOADER | & [[path]: ]name |
Load and execute program | no1 |
VALIDATE | V [medium] |
Filesystem check | no2 |
WRITE PROTECT | W- {0 |1 } |
Set/unset device write protect | yes |
00, OK,00,00
FAT32
has to be passedThe following special file syntax and OPEN
options are specific to CMDR-DOS:
Feature | Syntax | Description |
---|---|---|
Open for Read & Write | ,?,M |
Allows arbitrarily reading, writing and setting the position (P )1 |
Get current working directory | $=C |
Produces a directory listing containing the name of the current working directory followed by all parent directory names all the way up to / |
The following added command channel features are specific to CMDR-DOS:
Feature | Syntax | Description |
---|---|---|
POSITION | P channel p0 p1 p2 p3 |
Set position within file (like sd2iec); all args binary |
To use the POSITION command, you need to open two channels: a data channel and the command channel. The channel argument should be the same as the secondary address of the data channel.
OPEN 1,8,2,"LEVEL.DAT,S,R"
OPEN 15,8,15,"P"+CHR$(2)+CHR$(0)+CHR$(1)+CHR$(0)+CHR$(0)
This opens LEVEL.DAT for reading and positions the read/write pointer at byte 256.
OPEN 2,8,5,"LEVEL.DAT,S,R"
OPEN 15,8,15,"P"+CHR$(5)+CHR$(128)+CHR$(0)+CHR$(0)+CHR$(0)
This time, the secondary address is 5, and the pointer is at byte 128.
The $=C command will list the current working directory and its parent path. The current directory will be at the top of the listing, with each parent directory beneath, with / at the bottom.
DOS"$=C"
0 "/TEST "
0 "TEST" DIR
0 "/" DIR
65535 BLOCKS FREE.
Copyright 2020, 2023 Michael Steil <mist64@mac.com>, et al.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.