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>