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
     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 programmer.
     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:
      $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
                    HEX       DEC
         +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
     be stored.
     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.

     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.
                           Channel-open parameters:
      ICCOM     open code
      ICBAL     address of device name
      ICBAH      in memory
      ICAX1     direction code
      ICAX2     zero
     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)
            LDA OPEN
            STA ICCOM,X
            LDA NAME
            STA ICBAH,X
            LDA OREAD
            STA ICAX1,X
            LDA #0
            STA ICAX2,X
            JSR CIOV
            BPL OK
            JMP ERROR
      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:
      OPEN #1,4,0,"K:"
     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
          K   Keyboard
          P   printer
         *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
     and screen.

     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
      ICALH     buffer
     The following routine demonstrates the input command in assembly
     language.  Some of the equates are in the channel openning example
                                Input routine:
             GETREC = $05
             BUFF   = (address to store data at)
             BUFLEN = (number of bytes available at storage address)
             LDX IOCB1
             LDA GETREC
             STA ICCOM,X
             LDA < BUFF
             STA ICBAL,X
             LDA > BUFF
             STA ICBAH,X
             LDA < BUFLEN
             STA ICBLL,X
             LDA > BUFLEN
             STA ICBLH,X
             JSR CIOV
             BPL OK2
             JMP ERROR
      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
                                 For example:
      INPUT #1,IN$
     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:
      PRINT #1;OUT$
      PRINT #1;"HELLO"
     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:
      GET #1,IN
     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"
      30 GET #1,IN
      40 IN$(LEN(IN$)+1)=CHR$(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
      ICBAL   \
      ICBAH    same as in input
      ICBAH   /
     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:
      PUT #1,OUT
     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 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
            LDX IOCB1
            LDA CLOSE
            STA ICCOM,X
            JSR CIOV
     In BASIC, use the CLOSE command followed by the channel number.
      CLOSE #1
     With the disk drive, the file name is not put into the directory until
     the channel is closed.
     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
          get byte
          put byte
          get stat
          JMP init code (3 bytes)
     The open routine should validate the ICAX parameters and check for
     illegal commands.
     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


Craig Lisowski (