一、关于串操作的一些说明:
虽然我们知道,在高级语言里面,串 和 一般的数据的标识符会有一些不一样(比如C++ 里面整形的 int 以及字符串的 string )但是,在底层语言里面,他们类型其实都是一样的二进制数。只不过串是一个数据块。我们用下图说明一下:
那么, 在执行串操作之前,我们需要确定以下的几件事情:
- 串所在的区域,串的首地址(这里的串包括源串和目标串)
- 串的长度
- 串操作的方向,这个我们用一张图来理解:
如果横向的蓝色箭头表示的是源串首,假如串操作的方向是 由低地址向高地址,那么实际上要操作的串就是下方的一块区域;但是如果方向是 由高地址向低地址,那么要操作的串就应该是上方的一块区域。二者的串本身完全不一样!
- 串操作的指令之前可以加上一个重复前缀。
下面,我们一一来解释以下:
【1】首先是串的位置:我们的操作对象是 源串和目标串。其中,源串默认的段首地址存放在
(数据段里面),串首地址存放在
,即:
。(其中
可以使用段超越变成其他段)
而目标串它的段地址默认是在附加段
,串首地址在
里面(注意
不允许被段超越!)
【2】关于串的长度,我们会将串的长度值存放在 里面。在使用串操作指令之前,我们需要给 赋初值
【3】关于串操作的方向,我们使用状态标识里面的 来表示:如果 表示的是串操作从低地址向高地址执行;如果 ,那么表示从高地址向低地址执行。同样地,我们也要在使用串操作指令之前,给 赋初值: 表示将 清零; 表示将 置1
【4】关于串操作指令前面的循环前缀(这是串操作指令所特有的),重复前缀可以分为无条件前缀和条件前缀。其中,无条件前缀我们用 表示,它的意思是:只要当 的情况下,我们就一直执行 后面跟着的语句 我们可以看到,因为是无条件地反复执行,因此,后面常常跟串传输类指令
而对于条件前缀,我们分两种:
(1)REPZ,或者说 REPE(都行):它的意思是"相等则重复“当
,且
时,重复执行
(2)REPNZ,或者说 REPNE:它的意思是“不相等则重复”,即当
,且
时重复执行
我们可以看到,对于条件前缀,后面常常跟串运算类指令
1.1 串操作指令执行的一般流程
我们用下图说明:
其中,我们可以看到流程图主要分成了两块:左边的四个步骤,我们称之为初始化。这里需要特别说明:串操作指令中,每操作完一个字节或者一个字之后,串首指针是指令自动更改的。不需要我们手动设置。而上图几乎是所有串操作指令执行都需要经过的流程图,我们发现:在每操作完一个字节或者一个字之后,并不是立刻就去判断是不是已经完成了所有操作,而是在修改完地址指针之和,还需要经过修改串长度(这需要我们自己写程序控制)的这个操作之后,才到判断。
下面,我再把这个图分模块画一下:
那么,试想一下:假如我们的串操作方向是 DF = 0,而且是一个串传输指令;那么在所有数据传输完毕之后,串首指针应该是在串尾的后一位!
二、正题——串操作指令
2.1 串传送指令 MOVS
首先是串传送:
,更具体地说,应该分成两种:
和
顾名思义就是传送1个字节(也就是8位)的串;而
则是传送 1个字的串。
我们看一个例子学习它的用法:
使用 指令,将200个字节数据从内存数据段 MEM1 为首地址的区域送到一个逻辑段 MEM2
为首地址的区域:(我们下面给出程序的分析):
LEA SI, MEM1 //这句话的意思是给源串的串首赋初值(要赋值给SI寄存器)
LEA DI MEM2 //这句话也是给目标串的串首地址赋初值(要赋值给 DI 寄存器)
MOV CX 200 //给 串的长度赋初值(要赋值给 CX)
CLD //令 DF = 0,说明是从低地址向高地址执行
REP MOVSB //无条件重复:只要 CX ≠0 就一直执行串的传输
HLT //当REP跳出来了,就结束
当然也是可以用 的,这样的话,我们的 CX 就要赋初值为 100 了,因此 一次操作16位
那么,假如我们想要验证我们传输的串对不对,那么就需要将这两个串进行比较。因此就引出了串比较指令:
2.2 串比较指令 CMPS
同样地,CMPS 也分为:CMPSB, CMPSW。它执行的是啥呢?—— 目标串 - 源串,但是结果并不返回目标串。也即是实现两串的比较。
对于串比较指令,我们一般在它前面加条件前缀。
那么,依旧是刚刚的例子,我们在传输完了 200 个字节的串之后,想要检测一下是否正确传输,我们就需要比较两个串了:下面是程序分析:
LEA SI, MEM1
LEA DI MEM2
MOV CX, 200
CLD //上面四条语句全都是初始化
REPE CMPSB
JZ STOP //JZ 表示当 ZF = 1 时执行跳转,执行到这里如果ZF仍然为1说明两串一样
DEC SI //完成SI的自减,回顾上面的流程图,SI现在是在串低的后一位
MOV AL, [SI]
MOV BX, SI
STOP: HLT
首先,我们明确一件事情:REPE 的意思是相同即重复( ,且 ),也就是说当比较的串一样时,就继续比。那么 REPE 退出的情况有什么呢?
- CX = 0(说明比完了,那么就是两个串完全一样)
- ,但是 (说明在中间有两个串不一样的地方了)
JZ 表示当 时执行跳转,跳转到 STOP
2.3 串扫描指令
我们常常使用的是 或 。当我们使用 时,源操作数是不用写出来的,但是要在程序的前面声明。(注意:使用 时,源操作数是 或者 。当使用 时,一次操作一个字节单元,源操作数就是 )
另外,说一下 都干了什么:
我们以 为例,它执行的是将 里面存放的字节依次 和串内的每一个字节单元相减,但是结果不返回,影响标志位 ,用于搜索串里面有没有哪个字节单元的内容和 的一样。
举一个例子:在 ES段中从 2000H 单元开始存放了 10 个字符,寻找其中有没有字符 ‘A’,若有则记录搜索次数,将搜索次数写入 DATA1 单元,并将 ‘A’ 写入 DATA2 单元。
首先,我们要明确一件事情:目标串地址在 ES:DI 里面。
MOV DI, 2000H //先给DI 赋值
MOV BX, DI //地址备份,用于后面计算搜索次数
MOV CX, 10 //因为如果采用SCASB,那么就是一个字节一个字节搜索,10次
CLD
REPNZ SCASB //“若不相等则重复”,如果不相等,即ZF = 0 ,那么就一直搜索
JZ FOUND //若 ZF = 1 就跳转到 FOUND(ZF=1 说明找到一样的了)
MOV DI, 0
JMP DONE
FOUND: DEC DI //因为 DI 是先自增之后才进入判断的,所以在跳出之后DI 会在 'A' 的后一个单元
MOV DATA1, DI
INC DC
SUB DI, BX //计算搜索次数
DONE: MOV DATA2,DI
HLT
2.4 串加载与串存储
【1】首先是串加载,我们用 或 ,既然是 加载,那么就是将串中的数据装载到某个地方,因此,在 或 中,串是作为源操作数的,地址在
如果使用的是
,那么它执行的操作是将数据段中地址为
的单元装入
;
如果使用的是
,那么它执行的操作是将数据段中地址为
的单元装入
;
【2】下面是串存储,我们用 或 ,既然是存储,就是把某个内容存储进串里。因此,此时串就作为目标串,地址为:
如果使用的是
,那么它执行的是将
里面的内容存储进
内
如果使用的是
,那么它执行的是将
里面的内容存储进
内
注意:在串加载与串存储中,是已经限定了 的!