CPU提供的栈机制
现今的CPU中都有栈的设计,8086CPU也不例外,8086CPU提供入栈和出栈指令,最基本的两个是PUSH(入栈)和POP(出栈),比如,push ax表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax,8086CPU的r入栈和出栈操作都是以字为单位进行的。
我们可以将10000H—10000FH这段内存当作栈来使用
注意,字型数据用两个单元存放,高地址单元存放高8位,低地址单元存放低8位,看到所描述的push和pop指令,是否有一些疑惑,总结下来,大概是这两个问题。
- 其一,我们将10000H—1000FH这段内存当作栈来使用,CPU执行push和pop指令时,将对这段空间按照栈的后进先出的规则进行访问,但是,一个重要问题是,CPU如何知道10000H—1000FH这段空间被当作栈来使用?
- push ax等入栈指令执行时,要将寄存器中的内容放入当前栈顶单元的上方,成为新的栈顶元素,pop ax等指令执行时,要从栈顶单元中取出数据,送入寄存器中,显然,push,pop在执行的时候,必须知道哪个单元是栈顶单元,可是,如何知道?
解决方法:
- 8086CPU中,有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中,任意时刻,SS:SP指向栈顶元素。push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。
push ax的执行,由以下两步完成
- SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶
- 将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶
如图所示:
当初始状态栈是空的时候。
如图所示:
当10000H—1000F这段空间当作栈段,SS=1000H,栈空间大小为16字节,栈最底部的字单元地址为1000:000E,任意时刻,SS:SP指向栈顶,当栈中只有最后一个元素的时候,SS=1000H,SP=000EH,栈为空,就相当于栈中唯一的元素出栈出栈后,SP=SP+2,SP原来为000EH,加2后SP=10H,所以,当栈为空的时候,SS=1000H,SP=0010H。
接下来,我们描述pop指令的功能,例如pop ax
- 将SS:SP指向的内存单元处的数据送入ax中
- SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
如图所示:
栈顶超界问题
SS和SP只是记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶,可是,如何能够保证在入栈,出栈时,栈顶不会超出栈空间?
如图所示:
上面描述了执行push,pop指令时,发生的栈顶超界问题。
栈顶超界是危险的,因为我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了具有其他用途的数据,代码等,这些数据和代码可能是我们自己程序中的,也可能是别的程序中的(毕竟一个计算机系统并不是只有我们自己的程序在运行),但是由于我们在入栈出栈时的不小心,而将这些数据,代码意外地改写了,将会引发一连串的错误。
8086CPU不保证我们对栈的操作不会超界,这点就好像CPU只知道当前要执行的指令在何处(由CS:IP指示),而不知道要执行的指令有什么,从这两点我们可以看出8086CPU的工作原理,它只考虑当前的情况,当前的栈顶在何处,当前要执行的指令是哪一条。
我们在编程时要自己操心栈顶擦超界的问题,要根据可能用到的最大栈空间,来安排栈的大小,防止入栈的数据太多而导致的超界。