by
Mikael Nordman
FlashForth is a standalone native Forth operating system
implemented on the Microchip PIC18F processor family.
FF
(FlashForth) allows you to write and debug complex real-time
applications on the PIC. The complete system including the compiler
is executing on the PIC.
A Forth interpreter, compiler,
assembler, multitasker and user definable interrupts are provided.
A computer with a terminal emulator is used for communicating
with FF via a serial link.
The source files are edited and saved
on the computer and uploaded to the PIC as source code.
All
PIC registers and memories can be read and written from the command
line.
When the application is ready, the application word
address is stored in the turnkey vector, and your embedded
application has been set up.
FlashForth is mostly compatible
with the ANS'94 standard. This guide describes the differences and
features not covered by the ANS'94 standard.
Here are a few sources for learning
Forth.
http://www.albany.net/~hello/simple.htm
http://www.softsynth.com/pforth/pf_tut.htm
http://www.mpeltd.demon.co.uk/arena/ProgramForth.pdf
http://www.forth.com/starting-forth/
The ANS forth standard in HTML can be found here http://www.taygeta.com/forth/dpans.htm .
Firstly, I want to thank my family for tolerating me during this
project.
Many thanks to Joe Ennis, W7NET, and Pete Zawasky,
AG7C, for pushing FlashForth hard and bugging me with trouble
reports.
Here you can see the bot that Joe Ennis is building. It is running
FlashForth.
http://www.youtube.com/results?search_query=flashforth&search_type=
Send
me a mail via SourceForge if you want to see your application listed
here.
FF can be downloaded from http://www.sourceforge.net/projects/flashforth .
Expand the downloaded archive into a folder. The predefined MPLAB
project file is for the folder C:/FlashForth_33.
Select a
p18Fxxxx-xxxx-xxxx-xxxx.cfg file which supports your target
processor. Update the clock frequency and PLL info according to your
needs. If you have a low clock frequency you may need to lower the
serial baud rate. The configuration files has configuration options
for those PICs that should work together with FlashForth.
To
compile FF in MPLAB, define a project with only one source file
FlashForth_33.asm. Select the processor type in the IDE. Then locate
the relevant p18fxxxx-xxxx-xxxx-xxxx.cfg file and set your
configuration parameters.
FF can be compiled with mpasmwin
also. Use /pPIC18FXXXX as argument to mpasmwin to select the PIC
type.
On Linux, compile with gpasm -pp18fxxxx
FlashForth_33.asm. Gpasm must be at least version 0.13.5.
The
resulting HEX file can then be downloaded to your PIC. Suitable
device programmer hardware is needed. FF can not be installed with a
bootloader, since FF replaces the bootloader.
The following PIC processors should be able to run FlashForth. These are also supported in the configuration files.
- 18f242, 18f442, 18f252, 18f452 ->
p18fxx2xx8.cfg
- 18f248, 18f258, 18f448, 18f458 ->
p18fxx2xx8.cfg
- 18f2455, 18f2550, 18f4455, 18f4550 ->
p18f2455-2550-4455-4550.cfg
- 18f2420, 18f2520, 18f4420,
18f4420 -> p18f2420-2520-4420-4520.cfg
-
18f2525, 18f2620, 18f4525, 18f4620 ->
p18f2525-2620-4525-4620.cfg
- 18f6527, 18f6622, 18f6627,
18f6722 -> p18f6722-family.cfg
-
18f8527, 18f8622, 18f8627, 18f8722 ->
p18f8722-family.cfg
- 18f2458, 18f2553, 18f4458, 18f4553 ->
p18f2455-2550-4455-4550.cfg
- 18f2480, 18f2580, 18f4480,
18f4580 -> p18f2480-2580-4480-4580.cfg
-
18f2423, 18f2523, 18f4423, 18f4523 ->
p18f2423-2523-4423-4523.cfg
- 18f2585, 18f2680, 18f4585,
18f4680 -> p18f2585-2680-4585-4680.cfg
-
18f2682, 18f2685, 18f4682, 18f4685 ->
p18f2682-2685-4682-4685.cfg
FF is case sensitive. All FF core words are written in lowercase
letters and hex numbers must be entered in lower case also.
Although the FF core words are in lower case, in
documentation the FF core words may be written in UPPERCASE to make
it clear that a Forth word is referred to.
Forth source code can be interpreted and compiled by loading it
via a terminal emulator.
The default setting is 38400, 1, N,
XON/XOFF. It is mandatory to enable inband flow control (XON/XOFF).
When the PIC stores data in flash, the PIC chip will stop responding
for up to 20 milliseconds. XOFF will prevent the terminal emulator
from sending characters to the PIC, while data is beeing stored into
flash.
I am using minicom with linux and it works OK without
any extra delays. The text must be copy/pasted to the terminal
window. If you use send ascii file, there appears to be no flow
control ( on my system ).
With Windows I use TeraTerm. TeraTerm can send files to the FlashForth target, which is more convenient than using copy/paste.
It is recommended to disable sending of line feed from the terminal emulator. FF will ignore any received LF characters, but the echoing may cause extra line feeds.
Normally communication with the PC and writing to flash works very
reliably, but...
If to you see a vertical bar '|' output from
FlashForth, it means that the serial interrupt buffer has overflowed.
Recompile with a slower serial speed to fix the problem.
The interrupt buffer is now 78 bytes. If you use slower serial speeds
than 38400 you may decrease the buffer size.
If you see an
extra '~' output it means that a serial framing or overrun error has
occurred.
If your terminal emulator does not respond to the
XOFF fast enough, try using TeraTerm with an inter character delay of
20 milliseconds. That should fix the problem.
If you see a
'^' output from FlashForth, it means that the verification of a
program memory write has failed. FlashForth will try to write the
same buffer only once, then an warm start will be made.
If you get a write error, '^', when compiling Forth words, try
adjusting outer_loop and inner_loop constants. For example some
version of the 18f2520 series require (2,32) instead of (1,64). See
Microchip errata page for details of your particular chip.
Please
note that the chips 18F252, 18F452, 18F258, 18F458, do not work
reliably if you jump over the $3fff address border.
So these
chips are NOT recommended for FlashForth if your program grows over
the $3fff border.
This is a fault in the PIC chip, not in
FlashForth. The other 18Fxxxx chips do not have any such errata.
The PIC18Fxxxx memory is mapped by FF:
FLASH
is mapped to $0000 - $ebff
EEPROM
is mapped to $ec00 - $efff
RAM is
mapped to $f000 - $ffff
In
FlashForth data space is allocated by CREATE ALLOT VARIABLE , C,
VALUE DEFER .
The words RAM EEPROM FLASH sets the data area
from which the succeeding allocation will be made.
@ ! and
other memory access words can be used transparently with all types of
memory. The mapping penalty is 33 % (4 instruction cycles) for ram
stores and fetches.
The words MSET and MCLR are only for RAM.
These words are typically used for setting and clearing bits in PIC 8
bit registers.
\ A variable in
ram
ram variable var1
\ A variable in
eeprom
eeprom variable var2
It is not
recommended to create variables in EEPROM/FLASH unless these are
updated fairly seldom. FLASH can be written to 10000 times until it
may fail. EEPROM can be written to 100000 times until it may fail.
Compile a word which creates character arrays in the current
data space.
: array create
allot ;
Compile a word which creates a 20 character
array in eeprom.
eeprom decimal
20 chars array calibrate
ram
Compile a word
which creates 20 cell indexed arrays.
decimal
: counters create 20 cells allot does> swap 2*
+ ;
ram counters cnt1 \ Creates the table cnt1
1233 10 cnt ! \ Store 1233 in
table index 10
10 cnt @
\ Fetch from index 10
It is good practice to
always set the data space context to ram after flash or eeprom has
been used.
The word MARKER changes the data area to FLASH. It
is a good idea to set it back to RAM afterwards.
The Forth interpreter is a normal Forth interpreter. It parses
words delimited by space and tries to find the word in the
dictionary. TAB is ignored. If the word is found it is executed, if
not, FF tries to convert it to single precision number according to
the current base (or base prefix), and put the number on the stack.
If that fails ABORT is called.
After interpreting a line,
QUIT prints Ok and executes the deferred word PROMPT. If you don't
want to see the info from .ST, you can re vector PROMPT to for
example CELLS which does nothing.
' cells is prompt
' .st is prompt
FF is an 16-bit Forth and the single precision math operations are
consequently 16-bit.
The FF core has a 16 bit U/MOD on which
the other 16-bit division routines are based.
UM/MOD, M+ and
UM* are used as base for the 32-bit double precision math words that
can be loaded from math.fth.
FF supports single precision 16-bit number conversion.
Input
numbers can be prefixed by % # $ to achieve binary decimal and
hexadecimal number conversion without changing BASE.
Output
numbers are always converted according to BASE.
FF is a subroutine threaded Forth with native code generation.
Literals are compiled as native code.
DUP and 0=
before IF WHILE UNTIL are optimized away. Due to this
functionality DUP is never inlined automatically.
DUP may be
forced to inline with INLINE DUP.
All the structured
conditional words generate native code.
: and ] puts FF in
compilation state. ; ;I and [ enters the interpreter state.
FF can compile location independent assembler primitives as inline
code. Some of these words have the inline bit set in the word header.
Individual words can be inlined by prefixing the word with
INLINE.
: newswap
inline swap ;
When compiling a new word that
should be inlined automatically, the inline flag can be set with the
word INLINED.
: 1+
[ Sminus w, a, swapf, ] \ Decrement stack pointer with one
[ Splus f, a, infsnz, ] \ Add lower byte, skip next instruction
if the result was nonzero
[ Srw
f, a, incf, ] \ Add high byte
; inlined \ Set
the inline header flag
The words
drop p+ cwd rdrop false true 1 leave cell chars di ei
will
always be inlined by the compiler.
The following words can be
prefixed with INLINE
mset mclr
lshift rshift sp@ swap over rot dup + m+ - and or
xor
invert 1+ 1- 2+ 2* 2/ !p @p p++
Inline literals are
fast and do not take too much space. With CON you can define a
constant which is in native inline code, it does not use DOCREATE. If
you follow the definition with inlined, the compiler will
compile the constant as an inline literal.
34 con thirtyfour inlined
: native-inline-34
thirtyfour ;
The core dictionary is always searched before the user dictionary.
It is not possible to redefine existing words. These measures have
been taken to make the system more robust and to make it possible to
recover to the basic state, without the need to flash the chip again.
It also makes the code clearer, since there will not be two words
with the same name.
In order to recover to an earlier dictionary and memory allocation
state, use MARKER. Always before defining new words define a marker.
Otherwise you may need to return to an earlier marker or to say COLD
which will empty the dictionary and reset all memory allocations to
default values. A marker will restore TURNKEY, DP and LATEST. IRQ is
not affected.
FORGET can be used to forget a user word, but
FORGET can only adjust the FLASH DP. This means that allotted EEPROM
or RAM will not be reclaimed if you use FORGET.
Note that TURNKEY, DP, LATEST are stored in flash, but their respective addresses are in ram. These variables are cached in ram during interpretation of a input line and also during compilation state. This makes compilations run faster, and there will be less wear in the eeprom.
Since FF refuses to redefine words, certain words, typically oneline definitions, can be compiled from several source files. The first compilation is accepted, and the others rejected. This is quite practical for having the same definition in many files, so that you can compile exactly the words one or more applications needs.
For example i2c.fth and task-test.fth both have defined PORTC, but PORTC will be compiled only once, even if both files are loaded to FF.
After processor reset a check is made to see if a turnkey
word should be executed. If the eeprom value TURNKEY contains a
nonzero value, it must be an address of a valid user word. Unless the
user presses ESC within two seconds, the user word is executed. If
ESC is pressed, the user word will not execute. Instead the forth
interpreter is entered.
'
my_application is turnkey
If your TURNKEY word is
crashing, press ESC and as a first command give:
false is turnkey<enter>
This will disable the
TURNKEY and allow you to make corrections.
Interrupt routines can be written in assembly or in Forth.
The
Forth interrupt word has its own parameter stack of 8 cells. A
FF interrupt word has to be ended with ;I .
There are certain
limitations to which registers and Forth words can be used in an
interrupt word.
It is not recommended to store to flash or
eeprom in an interrupt routine.
The following words are safe
to use inside the Forth interrupt routine.
Words defined by:
variable
con
constant
: create does> ;
value
defer
The following words can be compiled into a Forth
interrupt word :
@ ! c@ c! mset
mclr mtst lshift rshift exit execute fexecute
cell aligned
cell+ cells char+ chars 2@ 2! 2drop 2over
umin umax 0 1
drop swap over rot >r r> r@ dup abs
+ - and or xor
invert negate 1+ 1- 2+ 2- 2* 2/ +! within
<> 0= 0<
= < > u< u> nip tuck ?negate min max false true
The
following immediate words can be used in a interrupt word :
['] [char] [ ] if else then begin while repeat until for next
The
following registers are saved by FF before the interrupt word is
executed.
Sreg(=FSR0), Treg(=FSR1),
PCLATH, TBLPTRL, TBLPTRH, TABLAT.
This makes it
possible to use the above Forth words in the interrupt routine.
Below is a interrupt word which counts the total number of
interrupts.
ram variable
irq_counter
: my_irq
irq_counter
@
1+
irq_counter
!
;i
The following way to store the
variable address as an inline literal, and inlining 1+, gives faster
code.
: my_irq
[ irq_counter literal ] @
inline 1+
[ irq_counter literal ] !
;i
To
activate the interrupt you store the interrupt word xt into the
interrupt vector IRQ.
' my_irq
is irq
IRQ is cleared at warm start, so to enable
the interrupt word
at startup, a initialization word must be
used.
: irq_init ['] my_irq is
irq ;
' irq_init is turnkey
The above
example is a simple example. To use individual interrupt sources the
interrupt enable bits and flag bits must be used.
See
servo.fth for an example for a complete
servo control routine that uses a timer and interrupts to control 4
servo channels.
Below is the interrupt counter implemented in
assembly
$28 as3
incf,
( f d a -- )
$48 as3 infsnz,
( f d a -- )
: lfsr, ( k f -- )
4 lshift over 8 rshift $f and or $ee00 or i, $ff and $f000 or i, ;
1 con f,
\ Destination File
0 con a,
\ Force Access Bank
1 con Treg
$ffe6 con Tplus \ Treg (FSR1) is interrupt safe
ram variable irq_counter
\
Interrupt routine written in assembly
: my_irq
[ irq_counter Treg lfsr, ]
[ Tplus f, a,
infsnz, ]
[ Tplus f,
a, incf, ]
;i
NOTE:
By going to compile state before end-of-line, there will be less
writes to FLASH and EEPROM and the compilation process will go
faster.
FF can execute background tasks concurrently with the operator
task.
The task switching is made cooperatively by executing
PAUSE. PAUSE is executed in I/O words KEY and EMIT so that background
tasks can run while the console is waiting for input or queuing for
output. PAUSE is also executed in FIND so that compilation does not
disturb the real time too much. MS executes PAUSE while it waits for
the specified delay to pass.
The words for tasking can be
loaded from task.fth.
TASK creates a new task and defines the
stack sizes and the additional user area size and the tibsize.
Tibsize can always be set to zero for background tasks. In the
future, new foreground tasks may be supported.
TINIT
initializes a task with the XT of the task loop. It also initializes
the task user area.
RUN makes the task run. It inserts the
task in the round-robin linked list.
END ends a task. It
removes the task from the round robin linked list.
SINGLE
ends all tasks except the operator task.
The tasking commands
may only be executed from the operator task.
I have always found DO..LOOP cumbersome to use. I wanted to
separate the loop count and the index handling. Therefore in FF 3.0
FOR..NEXT and the P register were introduced.
FOR..NEXT loops
exactly the amount of times specified ( also 0 ) .
: star [char] * emit ; ok
<16,2>
star *ok <16,2>
:
stars for star next ; ok <16,2>
10 stars
****************ok <16,2>
0 stars ok <16,2>
The loop count is held on top of the return stack and it can
be fetched by R@. LEAVE sets the loop count to 0, so that NEXT will
terminate the loop.
:
test
#10
for
r@
. r@ 4 =
if
leave
then
next
;
ok
test 9 8 7 6 5 4 ok
If you EXIT a
FOR..NEXT loop you must drop the loop count with RDROP
:
test
#10
for
r@
4 =
if
rdrop
exit
then
r@
.
next
; ok
test
9 8 7 6 5 ok
The P register can be used as a value or as a pointer. It can be
used in conjunction with FOR..NEXT or at any other time.
!P>R
pushes the current P value on the return stack and sets a new value
to P.
In a definition !P>R and R>P should always be
used to allow proper nesting of words.
R>P pops a value
into P from the return stack.
!P sets a new value into P. Use
!P only from the command line, or between !P>R and R>P in a
definition.
@P lets you fetch the value of P.
P+
increments P by one.
P++ ( n -- ) adds n to P.
P@ P!
PC@ PC! are used to access memory via the pointer.
Always
remember to balance the return stack in all branches of your code.
\ CMOVE src dst u -- copy u
bytes from src to dst
\ The source address is on the
parameter stack,
\ the destination address is in the P
register.
: cmove
swap !p>r
for
c@+ pc! p+
next
r>p drop
;
The PIC hardware stack us used as the Forth return stack.
FSR0
register is used as the parameter stack pointer. It also called S in
the assembler code.
FSR1 is used as a temporary pointer and
as temporary storage. It is called T in the assembler code.
PCL,
PCLATH, TBLPTRL TBLPRH are used for accessing flash memory. PCLATU
and TBLPTRU must be zero at all times.
$f000-$f03f
is the flash write buffer.
$f040-$f05f is used internally by
FF.
$f060-$f06f is for the interrupt parameter stack.
$f070-$f0ef is for the serial Rx and TX buffers.
$f0f0-$f0f9 is used for mirroring TURNKEY DP and LATEST
$f0fa contains the
interrupt vector
$f0fc
contains the current data section
$f0fe
contains the user pointer UP
$f100-$f1ff is the user area for
the OPERATOR task
$f200-$ff5f is free for application use.
$ff60-$ffff address the PIC special function registers.
FlashForth_33.asm is the
FlashForth core assembly file.
p18fxxxx.cfg
The main configuration file. It includes the other
needed files depending on the selected processor type.
asm.fth
The
assembler can be loaded from asm.fth. Look in blink.fth, i2c.fth and
irq.fth for examples how to use the assembler words.
MSET MCLR
MTST can be used for bit-manipulation but using inline assembly is
much faster.
blink.fth
blink
is a simple word showing how to access the PIC hardware by using
inline assembly.
case.fth ,
case-test.fth
Contains CASE
OF ENDOF ENDCASE.
core.fth
Contains EVALUATE FORGET DUMP FILL ERASE BLANKS ?DUP PICK
ct.fth , ct-test.fth
Contains
a word for making condition tables. The idea is to use this instead
of CASE statements. Look in seen.fth for an
example. There the see word has been implemented using a condition
table.
i2c.fth
A simple
implementation for accessing a serial eeprom and a temperature sensor
via i2c.
irq.fth
Contains
a counter for the total number of interrupts. A very simple interrupt
routine. Both assembly and Forth versions are shown as examples.
jt.fth , jt-test.fth
Contains
a word for making jump tables. This can be used instead of CASE
statements to execute words corresponding to a certain value.
A better jump table with a much clearer structure. Design by Pete
Zawasky.
math.fth
Here you can find double 32-bit math words.
pic.fth
Some PIC register
adresses are defined here. Thanks to Andrew Smith.
see.fth
The SEE word
decompiles forth words. SEE also decompiles the assembler
instructions bcf, bsf, btfsc, btfss, return, bra, bz and goto.
servo.fth ,
servo-test.fth
Contains
a servo pulse solution as an example of how to set up a interrupt
routine using Forth. It can control 4 servos on port B. The interrupt
routine uses TIMER0 to generate servo pulses.
task.fth
, task-test.fth
Contains
the words TASK, TINIT, RUN, END, SINGLE for task handling.
task-test.fth contain some led blinking background tasks.
words.txt
This file
contains the word list.
cold COLD!
FlashForth V3.3 on PIC18F2520
ok<16,2>
decimal ok<10,2>
$dead ok<10,2>57005
$beef ok<10,2>57005 48879
hex ok<16,2>dead beef
bin ok<2,2>1101111010101101 1011111011101111
hex ok<16,2>dead beef
+ ok<16,2>9d9c
drop ok<16,2>
words
marker
rdrop leave next for in, inline repeat while
again until begin else then if until, again,
begin, else, then, if, not, nc, nz, z,
br? true false dump allot .s words ms
ticks s0 latest rhere state bl 2- [']
; :noname : ] [ does> postpone create
[char] ihere ( char ' abort" ?abort ?abort?
abort prompt quit .st inlined immediate shb interpret
in? 'source >in tib tibsize number? >number sign?
digit? find immed? (f) cfa>nfa nfa>cfa nfa>lfa @+
c@+ place cmove word \ /string source user
base pad hp rcnt ssave rsave ulink bin
hex decimal . u.r u. sign #> #s
# >digit <# hold up min max ?negate
tuck nip / u*/mod u/ * u/mod um/mod
um* p++ p+ pc! p! pc@ p@ @p
r>p !p>r !p u> u< > < =
0< 0= <> within +! 2/ 2* >body
2+ 1- 1+ negate invert xor or and
- m+ + abs dup r@ r> >r
rot over swap drop ." s" type accept
1 umax umin spaces space cr 2dup 2drop
2! 2@ cf, chars char+ cells cell+ aligned
align cell c, , here dp cse ram
eeprom flash c@ @ c! ! sp@ con
constant variable @ex execute exit key? key emit
cold warm bra, rcall, call, goto, br3 br2
br1 as0 as1 as2 as3 rshift lshift ic,
i, operator khz mtst mclr mset iflush pause
turnkey is to defer value cwd literal irq
;i di ei scan skip n= ok <16,2>
\ Compile a word which creates h'20' cell indexed arrays in current data memory.
: array create 20 cells allot does> swap 2* + ; ok<16,2>
\ Create an array with elements in program flash
flash array arr1 ok<16,0>
0 arr1 ok<16,0>2528
' arr1 ok<16,0>2528 2522
\ Create an array with elements in eeprom
eeprom array arr2 ok<16,1>2528 2522
0 arr2 ok<16,1>2528 2522 e00a
\ Create the array arr3 with elements in ram
ram array arr3 ok<16,2>2528 2522 e00a
0 aar3 aar3?
0 arr3 ok<16,2>f200
1 arr3 ok<16,2>f200 f202
1 arr2 ok<16,2>f200 f202 e00c
@ ok<16,2>f200 f202 ffff
\ Empty the stack
.. ..?
\ Define a task loop that toggles PORTC outputs based on the
\ bitmask, delay determines the toggle period.
\ delay and bitmask are user variables to make it possible
\ to use the same task loop in many tasks, so that
\ each task can have it's own bitmask and delay values.
\ The compilation of the task definition words is not shown in this example
-lblink -lblink?
marker -lblink ok<16,2>
decimal ok<10,2>
$ff82 constant portc ok<10,2>
$ff94 constant trisc ok<10,2>
$2 user bitmask \ The bitmask ok<10,2>
$4 user delay \ The delay time in milliseconds ok<10,2>
ok<10,2>
: lblink
begin
bitmask c@ trisc mclr
delay @ ms
bitmask c@ portc mset
delay @ ms
bitmask c@ portc mclr
again
; ok<10,2>
\ Define the first task
flash $0 $10 $10 $4 task tblink ok<10,2>
\ Define a word that initialises tblink
: tblink-init
['] lblink tblink tinit
$1 tblink bitmask his !
$100 tblink delay his !
; ok<10,2>
\ Initialise the tblink task
tblink-init ok<10,2>
\ Run the the tblink task
tblink run ok<10,2>
\ tblink is running in the background while tblink1 is compiled
\ Define, init and run the second task
flash $0 $10 $10 $4 task tblink1 ok<10,2>
: tblink1-init
['] lblink tblink1 tinit
$4 tblink1 bitmask his !
$60 tblink1 delay his !
; ok<10,2>
tblink1-init ok<10,2>
tblink1 run ok<10,2>
\ Wait for 3000 milliseconds
3000 ms ok<10,2>
\ End both tasks
single ok<10,2>
\ Wait for 2000 milliseconds
2000 ms ok<10,2>
\ Make both tasks start after a warm start or power on
\ Define a word that initialises and runs both tasks
: blink2 tblink-init tblink1-init tblink run tblink1 run ; ok<10,2>
\ Test that blink2 works
blink2 ok<10,2>
\ Wait 4096 milliseconds
$1000 ms ok<10,2>
\ End both background tasks again.
single ok<10,2>
\Store the execution vector of blink2 in the turnkey vector in eeprom
' blink2 is turnkey ok<10,2>
\ Make a warm start
warm
FlashForth V3.3 on PIC18F2520
ESC
\ Now the leds should be blinking unless you pressed ESC.
\ The compilation of the see word is not shown in this example.
\ Decompile the blink2 word.
see blink2
291c dfb9 rcall tblink-init
291e dfe3 rcall tblink1-init
2920 dfa8 rcall tblink
2922 defb rcall run
2924 dfd0 rcall tblink1
2926 def9 rcall run
2928 0012 return
ok<16,2>This document is the User Guide for FlashForth 3.3 revision 0.3.