EBM BASIC V1.0
USER GUIDE

The Essex Business Machines BASIC computing language is a powerful and easy-to-use means of communicating with your EBM System/800 microcomputer, and is similar to the BASIC used in most other 8-bit personal, business and industrial computer systems, such as the Agon Light, the MEGA65, the Spectrum Next, the Foenix F256K2M, the Aquarius+ and the Commander X16. It is not dissimilar to the Lua language you may have used to program the PICO-8 and TIC-80 video game consoles.

The commands that follow will provide a valuable reference as you learn to write EBM BASIC. With a choice of 6x8 and 8x8 character modes, reprogrammable character graphics, a colourful bitmap mode, two colour modes - pastel and dark, 16 monochrome hardware sprites, hardware scrolling, and its versatile digital sound chip, porting your game programs from other systems is a breeze. Watch out for educational content on your favourite video-sharing platform, so you don't get stuck in a DO : LOOP

The guide covers the following subjects, click an item in the list or scroll. Or Click itch.io to download.

REMARKS (COMMENTS)

REM, short for remark, allows you to add comments to your program.

Command: REM

REM is essentially a command that does not execute. This means that your comments are compiled into memory when you run your program. You should, therefore, avoid extensive use of comments in loops that need to execute quickly. More generally, overusing comments wastes memory.

It is common to "comment out" lines while testing a computer program: the third example below is a statement that has been temporarily disabled with the REM command. Removing the REM command will 'uncomment' the line:

REM This game was written by Aiden Smith
REM TODO: add a high-score table
REM SCORE=SCORE+BADDIEKILLED*100 : LIVES=LIVES+1

LABELS

A label is used to name a specific point in your program. You create a label by typing a name followed by a colon (without a space), on a line of its own, beginning at the start of the line. The example below names a location DRAGONS before printing text.

DRAGONS:
PRINT "A DRAGON APPEARS!"
PRINT "IT BREATHES FIRE!"
PRINT "RUN!"

A label does nothing: it is not executed. Control commands such as GOTO and GOSUB reference a label: the statement GOTO DRAGONS, for example, redirects the program so that it continues to execute from the line following the named label.

VARIABLES

Variables store data. You can picture a variable as a labelled box: you can put a value into the box, and later you can retrieve the value using the label. There are two main kinds of variable: numeric and string.

A numeric variable holds a whole number (no minus sign or decimal place). A numeric variable named SCORE or SC may hold the player's score in a video game.

A string variable holds text, a series of up to 255 characters. The name of a string variable ends with a dollar sign. FIRSTNAME$ is an example of a string variable name. String variables can contain digits, in which case they can look like numbers, but you cannot use the numeric value that the string resembles to perform maths.

Variables live in the System/800's RAM memory. This is not an abstract concept: your program has 65,536 bytes, and that is for storage of your program including REM statements, your program's graphics and variables, and many system variables. A string occupies 256 bytes, regardless of whether or not all of those bytes are used. The three strings below each take up 256 bytes of RAM:

LET SHORT_STR$="HELLO WORLD"
LET LONGER_STR$="Greetings from the wrong side of history!"
LET EMPTY_STR$=""

Command: LET

LET works differently in EBM BASIC to most versions of BASIC. You must include the LET command the first time you reference the variable, to declare (create) the variable. You must not use the LET command when you reference the variable later.

Assigning a value to a variable (with an = sign) at the point of declaration is optional, but until you assign a value to the variable, its contents are unknown: the variable will contain whatever value its memory location contained when the program began to execute.

LET LIVES=5 : REM CONTAINS 5
LET ITEMS1=0 : REM CONTAINS 0
LET ITEMS2 : REM MAY OR MAY NOT CONTAIN ZERO

A string variable declared without a value holds random text, and will be of a random length up to 255 characters. You can change the value of a variable by following its name with an = sign and a value, with no LET command. The first line below creates a string variable that holds the name of a video game, then changes its contents. The second line creates an empty string. The third line creates a string that may or may not be empty!

LET GAME$="MANIC MINER" : GAME$="JET SET WILLY"
LET EMPTY$=""
LET UNKNOWN$

The System/800 ignores the LET command when it encounters the same program line more than once. This means that it is safe to declare a new variable (i.e. use the LET command) inside any part of the program, including sections that may be executed more than once, such as inside a loop or a subroutine. The LET statement will be ignored but the assignment will be executed.

ARRAYS

Arrays are a type of variable. Those new to variables may consider arrays to be an advanced topic, but most kinds of programs, including video games, can often be written without array variables, so skip this section if the concepts seem confusing.

An array is a numbered series of variables that share a name. Rather than invent a separate name for every entry in the array, you give the array one name, then access each value in the array by its index number. Square brackets indicate that a variable is of the array type.

You declare arrays with the LET command, followed by the name of the variable, then the size of the variable (the number of numeric values or text strings it can contain) within the square brackets. All of this applies whether the variable is an array of numeric variables or an array of string variables. If the array is an array of string variables, the dollar sign must be before the opening square bracket. An array cannot contain variables of both types.

LET NUMBERS[100]
LET WORDS$[50]

Arrays are numbered from zero; this is called zero indexed. The 100 variables (often called elements) in the first example above are accessed via the values 0 to 99: the third value is accessed via the name NUMBERS[2], for example.

Stating a value at the moment of declaration is optional. When a value is provided, the value is copied into every element. The value can be constructed from calculations and variables, as in the first example below:

LET STARS[100]=STAR_MAX-10
MONSTERS$[99]="no name yet"

You read or write to an element using the array name, followed by the element number in square brackets. The element number can be constructed from calculations and variables:

STARS[X]=128
PRINT PETNAMES$[A+B]

Only one-dimensional arrays are allowed in EBM BASIC V1.0 This may change in future revisions.

OPERATORS

Arithmetic Operators

EBM BASIC supports the standard arithmetic operators:

+ add
- subtract
* multiply
/ divide
MOD modulo (obtain remainder after division)

Round brackets control the order of operations. Examples:

LET TOTAL=(PEEK 128)+A
LET TOTAL=(PEEK 128+A)

All arithmetic is performed on whole numbers from 0 to 65535 or from 0 to 255 depending on numeric data type. Results that exceed the range wrap around.

Comparison Operators

Comparisons produce 1 if the condition is true and 0 if false. You do not normally see this value directly, but you can assign it to a variable. Comparisons are most commonly used with IF and DO WHILE / DO UNTIL:

=  equal to
<>  not equal to
<  less than
>  greater than
<=  less than or equal to
>=  greater than or equal to

In the first example below, we do not see the resulting value, 1. In the second, the resulting value is also 1, and this time we display it on the screen after storing it in a variable.

LET A=9 : IF A=9 THEN PRINT "IT'S NINE!"
LET B=A>8 : PRINT A : REM IT'S TRUE THAT 9 IS GREATER THAN 8

String variables can also be compared. Two strings are equal if they contain exactly the same characters. The other operators compare strings alphabetically.

IF NAME$="AIDEN" THEN PRINT "HELLO AIDEN!"
IF A$<B$ THEN GOSUB SWAP

Logic and Bitwise Operators: AND, OR, NOT

AND, OR and NOT enable comparison between values.

IF X>0 AND X<56 THEN GOSUB DRAW
IF FIRE=1 OR SPACE=1 THEN GOSUB SHOOT
IF NOT PAUSED THEN GOSUB GAMELOOP

These operators also work at the bit level. (Bit operations can only be understood by understanding how data is stored in a computer's memory.)

SOCRE AND 15 keeps only the lower four bits of SCORE. SCORE OR 128 sets bit 7 of SCORE. NOT flips every bit, therefore NOT 0 (zero) results in 65535.

FUNCTIONS

This section discusses EBM BASIC's built-in functions. These are not to be confused with user-defined functions in modern languages such as C and Lua, or subroutines in EBM BASIC or other forms of BASIC. One function, SPRITE HIT, applies only to sprites and is discussed in the section GRAPHICS: SPRITES.

FUNCTIONS: LIST FUNCTIONS

List Functions are functions that take several arguments (numeric values, typically), reading each argument in turn. EBM BASIC features four such functions: PEEK, RND, MIN and MAX. PEEK is discussed in the section MEMORY READ/WRITE: PEEK AND POKE, but essentially this function reads a value from an address in the System/800's RAM memory.

Numeric List Functions: RND, MIN, MAX

These three List Functions each return a single number, derived from the supplied numeric arguments.

Function: RND

RND returns a random number. While RND is considered a List Function, it can operate on either one or two arguments. One argument returns any value (including zero) less than the stated value. Two arguments limits to a specific range, e.g. RND 35 98 returns a value from 35 to 98 inclusive.

LET DICE=RND 6 : DICE=DICE+1 : REM 1 2 3 4 5 6
DICE=RND 1 6 : REM 1 2 3 4 5 6

Function: MIN
Function: MAX

MIN returns the smallest of two or more values, and MAX returns the largest of two or more values.

LEAST_MONSTERS=MIN M1 M2 M3 M4 M5 M6 M7
LET HISCORE=MAX SCORE 10000

FUNCTIONS: SINGLE VALUE FUNCTIONS

Single Value Functions are functions that accept only one parameter: they operate on the value immediately following the function keyword. EBM BASIC features many such functions, and they divide into three groups. The Maths Functions are SQR, SIN and COS. The Memory Functions are MEM, SIZE and COUNT. The String Functions are LEN, ASC, VAL, CHR, STR, LEFT, RIGHT and MID.

Maths Functions: SQR, SIN, COS

A Single Value Function works directly on the value to its right. If you want to work on more than one value, put round brackets around the values. Do not include the function name in the brackets: the brackets group the arguments.

Function: SQR

SQR returns the square root of a value. The square root of a number is the value that, when multiplied by itself, produces the number, so SQR 100 returns 10 because 10 × 10 is 100. Because the System/800 works with whole numbers only, any fractional part of the result is dropped: SQR 50 returns 7, not 7.07

The most common use of SQR in video game programming is finding the distance between two points, for example the distance between the player and an enemy. Note the brackets in the fourth example, which make SQR work on the whole calculation rather than just the first value:

LET DX=ENEMYX-PLAYERX
LET DY=ENEMYY-PLAYERY
IF DIST<20 THEN GOSUB ATTACK
LET DIST=SQR (DX*DX+DY*DY)

Function: SIN
Function: COS

SIN and COS smoothly sweep a value up and down as an angle increases, which is ideal for making things (such as sprites) bob, wave, or move in a circle. You provide the SIN or COS function with an angle, where a full circle is 256 steps, and the output you then use is a value from 0 to 255, resting at 128 in the middle. Angles from 0 to 255 cover one full circle; any larger value simply wraps around, so an angle of 256 gives the same result as 0. Because COS runs a quarter-circle (64 steps) ahead of SIN, using SIN for one axis and COS for the other moves an item in a circle.

SIN starts at the middle: SIN 0 returns 128, SIN 64 returns 255, SIN 128 returns 128, and SIN 192 returns 0. COS is the same wave but starts at the top: COS 0 returns 255.

The example below bobs a sprite up and down. SIN's output (a value from 0-255) is used directly as the sprite's Y position, so it sweeps smoothly across the full range:

LET ANGLE=0
DO
  LET Y=SIN ANGLE
  SPRITE 1 100 Y
  ANGLE=ANGLE+4
  WAIT
LOOP

To rescale to a different range, divide by 255 and multiply by the size of the intended range (the range upper limit minus the lower limit). The example below rescales to a range from 1 to 100:

LET ANGLE=0
DO
  LET V=1+(((SIN ANGLE)*99)/255)
  PRINT V
  ANGLE=ANGLE+4
  WAIT
LOOP

Memory Functions: MEM, SIZE, COUNT

These three functions return the address of the memory location where a variable's data is stored in memory and/or the number of bytes it occupies.

Function: MEM

MEM returns the address of the memory location where a single-byte variable's data is stored in memory, or the location where a larger variable's data begins. The argument must be a variable name. The following example will always output the same numerical value twice:

LET BYTE X=RND 99 : PRINT X : LET X_ADR=MEM X : PRINT PEEK X_ADR

Function: SIZE

SIZE returns the number of bytes that a variable occupies in memory. BYTE variables occupy a single memory location, therefore following SIZE with the name of such a variable will always return 1. Following SIZE with the name of a regular numeric variable will return 2. SIZE is useful if you need to check which kind of variable you are working with.

There is no obvious use for requesting the size of a string variable as all strings occupy 256 bytes. Use LEN to obtain the active character count of a string variable.

For an array, SIZE returns the total byte footprint — so a ten-element numeric array of the two-byte type returns 20, and a ten-element string array returns 2560 (ten slots of 256). To obtain the number of elements in an array. use COUNT.

Function: COUNT

COUNT returns the number of elements in an array. For a variable that is not an array, COUNT returns 0.

LET ENEMIES[20]=0
LET N=COUNT ENEMIES
PRINT N : REM PRINTS THE VALUE 20

String Functions: LEN, ASC, VAL, CHR, STR, LEFT, RIGHT, MID

These eight functions measure strings, convert between strings and numbers, and extract portions of a string.

Function: LEN
Function: ASC
Function: VAL

LEN returns the number of characters in a string. ASC returns the character code of a single-character string, or the first character of a multi-character string, or 0 if the string is empty. VAL converts a digit-only string into a numerical value, stripping out any spaces.

LET NAME$="BOB" : LET L=LEN NAME$ : PRINT L : REM 3
LET C=ASC "ABBA" : REM C=65, ASCII OF CAPITAL A
LET S$="    35    " : PRINT (VAL S$)*2 : REM 70

Function: CHR
Function: STR

CHR returns a single-character string from a character code. STR converts a number into a string of digit characters.

ABBA_first_letter$=CHR 65
LET S$=STR SCORE

LEFT returns characters from the start of a string. RIGHT returns characters from the end. For both, the parameter is the required number of characters, not the start/end position. MID returns the stated number of characters from any part of a string, starting from a stated zero-indexed position. The first paramater is the zero-indexed starting position, and the second parameter is the number of characters.

LET X$="IBM Personal Computer"
LET L$=LEFT X$ 3 : REM "IBM"
LET R$=RIGHT X$ 8 : REM "Personal"
LET M$=MID X$ 4 8 : REM "Computer"

PRINTING TEXT

PRINT writes characters to the screen in Character Mode. You can print literal numbers and text strings, and you can print the numeric and string values held in variables. After printing, the cursor moves to the start of the next line.

Command: PRINT

Used on its own, PRINT prints a blank line, moving the cursor down to the next line. When followed by a value, it prints that value. Numbers may be literals or calculations. Text strings are enclosed in quote marks.

PRINT : REM PRINTS A BLANK LINE
PRINT 5 : REM PRINTS THE VALUE 5
PRINT (2*2)+1 : REM CALCULATION, ALSO PRINTS 5
PRINT "GAME OVER" : REM A TEXT STRING
PRINT SCORE : REM PRINTS THE PLAYER'S SCORE

Sub-command: AT

The AT sub-command moves the cursor before the text is printed. AT must be followed by two values: the column and the row. Numeric variables may be used to set the print position dynamically. AT can be followed by data.

PRINT AT 5 10 : REM MOVE THE CURSOR TO COLUMN 5, ROW 10
PRINT AT PLAYERX PLAYERY : REM A POSITION HELD IN VARIABLES
PRINT AT 5 10 "PRESS FIRE TO START" : REM MOVE, THEN PRINT

A single PRINT statement can print several items in sequence — numbers, strings and variables.

PRINT "SCORE: " SCORE
PRINT "LIVES " LIVES " HI " HISCORE
PRINT "X: " PX " Y: " PY
PRINT "YOU HAVE " GOLD " GOLD"
PRINT "GET READY, " NAME$ "!"
PRINT "TIME LEFT: " SECS " SECONDS"

By default, after a PRINT statement, the cursor moves to the start of the next line. This behaviour can be controlled with two sub-commands.

Sub-command: CR
Sub-command: NCR

NCR, meaning no carriage return, ends a PRINT statement without moving the cursor. CR, carriage return, forces a line break.

In the example below, GAME OVER is printed several characters along the seventh row. CR moves the cursor to the start of the next line, where, assuming the variable DTS holds the value 389, the text Pac-Man ate 389 dots! is printed. The NCR leaves the cursor immediately to the right of the exclamation mark.

PRINT AT 10 6 "GAME OVER" CR "Pac-Man ate " DTS " dots!" NCR

MEMORY READ/WRITE: PEEK AND POKE

A memory location, also known as an address, being a single byte, can hold a whole number from 0 to 255. The PEEK function and the POKE command can be used to read from and write to those addresses. Since the System/800 is a 64K system, addresses are numbered from 0 to 65535.

Many memory locations are used by the system, and therefore have a special purpose, for example, memory location 68 holds the colour of the screen border. The majority of memory is free for use by your own programs. EBM BASIC is designed so that you need give little thought to memory. Dedicated commands effectively POKE values into specific memory locations to control the computer's behaviour. The BORDER command POKEs the stated value into memory location 68, for example.

Command: POKE

The POKE command writes a value into an address. The first value in a POKE statement is the address to write to, and the second is the value to write to that address. The first example draws a capital letter 'A' to the top-left corner of the screen. Variables and calculations can be used for either value, as in the second example:

POKE 2816 65
LET C=5 : LET SHIP=6 : POKE 2816+C SHIP+3

Function: PEEK

The PEEK function reads the value at an address. The value after the PEEK keyword is the address to read from. The value at the stated memory location is read, and can then be stored in a variable, used in a calculation, or printed straight to the screen. Variables and calculations can be used for the address, as in the second example below. Two-byte variables can be used to hold addresses greater than 255.

LET BYTE A=PEEK 2816
ITEM=PEEK STORE

PEEK is a single value function, meaning that it works only on the next value.

The first example below reads the value at 2816, which is the start of screen memory in character mode, then adds 1 to the value found at that address. To read from a calculated address instead, put round brackets around the calculation: the second example reads the value at address 2817.

SCRN_1st_CHR_PLUS_ONE=PEEK 2816+1
SCRN_2nd_CHR=PEEK (2816+1)

PEEK can be used wherever a number is valid, e.g. you can directly PRINT the result of a PEEK to the screen. The example below copies the character (perhaps a letter of the alphabet) in the first screen position (top-left) to the second position. The third line PRINTs the ASCII/ESSECSCII value of that character to the screen:

LET BYTE LETTER=PEEK 2816
POKE 2817 LETTER
PRINT "Second letter is now: " PEEK 2817

Sub-command: WORD

This sub-command extends the functionality of POKE and PEEK. Its capabilities may seem complex to those new to working directly with memory, but most kinds of programs, including sophisticated video games, can be written without ever using this sub-command, so skip this section if the concepts seem intimidating.

Values larger than 255 can be stored in memory by using two consecutive memory locations. In EBM BASIC, a series of two consecutive bytes is called a "word". POKE WORD writes a value between 0 and 65535 across two memory locations. When a value lower than 256 is stored in this way, the first location contains 0.

LET GHOST=570 : REM NOT A BYTE VARIABLE
POKE WORD 292 GHOST
POKE WORD 3000 7777

The third example above inserts the value 7777 into memory, starting at address 3000 and ending at 3001.

(The value 7777 can be represented in hex as $1E61. The big-endian system is used: the high byte, $1E, is stored in 3000, and the low byte, $61, is stored in 3001.)

It is as easy to program the System/800 without understanding hex as it is to program the Pico-8 without understanding hex. However, there are advantages to having an understanding of how computers store values in memory when writing video games.

PEEK WORD reads a two-byte value from two consecutive memory locations. A value written with POKE WORD can be read back using PEEK WORD by using the same address.

If you use PEEK WORD with a single-byte variable (a variable created with the BYTE sub-command), only the low byte is received (not the high byte, as you might expect). Attempt to read the two-byte value stored across two addresses into a byte variable, and the variable will receive the value stored in the address immediately after the address stated! Just avoid using PEEK WORD with single-byte variables unless you fully understand how values are stored in memory.

In the example below, ALL receives the word variable stored across addresses 400 and 401, while PART receives only the value stored in address 401.

LET ALL : REM TWO-BYTE (WORD) VARIABLE
LET BYTE PART : REM CAN ONLY STORE VALUES UNDER 256
ALL=PEEK WORD 400
PART=PEEK WORD 400 : REM RECEIVES VALUE FROM 401!

Let's assume that addresses 400 and 401 together contain the value 7777. The decimal value 7777 is $1E61 in hex: address 400 contains $1E and address 401 contains $61. PEEK WORD into a single-byte variable, and that variable will receive $61 hex, which is 97 in decimal.

MEMORY READ/WRITE: READ AND DATA

DATA provides a list of literal values directly inside your program. The statement does not store the values in memory: the values must be copied into variables or arrays using the READ command.

Command: DATA

A DATA statement inserts a list of literal values into your program.

The values may be numbers or text strings. They may not be numeric variables or string variables. A single DATA statement may hold any combination of the two types, though you must take care to READ each value into a variable of the matching type: Numeric values must be read into numeric variables, and strings into string variables.

Each numeric value should be a whole number from 0 to 255, or from 0 to 65535, depending on whether the value is to be read into a single byte or a two-byte variable. Enclose text strings in quote marks.

DATA 50 80 120 200 500 : REM FIVE NUMERIC VALUES
DATA "HOBBES" "CALVIN" "SUSIE" : REM THREE TEXT STRINGS
DATA 3 2 1 "COUNTDOWN!" : REM A MIXTURE OF BOTH

Command: READ

READ copies values from your DATA statements into variables, one value at a time.

(Since variables are stored in the System/800's RAM, this stores the values in RAM.)

The System/800 keeps a pointer that advances through the data as each value is read, so each READ continues from after the previous READ.

READ X : READ Y : READ Z : REM THREE SEPARATE STATEMENTS
READ X Y Z : REM THE SAME, IN ONE STATEMENT
READ NAME$[0] NAME$[1] NAME$[2] : REM INTO ARRAY ELEMENTS

It is common to use a loop to read multiple values into an array. The example below reads the five values from the first example above:

LET CNT=0 : DO 5 : READ A[CNT] : CNT=CNT+1 : LOOP

Sub-command: FROM

By default, READ works through the DATA statements in the order they appear in the program. The FROM sub-command moves the pointer to a particular DATA statement, identified by a label that precedes it.

Follow READ with FROM and a label name. The pointer jumps to the first DATA statement after that label, and the reads that follow continue from there. The label and its DATA may appear anywhere in the program, before or after the READ, but the DATA statement must be the first such statement after the label.

DRAGONS:
DATA "Smaug" "Ramoth" "Viserion" "Glaurung" "Brian"
READ FROM DRAGONS : REM MOVE THE POINTER
READ D1$ D2$ D3$ D4$ D5$ : REM THEN READ THE VALUES

Redirection may be combined with the reading of values in a single statement:

READ FROM GHOSTS A$ B$ C$ D$
GHOSTS:
DATA "Blinky" "Pinky" "Inky" "Clyde"

PROGRAM FLOW

GOTO, GOSUB and RETURN are used to affect the flow of the program, WAIT to pause execution of the program and STOP to end execution.

Command: GOTO

GOTO redirects execution of the program to a label elsewhere in the program. GOTO SETUP continues execution from the line immediately after the label SETUP. The label can be anywhere in the program — before or after the GOTO. If the label does not exist, the System/800 will generate an error.

Command: GOSUB

GOSUB SETUP continues execution from the line immediately after the label SETUP, just as GOTO would, but remembers the location of the GOSUB, so that a later RETURN statement may direct program flow back to the line following that GOSUB.

The System/800 does not check that a RETURN statement follows soon after the label, but without a RETURN, problems may occur. The programmer is encouraged to think in terms of subroutines, a subroutine being any series of commands preceded by a label and terminated by a RETURN command.

A subroutine may be called from any number of points in the program, and any subroutine may call other subroutines, but again, it is the programmer's responsibility to keep track of subroutines, for example to avoid creating an infinite loop — or attempting to RETURN when not inside a subroutine, which will generate an error.

In the example below, the subroutine PRINTHOORAY prints a message, then returns to the GOSUB that called it. The program prints the message twice, once before and once after a row of stars, and then ceases execution at the STOP command:

GOSUB PRINTHOORAY
PRINT "***"
GOSUB PRINTHOORAY
STOP
PRINTHOORAY:
PRINT "Hooray!"
RETURN

System Label: START

START is a special system label that exists whether or not it has been declared. If it has not been declared, GOTO START and GOSUB START will jump to the first line of the program.

System Label: NEXT

NEXT is a special system label that may be included in a program any number of times. A GOTO NEXT or GOSUB NEXT statement jumps forward to the next NEXT: label in the program. You cannot jump backwards to a label with this name. It is useful for certain program structures, such as algorithms or state machines, where the program must repeatedly jump forwards under certain conditions. The NEXT label also provides a convenient debugging tool, as it enables the programmer to conveniently skip several lines without having to choose a unique label name.

In the example below, the GOTO jumps forwards to the second NEXT label, not back to the first.

NEXT:
PRINT "THIS LINE IS PRINTED ONCE"
GOTO NEXT
PRINT "THIS LINE IS NOT PRINTED"
NEXT:
PRINT "THIS LINE IS PRINTED ONCE"

Command: WAIT

WAIT pauses the program until the System/800 has completed drawing the next frame, that is, until the next vertical blank. Without WAIT, a game loop that moves sprites and so on will run countless times before the screen is redrawn, moving the game's entities too quickly, and will likely cause a jittery effect. It is common to add this command to the end of a game program's main loop.

DO
X=X+1
SPRITE 5 PlayerX PlayerY
WAIT
LOOP

Follow WAIT with a numerical value to pause for multiple frames. Since the System/800 typically runs at 60 frames per second, WAIT 60 pauses for approximately one second.

Command: STOP

STOP ends execution of the program.

PROGRAM FLOW: LOOPS

A loop is a series of statements that the System/800 runs in succession. Loops can be placed, nested, inside other loops.

Command: DO/LOOP

A loop begins with the DO statement and ends with the LOOP statement. The statements between the two control statements run in order. The loop then jumps back to the DO command and runs them again.

DO and LOOP may be placed on one line separated by colons, and may or may not be separated by statements:

DO : LOOP : REM INFINITE LOOP
DO : A=A+1 : PRINT 1 : LOOP

The loop may be spread over separate lines, one statement to a line:

DO
  PRINT "HELLO"
  PRINT "WORLD"
LOOP

You can jump out of a loop using the GOTO command. The System/800 tracks loops intelligently: jumping out of loops does not create logical issues for loops entered later. The example below exits a loop once A is greater than B:

DO
  PRINT "Adding..."
  A=A+10
  IF A>B THEN GOTO ESCAPE
LOOP

Follow DO with a value to run the loop a set number of times. The number may be a literal, a variable or a calculation. If the value resolves to 0 or less, the loop does not run.

DO 5 : PRINT "HELLO" : LOOP : REM PRINTS HELLO FIVE TIMES

Sub-command: WHILE

DO WHILE runs the loop only while a condition is true. The condition is checked at the start of the loop, before the statements run, so if the condition is false at the start of the loop, the loop does not run.

DO WHILE SCORE<101 : GOSUB INC_SCR : LOOP
DO WHILE SC>HI : PRINT "HOORAY!" : GOSUB HI_2 : LOOP

Sub-command: UNTIL

DO UNTIL runs the loop until a condition becomes true. Again, the condition is checked at the start of the loop, therefore if the value is true at the start of the loop, the loop does not run.

DO UNTIL BONUS=1000 : GOSUB BONUS_UP : LOOP

Do/Loop Explicit Form

DO/LOOP has an explicit form which resembles the FOR/NEXT loop found in many other versions of BASIC. The explicit form uses a control variable that counts from a starting value, changing by a fixed amount each iteration until the variable reaches a target value.

DO must be followed by two or three values. The first value is the assignment: it sets the control variable's starting value. The condition ends the loop: the loop stops once this condition is met. An optional step value can be added to control how the variable increases or decreases each iteration. The control variable must have previously been declared with LET. Each expression must reference the same control variable.

DO N=1 N>10 4

In this example, variable N starts at 1, and 4 is added at the end of each iteration, so N takes the values 1, 5, 9 and 13. The condition N>10 ends the loop.

The condition is inclusive: the pass on which the condition first becomes true still runs, so the loop runs once more with N at 13 before stopping.

Command: CONTINUE

CONTINUE jumps back to the start of the loop. Whether the loop then runs again depends on its condition, exactly as if execution had reached the LOOP command.

The following example prints 1, 2, 4 and 5, using CONTINUE to skip the printing of 3:

LET N=0
DO WHILE N<5
  N=N+1
  IF N=3 THEN CONTINUE
  PRINT N
LOOP

Command: BREAK

BREAK exits the loop early, jumping to the first statement after the closing LOOP command.

DO
  IF INPUT FIRE THEN BREAK
  WAIT
LOOP

PROGRAM CONTROL: CONDITIONS

The IF command runs one or more statements only when a stated condition is true. This condition is a calculation that produces a numeric value. 0 is treated as false, and any other value is treated as true. The result of this calculation is not generally directly present: it is common to simply think of the result of this calculation as being true or false.

Command: IF/THEN

The simplest form of IF/THEN occupies a single line. Follow IF with a condition, the keyword THEN, then a single statement. The final statement runs if the condition is true.

IF D=1 THEN Y=Y-1
IF SCORE>=1000 THEN GOSUB BONUS
IF LIVES=0 THEN GOTO GAMEOVER

The statement after THEN cannot be another IF statement. Where nesting is required, use the multi-line form outlined below.

Sub-command: END

To run more than one statement, or to nest one IF statement inside another, use the multi-line form. The line ends immediately after THEN, and every statement from the next line up to the END command runs when the condition is true, or is skipped when it is false.

IF SCORE>HIGH THEN
  HIGH=SCORE
  PRINT "NEW HIGH SCORE!"
  GOSUB SAVE_TABLE
END

Multi-line blocks may be placed inside other multi-line blocks.

Sub-command: ELSE

ELSE adds statements that run when the condition is false. It can be used in both the single-line and multi-line forms.

On a single line, follow the statement after THEN with ELSE and another statement. When the condition is true, the statement after THEN runs and the statement after ELSE is skipped. When the condition is false, the statement after ELSE runs and the statement after THEN is skipped.

IF LIVES=0 THEN GOTO GAME_OVER ELSE GOSUB DRAW_LIVES

In the multi-line form, ELSE appears on a line of its own. The statements above ELSE run when the condition is true, and the statements below it run when the condition is false.

IF INPUT FIRE THEN
  PRINT "FIRE!"
ELSE
  PRINT "no fire"
END

USER INPUT

The INPUT keyword is both a command and a function.

Command: INPUT

To allow the user to enter a numeric value, use INPUT as a command, followed by the name of a numeric variable. The variable must previously have been declared with LET:

LET GUESS : INPUT GUESS

To allow the user to enter a text string, follow the INPUT command with the name of a string variable, including its dollar sign. The variable must previously have been declared with LET:

LET NAME$ : INPUT NAME$

In either case, a question mark will be printed at the current cursor position, followed by a space, and the program will pause until the user inputs characters and presses ENTER.

If the user inputs invalid data (e.g. text when the variable is not a string variable), the procedure will repeat until the user inputs a valid value. The input will be stored in the named variable. In the case of numeric variables, if the user presses ENTER without entering a value, the value 0 is stored.

A prompt string may be included after the variable name, which will be displayed to the left of the question mark:

INPUT NAME$ "What is your name"
INPUT PLAYERS "How many players"

An array element may be used as the target variable:

INPUT SCORES[count] "Enter score for player 2"
INPUT ANSWERS$[3]

Function: INPUT

When used inside a formula as a function, INPUT followed by a sub-command keyword reads the current state of the keyboard or joystick and returns a numeric value. The program continues to execute. This enables the player to control the player character in an arcade game program. It can also be used to write text input routines considerably more elegant and professional than can be achieved by using INPUT as a command.

Sub-command: ARROW

The INPUT command followed by the ARROW sub-command returns a value from 0 to 8 indicating the direction currently being pressed on the arrow keys and/or joystick. In the System/800, the keyboard arrow keys and joystick directions are indistinguishable at a hardware level, as they share the same address space in RAM. The fire button, however, does not share data with any key.

0 = no direction
1 = up
2 = right
3 = down
4 = left
5 = up-right
6 = down-right
7 = down-left
8 = up-left

If two opposing directions are pressed at the same time, they cancel each other out, resulting in the value 0. Example program lines:

LET D=INPUT ARROW
IF D=1 THEN Y=Y-1
IF D=3 THEN Y=Y+1

Sub-command: FIRE

The INPUT command followed by the FIRE sub-command returns 1 if the fire button is pressed, 0 if it is not. The fire button is independent of the keyboard. This allows the programmer to choose which key or keys to use as a game's fire button.

Sub-command: SPACE

The INPUT command followed by the SPACE sub-command returns 1 if the space bar is pressed, 0 if it is not.

Sub-command: CTRL

The INPUT command followed by the CTRL sub-command returns 1 if either control keys are pressed, 0 if neither are pressed.

Sub-command: ALPHA

The INPUT command followed by the ALPHA sub-command returns a value from 0 to 26 when one letter key is held down, with the value indicating the letter: 1 for A, 2 for B and so on up to 26 for Z. If either no letter key is held or more than one letter key is held, the returned value is 0.

Sub-command: DIGIT

The INPUT command followed by the DIGIT sub-command returns a value from 1 to 9 when one number key is held down, with the value indicating the number key. The 0 key is indicated by the value 10. If either no number key is held or more than one number key is held, the returned value is 0.

Sub-command: KEY

The INPUT command followed by the KEY sub-command returns 0 when no key is held and 1 when one or more keys are held. This is useful for creating lock variables to avoid a single key press triggering an action repeatedly.

Sub-command: ENTER

The INPUT command followed by the ENTER sub-command returns 1 if the ENTER key is held, 0 if it is not. This lets a program wait for the user to confirm or advance, without pausing as the INPUT command does.

Sub-command: ESCAPE

The INPUT command followed by the ESCAPE sub-command returns 1 if the ESCAPE key is held, 0 if it is not.

GRAPHICS

The System/800 has multiple screen modes and colour settings. These can be controlled with the commands MODE, CLEAR, BORDER and COLOUR.

Command: MODE

MODE determines how the System/800 displays graphics.

MODE 0 — the default Character Mode. Characters are 6 pixels wide and 8 pixels tall for readability. Each character can have its own paper and ink colour.

MODE 1 — wide Character Mode. Characters are 8x8 pixels, the standard for games programming. Each character can have its own paper and ink colour.

MODE 10 — Bitmap Mode with colour set per row. Each of the 22 rows can have its own paper and ink colour.

MODE 11 — Bitmap Mode with colour set per column. Each of the 42 columns can have its own paper and ink colour.

Modes 0 and 1 can collectively be considered Character Mode, modes 10 and 11 can be considered Bitmap Mode.

In Character Mode, the screen is a grid of character values. Each value is a reference into the System/800's character set's pixel patterns. When writing an arcade video game, you might redefine the pixel patterns to create walls that can be assembled to form a maze, and create monochrome character sprites for your player and enemies, then use the 16 hardware sprites to bring them to life with additional layers of colour.

For each screen position, the graphics hardware continuously looks up the pixel pattern of the relevant character and draws the pattern to the screen. This means that altering a character's pixel pattern instantly changes the appearance of each instance of that character on the screen. Think of the conveyor belt in a game like Manic Miner: once the belt has been drawn to the screen as a row of characters — the characters written to the screen's memory in series, likely using a loop — altering the character's pixel pattern will animate the entire conveyor belt, without the program again having to write the series of characters to the screen.

In Bitmap Mode, individual pixels can be cleared and set, allowing the programmer complete free range to draw lines and shapes to the screen.

Clear the screen with the CLEAR command after switching mode to avoid displaying visual noise.

Command: CLEAR

CLEAR clears the screen. In Character Mode, every position on the screen is replaced with a space, and the cursor is relocated to the top-left corner. In Bitmap Mode, every pixel is turned off. In both modes, the paper and ink colours for every row and column are reset to the default.

Command: BORDER

BORDER sets the colour of the border that surrounds the screen. The border exists in all modes. BORDER 2 sets the border to red.

The colour value corresponds to the System/800's 16-colour palette when in multicolour mode. In monochrome mode, changing the border colour has no visible effect.

Command: COLOUR

The COLOUR command controls whether the System/800 displays in Monochrome Mode or Multicolour Mode, and which of its two palettes it uses in Multicolour Mode.

The System/800 has separate colour control modes for each of its two display modes. In Character Mode, each cell on the screen has its own paper and ink colour. In Bitmap Mode, each row or column has its own colour combination: you cannot change the colours of individual character cells or pixels in this mode.

COLOUR MULTI — switch to 16-colour Multicolour Mode.
COLOUR MONO — switch to Monochrome Mode.

In Multicolour Mode, two colour palettes are available: the default Bright Palette featuring gentle pastel hues, and a Dark Palette.

COLOUR BRIGHT — select the bright palette.
COLOUR DARK — select the dark palette.

GRAPHICS: BITMAP MODE

The following commands control colours and draw pixels to the System/800's screen in Bitmap Mode. The bitmap is 336 pixels wide and 176 pixels tall. Pixel coordinates range from 0 to 335 horizontally and 0 to 175 vertically, with 0, 0 at the top-left corner.

Command: PAPER, INK

In Bitmap Mode, MODE 10 arranges the pixel rows into groups of 8, forming 22 8-pixel-high colour rows, while MODE 11 arranges the pixel columns into groups of 8, forming 42 8-pixel-wide colour columns.

PAPER sets the background colour, and INK sets the foreground colour.

When followed by one argument, PAPER and INK set the colour for every row or column on the screen at once:

PAPER 0
LET BYTE C_YLW=6 : INK C_YLW

When followed by two arguments, the commands set the colour of the stated row or column. In MODE 10, rows are numbered 0 to 21. In MODE 11, columns are numbered 0 to 41. The following example sets row or column 30 to red, depending on the mode — in MODE 10, no change will be visible because MODE 10 has only 22 rows:

INK 30 2

When followed by three arguments, the commands set the colour of a range of rows or columns. The first two values are the start and end of the range, the third is the colour. This example sets the paper colour for the third, fourth and fifth row or column to white, depending on the sub-mode:

PAPER 2 4 1

Command: PEN

The PEN command controls how drawing operations affect the pixels on the screen when a pixel, line or shape is drawn.

PEN OR — pixels are switched on. This is the default.
PEN XOR — pixels are toggled. Draw a pixel where a pixel is already present and the pixel will be erased.
PEN ERASE — pixels are switched off.

Command: SHAPE

The SHAPE command controls whether shapes are drawn as outlines or filled in.

SHAPE FRAME — draw outlines only. This is the default.
SHAPE FILL — draw filled shapes.

This setting affects RECT, CIRCLE and TRIANGLE. Each of these draw commands allows the SHAPE mode to be overridden by using FRAME or FILL as a sub-command.

Command: PIXEL

Draw a single pixel at the stated coordinates.

PIXEL 100 50
PIXEL X Y

Command: LINE

Draw a straight line between two points.

LINE 0 0 335 175
LINE X1 Y1 X2 Y2

Command: SET, DRAW
Sub-command: REL

The System/800 maintains a pen position that can be used for connected drawing. The SET command moves the pen to a position without drawing.

DRAW draws a line from the pen's current position to the stated coordinates, then moves the pen to those coordinates.

DRAW REL draws a line relative to the pen's current position. The values are added to the pen's coordinates.

This example draws a square by moving the pen in four directions from a starting point, then extends one corner to draw an additional line:

SET 50 50
DRAW REL 40 0
DRAW REL 0 40
DRAW REL -40 0
DRAW REL 0 -40
DRAW 50 60 : REM DRAW TO STATED COORDS

Sub-command: FRAME, FILL

When using the commands RECT, CIRCLE and TRIANGLE, keywords FRAME and FILL can be used as sub-commands to override the SHAPE settings by including the keyword after the command.

Command: RECT

Draw a rectangle from two corner coordinates. The first pair of values is the top-left corner, the second pair is the bottom-right corner.

RECT 10 10 100 80
RECT FRAME 10 10 100 80

Command: CIRCLE

Draw a circle by stating a centre point and a radius.

CIRCLE 168 88 40
CIRCLE FILL 168 88 40

Command: TRIANGLE

Draw a triangle from three vertex coordinates.

TRIANGLE 168 20 100 150 236 150

Command: COPY

Copy a rectangular region of the bitmap to another screen area. The first four values define the source rectangle, the last two values are the top-left corner of the destination.

COPY 0 0 63 63 100 50

The source and destination may overlap. Pixels are copied exactly, regardless of the current PEN setting.

GRAPHICS: CHARACTER MODE

In Character Mode, the screen is a grid of character values. Each value is a reference into the character set's pixel patterns. These patterns can be redefined to create custom character graphics.

For each screen position, the graphics hardware continuously looks up the pixel pattern of the relevant character and draws the pattern to the screen. Altering a character's pixel pattern instantly changes the appearance of each instance of that character on the screen.

Command: PAPER, INK

In Character Mode, PAPER and INK set the colour used by PRINT. Each character on the screen has its own ink and paper colour, which is set at the moment it is displayed depending on the colours set using the PAPER and INK commands.

INK 2 : PAPER 1 : PRINT "YOU'RE READING RED ON WHITE"

Screen Map

You can POKE directly to the screen map to position a character at a specific location. This can be more powerful than the PRINT command, and is therefore commonly used in games programming. The screen map starts at address 2816. In MODE 1, the screen is 42 columns wide, so each row of characters occupies 42 bytes. The following example inserts a random capital letter at the top-left corner of the screen, then copies the character to the position three rows further down.

MODE 1 : CLEAR : LET ROW=42
POKE 2816 (RND 26)+65
POKE 2816+(ROW*3) PEEK 2816

Character Definitions

The pixel patterns of the System/800's 256-character character set — which conforms to the ESSECSCII Standard, a 256-character set in which codes 0 to 127 follow ASCII — can be redefined to create custom character graphics.

The System/800's 256 character definitions are stored in memory starting at address 4096 and ending at 6143 — 256 characters of 8 bytes each. Character N starts at address 4096+(N*8). Each byte represents one row of pixels, with the leftmost pixel in the highest bit.

Changing the pixel pattern of a character instantly changes every instance of that character on the screen. For example, clearing the screen then changing the pixel pattern of the space character (ASCII 32) to resemble a smiley face will instantly fill the screen with smiley faces.

The following example redefines the capital letter O (ASCII 79) as a smiley face. The letter O starts at address 4096+(79*8)=4728. In the Windows calculator, choose Programmer Mode then click DEC and type e.g. 126 to see the binary pixel pattern for that decimal value.

POKE 4728 126 : REM 01111110
POKE 4729 129 : REM 10000001
POKE 4730 165 : REM 10100101
POKE 4731 129 : REM 10000001
POKE 4732 165 : REM 10100101
POKE 4733 153 : REM 10011001
POKE 4734 129 : REM 10000001
POKE 4735 126 : REM 01111110
PRINT "HELLO WORLD"

Use this principle in conjunction with loops and the DATA and READ commands to redefine the full character set.

GRAPHICS: CHARACTER MODE SCREEN SCROLL

The System/800 uses a hardware scrolling system similar to that used on Commodore's C64 home computer.

The C in C64 stands for "Consumer" (the company also planned a B64 for business users and an E64 for the education sector). While branding material on and after release — the user manual, and later the badges, for example — refer to the machine as the Commodore 64, none of this contradicts the company's assertion that the C stands for Consumer.

In Character Mode, the entire screen display can be shifted at a pixel level, from -4 to 3 pixels in any direction, or -3 to 2 pixels when shifting horizontally in Mode 0 as characters in this mode are 6 pixels wide.

The outermost rows and columns of the screen can be hidden to avoid partial-character artifacts at the screen edges.

Once the pixel offset has moved a full character (8 pixels, or 6 when scrolling horizontally in Mode 0), the programmer resets the pixel offset to the opposite end of its range, then shifts the screen contents in the same direction by one full character row or column — an action processed in an instant by the graphics hardware using a special command — and finally fills the vacated row or column with new content, effectively scrolling these characters onto the screen. Repeating this process in a loop scrolls the screen smoothly and continuously.

Command: SCROLL

Screen scrolling is controlled by the SCROLL command, followed by a sub-command. The values set by this command have no effect in Bitmap Mode, where the screen can be scrolled using the COPY command.

Sub-command: SHIFT

SCROLL SHIFT followed by a direction shifts the entire screen map and both colour maps (PAPER and INK) by one character cell in a given direction:

SCROLL SHIFT LEFT
SCROLL SHIFT RIGHT
SCROLL SHIFT UP
SCROLL SHIFT DOWN

SHIFT is effectively a character-copying operation. (More specifically, it is a value-copying operation, since it shifts both character data and colour data, but here we focus on the screen map.)

In the first example above, if the cell in the top-left corner of the screen contains a capital letter A (ASCII 65), the cell to its right contains a capital letter B (ASCII 66), and the cell to the right of that contains a capital letter C (ASCII 67), when the shift occurs the letter A is lost from the screen, the letter B taking its place as the value 66 is copied onto it, the letter B in the second cell is replaced by the letter C as the value 67 is copied onto it, and so on.

The vacated row or column, the right-most column in this example, is left untouched, retaining whatever character values were there before the shift. The programmer is responsible for filling in the new content.

A numeric value may be provided to shift by more than one character cell:

SCROLL SHIFT LEFT 3
SCROLL SHIFT DOWN ShiftDownSpeed

IMPORTANT: While it is easier to redraw the entire screen each loop, shifting the screen is more efficient. Your video game may run smoothly on your computer, but it may not run smoothly on a less powerful computer. Efficient programming is encouraged.

Sub-command: OFFSET

SCROLL OFFSET HORZ and SCROLL OFFSET VERT set the hardware pixel scroll offset for the entire screen display. Valid values range from -4 to 3—these eight values namely -4 -3 -2 -1 0 1 2 3 equate to one character's worth of pixels. SCROLL OFFSET HORZ in Mode 0 ranges from -3 to 2, as characters in this mode are 6 pixels wide. Setting a value outside the valid range results in the value 0.

SCROLL OFFSET HORZ 3
SCROLL OFFSET VERT ScrlVertOffset

This is the mechanism that, when used in conjunction with the manipulation of the screen map and colour maps, enables scrolling to appear smooth.

Sub-command: AREA

By default, the whole screen scrolls as one. SCROLL AREA divides the screen into a scrolling area and a fixed area, so that one part of the display can scroll while the rest remains unchanged. The fixed area might be used to display the player's score, a map and so on.

The sub-command takes a direction and, except for FULL, a number of rows or columns. The direction states where the scrolling area is to be located.

SCROLL AREA FULL — the whole screen scrolls. This is the default.
SCROLL AREA UPPER n — the top n rows scroll; the rest is fixed.
SCROLL AREA LOWER n — the bottom n rows scroll; the rest is fixed.
SCROLL AREA LEFT n — the leftmost n columns scroll; the rest is fixed.
SCROLL AREA RIGHT n — the rightmost n columns scroll; the rest is fixed.

The SHIFT and OFFSET sub-commands act on the scrolling area alone, leaving the fixed area unchanged.

SCROLL AREA LOWER 20 : REM TOP ROWS MIGHT HOLD A STATUS PANEL
SCROLL AREA FULL : REM RETURN TO SCROLLING THE WHOLE SCREEN

Sprites can be confined to the scrolling area or to the fixed area. This is controlled by the SPRITE CLIP command, discussed in the section GRAPHICS: SPRITES.

Sub-command: MASK

Imagine a photo in a frame with a rectangular opening the same size as the photo. You slide the photo an eighth of an inch to the left, so that the left-hand eighth of an inch is hidden behind the left side of the frame. Thanks to the image sliding beneath the frame, the left-hand edge will remain perfectly neat as you continue to slide the photo to the left.

The problem occurs on the right, where a gap appears. With each movement of the photo, as the gap widens, the right side of the photo sits at a different location. Visually, in terms of scrolling, this creates a jittery effect, or a jagged edge.

The solution is to widen the right-hand section of the frame, the right border, to ensure that the gap always remains hidden. Of course, if we wish to slide the photo in both directions, we need to widen the frame on the left also. The same principle applies vertically.

The System/800's MASK sub-command hides the outer edges of the scrolling area. This command takes one of four values:

SCROLL MASK OFF — no masking. This is the default.
SCROLL MASK HORZ — mask the left and right edges, for horizontal scrolling.
SCROLL MASK VERT — mask the top and bottom edges, for vertical scrolling.
SCROLL MASK BOTH — mask all four edges.

GRAPHICS: SPRITES

The System/800 has 16 hardware sprites, numbered 0 to 15. A hardware sprite is a 16x16 pixel image drawn independently of the character map or bitmap, and can be positioned directly via its own coordinates. Each sprite is a single colour, with the 0 pixel treated as transparent. This means that you can effectively create a multicoloured sprite by positioning several sprites at the same position.

Every sprite command can act on one or more sprites. Follow the command and any sub-commands with a single sprite number (zero-indexed, i.e. 0-15), a comma-separated range of sprite numbers, or ALL to affect every sprite at once.

Command: SPRITE

The SPRITE command, followed by a sprite number and two coordinates, sets a sprite's position. The position refers to the sprite's top-left corner. An x or y of 0 positions the sprite off-screen.

SPRITE 0 16 16 : REM TOP-LEFT CORNER
SPRITE 0 100 80 : REM PART-WAY ACROSS THE SCREEN
SPRITE 0 X Y : REM USING VARIABLES

Sub-command: COLOUR

The colour of each individual System/800 sprite can be set to any of the colours in the computer's palette. The examples below all set sprites to red. The first example affects only the fourth sprite.

LET CRED=2 : SPRITE COLOUR 3 CRED
SPRITE COLOUR 4,6 2 : REM SPRITE 4, 5 AND 6
SPRITE COLOUR ALL 2 : REM ALL 16 SPRITES

Sub-command: FRAME

A sprite's shape is defined by an image called a frame. The command SPRITE FRAME assigns a specific frame to a sprite. Frames are stored in an area of memory called a bank. The computer has eight banks, numbered from 0 to 7. Bank 0 is available by default, and holds only 20 sprite frames. Banks 1 to 7 each hold 32 frames.

Sprite frames are referenced by a number counting up from 0, starting from the first frame in bank 0, so, with the first bank holding only 20 frames numbered 0-19, the third frame in the second bank is numbered 22.

Any sprite can be assigned any sprite frame. Animating a sprite is simply a matter of changing its frame over time and/or in certain contexts.

SPRITE FRAME 2 5 : REM SHOW FRAME 5 ON SPRITE 2
SPRITE FRAME ALL 9 : REM ALL SPRITES GET FRAME 9
SPRITE FRAME 0 XY : REM USING A VARIABLE YOU CONTROL ELSEWHERE

Sub-command: FLIP

SPRITE HORZ FLIP mirrors a sprite left to right, and SPRITE VERT FLIP mirrors it top to bottom. The two axes are independent, so a sprite can be flipped on either or both axes, and UNFLIP returns an axis to normal.

SPRITE HORZ FLIP 0 : REM SPRITE 0 MIRRORED LEFT-RIGHT
SPRITE VERT FLIP 4,6 : REM SPRITES 4, 5 AND 6 UPSIDE DOWN
SPRITE HORZ UNFLIP ALL : REM UN-FLIP EVERY SPRITE

Sub-command: EXPAND

SPRITE EXPAND doubles a sprite's size, from 16x16 to 32x32 pixels; SPRITE HORZ EXPAND doubles the width only, and SPRITE VERT EXPAND the height only. Each pixel simply becomes twice as large, so the sprite gains no extra detail. UNEXPAND returns a sprite, or one axis, to normal size.

SPRITE EXPAND 0 : REM SPRITE 0 BECOMES 32x32
SPRITE VERT EXPAND 4,6 : REM SPRITES 4, 5 AND 6 GROW TALL
SPRITE UNEXPAND ALL : REM EVERY SPRITE BACK TO NORMAL

Sub-command: EXTEND

As described above, sprite frames are stored in areas of memory called banks. At program start, only bank 0 is present, holding 20 frames. SPRITE EXTEND adds one further bank of 32 frames each time it is called. The command takes no arguments; to add five banks, for example, include the command five times.

Banks can only be added, never removed, and a program may add at most 7 banks, giving 8 banks in total, bank 0 plus the added banks. Add the banks you need before loading frames into the sprite banking area.

Each added bank occupies memory that would otherwise be available to the program. To make room for the additional banks, SPRITE EXTEND relocates the running program and its variables to later in the computer's RAM. This is automatic and does not affect the running of the program. Should the program be too large to leave room for another bank, the command fails with the error OUT OF MEMORY.

SPRITE EXTEND : REM ADD ONE BANK OF 32 FRAMES (FRAMES 20-51)
SPRITE EXTEND : SPRITE EXTEND : REM ADD TWO BANKS IN ONE LINE

Sub-command: CLIP

When the screen is divided into a scrolling area and a fixed area using SCROLL AREA, SPRITE CLIP can be used to confine a sprite to either of the two areas. The command takes a sprite number followed by one of three options:

SPRITE CLIP 0 OFF — sprites can appear anywhere by default.
SPRITE CLIP 0 SCROLL — the sprite is confined to the scrolling area.
SPRITE CLIP 0 FIXED — the sprite is confined to the fixed area.

Confining does not affect the position of the sprite, areas of the sprite that stray beyond the specified area are simply not drawn.

Like the other sprite commands, SPRITE CLIP can act on a single sprite, a range, or on all of the sprites using ALL. Sprites are not clipped in Bitmap Mode, which has no scrolling area.

SPRITE CLIP PLAYER SCROLL : REM KEEP THE PLAYER IN THE PLAYFIELD
SPRITE CLIP 0,3 FIXED : REM SPRITES 0-3 STAY IN THE FIXED AREA
SPRITE CLIP ALL OFF : REM REMOVE ALL CLIPPING

Function: SPRITE HIT

SPRITE HIT is a function, not a command — rather than perform an action, it returns a value, which can be acted on immediately or stored in a variable.

Follow the function name SPRITE HIT with two sprite numbers, and the function will return 1 if the two sprites overlap, and 0 if they do not.

The test is made at the pixel level: two sprites are considered to be overlapping only when one or more of the solid (on) pixels of one sprite overlap with one or more of the solid pixels of the other sprite. Two sprites are never considered to be colliding when either or both sprite has its x or y coordinate set to 0.

IF SPRITE HIT 0 1 THEN GOSUB EXPLODE
LET STRUCK=SPRITE HIT PACMAN GHOST_SHADOW

INCLUDE FILES

The INCLUDE command inserts the entire contents of a file into your program at the point where the statement appears, at the moment that the program is compiled into memory, exactly as if the program lines appeared in the program directly. The command must be followed by the name of the file, enclosed in double quote marks. An included file can have any extension, or no extension at all.

INCLUDE "GRAPHICS.s8b"
INCLUDE "room1.dat" : INCLUDE "room2.dat" : INCLUDE "room3.dat"
INCLUDE "process_reset"

The same file may be included any number of times, though including a file multiple times may cause errors, for example, if the file defines one or more labels. An included file cannot itself contain an INCLUDE command.

MACROS

A macro is a file, typically small, with the extension .s8bm (System/800 BASIC macro) that you call by name as though it were a built-in command. When the program runs, the macro name is replaced by the contents of the macro file. Unlike including a file with the INCLUDE command, there is no command. A file named PRINT_SCORE_BAR.s8bm that calculates the player's score and prints both the score and the high score might be called with the following line:

PRINT_SCORE_BAR

Macros can take parameters to change their behaviour. In the macro file, type a parameter name surrounded by pointy brackets wherever you require substitution to occur. When you call the macro, follow the macro name with the name of the parameter, followed by an equals sign, followed by the characters that are to replace the parameter reference.

A macro can take up to eight parameters. When the macro is called, the parameters can be entered in any order, but every parameter that the macro uses should be supplied exactly once.

For example, a file called ASSERTION.s8bm contains just one line:

DO <<<AMOUNT>>> : PRINT "<<<NAME>>> IS COOL" : LOOP

This macro might be called in a number of ways, such as:

ASSERTION NAME=AIDEN AMOUNT=5
ASSERTION AMOUNT=3 NAME="METAL MICKEY"

The first example will insert the following line into the program, printing the quoted text down the screen five times:

DO 5 : PRINT "AIDEN IS COOL" : LOOP

If a parameter value contains a space, wrap it in double-quote characters as in the second example. The quote marks are not inserted into the program. The second example produces:

DO 3 : PRINT "METAL MICKEY IS COOL" : LOOP

Unlike INCLUDE files, macros cannot contain labels or the commands LET, GOTO, GOSUB, RETURN, STOP, DATA, READ, or INPUT. A macro cannot call macros.

IMPORTING SPRITES AND CHARACTERS

Rather than define sprites or characters (your character set) manually within your program, you can draw your artwork as an indexed-colour bitmap image (a .bmp file) in an image-editing program such as Aseprite and convert the image into a series of EBM BASIC data statements using the IMPORT command.

The command creates a text file that you can add to your program using the INCLUDE command. The image data can then be read into memory as sprite frames or character definitions.

The IMPORT command is typed directly into Terminal Mode.

Command: IMPORT

Follow the IMPORT command with the image type: SPRITE or CHAR. When importing a character set, the cell size 6X8 or 8X8 must then be stated. The name of the bitmap file must then be stated, enclosed in double quote marks. This can be followed by a name for the output file. When the output filename is omitted, the bitmap file's name is used with the extension .s8b The second example below will output a file with the name font.s8b

IMPORT SPRITE "mario.bmp" "MarioSpriteFrames.dat"
IMPORT CHAR 8X8 "font.bmp"

A sprite bitmap's width and height must both be multiples of 16, the width and height of a System/800 sprite, and therefore of a System/800 sprite frame.

A character bitmap's width must be a multiple of the cell width i.e. 6 or 8, and its height must be a multiple of 8. Where a bitmap holds a grid of several sprite frames or characters, the individual frames or images are numbered from the top-left.

Colour Options

The COLOUR and PAPER settings described below each set how colours in the bitmap image are used by the System/800. A colour may be stated by name, as a decimal value, or as a hexadecimal value. A decimal value is three numbers between 0 and 255 — the red, green and blue components — separated by colons. A hexadecimal value is a dollar sign followed by six hexadecimal digits, two for each component. Each of the three options below specifies magenta:

MAGENTA
255:0:255
$FF00FF

The System/800 recognises five colours by name. Their values are listed below:

BLACK — 0:0:0 — $000000
WHITE — 255:255:255 — $FFFFFF
GREY — 119:119:119 — $777777
GREEN — 0:255:0 — $00FF00
MAGENTA — 255:0:255 — $FF00FF

Do ensure that each colour in the bitmap is the precise colour shade intended. Some image editors, such as Aseprite, show HSV values (hue, saturation and value) by default, rather than the RGB values (red, green and blue) used here. You may end up with a colour that resembles the intended shade but is in fact a different but similar shade, the difference often being invisible to the eye, and which therefore will not be recognised by the IMPORT command.

Sub-command: COLOUR

The System/800's 16 sprites are displayed as independent monochrome images, each pixel either drawn or left transparent. IMPORT SPRITE COLOUR is used to determine which of the bitmap image's colours is to be left transparent. The default is BLACK. If the colour does not appear in the bitmap image, the result will be an error message.

Multicoloured sprites can, in effect, be created by positioning multiple sprites of different colours at the same position. For this reason, multicoloured bitmap images can be imported, and the image will be separated into separate frames for each colour. Colours that exist in the bitmap file's image data but that are not used in the actual drawn image are ignored.

Each of the examples below sets the sprite transparency colour:

IMPORT SPRITE COLOUR MAGENTA
IMPORT SPRITE COLOUR GREEN
IMPORT SPRITE COLOUR 3:22:111
IMPORT SPRITE COLOUR $A1B2C3

Sub-command: PAPER

A character does not exist as a single block of colour, but as a pattern of paper (background) and ink (foreground) pixels. IMPORT CHAR PAPER states which of the bitmap image's colours is to provide the paper colour. All other colours are treated as ink. The default is BLACK, and the colour is stated in the same way as the sprite transparency colour.

IMPORT CHAR PAPER WHITE
IMPORT CHAR PAPER BLACK
IMPORT CHAR PAPER 119:119:119
IMPORT CHAR PAPER $00FF00

Sub-command: ORDER

When a bitmap holds a grid of sprite frames or characters, ORDER sets whether the individual frames or characters are read row by row or column by column, i.e. whether we read along each row in turn, or down each column in turn. HORZ, the default, reads by row, VERT reads by column. A bitmap consisting of a single row or column reads the same in either case.

IMPORT SPRITE ORDER VERT
IMPORT CHAR ORDER HORZ

The transparency, paper and order settings are remembered between sessions.

SOUND

The System/800 has a sound chip with a unique set of voices.

Command: BEEP
Command: MEEP

The two basic commands, BEEP and MEEP, play short sounds in two tones. BEEP plays a high beep, and MEEP plays a deeper tone.

Either sound can be shaped with two additional values, both of which are optional. A letter from A to G provides the musical note, while a number from 0 to 8 provides the octave. Both parameters are optional, but when both are provided, the letter must be provided before the number.

BEEP C : REM NOTE C, USUAL OCTAVE
MEEP G 4 : REM NOTE G, OCTAVE 4
BEEP 7 : REM DEFAULT NOTE, OCTAVE 7
MEEP A OCT : REM OCTAVE FROM A VARIABLE

Command: SOUND

SOUND plays one of several possible sound patterns that slide up and/or down, ideal for game sound effects. The sound pattern continues to play as your program runs, similar to an interrupt on the C64. The patterns are played by following the SOUND command with one of 16 sub-commands.

Sub-command: RISE
Sub-command: SOAR
Sub-command: SLIDE

RISE, SOAR and SLIDE climb in pitch: RISE climbs steadily, SOAR speeds up over time, and SLIDE eases off towards the top.

Sub-command: POINT
Sub-command: HILL
Sub-command: CUT
Sub-command: VALLEY

POINT, HILL, CUT and VALLEY rise and fall within the one effect: POINT is a sharp peak, HILL a rounded hump, CUT a sharp dip, and VALLEY a rounded dip.

Sub-command: WAVE
Sub-command: TRILL
Sub-command: SPRING
Sub-command: SPIRAL

WAVE, TRILL, SPRING and SPIRAL wobble: WAVE rolls slowly, TRILL flutters quickly, SPRING bounces and settles, and SPIRAL climbs with a wobble.

Sub-command: PULSE
Sub-command: BUZZ
Sub-command: CRASH
Sub-command: RANDOM
Sub-command: SPARKLE

PULSE, BUZZ, CRASH, RANDOM and SPARKLE are flat or noisy: PULSE holds one steady note, BUZZ is a steady rasp, CRASH a falling smash, and RANDOM and SPARKLE scatter their notes about, with SPARKLE emphasising the high notes, though the exact character of each depends on the voice you choose.

Sub-command: INV

INV can optionally be included after the pattern name. This sub-command turns the pattern upside down, for example, a rising pattern becomes a falling pattern.

SOUND RISE INV : REM A RISE FLIPPED INTO A FALL
SOUND HILL INV : REM A HUMP FLIPPED INTO A DIP

Sub-command: VOICE

The System/800 has three sound voices, and each has its own fixed sound: voice 0 is a clean square tone, voice 1 a buzzy sawtooth, and voice 2 is noise. Follow the pattern with VOICE and a number from 0 to 2 to choose the voice it plays on. Without VOICE, the pattern plays on voice 0.

SOUND CRASH VOICE 2 : REM A NOISY SMASH
SOUND RISE VOICE 1 : REM A BUZZY RISE

Count and Speed

The shape (and the INV sub-command when included) can optionally be followed by one or two numbers. The first number sets the number of notes, from 1 to 8 (8 by default). A second number, when included, sets the speed (normally 5), 1 (slow) to 9 (fast).

SOUND RISE : REM A SIMPLE RISING EFFECT
SOUND CRASH INV : REM CRASH, FLIPPED UPSIDE DOWN
SOUND SPARKLE 8 9 : REM 8 NOTES, FAST
SOUND WAVE INV 8 3 : REM FLIPPED, 8 NOTES, SLOW