Why program in FORTH language on ARDUINO?
published: 9 June 2020 / updated 9 June 2020
Preamble
Hello,
I have been programming in FORTH language since 1983. I stopped programming in FORTH in 1996. But I never stopped watching the evolution of this language. I resumed programming in 2019 on ARDUINO with FlashForth.
I am the co-author of several books concerning the FORTH language:
- Introduction au ZX-FORTH (ed Eyrolles - 1984 - ASIN:B0014IGOXO)
- Tours de FORTH (ed Eyrolles - 1985 - ISBN-13: 978-2212082258)
- FORTH pour CP/M et MSDOS (ed Loisitech - 1986)
- TURBO-Forth, manuel d'apprentissage (ed Rem CORP - 1990)
- TURBO-Forth, guide de référence (ed Rem CORP - 1991)
Programming in FORTH language was always a hobby until 1992 when the manager of a company working in subcontracting for the automobile industry contacts me. They had a concern for software development in C language. They had to order an industrial PLC.
The two software designers of this company programmed in C: TURBO-C from Borland to be precise. And their code couldn't be compact and fast enough to fit within 64 kilobytes RAM memory. It was in 1992 and flash memory type extensions did not exist. In these 64 KB of RAM, you had to keep MS-DOS 3.0 and the application!
It’s been a month since the C language developers turned the issue around, up to reverse engineering with SOURCER (a disassembler) to eliminate unnecessary pieces of executable code.
I analyzed the problem that was exposed to me. Starting from scratch, I realized, alone, in one week, a fully operational prototype that met the specifications. The principle of this application is explained in this article:
An industrial automaton with fractal matrix
For three years, from 1992 to 1995, I produced many versions of this application that has been used on the assembly lines of several car manufacturers.
Limits between language and application
All programming languages are shared as follows:
- an interpreter and executable source code: BASIC, PHP, MySQL, JavaScript, etc... The application is contained in one or more files which will be interpreted whenever it is necessary. The system must permanently host the executing interpreter the source code;
- a compiler and / or assembler: C, Java, etc ... Some compilers generate native code, ie executable specifically on a system. Others, like Java, compile code executable on a virtual Java machine.
The FORTH language is an exception. It includes:
- an interpreter capable of executing any word in FORTH language
- a compiler able to extend the dictionary of FORTH words
What is a FORTH word?
A FORTH word means any dictionary expression made up of ASCII characters
and usable in interpretation and / or compilation: words
allows
to list all the words in the FORTH dictionary.
Some FORTH words can only be used in compilation: if else then
for example.
With the FORTH language, the essential principle is that one does not create an application. In FORTH, we extend the dictionary! Each new word you define will do as much part of the FORTH dictionary as all the pre-defined words when starting FORTH. Example:
\ Turn a port pin on, dont change the others. : high ( pinmask portadr -- ) mset ; \ Turn a port pin off, dont change the others. : low ( pinmask portadr -- ) mclr ;
We create two new words: high
and low
which will
complete the dictionary of pre-defined words.
Is a word a function?
Yes and no. In fact, a word can be a constant, a variable, a function... Here, in our example, the following sequence:
: high ...code... ;
would have its equivalent in C language:
void highFunction(int pinmask, int portadr) { ...code... }
In FORTH language, there is no limit between language and application.
In FORTH, as in C language, you can use any word already defined in the definition of a new word.
Yes, but then why FORTH rather than C?
I expected this question.
In C language, you can only access a function through the
main function main()
. If this function integrates several
additional functions, it becomes difficult to find a parameter error
if the program malfunctions.
On the contrary, with FORTH, it is possible to execute - via the interpreter - any word pre-defined or defined by you, without having to go through the main word of the program.
The FORTH interpreter is immediately accessible on the ARDUINO card via a terminal type program and a USB link between the ARDUINO card and the PC.
The compilation of programs written in FORTH language is done in the card ARDUINO and not on the PC. There is no upload. Example:
: LED.toggle ( ---)
LED pin@
if LED low
else LED high
then ;
This definition is transmitted by copied / pasted into the terminal. The interpreter/compiler
FORTH will analyze the flow and compile the new word LED.toggle
.
In the definition of LED.toggle
, we see the sequence LED high
.
To test this sequence, just type it in the terminal. To execute
LED.toggle
, just type this word in the terminal.
FORTH language compared to C language
This is the part I like the least. I don't like to compare FORTH language against to the C language. But since almost all developers use the C language, I will try the exercise.
Here is a test with if()
in C language:
if(j > 13){ // If all bits are received rc5_ok = 1; // Decoding process is OK detachInterrupt(0); // Disable external interrupt (INT0) return; }
Test with if
in FORTH language (code extract):
j @ 13 > \ If all bits are received if 1 rc5_ok ! \ Decoding process is OK di \ Disable external interrupt (INT0) exit then
Here is the initialization of registers in C language:
void setup() { // Timer1 module configuration TCCR1A = 0; TCCR1B = 0; // Disable Timer1 module TCNT1 = 0; // Set Timer1 preload value to 0 (reset) TIMSK1 = 1; // enable Timer1 overflow interrupt }
The same definition in FORTH language:
: setup ( -- ) \ Timer1 module configuration 0 TCCR1A ! 0 TCCR1B ! \ Disable Timer1 module 0 TCNT1 ! \ Set Timer1 preload value to 0 (reset) 1 TIMSK1 ! \ enable Timer1 overflow interrupt ;
Is FORTH as good as C?
Yes. FORTH performs on two important points: the speed of execution compiled code and the compactness of that code. Programs written in FORTH language are very fast in execution, often faster than their equivalent written in C language.
In terms of compactness, FORTH is even more compact than assembly language!
How is it possible? The FORTH compiler stores 16-bit addresses, say cfa
(code field address) in the definition of words. The assembly language will
compile a code like jmp addr
that fits in 24 bits while a
cfa address is in 16 bits. The internal engine of the FORTH language, which
occupies 50 bytes on average, engine responsible for executing the compiled code, executes these
cfa successions almost as quickly as assembled code.
Where the FORTH language gains in performance is the passage of data through the stack. The C language (and many other languages) manages local and global variables in abundance, where the FORTH language exploits a data stack whose access has no equivalent in terms of performance.
To get an idea of the extraordinary compactness of the compiled FORTH code, read this article:
Contrôler une LED par PWM en 176 octets
What FORTH allows to do compared to C language
We understood, FORTH immediately gives access to all the words in the dictionary, but not only. Via the interpreter, we also have access to all the memory of the microcontroller AtMega. Connect to the ARDUINO card on which FlashForth is installed, then simply type:
hex 0 $100 dump
You should find this on the terminal screen:
0000 :02 00 03 02 00 00 01 02 00 84 00 ff 00 00 97 6a ...............j 0010 :10 00 30 00 00 00 00 00 18 00 05 00 7d 02 1f 00 ..0.........}... 0020 :60 60 60 00 20 00 00 00 00 01 00 00 60 60 60 60 ```. .......```` 0030 :70 ff 60 60 60 04 00 00 60 60 60 00 00 00 00 00 p.```...```..... 0040 :7e 0a 00 00 02 03 ed f9 00 60 00 00 00 00 00 60 ~.....M..`.....` 0050 :30 ff 60 00 00 02 60 00 60 60 60 00 60 3a 02 b5 0.`...`.```.`:.. 0060 :00 00 60 60 00 60 00 60 00 00 60 00 00 00 02 00 ..``.`.`..`..... 0070 :00 60 60 60 60 60 60 60 00 00 00 00 00 60 00 00 .```````.....`.. 0080 :00 00 00 60 00 00 00 00 00 00 00 00 60 60 60 60 ...`........```` 0090 :60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 ```````````````` 00a0 :60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 ```````````````` 00b0 :00 00 00 00 00 60 00 60 00 f8 00 ff 08 00 60 60 .....`.`......`` 00c0 :40 98 06 60 19 00 70 60 60 60 60 60 60 60 60 60 @..`..p````````` 00d0 :60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 ```````````````` 00e0 :60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 ```````````````` 00f0 :60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 60 ```````````````` ok<$,ram>
This corresponds to the contents of the RAM memory of the address $ 0000 to $ 00ff:
- the first 32 bytes are those of the 32 registers of the ATmega;
- the next 64 bytes, from the address $0020 to $005f, correspond to the I/O registers
- the last 160 bytes, from $0060 to $00ff, correspond to the extended registers. If these registers are not implemented, we find the value $60
Thus, it is easy to examine each ATmega register. It is not recommended modify the content of these registers directly without knowing what you are doing.
For example, serial transmission is controlled by the UBRROL registers ($ 00c4) and UBRROH ($00c5). To know the value of the UBRROL register, it just type:
hex c4 c@ \ display 19 (25 in decimal)
To change the baud rate to 115200 baud, this value will be replaced by 8:
8 c4 c!
Immediately afterwards, you must enter the terminal parameters and select baud rate at 115200 baud:
There you go! You are now communicating at 115200 baud with the ARDUINO card!
This setting will not persist if you reset the ARDUINO card.
And that, C language can not do it?
Yes. but not as simple and interactive as in FORTH language.
Let's see another case highlighting the extraordinary compactness of the FORTH language...
The input/output (I / O) registers range from the address $0020 to $005f. Here are the addresses registers controlling port B:
address | register | name | function | init. value |
---|---|---|---|---|
$0025 | PORTB | data register | Output data | 0 |
$0024 | DDRB | direction register | 1: output; 0: input | 0 |
$0023 | PINB | input register | Status of input pins | -- |
We can define a constant pointing to PORTB:
$25 constant PORTB $24 constant DDRB
and configure the outputs as follows:
$20 DDRB c! $20 PORTB c!
This turn on the LED on the ARDUINO card. We can eliminate the constants and rewrite our code like this:
$20 $24 c! \ set DRRB to output $20 $25 c! \ turn LED on PORTB $00 $25 c! \ turn LED off
$20 $24
values before c!
pass through the stack
FORTH data. So there is no need to exploit variables,
even local. The data stack is accessible both via the interpreter
than in the definition of a compiled word.
But why a stack rather than variables?
The battery is a mechanism installed on almost all microcontrollers and microprocessors. Even C language uses a stack, but you don't have access to it.
Only the FORTH language gives full access to the data stack. For example,
to make an addition, we stack two values, we execute the addition, we display
the result: 2 5 + .
displays 7.
It's a bit unsettling, but when you understand the mechanism of the battery of data, we greatly appreciate its formidable efficiency.
The data stack allows a passage of data between FORTH words much more quickly only by processing variables like in C language or in any other language using variables.
Are you convinced?
Personally, I doubt that a single article will irreparably convert you programming in FORTH language. By trying to master the ARDUINO cards, you have two options:
- program in C language and use the many libraries available. But you will remain locked up in the capacities of these libraries. Adapting C language codes requires real prorammation knowledge in C language and master the architecture of ARDUINO cards. The development of complex programs will always be a concern.
- try the FORTH adventure and explore a new and exciting world. Certainly, this is not will not be easy. You will have to understand the architecture of the ARDUINO cards, the registers, registers flags extensively. In return, you will have access to a programming perfectly suited to your projects.
But are there professional applications written in FORTH?
Oh yes! Starting with the HUBBLE space telescope, some components of which have been written in FORTH language.
The German TGV ICE (Intercity Express) uses RTX2000 processors to control the motors via power semiconductors. The machine language of the RTX2000 processor is FORTH language.
This same RTX2000 processor was used for the Philae probe which attempted to land on a comet.
The choice of FORTH language for professional applications turns out to be interesting if we consider each word as a black box. Every word should be simple, therefore have a fairly short definition and depend on few parameters.
During the development phase, it becomes easy to test all the values possible processed by this word. Once perfectly reliable, this word becomes a black box, that is to say a function that we trust without imitating its proper functioning. Word by word, we make reliability easier a complex program in FORTH than in any other programming language.
But if we lack rigor, if we build gas factories, it is also very easy to get a malfunctioning app, or even crash FORTH!
Finally, it is possible, in the FORTH langage, to write the words you define
in any human language. However, the usable characters are limited to
ASCII character set between 33 and 127. Here's how we could rewrite
symbolically the words high
and low
:
PORTB %00100000 defPIN: _O_ \ symbol for LED \ Turn a port pin on, dont change the others. : __/ ( pinmask portadr -- ) mset ; \ Turn a port pin off, dont change the others. : \__ ( pinmask portadr -- ) mclr ;
Now, to turn the LED on, you can type:
_O_ __/ \ turn LED on
Yes! The sequence _O_ __/
is in FORTH language!
With FlashForth, here are all the characters at your disposal that can compose a word FORTH:
~}|{zyxwvutsrqponmlkjihgfedcba`_ ^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@? >=<;:9876543210/.-,+*)('&%$#"!
.....
Good programming.