第三章
真正實現多任務前需要了解的寄存器知識
當我們在做中斷的時候會進行除了把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;
}
目前這個函數只是入棧,出棧,任務不會切換,但是這個是任務切換的核心,之後的任務切換都是在這個基礎上得到的。
下一章我們將正式開始實現操作系統的任務部分,包括管理,切換,狀態等。