ARM GCC 内嵌汇编手册.doc

上传人:hw****26 文档编号:3219657 上传时间:2019-05-26 格式:DOC 页数:13 大小:154KB
下载 相关 举报
ARM GCC 内嵌汇编手册.doc_第1页
第1页 / 共13页
ARM GCC 内嵌汇编手册.doc_第2页
第2页 / 共13页
ARM GCC 内嵌汇编手册.doc_第3页
第3页 / 共13页
ARM GCC 内嵌汇编手册.doc_第4页
第4页 / 共13页
ARM GCC 内嵌汇编手册.doc_第5页
第5页 / 共13页
点击查看更多>>
资源描述

1、转自:http:/ GCC 内嵌(inline)汇编手册关于这篇文档这篇文章是本人为方便各位业界同仁而翻译,方便大家开发底层代码使用,转载请注明出处,谢谢。要是你 E 文功底好,本人还是建议阅读 E 文版的。http:/www.ethernut.de/en/documents/arm-inline-asm.html对于基于 ARM 的 RISC 处理器,GNU C 编译器提供了在 C 代码中内嵌汇编的功能。这种非常酷的特性提供了 C 代码没有的功能,比如手动优化软件关键部分的代码、使用相关的处理器指令。这里设想了读者是熟练编写 ARM 汇编程序读者,因为该片文档不是 ARM 汇编手册。同样也不

2、是 C 语言手册。这篇文档假设使用的是 GCC 4 的版本,但是对于早期的版本也有效。GCC asm 声明让我们以一个简单的例子开始。就像 C 中的声明一样,下面的声明代码可能出现在你的代码中。/* NOP 例子 */asm(“mov r0,r0“);该语句的作用是将 r0移动到 r0中。换句话讲他并不干任何事。典型的就是 NOP指令,作用就是短时的延时。请接着阅读和学习这篇文档,因为该声明并不像你想象的和其他的 C 语句一样。内嵌汇编使用汇编指令就像在纯汇编程序中使用的方法一样。可以在一个 asm声明中写多个汇编指令。但是为了增加程序的可读性,最好将每一个汇编指令单独放一行。asm(“mov

3、 r0, r0nt“mov r0, r0nt“mov r0, r0nt“mov r0, r0“);换行符和制表符的使用可以使得指令列表看起来变得美观。你第一次看起来可能有点怪异,但是当 C 编译器编译 C 语句的是候,它就是按照上面(换行和制表)生成汇编的。到目前为止,汇编指令和你写的纯汇编程序中的代码没什么区别。但是对比其它的 C 声明,asm 的常量和寄存器的处理是不一样的。通用的内嵌汇编模版是这样的。asm(code : output operand list : input operand list : clobber list);汇编和 C 语句这间的联系是通过上面 asm 声明中可

4、选的 output operand list 和input operand list。Clobber list 后面再讲。下面是将 C 语言的一个整型变量传递给汇编,逻辑左移一位后在传递给 C 语言的另外一个整型变量。/* Rotating bits example */asm(“mov %result, %value, ror #1“ : result “=r“ (y) : value “r“ (x);每一个 asm 语句被冒号(:)分成了四个部分。 汇编指令放在第一部分中的“”中间。“mov %result, %value, ror #1“ 接下来是冒号后的可选择的 output oper

5、and list,每一个条目是由一对(方括号)和被他包括的符号名组成,它后面跟着限制性字符串,再后面是圆括号和它括着的 C 变量。这个例子中只有一个条目。result “=r“ (y) 接着冒号后面是输入操作符列表,它的语法和输入操作列表一样value “r“ (x) 破坏符列表,在本例中没有使用就像上面的 NOP 例子,asm 声明的4个部分中,只要最尾部没有使用的部分都可以省略。但是有有一点要注意的是,上面的4个部分中只要后面的还要使用,前面的部分没有使用也不能省略,必须空但是保留冒号。下面的一个例子就是设置 ARM Soc 的 CPSR 寄存器,它有 input 但是没有 output

6、operand。 asm(“msr cpsr,%ps“ : : ps“r“(status)即使汇编代码没有使用,代码部分也要保留空字符串。下面的例子使用了一个特别的破坏符,目的就是告诉编译器内存被修改过了。这里的破坏符在下面的优化部分在讲解。 asm(“:“memory“);为了增加代码的可读性,你可以使用换行,空格,还有 C 风格的注释。asm(“mov %result, %value, ror #1“: result“=r“ (y) /* Rotation result. */: value“r“ (x) /* Rotated value. */: /* No clobbers */);在

7、代码部分%后面跟着的是后面两个部分方括号中的符号,它指的是相同符号操作列表中的一个条目。%result表示第二部分的 C 变量 y,%value 表示三部分的 C 变量 x;符号操作符的名字使用了独立的命名空间。这就意味着它使用的是其他的符号表。简单一点就是说你不必关心使用的符号名在 C 代码中已经使用了。在早期的 C 代码中,循环移位的例子必须要这么写:asm(“mov %0, %1, ror #1“ : “=r“ (result) : “r“ (value)在汇编代码中操作数的引用使用的是%后面跟一个数字,%1代表第一个操作数,%2代码第二个操作数,往后的类推。这个方法目前最新的编译器还是

8、支持的。但是它不便于维护代码。试想一下,你写了大量的汇编指令的代码,要是你想插入一个操作数,那么你就不得不从新修改操作数编号。优化 C 代码有两种情况决定了你必须使用汇编。1st,C 限制了你更加贴近底层操作硬件,比如,C 中没有直接修改程序状态寄存器(PSR)的声明。2nd 就是要写出更加优化的代码。毫无疑问 GNU C 代码优化器做的很好,但是他的结果和我们手工写的汇编代码相差很远。这一部分有一点很重要,也是被别人忽视最多的就是:我们在 C 代码中通过内嵌汇编指令添加的汇编代码,也是要被 C 编译器的优化器处理的。让我们下面做个试验来看看吧。下面是代码实例。bigtreejust:/emb

9、edded/basic-C$ arm-linux-gcc -c test.cbigtreejust:/embedded/basic-C$ arm-linux-objdump -D test.o编译器选择 r3作为循环移位使用。它也完全可以选择为每一个 C 变量分配寄存器。Load 或者 store 一个值并不显式的进行。下面是其它编译器的编译结果。E420A0E1 mov r2, r4, ror #1 y, x编译器为每一个操作数选择一个相应的寄存器,将操作过的值 cache 到 r4中,然后传递该值到 r2中。这个过程你能理解不?有的时候这个过程变得更加糟糕。有时候编译器甚至完全抛弃你嵌入的

10、汇编代码。C 编译器的这种行为,取决于代码优化器的策略和嵌入汇编所处的上下文。如果在内嵌汇编语句中不使用任何输出部分,那么 C 代码优化器很有可能将该内嵌语句完全删除。比如 NOP 例子,我们可以使用它作为延时操作,但是对于编译器认为这影响了程序的执行速速,认为它是没有任何意义的。上面的解决方法还是有的。那就是使用 volatile 关键字。它的作用就是禁止优化器优化。将 NOP 例子修改过后如下:/* NOP example, revised */asm volatile(“mov r0, r0“);下面还有更多的烦恼等着我们。一个设计精细的优化器可能重新排列代码。看下面的代码:i+;if

11、(j = 1)x += 3;i+;优化器肯定是要从新组织代码的,两个 i+并没有对 if 的条件产生影响。更进一步的来讲,i 的值增加2,仅仅使用一条 ARM 汇编指令。因而代码要重新组织如下:if (j = 1)x += 3;i += 2;这样节省了一条 ARM 指令。结果是:这些操作并没有得到许可。这些将对你的代码产生很到的影响,这将在下面介绍。下面的代码是 c 乘 b,其中 c 和 b 中的一个或者两个可能会被中断处理程序修改。进入该代码前先禁止中断,执行完该代码后再开启中断。asm volatile(“mrs r12, cpsrnt“orr r12, r12, #0xC0nt“msr

12、cpsr_c, r12nt“ : “r12“, “cc“);c *= b; /* This may fail. */asm volatile(“mrs r12, cpsrn“bic r12, r12, #0xC0n“msr cpsr_c, r12“ : “r12“, “cc“);但是不幸的是针对上面的代码,优化器决定先执行乘法然后执行两个内嵌汇编,或相反。这样将会使得我们的代码变得毫无意义。我们可以使用 clobber list 帮忙。上面例子中的 clobber list 如下:“r12“, “cc“上面的 clobber list 将会将向编译器传达如下信息,修改了 r12和程序状态寄存器

13、的标志位。Btw,直接指明使用的寄存器,将有可能阻止了最好的优化结果。通常你只要传递一个变量,然后让编译器自己选择适合的寄存器。另外 寄存器名 ,cc(condition registor 状态寄存器标志位) ,memory 都是在 clobber list 上有效的关键字。它用来向编译器指明,内嵌汇编指令改变了内存中的值。这将强迫编译器在执行汇编代码前存储所有缓存的值,然后在执行完汇编代码后重新加载该值。这将保留程序的执行顺序,因为在使用了带有 memory clobber 的asm 声明后,所有变量的内容都是不可预测的。asm volatile(“mrs r12, cpsrnt“orr r

14、12, r12, #0xC0nt“msr cpsr_c, r12nt“ : : “r12“, “cc“, “memory“);c *= b; /* This is safe. */asm volatile(“mrs r12, cpsrn“bic r12, r12, #0xC0n“msr cpsr_c, r12“ : “r12“, “cc“, “memory“);使所有的缓存的值都无效,只是局部最优(suboptimal) 。你可以有选择性的添加 dummy operand 来人工添加依赖。asm volatile(“mrs r12, cpsrnt“orr r12, r12, #0xC0nt“m

15、sr cpsr_c, r12nt“ : “=X“ (b) : “r12“, “cc“);c *= b; /* This is safe. */asm volatile(“mrs r12上面的第一个 asm 试图修改变量先 b,第二个 asm 试图修改 c。这将保留三个语句的执行顺序,而不要使缓存的变量无效。理解优化器对内嵌汇编的影响很重要。如果你读到这里还是云里雾里,最好是在看下个主题之前再把这段文章读几遍_。Input and output operands 前面我们学到,每一个 input 和 output operand,由被方括号中的符号名,限制字符串,圆括号中的 C 表达式构成。这些

16、限制性字符串有哪些,为什么我们需要他们?你应该知道每一条汇编指令只接受特定类型的操作数。例如:跳转指令期望的跳转目标地址。不是所有的内存地址都是有效的。因为最后的 opcode 只接受 24位偏移。但矛盾的是跳转指令和数据交换指令都希望寄存器中存储的是32位的目标地址。在所有的例子中,C 传给 operand 的可能是函数指针。所以面对传给内嵌汇编的常量、指针、变量,编译器必须要知道怎样组织到汇编代码中。对于 ARM 核的处理器,GCC 4 提供了一下的限制。ConstraintUsage in ARM state Usage in Thumb statef Floating point re

17、gisters f0 . f7 Not availableh Not available Registers r8.r15G Immediate floating point constant Not availableH Same a G, but negated Not availableIImmediate value in data processing instructionse.g. ORR R0, R0, #operandConstant in the range 0 . 255e.g. SWI operandJIndexing constants -4095 . 4095e.g

18、. LDR R1, PC, #operandConstant in the range -255 . -1e.g. SUB R0, R0, #operandK Same as I, but inverted Same as I, but shiftedL Same as I, but negatedConstant in the range -7 . 7e.g. SUB R0, R1, #operandl Same as rRegisters r0.r7e.g. PUSH operandMConstant in the range of 0 . 32 or a power of 2e.g. M

19、OV R2, R1, ROR #operandConstant that is a multiple of 4 in the range of 0 . 1020e.g. ADD R0, SP, #operandm Any valid memory addressN Not availableConstant in the range of 0 . 31e.g. LSL R0, R1, #operandO Not available Constant that is a multiple of 4 in the range of -508 . 508e.g. ADD SP, #operandrG

20、eneral register r0 . r15e.g. SUB operand1, operand2, operand3Not availablew Vector floating point registers s0 . s31 Not availableX Any operand限制字符可能要单个 modifier 指示。要是没有 modifier 指示的默认为 read-only operand。Modifier Specifies= Write-only operand, usually used for all output operands+ Read-write operand

21、, must be listed as an output operand在以张表中读取一个值然后在写到该表的另一个位置。其他内嵌汇编作为预处理宏要是经常使用使用部分汇编,最好的方法是将它以宏的形式定义在头文件中。使用该头文件在严格的 ANSI 模式下会出现警告。为了避免该类问题,可以使用_asm_ 代替 asm,_volatile_代替volatile。这可以等同于别名。下面就是个例程:#define BYTESWAP(val) _asm_ _volatile_ ( “eor r3, %1, %1, ror #16nt“ “bic r3, r3, #0x00FF0000nt“ “mov %0

22、, %1, ror #8nt“ “eor %0, %0, r3, lsr #8“ : “=r“ (val) : “0“(val) : “r3“, “cc“ );C 桩函数宏定义包含的是相同的代码。这在大型 routine 中是不可以接受的。这种情况下最好定义个桩函数。unsigned long ByteSwap(unsigned long val)asm volatile (“eor r3, %1, %1, ror #16nt“bic r3, r3, #0x00FF0000nt“mov %0, %1, ror #8nt“eor %0, %0, r3, lsr #8“: “=r“ (val): “0“(val): “r3“ );return val;替换 C 变量的符号名默认的情况下,GCC 使用同函数或者变量相同的符号名。你可以使用 asm 声明,为汇编代码指定一个不同的符号名unsigned long value asm(“clock“) = 3686400这个声明告诉编译器使用了符号名 clock 代替了具体的值。替换 C 函数的符号名为了改变函数名,你需要一个原型声明,因为编译器不接受在函数定义中出现 asm 关键字。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育教学资料库 > 精品笔记

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。