🚀
文章目录
感觉越来越能 “画” 了… 🍋
文章中穿插了一些个人的理解,特别是绿色字体部分,如有笔误,烦请留言。
上一篇文章链接: 《【计算机与汇编原理③】——80X86微处理器》.
下一篇文章链接: 《【计算机和汇编原理⑤】——指令系统(下)【 2万5千字总结 】》.
一、概述
● 指令:通知 CPU 执行某种操作的 “命令”。CPU全部指令的集合,称为指令系统。
我们在编写程序的时候,都是一行一行地编写的,每一行一般是个语句,而一条语句一般包含多条指令。比如说,我要执行 “A = B + C;” 这条语句,那么我得依次执行:① 取指令(取数据C)。 ② 取指令(取数据B)。③ 加指令(B和C相加)。④ 放指令(将结果送到A)
● 指令的书写格式:
◆ 目标指令(机器指令):二进制形式的序列 (即:一串0、1代码)。注意:硬件只能识别、存储、运行目标指令。
◆ 符号指令:字符串形式的序列 (即用规定的操作码和操作数助记符,按照规定的书写格式书写的指令。
● 键盘等外设的出现,为简化指令的输入带来了方便。同时,为了便于记忆和输入,在人机交互中,一般不再直接输入机器指令,而是输入对应的采用指令助记符描述的符号指令。
● 指令的组成:(大多数指令)由操作码和操作数两个部分组成。
◆ 操作码:通知CPU执行什么操作 (唯一)
◆ 操作数:指令的操作对象 (一个或多个)
● 举几个关于指令的栗子:
◆ 说明:
① 第一个操作 “1234H → AX” 的意思:将地址为 “1234H” 的寄存器里面的数据,传送到 CPU里面的通用寄存器 EAX 的低 16 位里面去。
② “80486符号指令” 这一栏就是程序员敲的代码,而 “80486机器指令” 就是计算机能读得懂的数据。
③ MOV(MOVE的缩写):传送。ADD: 加。SUB: 减。RET: RETURN 返回。
● CPU 只能识别、存储、运行目标指令,而用机器指令编程非常困难。于是早期的专家们发明了符号指令,再经过某种特殊的软件把符号指令 → 机器指令。目标程序的生成的流程图如下:
● 指令长度:指令在存储器中占用的字节数称为指令长度。86x86指令长度 (指机器指令长度) 为1~16 字节。
● 指令地址:存放指令第一字节的内存单元地址。单字节指令占用1个内存单元,多字节指令占用连续的内存单元,。
● 指令存放:指令存放在内存中时,先存放操作码,再存放操作数。多字节操作数连续存放。顺序依据小端法原则,即: 低位字节存放在低地址单元,高位字节存放在相邻的高地址单元。
如:12345H单元中有一条指令 “ MOV AX,6789H ”,则它在内存中的形式如下图所示:
● 符号指令的书写格式:
◆ 说明:
① 操作码:表示 CPU 执行的操作。
② 操作数:指令执行时的操作对象 (一个或多个)。
③ 标号:以字母开头,后面可以跟字母、数字或下划线,但长度必须 ≤ 31个字符,标号又称符号地址,代表该指令的逻辑地址。可有可无,设置是为了程序的转向。
④ 注解:以 “ ;” 开头,不执行,打印程序清单时照原样打印,“系统保留字”不能做标号。
◆ 举个栗子如下:
N
E
X
T
:
A
D
D
A
X
,
B
X
;
A
X
+
B
X
→
A
X
NEXT:\,\,\,\,\,\,\,ADD\,\,AX,BX\,\,\,\,\,\,;AX+BX → AX
NEXT:ADDAX,BX;AX+BX→AX
I
N
C
S
I
;
S
I
+
1
→
S
I
\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,INC\,\,\,\,\,\,SI\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,\,;SI+1 → SI
INCSI;SI+1→SI
二、寻址方式(这一章的难点、重点)
● 寻址:形成操作数地址码的过程。操作数是指令的操作对象,寻址方式就是在指令中,使用特定的助记符 (地址表达式),告知 CPU 如何计算出操作数的地址。寻址方式只有针对操作数而言才有意义。
● 操作数的分类:
① 立即数:操作数包含在指令中。
② 寄存器操作数:操作数存放在 CPU 的某个寄存器中。
③ 存储器操作数(内存操作数) :操作数存放在存储器中。
④ I/O 端口操作数:操作数存放在 I/O 端口中。
● 本章只讲述前 3 种操作数对应的寻址方式,包含以下 3 类共计 7 种寻址方式:
① 立即寻址方式:获得立即数
① 寄存器寻址方式:获得寄存器操作数
① 存储器寻址方式:获得存储器操作数(内存操作数)
1、立即寻址
● 立即寻址:操作数包含在本条指令中,是指令的一部分,和指令操作码一起存放在代码段中。CPU 完整地取出该条指令,也就获得了操作数。流程如图所下:
◆ 举个栗子如下:
例:
M
O
V
D
X
,
1234
H
MOV \quad DX,1234H
MOVDX,1234H
◆ 说明:
① “DX” 是目标操作数,“1234H” 是源操作数
② 对于“1234H” 这个源操作数,就是立即寻址,因为这个操作数就在指令里面。
② 而对于 “AX” 这个目标操作数,是寄存器寻址,后面再讲。
◆ 还有些例子如下:
①
M
O
V
E
A
X
,
12345678
H
MOV \quad EAX,12345678H
MOVEAX,12345678H
②
M
O
V
B
L
,
10101010
B
;
A
A
H
→
B
L
MOV \quad BL,10101010B \quad\quad;AAH → BL
MOVBL,10101010B;AAH→BL
③
M
O
V
C
L
,
–
4
;
F
C
H
→
C
L
MOV \quad CL, – 4 \quad\quad;FCH →CL
MOVCL,–4;FCH→CL
④
M
O
V
D
L
,
’
A
’
;
41
H
→
D
L
MOV \quad DL,’A’ \quad\quad;41H →DL
MOVDL,’A’;41H→DL
⑤
A
D
D
A
L
,
0
C
8
H
ADD \quad AL,0C8H
ADDAL,0C8H
⑥
M
O
V
S
I
,
3
∗
5
;
15
→
S
I
MOV \quad SI,3*5 \quad\quad;15 →SI
MOVSI,3∗5;15→SI
● 上例中 源操作数 均为为立即寻址, 立即数书写规定:
① 立即数以数字开头。若以A~F开头的 16 进制数,必须有个前缀 0。
② 立即数的数制用后缀表示,B 表示二进制,H 表示十六进制, D 或缺省表示十进制,单引号括起来的字符编译成相应的 ASCII 码 。
③ 可以用 “+、– 、* 、/ ” 组成立即数表达式。
④ 程序员可以按自己的习惯书写立即数。各种合法的立即数经汇编后,一律自动转换成等值的二进制数,负数用补码表示。
2、寄存器寻址
● 寄存器寻址:操作数在 CPU 的某个寄存器中,符号指令中直接写出寄存器名称。流程如图所下:
◆ 举个栗子如下:
例:
M
O
V
D
X
,
A
X
MOV \quad DX,AX
MOVDX,AX
◆ 说明:
① “DX” 是目标操作数,“AX” 是源操作数
② “AX” 和 “DX” 这两个操作数都在寄存器里面,都是寄存器寻址。
◆ 再把原先的例子搬下来:
①
M
O
V
E
A
X
,
12345678
H
MOV \quad EAX,12345678H
MOVEAX,12345678H
②
M
O
V
B
L
,
10101010
B
;
A
A
H
→
B
L
MOV \quad BL,10101010B \quad\quad;AAH → BL
MOVBL,10101010B;AAH→BL
③
M
O
V
C
L
,
–
4
;
F
C
H
→
C
L
MOV \quad CL, – 4 \quad\quad;FCH →CL
MOVCL,–4;FCH→CL
④
M
O
V
D
L
,
’
A
’
;
41
H
→
D
L
MOV \quad DL,’A’ \quad\quad;41H →DL
MOVDL,’A’;41H→DL
⑤
A
D
D
A
L
,
0
C
8
H
ADD \quad AL,0C8H
ADDAL,0C8H
⑥
M
O
V
S
I
,
3
∗
5
;
15
→
S
I
MOV \quad SI,3*5 \quad\quad;15 →SI
MOVSI,3∗5;15→SI
● 上例 目标操作数 (左边)均为为寄存器寻址,而 源操作数 (右边)均为为立即寻址
3、存储器操作数寻址方式
● 存储器操作数寻址方式是最复杂的,它又分成 5 个小类。
● 在读写内存操作数之前,CPU必须知道相关存储单元的物理地址。但由于 CPU 对存储器采用分段管理, 因此在指令格式中 只能写出存放操作数所在内存单元的 “逻辑地址”。
存储单元逻辑地址表达式的一般形式:
● 实模式下,物理地址 = 段寄存器 × 24 + 偏移地址【后面细讲】
程序员的责任仅在于正确的书写逻辑地址表达式,然后由 CPU 自动运算以求出物理地址。
3.1 直接寻址 ⭐️
● 直接寻址的流程如图所下:
① 地址表达式的格式1:“ 段寄存器:[偏移地址] ”
如:
M
O
V
A
L
,
E
S
:
[
2
C
H
]
;
从
E
S
(
附
加
段
)
且
偏
移
地
址
为
2
C
H
的
单
元
取
数
→
A
L
MOV \quad AL, ES:[2CH] \quad;从ES(附加段)且偏移地址为2CH的单元取数→AL
MOVAL,ES:[2CH];从ES(附加段)且偏移地址为2CH的单元取数→AL
如:
M
O
V
A
X
,
D
S
:
[
1234
H
]
;
从
D
S
(
数
据
段
)
且
偏
移
地
址
为
1234
H
的
单
元
取
数
→
A
X
MOV \quad AX, DS:[1234H] \quad ;从DS(数据段)且偏移地址为1234H的单元取数 → AX
MOVAX,DS:[1234H];从DS(数据段)且偏移地址为1234H的单元取数→AX
注:这种格式很少使用,通常情况下,程序员并不知道某单元的偏移地址。【常用下面这种格式】
② 地址表达式的格式2:“ 段寄存器:变量名 ”,即用变量名来代表存储单元的有效地址
◆ 举个采用第一种地址表达式的栗子如下:
例:
M
O
V
A
X
,
D
S
[
1234
H
]
MOV \quad AX,DS[1234H]
MOVAX,DS[1234H]
◆ 举个采用第二种地址表达式的栗子如下:
首先假设取名为 “XX” 的DS(数据段) 的内容为 11H
其次假设取名为 “YY” 的ES(附加段) 的内容为 2233H
M
O
V
A
L
,
D
S
:
X
X
(
或
M
O
V
A
L
,
X
X
)
;
取
名
为
“
X
X
”
的
D
S
(
数
据
段
)
的
内
容
→
A
L
MOV \quad AL, DS:XX (或 MOV \quad AL, XX) \quad\quad;取名为 “XX” 的DS(数据段)的内容→AL
MOVAL,DS:XX(或MOVAL,XX);取名为“XX”的DS(数据段)的内容→AL
M
O
V
A
X
,
E
S
:
Y
Y
(
或
M
O
V
A
X
,
Y
Y
)
;
取
名
为
“
Y
Y
”
的
E
S
(
附
加
段
)
的
内
容
→
A
X
MOV \quad AX, ES:YY (或 MOV \quad AX, YY) \quad\quad;取名为 “YY” 的ES(附加段)的内容→AX
MOVAX,ES:YY(或MOVAX,YY);取名为“YY”的ES(附加段)的内容→AX
◆ 说明:
① 汇编语言允许为某单元起一个 “名字”,这个名字就称为该单元的 “变量名”,经汇编之后,变量名有段基址和偏移量两种属性。
② 由于变量名是唯一的,程序中不能有重复的变量名。
③ “段寄存器:”可以省略不写。
3.2 寄存器间接寻址 ⭐️
● 寄存器间接寻址 又称 “间接寻址”、“间址”。
● 间接寻址操作数在内存单元中,该单元的段基址在段寄存器中,而偏移地址在间址寄存器中,CPU首先找到这两者,然后进行地址计算,得到最终的物理地址。
间接寻址与直接寻址的不同,就在于多了一个 “间接寄存器” ,它的操作数中没有直接 “放” 偏移量,而需要 CPU 去间接寄存器把那个偏移量找出来。
● 间接寻址的地址表达式:“ 段寄存器:[间址寄存器] ”
某单元的物理地址 = 段寄存器内容 × 24 + 间址寄存器
● 访问约定的逻辑段,间接寻址的地址表达式可简化为:“ [间址寄存器] ”
某单元的物理地址 = 约定的段寄存器内容 × 24+ 间址寄存器
● 间址寄存器和约定访问的逻辑段的图表:
间址寄存器 | 约定访问的逻辑段 | 适用范围 |
---|---|---|
BP | SS(堆栈段) | 16位寻址方式 |
BX、SI、DI | DS(数据段) | 16位寻址方式 |
EBP、ESP | SS(堆栈段) | 32位寻址方式 |
EAX、EBX、ECX、EDX、ESI、EDI | DS(数据段) | 32位寻址方式 |
◆ 举个栗子:
设取名为 “BUF” 的数据段的字节单元内容为 55H,需要取出该数 → AL
① 解法1:用 BX 间址【在表格中的第三排】
M
O
V
A
X
,
B
U
F
数
据
段
的
段
基
址
MOV \quad AX,\,BUF数据段的段基址
MOVAX,BUF数据段的段基址
M
O
V
D
S
,
A
X
MOV \quad DS,\,AX
MOVDS,AX
M
O
V
B
X
,
B
U
F
单
元
的
有
效
地
址
;
有
效
地
址
也
称
为
偏
移
地
址
MOV \quad BX,\,BUF单元的有效地址\quad \quad;有效地址也称为偏移地址
MOVBX,BUF单元的有效地址;有效地址也称为偏移地址
M
O
V
A
L
,
D
S
:
[
B
X
]
;
A
L
=
55
H
MOV \quad AL,\,DS:[BX] \quad \quad\quad \quad\quad \quad\quad ;AL=55H
MOVAL,DS:[BX];AL=55H
由于 BX 为间址寄存器,且为约定访问的是数据段,因此 “DS:” 可省。故最后一行代码等价于: “
M
O
V
A
L
,
[
B
X
]
MOV \quad AL ,\, [BX]
MOVAL,[BX]”
② 解法2:用 BP 间址【在表格中的第二排】
M
O
V
A
X
,
B
U
F
数
据
段
的
段
基
址
MOV \quad AX ,\, BUF数据段的段基址
MOVAX,BUF数据段的段基址
M
O
V
D
S
,
A
X
MOV \quad DS ,\, AX
MOVDS,AX
M
O
V
B
P
,
B
U
F
单
元
的
有
效
地
址
MOV \quad BP ,\, BUF单元的有效地址
MOVBP,BUF单元的有效地址
M
O
V
A
L
,
D
S
:
[
B
P
]
;
A
L
=
55
H
MOV \quad AL ,\, DS:[BP] \quad\quad\quad\quad\quad\quad\quad;AL=55H
MOVAL,DS:[BP];AL=55H
由于 BP 为间址寄存器,但约定访问的是堆栈段(不是数据段),因此 “DS:” 不可省,即不能写成: “
M
O
V
A
L
,
[
B
P
]
MOV \quad AL ,\, [BP]
MOVAL,[BP]”
② 解法3:用变量名直接寻址
M
O
V
A
X
,
B
U
F
数
据
段
的
段
基
址
MOV\quad AX,\,BUF数据段的段基址
MOVAX,BUF数据段的段基址
M
O
V
D
S
,
A
X
MOV\quad DS,\,AX
MOVDS,AX
M
O
V
A
L
,
B
U
F
;
A
L
=
55
H
MOV\quad AL,\, BUF\quad\quad\quad\quad\quad\quad\quad \quad\quad ;AL=55H
MOVAL,BUF;AL=55H
3.3 基址寻址 ⭐️
● 在基址寻址中,有效地址(即偏移地址)由两部分组成。一部分存放在基址寄存器中,另一部分为常量 。
基址寻址与间接寻址的不同,就在于偏移地址的计算复杂了点。此时 CPU 不能去间接寄存器直接找出偏移地址,而是去基址寄存器把 “基偏移地址” 找出来,再把这 “基偏移地址” 和 “位移量” 相加,间接地得到偏移地址。
① 基址寻址的地址表达式 1:“ 段寄存器:[基址寄存器 + 位移量] ”
物理地址 = 段寄存器内容 × 24 + 基址寄存器 + 位移量
② 基址寻址的地址表达式 2:“ [基址寄存器 + 位移量] ”。使用条件:访问约定的逻辑段。
物理地址 = 约定的段寄存器内容 × 24 + 基址寄存器 + 位移量
● 基址寄存器和约定访问的逻辑段:
基址寄存器 | 约定访问的逻辑段 | 适用范围 |
---|---|---|
BP | SS(堆栈段) | 16位寻址方式 |
BX | DS(数据段) | 16位寻址方式 |
EBP、ESP | SS(堆栈段) | 32位寻址方式 |
EAX、EBX、ECX、EDX、ESI、EDI | DS(数据段) | 32位寻址方式 |
◆ 举个栗子:
设数据段 BUF 单元依次有:
78
H
,
56
H
,
34
H
,
12
H
78H,56H,34H,12H
78H,56H,34H,12H,现在需要进行一些访问。
则:
M
O
V
A
X
,
数
据
段
段
基
址
MOV AX , 数据段段基址
MOVAX,数据段段基址
M
O
V
D
S
,
A
X
MOV \quad DS ,\, AX
MOVDS,AX
M
O
V
B
X
,
B
U
F
单
元
有
效
地
址
MOV \quad BX ,\, BUF单元有效地址
MOVBX,BUF单元有效地址
M
O
V
D
H
,
[
B
X
+
1
]
;
D
H
=
56
H
MOV \quad DH ,\, [BX+1] \quad \quad \quad\quad;DH=56H
MOVDH,[BX+1];DH=56H
M
O
V
D
X
,
[
B
X
+
2
]
;
D
X
=
1234
H
MOV \quad DX ,\, [BX+2] \quad \quad \quad \quad;DX=1234H
MOVDX,[BX+2];DX=1234H
注:DH 是 EDX 的高 8 位,所以只能送入一个 16 进制数。 DX 是 EDX 的低 16 位,所以能送入两个 16 进制数。
3.4 变址寻址 ⭐️
● 在变址寻址中,有效地址(即偏移地址)由两部分组成。一部分存放在变址寄存器中(可能这部分还要乘以一个系数),另一部分为常量 。
变址寻址与基址寻址的不同,就在于偏移地址的计算 更 复杂了点。此时 CPU 不去基址寄存器,而是去变址寄存器。
● ① 有比例因子的变址寻址的表达式:“ 段寄存器:[比例因子 × 变址寄存器 + 位移量] ”
物理地址 = 段寄存器 × 24 + 比例因子 × 变址寄存器 + 位移量
● ② 没有比例因子的变址寻址的表达式:“ 段寄存器:[变址寄存器 + 位移量] ”
访问约定的逻辑段可简化为 : [变址寄存器 + 位移量]
物理地址 = 约定的段寄存器 × 24 + 变址寄存器 + 位移量
● 变址寄存器与约定访问的逻辑段:
变址寄存器 | 约定访问的逻辑段 | 适用范围 |
---|---|---|
SI、DI | DS(数据段) | 无比例因子,且16位寻址方式 |
EBP | SS(堆栈段) | 有比例因子,且32位寻址方式 |
EAX、EBX、ECX、EDX、ESI、EDI | DS(数据段) | 有比例因子,且32位寻址方式 |
● 说明:比例因子只能是1、2、4、8
因此 “
M
O
V
A
L
,
[
8
×
S
I
+
15
]
;
MOV AL, [8\times SI+15];
MOVAL,[8×SI+15]; ” 是非法的,因为 16 位的寻址方式不能使用比例因子。(即 SI 不行,但 ESI 可以)
比例因子错了的非法指令:
M
O
V
A
L
,
[
10
×
E
S
I
+
5
]
MOV \quad AL ,\, [10\times ESI+5]
MOVAL,[10×ESI+5]
访问附加段的正确指令:
M
O
V
A
L
,
E
S
:
[
S
I
]
MOV \quad AL ,\, ES:[SI]
MOVAL,ES:[SI]
访问数据段的正确指令:
M
O
V
A
L
,
[
S
I
]
MOV \quad AL , \,[SI]
MOVAL,[SI]
3.5 基址加变址寻址 ⭐️
● 在基址加变址寻址中,有效地址(即偏移地址)由三部分组成。第一部分存放在基址寄存器中,第二部分存放在变址寄存器,第三部分为常量(位移量)。
基址加变址寻址,简单来说,就是 “基址寻址” 与 “变址寻址” 的结合。
① 有比例因子的基址加变址的地址表达式:“ 段寄存器:[基址寄存器 + 比例因子 × 变址寄存器 + 位移量] ”
② 有比例因子且有访问约定逻辑段的地址表达式:“ [基址寄存器 + 比例因子 × 变址寄存器 + 位移量] ”
③ 无比例因子基址加变址的地址表达式:“ 段寄存器:[基址寄存器+变址寄存器+位移量]
④ 无比例因子且有访问约定逻辑段的地址表达式:“ [基址寄存器+变址寄存器+位移量] ”
● 基址加变址寻址的要求:基址寄存器和变址寄存器必须都是 16 位或都是 32 位,否则(16位寻址和32位寻址混合使用)是非法指令。
● 默认的段寄存器不一致,这样的组合虽然是合法,但容易出错。如:[BP+SI],[BP+DI]
● BP 约定的逻辑段为堆栈段,而 SI、DI 约定的逻辑段为数据段,它们组合之后约定访问的逻辑段是堆栈段,但这样的组合最好要加段前缀。
例: M O V A L , [ B X + S I + 0250 H ] MOV\quad AL,\,[BX+SI+0250H] MOVAL,[BX+SI+0250H]
三、标志寄存器(这一节也很重要)
● 80486 标志寄存器为 32 位,实际只使用 15 位。
● 15 位标志分为两类:状态标志和控制标志。
● 状态标志记录了当前指令执行后的状态信息。
● 控制标志用来控制微处理器操作,本节首先介绍 6 种状态标志。
① C 标志——进位/借位标志
字节的加/减:最高位(D7)产生进位/借位时 C 标志置1,否则置0
字的加/减:最高位(D15)产生进位/借位时 C 标志置1,否则置0
双字的加/减:最高位(D31)产生进位/借位时 C 标志置1,否则置0
② A 标志——辅助进位/辅助借位标志
字节、字、双字的加/减,D3 位产生进位/借位时 A 标志置1,否则置0
③ S 标志——符号标志
字节运算后,若结果的最高位 D7 位为1,S标志置1,否则置0
字运算后,若结果的最高位 D15 位为1,S标志置1,否则置0
双字运算后,若结果的最高位 D31 位为1,S标志置1,否则置0
④ Z 标志——结果标志
运算结果为全0时,Z标志置1,否则置0
⑤ P 标志——奇偶标志(实际上是偶标志)
运算结果中,最低的 1 个字节中 “1” 的个数为偶数时(没有“1”也是偶数),P标志置1,否则置0
⑥ O 标志——溢出标志
运算结果产生溢出时,则 O 标志置1,否则置0
● 关于 “ 溢出的几个概念、CPU如何判断溢出、程序员如何判断溢出(有符号数/无符号数) ” 的相关知识,可详见这篇:
《计算机基础【原码 补码 反码 移码 BCD码 溢出 进位…】》: https://blog.csdn.net/Wang_Dou_Dou_/article/details/120054864?spm=1001.2014.3001.5501.
◆ 举个栗子:执行下列指令后,AL = ?,标志寄存器 A、C、O、P、S、Z 分别为何值?(必考)
M
O
V
A
L
,
0
C
8
H
MOV \quad AL,\,0C8H
MOVAL,0C8H
A
D
D
A
L
,
0
C
8
H
ADD \quad AL,\,0C8H
ADDAL,0C8H
最终结果为 AL = 90H。如果 C8H 为无符号数(值 = 200),则有溢出。如果 C8H 为有符号数(值 = -56),则无溢出。 这 6 个标志中,C、O、P、S、Z 可为程序的转移提供测试条件。
四、参考附录:
[1] 《微型计算机原理与接口技术(慕课板)》
清华大学出版社
[2] 《汇编语言程序设计(第2版)》
上一篇文章链接: 《【计算机与汇编原理③】——80X86微处理器》.
下一篇文章链接: 《【计算机和汇编原理⑤】——指令系统(下)【 2万5千字总结 】》.
PPT制图不易,多多支持 🚀 🚀