曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 6852|回复: 0
打印 上一主题 下一主题

扫描字符串(SCAS指令)

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2010-8-17 18:01:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
有时候扫描字符串搜索特定字符或者字符序列是有用的。使用 LODS 遍历字符串虽然可以,但比较耗时。Intel 提供了 SCAS 指令用以搜索字符串中特定的一个字符或者一组字符的方式。SCAS 指令有 3 种方式:
      SCASB : 比较内存中的一个字节和 AL 寄存器中的值
      SCASW :  比较内存中的一个字和 AX 寄存器中的值
      SCASL :  比较内存中的一个双字和 EAX 寄存器中的值
SCAS 使用 EDI 作为隐含目标操作数。EDI 必须包含要扫描的字符串的内存地址。在执行 SCAS 时,EDI 的值按照搜索字符的数据长度递增或递减 (取决于 DF 标志)。

进行比较时,会相应的设置 EFLAGS 的辅助进位、进位、奇偶校验、溢出、符号和零标志。和 CMPS 一样,SCAS 只有和 REPE、REPNE 前缀一起使用时,其方便性才能体现出来。使用 REPE,REPNE 前缀,可以扫描整个字符串,查找特定字符(或者字符序列)。

REPE : 查找不匹配搜索字符的字符,也就是说只要匹配,就还继续查找下去。
REPNE : 查找匹配搜索字符的字符,也就是说只要不匹配,就还继续查找下去。

对于大多数字符串扫描,使用 REPNE 指令,因为它将在字符串中找到搜索字符时停止扫描。当找到字符时,EDI 寄存器包含紧跟在定位到的字符后面的内存地址。这是因为 REPNE 指令在执行 SCAS 指令后递增 EDI 寄存器。ECX 寄存器包含搜索字符距离字符串末尾的位置,所以为了得到举例字符串开头的位置,要从这个值减去字符串长度,然后反转符号。
下面程序使用 SCAS 配合 REPNE 在字符串中搜索一个字符:
.section .data
string1:
     .ascii "This is a test - a long text string to scan."
length:
     .int 44
string2:
     .ascii "-"

.section .text
.global _start

_start:
     nop
     leal string1, %edi
     leal string2, %esi
     movl length, %ecx
     lodsb        #将 "-" 加载到 AL
     cld
     repne scasb   #从 string1 的末尾开始扫描,同时EDI递增
     jne notfound
     subw length, %cx   #CX中的值为28
     neg %cx
     movl $1, %eax
     movl %ecx, %ebx
     int $0x80
notfound:
     movl $1, %eax
     movl $0, %ebx
     int $0x80
运行与输出:
$ ./scas
$ echo $?
16

搜索多个字符
使用 SCASW 和 SCASL 可以搜索 2 个或者 4 个字符序列。这两个指令在扫描时,查找 AX 或 EAX 中的字符序列,他们并不进行逐字符比较,每次比较后,EDI 递增 2 (SCASW) 或者递增 4(SCASL)。如下程序演示:
.section .data
string1:
     .ascii "This is a test - a long text string to scan."
length:
     .int 11
string2:
     .ascii "test"

.section .text
.global _start
_start:
     nop
     leal string1, %edi
     leal string2, %esi
     movl length, %ecx
     lodsl    #装test到EAX
     cld
     repne scasl
     jne notfound
     subw length, %cx
     neg %cx
     movl $1, %eax
     movl %ecx, %ebx
     int $0x80
notfound:
     movl $1, %eax
     movl $0, %ebx
     int $0x80
运行与输出:
$ ./scas2
$ echo $?
0
从输出看到,我们并“没有找到” test ! 这是因为 SCASL 是每 4 个字符的连续查找,形如:
This (第一次)
<空格>is<空格> (第二次)
a<空格>te (第三次)
... ... 所以无法找到 test
如果将上面程序的 string2 改为 This,再编译运行程序则会在 echo 里看到返回值为 1。
虽然 SCASW 和 SCASL 指令不能很好的处理字符串。但它们在数据数组中搜索非字符串数据还是比较有用的,这是因为一个整数通常是 4 个字节。

计算字符串长度
SCAS 的一个非常有用功能是确定零结尾(也称为空结尾)的字符串长度。零结尾字符串在 C 语言中非常常见,在汇编中也可以通过 .asciz 声明一个零结尾字符串。对于零结尾字符串,要搜索的显然是零的位置,并且计数找到零时一共经过了多少个字符。下面程序演示了这种操作:
.section .data
string1:
     .asciz "Testing, one, two, three, testing.\n"

.section .text
.global _start

_start:
     nop
     leal string1, %edi
     movl $0xffff, %ecx    #假设字符串的长度不超过0xffff个字符
     movb $0, %al
     cld
     repne scasb
     jne notfound
     subw $0xffff, %cx
     neg %cx
     dec %cx            #长度中包含了结尾的0所以这里要减1
     movl $1, %eax
     movl %ecx, %ebx
     int $0x80
notfound:
     movl $1, %eax
     movl $0, %ebx
     int $0x80
运行与输出:
$ ./strsize
$ echo $?
35
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-29 11:11 , Processed in 0.073947 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表