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
_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
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
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!
Comments
Post a Comment
On topic comments are welcome. Junk will be deleted.