我记得我之前学习的时候卡在了,这一章,有些时间。我也会尽可能在做者以外描述出来,尽可能易懂些。数组
还记得之前保存的屏幕分辨率信息,咱们是存在了内存里面,而在昨天咱们直接写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,而后就没啦。。。。位运算相关的都写在注释里了