2. 深入了解 prefix
在 GPI(General-Purpose Instruction)指令里,Legacy Prefix 是在整个编码序列的最前部分,起了对内存操作数进行修饰补充作用。
- 1. Legacy prefix
- 1.1 legacy prefix 的作用
- 1.2 legacy prefix 的分类
- 1.3 REX prefix 的作用
- 2. 指令系统的上下文环境
- 3. 缺省和有效(default 与 effective)上下文环境
- 4. 调整操作数的大小(66H prefix - Operand Size Override)
- 5. 编译上下文环境(assembler)
- 5.1 生成多少位的机器指令
- 5.2 在源代码中指定位模式
- 6. processor 当前执行上下文环境
- 7. 调整地址大小(67H prefix - Address Size Override)
- 8. 调整段选择子(Segment override)
- 9. 通过 prefix 增强指令功能(F3 prefix 与 F2 prefix)
- 9.1 rep prefix 或者 repz prefix(F3H prefix)
- 9.1.1 rep 的意义
- 9.1.2 repz/repe 的意义
- 9.2 repne/repnz(F2H prefix)
- 9.1 rep prefix 或者 repz prefix(F3H prefix)
- 10 附加功能(LOCK prefix)
1. Legacy prefix
1.1 legacy prefix 的作用
legacy prefix 主要有以下作用:
- 调整内存操作数的属性
- 增强指令的功能
- 提供额外的作用
1.2 legacy prefix 分类
leagcy prefix 包括下面几类:
- operand size override prefix:66H --- 改变操作数大小
- address size override preifx:67H --- 改变操作数地址模式
- segment override prefix:改变 memory 操作数段选择子,包括:
- 2E --- CS register
- 3E --- DS register
- 26 --- ES register
- 64 --- FS register
- 65 --- GS register
- 36 --- SS register
- rep/repz prefix:F3H --- 串指字重复执行
- repnz prefix:F2H --- 串指字重复执行
- lock prefix: F0H --- LOCK
1.3 REX prefix 的作用
- 访问 64 位的资源:使用 64 位的操作数,改变指令操作数的 default operand size(可以使用 64 位 operands),同样是起 operand size override 的作用
- 访问 x64 体系扩展资源:x64 体系新增的 register,如:r8 - r15,xmm8 - xmm15 等
2. 指令系统的上下文环境
要彻底了解 prefix,必须要结合 3 个很重要的上下文环境:
- 指令本身的 default operand-size 和 default address-size 以及 effective operand size 和 effective address size
- assembler 编译器上文环境
- 当前 processor 的执行上下文环境。
x86/x64 指令编码会根据上面提到的 3 个上下文环境而对操作数的位置、大小以及地址进行调整改变。
这里操作数是内存操作数。出现调整的情形,这是因为:
- effective operands-size 可以为:16 位、32 位以及 64 位
- operands 的地址位置因段选择子的不同而不同(cs、ds、es、ss、fs 以及 gs)。
- effective address-size 可以为:16 位、32 位以及 64 位
也就是说:指令编码因指令操作数的 operand size, address size 以及 segment 的不同而不同
3. 缺省和有效(default 与 effective)上下文环境
在 x86/x64 平台的指令系统里有两个很重要的概念:
缺省(default)概念
包括:缺省操作数大小以及缺省地址大小(default operand-size 与 default address-size)。
- 在 32 位下:default operands-size 和 default address-size 都是 32 位。
- 在 16 位下:default operands-size 和 default address-size 都是 16 位。
在 64 位下,情况有些特殊:
|
指令的 default operand size 是由 CS.D 标志位来决定,这个 default operand size 可由 CS.D 标志位来改变:
- CS.D = 0 时:default operand size 和 default address size 是 16 位
- CS.D = 1 时:default operand size 和 default address size 是 32 位
- CS.L = 1 & CS.D = 0 时,processor 处于 64 位模式,default address size 是 64 位,default operand size 是 32/64 位
缺省的 operand size 和 address size 可由 66H prefix 及 67H prefix 进行 override,经过 override 后的 operand size 和 address size 是指令最终的 operand size 和 address size
有效(effective)概念
包括:有效的操作数大小以及有效地址大小(effective operand-size 与 effective address-size)
- 在 32 位下:effective operand size 与 effective address-size 可以为:16 位和 32 位
- 在 16 位下:effective operand size 与 effective address-size 可以为:16 位和 32 位。
- 在 64 位下:effective operand size 可以为:16 位、32 位和 64 位。
- 在 64 位下:effective address-size 可以为:32 位与 64 位。
模式
|
effective operand size
|
effective address size
|
备注
|
16 位
|
16, 32
|
16, 32
| |
32 位
|
16, 32
|
16, 32
| |
64 位
|
16, 32, 64
|
32, 64
|
在 64 位模式下,情况又有些特殊:在 64 位下支持 16 位,32 位以及 64 位的操作数大小。但是不支持 16 位的地址大小。
关于“default 与 effective”的更详细的论述,请见:default(缺省)与 effective(有效)一文
4. 调整操作数的大小(66H prefix - Operand Size Override)
4.1 Operand Size Override 行为
是指: |
基于这种需求,在指令编码中使用 66H prefix 来实现 operand size override ,例如:
- 在 16 位模式下,指令需要访问 32 位的数据
- 在 32 位模式下,指令需要访问 16 位的数据
bits 16 ; 16 位代码 mov eax, ebx
mov ax, bx |
上面的代码两种情况下都需要使用 66H prefix 进行改写 operand size
4.2 66H prefix(Operand-Size Override prefix)
66H 字节这个 prefix 用来更改 operands size,当然只能在指令所支持的 effective operand-size 范围内进行调整。
66H 在 Opcode 表中就是一个 prefix,processor 解码逻辑识别它不是 Opcode
4.3 Operand Size Override 的规则
怎样进行 Override 以及 Override 什么? 都是有固定的规则的,这和 default operand size 以及 effective operand size 有紧密的关系
模式
|
default operand size
|
effective operand size
|
prefix
|
REX prefix
|
描述
|
16 模式
|
16
|
16
|
---
|
---
| 16 位模式下的 2 种 default operand size 的情形 |
32
|
66H
| ||||
32
|
16
|
66H
| |||
32
|
---
| ||||
32 模式
|
16
|
16
|
---
|
---
| 32 位模式下的 2 种 default operand size 的情形 |
32
|
66H
| ||||
32
|
16
|
66H
| |||
32
|
---
| ||||
64 模式
|
32
|
16
|
66H
|
---
| 64 位模式下的 2 种 default operand size 情形 |
32
|
---
|
---
| |||
64
|
---
|
REX.W = 1
| |||
* 64
|
16
|
66H
|
---
| ||
64
|
---
|
每一种模式下都分为 2 种 default operand size 情形,除了 64 位模式下 default operand size 是 32 时,有 3 种 effective operand size 外,其它都是 2 种 effective operand size
表中:--- 表示无需 prefix,REX.W = 1 表示:调整到 64 位(REX.W = 0 它是使用 default operand size)
* 标注处的 default operand size = 64 只有少数的指令 default operand size 是 64 位,大部分指令的 default 是 32 位的。
- 在 16 位模式下
- 当 default operand size 是 16 位时:需要调整为 32 位,需要加 66H prefix
- 当 default operand size 是 32 位时:需要调整为 16 位,需要加 66H prefix
- 在 32 位模式下
- 当 default operand size 是 16 位时:需要调整为 32 位,需要加 66H prefix
- 当 default operand size 是 32 位时:需要调整为 16 位,需要加 66H prefix
- 在 64 位模式下
- 当 default operand size 是 32 位时:需要调整为 16 位时,需要加 66H prefix。需要调整到 64 位时,需要加 REX prefix
- 当 default operand size 是 64 位时:不能调整到 32 位,调整到 16 位时,需要 66H prefix
在 64 位的 default operand size 下,effective operand size 只有 2 种:
- 16 位的有效操作数
- 64 位的有效操作数
因此:只能使用 66H prefix 调整到 16 位的操作数,不能调整到 32 位操作数
4.4 为什么需要改变操作数大小?
原因前面已经提过,很简单:当 16 位代码下需要访问 32 位数据或者 32 位代码需要访问 16 位数据时。
bits 16 ; 16 位代码 mov bx, cx ; 不需要进行 operand size override |
上面代码所示,在 16 位模式下,上面的不需要 override,而下面一条指令则需要进行 override
4.5 看看几个例子
例 1: 在 16 位的 default operand size 下,指令: mov eax, ebx
由于在 16 位下,如果操作数的大小缺省是 16 位的,这条指令要访问 32 位的寄存器,那么需要使用 66H prefix 进行 operand size override
89 d8 ---> 66 89 d8 (使用 66H prefix 将 16 位 registers 改为 32 位 registers)
例 2: 在 32 位 default operand size 下,指令 mov ax, bx
由于在 32 位下,如果操作数的大小缺省是 32 位的,这条指令要访问 16 位寄存器,同样需要使用 66H prefix 进行调
89 d8 ---> 66 89 d8 (使用 66H prefix 将 32 位 registers 改为 16 位 registers)
表 4.5.1 (在 16 位和 32 位模式下适用)
指令
|
default operand size
|
指令编码
|
mov eax, ebx
|
16
|
66 89 d8
|
32
|
89 d8
| |
mov ax, bx
|
16
|
89 d8
|
32
|
66 89 d8
|
上表是这 2 条指令分别在不同的 default operand size 下的指令编码情况
有些人或许会感到疑惑,为什么例 1 与例 2 编译器生成的结果是一样的。这就是 assembler 在不同的 编译上下文环境 译为相同的指令编码。
例3:在 32 位 default operand size 下,指令:mov ax, [11223344h]
在 Microsoft 的语法里,在内存操作数前一般要加指示字 word ptr ,指明操作数的大小:mov ax, word ptr [11223344h] 实际上,在这条指令里,这个指示字不是必须的,加指示字只是比较直观。但有些情况是必须要加的,如:mov dword ptr [11223344h], 1这条指令有两种译法:
- 66 a1 44 33 22 11
- 66 8b 05 44 33 22 11
使用 66H prefix 进行 operand size override
第 1 条 encode 中,a1 是 opcode,44332211 是 immediate(而不是 dispalcement,因为不是 ModRM 寻址提供的), 66 改变了缺省的操作数大小,将 32 位调整为 16 位。
第 2 条 encode 的 opcode 是 8b, ModRm 是 05, 而 44332211 是 dispalcement 而不是 immediate(需要 ModRM 寻址)。
例4: 在 16 位 default operand size 下,同样一条指令:mov eax, [11223344]
同样一样指令,但目的操作数大小不同,并且 assembler 编译上下文环境不同。
它两种译法为:
- 66 67 a1 44 33 22 11
- 66 67 8b 05 44 33 22 11
与例 3 所不同的是:这条指令增加了 67H prefix 来进行 address size override
表 4.5.2
指令
|
default operand size
|
指令编码
|
mov ax, [11223344h]
|
16
|
66 67 a1 44 33 22 11
|
32
|
66 a1 44 33 22 11
| |
mov eax, [11223344h]
|
16
|
66 67 a1 44 33 22 11
|
32
|
a1 44 33 22 11
|
这里必须有一点要认识到的:当在 16 模式下, 地址 [11223344h] 多数编译器会它截断只取低 16 位地址
那么:mov ax, [11223344h] 会被编译为 66 a1 44 33 (它不需 67H prefix 进行 address size override)
5. 编译上下文环境(assembler)
在一个汇编语言源文件里,需要给编译器一些编译指示:指示目标代码将生成是多少的?目标平台是什么?文件格式是什么?等等...
这个就编译上下文环境。
例如:
操作系统的引导初始化代码部分是 16 位的,现在绝大多数 OS 是 32 位的,因此,在当前系统下写引导代码,则需要求编译器编译为 16 位实模式代码。因此,你不得不写 16 位代码,编译器根据情况将 32 位操作和地址调整至 16 操作数和地址。但在大部分情况下,不需要作调整,直接生成 16 位代码即可。
5.1 生成多少位的机器指令
以 nasm 编译器为例,下面给出一些代码片断:
; ********************************************************* %include "include\arch\x64.inc" ; mouseOS 0.02 project
org BOOT_SEG ; for int 19 ; A20 gate enable
; How do ? cli
db 0x66 ; adjust to 32-bit operand size ; third: enable proected mode
jmp dword code32_sel:code32_entry ; Now: entry 32bit protected mode, but paging is disable code32_entry: mov di, 0 |
上面代码片断显示:在一个源文件中,使用 bits 16 指示 nasm 生成 16 位的代码,并且使用 bits 32 指示 nasm 生成 32 位代码。 还可以使用 bits 64 来生成 64 位代码。
5.2 在源代码中指定位模式
上面代码片断中,使用 bits 伪指令来指示 nasm 生成何种代码。
这样的结果是:
实际上是:要 nasm 假设指令将运行在 16 位,32 位还是 64 位模式? |
如下例子:
bits 16 mov eax, ebx |
代码中使用 bits 16 指示生成 16 位代码,它实际上是要 nasm 假设下面的指令将要运行在 16 位模式下。
00000000 6689D8 mov eax,ebx |
这和表 4.5.1 和 表 4.5.2 所列的编码是一致的。 使用到了 66H prefix 和 67H prefix 进行 override
对于 16 位和 32 位模式来说,这个指令的位模在式相当于指出 default opernad size 是多少。但是对 64 位模式来说,并不代表 nasm 将假设指令的 default operand size 是 64 位。
6. processor 当前执行上下文环境
processor 处于什么模式下,这是系统程序员需要考虑的问题,从而通过代码体现出来,编译器根据代码生成相应的代码。
也就是说: |
机器指令
|
processor 在 16 位模式下
|
processor 在 32 位模式下
|
processor 在 64 位模式下
|
69 8d
|
mov ax, bx
|
mov eax, ebx
|
mov eax, ebx
|
上表显示,同一条机器指令,当 processor 运行在不同的模式下,指令的解码是不同的。但是只不同在于 operand size
7. 调整地址大小(67H prefix - Address Size Override)
当需要改变地址大小的时候,也需要使用 67H prefix 来进行调整。同样是在所支持的 effective address-size 范围内。
7.1 Address Size Override 行为
是指: |
7.2 67H prefix(address size override prefix)
指令中可以 67H 进行 address size override,同样 67H 不是 opcode,processor 的解码逻辑遇到它会把它当作 prefix
7.2 Address size override 规则
怎样进行 Override 以及 Override 什么? 都是有固定的规则的,这和 default address size 以及 effective address size 有紧密的关系
processor 模式
|
default address size
|
effective address size
|
prefix
|
16 位模式
|
16
|
16
|
---
|
32
|
67H
| ||
32
|
16
|
67H
| |
32
|
---
| ||
32 位模式
|
16
|
16
|
---
|
32
|
67H
| ||
32
|
16
|
67H
| |
32
|
---
| ||
64 位模式
|
64
|
32
|
67H
|
64
|
---
|
与 Operand size override 规则一样,在 effective address size 范围里调整为另一个 address size 需要使用 67H prefix
在 64 位模式下 default address size 是 64 位,不能调整到 16 位地址。
7.3 为什么要调整地址大小
以 16 位模式为例,如果需要访问 64K 以上的地址,需要什么 32 位的寻址模式。
那么需要使用 67H prefix 将 16 位寻址模式调整到 32 位寻址模式。
7.4 Address size override 的几个例子
例1:在 16 位下,以序言里的指令为例:mov dword ptr [eax+ecx*8+0x11223344], 0x12345678
由于在 16 位的 default operands size 和 default address size 下,但该指令使用 32 位 operand size 以及 32 位 address size
也就是说既要调整 operand size 也要调整 address size。所以,应加上 66H prefix 调整 operand-size,再加上 67H prefix 调整 address-size。
最终的 encode 为: 66 67 c7 84 c8 44 33 22 11 78 56 34 12
例2:在 32 位模式下,指令:mov eax, dword ptr [11223344]
该指令的编码为: a1 44 33 22 11
对这个编码,我们手工进行调整,加上 67H prefix: 67 a1 44 33 22 11
那么此时,用 67H prefix 调整为 16 位地址,那么在汇编语句将变为: mov eax, dword ptr [3344]
结果是: 加了 67H prefix 之后,它的地址将被截断为 16 位。即地址:0x3344,多出 22 11 两个字节属下条指令边界了。
例3:在 32 位下,指令:mov eax, dword ptr [bx + si + 0x0c]
很显然,这条汇编语句源操作数的地址是 16 位的。
在当前的 32 位编译环境下,应使用 67H prefix 将这个 16 位地址调整 32 位地址。
最终的 encodes 是: 67 8b 40 0c
40H 这个 ModRM 寻址的地址方式是: [bx + si + 0x0c] (16 位),当使用 67H prefix 调整为 32 位地址时是:[eax + 0x0c]
若这条指令的 encode 放在 16 位下执行,汇编形式变为: mov eax, dword ptr [eax + 0x0c](16 位下,67H prefix 调整的结果)
7.5 16 位寻址模式与 32 位寻址模式的区别
上面的 3 个例子显示了 16 位地址和 32 位地址的区别,主要来自 16 位的内存操作数寻址只支持 BX 与 BP 寄存器作为基址寄存器,SI 和 DI 寄存器作为变址寄存器,比 32 位的内存寻址少得多。
表7.5.1 assembler 在 32 位下和 16 位下的区别(assembler 编译上下文环境)
序号
|
指令
|
在 32 位下编译的结果
|
在 16 位下编译的结果
|
1 | mov dword ptr [eax + ecx * 8 + 0x11223344], 0x12345678 | c7 84 c8 44 33 22 11 78 56 34 12 | 66 67 c7 84 c8 44 33 22 11 78 56 34 12 |
2* | mov eax, dword ptr [0x11223344] | a1 44 33 22 11 | 66 a1 44 33 |
3 | mov eax, dword ptr [bx + si + 0x0c] | 67 8b 40 0c | 66 8b 40 0c |
表1中显示的是在不同的编译上下文环境,同一条指令产生的不同编码(例如,在 nasm 编译器使用 bits 16 和 bits 32 指示字)
注意:
在第 2 条时,地址 [0x11223344] 在 16 位代码的编译环境中,不同的编译器会有不同的处理结果:
★ 大多数 assembler(编译器)会将 [0x11223344] 截断为 [0x3344]。
★ 但是,一个功能强大的,全面的 assembler 应该将 [0x11223344] 还原为 [0x11223344],产生的编码应是: 66 67 a1 44 33 22 11
有关 16 位寻址模式和 32 位寻址模式,详细请参看 AMD 与 Intel 手册
7.6 67H prefix 的深层含义
67H prefix(address-size override)指示 processor 对内存操作数寻址模式上的转变:
★ 当运行在 16 位代码时,将要使用 32 位地址(default address-size 是 16 位)。因此,内存寻址要用 32 位寻址模式。
★ 当运行在 32 位代码时,将要使用 16 位地址(default address-size 是 32 位)。因此,内存寻址要用 16 位寻址模式。
表2:processor 在 16 位下与 32 位下解析区别 (processor 执行上下文环境)
序号
|
指令编码 encods(机器指令)
|
processor 运行在 32 位下时解析为
|
processor 运行在 16 位下时解析为
|
1* | c7 84 c8 44 33 22 11 78 56 34 12 | mov dword ptr [eax + ecx * 8 + 0x11223344], 0x12345678 | mov word ptr [si + 0x44c8], 0x2233 |
2* | 66 67 c7 84 c8 44 33 22 11 78 56 34 12 | mov word ptr [si + 0x44c8], 0x2233 | mov dword ptr [eax + ecx * 8 + 0x11223344], 0x12345678 |
3 | 67 8b 40 0c | mov eax, dword ptr [bx + si + 0x0c] | mov ax, word ptr [eax + 0x0c] |
4 | 8b 40 0c | mov eax, dword ptr [eax + 0x0c] | mov ax, word ptr [bx + si + 0x0c] |
表2中显示在不同的执行上下文环境,同一条机器指令编码产生的不同行为。
注意:
★ 第1条中,机器码:c7 84 c8 44 33 22 11 78 56 34 12 当 processor 在 16 位下,只解析前面的 c7 84 c8 44 33 22
剩下的 11 78 56 34 12 将被视为下一条指令。
★ 第2条中,机器码:66 67 c7 84 c8 44 33 22 11 78 56 34 12 当 processor 在 32 位下,只解析前面的 66 67 c7 84 c8 44 33 22
剩下的 11 78 56 34 12 将被视为下一条指令。
7.7 67H prefix 总结
在汇编代码层面上,assembler 根据当前编译环境,将汇编语句生成相应的 encodes 决定是否使用 67H prefix
在机器代码层面上,processor 根据当前执行环境来决定如何解析机器指令
8. 调整段选择子(Segment override)
对于大多数内存操数据来说,缺省以 DS 为段基址的。常见的是:DS 段基址,SS 段基址。与 operand-size / address-size 一样,当需要调整缺省的 segment 时,需要使用相应的 segment override prefix
8.1 缺省 segment register
与 default opernads-size、default address-size 一样,segment registers 同样有 default segment register
8.1.1 基址寄存器(base register)
default segment register 与内存操作数中的 base register 相关。
例如:mov eax, dword ptr [eax] 指令中的 [eax] 操作数 eax 就是 base register
缺省寄存器规则:
- 基址寄存器为 ebp 的,缺省的段寄存器(default segment register)是:SS
- 基址寄存器为 esp 的,缺省的段寄存器(default segment register)是:SS
- 在串处理指令中(如:movsb,scanb,cmpb 等),源串缺省段寄存器为 DS, 目标串缺省段寄存器为 ES
- 无基址寄存器的操作数中,缺省段寄存寄均为 DS (如:mov eax, [0x11223344],源操作数就是无基址寄存器)
- 所有代码段的缺省段寄存器都是 CS
- 除上面几种情况下,所有内存操作数的缺省段寄存器都是 DS
8.2 Segment override 例子
foo: |
[ebp-0xc]:这个内存操作数缺省是基于 SS 段的
[eax]:这个内存操作数缺省是基于 DS 段的。
因此,对于上面的片段,[ebp - 0x0c] 是在 SS segment,即 stack 内。
[eax] 这个内存地址按照程序的意图是访问 stack 内的数据,所以,这里我将它调整为 stack segment
lea eax, [ebp - 0x0c]mov eax, dword ptr ss:[eax] (调整为访问 stack)
为什么一般程序都不会这么写呢? 那是因为,现代的操作系统都是采用平坦的内存模式,即:CS=SS=DS=ES,所以对 [eax] 这个操作数不需调整其结果是正确的。
8.2.1 [eax] 内存操作数进行调整为:mov eax, dword ptr ss:[eax]
产生的编码是: 36 8b 00
其中,36 也就是 SS segment-override prefix,将 DS 段调整为 SS 段。
8.3 Segment override prefix
- CS segment:2E
- DS segment:3E
- ES segment:26
- FS segment:64
- GS segment:65
- SS segment:36
当需要进行调整段寄存器时,就使用以上的 segment-override prefix。
8.4 在 64 位模式下的 segment override
在 64 位模式下,segmentation 管理已经被最大程度上的弱化,因此,当代码中进行 segment override 时,已经显得不重要了,可以有两个分段管理被保留下来:
- fs 段
- gs 段
它们被保留下来,令到程序中可以有额外的段式管理手段,因此,在代码中依然可以使用 fs 与 gs 进行 segment override 操作
9. 通过 prefix 增强指令功能(F3 prefix 与 F2 prefix)
这些 prefix 对 Opcode 进行补充,增强指令的功能,优化指令执行。起重复执行指令的功能
- F3: rep/repz prefix
- F2: repnz prefix
看下面这段 c 代码:
char *move_char(char *d, char *s, unsigned count) while (count--) return p; |
这是典型的、经典的字符串复制c代码,对应以下类似的汇编代码:
最初版本:
move_char: move_loop: |
上面的代码性能低下,是很死板的实现,优化的空间巨大。
x86 为串提供了相应的串操作指令(ins,outs,lods,stos,scas,cmps),对这些串指令提供 prefix 来增强优化这些指令。
9.1 rep prefix 或者 repz prefix(F3H prefix)
可以看到 F3H prefix 有两重意义:rep 和 repz,但是使用的范围是不同的:
prefix 含义
|
使用范围
|
结束条件
|
rep
|
movs,lods,stos,ins,outs
|
ecx = 0
|
repz/repe
|
scas,cmps
|
ecx = 0 或 ZF = 0(比较结果不为零)
|
它们的使用范围和结束条件都不同。
9.1.1 rep 的意义
rep 重复执行指令一定的次数,这个次数在 ecx 中提供。
用伪代码描述为:
if (ecx != 0) { |
首先判断 ecx 是否为 0,不为 0 则执行指令。
使用 rep 优化版本:
mov_char: |
使用串指令 movsb 配合 rep prefix 进行复制,rep movsb 的编码为:
- f3 a4
9.1.2 repz/repe 的意义
F3 prefix 另一层意义是 repe/repz,用于改变标志位的串操作:scas, cmps 指令
意思是:当比较结果相等(ZF=1)并且循环次数(ecx)不为 0 时进行重复操作。(重复的条件是:ZF = 1 & ecx <> 0)
即:它的结束条件是:ecx = 0 或者 ZF = 0, 意思是:不相等时或者次数到了,就不重复执行指令
它的 c 伪码形式如下:
if (ecx != 0 && ZF = 1) { |
常见运用一些跳过字符的逻辑上,如下面 C 代码,用于截除串前面空格:
char *trim(char *s) return s; |
rep 与 repe/repz 是相同的 prefix,作用于不同的串指操作意义也不同:
- f3 a4 --- 这时它是 rep
- f3 ae --- 这时它是 repz/repe
当作用于不修改标志位的串指令时,它的意义是 rep,作用于修改标志位的串指令时,它的意义是 repz/repe
9.2 repne/repnz(F2H prefix)
F2H prefix 是表达 repne/repnz 意思是: 结果不相等(不为零)时循环。(重复条件是 ZF == 0 并且 ecx <> 0)
结束条件是:ecx = 0 或者 ZF = 1 即:结果相等时退出循环。
同样也是用于改变标志位的串操作 scas 和 cmps
它的 c 伪码形式如下:
if (ecx != 0 && ZF = 0) { |
常见一些查找字符的逻辑上,如下面 C 代码:
char *get_char(char *s, char c) ret |
10 附加功能(LOCK prefix)
对于写内存的一些指令增加了锁地址总线的功能,这些写内存的指令如常见的 sub,add 等指令,通过 Lock prefix 来实现这功能,使用 Lock prefix 将会使 procesor 产生 LOCK# 信号锁地址总线
注意:
Lock prefix 仅使用在一些对内存进行 read-modify-write 操作的指令上,如:add, sub, and 等指令。 否则,将会产生 #UD (无效操作码) 异常
如下指令所示:
lock add dword ptr [eax], 1 |
它的指令编码是:
- f0 81 00 01 00 00 00
F0: Lock prefix 锁地址总线。
版权所有 mik 2008 - 2011