1

So I'm learning x86 Assembly with NASM and I'm trying to make a simple function that prints out integers. My Code...

print_integer:          ; Integer input stored in eax
    mov ecx, 10
print_integer_loop:
    mov edx, 0
    div ecx         ; eax /= 10 | edx = eax % 10
    or edx, 0x30        ; Convert 0-9 to ‘0’-‘9’
    push edx        ; Push remaineder to stack

    mov eax, 4      ; sys_write
    mov ebx, 1      ; STDOUT
    mov ecx, esp        ; Top of Stack to ecx
    mov edx, 1      ; Print 1 byte
    int 0x80        ; syscall

    pop edx         
    cmp eax, 0      ; Compare quotient to 0
    jnz print_integer_loop

_main:
    mov eax, 4251
    call print_integer

    mov eax, 1      ; ‘Exit’ System call
    mov ebx, 0      ; Exit with error code 0
    int 0x80        ; syscall 

When I run this code nothing happens. It just ends without printing anything. I've looked over all the similar topics and none I've found have been helpful. I'm not sure what is going wrong so any insight would be great.

EDIT: I updated NASM to a more recent version so I could use x86_64 syscall. Code is below

print_integer:              ; Assume integers is in r15
    push 0x0a               ; Push endline character
    mov r14, 1              ; Setup counter for printing 
    stack_loop:
        mov rax, r15        ; Move r15 to rax for divison
        xor rdx, rdx        ; Avoids error
        mov rcx, 10         
        div rcx             ; rax = rdx:rax / rcx  |  rdx = rdx:rax % rcx
        add rdx, '0'        ; Convert 0-9 to ‘0’-‘9’

        push rdx            ; Push remainder to stack
        mov r15, rax        ; Move quotient to r15 register     
        inc r14             ; Counter + 1   

    cmp r15, 0              ; Compare quotient to 0
    jne stack_loop          ; If not zero, loop

; Characters are now on stack in reverse order
    print_loop:
        mov rsi, rsp        ; Put remainder in rsi
        mov rdx, 1          ; Print 1 char (length)
        mov rax, 0x2000004  ; System call write
        mov rdi, 1          ; Standard output, number of bytes
        syscall             ; Invoke kernal

        dec r14             ; Counter - 1
        pop r15             ; Remove off top of stack
    cmp r14, 0
    jne print_loop
    ret 

Hopefully this helps someone in the future. This may not be the best way to do it but it is a way.

CMilby
  • 624
  • 1
  • 6
  • 23
  • This print method seems to mess with the stack or something, because if i load a new value into r15 and try to print again it prints some garbage out, any ideas as to why it is happening? – Arqu Jan 13 '16 at 15:56
  • 1
    Can you message me your code? I can take a look at it and hopefully be able to help. This is he first I've come across that problem. – CMilby Jan 13 '16 at 16:28
  • You can find the code here: http://pastebin.com/jcpMhTQE seems like it messes with the stack (rbp), oh and I'm running on mac os x using x86_64 assembly and use NASM to generate the binary. – Arqu Jan 13 '16 at 16:40
  • Update: Never mind, it was an error on my side, I've just started out with ASM and used output from the gcc compiler as reference, the line mov rsp,rsb at the start messes up pointers to my stack. Seems to run fine now. – Arqu Jan 13 '16 at 16:49
  • related: [How do I print an integer in Assembly Level Programming without printf from the c library?](//stackoverflow.com/a/46301894) makes a string on the stack so you can efficiently do one `syscall`. – Peter Cordes Jan 22 '20 at 12:39

1 Answers1

1

First of all you are outputting the first remainder first. The approach you are using to write the decimal number will output the number in reverse order: The number 4251 will be printed as "1524".

Then your program is containing some errors:

  • The content of the "eax" register is overwritten twice (by "mov eax,4" and by "int 0x80") while calling the operating system. You have to do a "push eax" before the "push edx" and a "pop eax" after the "pop edx" to save and restore the content of the "eax" register.
  • The same is true for the content of the "ecx" register; this can be solved by using a "jnz print_integer" instead of a "jnz print_integer_loop" so "ecx" gets re-initialized.
  • Finally the "ret" instruction is missing after the "jnz" instruction so when the print_integer routine is done the program will fall back into the "main" routine.

The program how it is now should output the digit "1" endlessly. I tested on my computer and it does!

The fixed program looks like this:

print_integer:
    mov ecx, 10
; print_integer_loop is no longer needed
    mov edx, 0
    div ecx
    or edx, 0x30
    push eax     ; inserted
    push edx
    mov eax, 4
    mov ebx, 1
    mov ecx, esp
    mov edx, 1
    int 0x80
    pop edx
    pop eax      ; inserted
    cmp eax, 0
    jnz print_integer  ; changed
    ret          ; inserted

main:
    mov eax, 4251
    call print_integer
    mov eax, 1
    mov ebx, 0
    int 0x80

I tested the program on Ubuntu 12 and it worked well (however it prints the number 4251 as "1524").

By the way: Why do you name your main function "_main"?

Do you instruct the linker to link to "_main" as entry point (if yes: why not "_start") or do you use the normal start-up code for Windows?

If the last one is the case: The "int 0x80" system calls in your program only work under Linux; running your code under Windows or mixing the code with Windows code and running the result under Linux would not work!

--- Edit ---

Because I didn't expect you to use a Mac I didn't mention Mac OS X:

Because Windows does not use "int 80h" a program containing this instruction would probably crash under Windows.

I know that Mac OS X also uses "int 80h" so this instruction will not cause a program crash. I also know that under Mac OS X the usage of "int 80h" is completely different from Linux. As far as I know parameters are passed on the stack under Mac OS X.

The way your program is written is definitely only working under Linux and not under Mac OS X!

Unfortunately I do not know much about Mac OS X so I cannot give you any more hints on this.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • Is "_start" a normal naming convention for assembly? I'm really new to it and just going off of tutorial I found online and "_main" is what many people use as well as main being the start point for C-code. Also, I apparently have no idea what I'm doing cause I am doing this on an Intel-based Mac, so what would the correct "int" call be? – CMilby Jan 19 '15 at 07:17
  • @CMilby: very good question. OS X's low level calls seem to be standard: http://stackoverflow.com/questions/6990885/assembly-language-in-os-x (and references therein). – Jongware Jan 19 '15 at 13:02