If i have a linear search subprogram that needs to get called by a get_UnionArray subprogram... From main i pushed 3 arrays and 3 size variables. within get_UnionArray i pushed registers ebp, eax, ebx, ecx, edx... i have to call linear search next. I have to use one of the arrays and its size in linear search. My question is, do i have to push these two parameters on the stack again, or can i access them from linear search after i push ebp and mov ebp,esp within linear search?
Posted on 2010-04-03 15:04:48 by ZosoLzrd
Short answer: push 'em again. There may be optimizations available by using values that happen to already be on the stack. Taking advantage of these is liable to make your program "brittle" - if you change some little thing, the program will crash, unless you change a whole bunch of other stuff to compensate. I doubt if you'll find the added complication "worthwhile".

You do something a little "unusual" in your program so far...


...
push  array2 ; push array2 onto stack for get_array
push  size2 ; push size2 onto stack to count entries for array2

call  get_array ; call subprogram get_array to fill second array

pop    dword Posted on 2010-04-03 17:29:30 by fbkotler


That works (obviously, it is working), but the more "usual" way to do it would be to return the "result" - which is the count, in this case - in eax.


How would i do that?
Would i write:
ret    


Also, when pushing registers, do i need to use "push " or just "push eax"
Posted on 2010-04-03 19:16:07 by ZosoLzrd
Just "push eax" to save a register. If eax contained a valid address, "push dword " (Nasm would make us specify the size... although it seems obvious) would put the "" of that address on the stack. We might want to do that sometimes, but it doesn't preserve the value in eax.

There's a variety of "ret" that takes a parameter, but "ret " won't work. In the "stdcall" calling convention, a subroutine ends in "ret 4" or "ret 8" or "ret 12" - however many bytes of parameters the function took. This removes the parameters from the stack, freeing the calling code from having to do so. Neat! But we're using the "cdecl" calling convention (no idea how you pronounce that!), where "caller cleans up", so just "ret". If the value you want to return is in ecx, "mov eax, ecx" first. But you do "popa", which will overwrite eax... :(

Your "get_array" will make a good example.


;=== Subprogram get_array===
get_array:

segment .data

moreinputs db    "Are the more inputs to be entered(Y=1 or N=0)? ", 0
enterprompt db    "Enter next number: ", 0

segment .bss

segment .text
pusha
mov    ebp, esp ; copy esp into ebp to hold place on stack

mov    edx, ; move array into edx from stack
mov    ecx, 0 ; move size counter for array into ecx from stack

mov    eax, moreinputs ; move inputs prompt for printing
call  print_string ; print inputs prompt
call  read_int

if:
cmp    eax, 1
jne    endif

beginloop: ; statement to start while loop
mov    eax, enterprompt ; move enterprompt into eax for printing
call  print_string ; print enterprompt
call  read_int ; read integer from keyboard
mov    , eax ; place integer incoming integer into array
add    ecx, 1 ; increment counter for array size
;
mov    eax, moreinputs ; move inputs prompt for printing
call  print_string ; print inputs prompt
call  read_int ;
cmp    eax, 1 ; 8-bit comparison, if equal then zf = 1, otherwise zf =0
je    beginloop ; jump to start of loop if zf = 1
;
endif:

mov    , ecx ; copy counter into stack where size is.

popa
ret
;==== End of Subprogram get_array ====


I'm not "comfortable" with that label "isolated" from the code it refers to. If the preceeding source code happened to end in "section .data", the label would be moved elsewhere with the ".data"... it would make a subtle, hard-to-find bug. So I moved it. This has nothing to do with returning the value.

Then I changed to a more "canonical" prolog, "push ebp"/"mov ebp, esp". You could use "enter 0, 0" here - does the same thing. "enter 4, 0" would do "sub esp, 4" - like I've got commented out - as well. We don't need any local variables, so forget it (and don't ask what the second parameter to "enter" does!).

I ditched the "pusha"/"popa" and just saved the two registers we alter. We're "allowed" to trash these registers, according to the convention, but it can be "convenient" to save 'em. Dr. Carters subroutines in "asm_io" preserve all registers (except returning the result in eax, where there is one), so I did the same.

This results in the parameter being in a different position "up the stack", of course. The "last pushed" parameter, which we may be calling "first", is at ebp + 8 (skipping 4 for "caller's ebp" and 4 for return address). The address of our array is currently at ("second" parameter). I've left it that way, but you might want to change it...


;=== Subprogram get_array===

segment .data

moreinputs db    "Are there more inputs to be entered(Y=1 or N=0)? ", 0
enterprompt db    "Enter next number: ", 0

segment .bss

segment .text

get_array:
push ebp ; save caller's ebp
mov    ebp, esp ; copy esp into ebp to hold place on stack

; sub esp, ??? ; if you want local variables

push ecx  ; save caller's regs that we alter
push edx  ; these ones are optional - ebx, esi, edi are mandatory (but we don't alter 'em)

mov    edx,   ; move array into edx from stack
mov    ecx, 0 ; move size counter for array into ecx from stack

mov    eax, moreinputs ; move inputs prompt for printing
call  print_string ; print inputs prompt
call  read_int

if:
cmp    eax, 1
jne    endif

beginloop: ; statement to start while loop
mov    eax, enterprompt ; move enterprompt into eax for printing
call  print_string ; print enterprompt
call  read_int ; read integer from keyboard
mov    , eax ; place integer incoming integer into array
add    ecx, 1 ; increment counter for array size
;
; guard against buffer overflow!
; cmp ecx,
; ja buffer_full

mov    eax, moreinputs ; move inputs prompt for printing
call  print_string ; print inputs prompt
call  read_int ;
cmp    eax, 1 ; 8-bit comparison, if equal then zf = 1, otherwise zf =0
je    beginloop ; jump to start of loop if zf = 1
;
endif:

mov    eax, ecx ; copy counter to eax for return

pop edx  ; restore caller's regs
pop ecx

mov esp, ebp ; restore esp - we don't have to do this, since we didn't alter it
                    ; if we'd done that "sub esp, ???" to make a local variable,
                    ; this would "free" that memory
pop ebp  ; restore caller's ebp

; or "leave" does these two things at once

ret
;==== End of Subprogram get_array ====


Your calling code looks like:


...
push  array1 ; push array1 onto stack for get_array
push  size1 ; push size1 onto stack to count entries for array1

call  get_array ; call subprogram get_array to fill first array

pop    dword , eax


I don't know if you'd want to make those changes or not - the way you're doing it works alright. You do have that potential buffer overrun... the user will probably get bored before they enter 50 numbers, but it really isn't "secure"...

Making the user enter '1' or '0' for 'y' and 'n' is kinda strange... Could you use "read_char" here instead of "read_int"?

Save a copy of the "working" version before you start making "improvements"... just in case... :)

Best,
Frank

Posted on 2010-04-04 10:39:36 by fbkotler
THANKS! That's really helpful.

I've tried using Y or N, but for some reason it doesn't read the char.
My professor said i would have to write:

mov    eax, askformoreinputs
call  print_string
call  read_char
call  read_char


calling read_char twice... i guess it works like cin.ignore() in c++

I just used 1 or 0 to make it simpler to write, since i'm the only one that is going to use it.
However, i am planning to try to use Y or N once i have everything working.
Posted on 2010-04-04 10:49:23 by ZosoLzrd