30天自制操做系统-4

首先啊,今天的内容比较多,但你们能够直接看书最后面,一想到作完后就能看到这个界面上有咱们画上去的东西就开心纳,朝着这个尽头,开始吧。数组

首先就是用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会报错。

相关文章
相关标签/搜索