我要成为嵌入式高手之4月11日51ARM第六天!!
b指令
标签:表示这条指令的名称,可跳转至标签
b指令:相当于goto,可随意跳转
如:finish为标签,b finish跳转至该位置
finish
b finish
bl指令:跳转之后将跳转前的位置存储到 lr(链接寄存器) 寄存器中
bx指令:将某个寄存器的保存的地址写入 pc 寄存器
练习:用汇编写出1~100的累加和
cs
area reset, code, readonly
code32
entry
mov r0, #0
mov r1, #0
loop
add r1, r1, r0
add r0, #1
cmp r0, #100
ble loop
finish
b finish
end
2440栈的实现类型
2440实现保护和恢复现场使用的栈是数组栈,即用一段连续的内存空间为栈提供空间。从数组栈的具体实现来看入栈的方式有四种做法:
- 空增:先写入数据,再让栈指针自增;
- 空减:先写入数据,再让栈指针自减;
- 满增:先让栈指针自增,再写入数据;
- 满减:先让栈指针自减,再写入数据。
arm体系采用的方案是满减 ,但是在进行操作之前,我们必须告诉2440栈底的位置,这里我们把栈底设置为0x40001000,从地址0x40000000开始的0x1000这段内存空间对应的是2440内部的一段ram,总共4k。实际能够使用的内存空间为[0x40000000~0x40000FFF],
设置栈底指针寄存器: ldr sp =0x40001000
入栈保护指令stmfd(STMDB)
STMFD<c> <Rn>{!}, <registers>
其中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示入栈之后sp自动自减。如:stmfd sp!, {r0, r1, r2, r3-r12, lr}
出栈恢复指令ldmfd(LDM/LDMIA/)
LDMFD<c> <Rn>{!}, <registers>
中Rn表示栈底指针寄存器,< registers >表示需要入栈保护的寄存器,!表示出栈之后sp自动自增。如:ldmfd sp!, {r0, r1, r2, r3-r12, lr}
汇编程序中调用c程序函数
步骤:
1、先用 import 声明c函数
如: import c_add
2、入栈保护{r0 - r12}
3、若是函数参数小于4个时,通过 r0 ~ r3 寄存器进行传参
4、函数调用完的返回值结果通过 r0 寄存器返回
5、若参数个数大于4个,从第五个开始要通过栈进行参数传参
6、出栈恢复{r0 - r12}
cs
import c_add
stmfd sp!, {r0-r12}
mov r0, #1
mov r1, #2
mov r2, #3
mov r3, #4
mov r4, #5
stmfd sp!, {r4}
bl c_add
ldmfd sp!, {r4}
ldmfd sp!, {r0-r12
c程序中掉用汇编函数
1、import main
2、b main
3、export 汇编函数名
4、在main中声明汇编函数
5、在汇编函数结尾bx lr
如何切换工作模式
1、把r0中低5位清零
2、把r0中低五位设置为10000(user模式)
3、用msr指令将r0的值写入cpsr寄存器
cs
ldr sp, =0x40001000
bic r0, r0, #0x1F
orr r0, r0, #0x10
msr cpsr_c, r0
为什么此处sp变为0?
答:此时的sp为sp_svc,不是user模式下的sp,故需要设置sp_user,且要从栈顶往后走1k
cs
ldr sp, =0x40001000
bic r0, r0, #0x1F
orr r0, r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40001000
sub sp, sp, #1024
cpsr中指令
msr指令:写入cpsr
mrs指令:读取cpsr中数据
启动代码
异常向量表
初始化代码:
cs
preserve8
area reset, code, readonly
code32
entry
;用8个代码占用异常向量表的八个格式
b start
nop
nop
nop
nop
nop
nop
nop
start
ldr sp, =0x40001000
;状态切换到user模式下
mrs r0, cpsr
bic r0, #0x1F
orr r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40001000
sub sp, sp, #1024
import main
b main
finish
b finish
end
中断处理
中断类型:
软件中断SWI
SWI指令:发出一个软件中断,用法:SWI #立即数
作用:产生一个软件中断
流程 :产生一个异常,进入svc模式;查询异常向量表(找软件中断0x08)
cs
;用8个代码占用异常向量表的八个格式
b start
nop
b do_swi
nop
nop
nop
nop
nop
start
;设置的是svc下的栈指针寄存器
ldr sp, =0x40001000
;状态切换到user模式下
mrs r0, cpsr
bic r0, #0x1F
orr r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40001000
sub sp, sp, #1024
import main
b main
do_swi
import swi_handle
stmfd sp!, {r1-r12, lr}
bl swi_handle
ldmfd sp!, {r1-r12, pc}^
;^含义:不但要恢复现场,并且要恢复之前的工作模式
export asm_swi
asm_swi
swi #10
bx lr
;在写完函数之后都应该加上bx lr:将lr写入pc中
IRQ快速中断
初始化IRQ模式栈指针寄存器
cs
preserve8
area reset, code, readonly
code32
entry
;用8个代码占用异常向量表的八个格式
b start
nop
b do_swi
nop
nop
nop
b do_interrupt
nop
start
;设置的是svc下的栈指针寄存器
ldr sp, =0x40001000
;状态切换到user模式下
mrs r0, cpsr
bic r0, #0x1F
orr r0, #0x12;切换到IRQ模式
bic r0, r0, #(1 << 7);将中断允许清零,意为允许中断
msr cpsr_c, r0
;初始化的是IRQ模式下的栈指针寄存器
ldr sp, =0x40001000
sub sp, sp, #1024
mrs r0, cpsr
bic r0, #0x1F
orr r0, #0x10
msr cpsr_c, r0
ldr sp, =0x40001000
sub sp, sp, #2048
import main
b main
do_interrupt
import interrupt_handle
sub lr, lr, #4;查询中断类型表得出快速中断需要lr寄存器的值-4
stmfd sp!, {r1-r12, lr}
bl interrupt_handle
ldmfd sp!, {r1-r12, pc}^
do_swi
import swi_handle
stmfd sp!, {r1-r12, lr}
bl swi_handle
ldmfd sp!, {r1-r12, pc}^
;^含义:不但要恢复现场,并且要恢复之前的工作模式
export asm_swi
asm_swi
swi #10
bx lr
;在写完函数之后都应该加上bx lr:将lr写入pc中
finish
b finish
end
MINI2440
将软件写入MINI2440,需要修改的操作
若将软件下载进板子成功,会出现如此字样
调试结果与软件调试结果应一致