PICMicrocontollerBasicMathMethod

32-bit signed integer stack based math routines. add, subtract, multiply, divide, by Alan Cashin

I am writing a program for a PIC16F628, needed to do arithmetic. I adapted PETER HEMSLEY SIGNED 32-BIT INTEGER MATHS to run a stack for things like (A-B)/(C-D). I wondered if there would be interest in this and where I'd post the routines. An example straight from my code (with extraneous bits removed): {Ed: quite useful for FORTH implementations)

```    MOVLW   Vfast       ; push Vfast
CALL    Push4
MOVLW   Vslow       ; push Vslow
CALL    Push4
CALL    subtract    ; Vfast - Vslow
MOVLW   Cfast       ; push Cfast
CALL    Push3
MOVLW   Cslow       ; push Cslow
CALL    Push3
CALL    subtract    ; Cfast - Cslow
CALL    divide      ; get final result (slope)
CALL    round
MOVLW   Slope   ; pop result
MOVWF   FSR
MOVLW   4       ; 4 byte value returned
CALL    Mpop
```

I'm not very familiar with the MPLAB X environment, so I'm not sure if there are conventions I didn't follow. I think there's enough comments in the code for people to cannibalise it for their own use.

There's a few of Peter Hemsley's routines not included as I was only interested in +-*/ - probably not too hard to add if someone needs them.

Because all pops and pushes work through the FSR/INDF method, the variables can be in either bank0 or bank1. I toyed with the idea of putting the stack in bank2, but it was extra complexity I didn't need, as there was enough space in 0,1 for my needs.

Also the usual caveat, tested for most things but not guaranteed to be bulletproof, and probably not very efficient.

Source code:

```#INCLUDE P16F628A.inc
;
; Maths testing
;
__CONFIG    _WDT_OFF & _PWRTE_ON & _LVP_OFF
;
; WDT (Watch Dog Timer) disabled
; PWRT (Power on Timer) enabled
; Low Voltage Programming disabled :-
; RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming
;
; Multibyte numbers are stored little endian
;

;Variable declarations

dbank0  udata 0x20

Lit666		RES	2
Lit334		RES	2
Lit400		RES	2
WORKB		RES	3
WORKC		RES	3

dbank1	udata	0xA0
;
; Maths work area all in Bank 1
;
; Note that for the push and pop routines, the limit conditions expect the
; stack to be at address 0xA0 and be 32 bytes long. If a different address
; or size is desired, the limit conditions need changing
;
StackAddr	EQU	0xA0	; the EQUs to define REGA/B/D would
; not accept STACK, so explicitly define the address again
; so it was defined again. Not neat but works.
StackSiz	EQU	8	; Stack Size in 32bit numbers (see above)

STACK		RES	4*StackSiz
;
; The stack can be considered as an array of StackSiz columns of 4 rows.
; numbers are stored in columns - the bytes are StackSiz apart, not adjacent.
; The following EQUs are used to make the code more readable

; REGB is the 'top' of the stack, REGA is the 'first down'. Arithmetic
; is performed on REGA and REGB producing a result in REGA. Usually, each function
; pops the stack so the result overwrites REGB (top of stack).
; The exception is division, where REGA has the dividend and REGB
; the remainder. The caller can either pop both results or call the round
; function. The round function will round REGA then pop to get the rounded
; result at top af stack.
;

REGA0		EQU	StackAddr+1		; lsb
REGA1		EQU	StackAddr+(StackSiz + 1)
REGA2		EQU	StackAddr+(StackSiz * 2 + 1)
REGA3		EQU	StackAddr+(StackSiz * 3 + 1)

REGB0		EQU	StackAddr		; lsb
REGB1		EQU	StackAddr+StackSiz
REGB2		EQU	StackAddr+StackSiz * 2
REGB3		EQU	StackAddr+StackSiz * 3

; REGD is the destination BEFORE push, ends up in REGB after push
REGD0		EQU	StackAddr+(StackSiz * 4 - 1)
REGD1		EQU	StackAddr+(StackSiz - 1)
REGD2		EQU	StackAddr+(StackSiz * 2 - 1)
REGD3		EQU	StackAddr+(StackSiz * 3 - 1)

REGC0		RES	1	; lsb
REGC1		RES	1	; REGC used in multiply and divide. It
REGC2		RES	1	; holds the divisor between divide and round
REGC3		RES	1

MTEMP		RES	1	; work area
MCOUNT		RES	1	;  "    "
LitVal		RES	2	; a space for +ve constants used in arithmetic

ORG	0x000	; a reset redirects program to this point

GOTO	MAIN

;
ORG	0x004	; an interrupt redirects the program to here

;###########################################################

MAIN:
; compute (666+334)/(400-199)
; set up some values
MOVLW   0x02 ; = 666
MOVWF   Lit666+1
MOVLW   0x9A
MOVWF   Lit666
MOVLW   0x01 ; = 334
MOVWF   Lit334+1
MOVLW   0x4E
MOVWF   Lit334
MOVLW   0x01 ; = 400
MOVWF   Lit400+1
MOVLW   0x90 ; a value for workA
MOVWF   Lit400
;
BSF	STATUS,RP0	; bank 1 for Arithmetic
;
MOVLW   Lit666   ; push 666
call	Push2
;
MOVLW   Lit334   ; push 334
call	Push2
;
call	add
;
MOVLW   Lit400   ; push 400
call	Push2
;
MOVLW   d'199'  ; push 199
call	PushLit
;
call	subtract
;
call	divide
;
MOVLW	WORKB   ; pop remainder in WORKB
call	Pop3
MOVLW	WORKB   ; and push it back
call	Push3
;
call	round
;
MOVLW   WORKC	; pop result into workC
call	Pop3
BCF	STATUS,RP0	; bank 0 after arithmetic
;
; spin here
;
GOTO	\$

;##################### start math routines ##################

errorlevel -302 ; Turn off banking message
; known tested (good) code
;
; Push entry points for various size variables.
; Push 1 to 4 expect signed variables
;
PushLit:
MOVWF	LitVal	; entry to push 1 byte unsigned constants
CLRF	LitVal+1; have to make them 2 byte otherwise
MOVLW	LitVal	; values over 127 would be interpreted
MOVWF	FSR	; as -ve
MOVLW	2
GOTO	Mpush

Push4:
MOVWF	FSR ; entry to push a 4 byte variable
MOVLW	4
GOTO	Mpush

Push3:
MOVWF	FSR ; entry to push a 3 byte variable
MOVLW	3
GOTO	Mpush

Push2:
MOVWF	FSR ; entry to push a 2 byte variable
MOVLW	2
GOTO	Mpush

Push1:
MOVWF	FSR ; entry to push a 1 byte variable
MOVLW	1

Mpush:
;
; Transfers a new value to the stack with sign extension if required.
; The stack is barrel rolled 1 byte so the new value appears at REGB.
; The location of the value to push is held in FSR, the length specified
; by the least significant 4 bits of W. The remaining bits of W are
; unused but may have a future use so they are ANDed off.
;
; first, store passed info in the stack where the data is about
; to be 'pushed out'. It will be pushed to the location of REGB.
;
ANDLW	0x0F		; bytes to move
MOVWF	MCOUNT
DECF	FSR,F		; move the pointer from the first byte
ADDWF	FSR,F		; to the msb (the byte with the sign)
MOVF	INDF,W  ; get msb
ANDLW	0x80		; look at sign bit
BTFSS	STATUS,Z; skip if +ve (W=0)
MOVLW	0xFF
MOVWF	REGD0		; fill the destination with sign bits
MOVWF	REGD1		; except the MSB which will be always be
MOVWF	REGD2		; overwritten
MUnext:
MOVF	REGD2,W  	; shift destination register up one byte
MOVWF	REGD3
MOVF	REGD1,W
MOVWF	REGD2
MOVF	REGD0,W
MOVWF	REGD1
MOVF	INDF,W  	; get a byte of the incoming number
MOVWF	REGD0 	; into LSB
DECF	FSR,F		; repeat until all bytes moved
DECFSZ  MCOUNT,F
GOTO	MUnext
;
; roll the stack
;
MOVLW	STACK+1
MOVWF	FSR
MOVF	STACK,W ; get the first byte of stack
MPu2:
XORWF	INDF,W 	; 3 XORs swaps W and F
XORWF	INDF,F	; Each byte in STACK is moved up 1 byte
XORWF	INDF,W	; in memory, effectively moving each 4 byte
INCF	FSR,F	; value to the next column
BTFSC	FSR,5	; FSR ok from A1 to BF, leave when C0
GOTO	MPu2
MOVWF	STACK	; save what was the last byte of stack in first byte
; all done
RETURN
;
; Pop entry points for 3 or 4 byte variables
; 3 byte values are truncated with no testing
;
Pop4:
MOVWF	FSR
MOVLW	4	; 4 byte value returned
GOTO	Mpop
Pop3:
MOVWF	FSR
MOVLW	3	; 3 byte value returned

Mpop:
;
; transfers the value in REGB (top of stack) to a destination.
; The destination of the value to pop is held in FSR,
; the length specified by the least significant 4 bits of W.
; if length is less than 4, the most significant byte(s) is
; truncated. The stack is then popped (moved down 1 byte).
;
ANDLW	0x0F		; bytes to move
MOVWF	MCOUNT
MOnext:
MOVF	STACK,W	; the next byte to move
MOVWF	INDF	; into a destination byte
MOVF	REGB1,W	; Shift REGB down one byte
MOVWF	REGB0
MOVF	REGB2,W
MOVWF	REGB1
MOVF	REGB3,W
MOVWF	REGB2
INCF	FSR,F	; pointer to next destination
DECFSZ  MCOUNT,F; all moved?
GOTO	MOnext

; value moved - pop the stack down

PopStk:

; this entry point also used by the arithmetic functions
; to move the result to the top of stack

MOVLW	STACK+(StackSiz*4-2)
MOVWF	FSR
MOVF	STACK+(StackSiz*4-1),W ; last byte of stack
MPd2:
XORWF	INDF,W	; The value of of each byte is moved
XORWF	INDF,F	; down 1 byte in memory, effectively
XORWF	INDF,W	; moving each value to the previous
DECF	FSR,F	; column
BTFSC	FSR,5	; FSR ok from BE to A0, leave when 9F
GOTO	MPd2
; all done
RETURN
;
; The following functions are based on:
;
;*** SIGNED 32-BIT INTEGER MATHS ROUTINES FOR PIC16 SERIES BY PETER HEMSLEY ***
;
;Functions:
;	add
;	subtract
;	multiply
;	divide
;	round
;
; These were NOT implemented: sqrt, bin2dec, dec2bin
;
; The original routines mostly used lower case for instructions. Additions or
; changes are mostly in upper case. Almost all the changes are to the division
; routine where the role of REGB and REGC are reversed so the reminder is
; left in REGB (if changing the register name was the only modification the
; instruction is still in lower case). The round routine was mostly rewritten
; to save duplicating existing code. Apart from divide, return is via
; PopStk. PopStk does not affect the state of the C flag.
;
; IMPORTANT: these routines assume RP0/1 are set to Bank 1 by the caller
;
;*** 32 BIT SIGNED SUBTRACT ***
;REGA - REGB -> REGA
;Return carry set if overflow

subtract
call	negateb		;Negate REGB
skpnc
GOTO	PopStk		;Overflow

;*** 32 BIT SIGNED ADD ***
;REGA + REGB -> REGA
;Return carry set if overflow

add
movf	REGA3,w		;Compare signs
xorwf	REGB3,w
movwf	MTEMP

call	addba		;Add REGB to REGA

clrc			;Check signs
movf	REGB3,w		;If signs are same
xorwf	REGA3,w		;so must result sign
btfss	MTEMP,7		;else overflow
addlw	0x80
GOTO	PopStk

;*** 32 BIT SIGNED MULTIPLY ***
;REGA * REGB -> REGA
;Return carry set if overflow

multiply
clrf	MTEMP		;Reset sign flag
call	absa		;Make REGA positive
skpc
call	absb		;Make REGB positive
skpnc
GOTO	PopStk	;Overflow

;Move REGA to REGC
;Used by multiply

movf	REGA0,w	; code variation: this was in a subroutine,
movwf	REGC0		; but was moved inline
movf	REGA1,w
movwf	REGC1
movf	REGA2,w
movwf	REGC2
movf	REGA3,w
movwf	REGC3

;Clear REGA
;Used by multiply

clrf	REGA0		;Clear product
clrf	REGA1		; code variation: this was in a subroutine,
clrf	REGA2		; but was moved inline
clrf	REGA3
movlw	D'31'		;Loop counter
movwf	MCOUNT

muloop
call	sla		;Shift left product and multiplicand
rlf	REGC0,f	; code variation: this was in a subroutine,
rlf	REGC1,f	; but was moved inline
rlf	REGC2,f
rlf	REGC3,f

rlf	REGC3,w		;Test MSB of multiplicand
skpnc			;If multiplicand bit is a 1 then
call	addba		;add multiplier to product

skpc			;Check for overflow
rlf	REGA3,w
skpnc
GOTO	PopStk

decfsz	MCOUNT,f	;Next
goto	muloop

btfsc	MTEMP,0		;Check result sign
call	negatea		;Negative
GOTO	PopStk

;*** 32 BIT SIGNED DIVIDE ***
;REGA / REGB -> REGA
;Remainder in REGB
;Return carry set if overflow or division by zero

divide
clrf	MTEMP		;Reset sign flag
call	absb		;Make divisor (REGB) positive
skpnc
return  			;Overflow
;
; modification - so the remainder ends up on the stack, REGB is moved to
; REGC. The use of REGB and REGC is the opposite of the original code but
; the logic remains the same
;
MOVF	REGB0,w		; Move REGB (divisor) to REGC at the
MOVWF	REGC0	; same time test for zero divisor
MOVF	REGB1,w
MOVWF	REGC1
IORWF	REGB0,f
MOVF	REGB2,w
MOVWF	REGC2
IORWF	REGB0,f
MOVF	REGB3,w
MOVWF	REGC3
IORWF	REGB0,w
;
sublw	0	; if all zero, will set C
skpc
call	absa		;Make dividend (REGA) positive
skpnc
return			;Overflow
; clear REGB to take the remainder
clrf	REGB0		;Clear remainder
clrf	REGB1
clrf	REGB2
clrf	REGB3
call	sla		;Purge sign bit

movlw	D'31'		;Loop counter
movwf	MCOUNT

dvloop
call	sla		;Shift dividend (REGA) msb into remainder (REGB)
CALL	SlbTst		; shifts and tests remainder > divisor
skpc			;Carry set if remainder >= divisor
goto	dremlt

movf	REGC0,w		;Subtract divisor (REGC) from remainder (REGB)
subwf	REGB0,f
movf	REGC1,w
skpc
incfsz	REGC1,w
subwf	REGB1,f
movf	REGC2,w
skpc
incfsz	REGC2,w
subwf	REGB2,f
movf	REGC3,w
skpc
incfsz	REGC3,w
subwf	REGB3,f
clrc
bsf	REGA0,0		;Set quotient bit

dremlt
decfsz	MCOUNT,f	;Next
goto	dvloop

btfsc	MTEMP,0		;Check result sign
call	negatea		;Negative
return

;*** ROUND RESULT OF DIVISION TO NEAREST INTEGER ***

round
; modified from original. Some code duplication was noticed so some was
; put in subroutine SlbTst and the IncA entry to negatea was added.
; No error testing, should not be capable of creating an error
clrf	MTEMP		;Reset sign flag
call	absa		;Make positive
clrc
CALL	SlbTst  	; shifts and tests remainder > divisor
CLRW			; prevent IncA from returning an error
BTFSC	STATUS,C	; Carry set if remainder >= divisor
CALL	IncA		; Increment REGA
btfsc	MTEMP,0		;Restore sign
call	negatea
GOTO	PopStk

;UTILITY ROUTINES

;Add REGB to REGA (Unsigned)
;Used by add, multiply,

addba	movf	REGB0,w		;Add lo byte
addwf	REGA0,f

movf	REGB1,w		;Add mid-lo byte
skpnc			;No carry_in, so just add
incfsz	REGB1,w		;Add carry_in to REGB
addwf	REGA1,f		;Add and propagate carry_out

movf	REGB2,w		;Add mid-hi byte
skpnc
incfsz	REGB2,w
addwf	REGA2,f

movf	REGB3,w		;Add hi byte
skpnc
incfsz	REGB3,w
addwf	REGA3,f
return

;Check sign of REGA and convert negative to positive
;Used by multiply, divide, round

absa	rlf	REGA3,w
skpc
return			;Positive

;Negate REGA
;Used by absa, multiply, divide, round

negatea	movf	REGA3,w		;Save sign in w
andlw	0x80

comf	REGA0,f		;2's complement
comf	REGA1,f
comf	REGA2,f
comf	REGA3,f
incf	MTEMP,f		;flip sign flag
IncA ; new entry point from round routine
incfsz	REGA0,f
goto	nega1
incfsz	REGA1,f
goto	nega1
incfsz	REGA2,f
goto	nega1
incf	REGA3,f
nega1
addwf	REGA3,w		;Return carry set if -2147483648
return

;Check sign of REGB and convert negative to positive
;Used by multiply, divide

absb	rlf	REGB3,w
skpc
return			;Positive

;Negate REGB
;Used by absb, subtract, multiply, divide

negateb	movf	REGB3,w		;Save sign in w
andlw	0x80

comf	REGB0,f		;2's complement
comf	REGB1,f
comf	REGB2,f
comf	REGB3,f
incfsz	REGB0,f
goto	negb1
incfsz	REGB1,f
goto	negb1
incfsz	REGB2,f
goto	negb1
incf	REGB3,f
negb1
incf	MTEMP,f		;flip sign flag
addwf	REGB3,w		;Return carry set if -2147483648
return

SlbTst:
;
; code modification: moved from divide, used by divide, round
; shifts remainder - when dividing, shifts in a bit from REGA;
; if rounding, a zero bit . Then tests remainder => divisor
;
rlf	REGB0,f ; shift
rlf	REGB1,f
rlf	REGB2,f
rlf	REGB3,f
movf	REGC3,w	; Test
subwf	REGB3,w
skpz
RETURN
movf	REGC2,w
subwf	REGB2,w
skpz
RETURN
movf	REGC1,w
subwf	REGB1,w
skpz
RETURN
movf	REGC0,w
subwf	REGB0,w
RETURN

;Shift left REGA
;Used by multiply, divide, round

sla	rlf	REGA0,f
rlf	REGA1,f
rlf	REGA2,f
rlf	REGA3,f
return

errorlevel +302 ; Enable banking message
; untested code
;##################### end math routines ##################

END

```

 file: /Techref/microchip/math/32bmathstack-cashin.htm, 15KB, , updated: 2014/4/9 15:38, local time: 2023/2/5 22:07, TOP NEW HELP FIND:  18.232.31.206:LOG IN

 ©2023 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?Please DO link to this page! Digg it! / MAKE! 32-bit signed integer stack based math routines. add, subtract, multiply, divide, by Alan Cashin

After you find an appropriate page, you are invited to your to this massmind site! (posts will be visible only to you before review) Just type a nice message (short messages are blocked as spam) in the box and press the Post button. (HTML welcomed, but not the <A tag: Instead, use the link box to link to another page. A tutorial is available Members can login to post directly, become page editors, and be credited for their posts.

Link? Put it here:
if you want a response, please enter your email address:
Attn spammers: All posts are reviewed before being made visible to anyone other than the poster.
 Did you find what you needed? "No. I'm looking for: " "No. Take me to the search page." "No. Take me to the top so I can drill down by catagory" "No. I'm willing to pay for help, please refer me to a qualified consultant"

Welcome to sxlist.com!

Site supported by
sales, advertizing,
& kind contributors
just like you!

Please don't rip/copy
(here's why

Copies of the site on CD
are available at minimal cost.

.