Make the bytes in your computer show up on screen!
This page shows you how to take a byte of data and display its value as two hexadecimal characters on the screen. This lets you see the contents of memory or registers in a human-readable way. (Printing, itself, was covered by the previous page.)
Each hexadecimal digit is 4 bits (1 nibble), so to print AL, which is 1 byte, we'll print the left nibble and then the right nibble. Here's the plan:
Set AH to 0x0E for printing
Copy AL to BL so that we don't lose what's in it when putting the hex chars we wanna print in AL,
Print the left nibble:
Right-shift AL by 4 so that it stores just the value of the left nibble,
Check if AL is between 0 and 9 or between 10 and 15; if it's between 0 and 9, we'll print an ASCII number, otherwise we'll print a letter (between A and F),
ADD the appropriate number so that AL now stores the right ASCII character to print,
Print AL (call BIOS print interrupt \cdDle)
Copy BL back to AL, then
Print the right nibble:
ANDAL with 0xF so that it stores just the value of the right nibble,
Same steps as printing left nibble: (1) Check if AL is between 0-9 or higher, and (2) calculate the ASCII char to print automatically!
Quick Example
Step
Description
AL
BL
1
Set AH to 0x0E.
0x??
0x??
2
If AL stores the value 0x48, then we want 48 to appear on the screen.
0x48
0x??
3
Copy AL to BL.
0x48
0x48
4
Shift AL to the right by 4 bits.
0x04
0x48
5
Convert the number in AL to a hex character.
0x34 (ASCII character 4)
0x48
6
Call BIOS print.
0x34
0x48
7
Copy BL back to AL (now it has the original value again).
0x48
0x48
8
Do a logical and operation on AL to get rid of the upper nibble.
0x08
0x48
9
Convert the number to a hex char again.
0x38 (ASCII character 8)
0x48
10
Call BIOS Print.
0x38
0x48
Walkthrough
Setting Up
Setting up is pretty straightforward: AH needs to be set to Sho for printing, which can be done with \b4Sho, and then \8a\da to copy AL into BL:
\b4Sho\8a\d8
Next comes the code for printing the left nibble.
Printing Nibble 1
Our goal is to isolate one nibble, figure out which printable character it should become, and then put the corresponding ASCII code into AL.
Right now, the byte in AL has two nibbles jammed together: high nibble and low nibble. We want just the high nibble first, so we shift right by 4 bits. Example:
0x48 → 0x04
0b010_01000 → 0b0000_0100
The effect of shifting a number right by 4 bits. Top: Hexadecimal, Bottom: Binary
But we can't just print this; we need to convert the number now in AL to the ASCII character corresponding to its hex digit. Luckily, the numerals in ASCII are sequential:
What gets printed
Byte in AL
0
0x30
1
0x31
2
0x32
...
9
0x39
So if the number in AL is between 0 and 9 then adding 0x30 to it will convert it to its ASCII character:
Eot0
Machine code for adding 0x30 to AL. Eot is the opcode for ADDing to AL, and 0 is the byte 0x30.
This works for each number from 1 through 9, but not for letters A through F; the letters A–F aren’t packed right after the digits 0–9 in ASCII. They're farther up, starting at 0x41 for A. What that means is that if you add 0x30 to the value 10 (0x0A), you won't get the ASCII value for A (you'll get 0x3A which is :).
The ASCII value for A is 0x41, so if the register has 0x0A in it, we need to add 0x37 to it, since:
0x0A + 0x37 = 0x41
Adding 0x37 moves values 10–15 (0x0A–0x0F) into the correct spot in ASCII, starting from A at 0x41.
Distinguishing Between Digits and Letters
Since we need to handle 0–9 and A–F differently, I typically use a comparison (to check if it's 0–9 or A–F) and a conditional jump. The way that I implement this is:
If the value is less than0x0A, the jump skips over adding 7 and lands on adding 0x30.
Otherwise, the jump doesn't run, and both 7 and 30 get added to the AL register.
This way, for values between 0–9, execution goes to the +0x30 instruction, whereas for values between A–F, execution goes to the +0x07, and then also runs the +0x30 instruction next.
Essentially, it's set up like this:
Start off the idea that we'll need to add at least0x30 to AL,
But for A–F, first add 0x07 to "push" it up to where the letters are in ASCII,
then add the 0x30 afterwards.
We use the condtional jump to skip the ADD AL, 0x07 if the value is already in the 0–9 range. In effect:
If AL < 0x0A, skip adding 7 → only add 0x30.
If AL ≥ 0x0A, add 7 first → then add 0x30.