Why program in FORTH language on ARDUINO?

published: 9 June 2020 / updated 9 June 2020

Lire cette page en français

 


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:

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:

The FORTH language is an exception. It includes:

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:

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:

addressregistername functioninit.
value
$0025PORTBdata register Output data0
$0024DDRBdirection register 1: output; 0: input0
$0023PINBinput 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:

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.