字符串处理
字符串是字符的一个序列。对字符串的操作处理包括复制、检索、插入、删除和替换等。为了便于对字符串进行有效的处理,8086/8088提供专门用于处理字符串的指令,我们称之为字符串操作指令,简称为串操作指令。本节先介绍串操作指令及与串操作指令密切相关的重复前缀,然后举例说明如何利用它们进行字符串处理。
1、 字符串操作指令
1).一般说明
8086/8088共有五种基本的串操作指令。每种基本的串操作指令包括两条指令,一条适用于以字节为单元的字符串,另一条适用于以字为单元的字符串。
在字符串操作指令中,由变址寄存器SI指向源操作数(串),由变址寄存器DI指向目的操作数(串)。规定源串存放在当前数据段中,目的串存放在当前附加段中,也即在涉及源操作数时,引用数据段寄存器DS,在涉及目的操作数时,引用附加段寄存器ES。换句话说,DS:SI指向源串,ES:DI指向目的串。
串操作指令执行时会自动调整作为指针使用的寄存器SI或DI之值。如串操作的单元是字节,则调整值为1;如串操作的单元是字,则调整值为2。此外,字符串操作的方向(处理字符串中单元的次序)由标志寄存器中的方向标志DF控制。当方向标志DF复位(为0)时,按递增方式调整寄存器SI或DI值;当方向标志DF置位(为1)时,按递减方式调整寄存器SI或DI之值。
2).字符串装入指令(LOAD String)
字符串装入指令的格式如下:
LODSB ;装入字节(Byte)
LODSW ;装入字 (Word)
字符串装入指令只是把字符串中的一个字符装入到累加器中。字节装入把LODSB把寄存器SI所指向的一个字节数据装入到累加器AL中,然后根据方向标志复位或置位使SI之值增1或减1。它类似下面的两条指令:
MOV AL,[SI]
INC SI 或DEC SI
字装入指令LODSW把寄存器SI所指向的一个字数据装入到累加器AX中,然后根据方向标志DF复位或置位使SI之值增2或减2。类似于如下的两条指令:
MOV AX,[SI]
ADD SI,2 或SUB SI,2
字符串装入指令的源操作是存储操作数,所以引用数据段寄存器DS.
字符串装入指令不影响标志。
下面的子程序使用了LODSB指令。此外,该子程序算法也较好,所以它的效率较高。请与3.6节中的例4作比较。
;子程序名:STRLWR
;功 能:把字符串中的大写字母转化为小写(字符串以0结尾)
;入口参数:DS:SI=字符串首地址的段值:偏移
;出口参数:无
STRLWR PROC
PUSH SI
CLD ;清方向标志(以便按增值方式调整指针)
JMP SHORT STRLWR2
STRLWR1:SUB AL,‘A’
CMP AL,’Z’—’A’
JA STRLWR2
ADD AL,’a’
MOM[SI-1],AL ;注意指针已被调整
STRLWR2:LODSB ;取一字符,同时调整指针
AND AL,AL
JNZ STRLWR1
POP SI
RET
STRLWR ENDP
在汇编语言中,两条字符串装入指令的格式可统一为如下一种格式:
LODS OPRD
汇编程序根据操作数的类型决定使用字节装入指令还是字装入指令。也即,如果操作数的类型为字节,则采用LODSB指令;如果操作数的类型为字,则采用LODSW指令。请注意,操作数OPRD不影响指针寄存器SI之值,所以在使用上述格式的串装入指令时,仍必须先给SI赋合适的值。例如;
……
MESS DB ’HELLO’,0
TAB DW 123,43,332,44,-1
MOV SL,OFFSET MESS
LODS MESS ;LODSB
……
MOV SL,OFFSET TAB
LODS TAB ;LODSW
……
3).字符串存储指令(STOre String)
字符串存储指令的格式如下:
STOSB ;存储字节
STOSW ;存储字
字符串存储指令只是把累加器的值存到字符串中,即替换字符串中的一个字符。
字节存储指令STOSB把累加器AL的内容送到寄存器DI所指向的存储单元中,然后根据方向标志DF复位置位使DI之值 增1或减1。它类似下面的两条指令:
MOV ES:[DI],AL
INC DI 或 DEC DI
字装入指令STOSW把累加器AX的内容送到寄存器DI所指向的存储单元中,然后根据方向标志DF复位或置位使DI之值增2或减2。类似于如下的两条指令:
MOV ES:[DI],AX
ADD DI,2 或 SUB DI,2
字符串存储指令的源操作是累加器AL或AX,目的操作是存储操作数,所以引用当前附加段寄存器ES。
字符串存储指令不影响标志。
在汇编语言中,两条字符串存储指令的格式可统一为如下一种格式:
STOS OPRD
汇编程序根据操作数OPRD的类型决定使用字节存储指令还是字存储指令。操作数OPRD不影响指针寄存器DI之值。
例如;如下程序片段把当前数据段中便移1000H开始的100个字节的数据传送到从便移2000H开始的单元中。
CLD ;方向标志(以便按增值方式调整指针)
PUSH DS ;由于在当前数据段中传送数据
POP ES ;所以使ES等于DS
MOV SI,1000H ;置源串指针初值
MOV DI,2000H ;置目的串指针初值
MOV CX,100 ;置循环次数
NEXT:LODSB ;取一字节数据
STOSB ;存一字节数据
4).字符串传送指令(MOVe String)
字符串传送指令的格式如下:
MOVSB ;字节传送
MOVSW ;字传送
字节传送指令MOVSB把寄存器SI所指向的一个字节数据传送到由寄存器DI所指向的存储单元中,然后根据方向标志DF复位或置位使SI和DI之值分别增1或减1。字传送指令MOVSW把寄存器SI所指向的一个字数据传送到由寄存器DI所指向的存储单元中,然后根据方向DF标志复位或置位使SI和DI之值分别增2或减2。注意,根据DS和SI计算源操作数地址,根据ES和DI计算目的操作数地址。
字符串传送指令不影响标志。
该指令的源操作数和目的操作均在存储器中,它与下面的字符串比较指令一起属于特殊情况。
在汇编语言中,两条字符串传送指令的格式可统一为如下一种格式:
MOVS ORPD1, ORPD2
两个操作数的类型应该一致。汇编程序根据操作数的类型决定使用字节传送指令还是字传送指令。也即,如果操作数的类型为字节,则采用MOVSB指令;如果操作数的类型为字,则采用MOVSW指令。请注意,操作数OPRD1或OPRD2可起到方便阅读程序的作用,但不影响寄存器SI和DI之值,所以在使用上述格式的串传送指令时,仍必须先给SI和DI赋合适的值。
上面我们利用了字符串装入指令和字符串存储指令的结合实现数据块的移动,现在利用字符串传送指令实现数据块的移动。假设要求同上,程序片段如下,请作比较。
CLD ;清方向标志
…… ;其它指令同上
MOV CX,100 ;置循环次数
NEXT:NOVSB ;每次传送以字节数据
LOOP NEXT
现在循环体中只有一条串传送指令,指令速度可明显提高。在这个程序片段中,把100个字节的数据当作以字为单元的字符串,那么这个字符串也就执友50个单元了,于是循环次数可减少一半,执行速度还会提高。改写后的程序片段如下:
CLD ;清方向标志
…… ;其它指令同上
MOV CX,100/2 ;置循环次数
NEXT: MOVSW ;每次传送一字节数据
LOOP NEXT
5).字符串扫描指令(SCAn String)
字符串比较指令的格式如下:
SCASB ;串字节扫描
SCASW ;串字扫描
串字节扫描指令SCASB芭累加器的内容与由寄存器所指向一个字节数据采用相减方式比较,相减结果反映到各有关标志位(AF,CF,OF,PF,SF和ZF),但不影响两个操作数,然后根据方向标志复位或置位使之值增1或减1。串字扫描指令SCASW把累加器AX的内容由寄存器DI所指向的一个字数据比较,结果影响标志,然后DI之值增2或减2。
下面的程序片段AL中的字符是否为十六进制数符:
……
STRING DB ’0123456789ABCDEFabcdef’
STRINGL EQU $ — STRING
……
CLD
MOV DX,SEG STRING
MOV ES,DX
MOV CX,STRINGL
MOV DI,OFFSET STRING
NEXT: SCASB
LOOPNZ NEXT
JNZ NOT_FOUND
FOUND: ……
……
NOT_FOUND:
……
在汇编语言中,两条字符串比较指令的格式可统一为如下一种格式:
SCAS OPRD
汇编程序根据操作数的类型决定使用串字节扫描指令还是串字扫描指令。
6).字符串比较指令(COMPare String)
字符串比较指令的格式如下:
CMPSB ;串字节比较
CMPSW ;串字比较
串字节比较指令CMPS把寄存器SI所指向的一个字节数据与由寄存器DI所指向一个字节数据采用相减方式比较,相减结果反映到各有关标志位(AF,CF,OF,PF,SF和ZF),但不要影响两个操作数,然后根据方向标志DF复位或置位使SI和DI之值分别增1或减1。串字比较指令CMPSW把寄存器SI所指向的一个字数据与由寄存器DI所指向的一个字数据比较,结果影响标志,然后按调整2调整和之值。
在汇编语言中,两条字符串比较指令的格式可统一为如下一种格式:
CMPS OPRD1, OPRD2
两个操作数的类型应该一致。汇编程序根据操作数的类型决定使用串字节比较指令还是串字比较指令。请注意,OPRD1或OPRD2不影响寄存器SI和DI之值和段寄存器DS和ES之值。
2. 重复前缀