If you want to use this language, then start your program with a #!bas
line.
#!bas
REM global variables
LET acounter% = 123
LET anumber = 3.1415
LET anaddress% = $0048C
LET astring$ = "something"
DIM anarray(10)
REM Things to do on startup
SUB setup
REM local variables
LET iamlocal = 123
END SUB
REM Things to run for every frame, at 60 FPS
SUB loop
REM BASIC style print
PRINT "I"; " am"; " running"
REM Get MEG-4 style outputs
printf("a counter %d, left shift %d\n", acounter%, getkey%(KEY_LSHIFT))
END SUB
BASIC stands for Beginners' All-purpose Symbolic Instruction Code. It was created by John Kemeny in 1963 with the explicit goal
to teach students programming with it. The MEG-4 BASIC is a bit more modern than that, it supports all of ANSI X3.60-1978
(ECMA-55) and many featues from ANSI X3.113-1987 (ECMA-116) too, with minor deviations. It allows longer than 2 characters
identifiers, and its floating point as well as integer arithmetics are 32 bit. Most important differences: no interactive mode,
therefore you don't have to number each instruction any more (you can use labels instead), and all BASIC keywords are
case-insensitive as in the specification, but variable and function names are case-sensitive. The MEG-4 API function calls
must be in lower-case; as for the rest it is up to you, but those are case-sensitive too (for example APPLE
, Apple
and
apple
are three distinct variables).
It has one non-standard keyword, the DEBUG
, which you can place anywhere in your code and will invoke the built-in
[debugger]. After this, you can execute your code step by step, watching what it is doing.
Here goes a detailed description with examples, and all differences noted.
MEG-4 BASIC understands decimal based numbers (either integer or floating point with or without scientific notation). Hexadecimal
numbers must be prefixed by $
(not in the specification, but this was usual in Commodore BASIC and pretty much in every other
dialects of the '80s) and strings must be enclosed in double quotes:
42
3.1415
.123e-10
$0048C
"Goodbye and thanks for all the fish!\n"
WARN: The specification expects 7-bit ASCII, but MEG-4 BASIC uses zero terminated UTF-8 encoding. It also accepts C-like escape
sequences (eg. \"
is double quotes, \t
is tab, \n
is the newline character), and the string's maximum size is limited to
255 bytes (the specification requires 18 bytes).
Variables aren't declared, instead the last letter in their name identifies their type. This can be %
for integers, $
for
strings, and not in the specification, but MEG-4 BASIC accepts !
for bytes and #
for double bytes (word). Anything else is
interpreted as a floating point variable.
LET A% = 42
LET B! = $FF
LET C# = $FFFF
LET D$ = "string"
LET E = 3.1415
The conversion between byte, integer and floating point is automatic and fully transparent. However trying to use a string literal
in a number variable or storing a number literal in a string variable would reasult in an error (you must explicitly use STR$
and VAL
).
When you assign values to variables, the LET
command can be omited.
Literals can also be added to your program using the DATA
statement, and then can be assigned to variables with the READ
command. READ
reads in as many data literals as many variables its argument has, and can be called repeatedly. To reset to the
first DATA
statement where READ
reads from, use RESTORE
.
RESTORE
READ name$, income
DATA "Joe", 1234
DATA "John", 2345
There are a few special variables, provided by the system. RND
returns a floating point random number between 0 and 1,
INKEY$
returns the key the user has pressed or an empty string, TIME
returns the number of ticks (1/1000th seconds) since
power on, and finally NOW%
returns the number of elapsed seconds since Jan 1, 1970 midnight in Greenwich Mean Time.
Multiple elements of the same type can be assigned to a single variable, which is called an array.
DIM A(10)
DIM B(10, 10)
DIM C(1 TO 6)
The BASIC specification expects two-dimensional arrays to be handled, but MEG-4 BASIC supports up to 4 dimensions. Array elements
can be bytes, integers, numbers or strings. Dynamically resizing arrays with REDIM
is not possible, all are statically allocated
using DIM
. When the size isn't given, one dimension and 10 elements are assumed.
WARN: Index starts at 1 (as in ANSI and not at 0 as in ECMA-55). The OPTION BASE
statement isn't supported, but you can set the
first index of each array with the TO
keyword.
In descending order of precedence:
**Arithmetic** ``` ^ * / MOD + - ``` The first one is for exponentiation (eg. b squared is `b^2`), multiplication, division and `MOD` is modulus (aka. remainder after division, eg. `10 MOD 3` is 1), addition and subtraction.
**Relational** ``` <> = <= >= < > ``` Not equal, equal, less or equal, greater or equal, less than, greater than.
**Logical** ``` NOT AND OR ``` Logical negation (0 becomes 1, everything non-zero becomes 0), and (returns 1 if both arguments are non-zero), or (returns 1 if at least one of the arguments are non-zero).
There's one non-standard operator, the @
returns the address of a variable. This is usable when the MEG-4 API expects an
addr_t
address parameter.
Operators are executed in precedence order, for example in 1+2*3
we have two operators, +
and *
, but *
has the higher
precedence, therefore first 2*3
is calculated, and then 1+6
. That's why the result is 7 and not 9.
The statement END
stops control flow (exists your program).
MEG-4 BASIC does not use line numbers any more, instead it supports GOTO
with labels, for example:
this_is_a_label:
GOTO this_is_a_label
WARN: Some BASIC dialects allow you to use multiple commands separated by :
in one line. In MEG-4 BASIC :
identifies
a label, so you must use one command per line (as expected by ECMA-55).
Conditional jumps use labels too:
IF a$ <> b$ THEN this_is_a_label
ON a GOTO label1, label2, label3 ELSE labelother
The ON
.. GOTO
always needs a numerical expression, and chooses the label accordingly, starting from 1 (if the expression
is zero or negative, that always jumps to the ELSE
label). There's no ON
.. GOSUB
, because GOSUB does not accept labels in
MEG-4 BASIC.
WARN: The GOSUB
statement does not accept labels at all, and its semantics are a bit changed in MEG-4 BASIC, see below.
For IF
, both numerical and relational expressions are accepted (every non-zero expression considered true), and what's more,
multi line IF
.. THEN
.. ELSE
.. END IF
blocks are supported too (but no SELECT CASE
).
IF var >= 0 THEN
PRINT "var is positive"
ELSE
PRINT "var is negative"
END IF
As an exception, one command is allowed in a single line IF
, provided that's a GOTO
or an END
:
IF a < 0 THEN GOTO label
IF b > 42 THEN END
For iterations, the counting loop checks its condition before the iteration (does not execute the block if the initial value is greater (or less) than the limit), and looks like this:
FOR i = 1 TO 100 STEP 2
...
NEXT i
This FOR
.. NEXT
is essentially the same as:
LET i = 1
LET lim = 100
LET inc = 2
line1:
IF (i - lim) * SGN(inc) > 0 THEN line2
...
LET i = i + inc
GOTO line1
line2:
The loop variable must be of float type. The STEP
is optional (defaults to 1.0), and the expression after that can be a float
literal or another float variable. The from and limit both can be more complex expressions, but they too must return a floating
point value.
FOR i = (1+2+a)*3 TO 4*(5+6)/b+c STEP j
...
NEXT i
WARN: Unlike the specification, which allows multiple variables after NEXT
, MEG-4 BASIC accepts exactly one. So for
nested loops you'll have to use multiple NEXT
commands, exactly as many as FOR
statements there are.
FOR y = 1 TO 10
FOR x = 1 TO 100
...
NEXT x
NEXT y
MEG-4 BASIC has no other kind of loops like C, but if you want a non-counting pre-testing loop, you can do this:
again:
IF a > 0 THEN
...
GOTO again
END IF
And instead of a post-testing loop:
again:
...
IF a > 0 THEN again
INFO: Statements not inside of any subroutine are simply threated as if they were inside the setup
subroutine.
You can divide your programs into smaller programs which might be called multiple times, these are called subroutines. They are
defined between SUB
and END SUB
blocks. Two of these, setup
and loop
has special meaning, see [code editor]. As mentioned
earlier, in MEG-4 BASIC GOSUB
does not accept labels, and that's because here it accepts subroutine names only.
SUB mysubroutine
PRINT "do something that you want to do multiple times"
END SUB
REM do it once
GOSUB mysubroutine
REM then do it again
GOSUB mysubroutine
Control is transferred on GOSUB
, and it is returned to the line after GOSUB
when END SUB
(or the optional RETURN
) reached.
Subroutines can access global variables and they might have parameters.
SUB are_we_there_yet(A)
IF A > 0 THEN
PRINT "Not yet"
RETURN
END IF
PRINT "YES! Do things we wanted to do on arrival"
END SUB
REM do it once
GOSUB are_we_there_yet(1)
REM then do it again
GOSUB are_we_there_yet(0)
Functions are very similar, but they must have a RETURN
and their RETURN
statement must contain a returned value, same type
as identified by the function's name. Functions are simply called from programs by their names, followed by their argument list
in parenthesis (the parenthesis is mandatory, even if the argument list is empty). For example:
FUNCTION mystringfunc$()
RETURN "a string"
END FUNCTION
LET a$ = mystringfunc$()
PRINT expression [;|,] [expression [;|,] [expression [;|,]]] ...
Prints one or more exporession on screen. If the expressions are separated by ;
semi-colon, then tightly one after another.
If by ,
colon, then the output will be splitted into coloumns. Numbers are always prefixed by a space, and if the command ends
in an expression (not in ;
nor in ,
), then a newline character will be printed at the end as well.
INPUT "prompt" [;|,] variable
Prints out prompt, then reads in a value from user and stores it in the given variable. If the prompt and the variable is
spearated by a ,
colon, then it also prints a ?
question-mark after the prompt.
WARN: The ECMA-55 specification allows multiple variables to be specified, but the MEG-4 BASIC allows only one.
You can directly access the MEG-4 memory with these commands, even the MMIO area.
Read
variable = PEEK(address)
Reads in the byte at the given address, converts it into a floating point and stores it in the given variable.
For example, to check if the keyboard queue is not empty:
IF PEEK($1A) <> PEEK($1B) THEN
Store
POKE address, expression
Calculates the expression, converts it into a byte and stores that byte value at the given address.
For example, to set the palette for color index 1:
REM red component
POKE $84, 10
REM green component
POKE $85, 10
REM blue component
POKE $86, 10
REM alpha (transparency)
POKE $87, 255
Some MEG-4 API are provided as system variables, RND
([rnd]), TIME
([time]), NOW%
([now]), and INKEY$
([getc]).
Others are provided as commands, INPUT
([gets] + [val]), PRINT
([printf]), PEEK
([inb]), POKE
([outb]).
As to comply with ECMA-55, two functions are renamed: SQR
([sqrt]) and ATN%
([atan]). All the rest is used as they appear
in this documentation, except they are properly suffixed according their return types (for example, [str] returns a string, so
it is called STR$
).
Note that ECMA-55 expects that trigonometrical functions use radians by default (with an OPTION
command to switch to degrees),
however the MEG-4 API always uses degrees, 0 to 359, where 0 is up, and 90 is to the right. That's why the ATN%
gets an
integer type suffix for example, as it returns degrees in an integer.
Normally all functions without a return value must be called with the GOSUB
keyword, however the MEG-4 API is a special case,
because with those the keyword can be ommited, and the API function can be used as-is.