第九章 字符串和数组
9.2 基本字符串操作指令
32 位模式中,下表中的每条指令都隐含使用 ESI、EDI,或是同时使用这两个寄存器来寻址内存。
指令 | 说明 |
---|---|
MOVSB、MOVSW、MOVSD | 传送字符串数据:将 ESI 寻址的内存数据复制到 EDI 寻址的内存位置 |
CMPSB、CMPSW、CMPSD | 比较字符串:比较分别由 ESI 和 EDI 寻址的内存数据 |
SCASB、SCASW、SCASD | 扫描字符串:比较累加器 (AL、AX 或 EAX) 与 EDI 寻址的内存数据 |
STOSB、STOSW、STOSD | 保存字符串数据:将累加器内容保存到 EDI 寻址的内存位置 |
LODSB、LODSW、LODSD | 从字符串加载到累加器:将 ESI 寻址的内存数据加载到累加器 |
使用重复前缀
就其自身而言,字符串基本指令只能处理一个或一对内存数值。如果加上重复前缀,指令就可以用 ECX 作计数器重复执行。重复前缀使得单条指令能够处理整个数组。下面为可用的重复前缀:
REP | ECX > 0 时重复 |
REPZ、REPE | 零标志位置 1 且 ECX > 0 时重复 |
REPNZ、REPNE | 零标志位清零且 ECX > 0 时重复 |
TITLE testprogram INCLUDE Irvine32.inc .data Array1 BYTE "Hello World!",0 Array2 BYTE 13 DUP(?) .code main PROC mov esi,OFFSET Array1 mov edi,OFFSET Array2 mov ecx,SIZEOF Array1 REP MOVSB mov edx,OFFSET Array2 call WriteString call Crlf call WaitMsg exit main ENDP END main
方向标志位
根据方向标志位的状态,字符串基本青令增加或减少 ESI 和 EDI 如下表所示。可以用 CLD 和 STD 指令显式修改方向标志位:
CLD ;方向标志位清零(正向)
STD ;方向标志位置 1(反向)
方向标志位的值 | 对ESI和EDI的影响 | 地址顺序 |
---|---|---|
0 | 增加 | 低到高 |
1 | 减少 | 高到低 |
9.2.1 MOVSB,MOVSW和MOVSD指令
MOVSB、MOVSW 和 MOVSD 指令将数据从 ESI 指向的内存位置复制到 EDI 指向的内存位置。(根据方向标志位的值)这两个寄存器自动地增加或减少:
MOVSB | 传送(复制)字节 |
MOVSW | 传送(复制)字 |
MOVSD | 传送(复制)双字 |
MOVSB、MOVSW 和 MOVSD 可以使用重复前缀。方向标志位决定 ESI 和 EDI 是否增加或减少。增加 / 减少的量如下表所示:
指令 | ESI 和 EDI 增加或减少的数值 |
---|---|
MOVSB | 1 |
MOVSW | 2 |
MOVSD | 4 |
.data source DWORD 20 DUP(OFFFFFFFFh) target DWORD 20 DUP(?) .code cld ;方向为正向 mov ecx,LENGTHOF source ;设置 REP 计数器 mov esi,OFFSET source ;ES工指向 source mov edi,OFFSET target ;ED工指向 target rep novsd ;复制双字
9.2.2 CMPSB,CMPSW和CMPSD指令
.data source DWORD 1234h target DWORD 5678h .code mov esi,OFFSET source mov edi,OFFSET target cmpsd ;比较双字 ja L1 ;若 source > target 则跳转
mov esi,OFFSET source mov edi,OFFSET target cld ;方向为正向 mov ecx,LENGTHOF source ;设置重复计数器 repe cmpsd ;相等则重复
应该强调一下,只有在两个字符串相等的条件下,使用CMPSB指令比较两个字符串才是可行的。(不足,自行填充空格)
9.2.3 SCASB,SCASW和SCASD指令
.data alpha BYTE "ABCDEFGH",0 .code mov edi,OFFSET alpha ;ED工指向字符串 mov al, 'F' ;检索字符F mov ecx,LENGTHOF alpha ;设置检索计数器 cld ;方向为正向 repne seasb ;不相等则重复 jnz quit ;若未发现字符则退出 dec edi ;发现字符:EDI 减 1
9.2.4 STOSB,STOSW和STOSD指令
与 REP 前缀组合使用时,这些指令实现用同一个值填充字符串或数组的全部元素。例如,下面的代码就把 string1 中的每一个字节都初始化为 OFFh:
.data Count = 100 string1 BYTE Count DUP(?) .code mov al, OFFh ;要保存的数值 mov edi,OFFSET string1 ;ED:[指向目标字符串 mov ecx,Count ;字符计数器 cld ;方向为正向 rep stosb ;用 AL 的内容实现填充
9.2.5 LODSB ,LODSW和LODSD指令
【示例】数组乘法,下面的程序把一个双字数组中的每个元素都乘以同一个常数。程序同时 使用了 LODSD 和 STOSD:
;数组乘法 (Mult.asm) ;本程序将一个32位整数数组中的每个元素都乘以一个常数。 INCLUDE Irvine32.inc .data array DWORD 1,2,3,4,5,6,7,8,9,10 ;测试数据 mug" 0W0RD -10 .code main PROC cld ;方向为正向 mov esi,OFFSET array ;源数组索引 itqv edi,esi ;目标数组索引 mov ecx,LENGTHOF array ;循环计数器 L1: lodsd ;将 [ESI] 加载到 EAX mul multiplier ;与常数相乘 stosd ;将 EAX 保存到[EDI] loop L1 exit main ENDP END main