1. 保护现场(压栈)
保护现场的核心思想是 将寄存器的内容保存到栈中,确保在执行函数调用或处理中断时,能够保存当前的上下文(如寄存器值),等到处理完后恢复。
常见步骤:
将重要寄存器压入栈中:
通常会保存 R0-R12(通用寄存器),以及 LR(链接寄存器)和 CPSR(当前程序状态寄存器)。
通过
STMFD
(Store Multiple Full Descending)指令将多个寄存器压入栈中。
保存现场:
执行
STMFD
或PUSH
指令,将寄存器的值保存到栈中。
示例:保护现场并保存寄存器:
asm
STMFD SP!, {R0-R12, LR} ; 将 R0-R12 和 LR 保存到栈中,SP 自减
解释:
STMFD
表示 Store Multiple Full Descending,将多个寄存器存储到栈中。SP!
表示栈指针 自减,即将栈向低地址方向增长。{R0-R12, LR}
表示保存寄存器 R0 到 R12,以及链接寄存器 LR。
注意:
ARM 中的栈是全递减栈(Full Descending Stack),即每次操作栈时,栈指针会先自减,然后存取数据。栈向低地址方向增长。
2. 恢复现场(弹栈)
恢复现场是将压入栈中的寄存器值恢复回寄存器中,从而保证程序在保护和恢复后能继续执行原来的操作。恢复现场的步骤通常会在函数执行完毕或者中断处理完后执行。
常见步骤:
将寄存器从栈中弹出:
使用
LDMFD
(Load Multiple Full Descending)指令从栈中恢复多个寄存器的值。
恢复现场:
执行
LDMFD
或POP
指令,将栈中的值恢复到相应的寄存器。
示例:恢复现场并恢复寄存器:
asm
LDMFD SP!, {R0-R12, LR} ; 从栈中恢复 R0-R12 和 LR 的值
解释:
LDMFD
表示 Load Multiple Full Descending,从栈中加载多个寄存器的值。SP!
表示栈指针自增,即恢复栈指针。{R0-R12, LR}
表示恢复寄存器 R0 到 R12 和 LR 的值。
注意:
ARM 中的 弹栈操作 和 压栈操作 一致,也是全递减栈。
LDR
用于从内存加载数据到寄存器。STR
用于将寄存器的值存储到内存中。
1. ARM 汇编调用 C 语言函数及 C 语言调用汇编函数的参数和返回值处理
在 ARM 汇编和 C 语言混合编程中,函数调用是通过标准的调用约定(calling convention)进行的。ARM 体系结构的标准调用约定是 ARM EABI(Embedded Application Binary Interface),它定义了函数参数、返回值以及寄存器的使用规则。
ARM 汇编调用 C 语言函数
函数参数传递:
ARM 汇编中的函数调用遵循 ARM EABI 约定,函数参数通过寄存器传递。前四个参数使用寄存器
r0
到r3
传递,如果有更多参数,则通过栈传递。示例:
r0
传递第一个参数r1
传递第二个参数r2
传递第三个参数r3
传递第四个参数之后的参数通过栈传递。
返回值:
C 语言函数的返回值通常存储在寄存器
r0
中。函数执行完后,返回值通过r0
返回给调用者。
调用过程:
汇编中通过 bl
(Branch with Link) 指令调用 C 语言函数。bl
会将当前地址保存到 lr
(Link Register)中,返回时会从 lr
中恢复。
示例:假设调用一个 C 语言函数 c_add
,则汇编代码会像这样
ldr r0, =a ; 加载参数 a
ldr r1, =b ; 加载参数 b
bl c_add ; 调用 C 语言函数
返回时,C 函数的返回值会存放在 r0
中,汇编可以通过 r0
读取返回值。
C 语言调用汇编函数
函数参数传递:
C 语言中的函数调用会将参数传递给汇编函数,C 函数遵循 ARM EABI 约定,前四个参数也通过寄存器
r0
到r3
传递。示例:C 函数中传递参数给汇编函数
asm_func
:asm_func(a, b);
汇编中的
asm_func
定义如下:asm_func: mov r0, r0 ; 将传递的第一个参数保存到 r0 mov r1, r1 ; 将传递的第二个参数保存到 r1 ... bx lr ; 返回
asm_func: mov r0, r0 ; 将传递的第一个参数保存到 r0 mov r1, r1 ; 将传递的第二个参数保存到 r1 ... bx lr ; 返回
返回值:
汇编函数的返回值也通过
r0
寄存器传递给 C 语言函数。C 函数会根据汇编函数的返回值做相应处理。
调用过程:
C 语言使用标准函数调用方式,传递参数并接收返回值。ARM 汇编函数使用
bx lr
指令返回,lr
(Link Register)存储了调用时的返回地址。
2. ARM 内核中的异常及其工作模式
ARM 内核支持多种类型的异常(exception),这些异常使得处理器切换到不同的工作模式,以处理硬件或软件中断。
ARM 内核中的几种异常:
Reset Exception(复位异常):
当处理器上电或复位时,会触发复位异常,处理器进入 Reset 模式。
该异常是处理器启动时的第一个异常,用于初始化系统。
Undefined Instruction Exception(未定义指令异常):
当处理器遇到一个未定义的指令时,会触发此异常。
处理器进入 Undefined 模式。
Software Interrupt Exception(软件中断异常,SWI):
由软件发出的
swi
指令触发,通常用于系统调用或用户请求服务。处理器进入 Supervisor 模式。
Prefetch Abort Exception(预取中止异常):
当处理器尝试访问一个无效的指令地址时,触发预取中止异常。
处理器进入 Abort 模式。
Data Abort Exception(数据中止异常):
当处理器尝试访问一个无效的数据地址时,触发数据中止异常。
处理器进入 Abort 模式。
IRQ (Interrupt Request) Exception(外部中断请求异常):
当外部硬件或外部设备触发中断时,处理器响应此异常。
处理器进入 IRQ 模式。
FIQ (Fast Interrupt Request) Exception(快速中断请求异常):
与 IRQ 异常类似,但它是为高优先级中断设计的,处理速度较快。
处理器进入 FIQ 模式。
SWI (Software Interrupt) Exception(软件中断异常):
当执行
swi
指令时,触发此异常。它通常用于系统调用或程序控制流。处理器进入 Supervisor 模式。
ARM 异常导致的工作模式切换:
ARM 处理器在遇到不同类型的异常时,会根据异常的种类切换到不同的工作模式(Mode)。这些模式如下:
User Mode(用户模式):
用户程序执行时的默认模式。具有最小的权限,不能访问受保护的资源。
FIQ Mode(快速中断模式):
用于处理快速中断(FIQ),具有高优先级和快速响应能力。
IRQ Mode(普通中断模式):
用于处理外部中断(IRQ),优先级低于 FIQ,但高于 User Mode。
Supervisor Mode(管理模式):
用于处理软件中断(SWI)等操作,具有更高的权限。
Abort Mode(中止模式):
用于处理数据或指令访问错误,处理器会在此模式下处理访问异常。
Undefined Mode(未定义模式):
当遇到未定义指令时切换到此模式,用于处理无法识别的指令。
System Mode(系统模式):
处理器处于操作系统级别的模式,通常用于管理系统资源,权限最高。