Path: Home => AVR-EN => Assembler introduction => Conversion 8-bit    (Diese Seite in Deutsch: Flag DE) Logo

Beginner's introduction to AVR assembler language

Converting an 8-bit binary to a decimal ASCII string

Very often needed, so here explained in detail: how to convert an 8-bit binary value to an ASCII string. First simple, then with suppressing leading zeroes.

The method

8-bit binaries can be between 0x00 and 0xFF, or in decimal between 0 and 255. So we'll get a three-digit ASCII string between "000" and "255" as result of the conversion.

One possible method is that we'll take each bit's decimal value and add this to the result. We would have to add the folowing numbers:
BitIf 1 add
0"001"
1"002"
2"004"
3"008"
4"016"
5"032"
6"064"
7"128"
That means a lot of adds, a lot of decimal overflows (from one digit to the next higher) and a lot of decimal-to-ASCII conversions (and back). The assembler source code would be rather complicated, complex and impossible to debug.

Much simpler than this is to repeatedly subtract decimal 100 from the number until a carry occurs. The number of times, where the carry is not one, is either zero, one or two, and is the first digit of the result.

We can then repeat that with subtracting tens from the number. The number of times, we can subtract the ten without a carry can be between zero and nine, and is the second digit.

The third digit is simply the rest of the number left, we can simply convert this rest to ASCII and we're done.

The flow diagram of the conversion

Flow diagram of the 8-bit conversion This is the flow diagram. You see the two counting loops with the branching on carry clear (BRCC). All instructions to be used are assembler mnemonics, and you can type all that into a source code text file.

It starts with setting a register (here: R16) to the desired binary value. Just add the directive .equ cNmbr = 0x7B to define this number on top of your source code file.

The second step is to define the target, where the result goes to. Here, we use a pointer in the register pair X to point to a location in SRAM. To prepare this, we use the following directives:

.dseg
sD:
  .byte 3
.cseg

The SRAM is now prepared to receive the three-digit-ASCII-string.

Then follows the first subtraction loop. We set a counter, but we do not set it to zero. We set it to the ASCII character before the ASCII zero (the ASCII zero would be decimal 48), but we use '0' minus 1 as starting point. The loop, that starts with increasing the counter, now in its first execution sets the result byte to ASCII '0'. If the following subtraction of decimal 100 already ends with the carry flag set (if the number is smaller than 100), the result byte is '0'. If not, the loop is further repeated, until that is the case.

The last subtraction yielded a carry, so we have to undo this. As the assembler for AVRs does not have an ADDI instruction, we use SUBI, but with -100. That does virtually the same, but with a slightly different flag setting (which we don't need in our case).

The counter in R17, which is now either '0','1' or '2', is then written with an ST instruction to the SRAM location. The pointer is auto-incremented after the write operationandalready points to the next loaction in SRAM.

Now the loop with the subtraction of decimal ten follows. Again we start the counter with '0' minus one, but now we SUBI ten from the number. If no carry happens, we repeat the subtraction. After carry we again undo the last operation and write the second digit to the store.

The rest of the number is then converted to ASCII by adding 48. As there is no ADDI, we again use SUBI, but with -'0'. This last digit is written to the store and we are complete.

Simulating the conversion

The source code can be downloaded from here. It can be simulated with avr_sim as follows.

avr_sim after the first three instructions Here, the first three instructions of the source code have been executed. Register R16 has the number to be converted, the pointer register X points to the beginning of the SRAM.

avr_sim after the 100 loop Here, the first loop subtracting 100 has been executed and the result, an ASCII character '1' has been written to SRAM.

avr_sim after the 10 loop The second loop, subtracting 10 from the number, has been executed and the second digit has been written to SRAM.

avr_sim after the third digit And the last two instructions have produced the third digit in SRAM.

avr_sim at the end avr_sim with 190 at the end Here are the execution times: to the left with 123, to the right with 190. The times are not too different (factor of two, all well below 1 ms).

Suppressing leading zeros

Numbers like "000", "001" or "012" are not very convenient to read due to their leading zeros. So we'll have to remove those from the string, or: we have to suppress those already during the conversion process.

Formatting the string

Formatting the string is simple. Just place the pointer X to the beginning of the string and test the character if it is an ASCII zero. If yes, then replace it with a blank and check the second character, if is is a Zero, too. If yes, then blank this, too. Do not test and blank the third character: there could be nothing left, and that would be misleading.

avr_sim with leading zeros blanked This is the simulation with the leading zeros blanked. Of cource, the number converted here was zero.

Suppressing leading zeros during conversion

To avoid leading blanks already during the conversion process is a little more complicated than removing those after generation. While suppressing the leading hundred zeros is simple, the suppression of leading zeros in tens has to consider that the one hundred might not have been zero. In that case ten-zeros are not to be blanked. So we need a flag bit that prevents the zero check if the 100s aren't already zero.

As best choice for that we can use the T flag from SREG. If the 100s aren't zero, we clear the T flag, otherwise set it.

Before checking the tens for zero, we ask the T flag with BRTC if this flag is clear. If so, the zero check is jumped over and not executed.

This variation is activated if the constant cSuppress0 in the given source code is set to 1. It alters execution times slightly, but not too much.

To the page top

©2022 by http://www.avr-asm-tutorial.net