from Nikolai Golovchenko
;Division of 16bit by 8 bit with a result in Q16.16 form ; ; X_Int.X_Frac = X_Int / Y ; ; RAM - 6 bytes (1 temp): ; X_Int = X_IntH:X_IntL 16 bit input/ output integer part ; X_Frac = X_FracH:X_FracL 16 bit output fractional part ; Y divisor / temporary ; Counter counter ; ; Size = 39 instructions ; Execution time = 6+16*14-1+3+16*14-1+3+2(return) ; = 460 instruction cycles ; ; 8-July-2000 by Nikolai Golovchenko ; 16-February-2001 fixed, reduced execution time and temporaries Div16by8to16_16 clrf X_FracL clrf X_FracH movlw 16 movwf Counter movf Y, w ;keep Y value in accumulator clrf Y ;and use Y register as temporary ;Find integer part Div16by8to16_16a rlf X_IntL, f ;shift next msb into temporary rlf X_IntH, f rlf Y, f rlf Counter, f ;carry has 9th bit of temporary ;copy carry to counter subwf Y, f ;substract Y (in w) from temporary skpnc ;if no borrow, set Counter.0 bsf Counter, 0 btfss Counter, 0 ;if Counter.0 clear (borrow) restore temporary addwf Y, f clrc ;restore counter rrf Counter, f ;at this point carry is the next bit of result decfsz Counter, f ;repeat 16 times to find integer part goto Div16by8to16_16a ;shift last integer bit rlf X_IntL, f rlf X_IntH, f ;Find fractional part bsf Counter, 4 ;Counter = 16 Div16by8to16_16b ;Shift zero bit into temporary rlf X_FracL, f rlf X_FracH, f rlf Y, f rlf Counter, f ;carry has 9th bit of temporary ;copy carry to counter subwf Y, f ;substract Y(in w) from temporary skpnc ;if no borrow, set Counter.0 bsf Counter, 0 btfss Counter, 0 ;if Counter.0 clear (borrow) restore temporary addwf Y, f clrc ;restore counter rrf Counter, f decfsz Counter, f ;repeat 16 times goto Div16by8to16_16b ;shift last fractional bit rlf X_FracL, f rlf X_FracH, f movwf Y ;restore divisor return
Questions:
This code did not work as expected. I send you the whole code.list p=16F84A ; list directive to define processor #include <p16F84a.inc> ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_OFF & _XT_OSC ;***** VARIABLE DEFINITIONS ;w_temp EQU 0x0C ; variable used for context saving ;status_temp EQU 0x0D ; variable used for context saving ;********************************************************************** cblock 0x0C w_temp status_temp X_IntH X_IntL X_FracH X_FracL Counter Y endc ;********************************************************************** RESET_VECTOR CODE 0x0000 ; processor reset vector goto Start ; go to beginning of program ISR CODE 0x0004 ; interrupt vector location Interrupt: movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register movwf status_temp ; save off contents of STATUS register ; Place ISR Here movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt MAIN_PROGRAM CODE Start: ; Loading the divident and the divisor movlw .100 movwf X_IntL movlw .34 movwf Y Main Div16by8to16_16 clrf X_FracL clrf X_FracH movlw 16 movwf Counter movf Y, w ;keep Y value in accumulator clrf Y ;and use Y register as temporary ;Find integer part Div16by8to16_16a rlf X_IntL, f ;shift next msb into temporary rlf X_IntH, f rlf Y, f rlf Counter, f ;carry has 9th bit of temporary ;copy carry to counter subwf Y, f ;substract Y (in w) from temporary skpnc ;if no borrow, set Counter.0 bsf Counter, 0 btfss Counter, 0 ;if Counter.0 clear (borrow) restore temporary addwf Y, f clrc ;restore counter rrf Counter, f ;at this point carry is the next bit of result decfsz Counter, f ;repeat 16 times to find integer part goto Div16by8to16_16a ;shift last integer bit rlf X_IntL, f rlf X_IntH, f ;goto $ ;Find fractional part bsf Counter, 4 ;Counter = 16 Div16by8to16_16b ;Shift zero bit into temporary rlf X_FracL, f rlf X_FracH, f rlf Y, f rlf Counter, f ;carry has 9th bit of temporary ;copy carry to counter subwf Y, f ;substract Y(in w) from temporary skpnc ;if no borrow, set Counter.0 bsf Counter, 0 btfss Counter, 0 ;if Counter.0 clear (borrow) restore temporary addwf Y, f clrc ;restore counter rrf Counter, f decfsz Counter, f ;repeat 16 times goto Div16by8to16_16b ;shift last fractional bit rlf X_FracL, f rlf X_FracH, f movwf Y ;restore divisor ;return goto $ END ; directive 'end of program'
I've tried the 16bits by 8 bits with Q16.16 divide code running base on 16F873. The Integer seems to be ok however what answer should i expect from the fractional part. Is it a fractinal value or remainder
Nikolai Golovchenko answers:
You should expect the fractional part to be just that - the fractional part, it is not a remainder. This is a binary representation of fixed point numbers, where the highest bit (bit15) weight equals 1/2, bit14 = 1/4, bit13 = 1/8, ..., bit0 = 1/65536. A few examples: .0000 0000 0000 0000b = 0d .0000 0000 0000 0001b = 1/65536d ~= 0.00001526d .0000 0000 0000 0010b = 1/32768d ~= 0.00003052d .0000 0000 0000 0011b = 1/65536d + 1/32768d ~= 0.00004578d .0000 0000 0000 0100b = 1/16384d ... .1111 1111 1111 1111b = 1/65536d + 1/32768d + ... + 1/2d = = 65535 / 65536 ~= 0.9999847d You can convert it to decimal by simply dividing X_Frac by 65536. The full division result is: X_Int + X_Frac/65536 For example: X_Int = 103 Y = 2 Answer (X_Int/Y): X_Int = 51, X_Frac=32768. In binary: 0000 0000 0011 0011 . 1000 0000 0000 0000 \_____ X_Int______/ \_____ X_Frac ____/ Or, 32+16+2+1+1/2=51.5