汇编之串处理指令
前言
串处理指令一般与REP指令配合。
因为串操作指令其实只是对
“串”主要就体现在REP上
REP是前缀,但它不是伪指令,REP有具体功能,它可以判断CX是否为0,还可以执行CX=CX-1
CX的递减不影响标识位
串处理指令
与REP相配合的串传送指令
串传送类指令不影响标志位。
也就是说
MOVSB、MOVSW、MOVS;
LODSB、LODSW、LODS;
STOSB、STOSW、STOS
都不影响标志位。
但是它们受到方向标志位DF的影响。
很好的关于指令影响标志位的百度文档。
REP
REP重复串操作直到CX的内容为0为止
请注意,REP是前缀,但它不是伪指令,因为它有具体功能。像上面列出的判断CX是否为0,让CX-1等都是它的功能。
还要注意CX的递减不影响标志位。
MOVS
格式
MOVSB和MOVSW不需要有操作数,因为他们的操作数都是定好了的。
就是ES:DI⬅DS:SI(注意箭头方向)
不同的就是转移的是字节还是字
注意BYTE PTR和WORD PTR
它们用于指明字节
MOV ES:DI DS:SI
有一个操作数写明WORD PTR或BYTE PTR就行了。
MOV这种指令,起码有一个操作数一定要是位数清晰的,如果不清晰明确,就要指明
像PUSH,POP这种指令,就不需要指明,因为它们就只做字操作。
执行的操作
方向标志位DF决定串传送的方向。
SI和DI加一或者减一,这是由DF决定的。
如果CLD了,DF=0了,那就是SI+1和DI+1
串操作的准备工作
一般来说,源串存放在数据段中,目的串存放在附加段中。
使用REP重复前缀前,要把数据串长度放入CX
回忆一下移位操作,如果移位次数大于1,也要将移位次数放入CL
做准备工作不要忘记设置方向标志
CLD——使DF=0——增地址——先传小地址
STD——使DF=1——减地址——先传大地址
带重复前缀的串传送指令
先判断是否为0,再减1,再执行传送操作
REP MOVSB
REP MOVSW
这两条语句都不影响标志位。
例
例一
例二
17,也就是不包括$
$在汇编中是字符串结束的标志
$在字符串中显示不了,如果偏得要他显示那就得找别的方法。
REP MOVSB
先判断CX是否为0,CX=17,显然不为0,于是CX-1=16,再执行传送字节的操作,然后SI+1,DI+1,再判断CX是否为0,再减1,再执行传送字节操作,直到CX减到0,判断的时候发现CX是0,就停止操作
简洁的总结一下:
首先判断CX是否为0
不等于0的话
- CX-1
- 传送字节
- SI±1(或2),DI±1(或2)
再判断CX是否为0
如此循环
一开始SI和DI指向要被复制和填充的第一个单元,最后SI和DI指向被复制和填充的最后一个单元的下一个单元。
STOS
注意存入字节的话,用的是AL
其实我感觉这种情况下,用的一般都是AL哈
STOS是存进去一堆一样的,可以用于初始化。
注意执行完之后,DI是指向最后一个单元的下一个单元。
20H是空格的ASCII码
LODS从串取指令
LODS拿出来的只能存在AX里
所以与REP连用是没有意义的
最后AX中只有最后拿出来的那一个
就像狗熊掰苞米,最后夹在咯吱窝里的只有哪一个了。
注意高字节在高地址,低字节在低地址
串比较指令
与REPE/REPZ和REPNE/REPNZ联合工作的串比较指令CMPS和SCAS
REPE/REPZ和REPNE/REPNZ中CX的递减不影响标志位(即使CX减到零,也不影响)。
CMPS/SCAS则根据减法的结果设置标志位。
说实话这里,还有前面的STOS之类的,可能基本概念你掌握了,但是应用还是不熟练的,这个我们之后再补充,比如说
LODS SRC
这个用法你还是不知道怎么用啊
(虽然这个用法可能也不常见)
总之我们之后再通过实践来补充吧。但是这块的实践可是也不简单!加油!
注意:
字符串比较,关键的唯一的条件是ZF
如果只有最后一个字节不同,CX=0后退出重复。这个时候你还是要看ZF!如果你看到CX=0就觉得比较完了他俩都一样,这是不行的,你必须看ZF,如果ZF=0,就说明最后一个字节不同;ZF=1,就说明最后一个字节相同。
其实不止是最后一个字节,每个字节的比较的结果都要看ZF才能看出来
关键在于CX=0是退出条件,CX不能体现字符串比较出的结果,CX=0只能说明每一个都比完了,至于结果,结果唯一的条件就是ZF
REPE/REPZ 当相等/为零时重复串操作
REPE和REPZ是完全相等的同义词!两者可以随意相互替换。
E-Equal 相等
Z-ZF
相等的话,结果为0,ZF就等于1(这里真的好容易犯糊涂),因此从字面上也能看出虽然指令有一个字母的不同,但它们的意思是一样的。
REPE/REPZ——相等则重复,也就是说
ZF=1且CX≠0时重复
ZF=1,表示结果为0,两数相等;CX≠0,表示任务还没结束,还没扫描到字符串的结尾。
常用于比较指令
如果一直扫描到最后,我们就可以说这两个字符串完全相等。
执行的操作是先判断CX是否为0,ZF是否为0,CX=0,ZF=0两个条件满足一个就退出。否则继续向下,CX=CX-1,执行其后的串指令。
重复上述过程。
注意CX的递减不影响标志位。
DF决定先查小地址还是先查大地址
DF=0:先查小地址,增地址
DF=1:先查大地址,减地址
CLD使DF置0
STD使DF置1
REPNE/REPNZ 当不相等/不为零时重复串操作
同样地,REPNE和REPNZ也是完全相等的同义词!两者可以随意相互替换。
REPNE/REPNZ——不相等则重复
ZF=0且CX≠0时重复
ZF=0,表示两数相减结果不为0,两数不相等;CX≠0,表示任务还没结束。
退出条件:
常用于查找指令
比如要在一个很长的字符串里找到空格,就用REPNE或REPNZ,不相等会重复,相等时就停下。
执行的操作是先判断CX是否等于0,ZF是否等于0,CX=0,ZF=1满足其中一个就退出,否则向下执行,CX=CX-1,执行其后的指令。
重复上述操作。
注意CX的递减不影响标志位。
CMPS 串比较指令
用源串中的一个字节(或者字)减去目的串中的一个字节(或者字)
因为做了减法,结果很显然会对标志寄存器产生影响,如果结果为0,ZF标志位会等于1
SCAS 串扫描指令
用AL中的字节(或者字)和附加段中的字符串中的一个字节(或者字)做减法。
要记住8086的内存是以8位的字节为单位来组织的。
例题
DS和ES重叠,注意要从下面开始传送,注意串传送是从数据段传到附加段。
所以它这里说的向前移动一个不是图片上的向前,指的是地址上向前,而且移动的是一个字,也就是两个单元。
如果从P开始传送,因为DS和ES是重叠的,P会覆盖掉R,SI和DI向下再向下后指向的单元的内容还是P,这就不对了。
如果从下面开始,S先放到比S大两个字节的地方(这个地方原来是空的),R再放到比S原来的地方大一个字节的地方,这样是对的。
源串首地址送DI
请注意EXIT后的内容
先DEC DI,再将DI送BX
因为REPNE中,先看CX是否为0,然后CX-1,然后做减法,然后DI+1
这一串都做完之后,判断是否达到退出重复的条件,如果达到了条件,就向下执行,然后才到
JZ EXIT
要明白,SCASB只是一条指令而已!做重复的是REPNE,REPNE是ZF等于0时重复,也就是结果不为零,相减的两个数不相等的时候重复。找到#的时候ZF=0了,REPNE就不重复了,就执行下面的JZ EXIT。
当然不重复还有一种可能是都扫描完了也没找到#,这个时候也结束重复,执行下面的JZ,但是因为此时ZF=0,所以不会跳转。
找到#的话,JZ EXIT会跳转。
但是此时DI指向的是#的后面一个单元,因为SCAS做完减法后紧接着就会让地址DI+1。
因此要让DI-1,这才得到#字符(一个8位的字符)所在的地址。