PIC Binary to BCD Assembly Code - Page 1
Forum section: electronics / general

author: Dabbot
author_rank: Regular Contributor
timestamp: May 25, 2019, 05:06:27 am
content: 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 athttp://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:; 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 1sTEMP = h'40'; Assign as you please.BCD8CLRF 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.BCD10ADDLW 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 = 128AREG = h'50'BREG = h'51'CREG = h'52'LREG = h'53'HREG = h'54'BCD16CLRF AREG; Initialise BCD registersCLRF BREGCLRF CREGBSF 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_LOOPRLF LREG, F; Rotate integer registers leftRLF HREG, FRLF AREG, F; Rotate BCD registers leftRLF BREG, FRLF CREG, FRLF HREG, W; Once the integer registers have been rotated out, the bitIORWF LREG, W; pushed on at the start of the routine will now be atBTFSC 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 >= 5ADDWF AREG, FADDWF 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 - 3BTFSC AREG, 7; If bit 7 is set, don't subtract from the high nibble.ADDLW d'208'; WREG = WREG - 48SUBWF AREG, FMOVLW 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 - 3BTFSC BREG, 7; If bit 7 is set, don't subtract from the high nibble.ADDLW d'208'; WREG = WREG - 48SUBWF BREG, FBCF STATUS, C; Clear C.GOTO BCD16_LOOPYour 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.Dabbot.

author: NorthGuy
author_rank: Super Contributor
timestamp: May 25, 2019, 12:51:48 pm
content: Nice job.If I remember correctly, ther's no LSLF on PIC18, so you would have to use RLCF.

author: Dabbot
author_rank: Regular Contributor
timestamp: May 25, 2019, 01:57:35 pm
content: Thanks. Noted in the comments.I also fixed a bug in the BCD10 subroutine. That's what I get for not testing properly.

author: JTR
author_rank: Regular Contributor
timestamp: May 25, 2019, 02:27:59 pm
content: Quote from: NorthGuy on May 25, 2019, 12:51:48 pmAlso, 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.

author: Dabbot
author_rank: Regular Contributor
timestamp: May 25, 2019, 02:53:40 pm
content: 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).

author: NorthGuy
author_rank: Super Contributor
timestamp: May 25, 2019, 03:00:07 pm
content: Quote from: JTR on May 25, 2019, 02:27:59 pmIt 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'.

author: JTR
author_rank: Regular Contributor
timestamp: May 25, 2019, 03:33:25 pm
content: Quote from: NorthGuy on May 25, 2019, 03:00:07 pmNot 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.

author: NorthGuy
author_rank: Super Contributor
timestamp: May 25, 2019, 04:44:36 pm
content: Quote from: JTR on May 25, 2019, 03:33:25 pmI'm sorry. I didn't mean to upset you in any way. I meant this part:Quote from: JTR on May 25, 2019, 02:27:59 pmand commented that this is not a big deal cause it can be replaced.

author: JTR
author_rank: Regular Contributor
timestamp: May 25, 2019, 05:54:58 pm
content: Quote from: NorthGuy on May 25, 2019, 04:44:36 pmAll 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.

author: Dabbot
author_rank: Regular Contributor
timestamp: May 27, 2019, 06:04:24 am
content: 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.