Functions as parameters in FORTH

I was looking for something like the lambda in FORTH. It turns out there is the single quote operator which puts the “execution token” of a word on the stack, which you can EXECUTE later.

To give a practical example of such use, here is a maparray function:

: maparray ( xt a- n -- )
    begin
        dup 0 >
    while
            -rot ( n1 xt a- )
            dup @ ( n1 xt a- n2 )
            swap ( n1 xt n2 a- )
            -rot ( n1 a- xt n2 )
            swap ( n1 a- n2 xt )
            dup >r ( n1 a- n2 xt, xt )
            execute ( n1 a- n3, xt )
            over ( n1 a- n3 a-, xt )
            >r ( n1 a- n3, xt a- )
            swap ( n1 n3 a-, xt a- )
            ! ( n1, xt a- )
            1 - ( n4, xt a- )
            r> ( n4 a-, xt )
            cell + ( n4 a2-, xt )
            swap ( a2- n4, xt )
            r> ( a2- n4 xt )
            -rot ( xt a2- n4 )
    repeat
    drop drop drop
;

With this function, I can pass in the execution token, an array address, and the length of the array (in cells) and the execution token is executed on each array element, with the results stored back in the array.

christopher@nightshade ~/Repos/forth-learning$ gforth func.fs
Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
create a 1 , 2 , 3 , 4 , 5 , .s <0>  ok
a 5 cells dump                 
7FCF3CC56378: 01 00 00 00  00 00 00 00 - 02 00 00 00  00 00 00 00  ................
7FCF3CC56388: 03 00 00 00  00 00 00 00 - 04 00 00 00  00 00 00 00  ................
7FCF3CC56398: 05 00 00 00  00 00 00 00 -                           ........
 ok
: foo 1 + ;  ok                
' foo a 5 maparray  ok         
a 5 cells dump                 
7FCF3CC56378: 02 00 00 00  00 00 00 00 - 03 00 00 00  00 00 00 00  ................
7FCF3CC56388: 04 00 00 00  00 00 00 00 - 05 00 00 00  00 00 00 00  ................
7FCF3CC56398: 06 00 00 00  00 00 00 00 -                           ........
 ok

So, basically, the maparray function takes a function that acts on one element, and foo here is a function that adds one to an element, and I pass foo into maparray.

Fibonacci in gforth

Working through some FORTH exercises from the gforth tutorial, here is a function which generates the nth Fibonacci number:

: fib ( n1 n2 n3 -- n.
    compute n3'th fib number, index starting at zero
    first two args are starting nums, 0 1 for normal fib seq )
    begin
        dup 2 >
    while
            -rot ( n3 n1 n2 )
            dup ( n3 n1 n2 n2 )
            >r ( n3 n1 n2 )
            + ( n3 n4 )
            r> ( n3 n4 n2 )
            swap ( n3 n2 n4 )
            rot ( n2 n4 n3 )
            1 -
    repeat
    dup 0 = if
        drop drop
    else
        dup 1 = if
            drop swap drop
        else
            drop +
        then
    then
;

Execution:

christopher@nightshade ~/Repos/forth-learning$ ./fib.fi 
Gforth 0.7.3, Copyright (C) 1995-2008 Free Software Foundation, Inc.
Gforth comes with ABSOLUTELY NO WARRANTY; for details type `license'
Type `bye' to exit
0 1 0 fib . 0  ok
0 1 1 fib . 1  ok
0 1 2 fib . 1  ok
0 1 3 fib . 2  ok
0 1 4 fib . 3  ok
0 1 10 fib . 55  ok
0 1 50 fib . 12586269025  ok
0 1 70 fib . 190392490709135  ok

I can do, say, the 1000000th number and get a near instant result. But the integers start wrapping around long before that, so the result is not useful.

The above code can be done in a more recursive style with the RECURSE keyword, but there is not an automatic tall-call optimization, so that crashes the stack on large numbers.

A feature of the above code is use of the return stack, with the >R and R> words, to briefly save a value, so I don’t have to move the stack around so much.

Learning FORTH on Arduino

Some output from Nano running FORTH (no command echo)

FORTH is interesting as a language not as ugly and boring as C, yet more lightweight than lisp. For the uno (328PU) ulisp requires 97% of the program store space, and 74% of your dynamic memory, while Arduino-FVM needs 36% of program storage space and 69% of dynamic memory. There is more for me to explore as far as space efficiency goes, but anyway I’m giving FORTH a try.

There are a few different FORTH implementations for Arduino – I was looking for the easiest path to get set-up. I landed on Arduino-FVM simply because it was the first one I found that was an Arduino sketch/library which I load quickly without having to learn anything new about how to upload to the 328. I just had to install the FVM library, and then compile a sketch which gives me the actual interpreter to load on the microcontroller.

https://github.com/mikaelpatel/Arduino-FVM

You can’t really learn the fundamentals of FORTH programming from just looking at Arduino-FVM documentation; so I installed gforth on my Guix desktop computer and used the gforth info page that has nice reference material and an extensive tutorial. Then I looked at the Arduino-FVM source code, which documents the parameters for the arduino-specific words.

This one liner will blink the LED on and off:

: blink true 13 pinmode begin 500 delay 13 digitaltoggle again ; blink

For those total unfamiliar with FORTH, it is sort of like doing programming on a reverse polish notation calculator, but with a larger stack available.

Minimalist Arduino Board

Atmel 328PU microcontroller with minimal circuitry for 16Mhz operation

I wanted to get a feel for using the 328P without the arduino board, and practice soldering. I used this tutorial:

https://www.arduino.cc/en/Tutorial/BuiltInExamples/ArduinoToBreadboard/

16Mhz operation requires two capacitors, a quartz crystal, and a 10k resistor, as represented above. It is possible to cut down on the circuitry by using the internal 8Mhz oscillator, but I didn’t do that.

Up close view of top of minimalistic 328P board.

In my prototype, to supply power I simply clip 5V and GND leads onto the pads on the sides. The two wires coming out are the UART read and write lines (serial communication).

I plugged it into power and a USB-serial device (the TPE-USBSERIAL from ThinkPenguin) and to my surprise it worked the first time. I had already loaded ulisp on the microcontroller, and was able to run some lisp commands through the serial connection.

I was surprised it simply worked, mainly because of my frightening magnet-wire solder work.

Scary looking magnet wire mess on back of prototype board.

Methinks I might have to use a bigger board next time, or figure out a different approach.

Reading old EPROM with Arduino

Hand made Arduino reader for a 1980’s EPROM chip. Chip not shown in photo. Ribbon cables were not readily available, but some electrical tape made the wires manageable. Push button at top right initiates a full chip read with a hex dump to serial output.

My rather interesting new job involves maintaining some 1980’s full-motion flight simulator technology. The Digital Control Loading system for this simulator — the part which controls movement and force feedback of the flight controls — is made up of large TTL circuit cards (a Fokker system).

The mathematical procedures which calculate the feedback, etc., are not done in the main simulator computer, but are done by Channel CPU cards, which each contain an original intel 8086-2 microcontroller. Those microcontrollers run firmware which is burned onto old 32k and 64k EPROM memory chips.

1980’s era Channel CPU card. Performs mathematical functions to calculate positioning and force-feedback for flight simulator controls. Original Intel 8086-2 microcontroller at top left. 32KB and 64KB EPROM chips are the bottom four chips with labels on them.

We don’t have the source code for the math on the EPROMs, so we needed to make backups of the data on them. I was able without too much difficulty to put together a reader for the 32KB chips with an Arduino Mega and a few spare parts. I haven’t done the 64KB reader yet, but I do not think it will be difficult.

Reading these old EPROMs is straightforward: basically just setting address pins to the byte address you want, toggling the OUTPUT ENABLE pin, and then reading the byte from the 8 output pins. I have a button inserted onto the Mega which causes it to begin reading all the bytes on by one and dumping them to the serial output, in an ASCII format (0xHH).

Creating an Arduino programmer (burner) for the EPROM chips will be more challenging, I expect, since programming them requires higher voltage pulses (21V for the 32KB chip). But I should be able to manage that with a relay, I’m thinking.

In summary, it was an interesting and fun project illustrating how Arduino / AVR skills can be used in the work-a-day world.