首先回顾一下关于显示缓冲区0xb8000的相关知识:内存地址中0xb8000 – 0xbffff共32KB的空间,这是80 * 25彩色字符模式的显示缓冲区。向这个地址空间中写入数据,写入的内容将立即出现在显示器上。在80 * 25彩色字符模式下,显示器可以显示25行,每行80个字符,每个字符可以有256种属性,包括背景色、前景色、闪烁、高亮等互相组合。这样的话对于一个可显字符,ASCII码占用1个字节,属性占用1个字节,所以如果要显示一个字符就需要2个字节的内存空间。
从0xb8000开始,偏移0x0 – 0x9f对应显示器上第1行;偏移0xa0 – 0x13f对应显示器上的第2行;偏移0x140 – 0x1df对应的显示器上的第3行,以此类推。在一行中,一个字符占两个字节,高位字节存储属性,低位字节存储ASCII码。
字符属性格式如下:
bit位 7 6 5 4 3 2 1 0 BL R G B I R G B 闪烁 背景 高亮 前景
比如:
红底绿字,属性字节为:01000010B = 0x42; 红底闪烁绿字,属性字节为:11000010B = 0xc2; 红底高亮绿字,属性字节为:01001010B = 0x4a; 黑底白字,属性字节为:00000111B = 0x07; 白底蓝字,属性字节为:01110001B = 0x71。
光标位置与显示字符在显示器缓冲区位置的关系表如下:
显示位置 |
缓冲区地址 |
光标坐标(x,y) |
缓冲区地址偏移 |
0 |
0xb8000 |
0, 0 |
0 |
1 |
0xb8002 |
1, 0 |
2 |
2 |
0xb8004 |
2, 0 |
4 |
3 |
0xb8006 |
3, 0 |
6 |
… … |
… … |
… … |
… … |
80 |
0xb80a0 |
0, 1 |
160 |
81 |
0xb80a1 |
1, 1 |
162 |
82 |
0xb80a2 |
2, 1 |
164 |
83 |
0xb80a3 |
3, 1 |
166 |
… … |
… … |
… … |
… … |
先来编写一个函数,用于计算显示字符的位置并根据字符的ascii码将其显示到显示器上:
/*** * 根据一个字符的ascii显示到指定位置 * u16 x: 横坐标 * u16 y: 纵坐标 * char ch: 要显示的字符 */ void putascii(u16 x, u16 y, char ch) { //定义显存地址 char *video_addr = (char *) 0xb8000; //写入显存 u32 where = (y * 80 + x) * 2; //显示字符的实际物理地址 u8 *p = (u8 *) (video_addr) + where; //字符的ascii码 *p = ch; //颜色 *(p + 1) = 0x07; }
再来写一个函数叫putchar,专门负责将一个字符显示到当前光标位置:
/*** * 显示一个字符到当前光标位置 * char ch: 要显示的字符 */ void putchar(char ch) { //取得当前光标线性位置 u16 cursor_pos = get_cursor(); //计算横纵坐标 u16 x = cursor_pos % 80; u16 y = cursor_pos / 80; //如果是换行符\n if (ch == 0xa) { //换行 x = 0; y++; set_cursor(x, y); } //如果是制表符\t else if (ch == 0x9) { //显示空格 ch = 0x20; //显示8个空格 for (int i = 0; i < 8; i++) { putascii(x, y, ch); x++; set_cursor(x, y); } } //显示普通字符 else { putascii(x, y, ch); x++; set_cursor(x, y); } }
有了这个putchar函数,我们就不需要在每次显示字符是在0xb8000处直接写入数据了,想要显示一个字符,直接调用putchar即可。现在修改一下start_kernel函数的内容:
#include <kernel/kernel.h> #include <kernel/printf.h> //全局字符串指针变量 char *str = "Hello World!"; //内核启动程序入口 int start_kernel(int argc, char **args) { //显示str的内容到显示器上 for (int i = 0; str[i] != '\0'; i++) { //显示一个字符到光标所在位置,光标移动一个位置 putchar(str[i]); } //永无休止的循环 for (;;) { } return 0; }
最后执行make all命令并启动lidqos虚拟机,查看运行效果:
源代码的下载地址为:
https https://github.com/magicworldos/lidqos.git git git@github.com:magicworldos/lidqos.git subverion https://github.com/magicworldos/lidqos branch v0.8
Copyright © 2015-2023 问渠网 辽ICP备15013245号