Operating Systems Development Series
Prepare for the Kernel part 1
by Mike, 2008, 2009

はじめに

ようこそ!:)

これまで、私たちは多くのことを学んできたのではないでしょうか。OSの開発がいかに複雑であるかということがおわかりいただけたと思います。しかし、それでもなお、難しくなる一方です。

今回は2部構成のチュートリアルの第1回目です。最初のパートでは、アセンブリによる基本的な 32 ビットグラフィックプログラミングについて詳しく説明します。これには以下が含まれます。基本的なVGAプログラミングのコンセプト、ビデオディスプレイへのアクセス、文字列の印刷、画面のクリア、ハードウェアカーソルの更新などです。少し数学が入りますが、それほど多くはありません :)

デモ自体は完成しています。このチュートリアルの第2部では、新しい小さな FAT12 ドライバである Floppy ドライバを含む、完成した Stage 2 ソースコードの概要を紹介します。 これらは、私たちが追加する定義上「本物の」ドライバではありません。しかし、これらはドライバの機能と、なぜそれらが有用であるかを示すのに役立ちます。すべてのコードは、ブートローダからFAT12をロードするコードを大幅に修正したものなので、FAT12について再び詳しく説明するつもりはありません。

ステージ2の最後のチュートリアルであるパート2では、1MBの基本的な(純粋なバイナリ)カーネルイメージのロードと実行について説明します。

この 2 部構成のチュートリアルは、Stage 2 の最後のチュートリアルとなります!カーネルを起動するとき、さまざまな実行可能ファイル形式をカバーする必要があります。Stage 2がオブジェクトファイルを正しく実行することを確認する必要があります。このため、カーネルを起動するときに、現在の Stage 2 ブートローダにローダを追加して、カーネルを正しくロードすることを確認します。これは後の話ですが :)

このチュートリアルのパート 1 では、このようなことを学びます。

  • VGA プログラミングの基本概念
  • ディスプレイへのアクセス
  • 文字の印刷
  • 文字列の印刷
  • CRTマイクロコントローラの理論とハードウェアカーソルの更新
  • 画面の消去
このチュートリアルは、悪名高いチュートリアル7を多く参照しています。つまり、Real Mode Addressing MapDefault I/O Port Addresses です。ビデオアドレス空間とVGAポートアクセスについて説明するときに、このチュートリアルが役に立つかもしれません。

準備完了

ディスプレイについて

VGA - 理論

VGA(Video Graphics Array)は、1987年にIBMから発売されたアノログコンピュータディスプレイの規格です。MDACGAEGAが使用していたISA(Industry Standard Architecture)ボード内の数十個のロジックチップに代わる1つのチップとして開発されたため「Array」と呼ばれている。これが1枚のISAボードに収まっていたため、マザーボードへの接続が非常に容易だった。

VGAは、ビデオバッファ、ビデオDAC、CRTコントローラ、シーケンサユニット、グラフィックコントローラ、アトリビュートコントローラで構成されています。ビデオドライバの話を始めるまで、まだすべてを詳しく説明しないことに注意してください。これは、主にスペースを節約するためと、VGAのプログラミングが非常に複雑になるため、物事をより簡単にするためです。

ビデオバッファ

ビデオバッファは、ビデオメモリとしてマッピングされたメモリのセグメントである。どの領域のメモリをビデオメモリにマッピングするかは変更可能だ。起動時には、BIOSはビデオメモリを0xA0000.にマッピングしています(0xA0000.(チュートリアル7のリアルモードアドレスマップを覚えていますか?)これは重要です!

ビデオDAC

ビデオデジタルアナログコンバータ(DAC)には、ビデオデータをアナログビデオ信号に変換し、ディスプレイに送信するために使用するカラーパレットが含まれています。この信号は、赤、緑、青の強度をアナログで示します。後で詳しく説明しますので、まだ理解できていなくても気にしないでください。

CRTコントローラ

このコントローラは、水平・垂直同期信号のタイミング、ビデオバッファのアドレス指定、カーソルやアンダーラインのタイミングを生成します。カーソルを更新するときにCRTコントローラを経由する必要があるので、このチュートリアルの後半でより詳細に説明します。

シーケンサ

シーケンサは、ビデオメモリの基本的なメモリタイミングと、再生バッファフェッチを制御するためのキャラクタクロックを生成します。このため、システムがアクティブな表示間隔中にメモリにアクセスすることができます。 もう一度言いますが、ここではまだ詳細を説明しません。後でビデオドライバについて詳しく説明しますので、ご安心ください。

グラフィックコントローラ

ビデオメモリと属性コントローラ、およびビデオメモリとCPUの間のインターフェースです。アクティブな表示時間帯には、ビデオバッファ(ビデオメモリ)からメモリデータが送られ、アトリビュートコントローラに送られます。グラフィックモードでは、このデータはパラレルからシリアルのビットプレーンデータに変換されて送信されます。テキストモードでは、パラレルデータだけを送信します。

まだ理解していなくても気にしないでください。後でビデオドライバを開発するときに詳しく説明します。とりあえず、これだけは覚えておいてください。グラフィックコントローラは、ビデオメモリからのパラレルデータからディスプレイをリフレッシュします。これは、ディスプレイがアクティブな時間に基づいて自動的に行われます。つまり、ビデオメモリ(デフォルトでは0xA0000にマッピング)に書き込むことで、現在のモードによっては、実質的にビデオディスプレイに書き込むことになるのです。これは、文字を印刷するときに重要です。

グラフィックスコントローラで使用されるアドレス範囲を変更することが可能であることを忘れないでください。初期化の際、BIOSはビデオメモリを0xA0000にマップするために、まさにこれを行います。

ビデオモード

ビデオモード」とは、表示の仕様のことである。つまり、ビデオメモリーをどのように参照し、そのデータをビデオアダプターでどのように表示するかを記述している。

VGAは2種類のモードをサポートしています。APAグラフィックスとテキストです。

APAグラフィックス

APA(All Points Addressable)とは、ビデオモニターやドットマトリクスなど、画素配列で構成された装置において、各セルを個別に参照できる表示モードのこと。 ビデオ表示の場合、各セルは「画素」を表し、各画素は直接操作することができる。このため、ほとんどすべてのグラフィックモードがこの方式を採用しています。このピクセルバッファを変更することで、画面上の個々のピクセルを効果的に変更することができるのです。

画素(ピクセル

ピクセルとは、ディスプレイ上で表現できる最小の単位です。ディスプレイ上では、色の最小単位を表し、基本的には1つのドットとなります。各ピクセルの大きさは、現在の解像度とビデオモードに大きく依存します。

テキストモード

テキストモードとは、APAのように画面上のコンテンツをピクセルではなく、文字で内部表現する表示モードです。

ビデオコントローラは、テキストモードを実装する場合、2つのバッファを使用します。キャラクタマップは表示される個々の文字のピクセルを表し、バッファは各セルにどのような文字があるのかを表します。キャラクタマップバッファを変更することで、文字そのものを変更し、新しい文字セットを作成することができます。各セルの中にある文字を表すスクリーンバッファを変更することで、画面上に表示される文字を実質的に変更することができます。 テキストモードによっては、文字の色や、点滅、下線、反転、明るさなどの属性を指定することもできます。

MDA、CGA、EGA

VGAは、MDA、CGA、EGAをベースにしていることを忘れないでください。VGAは、これらのアダプタが行うモードのほとんどをサポートしています。これらのモードを理解することは、VGAをよりよく理解することにつながります。

MDA - 理論編

私が生まれる前の1981年に、IBMはPC用の標準的なビデオディスプレイカードを開発しました。モノクロディスプレイアダプタ(MDA)、モノクロディスプレイ&プリンタアダプタ(MDPA)である。

MDAにはグラフィックモードは一切なかった。80列×25行の高解像度テキスト文字を表示できるテキストモード(Mode 7)を1つだけ持っていた。

このディスプレイアダプターは、古いPCでよく使われていた規格である。

CGA - 理論編

また、1981年にはCGA(Color Graphics Adapter)を開発し、PC用カラーディスプレイの標準規格を初めて策定した。

CGAは、1ピクセルあたり4バイトに制限されていたため、16色のカラーパレットしかサポートしていなかった。

CGAは、以下の2つのテキストモードと2つのグラフィックモードをサポートしていた。

  • 40x25文字(16色)のテキストモード
  • 18x25文字(16色)のテキストモード
  • 320x200ピクセル(4色)のグラフィックモード
  • 640x200ピクセル(モノクロ)グラフィックス・モード
このディスプレイアダプターは、新しい「文書化されていない」ビデオモードを作成したり、発見したりすることが可能です。これについては後で詳しく説明します。

EGA - 理論編

1984年にIBMが発表したEGA(Enhanced Graphics Adapter)は、最大解像度640×350ピクセルで16色のディスプレイを実現した。

VGAアダプタは、80x86マイクロプロセッサファミリーと同様に、下位互換性があることを覚えておいてください。このため、BIOSは80列×25行をサポートするMode 7(元はMDA)で起動します。これは私たちにとって重要なことで、私たちはこのモードで使用しています。

VGAメモリのアドレス指定

VGAコントローラで使用されるビデオメモリは、PCのメモリに0xA0000から0xBFFFFまでマッピングされています。 チュートリアル5のリアルモードメモリマップを思い出してください。

通常、ビデオメモリは次のようにマッピングされています。

  • 0xA0000 - 0xBFFFFグラフィックモードに使用されるビデオメモリ
    • 0xB0000 - 0xB7777モノクロテキストモード
    • 0xB8000 - 0xBFFFFカラーテキストモードとCGA互換のグラフィックモード
メモリマッピングのアドレスが異なるため、同じマシンにECG、CGA、VGAの各ディスプレイアダプタをインストールすることが可能です。

ビデオアダプタカードが使用するメモリマッピングは、CRTマイコンを通じて変更することが可能です。通常、これはビデオドライバを介して行われます。これについては、後で詳しく説明します。

また、Video Controllerがこのメモリをどのように使用するかを変更することも可能です。そうすることで、「新しい」、いや、「文書化されていない」モードを作ることができます。よくあるモードの1つは、悪名高い「モードX」です。

ディスプレイバッファとテキストバッファを変更すると、画面に表示される内容が事実上変更されるのを覚えていますか?これは、ビデオコントローラが現在のリフレッシュレートに基づいてディスプレイをリフレッシュするために行われます。ビデオコントローラは、VGAポートを介してモニタ内部のCRTコントローラにコマンドを送信します。これにより、CRTの垂直方向と水平方向のリトレースが発生し、モニターの表示が更新されます。また、テキストとディスプレイのアダプタは、上記のPCのメモリアドレスにマッピングされているため。

このメモリ領域に書き込むと、画面に表示される内容が変化する

例えば、今が「モード7」であることを思い出してください。モード7はカラーテキストモードなので、0xB8000で始まるメモリを使用します。これはビデオコントローラが表示内容を決定するためのテキストバッファなので、0xB8000に書き込むと、実質的に画面にテキストが表示されることになります。

%define VIDMEM 0xB8000 ; video memory mov edi, VIDMEM ; get pointer to video memory mov [edi], 'A' ; print character 'A' mov [edi+1], 0x7 ; character attribute
この場合、ディスプレイの左上隅に白と黒の背景(The attribute)で「A」という文字が表示されます。

文字の印刷

さて、では、画面上の任意のx/y位置に文字を表示するにはどうしたらいいのでしょうか?

メモリの特殊な性質として、メモリの線形性があります。表示されている行の終端に到達すると、次のバイトはその直下の行にあります。リニアアドレスのため、画面に表示するためには、x/yの位置をリニアアドレスに変換できなければなりません。 そして、それを行うための特別な数式は、x + y * 画面幅です。

以下はその例です。例えば、文字'A'を x/y (5,5) にプリントするとします。 ビデオメモリは0xb8000で始まり、リニアなので、この x/y 位置を絶対アドレスに変換する公式を使用することができます。

address = x + (y * screen width) address = 5 + (5 * 80) address = 5 + 400 address = 405 This means, from the start of video memory, location 5,5 is 405 bytes away. So, add this to the base address of video memory: 0xB8000 + 405 = 0xB8195
つまり、0xB8195 に文字 'A' を書き込めば、実質的に x/y の位置 (5,5) に書き込むことになります。クールでしょう?

これは、BIOSのように動作させるためで、他のプログラムには必要ありません。

_CurX db 0 ; current x/y location _CurY db 0 %define VIDMEM 0xB8000 ; video memory %define COLS 80 ; width and height of screen %define LINES 25 %define CHAR_ATTRIB 14 ; character attribute (White text on black background)
ここはモード7であることを思い出してください。このモードでは、1行に80文字、25行の列があります。 そしてもちろん、ビデオメモリは0xB8000から始まります。でも、ちょっと待ってください。文字属性ってなんだろう?

テキストモード7は、実際には1文字あたり1バイトではなく、2バイトを使用します。これを覚えておいてください! 1バイト目は実際の文字を表し、2バイト目は...お待ちください...属性バイトです!このため、Mode 7 で画面に文字を書き込むときは、1 バイトではなく2バイトを書き込む必要があります。

アトリビュートバイトは、色や、点滅などのアトリビュートを指定するためのものです。その値は

  • 0 - Black
  • 1 - Blue
  • 2 - Green
  • 3 - Cyan
  • 4 - Red
  • 5 - Magenta
  • 6 - Brown
  • 7 - Light Gray
  • 8 - Dark Gray
  • 9 - Light Blue
  • 10 - Light Green
  • 11 - Light Cyan
  • 12 - Light Red
  • 13 - Light Magenta
  • 14 - Light Brown
  • 15 - White

属性バイトは、特定の属性を定義するバイトであり、前景色と背景色の両方を定義する。このバイトは次のような形式をとる。

  • Bits 0 - 2: Foreground color
    • Bit 0: Red
    • Bit 1: Green
    • Bit 2: Blue
  • Bit 3: Foreground Intensity
  • Bits 4 - 6: Background color
    • Bit 4: Red
    • Bit 5: Green
    • Bit 6: Blue
  • Bit 7: Blinking or background intensity
さて、これですべての設定が終わったので、文字を印刷してみましょう。

設定方法

文字の印刷は少し複雑で、現在の x/y 位置とビデオメモリに書き込むときの位置を追跡する必要があります。また、改行文字のような特定の文字を追跡し、行末を監視する必要があります。さらに、ハードウェアカーソルをこの位置に更新することも必要です。

Putch32は、ステージ2で文字を表示するpmodeルーチンです。心配しないでください、これらのルーチンはCを使ってカーネル用に書き直します。アセンブリでどのように行われるかを示すことによって、アセンブリ言語とC言語の関係を比較することができます。

ともかく、スタートアップ・コードを紹介しましょう。

bits 32 %define VIDMEM 0xB8000 ; video memory %define COLS 80 ; width and height of screen %define LINES 25 %define CHAR_ATTRIB 14 ; character attribute (White text on black background) _CurX db 0 ; current x/y location _CurY db 0 ;**************************************************; ; Putch32 () ; - Prints a character to screen ; BL => Character to print ;**************************************************; Putch32: pusha ; save registers mov edi, VIDMEM ; get pointer to video memory
さて、いくつかの基本的な定義ができました。_CurX と _CurY には、文字を書き込むための現在の x/y 位置が入ります。また、EDIにはビデオメモリーのベースアドレスが含まれています。つまり、ビデオメモリ[EDI]に書き込むことで、現在のビデオメモリマップに対応した文字を画面に表示することができるのです。

文字を表示する前に、それをどこに表示するかを調べなければなりません。そのためには、現在のX/Yの位置(_CurXと_CurY)に書き込めばいいのです。しかし、これはかなり単純ではありません。

ビデオメモリはリニアなので、x/yの位置をリニアメモリに変換しなければならないのです。x + y * スクリーン幅という公式を思い出してください。これは簡単に計算できますが、各文字は2バイトの大きさであることを忘れないでください。CurX、_CurY、COLS、LINESは、バイトではなく、文字に基づいていることを忘れないでください。1文字あたり2バイトなので、80*2で比較しなければならない。簡単でしょう?

このように、少し複雑になりますが、それほど難しいことではありません。

;-------------------------------; ; Get current position ; ;-------------------------------; xor eax, eax ; clear eax ;-------------------------------- ; Remember: currentPos = x + y * COLS! x and y are in _CurX and _CurY. ; Because there are two bytes per character, COLS=number of characters in a line. ; We have to multiply this by 2 to get number of bytes per line. This is the screen width, ; so multiply screen with * _CurY to get current line ;-------------------------------- mov ecx, COLS*2 ; Mode 7 has 2 bytes per char, so its COLS*2 bytes per line mov al, byte [_CurY] ; get y pos mul ecx ; multiply y*COLS push eax ; save eax--the multiplication
これは、y * 画面幅 (バイト)、または _CurY * (COLS*1 文字あたりのバイト数) という式の最初の部分です。これをスタックに格納することで、計算式を完成させることができます。
;-------------------------------- ; Now y * screen width is in eax. Now, just add _CurX. But, again remember that _CurX is relative ; to the current character count, not byte count. Because there are two bytes per character, we ; have to multiply _CurX by 2 first, then add it to our screen width * y. ;-------------------------------- mov al, byte [_CurX] ; multiply _CurX by 2 because it is 2 bytes per char mov cl, 2 mul cl pop ecx ; pop y*COLS result add eax, ecx
それでは!現在のバイト位置を得るために_CurXを2倍していることに注意してください。次に、y * COLSの結果をポップしてxの位置に追加し、x+y*COLSの式を完成させます。

やったー!さて、EAXには文字を表示するためのオフセットバイトが含まれているので、それをビデオメモリのベースアドレスを保持しているEDIに追加しましょう。

;------------------------------- ; Now eax contains the offset address to draw the character at, so just add it to the base address ; of video memory (Stored in edi) ;------------------------------- xor ecx, ecx add edi, eax ; add it to the base address
さて、これでEDIには書き込むべき正確なバイトが入り、BLには書き込むべき文字が入ります。もしその文字が改行文字であれば、次の行に移動します。そうでない場合は、その文字を表示するだけです。
;-------------------------------; ; Watch for new line ; ;-------------------------------; cmp bl, 0x0A ; is it a newline character? je .Row ; yep--go to next row ;-------------------------------; ; Print a character ; ;-------------------------------; mov dl, bl ; Get character mov dh, CHAR_ATTRIB ; the character attribute mov word [edi], dx ; write to video display ;-------------------------------; ; Update next position ; ;-------------------------------; inc byte [_CurX] ; go to next character cmp [_CurX], COLS ; are we at the end of the line? je .Row ; yep-go to next row jmp .done ; nope, bail out
さて、どうでしょう?かなり簡単でしょう?そうそう、次の行に移動するのは簡単です。
;-------------------------------; ; Go to next row ; ;-------------------------------; .Row: mov byte [_CurX], 0 ; go back to col 0 inc byte [_CurY] ; go to next row ;-------------------------------; ; Restore registers & return ; ;-------------------------------; .done: popa ; restore registers and return ret

文字列の操作

よし、これで文字を印刷できるぞ。イパーイ。一文字でもドキドキしちゃいますね。ええ、そんなことはありませんよ :)

実際の情報を表示するには、完全な文字列を表示する方法が必要です。現在位置を追跡し(そして更新し)、文字を表示するルーチンはすでにあるので、文字列を表示するために必要なのは単純なループだけです。

Puts32: ;-------------------------------; ; Store registers ; ;-------------------------------; pusha ; save registers push ebx ; copy the string address pop edi
では、Puts32()関数です。この関数は1つのパラメータを受け取ります。EBXはヌル文字で終端された文字列のアドレスで、これをプリントします。Putch32()関数では、印字する文字をBLに保存する必要があるので、EBXのコピーを保存する必要があり、ここでそれを行います。

では、ループさせます。

.loop: ;-------------------------------; ; Get character ; ;-------------------------------; mov bl, byte [edi] ; get next character cmp bl, 0 ; is it 0 (Null terminator)? je .done ; yep-bail out
EDIを使用して文字列を参照し、表示する現在の文字を取得します。ヌルターミネーターのテストに注意してください。もし見つかれば、ベイルアウトします。さて、その文字を表示するには...最も複雑なコードです。
;-------------------------------; ; Print the character ; ;-------------------------------; call Putch32 ; Nope-print it out
...いや、そうでもないか:)

あとは、次の文字に移動して、ループするだけです。

;-------------------------------; ; Go to next character ; ;-------------------------------; .Next: inc edi ; go to next character jmp .loop .done: ;-------------------------------; ; Update hardware cursor ; ;-------------------------------; ; Its more efficiant to update the cursor after displaying ; the complete string because direct VGA is slow mov bh, byte [_CurY] ; get current position mov bl, byte [_CurX] call MovCur ; update cursor popa ; restore registers, and return ret

ほら!32ビットプロテクトモードで文字列をプリントする方法を手に入れました。難しいことじゃないでしょう?MovCurは何のためにあるのでしょうか?それは次に見てみましょう。

ハードウェアカーソルの更新

さて、これで文字と文字列をプリントアウトできるようになりました。しかし、カーソルが動かないことにお気づきでしょうか?カーソルが動かないのです!このため、何をやってもそのままになってしまいます。このカーソルは、BIOSがテキストを印刷するときに現在の位置を示すために使用する単純な下線です。

このカーソルは、ハードウェアによって処理されます。実際、CRTマイクロコントローラです。そこで、このカーソルを動かすために、基本的なvgaプログラミングを知る必要があります。

CRTマイコン

CRTをお使いの方へのご注意

このように、OSの環境では、ハードウェアを直接操作し、すべてをコントロールすることができることを忘れないでください。

CRTモニターの故障は性質上激しく、爆発して鋭いガラス片が高速で飛び散ることがあります。また、周波数設定を変更すると、デバイスやマイクロチップが誤動作し、予期せぬ災害を引き起こす可能性があります。

このため、もし読者の皆様がこのコードで実験されるなら、実際のハードウェアに挑戦する前に、まずエミュレータですべての実験コードを最大限にテストされることをお勧めします。

ビデオドライバーの話をするまでは、ビデオプログラミングに関するすべての説明はしません。 その後に詳しく見ていきましょう。

というわけで、CRTコントローラについて説明します。

ポートマッピング

CRTコントローラは、ポート0x3D5にマッピングされた1つのデータレジスタを使用します。チュートリアル7のポート表を覚えていますか? CRTコントローラは、データレジスタのデータの種類を決定するために、インデックスレジスタという特別なレジスタを使用します。

CRTコントローラにデータを渡すには、 2つの値を書き込まなければなりません。 一つはインデックスレジスタ(書き込むデータの種類を格納)、もう一つはデータレジスタ(書き込むデータを格納)です。それほど難しいことではありません。

インデックスレジスタは、ポート0x3D5または0x3B5にマッピングされます。
データレジスタはポート0x3D4または0x3B4にマップされます。

この2つ以外にもレジスタはありますが(Misc.Output Registerなど)、ここではこの2つに焦点を当てます。

インデックスレジスタのマッピング

デフォルトでは、インデックス・レジスタのインデックスは次のようにマップされています。

CRT Microcontroller - Index Register
Index Offset CRT Controller Register
0x0 Horizontal Total
0x1 Horizontal Display Enable End
0x2 Start Horizontal Blanking
0x3 End Horizontal Blanking
0x4 Start Horizontal Retrace Pulse
0x5 End Horizontal Retrace
0x6 Vertical Total
0x7 Overflow
0x8 Preset Row Scan
0x9 Maximum Scan Line
0xA Cursor Start
0xB Cursor End
0xC Start Address High
0xD Start Address Low
0xE Cursor Location High
0xF Cursor Location Low
0x10 Vertical Retrace Start
0x11 Vertical Retrace End
0x12 Vertical Display Enable End
0x13 Offset
0x14 Underline Location
0x15 Start Vertical Blanking
0x16 End Vertical Blanking
0x17 CRT Mode Control
0x18 Line Compare

インデックス・オフセット値を書き込むことにより、データ・レジスタがどのレジスタを指しているか(つまり、何を参照しているか)を示すことができます。

上表のほとんどは、今、気にする必要はないでしょう。しかし、インデックス0xEと0xFについては、ちょっと見てください。

  • 0x0E:カーソルの位置の上位バイト
  • 0x0F:カーソル位置の下位バイト
Yippe!これらのインデックスは、ハードウェアカーソルの現在のオフセット位置を参照しています。このオフセットは、x/y 位置(線形位置として -x + y * スクリーン幅の式を思い出してください)であり、その上位バイトと下位バイトに分割されています。

ハードウェアカーソルの移動

まず、カーソルのインデックスは0x0Eと0x0Fで、これをポート0x3D4のインデックスレジスタに入れる必要があることを思い出してください。
mov al, 0x0f mov dx, 0x03D4 out dx, al
これはインデックス0x0F(カーソルの下位バイトのアドレス)をインデックスレジスタに入れることです。これで、データレジスタ(ポート0x3d5)に入れられた値は、カーソル位置のローバイトを示すことになります。
mov al, bl ; al contains the low byte address mov dx, 0x03D5 out dx, al ; low byte
これはカーソルの新しい下位バイトの位置を設定します!クールでしょう?上位バイトの設定も全く同じです。ただし、インデックスを0x0Eに設定しなければなりません。

以下は、このルーチンの完全版です。

;**************************************************; ; MoveCur () ; - Update hardware cursor ; parm/ bh = Y pos ; parm/ bl = x pos ;**************************************************; bits 32 MovCur: pusha ; save registers (aren't you getting tired of this comment?) ;-------------------------------; ; Get current position ; ;-------------------------------; ; Here, _CurX and _CurY are relitave to the current position on screen, not in memory. ; That is, we don't need to worry about the byte alignment we do when displaying characters, ; so just follow the forumla: location = _CurX + _CurY * COLS xor eax, eax mov ecx, COLS mov al, bh ; get y pos mul ecx ; multiply y*COLS add al, bl ; Now add x mov ebx, eax ;--------------------------------------; ; Set low byte index to VGA register ; ;--------------------------------------; mov al, 0x0f ; Cursor location low byte index mov dx, 0x03D4 ; Write it to the CRT index register out dx, al mov al, bl ; The current location is in EBX. BL contains the low byte, BH high byte mov dx, 0x03D5 ; Write it to the data register out dx, al ; low byte ;---------------------------------------; ; Set high byte index to VGA register ; ;---------------------------------------; xor eax, eax mov al, 0x0e ; Cursor location high byte index mov dx, 0x03D4 ; Write to the CRT index register out dx, al mov al, bh ; the current location is in EBX. BL contains low byte, BH high byte mov dx, 0x03D5 ; Write it to the data register out dx, al ; high byte popa ret
簡単でしょう?

次は画面を消去する

画面の消去

テキストを表示する方法はすでにあるので、ループして、現在位置を0にリセットするだけです!これは驚くほど簡単です....
;**************************************************; ; ClrScr32 () ; - Clears screen ;**************************************************; bits 32 ClrScr32: pusha cld mov edi, VIDMEM mov cx, 2000 mov ah, CHAR_ATTRIB mov al, ' ' rep stosw mov byte [_CurX], 0 mov byte [_CurY], 0 popa ret
簡単でしょう?

さて、テキストを印刷し、ハードウェアカーソルを更新し、画面をクリアする方法があります。もし望むなら、このステージ2ローダーを拡張して、カーネルに制御を与えるときに小さなメニューと高度なオプションを含めることができます。これについては後で詳しく説明します。

まとめ

次のチュートリアルをどのように進めるか、かなり迷いました。私は、2つのパートに分けることが良い解決策だと信じています(希望!)。

私たちはここで多くのこと、より具体的にはグラフィックの概念について説明しました。基本的なVGAの概念、文字、文字列の印刷、画面の消去、ハードウェアカーソルの更新について説明しました。 印刷するテキストの属性バイトを変更することで、簡単にいろいろな色で文字を印刷することができました!また、印刷された文字列の背景色を変更することもできました。属性バイトの色を変えて、ClrScr32()関数を呼び出せば、新しい背景を得ることもできます!クールだと思いませんか?確かに、白黒はつまらないですね... :)

次のチュートリアルではStage 2を終了し、1MBの基本的なピュアバイナリ32ビットカーネルイメージをロードして実行します。 心配しないでください--このシリーズのカーネルセクションに入ると、カーネルの構築方法を変更し、ロード方法を変更することになります。これにより、カーネルをオブジェクトフォーマットとしてロードし、シンボルのインポートやエクスポートを可能にし、C言語と混在させることができるようになるのです。

次のチュートリアルは、新しいことを学ぶという意味でのチュートリアルではありません。そのかわり、すでに説明されているコードをすべてカバーします。しかし、このコードは、より良いコードレイアウトと、基本的なファイルシステム(FAT12)ドライバとフロッピードライバの間のインタフェース(と分離)を提供するために修正されています。とはいえ、これでStage 2のチュートリアルは終了です。

Stage 2 は、より多くのオプションを提供したり、マルチブートや ブートオプションをサポートするように変更することができますので、後で Stage 2 に戻ります。その時はお楽しみに...。)