第三篇 世界的起源 任务如何启动
其实绝大部分操作系统的实现步骤都是这样的:
先压点合适的东西进堆栈----堆栈的初始化
(这步放在任务初始化的时候----切记关任务切换和定时中断)
只要恢复寄存器和其他数据之后
SP指针正确,状态寄存器没异常,
直接将PC指向新任务的第一条指令,不就行了嘛。
看下我们CORTEX-M3平台上的实现:(堆栈生长方向是--,跟X86相反)
/*把堆栈初始化模块用汇编写一遍,但愿性能会高点*/
__asm STACK_TYPE *TaskStkInit (void (*task),STACK_TYPE *ptos)
{
PUSH {R4-R6,LR}
MOV R4,R0
MOV R0,R1
MOV R5,#0x1000000
STR R5,[R0,#0]
SUBS R0,R0,#4
STR R4,[R0,#0]
MVN R5,#1
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x2
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x3
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x4
SUBS R0,R0,#4
STR R5,[R0,#0]
ASRS R5,R5,#1
SUBS R0,R0,#4
STR R5,[R0,#0]
SUBS R0,R0,#4
STR R1,[R0,#0]
MOV R5,#0x5
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x6
SUBS R6,R0,#4
MOV R0,R6
STR R5,[R6,#0]
MOV R5,#0x7
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x8
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x8
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x10
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x11
SUBS R0,R0,#4
STR R5,[R0,#0]
MOV R5,#0x12
SUBS R6,R0,#4
MOV R0,R6
STR R5,[R6,#0]
POP {R4-R6,PC}
}启动这个任务,就可以直接使用任务切换源码的后半部分(因为没有任务需要保存)
这样PC就指向了新任务的入口,
新任务可以开始运行啦!
世界就这样形成了...
实际启动是使用SVC中断启动
代码如下:
__asm void StartTask()
{
LDR R4, =NVIC_SYSPRI2 ; set the PendSV exception priority
LDR R5, =NVIC_PENDSV_PRI
STR R5, [R4]
MOV R4, #0 ; set the PSP to 0 for initial context switch call
MSR PSP, R4
LDR R4, =SYS_TICKS ;设置时钟节拍频率
LDR R5, =NVIC_SYSTICK_LOAD
STR R4, [R5]
MOV R4, #0x07
LDR R5, =NVIC_SYSTICK_CTRL
STR R4, [R5]
SVC 0 ;呼叫SVC中断
NOP
}
/*SVC中断入口*/
__asm void SVC_Handler()
{
LDR R6,=BenOSCurTCB
LDR R0, [R6] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDR R0, [R0] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDM R0, {R4-R11} ; restore r4-11 from new process stack
ADD R0, R0, #0x20
MSR PSP, R0 ; load PSP with new process SP
ORR LR, LR, #0x04 ; ensure exception return uses process stack
BX LR ; exception return will restore remaining context
}
|