← 返回 计组与微机控制

计组与微机控制

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 在这里是“提供次数”,不是“参与被移位”。

5.5 显式隐式 图 15.5 显式隐式 图 2

表 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, CL

CL 表示移动几位。

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

这句话非常重要。