X86指令内幕 ——深入了解Prefix

1. Legacy prefix

1.1 legacy prefix 的作用

legacy prefix 主要有以下作用:

  • 调整内存操作数的属性
  • 增强指令的功能
  • 提供额外的作用

 

1.2 legacy prefix 分类

(1) operand size override prefix:66H --- 改变操作数大小

(2) address size override preifx:67H --- 改变操作数地址模式

(3) segment override prefix:改变 memory 操作数段选择子,包括:

  • 2E --- CS register
  • 3E --- DS register
  • 26 --- ES register
  • 64 --- FS register
  • 65 --- GS register
  • 36 --- SS register

(4) rep/repz prefix:F3H --- 串指字重复执行

(5) repnz prefix:F2H --- 串指字重复执行

(6) lock prefix: F0H --- LOCK

 

1.3 REX prefix 的作用

★ 改变指令操作数的 default operand size(可以使用 64 位 operands),同样是起 operand size override 的作用

★ 访问 x64 体系所有 16 个 registers:rax ~ r15,xmm0 ~ xmm15

 

 

2. 指令系统的上下文环境

要彻底了解 prefix,必须要结合 3 个很重要的上下文环境:

  1. 指令本身的 default operand-sizedefault address-size 以及 effective operand sizeeffective address size
  2. assembler 编译器上文环境
  3. 当前 processor 的执行上下文环境。

x86/x64 指令编码会根据上面提到的 3 个上下文环境而对操作数的位置、大小以及地址进行调整改变。

这里操作数是内存操作数。出现调整的情形,这是因为:

  • effective operands-size 可以为:16 位、32 位以及 64
  • operands 的地址位置因段选择子的不同而不同(csdsesssfs 以及 gs)。
  • effective address-size 可以为:16 位、32 位以及 64

也就是说:指令编码因指令操作数的 operand size, address size 以及 segment 的不同而不同

 

3. 缺省和有效(default 与 effective)上下文环境

在 x86/x64 平台的指令系统里有两个很重要的概念:

★ 缺省(default)概念

包括:缺省操作数大小以及缺省地址大小(default operand-sizedefault address-size)。

  • 在 32 位下:default operands-size 和 default address-size 都是 32 位。
  • 在 16 位下:default operands-size 和 default address-size 都是 16 位。
在 64 位下,情况有些特殊
  • default address-size 是 64 位的
  • default operands-size 在大多情况下是 32 位的,而一些情况下是 64 位的

在 64 位下 default operands-size 是 32 位,是基于 x86 平滑扩展为 x64 的设计理念。

 

★ 有效(effective)概念

包括:有效的操作数大小以及有效地址大小(effective operand-sizeeffective 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 行为

是指
    在可接受的 effective operand size(有效操作数大小)范围内改变其 operands size(操作数的大小),使它的 operand size 不再是 default operands size

基于这种需求,在指令编码中使用 66H prefix 来实现 operand size override


4.2 66H prefix(Operand-Size Override prefix)

66H 字节这个 prefix 用来更改 operands size,当然只能在指令所支持的 effective operand-size 范围内进行调整。

66H 在 Opcode 表中就是一个 prefix,它不是 Opcode

 

4.3 Operand Size Override 的规则

怎样进行 Override 以及 Override 什么? 都是有固定的规则的,这和 default operand size 以及 effective operand size 有紧密的关系

表 4.3.1

模式
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 位的。

  1. 在 16 位模式下

    • 当 default operand size 是 16 位时:需要调整为 32 位,需要加 66H prefix
    • 当 default operand size 是 32 位时:需要调整为 16 位,需要加 66H prefix
  2. 在 32 位模式下

    • 当 default operand size 是 16 位时:需要调整为 32 位,需要加 66H prefix
    • 当 default operand size 是 32 位时:需要调整为 16 位,需要加 66H prefix
  3. 在 64 位模式下

    • 当 default operand size 是 32 位时:需要调整为 16 位时,需要加 66H prefix。需要调整到 64 位时,需要加 REX prefix
    • 当 default operand size 是 64 位时: 不能调整到 32 位,调整到 16 位时,需要 66H prefix

在 64 位的 default operand size 下,effective 只有 2 种:16 位和 64 位。因此:只能使用 66H prefix 调整到 16 位,不能调整到 32 位

 

4.4 为什么需要改变操作数大小?

原因很简单:16 位代码下需要访问 32 位数据或者 32 位代码需要访问 16 位数据。

 

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 编译器为例,下面给出一些代码片断:

; *********************************************************
; * unreal_mode.asm for test unreal mode on x86           *
; *                                                       *
; * Copyright (c) 2009-2010                               *
; * All rights reserved.                                  *
; * mik(deng zhi)                                         *
; * visit web site : www.mouseos.com                      *
; * bug send email : mik@mouseos.com                      *
; *                                                       *
; *                                                       *
; * version 0.01 by mik                                   * 
; *********************************************************

%include "include/arch/x64.inc"         ; mouseOS 0.02 project


BOOT_SEG equ 0x7c00 

 
 bits 16

 org BOOT_SEG                   ; for int 19
 
start:
        cli

; A20 gate enable 
        FAST_A20_ENABLE
 
        sti
 
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, BOOT_SEG


; real mode  --->  protected mode
 
next: 
; next setup is enter proected mode
; the proected mode is temporary

; How do ?
; frist: disable all IRQs and NMI

        cli
        NMI_DISABLE               ; NMI disable


; second: load temp GDT into GDTR

        db 0x66                         ; adjust to 32-bit operand size  
 lgdt [GDT_LIMIT]          ; load temp GDT into gdtr
 
 

; third: enable proected mode
 
 mov eax, cr0
 bts eax, 0                     ; CR0.PE = 1
 bts eax, 1                     ; CR0.MP = 1
 mov cr0, eax                   ; enable protected mode


; fourth: far jmp proected mode code

 jmp dword code32_sel:code32_entry

 
 bits 32

; Now: entry 32bit protected mode, but paging is disable
;  So: memory address is physical address

code32_entry:
 mov ax, data16_sel
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov esp, 0x7ff0

 mov di, 0
 mov si, protected_msg
 call printmsg

上面代码片断显示:在一个源文件中,使用 bits 16 指示 nasm 生成 16 位的代码,并且使用 bits 32 指示 nasm 生成 32 位代码。 还可以使用 bits 64 来生成 64 位代码。

 

5.2 在源代码中指定位模式

上面代码片断中,使用 bits 伪指令来指示 nasm 生成何种代码。

这样的结果是:

实际上是:要 nasm 假设指令将运行在 16 位,32 位还是 64 位模式?

如下例子:

bits 16

    mov eax, ebx
    mov eax, [dword 0x11223344]

代码中使用 bits 16 指示生成 16 位代码,它实际上是要 nasm 假设下面的指令将要运行在 16 位模式下。

00000000  6689D8            mov eax,ebx
00000003  6766A144332211    mov eax,[dword 0x11223344]

这和表 4.5.1 和 表 4.5.2 所列的编码是一致的。 使用到了 66H prefix67H prefix 进行 override

对于 16 位和 32 位模式来说,这个指令的位模在式相当于指出 default opernad size 是多少。但是对 64 位模式来说,并不代表 nasm 将假设指令的 default operand size 是 64 位。

 

 

6. processor 当前执行上下文环境

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 行为

是指
    在可接受的 effective address size(有效地址大小)范围内改变其 address size(地址大小),使它的 address size 不再是 default address size

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 有紧密的关系

 

表 7.2.1

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 sizedefault 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 prefix67 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 位下编译的结果
1mov 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
3mov 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
367 8b 40 0c mov eax, dword ptr [bx + si + 0x0c] mov ax, word ptr [eax + 0x0c]
48b 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-sizedefault 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:
    push ebp
    mov ebp, esp
    lea eax, [ebp - 0x0c]
   
    mov eax, dword ptr [eax]
    … 
    mov esp,ebp
    pop ebp
    ret

[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。

 

9. 通过 prefix 增强指令功能(F3 prefix 与 F2 prefix)

这些 prefix 对 Opcode 进行补充,增强指令的功能,优化指令执行。起重复执行指令的功能

  • F3rep/repz prefix
  • F2repnz prefix

 

看下面这段 c 代码:

char *move_char(char *d, char *s, unsigned count)
{
    char *p = d;

    while (count--)
        *d++ = *s++;

    return p;
}

这是典型的、经典的字符串复制c代码,对应以下类似的汇编代码:

最初版本:

move_char:
  push ebp
  mov ebp, esp
  sub esp, 0x0c
  mov eax, [ebp+8]
  mov edi, eax
  mov esi, [ebp+0x0c]
  mov ecx, dword ptr [ebp+0x10]

move_loop:
  mov bl, byte ptr [esi]
  mov byte ptr [edi], bl
  inc esi
  inc edi
  dec ecx
  jnz move_loop
    
  mov esp, ebp
  pop ebp
  ret

 

----------------------------------------------------------------
上面的代码性能低下,是很死板的实现,优化的空间巨大。

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) {
    repeat do
    ecx = ecx - 1
}

首先判断 ecx 是否为 0,不为 0 则执行指令。

使用 rep 优化版本:

mov_char:
        ... ...
        mov edi, [ebp + 0x08]
        mov esi, [ebp + 0x0c]
        mov ecx, [ebp + 0x10]
        rep movsb
        ... ...
        ret

使用串指令 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) {
    repeat do
    ecx = ecx - 1
}

常见运用一些跳过字符的逻辑上,如下面 C 代码,用于截除串前面空格:

char *trim(char *s)
{
    while (*s && *s == ' ')
        s++;

    return s;
}

reprepe/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) {
    repeat do
    ecx = ecx - 1
}

常见一些查找字符的逻辑上,如下面 C 代码:

char *get_char(char *s, char c)
{
    while (*s && *s != c)
        s++;

    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 锁地址总线。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值