自制操作系统(5)
作者:互联网
接收启动信息的结构体
启动信息包括键盘等状态、显卡模式、显存地址,屏幕大小等。
asmhead中的内容:
; BOOT_INFO関係
CYLS EQU 0x0ff0 ; 启动扇区设置(1字节)
LEDS EQU 0x0ff1 ; 键盘灯状态(1字节)
VMODE EQU 0x0ff2 ; 关于颜色数的信息。几位彩色?(2字节)
SCRNX EQU 0x0ff4 ; 屏幕像素宽度(2字节)
SCRNY EQU 0x0ff6 ; 屏幕像素高度(2字节)
VRAM EQU 0x0ff8 ; 图形缓冲区的起始地址()
bootpack.c中的内容:
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;
//该结构体指针指向的起始地址是0xff0
xsize = (*binfo).scrnx;
//根据BOOTINFO的定义,scrnx = 0xff0 + 0x1 + 0x1 + 0x1 + 0x1 = 0xff4
ysize = (*binfo).scrny;
//其实写成*(binfo.scrny)更容易理解
vram = (*binfo).vram;
init_screen(vram, xsize, ysize);
for (;;)
{
io_hlt();
}
}
结构体类型的指针中包含几个指针变量。要引用这些指针变量指向的内存地址中的值,有以下几种方法:
xsize = ( *binfo ) . scrnx ;
xsize = *(binfo . scrnx) ;
xsize = binfo -> scrnx ;
这个箭头表示的方法,可能本身就是为了表示“指向”的意思。本质上这三种表示方法都是在表示先取结构体型指针变量中的一个指针变量,“scrnx”,然后取其指向的内存地址上的内容,长度由“scrnx”的类型决定。
显示字体
为了方便使用4位二进制数表示1位16进制数,字体使用8*16的规格。
00000000就可以表示为0x00,00011000表示为0x18等等。。
static char font_A[16] =
{
0x00, // 0 0 0 0 0 0 0 0
0x18, // 0 0 0 1 1 0 0 0
0x18, // 0 0 0 1 1 0 0 0
0x18, // 0 0 0 1 1 0 0 0
0x18, // 0 0 0 1 1 0 0 0
0x24, // 0 0 1 0 0 1 0 0
0x24, // 0 0 1 0 0 1 0 0
0x24, // 0 0 1 0 0 1 0 0
0x24, // 0 0 1 0 0 1 0 0
0x7e, // 0 1 1 1 1 1 1 0
0x42, // 0 1 0 0 0 0 1 0
0x42, // 0 1 0 0 0 0 1 0
0x42, // 0 1 0 0 0 0 1 0
0xe7, // 1 1 1 0 0 1 1 1
0x00, // 0 0 0 0 0 0 0 0
0x00 // 0 0 0 0 0 0 0 0
};
如何将它显示到屏幕上?
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
/*
vram: 显存首地址
xsize: 屏幕宽度
x: 要显示字符的横坐标
y: 要显示字符的纵坐标
c: 要显示的字符的颜色
font: 字符的二进制点阵,如上面的A
*/
int i;
char d; /* data */
for (i = 0; i < 16; i++)
{
// i的初始值为0,循环一次+1,条件为i<16
d = font[i];
// C语言只能表示16进制
if ((d & 0x80) != 0) { vram[(y + i) * xsize + x + 0] = c; }
// 0x80 即 1000 0000
if ((d & 0x40) != 0) { vram[(y + i) * xsize + x + 1] = c; }
// 0x40 即 0100 0000
if ((d & 0x20) != 0) { vram[(y + i) * xsize + x + 2] = c; }
// 0x20 即 0010 0000
if ((d & 0x10) != 0) { vram[(y + i) * xsize + x + 3] = c; }
// 0x10 即 0001 0000
if ((d & 0x08) != 0) { vram[(y + i) * xsize + x + 4] = c; }
// 0x08 即 0000 1000
if ((d & 0x04) != 0) { vram[(y + i) * xsize + x + 5] = c; }
// 0x04 即 0000 0100
if ((d & 0x02) != 0) { vram[(y + i) * xsize + x + 6] = c; }
// 0x02 即 0000 0010
if ((d & 0x01) != 0) { vram[(y + i) * xsize + x + 7] = c; }
// 0x01 即 0000 0001
}
return;
}
与运算的规则: 0&1=0; 1&0=0; 1&1=1; 0&0=0.
所以用font[0]和0x80(1000 0000)进行与运算,则如果font[0]的第一个位是1,则运算结果就是0x80,就在这个位置上填充颜色,假如font[0]的第一个位是0,则运算结果就是0,就不做任何操作。对十六进制数进行判断后,就可以实现在每一行1的位置上填上颜色。对每一个十六进制数进行判断后,就实现了在每一行的1的位置上填上颜色。由此实现了显示一个字符。
显示字符串时使用了hankaku和makefont,不涉及原理的东西就先不看了吧。
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
extern char hankaku[4096];
for (; *s != 0x00; s++) // 字符串以0x00结尾
{
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
x += 8; //输出一个字符后,下一个字符
} //下一个字符的横坐标要加上字体宽度
return;
}
输出变量的值时使用了sprintf函数,包含在stdio.h中。
显示光标
void init_mouse_cursor8(char *mouse, char bc)
/* 准备鼠标指针(16×16) */
{
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;
}
和显示“A”有异曲同工之妙。
难点在GDT/IDT的初始化,下篇文章再写。
标签:0000,操作系统,16,int,自制,char,xsize,vram 来源: https://blog.csdn.net/qq_33296280/article/details/118435521