Classic Computer Magazine Archive COMPUTE! ISSUE 40 / SEPTEMBER 1983 / PAGE 204

INSIGHT: Atari

Bill Wilkinson


The new 600XL and 1400XL computers were exactly what I expected (except that Atari goofed and changed the number on the 1201 XL - and that's a joke until you study the case designs of the 1200XL and 1400XL). The 800XL was a little bit of a surprise, but kind of a logical step now that I have the benefit of hindsight. The 1450XLD was a pure delight.
    I really could envision a 1450XLD doing some nice, small business work. Especially if you put one of the new three-inch hard disk drives (that's over four megabytes of disk space) into that empty space supposedly designed for a second floppy.
    If Atari has any problems at all with the XL line of computers, it may be simply that they are priced too close together. After all, an 800XL is essentially a 600XL with 64K of RAM, and the already announced RAM-pack for the 600XL ends up producing an equivalent machine for the same price. Redundancy.
    The 1400XL suffers a little, also. After all, if the rumored price of the 1450XLD holds up ($800-$900 retail), why would you buy a 1400XL and then add a snail's-pace 1050 drive when you can have the much faster XLD for less money? And who but the more sophisticated user will buy a 1400XL when the 600XL (even with expansion to 64K) is so much less? Will the modem and speech synthesizer really prove attractive to a first-time user? Atari marketing obviously thinks so. I think that people who know they want those features will also know enough to want a disk drive.
    Anyway, all of that is crystal-balling and nitpicking on my part. The new lineup of computers is one that any company could be proud of. Atari should be doubly complimented after the fiasco with the 1200.

The New Disk Drives
Before I stop making observations about Atari, though, I would like to carp a bit about one thing: the new Atari disk drives and DOS III (or is it DOS 3?). When I first heard that Atari was going to throw away a potential 50K per disk drive, I thought there was an almost-good excuse. After all, Atari DOS 2.0S could, with absolutely minimum modifications, utilize all the sectors of the one-and-one-third density 1050 drive, so the change, though inefficient when compared to true double-density drives, would allow many current programs to work without modification.
    It is not to be. Atari DOS III is just as different from DOS 2.0S as our own Version 4 OS/A + is. Which means many, many programs (including data base programs, etc.) simply will not work without modification. I do not feel this is inherently bad. Let's face it: DOS 2.0S is not a particularly good DOS and it is totally inadequate for larger disk drives. DOS III is actually a very nice DOS for small drives (say up to 128K per drive). It goes downhill rapidly when used on larger drives. This means that if you convert your programs and data files from DOS 2.0S to DOS III this year, you will have to convert to some other DOS again next year, when you move to one of those nice little hard disks I mentioned.
    Anyway, when the 1050 finally appears,. watch here (I hope) for instructions for using DOS 2.0S (or OS/A+ Version 2) in one-and-one-third density mode, so you won't have to convert all your programs. (You'll still have to convert the diskettes themselves, which won't be easy or fast if you only have one drive, but the same holds true of DOS III - and, to be fair, OS/A + Version 4 - so you won't have lost anything.)

Self-Relocatable
Machine Language, Part III

This month, I will discuss some more techniques which can be used to make your machine language self-relocatable. Last month, we noted which kinds of instructions were implicitly "safe" (register-only instructions, branches, etc.). There was also a list of "Safe Relocatable Techniques." To summarize, the safe techniques mentioned were:

1. Change JMPs to branches.

2. Save register values in the stack, not in fixed memory.

3. From BASIC, pass the address of a string as a location (or series of locations) to load from or store to.

4. Move code from relocatable memory to fixed memory temporarily.

I also promised to discuss two points this month: (1) where the "safe" locations in Atari memory are; and (2) some special techniques usable only with Atari BASIC. Let me fulfill my promise.

Safe Locations
There are none. Next topic.
    Oh, all right, I admit that is a bit of an exaggeration, but it is dismayingly close to the truth. When I write machine language routines, I really do prefer that they be usable with as many products as possible. Just as a start - and not as a comprehensive list - I would hope that they would work with the following software: Atari BASIC, Atari DOS, OS/A+, BASIC A+, Atari Microsoft BASIC, Atariwriter, Atari Assembler Editor Cartridge, MAC/65, AMAC, and a few more.
    Okay. Not too long a list. How many zero page locations are not used by any of those? None. How many Page Six locations ($600 through $6FF) are not used by any of those? None. How many.... But I think you get the idea. Is all this strictly true?
    Actually, there are quite a few bytes which can be used for your temporary storage. And I suggest you consult your Atari Technical User's Notes or Mapping the Atari (from COMPUTE! Publications) to find where they are. (Caution: Watch out for changes in the new XL computers.) But even these locations are suspect. What happens if I write this neat new printer-spooler routine which uses location $00 (believe it or not, that's free in almost all the above programs), and-then you come along and add a driver for graphics mode 27 and you use location $00?
    Perhaps I am being a bit of a purist here. Certainly very little of my own programming is this clean, this free of conflict with other potential programs. And yet it really does require only a little more work to write a program "correctly" (by my definition), so why not do it right? Let's try.
    So, we must assume that no location outside our own, self relocatable, properly-loaded-at-LOMEM program is safe at all times. Unpleasant. However, that does not say that we can't use some almost-safe locations while our routine has control. In particular, you should be able to use several reserved locations in zero page (for indirect-Y pointers, etc.) by, if necessary, moving values into them from within your relocatable block; using and/or changing the zero page locations in your program; and then moving the values back into your relocatable block.
    Sounds complicated? It is. And yet you might be surprised at how seldom you really need to go through all that.
    So what zero page locations are safe, even as temporaries? Probably the safest spots, as long as you aren't writing an interrupt handler, are those locations used as temporaries by the DOS File Manager. Locations $43 through $49, inclusive, are always reinitialized by FMS every time it gets control. FMS does not presume the locations have maintained their contents from one call to the next. (In fact, the locations should properly be called "Device Driver Zero Page. Temporaries," since that is what they were intended for.)
    And one more comment before I leave you with the impression that absolutely nothing is safe to do on the Atari computers. If you are writing routines specifically designed to be used with Atari BASIC (as I suspect the majority of you are), there are several safe temporaries. First, you can always use the floating point work area, $D4 through $EF, whenever BASIC calls either a USR routine or an I/O routine. Also, BASIC does not use locations $CB through $CF (only four bytes!) at all. Again, let me give you the caution about adding your routine to a system which already has a custom routine. Be sure there is no conflict.

A Built-In Relocatable Pointer
It's true. There really is such a thing. There are some ifs though: if you are using Atari BASIC or OSS BASIC A+ or OSS BASIC XL; if you have placed your relocatable program in a string and are calling a machine language routine via USR( ADR( STRING$ ) ) or USR( "...machine-languagestring..."); if you don't mind a small trick.
    First, the trick. It's really quite simple. Whenever BASIC calls a USR routine, it calls the routine by placing the routine's calculated address in location $D4-$D5 (which just happens to be the first two bytes of floating point register zero). It then JSRs to a routine which simply does a "JMP ($D4)", a jump indirect to the USR routine.
    But why can't we take advantage of that pointer? It already points to our relocatable program, so why can't it point to our relocatable data? Perhaps a demonstration is in order.

FR0     =    $D4
USRROUTINE
        CLC
        BCC START      ; branches are ok
;
SAVEBYTE
        .BYTE 0        ; some data
;
;begin actual code
;
START
        LDY #SAVEBYTE-USRROUTINE ; index
        PLA            ; count of parameters
        CMP #1         ; how many?
        BNE NOPARAMS   ; none, we presume
; the user is passing a byte to us
        PLA            ; high byte ... ignored
        PLA            ; low byte ... stored
        STA (FR0),Y    ; thusly
;we join here, whether a byte is passed or not
NOPARAMS
        LDA (FR0),Y    ; get the byte
        STA FR0        ; to be returned
        LDA #0
        STA FR0+1l     ; high byte zero
        RTS

    This program is a very dumb one, for demonstration purposes only. If you call it from BASIC via, for example, "PRINT USR(routine)", your program will print the byte value saved in location SAVEBYTE (zero, initially). On the other hand, if you use "JUNK= USR(routine, 97)", the routine will store the second parameter (97) in location SAVEBYTE. Presumably, you could then later recover the 97.
    The point to be made, however, is that this program is completely self-relocatable and yet is able to load and store data from within its own relocatable block! The secret is the "LDY #SAVEBYTE-USRROUTINE" line directly after the label START. Since location FR0 contains the address of USRROUTINE, loading the Y-register with the proper offset (SAVEBYTE-USRROUTINE, which happens to be 3 in our example) will allow us to do indirect loads and stores to any location within 255 bytes following USRROUTINE.
    Can I put that more clearly? Since, when we do either the "LDA (FR0),Y" or the "STA (FR0),Y", the Y register contains the value 3 and location FR0 points to the location USRROUTINE, the LDA and STA instructions will reference the third byte after USRROUTINE. Which just happens to be SAVEBYTE.
    And just a reminder if you don't know or remember what the PLA instructions in this program are for. Whenever BASIC calls a USR routine, it pushes all the parameters it is given onto the CPU stack (after first converting them to 16-bit integers, of course). Then, the last thing it does before the call is to push a count of the number of parameters (presumed to be 1 or 0 in our example) onto the same stack. Thus, the first PLA lets us discover how many parameters were passed. The other two PLAs are necessary if a parameter is passed; otherwise the RTS instruction will return to an unknown location and will likely crash the system. (Note that in our simple-minded example you can probably crash BASIC by calling the routine with two parameters, since no check is made for more than one parameter.)
    Next month we're going to take this technique a couple of steps further. We will discover how to have more than 255 bytes of relocatable storage (which may or may not be useful to you) and how to generate similar self-pointers when the routine in question has not been called from BASIC.