Monday, July 21, 2014

PIC16F1708 Macros

So Easy, a Caveman can PIC it

The PIC processors have a small and primitive instruction set.  This makes it easy to learn, but doing anything remotely useful usually requires four or more instructions, liberally sprinkled with BANKSEL instructions, which all make the resulting code hard to read and error prone.

Your Own Development Kit



As you can see above, it is very easy to make a little development kit of your own, using Vero board and a DIP size PIC.  Once things are working, then you can use Eagle schematic and PCB designer to make a tiny surface mount board, or you can just spray the Vero model with conformal coating and stick it into a soap box with a blob of RVT...

Your Own High Level Language

The solution is to make your own high level language of sorts with macros.  The upshot of macros is that if you find a bug in one, then you can fix that bug in many places all at once.  The downside is that debugging complex macros can be hard, since the PICKit3 debugger has a very limited ability to step into a macro.  The best method is to write the code and debug it in a little test program, before turning it into a macro.

Goto in Macros

All high level languages have Goto statements.  Even Ada has it.  The PIC assembly language makes heavy use of Gotos in conditional branch instructions.

One of the annoying things with a macro language is that you cannot use a label inside a macro, since the second time you use the macro, the assembler will complain about a double defined label.  This makes it hard to do loops in a macro, but not impossible.  The trick is to use the goto $ operator:

;loop
        MOVIW FSR0++                ; Copy buffers from bottom upwards
        MOVWI FSR1++
        DECFSZ varlength, F
        GOTO $-3                    ; loop till zero


BASIC Macros

Possibly the most useful statements in BASIC were the PEEK and POKE instructions.  A typical embedded program is full of them.  So if you feel nostalgic, then Macros are an ideal way to get your revenge on Kernighan, Ritchie and Stroustrup.

Here are a few useful macros to get you going:

; Peek and Poke
_PEEK MACRO #register
        BANKSEL #register
        MOVF #register, W
        ENDM

_POKE MACRO #register, #literal
        BANKSEL #register
        MOVLW #literal
        MOVWF #register
        ENDM
       
_SAVE MACRO #register
BANKSEL #register
MOVWF #register
ENDM

_COPY MACRO #regfrom, #regto
        BANKSEL #regfrom
        MOVF #regfrom, W
        BANKSEL #regto
        MOVWF #regto
        ENDM

_ZAP MACRO #register
        BANKSEL #register
        CLRF #register
        ENDM
        
_INC MACRO #register
BANKSEL #register
INCF #register, F
ENDM

_DEC MACRO #register
BANKSEL #register
DECF #register, F
ENDM

; Turn one or more port pins on
_PORTON MACRO   #port, #state, #mask1
        BANKSEL #state
        MOVLW #mask1         ; get the mask
        IORWF #state, W      ; Set bit(s): OR with 1 mask
        MOVWF #state         ; save the state
        BANKSEL #port
        MOVWF #port          ; set the port
        ENDM

; Turn one or more port pins off
_PORTOFF MACRO   #port, #state, #mask0
        BANKSEL #state
        MOVLW #mask0         ; get state
        ANDWF #state, W      ; Clear bit(s): AND with 0 mask
        MOVWF #state         ; save state
        BANKSEL #port
        MOVWF #port          ; set the port
        ENDM

; Toggle one or more port pins
_PORTTOGGLE MACRO    #port, #state, #mask1
        BANKSEL #state
        MOVLW #mask1         ; get 1 mask
        XORWF #state, W      ; toggle bit(s): XOR with 1 mask
        MOVWF #state         ; Save state
        BANKSEL #port
        MOVWF #port          ; Set the port
        ENDM


; Parameter Call Stack
_PUSHALL MACRO #param1, #param2, #param3
        _POKE varstack1, #param1
        _POKE varstack2, #param2
        _POKE varstack3, #param3
        ENDM

_PUSH1 MACRO #param1
        _POKE varstack1, #param1
        ENDM

_PUSH2 MACRO #param2
        _POKE varstack2, #param2
        ENDM

_PUSH3 MACRO #param3
        _POKE varstack3, #param3
        ENDM

_POP1 MACRO
        _PEEK varstack1
        ENDM

_POP2 MACRO
        _PEEK varstack2
        ENDM

_POP3 MACRO
        _PEEK varstack3
        ENDM


; Function Call
_CALL   MACRO #name, #param1, #param2, #param3
        _PUSHALL #param1, #param2, #param3
        PAGESEL #name
        call #name
        ENDM

; Conditional Branches
; Branch if not equal
; Note: Negative statements are harder to understand
_BNEWR  MACRO   #register, #destination
        BANKSEL #register
        SUBWF #register, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

_BNERR  MACRO   #register1, #register2, #destination
        BANKSEL #register1
        MOVF #register1, W
        SUBWF #register2, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

_BNEWL  MACRO   #literal, #destination
        BANKSEL #destination
        SUBLW #literal
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination

        ENDM

_BNERL  MACRO   #register, #literal, #destination
        BANKSEL #register
        MOVLW #literal
        SUBWF #register, W
        BTFSS STATUS, Z     ; Test and skip if Z flag is set
        GOTO #destination
        ENDM

; Branch if equal
; Note: Positive statements are easier to understand
_BEQWR  MACRO   #register, #destination
        BANKSEL #register
        SUBWF #register, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQRR  MACRO   #register1, #register2, #destination
        BANKSEL #register1
        MOVF #register1, W
        SUBWF #register2, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQWL  MACRO   #literal, #destination
        BANKSEL #destination
        SUBLW #literal
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

_BEQRL  MACRO   #register, #literal, #destination
        BANKSEL #register
        MOVLW #literal
        SUBWF #register, W
        BTFSC STATUS, Z     ; Test and skip if Z flag is clear
        GOTO #destination
        ENDM

; Branch if Bit equal
_BBEQRL MACRO #register, #literal, #label
        BANKSEL #register       ; select bank 0
        BTFSC #register, #literal     ; test RCIF receive interrupt
        GOTO #label
        ENDM

; Function call parameter 'stack'
; Obviously this only works one call deep!
group1      UDATA 0x0020
varstack1   res 1               
varstack2   res 1
varstack3   res 1

La Voila!

No comments:

Post a Comment

On topic comments are welcome. Junk will be deleted.