Linux 学习记录53(ARM篇)
一、内存读写指令
在下图界面中可以搜索指定的内存地址
1. 在C语言中读取内存
例如已知可以访问的地址为:0x12345678
(unsigned int*)0x12345678//将其强制转换为unsigned int类型的地址
*((unsigned int*)0x12345678)//获取0x12345678地址下的数据
2. 指令码及功能
写内存:
1. str:向指定内存中写入一个字的数据
2. strh:向内存中写入半个字的数据
3. strb:向内存中写入一个字节的数据
读内存:
4. ldr:从内存中读取一个字的数据
5. ldrh:从内存中读取半个字的数据
6. ldrb:从内存中读取一个字节的数据
3. 格式
指令码{条件码} 目标寄存器 [目标地址]
例如:
str r1,r0] 将目标寄存器的数值写入到目标地址对应的内存中
ldr r1,[r0] 在目标地址中读取一个字的数据到
4. 使用示例
.text
.global _start
_start:
mov r0,#0x40000000
mov r1,#0xffffffff
str r1,[r0] @将r1中的数据写一个字到0x40000000内存中
stop:
b stop
.end
5. 寻址方式
(1. 前索引方式
.text
.global _start
_start:
mov r0,#0x40000000
mov r1,#0xffffffff
@前索引方式
str r1,[r0,#8] @将r1中的数据写一个字到0x40000000+8的内存中
ldr r2,[r0,#8] @读取r0+8对应的地址内存中的一个字的数据到r2中
stop:
b stop
.end
(2. 后索引方式
.text
.global _start
_start:
mov r0,#0x40000000
mov r1,#0xffffffff
@后索引方式 完成一次存储后r0寄存器会自动偏移8
str r1,[r0],#8 @将r1中的数据写一个字到0x40000000的内存中
stop:
b stop
.end
(3. 自动索引
结合了前两者的索引方式
.text
.global _start
_start:
mov r0,#0x40000000
mov r1,#0xffffffff
@自动索引方式 完成一次存储后r0寄存器会自动偏移8
str r1,[r0,#8]! @将r1中的数据写一个字到0x40000000+8的内存中
stop:
b stop
.end
6.批量寄存器操作指令
(1. 操作码
1. stm 写
2. ldm 读
(2. 格式
stm rm,{寄存器列表}:将寄存器列表中每一个寄存器的数值存放到以rm数值为起始地址的内存中
ldm rm,{寄存器列表}:从rm数值为起始地址的内存中 读取指定数量的数据到寄存器列表中每一个寄存器内
解释:
1.rm:存放操作的内存首地址的寄存器
2.关于寄存器列表中的寄存器的表达方式:
如果寄存器列表中寄存器编号连续,可以用-表示范围 {r7-r11}
如果寄存器列表中寄存器的编号不连续,可以用','分隔 {r7,r8,r9,r10,r11}
3.无论寄存器列表中的寄存器顺序是什么样的,在操作内存时始终是低地址对应小编号寄存器
ex:ldm r6,{r11,r10,r9,r7,r8} 虽然列表中寄存器顺序散乱,但是r7是对应最小的地址数值
(3. 使用示例
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
mov r6,#0x40000000
stm r6,{r1-r5} @将r1-r5寄存器的数据批量存放到内存中
stm r6,{r1,r2,r3,r4,r5} @将r1-r5寄存器的数据批量存放到内存中
ldm r6,{r7-r11} @将内存中的数据批量读取到r7-r1寄存器中
stop:
b stop
.end
(4. 地址增长方式
>1 ia后缀
ia后缀:先往指定的寄存器数值为起始地址中存放数据,然后该寄存器数值自动偏移
例:stmia r6!,{r1-r5}
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
mov r6,#0x40000000
stmia r6!,{r1-r5}
stop:
b stop
.end
>2 ib后缀
ib后缀:先让指定寄存器的数值增大,再存放数据
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
mov r6,#0x40000000
stmib r6!,{r1-r5}
stop:
b stop
.end
可以看到是先偏移在存放数据
>3 da后缀
da后缀:先存放后再向小地址偏移
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
ldr r6,=0x40000800
stmda r6!,{r1-r5}
stop:
b stop
.end
>4 db后缀
db后缀:先偏移后存放
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
ldr r6,=0x40000800
stmdb r6!,{r1-r5}
stop:
b stop
.end
可以看到是先从0800偏移后再存放的数据
二、栈内存的读写
1. 概述
栈指针寄存器(R13 [SP]),sp始终保存栈顶元素的首地址
栈的本质就是一段内存空间,被分出来用于存放一些临时数据,我们可以用过对栈区内存读写来保护现场
2. 栈的类型
1. 增栈:基于栈指针寄存器完成压栈之后,栈指针的数组往地址大的方向增长
2. 减栈:基于栈指针寄存器完成压栈之后,栈指针的数组往地址小的方向增长
3. 空栈:压栈结束后,栈指针寄存器保存的地址内存中没有有效数据(先压栈再增长地址)
4. 满栈:压栈结束后,栈指针寄存器保存的地址内存中存放追后一次压栈的数据(先增长地址,再压栈)
栈的类型可以分为:空增(EA)、空减(ED)、满增(FA)、满减(FD)
ARM处理器默认采用满减栈实现压栈和出栈
3. 满减栈的压栈和出栈实现
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
ldr sp,=0x40000800 @初始化栈
stmdb sp!,{r1-r5} @压栈用db
ldmia sp!,{r7-r11} @出栈用ia
stop:
b stop
.end
======================================
采用满减栈特有后缀 "fd"
.text
.global _start
_start:
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
ldr sp,=0x40000800 @初始化栈
stmfd sp!,{r1-r5} @压栈用db
ldmfd sp!,{r7-r11} @出栈用ia
stop:
b stop
.end
4. 叶子函数和非叶子函数
叶子函数:函数中没有再调用其他函数的函数称为叶子函数
在使用汇编指令跳转时,如果在非叶子函数再次发生跳转时就需要使用通过压栈的方式来对当前函数内现场进行保护,防止数据被覆盖后原有现场被破坏
.text
.global _start
_start:
@栈的初始化
LDR SP,=0X40000800
b main
main:
mov r1,#1
mov r2,#2
bl fun1
add r3,r1,r2
b main
fun1:
@ 压栈 保护现场,非叶子函数内部调用其他函数,lr的数值也会被覆盖,所以需要将它压栈保护
stmfd sp!,{r1,r2,lr}
mov r1,#5
mov r2,#2
bl fun2
sub r4,r1,r2
@出栈恢复现场
ldmfd sp!,{r1,r2,pc}
fun2:
@ 压栈 保护现场
stmfd sp!,{r1,r2}
mov r1,#6
mov r2,#4
mul r5,r1,r2
@出栈恢复现场
ldmfd sp!,{r1,r2}
mov pc,lr @函数返回
stop:
b stop
.end
三、状态寄存器CPSR读写指令
1. 指令码及格式
读:
MRS Rd,CPSR:将CPSR寄存器的数值读取到目标寄存器中
写:
MSR cpsr,操作数:将操作数写道CPSR寄存器中
2. 使用示例
从复位模式切换到USER模式
.text
.global _start
_start:
mrs r0,cpsr @读取CPSR寄存器
bic r0,r0,#0x1F @低5位清零
orr r0,r0,#0x10 @给定数值
msr cpsr,r0 @将修改后的值赋值
stop:
b stop
.end
3. 注意事项
USER模式作为唯一的非特权模式,我们不能直接修改CPSR的值切换至其他特权模式,为例保护系统
想要从USER模式切换到其他模式,需要特定的异常出现,才能切换到对应的模式
四、软中断指令
1. 概念
从软件层次上模拟的一个中断,用于ARM从工作模式从USER模式切换到特权模式
文章来源:https://www.toymoban.com/news/detail-599258.html
2. 指令码和格式
swi 中断号
注意:
1. swi是软中断的指令码
2. 中断号是系统中的中断标识,是由24位数据组成的一个立即数
3. ARM异常处理过程分析
(1. ARM异常源以及异常模式
(5种异常模式对应7种异常源)文章来源地址https://www.toymoban.com/news/detail-599258.html
异常模式 | 异常源 | 解释 |
---|---|---|
FIQ异常模式 | FIQ类型异常源 | 引发程序进入FIQ模式的异常事件 |
IRQ异常模式 | IRQ类型异常源 | 引发程序进入IRQ模式的异常事件 |
SVC异常模式 | 复位信号 | 按键复位/上电复位 |
SVC异常模式 | swi软中断指令 | swi 软中断号 |
undef异常模式 | 未定义异常源 | 译码器翻译指令时,无法编译成功,指令未定义 |
abort异常模式 | data abort | 取数据发生中断 |
abort异常模式 | prefetch abort | 取指令发生中断 |
五种异常工作模式,对应七种异常源
1. 当发生对应类型的异常源时
2. 则处理器会进入到异常的工作模式
3. 执行异常处理程序,完成某个特定的功能
4. 五种异常工作模式,对应七种异常源,异常源优先级
5. 复位的优先级最高
(2. 异常的处理过程分析
**********处理过程*********
保存现场:这个过程是由CPU自动完成(四大步三小步)
1.保存CPSR到SPSR_<MODE>寄存器中
2.修改CPSR寄存器:
1>修改状态位(T位),切换到ARM状态
2>根据需要禁止IRQ和FIQ中断位
3>修改CPSR寄存器中的模式位,切换到对应的异常模式 M[4:0]
3.保存返回地址到LR_<MODE>
4.修改PC指针,指向对应的异常向量表
**************恢复过程***********
1.恢复SPSR_<mode>寄存器中的值,到CPSR寄存器中
2.恢复LR_<mode>寄存器中的值,到PC寄存器中
1)保存现场是CPU自动完成的,当发生异常时,CPU自动完成保存现场过程
2)当修改PC指针,指向异常处理程序的入口时
3)由于异常处理程序的入口地址不固定
4)所以ARM公司设计引入异常向量表
(3. 异常向量表
1.异常向量表是代码段的一块空间,这块空间大小是32字节,被平均分成了8份,每份占用4个字节
2.异常向量表存放7种异常源对应的异常处理函数的跳转指令,有一份保留
3.7种异常源在异常向量表中的位置是固定的,不可以随意更改
4.只需要指定异常向量表的基地址,根据异常源在异常向量表中的偏移地址,就可以确定异常源在异常向量表中的位置
中断向量地址 | 异常中断类型 | 异常中断模式 | 优先级(6最低) |
---|---|---|---|
0x0 | 复位 | 特权模式(SVC) | 1 |
0x4 | 未定义的指令 | 未定义指令中止模式(Undef) | 6 |
0x8 | 软件中断(SWI) | 特权模式(SVC) | 6 |
0x0c | 指令预取中止 | 中止模式 | 5 |
0x10 | 数据访问中止 | 中止模式 | 2 |
0x14 | 保留 | 未使用 | 未使用 |
0x18 | 外部中断请求(IRQ) | 外部中断(IRQ)模式 | 4 |
0x1c | 快速中断请求(FIQ) | 快速(FIQ)中断模式 | 3 |
(4. swi异常处理代码
.text
.global _start
_start:
@初始化异常向量表
b main @复位异常
b . @undef异常
b do_swi @软中断异常
b . @指令中止
b . @数据中止
b . @保留
b . @IRQ异常
b . @FIQ异常
main:
@初始化栈
LDR SP,=0X40000800
@切换到USER模式
MSR CPSR,#0X10
mov r1,#1
mov r2,#2
@触发软中断
swi 1
add r3,r1,r2
b main
do_swi:
@压栈保护现场
STMFD SP!,{R1,R2,LR}
mov r1,#3
mov r2,#5
mul r4,r1,r2
@恢复现场返回主程序执行
LDMFD SP!,{R1,R2,PC}^ @^的作用是修改PC数值的同时将SPSR数值赋值给CPSR
stop:
b stop
.end
到了这里,关于Linux 学习记录53(ARM篇)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!