由于串操作指令每次只能对字符串中的一个字符进行处理,所以只用了一个循环,以便完成对整个字符的处理。为了进一步提高效率,8086/8088还提供了重复指令前缀。重复前缀可加在串操作指令之前,达到重复执行其后的串操作指令的目的。
1).重复前缀REP
REP用作为一个串操作指令的前缀,它重复其后的串操作指令动作。每一次重复都先判CX断是否为0,如果为0就结束重复,否则CX的值减1,重复其后的串操作指令。所以当CX值为0时,就不执行其后的字符串操作指令。
它类似于LOOP指令,但LOOP指令是先把CX的值减1,后再判是否为0。
注意,在重复过程中CX的减1操作,不影响各标志。
重复前缀REP主要用在串传送指令MOVS和串存储指令STOS之前。值得指出的是,一般不在LODSB或LODSW指令之前使用任何重复前缀。
使用重复前缀REP,可进一步改写前面的移动数据块的程序片段如下,请作比较:
CLD ;如果已清方向标志,则这条指令可省
…… ;其它指令同上
MOV CX,50
REP MOVSW ;重复执行(CX)次
在下面的子程序中,重复前缀REP与串存储操作指令配合,实现用指定的字符填充指定的缓冲区。
;子程序名:FILLB
;功 能:用指定字符填指定缓冲区
;入口参数:ES:DI=缓冲区首地址
; CX=缓冲区长度,AL=充填字符
;出口参数:无
FILLB ORPC
PUSH AX
PUSH DI
JCXZ FILLB_1 ;CX值为0时直接跳过(可省)
CLD
SHR CX,1 ;字节数转成字数
MOV AH,AL ;使AH与AL相同
REP STOSW ;按字填充
JNC FILLB_1 ;如果缓冲区长度为偶数,则转
STOGB ;补缓冲区长度为奇数时的一字节
FILLB_1: POP DI
POP AX
RET
FILLB ENDP
在上面的子程序中,先按字充填缓冲区,然后再处理可能出现的“零头”,这与重复CX次字节充填相比,可获得更高的效率。注意,字符串存储指令STOSW不影响标志。
2).重复前缀REPZ/REPE
REPZ与REPE是一个前缀的两个助记符下面的介绍以REPZ为代表。
REPZ用作为一个串操作指令的前缀,它重复其后的串操作指令动作。每重复一次,CX的值减1,重复一直进行到CX为0或串操作指令使零标志ZF为0时止。重复结束条件的检查是在重复开始之前进行的。
注意,在重复过程中CX的值减1操作,不影响标志。
重复前缀REPZ主要用在字符串比较指令CMPSB和字符串扫描指令SCSA之前。由于传送指令MOVS和串存储指令STOS都不影响标志,所以在这些串操作指令前使用前缀REP和前缀REPZ的效果一样。
在下面的子程序中,重复前缀REPZ与串比较指令CMPSB配合,实现两个字符串的比较。重复前缀REPZ与CMPSB的配合表示当相同时继续比较。
;子程序名:STRCMP
;功 能:比较字符串是否相同
;入口参数:DS:SI=字符串1首地址的段值:偏移
; ES:DI=字符串2首地址的段值:偏移
;出口参数:AX=0表示两字符串相同,否则表示字符串不同
;说 明:设字符串均以0为结束标志
STRCMP PROC
CLD
PUSH DI
XOR AL,AL ;先测一个字符串的长度
MOV CX,0FFFFH
NEXT: SCASB
JNZ NEXT
NOT CX ;至此CX含字符串2的长度(包括结束标志)
POP DI
REPZ CMPSB ;两个串比较(包括结束标志在内)
MOV AL,[SI-1]
MOV BL,ES:[DI-1]
XOR AH,AH ;如两个字符串相同,则AL应等于BL
MOV BH,AH
SUB AX,BX
RET
STRCMP ENDP
如果重复前缀REPZ与SCASB相配合,则表示当相等时继续搜索,直到第一个不等时为止(当然CX之值决定了最终搜索的次数)。
3).说明
重复的字符串处理操作过程可被中断。CPU在处理字符串的下一个字符之前识别中断。如果发生中断,那么在中断处理返回以后,重复过程在从中断点继续执行下去。但应注意,如指令前还有其他前缀(段超越前缀或锁定前缀)的话,中断返回时其他的前缀就不在有效。因为CPU在中断时,只能“记住”一个前缀,即字符串操作指令前的重复前缀。如字符串操作指令必须使用一个以上的前缀,则可在此之前禁止中断。
3. 字符串操作举例
下面再举几例来说明字符串操作指令和重复前缀的使用,同时说明如何进行字符串操作。
例2.1:写一个判别字符是否在字符串中出现的子程序。设字符串以0结尾。
串扫描指令可用于在字符串中搜索指定的字符,从而判别字符是否属于字符串。下面的子程序并没有利用串扫描指令,代码虽长,自有其独到之处,请注意。
;子程序名:STRCHR
;功 能:判字符是否属于字符串
;入口参数:DS:SI搜索字符串首地址的段值:偏移
; AL=字符代码
;出口参数:CF=0表示字符在字符串中,字符首次出现处的偏移
; CF=1表示字符不在字符串中
STRCHR PROC
PUSH BX
PUSH SI
CLD
MOV BL,AL ;字符串复制到BL寄存器
TEST SI,1 ;判地址是否为偶
JZ STRCHR1 ;是,转
LODSB ;取第一个字符,比较之
CMP AL,BL
JZ STRCHR3
AND AL,AL
JZ STRCHR2
STRCHR1: LODSW ;取一个字
CMP AL,BL ;比较低字节
JZ STRCHR4
AND AL,AL
JZ STRCHR2
CMP AH,BL
JZ STRCHR3
AND AH,AH
JNZ STRCHR1
STRCHR2: STC
JMP SHORT STRCHR5
STRCHR3: INC SI
STRCHR4: LEA AX,[SI-2]
STRCHR5: POP SI
POP BX
RET
STRCHR ENDP
上面的子程序对从奇地址开始存放的字符串的第一个字符作了特别处理。在随后的循环处理中,字符串便从偶地址开始,每次取一个字,即两个字符,在逐个字符比较。为什么要从偶地址开始取一个字?较好的理由留给读者思考。
例2.2:写一个在字符串1后追加字符串2的子程序。设字符串均以0结尾。
该子程序的实现流程如图6.1所示。现再作几点说明:(1)要传送的字符串2包括其结束标志;(2)字符串2的传送以字传送为主,考虑了从偶地址开始进行字的传颂;(3)最后处理可能遗留的一字节
;子程序明:STRCAT
;功 能:在字符串1末追加字符串2
;入口参数:DS:SI字符串1起始地址的段值:偏移
; DS:DI字符串2起始地址的段值:偏移
;出口参数:无
;说 明:不考虑在字符串1后是否留有足够的空间
STRCAT PROC
PUSH ES
PUSH AX
PUSH CX
PUSH SI
PUSH DI
CLD
PUSH DS
POP ES
PUSH DI
MOV DI,SI
XOR AL,AL
MOV CX,0FFFFH
REPNZ SCASB ;确定字符串1的尾
LEA SI,[DI-1] ;SI指向字符串1的结束标志
POP DI
MOV CX,0FFFFH
REPNZ SCASB ;CX测字符串2的长度
NOT CX ;DI为字符串2包括结束标志的长度
SUB DI,CX ;再次指向字符串2的首
XCHG SI,DI ;为拼接作准备
TEST SI,1 ;字符串2是否从奇地址开始?
JZ STRCAT1
MOVSB ;特别处理第一字节
DEC CX
STRCAT1: SHR CX,1 ;移动数据块长度除2
REPZ: MOVSW ;字移动
JNC: STRCAT2
MOVSB ;补字移动时遗留的一字节
STRCAT2: POP DI
POP SI
POP CX
POP AX
POP ES
RET
STRCAT ENDP