An Atari BASIC program ready to run
|Original author(s)||Paul Laughton
Revision C / 1983
|Platform||Atari 8-bit family|
|License||Copyright © 1979 Atari Inc. Proprietary|
Atari BASIC is a BASIC interpreter that shipped with the Atari 8-bit family of 6502-based home computers. The language was originally on an 8 KB ROM cartridge. On the XL/XE computers it is built-in and can be disabled by holding down the OPTION key while booting. The XEGS disables BASIC if powered without the keyboard attached.
- 1 History
- 2 Description
- 3 Differences from Microsoft BASIC
- 4 Advanced techniques
- 5 Keywords
- 6 See also
- 7 Notes
- 8 References
- 9 External links
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 Atari needed the BASIC programming language, then the standard language for home computers.
Atari purchased the source code to the MOS 6502 version of Microsoft 8K BASIC. The original 8K BASIC referred to its memory footprint when compiled on the Intel 8080's instruction set. The lower code density of the 6502 expanded the code to about 9 kB. This was slightly larger than the natural 8 kB size of the Atari's ROM cartridges.
Atari felt that they also needed to expand the language to add better support for the specific hardware features of their computers, similar to what Apple had done with their Applesoft BASIC. This increased the size from 9 kB to around 11 kB. Atari had designed their ROM layout in 8 kB blocks, and paring down the code from 11 to 8 kB turned out to be a significant problem. Adding to the problem was the fact that the 6502 code supplied by Microsoft was undocumented.
Six months later they were almost ready with a shippable version of the interpreter. However, Atari was facing a deadline with the Consumer Electronics Show (CES) approaching, and decided to ask for help to get a version of BASIC ready in time for the show.
In September 1978 Atari asked Shepardson Microsystems to bid on completing BASIC. Shepardson had written a number of programs for the Apple II family, which used the same 6502 processor, and were in the middle of finishing a new BASIC for the Cromemco S-100 bus machines (Cromemco 32K Structured BASIC). Shepardson examined the existing work and decided it was too difficult to continue paring it down; instead they recommended developing a completely new version that would be easier to fit into 8K. 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 result was a different version of BASIC, known as Atari BASIC. In particular, the new BASIC dealt with character strings more like Data General's BASIC than Microsoft's (which used strings similar to those from DEC BASIC).[N 1]
The contract specified a 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 it with them to the CES.
Shepardson's programmers found problems during the first review and managed to fix some of them, but Atari had already committed the cartridge to manufacturing. It became known (as a retronym) Revision A.
- Revision A – First Atari BASIC cartridge. 8K ROM. This version contains a bug in a subroutine that copies memory. Under certain conditions, deleting lines of code causes a lockup.
- Revision B – Fixes all of the major software bugs in Revision A. While fixing the memory copying bug the same problem was re-introduced in a more common routine, thereby dramatically increasing the number of crashes. Found built-in on the 600XL and early 800XLs. Never supplied on cartridges.
- Revision C – Eliminates memory leak in Revision B. Found on later 800XLs, the XEGS, and all XE computers. Limited cartridge production run.
The version can be determined by typing
PRINT PEEK(43234) at the READY prompt. The result is
162 for Revision A,
96 for Revision B, and
234 for Revision C.
Atari BASIC uses a line editor and immediately checks the line for syntax errors as soon as the ↵ Enter key is pressed. If a problem is found it re-displays the line, highlighting the text near the error in inverse video. This can make catching syntax errors on the Atari much easier than on other editors; most BASICs will not display the errors until the program is executed.
Program lines can be entered by starting with a line number, which will insert a new line or amend an existing one. Lines without a line number are executed immediately. When the programmer types
RUN the program executes from the lowest line number. Atari BASIC allows all commands to be executed in both modes. For instance, the
LIST command can be used inside a program.
Program lines ("logical lines") can be up to three screen lines ("physical lines") of 40 characters, so 120 characters total. The cursor can be moved freely in these lines, unlike in other BASICs where to get "up" a line one has to continuously scroll leftwards until the cursor is wrapped at the left margin (and similarly to go down when wrapping at the right margin) – though that works too, except the cursor when wrapping left to right or right to left does not move up or down a line. The OS handles tracking whether a physical line flowed to the next on the same logical line.
The cursor can always be moved freely around the screen, and it will wrap on all sides. Hitting ↵ Enter sends the tokenizer the (logical) line on which the cursor sits. So, in the example pictured above (with
PRUNT), all the author needs to do to fix the error is move the cursor over the
U, type I (the editor only has an overwrite mode) and hit ↵ Enter. This is a common editing technique for, say, renumbering lines. Atari BASIC has no built-in renumbering command, but one can quickly learn to overwrite the numbers on a set of lines then just hit ↵ Enter repeatedly to put them back into the program.
Atari BASIC uses a token structure to handle lexical processing for better performance and reduced memory size. The tokenizer converts lines using a small buffer in memory, and the program is stored as a parse tree.[N 2] The token output buffer (addressed by a pointer at LOMEM – 80, 8116) is 256 bytes, and any tokenized statement larger than the buffer generates an error (14 – line too long). Indeed, the syntax checking described in the "Program editing" section is a side effect of converting each line into a tokenized form before it is stored.
The output from the tokenizer is then moved into more permanent storage in various locations in memory. A set of pointers (addresses) indicates these locations: variables are stored in the variable name table (pointed to at VNTP – 82, 8316) and the 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 two bytes to address its entry into the appropriate table. Strings 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 (
FOR...NEXT) and subroutines (
GOSUB...RETURN). Finally, the end of BASIC memory usage is indicated by an address stored at MEMTOP – 90, 9116) pointer.
Atari BASIC uses a unique way to recognize abbreviated reserved words. In Microsoft BASIC, there are a few predefined short forms like
REM (6502 implementations do not support abbreviating REM with
'). Atari BASIC allows any keyword to be abbreviated using a period at any point in writing it. So
L. is expanded to
LIST, as is
LI.. 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
REM at the beginning (it can be typed as
.). When the program is later
LISTed it will always write out the full words with three exceptions:
GOTO has a synonym,
GO TO; and
LET 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.
In the keywords for communicating with peripherals (see the Input/Output section, below) such as
OPEN # and
PRINT #, the "
#" is actually part of the tokenized keyword and not a separate symbol. For example, "
PRINT #0" are the same thing,[N 3] just presented differently.
Atari BASIC differs considerably from Microsoft-style BASICs in the way it handles strings. In the Microsoft model, strings are variable length without a specified maximum size and string arrays are supported. Atari BASIC strings are arrays of characters like Fortran or C and there are no string arrays. A string is allocated a maximum size using the
DIM statement; its actual length can vary at runtime from 0 to this maximum size. There is a syntax for slicing up strings, where
A$ refers to the entire string and
A$(4,6) slices out the three characters at locations 4, 5 and 6. These replace the
RIGHT$ commands in Microsoft BASIC.
Strings are not initialized with a default value like "empty" and care has to be taken not to interpret random data in RAM as part of a string. The following trick allows fast string initialization:
REM Initialize A$ with 1000 characters of x DIM A$(1000) A$="x":A$(1000)=A$:A$(2)=A$
The Atari OS includes a subsystem for peripheral device input/output (I/O) known as CIO (Central Input/Output). All I/O went through a central point of entry (E45C16) passing the address of an I/O Control Block (IOCB), a 16-byte structure that defines which device was meant, and what kind of operation (read, write, seek etc.). There are 8 such IOCBs, allocated at fixed locations in page 3 of memory from
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. Existing drivers could be supplanted or augmented by new ones since the driver table was searched newest-to-oldest, so a replacement
E:, for example could displace the one in ROM to provide an 80-column display, or to piggy-back on it to generate a checksum whenever a line is returned (such as used to verify a type-in program listing).
CIO access in BASIC
Atari BASIC supports CIO access with reserved words
OPEN #, CLOSE #, PRINT #, INPUT #, GET #, PUT #, NOTE #, POINT # and
XIO #. There are routines in the OS for graphics fill and draw, but they are not all available as specific BASIC keywords.
DRAWTO 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
Up to eight IOCBs can be in use at a time, numbered 0 through 7 (0 is, by default, the editor
E:). The BASIC statement
OPEN # prepares a device for I/O access:
REM Opens the cassette device on channel 1 for reading in BASIC OPEN #1,4,0,"C:MYPROG.DAT"
OPEN # means "ensure channel 1 is free" (an error otherwise results), call the
C: 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
4 means "for read" (other codes were
8 for write,
12 = 8 + 4 for "read-and-write", and so forth), and the third number provides extra auxiliary information, here not used and set by convention to 0. The
C:MYPROG.DAT is the name of the device and the filename, as it happens, files on cassette were not named by this device[clarification needed]. The string gives the device name and optionally a filename. Physical devices can have numbers (mainly disks, printers and serial devices), so "P1:" might be the plotter and "P2:" the daisy-wheel printer, or "D1:" may be one disk drive and "D2:" another, "R1:" may be a modem and "R2:" an oscilloscope (R for RS-232, provided by an add-on interface and not built into the OS), and so on; if not present, 1 is assumed.
I/O routines return error codes of 128-255 (8016-FF16) via the processor's Y register and setting the carry flag of the processor. There are no user-friendly messages for standard error codes in the OS itself.
Atari BASIC (and other languages) have the freedom to return error codes less than 128, and these mean different things in different languages. There is nothing to stop a perverse implementer using error codes of 128 or above, but no incentive to do so.
Graphics and sound support
Atari BASIC has good built-in support of sound, (via the
SOUND statement), graphics (
GRAPHICS, SETCOLOR, COLOR, PLOT and
DRAWTO), joysticks (
STICK, STRIG), and paddles (
PADDLE, PTRIG). There isn't a supplied
FILL command to fill an arbitrary shape with pixels, but a limited operating system function exists and can be called with the
Advanced aspects of the hardware such as player/missile graphics (sprites), redefined character sets, scrolling, and custom graphics modes are not supported at the language level. Some of the graphics modes of the underlying hardware are also not directly supported in BASIC, notably what came to be known as "GRAPHICS 7.5", as it offered resolution halfway between GRAPHICS 7 and GRAPHICS 8. Support was added to the XL/XE operating system, accessible from BASIC as GRAPHICS 15.
Running on original hardware, Atari BASIC is slower than other BASICs on contemporaneous equipment for the same home market, sometimes by a surprising amount, especially when one takes into account the fact that the Atari's CPU is clocked almost twice as fast as that of most other 6502-based computers of that era. Most of these problems stem from two issues.
One is a side effect of how Atari BASIC searches for line numbers as the program is run. A
GOTO has to search through the program for the line number it needs. The same lookup code is also used to implement
NEXT in a
NEXT loop, so it dramatically lowers performance of these common loops.
The other is that all numbers are stored as floating point. Even the destination of a
POKE, which has to fit in a two byte integer, is stored as a 6-byte floating point value and converted to an integer on the fly. The Atari's key features rely on hardware that deals purely in integers (bytes or two-byte words), but all have to be floating point values in BASIC. Atari BASIC uses the OS's built-in floating point routines which are relatively slow compared to other representations, even on the same hardware. Most of the slowness lies in a particularly poor implementation of the multiply subroutine used throughout the OS math libraries.
Several commercial and shareware BASICs addressed some or all of these issues, resulting in performance that was 3 to 5 times faster than the Atari version. Using these BASICs, the Atari was one of the fastest home computers of its era.
Differences from Microsoft BASIC
- Atari BASIC uses a different string model and does not allow arrays of strings. String concatenation is not supported.
DEF FNis not supported.
INPUTcannot include a prompt.
- There is no support for integer variables.
- All string variables and arrays must be dimensioned prior to use while Microsoft BASIC does not require this if there are ten or fewer elements.
- Variable names can be of arbitrary length.
TABfunction is not supported. To get around this limitation, the user must
POKEcertain memory locations utilized by BASIC's screen editor.
?as in Microsoft BASIC, but Atari BASIC does not tokenize it into
LIST-ing a program will still show the question mark.
Because Atari BASIC can read in lines of code from any device, not just the editor, it is possible to save blocks of code and then read them in and merge them into a single program just as if they had been typed into the editor. Of course this means the lines being read in must have line numbers that are not used in the main program. The code to be merged is written to a device as text using the
LIST command, and can be put back into the program with the
ENTER command. So the stream of text on the device is, from the BASIC interpreter's point of view, no different from that had it been typed into the editor.
By carefully using blocks of line numbers that do not overlap, programmers can build libraries of subroutines (simulating functions as above) and merge them into new programs as needed.
Embedded machine language
Atari BASIC does not have a built-in assembler, but it 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
Machine code is invoked with the
USR 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
ROUTINE$ it can be called with parameters as
Parameters are pushed onto the hardware stack as 16-bit integers in the order specified in the
USR 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 vaues before returning via the
RTS 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.
|ABS||Returns the absolute value of a number|
|ADR||Returns the address in memory of a variable (mostly used for machine code routines stored in variables)|
|ASC||Returns the ATASCII value of a character|
|ATN||Returns the arctangent of a number|
|BYE||Transfers control to the internal "Self Test" program ("Memo Pad" on early models)|
|CHR$||Returns a character given an ATASCII value|
|CLOAD||Loads from cassette tape a tokenized program that was saved with CSAVE|
|CLOG||Returns the common logarithm of a number|
|CLOSE||Terminates pending transfers (flush) and closes an I/O channel|
|CLR||Clears variables' memory and program stack|
|COLOR||Chooses which logical color to draw in|
|COM||Implementation of MS Basic's COMMON was cancelled. Recognized but the code for DIM is executed instead|
|CONT||Resumes execution of a program after a STOP at the next line number (see STOP)|
|COS||Returns the cosine of a number|
|CSAVE||Saves to cassette tape a program in tokenized form with fast method (short inter-record gap on tape) (see CLOAD)|
|DATA||Stores data in lists of numeric or string values|
|DEG||Switches trigonometric functions to compute in degrees (radians is the default mode) (see RAD)|
|DIM||Defines the size of a string or array (see COM)|
|DOS||Transfers control to the Disk Operating System (DOS); if DOS was not loaded, same as BYE|
|DRAWTO||Draws a line to given coordinates|
|END||Finishes execution of the program, closes open I/O channels and stops any sound|
|ENTER||Loads and merges into memory a plain text program from an external device, usually from cassette tape or disk (see LIST)|
|FOR||Starts a for loop|
|FRE||Returns the amount of free memory in bytes|
|GET||Reads one byte from an I/O channel (see PUT)|
|GOSUB||Jumps to a subroutine at a given line in the program, placing the return address on the stack (see POP and RETURN)|
|GOTO and GO TO||Jumps to a given line in the program. GOTO can be omitted in "IF ... THEN GOTO ..."|
|GRAPHICS||Sets the graphics mode|
|IF||Executes code depending on whether a condition is true or not|
|INPUT||Retrieves a stream of text from an I/O channel; usually data from keyboard (default), cassette tape or disk|
|INT||Returns the floor of a number|
|LEN||Returns the length of a string|
|LET||Assigns a value to a variable. LET can be omitted|
|LIST||Lists (all or part of) the program to screen (default), printer, disk, cassette tape, or any other external device (see ENTER)|
|LOAD||Loads a tokenized program from an external device; usually a cassette tape or disk (see SAVE)|
|LOCATE||Stores the logical color or ATASCII character at given coordinates|
|LOG||Returns the natural logarithm of a number|
|LPRINT||Prints text to a printer device (same result can be achieved with OPEN, PRINT and CLOSE statements)|
|NEW||Erases the program and all the variables from memory; automatically executed before a LOAD or CLOAD|
|NEXT||Continues the next iteration of a FOR loop|
|NOTE||Returns the current position on an I/O channel|
|ON||A computed goto - performs a jump based on the value of an expression|
|OPEN||Initialises an I/O channel|
|PADDLE||Returns the position of a paddle controller|
|PEEK||Returns the value at an address in memory|
|PLOT||Draws a point at given coordinates|
|POINT||Sets the current position on an I/O channel|
|POKE||Sets a value at an address in memory|
|POP||Removes a subroutine return address from the stack (see GOSUB and RETURN)|
|POSITION||Sets the position of the graphics cursor|
|PRINT and ?||Writes text to an I/O channel; usually to screen (default), printer, cassette tape or disk (see LPRINT and INPUT)|
|PTRIG||Indicates whether a paddle trigger is pressed or not|
|PUT||Writes one byte to an I/O channel (see GET)|
|RAD||Switches trigonometric functions to compute in radians (see DEG)|
|READ||Reads data from a DATA statement|
|REM||Marks a comment in a program|
|RESTORE||Sets the position of where to read data from a DATA statement|
|RETURN||Ends a subroutine, effectively branching to the line immediately following the "calling" GOSUB (see GOSUB and POP)|
|RND||Returns a pseudorandom number|
|RUN||Starts execution of a program, optionally loading it from an external device (see LOAD)|
|SAVE||Writes a tokenized program to an external device; usually a cassette tape or disk (see LOAD)|
|SETCOLOR||Maps a logical color to a physical color|
|SGN||Returns the signum of a number|
|SIN||Returns the sine of a number|
|SOUND||Starts or stops playing a tone on a sound channel (see END)|
|SQR||Returns the square root of a number|
|STATUS||Returns the status of an I/O channel|
|STEP||Indicates the increment used in a FOR loop|
|STICK||Returns a joystick position|
|STOP||Stops the program, allowing later resumption (see CONT)|
|STRIG||Indicates whether a joystick trigger is pressed or not|
|STR$||Converts a number to string form|
|THEN||Indicates the statements to execute if the condition is true in an IF statement|
|TO||Indicates the limiting condition in a FOR statement|
|TRAP||Sets to jump to a given program line if an error occurs (TRAP 40000 cancels this order)|
|USR||Calls a machine code routine, optionally with parameters|
|VAL||Returns the numeric value of a string|
|XIO||General-purpose I/O routine (from "Fill screen" to "Rename file" to "Format disk" instructions)|
- BASIC A+/BASIC XL/BASIC XE – Extended BASICs for the Atari, from Optimized Systems Software (OSS)
- Turbo-Basic XL - Freeware BASIC compatible with Atari BASIC, also available with a compiler for greater speed and extra commands.
- The main differences were whether strings were allowed to grow and shrink in size once memory had been allocated for them, and whether the size of the string was constant from the outset (e.g. being padded with some special character meaning "end of string") or whether the size was stored independently. Both approaches have advantages and disadvantages, depending on how one is expecting them to be used.
- Although Wilkinson implements the parse tree as a set of tables which is really an implementation detail.
- Although 0 is actually explicitly disallowed here by BASIC assuming it to be a coding error, isn't it?