跟我一起写操作系统

    返回首页    发表留言
本文作者:李德强
          第二节 显示字符
 
 

        首先回顾一下关于显示缓冲区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-2018 问渠网 辽ICP备15013245号