网站设计酷站,学校网站建设培训心得体会,做网站花多少钱,如何成立一家公司GNU 汇编语法
在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编#xff0c;因为 Cortex-A 芯片一上电 SP 指针还没初始化#xff0c;C 环境还没准备好#xff0c;所以肯定不能运行 C 代码#xff0c;必须先用汇编语言设置好 C 环境#xff0c;比如初始化 DDR、…GNU 汇编语法
在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编因为 Cortex-A 芯片一上电 SP 指针还没初始化C 环境还没准备好所以肯定不能运行 C 代码必须先用汇编语言设置好 C 环境比如初始化 DDR、设置 SP指针等等当汇编把 C 环境设置好了以后才可以运行 C 代码。所以 Cortex-A 一开始肯定是汇编代码其实 STM32 也一样的一开始也是汇编比如MDK 和 IAR 下的启动文件 startup_stm32f10x_hd.s其中的汇编语法是有所不同的将 MDK 下的汇编文件直接复制到 IAR 下去编译就会出错因为 MDK 和 IAR 的编译器不同因此对于汇编的语法就有一些小区别。我们要编写的是 ARM汇编编译使用的 GCC 交叉编译器所以我们的汇编代码要符合 GNU 语法。
GNU汇编语法适用于所有的架构并不是ARM独享GNU汇编由一系列的语句组成
注意ARM中的指令、伪指令、伪操作、寄存器名等可以全部使用大写也可以全部使用小写但是不能大小写混用。
伪操作
伪操作含义.byte定义单字节数据比如.byte 0x12.short定义双字节数据比如.short 0x1234.long定义一个4字节数据比如.long 0x12345678.equ赋值语句格式为.equ 变量名表达式比如.equ num, 0x12表示 num0x12.align数据字节对齐比如.align 4 表示 4 字节对齐。.end表示源文件结束。.global定义一个全局符号格式为.global symbol比如.global _start。
比如
.global _start_start:
ldr r0,0x12 r00x12.global是一个位操作表示_start是一个全局标号相当于C语言中的全局变量一样。
段名
汇编系统预定义了一些段名
定义段名.text表示代码段.data初始化数据段.bss未初始化的数据段.rodata只读数据段
比如
函数
函数格式如下
函数名函数体返回语句GNU汇编函数返回语句不是必须的比如Cortex-A7中断服务函数
/*未定义中断*/
Undefined_Handler:ldr r0,Undefined_Handlerbx r0/*svc中断*/
SVC_Handler:ldr r0,SVC_Handlerbx r0/*预取中止中断*/
PerfAbort_Handler:ldr r0,PrefAbort_Handlerbx r0Cortex-A7常用汇编指令
需要系统性的学习Cortex-A7的所有汇编指令可以参考《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition.pdf》的 A4章节。
数据传输指令
指令目的寄存器源寄存器描述MOVR0R1将R1中的数据复制到R0中MRSR0CPSR将特殊寄存器CPSR里面额数据复制到R0中MSRCPSRR1将R1里面的数据复制到特殊寄存器CPSR中
1.MOV指令
MOV指令用于将数据从一个寄存器拷贝到另一个寄存器或者将一个立即数传递到寄存器中。示例如下
MOV R0,R1 将寄存器中R1的数据传递给R0,即R0R1
MOV R0,#0X12 将立即数0x12传递给R0寄存器即R00X122.MRS指令
MRS指令用于将特殊寄存器(如CPSR和SPSR)中的数据传递给通用寄存器要读取特殊寄存器中的数据只能使用MRS指令。示例如下
MRS R0,CPSR 将特殊寄存CPSR里面的数据传递给R0,即R0CPSR3.MSR指令
MSR 指令和 MRS 刚好相反MSR 指令用来将普通寄存器的数据传递给特殊寄存器也就是写特殊寄存器写特殊寄存器只能使用 MSR示例如下
MSR CPSR,R0 将R0的数据复制到CPSR中即CPSRR0存储器访问指令
ARM 不能直接访问存储器比如 RAM 中的数据I.MX6UL 中的寄存器就是 RAM 类型的我们用汇编来配置 I.MX6UL 寄存器的时候需要借助存储器访问指令一般先将要配置的值写入到 Rx(x0~12)寄存器中然后借助存储器访问指令将 Rx 中的数据写入到 I.MX6UL 寄存器中。读取 I.MX6UL 寄存器也是一样的只是过程相反。常用的存储器访问指令有两种LDR 和STR
指令描述LDR Rd,[Rn,#offset]从存储器Rnoffset的位置读取数据存放到Rd中STR Rd,[Rn,#offset]将Rd中的数据写入到存储器中的Rnoffset位置
1.LDR指令
LDR主要是从存储器中加载数据到寄存器Rx中LDR也可以将一个立即数加载到寄存器Rx中LDR加载立即数的时候要使用“,而不是”#。在嵌入式开发中LDR最常用的就是读取 CPU 的寄存器值比如 I.MX6UL 有个寄存器 GPIO1_GDIR其地址为 0X0209C004我们现在要读取这个寄存器中的数据示例代码如下
LDR R0,0x0209C004 将寄存器地址0x0209C004加载到R0寄存器中即R00x0209C004
LDR R1,[R0] 读取地址0x0209C004中的数据到R1寄存器中上述代码就是读取寄存器 GPIO1_GDIR 中的值读取到的寄存器值保存在 R1 寄存器中上面代码中 offset 是 0也就是没有用到 offset。
2.STR指令
LDR 是从存储器读取数据STR 就是将数据写入到存储器中同样以 I.MX6UL 寄存器GPIO1_GDIR 为例现在我们要配置寄存器 GPIO1_GDIR 的值为 0X2000002示例代码如下
LDR R0, 0x0209C004 将寄存器地址0x0209C004加载到R0中即R00x0209C004
LDR R1, 0X20000002 R1保存要写入到寄存器的值即R10X20000002
STR R1,[R0] 将R1的值写入到R0中所保存的地址中LDR 和 STR 都是按照字进行读取和写入的也就是操作的 32 位数据如果要按照字节、半字进行操作的话可以在指令“LDR”后面加上 B 或 H比如按字节操作的指令就是 LDRB 和STRB按半字操作的指令就是 LDRH 和 STRH。
3.ADR指令
ADR是一条小范围地址读取伪指令它将基于PC的相对偏移的地址值读到目标寄存器中
ADR R0exper编译源程序时汇编器首先计算当前PC值(当前指令位置)到exper的距离然后用一条ADD或SUB指令替换这条伪指令
比如
add register,pc,#offset_to_exper 标号exper与指令必须要在同一代码段
adr r0,_start 将指定地址赋值r0r0的值为标号_start与此指令的距离差 PC值。
堆栈操作指令
在日常代码逻辑中函数的调用随处可见通常会在 A 函数中调用 B 函数当 B 函数执行完以后再回到 A 函数继续执行。要想在跳回 A 函数以后代码能够接着正常运行那就必须在跳到 B 函数之前将当前处理器状态保存起来(就是保存 R0-R15 这些寄存器值)当 B 函数执行完成以后再用前面保存的寄存器值恢复R0-R15 即可。保存 R0-R15 寄存器的操作就叫做现场保护恢复 R0R15 寄存器的操作就叫做恢复现场。在进行现场保护的时候需要进行压栈(入栈)操作恢复现场就要进行出栈操作。压栈的指令为 PUSH出栈的指令为 POPPUSH 和 POP 是一种多存储和多加载指令即可以一次操作多个寄存器数据他们利用当前的栈指针 SP 来生成地址。
1.PUSH和POP
指令描述PUSH将寄存器列表存入栈中POP从栈中恢复寄存器列表
示例代码
PUSH {R0-R3,R12} 将R0-R3和R12压入堆栈
PUSH {LR} 将LR连接寄存器压入堆栈压栈后堆栈示意图(0x80000000是栈底每增加数据SP想低地址方向即栈顶方向移动) 出栈代码
POP {LR} 先恢复 LR
POP {R0~R3,R12} 在恢复 R0~R3,R12出栈的就是从栈顶也就是 SP 当前执行的位置开始地址依次减小来提取堆栈中的数据到要恢复的寄存器列表中。
2.STMFD和LDMFD
PUSH 和 POP 的另外一种写法是“STMFD SP”和“LDMFD SP!”上述代码可以改成 STMFD SP!,{R0~R3, R12} R0~R3,R12 入栈STMFD SP!,{LR} LR 入栈LDMFD SP!, {LR} 先恢复 LRLDMFD SP!, {R0~R3, R12} 再恢复 R0~R3, R12TMFD 可以分为两部分STM 和 FD同理LDMFD 也可以分为 LDM 和 FD。
LDR和STR是数据加载和存储指令但是每次只能读写存储器中的一个数据。STM 和 LDM 就是多存储和多加载可以连续的读写存储器中的多个连续数据。FD 是 Full Descending 的缩写即满递减的意思。根据 ATPCS 规则,ARM 使用的 FD 类型的堆栈SP 指向最后一个入栈的数值堆栈是由高地址向下增长的也就是前面说的向下增长的堆栈因此最常用的指令就是 STMFD 和 LDMFD。STM 和 LDM 的指令寄存器列表中编号小的对应低地址编号高的对应高地址。
跳转指令
关于跳转可以有多种跳转方法比如
1.直接使用跳转指令B、BL、BX
2.直接向PC寄存器里面写入数据借助PC指针直接跳转
通常我们还是使用跳转指令进行跳转
指令描述B跳转到label如果跳转范围超过了/-2KB可以指定B.W使用32位版本的跳转指令这样可以得到较大范围的跳转BX间接跳转跳转到存放于Rm中的地址处并切换指令集BL跳转到标号地址并将返回地址保存在LR中BLX结合 BX 和 BL 的特点跳转到 Rm 指定的地址并将返回地址保存在 LR 中切换指令集
在汇编中常用的是B和BL指令
1.B指令
B 指令会将 PC 寄存器的值设置为跳转目标地址 一旦执行 B 指令ARM 处理器就会立即跳转到指定的目标地址。如果要调用的函数不会再返回到原来的执行处那就可以用 B 指令。示例如下
_start:ldr sp,0X80200000 设置栈指针b main 跳转到main函数这就是典型的在汇编中初始化 C运行环境然后跳转到C文件的 main函数中运行上述代码只是初始化了 SP 指针有些处理器还需要做其他的初始化比如初始化 DDR 等等。因为跳转到 C 文件以后再也不会回到汇编了所以在第 4 行使用了 B 指令来完成跳转。
2.BL指令
BL 指令相比 B 指令在跳转之前会在寄存器 LR(R14)中保存当前 PC 寄存器值所以可以通过将 LR 寄存器中的值重新加载到 PC 中来继续从跳转之前的代码处运行这是子程序调用一个基本但常用的手段。
比如 Cortex-A 处理器的 irq 中断服务函数都是汇编写的主要用汇编来实现现场的保护和恢复、获取中断号等。但是具体的中断处理过程都是 C 函数所以就会存在汇编中调用 C 函数的问题。
而且当 C 语言版本的中断处理函数执行完成以后是需要返回到irq 汇编中断服务函数因为还要处理其他的工作一般是恢复现场。这个时候就不能直接使用B 指令了因为 B 指令一旦跳转就再也不会回来了这个时候要使用 BL 指令。
push {r0,r1} 保存r0,r1
cps #0x13 进入SVC模式允许其他中断再次进去bl system_irqhandler 加载C语言中断处理函数到r2寄存器中cps #0x12 进入IRQ模式
pop {r0,r1}
str r0,[r1,#0x10] 中断执行完成写EOIR第 5 行就是执行 C 语言版的中断处理函数当处理完成以后是需要返回来继续执行下面的程序所以使用了 BL 指令。
注
cps是一条armv7的汇编命令功能是在特权模式下直接修改CPSR寄存器的M[4:0]让处理器进入不同的模式
CPSR 的寄存器结构模型 其中M[4:0] 就是处理器的模式控制为不同的编码对应着不同的处理器模式如下图所示 算数运算指令
汇编中可以继续算数运算比如加减乘除常用命令如下表(#immed为立即数)
指令计算公式备注ADD Rd,Rn,RmRd Rn Rm加法运算指令为ADDADD Rd,Rn,#immedRd Rn #immed加法运算指令为ADDADC Rd,Rn,RmRd Rn Rm 进位带进位的加法运算指令为ADCADC Rd,Rn,#immedRd Rd #immed 进位带进位的加法运算指令为 ADCSUB Rd,Rn,RmRd Rn – Rm减法SUB Rd,#immedRd Rd - #immed减法SUB Rd, Rn, #immedRd Rn - #immed减法SBC Rd, Rn, #immedRd Rn - #immed – 借位带借位的减法SBC Rd, Rn ,RmRd Rn – Rm – 借位带借位的减法MUL Rd, Rn, RmRd Rn * Rm乘法(32 位)UDIV Rd, Rn, RmRd Rn / Rm无符号除法SDIV Rd, Rn, RmRd Rn / Rm有符号除法
在嵌入式开发中最常会用的就是加减指令乘除基本用不到。
逻辑运算指令
在C语言中常常需要用到逻辑运算比如“和”|等逻辑运算符。使用汇编语言的时候也可以使用逻辑运算指令如下表
指令计算公式备注AND Rd,RnRd Rd Rn按位与AND Rd,Rn,#immedRd Rn #immed按位与AND Rd,RnRmRd Rn Rm按位与ORR Rd,RnRd Rd| Rn按位或ORR Rd,Rn,RmRd Rn|Rm按位或ORR Rd,Rn,#immedRd Rn|#immed按位或BIC Rd,RnRd Rd (~Rn)位清除BIC Rd,Rn,RmRd Rn (~Rm)位清除BIC Rd,Rn,#immedRd Rn (~#immed)位清除ORN Rd,Rn,#immedRd Rn|(#immed)按位或非ORN Rd,Rn,RmRd Rn|(Rm)按位或非EOR Rd,RnRd Rd ^ Rn按位异或EOR Rd,Rn,#immedRd Rd ^ #immed按位异或EOR Rd,Rn,RmRd Rn ^ Rm按位异或
参考文章 https://blog.csdn.net/weixin_45309916/article/details/107837561