Day05
今天在开头讲了用c语言接收启动信息,之后是各种图形界面的优化,最后稍微提及了一点GDT和IDT的知识。
一些问题与感想
C语言指针的底层用法(续)
今天提到了在基础类型之外,我们还可以定义结构体,通过转换成结构体类型指针,来一次性读入更多内存,或者是把不同内存解释为不同的意义。
但是不太清楚的一点是结构体会存在对齐问题的,因此是不是也不能随意地去用结构体指针去指地址?
用位图来显示图形
今天提到的图形界面内容,全部是用位图来展示的,这样做确实可以比较方便地在像素级别上显示图像,也方便看到最后的成果。
GDT与IDT
GDT(global descriptor table),用于把内存分为多个段之后,记录段大小、起始地址以及管理属性。这样每个段都可以看作起始地址0来处理,分给不同的程序。
IDT(interrupt descriptor table),用于记录中断号对应的函数。
进入正题
接收启动信息
harib02a
从内存中读入相关的启动信息,放到c语言里
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;
试用结构体、试用箭头记号
harib02b
harib02c
把启动信息封装称一个结构体,这样做到一次性载入所有启动信息。
struct BOOTINFO
{
char cyls, leds, vmode, reserve;
short scrnx, scrny;
char *vram;
};
void HariMain(void)
{
char *vram;
int xsize, ysize;
struct BOOTINFO *binfo;
init_palette();
binfo = (struct BOOTINFO *)0x0ff0;
xsize = binfo->scrnx;
ysize = binfo->scrny;
vram = binfo->vram;
显示字符
harib02d
用位图的方式显示一个字符A,其实就是看哪些地方应该为黑,哪些地方应该为白罢了。
void HariMain(void)
{
static char font_A[16] =
{
0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
};
struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;
init_palette();
init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A);
for(;;)
{
io_hlt(); /* 执行naskfunc.nas的_io_hlt */
}
}
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];
int bit = 0x80;
int cnt = 0;
for(; bit != 0x00; bit >>= 1)
{
if((d & bit) != 0) p[cnt] = c;
++cnt;
}
}
}
其中我用了for循环代替了书上对应位与的操作,效果应该是一样的。
增加字体、显示字符串
harib02e
harib02f
我们导入了一个现成的字体库,然后对这个字体库用某个软件进行编译连接,就可以在c语言里用上了。
当然,光显示单个字符太累了,所以可以想办法做一个显示字符串的函数,
void HariMain(void)
{
extern char hankaku[4096];
struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;
init_palette();
init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
putfont8_asc(binfo->vram, binfo->scrnx, 8, 8, COL8_FFFFFF, "ABC 321");
putfont8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
putfont8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");
for(;;)
{
io_hlt(); /* 执行naskfunc.nas的_io_hlt */
}
}
void putfont8_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;
}
}
显示变量值
harib02g
我们可以把数字转化成字符串,这样就能在屏幕上打印出来了。需要用到<stdio.h>里的sprintf
char s[105];
sprintf(s, "scrnx = %d", binfo->scrnx);
putfont8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);
熟悉C语言还是很好理解的
显示鼠标指针
harib02h
同样的鼠标位图,不需要太多解释
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *)0x0ff0;
extern char hankaku[4096];
init_palette();
init_screen(binfo->vram, binfo->scrnx, binfo->scrny);
putfont8_asc(binfo->vram, binfo->scrnx, 8, 8, COL8_FFFFFF, "ABC 321");
putfont8_asc(binfo->vram, binfo->scrnx, 31, 31, COL8_000000, "Haribote OS.");
putfont8_asc(binfo->vram, binfo->scrnx, 30, 30, COL8_FFFFFF, "Haribote OS.");
char s[105];
sprintf(s, "scrnx = %d", binfo->scrnx);
putfont8_asc(binfo->vram, binfo->scrnx, 16, 64, COL8_FFFFFF, s);
int mx = (binfo->scrnx - 16) / 2; /* 使得鼠标坐标在中央 */
int my = (binfo->scrny - 28 - 16) / 2;
char mcursor[256];
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
for(;;)
{
io_hlt(); /* 执行naskfunc.nas的_io_hlt */
}
}
void init_mouse_cursor8(char *mouse, char bc)
{
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;
}
}
}
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];
}
}
}
不过得吐槽一下字符串不给\0的位置,编译器一堆warning。。。
GDT与IDT的初始化
harib02i
已经介绍过GDT与IDT了,直接看代码
struct SEGMENT_DESCRIPTOR
{
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};
struct GATE_DESCRIPTOR
{
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 init_gdtidt()
{
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *)0x00270000;
struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *)0x0026f800;
int i;
// init gdt
for(i = 0; i < 8192; ++i)
{
set_segmdesc(gdt + i, 0, 0, 0);
}
set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a); // 为bootpack.hrb准备
load_gdtr(0xffff, 0x00270000);
// init idt
for(i = 0; i < 256; ++i)
{
set_gatedesc(idt + i, 0, 0, 0);
}
load_idtr(0x7ff, 0x0026f800);
}
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;
}
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;
}
我们把GDT放到了0x270000 ~ 0x27ffff的地方,IDT放到了0x26f800 ~ 0x26ffff
明天会对这些属性进一步讲解,今天先到这里了。