'Converting ASCII hex number to 32-bit binary integer in x86

So im reading the user's 8-digit input, and saving it into a variable. for example:

Enter an 8-digit hex number: 1ABC5678

So, then i loop through the 1ABC5678 hex number and subtract 48 from numbers 0-9 and subtract 55 from A-F to have a number representation of the input. but that seems to be wrong. My goal is to convert that 8-digit hex into a octal. I already know how to get from binary to octal, and thats by masking and shifting the 32bit binary digit. But my issue is that my approach to get the binary is wrong.

Im coding on Intel IA-32. x86.

This is my code so far:

; --------------------------------------------------------------------------
; Definitions to make the assignment easier
%define STDIN 0
%define STDOUT 1
%define SYSCALL_EXIT  1
%define SYSCALL_READ  3
%define SYSCALL_WRITE 4
%define BUFLEN 256
%define BINARYLEN 32
; --------------------------------------------------------------------------



SECTION .data                           ; initialized data section
; --------------------------------------------------------------------------

msg1:   db "Enter an 8-digit hex number: "      ; user prompt 
len1:   equ $-msg1                              ; length of the message

msg2:   db "Octal: "                            ; converted string label 
len2:   equ $-msg2                              ; length of converted message

msgerr1: db 10, "It needs to be 8 Digits", 10   ; error message 1
lenerr1: equ $-msgerr1

msgerrgeneral: db 10, "Invalid Characters", 10   ; error message general
lenerrgeneral: equ $-msgerrgeneral
; --------------------------------------------------------------------------


SECTION .bss                            ; uninitialized data section
; --------------------------------------------------------------------------
buf:    resb BUFLEN                             ; buffer for read
binary: resb BINARYLEN                          ; buffer for binary
octal:  resb BUFLEN                             ; converted octal
octal2: resb BUFLEN                             ; converted octal
rlen:   resb 8                                  ; length
rolen:  resb 8                                  ; length
; --------------------------------------------------------------------------


SECTION .text                           ; Code section.
global  _start                          ; let loader see entry point

_start: nop                                     ; Entry point.
start:                                          ; address for gdb

; -------------------------------------------------------------------
; prompt user for input
;
mov     eax, SYSCALL_WRITE      ; write function
mov     ebx, STDOUT             ; Arg1: file descriptor
mov     ecx, msg1               ; Arg2: addr of message
mov     edx, len1               ; Arg3: length of message
int     080h                    ; ask kernel to write

; read user input
;
mov     eax, SYSCALL_READ       ; read function
mov     ebx, STDIN              ; Arg 1: file descriptor
mov     ecx, buf                ; Arg 2: address of buffer
mov     edx, BUFLEN             ; Arg 3: buffer length
int     080h
; -------------------------------------------------------------------


check_one:
; -------------------------------------------------------------------
; error check 2, making sure its 8 digits
;
mov     [rlen], eax             ; save length of string read
cmp     eax, 9                  ; check if any chars read
je      read_OK                 ; >0 chars read = OK
mov     eax, SYSCALL_WRITE      ; ow print error mesg
mov     ebx, STDOUT
mov     ecx, msgerr1
mov     edx, lenerr1
int     080h
jmp     exit                    ; skip over rest
; -------------------------------------------------------------------



read_OK:        ; Now lets start converting with loops
        ; First Step .
        ; Second Step
        ; So on


L1_init:
mov     ecx, [rlen]             ; initialize count
mov     esi, buf                ; point to start of buffer
mov     edi, binary             ; point to start of new string


L1_top:
mov     al, [esi]               ; get a character
inc     esi                     ; update source pointer

or        al, al                ; Set the condition code flags
je        L1_cont               ; Jump if the end of the string
cmp       al, '0'               ; Compare al with ascii '0'
jb        exit                  ; Jump if less that this value
cmp       al, '9'               ; Compare al with ascii '9'
jbe       bin_convert           ; If less than or equal to ascii '9' then ok
and       al, 0DFH              ; Force lower case to upper case
cmp       al, 'A'               ; Compare al with ascii 'A'
jb        exit                ; Jump if less than this value
cmp       al, 'F'               ; Compare with ascii 'F'
ja        exit                ; Jump if above this value
sub       al, 7                 ; Make binary adjustment

bin_convert:
sub       al, '0'                          ; Subtract ascii '0'
shl       edx, 4                           ; Shift the result four bits left
or        dl, al                           ; Merge in the four bits just computed
;loop      L1_top                          ; Loop back for next byte to convert


L1_cont:
mov     [edi], edx               ; store char in new string
inc     edi                     ; update dest pointer
dec     ecx                     ; update char count
jnz     L1_top                  ; loop to top if more chars



L1_end:


; print out user input for feedback
;

mov     EAX, SYSCALL_WRITE      ; write out string
mov     EBX, STDOUT
mov     ECX, octal
mov     EDX, 32
int     080h

; final exit
;
exit:   mov     EAX, SYSCALL_EXIT       ; exit function
mov     EBX, 0                  ; exit code, 0=normal
int     080h                    ; ask kernel to take over


Solution 1:[1]

Some of the problems are:

  • the input string is newline-terminated, not null-terminated
  • some logic errors

You could write it like this:

L1_init:
mov     ecx, 8                  ; initialize count
mov     esi, buf                ; point to start of buffer
mov     edi, binary             ; point to start of new string


L1_top:
mov     al, [esi]               ; get a character
inc     esi                     ; update source pointer

cmp       al, '0'               ; Compare al with ascii '0'
...
or        dl, al                           ; Merge in the four bits just computed
loop      L1_top                          ; Loop back for next byte to convert

mov     [edi], edx               ; store char in new string

L1_end:

Note that you have already checked that you have 8 chars so no need to do it again in the loop.

Solution 2:[2]

The easiest way to convert a Hex string to an int value is to use either strtol(), strtoll(), or strtoimax():

#include <inttypes.h>
#include <stdlib.h>

intmax_t     max;
long         l;
long long    ll;
const char * str = "1ABC5678";

max = strtoimax(str, NULL, 16);
l   = strtol(str, NULL, 16);
ll  = strtoll(str, NULL, 16);

Alternatively you can write the code by hand if you prefer:

int          pos;
long         l;
const char * str = "1ABC5678";

l = 0;
for(pos = 0; str[pos]; pos++)
{
    l = l << 4; // each Hex digit represents 4 bits

    // convert ASCII characters to int value
    if ((str[pos] >= '0') && (str[pos] <= '9'))
       l += (str[pos] - '0') & 0x0F;
    else if ((str[pos] >= 'A') && (str[pos] <= 'F'))
       l += (str[pos] - 'A' + 10) & 0x0F;
    else if ((str[pos] >= 'a') && (str[pos] <= 'f'))
       l += (str[pos] - 'a' + 10) 0x0F;
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 David M. Syzdek