FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

x86のVGA操作について

xv6の動作検証を行っている最中に、VGAの制御を行っている場所を見付けた。console.cでは、UARTの操作をしているだけだなく、VGAディスプレイに表示する機能もサポートしている。

void consputc(int c)
{
  if(c == BACKSPACE){
    uartputc('\b'); uartputc(' '); uartputc('\b');
  } else
    uartputc(c);
  cgaputc(c);
}

consputc()はコンソール(UART)への出力だけでなく、VGAディスプレイへの表示もサポートしている。

static void
cgaputc(int c)
{
  int pos;

  // Cursor position: col + 80*row.
  outb(CRTPORT, 14);
  pos = inb(CRTPORT+1) << 8;
  outb(CRTPORT, 15);
  pos |= inb(CRTPORT+1);
...

cgaputc()だが、x86BIOSへアクセスしてCRTポートを制御し、カーソルを移動して文字を出力する、という具合だ。

まず、CRTの制御関連は0x3d4, 0x3d5などにまとめられているらしい。

d.hatena.ne.jp

Text Mode Cursor - OSDev Wiki

CRTポートのインデックス14にアクセスすることで現在のカーソルの場所の上位8ビットにあたる場所を取得し、インデックス15にアクセスすることで下位8ビットにあたる場所を探している。

inb(CRTPORT+1,14) << 8 | inb(CRTPORT+1,15)

そしてカーソル位置を一つすすめ、CGAメモリに文字を書き出している。

  outb(CRTPORT, 14);
  outb(CRTPORT+1, pos>>8);
  outb(CRTPORT, 15);
  outb(CRTPORT+1, pos);
  crt[pos] = ' ' | 0x0700;

CGAモジュールのISSへの実装

とりあえず適当にメモリマップを決めて、実装してみた。

github.com

  const Addr_t crt_start  = 0xffffffffffff4000;
  const Addr_t crt_end    = 0xffffffffffff7fff;
  const Addr_t uart_start = 0xffffffffffff8000;
  const Addr_t uart_end   = 0xffffffffffffffff;
  const Addr_t ide_start  = 0xffffffffffff0000;
  const Addr_t ide_end    = 0xffffffffffff3fff;

...

  if (addr >= ide_start && addr <= ide_end) {
    internal_addr = addr - ide_start;
    result = m_ide->LoadData (internal_addr, size, &w_data);
    *data = static_cast<Word_t>(w_data);
  } else if (addr >= crt_start && addr <= crt_end) {
    internal_addr = addr - crt_start;
    result = m_crt->LoadData (internal_addr, size, &w_data);
    *data = static_cast<Word_t>(w_data);
  } else if (addr >= uart_start && addr <= uart_end) {
    internal_addr = addr - uart_start;
    result = m_uart->LoadData (internal_addr, size, &w_data);
    *data = static_cast<Word_t>(w_data);
  } else {
    result = LoadFromBus (addr, size, byte);
    memcpy (data, byte, 8);
  }

当該アドレス領域にアクセスすると、CRTモジュールにアクセスすることになる。 一応、実行してみるとログは出るが、ちゃんとしたログにするにはまだ先だな。

<CRT: Load (000003d5)
  13559843:M:MBar:[80100474][P80100474] 0007c783 : lbu        tp,0x000(tp)         tp=>ffff43d5 (00000000000003d5)=>00000000 tp<=00000000
  13559844:M:MBar:[80100478][P80100478] fef407a3 : sb         tp,0xfef(s6)         s6=>8010e0c0 tp=>00000000 (8010e0af)<=00000000
  13559845:M:MBar:[8010047c][P8010047c] fef44783 : lbu        tp,0xfef(s6)         s6=>8010e0c0 (000000008010e0af)=>11700000 tp<=00000000
  13559846:M:MBar:[80100480][P80100480] 00078513 : addi       s8,tp,0x000          tp=>00000000 s8<=00000000