← 返回 计组与微机控制

计组与微机控制

7.7 移位循环和串操作

7.7 移位循环和串操作

一、移位和循环移位指令

接下来进入:Shift / Rotate

也就是移位和循环移位。

表 4.10 里列出了:

7.7 移位循环和串操作 图 1

分成两类:

移位:SHL、SAL、SHR、SAR 循环移位:ROL、ROR、RCL、RCR

二、移位次数 count

书上说 count 是移位次数。在 8086 中,移位次数只能是:

1或者 CL

所以合法:

SHL AX, 1 MOV CL, 4 SHL AX, CL

但在 8086 里不合法:SHL AX, 4

因为立即数移位次数只能写 1,不能直接写 2、3、4。如果要移 4 位,应该:

MOV CL, 4

SHL AX, CL

这一点后面例 4.17 会专门考。

三、SHL / SAL:左移

SHL 是逻辑左移。SAL 是算术左移。

在 8086 里:SHL 和 SAL 效果相同

格式:SHL dest, 1 或 SHL dest, CL SAL dest, 1 或 SAL dest, CL

作用:所有位向左移动,最低位补 0,最高位移入 CF

例如:AL = 0101 0011B

执行:SHL AL, 1

结果:AL = 1010 0110B ,CF = 原最高位 0

左移和乘 2 的关系

对于无溢出的情况:

左移 1 位 ≈ 乘 2 左移 2 位 ≈ 乘 4

例如:0000 0011B = 3 左移 1 位 → 0000 0110B = 6但如果最高位被移出去,就可能溢出,要看 CF/OF。

四、SHR:逻辑右移

SHR 是逻辑右移。作用:所有位向右移动,最高位补 0,最低位移入 CF

例如:AL = 1000 0110B

执行:SHR AL, 1

结果:AL = 0100 0011B ,CF = 原最低位 0

SHR 常用于无符号数除 2。

SAR:算术右移 SAR 是算术右移。

作用:所有位向右移动,最高位保持原来的符号位,最低位移入 CF(补符号位)

例如负数:

AL = 1111 1000B

执行:SAR AL, 1 结果:

AL = 1111 1100B

最高位仍然补 1,因为要保持负数符号。

所以:SHR 用于无符号右移,SAR 用于带符号右移(之前都讲过在ARM里面)

五、移位指令对标志位的影响

移位指令会影响:CF、PF、SF、ZF、OF

AF 不确定。其中最重要的是 CF:

CF = 最后一次被移出去的那一位

如果移位次数是 1,OF 有明确意义:

左移时,如果符号位发生改变,则 OF = 1

如果移位次数大于 1,OF 通常不确定。

六、循环移位指令

循环移位包括:ROL ROR RCL RCR

1. ROL:循环左移

ROL 是 Rotate Left。

作用:最高位移到最低位,同时最高位送入 CF

例如:AL = 1001 0110B

执行:ROL AL, 1

结果:AL = 0010 1101B CF = 1原来的最高位 1 跑到最低位,也进入 CF。

2. ROR:循环右移

ROR 是 Rotate Right。

作用:最低位移到最高位,同时最低位送入 CF

例如:AL = 1001 0110B

执行:ROR AL, 1

结果:AL = 0100 1011B CF = 0

原最低位 0 进入最高位,也进入 CF。

3. RCL:带进位循环左移

RCL 是 Rotate through Carry Left。

它把 CF 也放进循环圈。

左边被移出去的位进入 CF,原 CF 进入最低位。可以想成:

CF + 操作数整体一起循环左移

例如 8 位操作数,实际上是 9 位循环:CF + 8 位数据

4. RCR:带进位循环右移

RCR 是 Rotate through Carry Right。

它也是把 CF 放进循环圈,只不过方向向右。

可以想成:CF + 操作数整体一起循环右移

ROL/ROR 和 RCL/RCR 的区别

最重要区别:

ROL / ROR:CF 只是保存移出去的位,不参与原数据内部循环 RCL / RCR:CF 参与循环

更直观:

ROL/ROR:只在操作数内部绕圈 RCL/RCR:操作数和 CF 一起绕圈

例 4.17:32 位数乘 4

书上例子要求:完成一个 32 位数乘 4

因为 8086 没有真正的 32 位寄存器,所以用:DX:AX表示 32 位数。

DX = 高 16 位,AX = 低 16 位

如果要乘 4,就是左移 2 位。

1. 为什么不能简单写 SHL DX:AX?

因为 8086 没有:SHL DX:AX, 1

这种 32 位移位指令。所以只能分两部分操作:低 16 位 AX 左移 移出去的位通过 CF 进入高 16 位 DX

2. 正确思路

每左移 1 位,要做两步:

SHL AX, 1 RCL DX, 1

含义:第一句:SHL AX, 1

低 16 位 AX 左移 1 位,AX 最高位被移入 CF。

第二句:RCL DX, 1

DX 带 CF 循环左移 1 位,把刚才 AX 移出去的那一位送进 DX 的最低位。

这样 DX:AX 整体就完成了 1 位左移。

3. 乘 4 要左移两次

所以正确程序是:

MOV AX, WORD PTR DATA MOV DX, WORD PTR DATA+2 SHL AX, 1 RCL DX, 1 SHL AX, 1 RCL DX, 1

这里不能偷懒。因为每一次 AX 移出去的位,都要马上通过 RCL DX,1 送进 DX。

4. 为什么下面这段有语法错误?

书上给的错误程序段 1:

SHL AX, 2 RCL DX, 2

在 8086 中,移位次数不能直接写 2。

只能写:SHL AX, 1

或者:

MOV CL, 2 SHL AX, CL

所以:SHL AX, 2对 8086 来说是语法错误。

5. 为什么下面这段有逻辑错误?

书上程序段 2:

MOV CL, 2 SHL AL, CL RCL DX, CL

这里有两个问题。

第一个很明显:SHL AL, CL

它只移动 AL,也就是 AX 的低 8 位,而不是整个 AX。

题目要处理 DX:AX 组成的 32 位数,低 16 位应该是 AX,不是 AL。

第二个更深层的问题是:即使写成:

SHL AX, CL RCL DX, CL

也不适合用来模拟 32 位整体左移 2 位。

因为 SHL AX,CL 移 2 位后,CF 只保存最后一次移出去的那一位,前一次移出去的位已经丢了。

而 32 位整体左移要求 AX 移出去的每一位都按顺序进入 DX。

所以必须一位一位来:

SHL AX, 1 RCL DX, 1 SHL AX, 1 RCL DX, 1

这是这道题的关键。

七、串操作类指令

最后一页开始进入串操作。

串操作指令用于处理连续内存数据,比如:

数组 字符串 数据块 表格 缓冲区

8086 提供一组专门的串操作指令。

1. 串操作指令有哪些?

书上列出:

MOVS LODS STOS CMPS SCAS

它们分别是:

MOVS:串传送 LODS:串装入 STOS:串存储 CMPS:串比较 SCAS:串扫描

还有重复前缀:

REP REPE / REPZ REPNE / REPNZ

2. 为什么需要串操作指令?

普通指令如果搬一段内存,需要循环:

MOV AL, [SI] MOV [DI], AL INC SI INC DI DEC CX JNZ AGAIN

串操作指令可以把这些操作简化。

比如:REP MOVSB

可以连续搬很多字节。

3. 串操作常用寄存器

串操作里有几个默认寄存器:

DS:SI → 源串地址 ES:DI → 目的串地址 CX → 重复次数 DF → 控制地址增减方向

其中 DF 是方向标志位。

如果:DF = 0地址自动增加。

如果:DF = 1地址自动减少。

常用指令:

CLD STD

其中:

CLD:清 DF,使地址递增 STD:置 DF,使地址递减

4. MOVS

MOVS 是串传送。

常见形式:

MOVSB MOVSW

其中:

MOVSB:传送一个字节 MOVSW:传送一个字

MOVSB 的功能:ES:[DI] ← DS:[SI]

然后:如果 DF = 0:

SI ← SI + 1 DI ← DI + 1

如果 DF = 1:

SI ← SI - 1 DI ← DI - 1

MOVSW 类似,只不过一次传送 2 字节,所以 SI、DI 每次加 2 或减 2。

5. REP MOVSB

如果加上 REP:REP MOVSB

含义是:当 CX ≠ 0 时,重复执行 MOVSB 每执行一次,CX ← CX - 1直到 CX = 0

例如复制 100 个字节:

CLD LEA SI, SRC LEA DI, DST MOV CX, 100 REP MOVSB

意思是:从 DS:SRC 复制 100 字节到 ES:DST

6. LODS

LODS 是串装入。

常见形式:

LODSB LODSW

LODSB:AL ← DS:[SI]

然后 SI 自动加 1 或减 1。

LODSW:AX ← DS:[SI]

然后 SI 自动加 2 或减 2。

LODS 常用于从字符串中逐个取字符。

7. STOS

STOS 是串存储。

常见形式:

STOSB STOSW

STOSB:ES:[DI] ← AL

然后 DI 自动加 1 或减 1。

STOSW:ES:[DI] ← AX

然后 DI 自动加 2 或减 2。

常用于清内存或填充内存。

例如把一段内存填成 0:

CLD MOV AL, 0 LEA DI, BUFFER MOV CX, 100 REP STOSB

8. CMPS

CMPS 是串比较。

常见形式:

CMPSB CMPSW

CMPSB 的本质:DS:[SI] - ES:[DI]

它不保存结果,只影响标志位。

然后 SI、DI 自动增减。

常配合:

REPE REPNE

使用。

9. SCAS

SCAS 是串扫描。

常见形式:

SCASB SCASW

SCASB 的本质:AL - ES:[DI]

它用 AL 和目标串中的元素比较,只影响标志位,然后 DI 自动增减。

常用于在字符串中查找某个字符。

重复前缀 REP / REPE / REPNE

串操作最强的地方是可以加重复前缀。

1. REP

REP MOVSB REP STOSB

含义:

只要 CX ≠ 0,就重复执行 每次 CX 减 1

常用于 MOVS、STOS。

2. REPE / REPZ

REPE CMPSB REPZ CMPSB

REPE 和 REPZ 等价。

含义:当 CX ≠ 0 且 ZF = 1 时重复

也就是:相等就继续比较,一旦不相等就停

常用于比较两个字符串是否相同。

3. REPNE / REPNZ

REPNE SCASB REPNZ SCASB

REPNE 和 REPNZ 等价。

含义:当 CX ≠ 0 且 ZF = 0 时重复

也就是:没找到就继续找,一旦找到就停

常用于扫描字符串中是否存在某个字符。

总结:

第一,除法前一定要准备好被除数:

8 位除法:AX ÷ src8 16 位除法:DX:AX ÷ src16

第二,带符号除法前常用:

CBW CWD

第三,BCD 调整指令按类型记:

AAA/AAS:非压缩 BCD 加减 DAA/DAS:压缩 BCD 加减 AAM:非压缩 BCD 乘法后调整 AAD:非压缩 BCD 除法前调整

第四,逻辑运算:

AND:清位/取位 OR :置位 XOR:翻转/清零 NOT:取反,不影响标志位 TEST:测试位,只影响标志位,不保存结果

第五,移位:

SHL/SAL:左移,低位补 0 SHR:逻辑右移,高位补 0 SAR:算术右移,高位补符号位

第六,循环移位:

ROL/ROR:操作数内部循环 RCL/RCR:带 CF 一起循环

第七,串操作:

DS:SI 是源 ES:DI 是目的 CX 是次数 DF 决定地址增减方向

这一段最容易考的是:除法前的 CBW/CWD、DAA/AAM/AAD 的使用顺序、TEST 和 CMP 的区别、32 位数用 SHL+RCL 移位、串操作的 SI/DI/CX/DF。