讀書筆記《30天自制操作系統》day06

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函數了。
 

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