NASM - The Netwide Assembler

NASM Forum => Programming with NASM => Topic started by: ZimbuTheMonkey on March 29, 2014, 11:30:48 PM

Title: Swapping the contents of two variables (via call by value)
Post 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:

Code: [Select]
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.
Title: Re: Swapping the contents of two variables (via call by value)
Post by: Gunner on March 30, 2014, 04:43:45 AM
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.

Code: [Select]
push a
push b
call swap

Code: [Select]
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
Title: Re: Swapping the contents of two variables (via call by value)
Post by: ZimbuTheMonkey on March 30, 2014, 05:47:59 AM
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.
Title: Re: Swapping the contents of two variables (via call by value)
Post by: encryptor256 on March 30, 2014, 07:23:19 AM
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.

Quote
I guess it expects

You are right, someone expect's something.

Quote
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.

Code: [Select]
using a function call by value
It seems it's a switch statement, or bad assignment description:
Code: [Select]
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.

Quote
(via call by value)
Sounds stupid.

:D
Title: Re: Swapping the contents of two variables (via call by value)
Post by: Frank Kotler on March 30, 2014, 01:07:59 PM
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").
Code: [Select]
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:
Code: [Select]
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...
Code: [Select]
; 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...
Code: [Select]
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!)
Code: [Select]
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"...
Code: [Select]
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