Atari BASIC

Atari BASIC is an interpreter for the BASIC programming language that shipped with the Atari 8-bit family of 6502-based home computers. Unlike most BASICs of the home computer era, Atari BASIC is not a derivative of Microsoft BASIC and differs in significant ways. It includes keywords for Atari-specific features and lacks support for string arrays, for example.
The language was an 8 KB ROM cartridge for the first machines in the 8-bit family: the 400, 800, and later the 1200XL. BASIC is built into subsequent XL and XE models, but can be disabled by holding the codice_1 key while booting. The XEGS disables BASIC if powered without the keyboard attached.
Atari BASIC was near the bottom in performance benchmarks of the era. The original authors addressed most of these issues in BASIC XL and BASIC XE. A host of third-party solutions like Turbo-Basic XL also appeared.
The complete annotated source code and design specifications of Atari BASIC were published as "The Atari BASIC Source Book" in 1983.
History.
The machines that would become the Atari 8-bit family had originally been developed as second-generation video game consoles intended to replace the Atari 2600. Ray Kassar, the new president of Atari, decided to challenge Apple Computer by building a home computer instead. This meant the designs, among other changes, needed to support the BASIC programming language, then the standard language for home computers.
In early 1978, Atari licensed the source code to the MOS 6502 version of Microsoft BASIC. The original Altair BASIC on the Intel 8080 came in three versions for different memory sizes, 4, 8 and 12 kB. The three versions were very different; the 4k version lacked string manipulation and used a 32-bit floating point format, the 8k version added string functionality, while the 12k version added more functions, 64-bit variables, and other features. By the time the Atari machines were being designed, RAM was becoming much less expensive. Microsoft took advantage of this by producing a single version for the 6502, most closely resembling the 8k version from the 8080. It was offered in two very similar versions, one using the original 32-bit number format that was about 7800 bytes when compiled, and another using an extended 40-bit format that was close to 9 kB.
Even the 32-bit version barely fit into the 8 kB size of the machine's ROM cartridge format. Atari also felt that they needed to expand the language to support the hardware features of their computers, similar to what Apple had done with Applesoft BASIC. This increased the size of Atari's version to around 11 kB; AppleSoft BASIC on the Apple II+ was 10240 bytes long. After six months, the code was pared down to almost fit in an 8 kB ROM. However, Atari was facing a January 1979 deadline with the Consumer Electronics Show (CES) where the machines would be demonstrated. They decided to ask for help to get a version of BASIC ready in time for the show.
Shepardson Microsystems.
In September 1978, Shepardson Microsystems won the bid on completing BASIC and was finishing Cromemco 16K Structured BASIC for the Z80-based Cromemco S-100 bus machines. Developers Kathleen O'Brien and Paul Laughton used Data General Basic, an integer-only implementation, as the inspiration for their BASIC, given Laughton's experience with Data General on a time-sharing system.
What became Atari BASIC is a pared-down version of Cromemco BASIC ported to the 6502. That needed 10K of code. To make it fit in Atari's 8K cartridge, some of common routines were moved to the operating system ROMs. This included 1780 bytes for floating point support that were placed in a separate 2K ROM on the motherboard.
Atari accepted the proposal, and when the specifications were finalized in October 1978, Paul Laughton and Kathleen O'Brien began work on the new language. The contract specified the delivery date on or before 6 April 1979 and this also included a File Manager System (later known as DOS 1.0). Atari's plans were to take an early 8K version of Microsoft BASIC to the 1979 CES and then switch to the new Atari BASIC for production. Development proceeded quickly, helped by a bonus clause in the contract, and an 8K cartridge was available just before the release of the machines. Atari took that version to CES instead of the MS version. Atari Microsoft BASIC later became available as a separate product.
Releases.
The version Shepardson gave to Atari for the CES demo was not supposed to be the final version. Between the time they delivered the demo and the final delivery a few weeks later, Shepardson fixed several bugs in the code. Unknown to Shepardson, Atari had already sent the CES version to manufacturing.
This version was later known as Revision A. It contains a major bug in a subroutine that copies memory; deleting lines of code that were exactly 256 bytes long causes a lockup. This was sometimes known as the "two-line lockup" because it did not trigger until the next line of code or command was entered. It cannot be fixed by pressing the key.
Revision B attempted to fix all of the major bugs in Revision A and was released in 1983 as a built-in ROM in the 600XL and 800XL models. While fixing the memory copying bug, the programmer noticed the same pattern of code in the section for inserting lines, and applied the same fix. This instead introduced the original bug into this code. Inserting new lines is much more common than deleting old ones, so the change dramatically increased the number of crashes. Revision B also contains a bug that adds 16 bytes to a program every time it is codice_2d and codice_3ed, eventually causing the machine to run out of memory for even the smallest programs. "Mapping the Atari" described these as "awesome bugs" and advised Revision B owners "Don't fool around; get the new ROM, which is available on cartridge" from Atari. The book provides a type-in program to patch Revision B to Revision C for those without the cartridge.
Revision C eliminates the memory leaks in Revision B. It is built-in on later versions of the 800XL and all XE models including the XEGS. Revision C was also available as a cartridge.
The version can be determined by typing codice_4 at the READY prompt. The result is codice_5 for Revision A, codice_6 for Revision B, and codice_7 for Revision C.
Description.
Program editing.
Like most home computer BASICs, Atari BASIC is anchored around its line editor. Program lines can be up to three physical screen lines of 40 characters, 120 characters total. The cursor can be moved freely, with the editor automatically tracking which BASIC program line the current screen line is part of. For instance, if the cursor is currently positioned in line 30 and the user uses cursor-up into line 20, any editing from that point will be carried out on line 20.
Atari BASIC's editor catches many errors that would not be noticed in MS-derived versions. If an error is found, the editor re-displays the line, highlighting the text near the error in inverse video. Errors are displayed as numeric codes, with the descriptions printed in the manual. Because of the way the line editor works, the user can immediately fix the error. In the example pictured above (with codice_8), the error can be fixed by moving the cursor over the codice_9, typing (the editor only has an overwrite mode), and hitting .
A line entered with a leading number, from 0 to 32767, is inserted in the current program or replaces an existing line. If there's no line number, the interpreter assigns it the number -1 (800016) and the commands are executed immediately, in "immediate mode". The codice_10 command executes the stored program from the lowest line number. Atari BASIC allows all commands to be executed in both modes. For example, codice_11 can be used inside a program, whereas in many interpreters this would be available in immediate mode only.
During entry, keywords can be abbreviated using the pattern set by Palo Alto Tiny BASIC, by typing a period at any point in the word. So codice_12 is expanded to codice_11, as is codice_14. Only enough letters have to be typed to make the abbreviation unique, so codice_15 requires codice_16 because the single letter P is not unique. To expand an abbreviation, the tokenizer searches through its list of reserved words to find the first that matches the portion supplied. More commonly used commands occur first in the list of reserved words, with codice_17 at the beginning (it can be typed as codice_18). When the program is later codice_11ed it will always write out the full words with three exceptions: codice_20 has a synonym, codice_21; codice_22 has a synonym, codice_23; and codice_24 has a synonym which is the empty string (so 10 LET A = 10 and 10 A = 10 mean the same thing). These are separate tokens, and so will remain as such in the program listing. MS BASICs also allowed codice_21 as a short-form for codice_20, but this used the same token so it expanded back to when ed, treating it as an abbreviation, not a synonym.
The tokenizer.
When the user presses while editing, the current line is copied into the BASIC Input Line Buffer in memory between 580 and 5FF16. Atari BASIC's tokenizer then scans the text and converts it to single-byte tokens stored in the token output buffer. The output buffer is located at the first 256 bytes of the lowest available free memory, pointed to by the LOMEM pointer stored at 80, 8116. The output from the tokenizer is then moved into more permanent storage in various locations in memory. The program is stored as a parse tree.
As is the case with most BASICs of the era, all keywords are converted into a one-byte token. For instance, is converted to 2016. This process aids the system in two ways. Because the original (in this case) five-letter keyword is replaced by a single byte, the resulting program code may be significantly smaller in memory. This also greatly aids runtime performance as the same text does not have to be scanned every time it is encountered, it is scanned only once at edit time.
Atari BASIC differs strongly from most BASICs of the era, notably Microsoft BASIC which was widely used, in that the entire line is converted to its runtime version. In MS BASIC, any item that is not a keyword - variable names, numeric constants, strings, etc. - are left in their original text format. At runtime, whenever these items are encountered they have to be read and converted to the internal format, which might mean a numeric constant like "100" will have to be converted to its internal floating point format over and over. Even an item like a string constant, which is stored mostly in its original format, still requires MS BASIC to scan the line of text to find the trailing close-quote.
In contrast, when Atari BASIC encounters this value it will first place a token in the output indicating a constant follows. It then runs the text-to-number conversion routine immediately to convert it to its 40-bit binary coded decimal format. The resulting six-byte value is placed in the output buffer and no longer has to be scanned. String constants are scanned and their length placed in the tokenized line, avoiding this task at runtime. Likewise, whereas variables in MS BASIC are set up in a table in memory when they are first encountered during runtime, in Atari BASIC this is performed during tokenization and the original variable name in text is replaced by a single byte pointing to the resulting storage. During runtime, MS has to search through the variable name table every time it encounters a variable name, whereas Atari BASIC has immediate access via a pointer.
Shepardson referred to this complete-tokenizing concept as a "pre-compiling interpreter". The resulting fully tokenized code has the advantage of eliminating any parsing during runtime, which has the potential advantage of making it run faster. It has the disadvantage that small constants, like 0 or 1, are stored internally as six-byte values, potentially making the resulting tokenized code longer than the original text. Thus, although the input text buffer is only 120 bytes and the output is 256, the possibility exists that the output buffer will overflow, resulting in error 14 – line too long.
A set of pointers (addresses) indicates various data: variable names are stored in the "variable name table" (VNTP – 82, 8316) and their values are stored in the "variable value table" (pointed to at VVTP – 86, 8716). By indirecting the variable names in this way, a reference to a variable needs only one byte to address its entry into the appropriate table. String variables have their own area (pointed to at STARP – 8C, 8D16) as does the runtime stack (pointed to at RUNSTK – 8E, 8F16) used to store the line numbers of looping statements (codice_27) and subroutines (codice_28). Finally, the end of BASIC memory usage is indicated by an address stored at MEMTOP – 90, 9116) pointer.
Mathematical functions.
Atari BASIC includes three trignometric functions: sine, cosine, and arc tangent. codice_29 and codice_30 set whether these functions use radians or degrees, defaulting to radians.
Eight additional functions include rounding, logarithms, and square root. The random function, codice_31, generates a number between 0 and 1, with the parameter to the function not being used.
String handling.
Atari BASIC differs considerably from Microsoft-style BASICs in the way it handles strings. Microsoft BASIC mostly copied the string-handling system of DEC's BASIC-PLUS, in which strings are first-class types with variable lengths and bounds. This allows both string variables, as well as arrays of strings, as both are represented internally by a computer word pointing to storage on a heap.
In contrast, Atari BASIC copied the string-handling system of Hewlett-Packard BASIC, where the basic data type is a single character, and strings are arrays of characters. A string is represented by a pointer to the first character in the string and its length. To initialize a string, it must be DIMensioned with its maximum length, thereby setting aside the required amount of memory on the heap. For example:
10 DIM A$(20)
20 PRINT "ENTER MESSAGE: ";
30 INPUT A$
40 PRINT A$
In this program, a 20 character string is reserved, and any characters in excess of the string length will be truncated. The maximum possible length of a string in Atari BASIC is 32,768 characters.
MS BASIC includes functions for accessing bits of strings, for instance, codice_32 returns the leftmost 10 characters of codice_33. These functions create new strings on the heap and return a pointer to the start of the new string. In Atari BASIC the string is represented by an array, and was accessed using array indexing functions, or "slicing". The equivalent statement in Atari BASIC would be codice_34; the arrays are 1-indexed, not 0-indexed, so a string of length 10 starts at 1 (not 0) and ends at 10. Because the slicing syntax was the same as the syntax for selecting a string in a two-dimensional array in other BASICs, there was no way to define or work with arrays of strings.
A major advantage of this style of access is that the slicing functions do not create new strings, they simply set pointers to the start and end points within the existing allocated memory. MS BASIC was known for running out of heap space in such programs and entering lengthy garbage collection delays while former temporary strings were removed from memory. Atari BASIC creates new heap entries only with the explicit DIM commands, so memory management is eliminated. This offers a major advantage in terms of performance and memory use in programs with significant amounts of string processing.
Atari BASIC does not initialize array variables, and a string or numeric array will contain whatever data was present in memory when it was allocated. The following trick allows fast string initialization, and it is also useful for clearing large areas of memory of unwanted garbage. Numeric arrays can only be cleared with a FOR...NEXT loop:
10 REM Initialize A$ with 1000 characters of X
20 DIM A$(1000)
30 A$="X":A$(1000)=A$:A$(2)=A$
String concatenation in Atari BASIC works as in the following example. The target string must be large enough to hold the combined string or an error will result:
10 DIM A$(12),B$(6)
20 A$="Hello ":B$="there!"
30 A$(LEN(A$)+1)=B$
40 PRINT A$
The INPUT statement cannot be used with a prompt nor with array variables. The latter must be filled indirectly via a statement like 20 INPUT A:B(1)=A. Array variables in Atari BASIC also may contain two subscripts.
Strings included in DATA statements do not have to be enclosed in quote marks in Atari BASIC, as a result, it is also not possible for data items to contain a comma. The READ statement also cannot be used with array variables.
Arrays have a base index of 0, so a statement such as DIM A(10) actually creates an 11-element array (elements 0-10).
Input/output.
The Atari OS includes a subsystem for peripheral device input/output (I/O) known as CIO (Central Input/Output). Most programs can be written independently of what device they might use, as they all conform to a common interface; this was rare on home computers at the time. New device drivers could be written fairly easily that would automatically be available to Atari BASIC and any other program using the Atari OS, and existing drivers could be supplanted or augmented by new ones. A replacement , for example could displace the one in ROM to provide an 80-column display, or to piggyback on it to generate a checksum whenever a line is returned (such as used to verify a type-in program listing).
Atari BASIC supports CIO access with reserved words and . There are routines in the OS for graphics fill and draw, but they are not all available as specific BASIC keywords. and for line drawing are supported while a command providing area fill is not. The fill feature can be used through the general CIO entry point, which is called using the BASIC command .
The BASIC statement prepares a device for I/O access:
10 REM Opens the cassette device on channel 1 for reading in BASIC
20 OPEN #1,4,0,"C:MYPROG.DAT"
Here, means "ensure channel 1 is free," call the driver to prepare the device (this will set the cassette tape spools onto tension and advance the heads keeping the cassette tape player "paused". The means "read" (other codes are for write and for "read-and-write"). The third number is auxiliary information, set to 0 when not needed. The is the name of the device and the filename; the filename is ignored for the cassette driver. Physical devices can have numbers (mainly disks, printers and serial devices), so " might be the plotter and " the daisy-wheel printer, or " may be one disk drive and " and so on. If not present, 1 is assumed.
The LPRINT statement is used to output strings to the printer.
Atari BASIC does not include an equivalent of the Microsoft BASIC or commands to detect a keypress, this can be simulated either by ing the keyboard driver directly or opening it as a file (e.g. ) although the latter will wait for a keypress unlike or .
Typing from BASIC will exit to the Atari DOS command menu. Any unsaved programs will be lost. There is no command to display a disk directory from within BASIC and this must be done by exiting out to DOS.
DOS occupies roughly 5k of memory, thus a cassette-based Atari machine (48k or greater) will have around 37,000 bytes of free BASIC program memory and 32,000 bytes if DOS is present. BASIC cannot use the extra RAM on XL and XE machines.
Graphics and sound support.
Atari BASIC has built-in support of sound, (via the statement), setting up the screen graphics (, drawing graphics ), joysticks (), and paddles (). The underlying operating system included a routine to fill arbitrary shapes, but BASIC did not have a command and it instead had to be called with the command.
There is no dedicated command for clearing the screen in Atari BASIC, this is usually done with , which PRINTs the clear screen control code (analogous to in Commodore BASIC). Atari BASIC does not include a TAB function; this can be simulated by either POKEing the cursor column position at $55 or the tab position at $C9, which has a default value of 10. The changed values will not take effect until a PRINT statement is executed. There is also no SPC function in Atari BASIC.
Advanced aspects of the hardware such as player/missile graphics (sprites), redefined character sets, scrolling, and custom graphics modes are not supported by BASIC; these will require machine language routines or PEEK/POKE statements. A few graphics modes cannot be accessed from BASIC on the Atari 400/800 as the OS ROMs do not support them; the only way to access them is in machine language by setting the ANTIC registers and Display List manually. The OS ROMs on the XL/XE added support for these modes.
Bitmap modes in BASIC are normally set to have a text window occupying the last four rows at the bottom of the screen so the user may display prompts and enter data in a program. If a 16 is added to the mode number invoked via the GRAPHICS statement, the entire screen will be in bitmap mode (e.g. GRAPHICS 8+16). If bitmap mode in full screen is invoked, Atari BASIC will automatically switch back into text mode when program execution is terminated unlike many other BASICs which leave the user in bitmap mode and have an unreadable screen that can only be switched out of via typing a blind command or resetting the computer.
Bitmap coordinates are calculated in the range of 0 to maximum row/column minus one, thus in Mode 6 (160x192), the maximum coordinates for a pixel can be 159 and 191. If the user goes over the allowed coordinates for the mode, BASIC will exit out with an error.
Advanced techniques.
Line labels.
Atari BASIC allows numeric variables and expressions to be used to supply line numbers to codice_22 and codice_36 commands. For instance, a subroutine that clears the screen can be written as codice_37, which is easier to understand than codice_38.
Includes.
Most BASICs of the era allow the codice_11 command to send the source code to a printer or other device. Atari BASIC also includes the codice_40 command, which reads source code from a device and merges it back into the program, as if the user had typed it in. This allows programs to be saved out in sections, reading them in using codice_40 to merge or replace existing code. By carefully using blocks of line numbers that do not overlap, programmers can build libraries of subroutines and merge them into new programs as needed.
Embedded machine language.
Atari BASIC can call machine code subroutines. The machine code is generally stored in strings, which can be anywhere in memory so the code needs to be position independent, or in the 256-byte Page 6 area (starting at address 153610, 60016), which is not used by BASIC or the operating system. Code can be loaded into Page 6 by reading it from codice_42 statements.
Machine code is invoked with the codice_43 function. The first parameter is the address of the machine code routine and the following values are parameters. For example, if the machine language code is stored in a string named codice_44 it can be called with parameters as .
Parameters are pushed onto the hardware stack as 16-bit integers in the order specified in the codice_43 function in low byte, high byte order. The last value pushed to the stack is a byte indicating the number of arguments. The machine language code must remove all of these values before returning via the codice_46 instruction. A value can be returned to the BASIC program by placing it in addresses 21210 and 21310 (D416 and D516) as a 16-bit integer.
Performance.
In theory, Atari BASIC should run faster than contemporary BASICs based on the MS pattern. Because the source code is fully tokenized when it is entered, the entire tokenization and parsing steps are already complete. Even complex mathematical operations are ready-to-run, with any numerical constants already converted to the internal 40-bit format, and variables values are looked up by address rather than having to be searched for. In spite of these theoretical advantages, in practice, Atari BASIC is slower than other home computer BASICs, often by a large amount.
On two widely used benchmarks of the era, "Byte" magazine's Sieve of Eratosthenes and the Creative Computing benchmark test written by David H. Ahl, the Atari finished near the end of the list in terms of performance, and was much slower than the contemporary Apple II or Commodore PET, in spite of having the same CPU but running it at roughly twice the speed of either. It finished behind relatively slow machines like the Sinclair ZX81 and even some programmable calculators.
Most of the language's slowness stemmed from three problems.
The first is that the floating-point math routines were poorly optimized. In the Ahl benchmark, a single exponent operation, which internally loops over the slow multiplication function, was responsible for much of the machine's poor showing.
In addition to performing most mathematical operations slowly, the conversion between the internal floating-point format and the 16-bit integers used in certain parts of the language were relatively slow. Internally, these integers were used for line numbers and array indexing, along with a few other tasks, but numbers in the tokenized program were always stored in binary-coded decimal (BCD) format. Whenever one of these is encountered, for instance, in the line number in , the tokenized BCD value has to be converted to an integer, an operation that can take as long as 3500 microseconds. Other BASICs avoided this delay by special-casing the conversion of numbers that could only possibly be integers, like the line number following a , switching to special ASCII-to-integer code to improve performance.
Another problem is due to how Atari BASIC implemented branches. To perform a branch in a codice_22 or codice_36, the interpreter searches through the entire program for the matching line number it needs. One minor improvement found in most Microsoft-derived BASICs is to compare the target line number to the current line number, and search forward from that point if it is greater, or start from the top if less. This improvement was missing in Atari BASIC.
The most serious problem was the implementation of codice_49...codice_50 loops. Almost all BASICs, including MS-derived versions, would push a pointer to the location of the codice_49 on a stack, so when it reached the codice_50 it could easily return to the codice_49 again in a single branch operation. Atari BASIC pushed the line number instead. This meant every time a codice_50 was encountered, the system had to search through the entire program to find the corresponding codice_49 line. As a result, any loops in an Atari BASIC program cause a large loss of performance relative to other BASICs.
Several BASICs for the Atari addressed some or all of these issues, resulting in large performance gains. BASIC XL reduced the time for the "Byte" benchmark from 194 to 58 seconds, over three times as fast. This was accomplished by caching the location of codice_56 loops, as in other BASICs, and also used this same cache to perform codice_22 and codice_36 line lookups for further improvements. Turbo-Basic XL included a different solution to the line-lookup issue, as well as a re-written, high-performance, floating-point library. On the Ahl benchmark, Atari BASIC required 405 seconds, while exactly the same code in Turbo BASIC took 41.6 seconds, an order of magnitude improvement.