Hex to BCD conversion routine

Thread Starter

RG23

Joined Dec 6, 2010
304
@Mark

I tried replacing ACC1 and ACC0 with X_FracH and X_FracL respectively in the code which you pasted before three posts.

But still not fixed the problem

If you have any idea please let me know

Thanks
 

beenthere

Joined Apr 20, 2004
15,819
Try some paper translations. You will see that the translation of .1h to .1d (in effect 1/16 to 1/10) does not proceed like integer values to integers. Eventually, .F (15/16) gets pretty strange. It's less than 1h but more than 1d.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
Is there any subroutine available in pic that takes fractional hex for eg '0.DB' as input and convert into decimal (0.85)?????
 

MrChips

Joined Oct 2, 2009
34,897
You keep asking the same question. The whole philosophy is wrong.
Hexadecimal is an illusion. It is all in your mind. All data on the computer is binary.

Someone already posted a solution.

The easiest thing to do is decide how many decimal places you wish to display.
If you want to show two decimal places, then begin with values that are multiplied by 100. After doing the math just insert the decimal place in the correct place. Just remember to account for rounding errors.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
; ACC = ACC * 0.152588
; Temp = TEMP
; ACC size = 16 bits
; Error = 0.1 %
; Bytes order = big endian
; Round = no

; ALGORITHM:
; Clear accumulator
; Add input / 8 to accumulator
; Add input / 32 to accumulator
; Substract input / 256 from accumulator
; Add input / 4096 to accumulator
; Move accumulator to result
;
; Approximated constant: 0.152588, Error: 4.096e-007 %

; Input: ACC0 .. ACC1, 16 bits
; Output: ACC0 .. ACC1, 14 bits
; Code size: 54 instructions

cblock
ACC0
ACC1
TEMP0
TEMP1
endc

;copy accumulator to temporary
movf ACC0, w
movwf TEMP0
movf ACC1, w
movwf TEMP1


;shift accumulator right 4 times
swapf ACC1, w
andlw 0x0F
movwf ACC1
swapf ACC0, w
movwf ACC0
andlw 0xF0
iorwf ACC1, f
xorwf ACC0, f

;substract temporary from accumulator
movf TEMP1, w
subwf ACC1, f
movf TEMP0, w
skpc
incfsz TEMP0, w
subwf ACC0, f

;shift accumulator right 3 times
rrf ACC0, f
movlw 0x80
xorwf ACC0, f ;invert bit shifted from carry
rrf ACC1, f
rlf ACC0, w
rrf ACC0, f
rrf ACC1, f
rlf ACC0, w
rrf ACC0, f
rrf ACC1, f

;add temporary to accumulator
movf TEMP1, w
addwf ACC1, f
movf TEMP0, w
skpnc
incfsz TEMP0, w
addwf ACC0, f

;shift accumulator right 2 times
clrc
rrf ACC0, f
rrf ACC1, f
clrc
rrf ACC0, f
rrf ACC1, f

;add temporary to accumulator
movf TEMP1, w
addwf ACC1, f
movf TEMP0, w
skpnc
incfsz TEMP0, w
addwf ACC0, f

;shift accumulator right 3 times
rrf ACC0, f
rrf ACC1, f
clrc
rrf ACC0, f
rrf ACC1, f
clrc
rrf ACC0, f
rrf ACC1, f
 

Thread Starter

RG23

Joined Dec 6, 2010
304
@Mark

You posted the above code earlier

In that I just replaced ACC0 with X_FracH and ACC1 with X_FracL

Is it the correct approach?

Please let me know
 

Thread Starter

RG23

Joined Dec 6, 2010
304
movlw h'07'
movwf count209
movlw h'00'
movwf X_IntH
movlw h'99'
movwf X_IntL

Div16by8to16_16
clrf X_FracL
clrf X_FracH
movlw 0x10
movwf Counter
movf count209, 0 ;keep Y value in accumulator
clrf count209 ;and use Y register as temporary
;Find integer part
Div16by8to16_16a
rlf X_IntL, 1 ;shift next msb into temporary
rlf X_IntH, 1
rlf count209, 1
rlf Counter, 1 ;carry has 9th bit of temporary
;copy carry to counter
subwf count209, 1 ;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 count209, 1
bcf CARRY ;restore counter
rrf Counter, 1
;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, 1
rlf X_IntH, 1
;Find fractional part
bsf Counter, 4 ;Counter = 16
Div16by8to16_16b
;Shift zero bit into temporary
rlf X_FracL, 1
rlf X_FracH, 1
rlf count209, 1
rlf Counter, 1 ;carry has 9th bit of temporary
;copy carry to counter
subwf count209, 1 ;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 count209, 1
bcf CARRY ;restore counter

rrf Counter, 1
decfsz Counter, 1 ;repeat 16 times
goto Div16by8to16_16b
;shift last fractional bit
rlf X_FracL, 1
rlf X_FracH, 1
movwf count209 ;restore divisor
movlw 0xC4 ;return LCD to Line 2 Char 5
movwf PORTD
call SND_CMD ;;;subroutine to bring the cursor to a particular position on display

movf X_IntL,0
call bcd ;;;;bcd subroutine converts to ASCII format
movlw 0x2E
movwf PORTD
call SND_DTA ;;;;;subroutine to display character
movf X_FracH,0
call bcd
 

Thread Starter

RG23

Joined Dec 6, 2010
304
I pasted my code above

Divisor is count209---- h'07'

Dividend is h'99'

Result I see on display is 15.DB correctly in hex format

But the desired result in decimal is 21.85

I implemented a hex to bcd subroutine which works fine only for integer part

that is, I am getting the result as 21.DB

I apologize I am still not clear on this one

If you have any idea please let me know

Thanks
 

MrChips

Joined Oct 2, 2009
34,897
If you insist on wanting to use 16-bit integer fractions I will give you the answer.

Firstly, you have to stop thinking about hexadecimal. It will only confuse you more.

The solution is to divide by 65536.
For example, if the result is 32768, then 32768/65536 = 0.5
So if you want to show the result as two decimal places, just multiply the result by 100 to give you 50.


I will make life easy for you and give you the solution.
I am not a great fan of PIC so I will give you the algorithm and you can supply your own code.

Step 1:
Take the 16-bit integer fraction and divide by 256. The easiest way to do this is to take the hi-byte and move it to the lo-byte and set the hi-byte to zero.

Step 2:
Multiply the result by 100.
(Since the hi-byte is always zero you can use an 8-bit x 8-bit multiply to give 16-bit result. So actually, you can combine Steps 1 and 2 by just multiplying the hi-byte by 100.)

Step 3:
Divide the result by 256. Really, there is no need to do this because your answer is already sitting in the hi-byte.

(So the quick solution without rounding is: Take X_FracH and multiply by 100. Your two digit answer is sitting in the hi-byte of the result.)

That's all folks!

Now if you wish I can show you an easy way to multiply by 100 and take care of rounding all at the same time.
 
Last edited:

Thread Starter

RG23

Joined Dec 6, 2010
304
; ACC = ACC * 0.00390625
; Temp = TEMP; ACC size = 16 bits
; Error = 0.5 %
; Bytes order = little endian
; Round = no
; ALGORITHM:
; Clear accumulator
; Add input / 256 to accumulator
; Move accumulator to result;
; Approximated constant: 0.00390625, Error: 0 %
; Input: ACC0 .. ACC1, 16 bits
; Output: ACC0, 8 bits
; Code size: 2 instructions

cblock ACC1
endc

;shift accumulator right 8 times

movf ACC1, w
movwf ACC0
 

Thread Starter

RG23

Joined Dec 6, 2010
304
I found the above code for dividing the result by 256 and tried the following

movf X_FracH,0
movwf TMP15
movwf TMP16

movf TMP15,0
andlw 0xF0
movwf ACC0
movf TMP16,0
andlw 0x0F
movwf ACC1
;shift accumulator right 8 times

movf ACC1,0
movwf ACC0

But I dont have the result in ACC0

If anyone has an idea please let me know
 
Last edited:

MrChips

Joined Oct 2, 2009
34,897
Well do you care to share what results you are getting? How do you expect me to help you?

Ok, let's start from scratch.

Tell me what is in X_FracH.
Multiply X_FracH by 10.
Take the the hi-byte and tell me your result. This is your first decimal place.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
Well do you care to share what results you are getting? How do you expect me to help you?

Ok, let's start from scratch.

Tell me what is in X_FracH.
Multiply X_FracH by 10.
Take the the hi-byte and tell me your result. This is your first decimal place.
__________________

I am dividing h'99' by h'07'. The result I get is 21.DB


I get X_FracH as DB

 

Thread Starter

RG23

Joined Dec 6, 2010
304
movf X_FracH,0
movwf Dividend1
call hextobcd;;;;; converts hex to bcd
movf r0,0
call bcd;;;;;;;;;;;;;;;;;; converts BCD to ASCII
movf r1,0
call bcd

Now I get r0 as 02

and r1 as 19

that is the result I get is 219

Now I have to divide 219 by 256 to get 0.85 which is required

If you have any idea please let me know

Thanks
 

MrChips

Joined Oct 2, 2009
34,897
Ok, now we're getting somewhere.

Stop thinking in hex. Hex is an illusion and a distraction.

Take X_FracH and multiply it by 10. Give me your answer.

Do not call hextobcd or any other conversion. You already have the right answer.

(Ok, I think I now understand your problem. You are not using a debugger but you are using a demo kit that can only show hex output.)

Respond and I will give you the full solution.
 

Thread Starter

RG23

Joined Dec 6, 2010
304
I executed the following code multiplying X_FracH by 10

movf X_FracH,0
movwf ACC0

;shift accumulator left 1 times
bcf CARRY
rlf ACC0, f
movlw 0x00
movwf ACC1
rlf ACC1, f

;copy accumulator to temporary
movf ACC1, w
movwf TMP16
movf ACC0, w
movwf TMP15

;shift temporary left 2 times
bcf CARRY
rlf TMP15, f
rlf TMP16, f
rlf TMP15, f
rlf TMP16, f

;add temporary to accumulator
movf TMP15, w
addwf ACC0, f
movf TMP16, w
skpnc
incfsz TMP16, w
addwf ACC1, f
movf ACC0,0
call bcd
 
Top