《30day自制操作系统》学习笔记06

(今天先将bootpack.c文件分成三个文件,然后又对makefile使用一般规则替代普通生成规则,后又将重复的内容提炼到bootpack.h头文件中,

引入头文件声明函数和宏定义,“”表示文件在同一个文件夹里,<>表示头文件位于i按一起所提供的文件夹里。

makefile中引入一般规则,类似有.*导入python包。

1.GDTR和IDTR详细讲解

汇编指令LGDT:从指定地址中读取6字节数据到GDTR寄存器里。GDTR高32位代表GDT开始地址,低16位的代表段上限。

内存分布

关于为什么MOV AX,[ESP+4]?

开始DWORD[ESP+4]里存放的是段上限(即0x0000ffff),DWORD[ESP+8]存放的是地址(即0x00270000)。按照字节写出来是[FF FF 00 00 00 00 27 00](注意低位放在内存地址小的字节里)。MOV AX,[ESP+4]读取最初的2字节0xffff,MOV [ESP+6],AX将其0xffff写入[ESP+6]里,最后变成[FF FF FF FF 00 00 27 00]。

_load_gdtr:        ; void load_gdtr(int limit, int addr);
        MOV        AX,[ESP+4]        ; limit
        MOV        [ESP+6],AX
        LGDT    [ESP+6]              //将[ESP+6]的内容加载到GDTR
        RET

_load_idtr:        ; void load_idtr(int limit, int addr);
        MOV        AX,[ESP+4]        ; limit
        MOV        [ESP+6],AX
        LIDT    [ESP+6]
        RET

段的8字节信息分别是什么:a、段的大小,由limit_low(2字节)和limit_high(低4位)共20位表示。b、段的起始地址。由基址low_base(2字节)、mid_base(1字节)、high_base(1字节)表示。c、段的管理属性。由access_right(1字节)和limit_high(高4位)共12位。这些信息都存在结构体sd中。具体赋值过程见void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)。

段属性里有Gbit位,当Gbit为1时,段上限表示的数据要×4KB(1页)。

;dsctbl.c
struct SEGMENT_DESCRIPTOR {
    short limit_low, base_low;
    char base_mid, access_right;
    char limit_high, base_high;
};

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
    if (limit > 0xfffff) {
        ar |= 0x8000; /* G_bit = 1 */
        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;
}

12位段数性:12位段数高4位放在limit_high里,低8位放在acess_right里。

其中高四位到386才能使用,由“GD00”构成,G指之前说的Gbit;D指段的模式(1指32位模式,0指16位模式)。低八位具有固定搭配,比如(0x00表示未使用)、(0x92表示系统专用,可读写的段,不可直行)。

2.初始化PIC

PIC(可编程中断控制器programmable interrupt controller)是将8个中断信号集合成一个中断信号的装置。

初始化PIC:

;int.c
void init_pic(void)
/* PIC初始化 */
{
    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连接 */
    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;
}

PIC相对CPU是外部设备,CPU使用OUT指令进行操作。PIC0、PIC1分别指主从PIC。其中端口号位于bootpack.h文件中,ICW1、ICW2、ICW3、ICW4端口号相同,但是有一套输出规则——ICW1之后必须紧跟ICW2可以区分。

PIC的寄存器:IMR(中断屏蔽寄存器,interrupt mask register),8位分别对应8路IRQ信号。如 果某一位为1,对应IRQ信号被屏蔽,PIC忽略该路信号。

ICW(初始化控制数据,initial control word),ICW1于主板配线方式有关、ICW3 与主从连接相关(00000100和2)、ICW4与电气特性有关,是固定值。 ICW2决定IRQ以哪一号中断通知CPU,即CPU之后会调用的显卡BIOS函数 号码。

(CPU允许中断处理后,命令PIC发送2字节数据,PIC发送的数据为:0xcd+0x20(翻译成汇编就是INT 0x20),然后CPU就会调用BIOS的0x20号函数。)

3.编写键盘和鼠标中断调用程序

1.初始化IDT、GDT时,将IDT设定(idt + 0x21——IDT对应的bios函数号码,IDT内存位置;

(int) asm_inthandler21——函数位置;2 * 8——asm_inthandler21属于哪一个段:AR_INTGATE32——设定IDT属性)写入内存中:

;bootpack.c
void init_gdtidt(void)
{
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
    struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) ADR_IDT;
    int i;

    /* GDT偺弶婜壔 */
    for (i = 0; i <= LIMIT_GDT / 8; i++) {
        set_segmdesc(gdt + i, 0, 0, 0);
    }
    set_segmdesc(gdt + 1, 0xffffffff,   0x00000000, AR_DATA32_RW);
    set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);
    load_gdtr(LIMIT_GDT, ADR_GDT);

    /* IDT偺弶婜壔 */
    for (i = 0; i <= LIMIT_IDT / 8; i++) {
        set_gatedesc(idt + i, 0, 0, 0);
    }
    load_idtr(LIMIT_IDT, ADR_IDT);

    /* IDT偺愝掕 */
    set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
    set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
    set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);

    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;
}

2.功能是啥?

;naskfun.nas
        EXTERN    _inthandler21, _inthandler27, _inthandler2c
_asm_inthandler21:
        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

3.编写用于INT ox21的中断处理程序:

;int.c
void inthandler21(int *esp)
/* PS/2僉乕儃乕僪偐傜偺妱傝崬傒 */
{
    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();
    }
}

4.栈

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值