Hello everyone!
Not so long ago I decided to learn assembly language. I have grasped the basics of it more or less, but there is one problem that I cannot solve (and find any help about). I wrote a program to output the sum of two integers that are entered by the user. I'm using the kernel sys_call write. It seems to add them up correctly, but it prints out the ASCII symbol of that number (or similar)! I've tried YASM, FASM and NASM, but the result is the same. Can anyone give me some advice as to how to output the actual number, not the ASCII code? Here's the source code -

....
section .bss
                  input1 resd 1
	input2 resd 1
	sum     resd 1
section .text
    global _start
    _start:
....
	mov	rax,[input1]
	add	rax,[input2]
	mov	[sum], rax

	; print sum
                mov           rdx,2
                lea             rcx,[sum]
                mov           rbx, 1
                mov           rax,4
                int 0x80

Please help or give some advice as to how I can get rid of this! Without solving this problem I cannot continue any further.

Dani AI

Generated

Ancient Dragon is spot on: you are writing raw binary bytes, not a textual decimal string. write only puts the exact bytes from your buffer on stdout, so if you pass it the 2 least-significant bytes of sum you will see non-printable glyphs. Convert the integer to ASCII first, then pass the pointer and length of that string to write. (man7.org)

One more gotcha in your snippet: you are on AMD64 but using int 0x80 with 64-bit registers. In 64-bit Linux you should use the syscall instruction and the x86-64 syscall register convention: rax = syscall number, args in rdi, rsi, rdx, r10, r8, r9. For write, that is rax=1, rdi=1 (fd), rsi=buf, rdx=len. If you want to keep int 0x80, assemble/link for 32-bit and use eax, ebx, ecx, edx. (man7.org)

Below is a compact, Linux x86-64 NASM/YASM routine that prints the signed 64-bit value in rax as decimal using only kernel syscalls. It converts by repeated division by 10, builds the string backward, handles zero and negatives, then calls write.

section .bss
outbuf  resb 32                 ; enough for -2^63 and newline

; on entry: rax = value to print
; clobbers: rbx, rcx, rdx, rsi, rdi, r8, r9
print_rax:
    lea     rsi, [outbuf + 32]  ; rsi = end of buffer
    mov     rbx, 10
    xor     r9d, r9d            ; sign flag = 0
    test    rax, rax
    jns     .abs
    neg     rax
    mov     r9d, 1
.abs:
    cmp     rax, 0
    jne     .loop
    mov     byte [rsi-1], '0'
    lea     rsi, [rsi-1]
    mov     rdx, 1
    jmp     .write
.loop:
    xor     rdx, rdx
    div     rbx                 ; rax = rax/10, rdx = remainder
    dec     rsi
    add     dl, '0'
    mov     [rsi], dl
    test    rax, rax
    jne     .loop
    test    r9d, r9d
    jz      .len
    dec     rsi
    mov     byte [rsi], '-'
.len:
    lea     rdx, [outbuf + 32]
    sub     rdx, rsi
.write:
    mov     rax, 1              ; sys_write
    mov     rdi, 1              ; fd = stdout
    syscall
    ret

Tip: as Tight_Coder_Ex’s nibble trick suggests, masking and shifting is fine for hex, but for decimal you really do need the divide-by-10 loop above. And if you later parse input, remember stdin gives you ASCII digits that must be converted back to binary before adding. (man7.org)

Recommended Answers

All 10 Replies

what you are doing is attempting to print the binary value of the number. It has to be converted to ASCII before it can be printed. If you look at an ascii chart you will see that the ascii value for '0' is 48 decimal, or 30H. So if you want to print the 0 digit you have to add 48 to make it printable on the screen.

using a loop your program needs to split the value of sum up into its individual digits and add 30H to the digit before printing it.

Thanks a lot for your reply. I will try it out soon, at least for singel digits. Is there a simpler way of fixing it?

If your application is pure assembly then the way AncientDragon explained it is the only way unless you use BCD or want to display result in decimal. A lot of applications I do are for windows therefore I use wsprintf a function of kernel32.lib and then one of the parameters such as %d or decimal or %X for hex will do the conversion for me

Guess I'll just have to try it then. Thanks a lot anyway!

If you really get stuck I can post a short snippet on the example Ancient Dragon gave you. Just to clarify you are using NASM on an intel based machine that uses Linux. The reason I ask is that I'm assuming in your first post rax actually means eax.

I'll be away for a bit so I'll give you the snippet regardless. This is written for an XP based machine, but I'm sure you'll be able to improvise.

push edi
mov al,30H ; Ascii equivalent to "0"
mov edx, 913387 ; You can make this any value
std ; set EDI to auto decrement
mov edi, 401087
.D0 mov al, dl
and al, 15 ; strip bits 7 - 4
or al, 30H
stosb
shr edx, 4 ; shift next digit
jnz .D0
mov eax, edi
inc eax
pop edi
cld
pop edi

This method leaves base address of string in EAX, but you can put it anywhere.

This only works if you are displaying decimal digits. You'll have to modify loop if you want hex in order to display A - F.

Thanks a lot! I managed to do it for one digit, but couldnt think how to do it for two or more! The machine that I'm using is AMD64 bit, so the register rax is equvalent to eax, but just is 64 bit. The assembler is YASM, which fully supports NASM's syntax. Thank you!

I managed to do it for one digit, but couldnt think how to do it for two or more!

I got the same problem. I managed to do it for one digit, but it's just impossible to do it for more.
I'm getting mad about this ><
^^

I'm using MASM32,but with a 16bit Linker.

Can anybody please give me a hint, what i made wrong ?

This is my source code.

DUDU SEGMENT

Affe db "Hallo Das einzige was das Programm hier tut, ist so eine Zeile auszugeben(derzeit nicht ;-))"
db "$"

Fenster db ?

db "$"

DUDU ENDS

TAM SEGMENT

ASSUME CS:TAM,DS:DUDU

Anfang: mov ax,DUDU
mov ds,ax


mov Fenster,85

lea di,Fenster


add Fenster,48

inc di

add Fenster,48


lea dx,Fenster



mov ah,09

int 21h



mov ah,4Ch

int 21h

TAM ENDS

END Anfang

#1: When you move 85 55H into Fenster your result will be two ascii digits. What you've done is added 48 to 85 and then 48 to your terminator $. This might help you out

Fenster     db    85, 0, '$'

mov   dx, offset Fenster
mov   ax, Fenster
push   ax
and    ax, 15
add    ax, 48
mov   dx, al
inc     dx
pop    ax
shr     ax, 4
add    ax, 48
mov    dx, al
dec     dx

I'm not advicating this is the best way to do it, but based on your logic this is probably the closest approximation based on your code

#1: When you move 85 55H into Fenster your result will be two ascii digits. What you've done is added 48 to 85 and then 48 to your terminator $. This might help you out

Fenster     db    85, 0, '$'

mov   dx, offset Fenster
mov   ax, Fenster
push   ax
and    ax, 15
add    ax, 48
mov   dx, al
inc     dx
pop    ax
shr     ax, 4
add    ax, 48
mov    dx, al
dec     dx

I'm not advicating this is the best way to do it, but based on your logic this is probably the closest approximation based on your code

Thanks.
Unfortunately I don't really understand, what you did :-)
But I'll play around with it a bit.

The main purpose of this script is getting a number, stored in ax, getting displayed.

It should be the final part of a calculator. Therefore the actual number in ax should naturally not be important, it should be exchangeable.

Mabye now you understand better, what i suppose the program to do.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.