I/O and You
Types of I/O Devices
Many kinds of I/O devices can be connected to your Atari computer. But there are seven specific kinds of devices that can be addressed in both Atari BASIC and Atari assembly language using specific procedures and specific commands. Each of these seven types of devices has a unique one letter abbreviation, or device name, by which it can be addressed in both Atari BASIC and Atari assembly languages. These seven types of devices, and their corresponding device names in both BASIC and assembly language are:
- Keyboard (K:).
- Line Printer (P:).
- Program (Cassette) Recorder (C:).
- Disk Drives (D:) (or, if more than one disk drive is used, Dl:, D2:, D3:, and D4:).
- Screen Editor (E:).
- TV Monitor (Screen) (S:).
- RS-232 Serial Interface (R:).
Note the colon following the letter in each of these abbreviations. The colon is an integral part of each device name, and may not be omitted.
The Eight Atari I/O Operations
In both Atari BASIC and Atari assembly language, there are eight I/O operations that can be performed using the seven abbreviations, or device names, listed above. These eight I/O operations are:
- OPEN (to open a specified device).
- CLOSE (to close a specified device).
- GET CHARACTER (to read one character from a specified device or file).
- PUT CHARACTER (to write one character to a specified device or file).
- READ RECORD (to read the next record, a string which must end with a return character [$9B] from a specified device or file).
- WRITE RECORD (to write a record, a string, which must end with a return character [$9B] to a specified device or file).
- STATUS (to get the status of a specified device).
- SPECIAL (to perform a specified special operation on specified device used primarily in file management and RS-232 serial operations).
How Device Names and I/O Operations are Used Together
In both Atari BASIC and Atari assembly language, all of the I/O operations listed earlier are designed to be performed using a centralized peripheral interface system called the Central I/O Utility, or CIO. The Atari CIO system, like most peripheral interface systems, is designed to handle sequences of data bytes called files. A file may contain data, text, or both, and it may or may not be arranged by records, strings of text or data separated by end of line characters (ATASCII code $9B). Some files, such as files recorded on disks, can be given individual names (such as "D1:TESTIT.SRC). Other files, such as those used with the Atari screen editor or line printer, do not have individual names, but are addressed simply by the name of the device on which they appear, for example, "E:" or "P:"
Both Atari BASIC and Atari assembly language allow programmers to access up to eight different devices and/or files at the same time. In both BASIC and assembly language, this access is provided via eight dedicated blocks of memory that are called Input/Output Control Blocks, or IOCBS. In Atari Assembly language, just as in Atari BASIC, the eight IOCBs are numbered from 0 to 7. In both assembly language and BASIC, any free IOCB number can be assigned to any I/O device, although IOCB #0 is always assigned to the screen editor when an Atari computer is first turned on, and is the screen editor's default IOCB number.
Opening a Device
In both Atari BASIC and Atari assembly language, I/O devices are assigned IOCB numbers when they are first addressed, or opened. When a device is first opened for either read or write operations, an IOCB number must be assigned to it. Once an IOCB number has been assigned to a device, the device can be referred to by that number until a command to close the device is issued. Once a device is closed, the IOCB number that was assigned to it becomes free again, and can be used to open any other device in your computer system.
Assembly Language Lacks IOCB Commands
In Atari BASIC, specific commands are provided to open, close, read from and write to any I/O devices that may be connected to a computer. No such commands exist in 6502 assembly language. The IOCB system used in Atari computers does provide the assembly language programmer with a means of handling all of the I/O devices that can be connected to an Atari computer. It can handle it in a way that is relatively easy to manage and easy to understand.
Opening a Device Using Atari BASIC
It is not difficult to open a device or a file using Atari BASIC. To open a device or a file, all a BASIC programmer has to do is write a line using the following formula.
10 OPEN #n,nl,n2,filespec
The following is an example of an Atari BASIC statement written using the standard IOCB formula.
10 OPEN #2,8,0:"Dl:TESTIT.BAS"
As you can see, there are five components in an OPEN statement in Atari BASIC: The OPEN command itself, a series of three parameters separated by commas, and a device name plus a file name, if applicable. A mandatory "#" mark appears before the first parameter after the OPEN statement and the device name is followed by a mandatory colon. In addition, the device name and the file name, if applicable, are enclosed in mandatory quotation marks. The meanings of the five components of an OPEN statement are explained below.
- "OPEN" - the OPEN command.
- "#n" (#2 in the sample statement above)-The IOCB number. This number, as we have pointed out, ranges from 0 through 7. "#2" in this position means "IOCB #2."
- "n1" (8 in our example)-A code number for a specific type of input or output operation. In our sample OPEN statement, the "8" in this position is the code number for an output (open for write) operation.
- "n2" (O in our sample statement)-A device dependent auxiliary code sometimes used for various purposes (in this case, though, not used).
- "filespec"-A device name plus a file name, if applicable. In our example, "D1:TESTIT.BAS" refers to a file called TESTIT.BAS which our computer will expect to find stored on a disk in disk drive 1.
How BASIC Processes an "OPEN" Command
When your computer encounters an OPEN command while processing a BASIC program, it carries out a series of standardized operations using the values in each of the four parameters of the OPEN statement. When all of those operations are completed, BASIC jumps to a special OS subroutine called the CIO vector, or CIOV. The CIOV subroutine then automatically opens the device in question, referring to the parameters that were contained in the OPEN statement (and are now stored in certain memory locations) in order to make sure that the proper device is opened for the kind of access called for in the OPEN statement.
Advantages of Assembly Language I/O Operations
To understand how a device is opened using Atari assembly language, it's helpful to know how devices are opened using Atari BASIC. That's because BASIC programs and assembly language programs open devices in exactly the same way. The only difference is that when you open a device using BASIC, your BASIC interpreter does most of the work for you. When you use assembly language, you have to do all of the work yourself. Fortunately, there's a payoff for doing all of this extra work. When you control your system's CIO system using assembly language, you have a lot more control over the system than you do when you allow BASIC to do all the work.
Opening a Device Using Assembly Language
Now let's take a look at exactly how devices are opened, read from, written to and closed, in both Atari BASIC and Atari assembly language.
Another Look at IOCBs
As we've pointed out, the I/O operations of an Atari computer are controlled using a series of eight I/O control blocks, or IOCBS. Each of these I/O control blocks is an actual block of memory in your computer. Each IOCB is 16 bytes long, and each byte in each IOCB has a specific name and a specific function. Moreover, each byte in each IOCB has the same name, and performs the same kind of function, as the corresponding byte in every other IOCB. That's important, so let's say it again in a different way: Each byte in each IOCB in your computer has the same name, and performs the same kind of function, as the byte with the same offset in each other IOCB.
Indirect Addressing in IOCB Operations
The reason this fact is important is that indirect addressing is used quite often in IOCB operations. Indirect addressing is a technique in which a memory location is sought out by means of an offset value stored in the 6502 processor's X or Y register. Since the offsets of all of the bytes in all Atari IOCBs correspond to each other, that makes the indirect addressing mode very easy to use in Atari IOCB operations.
The 16 Bytes of an IOCB
This concept is much easier to understand when examples are given. So an actual assembly language program will be used to explain the Atari I/O system. If this program looks familiar, that's because it's almost exactly like the one you were asked to type in back in Chapter 7. It is the same one we used to print a message on the screen. If you still have that program stored on a disk, you can load it into your computer, and with just a few changes, you can turn it into an exact replica of the program below. That way you won't have to type it all over again.
PROGRAM FOR PRINTING ON THE SCREEN
10 ; 20 .TITLE "PRNTSC ROUTINE" 30 .PAGE "ROUTINE FOR PRINTING ON THE SCREEN" 40 ; 50 *=$5000 60 ; 70 BUFLEN=255 ;(EXPANDED BEYOND PREVIOUS LIMITS) 80 ; 90 EOL=$9B;ATASCII CODE FOR END OF LINE CHARACTER 0100 ; 0110 OPEN=$03 ;TOKEN FOR OPENING A DEVICE OR FILE 0120 OWRIT=$0B ;TOKEN FOR "OPEN FOR WRITE OPERATIONS" 0130 PUTCHR=$0B ;TOKEN FOR "PUT CHARACTER" 0140 CLOSE=$0C ;TOKEN FOR CLOSING A DEVICE 0150 ; 0160 IOCB2=$20 ;OFFSET FOR IOCB NO.2 0170 ICCOM=$342 ;COMMAND BYTE (CONTROLS CIO OPERATIONS) 0180 ICBAL=$344 ;BUFFER ADDRESS (LOW BYTE) 0190 ICBAH=$345 ;BUFFER ADDRESS (HIGH BYTE) 0200 ICBLL=$348 ;BUFFER LENGTH (LOW BYTE) 0210 ICBLH=$349 ;BUFFER LENGTH (HIGH BYTE) 0220 ICAX1=$34A ;AUXILIARY BYTE NO.1 0230 ICAX2=$34B ;AUXILIARY BYTE NO.2 0235 ; 0240 CIOV=$E456 ;CIO VECTOR 0250 ; 0260 DEVNAM .BYTE "E:",EOL 0270 ; 0280 OSCR ;OPEN SCREEN ROUTINE 0290 LDX #IOCB2 0300 LDA #OPEN 0310 STA ICCOM,X 0320 ; 0330 LDA #DEVNAM&255 0340 STA ICBAL,X 0350 LDA #DEVNAM/256 0360 STA ICBAH,X 0370 ; 0380 LDA #OWRIT 0390 STA ICAX1,X 0400 LDA #0 0410 STA ICAX2,X 0420 JSR CIOV 0430 ; 0440 LDA #PUTCHR 0450 STA ICCOM,X 0460 ; 0470 LDA #TXTBUF&255 0480 STA ICBAL,X 0490 LDA #TXTBUF/256 0500 STA ICBAH,X 0510 RTS 0520 ; 0530 PRNT 0540 LDX #IOCB2 0550 LDA #BUFLEN&255 0560 STA ICBLL,X 0570 LDA #BUFLEN/256 0580 STA ICBLH,X 0590 JSR CIOV 0600 RTS 0605 ; 0610 CLOSED 0620 LDX #IOCB2 0630 LDA #CLOSE 0640 STA ICCOM,X 0650 JSR CIOV 0660 RTS 0680 TXTBUF=* 0690 ; 0700 *=*+BUFLEN 0710 ; 0720 .END
"PRNTSC.SRC," Line by Line
Now we'll take a good close look at this program and see how it works, line by line. We'll start with the first three lines of the program, lines 290 through 310.
Initializing a Device for "OPEN"
290 LDX #IOCB2 300 LDA #OPEN 310 STA ICCOM,X
Substitute literal numbers for the variables in these three lines, and this is how they will read.
290 LDX #$20 300 LDA #$03 310 STA $342,X
These three instructions are all it takes to open a device in Atari assembly language. In order to understand what they do, you have to know something about the structure of an Atari IOCB. As we've pointed out, there are eight IOCBs in your Atari's operating system, and each one contains 16 bytes (or $10 bytes in hexadecimal notation). That means that to address IOCB #1, you have to add 16 (or $10) bytes to the address of IOCB #0 and to address IOCB #2, you have to add 32 (or $20) bytes to the address of IOCB #0. In other words, when you use the address of IOCB #0 as a reference point (as the Atari CIO system does), the offset you have to use is 32 in decimal notation, or $20 using the hexadecimal system. Here are all of the IOCB offsets used, in the Atari CIO system:
The Eight Atari IOCB Offsets IOCBO=$00 IOCB4=$40 IOCBI=$10 IOCB5=$50 IOCB2=$20 IOCB6=$60 IOCB3=$30 IOCB7=$70
Now let's take another look at our literal value version of the first three lines of the PRNTSC.SRC program:
290 LDX #$20 300 LDA #$03 310 STA $342,X
Now you can begin to see why the number $20 has been loaded into the X register in line 300. Obviously, it's going to be used as an offset in line 320, but before we move on to line 320, let's take a look at line 310, the line in between. In line 310, the accumulator is loaded with the number $03-which has been identified back in line 110 of the program as the "token for opening a device." Now what does that mean?
Well, in the Atari CIO system, each of the eight I/O operations described at the beginning of this chapter can be identified by a one-digit (hex) code, or token. Here is a complete fist of those tokens, and the operations for which they stand.
Token Name Function $03 OPEN Open a specified device or file. $04 OREAD Open a device or file for read operations. $08 OWRITE Open a device or file for write operations. $05 GETREC Read a record from a specified device or file. $07 GETCHR Read character from specified device or file. $09 PUTREC Write a record to a specified device or file. $OB PUTCHR Writ character from specified device or file. $OC CLOSE Close a specified device or file.
Line 310 Explained
Now you can see what happens in line 310 of the program PRNTSC.ASM. The accumulator is loaded with the number $03, the token for "OPEN". In line 320, the OPEN token is stored in the indirect acddress ICCOM,X (or $342,X). Just what is this address?
ICCOM is the name of one of the 16 bytes in an IOCB. Specifically, ICCOM is the first byte (the zero offset byte) in every IOCB. Look at line 170 of the PRNTSC.ASM program and you'll see that ICCOM is located at memory address $342, and is identified as the "command byte" in the Atari CIO system. It is called the command byte because it is the byte that must be addressed when devices are to be initialized, opened or closed. ICCOM is the byte that points to a set of subroutines in your computer's operating system that perform all of those functions.
Since we have listed all of the Atari I/O devices, I/O commands, I/O offsets and I/O operation codes so far, we might as well provide a list of ICCOM and the rest of the 16 bytes in each of your computer's IOCBS. Here is a complete list of the bytes in each IOCB.
Byte Adrs Name Function ICHID $0340 Handler I.D. Preset by OS ICDNO $0341 Device Number Preset by OS ICCOM $0342 Command Byte Controls CIO operations ICSTK $0343 Status Byte Returns status of operations ICBAL $0344 Buffer Address, Low Holds address of text buffer ICBAH $0345 Buffer Address, High Holds address of text buffer ICFITL $0346 Unused Pointer Not used in programming ICPTH $0347 Unused Pointer Not used in programming ICBLL $0348 Buffer Length, Low Holds length of text buffer ICBLH $0349 Buffer Length, High Holds length of text buffer ICAXI $034A Auxiliary Byte No. 1 Picks write or read operation ICAX2 $034B Auxiliary Byte No. 2 Used for various purposes ICAX3 $034C Auxiliary Byte No. 3 Used by OS only ICAX4 $034D Auxiliary Byte No. 4 Used by OS only ICAX5 $034E Auxiliary Byte No. 5 Used by OS only ICAX6 $034F Auxiliary Byte No. 6 Used by OS only
Now you can understand the operation performed in lines 300 through 320 of the PRNTSC.SRC program.
290 LDX #IOCB2 300 LDA #OPEN 310 STA ICCOM,X
In line 290, the X register is loaded with the offset for IOCB #2: the number $20. In line 300, the accumulator is loaded with the token for the OPEN operation: the number $03. In line 310, the token of the OPEN operation (the number $03) is stored in ICCOM,X: the command byte of IOCB #2. After a few more operations, we're going to issue a "JSR CIOV" (jump to SubRoutine) statement, so our Atari will jump to the CIO vector and open IOCB #2, as we have instructed. But first, we're going to have to set a few more parameters, so our computer will know exactly what kind of operations to open IOCB #2 for. So let's zip right through the rest of this "OPEN" operation now.
Completing the "OPEN" Operation 330 LDA #DEVNAM&255 340 STA ICBAL,X 350 LDA #DEVNAM/256 360 STA ICBAH,X 370 ; 380 LDA #OWRIT 390 STA ICAX1,X 400 LDA #0 410 STA ICAX2,X 420 JSR CIOV
In lines 330 through 360, the text buffer in IOCB #2 is loaded with the address of a variable defined in line 260 as DEVNAM. The variable DEVNAM, as you can see by looking at line 260 contains the ATASCII code for the character string "E: " the device name for the Atari screen editor. We could have opened IOCB #2 for any other I/O device in exactly the same way. If we wanted to use IOCB #2 as a printer IOCB, for example, we could have written line 260 this way:
260 DEVNAM.BYTE "P:",EOL
Then in lines 330 through 360, the address of the ATASCII string "P:",EOL would be loaded in ICRAL,X. With that tiny change, the PRNTSC program, instead of opening your computer screen as an output device, would open your printer! You can also use this same programming procedure to open a specific file on a disk so that you can read from it or write to it, on either a character-by-character or a record-by-record basis. In the PRNTSC program, we could open a disk file instead of the screen editor by changing line 260 to read something like this:
260 DEVNAM.BYTE "Dl:TESTIT.BAS",EOL
Then, instead of opening the screen editor, our program would open the disk file TESTIT.BAS (provided, of course, that there was a disk drive connected to our computer and that all other necessary conditions for opening such a file existed). We have just seen two examples of the tremendous power of the Atari CIO system. While the system may seem complex at first glance, its incredible versatility is a real testament to the programming know-how of Atari's computer designers.
Let's continue on now with our "OPEN" operation. In lines 390 and 400, we load the number $08 the token for "open a device for a write operation" into Auxiliary Byte No. 1 of IOCB #2. We could make our program do something completely different if we stored the value $04, the token for "open read," in ICAXL,X instead of the value $08, the token for "open write" That's another demonstration of the versatility of the Atari CIO system.
We have now read lines 410 and 420, in which we clear Auxiliary Byte No. 2 of IOCB #2 (a byte that is not used in this routine) by stuffing it with a zero. Finally, in line 430, we jump to the Atari CIO vector at memory address $E456. With that operation, we have opened IOCB #2 for a write operation to the Atari screen editor. In other words, we have opened IOCB #2 to print on the screen.
Printing a Character
We have not yet actually printed a character on the screen, however. To do that, we must carry out two more sequences of I/O operations. Now that you understand how the Atari CIO system works, that will be a snap. Look at lines 450 through 610 of the PRNTSC.ASM program.
430 ; 440 LDA #PUTCHR 450 STA ICCOM,X 460 ; 470 LDA #TXTBUF&255 480 STA ICBAL,X 490 LDA #TXTBUF/256 500 STA ICBAH,X 510 RTS 520 ; 530 PRNT 540 LDX #IOCB2 550 LDA #BUFLEN&255 560 STA ICBLL,X 570 LDA #BUFLEN/256 580 STA ICBLH,X 600 RTS
In lines 450 and 460, we store the number $0B, the token for a "put character" operation, into the command byte of IOCB #2. In lines 480 through 520, the address of the text buffer we have created especially for this program is stored in the buffer address bytes of IOCBC#2. That prepares us for the PRNT routine that starts at line 540. In the PRNT routine, which extends from line 540 to line 610, the length of our specially created text buffer is stored in the buffer length bytes of IOCB #2. Then there is another jump to the CIO vector, which automatically takes care of printing the text in the PRNTSC text buffer on your computer screen.
Closing a Device
When you open a device in assembly language (as in Atari BASIC), you must close it when you're finished with it. Otherwise, you'll cause an IOCB error, and that could cause some serious problems.
Forgetting to carry out such tasks as closing IOCBs (at the time they should be closed) can lead to program crashes and long and agonizing debugging sessions. Anyway, IOCB #2 is closed in this version of the PRNTSC program. in Lines 630 through 680, the value of $OC-the token for closing a file-is loaded into ICCOM,X. Then there's a jump to CIOV, and the Atari OS closes the IOCB.
This ends our brief glimpse into the intricacies of the central input/output system of Atari computers. But we have by no means exhausted that topic; much more information on Atari I/O is available in more advanced books than this one, and in technical reference manuals.
Return to Table of Contents | Previous Chapter | Next Chapter