October 26, 2017

操作系统学习2 VGA

概述

VGA 接口支持在屏幕上显示 80*25 的字符,字符可以有 16 种前景和背景色。每个字符用 16 位表示,其结构示意如下:

struct Character {
	char bg:4;    // 16种背景色
	char fg:4;    // 16种前景色
	char c;       // ascii 字符
}

VGA 中字符数据被映射到内存 0xB8000 开始的线性区域,对内存操作即可写入内容(实模式)。

此外,有两个 IO 端口用于控制光标位置等其它操作:

  1. 0x3D4: control register
  2. 0x3D5: data register

VESA 提供了更丰富的图形支持。

常用操作

指定光标位置

通过 0x3D5 数据端口发送光标位置 y * 80 + x

由于光标位置由 uint16 表示,需要分两次传送:

  1. 0x3D4 <- 14,并写入高 8 位位置到 0x3D5
  2. 0x3D4 <- 15, 并写入低 8 位位置到 0x3D5

清屏

使用黑底白字空格填充整个屏幕数据即可

滚动

将屏幕数据内存向左移动 80*2 个字节,并用黑底白字空格填充末行

打印字符串

需要注意一些特殊字符的含义

  1. “\b” 退格
  2. “\t” 按 8 个字符对齐
  3. “\r” 光标移动行首
  4. “\n” 光标移动行首并换行

另外,可见字符在 ascii 码表中,都位于空格(” “)之后

示例

以 toaruos 早期代码(cdff0) vga.c 为例,注释如下

     1  #include <system.h>
     2  
     3  /*
     4   * Text pointer, background, foreground
     5   */
     6  unsigned short * textmemptr;

        // 黑底白字
     7  int attrib = 0x0F;

        // 光标位置,从 0 开始计数
     8  int csr_x = 0, csr_y = 0;
     9  
    10  /*
    11   * scroll
    12   * Scroll the screen
    13   */
    14  void
    15  scroll() {
    16          unsigned blank, temp;
    17          blank = 0x20 | (attrib << 8);
    18          if (csr_y >= 25) {
    19                  /*
    20                   * Move the current text chunk that makes up the screen
    21                   * back in the buffer by one line.
    22                   */
    23                  temp = csr_y - 25 + 1;
    24                  memcpy(textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);
    25                  /*
    26                   * Set the chunk of memory that occupies
    27                   * the last line of text to the blank character
    28                   */
                        // bug???
                        // memsetw( textmemptr + (csr_y - 1)*80, blank, 80 );
    29                  memsetw(textmemptr + (25 - temp) * 80, blank, 80);
    30                  csr_y = 25 - 1;
    31          }
    32  }
    33  

        // 位置用 16 位整数表示,分两次,先发高字节,后发低字节
        // 发数据前在控制端口(0x3D4)上发送 14 和 15 标识
    34  /*
    35   * move_csr
    36   * Update the hardware cursor
    37   */
    38  void
    39  move_csr() {
    40          unsigned temp;
    41          temp = csr_y * 80 + csr_x;
    42          
    43          /*
    44           * Write stuff out.
    45           */
    46          outportb(0x3D4, 14);
    47          outportb(0x3D5, temp >> 8);
    48          outportb(0x3D4, 15);
    49          outportb(0x3D5, temp);
    50  }
    51  

        // 用黑底白字空格填充整个屏幕,并重置光标位置
    52  /*
    53   * cls
    54   * Clear the screen
    55   */
    56  void
    57  cls() {
    58          unsigned blank;
    59          int i;
    60          blank = 0x20 | (attrib << 8);
    61          for (i = 0; i < 25; ++i) {
    62                  memsetw(textmemptr + i * 80, blank, 80);
    63          }
    64          csr_x = 0;
    65          csr_y = 0;
    66          move_csr();
    67  }
    68  
    69  /*
    70   * putch
    71   * Puts a character to the screen
    72   */
    73  void
    74  putch(
    75                  unsigned char c
    76           ) {
    77          unsigned short *where;
    78          unsigned att = attrib << 8;

                // 退格
    79          if (c == 0x08) {
    80                  /* Backspace */
    81                  if (csr_x != 0) csr_x--;
    82          } else if (c == 0x09) {
    83                  /* Tab */
                        // 经典对齐代码
    84                  csr_x = (csr_x + 8) & ~(8 - 1);

                // \r 回到行首
                // \n 加到行首,并切到下一行
    85          } else if (c == '\r') {
    86                  /* Carriage return */
    87                  csr_x = 0;
    88          } else if (c == '\n') {
    89                  /* New line */
    90                  csr_x = 0;
    91                  csr_y++;
    92          } else if (c >= ' ') {
    93                  where = textmemptr + (csr_y * 80 + csr_x);
    94                  *where = c | att;
    95                  csr_x++;
    96          }
    97  
    98          if (csr_x >= 80) {
    99                  csr_x = 0;
   100                  csr_y++;
   101          }
   102          scroll();
   103          move_csr();
   104  }
   105  
   106  /*
   107   * puts
   108   * Put string to screen
   109   */
   110  void
   111  puts(
   112                  unsigned char * text
   113          ){ 
   114          int i;
   115          int len = strlen(text);
   116          for (i = 0; i < len; ++i) {
   117                  putch(text[i]);
   118          }
   119  }
   120  
   121  /*
   122   * settextcolor
   123   * Sets the foreground and background color
   124   */
   125  void
   126  settextcolor(
   127                  unsigned char forecolor,
   128                  unsigned char backcolor
   129                  ) {
   130          attrib = (backcolor << 4) | (forecolor & 0x0F);
   131  }
   132  
   133  /*
   134   * init_video
   135   * Initialize the VGA driver.
   136   */
   137  void init_video() {
                // framebuffer 的地址被映射到 0xB8000 开始的线性区域
   138          textmemptr = (unsigned short *)0xB8000;
   139          cls();
   140  }