投稿日 : 2018/04/09 3:57:02
PC-G850のゲームをWindows上で開発②+PSG音源も搭載してみる
前にWindows上でポケコン用のゲームを開発する方法を記事にしましたが、今回はこの開発環境をさらにレベルアップしてみました。

なお、開発自体は以前と同様SDCCを使ったC言語によるプログラミングとなりますが、今回は描画ライブラリを刷新してさらに高速化したり、自作のPSG音源をミニI/O(11pin)側に接続してゲーム中に曲を鳴らしたりといったことが出来るようになりました。

ちなみに、描画処理は昔はオールC言語だったため速度にとても難がありましたが、前回の記事でLCDの全制御の方法が分かったため、これを踏まえアセンブラで描画エンジンを作り直したところ、前のライブラリとは比べものにならないレベルで速度が上がっており、アクションゲームなどもそれなりに作れるくらいのライブラリになりました。

しかし、機能をいろいろ入れすぎたためにライブラリだけでかなりのメモリを食うようになってしまったため、今度はゲーム自体の容量を如何に少なくするかという方が重要になってしまいました。
ここまでいろいろ出来るようになるとやはりメモリが欲しくなりますが、これがポケコンの限界でありその時代の限界でもあった、ということなんだと今更ながら思いました。

ふと考えたのが物理的にRAMを増設することは不可能なので、代わりに最近流行りのI2C接続のフラッシュメモリを使って画像などのデータを入れて置き、シーンの切り替え時にRAM上に転送して使うというようなことをすれば、ファイルのような感じで間接的に扱える容量を増やすことが出来るのではと思っています。
普通のゲームでもローディングがあることを考えても、転送のため少しくらい待たされても良いような気がするのは、今のゲームやOSに慣れすぎたためかもねw

I2Cプロトコル自体は速度の上限が決まっているだけで低速なら問題なく動作するはずなので、自前でこの信号線を制御すれば恐らくポケコン上でも問題無く動くはず。
ただ、そこまでしてゲームをやりたい人がどれほどいるのかは謎だし、そもそもまずはそのようなハードを作らないと遊べないわけなので、それならスマホ用のゲームを作って広告費でもいいから稼いだほうが断然いいのではと思われます。

ということで、これは構想止まりということで暇な人はやってみてくださいw
(実はArduino用にこのフラッシュメモリを1個だけ買ってあるので、実験することは出来なくはないww)

PSG音源

PSGとはProgrammable Sound Generatorの略で、例えば指定した周波数をセットするとその周期の短形波を出力したり、またその波形に対して音量変化(エンベロープ)を適用することで音色を変えたり、さらにノイズ発生器を制御することでドラムっぽい音を出させたりといったことが可能なサウンド生成器です。

このPSG音源は昔のMSXからPC88、PC98といったPCや、ファミコンやゲームギアなどゲーム機に音源として搭載されており、PCの場合は後期ではFM音源と一緒にPSG音源も含んだものもありました。(YM2203とかYM2608とか)

なお、現在のPCに入っている音源はPCM音源と言い、波形を直接指定して鳴らすことが出来るようになったため、実質どのような音も再現できるようにはなりましたが、そういう意味では音を生成しているのはあくまでも波形データであるため、実際には音源という表現は間違いではないかと思っています。

ちなみにPCM音源は、波形データをリアルタイムに音源に流し込むことで音を出しますが、これにはそれなりにCPUやメモリなどのスペックが必要で、昔のPCにはそこまでスペックが無かったため、仕方なくPC側ではなくIC側で音を生成する必要がありました。
そのため、出せる音はというと音源ICが持つ音色だけということになり、またこの音自体がかなり機械っぽい音だったため、このような音をピコピコ音と言っています。


実はPSG音源というICはまだ市販されています。
しかも秋月で売っているためすぐに手に入りますが、今回はこの音源をポケコンから制御することにします。

YAHAMA製YMZ294

ポケコン用のPSG音源について

ここではこの音源をポケコンで鳴らすためのハードウェアの作り方を紹介します。
なお、この音源のサンプルプログラムは新しいビルドツールに同梱されているため、作成したハードウェアを即座に試すことが出来ます。


ところでいきなりですが、実はこの音源チップはポケコンから直接制御することが出来ません。

何故かというとポケコンのミニI/O(11pin側)は8bitしか扱えないためで、音源にはデータの送信に使用する8bit端子の他に、リセット信号やチップ選択などの信号線が必要だからです。
これらを少なく見積もっても最低10bitの信号が必要になるのですが、今回はこの問題を解決するためにシフトレジスタというICを使用します。

8bitシフトレジスタの74HC595


シフトレジスタとは単純に言えばシリアルパラレル変換ICで、これを使うと最低3bitあれば8bitのデータを表現することが出来ます。

原理としてはクロック信号に合わせて1bitずつ状態与えることで、それまでに入力していたbit状態が内部でどんどんシフトされていき、最後にラッチ信号を与えることでその時のbitに合わせて一気にICの出力ピンが出力されます。
なお、次のデータもまた同じように1bitずつ与えていきますが、ラッチ信号を与えるまでは出力ピンの状態は変わらないため、接続されているIC側は誤作動を起こす心配もありません。

このICの欠点と言えば、1byteのデータを表現するのに最低8回のbit入力と最後のラッチ入力が必要となるため、CPUからのI/O出力に処理がかなり食われます。
通常は1回の出力で済むものが、全てのビットを出力するために8回も出力を繰り返す必要があるため、単純に8倍処理が増えていることになります。
※実際には計算などそれ以外の処理もあるため、もっと多くのCPUリソースが食われていることになります

ちなみに今回使用する74HC595は1個で8bit分の出力を行うことが出来ますが、これを直列に複数接続することで16bitや24bit、32bit以上のデータ出力を行わせることも出来ます。
ただし、今回ポケコンから制御するのに必要なビット数は、リセット等も含めて7bitで済むのと、ICが増えると配線も大変になるのでここでは1個だけ使うことにしました。


回路図
以下が今回制作したPSG音源ボードの回路図です。

※クリックで拡大


完成写真

オシレータについて

実は以前秋月で売っていた音源には、以下のような4MHzのオシレータが同梱されていました。


しかし、現在はオシレーターは同梱されておらず音源チップのみの販売となっており、このため別途4MHzのオシレーターを用意しなければなりませんが、この4MHzのオシレーターは秋月では取り扱いがありません。
仕方が無いので他のショップも探してみたのですが、何故かどのショップにも取り扱いがほとんどなく、入手がとても困難なものであることが分かりました。

このままではこのオシレーターを使った回路を考えても誰も作ることが出来ないと思ったので、何か別な方法が無いかいろいろ試行錯誤したところ、結果として今回使用したオシレータにたどり着きました。

多出力クリスタルオシレータ EXO3 12MHz


さてこのオシレータ、よく見ると12MHzと書かれています。
その通りこのオシレーターは12MHzのオシレーターではありますが、実はこれには分周機能が付いており、設定によりこのクロックの半分の半分の・・・といった感じで、最大8回まで分周したクロックを生成することが出来ます。

さらに運よく、このオシレータは秋月でも買えることが分かったので、今回はこれを使うことに決めました。
※アキバの店舗の場合は普通の棚には置かれていないようで、店員に聞かないとダメみたい


この12MHzのオシレータですが、まずは1回分周(1/2)すると6MHzになり、さらにもう1回分周すると(1/4)になり3MHzとなります。
しかしよく考えると付属していたオシレータは4MHzであり、これだとクロックが合わず使えないのではないかと思うかもしれませんが、音源の仕様書をよく読むと、実はこの音源は4MHzか6MHzのどちらかが選択できるようになっています。

これは音源チップの8番ピンにて設定することが可能となっており、このピンにVCCを与えると4MHz、GNDに接続すると6MHzで動作するようになっています。

ちなみに音源の仕様書がかなり汚く、読んでいると6MHzではなく8MHzの表記が混在しているように見えますが、正解は6MHzとなります。
一応確認のためこちらで売っている6MHz秋月でも売っている8MHzのオシレータも買ってテストしてみたのですが、6MHzのオシレーターでは正しい周波数、8MHzでは1.333倍の周波数で再生されたため、8を1.333で割ると6、つまり6MHzで間違いありません。
なお、今回この6MHzのオシレータを使わなかった理由として、単価が結構高く別途送料を含めると合計で1000円近くになってしまうので、なるべく安くかつ秋月のみで揃えられるパーツにしてみました。


さて、今回使用するこの多出力クリスタルオシレータはベースクロックは12MHzですが、3つのピンによりこの周波数を指定の回数だけ分周させることが出来るようになっています。
分周とは周波数を半分の半分の半分の…という感じでどんどん割った周波数を作ることを言うのですが、このオシレータの場合は8段階で最高1/256の周波数を生成することが出来るようになっています。

以下はデータシートから抜き出した設定ですが、今回は12MHzから6MHzとするためには1回だけ分周すれば良いため、この場合は3つのピン(ABC端子、実際には567番ピン)を全てLOW、つまりGNDに繋げてしまえばOKです。


実は秋月にはベースクロックが24MHzのオシレータも売っていますが、その場合は設定を1/4にすることで6MHzを作ることが出来ます。
ちなみに秋月には売っていませんが製品としては16MHzのものもあるようで、このオシレータを1/4に分周すれば4MHzで駆動させることも出来ます。
もしこれらのオシレータを既に持っているならば、回路を若干変更するだけで対応が可能です。

PSG音源のパーツ一覧

以下は全て秋月で入手可能です。
番号 部品名 型番 使用数 価格 補足
1 片面ガラス・ユニバーサル基板 Cタイプ(72x47.5mm) めっき仕上げ 1 60 回路はピン数違いのこちらも使えるように設計しています
※手持ちがある場合など
2 ヤマハ音源IC YMZ294 1 300
3 オシレータ 多出力クリスタルオシレータ EXO3 12MHz 1 200 8PのICソケット付き
4 8ビットシフトレジスタ SN74HC595N 1 40
5 低電圧1.2WオーディオアンプIC HT82V739 2.2~5.5V動作 (2個入) 1 100 2個入りだが1個だけ使用
6 ICソケット ( 8P) (10個入) 1 100 10個入りだが1個だけ使用/アンプ用
(オシレータは付属のものを使用)
8 ICソケット (16P) (10個入) 1 100 10個入りだが1個だけ使用/シフトレジスタ用
9 ICソケット (18P) (10個入) 1 100 10個入りだが1個だけ使用/音源用
10 ステレオミニジャック 3.5mmステレオミニジャックDIP化キット 1 150 売り切れの際は単品販売のものを組み合わせると良い
 ・基板取付用3.5mmステレオミニジャック
 ・3.5mmステレオミニジャックDIP化基板
 ・細ピンヘッダ 1×7
  (値段が一緒なら細ピンヘッダ 1×14の方が良い)
11 半固定ボリューム 10kΩ [103] 1 50 同じ10KΩだがこちらはピンの配置が異なるためNG
12 電解コンデンサ 1μF50V85℃ (ルビコンPK) 2 10
13 電解コンデンサ 470μF16V105℃(ルビコンWXA) 1 10
14 カーボン抵抗(炭素皮膜抵抗) 1/6W 100Ω (100本入) 1 100 100本入りだが1本だけ使用/使用するLEDに合わせる
15 超高輝度5mmピンク色LED OSK54K5111A(10個入) 1 250 10個入りだが1個だけ使用/自分の好きなLEDでOK
16 ピンヘッダ (オスL型) 1×40 (40P) 1 50
※これ以外に配線用のスズメッキ線やジャンパ線(部品の足で代用可)、はんだ等が必要です

PSG音源の制作

音源やシフトレジスタ、アンプICとオシレーターは直接基板にはんだ付けせず、ICソケットを使用してください。
これで他のものを作りたい時にパーツを流用したり、はんだごてでICを破損してしまう心配が無くなります。

部品面(表)

※基板違いはこちら


配線面(裏)

※基板違いはこちら


はんだ付けが終わったらIC等はまだ取り付けず、ポケコンに接続してLEDが光ることを確認してください。
LEDが光ったならICなどを取り付け、以下のポケコンビルドツールに同梱されているPSGのサンプルを実行させ、実際に曲が流れるか試してみましょう。

なお、ポケコンの端子からは常に電圧が出ているため、ポケコンの電源を切ってもLEDは光ったままとなります。
この状態はIC等にも電力が供給されていることになるため、使用しないときは必ずこのボードを外すようにしてください。

出力は一応ステレオジャックですが、この回路で使用しているアンプは1.2Wほど出るため、イヤフォンで聞く場合は音量の上げすぎに注意しないと耳が死にます。
通常はここにはイヤフォンではなく、アンプの付いていない100円ショップ等で買えるようなしょぼいスピーカーを繋いでください。
※コンデンサで直流成分をカットしているため、一応ライン入力などで録音も可能ではあります(インピーダンスとかはよくわかりませんw)

ポケコンビルドツール v2

ポケコンビルドツールv2とは、Windows上でポケコンのプログラムをC言語で作るために必要なソフトをまとめたものです。
なお、このビルドツールを使用するにはSDCCのインストールが必要なため、まだインストールをしていない場合は以前の記事を参考に先にインストールを行ってください。

ポケコンのビルドツールは以下からダウンロード出来ます。
ソフト名 内容
ポケコンビルドツール v2.00 開発に必要なソフト
改変版エミュレータやポケコン用ライブラリ、ビルド用バッチなどと、
 プロジェクトに必要なワークデータ一式が入っています

今回のビルドツールにはg800エミュレーターの改変版が同梱されており、SDCCのインストールさえ終わっていれば他に用意するものは何もありません。
※実機のROMが無くても動作はしますが、どうしても実機のROMを使用したい場合は以前の記事を参考にしてください

ビルドツールに含まれる各ライブラリは以前のものと互換性がほとんどありません。
そのため、以前のビルドツールにて作成したプログラムは移植作業が必要となります。

g800エミュレータの改変版について

同梱のエミュレータは、元のエミュレーターに対して以下のような修正が行われています。
  • VC2010でビルド出来るように関数などの定義を修正
  • 未実装のLCDコマンドのサポート(リードモディファイライトなど)
  • ブザー再生処理を修正(途切れ途切れになる問題を改善)
  • LCDの改行のタイミングで発生する1S割込みのエミュレーションに対応
  • ミニI/O(11pin)の入出力に繋がれたハードウェアを外部DLLによりエミュレート可
  • ブザー音やDLLからのフィードバック音をWAVファイルに録音可能(設定ファイルに追加)
同梱のエミュレーターのソースコードは以下からダウンロード可能です。
g800(改) VC++ Build v1.00のソース ダウンロード
※SDL2.0.7同梱
g800(改)用ミニI/O音源エミュDLLのソース ダウンロード
※実機と比べて1オクターブ高く再生されます
※全てVC2010用のプロジェクトになっています


元のエミュレーターのソースコードはこちらにあります。

g800エミュレーターのライセンス表記
---------------------------------------------------------------------------------------
Copyright (c) 2005 ~ 2014 maruhiro
All rights reserved. 

Redistribution and use in source and binary forms, 
with or without modification, are permitted provided that 
the following conditions are met: 

 1. Redistributions of source code must retain the above copyright notice, 
    this list of conditions and the following disclaimer. 

 2. Redistributions in binary form must reproduce the above copyright notice, 
    this list of conditions and the following disclaimer in the documentation 
    and/or other materials provided with the distribution. 

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------------------

PSG音源をWindows上で再現するために、以下のライブラリを使用しています。
-------------------------------------------------
  FM Sound Generator with OPN/OPM interface
  Copyright (C) by cisc 1998, 2003.
  http://retropc.net/cisc/m88/
-------------------------------------------------
※この中のPSG音源部のソースのみ利用しています

ビルドツールの内容

この内容は以前のものと基本的に同じではありますが、テンプレートやツール内のソフトウェアが変更されているため、ここで改めてファイルの構成について説明します。

ダウンロードしたビルドツールには、開発に必要なツールやサンプルコードがまとまっており、まずはこれを任意の場所に解凍してください。

解凍すると「開発用テンプレート(PSG音源対応)」と「開発用テンプレート(シンプル)」と言う2つのフォルダが作成され、それぞれ中に必要な全てのツールなどがまとまっています。

この「開発用テンプレート(****)」フォルダが実質1アプリ分のプロジェクトフォルダということになるので、あとあと作成するゲームの名前などに自由に変更してください。
※ここではテンプレートの中に各ツールが入っているため、プロジェクトが増えると同じツールが複数作成されることになりますが、ツール自体の容量はさほど大きくは無いので問題無いと思います

なお、これらの2つのフォルダは作成したいゲームに合わせて使用する方を選択します。
通常のゲームならば「開発用テンプレート(シンプル)」の方を使用しますが、上記のPSG音源を用いたゲームを作成する場合は「開発用テンプレート(PSG音源対応)」の方を使用してください。
※PSG音源対応版はビルドバッチやエミュレーターの設定がシンプル版と若干異なります

これらのテンプレートにはあらかじめサンプルコードが記述されているため、即座に実行して試すことが可能です。
※同梱のエミュレーターで実行したり、実機に転送して実行することが出来ます


以下は「開発用テンプレート(****)」内に入っているファイルの詳細です。
g800win32/ 改変版g800エミュレータが入っているフォルダです。
作成したゲームを即座に直接このエミュレータで実行することが出来ます。
※改変前の元のエミュレータを使用したい場合はこちらのフォルダに入れてください
 (改変前のエミュレーターでは今回のサンプルプログラムは正しく動作しません)
data/ サンプルの画像や曲データが入っています。
各データはtoolsフォルダ内の各ソフトで変換されたものです。
tools/ 転送ソフトのPCGToolやフォント変換用のAsciiConv2などの各種ツールが入っています。
※各ツールの詳細は同梱のreadme.txtを確認してください
work/ ビルド時にこの中にワーク用のファイルが生成されます。
この中に出来るファイルは必要なければ削除しても問題ありません。
※ビルド時に生成される「_app.ihx」がゲーム本体となるため、これを公開することで他の人にも遊んでもらうことが出来ます
!!!build.bat 作成したプログラムをビルド(コンパイル&リンク)します。
エラーがあるとプロンプト上に内容が表示されるので、これを確認してコードの修正を行います。
ビルド後に何かキーを押すことでエミュレータが起動するので、直接作成したプログラムを実行することが出来ます。
※途中でエラーが発生した場合でもバッチ自体は最後まで実行されてしまうため、毎回きちんとエラーを確認してください
!!!run.bat 作成済みのゲームを再度実行することが出来ます。
前回正しくビルドが成功していた場合にのみ使用してください。
!!!send.bat 作成したゲームを実機(ポケコン本体)に転送します。
単にビルド後に生成された「_app.ihx」を転送するだけのバッチなので、必ずビルドが成功した場合にのみ実行してください。
※作成したゲームを実機に転送する際は、最初に一度だけPCGToolを起動して通信設定を行ってください
ascii.h 自作のg800ライブラリの文字表示関数が使用している文字フォントバイナリヘッダです。
自分でフォントを作成・更新したい場合は、「tools/AsciiConv2.html」を確認してください。
crt0.s ゲームを実行するための基本処理が書かれたアセンブラファイルです。
グローバル変数の初期化やCでは実行が遅くなってしまう処理などが定義されています。
ちなみにこのアセンブラのコードから最終的にCのmain()関数を呼び出しています。
※このコードはアセンブルするとアドレス0100hに配置されるため、ゲームの起動は必ずこのアドレスをコールする形となります
g800.c 文字列や画像表示を行うための自作ライブラリのCのソースコードです。
一部の関数はさらに内部でcrt0.sの関数を呼び出すことで、Cだと処理がかかる部分をラッパーしています。
g800.h 文字や画像表示を行う関数のプロトタイプなどが宣言されています。
このヘッダをインクルードしたCファイル中からこれらの関数を使うことが出来ます。
game.c ゲームのメイン処理を記述するためのソースコードファイルです。
基本的には全てのゲーム処理はこのファイルの中で定義してください。
game.h ゲーム関数のプロトタイプです。
このヘッダファイルは基本的にmain.cがインクルードするために用意されたもので、
game.cのみが使う関数は通常はgame.cの中でプロトタイプ宣言をしますが、
プロトタイプ宣言は必ずヘッダファイルに入れるというようなポリシーがあるなら、
game.hに定義してもかまいません。
main.c crt0.sから最初に呼び出されるmain()関数が実装されています。
本来は自分でmain()関数から書くのが基本ですが、ここではテンプレートということで
既にgame.hの関数を呼び出すコードが記述されているため、基本的にこのファイルを変更する必要はありません。
platform.h SDCCでWin32用の定義を間接的に使用するための定義ファイルです。
例えばSDCCではcharはunsigned char(符号なし)となりますが、Win32ではsigned char(符号あり)となっており、
これらの違いを吸収するためにCHARという型が定義されています。
MMLPlayer.c PSG音源使用時のMML解析エンジンのソースコードです。
MMLPlayer.h PSG音源使用時のMML解析エンジンのヘッダファイルです。
※使い方はPSG対応テンプレートのサンプルコードを確認してください
ymz294.c ミニI/Oに接続したPSG音源を制御するためのI/Oライブラリのソースコードです。
ymz294.h ミニI/Oに接続したPSG音源を制御するためのI/Oライブラリのヘッダファイルです。
※使い方はPSG対応テンプレートのサンプルコードを確認してください

ゲームの作成は基本的に「game.c」のみを修正する形となるので、基本的にそれ以外のファイルはあまり気にする必要はありません。

テンプレート(シンプル)

シンプル版のテンプレートに含まれるソースは、フルスクリーンサイズの画像を表示するだけのプログラムです。
終了するにはBREAK(ON)キーを押します

ビルド方法は「!!!build.bat」を実行するだけです。

実行するとコマンドプロンプトが起動し、ビルド中のメッセージが表示されます。

※初回起動時に削除に失敗と表示されることがありますが、これは以前のワークファイルを削除する際に
 そのワークファイルが存在しないため表示されているだけです

最後に「Start emulator ?」と表示されたら、エンターキーなどを押すことでエミュレータが起動します。
なおビルド中にエラーが出ていたり、ビルド後にすぐに実機に転送したい場合は、ここで「×」を押して閉じてしまってかまいません。


エミュレーターが起動すると即座に0x100番地から実行が開始されます。
※今まではマシン語モニタからG100を実行していましたが、今回のバージョンではその手間が無くなりました

テンプレート(PSG音源対応)

PSG音源対応版のテンプレートに含まれるソースでは、PSG音源にて曲を実際に再生しつつ、フルサイズの画像を上下キーを使って全画面スクロールすることが出来ます。
また、終了するにはBREAK(ON)キーを押します

ビルド方法はシンプル版と同様に「!!!build.bat」を実行するだけです。
最後に「Start emulator ?」と表示されたら、エンターキーなどを押すことでエミュレータが起動します。
※こちらのエミュレーターはPSG音源をエミュレートするように設定済みです


ちなみにキャラクターはスティールちゃんと言って、本家のポケコンページで公開中のゲームのオリジナルキャラのフル画像バージョンですw
メモリが足りなくて泣く泣くボツになりましたが、ここにきて復活しましたwww

※PSGライブラリの詳細については以下参照

実機で実行

エミュレータで動作が確認出来たなら、実機に転送して実際に実行させてみましょう。

なお、転送を行うには先にポケコン側と通信ソフト側の通信設定を、合わせておく必要があります。

ポケコン側の設定方法は、まず「TEXT」を押してテキストエディタに入り「S」を押してSioのメニューを表示させます。
次にSioのメニューから「F」を押して通信設定画面を表示させます。
ここでは主にボーレート(baud rate)とフロー制御(flow)の設定を確認します。
※通常はボーレート9600bps、フローをRS/CSにします

PC側はまずは「tools/PCGTool.exe」を単体で起動します。
 
この中で「設定」内のCOMポートを通信ケーブルが使用しているポートに、またボーレートとフロー制御はポケコンと同じ設定にしてください。
設定が終わったら「×」ボタンを押して閉じてください。
※この設定は転送を行う前に必ず1回だけ行ってください

次に実機の方でマシン語エリアが正しく確保されているか確認します。
RUN MODEで「MON」と入力してマシン語モニタに入り、マシン語エリアとして使用する領域を確保するために「USER7600」と入力します。
※ちなみに「USER」とだけ入力すると現在確保されているエリアの範囲が表示されます
※ここでは確保可能な最大値を指定していますが、最終的には使用する分のメモリだけ確保すればOKです


上記の設定が全て終わっているならあとはポケコンを受信状態にし、転送用のバッチを実行するだけです。

まずポケコンをマシン語モニタ画面にし「R」と入力して受信待機状態とします。
次に「!!!send.bat」を実行します。
通信が完了すればポケコンにデータが転送されたので、あとはポケコン上で実行するだけです。
マシン語モニタ上であれば「G100」、RUN MODEであれば「CALL256」または「CA.256」と入力すれば実行出来ます。

なお、通信で送信されるプログラム本体は「work/_app.ihx」となるので、ファイル名を変えて公開することで他の人にもプレイしてもらうことが出来ます。

g800ライブラリ v2

g800.hにはゲームで利用可能な文字や画像表示などの関数が定義されています。
これを使うことでBASICと比べて高速に動作するゲームを簡単に作ることが出来ます。

以前のライブラリで使えていた関数のうち、システム関数はアンダーバーを2つ付けることで切り分けを行いました。
また画像表示ライブラリは、汎用化のため仮想VRAMと画像との違いを無くしました。

以前の仮想VRAMとは、最終的に画面に表示されるドット情報を保持したメモリであり、これには画像を書き込むことしか出来ませんでしたが、今回のライブラリでは逆に仮想VRAMを画像としても使用することが出来ます。
さらに仮想VRAMはプログラム上で任意のサイズでいくつでも作成出来るため、つまり3D的に言えばレンダーテクスチャが使えるようになったと言えます。
なお、仮想VRAMという表現はすでにおかしいため、ここからは画像バッファという表現にします。

ちなみに画像やこの画像バッファは、画面に直接表示させることも出来るため、画像を単に表示するような普通のプログラムも作ることが出来ます。
この場合たくさんの画像を表示すると、クリアと描画のタイミングによってはチラつきが発生してしまうため、気になるなら仮想VRAMのような作り方も考慮してください。
※原因は前回の記事を参照

今までの仮想VRAMのような感じでフルサイズで画像バッファを生成し、これにゲーム画面を作って最後に画面に表示させことで、以前のライブラリと同じようなことが可能です。
しかも、画面への表示はアセンブラにより高速化されているため、以前のライブラリより劇的に速度が出るようになっています。

このような改変により、新しいライブラリではCPUやメモリの使用量をプログラム側でもっと細かく制御出来るようになっています。


以下はg800ライブラリv2の関数の詳細です。
関数名 内容
BYTE __inp( BYTE port ) IOから指定のポートを直接読み取ります。
void __outp( BYTE port,BYTE data ) IOの指定のポートに直接値を出力します。
void __shutdown( void ) ポケコンの電源を切ります。
※まともに動作するかは未検証
BYTE __peek( WORD addr ) 指定のアドレスのメモリを直接読み取ります。
※セーブデータのロードなどに利用可能
void __poke( WORD addr,BYTE data ) 指定のアドレスに直接値を書き込みます。
※セーブデータのセーブなどに利用可能
void __di( void ) 割り込みを不可に設定します。
アセンブラのDIのラッパーです。
void __ei( void ) 割り込みを許可に設定します。
アセンブラのEIのラッパーです。
void __memset( void *buf,BYTE c,WORD len ) memsetの高速版。
void __memcpy( void *dst,void *src,WORD len ) memcpyの高速版。
void __puts( CHAR x, CHAR y,const CHAR *str,WORD len ) 表示したい文字数を指定して文字列をBIOS(IOCS)を使って描画します。
注意点としてx,yは文字単位の座標です。
※直接画面に反映されます
void Beep( WORD tone,WORD wait ) ポケコンの11pin I/O端子の3と7pinに繋げたスピーカーからビープ音を出します。
toneの値(音階)はこちらにまとめています。
※waitは音階によって調整が必要です(BASICのBEEP命令とは異なります)
void Clear( BYTE pattern ) 画面を指定のパターンで塗りつぶします。
パターンはGPRINTと同じ縦8ドット分を1Byteで定義します。
0x00なら白、0xFFなら黒になります。
void GetAllKey( LPKEYDATA data ) 用意したKEYDATA構造体の実体を渡すことでこの中に現在のキー情報を返します。
この関数は一度で全てのキーの状態を取得するため、キーの同時判定も可能です。
プログラム中でキーが押されているか判定するにはIsKey()マクロを使用してください。
例)
 KEYDATA kd;
 GetAllKey( &kd );
 if( IsKey(&kd,VK_A) ) {
   // Aキーが押されている場合の処理
 }
 ※この関数は処理が重いため通常は1フレームに1回呼び出すようにし、
  取得したKEYDATAを使いまわすような作り方にして下さい
void SetHookInterrupt( LPHOOKPROC addr ) フック関数をインストールします。
すでに登録済みなら何もしません。
電源入れ直しで再度セットが可能です。
ハンドラの型は「BOOL Handler( void );」です。
ハンドラの戻り値がTRUEなら、元の割り込みアドレスは呼び出されません。
void SetLCDInterruputTiming( int val ) フック関数が呼び出される頻度を変更します。
割込みはLCDの反転駆動タイミングを利用しています。
割込み頻度は0x31(早い)~0x3F(遅い)で指定し、
それ以外の値の場合はLCDの更新が停止します(割込みが極端に遅くなる)。

PC-G850Vでの参考値
val 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F
頻度(ms) 33 48 64 80 96 112 128 144 160 176 192 208 224 240 256
void ClearLCDInterruptFlag( void ) フック関数内で呼び出すことでLCDの割り込みフラグをクリアします。
これを呼ばないと次の割り込みが正しく実行されません。
void PInit( LPIMGDATA img,BYTE w,BYTE rows ) 幅と行数を指定して画像バッファの情報を設定します。
これはあらかじめ確保しておいた画像バッファのヘッダを初期化するもので、描画関数はこれを参照して描画を最適化します。
※この関数では実際の画像バッファメモリはクリアされません

画像バッファの構築方法と実際の使用方法については以下を参照。
// 画像バッファ定義
struct BUFFER {
    IMGDATA     head;               // 画像データとして扱うための画像ヘッダ
    BYTE        mVram[6][144];      // 実際の画像バッファ(行×幅) ※1次元配列のmVram[144*6]と等価
};

BUFFER mVram;                       // 実体化

PInit( (LPIMGDATA)&mVram,144,6 );   // BUFFERのポインタをIMGDATA型のポインタにキャストして渡す
LPIMGDATAIMGDATA*としてtypedefされています。

裏技として画像バッファの最大メモリを超えない範囲であれば、再度この関数でサイズを変更することも出来ます。
これにより、1つの画像バッファを複数の用途に使用することも可能です。
void PClear( LPIMGDATA img,BYTE pat ) 画像バッファを指定のパターンビットでクリアします。
パターンはClear()と同じ仕様になります。
void PBitBlt( LPIMGDATA dst,BYTE mode,SHORT x,SHORT y,LPIMGDATA src ) 入力画像バッファ(src)を出力画像バッファ(dst)の指定の位置(x,y)に転送します。
範囲外は描画されないため、特定のサイズで画像バッファを作ることで、疑似的にマスク表示のようなことも出来ます。
仕様として出力先を画像にすることも出来ますが、その画像はポケコン上ではRAM領域なので元の画像は破壊されます。

転送モードはDMODE定数として定義されています。
定数 内容
DMODE_NORMAL 画像に合わせて自動的に最適な表示を行う。
※抜きの有無
DMODE_NOMASK マスク画像が存在する場合はそれを無視して常に背景とORで結合する。
DMODE_COPY 画像をそのまま高速転送する。
ただし、yが8の単位でない場合はその行の空いた領域は白くなる。
画面をクリアしつつ全画面表示したい場合に使用する。
DMODE_MIRROR 画像を左右反転して描画する。
これは上記3つと組み合わせることが可能。
※反転機能はPBitBlt()とPPrintf()関数でのみ有効
void PPrintf( LPIMGDATA dst,BYTE mode,SHORT x,SHORT y,const char *s,... ) 出力画像バッファに文字列を描画します。
フォントはascii.hが使用されます。
ミラーモードでは全体が反転するのではなく1文字ずつ反転します。
void SetOffset( BYTE y ) LCDの縦のスクロール量を指定します(0~63)。
LCDに直接スクロールコマンドを送るため、よくわからない場合は使用しないでください。
void DrawImage( BYTE mode,SHORT x,SHORT y,const LPIMGDATA src ) LCDにIMGDATA画像を直接描画します。
画像や画像バッファを指定することが出来ます。
大量に描画を行うとLCDのチラつきが目立ちます。
void DrawString( BYTE mode,SHORT x,SHORT y,const char *s,... ) LCDに文字列を直接描画します。
これはPPrintf()のラッパー定義であり、出力先がNULLの場合は直接LCDに描画されるようになっています。
void SInit( void ) サウンドライブラリの初期化を行います。
サウンドの再生を行う前に必ず1回は呼び出す必要があります。
※サウンド系のライブラリを使用する場合はg800.hのUSE_M_FUNCTION定数を1にしなければなりません
void SStart( LPBYTE data ) MIDIから変換した曲データのポインタを指定して再生を開始します。
void SStop( void ) 曲を停止します。
BOOL SIsPlaying( void ) 現在曲が再生中かを返します。
WORD SGetCount( void ) 再生中の内部カウント値を返します。
SIntrruptPlaying()を呼び出すごとに1カウント増えます。
void SIntrruptPlaying( void ) 割り込み関数の中で毎回呼び出すようにすることで、曲を同一テンポで鳴らすことが出来ます。

ポケコンの割込みついて

割込みとは
割込みとは簡単に言うと、CPUに接続された割込み用のピンがONになることで、メイン処理を中断させて特定のアドレスへ強制的にジャンプさせることが出来る機能です。

このアドレスには割込み時の処理を書いておくことで、常にその処理を優先して行わせることが出来ます。
ただし、割込み中は当然メインは完全に停止してしまうことになるため、この中では通常は簡単な処理しか書かないようにします。

ポケコンのCPUにはZ80が入っていますが、このZ80には割込みモードが3つ用意されているようで、その中で実際にポケコンが使えるのはモード1となります。
モード1とは割り込みがあると常にアドレス0038hにジャンプしますが、実はポケコンの電源を入れなおすとこのアドレスのコードが毎回デフォルト値に書き直されます。
ちなみにこのデフォルト値はIOCSのとある番地にジャンプするコードになっており、つまり割込みがあると何らかのシステム処理が実行されるようになっています。

割込み関数について
割込みが発生する要因にはいくつかありますが、ポケコンはあるタイミングで1Sという割込みが発生します。
最初1秒間隔で動くものなのかと思いましたが、どうやらこれはLCDの行反転駆動のタイミングで発生するもので、LCDに対してあるコマンドを送ることでそのタイミングを変更することが出来ることが分かりました。

このコマンドについての詳細は前回の記事にありますが、この行反転駆動のタイミングは常に一定の間隔で入ってくるため、ポケコンの速度が遅かろうが速かろうが関係なく、常に同じタイミングで処理を実行することが出来ます。
※PC-G850無印とVやVSでは若干LCDの割込み速度が異なるため、一定のタイミングというのは変わらないが、その間隔が早かったり遅かったりします

これを行うための関数として、g800ライブラリv2にはSetHookInterrupt()、SetLCDInterruputTiming()、ClearLCDInterruptFlag()の3つの関数を用意しました。
実はすでに本家で公開中のポケコン版charatbeatでもこの割込みを使っていますが、今回はもっと分かりやすくするため関数として再定義しました。

使い方としてはまず割込み中の処理を実行する関数を作り、これをSetHookInterrupt()関数を使ってフック先として登録します。
次に割込み速度をSetLCDInterruputTiming()関数にて設定します。
最後に割込み関数内の終わりでClearLCDInterruptFlag()を呼び出すだけで、一定のタイミングで処理を行わせることが出来るようになります。

なお、気を付けなければならないこととして、メイン処理と割込み関数内でグローバル変数を共通で使っている場合、SDCCの最適化機能により思った通りの処理にならないことがあります。
例えば割込みが入るごとにメインを1回処理したいといった場合、以下のようなプログラムを組むと正しく動作しないことがあります。
int old_count = 0;
int now_count = 0;

BOOL HookProc()
{
    now_count++;

    ClearLCDInterruptFlag();    // 次の割り込みが来るようにする
    return TRUE;
}

void EnterFrame()
{
    // なんかの処理
        :

    // 割込みが発生するまで待つ
    while( old_count==now_count ) {
        // 同じカウントならまだ割込みは発生していないのでループして待つ
    }

    // 新しいカウントを記録
    old_count = now_count;

    // 抜けた
}

これはwhile内の処理が何もないために、コンパイラがその変数は変化しないと誤判定してしまうため、結果while(true);として解釈してしまうためです。

これを避けるにはメインと割込み内で共通で使用している変数に対してvolatileを付けます。
volatile int old_count = 0;
volatile int now_count = 0;
こうすることでこれらの変数に対する最適化は行われなくなり、while( old_count==now_count )をそのままコンパイルするようになります。

ちなみにMicrosoftのVisualC++ではこのような処理でも正しくコンパイルしてくれますが、組み込み系のコンパイラではよくこのような問題が発生するようです。

割込み禁止処理
実は割込み中に再度割込み用のピンがONになると、再度割込みが発生してしまいます。

このため通常は割込み処理中は次の割込みが発生しないよう、CPUに対して割込み禁止としておき、割込み処理が終わる際に割込みを許可してから抜けますが、実は上記のSetHookInterrupt()では、割込み時に直接割込み関数が呼ばれるわけではなく、いったん割込み禁止などの処理を行ってから割込み関数が呼ばれるようになっています。

そのため、割込み関数内でもう一度割込みが発生してしまうといった問題は起こらないようになっていますが、もしこの中で__ei()を呼び出してしまうと割込みが許可されてしまい、再度割込みが発生してしまうことになるため、特に理由が無い限りこの割込み内では__ei()は呼び出さないでください。

割込みによるPSG音源の制御
今回はこの割込みを使ってPSG音源を制御することで、ゲーム処理が重くなったとしても常に同じタイミングで曲を流すことが出来るようになりました。
ちなみに割込みでサウンドデバイスを制御するのは昔からの技術ではありますが、これをポケコンでも出来たというのが一番大きいところかと思います。

PSG音源自体はI/Oの制御だけなので、実はさほどCPUを使用しません。

ただ、今回のPSG音源はシリアルパラレル変換のチップを使用しているため、1コマンドあたりI/Oを20回ほど叩いています。
本来は2回の出力のみで済むのが最大で16倍の時間がかかるため、CPUを若干無駄に浪費しているように見えますが、それでも時間に換算すれば8μ秒ほどであるため、これがネックになることはあまりないかと思われます。
※むしろMMLの解析処理の方が時間がかかる

ymz294、MMLPlayerライブラリ

テンプレート(PSG音源対応)に同梱されているymz294とMMLPlayerライブラリは、2つ組み合わせることでPSG音源を制御しています。
なお、これらの使い方はこのテンプレートのソースコード(game.c)を確認してください。

ymz294ライブラリ
作成したPSG音源を低レベルで制御するためのライブラリで、実際にはシフトレジスタICと音源ICの各入力ビットを制御します。
また、ポケコンのミニI/Oを出力設定にする機能があるため、プログラムの最初に必ず初期化を実行しておく必要があります。
関数名 内容
BOOL YMZInit( void ) ポケコンのミニI/Oのピンを全て出力に設定して音源をリセットします。
void YMZExit( void ) 音源をリセットしてミニI/Oを元のモードに戻します。
void YMZSetReg( BYTE reg,BYTE value ) 音源にコマンドまたはデータを送信します。
※コマンドはデータシート参照
void YMZReset( void ) 音源のリセット端子ビットを操作することで音源をリセットします。

MMLPlayerライブラリ
その名の通りMML文字列をリアルタイムに解析してPSG音源にコマンドを送ります。
この解析は通常は割込み内で行うことで、ゲームの処理落ちに関係なく常に一定の速度で曲を流すことが可能となります。
関数名 内容
void MPInit( LPMMLPLAYER mp,CHAR *mml ) MML文字列を指定してMMLPLAYER構造体を初期化します。
MMLPlayerは指定されたMML文字列を直接解析しているため、
再生中に改変したり消去したりしてはなりません。
BOOL MPPlay( LPMMLPLAYER mp ) 曲を最低単位の1チック進めます。
通常は割込み内で呼びだすことで、一定の速度でチックが進み、
音源から音を出力させたり停止させたりが行われます。
演奏中はTRUE、終了かエラー時はFALSEを返します。
※この関数はテンポという概念は無いため、呼び出す速度や回数は上位で行う必要があります
BOOL MPIsPlay( LPMMLPLAYER mp ) 曲が再生中ならTRUE、終了していたならFALSEを返します。
ループ曲の場合は再生が終わらないため、常にTRUEを返します。
BYTE MPGetBpm( LPMMLPLAYER mp ) チックを進めて行った時の現在のテンポ値を返します。
このテンポ値からMPPlay()関数の呼び出しタイミングを調整することで、
実時間に合わせた再生を行うことが出来ます。
※実際に音を出したり停止したりというのは常に割込みのタイミングとなるため、
 そのタイミングによっては曲が一定の速度で進まないため、
 狂っているように聞こえることがあります

コメントはまだ登録されていません。
コメントする
名前
コメント
※タグは使用出来ません
この記事に関連するタグ
ポケコン(PC-G850)