This is a multi-part message in MIME format. ------=_NextPart_000_0005_01C48DC4.B19CDB90 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Charles Craft wrote: > 11 bytes of RAM and 183 words of program memory! > Simulator stopwatch shows 1882 instruction cycles That sounded like an awful lot to me. My knee jerk reaction was that doing a divide by 10 and using the remainder to make each digit would be more efficient, especially considering the shortcuts that can be taken with the special case of dividing by a small constant. Just for fun, I wrote an integer to BCD routine to test out this hunch. It is indeed significantly more efficient. My subroutine takes 65 instruction locations, and uses 4 temporary variables. The existing state of the temporary registers are saved/restored to/from a data stack. Execution takes 879 cycles in all 5 test cases I tried. If the subroutine is allowed to trash the scratch registers it uses (I don't normally do things that way) then it takes only 30 instruction locations and executes in 847 cycles. The disadvantage of my approach is that it's a bit harder to understand. The special case divide by 10 routine is especially tricky, so I added a lot of comments to try and explain what is going on. I have attached the main source module in case anyone is interested. It is written assuming my PIC development environment described at http://www.embedinc.com/pic. ------=_NextPart_000_0005_01C48DC4.B19CDB90 Content-Type: application/octet-stream; name="bcd.aspic" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="bcd.aspic" ; This firmware demonstrates converting a 16 bit integer to 5 digit ; BCD representation. ; include "bcdlib.inc" extern regs ;force general registers to be defined ; ; Application configuration parameters ; gbank equ 0 ;direct register bank for global variables ; ; Derived constants. ; gbankadr equ bankadr(gbank) ;adr within globals bank ; ; Global variables. ; defram gbankadr bcd res 5 ;BCD digits in least to most significant = order global bcd ; ; Set the static processor configuration bits. ; __config b'11111110100100' ; XX------------ band gap cal, preserved by = programmer ; --XXX--------- unused ; -----1-------- data EEPROM protection disabled ; ------1------- program memory code protection = disabled ; -------1------ brownout detect enabled ; --------1----- GP3 is MCLR pin, not I/O ; ---------0---- power up timer enabled ; ----------0--- watchdog timer disabled ; -----------100 internal oscillator, GP4 is normal = I/O ; ;*********************************************************************** ; ; Executable code. ; ; Reset vector. ; reset code 0 clrf intcon ;disable all interrupts gjump start ;jump to relocatable startup code ; ; Relocatable code. ; strt code start unbank ; ; Init the interrupt system to completely off and default = configuration. ; clrf pie1 clrf pir1 gcall stack_init ;init the software stack ; ; Call the integer to BCD subroutine with various test cases. The = simulator ; will be used to verify that the 5 digits in BCD match the 16 bit = integer ; value in BCDIN after each call. ; loadk16 reg0, 65535 mcall i16_bcd nop loadk16 reg0, 65432 mcall i16_bcd nop loadk16 reg0, 1 mcall i16_bcd nop loadk16 reg0, 10 mcall i16_bcd nop loadk16 reg0, 0 mcall i16_bcd nop nop goto $-1 ; ;*********************************************************************** ; ; Subroutine I16_BCD ; ; Convert the unsigned 16 bit integer value in REG1:REG0 to 5 BCD = digits ; in BCD. ; glbsub i16_bcd, regf0 | regf1 | regf2 | regf3 ibankif gbankadr ;set indirect access bank for access to = return value movlw 5 ;init number of digits left to generate movwf reg3 movlw low bcd ;init pointer to where to write next digit = value movwf fsr digit_loop ;back here to generate each new BCD digit ; ;******************** ; ; Divide the 16 bit integer value in REG1:REG0 by 10. The remainder = is ; written to where FSR is pointing. REG1:REG0 is updated with the ; quotient. ; ; Register usage: ; ; REG1:REG0 - Starts as numerator. Quotient is shifted into LSB ; by one bit each iteration. ; ; REG2 - Loop counter. Number of iterations left to do. ; movlw 13 ;init number of iterations left to do movwf reg2 bcf status, c ;init 17th bit to 0 ; ; One quotient bit is produced each iteration of DIV_LOOP. The 16 bit ; value numerator is compared to the reference value of 10 * 2**13 =3D ; 10100000b. If the numerator is greater than or equal to the = reference ; value, then the quotient bit is a 1 and the reference value is = removed ; (subtracted) from the numerator. If it is less than the numerator, ; then the quotient bit is 0 and the reference value is not subtracted ; off. ; ; This pretty much mimics the way long division is done by hand. Note ; that if you were dividing 65535 by 10, you would first divide the ; leading "65" part by 10 to produce a 6 quotient digit. By only ; dividing the left few digits of the numerator, you are really = shifting ; the denominator left several digits and doing the divide. In other ; words, the first part of the long division of dividing 65535 by 10 ; is really dividing 65535 by 10000. This yields the high digit of = the ; quotient (6), with the remainder used to generate the remaining = digits. ; The quotient digit times the shifted denominator (60000) is removed ; from the numerator, and the remainder used as the numerator for the ; next iteration to get the next digit. In this example 65535 - 60000 ; =3D 5535, which is used to get the next quotient digit. Each time ; the denominator is shifted right one digit. Therefore, the next ; iteration will divide 5535 by 1000. ; ; The code below uses this same technique, except that it does it in ; binary instead of base 10. This means the actual "divide" step is ; just a compare. The quotient digit is 1 if the shifted denominator ; is greater than or equal to the current numerator, and 0 otherwise. ; Since the quotient digit can only be 0 or 1, it is either subtracted ; directly from the numerator (quotient digit 1) or not (quotient = digit ; 0). ; ; Instead of shifting the denominator right one digit each iteration, ; the algorithm below shifts the numerator left one digit (bit) each ; iteration. This is equivalent mathematically. It also frees up ; the low bit of the numerator each time, which is where the new ; quotient bit is stored. After 13 iterations, the low 13 bits of the ; numerator are the quotient, and the high 3 bits are the remainder ; which is always a value of 0-9. ; ; There is one additional wrinkle that may be a bit confusing. The = numerator ; after the the denominator times the quotient bit is removed is = always ; less than denominator, but it may have its high bit set. When the ; numerator is shifted left one 1, this high bit must be taken into ; account. If this high bit is set at the start of an iteration, then ; the quotient bit is always 1, since the value with the high bit set ; is always larger than the numerator. This high bit is left in C ; after the numerator is shifted left 1 bit at the end of the previous ; iteration. This is referred to in the comments at the "17th" bit, ; and is checked at the start of each iteration. ; div_loop ;back here to find each quotient bit movlw b'10100000' ;get high byte of value 10 shifted left 13 = bits btfss status, c ;17th bit is set ? jump high0 ;no, need to do compare subwf reg1 ;update numerator with denominator removed bsf status, c ;shift in a 1 quotient bit this iteration jump div_shq ;go shift in the quotient bit high0 ;17th high bit is zero subwf reg1, w ;compare to remaining value skip_wgt ;this quotient bit is 0, bit value is in C = ? movwf reg1 ;update numerator with denominator removed div_shq ;C contains new quotient bit rlf reg0 ;shift numerator left 1, new quotient bit = into LSB rlf reg1 decfsz reg2 ;cone one less iteration left to do jump div_loop ;back to make next quotient bit ; ; The quotient is in the low 13 bits of REG1:REG0 with the remainder = in the ; high 3 bits. ; ; Write the remainder as the next BCD digit value. FSR is pointing to = the ; BCD digit value byte. ; rrf reg1, w ;get remainder in bits 6:4 andlw b'01110000' ;mask in only the remainder bits movwf indf ;write to this BCD digit swapf indf ;put the digit value into the low 3 bits ; ; Clean up the quotient in REG1:REG0. ; movlw b'00011111' ;get mask for valid quotient bits in REG1 andwf reg1 ;mask in only the quotient bits ; ; Done with the divide. ; ;******************** ; incf fsr ;update pointer to next result digit decfsz reg3 ;count one less digit left to generate jump digit_loop ;back to do next digit leaverest ;restore registers and leave end ------=_NextPart_000_0005_01C48DC4.B19CDB90 Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit _______________________________________________ http://www.piclist.com View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist ------=_NextPart_000_0005_01C48DC4.B19CDB90-- ***************************************************************** Embed Inc, embedded system specialists in Littleton Massachusetts (978) 742-9014, http://www.embedinc.com