Page 1 of 1
Using stack for arguments
Posted: Fri Feb 19, 2016 4:11 pm
by twtty
I do not feel entirely sure as to whether this fits best here or under the
General Programming. Should you feel it fits best under there, feel free to move this post! It is somewhat OS related as it is using BIOS interrupts and its registers for storing values.
I have been attempting to use a stack for variable transportation to a function. The code is compiled using NASM, which probably seems rather obvious to most. I have a stack set up like this:
Code: Select all
cli
mov ax, 0x0000
mov ss, ax
mov sp, 0xffff
sti
The variables I push to the stack are the head, track and sector used when loading from a disk. I push them like this:
Code: Select all
push 0 ; Head
push 0 ; Track
push 2 ; Sector
These are supposed to be loaded into CH (track number), CL (sector number) and DH (head number). I have tried various different methods to go about doing so. As my understanding of NASM is somewhat limited, it would be nice if somebody could explain to me the proper way of loading the correct values into the correct registers.
Here is what I am trying to achieve, but loading them from the stack rather than just hard coding in the values:
Code: Select all
mov cl, 2 ; Sector
mov ch, 0 ; Track
mov dh, 0 ; Head
Disk loading otherwise works fine, thanks to everybody who answered my previous post!
Any information you can give explaining the various aspects of using the stack for arguments would be wonderful. When I began trying to get using the stack for arguments to work, I thought it was as simple as popping off the argument into any of the registers. How come for example you cannot pop into the CL, CH and DH registers? Also, what is the proper way to do this?
Thank you!
Best regards,
Red
Re: Using stack for arguments
Posted: Fri Feb 19, 2016 4:42 pm
by iansjack
1. Don't set your stack pointer to an odd address. It must be 16-bit aligned (i.e. an even address.). Set it to 0 instead of 0xffff.
2. You can only pop into 16-, 32-, or 64-bit registers, not the 8-bit ones. How come? That's just the way things are.
3. The proper way is just to load the values into the registers. What's the point of this additional push/pop? If you really want to do this for some reason you will have to push/pop CX and DX.
Re: Using stack for arguments
Posted: Fri Feb 19, 2016 9:45 pm
by twtty
Is it not so that the stack builds downwards rather than upwards? By setting the stack pointer to zero, would it not stop working?
The point in "additional push / pops" is that the registers are cleared during the duration of a function call, I need some way of retrieving the values even after the registers have been reset. Currently what I have attempted:
Code: Select all
pop ax
mov cl, al ; Attempting to put popped value in CL
; Push back onto stack
push ax
This however, does not work. How come?
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 2:48 am
by iansjack
twtty wrote:Is it not so that the stack builds downwards rather than upwards?
That is correct. The instruction PUSH AX
1. Subtracts 2 from the value in SP.
2. Stores the word in AX in the memory location now pointed to by SP.
If SP starts out as 0 this will result in a new SP value of 0xFFFE with the contents of AX stored in the memory location SS:0xFFFE.
This however, does not work. How come?
"Does not work" is a little vague. Exactly what doesn't work?
I think you should try running snippets of code in a debugger, single-stepping through the code and inspecting the values in registers and memory at each step. Then you will get a grasp of what is going on.
As far as this question is concerned, that's it from me. This forum is not really intended to be a primer on assembler programming. But a Google search will reveal several excellent tutorial and books that will teach you the basics and beyond.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 5:24 am
by twtty
As far as this question is concerned, that's it from me. This forum is not really intended to be a primer on assembler programming. But a Google search will reveal several excellent tutorial and books that will teach you the basics and beyond.
Thank you for your kind help! After some fiddling with it I managed to get it to work. I do have one final question thought: how come the arguments I am sending to function are located two bytes into the stack? That is, how come I need to subtract the SP by two in order to be at where the arguments are stored? Is it the function call itself that is also stored in the stack, or is it something else?
Here is what I do:
Code: Select all
add sp, 2
pop ax ; Correct value popped into AX (previously pushed argument)
mov cl, al
push ax
sub sp, 2
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 5:36 am
by SpyderTL
Yes, calling a subroutine/function in the same segment is called a "near call", and it pushes the 2 byte return address onto the stack. The return from near call instruction simply pops the top 2 bytes and puts them in the IP register.
If you call a function in a different segment, that is a far call. In this case, the return address and the return segment are pushed onto the stack.
Edit: Also, there is no reason to push AX back onto the stack. You can simply just subtract 2 from SP.
Or better yet, don't modify SP at all, and use stack pointer offsets to read the incoming values.
You can even pass the amount of memory passed to the function to the RET instruction, which will pop the top 2 bytes to IP, and then add the specified value to SP, effectively "removing" those values from the stack.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 5:55 am
by iansjack
SpyderTL wrote:Or better yet, don't modify SP at all, and use stack pointer offsets to read the incoming values.
Even better is the 64-bit ABI which passes parameters to function calls in registers (within reason) rather than using the stack. Of course, this relies upon the CPU having a reasonable number of registers which is why it wasn't used in earlier iterations of the processor.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 6:40 am
by twtty
Or better yet, don't modify SP at all, and use stack pointer offsets to read the incoming values.
SP however cannot be used in this, correct? Rather BX or BP, SI or DI has to be used for addressing as such.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 6:44 am
by onlyonemac
SpyderTL wrote:Edit: Also, there is no reason to push AX back onto the stack. You can simply just subtract 2 from SP.
Or better yet, don't modify SP at all, and use stack pointer offsets to read the incoming values.
I tend to find that using push/pop to work with the stack rather than fiddling with the stack pointer makes for cleaner code (as, for example, one can ignore the size of the values on the stack and the code better reflects its function), although I believe that working with the stack pointer directly might make the code faster and that's probably why a lot of compilers do that.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 7:37 am
by iansjack
I suspect that compilers work that way because that is how they have to access local variables and it makes sense to treat parameters in the same way. Particularly as the local variables are allocated lower in the stack than the function parameters.
Re: Using stack for arguments
Posted: Sat Feb 20, 2016 9:51 am
by onlyonemac
iansjack wrote:I suspect that compilers work that way because that is how they have to access local variables and it makes sense to treat parameters in the same way. Particularly as the local variables are allocated lower in the stack than the function parameters.
True, it didn't occur to me that local variables are stored on the stack but I guess that's probably why, because then local variables and parameters can be treated the same way (i.e. determine offset of variable from stack pointer, then use that offset everywhere that you want to access that variable) without caring about how they got onto the stack (other than is needed to calculate the offset).
Re: Using stack for arguments
Posted: Mon Feb 22, 2016 12:01 am
by SpyderTL
twtty wrote:Or better yet, don't modify SP at all, and use stack pointer offsets to read the incoming values.
SP however cannot be used in this, correct? Rather BX or BP, SI or DI has to be used for addressing as such.
You can do [SP+/-] offsets in 32-bit mode. I'll have to look up whether you can do it in 16-bit mode. If NASM complains, then you are probably right.