内联汇编

原创 2002年11月22日 09:00:00
 

几天看了孙原等几位仁兄关于汇编语言的几篇文章,颇感兴趣。于是查了查98版的MSDN中,其中也有几篇关于内联汇编的基础,索引字是asm。讲得不算太难,于是试地将其内容写下来了,特此贴来。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

一、  内联汇编简述

Visual C++ 6.0编译器下,内联汇编可以使用所有的Intel486处理器指令集。而且可以对目标处理器建立起伪指令来实现附加指令功能。内联汇编可以使用MASM编译器所允许的表达式,其中的一些表达式可以通过操作符和操作数的组合,对单精值进行运算.

虽然内联汇编可以访问C/C++中的数据变量以及类对象,但它不可能通过MASM指令和操作符来定义数据及对象。尤其你还不能使用DB, DW, DD, DQ, DTDF等定义指令以及DUPThis操作符。汇编中的结构记录也不是可用的。内联汇编也不支持directives STRUC, RECORD, WIDTH, MASK指令。不过,在内联汇编可以使用到一个_emit宏指令,它类似于MASM中的DB指令,它可以在本区域内定义出一个字节型,虽然它每次只能定义一个字节出来,但还是可以用它来定义出一个字符串,请看示例:

#define randasm __asm _emit 0x4A __asm _emit 0x43 __asm _emit 0x4B

__asm 

      {

               randasm

     }

 

虽然内联汇编不支持MASM中的很多指令,但它支持EVEN ALIGN指令。它们被用于那些需要使用align labels来指定分界线的汇编指令。

内联汇编不可以是一个宏汇编程序,你不可以使用MASM中的宏定义指令以及宏操作符。但内联汇编是可以使用C/C++中的预理指令来定义宏。

在处理段时,你只能使用寄存器,而不是通过名字,因为在内联汇编中这是非法的。而且段必须显式地使用寄存器,: ES:[BX].

在内联汇编使用操作符LENGTH, SIZE, TYPE可以来对变量以及类型进行长度的测量,你可以使用它们来求得C/C++中的变量及类型的长度:

*LENGTH操作符可以返回在一个变量数组中的元素个数,如果返回为1则代表这不是一个变量数组。

*SIZE操作符可以求得一个变量及类型的总长度。这个值也可以由LENGTHTYPE积来求得。

*TYPE操作符可以求得一个变量及类型的长度,与SIZE不同的是,如果变量名是一个数组的话,则返加这个数组中单个元素的长度。

具体情况请看下表:

__asm

C

Size

LENGTH arr

sizeof(arr)/sizeof(arr[0])

8

SIZE arr

sizeof(arr)

16

TYPE arr

sizeof(arr[0])

2

         包含着内联汇编的程序可以使用/Zi选项编译,从而来进行代码级的调试工作。这样,你就可以同时在C/C++程序段与内联汇编段中设置断点,如果你使用/Fas选项允许混合汇编与C/C++源程序调试方式,那么你就可以看到混合着汇编与源程序的代码集合。

Visual C++编译器允许你在内联汇编程序中使用Intel处理器的MMX指令集。不过如果使用MMX指令集编译器会发生警告。更多的信息请查看MSDNWeb站点。

 

二、  关于内联汇编的具体使用说明:

因为内联汇编不需要编译与链接过程,因此它比一个汇编程序更为方便。由于它能够访问C/C++中的变量及函数,所以它能够更好和你C/C++代码融为一体。内联汇编可以在以下方面进行编程:

*用汇编语言编写函数。

*使用汇编语言来产生速度最优化代码段。

*直接使用汇编语言来对硬件进行访问。

*Writing prolog and epilog code for “naked” calls. (这句怎么翻?)

如果你计划在不同机器上运行程序的话,那么你应该分别放置不同机种的专一汇编代码。因为内联汇编有一定的机器专一性,它不完全支持所有MASM中的宏与数据类型。

VC不支持C++中的asm关键字,所以你需要使用__asm(两个下划线)关键字来编写你的内联汇编代码。

你可以使用这个关键字来编写一个内联代码段,如:

__asm

{

   mov al, 2

   mov dx, 0xD007

   out al, dx

}

也可以只编写一行式的内联代码,如:

__asm mov al, 2

__asm mov dx, 0xD007

__asm out al, dx

以上两段代码是同义的。但是第一种写法比较有好处(advantages?),它可以与C源码明显的区别开来,而且避免重复输入不必要__asm关键字。

在内联汇编代码段中以下的C语言元素是可以被应用的:

*符号,包括跳转标签,变量名和函数名。(所使用C/C++符号必须在其使用名域之内。)

*C/C++常量,包括const符号化常量和共用体(enum)中的常量

*宏以及预处理表达式。

*C/C++风格的注释,//,/*,*/

*类型名

*typedef定义的类型名

 

在内联汇编代码中使用C操作符

在内联汇编中不能使用C/C++专有的操作符,诸如:<<,虽然,有一些操作符是MASMC中都在使用的,比如:*操作符。但在内联汇编中被优先解释为汇编操作符。例如,在C中方括号是用来访问数组的元素的。C将它解释为首地址+单个元素长度*元素序号。而在内联汇编中,则将它解释为首地址+方括号中定义的数量。是对一个地址的字节偏移量。这一点是在编程中应该特注意的。所以以下这一段代码是错误的:

int array[10];

__asm mov array[6], 0 ;  期望达到C中的array[6] = 0功能,但这是错误的。

正确的代码如下:

__asm mov array[6 * TYPE int], 0 ; 

array[6] = 0;

 

在内联汇编中使用C/C++符号(如前面如述,符号包括常量名,变量名,函数名以及跳转标签)应注意以下几点:

*所使用C/C++符号必须在其使用名域之内。

*一般的情况下,一句汇编语句只允许出现一个C/C++符号。在LENGTH, TYPE,  SIZE表达式中则可以使用多个C/C++符号。

*就像C语言一样,在内联汇编中调用函数之前,必须显式的声明函数。否则编译器将会报错。

*注意在内联汇编中不能使用那些与MASM中保留字相同的C/C++符号。

*注意C/C++中的类,结构体以及共用体在内联汇编中不直接使用。

下面将举几个关于使用C/C++符号的例子。

如果先前C已经定义了一个变量var,那么则内联汇编可以访问这个变量如下:

__asm mov eax, var      ;将变量var中的值赋给eax寄存器中。

如果有一个结构体first_type和一个实例hal:

struct first_type

{

   char *weasel;

   int same_name;

} hal;

在访问hal对象时,则必须如下:

__asm

{

   mov ebx, OFFSET hal              ;取得hal对象的首地址

   mov ecx, [ebx]hal.same_name ;加上same_name偏移值,则可以访问到成员same_name

   mov esi, [ebx]hal.weasel    ;加上weasel偏移值。

}

 

下面是一个内联汇编如何实现一个函数的例子:

#include <stdio.h>

int power2( int num, int power );

void main( void )

{

   printf( "3 times 2 to the power of 5 is %d/n", /

           power2( 3, 5) );

}

int power2( int num, int power )

{

   __asm

   {

      mov eax, num    ; 取得第一个参数

      mov ecx, power  ; 取得第二个参数

      shl eax, cl     ; EAX = EAX * CL

   }

   //在函数中,返回值是由eax负责往回传递的。(顺便问一句ax与eax有什么不同啊?是不是一样的?)

}

因为内联函数中没有return,所以在上面的例子中,编译器会报出警告。还好,不像Java一样,少一个多一个return都会编译不通过。你可以使用宏#pragma warning来关掉警告器。在pascall式函数中堆栈的复位是由函数负责的,而不是调用者。在上面的例子中,由是在C函数中内部嵌入汇编来完成汇编函数的。在C函数出口处,C编译器会自动添加复栈指令,而不必自己添写。那反而会使系统混乱.

在内联汇编中跳转指令(包括条件跳转),可以跳转到C语言goto能到的所有地方。Goto也可以跳到内联汇编中定义的标签,示例如下:

void func( void )

{

   goto C_Dest;  /* Legal: correct case   */

   goto c_dest;  /* Error: incorrect case在C中大小写区分。*/ 

 

   goto A_Dest;  /* Legal: correct case   */

   goto a_dest;  /* Legal: incorrect case */

 

   __asm

   {

      jmp C_Dest ; Legal: correct case

      jmp c_dest ; Legal: incorrect case

 

      jmp A_Dest ; Legal: correct case

      jmp a_dest ; Legal: incorrect case

 

      a_dest:    ; __asm label

   }

 

   C_Dest:       /* C label */ 

   return;

}

另外,在给标签起名时尽量避免与C内部的或已经使用了的标签名重名,如果那样的将会出现灾难性的程序错误。因此,在起名时最好追查一下是否这个名字已经被使用了。

在引用函数时,应注意参数的从右向左方向地压栈。比如有一个函数是

int CAdd (int a,int b)

则应该如此调用:

__asm

{

       mov eax,2;

       push; 参数b等于2

       mov eax,3;

       push; 参数a等于3

       call CAdd;调用CAdd函数

       mov Result,eax;所有函数的返回值都被存放在eax。于是,Result等于5

}

注意内联汇编无法调用重载函数,因为被重载函数名与原函数名不一样。所以如果你需求调用的话, (我记得vcbase中有关于重载函数的文章),就不要定义重载函数,且C++函数必须使用extern "C"关键字来定义。

因为C中的预处理指令#define是字符代换,所以你可以使用#define来定义一个汇编宏,例如:

#define PORTIO __asm      /

/* Port output */         /

{                         /

   __asm mov al, 2        /

   __asm mov dx, 0xD007   /

   __asm out al, dx       /

}

 

以上,就是内联汇编的基本使用描述。由于,本人的英文并不是太好,所以写出来的文章有些不连续,而且大部分话是我自己说的,或许还会译错的地方,还请大家指教见谅。

以下是我自己写的一段关于类,结构体的示例:

#include <iostream.h>

struct MyData

{

       int nMember1;

       int * lpMember2;

};

 

void main()

{

       MyData sample;

 

       __asm//这是对成员变量赋值

       {

                 mov eax,12;

                 mov sample.nMember1,eax;

       }

       cout <<sample.nMember1<<endl;

 

       __asm//这是对成员指针赋值

       {

                 lea eax,sample.nMember1;

                 mov sample.lpMember2,eax;

       }

       cout <<*sample.lpMember2<<endl;

       

       __asm//这是对指针所指向的变量赋值

       {

                 mov ebx,sample.lpMember2;

                 mov eax,5;

                 mov [ebx],eax;

       }

       cout <<sample.nMember1<<endl;

}

不过,对于成员函数的调用仍没有成功。请各位高手帮忙解决这个问题。谢谢。

 

释雪

2002-11-21

gcc 内联汇编用法介绍

前言大部分内容翻译提取自某国外HOW-TO文档,原地址: http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html AT&T汇...
  • qq_29343201
  • qq_29343201
  • 2016年08月13日 16:52
  • 1488

c++中内联汇编

在x264.h中你可以观察到这样的语句:define DECLARE_ALIGNED( type, var, n ) __declspec(align(n)) type var #       def...
  • edcrfvzl
  • edcrfvzl
  • 2007年05月11日 16:16
  • 1141

内联汇编语法和使用方法

转自:http://blog.csdn.net/slvher/article/details/8864996 //自学专用  在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一...
  • ml_1995
  • ml_1995
  • 2016年04月02日 16:05
  • 2502

内联汇编使用简介

用3个实例实现将变量a和变量b相乘,得到的值存在result中.简单实例asm表示使用内联汇编, volatile表示不对内联汇编进行优化,避免造成误删.#includeint a = 10; int...
  • csujiangyu
  • csujiangyu
  • 2015年06月04日 10:31
  • 1253

用asm内联汇编实现系统调用

原创内容(cxsmarkchan 陈晓爽) 转载请注明出处 《Linux内核分析》MOOC课程学习笔记 为保证系统的稳定运行,CPU运行状态被分为内核态和用户态。操作系统在内核态下运行,因此拥有...
  • cxsmarkchan
  • cxsmarkchan
  • 2016年03月20日 23:11
  • 1029

x64内联汇编解决办法

以前一直在x32上内联汇编用着好好的,五一换了电脑,win10 x64,想在一个点停下,结果在用__asm{}的时候vs2015报错说在本结构上不支持该拓展……一路慢慢Google vs2015...
  • iextract
  • iextract
  • 2017年05月02日 19:33
  • 1278

VC之内联汇编代码...

今晚看教程时,惊喜万分呀,原来在VC中也可以包含汇编代码,形式是如此的简单: //_asm为VC内联汇编代码标志 _asm {     ...//你的汇编代码 } //呵呵...
  • friendan
  • friendan
  • 2012年05月12日 22:24
  • 1149

关于内联汇编的效率

关于内联汇编存在很多误区。 曾经在书上看到过手动写汇编码加入到C++程序里去,然后书上说这样会有效率上的提升,从此就想当然的认为那是对的,然后也就从此相信了只要把我们写的代码改成汇编码内联进去会快很...
  • d603010999
  • d603010999
  • 2013年10月31日 20:40
  • 644

内联汇编--引用源码中定义的数组

内联汇编1.引用源码中定义的数组DWORD intAry[5]={4,3,5,3,4}; DWORD* ptrAry=intAry; void test() { //目的 将intAry[2]...
  • lacoucou
  • lacoucou
  • 2016年12月17日 14:23
  • 690

最牛X的GCC 内联汇编

导读 正如大家知道的,在C语言中插入汇编语言,其是Linux中使用的基本汇编程序语法。本文将讲解 GCC 提供的内联汇编特性的用途和用法。对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86...
  • Jack__CJ
  • Jack__CJ
  • 2016年09月21日 19:10
  • 846
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:内联汇编
举报原因:
原因补充:

(最多只允许输入30个字)