;
;  PIC macro library
;  Developed by Karl Lunt, May 1999
;

	messg	"Karl Lunt's PIC macro library, version 1.0"

;
;  This library provides macros for program structures such
;  as FOR-NEXT, REPEAT-ALWAYS, REPEAT-UNTIL, and others.
;
;  To incorporate this library in your PIC assembler program,
;  add the following INCLUDE statement immediately after the
;  INCLUDE statement that adds the Microchip equates for your
;  specific processor:
;
;	include		"macros.asm"
;
;  For example, if you are developing code for the 12c672,
;  you would use equates similar to:
;
;	include		".\p12c672.inc"
;	include		".\macros.asm"
;

;
;  Version 1.0   29 May 1999
;  Initial release.  Includes WAITWHILE, WAITUNTIL, and POLL-ENDPOLL.
;  Changed movfw to movf, to support older MCUs.  Will still give errors
;  in NEXTL macro on older PICs, since that macro uses the addlw
;  instruction.
;


;
;  Declare some variables used by the macro library.
;

	variable	_forknt=0
	variable	_nxtknt=0
	variable	_rptknt=0
	variable	_alwknt=0
	variable	_untknt=0
	variable	_seltot=0
	variable	_selknt=0
	variable	_castot=0
	variable	_casknt=0
	variable	_waitk=0
	variable	_pollk=0
	variable	_pollt=0



;
;  Define the BEQ (branch-if-equal)
;
;  Syntax:
;		beq	label
;
;  The BEQ macro tests the current state of the Z-bit.  It
;  does not alter W or any registers.
;

beq	macro	addr
	btfsc	STATUS,Z
	goto	addr
	endm


;
;  BNE (branch-if-not-equal)
;
;  Syntax:
;		bne	label
;
;  The BNE macro tests the current state of the Z-bit.  It
;  does not alter W or any registers.
;

bne	macro	addr
	btfss	STATUS,Z
	goto	addr
	endm


;
;  FOR (start of FOR-NEXT loop)
;
;  Syntax:
;		for	var,begl,endl
;
;  The FOR macro starts a FOR-NEXT loop.  The arguments are:
;  var is a RAM variable used as the loop index,
;  begl is a literal value used as the initial index value,
;  endl is a literal value used as the ending index value.
;
;  Control will run through the FOR-NEXT loop until the value
;  in var EQUALS the endl literal value; the value is tested
;  at the top of the loop.  At this point, control exits the
;  loop at the corresponding NEXT macro.
;
;  This macro destroys the contents of the W register.
;
;  You may terminate a FOR loop with NEXT, NEXTL, or NEXTF.
;

for	macro	var,begl,endl
	movlw	begl
	movwf	var
_for#v(_forknt)
	movlw	endl
	subwf	var,w
;	movf	var,w
;	sublw	endl
	beq	_next#v(_forknt)
_forknt	set	_forknt+1
_nxtknt	set	_forknt
	endm


;
;  FORF (start of FORF-NEXT loop)
;
;  Syntax:
;		forf	var,begl,endf
;
;  The FORF macro starts a FORF-NEXT loop.  The arguments are:
;  var is a RAM variable used as the loop index,
;  begl is a literal value used as the initial index value,
;  endf is a flag or RAM variable used as the ending index
;  value.
;
;  Control will run through the FORF-NEXT loop until the value
;  in var EQUALS the endf variable value; the value is tested
;  at the top of the loop.  At this point, control exits the
;  loop at the corresponding NEXT macro.
;
;  This macro destroys the contents of the W register.
;
;  You may terminate a FORF loop with NEXT, NEXTL, or NEXTF.
;

forf	macro	var,begl,endf
	movlw	begl
	movwf	var
_for#v(_forknt)
	movf	var,w
	subwf	endf
	beq	_next#v(_forknt)
_forknt	set	_forknt+1
_nxtknt	set	_forknt
	endm


;
;  NEXT (end of a FOR-NEXT loop)
;
;  Syntax:
;		next	var
;
;  The NEXT macro terminates a FOR-NEXT loop.  The 
;  argument is:
;  var is a RAM variable that is the index of the FOR-NEXT loop.
;
;  Control will increment the value in var, then go back to
;  the top of the FOR-NEXT loop for testing.  Note that the var
;  argument for the NEXT macro must match the var argument for
;  the corresponding FOR macro.  The macro library does not perform
;  this check for you; you have to get it right yourself!
;
;  This macro alters the contents of the W register and the index
;  variable var.
;
;  You may use NEXT to terminate a FOR-NEXT, FORF-NEXT, or
;  FORL-NEXT loop.
;

next	macro	var
_nxtknt	set	_nxtknt-1
	incf	var,f
	goto	_for#v(_nxtknt)
_next#v(_nxtknt)
	endm


;
;  NEXTL (end of a FOR-NEXTL loop)
;
;  Syntax:
;		nextl	var,incl
;
;  The NEXTL macro terminates a FOR-NEXTL loop.  The 
;  arguments are:
;  var is a RAM variable that is the index of the FOR-NEXTL loop,
;  incl is a literal value used to modify the index.
;
;  Control will add the literal incl to the value in var, then
;  go back to the top of the FOR-NEXTL loop for testing.  Note
;  that the var argument for the NEXTL macro must match the var
;  argument for the corresponding FOR macro.  The macro library
;  does not perform this check for you; you have to get it right
;  yourself!
;
;  This macro alters the contents of the W register and the index
;  variable var.
;
;  You may use NEXTL to terminate a FOR-NEXTL, FORF-NEXTL, or
;  FORL-NEXTL loop.
;
;  NOTE:  This macro uses the addlw instruction, which is not
;  supported on the older PICs, such as the 16c54.  Using this
;  macro in a source file for such a chip will generate assembler
;  errors.
;

nextl	macro	var,incl
_nxtknt	set	_nxtknt-1
	movf	var,w
	addlw	incl
	movwf	var
	goto	_for#v(_nxtknt)
_next#v(_nxtknt)
	endm


;
;  NEXTF (end of a FOR-NEXTF loop)
;
;  Syntax:
;		nextf	var,incf
;
;  The NEXTF macro terminates a FOR-NEXTF loop.  The 
;  arguments are:
;  var is a RAM variable that is the index of the FOR-NEXTL loop,
;  incf is a register whose value is used to modify the index.
;
;  Control will add the value in incf to the value in var, then
;  go back to the top of the FOR-NEXTF loop for testing.  Note
;  that the var argument for the NEXTF macro must match the var
;  argument for the corresponding FOR macro.  The macro library
;  does not perform this check for you; you have to get it right
;  yourself!
;
;  This macro alters the contents of the W register and the index
;  variable var.
;
;  You may use NEXTF to terminate a FOR-NEXTF, FORF-NEXTF, or
;  FORL-NEXTF loop.
;

nextf	macro	var,incf
_nxtknt	set	_nxtknt-1
	movf	var,w
	addwf	incf,f
	goto	_for#v(_nxtknt)
_next#v(_nxtknt)
	endm


;
;  REPEAT (start of a REPEAT-ALWAYS or REPEAT-UNTIL loop)
;
;  Syntax:
;		repeat
;
;  The REPEAT macro marks the start of a REPEAT-ALWAYS or
;  REPEAT-UNTILEQ or REPEAT-UNTILNE loop.  Control will always
;  return to the start of the REPEAT macro if the loop is
;  terminated with an ALWAYS macro.  Control will conditionally
;  return to the start of the REPEAT macro if the loop is
;  terminated with an UNTILEQ or UNTILNE macro.
;
;  This macro does not alter the W register.
;

repeat	macro
_rpt#v(_rptknt)
_rptknt	set	_rptknt+1
_alwknt	set	_rptknt
_untknt	set	_rptknt
	endm


;
;  ALWAYS (returns to corresponding REPEAT macro)
;
;  Syntax:
;		always
;
;  The ALWAYS macro marks the end of a REPEAT-ALWAYS loop.
;  Control is automatically passed back to the corresponding
;  REPEAT macro.
;
;  This macro does not alter the W register.
;

always	macro
_alwknt	set	_alwknt-1
	goto	_rpt#v(_alwknt)
	endm


;
;  UNTILEQ (conditionally returns to corresponding REPEAT macro)
;
;  Syntax:
;		untileq
;
;  The UNTILEQ macro marks the end of a REPEAT-UNTILEQ loop.
;  Control is passed back to the corresponding REPEAT macro only
;  if the Z-bit is cleared at the time the UNTILEQ macro is
;  processed.
;
;  This macro does not alter the W register.
;

untileq	macro
_untknt	set	_untknt-1
	bne	_rpt#v(_untknt)
	endm


;
;  UNTILNE (conditionally returns to corresponding REPEAT macro)
;
;  Syntax:
;		untilne
;
;  The UNTILEQ macro marks the end of a REPEAT-UNTILNE loop.
;  Control is passed back to the corresponding REPEAT macro only
;  if the Z-bit is set at the time the UNTILNE macro is processed.
;
;  This macro does not alter the W register.
;

untilne	macro
_untknt	set	_untknt-1
	beq	_rpt#v(_untknt)
	endm


;
;  SELECT (declares start of SELECT-ENDSELECT structure)
;
;  Syntax:
;		select
;
;  The SELECT macro marks the beginning of a SELECT-ENDSELECT
;  structure.  A typical SELECT-ENDSELECT structure looks like this:
;
;	select			; start of SELECT block
;	case	5		; if W = 5...
;	  .			; do something
;	endcase			; end of W = 5 clause
;	casef	foo		; if W = foo
;	  .			; do something else
;	endcase			; end of W = foo clause
;	  .			; default action (all cases fail)
;	endselect		; end of SELECT block
;
;  This macro does not alter the W register.
;

select	macro
_seltot	set	_seltot+1
_selknt	set	_seltot
	endm


;
;  ENDSELECT (declares end of SELECT-ENDSELECT structure)
;
;  Syntax:
;		endselect
;
;  The ENDSELECT macro marks the end of a SELECT-ENDSELECT
;  structure.  You must terminate each SELECT macro with
;  a matching ENDSELECT macro or MPASM will report errors.
;
;  This macro does not alter the W register.
;

endselect	macro
sel#v(_selknt)
_selknt	set	_selknt-1
	endm


;
;  CASE (declares start of a CASE-ENDCASE structure)
;
;  Syntax:
;		case	lit
;
;  where lit is a literal value used as the CASE selector.
;
;  When the CASE macro is executed, the value in W is compared
;  with the literal value lit.  If W equals lit, code following
;  the CASE macro is executed.  If W does not equal lit, control
;  passes to the code following the corresponding ENDCASE macro.
;
;  W is preserved in the CASE macro.
;

case	macro	lit
_castot set	_castot+1
_casknt	set	_castot
	xorlw	lit
	beq	cas#v(_casknt)
	xorlw	lit
	goto	ecas#v(_casknt)
cas#v(_casknt)
	xorlw	lit
	endm


;
;  CASEF (declares start of a CASEF-ENDCASE structure)
;
;  Syntax:
;		casef	var
;
;  where var is a register or variable used as the CASEF selector.
;
;  When the CASEF macro is executed, the value in W is compared
;  with the value in var.  If W equals var, code following
;  the CASEF macro is executed.  If W does not equal var, control
;  passes to the code following the corresponding ENDCASE macro.
;
;  W is preserved in the CASEF macro.
;

casef	macro	var
_castot set	_castot+1
_casknt	set	_castot
	xorwf	var,w
	beq	cas#v(_casknt)
	xorwf	var,w
	goto	ecas#v(_casknt)
cas#v(_casknt)
	xorwf	var,w
	endm


;
;  ENDCASE (declares end of a CASE-ENDCASE or CASEF-ENDCASE structure)
;
;  Syntax:
;		endcase
;
;  The ENDCASE macro marks the end of a CASE-ENDCASE or CASEF-ENDCASE
;  structure.  This macro serves as a jump address for the corresponding
;  CASE or CASEF macro.
;
;  You must have an ENDCASE macro for each CASE or CASEF macro.  If
;  not, MPASM will report errors when it assembles your code.
;
;  This macro preserves the W register.
;

endcase	macro
	goto	sel#v(_selknt)
ecas#v(_casknt)
_casknt	set	_casknt-1
	endm


;
;  WAITWHILE (declares a high-speed WAIT-WHILE loop)
;
;  Syntax:
;
;  		waitwhile	addr,andl,xorl
;
;  The WAITWHILE macro creates a tight loop that reads the byte
;  in address addr, ANDs it with the literal andl, then XORs the
;  result with the literal xorl.  If the result is TRUE (non-zero),
;  the loop repeats.  If the result is FALSE (zero), control exits
;  the macro.
;
;  This macro destroys the W register.  It does not modify addr.
;

waitwhile	macro	addr,andl,xorl
waitw#v(_waitk)
	movf	addr,w
	andlw	andl
	if	xorl != 0
	xorlw	xorl
	endif
	bne	waitw#v(_waitk)
_waitk	set	_waitk+1
	endm


;
;  WAITUNTIL (declares a high-speed WAIT-UNTIL loop)
;
;  Syntax:
;
;  		waituntil	addr,andl,xorl
;
;  The WAITUNTIL macro creates a tight loop that reads the byte
;  in address addr, ANDs it with the literal andl, then XORs the
;  result with the literal xorl.  If the result is TRUE (non-zero),
;  control exits the macro.  If the result is FALSE (zero),
;  the loop repeats.
;
;  This macro destroys the W register.  It does not modify addr.
;


waituntil	macro	addr,andl,xorl
waitw#v(_waitk)
	movf	addr,w
	andlw	andl
	if	xorl != 0
	xorlw	xorl
	endif
	beq	waitw#v(_waitk)
_waitk	set	_waitk+1
	endm


;
;  POLL (starts a POLL-ENDPOLL structure)
;
;  Syntax:
;
;  		poll	addr,andl,xorl
;
;  The POLL macro reads the byte in address addr, ANDs it with
;  the literal andl, then XORs the result with the literal xorl.
;  If the result is TRUE (non-zero), control passes to the code
;  immediately following the macro.  If the result is FALSE
;  (zero), control jumps to the corresponding ENDPOLL macro.
;
;  For example, the following POLL command will test the address
;  SPORT for bit 3 high:
;
;	poll	SPORT,8,0		test bit 3
;	nop				do this if bit 3 is high
;	endpoll
;
;  The following POLL command will test the address SPORT for
;  bit 3 high while either bits 2 or 1 are low:
;
;	poll	SPORT,0x0e,0x06		test bits 1-3
;	nop				do if 3 is high, 1 or 2 is low
;	endpoll
;
;  This macro destroys the W register.  It does not modify addr.
;


poll	macro	addr,andl,xorl
_pollt	set	_pollt+1
_pollk	set	_pollt
	movf	addr,w
	andlw	andl
	xorlw	xorl
	beq	poll#v(_pollk)
	endm


;
;  ENDPOLL (marks end of a POLL-ENDPOLL structure)
;
;  The ENDPOLL macro terminates a POLL-ENDPOLL structure.
;  Control reaches this macro if the associated POLL macro
;  fails.
;

endpoll	macro
poll#v(_pollk)
_pollk	set	_pollk-1
	endm