Reputation: 73
I am working on a program that contains two procedures. One that pushes an array of N unsigned double words to the stack, and one that pops N unsigned double words from the stack and stores them in an array. I am able to push all the elements successfully to the stack, but then the procedure cannot return to the main program because the stack pointer (esp
register) has been changed.
I have been able to return to main by manipulating the esp
register so that the return address is saved, and I reload that address into esp
before I return. However, by the time the next procedure is called the entries I pushed to the stack have been overwritten.
Is there a correct way to save data in the stack while working in a procedure?
Here is some of my code:
Main procedure:
main PROC
main_loop:
; Main menu
mov edx, offset choicePrompt
call WriteString
read_input:
call ReadInt
jno good_input
jmp main_loop
good_input:
cmp eax, 0
je create_vector
cmp eax, 1
je array_stack
cmp eax, 2
je stack_array
cmp eax, -1
je end_program
call crlf
jmp main_loop
create_vector:
call CreateVector
jmp end_options
array_stack:
call ArrayToStack
jmp end_options
stack_array:
call StackToArray
jmp end_options
end_options:
call crlf
jmp main_loop
end_program:
mov edx, offset exitPrompt
call WriteString
call crlf
exit
main ENDP
Pushing the array to the stack in the ArrayToStack procedure:
mov esi, offset Vector
mov ebx, N
dec ebx
mov eax, 0
mov ecx, -1
push_array_loop:
inc ecx
mov al, [esi + ecx]
push eax
mov [esi + ecx], byte ptr(0)
cmp ecx, ebx
jl push_array_loop
Writing the stack to the console in the StackToArray procedure:
mov eax, N
mov ebx, 4
mul ebx
mov ebx, eax
mov ecx, 0
write_stack_loop:
mov eax, [esp + ecx]
add ecx, 4
call WriteDec
mov edx, offset spacePrompt
call WriteString
cmp ecx, ebx
jl write_stack_loop
Upvotes: 3
Views: 282
Reputation: 39166
Chech your premises. In your first paragraph you talk about a procedure that pushes an array of N unsigned double words to the stack, but your code deals with an array of N unsigned bytes.
Furthermore I observe that your output on the console will be in reverse order (to the arrray) and that your code zeroes the input array as it gets read. I've kept all of these things in below solutions.
The first 2 snippets will preserve ECX
and EDX
. They do clobber EAX
.
The true explanation for your coding problem is of course to see how the stack gets modified with each step. Watch carefully!
ArrayToStack:
[ ret ]
^ esp
mov eax, N ; The number of array elements is a runtime value
dec eax
shl eax, 2
sub esp, eax
<-- eax = (N-1)*4 -->
[ ][ ... ][ ][ ret ]
^ esp
push dword ptr [esp + eax]
[ ret ][ ][ ... ][ ][ ]
^ esp
push ecx
push edx
[ edx ][ ecx ][ ret ][ ][ ... ][ ][ ]
^ esp
xor ecx, ecx
ToStack:
xor edx, edx
xchg dl, [Vector + ecx] ; Reading byte-sized element while zeroing the source
mov [esp + 12 + eax], edx
inc ecx
sub eax, 4
jnb ToStack
[ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
^ esp ^ esp+12
pop edx
pop ecx
[ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
^ esp
ret ; EAX ends at -4
[ a_N ][ ... ][ a_2 ][ a_1 ]
^ esp
StackToConsoleProcedure:
[ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
^ esp
push ecx
push edx
[ edx ][ ecx ][ ret ][ a_N ][ ... ][ a_2 ][ a_1 ]
^ esp ^ esp+12
xor ecx, ecx
FromStack:
mov eax, [esp + 12 + ecx*4]
call WriteDec
mov edx, offset spacePrompt
call WriteString
inc ecx
cmp ecx, N
jb FromStack
shl ecx, 2 ; ECX = N*4
mov eax, [esp + 8] ; Fetch return address
mov [esp + 8 + ecx], eax
<-------- ecx = N*4 ------->
[ edx ][ ecx ][ ][ a_N ][ ... ][ a_2 ][ ret ]
^ esp ^ esp+8
mov eax, ecx
pop edx
pop ecx
<-------- eax = N*4 ------->
[ ][ a_N ][ ... ][ a_2 ][ ret ]
^ esp
add esp, eax
[ ret ]
^ esp
ret ; EAX ends at N*4
If there's no need to preserve the ECX
and EDX
registers, but still allowing EAX
to get clobbered:
ArrayToStack:
mov eax, N
dec eax
shl eax, 2
sub esp, eax
push dword ptr [esp + eax]
xor ecx, ecx
ToStack:
xor edx, edx
xchg dl, [Vector + ecx]
mov [esp + 4 + eax], edx
inc ecx
sub eax, 4
jnb ToStack
ret
StackToConsoleProcedure:
xor ecx, ecx
Fromtack:
mov eax, [esp + 4 + ecx*4]
call WriteDec
mov edx, offset spacePrompt
call WriteString
inc ecx
cmp ecx, N
jb FromStack
shl ecx, 2
pop eax
add esp, ecx
push eax
ret
Upvotes: 2
Reputation: 12434
When a procedure P needs to store data that has a lifetime beyond the lifetime of P, the data cannot be stored on the stack within the stack frame of P, because, as you have discovered, it "goes away" when P returns.
Here are a couple other options that will work.
Have the calling procedure (main) allocate space in its stack frame for the data, and pass a pointer to the space into P. This works if the caller knows or can determine a maximum size for how much data P will generate. The caller should always pass a size along with the pointer, so P doesn't exceed the space allocated.
Use malloc (or some equivalent) within P and return a pointer to the data to the caller.
Upvotes: 1