(3)从1开始写一个操作系统

第三章

真正实现多任务前需要了解的寄存器知识

当我们在做中断的时候会进行除了把PC入栈之外的动作,就是会把通用寄存器入栈,这是为什么呢?加入我们正在进行加法运算,使用了ACC寄存器,这时候进入了中断,我们的中断中也使用了ACC寄存器,当中断退出时原来的ACC已经被覆盖了,这样程序就出错了,这种事情在真实情况下是不会发生的。保证这个的方法就是在进入中断前,会把通用寄存器都一一入栈,在退出中断事在一一出栈。

那么为什么普通的函数切换就没有呢?因为编译器认为函数切换都是已知的,因为是程序中写的,并不像中断那样随机,所以函数切换只是根据需要编译器尽量使用不重复的寄存器保存临时变量,除非通用寄存器全部被占用,才会使用堆栈。

我们做的操作系统就是在普通函数中模拟中断的动作,所以我们需要将这个保存通用寄存器的动作补充上。

我们先看一个中断函数

void int0_int (void) interrupt INT0_VECTOR {
    u8 i;
    unsigned int counter = 0xffff;
    EA = 0;
    IE0 = 0;
    for(i=0; i<5; i++)
    {
        counter = 0x0fff;
        while( (~IE0) && (counter--) );
        if(IE0) {
            IE0=0;
        }
        EA = 1;
    }
}

不要管他是干什么用的,因为我也不知道,我们主要看他的汇编

    54: void int0_int (void) interrupt INT0_VECTOR {
    55:     u8 i;
C:0x061C    C0E0     PUSH     ACC(0xE0)
C:0x061E    C0F0     PUSH     B(0xF0)
C:0x0620    C083     PUSH     DPH(0x83)
C:0x0622    C082     PUSH     DPL(0x82)
C:0x0624    C0D0     PUSH     PSW(0xD0)
C:0x0626    75D000   MOV      PSW(0xD0),#0x00
C:0x0629    C000     PUSH     0x00
C:0x062B    C006     PUSH     0x06
C:0x062D    C007     PUSH     0x07
    56:     unsigned int counter = 0xffff;
C:0x062F    900013   MOV      DPTR,#0x0013
C:0x0632    74FF     MOV      A,#0xFF
C:0x0634    F0       MOVX     @DPTR,A
C:0x0635    A3       INC      DPTR
C:0x0636    74FF     MOV      A,#0xFF
C:0x0638    F0       MOVX     @DPTR,A
    57:     EA = 0;
C:0x0639    C2AF     CLR      EA(0xA8.7)
    58:     IE0 = 0;
C:0x063B    C289     CLR      IE0(0x88.1)
    59:     for(i=0; i<5; i++)
C:0x063D    900012   MOV      DPTR,#0x0012
C:0x0640    E4       CLR      A
C:0x0641    F0       MOVX     @DPTR,A
C:0x0642    900012   MOV      DPTR,#0x0012
C:0x0645    E0       MOVX     A,@DPTR
C:0x0646    FF       MOV      R7,A
C:0x0647    7E00     MOV      R6,#0x00
C:0x0649    C3       CLR      C
C:0x064A    EF       MOV      A,R7
C:0x064B    9405     SUBB     A,#0x05
C:0x064D    EE       MOV      A,R6
C:0x064E    6480     XRL      A,#P0(0x80)
C:0x0650    9480     SUBB     A,#P0(0x80)
C:0x0652    5030     JNC      C:0684
    60:     {
    61:         counter = 0x0fff;
C:0x0654    900013   MOV      DPTR,#0x0013
C:0x0657    740F     MOV      A,#0x0F
C:0x0659    F0       MOVX     @DPTR,A
C:0x065A    A3       INC      DPTR
C:0x065B    74FF     MOV      A,#0xFF
C:0x065D    F0       MOVX     @DPTR,A
    62:         while( (~IE0) && (counter--) );
C:0x065E    A289     MOV      C,IE0(0x88.1)
C:0x0660    B3       CPL      C
C:0x0661    5012     JNC      C:0675
C:0x0663    900013   MOV      DPTR,#0x0013
C:0x0666    74FF     MOV      A,#0xFF
C:0x0668    75F0FF   MOV      B(0xF0),#0xFF
C:0x066B    120538   LCALL    C?ILDIX(C:0538)
C:0x066E    AFF0     MOV      R7,B(0xF0)
C:0x0670    FE       MOV      R6,A
C:0x0671    EF       MOV      A,R7
C:0x0672    4E       ORL      A,R6
C:0x0673    70E9     JNZ      C:065E
    63:         if(IE0) {
C:0x0675    308902   JNB      IE0(0x88.1),C:067A
    64:             IE0=0;
C:0x0678    C289     CLR      IE0(0x88.1)
    65:         }
    66:         EA = 1;
C:0x067A    D2AF     SETB     EA(0xA8.7)
    67:     }
C:0x067C    900012   MOV      DPTR,#0x0012
C:0x067F    E0       MOVX     A,@DPTR
C:0x0680    04       INC      A
C:0x0681    F0       MOVX     @DPTR,A
C:0x0682    80BE     SJMP     C:0642
    68: }
C:0x0684    D007     POP      0x07
C:0x0686    D006     POP      0x06
C:0x0688    D000     POP      0x00
C:0x068A    D0D0     POP      PSW(0xD0)
C:0x068C    D082     POP      DPL(0x82)
C:0x068E    D083     POP      DPH(0x83)
C:0x0690    D0F0     POP      B(0xF0)
C:0x0692    D0E0     POP      ACC(0xE0)
C:0x0694    32       RETI    

他会把一些寄存器压入占中,退出前又会从栈中弹出,但是我们发现他并没有把所有的寄存器都压入栈中,这是因为编译器在编译的时候已经知道了这个中断函数会使用哪些寄存器,所以只把中断中使用到的,会覆盖原来值的寄存器入栈了。

作为我们编写的操作系统没有办法像编译器一样提前知道任务都是用了哪些寄存器,所以只能把可能会被改变的寄存器都压入栈中。

我们先来实现一个任务切换函数

void OS_TASK_SW(void)
{
    EA=0;

#pragma asm
    PUSH     ACC
    PUSH     B
    PUSH     DPH
    PUSH     DPL
    PUSH     PSW
    MOV      PSW,#00H
    PUSH     AR0
    PUSH     AR1
    PUSH     AR2
    PUSH     AR3
    PUSH     AR4
    PUSH     AR5
    PUSH     AR6
    PUSH     AR7
#pragma endasm
    os_task_stack_top = SP;
//切换任务栈
    SP = os_task_stack_top;
#pragma asm
    POP      AR7
    POP      AR6
    POP      AR5
    POP      AR4
    POP      AR3
    POP      AR2
    POP      AR1
    POP      AR0
    POP      PSW
    POP      DPL
    POP      DPH
    POP      B
    POP      ACC
#pragma endasm

    EA=1;
}

目前这个函数只是入栈,出栈,任务不会切换,但是这个是任务切换的核心,之后的任务切换都是在这个基础上得到的。

下一章我们将正式开始实现操作系统的任务部分,包括管理,切换,状态等。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章