FlashForth is a Forth stamp system implemented on the Microchip 8-bit PIC18F and 16-bit PIC24, 30, 33 and the Atmel Atmega microcontroller families.

FlashForth also works on the popular Arduino UNO and MEGA boards.

FF (FlashForth) allows you to write and debug complex real-time applications. The complete system including the compiler is executing on the microcontroller.

A Forth interpreter, compiler, assembler, multitasker and user definable interrupts are provided.

A computer with a terminal emulator is used for communicating with FF via a serial or USB link.

The Forth source files are edited and saved on the computer and uploaded to the microcontroller as Forth source code.

A special ff-shell, written in python, provides command line editing, history and file transfer capabilities.

All microcontroller registers and memories can be read and written from the command line.

When the application is ready, the application word address can be stored in the turnkey vector, and your autonomous embedded application has been set up.

FlashForth is mostly compatible with the ANS'94 standard. This guide describes the differences, and features not covered by the ANS'94 standard.

FlashForth is licensed according to the Gnu Public License v3.

If you feel that FlashForth has been useful for you, please contribute with a donation.

Design philosophy

The main idea with FlashForth is to enable a robust Forth stamp system that is easy to use.

The requirements were:


FF can be downloaded from http://www.sourceforge.net/projects/flashforth.

Download FlashForth 5.0. Expand the downloaded archive into a folder.
The git repository contains the latest developments.
The git repository can be cloned from SourceForge and GitHub.

git clone git://flashforth.git.sourceforge.net/gitroot/flashforth/flashforth
git clone https://github.com/oh2aun/flashforth.git

Check the device specific pages for further installation details.

Case sensitivity

FF is case sensitive. All FF core words are written in lowercase letters and hex numbers must be entered in lower case also.

Although the FF core words are in lower case, in documentation the FF core words may be written in UPPERCASE to make it clear that a Forth word is referred to.

The Interpreter

The Forth interpreter is a normal Forth interpreter. It parses words delimited by space and tries to find the word in the dictionary. TAB is ignored. If the word is found it is executed, if not, FF tries to convert it to a number according to the current base (or base prefix), and put the number on the stack. If that fails ABORT is called.

The interpreter can only be used by the OPERATOR task, not by the background tasks.

After interpreting a line, QUIT prints Ok and executes the deferred word PROMPT.
By default PROMPT executes .ST which prints a number base prefix symbol, the current data area memory type and the parameter stack contents.
If you don't want to see the info from .ST, you can re vector PROMPT to for example CHARS which does nothing.

' chars is prompt
' .st is prompt 


FF is a 16-bit Forth and the single precision math operations are consequently 16-bit.

FF also supports double precision 32-bit math.

UM/MOD, M+ and UM* are used as base for the extra 32-bit double precision math words that can be loaded from math.txt.

For working with 48 and 64 bit numbers there are words in qmath.txt.
The words UT* UT/ UT/* have 48 bits precision.
The words UQ* UQ/MOD QM+ D>Q have 64 bits precision.
The 64 bit words are implemented for PIC18 and PIC24-30-33.

Number conversion

FF supports single precision 16-bit and double precision 32-bit number conversion.

Double precision numbers are identified by a trailing dot.

Input numbers can be prefixed by % # $ to achieve binary decimal and hexadecimal number conversion without changing BASE.

Output numbers are always converted according to BASE.

The Compiler

FF is a subroutine threaded Forth with native code generation.

Code is always compiled to flash memory. PIC and ATMEGA can only execute code from flash.

Constants, variable addresses and literals are compiled as native code.

DUP and 0= before IF WHILE UNTIL are optimized away.

All the structured conditional words generate native code.

: and ] puts FF in compilation state. ;  ;I and [ enters the interpreter state.

The maximum word name length is 15 characters.

Words that should not be interpreted have a 'compile only' bit in the header. Interpreting these words will result in an ABORT and restarting the interpreter, that is jumping to QUIT.

The compiler performs tail call -> goto optimisation at the end of a word.

The data space

The harvard architecture microcontrollers have separate memory spaces for ram, flash and eeprom.

In order to make it possible for words like CMOVE to be used on all kinds of memories, these are mapped into one 64 Kbyte memory space. Like this it is avoided to make separate words for copying between different kinds of memories.

The memory spaces are mapped by FF in software:


FLASH is mapped to  $0000 - $ebff
EEPROM is mapped to $ec00 - $efff
RAM is mapped to    $f000 - $ffff 

PIC18F K42 K83:

FLASH is mapped to  $0000 - $bbff
EEPROM is mapped to $bc00 - $bfff
RAM is mapped to    $c000 - $ffff 

PIC24, dsPIC30, dsPIC33:

RAM is mapped to    $0000   - RAMSIZE-1
FLASH is mapped to  RAMSIZE - $fbff
EEPROM is mapped to $fc00   - $ffff 


RAM is mapped to    $0000   - (RAMSIZE-1)
FLASH is mapped to  (0xffff - FLASHSIZE + 1) - 0xffff

In FlashForth data space can be allocated by CREATE ALLOT VARIABLE 2VARIABLE , C, VALUE DEFER .

Each memory type has its own allocation pointer. The words RAM EEPROM FLASH sets the data area from which the succeeding allocations will be made.

@ ! C@ C! and derived memory access words can be used transparently with all types of memory.

The exception to this rule are the words MSET MCLR MTST BSET BCLR BTST that can only address RAM.
These words are typically used for setting and clearing bits in registers.

For PIC18 the mapping overhead for ram @ and ! is 4 instruction cycles.
For PIC24 the mapping overhead for ram @ and ! is 3 instruction cycles.
For Atmega the mapping overhead for ram @ and ! is 2 instruction cycles.

\ A variable in eeprom
eeprom variable var2

\ A variable in ram
ram variable var1

It is not recommended to create variables in eeprom unless these are updated fairly seldom.

Data areas in flash are normally used for constant data and constant execution vectors.

Typically flash cells can be written over 10000 times until it may fail. Eeprom cells can typically be written over 100000 times until it may fail.

As an example here is a word which creates character arrays in the current data space.

: carray: ( n "name" -- ) 
  create allot 
  does> + 

Create a 20 character array in eeprom called CALIBRATE.

decimal 20 carray: calibrate

It is good to always set the data space context back to ram after flash or eeprom has been used.

Compile a word which creates indexed cell arrays.

: array: ( n "name" -- )
  create cells allot 
  does> swap 2* + 
ram 20 array: cnt               \ Creates the array cnt ( size 20 cells )
1233 10 cnt !                   \ Store 1233 in table index 10
10 cnt @                        \ Fetch from index 10 

Create a table in flash with constant data.

flash create flash-table $1234 , $3456 , #12345 , %1010101010 ,

Create a table in eeprom with some data.

eeprom create eeprom-table $e123 , $e456 , #8888 , %1110111011101110 ,

In order to prevent accidental writes to flash or eeprom the word FL- can be used to prevent writing. FL+ can be used for enabling the writes again.

Typically FL- is used in the TURNKEY word when the embedded application starts up. FL+/FL- is then used specifically in those words that store something to eeprom or flash.

Inlining of words

FF can compile location independent assembler primitives as inline code. The shortest of these words have the inline bit set in the word header for automatic inlining.

Individual words can be inlined by prefixing the word with INLINE.

: newswap inline swap ; 

When compiling a new word that should be inlined automatically, the inline flag can be set with the word INLINED.

: 1+
  [ Sminus w, a, swapf, ]  \ Decrement stack pointer with one
  [ Splus  f, a, infsnz, ] \ Add lower byte, skip next instruction if the result was nonzero
  [ Srw    f, a, incf, ]   \ Add high byte
; inlined                  \ Set the inline header flag

Also words defined by CONSTANT, VARIABLE, 2CONSTANT and 2VARIABLE can be inlined. They compile the constant and the variable address as inline literal code.

If you append the definition with INLINED, the compiler will later compile the constant as an inline literal.

34 constant thirtyfour inlined
: literal-34 thirtyfour ;

See the device specific pages for further details.

Dictionary search order

The core dictionary is always searched before the user dictionary. It is not possible to redefine existing words. These measures have been taken to make the system more robust and to make it possible to recover to the basic state, without the need to flash the chip again. It also makes the code clearer, since there will not be two words with the same name.

In order to recover to an earlier dictionary and memory allocation state, use MARKER. Always before defining new words define a marker. Otherwise you may need to return to an earlier marker or to say EMPTY which will empty the dictionary and reset all memory allocations to default values. A marker will restore TURNKEY, DP and LATEST. IRQ is not affected.

FORGET can be used to forget a user word, but FORGET can only adjust the FLASH DP. This means that allotted EEPROM or RAM will not be reclaimed if you use FORGET.

Note that the eeprom variables TURNKEY, DP, LATEST are cached in ram during interpretation of a input line and also during compilation state. This makes compilations run faster, and there will be less wear of the eeprom.

Since FF refuses to redefine words, certain words, typically one line definitions, can be compiled from several source files. The first compilation is accepted, and the others rejected. This is quite practical for having some short definition in many files, so that you can compile exactly the words one or more applications needs.

For example i2c_base.txt and task-test.txt both have defined PORTC, but PORTC will be compiled only once, even if both files are loaded to FF.

The word FL- can be used for disabling writes to flash and eeprom. It is useful for making sure that no writes to flash or eeprom occur.

Boot sequence

After processor reset a check is made to see if a turnkey word should be executed. If the eeprom value TURNKEY contains a nonzero value, it must be an address of a valid user word. Unless the user presses ESC within the turnkey timeout, the user word is executed. If ESC is pressed, the user word will not execute. Instead the forth interpreter is entered.

' my_application is turnkey 

If your TURNKEY word is crashing, press ESC and as a first command give:

false is turnkey 

This will disable the TURNKEY and allow you to make corrections.

When FlashForth start it prints out a variable amount of characters indicating the restart reason:

P = Power on  reset (ALL)
B = Brown out reset (ALL)
W = Watchdog timeout reset (ALL)
S = Software Reset Instruction (PIC18, PIC24)
O = Return stack overflow (PIC18, PIC24)
U = Return stack underflow (PIC18)
E = External reset (Atmega, PIC24)
M = Math error (Divide by zero) (ALL)
A = Address Error (PIC24) 

Interrupt handling

See the device specific pages

Background tasks

FF can execute background tasks concurrently with the operator task.

The task switching is made cooperatively by executing PAUSE. PAUSE is executed in I/O words KEY and EMIT so that background tasks can run while the console is waiting for input or queuing for output. MS executes PAUSE while it waits for the specified delay to pass.

If IDLE_MODE in the configuration file is enabled, the processor will enter the idle powersaving mode in PAUSE in the OPERATOR task.
If you want to allow the processor to go into idle mode use the word IDLE. If not, use the word BUSY.
An interrupt will exit the idle mode and the processor will run until the next time idle mode is entered.

The percentage of time that the processor is busy can be read by the LOAD word. The integration interval is 256 milliseconds.

The words for tasking can be loaded from task.txt.

The user area lives in ram. The user area is initialized from the task definition in flash.

TASK: creates a new task and defines the stack sizes and the additional user area size and the tibsize.
Tibsize can be set to zero for background tasks, except if numeric output is used.
The end of the TIB is shared with the HOLD buffer. A task that uses “. U. <# # #s #>” etc., will
need a small TIB for number formatting.

TINIT initializes a task with the XT of the task loop. It also initializes the task user area.

RUN makes the task run. It inserts the task in the round-robin linked list.

END ends a task. It removes the task from the round robin linked list.

SINGLE ends all tasks except the operator task.

TASKS lists all running tasks.

The tasking commands may only be executed from the operator task.

KEY, KEY?, EMIT can be deferred and used in a background task to interact for example with a keyboard and a LCD display.

\ Task loop for displaying data on the LCD display
: lcd_display ( -- )
  ['] lcd_emit 'emit !     \ Use LCD emit
    #00 lcd_at             \ Position cursor at beginning of first line
    ." Ticks: " ticks u.4  \ Display the current number of ticks

$10 $20 $20 $0 task: lcd_task  \ tibSize stackSize retrunStackSize addSize --
' lcd_display lcd_task tinit
lcd_task run


I have always found DO..LOOP cumbersome to use. I wanted to separate the loop count and the index handling. The FF core implements FOR..NEXT and a re-entrant P register.

DO ?DO LOOP +LOOP LEAVE I J UNLOOP can be added from Forth source code.

FOR..NEXT loops exactly the amount of times specified ( also 0 ) .

: star [char] * emit ; ok <$,ram>
star * ok <@,ram>
: stars for star next ; ok <$,ram>
10 stars ****************ok <$,ram>
0 stars ok<$,ram>

The loop count is held on top of the return stack and it can be fetched by R@. ENDIT sets the loop count to 0, so that NEXT will terminate the loop.

: test
    r@ . r@ 4 =
; ok
test 9 8 7 6 5 4 ok 

If you EXIT a FOR..NEXT loop you must drop the loop count with RDROP.

: test
    r@ 4 =
      rdrop exit
    r@ .
; ok
test 9 8 7 6 5 ok 

The P register

The P register can be used as a variable or as a pointer. It can be used in conjunction with FOR..NEXT or at any other time.

!P>R pushes the current P value on the return stack and sets a new value to P.

In a definition !P>R and R>P should always be used to allow proper nesting of words.

R>P pops a value into P from the return stack.

!P sets a new value into P. Use !P only from the command line, or between !P>R and R>P in a definition.

@P lets you fetch the value of P.

P+ increments P by one.

P2+ increments P by two.

P++ ( n -- ) adds n to P.

P@ P! PC@ PC! are used to access memory via the pointer.

Always remember to balance the return stack in all branches of your code.

\ CMOVE src dst u -- copy u bytes from src to dst
\ The source address is kept on the parameter stack.
\ The destination address is kept in the P register.
: cmove
  swap !p>r
     c@+ pc! p+
  r>p drop

Interacting with FlashForth

Forth source code can be interpreted and compiled by sending it via a terminal emulator to FlashForth.

FF supports terminal communication via an UART or an USB serial emulation.
The inbuilt USB serial emulation requires that you use a PIC18F, PIC24 or Atmega chip with inbuilt USB tranceiver. Or a USB-UART bridge can be used.

When FF is interpreting a line of code it may take quite a long time, depending on line content and if writes to flash or eeprom are performed.
When writing to flash the chips can stop executing any code for tens of milliseconds. Therefore some flow control scheme is needed to not overflow the input buffers in FlashForth.
The simplest solution is to have a 200 millisecond delay at the end of each line.
This scheme always works, but if you are running a chip at a very low clock speed, even longer delays may be needed.
Most terminal emulators support this kind of delay.

A faster and optimal flow control scheme is line-by-line flow control.
Send a line of text to FF and wait for the end-of-line character to be echoed from FF.
Then the next line can be sent.
The FlashForth python shell (ff-shell.py) does this by default.

The default UART setting is 38400, 1, N, but it can be changed in the FF configuration files.

XON/XOFF and CTS flow control are available as a compilation options.

XON/XOFF (software) flow control works best when a real serial port is used for communication.

CTS (hardware) flow control works with real UARTs and with USB-UART bridges that support CTS flow control.

For the chips with inbuilt USB serial emulation, flow control is handled by the USB protocol. Use hardware flow control in the terminal emulator.

Adding a 200 ms line delay in minicom is a bit hidden.
Start minicom with -s option and go into File transfer protocols.
There you can modify the ascii file transfer program to /usr/bin/ascii-xfr -l200 -dsv
That defines a 200 millisecond end-of-line delay.

With Windows TeraTerm and a 200 ms line delay works just fine.

The FlashForth python shell (ff-shell.py) can be used for a more convenient interaction with FlashForth. The shell has a command line editor and command line history. It can send files to over the serial link with the #send directive. There is help system, to check the forth word descriptions and parameters. It also has a directive, #warm, for sending control-o over the serial link to force warm start of FlashForth.

ff-shell.py only available for linux.

$ shell/ff-shell.py -p/dev/ttyACM0
port:/dev/ttyACM0 speed:38400 hw:False sw:False newlinedelay:0 chardelay:0 cc:False nl:True
Shell directives:
#cat file      	Show the file contents
#cd path       	Change working directory
#esc           	Make a warm start and disable the turnkey
#help          	Print help text for all words
#help filter   	Print filtered help text
#history filter	Show the history
#ls {path}     	List files in working directory or in path
#pwd           	Print working directory
#send filename {startstring {stopstring}}	Send a file optonally starting at line with startstring and optionally stop at line with stopstring
#warm          	Send ctrl-o to FF

E FlashForth 5 ATmega328 05.09.2020

$ shell/ff-shell.py -h
usage: ff-shell.py [-h] [--port PORT] [--hw] [--sw] [--speed SPEED]
                   [--chardelay CHARDELAY] [--newlinedelay NEWLINEDELAY]
                   [--cc] [--nl]

Small shell for FlashForth.

optional arguments:
  -h, --help            show this help message and exit
  --port PORT, -p PORT  Serial port name, default = /dev/ttyACM0
  --hw                  Serial port RTS/CTS enable
  --sw                  Serial port XON/XOFF enable
  --speed SPEED, -s SPEED
                        Serial port speed
  --chardelay CHARDELAY, -d CHARDELAY
                        Character delay(milliseconds)
                        Newline delay(milliseconds)
  --cc, -c              Character by character flow control
  --nl                  Newline flow control disable

The ff-shell.py works well with the Arduino boards by having the end-of-line flow control (always active) and software flow control (--sw option). It is also possible to use character delay and end-of-line delay. It is also possible to have character-by-character flow control and hardware flow control (--hw) and to have character delay and end-of-line delay.

FlashForth recognises CRLF or only CR as end-of-line in ACCEPT. LF and CR are not echoed by ACCEPT, but sent by QUIT.

U1- and U2- can be used for disabling flow control if the end application can not support flow control on the serial interface.


Thanks to Peter Jacobs for the excellent tutorials.

Many thanks to Joe Ennis, W7NET, and Pete Zawasky, AG7C, for pushing FlashForth hard and bugging me with trouble reports.

Thanks to Igor, OM1ZZ for contributing a proto board and two PICs for the 24 and 33 PIC series.

Thanks to Brian Howell of WCU for contributing a PicKit 2.

Thanks to Microchip for contributing a PicKit 3, a Microstick II and a MicrostickPlus.

FlashForth Users

Western Carolina University, Kimmel School, Electrical and Computer Engineering Technology
is using FlashForth with PIC18F and dsPIC30F for teaching microcontroller and DSP concepts.

University of Queensland.

PZEF Company.


And many more.