MSXにはいろんな形で本当に本当にお世話になった身として、
MSX30周年に何か記事が書けないか?
と考えたところ、
現在最後の拙プログラムであり拙ゲームである
『Mistral Blue』の技術話(…というほどではありませんが)でも
書こうかと思います。
--------
・『Mistral Blue』そのものの印象について
最初に語っておきますと、
時間がないから仕上げはこだわれないけど
このアイデアは絶対イケてるから形にしたい!!
って出発点でのゲームは過去にいくつか作り、
結果手ごたえも感じていて、
ゆえに今回は逆のアプローチで、
例えばゲームバランスやボリュームに演出はもちろんながら
特に内部のプログラムにこだわりたい、という意識が大きく、
『Mistral Blue』はそういう部分から出発した企画でして、
そして丁寧に作るんだから
いつもより面白いゲームになるはず、
…という意識が強くありました。
が、現実はなかなかうまくいかず、
プログラム的アプローチでのアイデアは十二分であったものの
ゲームとしての表現のアイデア量が足りず
ゲーム自体(ゲーム性、物語部分共に)がスッカラカンになり、
これはマズいと自覚しつつも
どうにもできないまま完成してしまった作品でした。
・dpiの概念を知らなかった当時に
mspaint.exeの96dpiで作ったパッケージ。
そういえばWindowsXPのmspaint.exeのdpiって
どうやって変更できたんでしょう?
インストールされている本体によって
設定がまちまちだったのが不思議でした。
--------
・今回お話するのは起動時のチェック部分。
MSXはいろんなメーカーから
いろんな仕様の機種が出た割に
市販ゲームですらバージョンチェックは
ほとんどされていないのが現状でした。
なので動かないMSXでは
きっちりエラーメッセージを出して
動作を停止させてみたいな、と。
--------
・『Mistral Blue』の最低システム要件は
MSX2
VRAM128K
3.5インチ2DDドライブ
日本語MSX-DOS2
マッパメモリ192KB
MSX標準第一水準漢字ROM
というとても微妙なものでした。
現実的に言うとパナソニックのMSX2、FS-A1Fに
RAM付(容量問わず)のDOS2カートリッジを刺せば動作したはずです。
なぜこんな要件かと言いますと
本来MSXturboR専用だったのが
開発メンバーにMSX本体を持ってない人がいて、
その人がプレイできる可能性を少しでも上げるべく、
エミュレータでも動くようにしたい。
が、当時はBlueMSXがまだ世に出ていなくて
MSXturboRのエミュレートはまだまだという状況で
結果としてこのような最低要件になった次第。
後に『Mistral Blue』は『MSX MAGAZINE 永久保存版3』に収録され、
tubroR対応のMSXPLAYer上で動作し、
Windowsユーザーにも最良の環境でプレイできるようになりました。
--------
・動作チェックの話に進みますと、
3.5インチ2DDドライブは
メディアが入らないと話にならないので
有無チェックはしていません。
1DDドライブに入れて起動を試みた時の
挙動は気になりましたが
確認できる環境がなかったので
どんな動作になるかは不明です。
多分Disk I/O errorなんだと思います。
MSX-DISK-SYSTEMの必要RAM容量は最低16KBで
当初は16KB機でも読み込めるように
チェッカー部分だけ独立して作っていたのですが
今ソースを確認してみると開発終盤に
タイトル処理と合体させたようで
RAM16KBのMSXではOut of memoryで
停止する可能性があります。
また動作外であるRAM8K(MSX規格上の最小容量)のMSXでも
雀の涙ほどのワークエリアながら
DISK-BASICが起動するとの話を聞いていましたが
さすがにそれの対策はしませんでした。
またMSXのディスクドライブは
消費するワークエリアがピンキリで
一番ワークエリアを食うドライブが判明していて
それを入手できる環境があれば
それで動作を確認したかったものです。
--------
・ここから具体的な
ソースリストの解説に入ります。
チェック自体はプログラムにとって
都合のいいチェック順がありつつも
ここはあえて低いスペックのMSXから
きちんと順番に調べる方法をとりました。
これがまためんどくさかった。
・MSX-BASICは1行にマルチステートメントで詰め込み、
かつ命令間のスペースを省略する事でメモリが節約できたので
完成したソースはとんでもなく読みにくくなります。
今回の引用はスペースを追加した文を掲載します。
# 当時、条件式で "X OR Y" みたいな式をスペース詰めて書いたら
# XORって解釈になって気づくまで大変だったのも懐かしい思い出。
・またMSX特有の半角かなは全角かなに
ついでに半角カナも全角カナに変更します。
--------
10 ' SAVE "AUTOEXEC.BAS"
・最初の行は保存するファイル名をコメントにしておいて
保存するときに最初の行だけLISTで出して
頭数文字を消してリターンすれば保存できるよ、
という当時のTips。
みんなこれやってるけど発祥どこよ?って思ってたら
パナソニックのBASIC解説書でびっくりしました。
・AUTOEXEC.BASでの自動起動は
BASICの初期画面が表示されて
市販ゲームっぽくないという劣等感と
CTRL+STOPを押しながら起動されると
動作を必ず中断されてしまう事が玉に瑕です。
・余談ですが
MSXはシステム上での拡張子の厳格なルールづけはほとんどなく、
DOS1はCOMとBATだけで
BASICは自動起動時のみAUTOEXECというファイル名とセットで
拡張子BASを要求するだけでした。
・さらに余談ですが
コメントは"'"と"REM"で同じ意味ですが
REMの方がメモリの節約になります。
確か'が3バイト、REMが1バイトだったと記憶。
REMはlistで流しているときにコメントを発見しづらいので
あえて'にしていたと記憶しています。
--------
20 MAXFILES=1:POKE &HFBB1,1
・特殊な変数なのでMAXFILESは最初に宣言。
N88-BASICでいうところの"How many files(0-15)?"です。
・ワークエリアFBB1Hに1を書き込むと
STOPキーによる一時停止と
CTRL+STOPによるプログラムの中断操作を無効化しますが
実はこの効用を公式文章で一度も確認した事がありません。
もともと即売会で購入した同人ゲームの
ソース解析から知った機能だったと記憶していますが
(MSX・FAN誌だったかも?)
真相がいまだに知りたいところ。
またこの状態だとCTRL+STOP押下時にPSGレジスタが書き換わるので
PSGを使用する際に問題があります。
理想的な使い方としては
プログラム実行中はON STOP GOSUBで
飛び先をそのままRETURNにするルーチンを入れ、
それではプログラム中に
違うBASICプログラムに移動するタイミング
(例えば「100 RUN "ENDING.BAS"」)で
CTRL+STOPが有効になるので
その時だけFBB1Hを有効にし、
(「100 POKE &HFBB1,1:RUN "ENDING.BAS"」)
移行先でON STOP GOSUB定義後にFBB1Hを0に戻すのが
定石かと思います。
ちなみに『Mistral Blue』では
PSGアクセス時は必ず全てのレジスタに書き込む仕様になっているので
特にON STOP GOSUBの処理は行なわなかったようです。
もしくはSTOPキーによる一時停止を嫌ってのこの処理かもしれません。
--------
30 ON ERROR GOTO 1580
1580 ' エラー ショリ
1590 IF (PEEK(&HFBEC) OR 123)<>251 THEN RESUME ELSE BEEP
1600 IF (PEEK(&HFBEC) OR 123)=127 THEN 1590 ELSE 1600
・故意にディスクを抜かれた時用です。
BASICに戻って動作回避不能になるのを防ぐための例外処理なので
大きな処理はしていません。
キーを読んでリトライするだけの処理です。
・キー入力チェックには
INKEY$やINPUT$などキーバッファに依存するタイプのチェックは行なわず、
リアルタイムに押下されているキーをチェックします。
その際キーが押されていると離れているの判定は必ずセットにします。
--------
40 COLOR 15,15,15:SCREEN 0,0,0:WIDTH 40:KEY OFF:COLOR 1,15,15:CLS
・SET SCREENで個人設定されているであろう要素で
支障が出そうな部分を再定義。
特にSCREEN文の3番目のパラメータ、
キークリック音の有無は必ず再設定しましょう。
インターレースの有無はRF接続等で
画質改善の為に意図して有効にしているケースがあるので
個人設定を生かすようにしています。
・不思議な処理ですが
少しでも早くBASICの起動画面を消したかったから
こんな処理だったはず。
最後のCLSはSCREEN文で結果同等処理が行われますが、
あえてこうしたかったんでしょう。当時の俺。
・余談ですがMSXのキークリック音は
独自に持っている1ビットサウンドポートを使っていて
PSG等の音源に影響を与えません。
昔はこのポートを音声再生用に使って
喋らせる演出ばっかりやってました。
・喋らせるで思い出した。さらに余談。
『Mistral Blue』はエンディングで主人公が
MSXturboRのPCMで喋ります。
A1ST、GTは8ビットのD/Aコンバータを
無理無理A/Dコンバータとして使うせいで1ビット処理(?)になるから
PCM録音はMacintosh等の他の機種で作成すべし、という話があって
いまだにこの話の意味が分かっていません。
『Mistral Blue』のPCMデータは
上記話に従いSound Blaster Live!で録音しましたが
再生周波数と比較して2パーセント弱変わってしまい、
誤差範囲と割り切ったもののいまだ気になっています。
--------
60 IF PEEK(&H2D)<1 THEN A0$="MSX2 いこうよう です。":GOTO 1540
1540 ' ハード チェック アウト ヨウ クレジット ヒョウジ
1550 SCREEN 2:BLOAD "CHKOUT1.DAT",S:BLOAD "CHKOUT2.DAT",S:OPEN "GRP:" FOR OUTPUT AS#1:LINE (255,11)-(0,4),15,BF:PRINT #1,"Mistral Blue (C)2002 POPCoRN":LINE (255,23)-(0,16),15,BF:PRINT #1,"この MSX では どうさ できません。"
1560 LINE (255,35)-(0,28),15,BF:PRINT #1,A0$:CLOSE #1
1570 GOTO 1570
・やっとバージョンチェック処理へ。
まずはROM Version IDを読んでMSXのバージョンを確認。
MSX1ならエラーです。
・エラー画面はMSX1でも表示できるSCREEN 2にて
同サウンドトラックのジャケット画像のスキャンを表示して
エラー内容とクレジットを表示させます。
画像ファイルが2つあるのはカラーテーブルと
パターンジェネレータテーブルを分けている為。
・MSX-BASICのグラフィック画面での文字表示は
不思議なことにシーケンシャルファイル書き込み扱いで
このソースではエラー処理が終了する前に
ちゃんとファイルをクローズしてます。マメだなぁ。
# ソースではFOR OUTPUT ASとしているが
# 実際はFOR ASで大丈夫だったと記憶。
# ただその理由を知らないので省略はしなかった。
--------
70 IF (PEEK(&HFAFC) AND 6)/2<2THEN A0$="VRAM が 128Kbytes ひつようです。":GOTO 1540
・VRAMチェック。
日本語MSX-DOS2はVRAM64Kでも制限ありで動いたはずで
独立してこのチェックは必須だったはず。
--------
80 OUT &HD8,0:OUT &HD9,2:A1=0:FOR I=0 TO 7:IF I=0 THEN A0=0 ELSE A0=VAL("&B1"+STRING$(7-I,"0"))
90 IF INP(&HD9)=A0 THEN A1=A1+1
100 NEXT:IF A1<8 THEN A0$="かんじROM が ありません。":GOTO 1540
・漢字ROMチェック。
MSXにはメーカー独自の漢字ROMも存在するので
いちいち説明で「MSX標準漢字ROM」と記したのは懐かしい思い出。
MSXの漢字ROMのフォントは(権利の問題と思われます)各社独自で、
しかし特定のフォントの形状は全く同じにするという仕様があり
(確か全角のスラッシュ)
それを利用してそのフォントのビットデータをチェックする事で
有無を判定しています。
と、いうかこれしか判定方法がなかったはず。
・また、前述した事情で対応機種を増やすべく
第二水準漢字ROMは不要になるようにメッセージを調整しています。
(PUT KANJIで第二水準漢字を扱えるのはMSX2+から)
第二水準の有無チェックが面倒だったのも理由だったはず。
そのあおりで最後の敵の技『終焉の刻』が
『終宴の刻』に変更になっています。
--------
110 SCREEN 5:SET PAGE 0,1:VPOKE 1,PEEK(&HFC4B):VPOKE 0,PEEK(&HFC4A)
120 IF VPEEK(1)*256+VPEEK(0)<53271! THEN SET PAGE 0,0:SCREEN 0:A0$="ワークエリア が たりません。":GOTO 1540 ELSE SET PAGE 0,0:SCREEN 0
・MSXでは必須の行為と言っても過言ではない
予約済みワークエリアの確認。
これをせずに自分のプログラムがワークエリアに食い込んで
暴走したソフトの多かった事よ。
MSXは簡単な命令でこの処理を用意するか
ワークエリアを簡単に侵食されない対策が標準で欲しかった。
・ここでは一時保存エリアにVRAMを使うために
SCREEN 5の裏ページを使っています。
確か変数が使えなかった事情があったように記憶。
SET PAGEをいちいち戻してから
スクリーンモードを変えているのがなんか丁寧でアツい。
確かスクリーンモード再定義で
SET PAGEも0,0にリセットされたはず。念押しすぎ。
そしてTHENとELSEで同じ処理するなら判定の手前でやれよ…。俺。
--------
140 CLEAR 200,&HCFFF:DEFINT A-Z:RESTORE 1620
150 ON ERROR GOTO 1580
160 FOR I=&HD000 TO &HD016
170 READ A$:POKE I,VAL("&H"+A$)
180 NEXT:DEFUSR0=&HD000:A=USR0(0)
190 IF PEEK(&HD016)<2 THEN A0$="にほんごMSX-DOS2 せんようです。":GOTO 1540
1620 DATA 0E,6F,CD,7D,F3,B7,C0,78
1630 DATA FE,02,D8,32,16,D0,06,14
1640 DATA 0E,69,CD,7D,F3,C9,01
ORG 0D000H
LD C,6FH
CALL 0F37DH
OR A
RET NZ
LD A,B
CP 2
RET C
LD (WORK),A
LD B,14H
LD C,69H
CALL 0F37DH
RET
WORK: DEFB 1
END
・DOSのバージョンチェック。
BASICでは無理なのでマシン語導入。
先ほど確認した安全なエリアに書き込みます。
・アセンブラソースは残ってないみたいですが
確かここでDOS2設定のディスクバッファ(BUFFERS)を最大値にしているはず。
(追記:ハンド逆アセンブルしました。
バージョンチェック~バッファ確保であってました。)
この設定でMSXのフロッピーディスクアクセス速度は大化けします。
さらにA1ST、A1GTはMSX規格とは別で
独自のディスクキャッシュを持ってたはずで
さらに速くなりました。
・CLEAR文でエリア確保する際にリセットされるエラー処理を再定義。
MAX FILESは再定義不要だったはず。
・DEFFNで自作関数作るくせに
USRを関数と自覚していないのでこんな処理。
結果を戻り値に入るようにして
IF USR0(0)<2 THEN A0$="にほんごMSX-DOS2 せんようです。":GOTO 1540
って書き方をするためにUSRは関数なんだよ、
と当時の俺に言ってやりたい。
・DATA文の書式って
文字列=必ずダブルクオートで囲む、それ以外=数値、と思っているので
ダブルクオートで囲まなくても文字列って認識するのがいまだに不思議。
・DOS2と言えば
起動ブログラムには関係ないけどこれは書いておきたい。
カートリッジ版の日本語MSX-DOS2では
COPY文でのディスク←→VRAM間のBitBlt時に
ディレクトリ階層の指定ができません。
事前にCHDIR文などで階層指定が必要です。
MSXturboRでは直接指定が可能になっています。
--------
200 _RAMDISK(0):_RAMDISK(128,A):IF A<96 THEN A0$="マッパメモリ が 192Kbytes ひつようです。":GOTO 1540 ELSE IFA>=128 THEN POKE&HC9DA,255 ELSE POKE&HC9DA,0
・RAMディスク作成でマッパメモリのチェック。
マッパメモリが一定量見込める機種(具体的にはFS-A1GT)は
戦闘シーンのアニメーション処理を一括で読み込み
メモリが少ない機種(具体的にはFS-A1ST)では
敵のアニメは一括読み込みで
主人公のアニメはLRUで管理するのですが
そこのフラグ立てもやっています。
当時LRUなんて言葉も概念も知らず
関数作ったりしてこの処理組んだ当時の俺は凄いと思う。
なので技術的にはA1ST上で動作するほうががんばっている。
--------
210 IF PEEK(&H2D)<3 THEN 250 ELSE FOR I=&HD000 TO &HD011
220 READ A$:POKE I,VAL("&H"+A$)
230 NEXT:DEFUSR0=&HD000:A=USR0(0)
240 IF A<2 THEN A0$="R800 DRAMモード せんようです。":GOTO1540
1650 DATA CD,83,01,6F,26,00,22,F8
1660 DATA F7,3E,02,32,63,F6,21,F6
1670 DATA F7,C9
ORG 0D000H
CALL 183H
LD L,A
LD H,0
LD (0F7F8H),HL
LD A,2
LD (0F663H),A
LD HL,0F7F6H
RET
END
・R800 DRAMモードかチェック。再びマシン語で。
# DOS2チェックの時にやったマシン語読み込みを
# 2回に分けてやっているのはなぜだ…。
・前述した喋らせる演出が好きだった関係で
いつも容量優先のROMモードを採用していて、
多分これが最後の自作MSXゲームになるだろうから
速度優先のDRAMモードで操作性サクサクにしたい、と
頑張った記憶があります。
# 余談ですが『Mistram Blue』本編上だと
# スプライト禁止設定のZ80と
# スプライト許可設定のR800 ROMモードとでは
# 体感速度あんまりかわんなかった記憶が。
・フロッピーから通常起動しているなら間違いなくDRAMモードですが
HDD運用でイメージ等にしてファイラー等から起動している人用対策。
HDDはなくても全然平気な人だったので
HDDユーザーへの対策は気持ち面倒でした。
あとHDDにコピーして使用するユーザーは
ディスクアクセス時にAドライブ固定で指定されると
事前にASSIGNコマンドでのドライブ名入れ替えが必要になるので
カレントドライブで動くように書いて、と言われた事があって
DOS2は起動ドライブがカレントになるから
省略する事でFDD動作に悪影響もないと判断して
カレントドライブ指示はしていません。
--------
250 SCREEN 5:SET PAGE 0,1:IF VPEEK(1)*256+VPEEK(0)<58624! THEN SET PAGE 0,0:SCREEN 0:A0$="ワークエリア が たりません。":GOTO 1540 ELSE SET PAGE 0,0:SCREEN 0
・ここで再びワークエリアチェック。
前回ののワークエリアチェックは
DOS2とDRAMモードチェックさえできればOKな
メモリに余裕をみたチェックでしたが
今回はゲーム本編が収まる部分のチェックをします。
ここでコケた場合はいわゆるCTRL起動で
回避できる可能性があります。
--------
・…と、ここまでが起動処理でした。
本編も色々やりましたが
さすがに忘れてそうなので
ここまでとしたいと思います…。
・…が、…一体、誰が得するんだ?この記事。
MSXを知っている人は懐かしんでくれたり
知らない人は昔のプログラミングに
異国感を感じて頂ければ幸いです。