寄存器及数据存储
CPU组成
- 运算器进行信息处理
- 寄存器进行信息存储
- 控制器协调各种器件进行工作
- 内部总线先实现 CPU 内各个器件之间的联系
寄存器
寄存器是 CPU 内部的信息存储单元。
8086 CPU 有 14 个寄存器:
- 通用寄存器:AX, BX, CX, DX
- 变址寄存器:SI,DI
- 指针寄存器:SP,BP
- 指令指针寄存器: IP
- 段寄存器: CS, SS, DS, ES
- 标志寄存器: PSW
8086 CPU 所有的寄存器都是 16 位的,可以存放两个字节。
通用寄存器
一个 16 位寄存器存储一个 16 位的数据
每一位保存一个二进制位能保存的最大值为
2
16
−
1
2^{16} - 1
216−1 (FFFFH)
如何兼容 8 位寄存器环境下编写的程序?
通用寄存器均可以分为两个独立的 8 位寄存器使用,AX 可以分为 AH 和 AL,BX, CX, DX 同理
“字”在寄存器中的存储
8086 是 16 位 CPU,字长 为 16 bit
一个字可以存在一个 16 位寄存器中
- 这个字的高位字节存在这个寄存器的高 8 位寄存器
- 这个字的低位字节存在这个寄存器的低 8 位寄存器
mov指令
- MOV (通用寄存器) , (数据)
- MOV (寄存器) , (寄存器)
- MOV (寄存器) , (内存单元)
- MOV (内存单元) , (寄存器)
- MOV (段寄存器) , (通用寄存器)
汇编指令不区分大小写
数值默认为 10 进制,写入 16 进制需在末尾添加 ‘H’
在低位寄存器出现溢出时,不会进位到高位
确定物理地址的方法
CPU 访问内存单元时要给出内存单元的地址。
所有内存单元构成的存储空间是一个一维的线性空间。
每一个内存单元都有唯一的地址,叫物理地址。
8086 有 20 位地址总线,可传送 20 位地址,寻址能力是
2
20
=
1
M
2^{20} = 1 M
220=1M 。
8086 是 16 位结构的 CPU,运算器一次最多处理 16 位的数据,寄存器的最大宽度为 16 位。
在 8086 内部处理的、传输、暂存的地址也是 16 位,寻址能力只有 64 KB
如何处理地址总线 20 位的寻址能力受限于 16 位地址长度这一问题?
用两个 16 位地址(段地址和偏移地址)合成一个 20 位的物理地址。
地址加法器合成物理地址的方法:
物理地址
=
段地址
∗
16
+
偏移地址
物理地址 = 段地址 * 16 + 偏移地址
物理地址=段地址∗16+偏移地址
内存的分段表示法
内存并没有分段,段的划分来自于 CPU
段地址 : 偏移地址
同一段内存可以有多种分段方案
- 段地址 ∗ 16 段地址 * 16 段地址∗16 必然是 16 的倍数, 所以一个段的起始地址也一定是 16 的倍数。
- 偏移地址为 16 位,16 位地址的寻址能力为 64K,所以一个段的长度最大为 64K。
- 可以用不同的段地址和偏移地址形成同一个物理地址。
8086 有丰富的取址方式,因此偏移地址可以用多种方法提供。
段寄存器
段寄存器存放段地址,段寄存器只能由寄存器赋值,不支持用字面值直接赋值。
8086 CPU 有 4 个段地寄存器
- CS 代码段寄存器
- DS 数据段寄存器
- SS 栈段寄存器
- ES 附加段寄存器
Debug
Debug 是 DOS 系统中著名的调试程序。
使用 Debug 程序,可以查看 CPU 各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程序的运行。
常用 Debug 命令
- R 命令 查看、改变 CPU 寄存器的内容
-R 查看所有寄存器内容
-R (寄存器名) (数据) 修改特定寄存器中的数据 - D 命令 查看内存中的内容
-D 列出预设地址内存处的 128 个字节的内容,每次偏移 128 个字节
-D (段地址:偏移地址) 列出内存中指定地址处的内容
-D (段地址:偏移地址) (结尾偏移地址) 列出内存中指定地址范围内的内容 - E 命令 改变内存中的内容
-E (段地址:偏移地址) (数据1) (数据2) … 改变内存中的内容
-E (段地址:偏移地址) 逐个询问式修改,空格代表接受并继续,回车代表修改结束 - U 命令将内存中的机器指令翻译成汇编指令
-U (段地址:偏移地址) - A 命令以汇编指令的格式在内存中写入机器指令
-A (段地址:偏移地址) 逐行输入汇编指令 - T 命令执行机器指令
-T 执行 CS:IP 处的指令 - Q 命令推出 Debug
CS、IP 与代码段
CPU 将内存中 CS:IP 指向的内容当作指令执行。
8086 读取和执行指令
- 从 CS:IP 指向内存单元读取指令,读取的指令进入指令缓冲器
- IP = IP + 所读指令的长度,从而指向下一条指令
- 执行指令,跳转到步骤 1
- 重复此过程
jmp 指令
执行何处的指令,取决于 CS:IP 指向的数据
可以通过改变 CS 和 IP 中的内容,来控制 CPU 要执行的目标指令
Bebug 可以改变 CS 和 IP 的值,但是 Debug 是调试手段,并非程序方式。
8086 CPU 不提供对 IP 修改的指令,不能使用 MOV 指令修改,只能由 CPU 自行修改
使用转移指令 jmp
- jmp (段地址:偏移地址) 用段地址修改 CS ,偏移地址修改 IP
- jmp (某一合法寄存器) 用某一合法寄存器的数据修改 IP
内存中”字“的存储
8086 CPU 中,16 位作为 1 个字。
16 位的字,高 8 位存放高字节,低 8 位存放低字节,从而实现存储在 1 个 16 位的寄存器中。
字单元
子单元由两个地址连续的内存单元组成,存放一个字型数据
在一个字单元中,低地址单元存放低位字节,高地址单元存放高位字节
用 DS 和 [addres] 实现字的传送
CPU 要读取一个内存单元的时候,必须给出这个内存单元的地址
在 8086 PC 中,内存地址由段地址和偏移地址组成(段地址:偏移地址)
用 DS 和 [address] 配合,用 DS 寄存器存放要访问的数据的段地址
偏移地址用 […] 形式给出
MOV 指令中,只写 [address] 时,默认段地址就是 DS 。
DS 和数据段
对内存单元中数据的访问
对于 8086 , 可以根据需要将一组内存单元定义为一个段
- 物理地址 = 段地址 ∗ 16 + 偏移地址 物理地址 = 段地址 * 16 + 偏移地址 物理地址=段地址∗16+偏移地址
- 将一组长度为
N
(
N
≤
64
K
)
N(N \leq 64K)
N(N≤64K) 、地址连续、起始地址为 16 的倍数的内存单元当作专门存储数据的内存空间,从而定义了一个数据段。
用 DS 存放数据段的段地址,用指令访问数据段中的具体单元,单元地址由 [address]指出。
加法 add 和减法 sub 指令
- add (寄存器) , (数据)
- add (寄存器) , (寄存器)
- add (寄存器) , (内存单元)
- add (内存单元) , (寄存器)
段寄存器不能作为被加数,add 指令的参数不能全为内存单元 - sub (寄存器) , (数据)
- sub (寄存器) , (寄存器)
- sub (寄存器) , (内存单元)
- sub (内存单元) , (寄存器)
段寄存器不能作为被减数,sub 指令的参数不能全为内存单元。
栈及栈操作的实现
栈是一种只能在一端进行插入或删除操作的数据结构。
栈有两个基本的操作
- 入栈:将一个新的元素放到栈顶
- 出栈:从栈顶取出一个元素
栈顶元素总是最后入栈,需要出栈时,又最先被从栈中取出。
栈的操作规则:LIFO (Last In First Out ,后进先出)
CPU 提供的栈机制,支持用栈的方式访问内存空间,可以将一段内存当作栈来使用。
PUSH (寄存器) 将寄存器中的数据送入栈,实质上就是一种内存传送指令,与 MOV 不同的是,PUSH 指令访问的内存单元的地址不作为参数,而是由 SS:SP 所指向的。
POP (寄存器) 从栈顶取出数据送入寄存器,实质同 PUSH。
栈顶寄存器 SS 存放栈顶的段地址,栈顶指针寄存器 SP 存放栈顶的偏移地址,
SS:SP 任意时刻都指向栈顶元素。
栈指令执行过程
- PUSH (寄存器)
- SP = SP - 2
- 将寄存器中的内容送入 SS:SP 指向的内存单元处,SS:SP 此时指向新栈顶
- POP (寄存器)
- 将 SS:SP 指向的内存单元处的数据送入寄存器中
- SP = SP + 2
- SS:SP 指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。
栈顶越界问题
栈空时使用 POP 指令或栈满时使用 PUSH 指令,都会导致栈顶越界
8086 CPU 不保证对栈的操作不会越界,需要在编程时注意。