IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 基本内联汇编和扩展内联汇编 -> 正文阅读

[嵌入式]基本内联汇编和扩展内联汇编

基本内联汇编

基本内联汇编是最简单的内联形式,其格式为:
asm [volatile] ("assembly code")

  • asm:用于声明内联汇编表达式,这是内联汇编固定补分,不可少。asm和__asm__ 是一样的,是由gcc定义的宏:#define __asm__ asm

  • volatile:是可选项,它告诉gcc:“不要修改我写的汇编代码,请原样保留”。volatile 和__volatile__是一样的,是由gcc定义的宏:#define __volatile__ volatile

  • “assembly code” 是咱们所写的汇编代码,它必须位于圆括号中,而且必须用双引号括起来。规则如下:

    1. 指令必须用双引号引起来,无论双引号中是一条指令或多条指令
    2. 一对双引号不能跨行,如果跨行需要在结尾用反斜杠\转义。
    3. 指令之间用分号; 换行符\n或换行符加制表符\n \t分隔。
    4. 当指令在多个双引号中时,除最后一个双引号外,其余双引号中的代码最后一定要有分隔符,如:
      asm(“movl $9,%eax;””pushl %eax”) 正确
      asm(“movl $9,%eax””pushl %eax”)  错误
      

扩展内联汇编

格式:
asm [volatile] (“assembly code”:output : input : clobber/modify)

  • output:用来指定汇编代码的数据如何输出给C代码使用。内嵌的汇编指令运行结束后,如果想将运行结果存储到C变量中,就用此项指定输出的位置。output中每个操作数的格式为:“操作数修饰符约束名”(C 变量名),操作数修饰符通常为等号=。多个操作数之间用逗号,分隔。

  • input:用来指定C中数据如何输入给汇编使用。要向让汇编使用C中的变量作为参数,就要在此指定。input中每个操作数的格式为:“[操作数修饰符] 约束名”(C 变量名),操作数修饰符为可选项。多个操作数之间用逗号,分隔。

  • clobber/modify:汇编代码执行后会破坏一些内存和寄存器资源,通过此项通知编译器,可能造成寄存器或内存数据破坏,这样gcc就知道哪些寄存器或内存需要提前保护前来。

    1. 格式:用双引号把寄存器名称引起来,多个寄存器之间用逗号,分隔,寄存器不用再加两个%%,只要写名称即可,如:asm("movl %%eax, %0;movl %%eax,%%ebx":"=m" (ret_value)::"bx")
    2. 如果我们的内联汇编代码修改了标志寄存器 eflags 中的标志位,同样需要在 clobber/modify 中用”cc”声明
    3. 如果我们修改了内存,我们需要在 clobber/modify 中”memory”声明。
    4. 如果我们在 output 中使用了内存约束,gcc 自然会得到哪块内存被修改。但如果被修改的内容并未在output 中,我们就需要用”memory”告诉 gcc 啦。

  • 约束的作用:约束的作用是让C代码的操作数变成汇编代码能使用的操作数,所有的约束形式其实都是给汇编用的。约束是C语言中的操作数与汇编语言中的操作数之间的映射,他告诉gcc,同一个操作数在两种环境下如何变换身份,如何对接沟通。编译过程中C代码是要先变成汇编代码的,内联汇编中的约束就相当于gcc让咱们指定C中数据的编译形式。



以下是各种约束的解释:

  • 寄存器约束:寄存器约束就是要求gcc使用哪个寄存器,将input或output变量约束在某个寄存器中,常见的寄存器约束有:
    a:表示寄存器 eax/ax/al
    b:表示寄存器 ebx/bx/bl
    c:表示寄存器 ecx/cx/cl
    d:表示寄存器 edx/dx/dl
    D:表示寄存器 edi/di
    S:表示寄存器 esi/si
    q:表示任意这 4 个通用寄存器之一:eax/ebx/ecx/edx
    r:表示任意这 6 个通用寄存器之一:eax/ebx/ecx/edx/esi/edi
    g:表示可以存放到任意地点(寄存器和内存)。相当于除了同 q 一样外,还可以让 gcc 安排在内存中
    A:把 eax 和 edx 组合成 64 位整数
    f:表示浮点寄存器
    t:表示第 1 个浮点寄存器
    u:表示第 2 个浮点寄存器

  • 内存约束:内存约束是要求gcc直接将位于input和output中的C变量内存地址作为内联汇编代码的操作数,不需要寄存器做中转,直接进行内存读写,也就是汇编代码的操作数是C变量的指针。
    m:表示操作数可以使用任意一种内存形式
    o:操作数为内存变量,但访问它是通过偏移量的形式访问,即包括offset_address的格式。

  • 立即数约束:立即数即常数,此约束要求gcc在传值的时候不通过内存和寄存器,直接作为立即数传给汇编代码。由于立即数不是变量,只能作为右值,所以只能放在input中。
    i:表示操作数为整数立即数
    F:表示操作数为浮点数立即数
    I:表示操作数为 0~31 之间的立即数
    J:表示操作数为 0~63 之间的立即数
    N:表示操作数为 0~255 之间的立即数
    O:表示操作数为 0~32 之间的立即数
    X:表示操作数为任何类型立即数

  • 通用约束:
    0~9:此约束只用在 input 部分,但表示可与 output 和 input 中第 n 个操作数用相同的寄存器或内存。

  • 占位符:为了方便对操作数的引用,扩展内联汇编提供了占位符,他的作用是代表约束指定的操作数(寄存器、内存、立即数),我们更多的是在内联汇编中使用占位符来引用操作数。占位符分为序号占位符和名称占位符:

    1. 序号占位符:序号占位符是对在output和input中的操作数,按照它们从左到右的次序从0开始编号,一直到9,也就是说最多支持10个序号占位符,引用它的格式是%0~9。占位符所表示的操作数默认情况下为 32 位数据,在%和序号之间插入字符’h’来表示操作数为ah(第 8~15 位),或者插入字符’b’来表示操作数为 al(第 0~7 位)。
      h –输出寄存器高位部分中的那一字节对应的寄存器名称,如 ah、bh、ch、dh。
      b –输出寄存器中低部分 1 字节对应的名称,如 al、bl、cl、dl。
      w –输出寄存器中大小为 2 个字节对应的部分,如 ax、bx、cx、dx。
      k –输出寄存器的四字节部分,如 eax、ebx、ecx、edx。
      如:
      asm("addl %%ebx, %%eax":"=a"(out_sum):"a"(in_a),"b"(in_b));
      等价于
      asm("addl %2, %1":"=a"(out_sum):"a"(in_a),"b"(in_b));
      其中:
      “=a”(out_sum)序号为 0,%0 对应的是 eax。
      “a”(in_a)序号为 1,%1 对应的是 eax。
      “b”(in_b)序号为 2,%2 对应的是 ebx。

    2. 名称占位符:名称占位符与序号占位符不同,序号占位符靠本身出现在output和input中的位置就能被编译器识别出来。而名称占位符需要在output和input中把操作数显式地起个名字,它用这样的格式来标识操作数:[名称]”约束名”(C 变量)。这样,该约束对应的汇编操作数便有了名字,在 assembly code 中引用操作数时,采用%[名称]的形式就可以了。

  • 由于扩展内联汇编中的占位符要有前缀%,为了区别占位符和寄存器,只好在寄存器前用两个%做前缀啦,这就是本节前面解释在扩展内联汇编中寄存器前面要有两个%做前缀的原因。

  • 在约束中还有操作数类型修饰符,用来修饰所约束的操作数:内存、寄存器,分别在ouput 和 input中有以下几种。
    在output中:
    =:表示操作数是只写,相当于为 output 括号中的 C 变量赋值,如=a(c_var),此修饰符相当于 c_var=eax。
    +:表示操作数是可读写的,告诉 gcc 所约束的寄存器或内存先被读入,再被写入。
    &:表示此 output 中的操作数要独占所约束(分配)的寄存器,只供 output 使用,任何 input 中所分配的寄存器不能与此相同。注意,当表达式中有多个修饰符时,&要与约束名挨着,不能分隔。
    在input中:
    %:input 中的输入可以和下一个 input 操作数互换

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-17 12:54:59  更:2021-11-17 12:56:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 8:17:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码