1.2.1.可编程器件的编程原理
电子器件的发展方向
模拟器件 -> 数字器件
ASIC -> 可编程器件
可编程器件的特点
CPU在固定频率的时钟控制下节奏运行。
CPU可以通过总线读取外部存储设备中的二进制指令集,然后解码执行。
这些可以被CPU解码执行的二进制指令集是CPU设计的时候确定的,是CPU的设计者(ARM公司)定义的,本质上是一串由1和0组成的数字。这就是CPU的汇编指令集。
整个编程及运行过程
程序员用汇编指令编程 --经汇编器汇编成二进制可执行程序文件-->二进制文件被CPU读取进去-->CPU内部电路对二进制文件解码-->解码通过则CPU执行指令、完成指令动作。
如果程序员用C语言等高级语言编程,则编译器先将C语言程序编译为汇编程序,再进行上面的后续部分。
从源代码到CPU执行过程
1.2.2.指令集对CPU的意义
1.2.3.RISC和CISC的区别
1.2.4.统一编址&独立编址&哈佛结构&冯诺依曼结构
1.2.5.软件编程控制硬件的关键-寄存器
寄存器的演示
两类寄存器
1、SoC中有2类寄存器:通用寄存器和SFR
2、通用寄存器(ARM中有37个)是CPU的组成部分,CPU的很多活动都需要通用寄存器的支持和参与。
3、SFR(special function register,特殊功能寄存器)不在CPU中,而存在于CPU的外设中,我们通过访问外设的SFR来编程操控这个外设,这就是硬件编程控制的方法。
编程访问寄存器的方法
汇编
- ldr r1, =0xE0200280 #把0xE0200280放到r1这个寄存器中
- str r0, [r1] #把r0中的数放到以r1这个数为地址的内存中去
- mov r0, #0 #把0这个数字放到r0这个寄存器中
- int *p = (int *)0x30008000;
- *p = 16;
1.2.6.ARM体系结构要点总结
1.2.7.S5PV210的内存映射详解
什么是内存映射
1、S5PV210属于ARM Cortex-A8架构,32位CPU,CPU设计时就有32根地址线&32根数据线。2、32根地址线决定了CPU的地址空间为4G,那么这4G空间如何分配使用?这个问题就是内存映射问题(好像准确点应该叫地址映射吧,我懒的改标题了,大家注意这个小坑)。
S5PV210 datasheet中内存映射位置
一些专业术语
ROM:read only memory 只读存储器
RAM:ramdom access memory 随机访问存储器
IROM:internal rom 内部ROM,指的是集成到SoC内部的ROM
IRAM:internal ram 内部RAM,指的是集成到SoC内部的RAM
DRAM:dynamic ram 动态RAM
SRAM:static ram 静态RAM
SROM:static rom? sram and rom?
ONENAND/NAND:
SFR:special function register
1.2.8.CPU和外部存储器的接口
内存 内部存储器 用来运行程序的 RAM 举例(DRAM SRAM DDR)
外存 外部存储器 用来存储东西的 ROM 举例(硬盘 Flash(Nand iNand···· U盘、SSD) 光盘)
CPU连接内存和外存的连接方式不同。内存需要直接地址访问,所以是通过地址总线&数据总线的总线式访问方式连接的(好处是直接访问,随机访问;坏处是占用CPU的地址空间,大小受限);外存是通过CPU的外存接口来连接的(好处是不占用CPU的地址空间,坏处是访问速度没有总线式快,访问时序较复杂)
SoC常用外存:
NorFlash 总线式访问,接到SROM bank,优点是可以直接总线访问,一般用来启动。
NandFlash: 分为SLC和MLC
eMMC/iNand/moviNand eMMC(embeded MMC)iNand是SanDisk公司出产的eMMC,moviNand是三星公司出产的eMMC
oneNAND oneNand是三星公司出的一种Nand
SD卡/TF卡/MMC卡
eSSD
SATA硬盘(机械式访问、磁存储原理、SATA是接口)
外部总线接口(EBI)被用作S5PV210外围。它依赖于内存控制器释放 外部请求外部总线空闲内存控制器时,因为它没有当内存的知识 访问将开始或完成。它使一个SROM控制器、一个OneNAND控制器和一个快闪记忆体 控制器、分享外部内存总线、内存端口0。
S5PV210支持的外部存储器
见datasheet Section2.6 booting sequence
见datasheet Section5全部,memory
见datasheet Section8.7 SD/MMC部分
X210开发板支持的外部存储器
X210有2个版本,Nand版和iNand版,分别使用Nandflash和iNand为外部存储器。我们使用的是iNand版本,板载4GB iNand
S5PV210共支持4个SD/MMC通道,其中 通道0和2依次用作启动。X210开发板中 SD/MMC0通道用于连接板载MMC,因此外部启动时只能使用SD/MMC2通道(注意 通道3不能启动)。见《S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf》中P6
1.2.9.S5PV210的启动过程详解1
- 内存:
- SRAM 静态内存 特点就是容量小、价格高,优点是不需要软件初始化直接上电就能用
- DRAM 动态内存 特点就是容量大、价格低,缺点就是上电后不能直接使用,需要软件初始化后才可以使用。
- 单片机中:内存需求量小,而且希望开发尽量简单,适合全部用SRAM
- 嵌入式系统:内存需求量大,而且没有NorFlash等可启动介质
- PC机: 内存需求量大,而且软件复杂,不在乎DRAM的初始化开销,适合全部用DRAM
- 外存:
- NorFlash:特点是容量小,价格高,优点是可以和CPU直接总线式相连,CPU上电后可以直接读取,所以一般用作启动介质。
- NandFlash(跟硬盘一样):特点是容量大,价格低,缺点是不能总线式访问,也就是说不能上电CPU直接读取,需要CPU先运行一些初始化软件,然后通过时序接口读写。
- 所以一般PC机都是:很小容量的BIOS(NorFlash)+ 很大容量的硬盘(类似于NandFlash)+ 大容量的DRAM
- 一般的单片机: 很小容量的NorFlash + 很小容量的SRAM
- 嵌入式系统:因为NorFlash很贵,随意现在很多嵌入式系统倾向于不用NorFlash,
- 直接用:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM
- S5PV210使用的启动方式是:外接的大容量Nand + 外接大容量DRAM + SoC内置SRAM
- 实际上210的启动还要更好玩一些,210内置了一块96KB大小的SRAM(叫iRAM),同时还有一块内置的64KB大小的NorFlash(叫iROM)。210的启动过程大致是:
- 第一步:CPU上电后先从内部IROM中读取预先设置的代码(BL0),执行。这一段IROM代码首先做了一些基本的初始化(CPU时钟、关看门狗···)(这一段IROM代码是三星出厂前设置的,三星也不知道我们板子上将来接的是什么样的DRAM,因此这一段IROM是不能负责初始化外接的DRAM的,因此这一段代码只能初始化SoC内部的东西);然后这一段代码会判断我们选择的启动模式(我们通过硬件跳线可以更改板子的启动模式),然后从相应的外部存储器去读取第一部分启动代码(BL1,大小为16KB)到内部SRAM。
- 第二步:从IRAM去运行刚上一步读取来的BL1(16KB),然后执行。BL1负责初始化NandFlash,然后将BL2读取到IRAM(剩余的80KB)然后运行
- 第三步:从IRAM运行BL2,BL2初始化DRAM,然后将OS读取到DRAM中,然后启动OS,启动过程结束。
- 思路:因为启动代码的大小是不定的,有些公司可能96kb就够了,有些公司可能1MB都不够。所以刚才说的2步的启动方式不合适。三星的解决方案是:把启动代码分为2半(BL1和BL2),这两部分协同工作来完成启动。
- wakeup status 唤醒状态(复位状态)
1.2.10.S5PV210的启动过程详解2
Second boot support 二次启动
- - When 1stboot mode fails, SD/MMC boot will be tried through SD/MMC channel 2 with 4-bit data
- 当第一次启动模式失败(eMMC0),SD/MMC会偿试通过SD/MMC通道2(4-bit数据线)来启动
BL0做了什么?
- 1. Disable the Watch-Dog Timer
- 关看门狗
- 2. Initialize the instruction cache
- 初始化指令缓存
- 3. Initialize the stack region (see “memory map” on chap 2.5)
- 初始化栈
- 4. Initialize the heap region. (see “memory map” on chap 2.5)
- 初始化堆
- 5. Initialize the Block Device Copy Function. (see “Device Copy Function” on chap 2.7)
- 初始化块设备复制函数device copy functio
- 6. Initialize the PLL and Set system clock. (see “clock configuration” on chap 2.11)
- 设置SoC时钟系统
- 7. Copy the BL1 to the internal SRAM region (see “Device Copy Function” on chap 2.7)
- 复制BL1到内部IRAM(16KB)
- 8. Verify the checksum of BL1.
- If checksum fails, iROM will try the second boot up. (SD/MMC channel 2)
- 检查BL1的校验和
- 9. Check if it is secure-boot mode or not.
- If the security key value is written in S5PV210, It’s secure-boot mode.
- If it is secure-boot mode, verify the integrity of BL1.
- 检查是否是安全启动
- 10. Jump to the start address of BL1
- 跳转到BL1去执行
S5PV210的所有启动
先1st启动,通过OMpin选择启动介质再2nd启动,从SD2
再Uart启动
再USB启动
1.2.11.如何在开发板上选择不同启动方式
1.2.12.ARM的编程模式和7种模式
ARM的基本设定
ARM 采用的是32位架构.
ARM 约定:
Byte : 8 bits
Halfword :16 bits (2 byte)
Word : 32 bits (4 byte)
大部分ARM core 提供:
ARM 指令集(32-bit)
Thumb 指令集(16-bit )
Thumb2指令集(16 & 32bit)
Jazelle cores 支持 Java bytecode
ARM 约定:
Byte : 8 bits
Halfword :16 bits (2 byte)
Word : 32 bits (4 byte)
大部分ARM core 提供:
ARM 指令集(32-bit)
Thumb 指令集(16-bit )
Thumb2指令集(16 & 32bit)
Jazelle cores 支持 Java bytecode
ARM处理器工作模式
ARM 有7个基本工作模式:
User : 非特权模式,大部分任务执行在这种模式
FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式
IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式
Supervisor :当复位或软中断指令执行时将会进入这种模式
Abort : 当存取异常时将会进入这种模式
Undef : 当执行未定义指令时会进入这种模式
System : 使用和User模式相同寄存器集的特权模式
注意
除User(用户模式)是Normal(普通模式)外,其他6种都是Privilege(特权模式)。
Privilege中除Sys模式外,其余5种为异常模式。
各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是CPU在某些情况下自动切换。
各种模式下权限和可以访问的寄存器不同。
CPU为什么设计这些模式?
CPU是硬件,OS是软件,软件的设计要依赖硬件的特性,硬件的设计要考虑软件需要,便于实现软件特性。
操作系统有安全级别要求,因此CPU设计多种模式是为了方便操作系统的多种角色安全等级需要。
1.2.13.ARM的37个寄存器详解
1、ARM总共有37个寄存器,但是每种模式下最多只能看到18个寄存器,其他寄存器虽然名字相同但是在当前模式不可见。
2、对r13这个名字来说,在ARM中共有6个名叫r13(又叫sp)的寄存器,但是在每种特定处理器模式下,只有一个r13是当前可见的,其他的r13必须切换到他的对应模式下才能看到。这种设计叫影子寄存器(banked register)
总结
ARM共有37个寄存器,都是32位长度
37个寄存器中30个为“通用”型,1个固定用作PC,一个固定用作CPSR,5个固定用作5种异常模式下的SPSR。
CPSR程序状态寄存器
条件位:
N = Negative result from ALU
Z = Zero result from ALU
C = ALU operation Carried out
V = ALU operation oVerflowed
N = Negative result from ALU
Z = Zero result from ALU
C = ALU operation Carried out
V = ALU operation oVerflowed
Q 位:
仅ARM 5TE/J架构支持
指示饱和状态
J 位
仅ARM 5TE/J架构 支持
J = 1: 处理器处于 Jazelle状态
J = 1: 处理器处于 Jazelle状态
中断禁止位:
I = 1: 禁止 IRQ.
F = 1: 禁止 FIQ.
I = 1: 禁止 IRQ.
F = 1: 禁止 FIQ.
T Bit
仅ARM xT架构支持
T = 0: 处理器处于 ARM 状态
T = 1: 处理器处于 Thumb 状态
Mode位:
处理器模式位
- 判断3和3是否相等,也就是3-3是否等于0,
- 如果等于0,Z位就会置1,然后就会跳转,
- 并且跳转前把返回地址存到lr寄存器中
- sub 3 3
- BLE : branch lr equal
注意
CPSR中各个bit位表明了CPU的某些状态信息,这些信息非常重要,和后面学到的汇编指令息息相关(譬如BLE指令中的E就和CPSR中的Z标志位有关)
CPSR中的I、F位和开中断、关中断有关
CPSR中的mode位(bit4~bit0共5位)决定了CPU的工作模式,在uboot代码中会使用汇编进行设置。
PC(r15)程序控制寄存器
PC(Program control register)为程序指针,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)
整个CPU中只有一个PC(CPSR也只有一个,但SPSR有5个)。
1.2.14.ARM的异常处理方式简单介绍
什么是异常
正常工作之外的流程都叫异常
异常会打断正在执行的工作,并且一般我们希望异常处理完成后继续回来执行原来的工作
中断是异常的一种
异常向量表
所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的。
当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作)
异常向量表是硬件向软件提供的处理异常的支持。
- 同步:靠中断来实现(一种特殊的异常)
- 异步:同步时钟
ARM的异常处理机制
1、当异常产生时, ARM core:
(1)拷贝 CPSR 到 SPSR_<mode> 保护当前状态
(2)设置适当的 CPSR 位: CPU自动的
改变处理器状态进入 ARM 态
改变处理器模式进入相应的异常模式
设置中断禁止位禁止相应中断 (如果需要)
(3)保存返回地址到 LR_<mode>
(4)设置 PC 为相应的异常向量
2、返回时, 异常处理需要:
(1)从 SPSR_<mode>恢复CPSR
(2)从LR_<mode>恢复PC
(3)Note:这些操作只能在 ARM 态执行.
总结
异常处理中有一些是硬件自动做的,有一些是程序员需要自己做的。需要搞清楚哪些是需要自己做的,才知道如何写代码。
以上说的是CPU设计时提供的异常向量表,一般成为一级向量表。有些CPU为了支持多个中断,还会提供二级中断向量表,处理思路类似于这里说的一级中断向量表。
1.2.15_16.ARM汇编指令集1_2
两个概念:指令与伪指令
指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。
伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。
两种不同风格的ARM指令
ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]
GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]
ARM汇编特点1:LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现 ARM CPU和内存数据交换
ARM汇编特点2:8种寻址方式
寄存器寻址
mov r1, r2
把r2中的内容放到r1中
立即寻址 mov r0, #0xFF00 把0xFF00直接放到r0中,前面加#表示这是一个数字
寄存器移位寻址 mov r0, r1, lsl #3 把r1左移3位得到的数再给r0
寄存器间接寻址 ldr r1, [r2] 把r2这个内存地址中的值赋给r1,相当于指针
基址变址寻址 ldr r1, [r2, #4] 把r2+4之后这个地址中的值赋给r1
多寄存器寻址 ldmia r1!, {r2-r7, r12} 把r1理解成数组 ,r1就是数组名即数组头地址,然后把r1开始往后的7个元素读到r2~r7,r12寄存器中
堆栈寻址 stmfd sp!, {r2-r7, lr} 压栈,把 r2-r7,lr7个寄存器存储到sp起始地址并一直往下压直到结束
相对寻址 beq flag
flag: 冒号表示标号,用来标示后面一句指令的地址的,一般表示入口地址,类似于函数名等,有了标号,就可以跳转到标号处去执行
相对寻址:当前PC执行到哪,然后跳转以PC为基地址+偏移量的地方去执行
ARM汇编特点3:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B(byte)功能不变,操作长度变为8位
H(half word)功能不变,长度变为16位
S(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
S(S标志)功能不变,影响CPSR标志位
如 mov和movs
movs r0, #0
ARM汇编特点4:条件执行后缀
- mov r0, r1 @ 相当于C语言中的r0 = r1;
- moveq r0, r1 @ 如果eq后缀成立,则直接执行mov r0, r1;如果eq不成立则本句代码直接作废,相当于没有
- @ 类似于C语言中 if (eq){r0 = r1;}
- 条件后缀执行注意2点:
- 1、条件后缀是否成立,不是取决于本句代码,而是取决于这句代码之前的代码运行后的结果。
- 2、条件后缀决定了本句代码是否被执行,而不会影响上一句和下一句代码是否被执行。
- GT greater than
- LT less than
ARM汇编特点5:多级指令流水线
为增加处理器指令流的速度,ARM使用多级流水线.,下图为3级流水线工作原理示意图。(S5PV210使用13级流水线,ARM11为8级)允许多个操作同时处理,而非顺序执行。
PC指向正被取指的指令,而非正在执行的指令
1.2.17.ARM汇编指令集3
常用ARM指令1:数据处理指令
数据传输指令
mov mvn
- mov(move) mov r1, r0 @两个寄存器之间数据传递
- mov r1, #0xff @ 将立即数赋值给寄存器
- mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
- 按位取反的含义:
- 譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff 但是我mvn r0, r1后,r0=0xffffff00
逻辑指令
and orr eor bic
- and 逻辑与
- orr 逻辑或
- eor 裸机异或
- bic 位清除指令
- bic r0,r1,#0x1f @ 将r1中的数的bit0到bit4清零后赋值给r0 0x1f = 0x0000001f=0x0000```11111
比较指令 cmp cmn tst teq
- cmp cmp r0, r1 等价于 sub r2, r0, r1 (r2 = r0 - r1)
- cmn cmn r0, r1 等价于 add r0, r1
- tst tst r0, #0xf @测试r0的bit0~bit3是否全为0
- teq
- 比较指令用来比较2个寄存器中的数
- 注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
乘法指令 mvl mla umull umlal smull smlal
前导零计数
clz
常用ARM指令2:cpsr访问指令
mrs & msr
mrs用来读psr,msr用来写psr
mrs用来读psr,msr用来写psr
CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr。
- cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,
- 作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。
常用ARM指令3:跳转(分支)指令
b & bl & bxb 直接跳转(就没打开算返回)
bl branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用
bx跳转同时切换到ARM模式,一般用于异常处理的跳转。
常用ARM指令4:访存指令
ldr/str &
ldm/stm & swp
单个字/半字/字节访问 ldr/str
单个字/半字/字节访问 ldr/str
多字批量访问 ldm/stm
swp r1, r2, [r0] 把r0这个内存地址中的值读到r1,然后把r2放到r0这个内存地址中
swp r1, r1, [r0]
ARM汇编中的立即数
合法立即数与非法立即数
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。
合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数
- 合法立即数: 0x000000ff 0x00ff0000 0xf000000f
- 非法立即数: 0x000001ff
常用ARM指令5:软中断指令
swi(software interrupt)软中断指令用来实现操作系统中系统调用
1.2.18.ARM汇编指令集4
协处理器cp15操作指令
mcr & mrc
mrc用于读取CP15中的寄存器
mrc用于读取CP15中的寄存器
mcr用于写入CP15中的寄存器
什么是协处理器
SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。
ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)
协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。
MRC & MCR的使用方法
mcr{<cond>} p15, <opcode_1>, <Rd>, <Crn>, <Crm>, {<opcode_2>}
opcode_1:对于cp15永远为0
Rd:ARM的普通寄存器
Crn:cp15的寄存器,合法值是c0~c15
Crm:cp15的寄存器,一般均设为c0
opcode_2:一般省略或为0
举例(来自于uboot)
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
其他见uboot源码start.S中相关代码
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
其他见uboot源码start.S中相关代码
协处理器学习要点
不必深究,将uboot中和kernel中起始代码中的一般操作搞明白即可。
只看一般用法,不详细区分参数细节,否则会陷入很多复杂未知中。
关键在于理解,而不在于记住。
1.2.19.ARM汇编指令集5
为什么需要多寄存器访问指令
ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm
ldm(load register mutiple)
stm(store register mutiple)
举例(uboot start.S 537行)
stmia
sp, {r0 - r12}
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写
将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
一个访存周期同时完成13个寄存器的读写
8种后缀
ia(increase after)先传输,再地址+4
ib(increase before)先地址+4,再传输
da(decrease after)先传输,再地址-4
db(decrease before)先地址-4,再传输
fd(full decrease)满递减堆栈
ed(empty decrease)空递减堆栈
fa(·······) 满递增堆栈
ea(·······)空递增堆栈
8种后缀
ia(increase after)先传输,再地址+4
ib(increase before)先地址+4,再传输
da(decrease after)先传输,再地址-4
db(decrease before)先地址-4,再传输
fd(full decrease)满递减堆栈
ed(empty decrease)空递减堆栈
fa(·······) 满递增堆栈
ea(·······)空递增堆栈
四种栈
空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
增栈:栈指针移动时向地址增加的方向移动的栈
减栈:栈指针移动时向地址减小的方向移动的栈
!的作用
ldmia
r0, {r2 - r3}
ldmia r0!, {r2 - r3}
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
ldmia r0!, {r2 - r3}
感叹号的作用就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。
^的作用
ldmfd
sp!, {r0 - r6, pc}
ldmfd sp!, {r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
ldmfd sp!, {r0 - r6, pc}^
^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。
总结
批量读取或写入内存时要用ldm/stm指令
各种后缀以理解为主,不需记忆,最常见的是stmia和stmfd
谨记:操作栈时使用相同的后缀就不会出错,不管是满栈还是空栈、增栈还是减栈
1.2.20.ARM汇编伪指令
伪指令的意义
伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。
伪指令的意义在于指导编译过程。
伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。
gnu汇编中的一些符号
@ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似
# 做注释,一般放在行首,表示这一行都是注释而不是代码。
:以冒号结尾的是标号
. 点号在gnu汇编中表示当前指令的地址
# 立即数前面要加#或$,表示这是个立即数
常用gnu伪指令
.global _start
@ 给_start外部链接属性
.section .text @ 指定当前段为代码段
.ascii .byte .short .long .word
.quad .float .string @ 定义数据
.align 4
@ 以16字节对齐
.balignl 16 0xabcdefgh @ 16字节对齐填充
.equ
@ 类似于C中宏定义
偶尔会用到的gnu伪指令
.end
@标识文件结束
.include @ 头文件包含
.arm / .code32 @声明以下为arm指令
.thumb / .code16 @声明以下为thubm指令
最重要的几个伪指令
ldr
大范围的地址加载指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令
adr 小范围的地址加载指令
adrl 中等范围的地址加载指令
nop 空操作
ARM中有一个ldr指令,还有一个ldr伪指令
一般都使用ldr伪指令而不用ldr指令
adr与ldr
adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理;
adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里
ldr加载的地址和链接时给定的地址有关,由链接脚本决定。