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