30天自制操作系统——第六天
今天我们继续加把劲,争取把GDT和IDT这两个搞定。
一、整理文件
如果把所有代码都写在一个c文件里,可能会显得比较臃肿,且不利于我们后续对代码的修改,因此我们进行以下分割:
因此,我们还需要对makefile进行修改,利用makefile的特殊规则进行处理,如下:
OBJS_BOOTPACK = bootpack.obj naskfunc.obj hankaku.obj graphic.obj dsctbl.obj \
int.obj
TOOLPATH = ../z_tools/
INCPATH = ../z_tools/haribote/
MAKE = $(TOOLPATH)make.exe -r
NASK = $(TOOLPATH)nask.exe
CC1 = $(TOOLPATH)cc1.exe -I$(INCPATH) -Os -Wall -quiet
GAS2NASK = $(TOOLPATH)gas2nask.exe -a
OBJ2BIM = $(TOOLPATH)obj2bim.exe
MAKEFONT = $(TOOLPATH)makefont.exe
BIN2OBJ = $(TOOLPATH)bin2obj.exe
BIM2HRB = $(TOOLPATH)bim2hrb.exe
RULEFILE = $(TOOLPATH)haribote/haribote.rul
EDIMG = $(TOOLPATH)edimg.exe
IMGTOL = $(TOOLPATH)imgtol.com
COPY = copy
DEL = del
#默认
default :
$(MAKE) img
#操作
ipl10.bin : ipl10.nas Makefile
$(NASK) ipl10.nas ipl10.bin ipl10.lst
asmhead.bin : asmhead.nas Makefile
$(NASK) asmhead.nas asmhead.bin asmhead.lst
hankaku.bin : hankaku.txt Makefile
$(MAKEFONT) hankaku.txt hankaku.bin
hankaku.obj : hankaku.bin Makefile
$(BIN2OBJ) hankaku.bin hankaku.obj _hankaku
bootpack.bim : $(OBJS_BOOTPACK) Makefile
$(OBJ2BIM) @$(RULEFILE) out:bootpack.bim stack:3136k map:bootpack.map \
$(OBJS_BOOTPACK)
# 3MB+64KB=3136KB
bootpack.hrb : bootpack.bim Makefile
$(BIM2HRB) bootpack.bim bootpack.hrb 0
haribote.sys : asmhead.bin bootpack.hrb Makefile
copy /B asmhead.bin+bootpack.hrb haribote.sys
haribote.img : ipl10.bin haribote.sys Makefile
$(EDIMG) imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl10.bin len:512 from:0 to:0 \
copy from:haribote.sys to:@: \
imgout:haribote.img
# 其他指令
#%为Makefile规则通配符,一般用于规则描述,表示所有的目标文件及其依赖文件
#$* 表示取得 %及其之前的所有字符!!!
%.gas : %.c Makefile
$(CC1) -o $*.gas $*.c
%.nas : %.gas Makefile
$(GAS2NASK) $*.gas $*.nas
%.obj : %.nas Makefile
$(NASK) $*.nas $*.obj $*.lst
# 运行程序
img :
$(MAKE) haribote.img
run :
$(MAKE) img
$(COPY) haribote.img ..\z_tools\qemu\fdimage0.bin
$(MAKE) -C ../z_tools/qemu
install :
$(MAKE) img
$(IMGTOL) w a: haribote.img
clean :
-$(DEL) *.bin
-$(DEL) *.lst
-$(DEL) *.gas
-$(DEL) *.obj
-$(DEL) bootpack.nas
-$(DEL) bootpack.map
-$(DEL) bootpack.bim
-$(DEL) bootpack.hrb
-$(DEL) haribote.sys
src :
$(MAKE) clean
-$(DEL) haribote.img
然后,我们还需将之前的声明还有结构体等等,都放在头文件当中:
二、制作中断程序
PIC初始化
**PIC:**可编程中断控制器,由于一般来说cpu只能处理一个中断。因此,为方便能够同时处理多个处理中断程序,设计了PIC将8个中断信号集合成一个中断信号的装置。
PIC会同时监管这8个处理器管脚,一旦有要处理的,就会将会唯一的输出管脚设置成on进行中断。
除此之外,还设计了主从PIC模式,将中断信号拓展至16个,其中利用IRQ2连接从PIC。从PIC的中断需要经过主PIC同意后,cpu才会执行。
以下,将给出代码,代码中有详细的注释,可参考一下(仅代表个人理解),int.c:
#include "bootpack.h"
/*PIC0 ==> 主PIC PIC1 ==> 从PIC
IMR ==> 中断屏蔽寄存器,8位分别对应着8路IRQ信号,置为1则该路的中断会被屏蔽;
ICW ==> ICW1与ICW4 是与PIC主板配线,电气特性有关;
ICW3与主从有关;
ICW2可用于设定中断号,即将PIC0-15与int 20-2f对应起来,进行统一管理。
*/
void init_pic(void)
{
io_out8(PIC0_IMR, 0xff); //禁止所有中断
io_out8(PIC1_IMR, 0xff); //禁止所有中断
io_out8(PIC0_ICW1, 0x11); //边沿触发模式
io_out8(PIC0_ICW2, 0x20); //IRQ0-7由int20-27接收;
io_out8(PIC0_ICW3, 1 << 2); //PIC1由IRQ2连接;由于硬件已经规定了主的2号连接从,因此这里要写入00000100
io_out8(PIC0_ICW4, 0x01); //无缓冲区模式
io_out8(PIC1_ICW1, 0x11); //边沿触发模式
io_out8(PIC1_ICW2, 0x28); //IRQ8-15由int28-2f接收
io_out8(PIC1_ICW3, 2 ); //PIC1由IRQ2连接
io_out8(PIC1_ICW4, 0x01); //无缓冲区模式
io_out8(PIC0_IMR, 0xfb); //11111011 PIC1外全禁止
io_out8(PIC1_IMR, 0xff); //11111111 禁止所有中断
return;
}
//鼠标中断利用IRQ12(int 2c) 键盘中断利用IRQ1(int 21)
void inthandler21(int *esp)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");
for (;;) {
io_hlt();
}
}
void inthandler2c(int *esp)
/* 来自鼠标的中断 */
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 2C (IRQ-12) : PS/2 mouse");
for (;;) {
io_hlt();
}
}
void inthandler27(int *esp)
/* PIC0中断的不完整策略 */
/* 这个中断在Athlon64X2上通过芯片组提供的便利,只需执行一次 */
/* 这个中断只是接收,不执行任何操作 */
/* 为什么不处理?
→ 因为这个中断可能是电气噪声引发的、只是处理一些重要的情况。*/
{
io_out8(PIC0_OCW2, 0x67); /* 通知PIC的IRQ-07(参考7-1) */
return;
}
除此之外,我们还要修改naskdunc,nas ,因为我们完成中断后还要返回的(要用IRET)因此,需要利用汇编进行修改:
GLOBAL _asm_inthandler21, _asm_inthandler27, _asm_inthandler2c
EXTERN _inthandler21, _inthandler27, _inthandler2c
_asm_inthandler21:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS;以下三步是c规定的,必须相同 ss es ds
MOV DS,AX
MOV ES,AX
CALL _inthandler21
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler27:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler27
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler2c:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler2c
POP EAX
POPAD
POP DS
POP ES
IRETD
然后,我们还需要在dsctbl.c中的init_gdtidt中加入如下语句:
/* idt + 0x21 ==> asm_inthandler21注册在idt表的0x21号;
(int) asm_inthandler21 ==> cpu会自动调用21号中断;
2 << 3 ==> asm_inthandler21号程序具体在哪一个段地址空间中,上面已经设定了bootpac.hrb在2号段地址中;最低3位不可用
AR_INTGATE32 ==> 设定位0x008e 代表中断处理的有效设定。
*/
set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 << 3, AR_INTGATE32);
set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 << 3, AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 << 3, AR_INTGATE32);
最后一小部分,我们需要在主程序中进行一些设置:
首先。需要使用io_sti()来将IF设置为1,方便我们能够使用外部中断,然后进行初始化,最后在出程序的末尾,开发PIC的IMR权限,以便调用。。。
void HariMain(void)
{
struct BOOTINFO *binfo = ( struct BOOTINFO *) ADR_BOOTINFO;
char s[40], mcursor[256];
int mx,my;
init_gdtidt();//初始化gdt、idt表
init_pic(); //初始化pic控制器
io_sti();
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
/* 显示鼠标 */
mx = (binfo->scrnx - 16) / 2; /* 计算画面的中心坐标*/
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%d, %d)", mx, my);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
io_out8(PIC0_IMR, 0xf9); /* 开放PIC1和键盘中断(11111001),键盘是IRQ1 */
io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) ,鼠标是IRQ12*/
for(;;)
{
io_hlt();
//自建的hlt函数,源目标程序在naskfunc.nas中,c语言本身没有hlt这个命
}
}
make run:
总结
总算大致搞完这些中断和段的初始化。而且现在我们还能够使用键盘中断了,可喜可贺。但是鼠标还是不能移动。。。。。那就明天加油吧!!