ASM16-25 --串操作指令
ASM16
串操作指令
- 串操作指令是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与操作数之间的关系
REP重复前缀指令
1 | REP ;每执行一次串指令,CX减一 |
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:
local @n1:word local @n2:word local @n3[3]:byte1
2
3
4
5
6
7
- 定义一个过程(函数)`MY_ADD`,使用 `stdcall` 调用约定。
- `uses bx cx`:过程调用期间保存 `BX` 和 `CX` 寄存器的内容,并在过程结束时恢复它们。
- `p1:word, p2:word`:定义两个参数 `p1` 和 `p2`,都是 `word` 类型。mov bx,p1 mov cx,p2 mov @n1,2 mov @n2,31
2
3
4
5
- `local @n1:word`:声明一个局部变量 `@n1`,类型为 `word`。
- `local @n2:word`:声明一个局部变量 `@n2`,类型为 `word`。
- `local @n3[3]:byte`:声明一个局部变量 `@n3`,类型为 `byte` 数组,长度为3字节。invoke MY_ADD,1,21
2
3
4
5
6
7
8
- `mov bx,p1`:将参数 `p1` 的值加载到 `BX` 寄存器中。
- `mov cx,p2`:将参数 `p2` 的值加载到 `CX` 寄存器中。
- `mov @n1,2`:将值 `2` 存入局部变量 `@n1` 中。
- `mov @n2,3`:将值 `3` 存入局部变量 `@n2` 中。
#### 调用宏指令.if ax!=1 mov ax,bx .else mov bx,bx .endif1
2
3
4
5
- `invoke MY_ADD,1,2`: 调用 `MY_ADD` 宏,传入参数 `1` 和 `2`。
#### 条件分支和循环.while ax<10 mov cx,cx .endw1
2
3
4
5
6
7
- `.if ax!=1`: 如果 `AX` 不等于 `1`,执行接下来的代码。
- `mov ax,bx`: 如果条件成立,执行 `mov ax,bx`,将 `BX` 的值赋给 `AX`。
- `.else`: 否则执行 `.else` 分支的代码。
- `mov bx,bx`: 这行代码实际上什么也不做(无操作)。
- `.endif`: 结束条件分支。test ax,1 ;ax&1==>flag cmp ax,0 ;4 test ax,ax ;3 if(n==0)1
2
3
4
5
6
7
- `.while ax<10`: 当 `AX` 小于 `10` 时,进入循环体。
- `mov cx,cx`: 这行代码实际上什么也不做(无操作)。
- `.endw`: 结束 `while` 循环。
#### 测试与比较指令mov cx,10 LOOP1: mov ax,ax LOOP LOOP11
2
3
4
5
6
7
- `test ax,1`: 对 `AX` 进行按位与操作,检查最低位是否为1,设置标志位(影响后续条件跳转)。
- `cmp ax,0`: 比较 `AX` 和 `0` 的值。
- `test ax,ax`: 对 `AX` 自身进行按位与操作,常用于检查 `AX` 是否为0。
#### 循环指令mov ax,10 cld mov si,offset MY_MSG1 ;ds:[si] mov di,offset MY_MSG2 ;es:[di] mov cx,ax shr cx,1 ;正数 rep movsw mov cx,ax and cx,1 ;正数 取最低位 rep movsb1
2
3
4
5
6
7
- `mov cx,10`: 将循环计数器 `CX` 设为 `10`。
- `LOOP1:`: 定义一个标签 `LOOP1`,作为循环起始点。
- `LOOP LOOP1`: 将 `CX` 减1,如果 `CX` 不为0,则跳转到 `LOOP1` 标签,继续循环。
#### 字符串操作指令cld mov di,offset MY_MSG2 mov al,01 mov cx,10 rep stosb1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- `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 操作cld mov si,offset MY_MSG1 mov di,offset MY_MSG3 mov cx,4 repz cmpsb1
2
3
4
5
6
7
8
9
- `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 操作cld mov al,0 mov di,offset MY_MSG3 mov cx,20 repnz scasb1
2
3
4
5
6
7
8
9
- `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 操作
- `mov al,0`: 将 `AL` 设置为 `0`。
- `mov di,offset MY_MSG3`: 将 `MY_MSG3` 的偏移地址加载到 `DI` 中。
- `mov cx,20`: 将 `CX` 设置为 `20`,指定最大搜索范围。
- `repnz scasb`: 重复执行 `SCASB` 指令,查找 `DI` 所指向的字符串中第一个等于 `AL`(即0,表示字符串结束)的字节。
伪指令被翻译成汇编代码
![image-20240809214335457](../image/image-20240809214335457.png)
复制10个字节后
![image-20240809214838740](../image/image-20240809214838740.png)
将MY_MSG2中前10个字节置为01
![image-20240809214928737](../image/image-20240809214928737.png)
Hell和Hall在第2位不同,故循环在第2次结束后退出
![image-20240809215633222](../image/image-20240809215633222.png)
20次循环在第6次循环开始前退出,说明第5次循环检测到0,说明长度为4
![image-20240809215320391](../image/image-20240809215320391.png)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 翻斗花园!