BASIC interpreter and linker section
Here is a summarized guide for the Snowdrop OS BASIC interpreter. Like all other Snowdrop OS applications, I wrote it in assembly language.
This BASIC interpreter opens the door to simple software development from within Snowdrop OS.
There are a few ways to run BASIC programs:
1. Directly from the text editor
2. Invoking the BASICRUN application from the command line
3. Creating a standalone application from a source file, using the BASICLNK linker
A few demo BASIC programs (extension .BAS) are included with Snowdrop OS. Instructions end with either semicolons or new lines. Thus, semicolons allow for multiple instructions on a single line.
I've designed Snowdrop OS BASIC mostly starting from Sinclair BASIC, a dialect for an 80s British 8bit computer called ZX Spectrum.
Additionally, I've integrated the GUI framework with BASIC. This allows programs to easily add buttons, checkboxes, etc. and respond to events (clicks, selection/deselection, etc.).
All GUI framework wire-up, background/sprite state, etc. is abstracted away, greatly simplifying BASIC GUI programs.
BASIC-GUI framework integration
Version 21 of Snowdrop OS introduces an integration which allows BASIC programs access to the GUI framework. BASIC programs are now able to add GUI elements such as buttons, checkboxes, etc. User interactions with the interface (clicking buttons, checking/unchecking checkboxes) re-enter the BASIC program, causing BASIC statements to be executed.
There are three fundamental changes in BASIC which allow this:
First, the keyword YIELD has been introduced. YIELD causes a BASIC program to relinquish control. In the current implementation, YIELD is relied upon to give control back to the GUI framework once BASIC has finished acting in response to an user action, such as a button click. This is new; previously, BASIC programs ran to completion (or error) without ever giving up control.
The second change was the addition of "agreed upon" labels which act as the re-entry points into the BASIC program, depending on which event took place. For example, there's such a label for button clicks and one for images becoming selected. If the BASIC program doesn't contain the label for button clicks, then button clicks do nothing.
The third addition was the function GUIACTIVEELEMENTID, which returns the handle of the GUI element (button, checkbox, etc.) which raised the event. The purpose of GUIACTIVEELEMENTID is to differentiate between multiple possible buttons who could've raised the "button clicked" event.
These changes essentially allow control to "ping-pong" between BASIC and the GUI framework.
Needless to say, comparatively concise BASIC programs can now create GUI applications.
Labels
Labels are placed before instructions, "marking" places in the program, to be referenced by GOTO, CALL, etc.
Example (the label is myLoop):
myLoop: PRINT "Hello"; GOTO myLoop;
See the GUI section below for labels specific to the GUI framework.
Integers
BASIC integers range from -32,768 and 32,767.
Strings
String literals use double quote delimiters, for example: "Hello, world!"
Expressions
Expressions support a maximum of one operator or function. They can take the forms:
[value]
[value] [operator] [value]
[operator] [value]
[function]
[function] [value]
[function] [value], [value]
[function] [value], [value], [value]
where [value] can be literal ("one", "two", 0, 1337), or a variable.
Arithmetic and logic operators
+ integer addition and concatenation between a string and a second value
- integer subtraction
* integer multiplication
/ integer division quotient
% integer division modulo
= logical equality
> numerical greater than
< numerical less than
<= numerical less than or equal to
>= numerical greater than or equal to
<> numerical different than
AND logical
OR logical
XOR logical
NOT logical
BITAND bitwise AND
BITOR bitwise OR
BITXOR bitwise XOR
[x] BITSHIFTL [y] bitwise shift left [x] by [y] places
[x] BITSHIFTR [y] bitwise shift right [x] by [y] places
[x] BITROTATEL [y] bitwise rotate left [x] by [y] places
[x] BITROTATER [y] bitwise rotate right [x] by [y] places
Functions
LEN [expression]
returns the length of the specified string
RND [expression]
returns a random integer between 0 and [expression]-1, inclusive
KEY [expression]
returns true when the key with the specified scan code is currently pressed
CHR [expression]
converts a numeric ASCII value to a string with the character with that ASCII
ASCII [expression]
returns the numeric ASCII value of the character in a single-character string
VAL [expression]
returns the number represented by the specified string
BIN [expression]
returns the numeric value of the binary number specified as a string
SERIALDATAAVAIL
returns true when serial port data is available for reading with SERIALR
CHARAT [row expression], [column expression]
returns a string containing the character at the specified location on screen
SUBSTRING [string expression], [start expression], [length expression]
returns a substring from the specified start, with the specified length
STRINGAT [string expression], [index expression]
returns a string containing the one character in the input string at the specified index
Keywords
PRINT [expression]
writes the textual representation of [expression] to screen
PRINTLN [expression]
like PRINT, but also moves to a new line after writing
PRINTLN
moves cursor to a new line
LET [variable] = [expression]
assigns a value to a variable
GOTO [label]
moves execution to another point in the program, as specified by the label
REM "comment"
inserts comments in the program
FOR [variable] = [expression] TO [expression]
marks the beginning of a loop; each iteration increments counter by one
FOR [variable] = [expression] TO [expression] STEP [expression]
marks the beginning of a loop; each iteration adds the step value to counter
NEXT [variable]
jumps to the corresponding FOR instruction, to perform the next loop iteration
IF [expression] THEN [instruction]
executes the instruction when the expression is true (non-zero)
IF [expression] THEN [instruction1] ELSE [instruction2]
executes instruction1 when the expression is true (non-zero)
executes instruction2 when the expression is false (zero)
CALL [label]
like GOTO, but allows the code to RETURN to right after CALL
RETURN
returns to right after the last CALL
INPUTS [variable]
prompts the user to enter a string from the keyboard, assigning it to the variable
INPUTN [variable]
like INPUTS, but for numbers
AT [row expression], [col expression]
moves the cursor to the specified row and column
COLOURS [font expression], [background expression]
changes the current colours as specified
PAUSE [expression]
pauses for the specified number of centiseconds (hundredths of a second)
WAITKEY [variable]
waits for the user to press a key and assigns its string value to the variable
NOOP
does absolutely nothing; used to simplify IF statements
BEEP [frequency expression], [duration expression]
emits a sounds over the internal speaker, moving immediately to the next instruction
frequency number is taken from the documentation for int 89h
duration is given in centiseconds (hundredths of a second)
BEEPW [frequency expression], [duration expression]
like BEEP, but pauses program execution for the duration of the sound
STOP
stops program execution
CLS
clears screen using the current font and background colours
PARALLELW [register expression], [value expression]
writes the specified value to the specified parallel port register
SERIALW [value expression]
writes the specified value to the serial port
SERIALR [variable]
blocks until a byte is available from the serial port and reads it into the variable
if SERIALDATAAVAIL is checked before and found true, SERIALR will read and not block
Sample program
readNumber:
PRINT "Input a number between 1 and 150: ";
INPUTN number;
PRINTLN;
LET withinLowerBound = number >= 1;
LET withinUpperBound = number <= 150;
IF withinLowerBound AND withinUpperBound THEN GOTO valid;
IF NOT withinLowerBound THEN PRINTLN "Too low, try again";
IF NOT withinUpperBound THEN PRINTLN "Too high, try again";
GOTO readNumber;
valid:
LET square = number * number;
PRINT "Its square is " + square;
GUI-specific keywords, functions, and labels
YIELD
gives up control; used after the program has finished acting in response to an event
GUIBEGIN [title]
prepares the GUI framework for usage; must be used before any other GUI instructions
GUIACTIVEELEMENTID
returns handle of GUI element which caused last event
GUICLEARALL
deletes all elements and clears the screen
GUIAT [x], [y]
moves current location to the specified location
GUIATDELTA [dx], [dy]
moves current location by applying the specified delta to the current location
GUIPRINT [expression]
prints 8x8 pixel ASCII strings at current location
GUIRECTANGLEERASETO [x], [y]
erases a rectangular area from the current location to the specified location
GUIBUTTONADD [text], [x], [y]
create button and return its ID
GUIBUTTONDELETE [id]
deletes a button
GUIBUTTONENABLE [id]
enables a button
GUIBUTTONDISABLE [id]
disables a button
GUICHECKBOXADD [text], [x], [t]
create checkbox and return its ID
GUICHECKBOXDELETE [id]
deletes a checkox
GUICHECKBOXENABLE [id]
enables a checkbox
GUICHECKBOXDISABLE [id]
disables a checkbox
GUICHECKBOXISCHECKED [id]
returns true when the specified checkbox is checked; false otherwise
GUICHECKBOXSETISCHECKED [id], [is_checked]
sets whether the specified checkbox is checked
GUISETCURRENTRADIOGROUP [group_id]
sets current radio group ID, to be used by subsequent GUIRADIO* calls; can be any integer
GUIRADIOADD [text], [x], [y]
create radio button in the current radio group and return its ID
GUIRADIODELETE [id]
deletes a radio button
GUIRADIOENABLE [id]
enables a radio button
GUIRADIODISABLE [id]
disables a radio button
GUIRADIOISSELECTED [id]
returns true when the specified radio button is selected; false otherwise
GUIRADIOSETISSELECTED [id], [is_selected]
sets whether the specified radio button is selected
GUIIMAGEASCIIADD [text], [x], [y]
creates an ASCII-text image from a string and returns its ID
can be used to display text that changes
GUIIMAGEDELETE [id]
deletes an image
GUIIMAGEENABLE [id]
enables an image
GUIIMAGEDISABLE [id]
disables an image
GUIIMAGEISSELECTED [id]
returns true when the specified image is selected; false otherwise
GUIIMAGESETISSELECTED [id], [is_selected]
sets whether the specified image is selected
GUIIMAGESETSHOWSELECTEDMARK [id], [is_shown]
sets whether the selection marker is shown for the specified image
GUIIMAGESETSHOWHOVERMARK [id], [is_shown]
sets whether the hover marker is shown for the specified image
GUIIMAGEASCIISETTEXT [id], [text]
changes the text of the specified ASCII-text image
When a GUI framework event takes place, the following labels are looked up
in the program text, and execution jumps to the one appropriate for the event:
- timerTickEvent - called on automatic, recurring GUI framework timer ticks
- buttonClickEvent - called whenever a button is clicked
- checkboxChangeEvent - called whenever a checkbox changes value
- radioChangeEvent - called whenever a radio button changes value
- imageLeftClickedEvent - called whenever an image is left-clicked
- imageRightClickedEvent - called whenever an image is right-clicked
- imageSelectedChangeEvent - called whenever an image changes selected state
- guiRefreshEvent - called whenever the GUI framework has redrawn the screen; used to re-draw custom graphics, text, etc.
Programs must define these if they wish to act when an event takes place.
Use GUIACTIVEELEMENTID to discriminate between multiple buttons, images, etc.