暑假果然是滋生懒散的温床. (╯‵□′)╯︵┻━┻

好久不动都忘记之前做到哪里了, 上次好像做到了C语言的引入, 这一节所做的东西都相当轻松, 将会绘制出操作系统的基本界面.

 

  • 绘图的原理

  按照书中所说, 将值写入到显存中就能在屏幕上显示相应的像素, 在asmhead.nas 中有这一段:

 1 CYLS    EQU    0x0ff0    ; 设定启动区    
 2 LEDS    EQU    0x0ff1    
 3 VMODE    EQU    0x0ff2    ; 关于颜色数目的信息,颜色的位数    
 4 SCRNX    EQU    0x0ff4    ; 分辨率 X(Screen X)    
 5 SCRNY    EQU    0x0ff6    ; 分辨率 Y(Screen Y)    
 6 VRAM    EQU    0x0ff8    ; 图像缓冲区的起始地址    
 7 
 8     ORG    0xc200        
 9 
10 
11 
12     MOV    AL,0x13    ; VGA 显卡    
13     MOV    AH,0x00
14     INT    0x10
15     MOV    BYTE [VMODE],8    ; 记录画面模式    
16     MOV    WORD [SCRNX],320
17     MOV    WORD [SCRNY],200
18     MOV    DWORD [VRAM],0x000a0000

 

调用十号中断来指定显卡模式, 后面的几个MOV指令用来储存和显示有关的关键信息(为什么不直接用常量呢?), 在bootpack.c中有

1 struct BOOTINFO { /* 0x0ff0-0x0fff */
2     char cyls; /* 启动区读硬盘读到何处为止 */
3     char leds; /* 启动时LED的状态*/
4     char vmode; /* 显卡模式*/
5     char reserve;
6     short scrnx, scrny; /* 分辨率*/
7     char *vram;
8 };

这里就将结构体指针指向了这些信息, 可以知道VRAM的地址是0xa0000想指定地址写入数据, 当然是用指针, 将VRAM声明为一个数组即可.

作者顺便指出了数组的操作array[index] 仅是一个语法糖, a[i] 等价于*(a + i), 当然也等价于*(i + a), 所以很神奇的, a[i] 和 i[a] 在C语言里是等效的.

 

 

  • 设定调色板

  作者替我们指定了300x200 8位的颜色模式, 如何将 0xffffff 的颜色值映射0~0xfff的空间里? 一一对应显然是不可能的, 事实上8位色彩模式中, 0到0xfff这255中颜色是由我们自己指定的, 譬如说0可以对应红色0xff0000, 这种对应关系显然被储存起来, 这就是调色板(palette),作者给出了16中颜色的对应关系, 其中14号色被我改了, 调色板的数据结构如下(就一个unsigned char数组):

 1     static unsigned char table_rgb[16 * 3] =
 2            {
 3         0x00, 0x00, 0x00,    /*  0:黑色*/
 4         0xff, 0x00, 0x00,    /*  1:亮红*/
 5         0x00, 0xff, 0x00,    /*  2:亮绿*/
 6         0xff, 0xff, 0x00,    /*  3:亮黄*/
 7         0x00, 0x00, 0xff,    /*  4:亮蓝*/
 8         0xff, 0x00, 0xff,    /*  5:亮紫*/
 9         0x00, 0xff, 0xff,    /*  6:浅亮蓝*/
10         0xff, 0xff, 0xff,    /*  7:白色*/
11         0xc6, 0xc6, 0xc6,    /*  8:亮灰*/
12         0x84, 0x00, 0x00,    /*  9:暗红*/
13         0x00, 0x84, 0x00,    /* 10:暗绿*/
14         0x84, 0x84, 0x00,    /* 11:暗黄*/
15         0x00, 0x00, 0x84,    /* 12:暗青*/
16         0x84, 0x00, 0x84,    /* 13:暗紫*/
17         0x5b, 0x9b, 0xd5,    /* 14:浅灰蓝5B,9B,D5*/
18         0x84, 0x84, 0x84    /* 15:暗灰*/
19     };

 

如何让我们设置调色板生效? 作者给出了如下步骤:

《30天自制操作系统》读书笔记(4) 绘图

  这里的0x03c9 是设备端口号, 屏蔽和恢复中断使用CLI和STI两个指令, 在naskfunc.nas里面封装成了函数io_cli(); 和 io_stl(), CLI(Clear interrupt flag) 将中断标志(interrupt flag)置零, STI(Set interrupt flag)则是将中断标志置一. 设置调色板的时候需要屏蔽中断, 但不知道当前的状态是什么, 所以在CLI之前要保存中断标志的状态. 中断标志保存在32位寄存器EFLAGS上, 由16位的FLAGS扩展而来, 下图应该是FLAGS 的示意图:中断标志位于第9位. 0 位是进位标志, 2是奇偶标志, 4 是辅助进位标志, 6是零标志, 7是符号标志, 8,10额…忘了, 11是溢出标志…使用指令PUSHFD和POPFD储存和恢复整个EFLAGS的状态, 封装为io_load_eflags()函数和io_store_eflags(eflags)函数.

《30天自制操作系统》读书笔记(4) 绘图

 

对端口的读写要使用IN 指令和 OUT指令, 注意这里的IN是读取而OUT是写入.

结合以上的知识, 设置调色板的函数如下:

 1 void set_palette(int start, int end, unsigned char *rgb)
 2 {
 3     int i, eflags;
 4     eflags = io_load_eflags();    /*记录中断标志的值 */
 5     io_cli();                     /*禁止中断 */
 6     io_out8(0x03c8, start);
 7     for (i = start; i <= end; i++)
 8            {
 9         io_out8(0x03c9, rgb[0] / 4);    
10         io_out8(0x03c9, rgb[1] / 4);
11         io_out8(0x03c9, rgb[2] / 4);
12         rgb += 3;
13     }
14     io_store_eflags(eflags);    /* 恢复中断标志*/
15     return;
16 }

 

有个疑惑便是rgb[0] 为什么要除以四再写入, 如果去掉这一句颜色会变得很灰…有谁知道的还望告知…(/4不就是往左移动两位?)

 

  • 绘制界面

  我们写了一个叫做boxfill8的函数用来绘制矩形, 并用它来构建我们的基本界面:

二维屏幕和一维的地址换算关系如下: addr = 0xa0000 + x + y * 320

void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

参数的意思分别是: 显存地址, 换算参数(屏幕宽度), 颜色, 矩形的四个点.

使用以下的代码画出界面:

 1 void init_screen8(char *vram, int x, int y)
 2 {
 3     boxfill8(vram, x, COL8_008484,  0, 0,          x -  1, y - 29);    //绘制桌面填充灰蓝色
 4     boxfill8(vram, x, COL8_C6C6C6,  0, y - 28, x -  1, y - 28);    //第一条阴影
 5     boxfill8(vram, x, COL8_FFFFFF,  0, y - 27, x -  1, y - 27);    //绘制第二条阴影
 6     boxfill8(vram, x, COL8_C6C6C6,  0, y - 26, x -  1, y -  1);    //绘制任务栏主体
 7 
 8     boxfill8(vram, x, COL8_FFFFFF,  3, y - 24, 59, y - 24);        //绘制"开始"按钮 周围六条直线 
 9     boxfill8(vram, x, COL8_FFFFFF,  2, y - 24,  2, y -  4);
10     boxfill8(vram, x, COL8_848484,  3, y -  4, 59, y -  4);
11     boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y -  5);
12     boxfill8(vram, x, COL8_000000,  2, y -  3, 59, y -  3);
13     boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y -  3);
14 
15     boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);//绘制状态栏 四条直线 
16     boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
17     boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
18     boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
19     return;
20 }

 

得到的界面如下:

《30天自制操作系统》读书笔记(4) 绘图

屏幕下方左右两个"按钮"放大看:

《30天自制操作系统》读书笔记(4) 绘图《30天自制操作系统》读书笔记(4) 绘图

其实就是在原来的底色上画多了几条直线, 左边的是6条, 右边的是4条.

看情况, 这将会是我们的操作系统的桌面.

 

  • 显示字符

  使用了asmhead.nas 后我们进入了32位模式(似乎是保护模式), 不可以使用int 0x13中断了, 因此要显示字符只能自己画.

作者使用8x16 的矩阵来表示一个字符, 不知道真正的字体文件*.ttf是否用这样的方式储存…

《30天自制操作系统》读书笔记(4) 绘图

换算成16进制是:

《30天自制操作系统》读书笔记(4) 绘图

这样的表示方法难以调整, 所以作者使用了一个文本文档来描画字体, 在这个文本文档里面8是:

char 0x38
........
...**...
..*..*..
.*....*.
.*....*.
.*....*.
..*..*..
...**...
..*..*..
.*....*.
.*....*.
.*....*.
..*..*..
...**...
........
........

 

然后伟大的作者又搬出了自己的"编译器", makefont.exe, (╯‵□′)╯︵┻━┻), 神烦的"别人家的编译器", 这也意味这Makefile又要改动了…如何画出字符和字符串呢?

写一个很厉害的putfont8函数:

 1 void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
 2 {
 3     int i;
 4     char *p, d /* data */;
 5     for (i = 0; i < 16; i++) {
 6         p = vram + (y + i) * xsize + x;
 7         d = font[i];
 8         if ((d & 0x80) != 0) { p[0] = c; }
 9         if ((d & 0x40) != 0) { p[1] = c; }
10         if ((d & 0x20) != 0) { p[2] = c; }
11         if ((d & 0x10) != 0) { p[3] = c; }
12         if ((d & 0x08) != 0) { p[4] = c; }
13         if ((d & 0x04) != 0) { p[5] = c; }
14         if ((d & 0x02) != 0) { p[6] = c; }
15         if ((d & 0x01) != 0) { p[7] = c; }
16     }
17     return;
18 }

 

让特地的数和字体数据相与,判断特定位是否为1, 很厉害的做法.

显示字符串的函数:

1 void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
2 {
3     extern char hankaku[4096];
4     for (; *s != 0x00; s++) {
5         putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
6         x += 8;
7     }
8     return;
9 }

 

在主函数里调用

1 putfonts8_asc(binfo->vram, binfo->scrnx, 8, 8, COL8_FFFFFF, "ABC 123");
2 putfonts8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "OS.");
3 putfonts8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "OS."); 

 

结果如下:

《30天自制操作系统》读书笔记(4) 绘图

 

  • 显示变量值

为了显示变量值, 作者使用了名为GO的编译器里的sprintf函数,(这货是GO语言的GO吗?), Sprintf函数就是将指定内容格式化输出到字符流中, 然后我们再将这个字符显示出来, 达到显示变量内容的目的.

添加头文件stdio.h, 主函数加入以下语句:

1     int mx = (binfo->scrnx - 16) / 2;        //水平居中
2     int my = (binfo->scrny - 28 - 16) / 2;    //排除任务栏垂直居中
3     sprintf(s, "(%d, %d)", mx, my);
4     putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); 

 

效果如下:

《30天自制操作系统》读书笔记(4) 绘图

 

  • 显示鼠标指针

  鼠标的显示和字符大同小异, 但是这里使用字符来储存而不是二进制, 因为需要填充色, 边缘色和背景色…作者画的鼠标实在难看, 我用的是@BIT_祝威 写好的鼠标, 特此感谢!

 1 /* 16*16 Mouse */
 2     static char cursor[16][16] = {
 3         "*...............",
 4         "**..............",
 5         "*O*.............",
 6         "*OO*............",
 7         "*OOO*...........",
 8         "*OOOO*..........",
 9         "*OOOOO*.........",
10         "*OOOOOO*........",
11         "*OOOOOOO*.......",
12         "*OOOO*****......",
13         "*OO*O*..........",
14         "*O*.*O*.........",
15         "**..*O*.........",
16         "*....*O*........",
17         ".....*O*........",
18         "......*........."
19     };

 

 事前将上面的图案处理成颜色信息cursor[], 再使用putblock8_8函数将鼠标画出来, 效果如下:

《30天自制操作系统》读书笔记(4) 绘图

 这次就做到这里…按书中的指示将代码分装成几个文件, 下面是当前改动的代码:

 

 这次的大部分代码在graphic.c

  1 #include "bootpack.h"
  2 
  3 void init_palette(void)
  4 {
  5     static unsigned char table_rgb[16 * 3] =
  6            {
  7         0x00, 0x00, 0x00,    /*  0:黑色*/
  8         0xff, 0x00, 0x00,    /*  1:亮红*/
  9         0x00, 0xff, 0x00,    /*  2:亮绿*/
 10         0xff, 0xff, 0x00,    /*  3:亮黄*/
 11         0x00, 0x00, 0xff,    /*  4:亮蓝*/
 12         0xff, 0x00, 0xff,    /*  5:亮紫*/
 13         0x00, 0xff, 0xff,    /*  6:浅亮蓝*/
 14         0xff, 0xff, 0xff,    /*  7:白色*/
 15         0xc6, 0xc6, 0xc6,    /*  8:亮灰*/
 16         0x84, 0x00, 0x00,    /*  9:暗红*/
 17         0x00, 0x84, 0x00,    /* 10:暗绿*/
 18         0x84, 0x84, 0x00,    /* 11:暗黄*/
 19         0x00, 0x00, 0x84,    /* 12:暗青*/
 20         0x84, 0x00, 0x84,    /* 13:暗紫*/
 21         0x5b, 0x9b, 0xd5,    /* 14:浅灰蓝5B,9B,D5*/
 22         0x84, 0x84, 0x84    /* 15:暗灰*/
 23     };
 24     set_palette(0, 15, table_rgb);
 25     return;
 26 
 27     /* static char,储存数据, 相当于DB*/ 
 28 }
 29 
 30 void set_palette(int start, int end, unsigned char *rgb)
 31 {
 32     int i, eflags;
 33     eflags = io_load_eflags();    /*记录中断标志的值 */
 34     io_cli();                     /*禁止中断 */
 35     io_out8(0x03c8, start);
 36     for (i = start; i <= end; i++)
 37            {
 38         io_out8(0x03c9, rgb[0] / 4);    
 39         io_out8(0x03c9, rgb[1] / 4);
 40         io_out8(0x03c9, rgb[2] / 4);
 41         rgb += 3;
 42     }
 43     io_store_eflags(eflags);    /* 恢复中断标志*/
 44     return;
 45 }
 46 
 47 void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
 48 {
 49     int x, y;
 50     for (y = y0; y <= y1; y++) {
 51         for (x = x0; x <= x1; x++)
 52             vram[y * xsize + x] = c;
 53     }
 54     return;
 55 }
 56 
 57 void init_screen8(char *vram, int x, int y)
 58 {
 59     boxfill8(vram, x, COL8_008484,  0, 0,          x -  1, y - 29);    //绘制桌面填充灰蓝色
 60     boxfill8(vram, x, COL8_C6C6C6,  0, y - 28, x -  1, y - 28);    //第一条阴影
 61     boxfill8(vram, x, COL8_FFFFFF,  0, y - 27, x -  1, y - 27);    //绘制第二条阴影
 62     boxfill8(vram, x, COL8_C6C6C6,  0, y - 26, x -  1, y -  1);    //绘制任务栏主体
 63 
 64     boxfill8(vram, x, COL8_FFFFFF,  3, y - 24, 59, y - 24);        //绘制"开始"按钮 周围六条直线 
 65     boxfill8(vram, x, COL8_FFFFFF,  2, y - 24,  2, y -  4);
 66     boxfill8(vram, x, COL8_848484,  3, y -  4, 59, y -  4);
 67     boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y -  5);
 68     boxfill8(vram, x, COL8_000000,  2, y -  3, 59, y -  3);
 69     boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y -  3);
 70 
 71     boxfill8(vram, x, COL8_848484, x - 47, y - 24, x -  4, y - 24);//绘制状态栏 四条直线 
 72     boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y -  4);
 73     boxfill8(vram, x, COL8_FFFFFF, x - 47, y -  3, x -  4, y -  3);
 74     boxfill8(vram, x, COL8_FFFFFF, x -  3, y - 24, x -  3, y -  3);
 75     return;
 76 }
 77 
 78 void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
 79 {
 80     int i;
 81     char *p, d /* data */;
 82     for (i = 0; i < 16; i++) {
 83         p = vram + (y + i) * xsize + x;
 84         d = font[i];
 85         if ((d & 0x80) != 0) { p[0] = c; }
 86         if ((d & 0x40) != 0) { p[1] = c; }
 87         if ((d & 0x20) != 0) { p[2] = c; }
 88         if ((d & 0x10) != 0) { p[3] = c; }
 89         if ((d & 0x08) != 0) { p[4] = c; }
 90         if ((d & 0x04) != 0) { p[5] = c; }
 91         if ((d & 0x02) != 0) { p[6] = c; }
 92         if ((d & 0x01) != 0) { p[7] = c; }
 93     }
 94     return;
 95 }
 96 
 97 void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
 98 {
 99     extern char hankaku[4096];
100     for (; *s != 0x00; s++) {
101         putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
102         x += 8;
103     }
104     return;
105 }
106 
107 void init_mouse_cursor8(char *mouse, char bc)
108 /* 16*16 Mouse */
109 {
110     static char cursor[16][16] = {
111         "*...............",
112         "**..............",
113         "*O*.............",
114         "*OO*............",
115         "*OOO*...........",
116         "*OOOO*..........",
117         "*OOOOO*.........",
118         "*OOOOOO*........",
119         "*OOOOOOO*.......",
120         "*OOOO*****......",
121         "*OO*O*..........",
122         "*O*.*O*.........",
123         "**..*O*.........",
124         "*....*O*........",
125         ".....*O*........",
126         "......*........."
127     };
128     int x, y;
129 
130     for (y = 0; y < 16; y++) {
131         for (x = 0; x < 16; x++) {
132             if (cursor[y][x] == '*') {
133                 mouse[y * 16 + x] = COL8_000000;
134             }
135             if (cursor[y][x] == 'O') {
136                 mouse[y * 16 + x] = COL8_FFFFFF;
137             }
138             if (cursor[y][x] == '.') {
139                 mouse[y * 16 + x] = bc;
140             }
141         }
142     }
143     return;
144 }
145 
146 void putblock8_8(char *vram, int vxsize, int pxsize,
147     int pysize, int px0, int py0, char *buf, int bxsize)
148 {
149     int x, y;
150     for (y = 0; y < pysize; y++) {
151         for (x = 0; x < pxsize; x++) {
152             vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
153         }
154     }
155     return;
156 }
View Code

相关文章:

  • 2021-06-24
  • 2022-12-23
  • 2021-11-15
  • 2021-04-16
  • 2021-04-20
  • 2021-10-11
  • 2022-12-23
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-05-26
  • 2022-12-23
相关资源
相似解决方案