krüe :: avrforth



avrforth is a 16-bit subroutine threaded forth kernel for atmel's avr series of microcontrollers. Current development is done for the at90can128 (my cansynth board, to be exact), but it will run on most any mega device with minimal adjustments.

avrforth borrows some ideas from Terry Loveall's 4word. It is a colorless colorforth. The interpreter only interprets. Words must be compiled explicitly using the ] operator. Numeric input is handled by $, which parses the next word as a hexidecimal number.

avrforth uses null-terminated strings internally against tradition. I may switch to counted strings if they prove easier to deal with.

avrforth supports using the entire 128k instruction address space of the atmega128 and at90can128 for code.

avrforth has an interactive interpreter. It interprets input from the uart and compiles directly to flash. avrforth does not require separate memory to store program tokens.

avrforth has an integrated assembler. Assembly code words can be added without recompiling the kernel.

avrforth is public domain.


You need a host forth system to assemble avrforth. Development is done with gforth. You also need a utility to program the flash and a terminal program to communicate with the kernel through the avr's uart. I use avrdude and picocom for those tasks.


a quick run through...

edit configuration options
Configure the target device, programmer, and programmer port in makefile. Edit the options in config.f.
make should be enough here.
upload kernel
make upload
upload highlevel words
send blocks/core.f to the target device using the terminal program of your choice. In order to give avrforth enough time to program the flash, set your terminal program to pause after each line until it receives a newline back. 'make highlevel' will send all blocks.

Now you can send more program code or type commands interactively at the console.


Documentation has been pretty sparse but is getting better. Email me if there is anything in particular you need to know about.



Support for atmega328, atmega88p, atmega168p, and atmega328p.

Actually use the config options when declaring the interpreter stack.


Support for atmega640, atmega1280, and atmega2560. The full flash space isn't fully supported on the atmega2560 yet.

Update usart driver to use indexedname. Support usart2 and usart3.

Generate an error if the usart specified in config.f doesn't exist for the selected device.

Update gpio driver to support porth, portj, portk, and portl.

Update timer driver to support timer4 and timer5.

Initialize prescaler in twi.

Add config options for interpreter stack sizes.

Specify the amount of high flash used for i! separately for each device.

Use $ for hex literals.

Rename status register bits to avoid clashes with existing forth words.

Support 22 bit addresses in absolute mode instructions (call, and jmp,).

New words xt>r and r>xt access the return address. These are equivalent to >r and r> for most devices, but the atmega2560 has 3 byte return addresses.

Add latest to the dictionary.

Make memory usage information more accurate.

Generate an error if a memory range is exceeded.

Don't write output files if an error is generated.


Support for at90usb647 and at90usb1287.

Driver for device mode USB controller.

Vim syntax highlighting description.


Support for interrupt serivice routines in forth. Use the words int: and int; instead of : and ; to define a word and store it to a ram vector provided by one of the peripheral drivers.

Support for (optional) separate interrupt stacks. This feature avoids having to make all task stacks large enough for the task and interrupt code. Configuration of stack sizes is in config.f.

task-dequeue added to remove a task from the task queue.

-! added to complement +!.


New drivers for spi and external memory which are more comprehensive than the previous versions.

Additional factoring in the assembler.


New a/d driver which is more comprehensive than the old version.

Make task initization easier -- task-init now takes the size of return and data stacks as parameters instead of the actual pointer addresses.

Use create-file instead of open-file in the assembler to open files for writing.

Add predecrement memory access words: c@-, @-, c!-, and !-.

Predecrement and postincrement words that write to memory now take the address on the top of stack like !. They previously took the data on the top of stack.

Make send.f work for gforth versions < 0.6.9-20070604.


Added support for atmega48, atmega88, and atmega168. avrforth now runs on the arduino.


Added timer driver.

Added idump debugging word.

Made ;; a macro (bugfix).

Fall back to the proper unoptimized versions of set and clear if there are no literals (bugfix).


Use ram addresses instead of io addresses for atmega32 register definitions.

New gpio driver which simply creates constants for all pinx, ddrx, and portx words.

Added autoincrement memory words: c@+, @+, c!+, and !+. debug.f depends on c@+, which wasn't included previously.

New forth implementation of the 'send' shell script to send source files via the serial port. This version waits for avrforth to send a newline before sending the next line of source. The previous version simply waited a constant amount of time.


Support for at90can32 and at90can64.

Allow atmega8 and atmega32 to assemble again.

Turnkey action: the xt stored in the eeprom variable it will be executed at startup.

Bugfix for prescaler setting in timer initialization.


Support for atmega16 and atmega162.

New word single stops all tasks except the interpreter.

Added 2* and 2/ words to core.f. They were originally just macros.

Started debug.f, which contains dump for inspecting memory.

Made location of gforth configurable in the makefile.


Support for atmega128.


Added u*/mod and u*/.

Initial versions of extended precision arithmetic and linear interpolation wordsets.


avrforth has been rewritten to use my own forth style assembler, which is included under asm/. Presently, only the at90can128 has been tested -- support for other devices will be added back eventually.

Renamed bit operations on and off to set and clear. Added byte flag words on and off with the usual meaning.

Added words to control external memory. xmem enables external memory and bank selects the current bank. Current support assumes 128k of sram and maps it from $8000 to $ffff in 4 banks. Different configurations can be handled by modifying the source.


Updated atmega8 defines.


Initial twi driver.

Smaller and faster implementation of $.


New words, mainly double cell: tuck, -rot, 2@, 2!, 2drop, 2dup, 2swap, 2over, d+, and d-.

Updated atmega32 support.

Fixed a bug with conditional assembly that prevented usart initialization from occurring if usart0 was selected.

Ensured that the can driver is only included for the at90can128.


mobtx, mobrx, and mobrxb now clear status bits before enabling the mob.

spix calls pause while waiting for spi transfer to complete.


Switched back to round robin cooperative multitasking.

ec@ and ec! support eeprom byte access.

evar allocates variables in eeprom.

ticks returns system tick counter.


var now requires the length of variable to allocate.

rot no longer reverses the second stack item (bugfix).


Initial can driver.

The usart driver is now interrupt driven. The sizes of the tx and rx circular buffers can be set in config.asm.


avrforth has switched to a state machine type multitasking. There is now a periodic timer interrupt to run tasks. run and stop add and remove tasks from the active list.

>int and int> save and restore registers for use in interrupt service routines.

initial drivers for the spi and a/d converter.


added assembler.

added within to core wordset.

added on and off to set and clear bits. Literals are optimized to sbi and cbi instructions.


supported multiple usarts.

moved target specific defines to config.asm.


cmove, cmove>, and fill added.

pop renamed to ones to avoid confusion with the stack operation.

key and emit now use vectored i/o. 'key and 'emit are the respective vectors. rx and tx interface with the usart and are the default values for these vectors.

;, then, and the various ifs are now macros.

h. and b. are new words for hex output that use # and digit as factors.


pop calculates population count (number of ones).

ntz calculates number of trailing zeros.

f@, f! fetch and store condition codes.

0? tests the TOS, like ? used to. ? now compares the TOS with a literal.

c@, @, c!, and ! now optimize literals.

Added some double cell words: dinvert, d1+, dnegate, and extend.

High level multiplication: * and u*.

High level division: m/mod, /mod, /, mod, */mod, */, u/mod, u/, and umod. Fixed bug in um/mod. Large input values are now handled correctly.

Support for counted loops: for, next, and -next. The index is stored on the return stack.

Made words a high level word. Added macros and a common factor words'.

true and false affect the condition codes for a future if.

i! now only performs an erase cycle if a bit needs to change from 0 to 1; saves flash cycles.

Added high level register definitions for the at90can128. My main avrforth development board is the cansynth.

replaced traditional conditionals with ??, which performs a nondestructive comparision (much like ? is a nondestructive test). added support for the atmega16. the compiler now uses relative addressing instructions if they will reach (rcall and rjmp).
added support for the at90can128. added um/mod, um/mod2, and m/mod. moved usart words to a separate driver source file.
new words: .", um*, m*, and *. Also changed over a few more i/o instructions to use the optimizing macros.
changed all io defines to memory mapped addresses. the macros in io.asm will convert to io addresses if appropriate. this trick avoids having to use a memory mapped instruction just becuase one supported device has an io register out of range. also cleaned up a bunch of warnings due to supplying .db with an odd number of bytes.
added support for atmega32. added two new words. jump implements jump tables, and wait waits for a bit flag deferring with pause until the flag is set. also changed a few calls to rcalls.
modified source to build with avra.
literals are now inline. this method uses one more word of flash per literal but is much faster and i hope to optimize away most literals anyway. +, -, and, and or are optimized for literals in the kernel. the next step is to add macros to use the optimizations in compiled code. cells, icells, and ecells have been removed. since you already need to keep track of which memory space you're using on the avr, remembering to add in a 2* is not such a big deal.
added cooperative multitasker. taskinit initializes a task stack frame. taskqueue puts a task in the round robin queue. pause defers execution to other tasks. key and emit now call pause when blocking.
made if and then high level words. added -if. if and -if are non-destuctive now (they branch based on cpu flags and don't consume the top stack item). added atmega8 as supported target. added rshift and lshift.
added eeprom words e!, edp, ~edp, ehere, eallot, e,, and ecells. also added mark and empty to save/restore interpreter state.
2*, 2/, 1+, and 1- are now macros. added negate.
huge update, including...