#define bf_carry 3, 0 #define bf_zero 3, 2 #define same 1 #define wreg 0 #define stc bsf bf_carry #define clc bcf bf_carry ;-[ Div ]-------------------------------------------------------------- ; Call w/: Number in f_divhi:f_divlo, divisor in W. ; Returns: Quotient in f_divlo, remainder in f_divhi. W preserved. ; Carry set if error. Z if divide by zero, NZ if divide overflow. ; Notes: Works by left shifted subtraction. ; Size = 29, Speed(w/ call&ret) = 7 cycles if div by zero ; Speed = 94 minimum, 129 maximum cycles Div; addlw 0 ; w+=0 (to test for div by zero) stc ; set carry in case of error btfsc bf_zero ; if zero return ; return (error C,Z) call DivSkipHiShift iDivRepeat = 8 while iDivRepeat call DivCode iDivRepeat-- endw rlf f_divlo, same ; C << lo << C ; If the first subtract didn't underflow, and the carry was shifted ; into the quotient, then it will be shifted back off the end by this ; last RLF. This will automatically raise carry to indicate an error. ; The divide will be accurate to quotients of 9-bits, but past that ; the quotient and remainder will be bogus and carry will be set. bcf bf_zero ; NZ (in case of overflow error) return ; we are done! DivCode rlf f_divlo, same ; C << lo << C rlf f_divhi, same ; C << hi << C btfss bf_carry ; if Carry goto DivSkipHiShift ; subwf f_divhi, same ; hi-=w stc ; ignore carry return ; done ; endif DivSkipHiShift subwf f_divhi, same ; hi-=w btfsc bf_carry ; if carry set return ; done addwf f_divhi, same ; hi+=w clc ; clear carry return ; done
Questions:
hi,James Newton of Massmind replies: At that point in the code, we know we are not dividing by zero; we already tested for that withbtfsc bf_zero ; if zero. Clearing bf_zero after we detect an overflow, just makes sure the routine does not send an ambiguous error message. At this point, we might have an overflow (see the previous lines) but we certainly do not have a divide by zero error.
only one comment i suppose i don´t understand
this instruction:
bcf bf_zero ; NZ (in case of overflow error)
Always clear FLAG Z --> so always indicates an overflow error ?
Comments:
Code:
The next function divides a 16-bit number in MODREG1:MODREG0 by any N<=16 bit number in TEMP1:TEMP0. The quotient will be stored in DIVREG1:DIVREG:0 and the modulo in MODREG1:MODREG0. (in this notation, MODREG1 is th HI byte, etc.) It is optimized for dividing numbers by a constant. Restraints: - The divisor in TEMP1:TEMP0 should be left-aligned (eg, padded with zeroes on the LSB side). This is easy if you divide by 8-bits. - The high bit of the divisor (TEMP1.7) should be 1. If the high bit of the divisor is not 1 then the result may overflow. This will cause the (17-N)-bit quotient in DIVREG1:DIVREG0 to contain all ones and can be further detected by comparing MODREG1:MODREG0 by the used divisor (which was btw destroyed by this function). Bottomline, it is better to avoid this. - No detection for a divisor of zero (which would mean a divisor of 0 bits long which is nonsense) Worst case timing: 6+(17-N)*23 (where N is the number of bits in the divisor) N=4: T=305 N=8: T=213 N=12: T=121 Here's the universal version: ;;DIVIDE A 16-BIT NUMBER BY AN N-BIT NUMBER ;;IN: ;; MODREG1:MODREG0 = 16-BIT DIVIDEND ;; TEMP1:TEMP0 = N-BIT DIVISOR, LEFT ALIGNED, WITH TEMP1.7 = 1 ;;OUT: ;; DIVREG1:DIVREG0 = (17-N)-BIT WIDE QUOTIENT ;; MODREG1:MODREG0 = N-BIT REMAINDER ;;DESTROYED: ;; COUNT = 0 ;; TEMO1:TEMP0 = ORIGINAL TEMO1:TEMP0 << (17-N) ;; THAT MEANS TEMO1:TEMP0 = 0 IF INPUT IS CORRECT DIV16 MOVLW 17-N MOVWF COUNT CLRF DIVREG0 CLRF DIVREG1 DIV16_LOOP MOVF TEMP0,W ;W=MOD-DIVISOR SUBWF MODREG0,W MOVF TEMP1,W BTFSS STATUS,C ;PROCESS BORROW ADDLW 1 SUBWF MODREG1,W BTFSS STATUS,C ;IF W<0 GOTO DIV16_NOSUB MOVF TEMP0,W ;MOD=MOD-DIVISOR SUBWF MODREG0,F BTFSS STATUS,C DECF MODREG1,F MOVF TEMP1,W SUBWF MODREG1,F BSF STATUS,C DIV16_NOSUB RLF DIVREG0,F ;DIV << 1 + CARRY RLF DIVREG1,F BCF STATUS,C ;DIVISOR>>=1 RRF TEMP1,F RRF TEMP0,F DECFSZ COUNT,F GOTO DIV16_LOOP RETURN In the next example, it has been configured to divide a 16-bit number by a 6-bit number in WREG. It will put it in TEMP1 (and clear TEMP0) and shift it to the left by 2 bits to align it left. The result is 11-bits wide in DIVREG1:DIVREG0 and the remainder is of course 6-bits wide (same width as the divisor) and still occupies MODREG1:MODREG0. ;;DIVIDE A 16-BIT NUMBER BY A 6-BIT WREG. RESULT IS 11-BIT WIDE DIV16_6 ;SHIFT LEFT BY 10 BITS MOVWF TEMP1 CLRF TEMP0 BCF STATUS,C RLF TEMP1,F RLF TEMP1,F MOVLW 11 MOVWF COUNT CLRF DIVREG0 CLRF DIVREG1 DIV16_6_LOOP MOVF TEMP0,W ;W=MOD-DIVISOR SUBWF MODREG0,W MOVF TEMP1,W BTFSS STATUS,C ;PROCESS BORROW ADDLW 1 SUBWF MODREG1,W BTFSS STATUS,C ;IF W<0 GOTO DIV16_6_NOSUB MOVF TEMP0,W ;MOD=MOD-DIVISOR SUBWF MODREG0,F BTFSS STATUS,C DECF MODREG1,F MOVF TEMP1,W SUBWF MODREG1,F BSF STATUS,C DIV16_6_NOSUB RLF DIVREG0,F ;DIV << 1 + CARRY RLF DIVREG1,F BCF STATUS,C ;DIVISOR>>=1 RRF TEMP1,F RRF TEMP0,F DECFSZ COUNT,F GOTO DIV16_6_LOOP RETURN