30天自制操做系統-5

我記得我之前學習的時候卡在了,這一章,有些時間。我也會盡可能在做者以外描述出來,儘可能易懂些。數組

還記得之前保存的屏幕分辨率信息,咱們是存在了內存裏面,而在昨天咱們直接寫320這樣的數字,萬一等到後來調分辨率,就尷尬了。因此咱們來讀取咱們以前寫的吧。函數

binfo_scrnx = (short *) 0x0ff4;
    binfo_scrny = (short *) 0x0ff6;
    binfo_vram = (int *) 0x0ff8;
    xsize = *binfo_scrnx;
    ysize = *binfo_scrny;
    vram = (char *) *binfo_vram;

因爲以前就是保存在這些地址的,主要是注意一下類型,是short類型的也就是一個word工具

結構體的導入

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

struct BOOTINFO *binfo;

init_palette();
binfo = (struct BOOTINFO *) 0x0ff0;
xsize = (*binfo).scrnx;
ysize = (*binfo).scrny;
vram = (*binfo).vram;

注意看一下咱們定義的結構體,都是由必定順序的,是由於咱們在對這個結構體進行初始化的時候是按照0x0ff0來的,會依次向後讀取內存數據來填充結構體變量值。學習

指針玩兒法

init_screen(binfo->vram, binfo->scrnx, binfo->scrny);

由於會經常(*binfo).scrnx 這裏給了一個語法糖,這樣的狀況下用->就好啦。spa

顯示一個字符

以前顯示過字符,可是是調用BIOS自帶的功能來調用的。既然能填寫像素的方式畫出一個巨型,觸類旁通嘛,咱們想畫啥均可以,固然也能夠畫一個字出來。操作系統

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];
        if ((d & 0x80) != 0) { p[0] = c; }
        if ((d & 0x40) != 0) { p[1] = c; }
        if ((d & 0x20) != 0) { p[2] = c; }
        if ((d & 0x10) != 0) { p[3] = c; }
        if ((d & 0x08) != 0) { p[4] = c; }
        if ((d & 0x04) != 0) { p[5] = c; }
        if ((d & 0x02) != 0) { p[6] = c; }
        if ((d & 0x01) != 0) { p[7] = c; }
    }
    return;
}

來看下代碼,首先遍歷16此,是由於十六行。 而後d分別與0x80,0x40,0x20..等分別進行與操做,這些數這樣看,看不出來,若是你寫出二進制你就看的比較明白了 0x80->1000 000 0x40->0100 0000 能夠發現實際上是二進制挨着往右移動後的值。而後來講說怎麼控制的座標,首先vram是顯卡地址開始,而後(y + i)控制的是每行的位置, * xsize的緣由是一行又xsize寬度,x上以後則定位到具體某一行上,最後加x的緣由就是,在該行,以x的位置開始顯示字符。設計

讀取字符庫

固然像上面那樣寫,成功的顯示了字符,一般講,字符庫會有不少不少,那麼多藝術設計,那麼多種類的語言,須要別人幫助咱們去設計,因此咱們從外部來讀取最好。因此接下來讀取一個字符庫會。指針

首先的是須要把這個txt文件編譯到咱們的操做系統中去,做者使用的是makefont工具,他解釋道,其實就是定義了一個tag的感受,叫作_hankanku 這個,而後這個TAG下定義了那4096個字節。來細看一下,makefile裏面的文件,調試

clipboard.png

能夠看到會先把咱們的hankaku.txt製做成kankaku.obj,而且在48行,傳入了一個參數_hankaku(這個參數就是拿來給咱們在C語言中用的標記,固然你能夠改爲其餘的名字,可是必定不能把 ‘_’ 去掉,由於這是連接的意思),有了這個hankaku.obj,咱們會把他作成bootpack.bim。而後是bootpack.hrb,最後是haribote.sys,再寫入待haribote.img中去。
因此咱們在C語言中能夠名正言順的來寫這個變量啦。code

extern char hankaku[4096];
    putfont8(binfo->vram, binfo->scrnx,  8, 8, COL8_FFFFFF, hankaku + 'A' * 16);
    putfont8(binfo->vram, binfo->scrnx, 16, 8, COL8_FFFFFF, hankaku + 'B' * 16);
    putfont8(binfo->vram, binfo->scrnx, 24, 8, COL8_FFFFFF, hankaku + 'C' * 16);
    putfont8(binfo->vram, binfo->scrnx, 40, 8, COL8_FFFFFF, hankaku + '1' * 16);
    putfont8(binfo->vram, binfo->scrnx, 48, 8, COL8_FFFFFF, hankaku + '2' * 16);
    putfont8(binfo->vram, binfo->scrnx, 56, 8, COL8_FFFFFF, hankaku + '3' * 16);

拿到這個字符庫變量後就好辦啦,接下來就是對他進行讀取,因爲C語言中會把'A'解釋位0x41因此做者直接就寫成了 hankaku + 'A',最後X16的緣由就是一個字符16個字節,第N個字符就是從 X16開始。
因爲剛纔那個仍是有點麻煩,又繼續簡化了一下

void putfonts8_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;
    }
    return;
}

其實比較好理解,原理就是在以前的putfont8函數之上封裝了一個函數,負責解析成相似與剛纔寫了那麼多重複的代碼的功能。

打印調試

寫程序過程當中,最重要的就是看到某個值的變化狀況,因此這裏咱們要作這樣一個功能。sprintf就是往指定內存中寫數據,而後咱們再利用咱們剛寫的putfonts8_asc來輸出這個地址的值,就達到了輸出的做用啦。

顯示鼠標

既然能顯示出來一個字符,那麼按照原理來顯示一個鼠標箭頭也就是理所應當啦。

void init_mouse_cursor8(char *mouse, char bc)
/* ƒ}ƒEƒXƒJ[ƒ\ƒ‹‚ð€」õi16x16j */
{
    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;
            }
        }
    }
    return;
}

咱們定義了一個數組,讓這個數組內部的數據看起來像鼠標指針,而後利用這些數據填入不一樣的顏色,繪製最終的鼠標。

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

再根據剛纔填好的顏色,放到顯卡中去。

三四參數決定,這個鼠標的寬高,五六決定所在位置,mcursor:鼠標顏色形狀。bxsize 鼠標寬度。

void init_gdtidt(void)
{
    struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000;
    struct GATE_DESCRIPTOR    *idt = (struct GATE_DESCRIPTOR    *) 0x0026f800;
    int i;

    // 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);
    load_gdtr(0xffff, 0x00270000);

    // IDT的初始化
    for (i = 0; i < 256; i++) {
        set_gatedesc(idt + i, 0, 0, 0);
    }
    load_idtr(0x7ff, 0x0026f800);

    return;
}

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

void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
    if (limit > 0xfffff) {
        ar |= 0x8000; /* G_bit = 1 */   // 0x8000 => 1000 0000 0000 0000  讓ar的第一個必定是1
        limit /= 0x1000;  
    }
    sd->limit_low    = limit & 0xffff;  // 只要limit低16位
    sd->base_low     = base & 0xffff; // 只要base低16位
    sd->base_mid     = (base >> 16) & 0xff;  // 首先右移16位,而後與ff按位於,獲得接下來8位
    sd->access_right = ar & 0xff; // 取後8位
    sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);    // ((limit >> 16) & 0x0f)  只要第五位(十六進制), 要ar中第四位  
    sd->base_high    = (base >> 24) & 0xff;  // 取第七八位(十六進制)
    return;
}

首先是初始化8192全部地址,而後讓 1,2分別變爲ffffffff和7ffff,而後就沒啦。。。。位運算相關的都寫在註釋裏了

相關文章
相關標籤/搜索