X1turbo Agency

X1turbo Agency ブログ

8bitコンピュータ X1turboの探求サイト

X1turbo Agency

Windows10環境での USB-CVRS9の認識について

Windows10環境での USB-CVRS9の認識について

他のユーザさんから情報を頂きましたので紹介しておきます。
サンワサプライのUSB-シリアルアダプタ、USB-CVRS9が Windows10環境で認識されない件の解決方法です。

USB-CVRS9

USB-CVRS9をWindows10環境で使用した時に、認識がされず、
X1turbo Remote Monitorでの動作も出来ない件が発生することがあります。
この症状は USB-CVRS9がWindows10では非対応なため発生します。
解決方法は、Windows8のドライバをインストールすると解決するということです。

もし同じような症状で悩んでいる方がいらっしゃったら参考にして頂ければと思います。
情報をご連絡頂きましてありがとうございます。

Windows8ドライバ

X1turbo DMA バイト転送/バースト転送の負荷計測

X1turbo用 DMA負荷計測について

X1turboには Z80 DMAが搭載されています。
Z80 DMAのバイトモードと比較のためバーストモードについて探求しました。

実機側の処理負荷は走査線位置を表示して計測します。
この画像では、赤い線がDMA処理負荷、青い線がCPU処理負荷を示しています。
それぞれが同じ処理負荷ですが、DMA実行中はCPU処理が遅くなるため、青い線が太くなります。

Z80DMA バイト転送

バイトモードは、CPUとDMAの両方がバスを使用して転送を行います。
X1turboでFDを読込ながらBGMを鳴らす場合もこれを使用しています。
DMAの転送中でもCPUの処理が行えます。

Z80DMA バースト転送

バースト転送は、DMA転送が終了するまでバスをDMAが占有します。
その間はCPUは他の事ができなくなります。

サンプルプログラム

DMAテスト用プログラムを作成して検証を行います。

毎フレーム CPUで処理を実行し、並列にDMAバイト転送を行います。
バイト転送の転送先/転送元の種類を変更して、CPUの処理終了時間を実測します。

  • 転送種類

    • メインメモリ, GRAM,EMM
  • 計測時の画面モード

    • 15KHz / 24KHz
  • 実行環境

実行結果

  • 転送量 06c0h
  • M: メモリ / G: GRAM / E: EMM / T: TEXT
バイト転送計測比較

CPUとDMAが並列に実行しているモードです。
比率はDMAが動作していない時との処理時間の比率です。

バースト転送計測比較

DMAが優先して実行しているモードです。
比率は最速のメモリ間転送時の処理時間の比率です。

実行結果から分かった事

バイト転送
  • DMA実行時、CPUの実行スピードは 1/3 ~ 1/4 まで低下する。
  • 実機ではエミュに比べると転送 30%程度遅い。
バースト転送
  • GRAMからの読出しはあまり影響がないが、GRAMへの書込みは30%程度遅くなる。
    • CPUのアクセス速度では表面化しにくい?
  • GRAM間コピーはそれ以上に遅く、15KHz,24KHz転送時に差があり、24KHz で 50%、15KHzで 70%程度遅くなる。
  • 例えば、GRAMに描画データを載せて、DMAでGRAMへ転送すると想定以上に遅くなる。

まとめ

DMA転送の処理計測を行って特性を調べることが出来ました。
この辺りが判ると更に高速化、効率化を行う事が出来ます。

処理負荷を下げるには、「対象を明確にして不明点を無くすこと」が有効です。
Z80のシステムでも、64bit のマルチコアの最適化でも、そこは変わらないです。

X1用ゲームライブラリ SGL

X1 SGLとは?

X1 SGLとは X1/turbo用のゲームにおける性能を引き出す事を探求したライブラリです。

きっかけ

X1はハードウェアスプライトの機構を持っておらず、描画時にチラついたり、描画負荷の重さからプレーン数を減らして色数が少なくなる事も多いです。
Newグラディウスデモを制作時に、これらを改善するべく自分なりにライブラリを作ったらどうなるか?というのを探求してみました。
このページでは主に今まで分散していたSGL記事をまとめたものです。
その中でも、ソフトウェアスプライトでの取り組みを説明します。

問題点

ソフトウェアスプライトを実現するために問題点をピックアップしてみます。

X1では重ね合わせの処理が重い

ソフトウェアスプライト処理は、X1ではGRAMのデータを ANDを取ってORを使ってはめ込む重ね合わせ処理を行います。

重ね合わせ処理

バンク切り替えは不要なものの、I/O空間にGRAMがあるため比較的遅いI/O命令でのアクセスが必要です。
しかもそれらをRGB 3プレーンに対して処理を行うため、非常に重い処理になります。
この処理を軽減するためには、できるだけ重ね合わせを行わないことがポイントになります。
必要な時だけ重ね合わせを行って、それ以外は単純に書込みだけを行えば処理を軽減できます。
そこで重ね合わせを制御する機構としてビットラインバッファという制御ワークを用意します。
下の図はそのイメージ図です。

ビットラインバッファ

このビットラインバッファのアドレスはライン単位になっており、bitが各ラインが書込み済みかどうかを示しています。
X1のGRAMは変則的な並びですが、描画時にも処理を8ラインをひとまとめにすることで効率化を図っています。
ビットラインバッファを画面消去時もすでに消去済みかどうかをチェックして、2重に消去しないことで処理を軽減します。

描画プレーン数を減らして処理速度を改善する

描画プレーン数を減らす事で高速化を行いますが、出来るだけ表現力を上げる工夫をしてみます。
以下の様にパレットを設定します。

パレットセット

1プレーン描画では以下の色を描画します。
負荷が最も軽いため、数が多く登場するキャラクタに使用します。

単体プレーンのパレット

2プレーンの組み合わせを、Blue + Red,Blue +Green,Red + Greenと分けることで、少ないプレーン数で色数を増やしています。

  • 青色系統のキャラクタ (白/水色/青)
  • 赤色系統のキャラクタ (白/黄色/赤)
  • その中間 (白/水色/赤)

2プレーンパレットの組み合わせ

ちらつき軽減のためにダブルバッファを使用する

描画中の様子が見えてしまうとチラツキが発生します。
チラツキへの対処はダブルバッファが有効です。
X1は320x200x8色の描画ページを2枚持つことができます。これを活用してダブルバッファを実現します。
ページは、0000-07ffhのアドレスを分割して、0000h-03ffh, 0400h-07ffhの2ページで切り替えます。
この2ページを表示ページと描画ページに分けます。
表示ページを表示している間に、裏で描画ページに対して書込みを行います。
これをVSyncのタイミングで、表示と描画を切り替えます。 ダブルバッファは結構強力な機能です。3D用アプリケーションにも活用できると思います。

実装の詳細について

ビットラインバッファ

ビットラインバッファは書込まれているデータがあるかどうかを、ラインに対応したビットをOn/Offすることで判定を行います。
ビットラインバッファの配置アドレスは、0f800h とORすることで算出します。
アドレスは固定になりますが、メインメモリの 0f800-0ffffh の2KBをビットラインバッファに割り当てます。
この方法のメリットは、GRAMのアドレスからビットラインバッファへのアドレスを求める事ができることです。
ビットラインへのアドレス計算を別に持つ必要が無いため高速に扱えます。
画面消去時も、GRAMアドレスからビットラインバッファへのアドレスを算出することができます。

ビットラインバッファでの描画とプレーン数最適化による描画コスト削減

ビットラインバッファを参照して描画エリアにすでに描画されているかを判定して、
ビットが立っていない場合は直接書込みます。 ビットが立っている場合は重ね合わせ処理を行います。

書込み時に1,2,3プレーン数ごとに処理を分けます。
1プレーン書き込みを行う時は、BRGプレーンに分けて3色の描画処理。
2プレーン書込みを行う時は、BR,BG,RGの3パターンに分けます。
こうすることで、描画負荷を下げながら色数を増やす事ができます。
3プレーン書込みは、BRGプレーン全部に対して書込みを行うので一番処理が重いですが、
それよりも重ね合わせ処理の方が重いのです。

同時アクセスモードによる消去処理

X1の固有の処理として、同時アクセスモードを使用して3プレーンを同時にクリアできます。
描画時に描画アドレスとタイプをストックして消去に使用します。
描画アドレスをストックしておけば、そこからビットラインバッファアドレスを求める事が出来ます。
消去の際にビットラインバッファをチェックして一度消去した場所はスキップするようにしています。

座標系を固定小数点 9:7 にすることで高速化

X1 SGLは画面サイズは 320x200を使用しています。
横 320は8bitで収まらないため、16bitを使用した場合、計算用に固定小数点 8bitを加えると、24bit必要になります。
そこで、X1 SGL内の座標系は、X座標を 1byteで表現することで高速化を行います。
固定小数点 9:7 の16bitで計算を行い、当たり判定やクリッピングなどを1byteのみで判定しています。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
XH XH XH XH XH XH XH XH XH XL XL XL XL XL XL XL

0-319の座標は、上位byteでは 0~159で表現されます。
9bit分なので1bitを下位バイトに持たせています。
この方法のメリットは以下の通りです。

  • 24bitではなく16bitの計算で収まる。
  • 表示座標を求める時は、1bit シフトして下位byteの最上位ビットと合わせて行います。
  • 固定小数点が7bit使える。(大抵は 4bitもあれば十分)
  • 移動スピードが2の倍数の時は上位byteのみ計算する。
  • キャラクタ同士やBGの当り判定は 8bitで計算を行って高速化できる。

描画用データ

キャラクタデータは事前にビットシフトを計算して展開します。
容量を多く必要としますが、高速に描画することが可能です。
容量を減らしたい場合は、2ドット単位のデータにしたり、プレーン数を減らす事で調整を行います。

画像の変換は自前のコンバートツールを使っています。
高速化のためにクリップ情報などを事前に計算しています。

コンバート例:

 ; データテーブル
 en1_p0_c2:
  db 0fch ; Pivot(x) -4
  db 0f8h ; Pivot(y) -8
  db 8 ; データ数
  dw en1_p0_c2_0 ; 0
  dw en1_p0_c2_1 ; 1
  dw en1_p0_c2_2 ; 2
  dw en1_p0_c2_3 ; 3
  dw en1_p0_c2_4 ; 4
  dw en1_p0_c2_5 ; 5
  dw en1_p0_c2_6 ; 6
  dw en1_p0_c2_7 ; 7
  
 en1_p0_c2_0:
 ; Xoffset: 0
 ; Mask: [G R]
 ; OutSizeX: 16 OutSizeY: 16
  
  db 183 ; clipy(byte) 200-sizey-1
  db 020h ; DrawType (Plane: RG SizeY: 16)
  db 39 ; clipx(right) 40-sizex+1
  db 63 ; clipx(left) 64-sizex+1
  db 2 ; sizex(byte)
  db 48 ; sizey(pitch)
  
 ; 0
  db 0ffh, 000h, 000h
  db 0ffh, 000h, 000h
  db 0feh, 001h, 000h
  db 0e8h, 017h, 000h
  db 0c8h, 037h, 001h
  db 0c8h, 037h, 000h
  db 085h, 07ah, 010h
  db 082h, 07ch, 011h
  db 0c2h, 03ch, 019h
  db 0c1h, 03eh, 00ch
  db 0e2h, 01dh, 000h
  db 0fch, 003h, 000h
  db 0f0h, 00fh, 002h
  db 0f8h, 007h, 001h
  db 0fch, 003h, 000h
  db 0ffh, 000h, 000h

デモ Balls / GitHub

SGLで16x16スプライトを描画するデモ「Balls」を作ったので、
X1用Ballsの実行ファイル(X1用)を置いておきます。
X1/turbo用なのでX1でも実行できると思います。

youtu.be

2019/07/06 ソースファイルをgithubに公開しました。 github.com

X1 Balls (D88)

性能評価

プレーンの組合せで変わりますが、以下の性能でした。

FPSモード Ball 1プレーン Balls 2プレーン Ball 3プレーン
60 8 6 6
30 17 14 13
20 31 29 19

使用する環境(実機/エミュレータ),X1/turboでも数字が変動します。

プレーン2枚の負荷が相対的に軽いので、利用しやすいと思います。
重ね合わせ処理をもっと細かくチューニングすればもう少し向上できそうですが、
スプライト数が増えてくると、ビットラインバッファの制御負荷をもっと下げる工夫が必要です。

デモ MarkX

X1 SGLを使ったスプライトデモをもう一本作りました。
あんまりリソースを増やさずに作ろうと考えたので、3Dデモになりました。

youtu.be

まとめ/今後の課題

X1 SGLはまだ改善点を探求しています。
ソースを公開しているものの、そもそも使い方を書いてないので、そちらもまとめてみます。

  • より高速な描画や最適化について。
    • より高速化な手法が無いか考え中。制御ワークを使う以外では対象ごとに描画方法を変えるのが一番有効。
  • 左右反転描画手法
    • 処理速度を出来るだけ落とさずに左右反転を行う手法。メモリ軽減に大きく寄与できるのでぜひ欲しい。
  • 上下反転
    • 左右反転と合わせて、メモリ軽減に寄与できます。こちらの方がまだ現実的。
  • プレーン組合せ
    • これはわりと近々に試してみたいネタです。
    • 2プレーン書込みが割と実用的なので、BR,BG,RGを組み合わせても割と高速に表現できるはず。

X1turbo Remote Monitor v1.2.2リリース

X1turbo Remote Monitor v1.2.2をリリースしました。

詳しくはX1turbo Remote Monitorのページを見てください。

今回は以下の修正を行いました。

  • d88ファイル読込み時の例外発生
    • FD書込み時などで、d88ファイルを読込む際に例外が発生していたのを修正しました。
    • d88ファイルではセクタエラーが発生すると、セクタデータが無くなってしまい、それが原因で例外が発生していました。
    • セクタデータが無い場合は、ブランクデータを入れ替えることで例外が発生しないようにしました。

X1turbo Remote Monitor v1.2.2

X1turbo Remote Monitor v1.2.1リリース

X1turbo Remote Monitor v1.2.1をリリースしました。

詳しくはX1turbo Remote Monitorのページを見てください。

今回は以下の修正を行いました。

  • FD書込み/FD読み込み
    • FD書込み部分は対応が簡単なものになっていたので、エラーチェックや、タイミングなどを修正して安定性を向上させました。
    • 合わせて、FD読み込み部分も修正を行いました。まだ完璧ではないので、今後も修正を続けます。
    • FD書込み部分に Verify機能を追加して、書込み後に再度読み込みして比較を行い、データが正常に書込めているかをチェックします。
    • エラーになった場合は、再度書込むようにしました。

X1turbo Remote Monitor v1.2.1

X1turbo Remote Monitor v1.2.0リリース

X1turbo Remote Monitor v1.2.0をリリースしました。

詳しくはX1turbo Remote Monitorのページを見てください。

今回は以下の修正を行いました。

  • FD書込み
    • 2HD書込み時に77トラックまでの制限を掛けていたのですが 78トラック以上指定しているD88ファイルを指定するとアプリが停止していました。
    • 82シリンダ (184トラック)まで指定できるように修正しました。
    • 以前から不具合があったのですが、対応が遅れていました。

X1turbo Remote Monitor v1.2.0

X1turbo Remote Monitor v1.1.8リリース

X1turbo Remote Monitor v1.1.8をリリースしました。

詳しくはX1turbo Remote Monitorのページを見てください。

今回は以下の3つの機能を追加しました。

  • 転送ボーレート変更タイミングを各コマンドの開始時/終了時に変更するようにしました。
  • FD書込み
    • 書込みトラックの範囲を後から変更できるようにしました。
    • EMMへの書込み機能を用意しました。EMMにキャッシュデータを書込む等活用する事ができます。
  • プログラム実行機能
    • 実機でちょっとプログラムを検証する場合など、プログラムを転送して実行が可能です。
    • .2d/.d88などのディスクファイルを指定した場合は、IPL部分を転送/実行します。
    • CZ-8FB02などのBASICも直接起動できました。(その後の startup.basが読み込めないですが)

エミュレータでも開発を行うのですが、実機でプログラムを検証したい時に
実行機能が思いのほか便利でした。

通信エラーが発生する事があるということで、そちらも調査しています。
通信設定なども関係ありそうなので、いくつか環境を用意しています。
今回からトラブルシューティングのカテゴリも追加しました。

f:id:x1turbo_agency:20210525095336p:plain

X1turbo CRTCでの縦スクロールテスト

X1turbo CRTC制御による縦スクロールテスト

X1turboで縦方向のスムーズなスクロールは可能だろうか?
実現のきっかけは、スーパーターボさんがアップされていた以下の動画でした。

www.youtube.com

この中でX1turbo版ではカットされていた縦方向の画面スクロール部分を
CRTCで実現している箇所が衝撃的でした。
この動画を参考に、CRTCによる縦スクロール制御をテストを作ってみたのが以下の動画です。

www.youtube.com

CRTC縦スクロールの仕組み

CRTC 各レジスタを調整する事で縦スクロールを実現します。
まず レジスタR5を変更して、画面全体のラスタ位置調整して画面全体を上下させる事ができます。
しかしある程度の値を設定すると同期がずれて画面が崩れてしまいます。
次に、行のラスタを調整しているレジスタR9を書き換える事でラスタ総量を調整します。

例として、画面全体に1ライン下に下げる時は、R5値を+1して、
最終行のR9値を-1して全体のラスタ総量が変わらないようにします。

このレジスタR9を変更するのは走査線が最終行に差し掛かった時に行う必要があります。
それ以外のタイミングではR9を初期値に戻しておかないと同期ずれが発生します。

f:id:x1turbo_agency:20210404163438p:plain
CRTCレジスタ (「試験に出るX1」から引用)

これでラスタ単位の縦シフトが0-7まで実現できたので、あとは画面全体の8ドット単位のスクロールを
行います。CRTC R12,R13による描画開始位置を設定するのが処理負荷を下げやすいです。

R12,R13レジスタの値は、VSyncが終了する直前に設定されるので、画像表示期間中に変更しても
反映されません。即時反映する事ができれば、部分スクロールなどが実現できたのですが…
そうなるとティアリングで画面が壊れやすくなっていたでしょうね。

実装

最初のテスト版はVSyncや走査線チェックをCPUポーリングで行いました。
これらを非同期に行うため、その後CTC割込み版を実装しました。
上の図の緑色の吹き出しの箇所で割込みを発生させています。
CTC0を割込み中にリセットして再設定することで、より細かい制御が可能です。

サンプルプログラム

  • CRTCによるスムーズな縦スクロールのサンプルを githubに公開しました。
  • 検証やツッコミがあればレポートをお願いします。

2021/04/17 ソースファイルをgithubに公開しました。 github.com

f:id:x1turbo_agency:20210417225716p:plain
CRTC縦スクロールサンプル

TIPS

画面マスク

画面上部はCRTC R5レジスタ値を反映して何も表示されていません。
スクロールが見えないようにするためには、GRAMであれば描画量を調整し、
PCGであればマスクを上に載せる必要があります。
テスト版ではPCGの上にGRAMが表示されるように設定を行って、
スクロール値に合わせてマスクしています。

走査線の位置確認

走査線が今どの辺にあるかが判らないと調整しにくいです。
パレット0を一度黒以外の青とかに設定することで現在の走査線の位置を確認できます。
これを使ってどの走査線位置に割込みが起きているかを判定します。

パレット設定に失敗するタイミング

VSync中にパレットを変更するとパレットの反映が遅くなる事があるようです。
VSync中でない画像表示期間タイミングでパレット設定を行うと正常に設定されています。
この辺りはX1turboの機種によっても変わるかもしれません。

VSyncタイミング

1a01h pb7でVSyncの開始/終了を検知しています。
ところが、VSyncの終了検知したはずなのに、まだ VSyncが終わっていませんでした。
そこで、VSync開始時にタイマー割込みをセットして、VSync終了後最初の走査線に
差し掛かるタイミングを検出するようにしました。

VSync終了時に次のVSync割込みを設定しますが、
その際に一度HSyncを待たないと割込み位置が安定しませんでした。

HSyncを待つには

細かい走査線をチェックする上でHSyncを待たないといけない場面があります。
その場合は、PCG高速モードにして適当にCG読出しを行うと、HSyncを待つ事ができます。
HSync中でないとPCG/CGデータを読み出す事ができないためです。

動作検証とエミュレータ

動作検証はエミュレータ上では走査線ごとのCRTCレジスタ書換えに対応していないため、
実機で行う必要があります。
ただ、CRTCについては、かなりの再現度で武田さんのエミュレータは実機同様の動作を
しており、R9が即時反映されない以外は、VSyncタイミングのずれなども含めてほぼ完璧に
再現されています。

検証の度にFDDにプログラムを書込んでいたのでは検証に時間がかかるため、
X1turboRemoteMonitorでプログラムを転送/実行する機能を追加しています。
こちらは他機能が検証できたら公開したいと思います。

サウンド

CTCを画面制御に使っている関係で、サウンドの制御はVSyncタイミングで行えればと思っています。
X1turboのVSyncは15KHz,24KHzいずれも60fps丁度ではないので、サウンドのテンポの問題があります。

今後の改良

X1turbo用で解説していますがX1でも使用できるはずです。
CTCを搭載したX1で、HSyncをタイマー等で調整できれば同様に使えると思います。

今回はVSync中の処理時間もあって15KHzモードで実装しています。
24KHzでも一度試してみたいですね。


X1turboのVBlankタイミングについて

X1turboの垂直同期信号について

ずっと気になっていたVBlank周りの時間を計測してみます。

f:id:x1turbo_agency:20200530205515p:plain
VBlankイメージ

X1turboは IOポート 1a01h PB7に垂直帰線期間信号(VBlank)が出ています。
これはVBlankに入ると Lowになり、VBlank外では Highになります。

これをポーリングしてVBlankを検出します。
これがどの位のタイミングで変化しているかが気になっていたのです。

CPUのクロック数計測

まずは、CPUのクロック数で計測を行います。
実際は計測以外のコードも入っているので、合計値は少なめな値です。
対象はエミュレータ(武田さんエミュ)と実機(X1turboZIII)で計測します。

計測結果

Emu/実機 VBlank 期間(msec) VBlank外 期間(msec) 合計(msec)
24KHz (Emu) 1.32 15.52 16.84
15KHz (Emu) 2.96 12.07 15.03
24KHz (実機) 1.37 15.51 16.88
15KHz (実機) 3.00 12.07 15.07
  • Emuと実機ではほぼ差がありませんでした。
  • Width40,Width80でも計測しましたが差はありませんでした。
  • 15KHz,24KHzでVBlank時間に倍近い差があります。

15KHz, 24KHzで意外にも差があるのが判りました。

であれば、60回x60秒=3600回の長いフレームを計測してみます。
60秒にどれだけ近いか、という事になりますね。

以下が結果です。

Emu/実機 実測時間(sec) 同期時間(msec)
24KHz(Emu) 1:05 18.05
15KHz(Emu) 0:58 16.11
24KHz(実機) 1:05 18.05
15KHz(実機) 0:58 16.11

24KHzにけっこう大きなずれがありますね。
レースゲームなどでタイム計測をVBlankで行うと思わぬずれが発生しそうです。

まとめ

この計測が意味するところは、X1turboのVBlankは60fps程度かと思っていたら、
15KHzは 約 62fps、24KHzは 約 55fpsという結果でした。

最近のLEDディスプレイでも、640x400表示が可能なものは、
水平周波数 24.8KHz 垂直周波数 56.3Hzだったので
これで、ちょっとすっきりしました。

今度は、X1 / X1turbo / X1turboZ では異なるかどうかが気になりますね。
(CRTCの値が同じなので変わらないハズですが)

X1turbo Remote Monitor v1.1.6リリース

X1turbo Remote Monitor v1.1.6をリリースしました。
詳しくはX1turbo Remote Monitorのページを見てください。

以前より要望のあった、2HD版の起動ディスク作成機能を追加しました。
セットアップタブから、X1 Monitor起動ディスク作成を選択すると、
2D/2HDの選択を行えるようにしました。

また、同梱イメージに、x1_mon_2d.d88, x1_mon_2hd.d88 の2つのファイルを追加しました。
他機種で起動ディスクを作成する場合、こちらのファイルを利用してください。

こちらのツールも少しづつ更新を行います。

f:id:x1turbo_agency:20190728233800p:plain
X1turbo Remote Monitor v116

Newグラディウス デモ (Balls / まとめ)

X1 SGLについて

今回、New グラディウスデモを制作する上で、X1用のゲームライブラリとしてX1SGLという環境を用意しました。
ソフトウェアスプライトやコンバータなど一通り揃えるのが目標です。

今回、X1SGLを使って描画性能用プログラムのまとめとして、X1 Balls を作りました。
Ballsのオリジナルは、PlayStationSDK、そして、ユーザ開発環境「ネットやろうぜ」サンプルとして登場します。
16x16のスプライトを画面にどれだけ表示できるか、というベンチマークです。

X1 Balls

PlayStation用BallsをX1/turboで!」

■ あのPlayStation用BallsをX1/turbo用に移植してみました。
オリジナルと比べて、どのくらいの性能が出るのか?その検証用です。
ちなみにオリジナルは2000枚程度表示(30fps)できていたと思います。

操作説明

キーボードで操作します。
1…Ball (プレーン1)を一つ発生させる。
2…Ball (プレーン2)を一つ発生させる。
3…Ball (プレーン3)を一つ発生させる。
4…Ballを一つ消去する。
スペースバー…押すたびに 60/30/20fps を切り替えます。

表示項目

現在表示しているBallの数
現在の表示FPS (60/30/20)
処理落ち (Dropout) フレーム内で描画処理をオーバーすると「Dropout」を表示します。
ちなみに60/30/20fpsでBallの移動速度を変えて見た目の速度が変わらないようにしています。

ダウンロード

X1用Ballsの実行ファイル(X1用)を置いておきます。
X1/turbo用なのでX1でも実行できると思います。
エミュレータではテストしましたが、実機ではテストしていません。

近々、ソースファイルも公開しようと思います。
2019/07/06 ソースファイルをgithubに公開しました。 github.com

X1 Balls (D88)

性能評価

プレーンの組合せで変わりますが、以下の性能でした。

FPSモード Ball 1プレーン Balls 2プレーン Ball 3プレーン
60 8 6 6
30 17 14 13
20 31 29 19

使用する環境(実機/エミュレータ),X1/turboでも数字が変動します。

まとめ

newグラディウスデモを通じて、リアルタイムゲームをX1turboで作ってみた印象です。

  • リアルタイム(60fps)にキャラクタを動かすのは厳しいですが、20-30fpsであればぼちぼち動かせる。
  • 1,2プレーン時の性能が想定以上に良い。
  • 高速化にはメモリを多く使うので、バンクメモリを使える事がX1turboにとって活用のポイントだと思います。もっと早い時期からメモリを128KB以上搭載できていれば…。
  • Z80DMAの使いこなしがポイントです。想像以上に使えました。ここはまだまだ活躍が見込めます。
  • ファミコンのスプライトは本当に強力。4色で上下左右反転が使える。高いメモリ効率と表現力。

以下は見果てぬ夢の話。

  • VSync割込みがあれば良かったのに!
  • せめて 16色中8色などの多色化があれば表現力は飛躍的に向上したので、X1turboの時点で多色化が欲しかった…。
  • 逆にアドベンチャゲームやRPGは相性が良かったはずですが、PC88/98からの移植でもメモリや色数に対応できないのが厳しかったでしょうね。
  • X1/turboのPCGは強力ですが、商売的にはPC88からの移植が多く、使いこなしが難しかった印象です。

色々と書いたり試したりしてますが、結局はX1turboの再確認にすぎないです。
市場規模を拡大できず、機能アップを盛り込むことができなかった X1turbo。
しかし、X1turboは触っているだけで楽しい機種(ここ大事!)なので、まだまだ楽しみたいと思います。

Newグラディウス デモ (PSGサウンド)

PSGサウンドと処理量について

今回はPSG音源について書いてみます。
PSGは古くから使われていますが、周期や処理量に興味があってそれについて書いてみます。

サウンドドライバの割込み周期

これはゲームごとに違うと思いますが、VBLANK割込みの関係からか 60fpsが多く、
X1版グラディウスは 16.0msecでした。

今回制作したPSGドライバの場合 1ch当りの処理は、音が鳴っている時は 0.7msec、
3ch再生している時は 2.1msecです。
16.0msec周期で処理を行うと 20fpsでの比率では 3倍の6.3msecになり、
かなり重い処理になってしまいます。

曲のテンポと周期について

例えばテンポが120の時は、4分音符の長さは0.5秒で
64分音符の場合だと、0.03125秒(31.25msec)です。

曲でどのくらいの分解能が必要かは、曲の速さで変わると思いますが、
もし倍の周期の 32.0msec割込みにすれば処理を 2/3に減らすことができます。
処理量を気にして、今回は 32.0msec周期割込みを使用しています。
問題は、それで表現力がどのくらい変わるかです。

BGM

BGMデータをチェックしてみると基本的な音の長さは 96msecだったので、32.0msec周期で十分間に合います。
今回はありませんでしたが、ソフトウェアエンベロープや、
ビブラート等の表現があると、周期が長いのが気になると思います。
耳が良い人だと気が付くと思います。

効果音

X1版原作では、効果音の周期は16.0msecだったので、32.0msec用にデータを似せて作っています。
調整を行ってみましたが、「効果音は32.0msecで表現するのはきびしい」というのが感想です。
以下は波形を比べてみたものです。

・オリジナル (16msec周期波形)

f:id:x1turbo_agency:20190525234536p:plain
オリジナル(16msec)波形

・NewグラディウスDEMO (32msec周期波形)

f:id:x1turbo_agency:20190525234612p:plain
32msec波形

効果音は時間当りの周波数切替が激しく、対応する周波数の切り替え周期が長いために、表現力が落ちてしまいます。
聞き比べてみると少々寂しくなっています。

効果音を調整していて、サウンドを作れる人はやっぱりスゴイと思いました。

まとめ

BGMはともかく効果音は32.0msecだと厳しいというのが判りました。
最近気が付いたんですが、割込み周期を16msecにして、効果音だけ16msec周期にすれば良かったですね。
FM音源であればエンベロープ処理などをチップで行えるので、処理自体を軽く出来るかもしれません。
この辺りは今後検証を行ってみます。

Newグラディウス デモ (BG描画/制作/フレームレート)

BGスクロール

X1turboでスムーズなBGスクロールが出来ないかを考えていたのが、グラディウス制作のきっかけでした。

GRAM描画やCRTC調整で実現出来ないかと検証を繰り返した結果、PCGで少しづつずらしたキャラクタを定義してスクリーンに描画する方法が処理も軽く見た目も効果的でした。

f:id:x1turbo_agency:20190516080730p:plain
PCG定義データ

現在は、GRAM BANK1にスクリーンを展開しDMAでTEXTに転送しています。
処理は 1~2msecほど要しています。

スクロール量は4ドット単位だとX1版と変わらないため、最低でも2ドットのスクロールを目標にしました。
実際にピクセルを置いて、繰り返しがあまり目立たない様に調整を行いました。

当初は単純で広い面積を取っている所をPCGで行い、複雑でワンポイントな部分を
GRAMとで使い分ける想定でしたが、現状はPCGだけで実現しています。
色味については、グラディウス2(FC) 火山面が非常に良く出来ていたので参考にしています。

f:id:x1turbo_agency:20190516081007p:plain
FC版グラディウス2(減色したもの)
(たまに色々なゲーム画面を8色で減色してX1版だったら…と妄想します)

2ドット単位スクロールにした事で、AC/FC版とはスクロール速度が変わりました。
できるだけBGスクロールを滑らかに見せるために、ステージ全体の長さや敵セット位置を調整しています。
実際には約1.5倍長くなっています。

フレームレート

今回のグラディウスデモは 20fpsで動作しています。
出来るだけなめらかな動作を狙っていたのですが、出てくるキャラクタの量、
描画量を計算すると20fpsが落としどころでした。
ちなみにX1版は15fpsでした。640x200でよく動作できていると思います。
キャラクタの移動量はAC/FC版の動画から算出し、なめらかな動きになるように少数値を調整しています。

Titledマップエディタ

マップデータ作成/編集は Tiled マップエディタを使用しています。

f:id:x1turbo_agency:20190516081410p:plain
Tiledマップエディタ

BGマップ作成

AC版の動画を元にBGマップを起こしています。
そこからキャラクタに量子化を行うコンバータで変換して、Titledエディタで編集できるようにしました。
スクロール速度を合わせてマップ全体の大きさを何度か作り直しました。

当り判定編集

地形当りデータも、Tiledの別レイヤーを使って設定しました。
出力データをビット単位の当り判定データに変換して使用しています。
ダッカーなど地形の当りを細かく判定する必要があるキャラクタは、一度当り判定を行った後、キャッシュとして当り判定インデックスを使って高速化を行っています。

敵セット編集

敵セットは titledのオブジェクト機能を使って配置しています。
配置データはエクスポートで出力されないので、.tmx(xml)ファイルを変換して敵セットデータとして利用しています。
BGM切り替えやボス制御もここで配置しています。

背景の星

星は1~4ドットの4重スクロールです。
画面消去後に星を書くので重ね合わせは特に行っていません。
全部で32箇所書き換えていますが、それほど処理がかかっていない割には効果的に出来ました。
ばらつきすぎず、重なり過ぎないように調整しています。

Newグラディウス デモ 描画 (パレット,座標系)

プレーン描画

色数は減ってしまいますが、高速化には1プレーン,2プレーンの描画が効果的です。
使用しているパレットの組み合わせは以下になっています。

f:id:x1turbo_agency:20190509010241p:plain
パレットセット

2プレーン描画では、以下の組み合わせ色が使用できます。

  • 青系統の敵(白/水色/青)の組み合わせ
  • パワーアップカプセルや、オプション、赤色系統の敵(白/黄色/赤)の組み合わせ
  • 通常敵にもよく使用するその中間(白/水色/赤)

f:id:x1turbo_agency:20190509010311p:plain
パレット組合せ

このバリエーションのために、白色を 2色割り当てています。

また、1プレーンでも、白色、赤色、水色の表現ができます。
弾やミサイル、レーザーなどは1プレーンで描画しています。
前回の記事でも書きましたが、同時アクセスモードで3プレーンを消去しているため、
2プレーン描画時に1プレーンがクリア済の場合が多いです。
もし書き込まれていた場合は、ビットライン機構によって重ね合わせが行われます。

プレーンの組み合わせと縦サイズ別に20種類程度の描画処理を分けています。

画像変換

画像の変換は自前のコンバートツールを使っています。
高速化のためにクリップ情報などを事前に計算しています。

コンバート例:

 ; データテーブル
 en1_p0_c2:
  db 0fch ; Pivot(x) -4
  db 0f8h ; Pivot(y) -8
  db 8 ; データ数
  dw en1_p0_c2_0 ; 0
  dw en1_p0_c2_1 ; 1
  dw en1_p0_c2_2 ; 2
  dw en1_p0_c2_3 ; 3
  dw en1_p0_c2_4 ; 4
  dw en1_p0_c2_5 ; 5
  dw en1_p0_c2_6 ; 6
  dw en1_p0_c2_7 ; 7
  
 en1_p0_c2_0:
 ; Xoffset: 0
 ; Mask: [G R]
 ; OutSizeX: 16 OutSizeY: 16
  
  db 183 ; clipy(byte) 200-sizey-1
  db 020h ; DrawType (Plane: RG SizeY: 16)
  db 39 ; clipx(right) 40-sizex+1
  db 63 ; clipx(left) 64-sizex+1
  db 2 ; sizex(byte)
  db 48 ; sizey(pitch)
  
 ; 0
  db 0ffh, 000h, 000h
  db 0ffh, 000h, 000h
  db 0feh, 001h, 000h
  db 0e8h, 017h, 000h
  db 0c8h, 037h, 001h
  db 0c8h, 037h, 000h
  db 085h, 07ah, 010h
  db 082h, 07ch, 011h
  db 0c2h, 03ch, 019h
  db 0c1h, 03eh, 00ch
  db 0e2h, 01dh, 000h
  db 0fch, 003h, 000h
  db 0f0h, 00fh, 002h
  db 0f8h, 007h, 001h
  db 0fch, 003h, 000h
  db 0ffh, 000h, 000h

ドット絵ツール Edge

画像データはドット絵ツールのEdgeを使用して 256色pngで作成しています。
パレットを扱えてドット絵を編集するにはとても便利ですが、ヌキ色や半透明には対応していません。
今回は特定のパレット番号以上はヌキ色になるようにしました。
画像にあるグレイ色をヌキ色として扱います。
Edgeはパレットが16色以下になると出力pngが16色になるため、ヌキ色を16色以上の場所に配置しています。

f:id:x1turbo_agency:20190509010944p:plain
ドット絵ツール Edge

座標系

画面サイズは320x200です。

320は8bitで収まらないため、通常は座標計算を16bitで行い、
計算用に固定小数点 8bitの 24bitで取り扱うことが多いですが少々重くなります。

Newグラディウスデモでは、高速化のため 9:7の座標系を採用しました。

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
XH XH XH XH XH XH XH XH XH XL XL XL XL XL XL XL

0-319の座標は、上位byteでは 0~159で表現されます。
9bit分なので1bitを下位バイトに持たせています。
この方法のメリットは以下の通りです。

  • 24bitではなく16bitの計算で収まる。
  • 表示座標を求める時は、1bit シフトして下位byteの最上位ビットと合わせて行います。
  • 固定小数点が7bit使える。(大抵は 4bitもあれば十分)
  • 移動スピードが2の倍数の時は上位byteのみ計算する。
  • キャラクタ同士やBGの当り判定は 8bitで計算を行って高速化できる。

処理計測方法

処理を計測する方法を書いておきます。
パレットによる計測を行っています。
パレットの0番を赤や青色などに設定して、少し待ち、また元の黒色に戻します。
こうすることで現在の走査線の位置に色を付けることができます。

処理を計測したい所を挟み、その2点間の差を計算すると処理時間を知る事ができます。
1ラインが 63.5μsecなので、もし10ライン差があった時は、0.635msec程度の
処理がかかっていることになります。

武田さんがリリースしているエミュレータは、この処理を行っても実機とほぼ同じ位置に
パレットが表示されるため、処理計測をエミュレータ上で確認することができます。
これはすごい再現性です。

f:id:x1turbo_agency:20190509074212p:plain
パレットによる処理計測

※ 上記の画像はかなり前のテストバージョンです。

Newグラディウス デモ 描画 (GRAM)

描画について

画面づくりの目標

今回の目標は「チラツキを出来るだけ見せずになめらかに動かす」です。
そのために、320x200のダブルバッファを使用しました。

このダブルバッファが使用できるのもX1の強みですね。
処理さえ間に合えば、スプライトと違って横に並んでもチラつく事もないし、
フレームバッファとして活用すれば色々な事ができそうです。

640x200の方がタイリングが細かくてキレイですが、今回はなめらかに動かすのが目的ですので採用していません。

描画の割り振りとして、GRAM=キャラクタ,PCG=地形にすることにしました。
X1のポテンシャルを発揮するには、広い面積を持つ地形部分をPCG、
移動するキャラクタをGRAMで実現するのがベストと考えました。

キャラクタをどうやって高速に描画するか?

性能評価

まず、8x8単位でGRAMを書き換えた時の性能を計測しました。

種類 処理時間 60fpsで描画可能な数
RGB単純書込み 156us 106個
RGBブレンド 316us 52個

RGB単純書込み…単純にGRAMへ書き込み。(Write)
RGBブレンド…一度ReadしてAND/ORして書込み (Read/Write)

書込みとブレンド処理の速度差は約 2倍です。
ブレンド処理が発生すると一気に重くなるため、単純書込みとブレンドを判定するシステムが
あれば高速化が可能と考えました。

ビットラインアルゴリズム

単純書込みとブレンドを判定するシステムとして、
画面上8x8の領域に対応して、1byteの描画情報を持つバッファ、ビットラインバッファを定義します。

f:id:x1turbo_agency:20190505155351p:plain
ビットラインアルゴリズム

1bitが横8bit、各プレーンの1byteに対応しています。
書換えを行う際にビットラインバッファのチェックを行って、
その領域に何も書かれていなければ、単純書込み(Write)、
何か書かれていれば、ブレンド処理(Read/Write)を行います。

これはライン単位で判定を行うので、最小限の箇所だけブレンドを行う事で高速化が可能です。
例えば図中の緑色と赤色の領域は重なっていないので、単純書込みになります。
この方式をビットラインアルゴリズムと名付けました。

ビットラインバッファ

ビットラインバッファはダブルバッファ分を合わせて 2KB。
このビットラインバッファ領域は次のように計算して求めます。

ビットラインバッファアドレス = アクセスGRAMアドレス or 0f800h

0f800hをorする事で、RGBプレーンやライン(0-7),ついでにダブルバッファも吸収して、
対応するバッファ位置を求めています。
そのため、0f800~0fbdfh (Page0), 0fc00h~0ffdfh(Page1) の固定アドレスになります。
X1のGRAM配置がリニアではない事を活用しています。

消去

一度書いた後は、消去する必要があります。
これは X1の同時書込みモードを使って、3プレーンを一回でクリアしています。
その場合でも、ビットラインバッファをチェックして、一度クリアした所は、
クリア処理を実行しないようにしています。

改善点

このアルゴリズムにはまだ改善点があります。
それは一度書いた所を消すコストが高い事です。
ほとんどのケースで、一度描いた所は再度描く事が多いので、わざわざ消すのは非効率です。

このビットラインアルゴリズムは、そのフレーム内で初めて描いたか、2度目以降かを判定していますが、
前回のフレームで描かれた場所かどうかを判定することができれば、より高速化できるはずです。
これを改善したアルゴリズムを現在考えています。