The PIC18 version has been updated to be compiled with PIC-AS. MPASM is not supported anymore.
Follow the instructions in PIC18/install.txt
At least the following PIC processors are able to run FlashForth 5
18f242 18f442 18f252 18f452 18f248 18f258 18f448 18f458 18f2455 18f2550 18f4455 18f4550 18f2420 18f2520 18f4420 18f4420 18f2525 18f2620 18f4525 18f4620 18f6527 18f6622 18f6627 18f6722 18f8527 18f8622 18f8627 18f8722 18f2458 18f2553 18f4458 18f4553 18f2480 18f2580 18f4480 18f4580 18f2423 18f2523 18f4423 18f4523 18f2585 18f2680 18f4585 18f4680 18f2682 18f2685 18f4682 18f4685 18f14k50 18f24k50 18f25k50 18f45k50 18f24K20 18f25k20 18f26k20 18f44k20 18f45k20 18f26k20 18f14k22 18f24k22 18f25k22 18f26k22 18f44k22 18f45k22 18f46k22 18f24k42 18f25k42 18f26k42 18f27k42 18f45k42 18f46k42 18f47k42 18f55k42 18f56k42 18f57k42 18f25k83 18F26k83 18f65k90 18f66k90 18f85k90 18f86k90 18f16q41 18f25q43 18f26q43 18f27q43 18f26q71 18f46q71 18f56q71
FF can compile location independent assembler primitives as inline code. The shortest of these words have the inline bit set in the word header for automatic inlining.
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
On the PIC18 the following words are always inlined by the compiler.
[i i] drop p+ cwd r@ r> >r rdrop false true 1 endit cell chars di ei
On PIC18 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++ p2+ ticksAlso words defined by CONSTANT, VARIABLE, 2CONSTANT and 2VARIABLE can be inlined. They compile the constant and the variable address as inline literal code.
If you append the definition with INLINED, the compiler will later compile the constant as an inline literal.
34 constant thirtyfour inlined : native-inline-34 thirtyfour ;
Interrupt routines can be written in assembly or in Forth. FF interrupt words have to be ended with ;I .
On PIC18 Forth the interrupt word has its own parameter stack of 8 cells.
In general Forth words that normally would be used in an interrupt word are interrupt safe.
Words that start the interpreter or compile new words should not be used in an interrupt.
It is not possible to store to flash or
eeprom in an interrupt routine.
The following words are not interrupt safe:
n=
The following registers are saved on the return stack by [I and restored by I] :
Sreg(FSR0L FSR0H) TBLPTRL TBLPTRH TABLAT PRODL
The following registers are always preserved before the interrupt word is executed:
Treg (FSR1L FSR1H) PCLATH
Below is a interrupt word which counts the total number of interrupts.
ram variable irq_counter
: my_irq
[i
irq_counter @
1+
irq_counter !
i]
;i
To activate the interrupt you store the interrupt word xt into the interrupt vector. For PIC18 the interrupt vector is always zero for high priority interrupt handling. The PIC18 low priority interrupts are used internally by FlashForth for the millisecond tick interrupt and for UART receive interrupts.
' my_irq 0 int!
The interrupt vector in ram is cleared at warm start, so to enable the interrupt word at startup, a initialization word must be used.
: irq_init ['] my_irq 0 int! ; ' irq_init is turnkey
The above example is a simple one for PIC18. To use individual interrupt sources the interrupt enable bits and flag bits for each interrupt source must be used.
See servo.txt 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.
The PIC hardware stack is used as the Forth return stack.
The FSR0 register is used as the parameter stack pointer.
It is 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.
FSR2 is used as the return stack pointer by R> >R R@ and for saving the hardware return stack when multitasking.
It is called R in the assembler code.
PCL, PCLATH, TBLPTRL TBLPRH are used for accessing flash memory. PCLATU
and TBLPTRU must be zero at all times.
warm
FlashForth V3.4 PIC18F258
ESC
decimal ok<#,ram>
255 ok<#,ram>255
$ff ok<#,ram>255 255
%11111111 ok<#,ram>255 255 255
bin ok<%,ram>11111111 11111111 11111111
hex ok<$,ram>ff ff ff
2drop drop ok<$,ram>
words
marker
p2+ pc@ @p m? b? 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 .s words >pr .id ms ticks s0
latest state bl 2- ['] -@ ; :noname
: ] [ does> postpone create cr [char]
( char ' abort" ?abort ?abort? abort prompt
quit .st inlined immediate shb interpret 'source >in
tib ti# number? >number sign? digit? find immed?
(f) c>n n>c @+ c@+ place cmove word
parse \ /string source user base pad hp
task 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* ukey?
ukey uemit p++ 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 allot ." s" type accept
1 umax umin spaces space 2dup 2drop 2!
2@ cf, chars char+ cells cell+ aligned align
cell c, , here dp ram eeprom flash
c@ @ c! ! sp@ con constant variable
@ex execute key? key emit cold warm btfss,
btfsc, bsf, bcf, bra, rcall, call, goto, br3
br2 as1 as3 rshift lshift ic, i, operator
cpu_clk mtst mclr mset iflush pause turnkey is
to defer value cwd literal irq ;i di
ei scan skip n= rx1? rx1 tx1 i]
[i andlw, movf, w, a, exit ok<$,ram>
\ Compile a word which creates indexed cell arrays in current data memory.
: array create cells allot does> swap 2* + ; ok<$,ram>
\ Create an array with elements in program flash
flash #10 array flash-array ok<$,flash>
\ Get the address of element 0
0 flash-array hex ok<#,ram>30b6
\ Create an array with elements in eeprom
eeprom #30 array eeprom-array ok<$,eeprom>30b6
0 eeprom-array ok<$,ram>30b6 ec0c
\ Create an array with elements in ram
ram $20 array ram-array ok<#,ram>30b6 ec0c
0 ram-array ok<$,ram>30b6 ec0c f42a
2drop drop ok<$,ram>
\ move 10 cells
0 flash-array 0 ram-array #10 cells cmove ok<$,ram>
0 flash-array 10 dump
30b6 :f0 f1 f2 ff ff ff ff ff ff ff ff ff ff ff ff ff ................ ok<$,ram>
0 ram-array 10 dump
f42a :f0 f1 f2 ff ff ff ff ff ff ff ff ff ff ff ff ff ................ ok<$,ram>
\ 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<$,ram>
decimal ok<#,ram>
$ff82 constant portc ok<#,ram>
$ff94 constant trisc ok<#,ram>
$2 user bitmask \ The bitmask ok<#,ram>
$4 user delay \ The delay time in milliseconds ok<#,ram>
ok<#,ram>
: lblink
bitmask c@ trisc mclr
begin
delay @ ms
bitmask c@ portc mset
delay @ ms
bitmask c@ portc mclr
again
; ok<#,ram>
\ Define the first task
flash $0 $10 $10 $4 task: tblink ok<#,ram>
\ Define a word that initialises tblink
: tblink-init
['] lblink tblink tinit
$1 tblink bitmask his !
$100 tblink delay his !
; ok<#,ram>
\ Initialise the tblink task
tblink-init ok<#,ram>
\ Run the the tblink task
tblink run ok<#,ram>
\ 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<#,ram>
: tblink1-init
['] lblink tblink1 tinit
$4 tblink1 bitmask his !
$60 tblink1 delay his !
; ok<#,ram>
tblink1-init ok<#,ram>
tblink1 run ok<#,ram>
\ Wait for 3000 milliseconds
3000 ms ok<#,ram>
\ End both tasks
single ok<#,ram>
\ Wait for 2000 milliseconds
2000 ms ok<#,ram>
\ 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
\ Test that blink2 works
blink2 ok<#,ram>
\ Wait 4096 milliseconds
$1000 ms ok<#,ram>
\ End both background tasks again.
single ok<#,ram>
\Store the execution vector of blink2 in the turnkey vector in eeprom
' blink2 is turnkey ok<#,ram>
\ Make a warm start
warm
FlashForth V3.4 PIC18F258
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<$,ram>
tasks operator tblink1 tblink ok<$,ram>