在进行嵌入式Linux开发的时候是绝对要掌握基本的ARM汇编,因为Cortex-A芯片一上电SP指针还没初始化,C环境还没准备 好,所以肯定不能运行C代码,必须先用汇编语言设置好C环境,至于汇编程序写多少,就是看哪一步才把C语言环境配置好。
所谓的C语言环境就是保证C语言能够正常运行。C语言中的函数调用涉及到出栈入栈,这里解释一下,函数调用就是在使用函数栈,比如A函数调用B函数,在调用B函数执行完毕后,必须返回A函数对应的位置,需要对现场进行保护,而此处说的现场,就是指CPU运行的时候, 用到了一些寄存器,比如R0~R3,LR 等等,对于这些寄存器的值,如果你不保存而直接跳转到函数中去执行,那么很可能会被破坏了,因为函数执行需要用到这些寄存器。所以要进行入栈,把内部寄存器的数据存入栈中,在执行完B函数之后就需要恢复现场,把栈的数据进行出栈,继续执行。
出栈入栈就要对堆栈进行操作,所谓的堆栈其实就是一段内存,这段内存比较特殊,由SP指针访问,SP指针指向栈顶。芯片一上电SP指针还没有初始化,所以C语言没法运行,对于有些芯片还需要初始化DDR,因为芯片本身没有RAM,或者内部RAM不开放给用户使用,用户代码需要在 DDR中运行,因此一开始要用汇编来初始化DDR控制器。
当汇编把C环境设置好了以后才可以运行C代码。所以Cortex-A一开始肯定是汇编代码,其实 STM32 也一样的,一开始也是汇编,以 STM32F103 为例,启动文件 startup_stm32f10x_hd.s 就是汇编文件,只是这个文件ST 已经写好了,我们根本不用去修改,所 以大部分学习者都没有深入的去研究。
汇编的基本格式label:instruction @ comment
label就是标签,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意label后面的“:”,任何以“:”结尾的标识 符都会被识别为一个标号,label可写可不写。另外,label也可以是函数名,因为标号代表它的地址,进入到该地址执行下面的命令就是执行了该函数。
instruction 即指令,也就是汇编指令或伪指令。这个不能缺少。
@ comment就是注释。
注意!ARM中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用 小写,但是不能大小写混用。
比如下面,把立即数0x12拷贝到寄存器R0中,其中add表示这条指令的地址,@就是注释
add: MOVS R0, #0X12 @设置R0=0X12
汇编程序的默认入口标号是_start,不过我们也可以在链接脚本中使用ENTRY来指明其它 的入口点,下面的代码就是使用_start作为入口标号:
.global _start
_start:
ldr r0, =0x12 @r0=0x12
MOV指令:MOV指令用于将数据从一个寄存器拷贝到另外一个寄存器,或者将一个立即数传递到寄 存器里面
MOV R0,R1 @将寄存器R1中的数据传递给R0,即R0=R1
MOV R0, #0X12 @将立即数0X12传递给R0寄存器,即R0=0X12
MRS指令:用于将特殊寄存器(如CPSR 和 SPSR)中的数据传递给通用寄存器,要读取特殊寄存器的数据只能使用MRS指令!
MRS R0, CPSR @将特殊寄存器CPSR里面的数据传递给R0,即R0=CPSR
MSR指令:MSR指令和MRS刚好相反,MSR指令用来将普通寄存器的数据传递给特殊寄存器,也就是写特殊寄存器,写特殊寄存器只能使用MSR
MSR CPSR, R0 @将R0中的数据复制到CPSR中,即CPSR=R0
这三条指令只能处理CPU内部寄存器的数据传输,ARM架构是无法直接访问外部存储器内存(RAM)的,比如RAM中的数据,I.MX6UL 中的寄存器就是RAM类型的,我们用汇编来配置I.MX6UL寄存器的时候需要借助存储器访问指令,一般先将要配置的值写入到Rx(x=0~12)寄存器中,然后借助存储器访问指令将Rx中的数据写入到I.MX6UL寄存器
LDR指令:LDR主要用于从存储加载数据到寄存器Rx中,LDR也可以将一个立即数加载到寄存器Rx 中,LDR加载立即数的时候要使用“=”,而不是“#”
LDR R0, =0X0209C004 @将寄存器地址0X0209C004加载到R0中,即R0=0X0209C0042 LDR R1, [R0] @读取地址0X0209C004中的数据到R1寄存器中
STR指令:LDR 是从存储器读取数据,STR 就是将数据写入到存储器中
LDR R0, =0X0209C004 @将寄存器地址0X0209C004加载到R0中,R0=0X0209C004
LDR R1, =0X @R1保存要写入到寄存器的值,即R1=0X
STR R1, [R0] @将R1中的值写入到R0中所保存的地址中
LDR 和 STR 都是按照字进行读取和写入的,也就是操作的32位数据,如果要按照字节、 半字进行操作的话可以在指令“LDR”后面加上B或H,比如按字节操作的指令就是LDRB和 STRB,按半字操作的指令就是LDRH和STRH
更加复杂的寻址访问方式
STM指令:一次将多个数据写入到存储器中
LDM指令:一次将多个数据从存储器加载到寄存器中
更加复杂的用法 STMXX LDMXX,这里的XX对应下面的4种情况
数据处理指令 汇编可以进行计算操作,也就是加减乘除
ADD指令 加法指令
ADD Rd, Rn, Rm @Rd=Rn+Rm
ADD Rd, Rn, #立即数 @Rd=Rn+该立即数
ADC Rd, Rn,Rm @Rd = Rn + Rm + 进位 这里的进位是CF当中的数据
ADC Rd, Rn,#立即数 @Rd = Rn + 立即数 + 进位 这里的进位是CF当中的数据
SUB指令 减法指令
SBC 指令从寄存器<Rn>中减去<shifter_operand>表示的数值,再减去寄存器CPSR 中C 条件标志位的反码 ,并把结果保存到目标寄存器< Rd>中 ,同时根据操作的结果更新 CPSR 中相应的条件标志位。
SUB Rd, Rn, Rm @Rd=Rn-Rm
SUB Rd, Rn, #立即数 @Rd=Rn-该立即数
SBC Rd, Rn,Rm @Rd = Rn - Rm -借位 这里是CPSR 中C 条件标志位的反码
SBC Rd, Rn,#立即数 @Rd = Rn - 立即数 - 借位 这里是CPSR 中C 条件标志位的反码
逻辑运算指令 我们用C语言进行CPU寄存器配置的时候常常需要用到逻辑运算符号,比如“&”、“|”等逻辑运算符。使用汇编语言的时候也可以使用逻辑运算指令
AND:按位与运算
AND Rd,Rn @Rd=Rd&Rn
AND Rd,#立即数 @Rd=Rd&立即数
AND Rd,Rm,Rn @Rd=Rm&Rn
ORR:按位或运算
ORR Rd, Rn @Rd=Rd|Rn
ORR Rd,#立即数 @Rd=Rd|立即数
ORR Rd,Rm,Rn @Rd=Rm|Rn
BIC:位清除运算
BIC Rd, Rn @Rd = Rd & (~Rn)
BIC Rd, Rn, #immed @Rd = Rn & (~#immed)
BIC Rd, Rn , Rm @Rd = Rn & (~Rm)
注意 汇编里面是无法识别位移指令的 比如1<<4,汇编无法直接转换结果,只能是移位后的数据
ORN:按位或非运算
ORN Rd, Rn, #immed @Rd = Rn | (#immed)
ORN Rd, Rn, Rm @Rd = Rn | (Rm)
EOR:按位异或运算
EOR Rd, Rn @Rd = Rd ^ Rn
EOR Rd, Rn, #immed @Rd = Rn ^ #immed
EOR Rd, Rn, Rm @Rd = Rn ^ Rm
程序跳转指令
B:直接进行跳转,B指令会将PC寄存器的值设置为跳转目标地址, 一旦执行B指令,ARM处理器就会立即跳转到指定的目标地址。
BL:在跳转之前会在寄存器LR(R14)中保存当前PC寄存器值(PC保存的是BL下面一条指令的地址),所以可以通过将LR寄存器中的值重新加载到PC中来继续从跳转之前的代码处运行,这是子程序调用 一个基本但常用的手段。
如下面的图所示,第一条指令地址是0x0,第二条是0x4,第三条是0x8,在执行到BL指令时,此时的PC应该是0x8,也就是它下面一条指令的地址,会把这个地址0x8保存在LR中,再执行BL指令,将PC设置为跳转目标的地址。最后在DELAY函数执行完毕后,把LR的数值重新写入PC中,返回原来跳转的位置,执行下面的语句
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/bcyy/41409.html