
PCエンジンの互換機を作りはじめました。 いろんな情報をのっけていこうと思います。
目次
プロジェクトの主旨を「互換ハードウェアの開発」とします。
開発環境として、とりあえず回路図は BSchで作成しています。 回路シミュレータは自作するか、フリーウェアを使います。 実際の製作は FPGA を使うことになると思いますが、 とりあえず勉強のため汎用ロジックを使うことを想定して開発を進めます。
[Oct.14 2005] 結局汎用ロジックを想定するのはやめました。
[Nov.08 2005] 専用掲示板を設置しました。興味のある方は覗いてみてください。
ここで公開される技術情報は個人の範囲で利用願います(商的利用を禁じます)。
CPU 実装完了 (Rev. 4)。シミュレータと実 FPGA でデバッグ中。 思ったよりもゲートを使ってしまった(Speed で 60%)が、今回は実行系の大部分を LUT (4096 x 8bit) から マイクロコードを読み出す方式で実装したので、コンフィグ ROM → Block Select RAM に マイクロコードを書き込んで ROM を構成すればかなりゲート数が削減できるのではないかと思う。 →ざっとみたところ 500 slice くらい削減できそう → Speed で 30% くらいになるか?
2006/11/10 19:36 1,044 ALU.h
2006/11/11 13:17 8,080 ALURev5.v
2006/11/11 22:05 8,009 BLT.v
2006/11/11 16:06 19,963 Core6280.v
2006/09/16 18:54 730 GeneralRegister.v
2006/11/09 22:31 11,480 Instruction.h
2006/11/05 10:22 666 Interrupt.h
2006/09/23 15:17 2,487 MemoryMappingRegister.v
2006/11/11 16:04 55,139 MicroCode.h
2006/11/11 16:08 127,755 MicroCode.v
2006/11/11 00:26 2,886 MUX.h
2006/09/16 18:52 890 ProgramCounter.v
2006/09/23 12:03 891 StackPointer.v
2006/11/05 10:43 4,718 State.h
14 個のファイル 244,738 バイト
| Logic Utilization | Used | Available | Utilization | Note(s) |
| Number of Slice Flip Flops: | 316 | 3,840 | 8% | |
| Number of 4 input LUTs: | 1,905 | 3,840 | 49% | |
| Logic Distribution: | ||||
| Number of occupied Slices: | 1,160 | 1,920 | 60% | |
| Number of Slices containing only related logic: | 1,160 | 1,160 | 100% | |
| Number of Slices containing unrelated logic: | 0 | 1,160 | 0% | |
| Total Number 4 input LUTs: | 1,921 | 3,840 | 50% | |
| Number used as logic: | 1,905 | |||
| Number used as a route-thru: | 16 | |||
| Number of bonded IOBs: | 43 | 141 | 30% | |
| Number of GCLKs: | 1 | 8 | 12% |
Minimum period: 25.424ns (Maximum Frequency: 39.333MHz) Minimum input arrival time before clock: 27.646ns Maximum output required time after clock: 31.896ns Maximum combinational path delay: 34.549ns
| CPU [Rev. 4] | ||
|---|---|---|
| OVERALL COMPLETION | 100% | |
| ALU | 100% | |
| INSTRUCTION DECODER | 100% | |
| IMPLIED | 100% | |
| ACCUMULATOR | 100% | |
| IMMEDIATE | 100% | |
| IMMEDIATE ZERO PAGE/X | 100% | |
| IMMEDIATE ABSOLUTE/X | 100% | |
| RELATIVE | 100% | |
| ZERO PAGE/X/Y | 100% | |
| ZERO PAGE RELATIVE | 100% | |
| ZERO PAGE INDIRECT/X/Y | 100% | |
| ABSOLUTE/X/Y | 100% | |
| ABSOLUTE INDIRECT/X | 100% | |
| BLOCK TRANSFER | 100% | |
| PROGRAM COUNTER | 100% | |
| GENERAL REGISTERS | 100% | |
| STACK POINTER | 100% | |
| MEMORY MAPPING REGISTER | 100% | |
| STATE MACHINE | 100% | |
| VDC | ||
| OVERALL COMPLETION | 0% | |
| TIMING GENERATOR | 0% | |
| VRAM ADDRESS COUNTER | 0% | |
| VCE | ||
| OVERALL COMPLETION | 10% | |
| COLOR TABLE RAM | 100% | |
| COLOR TABLE ADDRESS COUNTER | 100% | |
| COLOR OUTPUT DAC | 0% | |
| PSG | ||
| OVERALL COMPLETION | 90% | |
| WAVE TABLE RAM | 100% | |
| F COUNTER | 90% | |
| WAVE TABLE ADDRESS COUNTER | 90% | |
| VOLUME ATTENUATORS | 100% | |
| PSEUDO-NOISE GENERATOR | 100% | |
| LFO FUNCTIONALITY | 0% | |
| SOUND OUTPUT DAC | 100% | |
| TIMER | ||
| OVERALL COMPLETION | 100% | |
| PRESCALER | 100% | |
| CONTROL REGISTER | 100% | |
| DOWN COUNTER | 100% | |
| RELOAD REGISTER | 100% | |
| INT. CONTROLLER | ||
| OVERALL COMPLETION | 100% | |
| MASK REGISTER | 100% | |
| STATUS REGISTER | 100% | |
| ADDRESS DECODER | ||
| OVERALL COMPLETION | 90% | |
| CD-ROM2 | ||
| OVERALL COMPLETION | 0% |
主にこのページで作成したモノをダウンロードできるようにするつもりです。
なんとなくはじめにタイマーの回路図を起こしてみました。 ちょっとごちゃごちゃしていますが... まだデバッグしていないので、多分動かないです。

PC Engine Timer は、
以下に PC Engine Timer の重要な特徴を挙げます。
とりあえず 4040 を D-FF を使って自力でシミュレートしました。
簡単なものからどんどんいきます。アドレスデコーダの回路図を起こしてみました。 まだデバッグしていないので、多分動かないです。

PC Engine と互換性のあるメモリマップを以下に示します。100% 互換かどうかは 未調査につき不明です。
-------------------+--------------------+------------------------ Logical Address | Physical Address | Activated Signal ===================+====================+======================== 00:0000-7F:1FFF | 000000-0FFFFF | /ROM 68:0000-87:1FFF | 0D0000-10FFFF | /CDRAM (64kB + 192kB) F7:0000-F7:1FFF | 1EE000-1EFFFF | /BRAM F8:0000-FB:1FFF | 1F0000-1F1FFF | /MAINRAM FF:0000-FF:03FF | 1FE000-1FE3FF | /VDC FF:0400-FF:07FF | 1FE400-1FE7FF | /VCE FF:0800-FF:0BFF | 1FE800-1FEBFF | /PSG FF:0C00-FF:0FFF | 1FEC00-1FEFFF | /TIMER FF:1000-FF:13FF | 1FF000-1FF3FF | /PAD FF:1400-FF:17FF | 1FF400-1FF7FF | /INTCTRL FF:1800-FF:19FF | 1FF800-1FF9FF | /CDROM FF:1A00-FF:1AFF | 1FFA00-1FFAFF | /AC -------------------+--------------------+------------------------ ※アドレスは 16 進数表記
ROM と CDRAM の範囲がオーバーラップしていますが、これは実機と同じく カートリッジが差し込まれたときに L になる信号線で切り替えることにします。
Logical Address の上二桁 XX: の部分は、CPU が持っている MPR というレジスタの値です。 この MPR について知っていないと、上の表は理解できないです。ですので、 以下 MPR について簡単に説明します。
MPR は、 i8086 のセグメントレジスタの概念とほぼ同じです。i8086 ではセグメントレジスタ の値の +1 は Physical Address の +16 に相当しますが、PC Engine では MPR の +1 は Physical Address の +8192 に相当します。
MPR は全部で 8 つあり、Physical Address を 8kB ($2000 bytes) のセグメントに分割したときの、セグメント番号(0-255)を保持します。 このセグメント番号は、Physical Address の上位 8 ビットの値そのものになります。
MPR が 8 つあるのはなぜかというと、CPU の 16-bit の実効アドレス空間 (Effective Address Space)を 8kB ($2000 bytes) 単位に分けると 8 分割されるからです。 この 8 つのセグメントに、 MPR0-MPR7 がそれぞれ下表のように対応します。 各々の MPR には 0-255 の値が書き込まれ、これに基づいて Logical Address → Physical Address の変換が行なわれます。変換式は、x を Effective Address の 上位 3 ビット、 Offset Address を Effective Address の 下位 13 ビットとして、
---------------------+--------------------+---------------------
Effective Address | Valid MPR | Offset Address
=====================+====================+=====================
0000-1FFF | MPR0 | 0000-1FFF
2000-3FFF | MPR1 | 0000-1FFF
4000-5FFF | MPR2 | 0000-1FFF
6000-7FFF | MPR3 | 0000-1FFF
8000-9FFF | MPR4 | 0000-1FFF
A000-BFFF | MPR5 | 0000-1FFF
C000-DFFF | MPR6 | 0000-1FFF
E000-FFFF | MPR7 | 0000-1FFF
---------------------+--------------------+---------------------
※アドレスは 16 進数表記
CPU がリセットされると、 MPR7=0 に初期化されます。これはどういうことかというと、 CPU がリセットベクタを読み取りに Effective Address FFFE-FFFF をアクセスしますが、 これは [MPR7]:1FFE-[MPR7]:1FFF に分解されるので、結局 CPU は Physical Address の 1FFE-1FFF を読み出すことになる、ということです。つまり、リセットベクタは ROM の先頭から 1FFE-1FFF に格納されている必要があります。
近いうちにこのアドレスデコーダの回路をシミュレートして動作を検証します。
描き直しました。

さらに描き直しました。
主な部分をシミュレートしました。自力でシミュレートするのは効率が悪すぎるので (ハードをデバッグする前にシミュレータをデバッグしないといけない)、 他の手段を考えます。
1023 クロック目(i=2046)にコントロールレジスタに '1' を書き込みます。 これによって、リロードレジスタの値(シミュレータでは 0x1F に固定)がダウンカウンタにロードされ、 カウント動作が許可されます。最後の方でコントロールレジスタに '0' を書き込み、 カウント動作が禁止されることを確認しています。
/LOAD 信号の真理値表を挙げておきます。+EDGE というのは、U10 の Q 出力のことで、 COUNT 信号の立ち上がりを検出して1になります。
---------+---------+-------+--------
RELOAD | CTRL.WR | +EDGE | /LOAD
=========+=========+=======+========
0 | 0 | 0 | 1
0 | 0 | 1 | 1
0 | 1 | 0 | 1
0 | 1 | 1 | 0
1 | 0 | 0 | 0
1 | 0 | 1 | 0
1 | 1 | 0 | 0
1 | 1 | 1 | 0
---------+---------+-------+--------
この表からただちに
/LOAD 信号は CPU の書き込み要求信号 CTRL.WR を使って生成しているので、 この信号のタイミングによっては、回路を若干変更する必要がありそうです。 ただ、6502 系の書き込み要求信号のタイミングがいまいちよく理解できてません。(^_^;
とりあえず、 CPU は W65C02S をなんとかして入手し、 HuC6280 完全互換の CPU が完成するまではこれを使おうと思っています。
とりあえず描きました。

Nuts and Volts (COMPUTE II ISSUE 1 / APRIL/MAY 1980) があまりにもすばらしいので、主な部分を解読しました。
プロセッサには D-フリップフロップ (D-FF) が多く使われる。 D-FF は、データ(D)入力と、エッジトリガ型のクロック入力を持っている。 D に与えられたデータは、クロックのアクティブエッジ(通常は立ち上がりエッジ) が発生したときに、出力に現れる。エッジトリガ型の FF は、クロックの アクティブエッジが発生したとき以外は出力を変化させずに保持し続ける。 74LS74 や、 74HC74 はエッジトリガ型 FF の一種である。
D-FF には、エッジトリガ型のほかにトランスペアレント・ラッチ(transparent latch)がある。 これも入力 D を持っているが、これはエッジトリガではなく、ゲート入力になっている。 ゲートが「真」の間、入力 D は出力に「つつぬけ」になる。入力 D は、ゲートが「真」から 「偽」へ変化するエッジで、FF に D の値がロック(記憶)される。
FF を用いたラッチ動作は、次の手順を踏襲している:
通常、FF に書き込まれるデータは、ロッキングエッジが起こる少し前から、ロッキングエッジの
少し後までの期間に与えられる。そこで、次の2つのパラメータを定義する。セットアップ時間
は、ロッキングエッジが起こるどれだけ前にデータを与えていなければならないかを示す時間の最小値である。
ホールド時間は、ロッキングエッジ終了後、データを与え続ける必要のある時間の最小値である。
74HC74 のセットアップ時間は約 20[ns]、ホールド時間は約 5[ns] である。
つまり、FF への入力データは、少なくともロッキングエッジ発生前の約 20[ns] から発生後約 5[ns] の間、
与え続けなければならない。

R/W 信号線が H のときは 6502 の1クロックサイクルは読み出しサイクルになり、 R/W 信号線が L のときは書き込みの1サイクルになる。一つのサイクルは、2つのほぼ対称な半波に分けられる。 はじめの半分は、φ1クロックが H である。R/W 信号線とアドレス信号線はφ1で変化する。φ2では、データ 転送が起こる。データシートによると、φ2の立ち下がりからφ1の立ち上がりまでの遅延時間はゼロであり得る (最大値が与えられていない)。遅延時間がゼロのときは、φ1クロックとφ2クロックはほぼ同時に変化する。 しかし、外部での使用においては、一般にφ2と/φ2が使われる。φ1をφ2のかわりに使うべきではない。
R/W 信号線とアドレス線は、φ1の立ち上がり後、最大 300[ns] のセットアップ時間を有する(TRWS と TADS)。 データはφ2の立ち上がり後 200[ns] 秒以内に現れる(TMDS)。φ2は 1[MHz] クロックの場合で最小 430[ns] の幅をもつ。 よって、データはφ2クロックの立ち下がり(ロッキングエッジ)までの最小 230[ns] の期間だけ存在する。 データはφ2の立ち下がりから 30[ns] の間保持(ホールド)される(THW)。よって、6502 は、セットアップ時間が 230[ns] 以下で、ホールド時間が 30[ns] 以下のどのようなデバイスに対しても、うまく書き込めることが保証される。 このタイミングにおいて暗黙に決まっているのは、φ2の立ち下がりエッジがロッキングエッジであるということである。 6502 は書き込みパルス信号を発生しない。書き込みパルス信号は、φ2と、反転した R/W 信号 W/R とを NAND して生成しなければならない。この信号は、φ2の書き込みサイクル期間中のみ L となるパルスである。 6502 のファミリデバイスでは、φ2と R/W は独立に入力され、この書き込みパルス信号生成のための ゲート処理はデバイス内部で行なわれる。
読み出し動作では、外部のデバイスがデータバスにデータを出力し、φ2終了時(立ち下り時)に 6502 内部に取り込まれる。 このタイミングを下図に示す。

書き込みのタイミングと同様、1[MHz] の 6502 では、アドレスと R/W のセットアップ時間は最大で 300[ns] である(TRWS, TADS)。 φ2終了前のデータセットアップ時間は最小で 100[ns] である(TDSU)。 さらに、読み出されるデータはφ2後 10[ns] 以上保持されていなければならない(THR)。
デバイスが出力するデータは、 R/W 信号線とデコードされたアドレス信号線から 生成された読み出しゲートを通ってデータバスに乗り、その値が CPU に読み出される。 データの読み出しパルス信号の生成はプロセッサの内部で行なわれる。 読み出しゲート信号は、このデータ読み出しパルスとデータ信号とが重なることを許容する。 6502 システムでは、ゲート信号がφ2終了と重なってしまうことが多い。 これだと、データの取り込み時間が削減されてしまい、 ホールド時間の要求値を満たさなくなってしまうように見える。 これについては、まずトライステートゲートがデータをバッファする場合は、 少なく見積もってもおそらく 15[ns] の遅延が発生する。また、φ2 が1つないしは2つの バッファゲートを通過している場合は、6502 のφ2終了時から 15〜30[ns] の遅延が発生する。よって、φ2 終了時にデータが切り落とされてしまうように見えても、 実際にはデータはφ2後 30〜40[ns] の間残留していることになるので、データは問題なく プロセッサに取り込まれる。
[Reference]Verilog-HDL で書いてみました。→ソース
上に示したタイマーの回路は非同期方式で、 Verilog-HDL 記述の方は同期方式になっています。 汎用 IC を使って回路を構成すると 自然と非同期方式になってしまい、HDL 記述では自然と同期方式になってしまいます。 また、非同期方式と同期方式では回路が結構変わってしまいます。
そういうわけで、回路と HDL 記述の両方をやるのは難しいと感じてきたので、とりあえず HDL で ざっと全体を記述してしまい、あとで興味深い部分だけをピックアップして回路図化 することを考えています。
上の Verilog-HDL ソースは、一応 FPGA ボードで学ぶ論理回路設計 に付属の学習ボードで動作を確認しました。システムクロックをそのまま与えると目で確認できないほど 高速に動作するので、33[MHz] のシステムクロックを 1024 分周したものをプリスケーラに与えています (よってさらに 1024 分周される)。
当分はこれで部分的に動作を検証しながら作っていくことになると思います。
タイマーを実装する上で注意すべき点を挙げておきます。
PCE で使われるクロック周波数を生成する分周器を Verilog-HDL で書いてみました。 1/2 分周器と 1/3 分周器でマスタークロック 21.47727[MHz] からそれぞれ 10.73863[MHz] と 7.159090[MHz] を得て、あとはこれら2系統の出力を各々 1/2 分周して 5.369317[MHz], 3.579545[MHz], 1.789772[MHz] を得ています。 →ソース
今回は iverilog + IVI で動作を検証しました。一応シミュレータ上では動作しているようですが、 これで本当にいいのかどうかはまだわかりません。
ソース修正しました。 always @(posedge clk or negedge clk) はできないらしいです。 実チップで動作を確認しました。
OT(Off-Topic) ですが、衝動的に作りたくなったので急遽作りました。

XILINX 製の CPLDに回路を「プログラミングする」ためのツールです。 純正品を買うと \6,500 くらいしますが、自作だと \2,000 くらいで作れます。 まぁ半日くらいかけて作るので、差額 \6500-\2000 を仕事の時給に換算するとあまり得した気分にもなりませんが...。 CPLD は小さい回路しか入らないので、これに PC Engine をプログラミングするのは到底無理です。
回路は XILINX の公式ウェブページで発見したものをそのまま作りました。 古い部品のあり合わせの寄せ集めで作ったので、 配線ミス等よりも部品が生きているかどうかの方が心配でしたが、 意外とあっさり動きました。
回路図は特に描いていません。 ダウンロードケーブルの回路図と XC9572-PC84 または XC95108-PC84 のピン配置表と 84ピン PLCC のピン番号図があれば作れるはずです。
基板自体は 84 ピン PLCC 専用になっていますが、 FPGA や 44 ピン PLCC のプログラミングも考慮して、 基板端の端子に信号線を出してあります(写真下)。 ALTERA 製の CPLD/FPGA もプログラミングできるのではないかと思います。 今度 7160 あたりを入手して試してみようと思います。
衝動的に作りたくなったと書きましたが、 主な製作動機を分析してみるとこんな感じです。(^_^;
CPLD で何か作ったら OT でここに掲載します。
PSG を Verilog-HDL で実装中です。 waveform を出力するところまでは特に難しくなさそうですが、 音量の実装が一筋縄ではいかない感じです。
PSG には音量調整用にアッテネータ(減衰器)が搭載されています。 1つは PSG の各チャネルの左右の音量を同時に操作するアッテネータ(CH-ATT)、 もう1つは PSG の各チャネルの左右の音量を個別に操作するアッテネータ(L-ATT, R-ATT)、 あと1つは全 PSG チャンネルに効果があるマスターアッテネータ(M-ATT)です。 実機ではもしかしたら減衰器ではなく増幅器が搭載されているかもしれませんが、 いずれにせよ振幅の比の話なのでどっちで考えてもいいと思います。今回は CH-ATT が増幅器みたいな実装になってしまいました。
アッテネータはログスケールで変化するので、 そのまま実装しようと思うと、ログの計算をすることになります。 でも、そんなことをやっているとゲートが足りなくなるので、 とりあえず CH-ATT だけはなんとかデジタルで実装し、 あとはアナログ回路で実装することにします。
CH-ATT は 32 ステップで 1.5[dB] ずつゲインが変化します。 5 ビットのステップ入力のそれぞれの重みは次のようになっています。 6.0[dB] はほぼ 2 倍、12[dB] はほぼ 4 倍、24[dB] はほぼ 16 倍 というところに注目して、高価なログの計算をビットシフトで近似してしまいます。 式の右辺でビットシフトを使って近似値を作っています。 ~= は「ほぼ等しい」という意味です。
D0: 1.5 [dB] ~= 1.1885倍 ~= (1+1/8+1/16)倍 = 1.1875倍 ~= 1.4927[dB] D1: 3.0 [dB] ~= 1.4125倍 ~= (1+1/4+1/8)倍 = 1.4375倍 ~= 3.1522[dB] D2: 6.0 [dB] ~= 2.0倍 = << 1 ~= 6.0206[dB] D3: 12.0[dB] ~= 4.0倍 = << 2 ~= 12.041[dB] D4: 24.0[dB] ~= 16.0倍 = << 4 ~= 24.082[dB] D0|D1: 4.5[dB] ~= 1.6788倍 ~= (1+1/2+1/8+1/16)倍 = 1.6875倍 ~= 4.5449[dB]
この近似を用いて実装したアッテネータのソースを掲載しておきます。
CH-ATT の出力は、チャネルごとに有効な左右のアッテネータ L-ATT/R-ATT に入力されます。 このアッテネータによって、左右の音量を独立して変化させることができ、 定位(panpot)を設定することができます。 このアッテネータは 16 ステップで 3.0[dB] ずつゲインが変化します。 これもデジタルで実装したいところですが、デジタルである程度きちんと計算しようとすると 次は 13 → 21 ビットになるので、CH-ATT よりもさらに大量のゲートが必要になります。 よってこれはゲートに余裕があればデジタルで実装することにします。 それまでは↓のアナログ回路でなんとかすることにします。

こんな回路は初めて描いたので、動くかどうかはちょっとわかりません。こういう回路は ゲートのノイズが乗りそうでかなり心配です。 左側にあるのが L-ATT です。R-ATT もこれと同じものを使います。L-ATT と R-ATT は PSG の各チャネルにそれぞれ 1 つずつあります。PSG は全部で 6 チャネルあるので、 L-ATT と R-ATT それぞれ 6 回路ずつ、全部で 12 回路作る必要があります。 結構大変かも... ラダー抵抗の後にバッファが必要かもしれません。
右が M-ATT です。これは 左右1つずつ、合計2回路あります。 これは PSG の各チャネルの L-ATT と R-ATT をそれぞれ通過した音声信号が、 最後に通るアッテネータです。この出力が最終的な音声出力となります。
近いうちにこれらの回路をデバッグします。CPU で制御してピーピー鳴らしたいです。
PSG を Verilog-HDL で実装してみました。 一応波形を出力していますが、 実際に演奏を聞いたわけではないので、 ちゃんと動いているかどうかわかりません。
相変わらず減衰器(ATT)には悩まされてます。 上に書いたアナログ回路の製作は、踏みとどまりました(笑)。 あれを作るくらいなら FPGA を1個追加した方が楽でしょう。 というわけで多少ゲートを使うのを大目に見て、ATT をすべてデジタルで実装しました。 上ではシフト演算でがんばってみましたが、今回は素直に乗算器を使いました。
3種類の ATT は、実は減衰量を足し合わせることによって1つにまとめてしまうことができます。 なぜかというと、これらの ATT はすべてログスケールで、なおかつ、
CH-ATT と、L-ATT/R-ATT, M-ATT は、1ステップあたりの減衰量が違います。 CH-ATT が最も細かく、5 ビット 32 ステップで -1.5[dB]/step です。 これに対して、L-ATT/R-ATT と M-ATT は 4ビット 16 ステップで -3.0[dB]/step です。 よって、L-ATT/R-ATT と M-ATT の1ステップ分の減衰量は、 CH-ATT の2ステップ分の減衰量と同じです。
1つの ATT にまとめるからには、1ステップあたりの減衰量を統一しないといけないので、 L-ATT/R-ATT と M-ATT を 5 ビット -1.5[dB]/step とし、1ステップ飛ばしで変化させることにします。 要は L-ATT/R-ATT, M-ATT の減衰量入力値を 2 倍するかわりに、1ステップあたりの減衰量を半分にするわけです。
なんか冗長な説明で申し訳ないですが、とにかく、こうすれば、 あとは 3 つの ATT に入力されたステップ値を足し合わせて、その合計の分だけ 減衰させるだけで OK です。こうして1つにまとめた ATT を、ここでは 複合 ATT (CATT) と呼ぶことにします。
CATT のステップ値の合計は、
あと、1つにまとめるといっても、LとRをまとめてしまうと出力がモノラルになってしまうので、 CATT は左右1回路ずつ、合計2回路必要です。
出力は 16 ビットとしました。 上位5ビットが整数部、下位 11 ビットが小数部です。 CATT 内部で 16+5 ビットになっていますが、 最終的に 16 ビットに戻しています。
あとは PSG 本体ですが、疲れたので要点だけ書きます。
"PSG" の下に "PsgChannel" が 6 回路ぶら下がっているイメージです。 PSG は全体のとりまとめを行ない、PsgChannel は波形出力を行ないます。
PSG は 3.579545[MHz] で動作しますが、CATT を 6 チャンネル分用意したくなかったので、 システムクロック 21.47727[MHz] = 3.579545*6[MHz] (!)を利用して 6 チャンネル分を順番に 減衰させています。その副作用として、全チャネルを同時に発音しても、 そのうちのいくつかのチャネルの出力が常に 1/3579545 [秒] だけ遅れますが、 そもそも PSG 自体が 2 チャネル以上を同時に発音開始できない構造になっているのでよしとしました。
あと、PSG は 3.579545[MHz] で動作すると言いつつ 7.159090[MHz] を使っているのは、 こうしないと CPU の動作と同期が取れないかなーと思ったからです。 でもこれだとこんどは CPU を 1.789772[MHz] で動かしたときは CPU --> PSG の書き込み動作がおかしくなりますね...。
ノイズチャネルとLFOは未対応です。
ためしに ALTERA Cyclone 向けにコンパイルしたところ 1330 LE でした。 結構でかくなってしまった...
海外の友人から W65C02S を送ってもらったので、回路図を描いてみました。 これで、作ったモジュールの動作テストをしようと思っています。

CPU, ROM, RAM, XTAL 周辺以外は CPLD で実装する予定です。 XC9536PC-44 に余裕で入るはずです。 汎用ロジック IC は CPLD に比べてゲートの遅延時間がけっこう大きいので、 その分高速なメモリを使う羽目になるので使わないことにします。
FF 3 個で構成しているのはデューティー比 50% の 1/3 分周器です。ちゃんと 21.47727[MHz] の XTAL を買ってきたので、1/3 分周して、 7.159090[MHz] で CPU を動作させようと思っています。 回路の動作波形はこんな感じになります(注意: ↓の修正版の動作波形です)。
__ __ __ __ __ __ __ __ __
CLK __| |__| |__| |__| |__| |__| |__| |__| |__|
_____ _____ _____
FF1Q _____| |___________| |___________| |_____
_____ _____ _____
FF2Q ___________| |___________| |___________|
_____ _____ __
FF3Q ______________| |___________| |___________|
________ ________ _____
PHI2 ___________| |________| |________|
汎用ロジック IC を使うと上の回路図のような感じになると思いますが、1/3 の分周器は
[Nov.20 2005] クロック分周器の Verilog-HDL 記述 で
すでに Verilog-HDL 記述してあるので、CPLD ではこっちを使うことにします。
割り込みは /IRQ に IRQ1 を、 /NMI に /TIRQ を割り当てることにします。
あとは 154, 138, 00 でアドレスデコードをしています。 PCE の構成とはかなり違いますが、周辺チップの動作テストを目的としているのでまあいいでしょう。 ROM 32kB, RAM 32kB なので、CPU の A15 が 'L' だと RAM, 'H' だと ROM が選択されます。 I/O は ROM エリア ($8000-$FFFF) に書き込みサイクルを実行することにより......しまった。 これだと I/O から読み出しができませんね(ROM の出力と I/O 出力が衝突する)。 あいたー。というわけで修正します。^_^;
ROM は、とりあえず 27256 にしてありますが、EEPROM 等でもかまいません。 私はもちろん、上で作った ROM エミュレータを使う予定です。
ところで、上の XTAL 周辺の R と C はどれくらいが相場なんでしょうか。 ちょっと調べたところ、R は 1[MOhm] 〜 10[MOhm]、 C は 10[pF] 〜 20[pF] が相場のようです。 最近中古で購入したヘボいオシロスコープで波形を観測できるように、1/2 分周器をつけました。

というわけで試作してみました。 この程度の回路でも実際に配線してみるとけっこうごちゃごちゃになりますね。 C は周囲の温度の影響を受けないように、ディップマイカとかフィルムのやつを 使うといいらしいですが、そんないいものは手持ちになかったので、普通にセラミックコンデンサ でいきます。

オシロスコープで波形を観測してみました。おお〜、何か出ている!

上の 65C02 の回路は途中で修正が面倒になったので Verilog-HDL で記述してしまいました。 というわけでとりあえず放置しておきます。1/3 分周器だけは修正したので、ここに載せておきます。

というわけで、だんだん何をやってるのかわからなくなってきましたが、 まだまだおもいっきり寄り道しようという気持ちは変わってません。 (^_^;
アドレスデコーダ+クロック分周器の Verilog-HDL をデバッグしました。
メモリマップは、次のよう決定しました。
I/O MODE: $0000-$3FFF RAM (16kB) $4000-$47FF VDC $4800-$4FFF VCE $5000-$57FF PSG $5800-$5FFF Timer $6000-$67FF Pad $6800-$6FFF IntCtrl $7000-$77FF CdRom $8000-$FFFF ROM (32kB) RAM MODE: $0000-$3FFF RAM (16kB) $4000-$7FFF RAM (16kB) $8000-$FFFF ROM (32kB)
RAM は 32kB のものを使い、16kB ずつ切り替えて使います。 6502 系はメモリ空間と I/O 空間の区別がないので、Z80 のようにそのまま 32kB ROM + 32kB RAM という構成にすると I/O を配置するスペースがなくなってしまいます。 また 6502 では $FFFA - $FFFF に割り込みベクタを配置する必要があり、 ゼロページ+スタックが $0000-$01FF に固定なので、 RAM は $0000 から配置し、ROM は $FFFF まで配置する構成が普通でしょう。 とすると、 I/O は RAM と ROM の「狭間」に配置することになります。ここでは $4000-$7FFF に I/O を配置しています (I/O MODE のとき)。
$8000-$FFFF にゼロを書き込むと "I/O MODE", 1を書き込むと "RAM MODE" になります。 RAM は 16kB ある時点ですでに PC Engine の倍もあるので充分なんですが、 32kB RAM の方が入手しやすいのでバンク切り替えで一応 32kB 全部使えるようにしてあります。
あとはクロック分周器ですが、CPLD のピンが余っているので、 とりあえずクロックは 21.47727[MHz] をシステムクロックとして入力し、 10.73863[MHz], 7.159090[MHz], 5.369317[MHz], 3.579545[MHz], 1.789772[MHz] を出力するようにしました。
最近 ALTERA の EPM7160ELC84-10 というチップを格安で手に入れたので、 今回はこいつを使ってみようと思ったら、デザインソフトは MAX +PLUS II じゃないとダメで、 さらに MAX +PLUS II は標準で Verilog-HDL をコンパイルしてくれず、 しかも最終的にプログラミングするには APU という特殊な(?)ハードが必要ということが 判明したので使用を断念しました。イター!
というわけで CPLD 待ち状態 になってしまいました。
CPLD を手に入れたので、ゴァーっと作ってみましたが、動きませんでした。 なんでだー!...残念...また来週末か...これどうやってデバッグしたらいいんだろう...

一応、アドレスデコーダとクロック分周器のほかに割り込みコントローラとタイマーも入れてみたんですが、 動かないのに書いてもあんまり意味ないですね...クロック波形は見れるのでクロック発振回路と分周器は 動いているようです。ROM エミュレータはまだつないでいません。ROM エミュレータのかわりに EEPROM (HN58C256P-20) をつないでいます。こいつは多分アクセス速度が 200[ns] なので、とりあえず 3.579545[MHz] で CPU を 動作させていますが、これでもまだ速すぎるんだろうか...
手動クロック(スイッチを押すと1クロック打つ回路)を作って、 リセット後 CPU のアドレスバスの値をテスターで1ビットずつ拾ってみました。 回路は参考書籍「CPU の創りかた」の回路の時定数を若干変えただけです。

1 ffff (←リセット後1クロック目は全てのビットが1) 2 0724 (←謎の値) 3 01fc (←スタックにアクセス プログラムカウンタH保存) 4 01fb (←スタックにアクセス プログラムカウンタL保存) 5 01fa (←スタックにアクセス フラグレジスタ保存) 6 fffc 04 (←リセットベクタL読み出し = 04) 7 fffd e0 (←リセットベクタH読み出し = e0) 8 ???? (←ここでおかしくなる)
1 〜 2 クロック目は、確か公式資料では「内部動作」とか書かれていたと思うので とりあえず何でもよしとします。(← Feb.10.2006: 公式資料ではなかったです)
リセットは割り込みの一種と考えられているため、3 〜 5 クロック目で、 アドレスバスはスタックにプログラムカウンタとフラグレジスタを保存するかのような 動作をしています。しかし、調べたところ、実際には R/W 信号が 'L' にならないため、 これらの動作は無効のようです。
意外にも 6 〜 7 クロック目でデータバスにリセットベクタ $E004 が出力されています。 このリセットベクタは ROM の $FFFC 番地と $FFFD 番地に入っています。 ...ということは ROM からデータ読み出しはできているということ...? だとしたら、 8 クロック目で CPU は $E004 を出力するはずです。 でも CPU は $E004 を出力してこない...。 ROM はちゃんとデータを出力しているけど、CPU がそれを読んでいないということ...? データバスに、しかるべきタイミングでデータがちゃんと現れているのに、CPU がそれを読んでいない...? どうやったらそんなことが起こるのか... CPU のデータバスの結線が間違えているのか... ...うーむ。
CPU のデータバスの結線が逆でした。
8 クロック目のアドレスバスの値は $0720 でした。 2 進数だと 0000 0111 0010 0000 です。 8 ビットを一組にして、ビットを逆順に並べ替えると 1110 0000 0000 0100。 これって 16 進数で $E004 ですね。
まぁ、なにはともあれ CPU はきちんと動作していたみたいです。 今日は修正する時間がもうないので修正作業はまた明日... 配線をたった 8 本つなぎかえるだけですが、 基板の裏はかなりボーボーでハンダゴテがうまく修正箇所までとどかなくなっています。
動きました(動作の様子は↑の写真と同じなので省略)。
タイマーからの割り込み信号を 65C02 の NMI に入力し、NMI ルーチン内で、 割り込みコントローラ(IntCtrl)に書き込みを行ない、割り込みを承認するようにしました (これで IntCtrl 経由でタイマーからの割り込み要求は取り下げられる)。 65C02 の NMI ピンをオシロスコープで観測してみると、 ちゃんと一定周期で 'L' と 'H' を繰り返していました。
やはりボード右下にある CPLD が今回の CPU ボードの肝。 この中に PC Engine のハードウェアの一部(タイマー と 割り込みコントローラ) が実装されています。CPLD にたくさん回路をつめこんだので、 これがなかったらボードの面積は↑の3倍〜4倍必要だったのではないかと思います。 あとは FPGA に PSG を実装すれば、ピーピー音を鳴らして遊べそうです。
回路図と Verilog-HDL ソースと 65C02 のテストプログラムは週末に掲載します。
回路図と CPLD のソースを掲載します。
65C02 プログラムは、PCEAS でアセンブルしてください。
これでやっといろいろ遊べそうになってきました。 これ作っている暇があったら CPU を記述できたかもしれませんが。(^_^;
まだバグってました。65C02 のロッキングエッジはφ2の立ち下がりなのに、 Timer と IntCtrl のポートの読み書きはφ2の立ち上がりで見ていたので、 7.159090[MHz] では動作しませんでした。内部でφ2(Clk7M159)を反転したものを Timer と IntCtrl に入力するようにしました。↑に修正版を載せました。
あと、このボード用に ROM emulator を若干修正しました。 修正した回路図と CPLD ソースを置いておきます。 ホスト PC の制御プログラムはそのまま使えます。

もうバグはないことを祈りつつ...
まだバグってました。(^_^; 裏 RAM にアクセスできてませんでした。 バグFIX2として↑を修正しました。 さらに 65C02 テストプログラムに RAM テストコードを追加しておきました。
↓問題の箇所はここです。
always @(posedge i_RW)
begin
if (w_ExRAM)
r_ExRAM <= i_D0;
end
当然といわれればそれまでですが...。 ↓こうしたら解決しました。
always @(negedge o_Clk7M159)
begin
if (w_ExRAM & ~i_RW)
r_ExRAM <= i_D0;
end
やってはいけないことは、やってはいけないということですね。ハイ。
SPARTAN 3チップを購入しました。 私のヘボカメラでは、ピン同士の隙間がくっきり撮れません(ピッチ0.5mm)...。

こんな細かいピッチではまだ使用可能な状態とはいえないので、論理的にはまだ私はこのチップを 「入手した」ことになっていません。(^_^;
↓のピッチ変換ボード上に 208 ピン全ての半田付けを間違いなくできて初めて「入手した」ことになります。(^_^;

...意外とすんなりできました。私がいろいろ調べているときに、 実装の大きい画像をあまり見つけることができなくて残念だったので、 大きい画像を載せておきます(クリックすると別画面で非縮小版が見れます)。 ...とはいえデジカメがヘボすぎるので、画像がでかい割に細部が鮮明に見えませんが。

最も注意しないといけないのはICパッケージの位置出しです。 ICの足は非常に細かいので、基板に良質な予ハンダをしておけば、 あとはICの足の先端をコテ先で上から軽く押さえるだけでそこそこ良好なハンダ付けができます。 私はこんな感じでハンダ付けしました:
おつかれさまでした。 細かいハンダ付けが終わったらコーヒーでも飲んで休憩しましょう(笑)。
PC Engine を本格的に動作させるためのハードウェアの製作を開始しました。 とはいえ、XC3S200 に PC Engine をすべて実装することは難しいようです。 私は多分無理だと思うので、ボードを追加するか、XC3S400 に乗せかえる方向で検討しています。 幸い、XC3S200 と XC3S400 は QFP208pin だとピン配置がほぼ同じなので、チップ+ピッチ変換ボード(↑参照) を交換するだけで乗せかえは完了するはずです。

構成の変更なしで、ファミコンもこのボードで実装可能にしてやろうと たくらんでいます。(^_^;
QFP-51 は 26x2列 のピンソケットを使うと完璧なんですが、25x2列 を買ってしまったので、 ところどころすき間が空いてしまっています... 13x2列 を 2個つなげるという手を考えるべきだった...
はだ配線はできていませんが、32kB の SRAM を3個実装しています。1つは MAIN RAM (8kbit x 8) + BRAM (8kbit x 8)、残りの2つは VRAM (32kbit x 16) として実装します。 XC3 は I/O に 5[V] を使えないことを忘れていました。何も考えずに SRAM を買ってきたら、 3.3V で動作するものはコレだけだったので、自動的にコレを使う羽目になりました。
LM317T の VREF = 1.25[V] を VCCINT として使います。 VCCINT は、通常 1.20[V] を供給します。 VCCINT の定格は -0.5[V] 〜 1.32[V] です。 LM317T の VREF の変動範囲は ±0.05[V] っぽいので、 1.20 <= VREF <= 1.30 [V]、よって VCCINT の定格を 満たすと判断しました。

3電源はつらいです。電源の配線だけでこんなになってしまいました。 つ、疲れた...(2度と作りたくねー)。 パスコンがやけに多いのは、誤動作が怖いからです。 VCCIO 3.3[V] と VCCAUX 2.5[V] は、 表面実装用の3端子レギュレータを使います。一番上にあるレギュレータで 1.25[V] を生成しようと思ったんですが、LM317T と同じ回路を組んでも 1.25[V] にならなかった(Adj 端子を GND に接続すると 2.5[V] を出力する仕様になっている??) ので未使用になってしまっています。
なんとか電源の配線は終わったので、ちょっと休憩してから、 デザインソフトからチップを認識するかどうか試してみることにします。
なんとか認識するところまできました。
XC3S では、JTAG 専用ピンのロジックが VCCAUX を使います。 よって、通信は 2.5[V] ロジック (LVCMOS25) で動作します。 以前、 OT で CPLD にプログラムを書き込むための小さなプログラマを製作しましたが、 アレは "Parallel Cable III" と言われるものらしく、通常 5.0[V] で動作します。
そこで、 2.5[V] で動作するロジックに 5.0[V] の入力を加えて大丈夫かという話になりますが、 XC3S の場合、5.0[V] どころか、3.3[V] でもそのまま入力するとロジックを破壊してしまいます。 ただ、これについては、Xilinx のページに対処方法が載っていて、入力に電流制限抵抗 RSER を入れてやると OK なのだそうです。 RSER の値は、 3.3[V] のときは 56[Ohm] 以上、 5.0[V] のときは忘れましたが 330[Ohm] だったと思います(5.0[V]使う人は確認してください)。
改めて Parallel Cable III の回路図を見てみると、既に 100[Ohm] の抵抗が出力にシリーズに 接続されているので、Parallel Cable III を 3.3[V] で動作させれば、入力の破壊については 心配なさそうです。
というわけで、3.3[V] を使えば問題なし...と思ったら、まだダメでした。 調べてみると、3.3[V] では 74HC125 の伝播遅延時間が問題になるらしく、 通信に失敗することが多いらしいです。この情報は、 なひたふ氏のページの「DWM付録Spartan3基板を使おう」に詳しく書かれています。
氏のページに書かれている通りに、74HC125 を 74AC125 に交換し(IC にハンダ直付けで、しかも配線がゴアーってなってたので大変だった)、 3.3[V] で動作させたところ、問題なく認識されるようになりました。

↓試作、しかも動くかどうかもわからない半信半疑の状態ということで、超テキトウにつないでいます。 こんなんじゃいつチップを壊してもおかしくないな...

あー大変だった...。では今からここまでの回路図を描きます。でき次第更新します。
回路図描きました。XCF02S も追加実装&認識しました。

VCCINT の 1.2[V] は、LM317 だと絶対定格は満たしますが、 推奨動作範囲からは少しはみ出てしまうためちょっとマズいです。 とりあえず今回は試作なのでOKとします。
Parallel Cable III の 74HC125 を 74AC125 に変えたら、 確かに XC3S は問題なくコンフィグできるようになりましたが、 逆に CPLD のコンフィグができなくなりました。 どうも反射が起こっているようです。 特に TCK が影響を受けているようです。
しばらく悩みましたが、次のような回路を追加したところ問題なく動作するようになりました。

入力が Parallel Cable III の 74AC125 からの信号です。 出力は CPLD の TCK/TMS/TDI に接続します。 これでちょうど 74HC14 の6個のインバータを全部使いきります。 ためしに 74HC14 なしで、抵抗だけで接続してみましたが、うまくいきませんでした(逆に 74HC14 のみでもダメでした)。
↓は、↑の回路の考察&説明です。私が勝手に考えたので、 正しいかどうかはわかりません。
反射がどういうところで起こるのかというと、配線の終端で起こります。 IC (74HC14) の入力インピーダンスは無限大(びくとも動かない硬い壁)に近いため、 ここへドバっと押しかけてきた入力信号のうち、ごくごくわずか(限りなくゼロに近い量)だけが入力と して受け入れられます。受け入れられなかった大部分は、ほかへ行くしかないのですが、 ↑の回路のように、一つの信号につき入力が1つしかない場合は、ほかに行くところが ないので、ここがこの信号にとっての終端となります。しかたがないので、信号はここで 反射して 74AC125 の方へ戻っていきます。ちなみにここで起こるのは反射なので、 位相が逆になります。こいつが現在もしくは次のデータと干渉すると考えられます。
反射をなくすには、終端抵抗を入れるといいらしいです。 これは何かというと、信号を終端で GND へ吸収させるための抵抗です。 終端抵抗を入れておくと、反射するはずの信号は、抵抗を介して GND へ 吸収されていきます。
問題は、抵抗値をいくらにすればいいかです。 ここはよくわかりません。 考え方としては、74HC14 の入力の硬い壁を緩和し、反射を鈍らせます。 それでも反射した分は、終端抵抗で吸収します。 終端抵抗は、74HC14 の入力インピーダンス(≒無限大)を無視できるくらい小さい値で、かつ、 74AC125 の出力インピーダンス(数オーム〜数十オーム??)を無視できるくらい大きな値 ということで、ここでは 1[kOhm] としました。あとは、同じ 1[kOhm] で GND に 落とします。
イメージ的には、直列に入っている 1[kOhm] が壁の緩衝材で、GND に落とす 1[kOhm] が緩衝材の表面に塗った接着剤といったところでしょうか?ここに、 剛速球が飛んでくるわけです。まぁ、なんか、そういうことを考えながら作ったのが ↑の回路です。
考え方が合ってるかどうかはわかりませんが、 とりあえず動いたのでよしとします。
...というのは、今日の本題ではないわけですが(笑)。
音声出力用に、D/A コンバータを作りました。

FPGA の I/O を節約したかったので、FPGA からはシリアルで音声を出力し、 CPLD がこれを受信、パラレルデータに変換します。CPLD (XC9572PC44) の I/O の都合で、出力は 15 ビットステレオになりました。あとはこれをR−2Rラダー型 D/A変換回路でアナログに変換して出力します。

L と R を 15 ビットずつ、合計30ビットを 21.47727[MHz] で順にグリグリ出力します。 サンプリング周波数は 21.47727 / 30 = 715.909 [kHz] です。
せっかく 65C02 の CPU ボードを作ったので、FPGA にはしばらくの間 PSGになってもらうことにして、ピーピー鳴らして遊ぼうと思います。
遅くなりましたが、↑のDACの回路図と CPLD の Verilog-HDL ソースを掲載します。

回路図が変ですが、それは遊んでいるからです。(^_^;
回路図上は 1.5[kOhm] と 3[kOhm] の2種類を使っていますが、 実装は全て 3[kOhm] を使っていて、1.5[kOhm] は 3[kOhm] を2本並列につないで作り出します。 ↑の回路では合計 90 本の 3[kOhm] を使います。
R-2R 方式の DAC は、抵抗の精度が重要です。おそらく、同じロットの抵抗は相対誤差が小さいと思われます。 そこで、同じロットの抵抗が入手できることを期待して、1種類の抵抗を 100 本単位で大量に購入します。 私は 3[kOhm] 誤差5%のカーボン抵抗を 100 本買って抵抗値をざっと測定してみましたが、相対誤差は1%以内に入っていました。 ここで得られた1%の相対誤差というのは、1.5[kOhm] と 3[kOhm] をそれぞれこの値の抵抗で個別に実現するよりも 高精度だと思うわけです。誤差1%の金属皮膜抵抗を使えば、相対誤差はさらに改善されるはずです。
出力にバッファ・アンプが追加されていますが、 これは R-2R ラダー抵抗によって出力インピーダンスが結構高い値になってしまっているためです。 実際にはまだバッファ・アンプは未実装で、その効果は確認できていません。 よって出力インピーダンスが高いことが原因かどうかは現時点では不明ですが、 波形を観察すると出力が容量負荷に負けている感じで、聴いた感じは software emulation よりも明らかに音質が悪いです。

出力電圧がすべて+側に現れるので、DC成分を除去するために、 Cを直列に入れないといけないのがこの回路の弱点だと思います。 個人的にはバッファ・アンプの高インピーダンスの入力側に小さなCを入れて、 バッファ・アンプを2電源動作にしてしまう(出力側のCを省いてしまう) のも面白そうだと思っています。
以下最近プロジェクトの進行が遅い理由など...
「白夜行」,東野圭吾著,集英社文庫,\1,050 を読んでました。 すばらしい本でした。著者に感謝!
加えてこの季節は予定が多く、自分の時間があまりとれません。 よってプロジェクトの方はぜんぜん進んでません。(^_^; ちょくちょく見に来ていただいている方がいるようなので、 ちょっと申し訳ないなーと思いつつ更新ネタを探してみたら、 これがまだだったことを思い出しました。 今回はこれでかんべんしてください。
FPGA のデバッグ用に RS232C 受信モジュールをテキトウに実装してみました。 115200bps, 8bit, start bit あり、stop bit ありです。 とりあえず 0 〜 255 の 256 バイトを受信できたので公開します。 ストップビットがない等の場合の動作は不定です。
入力クロックはとりあえず 7.159090[MHz] ですが、何でもいいです。 7.159090[MHz] を使う場合は、115200bps の通信速度を得ようと思うと、 7159090/115200 ~= 62 なので、62 分周器を作る必要があります。 ここでは 64 分周器を作ることにします。逆にこのとき、 7159090/64 ~= 111861 なので、baud rate の誤差は (115200-111861)/115200 * 100 ~= 2.90[%] になります。1転送単位で、1バイトをスタートビット+8ビット+ストップビットで転送します。 これで合計 10 ビットになるので、baud rate の誤差は 100[%] / 10 = 10 [%] 未満なら大丈夫(?) ということになります。よって、2.9[%] の誤差は多分大丈夫でしょう。
1/64 分周器は、6 ビットのバイナリカウンタで構成します。 bit5 の立下りが発生したら 64 カウントです。 通信速度の誤差は送信側のスタートビットを検出するごとに、 分周器をリセットすることで、修正します。
入力信号 i_Rxd は、PC の COM ポートを MAX232 または互換品を介して CMOS レベルに変換したものを想定しています。
↑の RS232C で FPGA に曲データを送って PSG を演奏させてみました。
試しにゲームの曲を送ってみたら、実機さながらの演奏でした。 実際にお聴かせできないのが残念です。 PC Engine の PSG 用の BGM データって PD でないですかね... ちょっと探してみます。
115200bps なので、1フレーム(1/60秒)に送れるデータはたかだか 115200/60/10 = 192 バイトです。 これを超えると処理落ちしてしまいます。(^_^; (今から思えば USB→パラレル を搭載した方がよかった)
ノイズも実装してみましたが、 ノイズ付きの "PsgChannelN" を PSG に接続するとなぜか全体が動かなくなります。 クロックのスキュー、遅延などいろいろ疑ってみましたが、 まだ原因がはっきりしません(ハマった...)。 なんでだー!
↑では勢いで 15-bit ladder DAC を製作しましたが、 なんとなく、 1-bit DAC の方が良さそうな気がしています。 でもまぁ、せっかくなので、 1-bit DAC を作ったら 15-bit ladder DAC と聴き比べてみようと思います。
PSG の最新のソースを掲載したいところですが、 NEC-HE の特許に触れると厄介なので、とりあえず見合わせています (金儲けをしようとしているわけではないので問題ないと思いますが...)。 どうしても欲しい方はメール下さい(以後この形式をとります)。

というわけで、今回は中身がないですが、この辺で。
更新ネタがないので、演奏データをちょっと作って FPGA、PC2E (software emulation)、本物の実PCエンジン で演奏を比較してみました。大した物ではないですが。 しかも FPGA はまだ若干バグいため比較は時期尚早かもしれません。
曲自体はFF5の「ピアノのおけいこ7」から耳コピしました。 データは MML で打ち込んで HuSIC-Watch(HESw0073) で HuSIC ドライバ用にコンパイルしました。
こうして聴き比べてみると、実PCエンジンの音は結構ローパスフィルタが効いているのがわかりますね。 ちなみに FPGA 版の出だしがモタついているのは、 RS232 の転送が遅いからです。
FPGA と PC2E はよく似た感じですが、私は PC2E の方が断然いいと思います。 FPGA の方は音の立ち上がりで「ボツボツ」音が耳につきます(オーバーシュート音??)。 PC2E と 実PCエンジンの演奏はスッと耳に入ってくるのに、FPGA のはものすごい不快な感じがします。 このへんはバグなのか、負荷が重いからかはっきりしません(多分バグですが)。
ところで、もっとカッコイイ自作HESデータを提供して下さる方、いませんか?
CPU の作り方間違えてたっぽいです。 XC3S200 のゲート使用率 70% を超え、 実用に耐えられなくなってきたので、開発途上品をここに放置することにします (これだけ書かないと間違いに気が付かんのか...>私)。 なぜ 70% を超えるとダメなのかというと、PSG がだいたい 30% を使っているからです。 XC3S200 1個に CPU と PSG くらい入れてしまわないと、最終的に XC3S200 を3個くらい 使うことになりそうで、あまり自己満足感が得られない気がするので、 ここでちょっと考え直すことにしました。
冒頭にある構造のコメントはかなりウソですので、信じてはいけません。(^_^; だいたいの構造はあってますが、細かいところで結構違います。 設計らしい設計もしていません。 かなり恥ずかしい作品ですが、ダメな見本ということで...
BRK, RTI と 割り込みまわりを実装してバグとりすれば CPU としてなんとか動くかもしれません。まぁ何がどうであれ、既にエリア優先で 1300 slice くらい使っているので、利用価値はあまりないでしょう。
命令デコーダをある程度マニュアルでデコードしながら書かなかったのが敗因です。 このへんはコンパイラがうまくやってくれるかなーと思ってましたが、甘かったです。 出直してきます。
あと、ブロック転送命令ですが、あれは転送前準備と後始末を除けば、 1回の転送を 6 クロックで完了するらしいです。 8 ビット/クロックで処理しようとすると、
次の新型は、ブロック転送命令を最初に実装することにします。
PCE のプログラムを実機上で実行するたびに ROM emulator を接続するのがいい加減面倒になってきたので、 開発ボードを作ってみました。DEVELO BOX 参考に 74HC157 で構成しました。ただ、DEVELO BOX は RS232C を使いますが、こちらは LPT のみで PC ←→ PCE 間の通信を行ないます。



74HC14 と 1[kOhm] の抵抗は、この間 JTAG の通信で経験した、 反射と思われる現象をあらかじめ回避する目的で実装してあります。 回路図は 1[kOhm] ですが、実際に製作時する段階で大量の 1.5[kOhm] が目に留まったため、1.5[kOhm] を使うことにしました。多分問題ないと思いますが、 まだ動作確認ができていないのではっきりとは言えません。
今回は、回路図エディタ上で配線パターンも考えました。 これをやっておくと、製作時に基板の裏表の配線方向を考える必要がないため、 ハンダ付け作業がかなり短縮化されます。 私の感覚では、オンザフライで配線を考えながらハンダ付け作業をする場合に比べると 半分以下の時間で完成できたように思います。 また、ハンダ面での配線ジャンプ(作業時間を食う)をなるべくしなくて良いように、 あらかじめ考えておけるので、見た目もキレイになりやすいと思います。 今回はむしろ部品面の方が汚く見えます。
D-SUB 25ピンのメスコネクタは、1段目と2段目の間に基板を挟んで、 ハンダ付けしています。こうやって装着すれば、 基板実装用の D-SUB 25 ピンコネクタとピッチ変換基板を使わずに済むので経済的です。
あと、作ってから気が付きましたが、74HC157 のパッド入力側(A 入力側)がプルアップされていません。 この構成では、74HC157 の A 入力側のレベルを固定するために、開発ボード使用時は PCE パッドを常に接続しておく必要があります。パッドを接続しなくても問題ないようにしたい 場合は、74HC157 の A 入力側 (2, 5, 11, 14 ピン)をプルアップしてください。
ハードはこれでほぼできたので、あとはソフトです。LPT からプログラムを受け取って実行する 簡単なプログラムを HuC か PCEAS で記述し、PCE で起動可能な CD-ROM を作成する予定です。 果たしてうまくいくかどうか...
PC − PCE 間の通信の質が劣悪で、どーーしてもうまくいきません。 PCE → PC の方がダメっぽいです。 あまりにもひどいので、ポートを4回読み出して、4つとも同じ値のときのみ そのデータを使うようにしましたが、それでも 8kB 転送すると1〜2バイトは $00 が $10 とか $20 とかに化けてしまいます。チェックサムを送って 一致しなかったらやり直すとかいう手はありますが、8kB 転送するのに何回も やり直すのはちょっと...
信号をオシロで見てみましたが、 反射しているような感じではなく、 結構キレイに出ていました。 なんでこれでうまくいかないんだ...
通信中は PC が LPT をポーリングするため CPU 使用率が 100% になり、 ファンがやたらうるさいです。これでさらに士気低下。
CD-R だと PCE の起動がなかなかうまくいかないようです。 ずっとジージーやっていて1分以上待たされることもザラです。 これでますます士気低下。
もうやる気がほとんどなくなったので この辺で半永久的に中止します(このままでは週末がコレだけで終わってしまう...)。 また似たようなことをするときにコレを思い出して生かすことにします。
気が付いた人がいるかもしれないので一応書いておきますが、 上のハンダ面の写真は1箇所ハンダ付け忘れがあります。 現在は修正済みです。
プログラム一式をここに放置
しておきます。
↓ちょっと見たい人用。
pcedev_pce.c というのが PCE 側、pcedev_pc.c が PC 側のプログラムです。 ハンドシェイクで通信するプログラムを初めて書きましたが、 けっこうややこしいですね(書き方もヘボい)。 まだバグっていますが、もうこんなものは放置、放置。
PCE のプログラムは、
PC のプログラムは、 Craig Peacock 氏の PortTalk を利用させていただいています。PortTalk のファイルを同じディレクトリに置いて、 MinGW で
最近失敗ばっかりだ...
若干改良を加えて動くようにしました(復活)。 ↑の回路できちんと通信するのはかなり辛いことが判明しました。 とりあえず改良版の回路図を載せておきます。

D-FF (74HC74) を追加しました。 これはなんでかというと、PCE → PC 通信の際に:
1の場合は、 CLR = 0 つまりデータビットがゼロのときに 入力 D0-D4 が(74HC157により) パッドになってしまい、LPT が読めないため、 ここでハンドシェイクが破綻します。
2の場合は、LPT を読むときに CLR = 1 になってさえいれば 良いわけですが、やはり CLR = 0 から CLR = 1 にする前に LPT を見る必要があるため、ここでハンドシェイクが破綻します。
このことに気が付かずにプログラムを書いていたので、 PCE → PC 方向の通信がめちゃくちゃになっていました。
というわけで、 D-FF を利用することにしました。こいつを↑の回路図 のように接続すれば、SEL を 0 → 1 に遷移させたときの CLR の値を保存してくれます。 以後、再び SEL を 0 → 1 に変化させるまで、CLR を自由に変更することが できます。これで、1の場合でデータビットがゼロのときは D-FF にゼロを 保存してから CLR = 1 にして LPT を読むことができます(2の場合は省略)。
PCE パッド側のプルアップ抵抗も追加しておきました。 5[V] 動作で 1.5[kOhm] だと、Lのときに抵抗1本あたり数[mA]消費してしまうので ちょっと無駄が多い気がしないでもないです。気になる人は抵抗値を大きくしてください。
ソフトはもうちょっと待ってください。(^_^;
ちょっと改良してから掲載しようかと思いましたが、 余力がないためそのまんま掲載します。
以下は↑のファイルの簡単な説明です。ソフトの動作の詳細はソースを読んでください。
PC 用ソフトは、PCE からのリクエスト待ちのときに PC の何らかのキーが押されると終了します。 この時点から次に PC 用ソフトを起動するまで、PCE からのリクエストに応答できませんが、 PCE 用ソフトは応答があるまでしつこく同じリクエストを繰り返すようになっているので、 次に PC 用ソフトを起動したときに、続きが処理されるはずです。 開発中に1度だけ処理が続行されなかったことがあったので、 まだ何かバグっているかもしれません。
最後の6が終了すると、PCE 側は2〜6を延々と繰り返します。 よって、6の転送中に、PC の何らかのキーを押すと、キリの良いところで 処理を中断できます。ここは、単に私がそうしているだけなので、 テキトウに変更して使ってください。
startup.pce が起動した時点で、既存のコードを破壊することなく PCE が実行に使えるバンクは、 bank4 くらいだと思います。その他を使いたい場合は全てを初期化して使う(=PCE 用ソフトも破壊する) 覚悟がいると思います。なお PCE 用ソフトは bank2($4000-$5fff) と bank5($a000-$bfff) に存在します。HuC 関係のコードは bank3($6000-$7fff) と bank6($c000-$dfff) にあります。 うまく既存のコードと共存するコードを書くか、全てを初期化して自分のモノにするかは 使用者次第です。ただし bank7($e000-$ffff)は BIOS (ROM) なので最低限 BIOS とは共存する 必要があります。
startup.pce は、
pcedev.asm の receive_byte ルーチンは動作未テストです。
6 で file.bin の転送が異様に遅いのは、この PCE 用ソフトの PCE → PC 転送プログラムが HuC で書かれているからです。5 の PCE → PC 転送プログラムはアセンブラで書かれています。 2〜3倍の速度差があるように感じます。 HuC6280 は、ソフトをアセンブラで書くことに意味があった、古き良き(?) CPU と言えると思います。
PC ←→ PCE 間転送をするときは、あらかじめ割り込みを禁止しておく必要があります。
PortTalk を利用した PC のパラレルポートの読み書きは、測定したところ 120k回/秒 程度でした。 初代 Pentium でも MS-DOS モードでは 1M回/秒くらい出ていたので、 パラレルポートの読み書きをもっと高速にできれば、通信は飛躍的に速くなると思います。 現状は、アセンブラコードで 8kB の PCE → PC が数秒、PC → PCE がその 1/3 程度です。
↑にも書きましたが、CD-R を PCE に読ませるのは結構つらいらしく、何回も失敗します。 PCE 用ソフトは、1度読み出せば、暴走させない限り、なるべく再起動しなくてもいいように 作りましたが、最初の読み込みが遅いのは結構痛いです。開発中に何枚か CD-R を焼くハメになりましたが、 記録面の色が暗い物(ライトブルーよりはダークブルー)の方がまだ読み込みの失敗が少ない気がしました。

これで、テストコードをゴリゴリ走らせることができるようになりました。
これは失敗とまではいかないかもしれませんが、 実装が気に入らなくなったので再び放置決定しました。
前回製作時にこの辺をテキトウにやってひどい目に遭ったので、今回は少し練ってみました。 未テストです。パスコンの配置は適宜行います。(^_^;

久々の更新です。「まだやってたの」とか思われてそうですが。(^_^;

こんなところです。 ↑の電源パターンは使わないかも...(^_^;
こんなところです。
おもいっきりバグってました(^_^; たまたま動いていただけでした。 修正版に差し替えました。 修正後のプログラムでテスト回路を XC3S500E に configuration して実際に動作するのを確認しました。
とりあえず専用掲示板を設置しました。興味のある方は覗いてみてください。
以下のページを作成された諸氏に感謝致します。
本プロジェクトを進める上で参考になりそうな/なっている書籍を挙げておきます。