Author Topic: PIC Binary to BCD Assembly Code  (Read 5359 times)

0 Members and 1 Guest are viewing this topic.

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: au
PIC Binary to BCD Assembly Code
« on: May 25, 2019, 05:06:27 am »
Hey guys! My first post!

I was roaming around the internet for code snippets to convert a binary integer to BCD in PIC Assembly.

There are some cool approaches over at http://www.piclist.com/techref/microchip/math/radix/index.htm. These inspired me to have a go at my own and share it with you all.

So here we go. Three subroutines. One 8-bit and one 10-bit. These work using successive subtraction (successive overflow) to test if each BCD bit should be set.
I also included my take on the 16-bit Double Dabble algorithm with a little trick to avoid a loop counter register.

Code: [Select]
; Converts an 8 bit integer in WREG to binary coded decimal.
; 26 instructions, 1 register, no loops. Works on PIC10 and up.
;
; Initial state:
; WREG contains the integer
; TEMP is cleared
;
; Final state:
; TEMP 7:4 contains 100s
; TEMP 3:0 contains 10s
; WREG contains 1s

TEMP = h'40' ; Assign as you please.

BCD8 CLRF TEMP ; Clear TEMP.

ADDLW d'56' ; Set carry if WREG >= 200. WREG is now WREG - 200.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'200' ; If carry was clear, revert WREG to the previous value.

ADDLW d'156' ; Set carry if WREG >= 100. WREG is now WREG - 100.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'100' ; If carry was clear, revert WREG to the previous value.

ADDLW d'176' ; Set carry if WREG >= 80. WREG is now WREG - 80.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'80' ; If carry was clear, revert WREG to the previous value.

ADDLW d'216' ; Set carry if WREG >= 40. WREG is now WREG - 40.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'40' ; If carry was clear, revert WREG to the previous value.

ADDLW d'236' ; Set carry if WREG >= 20. WREG is now WREG - 20.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'20' ; If carry was clear, revert WREG to the previous value.

ADDLW d'246' ; Set carry if WREG >= 10. WREG is now WREG - 10.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'10' ; If carry was clear, revert WREG to the previous value.

RETURN ; Done!




; Converts a 10 bit integer to binary coded decimal.
; 35 instructions, 1 register, no loops. Works on PIC12 and up.
; For PIC18, replace RLF with RLCF.
;
; Initial state:
; WREG contains the MSBs of the integer
; TEMP 1:0 contains the LSBs of the integer
; TEMP 7:2 is ignored
;
; Final state:
; TEMP 7:4 contains 100s
; TEMP 3:0 contains 10s
; WREG 3:0 contains 1s
; WARNING: There is no 1000s! So if the integer >= 1000 (decimal), TEMP 7:4 will be 10 (decimal).

TEMP = h'40' ; Assign as you please.

BCD10 ADDLW d'56' ; Set carry if WREG >= 200. WREG is now WREG - 200.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'200' ; If carry was clear, revert WREG to the previous value.

ADDLW d'156' ; Set carry if WREG >= 100. WREG is now WREG - 100.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'100' ; If carry was clear, revert WREG to the previous value.

ADDLW d'206' ; Set carry if WREG >= 50. WREG is now WREG - 50.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'50' ; If carry was clear, revert WREG to the previous value.

ADDLW d'231' ; Set carry if WREG >= 25. WREG is now WREG - 25.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'25' ; If carry was clear, revert WREG to the previous value.

ADDLW d'236' ; Set carry if WREG >= 20. WREG is now WREG - 20.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'20' ; If carry was clear, revert WREG to the previous value.

ADDLW d'246' ; Set carry if WREG >= 10. WREG is now WREG -10.
RLF TEMP, F ; Push carry onto TEMP.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'10' ; If carry was clear, revert WREG to the previous value.

ADDLW d'251' ; Set carry if WREG >= 5. WREG is now WREG - 5.
RLF TEMP, F ; Push carry onto TEMP.
RLF WREG, W ; Multiply WREG by 2 and add bit 1.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'10' ; If carry was clear, revert WREG to the previous value.

ADDLW d'251' ; Set carry if WREG >= 5. WREG is now WREG - 5.
RLF TEMP, F ; Push carry onto TEMP.
RLF WREG, W ; Multiply WREG by 2 and add bit 0.
BTFSS TEMP, 0 ; If carry was set, leave the new value in WREG.
ADDLW d'10' ; If carry was clear, revert WREG to the previous value.

RETURN ; Done!




; Converts a 16 bit integer to binary coded decimal.
; This is an implementation of the Double Dabble algorithm.
; 29 instructions, 5 registers, ~400 operations.
; Works on PIC10 and up.
; A little trick is used to avoid the need for a counter register.
;
; Initial state:
; LREG contains the LSBs of the integer.
; HREG contains the MSBs of the integer.
;
; Final state:
;
; AREG 3:0 contains 1s
; AREG 7:4 contains 10s
; BREG 3:0 contains 100s
; BREG 7:4 contains 1000s
; CREG 3:0 contains 10000s
; WREG and LREG are cleared.
; HREG = 128

AREG = h'50'
BREG = h'51'
CREG = h'52'
LREG = h'53'
HREG = h'54'

BCD16 CLRF AREG ; Initialise BCD registers
CLRF BREG
CLRF CREG

BSF STATUS, C ; Set carry flag. This single bit gets pushed onto the integer registers.
; Once it is moved to the MSB, the loop will exit.

BCD16_LOOP RLF LREG, F ; Rotate integer registers left
RLF HREG, F

RLF AREG, F ; Rotate BCD registers left
RLF BREG, F
RLF CREG, F

RLF HREG, W ; Once the integer registers have been rotated out, the bit
IORWF LREG, W ;  pushed on at the start of the routine will now be at
BTFSC STATUS, Z ;  the MSB, with all other bits cleared.
RETURN ; Test for this case, and exit if true.

MOVLW d'51' ; Additions to set bits 3 and 7 if respective nibbles >= 5
ADDWF AREG, F
ADDWF BREG, F

;MOVLW d'51' ; Prior addition to be subtracted. This value is already in WREG.
BTFSC AREG, 3 ; If bit 3 is set, don't subtract from the low nibble.
ADDLW d'253' ; WREG = WREG - 3
BTFSC AREG, 7 ; If bit 7 is set, don't subtract from the high nibble.
ADDLW d'208' ; WREG = WREG - 48
SUBWF AREG, F

MOVLW d'51' ; Prior addition to be subtracted.
BTFSC BREG, 3 ; If bit 3 is set, don't subtract from the low nibble.
ADDLW d'253' ; WREG = WREG - 3
BTFSC BREG, 7 ; If bit 7 is set, don't subtract from the high nibble.
ADDLW d'208' ; WREG = WREG - 48
SUBWF BREG, F

BCF STATUS, C ; Clear C.
GOTO BCD16_LOOP



Your thoughts / suggestions / tweaks and different approaches are always welcome!

Edit 1: NorthGuy's suggestions on PIC18 and fix a bug.
Edit 2: A bit more tweaking got the BCD10 subroutine down to 35 instructions.
Edit 3: My take on the 16-bit Double Dabble routine. 35 instructions.
Edit 4: Slight tweak to 16-bit Double Dabble. 34 instructions.
Edit 5: Updated 16-bit Double Dabble again. It is now 29 instructions. I realised there was no need to adjust the 10K register, as this will never be above 6 for a 16-bit integer. :palm:

Dabbot.
« Last Edit: May 28, 2019, 08:53:07 am by Dabbot »
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC Binary to BCD Assembly Code
« Reply #1 on: May 25, 2019, 12:51:48 pm »
Nice job.

If I remember correctly, ther's no LSLF on PIC18, so you would have to use RLCF.
 
The following users thanked this post: Dabbot

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #2 on: May 25, 2019, 01:57:35 pm »
Thanks. Noted in the comments.
I also fixed a bug in the BCD10 subroutine. That's what I get for not testing properly. :palm:
 

Offline JTR

  • Regular Contributor
  • *
  • Posts: 107
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #3 on: May 25, 2019, 02:27:59 pm »
Nice job.

If I remember correctly, ther's no LSLF on PIC18, so you would have to use RLCF.

Also, there is no RLF or RRF mnemonics on the PIC18 and no LSLF on the mid-range parts. Speaking of mid-range parts, there are 2x PIC10 devices that are mid-range so the second routine (one the LSLF error is corrected) will qualify for this.

Edit: In anycase, there are more powerful instructions on the PIC18 devices like conditional compare and skips and conditional branches so you could easily come up with shorter code for these parts.
« Last Edit: May 25, 2019, 02:32:44 pm by JTR »
 

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #4 on: May 25, 2019, 02:53:40 pm »
At first glance, the conditional compares might actually make it longer, because they're working on WREG and a target register.
The routines are doing a lot of work with literals, and the only way to get literals in and out of other registers is via WREG (Except for very special cases).
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC Binary to BCD Assembly Code
« Reply #5 on: May 25, 2019, 03:00:07 pm »
Also, there is no RLF or RRF mnemonics on the PIC18 and no LSLF on the mid-range parts. Speaking of mid-range parts, there are 2x PIC10 devices that are mid-range so the second routine (one the LSLF error is corrected) will qualify for this.

It is not a problem using RLF instead of LSLF. Where it's used, the carry flag is always '1', thus if you use RLF, '1' will always be shifted in. You only need to correct the subsequent code to account for '1' being shifted instead of '0'.
 

Offline JTR

  • Regular Contributor
  • *
  • Posts: 107
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #6 on: May 25, 2019, 03:33:25 pm »
Also, there is no RLF or RRF mnemonics on the PIC18 and no LSLF on the mid-range parts. Speaking of mid-range parts, there are 2x PIC10 devices that are mid-range so the second routine (one the LSLF error is corrected) will qualify for this.

It is not a problem using RLF instead of LSLF. Where it's used, the carry flag is always '1', thus if you use RLF, '1' will always be shifted in. You only need to correct the subsequent code to account for '1' being shifted instead of '0'.

Not sure who or what that reply was aimed at but for clarification I was not addressing the logicstics of the code.  What I said was there are no such mnemonics. On the PIC18 the mnemonics for rotates are RLCF, RRCF, RLNCF and RRNCF.
 

Online NorthGuy

  • Super Contributor
  • ***
  • Posts: 3146
  • Country: ca
Re: PIC Binary to BCD Assembly Code
« Reply #7 on: May 25, 2019, 04:44:36 pm »
Also, there is no RLF or RRF mnemonics on the PIC18 and no LSLF on the mid-range parts. Speaking of mid-range parts, there are 2x PIC10 devices that are mid-range so the second routine (one the LSLF error is corrected) will qualify for this.

It is not a problem using RLF instead of LSLF. Where it's used, the carry flag is always '1', thus if you use RLF, '1' will always be shifted in. You only need to correct the subsequent code to account for '1' being shifted instead of '0'.

Not sure who or what that reply was aimed at but for clarification I was not addressing the logicstics of the code.  What I said was there are no such mnemonics. On the PIC18 the mnemonics for rotates are RLCF, RRCF, RLNCF and RRNCF.

I'm sorry. I didn't mean to upset you in any way. I meant this part:

... no LSLF on the mid-range parts ...

and commented that this is not a big deal cause it can be replaced.
 
The following users thanked this post: JTR

Offline JTR

  • Regular Contributor
  • *
  • Posts: 107
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #8 on: May 25, 2019, 05:54:58 pm »
Also, there is no RLF or RRF mnemonics on the PIC18 and no LSLF on the mid-range parts. Speaking of mid-range parts, there are 2x PIC10 devices that are mid-range so the second routine (one the LSLF error is corrected) will qualify for this.

It is not a problem using RLF instead of LSLF. Where it's used, the carry flag is always '1', thus if you use RLF, '1' will always be shifted in. You only need to correct the subsequent code to account for '1' being shifted instead of '0'.

Not sure who or what that reply was aimed at but for clarification I was not addressing the logicstics of the code.  What I said was there are no such mnemonics. On the PIC18 the mnemonics for rotates are RLCF, RRCF, RLNCF and RRNCF.

I'm sorry. I didn't mean to upset you in any way. I meant this part:

... no LSLF on the mid-range parts ...

and commented that this is not a big deal cause it can be replaced.

All Good and I trust you are right about the logic of the code.  :) We were addressing different concerns both of which will be needed to be addressed when compiling on different PIC family devices.
 

Offline DabbotTopic starter

  • Regular Contributor
  • *
  • Posts: 192
  • Country: au
Re: PIC Binary to BCD Assembly Code
« Reply #9 on: May 27, 2019, 06:04:24 am »
Hey guys.

Updated with my take on the 16-bit Double Dabble algorithm.
29 instructions, 5 registers, ~400 operations.
I also used a neat trick to avoid the need for a loop counter.

Have a look and let me know what you think.
« Last Edit: May 28, 2019, 08:54:00 am by Dabbot »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf