Day05

今天在开头讲了用c语言接收启动信息,之后是各种图形界面的优化,最后稍微提及了一点GDT和IDT的知识。

一些问题与感想

C语言指针的底层用法(续)

今天提到了在基础类型之外,我们还可以定义结构体,通过转换成结构体类型指针,来一次性读入更多内存,或者是把不同内存解释为不同的意义。
但是不太清楚的一点是结构体会存在对齐问题的,因此是不是也不能随意地去用结构体指针去指地址?

用位图来显示图形

今天提到的图形界面内容,全部是用位图来展示的,这样做确实可以比较方便地在像素级别上显示图像,也方便看到最后的成果。

GDT与IDT

GDT(global descriptor table),用于把内存分为多个段之后,记录段大小、起始地址以及管理属性。这样每个段都可以看作起始地址0来处理,分给不同的程序。
IDT(interrupt descriptor table),用于记录中断号对应的函数。

进入正题

接收启动信息

harib02a

从内存中读入相关的启动信息,放到c语言里

char *vram;
int xsize, ysize;
short *binfo_scrnx, *binfo_scrny;
int *binfo_vram;

init_palette();

binfo_scrnx = (short *)0x0ff4;
binfo_scrny = (short *)0x0ff6;
binfo_vram = (int *)0x0ff8;

xsize = *binfo_scrnx;
ysize = *binfo_scrny;
vram = (char *)*binfo_vram;

试用结构体、试用箭头记号

harib02b
harib02c

把启动信息封装称一个结构体,这样做到一次性载入所有启动信息。

struct BOOTINFO
{
    char cyls, leds, vmode, reserve;
    short scrnx, scrny;
    char *vram;
};

void HariMain(void)
{
    char *vram;
    int xsize, ysize;
    struct BOOTINFO *binfo;

    init_palette();

    binfo = (struct BOOTINFO *)0x0ff0;
    xsize = binfo->scrnx;
    ysize = binfo->scrny;
    vram = binfo->vram;

显示字符

harib02d

用位图的方式显示一个字符A,其实就是看哪些地方应该为黑,哪些地方应该为白罢了。

void HariMain(void)
{
    static char font_A[16] =
    {
        0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
        0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
    };

    struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;

    init_palette();
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
    putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);


    for(;;)
    {
        io_hlt(); /* 执行naskfunc.nas的_io_hlt */
    }
}

void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
    int i;
    char *p, d; /*data*/
    for(i = 0; i < 16; ++i)
    {
        p = vram + (y + i) * xsize + x;
        d = font[i];
        int bit = 0x80;
        int cnt = 0;
        for(; bit != 0x00; bit >>= 1)
        {
            if((d & bit) != 0) p[cnt] = c;
            ++cnt;
        }
    }
}

其中我用了for循环代替了书上对应位与的操作,效果应该是一样的。

增加字体、显示字符串

harib02e
harib02f

我们导入了一个现成的字体库,然后对这个字体库用某个软件进行编译连接,就可以在c语言里用上了。
当然,光显示单个字符太累了,所以可以想办法做一个显示字符串的函数,

void HariMain(void)
{
    extern char hankaku[4096];

    struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;

    init_palette();
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
    putfont8_asc(binfo->vram, binfo->scrnx, 8, 8, COL8_FFFFFF, "ABC 321");
    putfont8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
    putfont8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");


    for(;;)
    {
        io_hlt(); /* 执行naskfunc.nas的_io_hlt */
    }
}

void putfont8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
    extern char hankaku[4096];
    for(; *s != 0x00; ++s)
    {
        putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
        x += 8;
    }
}

显示变量值

harib02g

我们可以把数字转化成字符串,这样就能在屏幕上打印出来了。需要用到<stdio.h>里的sprintf

char s[105];
sprintf(s, "scrnx = %d", binfo->scrnx);
putfont8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

熟悉C语言还是很好理解的

显示鼠标指针

harib02h

同样的鼠标位图,不需要太多解释

void HariMain(void)
{
    struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;
    extern char hankaku[4096];

    init_palette();
    init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
    putfont8_asc(binfo->vram, binfo->scrnx, 8, 8, COL8_FFFFFF, "ABC 321");
    putfont8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
    putfont8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");

    char s[105];
    sprintf(s, "scrnx = %d", binfo->scrnx);
    putfont8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);

    int mx = (binfo->scrnx - 16) / 2; /* 使得鼠标坐标在中央 */
    int my = (binfo->scrny - 28 - 16) / 2;
    char mcursor[256];
    init_mouse_cursor8(mcursor, COL8_008484);
    putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);

    for(;;)
    {
        io_hlt(); /* 执行naskfunc.nas的_io_hlt */
    }
}

void init_mouse_cursor8(char *mouse, char bc)
{
    static char cursor[16][16] =
    {
        "**************..",
        "*OOOOOOOOOOO*...",
        "*OOOOOOOOOO*....",
        "*OOOOOOOOO*.....",
        "*OOOOOOOO*......",
        "*OOOOOOO*.......",
        "*OOOOOOO*.......",
        "*OOOOOOOO*......",
        "*OOOO**OOO*.....",
        "*OOO*..*OOO*....",
        "*OO*....*OOO*...",
        "*O*......*OOO*..",
        "**........*OOO*.",
        "*..........*OOO*",
        "............*OO*",
        ".............***"
    };
    int x, y;
    for(y = 0; y < 16; ++y)
    {
        for(x = 0; x < 16; ++x)
        {
            if(cursor[y][x] == '*')
                mouse[y * 16 + x] = COL8_000000;
            if(cursor[y][x] == 'O')
                mouse[y * 16 + x] = COL8_FFFFFF;
            if(cursor[y][x] == '.')
                mouse[y * 16 + x] = bc;
        }
    }
}

void putblock8_8(char *vram, int vxsize, int pxsize, int pysize, int px0, int py0, char *buf, int bxsize)
{
    int x, y;
    for(y = 0; y < pysize; ++y)
    {
        for(x = 0; x < pxsize; ++x)
        {
            vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
        }
    }
}

不过得吐槽一下字符串不给\0的位置,编译器一堆warning。。。

GDT与IDT的初始化

harib02i

已经介绍过GDT与IDT了,直接看代码

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

struct GATE_DESCRIPTOR
{
    short offset_low, selector;
    char dw_count, access_right;
    short offset_high;
};

void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

void init_gdtidt()
{
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *)0x00270000;
    struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *)0x0026f800;
    int i;
    // init gdt
    for(i = 0; i < 8192; ++i)
    {
        set_segmdesc(gdt + i, 0, 0, 0);
    }
    set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
    set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a); // 为bootpack.hrb准备
    load_gdtr(0xffff, 0x00270000);

    // init idt
    for(i = 0; i < 256; ++i)
    {
        set_gatedesc(idt + i, 0, 0, 0);
    }
    load_idtr(0x7ff, 0x0026f800);
}


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


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

我们把GDT放到了0x270000 ~ 0x27ffff的地方,IDT放到了0x26f800 ~ 0x26ffff
明天会对这些属性进一步讲解,今天先到这里了。