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?) ??
Advertisement
Answer
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
.