|
0から作るOS開発 シンプルビデオドライバ
|
前回までの内容
これまでで、
- カーネルのコンパイルにはgcc、as、ld、makeを使用する
- カーネルをコンパイルするときのオプション
- リンケージ時に使用するセクションの定義とリンカスクリプトの書式
- MakeとMakefileの書式
- カーネルのデバッグ時には文字の表示とVirtual Boxのログファイルからレジスタ情報を見る
ことがわかりました。それではカーネルを0から開発していきましょう!
シンプルビデオドライバ
今回は文字を表示する簡単なビデオドライバを開発していきます
VGA(Video Graphics Array)のドライバとCRTマイクロコントローラのドライバについて見ていきます
VGA(Video Graphics Array)
VGAは1987年にIBMのIBM PS/2のビデオとして採用された、画面表示の規格です
VGAの前のISA(Industry Standard Architecture)基盤では多数の
チップセットがありましたが、VGAでは1つのチップに"Array(配列)"してまとめられました
VGAの前にはISAにMDA、CGA、EGAが使われていました
VGAを見ていく前に、前身のMDA、CGA、EGAをみていきましょう
MDA、CGA、EGA
MDAはモノクロームディスプレイアダプタ(Monochrome Display Adapter)を省略したものです
同様にCGAはカラーグラフィックアダプタ(Color Graphics Adapter)、
EGAはエンハンスドグラフィックアダプタ(Enhanced Graphics Adapter)の略となります
MDA(Monochrome Display Adapter)
MDAは1981年にIBMが開発したパソコン用の標準ディスプレイカードです
MDAは現在言うビデオモードというものが無く、モノクロの文字を画面に80列×25行表示だけが
できます(このモードは現在でいうところのビデオモード7にあたります)
ビデオRAMは4kバイトで、アトリビュート(属性)を指定することができます
アトリビュートには”表示しない(invisible)”、”アンダーライン(underline)”、”ノーマル(normal)”、
”ボールド(bright)”、”反転(reverse video)”などがあります
CGA(Color Graphics Adapter)
CGAも1981年にIBMが開発した初のカラーグラフィックカードで、初のカラーコンピュータディスプレイ標準です
ビデオRAMは16kバイトで、NTSC準拠のモニタやコンポジットビデオ用のRCAコネクタ、
4ビットRGBIが接続できるCRTモニタに接続することができます
1ピクセルあたりにアトリビュートと文字色に4バイトだけしか使用できないので、
16色を使用できるカラーパレットしかありません。ビデオモードとしてテキストモードとグラフィックモードがあり
- 40×25文字を表示するテキストモード(文字小、16色パレット)
- 18×25文字を表示するテキストモード(文字大、16色パレット)
- 解像度320×200で4色のビデオモード
- 解像度640×200で1色のビデオモード
のモードがあります。CGAにより、文章以外の映像表示が簡単にできるようになりました
EGA(Enhanced Graphics Adapter)
EGAは1984年にIBMが開発した解像度640×350で64色のパレットから16色を同時表示できる
コンピュータディスプレイ標準です。EGAはCGAと完全互換でCGAで表示可能なモードは
EGAでも使用することができます
VGAの仕様
VGA(ビデオコントローラ)はビデオバッファ、ビデオDAC(Digital/Analog Converter)、
CRT(Cathode Ray Tube)コントローラ、シーケンサ、グラフィックコントローラ、
アトリビュートコントローラから構成されています
ビデオバッファ
ビデオバッファはビデオメモリ(VRAM)としてメモリ上にマップされています。起動時にBIOSが
メモリの0x000A0000にマップします。アドレスマップは
カーネルローダその1
を参照してください。
VESAの仕様によれば、このアドレスは変更することができます
ビデオDAC(Digital/Analog Converter)
ビデオDACはカラーパレットを参照してVRAMのデジタルデータをアナログのビデオ信号に変換して、
ディスプレイに信号を送ります。ビデオDACから出力される信号はレッド、グリーン、ブルーの3つの色を
アナログ信号として出します
CRT(Cathode Ray Tube)コントローラ
CRTコントローラはVRAMに設定された通りに、ディスプレイの水平同期信号タイミング、垂直同期信号タイミング、
カーソルタイミング、アンダーラインタイミングを生成します
シーケンサ
シーケンサはVRAMにアクセスするために、メモリタイミングとVRAMからデータを読み出すキャラクタタイミングを生成します
これにより、CRTディスプレイを捜査している間に、CPUはメモリにアクセスすることができるようになります
グラフィックコントローラ
グラフィックコントローラはCRTディスプレイ捜査中はVRAMとアトリビュートコントローラのインターフェイスとし動作し、
CPUがVRAMにアクセスしている時は、CPUとVRAMのインターフェイスとして動作します
ディスプレイ捜査中は、VRAMのデータをアトリビュートコントローラに転送します。グラフィックモードの場合、
データが転送される前にパラレルからシリアルにデータ変換されてから転送します。テキストモードの場合、
パラレルデータのままアトリビュートコントローラに転送します
CPUがVRAMにアクセスしている時は、CPUがVRAMにデータを転送する前に、グラフィックコントローラが
そのデータを捜査することができます。
アトリビュートコントローラ
グラフィックコントローラがVRAMからアトリビュートコントローラにデータを転送します。この転送されるデータは、
グラフィックモードの時はシリアルデータで、テキストモードのときはパラレルデータとなります
アトリビュートコントローラはデータを受け取ると、データをもとに64色のパレットから色を選びます(実際には色の値)
この色(の値)はビデオDAC(Digital/Analog Converter)でアナログ出力に変換されて、ディスプレイに表示されます
ブリンク(瞬くような表示)、アンダーライン、カーソル移動などをアトリビュートコントローラが制御します
ビデオモード
ビデオモードには大きく分けて2つあります。APA(All Points Addressable)グラフィックモードとテキストモードです
モードを変えることで、VRAMに書かれたデータをどのように表示するかが変わります
APA(All Points Addressable)グラフィックモード
APAグラフィックモード(このサイトでは単にグラフィックモードと記載します)は、VRAM上のデータを1つ1つのピクセルとして
表示するモードです。ディスプレイは”ピクセル”と呼ばれる枠(1つのドット)を表示の最小単位としています。
テキストモード
VRAM上のデータを文字として扱い、文字をディスプレイに表示するモードです
VGAコントローラはテキストモードで2つのバッファを使用します。キャラクタマップとスクリーンバッファです
キャラクタマップはどの文字を表示するかを設定するバッファで、
スクリーンバッファはディスプレイのどこに表示するかを設定します
テキストモードは他にアトリビュートで文字色、ブリンク、アンダーラインなどを設定できます
BIOSのビデオモード
起動時にBIOSはデフォルトのビデオモードを設定します
カラーディスプレイのデフォルトモーはBIOSビデオモード3+で、
モノクロディスプレイのデフォルトモードはBIOSビデオモード7+が設定されています
(モードの番号は16進数です)
デフォルトモードの文字表示数を下記表で確認できます
BIOSビデオモード
|
モード
HEX
|
Type
|
色数
|
文字表示数
|
VRAM開始アドレス
|
ボックスサイズ
|
最大ページ
|
周波数
|
解像度
|
0, 1
|
テキスト
|
16
|
40x25
|
0x000B8000
|
8 x 8
|
8
|
70Hz
|
320x200
|
0*, 1*
|
テキスト
|
16
|
40x25
|
0x000B8000
|
8 x14
|
8
|
70Hz
|
320x350
|
0+, 1+
|
テキスト
|
16
|
40x25
|
0x000B8000
|
9 x16
|
8
|
70Hz
|
360x400
|
2, 3
|
テキスト
|
16
|
80x25
|
0x000B8000
|
8 x 8
|
8
|
70Hz
|
640x200
|
2*, 3*
|
テキスト
|
16
|
80x25
|
0x000B8000
|
8 x14
|
8
|
70Hz
|
640x350
|
2+, 3+
|
テキスト
|
16
|
80x25
|
0x000B8000
|
9 x16
|
8
|
70Hz
|
720x400
|
4, 5
|
グラフィック
|
4
|
40x25
|
0x000B8000
|
8 x 8
|
1
|
70Hz
|
320x200
|
6
|
グラフィック
|
2
|
80x25
|
0x000B8000
|
8 x 8
|
1
|
70Hz
|
640x200
|
7
|
テキスト
|
-
|
80x25
|
0x000B0000
|
9 x14
|
8
|
70Hz
|
720x350
|
7+
|
テキスト
|
-
|
80x25
|
0x000B0000
|
9 x16
|
8
|
70Hz
|
720x400
|
D
|
グラフィック
|
16
|
40x25
|
0x000A0000
|
8 x 8
|
8
|
70Hz
|
320x200
|
E
|
グラフィック
|
16
|
80x25
|
0x000A0000
|
8 x 8
|
4
|
70Hz
|
640x200
|
F
|
グラフィック
|
-
|
80x25
|
0x000A0000
|
8 x14
|
2
|
70Hz
|
640x350
|
10
|
グラフィック
|
16
|
80x25
|
0x000A0000
|
8 x14
|
2
|
70Hz
|
640x350
|
11
|
グラフィック
|
2
|
80x30
|
0x000A0000
|
8 x16
|
1
|
70Hz
|
640x480
|
12
|
グラフィック
|
16
|
80x30
|
0x000A0000
|
8 x16
|
1
|
70Hz
|
640x480
|
13
|
グラフィック
|
256
|
40x25
|
0x000A0000
|
8 x 8
|
1
|
70Hz
|
320x200
|
ここでモードの”*”と”+”はエンハンスモードを意味しています。グラフィックモードの色数は256K色あります
またグレーシェードは64色あります
VGAのVRAMアドレス
カーネルローダその1でメモリマップについて説明しました
メモリマップをもう一度見てみます
VRAMのそれぞれの領域は、
- 0x000A0000 - 0x000AFFFF : この領域はグラフィックモードで使用します
- 0x000B0000 - 0x000B7FFF : この領域はテキストモード(モノクロ)で使用します
- 0x000B8000 - 0x000BFFFF : この領域はテキストモード(カラー)とCGAグラフィックモードで使用します
と、使用目的によって扱う領域が違います。このアドレスはビデオアダプタのCRTコントローラを
制御をすることで変更することができます
ディスプレイに文字を表示させてみる
ここまでで、色々難しいことがでてきましたが、やることは非常に簡単です
VRAMにアスキーコード1バイトと文字の色などを設定するアトリビュートを1バイト書けば
文字が1文字表示されるという仕組みになっています
まずは0x0008B000に好きなアスキーコードと、0x00008B001に適当な数字をいれてみましょう
#define VRAM_TEXTMODE 0x000B8000
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Funtion :displaySample
Input :void
Output :void
Return :void
Description :print a character with setting color in text mode
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
void displaySample( void )
{
unsigned short *vram_TextMode;
vram_TextMode = ( unsigned short *)VRAM_TEXTMODE;
*vram_TextMode = ( 0x07 << 8 ) | 'A';
}
このような感じで表示できます
#define VRAM_TEXTMODE 0x000B8000
VRAMのアドレス定義です
unsigned short *vram_TextMode;
VRAMのアドレスを格納するポインタ変数です。short型にしたのは1文字を表示したいときに
VRAMに書き込む値がアスキーコード1文字(1バイト)+アトリビュート1バイト=2バイトだからです
これで1文字単位で制御することができるようになりますので、作りが簡単になるかと思います
vram_TextMode = ( unsigned short *)VRAM_TEXTMODE;
VRAMのアドレスを格納します
*vram_TextMode = ( 0x07 << 8 ) | 'A';
VRAMに文字Aのアスキーコードとアトリビュート0x07を格納します
シフトしているのは下位バイト(0x000B8000)に'A'を格納し、
上位バイト(0x000b8001)に0x07を格納したいので、
short型の値0x0741を作りたいためです
VRAMのアドレスをunsgined char*で宣言して制御しても面白いとおもいます
実際にやってみると思ったよりも結構簡単だったのではないでしょうか
ディスプレイに文字列を表示する
VRAMに書き込み文字とアトリビュートとディスプレイの関係について見ていきます
起動時にBIOSが設定するデフォルトモードはモード3+でした
このモードでは文字の表示数は80(列)×25(行)となります
80x25の文字位置とVRAMのアドレスの対応関係は次の図のようになります
アスキーコードとアトリビュートの2バイトで1文字を表しますので、アドレスは2バイトずつ増えていきます
1行は80文字ですので、アドレス0x000B80A0は2行目の最初の1文字になります
このようにアドレスとディスプレイの文字の位置は決まっていますので、計算して求めることができます
80x25の座標のうち表示したい位置を( x, y )とすると、VRAM上のアドレスは
VRAM上のアドレス = x + ( y * 80 )
と計算できます。ここで注意していただきたいのが、上記の式はunsigned short型の
ポインタで計算しているという点です。unsigned char型のポインタであれば
VRAM上のアドレス = x * 2 + ( y * 80 ) * 2
と×2をする必要があります。この計算を行うため、defineで定義しておきます
#define MAX_Y 25
#define MAX_X 80
#define MAX_XY (80*25)
のように適当な名前をつけて定義しておけば、よいかと思います
アトリビュートの設定
最初の1バイトは表示したいアスキーコードを入れました。アトリビュートは次のように設定します
アトリビュートのビットアサイン
|
ビット
|
フラグ
|
アトリビュート
|
説明
|
0-2
|
Foreground Color
|
フォアグランドカラー
|
文字の色を決定します。0から2の各ビットがRGBに対応しています
ビット0:B ブルー
ビット1:G グリーン
ビット2:R レッド
|
3
|
I/CS
|
文字の明度または文字のフォントを選択
(1ビットなので、どちらの場合でも0か1かで選択)
|
アトリビュートモード制御レジスタ(I/Oアドレス:0x03C0-0x03C1)の
設定によって文字の明度を変えるのか文字のフォントを変えるかの
機能が変わります。デフォルトでは文字の明度を変更する機能となります
|
4-6
|
background color
|
バックグラウンドカラー
|
文字の背景色を決定します。4から6の各ビットがRGBに対応しています
ビット4:B ブルー
ビット5:G グリーン
ビット6:R レッド
|
7
|
B/I
|
文字をブリンク(点滅)または文字の背景色の明度を変更
(1ビットなので、どちらの場合でも0か1かで選択)
|
アトリビュートモード制御レジスタ(I/Oアドレス:0x03C0-0x03C1)の
設定によって文字をブリンクさせるのか文字の背景色の明度を変えるかの
機能が変わります。デフォルトではブリンクさせる機能が選択されています
|
文字色と文字の背景色に使用できるカラーは以下のようになります
(Iビットの明度を入れた合計4ビットで表現できるカラーです)
RGBの設定とカラー
|
設定値
|
カラー
|
0x0
|
ブラック
|
0x1
|
ブルー
|
0x2
|
グリーン
|
0x3
|
シアン
|
0x4
|
レッド
|
0x5
|
マゼンタ
|
0x6
|
ブラウン
|
0x7
|
ライトグレイ
|
0x8
|
ダークグレイ
|
0x9
|
ライトブルー
|
0xA
|
ライトグリーン
|
0xB
|
ライトシアン
|
0xC
|
ライトレッド
|
0xD
|
ライトマゼンタ
|
0xE
|
ライトブラウン
|
0xF
|
ホワイト
|
文字の表示まとめ
アスキーコードとアトリビュートの文字色、背景色と、( x、 y )座標を指定して
文字を表示するサンプルドライバです
#define VRAM_TEXTMODE 0x000B8000
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Funtion :displaySample
Input :char c
< a character to print >
unsigned char foreColr
< foreground color >
unsigned char backColor
< background color >
int x
< x-axis position where a character is printed >
int y
< y-axis position where a character is printed >
Output :void
Return :void
Description :print a character with setting color in text mode
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
void displaySample( char c, unsigned char foreColor, unsigned char backColor,
int x, int y )
{
unsigned short *vram_TextMode;
unsigned short color;
vram_TextMode = ( unsigned short *)VRAM_TEXTMODE;
color = ( backColor << 4 ) | ( foreColor & 0x0F );
vram_TextMode += x + y * MAX_X;
*vram_TextMode = ( color << 8 ) | c;
}
文字列を表示する場合はfor文、while文などで繰り返しこの関数をコールすることで
表示します。