In a high-level language, I would write it as:
def put_nibble(buf: array[byte], n: int):
buf[0] = '0' + ((n shr 3) bitand 1)
buf[1] = '0' + ((n shr 2) bitand 1)
buf[2] = '0' + ((n shr 1) bitand 1)
buf[3] = '0' + ((n shr 0) bitand 1)
def char_to_nibble(ch: byte) -> int:
if '0' <= ch <= '9': return ch
ch = ch bitor 0x20 # convert to lowercase
if 'a' <= ch <= 'z': return ch - 'a' + 10
error
def byte_to_binary(b: str):
buf = byte[8] # reserve memory on the stack
hi = char_to_nibble(b[0])
put_nibble(buf, hi)
lo = char_to_nibble(b[1])
put_nibble(but + 4, lo)
sys_write(1, buf, 8)
This way you avoid comparing each possible digit on its own.
The above code also reduces memory access to the memory on the stack, since it doesn't need any external strings for the bit patterns.
Sure, the put_nibble code looks a bit repetitive, but you can merge all the '0' + … together into a single addition:
add dword [buf], 0x30303030 ; 0x30 == '0'
You could also compute the buffer for a whole nibble in a register and then write it to memory in a single instruction:
; input: al = the nibble to print
; output: ebx = 4 bytes ASCII buffer containing the 4 binary digits
xor ebx, ebx
rcr al, 1
adc ebx, '0'
rol ebx, 8
rcr al, 1
adc ebx, '0'
rol ebx, 8
rcr al, 1
adc ebx, '0'
rol ebx, 8
rcr al, 1
adc ebx, '0'
rol ebx, 8
mov [buf], ebx
Once you have translated the above high-level code into assembler, you probably want to optimize the code a bit. Or just use a compiler to do the heavy work for you. Write your code in C or in Go, assemble it into machine code and look at the result. Some keywords for searching:
- go tool objdump
- gcc -S binary.c -O2
By the way, you should not place the string literals for the prompt and the bit patterns in a writeable segment since they are not intended to be overwritten by any part of your program.