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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章