Bran的内核开发指南(4)

导读:

  现在我们将试着在屏幕上显示点东西。为此,我们需要一种管理屏幕滚动的方法。同时,能在屏幕上显示不同的颜色也是一件美妙的事情。 幸运的是,VGA显卡使这很容易实现:为了在显示器上显示内容,显卡给定了一个内存块,我们只需向内存中写入字符和属性对。 VGA控制器会自动地把更新的内容画到屏幕上。滚动屏幕是由我们的内核软件来维护的。从技术上说,这是我们的第一个驱动程序,现在我们就开始编写。

  如上面所提到的,字符缓存只是在我们地址空间中的一块内存。这块缓存在0xB8000的物理内存位置上。 缓存的类型为“short”,这意味着缓存中的每一项内容都是由16位组成的,而不是我们通常认为的8位。 缓存中的每一个16位元素,都可以被分为“高8位”和“低8位”。低8位代表需要显示的字符。高8位通常定义了这个字符的前景色和背景色。

  15 12 11 8 7 0

  背景色 前景色 字符

  16位中的高8位被称为“属性位”,低8位被称为“字符位”。正如你在上面的表格中看到的,每一个16位元素中,属性位又被分为2个4位的块:一个代表背景色,另一个代表前景色。 现在因为只用4位来表示颜色的原因,最多只可能有16种不同的颜色可供选择,(使用公式:(位数 ^ 2) - 4^2 = 16 )。以下是16种颜色表。

  值 颜色 值 颜色

  0 黑 8 深灰

  1 蓝 9 淡蓝

  2 绿 10 淡绿

  3 青绿 11 淡青绿

  4 红 12 淡红

  5 品红 13 淡品红

  6 棕 14 淡棕

  7 淡灰 15 白

  最后,为了能处理内存中特定的索引内容,我们需要使用有一个公式。 字符型的内存是一个简单的“线性”(或平坦)的内存区域,但是显示控制器使它看起来像一个80x25的16位矩阵。 在内存中,文字的每一行都是相等的;前后相互连接。 因此我们试着把屏幕变为平行的线。完成这个过程的最好方法是用公式:

  index = (y_value * width_of_screen) + x_value;

  如果我们要控制(3,4)位置上的字符,使用这个公式,就得到 4 * 80 + 3 = 323。 也就是说,在屏幕(3,4)位置上操作,就等同于如下操作:

  unsigned short *where = (unsigned short *)0xB8000 + 323;

  *where = character | (attribute <<8);

  以下内容是'scrn.c'文件,这个文件中包含了我们处理屏幕显示时要用到的函数。 我们include了'system.h'文件,这样我们就能使用outportb,memcpy,memset,memsets和strlen了。 我们使用的滚动屏幕功能是十分有趣的: 我们从第1行开始操作字符缓存(而不是第0行),然后把它复制到第0行上去,实际上就是把整个屏幕向上移动了一行。最后,我们用一行带有属性的空格写满最后一行。 这个文件中的putch函数可能是最复杂的一个了,同样也是最大的一个。 因为它需要处理换行("/n"),回车("/r")和退格("/b")。 如果你想要的话,你可以接着处理警告字符("/a" - ASCII 7), 处理时应该会发出一声短beep。 如果你需要的话,我已经编写了settextcolor函数来设置字符的颜色。

  #include ="">
  /* 这些内容定义了我们的文字指针,背景和前景颜色(属性),和xy坐标。 */

  unsigned short *textmemptr;

  int attrib = 0x0F;

  int csr_x = 0, csr_y = 0;

  /* 滚动屏幕 */

  void scroll(void)

  {

  unsigned blank, temp;

  /* 把空格定义为空白字符...我们也要设置他的背景颜色 */

  blank = 0x20 | (attrib <<8);

  /* 25行是结尾,也就是说,我们要把它滚动上去 */

  if(csr_y >= 25)

  {

  /* 把当前的字符块向上移动一行 */

  temp = csr_y - 25 + 1;

  memcpy (textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);

  /* 最后,我们把最后一行设置为我们定义的空白字符。 */

  memsetw (textmemptr + (25 - temp) * 80, blank, 80);

  csr_y = 25 - 1;

  }

  }

  /* 更新硬件光标: 在输入的字符之后的那一行上显示一个闪烁。 */

  void move_csr(void)

  {

  unsigned temp;

  /* 在线性的内存块中找到索引的公式。表示为:

  * Index = [(y * width) + x] */

  temp = csr_y * 80 + csr_x;

  /* 向VGA控制器的CRT控制寄存器发送14和15标志。

  * 他们是索引字符的高位和低位,这个字符显示在硬件光标的闪烁处。

  * 想知道更多细节,你可以查看VGA规范的编程文档。一个相当好的文档在

  * http://www.brackeen.com/home/vga */

  outportb(0x3D4, 14);

  outportb(0x3D5, temp >> 8);

  outportb(0x3D4, 15);

  outportb(0x3D5, temp);

  }

  /* 清空屏幕 */

  void cls()

  {

  unsigned blank;

  int i;

  /* 同样的,我们需要一个用来做空白的'short'颜色 */

  blank = 0x20 | (attrib <<8);

  /* 用带有空白颜色的空白画满整个屏幕 */

  for(i = 0; i <25; i++)

  memsetw (textmemptr + i * 80, blank, 80);

  /* 更新我们的虚拟光标,然后移动物理光标 */

  csr_x = 0;

  csr_y = 0;

  move_csr();

  }

  /* 在屏幕上显示单个字符 */

  void putch(unsigned char c)

  {

  unsigned short *where;

  unsigned att = attrib <<8;

  /* 处理退格时,把光标向前一动一格 */

  if(c == 0x08)

  {

  if(csr_x != 0) csr_x--;

  }

  /* 处理一个跳格时,增加光标的x值,但只移动到可以被8整除的位置上。 */

  else if(c == 0x09)

  {

  csr_x = (csr_x + 8) &~(8 - 1);

  }

  /* 处理一个回车, 直接把光标放回到最前面 */

  else if(c == '/r')

  {

  csr_x = 0;

  }

  /* 我们用DOS和BIOS的方法来处理新换行:我们把CR看作和换行一同出现。

  * 我们把x放到最前面,然后增加y值 */

  else if(c == '/n')

  {

  csr_x = 0;

  csr_y++;

  }

  /* 任何比空格大的字符都是可以被打印出来的,包括空格本身。

  * 用来从线性的内存快中找出索引的公式表示为:

  * Index = [(y * width) + x] */

  else if(c >= ' ')

  {

  where = textmemptr + (csr_y * 80 + csr_x);

  *where = c | att; /* 字符 AND 属性: 颜色 */

  csr_x++;

  }

  /* 如果光标到达了屏幕宽度的边缘,我们就插入另一行 */

  if(csr_x >= 80)

  {

  csr_x = 0;

  csr_y++;

  }

  /* 如果需要的话,滚动屏幕,并移动光标 */

  scroll();

  move_csr();

  }

  /* 使用以上的方法,输出一个字符串 */

  void puts(unsigned char *text)

  {

  int i;

  for (i = 0; i
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值