原创 Small RTOS51学习(转帖)

2011-1-18 23:22 1792 7 7 分类: MCU/ 嵌入式

从函数的执行顺序来分析Small RTOS51内核,以便了解整个内核的实现过程及运行机理。
照配套光盘里面的例程分析
EXT1
#include "config.h"

void main(void)
{
 TMOD = (TMOD & 0XF0) | 0X01; // 定时器0初始化为16位定时器
 TL0 = 0x0;
 TH0 = 0x0;
 TR0 = 1;
 ET0 = 1;
    OSStart();
}

首先是设定定时器的基本参数,并未开启总中断。接着便进入了OSStart函数,接下来我们看看OSStart函数都做了些什么工作。
OSStart函数属于OS_cpu_c.c文件中
函数将初始化small rtos51,并开始执行任务ID为0的任务
       
void OSStart(void)
       
{
    uint8 idata *cp;    //新建一个指针用于后面建立堆栈空间使用
    uint8 i;
   
 // extern idata uint8 STACK[1]; 堆栈起始位置,在OS_CPU_A定义 CP指向这段空间
    cp = STACK;        
   
 /* uint8 idata * data OSTsakStackBotton[OS_MAX_TASKS + 2]; 任务堆栈底部位置
   在config.h中有做如下定义
  void  (* const TaskFuction[OS_MAX_TASKS])(void)={TaskA,TaskB,TaskC};
   将栈顶指向STACK
 */
    OSTsakStackBotton[0] = STACK;
    // 堆栈底部
    OSTsakStackBotton[OS_MAX_TASKS + 1] = (uint8 idata *)(IDATA_RAM_SIZE % 256);
   
    /* 初始化优先级最高的任务堆栈,使返回地址为任务开始地址 */
    *cp++ = ((uint16)(TaskFuction[0])) % 256;
    SP = (uint8) cp;
    *cp = ((uint16)(TaskFuction[0])) / 256;

    /* 初始化优先级最低的任务堆栈 */
    cp = (uint8 idata *)(IDATA_RAM_SIZE - 1) ;
    *cp-- = 0;
    *cp-- =  ((uint16)(OSIdle)) / 256;
    OSTsakStackBotton[OS_MAX_TASKS] = cp;
    *cp-- =  ((uint16)(OSIdle)) % 256;
   
    /* 初始化其它优先级的任务堆栈 为其他任务分配堆栈空间*/
    for(i = OS_MAX_TASKS - 1; i > 0; i--)
    {
        *cp-- = 0;
        *cp-- =  ((uint16)(TaskFuction)) / 256;
        OSTsakStackBotton = cp;
        *cp-- =  ((uint16)(TaskFuction)) % 256;
    }
    /* 允许中断 */
    Os_Enter_Sum = 1;
    OS_EXIT_CRITICAL();
    /* 函数返回优先级最高的任务 */
}
其实也就是为每一个任务分配一个空间。
这个时候定时器会开始计时了。当函数返回的时候,由于SP指向的是ID0的位置
所以函数就会跳转到TaskA函数处运行
void TaskA(void)
{
    while (1)
    {
        OSWait(K_TMO,5);
    }
}
进入函数之后就会一直在while循环中。不断调用OSWait函数做延时,
接下来我们再看看它又做了些什么工作
 uint8 OSWait(uint8 typ, uint8 ticks)

{
    OSWaitTick[OSTaskID] = ticks;               /* 设置超时时间         */
                                                /* 可以优化寄存器的使用  */
    switch(typ)
    {
    case K_SIG:                                 /* 等待信号,即挂起自己  */
        OSWaitTick[OSTaskID] = 0;               /* 没有超时处理         */
        OSClearSignal(OSTaskID);                /* 任务进入等待状态     */
        OSSched();                              /* 运行下一个任务       */
        return SIG_EVENT;
    case K_TMO:                                 /* 等待超时,即延时一段时间 */
        OS_ENTER_CRITICAL();
        while (OSWaitTick[OSTaskID] != 0)       /* 判断超时时间是否到   */
        {
            OSClearSignal(OSTaskID);            /* 任务进入等待状态     */
            OSSched();                          /* 运行下一个任务       */
        }
        OS_EXIT_CRITICAL();
        return TMO_EVENT;
    case (K_TMO | K_SIG):                       /* 等待信号(挂起自己)直到超时  */
                                                /* 别的任务或中断可以恢复它 */
        OS_ENTER_CRITICAL();
        if (OSWaitTick[OSTaskID] == 0)          /* 判断超时时间是否到   */
        {
            return TMO_EVENT;
        }
        OSClearSignal(OSTaskID);                /* 任务进入等待状态     */
        OS_EXIT_CRITICAL();
        OSSched();                              /* 运行下一个任务       */
        if (OSWaitTick[OSTaskID] != 0)
        {
            OSWaitTick[OSTaskID] = 0;
            return SIG_EVENT;
        }
        return TMO_EVENT;
    default:
        OSWaitTick[OSTaskID] = 0;
        return NOT_OK;
    }
}
这个是内核函数来的,包含在OS_core.c中,靠定时器0中断调用OSTimeTick函数来
使 OSWaitTick[OSTASKID] -- 直至OSWaitTick[OSTASKID]为0,当不为零的时候
 OSClearSignal(OSTaskID)会使任务进入等待状态,OSSched()函数将会将下一个
 就绪任务运行,现在我们就来看看如何将该任务取消运行进入等待状态的

 void OSClearSignal(uint8 TaskId)
 {
  //  判断当前任务是否在许可范围内否则返回
    if (TaskId < OS_MAX_TASKS)
    {
  // 进入临界状态
        OS_ENTER_CRITICAL();
#if OS_MAX_TASKS < 9
/* 将OSTaskRuning的相应位复位
现在我们来看看OSTaskRuning的定义
#if OS_MAX_TASKS < 9
uint8 OSTaskRuning = 0xff;
#else
uint16 OSTaskRuning = 0xffff; 它被定义为一个全局变量每一位代表一个任务的状态
为1表示任务就绪,为零表示任务挂起。
uint8 const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00};
*/
        // 总任务数要是小于9,那么用UINT可以表示,则可以直接用TABLE表清零
        OSTaskRuning &= ~OSMapTbl[TaskId];
#else
    // 任务总数大于9,若是当前任务小于8,那么将低8位操作就可以了
        if (TaskId < 8)
        {
            ((uint8 *)(&OSTaskRuning))[LOW_BYTE] &= ~OSMapTbl[TaskId];
        }
    // 任务若是大于8,那么必须操作高8位
        else
        {
            ((uint8 *)(&OSTaskRuning))[HIGH_BYTE] &= ~OSMapTbl[TaskId & 0x07];
        }
#endif
        OS_EXIT_CRITICAL();
    }
}
这个我也可以理解,呵呵。现在我们似乎要看看调度算法了
明天一起再看看调度算法的执行过程,一步一步理下去,我相信就可以理解了,呵呵

while (OSWaitTick[OSTaskID] != 0)       /* 判断超时时间是否到   */
        {
            OSClearSignal(OSTaskID);            /* 任务进入等待状态     */
            OSSched();                          /* 运行下一个任务       */
        }
因为此处的while循环,所以在OSWaitTick[OSTaskID]不被定时器调用OSTimeTick
清零之前,会一直在此处运行下去,接下来看看OSSched()是如何运行的
 void  OSSched(void)
{
    uint8 temp;

    OS_ENTER_CRITICAL();
#if EN_OS_INT_ENTER > 0
    if (OSIntNesting == 0)              /* 是否是中断中调用 */
    {
#endif

#if OS_MAX_TASKS < 9
                /* 查找处于就绪状态的任务中优先级最高的任务 */
        temp = OSTaskRuning;
        for (OSNextTaskID = 0; OSNextTaskID < OS_MAX_TASKS; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                break;
            }
            temp = temp >> 1;
        }
// 在os_cpu.h 中有如下宏定义 #define  OS_TASK_SW()         OSCtxSw()
   该OSCtxSw()在OS_CPU_A.ASM中定义,用汇编写的,将此处传递的OSNextTaskID作为参数使用

        OS_TASK_SW();                   /* 进行任务调度 */
#else
                /* 查找处于就绪状态的任务中优先级最高的任务 */
        temp = OSTaskRuning % 256;
        for (OSNextTaskID = 0; OSNextTaskID < 8; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                goto TaskSw;
            }
            temp = temp >> 1;
        }

        temp = OSTaskRuning / 256 ;
        for (; OSNextTaskID < OS_MAX_TASKS; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                break;
            }
            temp = temp >> 1;
        }
TaskSw:
        OS_TASK_SW();                   /* 进行任务调度 */
#endif

#if EN_OS_INT_ENTER > 0
    }
#endif
    OS_EXIT_CRITICAL();
}
这个函数都好理解,在没有中断嵌套的情况下,将OSTaskRuning中为1的查找出来,
也就是查找出任务就绪状态的任务ID号并赋给OSNextTaskID,用于在OSCtxSw中使用
看来最本质的东西还不在这里,这里只是查找出来而,而真正操作系统的保存环境
载入新任务的环境的实现方法还得继续追踪了
汇编是俺的弱项,所以这里要查资料一点一点分析了……
得去下载个汇编教程才能继续分析了,今天比较忙,就分析到这里了,呵呵

搞了一份KEIL手册来看汇编,呵呵,现在开始学习OSCtxSW这个函数

RSEG  ?PR?OSCtxSw?OS_CPU_A
OSCtxSw:
    USING 0                       ;使用BANK 0
                                    ;设置标志:任务再次恢复运行时不必恢复所有寄存器
    MOV     DPTR,#OSMapTbl          ;将OSMapTbl的地址作为基址

 //将OSTaskID作为便宜地址,就可以访问到SOMapTbl数组中相对应OSTaskID(也就是当前任务ID)的常量
    MOV     A,OSTaskID             
#if OS_MAX_TASKS < 9     // 判断任务数是否在9以下,也就是看是否使用8BIT的OSFastSwap可以表示
// 小于,那么直接将对应的常量跟OSFastSwap相或,既可将OSFastSwap中相应的位置位
// OSFastSwap相对应任务ID的位为高,则表示有任务调用,否则表示为中断调用
    MOVC    A,@A+DPTR   
    ORL     A,OSFastSwap
    MOV     OSFastSwap,A
#else
// 大于,将ACC的借位标志位CY清零,并将任务ID与8相减,若是任务号小于8,则CY会置为,通过
// 判断CY的高低就可以知道当前任务是否小于8(也就说任务小于8呢,将使用OSFastSwap的低八位)
// (若是任务大于8,那么肯定在OSFastSwap的高八位中表示)
    CLR     C
    SUBB    A,#8
    JC      OSCtxSw_1   ;小于8,跳转到OSCtxSW_1处执行
    MOVC    A,@A+DPTR   ; 大于8,那么将该任务ID减去8之后的值作为偏移量
    ORL     A,OSFastSwap
    MOV     OSFastSwap,A ;将A中的值与OSFastSwap高八位相或
    LJMP    C_OSCtxSw
OSCtxSw_1:
    MOV     A,OSTaskID 
    MOVC    A,@A+DPTR
    ORL     A,OSFastSwap+1 ;与OSFastSwap低八位操作
    MOV     OSFastSwap+1,A
#endif
    LJMP    C_OSCtxSw

 当看这段代码的时候,我十分不解的地方有两个,不过都是因为逻辑能力太弱和
 汇编匮乏引起的,一个就是,如果最大任务大于9且当前任务大于8的时候,常量表中
 只有9个量呀。假如当前任务是12,那么将超出常量表的最大表示范围了,后来才看
 原来用了SUBB后,结果是存放在A中的,也就是说相当于偏移量其实是4,那么,用OSFastSwap的
 高八位与常量表的第四位相或,其实就实现了呀,作者实现得真是非常巧妙,呵呵
 还有一点就是KEIL对双字节变量在内存中的存储方式不了解,后来查看了资料,翻看了调试中的
 汇编代码,知道了KEIL在存放双字节变量的时候,是先放高字节,再放低字节的,所以OSFastSWap+1
 也就可以理解了。

 继续看代码...........跳转到C_OSCtxSw
 貌似这里才是最关键的地方哦,真正的任务堆栈处理就在这里

 这段代码作者有用汇编来写了,为了提高效率

 RSEG  ?PR?C_OSCtxSw?OS_CPU_C
C_OSCtxSw:
    PUSH    Os_Enter_Sum            ;保存关中断计数器
    mov     r2,sp  ;保存堆栈指针(若是OSTaskID = OSNextTaskID)就直接函数返回
   
;     cp1 = (unsigned char idata *)SP +1; 将CP1指向栈顶
    MOV     R0,SP

IF EN_SP2  <> 0
    mov     sp,#(Sp2-1)             ;堆栈指向临时空间,允许“软非屏蔽中断”
ENDIF

    INC     R0
;temp用于保存OSnextTaskID的栈底地址
;     temp = (unsigned char )OSTsakStackBotton[OSNextTaskID+1];
    MOV     A,#LOW (OSTsakStackBotton+01H)
    ADD     A,OSNextTaskID
    MOV     R1,A
    MOV     A,@R1
    MOV     R7,A
;CP2保存当前任务的栈底地址
;     cp2 = OSTsakStackBotton[OSTaskID+1];
    MOV     A,#LOW (OSTsakStackBotton+01H)
    ADD     A,OSTaskID
    MOV     R1,A
    MOV     A,@R1
    MOV     R1,A
;如果当前任务小于下个任务
;     if( OSNextTaskID > OSTaskID)
    MOV     A,OSNextTaskID
    SETB    C
    SUBB    A,OSTaskID
    JC      ?C0001
;     {
;将OSNextTaskID堆栈复制到当前栈顶处
;         while(cp2 != (unsigned char idata *)temp)
;         {
;             *cp1++ = *cp2++;
;         }
    MOV     A,R7
    CLR     C
    SUBB    A,R1
    MOV     R6,A
?C0002:
    MOV     A,@R1
    MOV     @R0,A
    INC     R0
    INC     R1
    DJNZ    R6,?C0002
?C0003:
;         temp = OSTsakStackBotton[OSTaskID+1] - (unsigned char idata *)SP-1;
    MOV     A,#LOW (OSTsakStackBotton+1)
    ADD     A,OSTaskID
    MOV     R1,A
    MOV     A,@R1
    SETB    C
    ;SUBB    A,sp
    SUBB    A,r2
    MOV     R7,A
;SP指向刚移动好的下个任务堆栈的地方
;         SP = (unsigned char )cp1 - 1;
    DEC     R0;
    MOV     SP,R0
;重新给任务分配空间
;         for(i = OSTaskID+1;i < OSNextTaskID+1; i++)
;         {
;             OSTsakStackBotton -= temp;
;         }
    MOV     A,OSNextTaskID
    CLR     C
    SUBB    A,OSTaskID
    MOV     R6,A
    JZ      ?C0005

    MOV     A,#LOW (OSTsakStackBotton)
    ADD     A,OSTaskID
    MOV     R1,A   
    MOV     A,R7
    CPL     A
    INC     A
    MOV     R7,A
?C0004:
    INC     R1
    MOV     A,R7
    ADD     A,@R1   
    MOV     @R1,A
    DJNZ    R6,?C0004
?C0005:
;         OSTaskID = OSNextTaskID;
    MOV     OSTaskID,OSNextTaskID
;         LoadCtx();   
    LJMP    LoadCtx
;     }
?C0001:
; 如果当前任务ID小于下个任务ID
;     if( OSNextTaskID != OSTaskID)
    MOV     A,OSNextTaskID
    XRL     A,OSTaskID
    JZ      ?C000r
;     {
;反向移动堆栈空间
;          cp2--;
;          cp1--;
;         while(cp2 != (unsigned char idata *)temp)
;         {
;             *cp2-- = *cp1--;
;         }
    ;MOV     A,R7
    ;CLR     C
    ;SUBB    A,R1
    ;MOV     R6,A
    mov     a,r0
    clr     c
    subb    a,r7
    mov     r6,a
?C0008:
    DEC     R0
    DEC     R1
    MOV     A,@R0
    MOV     @R1,A
    DJNZ    R6,?C0008
?C0009:
;         temp = OSTsakStackBotton[OSTaskID+1] - (unsigned char idata *)SP-1;
    MOV     A,#LOW (OSTsakStackBotton+01H)
    ADD     A,OSTaskID
    MOV     R1,A
    MOV     A,@R1
    SETB    C
    ;SUBB    A,SP
    SUBB    A,r2
    MOV     R7,A
;         SP = (unsigned char )OSTsakStackBotton[OSNextTaskID+1];
    MOV     A,#LOW (OSTsakStackBotton+01H)
    ADD     A,OSNextTaskID
    MOV     R1,A
    MOV     A,@R1
    MOV     SP,A
;         for(i = OSNextTaskID+1;i < OSTaskID+1; i++)
;         {
;             OSTsakStackBotton += temp;
;         }

    MOV     A,OSTaskID
    CLR     C
    SUBB    A,OSNextTaskID
    JZ      ?C0011

    MOV     R6,A
    MOV     A,#LOW (OSTsakStackBotton)
    ADD     A,OSNextTaskID
    MOV     R1,A   
?C0010:
    INC     R1
    MOV     A,R7
    ADD     A,@R1   
    MOV     @R1,A
    DJNZ    R6,?C0010

?C0011:
;         OSTaskID = OSNextTaskID;       
    MOV   OSTaskID,OSNextTaskID
;         SP--;
    DEC   SP
;     }
?C0007:
;     LoadCtx();
    LJMP  LoadCtx
?C000r:
IF EN_SP2  <> 0
    mov     SP,r2
ENDIF
    LJMP  LoadCtx

这个过程理解了很长时间,现在终于有点眉目,过程大致是这样的
首先呢,比如现在是ID为0的任务正在运行,ID为0的任务堆栈空间是从STACK定义的地址开始的
我这边运行结果来看是0x18地址开始(在OSStart()函数分配的时候决定的)
而其他任务的堆栈空间分配时由ID从大到小,从0XFF往下分配的
我们分配任务堆栈空间完毕之后不是将SP指向了ID0的地址处么。那么这个时候,SP其实就是跟着
任务堆栈空间在走的。所以当下个任务大于当前任务的时候(我们第一次运行,肯定满足这个条件)
那么就会利用*cp1++ = *cp2++将下个任务的堆栈往下移动到当前任务堆栈结束的地方,也就是当前
SP指向的地方。SP我是这么理解的,沿途记录来的地方,换个车站就记录上一个车站的地址
也就是调用一个函数,那么就记录上一个函数的地点,执行完这个函数就返回,当然还记录一些入栈
的量。当函数需要返回的时候呢,就依次弹出相应的值,以便回到来的地方。
说了这么多,回到正题,将SP指向下个任务堆栈的顶部,其实在这里也就是存放了下个任务存放在CODE
中的位置,那么当函数返回的时候,SP会将这个值弹出给PC,就直接飞到了下个任务的地方开始执行了
依次类推。
若是当前任务ID比下个任务ID大其实也是同样的道理,只不是要反方向移动堆栈空间

下面这个函数是恢复下个要执行任务的堆栈的函数了

 RSEG  ?PR?LoadCtx?OS_CPU_A
LoadCtx:
 USING 0
   
    POP     Os_Enter_Sum            ;恢复关中断计数器
                                    ;判断是否需要恢复所有寄存器
    MOV     A,OSTaskID
    CJNE    A,#OS_MAX_TASKS,LoadCtx_0
    SJMP    LoadCtx_2
LoadCtx_0:
    MOV     DPTR,#OSMapTbl
#if OS_MAX_TASKS < 9
    MOVC    A,@A+DPTR
    ANL     A,OSFastSwap
#else
    MOV     R6,OSFastSwap
    CLR     C
    SUBB    A,#8
    JNC     LoadCtx_1
    MOV     R6,OSFastSwap + 1
    MOV     A,OSTaskID
LoadCtx_1:
    MOVC    A,@A+DPTR
    ANL     A,R6
#endif
    JNZ     LoadCtx_2
                                    ;恢复寄存器
    POP     7
    POP     6
    POP     5
    POP     4
    POP     3
    POP     2
    POP     1
    POP     0
    POP     PSW
    POP     DPL
    POP     DPH
    POP     B
    POP     ACC
LoadCtx_2:
                                    ;判断是否需要开中断
    INC     Os_Enter_Sum
    djnz    Os_Enter_Sum,LoadCtx_3
    SET_EA                          ;开中断
LoadCtx_3:
    RET
以上就是其实就是首先将C_OSCtxSw()入栈的Os_Enter_Sum弹出
然后就是判断当前执行时中断所为还是任务所为,我们先来分析任务所为
因为现在确实任务所为,任务所为我们就不需要弹出其他东西了,因为调用
函数的时候,寄存器会帮忙保存一下量,所以我们返回值,相应的值会自动填
到相应的寄存器里面去。中断就暂不做讨论了,等下再来看

我们定时器一直在跳动着,假如时间到了我们任务0延时结束的时间了

前面说到了 OSTickISR(void) interrupt OS_TIME_ISR调用OSTimeTick()会将
OSWaitTick的值减到0,
void  OSTimeTick(void)
{
    uint8 i;

    for (i = 0; i < OS_MAX_TASKS; i++)                
    {
        if (OSWaitTick != 0 )
        {
            OSWaitTick--;
            if (OSWaitTick == 0)
            {
                OSIntSendSignal(i);
            }
        }
    }
}
到0过后,就要调用OSIntSendSignal()函数,再看看具体实现过程

void OSIntSendSignal(uint8 TaskId)

{
    if (TaskId < OS_MAX_TASKS) ;判断任务ID是否有效
    {
        OS_ENTER_CRITICAL();
#if OS_MAX_TASKS < 9
        OSTaskRuning |= OSMapTbl[TaskId]; 任务有原来的挂起进入就绪状态
#else
        if (TaskId < 8)
        {
            ((uint8 *)(&OSTaskRuning))[LOW_BYTE] |= OSMapTbl[TaskId];
        }
        else
        {
            ((uint8 *)(&OSTaskRuning))[HIGH_BYTE] |= OSMapTbl[TaskId & 0x07];
        }
#endif
        OS_EXIT_CRITICAL();
    }
}
这个函数实现的功能就是将原来挂起的任务就绪,然后调用OS_EXIT_CRITICAL函数
       
void OSIntExit(void)
{
    uint8 temp;

    OS_ENTER_CRITICAL();
                /* 中断嵌套处理 */
#if EN_OS_INT_ENTER > 0
    if (OSIntNesting > 0)
    {
        OSIntNesting--;
    }
    if (OSIntNesting == 0)
    {
#endif
       
        Os_Enter_Sum = 0;/* 因为在中断中,所以关中断计数器为0 */
#if OS_MAX_TASKS < 9
                /* 查找处于就绪状态的任务中优先级最高的任务 */
        temp = OSTaskRuning;
        for (OSNextTaskID = 0; OSNextTaskID < OS_MAX_TASKS; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                break;
            }
            temp = temp >> 1;
        }
        OSIntCtxSw();                   /* 进行任务调度 */
#else
                /* 查找处于就绪状态的任务中优先级最高的任务 */
        temp = OSTaskRuning % 256;
        for (OSNextTaskID = 0; OSNextTaskID < 8; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                goto TaskSw;
            }
            temp = temp >> 1;
        }

        temp = OSTaskRuning / 256;
        for (; OSNextTaskID < OS_MAX_TASKS; OSNextTaskID++)
        {
            if ((temp & 0x01) != 0)
            {
                break;
            }
            temp = temp >> 1;
        }
TaskSw:
        OSIntCtxSw();                   /* 进行任务调度 */
#endif

#if EN_OS_INT_ENTER >0
    }
#endif
    OS_EXIT_CRITICAL();
}
该函数就是要查找出处于就绪状态的优先级最好的任务,也就是OSNextTaskID作为参数
传递给OSIntCtxSW()函数,该函数定义在OS_CPU_A.ASM中

RSEG  ?PR?OSIntCtxSw?OS_CPU_A
OSIntCtxSw:
 USING 0
                                        ;是否是优先级最低任务
    MOV     A,#OS_MAX_TASKS
    XRL     A,OSTaskID
    JNZ     OSIntCtxSw_0
                                        ;是则不需要保存所有寄存器
;SP=SP-13-4                             ;4:两层函数调用堆栈,13:寄存器数目
    MOV     A,#(-17)
    ADD     A,SP
    MOV     SP,A
                                        ;跳转到OSCtxSw,同时通知CPU中断处理完成
    MOV     A, #LOW  OSCtxSw
    PUSH    ACC
    MOV     A, #HIGH OSCtxSw
    PUSH    ACC
    RETI
                                        ;需要保存所有寄存器
OSIntCtxSw_0:
;SP=SP-4                                ;4:两层函数调用堆栈
    MOV     A,#0FCH
    ADD     A,SP
    MOV     SP,A
                                        ;设置标志:任务再次恢复运行时需要恢复所有寄存器
    MOV     DPTR,#OSMapTbl
    MOV     A,OSTaskID
#if OS_MAX_TASKS < 9
    MOVC    A,@A+DPTR
    CPL     A
    ANL     A,OSFastSwap
    MOV     OSFastSwap,A   
#else
    CLR     C
    SUBB    A,#8
    JC      OSIntCtxSw_1   
    MOVC    A,@A+DPTR
    CPL     A   
    ANL     A,OSFastSwap
    MOV     OSFastSwap,A
    SJMP    OSIntCtxSw_2
OSIntCtxSw_1:
    MOV     A,OSTaskID
    MOVC    A,@A+DPTR
    CPL     A   
    ANL     A,OSFastSwap+1
    MOV     OSFastSwap+1,A
OSIntCtxSw_2:
#endif
                                        ;跳转到堆栈处理,同时通知CPU中断处理完成
    MOV     A, #LOW  C_OSCtxSw
    PUSH    ACC
    MOV     A, #HIGH C_OSCtxSw
    PUSH    ACC
    RETI

中断调用的时候,寄存器会自动入栈,所以该函数就是要判断出该任务需要恢复寄存器
最后调用C_OSCtxSw()恢复任务寄存器就OK了,大致分下来明白了该OS运行的原理,在继续
分析一段时间,写一篇总结性的文字。

 

文章评论0条评论)

登录后参与讨论
我要评论
0
7
关闭 站长推荐上一条 /2 下一条