1. 鼠標及鍵盤的響應功能,因爲用到中斷所以必須自32位保護模式下設置GDT和IDT,以前在asmhead中設置時用匯編代碼設置,現在用C操作。
2. GDT,IDT的補充知識,圖片來自趙炯博士linux0.11內核分析書
(1)實模式下尋址方式是:物理地址=段值*16+偏移地址
(2)保護模式下也是用(段值:偏移量)形式表示地址,但是段與以前的段是不一樣的,實模式下的段值仍可以看做是一個地址,但保護模式下段值只是一個索引。
(3)保護模式下有全局段表位置保存在GDTR這個新寄存器中,每個表項就是一個段描述符(圖六),這是個描述段信息的結構,保存段索引的寄存器叫段選擇子,它其實就是一個寄存器但有圖五所描述的結構。
圖一:新的寄存器們
圖二:保護模式下的尋址,分段情況和分段分頁情況
圖三:段選擇子選擇GDT與LDT關係
圖四:GDT與LDT關係
圖五:段選擇子(段選擇符)TI標記位功能見圖三
圖六:全局段描述符GDT
struct SEGMENT_DESCRIPTOR
{
short limit_low,base_low;
char limit_high,base_high;
};
圖七:LDT,IDT結構
struct GATE_DESCRIPTOR
{
short offset_low,selector;
char dw_count,access_right;
short offset_high;
};
圖八:IDT原理
圖九:GDT與IDT關係
3. gdt和idt初始化C代碼
void init_gdtide()
{
sturct SEGMENT_DESCRIPTOR* gdt = (sturct SEGMENT_DESCRIPTOR*)0x00270000;//gdt表的首位到0x0027ffff結束
struct GATE_DESCRIPTOR* idt=(struct GATE_DESCRIPTOR*)0x0026f800;//idt表的首位到0x0026ffff結束
int i;
for(i=0;i<8192;i++)//段選擇子用12位表示段值,故最大8192
{
set_segmdesc(gdt+i,0,0,0);//全部初始化爲0
}
//0號段保留NULL Description
set_segmdesc(gdt+1,0xffffffff,0x00000000,0x4092);//os數據段
set_segmdesc(gdt+2,0x0007ffff,0x00280000,0x409a);//os代碼段,bootback.c在這裏
load_gdtr(0xfff,0x00270000);//將GDT表首地址加載到GDTR
for(i=0;i<256;i++)
{
set_gatedesc(idt+i,0,0,0);
}
load_idtr(0x7ff,0x0026f800);
return ;
}
/*
* 設置GDT
* sd:GDT表首址
* limit:段限長
* base:段指向代碼或數據段地址
* ar:段管理屬性 00000000(0x00):未使用的記錄表
* 10010010(0x92):os用,可讀寫段,不可執行,ring0
* 10011010(0x9a):os用,可執行段,可讀不可寫,ring0
* 11110010(0xf2):app用,可讀寫,不可執行,ring3
* 11111010(0xfa):app用,可執行,可讀不可寫,ring3
*/
void set_segmdesc(struct SEGMENT_DESCRIPTOR* sd,unsigned int limit,int base,int ar)
{
if(limit>=0xffff)
{
ar!=0x8000;
limit/=0x1000;
}
sd->limit_low = limit&0xffff;
sd->base_low = base&0xffff;
sd->base_mid = (base>>16)&0xff;
sd->access_right = ar&0xff;
sd->limit_high = ((limit>>16)&0x0f)|((ar>>8)&0xf0);
sd->base_high = (base>>24)&0xff;
return;
}
void set_gatedesc(struct GATE_DESCRIPTOR* gd,int offset,int selector,int ar)
{
gd->offset_low = offset&0xffff;
gd->selector = selector;
gd->dw_count = (ar>>8)&0xff;
gd->access_right = ar&0xff;
gd->offset_high = (offset>>16)&0xffff;
return ;
}
4. PIC的初始化,計算機上有兩個PIC,從PIC連到了主PIC的IRQ2引腳,PIC的初始化一般使用都是固定的,對於PIC來說,PIC是外部設備所以cpu使用out指令將設置外設信息寫到外設對應寄存器裏就可以了。
PIC的寄存器們:(1)IMR是中斷屏蔽寄存器,該寄存器爲8位,每個PIC都有,如果某一位設置爲1則表示PIC忽略該路中斷信號。(2)ICW是初始化控制數據,ICW有四個ICW1與ICW4寄存器保存的數據是固定的。ICW3是主從連接設定其實也是固定的從PIC是連着主PIC的IRQ2引腳所以值爲00000100。ICW2是我們需要設定的表示IRQ以哪一號中斷通知CPU。
void init_pic()
{
io_out8(PIC0_IMR,0xff);//禁止所有中斷
io_out8(PIC1_IMR,0xff);
io_out8(PIC0_ICW1,0X11);//固定
io_out8(PIC0_ICW2,0X20);//IRQ1~7中斷信號由INT20~27發出
io_out8(PIC0_ICW3,1<<2);//固定
io_out8(PIC0_ICW4,0X01);//固定
io_out8(PIC1_ICW1,0X11);//固定
io_out8(PIC1_ICW2,0X28);//IRQ8~15中斷信號由INT28~2f發出
io_out8(PIC1_ICW3,2);//固定
io_out8(PIC1_ICW4,0X01);//固定
io_out8(PIC0_IMR,0Xfb);
io_out8(PIC1_IMR,0xff);
return;
}
5. 鼠標連接到了IRQ12上其中斷響應號爲0x2c,鍵盤是IRQ1中斷響應號是0x21 。中斷處理代碼執行結束後應調用IRETD,該指令需要使用匯編編寫,並且在處理中斷時需要保存寄存器值,並在結束後恢復,所以調用C語言編寫的中斷處理程序代碼要包裝一下。
extern _inthandler21
_asm_inthadler21:
push es
push ds
pushad
mov eax,esp
push eax
mov ax,ss
mov ds,ax
mov es,ax
call _inthandler21
pop eax
popad
pop ds
pop es
iretd
void inthandler21(int* esp)
{
//...
}
6. IDT的設置如下
set_gatedesc(idt+0x21,(int)asm_inthandler21,2*8,AR_INTGATE32);
上述代碼中中斷代碼的偏移量由asm_inthandler21指出,而所在段爲2號,而二號端是GDT中指出的
set_segmdesc(gdt+1,0xffffffff,0x00000000,0x4092);/*系統專用,不可執行,可讀寫數據段*/
set_segmdesc(gdt+2,0x0007ffff,0x00280000,0x409a);/*系統專用,可執行,只讀代碼段*/
可執行代碼段2中保存了bootpack.c代碼,在asmhead.nas中將bootpack.c代碼memcpy到了0x00280000這裏
BOTPAK EQU 0x00280000
MOV ESI,bootpack
MOV EDI,BOTPAK
MOV ECX,512*1024/4
CALL memcpy
同樣在asmhead.nas中有,跳入HairMain的代碼,也是跳到2段中
JMP DWORD 2*8:0x0000001b
這樣當產生鍵盤中斷時能調用inthandler21函數了。