2

I'm trying to make this program in assembly. It should just convert a string to a decimal

int main (int argc, char **argv)
{
    int result = 0; 
    char *c = argv[1];  
    while(*c != 0)
    { 
        result *= 10;           
        result += (*c - 48);
        c++;    
    }
return result;
}

but without getting argv from the stack... so I wrote a little code in asm that I think should do this but it does not work.

Can anyone point me to what I'm doing wrong? I commented the code so you will know what I think is going on, please correct me with anything.

section .bss
        buf resb 8  ; define an array of 8 uninitialised bytes
section .text

global _start
_start:
    mov rax, 3  ; system call number (sys_read)
    xor rbx, rbx    ; file descriptor 0 (stdin)
    mov rcx, buf    ; buffer to store data
    mov rdx, 8  ; Lenght of buffer
    int 0x80

    xor rax,rax ; rax = 0
    mov rbx, buf    ; rbx = &buf
    xor rcx, rcx    

StrToDec:
        mov cl, [rbx]   ; cl = *rbx 
        cmp rcx, 0      ; check for nul THIS IS WRONG
        cmp rcx, 10     ; we have to check for NL ascii code 
        je end          ; if rcx == 0 goto end
        imul rax, 10    ; rax *= 10
        sub rcx, 48     ; rcx -= 48 (48 is acii for '0')
        add rax, rcx    ; rax += rcx
        inc rbx         ; rbx++
        jmp StrToDec

end:
    mov rbx, rax    ; rbx = rax
    mov rax, 1      ; rax = 1
    int 0x80

When i run this program and type 100 for example and then type echo $? to the terminal it should print 100

Thanks.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Renato
  • 193
  • 1
  • 4
  • 16
  • What results are you getting? – Jonathan M Aug 01 '11 at 17:58
  • What is "does not work"? – Josh Aug 01 '11 at 17:59
  • When i type 100 it returns 194, when i type 10 it returns 62 – Renato Aug 01 '11 at 18:03
  • Can you give a couple more examples? What happens when you type `543` or `1000`? – Jonathan M Aug 01 '11 at 18:11
  • When i type 543 i get 16 when i type 1000 i get 232... i dont understand anything anymore =( – Renato Aug 01 '11 at 18:15
  • Are you sure you're not running into 16/32 bit issues with these registers? See http://stackoverflow.com/questions/3818755/imul-assembly-instruction-one-operand and http://stackoverflow.com/questions/1948058/problem-in-understanding-mul-imul-instructions-of-assembly-language – Jonathan M Aug 01 '11 at 18:37
  • What is the initial value of rbx? Looks kind of random. – Jonathan M Aug 01 '11 at 18:41
  • i dont think its a 16/32 bit issu because im not using any real big numbers, but then again i could be wrong... do you want to know what is the value of rbx right when te program starts? cause at first it should contain 0 for the first syscall, then it should contain the address of buf, buf+1, buf+2, etc – Renato Aug 01 '11 at 19:34
  • Which assembler are you using? It looks like MASM syntax, but I'm still not sure what it is. – Anderson Green Apr 03 '13 at 23:19
  • 1
    related: [What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?](https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code) – Peter Cordes Mar 29 '18 at 02:44
  • and don't use magic numbers like 48. Use `'0'` instead – phuclv Mar 29 '18 at 03:16

2 Answers2

2
    mov rcx, [rbx]  ; rcx = *rbx 

That's wrong, it loads eight bytes from the buffer. You want to load only 1. Use mov cl,[rbx] or movzx ecx, byte [rbx]

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • I changed the above line to what you said but it still wont work returns exactly the same wrong results – Renato Aug 01 '11 at 19:21
0

FIXED:

I found out what was wrong with the program =D

First of all you need to check for value 10 so

cmp rcx, 0      ; check for nul 

is wrong because were looking for the new line ascii code not a null

cmp rcx, 10      ; check for NL

is right, I don't think it works if your getting the args from the stack, when getting args from stack you should check for 0.

Second: For numbers bigger than 255, echo $? would give me wrong results, but that's ok because echo $? can only show numbers up to 255, so even tho echo may show a wrong number the register rbx holds the right value.

I debugged it and it works fine now.

Ma_124
  • 45
  • 1
  • 11
Renato
  • 193
  • 1
  • 4
  • 16
  • Or more generally, check for a non-digit character to stop the loop! Unless you don't mind "123xyz\n" turning into `1*10^5 + 2*10^4 + 3*10^3 + ('x'-'0')*10^2 + ('y'-'0')*10 + ('z'-'0')` or something. Use `movzx ecx, byte [rbx]` / `sub ecx, '0'` / `cmp ecx, 9` / `ja .non_digit` to convert from ASCII to integer and range-check all in one. – Peter Cordes Mar 29 '18 at 02:37
  • [NASM Assembly convert input to integer?](https://stackoverflow.com/a/49548057) has working 64-bit code that stops at the first non-digit. – Peter Cordes Dec 06 '22 at 22:10