我的汇编学习——基础知识(寄存器内存访问)

1.内存中字的存储

CPU中,用16位寄存器来存储一个字。高8位存放高位字节,低8位存放低位字节。在内存中存储时,由于内存单元是字节单元(一个单元存放一个字节),则一个字要用两个地址连续的内存单元来存放,这个字的低位字节存放在低地址单元中,高位字节存放在高地址单元中。比如我们从0地址开始存放20000,这种情况如图3.1所示。
在这里插入图片描述
在图3.1中,我们用0、1两个内存单元存放数据20000(4E20H)。0、1两个内存单元用来存储一个字,这两个单元可以看作一个起始地址为0的字单元(存放一个字的内存单元,由0、1两个字节单元组成)。对于这个字单元来说,0号单元是低地址单元,1号单元是高地址单元,则字型数据4E20H的低位字节存放在0号单元中,高位字节存放在1号单元中。同理,将2、3号单元看作一个字单元,它的起始地址为2。在这个字单元中存放数据18(0012H), 则在2号单元中存放低位字节12H,在3号单元中存放高位字节
00H。

我们提出字单元的概念:字单元,即存放一个字型数据(16位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。我们将起始地址为N的字单元简称为N地址字单元。比如一个字单元由2、3两个内存单元组成,则这个字单元的起始地址为2,我们可以说这是2地址字单元。

2.DS和[address]

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

mov bx, 1000H                    //先把段地址装入一个通用寄存器中
mov ds,bx                            //在转入段寄存器ds中
mov al, [0]

上面的3条指令将10000H(1000:0)中的数据读到al中。

下面详细说明指令的含义。
mov al, [0]
前面我们使用mov指令,可完成两种传送:①将数据直接送入寄存器;②将一个寄存器中的内容送入另一个寄存器。
也可以使用 mov 指令将一个内存单元中的内容送入一一个寄存器中。 从哪一个内存单元送到哪一个寄存器中呢?在指令中必须指明。寄存器用寄存器名来指明,内存单元则需用内存单元的地址来指明。

显然,此时mov指令的格式应该是:mov 寄存器名,[内存单元地址]。
“[…]”表示一个内存单元,“[0]” 中的0表示内存单元的偏移地址。 我们知道,只有偏移地址是不能定位一个内存单元的,那么内存单元的段地址是多少呢?指令执行时,8086CPU自动取ds中的数据为内存单元的段地址。

再来看一下,如何用mov指令从10000H 中读取数据。10000H 用段地址和偏移地址表示为1000:0,我们先将段地址1000H放入ds,然后用mov al,[0]完成传送。
mov指令中的[ ]说明操作对象是一一个内存单元,[0]中的0说明这个内存单元的偏移地址是0,它的段地址默认放在ds中,指令执行时,8086CPU 会自动从ds中取出。
mov bx, 1000H
mov ds,bx
若要用mov al,[0]完成数据从1000:0单元到al的传送,这条指令执行时,ds 中的内容应为段地址1000H, 所以在这条指令之前应该先将1000H送入ds。

如何把一个数据送入寄存器呢?我们以前用类似“mov ax,1” 这样的指令来完成,从理论上讲,我们可以用相似的方式:mov ds,1000H, 来将1000H 送入ds。可是,现实并非如此,8086CPU不支持将数据直接送入段寄存器的操作,ds是一个段寄存器,所以mov ds, 1000H这条指令是非法的。那么如何将1000H 送入ds呢?只好用一个寄存器来进行中转,即先将1000H送入一个一般的寄存器,如bx,再将bx中的内容送入ds。

3.字的传送

前面我们用mov指令在寄存器和内存之间进行 字节型数据 的传送。因为8086CPU是16位结构,有16根数据线,所以,可以一次性传送16位的数据,也就是说可以一次性传送一一个字。只要在mov指令中给出16位的寄存器就可以进行16位数据的传送了;如果给出的是8位寄存器,那么进行的就是8位数据的传送。 比如:

mov bx, 1000H
mov ds, bx              ;初始化ds段寄存器
mov ax, [0]              ;1000:0处的字型数据送入ax
mov [0] ,CX             ;CX中的16位数据送到1000:0处

@问题3.3@
内存中的情况如图3.2所示,写出下面的指令执行后寄存器ax,bx,cx中的值。
在这里插入图片描述
分析:
在这里插入图片描述
@问题3.4@
内存中的情况如图3.3所示,写出下面的指令执行后内存中的值,思考后看分析。
mov ax, 1000H
mov ds, ax
mov ax, 11316
mov [0], ax
mov bx, [0]
sub bx, [2]
mov [2] , bx
在这里插入图片描述
在这里插入图片描述

mov、add、sub指定

前面我们用到了mov、add、sub 指令,它们都带有两个操作对象。
到现在,我们知道,mov指令可以有以下几种形式。

mov 寄存器,数据                        比如: mov ax,8
mov 寄存器,寄存器                    比如: mov ax,bx
mov 寄存器,内存单元				 比如: mov ax,[0]
mov 内存单元,寄存器                比如: mov [0],ax
mov 段寄存器,寄存器                比如: mov ds,ax
mov 寄存器, 段寄存器

在这里插入图片描述

5.数据段

前面讲过,对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。我们可以将一组长度为NQN≤64KB)、地址连续、起始地址为16 的倍数的内存单元当作 专门存储数据 的内存空间,从而定义了一个数据段。 比如用123B0H—123B9H这段内存空间来存放数据,我们就可以认为,123B0H~123B9H 这段内存是一个数据段,它的段地址为123BH,长度为10个字节。如何访问数据段中的数据呢?将一段内存当作数据段,是我们在编程时的一种安排,可以在具体操作的时候,用ds存放数据段的段地址,再根据需要,用相关指令访问数据段中的具体单元。
比如,将123B0H~123B9H的内存单元定义为数据段。现在要累加这个数据段中的 前3个单元(字节单元) 中的数据,代码如下。
在这里插入图片描述
现在要累加这个数据段中的 前3个字单元中的字型数据,代码如下。
在这里插入图片描述
注意,一个字型数据占两个单元,所以偏移地址是0、2、4。
只要在mov指令中给出16位的寄存器就可以进行16位数据的传送了;如果给出的是8位寄存器,那么进行的就是8位数据的传送。

6.栈

栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。

CPU提供的栈机制

8086CPU提供入栈和出栈指令,最基本的两个是 PUSH(入栈) 和 POP(出栈) 。比如:
push ax 表示将寄存器 ax 中的数据送入栈中
pop ax 表示从当前 栈顶 取出数据送入ax。

@8086CPU的 入栈 和 出栈 操作都是以 字为单位 进行的。@
下面举例说明,我们可以将 10000H~1000FH 这段内存当作栈来使用。

下面描述了一段指令的执行过程。
在这里插入图片描述
在这里插入图片描述
注意,字型数据用两个单元存放,高地址单元存放高8位,低地址单元存放低8位。

看到上图所描述的push和pop指令的执行过程,是否有一些疑惑?总结一下,大概是这两个问题。

其一,CPU如何知道10000H~1000FH这段空间被当作栈来使用?
其二,push ax 等入栈指令执行时,要将寄存器中的内容放入当前栈项单元的上方,成为新的栈顶元素;pop ax 等指令执行时,要从栈顶单元中取出数据,送入寄存器中。显然,push、pop 在执行的时候,必须知道哪个单元是栈顶单元,可是,如何知道呢?

这不禁让我们想起另外一个讨论过的问题,就是,CPU如何知道当前要执行的指令所在的位置?我们现在知道答案,那就是CS、IP中存放着当前指令的段地址和偏移地址。现在的问题是:CPU如何知道栈顶的位置?显然,也应该有相应的寄存器来存放栈顶的地址,8086CPU 中,有两个寄存器,段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。任意时刻,SS:SP 指向栈顶元素。push 指令和 pop 指令执行时,CPU从SS和SP中得到栈顶的地址。

现在,我们可以完整地描述push和pop指令的功能了,例如push ax。

@push ax 的执行,由以下两步完成。@
(1)SP=SP - 2,SS:SP 指向当前栈项 前面 的单元,以当前栈顶 前面 的单元为新的栈项; (2)将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶。

在这里插入图片描述
从图中我们可以看出,8086CPU中,入栈时,栈顶从高地址向低地址方向增长。

问题3.6
如果将10000H~1000FH这段空间当作栈,初始状态栈是空的,此时,SS=1000H,SP=?
分析:
在这里插入图片描述
栈最底部的字单元地址为10000E。 任意时刻,SS:SP 指向栈顶,当栈中只有一个元素的时候,SS=1000H, SP=000EH。 栈为空,就相当于栈中唯一的元素出栈,出栈后,SP=SP+2,SP原来为000EH,加2后SP=10H, 所以,当栈为空的时候,SS=1000H,SP=10H。

换一个角度看,在任意时刻,SS:SP 指向栈顶元素,当栈为空的时候,栈中没有元素,也就不存在栈项元素,所以SS:SP只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2,栈最底部字单元的地址为1000:000E,所以栈空时,SP=0010H。

接下来,我们描述pop指令的功能,例如
pop ax。

@pop ax 的执行过程和push ax刚好相反,由以下两步完成。@
(1)将SS:SP指向的内存单元处的数据送入ax中; (2)SP=SP+2,SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。

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

8.栈顶的超界问题

我们现在知道,8086CPU 用SS和SP指示栈顶的地址,并提供push和pop指令实现入栈和出栈。但是,还有一个问题需要讨论,就是SS和SP只是记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶。可是并不能够保证在入栈、出栈时,栈顶不会超出栈空间。

9.push、pop指令

前面我们一直在使用push ax和pop ax,显然push和pop指令是可以在寄存器和内存(栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊的方式进行访问的内存空间。)之间传送数据的。

push和pop指令的格式可以是如下形式:

push 寄存器                   ;将一个寄存器中的数据入栈
pop 寄存器                     ;出栈,用一个寄存器接收出栈的数据

当然也可以是如下形式:
push和pop也可以在内存单元和内存单元之间传送数据,我们可以:

push [内存单元]					;将一个内存字单元处的字入栈(注意:栈操作都是以字为单位)
pop [内存单元]					;出栈,用一个内存字单元接收出栈的数据

比如:
在这里插入图片描述
指令执行时,CPU要知道内存单元的地址,可以在push、pop指令中只给出内存单元的偏移地址,段地址在指令执行时,CPU 从ds中取得。

问题3.7
在这里插入图片描述
问题3.8
编程:
(1) 将10000H~1000FH这段空间当作栈,初始状态栈是空的;
(2)设置AX=001AH,BX=001BH;
(3)将AX、BX中的数据入栈;
(4)然后将AX、BX清零;
(5)从栈中恢复AX、BX原来的内容。
在这里插入图片描述
在这里插入图片描述
从上面的程序我们看到,用栈来暂存以后需要恢复的寄存器中的内容时,出栈的顺序要和入栈的顺序相反,因为最后入栈的寄存器的内容在栈顶,所以在恢复时,要最先出栈

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值