3.1 内存中的字存储

字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。

将起始地址为N的字单元简称为N地址字单元。比如一个字单元由2、3两个内存单元组成,则这个字单元的起始地址为2,可以说这是2地址字单元。

image-20211130201218289 20000(4E20H)、18(0012H)

3.2 DS和[address]

CPU 要读写一个内存单元的时候,必须先给出这个内存单元的地址,在8086PC 中,内存地址由段地址和偏移地址组成。8086CPU中有一个DS寄存器,通常用来存放要访问数据的段地址。比如我们要读取10000H单元的内容,可以用如下的程序段进行。(从内存单元送到寄存器中)

1
2
3
mov bx,1000H
mov ds,bx
mov al,[0]

“[···]”表示一个内存单元,“[···]”中的0表示内存单元的偏移地址。只有偏移地址是不能定位一个内存单元的,指令执行时,==8086CPU自动取ds中的数据为内存单元的段地址==。

如何把一个数据送入寄存器呢?

==8086CPU 不支持将数据直接送入段寄存器的操作==,ds是一个段寄存器,所以mov ds,1000H这条指令是非法的。那么如何将1000H送入ds呢?只好用一个寄存器来进行中转,即先将1000H送入一个一般的寄存器,如 bx,再将bx中的内容送入ds。

1
2
3
mov bx,1000H
mov ds,bx
mov [0],al

3.3 字的传送

只要在 mov指令中给出16 位的寄存器就可以进行16位数据的传送了。

1
2
3
4
mov bx,1000H
mov ds,bx
mov ax,[0]   ; 1000:0处的字型数据送入ax
mov [0],cx   ; cx中的16位数据送到1000:0处

3.7 CPU提供的栈机制

8086CPU 提供入栈和出栈指令,最基本的两个是PUSH(入栈)和POP(出栈)。比如,push ax表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax。==8086CPU的入栈和出栈操作都是以字为单位进行的==。

CPU 如何知道当前要执行的指令所在的位置?我们现在知道答案,那就是CS、IP中存放着当前指令的段地址和偏移地址。

现在的问题是: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此时指向新栈顶。

图3.10描述了8086CPU对push指令的执行过程。

image-20211130211334658

从图中我们可以看出,8086CPU中,==入栈时,栈顶从高地址向低地址方向增长==。

思考:如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?

SP=0010H,如图3.11所示。

image-20211130213511410

pop ax的执行过程和push ax刚好相反,由以下两步完成。

(1) 将SS:SP指向的内存单元处的数据送入ax中;

(2) SP=SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

图3.12描述了8086CPU对pop指令的执行过程。

image-20211130213622610

注意,图 3.12中,出栈后,SS:SP指向新的栈顶1000EH,pop操作前的栈顶元素,1000CH 处的2266H依然存在,但是,它已不在栈中。当再次执行 push等入栈指令后,SS:SP移至1000CH,并在里面写入新的数据,它将被覆盖。

段的综述

我们可以用一个段存放数据,将它定义为“数据段”;

我们可以用一个段存放代码,将它定义为“代码段”;

我们可以用一个段当作栈,将它定义为“栈段”。

我们可以这样安排,但若要让CPU按照我们的安排来访问这些段,就要:

对于数据段,将它的段地址放在 DS 中,用mov、add、sub等访问内存单元的指令时,CPU就将我们定义的数据段中的内容当作数据来访问;

对于代码段,将它的段地址放在CS中,将段中第一条指令的偏移地址放在IP中,这样CPU就将执行我们定义的代码段中的指令;

对于栈段,将它的段地址放在SS中,将栈顶单元的偏移地址放在SP中,这样CPU在需要进行栈操作的时候,比如执行push、pop指令等,就将我们定义的栈段当作栈空间来用。

Debug

(1)关于D命令

Debug 在执行如d 段地址:偏移地址这种D命令时,将段地址送入ds 中比较方便。

D命令也提供了一种符合CPU机理的格式:d 段寄存器:偏移地址,以段寄存器中的数据为段地址SA,列出从SA:偏移地址开始的内存区间中的数据。以下是几个例子。

image-20211130224915186

(2)在E、A、U命令中使用段寄存器。

在E、A、U这些可以带有内存单元地址的命令中,也可以同D命令一样,用段寄存器表示内存单元的段地址,以下是几个例子。

image-20211130225022187

(3)下一条指令执行了吗?

image-20211130225054566 image-20211130225109010

==Debug 的T命令在执行修改寄存器SS 的指令时,下一条指令也紧接着被执行==(不单是 mov ss,ax,对于如mov ss,bxmov ss,[0]pop ss 等指令都会发生上面的情况,这些指令都是修改栈段寄存器SS的指令)。