Skip to content

Graphics

Masakazu Yonehara edited this page Aug 24, 2026 · 8 revisions

グラフィック画面

テキスト画面とは別に、640x400 ドットのグラフィック画面があります。 プレーンごとに分かれた VRAM へ直接書き込んで描きます。

基本

グラフィック VRAM は 色の成分ごとに別のアドレスに分かれています(プレーン方式)。

アドレス プレーン
0xA8000 青
0xB0000 赤
0xB8000 緑
0xE0000 輝度(16色モードのときだけ有効)
  • 1バイトが横8ドット、bit7 が左端です
  • 1行は80バイト(80 × 8 = 640ドット)
  • 1プレーンは32000バイト(80 × 400)

ある点のアドレスとビットはこう求まります。

オフセット = y * 80 + x / 8
ビット     = 0x80 >> (x & 7)

同じ位置のビットを複数のプレーンで立てると、その組み合わせが色になります。 3プレーンすべてを立てれば白、青プレーンだけなら青、という具合です。

前提条件

  • グラフィック表示は既定で消えています。 VRAM に書いても、表示を開始するまで何も見えません。
  • 既定は3プレーン(8色)です。 0xE0000(輝度)は 16色モードにしないと効きません。 16色モードにせずに 0xE0000 へ書いても、エラーは出ず、ただ何も起こりません。

表示の開始と終了

グラフィック用の GDC にコマンドを送ります。

        mov     al, 0x0D
        out     0xA2, al        ; グラフィック表示を開始
        ...
        mov     al, 0x0C
        out     0xA2, al        ; グラフィック表示を終了

16色を使うときは、あわせてモードを切り替えます。

        mov     al, 0x01
        out     0x6A, al        ; 16色モード

点を打つ

青プレーンに1点打つ例です。

; AX=x, BX=y
plot:
        push    ax
        push    bx
        push    cx
        push    dx
        push    di

        mov     cx, ax
        and     cx, 7
        mov     dl, 0x80
        shr     dl, cl                  ; ビットマスク
        push    dx                      ; MUL は DX を壊すので退避しておく

        mov     cl, 3
        shr     ax, cl                  ; x / 8
        mov     di, ax

        mov     ax, bx
        mov     cx, 80
        mul     cx                      ; DX:AX = y * 80
        add     di, ax

        pop     dx                      ; ビットマスクを戻す
        mov     ax, 0xA800              ; 青プレーン
        mov     es, ax
        or      [es:di], dl

        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

MUL は DX を壊します。 ビットマスクを DL に置いたまま y * 80 を計算すると、 マスクが 0 になって 何も描かれません。アセンブルは通り、コードも一見正しく見えるので 気づきにくい失敗です。

画面を消すときは、各プレーンを 32000 バイトぶん 0 で埋めます。

パレット

16色モードでは、色番号ごとの実際の色をパレットで決められます。

ポート 意味
0xA8 これから設定する色番号
0xAA 緑の強さ(0〜15)
0xAC 赤の強さ(0〜15)
0xAE 青の強さ(0〜15)

色番号は、その点のプレーンの組み合わせです(青=1, 赤=2, 緑=4, 輝度=8 の合計)。

        mov     al, 1
        out     0xA8, al        ; 色番号1を設定する
        mov     al, 0x0F
        out     0xAC, al        ; 赤を最大に
        xor     al, al
        out     0xAA, al
        out     0xAE, al        ; 緑と青は0 → 色番号1が赤になる

強さは 0〜15 で、画面に出る明るさはこの値にほぼ比例します (実測では 4 で約 68、8 で約 137、15 で 255)。

GRCG(グラフィックチャージャー)で速く塗る

プレーンごとに4回書き込む代わりに、1回の書き込みで4プレーンすべてに書ける ハードウェアの支援機能があります。塗りつぶしや画面消去が4倍速くなります。

ポート 意味
0x7C モード。bit7 を立てると有効
0x7E タイルレジスタ。4回続けて書くと 青→赤→緑→輝度 の順に入る
        mov     al, 0x80
        out     0x7C, al        ; GRCG を有効にする

        mov     al, 0xFF        ; 青プレーンに書く値
        out     0x7E, al
        mov     al, 0xFF        ; 赤
        out     0x7E, al
        xor     al, al          ; 緑
        out     0x7E, al
        out     0x7E, al        ; 輝度

        ; ここで VRAM へ1バイト書くと、上のタイルどおりに4プレーンへ同時に書かれる
        mov     ax, 0xA800
        mov     es, ax
        xor     di, di
        mov     al, 0xFF
        mov     cx, 32000
        rep     stosb           ; 画面全体が「青+赤」で塗られる

        xor     al, al
        out     0x7C, al        ; 使い終わったら必ず無効に戻す

書き込み先はどのプレーンのアドレスでもかまいません。 有効にしている間は、 アドレスではなくタイルレジスタが行き先を決めます。

使い終わったら必ず無効に戻してください。 有効のままにすると、 以降の普通の VRAM 書き込みがすべてタイルどおりに化けます。

EGC で4プレーンまとめて書く

EGC は GRCG よりさらに高機能な描画支援機能です。ここでは、確認できた範囲として 1回の書き込みで4プレーンすべてに書く手順を載せます。

有効にする

順番が決まっています。0x7C の bit7 を立てるのが先です。

        mov     al, 0x80
        out     0x7C, al        ; GRCG のモードレジスタ bit7。これが無いと EGC は動かない
        mov     al, 0x07
        out     0x6A, al        ; 拡張モードの変更を許可する
        mov     al, 0x05
        out     0x6A, al        ; EGC 拡張モードにする

0x7C は GRCG のモードレジスタと同じものですが、EGC を使うときも立てる必要があります。 これを忘れると、EGC のレジスタをいくら設定しても普通の書き込みのまま (1プレーンだけ書かれる)になります。

レジスタを設定する

ポート 設定する値 意味
0x4A0 0xFF00 4プレーンすべてに書く
0x4A4 0x0000 CPU が書いた値をそのまま使う
0x4A8 0xFFFF マスクなし
0x4AC 0x0000 シフトなし
0x4AE 0x0007 ビット長

いずれも16ビットのレジスタですが、下位・上位を1バイトずつ書いてもかまいません。

        mov     dx, 0x04A0
        mov     ax, 0xFF00
        out     dx, al          ; 下位
        inc     dx
        mov     al, ah
        out     dx, al          ; 上位

書く

設定したあとは、どれか1つのプレーンのアドレスへ書くだけです。

        mov     ax, 0xA800
        mov     es, ax
        xor     di, di
        mov     al, 0xFF
        mov     cx, 32000
        rep     stosb           ; 4プレーンすべてが 0xFF になる = 画面全体が白

実測では、0xA8000 へ 1 バイト書いた結果が 4 プレーンとも 0xFF になりました。

使い終わったら 0x7C を 0x00 に戻してください。 戻さないと以降の 普通の VRAM 書き込みが EGC を通ったままになります。

確認できていないこと

  • 前景色レジスタ(0x4A6)で色を指定する方法。 色を変えても結果は 「CPU が書いた値がそのまま全プレーンへ」で変わりませんでした。 今回試した組み合わせでは前景色が反映されていません。
  • 読み書き両方を伴うモード(0x4A4 = 0x0CAC)。 この設定では何も書かれませんでした。 読み出しを伴う手順が必要と思われますが、確認していません。
  • シフト、ビット単位のマスク、ラスタオペレーション全般。

画面モードを切り替える

解像度と表示ページは、INT 18h の AH = 0x42 で切り替えられます。

モード指定コードは CH で渡します。 AL ではありません。

CH 結果
0xC0 400ライン・表画面(通常)
0x80 200ライン。VRAM の前半を使う
0x40 200ライン。VRAM の後半を使う
0xD0 0xC0 と同じで、裏画面を表示する
        mov     ax, 0x4200
        mov     ch, 0xC0        ; 400ライン・表画面
        int     0x18

200ラインモードでは、画面には1行おきに表示されます (VRAM の 200 行が画面全体の高さに広がります)。

CH に 0 を入れて呼ばないでください。 画面が消え、 グラフィック表示を入れ直しても VRAM を塗り直しても戻りません (正しい CH をもう一度渡せば戻ります)。

色数(8色 / 16色)の切り替えは、このページの上のほう(OUT 0x6A, 0x01)を参照してください。

表示ページと描画ページ

グラフィック画面は2ページあり、「いま画面に出ているページ」と 「CPU が書き込むページ」を別々に指定できます。 見えていないページに描いてから表示を切り替えれば、描いている途中が見えません。

ポート 意味
0xA4 表示ページ(画面に出るページ)
0xA6 描画ページ(0xA8000 などへ書いたときに書かれるページ)

どちらもページ番号(0 または 1)を書きます。

        mov     al, 1
        out     0xA6, al        ; これから書くのはページ1
        ; ... ここで描く(画面には出ない)...

        mov     al, 1
        out     0xA4, al        ; 描き終わったら表示を切り替える

実測では、ページ0に画面上部、ページ1に画面下部を描いたところ、 表示ページを切り替えるたびにそれぞれの内容だけが出ました。 2つのページは互いに独立して内容を保持しています。

よくある間違い

  • VRAM に書いたのに何も出ない。 グラフィック表示は既定で消えています。 OUT 0xA2, 0x0D を忘れていないか確認してください。
  • 輝度プレーン(0xE0000)に書いても何も起きない。 16色モード (OUT 0x6A, 0x01)にしていないと効きません。エラーは出ません。
  • MUL でビットマスクを壊す。 DX が上書きされ、何も描かれなくなります。
  • GRCG を有効にしたまま放置する。 その後の普通の書き込みが全部タイルどおりに化けます。
  • 1行を64バイトや100バイトで計算する。 1行は 80バイトです。
  • AH=0x42 のモード指定を AL に入れる。 CH で渡します。CH が 0 のまま 呼ぶと画面が消え、表示を入れ直しても戻りません。

実機との差異

現時点で確認できているものはありません。 ただし このページの測定はすべて np2kai 上で行っています。 実機での確認は行っていません。

未検証

  • EGC の前景色指定、読み書きを伴うモード、シフト、ビット単位のマスク
  • GRCG の 0x7C の bit7 以外のビット(動作モードの違い)
  • CH の 0xE0(モノクロ指定)でグラフィックが出なくなる理由
  • 8色モードでも表示ページ/描画ページが使えるか
  • 400ライン以外の解像度、および画面モードの切り替え全般
  • パレットの読み出し、8色モードでのパレット設定
  • テキスト画面との重ね合わせの優先順位

検証: 2026-08-24 / WebNP2 327d061 (np2kai) + FreeDOS(98) / 再現: guest/probes/GIO2.ASM(測定用のブリッジ), guest/probes/WIKIEX5.ASM(上の点を打つ例)

使用例は実行して、対角線上の (0,0) (50,50) (100,100) (150,150) (199,199) が点灯し、 線から外れた位置が消灯していることを確認しています。

Clone this wiki locally