;
; PicMacro12bitCore.inc
;
; Collection of PIC 12 bit core MPASM Macro's
; Terry Rudersdorfer, AScT (VE7TRZ) 15/Nov/2014
;
; this file is provided as is to the public domain      
;
; Inspired by Karl Lunt's Macro Library (May 1999)
;
; I built this as a fun project specifically to use; sav|svc|cmp<n><r>, _if, select, 
; and mSec(s) Macro's ... all of the other Macro's fall in to the nice to have category
;
; there is undoubtably going to be simpler ways to implement some logic but for quick
; prototyping I found these Macro's very useful ... specifically they quickly provided 
; constructs to create a State Machine for a simple automation project    
;
; Macro Parameters
;
; There are 3 basic parameters; <L>,<S>, and <D> where:
; L = Literal as 1 or 2 bytes or a bit
; S = Source as 1 or 2 registers or a bit
; D = Destination as 1 or 2 registers or a bit (results returned in D or Status)
;
; There are an additional 4 special parameters; <B>,<Z>,<A>, and <X> where;
; B = Bit Reference, always requires a D or S register ie S,Bs or D,Bd
; Z = Bit Reference, always requires a Register,Bit (cmp16 bit interim Z value)
; A = action _ne, _eq, _gt, or _lt (used in _if tests)
; X = Operation as: logical AND or logical OR (used in _if tests)
;
; Numeric suffixes on Macro Names, L, S and D are defined as:
;   1 = Bit, 8 = Byte, 16 = unsigned Word (data pairs registers are in Low High format)
;
; Suffixes on B (Bit) are s = Source or d = Destination
;
; A suffix of r on the end of a Macro name indicates the Macro uses a Source Register ... 
; the numeric suffix identifies the size of the Source and Destination
;
; A special case of Zr,Zb points to a Bit Location to temporarily store the value of the 
; Zero Flag required only for 16bit compares; cmp16, cmp16r, tst16 and tst16r ... Zr is the
; register, Zb is the bit number
;
; Macro List
;
; Macro  Params                         Operation           Results
;------ ----------------------------    ------------------- ------- 
; and8   <L>,<D>                        L       & D      -> D
; and8r  <S>,<D>                        S       & D      -> D
; and16  <L16>,<D16>                    L16     & D16    -> D16
; and16r <S16>,<D16>                    S16     & D16    -> D16
; add8   <L>,<D>                        L       + D      -> D
; add8r  <S>,<D>                        S       + D      -> D
; add16  <L>,<D>                        L16     + D16    -> D16
; add16r <S>,<D>                        S16     + D16    -> D16
; clr1   <D>,<B>                        0                -> D,B
; clr8   <D>                            0                -> D
; clr16  <D16>                          0                -> D16
; cmp1   <L>,<D>,<Bd>                   L       = D,Bd?  -> Z = 1 (L = D)
; cmp1r  <S>,<Bs>,<D>,<Bd>              S,Bs    = D,Bd?  -> Z = 1 (S = D)
; cmp8   <L>,<D>                        L       = D?     -> Z = 1 (L = D)
;                                                           C = 1 (L > D)
;                                                           C = 0 (L < D)
; cmp8r  <S>,<D>                        S       = D?     -> Z = 1 (S = D)
;                                                           C = 1 (S > D)
;                                                           C = 0 (S < D)
; cmp16  <L16>,<D16>,<Zr>,<Zb>          L16     = D16?   -> Z = 1 (L = D)
;                                                           C = 1 (L > D)
;                                                           C = 0 (L < D)
; cmp16r <S16>,<D16>,<Zr>,<Zb>          S16     = D16?   -> Z = 1 (S = D)
;                                                           C = 1 (S > D)
;                                                           C = 0 (S < D)
; cpl1   <D>,<B>                        D,B     ~        -> D,B
; cplw                                  W       ~        -> W
; cpl8   <D>                            D       ~        -> D
; cpl16  <D16>                          D16     ~        -> D16
; inc8   <D>                            D       + 1      -> D   Z = 1 (D = 0)
; inc16  <D16>                          D16     + 1      -> D16 Z = 1 (D = 0)
; ior8   <L>,<D>                        L       & D      -> D
; ior8r  <S>,<D>                        S       & D      -> D
; ior16  <L16>,<D16>                    L16     & D16    -> D16
; ior16r <S16>,<D16>                    S16     & D16    -> D16
; sav1   <L>,<D>.<Db>                   L                -> D,Bd
; sav1r  <S>,<Bs>,<D>.<Db>              S,Bs             -> D,Bd
; sav8   <L>,<D>                        L                -> D
; sav8r  <S>,<D>                        S                -> D
; sav16  <L16>,<D16>                    L16              -> D16
; sav16r <S16>,<D16>                    S16              -> D16
; set1   <D>,<Bd>                       1                -> D,Bd
; sub8   <L>,<D>                        D       - L      -> D     Z = 1 (D = 0)
;                                                                 C = 1 (D > 0)
;                                                                 C = 0 (D < 0)
; sub8r  <S>,<D>                        D       - S      -> D     Z = 1 (D = 0)
;                                                                 C = 1 (D > 0)
;                                                                 C = 0 (D < 0)
; sub16  <L16>,<D16>                    D16     - L16    -> D16   Z = 1 (D = 0)
;                                                                 C = 1 (D > 0)
;                                                                 C = 0 (D < 0)
; sub16r <S16>,<D16>                    D16     - S16    -> D16   Z = 1 (D = 0)
;                                                                 C = 1 (D > 0)
;                                                                 C = 0 (D < 0)
; svc8   <L>,<D>                        L       ~ + 1    -> D
; svc8r  <S>,<D>                        S       ~ + 1    -> D
; svc16  <L16>,<D16>                    L16     ~ + 1    -> D16
; svc16r <S16>,<D16>                    S16     ~ + 1    -> D16
; tcp8   <D>                            D       ~ + 1    -> D
; tcp16  <D16>                          D16     ~ + 1    -> D16
; xor8   <L>,<D>                        L     xor D      -> D
; xor8r  <S>,<D>                        S     xor D      -> D
; xor16  <L16>,<D16>                    L16   xor D16    -> D16
; xor16r <S16>,<D16>                    S16   xor D16    -> D16
;
;
; Specialty Macros:
;
; Timers:
;
; mSec                                 1 mSec Delay
;
; mSecs <L16>,<D16>                    Ll6 (literal) * 1 mSec Delay (uses D16)
;
;
; Logical:
;
; if, then, else, endif (all are keywords, so macros are prefixed with '_')
; else is optional
;
; tst# and tst#r are tests that can be chained together to form complex if's
; the last tst in any _if must have an Operation = __
;
; if's can be nested with or without chained tests with like Operators
; (NOTE: while mixing Operators is not recommended, A OR (B and C) will work
;
; if tst and tst and tst then do something endif
;
; if tst or tst or tst then do something else do something else endif
;
; example: test a flag/bit state
;          and test a 16 bit register
;          and test an 8 bit register
;          then
;             test a 16 bit register against another 16 bit register
;
; #define SomeFlag r08,0
; #define TmpZ     r08,1
; #define Reg16x   r09   ; and r0A
; #define Reg16y   r0B   ; and r0C
; #define Reg8x    r0D
; #define Reg8y    r0E
;
;         _if
;            tst1  1,      SomeFlag,       _eq, _and
;            tst16 0x1212, Reg16x,   TmpZ, _ne, _and
;            tst8r Reg8x,  Reg8y,          _eq, __
;         _then
;             _if
;                tst16r Reg16x, Reg16z, TmpZ, _eq, __
;             _then
;                nop
;                nop
;             _else
;                nop
;             _endif
;         _endif
;
; _if
;
; _tst1   <L>,<D>,<Bd>,<A>,<X>            A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _tst1r  <S>,<Bs>,<D>,<Bd>,<A>,<X>       A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _tst8   <L>,<D>,<A>,<X>                 A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _tst8r  <S>,<D>,<A>,<X>                 A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _tst16  <L16>,<D16>,<Zr>,<Zb>,<A>,<X>   A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _tst16r <S16>,<D16>,<Zr>,<Zb>,<A>,<X>   A; 0(!=), 1(=), 2(>), 3(<)
;                                         X; 0(or), 1(and)
; _then
;
; _else
;
; _endif
;
; 
; considered adding a select16 however didn't have a use for it in my last project
;
; select, case, endcase, otherwise, endselect
;
; case#, case#r and otherwise can be chained together
; otherwise is optional
;
; example 
;             select
;                 movf SomeReg,W
;                 
;                 case8 0x11  ; W = 0x11?
;                    nop      ; yes do something
;
;                 case8r AnotherReg ; W = AnotherReg?
;                    nop      ; yes do somthing else
;
;                 otherwise   ; optional 
;                    nop      ; default something
;
;              endselect
;
;
; select
;
; case8  <L>                          L     = W?       -> do case
;
; case8r <S>                          S     = W?       -> do case
;
; endcase
;
; otherwise
;
; endselect
;

; Constants
#define mccComplement 0xFF
#define mccZero       0x00
#define mccOne        0x01

; if then else; tst modifiers
#define __   1 ; must always be the last tst in any _if
#define _or  0
#define _and 1
#define _ne  0
#define _eq  1
#define _gt  2
#define _lt  3

; And Literal with D Register
and8 macro L,D
        movlw L
        andwf D,F
     endm


; And S Register with D Register
and8r macro S,D
        movf  S,W
        andwf D,F
     endm


; And 16 bit Literal with 16 bit D Register
and16 macro L16,D16
         and8 LOW(L16) ,D16
         and8 HIGH(L16),D16+1
      endm


; And 16 bit S Register with 16 bit D Register
and16r macro S16,D16
         and8r S16,  D16
         and8r S16+1,D16+1
      endm


; Add Literal to D Register
add8 macro L,D
        movlw L
        addwf D,F
     endm


; Add S Register to D Register
add8r macro S,D
       movf  S,W
       addwf D,F
     endm


; Add 16 bit Literal with 16 bit D Register
add16 macro L16,D16
         add8 HIGH(L16),D16+1
         add8  LOW(L16),D16
         _add16 D16
      endm


; Add 16 bit S Register to 16 bit D Register
add16r macro S16,D16
         add8r S16+1,D16+1
         add8r S16,  D16
         _add16 D16
       endm


_add16 macro D16
       local addend
          bnc  addend
          incf D16+1,F
addend
       endm


; Clear Bd bit in D Register
clr1 macro D,Bd
        bcf D,Bd
     endm


; Clear D Register
clr8 macro D
        clrf D
     endm


; Clear 16 bit D Register
clr16 macro D16
         clrf D16
         clrf D16+1
      endm


; Compare Literal bit with D Register, Bd bit (Z = 1 when equal)
cmp1  macro L,D,Bd
         clrw
         if L == 0
         cplw
         endif
         btfss D,Bd
         cplw
      endm


; Compare S Register, Bs bit with D Register, Bd bit (Z = 1 when equal)
cmp1r  macro S,Bs,D,Bd
         clrw
         btfss S,Bs
         cplw
         btfss D,Bd
         cplw
      endm


; Compare Literal with D Register  Z = 1 when D EQ L
;                                  C = 1 when D GT L
;                                  C = 0 when D LT L
cmp8  macro L,D
         movlw L
         subwf D,W
      endm


; Compare S Register with D Register  Z = 1 when D EQ S
;                                     C = 1 when D GT S
;                                     C = 0 when D LT S
cmp8r macro S,D
         movf  S,W
         subwf D,W
      endm
     

; Compare 16 bit Literal with 16 bit D Register  Z = 1 when D16 EQ L16
;                                                C = 1 when D16 GT L16
;                                                C = 0 when D16 LT L16
cmp16 macro L16,D16,Zr,Zb
      local _bc
         cmp8  LOW(L16),D16
         sav1r STATUS,Z,Zr,Zb
         movlw HIGH(L16)
         bc    _bc
      if HIGH(L16) == mccComplement
         movlw mccZero
      else
         movlw HIGH(L16) + 1
      endif
_bc      subwf (D16)+1,W
         btfss Zr,Zb
         iorlw mccOne
         btfss STATUS,Z
         iorlw mccOne
      endm


; Compare 16 bit S Register with 16 bit D Register  Z = 1 when D16 EQ S16
;                                                   C = 1 when D16 GT S16
;                                                   C = 0 when D16 LT S16
cmp16r macro S16,D16,Zr,Zb
      local _bc
         cmp8r S16,D16
         sav1r STATUS,Z,Zr,Zb
         movf  S16+1,W
         bc    _bc
         incf  S16+1,W
_bc      subwf (D16)+1,W
         btfss Zr,Zb
         iorlw mccOne
         btfss STATUS,Z
         iorlw mccOne
      endm


; Complement Bd bit in D Register
cpl1 macro D,Bd
        movf  D,W
        clrf  D
        bsf   D,Bd
        xorwf D,F
     endm


; Complement W Register
cplw macro
        xorlw mccComplement
     endm


; Complement D Register
cpl8 macro D
        comf D,F
     endm


; Compliment 16 bit D Register
cpl16 macro D16
         cpl8 D16
         cpl8 D16+1
      endm


; Increment D Register (Z = 1 when Zero)
inc8 macro D
        incf D,F
     endm


; Increment 16 bit D Register (Z = 1 when Zero)
inc16 macro D16
         incfsz D16,W
         decf   D16+1,F
         incf   D16+1,F
         movwf  D16
         iorwf  D16+1,W
      endm


; Ior Literal with D Register
ior8 macro L,D
        movlw L
        iorwf D,F
     endm


; Ior S Register with D Register
ior8r macro S,D
        movf  S,W
        iorwf D,F
     endm


; Ior 16 bit Literal with 16 bit D Register
ior16 macro L16,D16
         ior8 LOW(L16) ,D16
         ior8 HIGH(L16),D16+1
      endm


; Ior 16 bit S Register with 16 bit D Register
ior16r macro S16,D16
         ior8r S16,  D16
         ior8r S16+1,D16+1
      endm


; Save/Copy Literal bit to D Register, Bd bit
sav1 macro L,D,Bd
        if L == 0
        clr1  D,Bd
        else
        set1  D,Bd
        endif
     endm


; Save/Copy S Register, Bs bit to D Register, Bd bit
sav1r macro S,Bs,D,Bd
        clr1  D,Bd
        btfsc S,Bs
        set1  D,Bd
     endm


; Save/Copy Literal to D Register
sav8 macro L,D
        movlw L
        movwf D
      endm


; Save/Copy S Register to D Register
sav8r macro S,D
         movf  S,W
         movwf D
      endm


; Save/Copy 16 bit Literal to 16 bit D Register
sav16 macro L16,D16
         sav8   LOW(L16),D16
         sav8  HIGH(L16),D16+1
      endm


; Save/Copy 16 bit S Register to 16 bit D Register
sav16r macro S16,D16
         sav8r  S16,  D16
         sav8r  S16+1,D16+1
      endm


; Set Bd bit in D Register
set1 macro D,Bd
        bsf D,Bd
     endm


; Subtract Literal from D Register  Z = 1 when D EQ L
;                                   C = 1 when D GT L
;                                   C = 0 when D LT L
sub8 macro L,D
        movlw L
        subwf D,F
     endm


; Subtract S Register from D Register  Z = 1 when D EQ S
;                                      C = 1 when D GT S
;                                      C = 0 when D LT S
sub8r macro S,D
        movf  S,W
        subwf D,F
     endm


; Subtract 16 bit Literal from 16 bit D Register  Z = 1 when D16 EQ L16
;                                                 C = 1 when D16 GT L16
;                                                 C = 0 when D16 LT L16
sub16 macro L16,D16
      local _bc
         sub8  LOW(L16),D16
         movlw HIGH(L16)
         bc    _bc
      if HIGH(L16) == mccComplement
         movlw mccZero
      else
         movlw HIGH(L16) + 1
      endif
_bc      subwf (D16)+1,F
         movf  D16,W
         iorwf D16+1,W
      endm


; Subtract 16 bit S Register from 16 bit D Register  Z = 1 when D16 EQ S16
;                                                    C = 1 when D16 GT S16
;                                                    C = 0 when D16 LT S16
sub16r macro S16,D16
      local _bc
         sub8r S16,D16
         movf  S16+1,W
         bc    _bc
         incf  S16+1,W
_bc      subwf (D16)+1,F
         movf  D16,W
         iorwf D16+1,W
      endm


; Save Literal 2's Complimemt result in D Register
svc8 macro L,D
        sav8  L,D
        tcp8  D
     endm


; Save S Register 2's Complimemt result in D Register
svc8r macro S,D
         sav8r S,D
         tcp8  D
      endm


; Save 16 bit Literal 2's Complimemt result in 16 bit D Register
svc16 macro L16,D16
         sav16 L16,D16
         tcp16 D16
      endm


; Save 16 bit S Register 2's Complimemt result in 16 bit D Register
svc16r macro S16,D16
          sav16r S16,D16
          tcp16  D16
       endm


; 2's Complement D Register
tcp8 macro D
        cpl8  D
        inc8  D
     endm


; 2's Complement 16 Bit D Register
tcp16 macro D16
         cpl16 D16
         inc16 D16
      endm


; Xor Literal with D Register
xor8 macro L,D
        movlw L
        xorwf D,F
     endm


; Xor S Register with D Register
xor8r macro S,D
        movf  S,W
        xorwf D,F
     endm


; Xor 16 bit Literal with 16 Bit D Register
xor16 macro L16,D16
         xor8 LOW(L16) ,D16
         xor8 HIGH(L16),D16+1
      endm


; Xor 16 bit S Register with 16 Bit D Register
xor16r macro S16,D16
         xor8r S16,  D16
         xor8r S16+1,D16+1
      endm


; mSec Delay 1 millisecond
; in the 10F206 there are no interrupts so Delays can be very useful
; this 1 milli second delay requires the 10F206 to use the TMR PSA
;      Uses TMR0 expects: Osc = 4 MHz
;                         F0SC / 4 = 1 MHz
;                         TMR set to use PreScaler
;                         Prescaler set to 1:8 (PSA = 2 in 10F206)
;
#define mccTMR0 0x7D ; FOSC/4 1:8 delay count to match 1 millisecond

mSec macro
     local mcLP
        clrf TMR0
mcLP    movlw mccTMR0   ; check delay value
#ifdef _WDT_SET
        clrwdt
#endif
        subwf TMR0,W
        bnc mcLP
     endm


; mSecs Delay 16 bit Literal * millisecond Uses 16 bit D Register
mSecs macro L16,D16
      local mcLP
         svc16 L16,D16
mcLP     mSec
         inc16 D16
         bnz mcLP
      endm


; if then else endif (else is optional)
;
; multiple tests can be chained together
; tst# and or tst#r can be combined with AND (X = 1) or OR (X = 0)
; actions include: D != S (A = 0), D = S (A = 1), D > S (A = 2), D < S (A = 3)
; caveat:
;  - the last test in an _if MUST always have an Operation of __
;  - if a combination of OR and AND are used, OR tests MUST be done first
;    a == 0 OR (b != 0x1000 AND c > 3) can be coded as
;   _if
;   tst8   a,0x00,        _eq,_or
;   tst16  b,0x1000,Zr,Zb,_ne,_and
;   tst8   c,0x03,        _gt,__
;   _then
;      <do some stuff>
;   _else
;      <alternate stuff>
;   _endif


; the key to this logic is the _ifs pointer which must keep incrementing to
; ensure labels are not duplicated for multiple instances of _if
; _ifptr also uses last value logic to allow for nesting of if statements
            variable _ifs    =0  ; number of _if instances
            variable _ifptr  =0  ; pointer to current _if instance
            variable _lastif =0  ; Temporary Storage

_if         macro
_ifs           set       _ifs+1     ; new if instance
_lastif        set       _ifptr     ; temporary storage of last if instance
_ifptr         set       _ifs       ; pointer to current if instance
_lastptr#v(_ifptr) set   _lastif    ; last if instance saved to current instance
_elseflg#v(_ifptr) set   1          ; special flag to allow optional else
            endm

tst1        macro L,D,Bd,A,X          ; Literal bit = Destination bit?
               cmp1 L,D,Bd
               _tst A,X
            endm

tst1r       macro S,Bs,D,Bd,A,X       ; Source bit = Destination bit?
               cmp1r S,Bs,D,Bd
               _tst  A,X
            endm

tst8        macro L,D,A,X             ; Literal Byte = Destination Byte?
               cmp8 L,D
               _tst A,X
            endm

tst8r       macro S,D,A,X             ; Source Byte = Destination Byte?
               cmp8r S,D
               _tst  A,X
            endm

tst16       macro L16,D16,Zr,Zb,A,X   ; Literal Word = Destination Word? (LoHi)
               cmp16 L16,D16,Zr,Zb
               _tst  A,X
            endm

tst16r      macro S16,D16,Zr,Zb,A,X    ; Source Word = Destination Word? (LoHi)
               cmp16r S16,D16,Zr,Zb
               _tst   A,X
            endm

_tst        macro A,X
            if A == _ne               ; S <> D
            if X == _or               ; OR
               bnz _thenx#v(_ifptr)   ; OR true -> then,  false -> next test
            else                      ; AND
               bz  _elsex#v(_ifptr)   ; AND true -> next test, false -> else
            endif                     ; Logical
            endif                     ; Equal
            if A == _eq               ; S = D
            if X == _or               ; OR
               bz  _thenx#v(_ifptr)   ; OR true -> then,  false -> next test
            else                      ; AND
               bnz _elsex#v(_ifptr)   ; AND true -> next test, false -> else
            endif                     ; Logical
            endif                     ; Equal
            if A == _gt               ; S > D
            if X == _or               ; OR
               bnc _thenx#v(_ifptr)   ; OR true -> then,  false -> next test
            else                      ; AND
               bc  _elsex#v(_ifptr)   ; AND true -> next test, false -> else
            endif                     ; Logical
            endif                     ; Equal
            if A == _lt               ; S < D
            if X == _or               ; OR
               bc  _thenx#v(_ifptr)   ; OR true -> then,  false -> next test
            else                      ; AND
               bnc _elsex#v(_ifptr)   ; AND true -> next test, false -> else
            endif                     ; Logical
            endif                     ; Equal
            endm

; then
_then       macro
_thenx#v(_ifptr)
            endm

; else     (optional)
_else       macro
              goto _endifx#v(_ifptr)
_elsex#v(_ifptr)
_elseflg#v(_ifptr)    set     0        ; clear else option flag
            endm

; endif
_endif      macro
            if  _elseflg#v(_ifptr) == 1 ; test to see if else was included
_elsex#v(_ifptr)                        ; set the elsex pointer when no else
_elseflg#v(_ifptr)    set     0
            endif
_endifx#v(_ifptr)
_ifptr       set     _lastptr#v(_ifptr) ; set pointer to last if instance
            endm


; select case endcase otherwise endselect  (otherwise is optional)
;
; mutiple cases can be chained together

            variable _sel    =0   ; Select instance counter
            variable _selptr =0   ; current Select instance pointer
            variable _case   =0   ; Case instance counter
            variable _caseptr=0   ; current Case instance pointer
            variable _lastsel=0   ; temporary storage

; Select
select      macro
_sel        set      _sel+1        ; get next instance
_lastsel    set      _selptr       ; temporary storage of last instance pointer
_selptr     set      _sel          ; set current instance pointer
_lastselptr#v(_selptr) set _lastsel ; save last instance pointer
            endm

; Case compares Literal to W Register
case8       macro    L
_case       set     _case+1
_caseptr    set     _case
               xorlw L
               bz    nextcase#v(_caseptr)
               xorlw L
               goto  endcasex#v(_caseptr)
nextcase#v(_caseptr)
               xorlw L
            endm

; Case compares S Register to W Register
case8r      macro S
_case       set      _case+1
_caseptr    set      _case
               xorwf S,W
               bz    nextcase#v(_caseptr)
               xorwf S,W
               goto  endcasex#v(_caseptr)
nextcase#v(_caseptr)
               xorwf S,W
            endm

; Endcase
endcase     macro
               goto sel#v(_selptr)
endcasex#v(_caseptr)
            endm

; Otherwise (optional)
otherwise   macro
_case       set      _case+1
_caseptr    set      _case
            endm

; EndSelect
endselect   macro
sel#v(_selptr)
_selptr     set      _lastselptr#v(_selptr)
            endm
