In comp.arch.embedded a discussion went on, for a while, about the merits of assembler verses C. Don Yuniskis posted this little gem as an example of an efficient 16 bit Binary to 4 digit Binary Coded Decimal routine. Don wrote: -->> Here's one version of a binary-decimal conversion routine. Call with r.hl having binary value; returns 4 digit packed BCD in r.de -- I neglected the fifth digit :-( It can be rewritten to preserve that extra digit. To produce ASCII, obviously you can append a code fragment to add '0' to each unpacked digit. BinDec: LD B,16 ;7 LD DE,0 ;10 Loop: ADD HL,HL ;11 LD A,E ;4 ADC A,A ;4 DAA ;4 LD E,A ;4 LD A,D ;4 ADC A,A ;4 DAA ;4 LD D,A ;4 DJNZ Loop ;13/8 RET ;10 <<-- I wrote: Of course it only works if the processor has a DAA instruction. (Decimal Adjust Accumulator). I mentioned at the time that both the PIC and the 68HC05 don't have a DAA instruction and so his routine wouldn't work. To date I have been using the slower 16 bit to 5 digit algo. that uses the divide and makes use of the location that holds the remainder after a divide. It uses three bytes of RAM and calls the divide routine. Not a big deal if 16 bit divide is used elsewhere and has to be part of the application anyway. -->> unsigned long ubcd(void) { unsigned long ulResult; // Working result and arg. unsigned int i; // loop counter ulResult = 0; for (i=0; i<16; i+=4) { ulArg = ulArg / 10; // get rid of LS Digit long_q <<= i; // put remainder in correct position. ulResult |= long_q; // add to result } return ulResult; // packed BCD. }; <<-- However the penalty in using the divide is fairly high as it shifts through the 16 bit quotient 16 times. Don's routine looked attractive as it also walks through the 16 bit binary value but only once, not five times. So I have cloned the DAA instruction and rewritten his routine to also generate 5 digits instead of 4. It's written in C and before anyone has a heart failure be aware that I wrote it in Assembler first. Then re-wrote it in C in a way that simulated as much of my assembler as possible. This makes it easier to shove it around in the source file without worrying about page bits etc. Assembler would be a few bytes smaller where the C compiler generates redundant code. The goto is there to allow this routine to be replicated three times instead of being used as a subroutine. Why? Because the 16C57 only has a two level stack. BTW, Dag Bakken should note that I do indeed take advantage of the type of code generated by the compiler when I: "argument + 0x66; // set C,DC flags with trial add." I still believe that a compiler should not even generate code for this but the MPC does and I take advantage of it. It could 'break' in the future though with a new compiler release which is why I really don't like doing it. // Decimal Adjust Accumulator simulation // Argument is the value to decimal adjust. // Must be called immediately after addition in order to not lose // status of CARRY and DIGIT CARRY bits in status register. // ie: a hidden argument is the status register. unsigned char DAA(unsigned char argument) { Flags.DAA_FLAG = 0; // Flag for doing code twice. Flags.Carry = STATUS.C; // Mustn't clear if already set. while (1) { // this could be a label that we goto. DAA_VALUE = 0; // Clear DAA adder. // Now custruct DAA adder if (STATUS.DC) { // if a Half carry occured then make BCD DAA_VALUE = 6; // by adding 6 lower digit. }; if (STATUS.C) { // If a full carry occured make BCD digit by DAA_VALUE |= 0x60; // adding 6 to upper digit. }; // If digits overflowed from addition // we now make them BCD again. argument += DAA_VALUE; // DAA_VALUE can be 0x00, 0x06, 0x66 if (Flags.DAA_FLAG) // We do above code twice. goto DONE_DAA; // This is how we get out. Flags.DAA_FLAG = 1; // Secondly test if BCD digits are in 'A'..'F' range byt a trial add. argument + 0x66; // set flags with trial add. }; DONE_DAA: // Carry might be already be 1 so we cannot just assign passed parameter CARRY flag // which might have been zero. if (Flags.Carry) STATUS.C = 1; return(argument); }; unsigned char de0,de1,de2; int counter; unsigned long hl; // Binary to Decimal Conversion routine 16bit to 5 digit. // Parameter is passed in global var. 'hl' and is cleared when complete. // result is in 'de0,de1,de2' where 'de0' is least sig digits. // Uses 8 bit counter to walk through binary number. void BinDec() { de0 = 0; // Clear result accumulator. de1 = 0; de2 = 0; counter = 16; // All 16 bits of argument. do { hl <<= 1; // Put MSb into Carry. // Now if we had a ADC instruction this part wouldn't be needed. WREG = 0; if (STATUS.C) WREG = 1; // Simulate the Add with carry operation. // At first glance it appears like a RLF would do the same thing but // we need the STATUS.DC set if there is a digit overflow. // 68HC05 does have ADC instruction. de0 += de0 + WREG; // After addition do a decimal adjust. de0 = DAA(de0); // if there was an overflow from the previous addition add it into // the next BCD digits. WREG = 0; if (STATUS.C) WREG = 1; de1 += de1 + WREG; de1 = DAA(de1); WREG = 0; if (STATUS.C) WREG = 1; de2 += de2 + WREG; de2 = DAA(de2); } while (--counter); }; Finally I do welcome all input. Especially if someone can improve on the speed/space of this one. Cheers, John. Pioneers are the ones, face down in the mud, with arrows in their backs. Automation Artisans Inc. Ph. 1-250-544-4950 PO Box 20002 Fax 1-250-544-4954 Sidney, BC CANADA V8L 5C9