NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: Mixolydian on May 31, 2014, 09:48:55 AM
-
I decided that yesterday would be the day that I would begin to learn assembly! Several hours later, after choosing NASM, reading some tutorials, and playing around, I have written my first program:
; External functions
extern _printf
; Global functions
global getMessageLength
global alterMessage
global printMessage
global _main
; Global variables
section .data
message:
db "abcd", 0
; Code
section .text
getMessageLength:
push ebp
mov ebp, esp ; setup a stack frame
mov eax, message ; store address of message into eax (caller saved, so we are allowed to modify it)
jmp getMessageLength_loop2
getMessageLength_loop:
add eax, 1 ; next character
getMessageLength_loop2:
cmp [eax], byte 0 ; is dereferenced character at eax the string's NULL truncator?
jnz getMessageLength_loop
sub eax, message ; subtract message from eax to get the difference from the byte that was 0 and the start (string length)
leave ; restore the stack back to how it was
ret ; returns with message length in eax
alterMessage:
push ebp
mov ebp, esp ; setup a stack frame
mov ecx, message ; store address of message into ecx
call getMessageLength
add eax, ecx ; eax holds the end of the message
alterMessage_loop:
mov bl, byte [ecx] ; bl (lowest byte of ebx) will hold the dereferenced byte at ecx
add bl, 1 ; add one to this
mov [ecx], byte bl ; put the modified character back into memory at location ecx
add ecx, 1 ; go to the next character
cmp ecx, eax
jnz alterMessage_loop ; if we have not reached the final character, repeat
leave ; restore the stack back to how it was
ret
printMessage:
push ebp
mov ebp, esp ; save the stack before calling a function with arguments
mov eax, message ; put address of message into eax (because we can't directly put it into the stack)
;lea eax, [message] ; equivalent to the above opcode
mov [esp], eax ; put eax into dereferenced stack pointer (first parameter for an int or pointer)
call _printf
;add esp, 4 ; equivalent to below opcode (in this situation)
leave ; restore the stack back to how it was
ret
_main:
call alterMessage
call printMessage
mov eax, 0
ret ; return 0
It takes the NULL truncated string "abcd" from memory, gets its length, loops through it and adds 1 to all characters. The output is:
bcde
I have programmed in C a lot (~5 years) but never dealt with assembly before so I am asking for some more experienced people to look over the above code and tell me if I am doing anything blatantly wrong, slow, or even bad stylistic layout so that I can catch these bad habits before I get more involved.
For example, I was told that using "add eax, 1" is faster than "inc eax" and so have been using that in my code but I have my doubts.
I have also seen a "loop" instruction, however it looks limited and I am not sure whether I should try to use it, or avoid it like HTML's "<center>" tag.
Thanks in advance; any suggestions are welcome.
-
Hi!
Your code looks great, I didn't tried to compile.
Some suggestions:
If you want to clear, zero-out or init register you could use xor instruction.
So, "mov eax, 0" could be replaced by "xor eax,eax".
I think XOR is faster or in the same time it occupies less memory, than "mov eax,0".
Loop instruction, It works this way: You have to set ecx register to a number of how many times the loop should loop,
during the loop you should not modify ecx OR you can modify it but make sure to save and restore.
; This will loop ten times or nine - It will loop while ecx is not zero, each "loop" instruction decrements ecx.
mov ecx,10
.cycle:
; Save ecx
push ecx
; Do other things with ecx, ... other registers
; Restore ecx
pop ecx
loop .cycle
-
The xor trick is neat, I'll keep that in mind.
Regarding the loop though, I don't understand why it is ".cycle:" rather than just "cycle:". I've compiled with both names and the code appears to do the same whether it is has the dot or not.
So is a dot in front of a label just a way of saying that this isn't a function, it's just part of a function?
So in my example should I change "getMessageLength_loop:" and "getMessageLength_loop2:" to have dots before them?
EDIT: Nevermind, I was able to write to a file using this code:
writeFile:
push ebp
mov ebp, esp
push writeBinary
push filename
call _fopen
push eax
push message
push eax
call _fprintf
pop eax
push eax
call _fclose
leave
ret
-
The xor trick is neat, I'll keep that in mind.
Yes, that's a rather standard way of setting the register to zero.
Regarding the loop though, I don't understand why it is ".cycle:" rather than just "cycle:". I've compiled with both names and the code appears to do the same whether it is has the dot or not.
So is a dot in front of a label just a way of saying that this isn't a function, it's just part of a function?
For Nasm the dot in front of the label ties it a previously defined label. It enables you to "reuse" the same label name in the same source file but within another defined function. For a simplified example:
alterMessage:
push ebp
mov ebp, esp
mov ecx, 10
.loop ; <- this label will become alterMessage.loop
.
; do stuff
.
jmp .loop ; <- will jump to alterMessage.loop
mov esp, ebp
pop ebp
ret
alterAnotherMessage:
push ebp
mov ebp, esp
mov ecx, 10
.loop ; <- this label will become alterAnotherMessage.loop
.
; do stuff
.
jmp .loop ; <- will jump to alterAnotherMessage.loop
mov esp, ebp
pop ebp
ret
This is a very powerful construct when building macros. Just don't define the same label twice within the same function.
-
In the .bss section, is there a difference between:
key resd 1
And:
key: resd 1
To reserve 1 doubleword named key?
-
In the .bss section, is there a difference between:
key resd 1
And:
key: resd 1
To reserve 1 doubleword named key?
No!
Both works the same, are equal.
It's just a variation, you can choose the right way for you.
I use this variation with ":", "key: resd 1".
-
OK, thanks.
Is there a way to block comment out code? Like C's:
/* ignored */
I found macros %comment and %endcomment but it appears like they only get ignored by the preprocessor.
Must I go through every line and write ";" at the beginning?
EDIT: Nevermind:
%if 0 ignored %endif
is working.