Starting PIC Forth

If you are using a Unix-like environment you can type "make interactive" from the PIC Forth directory to start PIC Forth in interactive mode. The actual command (which might be useful for Windows users) is:
gforth picforth.fs -e 'host picquit'

You can also name a hex file to make and it will create the hex file from the corresponding .fs file. The command is:

gforth picforth.fs -e 'include xxx.fs file-dump xxx.hex map bye'

Assuming your source file is xxx.fs and you want to create xxx.hex. For now, stick with interactive mode.

Try a simple program:

main : test 3 2 + recurse ;

This defines a word test that is your main program. It will add three plus two and keep doing it forever (recurse). Right? Not exactly. PIC Forth has a disassembler built in, so issue this command:

see test

This will show you:

        ; name: test
        ; max return-stack depth: 0
0x0005  3004    movlw   0x05
0x0006  0384    decf    0x04,f
0x0007  0080    movwf   0x00
0x0008  2805    goto    0x005   ; test

PIC Forth knows it can compute 2 + 3 at compile time so it does it. The first line loads 5 into W. The next two lines push it onto the stack (FSR, register 4, is the stack pointer). INDF (register 0) is the top of stack. Then a goto jumps to location 5, the start of the program.

To turn this into a hex file, type:

file-dump xxx.hex

Of course, replace xxx with the name of your choice. You can also see a map of memory used by issuing the map command. Use bye to quit.

Consider this program:

0 pin-b led 
: init led >output ; 
: pulse led high led low ; 
: mainloop begin pulse again ; 
main : test init mainloop ; 
 The first line sets a word led to refer to port B pin 0. Then three words are defined:
bulletinit - Make led an output
bulletpulse - Toggle the led
bulletmainloop - Call pulse forever

Then the test word, which is the main program, calls init and mainloop.

 

If you disassemble the code (dis command) you'll see that each word is a subroutine:

 

0x0000  018A    clrf    0x0A
0x0001  280C    goto    0x00C   ; (init-picforth)
0x0002  0000    nop
        ; name: init
        ; max return-stack depth: 0
0x0003  1683    bsf     0x03,5
0x0004  1006    bcf     0x06,0
0x0005  1283    bcf     0x03,5
0x0006  0008    return
        ; name: pulse
        ; max return-stack depth: 0
0x0007  1406    bsf     0x06,0
0x0008  1006    bcf     0x06,0
0x0009  0008    return
        ; name: mainloop
        ; max return-stack depth: 1
0x000A  2007    call    0x007   ; pulse
0x000B  280A    goto    0x00A   ; mainloop (rs depth: 1)
        ; name: (init-picforth)
        ; max return-stack depth: 0
0x000C  3032    movlw   0x32
0x000D  0084    movwf   0x04
        ; name: test
        ; max return-stack depth: 1
0x000E  2003    call    0x003   ; init
0x000F  280A    goto    0x00A   ; mainloop (rs depth: 1)

This is typical of a high level language and not very efficient at run time. However, having the words broken up (factored) like this make the program easy to read and maintain. However, Forth can let you have both efficient code at run time and easy to read code at compile time:

0 pin-b led 
macro  
: init led >output ;  
: pulse led high led low ;  
: mainloop begin pulse again ;  
target  
main : test init mainloop ;  
This is the same as before, except for the introduction of the macro word before the word definitions. The target word reverses the effect of the macro word. When you disassemble this you get: 
 
0x0000  018A    clrf    0x0A
0x0001  2803    goto    0x003   ; (init-picforth)
0x0002  0000    nop
        ; name: (init-picforth)
        ; max return-stack depth: 0
0x0003  3032    movlw   0x32
0x0004  0084    movwf   0x04
        ; name: test
        ; max return-stack depth: 0
0x0005  1683    bsf     0x03,5
0x0006  1006    bcf     0x06,0
0x0007  1283    bcf     0x03,5
0x0008  1406    bsf     0x06,0
0x0009  1006    bcf     0x06,0
0x000A  2808    goto    0x008   ; test + 0x003

Now all the "subroutines" were expanded inline!

You can define variables and constants in several different ways. Here's part of a program that uses a counter:

 

$F0 constant divreset
create counter 0 ,  \ This makes a variable and initializes it to 0
variable s1-len     \ This variable is not initialized

When you want to read the value of a variable you use @. So if nothing has changed since the program started, this line will put zero on the top of the stack:

counter @

Storing requires the ! word so this sets counter = 10:

10 counter !

There are shortcut words that do things like add directly to a variable (+!) or subtracts directly from a variable (-!). So to decrement counter, you might write:

1 counter -!

 

Back Home Next