四、串操作类指令
数据串是存储器中一片连续的存储单元,字符串操作指令的实质是对一片连续存储单元进行处理,这片存储单元是由隐含指针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. MOVS指令
功能: 字符串传送指令
语法: MOVSB/MOVSW
影响标志: AF、CF、OF、PF、SF和ZF
串传送指令MOVS将数据段主存单元的一个字节或字,传送至附加段的主存单元中去。助记符中字母B和W分别表示字节和字串操作。
串操作指令中,源操作数用寄存器SI寻址,默认在数据段DS中,但允许段超越;目的操作数用寄存器DI寻址,默认在附加段ES中,不允许段超越。每执行一次串操作指令,作为源地址指针的SI和作为目的地址指针的DI将自动修改:±1(对于字节串)或±2(对于字串)。地址指针是增加还是减少则取决于方向标志DF。在系统初始化后或者执行指令CLD后,DF = 0,此时地址指针增1或2;在执行指令STD后,DF = 1,此时地址指针减1或2。
该指令的执行不影响任何标志位。例如,将数据段source指示的100个字节数传送到附加段destination指示的主存区:
mov si,offset source ;汇编伪指令offset指示source的偏移地址
mov di,offset destination
mov cx,100 ;cx←传送次数
cld ;设置DF=0,实现地址增加
again: movsb ;传送一个字节
dec cx ;传送次数减1
jnz again ;判断传送次数cx是否为0。不为0(ZF=0),
;则转移到again位置继续执行;否则,结束
2. CMPS指令
功能: 字符串比较指令
语法: CMPSB/CMPSW
影响标志: AF、CF、OF、PF、SF和ZF
该指令是把指针DS:SI和ES:DI所指向字节、字或双字的值相减,并用所得到的差来设置有关的标志位。与此同时,变址寄存器SI和DI也将根据标志位DF的值作相应增减。
3. SCAS指令
功能: 字符串扫描指令
语法: SCASB/SCASW
影响标志: AF、CF、OF、PF、SF和ZF
该指令是用指针ES:DI所指向字节、字的值与相应的AL、AX的值相减,用所得到的差来设置有关标志位。与此同时,变址寄存器DI还将根据标志位DF的值进行增减。
4. LODS指令
功能: 取字符串数据指令
语法: LODSB/LODSW
从由指针DS:SI所指向的内存单元开始,取一个字节、字进入AL、AX中,并根据标志位DF对寄存器SI作相应增减。该指令的执行不影响任何标志位。在指令LODS中,它会根据其地址表达式的属性来决定读取一个字节或字。即:当该地址表达式的属性为字节或字时,将从指针DS:SI处读一个字节到AL中,或读一个字到AX,与此同时,SI还将分别增减1或2。其它字符串指令中的“地址表达式”作用与此类似,将不再说明。
5. STOS指令
功能: 置字符串数据指令
语法: STOSB/STOSW
该指令是把寄存器AL、AX的值存于以指针ES:DI所指向内存单元为起始的一片存储单元里,并根据标志位DF对寄存器DI作相应增减。该指令不影响任何标志位。
6. INS指令
功能: 输入字符串指令
语法: INSB/INSW
该指令是从某一指定的端口接受一个字符串,并存入一片存储单元之中。输入端口由DX指定,存储单元的首地址和读入数据的个数分别由ES:DI和CX来确定。在指令的执行过程中,还根据标志位DF对寄存器DI作相应增减。该指令不影响任何标志位。与指令有关的操作数ES、DI、DX和CX等都是隐含操作数。
7. OUTS指令
功能: 输出字符串指令
语法: OUTSB/OUTSW
该指令是把一个字符串输入到指定的输出端口中。输出端口由DX指定,其输出数据的首地址和个数分别由DS:SI和CX来确定。在指令的执行过程中,还根据标志位DF对寄存器SI作相应增减。该指令的执行不影响任何标志位。与指令有关的操作数DS、SI、DX和CX等都是隐含操作数。
8. REP指令(重点)
前面介绍了七种不同的字符串操作指令:取字符串数据、置字符串数据、字符串传送、输入字符串、输出字符串、字符串比较和字符串扫描等指令,所叙述是这些指令执行一次所具有的功能。但我们知道:每个字符串通常会有多个字符的,所以,就需要重复执行这些字符串操作指令。为了满足这种需求,指令系统提供了一组重复前缀指令。
虽然在这些字符串指令的前面都可以添加一个重复前缀指令,但由于指令执行结果的差异,对某个具体的字符串指令又不用重复前缀指令而改用其它循环来实现重复的需要。重复字符串操作指令对标志位的影响是由被重复的字符串操作指令来决定。
(1)重复前缀指令REP
重复前缀指令是重复其后的字符串操作指令,重复的次数由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)。
从上面的重复前缀指令格式来看,虽然我们可以使用重复取字符串数据指令(第一组指令),但可能会因为指令的执行结果而在程序中几乎不被使用。例如,编写一段程序,计算字符串“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
(2)条件重复前缀指令
条件重复前缀指令与前面的重复前缀指令功能相类似,所不同的是:其重复次数不仅由CX来决定,而且还会由标志位