;***************************************************************************
;
;	16-bit Arithmetic Routines  V1.01
;	=================================
;
;	written by Peter Luethi, 2010/05/03, Switzerland
;	http://www.electronic-engineering.ch
;	last update: 2010/05/14
;
;	This code and accompanying files may be distributed freely and
;	modified, provided this header with my name and this notice remain
;	intact. Ownership rights remain with me.
;	You may not sell this software without my approval.
;
;	This software comes with no guarantee or warranty except for my
;	good intentions. By using this code you agree to indemnify me from
;	any liability that might arise from its use.
;
;
;	SPECIFICATIONS
;	==============
;	Processor:			any 8-bit Microchip PIC controller
;
;
;	DESCRIPTION
;	===========
;	Provides basic 16-bit arithmetic routines:
;		CLR16	DST
;		INIT16	DST, VAL
;		MOV16	DST, SRC
;		MOVI16	DST, SRC
;		NEG16	DST
;		INC16	DST
;		DEC16	DST
;		LSR16	DST
;		LSL16	DST
;		SUB16	DST, SUB	(handles carry bit correctly)
;		SUBI16	DST, SUB	(handles carry bit correctly)
;		ADD16	DST, SRC
;		ADDI16	DST, SRC
;		CMP16	X, Y		(handles zero and carry bits correctly)
;		CMPI16	X, Y		(handles zero and carry bits correctly)
;		TSTF16	X			(handles zero bit correctly)
;
;
;	DECLARATIONS needed in MAIN PROGRAM
;	===================================
;	-
;
;
;	LIMITATIONS
;	===========
;	Only for little-endian systems, i.e, &HI = &LO+1
;	Thus ensure, high byte is located directly above low byte, e.g.
;		LO_val	equ	BASE+0x0
;		HI_val	equ	BASE+0x1
;
;
;	REQUIRED MEMORY
;	===============
;	-
;
;
;	CREDITS
;	=======
;	Chuck McManis (http://www.mcmanis.com/chuck)
;	for his 16 bit arithmetic routines (16bits.inc) at
;	http://www.mcmanis.com/chuck/Robotics/pic_index.html
;
;
;	HISTORY
;	=======
;	2010/05/03	1.00  Initial release based on code of Chuck McManis
;	2010/05/14	1.01  Added tstf16, similar to 8-bit variant tstf
;
;***************************************************************************
#DEFINE	M_16BIT_ID dummy

;***** INCLUDE FILES *****

;***** CONSTANT DECLARATION *****
			
;***** REGISTER DECLARATION *****

;***** MACROS *****

; 16 bit clear (set to zero)
CLR16 macro DST
	clrf	DST
	clrf	DST+1
	endm


; 16 bit initialize
INIT16 macro DST, VAL
	IF (VAL == 0)
	CLR16	DST				; use clr macro
	ELSE
	movlw	low VAL
	movwf	DST
	movlw	high VAL
	movwf	DST+1
	ENDIF
	endm


; 16 bit move from SRC to DST
; DST <- SRC
MOV16 macro DST, SRC
	movfw	SRC
	movwf	DST
	movfw	SRC+1
	movwf	DST+1
	endm

MOVI16 macro DST, SRC
	INIT16	DST, SRC
	endm


; negate 16 bit value (two's complement)
NEG16 macro DST
	comf	DST,f			; complement of low byte
	comf	DST+1,f			; complement of high byte
	INC16	DST
	endm


; increment 16 bit value, including proper zero flag handling
INC16 macro DST
	incfsz	DST,w			; increment low byte
	decf	DST+1,f			; no carry (negates next step in case of no carry)
	incf	DST+1,f			; increment high byte
	movwf	DST				; store updated low byte
	iorwf	DST+1,w			; update zero flag accordingly
	endm


; decrement 16 bit value, including proper zero flag handling
DEC16 macro DST
	decf	DST,f			; decrement low byte
	incfsz	DST,w			; check for underflow
	incf	DST+1,f			; adjust (negates next step in case of no carry)
	decf	DST+1,f			; update
	movfw	DST
	iorwf	DST+1,w			; update zero flag accordingly
	endm


; 16 bit logical shift right
LSR16 macro DST
	bcf		STATUS,C		; clear carry
	rrf		DST+1,f			; rotate right high byte
	rrf		DST,f			; rotate right low byte
	endm


; 16 bit logical shift left
LSL16 macro DST
	bcf		STATUS,C		; clear carry
	rlf		DST,f			; rotate left low byte
	rlf		DST+1,f			; rotate left high byte
	endm


; 16 bit unsigned subtraction
; DST = DST - SUB
; DST is replaced, SUB is preserved, carry bit is set correctly
; X = X - Y:  C = 1: X >= Y
;             C = 0: X < Y
SUB16 macro DST, SUB		; DST & SUB are 16 bit register locations
	movfw	SUB				; get low byte of subtrahend
	subwf	DST,f			; compute low byte (result = f - w)
	movfw	SUB+1			; get high byte of subtrahend
	skpc					; if carry, result is zero or positive
	incfsz	SUB+1,w			; if no carry, increment high byte of subtrahend
	subwf	DST+1,f			; compute high byte (result = f - w)
	endm

; Note: carry bit is only handled if SUB > 0
SUBI16 macro DST, SUB		; DST is 16 bit register location, SUB is literal
	IF (SUB > 0)
	movlw	low SUB			; get low byte of subtrahend
	subwf	DST,f			; compute low byte (result = f - w)
	movlw	high SUB		; get high byte of subtrahend
	skpc					; if carry, result is zero or positive
	movlw	(high SUB)+1	; if no carry, increment high byte of subtrahend
	subwf	DST+1,f			; compute high byte (result = f - w)
	ENDIF
	endm


; 16 bit unsigned addition
; DST = DST + SRC
; DST is replaced, SRC is preserved, carry bit is set correctly
ADD16 macro DST, SRC
	movfw	SRC				; get low byte
	addwf	DST,f			; compute low byte (result = f + w)
	movfw	SRC+1			; get high byte
	addcf	SRC+1,w			; add carry, if existing
	addwf	DST+1,f			; compute high byte (result = f + w)
	endm

; Note: carry bit is only handled if SRC > 0
ADDI16 macro DST, SRC
	IF (SRC > 0)
	movlw	low SRC			; get low byte
	addwf	DST,f			; compute low byte (result = f + w)
	movlw	high SRC		; get high byte
	skpnc
	movlw	(high SRC) + 1	; get high byte + 1
	addwf	DST+1,f			; compute high byte (result = f + w)
	ENDIF
	endm


; compare two 16-bit values, including proper flag handling
; cmp(X,Y):  Z = 1: X == Y
;            C = 1: X >= Y
;            C = 0: X < Y
CMP16 macro X, Y
	local	_CMP_DONE
	movfw	Y+1				; get high byte
	subwf	X+1,w			; compare high bytes
	bnz		_CMP_DONE		; if not zero, we're done
	movfw	Y				; get low byte
	subwf	X,w				; compare low byte
_CMP_DONE					; flags are now all set correctly (C & Z)
	endm

CMPI16 macro X, Y
	local	_CMP_DONE
	movlw	high Y			; get high byte
	subwf	X+1,w			; compare high bytes
	bnz		_CMP_DONE		; if not zero, we're done
	movlw	low Y			; get low byte
	subwf	X,w				; compare low byte
_CMP_DONE					; flags are now all set correctly (C & Z)
	endm

; test contents of 16-bit registers, similar to 8-bit variant tstf
; tstf16 X:  Z = 1: X == 0
TSTF16 macro X
	local	_CMP_DONE
	tstf	X+1				; check high byte
	bnz		_CMP_DONE		; if not zero, we're already done
	tstf	X				; check low byte
_CMP_DONE					; zero flag is set correctly (Z)
	endm

