ARM核介绍

一.ARM核(ARMv7)寄存器资源

在这里插入图片描述

有单独标识的是这个寄存器独有的寄存器资源

(1)寄存器用途分析

  • R0 - R10 用来存放用户的数据
  • R11(fp:frame-pointer) 用来记录一个栈空间的开始地址
  • R12(ip: The Intra-Procedure-call scratch register) 用来临时存储sp
  • R13(sp:stack pointer) 栈指针寄存器
  • R14(lr:link register) 在发生跳转的时候,用来保存PC寄存器的值
  • R15(pc:program counter) 用来存放CPU需要执行的指令所在内存的地址

(2)CPSR(当前程序状态寄存器)

在这里插入图片描述
在这里插入图片描述
ARM核工作模式
在这里插入图片描述

(3)SPSR

异常产生的时候,用来保存CPSR的值

二.ARM指令集

1.ARM编译器介绍

  • 交叉编译工具链的命名规则为:arch [-vendor] [-os] [- (gnu)eabi]
    • arch - 体系架构,如ARM、MIPS等
    • vendor - 工具链提供商
    • os - 目标操作系统
    • eabi - 嵌入式应用二进制接口(Embedded Application Binary Interface)
  • 交叉编译:在PC机上完成代码的编写与编译,在开发板上完成代码的运行
  • Embedded Application Binary Interface:嵌入式应用二进制接口指定了文件格式、数据类型、使用、堆积组织优化和在一个嵌入式软件中的参数的标准约定
  • 根据对操作系统的支持与否,ARM GCC可分为支持和不支持操作系统,如arm-none-eabi:这个是没有操作系统的,自然不可能支持那些跟操作系统关系密切的函数,比如fork(2),它使用的是newlib这个专用于嵌入式系统的C库。arm-none-linux-eabi:用于Linux的,使用Glibc

(1)arm-none-eabi-gcc

Arm 官方用于编译 ARM 架构的裸机系统(包括 ARM Linux 的 boot、kernel,不适用编译 Linux 应用),一般适合 ARM7、Cortex-M 和 Cortex-R 内核的芯片使用,所以不支持那些跟操作系统关系密切的函数,比如 fork (2),它使用的是 newlib 这个专用于嵌入式系统的 C 库。
下载地址:https://developer.arm.com/downloads/-/gnu-rm

(2). arm-none-linux-gnueabi-gcc

- 主要用于基于ARM架构的Linux系统,可用于编译ARM架构的u-boot、Linux内核、Linux应用等。
- arm-none-linux-gnueabi基于GCC,使用Glibc库,经过Codesourcery公司优化过推出的编译器。
- arm-none-linux-gnueabi-xxx交叉编译工具的浮点运算非常优秀。一般ARM9、ARM11、Cortex-A内核,带有Linux操作系统的会用到。

(3) arm-eabi-gcc

- Android ARM编译器。

(4). armcc

- ARM公司推出的编译工具,功能和arm-none-eabi类似,可以编译裸机程序(u-boot、kernel),但是不能编译Linux应用程序。
- armcc一般和ARM一起,Keil MDK、ADS、RVDS和DS-5中的编译器都是armcc,所以armcc编译器都是收费的。

(5). aarch64-linux-gnu-gcc

- aarch64-linux-gnu-gcc是由Linaro公司基于GCC推出的的ARM交叉编译工具。
- 可用于交叉编译ARMv8 64位目标中的裸机程序、u-boot、Linux kernel、filesystem和App应用程序。
- aarch64-linux-gnu-gcc交叉编译器必须安装在64位主机上,才能编译目标代码。

2.指令格式

在这里插入图片描述

(1)立即数

在这里插入图片描述

  • 快速判定是否合法立即数:
    • 首先将这个数转换为32bit的16进制形式,例如218=0xDA=0x000000DA
    • 除零外,仅有一位数为合法立即数0x0010 000
    • 除零外,仅有二位数,并且相邻(包括首尾,如0x1000000A)的为合法立即数。
    • 除零外,仅有三位数,并且相邻(包括中间有0相间,例如0x10800000,包括首尾相邻,如:0x14000003),这三位数中,最高位取值仅能为1、2、3,最低位取值仅能为4、8、C,中间位0x0~0xF。这种组合的为合法立即数。
  • 下面哪些立即数是合法的立即数:
    • (a) 0x00AB0000
    • (b) 0x0000FFFF
    • © 0xF000000F
    • (d) 0x08000012
    • (e) 0x00001f80
    • (f) 0xFFFFFFFF

(2)寄存器移位

  • 将寄存器读取之后,进行移位运算后,作为操作数2参与运算。支持的移位方式如下:
    • LSL(Logical Shift Left):逻辑左移
    • LSR(Logical Shift Right):逻辑右移
    • ASR(Arithmetic Shift Right):算术右移
  • 示例:
    • r0, lsr #4 表示 r0 >> 4
    • r0, lsr r1 表示 r0 >> r1
    • #3, lsl #4 错误,只能是寄存器移位,不能是立即数移位

3.常用ARM核指令

(1)MOV指令

  • 格式:mov 目标寄存器,操作数2
  • 功能:将操作数2的值赋值给目标寄存器
  • 举例:
    • mov r0, #12 // r0 = 12
    • mov r1, r0 // r1 = r0
    • mov r1, r0, lsl #2 // r1 = r0 << r2

(2)MVN指令

  • 格式:mvn 目标寄存器,操作数2
  • 功能:将操作数2取反的值给目标寄存器
  • 举例:
    • MVN r0, #0 // r0 = ~0 => r0 = 0xffff, ffff

(3)LDR指令

  • 格式:LDR 目标寄存器, =数据
  • 功能:完成任意的数据传送到目标寄存器
  • 注意:数据前面不能加#,因为此时数据不按立即数来处理
  • 举例:
    • LDR r0, =0x12345678 // r0 = 0x12345678

(4)ADD指令

  • 格式:add 目标寄存器,操作数1,操作数2
  • 功能:将操作数1加上操作数2的结果给目标寄存器
  • 举例:
    • add r0, r1, #3 // r0 = r1 + 3
    • add r0, r1, r2 // r0 = r1 + r2
    • add r0, r1, r2, lsl #2 // r0 = r1 + (r2 << 2)

(5)SUB指令

  • 格式:sub 目标寄存器,操作数1,操作数2
  • 功能:将操作数1减去操作数2的结果给目标寄存器
  • 举例:
    • sub r0, r1, #3 // r0 = r1 - 3
    • sub r0, r1, r2 // r0 = r1 - r2
    • sub r0, r1, r2, lsl #2 // r0 = r1 - (r2 << 2)

(6)MUL指令

  • 格式:mul 目标寄存器,操作数1,操作数2
  • 功能:将操作数1乘以操作数2的结果存放在目标寄存器
  • 注意:
    • 操作数1和操作数2必须都是寄存器,并且操作数1的寄存器编号不能和目标寄存器一样
  • 举例:
    • mul r0, r1, r2
    • mul r0, r1, #3 // 错误
    • mul r0, r0, r1 // 错误
    • mul r0, r1, r0

(7)AND指令

  • 格式:and 目标寄存器,操作数1,操作数2
  • 功能:将操作数1按位与操作数2的结果存放在目标寄存器
  • 举例:
    • and r0, r1, r2 // r0 = r1 & r2
    • and r0, r1, #10 // r0 = r1 & 10
    • and r0, r1, r2, lsl #2 // r0 = r1 & (r2 << 2)

(8)ORR指令

  • 格式:orr 目标寄存器,操作数1,操作数2
  • 功能:将操作数1按位或操作数2的结果存放在目标寄存器
  • 举例:
    • orr r0, r1, r2 // r0 = r1 | r2
    • orr r0, r1, #10 // r0 = r1 | 10
    • orr r0, r1, r2, lsl #2 // r0 = r1 | (r2 << 2)

(9)EOR指令

  • 格式:eor 目标寄存器,操作数1,操作数2
  • 功能:将操作数1按位异或操作数2的结果存放在目标寄存器
  • 举例:
    • eor r0, r1, r2 // r0 = r1 ^ r2
    • eor r0, r1, #10 // r0 = r1 ^ 10
    • eor r0, r1, r2, lsl #2 // r0 = r1 ^ (r2 << 2)

(10)BIC指令

  • 格式:bic 目标寄存器,操作数1,操作数2
  • 功能:将操作数1按位与操作数2取反的结果存放在目标寄存器
    • 目标寄存器 = 操作数1 & ~操作数2
  • 举例:
    • mov r0, #0xff // r0 = 1111, 1111
    • bic r1, r0, #0xa // -0xa = 0000, 1010
      // & 1111 0101

(11)cmp比较指令

  • 格式:cmp 寄存器,操作数2
  • 功能:将寄存器的值与操作数2比较,比较的结果会自动影响CPSR的NZCV
  • 练习:
    • 将下列代码用ARM汇编指令实现
      a = 5    r0
      b = 6    r1
      c = 10   r2
      
      if(a > b){
          b ++;
      }
      
      if(a <= c){
          c ++;
      }
      
.global _start
_start:
     mov r0,#5
     movr r1,#6
     mov r2,#10
     cmp r0, r1
     addgt r0,r0,#1
     cmp r0,r2
     addle r2,r2,#1
stop:
  b stop

(12)B/BL(跳转指令)

  • 格式:B/BL 标签
  • 功能:跳到一个指定的标签,BL跳转之前,将跳转前的PC的值保存在LR,跳转范围 +/- 32M

(13)给PC赋值

在这里插入图片描述

练习
在这里插入图片描述

.global _start
_start:
   mov r0,#0
   mov r1,#1
   cmp r1,#100
   ble loop
   
stop:
   b stop   

4.内存访问指令

(1)单个数据访问

以下是提取的内容:

  • LDR:将内存中的值加载到寄存器(读内存)
  • STR:将寄存器的内容写入内存(写内存)
  • 寄存器间接寻址:寄存器的值是一个地址
    • LDR r0, [r1] // r0 = *r1
    • STR r0, [r1] // *r1 = r0
  • 基址变址寻址:将基地址寄存器加上指令中给出的偏移量,得到数据存放的地址
    • A. 前索引
      • STR r0, [r1, #4] // *(r1 + 4) = r0
      • LDR r0, [r1, #4] // r0 = *(r1 + 4)
    • B. 后索引
      • STR r0, [r1], #4 // *r1 = r0 && r1 = r1 + 4
      • LDR r0, [r1], #4 // r0 = *r1 && r1 = r1 + 4
    • C. 自动索引
      • STR r0, [r1, #4]! // *(r1 + 4) = r0 && r1 = r1 + 4
      • LDR r0, [r1, #4]! // r0 = *(r1 + 4) && r1 = r1 + 4

在这里插入图片描述

//2
.global _start

_start:
  mov r0,#0x40000000
  ldr r1,=0x1234
  str r1,[r0],#4
  ldr r1,=0xabcd
  str r1,[r0],#-4

   ldr r1,[r0]
   ldr r2,[r0,#4]
   add r0,r1,r2
stop b stop
//1
.global _start

_start:
  mov r0,#0x40000000
  mov r1,#1
  mov r2,#10
  bl write_data

 mov r0,#0x4000000
 ldr r1,=0x40000100
 mov r2,#10
 bl copy_data
stop:
 b stop

write_data:
  str r1,[r0],#4
  add r1,r1,#1
  cmp r1,r2
  ble write_data
mov pc,lr

copy_data:
  ldr r3,[r0],#4
  str r3,[r1],#4
  sub r2,r2,#1
  cmp r2,#0
  bgt copy_data

  mov pc,lr

(2)多个数据访问

  • LDM:将一块内存的数据,加载到多个寄存器中

  • STM:将多个寄存器的值,存储到一块内存

  • 格式:

    • LDM{条件}{s}<MODE> 基址寄存器{!}, {Reglist}^
    • STM{条件}{s}<MODE> 基址寄存器{!}, {Reglist}^

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

_start:
  mov r0 #0x40000000
  mov r1,#0x11
  mov r2,#0x22
  mov r3,#0x33
  stmia r0,{r1-r3}
  ldmdb r0!,{r4-r6}

(3)栈操作指令

  • A. 进栈
    • stmfd sp!, {寄存器列表}
  • B. 出栈
    • ldmfd sp!, {寄存器列表}
  • 注意:
    • 在对栈操作之前,必须先设置sp的值,进栈和出栈的方式一样,ATPCS标准规定满减栈

在这里插入图片描述
在这里插入图片描述

5.CPSR/SPSR操作指令

  • A. 读操作
    • MRS Rn, CPSR/SPSR
    • 将状态寄存器的值,读到通用寄存器中
  • B. 写操作
    • MSR CPSR/SPSR, Rn
    • 将通用寄存器的值,写到状态寄存器
  • 练习:
    • A. 写一段代码,将CPSR的第I(7)位清0,其他位不变(使能IRQ异常)
    • B. 写一段代码,将CPSR的第I(7)位置1,其他位不变(禁用IRQ异常)
.global _start
_start:
   mrs r0,cpsr
   mov r1,#1
   bic r0,r0,r1,lsl #7
   msr cpsr,r0
   
   mrs r0,cpsr
   mov r1,#1
   orr r0,r0,r1,lsl #7
   msr cpsr,r0
_stop:
 b stop

6.指令流水线

  • 在ARM核中,为增加处理器指令流的速度,ARM7系列使用3级流水线,允许多个操作同时处理,而非顺序执行。
  • 不同的ARM核,流水线的级数是不一样的,ARM核版本越高,流水线级数越多
  • 对于软件工程师编程而言,统一按照三级流水线来分析就可以了。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

7.ARM伪指令

在这里插入图片描述

8.ATPCS标准

(1)ATPCS标准介绍

在这里插入图片描述

  • 前四个寄存器r0-r3用于将参数值传递给例程,并将结果值传递给例程,并在例程内保存中间值(但通常仅在子程序调用之间)。在ARM状态下,寄存器r12也称为IP可用于保存子程序调用之间的中间值。
  • 通常,从r4到r11的寄存器用于保存例程局部变量的值。它们也被标记为v1-v8。只有v1-v4可以被整个Thumb指令集统一使用(如图所示)
  • 在过程调用标准的所有变体中,寄存器r12-r15都有特殊角色。在这些角色中,它们被标记为IP、SP、LR和PC(或ip、sp、lr和pc,但本规范使用大写名称来表示特殊角色)。
  • 只有寄存器r0-r7、SP、LR和PC在Thumb状态下普遍可用。它们的同义词和特殊名称显示得更加大胆。很少有Thumb指令可以访问寄存器、v5-v8、SB、SL和IP。

(2)参数传递

  • 函数间传递参数的时候,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数。
  • 在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3;如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。
  • 所以在写C程序进行函数传参的时候,最好不要超过4个参数,这样可以提高效率。

在这里插入图片描述

(3)函数返回值

  • 返回值为一个32位的整数时,可以通过寄存器R0返回。
  • 返回值为一个64位整数时,可以通过R0和R1返回,依此类推。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值