汇编语言学习(五):栈
3.6-3.7
栈的规则是LIFO(last in first out)
也就是先进去的就到了栈底,最后放进去的就在表面上,最后放进去的可以最先拿出来。
8086提供入栈和出栈指令,PUSH和POP
以-我们的一段内存可以以栈的方式来访问。执行的时候
1 | mov ax,0123 |
即将寄存器里的数据放到栈中,因为是字型数据,占高八位和第八位,栈底对应的是高地址位,所以把高位01先入栈,到栈底。
向上堆叠时
1 | mov bx,2266 |
会以同样形式向上走一个字型内存。
出栈时用pop先从栈顶出
1 | pop ax |
字型数据用两个单元存放,高地址存放高八位,反之亦然。
CPU如何找到栈顶?
有相应的寄存器来存放栈顶的地址,段寄存器SS和寄存器SP,栈的段地址放在SS中,偏移地址放在SP中。任意时刻,SS:SP指向栈顶元素。push和pop执行时,CPU从SS:SP获得栈顶地址。
当前属性
1 | SS=1000 |
如果初始栈是空的,怎么办?栈顶指针会指向栈空间最高地址的下一个单元,很奇妙。
执行 push ax
之后,SS:SP指向栈中第一个元素
我们来看POP执行过程,先
1 | 当我们按下pop ax时,CPU会先把SS:SP指向内存单元处的数据送入ax, |
注意!pop操作前的栈顶元素在那个内存单元中2266仍然存在,不会消失!但是我们说他已经不在栈中,当再次入栈时,新数据会直接覆盖他!
3.8-栈顶超界问题
如何保证出栈入栈时栈顶不会超出栈空间?
栈空时指向栈最高地址的下一位,如图。如果栈空间有16字节大小,则执行八次 push ax
后,栈空间满,再次执行push
ax中的数据会送入1000E,将栈外空间覆盖!
同理执行八次pop ax后从栈中弹出8个字,栈空,SS:SP指向10020
再次执行pop ax: SP=SP + 2指向10022超出了栈空间,也覆盖到了外面。
注:8086CPU没有解决这些问题,需要我们自己计算,自己考虑,因为栈外空间很可能存了其他内存数据,被改写可能会引起错误!
push,pop一般传参寄存器,但是也可以直接到内存空间等,以下方式都行:
比如:
1 | mov ax,1000 |
可以将段寄存器的数据入栈,也可以出栈到段寄存器
执行指令时,CPU要指导内存单元的地址,可以在push,pop指令中只给出内存单元的偏移地址,段地址在执行指令时,CPU从ds中取得。
下面练习:
1 | mov ax,1000 |
看这个题,如果是交换AX,BX中的数据则出栈时先出给AX即可
1 | mov ax,1000 |
再来看此题:
显然我们栈空时向下进位直接为0000了。
从栈空时SP=0000到栈满时SP=0000,如果再次压栈,将覆盖最初的数据所以说最大容量为(2**4)^4也就是16^4为64KB,即一段的总空间,偏移地址的变化范围。
注意一下知识点:
数据段的段地址:DS,用mov,add等指令访问时,里面的内容当作数据来访问。
代码段的段地址:CS,里面的内容当作指令的地址。
栈段的段地址:SS
例:我们将10000-1001f安排为代码段,并且储存以下代码:
1 | mov ax,1000 |
设置cs=1000,ip=0,这段代码将得到执行,我们在代码中又将10000-1001f安排为栈段和数据段。
即一段内存既可以是代码的存储空间也可以是数据的存储空间。
结束,下面第四章,编译运行exe