|A Numeric Example|
Using the APP-II's serial port it is easy to interact with your program. Consider this program, that converts decimal to hexadecimal:
<<NOTE: If you want to know what an instruction does and it is underlined, just click on it!>>
;Simple Program Example PROCESSOR 16F873 __CONFIG 0x3FF6 INCLUDE <P16F873.INC> INCLUDE <app2.inc> CHAR EQU 0x20 DECNUM EQU 0x21 org 0 goto start org 5 start:
; Init Serial Port movlw HIGH SerialSetup movwf PCLATH call SerialSetup clrf PCLATH main: clrf DECNUM ; DECNUM=0
; Send a ?
iloop: ; Read a character
; If Enter go to cr
; Not enter, must be a number
; So subtract 0 from it
; Multiply running total by 10 movf DECNUM,W addwf DECNUM,W ; W=DECNUM * 2 bcf STATUS,C rlf DECNUM,F bcf STATUS,C rlf DECNUM,F bcf STATUS,C rlf DECNUM,F ; DECNUM=DECNUM * 8 addwf DECNUM,F ; DECNUM = (DECNUM*8) + (DECNUM*2) = 10*DECNUM movf CHAR,W addwf DECNUM,F goto iloop cr: movlw DECNUM movwf FSR movlw HIGH Hexout movwf PCLATH call Hexout movlw HIGH SerialTransmit movwf PCLATH movlw .13 call SerialTransmit goto main END
You can try the program first to see how it works.
Unless you turn local echo on, you'll be typing blind. When you hit enter, the APP-II will echo back the number you typed, an equal sign, and the hex equivalent. Then you'll see another question mark and you can enter another number.
If you skim the code, you'll see that some addwf operations store their result in the W register and some store their result in the register you are using. This is common with math operations on the PIC. The trick is to look at the last part of the instruction. So:
adds W and SOMEVAR and puts the result in the W register. However:
does the same computation, but it stores the result in the SOMEVAR register. This offers a great deal of flexibility since you don't have to move everything out of W.
The movf instruction moves a register. It can also take a W or F modifier. It makes sense that if you want to move SOMEVAR to W you'd write:
However, what is the meaning of this next statement?
This moves the SOMEVAR register to itself. At first this seems like nonsense until you realize that moving things around sets certain flags in the STATUS register. So, for example, if SOMEVAR is zero in the above example, the STATUS register's Z bit will be set. Otherwise, it will be cleared.
One other thing to notice about this code is that the calls to the APP-II's ROM routines require a change in the bank. The PIC's goto and call instructions only have room for the lower bits of the program counter (the special register, PCL, that tells the current program location).
Think about a phone number in a small town. All the numbers have the same first three digits. So if you don't have enough room, you just write down the last four digits. That's the same thing here. The goto and call instructions just set the last bits of the PCL register. The top bits come from the PCLATH register.
So to call the non-local APP-II routines, you have to write code like this:
The first instruction loads the high bits of the target label. The second instruction stores that value in PCLATH. The call executes the code, and automatically uses the value in PCLATH. Finally, the last line zeros out PCLATH so any subsequent call or goto will use zeros for the topmost bits (which is what we want since we are at the start of the APP-II's memory space).
The other thing that confuses beginners is how to do conditional jumps. The PIC doesn't have a conditional goto instruction. Instead, you test a bit in a register and skip the next instruction based on the state of that bit. Here's how the program tests for a carriage return (decimal value 13):
movwf CHAR ; get character in W sublw .13 ; Subtract 13 from character btfsc STATUS,Z ; Test STATUS, Z bit (zero bit) and skip if clear goto cr ; If it was set, goto cr ; here if not carriage return
Wow! This module is already long enough, but let me describe the algorithm in use. When you start typing a number, the PIC zeros a register (DECNUM). When you enter each character, the PCI multiplies DECNUM by 10, and then adds the value (the character minus '0') into DECNUM. So if you enter 153 it goes something like this:
Multiplying by 10 might sound hard on the PIC, but it is easy. The trick is realize that shifting a number left by 1 bit multiplies it by 2; shift it twice, you multiply by 4. Three shifts multiplies by 8. Then it is easy to figure it like this:
10 * x = 8 * x + 2 * x = (x shifted by 3) + (x shifted by 1)
Whew! If you are still with me, you are doing great! Read the code again until you are comfortable with it. Then take a break and try the next module.