读书笔记《30天自制操作系统》day06
1. 鼠标及键盘的响应功能,因为用到中断所以必须自32位保护模式下设置GDT和IDT,以前在asmhead中设置时用汇编代码设置,现在用C操作。2. GDT,IDT的补充知识,图片来自赵炯博士linux0.11内核分析书(1)实模式下寻址方式是:物理地址=段值*16+偏移地址(2)保护模式下也是用(段值:偏移量)形式表示地址,但是段与以前的段是不一样的,实模式下的段值仍可以看做是一个地
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函数了。
更多推荐
所有评论(0)