にがMSX

〜MSXを利用してオールドPCにデータを流し込む〜


National Personal Computer

JR-100

前回、OCRとMSXを利用してSTAR FIREのダンプリストをテキストファイル化し、JR-100エミュレータVer2でゲームをプレイすることに成功した。

JR-100がどんなにモッサリ動作のマシンなのか、実機を知っている人が見ればSTAR FIREは凄いゲームだし、それを再現しているエミュレータも素晴らしいと思う。ただ、自分にとって今時のPC上で動作させても何か物足りない感じもする。やはり当時のままの状態で再現したい。


手段の検討

手段

メリット

デメリット
1)手入力 特別な装置が不要 面倒くさくて死ぬ
2)ROM増設 面白そう 製作にコストと手間がかかる
3)カセットテープ信号に変換 コストは掛からず 波形を作る手間がかかる

 まずは実機でSTAR FIREをプレイする手段について検討する。手入力は論外として、適当な16kBのROMを焼いて外部バス経由で拡張ROMの領域($8000-BFFF)にアサインするか、カセットテープ信号に変換してJR-100に流し込むか、このいずれかしかないだろう。

ROM案は完成すれば世界初(?)のJR-100用ROMカセットゲームになるかも知れないが、ワイヤーをチマチマ配線するのは面倒だし、材料調達にも手間とコストがかかる。最大の問題が自分には6800系のマシン語が読み書きできないことで、拡張ROMのプログラムをAUTOEXECできる仕組みがあるのかどうかも分からない。よって、ここは前回入力したデータをカセットテープ(CMT:Cassette Magnetic Tape)信号に変換する方針で行く。

カセットインターフェイス仕様

JR-100
MSX 1200ボー

MSX 2400ボー

方式
FSK FSK FSK

ボーレート
600 bps 1200 bps 2400 bps

スタートビット
0 0 0

データ
リトルエンディアン リトルエンディアン リトルエンディアン

ストップビット
11 11 11

スペース
1200 Hz 1200 Hz 2400 Hz

マーク
2400 Hz 2400 Hz 4800 Hz
 JR-100のカセットインターフェイスの仕様を調べてみると、MSX1200ボーと共通点が多い。スペースはbit=0を表現する発振周波数で、マークはbit=1の発振周波数。異なるのはボーレートのみ。

MSXの信号波形

 MSXテクハンWikiより画像を引用。1200ボーでbit=0を表現するには、1200Hzのパルスを1波出力すれば良いらしい。bit=1の時は2400Hzが2波。これで1秒間に1200bitのデータ送信ができる仕組み。このように0と1の状態を異なる周波数のパルスで表現する方式をFSK(Frequency Shift Keying)と言うんだそうな。

実際にパルスを生成する場合、bit=1を送信するには立ち上がりと立下り間に208μs = 745ステートのwaitを設けて適当なIOポートに1010を出力すれば良いと解釈できる。

JR-100の信号波形

 一方、JR-100の600ボーでは単純に波の数を2倍にしたような感じになる(画像はテクハンWikiのものを改変)。記載しているステート数はZ80 3.58MHz基準の値。

1バイトの構成

 図の如く、1バイトはスタートビット、データ、ストップビットの合計11ビットで表現する(画像はMSXテクハンWikiより引用)。ここはMSXJR-100も同じフォーマットで良いようだ。6800系はZ80と違ってビッグエンディアンなので、MSBからLSBの順で送信するのかと思いきや、何故かMSXと同じリトルエンディアンになっている。

JR-100のテープ信号フォーマット

種別

データ量

非データ部分1
1 bit 1

非データ部分2
9 bit x 28 11111110 x 28回

非データ部分3
1 bit x3828 1 x 3828 (255x15 +3)回

ファイル名
16 bytes ASCIIコードで15字まで 余白は$00で埋める

先頭アドレス
2 bytes Hi-Lowの順で送信 / BASICの場合 $0246

データ量
2 bytes Hi-Lowの順で送信 =n

フラグ
1 byte BASIC:$00 / マシン語:$4D

埋め草
11 bytes $00 x 11回

ヘッダチェックサム
1 byte ファイル名からフラグまでの加算値 下位1byte

非データ部分4
1 bit x255 1 x 255回

データ本体
1 byte xn 1byte x データ量n

データチェックサム
1 byte データ本体の加算値 下位1byte

 JR-100エミュレータ作者のけむしろう氏の解析したデータを参照させていただいたが、JR-100のCMT信号のフォーマットはこのようになっている。先頭アドレスとデータ量は、上位1バイト→下位1バイトの順で送信する点に注意が必要。ここは6800系の基本のビッグエンディアンになっている。データ量の項目がbit/byte混在している点にも要注意。

JR-100にCMT信号を読ませるためには、上から下までこの通りのデータ配列で途切れないパルスを作り出せば良い、ということになる。


既存ソフトの利用

 要求されている信号が理解できたところで、パルスを生成するために既存のソフトが利用できないか調べてみた。他機種用でもデータ部分が作れれば、ヘッダ部分をJR-100実機で出力した信号と結合して何とかなるのではないかと考えた。

とりあえずヒットしたのがこちらのサイト。紹介されているのはPC6000シリーズ用のCMT信号の読み取り/生成ソフト(P6DatRec)で、P6用FSK1200ボーのカセットテープ信号のWAVファイルを作る機能がある。600ボーに対応できるのかと調べてみたら、出力を1/n倍速にする機能があり、設定ファイルを弄って「出力の遅さ」を2.0にすると実質600ボーになりそうだったのでやってみた。

P6DatRec で生成したパルス

 WAVファイルを生成して波形エディタでSTAR FIREの1バイト目のデータ 01111110b=7Ehの部分を取り出してみた。このソフトは正弦波でパルスを作っているようだが、量子化ビット数やサンプリングレートが低そうで、波が刺々しい印象。CMT信号は1ビットデジタルなので正弦波より矩形波の方が良いような気がする。

波の数は確かに600ボー相当になっているのだが、ストップビットが3ビットになっているのも気になる。MSXではこのフォーマットでも読めるらしいが、果たしてJR-100で読めるのかどうか…。


MSXのCMT出力関連BIOS

TAPOON(00EAH/MAIN)……………書き出しOPEN

入力: A←ヘッダの種類(0=ショートヘッダ, 0以外=ロングヘッダ)
機能: テープレコーダのモータを起動し、Aレジスタで指定された種類のヘッダをテープに書き出す。
割り込みは禁止される。

TAPOUT (00EDH/MAIN)……………1バイトの書き出し

入力: A←書き込みデータ
機能: Aレジスタの内容をテープに書き込む。

TAPOOF (00F0H/MAIN)……………書き込みCLOSE

機能: テープへの書き込み動作を終了する。同時に割り込みは再開される。

 既存ソフトの利用では正しいJR-100用のCMT信号の生成は困難なようで、ヘッダ情報などを波形エディタで個別に編集する手間もばかにならない。そこでMSXを利用してパルスを生成してみることにした。

とりあえず只MIDI(MIDIインターフェイス)のようにプリンタポートに適当なタイミングでデータを出力して、オペアンプで音声信号レベルに変換して…とかやればなんとかなるかなと漠然と考えていたが、いざプログラムを書こうとすると、waitのステート計算が面倒くさい。

よくよく考えると、元々MSXには1200ボーでCMT信号を出力する機能があるわけで、これを利用してCMT端子出力で600ボーの信号が作れるかも知れないと、関連するBIOSを調べてみた。

上表はテクハンWikiより引用したもの。メインROMの00EDhをコールするとCMT端子に1バイトのデータが出力できるらしい。もちろん、1200/2400ボー出力のBIOSコールなのでそのままでは使えないが、調べてみる価値はあるだろう。

CMT関連ワークエリア

● CS120(F3FCH, 5)1200ボー

F3FCh 83(LOW01) ……………ビット0を表すLOWの幅
F3FDh 92(HIGH01) ……………ビット0を表すHIGHの幅
F3FEh 38(LOW11) ……………ビット1を表すLOWの幅
F3FFh 45(HIGH11) ……………ビット1を表すHIGHの幅
F400h HEADLEN*2/256 …ショートヘッダ用のヘッダビットのHIGHバイト(HEDLEN=2000)

● CS240(F401H, 5)2400ボー

F401h 37(LOW02) ……………ビット0を表すLOWの幅
F402h 45(HIGH02) ……………ビット0を表すHIGHの幅
F403h 14(LOW12) ……………ビット1を表すLOWの幅
F404h 22(HIGH12) ……………ビット1を表すHIGHの幅
F405h HEADLEN*4/256 …ショートヘッダ用のヘッダビットのHighバイト(HEDLEN=2000)

● LOW(F406H, 2)

F406h LOW01……………現在のボーレートのビット0を表すLOWの幅
F407h HIGH01……………現在のボーレートのビット0を表すHIGHの幅

● HIGH(F408H, 2)

F408h LOW11……………現在のボーレートのビット1を表すLOWの幅
F409h HIGH11……………現在のボーレートのビット1を表すHIGHの幅

● HEADER(F40AH, 1)

F40Ah 初期値: HEADLEN*2/256(デフォルト1200ボー)
内容 :現在のボーレートのショートヘッダ用のヘッダビット
(HEDLEN=2000)。SCREEN文の<カセットボーレート>により設定される

 CMT出力に関連するワークエリアを調べてみた。1200ボーと2400ボーのパルス幅の規定値がF3FChからの10バイトに書かれていて、現在のパルス幅はF406hからの4バイトに書かれている。SCREEN命令でボーレートが変更になると、規定値から現在のパルス幅にデータがコピーされる仕組みのようだ。実機でこの値を読んでみると、F406hからは83、F407hから92が帰ってきた(値は10進数)。

これらの値は1200Hz,2400Hz,4800Hzのパルスを生成する時のwaitに関わる定数と思われ、ここを弄れば発振周波数を柔軟に設定できそうではある。しかし、1bitを表現するパルスの数は設定できず、JR-100仕様の600ボー対応はそのままでは無理と分かった。

FS-A1のBIOSフック

00E1: JP RM1A63 ;TAPION ……読み込みOPEN
00E4: JP RM1ABC ;TAPIN ……1バイトの読み込み
00E7: JP RM19E9 ;TAPIOF ……読み込みCLOSE
00EA: JP RM19F1 ;TAPOON ……書き出しOPEN
00ED: JP RM1A19 ;TAPOUT ……1バイトの書き出し
00F0: JP RM19DD ;TAPOOF ……書き込みCLOSE
00F3: JP RM1387 ;STMOTR ……モータの動作指定
 パルス生成のwaitのとり方など参考になるかなと思い、FS-A1(MSX2)から吸い出したメインROMのBIOSを覗いてみることにした。テクハンwikiに書かれていたBIOSコールのアドレス00EDhを読むと、フックになっていて、そのままJP 1A19hで飛んでいた。

1バイトを書き込むルーチン

 メインROMの1A19hからのルーチンを逆アセして解析してみた。コメントとラベルは自分で勝手に付けたものなので、おかしなところがあってもご容赦を。

肝になるルーチンは最後の方、@IO_WR:とラベルしたところから下で、ここにパルスのwaitのとり方と、IOポートへの出力の方法が記されている。HLレジスタにワークエリアに書かれた定数をロードして、それをDECして0になるまでの時間をwaitとして使っているようだ。

wait時間を計算してみると、ステート数はDEC Lが5T、JP NZ,nnが11Tなので、発振周波数1200Hzの時のwait時間はL幅83x16=1328T、H幅92x16=1472Tとなっており、前後の処理で1491Tに近い値に調整していると思われる。

また、CMT信号の出力はIOの#ABhのPPIコマンドレジスタへの書き込みで行うことも分かった。

IOポート #AAh-ABh

PPI レジスタC #AAh

bit

機能

0-3
キーマトリクス row選択レジスタ

4
カセットMOTORコントロール 1=off

5
カセットデータ書き込み 1=high

6
CAPS LED 1=off

7
1bitキークリック音出力 1=high

PPI コマンドレジスタ #ABh

bit

機能

0
書き込む値をセット

1-3
PPIレジスタCに書き込むビットを指定

4-6
不使用

7
0をセット

 CMT出力に関連するIOポートを調べてみた。カセットデータ書き込み用のIOポートは#AAh PPIレジスタCのbit5にあるが、ここをアクセスする時には他のビットに影響を与えないために、#ABhのPPIコマンドレジスタを介してデータを書き込む作法になっているらしい。秘書を通さないと話をさせてくれないエライ人みたいだ。

 IO表を見ている内に#AAhのbit7にキークリック音を出力するためのレジスタがあることに気付いた。MSXのキーを押下すると「ポツポツ」と音がするアレだ。普段は地味〜〜な音しか出していないが、これは1ビットサウンド機能になっていて、ここにパルスを出力すれば、MSXの音声出力端子から目的の信号が取り出せるのではなかろうか、と思いついた。これが可能ならCMT端子を持たないターボRでも使えるし、ケーブルも専用品が不要になって一石二鳥。

CMT信号生成ソフト JRSAVER

 ということで、BIOS解析結果を基にJR-100仕様CMT信号生成ソフト「JRSAVER」を作ってみた。バイナリファイルをJR-100仕様のヘッダ付き600ボーCMT信号に変換してMSXの音声出力端子から出力する。MSX実機、エミュレータblueMSXに対応。ターボRもOK。CMTケーブル不要。実機ではアンプによる信号増幅が必要(直結不可)。正常動作可能クロックは3.58MHzのみ(高速モードで実行すると面白い)。マシン語バイナリはオフセットアドレスの指定が必要。BASICプログラムはメモリ格納形式のバイナリにしておく必要あり。JR-100エミュレータで使われるヘッダ情報入りのPROG形式ファイルにも対応しているので、一々オフセット指定とか面倒だったらJR-100エミュレータで予めPROG形式に変換して食わせるとラク。

世の中にどのくらい必要としている人が居るか分かりませんが、JRSAVERのダウンロードはこちらからどうぞ。

JRSAVER.COMで生成したパルス

 MSXエミュレータblueMSXの録音機能を使ってwav化したファイルの波形がこちら。例によってSTAR FIREの最初の1バイト7Ehの部分を見ている。ビット配列と波形パターンは仕様通りになっており、多少エッジがダレてはいるものの矩形波に近い形状になっていると思う。

テキスト→バイナリへの変換

10 MAXFILES=2
20 OPEN"06003BBF.DMP" FOR INPUT AS #1
30 OPEN"STARFIRE.BIN" FOR OUTPUT AS #2
40 FOR I=&H600 TO &H3BB8 STEP8
50 A$=INPUT$(4,#1):PRINT A$
60 FOR J=1 TO 8
70 A$=INPUT$(1,#1):IF A$=" "THEN 70
80 B$=INPUT$(1,#1)
90 D=VAL("&H"+A$+B$)
100 PRINT #2,CHR$(D);
110 NEXT J
120 A$=INPUT$(1,#1):IF A$<>CHR$(10) THEN 120
130 NEXT I
140 CLOSE #1:CLOSE #2
150 PRINT"COMPLETE!"

 STAR FIREの打ち込みテキストファイルをバイナリファイルに変換する手段だが、JR-100エミュレータにインポートしてからPROG形式でエクスポートすると簡単。PROG形式はJR-100エミュレータの独自仕様であるが、オフセットアドレスやファイル名等の情報を含むヘッダ付きファイルになっており、JRSAVERではファイルの先頭に"PROG"文字列があればPROG形式とみなし、パラメータを自動設定する。

エミュレータを使わずにバイナリに変換するのなら、上記リストのようにMSXのBASICプログラムで変換可能。実機&FDD環境だと時間がかかって厳しいと思うが、エミュレータの加速装置(10倍速)で処理するとあっという間に終わる。なお、このやり方だとファイルの最後にEOFの1Ahが付加されて1バイト大きくなってしまうので、気になる場合はバイナリエディタでそぎ落としておく。ヘッダ無しバイナリをJRSAVERで読ませる場合はオフセットアドレスの指定が必要となる。


実機へロード

 blueMSX経由で実機にデータを流し込んでいる様子がこちら。PanasonicのLet'sNoteからNationalのJR-100にファイルを転送している様は、孫がじいちゃんの面倒を見ているみたい(父ちゃんのMSXは遺影になってる)。

接続はPCのヘッドフォン端子JR-100テープレコーダ「モニタ」端子両端ミニプラグケーブルで繋ぐだけ。MSX実機で使う場合は信号レベルの問題で直結はできず、MSXを繋いでいるテレビモニタのヘッドフォン端子とJR-100を繋ぐことになる。JRとMSXでテレビ2台用意するのは大変なのでノートPCでエミュレータを使ったほうがお手軽だろう。

レベル設定

 実機へのロードの際には、音声信号のレベルを目一杯引き上げる必要がある。blueMSXのミキサー画面で、KBD(キークリック音)とメイン出力のツマミを100%に上げておく。PC本体のヘッドフォンのレベルもかなり高くしないと正常にロードできない。じいちゃんは耳が遠いんだ。

あと、なぜかblueMSXではコマンド実行直後にどこかのIOデバイスからノイズが発生するようなので、IOからのサウンド出力をミュートしておく。これはDIRコマンド等でも同じ現象が起こるので、ソフトの問題ではない模様。ターボRモードで実行する場合はCPUクロックをZ80に落としておくのもお忘れなく。

正しくヘッダを読むと、JR-100の画面でファイル名が表示されて、ロード中のデータがASCIIキャラクター(?)で表示される。JR画像右上の豆腐がソレ。リアルタイムでデータが流れている様子が分かって面白い。なかなかロードが始まらない場合は音声信号レベルが足りていないかも。正常にロードが終わると、JR-100のBEEP音がピっと鳴ってREADY表示される。ピピっと2回鳴ってSUM CHECK ERRORと表示されたら信号レベルを確認してやり直し。

起動!

 STAR FIREの600ボーでのロード時間を計算してみると13760 x 11bit / 600bps =252sec なので、ヘッダを入れると4分半くらい。ロードがエラーなく終了し、本誌の説明どおりA=USR($D00)でゲームは起動。30年振りに実機であの画面が目の前に!まさに、スペース・ウォーズの古典 ここによみがえる!!(先日も同じことを言ったような気がするが)

ちなみに、JR-100実機はNTSC信号が規格外レベルのようで、現代のLCDモニタだと正しく表示されないことが多い。うちの日立のプラズマミテレビだと比較的まともに映ったが、下の1行が半分切れている。いずれオシロで原因を探ってみたい。

BASICプログラムのロード

 自分が小学生の時に打ち込んだベーマガのプログラムは2010年に全て取り込んでエミュレータ用のPROG形式のファイルに変換してある。

それをJRSAVERでCMT信号に復元し、実機に読ませてみた。特に問題なくロード完了し、RUNでゲームが起動。

画像はMINI TREKゲーム(ベーマガ1983年2月号)。CAPTAINのスペル間違ってるけど、当時の打ち込みミスもそのまんま復元。

 3D CAR RECE(ベーマガ1983年8月号)もあの時のまま。


つづく

 実機でのSTAR FIREのロードに成功し、念願のプレイ!と行きたいところであったが、残念ながらうちのJR実機はキーの接触が悪くなっており、著しく操作性が悪かった。そうでなくてもこの消しゴムキーボードでミレニアム・ナントカ号を操って帝国軍に勝利するのはハン・ソ■でも難しい気がする。ちなみにエミュレータで発信音が出っぱなしだったサウンドだが、実機での動作ではレーザー発射、爆発、UFO出現でキチンと効果音が出た。

 前回書いたように、STAR FIREJR-100仕様のジョイスティックに対応しているはず。そして、けむしろう氏JR-100エミュレータのサイトのコメント欄に、まりすさんという方がジョイスティックのIOに関わる情報を記している。検証はされていないようだが、その情報通りの仕様でインターフェイスを作ればMSXのジョイスティックを接続できる可能性がある。もうここまで来たら製作にチャレンジするしかないだろう。

この記事の内容につきまして全面的に協力をしてくださったMikasenさんに感謝いたします。

JR-100エミュレータの作者、けむしろうさんにも感謝いたします。密かにバージョンアップに期待しています。

JR-100の解析情報を公開されているEnriさん、大変参考にさせていただきました。ありがとうございます。

にゃごすさんのテクハンWikiにはいつもお世話になっております。ありがとうございます。

次回ジョイスティック編へつづく!


ご意見、ご感想、ご質問はにがBBSまで!

Top pageへ戻る

copyright (C) 2016 Niga