ZX-Basicus is a PC console program written in C++ that synthesizes, analyzes, optimizes and runs directly ZX Spectrum 48K Sinclair BASIC programs (it also does some management of .TAP, .TZX, .TAB and .SNA files). Under the hood, it uses a formal grammar specification that is extremely respectful of the original language, and also the ZXEcosystem library for interpreting programs with the original ZX Spectrum 48K peripherals.
The program is easy to use. All its options can be shown simply by calling it with --help, or you can consult the help for only one option with --help <opt-name-without-first-dash>. If you are more visual, you may use the integrated graphical option selector, that is opened if you set no option at all, although that is only visual sugar for the specification of parameters (it is not an IDE -yet-) and we strongly recommend the console mode.
More detailed description of the toolkit are in the sections below (the file management is really straightforward and therefore omitted; just look for option -f in the help, or write zxbasicus --help f in console).
Over the years I have also written other software related to the ZX Spectrum that might interest you. In particular, if you wish to program the PC with ZX behaviour in modern C++ instead of the original Sinclair BASIC, you can use the ZXEcosystem library.
As an analyzer, ZX-Basicus is maybe the most intelligent tool out there; it extracts information from the BASIC program contained in a file and perform a number of automated analyses on that. It can work with:
In all cases, it allows the user to inspect the internals of existing programs, obtain help for coding efficiently, and also test compliance with the original ZX Sinclair BASIC for 48K. Moreover: it is able to detect diverse obfuscation techniques and clean the code in the case of binary programs (.sna and .tap).
The general syntax for this tool is:
zxbasicus -a [options] -i bas-tap-tab-or-sna-fileThe options are:
--obfuscated. If the input file is not binary, this option is ignored. Otherwise, the analyzer tries to fix the following obfuscation tricks and recover a clean BASIC code:
As a synthesizer, ZX-Basicus creates, from a plain text file containing a BASIC program source, a tape file (in .tap format) that can be loaded by any emulator. With this you can write your own BASIC programs in any code editor you like (which will certainly be more comfortable than writing them directly in an emulator of the original ZX!).
Again, the syntax is straightforward:
zxbasicus -s [--line 10] [--progname NAME] [--obfsnum] -i bas-file -o tap-filewhere the
--lineoptional parameter indicates the starting execution line when the file is loaded into an emulator,
--prognameprovides a name for the header of the tap file, and
--obfsnumsubstitutes all number literals by "1" to save space, keeping the hidden numbers valid; it also substitutes the negation of numbers by "1", storing the already negated number in the hidden value, which saves the ZX BASIC interpreter one mathematical operation in runtime.
The synthesizer is also able to generate some helper code automatically for your BASIC programs. In particular:
--defadd defline:actline:memloc. Add to the input BASIC file two subroutines to do the DEFADD trick for fast memory copies, and then save the resulting BASIC source code. (The DEFADD trick needs a hardware emulator for execution; the resulting program will not work well in the ZX-Basicus interpreter). The parameter
memlocindicates the location in RAM where the 18 bytes needed for the trick will be stored, the parameter
deflineis the BASIC line where the first subroutine, in charge of installing those bytes, will be placed, and
actlinewill be the line where the second subroutine, that makes a fast copy of
Lbytes of memory from address
Oto address
D, will be placed. Both lines must be beyond the last line existing in the input program.
--comprscr firstrow:lastrow. Read a ZX entire screen from the input file and compresses it for BASIC usage in a "Wudang" style (the pure BASIC game that won the bytemaniacos 2020 contest). Concretely, it synthesizes a .tap file that contains two elements: a code element with the screen data in a compressed format, based on a possibly new character set, and a BASIC program that loads and shows the compressed screen just using PRINT (you have to run the program in a hardware emulator to work, not in ZX-Basicus, since it uses the DEFADD trick). The part of the original screen that is processed is the one within character rows
firstrow:lastrow(both rows included). The code element within the .tap result is a binary compression of the screen with the following format:
0..8a fake DEFADD to hold a pointer to the compressed screen and be used consequently as a string variable P$ that can be printed.
9+the new charset for the compressed screen, if it needs one.
9+the content of the compressed screen, including chars and color controls.
ZX-Basicus contains a number of tools that transform BASIC source files. The main goal is increasing the execution efficiency in a ZX Spectrum, but some transformations are useful for other things, such as publishing BASIC code in web pages. The optimizing tools are inspired in the way the ZX ROM interpreter works and what it needs to execute faster.
You can find complete and detailed explanations of the optimizations in these posts.
The general syntax for these tools is:
zxbasicus -t [--tool [parameter]] -i bas-file -o resulting-bas-fileThe tools are:
--delemptyDelete empty statements.
--delrem [parm]. Delete all REM statements which comments do not begin with any of the letters included in the string parameter, or all the REM statements if that paramter is 'all'.
--subscts [w:preff][
--subs1varin v1.*.* versions]. Substitute all uses of a variable when it is assigned only once to a literal value (either string or number) in a LET statement; take the literal value of that assignment for the substitutions. In other words, consider that variable as a constant. If
[w:preff]is provided,
wmust be either
*(to detect all constants, strings and numbers),
n(to detect only numeric constants) or
s(to detect only string constants), while
preff, that could be empty, is a preffix string that all constant variables should have at their names to be considered in this optimization. If the parameter is missing, it is taken as
*:(strings + numbers, no preffix).
--summexprs. Reduce all expressions that can be partially or entirely evaluated before runtime.
--delunreach. Delete all statements that cannot be reached in the execution flow of the program: those after GOTO, RUN or NEW, but also those IF that have an always-false or always-true condition (in the former case, all statements of the IF body are deleted, not only the IF).
--delunusedv. Delete all statements that assign/write to a variable that is unused in the program.
--delunusedfn. Delete all statements that define a function (DEFFN) which is never used in the program.
--shortenv. Shorten all scalar variable names that are longer than one character, as long and as much as it can.
--mergelines. Merge into one all contiguous lines that it can without affecting the control flow of the program, therefore making lines longer and fewer. It also takes care of not affecting the speed of the program: for instance, FOR statements that are at the beginning of their lines are not merged up with previous line, since that would produce slower iterations (the FOR statement is to be searched by the interpreter within the line after each NEXT).
--alloptim. Do the previous optimization options in the pre-defined sequence shown above.
--valtrick firstline:lastline. Substitute automatically isolated numeric literals by the VAL trick or similar in the program section enclosed within [firstline,lastline].
--move parm. Move, if possible, a given set of lines to another place, renumbering them. It must be followed by an argument with the format
nnnn:mmmm:dddd:iiii, where
nnnnis the first line to move,
mmmmthe last line to move,
ddddthe first destination line, and
iiiithe line increment to use after moving for renumbering the moved lines.
--strlitwchr. Change all non-standard ASCII chars in string literals into calls to the CHR$() function.
--HTML parm. With a parameter that can be either
darkor
light, produce source code in HTML format for dark or light backgrounds, respectively.
Below you can see an example of the optimizations done on the file "test_alloptim.bas" included in the ZX-Basicus download; a more detailed video is also available.
As an interpreter, ZX-Basicus runs ZX Spectrum 48K BASIC programs from their source text form (plain ASCII files, both .BAS and .TAB), not by emulating the internal hardware of the ZX, but by directly interpreting each statement and expression at PC speed!
These programs can be designed to be synthesized for later execution in a real ZX or in a ZX hardware emulator, using ZX-Basicus as a development aid that is more convenient than the original machine, or they can, from the beginning, be intended for the PC, becoming then more powerful than the original ZX BASIC software both in speed and memory space: they are not limited either in code size (although for compatibility reasons they can reach only to 32767 lines and have up to 127 statements per line) or in the number of variables, none of these residing in the 64KB memory space of the running environment. The ROM is not there either (USRs calls do not work), its place becoming just another part of the RAM; the 64KB RAM serves then to hold the screen, a few system vars (again for the sake of compatibility, because it is really common that BASIC programs use them through POKE/PEEK), UDGs and fonts, and to be POKEd with any raw data the user program needs to store.
The interpreter has a built-in console debugger that can aid in developing pure BASIC programs, with the usual debugging options (step-by-step execution, variable inspection, expression watch, breakpoints, etc.).
The syntax of the interpreter is like this:
zxbasicus -r [options] -i bas-fileThe available options are:
-im parm. The
parmis either
micoor
full(default). The interpreter uses the ZXEcosystem library for executing programs with full-fledge original ZX Spectrum sound, screen and keyboard in
fullmode, but you can choose to execute them in a much simpler black/white, mute PC console if you wish (that would be a sort of retro-scripting language for the PC!) with
micomode.
-fm parm. Mode of file management when executing file operations (LOAD, SAVE, etc.). It is either
raw(binary mode),
tap:<filename>(tape mode) or
TAP:<filename>(also tape mode, but forcing to call the tape manager at each tape operation). In binary mode, each tape operation will work on an individual disk file provided in the name argument of the operation (that does not have the limitations of ZX BASIC file name arguments) and with the data in raw, binary format; in tape mode, the provided filename will correspond to a .tap file where all operations will occur using the .tap format.
--delay parm. Delay
parmmilliseconds after executing each BASIC statement; if 0 (default), no delay. If running a program originally written for a ZX, this can be useful to reduce the execution speed (usually, delays of 5-10 millis are enough), and also for not overloading the CPU time in your computer: if your program does no pauses, it will tend to consume all the available CPU power!
--line parm. The
parmis the number of the line where the program starts (default is 0).
--profile parm. If speficied, dump to the file given by
parmthe results of profiling the execution.
--debug. If specified, the interpreter will enable debug mode.
--ign-usrn. If specified, any machine code call (USR n) will be executed without effect. Otherwise, they stop the interpreter.
--randomize. If specified, the interpreter will fill the seed of the randomize engine of the ZX with a random number at starting. Otherwise, its default original value is used.
--stopatend. If specified, the interpreter will pause after the program stops without closing the window until the user press a key.
--silent. To prevent any output in console except the one the BASIC program may produce by using the stream #3 (printer, see below).
The ZX-Basicus interpreter can execute old programs, or you can write your own.
The interpreter has been throughly tested with many programs of the 80s but also with programs taken from the BASIC Jam 2017 contest, some of the Spanish "bytemaniacos" contests, and several books preserved at proyectoBasicZX.
The particularities of the interpreter in full mode are the following:
-fm tap:*,
-fm TAP:*or when running a .tab file- if pressing F3.
On the other hand, the particularities when using the simpler mico mode are:
The development history of ZX-Basicus started in Spring 2019 (v1.0.0). It is listed below along with some downloads:
ZX-Basicus has been developed by Juan-Antonio Fernández-Madrigal. If you are interested in contacting me, you can use "software" (remove quotes) at jafma.net.