
We are going to look at a series of Boriel ZX Basic concepts aimed at programmers coming from the classic Sinclair ZX BASIC who want to start programming in Boriel ZX Basic. It is not possible to summarise or explain everything in a single article, nor is it something you can learn in two minutes, so in this text we will explain general concepts without going into excessive detail or advanced topics.
To follow the steps in this article, we will assume that you already have your development environment installed and configured and that you are familiar with the basic concepts. If this is not the case, I refer you to the first chapter of the Boriel video course, which you can find on YouTube.
To try out the examples, we will create a folder called borieltest inside C:\zxtools\projects\borieltest\, or inside whichever folder you have ZX Basic Studio installed in.
Once that folder has been created, we can open ZX Basic Studio and create a new project, selecting the folder we have just created.
When the project configuration window opens, we can set the Main file field to Main.bas and leave the rest of the parameters at their default values.
Next, we create a new file using the menu option File → New → File, select the Code category, the ZX Basic file type, and give it the name Main.bas.
Now we can type our code and press F5 to run it in the emulator.
But what does Boriel ZX Basic give me?
This is the first question programmers usually ask when they hear about Boriel Basic (Boriel from now on). And the answer is not simple—not because it is difficult to explain what it offers, but because it is a long answer:
- It compiles the code: Boriel is a compiler, which means that it converts our BASIC code into machine code. Sinclair ZX BASIC is an interpreted language, meaning that every command must be read, decoded and executed by the ROM each time it runs. Compiled code, on the other hand, runs much faster.
- It optimises the code: Once compiled, Boriel can optimise the code so that it runs even faster and uses less memory. For example, most mathematical operations are faster,
CIRCLEruns faster,PRINTflies, and so on. - It cleans the code: Code that is never executed is removed, reducing memory usage.
- It adds language improvements: Boriel adds features from modern languages such as C or Visual Basic, and even allows assembler code to be used and integrated seamlessly with BASIC.
- It allows code structuring: Structured code is easier to read and reuse in other projects.
- It breaks the assembler barrier: BASIC was slow and assembler was difficult. Boriel allows assembler to be integrated gradually and naturally.
- Support: The Boriel ZX Basic Telegram channel and forum are very active, and Boriel himself often takes part in discussions.
Differences between Sinclair ZX BASIC and Boriel ZX Basic
Boriel is around 95 % compatible with Sinclair ZX BASIC. Below are the main differences.
Line numbers
In Boriel, line numbers are optional:
10 BORDER 0: PAPER 0: INK 6: CLS
20 FOR N=0 TO 20
30 PRINT N
40 NEXT N
Becomes:
BORDER 0: PAPER 0: INK 6: CLS
FOR N=0 TO 20
PRINT N
NEXT N
Indentation makes the code easier to read.
Labels
Labels replace line numbers:
LET A=0
Loop:
LET A=A+1
PRINT AT 0,0;A
IF A<100 THEN GOTO Loop
PRINT "END"
Computed jumps
Boriel does not allow computed GOTOs like:
GOTO 1000+(variable*100)
Instead:
ON variable GOTO 1000,1100,1200
Array base
By default, arrays start at index 0 in Boriel:
DIM a(100)
This creates 101 elements (0–100). This also affects strings:
PRINT A$(1 TO 4)
Sinclair ZX BASIC: 1234 Boriel ZX Basic: 2345
This can be changed with compiler switches, but using base 0 is recommended.
String variables without $
$ is no longer required:
a="Hello"
A and A$ refer to the same variable.
Default commands
Some Sinclair commands (INPUT, ATTR, SCREEN$, etc.) require the --sinclair option.
Extended variable types
Sinclair ZX BASIC only supports floating-point numbers (5 bytes, slow). Boriel adds multiple integer and fixed-point types:
| Type | Size (bytes) | Range | Description |
|---|---|---|---|
| Byte | 1 | -128…127 | Signed 8‑bit |
| UByte | 1 | 0…255 | Unsigned 8‑bit |
| Integer | 2 | -32768…32767 | Signed 16‑bit |
| UInteger | 2 | 0…65535 | Unsigned 16‑bit |
| Long | 4 | ±2,147,483,647 | Signed 32‑bit |
| ULong | 4 | 0…4,294,967,295 | Unsigned 32‑bit |
Decimal types:
| Type | Size (bytes) | Range | Description |
|---|---|---|---|
| Fixed | 4 | -32767.9999847 .. 32767.9999847 | 16 fixed‑point |
| Float | 5 | Pierde precisión con muchos decimales o con números muy grandes | 5Sinclair floating‑point |
Text strings do not have a fixed size; they occupy 2 bytes plus the text content. Those two extra bytes contain the current size of the text. Text variables are defined using the “string” keyword.
Boriel allows you to define the type of variables you wish to use, which helps to speed up operations performed on these variables. You can define variables as follows:
DIM shortNumber AS BYTE
DIM unsignedShortNumber AS UBYTE
DIM integerNumber1, integerNumber2 AS UINTEGER
DIM textString AS STRING
DIM integerArray(100) AS UINTEGER
DIM bidimensionalArray(10,5) AS UBYTE
Bear in mind that the value specified when declaring an array indicates the number of elements plus one, as indices start at 0. In short, 100 means it has 101 elements, from 0 to 100.
It is recommended that you use the smallest possible variable type, but you must take great care not to exceed the maximum and minimum values.
SUB routines
Instead of GOSUB:
PrintAt(10,5,"Hello")
SUB PrintAt(Y AS UBYTE, X AS UBYTE, Text AS STRING)
PRINT AT Y,X;Text
END SUB
Local variables only exist inside the SUB.
FUNCTION
A function returns a value:
PRINT Add(10,3)
FUNCTION Add(A AS INTEGER, B AS INTEGER) AS INTEGER
RETURN A+B
END FUNCTION
Assembler integration
Assembler is embedded using ASM … END ASM:
FUNCTION GetReg(ByVal NextRegister AS UBYTE) AS UBYTE
ASM
out (c),a
in a,(c)
END ASM
END FUNCTION
Data can also be defined with DB, avoiding DATA, READ and POKE.
Libraries
Built‑in libraries are included with angle brackets:
#INCLUDE <keys.bas>
External libraries use quotes:
#INCLUDE "MyLibrary.bas"
WHILE…WEND
Cleaner looping without GOTO:
WHILE INKEY$=""
PRINT A
A=A+1
WEND
Break early:
BREAK WHILE
DO…LOOP
More flexible loops:
DO
PRINT "Until a key is pressed"
LOOP WHILE INKEY$=""
There are many more features, but this article is intended only as a brief introduction to Boriel ZX Basic.
Acknowledgements
Some examples are taken from NextBuild by David Saphier (em00K). Thanks to José Rodríguez (Boriel) for his help and for creating Boriel ZX Basic. Main image designed by vectorpouch / Freepik.