内存中字的存储
CPU中,用16位寄存器来存储一个字,一个字要用两个连续的内存单元来存放,这个字的低位字节存放到低地址单元中,高位字节存放在高地址单元中。假如用0、1两个内存单元存放一个字,这两个单元看作一个起始地址为0的字单元。
DS和[address]
8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址。比如我们要读取10000H单元中的内容,可用下面的程序段进行:
mov bx,1000H
mov ds,bx
mov al,[0]
上面的3条指令将10000H(1000:0)中的数据读到al中。我们使用mov指令将一个内存单元中的内容送入一个寄存器中,格式为:mov 寄存器名,内存单元地址。寄存器用寄存器名来指明,内存单元则用内存单元的地址指明。[…]中的0表示内存单元的偏移地址,指令执行时,8086CPU自动取ds中的数据为内存单元的段地址。
我们之所以使用 mov bx,1000H 是因为:8086CPU不支持将数据直接送入段寄存器的操作,所以用一个寄存器来进行中转。
CPU提供的栈机制
现今的CPU中都有栈的设计,8086CPU提供相关的指令来以栈的方式来访问内存空间,在基于8086CPU编程时,可以将一段内存当作栈来使用,CPU并不会由于这种安排,就在执行栈操作相关的指令时自动地将我们定义的栈段当作栈空间来访问。
最基本的指令是PUSH(入栈)和POP(出栈)。比如PUSH ax 表示将寄存器ax中的数据压入栈中,pop ax 表示从栈顶取出数据送入ax。8086CPU的入栈和出栈操作都是以字为单位进行的。
CPU如何知道栈顶的位置?**8086CPU中,有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。push和pop指令执行时,CPU从SS和SP中得到栈顶的地址。举例来说**push ax的执行,由以下两部来完成:
(1)SP=SP-2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶;
(2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。
怎么初始化一个空栈?如果将10000H-1000FH这段空间当作栈,初始状态栈是空的,此时SS=1000H,SP=0010H。因为:栈最底部的字单元地址为1000:000EH,任意时刻,SS:SP指向栈顶,当栈中只有一个元素的时候,SP=000EH。当栈为空时,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2=10H。所以,当栈为空时,SS=1000H,SP=10H。
pop ax的执行,由以下两部来完成:
(1)将SS:SP指向的单元处的数据送入ax中;
(2)PS=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
需要注意的是:pop操作执行后原内存单元处的数据依然存在,但是它不在栈中,当再次执行push等入栈指令后,它将被覆盖。
栈顶超界的问题
8086CPU只知道栈顶在何处(由SS:SP指示),而不知道我们指定的栈空间有多大。所以当栈满的时候在push或者当栈空的时候再使用pop指令,都将发生栈顶超界问题。
push和pop指令
push和pop的对象可以是一般的寄存器、段寄存器、内存单元或者偏移地址。
我们将10000H-1000FH这段空间当作栈,初始状态栈是空的,将ax,bx,ds中的数据入栈。
mov ax,10000H
mov ss,ax
mov sp,0010H ;初始化栈顶
push ax
push bx
push ds
push和pop等栈操作指令,修改的只是SP。也就是说栈顶的变化范围最大为:0-FFFFH,最大长度为64KB。
段的综述
我们对内存的使用,关键在于CPU中寄存器的设置,即CS、IP,SS、SP,DS的指向。