Hamed
Hamed

Reputation: 313

NASM: Convert multi character input to decimal

I am trying to write a program that gets a number with one or two digits and write Hello! as many times as that number.

I used this posts to write my code:
NASM: The loop turns into an infinite loop
Check null character in assembly language
Multi-Digit Input from NASM I did not understand this :)

But my code only works for two digit numbers and the result for single digit numbers are wrong.

My code:

section .data
    msg db 'Hello!',0xA
    len equ $-msg

section .bss
    n resb 2

section .text
    global _start

_start:
    ;get n
    mov edx, 2
    mov ecx, n
    mov ebx, 0
    mov eax, 3
    int 0x80

    lea     esi,[n]     ;address of our text
    call    toint

    ;loop for print 'Hello!\n'
    print_loop:
        push ecx
        mov edx, len
        mov ecx, msg
        mov ebx, 1
        mov eax, 4
        int 0x80    
        pop ecx
    loop print_loop

mov eax, 1
int 0x80


toint:
    push    eax
    push    ebx
    xor     ebx, ebx
    next_digit:
        movzx   eax, byte[esi]      
        sub     al , '0'
        imul    ebx, 10
        add     ebx, eax
        inc     esi
        cmp     byte [esi] , 0x0    ;check next character is null or not
        jne     next_digit
    ; end_loop:
    mov     ecx, ebx
    pop     eax
    pop     ebx
ret

Upvotes: 0

Views: 292

Answers (1)

Sep Roland
Sep Roland

Reputation: 39166

The sys_read call returns in EAX the count of characters that were sent to your inputbuffer. Because you allowed for an input of max. 2 characters, this count will be either 0, 1, or 2. You could use this info in your toint routine.

; IN (eax,esi) OUT (ecx)
toint:
    mov     ecx, eax         ; Number of digits
    jecxz   done
    push    eax              ; (1)
    push    ebx              ; (2)
    push    esi              ; (3)
    xor     ebx, ebx
next:
    movzx   eax, byte [esi]      
    sub     al , '0'
    imul    ebx, 10
    add     ebx, eax
    inc     esi
    dec     ecx
    jnz     next
    mov     ecx, ebx
    pop     esi              ; (3)
    pop     ebx              ; (2)
    pop     eax              ; (1)
done:
    ret

Please notice that there's a reversed ordering to be observed when preserving/restoring registers on the stack! (Your code missed this...)


4 tips

a. Prefer the MOV variant to load an address. It's always a shorter instruction.
b. Guard yourself against an input of zero.
c. Don't use LOOP. It's a slow instruction.
d. Provide the exit code to terminate the program.

    mov     esi, n      ; (a)
    call    toint       ; -> ECX
    jecxz   Exit        ; (b)
print_loop:
    ...
    dec     ecx         ; (c)
    jnz     print_loop
Exit:
    xor     ebx, ebx    ; (d)
    mov     eax, 1
    int     0x80

Upvotes: 1

Related Questions