PC-88 と MIDI で遊ぶ


img
今回は音と通信半々くらいの企画です。
シリアル・パラレル・ジョイスティックと 3 つの出力ポートでシリアル出力を行います。

当時は MIDI 機器への出力に RCM-PC88 や MPU-401 などの高価な機材が必要でしたが
CPU パワーを全力で注ぎ込めば再生だけなら出来た、というお話。



■ PC-8801 で Standrard MIDI File を再生する


MIDIplay for PC-60/88 - 20131230版 (D88 イメージとソースコード)


対応機種は
・PC-6001mk2/PC-6601
・PC-8801mk2SR以上(V2モード 4/8MHz ただし 8MHz-H は未対応)

確認が取れているのは プリンタポート版とジョイスティック(マウス)ポート版だけです。
RS-232C は筆者の環境では何故か動きませんでした。
PC と繋いでログを取った限りでは、一応 38400bps で通信自体は出来ているようなのですが原因不明。

一応…RS-232C で動かす場合は SR/TR/FR/MR はディップスイッチで 1200bps にする必要があります。
I/O ポート 0x6F で設定可能な FH 以降は自動設定。

8MHz-H や V1 モード、80SR でも対応は可能かと思いますが、後述するようにタイミングがシビアなので
机上での計算だけでは実装は不可能です。適当に改造してください。動作報告歓迎。

演奏開始前や演奏開始後に各種リセットメッセージを送りません。
筆者の所では MIDI 機器側で初期化を掛けておかないと演奏が開始しない場合がありました。
誤動作を起こすと MIDI 機器が鳴りっぱなしになる可能性があるので音量には注意しましょう。
ポートの初期状態にも左右されるので、鳴らない場合もあるかもしれません。


同梱のディスクイメージにはあらかじめサンプル曲が入っていますが、
以下のようにすることで、別な SMF を書き込むことができます。SMF の制限は次の通り。
・SMF は Format0/1 で 32KBまで、24トラックまでのもの。プログラムを弄ればそれ以上も可。
・タイムベースは 48 を推奨します。昨今の 384〜480 くらいはかなり厳しいので変換を強く推奨。

> makemid80.exe sample.mid    PC88の場合

例によって trk.1 sec.1 から書き込んでいるだけなので分かる人はよろしくやってください。

サンプル曲(sample.mid)は以下のサイトの Beethoven > Sonata No. 14 > Part.1 を使用させていただきました。
SMF の Format.0 を Format.1 に、Timebase を 48 に変更しています。
>Copyright (c) 1996-2013 by Bernd Krueger
>Home: http://www.piano-midi.de
ライセンスが CC-BY-SA なので本作品についてもそれに従います。

シリアルポート版は、MIDI音源と PC の RS-232C ポートを直接つなぐケーブルがあるので、それを使用します。
MIDI 音源のスイッチはメーカーによって異なりますが PC-2 / 38.4k / RS232C-2 / (I) などの設定にしておいてください。

プリンタポート版およびジョイスティックポート版はインターフェースの工作が必要です。
といっても最低限必要なのは 220Ω の抵抗 2 本と若干量のケーブル(できればシールド線)だけです。

プリンタ版は、アンフェノール14pinオスコネクタ、
ジョイスティック版は、D-Sub 9pin メスコネクタがあると接続が容易です。

それから MIDI ケーブルを持っているなら DIN 5pin メスコネクタがあると良いでしょう。
でも MIDI ケーブルもコネクタも少々お高いんですよね。

以下に図を示します。図のコネクタはいずれも機器側コネクタを正面から見た状態です。
ケーブルやコネクタのオスメス、正面か裏面かで左右が逆になるので注意しましょう。

ジョイスティックポートは普段は入力に使用するポートなので、想定外の機器が繋がったまま出力してしまうと
本体ごと壊してしまう可能性があります。十分注意してください。


雑ですみません。
220Ωは赤赤茶(金)です。耐圧 1/4W とか 1/2W とかありますが何でもいいです。
220Ωが無ければ110Ωを直列にするとか中学理科を思い出しながらやってみましょう。

原理およびインターフェースは MSX の "只 MIDI" by 戯音匠者 氏 を参考にさせていただきました。というかそのままです。


■ パラレル通信とシリアル通信

2 つの通信方式の違いについておおざっぱに。原理的な説明なので負論理とかツイストペアケーブル云々は略。
通信の基本は 1 バイトをどのように送るかです。メモリが 16GB あろうが HDD が 4TB あろうが、基本は 1 バイトの転送。
で、その 1 バイトを、パラレルでは 8 本の信号線で送り、シリアルでは 1 本の信号線で送ります。
1 バイト = 8ビットだから 8 個の ON/OFF で表すわけですね。

パラレルは簡単です。ビットの状態そのままに信号線に電圧を掛けて「はい、チーズ」と送ってやると、受信側も信号を確定します。
この「はい、チーズ」をストローブ信号といいます。カメラにおけるストロボと同じ。

シリアルは多少手間がかかります。
信号線が 1 本しかないので、8 ビットを分解して 1 ビットずつ電圧を掛けたり掛けなかったりを繰り返します。
このとき、送信側と受信側には「約束事」が必要になります。それが通信速度と同期方式です。
ON と OFF しか送ってこないので、「どこが始まりなのか」が分からないといけません。
また、喋る方と聞く方で速度が合っていないとで聞き逃しが発生してしまいます。

MIDI では 31250 ビット/秒の速度で、同期方式は調歩同期ということになっています。
この設定を送信・受信ともに守らないと通信できません。

調歩同期方式では、何も通信していない時に電圧をかけておいて("1"の状態)、8 ビットの転送を始める直前に "0" を送ります。
この "0" は「スタートビット」と言います。受信側は 1 -> 0 になるタイミングを開始の合図と見なすわけです。
コンピュータの通信では、通信をしている時より、していない時の方("1"が連続している状態)が圧倒的に多いので、
運悪く通信途中から聞き始めたりしない限り間違えることはありませんし、必要ならエラー検出も出来ます。

で、そこから 8 ビット分の ON/OFF を 31250bps の速度で送ったら、また "1" の状態に戻して送信完了の合図とします(ストップビット)。
なので、8 ビット送るのに全部で 10 ビット必要になります。



31250 bps という数字は一見速そう(実際 8bit マシンにとっては速い)ですが、あくまで「10bit を送る間の秒速」という意味であって
常に 31250bit が切れ目無く飛び交っているわけではありません。
また、MIDI では通信の上りと下りが完全に分かれており、MIDI In と MIDI Out と端子自体も別になっています。
鍵盤からデータを取り込んだりするなら別ですが、PC から演奏データを送る分には信号線 1 本だけで大丈夫です。


PC-88 などに標準実装されている RS-232C(準拠)ポートは以上のことを自動的にやってくれます。
すなわち、CPUが 1 バイトを I/O ポートに書き込むと、後は勝手に 8 ビットに分解して適切な速度で送信してくれるのです。便利。

さてここからが本題。
勝手に 8 ビットに分解して適切な速度で送信してくれるのなら、それを CPU が自前でやっても同じことであり全く問題ありません。
出力にはパラレルポートであるプリンタ端子、あるいはジョイスティックポートを使います。どちらも +5V で MIDI の規格に合います。
プリンタポートは 8 本ある出力端子のうちの 2 番ピンを使います。
8 ビットをバラバラに送るわけでは無いので、前述のストローブ信号などは使いません。

ジョイスティックポートは一見入力っぽいですが実は出力用としても使えるので(汎用入出力ポートと呼ばれている)これを利用します。
OPN(PSG 部)を介するのが少し面倒ですが、基本的にはプリンタポートと同じ。

以下へつづく。

■ PC60 と PC88 の CPU ウェイトについて

シリアルポート出力以外では、CPU が自ら 1 バイトを 8 ビットに分解し、スタートビットとストップビットを付け加えて
タイミングを計りながら 1 ビットずつ I/O ポートに出力しなければなりません。合計 10 ビット。
そのタイミングですが、以下のように求めることができます。

CPU のクロック = 3,993,600 clock / sec (3.9936MHz)
MIDI の信号受信速度要件 = 31250 bit/sec (31250bps)

3993600 / 31250 = 127.7952

CPU が 約 128 クロック分処理を進めるたびに 1 ビット送出する。

誤差は無視していいのか?という疑問がわきますが、MIDI 規格上 1% の誤差は認められているので
128 クロックとしたときの bps = 31200 (誤差 0.16%) は特に問題ありません。
ちなみに CPU がきっかり 4.0MHz だと 128 クロックぴったりです。

ということで、CPU は 10 ビット送る間、誰にも邪魔されず、空命令を発行して正確なタイミングを取らなければなりません。

しかし CPU は環境によって、+αのクロック数が必要な場合があります。


PC60(4MHz)PC88(V2-4MHz)PC88(V2-8MHz)
M1ウェイトありなしなし
I/Oウェイトありなしなし
メモリウェイトなしなしあり
以上の「あり」の所に +1 クロック必要。
※ メイン RAM 上でのアクセスウェイト。

M1 ウェイトというのはおおざっぱに言うと、CPU のオペコード毎のウェイトです。1バイト系命令なら+1、2バイト系命令なら+2。
I/O ウェイトはその名の通り、I/O アクセスを伴う命令毎にウェイト。
メモリウェイトはメモリアクセス毎にウェイトです。命令自体を読み出すのにもウェイト、結果を書き戻すのにもウェイト。

同じ命令でどれくらい違うか見てみると、


PC60(4MHz)PC88(V2-4MHz)PC88(V2-8MHz)
LD A,B545
LD A,16879
OUT (0xFF),A131113
LD HL,(0xC000)171621
LD IX,(0xC000)222026
EX (SP),IX252329

PC88-4MHz は全て「なし」なので元の数字のままなのですが、それに比べると V2-8MHz の数字が泣けますね。
最後の EX (SP),IX は、命令自体が 2 バイト、(SP) からの 2 バイト読み出し、(SP) への 2 バイト書き込みで、合計 +6 です。

PC88 の V1 モードおよび PC60 の SR 世代は分からないので省略しました。
ちなみに PC88FE2/MC 世代の 8MHz-H モードでは 8MHz でもノーウェイトなのがウリでした。
PC88 に関しては、こちら(http://www7b.biglobe.ne.jp/~crazyunit/pc88.html)で 実機による詳細な検証を公開されています。

以上の結果を用いて、なんとか 128 クロック(8MHz 時は倍なので 256 クロック)を空ループで作り出せれば、後はそれを 10 回繰り返すだけです。
パズルのような作業で中々面白かったのですが、かなり苦労しました。

それから、相手(MIDI機器)が正常に受け取ったかどうか、などを気にする必要はありません。非同期垂れ流しと言われる由縁。
というより、それを確認する手段もありません。繋がっているのがたった 1(+1) 本のラインですからね。
だからこそ送信側でクロック計算を厳密にする必要があるのです。


■ SMF に関する覚え書き

MIDI や SMF の解説は検索すれば沢山見つかるのですが、筆者が詰まった所のメモを残しておきます。

■ 雑 感

やはり実機で何かするのは非常に大変ですね。何より場所をとるし動作検証にも時間がかかります。
SMF は取り回しの観点からも、何らかの OS をベースにすべきだと思いますが
複雑になって敷居が上がるのもアレなので、基本的な部分だけにとどめました。

音関係ばかり続いていますが、今回のは割と応用範囲は広めだと思います。

記事作成にあたってアドバイスをいただいた皆様に感謝。


■ 更新履歴



▲ TOP