The series on writing your own interpreter continues. In part 2, the expression evaluator and the "PRINT" statement are added to BAIT. There's also a look at Atari's new 200XL computer.
We hope to introduce several new products at the West Coast Computer Faire this year, including some designed specifically for the new model 1200 Atari (of which machine I will speak more below). I can't tell you exactly what the new products will be, but I can say that I think that those who have written software which follows the "rules" will benefit.
Which "rules"? Oh, nothing much. Just those regarding LOMEM, HIMEM, device drivers, reset vectors, break vectors, etc. If you are an author (or company) who is developing or has developed software for the Atari computers, you might want to ask Atari for a copy of the note from Howard Chan, Manager of Software Acquisition, which details what Atari considers the "untouchable" locations as well as what "vectors" are immutable. We hope to be able to reproduce that note in this column next month.
Anyway, what are we looking into in this month's column? Obviously, we will have part two of the series on writing your own interpreter. (And if you missed part one, you must go out right now and buy the March issue! We cannot and will not recap the materials previously covered.) Also, as mentioned, I would like to briefly discuss the new Atari 1200XL machine. But first I am going to hang my head a little.
Pardon Me, My Pratfall Is Showing
After giving everyone else (particularly Atari) a hard time about not doing things "right," I am embarrassed to admit that I, too, did a thing definitely "un-right."
I must start by giving credit to F. T. Meiere, President of the Indy Atari Club from Indianapolis, for not only finding my goof, but also giving me what seems to be a workable and proper fix.
The mistake occurred, not surprisingly, in my fix to the Atari RS-232 drivers, as published in this column in the December 1982 issue of COMPUTE!. It came about because of the variety of configurations that I work in. The possible combinations I use can be shown as a small array:
Now, obviously, the vast majority of the Atari user population finds itself in the upper left box (Atari BASIC with Atari DOS). And, yet, because I really don't like working with "MEM.SAV" and "DUP.SYS" (and the consequential swapping in and out and sometimes losing my memory and …), I generally leave that left-hand column for last. And, unfortunately, in this case I apparently didn't even get to it. For shame.
Anyway, taking F.T. Meiere's advice to heart, I have indeed tested the change he has proposed in several of the possible configurations. Additionally, I have looked at my original code and found out why it failed (and why this new code works). So here, without further ado, is the fix to my RS-232 fix in the form of a change to line 1990 of the assembly language code:
was: 1990 JMP (DOSINI) WRONG! now: 1990 JMP PATCH3 RIGHT!
To Excel Or Not To Excel
The new Atari machine is named the "1200XL." I suppose the "XL" is supposed to designate speed and sexiness, a la sports cars. And certainly the machine looks sleek and sexy enough; it is by far the best looking of the current crop of home computers. Were it not for the serial I/O cable, you could easily envision holding the machine in your lap while leaning back in your easy chair, admiring and caressing it as you would a glass of good wine.
Let's look at the obvious features:
- Pluses: 62K of RAM, two character sets, a self-test capability, nearly complete compatibility with the 400/800 systems, four function keys and a "help" key, two status LEDs.
- Minuses: One cartridge slot (on the side, and you can remove the cartridge with power on even though you shouldn't), two (not four) joystick ports (both on the same side of the case; consider getting a joystick cord extender for two-person games), no memory board slots, no external expansion capabilities.
- Implications: Goodbye, 80-column boards. Good-bye, RAMDISKs and the like. Goodbye, CORVUS hard disk drive (which, I believe, interfaces via joysticks three and four).
- Unfounded rumors: There is not an RS-232 interface built in. There is certainly no parallel printer port. In fact, there is no hardware other than what I have described.
Some "features" of the machine are less obvious: none of the current Atari software will take advantage of the expanded RAM. When you bank select the RAM, all of the OS software, including the interrupt handlers, goes away, so you must provide at least a minimal OS substitute. Because the I/O space is from $D000 to $D800 (as on the 400/800), there is no way around having a "hole" in your otherwise contiguous RAM. There is no way to get at the RAM which is "under" the cartridge (this flaw is left over from the 400/800; it is a real deficiency). It uses the same old slow floating point routines.
So how do I rate the 1200XL in overall features and performance? Quite honestly, it depends entirely on what the price of the machine is. At anything under $450, it's a terrific bargain. I feel that, given the obvious cost-cutting Atari was able to achieve, it should be able to sell for half the cost of the 800. However, the indications are that the price of the 800 will be dropped and that the 1200 will cost more than the 800. If so, buy an 800 quick!
The exception to this suggestion is if you will write in machine language or be using non-Atari languages that can take advantage of the extra 14K of RAM (now where would you get a language like that?). If you need the extra RAM, then you may have to seriously consider the 1200. Of course, by the time you read this, the price of the 1200 and the new price of the 800 should be public knowledge, so you will be able to see how accurate my forecasting is.
BAIT, Part 2
In March, we started the process of writing a pseudo-BASIC interpreter, which I called "BAIT." If you don't have that article, this month's work will make virtually zero sense, so don't even attempt to follow the rest of this column.
This month, as promised, we add the expression evaluator and the "PRINT" statement to BAIT. Note that the listing published here is not complete. It is meant to be added to the March listing. In a few cases, this month's lines will over-write (be the same number as) those from March. For example, we have replaced lines 4010 through 4040 and deleted line 4050.
Before we get into the explanation of the actual listing, we need to extend our discussion of just how an interpreter - and, in particular, BAIT - works.
There are two major parts to most language interpreters: the program editor and the program executor. The March column presented BAIT's editor. It is not fundamentally different from most BASIC editors. True, only a few BASICs that I know of use a line number table, as we did for BAIT (some that do include Cromemco 32K Structured BASIC, which we wrote, and Data General's Business BASIC, both designed for relatively large machines). But, to be fair, BAIT cheats by using a very small fixed number of possible line numbers.
The editor used by Atari BASIC and BASIC A + (and Cromemco and DG BASICs) does, however, differ markedly from BAIT's editor in one important apsect. In these more sophisticated BASICs, the user's program line is scanned for correct syntax as it is entered and automatically converted to more usable internal "tokens." Of course, BAIT should not be chided for any deficiency here: most microcomputer BASICs (including, for example, Microsoft BASICs) do not do any syntax checking at entry (nor do they to-kenize anything except, perhaps, recognized keywords). In any case, BAIT's editor seems quite adequate to me.
This month, we begin the second major part of an interpreter: the program executor. Not surprisingly, the program executor is much larger and more complex than the editor. In fact, we need to break the executor down into manageable hunks. I think an outline would be useful here.
I. Program Editor
II. Program Executor
B. Execution by Line
1. Execution by Statement
2. Execution of Statements
a. Display statement
b. Print statement
C. Execution of a direct statement or line
D. Error handler
This month, we will add parts C, D, and B to BAIT. (Note that we did part A in March and faked C.) Actually, part C and part B are so intimately entwined in BAIT that it is hard to see where one begins and the other leaves off, but that doesn't make our outline any less valid.
Executing Expressions In BAIT
Not shown in the above outline are the major routines which are common to the execution of most statements. To illustrate, first consider these two BAIT statements:
L A = 7*13 (Let A = 7*13) P A + 5 (Print A + 5)
What do these two statements have in common? An expression. From BAIT's viewpoint, the two expressions here are "7*13" and "A + 5". A major portion of BAIT (and, indeed, a major portion of any language) is the subroutine known as "EXecute EXPression," which resides in lines 5000 through 5999 in the accompanying listing. Actually, EXEXP in BAIT is fairly simple when compared to that of Atari BASIC. Remember the rules from last month? No functions, no precedence of operators, no arrays, no strings.
Not surprisingly, almost all BAIT statements call the EXEXP subroutine. In turn, EXEXP calls a couple of routines, including GETNC (GET Next Character – lines 8100 to 8160). GETNC is perhaps the lowest level routine of the program execution phase of BAIT. It simply scans the program memory for the next non-space character, tests to see if it is an alphabetic character, and protests when the line runs out of characters.
EXEXP uses GETNC (line 5100) to find any ALPHAbetic characters in an expression; such characters are assumed to be variables (lines 5300, 5310). If instead, GETNC found a numeric character (line 5110), EXEXP backs up and scans for the entire number (lines 5400 to 5450). Only digits and a decimal point are allowed (line 5430); but there is a flaw (read that as bug) here that allows, but ignores, more than one decimal point and the digits which might follow. Finally, if the character is neither alphabetic nor numeric, BAIT assumes that it is an operator and figures out which one (lines 5120 to 5230). If it is not an operator, and if the expression was valid, EXEXP returns to its caller (line 5160).
Note that in the case of either a variable or a numeric literal, EXEXP assumes that it has received the second argument of an expression of the form "argl op arg2" (lines 5500 through 5530). Of course, in the case of the very first argument in any expression, there has been no preceding argument. But EXEXP takes care of that by providing a dummy argument ("0") and a dummy operator ("+rdquo;) in its initialization code (line 5010). Incidentally, if EXEXP detects two operators or two arguments in a row, it rules the expression invalid (lines 5210, 5220, and 5510). Similarly, null expressions and expressions ending in an operator are illegal (lines 5230, 5530, and 5160).
Finally, the actual operators of BAIT are "simulated" via Atari BASIC in lines 5610 through 5680. Note that BAIT allows BASIC's operators "+", "-", "*", "/", ">", "<", and " = ". BAIT simplifies the inequality sign to "#", instead of BASIC's "<>". (But did you know that many, many of the early BASICs used or allowed "#" as an alternative to "<>"?)
Normally, I wouldn't be so bold as to suggest changing an entire section of code, but I think the clumsiness of EXEXP deserves at least one alternative idea. If you are using BASIC A + (or any BASIC with a "FIND" or "SUBSTRing" function), you could replace lines 5120 to 5128 with a single line of code:
5120 OP = FIND("+-*/>=", C$, 0): IF OP THEN 5200
Of course, one could have achieved similar results with a string and a FOR/NEXT loop under Atari BASIC, but that would have slowed down EXEXP even more than it already is.
BAIT's Print Statement
Lines 10200 through 10330 comprise the execution of "Print" under BAIT. Notice that DOPRINT also uses GETNC (line 10210). Here, we are looking to see whether a quoted string (line 10220), an expression (line 10240), or nothing at all (line 10210) follows the "P" keyword. (Or should we call it a key-letter?)
Literal strings are fairly simple to handle. Starting at the character after the quote mark, we simply loop through the buffered line printing characters as we go and looking for an ending quote (lines 10300 and 10310). If no matching quote is found, it is not an error, just as with Atari BASIC (end of line 10310). If the quote is found, we adjust the character pointer and look for a trailing semi-colon or comma (lines 10320, 10330, then 10250 to 10280).
And, strangely enough, arithmetic expressions are the easiest of all things to print. We simply call EXEXP and display the calculated result (line 10240), falling through to the trailing semi-colon and comma check. (Of course, if we were writing in assembly language, we would have to write the "display a numeric result in ASCII" routine, but even here the Atari OS ROMs would help us.)
What Else Was Added
Finally, we must comment on the other code that was added this month. Most of it, of course, was needed to support the EXEXP and DOPRINT routines. However, some of it certainly is obscure enough to bear explanation. As we did in March, we will comment on the code by line number(s).
1100. C$ is used to capture the next character by GETNC. The array VARIABLES is designed to hold 26 variables (A-Z). One could easily amend this to any multiple of 26 and allow variable names of the form A1, A2, etc.
1110. This is kind of silly. In the final code, all variables will be initialized to zero. However, since we do not yet have a "Let" statement, I wanted to give each variable a unique value so we could use it in "Print". Hence, A = 1, B = 2, C = 3, etc.
1120. Simply a place to stuff an error message.
1520 to 1550. The line numbers of some of our more important routines.
1710. I hate using "TRAP 40000". I like "TRAP UNTRAP" much better.
2360. The only line I actually corrected from the March listing. Do you see what the bug was?
3320. Just changed the comment to make more sense.
4010 to 4040. The beginnings of our "Line execution" control routine. We get the starting and ending positions of the current line. If the line doesn't exist, we try for the next line. If this is a direct line, we flag it for later detection (line 4040).
4210. As things sit now, if we get here we are ready to execute the direct statement. It had better be the "P" (Print) key-letter.
4220. Why call line 4900? Why not do it in-line right here? Wait until next month.
4610. If we didn't just execute a direct line, we go do another line. (Won't happen this month.)
4620 to 4640. This code was at lines 4010 to 4040 last month. It just cleans up the program buffer for use by the editor.
4910. Read line 4920.
5010 to 8160. Described in the text above.
8200 to 8290. Why do this several places wher a single routine will do? Note line 8240: Atari BASIC does a similar thing with the 6502's CPU stack when it encounters an error. Why try to recover through who knows how many subroutine calls when one can simply reset the stack to the top and ignore them?
10200 to 10330. Described in the text above.
Using What We Have
Again, BAIT seems to work as designed up to this point. You can type in program lines (with preceding line numbers) or you can type in a direct statement. Unfortunately, all direct statements are assumed to be "Print," but just wait until next month.
And just what can you "Print"? Virtually any numeric expression that uses the BAIT operators and literal numbers. Of course, you can also use the variable letters "A" through "Z," but this month you will get the artificial values they contain. To get you started, here are some statements to try when you get BAIT's "ready" prompt:
P "HI THERE" P "HI THERE", P "HI THERE"; p 1 + 2 + 3 + 4 p 1 + 2 + 3 + 4 P A + B + C + D P 4>5 P 4<5 P 1/3 P 1/2 = 0.5 P 1/2 # 0.5 P 1/3;
And one last P.S., a kind of taste of what's to come. Once you have the listing working and saved, try adding one line:
4905 IF C$ = "D" THEN GOTO DODISPLAY
If you don't see what it allows, then wait for next month.
Naturally, we will have Part 3 of BAIT. We will actually begin running BAIT programs, and we will add about half of the remaining BAIT statements to our vocabulary.
Unless something else hits me in the next week or two, I think I will respond to my own challenge and begin talking about how to write self-relocatable assembly language.