1. 向内存中写:
str{条件码} 目标寄存器,[目标地址] : 将目标寄存器的4字节数值写入到目标地址为首地址的空间中
strh{条件码} 目标寄存器,[目标地址] : 将目标寄存器的2字节数值写入到目标地址为首地址的空间中
strb{条件码} 目标寄存器,[目标地址] : 将目标寄存器的1字节数值写入到目标地址为首地址的空间中
2. 从内存中读:
ldr{条件码} 目标寄存器,[目标地址] : 从目标地址为首地址的空间中读取4字节数据存放在目标寄存器中
ldrh{条件码} 目标寄存器,[目标地址] : 从目标地址为首地址的空间中读取2字节数据存放在目标寄存器中
ldrb{条件码} 目标寄存器,[目标地址] : 从目标地址为首地址的空间中读取1字节数据存放在目标寄存器中
.text
.global start
_start:
mov r0,#0XFFFFFFFE
mov r1,#0X40000000 @R1保存内存地址
@将r0的值写入到r1对应的地址空间
str r0,[r1]
@从r1对应的地址空间中读取数据保存在r2中
ldr r2,[r1]
wh:
b wh
.end
str{条件码} 目标寄存器,[目标地址,#立即数]
//将目标寄存器的数据保存在目标地址+立即数为起始地址的内存中
ldr{条件码} 目标寄存器,[目标地址,#立即数]
//从目标地址+立即数为起始地址的内存中读取数据保存在目标寄存器
str{条件码} 目标寄存器,[目标地址],#立即数
//将目标寄存器的数据保存在目标地址为起始地址的内存中,接着目标地址自加立即数大小
ldr{条件码} 目标寄存器,[目标地址],#立即数
//从目标地址为起始地址的内存中读取数据保存在目标寄存器,接着目标地址自加立即数大小
str{条件码} 目标寄存器,[目标地址,#立即数]!
//将目标寄存器的数据保存在目标地址+立即数为起始地址的内存中,接着目标地址自加立即数大小
ldr{条件码} 目标寄存器,[目标地址,#立即数]!
//从目标地址+立即数大小为起始地址的内存中读取数据保存在目标寄存器,接着目标地址自加立即数大小
向内存写:
stm 目标地址,{目标寄存器列表}
将列表中各个寄存器的数值保存在目标地址对应的地址空间中
从内存读:
ldm 目标地址,{目标寄存器列表}
从目标地址对应的地址空间中拿数据保存到寄存器列表中各个寄存器中
注意:
1. 寄存器列表中每一个寄存器之间用','分隔,
如果寄存器列表中寄存器的编号连续,那么可以用-表示一定范围内的寄存器,比如 {r1-r5}
2. 无论寄存器列表中的寄存器表现形式如何,在存取数据时始终是小编号寄存器对应低地址
.text
.global start
_start:
mov r0,#0X40000000
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
@向内存中写
@stm r0,{r2,r1,r4,r3,r5}
stm r0,{r1-r5}
@从内存中读取数据
ldm r0,{r6-r10}
wh:
b wh
.end
.text
.global start
_start:
mov r0,#0X40000000
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
@向内存中写
stm r0!,{r1-r5}
wh:
b wh
.end
增栈 : 每次压栈结束,SP保存的栈顶地址往高地址方向增栈
减栈 : 每次压栈结束,SP保存的栈顶地址往低地址方向增栈
空栈 : 压栈结束后,SP保存的栈顶空间中没有有效数据
满栈 : 压栈结束后,SP保存的栈顶空间中有有效数据
其本质就是先改变指向还是先存放数据
空增栈(EA)/空减栈(ED)/满增栈(FA)/满减栈(FD)
E : empty 空 F : full 满
A : addition 增加 D : delete 减少
当前ARM处理器使用的是哪种栈?满减栈
.text
.global start
_start:
@初始化栈
ldr SP,=0X40000020
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
@压栈
stmdb sp!,{r1-r5}
@出栈
ldmia sp!,{r6-r10}
wh:
b wh
.end
.text
.global start
_start:
@初始化栈
ldr SP,=0X40000020
mov r1,#1
mov r2,#2
mov r3,#3
mov r4,#4
mov r5,#5
@压栈
stmfd sp!,{r1-r5}
@出栈
ldmfd sp!,{r6-r10}
wh:
b wh
.end
如:
.text
.global _start
_start:
@初始化栈
ldr SP,=0X40000020
b main
main:
mov r1,#3
mov r2,#4
bl fun1
add r3,r1,r2
b main
fun1:
@压栈保护现场
stmfd sp!,{r1,r2}
mov r1,#7
mov r2,#9
sub r4,r2,r1
@出栈恢复现场
ldmfd sp!,{r1,r2}
mov pc,lr @程序返回
.end
.text
.global _start
_start:
@初始化栈
ldr SP,=0X40000020
b main
main:
mov r1,#3
mov r2,#4
bl fun1
add r3,r1,r2
b main
fun1:
@压栈保护现场
stmfd sp!,{r1,r2,lr}
mov r1,#7
mov r2,#9
bl fun2
sub r4,r2,r1
@出栈恢复现场
ldmfd sp!,{r1,r2,lr}
mov pc,lr @程序返回
fun2:
stmfd sp!,{r1,r2}
mov r1,#4
mov r2,#8
mul r4,r2,r1
@出栈恢复现场
ldmfd sp!,{r1,r2}
mov pc,lr @程序返回
.end
格式:
msr CPSR,第一操作数
将第一操作数的数值写入到CPSR寄存器中
mrs 目标寄存器,CPSR
读取CPSR数值保存到目标寄存器中
.text
.global _start
_start:
mrs r1,CPSR @读取CPSR数值
@切换到USR模式,取消FIQ和IRQ禁止
msr CPSR,#0x10
.end
注意:
user模式是ARM处理器工作模式中唯一的非特权模式,
这种模式下无法通过手动修改CPSR数值切换到特权模式,
只有发生对应的异常后才可以切换到异常模式
5种异常模式对应7种异常源
异常模式 | 异常源 | 解释 |
---|---|---|
FIQ | FIQ类型异常源 | 一些硬件发生了FIQ异常事件进入FIQ模型 |
IRQ | IRQ类型异常源 | 一些硬件发生了IRQ异常事件进入IRQ模型 |
SVC | 复位信号 | 按键复位/上电复位时产生 |
swi软中断指令 | 执行swi指令 | |
undef | 未定义异常源 | 译码器在翻译指令时,遇到无法翻译的指令,指令未定义 |
abort | data abort | 取数据发生异常时 |
prefetch abort | 取指令发生异常时 |
异常的处理过程分析(面试重点)
1. 保存发生异常之前的CPSR的值到对应异常模式下的SPSR寄存器中
2. 修改CPSR的数值
2.1 根据实际情况设置FIQ和IRQ中断禁止 CPSR[7:6]
2.2 修改处理器工作状态为ARM状态 CPSR[5]
2.3 修改处理器的工作模式为对应的异常模式 CPSR[4:0]
3. 保存主程序的返回地址到对应模式下的LR寄存器中
4. PC的值到对应异常模式下的异常向量表中
1. 恢复CPSR寄存器的值为未发生异常之前的状态
2. 修改PC的值为未发生异常之前的下一条指令地址 PC=LR
.text
.global _start
_start:
@初始化异常向量表
b main
b .
b do_swi
b .
b .
b .
b .
b .
main:
@初始化栈
mov sp,#0X40000020
@切换到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,#4
mul r4,r1,r2
@恢复现场
ldmfd sp!,{r1,r2,pc}^ @ ^的作用是修改PC的值的同时将SPSR的值赋值给CPSR
.end
要想实现C和汇编的混合编程必须遵循ATPCS规范。
ATPCS : ARM-Thumb Procedure Call Standard
int add(int i,int j)
{
return i+j;
}
将汇编的标签当作C语言的函数使用
将C语言的函数当作汇编的标签使用
函数参数的传递采用R0-R3进行传递,如果参数的个数大于4个通过压栈的方式进行传递
函数的返回值通过R0返回,如果函数的返回值大于4个字节通过r0-r1返回。
ATPCS规范中规定ARM采用满减栈。
示例:
.text
.global _start
_start:
@ 1. 初始化栈指针,C代码运行必须有栈
ldr sp, =0x40000820
@ 2. 汇编调用c函数
@ 2.1 给C的函数传递实参值
mov r0, #3 @ a = 3
mov r1, #4 @ b = 4
mov r2, #5 @ c = 5
mov r3, #6 @ d = 6
@ 2.2 汇编调用c的函数
bl add_func
@ 2.3 函数的返回通过r0返回,查看r0寄存器中的值
loop:
b loop
.end
// c代码的函数是一个全局的函数
int add_func(int a, int b, int c, int d)
{
return (a+b+c+d);
}
示例:
.text
.globl _start
_start:
@ 1. 初始化栈指针,C代码运行必须有栈
ldr sp, =0x40000820
@ 2. 汇编调用c,跳转到main函数
b main
.end
// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);
int sum = 0;
int main()
{
// 在c代码中调用汇编代码
sum = add_func(1,2,3,4);
while(1);
return 0;
}
.text
.global add_func @ 将add_func函数声明为全局
add_func:
add r0, r0, r1
add r0, r0, r2
add r0, r0, r3
mov pc, lr
.end
asm volatile(
"汇编指令模板\n\t" //"\n\t"表示一条指令的结束
.....
:输出列表 //指令结果的输出值
:输入列表 //指令的数据输入
:破坏列表 //破坏列表指定我们当前可用的寄存器
);
.text
.globl _start
_start:
@ 1. 初始化栈指针,C代码运行必须有栈
ldr sp, =0x40000820
@ 2. 汇编调用c,跳转到main函数
b main
.end
// 内联汇编
int add_func2(int a, int b, int c, int d)
{
int sum = 0;
// 使用汇编实现求和
asm volatile( "add r0, r0, r1\n\t" "add r0, r0, r2\n\t" "add r0, r0, r3\n\t" :"=r"(sum) :"r"(a),"r"(b),"r"(c),"r"(d) :"memory" );
return sum;
}
//"=r"(sum)表示输出从寄存器中放到变量sum中
// "r"(a) 指定输入从变量a中获取放到通用寄存器
//"memory"声明使用内存
// 使用extern对函数进行声明
extern int add_func(int a, int b, int c, int d);
int sum = 0;
int main()
{
// 调用内联汇编的函数
sum = add_func2(5,6,7,8);
// 在c代码中调用汇编代码
sum = add_func(1,2,3,4);
while(1);
return 0;
}
.text
.global add_func @ 将add_func函数声明为全局
add_func:
add r0, r0, r1
add r0, r0, r2
add r0, r0, r3
mov pc, lr
.end