Segment registers are used to increase the range of addressable memory from 64K to 1M bytes. But, I have trouble understanding stack segment register (SS) because stack already has two other registers associated to it, sp and bp.
Suppose, I have set SS to 5000h and then decided to initialize the stack by initializing bp and sp registers. Initially the stack should be empty. So, both of sp and bp should have same content initially. Can I initialize sp with any random address or Will I have some restriction?
For Example, is it ok to initialize sp with the address 7000h
In x86, the stack is a LastInFirstOut (LIFO) structure where the SS
segment register marks the start and the stackpointer SP
points directly above the free space on the stack. In memory, the free space is lower than the used space because the stack grows downward. It is this downward expansion that makes talking about the stackpointer as "the top of the stack" confusing because it is counterintuitive for the top to be at the bottom.
In x86-16, the stack can occupy at most 64KB or 65536 bytes. The SP
register which is a 16-bit register can never address anything outside of this stack segment.
Now if your program initialization has these instructions:
mov ax, 5000h
mov ss, ax
mov sp, 7000h
you are telling that the stack is going to be a chunk of 28672 (7000h) bytes starting at linear address 0005'0000h and ending at linear address 0005'6FFFh. At this point in your program you can say that "the stack is empty". And it would be a severe programming error to eg. pop ax
while the SS:SP
register pair has 5000h:7000h.
| 5000h (SS) | 6000h
| |
|<--------------------------------- 64KB ----------------------->|
| This is the stack | This is not part of the stack |
| |
| ^ |
| 0 | SP=7000h 65535 |
In order to place a new item on the stack (push
/ call
/ int
), the stackpointer SP
is lowered and then the new item is written at that address. For removal (pop
/ ret
/ iret
) the item where SP
points at is read and then SP
is raised.
Let's see that in action:
mov cx, 6144
More:
push cx
loop More
Registerwise, only the stackpointer SP
has changed.
| 5000h (SS) | 6000h
| |
|<--------------------------------- 64KB ----------------------->|
| This is the stack | This is not part of the stack |
| xxxxxxxxxxxx |
| ^ |
| 0 | SP=4000h 65535 |
Now removing two thirds of it:
mov cx, 4096
More:
pop ax
loop More
Once again, registerwise only the stackpointer SP
has changed.
| 5000h (SS) | 6000h
| |
|<--------------------------------- 64KB ----------------------->|
| This is the stack | This is not part of the stack |
| xxxx |
| ^ |
| 0 | SP=6000h 65535 |
We can read/write the stack memory just like any other memory. However because of the segmented nature of memory, ordinarily we would need to use the SS:
segment override:
mov ax, [ss:6000h]
mov bx, 6000h
mov ax, [ss:bx]
Or we could just make DS
refer to the stack segment:
mov cx, 5000h
mov ds, cx
mov ax, [6000h]
mov bx, 6000h
mov ax, [bx]
Here begins the strange case of BP
. The designer has made it so that all memory referencing that relies on the BP
register, will be relative to the stack segment by default. We can address data in the stack segment without having to specify a segment override or manipulating the DS
segment register if we load the offset address in BP
:
mov bp, 6000h
mov ax, [bp]
Other than this 'stickyness' to the stack segment, there's nothing special about BP
.