1

I'm new to this forum. I have a little experience with high-level languages (really little). Nearly one month ago I thought it would be a good idea to see how assembly worked so after choosing nasm (IA-32) on linux I started learning from a tutorial.

Now, after ending it, I tried to write a simple program where you get the computer to print a list of 100 number (1 2 4 8 16...) but I couldn't even get it right. I get this output:

1PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP(continues)...

The program is this:

section .text
    global main
main:
    mov word [num], '1'
    mov ecx, 100
doubl:
    push ecx ; to push the loop counter

    mov eax, 4
    mov ebx, 1
    mov ecx, num
    mov edx, 1
    int 0x80

    sub ecx, 30h
    add ecx, ecx   ; shl ecx, 1
    add ecx, 30h
    mov [num], ecx   ; deleting this line I get  11111111111111111...

    pop ecx  ; to pop the loop counter
    loop doubl
exit:
    mov eax, 1
    int 0x80    
section .bss
num resw 2

It looks like the error is in the part that doubles the number or the one that stores it in the variable 'num', yet I don't understand why it happens and how to solve it.

By the way can someone explain me when to use the square brackets exactly? Is there a rule or something? The tutorial calls it "effective address" and it looks like I have to use the brackets when I want to move (or do something with) the content of a variable instead of doing it to the variable's address. Yet I'm quite confused about it. I see it used in:

mov ebx, count
inc word [ebx]
mov esi, value
dec byte [esi]

But isn't it obvious that one wants to increment the content of the register (since it doesn't have an address (or does it?) ??

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Klei
  • 13
  • 3
  • The `sys_write` call needs a pointer to the bytes you want to output in `ecx`, and you're trying to keep the actual value there. Also, your add/sub 30h technique will only work for values less than 10. – nobody Jul 31 '14 at 16:53
  • related: an x86-64 Linux (with 64-bit `syscall` instead of 32-bit `int 0x80`) implementation of converting and printing, a lot like Michael's answer here: https://stackoverflow.com/questions/13166064/how-do-i-print-an-integer-in-assembly-level-programming-without-printf-from-the/46301894#46301894 – Peter Cordes Nov 07 '17 at 17:24

1 Answers1

3

Your numbers will quickly grow larger than just a single digit. What you ought to be doing is have an integer in num rather than a character, and then convert that integer into a string that you can print with sys_write.

Here's one way of doing the conversion: repeated division by 10, getting the lowest digit first as the remainder:

; Input:
; eax = integer value to convert
; esi = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; eax = pointer to the first character of the generated string
; ecx = length of the generated string
int_to_string:
  add esi,9
  mov byte [esi],0    ; String terminator

  mov ebx,10
.next_digit:
  xor edx,edx         ; Clear edx prior to dividing edx:eax by ebx
  div ebx             ; eax /= 10
  add dl,'0'          ; Convert the remainder to ASCII 
  dec esi             ; store characters in reverse order
  mov [esi],dl
  test eax,eax            
  jnz .next_digit     ; Repeat until eax==0

  ; return a pointer to the first digit (not necessarily the start of the provided buffer)
  mov eax,esi
  ret

Which you can use like this:

    mov    dword [num],1
    ...
    mov    eax,[num]       ; function args using our own private calling convention
    mov    esi,buffer
    call   int_to_string
; eax now holds the address that you pass to sys_write
    ...

section .bss
    num    resd 1
    buffer resb 10

Your number-doubling can be simplified to shl dword [num],1. Or better, double it at some point while it's still in a register with add eax,eax.

Michael
  • 57,169
  • 9
  • 80
  • 125
  • Thank a lot. It really helped. I really liked the way to transform the number in string (although I couldn't understand how it worked 'til I tried it myself with paper and pen). Just one question: the subroutine .next_digit starts with a dot. Is it a convention for all subroutines or a particular genre? – Klei Aug 01 '14 at 15:01
  • `.next_digit` isn't really a subroutine. It's just a label, that in this case marks the beginning of a loop. The dot at the beginning makes it a [local label](https://www.tortall.net/projects/yasm/manual/html/nasm-local-label.html). Local labels can be convenient to use within a subroutine, since that allows you to have multiple labels with the same name at different places in your program (good for labels with generic names, so that you won't have to use silly naming schemes like `else1:`, `else2:`, `else3:`, etc). – Michael Aug 01 '14 at 15:11
  • I had thought that 'ret' was part of .next_digit . I just saw it belonged to int_to_string instead. Thanks again. – Klei Aug 01 '14 at 16:47