BASIC8 - The Fantasy Computer/Console!

View on GitHub

Welcome to BASIC8

Click to read this manual online; or read about Workshop.

BASIC8 is an integrated fantasy computer for game and other program development. You can create, share and play disks in a BASIC dialect, with retro 8-bit style flavour, and built-in tools for editing sprite, tiles, map, quantized, etc.

It’s encouraged to share your creativity. All disks are shared under the CC-BY license as default, if you had not chosen other license explicitly. Every supported code, sprite, map and other data are viewable and editable to other users, it is recommended to learn and communicate with each other, also allowed to derive from other users’ work or even collaborate together. The only community guideline is just be polite and respectful to other users and their work too.

You could read this manual in any order as you wish, and get back to lookup for explanations at any time.

Table of content


Part I. Fundamental

Getting started

It’s been a while since we used to enjoy coding and playing straightforward after a computer bootup. The goal of BASIC8 is bringing a sense of joy back from retro/vintage computing and gaming, also being quick at getting higher level stuff done for contemporary development for everyone. It’s focusing to stay at a balance point of simplicity, creativity, fun and usability.


Most home computers from 1970-80s use keyboard as the only input method for users, other than widespread mouse and touch nowadays. Disks are supposed to be played using keyboard, mouse/touch and gamepad. Mouse is mainly used in BASIC8 to operate on the GUI of editors; while keyboard is mainly used to write code (since it’s text based), and do some shortcuts:

It refers to the first icon item on the main menu bar, when speaking of clicking [Head] in this document:


The limitations of BASIC8 are carefully chosen to make programs way easy and simple:

Disk library

All disks are stored in a library directory on hard drive, each disk has its own sub directory for all code, assets and data. The default root path of library is:


There are some configurable options available:


A big part of retro computing which makes it fun to work with is sharing and discussing user generated contents on magazines and other networks. It is convenient to export disk and record frames in BASIC8 for sharing.

Exporter and importer

To export a disk, select it on the main library screen, click [Head], Export, then choose one of Save "*.b8" file, Save "*.png" file, and Copy text based; or right click on a disk, then click Export to open the dialog box. It packs all code, assets and customized data (*.data) in the content directory of a disk; but doesn’t deal with sub directories.

To import a disk, click [Head], Import, then choose one of From a "*.b8" file, From a "*.png" file, and From text based; you should have copied text based disk onto clipboard already, before using the third option. Use option Overwrite (for confliction) to replace any old disk with importing, otherwise use Keep both; BASIC8 uses the UID (Unique IDentifier) of disks to identify them.


Standalone “.b8” files are standard “zip” packages, you could use them for the purpose of archiving and attaching. Standalone “.png” files are encoded disks in pixels. Text based disks are generated with ASCII characters, which are friendlier when posting to places with text allowed only. Besides, you can share your disks via Workshop with built-in tools.

Recording GIF

Click Disk, Record frames, or just press F8 while playing to record a sequence of frames for further GIF exporting, click Disk, Stop recording, or press F8 again to stop recording.


Part II. Syntax

Basic principles

BASIC, it was almost the only thing a user could get with vintage home computers. That’s deeply impressive to see how challenging it was when constrained functionalities put stress on hobbyists, and how intelligent enthusiasts could be. BASIC8 implements a BASIC dialect with retro simplicity, and tackles a lot of aspects of modern concepts. You will get how to program in BASIC8 in this part. In this document, BASIC8 stands for either the fantasy computer or the language it offers, according to where it appears.

Identifiers and keywords are case-insensitive, but it stores what exactly you typed within strings. All keywords and functions in this document are in upper case, to distinguish from other literal words.

Variable and data types

BASIC8 is a dynamic programming language, therefore variables don’t have types, but values do. The built-in types are nil, numbers, string, type, array, collections, iterators, class, routine, lambda, coroutine; besides, BASIC8 offers some data structures as libraries which will be explained later.

Nil is a special type, the only valid value is NIL, a.k.a. NULL, NONE or NOTHING.

A variable identifier is formed with letters, numbers, underline and an optional dollar postfix, but it must begin with a letter or an underline. It’s not accepted to use type specifier for variable, and you don’t need to declare it before accessing it neither. Assignment statements begin with optional LET keywords, eg. LET pie = 22 / 7, s = pie * r * r, etc.

The dollar sigil $ is reserved from traditional BASIC dialects as a valid postfix of a variable identifier. Representing for different identifiers respectively with or without it. But it doesn’t denote for type of string in most cases in BASIC8:

A$ = 1 ' Can store any type with a "$" decorated variable.
B$ = 2
PRINT A$ + B$;

A = "Hello " ' Can store string without "$".
B = "World"

However, there are special cases that $ does mean something with the DIM and INPUT statements.

Only 0, FALSE, NIL and JSON_BOOL(FALSE) result in “false” within boolean expressions; non-zero numbers, TRUE and all other values, including empty string "" result in “true” in BASIC8.


There are two types of number in BASIC8, integer and real (float point). Integer is implemented as 32-bit signed, with range of values from -2,147,483,648 to 2,147,483,647. Real is implemented as single precision float point of IEEE 754 standard. An arithmetic expression with real number might result an integer, if the result value doesn’t have a fractional part, eg. 1.5 / 0.5 results in 3 rather than 3.0.

An octal integer begins with a 0 prefix, a hexadecimal begins with a 0x, eg. 010 (oct) equals to 8 (dec), 0x10 (hex) equals to 16 (dec).


A string begins and ends with a pair of double quotation marks "", it stores what exactly you typed as a sequence of characters. Strings can be used to represent sentences, descriptions, or any other text based data.


A comment is a user readable explanation or annotation in source code. They are added with the purpose of making source code easier for humans to understand, and are ignored by compilers and interpreters. BASIC8 supports traditional single line comment begins with the REM statement, which stands for “REMark”, and single quotation mark ' as a shortcut. All text from a comment mark till the end of line are ignored. Besides, BASIC8 supports a form of multiline comment, begins with '[ and ends with '], all text between are ignored for computing.

Arithmetic and relational operations

These operators are used for arithmetic calculations:

These operators are used for relational comparisons and calculations:

Logical truth table:

F T F T -
F F F F -

An expression is evaluated from left to right, with top down priorities:

Level Operation
1 ( )
2 - (negative), NOT
3 ^
4 *, /, MOD
5 +, - (minus)
6 <, >, <=, >=, <>, = (equal comparison)
8 = (assignment)

Other numeric functions will be mentioned at “Math functions”.

Bitwise operations

String operations

It generates new string objects, with memory allocation, when concatenating strings.

Comparison operators can also be applied to strings. It starts comparing the first characters of both strings, if they equal to each other, it continues checking the following ones until a difference occurs or reaching the end of any string.

Other string functions will be mentioned at “String functions”.


BASIC8 supports array up to 4 dimensions. An array can store a set of data that each element can be accessed by the array name and indices. An array can hold either real number or string data, depends on whether the identifier ends up with a $ sigil. An array must be defined with a DIM statement before using it:

DIM nums(10)
DIM strs$(2, 5)

The common naming rule for array identifiers is the same as naming variables, actually all user identifiers in BASIC8 do the same. It’s defined by a DIM keyword followed with an array identifier, begins with an open bracket and ends with a close bracket. Dimensions are separated by commas. Array index begins from zero in BASIC8, therefore nums(0) is the first element of array nums, it’s a little different from other BASIC dialects, but more common in most modern programming languages. An array index can be a non-negative integer value from a constant, a variable or an expression that results in an integer; invalid index will cause an error.

Control structures

Normal statements execute line by line. However, you can do more than that with some workflow control structures.


A conditional structure is used to execute particular code block depending on conditions, with IF/THEN/ELSEIF/ELSE/ENDIF statements.

It supports to write conditional IF statements in two ways. One is single line which the whole conditional block is written in a compact form:


The other way is multiline:

IF n = 1 THEN
	PRINT "One";
	PRINT "Two";
	PRINT "Three";
	PRINT "More than that";


The FOR/TO/STEP/NEXT statements are used to loop through certain steps:

FOR i = 1 TO 10 STEP 1

The STEP 1 part is optional if the increment step is 1. The loop variable after NEXT is also optional if it is associated with a corresponding FOR.

A variant form of FOR/IN/NEXT is used to iterate collections and other iterable structures, which will be mentioned later.

The WHILE/WEND, DO/UNTIL statements are used when the number of loop steps is uncertain. WHILE/WEND checks a loop condition before executing a loop body; while DO/UNTIL executes a loop body before checking a condition.

It keeps looping when a condition is true with WHILE/WEND:

a = 1
WHILE a <= 10
	a = a + 1

It keeps looping until a condition is true with DO/UNTIL:

a = 1
	a = a + 1
UNTIL a > 10


The EXIT statement is used to discontinue a loop.

A labeled target constructs of a label identifier and a following colon as label:. BASIC8 supports a GOTO statement which performs unconditional control transfer. You can call it as GOTO label, to transfer the execution flow to label: at next step, rather than the following line of the GOTO.

GOSUB is similar to GOTO, but it can be returned by a RETURN statement.

Advanced syntax


BASIC8 supplies generic list and dictionary collections, which accept almost all types of data; and associated manipulation functions for creation, accessing, iteration, etc. as following:

It’s also supported to apply some other generic function to collections:


l = LIST(1, 2, 3, 4)
SET(l, 1, "B")
PRINT EXISTS(l, 2); POP(l); BACK(l); LEN(l);

d = DICT(1, "One", 2, "Two")
SET(d, 3, "Three")

BASIC8 supports accessing elements directly in a list or dictionary using brackets:

l = LIST(1, 2, 3, 4)
d = DICT()
d(1) = l(2)
PRINT d(1);

List index begins from zero too, as how array does in BASIC8.


List iterator and dictionary iterator are used to walk through all elements in collections.

Iterate a list with an explicit movement pattern:

l = LIST(1, 1, 2, 3, 5, 8)

A ranged iteration, increasing by 1, with a FOR loop:


A ranged iteration, decreasing by 1, with a FOR loop:


Use the GET statement to get the key of a dictionary iterator, and VAL to get the value:

d = DICT("One", 1, "Two", 2, "Three", 3)
	PRINT GET(i), ", ", VAL(i);

Iterate the previous dictionary d with a FOR loop, while the iteration variable represents for each key:

FOR i IN d
	PRINT i, ", ", d(i);

Sub routine

A sub routine, a.k.a. “routine”, “sub”, “function”, or “procedure”, is often supposed to be a good abstraction of common and reusable code blocks. A sub routine in BASIC8 begins with DEF and ends with ENDDEF, it will be hereinafter described as “routine” for short. A routine name follows after DEF, then with a list of parameters clamped with a pair of brackets. Call a routine by its name, with a list of arguments clamped with a pair of brackets. It requires an extra CALL statement when a routine is declared below the invocation. The RETURN statement is also used to end a routine, with an optional result value to its caller.

Each routine has its own scope, from where statements in that routine body look up for variables preferentially, before looking into outer scopes.


DEF foo(a, b)
	c = CALL bar(a, b)
DEF bar(a, b)
	RETURN a + b
PRINT foo(1, 2);

It’s also possible to use the CALL statement with brackets clamped routine name to get an invokable object, instead of calling it instantly:

c = CALL(bar)
c(a, b)

Keep in mind the difference between CALL bar(...) and CALL(bar).

But, there’s a limitation that it’s not accepted to use mixed routines with DEF/ENDDEF and jumps with GOTO/GOSUB together in one program.


An invokable is supposed to receive indefinite arity sometimes. BASIC8 uses the variadic symbol ... for both parameter list definition, and argument unpacking. It refers to “more arguments” can be passed as a parameter list; or refers to all remaining arguments in the body of a routine. Eg:

DEF foo(a, b, ...)
	RETURN a + " " + b + " " + ... + ...
DEF bar(...)
	RETURN foo(...)
PRINT bar("Variadic", "argument", "list", "...");

Use the LEN statement to tell the count of remaining arguments. To iterate all arguments, eg:

l = LEN(...)
FOR i = 1 TO l
	s = s + ...


	s = s + ...


t = 0
	s = s + t
	t = ...

Triple dots are also used to denote that a library function accepts more than one arity in this document.


A lambda abstraction, a.k.a. “anonymous function” or “function literal”, is a function definition that is not bound to an identifier. Lambda functions are often:

A Lambda becomes a closure after it captured some values from an outer scope.

BASIC8 offers full support for lambda, including invokable as a value, higher order function, closure, currying, etc.

It begins with a LAMBDA keyword, follows with a parameter list, with none or multiple parameter identifiers; and then a lambda body, that is also clamped with a pair of brackets. It’s able to write multiline statements in a lambda body. Use the RETURN statement to return a value and exit from a lambda execution as well. The tilde symbol ~ is short for LAMBDA.


DEF counter()
	c = 0
		c = c + n
		PRINT c;
acc = counter()


BASIC8 supports a prototype-based programming paradigm which is a kind of OOP (Object-Oriented Programming). They all mean the same thing by “class instance” or “prototype” in BASIC8. This programming paradigm is also known as “prototypal”, “prototype-oriented”, “classless”, or “instance-based” programming. Use a pair of CLASS/ENDCLASS statements to define a class (a prototype object). Use VAR to declare a member variable in a class. It’s possible to define member function, a.k.a. “method” of a class with the DEF/ENDDEF statements as well. Write another prototype surrounding with a pair of brackets after a declaration statement to inherit from it (or say using it as a meta class). Use NEW to instantiate an instance of a prototype.

See the following example of class:

	VAR a = 1
	DEF fun(b)
		RETURN a + b
CLASS bar(foo)
	VAR a = 2
inst = NEW(bar)
PRINT inst.fun(3);

The ME keyword always stands for the current class instance. A.k.a. SELF or THIS in other languages.

The REFLECT statement is used to traverse all member variables and routines in a class instance. It returns a dictionary filled by member names and their values as key-value pairs.

The generic GET statement can be also applied to a class instance to get a member of it. It results in the value of a variable or the invokable object of a routine:

PRINT GET(foo, "A");   ' Results in the value of "A".
PRINT GET(foo, "FUN"); ' Results in the invokable object.

The SET statement can be applied to a class instance to set the value of a field variable:

SET(foo, "A", 42)
PRINT GET(foo, "A");


The TYPE statement is used to tell the type of a value as PRINT TYPE(22 / 7);; or get a “type” object with specific type name as TYPE("NUMBER"). It returns a “type” typed value; pass a type value to the STR statement to get the type name string.

The IS operator is used to check whether a value is an instance of a certain type, or if an object is an instance of a class, eg. 1 IS TYPE("NUMBER"), "Hello" IS TYPE("STRING"), inst IS foo, etc.

Besides, there is also a TYPEOF function, which gets the type of a non-referenced library value.

Importing another file

The IMPORT statement is used to import another source file to help organize long programs:

IMPORT "directory/file_name.bas"

It could be placed in outermost scope only without conditional or other structured trunks. All source files share a same global scope.


A coroutine is a special data type in BASIC8 encapsulating an invokable object. It’s a programming component that generalizes subroutines for non-preemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations. It obtains the execution flow when iterating on it, then keeps executing until all invokable statements finished or it hands over the flow proactively. Besides, there is also an automatically dispatched mode.


	LAMBDA (x, y)
		PRINT x / y;
		FOR i IN LIST(1 TO 3)
			YIELD i
		YIELD LAMBDA () (PRINT "I = " + STR(i);)
	22, 7
FOR t IN co
		PRINT t;
		PRINT t;
		PRINT "List";

Automatic memory management

BASIC8 automatically manages memory with GC (Garbage Collection). Thus you don’t need to explicitly release memory no longer in use. However, consider setting referenced variables to NIL when the values become useless, eg. closing databases, finished files, network connections, etc.

Generic functions


Part III. Game driver


The coordinate definition in BASIC8 is:

Program execution and rendering run on different threads with respective frame rates in BASIC8, program at 30 FPS and rendering at 60. Some properties of graphics commands, such as positions, rotations, etc. can be interpolated between the current and previous logic frames for rendering, see SET_INTERPOLATOR for details. Render queue can be ordered by rules:

These functions are used to communicate with a driver:

UPDATE_WITH returns when r(...) has just returned non-zero. Eg:

m = 1
DEF update(delta)
	t = t + delta
	IF t > 5 THEN
PRINT "Done with: ", n;

This UPDATE_WITH will return in five seconds.

These functions are used to create, load and extract graphics objects and values:

These functions are used to manipulate the states of a sprite:



The beginning index of sprite frame is 1.

A rotation can be either specified by simple angle as real number, or by VEC3(center_x, center_y, degrees); the former rotates around the center of a sprite, while the latter rotates relatively to ratio x, y, with range of values usually from 0.0 to 1.0. This rule also applies to functions for map and quantized image.


The beginning index of map layer is 0. Moreover, layer 0 is for the purpose of logic marking, with range of values from 0 to 15; layer 1, 2, and 3 are for rendering, with range of values from 0 to 239. Except for map generated by the LOAD_BLANK function, all layers (including layer 0) are renderable.



Mouse and touch

The TOUCH statement takes an index of pressing, and assigns all result values to following variables.


A virtual gamepad has 6 buttons, each button may be binded with a key on keyboard, or a button/trigger on a game controller.




The tones are indicated by letters A through G. Accidentals are indicated with a + or # (for sharp) or - (for flat) immediately after the note letter. See this example:

PLAY "C C# C C#"

Whitespaces are ignored inside the string expression. There are also codes that set the duration, octave and tempo. They are all case-insensitive. PLAY executes the commands or notes the order in which they appear in the string. Any indicators that change the properties are effective for the notes following that indicator.

Ln     Sets the duration (length) of the notes. The variable n does not indicate an actual duration
       amount but rather a note type; L1 - whole note, L2 - half note, L4 - quarter note, etc.
       (L8, L16, L32, L64, ...). By default, n = 4.
       For triplets and quintets, use L3, L6, L12, ... and L5, L10, L20, ... series respectively.
       The shorthand notation of length is also provided for a note. Eg. "L4 CDE L8 FG L4 AB"
       can be shortened to "L4 CDE F8G8 AB". F and G play as eighth notes while others play as quarter notes.
On     Sets the current octave. Valid values for n are 0 through 6. An octave begins with C and ends with B.
       Remember that C- is equivalent to B. 
< >    Changes the current octave respectively down or up one level.
Nn     Plays a specified note in the seven-octave range. Valid values are from 0 to 84. (0 is a pause).
       Cannot use with sharp and flat. Cannot use with the shorthand notation neither.
MN     Stand for Music Normal. Note duration is 7/8ths of the length indicated by Ln. It is the default mode.
ML     Stand for Music Legato. Note duration is full length of that indicated by Ln.
MS     Stand for Music Staccato. Note duration is 3/4ths of the length indicated by Ln.
Pn     Causes a silence (pause) for the length of note indicated (same as Ln). 
Tn     Sets the number of "L4"s per minute (tempo). Valid values are from 32 to 255. The default value is T120. 
.      When placed after a note, it causes the duration of the note to be 3/2 of the set duration.
       This is how to get "dotted" notes. "L4 C#." would play C sharp as a dotted quarter note.
       It can be used for a pause as well.


MIDI resource is loaded by the LOAD_RESOURCE function. MIDI shares same sound font with MML music.

Sound effects

Channel is indicated implicitly when play a sound effect; redundant sound effect will be abandoned if all the channels are occupied.

Plus 4096 to y for interpolating hz from the current set with the following one’s hz.



Part IV. Functions

Basic functions

Math functions

All trigonometric functions calculate in radians.

String functions

Data transfer and persistence

Retro BASIC dialects collect data before running a program with the DATA transfer statements, and only accepts simple data. It’s different with BASIC8, it actually executes a DATA statement at runtime and it must appear before calling READ, it can also collect advanced data types in BASIC8.


DATA 22, 7, 355, 113, "Hello", LIST(1 TO 42)
pos = READ a, b
READ c, d
PRINT a / b; c / d;
READ w, x, y, z
PRINT w; x; y; LEN(z);

The PERSIST statement automatically saves and loads data with variables, for nonvolatile storage. It makes a way simple saving and loading data, such as highscore, game progress, etc. It can deal with a couple of data types: nil, integer, real, string, and collections. The function persists the values of variables at program exit, and regains values when running to the same PERSIST statements at next execution.


PERSIST x, y, z
x = x + 1
y = y + 2
z = z + 3
PRINT x, ", ", y, ", ", z;
w = LIST(-1, -2, -3)


Most aspects in BASIC8 are architecture independent. Nevertheless, keep in mind that BASIC8 always uses little-endian as sequential bit order.



These functions are used to perform a pathfinding algorithm on 2D grids:

Grid coordinates can be any integer, with range of values from -32,767 to 32,767. Once the SET function of a pather is called, a cost matrix will be prefilled; it exists until calling CLEAR. The FIND function prefers to use invokable to get grid cost, and falls to use prefilled matrix if no lazy evaluator provided.

Walking cost is combined with two parts by multiplicative: neighbor cost and map cost. Neighbor cost stands for how much does it cost to walk from the current grid to its neighbor directions as following, in which D defaults to 1.414:

|     |     |     |
|  D  |  1  |  D  |
|     |     |     |
|  1  |     |  1  |
|     |     |     |
|  D  |  1  |  D  |

Pather retrieves grid cost from either lazy evaluator or prefilled matrix. All cost states must be immutable when calling the FIND function. It’s not walkable if either part of the cost combination results -1; positive cost means walkable, and the pather prefers lower cost grids. Eg:

p = PATHER(-3, -3, 3, 3)
p.SET(1, 1, 2)
l = p.FIND(-3, -3, 3, 3, NIL)
FOR n IN l
	UNPACK(n, x, y)
	PRINT x, ", ", y;

Use invokable as p.FIND(-3, -3, 3, 3, LAMBDA (x, y) (RETURN m)) to evaluate with walking cost m for grid x, y.


These functions are used to perform a raycasting algorithm on 2D grids:


These functions are used to perform a smooth walk algorithm on 2D grids:




BASIC8 uses SQLite as a storage engine of database. See its documentation for the SQL syntax.

Date time

Parameter format of NOW:

Specifier Replaced by Example
None Represent Www Mmm dd hh:mm:ss yyyy Fri Jun 20 09:29:00 1980
%D Short MM/DD/YY date 06/20/80
%F Short YYYY-MM-DD date 1980-06-20
%T ISO 8601 time format (HH:MM:SS) 14:55:02
%R 24-hour HH:MM time 14:55
More… Refer to strftime  






Conversions from JSON values to BASIC8 values, or vice versa:

OBJECT DICT (unordered)


Vector and matrix

There are some built-in data types for vector and matrix algebra, which are all implemented as non-referenced values. The vec4 is also used to represent for the structure of quaternion. BASIC8 uses row-major order to store matrix:

m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44

These functions are used to construct vector and matrix:

And some other construction functions:

These functions are used to unpack vector and matrix to variables, all of them accepts an input argument and a number of output variables:

These operators are used to perform component-wise calculations:

These operators are used to transform values:

Besides, these functions are used to do other linear computations:

Intersection detection


The addr parameter is combined with four parts, direction, protocol, address and port:

Part Value
Direction > for connecting, < for listening
Protocol udp://, tcp://
Address IP address
Port Port number

For example:

Address string Connectivity
“>tcp://” As client, connects to port 12000 via TCP
“<udp://” As server, listens from local host port 12000 via UDP
“tcp://12000” As server, listens from port 12000 via TCP

The callback of received event is an invokable that accepts two parameters respectively represent for the data has been just received, and the remote adress in string. Eg. LAMBDA (msg, addr) (). The type of the first parameter is determined by the “data_type” option.

The callback of connection established is an invokable that accepts a parameter represents for the remote address in string; nil for failure for outcoming connection. Eg. LAMBDA (addr) (). It’s invoked when either incoming or outcoming connection established; this is ignored by UDP.

The callback of disconnected is an invokable that accepts a parameter represents for the remote address in string. Eg. LAMBDA (addr) (). It’s invoked when either incoming or outcoming connection disconnected; this is ignored by UDP.

Key Note Value
“bytes_with_size” Whether packs size before bytes Can be “true” or “false”, defaults to “true”
“data_type” Parameter type of received callback Can be “string”, “bytes” or “json”, defaults to “json”

A single transmission or datagram cannot be longer than 32KB.

Consider closing and setting a network instance to nil as soon as it’s no longer in use.

For both UDP and TCP transmission, sent string and json always end up with a zero byte, vice versa, received string and json must end up with a terminal zero byte.

If the “bytes_with_size” option is set to “true”, an extra 32-bit unsigned integer will be automatically packed at head of bytes before sending, the size head itself also counts; bytes parameter in the received callback doesn’t contain that head. In short words, it’s transparency within BASIC8 programs, but it’s helpful to communicate with other endpoints to distinguish different messages, and you have to adapt the rule in other environment. You have to determine how to split bytes into message if “bytes_with_size” is set to “false”.


It’s not recommended to use functions marked with “platform dependent”, to get best compatibility across platforms.




Part V. Editors


To open a disk for editing, select it, then click [Head], Open; or right click on a disk, then click Open; or simply double click on it when the Run on click option is disabled. You can have only one disk opened at a time.

To run a disk, select it, then click Disk, Run; or right click on a disk, then click Run; or simply double click on it when the Run on click option is enabled.

To pause a running disk, click Disk, Pause, it is only doable with an opened disk.

To stop a running disk, click Disk, Stop; or just click the close button on a running canvas.

To edit the properties of a disk, right click on it, then click Properties.

To duplicate a disk, select it, then click [Head], Duplicate.

To purge all persistence data of a disk, select it, then click [Head], Cleanup. This will delete all files in the container directory (parent of “content”), except for the content directory.

To reload example disks, click Help, Examples, Reload all. BASIC8 uses UID to identify different disks, this operation will overwrite the disk with the same UID.

All paletted assets (sprites, tiles, quantized images) in a disk share a same palette data.

The title bar of an unsaved asset will be shown in different color. It automatically saves modifications when closing an asset, or closing a disk; to close without saving, click Disk, Close without saving with Shift key pressed.


Each disk has an entry source code file “main.bas”, where execution starts from.

To create a new code file, click Disk, New asset, Source code.

To open an existing code file, click Disk, Open asset, then select a “*.bas” file.

To rename a code file, click Disk, Rename asset, then select a “*.bas” file.

To delete a code file, click Disk, Delete asset, then select a “*.bas” file.

Sprite asset

You can edit colors of a shared palette by right clicking on it.

To create a new sprite asset, click Disk, New asset, Sprite.

To open an existing sprite asset, click Disk, Open asset, then select a “*.sprite” asset.

To rename a sprite asset, click Disk, Rename asset, then select a “*.sprite” asset.

To delete a sprite asset, click Disk, Delete asset, then select a “*.sprite” asset.

Map asset

To create a new map asset, click Disk, New asset, Map.

To open an existing map asset, click Disk, Open asset, then select a “*.map” asset.

To rename a map asset, click Disk, Rename asset, then select a “*.map” asset.

To delete a map asset, click Disk, Delete asset, then select a “*.map” asset.

Tiles asset

You can edit colors of a shared palette by right clicking on it.

All map assets in a disk use a shared tiles asset as reference. You’ll be asked to specify the size per tile when creating first map asset for a disk.

To open and edit a tiles asset, open an existing map asset first, then click the Edit button on the top-left corner of a map editor.

Quantized asset

You can edit colors of a shared palette by right clicking on it.

A quantized image is a paletted image. There are two ways in BASIC8 to create a quantized image, one is creating a blank with specific size, the other one is importing from an existing image file (supports “.png”, “.bmp”, “.tga” and “.jpg” files).

To create a new quantized image asset, click Disk, New asset, Quantized image, then fill in size to create a blank, or browse and import from an image file.

To open an existing quantized image asset, click Disk, Open asset, then select a “*.quantized” asset.

To rename a quantized image asset, click Disk, Rename asset, then select a “*.quantized” asset.

To delete a quantized image asset, click Disk, Delete asset, then select a “*.quantized” asset.


Part VI. Appendix

Built-in sound fonts

Reserved words

Some words are not implemented for actual functions, yet they are reserved for future. It’s suggested not to use them as identifiers:

Type names

The valid type names are: “NIL”, “UNKNOWN”, “INTEGER”, “REAL”, “NUMBER”, “STRING”, “TYPE”, “USERTYPE”, “USERTYPE_REF”, “ARRAY”, “LIST”, “LIST_ITERATOR”, “DICT”, “DICT_ITERATOR”, “COLLECTION”, “ITERATOR”, “CLASS”, “ROUTINE”. It’s guaranteed that these names won’t be changed in future release.

Library data structures might be implemented as either referenced “USERTYPE_REF”, or non-referenced “USERTYPE”. It is possible to get particular type name with the TYPE statement if a structure has overridden it, like “SPRITE”, “JSON”, etc; and use the TYPEOF statement to get overridden names for non-referenced structures, like “DRIVER”, “JSON_BOOL”, etc. However all overridden type names might be changed in future, so don’t presume these strings for specific types.