来自于《Intel汇编语言程序设计》(第四版)第八章。
裁剪字符串过程从以空字符串结尾的字符串中删除特定字符之后的所有字符。
需要的知识:
1.
重复前缀。
字符串操作指令本身每次只处理一个内存值。但如果增加一个重复前缀的话,该指令就会使用ECX作为计数器进行重复。
几个重复前缀如下:
REP 当ECX>0时重复
REPZ,REPE 当零标志被设置并且ECX>0时重复
REPNZ,REPNE 当零标志被清除并且ECX>0时重复
2.
SCASB,SCASW和SCASD指令,将AL/AX/EAX中的值同目标内存中的字节,字或双字相比较,目标内存数据是由EDI寻址的。
这些指令在一个长字符串或数组中查找一个值的时候特别有用。
如果使用REPE(或REP)前缀,当ECX>0并且AL/AX/EAX不能匹配内存中的值时,指令将继续扫描字符串或数组。
REPNE前缀使得指令扫描字符串直到AL/AX/EAX匹配内存中的一个值或ECX=0为止。
例如下面扫描匹配字符的例子,在字符串变量alpha中查找字符"F",如果找到了该字母,EDI指向匹配字符串后面的一个字符(如果没有找到匹配项,就会执行JNZ指令退出):
.data
alpha BYTE "ABCDEFGH",0
.code
mov edi,OFFSET alpha ; EDI points to the string
mov al,'F' ; search for the letter F
mov ecx,LENGTHOF alpha ; set the search count
cld ; direction = up
repne scasb ; repeat while not equal
jnz quit ; quit if letter not found
dec edi ; found : back up EDI
整个程序的重点就在于红色部分,他会一直循环,直到匹配了F或者ECX=0为止。甚至在循环完之后,还会对是这两种条件的哪一种进行判断。如果是ECX=0(就是循环了所有的字符之后仍未找到),则直接quit;如果不是,则必定是因为匹配了F,则将EDI后退一个字节,指向被匹配的这个字符。
在Str_trim中,我们也要用到scasb。
3.
字符串指令使用CPU的方向标志来决定ESI和EDI是自动增加还是自动减少:
可以使用CLD和STD指令显式地改变,CLD将清除方向标志,STD设置方向标志,其造成影响如下所示:
方向标志被清除:ESI和EDI自动增加,寻址顺序按照从低到高。
方向标志被设置:ESI和EDI自动减少,寻址顺序按照从高到低。
下面我们来看一下裁剪字符串程序源代码:
;----------------------------------------------------------------------------
Str_trim PROC USES eax ecx edi,
pString : PTR BYTE, ; points to string
char : BYTE ; char to remove
;
; remove all occurrences of a given character from
; the end of a string.
; Returns : nothing
;----------------------------------------------------------------------------
mov edi,pString
INVOKE Str_length,edi ; returns length in EAX
cmp eax,0 ; zero-length string ?
je L2 ; yes : exit
mov ecx,eax ; no : counter = string length
dec eax
add edi,eax ; EDI points to last char
mov al , char ; char to trim
std ; direction = reverse
repe scasb ; skip past trim character
jne L1 ; remove first character ?
dec edi ; adjust EDI : ZF=1 && ECX=0
L1: mov BYTE PTR [edi+2],0 ; insert null byte
L2: ret
Str_trim ENDP
程序首先使用:
USES eax ecx edi,
将这几个寄存器的值压进堆栈保存起来。
然后函数接受两个参数:
pString : PTR BYTE, ; points to string
char : BYTE ; char to remove
第一个是指向字符串的地址,第二个是待查找删除的字符。
下面是程序体:
mov edi,pString ; 程序首先将接收到得pString地址保存到edi中
INVOKE Str_length,edi ; 调用Str_length过程求的接收到的pString的长度,长度值会被保存到eax中
cmp eax,0 ; 长度值是否为0?
je L2 ; 如果为零,则跳转到L2,程序结束。
mov ecx,eax ; 如果不为零,则将字符串长度保存到ecx中
dec eax ; eax中的长度值减1
add edi,eax ; 将edi指向字符串pString的最后一个字符
mov al , char ; 将要查找删除的字符保存到al中
std ; ESI和EDI将在执行字符串指令时自动减少,并且寻址顺序是由高到低
repe scasb ; 没看懂
jne L1 ; 没看懂
dec edi ; 没看懂
L1: mov BYTE PTR [edi+2],0 ; 没看懂
L2: ret
后面代码没看懂,始终不明白为什么要将edi指向最后一个字符,后面的那些操作也不清楚。