ARM汇编

Cortex-A7处理器有9种处理模式

模式 描述
User(USR) 用户模式,非特权模式,大部分程序运行的时候就处于此模式
FIQ 快速中断模式,进入FIQ中断异常
IRQ 一般中断模式
Supervisor(SVC) 超级管理员模式,特权模式,供操作系统使用
Monitor(MON) 监视模式?这个模式用于安全扩展模式
Abort(ABT) 数据访问终止模式,用于虚拟存储以及存储保护
Hyp(HYP) 超级监视模式?用于虚拟化扩展
Undef(UND) 未定义指令终止模式
System(SYS) 系统模式,用于运行特权级的操作系统任务

除了USR用户模式以外,其他8中模式都是特权模式,这几种模式可以通过软件进行任意切换,也可以通过中断或异常来进行切换。

大多数程序都运行在USR模式下,但是USR模式下不能访问系统所有资源,有些资源是受限的。

USR模式是不能直接进行切换的,只能通过异常来完成模式的切换。

 

每一种异常都有一组寄存器供异常处理程序使用,这样的目的是为了保证在进入异常模式以后,用户模式下的寄存器不会被破环。

 

Cortex-A的内核寄存器组,注意不是芯片的外设寄存器

ARM架构提供了16个32位的通用寄存器(R0-R15)供软件使用,前15个(R0-R14)可以用作通用的数据存储,R15是程序计数器PC,用来保存将要执行的指令。

ARM还提供了一个当前程序状态寄存器CPSR和一个备份程序状态寄存器SPSR,SPSR寄存器就是CPSR寄存器的备份。

 

Cortex-A7有9种运行模式,每一种运行模式都有一组与之对应的寄存器组。

每一种模式可见的寄存器包括15个通用寄存器(R0-R14)、一两个程序状态寄存器和一个程序计数器PC。

各个模式拥有的寄存器如下表

 

 图中前的字体是与User模式所共有的寄存器,蓝绿色背景是各个模式所独有的寄存器。

可以看出在所有模式中,地寄存器组(R0-R7)是共享同一组物理寄存器的,只是一些高寄存器组在不同的模式有自己独有的寄存器。

 

通用寄存器

R0-R15是通用寄存器,通用寄存器分为以下三类:

1.未备份寄存器,即R0-R7

  在所有模式下这8个寄存器都是同一个物理寄存器,在不同的模式下,这8个寄存器中的数据就会被破坏。

2.备份寄存器,即R8-R14

  每个模式有各自独立的寄存器。其中R14(LR)寄存器在ARM中主要有2个作用:

  • 每种处理器模式使用R14(LR)来存放当前子程序的返回地址。

    如果使用BL或者BLX来调用子函数的话,R14(LR)被设置成该子函数的返回地址,在子函数中将R14(LR)中的值赋给R15(PC)即可完成函数返回

    eg:

      MOV PC,LR     @寄存器LR中的值赋值给PC,实现跳转

    或者可以在子函数的入口处将LR入栈:

      PUSH {LR}      @将LR寄存器压栈

    在子函数最后出栈即可

      POP {LR}      

  • 当异常发生以后,该异常模式对应的R14寄存器被设置成该异常模式将要返回的地址,R14寄存器也可以当作普通的寄存器使用

3.程序计数器PC,即R15

   R15保存着当前执行的指令地址加8个字节,这是因为ARM的流水线机制导致的。

  ARM处理器3级流水线:取指->译码->执行,这三级流水线循环执行。

    比如当前执行第一条指令的同时也对第二条指令进行译码,第三条指令也同时被取出来存放在R15(PC)中。

  所以R15指向的是当前正在执行的指令地址再加上2条指令地址。

    对于32位的ARM处理器,每条指令占4个字节

    所以,R15(PC)值 = 当前执行的程序位置 + 8个字节

 

程序状态寄存器CPSR

  所有的处理器模式都共用一个CPSR物理寄存器,因此CPSR可以在任何模式下被访问。

所有的处理器模式都共用一个CPSR(当前程序状态寄存器)必然会导致冲突,为此,除了User和Sys这两个模式以外,其他的7个模式每个都配备了一个专用的物理状态寄存器SPSR(备份程序状态寄存器),

当特定的异常中断发生时,SPSR寄存器用来保存当前程序状态寄存器CPSR的值,当异常退出以后可以用SPSR中保存的值来恢复CPSR。

 

N(bit31):当两个补码表示的 有符号整数运算的时候,N=1 表示运算对的结果为负数,N=0表示结果为正数。

Z(bit30):Z=1 表示运算结果为零,Z=0 表示运算结果不为零,对于CMP 指令,Z=1 表示进行比较的两个数大小相等。

C(bit29):在加法指令中,当结果产生了进位,则C=1,表示无符号数运算发生上溢,其它情况下C=0。在减法指令中,当运算中发生借位,则C=0,表示无符号数运算发生下溢,其它情况下C=1。对于包含移位操作的非加/减法运算指令,C 中包含最后一次溢出的位的数值,对于其它非加/减运算指令,C 位的值通常不受影响。

V(bit28):对于加/减法运算指令,当操作数和运算结果表示为二进制的补码表示的带符号数时,V=1 表示符号位溢出,通常其他位不影响V 位。

Q(bit27):仅ARM v5TE_J 架构支持,表示饱和状态,Q=1 表示累积饱和,Q=0 表示累积不饱和。

IT[1:0](bit26:25):和IT[7:2](bit15:bit10)一起组成IT[7:0],作为IF-THEN 指令执行状态。

J(bit24):仅ARM_v5TE-J 架构支持,J=1 表示处于Jazelle 状态,此位通常和T(bit5)位一起表示当前所使用的指令集,如下表

J T 描述
0 0 ARM
0 1 Thumb
1 1 ThumbEE
1 0 Jazelle

GE[3:0](bit19:16):SIMD指令有效,大于或等。 

IT[7:2](bit15:10):参考 IT[1:0]。
E(bit9):大小端控制位,E=1表示大端模式,E=0表示小端模式。
A(bit8):禁止异步中断位,A=1表示禁止异步中断。
I(bit7):I=1禁止 IRQ,I=0使能 IRQ。
F(bit6):F=1禁止 FIQ,F=0使能 FIQ。
T(bit5):控制指令执行状态,表明本指令是ARM指令还是Thumb指令,通常和J(bit24)一 起表明指令类型,参考J(bit24)位。
M[4:0]:处理器模式控制位,含义如下表

M[4:0] 处理器模式
10000 User模式
10001 FIQ模式
10010 IRQ模式
10011 Supervisor(SVC)模式
10110 Monitor(MON)模式
10111 Abort(ABT)模式
11010 Hyp(HYP)模式
11011 Undef(UND)模式
11111 System(SYS)模式

 

 ARM寄存器CPSR
  https://blog.csdn.net/laviolette/article/details/51376751
ARM条件码
  https://wenku.baidu.com/link?url=esOgjNZWMUGWC2xJfEXygyL9FpjNGc_jdkrleNcYaNcULpuTQBE9hPLRrMJ9YhBSeh2KNGU0brk7DPCD2pW95MMSyMJyZPq4xphwK4LHJDW

 

 

 ARM汇编  

GUN汇编  GUN汇编适合所有的架构

  每条语句有三个可选部分组成

  label: instruction@comment

label即标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址标号也可以用来表示数据地址

  任何以" : "结尾的标识符都可以被识别为一个标号。

instruction即指令,汇编指令或伪指令

  @符号表示后面的时注释

1 add:
2     MOVS R0,#0X12 @设置R0=0x12

ARM中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用

 

用户可以使用.section伪操作来定义一个段,汇编系统预定义了一些段名:

.text 代码段
.data 初始化的数据段
.bss 未初始化的数据段
.rodata 只读数据段

使用.section自己定义段

 .section .testsection@定义一个testsection段

 

汇编程序的默认入口标号是_start      也可以在链接脚本中使用ENTRY来指明其他的入口点

.global _start


_start:
    ldr r0,=0x12    @r0=0x12

上面代码中.global是伪操作,表示_start是一个全局标号

常见伪操作:

.byte 定义单字节数据 比如.byte 0x12
.short 定义双字节数据 比如.short 0x1234
.long 定义一个4字节数据 比如.long 0x12345678
.equ 赋值语句,格式为:.equ 变量名,表达式 比如.equ num,0x12  表示num=0x12
.align 数据字节对齐 比如.align 4  表示4字节对齐
.end 表示源文件结束  
.global 定义一个全局符号,格式为:.global symbol 比如.global_start

 

GUN汇编同样也支持函数,函数格式如下:

函数名:
    函数体
    返回语句    @不是必须的

 

 

 Cortex-A7常用汇编指令

处理器内部数据传输指令

传递数据常见操作:

  1.将数据从一个寄存器传递到另一个寄存器

  2.将数据从一个寄存器传递到特殊寄存器,如CPSR和SPSR寄存器

  3.将立即数传递到寄存器

指令 目的 描述
MOV R0 R1 将R1里面的数据复制到R0中
MRS R0 CPSR 将特殊寄存器CPSR里面的数据复制到R0中
MSR CPSR R1 将R1里面的数据复制到特殊寄存器CPSR中

1.MOV指令

   MOV指令用于将数据从一个寄存器拷贝到另一个寄存器,或者将一个立即数传递到寄存器里面

MOV R0, R1              @将寄存器R1中的数据传递给R0,即R0=R1  
MOV R0, #0x12          @将立即数0x12传递给R0寄存器,即R0=0x12

2.MRS指令

  MRS指令用于特殊寄存器(如CPSR和SPSR)中的数据传递给通用寄存器,要读取特殊寄存器的数据只能使用MRS指令

MRS R0, CPSR            @将特殊寄存器CPSR里面的数据传递给R0,即R0=CPSR

3.MSR指令

  MSR指令和MRS指令刚好相反,MSR指令用来将普通寄存器的数据传递给特殊寄存器,也就是写特殊寄存器,写特殊寄存器只能使用MSR

MSR CPSR,R0            @将R0中的数据复制到CPSR中,即CPSR=R0

 

存储器访问指令

  ARM不能直接访问存储器,比如RAM中的数据。

  有一些寄存器是RAM类型的,我们用汇编来配置寄存器的时候就需要借助存储器访问指令,一般先将要配置的值写入到Rx(x=0~12)寄存器中,然后借助存储器访问指令将Rx中的数据写入到寄存器中,读取就是相反的过程。

指令 描述
LDR Rd, [Rn, #offset] 从存储器Rn+offset的位置读取数据存放到Rd中
STR Rd, [Rn, #offset] 将Rd中的数据写入到存储器中的Rn+offset位置

1.LDR指令

  LDR主要用于从存储器加载数据到寄存器Rx中,LDR也可以将一个立即数加载到寄存器Rx中,LDR加载立即数的时候要使用=,而不是#

  eg:有一个寄存器GPIO1_GDIR,其地址为0x0209C004,读取这个寄存器中的数据方法如下

LDR R0, =0x0209C004                @将寄存器地址0x0209C004加载到R0中,即R0=0x0209C004
LDR R1, [R0]                       @读取地址0x0209C004中的数据到R1寄存器中

2.STR指令

  将数据写入到寄存器中

  eg:配置寄存器GPIO1_GDIR的值为0x20000002,方法如下

LDR R0, =0x0209C004                    @将寄存器地址0x0209C004加载到R0中,即R0=0x0209C004
LDR R1, =0x20000002                    @R1保存要写入到寄存器的值,即R1=0x20000002
STR R1, [R0]                           @将R1中的值写入到R0中所保存的地址中

 LDR和STR都是按照字进行读取和写入的,也就是操作的32为数据,

如果要按照字节、半字节进行操作的话可以在指令LDR后面加上B或H。

eg:按字节操作指令就是LDRB和STRB

  按半字操作的指令就是LDRH和STRH

 

压栈和出栈指令

  PUSH和POP是一种多存储和多加载指令,即可以一次操作多个寄存器数据,他们利用当前的栈指针SP来生成地址

指令 描述 另一种写法
PUSH <reg list> 将寄存器列表存入栈中 STMFD SP!,{R0~R3,R12}  @R0-R3,R12入栈
POP <reg list> 从栈中恢复寄存器列表 LDMFD SP!,{R0~R3,R12}  @R0-R3,R12出栈
PUSH {R0~R3, R12} @将R0-R3和R12压栈

 

栈向下增长

压栈以后的堆栈如图,栈指针SP

 

 再将LR进行压栈后的堆栈模型如图        代码倒着执行

 出栈从栈顶(SP)位置开始

POP {LR}               @先恢复LR   
POP {R0~R3,R12}        @在恢复R0-R3,R12

STMFD SP!,{R0~R3,R12}  @R0-R3,R12入栈
LDMFD SP!,{R0~R3,R12}  @R0-R3,R12出栈

 STM和LDM是多存储和多加载,可以连续的读写存储器中的多个连续数据,LDR和STR每次只能读写存储器中的一个数据

FD:满递减。

 

跳转指令

1.直接使用跳转指令B、BL、BX

2.直接向PC寄存器里面写入数据

指令 描述
B<label> 跳转到label,如果跳转范围超过了+/-2KB,可以使用 B.W<label> 使用32位版本的跳转指令,这样可以得到较大范围的跳转
BX<Rm> 间接跳转,跳转到存放于Rm中的地址处,并且切换指令集
BL<label> 跳转到标号地址,并将返回地址保存在LR中
BLX<Rm> 结合BX和BL的特点,跳转到Rm指定的地址,并将返回地址保存在LR中,切换指令集

1.B指令

  B指令会将PC寄存器的值设置为跳转目标地址,一旦执行B指令,处理器会立即跳转到指定的目标地址。  如果要调用的函数不会在返回到原来的执行处则可以使用B指令

          代码是典型的在汇编中初始化C运行环境,然后跳转到C文件的main函数中运行
_start: ldr sp,
=0X80200000 @设置栈指针 b main @跳转到main函数

2.BL指令

  BL指令相比较B指令,在跳转之前会在寄存器LR(R14)中保存当前PC寄存器值,所以可以通过LR寄存器中的值重新加载到PC中继续运行跳转之前的代码。

          中断服务函数
push {r0, r1} @保存r0,r1 cps #
0x13 @进入SVC模式,允许其他中断再次进入 bl system_irqhandler @加载C语言中断处理函数到r2寄存器中 //c语言中的中断处理函数 cps #0x12 @进入IRQ模式 pop {r0, r1} str r0, 【r1, #0x10】 @中断执行完成,写EOIR

 

算数运算指令

  常用的算数运算指令

指令 计算公式 备注
ADD Rd,Rn,Rm Rd = Rn + Rm 加法运算,指令为ADD
ADD Rd,Rn,#immed Rd = Rn + #immed
ADC Rd,Rn,Rm Rd = Rn + Rm + 进位 带进位的加法运算,指令为ADC
ADC Rd,Rn,#immed Rd = Rn + #immed + 进位

SUB Rd,Rn,Rm

Rd = Rn - Rm

减法
SUB Rd,#immed Rd = Rd - #immed
SUB Rd,Rn,#immed Rd = Rn - #immed
SBC Rd,Rn,#immed Rd = Rn - #immed - 借位  带借位的减法
SBC Rd,Rn,Rm Rd = Rn - Rm - 借位 
MUL Rd,Rn,Rm Rd = Rn * Rm   乘法(32位)
UDIV Rd,Rn,Rm Rd = Rn / Rm  无符号除法 
SDIV Rd,Rn,Rm Rd = Rn / Rm  有符号除法 

逻辑运算指令

  常用的逻辑运算指令

指令 计算公式 备注
AND Rd,Rn Rd = Rd & Rn 按位与
AND Rd,Rn,#immed Rd = Rn & #immed
AND Rd,Rn,Rm Rd = Rn & Rm
ORR Rd,Rn Rd = Rd | Rn 按位或
ORR Rd,Rn,#immed Rd = Rn | #immed
ORR Rd,Rn,Rm Rd = Rn | Rm
BIC Rd,Rn Rd = Rd & (~Rn) 位清除
BIC Rd,Rn,#immed Rd = Rn & (~#immed)
BIC Rd,Rn,Rm Rd = Rn & (~Rm)
ORN Rd,Rn,#immed Rd = Rn | (#immed) 按位或非
ORN Rd,Rn,Rm Rd = Rn | (RM)
EOR Rd,Rn Rd = Rd ^ Rn 按位异或
EOR Rd,Rn,#immed Rd = Rn ^ #immed
EOR Rd,Rn,Rm Rd = Rn ^ Rm

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1.需要汇编初始化一些SOC外设(三星芯片需要关注看门狗)

2.使用汇编初始化DDR,I.MX6U不需要

3.设置SP指针,一般指向DDR,设置好C语言运行环境

在stm32中,汇编定义了中断函数,startup_stm32fxxxxxx.s

 

汇编调用函数如何传参:    中断函数为例

  xxx_irqhandler是一个C语言函数,中断函数有一个参数,参数是中断号。

汇编调用C语言函数时建议形参不要超过4个,形参可以由r0 - r3这四个寄存器来传递

如果形参大于4个,那么多的部分就要使用堆栈进行传递。

所以给r0寄存器写入中断号就可以完成了给xxx_irqhandler函数传递参数

 

pc指向的是正在取值的地址    三级流水线:取值、译指、执行, pc = 当前执行指令地址+8

0x2000 MOV R1,R0  执行

0x2004 MOV R2,R3  译指

0x2008 MOV R4,R5  取值 PC

 

posted @ 2022-03-01 22:22  二人了一  阅读(400)  评论(0编辑  收藏  举报