栈操作
接上文LDM/STM指令部分提到的栈操作,栈操作主要分为两种:
- PUSH:压栈
- POP:弹栈
栈操作时又分为两种实现方式:
- 满栈:SP所指的地址有数据
- 空栈:SP所指的地址无数据
以下为不同的栈类型对应的栈操作指令:
栈类型 | 压栈 | 弹栈 |
---|---|---|
满栈降序 | STMFD(STMDB,先减少,再操作) | LDMFD(LDMIA,先操作,再增加) |
满栈增序 | STMFA(STMIB,先增加,再操作) | LDMFA(LDMDA,先操作,再减少) |
空栈降序 | STMED(STMDA,先操作,后减少) | LDMED(LDMIB,先操作,再增加) |
空栈增序 | STMEA(SRMIA,先操作,后增加) | LDMEA(LDMDB,先减少,再操作) |
软中断
通过汇编指令软件实现中断称为软中断。通过软中段机制,运行在用户模式下的应用程序,可请求操作系统执行一系列的特权操作。
为了参考,我们再次将上一篇文章中的ARM内核寄存器组列表罗列于此:
寄存器 | ||||||||
---|---|---|---|---|---|---|---|---|
R0 | √ | |||||||
R1 | √ | |||||||
R2 | √ | |||||||
R3 | √ | |||||||
R4 | √ | |||||||
R5 | √ | |||||||
R6 | √ | |||||||
R7 | √ | |||||||
R8 | √ | √ | ||||||
R9 | √ | √ | ||||||
R10 | √ | √ | ||||||
R11 | √ | √ | ||||||
R12 | √ | √ | ||||||
SP(R13) | √ | √ | √ | √ | √ | √ | √ | √ |
LR(R14) | √ | √ | √ | √ | √ | √ | √ | |
PC(R15) | √ | |||||||
CPSR | √ | |||||||
SPSR | √ | √ | √ | √ | √ | √ | √ | |
ELR_hyp | √ | |||||||
模式 | User/System | Hyp | Supervisor | Abort | Undef | Moniter | IRQ | FIQ |
软中断命令
SWI{(cond)} <SWI NUMBER>
SWI指令可以产生一个异常陷阱,从而跳转到异常向量表的SWI_handler,SWI NUMBER为中断号。
^符号可以在弹栈的同时将SPSR的值赋给CPSR。
软中断原理
软中断需要实现模式改变后寄存器现场的保存,和模式恢复后寄存器现场的恢复,分为以下几步:
- 初始化特殊模式的内存栈,将特殊模式的内存栈首地址赋值给特殊模式的SP寄存器,此处我们赋值给软中断模式的SP寄存器。
- SWI指令产生异常陷阱,同时生成中断号。
- 跳转入异常向量表,再由异常向量表跳转至代码段对应的指令段。
- 将之前公用寄存器现场保存压入特殊模式的内存栈中,因为这些公用的寄存器在特殊模式下也会使用,会造成之前模式的寄存器现场破坏。
- 获取中断号。
- 跳转至对应中断号的指令代码
- 中断程序执行完毕,将特殊模式内存栈中存储的上一模式的寄存器现场恢复,即弹栈恢复。
以上为软中断实现的基本步骤,以下为满栈降序式的内存栈为例,进行的软中断实现:
.text
.global _start
_start:
@0
b reset
@4
b undef_handler
@8
b swi_handler @进入异常向量表后,工作模式由user转为svc,从异常向量表跳转到软中断代码段
b perfetch_handler
b databort_handler
nop
b irq_handler
b fiq_handler
reset:
@将SVC模式下的栈内存首地址赋给SVC模式的SP寄存器
ldr r0,=svc_stack
add r0,#128 @指到栈底
mov sp,r0 @初始化svc的sp
mrs r0,cpsr
bic r0,#3
msr cpsr,r0 @进入user模式
@将USER模式下的栈内存首地址赋给USER模式的SP寄存器
ldr r0,=user_stack
add r0,#128 @指到栈底
mov sp,r0 @初始化user的sp
mov r1,#11
mov r2,#12
mov r3,#13
@swi跳转到异常向量表
swi 10
nop
nop
nop
b .
undef_handler:
swi_handler:
@将r0~r12,lr寄存器的数据压入svc模式下的内存栈中
@保护user模式的寄存器现场
stmfd sp!,{r0-r12,lr}
mov r1,#1
mov r2,#2
mov r3,#3
@取当前模式的中断号
sub lr,#4
ldr r4,[lr]
bic r4,#0xff000000
cmp r4,#10
beq swi_10 @若中断号为10跳转
cmp r4,#1
b swi_end
nop
nop
nop
movs pc,lr @跳转回reset
swi_10:
@将svc模式下内存栈中的数据弹栈到寄存器r0~r12,lr
@^ 出栈的同时将当前的spsr->cpsr,恢复模式改变前cpsr的寄存器状态
@将中断前user模式下的寄存器现场恢复
@将lr的值赋给pc,也就是将程序重新指向中断事件断点处。
ldmfd sp!,{r0-r12,pc}^
swi_end:
movs r0,#0
ldmfd sp!,{r0-r12,pc}^ @^ 出栈的同时将当前的spsr->cpsr
perfetch_handler:
databort_handler:
irq_handler:
fiq_handler:
.data
svc_stack: .space 128
user_stack: .space 128
.end