第三章 寄存器
3.1 内存中字的存储
任何两个地址连续的内存单元,N号单元和 N+1号单元,可以将它们看成两个内存单元 ,也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元。
3.2 DS和【address】
-
CPU要读取一个内存单元时,必须先给出这个内存单元的地址
-
在8086PC中,内存地址由段地址和偏移地址组成
-
8086CPU中有一个DS寄存器,通常用来存放要访问的数据的段地址
mov al,[0]
已知的mov指令可完成的两种传送功能:
- 将数据直接送入寄存器;
- 将一个寄存器中的内容送入另一个寄存器中。
mov 指令 还可以将一个内存单元中的内容送入一个寄存器。
mov指令的格式
mov 寄存器名,内存单元地址
【……】表示一个内存单元,“【……】”中的0表示内存单元的偏移地址
那么内存单元的段地址是多少呢?
执行指令时,8086CPU自动取DS中的数据为内存单元的段地址。
如何用mov指令从10000H中读取数据?
- 10000H表示为1000:0(段地址:偏移地址)
- 将段地址1000H放入ds
- 用mov al,[0]完成传送(mov指令中的[]说明操作对象是一个内存单元,[]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中)
如何把1000H送入ds?
首先,类似于mov ds,1000H是非法的,8086CPU不支持将数据之间送入段寄存器的操作
一般是数据 ⟶ \longrightarrow ⟶一般的寄存器 ⟶ \longrightarrow ⟶段寄存器
怎样将数据从寄存器送入内存单元?
mov bx,1000H
mov ds,bx
mov 【0】,al
3.3 字的传送
因为8086CPU是16位结构,有16根数据线,所以可以一次性传16位的数据,也就是一次性传送一个字
3.4mov,add,sub指令
已学mov指令的几种形式:
mov 寄存器,数据
mov 寄存器,寄存器
mov 寄存器,内存单元
mov 内存单元,寄存器
mov 段寄存器,寄存器
根据已知指令进行推测:(未验证)
mov 段寄存器,寄存器 ⟶ \longrightarrow ⟶mov 寄存器,段寄存器
mov 内存单元,寄存器 ⟶ \longrightarrow ⟶mov 内存单元,段寄存器、mov 段寄存器,内存单元
add与sub指令
add 寄存器,数据
add 寄存器,寄存器
add 寄存器,内存单元
add 内存单元,寄存器
sub 寄存器,数据
sub 寄存器,寄存器
sub 寄存器,内存单元
sub 内存单元,寄存器
3.5 数据段
我们可以将一组长度为N(N≤64K)、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
如何访问数据段中的数据呢?
将一段内存当作数据段,是我们在编程时的一种安排,我们可以在具体操作的时候 ,用 ds 存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
3.6 栈
我们研究栈的角度:
栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。
栈的基本操作
出栈,入栈
又称为LIFO(last in first out)
3.7 CPU提供的栈机制
CPU有栈的设计,这意味着我们在使用8086CPU编程时可以将一段内存当做栈来使用
栈指令
push(入栈)
out(出栈)
CPU如何知道当前要执行的指令所在的位置?
寄存器CS和IP中存放着当前指令的段地址和偏移地址。
8086CPU中,有两个寄存器:
段寄存器SS 存放栈顶的段地址
寄存器SP 存放栈顶的偏移地址
任意时刻,SS:SP指向栈顶元素。
如果我们将10000H~1000FH 这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=0010H
栈空时:SS:SP指向栈空间最高地址的下一个单元
执行push ax后:SS:SP指向栈中的第一个元素
pop指令的执行过程
pop ax
- 将SS:SP指向的内存单元处的数据送入ax中
- SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶
注意:
出栈后,SS:SP指向新的栈顶 1000EH,pop操作前的栈顶元素,1000CH 处的2266H 依然存在 ,但是,它已不在栈中。
当再次执行push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖。
3.8 栈顶超界问题
栈顶超界是危险的
因为我们既然将一段空间安排为栈 ,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等,这些数据、代码可能是我们自己的程序中的,也可能是别的程序中的。
8086CPU不保证对栈的操作不会超界
因为根据8086CPU工作机理,只考虑当前情况
当前栈顶在何处
当前要执行的指令是哪一条
3.9 push,pop指令
push寄存器:将一个寄存器中的数据入栈
pop寄存器:出栈,用一个寄存器接收出栈的数据
push ax
Pop bx
push段寄存器:将一个段寄存器中的数据入栈
pop段寄存器:出栈,用一个段寄存器接收出栈的数据
push ds
pop es
push内存单元:将一个内存单元处中的字入栈
pop内存单元:出栈,用一个内存字单元接收出栈的数据
push 【0】
pop【2】
指令执行时 ,CPU 要知道内存单元的地址,可以在 push、pop 指令中给出内存单元的偏移地址,段地址在指令执行时,CPU从ds中取得。
结论:
push、pop 实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。
同时,push和pop指令还要改变 SP 中的内容。
另外
push和pop指令同mov指令不同,CPU执行mov指令只需一步操作,就是传送,而执行push、pop指令却需要两步操作。
执行push时:先改变SP,后向SS:SP处传送。
执行pop时:先读取SS:SP处的数据,后改变SP。
3.10 栈段
我们可以将长度为 N(N ≤64K )的一组地址连续、起始地址为16的倍数的内存单元,当作栈来用,从而定义了一个栈段。
比如
比如我们将10010H~1001FH 这段长度为 16 字节的内存空间当作栈来用,以栈的方式进行访问。
这段空间就可以成为栈段,段地址为1000H,大小为16字节。
如何使的如push、pop 等栈操作指令访问我们定义的栈段呢?
将SS:SP指向我们定义的栈段。
一个栈段最大可以设为多少?
所以一个栈段的容量最大为64KB
3.11 段的综述
我们可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。这完全是我们自己的安排。
我们可以用一个段存放数据,将它定义为“数据段”;
我们可以用一个段存放代码,将它定义为“代码段”;
我们可以用一个段当作栈,将它定义为“栈段”;
对于CPU来说
对于数据段,将它的段地址放在 DS中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据段来访问;
对于代码段,将它的段地址放在 CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;
对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地置放在 SP 中,这样CPU在需要进行栈操作的时候,比如执行 push、pop 指令等,就将我们定义的栈段当作栈空间来用。
可见,不管我们如何安排 ,CPU 将内存中的某段内存当作代码 ,是因为CS:IP指向了那里;CPU将某段内存当作栈 ,是因为 SS:IP 指向了那里。
一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么也不是。
关键在于CPU中寄存器的设置,即:CS、IP、SS、SP、DS的指向