前一张直接在bootpack.c中对VRAM地址、x、y赋值,实际应该将asmhead.nas中的数据读入赋值,这一章节首先通过指定地址和asmhead.nas对应的指针,读取这些指针到函数里,之后进行赋值,至于为什么是0x0ff4还是不太清楚?
之后利用利用数据地址挨着的特性,将数据封装进了结构体指针struct将数据按顺序读取进函数。
注意:之前的调用显卡BIOS,意思是调用能控制显卡的BIOS函数,比如0x0010(显示字符)、0x0013(显示VGA,320*200*8位模式的画面)。
c语言知识
访问结构体指针的变量时,有两种方法:(*bino).x 和 bino->x
显示文字和鼠标指针
字符可以用8×16个像素点阵(或者说二进制数)表示。也可以写成16个字节数据。
像这种描画文字形状的数据成为字体。注意字体(8×16位)与显示在屏幕上的字(8×16个字节数据)不同。
注意:内存地址坐标=(y_1+y_2)*x_pinmuchangdu+x_1+x_2
其中,(x_1,y_1)为整个字体相对屏幕的坐标,(x_2,y_2)为字体中每个像素点相对整个字体的坐标。
现在的bootpack.c:
;bootpack.c
//申明函数
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_screen(char *vram, int x, int y);
void putfont8(char *vram, int xsize, int x, int y, char c, char *font);
//定义宏,色号
#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
//定义结构体,用于读取VRAM画面信息
struct BOOTINFO {
char cyls, leds, vmode, reserve;
short scrnx, scrny;
char *vram;
};
//程序主体
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) 0x0ff0; //创建结构体指针变量
static char font_A[16] = { //创建字体,字符串
0x00, 0x18, 0x18, 0x18, 0x18, 0x24, 0x24, 0x24,
0x24, 0x7e, 0x42, 0x42, 0x42, 0xe7, 0x00, 0x00
};
init_palette(); //设置调色板
init_screen(binfo->vram, binfo->scrnx, binfo->scrny); //填充画面
putfont8(binfo->vram, binfo->scrnx, 10, 10, COL8_FFFFFF, font_A); //显示字体,通过向VRAM写数据
for (;;) {
io_hlt();
}
}
void init_palette(void)
{
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;
/* 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;
}
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_screen(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; //vram=0xa0000,x、y是字体坐标,i是相对字体的y坐标
d = font[i];
if ((d & 0x80) != 0) { p[0] = c; } //[0]是相对字体的x坐标
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;
}
GDT与IDT初始化
作者在asmhead.nas尽量少写东西,是为了用c语言,也没有写这些设定。
分段与分页。
在32位操作系统中,数据地址[DS:BX]改为[DS:EBX],DS的含义也不再是DS的16倍,而是段的起始地址。但是段寄存器只有16位,根本不够用来存储段的信息(每个段用8个字节的数据表示信息),实际上段寄存低3为不能使用,也就是说只能表示2^13=8192个数字。这就要用到GDT(全局段号记录表global(segment) descriptor table),将这些8192*8=64KB的数据按顺序存储在内存中某个地方,并将内存的起始地址和有效设定个数放在CPU中被称为GDTR的特殊寄存器中。(段寄存器记录2^13=8192个段号,GDTR寄存器中存放GDT的起始地址和有效设定个数,内存中存放GDT)
IDT中断记录表(interrupt descriptor table)设定类似与GDT,记录了1~255号码对应的调用函数。可以帮助CPU摆脱一直查询,集中在任务上。(内存存放IDT)
;bootpack.c
struct SEGMENT_DESCRIPTOR {
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};//GDT结构体
struct GATE_DESCRIPTOR {
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};//IDT结构体
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); //函数申明,其中load_gdtr、load_idtr用汇编编写
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++) {
set_segmdesc(gdt + i, 0, 0, 0);
}
set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, 0x4092);
set_segmdesc(gdt + 2, 0x0007ffff, 0x00280000, 0x409a);
load_gdtr(0xffff, 0x00270000);
/* IDT偺弶婜壔 */
for (i = 0; i < 256; i++) {
set_gatedesc(idt + i, 0, 0, 0);
}
load_idtr(0x7ff, 0x0026f800);
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;
}
_load_gdtr: ; void load_gdtr(int limit, int addr);
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LGDT [ESP+6] //将[ESP+6]的内容加载到GDTR
RET
_load_idtr: ; void load_idtr(int limit, int addr);
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LIDT [ESP+6]
RET
MOV AX,[ESP+4]
MOV [ESP+6],AX
LGDT [ESP+6]是为什么?AX是16位,load_gdtr(0xffff, 0x00270000)变成0x002700ffffff?