首先啊,今天的内容比较多,但你们能够直接看书最后面,一想到作完后就能看到这个界面上有咱们画上去的东西就开心纳,朝着这个尽头,开始吧。数组
首先就是用C语言写内存,若是用汇编写的话,就是用[]这种来进行内存读写,其实咱们就是要在汇编里封装一些函数让C去调用。这里忽然想起了,作手游,为了热更新,也是这个套路。函数
_write_mem8: ; void write_mem8(int addr, int data); MOV ECX,[ESP+4] ; [ESP+4]‚中存放的是地址,将其读入ECX MOV AL,[ESP+8] ; [ESP+8]‚中存放的是数据,将其读入AL MOV [ECX],AL RET
首先,写了个函数叫write_mem8,其意是向内存地址中写入8个字节,因此用的是AL。
函数自己也很简单,做者在这里作了说明,只能用EAX,ECX,EDX,由于用于记忆C语言编译后的机器语言中。
[INSTRSET "i486p"] ; 486‚指定用于486,固然也不是说其余的就不能用。操作系统
int i; for (i = 0xa0000; i <= 0xaffff; i++) { write_mem8(i, i & 0x0f); } for (;;) { io_hlt(); }
注释也省略了,由于我以为到了C语言,应该没有什么语法看不懂的了,主要是思惟。昨天已经介绍过0xa00000-0xaffff是使用这个模式,对这部份内存操做就是对最终屏幕绘制出来的内容密切相关。线程
条纹图案指针
这里巧妙的运用了AND运算符,因为每次遍历出来一个字节,8位,与0x0f与运算,也就是0x0f有低四位可是高四位是0。因为i、每隔16进一次位,因此每隔16次反复进行此运算,而这16此每一次的计算,保留低4位,由于f的存在,高四位变成0由于0x0f中0的存在。code
指针
理所固然C语言的指针就是拿来玩儿内存的,多是读者为了让咱们更了解原理,而后写了以前的read_mem8这个函数。若是直接对i进行取地址,那他固然不知道这个地址是个什么类型,固然也就不知道赋值多少位过去,因此写了个char *p
指针在专栏里做者讲了一大堆,若是能看懂,哦,做者讲的确实对。但我以为对于初学者,去学一些C语言的书,多看些打印的值,更直观些。说个我也会忘的知识吧 a[1] 1[a]是同样的意思。游戏
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);ip
// 就算写在同一个源文件里,在使用以前必须声明一下,若是函数在前面则不用内存
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);it
void HariMain(void)
{
int i; // 声明变量,这里的变量i是32位整型 char *p; // 变量p是Byte[...]用的地址 init_palette(); // 设定调色板 p = (char *) 0xa0000; // 指定地址 for (i = 0; i <= 0xffff; i++) { p[i] = i & 0x0f; } for (;;) { io_hlt(); }
}
void init_palette(void)
{
static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc6, 0xc6, 0xc6, 0x84, 0x00, 0x00, 0x00, 0x84, 0x00, 0x84, 0x84, 0x00, 0x00, 0x00, 0x84, 0x84, 0x00, 0x84, 0x00, 0x84, 0x84, 0x84, 0x84, 0x84 }; set_palette(0, 15, table_rgb); return; // C语言中的static char语句只能用于数据,至关于汇编中的DB指令
}
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags; eflags = io_load_eflags(); // 记录中断许可标志的值 io_cli(); // 将中断许可标志置为0,禁止中断 io_out8(0x03c8, start); for (i = start; i <= end; i++) { io_out8(0x03c9, rgb[0] / 4); io_out8(0x03c9, rgb[1] / 4); io_out8(0x03c9, rgb[2] / 4); rgb += 3; } io_store_eflags(eflags); // 复原中断许可标志 return;
}
代码有点长,不过声明就占了好些行数。记得无论之前是学C仍是其余高级语言对于静态变量的说法都是,静态存储区,特有的存储区。具备全局性等,但如今若是从汇编的角度来看,好像会对他产生全新的认识。记住static就至关于在汇编使用DB同样。做者说这个方式a[0] = 1;占三个字节的缘由多是赋值语句转换位汇编是mov ....多是这样致使的,因此改为static就能避免不少这样的操做。
咱们初始咱们想要的颜色后,如今要设定到调色板里去。虽然做者说让咱们先不理解io_out这个函数,但我以为有点奇怪,一直往一个地址里面写值,不久把以前的覆盖了吗?仍是我写一个,就会有相应的东西把值取走,而后保存下来?
有两个函数CLI与STL这两个配合起来意思是我在作这段代码的过程当中不准任何人打搅,感受有点像高级语言的线程锁。
前面几个函数相对简单,我直接来搞后两个新函数。若是以前知道push和pop的来理解也就不难,PUSHFD先把eflags的值存在栈里面,而后又放到EAX里面去(不知道为何做者没讲,要想返回函数的值,放到EAX里面就行了,因此在C语言咱们能够接收到一个返回值)。因为这个值到C那边跑了一圈,又传回来了,因此咱们再入栈,在POPFD又回到了EFLAGS里边儿。
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
int x, y; for (y = y0; y <= y1; y++) { for (x = x0; x <= x1; x++) vram[y * xsize + x] = c; } return;
}
看到这里更带劲儿了,由于画出了个正方形,来看看这个函数,思想也比较简答, 就是块数组,指定区域用指定值,而后对应的就是指定屏幕变成指定颜色。像这种常值的值,定义成define就好。
void HariMain(void)
{
char *vram; int xsize, ysize; init_palette(); vram = (char *) 0xa0000; xsize = 320; ysize = 200; boxfill8(vram, xsize, COL8_008484, 0, 0, xsize - 1, ysize - 29); boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 28, xsize - 1, ysize - 28); boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize - 27, xsize - 1, ysize - 27); boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 26, xsize - 1, ysize - 1); boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize - 24, 59, ysize - 24); boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize - 24, 2, ysize - 4); boxfill8(vram, xsize, COL8_848484, 3, ysize - 4, 59, ysize - 4); boxfill8(vram, xsize, COL8_848484, 59, ysize - 23, 59, ysize - 5); boxfill8(vram, xsize, COL8_000000, 2, ysize - 3, 59, ysize - 3); boxfill8(vram, xsize, COL8_000000, 60, ysize - 24, 60, ysize - 3); boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize - 4, ysize - 24); boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize - 4); boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize - 3, xsize - 4, ysize - 3); boxfill8(vram, xsize, COL8_FFFFFF, xsize - 3, ysize - 24, xsize - 3, ysize - 3); for (;;) { io_hlt(); }
}
这段代码运行后,简直是太棒啦,感受有点想咱们的桌面了,但其实仍是前面的知识,只是巧妙的利用了boxfill8这个函数。
大概解释一下吧,第一个boxfill8是绘制桌面的感受,一张蓝绿色的低,从左上角到离右下角y29,x1的位置,至于为何做者在x方向少了一个1不太理解,多是留出边缘吧。第二个是绘制一个桌面与状态栏的灰色分割线。而后是蓝色分割线,以此。。。还有一个办法看每一行到底作了哪些东西,能够一句代码一句代码的注释,而后看效果。实话说这个操做系统是真的小,像我作exe,apk,ipa什么的随便弄个空项目出来就好几百K,甚至用游戏引擎的话,随随便便就是几MB,简直不敢想象,那个在咱们看来是个空项目,内部到底有多少代码。
写在最后我记得我在编译这一天代码的时候出现过一个问题, 不过最后也找到了,就是路径问题,请你们注意一下,tolsetz_toolsharibote这个路径下的haribote.rul文件内容的路径必定也要修改,否则make run会报错。