Sourav Rai Sourav Rai - 3 years ago 166
Linux Question

printing numbers in nasm

I have written an assembly code to print numbers from 1 to 9 but the code only prints 1 and no other element other than 1 is printed and only one output is received.It means that the loop is also not being run. I cant figure out what is wrong with my code.

section .bss

lena equ 1024
outbuff resb lena

section .data

section .text

global _start
_start:
nop
mov cx,0
add cx,0x30

incre:
inc cx
mov [outbuff],cx

cmp cx,39h
jg done

cmp cx,39h
jl print


print:
mov rax,1 ;sys_write
mov rdi,1
mov rsi,outbuff
mov rdx,lena
syscall
jmp incre

done:
mov rax,60 ;sys_exit
mov rdi,0
syscall


My OS is 64 bit linux. this code is built using nasm with the following commands : nasm -f elf64 -g -o num.o num.asm and ld -o num num.asm

Answer Source

Answer rewritten after some experimentation.

There two errors in your code, and a few inefficiencies.

First, you add 0x30 to the number (to turn it from the number 1 to the ASCII 1). However, you do that increment inside the loop. As a result, your first iteration cx is 0x31, second 0x62 ("b"), third 0x93 (invalid UTf-8 sequence) etc.

Just initialize cx to 0x30 and remove the add from inside the loop.

But there's another problem. RCX is clobbered during system calls. Replacing cx with r12 causes the program to work.

In addition to that, you pass the buffer's length to write, but it only has one character. The program so far:

section .bss

        lena equ 1024
        outbuff resb lena

section .data

section .text

        global _start
        _start:
                nop
                mov r12,30h

                incre:
                inc r12
                mov [outbuff],r12

                cmp r12,39h
                jg done

                cmp r12,39h
                jl print


                print:
                mov rax,1           ;sys_write
                mov rdi,1
                mov rsi,outbuff
                mov rdx,1
                syscall
                jmp incre

                done:
                mov rax,60          ;sys_exit
                mov rdi,0
                syscall

Except even now, the code is extremely inefficient. You have two compares on the same condition, one of them branches to the very next instruction.

Also, your code would be much much much faster and smaller if you moved the breaking condition to the end of the code. After the changes, we get:

section .bss

        lena equ 1024
        outbuff resb lena

section .data

section .text

        global _start
        _start:
                nop
                mov r12,30h

                incre:
                inc r12
                mov [outbuff],r12

                mov rax,1           ;sys_write
                mov rdi,1
                mov rsi,outbuff
                mov rdx,1
                syscall

                cmp r12,39h
                jl incre

                mov rax,60          ;sys_exit
                mov rdi,0
                syscall

There's still lots more you can do. For example, you call the write system call 9 times, instead of filling the buffer and then calling it once (despite the fact that you've allocated a 1024 bytes buffer). It will probably be faster to initialize r12 with zero (xor r12, r12) and then add 0x30. Maybe work with a register of one byte length, and only use r12 for storage across system calls, etc.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download