第三篇 世界的起源 任务如何启动
其实绝大部分操作系统的实现步骤都是这样的: 先压点合适的东西进堆栈----堆栈的初始化 (这步放在任务初始化的时候----切记关任务切换和定时中断) 只要恢复寄存器和其他数据之后 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 } |
|