我的博客startcraft
昨天写的内核在屏幕上没有我们输出的东西,今天就来想办法显示点什么
文字的显示
要显示东西就涉及到显卡了,显卡有两种模式,文本模式和图形模式,现在基本都是图形模式用得多,但是我们这个就用文本模式了,毕竟不涉及ui啥的
文本的显示规则
显卡通电后就自动初始化了80\ * 25分辨率的文本模式,即一屏25行,一行80个字符
之前说过内存地址空间不是全部映射到主存的,有一部分映射到外部设备,0xB8000~0xBFFFF就是映射到显卡文本模式的显存的地址空间
从0xB8000开始,每两个字节对应屏幕上的一个字符,从第一行开始,字符在内存中的存储形式叫内码,第一个字符对应字符的ASCII码,第二个控制字符的颜色等等信息,每一位都有不同的含义,如下图: (图片来自教程)
除了显存之外,显卡还有一些控制单元,这些单元没有映射在cpu寻址的4GB空间里,需要使用in / out命令来读写,这部分寄存器的访问规则是:
- 通过0x3D4端口来设置寄存器索引,就是要访问哪一个寄存器
- 通过0x3D5端口来设置寄存器的值
端口读写函数
这些要用in / out命令访问的寄存器用c的代码显然是不行的,只能用汇编实现,这里选择在c里面内嵌汇编代码参考汇编语言和C语言混合编程和内联汇编
libs/common.c
#include "common.h"
//往端口写一个字节
inline void outb (uint16_t port, uint8_t value)
{
asm volatile ("outb %1,%0"::"dN"(port), "a"(value));
}
//从端口读一个字节
inline uint8_t inb (uint16_t port)
{
uint8_t ret;
asm volatile ("inb %1,%0":"=a"(ret):"dN"(port));
return ret;
}
//从端口读一个字
inline uint16_t inw (uint16_t port)
{
uint16_t ret;
asm volatile ("inw %1,%0":"=a"(ret):"dN"(port));
return ret;
}
include/common.h
#ifndef INCLUDE_COMMON_H_
#define INCLUDE_COMMON_H_
#include "types.h"
//往端口写一个字节
void outb (uint16_t port, uint8_t value);
//从端口读一个字节
uint8_t inb (uint16_t port);
//从端口读一个字
uint16_t intw (uint16_t port);
#endif
头文件前面的那个ifndef和define是为了防止在不同的文件中include了同一个头文件会出现的重复定义错误
函数前面的inline是建议编译器把函数当成内联函数来编译,即在函数的调用处进行代码展开,而不是传统函数调用
颜色定义和屏幕操作函数
是颜色定义的枚举和一些屏幕控制函数的声明
include/console.h
#ifndef INCLUDE_CONSOLE_H_
#define INCLUDE_CONSOLE_H_
#include "types.h"
typedef
enum real_color {
rc_black = 0,
rc_blue = 1,
rc_green = 2,
rc_cyan = 3,
rc_red = 4,
rc_magenta