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
.