Commodore‎ > ‎BASIC‎ > ‎Keywords‎ > ‎


Keyword Abbreviation Token (hex) Version(s) Classification
OPEN O{Shift+P} 9F 1.0+ Command and Statement

OPEN fileNumber [ , deviceNumber [ , flags [ , commandString ] ] ]
Parameters Type Legal Value(s) Default Value Note(s)
fileNumber Unsigned Byte  1* to 255  
* some versions of BASIC allow fileNumber 0 for some devices
deviceNumber Unsigned Byte  0 ~ 255  1
0 = keyboard
1 or 2 = cassette (machine specific)
2 = RS-232  (machine specific)
3 = (active) display screen
4 ~ 30 for IEEE or serial bus (IEC)
4 or 5 usually a printer
8 ~ 10 usually a mass-storage device (like a floppy or hard disk)
flags Unsigned Byte 0 ~ 255  0 or 255
Device-specific (for disk drives, a SecondaryAddress 0 ~ 15) 
commandString String
0 ~ 191 (cassette)
1 ~ 255 (serial bus)
empty string For a disk drive (serial bus) this is often a filename (limited to 16 characters).
However, it may be (in general) any device-specific "command".
Open a "file" for subsequent data transfer.
The OPEN statement is the key to access of most external devices.  This command is very device-specific.  The fileNumber is an arbitrary number which the programmer may choose.  It will be needed for subsequent use with CMD, GET#, INPUT# or PRINT# and eventually CLOSE.  The main restriction is it must be currently unused, otherwise FILE OPEN ERROR is generated (i.e., the fileNumber is already in use).  The fileNumber may be used again in another OPEN after it has been CLOSEd. 
The fileNumber should usually be less than 128.  Otherwise, CMD and PRINT# will append a line feed [CHR$(10)] after each automatic return.  An automatic return is one sent by those commands if they do not end with a comma (,) or semicolon (;).  In particular, a line feed will not be sent if you manually send a return (with CHR$(13) for example).  This seems to be designed for some printers which expect a line feed to follow each carriage return.  May also be useful to generate DOS / Windows compatible text files or in RS-232 communications (of course if you are using PETSCII or extended characters of ASCII-X you will probably need to translate character codes too).
If the fileNumber is not legal (see table above) an ILLEGAL QUANTITY ERROR occurs; one exception is with BASIC v3.5 or later.  These versions allow fileNumber of zero with a deviceNumber of zero (the keyboard).  This is due to the way CMD works; it uses the value of zero to distinguish standard (screen) output from user-specified device output; however the keyboard is input-only so CMD will not work with the keyboard and thus no conflict exists.  In BASIC versions less than 3.5, using a fileNumber of zero will generate the not-so-helpful message NOT INPUT FILE ERROR.
SYNTAX ERROR is generated if fileNumber is omitted.
On most CBM machines, a maximum of 10 files may be open at one time (this is rarely a serious restriction).  If 10 files are already opened then OPEN will generate TOO MANY FILES ERROR.
In simple programs, the fileNumber will often be the same as deviceNumber, as this keeps things simple for the programmer.  But some devices allow more than one "file" to be opened at a time in which case this simple scheme won't work (you will need multiple fileNumbers for the same device, and because each fileNumber must be unique, you can't use the deviceNumber for all of them).  Unless the device supports multiple "files", it is usually a bad idea to open multiple fileNumbers to the same deviceNumber.  This is mainly because it needlessly complicates programming, but in the case of an RS-232(C) device it will cause the previous buffers to be "forgotten" which can result in data loss, and on machines which dynamically allocate RS-232 buffers, new buffers will be allocated.
The deviceNumber refers to the device you would like to communicate with (I bet you knew that!).  See the table above.  In particular, the default value is 1 (cassette).
Some machines (only some PETs, I believe) do not support RS-232(C) at all.  These may support a second cassette as device 2.  For all other machiens, OPEN to deviceNumber 2 (i.e., RS-232(C)) will almost always succeed, even if there is no modem (or other RS-232(C) device) connected.  Except for the C128, which has dedicated buffers, opening a RS-232(C) device will perform CLR after dynamically allocating the buffers!  This means you should set up your variables (and functions) after you OPEN the RS-232 device.  Often this means some kludge will be needed, such as POKEing important variables into RAM that is not used by BASIC, or storing them in a file (neither kludge will work for user functions).
The optional flags is really just a device-specific numeric parameter (with the commandString being a string parameter).  Here is a table of some common meanings:
deviceNumber flags value Default Value Note(s)
0 (keyboard) unused  0 only supports input
1 (cassette) direction 
0 = read
1 = write
2 = write with end-of-tape marker
2 unused or
Unused for RS-232(C); same as above if cassette
3 (screen) unused 0
This refers to the "active" screen on the C128
4 or 5
(often a printer)
channel 255  A value of 0 or 255 often selects the ASCII-X font,
while a value of 7 often selects the PETSCII font.
8 to 11
(often a disk)
channel  255 "channel" is often called "Secondary Address"
A value of 128 or more means no "channel" is used with the device
A value of 15 indicates the command/status "channel"
A value of 0 indicates open for reading (reserved for LOAD)
A value of 1 indicates open for writing (reserved SAVE)
Values 2 to 14 generally allow either reading or writing
(the direction is usually indicated in the commandString)
An ILLEGAL QUANTITY ERROR is generated if either the deviceNumber or flags is less than 0 or greater than 255.  TYPE MISMATCH ERROR is generated if any of fileNumber, deviceNumber, or flags is a string.

For an IEEE/IEC device (deviceNumber 4 or more), the flags will select a "channel" (also called a "Secondary Address").  Just like the computer can have more than 1 file open, many of these devices (in particular, mass-storage devices) can manage more than 1 file at a time.  So the "channel" is how the computer tells the device which of its open "files" to be used.  The "file" is an abstraction in general (although it often refers to an actual file, hence the name).  In particular, channel 15 of a disk drive is the command/status channel, not a real file; these devices often allow data buffers to be opened which are also not files.

For an IEEE/IEC device (deviceNumber 4 or more), the flags value (in this case the "channel") should be a value less than 31.  A value of 31 will cause conflicts with the bus often locking up the computer.  A value of 32 or more will cause various bus-control bits to mixed with standard commands also resulting in strange behavior.  Neither BASIC nor the KERNAL checks for a "bad value" (31 or more), so it is now your responsibility!
For a cassette device (deviceNumber of 1 or perhaps 2), OPEN will cause the message PRESS PLAY ON TAPE [#deviceNumber] to be displayed if the direction (see table above) is read and PLAY is not currently pressed, or the message PRESS RECORD & PLAY ON TAPE [#deviceNumber] if the direction is write and neither PLAY nor RECORD is currently pressed.  The deviceNumber will be displayed in the message only on machines which allow 2 cassettes.  Note the system can't tell if the user pressed the correct button(s).  For example, if PLAY is currently pressed when OPEN 1,1,2 is executed (open for write) there will be no message (the computer will proceed to send data to the cassette but it will not be written because RECORD is not pressed); a similar (but worse) problem exists if RECORD & PLAY are currently pressed and an OPEN for reading is executed (now the computer will begin searching for a file, but the cassette will be recording "null", effectively erasing the tape).
The optional commandString is often a filename.  This is always true with cassette and often the case with other mass-storage devices.  It is never the case with an RS-232(C) device.  Other devices may have special uses for it or ignore it completely (consult your device's documentation).
Hardware note: the user port of most (all?) CBMs use TTL voltage levels (0 or 5 volts) which is not true RS-232 format (which uses -12V and +12V).  However Commodore is not alone and many devices use TTL levels; this is known as RS-232C.  It is my understanding (I don't own one) that Commodore's RS-232 adapters convert the TTL voltages of the computer into real RS-232 voltages.  Of course if you have an RS-232C device then you don't need an RS-232 adapter, just some way to plug it into the user port.
The commandString is ignored for the keyboard and screen (deviceNumber 0 and 3 respectively).  TYPE MISMATCH ERROR is generated if a numeric expression is supplied for the commandString.
If no commandString is given when opening to cassette, the system will search for the start of the next file on tape and display FOUND fileName at which point the user can accept the found file by pressing a certain key (for example C= on the C64) or skip it to continue searching by waiting a few seconds or pressing another certain key (for example Spacebar on the C64).  If a commandString is given the system will search for a fileName with that value; each file it discovers will generate a FOUND message and the user is again allowed to press a key, but now the "accept file" key will not work (it acts like skip file key) if the fileName on the tape does not match the commandString of OPEN, however the "skip file" key will skip a file, even if it has the correct fileName.  In either case, if an end-of-tape marker is found in the process then FILE NOT FOUND ERROR is generated.  If the user presses STOP in the process, a BREAK ERROR is generated.
For an RS-232(C) device, the commandString is usually built from a series of CHR$ values each one representing a byte for a (virtual) ACIA device's register.  If this isn't archaic enough, the registers are divided up into bits and bit fields, which means you need to do a little math to figure out the appropriate CHR$ values:
byte number(s) bit number(s) Default Value Note(s)
0 0 ~ 3 6 0 = custom rate (see bytes 2 and 3)
1 = 50 baud
2 = 75 baud
3 = 110 baud
4 = 134.5 baud
5 = 150 baud
6 = 300 baud
7 = 600 baud
8 = 1200 baud
9 = 1800 baud
10 = 2400 baud
11 = 3600 baud
12 = 4800 baud
13 = 7200 baud
14 = 9600 baud
15 = 19200 baud
0 4 (ignored!) 0
0 = external receiver clock
16 = baud rate generator
5 ~ 6
0 = 8-bit word
32 = 7-bit word
64 = 6-bit word
96 = 5-bit word
0 7 0
0 = 1 stop bit
128 = 2 stop bits 
0 = disable receiver (DTR high) a.k.a. "3-line handshake" 
1 = enable receiver (DTR low) a.k.a. "X-line handshake"
1 (unused?) 0 0 = enable receiver interrupt 
2 = disable receiver interrupt
1 2 ~ 3 (unused?) 0 0 = disable interrupt, RTS high, transmitter off 
4 = enable interrupt, RTS low, transmitter on 
8 = disable interrupt, RTS low, transmitter on 
12 = disable interrupt, RTS low, transmit BRK
1 4 (ignored!) 0 0 = normal receiver, a.k.a. "full duplex"
16 = echo receiver, a.k.a. "half duplex"
1 5 0 0 = no parity
32 = include parity (see bits 6 and 7)
1 6 ~ 7
(only used if bit 5 set)
0 = odd parity, receive and transmit
64 = even parity, receive and transmit
128 = transmit mark parity, ignore received parity
192 = transmit space parity, ignore received parity
2 0 ~ 7  none  low byte of transciever rate = (system clock / custom rate / 2 - 100) AND 255
3 0 ~ 7 none high byte of transciever rate = (system clock / custom rate / 2 - 100) / 256 
The formula for custom rate comes from the C64 Programmer's Reference Guide.  Except for the "fudge factor" of -100, it should work with any CBM machine if you know the system clock frequency (reasonably close to 1 MHz for both NTSC and PAL versions of C64/C128, but quite different on other machines).  The maximum rate (using either a pre-defined baud rate or a custom baud rate) is machine-specific.  Some documentation (such as the just mentioned C64 P.R.G.) claims that bit 4 of byte 1 controls "duplex mode" (either half or full duplex), but on most systems (definately the C64 and C128) the RS-232(C) software always operates in full-duplex mode.
Due to a bug (failure to initialize) on some systems, both bytes 0 and 1 and should be specified (i.e., do not rely on the default values).  For the completeness of documentation, the default values are suppose to be: 300 baud, 8 data bits, 1 stop bit, 3-line handshake, no parity.
Not all logically valid combinations of values will work!  I'm not sure if this is a software-effort to maintain compatibility with real ACIAs (in the case of most CBMs which lack a hardware ACIA), or just a bug in the system software.  A related point is this ACIA (real or emulated) does not allow you to disable stop bits.  These are generally not needed when parity is used, which means you either waste bandwidth or turn off parity which often increases the error rate.
I thought I should point out here (this seems like a good place) that the often confusing term "baud rate" is actually very simple!  It is the number of "actual" bits which are sent per second.  This means not only your data bits, but also all control bits: the start bit, the stop bit(s), and possibly a parity bit.  The term "bit rate" usually refers to only the data bits.  In other words, you need to know the protocol (what control bits are present and the word size) and do a little math to convert between bit rate and baud rate.  In any case, the bit rate will be less than the baud rate.
If the optional commandString is omitted, the command will usually succeed even if the device is not connected (or connected but turned off).  The computer generally won't know this until a subsequent I/O command (such as PRINT#) is executed.  For example, if disk device 8 is turned off, OPEN 8,8,8 will not report an error (but subsequent attempts at I/O will).  However on some hacked machines OPEN may give DEVICE NOT PRESENT when commandString is omitted.  The best example would probably be JiffyDOS which replaces the cassette routines in ROM with its own high-speed disk routines.  These machines will report DEVICE NOT PRESENT if you attempt to OPEN a file on cassette (no matter if you have one and regardless if you include or omit commandString).
For a deviceNumber referring to an IEEE/IEC device (i.e., value 4 or more), supplying a commandString will generate DEVICE NOT PRESENT if the device is not connected or not powered-on.
For most mass-storage devices, like disk drives (typically a deviceNumber from 8 to 11), the commandString is almost always a file name when using a flags value(channel#) of 0 to 14.  In such cases, the format of the commandString is generally:
[ drive_partition [ / path ] : ] fileName [ , { type | L recordSize } ] [ , mode ]
Parameters Type Legal Value(s) Default Value Note(s)
drive_partition "Byte"  0 or 1 (most)
0 to 255 (CMD compatible)
CMD (compatible) devices allow multiple partitions (selected by number),
which should not be confused with 1581 partitions (selected by name)
The popular 1541, 1571, and 1581 only allow drive 0.
path String  varies  current directory
Only for CMD (compatible) devices
fileName String 1 ~ 16 chars (typical)  none
A value of * selects the most-recently accessed file, or the first file on disk 
type Character
"D" Deleted (rarely works)
"S" Sequential
"P" Program
"U" User
"C" 1581 partition ?
??? directory
any (read)
Sequential (write)
Due to a bug in the fast-serial routines of the 1571 and 1581,
non-program types may fail when used with the C128.
If a flags value (channel#) of 0 or 1 is used, the default is Program
recordSize CHR$ 2 to 254 per-file (read)
none (write) 
Used with Relative (REL) files; to create a new file the recordSize is required
To OPEN an existing file, it is optional but if specified
it must be the same value used when the file was created.
mode Character "R" Read
"W" Write (new)
"A" Append (existing)
"M" Modify* 
"R" The mode is illegal (?) with REL files which are always read+write.
*The "M" mode is intended to retrieve data from a "splat" file.
For most mass-storage devices, like disk drives (typically a deviceNumber from 8 to 11), the commandString is a often a command when using a flags (channel#) value of 15.  In such cases, the following commands are often available: 
Name Command Format Note(s)
Block-Allocate B[junk]-A[junk] : drive# track# sector#
There is a bug on many CBM drives that will allocate all sectors of the track
if the requested sector is already allocated!
To work reliably, a "data buffer" should be opened before use.
Block-Execute B[junk]-E[junk] : channel# drive# track# sector#
channel# must refer to a previously opened "data buffer".
Typically fails on MMC / SD2IEC devices. 
Block-Free B[junk]-F[junk] : drive# track# sector#
To work reliably, a "data buffer" should be opened before use.
Block-Read B[junk]-R[junk] : channel# drive# track# sector#
channel# must refer to a previously opened "data buffer"
The maximum number of bytes is determined by the first byte of the sector. 
Block-Read 2 U1 : channel# drive# track# sector#
 ~ or ~ 
UA : channel# drive# track# sector#
channel# must refer to a previously opened "data buffer"
Block-Write B[junk]-W[junk] : channel# drive# track# sector#
channel# must refer to a previously opened "data buffer"
The number of bytes written to the buffer is stored in the first byte of the sector.
Block-Write 2 U2 : channel# drive# track# sector#
 ~ or ~ 
UB : channel# drive# track# sector#
channel# must refer to a previously opened "data buffer"
Buffer-Pointer B[junk]-P[junk] : channel# byte#
channel# must refer to a previously opened "data buffer" or REL file
Change Directory CD[drive_partition] : path 
 ~ or ~ 
CD[drive_partition] : ←
CMD (compatible) devices only.
The "left arrow" version sets the current directory to the parent directory.
On devices which support disk images, this will often "unmount" the image.
Concatenate C[junk][drive_partition[ / path ]] : newFile [ drive_partition[ / path ]] : fileName { , [ drive_partition[ / path ]] : fileName } ... Multiple fileNames may specified (limited only by length of command) and they will be concatenated in the order specified.  See also CONCAT.
Copy C[junk][drive_partition[ / path ]] : newFile [ drive_partition[ / path ]] : existingFile Can't be used to copy files between different deviceNumbers
See also COPY.
Format N[junk][drive_partition[ / path ]] : diskName [ , id ] Omitting id will do a quick format; required for a never-formatted floppy.
See also HEADER.  
Initialize I[junk][drive_partition] [ : ] See also DCLEAR.
Memory-Read "M[junk]-R[junk] :" + CHR$(low_byte) + CHR$(high_byte) [ + CHR$(#_bytes) ] The #bytes defaults to 1 if omitted.
Memory-Execute  "M[junk]-E[junk] :" + CHR$(low_byte) + CHR$(high_byte) Typically fails on MMC/SD2IEC devices.
Memory-Write "M[junk]-W[junk] :" + CHR$(low_byte) + CHR$(high_byte) + CHR$(#_bytes) { + CHR$(data) } ... Typically fails on MMC/SD2IEC devices. 
Record select "P" + CHR$(channel# + 96) + CHR$(low_byte) + CHR$(high_byte) + CHR$(byte# The low_byte and high_byte (8 bits each) specify a 16-bit record#.
The byte# is a 1-based index into the selected record. 
See also RECORD. 
Rename R[junk][drive_partition[ / path ]] : newName oldName See also RENAME.
Scratch R[junk][drive_partition[ / path ]] : fileName  See also SCRATCH. 
Validate V[junk][drive_partition] [ : ] See also COLLECT.
All of the above commands may also be sent with PRINT# (or CMD) after OPEN has successfully executed (and before a corresponding CLOSE has been executed).  In such cases, the + between CHR$ values may be omitted or replaced with a semicolon (;), however a + must be used with OPEN.  There are other commands possible with various mass-storage devices, but I think those above are the most common.
*WOW* You're still reading this?
Now if the syntax of the command is correct, BASIC will effectively zero the reserved variable ST and call the KERNAL Open routine (which may set a non-zero value in ST).  Assuming the KERNAL does not return an error, BASIC version 3.5+ will then clear the reserved variable DS (and DS$).  However, the secret variable "disk unit" is not updated, which may cause future references to DS or DS$ to report on the wrong device!
Examples with keyboard:
OPEN 1,0 : REM any version of BASIC

OPEN 2,0 : REM version 2- of BASIC

OPEN 3,0 : REM version 3.5+ of BASIC

Examples with cassette:
OPEN 1 : REM open for reading (user can choose file)

OPEN 2,1,0,"NAME" : REM open for reading (user can skip files, but only choose one called 'NAME')

OPEN 3,1,1,"NAME" : REM open file 'NAME' for writing (normal)

OPEN 4,1,2,"NAME" : REM open file 'NAME' for writing (add end-of-tape marker)

Examples with RS-232(C):
OPEN 1,2 : REM 300 baud, 8 data bits, 1 stop bit, 3-line handshake, no parity

OPEN 2,2,0,CHR$(8) : REM 1200 baud, 8 data bits, 1 stop bit, 3-line handshake, no parity

OPEN 3,2,0,CHR$(6+32)+CHR$(32) : REM 300 baud, 7 data bits, 1 stop bit, 3-line handshake, odd parity

Examples with screen (video display):
OPEN 1,3 : REM normal PRINT#

OPEN 129,3 : REM each PRINT# will append "line feed" after "carriage return"

Examples with printer:
OPEN 1,4 : REM use ASCII-X font (CBM-Compatible printers)

OPEN 2,4,7 : REM use PETSCII font (CBM-Compatible printers)

Examples with disk drive:
OPEN 1,8 : REM usually fails to do anything on the device, but creates a BASIC "file"

OPEN 2,8,2,"MY FILE" : REM open 'MY FILE' for reading (any type but CBM or DIR) 

OPEN 3,8,3,"MY FILE,W" : REM open 'MY FILE' for writing (SEQ) 

OPEN 4,8,4,"MY FILE,P,W" : REM open 'MY FILE' for writing (PRG) 

OPEN 5,8,5,"MY FILE,L"+CHR$(32) : REM open 'MY FILE' for read/write (REL with 32-byte records) 

OPEN 15,8,15,"S0:MY FILE" : REM scratch (delete) 'MY FILE' in current directory

OPEN 6,8,6,"0/PROGS:MY FILE" : REM open 'MY FILE' for reading with relative path 'PROGS' 

OPEN 7,8,7,"0//PROGS:MY FILE" : REM open 'MY FILE' for reading with absolute path 'PROGS' 

  Compare With  
  Contrast With  
  See Also  

© H2Obsession, 2014