Note: I've been busy with this for a while, so I don't know what has been printed here since around 3PM or so.
I will post a general algorithm for converting between two bases later, but for this particular question, there is a shortcut based, as you noted, on the fact that a hex numeral exactly represent four bits. Given that, if we assume that we have a value less than 256 (one byte), then a basic algorithm might be written using Python as a pseudo-code, as such:
Code: Select all
# look up table for the hexadecimal numerals
hex_numerals = "0123456789ABCDEF"
def byte2hex(value):
hex_str = ""
# to get the low bits, you apply a 'mask'
# to the high bits using a bitwise AND.
low_nibble = value & 0x0F
hex_str += hex_numerals[low_nibble]
# to get the high bits, shift the value to the right
# by four. The low bits will fall off of the byte,
# and the high bits will now be in the lower bits.
high_nibble = value >> 4
hex_str = hex_numerals[high_nibble] + hex_str
return hex_str
Just to explain a bit more (not sure if you need it, but again, a refresher can't hurt): when you AND two values, it leaves only the bits that are set in both values. For example,
This can be used to isolate specific bits or groups of bits, by having a 'mask' that matches the bits you want to capture. In this case, we want all of the low bits, and only those, so the mask we'd use for a one-byte value would be binary 00001111, or hex 0F.
The shift and rotate operations move the bits in the word either left (towards the most significant bit) or right (towards the least significant bit). With most instruction sets, 'shift' means that the bits that 'fall off the end' are discarded, while 'rotate' means the 'wrap around' the the other side. For example:
Code: Select all
01101101 Shift left 2 == 10110100
01101101 Shift right 2 == 00011011
01101101 rotate left 2 == 10110001
01101101 rotate right 2 == 01011011
Some high-level languages such as C and Python support shifts, but not rotates; most CPUs do have both, as they are both trivial to implement in hardware.
Note that a shift is, in effect, the same as an integer multiply or divde by a power of 2:
40 == (5 SHL 3) == (5 * 2^3) == 40
31 == (255 SHL 3) == (255 // 2^3) == 31
If we can assume that we're using
ASCII characters (or a compatible representation such as UTF-8), we can eliminate the numeral table, and instead just add each nibble with a value of 9 or less to the byte value 0x30, which is binary ASCII code for the numeral '0', while nibbles more than 9 can be subtract 10 (such that 0xA -> 0, 0xB => 1, etc.) and then add 0x41, the ASCII code for 'A'. Since the characters in question are sequential, this allows you to use the ASCII character set itself as an implicit table lookup.
Code: Select all
def byte2hex(value):
# to get the low bits, you apply a 'mask'
# to the high bits using a bitwise AND.
low_nibble = value & 0x0F
# to get the high bits, shift the value to the right
# by four. The low bits will fall off of the byte,
# and the high bits will now be in the lower bits.
high_nibble = value >> 4
hex_str = ""
for nibble in [high_nibble, low_nibble]:
if nibble <= 9:
hex_str += chr(nibble + 0x30)
else:
hex_str += chr((nibble - 10) + 0x41)
return hex_str
I've written an example of how to implement this in x86 assembly which I tested by incorporating it into a stripped down version of my boot loader. However, it should be fairly straightforward to implement yourself.
Code: Select all
byte2hex:
push cx
push di
mov cx, 2
mov al, bl
mov ah, bl
and al, 0x0f ; isolate low nibble
shr ah, 4 ; isolate high nibble into low nibble
.nibble_loop:
cmp ah, 9
jg .alphanum
add ah, 0x30
jmp short .store_char
.alphanum:
add ah, (0x41 - 0xA) ; NASM will compute imm. value
.store_char:
mov [di], ah
mov ah, al
inc di
loop .nibble_loop
pop di
pop cx
ret
I've attached the test files I wrote for each of these so you can confirm this; the Python code is for v 3.
x, while the NASM code can be assembled with
Code: Select all
nasm -f bin hexprint.asm -o hexprint.bin
I ran it with QEMU like so:
I also tested it with Bochs.
Whew, that's... a lot. I figured out some of the problems I was having with VERBUM along the way, though honestly I am not sure it is really something I need to be working on.