(裏)ゲームセンター荒らしforMSX2開発記

ゲームレジェンド30で展示した1chipMSXのミニゲーム作成の顛末です

MSXのプログラムをまともに組むものは初めて、
開発環境も厳しい、
MSXはとにかくスピードが遅い、
果たして無事完成するのだろうか

純粋なプログラミングな内容は第二章から
純粋なz88dkは第三章から
リリース後の高速化は第四章の予定です
お好きな表題を(クリックorタップ)して中身を読んでみてください

一章 思い立ち〜準備

ゲームレジェンドでは展示のみの参加もOK、それならいろいろ見せびらかしたい

三月ごろに新刊が完成。次の作業は…
スペースを目立たせたいと考えたて
  • 業務用の卓上ゲーム機
  • ダブレットで動画を流す
  • ゲーム機を置いてゲームのフリープレイ
  • 卓上ゲーム機は押し入れから引っ張り出して確認して完了
    タブレットは、趣味のwebページのありもの素材をつなげて完成

    ゲーム機は、当初はMSXに手持ちのゲームROMと緩く考えてしましたのですが
    三月ごろに早々と本も完成して余裕があり
    さらに家の者が「もう一冊本を作ろう」とけしかけてきたのです
    が、「ネタもないし新刊のネタでゲームを作る」と言い放つことになりました
    (プログラムは本業だしね)

    「電源無し」なので、小型でバッテリーで動きそうなマシン

    ゲームレジェンドはイベントの申し込み時に電源の使用の有り無し、を申請します
    ここで電源無しを選んでいたため、会場で電源コンセントの使用は期待できません

    バッテリーで動きそうな手持ちのゲーム機から選択したのが1chipMSX

    過去のゲーム機に比べれば最近に作られたものですしきっと消費電力も少ないでしょう
    当時購入して、ほとんど使わずにお蔵入りしていた機器の出番です

    1chipMSXには5VのACDCアダプターが付属しています。
    これの代わりにスマホバッテリーに接続できるケーブルを作成します
    早速秋葉原にいって同型のプラグのパーツを秋葉原で買ってきます。
    (USBの方の端子は使っていないケーブル等を切断して安く済ますつもり)

    自宅に戻ってUSBのケーブルをあさっていると……
    なんとUSB卓上扇風機の電源ケーブルが作ろうとしているケーブルと同じです。
    USBの電源に接続するとそのまま動作してしまいました。…プラグは買わなくても良かった。

    ジャンクで買って放置していたUSB電源のモニター

    1chipMSXだけではモニターディスプレイがありません。
    こちらはちょっと当てがありました。
    以前に秋葉原で購入していたOn-Lap 1303Hのジャンク品
    USBの電源で動作するフルHD解像度のLCDモニタです。
    HDMI入力コネクタの接触が悪いのです、自分で直そうとしていたのですが、うまくいかず
    修理を放り出してmicroHDMIのコネクタを引きはがしたまま放置していた物件がありました。

    これのアナログRGB入力が使えるのではないかと思い調べてみます
    MSXの映像信号の水平周波数15kHzの入力にも対応しているらしいのです
    アマゾンで対応アナログRGBケーブルを発注して、無事動作を確認。モニタを組み立てなおします。
    残念ながらアスペクト比の変更ができないのですがそこは我慢します。
    こちらもスマホバッテリーで動作させます(MSX本体より消費電力高そう)
    これでモニタも用意できました。

    甘い目論見

    さて本体とモニタが用意できたので、展示の中身です。
    画面を見て目を引いてくれればいい程度に思っていました
    最初は手持ちのROMカセットをデモしていればいいんじゃないか?と思っていたのですが…

    印刷所から本が届くと、家の者が「まだ時間があるからもう一冊作らないの?」と
    (おたく同人作家はこういう思考をするのか)
    そんなにネタはありません、それに俺プログラマーだし、やるならプログラムでしょ

    MSXはスプライトマシン。少ないキャラならBASICでも簡単なゲームが作れるんじゃないかな
    に関係したっぽいゲームを作れば相乗効果が期待
    と思ってしまったのです。

    二章 開発〜準備から

    MSXのプログラムは殆ど未経験

    MSX2との出会いは学生の頃のFS-A1、安い値段に惹かれてPC-98を持っていたにもかかわらず購入
    プログラムを組むつもりでテクニカルハンドブックも購入しました
    多色スプライトやVDPの転送コマンドにはものすごく期待したものでした。
  • VDPコマンドを使えばスプライトの代わりになるかも?とか
  • adjust機能と転送コマンドを使えば横スクロールもできるかも?とか
  • など期待したのですが
    VDPのコマンドはスプライトと肩を並べるほど速くもなく(当時はVSYNC至上主義者でもあったので)
    重ね多色スプライトは使いづらく
    それ以上MSX2プログラミングに興味を持つこともなくROMカートリッジでゲームするのみで
    それ以上プログラムを組むことはありませんでした

    Z80を本でかじっただけで8086に移行してしまった私にMSXのプログラムは組めるのだろうか

    やりたい事を実現できそうな仕様を絞り込む

    大体のMSX2の仕様は
    Z80CPU
  • スクリーンはVRAM128K、横256ドットで512色中16色、256色固定など
  • スプライトは16x16単色ライン毎が32枚、重ね合わせで多色表示
  • サウンドはPSGで矩形波三重音とノイズ
  • ジョイステイック使用可能
  • これを基に構築します

    ハードウエア仕様からの見積もり

  • 自キャラ + 敵×2、縦にスプライトを連結して16×32 + アイテム16×16 それぞれ二面重ね3色
  • フィールドはBG画面に描きこむ、背景の裏へのキャラの回り込みは無し
  • 自動ページ切り替え機能でパタパタアニメをさせよう
  • PSGでBGMとSEをならす
  • ゲームの基本ルール

  • 舞台はゲームセンター、テーブル筐体が綺麗にマス目に並んでいる
  • 自分、敵は、筐体周囲の通路を移動する
  • 主人公の"裏荒らし"は、アイテムを使ってテーブルゲーム機にクレジットを入れる
  • 敵の店長に捕まるとミス
  • 画面の雰囲気は「新入社員とおる君」的な?

  • 脳内で想定のゲームをプレイしている想像をしてみます。
    的確に追ってくる敵は狭いフィールド敵にすぐに追いつかれそうな気がします。
    (昔のBASICの投稿ゲームではよくある光景です)
    フィールドを迷路のように複雑にするべきだろうか、敵の行動にルールを設けるべきだろうか
    などともうすこし詰める必要がありそうです。

    blue MSX

    さすがに実機での開発は御免こうむりたいです(慣れたエディタを使いたいね)。MSXのエミュレータで開発できればと思います
    google検索でMSXのエミュレータを検索します。blue MSXというのがなかなかよさそうです
    FDイメージのマウントのほかに、WindowsのフォルダをMSXのFDDとしてマウントすることが出来るそうです
    クロス開発に打って付けです

    解説サイトを見ながら設定すると。無事おなじみのMSX-BASICの画面が出てきました
    早速簡単なプログラムを動かしてみたのですが、右側のSHITキーやCtrlキーが使えません。
    記号の入力の時などうっかり右のSHIFTキーを押してしまいます。注意が必要です

    スプライトエディタSST

    ゲームプログラムの要、画面を出力するところから始めます
    まずはMSX2の二面重ね3色スプライトのデータを作成します

    Windows上で動作するスプライトエディタが無いかといろいろ調べたのですがちょっと見つかりませんでした
    MSX2上で動作するSSTというスプライトエディタがありました
    このツールはBASICで出来ているそうです。こちらを使わせて頂きます。
    改造して機能追加したり、プログラムの参考にもさせて頂こうと思います

    blueMSX上でSSTを動かしてみます。カーソルキーで編集するようです
    マウスで大まかに当たりを取りたいので、マウスでも動作するように改造してみます
    すると機能追加の分、速度が遅くなったのでblueMSXでエミュレーションの速度を上げてもたつきを解消します
    すると今度は、キー入力が速くなりすぎます
    キー入力判定をエッジ判定に書き換えます

    マウスでドットを打ち、保存してみます
    (ちょっと改造したSST)
    するとなぜかファイルが保存されていません。
    blueMSXでのフォルダのマウント機能では安全のためファイルの書き込みは出来ないようになっているようです

    解決するためにAドライブにはデータ保存用のDSKイメージをマウント
    BドライブにはアスキーセーブしたSSTのベーシックファイル等をマウントします
    これでデータはAドライブに保存されます
    DSKイメージからファイルの取り出しはDisk-Managerを使用します

    BITMAPデータコンバータ BMP to MSX

    ゲームの背景は手抜きをして一枚絵の画像データをファイルから一度に読み込んで完了とします
    背景のタイルや筐体等を個別に描画すればデータ容量も少なくスピードも上がりそうです
    全画面分の画像データを簡単に作成できる今の時代ならではの手抜きかもしれません

    これをMSXのbloadで読み込める形式にするためにコンバーターが必要です
    BMP to MSXを使わさせて頂きます。
    windowsのBMPファイルからMSXで読み込める桁式に変換して減色処理も行ってくれる優れものです

    画像を綺麗に見せるにはSCREEN8の256色モードです
    しかしこのモードはカラーパレット機能が無く、スプライトも固定16色表示になってしまいます
    これではスプライトに好きな色が選べずに制限が出てしまいます
    なのでSCREN5のパレット付き16色モードでコンバートします。
    ここでも注意が必要です、MSX2のパレットは一組で背景とスプライトで共用しています。
    なので(先にスプライトで必要な色を決めている場合)スプライトで使っている色を使って背景データを作らなければなりません

    BMP to MSXの設定画面で、すべてのパレットを固定してコンバートします。

    (パレットデータはスプライトエディタのパレットデータを開きながら手動で設定していきました)

    スプライトが思ったように表示されない

    まずスクリーンモードを256×212×16色に切り替えます(SCREEN 5)
    スプライトも見栄えがするように16×16ドットの2倍モードにしておきます
    キャラも合わせて三体しかいませんし

    BASICで読み込んでみる

    bloadでコンバートした背景CGを読み込みます。パレットも反映させます
    SST付属のサンプルを参考にスプライトを読み込みます

    さて二面重ねスプライトの表示です(スプライトモード2)
    MSX2のスプライトモード2では 2面重ねて表示すると三色目の色を表示できる機能です
    MSX2のBASICではスプライトの命令にPUTSPRITE命令とCOLORSPRITE$命令を使用します。

    サンプルソースが見つからない

    さて、BASICのサンプルを得るためにいろいろ調べてみました
    手元にある月刊マイコンベーシックマガジンを調べたり
    google検索でMSX2の多色スプライトのBASICのソースを検索したり
    いろいろ調べてたのですが、なかなか多色スプライトを使ったBASICサンプルが見つかりません
    SSTのソースはSCREEN1.5の自前の機械語ルーチンを用意しているようでSCREEN5にはそのまま使えません

    自分で試行錯誤しているのですが思ったように表示できません。
    そのうち、某巨大掲示板に二面重ねしているBASICのソースを見つけました。
    これをもとに手直しして表示できるようになりました。COLORSPRITE$とPUTSPRITE命令の順番がカギのようです
    表示できるようになりました。歩きアニメのために定義パターンを変更するには毎回COLORSPRITE$が必要です
    またX座標がマイナスになったときには、BASICの制御とCOLORSPRITE$がかち合ってちらついてしまうようです

    これはBASICでの開発は無理だ

    さて、この時点でエミュレータの速度を3倍くらいにあげていました
    敵を二体表示させると、3倍速でも処理落ちするようになりました
    8bit機のBASICとはいえスプライトマシンなのにキャラを三体動かすこともできないのかとMSXの遅さに愕然です
    たしかに、1キャラにつき4個のスプライト表示や毎回のカラーテーブルを更新は重い処理だったのかもしれませんが……

    もっと処理速度が必要です。どうする

    z88dkを導入する。コマンドラインでコンパイル。

    MSXで使えるフリーのCコンパイラを探します
    MSXで使えるCコンパイラは結構あるようで比較解説しているサイトもあります。
    そのなかでz88dkを選びました。
    windows上でMSXの実行ファイルを作成できる開発環境です
    ANSI Cに対応していることと、インラインアセンブラが使えるところが決め手です

    詳しく解説されているサイトを参考に導入します。

    最近の開発環境は、統合環境が当たり前ですが、
    z88dkは別途用意したテキストエディタでソースを記述して保存
    コマンドラインからコンパイルする、という昔ながらの開発です

    MS-DOSの頃はMS-C + MIFESで同じように開発していたのを、思い出します。

    z88dkを導入する。MSXで実行できるファイルを作る方法

    z88dkはコマンドラインからzccとタイプして実行しますが、いくつかのコマンドラインオプションを指定する必要があります。
    +msxMSXをターゲットとする指定らしい
    -create-appアプリケーションを作成する指定らしい
    -subtype=msxdosMSX-DOS上で動作する指定
    -startup=2起動時の処理の指定
    -o<flename>出力ファイル名の指定
    -x<flename>ライブラリ作成の指定

    MSX上でMSX-DOSを起動できるようにしておきます
    DISKにMSXDOS.SYSとCOMMAND.COMが必要です(1chipMSXにはおまけでついてきます)
    エミュレータしかない場合はMSX MAGAZINE永久保存版等から入手できるそうです(現在プレミアがついていますが)

    無事実行ファイルが出来たら実行ファイルのあるフォルダをblueMSXのドライブとしてマウントします
    ファイルの内容はまうんとの時 私はAドライブにMSX-DOS、Bドライブに実行ファイル+データを置いています
    コンパイル等でファイルが更新されてもにblueMSXの方では内容は元のままで更新されません。
    再度マウント(ディスクトライブに対してディレクトリの挿入)が必要です、注意。

    三章 開発〜いろいろ問題発生

    まずはサンプルプログラムをコンパイルしてみる

    インストールしたz88dkにはexamplesフォルダがありサンプルソースがいくつか用意されています。
    その中からmsx用のサンプルをコンパイル実行してみます
    なぜかうまくコンパイルできないものもありますが、いくつかのサンプルはコンパイル、実行できました。
    これを参考にプログラムを組んでみます

    自分でソースを組んでみる

    テキストエディタでソースを作成して、コマンドラインでコンパイルします

    最近の開発環境はソース編集+コンパイル+デバッグが一つでできる統合環境が当たり前です
    なので、コードを書いていても、関数や構造体メンバの候補が出てこないので不安です
    (VSでソースを書いてz88dkのコンパイラを呼び出すことは可能かな?)
    (そういえば最近はコンパイルなんて言わずに実行ファイルを作成することをビルドって言いますね)

    まずは画面モードを切り替えてみます。

    動作しています。

    z88dkで、コンパイルとリンクを別々に行う方法がよくわかりません
    とりあえずライブラリ化して分割することにします

    VSYNC待ち出来るのか確認してみる

    スプライトマシンとはいえ、画面更新は垂直帰線期間行わないとティアリングが発生してしまいます。
    ゲームの速度も垂直同期で調整するつもりなので必須です
    エミュレーターではどのように動作するのか確認の意味も含めてテストしてみます
    垂直帰線信号をポーリングして、タイミングを取ります
    まだ画面を描き変える処理を作っていません
    そこで音を使ってタイミングをチェックします

    インラインアセンブラの練習として I/Oポートの入出力関数を作ってみます
    ネットでz88dkで検索するとインラインアセンブラで関数を記述する方法を解説してくださっているサイトが見つかります
    これを参考に作ります
    関数の引数は先頭から順にスタックに積まれる様です。引数をcharで宣言してもC言語の仕様でintに変換されスタックに積まれます

    1bit音声出力ポートのアクセス関数も作ります(無作法に直接ポートをアクセスしてます)

    実行するとなんとなく60Hzのブーという音が聞こえてきます。
    正しく動いてるのではと思います

    背景画像を読み込んでVRAMに転送する

    BASICではBLOAD命令でVRAMに転送することが出来ました。
    MSXのVRAMはCPUから直接扱えるメモリでありません。fread関数でVRAMに読み込むことは出来ません
    ファイルを一旦メインメモリに読み込んでからVDP経由でVRAMに転送します

    VPOKEは使えない?

    MSXのサンプルにvpoke関数がありました。BASICのVPOKE命令と同様の働きのようです
    vpokeを使ってファイルの背景データをVRAMに転送することにします

    実行するのですが、思った結果になりません

    スクリーンの中ほどから内容が書き換わっていますが、どう見ても背景データではありません
    スクリーンモードが切り替わっていないのかとも思いますが、そうでもないようです
    なにが悪いのか全く分かりません。
    msx.hを見るとmsx_vpoke, msx_vpoke_calleeとちょっと違う関数の定義があります、こちらに変えて実行してみます
    やはり結果は同じで、画像ば出てきません。中身の処理は同じようです

    …と、msx.hを調べていると、msx_vwriteなる関数の定義があります。連続したメモリをVRAMに転送する関数のようです
    こちらを使用してみると

    あっさりファイルの中身が出てきました(パレットを反映していないので色合いが異なっています)
    推測ですがz88dkのMSXサポートはMSX1に対応しているとのことです
    vpoke関数もMSX1の場合しかうまく動作しないのかもしれません(MSX2未対応のBIOSを使っているとか)

    スプライトエディタ

    SSTはスプライトの同時エディット数が15個です(実際のスプライト面から来る制限がある様です)
    もう少し定義する必要があるので、ほかのスプライトエディタを探しました
    SEPTというMSX-DOS上で動作するエディタが見つかりました。こちらを使わせていただくことにします
    こちらはマウス主体のエディタです。キーボードでの操作はカーソルキーでマウスカソールを移動させるタイプです

    スプライトの表示は更新するデータが少なくて済む…はず

    一般にスプライトというのは、一度表示パターンを定義すれば
    あとは表示位置情報の更新のみで画面を高速に動き回る
    というイメージを持っていました

    MSXの場合X,Y座標にそれぞれ1バイト, 定義パターン指定に1バイト、未使用1パイト
    4バイトの更新でスプライトを制御できるはず…と思っていました。

    意外と多い書き換え情報

    今回はキャラサイズを16×32ドットのパターンとして二面重ねの多色スプライトなので 2×2の4面のスプライトを使います
    なので表示位置情報は16バイト
    最大画面上には3キャラ+1アイテムで56バイトの転送。
    まぁこんなものでしょうか

    そうは問屋が卸さない

    アトリビュートテーブルから派生したカラーテーブル(ジェネレーターの拡張ではない)

    MSX2のスプライト(V-9938)は大きく三つのVRAM領域を使用しています
  • ジェネレーターテーブル; スプライト定義、ドットパターン(モノクロ)を格納します
  • アトリビュートテーブル; スプライト面のX,Y座標、定義パターンNo.の指定をします。ゲーム中に頻繁に更新されます
  • カラーテーブル; スプライト面の1ラインごとの色や重ね、X座標がマイナス時のずらし指定、その他の細かい指定します
  • カラーテーブルはMSX1のアトリビュートテーブルの一部が拡大独立したものと推測されます
    大雑把には今までスプライト一面単位だった指定部分をスプライトの水平1ラインことにできるように細分化したものです

  • 色指定
  • 重ね指定
  • 割り込み制御
  • 表示位置ずらし
  • 正直ずらし処理、重ね指定、割り込み指定等 水平1ライン毎にあっても持て余します
    スプライトがちょっと画面の左に隠れるたびにカラーテーブルを16バイト書き換え無ければなりません。
    今回は 1キャラ4スプライトですが64バイトにもなります

    例えば移動、攻撃等で表示パターンが変更になります。パターン定義のNo.を変えます。(これは1バイト)
    この時に凝った色使いをしているとラインごとの色指定が変わります
    ※今作だとプレイヤーキャラが振り向いたり、椅子に座ったりすると色使いが変わっています
    するとカラーテーブルも一緒に書き換えないといけません

    さらに表示順が入れ替わる場合もカラーテーブルの更新が必要になります
    今回の画面は斜め見下ろしです。画面上側が遠く、画面下側が近くなります。
    遠くのキャラは近くのキャラに隠れるように表示しなければいません。
    キャラの前後の位置関係が変わるたびに表示するスプライト面も変更になります
    そしてカラーテーブルも書き換えることになるのです

    せめてパレットテーブル的にカラーテーブルのテーブルNo.を指定する仕様にしてくれればよかったのに……

    ゲーム内容を進めて行きます

    裏ページを使ってパタパタアニメ―ョン

    画面がさみしいのでフィールド上のテーブル筐体の画面にゲーム画面を描こうと思います
    せっかくなのでMSX2の自動ページ切り替え機能で2コマのアニメーションをさせようと思います
    VRAM128Kの場合SCREEN5は4枚のページを持てます
    ゲーム起動時に0,1ページに二枚のフィールドの画像を読み込むようにします

    なぜか自動ページ切り替えができない

    VDPのレジスタに設定することで偶数奇数ページを自動で切り替える機能があります
    なぜかページが切り替わりません。まさかblueMSXは堂々切り替えに対応していないのでしょうか?
    時間もないので、プログラムから手動で切り替えることにします。
    VDPレジスタの設定で行いますが、こちらは問題なく切り替わりました

    次のページも使う

    「すべてのテーブル筐体にクレジットを入れる」がゲームの目的です
    すでにクレジットを入れた筐体の区別がわかりやすいようにします
    クレジットが入った、専用の画面を表示することにします
    アクションでクレジットが入ると、画面が変わってプレイヤーに判るようにします
    クレジットが入った筐体の画像を作成して、未使用のVRAMの画面ページに読み込みます。これを筐体の位置に転送し書き換えます

    VDPのVRAM間転送コマンド

    クレジットが入った画面は個別の筐体毎なのでページ切り替えでは不可能です
    VDPの転送コマンドを使用します、CPUと並列で動作するので高速化できます

    テクニカルハンドブックのアセンブラのサンプルを参考にインラインアランブラで記述してみます
    コンパイルすると、インラインアランブラの途中でエラーが発生します
    一見誤りが無いように見えるのですが、なぜかエラーが発生するのです
    何が悪いのか全く分からないので(開発環境が悪いんじゃないの?)
    仕様がないのでインラインアセンブラはあきらめてoutp関数で記述します

    全部のページを使ってしまおう

    あとページが一枚残っています。このページにタイトル画面を読み込んで切り替えて使うことにします
    クレジット画面の空きに筐体画面を賦存しておきます(一面クリア後にゲーム画面を復帰させるため)
  • ページ0,1:交互に切り替えて筐体画面の2コマアニメーション
  • ページ2:CREDITを入れた時のPUSH 1P画面(ページ0,1へ筐体の領域をVRAM間転送する)空き領域に筐体画面を退避
  • ページ3:タイトル画面
  • これで、一度画像データを読み込んだら再度読み込みは必要ありません

    簡易デバッグにも便利

    ミニゲームとはいえスコア表示は付けたいです。画面が引き締まる効果もあります
    当初は背景画面に書き込もうかと考えていたのですが、
    背景との重ね合わせ処理やキャラの奥に隠れる等、懸念点もあります
    そこで使っていないスプライト面を使用することにしました

    現在、一キャラにつき4面、最大3キャラ + アイテムで2面、合計14面使用しています。
    MSXのスプライト面は32面なので18面の余裕があります。定義にも余裕があります
    スプライトモード2は最大8面のスプライトを水平に並べられます。
    拡大モードのスプライトなら画面の横幅一杯に並べることが可能です

    8×8ドットの文字を想定しているのでスプライト 一定義に四文字含まれます
    一文字単位で自由に表示するために定義を直接書き換えることにします
    プログラム内にフォントデータを持ってCPUからVRAMに1文字単位で転送して表示します
    転送処理の負荷が気になりますが、毎回更新しなければ大丈夫でしょう、と軽い気持ち
    最初に左上にスプライトを3面並べて、表示したい文字をジェネレーターテーブルに転送します
    無事スコア表示が組み込まれました。

    スプライトは正方形の16×16ドットのパターンです、8×8ドットの文字だと縦横2文字の配置です
    ちなみにライン毎のずらし指定bitで上8ラインと下8ラインを32ドットずらすようにしてみました
    8文字限界まで文字を表示すればよかった…

    これで気になる変数を表示させながら実行するなど開発時に役に立つようにもなりました

    構造体の処理にバグがある?

    さて自キャラ、敵キャラの動きを作ります。

  • X,Y座標を通路の通路の間隔で割った余りが0の場合に、通路上にいるとして移動可能フラグをON
  • 角をスムーズに曲がれるように、フラグがOFFの時は補正移動させる
  • 通常は移動モード, 筐体の前でトリガが押されたら、アクションモードに遷移
  • 敵は通常は外周を巡回、外周四辺と向きで8モード持つ、移動完了すると次のモードへ遷移
  • 通路上で、ターゲット(プレイヤー)を見つける(座標が合う)と突撃モードへ遷移
  • 突撃モードで反対まで行くと、外周巡回モード、ターゲットに近くなる方向に遷移
  • もしくは反転して今来た道を戻る(ターゲットが通路上に戻る可能性)
  • 突撃以降の行動、移動スピードbitのフラグ情報で持ち、OFFならその行動はしない(難易度変更)
  • 今回はミニゲームで貧弱なCPUということもあり、ほどほど単純なルーチンにしました
    また、コーディングも処理速度が速くなるようにしたつもり
  • 決まったループは展開(どうせキャッシュもないし)
  • 構造体を渡す関数は作らない、処理はそのまま展開
  • 困ったらインラインアセンブラ
  • こんな感じで組んでいると、思ったように動きません
    調べていくと、おかしい原因が絞れてきました
    構造体のメンバの値を構造体の別のメンバに代入している式があります
    この結果が、期待した値になっていないようなのです


    コンパイラにバグがあるのでしょうか
    仕様がないので変数を介して値を代入することにします

    フリーのコンパイラなので仕方が無いのでしょうか
    時間もないので対処療法で先に進みます

    作曲しよう

    この時点で当日まで一週間を切っていました
    曲作成一日、サウンドドライバ一日の目安で作業します

    ローランドのPMA-5を引っ張りだしてゲームに合う感じの曲を作ります

    私は鼻歌でオリジナルのメロディが思いつく程度の素人なので簡単にはいきません
  • youtubeでPSGの曲を聴いてPSGの曲の雰囲気をつかむ
  • PSGの1チャンネルを想定してベースとコードとドラムを交互に鳴らすバッキングを作る
  • 残りの2チャンネルでデチューンをかけたメロディを鳴らす
  • コード進行は、"鉄板のコード進行"とかでネットで検索して当てはめる
  • コードに合わせてメロディを作る
  • …の予定でしたがメロディがうまく出てきません。どうする
    此処は奥の手「歌詞を作ってそれでメロディ作る」を実行します
    作詞:さらだかきもち
    俺はゲームに金は(金は)払わねぇ
    払う金などありゃしねぇ
    だけどゲームが俺のゆりかご
    地獄の底まで添い遂げる
    ゲームの主人公のテーマです:-) なんか適当ですが、これでメロディが浮かんでくればしめたものです
    ……なんかガチャガチャした曲になりました、時間もないのでこれを使います

    PSGのハードウエアエンベロープで音階出力にチャレンジ

    MSXのPSGでぜひやってみたいことがありました。
    AY-3-8910は音量のハードウエアエンベロープを一つ持っています。時間とともに音量を変化させる機能です
    エンベロープの時間の指定は16bitで範囲が結構広いのです
    繰り返すタイプのエンベロープを可聴周波数で発生させることが出来ます
    ※PSGのミキサーでチャンネルのトーンとノイズをOFFにすると純粋な波形が出力されます
    その音はPSGの出力する通常の波形と異なる音色となるのです
    ドライバの仕様として
  • データ形式はソース中に文字列で持つ簡易MMLデータ
  • 実行時に逐次MMLを解析しながら演奏する
  • メイン処理はメインループ中に一回呼び出す(処理落ちが無ければ1/60秒ごと)
  • HWエンベロープ指定で発音する機能を付ける
  • BYTEのポインタの示す値でswitchで分岐して"V"なら次の音量値を取得して更新、"CDE"なら現在のオクターブと長さでドレミを演奏、という感じのエンジンを作成します
    これを3チャンネル分繰り返して三重和音の再生です
    音階に対応するのPSG用のデータ等はネットで検索して用意しておきます

    三角波、ノコギリ波、鳴りました。通常の音階データを与えると数オクターブ低い音階でなっているようです
    なので高いオクターブは厳しくなります
    とりあえず鳴らすことは達成しました :-)

    波形を確認してみる

    blueMSXのアナログ出力の波形を見てみる
    オシロスコープを使って波形を確認してみます
    左から、通常の矩形波,三角波,鋸波

    一応それっぽい波形になっているようです
    PSGの音量出力が対数になっていることもあってか波形も曲線を描いて、本来の三角波、鋸はとは少し異なります
    ちなみに1chipMSXの出力はこんな感じです

    櫛の歯の様な波形が出ています(耳で聞く分には違いは感じられないんですけどね)
    1chipMAXはPWMでオーディオ出力を生成しているとどこかで見た記憶があります
    高周波成分がある為かオシロスコープもなかなか波形を読み取ってくれませんでした

    サウンド再生は処理落ちに敏感

    実際にゲーム中に鳴らすと、頻繁に処理落ちが発生して、テンポがつまづきます。
    たまの処理落ちも耳で感じ取れてしまいます、これは良くない
    とりあえずゲーム中はリズムトラックのみ鳴らすことにします

    さて、大詰め、イベントの二日前に迫ってきました
    8bitCPUのZ80は遅い、ということで細かく高速化してきました
    一番、高速化の実感がわいたのが

    構造体を辞めてバラの変数とする

    するとなんと、敵キャラ2体動作時でも処理落ちが無くなりました

    以前8086のアセンブラの本の解説に8086は高級言語コンパイラを使用する前提の設計で…
    と書かれていていまいち実感がわきませんでした
    今思えば8086でのBPによるスタック上の引数や自動変数のアクセスの利便性や、1命令でのbyte→wordの符号拡張
    Z80の機械語にやらせたらどれだけコストが掛かるのか、出力コードは怖く未だ見ていません:-)
    テストプレイしていると、また処理落ちが発生するようになりました

    あれ、敵一体でも遅い

    もう時間もないので30fpsを基準に調整しなおすかと考えます
    ・・・
    ぎりぎりまで細かく調査してみると、数回アクションする頃に処理落ちが始まる事を発見します
    しかも敵が一体でも

    原因はchar型の変数

    原因は、スコア用のスプライト定義の転送ルーチンが毎フレーム呼ばれてしまうことでした
    直線のスコアと今回のスコアを比較して異なっている場合に書き換えるという処理
    でしたが、直前のスコアの変数がchar型だったためスコアがある程度増えると、毎回書き換えることになっていたのでした
    高速化の足しになればと、変数をintからcharに書き換えてしまったのが原因です

    前日、ようやくリリース

    なんとか間に合いました

    四章 高速化と今後

    イベント当日、パグも取れ、無事展示が出来ました
    たくさんの人たちにプレイして頂けました
    「これ普通のMSXでも動作するのですか?」と質問を受け
    はたと気が付きました『1chipMSXの高速化スイッチをONにしたままだった』と
    案の定、うちに帰って通常のスピードでプレイしてみると結構処理落ちします
    これはなんとかしなければ

    処理落ちは常に発生しているのではなく、何かのタイミングで起こっている感じです
  • キャラが左端に入った瞬間、また左端から出た瞬間
  • キャラが画面縦方向に移動して、前後が入れ替わるとき
  • クレジット入れのアクションをしているタイミング
  • いずれもカラーテーブルを書き換えているタイミングです
    これを高速化することにします

    カラーテーブルのVRAM間転送

    現在は、1キャラに大してスプライト4面、カーテーブルCPUから64バイト程の転送です
    余っているVRAMにカラーテーブルを置いてVDPコマンドで転送すると速い
    とネットの記事にありました。これをやってみます
    スプライト定義64個分のカラーテーブルの定義をVRAMの空き領域に転送します
    VRAM領域を眺めると0x7000からが空いているのでここに配置します
    そしてもう一つ用意が必要です
    X座標がマイナスになった時には、ずらしビット(0x80)をONにする必要があります
    そこで中身が同じでずらしビットをONにしただけのカラーテーブルを用意して、別の空き領域に配置します
    スプライト面がX座マイナスがどうかで、転送するカラーテーブルを切り替えるようにします
    これだけのためにちょっともったいない気もしますが、高速化のためには必要です
    幸いVRAMも余っていますし

    通常VRAMへの転送はVRAMのアドレスを指定しますが、VDPのVRAM間転送コマンドは座標を指定します。
    VRAMを大きな一枚の画面とみなしてX,Yの座標で指定するのです

    そういえば、VDPコマンドの説明に、ビットマップ画面以外のモードでもコマンドが使用できるように思わせる指定があります
    これってMSX1のPCGな画面モード時に画面転送コマンドが使用できるという事なのでしょうかね。スクロールとか早くなるかも。ちょっと興味があります

    カラーテーブルをVRAMに載せると結構早くなった気がします
    スプライト面のプライオリティ入れ替え時や等の引っ掛かりも気にならなくなりました
    ですが、主人公のアクション時にまだ引っ掛かります。
    どうやらスコア表示のパターン転送(CPU→VRAM)が足を引っ張っているようです
    6桁の数値のパターンを一度に転送していたのがよくないようです
    これを1フレームに1桁ごと転送をばらしてみます
    スコア表示を凝視していると、文字の書き換わりが波打つのが見えますが、さほど問題ないでしょう
    (書き換え中にスクリーンショットを撮るとちょっとおかしなスコア表示になりますが…)
    (一度パターンをクリアするとか、書き換え中はスコアを非表示するとか対策が考えられます)

    さて、まだ一瞬、処理落ちします。
    SEが鳴っているときに処理落ちしやすいようです
    サウンドルーチンの高速化、MMLをコンバート済みにする等を考えましたが、 その前に一つ試してみたいことを思いつきました

    オート変数をスタティック変数に変更してみる

    読んで字のごとく、あらかじめ決まったアドレスに変数が確保されます
    対して、関数内に普通にに変数を宣言するとオート変数になります
    オート変数はスタック領域に動的に確保されます
    変数へのアクセスはSPからの相対アドレスになってしまいアドレスの計算利用が増えてしまいます
    (あらためて8086のBPやSPの相対アドレシングやRET命令でのスタックの解放は高級言語用と納得します)
    				int count = 0;
    			
    となっているところを
    				static int count;
    				count = 0;
    			
    とするわけです。
    このプログラムはマルチスレッドも割り込みも使用していません
    なので関数が二重に呼び出されることもありません。特に気にすることはありません
    毎回値が初期化されるように、値の代入を宣言の後に書いておくことくらいでしょうか

    目に見えて効果が出た

    メインループ内の変数を静的変数にしてみました。
    するとどうしたことでしょう、フレーム落ちがほぼなくなりました。
    予定していたMMLを事前展開は見送ることにしました :-)

    アドレス固定の変数……、なんだかハンドアセンブルの機械語のプログラム見たいです。

    MSX2でプログラムを組んでみて感じたことはズバリ「実行速度遅い」事「VDPの仕様厄介なこと」でした
    アーケードゲーム並みの60fpsを目指そうとすると、BASICはおろかCコンパイラ(z88dk)でも厳しいものでした
    これ以上処理を複雑にしたい場合は素直にアセンブラで記述する必要がありそうです
    VDPの仕様も面倒でした、過去のMSX2のゲームであまり多色スプライトを使用したものがなかったのにもうなずけます



    戻る