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.
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...
Here is a short list of uForth goals:
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.
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!
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.
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
This document was generated using AFT v5.097