GNU风格 ARM 汇编指令语法总结

GNU ARM汇编语句格式

 

汇编源程序一般用于系统最基本的初始化:初始化堆栈指针、设置页表、操作 ARM的协处理器等。这些初始化工作完成后就可以跳转到C代码main函数中执行。

1、  GNU汇编语言语句格式

   任何Linux汇编行都是如下结构:
       [

  •          instruction伪指令
  •          directive伪操作
  •          pseudo-instruction伪指令
  •          
  •          comment为语句的注释

下面定义一个"add"的函数,最终返回两个参数的和:

    .section .text, “x”
    .global add      @ give the symbol “add” external linkage

    add:
        ADD r0, r0, r1 @ add input arguments
        MOV pc, lr   @ return from subroutine

    @ end of program

注意:

  •     ARM指令,伪指令,伪操作,寄存器名可以全部为大写字母,也可全部为小写字母,但不可大小写混用。
  •     如果语句太长,可以将一条语句分几行来书写,在行末用“\”表示换行(即下一行与本行为同一语句)。“\”后不能有任何字符,包含空格和制表符(Tab)。

2、  GNU汇编程序中的标号symbol(或label)

    标号只能由a~z,A~Z,0~9,“.”,_等(由点、字母、数字、下划线等组成,除局部标号外,不能以数字开头)字符组成。
    Symbol的本质:代表它所在的地址,因此也可以当作变量或者函数来使用。

  •    段内标号的地址值在汇编时确定;
  •    段外标号的地址值在连接时确定。

Symbol的分类:3类(依据标号的生成方式)。

  1. 基于PC的标号。基于PC的标号是位于目标指令前的标号或者程序中数据定义伪操作前的标号。这种标号在汇编时将被处理成PC值加上(或减去)一个数字常量,常用于表示跳转指令”b”等的目标地址,或者代码段中所嵌入的少量数据。
  2. 基于寄存器的标号。基于寄存器的标号常用MAP和FIELD来定义,也可以用EQU来定义。这种标号在汇编时将被处理成寄存器的值加 上(或减去)一个数字常量,常用于访问数据段中的数据。
  3. 绝对地址。绝对地址是一个32位数据。它可以寻址的范围为[0,232-1]即可以直接寻址整个内存空间。

特别说明:局部标号Symbol

    局部标号主要在局部范围内使用,而且局部标号可以重复出现。它由两部组成:开头是一个0-99直接的数字,后面紧接一个通常表示该局部变量作用范围的符号。局部变量的作用范围通常为当前段,也可以用ROUT来定义局部变量的作用范围。
    局部变量定义的语法格式:N{routname}

  •    N:为0~99之间的数字。
  •    routname:当前局部范围的名称(为符号),通常为该变量作用范围的名称(用ROUT伪操作定义的)。

    局部变量引用的语法格式:%{F|B}{A|T}N{routname}

  •    %:表示引用操作
  •    N:为局部变量的数字号
  •    routname:为当前作用范围的名称(用ROUT伪操作定义的)
  •    F:指示编译器只向前搜索
  •    B:指示编译器只向后搜索
  •    A:指示编译器搜索宏的所有嵌套层次
  •    T:指示编译器搜索宏的当前层次

例:使用局部符号的例子,一段循环程序

   subs r0, r0, #1 @每次循环使r0=r0-1
   bne 1F      @跳转到1标号去执行
   ...
1:
   mov pc,lr

注意:

  •    如果F和B都没有指定,编译器先向前搜索,再向后搜索
  •    如果A和T都没有指定,编译器搜索所有从当前层次到宏的最高层次,比当前层次低的层次不再搜索。
  •    如果指定了routname,编译器向前搜索最近的ROUT伪操作,若routname与该ROUT伪操作定义的名称不匹配,编译器报告错误,汇编失败。

3、  GNU汇编程序中的常数

  1.   十进制数以非0数字开头,如:123和9876;
  2.   二进制数以0b开头,其中字母也可以为大写;
  3.   八进制数以0开始,如:0456,0123;
  4.   十六进制数以0x开头,如:0xabcd,0X123f;
  5.   字符串常量需要用引号括起来,中间也可以使用转义字符,如: “You are welcome!\n”;
  6.   当前地址以“.”表示,在GNU汇编程序中可以使用这个符号代表当前指令的地址;
  7.   表达式:在汇编程序中的表达式可以使用常数或者数值, “-”表示取负数, “~”表示取补,“<>”表示不相等,其他的符号如:+、-、*、 /、%、<、<<、>、>>、|、&、^、!、==、>=、<=、&&、|| 跟C语言中的用法相似。

4、 函数的定义伪操作

  • 函数的定义,格式如下:

      函数名:
             函数体
      返回语句

    一般的,函数如果需要在其他文件中调用, 需要用到.global伪操作将函数声明为全局函数。为了不至于在其他程序在调用某个C函数时发生混乱,对寄存器的使用我们需要遵循APCS准则。函数编译器将处理函数代码为一段.global的汇编码。

  • 函数的编写应当遵循如下规则:
    1) a1-a4寄存器(参数、结果或暂存寄存器,r0到r3 的同义字)以及浮点寄存器f0-f3(如果存在浮点协处理器)在函数中是不必保存的;
    2) 如果函数返回一个不大于一个字大小的值,则在函数结束时应该把这个值送到 r0 中;
    3) 如果函数返回一个浮点数,则在函数结束时把它放入浮点寄存器f0中;
    4) 如果函数的过程改动了sp(堆栈指针,r13)、fp(框架指针,r11)、sl(堆栈限制,r10)、lr(连接寄存器,r14)、v1-v8(变量寄存器,r4 到 r11)和 f4-f7,那么函数结束时这些寄存器应当被恢复为包含在进入函数时它所持有的值。

 例如:

asm_putc:
	ldr	r1, =ELFIN_UART_BASE
1:	ldr   r2, [r1, #UTRSTAT_OFFSET]
	tst   r2, #2
	beq  1b
	str	r0, [r1, #UTXH_OFFSET]
	mov	pc, lr

5、  GNU ARM汇编特殊字符和语法

  1.   代码行中的注释符号: ‘@’
  2.   整行注释符号: ‘#’
  3.   语句分离符号: ‘;’
  4.   立即数前缀: ‘#’ 或 ‘$’

 

GNU ARM汇编伪操作

1、.section

  语法格式

.section section_name[,"flags"[,%type[,flag_specific_arguments]]] 

  定义一个段,每一个段以段名为开始,以下一个段名或者文件结尾为结束。
  ELF格式允许的段标志: a:可分配
             w:可写段
              x:执行段

  举例

.section .mysection   @自定义数据段,段名为“.mysection”

2、预定义段 .text、.data、.bss

  语法格式

.text{subsection}       @代码段

.data{subsection}       @初始化数据段 .data Read-write initialized long data 

.bss{subsection}        @未初始化数据段

  .text、.data和.bss将汇编系统预定义的段名编译到相应的代码段、数据段和bss段。

  注意:源程序中.bss段应该在.text之前。

  bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域 数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域

  举例

.section .data 
 

.section .bss 
 

.section .text .global _start 
_start: 

3、.code16、code32、.thumb、.arm

  语法格式

.code16、code32 

.thumb 

.arm 

  用来选择ARM或者Thumb指令集,.thumb伪操作等同于.code16,表明使用Thumb指令类似的.arm等同于.code32

   .code伪操作用来选择ARM或者Thumb指令集,格式如下:
    .code 表达式
  如果表达式的值为16则表明下面的指令为Thumb指令,如果表达式的值为32则表明下面的指令为ARM指令.

  •          .thumb伪操作等同于.code 16, 表明使用Thumb指令, 类似的.arm等同于.code 32
  •          .force_thumb伪操作用来强制目标处理器选择thumb的指令集而不管处理器是否支持
  •          .thumb_func伪操作用来指明一个函数是thumb指令集的函数
  •          .thumb_set伪操作的作用类似于.set, 可以用来给一个标志起一个别名, 比.set功能增加的一点是可以把一个标志标记为thumb函数的入口, 这点功能等同于.thumb_func

4、.end

  语法格式

    .end

  表明源文件的结束,如果该标号之后还有代码,不会被编译到执行文件中

5、.include

  语法格式

.include "filename" 

  可以将指定的文件在使用位置处展开,一般是头文件

6、.incbin

 语法格式

.incbin "file"[,skip[,count]] 

  可以将原封不动的一个二进制文件编译到当前文件中。其中,skip表明是从文件开始跳过skip个字节开始读取文件,count是读取的字数

7、.if、.else/.endif

 语法格式

.if 条件表达式  

    代码段1 

.else

    代码段2 

.endif

    根据一个表达式的值来决定是否要编译下面的代码, 用.endif伪操作来表示条件判断的结束, 中间可以使用.else来决定.if的条件不满足的情况下应该编译哪一部分代码。

       .if有多个变种:

            .ifdef symbol                   @判断symbol是否定义
            .ifndef symbol                 @判断是否没有定义symbol, 跟.ifdef恰好相反
            .ifnotdef symbol              @同.ifndef
            .ifc string1,string2           @字符串string1和string2是否相等,字符串可以用单引号括起来
            .ifeq expression              @判断expression的值是否为0
            .ifeqs string1,string2       @判断string1和string2是否相等,字符 串必须用双引号括起来
            .ifge expression              @判断expression的值是否大于等于0
            .ifgt absolute expression@判断expression的值是否大于0
            .ifle expression               @判断expression的值是否小于等于0
            .iflt absolute expression @判断expression的值是否小于0
            .ifnc string1,string2         @判断string1和string2是否不相等, 其用法跟.ifc恰好相反。
            .ifne expression              @如果expression的值不是0, 那么编译器将编译下面的代码
            .ifnes string1,string2       @如果字符串string1和string2不相 等, 那么编译器将编译下面的代码.

8、.align

  语法格式
               .align [absexpr1, absexpr2]

  用来指定数据的对齐方式。以某种对齐方式,在未使用的存储区域填充值. 第一个值表示对齐方式,4, 8,16或 32. 第二个表达式值表示填充的值。

9、.macro、.exitm和.endm

  语法格式

.macro 宏名 参数名列表       @伪操作.macro定义一个宏  
      宏体 
.endm                @.endm表示宏结束 

  如果宏使用参数,那么在宏体中使用该参数时添加前缀“\”。宏定义时的参数还可以使用默认值,可以使用.exitm伪指令来退出宏

  举例

.macro SHIFTLEFT a,b 

.if \b<0 
   MOV \a,\a,ASR #-\b 
   .exitm 
.endif 
   MOV \a,\a,LSL #\b 

.endm

10、.byte

  语法格式

.byte 表达式{,表达式}... 

  定义单字节

  举例

.byte 1,2,'s',0x34

11、.short、.hword

  语法格式

.short/.hword 表达式{,表达式}... 

  定义双字节数据

  举例

    .short 0x1234

12、.long、.word、.int

  语法格式
              .long/.word/.int 表达式{,表达式}... 

  .long、.word和.int定义4字节数据

       举例

.long 0x12345678,23876565
定义两个4字节的数值

valueOfStart:
      .word Start
这样程序的开头Start便被存入了内存变量valueOfStart中。

hwlist:
     .hword  {,} …
插入一个16-bit的数据队列。(与armasm中的DCW相同)

13、.quad、.float

  语法格式
            .quad 表达式{,表达式}...

  .quad定义8字节数据;.float定义浮点数

       举例

.quad 0x1234567890abcdef
.float 0f-31415926535897932384626433832795028841971.693993751E-40 @ - pi

14、.string/.asciz/.ascii

  语法格式
        .string/.asciz/.ascii 表达式{,表达式}...

  .string/.asciz/.ascii定义多个字符串。注意:ascii伪操作定义的字符串需要自动添加结尾字符'\0'

  举例

.string "abcd", "efgh", "hello!"
.asciz "qwer", "sun", "world!"
.ascii "welcome\0"

15、.zero、.space、.skip

  语法格式

.zero  
.space/.skip  {,} 

  分配number_of_bytes字节的数据空间,.zero伪操作用0填充内存,.space/.skip用值为fill_byte填充内存,若未指定该值,缺省填充0

16、.rept、.endr

  语法格式

.rept 重复次数  
  数据定义 
.endr       @结束重复定义 

  .rept及.endr用于重复定义伪操作

  举例

.rept 3 
.byte 0x23 
.endr

17、.equ、.set

  语法格式
                .equ(.set)常量名,表达式

  .equ和.set用于为程序中标号定义名称

  举例

.equ abc, 3     @让abc=3

18、.global/.globl

  语法格式
                .global/.globl symbol

  .global和.globl用来定义一个全局的符号

  举例
              汇编程序的缺省入口是_start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点。

.globl  _start
_start:
    

19、.extern

  语法格式
               .extern label

  .extern用于声明一个外部标号

20、.ltorg、.pool

  语法格式
               .ltorg/.pool

  .ltorg和.pool用于声明一个数据缓冲池的开始,它可以分配很大的空间

21、ARM特有的伪操作

  21.1、语法格式
               .reg: 用来给寄存器赋予别名,格式如下:
                       别名 .req 寄存器名

              .unreq: 用来取消一个寄存器的别名,格式如下:
             .unreq 寄存器别名

       注意被取消的别名必须事先定义过,否则编译器就会报错,这个伪操作也可以用来取消系统预制的别名, 例如r0, 但如果没有必要的话不推荐那样做。

22、其他伪命令

      .err       使编译结果产生错误报告
      .eject     在汇编符号列表文件中插入一分页符
      .list      .产生汇编列表(从.list到.nolist)
      .nolist    汇编列表结束处。再次使用.list产生汇编列表
      .title "title_name"  使用heading作为标题(位于汇编列表文件中文件名下一行)
      .sbttl "title_name"  使用heading作为子标题(位于.title标题下一行)
      .print string        打印输出信息到标准输出

你可能感兴趣的:(ARM)