ASM16 25 串操作类指令
串操作类指令
- 串操作指令是8086指令系统中比较独特的一类指令,采用比较特殊的数据串寻址方式,在操作主存连续区域的数据时,特别好用、因而常用
- 重要掌握:
MOVS
STOS
LODS
CMPS
SCAS
REP
- 一般了解:
REPZ
/REPE
REPNZ
/REPNE
- 重要掌握:
串数据类型
- 串操作指令的操作数是主存中连续存放的数据串(String)——即在连续的主存区域中,字节或字的序列
- 串操作指令的操作对象是以字(W)为单位的字串,或是以字节(B)为单位的字节串
串存储STOS(store string)
把AL或AX数据传送至目的地址
串读取LODS(load string)
把指定主存单元的数据传送给AL或AX
串比较CMPS(compare string)
将主存中的源操作数减去至目的操作数,以便设置标志,进而比较两操作数之间的关系
串扫描SCAS(scan string)
将AL/AX减去至目的操作数,以便设置标志,进而比较AL/AX与操作数之间的关系
重复前缀指令(repeat)
- 串操作指令执行一次,仅对数据串中的一个字节或字量进行操作。但是串操作指令前,都可以加一个重复前缀,实现串操作的重复执行。重复次数隐含在CX寄存器中
- 重复前缀分2类,3条指令:
- 配合不影响标志的
MOVS
、STOS
(和LODS
)指令的REP前缀 - 配合影响标志的
CMPS
和SCAS
指令的REPZ
和REPNZ
前缀
- 配合不影响标志的
REP重复前缀指令
1 | REP ;每执行一次串指令,CX减一 |
- REP前缀可以理解为:当数据串没有结束(CX不等于0),则继续传送
REPZ重复前缀指令
1 | REPZ ;每执行一次串指令,CX减1 |
REPZ
/REPE
前缀可以理解为:当数据串没有结束(CX不等于0),并且串相等(ZF=1),则继续比较
代码分析
1 | MyStack segment stack |
MY_ADD
1 | MY_ADD proc stdcall uses bx cx, p1:word, p2:word |
MY_ADD proc stdcall uses bx cx, p1:word, p2:word
:- 定义一个过程(函数)
MY_ADD
,使用stdcall
调用约定。 uses bx cx
:过程调用期间保存BX
和CX
寄存器的内容,并在过程结束时恢复它们。p1:word, p2:word
:定义两个参数p1
和p2
,都是word
类型。
- 定义一个过程(函数)
1 | local @n1:word |
local @n1:word
:声明一个局部变量@n1
,类型为word
。local @n2:word
:声明一个局部变量@n2
,类型为word
。local @n3[3]:byte
:声明一个局部变量@n3
,类型为byte
数组,长度为3字节。
1 | mov bx,p1 |
mov bx,p1
:将参数p1
的值加载到BX
寄存器中。mov cx,p2
:将参数p2
的值加载到CX
寄存器中。mov @n1,2
:将值2
存入局部变量@n1
中。mov @n2,3
:将值3
存入局部变量@n2
中。
1 | ret |
ret
:返回调用程序,结束过程。MY_ADD endp
:结束MY_ADD
过程的定义。
调用宏指令
1 | invoke MY_ADD,1,2 |
invoke MY_ADD,1,2
: 调用MY_ADD
宏,传入参数1
和2
。
条件分支和循环
1 | .if ax!=1 |
.if ax!=1
: 如果AX
不等于1
,执行接下来的代码。mov ax,bx
: 如果条件成立,执行mov ax,bx
,将BX
的值赋给AX
。.else
: 否则执行.else
分支的代码。mov bx,bx
: 这行代码实际上什么也不做(无操作)。.endif
: 结束条件分支。
1 | .while ax<10 |
.while ax<10
: 当AX
小于10
时,进入循环体。mov cx,cx
: 这行代码实际上什么也不做(无操作)。.endw
: 结束while
循环。
测试与比较指令
1 | test ax,1 ;ax&1==>flag |
test ax,1
: 对AX
进行按位与操作,检查最低位是否为1,设置标志位(影响后续条件跳转)。cmp ax,0
: 比较AX
和0
的值。test ax,ax
: 对AX
自身进行按位与操作,常用于检查AX
是否为0。
循环指令
1 | mov cx,10 |
mov cx,10
: 将循环计数器CX
设为10
。LOOP1:
: 定义一个标签LOOP1
,作为循环起始点。LOOP LOOP1
: 将CX
减1,如果CX
不为0,则跳转到LOOP1
标签,继续循环。
字符串操作指令
1 | mov ax,10 |
mov ax,10
: 将AX
设置为10
。cld
: 清除方向标志,使后续字符串操作从低地址向高地址方向进行。在 x86 架构中,方向标志位(DF)决定了处理器在处理字符串操作时的方向:
- DF = 0:从低地址到高地址(递增),也就是内存地址递增的方向。这是字符串操作的默认方向。
- DF = 1:从高地址到低地址(递减),内存地址会递减。
某些情况下,之前的代码可能会设置 DF 为 1(使用
STD
指令)。如果在此情况下直接使用字符串操作指令,数据将按从高到低的顺序处理。而对于大多数情况下,我们希望数据从低到高顺序操作,因此需要在使用字符串操作指令之前确保 DF 被清除(设置为 0)。mov si,offset MY_MSG1
: 将MY_MSG1
的偏移地址加载到SI
中,指向数据段中的字符串。mov di,offset MY_MSG2
: 将MY_MSG2
的偏移地址加载到DI
中,指向附加段中的字符串。mov cx,ax
: 将AX
的值加载到CX
中,作为循环计数器。shr cx,1
: 将CX
右移一位,除以2,准备进行字的复制。(除2取商)rep movsw
: 重复执行MOVSW
指令,将SI
所指向的数据段中的字(2字节)复制到DI
所指向的附加段。mov cx,ax
: 再次将AX
的值加载到CX
中。and cx,1
: 对CX
进行按位与操作,保留最低位。(除2取余)rep movsb
: 重复执行MOVSB
指令,将剩余的字节复制到附加段。
memset 操作
1 | cld |
cld
: 清除方向标志。mov di,offset MY_MSG2
: 将MY_MSG2
的偏移地址加载到DI
中。mov al,01
: 将01
加载到AL
中。mov cx,10
: 将CX
设置为10
。rep stosb
: 重复执行STOSB
指令,将AL
中的值(01
)存储到DI
所指向的附加段内的10个字节中。
memcmp 操作
1 | cld |
cld
: 清除方向标志。mov si,offset MY_MSG1
: 将MY_MSG1
的偏移地址加载到SI
中。mov di,offset MY_MSG3
: 将MY_MSG3
的偏移地址加载到DI
中。mov cx,4
: 将CX
设置为4
,指定要比较的字节数。repz cmpsb
: 重复执行CMPSB
指令,比较SI
和DI
所指向的字节,直到找到不相等的字节或CX
为0。
strlen 操作
1 | cld |
cld
: 清除方向标志。mov al,0
: 将AL
设置为0
。mov di,offset MY_MSG3
: 将MY_MSG3
的偏移地址加载到DI
中。mov cx,20
: 将CX
设置为20
,指定最大搜索范围。repnz scasb
: 重复执行SCASB
指令,查找DI
所指向的字符串中第一个等于AL
(即0,表示字符串结束)的字节。
运行结果
伪指令被翻译成汇编代码
复制10个字节后的结果
将MY_MSG2中前10个字节置为01
Hell和Hall在第2位不同,故循环在第2次结束后退出
20次循环在第6次循环开始前退出,说明第5次循环检测到0,说明长度为4