什么是串操作指令?
你或许觉得对连续的数据进行重复的操作会花费你大量的时间并且显得代码极其冗余,那为何不试试“专门用于对连续数据进行操作的指令“——串操作指令。”串“操作指令,顾名思义就是对在内存中连续存在的数据进行操作的指令,那为何不是寄存器或者立即数呢?寄存器一共也就16位并且立即数是我们直接给出的操作数,这两者根本不用调用多条指令,一条指令即可KO他们。
串操作指令的特点
说起串操作指令,那它不但具有普通操作指令的共性,同时也具备连续操作指令的特性。要对一连串数据进行操作,我们要知道以下内容:
特性 | 要对多少数据进行操作? | 数据串长度 |
特性 | 数据串操作方向(从低到高还是从高到低) | 串操作方向 |
共性 | 到底进行何种操作? | 操作指令 |
共性 | 数据从哪里来又到哪里去? | 源/目标操作数 |
注意:无论是字符还是数据,在内存中均以其ASCII表示,我们统称之为数据。
首先,我们先来解决一下串操作指令独有的个性:
相关寄存器 | 串操作属性 |
CX计数寄存器 | 数据串长度/操作重复次数 |
FLAGS标志寄存器中DF标志位 | 串操作方向 |
注意:DF标志位与串操作的方向一定别闹混了:
DF标志位状态 | 连续操作方向 | 指令 |
0 | 从低地址到高地址 | CLD |
1 | 从高地址到低地址 | SLD |
其次,我们再来了解一下串操作指令作为操作指令的共性——操作数的寻址方式,除了与累加器AX打交道的串操作指令外,所有串操作指令都具有以下特点:
1)源操作数为SI寄存器操作数,且指向的内存地址默认在数据段(DS:SI),但是允许我们进行段重设,毕竟被移动的数据串所在逻辑段可以是四个逻辑段中的任意一个;
2)目标操作数为DI寄存器操作数,且一定在ES附加段(ES:DI),不允许段重设,这就好比“我们打出租的地点不同,但是目的地均是家“一样。
串操作寄存器可以拓展为以下流程:
由于一个完整的串操作指令的格式为:
[重复操作前缀] 指令助记符 [目标操作数] [源操作数]
因此,我们也可以将流程分解:
串操作指令会对FLAGS寄存器中的标志位产生影响,因此后面可以跟随程序跳转指令。除此之外,如果串操作指令前加上重复前缀,那么串操作指令可以在满足条件的情况下不断循环执行相同的操作。串操作指令的重复前缀如下所示:
重复执行的条件 | 指令助记符 |
CX不为零 | REP |
CX不为零且ZF=1 | REPE/REPZ |
CX不为零且ZF=0 | REPNZ/REPNE |
注意:ZF表示结果为0,常用于判断运算类指令的执行结果。
总之,有关串操作指令的三大块分别为:
地址指针 | 源变址寄存器SI、目标变址寄存器DI |
串操作方向 | DF的状态 |
重复执行前缀 | REP、REPZ/REPE、REPNZ/REPNE |
特别注意:DF状态和源变址寄存器SI、目标变址寄存器DI搭配所出现的情况:
串操作指令解析
数据传送指令
数据传送类指令一共有三个:MOVS、MOVSB、MOVSW,MOVSB和MOVSW均采用隐含寻址的方法,目标操作数默认为DS:SI,源操作数默认为ES:DI。三种指令如下所示:
穿操作指令格式 | 目标操作数 | 源操作数 | 操作数字长 |
MOVS SI DI | DI | SI | 字节或者字 |
MOVSB | 隐含寻址 | 隐含寻址 | 字节 |
MOVSW | 隐含寻址 | 隐含寻址 | 字 |
注意:
1. 源操作数不可以段重设,一定是ES:DI所指向的位置;目标操作数可以段重设,如果不显式进行段重设,那么目标操作数默认在数据段,即DS:SI;
2. 数据串传送类指令常常结合“重复前缀”一起使用,一提高运行效率,而且因为数据传送类指令不影响标志位,因此使用的重复前缀是无条件重复前缀REP。
数据传送类指令的使用方式如下所示:
串比较指令
要想知道一个指令的工作逻辑以及后面一条指令与其的关系,首先我们要关注“这条指令的执行前提”以及“该指令执行完后的影响”。在我们前面算术运算类指令章节中提及过比较指令CMP,CMP属于减法运算指令,影响除DF外的其他全部标志位,但是不影响目标操作数的值,因此比较类指令后面跟着的一定是和标志位有关系的跳转指令。与前面的数据传送类指令一样,传比较指令也分为三个:CMPS、CMPSB、CMPSW,其中CMPSB、CMPSW均采用隐含寻址的方法,目标操作数默认为DS:SI,源操作数默认为ES:DI。
指令类型 | 源操作数 | 目标操作数 | 操作数字长 |
CMPS | DI | SI | 字节或者字 |
CMPSB | 隐含寻址 | 隐含寻址 | 字节 |
CMPSW | 隐含寻址 | 隐含寻址 | 字 |
注意:
1. 源操作数不可以段重设,一定是ES:DI所指向的位置;目标操作数可以段重设,如果不显式进行段重设,那么目标操作数默认在数据段,即DS:SI;
2. 数据串比较类指令常常结合“重复前缀”一起使用,一提高运行效率,而且因为数据比较类指令影响标志位,因此使用的重复前缀是有条件重复前缀REPZ或者REPNZ。
串比较类指令的使用示例:
串扫描指令
CMPS、CMPSB、CMPSW都是将两个数据串进行比较,那有没有一种指令将数据串和一个特定的数据比较呢?有,这就是串扫描指令SCAS、SCASB、SCASW,三者形式如下所示:
指令助记符 | 目标操作数 | 操作数字长 |
SCAS | DI | 字节或者字 |
SCASB | 隐含寻址 | 字节 |
SCASW | 隐含寻址 | 字 |
注意:
1. 串扫描类指令其实有两个操作数,一个是放置比较数据的AX或者AL,另一个是由ES:DI指向的存储器操作数;
2. 串扫描类指令只影响标志位不影响任何一个操作数的数值。
串扫描类指令使用示例:
串装入指令
我们前面学过“将以M1为首的数据串装入以M2为首的数据串“,那现在我们来学习一下”将数据串中的每个数据不断地装入通用寄存器中“的指令——串装入指令(LODS、LODSB、LODSW),这三个指令如下所示:
指令助记符 | 源操作数 | 操作数字长 |
LODS | SI | 字节或者字 |
LODSB | 隐含寻址 | 字节 |
LODSW | 隐含寻址 | 字 |
注意:
1. 串装入指令的目标操作数采用隐含寻址为AX累计寄存器或者AL寄存器(AX累计寄存器的低8位);
2. 源操作数一定是SI,可以进行段重设,如果不显式地段重设,那么默认在数据段,即DS:SI;
3. 由于数据串中的数据装入AX或者AL寄存器中,而且AX和AL寄存器分别为16位和8位,因此串装入指令一般不添加重复前缀,因为装入AX或者AL的数据得等到利用完后,再调用串装入指令将旧值覆盖掉;
4. 每调用一次串装入指令,SI中的地址就会根据DF指向的串操作方向和操作数字长进行自增或者自减。
串存储指令
我们前面学过串装入指令(将串数据装入AX/AL寄存器中并且SI中地址自增/自减),那么这个串存储指令就会使得“将AX/AL寄存器中的数据不断地装入一段内存中“,这个命令常用于将存储器中一段内存赋予初值。该指令也有三种形式:STOS、STOSB、STOSW。
指令助记符 | 目标操作数 | 操作数字长 |
STOS | DI | 字节或者字 |
STOSB | 隐含寻址 | 字节 |
STOSW | 隐含寻址 | 字 |
注意:
1. 串存储指令的源操作数采用隐含寻址为AX累计寄存器或者AL寄存器(AX累计寄存器的低8位);
2. 目标操作数DI一定不可以进行段重设,存储器操作数为ES:DI;
3. 每调用一次串装入指令,DI中的地址就会根据DF指向的串操作方向和操作数字长进行自增或者自减。