Optimized Systems Software
A quick way to verify cassettes, a survey of languages available for the Atari, and a fix for a bug in Atari's RS-232 handlers.
Well, I didn't quite make it. I was trying to have a cassette verify program done in time for this month's column, but the pressures of writing a couple of sections for the new COMPUTE!s Book of Atari Graphics, producing three major new OSS products, and answering literally hundreds of phone calls got to me. So, wait for next month. But in the meantime, at least I have a quickie verify method that might keep the frustrations away for a month.
Quick And Dirty
One of the major flaws of the Atari computers has always been the lack of a cassette verify capability. But there is an almost effortless way to simulate this missing capability.
The secret lies in the fact that, because of Atari's superior operating system and because BASIC interfaces properly to it, you can LIST to any file or device. So, when you are ready to save your program to cassette, do not use CSAVE. Instead, Use LIST"C:" to produce an ATASCII listing on the cassette. Then you can rewind the tape and, without deleting or changing the program in memory, enter the following direct statements:
DIM Q$(256) : OPEN #l,4,0,"C:" FOR Q= TO 100000:INPUT #1,Q$:PRINT Q$: NEXTQ
Do you see the reason for the trick? Atari makes no distinction between a listing file and a data file, even on a cassette, so we can simply read the listing as data and print what we read on the screen. If what appears on the screen is correct, the cassette was recorded correctly. Incidentally, the FOR/NEXT loop is only needed so that we can enter the statements in direct mode (without line numbers). The loop will execute more times than there are lines in the file, but the end of file error will stop the process anyway. (And it is a good idea to type "END" after getting the end of file error.)
For the adventuresome, there might be an even easier way. After using the LIST"C:", simply rewind the tape and type ENTER"C:". Remember, ENTER does not erase the program in memory, but instead merges the filed program with the current one. But in this case, since the two programs are the same (if the file was recorded correctly), the ENTER should have no visible effect. If there is an error in the tape, the ENTER will simply halt and no harm will be done. Theoretically. In truth, it is possible that one line could be destroyed (if it were partially ENTERed from one tape block and then blew up in the next block). I have not tested this exhaustively, so use it at your own risk.
What is the Atari language? What is the best language for doing the most things with an Atari computer? Is there such a thing? There may be no good answers to these questions, but trying to answer them may prove interesting, so let's give it a shot.
The Atari now has a respectable complement of languages available for it. I will list those I know of here and I must apologize in advance for any omissions. The listings within each category are roughly in order of date of introduction of the product. An asterisk indicates a product no longer actively advertised, so check with the publisher for availability.
Cassette-Based Assembler – Quality Software*
Assembler/Editor Cartridge – Atari, Inc.
EASMD (Edit/ASseMble/Debug) – OSS, Inc.*
DATASM/65 - Datasoft*
MAE (Macro Assembler/Editor) – Eastern House
Macro Assembler – ELCOMP
AM AC (Atari MACro assembler) – Atari, Inc
Atari BASIC – Atari, Inc.
BASIC A#– OSS, Inc.
tiny c – OSS, Inc.
Microsoft BASIC – Atari, Inc.
QS FORTH – Quality Software
Atari FORTH – Atari Program Exchange
pns FORTH – Pink Noise Studios
PASCAL – Atari Program Exchange
ValForth – Valpar
PASCAL –Atari Program Exchange
I admit I hesitated over classifying FORTH as a pseudo-compiled language, but I was trying to group the products by speed and space considerations. Technically, FORTH is a "threaded" language, but that doesn't imply anything about its implementation. Besides, I love to bug the FORTH aficionados. Anyway, to proceed.
The assemblers grow more numerous almost monthly, and it is obvious that most serious graphics work for the Atari is still being done in assembly language, even though the 6502 has one of the strangest assembly languages in existence. (There used to be others far stranger, but they've either died out or been relegated to the dedicated controller market. You know you're an old-timer if you ever used a 4004, PPS-4, PPS-8, 8008, F-8, 2650, COPS, Til000, etc.)
Of course, Macro Assemblers are a step in the right direction, but I have yet to see any 6502 assembler system done "right," with relocatable and linkable object modules, a symbolic debugger, and more. Yet. For those of you not familiar with macro-assembly techniques, I should point out that old macro hackers usually build up a library of their favorite macros and can easily plug together several variations on a utility program (for example) by simply picking and choosing from their assortment of macros.
I don't really want to explore this subject in depth right now, but I would like to point out that, using some – or at least one – of the currently available macro assemblers for the Atari, you can write assembly language programs that look like this:
OPEN l,8,0,"D:NEWFILE" LOOP INPUT 1,LINE IFERROR EXIT PRINT 0,LINE GOTO Loop ; EXIT
It would seem to me that the percentage of Atari owners who will successfully dive into assembly language is too small to make any assembler become the dominant Atari language. Currently, though, there is no other way to write such marvels as Eastern Front, Frogger, and operating systems. So, at least for many software heavyweights, assembly is the language.
Compiling 6502 Code
I'd like to skip the interpreters for now and discuss both kinds of compilers. For starters, what's the difference between a compiler and a pseudo-compiler? Software purists could argue this point for days, but I will use a simple rule here: if it produces output, it's a compiler. If it produces tokens or words which must be interpreted, it's a pseudo-compiler.
Now, quite honestly, on a 6502 there probablv isn't much advantage in one of these over the other. Generally, a pseudo-compiler produces fewer bytes of code, but requires a relatively massive runtime support module (the interpreter, including I/O routines, etc.). As a rule, on most computers, pseudo-compiled code will run slower than compiled code because of the overhead of the interpreter.
Unfortunately, most conventional language compilers for 6502-based machines will of necessity produce large and generally clumsy code. Consider the following statement, legal, with minor variation, in most higher level languages:
array(index) = value ;
Given that all three variables shown are 16-bit globals, a really good compiler for a Z80 could produce as few as 15 bytes of code to execute it (and the one we wrote for Cromemco produces only 16 bytes).
A superb compiler for the 6502 could produce as few as 25 bytes, but only if it knew that "index" would not contain a value exceeding 127! And, oh yes, most pseudo-code compilers would probably produce 11 or 12 bytes of tokens for this same code.
So, you see, even a multi-pass optimizing compiler can at best coax the 6502 into using 1.5 to 2.5 times the amount of code that a Z80 needs. And, in truth, there aren't any "superb," "multipass," "optimizing" compilers yet available for the Atari. So the code generated will be even bigger, perhaps as much as three to four times that needed by.a Z80. (To be fair, an "average" Z80 compiler would produce 25 Or so bytes of code, itself.)
So why did we digress through all of this? Simply to show that it is remarkable that there are any compilers at all for the Atari. Of the two compilers shown, the PASCAL is the more complete language, but it is a little difficult to work with, needs a huge support library, and requires two disk drives. Still, since it is an APX product, it is a remarkable bargain. C/65, on the other hand, is a subset of the full C language; it is a one-pass compiler (no optimizing here, obviously) which produces macro assembly language output. Its primary advantages: the assembly language can produce a listing with the original C code interspersed as comments, it uses a very small support library, and it can run on a single drive. But I think we may not have seen the end of compiler efforts on the 6502.
But now we come to my favorite topic: interpreters. Despite its shortcomings as a compiled-for machine, the 6502 comports itself nicely when interpreting: it is fast and needs only relatively compact code to implement. Why? Simply because interpreters generally work on "lines" of input. But if we limit a line to 256 characters (a very reasonable limitation), we find that there are several modes of operation on the 6502 that just love working with such short character strings. (Especially, of course, the "indirect indexed" or "(zero page),Y" instructions.) The truth of the matter is that the designer of a 6502-based interpreter has a lot of leeway in prescribing how the language will run best.
So look at the wealth of interpreters available already! With more to come, I am sure. We find in these interpreters the most used of all Atari languages, Atari BASIC. Well, that's not surprising, considering that it's essentially a required ingredient in an Atari system. But let's come back to it in a moment.
Naturally, PILOT is here. It's a nice, simple language which can easily be interpreted. It was probably a joy to program; I would have loved being involved.
But there are some real powerhouse languages here, also. LISP has traditionally been an interpreter, the darling of the Artificial Intelligence people. And, finally, there is Microsoft BASIC and BASIC A + . Quite honestly, I feel that these last two languages provide the best and easiest access to the Atari's features. Naturally, I am prejudiced towards BASIC A +, but the Microsoft BASIC has a few nice and unique features even if it isn't quite as easy to use.
What's The Atari Language?
So, after all that, just what is the Atari language? Well, I'm going to cop out and say that it's Atari BASIC. Despite all the nasty things said about the poor thing, look at all the things written in BASIC. And they work.
Atari BASIC is an excellent starting point. The easiest next step is BASIC A +, but most people won't have too much trouble learning other algebraic languages, such as PASCAL or C (the only real problem with these languages is that debugging is so much harder than with an interpreter). I consider PILOT and LISP useful languages in their own right, but much of what you learn in them is non-transportable to other languages.
The same is true of FORTH. FORTH enthusiasts would have you believe that FORTH is the only language you will ever need. Nonsense. Each language has its uses, its strong points, and its failings. (In my opinion, the major failings of FORTH are (1) that it operates independent of the host system's DOS and (2) techniques learned in FORTH are often non-transportable to other languages, because of FORTH's reverse-Polish notation. However, I respect the language for what it is: a hacker's dream come true. And I'm a hacker.)
Personally, I like to collect languages the way other people collect games. Seldom will I find one that won't teach me something new about how computers can be made to work. So try some "foreign" languages yourself soon and see how much fun they can be. (And pain and trouble and frustrating and educational and uplifting.)
System Reset And The 850
A couple of times in the past, I have presented in this column the "rules" for adding device drivers to Atari OS. Well, would you believe it, Atari itself broke the rules when they implemented the 850 (RS-232) handlers. The violation was a minor one, yet the consequences can be severe. To start with, let's recap my rules:
- Locate the current value of system LOMEM (contents of $02E7).
- Load your driver into memory and relocate it to LOMEM.
- Adjust the contents of LOMEM to reflect the memory being used by your driver.
- Add your device's name and handler address to the handler table (HATABS, at $031 A).
- Get the current value of DOSINI (location $000C) and save it somewhere in your handler. Put your own initialization address into DOSINI.
- Whenever your initialization routine is called (i.e., when System Reset is pushed by a user), first call the initializer whose address was in DOSINI before you changed it. Then perform steps 3 and 4 again, since Reset will have changed LOMEM and reloaded the HATABS.
Now step 2 is the most difficult of these to accomplish, in practice, because it is hard to produce a relocatable module on the Atari. Many programs I have seen (and written) are actually assembled absolute at a "known" good location. This is okay, if you are writing for your own private system: you know what will be loaded when and where. But if you are producing a driver for sale, you really should follow the rule faithfully.
Atari's 850 drivers do, indeed, relocate themselves beautifully. They add their name to the handler table. They adjust the system LOMEM pointer. So what do they do wrong? One minor thing: they do steps 3 and 4 before they call the old initialization routine (see step 6) instead of after!
The result: the 850 handler changes LOMEM to just above itself and then calls the DOS initialization, which resets LOMEM to just above DOS! Thus, the RS-232 handlers are not protected from programs which come in and quite properly use RAM starting at LOMEM. Generally, if you are running with Atari BASIC, this won't affect you, since BASIC maintains its own pointer to LOMEM once it is initialized at power on. But if you return to DOS without MEM.SAV, or run some assembly language utility… well, there are just too many cases where this little faux pas can wipe you out.
I am currently working on a patch (ready by next month, I hope) to the handler (to be made via the handler loader) which will fix this problem. In the meantime, it might be a good idea to have your programs check for the existence of the "R" name in HATABS and avoid the appropriate amount of memory if it is found.
In December we'll have some heavy assembly language stuff, what with the patch to the 850 handler and the cassette verify routine. I hope to return to some more BASIC stuff to start off the new year.