在 Visual C++ 中使用内联汇编- -
    使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。
    内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。
一、 优点
    使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。
    内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。
它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。
    内联汇编的用途包括:   
使用汇编语言编写特定的函数;
编写对速度要求非常较高的代码;
在设备驱动程序中直接访问硬件;
编写 naked 函数的初始化和结束代码。
二、 关键字
    使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。我们来看一些例子:   
简单的 __asm 块:
        __asm
        {
            MOV AL, 2
            MOV DX, 0xD007
            OUT AL, DX
        }
在每条汇编指令之前加 __asm 关键字:
        __asm MOV AL, 2
        __asm MOV DX, 0xD007
        __asm OUT AL, DX
因为 __asm 关键字是语句分隔符,所以可以把多条汇编指令放在同一行:
        __asm MOV AL, 2  __asm MOV DX, 0xD007  __asm OUT AL, DX
汇编语言结束指令    显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。
    不像在 C/C++ 中的"{ }",__asm 块的"{ }"不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。
    为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。
三、 汇编语言
1. 指令集
    内联汇编支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它处理器的指令可以通过 _EMIT 伪指令来创建(_EMIT 伪指令说明见下文)。
2. MASM 表达式
    在内联汇编代码中,可以使用所有的 MASM 表达式(MASM 表达式是指用来计算一个数值或一个地址的操作符和操作数的组合)。
3. 数据指示符和操作符
    虽然 __asm 块中允许使用 C/C++ 的数据类型和对象,但它不能使用 MASM 指示符和操作符来定义数据对象。这里特别指出,__asm 块中不允许 MASM 中的定义指示符(DB、DW、DD、DQ、DT 和 DF),也不允许使用 DUP 和 THIS 操作符。MASM 中的结构和记录也不再有效,内联汇编不接受 STRUC、RECORD、WIDTH 或者 MASK。
4. EVEN 和 ALIGN 指示符
    尽管内联汇编不支持大多数 MASM 指示符,但它支持 EVEN 和 ALIGN。当需要的时候,这些指示符在汇编代码里面加入 NOP 指令(空操作)使标号对齐到特定边界。这样可以使某些处理器取指令时具有更高的效率。
5. MASM 宏指示符
    内联汇编不是宏汇编,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(<>、!、&、% 和 .TYPE)。
6. 段
    必须使用寄存器而不是名称来指明段(段名称"_TEXT"是无效的)。并且,段跨越必须显式地说明,如 ES:[EBX]。
7. 类型和变量大小
    在内联汇编中,可以用 LENGTH、SIZE 和 TYPE 来获取 C/C++ 变量和类型的大大小。
    * LENGTH 操作符用来取得 C/C++ 中数组的元素个数(如果不是一个数组,则结果为 1)。
    * SIZE 操作符可以获取 C/C++ 变量的大小(一个变量的大小是 LENGTH 和 TYPE 的乘积)。
    * TYPE 操作符可以返回 C/C++ 类型和变量的大小(如果变量是一个数组,它得到的是
数组中单个元素的大小)。
    例如,程序中定义了一个 8 维的整数型变量:
        int iArray[8];
    下面是 C 和汇编表达式中得到的 iArray 及其元素的相关值:
        __asm                  C                                  Size
        LENGTH iArray          sizeof(iArray)/sizeof(iArray[0])    8
        SIZE iArray            sizeof(iArray)                      32
        TYPE iArray            sizeof(iArray[0])                  4
8. 注释
    内联汇编中可以使用汇编语言的注释,即";"。例如:
        __asm MOV EAX, OFFSET pbBuff  ; Load address of pbBuff
    因为 C/C++ 宏将会展开到一个逻辑行中,为了避免在宏中使用汇编语言注释带来的混乱,内联汇编也允许使用 C/C++ 风格的注释。
9. _EMIT 伪指
    _EMIT 伪指令相当于 MASM 中的 DB,但是 _EMIT 一次只能在当前代码段(.text 段)中定义一个字节。例如:
        __asm
        {
            JMP _CodeLabel
            _EMIT 0x00        ; 定义混合在代码段的数据
            _EMIT 0x01
        _CodeLabel:          ; 这里是代码
            _EMIT 0x90        ; NOP指令
        }
10. 寄存器使用
    一般来说,不能假定某个寄存器在 __asm 块开始的时候有已知的值。寄存器的值将不能保证会从 __asm 块保留到另外一个 __asm 块中。
    如果一个函数声明为 __fastcall 调用方式,则其参数将通过寄存器而不是堆栈来传递。这将会使 __asm 块产生问题,因为函数无法被告知哪个参数在哪个寄存器中。如果函数接收了 EAX 中的参数并立即储存一个值到 EAX 中的话,原来的参数将丢失掉。另外,在所有声明为 __fastcall 的函数中,ECX 寄存器是必须一直保留的。为了避免以上的冲突,包含 __asm 块的函数不要声明为 __fastcall 调用方式。   
提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。但如果你用到了 DS、SS、SP、BP 和标志寄存器,那就应该用 PUSH 保存这些寄存器。
提示:如果程序中改变了用于 STD 和 CLD 的方向标志,必须将其恢复到原来的值。
四、 使用 C/C++ 元素
1. 可用的 C/C++ 元素
    C/C++ 与汇编语言可以混合使用,在内联汇编中可以使用 C/C++ 变量以及很多其它的 C/C++ 元素,包括:
符号,包括标号、变量和函数名;
常量,包括符号常量和枚举型成员;
宏定义和预处理指示符;
注释,包括"/**/"和"//";
类型名,包括所有 MASM 中合法的类型;
typedef 名称,通常使用 PTR 和 TYPE 操作符,或者使用指定的的结构或枚举成员。
    在内联汇编中,可以使用 C/C++ 或汇编语言的基数计数法。例如,0x100 和 100H 是相
等的。
2. 操作符使用
    内联汇编中不能使用诸如"<<"一类的 C/C++ 操作符。但是,C/C++ 和 MASM 共有的操作符(比如"*"和"[]"操作符),都被认为是汇编语言的操作符,是可以使用的。举个例子:
        int iArray[10];
        __asm MOV iArray[6], BX            ; Store BX at iArray + 6 (Not scaled)
        iArray[6] = 0;                      // Store 0 at iArray+12 (Scaled)   
提示:在内联汇编中,可以使用 TYPE 操作符使其与 C/C++ 一致。比如,下面两条语句是一样的:
        __asm MOV iArray[6 * TYPE int], 0  ; Store 0 at iArray + 12

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。