0

I have my first Assembly program running on an ATmega16 and now I want to convert a decimal number which is saved in a register called speed into a binary form of a hexadecimal number which can be displayed on a 7-seg display. For example speed has saved the decimal 0101 0000 (80) and I want to convert it into the Hex form 0011 0010 (50). How do i do that for any decimal saved in speed?

Thank you for any help you can provide.

Treton
  • 1
  • 1
  • if you want to display 80 on a 7 segment display, first you need to turn it into two values 8 and 0 0x00001000 and 0x00000000. then to display on a seven segment display then is very specific to the wiring of the display itself and how the many signals are connected to the mcu. as answered there isnt such a thing as binary decimal nor binary hex. these are just bits, thats it, interpretation is that of the programmer 80 = 0x50 0x01010000 is the value 80 which is the value 0x50. – old_timer Jun 19 '23 at 06:56
  • how to get from 0x01010000 to 8 and 0 should be the real question and that is elementary math. represent 135 seconds in mm:ss (minutes and seconds) represent 80 as tens and ones.... – old_timer Jun 19 '23 at 06:58
  • seven segment requires schematics and datasheets which you have not provided. – old_timer Jun 19 '23 at 06:58

2 Answers2

2

Binary and decimal are two different number bases. The normal way1 to have an integer in a register is pure binary, no decimal involved. A decimal or hex representation of that binary number is convenient for humans to look at, but that doesn't change what's actually in the register.

In your case, 0b0101'0000 is decimal 80, hex 0x50; you could write the constant any of those 3 ways in C++ source code. Note that the high 4 bits are 0101, binary for 5, and the low 4 are 0000 (0).

Base 16 is a power of 2, so 4-bit groups of bits map to hex digits. To get ASCII codes, you could use a lookup table, or add '0' or 'A' depending on whether the nibble value is >= 10 or not. (Or to add an extra 7 ('A' - '0') for numbers above 10, so it's just an if, not if/else). To get 7-segment-display codes, you might use a lookup table indexed by 4-bit integers.

In AVR assembly, you can isolate the two 4-bit halves of an 8-bit integer with and and swap instructions, like a compiler would make from this source

void foo(unsigned char in, unsigned char *out)
{
    out[0] = in>>4;       // high hex digit is first in printing order
    out[1] = in & 0x0f;   // low hex digit.
//  Actually just 0..15 integers, not mapped to ASCII or 7-segment codes
}

(Godbolt)

foo(unsigned char, unsigned char*):
        mov r30,r22
        mov r31,r23        # output pointer
        mov r25,r24        # copy the first input

        swap r25
        andi r25, 15   # emulate >> 4  with SWAP + ANDI with 0xf
        st Z,r25

        andi r24, 15   # isolate the low 4 bits
        std Z+1,r24

        ret

The storing is of course optional; if you want to use the values right away, you don't need an array to store them in. That just made it possible to write a C function that produced both outputs so we could look at the compiler-generated asm.

So really it just took mov + swap + 2x andi instructions to isolate the two nibbles which we can then map to hex digits. AVR only has shifts by 1 bit at a time, except for swap which rotates by 4. (Thus we have to mask away the high 4 bits to emulate shifting them out.)


You never want to have 0011 0010 (decimal 50) in a register in the first place: you could get that by multiplying 0b0101 by ten (0b1010), but the only way you could split it up again into 5 and 0 would be dividing by ten (0b1010), which is computationally much more difficult. And it makes no sense, you have hex 0x50, not decimal 50.

Don't do base-conversion by creating a binary integer whose decimal digits are the digits you want for some other base. That won't work for numbers like 0xa5 since decimal only allows digits up to 9, and for smaller bases like 8 or 2 you can only store at most 1111111111 in a 32-bit integer, so that's only 10 bits. And it gives you no easy way to get the digits back out when you want to do anything with them.


Footnote 1: Computers store everything in bits because they use binary logic, on or off, not 10 or 16 voltage levels. Another way you can use those bits is BCD, Binary Coded Decimal. That's a format where you use 4 bits for every decimal digit, and you have to manually do wrapping and carry-out for digits that become greater than 10 when adding. Or unpacked BCD where you store one decimal digit per byte, saving the unpacking work.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
2

For example speed has saved the decimal 0101 0000 (80) and I want to convert it into the Hex form 0011 0010 (50). How do i do that for any decimal saved in speed?

The number 8010 is the same number and equal to 5016, which is also the same number and equal to 010100002 — they all represent the same exact numeric value.  The number base doesn't change the numeric value — changing the number base changes the string of digits not its numeric value.

Let's examine the numbers in the other bases: 5010 is also 3216, which is the same as 001100112.

The relationship between 8010 and 5010 is a false equivalency — there is no equality relationship — these are simply different numbers.  8010 is only 5016.

We don't really have a notion of hex in decimal, whether binary or otherwise, and here's the reason why.  Consider instead, for example, 7910, which in hex/base 16 is the same as 4F16 and in binary is 010011112.  Now, we simply cannot represent 4F as a decimal number, false equivalency or not, so the question then is what base 2 value do you want for 7910?

If you want to convert the number to a hex string, you'll have to consider the digit range is 0-9 plus A-F as well, as base 16 has 16 different 1-character digits.

Erik Eidt
  • 23,049
  • 2
  • 29
  • 53