第三章
第 3 章 寄存器(内存访问)
3.1 内存中字的存储
- CPU 中,用 16 位寄存器来存储一个字。高 8 位存放高位字节,低 8 位存放低位字节。
举例:用 0,1 两个内存单元存放数据 2000(4E20H), 用 2,3 两个内存单元存放数据 18(0012H)。
- 字单元:即存放一个字型数据(16 位)的内存单元,由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节,低地址内存单元中存放字型数据的低位字节。
- 将起始地址为 N 的字单元简称为 N 地址单元。
举例:比如一个字单元由 2,3 两个内存单元组成,则这个字单元的起始地址为 2,我们可以说这是 2 地址。
对于图3.1:
(1) 0地址单元中存放的字节型数据是多少?
(2) 0地址字单元中存放的字型数据是多少?
(3) 2地址单元中存放的字节型数据是多少?
(4) 2地址字单元中存放的字型数据是多少?
(5) 1地址字单元中存放的字型数据是多少?
思考后看分析。
分析:
(1) 0地址单元中存放的字节型数据:20H;
(2) 0地址字单元中存放的字型数据: 4E20H;
(3) 2地址单元中存放的字节型数据:12H;
(4) 2地址字单元中存放的字型数据:0012H;
(5) 1地址字单元,即起始地址为1的字单元,它由1号单元和2号单元组成,用这 两个单元存储一个字型数据,高位放在2号单元中,即:12H,低位放在1号单元中,即:4EH,它们组成字型数据是124EH,大小为:4686。
- 任何两个地址连续的内存单元,N号单元和N+1号单元,可以将它们看成两个内存单元,也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。
3.2 DS 和 [address]
- CPU 要先给出一个内存单元的地址,才能读写这个内存单元。
- 在 8086PC 中,内存地址由段地址和偏移地址组成。8086CPU 中 DS 寄存器通常用来存放要访问数据的段地址。
举例:比如我们要读取 10000H 单元的内容,可以执行如下程序段:
mov bx,1000H mov ds,bx mov al,[0]
上面的3条指令将10000H(1000:0)中的数据读到al中。
- mov 指令传送:
- 将数据直接送入寄存器
- 将一个寄存器中的内容送到另一个寄存器中
- 将一个内存单元中的内容送到一个寄存器中
- “[···]” 表示一个内存单元, “[···]” 中的 0 表示偏移地址。指令执行时,8086CPU 自动取 ds 中的数据为内存单元的段地址。
- 8086CPU 不支持将数据直接送入段寄存器的操作,需要用一个寄存器中转。
举例:写几条指令,将 al 中的数据送入内存单元 10000H 中。
mov bx,1000H mov ds,bx mov [0],al
3.3 字的传送
- 8086CPU 是 16 位结构,有 16 根数据线,所以,可以一次性传送 16 位数据,即一个字。
举例:
mov bx,1000H mov ds,bx mov ax,[0] ;1000:0 处的字型数据送入 ax mov [0],cx ;cx 中的 16 位数据送到 1000:0 处
举例:
举例:
3.4 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 寄存器,段寄存器 | 比如:mov ax,ds |
mov 段寄存器,内存单元 |
- add 和 sub 指令同 mov 一样,都有两个操作对象,有以下几种形式:
形式 | 例子 |
---|---|
add寄存器,数据 | 比如:addax,8 |
add寄存器,寄存器 | 比如:addax,bx |
add寄存器,内存单元 | 比如:addax,[0] |
add内存单元,寄存器 | 比如:add[0],ax |
sub寄存器,数据 | 比如:sub ax,9 |
sub寄存器,寄存器 | 比如:subax,bx |
sub寄存器,内存单元 | 比如:subax,[0] |
sub内存单元,寄存器 | 比如:sub[0],ax |
3.5 数据段
- 对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。将一组长度为N(N <= 64KB)、地址连续、起始地址为16的倍数
的内存单元当作专门存储数据的内存空间,从而定义了一个数据段.
举例:比如用123B0H〜123B9H这段内存空间来存放数据,我们就可以认为,123B0H〜123B9H这段内存是一个数据段,它的段地址为 123BH,长度为 10 个字节。
举例:
举例:
3.6 栈
- 栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。
举例:盒子代表栈,书代表数据
- 栈有两个基本的操作:入栈和出栈。
- 入栈就是将一个新的元素放到栈顶,出栈就是从栈顶取出一个元素。栈顶的元素总是最后入栈,需要出栈时,又最先被从栈中取出。
- 栈的这种操作规则被称为:LIFO(Last In FirstOut,后进先出)。
3.7 CPU 提供的栈机制
- 8086CPU提供入栈和出栈指令,最基本的两个是PUSH(入栈)和POP(出栈)。
比如,push ax表示将寄存器ax中的数据送入栈中,pop ax表示从栈顶取出数据送入ax.8086CPU的入栈和出栈操作都是以字为单位进行的。
举例:将 10000H~1000FH 这段内存当作栈来使用。
- 8086CPU中,CS、IP中存放着当前指令的段地址和偏移地址。段寄存器SS和寄存器SP,栈顶的段地址存放在SS中,偏移地址存放在SP中。
- 任意时刻,SS:SP 指向栈顶元素.push指令和pop指令执行时,CPU从SS和SP中得到栈顶的地址。
举例:
从图中我们可以看出,8086CPU中,入栈时,栈顶从高地址向低地址方向増长。
举例:栈空的状态
举例:pop 指令的执行过程
原数据依然存在,不属于栈,新数据重新入栈时覆盖旧数据。
3.8 栈顶超界的问题
举例:执行 push 后栈顶超出栈空间
举例:执行 pop 后栈顶超出栈空间
- 栈顶越界非常危险,有可能改写栈外空间的数据,引发一系列意外错误。
- 8086CPU 没有检查栈顶越界的寄存器,它只考虑当前的栈顶在何处,当前要执行的指令是哪条。
3.9 push、pop 指令
- push、pop 指令形式:
push寄存器 ;将一个寄存器中的数据入栈
pop寄存器 ;出栈,用一个寄存器接收出栈的数据
push段寄存器 ;将一个段寄存器中的数据入栈
pop段寄存器 ;出栈,用一个段寄存器接收出栈的数据
push内存单元 ;将一个内存字单元处的字入栈(注意:栈操作都是以字为单位)
pop内存单元 ;出栈,用一个内存字单元接收出栈的数据
比如:
mov ax,1000H
mov ds,ax ;内存单元的段地址要放在ds中
push [0] ;将1000:0处的字压入栈中
pop [2] ;出栈,出栈的数据送入1000:2处
- 指令执行时,CPU 要知道内存单元的地址,可以在push、pop 指令中只给出内存单元的偏移地址,段地址在指令执行时,CPU 从 ds 中取得。
举例:
举例:
举例:
举例:
-
push、pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的。同时,push和pop指令还要改变SP中的内容。
-
我们要十分清楚的是,push和pop指令同mov指令不同,CPU执行mov指令只需一步操作,就是传送,而执行push、pop指令却需要两步操作,执行push时,CPU的两步操作是:先改变SP,后向SS:SP处传送。执行pop时,CPU的两步操作是:先读取SS:SP处的数据,后改变SP。
-
注意,push, pop等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。
-
提供:SS、SP指示栈顶;改变SP后写内存的入栈指令;读内存后改变SP的出栈指令。这就是 8086CPU 提供的栈操作机制。
3.10 栈 段
- 对于8086PC机,在编程时,可以根据需要,将一组内存单元定义为一个段。
- 我们可以将长度为N(N <= 64KB)的一组地址连续、起始地址为16的倍数
的内存单元,当作栈空间来用,从而定义了一个栈段。
比如,我们将10010H-1001FH这段长度为16字节的内存空间当作栈来用,以栈的方式进行访问。这段空间就可以称为一个栈段,段地址为1001H,大小为16字节。
- 将一段内存当作栈段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就在执行push、pop等栈操作指令时自动地将我们定义的栈段当作栈空间来访问。
- 将SS:SP指向我们定义的栈段,从而 push、pop 等栈操作指令可以访问我们定义的栈段。
举例:
SP = 0000H。