;---------------------------------------------------- ;Copyright (C), 2004-2009, lst. ;版权所有 (C), 2004-2009, lst. ;所属模块:调度器 ;作者:lst ;版本:V1.0.1 ;文件描述:调度器中与CPU直接相关的汇编代码,主要是线程切换方面的代码。 ;其他说明: ;修订历史: ;2. 日期: 2010-01-04 ; 作者: lst ; 新版本号: V1.0.1 ; 修改说明: 从gcc移植到keil ;1. 日期: 2009-01-04 ; 作者: lst ; 新版本号: V1.0.0 ; 修改说明: 原始版本 ;------------------------------------------------------ IMPORT int_contact_trunk IMPORT int_contact_asyn_signal IMPORT int_restore_asyn_signal IMPORT __djy_vm_engine IMPORT |Image$$usr_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 IMPORT |Image$$svc_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 IMPORT |Image$$irq_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 IMPORT |Image$$fiq_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 IMPORT |Image$$undef_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 IMPORT |Image$$abort_stack$$ZI$$Limit| ;栈顶地址,在脚本中定义 MODEMASK EQU 0x1f NOIRQ EQU 0x80 ;注: sp并不保存在栈中,而是在vm->stack中!! PRESERVE8 ;告诉汇编器,栈是8字节对齐的 AREA |.text|, CODE, READONLY ;----重置线程----------------------------------------------------------------- ;功能: 初始化线程的栈,并挂上线程执行函数,新创建线程时将会调用本函数 ;参数: thread_routine,被重置的线程函数 ; vm,线程虚拟机指针 ;返回: 初始化结束后的当前栈指针 ;函数原型:void * __asm_reset_thread(void (*thread_routine)(struct event_script *), ; struct thread_vm *vm); ;----------------------------------------------------------------------------- __asm_reset_thread PROC EXPORT __asm_reset_thread stmfd sp!,{lr} ldr r2,[r1,#4] ;取虚拟机栈顶指针 ldr r3,=__djy_vm_engine ;取虚拟机引擎指针 stmfd r2!,{r3} ;存pc, sub r2,r2,#13*4 ;后退13个寄存器,初始状态r1-r12随意,__vm_engine ;函数不返回,lr也无意义 ;__vm_engine的参数,切换上下文时,service_routine将恢复到r0中,根据调用约 ;定,r0的值就是__vm_engine函数的参数。这只在创建并初始化线程上下文时需要这 ;样做,线程启动以后线程切换要保存上下文时,保存r0的实际值即可 stmfd r2!,{r0} ;保存 thread_routine指针到r0的位置. mrs r0,CPSR ;取cpsr stmfd r2!,{r0} ;保存cpsr str r2,[r1] ;保存vm的当前栈指针到vm->stack中 ldmfd sp!,{pc} ENDP ; end of __asm_reset_thread ;----复位老线程,切换到新线程------------------------------------------------- ;功能: 把old_vm的上下文重新初始化到新创建的状态,然后切换到新线程的 ; 上下文中继续执行。 ; 当一个在常驻内存的虚拟机中处理的事件完成后,不能删除该虚拟机,必须复位 ; 该虚拟机,然后切换到其他就绪线程,这时候就需要调用本函数。因此本函数在 ; 被复位的线程上下文里面执行。 ;参数: thread_routine,被重置的线程函数 ; new_vm,目标线程的虚拟机 ; old_vm,被复位的虚拟机 ;返回: 无 ;函数原型:void __asm_reset_switch(void (*thread_routine)(struct event_script *), ; struct thread_vm *new_vm,struct thread_vm *old_vm); ;----------------------------------------------------------------------------- __asm_reset_switch PROC EXPORT __asm_reset_switch ldr sp,[r2,#4] ;取老虚拟机栈顶指针 ldr r12,=__djy_vm_engine ;取虚拟机引擎指针 stmfd sp!,{r12} ;存pc sub sp,sp,#13*4 ;后退13个寄存器,初始状态r1-r12随意.__djy_vm_engine ;函数不返回,lr也无意义 ;至此,完成老虚拟机复位 stmfd sp!,{r0} ;保存 thread_routine指针.至此,完成老线程重置 mrs r0,CPSR ;取cpsr stmfd sp!,{r0} ;保存cpsr str sp,[r2] ;保存当前栈指针到old_vm->stack中 ldr sp,[r1] ;取得新上下文指针 bl int_restore_asyn_signal ;对应done函数开头的 int_save_asyn_signal 调用 ldmfd sp!,{r1} msr CPSR_cxsf,r1 ;恢复新cpsr ldmfd sp!,{r0-r12,lr,pc} ;恢复寄存器, ENDP ; end of __asm_reset_switch ;----切入上下文--------------------------------------------------------------- ;功能: 不保存原上下文,直接切入新的上下文执行 ;参数: new_sp,新上下文的栈指针 ;返回: 无 ;函数原型: void __asm_turnto_context(struct thread_vm *new_vm); ;说明: 当事件完成,就没有必要保存旧事件的上下文,直接切换到新事件即可. ;----------------------------------------------------------------------------- __asm_turnto_context PROC EXPORT __asm_turnto_context ldr sp,[r0] ;取得新上下文指针 bl int_restore_asyn_signal ;对应done函数开头的 int_save_asyn_signal 调用 ldmfd sp!,{r0} msr CPSR_cxsf,r0 ;恢复新cpsr ldmfd sp!, {r0-r12,lr,pc} ;恢复寄存器, ENDP ; end of __asm_turnto_context ;----上下文切换--------------------------------------------------------------- ;功能: 保存当前线程的上下文,切换到新线程的上下文中继续执行。 ;参数: new_sp,新上下文的栈指针 ;参数: old_sp,旧上下文的栈指针的指针,即&vm->stack。无需提供旧上下文栈指针, ; sp寄存器的当前值就是 ;返回: 无 ;函数原型: void __asm_switch_context(struct thread_vm *new_vm,struct thread_vm *old_vm); ;----------------------------------------------------------------------------- __asm_switch_context PROC EXPORT __asm_switch_context stmfd sp!,{lr} ;保存PC stmfd sp!,{r0-r12,lr} ;保存寄存器和LR mrs r4,CPSR stmfd sp!,{r4} ;保存cpsr str sp,[r1] ;保存旧上下文栈指针到old_vm->stack ldr sp,[r0] ;取得新上下文指针 ldmfd sp!, {r0} msr CPSR_cxsf, r0 ;恢复新cpsr bl int_contact_asyn_signal bl int_contact_trunk ldmfd sp!, {r0-r12,lr,pc} ;恢复寄存器, ENDP ; end of __asm_switch_context ;中断上下文切换函数, ;本函数由中断函数调用,由于中断发生时间是随机的,编译器没有任何办法保护lr,若发生 ;上下文切换,则必需手动把LR_sys保存到上下文中,恢复时才不会出错. ;本函数必需由C语言调用,如果汇编调用,调用前要保存LR,见switch_context函数的注释. ;只有在中断没有嵌套时才可能调用本函数,中断前的状态是sys态. ;----中断中的上下文切换------------------------------------------------------- ;功能: 保存被中断线程的上下文,切换到新线程的上下文中继续执行。本函数虽然在中 ; 断服务函数(非用户用int_isr_connect函数连接的中断服务函数)中,但在ARM ; 中,却运行在svc态 ;参数: new_sp,切换目标虚拟机 ;参数: old_sp,被中断线程虚拟机 ;返回: 无 ;函数原型: void __asm_switch_context_int(struct thread_vm *new_vm,struct thread_vm *old_vm); ;----------------------------------------------------------------------------- __asm_switch_context_int PROC EXPORT __asm_switch_context_int stmfd sp!,{r2-r12,lr} ;保存正在服务的中断上下文 ;以下几行把old_vm的上下文从正在服务的中断栈顶转移到虚拟机栈中 ldr r2,=|Image$$irq_stack$$ZI$$Limit| ;取irq栈基址,这里存放着被中断线程的上下文 ldmea r2!,{r3-r10} ;按递增式空栈方式弹栈,结果: ;[r2-1]=LR_irq->r10,被中断线程的PC+4 ;[r2-2]=r12->r9,被中断线程的r12 ;[r2-3]=r11->r8,被中断线程的r11 ;....... ;[r2-8]=r6->r3,被中断线程的r6 sub r10,r10,#4 ;中断栈中的LR_irq-4=PC ;以下三句就是取出old_vm的SP_sys,只能通过stmfd指令间接取 mov r11,sp ;下一句不能用SP,故先拷贝到r11 stmfd r11,{sp}^ ;被中断线程的SP_sys压入正在服务的中断栈中 sub r11,r11,#4 ;这两条指令本可用 stmfd r11!,{sp}^完成,不知为何keil不行 ldmfd r11!,{r12} ;从正在服务的中断栈中读取 SP_sys->R12 stmfd r12!,{r10} ;保存 PC_sys stmfd r12,{lr}^ ;保存 lr_sys sub r12,r12,#4 ;这两条指令本可以用 stmfd r12!,{lr}^完成,不知为何keil不行 stmfd r12!,{r3-r9} ;保存被中断线程的r12-r6到它的栈中 ldmea r2!,{r3-r9} ;读被中断线程的r5-r0->r9-r4,SPSR_irq->r3,递增式空栈 stmfd r12!,{r3-r9} ;保存被中断线程的r5-r0,CPSR_sys到它的栈中 str r12,[r1] ;换出的上下文的栈指针-->old_sp ;以下几行把new_vm的上下文copy到IRQ栈顶 ;与递减式满栈对应,此时IRQ栈用递增式空栈的方式访问 ldr r12,[r0] ;读取需换入的栈指针 ldmfd r12!,{r3-r11} ;读取换入线程的CPSR_sys->r3 ;读取换入线程的r0-r7->r4-r11 stmea r2!,{r3-r11} ;保存换入线程的CPSR_sys->SPSR_irq, r0-r7到IRQ栈 ldmfd r12!,{r3-r7} ;读取换入线程的r8-r12->r3-r7 stmea r2!,{r3-r7} ;保存换入线程的r8-r12到IRQ栈 ldmfd r12,{lr}^ ;恢复换入线程的LR_sys到寄存器中 add r12,r12,#4 ;这两条指令本可用 ldmfd r12!,{lr}^完成,不知为何keil不行 ldmfd r12!,{r3} ;读取换入线程的PC->r3 add r3,r3,#4 ;模拟IRQ保存被中断上下文PC的方式:PC+4->LR_irq stmea r2!,{r3} ;保存换入线程的LR_irq到IRQ栈 stmfd r12!,{r12} ;读取SP_sys到r12 ldmfd r12,{SP}^ ;恢复SP_sys add r12,r12,#4 ;这两条指令本可用 ldmfd r12!,{sp}^完成,不知为何keil不行 mov r0,r0 ;无论是否操作当前状态的SP,操作sp后,不能立即执行函数 ;返回指令,否则返回指令的结果不可预知。 ldmfd sp!,{r2-r12,pc} ENDP ; end of __asm_switch_context_int ; IF :DEF:debug ;USERMODE EQU 0x10 ;SYSMODE EQU 0x1f ;FIQMODE EQU 0x11 ;IRQMODE EQU 0x12 ;SVCMODE EQU 0x13 ;ABORTMODE EQU 0x17 ;UNDEFMODE EQU 0x1b ;NOINT EQU 0xc0 ; ; IMPORT pre_start ; ENTRY ;__asm_set_debug PROC ; EXPORT __asm_set_debug ; mrs r0,cpsr ;取CPSR ; bic r0,r0,#MODEMASK ;清模式位 ; orr r1,r0,#SVCMODE|NOINT ;设置为管理态,并禁止中断 ; msr cpsr_cxsf,r1 ;切换到管理态,可防止意外返回0地址时出错. ; ; mrs r0,cpsr ; bic r0,r0,#MODEMASK ; ; orr r1,r0,#UNDEFMODE|NOINT ; msr cpsr_cxsf,r1 ; UndefMode ; ldr sp,=|Image$$undef_stack$$ZI$$Limit| ; ; orr r1,r0,#ABORTMODE|NOINT ; msr cpsr_cxsf,r1 ; AbortMode ; ldr sp,=|Image$$abort_stack$$ZI$$Limit| ; ; orr r1,r0,#IRQMODE|NOINT ; msr cpsr_cxsf,r1 ; IRQMode ; ldr sp,=|Image$$irq_stack$$ZI$$Limit| ; ; orr r1,r0,#FIQMODE|NOINT ; msr cpsr_cxsf,r1 ; FIQMode ; ldr sp,=|Image$$fiq_stack$$ZI$$Limit| ; ; orr r1,r0,#SVCMODE|NOINT ; msr cpsr_cxsf,r1 ; SVCMode ; ldr sp,=|Image$$svc_stack$$ZI$$Limit| ; ; orr r1,r0,#SYSMODE|NOINT ; msr cpsr_cxsf,r1 ; userMode ; ldr sp,=|Image$$usr_stack$$ZI$$Limit| ; bl pre_start ; ENDP ; ENDIF ALIGN END


Created by 2beanet Embedded Group : http://www.2beanet.com