汇编基础
字符串和数组
如果可以有效的处理字符串和数组,就能掌握代码优化中的最常见情况。研究表明,绝大多数程序用90%的运行时间执行其10%的代码。毫无疑问,这10%通常发生在循环中,而循环正是处理字符串和数组所要求的结构。本节以编写高效代码为目的,阐释字符串和数组的处理技术
串处理
字符串操作指令的实质是对一片连续存储单元进行处理,这片存储单元是由隐含指针DS:SI或ES:DI来指定的。字符串操作指令可对内存单元按字节或字进行处理,并能根据操作对象的字节数使变址寄存器SI(和DI)增减1或2。
字符串基本指令
x86指令集有5组指令用于处理字节,字,双字数组。虽然他们被称为字符串原语(string primitives),但它们并不局限于字符数组。
指令 | 说明 |
---|---|
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寻址的内存数据加载到累加器 |
使用重复前缀
指令 | 说明 |
---|---|
REP | ECX>0时重复 |
REPZ,REPE | 零标志位置1且ECX>0时重复 |
REPNZ,REPNE | 零标志位清零且ECX>0时重复 |
实例:复制字符串
1 | cld ;清除方向标志位 |
上面例子,MOVSB从string1传10字节到string2,每次rep前先测试ECX是不是0,若为0则跳过这句,若ECX>0则将ECX减1执行MOVSB,而MOVSB会自动增加ESI和EDI,这个操作由CPU的方向标志位控制。
MOVSB减1,MOVSW减2,MOVSD减4
方向标志位
方向标志位的值 | 对ESI和EDI的影响 | 地址顺序 |
---|---|---|
0 | 增加 | 低到高 |
1 | 减少 | 高到低 |
可以使用CLD和STD显示修改方向标志位
CLD ;方向标志位清零(正向)
STD ;方向标志位置1(反向)
比较
CMPSB, CMPSW,SMPSD用于比较字节,字,双字,同样方向标志位决定EDI和ESI增加还是减少
实例:比较双字
1 | .data |
REPE前缀重复比较操作,自动增加ESI和EDI直到ECX=0或者发现了一对不相等的双字
扫描
SCASB,SCASW,SCASD指令分别将AL,AX,EAX中的值与EDI寻址的一个字节,字,双字进行比较。这些指令可以用于在字符串或数组中寻找一个数值,结合REPE。
实例:扫描是否有匹配字符
1 | .data |
需要记得减1才是F的位置,不然指向的是后一位,循环之后添加了JNZ,以避免出现由于ECX=0而没有找到AL中的字符结束循环的可能性
填充
LODSB,LODSW,LODSD指令分别从ESI指向的内存地址加载一个字节或一个字到AL,AX,EAX。ESI按照方向标志位的状态递增或递减。LODS很少与REP前缀一起使用。原因是,加载到累加器的新值会覆盖其原先的内容。所以LODS常常被用于加载单个数值
实例:数组乘法
1 | INCLUDE Irvine32.inc |
部分字符串过程
用Irvine32链接库中的几个过程来处理空字节结束的字符串。这些过程和标准C库有明显的相似性:
1 | ;将源串复制到目的串 |
Irvine64库中的字符串过程
- Str_compare
比较两个字符串
输入参数:RSI为源指针,RDI为目的指针
返回值:若源串<目的串,进位标志位CF=1,若源串=目的串,ZF=1,若源串>目的串,ZF=0且CF=0 - Str_copy
将源串复制到目的指针指向的位置
输入参数:RSI为源串指针,RDI指向被复制串将要存储的位置 - Str_length
返回以空字节结束字符串的长度
输入参数:RCX为字符串指针
返回值:RAX为该字符串的长度
二维数组
在汇编语言的程序员看来,二维数组是一维数组的高级抽象。高级语言有两种方法在内存中存放数组的行和列:行主序和列主序,就是按行的顺序来排列或者按列的顺序来排列
二维数组的定义和访问
1 | .data |
64位模式下的基址-变址操作数
1 | Crlf PROTO |
需要注意的是,如果使用寄存器索引操作数,就必须使用64位寄存器