assembly language
ALL ABOUT I/O
Important tutorial for both assembly language and BASIC
by MARK ANDREWS
A complete tutorial explaining how to print to the screen in assembly language. Includes valuable information on the I/O system for all programmers, but assumes some beginning knowledge of machine language. Two demonstration listings are included which will run on all Atari computers but require either Atari Assembler Editor or Macl65 (OSS). Antic disk subscribers should follow the procedure outlined in the article. Both object and source files are on the disks.
If you're an advanced beginner or intermediate Atari programmer, you're
aware that the techniques for controlling Input and Output (I/O) activities
are not always easy to figure out. In fact, this topic brings in
questions from Antic readers month after month. But now, every answer
you need for taking full charge of your I/O destiny is right here ... in
Antic's second excerpt from Mark Andrews' outstanding book Atari
Roots - A Guide to Atari Assembly Language.
If you read last issue's excerpt, "First Lesson in Assembly
Language", you know why we say that Atari Roots is the clearest-written
and most understandable book for learning Atari assembly language that
we've come across yet. But this chapter, "All About I/O", is just
as useful for a BASIC programmer as for an assembly language student because
much of the material is closely related in both languages.
Atari Roots ($14.95) is published by Datamost, 20660 Nordhoff
St., Chatsworth, CA 91311. (818) 709-1202.
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).
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 "Dl.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.
1. "OPEN" - the OPEN command.
2. "#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."
3. "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.
4. "n2" (O in our sample statement)-A device dependent auxiliary code sometimes used for various purposes (in this case, though, not used).
5. "filespec"-A device name plus a file name, if applicable. In our example, "Dl.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. It shows how to print messages on the screen.
Listing 1 is the program which we will examine. Listing
2 is a brief routine which uses listing 1 to print a short message to the
screen. To use the two listings, type them in using either Atari
Assembler Editor or Mac/65. Save the source code to disk: listing
1 will be PRNTSC.ASM and listing 2 PRTSMPLE.ASM. Next, assemble the two
source files into compiled object files called PRNTSC.OBJ and PRTSMPLE.OBJ..
(See your assembler for proper procedure.) Atari DOS 2.0 users should load
both files into memory by typing L and then the filename for each file.
After the files are in memory, type M and respond to the address prompt
with 066A. DOSXL users should LOAD each file then type RUN 066A.
"PRNTSC.ASM," 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"
300 LDX #IOCB2
310 LDA #OPEN
320 STA ICCOM,X
Substitute literal numbers for the variables in these three lines, and this is how they will read.
300 LDX #$20
310 LDA #$03
320 STA $342,X
These instructions are all it takes to open a device in Atari assembly language. 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:
300 LDX #$20
310 LDA #$03
320 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?
I/O Tokens
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.
IOCB Addresses
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.
300 LDX #IOCB2
310 LDA #OPEN
320 STA ICCOM,X
In line 300, the X register is loaded with the offset for IOCB #2: the
number $20. In line 310, the accumulator is loaded with the token
for the OPEN operation: the number $03. In line 320, 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.
In lines 340 through 370, the text buffer in IOCB #2 is loaded
with the address of a variable defined in line 270 as DEVNAM. The
variable DEVNAM, as you can see by looking at line 270 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 270 this way:
270 DEVNAM.BYTE"P:",EOL
Then in lines 340 through 370, 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 270 to read something like this:
270 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.
Moving Along
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.
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.
Mark Andrews has written 11 books about computers and is a syndicated computer columnist He recently moved from Manhattan to San Francisco's Telegraph Hill.
Listing 1: PRNTSC.ASM Download / View
Assembled: PRNTSC.OBJ Download
Listing 2: PRTSMPLE.ASM Download / View
Assembled: PRTSMPLE.OBJ Download