# Atari BASIC programming

This article by Dan Polansky intends to give an impression of Atari BASIC for 8-bit Atari computers, including exercises and examples. One may try out the examples in an Atari emulator such as Atari800 or Altirra (with BASIC ROM) and appreciate the speed of modern computers. One can have more fun by trying the exercises without reading the solutions. One purpose is to get an idea of what kinds of simple programs kids in the 1980ies could have been writing. Another purpose is to give an idea to beginners how programming close to the hardware looks like, e.g. by manipulating the video memory.

## Identification and hardware

Atari BASIC refers here to versions originally distributed in ROM cartridges and later in ROM chips. The target for testing most content on this page is the 'final "C" version in late-model XLs and the XE series.' The version can be determined by PRINT PEEK (43234).
The target hardware may include the 1979 Atari 400 and 800 computers (8 KB or 16 KB) and 1983 600 XL (16 KB) but is above all the 1983 Atari 800 XL (64 KB). There is not much difference between Atari 800 XL and the later XE machines. Working hypothesis: some graphical modes are only available on the XL and XE machines.

## Exercise statements

1) Print numbers from 1 to 10.
2) Calculate the factorial of n input by the user and print it.
3) Plot a large cross made from two diagonal lines.
4) Plot an ellipse using sine and cosine and plotting the individual pixels.
5) Cover the screen in random-color pixels in various graphical modes.
6) Plot the Mandelbrot set in a 2-color, 4-color or 16-color mode.
7) Other exercises can be inferred from section headings.

## Elements
### Getting started

Programs have to be entered with line numbers. One can see the program listing by typing "LIST" or "L.". One can list a particular line number e.g. by typing "LIST 30". Once one is ready to run the program, one enters "RUN". To enter a line, one types e.g. "10 PRINT 3*7". To erase a line, one types just its line number, e.g. "10".
One can also enter BASIC commands interactively, e.g. by typing "PRINT 3*7".
To erase the current program, one can type NEW.
The Atari emulator may have a "Paste" option in its context menu that types text content of the clipboard.
A paused program can be resumed using CONT.
Variable names cannot not start with a builtin keyword name. Thus, entering LETTER=1 results in TER being set to 1 since the statement is interpreted a LET TER=1. Similarly, FORMULA=1 results in error: it is interpreted as FOR MULA=1. Underscores are disallowed in variable names.

### Conditionals

There is IF conditional. There is no block grouping (BEGIN, END or {, }); at least, multiple statements can be made on the same line of the IF conditional. To achieve arbitrarily large blocks guarded by IF, one uses GOTO.
There is no ELSE. To achieve if-else, one can either use GOTO or one can repeat the condition, e.g. like this:
There is no SWITCH command.

### Loops

A for loop:
An early exit from the loop ("break"):
There is no while loop. One can use GOTO instead:
One can also emulate a do-while (repeat until) loop using GOTO:

### Arrays

One can dimension and use a one-dimensional array of Atari BASIC numbers.
One can also have two-dimensional arrays, but not three-dimensional ones:
One can emulate any-dimensional array by using a one-dimensional array and calculating the physical index from the logical indices.
String arrays are impossible except as being emulated by using a single string and interpreting it as sequence of blocks.
Strings, like arrays, need to be dimensioned, and can be thought of as 8-bit character arrays. They can be actually used as arrays of bytes/octets: to store a byte, one would use CHR$, and to retrieve a byte, one would use ASC. The benefit is that this way, one item take one bytes, whereas an Atari BASIC number takes 6 bytes.

### Strings

Strings have to be dimensioned for size before use. Their use is as follows:
Strings can be used as byte arrays, via ASC and CHR$. The syntax is cumbersome but if one uses a numerical array to store byte values, one value occupies 6 bytes. Setting the byte value to 65 and then incrementing it:
We can also use strings as byte arrays via ADR, PEEK and POKE:
ADR and other techniques from above are also used to store machine code in a string, as per Calling assembly section.
While variables (including strings) keep their values when the program ends, the variable locations in memory are not guaranteed to be remain intact. Thus, storing a string address into a variable SA during a run of a program and then trying to access the string via that variable after the program ended may lead to a bad surprise.

### Floating point numbers

The numbers Atari BASIC works with are floating point numbers; one cannot declare a variable to be an integer. They are 6-byte floating point numbers. There is 1 bit for sign, 7 bits for exponent and 5 bytes for binary-coded decimal (BCD) mantissa, representing 10 decimal digits. The 7-bit exponent indicates not the power of 10 but rather the power of 100. A quick and inconclusive experiment suggests that the largest number is 9.99999999E97, the smallest number is -9.99999999E97 and the smallest positive number is 1.0E-98.
The floating point output and entry make it seem that there are only 9 decimal digits rather than 10. Thus, when one enters "A=0.3333333333" (10 threes), one only really enters 9 threes. However, by entering "A=1/3", one makes use of all the 10 decimal digits. One can enter all 10 decimal digits (arbitrary ones) by a trick: "A=0.333333333+3E-10", and one can then verify success by another trick: "PRINT A-0.3". Further calculation often does make use of all the 10 digits, as verifiable e.g. thus: "A=1/3:A=A*2:PRINT A-0.6". Full 10 digits seem often used by trigonometric functions, e.g. "A=SIN(0.5):PRINT A-0.4". Similarly, full 10 digits are used here: "PRINT ATN(1)-0.7". However, the use of only 9 digits is revealed in "PRINT ATN(10)-1", possibly because ATN(10) is 1.47... and the uppermost two digits in the internal representation are "01". Here, the uppermost digit cannot be "1" since the exponent as internally represented cannot represent powers of 10, only powers of 100. Thus, given a floating point number with non-zero leading digit in the internal representation and thus with full 10 decimal digits, one loses 1 digit of precision by either multiplying it or dividing it by 10. Thus, 1/3 is 10-precise, 10/3 is 9-precise, 100/3 is 10-precise, 1000/3 is 9-precise, etc, which one can verify thus: "? 1/3-0.3:? 10/3-3:? 100/3-30:? 1000/3-300:? 10000/3-3000:? 100000/3-30000".
The pseudo random number generator often achieves the full 10-digit precision, as per this: "FOR I=1 TO 10:A=RND(0):PRINT A:PRINT A-INT(A*10)/10:NEXT I".
Other tests:
The following tests suggests that 1000000000 (1E9) is the largest integer contiguous with other integers with units precisely represented (and thus, 29-bit unsigned integers are fully covered):
However, the above terminates after 10 iterations while outputting 10000000000. This puzzle is due to the fact that the output pretends the precision is never more than 9 decimal digits.
One more test:
The above outputs K with the same precision as I, but the J output has the final digit replaced with zero, suggesting that the representation during calculation is different from the one for output, and also possibly different from the representation used for tokenization (for storage as a literal in the program).
The internal representation of floating point numbers can be investigated using the following:
We may explore the internal representation in hexadecimal, using more code:
The output confirms the ideas we have about the internal representation:
Link:
* Inside Atari BASIC by Larry Isaacs, atariarchives.org
* Why did 8-bit Basic use 40-bit floating point?, retrocomputing.stackexchange.com -- the title of the question is misleading since Atari BASIC uses 48-bit floating point
* De Re Atari, archive.org -- section Floating Point Package 8-45 (its claim that the end of dynamic range is 1E98 appears incorrect)

### Mathematics

Mathematical operators include +, -, *, / and ^.
Mathematical functions include abs, sin, con, atn, etc.
The exponentiation operator ^ has a limitation or glitch: it is imprecise even for relatively low integral operands. Thus, e.g. 10^9 yields 999999999, one off. One can get a precise value via multiplication in a loop.

### Graphics modes

In the plotting examples below, we refer to graphics modes via GRAPHICS command. A table of those is in the link. For a start, we need to know:
* mode 0 is the default, hi-res text/character mode
* mode 7 is a low-res four-color pixel bitmap mode with split text window; this is the resolution of Draconus although Draconus (from what I recall) uses character mode rather than pixel bitmap mode
* mode 8 is a hi-res two-color pixel bitmap mode with split text window
* mode 8+16 is a variant of mode 8 without a split window, 320x192 pixels
Links:
* Atari Graphics and Arcade Game Design-Chapter 1, atariarchives.org

### Player-missile graphics

Player-missing graphics (called sprites on some computers) can be controlled only via low-level access, peeks and pokes. One can use string tricks, by which one cleverly makes sure a string to contain the PMG buffer is aligned at a boundary, which is required of the buffer. Moreover, copying strings is rather fast, unlike copying bytes one by one in a for loop; and copying blocks of bytes is required to change the vertical position of the PMG objects.
Links:
* Atari Player-Missile Graphics in BASIC by Philip C. Seyer, 1984, atariarchives.org
* Player-Missile Graphics in The Creative Atari, 1983, atariarchives.org

### Clearing the screen

There is no CLS. One can PRINT CHR$(125) instead.

### Abbreviations

Abbreviations include:
* "?" for PRINT.
* L. for LIST
* GR. for GRAPHICS
* SE. for SETCOLOR
* Etc.
Links:
* Atari BASIC Quick Reference Guide, atariwiki.org

### Memory map

Memory map is useful to know the effects of various PEEKs and POKEs. See the link.
Links:
* Mapping The Atari-Memory Map, atariarchives.org

### Measuring time

Measuring time is useful e.g. for benchmarking.
The link below suggests the following (however, one of the responses warns against race conditions):
Above, the division by 60 is for NTSC; it should be 50 for PAL.
Links:
* benchmarking - How can you measure time using BASIC on Atari XL computers?, retrocomputing.stackexchange.com

### Manipulating video memory

There is value in being able to directly access video memory (video buffer) using POKE and PEEK. For a character mode, one can not only write (with no cursor effect) but also read the character at a given position on screen. For a graphical mode, one can read the pixel rather than only changing it via PLOT.
Two methods of determining the beginning of the video memory can be found in non-authoritative sources:
One cannot generally know a fixed address for a given graphical mode, in part since different Atari machines have different available memory, and for other reasons.
The following inconclusive test suggests the two methods are equivalent under some circumstances:
Links:
* atari 800 - where does address start for graphics 8 mode, forums.atariage.com

### Calling assembly

One can call assembly/machine code using USR.
Two places where to store the machine code:
* Page 6: starts at 1536, at most 256 bytes (a page)
* String, declared via DIM, with more than 256 bytes possible
Two manners of machine code entry:
* DATA statement
* String literal
Interaction/interfacing between the calling BASIC code and the machine code:
* Stack contains the number of arguments and then two bytes per argument containing the integer part of the argument value. It is to be found out how and whether a string argument can be passed.
* The USR return value is at addresses 212 and 213.
To obtain the machine code from assembly, one can use e.g. the online assembler at masswerk.at. However, it outputs hexadecimal codes, whereas the literals used with DATA command are decimal.
As an example, we implement 8-bit bitwise OR, to fill the gap in the BASIC command/function set:
A related page: Learning 6502 assembly.
Link:
* Page 6 - Issue 11 - What is USR?, page6.org
* Call Assembly Language Code from Your BASIC Program (10-15 mins), atariprojects.org -- has decimal values for 6502 assembly instructions
* virtual 6502 Assembler, masswerk.at
* 6502 Instruction Set, masswerk.at
* Easy 6502 by skilldrick, skilldrick.github.io
* Part 3 of 11 -- Simple Assembly for Atari BASIC - kenjennings' Blog, forums.atariage.com

### Limitations

Limitations of Atari BASIC include the following:
* Very limited procedural programming (with GOSUB and RETURN): no named procedures, no argument declaration and no automatic storage of local variables on the stack.
* No separation of integer arithmetic from floating-point arithmetic. The consequence is a major slowdown of what could otherwise be integer operations.
* No while loop. One can use GOTO instead.
* No blocks (like BEGIN and END). One can use GOTO instead.
* No bitwise operators.
* No taking of address of a variable except for a string variable.
* No hexadecimal literals.
* No commands for player-missile graphics.
* Almost no plotting commands: only PLOT and DRAWTO.
* No CLS (very minor limitation if one at all).

## Exercises
### Plotting diagonal lines

Depends on PLOT for plotting individual pixels. To switch back to the default graphics mode for comfortable source code listing, one may type "GRAPHICS 0".
Doing the same by drawing lines using DRAWTO:

### Plotting an ellipse

The above is very slow.

### Random numbers

The function RND outputs floating-point numbers in the range of 0 to 1, excluding 1. We can output random integers using INT function (here we output random integers from 0 to 9 including):
We can obtain random integers from 0 to 255 from address 53770. The following plots the counts of generated values, allowing a partial verification of the generator by means of visual inspection.
We can make the above frequency investigation for the Mandelbrot map-based pseudorandom generator (for more of which see section Plotting random pixels):
Links:
* The Beginner's Page: The Random Function, atarimagazines.com

### Plotting random pixels

Plotting random pixels in 2-color high-resolution mode (with text window):
Plotting random pixels in 4-color low-resolution (2x2-sized pixel) mode (with text window):
Plotting random pixels in 16-color low-resolution mode (4x1-sized pixel):
Plotting random pixels by low-level access to video memory (not particularly fast either):
Plotting random pixel by low-level access, using not the RND but rather custom low-quality pseudorandom generator based on the Mandelbrot map f(x) = x**2 - 2. The result looks sort of random, but with suspectly long streaks/blocks of pixels, which probably correspond to the attractive power of x = 2 for the f(x). We rescale [-2, 2] to [0, 1] to make this work; rescaled, the attracting point x=1 shows it power, e.g. in the following sequence:
* 0.999753695
* 0.999015022
* 0.99606397
* 0.984317847
The properties of the Mandelbrot map are investigated in the article Mandelbrot set along the real axis and the orbits.

### Outputting Atari ASCII characters

Outputting Atari ASCII sequence except for special CHR$ codes, which are replaced with underscore ("_"):
Outputting them one character per line, with the ASCII code:
Links:
* ATASCII, wikipedia.org
* ATASCII to Unicode Mapping, kreativekorp.com
* The Beginners Page: Printing And Asking, atarimagazines.com

### Plotting Mandelbrot set

An adaptation of AWK code from rosettacode.org for the 2-color mode 8:
A modification of the above for the 4-color mode 7:
A modification of the above for the 16-color mode 9:

### Calculating modulo

Atari BASIC has no built-in modulo operator. Thus:

### Random walk

Plot a random walk: let x start at 0 and in each successive step, let x either stay where it is, have 1 added to it or have 1 subtracted from it, where one of the 3 options is chosen at random.
A different kind of random walk in which the system starts at the middle of the screen, and then both of its X and Y coordinates are repeatedly subject to addition of one of -1, 0, or 1, chosen at random:

### Plotting Mandelbrot map orbit diagram

Let Mandelbrot map refer to x**2 + c. Plotting its orbit diagram/bifurcation diagram:
A variant of the above with initial iterations not plotted:

### Color registers

Color registers are affected via SETCOLOR. The SETCOLOR parameters are color register number, hue and luminance.
Color register test for two-color text mode 0:
Color register test for two-color pixel bitmap mode 8:
Color register test for four-color pixel bitmap mode 7:
Color register test for four-color pixel bitmap mode 7 with low-level access (POKEs and bit patters):
The above confirms the correspondence of the color index used by COLOR to bit patterns 0b00, 0b01, 0b10 and 0b11 in the video memory.
Links:
* Compute!'s Second Book of Atari Graphics, atariarchives.org

### Low-level access plotting

Assignment: plot diagonal lines using low-level bit-manipulation programming for plotting pixels in color 1 in mode 8, avoiding PLOT and DRAWTO commands. Put differently, imagine an enemy has destroyed the PLOT and DRAWTO commands but you have to plot pixels anyway. Thus, determine the video memory base and manipulate the video memory directly. While the result is going to be very slow, one can practice the low-level bit-by-bit reasoning and manipulation.
Above, we use a custom routine for byte-OR to compensate for its lack in Atari BASIC; an alternative would be to use an assembly routine embedded in BASIC.
Exercise: Modify the above code to make it mode efficient. Ideas: 1) determining the videobase each time we want to draw a pixel is an avoidable overhead; 2) having a separate OR routine to jump to creates an overhead, given jumps are slow in Atari BASIC (inlining helps a little); 3) having a bit-setting routine rather than a general OR routine could turn to be faster.
Exercise: Create a routine that takes color 0 or 1 as an input, and plots the pixel in that color for mode 8. Thus, the routine should be able not only to set a pixel but also to erase it. Demonstrate the routine by drawing lines in the colors.
A solution:

### Bitwise OR

Byte-sized bitwise OR:
Modifying the above code to obtain AND or XOR or to obtain larger value size (e.g. 16-bit) is a very easy exercise. The Stack Overflow link below contains C code for bitwise OR, AND and XOR.
Links:
* discrete mathematics - Is it possible to implement bitwise operators using integer arithmetic?, stackoverflow.com

### Plotting joystick-controlled vehicle

The following plots a joystick-controlled vehicle. The vehicle is a single pixel that has a direction (not just orthogonal) and leaves behind a trace.

### Plotting a spiral

Plotting a spiral with a linearly growing radius:
Plotting a spiral with an exponentially growing radius:

### Monte Carlo area calculation

We can calculate unit circle area using Monte Carlo integration, which will give us the value of pi. We will consider a circle with the radius of 128. We will visualize the random points that landed in the circle. There are more efficient methods of pi calculation; this is to illustrate Monte Carlo area calculation, which can be adapted for a broad range of shapes.

### Calculating median

To calculate the median, we need to sort the sequence.

### Plotting trigonometric functions

Plotting trigonometric functions (sine, cosine, arcus tangent):

### Calculating pi

One can get an acceptable approximation of pi via PI=4*ATN(1) or PI=2*ATN(1E97), the latter turning out to be more accurate. An algorithm for calculation of many digits coded in Atari BASIC is in the link.
Further reading:
* Pi, rosettacode.org

### A LCG pseudorandom number generator

There is the built-in RND pseudorandom generator function, which probably uses the 8-bit pseudorandom generator provided by the POKEY chip's 17-bit linear feedback shift register available at address 53770. This generator does not provide seeding and reproducibility; sources indicate that POKEY updates the value of its generator each CPU cycle, regardless of whether the value is being read. If one wants to have seeding and reproducibility, one can use e.g. linear congruential generator, in which the next integral value is calculated from the previous one using the formula x := a*x + c mod m for well chosen values of a, c and m. Since Atari BASIC numbers are floating point numbers with guaranteed 9 decimal places (rather than integers), one has to use m much smaller than 2^32 or 2^31. In the following, from Wikipedia's parameter combinations we select the one indicated for ZX81 (it traces to no source!), for which both the m and a*m fit with full integer precision into the Atari BASIC float. We convert the generated numbers to 8-bit integer values and plot the counts of value visits for a crude visual verification of the uniformity of the distribution. An alternative with a much longer period would be Wichmann–Hill, which combines three LCGs with the m of ca. 30000 and the a of ca. 170, well fitting into Atari BASIC float.
Further reading:
* Linear congruential generator, wikipedia.org
* Wichmann–Hill, wikipedia.org

### Hangman

The game of hangman is beautifully simple to implement, making it a good fit as an exerise for novice kid programmers. In the following, we omit any graphics and only count lives lost/left. Note that the words in the DATA statement are entered, perhaps surprisingly, without quotation marks.

### Binary expansion

We can calculate the binary expansion of a number between 0 and 1, an analogue of decimal expansion. The bits after decimal points represent 1/2, 1/4, 1/8, etc. The task seems simple enough for kids to figure it out. Tests values: 0.5: 0.1; 0.25: 0.01; 0.125: 0.001; 0.625: 101; 0.7: 0.10110011001100110011....
There is an ambiguity: e.g. 0.5 can be rendered as 0.1 or as 0.01111... The requirement is to produce the former, arguably more natural, output, in part since we cannot really output an infinite sequence, so the output sequence would be slightly imprecise in the latter option but not in the former option.
An alternative implementation by repeated multiplying the X by 2.
Going in the other direction, from binary to decimal, is also very simple and is left as an exercise.

### Ternary expansion

Using a method similar to binary expasion, we can calculate the ternary expansion. We can also add a verification part, which converts the ternary expansion back to the decimal form.

### Base n expansion

Using the exercise of ternary expansion above as a warm up, we can implement general base n expansion. For base greater than 16, we output the digits in decimal separated by semicolon. For base greater than 10 and smaller than or equal to 16, we output A-F as digits in addition to 0-9. We include verification by calculating the inverse, where the inverse is simpler.

### Number system conversion

We can implement number system conversion for positive integers: the user enters a decimal number and the target base; the number converted to the targer base is output. The idea used is rather similar to the base n expansion above. The reader can write a more general program as an exercise: the input is not in decimal and is a string, and there are both input base and output base specified.

### Gollatz conjecture

Assignment: explore Gollazt conjecture for as many initial integers as possible. Find the first integer for which the exploration of the conjecture exceeds the integer precision limit 1E9. Optionally, use caching in memory to speed it up.
One can show Gollatz conjecture even for some integers that are higher than the first integer for which the precision limit is hit.
Exploring the Gollatz conjecture on a small 8-bit computer is very inefficient but can be done as an exercise. One can try to figure out small optimizations to achieve faster exploration.
A simple naive algorithm with no caching:
A variant with a byte array (a string) used to refuse to explore already visited/seen numbers:
A variant with a byte array (a string) used to refuse to explore already visited/seen numbers and another byte array to prevent considering a cycle in the sequence as a descent to 1:
Above, we exit as soon as we visit a number higher than 1E9, the highest integer that is part of contiguous sequence of integers.
Given the very limited memory, one would want to store the flags on bit level rather than byte level. However, Atari BASIC lacks facilities to support this, especially bit test, which can be done with BITAND. One could implement it anyway; it is left as an exercise for the reader.
The caching optimization above is just an exercise with no impact once we start to explore the higher echelons. This is so even if we progressively let the cache start not at 1 but rather at, say, 1000, 2000, etc. Since, the operations of 3x+1 and x/2 work on scale, and the higher the starting number, the more removed are the next items from the baseline, and the less likely the unit-contiguous cache is going to be of any use.
Some questions: does it pay off to write a bit-array code given the lack of bitwise facilities? Does it pay off to have two arrays C$ and D$ with half of the memory that could be allocated to array C$ only? Answers to these questions depend on how soon the limit 1E9 is hit.
A variant assignment: output initial values of the Gollatz process whose orbit/trajectory reached previously unreached heights. This is https://oeis.org/A006884. Code without caching:
The same using a single-array caching:
The same using a two-array caching:
The single-array variant of the max-height start value finder seems to be generally fastest.

### Sieve of Eratosthenes

Assignment 1: implement the method of searching for prime numbers known as the sieve of Erathosthenes and report all the primes found. Aim for maximum prime number discoverable in this way. Use byte array (a string).
Assignment 2: as above but use bit array rather than byte array. Hint: use integer arithmetic to emulate bitwise access; warning: this is going to be very slow.
A solution to assignment 1:
The above requires quite a bit of patience but is tolerable. Recall that the finding of prime numbers becomes increasingly faster as the prime becomes bigger, a property of the sieve.
A solution to assignment 2:
The above bit manipulation via integer arithmetic is so slow that the above exercise is pointless: one would not dream of setting ILEN to 32767; setting ILEN to 50 is slow enough.
Reader challenge: modify the above by implementing the bit array access in assembly embedded in BASIC in a DATA statement.

### Minesweeper

Implementing Minesweeper in the character mode 0 is a reasonably simple exercise, although it requires some low-level tricks. There is going to be a cursor controlled by joystick and the cursor is to be indicated using inverse character (base code + 128). Optionally, pressing M key shall toggle a mine mark on the field. Pressing fire shall uncover the field unless the field has a mine mark. Here it goes.
Above, we use low-level tricks: 1) we access the video memory of the character mode directly via POKE and PEEK rather than using PRINT. The character values in the video memory are not the same as ATASCII and need a mapping; 2) we test for key press via POKE and PEEK.
Even for the size of 20x12 (the maximum is 40x24), it takes several seconds to generate the mines and several more seconds to draw the initial screen with its not yet explored fields. This suggests that Atari BASIC is too slow for most kinds of even simple games. Nonetheless, one could create a simple maze game or Sokoban, accepting the time it initially takes to draw the screen. Comparing the performance of Atari BASIC for Minesweeper with e.g. Boulder Dash shows how hugely faster assembly was on the Atari; Boulder Dash screens are large (in part not shown) and are drawn instantly.
An assignment: expand the above code to implement automatic uncovering of the fields around zero-noted fields.
A solution:
The above code uses GOSUB recursively (which uses stack for place of return), but all variables are global rather than being on the automatic stack. A stack is maintained manually. Positions to explore can appear on the stack repeatedly, a suboptimality. The above is slow but still more comfortable for the player than having to explore the affected fields manually.
An assignment: expand the above code with a solver that is going to automatically mark at least some fields where the presence of a mine is certain, to be called when the user presses S key. For instance, if the field says 1, there are 7 non-mine explored fields around it and there is one unexplored field around it not marked with a mine, mark that field with a mine.
A challenge: modify the code to use character mode 2, which allows different text colors for different characters (4 text colors + background color), where one pixel of the character is 2x2 the pixel of character mode 0. Use different colors for different purposes to increase usability; for instance, let the numbers have color #1, let the unexamined field have color #2 and let the mine mark have color #3. (In this mode, there are no inverse characters to be used for the cursor; one can use e.g. caret for cursor and keep the field state under the cursor in a separate variable.)
A challenge: rewrite the code to use character mode 12 (XL machines only), which allows 4 colors per character at the cost of lower resolution, while still allowing 40x24 characters on screen. Use custom character set. Use a PMG/sprite for cursor. This will enable a more colorful experience and probably a better usability. On the other hand, the code will be not so elegantly short. And one will have to think not only of programming but also of graphical design of the characters, and encode the character design in decimal numbers. This exercise is an excuse to learn about how to redefine a character set (including about how pairs of bits map to pixels and their colors) and how to use PMG/sprites.

### Generate maze

Assignment: Use the algorithm from Rosetta Code to generate a maze. Thus, consider a matrix of free square spaces all separated by walls. Randomly start at one such free space. Consider all four neighbors in random order and for all not yet visited ones, remove the wall facing them, mark them as visited and add them to a processing list implemented as a stack. Continue until the stack is empty. Handle corner cases and details not described here to make it work. Use the character mode 0 (40x24 characters).
A solution:
Notes on the implementation: The spaces are the locations with both x and y even and the walls are the other locations.
An exercise: modify the above code to plot the maze using graphics mode 7 (160x80 pixels). Modify the code further to support as big a maze as graphics mode 7 and Atari 800 XL memory allow.
A solution:
Implementation notes: we use a string as a byte array for the field array and visited array, dropping the memory requirements for them by the factor of 6. As a result, we need to calculate the physical index for the 2-dimensional array manually, and we need to repeate the index.
The above code makes full use of mode 7 screen size. Switching the above to mode 8 runs out of memory on Atari 800 XL: mode 8 needs twice as much video memory as mode 7. One could support mode 8 by encoding the boolean information in bits rather than bytes, but given the lack of bit manipulation operations in Atari BASIC, the result would be intolerably slow.

### Reconstructing floating-point numbers

Assignment: Using the understanding we gained in section Floating point numbers, write code to reconstruct a floating-point number from its internal representation, knowing that variable base is stored at addresses 134 and 135 and that the first variable's data start at offset 2. Test the reconstruction by randomly generating values to be reconstructed. This is a little exercise in systematic automated verification of an engineering hypothesis.
Note: One would think that exponentiation of 100 could be done using the ^ operator. But it is not so: the operator leads to loss of precision for high exponents.
Links:
* The Atari BASIC Source Book. 1983. https://archive.org/details/ataribooks-the-atari-basic-source-book. -- chapter Variable Values

### Assembly-style integer multiplication

Assignment: implement positive integer multiplication in the style of 6502 assembly. Thus, use only operations that have direct 6502 assembly equivalent, including multiplication by 2 (arithmetic shift left), addition and testing for inequality. Do not use general multiplication ("*"): it has no direct equivalent. As a simplification, do no bother about the 8-bit value limitations, and thus, pretend you have a many-bit integer multiplication by 2 rather than only 8-bit one. You can use the bitwise OR implementation from another exercise to create bitwise AND. The point: figure out or recall the algorithm that would be implemented in assembly, without having to write assembly. The algorithm is a simplified version of the algorithm for decimal multiplication learned in school.
A solution:
Compared to the builtin multiplication, one limitation of the above is that the bitwise AND only handles 32-bit input values. For 999999999*999999999, the above is a little less precise than the builtin.

### String integer multiplication

Assignment: given two input strings indicating positive integers in decimal, calculate an output string containing the result of multiplication.
A solution, arrived at by first writing and testing support procedures:

## Other considerations
### Performance

Atari BASIC is hugely slower compared to 6502 assembly. According to various sources, Frank Ostrowski's Turbo-BASIC XL is much faster. The causes of the slowness of Atari BASIC compared to assembly include its interpreted nature, its lack of integral arithmetic (all is floating point), and other.
We can compare the speed of Atari BASIC to assembly by considering the task of clearing the character mode screen, which amounts to zeroing the video memory. The speed of assembly for this task is given by printing CHR$(125), equivalent to CLS in some other BASICs.
The above test experimentally produces the performance ratio of over 400 times in favor of assembly, and that even counts the overhead of the loop around chr$(125).
We can make a similar comparison for fast string filling vs. manual one:
The above test experimentally produces the performance ratio of over 300 times in favor of assembly. Changing SLEN to 10000 results in a ratio of over 500.
Links:
* Atari BASIC Faster and Better, archive.org

### Vintage BASIC

Vintage BASIC is a FOSS implementation of an 8-bit-style BASIC. The web site features BASIC sources for many games. One can try to make them work with Atari BASIC, which requires some adaptation. One problem is that PRINT TAB(...) does not work. Another one is that Atari BASIC requires string to be dimensioned. Another one is that Atari BASIC's INPUT does not support prompt string and a separate PRINT is required instead.
Links:
* Games, vintage-basic.net
* Vintage BASIC User's Guide, vintage-basic.net

### Altirra BASIC

Altirra BASIC is a reimplementation of Atari BASIC, with additional features. Its reference manual can be used as a guide to Atari BASIC as long as one pays attention to the differences, which are indicated in section "Compatibility with Atari BASIC".
Links:
* Altirra BASIC Reference Manual, virtualdub.org

### Rosetta Code

One can use Rosetta Code as a source of exercises. Most lack a solution for Atari BASIC there. Exercises/assignments fit for Atari BASIC are likely to include those that have a solution for Commodore BASIC for Commodore 64, of which there are many more. Other categories of interest in Rosetta Code may be those for BBC BASIC and Applesoft BASIC.
Links:
* Category:Commodore BASIC, rosettacode.org