b8

BASIC8 - The Fantasy Computer!

View on GitHub

Welcome to BASIC8

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 some built-in tools for editing sprites, maps, sounds, etc.

It’s encouraged to share your creativity. All disks are shared under the CC-BY license as default, if you didn’t explicitly prefer other licenses. Every 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 cooperate together. The only community guidelines are just to be polite and respectful to other people and their work too; and, don’t do anything bad.

You may read this manual in any order according to your interest, and open this to lookup for explanations at any time.

Table of content

HOME

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.

Operations

Most home computers from 1970-80s use keyboards as the only developer input method, but mouse and touch are everywhere nowadays. It supports to use keyboard, mouse/touch and gamepad to play disks. But BASIC8 uses mouse as the main operation method on the GUI of editors; keyboard is mainly used to write code, and do some shortcuts:

When speaking of clicking [Head] in this document, it means the first icon item on the main menu bar:

Specifications

The limitations of BASIC8 are carefully chosen to make it easy and simple during making programs:

Disk library

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

Configuration

There are some configurable options available:

Sharing

A big part of retro computing which makes it fun to work with is sharing and discussing user generated contents through magazines and other medias. 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.

Disks

Standalone “.b8” files are standard “zip” packages, you may 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.

HOME

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"
PRINT A + B;

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.

Number

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 may 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).

String

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.

Comment

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 comments begin with REM statements, which stands for “REMark”, and single quotation marks ' as shortcut. All text from a comment mark till the end of line are ignored. Besides, BASIC8 supports a form of multiline comments, begins with '[ and ends with '], all text between them are ignored.

Arithmetic and relational operations

These operators are used for arithmetic calculations:

These operators are used for relational comparisons and calculations:

Logical truth table:

P Q P AND Q P OR Q NOT Q
T T T T F
T F F T T
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)
7 AND, OR
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”.

Array

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 which 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.

Conditional

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:

IF n MOD 2 THEN PRINT "Odd"; ELSE PRINT "Even";

The other way is multiline:

INPUT n
IF n = 1 THEN
	PRINT "One";
ELSEIF n = 2 THEN
	PRINT "Two";
ELSEIF n = 3 THEN
	PRINT "Three";
ELSE
	PRINT "More than that";
ENDIF

Looping

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

FOR i = 1 TO 10 STEP 1
	PRINT i;
NEXT i

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
	PRINT a;
	a = a + 1
WEND

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

a = 1
DO
	PRINT a;
	a = a + 1
UNTIL a > 10

Jumping

The EXIT statement is used to discontinue a loop.

BASIC8 offers a GOTO statement which performs unconditional control transfer. You can call it as GOTO label, it executes from label: in next step, rather than the following line of a GOTO statement. A labeled target constructs of a label identifier and a following colon as label:.

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

Advanced syntax

Collections

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:

Eg:

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")
PRINT LEN(d);

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.

Iterators

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)
i = ITERATOR(l)
WHILE MOVE_NEXT(i)
	PRINT GET(i);
WEND

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

FOR i IN LIST(1 TO 5)
	PRINT i;
NEXT

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

FOR i IN LIST(5 TO 1)
	PRINT i;
NEXT

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)
i = ITERATOR(d)
WHILE MOVE_NEXT(i)
	PRINT GET(i), ", ", VAL(i);
WEND

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);
NEXT

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 mentioned uniformly as “routine” for short in this document. A routine name follows after DEF, then with a list of parameters clamped with a pair of brackets. Use the name of a routine directly to call it, with a list of arguments clamped with a pair of brackets. But it requires an extra CALL statement when a routine is declared below where it’s called. The RETURN statement is also used there to exit a routine, with an optional result value.

Each routine has its own scope, in which statements in that routine body would lookup for variables, before looking into outer scopes.

Eg:

DEF foo(a, b)
	c = CALL bar(a, b)
	RETURN c
ENDDEF
DEF bar(a, b)
	RETURN a + b
ENDDEF
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 immediately, as:

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.

Variadic

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

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

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

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

Or:

WHILE LEN(...)
	s = s + ...
WEND

Or:

t = 0
DO
	s = s + t
	t = ...
UNTIL TYPE(t) = TYPE("UNKNOWN")

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

Lambda

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; then a lambda body, which 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.

Eg:

DEF counter()
	c = 0
	RETURN LAMBDA (n)
	(
		c = c + n
		PRINT c;
	)
ENDDEF
acc = counter()
acc(1)
acc(2)

Class

BASIC8 supports a prototype-based programming paradigm which is a kind of OOP (Object-Oriented Programming). When speaking of “class instance” or “prototype” in BASIC8, they mean the same thing. This programming paradigm can also be 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 of 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 (which means using it as a meta class). Use NEW to instantiate a new object of a prototype.

See the following example of class:

CLASS foo
	VAR a = 1
	DEF fun(b)
		RETURN a + b
	ENDDEF
ENDCLASS
CLASS bar(foo)
	VAR a = 2
ENDCLASS
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 with name of member as key, value of member as value.

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");

Typing

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, as if its content was just written at where the IMPORT statement is:

IMPORT "directory/file_name.bas"

Coroutine

A coroutine is a special data type in BASIC8, which encapsulates 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 by itself. Besides, there is also an automatically dispatched mode.

Eg:

co = COROUTINE
(
	LAMBDA (x, y)
	(
		PRINT x / y;
		FOR i IN LIST(1 TO 3)
			YIELD i
		NEXT
		YIELD LAMBDA () (PRINT "I = " + STR(i);)
		YIELD LIST()
		YIELD COROUTINE(LAMBDA () (PRINT "Nested";))
		RETURN "CO"
	),
	22, 7
)
FOR t IN co
	IF t IS TYPE("NUMBER") THEN
		PRINT t;
	ELSEIF t IS TYPE("STRING") THEN
		PRINT t;
	ELSEIF t IS TYPE("LIST") THEN
		PRINT "List";
	ELSEIF t IS TYPE("ROUTINE") THEN
		t()
	ELSE
		WHILE MOVE_NEXT(t)
		WEND
	ENDIF
NEXT

Automatic memory management

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

Generic functions

HOME

Part III. Game driver

Graphics

Coordinate system as following:

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 frames, 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
		RETURN m
	ENDIF
ENDDEF
n = UPDATE_WITH(DRIVER(), CALL(update))
PRINT "Done with: ", n;

This UPDATE_WITH will return in five seconds.

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

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

Primitives

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.

Map

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.

Quantized

Input

Mouse and touch

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

Gamepad

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

Keyboard

Audio

Music

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.

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.

HOME

Part IV. Editors

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 only have 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 clickable 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.

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.

Code

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

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 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 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 of a 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

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 (it 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.

HOME

Part V. 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.

Eg:

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

The PERSIST statement automatically saves and loads data with variables, all data are persisted on disk. It’s helpful to make a way simple saving and loading data, such as highscore, game progress, etc. It deals 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 next time.

Eg:

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

Libraries

Most aspects in BASIC8 are hardware independent. Nevertheless, it should be noticed that BASIC8 always uses little-endian as sequential bit order.

Algorithm

Pathfinding

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_DIAGONAL_COST(-1)
p.SET(1, 1, 2)
l = p.FIND(-3, -3, 3, 3, NIL)
PRINT LEN(l);
FOR n IN l
	UNPACK(n, x, y)
	PRINT x, ", ", y;
NEXT

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

Archive

Bytes

Database

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  

File

GUI

Image

IO

JSON

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

JSON BASIC8
Null NIL
Bool JSON_BOOL
Number INTEGER or REAL
String STRING
Array LIST
Object DICT (unordered)

Math

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:

Network

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://192.168.0.1:12000” As client, connects to 192.168.0.1 port 12000 via TCP
“<udp://127.0.0.1:12000” 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 Value Note
“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.

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”.

System

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

Text

Web

HOME

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

Valid type names as following: “NIL”, “UNKNOWN”, “INTEGER”, “REAL”, “NUMBER”, “STRING”, “TYPE”, “USERTYPE”, “USERTYPE_REF”, “ARRAY”, “LIST”, “LIST_ITERATOR”, “DICT”, “DICT_ITERATOR”, “COLLECTION”, “ITERATOR”, “CLASS”, “ROUTINE”.

Data structures in library may be implemented as referenced “USERTYPE_REF”, or non-referenced “USERTYPE”. You may get particular type name with the TYPE statement if a structure has overridden typing, like “SPRITE”, “JSON”, etc. Also the TYPEOF statement is used to get overridden typing for non-referenced structures, like “DRIVER”, “JSON_BOOL”, etc. All overridden type names may be changed in future version, so don’t presume to use overridden names.

HOME