计组与微机控制
7.7 移位循环和串操作
7.7 移位循环和串操作
一、移位和循环移位指令
接下来进入:Shift / Rotate
也就是移位和循环移位。
表 4.10 里列出了:

分成两类:
移位: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。
