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.

Eight digit Max7219 Seven-Segment Displays

Eight-digit seven-segment display driven by Uno SPI.

I have been experimenting with these 8 digit displays driven by the Max7219 driver chip. The driver chip allows you to control the segments with persistent serial data commands, rather than having to drive the segments yourself regularly from your main microcontroller.

They are driven over an SPI bus, which requires only three lines, not including vcc and gnd. SPI is built into the 328P microcontroller on the UNO. One line is for the serial data out, one is a synchronizing clock, and one line is the Chip Select line. (SPI uses another line for serial data in, but I don’t need that in this case.) The Max7219 doesn’t actually have full SPI compatibility, as it does not have a Chip Select line, but rather a LOAD line, which is a little different, and is problematic if you wanted to put more than one device on your SPI bus. I ordered some Max7222 chips also, which have full SPI compatibility; but in this use case it doesn’t really matter as the Max7219 drivers support daisy chaining of the shift registers, meaning I can hook up multiple 8 digital displays using only one SPI device slot.

The arduino reference material on the SPI library is a little confusing, so it took some time for me to figure out how to program the SPI communication correctly. Also, I was confused for a while because the display kept showing up as randomly garbled. It turned out that there wasn’t really anything wrong fundamentally with my code, but just when the Max7219 boots up, the segment registered are pre-filled with seemingly random data, so I needed to blank or overwrite the digits first to see only what you want to see.

Here is the test code.

/*
  Copyright 2020 Christopher Howard

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include <SPI.h>

#define CH_H 0b00110111
#define CH_I 0b00010000
#define CH_BLK 0b00000000
#define CH_0 0b01111110
#define CH_1 0b00110000
#define CH_2 0b01101101
#define CH_3 0b01111001
#define CH_4 0b00110011
#define CH_POINT 0b10000000

void setup ()
{
	pinMode(8, OUTPUT);
	SPI.begin();
	SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
	digitalWrite(8, LOW);
	/* Normal Mode */
	SPI.transfer(0b00001100);
	SPI.transfer(0b00000001);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	/* Scan limit: all digits */
	SPI.transfer(0b00001011);
	SPI.transfer(0b00000111);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	/* 25/32 intensity */
	SPI.transfer(0b00001010);
	SPI.transfer(0b00001100);
	digitalWrite(8, HIGH);
	/* Write some characters */
	digitalWrite(8, LOW);
	SPI.transfer(0b00000001);
	SPI.transfer(CH_0);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000010);
	SPI.transfer(CH_1);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000011);
	SPI.transfer(CH_2 | CH_POINT);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000100);
	SPI.transfer(CH_3);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000101);
	SPI.transfer(CH_4 | CH_POINT);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000110);
	SPI.transfer(CH_BLK);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00000111);
	SPI.transfer(CH_I);
	digitalWrite(8, HIGH);
	digitalWrite(8, LOW);
	SPI.transfer(0b00001000);
	SPI.transfer(CH_H);
	digitalWrite(8, HIGH);
	SPI.endTransaction();
	SPI.end();
}

void loop ()
{
}