Machine Language Graphics
Last month we looked at how Atari BASIC translates its own graphics-oriented statements into simpler pieces for its calls to the Atari's operating system (the OS). Or, more correctly, we showed how you could do such an expansion. When Atari BASIC executes a statement in your program, it actually interprets it as a request to do a series of machine language operations—the equivalents of the simplified pieces we discussed last month.
The only example we've taken a close look at so far is POKE. I showed you that
may be accomplished by the machine language instructions
LDA xpos STA 85
(Remember, I'm using variable names with lowercase letters on purpose, to remind you that the names are arbitrary. Please pick your own.)
Again, if you go back to last month's column, you'll find that the only BASIC statements I used to simulate the graphics commands of BASIC were OPEN, CLOSE, PUT, GET, and XIO. You may also have noted that each of these statements was associated with a channel number (specifically, channel 6, because that's where BASIC does all its graphics operations). You won't be too surprised, then, when I tell you that each of these five is actually a fundamental OS operation. Specifically, each involves a direct call to Atari's Central Input/Output (CIO) processor. You may, however, be a little startled when I tell you these five calls represent all but one of the fundamental OS operations. (The missing one is represented by BASIC's STATUS statement, which is generally used only for modem operations because of a flaw in BASIG's implementation of the OS call.)
The point of all this is both simple and important: If you master these five OS calls from machine language, you can use virtually any input/output(I/O) operations you might need or want. For example, you can read records from a disk file using only three of these operations (OPEN, GET, and CLOSE). True, there are some variations on GET and PUT that are useful with lines of text or with large files, but the concepts are all the same. So, without further delay, let's translate the five BASIC I/O statements into five machine language routines.
All I/O on the Atari is controlled through eight Input/Output Control Blocks (IOCBs), one for each channel or file number. Each IOCB is 16 bytes long and is located adjacent to another, beginning at addresses 832, 848, 864, and so on. (In hexadecimal, the sequence is $340, $350, $360, and so on.)The channels are numbered 0-7 in BASIC, but in machine language, we use the offset from the start of the first IOCB as the IOCB number. Under this system, the first block is still IOCB 0, but the fourth, known as channel 3 in BASIC, is designated as IOCB number 48 ($30). The reason for this is because it begins at memory location 880 ($370), which is 48 bytes beyond the start of IOCBs at location 832.
To perform any I/O operation, you put information into certain places in the IOCB of your choice. Then you put the IOCB number into the processor's X register and call the CIO routine at address $E456 in ROM. (I'm not going to put in the decimal equivalents from now on. You really should learn to use hexadecimal—it's much more logical for machine language.) The only magic, then, is in learning just what to put into the IOCBs.
Each IOCB consists of 16 bytes, as shown in Table 1.
All of these labels and bytes have uses (I refer you to Mapping the Atari, or Atari Roots for more details), but for our purposes, we need to learn about only a few of them. Again, I have prepared a chart (Table 2) to summarize which labels are meaningful for which graphics-related commands. (Remember, see last month's column for examples of the BASIC commands we are using.) If a labeled location has a number assigned to it, then use that number with the operation. Descriptions in italics (device, for example) will be explained in the text that follows. An X means that the value in the corresponding location has no effect for the operation, and = = = means that the contents of the corresponding location should not be disturbed. For our purposes, these two symbols are equivalent: We won't change the contents of these locations.
|Label Name||Size in bytes||Offset in IOCB||Mnemonic Description|
|ICAX1||1||10||Auxillary byte 1|
|ICAX2||1||11||Auxillary byte 2|
|ICAX3||1||12||Auxillary byte 3|
|ICAX4||1||13||Auxillary byte 4|
|ICAX5||1||14||Auxillary byte 5|
|ICAX6||1||15||Auxillary byte 6|
|GET||7||X||$0000||= = =||= = =|
|PUT||11||X||$0000||= = =||= = =|
|XIO||xio||device||X||= = =||= = =|
CLOSE is the simplest of the routines. To do a CLOSE, you simply place the command number in the appropriate location, load the X register properly, and call CIO. The complete routine looks like this:
LDX #$60 ;using channel 6—graphics LDA #12 ;CLOSE command STA ICCOM,X ;put command in place JSR $E456 ;call CIO
Don't understand all that? Don't worry. A few sessions with an assembler and a good tutorial will help you get started.
For OPEN and XIO, the buffer address (ICBA) field should contain the address in memory of the beginning of a string, and that string should have the name of the device (and/or file) that you wish to work with. For graphics, the device name is always S:. The command value (ICCOM) is always 3, for OPEN. For XIO, you use the same number you would in BASIC. (For example, 17 for DRAWTO, as we saw last month.)
For OPEN, the first two auxillary bytes (ICAX1 and ICAX2) correspond to the two auxillary values in the BASIC version of the statement. Although ICAX2 is usually given a zero value, when opening a graphics screen, it gets the number of the appropriate graphics mode instead. Usually no command, except OPEN, should touch the auxillary two bytes. (Atari BASIC actually errs in making them part of the normal XIO commands, and that's why we had to stick in a value of 12 in our DRAWTO equivalent last month. The exceptions that prove the rule are various modem command XIOs, used with the R: device.)
Finally, for GET and PUT, as we will use them for graphics, you need only put a value of zero in both bytes of the buffer length (ICBL), put the appropriate command value (7 or 11) in its field (ICCOM), set up the X register, and use the A register to transfer the byte. That is, if you want to PUT a byte to the screen— which, as I hope you remember from last month, is how you implement PLOT—put the byte (for example, the color value) in the A register just before calling CIO. If you want to GET a byte from the screen to simulate the LOCATE command, do all of the above and the byte will be in the A register after your call to CIO.
Too complicated? Cheer up. This is the worst of it. Next month we'll put together some bona fide examples to try out. Next month will also be the last part of this series on converting BASIC graphics commands to machine language. I intended all of this to be an introduction (or refresher, for you old-timers) to machine language. If you want to take this topic further, you really must get an assembler and a couple of books. Good luck.