书中关于C语言编译的过程和中间的文件可以参考这张图
今天的其中一个小任务就是把我们写出来的命令行窗口填上颜色,这就要说明一下VRAM是什么东西了,VRAM是 video RAM的缩写,在电脑中指的是显卡内存,也就是用来显示画面的一段内存地址。
我们在bootpack.c中用一个for循环来向地址0xa0000~0xaffff地址的位置写入0x15,意思是全部像素的颜色都是第15种颜色。
void io_hlt(void);
void write_mem8(int addr, int data);
void HariMain(void)
{
int i; /*?量声明。?量i是32位整数*/
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, 15); /* MOV BYTE [i],15 */
}
for (;;) {
io_hlt();
}
}
接下来换一种方式,通过逻辑运算的方式,将地址与0x0f进行按位与运算,这个时候,低四位保留,高四位全部变为0;
void io_hlt(void);
void write_mem8(int addr, int data);
void HariMain(void)
{
int i; /*变量声明。变量i是32位整数*/
for (i = 0xa0000; i <= 0xaffff; i++) {
write_mem8(i, i & 0x0f);
}
for (;;) {
io_hlt();
}
}
补充说明一下其余运算方式的结果
通过上面的运算方式,我们写入的值就是00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00 01 02 03 04 05 06…每16个像素,色号就反复一次。
改成与0xFF相与就变成这样了…
都是学过C的人了,指针用起来!!!
void io_hlt(void);
void HariMain(void)
{
int i; /*变量声明。变量i是32位整数*/
char *p; /* p这个变量是BYTE[…]地址*/
p = (char *) 0xa0000; /*代入*号*/
for (i = 0; i <= 0xffff; i++) {
p[i] = i & 0x0f;
}
for (;;) {
io_hlt();
}
}
这样就把上面的那个write函数给替代了,这就是C语言里面指针的功能,用来向指定的内存地址处写入数据。这里用p[i]的方式,实际上p[i]和*(p+i)是等价的。
颜色设置
我们之前用的颜色是采用8为二进制数来显示的,而实际上现在的操作系统图形界面采用的都是RGB的方式来显示色彩的,比如#ffffff。由于制作一个简易的操作系统只需要一些颜色就足够了,所以在bootpack.c中设置如下颜色
bootpack.c完整代码
//bootpack.c 完整代码
#include <stdio.h>
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);
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);
void init_screen8(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s);
void init_mouse_cursor8(char *mouse, char bc);
void putblock8_8(char *vram, int vxsize, int pxsize,
int pysize, int px0, int py0, char *buf, int bxsize);
#define COL8_000000 0
#define COL8_FF0000 1
#define COL8_00FF00 2
#define COL8_FFFF00 3
#define COL8_0000FF 4
#define COL8_FF00FF 5
#define COL8_00FFFF 6
#define COL8_FFFFFF 7
#define COL8_C6C6C6 8
#define COL8_840000 9
#define COL8_008400 10
#define COL8_848400 11
#define COL8_000084 12
#define COL8_840084 13
#define COL8_008484 14
#define COL8_848484 15
//接受启动信息写成结构体的形式 P89.接着从asmhead.nas中读取启动信息数据(启动地址和内容)
//注意在第一天中,我们已经把asmhead.nas中的信息写到了镜像中。每次系统启动时,首先载入镜像,然后读到asmhead.nas中的内容启动
struct BOOTINFO {
char cyls, leds, vmode, reserve;
short scrnx, scrny;
char *vram;
};
struct SEGMENT_DESCRIPTOR {
//GDT的内容;8字节
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};
struct GATE_DESCRIPTOR {
//IDT的内容,8字节
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};
void init_gdtidt(void);
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0;
char s[40], mcursor[256];
int mx, my;
init_gdtidt();
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny); //所谓的使用箭头记号->
mx = (binfo->scrnx - 16) / 2; /* 启动信息结构体BOOTINFO 从asmhead.nas中读取启动信息数据*/
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484); //初始化并显示鼠标指针
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%d, %d)", mx, my); //显示变量,先把内容放到字符串s中
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); //接着把字符串s显示出来
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;
}
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;
}
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;
}
void init_screen8(char *vram, int x, int y)
{
boxfill8(vram, x, COL8_008484, 0, 0, x - 1, y - 29);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 28, x - 1, y - 28);
boxfill8(vram, x, COL8_FFFFFF, 0, y - 27, x - 1, y - 27);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 26, x - 1, y - 1);
boxfill8(vram, x, COL8_FFFFFF, 3, y - 24, 59, y - 24);
boxfill8(vram, x, COL8_FFFFFF, 2, y - 24, 2, y - 4);
boxfill8(vram, x, COL8_848484, 3, y - 4, 59, y - 4);
boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y - 5);
boxfill8(vram, x, COL8_000000, 2, y - 3, 59, y - 3);
boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y - 3);
boxfill8(vram, x, COL8_848484, x - 47, y - 24, x - 4, y - 24);
boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y - 4);
boxfill8(vram, x, COL8_FFFFFF, x - 47, y - 3, x - 4, y - 3);
boxfill8(vram, x, COL8_FFFFFF, x - 3, y - 24, x - 3, y - 3);
return;
}
//输出,显示字符
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;
}
//输出,显示字符串
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;
}
void init_mouse_cursor8(char *mouse, char bc)
/* 准备,初始化鼠标指针 16*16=512字符(字节)*/
{
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;
}
void init_gdtidt(void) //GDT和IDT的初始化
{
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++) {
//i每次加一;但是gdt指向8字节的结构体;结果地址增加了8
set_segmdesc(gdt + i, 0, 0, 0); //gdt*(地址)每次+8
}
set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092); //段号为1;大小4G 表示CPU管理的全部内存
set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a); //段号位2; 大小512K 为bootpack.hrb准备
load_gdtr(0xffff, 0x00270000); //GDT 0x270000-0x27ffff 借助汇编语言的力量给寄存器GDTR赋值
/* IDT初始化 */
for (i = 0; i < 256; i++) {
//和上面一样
set_gatedesc(idt + i, 0, 0, 0);
} //IDT 0x26f800-0x26ffff
load_idtr(0x7ff, 0x0026f800); //向寄存器IDTR赋值
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_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->offset_low = offset & 0xffff;
gd->selector = selector;
gd->dw_count = (ar >> 8) & 0xff;
gd->access_right = ar & 0xff;
gd->offset_high = (offset >> 16) & 0xffff;
return;
}
bootpack.c 完整代码day_05
harib02a:
P89 这里做的就是数值写入asmhead.nas中,然后取值;
而不是将这些数值直接写入程序bootpack.c中
//bootpack.c节选
void HariMain(void)
{
char *vram;
int xsize, ysize;
short *binfo_scrnx, *binfo_scrny;
int *binfo_vram;
init_palette();
binfo_scrnx = (short *) 0x0ff4;
binfo_scrny = (short *) 0x0ff6;
binfo_vram = (int *) 0x0ff8;
xsize = *binfo_scrnx;
ysize = *binfo_scrny;
vram = (char *) *binfo_vram;
init_screen(vram, xsize, ysize);
for (;;) {
io_hlt();
}
}