I'm making an operating system for fun, whilist learning more Assembly on the way. I learned about pushing and popping, as I understand, pop takes the value that is on top of any other value on the stack, so it gets the last pushed one. But I wonder, can we actually directly pop the value at the stack base (i.e. the first pushed one)?
Like:
push 'A'
push 'B'
push 'C'
pop bx ; Gets C
Instead of C, I want to get A as an example. (I'm in 16-bit mode, and the assembler is NASM, specified in the tags as well.)
CodePudding user response:
Try to write down the layout of the stack in memory. You'll see that “popping” any element in the middle of the stack would entail shuffling a lot stack elements around in memory. Processors don't have support for that. You can however load any element on the stack into a register at any time without popping it (i.e. it'll still be on the stack afterwards).
To do so, first copy the stack pointer into some register that can be used for addressing memory (bp
is the canonical choice), then access the stack from that.
mov bp, sp ; make the stack accessible
mov bx, [bp 4] ; load stack element A into bx
Usually, bp
is set up as a part of establishing a stack frame for the current function, so you have it always available for accessing the stack. You can also use the registers bx
, si
, and di
, but keep in mind that a ss
segment override is needed in that case (it's implicit for bp
).
Now, why do we have to add 4 to get item A? Recall that the stack grows towards lower addresses in memory (i.e. with each push
, sp
is decreased) and each stack slot occupies 2 bytes of memory in 16 bit mode. So assuming sp = 1000h
after your sequence of pushes, we have the following memory layout:
1004h A
1002h B
1000h C <-- SP
so by simple arithmetic, SP 4
points at stack slot A and SP 2
at stack slot B.
CodePudding user response:
In 16-bit mode, there's no sp
relative addressing mode, but you can copy the value in sp
into, say, bp
, and use bp
relative addressing to access A's location. This won't pop A, of course, but you can read or write A.
This is one reason why x86 functions often have mov bp,sp
in their prologue (function startup code). Once bp
is set up to refer to its stack, a function can access the parameters that its caller pushed using bp
relative addressing (bp
displacement).