PC-98用 EGCの解説書もどき PC-98用DOSでも読めるようにCP932でCR+LFで書いてるつもりです。 第1章 前置き 想定しているのはVX以降の標準的なPC-9800シリーズです。 初代〜VM及び派生品シリーズはちょっと扱いが違うので注意してください。 といってもこんなものを読んでる人はそれくらい常識な人と思います。 なおどこかで書かれていることばかりです。もともとは私の脳内の整理のための文章です。 ご自由にお使いいただいてかまいません。 ただ、物知りな方がいらっしゃって間違ってる部分があったならぜひ指摘してください。 第2章 建て付け グラフィック用VRAMは128Kbが表と裏の切り替えで使えるタイプです。 (もう一度書きますが想定していない機種は違います。) 表を表示しながら裏に書いておき、タイミングを見て裏を表示する、なんてことで 工夫した表示をしたりすることもできたようです。 しかし連続した場所に配置されていません。 プレーンモード+パレット番号でアクセスするのが標準の仕組みです。 BIOS-CALLでグラフィックを有効にすると(他の方法でも構いませんが) 第0プレーンがA800セグメント 第1プレーンがB000セグメント 第2プレーンがB800セグメント 第3プレーンがE000セグメントの32kbにそれぞれ現れます。 各プレーンの同じアドレス1byteの重ね合わせで画面上の8dotのパレット番号カラーを指定します。 グラフィックGDCのスクロール命令でアドレスと画面上のdotの関係を指定できますが とても標準的な使い方をすると アドレス0の1WORDで640x400の画面の左上16dotを指定しています。 (CPUがリトルエンディアンであることには一応注意) そこから1byte毎に右に8dotずれていきます。 デバッガで適当にVRAMに数値を書き込むとそこが光ることで実際にはすぐに確認できます。 もちろん横は640dotしかありませんから80h進むと行が変わります。 書きたい場所に書きたい色を決めたらその通りにVRAMに書けばその通りに絵が出ます。 パレットの指定はIOアクセスで適当にやってください。 初期状態の16色で十分意味はわかるはずです。 第3章 EGC 実際は上までのことが前置きです。 PC-9801VXにはEGCが新しく搭載されました。 バグっていたらしくのちに改良されているそうですが実機を持っていないためよくわかりません。 前段階としてGRCGという4プレーン同時アクセス用のアクセラレータも準備されていました。 EGCの機能はよく言われているのが以下のものです。 GDCとの並列処理、ラスタオペレーション、ビット単位のシフト、VRAM上の任意領域の高速ブロック転送を可能にした。 引用元 http://radioc.web.fc2.com/column/pc98bas/pc98disphw1.htm ですがその方法がweb上に具体的に解説されているものがあまり見当たりません。 よってemulatorでの実装もそれぞれ残念な結果になっています。 情報元 https://seesaawiki.jp/asem9821/d/EGC%A4%CE%A5%A8%A5%DF%A5%E5%A4%C8%BC%C2%B5%A1%A4%CE%B0%E3%A4%A4 私もEPSON版Windows3.0のCRTE.DRVで試すまでまったく気にしていませんでした。 Anex86がここでは紹介されていませんが、これが一番実機に近い実装をされていると思いました。 この方は理性的な書き方でEGCの情報は非公開ではなかったと主張されています。 https://kei-sakaki.jp/2017/06/09/reason-talked-closed-specification-of-pc-9800-egc/ この書籍はもっていませんが確かに非公開ではないと思います。 ではweb上にあるEGCの解説を探してみましょう。 まずは誰もが知ってるUndocumented9801/9821 Vol.2です。 http://www.webtech.co.jp/company/doc/undocumented_mem/io_egc.txt ただし本当にIOの説明しかしていません。 次に室蘭PCによるDOSゲームの作り方的なマニュアルです。 もうwebarchiveにしかありませんがリンクです。 https://web.archive.org/web/20051120071049/http://www2.muroran-it.ac.jp/circle/mpc/program/pc98dos/egc.html テストプログラムもついているので何をやっているのかひとつづつ追いかけることが可能です。 Borland用ソースですがLSI-C86試食版あたりでもちょこっと書き換えれば動きます。 今も読めるものです。 http://software.aufheben.info/oohpc/gvram.html(リンク切れ 下に更新) http://software.aufheben.info/contents.html?contents_key=gvram 前段で書いた建て付けのことなども解説されています。 一番下にテストプログラムが載っています。 まだまったく読んでいないことに気づいたので読みます。 もう一つみつけたものです。 THE パブリック ドメイン マニュアル of エンハンスト グラフィック チャーシュー (Ver. 0.55) Written by Applause です。 https://onedrive.live.com/?cid=37E157F1D87E4BC4&id=37E157F1D87E4BC4%21586&parId=37E157F1D87E4BC4%21584&o=OneUp 転載してくれた方のおかげで読めました。 ここだけの情報としてはEPSON版のEGCはNECのバグを潰しているということが書いてあります。 最後に開発側からの文章が出てきましたので紹介します。 概念図がとてもわかりやすいです。 しかしNECがオープンにしなかった、という文言にはさまざまな傍証から私は否定をします。 https://jhalfmoon.com/dbc/2023/10/17/%e8%aa%98%e3%81%86pc98%e4%ba%92%e6%8f%9b%e6%a9%9f5-gdc%e3%81%a0%e3%81%91%e3%81%a7%e3%81%af%e3%81%aa%e3%81%8fegc%e3%82%82%e3%81%82%e3%82%8b/ 第4章 EGCを使っている商用 非商用プログラム 上で紹介した103B_EGC.EXEはAnex86以外では実機と違う結果を示します。 RSoldier IO誌投稿プログラム GDCスクロールも入ってます。スクロールさせてないemulatorは表示できないはずです。 Anex86はスクロールコマンドに対応していないと思われます。 (私的に改造しているqemu/9821では一応OK ただしBEEP-PCMが出ませんよ) 東方Projectのシューティングゲーム体験版 Anex86はGDCスクロールがおかしいです。 NEC製Windows3.0B 16色モード emulatorでも問題ないようです。 NEC製Windows3.1 16色モード emulatorでも問題ないようです。         GDCからEGCを使った書き込みがqemu/9821で実装されていませんでした。 MS製Windows3.1 同上 Windows9x セーフモードでEGCを使っています。emulatorでも問題ないようです。 WindowsNT 16色モード emulatorでも問題なさそうです。 EPSON版Windows3.0 実機Na15とAnex86以外では不具合があります。それを次章で書きます。 EPSON版Windows3.1 NEC機ではダメらしいです。    情報元http://www.cham-reo.com/logsearch/Log.aspx?c=windows&d=20001015T075504&id=9961 Browser Returns すごいソフトです。触って感動してください。 https://www.vector.co.jp/soft/dos/net/se148214.html このソフトはEGCを使っていますがVRAMにはバイトアクセスをしています。 第5章 EPSON版Windows3.0の動作で確認するemulatorでの実装 qemu/9821で動かそうとしました。 NP2系からの移植でできているのでNP2系も動作はほぼ同じです。試せていませんがCSPも同じだと思います。 できていなかったことはパターンレジスタを使ってVRAMに書き込むとき つまりIO 4A4H bit12が立っている時のbitマスク及びbitシフトが計算されていません。 FGやBG、パターンレジスタが全部VRAMに一気に書き込みされてしまうので画面がぐちゃぐちゃになります。 SL9821も動作を見てるとそんな気がします。 書籍では画面塗りつぶしに使いましょうと提案されている?ようで16dot全部塗りつぶしでも実際はほぼ問題が起きなかったようです。 追記 しかし、EGCを有効にしてから1度もROP計算をせずにいきなりパターンをVRAMに書き込む動作をさせると16dot全部塗りつぶしになります。 (現在実機確認でならなくなりましたが、変な挙動であることは確かです。) NP2の作者さまはこれを確認して16dot全部塗りつぶしをさせたのではないか?と予想をします。 またSL9821の作者さまも一度目の実行と2度目の実行で結果が違うことがあることを指摘されています。 次に1プレーンリードの値です。NECのバグがあるようですがCRTE.DRVではbit13は立ててくれるのでNEC機でも問題ないようです。 NP2及びそれの移植物ではVRAMの値をそのまま返します。 (追記 嘘でした。シフトは行われています。それが完全な計算ではなかっただけです。bit10が立っていないときだけですが lastvramがinptrに入れられそれがシフト計算された後 egc.srcに格納されてそれを返しています。 bit10が立っている場合はシフトされずにVRAMデータを返しています。ただし読み出し時にこのbitを立てることはバグっているという解説があります。) 実機ではシフトして渡されていました。 最後に偶数アドレスにWORDアクセスしてこない場合です。 残念ながら好き勝手に書かれてしまいます。奇数アドレスからWORDアクセスもしてきますし、BYTEアクセスもしてきます。 第6章 np2ソースで確認する実行内容 EGCをONOFFするのはIOでやれます。それは上の紹介した解説でわかります。 ではVRAMに書き込むときに何が起きているかのemulatorのソースによる解説です。 np2系のmemegc.cを参考にします。 基本的にWORDアクセスが偶数バイトにあった場合です。それ以外はイレギュラーです。 最初にGDCからのアクセスかどうかのチェッカがありますがそこは省略します。 まずはパターンレジスタの中身を調整します。 IO4A4hのbit9が立っていてbit8が立っていなければ パターンレジスタの中身をVRAMからコピーします。 bit8が立っている場合は書き込み時には変えずに読み込み時にパターンレジスタを入れ替えます。 ただしbit13が立っていると全部のプレーンをロードしますが立っていないとIO4A2hでの選択プレーンだけを変更するとされています。(未テスト) np2のソースでは全部のプレーンでやっているようです。が、これで問題が起きるようなプログラムはなかったようです? bit12が立っているとこのパターンレジスタをVRAMに書き込みます。 パターンレジスタの中身はフォアグラウンドカラーの場合やバックグラウンドカラーの場合があります。 それはIO4A2のbit13とbit14で確認します。 ただnp2系はここがbitシフトされていたりbitマスクされていたりしてもそのまま16dot書き込んでしまうので修正案をアップロードしておきます。 (追記 この案は削除済み) IO4A4hのbit11もbit12も立っていないとCPUから書き込んだ値をそのまま書き込みます。 ただし4プレーン同時アクセスですから実機では単純に4回書き込むよりは早く処理できる可能性があります。 ここもプレーンマスクは使えますがnp2ではbitシフトの計算がされていません。 まぁ直接書き込むわけですから実際にはbitシフトは書き込む前にプログラム側が計算していることがほとんどでしょう。 と思い込みましたがやっぱりbitシフトもbitマスクもしないとだめなようです。 次が大事なIO4A4hのbit11が立っていてbit12が立っていない場合です。ROPという計算を行います。 下位8bitで計算の内容を決めます。それぞれのbitが立っているときどんな値を書き込むかが具体的に書かれています。 bit10が立っていなかったらシフタに入っている値を使い、立っていればCPUから書き込まれた値を新たにシフタに使います。 それをIO4AChとIO4AEhで決まったシフト値と書き込み長さからいろんな計算をして書き込むbitを決めてシフタに入れ直します。 (パターンを書くときや直接値を書き込むときにも書き込むbitとシフトは必要な計算だと思います。これが上で書いた省略されているものです。) 次にROPの本番です。ope_xxを呼んだ場合を考えます。 パターンレジスタにフォアグラウンドカラーかバックグラウンドカラーかシフタに入っているデータのどれかを入れます。 それはIO4A2Hのbit13とbit14で指定されているものを利用します。 次にVRAMのデータを別で準備します。 それから下位8bitで立っているbitに従って パターンレジスタの値とVRAMの値とシフタに入っている値を組み合わせます。 xxの場合は 一番左のbitから有効にされていると   パターン & シフタ &  VRAM NOTパターン & シフタ  & VRAM   パターン & シフタ  & NOT(VRAM) NOTパターン & シフタ  & NOT(VRAM)   パターン & NOTシフタ&  VRAM NOTパターン & NOTシフタ & VRAM   パターン & NOTシフタ & NOT(VRAM) NOTパターン & NOTシフタ & NOT(VRAM) を全部ORしたものが入ります。 ここの書き順が書き手によって違いますが同じものです。シフタと書いた部分は室蘭PCのマニュアルにはソースデータと書かれています。 例えば下位8bitが全部立っていると 自分自身のNOTとORされることになりますから0xffを返すことになります。 他の場合も同様に計算されます。計算省略のためにいくらかはセットアップが準備されていますが基本的にはxxの計算と同じはずです。 使う人は必要なものだけを取り出せるようにbitを立たせるのがコツのようです。 なお室蘭PCのマニュアルにはROPしてからシフトして書き込むとされています。np2では順序が違うようですが…そこはまだよくわかりません。 一度改造に取り組みましたが諦めました。動いているので考えることを放棄しました。 3種、どの場合でも出てきた64bitデータが各プレーンの1WORD値を4つ並べたものになります。 それをIO4A0で設定した有効プレーンにだけ書き込み処理が終わります。 なお書き込みdot長が16の倍数ではない場合に連続書き込みで残ったbit分VRAMに0を書き込むようですがnp2では書き込み自体をやめてしまいます。 次はリードの場合です。 IO4A4hのbit10が立っていなければシフタにVRAMのデータを取り込みます。 立っていれば前述の通り書き込むタイミングでシフタに必要なものが取り込まれるはずです。 IO4A4hのbit8が立っていてbit9が立っていなければ(書き込みの時と逆の時です。) この段階でパターンレジスタにVRAMの中身をコピーします。 ここもbit13の各プレーンのみになるチェックが抜けていると思います。が普通は4プレーンコピーするでしょう。 この後IO4A4Hのbit13のチェックがあり それが立っていないとIO4A2hのbit8とbit9で選択しておいたプレーンのVRAM値を返します。 実機ではシフト付で返しているのは確認済です。np2ではシフトせずに返します。 ただしNEC機ではIO4A4Hのbit10が立っていないと変な値を返すとされています。 np2はそのように作られています。 bit13が立っているとコンペアリードというモードに切り替わります。 GRCGのタイルレジスタと同じなら1という奴と同じでフォアグラウンドカラーと同じなら1を返すという機能ですが np2ではそのままVRAMの値を返すようになっています。 間違えてはいますが実プログラムで問題になるかはわかりません。 第7章 プログラム例 EGCでできることに任意のブロックの高速転送というのがあります。 SL9821の技術的な話 グラフィックのページに http://www.satotomi.com/sl9821/sl9821_tec4.html IO操作が載っています。 IO4A4Hを4A6Hと書き間違えているようですが IOアクセスが終わった後 メモリをリードライトと繰り替えして右に2dot動くプログラムはできました。 メモリのリードがないと全画面0で埋め尽くされます。 IO4A4hが28F0hのときに起こるであろうことを解説します。 まずメモリを読まずに書き込んだ場合 bit8もbit9も立っていませんからパターンレジスタは変わりません。 bit10も立っていませんからシフタにはVRAMから読んでおいた値を使います。(これが空です。) bit11が立っていますからROPしたものを使います。 下位8bitがF0hですからシフタに入っているものだけを返します。 よって全部0が書かれます。全画面パレット0番(黒)塗りつぶしです。 メモリを書く前に一度読みながら書いた場合 bit10が立っていないのでVRAMを読む毎にシフタにVRAMのデータが入ります。 これをROPで使います。2dot移動させます。綺麗に右に動きました。 ただしIO4AEhで長さを適切に設定しないと2dotの移動が狂います。 640dot毎に2dot移動させるのか16dot毎に2dot移動させるのかここで設定できるようです。 当然左に移動させたい場合は話が変わります。2byte先を読んでから書き込む必要があると思います。 SL9821の技術ページPEGCにはデクリメントで行う方法が紹介されています。 では同じことをVRAMの読み込み操作なしでやってみましょう。 VRAMにシフタの値を使うには読み込みが必要です。そうしないなら 書き込み時にパターンレジスタにVRAMをコピーしてもらえれば解決です。 bit9を立てる必要があります。ROPではパターンだけを返したいので下位8bitはAAhになります。 実際には4プレーン全部をコピーしたいですからbit13も立てる必要があるでしょう。(ここはnp2は無視しています) 4A4hに2AAAhを入れてシフト付で書き込むだけで数dotのずらしができます。と思いましたがシフト演算はパターンに対してはnp2ではやっていません。 (実機で行えるかまだテストしていません。) 同じアドレス内だけでしか操作が効かないのがこの方法の弱点です。 28F0を使って移動する方法とは別で2CF0hでやる方法があります。 これがかなり悩まされたEPSON版Windows3.0のCRTE.DRV下のソリティアで起きていたこと(と思い込んでいるもの)です。 まずIO4A4を0800hにしてカードの絵柄を1プレーンリードで4回読み込み保存します。 ここでシフト付で読んでいるのでnp2系ではバグっていました。 次に読んだ値を別の位置に2CF0でCPUからコピーします。 bit10が立っているのでCPUから書き込む値がシフタに使われます。 ソリティアのカードの移動毎に何度も絵柄を読みにいかなくていいように最初に読み込んでいるのだと思います。 次にEGCが実際によく使われたであろう透過処理です。 背景にキャラクタを配置するなどする場合の処理です。 103B_EGC.EXEではマウスカーソルが表示されるときに行われています。 VRAMに以下のようにデータが入っていたとします。 12 34 56 78 9A この56hの部分だけを81hにしたいという場合を想定しましょう。 (WORDで考えるとリトルエンディアンのことを考えてめんどくさいのでBYTE操作で考えることにします。) FF FF 00 FF FFといったデータを準備します。 これを4A4HをCC0hにして書き込みます。上書きしたい部分だけが0とANDを取られて消えますが他はFFとANDを取られるので変化なしです。 次に00 00 81 00 00といったデータをORを取るように書き込めば透過して書き込まれたように見えます。 4A4HはCFChです。 第8章 まとめ EGCの処理についてよく公開されていないとされているが、事実としてweb上にあるもの以上のものはみつからなかった。 それでも全部の処理が追いかけられた。もちろん未使用bitとされているものに何か機能が隠されているかもしれない。 ただしBYTEで書き込んだ場合、奇数WORDから書き込んだ場合についてはイレギュラーとして放置したが そこの処理がよくわかっていない。そこもそのうちまとめるかもしれない。 第9章 蛇足 なおPC-H98シリーズにはE2GC PC-9821初代及びMultiにはPEGC A-MATEで使えるハイレゾボードにはE3GCと機能追加シリーズがあるようです。 第10章 おまけ openwatcomでコンパイルできるテキトーなプログラムを付属させました。 元々は室蘭PCによるDOSゲームの作り方的なマニュアルのEGC解説に付属していたプログラムコードです。 完全に流用しています。転載はOKですが改変OKとは書かれていないので困りました。言い訳としては研究用の引用ということにしています。 縦線を3本描きます。103B_EGCがテストしていることとほぼ同じです。左はwordで、真ん中は奇数偶数byte。右は奇数byteです。 左中程にキャラクタを置きます。それを右に二回コピーしています。左の方はシフトなし、右側の方はシフト付きでテストしました。 上の段はCPUで読み込んだあとに書き込み、下の段はパターンデータを取り込みそれを書き込ませています。ROPはさせていません。 その時にCPUから見えるデータを文字にして表示します。(パターンデータも表示したかったのですが読み取り時と出力時で中身が変わるかもしれないのでやめました。) パラメータを適度に変えてコンパイルして試せるようにしました。そのため引数がとても多いです。コマンドオプションにはしませんでしたがその方がよかったかもしれません。 令和2年 8月12日深夜 書いてる途中 令和2年 8月13日夜  まだ書いてる途中 ただしROPとシフトの計算が逆ではないかと妄想中 そんなことなかった 令和2年 8月15日夜  一応初版とする。 令和2年 8月17日   np2の64bitデータ部を32bitとの書き間違え修正 2AAAでのプログラム例を追加 令和2年 8月18日 即値を書き込むときもシフトは必要と書きたし  令和2年 8月21日 ROPの指定bitをなぜか下位4bitと書いていたのを修正 bitマスクとプレーンマスクの表記をなるべく区別 令和2年 8月24日 2版 書き直し予定が取れない気がするのでここでアップロード 令和3年 10月17日 3版 追記を少々 テストプログラム付属(著作権的にどうですかね?) 令和5年 10月17日 2年ぴったりで更新 当時の中の人の解説文へのリンク追加 EGC使用プログラム例を追加 令和5年 10月20日 BrowserReturnsの動作をチェック 文責リウ https://twitter.com/drachen6jp