※開発中の様子
MSX-Cの確認をしようとしていたら…
先日のブログで「MSX-Cが出力するコードはZ80のみなのかな?」的な事を書いていました。どうやら調べてみると基本は8080コードが出力されるらしい。ただ、ライブラリはZ80コードが使われているようで、基本的なライブラリも含めて独自で書いた方が安全みたい。
で、自分でコードを確認をしようと(普段は棚に置いてある)MSXを設置し、動作確認をしようとしていたところ、フロッピーディスクドライブが大きな音を立てて壊れてしまいました…orz
う〜ん…今はMSXを修理する気力が無い。結果は後日に繰り越しとなりました。
とりあえず画面表示をしたいんだけど…
PC-8201で何を作るにしても、画面描画とキー入力が出来るようになれば、とりあえずは作り始められると思います。
レトロマシンは構造が単純ってのも好きです(^^)
目的に一直線で行ける感じがたまらないw
…なんて書きつつ、実はPC-8201の表示は、仕組みが複雑なんです。
基本は240x64ドットのグラフィック画面しか無い構造です。
大半のハンドヘルドマシンが似たような構造だと思う(PC-9801ノート等は除く)。
テキストとグラフィックが同じ画面上に共存します。
テキストがスクロールするとグラフィックもスクロールする…のではなくて、PC-8201ではテキストを再描画するためにグラフィックが消えてしまう。
テキストとグラフィックの合成は、気難しいテキストと付き合わなくてはならないです。
※画面にグラフィックで点を描いても…
※テキストを表示させるとグラフィックが消えちゃう。再描画しない部分だけ残る。
じゃあグラフィックの方が扱い易いのかと言えば、これがまた一筋縄ではいかない。
むしろテキストの方が簡単に表示出来る。
マシン語で1文字出力は、H:X座標、L:Y座標、C:ASCIIコードを入れて45E4hをコールすればおっけ。
グラフィックはそんな単純じゃ無い。
この矛盾をどう説明したら良いのか(^^;
PC-8201のVRAM構造
PC-8201の表示を複雑にしている原因は、VRAM(表示するためのメモリ)の場所にあります。
どこにあるかと言えば、LCDコントローラのチップ上に、10分割されて格納されてます。
画面は240x64ドット構成。
データは縦8ドット=1バイトという構成になっていて、画面全体で240x8バイト=1920バイトの容量。
この容量が、それぞれ10個のLCDコントローラに分割されており、該当するメモリをアクセスする事で絵が出せるようになっています。
まずこれだけでも面倒なのが分かってもらえるかも(^^;
LCDコントローラ
本体を分解してみると分かるのですが、液晶側に10個の大きめなチップが確認出来ます。
型番はHD44102CHと印刷されていますね。
このチップの中に200バイトの表示メモリがあります。
横に200バイトのデータではなくて、横50バイト、縦4バイト(32ドット)分。
4分割されていて、それぞれページという名前で分かれています。
そして1バイトの表現はこんな感じ。上がbit0、下がbit7。
なんとなく上と下が逆のイメージ(私の感覚では…ですがw)
縦8ドットが1バイト構成なので、PC-8201は横方向にはドット単位スクロールさせやすいです。逆に縦方向にドットスクロールは相当厳しい処理になるでしょう。
任意の場所に表示させるためには…
任意の点を点灯させようとした場合には…
・XY座標からLCDのブロック番号(1〜10)を計算
ブロック番号=X座標÷50+(Y座標÷32)×5
・ブロック内のページ数(0〜3)を計算
ページ=(Y座標&31)÷8
・ページ内のXオフセット(0〜49)を得る
オフセット=X%50
・そのアドレスにある1バイトデータを得る
・ANDしてOR(ドットを点灯)
・1バイトデータを書き込む
という手順が必要となります。
1つの点でコレ。
もう少し詳しく…
PC-8201は、10個あるLCDCのうち、同時にアクセスが出来るのは1つだけです。
この1つを8155(8255の書き間違いではない)を通じて選択します。
8155は、他にもプリンタやキー入力、クロックなども選択されます。
特にキーについては、割り込み処理プログラム中で8155を(勝手に)切り替えてしまうため、LCDCアクセス中は割り込み禁止が必要です。
対象のLCDCブロックが確定したら、次はページ数の指定、Xオフセットを指定してアクセス…という手順です。
LCDCへのデータ出力は、LCDCがReadyかBusyかを判定しながら出力しています。
ところで…LCDCのReady信号は「PC-8200 Technical Library」ではI/OポートF0hに出てると書いてあるのですが、N82-BASICではFEhポートを見ています。どっちが正しいのか分かりませんが、N82-BASICで行っている処理であれば間違いないだろう…と思い、私はFEhに出ているモノとしてプログラムしました。
メモリ中に仮想VRAMを持つ
ゲームなどを作ろうとした場合、上記のような複雑なアクセスをしていたのでは肝心のゲーム作りに集中が出来ないです(T_T)
出来れば画像処理などはメインメモリ中の連続した領域にアクセスを行い、最終的な結果をLCDCへ転送する仕組みを作るのが良さそうです。
私はこんな仕組みを作ってみました…というご紹介です。
メインメモリ中に、256x8バイト=2048バイトの領域を確保します。
横方向は240バイト分あれば済むはずですが、計算を楽ちんにするために256としています。
見えない16ドット分がある計算です。
横方向を256にするメリットは、マシン語を使ったことがある人であれば容易に想像出来るかと思います(^^)
メモリ的には2048バイトの連続した領域です。
ゲームなどのアプリケーションプログラムは、この2048バイトの範囲を好きに書き替える事でキャラクタを動かします。
そして一連の表示が終わった後、このデータをLCDCへ転送する仕組みを作りました。
ここにプログラムソースをだららっと載せるわけにも行きませんが、まぁ概念だけの説明と言うことですみません。
全画面書き換えが前提となっているため、画面のほんの一部のみしか書き替えられていない場合であっても全て転送されてしまう効率の悪さです。それでもこのくらいの書き替え速度が出ました。
動画撮ってみました。RUNしてからしばらく止まるのは、マシン語をメモリに書き込んでる時間。画面が動き出してからスタートで、全画面を50回描き変えてます。LCDCにデータを書き込んでから、次のデータを書き込むまで40サイクルくらい待たないといけないみたい。資料が少ないので試行錯誤です(TT) pic.twitter.com/RJ8DLX653N
— PocketGriffon (@GriffonPocket) 2020年4月13日
ちなみにマシン語を呼び出した時のスタック領域は、たったの16バイトしか用意されていません。 さすがに16バイトだとCALLしてPUSHして…ってやってるとすぐに無くなってしまいます。メモリ中の安心出来る領域(しかもそれなりの容量がある)にスタックを移してやる必要があります。
最初はスタック用のメモリを確保していたのですが、実は上記の仮想VRAM方式をとることで、スクリーンバッファ(FD00hからの320バイト)が空いている事に気づき、そこを利用するようにしました。プログラムを終了させてBASICに戻ってくると、スクリーンの一部がスタックとして使われたおかげで壊れるのですが「おお、頑張って仕事したな」と微笑ましく思いながらCLSしてます(^^;
という感じで…
特にプログラムソースは出さない流れで説明をしてみました。
このブログを観ている方で、どのくらいの方が興味あるのか分からないのですが、もしも「興味あるから見せて」って方がいましたら公開を検討しようかなーと思います。
また次回!