昨天我们学习了简单寄存器的使用和DEBUG程序的使用
这一篇我们将学习 寄存器的内存访问
寄存器(内存访问)
从访问内存的角度认识学习寄存器i
我们知道一个字的存储分为高字节和低字节,由于内存地址是自上而下向下递增的,所以高位字节从内存分布上看再地位字节的下面
也就是说当我们从0地址开始存放 数值20000(4E20H)
其在内存空间中的顺序为
1 | +---------+----------+----------------------------------- |
这样的分布特点我们称之为 小端序
综上所述,我们知道任何两个地址连续的内存单元,N号单元和N+1号单元,可以将它看成两个内存单元,也可以看成一个地址为N的字单元中的高位字节单元和低位字节单元
DS和[address]
8086CPU中有一个DS寄存器,通常用来存档要访问的数据的段地址。
我们用下面的例子来展示它的用法,比如读取10000H单元的内容:
1 | mov bx,1000H |
我们通过上面的三个指令,实现读取,接下来一一解释
首先是前两句,为什么不能直接
mov ds,1000H
呢?这是8086CPU的硬件问题,我们并不支持此行为,所以我们用一个寄存器来中转
第三句的 […] 又是什么意思呢? […]表示操作对象是一个内存单元,里面的数值代表内存单元的偏移地址
mov,sub,add指令
首先我们需要知道这三个指令的特点:他们都有两个操作对象
MOV指令
我们先看看至今我们所知的mov的用法:
- mov 寄存器,数据
- mov 寄存器,寄存器
- mov 寄存器,内存单元
- mov 内存单元,寄存器
- mov 段寄存器,寄存器
根据这些我们可以合理的猜测一些其他的用法,并使用DEBUG程序来验证:
- mov 内存单元,段寄存器 验证通过
- mov 寄存器,段寄存器 验证通过
- mov 段寄存器,内存单元 验证通过
SUB ADD指令
他们也可以有以下用法:
- add 寄存器,数据
- add 寄存器,寄存器
- add 寄存器,内存单元
- add 内存单元,寄存器
- sub 寄存器,数据
- sub 寄存器,寄存器
- sub 寄存器,内存单元
- sub 内存单元,寄存器
数据段
在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将一组长度为N(N<=64KB)、地址连续、起始地址为16的倍数的内存单元作为专门存储数据的内存空间。从而定义了一个数据段
将一段内存作为数据段,是我们编程时的一种安排,我们可以在具体操作时,用DS存放数据段的段地址,从而进行访问
比如一段数据段 123B0H~123B9H 的内存单元定义为数据段 ,现在要累加这个数据段的前三个内存单元的值
1 | mov ax,123B |
栈
8086CPU 提供相关的指令来以栈的方式访问内存空间。这意味着,在基于8086CPU编程时,可以将一段内存作为栈来使用
8086CPU提供入栈和出栈指令,分别时POP和PUSH
push ax
将ax中的数据送入栈中pop ax
从栈顶取出数据送入ax中
注意: 入栈和出栈的操作都是以字为单位进行的
SS:SP
CPU是怎么知道栈顶的位置呢?
在8086CPU中,有两个寄存器,分别是段寄存器SS 和 寄存器SP,栈顶的段地址存放在SS中,栈顶的偏移地址存放在SP中。任意时刻,SS:SP指向栈顶元素。执行pop和push时,CPU从SS和SP中得到栈顶的地址。
现在我们可以对pop和push进行完整的描述了:
push:
- SP = SP - 2,SS:SP指向当前栈顶前面的单元,以当前栈顶前面的单元为新的栈顶
- 将ax中的内容送入SS:SP指向的内存单元,SS:SP 此时指向新的栈顶
pop:
- 将SS:SP 指向的内存单元处的数据送入ax中
- SP = SP + 2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶
当栈顶的数据出栈之后,其内存单元所存储的数据仍然存在,但其已经不在栈顶中。当再次进行入栈操作时,直接对其数据进行覆盖
pop push指令
栈空间也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间
push,pop可以是指令格式:
- push,pop 寄存器
- push,pop 段寄存器
- push,pop 内存空间
栈顶超界问题
我们在此讨论一个问题,虽然我们可以通过SS 和SP来确保在进行入栈和出栈时找到栈顶。可是怎么保证栈顶不会超出栈空间呢?
当我们把一个空间容量为16个字节的内存空间当作栈时,向其中压入八个字后就已经达到了栈顶,此若是再使用push操作,其数据便会溢出栈空间,覆盖栈以外的数据。
同理当我们已经达到栈底时,我们再进行一次pop操作,我们会把栈空间以下的数据弹出。
以上这些操作我们都称为 栈顶越界问题
如果CPU中有记录栈顶上限和栈底的寄存器,那么可以检测越界问题。但是,在8086CPU中,并没有这个寄存器,因此其不保证我们对栈的操作不会越界。这一点需要操作者自行考虑
栈段
如果我们设置一段内存,将它当作栈,并以栈的形式进行访问,那么我们可以称之为 栈段
这里我们有一个问题,如果将10000H~1FFFFH这段空间当作栈段,SS = 1000,SP = FFFE。也就是说,此时栈段内还有一个数据,如果我们将这个数据进行出栈操作,那么此时SP = ?,栈顶指向哪里?
由于出栈后,SP = SP + 2,栈顶指向最底部单元下面的单元。所以此时SP = 0
那么我们可以说 SP=0,此时即是空栈也是满栈
实验二:用机器指令和汇编指令编程
正如前文所言,我们使用D命令查看内存单元的命令,那么我们有以下疑问:
Debug是靠什么来执行D命令的? 是一段程序
谁来执行这段程序? 用CPU
CPU在访问内存单元时从哪里得到内存单元的段地址? 从段寄存器得到
所以我们得出结论 在处理D命令的程序段中,必须有将段地址送入段寄存器的代码
段寄存器有4个:SS,ES,CS DS,那么将段地址送入那个段寄存器呢?
由于CS要用来指向处理D命令的代码,而SS要作为指向栈顶的代码。再因为一般默认段地址再DS中,所以我们将段地址送入DS中
下一条指令执行了嘛?
我们有这样一个程序:
1 | mov ax,2000 |
我们使用T命令单步执行,看一看发生了什么?

注意看,在执行 mov ss,ax
之后本来应该是
mov sp,10
但是却直接来到了 mov ss,ax
但通过观察SP的值,我们可以知道
mov sp,10
得到了执行,这是为什么呢?
这是因为设计到了之后的一个内容:中断机制
在这里我们只需要知道,T命令在执行修改寄存器 SS 的指令时,下一条指令也被紧接着执行