(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;
}

目前這個函數只是入棧,出棧,任務不會切換,但是這個是任務切換的核心,之後的任務切換都是在這個基礎上得到的。

下一章我們將正式開始實現操作系統的任務部分,包括管理,切換,狀態等。

 

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