自制操作系统(五)

    这一节要实现中断。

    中断是有固定格式的,要顺应着CPU来,格式如下:
 

typedef struct SEGMENT_DESCRIPTOR{
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
} SegmentDescriptor;

typedef struct GATE_DESCRIPTOR{
    short offect_low, selector;
    char dw_count, access_right;
    short offset_high;
} GateDescriptor;


    对了,就是要设计GDI和IDT,依次初始化它就行。

    值得注意的是,在其他段都可以设为0,但是在GDT第一段和第二段要设不同的值,尤其是段2,它的大小是512KB,地址为0x280000,正好是我们主程序的代码段。
 

int init_GDT(){
    SegmentDescriptor *GDT = (SegmentDescriptor*) 0x00270000;
    int i;
    for(i=0; i<8192; i++){
        set_GDT(GDT+i, 0,0,0);
    }
    set_GDT(GDT+1, 0xffffffff, 0x00000000, 0x4092);
    set_GDT(GDT+2, 0x0007ffff, 0x00280000, 0x409a);

    return 0;
}

int init_IDT(){
    GateDescriptor *IDT = (GateDescriptor*) 0x0026f800;
    int i;
    for(i=0; i<256; i++){
        set_IDT(IDT+i, 0,0,0);
    }
    return 0;
}

    另一个就是指定GDTR这个48位寄存器,从 指定的地hi读取6个字节,然后赋值给GDTR寄存器,就OK了。
 

_load_GDTR: ; void load_GDTR(int limit, int addr)
    mov ax, [esp+4]
    mov [esp+6], ax
    LGDT [ESP+6]
    ret

    按照CPU的要求,要将段的信息归结成8个字节写入内存,分别是段大小,起始地址,段的管理属性(禁止写入,禁止执行,系统专用等),因此我们定义了这样的结构体。
  
    段地址用32位来表示,用2+1+1字节来表示,使用移位运算符往各个字节里填入相应的数值,之所以要分3段,主要是为了与80286时代的程序兼容。

    下一步就是初始化PIC(programmable interrupt contorller, 可编程中断控制器)。是一个将8个中断信号集合成一个中断信号的装置。但外部设备并不仅仅只有8个,因此只能通过增加PIC来处理更多的中断信号。



    IMR是interrupt maskregister,中断屏蔽寄存器,8位分别对应8路IRQ信号,某一位是1,则屏蔽该路信号。如果那路没有设备的话,应该就是屏蔽掉。
    ICW是initial control word,初始化控制数据,共有4个,分别编号1~4,但只有ICW2有用,决定了以哪一号中断通知CPU。而中断号往往是系统指令,就是CPU可以执行的那种。鼠标是IRQ12,键盘是IRQ1。

    在32位模式下,段与偏移地址的结合就是简单的拼接结合了,段基地址为2个字节8位,能表示65535个段,但是由于设计原因,段寄存器低3位不可用,因此只能表示8192个段。

    如果可以设8192个段,每一个段基地址都要用8位,你至少要存着这8192*8=65535=64K的数据,这64K的数据就是GDT。

    然后你将这个GDT存放于内存中的某一处,然后让GDTR寄存器指向它,设定就完成了。

   IDT是中断向量表,它记录了0~255个中断号码,你可以借此使用回调函数来实现鼠标移动,键盘打字等功能。同样需要绑定到CPU里的寄存器中。

    我设定GDT在0x270000~0x27ffff,内存这一块地方并没有使用。IDT也是一样。然后将GDT的基址上限访问权限都设为了0.
    然后我将段一的地址设为0,上限设为0xffffffff,正好是4GB,代表cpu所能管理的全部内存。段属性设置了一下。段2大小是512K,地址为0x280000,在boot我们设定的内存地址正好是这个数,也就是说,段2是存储整个代码的段,我们的main是直接粘贴在boot代码下面的,因此执行完boot就会直接执行main代码没问题,cpu总是一条一条往下执行命令。除非到达一个死循环。

   记住了,我定义的GDT是在2700开始,执行代码是在2800开始,中间正好差65535个字节开存放GDT。

   在这里我说一下从开机到关机的内存使用方法,从开机时,是16位的实模式,可供使用的内存仅1MB并被BIOS完全掌握,前640k是基本内存,中间128k作为显存,后264k由bios使用,正好1MB。
    在基本内存部分,前1kb是中断向量表,由bios分配,每一项占领4字节,共256项;中间256字节为bios数据区;后面部分是自由内存区,我们可以直接使用,其中的0x00007c00~0x00007dff为引导程序加载区,我们就是把IPL定义在了这里,然后直接蹦到0x00280000,启动boot,然后紧接着就是main函数了。由于1MB = 0x0010 0000,因此我们直接蹦到1MB之外,就不用考虑bios内存占用的问题了。
    而在后面bios使用的部分,前32k是显卡bios使用,中间16k由IDE控制器bios使用,后64k由系统bios使用。

    而如何将自己写的IPL放到这个0x00007c00,关键是看你用的是什么介质,一般把IPL放到软盘也好,光盘也好,硬盘也好,它们都会有启动区,放到这里,然后BIOS会把启动区里的512字节放到这个地址,然后开始运行它了。IPL最好写存储介质相关,例如将磁盘上的内容复制到内存上,最后一个跳转到boot,这个ipl也就算执行完毕了,而boot的主要作用是从16位变为32位,同时在变为32位之前执行一些只有16位才能执行的函数,就是这样,然后紧接着就是main函数,进入32位系统,就是这样。















 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值