FVM
Forth-like system for the TI MSP430
and Linux/x86

Currently under development.

Unstable and Unfinished
Software and Documentation

 

 

Introduction and Concepts

Froth is NOT Forth

Installation

Start

First-contact: creating an Anonymous Definition

Creating a Named Definition

BUILD>..DOES>..

Download FVM

Others Documents

Licensing

 

Introduction and Concepts

FVM is both a variant of the Forth Languag (target) and a Cross-Development Environment (host).

The goal is to provide a Forth-based environment, suitable for CPU, MPU or DSP with very little memory. FVM is currently ported to the MSP430 F1121, which has only 4K of code (Flash), and 256 bytes of RAM. I added also support Linux/x86 as target: no need for additional hardware, and convenient for prototyping some code.

This Forth-like language does not try to follow any kind of Forth language standard. We strongly believe that Forth should be considered more as a simple and flexible concept rather than a normalized and standardized thing. A real Forth system should be anyway customized to suit the needs and the requirement of both the project, the hardware and the programmer.

The Interactive Cross-Development environment is running on a host (PC running Linux or FreeBSD), communicating with the device running FVM using a kind of umbilical link (JTAG, serial, Ethernet, mapped memory, etc.). The development environment does not include a simulator, so the only way to use the compiler is to connect it to a real hardware (currently only the Texas-Instruments fet430x110 board is supported). I added recently some x86 support, so any PC running Linux could be used for prototyping (in this case, communication between the host and the ‘target’ –actually a linux process- is done through TCP/IP).

Although FVM could be easily ported to any CPU, MPU or DSP (16-bits model only for now), we have chosen to focus for now only on the Texas-Instrument MSP430.

In the following documentation, we’ll refer to:

-         Froth is the language running in the Target: very close to the Forth language, with differences and extensions

-         FVM is the cross-development environment, running on the host (PC Linux or FreeBSD)

Froth is a Stack-Based langage, and use the next Stacks and Registers:

-         a Parameter Stack: used to exchange data between definitions (a definition can be compared somewhat as a procedure or a function, but a variable, a constant are also a definition in Froth).

-         a Return Stack: mainly used to save/restore call contexts.

-         an Address Register (AREG), to reference memory.

-         a pointer (IP) into the code stream, known as the “Interpret Pointer”.

The FVM cross-compiler generates addresses codes that are interpreted at run-time by an optimized address interpreter. This common Forth implementation model is known as Indirect Threading Code. Currently, FVM is only 16-bits (which is anyway the memory model for the MSP430), but I am thinking to support also a 32-bits model for CPU where it would make more sense.

The code of the Inner Interpreter (e.g. the Address Interpreter) is here (MSP430) and also here (x86/Linux).

Code for arithmetic (16-bit) is here and also here (x86/Linux).

Code for stack management (16-bits) is here and also here (x86/Linux).

Froth is NOT Forth

Although Froth is derived from the Forth language and proudly assume its heritage, it is not really strictly speaking Forth. We did not tried at all to follow any kind of ‘standard’ or make FVM compatible with any kind of current Forth implementation. There are many differences with 'standard' Forth, but the most important are:

-         the host cross-compiler (fvm-host) is written entirely in C, and not in Forth: only the Forth runtime-related definitions are running into the target, and all the compilation aspect is solely managed on the host. For instance, : (colon), ; (semi-colon), BEGIN, LOOP, IF, etc. are all written in C, but generate Froth code for the target. The main impact is that you can create code for the target, but not for the host (i.e. any new definition is targeting your target, not the host).

-         the compiler is always compiling into the target

*        either an anonymous definition (un-named definition)

*        or a named definition (colon definition)

-         when ; (semi-colon) is parsed:

1.      if this is an anonymous definition: the compiler either execute the code in the target, then 'forget' it

2.      if this is a named definition, the cross-compiler just update his internal pointers ('compilation')

-         a side-effect to this model is that in order to compile some code, the compiler must connect to a target - could be real hardware, or a simulator.

-         memory access is managed trough an Address Register (see >A, A>, !, @): addresses are not explicitly provided on the stack but make use of the Address Register.

-         The build> does> construction scheme is really different from the fig-forth model, and allow use of local (automatic) variables, named field structures, and some object-oriented functionalities (methods).
Note: only automatic variables are currently implemented for now.

-         It is not allowed to redefine a definition.

-        The compiler does stack-balancing checking (only the parameters stack for now, but the return stack should be done too soon). That should avoid stack underflow issues at run-time. A drawback is that the stack protocol should be the same for every branches of the control structures. For instance, the next definition : test IF 10 ENDIF + ; is not allowed in Froth, but the next definition will be : test IF 10 ELSE 20 ENDIF + ;

Installation

             “the hard way”: from the sources

Download the full .tar.gz source archive, then use the Make. For instance:

~/temp$ wget -q -E http://singla.us/cgi-bin/viewcvs.cgi/fvm430/fvm430.tar.gz?tarball=1

~/temp$ mv fvm430.tar.gz\?tarball\=1 fvm430.tar.gz

~/temp$ tar -z -f fvm430.tar.gz -x

~/temp$ cd fvm430

~/temp/fvm430$ make

 

            All binaries and DLLs are under fvm/bin, and you must define an environment variables named FVM_PATH:

~/temp/fvm430$ cd fvm/bin

~/temp/fvm430/fvm/bin$ export PATH=~/temp/fvm430/fvm/

 

            Installing and starting: from the rpm

Download then install the .rpm file Note that the rpm install the package under /var/fvm430. You may want to change permissions inside this directory, if you want to change or create some examples (under /var/fvm430/tests):

~/temp$ wget -q http://singla.us/rpms/fvm430-0.1-36.i386.rpm

~/temp$ su

Password:

/home/osingla/temp# rpm -qa | grep fvm430

fvm430-0.1-36

/home/osingla/temp# rpm -e fvm430-0.1-36

/home/osingla/temp# rpm -Uvh fvm430-0.1-36.i386.rpm

Preparing...                ########################################### [100%]

   1:fvm430                 ########################################### [100%]

/home/osingla/temp# exit

~/temp$ cd /var/fvm430/bin/

/var/fvm430/bin$

Start

            “the hard way”

You’ll need to connect your PC to the fet430x110 board. If you do not have such board, you can use the x86 forth target linux process (see “the easy way”): in this case, there is a process to start first (fvm86), which create a pseudo-target (x86).

 

Start the FVM development environment (fet430x110 board, see the .gdbinit-fet430x110 gdb startup script):

~$ cd /var/fvm430/bin/

/var/fvm430/bin/$ export FVM_PATH=/var/fvm430/

/var/fvm430/bin/$ ./fvm_host --target-port=0 --profile="MSP430F1121A" \

   --umbilical="fet430x110" --editor="/usr/X11R6/bin/nedit" --goto-line-split \

   --goto-line-option="-line" --mass-erase

 

The target-port=0 options means the 1st parallel port (jtag connection). Both the profile and the umbilical options are implemented as DLL, so it’s critical to have the right path setup (through the environment variable FVM_PATH – although if it does not exist, the default is the one we have specified).

 

Sounds too complicated ? Then just start the next shell script:

~$ /var/fvm430/bin/start-fet430x110

 

             “the easy way”

If you do not have a fet430x110 board, you then can start the x86 target process (see the .gdbinit-86pclinux gdb startup script):

~$ cd /var/fvm430/bin/

/var/fvm430/bin/$ ./fvm86 &

/var/fvm430/bin/$ export FVM_PATH=/var/fvm430/

/var/fvm430/bin/$ ./fvm_host --target-port=localhost:8086 --profile="86pclinux" --umbilical="pclinux" --editor="/usr/X11R6/bin/nedit" --goto-line-split --goto-line-option="-line" --mass-erase  --kernel=pc86/kernel.fvm --startup=pc86/startup.fvm

 

Note: you may want to customize the source editor that will be used to edit the sources (spawned by FVM). The shell script assume you have nedit in /usr/X11R6/bin.

 

Sounds too complicated ? Then just start the next shell script:

~$ /var/fvm430/bin/start-fvm86

 

            Start

If the host can connect to the target (either real hardware or x86 process-target), you should see something like this:

 

FVM v0.1.35 - compiled on Feb 17 2004 19:52:21

                                                                                                            

 Profile: 86pclinux

     RAM: start=0200 end=A000 len=9DFF

Boot ROM: start=0C00 end=1000 len=0400

Flash IM: start=1000 end=1100 len=0100 Segments: sz=0080 nb=2

Flash PM: start=1100 end=0000 len=EF00 Segments: sz=0200 nb=120

  Anodef: start=0208 end=0216 len=000F

  rstack: start=0256 end=0219 len=003E

  pstack: start=0270 end=0257 len=001A

                                                                                                            

Switching from code address 1100 to code address F800 .

Label next is F85B at F89B ...

Starting Kernel...

Kernel started... ........................................................

Label next is F85B at FC0F ................

Switching back from code address FCB0 to code address 1100 ......

Target: 82 words

anodef0=0208 anodef=0208 - cp0=1100 cp=11B2 - dp=0270 areg=0270

Stack empty

Current stack effect: parameters:0, 0  return:0, 0

Current number of CPU cycles:0

                                                                                                            

Use command 'help' to get some help!

Froth is *always* compiling: use ; (semicolon) to execute!

(0)

First-contact: creating an Anonymous Definition

Use command 'help' to get some help!

(0)

 

(0)  shows the number of data currently available on the Parameters Stack of the Target. Enter something like:

(0) 10 30 +

(0)

 

Basically you asked the cross-compiler to generate some bytecode into the target to perform the next operations:

-         push the literal value (decimal) 10 on the Parameter Stack

-         push the literal value (decimal) 30 on the Parameter Stack

-         pop the 2 top values from the stack, add them, and push back the result

 

Why we still see (0) ?
We should see (1), meaning that there is a value on top of the Parameters Stack into the Target!

 

That’s because, until we ask the anonymous definition to be executed into the Target, the cross-compiler just generate bytecode into the Target. We can see the bytecode generated for the Anonymous Definition by entering this command:

(0) ??

 

Then we should see something like this:

(0) 10 30 +

(0) ??

Target: 109 words

anodef0=0208 anodef=0210 - cp0=F000 cp=F0F2 - dp=0270 areg=0270

Stack empty

Current stack effect: parameters:0,+1  return:0, 0

0208: F962 (lit32)         1966090

020E: FA2E +

(0)

 

Note: you should normally see (lit16) 10 (lit16) 30, but actually as the optimizer was active when I typed this, the compiled choose to compile the two 16-bits literals as one 32-bits literal.

 

The command ?? is very useful to see the current anonymous definition compiled into the target. Now let’s run this anonymous definition, using the semi-colon definition:

 (0) ;

 (1)

 

This is what we expected to see: the (1) means that there is a value available on top of the Parameters Stack into target. The command ?? could be used to show the Stack into the target. We can use also the definition . (dot) that pop the top value from the stack and display it:

 (1) . 40

 (0)

Creating a Named Definition

Creating a Named Definition is not very different from creating an Anonymous Definition: the first step is obviously to declare a name for the new definition. This is done using the colon definition (:). As a simple example, let?s create a definition that expects 2 integer values (A and B) from the Parameters Stack, and then return (A+B/2:

(0) reset-optimize

(0) : (3A+5B)/2 5 * swap 3 * + 2 / ;

(0)

 

The definition name can contains any symbol different from the space (as the space is used as symbol separator). Also, the new definition must neither be a number (for instance :10 is not allowed), or an existent definition. We have used also set-tokenize, to ask the compiler to generate a bytecoded definition (as there is a limited room for bytescodes in a 8-bits VM implementation, not all definitions can be tokenized).

 

We can list the current definitions defined into the Target using the host command */words/*:

(0) words

(0)

 

the host word decompcan be used to decompile a given definition:

 (0) decomp (3A+5B)/2

Code: start=F0F2 end=F10C

stack_effect=2,-1 rstack_effect=0, 0

F0F2: F8BE (:)

F0F4: F8CA (lit)           5

F0F8: FA86 *

F0FA: F8E4 swap

F0FC: F8CA (lit)           3

F100: FA86 *

F102: FA2E +

F104: F8CA (lit)           2

F108: FAAE /

F10A: F8C4 (;)

(0) 50 20 (3A+5B)/2

(0) ??

Target: 110 words

anodef0=0208 anodef=0212 - cp0=F000 cp=F10C - dp=0270 areg=0270

Stack empty

Current stack effect: parameters:0,+1  return:0, 0

0208: F8CA (lit)           50

020C: F8CA (lit)           20

0210: F0F2 (3A+5B)/2

(0) ;

(2) .s

Stack (2):  250 2

(2) whatis (3A+5B)/2

User 'colon' definition: start=F0F2 end=F10C  Parameters:2,-1 Return:0, 0

(2)

 

BUILD>..DOES>..

One of the most interesting feature of Forth is the power of the BUILD>..DOES>.. construction. Basically, using these two definitions, you can create a definition that will create other definitions. Therefore, you’ll have to define 2 things:

-         what will be performed at compile-time (e.g. when new definition is created)

-         what will be performed at run-time (e.g. when new definition will be executed).

 

There is a 3rd definition that is always used with BUILD> DOES>, which is ALLOT: used to allocate memory.

 

Let’s take a 1st very simple example. We’ll create a definition called CONST which can be used to create new literal constant values (16-bits). Note that FVM offers a similar built-in word (CONSTANT), which is doing the very same thing. We’ll use CONST to demonstrate the purpose and use of BUILD> DOES>.

 

: const

  build> 1 allot !

  does> @ ;

10 const ten

100 const cent

: test ten cent + ;

 

(0) test ;

(1) . 110

(0)

 

-         After build> we define what will be executed when the definition will be created (usually it’s compilation-time, but with FVM, that’s not always true!). We do reserve one cell of memory (one cell is 16-bits, depending the hardware supported by the compiler): 1 allot. Note that allot assigns the value of the Address Register to the new allocated memory [the Address Register is not Forth standard and used by definitions such @ (fetch) and ! (store)]. Then we use ! (store) which save in memory the value given when const is called.

 

-         After does> we define what will be executed when the definition will be called. It’s very important to know that the Address Register is then set to the strarting address of the memory which has been allocated. Therefore in our example, we have just to read (@) to get back the value.

 

Here is another example: we’ll create a definition called INT (for Integer) that can be used to create 16-bits integer variables:

 

: int

  build> 1 allot

  does> ;

int x

int y

: test1 10 x ! 100 y ! x @ y @ + ;

: test2 int a int b 5 a ! 7 b ! a @ b @ + ;

 

(0) test1 ;

(1) . 110

(0) test2 ;

(1) .

(0) 12

 

A more complex example: we’ll create a definition called DATE, that can be used to hold date information. We have somewhat extended the BUILD> DOES> concept to support named fields and some pretty limited object oriented functionalities:

 

: date-weekday  ( AREG> day month year )

  blah blah ... ;

: date

  build>

     3 allot        (day month year --- day month year)

     rot            (day month year --- month year day)

     !+             (month year day --- month year)

     swap           (month year --- year month)

     !+             (year month --- year)

     !

  does>

     @+ @+ @        ( --- day month year)

  fields>

     field: day offset: 0

     field: month offset: 1

     field: year” offset: 2

  methods>

     method: weekday is: date-weekday

;

14 1 1789 date french-revolution

23 5 1961 date my-birthday

: test1 my-birthday.year french-revolution.year - ;

 

(0) test1 ;

(1) . 172

(0) my-birthday.weekday ;

Download FVM

Currently, FVM is provided either as source file (tar.gz file, under CVS, you then will have to compile the things), or as a RPM (binaries, DLLs, compiler files, examples).

 

To compile the application, first read the file readme.1st, but basically, just run the Makefile (gnu make) at the top directory. The compiler, and all the DLLs will then be built. The MSP430 kernel is also built, I wrote a complete MSP430 assembler, therefore all the low-level definitions written in assembler are defined using CODE..ENDCODE. MSP430 use several DLLs: I have chosen to ‘externalize’ several components, such the umbilical communication –serial, tcpip, jtag-, the assembler and the hardware profile. The whole idea is to have a stable core cross-compiler, that can be extended to handle different targets, even with a different type of CPU, by just providing several DLLs on the command-line.

 

I compiled and tested FVM under Linux (RedHat 8.0, 9.0, Fedora and also Mandrake), and also under FreeBSD (v4.3, v5.0). I did not tested it under FreeBSD since many months, so it will likely require some modifications (likely for the // interface).

 

Although not mandatory, the ctags and cproto utilities are used by the Makefiles if present (in /usr/local/bin). Actually, I do believe that cproto could be mandatory (if you download the code using CVS, I do not include under CVS all the prototypes files, as they can be built using cproto: make protos).

 

You will need glib2 : I use it for several features (such managing binary trees –for the symbols management- and tab completion). You will need also curses.

 

What do you need to run and test the cross-compiler suite ?

-         either you have the fet430x110 board (the fet430x110 cost only $49): the interactive cross-compiler must then be connected to it (// cable, jtag)

-         if not, you can use the fvm86 target process: this standalone Linux process allocate 64K of memory, mmap to address 0, and with then listen to request coming from the host FVM compiler (TCP port 8086): read/write memory, execute memory. This is a cheap emulator, but it works fine, and allow to prototype any code which is not tied to the MSP430 architecture. Note that I did not tried to make any optimisations, for instance the inner forth interpreter (next) is really horrible… Also, I have created some definitions that give access to the linux kernel calls (sys0, sys1, etc.), see linux.fvm

 

To see how to start the cross-compiler, check here

 

 

C Sources
(Linux only)

svn co https://fvm430.svn.sourceforge.net/svnroot/fvm430 fvm430

Anonymous SVN access

Browse CVS

Others Documents

Inside FVM

Definitions Glossary

Licensing

This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

 

This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 

Please take a look at the GNU Web Pages for a copy of the GPL license: http://www.gnu.org/licenses/gpl

 

 

Olivier Singla

Pages created:
December 2002

Pages last revised:
September 16th 2007

SourceForge.net Logo