1:栈是一种先进后出的操作
栈(比喻:碟盘子)。
8086cpu提供相关的指令来以栈的方式访问内存空间
也就是说:在基于8086cpu编程 的时候,可以将一段内存当作栈来使用
2:入栈和出栈指令:
PUSH 入栈
POP 出栈
例:
push ax : 将寄存器ax中的数据送入栈中
pop ax: 从栈顶取出数据送入ax
8086cpu的入栈和出栈操作以 字 为单元
3:8086cpu的栈操作:
出栈操作pop ax ,ax里面的值变为1122,此时,1000AH和1000BH里的数据还存在的,只是栈顶指针移动了,里面的数据下一次push操作的时候将会覆盖它。
出栈操作pop bx ,bx里面的值变为2266,
出栈操作pop cx ,cx里面的值变为0123,
4:
困惑?
1:cpu怎么栈顶一段内存空间被当做栈使用?
2:执行push和pop的时候,如何知道那个单元是栈顶单元?
根据前面所学知道,cpu怎么知道当前要执行的指令的位置?答: 寄存器CS和IP中存放当前指令的段地址和偏移地址
还有 DS:偏移地址 ----指向---> 数据。
所以栈也有段寄存器SS和寄存器SP.
堆栈段寄存器 SS(Stack Segment) :存放栈顶的段地址
寄存器SP: 存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素
当执行push和pop的时候,栈顶单元改变,cpu怎么知道当前的栈顶单元?
例如:push ax(先偏移地址减-2,后放数据)
(1)SP=SP-2
(2)将ax中的内容送入SS:SP指向的内存单元,SS:SP此时是新栈顶
pop ax 则是相反,先取出ss:sp指向的内存单元的数据放到ax里面,然后SP+2;
任意时刻,SS:SP指向栈顶元素,当栈位空的时候,栈中没有元素,也就不存在栈顶元素
所以,SS:SP只能指向栈的最底部单元下面的单元,此单元的偏移地址为栈最底部的字单元的偏移地址+2
例如:栈最底部字单元的地址为1000:000E,所以栈空时,SP=0010H.
5:栈顶越界的问题:
当栈满的时候在push指令入栈,或者栈空的时候在使用pop指令出栈,都将发生栈顶超界的问题,栈顶超界是危险的
所以,在编程的时候要小心操作栈顶超界的问题,要根据可能用到的最大栈空间,安排栈的大小,防止栈数据太多导致的越界;
堆(heap):是由程序员分配的,是零碎的内存,堆最后一定要释放内存。
栈与内存:
栈空间是内存空间的一部分,它只是一段可以特殊(先进后出FILO(First-In/Last-Out))方式进行访问的内存空间
6: push和pop指令
push和pop指令可以在寄存器和内存直接传送数据的。
push和pop指令的格式(1)
push 寄存器:将一个寄存器中的数据入栈 例如:push ax
pop 寄存器:出栈,用一个寄存器接受出栈的数据 例如:pop bx
push和pop指令的格式(2)
push 段寄存器:将一个段寄存器中的数据入栈 例如: push ds
pop 段寄存器:出栈,用一个段寄存器接受出栈的数据 例如:pop es (ES:附加段寄存器)
push和pop指令的格式(3)
push 内存单元:将一个内存单元的字入栈 (栈操作都是以字为单元). 例如:push [0] (也就是 push ds:0)
pop 内存单元:出栈,用一个内存单元接受出栈的数据. 例如:pop [2] (也就是 pop ds:2)
执行指令时,cpu要知道内存单元的地址,可以在push,pop指令中给出内存单元的偏移地址,段地址在指令执行时,cpu从ds中取得.
7:问题:如果要在10000H处写入字型数据2266H,可以用下面的代码实现:
mov ax,1000H
mov ds,ax //把内存单元的地址通过ax中介放到段地址中
mov ax,2266H
mov [0],ax //把寄存器的数据 放入内存单元(1000:0)中
还可以用下面的代码完成这个功能,不能使用"mov 内存单元,寄存器 ’这类指令
mov ax,1000H
mov ss,ax //栈的寄存器段地址是1000H
mov sp,2 //栈的栈顶偏移量要在0的下面,就是高,因为push入栈,先是sp=sp-2,然后在放入数据到栈中,ss:sp指向栈顶元素
mov ax,2266H
push ax
结论:
push,pop实质上是一种内存传送指令,可以在寄存器和内存直接传送数据,
与mov指令不同的是,push和mov指令访问的内存的单元地址不是在指令中给出的,而是由SS:SPd指出的。
同时,push和pop还要改变sp中内容.
执行push时,先改变sp=sp-2,后向SS:SP处传送
执行pop, 先读取SS:SP处的数据,后改变SP=sp+2
8注意:
(1)栈的变化范围最大为:0~FFFFH(FFFFH=65535(D),栈的操作是16位模式,2^16=64KB)
(2)SS:SP指向栈顶,改变SP(sp=sp-2)后写入内存 是入栈指令 ;读取内存后改变SP(sp=sp+2) 是出栈指令
9栈的小结:
(1)8086cpu提供了栈操作机制:
在SS:SP存放栈顶的段地址和偏移地址;
提供入栈和出栈指令,根据SS:IP指向的地址,按照栈的方式访问内存单元
(2)push指令的步骤: <1> sp=sp-2; <2> SS:SP指向的字单元中传入数据
(3)pop指令步骤:<1>从SS:SP指向的字单元读取数据 ; <2>sp=sp+2
(4)任意时刻,SS:SP指向栈顶元素
(5)8086cpu只记录栈顶,栈空间大小有我们自己管理
(6)push,pop实质上是一种内存传送指令
10: 栈段
前面讲过,对于8086cpu,在编程时,可以根据需要,在一组内存单元中定义一个段
所以,我们可以将长度为N(N<=64KB)的一组地址连续,起始地址为16的倍数的内存单元,当做栈的空间使用,从而定义一个栈段。
cpu如何把我们定义的栈段当做栈空间来访问呢? 就要指定 SS:SP指向我们定义的栈段.