Hexadecimal For Assembly Programming
Hexadecimal is a number system that uses a base of 16 and is frequently encountered in Assembly programming.
There’s a good reason that hex is ubiquitous in Assembly: one hexadecimal digit can represent four bits, which is called a ‘nibble’. It only takes two hex digits to represent a full byte. This makes hex a convenient format and replacement for pure binary machine code.
In some ways, hex is also more intuitive than binary, because it incorporates the numbers 0-9. Most of us are already familiar with these numbers due to our use of the decimal system in daily life. To understand hex, we simply add the characters A through F to the decimal system.
When learning any Assembly Language, knowledge of hexadecimal and the ability to quickly convert between hex, binary, and decimal are essential skills to master.
In this tutorial, we’ll cover the hexadecimal number system in detail. We’ll learn how hex works, how to do common conversions, and why hex is so important when learning Assembly Language. We’ll also see an example of how hex is used in an Assembly instruction!
Introduction to Hexadecimal
Hexadecimal is a base-16 number system that uses the digits 0-9 and letters A-F.
Hex extends the decimal system to include values (A-F) for numbers that have a decimal value between 10 and 15. We can visualize this by comparing hex to decimal:
Decimal | Hexadecimal |
---|---|
0 | 0 |
1 | 1 |
2 | 2 |
3 | 3 |
4 | 4 |
5 | 5 |
6 | 6 |
7 | 7 |
8 | 8 |
9 | 9 |
10 | A |
11 | B |
12 | C |
13 | D |
14 | E |
15 | F |
You may note that decimal and hex are the same from 0 to 9 and they diverge at the decimal value of 10, which is ‘A’ in hex.
The Hexadecimal Prefix
Hexadecimal numbers are often distinguished from others (such as decimal or binary) by using a prefix of ‘0x’. This prefix is not actually part of the number, but is used to signal that the number following it is in hexadecimal. For example:
0xABCD => This is ‘ABCD’ in hex, which has a decimal value of 43981.
0x1234 => This is ‘1234’ in hex, which has a decimal value of 4660.
Counting in Hexadecimal
Now that we understand how hex ‘0’ through ‘F’ is related to the numbers 0 through 15 in decimal, let’s practice counting higher numbers in hex in order to gain an intuitive understanding of how the hexadecimal number system works.
Let’s start with ‘F’, which is equal to ’15’ in decimal:
0xF
In order to increment ‘F’ by one, we need to add a second digit to the numer. This is exactly the same thing that happens when we want to increment the number ‘9’ in decimal by one. To do so, we add a second digit with the value of ‘1’ and zero-out the first digit:
0xF + 0x1 = 0x10
This number looks like decimal ’10’, but it’s actually equal to decimal ’16’.
From here if we want to continue counting, we can just increment the least-significant bit:
0x10 + 0x01 = 0x11
0x11 + 0x01 = 0x12
When we get to 0x19, we can continue counting up by using A-F:
0x19 + 0x01 = 0x1A
0x1A + 0x01 = 0x1B
When we get to 0x1F, in order to continue counting, we need to again increment the most-significant bit:
0x1F + 0x01 = 0x20
This number looks like decimal ’20’, but it’s actually equal to decimal ’32’!
It’s a good idea to spend a few minutes practicing counting up and down in hex. We see hex all the time when working with Assembly, so being able to quickly count up and down is essential.
Converting Between Hexadecimal and Binary
Another helpful skill when working in Assembly is being able to quickly (and mentally) convert between hexadecimal and binary. You don’t really need to do this for large numbers; you just need to be able to look at a single hex character and convert it to a nibble (four bits/half a byte) and vice versa.
We’ll often see hexadecimal numbers grouped two at a time because this represents a byte. For example, ‘AF’, ‘1D’, or ‘4C’.
This is also a great opportunity to continue to work on your skills with binary.
The following table shows the conversions between hex and binary, and includes the decimal values for reference.
Hex | Binary | Decimal |
---|---|---|
0 | 0000 | 0 |
1 | 0001 | 1 |
2 | 0010 | 2 |
3 | 0011 | 3 |
4 | 0100 | 4 |
5 | 0101 | 5 |
6 | 0110 | 6 |
7 | 0111 | 7 |
8 | 1000 | 8 |
9 | 1001 | 9 |
A | 1010 | 10 |
B | 1011 | 11 |
C | 1100 | 12 |
D | 1101 | 13 |
E | 1110 | 14 |
F | 1111 | 15 |
It’s best to be able to directly convert between hex and binary without needing to mentally use decimal as an intermediate step.
If you practice, you can quickly get the hang of it! There’s also a great game to help master, hex to binary, called Flippy Bit and the Attack of the Hexadecimals From Base 16.
How is Hexadecimal Used in Assembly?
When it comes to Assembly programming, hexadecimal is frequently used for several purposes, which we’ll see throughout this course:
1. Memory Addresses: Memory addresses in assembly are typically represented in hex. For example, a memory address might be 0x1000
, which indicates the address location ‘4096’ in decimal. While high level programming languages like Java or Python abstract away memory, in Assembly we work closely with it. This also makes Assembly great for developing skills with programming languages that can also work at low levels like C or Rust.
2. Machine Instructions: Assembly language instructions and opcodes are also often represented in hex. Each instruction is encoded as a sequence of bits, and these bits are grouped together into nibbles and represented as hexadecimal values for ease of understanding and readability.
3. Data Representation: Hexadecimal is frequently used to represent binary data. This makes it simpler to write and understand data patterns.
Here’s an example of an x86 assembly instruction:
MOV RAX, 0x123456789ABCDEF0
In this instruction:
MOV
is the mnemonic for “move”.RAX
is a 64-bit general-purpose register in x86-64 architecture.0x123456789ABCDEF0
is a 64-bit hexadecimal constant.
This instruction moves the hex value 0x123456789ABCDEF0
into the RAX
register. In x86-64 assembly, registers are 64 bits wide, allowing for operations on 64-bit integers and memory addresses. RAX
is the accumulator register in x86-64 and is used in many arithmetic and logic operations.
This example also demonstrates another feature of hex: it takes 16 hex digits to represent 64 bits. This is because, as we’ve seen a few times already, one hex digit equates to a nibble, or 4 bits.