Language supports the most basic commands like LET
, PRINT
, GOTO
, IF
and INPUT
, 26 single-letter variables are available to the programmer, despite these limitations the language is Turing complete allowing to create (almost) any simple program.
Code memory space, expression tokens limit, maximum line number and shell IO communication way can be configured. No printfs are used (except for the debug stuff), that allows to change the communication method to for example microcontroller's UART module.
POKE
and PEEK
command can be disabled when used on PC, as they always cause segmentation fault, and SAVE
and LOAD
can be disabled when used on a MCU that doesn't have access to file system.
In case infinite loop occures there is a way to kill the execution by sending any key to the console. Execution is stopped and there is no problem with loosing progress having to reset the MCU. (KILL_IO
must be enabled.)
Keywords and variable names are case-insensitive.
LET <variable> = <expression>
Assigns the result of an expression to the given variable,LET
keyword isn't necessary and format<var> = <expr>
will also be understood.PRINT <"string"> : <expression>
Prints out strings and expression results to the console,:
token can be used as a separator to for example put a value after a string, leaving the separator token at the end of the line disables the linefeed that would have been sent at the end of the line.CHAR <variable>
Print a character in the given variable (equivalent to C'sputchar()
)GOTO <line number>
This command jumps to the given line number.IF <expression> <comparison> <expression> THEN <command>
Executes the command after theTHEN
keyword if the comparison result is true, available comparisons are the following: equal (=
), not equal (<>
), lower than (<
) and greater than (>
)INPUT <variable>
Gets the expression from the console and put it into the specified variable.REM <comment>
Do nothing, only purpose of this command is to store commentsCLEAR
Clears the console and homes the cursor.LIST
Lists the program.MEMORY
Shows how much code memory is left.RUN
Starts the program from the first line.NEW
Clears the code memory after confirmation.
POKE <address expression>, <value expression>
Sets the memory at the given address to the given valuePEEK <address expression>, <variable>
Gets the memory from the given address and stores it in the given variable.POKEB <address expression>, <value expression>
Same asPOKE
but only accessesuint8_t
instead ofpeek_t
PEEKB <address expression>, <variable>
Same asPOKE
but only accessesuint8_t
instead ofpeek_t
SAVE <filename>
Saves memory contents.LOAD <filename>
Loads memory contents.
Language interpreter contains a simple expression solver supporting basic integer arythmetic operations like: add (+
), subtract (-
), multiply (*
), divide (/
) and remainder (%
). Basic logic functions are also supported: AND (&
), OR (|
), XOR (^
) and NOT (!
).
Precedence | Operation | Operator(s) |
---|---|---|
1 | Subexpressions in brackets | () |
2 | Unary operators | +, -, ! |
3 | Important arythmetics | *, /, % |
4 | Low importance arythmetics | +, - |
5 | Logic operations | &, |, ^ |
Name | Format | Examples |
---|---|---|
Variable | Single letter variable name | A , B , c , z , Z |
Decimal | Normal value starting with non-zero digit | 0 , 1 , 2 , 420 , 1024 , 911 |
Hexadecimal | Hex value starting with 0x prefix |
0x45 , 0xFF , 0xDEADBEEF |
Octal | Value starting with '0' prefix | 033 , 0377 , 0105 , 00 |
Binary | Value starting with '0b' prefix | 0b0110 , 0b1001 , 0b01010101 |
NEWLINE
- Character that will be interpreted as a new line.BACKSPACE
- Character that will be interpreted as a backspace.CODE_MEMORY_SIZE
- Size of the program memory.EXPR_MAX_TOKENS
- Size of expression solver buffer.MAX_LINUENUM
- Maximum valid line number (not including MAX_LINENUM).POKE_PEEK
- EnablePOKE
andPEEK
commands.FILE_IO
- EnableSAVE
andLOAD
commands.IO_KILL
- Enable breaking the execution if new characters were received during execution.LOOPBACK
- Enable cosole loopback (input characters will be sent back).
line_t
- Format in which line number is stored.var_t
- Format in which variables are stored.uvar_t
- Unisgned version of the format in which variables are stored.peek_t
- Format in whichPOKE
andPEEK
accesses memory.
In the definitions x
is the pointer to the data that has to be sent, received or checked.
IO_INIT
- Called at the start ofmain()
starts up the IO device.PUTCHAR(x)
- Prints thex
character to the IO device.GETCHAR(x)
- Return character from the IO device.IO_CHECK(x)
- Returns if there are any new characters in IO (used for IO_KILL).
#define NEWLINE '\n'
#define BACKSPACE '\b'
#define CODE_MEMORY_SIZE 8192
#define EXPR_MAX_TOKENS 64
#define MAX_LINENUM 10000
#define POKE_PEEK 0
#define FILE_IO 1
#define IO_KILL 0
#define OUTPUT_CRLF 0
#define SHORT_STRING 0
#define LOOPBACK 0
typedef uint16_t line_t;
typedef int32_t var_t;
typedef uint32_t uvar_t;
typedef size_t peek_t;
#define IO_INIT() ((void)0)
#define PUTCHAR(x) (putchar(*x))
#define GETCHAR(x) (*x = getchar())
#define IO_CHECK(x) (*x = false)
#define NEWLINE '\n'
#define BACKSPACE '\b'
#define CODE_MEMORY_SIZE 512
#define EXPR_MAX_TOKENS 16
#define MAX_LINENUM 10000
#define POKE_PEEK 1
#define FILE_IO 0
#define IO_KILL 1
#define CRLF 1
#define SHORT_STRING 1
#define LOOPBACK 0
typedef uint16_t line_t;
typedef int32_t var_t;
typedef uint32_t uvar_t;
typedef size_t peek_t;
#define F_CPU 16000000UL
#define __AVR_ATmega328__
#include <avr/io.h>
#define IO_INIT() { \
UBRR0 = F_CPU / 8 / 9600 - 1; \
UCSR0A = 1<<U2X0; \
UCSR0B = 1<<TXEN0 | 1<<RXEN0; \
}
#define PUTCHAR(x) { \
while (!(UCSR0A & (1<<UDRE0))); \
UDR0 = *x; \
}
#define GETCHAR(x) { \
while (!(UCSR0A & (1<<RXC0))); \
*x = UDR0; \
}
#define IO_CHECK(x) { \
*x = !!(UCSR0A & (1<<RXC0)); \
}
In case of different board memory size might need to be adjusted and in case of some boards (ESP8266 for example), int main(void)
might need to be replaced with void setup(void)
and void loop(void)
might need to get added.
#define NEWLINE '\r'
#define BACKSPACE '\b'
#define BACKSPACE_STR "\b \b"
#define CODE_MEMORY_SIZE 512
#define EXPR_MAX_TOKENS 16
#define MAX_LINENUM 10000
#define POKE_PEEK 0
#define FILE_IO 0
#define IO_KILL 1
#define OUTPUT_CRLF 1
#define SHORT_STRING 1
#define LOOPBACK 1
typedef uint16_t line_t;
typedef int32_t var_t;
typedef uint32_t uvar_t;
typedef size_t peek_t;
#define IO_INIT() (Serial.begin(115200))
#define PUTCHAR(x) (Serial.write(*x))
#define GETCHAR(x) { while (!Serial.available()); *x = Serial.read(); }
#define IO_CHECK(x) (*x = Serial.available())
10 REM Primes generator, get all primes up to max prime
20 PRINT "Enter max prime: ":
30 INPUT C
40 A = 2
50 GOTO 90
60 A = A + 1
70 IF A < C + 1 THEN GOTO 50
80 END
90 B = 2
100 IF A % B = 0 THEN GOTO 60
110 B = B + 1
120 IF B < A / 2 THEN GOTO 100
130 PRINT A
140 GOTO 60
10 REM Fibonacci Sequence
20 A = 1
30 B = 1
40 C = 20
50 PRINT A
60 D = A
70 A = A + B
80 B = D
90 C = C - 1
100 IF C > 0 THEN GOTO 50
10 REM Base Converter
20 PRINT "Enter a value: ":
30 INPUT A
40 PRINT ""
50 PRINT "Decimal: " : A
60 B = A
70 C = 32
80 PRINT "Binary: ":
90 IF B & 0x80000000 <> 0 THEN PRINT "1":
100 IF B & 0x80000000 = 0 THEN PRINT "0":
110 B = B * 2
120 C = C - 1
130 IF C > 0 THEN GOTO 90
140 PRINT ""
150 B = A
160 C = 8
170 PRINT "Hexadecimal: ":
180 D = (B / 268435456 & 0xF) + 0x30
190 IF D > 0x39 THEN D = D + 0x7
200 CHAR D
210 B = B * 16
220 C = C - 1
230 IF C > 0 THEN GOTO 180
240 PRINT ""
250 B = A
260 C = 10
270 PRINT "Octal: ":
280 D = (B / 1073741824 & 0x3) + 0x30
290 CHAR D
300 D = (B / 134217728 & 0x7) + 0x30
310 CHAR D
320 B = B * 8
330 C = C - 1
340 IF C > 0 THEN GOTO 300
350 PRINT ""
360 PRINT ""