计组与微机控制
5.5 显式隐式
5.5 显式隐式
数据寄存器的“通用”和“隐含使用”
课本这里有一个非常关键的概念:
这些寄存器大多数时候可以通用,但在某些指令中会被隐含使用。
什么叫通用?
比如:ADD CH, DH
意思是:CH ← CH + DH
这条指令里,程序员明确写出了 CH 和 DH。
CPU 就按你写的来。这就叫普通使用、显式使用。
再比如:
MOV AX, BX
ADD DX, CX你想用哪个寄存器,就写哪个寄存器。
什么叫隐含使用?
有些指令虽然你没有在指令里写某个寄存器,但 CPU 默认就会使用它。
这叫 隐含使用。
例如:LOOP 标号
这条指令里没有写 CX。
但是 8086 规定:
LOOP 指令默认使用 CX 作为循环计数器。
执行一次 LOOP,大概过程是:
CX ← CX - 1
如果 CX ≠ 0,就跳转到目标标号
如果 CX = 0,就继续执行下一条指令所以虽然你没写 CX,CPU 也会自动用 CX。
这就是隐含使用。
LOOP 指令为什么用 CX?
课本举了 LOOP 的例子。
MOV CX, 5
again:
; 循环体
LOOP again执行逻辑是:
第一次执行 LOOP:CX 从 5 变 4,4 不等于 0,跳回 again
第二次执行 LOOP:CX 从 4 变 3,跳回
第三次执行 LOOP:CX 从 3 变 2,跳回
第四次执行 LOOP:CX 从 2 变 1,跳回
第五次执行 LOOP:CX 从 1 变 0,不跳,循环结束所以 CX 在这里是“循环次数计数器”。
这也是 CX 名字 Count Register 的来源。
CL 在移位指令中的特殊作用
课本还讲了移位指令。
如果移位次数大于 1,通常要把移位次数放到 CL 里。
比如:
MOV CL, 4
SAR AX, CL意思是:AX 算术右移 4 位
这里 CL 不是被移动的数据,而是“移动几位”的计数器。
注意一个细节:移位指令使用 CL 作为次数,但执行后 CL 本身的内容不变。
比如:
MOV CL, 4
SAR AX, CL执行后,AX 改变,但 CL 仍然是 4。
所以 CL 在这里是“提供次数”,不是“参与被移位”。


表 2.1:通用寄存器的特定/隐含使用
有些寄存器在某些指令中有固定角色。
AL / AX 在输入输出指令中常作数据寄存器
例如:
IN AL, 60H OUT 60H, AL
在 I/O 输入输出指令里,数据经常通过 AL 或 AX 传送。
8 位 I/O 用 AL。 16 位 I/O 用 AX。
例如:IN AL, 端口号表示从端口读 8 位数据到 AL。
IN AX, 端口号 表示从端口读 16 位数据到 AX。
AL / AX 在乘法指令中的隐含作用
乘法指令 MUL 中,经常隐含使用 AL 或 AX。
比如 8 位乘法:MUL BL
这条指令没有写 AL,但实际上是:
AL × BL → AX 结果为什么放 AX?
因为两个 8 位数相乘,结果最多需要 16 位保存。
例如:
FFH × FFH = FE01H
结果超过 8 位,所以要用 AX 保存。
16 位乘法时:MUL BX
实际含义是:
AX × BX → DX:AX
两个 16 位数相乘,结果最多需要 32 位。
8086 没有单独的 32 位寄存器,所以用 DX 和 AX 拼起来:
DX 保存高 16 位
AX 保存低 16 位写成:DX:AX
AL / AX 在除法指令中的隐含作用
除法也经常隐含使用 AX 或 DX:AX。
8 位除法:DIV BL
实际含义大致是:
AX ÷ BL 商放 AL 余数放 AH
16 位除法:DIV BX
实际含义大致是:DX:AX ÷ BX
商放 AX 余数放 DX所以乘除法里,AX、DX 非常常见。
AH 在 LAHF 指令中作目的寄存器
表里提到 AH 在 LAHF 指令中作目的寄存器。
LAHF 的意思可以先简单理解成:
把标志寄存器的一部分状态装入 AH。
也就是把若干标志位打包放进 AH。
所以这里 AH 是固定被使用的。
AL 在 BCD 调整指令、XLAT 指令中的作用
BCD 调整指令暂时可以先不用深究,它和十进制调整有关。
现在只要知道:
某些 BCD 运算调整指令默认会用 AL 或 AH。
XLAT 指令也比较特殊,它常用于查表。
XLAT 中:BX 常作表的基址 AL 常作表内索引或接收结果
所以表里会写:BX 在 XLAT 中作基址寄存器。 AL 在 XLAT 中作目的寄存器。
CX 在循环指令中作循环次数计数器
这个刚才讲过。LOOP label
默认使用 CX。
CL 在移位指令中作移位次数计数器
这个也讲过。
SHL AX, CL
SAR AX, CL
R OR BL, CLCL 表示移动几位。
DX 在字乘除法中作辅助累加器
在 16 位乘除法里,DX 经常和 AX 配合。
乘法:AX × 操作数 → DX:AX
除法:DX:AX ÷ 操作数
所以 DX 经常保存高 16 位。
SP 在堆栈操作中作堆栈指针
例如:PUSH AX POP BX
PUSHF POPF这些栈操作会隐含使用 SP。
PUSH 时,SP 会变化,然后数据压入栈。
POP 时,从栈顶取数据,然后 SP 变化。程序员不一定在指令里写 SP,但 CPU 会自动使用 SP。
SI / DI 在串操作指令中的作用
表里提到:
SI 在串操作中作源变址寄存器。 DI 在串操作中作目的变址寄存器。
例如:MOVSB 它的作用是传送一个字节。
默认:
源地址:DS:SI
目的地址:ES:DI也就是:把 DS:SI 指向的字节送到 ES:DI 指向的位置
所以:SI 指源,DI 指目的。
指针寄存器:SP 和 BP
指针寄存器有两个:
SP BP
它们通常作为 16 位地址指针使用。
这里的“地址指针”不是完整物理地址,而是:段内偏移地址。
8086 访问内存时通常是:物理地址 = 段地址 × 16 + 偏移地址
SP 和 BP 通常提供的就是这个“偏移地址”。
SP:堆栈指针
SP 是 Stack Pointer,堆栈指针。
它专门指向栈顶。8086 中,栈的位置由:
SS:SP决定。
SS 指出栈段从哪里开始。
SP 指出栈顶在栈段内的偏移位置。所以 SP 本身不是完整地址。
它只是栈段内的偏移量。
PUSH 和 POP 为什么隐含使用 SP?例如:PUSH AX
你没有写 SP,但是 CPU 会自动修改 SP,并把 AX 压入栈。
再如:POP BX
CPU 会从 SS:SP 指向的位置取数据给 BX,然后自动修改 SP。
所以 SP 是堆栈操作的核心。
你可以把 SP 想成:
栈顶指针。
它永远跟着栈顶移动。
BP:基址指针
BP 是 Base Pointer,基址指针。
它也常用来访问栈段中的数据。
BP 和 SP 的区别是:
SP 经常自动变化,表示当前栈顶。 BP 通常作为一个稳定的基准,用来访问栈中的参数或局部数据。
比如函数调用时,栈里可能有:
返回地址参数临时变量
SP 可能随着 PUSH、POP 不断变。
BP 可以固定下来,用来稳定访问这些内容。BP 默认和 SS 搭配
这是一个很重要的规则。
在 8086 中,如果用 BP 参与寻址,默认段寄存器通常是 SS。
例如:MOV AX, [BP]
默认理解为:
从 SS:BP 指向的内存取数据到 AX
而如果用 BX、SI、DI,默认一般是 DS。
例如:
MOV AX, [BX]
MOV AX, [SI]
MOV AX, [DI]默认通常是:
DS:BX
DS:SI
DS:DI所以你可以记:
BP 偏向栈段 SS。
BX、SI、DI 偏向数据段 DS。变址寄存器:SI 和 DI
变址寄存器有两个:
SI DI
它们都是 16 位。
它们常用来作为地址偏移量的一部分。
什么叫变址?
“变址”可以理解为:
通过改变一个寄存器的值,访问一串连续内存单元。
比如数组:
arr[0] arr[1] arr[2] arr[3]
如果 SI 指向 arr[0],那么:
SI 加 1 可以访问下一个字节。 SI 加 2 可以访问下一个字。 不断改变 SI,就能遍历数组。
这就是“变址”的感觉。
SI 是源变址寄存器
SI = Source Index。
Source 是源。
所以 SI 常表示数据从哪里来。
特别是在串操作中:
源串操作数必须由 SI 提供偏移量
例如:MOVSB
默认源地址是:DS:SI
DI 是目的变址寄存器
DI = Destination Index。
Destination 是目的。DI 常表示数据送到哪里去。
在串操作中:目的串操作数必须由 DI 提供偏移量
例如:MOVSB
默认目的地址是:ES:DI
所以字符串操作里常见组合是:
DS:SI → ES:DI
这句话非常重要。
