'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 |