\ Sample PicForth program. fload picf16.f \ Load standard definitions. \ Sum the positive integers from 1 to n. : sumn ( n -- sum ) 0 swap \ Sum on the stack. 1+ 1 do \ Loop from 1 to n. I + loop ; : main \ PicForth entry point is here. \ Print the sums of the positive integers to 100. 101 0 do I sumn . loop begin again \ Don't let the main word end. ;
PicForth has two dictionaries that are searched separately: one for compiling mode that is searched in colon definitions, the other for interpreting mode that is searched outside of colon definitions. Colon definitions only add to the compiling dictionary, there are no mechanisms for adding to the interpreting dictionary. This may change in the future.
PicForth and its memory model do not support the string handling, conversion, and I/O functions of a full blown Forth system. It would surprise me if anyone wanted to do these things on a microcontroller with less than 450 bytes of ram, so they are not included. Specifically words like <#, #>, >BODY, and EVALUATE are not included, although . (dot) and ." (dot-quote) are included, as the microcontroller is likely to be attached to an LCD display.
Variables are declared outside of colon definitions, but must be initialized with colon definitions. Because the PIC does not have a contiguous memory map, but rather it is broken into banks, ALLOT is not used. Instead use ARRAY to declare a fixed size array, the compiler will give an error message if it can not fit the array into available ram. Similarly, , (comma) and C, are not included as they depend on contiguous memory allocation. Single byte variables can be allocated with CVARIABLE.
One solution might be to use an inner interpreter, which for performance reasons PicForth chooses not to use (PicForth is subroutine threaded.) The answer in PicForth is to selectively use an alternate call/return mechanism when performance and code/space are not important. To invoke the alternate mechanism, a new word is defined, :: (double-colon), which is used the same way as the normal : (colon) to define words. Unfortunately, words defined with :: use an inefficient calling mechanism that takes several instructions to invoke (it takes 5 extra instructions (codespace) to make a call this way, and is even more costly in terms of clock cycles) and should only be used for infrequently called words. Also the address of words defined this way should not be taken with ['] or called from assembly.
In order to solve this problem, two procedures are given: enable_ints and disable_ints. There is a lock count on the GLINTD bit that is incremented when disable_ints is called, and decremented when enable_ints is called. Only when the lock count reaches zero will the interrupts be re-enabled.
n :interrupt interrupt_name
Where n is the number of the interrupt vector that you want to handle, eg. 32 is the peripheral interrupt vector (consult the Microchip documentation for the address of other interrput vectors.). An interrupt handler is ended with interrupt; instead of just a ; (semicolon.)
You also must include the interrupt entering and leaving routines by including in your code
fload interupt.f
before you declare an interrupt handler. You may want to write the interrupt handler in inline assembly, or if you are not worried about nesting the return stack too deep, you can write interrupt handlers in high level Forth. See here for an example of a peripheral interrupt handler in action.