Binary calculations => Conversions
(Diese Seite in Deutsch:
Number format conversion in AVR assembler
Converting numbers is very often used in assembler, because processors prefer
calculating in binary, while people on a terminal prefer decimal numbers. If one
has to communicate from within an assembler routine with one of the human species,
number conversion is a must. This page provides some basic know how on number
conversion. Learn a bit more in detail, how conversion between different number
systems is done.
For those who prefer going directly to the source code, here is
the link to the HTML version and the
link to the asm version of the source code.
The number systems discussed here ar:
So far the number format basics.
The AVR assembler software for conversion has to be somehow flexible to apply
in different situations. So it is better to spent some thinking before writing
it. I have adopted some rules in writing this source code. Here are the rules:
- Decimal: Each byte represents a decimal digit, coded in ASCII. Decimal
48 or binary $30 represents the digit 0, decimal 49 the digit 1, and so on to 57,
which represents a 9. Other numbers, from 0 to 47 and from 58 to 255, do not
represent correct digits. (Why 0 is 48, and not 0, can only be understood
historically, it reaches back to the times of US military teletype machines,
but this is another long story).
- BCD-numbers: BCD means Binary Coded Decimal. It's like decimal ASCII,
but the decimal digit zero here is indeed 0. BCDs reach from 0 to 9. Numbers
greater than 9 (that is 10 to 255) are illegal and not BCD.
- Binary numbers: Here only the digits 0 and 1 are used. Read from the
least significant bit, the value of the digits doubles, so the binary 1011 is
equal 1*(2 ** 0) + 1*(2 ** 1) + 0*(2 ** 2) +
1*(2 ** 3). Just like decimal 1234 is equal to 4*(10 ** 0)
+ 3*(10 ** 1) + 2*(10 ** 2) + 1*(10 ** 3). (By
the way: each number ** 0 is 1, except 0 ** 0, which is not
known exactly). Binary numbers are handled in packages of 4 (called nibble), 8
(called Byte) or 16 (called word), because single bits are very small numbers
and e.g. do not allow to buy five apples (needs a package of three bits).
- Hex numbers: Hex numbers are packages of four single bits. These
four bits represent 16 different hex digits. The first ten are named like
their decimal relatives, but the digits 10 to 15 are named A to F. As string
of ASCII-coded hex digits this is rather unlucky, because the ASCII representation
of the 9 is 57, of the A is 65. If you prefer using a..f for the additional six
digits in hex, the ASCII representation is even 97. This is relevant for number
conversions, because these hex digits are distributed over the ASCII character
table, complicating conversion.
The routine AscToBin2 is suitable for
converting ASCII-coded numbers in buffers. It reads over leading blanks
and zeros and stops conversion on the first non-decimal digit read. The
length of the number is limited to the 16-bit long result registers.
Numbers that exceed 65,535 cause an error with the T-flag set and Z
placed on the first digit that exceeds the 16-bit limit.
- Binary numbers: All binaries are designed for 16 Bit, with
values between 0 and 65,535. They are generally located in the register
pair rBin1H (upper 8 bits, MSB) and rBin1L (lower 8 bits, LSB). This binary
word is abbreviated as rBin1H:L. Preferred location for this word are the
registers R2 and R1, but it doesn't really matter, where and in which order
you place it. Some conversions require a second register pair, named
rBin2H:L. You can place it in the registers R4 and R3. Generally the values
in this register pair are restored after use, so you can use it for other
purposes, too, without the risk of a conflict.
- BCD- and ASCII-coded numbers: These numbers are most often located
somewhere in the SRAM space of the AVR, e.g. in a buffer of incoming
characters from the SIO. So these numbers are generally handled with the
pointer register Z (pointer register ZH:ZL or R31:R30). The numbers are
assumed to be in the following order: The higher the value of the digit the
lower the pointer adress. The number 12345 so would be located in SRAM like
this: $0060: 1, $0061: 2, $0062: 3, and so on. You can also place the pointer
Z to locations in the register space, e.g. to $0005. The 12345 would then be
located like this: R5: 1, R6: 2, and so on. The conversion software so works
as well in the register space, if you place the digits in that order to the
registers. No need to write a special conversion routine for the register
space. But beware of pointer bugs: the software overwrites registers without
asking you. You can even overwrite the pointer registers ZH:ZL. What happens
then is rather unknown. Thorough planning of the register space is a must, if
you use that routines in register space.
- Packaging: Because not all conversion routines are necessary, I
have packed the routines into four packages. The last two packages are only
dedicated to hex conversions. The first package converts ASCII- and BCD-numbers
to binaries, package II converts binaries to ASCII and BCD. Each package with
the called subroutines in it runs separately. If you remove parts of the
package content, be sure to keep the appropriated subroutines. The whole
four packages compile to 217 words of program space.
- Errors: If a number conversion leads to an error, the T-flag is
set to one. The T-flag can be used to react to an error, e.g. by using the
branch instructions BRTS and BRTC. If you need the T-flag for
other purposes, feel free to exchange the respective lines with the
instructions SET, CLT, BRTS and BRTC to use a
bit somewhere in a register. If an error is encountered the pointer Z
points to the point of error. If the error is caused by an illegal character,
it points to that character. If the error is an overflow error, the pointer
points to the digit, where the error occurred.
- Other rules: Further details on the different routines are listed
in the general part of the source code.
There you'll find a
overview on all rountines,
showing all available functions, their calling conditions and their error
If the length of the ASCII number is exactly five digits, you can use
the routine Asc5ToBin2. Every
illegal character causes an error, except leading blanks and zeros.
Conversion is done from the left to the right of the number. Each
additional digit causes a multiplication of the result by 10, followed
by adding the digit to the result. The routine is somewhat slow due
to the multiplication by 10. There sure are quicker methods to do the
conversion job. But this one is easy to understand.
Conversion of BCDs to binary is similiar to ASCII conversion, so we do
not discuss this routine in detail. See the
source code of this routine for
This routine Bin1Mul10 multiplies
a 16-bit-binary in rBin1H:L by 10. To do that, the binary is copied to
another location, added two times with itself, then the copy is added,
and another addition with itself yields the tenfold. As during each
step of adding an overflow of the 16-bit-binary can occur, the
detection of the overflow and the following reaction cause the most
time and code of that routine. If you are fine without that level of
error detection and if you don't care if your result is ok or if you
prefer error detection a different way, you can skip the parts of the
routine that branch around. The code will be much shorter and faster
Conversion from binary to ASCII is done in the routine
Bin2ToAsc5. The result is generally
five digits long. The conversion is done by a call to the subroutine
Bin2ToBcd5. This is discussed more
in detail below.
If you use the routine Bin2ToAsc
instead, the routine returns with the pointer Z on the first non-zero
digit and the number of remaining digits in rBin2L. This is convenient
if you have to send the result via SIO to the man behind the terminal
without boring him with leading blanks.
Bin2ToBcd5 converts the binary
in rBin1H:L to decimal BCD. This conversion is a bit long in code and
required time, but sure easier to understand than others. The
conversion is done by repeated subtraction of binary representations
of the decimal numbers 10,000, 1,000, 100 and 10. Only the remaining
one's are treated different!
The routine Bin2ToHex4 produces
a four digit Hex-ASCII number from a 16-bit binary in rBin1H:L.
Such hex numbers are somewhat easier to read and remember than the
binary original. How would you spell 1011011001011110? Hex B65E is a
little bit more convenient to remember, nearly like the decimal
The routine converts the hex digits A to F in capital letters. If you
more like a to f, change the source code accordingly.
With Hex4ToBin2 the conversion
goes the other way round. Because illegal (non-hex) digits can occur,
some code is necessary to detect those errors and signal them in the
©2002 by http://www.avr-asm-tutorial.net