MZ-1500バージョンアップアダプタ

2016.5.8 初版

 MZ-700本体に一切の改造を施すことなく、取り付けるだけでMZ-1500にパワーアップする…。

 またひとつ、MZ-700に不可能がなくなったのです。

"バージョンアップアダプタ"とは

 まずは実際に動いているところを動画でご覧頂きましょう。

…どうですか? 決してMZ-700にMZ-1500のメインボードをどうにかして入れたとかいうわけではありませんよ? 背面の拡張バスコネクタにこのボードを装着して、ケーブルを何本かと外付けのQDドライブを接続しただけで、MZ-1500のソフトが動くようになるわけです。

 そもそも、MZ-1500というのはMZ-700に対して機能拡張した製品であり、そのまま拡張部分を付け足しただけの構成である…と言われていました。MZ-700ユーザーだった人なら、マニュアルにこんな記述があったのを覚えてはおられませんでしょうか?

 $E5ポートと$E6ポートについて、禁止とか禁止の前の状態とか、わけのわからないことが書かれています。だいいち禁止とかするくらいなら実装しないでちょうだいよ、と思うところです。
 その説明に相当する部分が、MZ-1500ではどうなっていたかというと…。

グラフィックメモリ(PCG-RAM)が割り当てられていました。MZ-700のアーキテクチャを決定づけるカスタムLSI・M60719が引き続きMZ-1500でも使われているからなんですけど、いやそれもしかしてMZ-700設計時にグラフィック表示が計画されていた可能性があるんじゃない? という話はさておき、とにかくMZ-700で未使用だったエリアにPCG用のRAMが置かれたということになります。

 つまり単純に言うとこの未使用領域にPCG-RAMを置いてグラフィック表示させ、PSGとかを後付けすればMZ-1500になっちゃうんじゃない? となるわけです。そうは問屋が卸さないってことはあるだろうと思いつつも、技術的チャレンジとして面白そうです。しかも本体は一切改造などせず、外部からアクセスできるコネクタに装着するだけでバージョンアップできたら楽しいと思いません? そう、あのMSX1のカートリッジスロットに差し込むだけでMSX2に機能アップするという「MSX2バージョンアップアダプタ」みたいに。というわけで名称もあやかって「MZ-1500バージョンアップアダプタ」としてみました。若干の機能制限はありますが、その名に恥じない面白いものができたと思います。


ボードについて

 では今回製作したMZ-1500バージョンアップアダプタ(以下アダプタ)のボードを見ていきましょう。ボードの写真にマウスカーソルを乗せると各部分の機能の名前がご覧になれます。

 全ての機能が、この一枚に収められています。一枚といってもあちこち二階建てになっててあまりスマートさはありませんが…今回は技術検証のつもりだったので、あまり基板の加工とか面倒なことがないように、もし失敗なら/成功しても気が済んだら楽に部品取りできるように、変換基板のあるものは積極的に使って製作を楽にするようにしたのです。

 まずはコネクタから。左の黒い四角がカードエッジのバスコネクタで、これが直接MZ-700背面の50ピン拡張バスコネクタに刺さります。

 右上のD-SubコネクタはVGAです。MZ-700にはないグラフィック(PCG)の表示をしますから、アダプタ独自に表示出力を持つ必要があるのです。今時のものなのでボードにアプコンを組み込んで、VGAが表示できるモニタに直接映し出せるようにしています。

 右の黒くて四角いのはクイックディスク(QD)ドライブ用コネクタです。QDに収められたMZ-1500用ソフトを起動するため、外付けのオプションとして発売されていたMZ-1F11を接続できるようにしてあります。

 右下の青い基板に黒い四角はオーディオ出力用ミニジャックです。PSGも搭載しますから、やはりアダプタ独自に音を出せないといけないんですね。その左にローパスフィルタ(LPF)の部品が並んでいます。

 その左、大きい基板に隠れてちらっと見える黄土色の四角いのは電源コネクタです。電源は拡張バスコネクタには出ていませんので、ジョイスティックコネクタからいただきます。

 LPFの上の黄土色のコネクタはデジタルRGB入力です。ここにMZ-700のRGB信号を入力することで、このボードが本体と協調動作することができるようになります。

 このアダプタをMZ-700に装着するとこんな感じになります。

◆背面のバスコネクタに装着され、電源とRGBが本体に接続されています。 ◆さらにQD・VGA・オーディオ出力にそれぞれのケーブルが接続されるとこんな感じ。

 本当はケーブルが接続されるコネクタに基板を刺しているのでカードエッジコネクタの反対側が浮いてしまいます。MZ-700用QD I/FのMZ-1E14もそうでしたがブラブラしないよう足(六角スペーサ)を取り付けてあります。
 ジョイスティックコネクタ(電源のみ)とRGBコネクタに接続されているケーブルは、別のネタで作ってあったものを流用しています。利用できるものはどんどん使って、作業を減らすのが賢い工作というものですよね。

 二階建てのうち直付けしてないものを取り外すとこんな感じになります。

 拡張バス用カードエッジコネクタの右に並ぶICは5V→3.3V変換用バッファ。お手軽にHC245を使っています。デジタルRGB入力もそうですね。データバスだけは双方向なので3.3V→5Vにも変換する必要から、LVCC3245を使っています。それが小さい方の二階建てです。そう言えばこれも以前変換基板にはんだ付けしたものの、その時は余って使わなかったものでした。

 3.3V→5Vの逆変換については、Z80PIO経由のタイマと割り込みとウェイトの各信号出力にF125、QDの制御信号もろもろをLS541(これは純正と同じ)を使ってます。

 大きい二階建て基板は今回のメイン、Arrow ElectronicsBeMicro MAX10ボードです。Alteraの最新FPGA(MAXシリーズは本来CPLDという分類だったが、かねてからFPGAと同じLUTで構成されていると指摘されており、ついにFPGAの分類に入ってしまった)であるMAX10の10M08を使用しており、そこそこ大きな回路が入ります。このボードにはFPGAだけでなくSDRAMやシリアルFlash ROMなどいろいろ便利なデバイスも搭載されてなんとお値段たったの$30。しかも最近は本国から直輸入しても送料無料とかいう話があるらしく、チップ単体を買うよりも安い(しかも自分で実装したりする手間いらず)…となれば、使わない手はないでしょう。

 次に回路図をどうぞ。例によってFPGAありきの回路ですが…。

 点線で囲ったところがBeMicro MAX10ボードのコネクタです。ここだけ部品番号をMAX10ボードの回路図に合わせてありますので、PMODコネクタ(U5とU6)だけ半導体部品につけるUになっています。またU9のF125は手持ちの部品を使ったが故の選定で、LS125でも問題ありません。1番と2番、4番と5番の接続をそれぞれ交換した方がよかったような気がします(オープンコレクタと同等の動作となる)。

 3.3V電源はMAX10ボードの電源回路をまるまる利用しています。電力的に本当に問題ないかまでは検討してませんのでご注意下さい。またMAX10ボードの5V電源の供給をJ5コネクタから行っているのですが、MAX10ボードにコンフィグ用USBケーブルを接続するとそこからも5Vが供給されることになり、MZの電源をOFFにしてもMZへ5Vが流れ続けてしまいます。本当はダイオードを入れて逆流を防ぎたいところなんですが電圧降下するのがイヤなので…(レギュレータ経由で3.3Vに下げるのならともかく、5Vをそのまま使う前提)。もちろん正式には9Vとかを別に供給して5Vにレギュレーションするべきなんですけどね。

 FPGAのプロジェクトについてはGitHubにて公開しています。一部に外部の著作権が存在するデータがあり、それを削除してありますのでご注意下さい。


動作原理・実装方針

 ではここから、アダプタの中身というか、FPGAにどんなものを実装したのか説明してまいります。

●グラフィック(PCG)

 まずはMZ-1500最大の機能向上ポイントのグラフィックから。グラフィックと言っても実際にはPCGなので、メモリに書き込んだパターンがそのまま表示されるわけではなく、PCG用VRAMによって指定された番号から導き出されるメモリ位置のパターンが表示される…という、今見てもちょっと不思議なアーキテクチャですね。ゲーム機なんかのスプライトでいうところのBG画面と似てる、という説明をする人もいますね。

 そのPCGのパターンを保持するメモリ(PCG-RAM)は、1024個という多さとフルカラー(8色)で表現できるという仕組みのため、計24KBあります。これは先ほど紹介した、MZ-700時代には「禁止」とされていたレジスタ操作で実現する未実装領域に割り当てられます。その空間ひとつでは容量としては足りませんからポートへの書き込み値でさらにバンクを選択するのですね。

 既にあるのなら利用したいところですが…当然ながら内部から多数の信号を引っぱり出さないといけませんし、何よりVRAMのアクセスタイミングがかなり違います。ですので、表示に適したタイミングでアクセスできるVRAMを別途用意するしかありません。それはPCG-RAM共々、FPGAの内蔵メモリを使って実装することにします。

 しかしひとつ気になることがあります。MZ-700にVRAMがあって、それを無効化しないのに、別にVRAMを設けてしまったら喧嘩というか衝突しないのだろうかと…そこで、アダプタ側のVRAMをCPUからは「書き込み専用」として読み出せなくしてしまいます。読む必要が発生しても困ることはありません。なぜなら読むのは本体内のVRAMから読めば良いからです。同じ値がどちらにも書き込まれているので、片方から読み出せれば十分用は足せます。

◆書き込みの場合。表示に使われるのはアダプタのVRAMのみだが、本体のVRAMにも同じデータが書き込まれる。 ◆読み出しの場合。アダプタのVRAMからは読み出しデータが出力されず、本体のVRAMの値だけがCPUに出力される。書き込み時にどちらにも同じ値が書き込まれているので、ソフト的には何の問題もない。

 本当にそんなことやって問題ないのか心配になりそうなところですが、実は似たような話が既にシャープ純正品にあったりします。
 MZ-700では拡張ボードに搭載するROM(FDD I/FのブートROMや、バッテリバックアップRAMボードの読み書きルーチンなど)をアクセス可能にするか否かの制御について、バンク切り替え状態を加味する必要から、なんとボード毎に切り替え状態を保持するレジスタを持っているんですね。当然共通する機能についてのI/Oレジスタですから、どれも同じアドレスに割り当てられています。なんとも泥臭い話ですが、本体内部に持っているレジスタの値を取り出せない以上、それぞれのボードでそのコピーを持つしかないんです。バスコネクタには、本体内部のアクセスであったとしても全ての動作が現れますので、覗き見みたいに監視するようなことだって可能なのです。

 なおバンク切り替えの存在を前提とし、切り替え状態によってはボード上のROMに対するアクセスを許可する必要が発生するシステムの一般的な構成では、拡張バスにバンク切り替え状態を出すようにするか、内部アクセス時には外部にその動きを遮断して出さないようにするのが普通だと思います。

 話を元に戻しまして、次はVRAMとPCG-RAMアクセスのウェイトについてです。いずれも水平ブランキング期間中以外のアクセスではウェイトが入り、ブランキングまで待たされることになっています。アダプタが独自のタイミングで表示したとすると、本来表示してない時に書き換えされているはずが、表示中にも書き換えされる可能性が高くなる(というか時間的には表示にかかってる方が長い)ことになり、ノイズのようなものが見えるかもしれません。

 テキストVRAMのウェイトについてはMZ-700内部で作られていますが、PCG-RAMアクセスのウェイトはそもそもRAM自体から存在しませんので、アダプタから発生させる必要があります。いや確かに本来はウェイトなしでアクセスできるならそれがベストだったのですが、結果としてゲームなどの速度に影響を与えていることがあって、ウェイトなしでは正常動作にならないんです。実は1Chip MSXにMZ-700を実装した時、画面表示だけMZ-1500コンパチのPCGを搭載したのですが、テキスト含めノーウェイトだったため、例えばハドソンの「野球狂」では相手のピッチャーの投げる球が豪速球となってとても打てたものではなくなってしまったという経験がありまして…各所に存在するウェイトがソフトにどのように影響を与えているかわかりませんので、できるだけ忠実に再現するしかなく…。

 ということで、アダプタの表示タイミングを本体とできるだけ同期させて、もともとの動作と違和感なくアクセス可能にするよう工夫することにしました。その鍵は水平同期信号をサンプルすることでの同期合わせです。同期信号は1ラインや1画面で1回というか1往復分(H→L→H)しか変化しませんから、タイミングを合わせてそれと同時に変化する信号を作れればすなわちそれは画面表示回路が内部と同じタイミングで動いているということになります。

 具体的には水平同期信号の変化を捉えて内部のカウンタを相当する値に設定するということなのですが、そもそも内部とアダプタのクロックは非同期の関係ですから、完全に同じにすることはできません。そこで、ある程度の誤差が発生するのはしかたがないと諦めて、その誤差をできるだけ小さくするよう工夫します。具体的にはできるだけ速いクロックでサンプルすることで、今回は約57MHzのクロック信号(CPUクロックの16倍)を作って使用しています。本当は114MHzくらいのを作りたかったのですが、FPGA内蔵PLL(分周・逓倍をかなり自由に設定できるのでいろいろな周波数のクロックを生成させられる)の個数が少なく余り凝った周波数には設定できませんでした…。

 MZ-1500の画面出力タイミングはMZ-700のタイミング信号をベースに拡張されているような構成になっていますので、まずMZ-700のテキスト画面を出力できるようにするのが確実です。その動作確認も兼ねて、本体の画面出力は使用せず全てアダプタ内で完結するように制作しました。本体のRGB端子に接続し、全部の信号を取り込めるようにしてありますけれど、使用しているのは同期信号だけなのです。お手本として紅茶羊羹さんの解析された「M60719 Horizontal Timing」とMZ-1500の回路図を参考にしました。部分的には回路図そのままっぽいところも…。

 ウェイトも回路図に合わせる形で発生させるようにしています。普通のプログラムではウェイトがランダムに発生しますから、実機と同様に作っているつもりでもどこまで同じかは判別つかなかったりしますけど…。

 あとはパレットやプライオリティですけれども、これらは構造上純粋に追加されているだけのものなので、仕様通りに動くよう作ってしまえばOKです。仕様通りにね。そう、仕様通りに。プライオリティをRGB各色単位に処理してみたり、ブランキング処理した後にパレットを適用したりとかしなければ特に難しいものではありませんよ…(挙動不審)。

 ここまでの話はタイミング関係をMZ-700やMZ-1500と同等にすることでウェイト含め実機と同等にするということだったのですが、それだとRGB信号そのものが実機と同じになってしまうわけで、今時だと接続できるディスプレイを選んでしまうという問題が発生します。これまで何度もFPGAでアップスキャンコンバータを作っているんですから、今回も以前の設計を引っ張ってきて内蔵することにしてしまいます。

 実際の回路はX1シリーズ用コンソールエミュレータと同じく、4倍クロックで同期し信号の安定しているタイミングでサンプルする方式になりました。VGAのタイミングとMZ-700のタイミング、微妙に合わなくて素直に作るとジッタが激しくなってうまく行かなかったんですよね…。

●PSG(DCSG)

 MZ-1500のPSGは、当時の他の機種に多かったAY-3-8910系ではなく、TIのSN76489AN・通称DCSGが使われています。AY-3-8910に比べて表現力が乏しい分構造が簡単で、チップも16ピンDIPと小型なので2個搭載してステレオのように使えるようにしてありました。

 小さいという意味では、まだ入手可能ということもありますし、アダプタにも実チップを搭載しても良かったのでしょうが、FPGA版MZ-1500にフィードバックすることを目論んでこれもFPGAに実装してしまうことにしました。

 構造が簡単なのだから自分で作るという手もありますが、既に動いているものがあるならそれを持ってくる方がより簡単というものです。と思ってFPGAArcadeというサイトに公開されていたコレコビジョンのFPGA実装から持ってこようとしたのですが以前ダウンロードしたのがどこかに紛れてしまい、では配布元はというと新プロジェクト立ち上げに伴ってコレコビジョンのデータが消えており…。

 しかたがないので改めて探したところ、MISTというプロジェクトのセガ・マスターシステムの実装を発見したのでそこからPSGブロックを頂いてきました。MIST(aMIga-atariSTの略らしい)はCycloneIIIをコアにしたレトロPCおよびコンシューマゲーム機再現用ボードで、音声出力周りは(紆余曲折あったんですが)そのローパスフィルタを参考に(というかそのままに)製作することにしました。

 いくら動作実績があるといっても案外とそのままでは使えないもので…いろいろ改造することになりました。機能追加したりしてエンティティもこんな感じに変わってたりなど…。

entity psg is
  port (clk : in STD_LOGIC;
  WR_n : in STD_LOGIC;
  D_in : in STD_LOGIC_VECTOR (7 downto 0);
  output: out STD_LOGIC);
end entity;
entity psg is
  port (RST_n : in STD_LOGIC;
  clk : in STD_LOGIC;
  CE_n : in STD_LOGIC;
  WR_n : in STD_LOGIC;
  D_in : in STD_LOGIC_VECTOR (7 downto 0);
  daclk : in STD_LOGIC;
  ready : out STD_LOGIC;
  input : in STD_LOGIC;
  output: out STD_LOGIC);
end entity;
オリジナル 改造後

 ざっと説明しましょう。RST_nは実チップにはない信号です。DCSGは一旦音を出したら止める操作(出力ゲインをゼロにする)をしないと止まらないのですが、MZ-1500ならシステム起動直後の初期化で止められるのでしょうけど、素のMZ-700システムではDCSGなんて知りませんから鳴りっぱなしになってしまいます。そこでリセットが入ったら強制的にゲイン値をゼロにして音を止めるようにしました。

 いざ動かしてみると妙に音程が低かったので、ソースをよく見てみると基準クロックの64分周で動作するようになっていました。実チップは32分周なので、それで音が低くなったんですね。元のマスターシステムのソースでは通常の動作クロックの倍(8MHz)が供給されていましたから、でもソフトは4MHz前提で設定しますから、つじつま合わせで分周比を変えたのでしょうね。
 …とすれば大丈夫と思っていたんですが、どうも音が変なような気がする…いやそうでもないか…と思いつつそのままにしていたところ、動画を公開したら「音程が低い」との指摘が…! 分周比設定についての式が32分周と読めたので合ってるはずと思い込んでいたのですがブロック図をよく見てみると16分周して2分周してるじゃないですか…そうか駆動クロックは16分周するんだ…。

 エンティティの説明に戻ると、clkの他にdaclkがあるのは、内包するΔΣ変調方式のD/Aコンバータ(DAC)用クロックを別のもっと速いやつにしたかったからです。デバッグ中なかなか綺麗な音で鳴ってくれなくて、もしやDACクロックが問題なのでは…と50MHzを入れてみたのですけど、もっと遅いクロックでも良かったですね。

 またデータシートには記述が発見できない音声入力機能。MZ-1500では8253とボイスボードの出力をDCSGとそれぞれミックスするのに使われていますが、アダプタの実装でも同様の使い方ができるようにinput入力を追加しました。これはDCSGの外部でミックスするとどうも8253の出力音量が大きくなってしまうことから、調整したかったということでもあります。ミックスといってもDACに与える数値に適当な値を足すだけなんですけどね。

 そしてready信号、つまりウェイト出力。まぁぶっちゃけ、テンポや音長に従って周波数を設定するのですからあまりウェイトが影響することはないかと思いはしますが…何事も念のため…。

 DCSGのデータシートを見ると、ウェイトの長さについて不思議な事が書かれています。アクセスが終了するまで、"apploximately 32 clocks"…おおよそ32クロックであるというのです。てか、おおよそってなんなんですか。アクセスに応答するのに必要な時間は、設計したら決まるのではないんでしょうか。

 DCSGのアクセスウェイトについては、実は既に紅茶羊羹氏が「MZ-1500 SN76489ANアクセス時のウェイト」と題して興味深い調査結果を公表してくださっています。これを見ると、OUT命令も含めて32クロックだったり48クロックだったりと、アクセスにかかる時間が伸び縮みしていることがわかります。というかその前に「同期」とか書かれてたり、なにやらDCSGが勝手に動いているみたいな感じなんですが…。

 …そういえば、アクセス時間がだいたい32クロックなら、DCSGは入力クロックを32分周して使っていたりしたのでした。てことは、その32分周したタイミングでしか書き込みデータを受け取れないってことなんでしょうか? 32クロック周期でバスI/Fも動いている?

 いろいろ検討してみたところ、実際には16および32クロック周期で動いているのだろうと考えられることがわかりました。模式図にするとこんな感じになります。

 箱は1クロックの周期、箱の中の数値はカウンタの計数値と考えて下さい。0から31まで数字がありますので、5ビットのカウンタが動いていることになります。

 カウンタは通常…つまりアクセスがない時は0から15までをループしています。チェックポイントがふたつありますが、チェックポイント2の条件が成立しないかぎり16より右には進みません。

 アクセスが発生…つまりCE_xがLになると、まず第12クロックのチェックポイント1でCE_xの状態が確認されます。その結果を第15クロックのチェックポイント2で確認し、チェックポイント1時点でLであったならカウンタを16に進めます。

 ready信号はCE_xがLになった時点でLレベルを返し、ウェイト状態を示します。これはカウンタが31から0になるところで解除されます。書き込みデータもその時に取り込まれます。ウェイトが解除されるのでCPUも動けるようになり、しかる後アクセスが終了するわけです。これが標準的な動きです。アクセスは第12クロックでチェックされますからそこから前のいつ始まったとしても、チェックポイント後の動きが決まっているため全体としてアクセス時間が伸び縮みするというわけなのです。

 チェックポイント1の後から2まで、つまり第13クロックから15クロックまでのところでアクセスが始まったらどうなるのでしょうか。それが次の図になります。

 チェックポイント2でのチェックはチェックポイント1の結果なので、チェックポイント1でCE_xがまだHであれば、カウンタは16以上に進まず0に戻ってしまいます。ウェイトは出続けますから、CPUは次のチェックポイントの到来まで待たされることになります。もう一回回ってきたチェックポイント1にてCE_xがLであることが確認されますので、これでようやく先ほどと同じ動きで書き込み動作が行われます。この場合は32クロック以上のアクセス時間がかかることになります。

 実チップはもっと非同期的な動きだと思われるのでこの模式図どおりではないと思いますが、このように実装することで紅茶羊羹さんの調査結果とぴったり合う動作をさせることができるようになりました。

 さて、DCSGはそれでいいんですが、音楽演奏のためにはまだ足りません。MZ-1500ではBASICにてタイマ割り込みによるバックグラウンド演奏を行うようになっています。その割り込みはZ80APIOによるもので、タイマは8253が利用されています。PIOは元からなかったので当然として、8253はその出力を内部から引っ張ってこれないので、こちらもFPGAに実装することにします。

 8253は以前からFPGA版MZシリーズにて簡易的に実装したものを使用していました。モードがたくさんあるのですが、実際には使わないものもあるのでまじめに実装してもしんどいだけであまり報われないんですよね。なので時計と音楽演奏に十分な機能だけ実装していました。キャリーラボの三重和音プログラムがちょっと特殊な使い方をしていたのでそれだけ対応していたのです。

 が、今回は音楽だけでなくタイマとして使われるということで、特にチャンネル0については複数のモードを切り替えて使用することが確実になりました。タイマ割り込みなのですから、どういうタイマにするか制約はないので、まじめに全モードについて実装する必要があるはずです。

 ちょっとそれは大変なのでネットを探してみたところ、OpenCores8254(とライブラリ)がありましたのでそれを使わせてもらうことにしました。8254とは8253に加えてステータスレジスタを読み出せるように拡張したもので、8253として扱えば全く同じものです。
 ただ、バス仕様がOpenCores標準のWISHBONEだったので、これをZ80バスに合わせる必要がありました。ここは改造するのではなくてZ80バスのラッパーを作ることで最小限の手間で済ませることができました。それ以外にも若干のバグがありましたけどね…。

 Z80PIOもFPGA版MZ-80Bで実装したものを使用していたので実績がある…と思っていたのですが、今までひどい勘違いをしていたようで…8255の汎用入出力みたいにして使うならPIOはモード3以外の選択肢はなかったんですね…モード0か1だと思い込んでて、それでいてモード3みたいな実装になってました…しかもモード3だと割り込み要因の検知はエッジだったんですね…よくキー割り込みを使用するHuBASICがちゃんと動いていたもんだな…。

 さらには、8253がタイマ割り込みで使用されると従来の音楽演奏ができないだけでなくその周期パルスの音がノイズになってしまいますので、8255の出力によりこれを止めることになっています。というわけで信号1本のことなのですが8255も実装することになってしまいました。

 8255と8253はMZ-700に元からあるデバイスで当然同じアドレスにマッピングされるものですが、先ほどのキャラクタVRAMと同様「書き込みは受け付けるが読み出せない」ようにしています。PIOはこのアダプタにしか搭載されていませんが、元のプリンタポートと同じになっていますので、一部を除きやはり書き込み専用としています。ただポートAのみタイマ割り込み用の信号が未使用ビットに割り当てられていますので、その2ビットだけ読み出せるようにしてあります。

●QD I/F

 MZ-1500用ソフトを動かすため、そしてよりMZ-1500らしくするため、MZ-700用I/FのMZ-1E14互換機能を搭載しています。といってもアドレスデコード以外はZ80バスそのものなので、ほぼそのままバス信号を接続しているだけとも言えますね。
 電源だけは違っていて、MZ-1E14ではCMT用電源をQDドライブが受けてケーブルを経由してI/Fに供給していたのを、MZ-2000/2200用I/FのMZ-1E18みたいにI/Fから供給するようにしています。

 MZ-1E14にはブート兼用の拡張モニタ・9Z-501Mがありますが、MZ-1500との互換性を完全にするためには第2ROMである9Z-502Mが必要になるはずですので、しかるべきアドレスで読み出せるようFPGAの内蔵メモリを使って実装しています(開発の初期、まだPCG-RAMなどを実装していなかった時にはQDが使えると便利なので一時的に9Z-501Mを載せてました)。外部で切り替えられるようジャンパスイッチを設けたのですが、肝心の内蔵メモリが足らなくなったので断念しました。いやまぁ、いくらでもやりようはあるんですがそこまで凝る必要はないかと…。

 ところでMZ-1E14の回路図を確認するとROMには1ウェイトほどかかるようになっているのですが、MZ-1500ではノーウェイトになっています。CPUは変わらないのになんでなんでしょうね? ここはMZ-1500に合わせてノーウェイトにしましたが…。

 MZ-1500では、リセット時に実行するモニタ(1Z-009B)の先頭にて$E800番地に飛ぶようになっており、初期化等全て9Z-502Mが行うようになっています。
 一方MZ-700の拡張モニタである9Z-501Mも$E800番地にマッピングされますが、モニタ1Z-009Aはこの先頭番地が$00だと自動でジャンプしてくるようになっています(MZ-80Aのモニタ・SA-1510以来の機能)。

 そして9Z-502Mは、1Z-009Bの先頭でジャンプする仕様のはずなのに、$E800番地が$00になっていて、MZ-700でマッピングさせてもちゃんと自動でここに処理が移るようになっています。ただ起動時のビープ音をそれぞれで鳴らすので、2回「ピッ」と音がするのですけどまぁご愛嬌ということで…。初期化を上書きすることには問題ありませんので、気にしないことにします。

 しかし、9Z-502Mの先頭が$00なんてのはMZ-1500開発の名残だったりしないでしょうか。もしかしたらこの「MZ-1500バージョンアップアダプタ」は当時シャープで開発していたテストボードを再現しているのかもしれませんね。

●RAMファイル・EMM

 今回のアダプタ開発において、紅茶羊羹さん作勝手移植版「ドルアーガの塔」が遊べることをわかりやすいひとつの目標に掲げることにしました。この作品ではオプションだったRAMファイル・MZ-1R18が動作に必須となっています。MZ-700には当然装着できるスロットはありませんが、幸いにしてFPGAボードに容量8MBのSDRAMがありますので、これを利用してRAMファイルを実装することにしました。

 SDRAMコントローラは以前FPGA版MZの実装で作っていますのでそれを持ってきました(実績あるから大丈夫だと思ったらバグが潜んでいたりとかしましたけど…)。SDRAMコントローラの自作はステートマシンのお勉強という意味で適度に難しく良い教材だと思うのですが、一度作ってしまうと使い回しができるので、今回みたいな場合もフルスクラッチからの難しさとは裏腹のお手軽感いっぱいになっちゃいますね。なお私のコントローラのI/FはSRAM互換にしてあるのであとはアドレス設定とカウントアップを実装するだけです。

 ということで、見事にドルアーガの塔が動きました。

 ついでに、MZ-700のHu-BASICで使用できるEMMも実装しました。仕組みとしてはRAMファイルとあまり変わりませんので簡単ですね。

●漢字ROM・辞書ROM

 ここまでできればゲームなんかの実行には十分な機能があるのですが、拡張バスコネクタを専有していることもあり漢字ROM(と辞書ROM)が装着できないことから、BASICでも使えない辞書ROMをサポートする「ユーカラJJ」が動かせないのがちょっと残念…。

 漢字ROM(MZ-1R23)と辞書ROM(MZ-1R24)の容量は合計384KB。これだけの大きさのメモリはFPGA内にはありませんので、外部に接続されたメモリデバイスに頼るしかありません。

 その筆頭といえばやはりSDRAM。もともと8MBの容量がありますから、RAMファイルとEMMに使ってもまだまだ有り余っています…とは言うものの、そこはやっぱりRAMなので、ROMとして使うならあらかじめそのROMの内容を書き込んでおく必要があるわけで…FPGA版MZの実装ならSDカードから読み出してなんてこともしてますが、今回はそういうのをつけてませんので…。

 とするとROMチップの搭載でしょうか? でももうそんなスペースは余ってないかも…いや、シリアルフラッシュROMという手がありますか。必要な信号も少なそうですし、搭載するだけなら簡単そうですよね?

 ただやはり問題は、当たり前なんですがデータがシリアルになっているということで…つまり同じだけのデータレートを確保するには、8倍速いクロックで駆動されなければならないということです。MZ-700の場合CPUクロックは3.58MHzですから、この周期で送られるデータがあるとすると、シリアルなら28.6MHzでなければ同じにならないわけですね。

 加えて、シリアルフラッシュは読み出すアドレスの与え方もシリアルになっています。最近のROMは容量も大きいのでそのアドレスも24ビット…3バイトあったりします。さらには、フラッシュメモリなので書き換えに関するアクセスも必要なことから、アドレスに先立って1バイトのコマンドも必要です。ということは、1バイト読み出すために

1(コマンド) + 3(アドレス) + 1(データ) = 5バイト(40ビット)

のデータ列を入出力しないといけないわけです。1980年代のROMでもアクセス速度は速くて150nsくらいでしたが、それと同じだけのアクセス速度をシリアルフラッシュで実現しようとすると2.66GHzものクロックが必要になってしまいます。しかしいくつかのメーカーのデータシートを見てみたところクロックはせいぜい33〜75MHzぐらいまでという状況なので、やはり使い方自体が全く違うということなのでしょう。

 もっと高速にアクセスできる方法はないかと探すうち、そういえばQSPIなんてのがあったなと思いだしました。Q…Quad、つまり4ビットパラレルでアクセスするシリアルフラッシュ(微妙に矛盾してるが)なんですけど、実は4ビットなのはデータ部分だけで、コマンドとアドレスは1ビットの純粋なシリアルだったという…つまり40ビットが34ビットになるだけなんですね。そもそもシリアルフラッシュは一度アドレスを与えたら後はデータがずらずら続いて取り出せるというのが特徴なので、データ部分の量が多ければコマンドとアドレスのオーバーヘッドが相対的に小さくなるがために、その部分を短縮する改良はされないのでしょう。そしてアクセスのほぼ全てがデータと考えられるならそれを2ビットや4ビットに並列に扱えれば時間短縮効果は大きくなるわけで…。

 またさらにデータシートを見ていると、読み出しについて「ハイスピードモード」があるものが多いことがわかります。どうやら特別なコマンドを用いるとクロックを高速にしてかまわないということのようで…いや逆ですね、その他のコマンドはどれも高速クロックで使えるんだけど読み出しだけは例外で、それゆえに高速クロックで使える読み出しコマンドが追加されているという感じに見える…。
 ただこれも考え物で、高速読み出しではアドレスの次に8ビット分のダミーサイクルが入ることになっていて、全体で最短48ビット(6バイト)に伸びています。なんだか、CPUの速度を上げたくてクロックアップしたけどウェイトが必要になって、トータルで見たらほとんど効果がなかったみたいなのに似た状況になりかねない…無理に高速読み出しにせずともよいこともありそうな気がします。

 そういえばですね、実際にZ80側で必要なアクセス時間というのはどれくらいなのでしょうか? というかMZ-1R23のアクセスってノーウェイト?
 実は以前、個人的にMZ-1R23とMZ-1R24の回路を図面に起こしたことがありまして、それを見なおしてみるとなんと約12クロックもウェイトが入っていることがわかりました。というか思い出しました。ロジアナで計測してみると、こんな感じになります。

 CLKはCPUクロック、Addrは下位8bitだけサンプルしていて、$B9番地というのは漢字ROMのパターン読み出しアドレスになります。実際にウェイトとして出力されているのがWAIT#で、数えてみると12クロックありますね。

 おおお、これだけの時間があれば速いクロックといっても現実的なものが使えますよ。それにゲームではないので、同じだけのウェイトを再現しなければならない積極的理由はありません。あえて言えば本物より遅くならないようにすればいいかな? という程度ですね。

 というわけでネットでいろいろ探して使いやすそうなモジュール(ブレイクアウトボートみたいなやつ)を発注! …したけど…あれ? そういえば最初からFPGAボードに搭載されてなかったっけ?

 …あ、あった…。

 …端子はこれか…もっとちゃんとドキュメントとか読めって話だな…。

 16Mbit=2MBですね、容量としても十分です。おかげで届く前から積み基板決定じゃないか…あまり高いものじゃなかったから良いようなものの…。

 で、搭載されているシリアルフラッシュのチップを直接見てみるとST Microelectronicsのロゴが見えるような気がするんですが、ドキュメントにもデータシートを探してもMicron製だということらしい…。調べてみると高速クロックとして75MHzまで、低速クロックでも33MHzで使えるようですから、これならCPUクロックの8倍(約28.6MHz)でちょうど良さそうですね。

 ということで、バージョンアップアダプタとは別にkromcopyというプロジェクトを作って、データ書き込み用回路とそれを駆動するプログラムを作成しました。MZ-1500にボードを挿して直接的に漢字ROMとかからコピーしようかと思ったのですが、ボードがウェイトをつぶしてしまう回路になっていたため、一旦テープでデータを吸い出した後MZ-700で書き込むようにしました。データは紅茶羊羹さん作の吸い出しツールを使わせていただきました。まったく、紅茶羊羹さんさまさまですなぁ…。

 シリアルフラッシュの読み出し回路はごく普通に、ひとつのデータに対してひとつのアドレスを与える、つまりシングルリードを繰り返すよう動作します。前述のとおり読み出しは一度アドレスを与えたら連続して取り出せるので、これがMZ-1R23のROMアクセス回路の動きと似ているので一瞬これを利用できそうな気がしてしまうのですが…「連続」の意味合いがちょっと違うので断念。そんなわけでシリアルフラッシュROMに対してはこういうアドレスの与え方になりました。

 シリアルフラッシュROMの容量は2MBありますので21ビットあれば全て指定できますが、コマンドフォーマット上では24ビット指定する必要があるので頭3ビットは未使用となります。
 ポート$B9は漢字ROMの場合パターンアドレス、辞書ROMの場合辞書データアドレスを指定するI/Oレジスタです。BCレジスタ間接アドレッシングにてアクセスすることになっており、データが下位8ビット・Bレジスタが上位8ビットとなります。漢字ROMのパターンアドレスの指定では頭4ビットは使われません。
 ポート$B8は漢字ROM・辞書ROMの選択とパターン並び、辞書ROMの場合のバンク選択を設定するレジスタです。そのバンク選択がポート$B9で指定するアドレスの上に付け足されます。

 アクセスすると、漢字ROMでは下位5ビットが、辞書ROMでは同16ビット分が1バイト読み出す度にカウントアップします。漢字ROMの時のカウンタはポート$B8を設定するとゼロクリアされるようになっています。この辺りの動きは本物と同じにしてあります。

 というわけでユーカラJJもこの通り。

 辞書ROMは8万語を収録しているとされていたのですが、時代の変遷でよく使われる単語が変わるのか、意外に変換できないものがあったりしますね。動画の収録の際「実装している」とか入力したかったんですが候補に出てきませんでした。そう言えば当時は「実装」なんてコンピュータ用語としても使ってませんでしたかね…。

 ちなみにシリアルフラッシュのアドレス割り当てですが、

000000 01FFFF 漢字ROM
020000 03FFFF 未使用
040000 07FFFF 辞書ROM
080000 1FFFFF 未使用

となっています。まだまだ余裕がありますが、今回はここまでにしておきましょうか。

●その他

 ボイスボードは接続しませんが、ステータスがACKにならないと発声ルーチンで固まってしまいますので、$E8ポートの読み出しでゼロを返すようにしています。


制限事項

 本体無改造で装着するだけのアダプタとしてはよく動いていると自画自賛していますが、やはり多少の無理と技術検証ゆえに省略したせいでの制限事項があります。

MZ-1500ではSIOもディジーチェーンに組み込まれ割り込みを発生させることができるようになっています。QDの読み書きをポーリングではなく割り込み駆動させることでバックグラウンドでのアクセスができるのではないかと思われますが、MZ-1F11のケーブルには割り込みの信号がありませんので、このアダプタでもSIO割り込みを使うことはできません。実際に使っているプログラムはほとんどないと思いますが…。
これは単純にコネクタを設けてないだけです。それだけでおしまいの話なんですが、なぜやってないかと問われれば、手元のボイスボードの余りがなかったのと、固定方法がなかったためです。というか、できることならボイスボードもFPGAに実装してしまいたいのですがねぇ。
音楽演奏のテンポを司るPIOがプリンタポートに割り付けられているのは、プリンタのステータス信号でも割り込みを発生させてバックグラウンド印刷ができるようにするためです。が、アダプタではプリンタの信号を入れてないのでせっかくRAMファイルがありながらスプーラ機能には使えません。消耗品もろもろを考えると当時のプリンタを今でも動かすというのは難しくなってるところもありますので、まず困ることはないかな、と…。
アダプタがバスコネクタを専有してしまうので、その他一切の拡張ボードを接続することができません。動けば面白いということで漢字ROMと辞書ROMは内蔵させましたが、RS232Cとか…あとやはりFD I/Fが接続できないのは大きいですね。これが最大の制限だと思います。
ただ、FDでのソフトはDISK BASICとS-OSくらいしかありませんので、どうしても困るというものでもないのですよね。別の見方をすればそれぐらいしかないのならプロテクトを掛けたディスクというのもないわけで、それならFPGA版MZ-80Bで実装した仮想FDDをこちらにも実装するという手もあるわけですが…ディスクイメージファイルのマウント方法などUIの問題もあるわりには、苦労して実装する甲斐もなさそうということで、今は見送っています。
QDを動かすためには拡張ROMが必要…でもまるくん作のMZ-700エミュレータ「MZ700WIN」でもあったように、ハードだけMZ-1500と互換を持たせてあれば、テープベースが前提にはなるのですがROMがなくてもある程度のソフトが動くようにはなります。
ただBASICは…5Z-001はやはり一部処理にて9Z-502Mを呼び出していますので、つまりはIPL起動のものだけしかROMなしでは動かないことになってしまいますね。アダプタを使ってMZ-700をMZ-1500に変身させようと思ったら、ROMのためにMZ-1500が必要だったという本末転倒感…。

さいごに

 いやぁ、できちゃうもんですね。というかMZ-1500の主要な部分をアダプタの形で外付けしただけやん…というような気もしなくもないですが、いやいやCPUもメインメモリも時計としての8253もキーボード関連も、もちろん電源もデータレコーダもみんなMZ-700のものですから、まぁ気のせい、気のせい…。

 当初検討した時、最大のヤマは画面出力タイミングを本体と同期できるかだと思っていたのですが、多少悩みながらもわりとすんなりテキスト画面が出たことで、成功する確信が持てました。実は他の部分もヤマとしては小さくなかったのですけどね…。

 ところで、こういうハードが作れてしまうのであれば、同じような考え方でMZ-800との互換性を実現するアダプタが作れるのではないか…とか思ってしまいそうなものですが、多分それは難しいでしょうね。なんといってもカスタムLSIの機能が拡大されており、元のMZ-700ではバンクではなかったところにバンク切り替えが設けられたりしています。そのバンクにフォントROMの内容が見えて、それを拾ってグラフィックメモリ(これまた新規のバンク)に描画してるのですから、それらがまとめて解決できなければ実現は不可能でしょう。

 ではMZ-800のMZ-700互換モードにてこのアダプタを接続したらMZ-1500になりはしないでしょうか? どうもハード的にはいけそうな気もするのですが、モニタROMが第2ROMだけしか互換性がなく、また8255なんかもMZ-1500で拡張された部分が共通で使えるわけではないのですから、惜しい感じはするのですけどうまくいかなさそうなんですよね…まぁその前に拡張I/Oボックスを接続するためのI/Fボード・MZ-1E20が接続のためには必要になるんですけどね。

 それはさておき、今回のような手法は他でも使えそうな気がします。例えば、MZ-2800の8bitモード(MZ-2500モード)に隠された…というか中途半端に残るMZ-80B/2000モードを復活させるための画面出力周りの実装とかです。MSXは本当によく練られたシステムで、それゆえに驚くような柔軟性があったりするわけですが、そんな準備のないMZでもよくよく見れば面白いことができる「穴」があるのかもしれませんよ?

戻る