OS_TASK_SW, OS_CPU_IRQ_ISR,OSIntCtxSw,OS_CPU_FIQ_ISR,OSStarHighRdy
下面是关于这个几个函数的汇编,处理是基于ARM的S3C2410,其中的一些有关汇编的知识在
http://blog.youkuaiyun.com/cangencong/article/details/6890933这篇博文中已经讲到了,但是对于这几个函数的一些注解,自己也有一些不懂的地方,并且标注出来了,希望如果有知道的人,看到还望他不吝赐教。
OS_TASK_SW
是一个普通的任务级的任务调度
;以下是定义全局变量的定义
;IMPORT伪操作告诉编译器当前的符号不是在本源文件中定义的,
;而是在其他源文件中定义的,在本源文件中可能引用该符号,
;而且不论本源文件是否实际引用该符号,该符号都将被加入到本源文件的符号表中。
;
IMPORT OSTCBCur
addr_OSTCBCur DCD OSTCBCur ;当前任务TCB指针
IMPORT OSTCBHighRdy
addr_OSTCBHighRdy DCD OSTCBHighRdy ;当前最高优先级的TCB指针
IMPORT OSPrioCur
addr_OSPrioCur DCD OSPrioCur ;当前任务优先级
IMPORT OSPrioHighRdy
addr_OSPrioHighRdy DCD OSPrioHighRdy ;新就绪最高优先级任务
OS_TASK_SW ;只是一个标号,相当于函数名
STMFD sp!,{lr}
STMFD sp!,{lr}
STMFD sp!,{r0-r12}
MRS r4,CPSR
STMFD sp!,{r4}
MRS r4,SPSR
STMFD sp!{r4}
;最高优先级的值放入当前优先级的值中
LDR r4,addr_OSPrioCur
LDR r5,addr_OSPrioHighRdy
LDRB r6,[r5]
STRB r6,[r4]
;把此时的sp地址作为(旧任务的栈顶值),说明入栈需要保存的数据保存结束
LDR r4,addr_OSTCBCur
LDR r5,[r4]
STR sp,[r5] ;任务控制块的第一个成员就是OS_STK *OSTCBSTKPT,专门用来存放栈顶
;把此时最高优先级的任务(新任务)的栈顶指针放入sp中
LDR r6,addr_OSTCBHighRdy
LDR r6,[r6]
LDR sp,[r6]
;把OSTCBCur的值修改为新就绪最高优先级任务的控制块地址
;并且进行出栈,则开始执行此新任务,完成任务调度功能
STR r6,[r4] ;r4指向当前任务控制块指针的地址,r6是最高优先级任务控制块指针
;按照入栈相反的顺序出栈所保存的数据,此时的sp已经是最高优先级任务的栈顶了
LDMFD sp!,{r4}
MSR SPSR_cxsf,{r4}
LDR sp,{r4}
MSR CPSR_cxsf,{r4}
LDMFD sp!,{r0-r12,lr,pc}
OS_CPU_IRQ_ISR
普通中断处理函数方式,
;整个过程方式是:在产生中断时,必须快速进入中断,避免中断丢失,
;当保存了最主要的中断信息后,再次回到SVC模式中存储正在执行任务的信息。
;结束后,在回到IRQ模式中,执行中断服务程序,执行完成后,再次返回SVC模式。
;OS_CPU_IRQ_ISR函数
;以下是部分定义
NO_INT EQU 0xc0 ;用于禁止FIR和IRQ
NO_IRQ EQU 0x80 ;用于禁止IRQ
NO_FIQ EQU 0x40 ;用于禁止FIR
SVC32_MODE EQU 0x13 ;处理器模式为32位SVC模式
FIQ32_MODE EQU 0x11 ;处理器模式为32位FIQ模式
IRQ32_MODE EQU 0x12 ;处理器模式为32位IRQ模式
;IRQ中断处理函数
OS_CPU_IRQ_ISR ;标号,可以在C语言程序中调用此函数
;首先禁止CPU的FIQ中断,并切换到IRQ中断模式
MSR CPSR_c,#(NO_INT|IRQ32_MODE)
STMFD SP!,{R1-R3};将R1-R3通用寄存器存入IRQ模式下的栈(SP_IRQ)中
MOV R1,SP ;①
ADD SP,SP,#12 ;???为什么留出3个字的空间
SUB R2,LR,#4 ;获取返回值??是因为ARM预读指令的原因吗
MRS R3,SPSR ;②读取SPSR寄存器
MSR CPSR_c,#(NO_INT|SVC32_MODE) ;切换到SVC模式
;存储任务的上下在SVC模式的栈空间中
STMFD SP!,{R2} ;存储返回地址
STMFD SP!,{LR} ;存储LR寄存器的值
STMFD SP!,{R4-R12} ;存储R4-R12通用寄存器的值
LDMFD R1!,{R4-R6} ;因为在①出获得了SP的值,可以把IRQ的寄存器R1-R3的值取出来
STMFD SP!,{R4-R6} ;把IRQ模式中的R1-R3原始值入栈到SVC模式栈中
STMFD SP!,{R0} ;将R0入SVC模式栈中
;将全局变量OSTntNesting自动加1,也可以执行OSIntEnter()代替
LDR R0,OS_IntNesting
LDRB R1,[R0] ;只传递后8位,难道是可以提高效率????
ADD R1,R1,#1
STRB R1,[R0]
;如果条件满足(OSIntNesting==1)即,只有这一个中断,
;将设置当前任务控制块成员变量OSTCBCur->OSTCBStrPtr=SP
CMP R1,#1 ;R1与1比较,结果会改变状态寄存器
BNE OS_CPU_IRQ_ISR_1 ;如果R1不等1则跳转
LDR R4,OS_TCBCur
LDR R5,[R4]
STR SP,[R5] ;第一个成员变量是OSTCBCur->OSTCBStrPtr = SP
OS_CPU_IRQ_ISR_1
;返回到IRQ中断,使能FIQ中断,执行中断子程序OS_CPU_IRQ_ISR_Handler()
MSR CPSR_c,#(NO_IRQ|IRQ32_MODE)
LDR R0,OS_CPU_IRQ_IRQ_Handler
MOV LR,PC ;因为LR在执行BL子程序调用指令时,作为R15的备份
BX R0
;当中断服务程序执行结束后,返回这里,CPU的状态切换为SVC模式
MSR CPSR_c,#(NO_INT|SVC32_MODE)
;调用OSIntExit()函数,引发新的调用
LDR R0,OS_IntExit
MOV LR,PC
BX R0
;读取新任务的上下文信息
LDMFD SP!,{R4}
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^
;在特权模式下(SVC)下,"S"或者"^"的作用就是使指令在执行时,同时从SPSR到CPSR的复制,
;达到恢复状态寄存器。
OSIntCtxSw
;中断级任务调度切换函数
;由于ARM有各种不同的模式,因此在普通模式下的任务切换和中断后的任务切换是不同的
OSIntCtxSw
LDR R0,OS_TaskSwHook ;执行特殊的钩子函数①OSTaskSwHook()
MOV LR,PC
BX R0
;将全局变量OS_PrioCur的值修改为OS_PrioHighRdy
LDR R4,OS_PrioCur
LDR R5,OS_PrioHighRdy
LDRB R6,[R5]
STRB R6,[R4]
;将全局变量OS_TCBCur的值修改为OS_TCBHighRdy
LDR R4,OS_TCBCur
LDR R5,OS_TCBHighRdy
LDR R6,[R5]
STR R6,[R4]
;读出新任务的堆栈指针SP = OSTCBHighRdy->OSTCBStrPtr
LDR SP,{R6}
;恢复新任务的上下文内容,然后自动跳转,因为PC值变了
LDMFD SP!,{R4}
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^
;子函数①OSTaskSwHook()由移植此系统的用户添加代码,
;默认为空,当用户认为有必要执行某些特殊功能时,在函数中添加相应的代码
OS_CPU_FIQ_ISR
FIQ中断与普通中断比较类似,但是更为紧急,能够中断IRQ。
OS_CPU_FIQ_ISR
;为什么不手动进入FIQ模式,IRQ模式是手动进入的
STMFD SP!,{R1-R4} ;将会使用到的寄存器压入FIQ栈中
MOV R1,SP ;②读取FIQ的SP指针
SUB R2,LR,#4 ;①调整LR,以得到PC返回值
MRS R3,SPSR ;读取SPSR_fiq的值
MOV R4,R3
AND R4,R4,#0x1F ;提取SPSR后5位
CMP R4,#IRQ32_MODE ;测试FIQ是否在IRQ模式下发生的,即是否中断嵌套了
BEQ OS_CPU_FIQ_ISR_2 ;如果是,则跳到OS_CPU_FIQ_ISR;如果不是直接执行FIQ中断处理
MSR CPSR_c,#(NO_INT|SVC32_MODE) ;将当前CPU修改为SVC模式
;保存上下文内容到任务栈中
STMFD SP!,{R2} ;将PC放入SVC模式栈中,在①出,R2已经得到PC值
STMFD SP!,{LR} ;将LR保存到SVC模式中
LDMFD R1!,{R5-R8} ; 将FIQ栈中的R1-R4存储到SVC栈中,在②处,已经获得FIQ栈指针
STMFD SP!,{R5-R8}
STMFD SP!,{R0}
STMFD SP!,{R3} ;将SPSR_fiq存储到SVC栈中
LDR R0,OS_IntNesting ;中断嵌套次数加1,
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
;如果条件满足OS_IntNesting=1
;设置当前任务的任务控制块成员变量OSTCBCur->OSTCBStrPtr = SP
CMP R1,#1
BNE OS_CPU_FIQ_ISR_1
OS_CPU_FIQ_ISQ_1
MSR CPSR_c,#(ON_INT|FIQ32_MODE) ;进入FIQ模式
ADD SP,SP,#16 ;修改FIQ栈指标(为什么要修改)
LDR R0,OS_CPU_FIQ_ISR_Handler ;调用中断服务程序
MOV LR,PC
BX R0
;读取新任务的上下文内容,开始执行新任务
LDMFD SP!,{R4} ;将新任务的cpsr出栈
MSR SPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}^ ;由于PC值改变,相当于return了。
;FIQ中断将中断IRQ中断
OS_CPU_FIQ_ISR_2
;将IRQ的上下文存储到FIQ的栈中
STMFD SP!,{R0-R7,LR}
;中断次数加一,相当于执行OSIntEnter()函数
LDR R0,OS_IntNesting
LDRB R1,[R0]
ADD R1,R1,#1
STRB R1,[R0]
LDR R0,OS_CPU_FIQ_ISR_Handler
MOV LR,PC
BX R0
;全局变量OSIntNesting自动减一,此处没有必要执行OSIntExit()函数
;因为返回到IRQ模式下时将调用此函数,另外,在中断嵌套中不允许引发新的调度
LDR R0,OS_IntNesting
LDR R1,[R0]
SUB R1,R1,#1
STRB R1,[R0]
;恢复IRQ模式下的上下文内容,然后返回到IRQ模式执行IRQ中断
LDMFD SP!,{R0-R7,LR}
LDMFD SP!,{R1-R4}
SUBS PC,LR,#4
OSStarHighRdy
;运行最高优先级任务
;GLOBAL 与 EXPORT 相同,EXTERN 与 IMPORT 相同
IMPORT OSTCBCur
addr_OSTCBCur DCD OSTCBCur
IMPORT OSTCBHighRdy
addr_OSTCBHighRdy DCD OSTCBHighRdy
EXPORT OSStarHighRdy
OSStarHighRdy
LDR R4,addr_OSTCBCur ;获取当前任务TCB的地址
LDR R5,addr_OSTCBHighRdy ;获取最高优先级的TCB地址
LDR R5,[R5]
LDR SP,[R4]
LDMFD SP!,{R4}
MSR SPSR_cxsf,R4
LDMFD SP!,{R4}
MSR CPSR_cxsf,R4
LDMFD SP!,{R0-R12,LR,PC}
END