The ATARI computer uses a very easy-to-use input and output
system called the Central Input/Output utility, or CIO. Nearly all
input or output passes through this utility.
CIO uses eight "channels" as paths for I/O. There are not really
separate channels for I/O but the computer acts as if there were.
Each channel is controlled by a 16 byte block of memory called an
Input/Output Control Block or IOCB. The channels are used by putting
the proper numbers in the proper IOCB bytes then jumping to the CIO
routine. In BASIC, complete I/O operations can be as easy as typing a
command such as LPRINT. In this case BASIC does all the work for
THE CIO CHANNELS
There are eight CIO channels, numbered from 0 to 7. In BASIC some
channels are reserved for BASIC's use.
BASIC CIO channel assignments
Channel 0 Permanently assigned to the screen editor
6 Used for graphics commands
7 Used for the Cassette, disk and printer
Channels 6 and 7 are free to use when they are not being used by
BASIC. With machine language, all of the channels are available to
THE IOCB STRUCTURE
The IOCB for channel zero starts at address $0340 (decimal 832). This
is the only address you need to know. Indexing from this address is
used to find all of the other bytes. Below are the names and uses of
the IOCB bytes.
IOCB bytes and uses:
ADDRESS NAME EXPLANATION
$0340 ICHID handler Identifier
$0341 ICDNO device number (disk)
$0342 ICCOM command
$0343 ICSTA status
$0344 ICBAL buffer address (low byte)
$0345 ICBAH buffer address (high byte)
$0346 ICPTL address of put byte
$0347 ICPTH routine (used by BASIC)
$0348 ICBLL buffer length (low byte)
$0349 ICBLH buffer length (high byte)
$034A ICAX1 auxiliary information
$034B ICAX2 -
$034C ICAX3 the remaining auxiliary
$034D ICAX4 bytes are rarely used
$034E ICAX5 -
$034F ICAX6 -
When a channel is open, the handler I.D. contains an index to the
handler table. The handler table (to be discussed later) holds the
address of the device handling routines. When the channel is closed
ICHID contains $FF.
The device number is used to distinguish between multiple devices with
the same name, such as disk drives.
The command byte tells CIO what operation to perform.
CIO command codes
+Open $03 3
+close $0C 12
get $07 7
put $09 11
input $05 5
print $09 9
request $0D 13
+*special >$0D >13
+ command may be made to a closed channel
* device specific commands
The status byte contains an error code if something goes wrong. If
bit 7 is 0 there have been no errors.
ICBAL and ICBAH
Before a channel is opened, the buffer address bytes are set point to
the block of memory which contains the name of the device the channel
is to be opened to. Before actual input or output these bytes are set
to point to the block of memory where the I/O data is stored or is to
ICPTL and ICPTH
The put routine pointer is set by CIO to point to the handlers'
put-byte routine. When the channel is closed the pointer points to
the IOCB-closed routine. This pointer is only used by BASIC.
ICBLL and ICBLH
The buffer length bytes show the number of bytes in the block of
memory used to store the input or output data. (See ICBAL and ICBAH.)
If the amount of data read during an input operation is less than the
length of the buffer, the number of bytes actually read will be put in
ICBLL and ICBLH by CIO.
ICAX1 through ICAX6
The auxiliary information bytes are used to give CIO or the device any
special information needed.
OPENNING A CIO CHANNEL
Before using a CIO channel it must be assigned to an I/O device. In
machine language you start by putting the channel number in the four
high bits of the 6502 X register (X = $30 for channel three). Next
you place the necessary codes (parameters) into IOCB 0 indexed by X.
The X register will cause the numbers to be offset in memory by 16
times the channel number. This puts the numbers into the correct IOCB
instead of IOCB 0. Below are the parameters used to open a channel.
ICCOM open code
ICBAL address of device name
ICBAH in memory
ICAX1 direction code
The direction code byte in ICAX1 takes on the following format:
ICAX1 format for opening a channel
7 6 5 4 3 2 1 0
ICAX1 | W R |
8 4 2 1
W 1 = open for output (write)
R 1 = open for input (read)
ICAX1 may have the following data
CIO direction codes
HEX DEC operation
$04 4 input
$08 8 output
$0C 12 input and output (cannot change the length
of a disk file)
ICBAL and ICBAH point to the device name stored in memory. The device
and file name must be followed by 0 or $9B (decimal 155).
Once the parameters are set, jumping (JSR) to the CIO vector
(CIOV) at address $E456 (58454) will cause the channel to be opened.
In the following example a basic knowledge of assembly language is
Routine to open channel 1 to the keyboard:
ICHID = $0340
ICCOM = ICHID+2
ICAX1 = ICHID+10
ICAX2 = ICHID+11
IOCB1 = $10 channel in four high bits
CIOV = $E456
OPEN = $03
OREAD = $04 ;open for input
ERROR = (address of error handling routine)
START LDX IOCB1
NAME .BYTE "K:",$9B
OK (program continues here)
To open a CIO channel in BASIC the OPEN command is used.
BASIC OPEN command format:
OPEN #channel,aux1,aux2,device:file name
aux1 = direction code
aux2 = special code
To open channel 1 to the keyboard in BASIC Type:
The third parameter, aux2, is a rarely used special parameter. One
use is to keep the screen from erasing when changing graphics modes.
The fourth parameter is the device to open the channel to. It may be
either a string in quotes or a string variable.
CIO device names
C cassette recorder
*D disk drive
E screen editor
*R RS 232 I/O port
S screen handler
* Uses a non-resident handler loaded by the device at
The device name must usually be followed by a colon. With the disk
drive a file name is expected after the device name. The screen
handler is used for graphics. The screen editor uses both the
keyboard handler and the screen handler to work between the keyboard
USING AN OPEN CHANNEL
Once a channel is opened to a device you have several options:
INPUT: (ICCOM = $05)
The computer reads data from the device until a carriage-return is
read (decimal number 155, hex $9B) or the end of the file (EOF) is
reached. A carriage return is also known as an End-Of-Line or EOL.
The IOCB input parameters are:
IOCB input parameters:
ICCOM get record code
ICBAL address of buffer to
ICBAH store the data in
ICBLL length of the data
The following routine demonstrates the input command in assembly
language. Some of the equates are in the channel openning example
GETREC = $05
BUFF = (address to store data at)
BUFLEN = (number of bytes available at storage address)
LDA < BUFF
LDA > BUFF
LDA < BUFLEN
LDA > BUFLEN
OK2 (continues if no errors)
If the data retrieved is shorter than the prepared buffer, the number
of bytes actually read will be put into ICBLL and ICBLH.
In BASIC, the INPUT command is used.
BASIC INPUT command format:
INPUT #channel,string variable
INPUT #channel,arithmetic variable
The above commands will cause the data from the device to be put into
the specified buffer (IN$ in the BASIC example) until an EOL is
reached. If the INPUT statement is used again, without closing the
channel, the computer will get more data from the device until another
EOL is read or the end of the file is reached. The new data will
write over the old data in the input string or buffer. If an
arithmetic variable is used, only numbers can be input.
PRINT: (ICCOM = $09)
In assembly language the print command is identical to the input
command. The only difference is that the PUTREC code ($09) is stored
in ICCOM. Of course the buffer bytes of the IOCB then specify the
block of memory to be output from rather than input to. With the
print command, EOLs within the string or buffer are ignored but an EOL
is placed at the end of the data when sent to the device.
In BASIC, the PRINT command is used like INPUT except you want to use
a semicolon instead of a comma to separate parameters. For example:
If you use a comma, ten space characters will be sent before the
If the print command is used again, without closing the channel, the
new data will be appended to the end of the data previously sent. Old
data will not be written over.
GET: (ICCOM = $07)
In BASIC this command inputs a single byte of data from the device.
EOLs are ignored. In BASIC, GET is used like INPUT except an
arithmetic variable must be used. For example:
If the get command is used again the next byte from the device will be
read. If the end of a file is reached an error will occur.
There is no command in BASIC to input an entire file without stopping
at each EOL. If you wish to ignore EOLs while reading a file to a
string, you must use the GET command. Each byte of data is then put
into the string by the program.
10 OPEN #1,4,0,"D:TEST"
20 TRAP 60:REM GOES TO LINE 60 WHEN END OF FILE ERROR OCCURS
30 GET #1,IN
50 GOTO 30
60 CLOSE #1
In assembly language, the get command can be used to get any number of
bytes from the device. It works just as INPUT does except EOLs are
IOCB get-byte parameters:
ICCOM get-character (single byte) code
ICBAH same as in input
Other than the ICCOM code (GETCHR = $07) this command is identical to
the input command.
PUT: (ICCOM = $0B)
In BASIC, PUT is the opposite of GET. It outputs a single byte from a
variable to the device. PUT is used the same as GET. For example:
In assembly language, the command byte of the IOCB is loaded with the
put-character code (PUTCHR = $0B). Otherwise the PUT command is
identical to GET.
CLOSING A CHANNEL
Closing a channel frees it for use by another device or for changing
parameters. In assembly language the close code is put into the
command byte of the IOCB then the CIOV call is made.
IOCB close command:
CLOSE = $0C
In BASIC, use the CLOSE command followed by the channel number.
With the disk drive, the file name is not put into the directory until
the channel is closed.
THE DEVICE TABLE
CIO uses a jump table located at $031A (794). When a CIO call is
made, CIO searches the table for the one-byte device name. The two
bytes following the device name contain the address of the device
handler's vector table. CIO searches the device table from the end,
$033D (829) to the beginning. This way, if a custom handler has ben
substituted for a resident handler, the custom handler will be found
first. (custom handlers cannot be inserted directly in the place of
resident handlers in the device table.)
Each handler has its' own vector table. This vector table is 16 bytes
long. The two-byte vectors point to the various handler routines.
The vectors are stored in the vector table in the following order:
Handler vector table order
JMP init code (3 bytes)
The open routine should validate the ICAX parameters and check for
The close routine should send any remaining data in the buffer to the
device, mark the End-Of-File and update any directories, etc.
The get byte routine should get a byte from the device or the handler
buffer and put it in the 6502 A register. Handlers with long timouts
must monitor the break key flag and put a $80 in the 6502 Y register
if the [BREAK] key is pressed.
The put byte routine should send the byte in the 6502 A register to
the device or handler buffer. If the buffer fills, it should be sent
to the device. BASIC can call the put byte routine without using
The get status routine may get 4 bytes of status information from the
device and put them in DVSTAT [$02EA] to DVSTAT+3.
For special commands the handler must examine the command byte and
find the proper routine entry point.
In all cases the status (error code) of the operation should be put in
the 6502 Y register.
To be compatible with all versions of the operating system, the
handler must redirect DOSINI [$000C,2 (12)] for initialization upon
reset. This initialization must restore the vectors in the handler
vector table and jump to the origional DOSINI vector.
Some devices have special CIO commands. These are known as device
specific commands. In assembly language these commands are executed
just as any other CIO command is. In BASIC the XIO command is used.
An example of the XIO command is:
XIO command code #channel,aux1,aux2,device:file name
To open a channel with the XIO command instead of the OPEN command
XIO 3 #1,4,0,"K:"
Note that the above command is identical to the OPEN command except
"XIO 3" is used instead of "OPEN". Also note that $03 is the IOCB
open code for ICCOM.
Useful database variables and OS equates
DOSINI $000C,2 (12): initialization vector
BRKKEY $0011 (17): break key flag
ICHID $0340 (832): start of IOCBs
ICDNO $0341 (833):
ICCOM $0342 (834):
ICSTA $0343 (835):
ICBAL $0344 (836):
ICBAH $0345 (837):
ICPTL $0346 (838):
ICPTH $0347 (839):
ICBLL $0348 (840):
ICBLH $0349 (841):
ICAX1 $034A (842):
ICAX2 $034B (843):
HATABS $031A,16 (794): device handler table
CIOV $E456 (58454): CIO entry vector