(11)字符串操作指令
字符串操作指令的实质是对一片连续存储单元进行处理,这片存储单元是由隐含指针DS:SI或ES:DI来指定的。字符串操作指令可对内存单元按字节、字或双字进行处理,并能根据操作对象的字节数使变址寄存器SI(和DI)增减1、2或4。具体规定如下:
(1)、当DF=0时,变址寄存器SI(和DI)增加1、2或4;
(2)、当DF=1时,变址寄存器SI(和DI)减少1、2或4。
在后面各指令中,有关变址寄存器都按上述规定进行增减,不再一一说明。
1、取字符串数据指令(Load String Instruction)
从由指针DS:SI所指向的内存单元开始,取一个字节、字或双字进入AL、AX或EAX中,并根据标志位DF对寄存器SI作相应增减。该指令的执行不影响任何标志位。
指令的格式:LODS 地址表达式
LODSB/LODSW
LODSD ;80386+
在指令LODS中,它会根据其地址表达式的属性来决定读取一个字节、字或双字。即:当该地址表达式的属性为字节、字或双字时,将从指针DS:SI处读一个字节到AL中,或读一个字到AX,或读一个双字到EAX中,与此同时,SI还将分别增减1,2或4。
其它字符串指令中的“地址表达式”作用与此类似,将不再说明。
2、置字符串数据指令(Store String Instruction)
该指令是把寄存器AL、AX或EAX中的值存于以指针ES:DI所指向内存单元为起始的一片存储单元里,并根据标志位DF对寄存器DI作相应增减。该指令不影响任何标志位。
指令的格式:STOS 地址表达式
STOSB/STOSW
STOSD ;80386+
3、字符串传送指令(Move String Instruction)
该指令是把指针DS:SI所指向的字节、字或双字传送给指针ES:DI所指向内存单元,并根据标志位DF对寄存器DI和SI作相应增减。该指令的执行不影响任何标志位。
指令的格式:MOVS 地址表达式1, 地址表达式2
MOVSB/MOVSW
MOVSD ;80386+
4、输入字符串指令(Input String Instruction)
该指令是从某一指定的端口接受一个字符串,并存入一片存储单元之中。输入端口由DX指定,存储单元的首地址和读入数据的个数分别由ES:DI和CX来确定。在指令的执行过程中,还根据标志位DF对寄存器DI作相应增减。该指令不影响任何标志位。
与指令有关的操作数ES、DI、DX和CX等都是隐含操作数。
指令的格式:INS 地址表达式
INSB/INSW
INSD ;80286+
5、输出字符串指令(Output String Instruction)
该指令是把一个字符串输入到指定的输出端口中。输出端口由DX指定,其输出数据的首地址和个数分别由DS:SI和CX来确定。在指令的执行过程中,还根据标志位DF对寄存器SI作相应增减。该指令的执行不影响任何标志位。
与指令有关的操作数DS、SI、DX和CX等都是隐含操作数。
指令的格式:OUTS 地址表达式
OUTSB/OUTSW
OUTSD ;80286+
6、字符串比较指令(Compare String Instruction)
该指令是把指针DS:SI和ES:DI所指向字节、字或双字的值相减,并用所得到的差来设置有关的标志位。与此同时,变址寄存器SI和DI也将根据标志位DF的值作相应增减。
指令的格式:CMPS 地址表达式1, 地址表达式2
CMPSB/CMPSW
CMPSD ;80386+
受影响的标志位:AF、CF、OF、PF、SF和ZF
7、字符串扫描指令(Scan String Instruction)
该指令是用指针ES:DI所指向字节、字或双字的值与相应的AL、AX或EAX的值相减,用所得到的差来设置有关标志位。与此同时,变址寄存器DI还将根据标志位DF的值进行增减。
指令的格式:SCAS 地址表达式1
SCASB/SCASW
SCASD ;80386+
受影响的标志位:AF、CF、OF、PF、SF和ZF
8、重复字符串操作指令(Repeat String Instruction)
前面介绍了七种不同的字符串操作指令:取字符串数据、置字符串数据、字符串传送、输入字符串、输出字符串、字符串比较和字符串扫描等指令,所叙述是这些指令执行一次所具有的功能。但我们知道:每个字符串通常会有多个字符的,所以,就需要重复执行这些字符串操作指令。为了满足这种需求,指令系统提供了一组重复前缀指令。
虽然在这些字符串指令的前面都可以添加一个重复前缀指令,但由于指令执行结果的差异,对某个具体的字符串指令又不用重复前缀指令而改用其它循环来实现重复的需要。
重复字符串操作指令对标志位的影响是由被重复的字符串操作指令来决定。
(a) 重复前缀指令REP(Repeat String Instruction)
重复前缀指令是重复其后的字符串操作指令,重复的次数由CX来决定。其一般格式为:
REP LODS/LODSB/LODSW/LODSD
REP STOS/STOSB/STOSW/STOSD
REP MOVS/MOVSB/MOVSW/MOVSD
REP INS/ INSB/INSW/INSD
REP OUTS/OUTSB/OUTSW/OUTSD
重复前缀指令的执行步骤如下:
(1)、判断:CX=0;
(2)、如果CX=0,则结束重复操作,执行程序中的下一条指令;
(3)、否则,CX=CX-1(不影响有关标志位),并执行其后的字符串操作指令,在该指令执行完后,再转到步骤(1)。
从上面的重复前缀指令格式来看,虽然我们可以使用重复取字符串数据指令(第一组指令),但可能会因为指令的执行结果而在程序中几乎不被使用。
例5.20 编写一段程序,计算字符串“12345abcdefgh”中字符的ASCII之和。
解:
…
MESS DB '12345abcdefgh' ;在数据段中进行变量说明
…
MOV AX, SEG MESS
MOV DS, AX
LEA SI, MESS ;用DS:SI来指向字符串的首地址
MOV CX, 13D ;重复次数
XOR BX, BX ;置求和的初值为0
REP LODSB
…
虽然指令“REP LODSB”能从字符串中取出每个字符,但它是在一条指令中完成的,程序的其它指令根本无法处理每次取出的数据,指令的执行结果是:AL只保存最后一次所取出的字符'h'的ASCII码。
所以,为了实现本例的要求,不能使用重复前缀指令,而要把指令“REP LODSB”改写成如下四条指令:
XOR AH, AH ;为后面的累加作准备
again: LODSB
ADD BX, AX ;AL是被取出的字符,AH已被清0
LOOP again
(b) 条件重复前缀指令REPE(Repeat String Conditionally)
条件重复前缀指令与前面的重复前缀指令功能相类似,所不同的是:其重复次数不仅由CX来决定,而且还会由标志位ZF来决定。根据ZF所起的作用又分为二种:相等重复前缀指令REPE/REPZ和不等重复前缀指令REPNE/REPNZ。
A、相等重复前缀指令的一般格式为:
REPE/REPZ SCAS/SCASB/SCASW/SCASD
REPE/REPZ CMPS/CMPSB/CMPSW/CMPSD
该重复前缀指令的执行步骤如下:
(1)、判断条件:CX≠0 且 ZF=1;
(2)、如果条件不成立,则结束重复操作,执行程序中的下一条指令;
(3)、否则,CX=CX-1(不影响有关标志位),并执行其后的字符串操作指令,在该指令执行完后,再转到步骤(1)。
B、不等重复前缀指令的一般格式为:
REPNE/REPNZ SCAS/SCASB/SCASW/SCASD
REPNE/REPNZ CMPS/CMPSB/CMPSW/CMPSD
该重复前缀指令的执行步骤如下:
(1)、判断条件:CX≠0 且 ZF=0;
(2)、如果条件不成立,则结束重复操作,执行程序中的下一条指令;
(3)、否则,CX=CX-1(不影响有关标志位),并执行其后的字符串操作指令,在该指令执行完后,再转到步骤(1)。