NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: ZimbuTheMonkey on March 29, 2014, 11:30:48 PM
-
hi again,
I need to swap the contents of two declared variables using a function call by value, here's what I have so far. I'm actually not quite familiar with using the dword keyword, I'm just following my course slides:
section .data
a db "3"
alen equ $-a
b db "7"
blen equ $-b
msga db "The value of A is "
msgalen equ $-msga
msgb db "The value of B is "
msgblen equ $-msgb
swapmsg db "SWAPPED!"
swapmsglen equ $-swapmsg
section .text
global _start
_start:
; print initial values
call print_a
call print_b
; call swap by value
push dword [a]
push dword [b]
call swap
; print new values
call print_a
call print_b
;exit
mov eax, 1
mov ebx, 0
int 0x80
; ********** functions ************
swap:
; preserve our register data
push ebp
push ebx
push eax
mov ebp, esp
; get data from the stack
mov eax, [ebp + 20]
mov ebx, [ebp + 16]
; swap stack data
mov [ebp + 16], eax
mov [ebp + 20], ebx
; print swap confirmation
mov eax, 4
mov ebx, 1
mov ecx, swapmsg
mov edx, swapmsglen
int 0x80
; restore register data
pop eax
pop ebx
pop ebp
; newline
mov al, 0xA
call print_char
ret 8
print_a:
mov eax, 4
mov ebx, 1
mov ecx, msga
mov edx, msgalen
int 0x80
mov eax, 4
mov ebx, 1
mov ecx, a
mov edx, alen
int 0x80
mov al, 0xA
call print_char
ret
print_b:
mov eax, 4
mov ebx, 1
mov ecx, msgb
mov edx, msgblen
int 0x80
mov eax, 4
mov ebx, 1
mov ecx, b
mov edx, blen
int 0x80
mov al, 0xA
call print_char
ret
; function to print the character in al (like the various newlines, # and colons, etc.)
print_char:
push edx
push ecx
push ebx
push eax ; must be pushed last - it's our "buffer"
mov eax, 4
mov ebx, 1
mov ecx, esp ; our buffer
mov edx, 1 ; just one
int 0x80
pop eax
pop ebx
pop ecx
pop edx
ret
When I run it, the contents are unchanged it and it prints 3 and 7 twice, in the same order (rather than the reverse). If I had to guess, it probably has something to do with the sizes being manipulated, but I'm not seeing it.
EDIT: So the actual memory locations are being swapped (I've tested and printed both locations before and after the swap). However, I can't make them go back to dwords a and b.
-
If you are passing "By Value", you are doing just that, passing the value of something and not the address. How can you modify a value if you don't know the address? If you want to modify a variable, you need to pass "By Reference" - pass the address.
push a
push b
call swap
swap:
; get data from the stack
mov eax, [esp + 4]
mov ecx, [esp + 8]
mov dl, [eax]
mov bl, [ecx]
mov byte[eax], bl
mov byte[ecx], dl
; print swap confirmation
mov eax, 4
mov ebx, 1
mov ecx, swapmsg
mov edx, swapmsglen
int 0x80
mov al, 0xA
call print_char
ret 4 * 2
-
The assignment asked for both methods.
I guess it expects us to change the actual contents of a and b through a 'mov' within the swap function.
-
The assignment asked for both methods.
I guess it expects us to change the actual contents of a and b through a 'mov' within the swap function.
Then where is that assignment? - Is it in your head and you forgot to post it?
Wanna help? Post assignment or describe it well.
I guess it expects
You are right, someone expect's something.
I need to swap the contents of two declared variables using a function call by value
Bad assignment description, if it rises more questions.
Variables have size's or length's.
Basic SWAP algorithm, you can do it on a paper.
using a function call by value
It seems it's a switch statement, or bad assignment description:
if(value==thisone) call thisfunction
else if(value == thisotherone) call thisotherfunction
I guess it expects us to change the actual contents of a and b through a 'mov' within the swap function.
No, i think, he expects you, to send, those, two variables to email, then swap, then send back.
Hmm.... But maybe he expects you to write variables, each on a separate paper, then swap papers how many times you want.
(via call by value)
Sounds stupid.
:D
-
I suspect that the purpose of the assignment is to illustrate the difference between "pass by value" and "pass by reference" - not just how to do it, but which is appropriate in which case. As Gunner points out, to actually swap the contents of the two variables, we need to know the addresses. So "pass by value" would not be appropriate. This may be the expected answer.
We could print "This is a:"... and then print [ b ]. I doubt if that's what's expected!
There's an issue with "sizes", but it isn't really the problem. Your variables are bytes. You can't push a byte on the stack. It is "legal" to push just 16 bits onto a 32-bit stack, but it leaves the stack poorly aligned. Pushing a dword, as you've done, is the way to do it, but we need to remember that only a byte is valid - the remainder of the dword is garbage. (includes the "7" as well as the "3") "mov al, [ebp + ?]" might be better than "mov eax, [ebp + ?]". The value we're interested in is in al both ways, but don't try to do an operation using all of eax! You could avoid this issue by making your variables dwords, but you don't need to.
You do something rather "unusual" in your function prolog (also called a "stack frame" or "activation record").
swap:
; preserve our register data
push ebp
push ebx
push eax
mov ebp, esp
; get data from the stack
mov eax, [ebp + 20]
mov ebx, [ebp + 16]
That'll work, since you undo it the same way, but you needed to calculate the offset from ebp depending on how many registers you pushed. The "usual" (for good reason) way is:
swap:
push ebp ; caller was probably using this!
mov ebp, esp ; we alter ebp here, then we leave it alone!
; if we needed local variables, we'd reserve space for 'em here
; we don't
; sub esp, enough
; we may also want to align the stack here
; now we can save the registers we need to save.
push ebx
push eax
; unusual to be saving eax - usually we return the "answer" in it
; now our parameters are at fixed, known offsets from ebp
mov al, [ebp + 8] ; "first" parameter (last pushed)
mov bl, [ebp + 12] ; second parameter
Now we can add 'em together, multiply 'em, divide 'em, raise 'em to the Nth power... but we can't alter the originals. We haven't got the originals! All we have is a copy, passed to us on the stack. Now if we had the address passed to us on the stack... but that would be "pass by reference" and we've got "pass by value". Bummer!
This would almost work...
; swap stack data
mov [ebp + 16], eax
mov [ebp + 20], ebx
If you'd ended with a plain "ret" instead of "ret 8", and if your variables were dwords...
push dword [a]
push dword [b]
call swap
pop dword [b]
pop dword [a]
... that just might give you the result you want. I'd consider that a "cheat". I suppose... it is "pass by value" and we did swap 'em in the function (as opposed to the much simpler way of popping 'em in the opposite order and skip the function entirely). I doubt if that's the expected answer.
You do something else a little "unusual", maybe... (if that's what they're showing you, that's the way you should be doing it!)
ret 8
You're using a "stdcall" calling convention. Nothing wrong with that, Windows does it with their API, it's a nice convenient convention. But you don't see it too often on Linux. Being written in C, Linux usually uses the "cdecl" calling convention. The functions end with a plain "ret" and it's the caller's responsibility to "clean up the stack"...
push param2
push param1
call somefunc
add esp, 8
Or, you can "pop" a couple of times to remove the parameters... perhaps into some variables...
I think the correct answer is "you can't do it with pass by value".
Best,
Frank