您的位置:首页 > 其它

30天自制操作系统第四天

2015-03-01 18:20 197 查看
1:用C语言实现内存的写入

上篇成功的实现了黑屏,今天我们要争取把画面上显示一些东西。想要画些东西的话,只要往VRAM里写点东西就可以了,这里我们先使用汇编语言写入内存,

_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

在C语言中如果使用write_mem8函数,就会跳转到_write_mem8,此时参数指定的数字存放在内存里,分别是

第一个数字的存放地址:[ESP+4]

第二个数字的存放地址:[ESP+8]

第三个数字的存放地址:[ESP+12]

第四个数字的存放地址:[ESP+16]

以下略

其中ECX为32位寄存器。如果与C语言联合使用的话,有的寄存器可以自由使用,有的寄存器不能自由使用,能自由使用的只有EAX、ECX、EDX这三个。其它只能使用其值,不能改变其值。

for (i = 0xa0000; i <= 0xaffff; i++) {

write_mem8(i, 15); /* MOV BYTE [i],15 */

}

在C文件中在每个显卡内存中写入15,第15中颜色是白色,所以我们运行出来,是白板。

2.条纹图案

接着我们再此稍微修改一下,就可以显示条纹图案了。

for (i = 0xa0000; i <= 0xaffff; i++) {

write_mem8(i, i & 0x0f); /* MOV BYTE [i],15 */

}

让特定位变为1的功能,可以用或;

让特定位变为0的功能,可以用与;

让特定为翻转,可以用异或;

运行后可以发现,显现为条纹状。

3.挑战指针

C语言中没有直接向内存地址写入的语句,实际上不是C语言的缺陷,在C语言中我们可以使用指针来向内存地址写入。

修改程序如下:

char p; / p用于BYTE型地址

for (i = 0xa0000; i <= 0xaffff; i++) {

p = i; /* 代入地址

*p = i & 0x0f;

}

程序运行结果和上面调用汇编程序显示成功一样。

注意:p代表的是内存地址,如0x123,而*p代表的是改地址存储的东西,如[0x123]

4.色彩的设定

在显示完条带图形后,我们现在想要做个操作系统的界面,在之前,我们首先要做的就是色号的设定。由于我们采用了8位颜色模式,也就是0~255的数,这个非常的小,一般指定颜色都是用#ffffff一类的数,用六位十六进制数,就是24位来指定延时,8位数完全不够,因此我们在这里就采用了调色板,我们可以随意指定0~255的数字所对应的颜色的。如果默认设定,则0号颜色代表#000000,15号颜色就是#ffffff。这里作者随便设定了几个操作系统需要用到的颜色号。

注意:关于调色板,我在博客中发现了一些详细的解释。调色板只有图片的颜色小于等于256色的时候才有,16位高彩和24位32位真彩是没有调色板的。调色板的存在的意义只是在当初486以前为了节省空间的一种采用索引的压缩算法,现在没有人这种东西。调色板是为了节约空简所用的,相当于一个索引。只有16位以下的才用调色板,真彩色不用调色板。

让我们来看看下面的例子。

有一个长宽各为200个象素,颜色数为16色的彩色图,每一个象素都用R、G、B三个分量表示。因为每个分量有256个级别,要用8位(bit),即一个字节(byte)来表示,所以每个象素需要用3个字节。整个图象要用200×200×3,约120k字节,可不是一个小数目呀!如果我们用下面的方法,就能省的多。

因为是一个16色图,也就是说这幅图中最多只有16种颜色,我们可以用一个表:表中的每一行记录一种颜色的R、G、B值。这样当我们表示一个象素的颜色时,只需要指出该颜色是在第几行,即该颜色在表中的索引值。举个例子,如果表的第0行为255,0,0(红色),那么当某个象素为红色时,只需要标明0即可。

让我们再来计算一下:16种状态可以用4位(bit)表示,所以一个象素要用半个字节。整个图象要用200×200×0.5,约20k字节,再加上表占用的字节为3×16=48字节.整个占用的字节数约为前面的1/6,省很多吧?

这张R、G、B的表,就是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(Look Up Table),似乎更确切一些。Windows位图中便用到了调色板技术。其实不光是Windows位图,许多图象文件格式如pcx、tif、gif等都用到了。所以很好地掌握调色板的概念是十分有用的。

有一种图,它的颜色数高达256×256×256种,也就是说包含我们上述提到的R、G、B颜色表示方法中所有的颜色,这种图叫做真彩色图(true color)。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用R、G、B三个分量字节表示,而不采用调色板技术。原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24位(因为总共有224种颜色,即调色板有224行),和直接用R,G,B三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个256×256×256×3个字节的大调色板。所以真彩色图直接用R、G、B三个分量表示,它又叫做24位色图。来自:http://blog.csdn.net/applenob/article/details/19134911

上面的话是我直接摘自该博客的文字;

这次我们给bootpack.c加入了很多的代码,我们下面来一点点的分析。

首先,我们加入了两个函数

void init_palette(void);

void set_palette(int start, int end, unsigned char *rgb);

第一个是初始化调色板,第二个是设置调色板;

初始化调色板的具体内容如下:

static unsigned char table_rgb[16 * 3] = {

0x00, 0x00, 0x00, /* 0:黒 */

0xff, 0x00, 0x00, /* 1:亮红 */

0x00, 0xff, 0x00, /* 2:亮绿 */

0xff, 0xff, 0x00, /* 3:亮黄 */

0x00, 0x00, 0xff, /* 4:亮蓝*/

0xff, 0x00, 0xff, /* 5:亮紫 */

0x00, 0xff, 0xff, /* 6:前亮色 */

0xff, 0xff, 0xff, /* 7:白 */

0xc6, 0xc6, 0xc6, /* 8:亮灰 */

0x84, 0x00, 0x00, /* 9:暗红*/

0x00, 0x84, 0x00, /* 10:暗緑 */

0x84, 0x84, 0x00, /* 11:暗黄 */

0x00, 0x00, 0x84, /* 12:暗青 */

0x84, 0x00, 0x84, /* 13:暗紫 */

0x00, 0x84, 0x84, /* 14:浅暗色 */

0x84, 0x84, 0x84 /* 15:暗灰色 */

};

set_palette(0, 15, table_rgb);

return;

}

程序开头定义了一个静态数组,之所以用静态数组的原因是静态数组很大的精简了机器码,因为char a[3]={1,2,3},相当于char a[3] ,a[1] =1;a[2]=2;a[3]=3;因为每个赋值语句就消耗了三个字节,而使用静态赋值,相当于使用DB只需要3个字节就可以了。

程序接下来调用了,set_palette函数,我们来看看它是如何定义的:

void set_palette(int start, int end, unsigned char *rgb)

{

int i, eflags;

eflags = io_load_eflags(); /* 记录中断许可标志的值

io_cli(); /* 关闭中断

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;

}

程序的核心部分是调用io_out8函数,这个函数是干什么用的,我们具体解释一下:

_io_out8: ; void io_out8(int port, int data);

MOV EDX,[ESP+4] ; port

MOV AL,[ESP+8] ; data

OUT DX,AL

RET

io_out8 是一个汇编代码。它是往指定设备发送数据的函数;

为什么不用C语言来完成呢?

我们的CPU通过管脚与内存相连,完成计算和存储功能呢,但计算机还有很多外设,CPU还需要处理外设的响应,从外设获得信息,如通过声卡发出声音,通过网卡获得网络信息等。因此CPU与设备相连,那么CPU就有与这些设备交流的指令,向设备发送电信号的是OUT指令,获得设备电信号的是IN指令。为了区分不同的设备,我们也要使用设备号码,port。

其中设置调色板的访问步骤如下:

1.首先在一连串的访问中屏蔽中断;

2.将想要设定的调色板号码写入0x03c8,紧接着,按RGB的顺序写书0x03c9。如果设定下一个调色板,则省略调色板号码,在按照顺序写入0x03c9就行了。

3.如果想要读出当前调色板状态,首先将调色板号码写入0x03c7,再从0x03c9中读取3次,读出的顺序就是RGB,如果要继续读出下一个调色板,同样也是省略调色板号码,按RGB的顺序读出。

4.如果执行了CLI,那么最后要执行STI。

现在,我们来看看程序,程序首先向0x03c8写入0,代表要写入0号调色板,然后在循环中,不断向0x03c9写入RGB数据,直到写完16个调色板。

下面我们来看一些还没有解决的代码:eflags = io_load_eflags()这个函数时什么呢,我们来看一看:

_io_load_eflags: ; int io_load_eflags(void);

PUSHFD ; PUSH EFLAGS

POP EAX

RET

该段程序的意思简明来写就是让EAX=EFLAGS;在EAX中存储中断许可标志值。这是第一个有返回值的函数,根据C语言规约,EAX的值会被看做函数的返回值。

_io_cli: ; void io_cli(void);

CLI

RET

禁止中断,将中断许可标志置零。

_io_store_eflags: ; void io_store_eflags(int eflags);

MOV EAX,[ESP+4]

PUSH EAX

POPFD ; POP EFLAGS

RET

程序的意思是EFLAGS = EAX。还原中断许可标志。

那么EFLAGS这个寄存器是什么呢,它是由FALGS的16为寄存器扩展而来的32位寄存器,FLAGS是存储进位标志等标志的寄存器。

5.绘制矩形

颜色备齐了,下面我们就可以画画了,画面上有320*200=64000个像素,右下点的坐标为(319,199)那么像素坐标(x,y)对应的VRAM地址计算为:0xa0000+x+y*320;

本程序就多了一个新的函数:

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声明是为了便于理解。

接下来作者就接着绘制了简单的系统界面。今天的内容就到此结束了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息