-*- outline -*-

--- README for picforth $ReleaseVersion: 0.2 $

* What is that?

This program is a Forth compiler for Microchip PIC 16F873/876.

* Why this project?

I needed to write some code on a PIC to control a digital model railroad
system using the DCC (Digital Control Command) protocol. However, writing
it in assembly is error-prone and writing it in C is no fun as C compiled code
typically needs a lot of space.

So I wrote this compiler, not for the purpose of writing a compiler, but as
a tool to write my DCC engine.

* State of the compiler

The compiler does not aim to be ANS Forth compliant. It has quite a few words
already implemented, and I will implement more of them as needed. Of course,
you are welcome to contribute some (see below for license information).

At this time, many words are missing from standard Forth. For example, I have
no multiply operation as I have no use for it at this time and won't spend
time to implement things I don't need (remember, Forth is a tool before
anything else).

* License

The compiler is released at the moment under the GNU General Public
License version 2 (I intend to use the less restrictive BSD license in
the future, but as it is based on gforth, I have to sort out those
issues with gforth copyright holders).

However, the code produced by using this compiler is not tainted by the
GPL license at all. You can do whatever you want with it, and I claim
absolutely no right on the input or output of this compiler. I encourage
to use it for whatever you want.

Note that I would really like people to send me their modifications
(be they bug fixes or new features) so that I can incorporate them in
the next release.

* Why not use Mary?

Mary was a great inspiration source, I even kept some of the names from it.
However, no code has been reused, as both Forth do not have the same goal.

* Organisation

The stack is indexed by the only indirect register, fsr. The indf register
automatically points to the top of stack.

The w register is used as a scratch. Attempts to use it to cache the
top of stack prove to be inefficient, as we often need a scratch register.

* Compiling

The compiler is hosted on gforth, a free software compiler for Unix systems.
The command line to use to compile file foo.fs into foo.hex, and getting a
usable map into foo.map is:

  gforth picforth.fs -e 'include foo.fs final-dump foo.hex' | sort -o foo.map

Of course, you should automate this in a Makefile, such as the one provided
with the compiler.

If you install the GNU PIC utils (from http://gputils.sourceforge.net/),
then you can read the assembled code by using "gpdasm".

* Conditionals

There is a "/if" test which is equivalent to "0= if" much executes much
faster.

* Looping

There exists a "v-for"/"v-next" structure:

  v-for ( n addr -- )
    Initialize addr content with n.

  v-next ( addr -- )
    Decrement addr content. If content is not zero, jump to v-for location.

Also, the words "begin", "again", "while", "until" and "repeat" are
implemented.  "/while" does the same thing as "while" with a test
reversed, as does "/until" compared to "until".

* Tables

A table starts with "table" word which takes the top of stack and executes
the first element for 0, the second for 1, ... Control returns from the
current word after executing the requested action. Action is "tc: word"
which executes word.

  table ( n -- )
  tc: ( "name" -- )

Example:

  : print-number ( n -- )   \ n <= 3, print number
    table
      tc: zero tc: one tc: two tc: three
  ;

The table structure does not consume return stack slots.

* Main program

A "main" word indicates that the next address is the main program. Use for
example:

main : main-program ( -- )
  (do initialisations)
  (call mainloop)
;

* Interrupts

If you want to use interrupts, use

  include picisr.fs

Two words do respectively save and restore the context around interrupt
handling code:

  isr-save ( -- )
  isr-restore-return ( -- )

Also, the word "isr" is provided to notify that the next address is the
isr handler.

For example, you can write an interrupt handler with:

isr : interrupt-handler ( -- )
  isr-save
     (interrupt handling code here)
  isr-restore-return
;

Do not forget that the return stack depth is only height. An interrupt can
occur at any time unless you mask them or unset the GIE bit.

Two facility words that manipulate GIE are also provided:

  enable-interrupts ( -- )
  disable-interrupts ( -- )

You have to dispatch the interrupts and clear the interrupt bits manually
before you return from the handler.

Versions that do nothing are provided in the default compiler. Useful versions
are redefined when using picisr.fs.

Because of this, include picisr.fs as soon as possible, before other files
and before using enable-interrupts and disable-interrupts. Other included
files may fail to act properly if you don't.

* Bit manipulation

To ease bit manipulation, the following words are defined for port p:

   and!       ( n p -- )    logical and with n
   /and!      ( n p -- )    logical and with ~n
   or!        ( n p -- )    logical or with n
   xor!       ( n p -- )    logical xor with n
   bit-set    ( b p -- )    set bit b of p (both have to be constants)
   bit-clr    ( b p -- )    clear bit b of p (both have to be constants)
   bit-toggle ( b p -- )    toggle bit b of p (both have to be constants)
   bit-set?   ( b p -- f )  return non-zero if bit b of p is set

Note that there is no bit-clr? operation. Use a "/if" instead of "if" and
"/while" instead of "while" if you want to test for the absence of a bit.

Five words help create a port pin:

   pin-a  ( n "name" -- ) ( Runtime: -- n porta )
   pin-b  ( n "name" -- ) ( Runtime: -- n portb )
   pin-c  ( n "name" -- ) ( Runtime: -- n portc )
   pin-d  ( n "name" -- ) ( Runtime: -- n portd )
   pin-e  ( n "name" -- ) ( Runtime: -- n porte )

For example, you can create a pin designating an error LED and manipulate
it using:

   3 pin-b error-led                   \ Error LED is on port B3
   : error error-led bit-set ;         \ Signal error
   : no-error error-led bit-clr ;      \ Clear error

* Watchdog timer

The word "clrwdt" is available from Forth to clear the watchdog timer.

* Reading from or writing to EEPROM

By using

   include piceeprom.fs

you have access to new words allowing you to access the PIC EEPROM:

   ee@          ( a -- b )  read the content of a and return it
   ee!          ( b a -- )  write b into a

* Reading from or writing to flash memory

Two words allow reading from and writing to the flash memory when the file
"picflash.fs" is included with

   include picflash.fs

Those words expect manipulate a 14 bits program memory cell whose 13 bits
address is in EEADRH:EEADR. The data is read from or stored to EEDATH:EEDATA.

   flash-read ( -- )
   flash-write ( -- )

If "picisr.fs" has been included before this file, interrupts will be properly
disabled around flash writes.

* Optimizations

The following optimizations are implemented:

** Tail recursion

Tail recursion is implemented at "exit" and ";" points.

   : x y z ;

generates the following code for word x:

   call    y
   goto    z

** Redundant pop/push are removed

For example, the (particularily stupid and useless)

   dup dup drop

sequence generates

   movf     0x00,w
   decf     0x04,f
   movwf    0x00

which in fact corresponds to a single "dup".

** Most operations use direct-access and literal variants when possible

The following sequence

   9 and

generates

   movlw    0x09
   andwf    0x00,f

Also, combined with the redundant push/pop eliminations, the following code

   dup 9 and if ...

generates

   movf    0x00,f
   andlw   0x09
   btfsc   ...

** Condition inversions

Short (one instruction) "if" actions are transformed into reversed
conditions. For example, the following word:

   \ This word sets port a0 if port c2 is high, and port b1 in any case.
   : z 2 portc bit-set? if 0 porta bit-set then 1 portb bit-set ;

generates the following code: (hand-commented assembly)

   btfsc    0x07,2  ; skip next instruction if port c2 is low
   bsf      0x05,0  ; set port a0 high
   bsf      0x06,1  ; set port b1 high
   return           ; return from word

** Bank switch optimizations

The compiler tries to remove useless bank manipulations. The following word

  : eeprom-read eepgd eecon1 bit-set rd eecon1 bit-set ;

generates (hand-commented assembly):

   bsf      0x03,5     ; select ...
   bsf      0x03,6     ; ... bank 3
   bsf      0x0c,7     ; set bit eepgd of eecon1 (in bank 3)
   bsf      0x0c,0     ; set bit rd of eecon1 (in bank 3)
   bcf      0x03,6     ; restore ...
   bcf      0x03,5     ; ... bank 0

* Configuration word

The configuration can be configured with the following words:

   set-fosc   ( n -- )       Choose oscillator mode from: (default: fosc-rc)
                                  fosc-lp   Low power
                                  fosc-xt   External oscillator
                                  fosc-hs   High-speed oscillator
                                  fosc-rc   RC circuit
   set-wdte   ( flag -- )    Watchdog timer enable (default: true)
   set-/pwrte ( flag -- )    Power-on timer disable (default: true)
   set-boden  ( flag -- )    Brown-out detect enable (default: true)
   set-lvp    ( flag -- )    Low voltage programming (default: true)
   set-cpd    ( flag -- )    EEPROM protection disable (default: true)
   set-wrt    ( flag -- )    FLASH protection disable (default: true)
   set-debug  ( flag -- )    In-circuit debugger disable (default: true)
   set-cp     ( n -- )       Code protection, choose from: (default: no-cp)
                                  no-cp     No protection
                                  full-cp   Full protection

* Examples

Some files are included as examples with a Makefile. For example, to build
"booster.hex", run "make booster.fs":

  - booster.fs: code for a booster which handles overload and overheat
    signals

  - generator.fs: code for a DCC signal generator based on serial commands
    (work in progress)

* Credits

I would like to thank the following people:

  - Keith Wootten for his precious examples of how he uses a forth-ish
    assembler for the PIC and his inspiration for some control structures

  - Francisco Rodrigo Escobedo Robles for his Mary PIC Forth compiler
