このエントリーをはてなブックマークに追加
スポンサーリンク

グラフィックV-RAM

グラフィックV-RAMとは

グラフィックV-RAMは通常、J-3100やPC/AT互換機でいうところのV-RAMのことです。文字専用ではないため、自由にドット単位で図形や絵などが表示可能です。

ただし、PC-9801ではテキストV-RAMが存在する関係でグラフィックV-RAMは多くの機種で低速です。テキストの表示はできる限りテキストV-RAMを使い、グラフィックV-RAMは罫線程度のものを表示するぐらいに留めておいた方が良いです。

デジタルモードとアナログモード

PC-9801は当初ビジネスマシンとして発売されたものです。なので、初期の機種ではデジタル8色表示でした。その後、PC-9801VMが発売されると、4096色中16表示が可能になり、以降PC-9801では4096色中16色表示が標準となりました。
PC-9801のBIOSではデジタル8色モードに関する機能は用意されていますが、アナログモードに関する機能が用意されていません。そのため、アナログ16色モードを使うためには、I/Oポートの操作が必要となります。

パソコンでプログラムを作る時、特にMSXやPC/AT互換機のような異なるメーカーで互換性を保つような機種では、極力ROM内ルーチンを直接コールしたりI/Oポートを直接操作したりしないのが普通ですが、NECのPCシリーズ・・・とりわけPC-9801シリーズでは直接I/Oポートを操作しなければならない場面が普通に出てきます。

PC/AT互換機とPC-9801では歴史的な経緯の違いにより互換性を保つ手段が異なります。PC/AT互換機は異なるコントローラーでBIOS上で互換性を保つように作られていたため、BIOSが充実していてI/Oポートを直接操作する必要はめったにありませんが、PC-9801はコントローラーコマンドといったハード上での互換性を保つように作られたため、BIOSはさほど充実してないようです。

V-RAM

V-RAMの構造は、PC/AT互換機と同じようにプレーン0からプレーン3まであります。ただし、PC/AT互換機とは異なり同じ各プレーンが同じアドレスにマッピングされているわけではなく、それぞれ異なるアドレスにマッピングされています。また、歴史的な経緯により16色モードは建て増しで作られたため、プレーン3はアドレスが飛んでいます。


プレーン0〜2はそれぞれBRGの発色を担当しており、プレーン3はPC/AT互換機と同じく明るい色にするかどうかを区別します。

また、V-RAMの同じアドレスに「表バンク」「裏バンク」がマッピングされており、表と裏を切り替える事でアニメーションを作ったり(裏バンクに画像を転送しておいてバンク切り替えによって高速に画面を切り替える)、裏バンクをワークエリアに使ったりする事ができます。

グラフィックV-RAM使用開始

初期状態ではグラフィックV-RAMは使わない設定になっているので、グラフィックV-RAMに書き込みをしても何も表示されません。そこで、グラフィックV-RAMを使う前にコントローラーに使用開始を命令します。I/Oポートを操作してもできますが、互換性を考えてできる限りBIOSでできるものはBIOSで行います。

AH=40H グラフィックV-RAMの使用開始
MOV	AH,40H
INT	18H
AH=41H グラフィックV-RAMの使用停止
MOV	AH,41H
INT	18H
AH=42H 400ラインモードか200ラインモードかを指定します。
CHレジスターでモードを指定します。
ビット 意味
7〜6 ビット7・6がそれぞれ、
 01 200ライン(アッパー)
 10 200ライン(ローワー)
 11 400ライン
5 0 カラー
1 モノクロ
4 0 表バンク使用
1 裏バンク使用
3〜0 未使用

200ラインアッパーにすると、V-RAMはオフセット0000Hから、200ラインローワーにすると、V-RAMはオフセット3E80Hから使われます。
MOV	AH,42H
MOV	CH,192
INT	18H
この例では、400ライン・カラー・バンク0を指定しています。

デジタルパレット

デジタル8色パレットを変更します。

AH=43H
CS:BX パレットのデーター 4ビットずつ指定します。
アドレス 意味
+0 ビット0〜3 カラーコード7のカラー
ビット4〜7 カラーコード6のカラー
+1 ビット0〜3 カラーコード5のカラー
ビット4〜7 カラーコード4のカラー
+2 ビット0〜3 カラーコード3のカラー
ビット4〜7 カラーコード2のカラー
+3 ビット0〜3 カラーコード1のカラー
ビット4〜7 カラーコード0のカラー

使い道とすれば、SA・ZI・RIやリバイバーで夜になった効果(全体的に青っぽくなる)ぐらいでしょうか。あと、アクションゲームで背景を1つのプレーン、キャラクターで他の2プレーンに使い不自然にならない配色にするとか。

ドラゴンスレイヤー英雄伝説みたいに画面がフェードアウトするような効果はアナログパレットを使わないと無理ですね。

アナログパレット

アナログモードに関しては、後から追加したためかBIOSにアナログパレットを操作する機能がありません。仕方がないのでI/Oポートを操作します。というかINT 18Hを拡張してアナログパレットを操作できるようにしてくれてもバチは当たらないような気もしますが。このように、PC-9801ではI/Oポートを操作しなければならない場面が頻繁に出てきます。

デジタル8色モードにする
MOV    AL,0
OUT    06AH,AL
アナログ4096色中16色モードにする
MOV    AL,1
OUT    06AH,AL
アナログパレットはデフォルトでこのようになっています。PC/AT互換機のような、暗い黄色のところに茶色に当てられたりはしていません。
プレーン デフォルトの明るさ
(BRG)
デフォルトの
パレット番号
3 2 1 0
0 0 0 0 000H 00H
0 0 0 1 暗い青 007H 01H
0 0 1 0 暗い赤 070H 02H
0 0 1 1 暗いマゼンダ 077H 03H
0 1 0 0 暗い緑 700H 04H
0 1 0 1 暗いシアン 707H 05H
0 1 1 0 暗い黄色 770H 06H
0 1 1 1 暗い白 777H 07H
1 0 0 0 明るい黒 444H 08H
1 0 0 1 明るい青 00FH 09H
1 0 1 0 明るい赤 0F0H 0AH
1 0 1 1 明るいマゼンダ 0FFH 0BH
1 1 0 0 明るい緑 F00H 0CH
1 1 0 1 明るいシアン F0FH 0DH
1 1 1 0 明るい黄色 FF0H 0EH
1 1 1 1 明るい白 FFFH 0FH

変更するためには、以下のI/OポートにWriteします。
アドレス 意味
A8H ビット0〜3 パレット番号
ビット4〜7 未使用
AAH ビット0〜3 緑の明るさ
ビット4〜7 未使用
ACH ビット0〜3 赤の明るさ
ビット4〜7 未使用
AEH ビット0〜3 青の明るさ
ビット4〜7 未使用
まず、A8Hに設定したいパレット番号をWriteした後に、AAH、ACH、AEHにBRGそれぞれの明るさを4ビットでwriteします。パレット番号は0〜15まで、BRGそれぞれの明るさも0〜15までです。したがって、16×16×16=4096色中の16色が同時に発色できます。

V-RAMの開始アドレス

V-RAMの横幅はPC/AT互換機のVGAと同じく640ドット80バイトですので、左上を(0,0)右下を(639,399)とすると、座標(X,Y)のアドレスは、(Y*80)+(X/8)となり、
;==============================================================================
;               開始アドレスセット
;               In:GX1 GY1    Out:SADDR
;==============================================================================
CSET:           MOV     AX,[GX1]
                SHR     AX,1
                SHR     AX,1
                SHR     AX,1
                MOV     [K1],AX

                MOV     AX,[GY1]
                MOV     BX,80
                MUL     BX
                MOV     [K2],AX

                MOV     AX,[K1]
                ADD     AX,[K2]
                MOV     [SADDR],AX
                RET
というようなサブルーチンを作っておくと便利です。「処理に変数が多すぎて実用的ではありません((C)虎鉄)」って言われそうですけど、そこはまあご愛嬌で。

GRCGを使ったプレーンの操作

V-RAMはプレーン0〜3まであり、それぞれのプレーンに同じ物を書いていたのでは処理の低速化を招きます。そこで、GRCGを使って書き込むプレーンを指定しておき、あとは代表的にプレーン0にだけ書き込めば処理は高速化されます。

まずモードレジスタ7CHでGRCGの使用、使用するプレーンを選択します。PC/AT互換機でいうところの「マップマスクレジスタ」「リードマップセレクトレジスタ」のようなものですね。

モードレジスタ7CH
ビット 意味
7 0 GRCG無効
1 GRCG有効
6 0 TDW/TCRモード
1 RMWモード
5〜4 未使用
3 プーン3へのアクセス指定(0で有効)
2 プーン2へのアクセス指定(0で有効)
1 プーン1へのアクセス指定(0で有効)
0 プーン0へのアクセス指定(0で有効)

ポート7CHのビット7に1を書き込みGRCGを有効にすると、ポート7EHはタイルレジスタとして動作します。1回書き込むたびにプレーン0、プレーン1、プレーン2、プレーン3に対するタイルパターンの設定になります。PC/AT互換機でいうところの「ビットマスクレジスタ」のようなものですね。タイルレジスタは必ずプレーン0〜3までセットで指定しなければなりません。

タイルレジスタ 7EH
書き込み回数 意味
1回目 プレーン0へのタイルパターンの設定
2回目 プレーン1へのタイルパターンの設定
3回目 プレーン2へのタイルパターンの設定
4回目 プレーン3へのタイルパターンの設定
参考文献(PC-9801スーパーテクニック)では、タイルレジスタとして7CHに書き込んでましたけど、多分ミスプリじゃないかと思います。どうでもいいけど、この本けっこうミスプリが多いです。(もっとも、このサイトも人の事いえないぐらい多いですが)

例) [IRO]にカラーコード、タイルパターンは全プレーン255(全ビットオン)として、
;-------------- GRCG有効、RMWモード、全プレーン有効
		MOV	AL,192
		OUT	7CH,AL

;-------------- タイルレジスタ設定
;----------- (1)
		MOV	AL,[IRO]
		AND	AL,1
		CMP	AL,0
		JE	TAIL_SET1
		MOV	AL,255
		OUT	7EH,AL
		JMP	TAIL_SET2
TAIL_SET1	=	$
		MOV	AL,0
		OUT	7EH,AL

;----------- (2)
TAIL_SET2	=	$
		MOV	AL,[IRO]
		AND	AL,4
		CMP	AL,0
		JE	TAIL_SET3
		MOV	AL,255
		OUT	7EH,AL
		JMP	TAIL_SET4
TAIL_SET3	=	$
		MOV	AL,0
		OUT	7EH,AL

;----------- (3)
TAIL_SET4	=	$
		MOV	AL,[IRO]
		AND	AL,2
		CMP	AL,0
		JE	TAIL_SET5
		MOV	AL,255
		OUT	7EH,AL
		JMP	TAIL_SET6
TAIL_SET5	=	$
		MOV	AL,0
		OUT	7EH,AL

;----------- (4)
TAIL_SET6	=	$
		MOV	AL,[IRO]
		AND	AL,8
		CMP	AL,0
		JE	TAIL_SET7
		MOV	AL,255
		OUT	7EH,AL
		JMP	TAIL_SET8
TAIL_SET7	=	$
		MOV	AL,0
		OUT	7EH,AL
TAIL_SET8	=	$
TDWモードで書き込むと、V-RAMに何を書いてもタイルレジスタの値が書き込まれます。特定のビットパターンで書き込みたい時に便利です。また、RMWモードで書き込むと、V-RAMに書いたデーターとタイルレジスタとANDを取った値が書き込まれます。特定の箇所を消したい時に便利です。

TCRモードで読み込むと、各タイルレジスター0〜3とプレーン0〜3、それぞれとANDを取った値が読み出されます。特定のプレーンの特定の場所にドットがあるかどうか調べたい時は、各プレーンに対応するタイルレジスタの調べたい場所のビットを立てておいて、読み込んで0かどうかで判別します。また、特定のプレーン全体を調べたい時は、調べたいプレーンのタイルレジスターを0FFHにしておき、他のプレーンのタイルレジスタを00Hにしておきます。

残念なのは、GRCGにはVGA搭載のPC/AT互換機のようなデーターローテートレジスタがない事です。ビット演算が必要な時はEGCを使う必要があります。

EGCを使ったプレーンの操作

EGCを使う事でVGA搭載のPC/AT互換機のような高速で複雑なV-RAMの操作ができます。しかし、EGCを使うと対応機種が「VX/UX以降」になります。とはいえ、PC-9801が本格的にゲーム機として使われだしたのはVX/UXが発売されて以降なので、ゲームソフトを作る上では特に障害にはならなかったようです。PC-9801のアクションゲームに「VX/UX以降」が多いのはこのためです。

EGC搭載機種であってもGRCGを使う事もできます。これは、PC-9801の歴史的な経緯により、上位互換を保ちつつコントローラーを後付けで建て増しを続けたためです。

ポート6AH EGCを有効にする
EGCを有効にするためには、(1)モード変更可(2)拡張モード(3)モード変更不可と3段階の操作が必要です。これは通常はモード変更不可の状態にしておく必要があるためです。
意味
7 モード変更可
6 モード変更不可
5 拡張モード
4 GRCG互換モード

例)拡張モードオンにする
;---------- 拡張モードON
		MOV	AL,7
		OUT	6AH,AL
		MOV	AL,5
		OUT	6AH,AL
		MOV	AL,6
		OUT	6AH,AL
ポート04A0H 書き込みプレーンの指定
どのプレーンに対して書き込むかを指定します。GRCGとは異なり16ビット長で、ワード単位で書き込みます。
ビット 意味
15〜4 常に1
3 プーン3へのアクセス指定(0で有効)
2 プーン2へのアクセス指定(0で有効)
1 プーン1へのアクセス指定(0で有効)
0 プーン0へのアクセス指定(0で有効)
;--------- 全プレーン動作
		MOV	DX,04A0H
		MOV	AX,0FFF0H
		OUT	DX,AX
ポート 04A2H 読み込むプレーンとフォアグランドカラー、バックグランドカラーの指定
読み込むプレーン、フォアグランドカラー、バックグランドカラーの有効/無効を指定します。
ビット 意味
15 常に0
14 フォアグランドカラーを有行為にする(1で有効)
13 バックグランドカラーを有行為にする(1で有効)
12 常に0
11 プレーン3に対する読み込み指定(0で有効)
10 プレーン2に対する読み込み指定(0で有効)
9 プレーン1に対する読み込み指定(0で有効)
8 プレーン0に対する読み込み指定(0で有効)
7〜0 常に1

ポート04A4H ビット演算指定
GRCGではできなかったビット演算の指定ができます。
ビット 意味
15 常に0
14 常に0
13 コンペアリード 0する 1しない
12〜11 ビット12・11が、
  00: CPUデーター
  01: ラスタオペレーションの結果
  10: パターンレジスタの値
10 読み込んだ場合に返す値
 0 V-RAMデーター
 1 CPUデーター
9 0 パターンレジスタ変更なし
1 書き込み時に前のV-RAMデーターをパターンレジスタにセットしてから書き込む
8 0 パターンレジスタ変更なし
1 読み込む時にV-RAMのデーターがパターンレジスタにセットされる
7〜0 ビット演算指定

ここでビット7〜0の該当するビットを立てておくと、V-RAMに書き込む際に、有効になっている各プレーンに対して、CPUデーター、V-RAMデーター、パターンレジスタのそれぞれとANDを取った値が書き込まれます。複数のビットを立てておくと、それぞれの値とORを取った値が書き込まれます。
ビット パターンレジスタ CPUデーター V-RAMデーター
7
6 NOT
5 NOT
4 NOT NOT
3 NOT
2 NOT NOT
1 NOT NOT
0 NOT NOT NOT

使い道ですが・・・よくわかりません。参考文献(PC-9801スーパーテクニック)でも肝心の説明の部分にミスプリがあったりしてよくわかりませんでした。市販の文献にもミスプリはあるもんだなーと。

で、文献によると、
RMWモード: V-RAMデーターとタイルレジスタのANDを取った値→ 2CACH
上書きモード:V-RAMデーターに関係なくCPUデーターとタイルレジスタのANDの値→2CA0H
らしいので、仕方がないのでこの2つだけを使ってました。根拠となる計算式は難しくて私にはわかりませんでした。

どうもPC-9801のプログラムは一見さんお断りみたいな所があって難しいです。複雑な演算が可能な反面、設定が難しいです。PC/AT互換機のデーターローテートレジスタみたいにシンプルにできなかったものかと。

ポート04A6H フォアグランドカラーの指定
GRCGを使う場合、カラーコードを判断して各プレーンに対してタイルレジスターを00HやFFHを設定しました。EGCではフォアグランドカラー/バックグランドカラーを有効にすると、タイルレジスターを使わずにカラーコードを指定する事で同様の処理ができます。
ビット 意味
7〜4 常に0
3〜0 フォアグランドカラー 0〜15

ポート04AAH バックグランドカラーの指定
ビット 意味
7〜4 常に0
3〜0 バックグランドカラー 0〜15
該当するビットが1のドットがフォアグランドカラー、0のドットがバックグランドカラーで描画されます。

ポート04A8H マスクレジスタの指定
16ビット幅あり、該当ビットを0にしておくとV-RAMへのライトがマスクされます。全てのプレーンに対してマスクされます。

ブロック転送
カラーモード時にV-RAMのプレーン0〜3をまとめて別の場所に移動させたい時がよくあります。この場合、プレーン0〜3をそれぞれ4回ブロック転送すれば良いわけですが、それだとスクロールさせた時にRBGが別々にスクロールする事になり美しくないです。そこで、全てのプレーンを同時に動かす事ができます。VGA搭載のPC/AT互換機のグラフィックコントローラーにある「書き込みモード1」のようなものです。

4ACH ビットアドレスの指定
ビット 意味
15〜13 常に0
12 0 アドレスの低い方から高い方へ転送
1 アドレスの高い方から低い方へ
7〜4 ソースビットアドレス
3〜0 ディスティネーションビットアドレス
キャラクター単位でスクロールさせるのではなく、ドット単位で横スクロールさせたい時は、ビット0〜7で先頭のビットアドレスを指定します。

4AEH ビット長の指定
ビット 意味
15〜12 常に0
11〜0 転送ビット長-1
ブロック転送する時のビット長を指定します。0〜4095を指定します。結果、1〜4096ビットまで同時に転送ができます。

実際にブロック転送をする時は、DSレジスターとESレジスターにA800H、SIレジスターに転送元アドレス、DIレジスターに転送先アドレスをセットして、MOVSB命令を発動します。4AEHにセットしたビット長がブロック転送されます。4096バイト以上転送する時は、CXレジスターに繰り返す回数をセットしておいて、
CLD
REP MOVSB
のようにします。

例)ワプの翼の効果(WPCLS98)の一部
;---------- EGCを動作可能にする
                PUSH       ES
                MOV        AX,0
                MOV        ES,AX
                PUSHF
                CLI
                MOV        AL,128
                OUT        7CH,AL
                MOV        ES:[0495H],AL
                POPF
                POP        ES

;---------- 拡張モードON
                MOV        AL,07H
                OUT        6AH,AL
                MOV        AL,5
                OUT        6AH,AL
                MOV        AL,06H
                OUT        6AH,AL

;--------- 全プレーン動作、パターンレジスタを使用
                MOV        DX,04A0H
                MOV        AX,0FFF0H
                OUT        DX,AX
                MOV        DX,04A2H
                MOV        AX,255        
                OUT        DX,AX

;-------- マスクなし ビットアドレス0 ビット長16
                MOV        DX,04A8H
                MOV        AX,0FFFFH
                OUT        DX,AX

                MOV        DX,04ACH
                MOV        AX,0
                OUT        DX,AX

                MOV        DX,04AEH
                MOV        AX,15
                OUT        DX,AX

;-----------------------------------------------------------------------------
;               スクロールメイン
;-----------------------------------------------------------------------------
SCROL           EQU        $
                MOV        [CHURCH],BYTE PTR 3
                MOV        [GENKAI],BYTE PTR 0
                MOV        [MOU],BYTE PTR 3
SCROL2          EQU        $
                MOV        [BY1],BYTE PTR 3
SCROL3          EQU        $
                CALL       PUTB
                MOV        AL,[BY1]
                CMP        AL,[CHURCH]
                JAE        SCROL4
                ADD        [BY1],BYTE PTR 1
                CMP        [BY1],BYTE PTR 48
                JA         SCROL5
                JMP        SCROL3
SCROL4          EQU        $
                SUB        [MOU],BYTE PTR 1
                CMP        [MOU],BYTE PTR 0
                JNE        SCROL2

                ADD        [CHURCH],BYTE PTR 2
                MOV        [MOU],BYTE PTR 3
                JMP        SCROL2

SCROL5          EQU        $
                CALL       SIRO
                ADD        [GENKAI],BYTE PTR 1
                CMP        [GENKAI],BYTE PTR 25
                JA         SCROL6
                JMP        SCROL2
SCROL6          EQU        $
                JMP        OWARI
;------------------------------------------------------------------------------
;    四角のBLOCKを1ブロック上に複写
;
;           In:BY1  Out:なし
;------------------------------------------------------------------------------
PUTB            EQU     $

;------- VRAMをパターンレジスタにいれて、パターンレジスタをライト
                MOV        DX,04A4H
                MOV        AX,0011000110000000B
                OUT        DX,AX


                MOV        AL,[BY1]
                CMP        AL,1
                JBE        PUTBEN
                CMP        AL,50
                JA         PUTBEN
                MOV        [GX1],WORD PTR 0
                MOV        [GX2],WORD PTR 0
                MOV        AL,[BY1]
                SUB        AL,1
                MOV        AH,0
                SHL        AX,1
                SHL        AX,1
                SHL        AX,1
                MOV        [GY1],AX
                JMP        L8
PUTBEN:         RET
;------------------------------------------------------------------------------
;                四角PUT
;
;------------------------------------------------------------------------------
L8               =         $
;------------------------------------------------------------------------------
;                カラー用
;------------------------------------------------------------------------------
COLL81           =         $

                CALL       CSSET

                MOV        BX,[SADDR]
                MOV        DX,BX
                SUB        DX,280H
                SUB        DX,280H

                PUSH       DS
                MOV        AX,0A800H
                MOV        DS,AX
                MOV        ES,AX

                MOV        SI,BX
                MOV        DI,DX
                MOV        CX,640
                CLD
                REP        MOVSB
                POP        DS

                RET
;------------------------------------------------------------------------------
;    黒い四角のLINEを最下段に引く
;
;           In:BY1  Out:なし
;------------------------------------------------------------------------------
SIRO            =          $

;-----------カラー用
CSIRO           =          $

;------- VRAMをパターンレジスタにいれて、CPUデータをライト
                MOV        DX,04A4H
                MOV        AX,0000000110000000B
                OUT        DX,AX


                MOV        CX,640
                MOV        SI,7800H
                MOV        AX,0
                PUSH       DS
                MOV        BX,0A800H
                MOV        DS,BX
                MOV        ES,BX
                CLD
                REP        STOSW
                POP        DS
CSL8EN          =          $

腕の見せ所

BIOSが貧弱な分、PC-9801はI/Oポートを直接操作したさまざまなライブラリが作られ、プログラマのスキル次第で極めれば本体性能を120%引き出したような高速処理も可能でした。

しかし、残念ながら当時は今のようにインターネットがあるわけではなく、プログラマ同士の情報交換はパソコン通信ぐらいしかなく、同じ処理(画面に罫線や矩形を描く)にしても、それぞれ皆が別々に作ってました。

また、せっかくPC-9801でのテクニックを極めても、現在のWindowsでプログラミングする上でほとんど経験が役に立たないのも残念です。

参考文献
『PC‐9801スーパーテクニック』 1992年 小高輝真、清水和文、速水祐 著 アスキー
『J-3100解析ハンドブック』 1989年 土屋勝著 ナツメ社
『DOS/Vテクニカル・リファレンス・マニュアル』 1993年 芦達剛著 ソフトバンク
スポンサーリンク