Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Embedded Systems

Ficl: An Embeddable Extension Language Interpreter


Jan99: Ficl: An Embeddable Extension Language Interpreter

John works for Hewlett-Packard. He can be contacted at [email protected].


Sidebar: Ficl Syntax (and more)

Glue languages such as Perl, Tcl, and Python are popular because they help you get results quickly. They make quick work of problems that are often tedious to code in C or C++, and they can work with code written in other languages. People often miss this last benefit because Perl and Python usually run standalone. Tcl, on the other hand, was designed from scratch as an extension language and is relatively simple to insert into another program. All of these languages were designed to work with mainstream operating systems, so they require lots of memory, a filesystem, and other resources that are commonplace on a modern PC or workstation.

In this article, I'll describe an interpreter that has a system interface similar in spirit to Tcl, but is specifically designed for embedded systems with minimal resources. Because the syntax is ANS Forth, I call it "Ficl," short for "Forth-Inspired Command Language." (From here on, I'll use the terms "Forth" and "Ficl" interchangeably.) The complete source code for Ficl is available electronically; see "Resource Center," page 5.

Ficl is Forth, but you don't have to be a Forth zealot to use it. In fact, you can learn enough Forth in a half-hour to begin using it.

Ficl is a small but complete programming language -- not just a command interpreter -- so you can hack a prototype for new behavior. Because Ficl is interactive, you can use it to help troubleshoot problems. One further advantage of using Ficl is that you can find several good tutorials for free on the web, so you save time on documentation and training -- you only need to document extensions to the language.

Furthermore, people who do not program for a living can do useful work with Ficl. Electrical engineers can easily benchtest new hardware using Ficl, saving you time to add features or fix bugs (not that there would be any). You can use a minimal Ficl system to do rapid prototyping on new hardware and get new features to a demonstrable state in much less time than would be required with a compiled language.

Using Ficl

In the examples that follow, you may wish to download the Ficlwin executable and play with the language. Ficlwin has some simulated hardware that will be useful later. Ficlwin runs under Windows 95 or NT.

The first rule of Ficl is to use spaces to separate everything. Forth is simple minded about parsing its input. It looks for space-delimited tokens and tries to interpret them one by one.

The second rule of Ficl is this: If it's not a word, try to make it a number. If that does not work, it's an error. A word is a named piece of code (like a function or subroutine) that may also own some data. Words are organized into a list called a "dictionary." For each token in the input stream, the interpreter tries to find a word in the dictionary with the same name. If successful, the interpreter will execute the word; otherwise, the interpreter will attempt to convert the token to a number. If the conversion fails, you get an error message. (By the way, the second rule means that you can do evil things such as redefining your favorite number.)

The third rule of Ficl is that words find their arguments on a stack. The interpreter pushes numbers onto the stack automatically. The language does not have the equivalent of a function prototype, so Forth programmers use comments to show the state of the stack before and after execution of a word. For example:

+ ( a b - - c )

indicates that the word "+" consumes two values from the stack (a and b) and leaves a third (c, the sum). An open paren followed by a space tells the interpreter to treat everything up to the next close paren as a comment. You can comment to the end of a line with a backslash character followed by a space.

Here's how to add the two numbers in Forth:

2 3 + .

The interpreter pushes the "2" and the "3," then executes the word "plus" and finally executes the word "dot," which prints 5, the top value on the stack.

There are really two stacks in Forth -- one stores return addresses and the other stores parameters. I'll refer to the parameter stack simply as "the stack" from now on.

Ficl is not case sensitive. Word names can be up to 255 characters long, but only the first 31 characters will be stored. All words are stored in a linked list.

The main Ficl data structures are:

  • A virtual machine that stores one execution context, and would typically map to a thread.
  • Two stacks within each virtual machine; one for parameters and the other for return addresses.
  • A word that binds a name to code, and optionally, data.
  • The dictionary -- a list of all words of the system.

See the accompanying text box entitled "Ficl Syntax (and More)" for more information.

Porting Ficl

As Listing One shows, it only takes a few lines of code to hook Ficl into your system: Initialize the Ficl system-data structures with a call to ficlInitSystem, and create one or more virtual machines using ficlNewVM. After that, you simply feed blocks of text to the virtual machine from an I/O device, a file, or stored strings using ficlExec. You can tear down memory allocated to Ficl with ficlTermSystem.

Ficl requires an ANSI C compiler and its run-time library to build and execute. Porting to a new CPU involves editing the files sysdep.c and sysdep.h. The header file contains macros that control build properties of Ficl and insulate the implementation from differences among compilers. Ficl interfaces to the operating system via four functions:

  • FiclMalloc and ficlFree map closely to the standard C malloc and free functions, but they act as a choke point for Ficl memory management in case your operating system has specialized functions for this purpose or you've rolled your own OS.
  • ficlLockDictionary provides exclusive access to the dictionary in multithreaded implementations. If you only intend to make one Ficl Virtual Machine, this function can be empty.
  • ficlTextOut acts like standard C's puts, except that it takes a virtual machine pointer and one additional parameter to indicate whether or not to terminate the string with a newline sequence.

Although Ficl is a 32-bit Forth, the language requires some 64-bit math. There are two unsigned primitives in sysdep.c that handle this. One function multiplies two 32-bit values to yield a 64-bit result, and the other divides a 64-bit value by a 32-bit value to return a 32-bit quotient and remainder. These are usually simple to implement as inline assembly for a 32-bit CPU (see the Intel 386 example in the source). I was too lazy to come up with a generic version in C. If you're also lazy, you can kludge these functions to use only the low 32 bits of the 64-bit parameter and be safe -- as long as you avoid multiplying and dividing really big numbers.

Memory requirements of the code vary by processor. The dictionary is the largest RAM-resident structure. The word-set that comes with the source requires about 1000 cells or 4 KB. Stacks default to 128 cells (512 bytes) each, so you can fit a useful implementation into 8 KB of RAM plus code space (which can be in ROM).

Use testmain.c as a guide to install the Ficl system and one or more virtual machines into your code. You do not need to include testmain.c in your build. The source package includes a Win32 executable that helps you get a feel for the language.

Rolling Your Own Extensions

You can extend the language with words, written in C, Forth, or a mixture of C and Forth, that are specific to your application. Use the ficlBuild function to bind a C function to a name in the dictionary. Functions that implement Ficl words take one parameter: a pointer to a FICL_VM. This pointer refers to the running virtual machine in whose context the word executes. The files words.c and testmain.c have (literally) hundreds of examples of words coded in C. Listing Two is a function that interfaces ficl to the Win32 chdir service.

To write a new Ficl word in Forth, follow the examples in softcore.c; embed the source in a string constant and feed the string to ficlExec once you have a virtual machine created.

You'll also find some examples of words coded in a mixture of C and Forth in words.c (see evaluate, for example). Because ficlExec calls can be nested, you can invoke ficlExec from within a function that implements a word and feed it a string argument, effectively mixing the two languages.

An Example

Suppose that you have a simple target board that has a block of eight LEDs, eight DIP switches, an 8-bit analog to digital converter, and an 8-bit DAC. (Conveniently, this is what Ficlwin simulates). You start by setting up an address map.

The first line in Example 1 tells Ficl that you're going to be writing numbers in hexadecimal. The next four lines set up named constants for the registers you want to use. When you invoke one of these constants (by typing its name), it pushes its value.

A constant pushes its value when invoked; and a variable pushes its address when invoked. The Ficl word @ (the at sign) fetches the contents of an address and puts the 32-bit value on the stack. Example 2 defines words that fetch and store using the address constants defined earlier. Ficl also has c@ to fetch a byte, and w@ to fetch 16 bits. Likewise, ! w! and c! store a value at an address. The syntax is ( value address -- ), meaning that the operations consume a value and an address from the stack. Example 2 creates Ficl words that hide the width of the registers they use by wrapping the fetch or store operation. Ficlwin implements the four words in these examples so that you can try the rest of the code verbatim.

The first line in Example 3 creates a variable to track the LED state. (Real hardware engineers often find it too expensive or too bothersome to add a readback capability to digital output registers.) The word on the second line writes the LED register and updates the shadow variable. The last line forces the LEDs off.

The word in Example 4 toggles an LED by index (0..7). LSHIFT is equivalent to C's << operator.

Finally, Example 5 loops, writing the ADC value back to the DAC until the ADC value reaches 255. After each ADC sample, there is a 100 millisecond pause (msec is implemented in Ficlwin with the Win32 Sleep function).

Although these are simple examples, they give a feel for the process you use to bring up hardware with Ficl. I invite you to try them with Ficlwin.

For More Information

http://www.taygeta.com/forth.html
http://www.taygeta.com/forth/dpans.html
http://www.fig.org/fig.html
http://www.forth.org/forth_intro/stackflo.htm
http://astro.pas.rochester.edu/Forth/forth.html
http://www.gis.net/~dmiller/mmsforth.html#BOOKS
ftp://ftp.taygeta.com/pub/Forth/Compilers/native/misc/

DDJ

Listing One

FICL_VM *pVM;ficlInitSystem(10000); /* create a 10,000 cell dictionary */
pVM = ficlNewVM();


</p>
for (;;)
{
    int ret;
    gets(in);
    ret = ficlExec(pVM, in);
    if (ret == VM_USEREXIT)
    {
        ficlTermSystem();
        break;
    }
}

Back to Article

Listing Two

/* Ficl interface to _chdir (Win32)** Gets a newline (or NULL) delimited string from the input
** and feeds it to the Win32 chdir function...
** Usage example:
**    cd c:\tmp
*/
static void ficlChDir(FICL_VM *pVM)
{
    FICL_STRING *pFS = (FICL_STRING *)pVM->pad;
    vmGetString(pVM, pFS, '\n');
    if (pFS->count > 0)
    {
        int err = _chdir(pFS->text);
        if (err)
        {
            vmTextOut(pVM, "Error: path not found", 1);
            vmThrow(pVM, VM_QUIT);
        }
    }
    return;
}
/* Here's the corresponding ficlBuild call... 
** ficlBuild("cd",       ficlChDir,    FW_DEFAULT);
*/

Back to Article


Copyright © 1999, Dr. Dobb's Journal

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.