Chinaunix首页 | 论坛 | 博客
  • 博客访问: 329935
  • 博文数量: 67
  • 博客积分: 668
  • 博客等级: 上士
  • 技术积分: 1591
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-16 22:08
文章分类

全部博文(67)

文章存档

2015年(1)

2014年(13)

2013年(28)

2012年(23)

2011年(2)

分类: C/C++

2013-01-25 17:44:11

1,gnu内联汇编的语法:
asm或__asm__("汇编代码");
指令必须包含在引号里
如果包含的指令超过一行 必须使用新行分隔符分隔



使用c全局变量, 不能在内联汇编中使用局部变量, 注意在汇编语言代码中值被用做内存位置, 而不是立即数值

如果不希望优化内联汇编, 则可以volatile修饰符如:__asm__ volatile("code");

2,GCC内联汇编的扩展语法
__asm__("assembly code":output locations:input operands:changed registers);
第一部分是汇编代码
第二部分是输出位置, 包含内联汇编代码的输出值的寄存器和内存位置列表
第三部分是输入操作数,包含内联汇编代码输入值的寄存器和内存位置的列表
第四部分是改动的寄存器, 内联汇编改变的任何其他寄存器的列表
这几个部分可以不全有, 但是没有的还必须使用:分隔

1, 指定输入值和输出值, 输入值和输出值的列表格式为:
"constraint"(variable), 其中variable是程序中声明的c变量, 在扩展asm格式中, 局部和全局变量都可以使用,
使用constrant(约束)定义把变量存放到哪(输入)或从哪里传送变量(输出)
约束使用单一的字符, 如下:
约束                                   描述
a                         使用%eax, %ax, %al寄存器
b                         使用%ebx, %bx, %bl寄存器
c                         使用%ecx, %cx, %cl寄存器
d                         使用%edx, %dx, %dl寄存器
S                         使用%esi, %si寄存器
D                         使用%edi, %di寄存器
r                         使用任何可用的通用寄存器
q                         使用%eax, %ebx, %ecx,%edx之一
A                         对于64位值使用%eax, %edx寄存器
f                         使用浮点寄存器
t                         使用第一个(顶部)的浮点寄存器
u                         使用第二个浮点寄存器
m                         使用变量的内存位置
o                         使用偏移内存位置
V                         只使用直接内存位置
i                         使用立即整数值
n                         使用值已知的立即整数值
g                         使用任何可用的寄存器和内存位置


除了这些约束之外, 输出值还包含一个约束修饰符:
输出修饰符                             描述
+                         可以读取和写入操作数
=                         只能写入操作数
%                         如果有必要操作数可以和下一个操作数切换
&                         在内联函数完成之前, 可以删除和重新使用操作数

如:
__asm__("assembly code": "=a"(result):"d"(data1),"c"(data2));
把c变量data1存放在edx寄存器中, 把c变量data2存放到ecx寄存器中, 内联汇编的结果
将存放在eax寄存器中, 然后传送给变量result


在扩展的asm语句块中如果要使用寄存器必须使用两个百分号符号


不一定总要在内联汇编代码中指定输出值, 一些汇编指令假定输入值包含输出值, 如movs指令


其他扩展内联汇编知识:
1, 使用占位符
     输入值存放在内联汇编段中声明的特定寄存器中, 并且在汇编指令中专门使用这些寄存器.
     虽然这种方式能够很好的处理只有几个输入值的情况, 但对于需要很多输入值的情况, 这
     中方式显的有点繁琐. 为了帮助解决这个问题, 扩展asm格式提供了占位符, 可以在内联
     汇编代码中使用它引用输入和输出值.
   
     占位符是前面加上百分号的数字, 按照内联汇编中列出的每个输入和输出值在列表中的位置,
     每个值被赋予从0开始的地方. 然后就可以在汇编代码中引用占位符来表示值。
   
   
     如果内联汇编代码中的输入和输出值共享程序中相同的c变量, 则可以指定使用占位符作为
     约束值, 如:
     __asm__("imull %1, %0"
             : "=r"(data2)
             : "r"(data1), "0"(data2));
     如输入输出值中共享相同的变量data2, 而在输入变量中则可以使用标记0作为输入参数的约束

2, 替换占位符
     如果处理很多输入和输出值, 数字型的占位符很快就会变的很混乱, 为了使条理清晰 ,GNU汇编
     器(从版本3.1开始)允许声明替换的名称作为占位符.替换的名称在声明输入值和输出值的段中
     定义, 格式如下:
     %[name]"constraint"(variable)
     定义的值name成为内联汇编代码中变量的新的占位符号标识, 如下面的例子:
     __asm__("imull %[value1], %[value2]"
             : [value2] "=r"(data2)
             : [value1] "r"(data1), "0"(data2));

3, 改动寄存器列表
     编译器假设输入值和输出值使用的寄存器会被改动, 并且相应的作出处理。程序员不需要在改动的
     寄存器列表中包含这些值, 如果这样做了, 就会产生错误消息. 注意改动的寄存器列表中的寄存器
     使用完整的寄存器名称, 而不像输入和输出寄存器定义的那样仅仅是单一字母。 在寄存器名称前面
     使用百分号符号是可选的。
   
     改动寄存器列表的正确使用方法是, 如果内联汇编代码使用了没有被初始化地声明为输入或者输出
     值的其他任何寄存器 , 则要通知编译器。编译器必须知道这些寄存器, 以避免使用他们。如:
     int main(void) {
        int data1 = 10;
        int result = 20;
      
        __asm__("movl %1, %%eax\n\t"
                "addl %%eax, %0"
                : "=r"(result)
                : "r"(data1), "0"(result)
                : "%eax");
        printf("The result is %d\n", result);
        return 0;
     }
   
4, 使用内存位置
     虽然在内联汇编代码中使用寄存器比较快, 但是也可以直接使用c变量的内存位置。 约束m用于引用输入值
     和输出值中的内存位置。 记住, 对于要求使用寄存器的汇编指令, 仍然必须使用寄存器, 所以不得不定义
     保存数据的中间寄存器。如:
     int main(void) {
        int dividentd = 20;
        int divisor = 5;
        int result;
      
        __asm__("divb %2\n\t"
                "movl %%eax, %0"
                : "=m"(result)
                : "a"(dividend), "m"(divisor));
        printf("The result is %d\n", result);
        return 0;
     }
   
5, 处理跳转
     内联汇编语言代码也可以包含定义其中位置的标签。 可以实现一般的汇编条件分支和无条件分支, 如:
     int main(void) {
        int a = 10;
        int b = 20;
        int result;
      
        __asm__("cmp %1, %2\n\t"
                "jge greater\n\t"
                "movl %1, %0\n\t"
                "jmp end\n"
                "greater:\n\t"
                "movl %2, %0\n"
                "end:"
                :"=r"(result)
                :"r"(a), "r"(b));
        printf("The larger value is %d\n", result);
        return 0;
     }
   
     在内联汇编代码中使用标签时有两个限制。 第一个限制是只能跳转到相同的asm段内的标签,
     不能从-个asm段跳转到另一个asm段中的标签。第二个限制更加复杂一点。 以上程序使用
     标签greater和end。 但是, 这样有个潜在的问题, 查看汇编后的代码清单, 可以发现内联
     汇编标签也被编码到了最终汇编后的代码中。 这意味着如果在c代码中还有另一个asm段, 就
     不能再次使用相同的标签, 否则会因为标签重复使用而导致错误消息。还有如果试图整合使用
     c关键字(比如函数名称或者全局变量)的标签也会导致错误。

阅读(10781) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~