uForth Quick Guide


Todd Coram (todd of maplefish.com)

DRAFT DRAFT DRAFT
Dec 4, 2009
Latest release is 0.99
A prebuilt MSP430 binary for the MSP430 based EZ430
Changes here.

Introduction

uForth is a Forth like scripting system for microcontrollers with limited resources. It is written in C and is best used as a glue language to tie C functions and services together dynamically. With that intention, uForth is not a full system Forth. It is neither ANSI compliant nor suitable for Forth programming from the ground up.

uForth can be compiled as a 16 bit or 32 bit stack Forth. The dictionary is stored as 16 bit values.

The VM and interpreter fits in as little as 8KB of Flash and 1KB of RAM (assuming, but not counting, a flash resident dictionary).

The dictionary stores all entries as 16 bit cells in the processor's native endianess. The dictionary can be saved and loaded on processes with the same endianess. It should be trivial to transform the dictionary between processes of different endian.

uForth implements ~55 primitives in C. While some of these words can be defined in uForth itself, they are implemented in C for space efficiency and speed.

Limitations

uForth is a scripting language. It is still very much a Forth, albeit a tiny one. As such, it has limitations.

uForth is not a strong meta Forth environment. You can define new words (:), create, and mark words immediate, but there is no DOES>, nor any decent way to disassemble words (yet!).

There is some limited support of strings and characters. Strings are packed into 16 bit cells. The lack of rich string support further distinguishes uForth scripting from traditional Forth system building.

There is a PAD, but there is no general RAM based string storage. If you want more string support you can add it via Scripting C.

uForth doesn't support floating point.

uForth has no notion of I/O. You can easily provide this by extending uForth with C (see Scripting C for an example).

uForth keeps word headers and code together. While not a real limitation this does make it a bit more difficult to generate a compact headerless dictionary.

uForth primary cell format is as a signed int. This limits uForth to a 32KB dictionary (RAM or ROM) and 32KB user RAM. (See uForth Addressing).

uForth's stack can be compiled for 32 bit or 16 bit integers. When 32 bit, all of RAM is treated as 32 bit cells. The dictionary remains 16 bit (32 bit numbers are stored as 16 bit pairs in the dictionary) but all numeric stack operations are 32 bit.

Don't forget: uForth is a scripting language, not a systems language. If you are using a lot of dictionary or user RAM space, you are doing something wrong. Go find a real Forth!

Why all of these limitations? Read on...

Goals

Here is a short list of uForth goals:

  1. Support scripting C functions and libraries.
  2. Embed into existing C programs.
  3. Run on small microcontrollers.
  4. Support cross platform development.
  5. Fast and light.
  6. Easily extendable via C.
  7. ROMable dictionary.
  8. Cross compilation and development (portable dictionary).
  9. Simple virtual machine.

Scripting C using uForth

To support scripting, uForth is easily extended with C. C code has direct access to uForth memory and stacks. There is a single entry function called uforth_stat c_handle(void). It takes no parameters since it is expected to pop values off of the uForth data stack (and leave results there too).

The c_handle() function is invoked via the cf uForth word. A typical registration by a C application could look like this:

  uforth_interpret(": emit 2 cf ;");
  uforth_interpret(": key 3 cf ;");
  uforth_interpret(": mem 4 cf ;");
  uforth_interpret(": type 5 cf ;");
  uforth_interpret(": save-image 7 cf ;");
  uforth_interpret(": load-image 8 cf ;");

The c_handle() function for the above definitions would begin like this:

uforth_stat c_handle(void) {
  CELL r1 = dpop();
  char *str;
  switch(r1) {
  case 1: /* dot  */
    if (iram.didx > -1) {
      r1 = dpop();
      printf("%d ",r1);
      fflush(stdout);
    } else printf("EMPTY\n");
    break;
  case 2: /* emit */
    printf("%c", dpop());
    break;

You can also call uForth from C, passing words as C strings. This is provided by the uforth_stat uforth_interpreter(char*) function.

The Portable Dictionary

uForth doesn't directly support writing to a Flash stored dictionary. You can designate the dictionary as RAM based or ROM (Flash) based. It is up to your application to work out how to compile definitions to Flash (see TBD example).

The PC (Unix/Cygwin) uForth allows you to save dictionary snapshots with the save-image command. You specify the output file (for example: save-image core.img) and it will dump the dictionary. This dumped dictionary can be loaded into a uForth at compile time. That is, you can do script development on the PC and then save the script as a loadable dictionary. There is C program (make_core_dict) that can convert this image into a C array that can be included in a target build via a generated header file (e.g. core_dict.h). This array can be designated as ROM or RAM based (depending on your target compiler).

Since uForth's C extension is based on tokens (numerical values as selectors), you can pretty much move this dictionary to any target uForth so as long as the token convention is followed!

uForth Addressing

Addressing in uForth is restrictive to relative indexing. There are two address spaces: dictionary and uram (there is also an iram but it doesn't support addressing). The dictionary may live in RAM or ROM. If it lives in ROM, special functions may be called to read and write values.

In treating the dictionary and uram as two separate address spaces, it can be said that uForth adopts a Harvard architecture.

The dictionary is addressed by 16 bit cells. The address is expressed as in index starting at 0.

The uram space is also addressed by 16 bit or 32 bit cells (depending on how it is compiled). The dictionary and uram share a 64KB indexable space that can be segmented for a harvard style processor.

uForth comes with the usual Forth words. It can be considered a subset of ANSI Forth. Where ANSI word names are used, ANSI functionality should follow.

uForth is case sensitive and uses lowercase for core words.

Words

uForth C code constains ~41 primitive words. Many of these words could be written in uForth itself, but in order to save precious dictionary space, they are written as primitives. In fact, some of the immediate primitives generate other primitives (a prime candidate for coding in uForth!). Over time, I am moving more words out of C core and into uForth.

uForth makes no claim to ANSI compatibility, but where it can, it will make words you can find in ANSI indeed compatible with ANSI.

Here are the currently implemented primitive, init.f, util.f and core.f words:

p4ren  p4ifg  p4dir  p4out  p4in  p3ren  p3ifg  p3dir  p3out  
p3in  p2ren  p2sel  p2ifg  p2dir  p2out  p2in  p1ren  p1sel  
p1ifg  p1dir  p1out  p1in  words  _cc  greet  version  memory  
debug"  debug  debug-pre  d.  .s  .  ."  [char]  char  space  cr  
max  min  binary  decimal  hex  count  if>0  >d  vs  allot  is  
defer  endcase  endof  of  case  _endof  until  repeat  while  
again  begin  exit  unloop  loop  +loop  leave  j  i  do  
_leaveloop  then  else  if  constant  variable  create  [compile]  =  
mod  decr  incr  +!  >=  <=  >  r@  rot  2drop  over  dup  2*  
2+  1-  1+  not  negate  tos  rsa  dsa  rslen  dslen  ridx  
sidx  compiling?  base  maxdict  dversion  cmem!  cmem@  mem!  
mem@  sleep  time  ms  save-image  type  key  emit  {  (  \  
include  <  here  word  lwa  postpone  ,"  '  [']  _allot1  
interpret  _create  :  ;  !  @  r>  >r  dlit  lit  0=  */  /  *  
rshift  lshift  invert  xor  or  and  -  +  dummy,  ,  exec  0jmp?  
jmp  swap  rpick  drop  pick  abort  immediate  uram cf  

Changes

Version 0.99

Version 0.98

Version 0.97

32 bit numbers

Words

Version 0.96 (27oct2009)

MSP430

Version 0.95 (20oct2009)

Words

Version 0.92 (15oct2009)

IRAM and URAM

Version 0.91 (14oct2009)

Defining words

Version 0.9 (09oct2009)

32 bit numbers

exec

Version 0.8 (07oct2009)

uforth core

Version 0.7 (07oct2009)

msp430

variables

Version 0.6 (06oct2009)

Fixed unix compilation

msp430

Version 0.5 (05oct2009)

Refactored Shells

Preliminary work for Flash support

Version 0.4 (01oct2009)

Loops

MSP430 Dictionary



This document was generated using AFT v5.097