# Atari BASIC programming

This resource includes primary and/or secondary research. Learn more about original research at Wikiversity. |

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.

## Exercises

edit1) 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.

## Getting started

editPrograms 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.

## Text output

edit10 A=1:B=2:C=3:D=4 20 PRINT A,B,C,D:REM COLUMNS WITH LARGE SEPARATION 30 PRINT A;B;C;D:REM NO SEPARATION 40 PRINT A;" ";B;" ";C;" ";D 50 PRINT A;:PRINT B:REM NO NEWLINE AFTER A

## Conditionals

editThere 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.

10 IF 5>4 THEN PRINT 1:PRINT 2 20 INPUT A:IF A<=4 THEN GOTO 40 30 PRINT "A IS > 4" 40 INPUT A:IF A>=2 THEN 60:REM IMPLICIT GOTO 50 PRINT "A IS < 2" 60 END

There is no ELSE. To achieve if-else, one can either use GOTO or one can repeat the condition, e.g. like this:

10 INPUT X 20 IF X>=10 THEN PRINT "X>=10" 30 IF X<10 THEN PRINT "X<10"

There is no SWITCH command.

## Loops

editA for loop:

10 FOR I=1 TO 10 20 PRINT I 30 NEXT I

An early exit from the loop ("break"):

10 FOR I=1 TO 10 20 PRINT I 30 IF I=5 THEN I=10 40 NEXT I

There is no while loop. One can use GOTO instead:

10 I=1 20 IF I>10 THEN GOTO 60 30 PRINT I 40 I=I+1 50 GOTO 20 60 END

One can also emulate a do-while (repeat until) loop using GOTO:

10 I=1 20 PRINT I 30 I=I+1 40 IF I<=10 THEN GOTO 20

## Arrays

editOne can dimension and use a one-dimensional array of Atari BASIC numbers.

10 DIM A(10) 20 FOR I=1 TO 10:A(I)=I:NEXT I 30 FOR I=1 TO 10:PRINT A(I):NEXT I

One can also have two-dimensional arrays, but not three-dimensional ones:

10 DIM A(10,10) 20 FOR I=1 TO 10 30 FOR J=1 TO 10:A(I,J)=I*J:NEXT J 40 NEXT I 50 FOR I=1 TO 10 60 FOR J=1 TO 10:PRINT A(I,J);";";:NEXT J 70 PRINT 80 NEXT I

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.

## Calculating factorial

edit10 INPUT N 20 F=1 30 FOR I=1 TO N 40 F=F*I 50 NEXT I 60 PRINT F

## Graphics modes

editIn 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

## Plotting diagonal lines

edit10 GRAPHICS 8 11 COLOR 1 20 FOR X=0 TO 150 30 PLOT X,X 40 PLOT 150-X, X 50 NEXT X

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:

10 GRAPHICS 8 20 COLOR 1 30 PLOT 0,0 40 DRAWTO 150,150 50 PLOT 150,0 60 DRAWTO 0,150

## Plotting an ellipse

edit10 GRAPHICS 8 20 COLOR 1 30 RDS=75 40 FOR X=0 TO 2*3.141592 STEP 0.02 50 PLOT 2*RDS+2*RDS*SIN(X),RDS+RDS*COS(X) 60 NEXT X

The above is very slow.

## Random numbers

editThe 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):

10 FOR I=0 to 10 20 PRINT INT(10*RND(1)) 30 NEXT I

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.

10 REPEATCOUNT=1000000 20 DIM VALCOUNT(256) 30 GRAPHICS 8: COLOR 1 40 FOR I=1 TO 256: VALCOUNT(I)=0: NEXT I 50 FOR I=1 TO REPEATCOUNT 60 R=PEEK (53770): PRINT "RND: ";R 70 VALCOUNT(R+1)=VALCOUNT(R+1)+1 80 IF VALCOUNT(R+1)>160 THEN END:REM SCREEN LIMIT 90 PLOT R, VALCOUNT(R+1)-1 100 NEXT I

We can make the above frequency investigation for the Mandelbrot map-based pseudorandom generator (for more of which see section Plotting random pixels):

10 REPEATCOUNT=1000000 20 DIM VALCOUNT(256) 30 GRAPHICS 8: COLOR 1 35 X=0.1 40 FOR I=1 TO 256: VALCOUNT(I)=0: NEXT I 50 FOR I=1 TO REPEATCOUNT 55 GOSUB 200:REM RND 60 R=INT(256*X): PRINT "RND: ";R 70 VALCOUNT(R+1)=VALCOUNT(R+1)+1 80 IF VALCOUNT(R+1)>160 THEN END:REM SCREEN LIMIT 90 PLOT R, VALCOUNT(R+1)-1 100 NEXT I 110 END 200 REM NEXT RND 205 X=X*4-2.0 210 X=X*X-2.0:REM FROM [-2, 2] to [-2, 2] 215 X=X/4+0.5 216 IF X=1 THEN X=0.999999999 260 RETURN

Links:

- The Beginner's Page: The Random Function, atarimagazines.com

## Plotting random pixels

editPlotting random pixels in 2-color high-resolution mode (with text window):

10 GRAPHICS 8 20 COLOR 1 30 FOR X=0 TO 319 40 FOR Y=0 TO 159 50 C=INT(2*RND(1)) 60 COLOR C 70 PLOT X,Y 80 NEXT Y 90 NEXT X

Plotting random pixels in 4-color low-resolution (2x2-sized pixel) mode (with text window):

10 GRAPHICS 7 20 COLOR 1 30 FOR X=0 TO 159 40 FOR Y=0 TO 79 50 C=INT(4*RND(1)) 60 COLOR C 70 PLOT X,Y 80 NEXT Y 90 NEXT X

Plotting random pixels in 16-color low-resolution mode (4x1-sized pixel):

10 GRAPHICS 9 20 COLOR 1 30 FOR X=0 TO 79 40 FOR Y=0 TO 191 50 C=INT(16*RND(1)) 60 COLOR C 70 PLOT X,Y 80 NEXT Y 90 NEXT X

Plotting random pixels by low-level access to video memory (not particularly fast either):

10 GRAPHICS 8 20 P=PEEK(560)+PEEK(561)*256 30 VB=PEEK(P+4)+PEEK(P+5)*256:REM VIDEOBASE 40 FOR I=0 TO 320/8*160-1 50 POKE VB+I, PEEK(53770) 60 NEXT I

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.

10 GRAPHICS 8 20 P=PEEK(560)+PEEK(561)*256 30 VB=PEEK(P+4)+PEEK(P+5)*256:REM VIDEOBASE 35 X=0.1 40 FOR I=0 TO 320/8*160-1 45 BYTE=INT(256*X) 50 POKE VB+I,BYTE 55 GOSUB 100 60 NEXT I 70 END 100 REM NEXT RND 105 X=X*4-2.0 110 X=X*X-2.0:REM FROM [-2, 2] to [-2, 2] 115 X=X/4+0.5 116 IF X=1 THEN X=0.999999999 155 PRINT X;","; 160 RETURN

## Clearing the screen

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

## Outputting Atari ASCII characters

editOutputting Atari ASCII sequence except for special CHR$ codes, which are replaced with underscore ("_"):

10 DIM S$(256) 20 FOR I=0 TO 255 30 S$(I+1)=CHR$(I) 40 NEXT I 60 S$(27+1)="_" 61 S$(28+1)="_" 62 S$(29+1)="_" 63 S$(30+1)="_" 64 S$(31+1)="_" 80 S$(125+1)="_" 90 S$(155+1)="_" 100 S$(253+1)="_" 101 S$(254+1)="_" 102 S$(255+1)="_" 110 PRINT S$

Outputting them one character per line, with the ASCII code:

10 DIM S$(256) 20 FOR I=0 TO 255 30 S$(I+1)=CHR$(I) 40 NEXT I 60 S$(27+1)="_" 61 S$(28+1)="_" 62 S$(29+1)="_" 63 S$(30+1)="_" 64 S$(31+1)="_" 80 S$(125+1)="_" 90 S$(155+1)="_" 100 S$(253+1)="_" 101 S$(254+1)="_" 102 S$(255+1)="_" 110 FOR I=0 TO 255 120 PRINT I, S$(I+1,I+1) 130 NEXT I

Links:

- ATASCII, wikipedia.org
- ATASCII to Unicode Mapping, kreativekorp.com
- The Beginners Page: Printing And Asking, atarimagazines.com

## Plotting Mandelbrot set

editAn adaptation of AWK code from rosettacode.org for the 2-color mode 8:

10 GRAPHICS 8 20 XSIZE=320:YSIZE=160 30 ITERMAX=10 40 MINIM=-1.0:MAXIM=1.0:MINRE=-2.0:MAXRE=1.0 50 STEPX=(MAXRE-MINRE)/XSIZE:STEPY=(MAXIM-MINIM)/YSIZE 60 FOR Y=0 TO YSIZE-1 70 CIM=MINIM+STEPY*Y 80 FOR X=0 TO XSIZE-1 90 CRE=MINRE+STEPX*X 100 ITCOUNT=0 110 RE=0:IM=0 120 RESQ=RE*RE:IMSQ=IM*IM 130 IF RESQ+IMSQ > 4 THEN GOTO 170 140 ITCOUNT=ITCOUNT+1 150 IM=2*RE*IM+CIM:RE=RESQ-IMSQ+CRE 160 IF ITCOUNT<ITERMAX THEN GOTO 120 170 ICMOD = ITCOUNT-INT(ITCOUNT/2)*2 180 COLOR ICMOD 190 PLOT X,Y 200 NEXT X 210 NEXT Y

A modification of the above for the 4-color mode 7:

10 GRAPHICS 7 15 SETCOLOR 4,0,0:SETCOLOR 0,0,5:SETCOLOR 1,0,10:SETCOLOR 2,0,15 20 XSIZE=160:YSIZE=80 30 ITERMAX=16 40 MINIM=-1:MAXIM=1:MINRE=-2:MAXRE=1 50 STEPX=(MAXRE-MINRE)/XSIZE:STEPY=(MAXIM-MINIM)/YSIZE 60 FOR Y=0 TO YSIZE-1 70 CIM=MINIM+STEPY*Y 80 FOR X=0 TO XSIZE-1 90 CRE=MINRE+STEPX*X 100 ITCOUNT=0 110 RE=0:IM=0 120 RESQ=RE*RE:IMSQ=IM*IM 130 IF RESQ+IMSQ>4 THEN GOTO 170 140 ITCOUNT=ITCOUNT+1 150 IM=2*RE*IM+CIM:RE=RESQ-IMSQ+CRE 160 IF ITCOUNT<ITERMAX THEN GOTO 120 170 ICMOD=ITCOUNT-INT(ITCOUNT/4)*4 180 COLOR ICMOD 190 PLOT X,Y 200 NEXT X 210 NEXT Y

A modification of the above for the 16-color mode 9:

10 GRAPHICS 9 20 XSIZE=80:YSIZE=192 30 ITERMAX=16 40 MINIM=-1.0:MAXIM=1.0:MINRE=-2.0:MAXRE=1.0 50 STEPX=(MAXRE-MINRE)/XSIZE:STEPY=(MAXIM-MINIM)/YSIZE 60 FOR Y=0 TO YSIZE-1 70 CIM=MINIM+STEPY*Y 80 FOR X=0 TO XSIZE-1 90 CRE=MINRE+STEPX*X 100 ITCOUNT=0 110 RE=0:IM=0 120 RESQ=RE*RE:IMSQ=IM*IM 130 IF RESQ+IMSQ > 4 THEN GOTO 170 140 ITCOUNT=ITCOUNT+1 150 IM=2*RE*IM+CIM:RE=RESQ-IMSQ+CRE 160 IF ITCOUNT<ITERMAX THEN GOTO 120 170 ICMOD = ITCOUNT-INT(ITCOUNT/16)*16 180 COLOR ICMOD 190 PLOT X,Y 200 NEXT X 210 NEXT Y

## Calculating modulo

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

10 INPUT X, Y 20 MOD = X - Y*INT(X/Y) 30 PRINT X;" MOD ";Y;": ";MOD

## Random walk

editPlot 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.

10 GRAPHICS 8 20 COLOR 1 30 PLOT 0,0 40 DRAWTO 0,160 50 PLOT 0,80 60 DRAWTO 319,80 70 X=0 80 STEPNO=0 90 PLOT STEPNO,80-X 100 X=X+INT(3*RND(1))-1 110 STEPNO=STEPNO+1 120 IF STEPNO<320 THEN GOTO 90

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:

10 GRAPHICS 8 20 COLOR 1 30 X=160:Y=80 40 PLOT X,Y 50 X=X+INT(3*RND(1))-1 60 Y=Y+INT(3*RND(1))-1 70 GOTO 40

## Plotting Mandelbrot map orbit diagram

editLet Mandelbrot map refer to x**2 + c. Plotting its orbit diagram/bifurcation diagram:

10 GRAPHICS 8 20 COLOR 1 30 FOR X=0 TO 320-1 40 C=X/320*2.25-2 50 Y=0 60 FOR IT=0 TO 20 70 YPL=80-Y*40 75 PLOT X,YPL 80 Y=Y*Y+C 90 NEXT IT 100 NEXT X

A variant of the above with initial iterations not plotted:

10 GRAPHICS 8 20 COLOR 1 30 FOR X=0 TO 320-1 40 C=X/320*2.25-2 50 Y=0 60 FOR IT=0 TO 40 70 YPL=80-Y*40 72 IF IT <= 20 THEN GOTO 80 75 PLOT X,YPL 80 Y=Y*Y+C 90 NEXT IT 100 NEXT X

## Color registers

editColor registers are affected via SETCOLOR. The SETCOLOR parameters are color register number, hue and luminance.

Color register test for two-color text mode 0:

10 GRAPHICS 0 20 PRINT "HELLO" 30 SETCOLOR 0,2,8:REM NO IMPACT 40 SETCOLOR 1,7,0:REM TEXT FOREGROUND; HUE HAS NO IMPACT 50 SETCOLOR 2,0,15:REM TEXT BACKGROUND; SETS HUE FOR TXT FRG 60 SETCOLOR 3,3,8:REM NO IMPACT 70 SETCOLOR 4,1,8:REM OUTER BACKGROUND

Color register test for two-color pixel bitmap mode 8:

10 GRAPHICS 8 20 COLOR 0:PLOT 0,0:DRAWTO 319,0 30 COLOR 1:PLOT 0,1:DRAWTO 319,1 35 SETCOLOR 0,2,8:REM NO IMPACT 40 SETCOLOR 1,7,0:REM COLOR 1; HUE HAS NO IMPACT 50 SETCOLOR 2,0,15:REM COLOR 0; SETS HUE FOR COLOR 1 60 SETCOLOR 3,3,8:REM NO IMPACT 70 SETCOLOR 4,1,8:REM OUTER BACKGROUND

Color register test for four-color pixel bitmap mode 7:

10 GRAPHICS 7 20 COLOR 0:PLOT 0,0:DRAWTO 80-1,0 25 COLOR 1:PLOT 80,0:DRAWTO 160-1,0 30 COLOR 0:PLOT 0,1:DRAWTO 80-1,1 35 COLOR 1:PLOT 80,1:DRAWTO 160-1,1 40 COLOR 2:PLOT 0,2:DRAWTO 80-1,2 45 COLOR 3:PLOT 80,2:DRAWTO 160-1,2 50 COLOR 2:PLOT 0,3:DRAWTO 80-1,3 55 COLOR 3:PLOT 80,3:DRAWTO 160-1,3 60 SETCOLOR 0,0,5:REM COLOR 1 70 SETCOLOR 1,0,10:REM COLOR 2 80 SETCOLOR 2,0,15:REM COLOR 3 90 SETCOLOR 3,2,8:REM NO IMPACT 95 SETCOLOR 4,0,0:REM COLOR 0 AND OUTER BACKGROUND

Color register test for four-color pixel bitmap mode 7 with low-level access (POKEs and bit patters):

10 GRAPHICS 7 20 P=PEEK(560)+PEEK(561)*256 30 VB=PEEK(P+4)+PEEK(P+5)*256: REM VIDEOBASE 35 LB=320/8:REM LINE BYTES 40 FOR I=0 TO LB-1: POKE VB+I, 0: NEXT I 50 FOR I=LB TO 2*LB-1: POKE VB+I, 85: NEXT I 60 FOR I=2*LB TO 3*LB-1: POKE VB+I, 170: NEXT I 70 FOR I=3*LB TO 4*LB-1: POKE VB+I, 255: NEXT I 80 SETCOLOR 0,0,5:REM COLOR 1 90 SETCOLOR 1,0,10:REM COLOR 2 100 SETCOLOR 2,0,15:REM COLOR 3 110 SETCOLOR 3,2,8:REM NO IMPACT 120 SETCOLOR 4,0,0:REM COLOR 0 AND OUTER BACKGROUND

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

editPlotting diagonal lines using assembly-style low-level programming for plotting pixels in color 1 in mode 8:

10 GRAPHICS 8 20 FOR I=0 TO 159 30 X=I:Y=I:GOSUB 200 40 NEXT I 50 FOR I=0 TO 159 60 X=I+1:Y=I:GOSUB 200 70 NEXT I 80 FOR I=0 TO 159:REM REPEAT FOR TEST 90 X=I:Y=I:GOSUB 200 100 NEXT I 110 END 200 REM PLOT X,Y FOR MODE 8 WITH COLOR 1 205 REM -------------------------------- 210 P=PEEK(560)+PEEK(561)*256 220 VB=PEEK(P+4)+PEEK(P+5)*256: REM VIDEOBASE 230 BYTEOFFSET=INT(X/8) + 320/8*Y 240 MOD=X-INT(X/8)*8 250 BITPAT=2^(7-MOD) 260 A=PEEK(VB+BYTEOFFSET):B=BITPAT 270 GOSUB 400 360 POKE VB+BYTEOFFSET, ORED 370 RETURN 400 REM BYTE-OR A, B 405 REM ------------------ 410 ORED=0 420 FOR J=0 TO 7 430 ORED=2*ORED 440 IF A<128 AND B<128 THEN GOTO 480 450 ORED=ORED+1 460 IF A>=128 THEN A=A-128 470 IF B>=128 THEN B=B-128 480 A=A*2: B=B*2 490 NEXT J 500 RETURN

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.

## Bitwise OR

editByte-sized bitwise OR:

400 REM BYTE-OR A, B 405 INPUT A,B 410 ORED=0 420 FOR J=0 TO 7 430 ORED=2*ORED 440 IF A<128 AND B<128 THEN GOTO 480 450 ORED=ORED+1 460 IF A>=128 THEN A=A-128 470 IF B>=128 THEN B=B-128 480 A=A*2: B=B*2 490 NEXT J 500 PRINT ORED

The Stack Overflow link below contains C code for bitwise AND and XOR that can be translated into Atari BASIC in a rather straightforward manner.

Links:

## Reporting joystick state/events

edit10 PRINT STICK(0), STRIG(0) 20 GOTO 10

## Plotting joystick-controlled vehicle

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

10 GRAPHICS 8 15 TWOPI=2*3.141592 20 ANGLERAD=0 30 XRE=160:YRE=80 35 COLOR 1 40 PLOT INT(XRE), INT(YRE) 50 XRE=XRE+COS(ANGLERAD) 60 YRE=YRE+SIN(ANGLERAD) 70 REM ANGLERAD=ANGLERAD+RND(1)*0.1-0.5*0.1 80 IF STICK(0)<>11 THEN GOTO 100:REM 11=RIGHT 90 ANGLERAD=ANGLERAD-0.1 100 IF STICK(0)<>7 THEN GOTO 120:REM 7=LEFT 110 ANGLERAD=ANGLERAD+0.1 120 IF ANGLERAD<TWOPI THEN GOTO 140 130 ANGLERAD=ANGLERAD-TWOPI 140 IF ANGLERAD>=0 THEN GOTO 160 150 ANGLERAD=ANGLERAD+TWOPI 160 GOTO 40

## Plotting a spiral

editPlotting a spiral with a linearly growing radius:

10 GRAPHICS 8 20 COLOR 1 30 R=0 40 PLOT 160,80 50 FOR X=0 TO 8*2*3.141592 STEP 0.1 60 DRAWTO 160+INT(R*COS(X)),80+INT(R*SIN(X)) 70 R=R+0.1 80 NEXT X

Plotting a spiral with an exponentially growing radius:

10 GRAPHICS 8 20 COLOR 1 30 R=2 40 PLOT 160,80 50 FOR X=0 TO 6*2*3.141592 STEP 0.1 60 DRAWTO 160+INT(R*COS(X)),80+INT(R*SIN(X)) 70 R=R*1.01 80 NEXT X

## Abbreviations

editAbbreviations include:

- "?" for PRINT.
- L. for LIST
- GR. for GRAPHICS
- SE. for SETCOLOR
- Etc.

Links:

- Atari BASIC Quick Reference Guide, atariwiki.org

## Floating point numbers

editThe numbers Atari BASIC works with are floating point numbers. They are 6-byte floating point numbers, stored in variable memory as binary-coded decimal (BCD) numbers^{[1]} (but then, why is E97 maximum if it is BCD and not e.g. E99?) The 6 bytes seem to be not always fully made use of. Of them, 5 bytes seem to be for mantissa, and 1 byte for exponent and sign. The 5 bytes should theoretically be able to represent 9999999999, but this gets rounded to 9999999990.

Test:

10 MAXNUM=9.99999999E97 20 PRINT MAXNUM 30 MINNUM=-9.99999999E97 40 PRINT MINNUM 50 PRINT MAXNUM+MINNUM 60 SMALLPOSNUM=1.0E-98:REM PERHAPS NOT SMALLEST 70 PRINT SMALLPOSNUM 80 THIRD=1/3 90 PRINT THIRD 100 IF THIRD=0.333333333 THEN GOTO 100 110 PRINT "THIRD DIFFERS WITH DELTA:", THIRD-0.333333333

The following tests suggests that 1000000000 (1E9) is the largest integer contiguous with other integers with units precisely represented:

10 A=999999999 20 PRINT A 30 A=A+1 40 IF A<1000000010 THEN GOTO 20

However, the above terminates after 10 iterations while outputting 10000000000, which is puzzling and suggests the output does not exactly match the representation.

One more test:

10 FOR I=1 TO 100 20 J=1E9+I 30 K=J-1E9 40 PRINT I,K,J 50 NEXT I

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:

10 INPUT A 20 VB=PEEK(134)+256*PEEK(135):REM VARIABLE BASE 30 PRINT PEEK(VB+2);" ";PEEK(VB+3);" ";PEEK(VB+4);" ";PEEK(VB+5);" ";PEEK(VB+6);" ";PEEK(VB+7)

Link:

- Inside Atari BASIC by Larry Isaacs, atariarchives.org

## Calling assembly

editOne 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:

ASM ----- PLA ;arg count; discard PLA ;arg1 upper byte; discard PLA ;arg1 lower byte STA 212; lower byte of USR return value PLA ;arg2 upper byte; discard PLA ;arg2 lower byte ORA 212 STA 212 LDA #0 STA 213 ;clear the upper byte of ret val RTS ASM HEX ----- 0000: 68 68 68 85 D4 68 68 05 0008: D4 85 D4 A9 00 85 D5 60 ASM DEC ----- 0000: 104 104 104 133 212 104 104 5 0008: 212 133 212 169 00 133 213 96 BASIC WITH ASM AT PAGE 6 ------------------------ 10 X=0:RESTORE 40 20 READ D:IF D=-1 THEN GOTO 100 30 POKE 1536+X,D:X=X+1:GOTO 20 40 DATA 104, 104, 104, 133, 212, 104, 104, 5 50 DATA 212, 133, 212, 169, 00, 133, 213, 96, -1 100 INPUT A, B 110 RES=USR(1536, A, B) 120 PRINT RES BASIC WITH ASM AT A STRING -------------------------- 5 DIM ORASM$(20) 10 X=1:RESTORE 40 20 READ D:IF D=-1 THEN GOTO 100 30 ORASM$(X)=CHR$(D):X=X+1:GOTO 20 40 DATA 104, 104, 104, 133, 212, 104, 104, 5 50 DATA 212, 133, 212, 169, 00, 133, 213, 96, -1 100 INPUT A, B 110 RES=USR(ADR(ORASM$), A, B) 120 PRINT RES

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
- Part 3 of 11 -- Simple Assembly for Atari BASIC - kenjennings' Blog, forums.atariage.com

## Monte Carlo area calculation

editWe 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.

10 GRAPHICS 8: COLOR 1 20 I128SQ=128*128 30 FOR I=1 TO 10000 40 X=PEEK(53770)-128 50 Y=PEEK(53770)-128 60 RSQ=X*X+Y*Y 70 IF RSQ <= I128SQ THEN INSIDECNT=INSIDECNT+1 80 IF RSQ <= I128SQ THEN PLOT INT((X+128)/2), INT((Y+128)/2) 90 TOTALCNT=TOTALCNT+1 100 NEXT I 110 AREARATIO=INSIDECNT/TOTALCNT 120 PIAPPROX=AREARATIO*4 130 PRINT "PI ESTIMATE: ";PIAPPROX 140 REM CIRCLE AREA=PI*R^2; SQUARE AREA=4*R^2

## Calculating median

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

10 DIM V(101) 20 FOR I=1 TO 101 30 V(I) = PEEK(53770):REM RANDOM in 0-255 40 NEXT I 50 FOR I=1 TO 101: ? V(I);", ";:NEXT I:PRINT 60 GOSUB 200:REM BUBBLE SORT 70 PRINT "SORTED: " 80 FOR I=1 TO 101: ? V(I);", ";:NEXT I:PRINT 90 PRINT "MEDIAN: ";V(51) 100 END 200 REM BUBBLE SORT ON V 210 SW=0:REM SWAPPED 220 FOR I=1 TO 100 230 IF V(I+1)<V(I) THEN S=V(I):V(I)=V(I+1):V(I+1)=S:SW=1 240 NEXT I 250 IF SW=1 THEN GOTO 210 260 RETURN

## Plotting trigonometric functions

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

10 GRAPHICS 8 20 FOR I=0 TO 319 30 X=I/320*8*2 40 PLOT I,80-40*SIN(X) 50 PLOT I,80-40*COS(X) 55 PLOT I,80-40*ATN(X) 60 NEXT I

## Strings

editStrings have to be dimensioned for size before use. Their use is as follows:

10 DIM S$(5) 20 S$="HEY":PRINT S$ 30 S$="HELLO":PRINT S$ 40 S$(2,2)="A":PRINT S$ 50 FOR I=1 TO LEN(S$):PRINT S$(I,I);:NEXT I:? 60 S$="HELLO1":REM TRUNCATE 1 70 PRINT CHR$(65):REM A 80 PRINT ASC("A") 90 DIM H$(11) 100 H$="HELLO" 110 H$(LEN(H$)+1)=" WORLD":REM CONCATENATE 120 PRINT H$

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:

10 DIM S$(5) 20 FOR I=1 TO 5: S$(I,I)=CHR$(65): NEXT I 30 FOR I=1 TO 5: S$(I,I)=CHR$(ASC(S$(I,I))+1): NEXT I 40 PRINT S$

We can also use strings as byte arrays via ADR, PEEK and POKE:

10 DIM S$(5) 15 S$(5)="A":SA=ADR(S$) 20 FOR I=1 TO 5: POKE SA+I-1, 65: NEXT I 30 FOR I=1 TO 5: POKE SA+I-1, PEEK(SA+I-1)+1: NEXT I 40 PRINT S$

ADR and other techniques from above are also used to store machine code in a string, as per Calling assembly section.

## Calculating prime numbers

edit10 FOR I=2 TO 100 20 PR=1 30 FOR D=2 TO INT(SQR(I)) 40 IF D<>I AND I/D=INT(I/D) THEN PR=0 50 NEXT D 60 IF PR=1 THEN PRINT I 70 NEXT I

## Calculating pi

editOne 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

## Player-missile graphics

editPlayer-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

## Plotting a rectangular spiral

edit10 GRAPHICS 8: COLOR 1 20 FOR I=0 TO 39 30 PLOT 0+2*I,0+2*I 40 IF I>0 THEN PLOT 0+2*I-2,0+2*I 50 DRAWTO 319-2*I,0+2*I 60 DRAWTO 319-2*I,159-2*I 70 DRAWTO 0+2*I,159-2*I 80 DRAWTO 0+2*I,0+2*I+2 90 NEXT I

## A LCG pseudorandom number generator

editThere 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.

10 REPEATCOUNT=1000000 20 DIM VALCOUNT(256) 30 GRAPHICS 8: COLOR 1 35 RX=0:REM OR A DIFFERENT SEED 40 FOR I=1 TO 256: VALCOUNT(I)=0: NEXT I 50 FOR I=1 TO REPEATCOUNT 55 GOSUB 200:REM RX:=RND 60 R=RX-256*INT(RX/256):REM MOD 70 VALCOUNT(R+1)=VALCOUNT(R+1)+1 80 IF VALCOUNT(R+1)>160 THEN END:REM SCREEN LIMIT 90 PLOT R, VALCOUNT(R+1)-1 100 NEXT I 110 END 200 REM NEXT RND 210 RX=75*RX+74 220 RX=RX-65537*INT(RX/65537):REM MOD 260 RETURN

Further reading:

- Linear congruential generator, wikipedia.org
- Wichmann–Hill, wikipedia.org

## Hangman

editThe 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.

10 WORDMAXLEN=10:LIVES=5:WRDCOUNT=10 20 DIM WORD$(WORDMAXLEN), GUESS$(1) 30 DATA GROUND,SNEEZE,MOTHER,SISTER,PILLAR,SMILED,POOLED,TOTALS,FROZEN,RABBIT 40 IDX=INT(RND(1)*WRDCOUNT) 50 I=0 60 READ WORD$ 70 IF I<IDX THEN I=I+1: GOTO 60 80 DIM LTRUNKN(WORDMAXLEN) 90 FOR I=1 TO LEN(WORD$):LTRUNKN(I)=1:NEXT I 100 LTGC=LEN(WORD$):REM LETTER TO GUESS COUNT 110 FOR I=1 TO LEN(WORD$) 120 IF LTRUNKN(I)=1 THEN PRINT "*"; 130 IF LTRUNKN(I)=0 THEN PRINT WORD$(I,I); 140 NEXT I 150 PRINT 160 IF LTGC=0 THEN PRINT "COMPLETED!":END 170 INPUT GUESS$ 180 LTGCO=LTGC 190 FOR I=1 TO LEN(WORD$) 200 IF WORD$(I,I) = GUESS$ THEN LTRUNKN(I) = 0:LTGC=LTGC-1 210 NEXT I 220 IF LTGCO=LTGC THEN LIVES=LIVES-1: PRINT "LIVES LEFT: ";LIVES 230 IF LIVES = 0 THEN PRINT "NO MORE LIVES.":END 240 GOTO 110

## Binary expansion

editWe 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.

10 PRINT "BINARY EXPANSION" 20 INPUT X 30 IF X<0 OR X>=1 THEN PRINT "BAD X":END 40 BV=0.5 50 PRINT "0."; 60 FOR I=1 TO 30 70 IF X >= BV THEN GOTO 90 80 PRINT "0";:GOTO 100 90 X=X-BV:PRINT "1"; 100 BV=BV/2 110 IF X=0 THEN I=30 120 NEXT I 130 PRINT 140 GOTO 20

An alternative implementation by repeated multiplying the X by 2.

10 PRINT "BINARY EXPANSION" 20 INPUT X 30 IF X<0 OR X>=1 THEN PRINT "BAD X":END 35 PRINT "0."; 40 FOR I=1 TO 30 50 X=X*2 60 IF X<1 THEN PRINT "0"; 70 IF X>=1 THEN PRINT "1";:X=X-1 80 IF X=0 THEN I=30 90 NEXT I 100 PRINT 110 GOTO 20

Going in the other direction, from binary to decimal, is also very simple and is left as an exercise.

## Ternary expansion

editUsing 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.

5 DIM S$(37) 10 PRINT "TERNARY EXPANSION" 20 INPUT X 30 IF X<0 OR X>=1 THEN PRINT "BAD X":END 40 S$="0." 50 FOR I=1 TO 35 60 X=X*3 70 IF X<1 THEN S$(I+2,I+2)="0" 80 IF X>=2 THEN S$(I+2,I+2)="2":X=X-2 90 IF X>=1 THEN S$(I+2,I+2)="1":X=X-1 100 IF X=0 THEN I=35 110 NEXT I 120 PRINT S$ 130 REM ----Verification---- 140 W=1/3:X=0 150 FOR I=1 TO 35 160 IF S$(I+2,I+2)="1" THEN X=X+W 165 IF S$(I+2,I+2)="2" THEN X=X+2*W 170 W=W/3 180 NEXT I 190 PRINT "RECONSTRUCTED X: ";X

## Base n expansion

editUsing 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.

10 DIM S(30), SEP$(1) 20 PRINT "BASE N EXPANSION" 30 PRINT "X:";:INPUT X:OX=X 40 IF X<0 OR X>=1 THEN PRINT "BAD X":END 50 PRINT "BASE:";:INPUT B 60 B=INT(B) 70 IF B<2 THEN PRINT "BAD BASE":END 80 L=30:LF=0:REM LENGTH and LENGTH FOUND 90 FOR I=1 TO 30 100 X=X*B 110 IF X<1 THEN S(I)=0 120 FOR J=B-1 TO 1 STEP -1 130 IF X>=J THEN S(I)=J:X=X-J 140 NEXT J 150 IF LF=0 AND X=0 THEN LF=1:L=I 160 NEXT I 170 PRINT "0."; 180 IF B>10 AND B<=16 THEN GOTO 230 190 SEP$="":IF B>10 THEN SEP$=";" 200 FOR I=1 TO L:PRINT S(I);SEP$;:NEXT I 210 PRINT 220 GOTO 300 230 REM BASE 11 TO 16 240 FOR I=1 TO L 250 IF S(I)<10 THEN PRINT S(I); 260 IF S(I)>=10 THEN PRINT CHR$(65+S(I)-10); 270 NEXT I 280 PRINT 290 REM -------- Verification -------- 300 RX=0 310 W=1/B 320 FOR I=1 TO 30 330 RX=RX+S(I)*W 340 W=W/B 350 NEXT I 360 PRINT "RECON.X & DELTA: ";RX;"; ";ABS(OX-RX)

## Number system conversion

editWe 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.

10 DIM S(100) 20 PRINT "NUMBER SYSTEM CONVERSION" 30 PRINT "X: ";:INPUT X 40 PRINT "BASE: ";:INPUT B 50 TH=1:REM THRESHOLD 60 TH=TH*B 70 IF TH<=X THEN GOTO 60 80 I=1 90 TH=TH/B 100 IF X<TH THEN S(I)=0 110 FOR J=B-1 TO 1 STEP -1 120 IF X>=TH*J THEN S(I)=J:X=X-TH*J:J=1 130 NEXT J 140 IF TH>1 THEN I=I+1:GOTO 90 150 IF B>10 AND B<=16 THEN GOTO 190 160 DIM SEP$(1):SEP$="":IF B>10 THEN SEP$=";" 170 FOR K=1 TO I: PRINT S(K);SEP$;:NEXT K:PRINT 180 GOTO 250 190 REM BASE 11-16 200 FOR K=1 TO I 210 IF S(K)<10 THEN PRINT S(K); 220 IF S(K)>=10 THEN PRINT CHR$(65+S(K)-10); 230 NEXT K 240 PRINT 250 REM ---- Verification ---- 260 X=0:W=1 270 FOR K=I TO 1 STEP -1:X=X+S(K)*W:W=W*B:NEXT K 280 PRINT "RECON.X: ";X

## Limitations

editLimitations 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).

## References

edit- ↑ Why did 8-bit Basic use 40-bit floating point?, retrocomputing.stackexchange.com

## Further reading

edit- Atari BASIC, wikipedia.org
- ATASCII, wikipedia.org
- Atari BASIC Reference Manual, archive.org
- Atari BASIC Quick Reference Guide, atariwiki.org
- Understanding Atari graphics, atarimania.com
- Atari Graphics & Arcade Game Design by Jeffrey Stanton with Dan Pinal, atariarchives.org
- Category:Atari BASIC, rosettacode.org
- Atari 8-bit, emulation.gametechwiki.com
- Atari Basic instructions, blog.3b2.sk -- short descriptions of instructions
- Atari BASIC Source book, atariwiki.org
- Atari BASIC, atariwiki.org