Compiling BASIC with ZX Basic
ZX Basic is a BASIC compiler for the ZX
Spectrum, by José Rodríguez (Boriel), widely used to make games. There is a
version with a ZX-V target that turns a BASIC program into a native program
(.zxv), like the C ones of the SDK: you load it with
LOAD "name" and it runs about 90 times faster than the same program in the
machine's BASIC.
It is not an official version of ZX Basic: it lives in a fork, cronomantic/zxbasic, based on version 1.19.0.
Which BASIC it is
ZX Basic is not ZX-V's BASIC. It is the Spectrum's BASIC with additions
from modern BASICs: types (UByte, Integer, Long, Float, String...),
SUB and FUNCTION with local variables, DO ... LOOP, #include, and no
line numbers needed. A Spectrum program usually compiles as it is: PRINT AT,
INK, PLOT, DRAW, CIRCLE, BEEP, INKEY$, PEEK and POKE work as
there, also with the addresses of the screen (16384) and of the system
variables.
What is only in ZX-V (the modes, the two pages, sprites, shapes, the joystick
and the AY chips) is in the zxv.bas library, with the same names as in ZX-V's
BASIC:
#include <zxv.bas>
GRABSTR 0, 4, "1111122112211111"
FOR x = 0 TO 240 STEP 16
SPRITE 0, x, 80
NEXT x
BOX 4, 100, 20, 20, 20
| Commands | |
|---|---|
MODE, PAGE, SCREEN, SCROLL, REMAP, WAIT |
The modes, the page shown and the one drawn on (double buffering), scrolling, colour changes and a steady 60 frames per second |
PALETTE, PALETTERESET |
The palette: any colour (red, green and blue from 0 to 255) for each of the 16, and the standard ones again |
GRAB, GRABSTR, SPRITE, HIT |
Sprites, mirrored, recoloured or scaled, and pixel-exact collisions |
BOX, DISC, POLY, CLIP, PIXEL |
Filled shapes, clipping and the colour of a pixel |
KEY, JOY, SOUND, TIME |
Keys held down, joysticks, the AY registers and the frame counter |
MUSICLOAD, MUSICPLAY, MUSICSTOP, MUSICPAUSE, MUSICCONTINUE, MUSIC |
Music from a tracker (a .pt3 from zxv-tracker...), in the background while the program goes on |
SFXLOAD, SFX, SFXSTOP, SFXPLAYING |
Sound effects from an AYFX bank |
Where it differs from ZX-V's BASIC: the sprite of a string is GRABSTR
(GRAB n, width, a$ in ZX-V's); KEY, JOY, HIT and PIXEL take
parentheses (KEY(9)); REMAP and SOUND take one pair at a time and
PALETTE one colour, only in table 0 (no #t); REMAP and PALETTE alone
are REMAPRESET and PALETTERESET; POLY takes up to 8 points; and music and effects are loaded with MUSICLOAD f$ and
SFXLOAD f$ (LOAD f$ MUSIC and SFX in ZX-V's), played with MUSICPLAY,
MUSICSTOP... and the function that says whether an effect plays is
SFXPLAYING.
SAVE, LOAD and VERIFY use the SD card as ZX-V's BASIC does: without an
extension, the screen goes in a .gfx (the ZX-V one, with its colours if they
are not the standard ones) and CODE and DATA in a .bin, just the bytes; with the extension .tap, in a Spectrum tape. Compiled with
--enable-break, Esc stops the program and errors say the line. The rest, and
the libraries there are, is in its
target documentation.
Installing it
On Windows: download zxbasic-1.19.0-zxv.5-win64.zip from the
release page
and unzip it anywhere. It has zxbc.exe, which does not need Python.
On Linux and macOS: download the source code from that same page (or
clone the fork's zxv branch). You need Python 3.14; the compiler is
zxbc.py.
Also, on all of them: the RISC-V C compiler, the same one the SDK uses. How to install it is in the SDK guide.
Your first program
Save this as hello.bas:
DIM x AS FLOAT
PAPER 1: INK 7: CLS
PRINT AT 10, 6; "Hello from ZX Basic"
FOR x = 0 TO 255
PLOT x, 40 + 30 * SIN(x / 20)
NEXT x
Compile it:
zxbc --arch=zxv hello.bas
(zxbc.exe on Windows, python3 zxbc.py with the source code.) That gives
hello.zxv: copy it to the SD card (in the emulator, to the folder that acts
as the card) and load it with LOAD "hello". When it ends, its report (0 OK
or the error) stays at the bottom until you press a key, as on the Spectrum.
With --loading-screen title.png (or a .gfx with a screen), the .zxv
carries a loading screen, as elf2zxv.py --screen does. The machine shows it
while it loads the program.
Mind the types. Without DIM, a variable takes the smallest type that fits
it: here, without the first line, x would be a UByte (0 to 255). And
between integers, / divides as integers: x / 20 would give 0, 0, 0... 1, 1,
1, and the curve would come out in steps. And the FOR would never end,
because a UByte never gets past 255. Declare as FLOAT whatever needs
decimals, and with a bigger type the counters that reach the limit.
An example
Stardust is a small game made
with zxv.bas: double buffering, sprites from strings, collisions, keyboard
or joystick and sound, at 60 frames per second.
The licence of your programs
The compiler is AGPL 3, but what goes inside your programs (ZX Basic's runtime and libraries, and the parts of the ZX-V SDK) is MIT: your programs can have any licence you like, closed ones too.