一、综述
在不同的平台下,或者不同的编译器,汇编语法会有所不同。例如,MDK和IAR的启动文件startup_stm32f10x_hd.s都是不同的,因为它们的编译器不同。汇编语法就有区别。而在ARM下,使用gcc交叉编译器,所有需要GNU汇编语法。GNU汇编语法适用于所有的架构,并不是ARM独享。
GNU汇编由一系列的语句组成,每一行一条语句,每条语句有如下三个可选部分。
label:instruction @comment
label,标号,表示地址位置,标号也可以用来表示数据地址。任何以”:“结尾的标识符都会被识别为一个标号。
instruction:指令,表示汇编指令或伪指令。
comment:注释。
例如
add:
movs r0, #0x12 @设置R0=0x12
上面是两行汇编语句,第一条语句只有标号:add。第二条语句包含了指令和注释。需要注意的是,ARM语句中可以全部使用大写,也可以全部使用小写,但是不能大小写混用。
二、伪操作、函数
常见的伪操作有:
.byte 定义单字节数据,例如.byte 0x12
.short 定义双字节数据,例如.short 0x1234
.long 定义4字节数据,例如.long 0x1234 5678
.equ 赋值语句,例如.equ num, 0x12,表示num=0x12
.align 数据字节对齐,例如.align 4,表示4字节对齐
.end 表示源文件结束
.global 定义全局,例如.global _start
.section 定义一个段,例如.section .addnum,表示定义了一个.addnum的段
汇编系统预定义了一些段名:
.text 代码段
.data 初始化的数据段
.bss 未初始化的数据段
.rodata 只读数据段
例如
.global _start
_start:
ldr r0, =0x12 @r0=0x12
汇编也支持函数,如下所示
函数名:
函数体
返回语句
例如
SVC_Handler:
ldr r0, = SVC_Handler
bx r0
三、常用汇编指令
1、处理器内部数据传输指令
常用的指令有三个:mov、mrs和msr。
如下图所示:
mov指令用于将数据从一个寄存器复制到另外一个寄存器,或者将一个立即数传递到寄存器里面。
mov r0, r1 @将寄存器r1中的数据传递给r0,即r0=r1
mov r0, #0x12 @将立即数0x12传递给r0,即r0=0x12
mrs指令用于将特殊寄存器中的数据传递给通用寄存器,即读特殊寄存器。
mrs r0, cpsr @将特殊寄存器cpsr里面的数据传递给r0,即r0=cpsr
msr指令用于将通用寄存器中的数据传递给特殊寄存器。即写特殊寄存器。
msr cpsr,r0 @将r0中的数据复制到cpsr中,即cpsr=r0
2、存储器访问指令
ARM不能直接访问存储器,存储器的地址,需要通过寄存器来间接的获取。常用的指令有两个,ldr,str。
ldr指令用于从存储器加载数据到寄存器rx中,也可以将一个立即数加载到寄存器rx中。
ldr r0, =0x0209c004 @将寄存器地址0x0209c004加载到r0中,即r0=0x0209c004
ldr r1, [r0] @读取地址0x0209c004中的数据到r1寄存器中,其中offset为0
str指令用于将数据写入到存储器中。
ldr r0, =0x0209c004 @将寄存器地址0x0209c004加载到r0中,即r0=0x0209c004
ldr r1, =0x20000002 @将r1保存要写入到寄存器的值,即r1=0x200000002
str r1, [r0] @将r1中的值写入到r0中所保存的地址中
3、压栈、出栈
在调用函数时,要想让函数返回后继续之前的代码继续运行,需要将处理器状态保存起来,即保存R0-R15的值。等执行完毕后,再恢复R0-R15。保存的操作称为压栈,恢复的操作称为出栈。压栈的指令为push,出栈的指令为pop。他们可以一次性压栈或出栈多个数据。
push压栈指令,当前的sp指针指向0x8000 0000,处理器的堆栈是向下增长的。
push {r0~r13,r12} @将r0~r3和r12压栈
压栈完成后,如图所示
push {lr} @将lr压栈
pop出栈指令,
pop {lr} @恢复lr
pop {r0~r3,r12} @恢复r0~r3,r12
4、跳转指令
常用的跳转指令:b、bl、bx等。
b指令,最简单的跳转指令,b指令会将pc寄存器的值设置为跳转目标地址,一旦执行,就会立即跳转到指定的目标地址,不会再返回。
_start:
ldr sp, =0x80200000 @设置sp
b main @跳转到main函数
bl指令,它会在跳转之前,在lr中保存当前pc寄存器的值,所以可以通过将lr寄存器的值重新加载到pc中来继续从跳转之前的代码出运行。
push {r0, r1} @保存r0,r1
cps #0x13 @进入svc模式
bl system_irqhandler @加载c语言中中断函数到r2
cps #0x12 @进入irq模式
pop {r0,r1}
str r0, [r1,#0x10] @中断执行完成
5、算数运算指令
6、逻辑运算指令