Video Graphic Array
- 640x480という解像度を指すこともある
- ビデオカード全般をVGAと書くときは、Video Graphic Adapter, Video Graphic Acceralatorの略と解釈するのが普通
- ここではそのどちらについても興味はなく、AT互換機上でBIOSを全く使わないで640x480x16色の描画をやるために必要な技術資料をまとめたい
- 一番お手軽な方法はBIOSを使って320x200x256色に切り替えてパレットを設定して、あとはVRAMを直接アクセスする方法である。→ (AT)BIOS
- この解像度に飽きたら、次はVESAのリニアアクセスに進むことをおすすめする(ウィンドウアクセスはウィンドウ切り替えが面倒)。
- しかし、VESAのリニアアクセスは32bitモードを使いこなす必要があるし、VESAが使えないカードもまあまあある。その場合に、ほとんどすべてのビデオカードがサポートしているVGAを利用したいと思うこともあるだろう。色数は320x200x256色に比べるとかなり少なくなるが(16色)、解像度が640x480になるのはありがたいだろう。
VGAのレジスタ
CRTコントローラ
- 0x03d4に16bitアクセスで書き込む
- bit0-7がレジスタ番号
- bit8-15がデータ
- 0x03d4と0x03d5をバイトアクセスする方法もある
- その場合はまず0x03d4にレジスタ番号をライトし、0x03d5にデータを書き込むか、もしくは0x03d5をリードしてレジスタデータを読み込む。
- モノクロディスプレイの場合、I/Oポートが0x03b4、0x03b5になる
- どっちなのか分からないときは、I/Oポート0x03ccをリードしてbit0を見れば分かる。0なら0x03b4、1なら0x03d4である。
- [0x00] Horizontal Total (水平総キャラクタ数)
- [0x01] Horizontal Display Enable End (水平表示キャラクタ数)
- bit0-7:水平走査期間内の表示キャラクタ数 - 1
- [0x02] Start Horizontal Blanking (水平非表示開始キャラクタ位置)
- [0x03] End Horizontal Blanking (水平非表示終了キャラクタ位置)
- bit0-4:水平非表示を終了するキャラクタ位置
- bit5-6:水平表示スキュー
- bit7:リザーブ
- [0x04] Start Horizontal Retrace (水平帰線開始キャラクタ位置)
- [0x05] End Horizontal Retrace (水平帰線終了キャラクタ位置)
- bit0-4:水平非表示を終了するキャラクタ位置
- bit5-6:水平帰線スキュー
- bit7:水平非表示終了位置のビット5
- [0x06] Vertical Total (垂直表示ライン数)
- [0x07] Overflow (各種上位ビット)
- bit0:Vertical Totalのビット8
- bit1:Vertical Display Enable Endのビット8
- bit2:Vertical Retrace Startのビット8
- bit3:Start Vertical Blankingのビット8
- bit4:Line Compareのビット8
- bit5:Vertical Totalのビット9
- bit6:Vertical Display Enable Endのビット9
- bit7:Vertical Retrace Startのビット9
- [0x08] Preset Row Scan (プリセット行走査)
- bit0-4:垂直スクロールのライン数(テキストモード時)
- bit5-6:水平スクロールのビット数(バイト単位)
- bit7:リザーブ
- [0x09] Maximum Scan Line (最大走査数)
- bit0-4:1キャラクタのライン数-1
- bit5:Start Vertical Blankingのビット9
- bit6:Line Compareのビット9
- bit7:200ラインモードの二重表示指定
- [0x0a] Cursor Start (カーソル開始ライン位置)
- bit0-4:カーソル表示ライン-1
- bit5:カーソル非表示フラグ
- bit6-7:リザーブ
- [0x0b] Cursor End (カーソル終了ライン)
- bit0-4:カーソル終了ライン-1
- bit5-6:カーソルスキュー
- bit7:リザーブ
- [0x0c] Start Address High (VRAM表示開始アドレス上位)
- [0x0d] Start Address Low (VRAM表示開始アドレス下位)
- [0x0e] Cursor Location High (カーソル位置上位)
- [0x0f] Cursor Location Low (カーソル位置下位)
- [0x10] Vertical Retrace Start (垂直帰線開始ライン位置)
- [0x11] Vertical Retrace End (垂直帰線終了ライン位置)
- bit0-3:垂直帰線を終了するライン位置
- bit4:垂直帰線割り込み時に通告(0のとき)
- bit5:垂直帰線割り込みの許可(0のとき)
- bit6:水平周波数の選択
- bit7:[0x00]~[0x07]レジスタのライトプロテクト(1で書き込めなくなる)
- [0x12] Vertical Display Enable End (表示ライン数)
- bit0-7:垂直表示を終了するライン位置の下位8ビット
- [0x13] Offset (VRAM上の1ライン幅)
- bit0-7:1ライン当たりの幅(グラフィックの場合はワード単位)
- [0x14] Under Line Location (下線表示ライン位置)
- bit0-4:下線表示ライン(テキストモード時)
- bit5:クロック4分周(1のとき)
- bit6:16ビット/32ビットアドレス切り替え(1で32ビット)
- bit7:リザーブ
- [0x15] Start Vertical Blanking (垂直非表示開始ライン位置)
- bit0-7:垂直非表示を開始するライン位置の下位8ビット
- [0x16] End Vertical Blanking (垂直非表示終了ライン位置)
- bit0-7:垂直非表示を終了するライン位置の下位8ビット
- [0x17] Mode Control (モード設定)
- bit0:1にするとインターレース表示
- bit1:1にすると垂直ラインアドレス有効
- bit2:1にすると垂直幅2倍
- bit3:1にするとクロック2分周
- bit4:リザーブ
- bit5:アドレス折り返し指定
- bit6:8ビット/16ビットアドレス切り替え(1で16ビット)
- bit7:1にすると垂直水平帰線許可
- [0x18] Line Compare (画面分割ライン位置)
- bit0-7:画面を分割するライン位置の下位8ビット
- 0x19以降のレジスタはリザーブ
アトリビュートコントローラ
- 0x03c0に8bitアクセスで書き込む(アドレスポート時)
- bit0-4がレジスタ番号
- bit5-7は000
- 書き込み完了と同時に、データポートに変わる
- 0x03c0に8bitアクセスで書き込む(データポート時)
- 0x03c0に8bitアクセスで、0x20を書き込む(通常モード復帰直後)
- これでアトリビュートコントローラは完全に通常モードになる
- 0x03c1に8bitアクセスで読み込む(データポート時)
- bit0-7がレジスタデータ
- 一部の資料では、リードも0x03c0のほうで読むと説明している。どちらが正しいのか、まだテストしていないので分からない(すみません)。
- 具体的なアクセス方法
- 以下の操作中は割り込みなどを受け付けないようにすること。時間的な間があいたり、他のI/Oにアクセスするだけでも、とたんにおかしくなるビデオチップが多い。
- 0x03daを空読み(0x03baの場合もある -- どちらになるかはI/Oポート0x03ccをリードしてbit0をチェックする -- 参考:VGAコントローラ)
- 空読みした値は他の用途に利用してもいいが、とりあえずアトリビュートコントローラを使うだけなら不要なので捨てる。
- 以下を好きなだけやる。
- 0x03c0にレジスタ番号を書き込む(0x00~0x14)。そして0x03c0にレジスタデータを書き込む。
- 0x03c0にレジスタ番号を書き込む(0x00~0x14)。そして0x03c1からレジスタデータを読み込む(読み込みも0x03c0を使うべきだと書いてある資料もあるのでここはちょっと自信なし)。
- (註)レジスタ番号を書き込んで、データを書くとか読むとかできるのは一度だけである。同じレジスタに連続してアクセスしたいのであれば、同じレジスタ番号をもう一度書き込むことになる。
- (註)レジスタ番号を書いたら、必ずデータを書くか読むかのどちらかをやること。
- アトリビュートコントローラへのアクセスが済んだら、またただちに0x03daを空読みする(0x03baの場合もある -- 上に同じ)
- そして0x03c0に0x20を書き込む。
- これでビデオチップは通常状態に戻る。
- [0x00]-[0x0f] Color Palette 0~15 (ビデオDAコンバータに渡すカラーコード)
- bit0-5:ビデオDAコンバータに渡すカラーコード(0~63)
- bit6-7:リザーブ(書き込み時には0推奨)
- [0x10] Mode Control (表示モード)
- bit0:グラフィックモード選択(0:テキスト、1:グラフィック)
- bit1:モノクロモード選択(0:カラー、1:モノクロ)
- bit2:グラフィックキャラクタ選択
- bit3:ブリンク選択(0:高輝度、1:ブリンク)
- bit4:リザーブ(書き込み時には0推奨)
- bit5:レジスタ[0x13]有効(0:無効、1:有効)
- bit6:パックドピクセル選択(0:カラープレーン、1:パックドピクセル)
- bit7:レジスタ[0x14]有効(0:[0x00]-[0x0f]が有効で[0x14]は無効、1:[0x14]が有効で[0x00]-[0x0f]が無効)
- [0x11] Sreen Border Color (ボーダーカラー)
- [0x12] Color Plane Enable (プレーン表示許可)
- bit0:プレーン0表示許可(1で表示)
- bit1:プレーン1表示許可(1で表示)
- bit2:プレーン2表示許可(1で表示)
- bit3:プレーン3表示許可(1で表示)
- bit4-5:Input Status 1へ出力するプレーン番号
- bit6-7:リザーブ(書き込み時には0推奨)
- [0x13] Horizontal Panning (水平スクロール)
- bit0-3:水平スクロールピクセル数
- bit4-7:リザーブ(書き込み時には0推奨)
- [0x14] Color Select (カラーコード上位)
- bit0-3:ビデオDAコンバータに渡すカラーコードの上位番号
- bit4-7:リザーブ(書き込み時には0推奨)
- 0x15以降のレジスタはリザーブ
VGAコントローラ
- 0x03c2:ステータスレジスタ(8bit、R)
- bit0-3:リザーブ
- bit4:スイッチ指定(1ならカラーディスプレイ)
- bit5-6:リザーブ
- bit7:垂直割り込み発生中
- 0x03c2:コンロトールレジスタ(8bit、W)
- bit0:I/Oアドレス指定
- 0:0x03b4、0x03b5、0x03ba
- 1:0x03d4、0x03d5、0x03da
- bit1:ビデオバッファアクセス許可(1で許可)
- bit2-3:クロック指定(00:640ドット、他は不明)
- bit4:リザーブ
- bit5:CGAページ切り替え(0:低位64KBのページ)
- bit6:水平帰線の極性
- bit7:垂直帰線の極性
- 0x03c3:VGA動作許可レジスタ(8bit、W)
- bit0:VGA動作許可(1:動作許可)
- bit1-7:リザーブ
- 0x03cc:読み出し用コントロールレジスタ(8bit、R)
- bit0-5:コンロトールレジスタと同じ(上記参照)
- bit6-7:垂直ライン指定(11なら480ライン)
- 0x03da:カラーステータスレジスタ(8bit、R)
- これは0x03ccのbit0が1の時のみ有効
- bit0:表示期間中(0のとき)
- bit1-2:リザーブ
- bit3:垂直帰線中(1のとき)
- bit4-7:リザーブ
- このレジスタをアトリビュートコントローラの通常動作時にダミーリードすると、アトリビュートコントローラの0x03c0が、アドレスポートになる。
- このレジスタをアトリビュートコントローラのデータポート利用後にダミーリードすると、アトリビュートコントローラが通常動作にもどる。
- 0x03ba:モノクロステータスレジスタ(8bit、R)
- これは0x03ccのbit0が0の時のみ有効
- bit0:表示期間中(0のとき)
- bit1-2:リザーブ
- bit3:垂直帰線中(1のとき)
- bit4-7:リザーブ
- このレジスタをアトリビュートコントローラの通常動作時にダミーリードすると、アトリビュートコントローラの0x03c0が、アドレスポートになる。
- このレジスタをアトリビュートコントローラのデータポート利用後にダミーリードすると、アトリビュートコントローラが通常動作にもどる。
シーケンサー
- 0x03c4に16bitアクセスで書き込む
- bit0-7がレジスタ番号
- bit8-15がデータ
- 0x03c4と0x03c5をバイトアクセスする方法もある
- その場合はまず0x03c4にレジスタ番号をライトし、0x03c5にデータを書き込むか、もしくは0x03c5をリードしてレジスタデータを読み込む。
- 0x03c5にアクセスすると、自動的に0x03c4がインクリメントされ、連続して次のレジスタにアクセスできる。
- 16bitアクセスの場合は、アクセス後に0x03c4に次のアドレス番号がセットされるかどうかは不明。
- [0x00] Reset (リセット)
- bit0:非同期リセット (0でリセット)
- bit1:同期リセット (0でリセット)
- bit2-7:リザーブ
- [0x01] Clock Mode (クロックとモード設定)
- bit0:文字幅指定 (0:8ドット、1:9ドット)
- bit1:リザーブ
- bit2:16ビットアドレス指定 (1で有効)
- bit3:クロック2分周指定 (1で有効)
- bit4:32ビットアドレス指定 (1で有効)
- bit5:非表示設定 (1で有効)
- [0x02] Map Mask (プレーンごとの書き込み許可)
- bit0:Plane0 Write Enable (1で書き込み許可)
- bit1:Plane1 Write Enable (1で書き込み許可)
- bit2:Plane2 Write Enable (1で書き込み許可)
- bit3:Plane3 Write Enable (1で書き込み許可)
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x03] Character Generator Select (キャラクタジェネレータ設定)
- bit0-1:Character Set Bのbit1-2
- bit2-3:Character Set Aのbit1-2
- bit4:Character Set Bのbit0
- bit5:Character Set Aのbit0
- bit6-7:リザーブ
- [0x04] Memory Mode (VRAMモード)
- bit0:リザーブ
- bit1:Memory Size (0:VRAM64KB、1:VRAM128KB)
- bit2:1、2プレーン連続指定(0で有効)
- bit3:4プレーン連続指定(0で有効)
- bit4-7:リザーブ
- 0x05以降のレジスタはリザーブ
ビデオDAコンバータ
- 0x03c6:ピクセルマスクレジスタ(8bit、R/W)
- bit0-7:DAコンバータが受け取ったカラーコードは、このレジスタの内容でマスクされた後にRGBに変換され、ディスプレイに送られる。
- 0x03c7:ステータスレジスタ (8bit、R)
- bit0-1:これが00だったら書き込み中
- bit2-7:リザーブ
- 0x03c7:アドレスレジスタ (8bit、W)
- ここに書き込みアクセスすると、パレット読み込みモードになる
- bit0-7:パレット番号
- 0x03c8:アドレスレジスタ (8bit、W)
- ここに書き込みアクセスすると、パレット書き込みモードになる
- bit0-7:パレット番号
- 0x03c9:データレジスタ (8bit、R/W)
- bit0-7:RGB成分の一つ(0~63)
- 書き込むときは上位2ビットを0に、読み込むときは上位2bitを0と見なす
- パレットのアクセスの手順
- まず一連のアクセス中に割り込みなどが入らないようにする(たとえばCLI)。
- 0x03c8に設定したいパレット番号を書き込み、続いて、R、G、Bの順に0x03c9に書き込む。もし次のパレットも続けて設定したいのなら、パレット番号の設定を省略して、さらにRGBの順に0x03c9に書き込んでよい。
- 現在のパレット状態を読み出すときは、まず0x03c7にパレット番号を書き込んで、0x03c9を3回読み出す。これが順にR、G、Bになっている。これももし次のパレットも読み出したいときは、パレット番号の設定を省略してRGBの順に読み出してよい。
- 最初にCLIした場合は、最後にSTIする。
VGAグラフィックコントローラ
- 0x03ceに16bitアクセスで書き込む
- bit0-7がレジスタ番号
- bit8-15がデータ
- 0x03ceと0x03cfをバイトアクセスする方法もある
- その場合はまず0x03ceにレジスタ番号をライトし、0x03cfにデータを書き込むか、もしくは0x03cfをリードしてレジスタデータを読み込む。
- [0x00] Set/Reset (ラッチとの演算に関するレジスタ)
- このレジスタは、書き込みモード0、3でのみ利用される
- bit0-3:カラーコード
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x01] Enable Set/Reset (ラッチとの演算に関するレジスタ)
- このレジスタは、書き込みモード0でのみ利用される
- bit0:Plane0 Enable (1でenable)
- bit1:Plane1 Enable (1でenable)
- bit2:Plane2 Enable (1でenable)
- bit3:Plane3 Enable (1でenable)
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x02] Color Compare (比較用カラーコード)
- このレジスタは、読み込みモード1でのみ利用される
- bit0-3:カラーコード
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x03] Data Rotate/Function Select (描画時の演算とCPUデータ回転指定)
- 書き込みモード0、2、3でのみ利用される
- bit0-2:右ローテートカウント(0-7) CPUデータはここで指定された値の分だけ右ローテートされて演算回路に送られる(000ならローテートしないので少し速くなる ここを使うよりもCPUでローテートしたデータを書き込むほうが高速だと言われている)
- bit3-4:描画時の演算モード(00:PSET、01:AND、10:OR、11:XOR)
- bit5-7:リザーブ (書き込み時には0推奨)
- [0x04] Read Plane Select (読み出しプレーン選択)
- このレジスタは、読み込みモード0でのみ利用される
- bit0-1:読み出しプレーン番号(0~3)
- bit2-7:リザーブ (書き込み時には0推奨)
- [0x05] Mode (アクセスモード指定)
- bit0-1:書き込みモード番号(0~3)
- bit2:リザーブ (書き込み時には0推奨)
- bit3:読み込みモード番号(0~1)
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x06] Miscellaneous (雑用レジスタ)
- 描画には直接関係しない。画面モード設定関係。詳細不明。
- bit0:Graphic Mode
- bit1:CGA Compatible
- bit2-3:VRAM Address (00:0xa0000~0xbffff、01:0xa0000~0xaffff、10:0xb0000~0xbffff、11:0xb80000~0xbffff)
- bit4-7:リザーブ
- [0x07] Color Don't Care (カラー比較無効プレーン指定)
- このレジスタは、読み込みモード1でのみ利用される
- bit0:Plane0 (1だとこのプレーンは一致したと見なされる)
- bit1:Plane1 (1だとこのプレーンは一致したと見なされる)
- bit2:Plane2 (1だとこのプレーンは一致したと見なされる)
- bit3:Plane3 (1だとこのプレーンは一致したと見なされる)
- bit4-7:リザーブ (書き込み時には0推奨)
- [0x08] Bit Mask (描画マスク)
- bit0-7:描画マスクデータ(0のビットは描画されない)
- 0x09以降のレジスタはリザーブ
描画に関係するレジスタのリスト
- こんなにレジスタがいっぱいあると混乱すると思うので、画面モード設定とか仮想画面サイズ設定やパレット設定などにしか使わないレジスタを除いた、純粋に描画に関係するレジスタのリストを作ってみました。それぞれの詳しい説明は上記参照。
- シーケンサーより:
- VGAグラフィックコントローラより:
- Set/Resetレジスタ
- Enable Set/Resetレジスタ
- Data Rotate/Function Selectレジスタ
- Modeレジスタ
- Bit Maskレジスタ
VRAMアドレスとラッチ
- VGAモードの場合、VRAMのアドレスは0xa0000からの64KBになる。0xb0000からの64KBもビデオカード用に予約されているが、VGAモードでは使わない。
- 1ドットが1ビットに対応しており、640x480/8=37.5KBにアクセスすることになる。VGAの表示域は640x480固定だが、例えば800x600/8=58.6KBくらいの仮想画面を使うこともできる(なお800x600の表示域を設定するためのVGA互換VESAモードというのもある → (AT)BIOS の画面モード0x6aを参照)。
- この説明だとFMRみたいにバンク切り替え式のプレーンアクセスを想像したくなるが、実はそうではない(書き込みモード3でラッチをクリアしておいて、さらにMap Maskレジスタをうまく使えば似たようなことはできます)。
- 普通のプレーンアクセスだと、ANDやORやXORをした場合、アクセスしたアドレスのデータと演算をして書き込むが、VGAではアクセスしたアドレスのデータは演算には使わない。ラッチと呼ばれる一種のレジスタがあり、そのレジスタとCPUが書き込んだデータとの間で演算をして、アクセスしたアドレスに書き込まれる。
- これは推測だが、おそらくVRAMの構造が、CPUからの書き込みを高速にする代わりに読み出しを低速にしていて、演算のたびにVRAMを読み出さなくていいように、ラッチを使うように設計されているのだろう。
- ラッチのデータとANDやORやXORの演算処理をするのはビデオチップの中の話であり、CPUにはこれらのデータを直接読むことはできない。したがって、VRAMに対しては、ANDやORであっても、普通のMOV命令を使う(VRAMへの書き込み)。結局PSETの場合と同じ方法で、ANDやORができるということである。PSET/AND/OR/XORの指定は、Data Rotate/Function Selectレジスタのbit3-4で行なう。
- ラッチにデータをセットするには、VRAMをリードすればよい。リードした部分のVRAMの状態がラッチとしてビデオチップに取り込まれる。
4つの書き込みモード
- モード0
- ややこしい割には用途不明。
- Enable Set/Resetレジスタでenableになっているプレーンに対しては、モード3でCPUデータが0xffだったときと同じ結果になる(つまりこのプレーンに関してはライト時のCPUデータは無視されていることになる)。
- Enable Set/Resetレジスタでdisableになっているプレーンに対しては、モード3でSet/Resetレジスタが0x0fだったときと同じ結果になる(つまりこのプレーンに関しては書き込み時のSet/Resetレジスタの内容が無視されていることになる)。
- このモードが、他のモードよりも便利に使えるような局面がありましたら、是非教えてください。
- モード1
- CPUデータに関わらず、ラッチの内容がそのままVRAMに書き込まれる。Bit Maskレジスタのマスクすらきかない(らしい)。
- もっとも高速な描画モード。
- VRAMをフィルしたい場合などに多用する。最初の8ドットを他のモードで描き込んで、それをラッチに読み込んで、あとはライトアクセスするだけでフィルされていくわけである。
- OSASKではマウスカーソルの消去にも使っている(描画前にVRAMの空き領域へモード1を使ってマウスカーソル位置のVRAM状態を転送(リードとライトを交互にやる)。消去時は転送方向を逆にする)。
- モード2
- CPUが書き込んだデータをカラーコードとして解釈(上位4bitは無視される)。
- Data Rotate/Function Selectレジスタの機能で、ラッチの内容と演算。
- アクセスしたアドレスの8ドットがそれぞれ同じカラーで同じ演算を施されることになる。
- Bit Maskレジスタは有効で、マスクされたドットは変更されない。
- 結局どういう状況で役立つのかよくわからないモード。
- モード3
- CPUが書き込んだデータを8ドットのマスクパターンとして解釈。1のドットはラッチのデータと演算され、0のドットはラッチのデータがそのまま使われる。
- Data Rotate/Function Selectレジスタの機能で、ラッチの内容と演算。
- 演算に利用するカラーはSet Resetレジスタで指定。
- Bit Maskレジスタは有効で、マスクされたドットは変更されない。
- K が一番有用だと思っているモード。OSASKでは、フォントの描画などで多用している。
- ラッチに背景色を読み込んでおいて、フォントカラーをSet Resetレジスタにセット。Bit Maskは0xff。PSETモードでフォントパターンをCPUからVRAMに書き込んでやると、比較的高速にフォントが描画できる。
- ちなみにOSASKではほとんどの描画において、書き込みモードは3固定でBit Maskは0xffに固定されている。例えば1ドットだけ打ちたいときは、Bit Maskを変更して他の7ドットを不変にするのではなく、まずその場所をリードしてラッチに取り込ませて、それで同じアドレスに0x80などを書く。この方法がいいのは、カラーを変更しない限りI/Oアクセスしなくていいので、描画速度が速いこと。
- ついでなので書いておくと、OSASKではVRAMの空き領域(画面外)の16バイトを使って、カラー0が8ドット並んだ部分、カラー1が8ドット並んだ部分、カラー2が8ドット並んだ部分・・・を隠し持っている。これはラッチを任意の色で埋め尽くすのに使っていて、文字描画の背景色設定や、モード1でフィルする場合のラッチ初期化用に利用している。
- もし、他に良さそうな方法があったら(モード3に限らず)こめんと欄にどんどん情報をお寄せください。みんなで高速なアルゴリズムを共有しましょう。
2つの読み込みモード
- モード0
- VRAMに読み込みアクセスすると、その8ドットがラッチに取り込まれ、ラッチのうちのRead Plane Selectで選ばれたプレーンの内容が、そのままCPUに渡される。
- 単純明快なので、多分読み出しモードのうちでは最も高速。
- モード1
- VRAMに読み込みアクセスすると、その8ドットがラッチに取り込まれ、ラッチの8ドットがColor Compareレジスタとそれぞれ比較され、一致したビットは1、不一致は0として8ビットに集計され、その結果がCPUに渡される。
- 比較の際にはColor Don't Careレジスタも利用される。
- 多少複雑なのでリードにwaitがおおめに入る可能性がある。しかし、これはこれで便利なモードだと思う。
パレットの設定
- VGAのパレット設定は2段式になっている。
- まずビデオチップはVRAMからカラーコード(0~15)をリードする。
- それをアトリビュートコントローラ内のテーブルで、0~63のカラーコードに変換する。
- その0~63の値は、ピクセルマスクレジスタ(ビデオDAコンバータ)の内容でマスクされる(結果は0~63)。
- マスクされた値からビデオDAコンバータのテーブルでRGBに変換する。
- それぞれのRGB階調数は6bit。つまりVGAは26万色中16色ということになる。
- 2段式になっているのは、EGAとの互換のため。
- これはとてもうっとうしいので、アトリビュートコントローラ内のテーブルを、0→0、1→1、2→2、3→3、・・・、15→15のように設定しておくことをおすすめする。
- アトリビュートコントローラへのアクセスと、ビデオDAコンバータへのアクセスは、時間をかけてはいけないので、設定中に割り込みが入らないようにしておくことが好ましい。
- 中途半端なところで時間がかかるとハードウェア側のフェーズが戻ってしまい、ソフトウェア側のフェーズとの不一致が起きて、誤動作するだろう。
- アトリビュートコントローラがパニックを起こすとビデオチップがハングアップする。
スクロールなど
- VRAM構成を実画面サイズよりも大きくする方法
- VGAではVRAMに余裕があれば、VRAM上の画面サイズを640x480以上に設定可能で、これを使って画面をスクロールさせることができる。
- これはCRTコントローラのオフセットレジスタを使う([0x13])。ここはVRAM上のx方向の画面サイズを設定するところで、ワード単位、すなわち16ドット単位で指定する。
- たとえば640ドットなら0x28、800ドットなら0x32になる。
- スクロール
- とりあえず、CRTコントローラのレジスタ[0x0c]と[0x0d]を使えば、x方向8ドット単位、y方向1ドット単位のスクロールができる。
- もしx方向に1ドット単位のスクロールがしたい場合は、8で割ったあまりをアトリビュートコントローラのレジスタ[0x13]に設定する。
BIOSを使わないでVGAモードにするには
- 前提:切り替える前の画面モードは、VGAもしくはそれ以前の規格(MDA、CGA、EGA、MCGA)時代の画面モードでなくてはいけない(BIOSの画面モードで言えば、0x00~0x07と0x0d~0x13のどれか)。これを満たさない画面モード(VESAの拡張された画面モードなど)は、VGAレジスタのリザーブ部分でVGA非互換にする設定になっていることが多く、VGAで定義されているレジスタをいじってもVGA画面にならない。
- またこの方法で画面を切り替えた後は、BIOSやVESAファンクションで画面を切り替えることもたいていはできなくなる。というのは、BIOSやVESAは現在の画面モードを内部で覚えていて、画面モード切り替えの際には変化するレジスタだけを設定したりするものが多く、このように自前でいじってしまうと食い違いが生じてBIOSやVESAは混乱し、切り替えがうまくできなくなるためである。
- しかしここでは冒頭のように、BIOSを使いたくないとか、さらにVESAも使いたくない、もしくは使えないという状況を想定しての話なので、これは制約にはならないだろう。
- またBIOSやVESAで画面モードを切り替えても、たとえばパレットを自前のI/Oアクセスでいじったり、VRAM上の1ライン当たりのバイト数などをいじることは、その後のBIOS経由、VESA経由の画面モード設定には影響しないようである。だから、ここでの設定例を参考にしながら、上記レジスタ群の意味を理解し、活用するというのもいいだろう。
- 具体的な設定例
- 以下適宜に割込み禁止などをして、タイミングが狂わないようにすること
- シーケンサーの[0x00]に0x01を書く(DX = 0x03c4; AX = 0x0100; OUT(DX, AX);)
- VGAコントローラの設定
- コントロールレジスタに0xe3を書く(DX = 0x03c2; AL = 0xe3; OUT(DX, AL);)
- VGA動作許可レジスタに0x01を書く(DX = 0x03c3; AL = 0x01; OUT(DX, AL);)
- シーケンサーの設定
- レジスタ[0x01]~[0x04]に、{ 0x01, 0x0f, 0x00, 0x06 }を書く
- そのあとでレジスタ[0x00]に0x03を書く
- CRTコントローラの設定
- レジスタ[0x11]に0x20を書く
- レジスタ[0x00]~[0x07]に、{ 0x5f, 0x4f, 0x50, 0x82, 0x54, 0x80, 0x0b, 0x3e }を書く
- レジスタ[0x08]~[0x0f]に、{ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }を書く
- レジスタ[0x10]~[0x18]に、{ 0xea, 0x8c, 0xdf, 0x28, 0x00, 0xe7, 0x04, 0xe3, 0xff }を書く
- VGAグラフィックコントローラの設定
- ここの設定は自分が最初に使うモード等にあわせて設定してよい。ここでは[0x06]の設定が主な目的。
- レジスタ[0x00]~[0x08]に、{ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x03, 0x05, 0x00, 0xff }を書く
- アトリビュートコントローラの設定
- 0x03daのダミーリードなどを忘れずに
- レジスタ[0x00]~[0x07]に、{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }を書く
- レジスタ[0x08]~[0x0f]に、{ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f }を書く
- レジスタ[0x10]~[0x14]に、{ 0x01, 0x00, 0x0f, 0x00, 0x00 }を書く
- 0x03daのダミーリードと0x20の書き込みを忘れずに
- ビデオDAコンバータのピクセルマスクに0xffを書く(DX = 0x03c6; AL = 0xff; OUT(DX, AL);)
- あとはビデオDAコンバータのパレット0x00~0x0fを、お好みに合わせて設定
こめんと欄
- プレーンアクセスはできないんですか? OSASKのソースを見るとグラフィック転送はプレーンの方が速そうな気がします。 -- I.Tak. 2003-09-01 (月) 22:26:29
- 実は普通のプレーンアクセスはできないのです。ラッチという厄介な(しかしある意味有用な)仕組みがありまして、リードとライトを交互にやらなければいけないという・・・。その辺は少しずつ説明を書きますので、まあ1ヶ月くらい待っていてください。 -- K 2003-09-02 (火) 14:11:06
- いやちがうな、ラッチは気にしないでいいのか。プレーンアクセスっぽいこともできます。そっちのほうが速いのかな。パックドピクセルをプレーンに変換するのに手間が掛かりそうで検討しませんでしたが・・・。たしかOSASKのカラーフォント描画ルーチンが、フォントパターン受け取り時にコンバートして、プレーンアクセス風に描画していたと思います。>I.Tak.さん -- K 2003-09-02 (火) 14:27:07
- とりあえず、描画と読み出し関係だけは全部書きました。BIOSで画面モードとパレット設定をやれば、あとは自力で描画できるはずです。 -- K 2003-09-04 (木) 08:23:41
- 書き込みモード2を使って短形描画しています。でもこう見てみるとモード3のほうが便利かも -- ぐりぽん 2003-09-05 (金) 22:33:21
- とりあえず、これでパレット設定もBIOSを使わずにできます。 -- K 2003-09-09 (火) 12:25:25
- BIOSを使わない設定についても一応書けました。 -- K 2003-09-10 (水) 11:14:14
- とりあえず、VGAに関して集めた資料は全部まとめたつもりです。 -- K 2003-09-12 (金) 11:17:30
- すごい!今まで英語とか読んで苦労してたのが嘘みたいにラクです! -- ぐりぽん 2003-09-12 (金) 18:49:14
- テキストモードにする手順キボンと煽って見るテスト -- 2003-09-12 (金) 19:18:10
- テキストモード使いますか?・・・いやー、OSASKでは使わないだろうと思ったので、資料集めをまじめにやってきませんでした。テキストモードを使うOS開発プロジェクトが projects に加わって、そんでもってその人たちがすばらしい資料を(AT)Textmodeみたいなページ名で追加してくれたら、とてもいいのではないかと・・・。ぐりぽんさん、お役に立てたようで、うれしいです。 -- K 2003-09-12 (金) 19:38:28
- 何かあったときテキストモードはデバッグ等に非常に有用なモードだと思うので。 -- 2003-09-13 (土) 10:44:45
- いやその、テキストモードの有用性は否定しないのですが、このOS-Wikiでは projects が必要としない情報を書いてはいけないというルールがあるので、テキストモードを積極的に利用するOSが projects に入ってきてくれるか、もしくはすでに登録されているOSASKとかがテキストモードに対応しないと(デバッグ時のみでももちろん可)、ここでは情報を記載できないのです。・・・OSASKでは、グラフィックモードを使ってのテキスト表示ルーチンが非常時も安定しているので、わざわざテキストモード用のデバッグルーチンを書くことはたぶんないかと・・・。VGAに切り替えてグラフィックモードで書いちゃえばいいし・・・。そういう意味で、Monaが来てくれると幅が出てうれしいんですが・・・。 -- K 2003-09-13 (土) 12:19:15
- Monaはすでにグラフィックモードで半角カナ表示してるので、テキストモードに戻ることは(私の書いたコードが糞でない限り)ないんじゃないでしょうか。osageではブートストラップが完成するまでテキストモードを利用していましたが、ブートストラップが完成しGUIに移行しようという現時点ではテキストモードは必要ありませんし。。 -- ぐりぽん 2003-09-13 (土) 19:23:22
- もっと言えば致命的エラー発生時にはレジスタダンプが再優先事項ではないかなーと -- ぐりぽん 2003-09-13 (土) 19:35:49
- [ビデオDAコンバータ]->[パレットのアクセスの手順]で、パレット読み込みアドレスと書き込みアドレスが逆ですよ。(パレット操作する必要性に迫られて気付きました(^^;) -- ぐりぽん 2003-10-18 (土) 10:05:43
- ほんとだ・・・。直しました。ご指摘ありがとうございます。 -- K 2003-10-18 (土) 11:07:21
- シーケンサーの[0x01]Clock Mode bit0の説明が逆のような。1のとき8ドットですよね? -- トオリスガリ 2005-05-31 (火) 18:58:05
- 指摘されたら急に自信がなくなってきました。確認しますので、しばらくお待ちください。>トオリスガリさん -- K 2005-06-04 (土) 21:44:20
- VESA非対応のPCで800x600表示できました。うれしー。 所で、デバッグ時にはシリアルポートからASCII吐き出すのがラクでは・・ -- pen 2006-04-13 (木) 15:26:37
- ↑(VESA VBE1.2非対応) -- pen 2006-04-13 (木) 17:11:17
- Hi, you have very good site! Thank you! -- Mark 2006-08-25 (金) 10:06:02