[OSC] CGA显示
前言:此为从零开始造轮子搭建操作系统的笔记,框架来自学校课程,编程语言主要为C++和汇编,涉及大量底层硬件相关。 本篇为CGA显示驱动和基础总线读写
注:如无特殊说明,
char
类型均为1 byte,word
为 2byte。- 所有index都从0开始,包括屏幕上的坐标(x,y) 和寄存器编号。
0x00 CGA 显示设备
概述
CGA文本模式非常简单,一般为80x25大小的单元格矩阵,每个单元格可以显示一个字符 (code page #437),拥有独立的前景/
背景色,以及一个额外的“闪烁”模式。另外可以单独设置光标位置。
编码
每个格子由16 bit 编码,其中高八位编码256个字符,
第八位编码字体颜色和背景色。每个格子(x,y)在内存
中对应的位置可如下计算:
mem_offset = (80 * y + x) * 2 * sizeof(char)
前景色和背景色共同编码为一个8位char
,
其中高四位为背景,低四位为前景,具体编码参考
wiki.
例如黑底白字编码为0x0f
.
CGA 显存的起始位置一般为 0xb8000
, 以下代码会在 (x,y) 位置显示一个字符
#define COL 80
char* const CGA_START_C = (char*)0xb8000;
void show_char(int x, int y, char c, unsigned char attrib){
char* addr = CGA_START_C + 2 * (x + y * COL);
// set character
*addr = c;
// set color attribute
++ addr;
*addr = attrib;
}
光标
光标位置由一个一维16 bit整数offset
表示,对应光标位置(x,y),
offset = x + 80 * y;
offset 的高八位和低八位分别存储在显卡的两个8位控制寄存器(control register)中,寄存器编号为14,15。
然而这两个寄存器无法直接访问,只能通过总线以及另外两个特殊的寄存器间接访问。 这两个寄存器分别是
Port | Regisger | W/R |
---|---|---|
0x3d4 | Index Reg.(IR) | Write Only |
0x3d5 | Data Reg.(DR) | Read & Write |
例如,将光标位置设置为 x=42,y=3;
- 计算光标位置
offset = 42 + 3*80 = 282
也即二进制00000001 00011010
. - 向 IR 写入15
- 将低八位
00011010
写入DR - 向 IR 写入14
- 将高八位
00000001
写入DR
0x01 BUS IO 总线读写
设置光标需要通过总线对设备寄存器进行读写。这一操作可以通过几行简单的ASM实现:
io.h
extern "C" void outb(int port, int value);
extern "C" unsigned char inb(int port);
void outb(int val, int addr) const { ::outb(addr, val); };
int inb(int addr) const { return ::inw(addr); };
io.asm
[GLOBAL outb]
[GLOBAL inb]
[SECTION .text]
outb:
push rbp
mov rbp, rsp
mov rdx, rdi
mov rax, rsi
out dx, al
pop rbp
ret
inb:
push rbp
mov rbp, rsp
mov rdx, rdi
in al, dx
pop rbp
ret
汇编代码的解释放在后面写,这里只是一个简单的overview。
(to be continued)
[+] click to leave a comment [+]
>> SEND COMMENT <<