Following reset, the core module starts by calling two
target-supplied routines, t_ColdBoot() and t_WarmBoot().
These routines allow the target module to set up the processor
and perform any port or system initialization needed.
The core module then enters a mammoth routine called loop(),
which does all the work of TBL.
The loop() routine repeatedly prompts the user for input,
which is gathered via calls to a target-supplied routine
called t_GetChar(). After a line of text has been
collected, loop() steps through the text buffer, finding
tokens by searching a long, space-delimited string of known
keywords, then using a large switch statement to route control
via goto statements to appropriate code within the loop()
routine.
For example, the loop exec might scan the text buffer and locate the keyword LIST. The exec translates this into the value KW_LIST and invokes the large switch statement at the exec's core. This in turn causes a goto to a block of code dedicated to processing the LIST token; this code further parses the text buffer to collect any arguments, then executes the requested LIST command. Once the LIST command has finished, control passes back to the top of the exec loop, which continues processing the text buffer until the buffer is exhausted and the user is prompted for more input.
The core includes a recursive-descent parser that analyzes
the text buffer and performs any required mathematical or
logical operations, yielding a single value to be used by
subsequent routines. The parser supports basic
operators, such as addition, logical-OR, and shifting.
It also supports a set of functions, such as RND() and
ADDR(). These functions are defined in a table of
function keywords. Adding a function to this table
requires adding code to the parser specific to that new
function. As an example, the ADDR() function returns the
address of a variable passed as an argument in the function
call. So:
10 a = addr(b)
causes variable A to hold the address of variable B.
The code for parsing the argument and determining the value to
return exists in the parser.
Your program exists in an area of memory known as program
RAM. The core assumes the target provides a second area
of RAM that acts as a temporary file storage area; the number
of files and total RAM in this area is target-dependent.
You can save your working program to this RAM buffer using the
SAVE keyword; this saves your program as a named file.
You can load a program previously saved to this RAM buffer
using the LOAD keyword. However, this storage is in RAM;
its contents will NOT survive a power-cycle or reset!
Saving your files permanently requires that the target
support some kind of flash read/write system. If such is
supported, you can copy the entire RAM file storage area to
flash using the FLSAVE command. Note that support for
this type of storage is target-dependent.
The core knows nothing about target-specific ports or I/O
registers. The link between the core's exec and the
target's I/O is a target-based table called ports_tab.
This long, space-delimited string must be defined by the
target and contains all I/O ports that the target wants to
make available. If the exec finds a match between the
text buffer and an entry in the ports_tab string, the index of
that match will be passed to the target in a call to
t_ReadPort() or t_WritePort(), as appropriate. The
target can use the passed index to take the appropriate
action.
Note that the target ports defined in ports_tab need not be
true I/O ports. These port entries may be virtual ports,
keywords that prompt action by the target rather than
addresses to read/modify. For example, my STM32F407
target has "ports" named RED_LED and BLUE_LED. Writing a
1 to RED_LED causes the target to light the on-board red LED;
there is no actual STM32F407 port named RED_LED.
TBL's variables use the target's integer size; yes, you can
have a 64-bit TinyBasicLike. If you are unsure as to a
variable's size, you can enter:
?x -1
to see the largest integer TBL can display.
The core supports 26 named variables, A through Z.
Additionally, variables X, Y, and Z are also dimensioned as
400-element vectors (X(0) through X(399)) and as 20x20 2D
arrays (X(0,0) through X(19,19)). X, X(0), and X(0,0)
are all the same address.
The core communicates with the user through a set of target
routines. These typically support a UART or other serial
connection, but could instead support, for example, a USB
keyboard and HDMI display.
The core recognizes ctrl-C as a universal BREAK
character. For example, you can break out of an infinite
loop by entering the BREAK character at any time.
The character ':' serves as a mid-line end-of-line character
(MIDEOL). For example:
10 A = B + 3 : PRINT A,
B
The core accepts string constants for use in PRINT
statements, but does not support string variables or string
operators.
The core accepts both upper- and lowercase characters on
entry, but most characters will be converted to uppercase for
internal storage and will appear as uppercase when you list
out your program. Exceptions to this conversion are
string constants and text following a comment keyword.
For example, consider:
10 print "Hello,
world!" : \ This is a comment
If you LIST this line, it will appear as:
10 PRINT "Hello, world!" : \ This is a comment
True to its TinyBasic ancestors, the core has three generic
error messages. "What?" appears for syntax or some
illegal operations. "How?" appears for errors involving
line numbers or bad IF or FOR constructs. "Sorry."
appears if you request an unsupported operation, such as
trying to REBOOT from within a program. Note that the
original authors of the foundational code probably had strict
rules for which of these error messages appeared under what
circumstances. I have tried to stay consistent with
their design, but probably messed up a few times in selecting
the proper response. If your program throws one of these
errors when running, the core will tell you the offending line
number.
Here is a list of all keywords supported by the TBL
core. Most keywords can be entered directly at the
command prompt, such as PRINT or LIST. Other keywords
are only legal within a program, such as FOR or NEXT.
Keyword |
Function |
Examples |
Notes |
LIST |
List one or more lines of the current
program |
LIST LIST 10 30 |
|
LOAD |
Load a program from the target's RAM
holding buffer, if supported. Overwrites current
program. |
LOAD "timer.bas" |
Filename must be enclosed in
double-quotes. Warns about overwriting current
program, if necessary. |
MERGE |
Loads a program from the target's RAM
holding buffer, if supported. Merges the loaded
program with the current program. |
MERGE "lib1.bas" |
Filename must be enclosed in
double-quotes. Overwrites any lines that have the
same number. |
NEW |
Erases current program. |
NEW |
Warns about erasing current program, if
necessary. |
RUN |
Executes current program. |
RUN |
|
SAVE |
Writes one or more lines of current
program to a RAM holding buffer on the target. |
SAVE "timer.bas" SAVE 10 400 "lib1.bas" |
Filename must be enclosed in
double-quotes. Does NOT save to target flash! See FLSAVE. |
FLSAVE |
Writes target's RAM holding buffer to
target's flash system, if supported. |
FLSAVE |
Writes entire RAM holding buffer,
including all previous SAVEs, to target flash, if
supported. |
FOR |
Begins FOR-NEXT loop. Selects loop
iterator and defines end limit. Supports STEP for
controlling change to iterator at loop's NEXT. |
FOR N=1 TO 10 FOR N = 1 TO 20 STEP 2 |
Iterator increments by 1 as
default. Use STEP to select different iterator
(can be negative). |
NEXT |
Ends FOR-NEXT loop. Updates loop
iterator, exits loop if iterator exceeds end limit. |
50 NEXT N |
|
LET |
Assigns value to a variable. |
30 LET A = 4 |
Obsolete but supported; can be omitted: A = 4 |
IF |
Tests a value or condition. If
condition is TRUE, executes remainder of line. |
30 IF A > 5 K = 1 30 IF A > 5 GOTO 200 |
|
GOTO |
Transfers to line number following
GOTO. Supports variables and calculations for line
numbers. |
70 GOTO 250 70 GOTO A+30 |
Can be entered at the command prompt to
start program execution at a specific line. |
GOSUB |
Calls a subroutine at the line number
following GOSUB. Supports variables and
calculations for line numbers. |
90 GOSUB 250 90 GOSUB A+30 |
Does not support mid-EOL (:) characters in GOSUB statement. |
RETURN |
Returns control to the line following the
line containing the original GOSUB. |
200 RETURN |
|
REM |
Marks remainder of line as a comment. |
100 REM This is a test. |
Must be first token in a statement or
following ':' MIDEOL. Preserves case of comment
text. |
\ |
One-char REM operator. Marks
remainder of line as a comment. |
100 \ This is a test. |
Must be first token in a statement or
following ':' MIDEOL. Preserves case of comment
text. |
INPUT |
Accepts an integer value from the
console. |
30 INPUT A |
Prompts the user with the variable to be
modified and a question mark. A ? Blocks until user enters a value. Blank lines are ignored and the user is prompted again. Precede hex value with 0x: A ? 0x30 Enter an ASCII character by enclosing it in single-quotes: A ? 'v' |
PRINTX |
Print integer as a hex value. |
PRINTX A |
In example, if A is 255, prints FF. |
?X |
Print integer as a hex value. |
?X A |
In example, if A is 255, prints FF. |
PRINTA |
Print integer as an ASCII character. |
PRINTA 48 |
Example prints a '0'. |
?A |
Print integer as an ASCII character. | ?A 48 | Example prints a '0'. |
PRINT |
Print integers, variables, or fixed
strings. Supports fields (comma) and concatenated
text (semicolon). |
PRINT A, B PRINT "A=";A PRINT A+2*B; |
First example prints values of A and B
separated by spaces, followed by CR/LF. If A = 47 in second example, prints: A=47, followed by CR/LF. If A = 47 and B = 3 in third example, prints: 53 and CR/LF is suppressed. |
? |
Print integers, variables, or fixed strings. Supports fields (comma) and concatenated text (semicolon). | ? A, B ? "A=";A ? A+2*B; |
First example prints values of A and B
separated by spaces, followed by CR/LF. If A = 47 in second example, prints: A=47, followed by CR/LF. If A = 47 and B = 3 in third example, prints: 53 and CR/LF is suppressed. |
POKE |
Write a value to a memory location. |
----- |
In original TinyBasic but not implemented
in this version. |
STOP |
Halts program execution and returns to
command prompt. |
100 STOP |
Synonym for END. |
END |
Halts program execution and returns to command prompt. | 100 END |
Synonym for STOP. |
BYE |
Exits the TBL program. |
BYE |
User will be prompted to save any changes
to current program before exiting TBL. BYE will
not exit TBL unless changes are saved or deleted. Exiting TBL from within a program is not supported; BYE must be entered at the command prompt. Note that on most embedded systems, there is nowhere to go after exiting main(); control usually loops in place waiting for a reset. Actual behavior of BYE is target-dependent. |
REBOOT |
Exits the TBL program, then forces a
hardware reset of the system, if supported by the
target. |
REBOOT |
User will be prompted to save any changes
to current program before rebooting. REBOOT will
not reset target unless changes are saved or deleted. Rebooting from within a program is not supported; REBOOT must be entered at the command prompt. Target must provide hardware reset. |
FILES |
Lists all files currently in RAM file
buffer. |
FILES |
Lists only files in the RAM buffer.
Does not list files in the target's flash memory, even
if target supports flash file system. |
DELETE |
Deletes a file from the RAM file buffer. |
DELETE "file.bas" |
Deletes a file from the RAM buffer.
Does not delete the matching file from the target's
flash memory. If the target supports a flash file system, first DELETE the file from the RAM buffer, then use FLSAVE to write the updated RAM buffer to the flash file system. |
MEM |
Display amount of memory available for
program storage. |
MEM |
Memory available is target-dependent. |
AWRITE |
Read an analog input. |
----- |
In original TinyBasic but not implemented in this version. |
DWRITE |
Write a digital output. |
----- |
In original TinyBasic but not implemented in this version. |
RSEED |
Sets seed for random number generator
(RNG). |
RSEED 99 |
Using a value of 0 will reset the RNG
seed to its initial (power-on) value. |
CHAIN |
----- |
In original TinyBasic but not implemented in this version. | |
WORDS |
Shows a list of all keywords, logical
operators, mathematical operators, comparison operators,
and all target-defined addresses and ports. |
WORDS |
|
TIMERRATE |
Sets the tic rate for all down-counting
timers (DCTs) value is tic rate in microseconds. |
TIMERRATE 1000 |
Variables in the DCT list will
automatically decrement their value by one on each DCT
tic until the value reaches 0. By default, target should have already set the DCT tic rate to 1 millisecond (value of 1000). |
ADDTIMER |
Adds a variable to the list of DCTs |
ADDTIMER T |
Number of supported DCTs is
target-dependent. Adding a variable that is
already in the list is ignored. |
DELTIMER |
Removes a variable from the list of DCTs. |
DELTIMER T |
Deleting a variable that is not in the
list is ignored. |
TONE |
Generates a tone on the target-defined
output pin, if supported. First argument is
frequency in Hz. Second argument is duration in
msecs. |
TONE 440, 500 |
This command starts a tone. Use
TONE 0 to shut off the tone. Example sounds a 440 Hz tone for 500 msecs. |
LOCK? |
Reports state of editing lock on current
program. |
LOCK? |
|
LOCK |
Prevents editing all or a section of the
current program. Lock range is specified as line
numbers. |
LOCK LOCK 100,200 LOCK 100, LOCK ,999 |
First example locks entire program. Second example locks all lines from 100 to 200, inclusive. Third example locks all lines from 100 to last legal line number (65534). Fourth example locks all lines from 1 to 999, inclusive. |
UNLOCK |
Removes editing lock of current program. |
UNLOCK |
|
OUTCHAR |
Sends a value to the console as an ASCII
character. |
OUTCHAR 48 |
Example sends the character '0' to the
console. |
BEEP |
If target supports tone generation,
sounds a predefined beep on the target's speaker. |
BEEP |
|
TEST |
Invokes a target-dependent test routine. |
TEST |
Functionality depends on target
code. This is usually used to trigger some test
code during development. |
Here is a list of all functions supported by the TBL core.
Name |
Function |
Example |
Notes |
ABS |
Returns absolute value of a variable or
expression. |
? ABS(10) ? ABS(B) ? ABS(-7) |
First example prints 10. Second example prints the absolute value of variable B. Third example prints 7. |
ADDR |
Returns the address of a variable. |
? ADDR(B) ? ADDR(X(10)) ? ADDR(Z(5,2)) |
First example prints the address of the
variable B. Second example prints address of element 10 (from 0) of array X. Third example prints the address of element (5,2) (from 0,0) of array Z. |
AREAD |
Returns value from analog input. |
----- |
In original TinyBasic but not implemented in this version. |
DREAD |
Returns state of a digital input. |
----- |
In original TinyBasic but not implemented in this version. |
PEEK |
Returns contents of a memory address. |
----- |
In original TinyBasic but not implemented in this version. |
RND |
Returns a random number in range of 0 to
(argument-1). |
? RND(10) |
Prints a random number from 0 to 9. Refer to RSEED keyword for modifying the RNG seed. |
TONE? |
Returns state of tone generator, if
supported. |
? TONE?() |
Returns 1 if tone is active or 0 if tone
is not active. |
Here is a list of all numeric operators supported by the TBL
core.
Operator |
Function |
Example |
Notes |
= |
Assignment operator; stores a value into
a named variable. Supports storing a sequence of
values into a sequence of array elements. |
10 A = 5 10 X(5) = B 10 X(0) = 1,2,3,4 10 X(5) = 1,,4 |
First example stores 5 in A. Second example stores the value of B in X(5). Third example stores the value 1 in X(0), 2 in X(1), 3 in X(2), and 4 in X(3). Fourth example stores the value 1 in X(5) and the value 4 in X(8); the contents of X(6) and X(7) are not changed. |
+ |
Addition |
10 A = 7 + B |
|
- |
Negation |
10 A = -B |
Unary negation |
- |
Subtraction |
10 A = B - 22 |
|
* |
Multiplcation |
10 A = B * K |
|
/ |
Division |
10 A = 8 / R |
|
% |
Modulus |
10 A = B % 4 |
Modulus of 0 is illegal. |
MOD |
Modulus |
10 A = B MOD 4 |
Modulus of 0 is illegal. |
Here is a list of all relational operators supported by the
TBL core.
Operator |
Function |
Example |
Notes |
>= |
Greater than or equal |
10 IF A >= 0 GOTO 20 |
|
<> |
Not equal |
10 IF A <> B GOTO 20 |
|
!= |
Not equal |
10 IF A != B GOTO 20 |
|
<= |
Less than or equal |
10 IF A <= B GOTO 20 |
|
> |
Greater than |
10 IF A > B GOTO 20 |
|
= |
Equal |
10 IF A = B GOTO 20 |
|
< |
Less than |
10 IF A < B GOTO 20 |
Here is a list of all logical operators supported by the TBL
core.
Operator |
Function |
Example |
Notes |
AND |
Returns logical-AND of two values |
? 4 AND 0x55 |
Prints 0 |
NOT |
Returns the logical negation of a value |
? NOT B |
Prints 1 (B is 0 by default; TRUE is
returned as 1) |
OR |
Returns the logical-OR of two values |
? 4 OR 0x55 |
Returns 85 |
XOR |
Returns the logical exclusive-OR of two
values |
? 4 XOR 0x55 |
Returns 81 |
Here is a list of all bit-wise shift operators supported by
the TBL core.
Operator |
Function |
Example |
Notes |
<< |
Left bit-wise shift |
?x 0x10 << 2 |
Prints 40 |
>> |
Right bit-wise shift |
?x 0x10 >> 2 |
Prints 4 |
This has been the most fun I've had in embedded development
in quite a while. It has been a pleasure to work with
such well-designed code, and I appreciate the effort that the
previous contributors put into their work. I also
appreciate their making this code available for people like me
to play with.
I have left their licensing and acknowledgements intact in my
TinyBasicLike.c source file, while adding my own list of
contributions. If you use or modify any code in either
of my C source files, please retain all original licensing and
acknowledgements.