Last Monday I released the 59th issue of De Programmatica Ipsum, my dear monthly magazine about code, developers, and society, and this month I talked about BASIC in all of its flavors. As part of the preparation of this issue, I dived into the world of Minimal BASIC code, the one with source code line numbers, the one that would start immediately after powering up your computer, and the one that brings endless nostalgia.
As part of my exploration, I decided to reimplement my venerable Polyglot Conway project in what is usually called “Minimal BASIC”, defined as a standard known as ECMA-55 and released in 1978.
Writing it
To make it fit in the screen space offered by most emulators, I reduced the size of the grid I used in other versions of the program from 30 by 30 to just 9 by 9, with only a few “objects” animated.
The result of my work, admittedly retro, is right here, with all the bells and whistles you expect from ECMA-55: line numbers, global variables, uppercase statements, DATA
, GOTO
, GOSUB
, the whole package.
In line 100 we initialize the variable W
(or “World”) with a matrix of data read from lines 1000 to 1090. The value 1
, as you might expect, represents a location occupied by a living cell, while 0
represents an empty location.
100 REM INITIALIZATION
110 LET S = 8
120 LET D$ = "|"
130 DIM W(S, S)
140 DIM Z(S, S)
150 FOR I = 0 TO S
160 FOR J = 0 TO S
170 READ W(I, J)
180 NEXT J
190 NEXT I
200 LET G = 0
The variable D$
in line 120 contains a vertical bar, separated on its own, so that we can change its value (for example, in the Commodore 64, we change it to an uppercase B
, which in PETSCII is represented as a vertical bar. Don’t ask.)
The variable S
contains the maximum index of rows or columns in the grid. Since we iterate from zero, the final grid is 9 by 9.
From lines 370 to 400 we print the grid system on top and to the left of the world. In lines 420 and 430 we print an X
at each living cell, and a blank space for the rest.
In line 440 we avoid printing a final vertical bar if we’re at the end of the line, to save some screen space.
We use two GOSUB
statements here (lines 380 and 510), and both “procedures” are described below in this article. Line 520 contains a nice GOTO
statement that makes this part of the code repeat ad nauseam.
350 REM PRINT GRID IN AN ENDLESS LOOP
360 PRINT ""
370 FOR A = 0 TO S
380 IF A = 0 THEN GOSUB 600
390 PRINT A;
400 PRINT D$;
410 FOR B = 0 TO S
420 IF W(B, A) = 0 THEN PRINT " ";
430 IF W(B, A) = 1 THEN PRINT " X ";
440 IF B < S THEN PRINT D$;
450 NEXT B
460 PRINT ""
470 NEXT A
480 LET G = G + 1
490 PRINT ""
500 PRINT "GENERATION ", G
510 GOSUB 700
520 GOTO 350
In line 380 we GOSUB
to line 600 to print the first line of the grid system, and then we return. GOSUB
s are the closest thing we have to subroutines in Minimal BASIC.
Also, remember: all variables are global!
600 REM FIRST LINE WITH COORDINATES
610 FOR B = 0 TO S
620 IF B = 0 THEN PRINT " 0 ";
630 IF B > 0 THEN PRINT B;
640 IF B < S THEN PRINT D$;
650 NEXT B
660 PRINT ""
670 RETURN
From lines 700 to 900 we have a routine that makes the W
variable evolve to its next state. From 710 to 760 we reset all values in W
to zero; but don’t worry, we make a copy of W
called Z
in line 730.
Then from lines 770 to 900 we have a series of nested FOR
loops. We iterate cell by cell, and for each, we count the number of living organisms around them, storing it in a variable called C
.
In line 850 we subtract the value of the current cell, because we only want to count the number of living neighbors.
Finally, we apply the evolution algorithm: if the cell was alive (line 860) it stays alive only if it had 2 or 3 neighbors. Otherwise, it dies, either of boredom or of suffocation. Poor thing.
If the cell was dead (line 870) it becomes alive if it has three neighbors (I won’t comment on the practical implications of such number.) We set the values in the W
global variable, and we return to line 520, which is a GOTO
to line 350, to start the process all over again.
700 REM EVOLVE WORLD TO NEXT GENERATION
710 FOR I = 0 TO S
720 FOR J = 0 TO S
730 LET Z(I, J) = W(I, J)
740 LET W(I, J) = 0
750 NEXT J
760 NEXT I
770 FOR X = 0 TO S
780 FOR Y = 0 TO S
790 LET C = 0
800 FOR A = X - 1 TO X + 1
810 FOR B = Y - 1 TO Y + 1
820 IF A >= 0 AND B >= 0 AND A <= S AND B <= S THEN LET C = C + Z(A,B)
830 NEXT B
840 NEXT A
850 LET C = C - Z(X, Y)
860 IF Z(X, Y) = 1 AND (C = 2 OR C = 3) THEN LET W(X, Y) = 1
870 IF Z(X, Y) = 0 AND (C = 3) THEN LET W(X, Y) = 1
880 NEXT Y
890 NEXT X
900 RETURN
At the end of the program, we have the DATA
statements used to initialize the W
variable on top of the program, and a mandatory END
statement at the end, even if we never really reach it, thanks to the GOTO
in line 520.
It’s not hard to see the initial configuration of the grid through the ones scattered in the DATA
.
1000 REM INITIALIZATION DATA
1010 DATA 0, 1, 0, 0, 0, 0, 0, 0, 0
1020 DATA 0, 1, 0, 0, 0, 0, 1, 0, 0
1030 DATA 0, 1, 0, 0, 1, 0, 1, 0, 0
1040 DATA 0, 0, 0, 0, 0, 1, 1, 0, 0
1050 DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
1060 DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
1070 DATA 0, 0, 1, 0, 0, 0, 0, 0, 0
1080 DATA 0, 1, 0, 1, 0, 0, 0, 0, 0
1090 DATA 0, 0, 1, 0, 0, 0, 0, 0, 0
9999 END
Et voilà :)
Running it
This code runs verbatim (and compiles with FreeBASIC) with the following interpreters and compilers, all tested on Fedora 38.
- VICE - the Versatile Commodore Emulator
- Attach the datasette image
conway.tap
(menu “File / Attach datasette image…”) and typeLOAD
. Select theplay
command (click on the “Tape” word at the bottom of the emulator and select “Play”), and wait for the program to load. Then typeLIST
to see the code, and thenRUN
. PressESC
to stop. - You can also paste the code using the “ALT+F9” (to reset the emulator) and “ALT+INSERT” key combinations (to paste the code as if it were entered manually) but then you’ll need to manually change line 120 before running, as follows:
120 LET D$ = "B"
(uppercase B in PETSCII is a vertical bar) - Only tested on the Commodore 64 emulator of the suite.
- Attach the datasette image
- FreeBASIC 1.10.0
- Use the
fbc conway.bas -lang qb -x bin/conway
command to compile, and then runbin/conway
. CTRL+C to stop. - You will get an error if you forget the
-lang qb
argument! - Tested on FreeDOS and Fedora 38.
- Use the
- PC-BASIC
- Run the program with
pcbasic conway.bas
. CTRL+C to stop. Close the window to quit. - You can also just launch
pcbasic
, press F3, and loadconway.bas
. Press F1 toLIST
and F2 toRUN
. CTRL+C to stop.
- Run the program with
- Bywater BASIC Interpreter
- Run the program with
bwbasic conway.bas
. CTRL+C to stop. - You can also just launch
bwbasic
, typeLOAD
, and enterconway.bas
. TypeLIST
andRUN
. CTRL+C to stop. Typequit
to exit the program. Tested on FreeDOS and Fedora 38.
- Run the program with
- QB64
- Open the
conway.bas
file in the editor and pressF5
to compile and run.
- Open the
- Vintage BASIC
- Run the program with
vintbas conway.bas
. CTRL+C to stop.
- Run the program with
- Chipmunk Basic
- Run the program with
basic conway.bas
. CTRL+C to stop. Typequit
to exit the program.
- Run the program with
- SmallBASIC
- Download the AppImage file, and
chmod +x
it. Then run it at the console withconway.bas
as the only parameter. - Not to be confused with Small Basic, from Microsoft, which is not compatible with this project.
- Download the AppImage file, and
- Microsoft QBASIC on MS-DOS 5.0 and later.
I’ve also managed to run the code verbatim on some online emulators:
- Owlet BBC BASIC Editor
- Clicking on this link will load and start the program directly on the emulator.
- Otherwise, paste the code of
conway.bas
on the editor and press the “Play” button. To make the output more readable, remove the following lines used to print the coordinate system above and to the left of the grid:
380 IF A = 0 THEN GOSUB 600
390 PRINT A;
400 PRINT D$;
- Online AppleSoft BASIC in JavaScript
- Paste the code of
conway.bas
on the editor and press “Run”.
- Paste the code of
- Decimal BASIC
- Select the “Option / Syntax” menu and select “Obsolete Minimal BASIC”. Paste the code on the editor and hit the play button.
- 8bitworkshop IDE
- Paste the code of
conway.bas
on the editor and press the “Play” button.
- Paste the code of
- Quite BASIC
- It almost works off-the-box: you must change the comma for a
+
sign on line 500 before running, because Quite BASIC does not recognize commas onPRINT
statements.
- It almost works off-the-box: you must change the comma for a
It was a fun experience to discover the incredible community of users online that keep the memory of BASIC alive, providing emulators of all kinds, both online and offline. I was pleased to see the fervor and the love people put in these things, and it was a fantastic trip 40 years in the past.
More Conway in BASIC
With this implementation, there are now 5 6 different dialects of BASIC in the Conway project:
- Minimal BASIC, described in this page.
- QBasic, using
Sub
procedures instead ofGOSUB
, and getting rid of theGOTO
statement with a nicerDo… Loop
, but still withDATA
statements. - VBScript to be run on Windows 11 using the
cscript
command. - FreeBASIC using include files and other features of this compiler.
- VB.NET, probably the most “modern” version of all.
The last three ones are those that look more similar to other projects. In the first three cases, I had to adapt the code to the various constraints of those BASIC dialects.
Update, 2023-08-18: Added a sixth BASIC dialect with a version for Visual Basic 1.0, thanks to WinWorld offering downloads of MS-DOS 5.0, Windows 3.1, and Visual Basic 1.0, ready to be conveniently installed and run in a VirtualBox VM.