Atari BASIC programming

From Wikiversity
Jump to navigation Jump to search

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.

Exercises[edit | edit source]

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.

Getting started[edit | edit source]

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.

Text output[edit | edit source]

10 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[edit | edit source]

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 NO GOTO
50 PRINT "A IS < 2"
60 END

Calculating factorial[edit | edit source]

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

Graphics modes[edit | edit source]

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:

Plotting diagonal lines[edit | edit source]

10 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[edit | edit source]

10 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[edit | edit source]

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

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:

Plotting random pixels[edit | edit source]

Plotting 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[edit | edit source]

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

Outputting Atari ASCII characters[edit | edit source]

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

Plotting Mandelbrot set[edit | edit source]

An 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[edit | edit source]

Atari 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[edit | edit source]

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.

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[edit | edit source]

Let 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[edit | edit source]

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:

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:

Low-level access plotting[edit | edit source]

Plotting 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[edit | edit source]

Byte-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[edit | edit source]

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

Plotting joystick-controlled vehicle[edit | edit source]

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.

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[edit | edit source]

Plotting 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[edit | edit source]

Abbreviations include:

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

Links:

Floating point numbers[edit | edit source]

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

Calling assembly[edit | edit source]

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:

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:

Monte Carlo area calculation[edit | edit source]

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.

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[edit | edit source]

To 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[edit | edit source]

Plotting 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[edit | edit source]

Strings 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[edit | edit source]

10 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[edit | edit source]

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

Player-missile graphics[edit | edit source]

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:

Plotting a rectangular spiral[edit | edit source]

10 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[edit | edit source]

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.

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:

Limitations[edit | edit source]

Limitations of Atari BASIC include the following:

  • No separation of integer arithmetic from floating-point arithmetic. The consequence is a major slowdown of what could otherwise be integer operations.
  • Almost no plotting commands: only PLOT and DRAWTO.
  • No commands for player-missile graphics.
  • No CLS (very minor limitation if one at all).
  • No bitwise operators.
  • 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 taking of address of a variable except for a string variable.
  • No hexadecimal literals.

References[edit | edit source]

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

Further reading[edit | edit source]