LV01-04-ARM汇编-01-ARM汇编基础
本文主要ARM汇编基础知识的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows版本 | windows11 |
Ubuntu版本 | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) |
Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03) |
linux内核 | linux-4.15(NXP官方提供) |
Win32DiskImager | Win32DiskImager v1.0 |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
https://elixir.bootlin.com/linux/latest/source | 在线阅读linux kernel源码 | |
nxp-imx/linux-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP linux内核仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.1.15_2.1.0_ga | NXP u-boot仓库tags中的rel_imx_4.1.15_2.1.0_ga | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) | |
ARM | Cortex-A7 MPCore Technical Reference Manual | Cortex-A7 MPCore技术参考手册 |
ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition | ARM架构参考手册ARMv7-A和ARMv7-R版 | |
Arm Generic Interrupt Controller Architecture Specification- version 3 and version 4 | Arm通用中断控制器架构规范-版本3和版本4 | |
ARM Generic Interrupt Controller Architecture Specification - Version 2.0 | Arm通用中断控制器架构规范-版本2.0 | |
ARM Cortex-A Series Programmer's Guide for ARMv7-A | Cortex-A系列ARMv7-A编程指南 |
汇编相关可以参考《ARM® Architecture Reference Manual ARMv7-A and ARMv7-R edition 》的Chapter A5 ARM Instruction Set Encoding 部分。
一、CISC和RISC
根据指令复杂度来区分,所有 CPU 可以分为 2 类:
- CISC 复杂指令集计算机, Complex Instruction Set Computer,比如x86
- RISC 精简指令集计算机, Reduced Instruction Set Computing,比如ARM, RISC-V
比如,对于加法运算: a = a + b,它涉及 4 个步骤的操作:读出 a,读出b,计算 a+b,把结果写回 a。
(1)使用 CISC(复杂指令集计算机,比如 x86)提供的加法指令:只需要一条指令即可完成这 4 步操作。当然,这一个指令需要多个 CPU 周期才可以完成。
(2)而 RISC 不提供“一站式”的加法指令:需调用四条指令完成两数相加:内存 a 加载到寄存器,内存 b 加载到寄存器,两个寄存器中数相加,寄存器结果存入内存 a。
ARM 芯片属于精简指令集计算机(RISC: Reduced Instruction SetComputing),它所用的指令比较简单,有如下特点:
(1)对内存只有读、写指令。
(2)对于数据的运算是在 CPU 内部实现。
(3)使用 RISC 指令的 CPU 复杂度小一点,易于设计 。
二、为什么要学习汇编?
为什么要学ARM汇编?
我们在学习 STM32的时候几乎没有用到过汇编,但是我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编,对于 Cortex-A 芯片来讲,大部分芯片在上电以后 C 语言环境还没准备好,所以第一行程序肯定是汇编的,至于要写多少汇编程序,那就看我们能在哪一步把 C 语言环境准备好。
所谓的 C 语言环境就是保证 C 语言能够正常运行。 C 语言中的函数调用涉及到出栈入栈,出栈入栈就要对堆栈进行操作,所谓的堆栈其实就是一段内存,这段内存比较特殊,由 SP 指针访问, SP 指针指向栈顶。芯片一上电 SP 指针还没有初始化,所以 C 语言没法运行,对于有些芯片还需要初始化 DDR ,因为芯片本身没有 RAM ,或者内部 RAM 不开放给用户使用,用户代码需要在 DDR 中运行,因此一开始要用汇编来初始化 DDR 控制器。后面学习 Uboot 和 Linux 内核的时候汇编是必须要会的。
官方文档?
我们在 ARM 的官网上可以找到很多的芯片手册以及编程指南,关于这篇笔记中的 ARM 指令,我们可以参考这篇文档:《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》的 A4 章节,我们可以在这一章节系统的了解到 Cotex-A7 的所有汇编指令。我使用的开发板使用的 Cotex-A 系列的 ARM 处理器,所以这份文档就可以了。
三、 GNU 汇编语法
1. 伪指令和伪操作
1.1 伪指令
伪指令( Pseudo Instruction )是用于对汇编过程进行控制的指令,该类指令并不是可执行指令,没有机器代码,只用于汇编过程中为汇编程序提供汇编信息。例如,提供如下信息:哪些是指令、哪些是数据及数据的字长、程序的起始地址和结束地址等。伪指令有 2 个特点:
(1)由于是伪“指令”,因而它只存在于汇编语言中。高级语言中不叫指令,叫语句;
(2)由于是“伪”指令,也即“假”指令,因而不是可执行指令,不会产生机器代码,不会占用 ROM 空间,只用于汇编过程中为汇编程序提供汇编信息。
1.2 伪操作
汇编语言程序语句除指令以外还可以由伪操作和宏指令组成。伪操作不像机器指令那样是在程序运行期间由 CPU 来执行的,它是在汇编程序对源程序汇编期间由汇编程序处理的操作,它们可以完成如数据定义、分配存储区、指示程序结束等功能。
2. 汇编语法格式
GNU 汇编语法适用于所有的架构,并不是 ARM 独享的, GNU 汇编由一系列的语句组成,每行一条语句,每条语句有三个可选部分:
1 | label: instruction @ comment |
【语句说明】
label :标号,表示地址位置,有些指令前面可能会有标号,这样就可以通过这个标号得到指令的地址,标号也可以用来表示数据地址。注意 label后面的 : ,任何以 : 结尾的标识符都会被识别为 一个标号。
instruction :指令,也就是汇编指令或伪指令。
@ :表示后面的是注释,就跟 C 语言里面的 “ /* “和 ” */ “ 一样,其实在 GNU 汇编文件中我们也可以使用” /* “和” */ “来注释。
comment :就是注释的内容了。
【注意】
(1) ARM 中的指令、伪指令、伪操作、寄存器名等可以全部使用大写,也可以全部使用小写,但是不能大小写混用。
(2)汇编指令每行结束不需要带 ; 或者其他任何符号。
3. 预定义段
我们可以使用以下伪操作来定义一个段:
1 | .section .UserSection @定义一个 UserSection段 |
在汇编系统中预定义了一些段名:
.text | 表示代码段。 |
.data | 初始化的数据段。 |
.bss | 未初始化的数据段。 |
.rodata | 只读数据段。 |
4. 汇编的开始与结束
汇编程序的默认入口标号是 _start ,不过我们也可以在链接脚本中使用 ENTRY 来指明其它的入口点,一般还是使用 _start 作为入口标号,下边就是一个完整的 GNU 汇编程序开始与结束的格式:
1 | .text @ 表示当前段为代码段 |
5. GNU 函数语法
5.1 函数语法
在汇编中同时也支持函数的定义使用,一般定义格式如下:
1 | 函数名 : |
【注意】 GNU 汇编函数返回语句不是必须的。
5.2 使用实例
点击查看实例
1 | /* 未定义中断 */ |
四、汇编与机器码
1. 从一个例子开始
前面我们有这么一张图:
数值 a 原来是保存在内存里的,执行了某条指令后,它的值被读入内存,那问题来了:
- (1)什么指令,可以让 CPU 从内存里把数据读进来?
例如:
1 | mov r3, #addr_a @把变量 a 的地址传给 CPU 寄存器 r3 |
- (2)读进来后,这个数保存在哪里?
保存在 CPU 内部,存在某个寄存器里,上面的代码用寄存器 r0 来保存该值。
- (3)如何处理数据?
CPU 执行加法指令,比如:
1 | add r0, r0, r1 @在 CPU 内部, r0=r0+r1 |
- (4)最终数据怎么写入内存?
CPU 执行指令,比如 :
1 | str r0, [r3] @将 r0 的值写入 r3 所指的内存 |
2. 指令是什么?
上面例子中, mov、 add、 ldr、 str 等都是汇编指令,或者说它们是“助记符”──也就是帮助我们记忆的。这些指令其实是一个一个数值,我们去记这些数值有难度,所以就用 mov、 add 表示不同指令对应的数值。在下面的表格中, op1 对应bit[27:25],不同的 op1 对应不同的指令。我们就用 mov、 add等等来表示这些值。
需要注意的是, op1 只是指令,它只占据 3 位(bit[27:25]),一条完整的指令还需要更多参数。比如“ mov r1, r0”里面的 r1、 r0 就是参数。这些参数也是保存在同一个 32 位数里。这个 32 位数值就是机器码,即汇编指令是机器码的助记符。 ARM 指令机器码是有一定格式(对于ARM
的Cotex-A7
系列的指令编码格式可以看这篇文档《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》的A5
章节),如下:
举个例子?我们后边编译完程序,可能会得到反汇编文件,里边将会包含机器码与汇编指令的对应关系,如:
3. 汇编的执行
这里举两个例子,一个是MOV,一个是BL。
3.1 MOV指令
我们以MOV指令为例,MOV 指令: Move register or constant,把某个寄存器的值移给另一个寄存器,或是把一个常数移入寄存器。 有如下汇编指令:
1 | mov r1, #10 @ 将 10 赋值给寄存器 r1,即 r1=10 |
执行过程如下:
(1)取指: 假设从内存的 addrA 地址取机器码 e3a0100a(即 mov r1, #10 指令)
(2)译码: 原来是 MOV 指令
(3)执行: CPU 内部寄存器 R1 等于 10
机器码 e3a0100a, MOV 指令各位的解析如下:
[31:28]位是条件码 0xe; [15: 12]位是寄存器 R1,即 0x1; [12:0]位是立即数(前边学习另一个课程的时候起始学过了) 10, 即 0x00a
3.2 BL指令
BL 指令: Branch with Link,跳转并把返回地址记录在 LR 寄存器里。 例如
1 | bl test_tag |
第 1 行,跳转到 test_tag 标签处执行“mov r3, #0”指令,并且将下一条指令即“mov r1, #10”指令的地址存储到 LR 寄存器。
第 6 行,返回到“ mov r1, #10”指令地址,并且执行“ mov r1, #10”指令指令执行过程,如下:
(1)CPU 从内存的 addrA 地址取机器码 eb000000(即“bl test_tag”指令),执行后, PC 跳转到 test_tag 标签位置,即内存的 addrA+8 地址,从上图可知,其实 test_tag 标签的地址是“mov r3, #0”指令的地址。同时自动将内存的 addrA+4 地址存储在寄存器 LR 中。
(2)CPU 从内存的 addrA+8 地址取机器码 e3a03000(即“mov r3, #0”指令),执行, CPU 内部寄存器 R3 等于 0。
(3)CPU 从内存的 addrA+12 地址取机器码 e1a0f00e(即“mov pc, lr”指令),执行, PC 跳转到内存的 addrA+4 地址。
(4)CPU 从内存的 addrA+4 地址取机器码 e3a0100a(即“mov r1, #10”指令),执行, CPU 内部寄存器 R1 等于 10其中,机器码 eb000000, BL 指令各位的解析可以查《ARM Architecture Reference Manual ARMv7-A and ARMv7-R edition》的A5
章节。