我記得我之前學習的時候卡在了,這一章,有些時間。我也會盡可能在做者以外描述出來,儘可能易懂些。數組
還記得之前保存的屏幕分辨率信息,咱們是存在了內存裏面,而在昨天咱們直接寫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裏面的文件,調試
能夠看到會先把咱們的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[ƒ\ƒ‹‚ð€」õi16x16j */ { 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,而後就沒啦。。。。位運算相關的都寫在註釋裏了