Search code examples
assemblyx86binarydecimalatt

Converting decimal to binary in assembly x86


I'm trying to write decimal to binary converter and for some numbers it works and for others it doesn't. 0 - 8 numbers works fine, but when I input 9 it displays 101.

SYSEXIT = 1
SYSREAD = 3
SYSWRITE = 4
STDOUT = 1
STDIN = 0
 
.bss                                   
.equ bufsize, 32               
.lcomm buf, bufsize             #buf - saved user input
                       
.equ buf2size, 32              
.lcomm buf2, buf2size           #binary in wrong order
 
.equ buf3size, 32              
.lcomm buf3, buf2size           #binary correct order
 
.data
 
msg_podaj:
.ascii "Wprowadz liczbe:\n"
msg_dlpodaj = .- msg_podaj
 
msg_test:
.ascii "TEST\n"
msg_dltest = .- msg_test
 
.text
.global _start
 
_start:
 
 
mov $SYSWRITE, %eax                             
mov $STDOUT, %ebx
mov $msg_podaj, %ecx
mov $msg_dlpodaj, %edx
int $0x80
 
mov $SYSREAD, %eax                              
mov $STDIN, %ebx
mov $buf, %ecx
mov $bufsize, %edx
int $0x80
 
xor %eax, %eax
xor %ecx, %ecx
 
mov $0, %edi                            
movb buf(,%edi,), %al                   
sub $48, %eax                           
 
read:
incl %edi                                            
movb buf(,%edi,), %cl                   
sub $48, %ecx
 

cmp $0, %cl                            
jl tu                                   
cmp $9, %cl                             
jg tu                              
 
imul $10, %eax                          
add %ecx, %eax                         
 
jmp read

tu:
 
mov $0, %edi                            
mov $0, %edx
mov $2, %ebx
 
cmp $0, %eax
je wstaw
 
movb $'1', buf3(,%edi,)
jmp loop
 
wstaw:
movb $'0', buf3(,%edi,)
 
loop:
cmp $1, %eax
jle changeorder
 
incl %edi
DIV %ebx
mov %edx, buf2(,%edi,)
add $'0', buf2(,%edi,)
 
jmp loop
 
changeorder:
mov $1, %esi
 
loop2:
cmp $0, %edi
je display
 
movb buf2(,%edi,), %ah
movb %ah, buf3(,%esi,)
incl %esi
decl %edi
jmp loop2
 
display:
mov $SYSWRITE, %eax
mov $STDOUT, %ebx
mov $buf3, %ecx
mov $buf3size, %edx
int $0x80
 
exit:                                         
mov $SYSEXIT, %eax
int $0x80

Solution

  • Your code could be simplified a lot (and simpler code usually means that it's simpler to find any mistakes). Here's an outline of an easier way of doing this (I'll leave it up to you to implement it in x86 assembly):

    void to_bin_string(unsigned input) {
      char output[33];
    
      // The number of binary digits needed to represent the input number, if we
      // exclude leading zeroes.
      unsigned digits = highest_set_bit(input) + 1;
    
      // Shift the input so that the most significant set bit is in bit 31.
      input <<= (32 - digits);
    
      for (unsigned i = 0; i < digits; i++) {
        // If the current msb is set, store a '1' in the output buffer. Otherwise
        // store a '0'.
        output[i] = (input & 0x80000000) ? '1' : '0';
        // Move the second-most significant bit into the msb position.
        input <<= 1;
      }
      output[digits] = '\0';
    }
    

    You can use the BSR instruction to compute highest_set_bit on x86 CPUs. The & operation can be done with an AND or a TEST, and << would be SHL.

    Also keep in mind that you typically should be using byte operations when accessing ASCII strings (i.e. not things like mov %edx, buf2(,%edi,)).