<script>function StorePage(){d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();}</script>
作者:刘洪涛,华清远见嵌入式培学院金牌讲师,ARM ATC授权培训讲师。
__asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。
带有C/C++表达式的内联汇编格式为:
__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
其中每项的概念及功能用法描述如下:
1、 __asm__
__asm__是GCC 关键字asm 的宏定义:
#define __asm__ asm
__asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。
2、Instruction List
Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory");
就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。 当在"Instruction List"中有多条指令的时候,可以在一对引号中列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(\n)将它们分开. 综上述:(1)每条指令都必须被双引号括起来 (2)两条指令必须用换行或分号分开。
例如: 在ARM系统结构上关闭中断的操作
int disable_interrupts (void)
{
unsigned 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 的宏定义
#define __volatile__ volatile
__volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。
4、 Output
Output 用来指定当前内联汇编语句的输出
例如:从arm协处理器p15中读出C1值
static unsigned long read_p15_c1 (void)
{
unsigned 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", value);
#endif
return value;
}
5、 Input
Input 域的内容用来指定当前内联汇编语句的输入Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)
例如:向arm协处理器p15中写入C1值
static void write_p15_c1 (unsigned long value)
{
#ifdef MMU_DEBUG
printf ("write %08lx to p15/c1\n", value);
#endif
__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在编译时能够将这一点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。
例如:
__asm__ ("mov R0, #0x34" : : : "R0");
寄存器R0出现在"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(int __argc, char* __argv[])
{
int* __p = (int*)__argc;
(*__p) = 9999;
__asm__("":::"memory");
if((*__p) == 9999)
return 5;
return (*__p);
}
本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。
另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中
# define barrier() _asm__volatile_("": : :"memory")
主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是最终执行的顺序,这个是处理器有关的。
分享到:
相关推荐
__asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。...
C语言内嵌汇编(_asm)是一种将汇编代码直接插入到C或C++程序中的技术,这对于在特定场合优化性能或者访问硬件寄存器等低级操作时非常有用。这种混合编程方式使得程序员可以在高级语言的便利性和汇编语言的精确控制...
### 内嵌汇编用法及分析 #### 引言 在现代软件开发过程中,内嵌汇编(Inline Assembly)是一种特殊的技术手段,允许开发者直接在高级语言(如C/C++)代码中嵌入汇编指令。这种方法可以提高程序性能、优化特定功能...
2. **指令系统**:深入学习不同体系结构(如x86、ARM等)的指令集,包括数据处理、控制流、输入/输出、内存访问等各种指令的使用方法。 3. **寄存器**:理解处理器中的寄存器工作原理,如通用寄存器、程序计数器、...
标题中的"PIC-TEMP.zip_PIC_PIC 单片机 汇编_PIC.asm_pic_temp"揭示了这个压缩包内容的核心,它涉及到的是使用PIC单片机进行温度测量的一个项目。PIC单片机是由Microchip Technology公司生产的一系列广泛应用的微...
标题中的“LCD.rar_12864_12864 51汇编语言_12864 asm_12864 lcd”指的是一个关于12864点阵图形液晶显示屏(LCD)的编程资源,其中包含了使用51系列单片机的汇编语言代码。12864 LCD是指具有128行和64列像素的显示...
"asm.rar_双机通信汇编_在线 编写asm_用汇编语言编写串行口双机通信"这一资源是关于使用汇编语言实现两台计算机之间通过串行接口进行通信的实例。下面我们将深入探讨这个主题,了解相关的知识点。 首先,我们要明白...
本压缩包“MD5_Asm.zip”包含了MD5算法的汇编语言实现,汇编语言是计算机程序设计的一种底层语言,直接对应机器指令,具有高度可移植性和执行效率。通过学习这个汇编实现,我们可以深入理解MD5算法的内部工作原理,...
标题中的“BIN2HEX__asm_my.rar_bin2asm_bin2hex_汇编 进制转换_进制转换”表明这个压缩包包含了与汇编语言编程相关的资源,特别是关于二进制(bin)到十六进制(hex)的转换。在计算机科学中,进制转换是基本的操作...
在这个STM32-asm.rar压缩包中,包含了一份名为"STM32 asm.pdf"的文档,很可能是关于STM32汇编语言编程的详细指南。 1. **STM32汇编基础**: - **指令集**:STM32汇编语言基于ARM Cortex-M内核的指令集,包括数据...
标题 "iop_sw_mpu_defs_asm.rar_H.R.H." 暗示了这是一个与IOP(Input/Output Processor)软件内存保护单元(MPU - Memory Protection Unit)相关的资源,其中包含了汇编语言(asm)定义的头文件。描述中提到的 ...
在本文中,我们将深入探讨如何在Windows环境下使用汇编语言编写一个简单的时钟程序,以实现显示当前时间的功能。这个程序是由罗云彬所创建,他在编程领域有着丰富的经验,并且他的作品通常受到广大编程爱好者的关注...
"PIC ASM"和"pic.asm"标签提示我们,内容可能包括使用汇编语言编程的实例,可能是源代码文件。"pic_汇编"进一步确认了这个主题,意味着我们将探讨的是针对PIC单片机的低级编程。"pic12asm"可能是指一套教程或者一...
ASM_firasm_滤波_滤波 汇编_滤波器"所指的是一份关于使用51系列单片机实现滤波器的汇编语言代码集合,其中包含了FIR(Finite Impulse Response,有限脉冲响应)滤波器的相关实现。51单片机是一种广泛应用的微控制器...
ASM,全称是Adaptive Structural Modeling,是一种在图像处理和计算机视觉领域中广泛使用的形状建模方法。在MATLAB环境中,ASM工具箱提供了一套完整的框架来实现这种算法,使得用户能够方便地进行形状分析、识别和...
《mg.rar_asm 游戏_asm迷宫_汇编 迷宫_汇编 迷宫游戏_汇编小游戏》是一款基于汇编语言开发的简单迷宫游戏。汇编语言是计算机科学的基础,它是一种低级编程语言,直接对应于机器指令,具有高度的效率和直接控制硬件的...
标题中的"pic_sub1.rar_PIC_PIC ASM_PIC.asm_pic 汇编_pic汇编"揭示了这个压缩包的内容主要是关于PIC微控制器的汇编语言编程资源。PIC是Microchip Technology公司生产的一系列广泛应用的微控制器(MCU),而ASM则是...
`__asm__ __volatile__`确保编译器不会优化掉这段汇编代码。汇编指令部分在双引号中,`"\n"`用于换行,`: "=r" (count)`和`": "0" (count)"`定义了输入/输出的操作数。 在某些情况下,我们可能还需要在汇编代码中...
- **内嵌汇编关键字**:`__asm__` 或 `__asm__` 的别名 `asm`。 - **volatile 关键字**:`__volatile__` 或其别名 `volatile`,用于指示编译器不要对内联汇编指令进行优化。 - **汇编指令**:放在圆括号内的汇编指令...