您好,欢迎来到划驼旅游。
搜索
您的当前位置:首页C语言与ARM汇编混编

C语言与ARM汇编混编

来源:划驼旅游
__asm _______ volatile__嵌汇编用法简述在阅读C/C++原码时经常会遇到内联

汇编的情况,下面简要介绍下__asm ____________ volatile__嵌汇编用法。

带有C/C++表达式的内联汇编格式为:

__asm _______ volatile__(\"I nstruction List\" : Output : In put : Clobber/Modify;其中每

项的概念及功能用法描述如下:

1、 __asm__

__asm 是 GCC关键字asm的宏定义: #defi ne __asm__ asm

_asm』asm用来声明一个内联汇编表达式,所以任何一个内联汇编表达式 都是以它开头

的,是必不可少的。

2、 Instruction List

In structio n List是汇编指令序列。它可以是空的,比如:

__asm__

__volatile__(\"\";或__asm__ (\"\";都是完全合法的内联汇编表达式,只不过这两条语 句没有什么意

义。但并非所有In structi on List为空的内联汇编表达式都是没有意义 的,比如:__asm__

(\"\":::\"memory\";

就非常有意义,它向GCC声明:内存作了改动” GCC在编译的时候,会将 此因素考虑进去。当在\"Instruction List\"中有多条指令的时候,可以在一对引号中 列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引 号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则 必须用分号(;)或换行符(\\n)将它们分开.综上述:(1)每条指令都必须被双 引号括起来(2两条指令必须用换行或分号分开。 在ARM系统结构上关闭 中断的操作

例如:

int disable」nterrupts (void

{

un sig ned long old,temp;

__asm _______ volatile__(\"mrs %0, cpsr\\ n\"

\"orr %1, %0, #0x80\\n\"

\"msr cpsr_c, %1\"

:\"=r\" (old, \"=r\" (temp

:\"memory\";

return (old & 0x80 == 0;

}

3. volatile

volatile__是GCC关键字volatile的宏定义

#defi ne volatile volatile

GCC声明不允许对该 GCC将会根据自己的判

__volatile__或volatile是可选的。如果用了它,则是向 内联汇编优化,否则当 使用了优化

选项(-0进行编译时, 断决定是否将这个内联汇编表达式中的指令优化掉。

4、Output

Output用来指定当前内联汇编语句的输出

例如:从arm协处理器p15中读出C1值

static un sig ned long read_p15_c1 (void

{

un sig ned long value; __asm _______ volatile__(

\"mrc p15, 0, %0, c1, c0, 0 @ read control reg\\n\" : \"=r\" (value @编译器选择一个 R*寄存器

:\"memory\";

#ifdef MMU_DEBUG

printf (\"p15/c1 is = %08lx\\n\

#en dif

return value;

5、In put

In put域的内容用来指定当前内联汇编语句的输入 Output 和 In put 中,格式为

形如 “constraint ” (var的列e表 (逗号分隔

例如:向arm协处理器p15中写入C1值

static void write_p15_c1 (unsigned long value

#ifdef MMU_DEBUG

printf (\"write %08lx to p15/c1\\n\

#en dif

__asm _______ volatile__(

\"mcr p15, 0, %0, c1, c0, 0 @ write it back\\n\"

:\"r\" (value @编译器选择一个 R*寄存器

:\"memory\";

read_p15_c1 (;

}

6.

、Clobber/Modify

有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修 改,希望GCC在编译时能够将这一点考虑进去。那么你就可以在

域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在

List\",但却不是由Input/Output操作表达式所指定的,也不是在一些

Clobber/Modify \"I nstructio n Input/Output

操作表达式使用\"r\"约束时由GCC为其选择的,同时此寄存器被\"Instruction List\"中 的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。

例如:

__asm__ (\"mov R0, #0x34\" : : : \"R0\";

寄存器RO出现在\"Instruction List中\",并且被mov指令修改,但却未被任何 Input/Output操作表达式指定,所以你需要在 Clobber/Modify域指定\"R0\",以让 GCC知道这一点。因为你在Input/Output操作表达式所指定的寄存器,或当你为 一些Input/Output操作表达式使用\"r\"约束,让GCC为你选择一个寄存器时,GCC 对这些寄存器是非常清楚的一一

它知道这些寄存器是被修改的,你根本不需要在

Clobber/Modify域再声明它

们。但除此之外,GCC对剩下的寄存器中哪些会被当前的内联汇编修改一无所 知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在 Clobber/Modify中声明它们,让GCC针对这些寄存器做相应的处理。否则有可能 会造成寄存器的不一致,从而造成程序执行错误。

如果一个内联汇编语句的

Clobber/Modify域存在\"memory\",那么GCC会保证在此内联汇编之前,如果某个 内存的内容

被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处 的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。 因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。

这只是使用\"memory\"时,GCC会保证做到的一点,但这并不是全部。因为使 用\"memory\"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这 一点。

例如:

int main (i nt __argc, char* __argv[]

{

int* __p = (int*__argc;

(*__p = 9999;

__asm__(\"\":::\"memory\";

if((*__p == 9999

return 5;

return (*__p;

}

本例中,如果没有那条内联汇编语句,那个 废话。GCC在优化时会意识到这一点,而直接只生成

if语句的判断条件就完全是一句

return 5的汇编代码,而不会

再生成if语句的相关代码,而不会生成return (*__p的相关代码。但你加上了这条 内联汇编语句,它除了声明内存变化之外,什么都没有做。但

GCC此时就不能简

单的认为它不需要判断都知道(*_p —定与9999相等,它只有老老实实生成这条 if语句的汇编代码,一起相关的两个

return语句相关代码。

另外在linux内核中内存屏障也是基于它实现的 include/asm/system.h中

# defi ne barrier( _asm__volatile_(\"\": : :\"memory\"

主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不 一定是最终执行的顺序,这个是处理器有关的

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo6.com 版权所有 湘ICP备2023023988号-11

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务