2022年11月7日月曜日

謎の遊技機コントロールユニットの分解

電子機器の分解一覧


謎の遊技機コントロールユニットの分解


注意:このサイトの内容を鵜呑みにし、事故や損失を招いた場合でも当方は一切の責任は負いかねます。自己責任でお願いします。



ハードオフでよくわからない遊技機コントロールユニットを見つけたので分解してみました。









装置本体に型番など記載されていなく、ネット上にも情報がないため何の装置のコントロールユニットかは一切不明です。

表面には「ディップスイッチの使用法B」と記載されたシールが貼られています。
「コイン枚数」「タイマー1」「タイマー2」「周回数」などの項目をディップスイッチで設定出来るようです。
また、右下の方にJASRACシールが貼ってあり、音楽を流す機能もありそうです。

側面には、ボリュームとトグルスイッチ、赤色のプッシュスイッチ、ヒューズボックスがあります。
トグルスイッチの上には「メリーゴーランド」のシールが貼られていて、さらにその上から「PITCHDOWN」のテプラが貼ってあります。

これらから推測すると、かつてデパートの屋上などで置かれていた乗り物のコントロールユニットでしょうか?



・分解







分解してみると、ROM、MCU、音源IC、オーディオアンプというシンプルな構成となっています。

今でこそ1チップマイコンで事足りるような構成ですが、当時はこれでもだいぶシンプルな構成であると伺えます。

パワートランジスタが5つありますが、電飾はともかくモータの直接ドライブ能力が無さそうなので、電飾・リレーのドライブ用であると予想できます。

基板裏のパターンには「SSG-101」の文字があるので、おそらくこれが型番だと思います。


詳しく見てみましょう。
ROMはNECのμPD27128が使われています。
窓遮光シールには「メリーゴーランド」と書かれています。


MCUは日立のHD63A03XPが使われています。
モトローラのMC6803(MC6801の外付けROM化) のCMOS版のシリーズのようです。

192ByteのRAMや汎用ポート、タイマー、SCIなどが内蔵されており、外付けのROMを用意するだけで組み込み装置ができます。


音源ICはGIのAY-3-8910 (PSG音源)が使われています。
矩形波3音、ノイズ1音を出すことができます。
基板裏のパターンに「YM2149」と表示があるので、YAMAHAのSSG音源の使用を想定して設計されたと考えられます。


他にもTC4584BP(シュミットトリガインバータ)やTC4013BP(D-FF)などが乗っています。


・曲の演奏
2種類あるようです。



・PCMの再生
人の声が再生されます。
かなり音質が悪く、合っているかわかりませんが次のように聞こえます。
--「ばいばいまたね」x2
--「いっぱいあそんでね」
--「こんにちは」
--「今日はどうですかな」
-- ノイズ音


・回路



・電源
基板裏の電源部分のパターンに「+14」の文字が入っているので、電源は+14の入力を想定しているようです。
3端子レギュレータは2つ付いており、パワトラ&オーディオアンプ用に12V、デジタル回路用に5Vを生成しています。


・MCU & ROM
この基板についているROMは27128で、16kBの容量があります。MCUがサポートしているアドレス空間は64kB($0000~$FFFF、A0~A15)なのでプログラムの容量はそこまでないと予想できます。
基板裏のパターンを見るとROMが付いている部分に「27512」と書かれたパターンがあるので、64kBのROMである27512が付けられる設計になっていると思われます。
この回路ですと、27512を取り付けた場合は特に問題は無いのですが、それ以外の低容量のROMを取り付ける場合は、ROMのアドレス範囲に気を付けなければいけません。

MC6800系のMCUの場合、リセットや割り込みなどが起きた際にベクタアドレスにある値を読み込み、その値のアドレスをPCへセットします。この「ベクタアドレス」は固定であり、しかも、アドレスの最後の方である$FFEA~$FFFF(HD63A03XPの場合)に割り当てられています。
MCUがベクタアドレスにアクセスして値を読み出せるようにする必要があります。もし、ベクタアドレスにある値が適当でない場合、プログラムの開始などができなくなります。
つまり、ROMはアドレスは$FFEA~$FFFFに被るように配置しなければいけないということです。(どうしてもアドレス空間の後ろにROMを配置したくなければロジックICでベクタアドレス部分にアクセスしたときに値を示す回路を入れればよいですが…)
もちろん、ROMをアドレス空間の後ろに配置するとプログラム内の絶対アドレスの指定もずらさなければいけません。

今回の基板には、16kBのROMである27128が付いていたので、ROM配置アドレスは$C000~$FFFFということになります。ROMの詳しい解析は後ほど記載します。

MCU周りは特に難しい部分は無さそうです。
HD63A03XPにはクロックジェネレータが内蔵されており、水晶振動子を付けるだけでクロック生成できます。基板には3.58MHzの水晶振動子が付いていますが、HD63A03XPには4分周回路が入っているので、実際の動作周波数は3.58/4MHzとなります。
リセット回路もRCで作られた簡易的なものとなっています。


・音源周り
音源ICとしてGIのAY-3-8910 (PSG)を使っています。
このAY-3-8910には音声生成以外に汎用ポートがあります。この謎コントロールユニットでは、この汎用ポートを積極的に活用しているようです。

1つは、PSG汎用ポートを入力として使い、ディップスイッチの状態のチェックに使用しています。ディップスイッチの役割はこの記事の冒頭で載せた写真を参照してください。

もう1つは、PSG汎用ポートを出力として使い、4bitの簡易音声DACとしています。
結構面白い使い方をしていますね。4bitのPCMっぽいですが、音質はかなり悪くなりそうです。バイナリ加重DACではなく、量子化幅が一定ではありません。DAコンバートした際のバイナリ-アナログの直線性を失います。サンプリング時に標本値の加工が必要になりそうです。
実際の基板裏にはまだ抵抗を付けられそうな空パターンがあったので、8bitDACとして設計したものの、DAC量子化問題やコストの問題などで4bit化したのではないかと予想できます。(8→4bitにすると音声データの量も半分になるため、低容量のROMで良くなる)

PSGのクロック源はMCUのクロック源を1/2に分周しているようです。


・その他
ボタンやコイン投入、センサなどの入力のチャタリング除去はシュミットトリガインバータ(TC4584BP)とRCで行っているようです。
この7つの入力のうち1つはMCUのNMI端子へ接続されています。NMIのベクタアドレスは$FFFC~$FFFDなので、ROM内の該当ベクタアドレスを覗いてみると$C000となっていました。$C000はROMの先頭アドレスなので、この入力NMI端子は緊急停止かなにかのスイッチなのではないかと考えられます。

他にも、IRQ2端子へ接続されている入力があります。IRQ2のベクタアドレスは$FFEA~$FFEBなので、ROM内の該当ベクタアドレスを覗いてみると$C003となっていました。おそらくコイン投入センサへ接続されているのでしょう。

それ以外の端子はMCUの汎用ポートへ接続されています。実際に入力してみると、何かしらの音声が再生されます。(音質が悪すぎてなんの音声かは判別不能です。)

コントロールユニットの側面には、赤色スイッチのプッシュスイッチと選曲セレクト用トグルスイッチが取り付けられています。

パワートランジスタは東芝のダーリントントランジスタの2SD633が付いています。
Ic=7A、Pc=40W、ヒートシンク無しなので豆球数個程度なら余裕でドライブできそうです。
さすがに人を乗せて何か動かすようなモータドライブは出来なさそうなので、モータはリレーを介しての駆動をしていそうです。



・ROMの解析

さて、最後にROMの解析を行いたいと思います。
回路起こしの結果からROMに含まれているデータは、
・プログラム
・演奏データ
・4bit PCMデータ
であろうと予想できます。

幸いにもROMのサイズが16kBとそれほど大きくないため、データのほとんどは演奏データやPCMデータであると考えられます。


・プログラム
ROMから吸い出したデータはすべてのデータが繋がった状態となっています。
プログラム、演奏データ、PCMデータのうちデータの先頭がすぐにわかるのはプログラム部分だけなので、まずプログラムを解析します。

プログラムは、機械語(バイナリデータ)となっているためこのままでは読みにくいです。
今回は逆アセンブルソフトのDASMxを利用してアセンブラで表示します。DASMxの使い方は省略します。

ROMデータのうち、まず初めに確認するのがベクタアドレスです。
この領域にはMCUのリセットや割り込みなどが発生した際に、PCへセットする値が記録してあります。
HD63A03XPの場合、$FFEA~$FFFFがこれに相当します。値は2バイト分で、偶数アドレスがMSB、奇数アドレスがLSBです。

HD63A03XPのデータシートを確認してみると、
$FFFE,$FFFF   ~RES 
$FFFC,$FFFD   ~NMI
$FFFA,$FFFB   ソフトウェア割り込み
$FFF8,$FFF9   ~IRQ1  
$FFF6,$FFF7   Timer1 input Capture
$FFF4,$FFF5   Timer1 Compare 1,2
$FFF2,$FFF3   Timer1 overflow
$FFF0,$FFF1   SIO
$FFEE,$FFEF   TRAP
FFEC,FFED   Timer2 Counter Match
FFEA,FFEB   ~IRQ2
の様に割り当てられています。

では、実際のROMデータを見てみましょう。
せっかくなので逆アセンブラしたデータを見てみます。
//逆アセンブラしたROMデータ最後尾////////////////////////////////
FFE8 : FF FF C0 "   " stx XFFC0
;
FFEB : 03 " " db $03
;
FFEC : FF FF C0 "   " stx XFFC0
;
FFEF : 00 " " db $00
;
FFF0 : FF FF FF "   " stx XFFFF
FFF3 : FF FF FF "   " stx XFFFF
FFF6 : FF FF FF "   " stx XFFFF
FFF9 : FF C0 00 "   " stx LC000
FFFC : C0 00 "  " subb #$00
FFFE : C0 00 "  " subb #$00
//////////////////////////////////
何やらプログラムがあるように見えます。ですが実際には全くのでたらめです。
基本的に逆アセンブラソフトはバイナリデータとニーモニックを1対1で結び付るだけしかしてくれません。よって、設定でもしない限り、プログラムではない部分もそのままニーモニックを付けてしまいます。
つまり、残念なことに、実は逆アセンブルしたデータを見ただけでは、プログラムデータかその他のデータなのかは判別できないのです。
判別するには、やはり順を追ってアセンブラを解読するしかなさそうです。

このデータはベクタなのでHD63A03XPのデータシートのベクタアドレスのテーブル(table1)を参考に見てみましょう。
//逆アセンブラしたROMデータ最後尾////////////////////////////////
FFE8 : FF FF C0 "   " stx XFFC0    //ベクタ(FFEA,FFEB)IRQ2                  "C003"
;
FFEB : 03 " " db $03
;
FFEC : FF FF C0 "   " stx XFFC0    //ベクタ(FFEE,FFEF)TRAP                   "C000"
;
FFEF : 00 " " db $00
;
FFF0 : FF FF FF "   " stx XFFFF
FFF3 : FF FF FF "   " stx XFFFF
FFF6 : FF FF FF "   " stx XFFFF
FFF9 : FF C0 00 "   " stx LC000    //ベクタ(FFFA,FFFB)ソフトウェア割り込み   "C000"
FFFC : C0 00 "  " subb #$00     //ベクタ(FFFC,FFFD)NMI                   "C000"
FFFE : C0 00 "  " subb #$00     //ベクタ(FFFE,FFFF)RES   
//////////////////////////////////

整理すると、
RES   (C000)
NMI   (C000)
ソフトウェア割り込み(C000)
TRAP   (C000)
IRQ2  (C003)
となりました。0xFFFFが入っている部分は意味をなさないので、無視しました。
プログラムはRESベクタアドレスにある値が先頭なので、$C000がプログラムの先頭となります。
他の割り込みなども1つを除いて$C000となっているので、リセット動作ど考えてよいでしょう。

IRQ2のみ$C003となっているので、位置検出センサや数え間違えの出来ない課金制御用と予想できます。


さて、プログラムの先頭は$C000と判明したので、アセンブラを読み解いていきます。
解説は省きますのでコメントを参照してください。
(コメントはあくまでこちらが予想した動作です。ミスや見当違いな部分も多くあります。)

//プログラム(DASMxで逆アセンブル、コメント付き)////////
C000 LC000: //プログラム先頭(ベクタ$FFFFで指定されたアドレス) C000 : 7E C0 6B "~ k" jmp LC06B //変数初期化LC06Bへ→LC006へ C003 : 7E C4 C2 "~ " jmp LC4C2 //~IRQ2割り込み ; C006 LC006: //演奏前準備:LC06Bより C006 : BD C0 CF " " jsr LC0CF //LC0CF(演奏前準備)へ C009 LC009: //LC009メインループ C009 : BD C0 A4 " " jsr LC0A4 //LC0A4(AY-3-8910ミキサー,I/O設定)へ C00C : BD C0 AE " " jsr LC0AE //LC0AE(ディップスイッチの状態を読み取り、X0089へ入れる)へ C00F : BD C0 DE " " jsr LC0DE //LC0DE(ディップスイッチの状態から「ディップスイッチの使用法B」の「コイン枚数」の番号をX007F(「ディップスイッチの使用法B」「コイン枚数」設定用)に入れる)へ C012 : BD C1 09 " " jsr LC109 //LC109(ディップスイッチの状態から「ディップスイッチの使用法B」の「タイマー2周回数」の番号をX0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)に入れる)へ C015 : BD C1 82 " " jsr LC182 //LC182(コイン投入ボタン(茶線)処理)へ C018 : 96 79 " y" ldaa X0079 //+/AレジスタにX0079(コイン投入モードフラグ)を入れる C01A : 81 01 " " cmpa #$01 //+/ C01C : 27 1E "' " beq LC03C //+/X0079(コイン投入モードフラグ)が1(コイン投入モード)の場合、LC03Cへ C01E : 96 15 " " ldaa X0015 //*/AレジスタにX0015(ポート5)を入れる C020 : 84 08 " " anda #$08 //*/A = A & 0x08 __(筐体側面の赤色プッシュスイッチ(コインテスト投入スイッチ)) C022 : 27 08 "' " beq LC02C //*/X0015(ポート5)のP54(筐体側面の赤色プッシュスイッチ)が0(押されたとき)の時、LC02C(コインテスト投入スイッチ処理)へ C024 : 96 89 " " ldaa X0089 //-/AレジスタにX0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)を入れる C026 : 84 80 " " anda #$80 //-/A = A & 0x80 __(「ディップスイッチの使用法B」「DIPAW8_ON:コイン_OFF:電源」) C028 : 26 02 "& " bne LC02C //-/X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)が0(DIPAW8「ディップスイッチの使用法B」「電源」)でない時、LC02Cへ C02A : 20 DD " " bra LC009 //LC009(メインループ先頭)へ ; C02C LC02C: //コインテスト投入スイッチ処理:LC009より C02C : C6 60 " `" ldab #$60 //+/Bレジスタに#$60を入れる(#$F0回ループ) C02E : BD C1 B1 " " jsr LC1B1 //+/ループウェイトサブルーチン,Bレジスタで設定 C031 : 7C 00 8A "| " inc X008A //*X008A(筐体側面の赤色プッシュスイッチ押された時間カウント用)インクリメント C034 : 96 8A " " ldaa X008A //*/AレジスタにX008Aを入れる C036 : 81 03 " " cmpa #$03 //*/ C038 : 27 02 "' " beq LC03C //*/X008A(筐体側面の赤色プッシュスイッチ押された時間カウント用)が3の時、LC03C(コイン投入モード)へ C03A : 20 CD " " bra LC009 //LC009(メインループ先頭)へ ; C03C LC03C: //コイン投入モード:LC009より C03C : 96 80 " " ldaa X0080 //AレジスタにX0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)を入れる C03E : 97 81 " " staa X0081 //X0081(X0080「タイマー2 周回数」設定(テストコイン投入モードの際保存))に C040 : BD C1 58 " X" jsr LC158 //LC158(「ディップスイッチの使用法B」「タイマー1」ディップスイッチ読み取り)へ C043 : 86 20 " " ldaa #$20 //+/Aレジスタに#$20を入れる C045 : 97 17 " " staa X0017 //+/X0017(ポート6)に#$20を入れる(P65=H,パワトラ紫線を導通) C047 : 86 10 " " ldaa #$10 //-/Aレジスタに#$20を入れる C049 : 97 17 " " staa X0017 //-/X0017(ポート6)に#$10を入れる(P64=H,パワトラ水色線を導通) C04B : 96 17 " " ldaa X0017 //*/AレジスタにX0017(ポート6)を入れる C04D : 84 04 " " anda #$04 //*/A = A & 0x04 C04F : 27 03 "' " beq LC054 //*/X0017(ポート6)のP63(選曲セレクトトグルスイッチ)が0のとき、LC054(LC407(一曲目演奏演奏開始処理(課金後)))へ C051 : 7E C4 1C "~ " jmp LC41C //*/X0017(ポート6)のP63(選曲セレクトトグルスイッチ)が1のとき、LC41C(二曲目演奏演奏開始処理(課金後))へ C054 LC054: //:LC03Cより C054 : 7E C4 07 "~ " jmp LC407 //LC407(一曲目演奏演奏開始処理(課金後))へ ; C057 LC057: //課金分終了処理 C057 : 0F " " sei //割り込み無効 C058 : BD C0 CF " " jsr LC0CF //演奏開始サブルーチン C05B : 86 20 " " ldaa #$20 //*/#$20=B00100000 C05D : 97 17 " " staa X0017 //*/X0017(ポート6,P65=Hにセット(パワトラ)) C05F : C6 F0 " " ldab #$F0 //+/Bレジスタに#$F0を入れる(#$F0回ループ) C061 : BD C1 B1 " " jsr LC1B1 //+/ループウェイトサブルーチン,Bレジスタで設定 C064 : 8D 3E " >" bsr LC0A4 //AY-3-8910ミキサー,I/O設定サブルーチン C066 : BD C4 4B " K" jsr LC44B //PCM_2再生(終了音声) C069 : 20 95 " " bra LC000 //プログラム先頭へ ; C06B LC06B: //変数初期化:LC000より C06B : 0F " " sei //割り込み無効 C06C : 8E 00 60 " `" lds #$0060 //スタックポインタに#$0060を入れる(HD63A03XPは$0040~$00FFまで内蔵RAMを持ち、その内$0040~$0060をスタック領域とする) C06F : CE 00 62 " b" ldx #$0062 //Xレジスタに#$0062を入れる C072 : 4F "O" clra //aレジスタクリア C073 LC073: //メモリ初期化ループ:LC073より C073 : A7 00 " " staa $00,x //-/ C075 : 08 " " inx //-/ C076 : 8C 00 FF " " cpx #$00FF //-/ C079 : 26 F8 "& " bne LC073 //-/X62~XFFまでメモリ初期化(変数初期化) C07B : 86 00 " " ldaa #$00 C07D : 97 08 " " staa X0008 //X0008(タイマーコントロール/ステータスレジスタ1)に0x00をセット C07F : 86 10 " " ldaa #$10 C081 : 97 0F " " staa X000F //X000F(タイマーコントロール/ステータスレジスタ2)に0x10をセット__(4bit目は予約ビットであり、なぜ入れているか不明) C083 : 86 20 " " ldaa #$20 C085 : 97 1B " " staa X001B //X001B(タイマーコントロール/ステータスレジスタ3)に0x20をセット__(5bit目は予約ビットであり、なぜ入れているか不明) C087 : 86 20 " " ldaa #$20 C089 : 97 11 " " staa X0011 //X0011(tx、rxコントロールステータスレジスタ)に0x20をセット__(Transmit Data Register Empty = H) C08B : 86 04 " " ldaa #$04 C08D : 97 10 " " staa X0010 //X0010(レート、モードコントロールレジスタ)に0x04をセット__(ポート2,P22をSCIで使用しない) C08F : 86 40 " @" ldaa #$40 C091 : 97 14 " " staa X0014 //X0014(RAM/ポート5コントロールレジスタ)に0x40をセット__(MRE = H) C093 : 86 FF " " ldaa #$FF C095 : 97 01 " " staa X0001 //X0001(ポート2データ方向レジスタ)に0xFFをセット__(-, -, -, -, -, -,DDR1~7 = H, DDR0 = H) C097 : 86 FB " " ldaa #$FB C099 : 97 16 " " staa X0016 //X0016(ポート6データ方向レジスタ)に0xFBをセット__(ポート6,P62のみ入力) C09B : 86 00 " " ldaa #$00 C09D : 97 03 " " staa X0003 //X0003(ポート2)に0x00を入れる C09F : 97 17 " " staa X0017 //X0017(ポート6)に0x00を入れる C0A1 : 7E C0 06 "~ " jmp LC006 //LC006(演奏前準備)へ ; C0A4 LC0A4://AY-3-8910ミキサー,I/O設定(AY-3-8910:PORTAは出力、PORTBは入力) C0A4 : 86 7F " " ldaa #$7F //AY-3-8910ミキサー,I/O設定レジスタに0x7F入れる C0A6 : 97 73 " s" staa X0073 C0A8 : C6 07 " " ldab #$07 //AY-3-8910アドレス#$07(ミキサー,I/O設定レジスタ) C0AA : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C0AD : 39 "9" rts //サブルーチン終了 ; C0AE LC0AE: //ディップスイッチの状態を読み取り、X0089へ入れる:LC009より C0AE : C6 0F " " ldab #$0F //Bレジスタに#$0Fを入れる C0B0 : 86 03 " " ldaa #$03 //Aレジスタに#$03を入れる C0B2 : 97 17 " " staa X0017 //X0017(ポート6)に#$03を入れる__(P60=BD1=H, P61=BDIR=H__AY-3-8910アドレスラッチモード) C0B4 : D7 03 " " stab X0003 //X0003(ポート2)に#$0Fを入れる__(AY-3-8910ポートB(ディップスイッチ)アドレス指定) C0B6 : 86 00 " " ldaa #$00 //*/Aレジスタに#$00を入れる C0B8 : 97 17 " " staa X0017 //*/X0017(ポート6)に#$00を入れる__(P60=BD1=L, P61=BDIR=L__AY-3-8910インアクティブモード) C0BA : 86 01 " " ldaa #$01 //+/Aレジスタに#$01を入れる C0BC : 97 17 " " staa X0017 //+/X0017(ポート6)に#$01を入れる__(P60=BD1=H, P61=BDIR=L__AY-3-8910_リードモード) C0BE : 86 00 " " ldaa #$00 //-/Aレジスタに#$00を入れる C0C0 : 97 01 " " staa X0001 //-/X0001(ポート2データ方向レジスタ)に#$01を入れる__(ポート2_P20~P27_INPUTモードに設定) C0C2 : 96 03 " " ldaa X0003 //AレジスタにX0003(ポート2)を入れる__(AY-3-8910ポートB(ディップスイッチ)読み取り) C0C4 : 97 89 " " staa X0089 //X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)にAレジスタ(AY-3-8910ポートB(ディップスイッチ)の状態)を入れる__ C0C6 : 86 00 " " ldaa #$00 //*/Aレジスタに#$00を入れる C0C8 : 97 17 " " staa X0017 //*/X0017(ポート6)に#$00を入れる__(P60=BD1=L, P61=BDIR=L__AY-3-8910インアクティブモード) C0CA : 86 FF " " ldaa #$FF //+/Aレジスタに#$00を入れる C0CC : 97 01 " " staa X0001 //+/X0001(ポート2データ方向レジスタ)に#$01を入れる__(ポート2_P20~P27_OUTPUTモードに設定) C0CE : 39 "9" rts //サブルーチン終了 ; C0CF LC0CF: //演奏前準備:LC006より C0CF : CE 00 0F " " ldx #$000F C0D2 : DF 6F " o" stx X006F //X006Fに#$000F C0D4 : CE C5 F0 " " ldx #$C5F0 //演奏前準備読み取り開始アドレス C0D7 : 86 05 " " ldaa #$05 C0D9 : 97 78 " x" staa X0078 //X0078(選曲)に#$05(#$05番目は無選曲処理としてのフラグ) C0DB : 7E C1 E2 "~ " jmp LC1E2 //楽譜データ(演奏前準備用データ)読み取り処理 ; C0DE LC0DE: //ディップスイッチの状態から「ディップスイッチの使用法B」の「コイン枚数」の番号をX007F(「ディップスイッチの使用法B」「コイン枚数」設定用)に入れる:LC009より C0DE : 96 89 " " ldaa X0089 //+/AレジスタにX0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)を入れる C0E0 : 84 03 " " anda #$03 //+/A = A & 0x03__(ディップスイッチ2,1読み取り(「ディップスイッチの使用法B」「コイン枚数」設定用)) C0E2 : 81 01 " " cmpa #$01 //+/ C0E4 : 27 0F "' " beq LC0F5 //+/Aレジスタが1の時、LC0F5(X007F(「ディップスイッチの使用法B」「コイン枚数」設定用)に#$00を入れる処理)へ C0E6 : 81 02 " " cmpa #$02 ///////////////同様 C0E8 : 27 10 "' " beq LC0FA C0EA : 81 03 " " cmpa #$03 C0EC : 27 11 "' " beq LC0FF C0EE : 81 00 " " cmpa #$00 C0F0 : 27 12 "' " beq LC104 ///////////// C0F2 : 7E C0 00 "~ " jmp LC000 //プログラム先頭へ(通常到達しないコード) ; C0F5 LC0F5: //LC0DEより C0F5 : 86 01 " " ldaa #$01 C0F7 : 97 7F " " staa X007F //X007F(「「ディップスイッチの使用法B」「コイン枚数」設定用)に#$01を入れる C0F9 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C0FA LC0FA: //LC0DEより C0FA : 86 02 " " ldaa #$02 C0FC : 97 7F " " staa X007F //X007F(「「ディップスイッチの使用法B」「コイン枚数」設定用)に#$02を入れる C0FE : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C0FF LC0FF: //LC0DEより C0FF : 86 03 " " ldaa #$03 C101 : 97 7F " " staa X007F //X007F(「「ディップスイッチの使用法B」「コイン枚数」設定用)に#$03を入れる C103 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C104 LC104: //LC0DEより C104 : 86 04 " " ldaa #$04 C106 : 97 7F " " staa X007F //X007F(「「ディップスイッチの使用法B」「コイン枚数」設定用)に#$04を入れる C108 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C109 LC109: //ディップスイッチの状態から「ディップスイッチの使用法B」の「タイマー2周回数」の番号をX0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)に入れる:LC009より C109 : 96 89 " " ldaa X0089 //AレジスタにX0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)を入れる C10B : 84 70 " p" anda #$70 //A = A & 0x70__(ディップスイッチ7~5読み取り(「ディップスイッチの使用法B」タイマー2 周回数設定)) C10D : 81 00 " " cmpa #$00 //+/ C10F : 27 1F "' " beq LC130 //+/Aレジスタが0の時、LC130(X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$00を入れる処理)へ C111 : 81 10 " " cmpa #$10 ///////////////同様 C113 : 27 20 "' " beq LC135 C115 : 81 20 " " cmpa #$20 C117 : 27 21 "'!" beq LC13A C119 : 81 30 " 0" cmpa #$30 C11B : 27 22 "'"" beq LC13F C11D : 81 40 " @" cmpa #$40 C11F : 27 23 "'#" beq LC144 C121 : 81 50 " P" cmpa #$50 C123 : 27 24 "'$" beq LC149 C125 : 81 60 " `" cmpa #$60 C127 : 27 25 "'%" beq LC14E C129 : 81 70 " p" cmpa #$70 C12B : 27 26 "'&" beq LC153 ///////////// C12D : 7E C0 00 "~ " jmp LC000 //プログラム先頭へ(通常到達しないコード) ; C130 LC130: //:LC109より C130 : 86 00 " " ldaa #$00 C132 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$00を入れる C134 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C135 LC135: //:LC109より C135 : 86 01 " " ldaa #$01 C137 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$01を入れる C139 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C13A LC13A: //:LC109より C13A : 86 02 " " ldaa #$02 C13C : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$02を入れる C13E : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C13F LC13F: //:LC109より C13F : 86 03 " " ldaa #$03 C141 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$03を入れる C143 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C144 LC144: //:LC109より C144 : 86 04 " " ldaa #$04 C146 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$04を入れる C148 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C149 LC149: //:LC109より C149 : 86 05 " " ldaa #$05 C14B : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$05を入れる C14D : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C14E LC14E: //:LC109より C14E : 86 06 " " ldaa #$06 C150 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$06を入れる C152 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C153 LC153: //:LC109より C153 : 86 07 " " ldaa #$07 C155 : 97 80 " " staa X0080 //X0080(「ディップスイッチの使用法B」「タイマー2 周回数」設定用)に#$07を入れる C157 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C158 LC158: //「ディップスイッチの使用法B」「タイマー1」ディップスイッチ読み取り:LC03Cより C158 : 96 89 " " ldaa X0089 //*/AレジスタにX0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)を入れる C15A : 84 0C " " anda #$0C //*/A = A & 0x0C __(ディップスイッチ3,4(「ディップスイッチの使用法B」「タイマー1」)) C15C : 81 00 " " cmpa #$00 //*/ C15E : 27 0E "' " beq LC16E //*/X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)が#$00(「タイマー1」60秒)の時、LC16Eへ C160 : 81 04 " " cmpa #$04 //*/ C162 : 27 0F "' " beq LC173 //*/X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)が#$04(「タイマー1」90秒)の時、LC173へ C164 : 81 08 " " cmpa #$08 //*/ C166 : 27 10 "' " beq LC178 //*/X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)が#$08(「タイマー1」120秒)の時、LC178へ C168 : 81 0C " " cmpa #$0C //*/ C16A : 27 11 "' " beq LC17D //*/X0089(AY-3-8910のポートB(ディップスイッチ)の状態保存用)が#$0C(「タイマー1」30秒)の時、LC17Dへ C16C : 20 05 " " bra LC173 //LC173(「タイマー1」90秒)へ(通常到達しないコード) ; C16E LC16E: //LC158より C16E : 86 3C " <" ldaa #$3C //+/Aレジスタに#$3C(60D)を入れる C170 : 97 7B " {" staa X007B //+/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)に60を入れる C172 : 39 "9" rts //サブルーチン終了(LC03Cへ戻る) ; C173 LC173: //LC158より C173 : 86 5A " Z" ldaa #$5A //+/Aレジスタに#$5A(90D)を入れる C175 : 97 7B " {" staa X007B //+/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)に90を入れる C177 : 39 "9" rts //サブルーチン終了(LC03Cへ戻る) ; C178 LC178: //LC158より C178 : 86 78 " x" ldaa #$78 //+/Aレジスタに#$78(120D)を入れる C17A : 97 7B " {" staa X007B //+/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)に120を入れる C17C : 39 "9" rts //サブルーチン終了(LC03Cへ戻る) ; C17D LC17D: //LC158より C17D : 86 1E " " ldaa #$1E //+/Aレジスタに#$1E(30D)を入れる C17F : 97 7B " {" staa X007B //+/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)に30を入れる C181 : 39 "9" rts //サブルーチン終了(LC03Cへ戻る) ; C182 LC182: //コイン投入ボタン(茶線)処理:LC009より C182 : 96 15 " " ldaa X0015 //*/AレジスタにX0015(ポート5)を入れる C184 : 84 04 " " anda #$04 //*/A = A & 0x04 (茶線) C186 : 27 04 "' " beq LC18C //*/(X0015(ポート5) & 0x04)が0の時(茶線を押したとき)、LC18C(コイン投入ボタンのフラグ処理)へ C188 : 4F "O" clra //Aレジスタクリア C189 : 97 79 " y" staa X0079 //X0079(コイン投入モードフラグ)に0を入れる C18B : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C18C LC18C: //コイン投入ボタンのフラグ処理:LC182より C18C : 86 08 " " ldaa #$08 //+/Aレジスタに#$08を入れる C18E : 97 17 " " staa X0017 //+/X0017(ポート6)に#$08を入れる(パワトラ緑線を導通) C190 : C6 8F " " ldab #$8F //Bレジスタに#$8Fを入れる(ウェイト) C192 : 8D 1D " " bsr LC1B1 //LC1B1(ループウェイト処理B_レジスタで設定)へ C194 : 86 00 " " ldaa #$00 //*/Aレジスタに#$08を入れる C196 : 97 17 " " staa X0017 //*/X0017(ポート6)に#$00を入れる(パワトラ緑線を開放) C198 : 96 7E " ~" ldaa X007E //-/AレジスタにX007Eを入れる C19A : 4C "L" inca //-/Aレジスタインクリメント C19B : 97 7E " ~" staa X007E //-/X007E(コイン投入ボタンのコイン入回数) = X007E + 1 C19D : 91 7F " " cmpa X007F //-/ C19F : 27 08 "' " beq LC1A9 //-/X007E(コイン投入ボタンのコイン入回数)がX007F(「ディップスイッチの使用法B」「コイン枚数」設定用)と等しい場合、LC1A9 C1A1 : 4F "O" clra //+/Aレジスタクリア C1A2 : 97 79 " y" staa X0079 //+/X0079(コイン投入モードフラグ)に0を入れる C1A4 : C6 60 " `" ldab #$60 //Bレジスタに#$60を入れる(ウェイト) C1A6 : 8D 09 " " bsr LC1B1 //LC1B1(ループウェイト処理B_レジスタで設定)へ C1A8 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C1A9 LC1A9: //LC18Cより C1A9 : 86 01 " " ldaa #$01 //+/Aレジスタに#$01を入れる C1AB : 97 79 " y" staa X0079 //+/X0079(コイン投入モードフラグ)に1を入れる C1AD : 4F "O" clra //*/Aレジスタクリア C1AE : 97 7E " ~" staa X007E //*/X007E(コイン投入ボタンのコイン入回数)に0を入れる C1B0 : 39 "9" rts //サブルーチン終了(LC009へ戻る) ; C1B1 LC1B1: //-/ループウェイトB_レジスタで設定 C1B1 : CE 00 FF " " ldx #$00FF C1B4 LC1B4: C1B4 : 09 " " dex C1B5 : 26 FD "& " bne LC1B4 C1B7 : 5A "Z" decb C1B8 : 26 F7 "& " bne LC1B1 C1BA : 39 "9" rts //-/ ; C1BB LC1BB: //ポート6操作系(AY-3-8910のP60=BD1,P61=BDIR)//AY-3-8910書き込みサブルーチン C1BB : 96 17 " " ldaa X0017// X0017 = (X0017 & FC) | 0x03 C1BD : 84 FC " " anda #$FC C1BF : 8A 03 " " oraa #$03 C1C1 : 97 17 " " staa X0017//(BD1=H,BDIR=H:AY-3-8910アドレスラッチモード) C1C3 LC1C3: //ポート2操作系(AY-3-8910のDA0~DA7) C1C3 : D7 03 " " stab X0003 //X0003(ポート2)にbレジスタを入れる C1C5 : 84 FC " " anda #$FC C1C7 : 97 17 " " staa X0017 // X0017 = 0xFC & X0017(BD1=L,BDIR=L:AY-3-8910インアクティブモード) C1C9 : 96 73 " s" ldaa X0073 //+/ C1CB : 97 03 " " staa X0003 //+/X0003(ポート2)にX0073(AY-3-8910のDA0~DA7に出力するデータ)を入れる C1CD : 96 17 " " ldaa X0017 //-/X0017 = (X0017 & 0xFC) | 0x02 C1CF : 84 FC " " anda #$FC //-/ C1D1 : 8A 02 " " oraa #$02 //-/ C1D3 : 97 17 " " staa X0017 //-/(BD1=H,BDIR=H:AY-3-8910_PSG書き込みモード) C1D5 : 84 FC " " anda #$FC //*/X0017 = (X0017 & 0xFC) C1D7 : 97 17 " " staa X0017 //*/(BD1=L,BDIR=L:AY-3-8910インアクティブモード) C1D9 : 39 "9" rts //サブルーチン終了 ; C1DA LC1DA: //-/xレジスタにセットされた現在の読み取りポイントをX0073(AY-3-8910のDA0~DA7に出力するデータ)に入れる C1DA : A6 00 " " ldaa $00,x //-/ C1DC : 97 73 " s" staa X0073 //-/ C1DE : 39 "9" rts //サブルーチン終了 ; C1DF LC1DF://演奏開始先頭楽譜データ1ブロック読み取り(先頭1ブロックはエンベロープ等の設定) C1DF : 08 " " inx // C1E0 : 08 " " inx // C1E1 : 08 " " inx // C1E2 LC1E2: //(演奏前準備データの場合のみここからスタート):LC0CFより C1E2 : C6 01 " " ldab #$01 //AY-3-8910アドレス指定:チャンネルAの音程上位4ビット(演奏前処理データ0バイト目、演奏開始楽譜データ3バイト目) C1E4 : 8D F4 " " bsr LC1DA //xレジスタにセットされた現在の読み取りポイントをX0073に入れる C1E6 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C1E9 : 08 " " inx C1EA : C6 06 " " ldab #$06 //AY-3-8910アドレス指定:ノイズ分周(演奏前処理データ1バイト目、演奏開始楽譜データ4バイト目) C1EC : 8D EC " " bsr LC1DA //xレジスタにセットされた現在の読み取りポイントをX0073に入れる C1EE : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C1F1 : 08 " " inx C1F2 : C6 0B " " ldab #$0B //AY-3-8910アドレス指定:エンベロープ周期(演奏前処理データ2バイト目、演奏開始楽譜データ5バイト目) C1F4 : 8D E4 " " bsr LC1DA //xレジスタにセットされた現在の読み取りポイントをX0073に入れる C1F6 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C1F9 : 08 " " inx C1FA : C6 0C " " ldab #$0C //AY-3-8910アドレス指定:エンベロープ周期(演奏前処理データ3バイト目、演奏開始楽譜データ6バイト目) C1FC : 8D DC " " bsr LC1DA //xレジスタにセットされた現在の読み取りポイントをX0073に入れる C1FE : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C201 : 08 " " inx C202 : C6 0D " " ldab #$0D //AY-3-8910アドレス指定:エンベロープ波形(演奏前処理データ4バイト目、演奏開始楽譜データ7バイト目) C204 : 8D D4 " " bsr LC1DA //xレジスタにセットされた現在の読み取りポイントをX0073に入れる C206 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C209 : 08 " " inx C20A LC20A: //楽譜データ1ブロック読み取りループ:LC2E5より C20A : A6 00 " " ldaa $00,x //*/Aレジスタにxアドレスにある値を入れる(楽譜データ読み取り) C20C : 81 FF " " cmpa #$FF //*/ C20E : 27 58 "'X" beq LC268 //*/楽譜データ0バイト目が#$FFのとき、LC268(曲終端(ループ)処理)へ C210 : A6 00 " " ldaa $00,x //+/Aレジスタにxアドレスにある値を入れる(楽譜データ読み取り) C212 : 81 0F " " cmpa #$0F //+/ C214 : 27 55 "'U" beq LC26B //+/楽譜データ0バイト目が#$0Fのとき、LC26B(曲終端(リセット)処理)へ C216 : C6 00 " " ldab #$00 //AY-3-8910アドレス指定:チャンネルAの音程下位8ビット(楽譜データ0バイト目) C218 : 8D C0 " " bsr LC1DA //Xレジスタにセットされた現在の読み取りポイントをX0073に入れる C21A : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C21D : 08 " " inx C21E : C6 02 " " ldab #$02 //AY-3-8910アドレス指定:チャンネルBの音程下位8ビット(楽譜データ1バイト目) C220 : 8D B8 " " bsr LC1DA //Xレジスタにセットされた現在の読み取りポイントをX0073に入れる C222 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C225 : 08 " " inx C226 : C6 03 " " ldab #$03 //AY-3-8910アドレス指定:チャンネルBの音程上位4ビット(楽譜データ2バイト目) C228 : 8D B0 " " bsr LC1DA //Xレジスタにセットされた現在の読み取りポイントをX0073に入れる C22A : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C22D : 08 " " inx C22E : C6 04 " " ldab #$04 //AY-3-8910アドレス指定:チャンネルCの音程下位8ビット(楽譜データ3バイト目) C230 : 8D A8 " " bsr LC1DA //Xレジスタにセットされた現在の読み取りポイントをX0073に入れる C232 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C235 : 08 " " inx C236 : C6 05 " " ldab #$05 //AY-3-8910アドレス指定:チャンネルCの音程上位4ビット(楽譜データ4バイト目) C238 : 8D A0 " " bsr LC1DA //Xレジスタにセットされた現在の読み取りポイントをX0073に入れる C23A XC23A:(無効なラベル) C23A : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C23D : 08 " " inx C23E : BD C3 24 " $" jsr LC324 //LC324(AY-3-8910ノートオンオフ関連楽譜データ読み取り&AY-3-8910へ書き込みサブルーチン)へ(楽譜データ5,6バイト目) C241 : 08 " " inx C242 : 96 78 " x" ldaa X0078 //現在のポイントのデータを読み取り(楽譜データ7バイト目(ブロック終端)) C244 : 81 05 " " cmpa #$05 //-/楽譜データ7バイト目が#$05で0ディレイで次の楽譜読み取り C246 : 27 C2 "' " beq LC20A //-/楽譜データ7バイト目が#$05でLC20A(楽譜データ1ブロック読み取り)へ C248 : 96 82 " " ldaa X0082 //+/X0082(パワトラ点滅保存用)をAレジスタへ入れる C24A : 26 0E "& " bne LC25A //+/X0082(パワトラ点滅保存用)が0でない時、LC25Aへ C24C : 96 17 " " ldaa X0017 //AレジスタにX0017(ポート6)を入れる C24E : 84 3F " ?" anda #$3F //A = A & 0x3F C250 : 8A 80 " " oraa #$80 //A = A | 0x80 C252 : 97 17 " " staa X0017 //X0017(ポート6)にAレジスタを入れる__(ポート6 = (ポート6 & B00111111) | B10000000)(P67(パワトラ)をH,P66(パワトラ)をLにする) C254 : 86 01 " " ldaa #$01 //*/Aレジスタに#$00を入れる C256 : 97 82 " " staa X0082 //*/X0082(パワトラ点滅保存用)に#$01を入れる C258 : 20 14 " " bra LC26E //LC26Eへ ; C25A LC25A: //ポート6操作系(AY-3-8910のP60=BD1,P61=BDIR)(パワトラ点滅処理分岐) C25A : 96 17 " " ldaa X0017 //AレジスタにX0017(ポート6)を入れる C25C : 84 3F " ?" anda #$3F //A = A & 0x3F C25E : 8A 40 " @" oraa #$40 //A = A | 0x40 C260 : 97 17 " " staa X0017 //X0017(ポート6)にAレジスタを入れる__(ポート6 = (ポート6 & B00111111) | B01000000)(P67(パワトラ)をL,(P66(パワトラ)をHにする) C262 : 86 00 " " ldaa #$00 //+/Aレジスタに#$00を入れる C264 : 97 82 " " staa X0082 //+/X0082(パワトラ点滅保存用)に#$00を入れる C266 : 20 06 " " bra LC26E //LC26Eへ C268 LC268: //曲終端(ループ)処理:LC20Aより C268 : 7E C2 EC "~ " jmp LC2EC //LC2EC(曲ループ処理)へ C26B LC26B: //曲終端(リセット)処理:LC20Aより C26B : 7E C2 EB "~ " jmp LC2EB //LC2EB(曲リセット処理)へ ; C26E LC26E: //:LC20Aより C26E : DF 69 " i" stx X0069 //X0069(Xレジスタ一時退避用)にXレジスタ(楽譜データブロック7バイト目のアドレスが入っている?)の値を入れる C270 : 0E " " cli //割り込みフラグクリア C271 : 86 42 " B" ldaa #$42 //-/Aレジスタに#$42を入れる_※X0014=(STBY_PWR,RAME,-,-,HLTE,MRE,IRQ2_E,IRQ1_E) C273 : 97 14 " " staa X0014 //-/X0014(RAM/ポート5コントロールレジスタ)に#$42を入れる__(STBY_PWR=L, RAME=H, -, -, HLTE=L, MRE=L, IRQ2_E=H, IRQ1_E=L)(オンチップメモリ有効,割り込みIRQ2有効にセット) C275 : A6 00 " " ldaa $00,x //AレジスタにXレジスタ(楽譜データブロック7バイト目のアドレスが入っている?)に入ったアドレスにある値を入れる C277 LC277: //LC2D4より(楽譜データブロック7バイト目に0が入っている場合ここに飛ぶ) C277 : DE 6F " o" ldx X006F //XレジスタにX006F(楽曲テンポ)を入れる C279 LC279: //演奏用ウェイトループ先頭とボタン読み取り処理(PCM再生用) C279 : 7C 00 8E "| " inc X008E //+/X008Eインクリメント C27C : 7A 00 8E "z " dec X008E //+/X008Eデクリメント(X008Eの操作はここだけなのでウェイト処理?) C27F : C6 10 " " ldab #$10 //Bレジスタに#$10を入れる C281 : 7D 00 84 "} " tst X0084 //(「定位置で止まる」スイッチ押されたフラグ?)のテスト(フラグレジスタのみ変化) C284 : 26 76 "&v" bne LC2FC //X0084(「定位置で止まる」スイッチ押されたフラグ?)が0でないときはLC2FC(課金分終了処理)へ C286 : D6 15 " " ldab X0015 //*/BレジスタにX0015(ポート5)を入れる__(ポート5はPCM再生用のボタンへ接続) C288 : C4 F0 " " andb #$F0 //*/B = B & 0xF0 C28A : C1 00 " " cmpb #$00 //*/ C28C : 27 10 "' " beq LC29E //*/X0015(ポート5)の上位4bitがB0000の時(赤,黄,橙,白の線のボタンが押されていない時)、LC29Eへ C28E : C1 10 " " cmpb #$10 //+/ C290 : 27 11 "' " beq LC2A3 //+/X0015(ポート5)の上位4bitがB0001の時(橙の線のボタンが押された時)、LC2A3へ C292 : C1 20 " " cmpb #$20 //-/ C294 : 27 13 "' " beq LC2A9 //-/X0015(ポート5)の上位4bitがB0010の時(黄の線のボタンが押された時)、LC2A9へ C296 : C1 40 " @" cmpb #$40 //*/ C298 : 27 15 "' " beq LC2AF //*/X0015(ポート5)の上位4bitがB0100の時(白の線のボタンが押された時)、LC2AFへ C29A : C1 80 " " cmpb #$80 //+/ C29C : 27 17 "' " beq LC2B5 //+/X0015(ポート5)の上位4bitがB1000の時(赤の線のボタンが押された時)、LC2B5へ C29E LC29E: //(すべて押されていない時):LC279より C29E : 7F 00 8D " " clr X008D //X008D(PCM再生ボタン読み取り用)に#$00を入れる C2A1 : 20 16 " " bra LC2B9 //LC2B9(演奏用ウェイト処理)へ ; C2A3 LC2A3: //(橙の線のボタンが押された時):LC279より C2A3 : C6 01 " " ldab #$01 //+/ C2A5 : D7 8D " " stab X008D //+/X008D(PCM再生ボタン読み取り用)に#$01を入れる C2A7 : 20 10 " " bra LC2B9 //LC2B9(演奏用ウェイト処理)へ ; C2A9 LC2A9: //(黄の線のボタンが押された時):LC279より C2A9 : C6 02 " " ldab #$02 //-/ C2AB XC2AB:(無効なラベル) C2AB : D7 8D " " stab X008D //-/X008D(PCM再生ボタン読み取り用)に#$02を入れる C2AD : 20 0A " " bra LC2B9 //LC2B9(演奏用ウェイト処理)へ ; C2AF LC2AF: //(白の線のボタンが押された時):LC279より C2AF : C6 03 " " ldab #$03 //*/ C2B1 : D7 8D " " stab X008D //*/X008D(PCM再生ボタン読み取り用)に#$03を入れる C2B3 : 20 04 " " bra LC2B9 //LC2B9(演奏用ウェイト処理)へ ; C2B5 LC2B5: //(赤の線のボタンが押された時):LC279より C2B5 : C6 04 " " ldab #$04 //+/ C2B7 : D7 8D " " stab X008D //+/X008D(PCM再生ボタン読み取り用)に#$04を入れる C2B9 LC2B9: //演奏用ウェイト処理(Xレジスタに入ったX006F(楽曲テンポ)から1を引いていき0になるまでLC279ループ) C2B9 : 09 " " dex //Xレジスタデクリメント(Xレジスタの初期値はX006F(楽曲テンポ)) C2BA : 26 BD "& " bne LC279 //Xレジスタが0出ない時、LC279(演奏用ウェイトループ先頭)へ C2BC : D6 80 " " ldab X0080 //-/BレジスタにX0080を入れる C2BE : C1 00 " " cmpb #$00 //-/ C2C0 : 27 06 "' " beq LC2C8 //-/ C2C2 : C1 07 " " cmpb #$07 //-/ C2C4 : 27 02 "' " beq LC2C8 //-/X0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)が#$00(「タイマー1」)または#$07(「タイマー2」)の時、LC2C8へ C2C6 : 20 09 " " bra LC2D1 //LC2D1(LC365(赤,橙,黄,白ボタン状態分岐 & PCM再生処理))へ ; C2C8 LC2C8: //LC2B9より C2C8 : 8D 35 " 5" bsr LC2FF //LC2FF(課金タイマー処理関連?)へ C2CA : 0C " " clc C2CB : D6 7B " {" ldab X007B //*/BレジスタにX007B(「ディップスイッチの使用法B」「タイマー1」設定用)を入れる C2CD : C0 05 " " subb #$05 //*/B = B - 0x05 C2CF : 25 03 "% " bcs LC2D4 //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が6以上の場合、LC2D4へ C2D1 LC2D1: //LC2B9より C2D1 : BD C3 65 " e" jsr LC365 //LC365(赤,橙,黄,白ボタン状態分岐 & PCM再生処理)へ C2D4 LC2D4: //:LC2C8より::LC365(赤,橙,黄,白ボタン状態分岐 & PCM再生処理)からの戻り C2D4 : 4A "J" deca //Aレジスタ(LC26Eで入れた楽譜データブロック7バイト目が入っている?)デクリメント C2D5 : 26 A0 "& " bne LC277 //Aレジスタが0でない時、LC277へ C2D7 : D6 80 " " ldab X0080 //-/BレジスタにX0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)を入れる C2D9 : C1 07 " " cmpb #$07 //-/ C2DB : 26 08 "& " bne LC2E5 //-/X0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)が#$07(「タイマー2」)の時、LC2E5(楽譜データの次のブロックへ読み取り)へ C2DD : D6 7B " {" ldab X007B //+/BレジスタにX007B(「ディップスイッチの使用法B」「タイマー1」設定用)を入れる C2DF : 27 1B "' " beq LC2FC //+/ C2E1 : C1 01 " " cmpb #$01 //+/ C2E3 : 27 17 "' " beq LC2FC //+/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が0または1の時、LC2FC(課金分終了処理)へ C2E5 LC2E5: //楽譜データの次のブロックへ読み取り:LC2D4より C2E5 : DE 69 " i" ldx X0069 //XレジスタにX0069(LC26E内Xレジスタ一時退避用(楽譜データブロック7バイト目))を入れる C2E7 : 08 " " inx //Xレジスタインクリメント(次の楽譜データブロックの0バイト目を指定) C2E8 : 7E C2 0A "~ " jmp LC20A //LC20A(楽譜データ1ブロック読み取りループ)へ C2EB LC2EB: //曲終端判定(リセット):LC26Bより C2EB : 39 "9" rts //サブルーチン終了 ; C2EC LC2EC: //曲終端判定(ループ):LC268より C2EC : 96 78 " x" ldaa X0078 // C2EE : 81 01 " " cmpa #$01 C2F0 : 27 04 "' " beq LC2F6 //X0078(選曲)が#$01の時,LC2F6(一曲目演奏開始)へ C2F2 : 81 02 " " cmpa #$02 C2F4 : 27 03 "' " beq LC2F9 //X0078(選曲)が#$02の時,LC2F9(二曲目演奏開始)へ C2F6 LC2F6: //LC2ECより C2F6 : 7E C4 0D "~ " jmp LC40D //LC40D(一曲目演奏開始)へ C2F9 LC2F9: //LC2ECより C2F9 : 7E C4 22 "~ "" jmp LC422 //LC422(二曲目演奏開始)へ C2FC LC2FC: //課金分終了処理:LC2D4より C2FC : 7E C0 57 "~ W" jmp LC057 //課金分終了処理へ ; C2FF LC2FF: //LC2C8より C2FF : 0C " " clc //キャリーフラグクリア C300 : 7A 00 7A "z z" dec X007A //X007A(課金動作タイマーカウンタ用)デクリメント C303 : 27 01 "' " beq LC306 //X007A(課金動作タイマーカウンタ用)が0の時、LC306へ C305 : 39 "9" rts //サブルーチン終了(LC2C8へ戻る) ; C306 LC306: LC2FFより C306 : D6 80 " " ldab X0080 //BレジスタにX0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)を入れる C308 : C1 00 " " cmpb #$00 //*/ C30A : 27 05 "' " beq LC311 //*/ C30C : C1 07 " " cmpb #$07 //*/ C30E : 27 01 "' " beq LC311 //*/X0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)が0(「タイマー1」)または7(「タイマー2」)の時、LC311へ C310 : 39 "9" rts //サブルーチン終了(LC2C8へ戻る) ; C311 LC311: LC306より C311 : 7A 00 7B "z {" dec X007B //X007B(「ディップスイッチの使用法B」「タイマー1」設定用)デクリメント C314 : 27 05 "' " beq LC31B //X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が0の時、LC31Bへ C316 : D6 7C " |" ldab X007C //+/BレジスタにX007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))を入れる C318 : D7 7A " z" stab X007A //+/X007A(課金動作タイマーカウンタ用)にX007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))を入れる C31A : 39 "9" rts //サブルーチン終了(LC2C8へ戻る) ; C31B LC31B: LC311より C31B : D6 7C " |" ldab X007C //-/BレジスタにX007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))を入れる C31D : D7 7A " z" stab X007A //-/X007A(課金動作タイマーカウンタ用)にX007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))を入れる C31F : C6 01 " " ldab #$01 //*/Bレジスタに#$01を入れる C321 : D7 7B " {" stab X007B //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)に#$01を入れる C323 : 39 "9" rts //サブルーチン終了(LC2C8へ戻る) ; C324 LC324: //AY-3-8910ノートオンオフ関連楽譜データ読み取り:LC20Aより C324 : A6 00 " " ldaa $00,x //-/Xレジスタアドレスにある値(楽譜データのブロックの5バイト目)をAレジスタに入れる C326 : 84 0F " " anda #$0F //-/A = A & 0x0F C328 : 8A 30 " 0" oraa #$30 //-/A = A | 0x30 C32A : 97 73 " s" staa X0073 //-/X0073(AY-3-8910のDA0~DA7に出力するデータ用)に((楽譜データのブロックの5バイト目 & 0x0F) | 0x30)を入れる__(ノイズC,Bは無効、下位4ビット(ノイズA,チャンネルC,チャンネルB,チャンネルA)のミキサーコントロール、I/O設定レジスタの設定) C32C : C6 07 " " ldab #$07 //AY-3-8910アドレス指定:ミキサーコントロール、I/O設定レジスタ C32E : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C331 : A6 00 " " ldaa $00,x //+/Xレジスタアドレスにある値(楽譜データのブロックの5バイト目)をAレジスタに入れる C333 : 84 F0 " " anda #$F0 //+/A = A & 0xF0 C335 : 8A 00 " " oraa #$00 //+/(無意味な処理) C337 : 97 73 " s" staa X0073 //+/X0073(AY-3-8910のDA0~DA7に出力するデータ用)に(楽譜データのブロックの5バイト目 & 0xF0)を入れる__(チャンネルAエンベロープ有効無効(4bit目)のみ操作) C339 : C6 08 " " ldab #$08 //AY-3-8910アドレス指定:チャンネルAエンベロープ音量設定レジスタ C33B : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C33E : 08 " " inx C33F : A6 00 " " ldaa $00,x //*/Xレジスタアドレスにある値(楽譜データのブロックの6バイト目)をAレジスタに入れる C341 : 84 F0 " " anda #$F0 //*/A = A & 0xF0 C343 : 8A 00 " " oraa #$00 //*/(無意味な処理) C345 : 97 73 " s" staa X0073 //*/X0073(AY-3-8910のDA0~DA7に出力するデータ用)に(楽譜データのブロックの6バイト目 & 0xF0)を入れる__(チャンネルBエンベロープ有効無効(4bit目)のみ操作) C347 : C6 09 " " ldab #$09 //AY-3-8910アドレス指定:チャンネルBエンベロープ音量設定レジスタ C349 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C34C : A6 00 " " ldaa $00,x //-/Xレジスタアドレスにある値(楽譜データのブロックの6バイト目)をAレジスタに入れる C34E : 48 "H" asla //-/Aレジスタの値を1ビット左シフト C34F : 48 "H" asla //-/Aレジスタの値を1ビット左シフト C350 : 48 "H" asla //-/Aレジスタの値を1ビット左シフト C351 : 48 "H" asla //-/Aレジスタの値を1ビット左シフト C352 : 8A 00 " " oraa #$00 //-/(無意味な処理) C354 : 97 73 " s" staa X0073 //-/X0073(AY-3-8910のDA0~DA7に出力するデータ用)に((楽譜データのブロックの6バイト目<<4) & 0xF0)を入れる__(チャンネルCエンベロープ有効無効(0bit目)のみ操作) C356 : C6 0A " " ldab #$0A //AY-3-8910アドレス指定:チャンネルCエンベロープ音量設定レジスタ C358 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C35B : 86 00 " " ldaa #$00 //Aレジスタに#$00を入れる C35D : 97 73 " s" staa X0073 //X0073(AY-3-8910のDA0~DA7に出力するデータ用)に#$00を入れる__(エンベロープ波形は0番目「|\_____」に固定) C35F : C6 0D " " ldab #$0D //AY-3-8910アドレス指定:エンベロープシャープコントロールレジスタ C361 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C364 : 39 "9" rts //サブルーチン終了 ; C365 LC365: //(赤,橙,黄,白ボタン状態分岐 & PCM再生処理):LC2D1より C365 : 36 "6" psha //Aレジスタをスタックへプッシュ(Aレジスタには楽譜ブロック7バイト目のアドレスが入っている?) C366 : 37 "7" pshb //Bレジスタをスタックへプッシュ(BレジスタにはX0080が入っている?) C367 : DF 6D " m" stx X006D //X006D(Xレジスタ一時退避用)にXレジスタを入れる(XレジスタにはX006F(楽曲テンポ)をデクリメントして0が入っている?) C369 : 96 8D " " ldaa X008D //AレジスタにX008D(PCM再生ボタン読み取り用)を入れる C36B : 26 02 "& " bne LC36F //X008D(PCM再生ボタン読み取り用)が0でない時LC36F(赤,橙,黄,白ボタン状態分岐)へ C36D : 20 34 " 4" bra LC3A3 //LC3A3(スタック戻し & サブルーチン終了処理)へ ; C36F LC36F: //ポート6操作系(AY-3-8910のP60=BD1,P61=BDIR)(赤,橙,黄,白ボタン状態分岐):LC365より C36F : 96 17 " " ldaa X0017 //+/AレジスタにX0017(ポート6)を入れる C371 : 84 DF " " anda #$DF //+/A = A & 0xDF C373 : 8A 20 " " oraa #$20 //+/A = A | 0x20 C375 : 97 17 " " staa X0017 //+/X0017(ポート6)にAレジスタを入れる__(ポート6 = (ポート6 & B11011111) | B00100000)(P65(パワトラ)をHにする) C377 : 96 8D " " ldaa X008D //AレジスタにX008D(PCM再生ボタン読み取り用)を入れる C379 : 81 01 " " cmpa #$01 //-/ C37B : 27 0E "' " beq LC38B //-/X008D(PCM再生ボタン読み取り用)が#$01(橙の線のボタンが押された時)のときLC38Bへ C37D : 81 02 " " cmpa #$02 //*/ C37F : 27 0F "' " beq LC390 //*/X008D(PCM再生ボタン読み取り用)が#$02(黄の線のボタンが押された時)のときLC390へ C381 : 81 03 " " cmpa #$03 //+/ C383 : 27 10 "' " beq LC395 //+/X008D(PCM再生ボタン読み取り用)が#$03(白の線のボタンが押された時)のときLC395へ C385 : 81 04 " " cmpa #$04 //-/ C387 : 27 11 "' " beq LC39A //-/X008D(PCM再生ボタン読み取り用)が#$04(赤の線のボタンが押された時)のときLC39Aへ C389 : 20 18 " " bra LC3A3 //X008D(PCM再生ボタン読み取り用)がそれ以外の時、LC3A3(スタック戻し & サブルーチン終了処理)へ ; C38B LC38B: //X008D(PCM再生ボタン読み取り用)が#$01(橙の線のボタンが押された時)のとき:LC36Fより C38B : BD C4 65 " e" jsr LC465 //サブルーチンLC465(PCM_3再生)へ C38E : 20 0D " " bra LC39D //LC39D(P65(パワトラ)をL & スタック戻し& サブルーチン終了処理)へ ; C390 LC390: //X008D(PCM再生ボタン読み取り用)が#$02(黄の線のボタンが押された時)のとき:LC36Fより C390 : BD C4 84 " " jsr LC484 //サブルーチンLC484(PCM_4再生)へ C393 : 20 08 " " bra LC39D //LC39D(P65(パワトラ)をL & スタック戻し& サブルーチン終了処理)へ ; C395 LC395: //X008D(PCM再生ボタン読み取り用)が#$03(白の線のボタンが押された時)のとき:LC36Fより C395 : BD C4 A3 " " jsr LC4A3 //サブルーチンLC4A3(PCM_5再生)へ C398 : 20 03 " " bra LC39D //LC39D(P65(パワトラ)をL & スタック戻し& サブルーチン終了処理)へ ; C39A LC39A: //X008D(PCM再生ボタン読み取り用)が#$04(赤の線のボタンが押された時)のとき:LC36Fより C39A : BD C4 E5 " " jsr LC4E5 //サブルーチンLC4E5(ノイズ音再生?)へ C39D LC39D: //P65(パワトラ)をL処理 C39D : 96 17 " " ldaa X0017 //+/AレジスタにX0017(ポート6)を入れる C39F : 84 DF " " anda #$DF //+/A = A & 0xDF C3A1 : 97 17 " " staa X0017 //+/X0017(ポート6)にAレジスタを入れる__(ポート6 = ポート6 & B11011111)(P65(パワトラ)をLにする) C3A3 LC3A3: //スタック戻し&サブルーチン終了処理:LC365より C3A3 : 7F 00 8D " " clr X008D //X008D(PCM再生ボタン読み取り用)に0を入れる C3A6 : DE 6D " m" ldx X006D //XレジスタにX006D(Xレジスタ一時退避用)を入れる C3A8 : 33 "3" pulb //スタックからプルし、Bレジスタへ戻す(BレジスタにはX0080が入っている?) C3A9 : 32 "2" pula //スタックからプルし、Aレジスタへ戻す(Aレジスタには楽譜ブロック7バイト目のアドレスが入っている?) C3AA : 39 "9" rts //サブルーチン終了(LC2D1へ戻る) ; C3AB LC3AB: //PCM再生(4bitPCM出力):LC3AB(ループ)とLC439等より C3AB : 8D 3E " >" bsr LC3EB //+/LC3EB(PCMの2サンプリングデータ(1バイト)取得 & ウェイト処理(AレジスタにXレジスタの示すアドレスにある値を入れる))へ C3AD : 44 "D" lsra //+/A = A >> 1 C3AE : 44 "D" lsra //+/A = A >> 1 C3AF : 44 "D" lsra //+/A = A >> 1 C3B0 : 44 "D" lsra //+/A = A >> 1 C3B1 : 84 0F " " anda #$0F //+/A = A & 0x0F C3B3 : 97 73 " s" staa X0073 //+/X0073(AY-3-8910のDA0~DA7に出力するデータ)に((X0065 >> 4) & 0x0F)を入れる C3B5 : C6 0E " " ldab #$0E //*/AY-3-8910アドレス指定:AY-3-8910ポートA(4bit簡易DACへ接続) C3B7 : BD C1 BB " " jsr LC1BB //*/LC1BB(AY-3-8910書き込みサブルーチン)へ C3BA : 8D 2F " /" bsr LC3EB //-/LC3EB(PCMの2サンプリングデータ(1バイト)取得 & ウェイト処理(AレジスタにXレジスタの示すアドレスにある値を入れる))へ C3BC : 84 0F " " anda #$0F //-/A = A & 0x0F C3BE : 97 73 " s" staa X0073 //-/X0073(AY-3-8910のDA0~DA7に出力するデータ)に(X0065 & 0x0F)を入れる C3C0 : DE 65 " e" ldx X0065 //+/XレジスタにX0065(PCM再生アドレス)を入れる C3C2 : 08 " " inx //+/Xレジスタデクリメント C3C3 : DF 65 " e" stx X0065 //+/X0065(PCM再生アドレス)にXレジスタを入れる(X0065 = X0065 - 1) C3C5 : C6 0E " " ldab #$0E //*/AY-3-8910アドレス指定:AY-3-8910ポートA(4bit簡易DACへ接続) C3C7 : BD C1 BB " " jsr LC1BB //*/LC1BB(AY-3-8910書き込みサブルーチン)へ C3CA : DE 67 " g" ldx X0067 //-/XレジスタにX0067(PCMデータサイズ)を入れる C3CC : 09 " " dex //-/Xレジスタデクリメント C3CD : DF 67 " g" stx X0067 //-/X0067(PCMデータサイズ)にXレジスタを入れる(X0067 = X0067 - 1) C3CF : 8C 00 00 " " cpx #$0000 //-/X0067(PCMデータサイズ)が C3D2 : 26 D7 "& " bne LC3AB //-/X0067(PCMデータサイズ(PCMデータ読むたびデクリメントする))が0でない時、LC3AB(PCM再生ループ先頭)へ C3D4 : 96 77 " w" ldaa X0077 //+/AレジスタにX0077(再生中のPCM再生番号)を入れる C3D6 : 81 01 " " cmpa #$01 //+/ C3D8 : 27 05 "' " beq LC3DF //+/X0077(再生中のPCM再生番号)が1の時、LC3DF(PCM1再生回数がゼロになるまでループ)へ C3DA : 81 02 " " cmpa #$02 C3DC : 27 07 "' " beq LC3E5 //PCM_2再生回数がゼロまでループ C3DE : 39 "9" rts //サブルーチン終了 ; C3DF LC3DF: //PCM1再生回数がゼロになるまでループ:LC3ABより C3DF : 7A 00 75 "z u" dec X0075 //X0075(PCM1再生回数)デクリメント C3E2 : 26 55 "&U" bne LC439 //X0075(PCM1再生回数)が0でない時、LC439(PCM1再セットアップ & 再生(PCM1以外再生中はここへは到達しない))へ C3E4 : 39 "9" rts //サブルーチン終了 ; C3E5 LC3E5: //PCM_2再生回数がゼロまでループ:LC3ABより C3E5 : 7A 00 76 "z v" dec X0076 //X0076(PCM2再生回数)デクリメント C3E8 : 26 69 "&i" bne LC453 //X0076(PCM2再生回数)が0でない時、LC453(PCM2再セットアップ & 再生)へ C3EA : 39 "9" rts //サブルーチン終了 ; C3EB LC3EB: //PCMの2サンプリングデータ(1バイト)取得 & ウェイト処理:LC3ABより C3EB : 8D 05 " " bsr LC3F2 //PCM再生ウェイト処理 C3ED : DE 65 " e" ldx X0065 //XレジスタにX0065(PCM再生アドレス)を入れる C3EF : A6 00 " " ldaa $00,x //AレジスタにXレジスタの示すアドレスにある値を入れる C3F1 : 39 "9" rts //サブルーチン終了 ; C3F2 LC3F2: //PCM再生ウェイト処理:LC3EBより C3F2 : 36 "6" psha //Aレジスタをスタックへプッシュ C3F3 : 86 29 " )" ldaa #$29 //-/PCM再生速度 C3F5 LC3F5: //LC3F5(0x29回ループ用):LC3F5より C3F5 : 4A "J" deca //Aレジスタデクリメント C3F6 : 26 FD "& " bne LC3F5 //-/0x29回ループ C3F8 : 96 77 " w" ldaa X0077 //*/AレジスタにX0077(PCM再生番号)を入れる C3FA : 81 02 " " cmpa #$02 //*/ C3FC : 27 04 "' " beq LC402 //*/X0077(PCM再生番号)が0x02のときLC402へ((「定位置で止まる」スイッチ押されたフラグ?)チェック無視する) C3FE : 96 84 " " ldaa X0084 //-/AレジスタにX0084(「定位置で止まる」スイッチ押されたフラグ?)を入れる C400 : 26 02 "& " bne LC404 //-/X0084(「定位置で止まる」スイッチ押されたフラグ?)が0でないときはLC404(課金分終了処理)へ C402 LC402: C402 : 32 "2" pula //スタックからプルしAレジスタへ C403 : 39 "9" rts //サブルーチン終了(LC3EBへ戻る) C404 LC404: //LC3F5より C404 : 7E C0 57 "~ W" jmp LC057 //課金分終了処理 ; C407 LC407: //一曲目演奏演奏開始処理(課金後):LC03Cより C407 : C6 3D " =" ldab #$3D //+/Bレジスタに#$3Dを入れる C409 : D7 7C " |" stab X007C //+/X007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))に#$3Dを入れる C40B : D7 7A " z" stab X007A //+/X007A(課金動作タイマーカウンタ用)に#$3Dを入れる C40D LC40D: //一曲目演奏演奏開始処理:LC2F6より C40D : CE 01 40 " @" ldx #$0140 //テンポ(#$0140) C410 : DF 6F " o" stx X006F //#$0140をX006Fに入れる C412 : 86 01 " " ldaa #$01 //-/ C414 : 97 78 " x" staa X0078 //-/X0078(選曲)を#$01にする C416 : CE C6 00 " " ldx #$C600 //一曲目楽譜データ先頭アドレス(#$C600) C419 : 7E C1 DF "~ " jmp LC1DF //LC1DF(演奏開始先頭楽譜データ1ブロック読み取り&演奏)へ ; C41C LC41C: //二曲目演奏演奏開始処理(課金後):LC03Cより C41C : C6 3D " =" ldab #$3D //+/Bレジスタに#$3Dを入れる C41E : D7 7C " |" stab X007C //+/X007C(課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?))に#$3Dを入れる C420 : D7 7A " z" stab X007A //+/X007A(課金動作タイマーカウンタ用)に#$3Dを入れる C422 LC422: //二曲目演奏演奏開始処理:LC2F9より C422 : CE 01 30 " 0" ldx #$0130 //テンポ(#$0130) C425 : DF 6F " o" stx X006F //-/ C427 : 86 02 " " ldaa #$02 //-/X0078(選曲)を#$02にする C429 : 97 78 " x" staa X0078 //X0078(選曲)を#$01にする C42B : CE CC 00 " " ldx #$CC00 //二曲目楽譜データ先頭アドレス(#$CC00) C42E : 7E C1 DF "~ " jmp LC1DF //LC1DF(演奏開始先頭楽譜データ1ブロック読み取り&演奏)へ ; //到達しないコード C431 : 86 02 " " ldaa #$02 //*/Aレジスタに#$02を入れる//到達しないコード C433 : 97 75 " u" staa X0075 //*/X0075(PCM_1再生回数)に#$02を入れる//到達しないコード C435 : 86 01 " " ldaa #$01 //-/Aレジスタに#$01を入れる//到達しないコード C437 : 97 77 " w" staa X0077 //-/X0077(PCM再生番号)に#$01を入れる //到達しないコード C439 LC439: //PCM1セットアップ & 再生:LC3DF等より C439 : CE D2 00 " " ldx #$D200 //*/Xレジスタに#$D200を入れる C43C : DF 65 " e" stx X0065 //*/X0065(PCM再生アドレス)に#$D200を入れる(楽譜データと重複しているため使用不可) C43E : CE 07 FF " " ldx #$07FF //+/Xレジスタに#$07FFを入れる C441 : DF 67 " g" stx X0067 //+/X0067(PCMデータサイズ)に#$07FFを入れる C443 : C6 AF " " ldab #$AF //-/Bレジスタに#$AFを入れる C445 : BD C1 B1 " " jsr LC1B1 //-/LC1B1(ループウェイトBレジスタで設定)へ C448 : 7E C3 AB "~ " jmp LC3AB //LC3AB(PCM再生)へジャンプ ; C44B LC44B: //PCM2セットアップ & 再生(駆動終了音声)緑線 C44B : 86 02 " " ldaa #$02 //*/Aレジスタに#$02を入れる C44D : 97 76 " v" staa X0076 //*/X0076(PCM_2再生回数)に#$02を入れる C44F : 86 02 " " ldaa #$02 //-/Aレジスタに#$02を入れる C451 : 97 77 " w" staa X0077 //-/X0077(PCM再生番号)に#$02を入れる C453 LC453: //PCM2再セットアップ & 再生:LC3E5等より C453 : CE DA 00 " " ldx #$DA00 //*/Xレジスタに#$DA00を入れる C456 : DF 65 " e" stx X0065 //*/X0065(PCM再生アドレス)に#$DA00を入れる C458 : CE 07 FF " " ldx #$07FF //+/Xレジスタに#$07FFを入れる C45B : DF 67 " g" stx X0067 //+/X0067(PCMデータサイズ)に#$07FFを入れる C45D : C6 AF " " ldab #$AF //-/Bレジスタに#$AFを入れる C45F : BD C1 B1 " " jsr LC1B1 //-/LC1B1(ループウェイトBレジスタで設定)へ C462 : 7E C3 AB "~ " jmp LC3AB //LC3AB(PCM再生)へジャンプ ; C465 LC465: //PCM_3再生設定&再生(橙の線のボタンが押された時)橙線:LC38Bより C465 : 86 01 " " ldaa #$01 //*/Aレジスタに#$01を入れる C467 : 97 75 " u" staa X0075 //*/X0075(PCM_1再生回数)に#$01を入れる(PCM1以外は無意味) C469 : 86 03 " " ldaa #$03 //-/Aレジスタに#$03を入れる C46B : 97 77 " w" staa X0077 //-/X0077(PCM再生番号)に#$03を入れる C46D : CE E2 00 " " ldx #$E200 //+/Xレジスタに#$E200を入れる C470 : DF 65 " e" stx X0065 //+/X0065(PCM再生アドレス)に#$E200を入れる C472 : CE 07 FF " " ldx #$07FF //*/Xレジスタに#$07FFを入れる C475 : DF 67 " g" stx X0067 //*/X0067(PCMデータサイズ)に#$07FFを入れる C477 : BD C0 A4 " " jsr LC0A4 //AY-3-8910ミキサー,I/O設定 C47A : BD C3 AB " " jsr LC3AB //LC3AB(PCM再生)へ C47D : 7F 00 77 " w" clr X0077 //X0077(PCM再生番号)クリア C480 : BD C5 5B " [" jsr LC55B //LC55B(PCM_3再生終了処理)へ C483 : 39 "9" rts //サブルーチン終了(LC38Bへ戻る) ; C484 LC484: //PCM_4再生設定&再生(黄の線のボタンが押された時)黄線:LC390より C484 : 86 01 " " ldaa #$01 //*/Aレジスタに#$01を入れる C486 : 97 75 " u" staa X0075 //*/X0075(PCM_1再生回数)に#$01を入れる(PCM1以外は無意味) C488 : 86 04 " " ldaa #$04 //-/Aレジスタに#$04を入れる C48A : 97 77 " w" staa X0077 //-/X0077(PCM再生番号)に#$04を入れる C48C : CE EA 00 " " ldx #$EA00 //+/Xレジスタに#$EA00を入れる C48F : DF 65 " e" stx X0065 //+/X0065(PCM再生アドレス)に#$EA00を入れる C491 : CE 07 FF " " ldx #$07FF //*/Xレジスタに#$07FFを入れる C494 : DF 67 " g" stx X0067 //*/X0067(PCMデータサイズ)に#$07FFを入れる C496 : BD C0 A4 " " jsr LC0A4 //AY-3-8910ミキサー,I/O設定 C499 : BD C3 AB " " jsr LC3AB //LC3AB(PCM再生)へ C49C : 7F 00 77 " w" clr X0077 //X0077(PCM再生番号)クリア C49F : BD C5 55 " U" jsr LC555 //LC555(PCM_4,5再生終了処理)へ C4A2 : 39 "9" rts //サブルーチン終了(LC390へ戻る) ; C4A3 LC4A3: //PCM_5再生設定&再生(白の線のボタンが押された時)白線:LC395より C4A3 : 86 01 " " ldaa #$01 //*/Aレジスタに#$01を入れる C4A5 : 97 75 " u" staa X0075 //*/X0075(PCM_1再生回数)に#$01を入れる(PCM1以外は無意味) C4A7 : 86 05 " " ldaa #$05 //-/Aレジスタに#$05を入れる C4A9 : 97 77 " w" staa X0077 //-/X0077(PCM再生番号)に#$05を入れる C4AB : CE F2 00 " " ldx #$F200 //+/Xレジスタに#$F200を入れる C4AE : DF 65 " e" stx X0065 //+/X0065(PCM再生アドレス)に#$F200を入れる C4B0 : CE 07 FF " " ldx #$07FF //*/Xレジスタに#$07FFを入れる C4B3 : DF 67 " g" stx X0067 //*/X0067(PCMデータサイズ)に#$07FFを入れる C4B5 : BD C0 A4 " " jsr LC0A4 //AY-3-8910ミキサー,I/O設定 C4B8 : BD C3 AB " " jsr LC3AB //LC3AB(PCM再生)へ C4BB : 7F 00 77 " w" clr X0077 //X0077(PCM再生番号)クリア C4BE : BD C5 55 " U" jsr LC555 //LC555(PCM_4,5再生終了処理)へ C4C1 : 39 "9" rts //サブルーチン終了(LC395へ戻る) ; C4C2 LC4C2: //~IRQ2ピン割り込み(緑線)(LC003より) C4C2 : 96 80 " " ldaa X0080 C4C4 : 27 0C "' " beq LC4D2 //X0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)が0(「タイマー1」)の時LC4D2へ C4C6 : 7A 00 81 "z " dec X0081 //X0081(「タイマー2 周回数」設定用(テストコイン投入モードの際保存))デクリメント C4C9 : 27 14 "' " beq LC4DF //X0081(「タイマー2 周回数」設定用(テストコイン投入モードの際保存))が0の時LC4DFへ C4CB : C6 40 " @" ldab #$40 //Bレジスタに#$0x40を入れる(ウェイト時間設定) C4CD : BD C1 B1 " " jsr LC1B1 //ループウェイトサブルーチン(Bレジスタで設定) C4D0 : 0E " " cli //割り込みフラグクリア C4D1 : 3B ";" rti //割り込み終了 ; C4D2 LC4D2: //X0080(「ディップスイッチの使用法B」タイマー2 周回数設定用)が0(「タイマー1」)の時(LC4C2より) C4D2 : D6 7B " {" ldab X007B //*/BレジスタにX007B(「ディップスイッチの使用法B」「タイマー1」設定用)を入れる C4D4 : 27 09 "' " beq LC4DF //*/ C4D6 : C1 01 " " cmpb #$01 //*/ C4D8 : 27 05 "' " beq LC4DF //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が0または1のときLC4DFへ C4DA : 7F 00 84 " " clr X0084 //X0084(「定位置で止まる」スイッチ押されたフラグ?)をクリア C4DD : 0E " " cli //割り込みフラグクリア C4DE : 3B ";" rti //割り込み終了 ; C4DF LC4DF: //定位置検出?(LC4C2,LC4D2より) C4DF : 86 01 " " ldaa #$01 //Aレジスタに#$0x01を入れる C4E1 : 97 84 " " staa X0084 //X0084(「定位置で止まる」スイッチ押されたフラグ?)に#$0x01を入れる C4E3 : 0F " " sei //割り込み禁止 C4E4 : 3B ";" rti //割り込み終了 ; C4E5 LC4E5: //ノイズ音&再生(赤の線のボタンが押された時):LC39Aより//赤線_ノイズ音関連 C4E5 : 86 01 " " ldaa #$01 //Aレジスタに#$0x01を入れる C4E7 : 97 85 " " staa X0085 //X0085(LC4E5で使用(#$01ストアのみで無意味、デバッグ用?))に#$0x01を入れる C4E9 : CE 05 00 " " ldx #$0500 //Xレジスタに#$0500を入れる C4EC : DF 71 " q" stx X0071 //LC53E内ウェイト回数(#$0500)ノイズ音発音時間? C4EE : CE D1 E0 " " ldx #$D1E0 //+/Xレジスタに#$D1E0を入れる C4F1 : A6 00 " " ldaa $00,x //+/Aレジスタにアドレス#$D1E0にある値を入れる C4F3 : 97 74 " t" staa X0074 //+/X0074にアドレス#$D1E0にある値を入れる C4F5 LC4F5: //LC4F5無限ループ C4F5 : 8D 27 " '" bsr LC51E //AY-3-8910のすべてのレジスタへ,アドレス(変数X0074で指定)から順に値を書き込む:LC4F5より C4F7 : 8D 45 " E" bsr LC53E //X0071回ウェイト&ボタン処理?サブルーチン C4F9 : 96 74 " t" ldaa X0074 //*/AレジスタにX0074を入れる C4FB : 4A "J" deca //*/Aレジスタデクリメント C4FC : 81 22 " "" cmpa #$22 //-/ C4FE : 27 09 "' " beq LC509 //-/Aレジスタが#$22と等しい時LC509へ C500 : 4A "J" deca //*/Aレジスタデクリメント C501 : 81 22 " "" cmpa #$22 //+/ C503 : 27 04 "' " beq LC509 //+/Aレジスタが#$22と等しい時LC509へ C505 : 97 74 " t" staa X0074 //X0074(赤線特殊PCM再生用?(チャンネルa_Ltone指定用))に(X0074 - 1)を入れる C507 : 20 EC " " bra LC4F5 //LC4F5へ無限ループ(Aレジスタが#$22と等しい時LC509へ抜ける) ; C509 LC509: //LC4F5より C509 : 8D 13 " " bsr LC51E //AY-3-8910のすべてのレジスタへ,アドレス(変数X0074で指定)から順に値を書き込む:LC4F5より C50B : 8D 31 " 1" bsr LC53E //X0071回ウェイト&ボタン処理?サブルーチン C50D : 8D 2F " /" bsr LC53E //X0071回ウェイト&ボタン処理?サブルーチン C50F LC50F: //LC50F無限ループ C50F : 96 74 " t" ldaa X0074 //AレジスタにX0074を入れる C511 : 4C "L" inca //Aレジスタインクリメント C512 : 81 8F " " cmpa #$8F //*/ C514 : 27 3F "'?" beq LC555 //*/Aレジスタが#$8Fと等しい時LC555へ C516 : 97 74 " t" staa X0074 //X0074(赤線特殊PCM再生用?(チャンネルa_Ltone指定用))にAレジスタを入れる C518 : 8D 04 " " bsr LC51E //AY-3-8910のすべてのレジスタへ,アドレス(変数X0074で指定)から順に値を書き込む:LC4F5より C51A : 8D 22 " "" bsr LC53E //X0071回ウェイト&ボタン処理?サブルーチン C51C : 20 F1 " " bra LC50F //LC50Fへ無限ループ(Aレジスタが#$8Fと等しい時LC555へ抜ける) ; C51E LC51E: //AY-3-8910のすべてのレジスタへ,アドレス(変数X0074で指定)から順に値を書き込む:LC4F5より C51E : 86 0D " " ldaa #$0D //*/Aレジスタに#$0Dを入れる C520 : 97 64 " d" staa X0064 //*/X0064(LC51E,LC530ループカウント用)に#$0Dを入れる C522 : C6 00 " " ldab #$00 //#$00(AY-3-8910チャンネルA_tone_Lbit)をBレジスタ(AY-3-8910のアドレス指定)へ C524 : 96 74 " t" ldaa X0074 //+/ C526 : 97 73 " s" staa X0073 //+/X0073(AY-3-8910のDA0~DA7に出力するデータ)にX0074(赤線特殊PCM再生用?(チャンネルa_Ltone指定用))を入れる C528 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C52B : CE D1 E1 " " ldx #$D1E1 //Xレジスタに#$D1E1(PCM_6(赤線)再生アドレス)を入れる C52E : C6 01 " " ldab #$01 //Bレジスタ(AY-3-8910のアドレス指定)に#$01(AY-3-8910チャンネルA_tone_Hbit)を入れる C530 LC530: //LC530ループ C530 : BD C1 DA " " jsr LC1DA //xレジスタ値のアドレスにある値をX0073(AY-3-8910のDA0~DA7に出力するデータ)に入れるサブルーチン C533 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C536 : 08 " " inx //Xレジスタインクリメント C537 : 5C "\" incb //Bレジスタインクリメント C538 : 7A 00 64 "z d" dec X0064 //X0064デクリメント C53B : 26 F3 "& " bne LC530 //X0064が0になるまでループ(#$0D回ループ) C53D : 39 "9" rts //サブルーチン終了 ; C53E LC53E: //LC4E5より C53E : DF 6B " k" stx X006B //X006B(Xレジスタ一時退避用)にXレジスタを入れる C540 : DE 71 " q" ldx X0071 //XレジスタにX0071(LC53E内ウェイト回数(ノイズ音発音時間))を入れる C542 : 96 84 " " ldaa X0084 //AレジスタにX0084(ボタン押されたフラグ?)を入れる C544 : 26 06 "& " bne LC54C //X0084ボタン押されたフラグ?が0でないときはLC54Cへ C546 LC546: //-/Xレジスタが0になるまで繰り返し(X0071回ウェイト) C546 : 09 " " dex // C547 : 26 FD "& " bne LC546 //-/ C549 : DE 6B " k" ldx X006B //XレジスタにX006B_Xレジスタ一時退避用を入れる C54B : 39 "9" rts //サブルーチン終了 C54C LC54C: //LC53Eより C54C : 7E C0 57 "~ W" jmp LC057 //課金分終了処理へ ; //到達しないコード C54F : 0C " " clc //キャリーフラグクリア//到達しないコード C550 : 7A 00 7B "z {" dec X007B //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)デクリメント//到達しないコード C553 : 25 53 "%S" bcs LC5A8 //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が(0→-1)の時、LC5A8(課金分終了処理)へ//到達しないコード C555 LC555: //PCM_4,5再生終了処理:LC484,LC4A3より C555 : 0C " " clc //キャリーフラグクリア C556 : 7A 00 7B "z {" dec X007B //-/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)デクリメント C559 : 25 4D "%M" bcs LC5A8 //-/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が(0→-1)の時、LC5A8(課金分終了処理)へ C55B LC55B: //PCM_3再生終了処理:LC465より C55B : 0C " " clc //キャリーフラグクリア C55C : 7A 00 7B "z {" dec X007B //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)デクリメント C55F : 25 47 "%G" bcs LC5A8 //*/X007B(「ディップスイッチの使用法B」「タイマー1」設定用)が(0→-1)の時、LC5A8(課金分終了処理)へ C561 : C6 FF " " ldab #$FF //-/ウェイト時間設定 C563 : BD C1 B1 " " jsr LC1B1 //-/ループウェイトサブルーチン_0xFF*0xFF C566 : 96 78 " x" ldaa X0078 //+/AレジスタにX0078(選曲番号)を入れる C568 : 81 03 " " cmpa #$03 //+/ C56A : 27 04 "' " beq LC570 //+/X0078(選曲番号)が3の時、LC570へ(選曲スイッチの状態から楽譜演奏データ先頭アドレス選択)(選曲番号が3の場合は起こらないためデバッグモード?) C56C : 81 04 " " cmpa #$04 //+/ C56E : 27 05 "' " beq LC575 //+/X0078(選曲番号)が4の時、LC575へ(選曲スイッチの状態から楽譜演奏データ先頭アドレス選択)(選曲番号が4の場合は起こらないためデバッグモード?) C570 LC570: //(X0078(選曲番号)が3の時)または、4以外の時:LC55Bより C570 : CE C6 03 " " ldx #$C603 //Xレジスタに#$C603(1曲目の先頭アドレス+3)を入れる C573 : 20 03 " " bra LC578 //LC578へ ; C575 LC575: //(X0078(選曲番号)が4の時):LC55Bより C575 : CE CC 03 " " ldx #$CC03 //Xレジスタに#$CC03(2曲目の先頭アドレス+3)を入れる C578 LC578: //LC570より C578 : 86 00 " " ldaa #$00 //+/Aレジスタに#$00を入れる(チャンネルA、B、C音量ゼロにする処理をする) C57A : 97 73 " s" staa X0073 //+/X0073(AY-3-8910のDA0~DA7に出力するデータ)に#$00を入れる C57C : C6 08 " " ldab #$08 //AY-3-8910アドレス指定:チャンネルA音量コントロールレジスタ__(チャンネルAエンベロープ無効(4bit目),音量0(0~3bit目)) C57E : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C581 : C6 09 " " ldab #$09 //AY-3-8910アドレス指定:チャンネルB音量コントロールレジスタ__(チャンネルBエンベロープ無効(4bit目),音量0(0~3bit目)) C583 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C586 : C6 0A " " ldab #$0A //AY-3-8910アドレス指定:チャンネルC音量コントロールレジスタ__(チャンネルCエンベロープ無効(4bit目),音量0(0~3bit目)) C588 : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C58B : C6 01 " " ldab #$01 //AY-3-8910アドレス指定:チャンネルAの音程上位4ビット(演奏開始楽譜データ3バイト目) C58D : 8D 1D " " bsr LC5AC //LC5AC(xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント)へ C58F : C6 06 " " ldab #$06 //AY-3-8910アドレス指定:ノイズ分周(演奏開始楽譜データ4バイト目) C591 : 8D 19 " " bsr LC5AC //LC5AC(xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント)へ C593 : C6 0B " " ldab #$0B //AY-3-8910アドレス指定:エンベロープ周期(演奏開始楽譜データ5バイト目) C595 : 8D 15 " " bsr LC5AC //LC5AC(xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント)へ C597 : C6 0C " " ldab #$0C //AY-3-8910アドレス指定:エンベロープ周期(演奏開始楽譜データ6バイト目) C599 : 8D 11 " " bsr LC5AC //LC5AC(xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント)へ C59B : C6 0D " " ldab #$0D //AY-3-8910アドレス指定:エンベロープ波形(演奏開始楽譜データ7バイト目) C59D : 8D 0D " " bsr LC5AC //LC5AC(xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント)へ C59F : 96 86 " " ldaa X0086 //*/AレジスタにX0086(無意味、デバッグ用?)を入れる C5A1 : 26 01 "& " bne LC5A4 //*/X0086が0でない時、LC5A4(X0086をゼロにする)へ C5A3 : 39 "9" rts //サブルーチン終了 ; C5A4 LC5A4: //X0086(無意味、デバッグ用?)をゼロにする:LC578より C5A4 : 7F 00 86 " " clr X0086 //X0086(無意味、デバッグ用?)クリア C5A7 : 39 "9" rts //サブルーチン終了(LC578へ戻る) ; C5A8 LC5A8: //課金分終了処理 C5A8 : 0F " " sei //割り込み禁止 C5A9 : 7E C0 57 "~ W" jmp LC057 //LC057(課金分終了処理)へ ; C5AC LC5AC: //xレジスタにセットされた現在の読み取りポイントアドレスにある値をAY-3-8910に書き込み、xレジスタインクリメント C5AC : BD C1 DA " " jsr LC1DA //xレジスタにセットされた現在の読み取りポイントアドレスにある値をX0073に入れる C5AF : BD C1 BB " " jsr LC1BB //LC1BB(AY-3-8910書き込みサブルーチン)へ C5B2 : 08 " " inx //楽譜データ次の読み取りポイントアドレス指定 C5B3 : 39 "9" rts //サブルーチン終了 ; //到達しないコード C5B4 : BD C1 E2 " " jsr LC1E2 C5B7 : BD C1 C3 " " jsr LC1C3 C5BA : 08 " " inx C5BB : 39 "9" rts ; C5BC : FF FF FF " " stx XFFFF C5BF : FF 26 01 " & " stx X2601 C5C2 : 39 "9" rts ; C5C3 : 7F 00 86 " " clr X0086 C5C6 : 39 "9" rts ; C5C7 : 0F " " sei C5C8 : 7E 80 5F "~ _" jmp L805F ; C5CB : BD 81 E2 " " jsr L81E2 C5CE : BD 81 C3 " " jsr L81C3 C5D1 : 08 " " inx C5D2 : 39 "9" rts ;
//以降演奏データとPCMデータ




//アドレス空間と変数の予想//////////////////////////////////
//X0000~X001Fは特定の用途のレジスタ
//X0020~X0040は外部領域
//X0040~X00FFは内蔵ram
//X0100~XFFFFは外部領域
X0000(ポート1データ方向レジスタ)※HD6301V1のみでHD63A03XPには無し
X0001(ポート2データ方向レジスタ)
X0002(ポート1)※HD6301V1のみでHD63A03XPには無し
X0003(ポート2)
X0004(ポート3データ方向レジスタ)※HD6301V1のみでHD63A03XPには無し
X0005(ポート4データ方向レジスタ)※HD6301V1のみでHD63A03XPには無し
X0006(ポート3)※HD6301V1のみでHD63A03XPには無し
X0007(ポート4)※HD6301V1のみでHD63A03XPには無し
X0008(タイマーコントロール/ステータスレジスタ1)
X000F(タイマーコントロール/ステータスレジスタ2)
X0010(レート、モードコントロールレジスタ)
X0011(tx、rxコントロールステータスレジスタ)
X0014(RAM/ポート5コントロールレジスタ)(STBY_PWR,RAME,-,-,HLTE,MRE,IRQ2_E,IRQ1_E)
X0015(ポート5)
X0016(ポート6データ方向レジスタ)
X0017(ポート6)
X001B(タイマーコントロール/ステータスレジスタ3)

X0064:LC51E,LC530ループカウント用
X0065:PCM再生アドレス
X0067:PCMデータサイズ
X0069:LC26E内Xレジスタ一時退避用(現在の楽譜再生位置記録(楽譜データブロック7バイト目))
X006B:LC53E内Xレジスタ一時退避用(現在の楽譜再生位置記録?)
X006D:LC365内Xレジスタ一時退避用(不明)
X006F:楽曲テンポ
X0071:LC53E内ウェイト回数(ノイズ音発音時間)
X0073:AY-3-8910のDA0~DA7に出力するデータ
X0074:赤線特殊PCM再生用?(チャンネルa_Ltone指定用)
X0075:PCM1再生回数
X0076:PCM2再生回数
X0077:PCM再生番号
X0078:選曲番号
X0079:コイン投入モード(0:通常,1:コイン投入モード)
X007A:課金動作タイマーカウンタ用
X007B:「ディップスイッチの使用法B」「タイマー1」設定用(30,60,90,120が入る)
X007C:課金後初期値#$3Dが入る(課金動作タイマー初期値保存用?)
X007E:テストコイン投入ボタン(茶線)のコイン入回数
X007F:「ディップスイッチの使用法B」「コイン枚数」設定用(1~4が入る)
X0080:「ディップスイッチの使用法B」「タイマー2 周回数」設定用(0~7が入る)
X0081:「ディップスイッチの使用法B」「タイマー2 周回数」設定用(0~7が入る)(テストコイン投入モードの際保存)
X0082:パワトラ点滅保存用
X0084:「定位置で止まる」スイッチ押されたフラグ?(1で定位置終了)
X0085:LC4E5で使用(#$01ストアのみで無意味、デバッグ用?)
X0086:LC578で使用(無意味、デバッグ用?)
X0089:AY-3-8910のポートB(ディップスイッチ)の状態保存用
X008A:筐体側面の赤色プッシュスイッチ押された時間カウント用(LC02Cでのみ使用)
X008E:LC279内で使用(無意味な用法)
X008D:PCM再生ボタン読み取り用
//////////////////////////////////



・演奏データ
プログラムの解析があらかた済んだので次に演奏データの解析を行います。
こちらもプログラムを読んでいけばわかるので、結果のみまとめます。
筐体にJASRACのシールが貼られてので、ROM内の演奏データの記載は行いません。
代わりに差し替えた演奏データを例としてページの最後の方に載せておきます。


このプログラムでは2種類の曲をスイッチで選択して演奏することができます。

1曲目:$0417と$0570に読み込み先頭アドレス+0xC600を記載
テンポ:$040Eで設定(2バイト)
2曲目:$042Cと$0575に読み込み先頭アドレス+0xCC00を記載
テンポ:$0423で設定(2バイト)

演奏データは独自のフォーマットとなっています。
基本的に、
ヘッダ+ブロック+ブロック+…
という感じでAY-3-8910のレジスタを意識した構成となっています。

ヘッダやブロックは、8バイトで一組となっているようです。

------ヘッダ------
0:なし
1:なし
2:なし
3:チャンネルA音程上位4bit
4:ノイズ分周
5:エンベロープL Byte
6:エンベロープH Byte
7:エンベロープ波形(変更しても反映なし)
------ ------

------ブロック------
0:音程A ※
1:音程B L Byte
2:音程B H Byte
3:音程C L Byte
4:音程C H Byte
5:設定1
6:設定2
7:長さ

//※Byte0
0xFFのときループ
0x0Fのときリセット
それ以外は音程A L Byte

//Byte5_設定1
bit:7_未使用
bit:6_未使用
bit:5_未使用
bit:4_チャンネルAエンベロープ有効無効
bit:3_ノイズA 1でoff,0でon
bit:2_チャンネルCミキサ 1でoff,0でon
bit:1_チャンネルBミキサ 1でoff,0でon
bit:0_チャンネルAミキサ 1でoff,0でon

//Byte6_設定2
bit:4_チャンネルAエンベロープ有効無効
bit:0_チャンネルAエンベロープ有効無効

//Byte7
音長
値が大きいほど、長く発音

------ ------

音程の計算はAY-3-8910の音程設定方法と同じです。ただし、チャンネルAのみ音程上位4bitはヘッダ部分でセットし、以降変更できません。

基本的にPSGのチャンネルAの音域は狭く、BとCをメロディーに使う感じでしょうか?
ノートオンやノートオフは、ブロックデータのByte5で行います。

演奏データの終端ではブロックデータのByte0を0xFFまたは0x0Fにします。
0xFFの時にはループ、0x0Fの時にはリセットします。

1つのチャンネルのキーオン・キーオフするだけでも8バイト使ってしまうので、できるだけ複数のチャンネルを同時キーオン・キーオフするように工夫しないと、ROM内に収まりきらなくなりそうです。




試しに、ROMの演奏データを書き換えて演奏した例を示します。


 



変換ツール等は作成していないので、演奏データの差し替えは地道に打ち込みました。



・4bit PCMデータ
さいごにPCMデータの解析をしてみます。
と言っても、仕組み的に4bit+4bitのモノラルPCMデータが連なっているだけなので、何も難しくありません。

・データ構造
[4bit+4bit],[4bit+4bit],…

再生時は、PSGによる演奏を一時停止し、4bitずつROMにあるPCMデータを読み取り、AY-3-8910の汎用ポートIOA0~IOA3に出力していきます。


//コネクタ配線とPCMデータアドレス//////////////////////////
コネクタ緑線
「ばいばいまたね」x2
$C454で先頭アドレスの位置を設定
PCMデータ先頭アドレス:$DA00
$C459でサイズ(バイト)を設定
PCMデータサイズ:$07FF

コネクタ青
リセット

コネクタ茶線
無し

コネクタ白線
「いっぱいあそんでね」
$C4ACで先頭アドレスの位置を設定
PCMデータ先頭アドレス:$F200
$C4B1でサイズ(バイト)を設定
PCMデータサイズ:$07FF

コネクタ橙線
「こんにちは」
$C46Eで先頭アドレスの位置を設定
PCMデータ先頭アドレス:$E200
$C473でサイズ(バイト)を設定
PCMデータサイズ:$07FF

コネクタ黄線
「今日はどうですかな」
$C48Dで先頭アドレスの位置を設定
PCMデータ先頭アドレス:$EA00
$C492でサイズ(バイト)を設定
PCMデータサイズ:$07FF

コネクタ赤線
ノイズ音  ※演奏データと被るため
$C439で先頭アドレスの位置を設定
PCMデータ先頭アドレス:$D200
$C43Fでサイズ(バイト)を設定
PCMデータサイズ:$07FF
////////////////////////////

PCMデータの差し替えもそれほど難しくなさそうです。注意点としては量子化幅が一定ではないため、通常のPCMデータでは、DAコンバートした際のバイナリ-アナログの直線性を失います。
本来ならサンプリング時に標本値の加工が必要になるのですが、4bitPCMの時点で音質が悪いため、標本値の加工無しでもたいして変わらなさそうです。



4bitPCMデータの作成はGB用PCm変換ツールが使えそうです。

PCM Driver for Gameboy - Bookworm's Library

サンプリングレートを高く設定するほど、実機上での再生速度が落ちます。



・ROMデータ

さいごに、研究目的での使用を想定してROMデータ(演奏データと一部のPCMデータ書き換え済み)を載せておきます。

演奏データは「よいまちカンターレ」のサビ部分に置換してあります。
一部のPCMデータは「時は来た」と「時、来てるぞ」に置換してあります。


「・回路」の項の回路図の通り組み立てれば動くはずです。


ROMデータは16kBのROM(27128、27C128など)にそのまま書き込むことができます。
16kB以外のROM(27C256、27C512など)に書き込む場合は、データをROM内の最後の方(27C256の場合はROM内アドレス$4000~$7FFF、27C512の場合はROM内アドレス$C000~$FFFF)に配置します。


演奏データ書き換え例の動画では、256kbitのSRAMに電気二重層コンデンサを付けたものを使用しています。
















2022年9月17日土曜日

電子部品図鑑お問い合わせページ

 電子部品図鑑(https://eleparts.ml/)のお問い合わせページです。
ページ下部に「連絡フォーム」があるのでご利用ください。

2022年8月2日火曜日

音源ICの使い方

音源ICの使い方

過去にまとめた音源ICの使い方の一覧です。

・FM音源

・OPL系
    ・YM3812 (OPL2)

・OPM系
    ・YM2151 (OPM)


・ PSG・DCSG系音源



・波形合成音源



・PCM音源



・その他

    ・SOUND8  ※動作未検証なので注意






2022年6月25日土曜日

YM2151 (OPM)の使い方

YM2151の使い方

注意:このサイトの内容を鵜呑みにし、事故や損失を招いた場合でも当方は一切の責任は負いかねます。自己責任でお願いします。






YM2151は4オペレータのFM音源IC (OPM)です。(LSIですが便宜上ICと呼びます。)

X68KやMSX拡張シンセサイザユニット「YAMAHA SFG-01」などに用いられていました。

アーケードゲームなどにもよく用いられていたようで、中古品も割と豊富に出回っているため、入手は比較的容易です。
ただし、某オークションサイト等では偽物も多く出回っているため、注意が必要です。

確実に本物のYM2151を入手したい場合、アーケード基板を購入して部品取りをして利用すると良いでしょう。

本物を手軽に入手したい場合、東京ラジオデパートの1Fでガチャガチャ方式で販売している"FM音源伝説"がおすすめです。(動作保証していなく、故障している可能性もあります。注意してください。)





YM2151単体では、音を出すことはできず、YM3012などの専用のDACが必要です。





・YM2151の特徴


・4オペレータ8アルゴリズム、同時発音数 : 8
・24ピンDIP
・ハードウェアエンベロープ
・ハードウェアLFO
・ノイズジェネレータ


・YM2151のブロック図

YM2151のデータシートより抜粋


・ピン配置



ピン番号 名称 I/O 機能
1 VSS(GND) - グランド端子です。
2 ~IRQ O 割り込み要求端子です。

内部タイマーカウンタにより、割り込み要求がされるようです。

オープンドレイン出力なので、プルアップ抵抗が必要になります。
3 ~IC I リセット端子です。
この端子がLの期間、内部レジスタを初期化します。
4 A0 I アドレス / データセレクト端子です。

この端子がLの場合、D0~D7はアドレス入力端子となります。

Hの場合、D0~D7はデータ入力端子となります。
5 ~WR I ライト端子です。

この端子がLでかつ、「~CS」がLの期間、
D0~D7端子の状態が内部レジスタへラッチされます。
6 ~RD I リード端子です。

この端子がLでかつ、「A0」がHでかつ、「~CS」がLの期間、
内部ステータスレジスタの状態がD0~D7端子へラッチされます。
7 ~CS I チップセレクト端子です。

この端子がLの期間、「~WR」または「~RD」端子がLの時、
書き込み・読み込みができます。
8 CT1 O 汎用出力端子です。

$1Bのbit7に割り当てられています。
9 CT2 O 汎用出力端子です。

$1Bのbit6に割り当てられています。
10 D0 I/O データピンです。
11 VSS(GND) - グランド端子です。
12 D1 I/O データピンです。
13 D2 I/O データピンです。
14 D3 I/O データピンです。
15 D4 I/O データピンです。
16 D5 I/O データピンです。
17 D6 I/O データピンです。
18 D7 I/O データピンです。
19 SH2 O チャンネル2のアナログ出力ホールド端子です。

チャンネル1のデータラッチ信号としても使います。(要検証)

YM3012などのDACへ接続します。
20 SH1 O チャンネル1のアナログ出力ホールド端子です。

チャンネル2のデータラッチ信号としても使います。(要検証)

YM3012などのDACへ接続します。
21 SO O シリアルデータ出力端子です。

YM3012などのDACへ接続します。
22 VDD(+5V) - 電源端子です。
+5Vの電源を用意してください。
23 Φ1 O クロック出力端子です。

YM3012などのDACへ接続します。
24 ΦM I クロック入力端子です。

標準で3.58MHzを入力します。
最大で4.0MHzまで入力できます。








・YM2151のレジスタ(書き込み専用)

YM2151は$00~$FFの256バイトのレジスタを持っています。

アドレス
レジスタ名 機能
$00 - 未使用
$01 TEST テスト用のレジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
TEST_6TEST_5TEST_4TEST_3TEST_2TEST_1LFO_RESETTEST_0


TEST_0 ~ TEST_6 には常に0を書き込んだ状態にしてください。


LFO_RESET:
このビットをHにすると、LFOが停止します。

LFO = H : LFOの停止
LFO = L : LFOを動作(位相がリセットされます。)

$02~07 - 未使用
$08 KON KEY ON
キーオン・キーオフレジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
-SN_3SN_2SN_1SN_0CH_2CH_1CH_0


SN:
スロット番号です。
オペレータのオンオフを指定します。
SN = H : キーオン
SN = L : キーオフ

SN_0 : M1(OP1)
SN_1 : C1(OP2)
SN_2 : M2(OP3)
SN_3 : C2(OP4)



CH:
チャンネル番号を指定します。
CH = 0~7(0x0~0x7) : キーオン・キーオフしたいチャンネル CH1~CH8

$09~$0E - 未使用
$0F NOISE Noise Generator
ノイズジェネレータの設定レジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
NE--NFRQ_4NFRQ_3NFRQ_2NFRQ_1NFRQ_0


NE:
ノイズ有効無効ビットです。
有効にすると、スロット32(CH8のC2)がノイズジェネレータと入れ替わります。

NE = L : 無効
NE = H :有効


NFRQ:
ノイズ周波数決定ビットです。

  fn = ΦM / (32 * NFRQ)    [Hz]

fn : ノイズ周波数[Hz]
ΦM : 入力クロック周波数[Hz]
NFRQ = 0~31(0x00~0x1F) :ノイズ周波数決定ビット

$10 CLKA1 内部タイマーAの設定値10bit中の上位8bitです。

CLKA2($11)に下位2bitをセットします。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
NA_9NA_8NA_7NA_6NA_5NA_4NA_3NA_2


NA:
タイマーAの設定値です。

  Ta  = 64 * (1024 - NA) / ΦM    [s]

Ta : タイマーAオーバーフロー周期[s]
NA = 0~1023(0x000~0x3FF) :タイマー周期決定ビット
ΦM : 入力クロック周波数[Hz]

$11 CLKA2 内部タイマーAの設定値10bit中の下位2bitです。

CLKA1($10)に上位8bitをセットします。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
------NA_1NA_0


$12 CLKB 内部タイマーBの設定値8bitです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
CLKB_7CLKB_6CLKB_5CLKB_4CLKB_3CLKB_2CLKB_1CLKB_0


CLKB:
タイマーBの設定値です。

  Tb  = 1024 * (256 - CLKB) / ΦM    [s]

Tb : タイマーAオーバーフロー周期[s]
CLKB = 0~255(0x00~0xFF) :タイマー周期決定ビット
ΦM : 入力クロック周波数[Hz]

$13 - 未使用
$14 TIM_CONF 内部タイマーの動作設定ビット群です。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
CSM-F_RESET_BF_RESET_AIRQ_EN_BIRQ_EN_ALOAD_BLOAD_A


CSM:
このビットをHにすると、タイマーAのオーバーフロービットがHの時に、
すべてのスロットをキーオンします。


F_RESET:
このビットにHを書き込むと、タイマーのオーバーフロービットをクリアします。
Lを書き込んだ場合、変化しません。

F_RESET_A <- H : タイマーAのオーバーフロービットをクリア
F_RESET_B <- H : タイマーBのオーバーフロービットをクリア


IRQ_EN:
このビットをHにすると、タイマーがオーバーフローした時に、割り込み要求出力をします。

IRQ_EN_A = H : タイマーAによる割り込み要求出力を有効
IRQ_EN_B = H : タイマーBによる割り込み要求出力を有効


LOAD:
このビットをHにすると、タイマーが動作します。Lにすると、停止します。

LOAD_A = H : タイマーAを動作させる。
LOAD_B = H : タイマーBを動作させる。

$15~$17 - 未使用
$18 LFRQ LOW FREQUENCY
LFO周波数設定レジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
LFRQ_7LFRQ_6LFRQ_5LFRQ_4LFRQ_3LFRQ_2LFRQ_1LFRQ_0


LFRQ:
LFRQ = 0~255 (0x00~0xFF) : LFO発振周波数決定レジスタ
(入力クロック周波数 ΦM = 3.579545Mhzの場合、0.0008Hz~52.9127hz)

LFRQにセットする値が大きいほど発振周波数が高くなります。

$19 PMD_AMD PHASE MODULATION DEPTH / AMPLITUDE MODULATION DEPTH
位相変調・振幅変調深度設定レジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
LFO_MD_FLFO_MD_6LFO_MD_5LFO_MD_4LFO_MD_3LFO_MD_2LFO_MD_1LFO_MD_0


LFO_MD_F:
変調データのセット選択ビットです。LFO_MD_0~LFO_MD_6のデータのセット先を決定します。

LFO_MD_F = H : PMD(位相変調深度レジスタへ書き込み)
LFO_MD_F = L : AMD(振幅変調深度レジスタへ書き込み)


LFO_MD:
変調深度をセットします。
PMDの場合、2の補数でセットします。
AMDの場合、補数なしでセットします。

LFO_MD = -64 ~ 63 (0x40 ~ 0x3F): PMD深度のセット(LFO_MD_F = H)
LFO_MD = 0~127 (0x00 ~ 0x7F): AMD深度のセット(LFO_MD_F = L)

$1A - 未使用
$1B CT_W CONTROL OUTPUT / WAVE FORM
汎用端子出力ビット・LFO変調波形設定レジスタです。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
CT2CT1----LFO_W_1LFO_W_0


CT:
汎用出力ポートに出力する値をセットします。

CT1 = H or L : CT1端子(8pin)にHまたはLを出力します。
CT2 = H or L : CT2端子(9pin)にHまたはLを出力します。


LFO_W:
LFO変調波形決定ビットです。

LFO_W_1LFO_W_0波形
LL鋸歯状波
LH矩形波
HL三角波
HHノイズ

$1C~$1F - 未使用
$20~27 LR_FB_CON チャンネル出力・フィードバック・アルゴリズム設定レジスタです。

$20~$27にCH1~CH8が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
RL_RRL_LFL_2FL_1FL_0CONECT_2CONECT_1CONECT_0


RL:
LEFT CHANNEL ENABLE / RIGHT CHANNEL ENABLE
LまたはRチャンネルの出力を決定します。

RL_R = H : Rチャンネルの出力を有効にします。
RL_L = H : Lチャンネルの出力を有効にします。


FL:
SELF FEEDBACK LEVEL
M1(OP1)のフィードバックレベルを指定します。

   Fb_level = π * (2 ^ (-5 + FL))   ※FL ≠ 0

Fb_leve : フィードバックレベル
FL = 0~7 (0x0 ~ 0x7) : フィードバックレベル設定ビット(FL = 0のときのみ Fb_level = 0)


CONECT:
CONNECTION
コネクションを設定します。

CONECT_2CONECT_1CONECT_0アルゴリズム
LLL
0. シリアル4連モード

[M1]→[C1]→[M2]→[C2]→OUT

※[M1]は自己フィードバックあり
LLH
1. ダブル変調シリアル3連モード

[M1]→⊕→[M2]→[C2]→OUT
[C1]↗

※[M1]は自己フィードバックあり
LHL
2. ダブル変調モード①

[C1]→[M2]→⊕→[C2]→OUT
[M1]↗

※[M1]は自己フィードバックあり
LHH
3. ダブル変調モード②

[M1]→[C1]→⊕→[C2]→OUT
[M2]↗

※[M1]は自己フィードバックあり
HLL
4. シリアル2連・2パラレルモード

[M1]→[C1]→⊕→OUT
[M2]→[C2]↗

※[M1]は自己フィードバックあり
HLH
5. 共通変調3パラレルモード

↗[C1]↘
[M1]→[M2]→⊕→OUT
↘[C2]↗

※[M1]は自己フィードバックあり
HHL
6. シリアル2連+2サインモード

[M2]↘
[M1]→[C1]→⊕→OUT
[C2]↗

※[M1]は自己フィードバックあり
HHH
7. パラレルサイン合成モード

[M1]↘
[C1]↘
[M2]→⊕→OUT
[C2]↗

※[M1]は自己フィードバックあり

$28~$2F KC KEY CODE
音程設定レジスタです。

$28~$2FにCH1~CH8が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
-OCT_2OCT_1OCT_0NOTE_3NOTE_2NOTE_1NOTE_0


OCT:
オクターブ設定ビットです。

OCT = 0~7 (0x0 ~ 0x7) : 音程のセット


NOTE:
ノート設定ビットです。

入力クロックΦM = 3.579545MHzの場合
音程NOTE
C#0 (0x0)
D1 (0x1)
D#2 (0x2)
E4 (0x4)
F5 (0x5)
F#6 (0x6)
G8 (0x8)
G#9 (0x9)
A10 (0x10)
A#12 (0x12)
B13 (0x13)
C14 (0x14)
※NOTE = 3, 7, 11の場合は不明



-- 出力周波数foutとOCT, NOTEの関係 --
ΦM = 3.579545MHz , OCT = 4 , NOTE =10 の時、440Hz(A)を出力

   n = NOTE - ( (NOTE - ((NOTE-1)/3) ) / 3) ※nを求める場合のみ剰余は切り捨てで演算

   fout = 440 * ( 2 ^ (-4 + OCT + (n - 8)/12 ) ) *(ΦM / 3579545)
   = ( 2 ^ (OCT + (n/12)) ) * ΦM * (11 / 1431818) * (2 ^ (-8/12))
   = ( 2 ^ (OCT + (n/12)) ) * ΦM * (1 / 206624)
   = ( 2 ^ (OCT + (n/12)) ) * ΦM * (4.83970μ)   [Hz]

OCT : オクターブ設定ビット
NOTE : ノート設定ビット
n : ノート設定ビットから求めた音階(NOTE=0~14 → n=0~11)
ΦM : 入力クロック周波数[Hz]

$30~$37 KF KEY FRACTION
キーフラクション(音程の微調整)レジスタです。

$30~$37にCH1~CH8が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
KF_5KF_4KF_3KF_2KF_1KF_0--


KF:
キーフラクションビットです。

KF = 0~63 (0x00 ~ 0x3F) : キーフラクションのセット

KFが1つ大きくなるごとに音程が1.6(100/64)セント上がります。

$38~$3F PMS_AMS PHASE MODULATION SENSITIVITY / AMPLITUDE MODULATION SENSITIVITY
LFO位相変調・振幅変調感度設定レジスタです。
このレジスタではチャンネルごとにLFOの感度を設定できます。

$38~$3FにCH1~CH8が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
-PMS_2PMS_1PMS_0--AMS_1AMS_0


PMS:
LFO位相変調感度設定ビットです。

PMS = 0~7 (0x0 ~ 0x7) : PMS感度のセット

PMS_2PMS_1PMS_0最大変調度(セント)
LLL
0
LLH
±5
LHL
±10
LHH
±20
HLL
±50
HLH
±100
HHL
±400
HHH
±700


AMS:
LFO振幅変調感度設定ビットです。
$A0~$BFの「AMS_EN」ビットがHのとき、LFOによる振幅変調がかかります。

最大振幅変調度
AMS = 0 : 0dB
AMS = 1 : 23.90625dB
AMS = 2 : 47.8215dB
AMS = 3 : 95.625dB

$40~5F DT1_MUL DETUNE(1) / PHASE MULTIPLY
スロット倍率微調整・スロット倍率設定レジスタです。

$40~$5FにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
-DT1_2DT1_1DT1_0MUL_3MUL_2MUL_1MUL_0


DT1:
スロット周波数の倍率微調整ビットです。
数セント程度のデチューンができます。

DT1 = 0~7 (0x0 ~ 0x7) : デチューンのセット


MUL:
スロット周波数の倍率設定ビットです。

MUL = 0~15 (0x0 ~ 0xF) :スロット倍率のセット

   Ns = MUL    (MUL ≠ 0)
   Ns = 1/2     (MUL = 0)

Ns : 基本周波数からの倍率[倍]

$60~$7F TL TOTAL LEVEL
スロット出力レベル設定レジスタです。

$60~$7FにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
-TL_6TL_5TL_4TL_3TL_2TL_1TL_0


TL:
スロット出力レベル設定レジスタです。
TL = 0で最大信号レベル、127で最小信号レベルとなります。

TL = 0~127 (0x00 ~ 0x7F) : スロット出力レベルのセット

減衰量Atlは、

Atl = 0.75 * TL    [dB]

となります。

$80~$9F KS_AR KEY SCALING / ATTACK RATE
ピッチによるエンベロープ補正・エンベロープアタックレート設定レジスタです。

$80~$9FにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
KS_1KS_0-AR_4AR_3AR_2AR_1AR_0


KS:
ピッチによるエンベロープ時間補正ビットです。
実際の楽器の場合、ピッチが高くなるにつれ、エンベロープ時間が短くなります。
このビットは、この特性を再現するためのビットです。

KS = 0~3 (0x0 ~ 0x3) : ピッチによるエンベロープ時間補正のセット(値が大きいほどエンベロープ時間を短くします。)



AR:
出力レベルのエンベロープ制御する際のアタック時間を設定するビットです。
この値が小さいほどアタック時間が増加します。

AR = 0~31 (0x0 ~ 0x1F) : アタック時間のセット

$A0~$BF AMS_EN_D1R AMPLITUDE MODULATION SENSITIVITY ENABLE / FIRST DECAY RATE
AMS有効フラグ・エンベロープファーストディケイレート設定レジスタです。

$80~$9FにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
AMS_EN--D1R_4D1R_3D1R_2D1R_1D1R_0


AMS_EN:
スロットLFO振幅変調有効無効設定ビットです。
有効にすると、LFOより振幅変調を行います。振幅変調深度は、$37~$3Fにある「PMS_AMS」レジスタのbit0, bit1で決めます。

AMS_EN = H : AMS有効
AMS_EN = L : AMS無効


D1R:
出力レベルのエンベロープ制御する際のファーストディケイ時間(ファーストディケイレベルまで移行する時間)を設定するビットです。
この値が小さいほどファーストディケイ時間が増加します。

D1R = 0~31 (0x00 ~ 0x1F) : ファーストディケイ時間のセット

$C0~$DF DT2_D2R DETUNE(2) / SECOND DECAY RATE
スロット倍率粗調整・エンベロープセカンドディケイレート設定レジスタです。

$C0~$DFにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
DT2_1DT2_0-D2R_4D2R_3D2R_2D2R_1D2R_0


DT2:
スロット周波数の倍率粗調整ビットです。
数セント程度のデチューンができます。

DT2 = 0~3 (0x0 ~ 0x3) : デチューンのセット

DT2_1DT2_0デチューン[セント] (x n倍)
HH+950 (1.731)
HL+781 (x1.570)
LH+600 (x1.414)
LL+0 (x1)



D2R:
出力レベルのエンベロープ制御する際のセカンドトディケイ時間(0レベルまで移行する時間)を設定するビットです。
この値が小さいほどセカンドディケイ時間が増加します。

D2R = 0~31 (0x00 ~ 0x1F) : セカンドトディケイ時間のセット

$EF~$FF D1L_RR FIRST DECAY LEVEL / RELEASE RATE
エンベロープファーストディケイレベル・エンベロープセカンドディケイレート設定レジスタです。

$C0~$DFにSLOT1~SLOT32が割り当てられています。


bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
D1L_3D1L_2D1L_1D1L_0RR_3RR_2RR_1RR_0


D1L:
出力レベルのエンベロープ制御する際のファーストディケイレベル(ファーストディケイ時間経過後の出力レベル)を設定するビットです。
この値が小さいほどファーストディケイレベルが大きくなります。

D1L = 0~15 (0x0 ~ 0xF) : ファーストディケイレベルのセット

ファーストディケイレベルfdl[dB]は、

   fdl = -3 * D1L   [dB]   : ※D1L ≠ 0xF
   fdl = -3 * D1L -48   [dB]   : ※D1L = 0xF

となります。


RR:
出力レベルのエンベロープ制御する際のリリース時間(キーオフ後に出力レベルが0移行するまでのに時間)を設定するビットです。
この値が小さいほどリリース時間が増加します。

RR = 0~15 (0x0 ~ 0xF) : リリース時間のセット



レジスタマップ

YM2151の内部レジスタのレジスタマップ(ビットマップ)です。

"レジスタビット名"_"ビット名またはチャンネルNO.またはスロットNO."_"第何ビット"
の形で表しています。

bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
$00
$01 TEST_6 TEST_5 TEST_4 TEST_3 TEST_2 TEST_1 LFO_RESET TEST_0
$02
$03
$04
$05
$06
$07
$08 SN_3 SN_2 SN_1 SN_0 CH_2 CH_1 CH_0
$09
$0A
$0B
$0C
$0D
$0E
$0F NE
NFRQ_4 NFRQ_3 NFRQ_2 NFRQ_1 NFRQ_0
$10 NA_9 NA_8 NA_7 NA_6 NA_5 NA_4 NA_3 NA_2
$11 NA_1 NA_0
$12 CLKB_7 CLKB_6 CLKB_5 CLKB_4 CLKB_3 CLKB_2 CLKB_1 CLKB_0
$13
$14 CSM F_RESET_B F_RESET_A IRQ_EN_B IRQ_EN_A LOAD_B LOAD_A
$15
$16
$17
$18 LFRQ_7 LFRQ_6 LFRQ_5 LFRQ_4 LFRQ_3 LFRQ_2 LFRQ_1 LFRQ_0
$19 LFO_MD_F LFO_MD_6 LFO_MD_5 LFO_MD_4 LFO_MD_3 LFO_MD_2 LFO_MD_1 LFO_MD_0
$1A
$1B CT2 CT1 LFO_W_1 LFO_W_0
$1C
$1D
$1E
$1F
$20 RL_CH1_R RL_CH1_L FL_CH1_2 FL_CH1_1 FL_CH1_0 CONECT_CH1_2 CONECT_CH1_1 CONECT_CH1_0
$21 RL_CH2_R RL_CH2_L FL_CH2_2 FL_CH2_1 FL_CH2_0 CONECT_CH2_2 CONECT_CH2_1 CONECT_CH2_0
$22 RL_CH3_R RL_CH3_L FL_CH3_2 FL_CH3_1 FL_CH3_0 CONECT_CH3_2 CONECT_CH3_1 CONECT_CH3_0
$23 RL_CH4_R RL_CH4_L FL_CH4_2 FL_CH4_1 FL_CH4_0 CONECT_CH4_2 CONECT_CH4_1 CONECT_CH4_0
$24 RL_CH5_R RL_CH5_L FL_CH5_2 FL_CH5_1 FL_CH5_0 CONECT_CH5_2 CONECT_CH5_1 CONECT_CH5_0
$25 RL_CH6_R RL_CH6_L FL_CH6_2 FL_CH6_1 FL_CH6_0 CONECT_CH6_2 CONECT_CH6_1 CONECT_CH6_0
$26 RL_CH7_R RL_CH7_L FL_CH7_2 FL_CH7_1 FL_CH7_0 CONECT_CH7_2 CONECT_CH7_1 CONECT_CH7_0
$27 RL_CH8_R RL_CH8_L FL_CH8_2 FL_CH8_1 FL_CH8_0 CONECT_CH8_2 CONECT_CH8_1 CONECT_CH8_0
$28 OCT_CH1_2 OCT_CH1_1 OCT_CH1_0 NOTE_CH1_3 NOTE_CH1_2 NOTE_CH1_1 NOTE_CH1_0
$29 OCT_CH2_2 OCT_CH2_1 OCT_CH2_0 NOTE_CH2_3 NOTE_CH2_2 NOTE_CH2_1 NOTE_CH2_0
$2A OCT_CH3_2 OCT_CH3_1 OCT_CH3_0 NOTE_CH3_3 NOTE_CH3_2 NOTE_CH3_1 NOTE_CH3_0
$2B OCT_CH4_2 OCT_CH4_1 OCT_CH4_0 NOTE_CH4_3 NOTE_CH4_2 NOTE_CH4_1 NOTE_CH4_0
$2C OCT_CH5_2 OCT_CH5_1 OCT_CH5_0 NOTE_CH5_3 NOTE_CH5_2 NOTE_CH5_1 NOTE_CH5_0
$2D OCT_CH6_2 OCT_CH6_1 OCT_CH6_0 NOTE_CH6_3 NOTE_CH6_2 NOTE_CH6_1 NOTE_CH6_0
$2E OCT_CH7_2 OCT_CH7_1 OCT_CH7_0 NOTE_CH7_3 NOTE_CH7_2 NOTE_CH7_1 NOTE_CH7_0
$2F OCT_CH8_2 OCT_CH8_1 OCT_CH8_0 NOTE_CH8_3 NOTE_CH8_2 NOTE_CH8_1 NOTE_CH8_0
$30 KF_CH1_5 KF_CH1_4 KF_CH1_3 KF_CH1_2 KF_CH1_1 KF_CH1_0
$31 KF_CH2_5 KF_CH2_4 KF_CH2_3 KF_CH2_2 KF_CH2_1 KF_CH2_0
$32 KF_CH3_5 KF_CH3_4 KF_CH3_3 KF_CH3_2 KF_CH3_1 KF_CH3_0
$33 KF_CH4_5 KF_CH4_4 KF_CH4_3 KF_CH4_2 KF_CH4_1 KF_CH4_0
$34 KF_CH5_5 KF_CH5_4 KF_CH5_3 KF_CH5_2 KF_CH5_1 KF_CH5_0
$35 KF_CH6_5 KF_CH6_4 KF_CH6_3 KF_CH6_2 KF_CH6_1 KF_CH6_0
$36 KF_CH7_5 KF_CH7_4 KF_CH7_3 KF_CH7_2 KF_CH7_1 KF_CH7_0
$37 KF_CH8_5 KF_CH8_4 KF_CH8_3 KF_CH8_2 KF_CH8_1 KF_CH8_0
$38 PMS_CH1_2 PMS_CH1_1 PMS_CH1_0 AMS_CH1_1 AMS_CH1_0
$39 PMS_CH2_2 PMS_CH2_1 PMS_CH2_0 AMS_CH2_1 AMS_CH2_0
$3A PMS_CH3_2 PMS_CH3_1 PMS_CH3_0 AMS_CH3_1 AMS_CH3_0
$3B PMS_CH4_2 PMS_CH4_1 PMS_CH4_0 AMS_CH4_1 AMS_CH4_0
$3C PMS_CH5_2 PMS_CH5_1 PMS_CH5_0 AMS_CH5_1 AMS_CH5_0
$3D PMS_CH6_2 PMS_CH6_1 PMS_CH6_0 AMS_CH6_1 AMS_CH6_0
$3E PMS_CH7_2 PMS_CH7_1 PMS_CH7_0 AMS_CH7_1 AMS_CH7_0
$3F PMS_CH8_2 PMS_CH8_1 PMS_CH8_0 AMS_CH8_1 AMS_CH8_0
$40 DT1_SLOT1_2 DT1_SLOT1_1 DT1_SLOT1_0 MUL_SLOT1_3 MUL_SLOT1_2 MUL_SLOT1_1 MUL_SLOT1_0
$41 DT1_SLOT2_2 DT1_SLOT2_1 DT1_SLOT2_0 MUL_SLOT2_3 MUL_SLOT2_2 MUL_SLOT2_1 MUL_SLOT2_0
$42 DT1_SLOT3_2 DT1_SLOT3_1 DT1_SLOT3_0 MUL_SLOT3_3 MUL_SLOT3_2 MUL_SLOT3_1 MUL_SLOT3_0
$43 DT1_SLOT4_2 DT1_SLOT4_1 DT1_SLOT4_0 MUL_SLOT4_3 MUL_SLOT4_2 MUL_SLOT4_1 MUL_SLOT4_0
$44 DT1_SLOT5_2 DT1_SLOT5_1 DT1_SLOT5_0 MUL_SLOT5_3 MUL_SLOT5_2 MUL_SLOT5_1 MUL_SLOT5_0
$45 DT1_SLOT6_2 DT1_SLOT6_1 DT1_SLOT6_0 MUL_SLOT6_3 MUL_SLOT6_2 MUL_SLOT6_1 MUL_SLOT6_0
$46 DT1_SLOT7_2 DT1_SLOT7_1 DT1_SLOT7_0 MUL_SLOT7_3 MUL_SLOT7_2 MUL_SLOT7_1 MUL_SLOT7_0
$47 DT1_SLOT8_2 DT1_SLOT8_1 DT1_SLOT8_0 MUL_SLOT8_3 MUL_SLOT8_2 MUL_SLOT8_1 MUL_SLOT8_0
$48 DT1_SLOT9_2 DT1_SLOT9_1 DT1_SLOT9_0 MUL_SLOT9_3 MUL_SLOT9_2 MUL_SLOT9_1 MUL_SLOT9_0
$49 DT1_SLOT10_2 DT1_SLOT10_1 DT1_SLOT10_0 MUL_SLOT10_3 MUL_SLOT10_2 MUL_SLOT10_1 MUL_SLOT10_0
$4A DT1_SLOT11_2 DT1_SLOT11_1 DT1_SLOT11_0 MUL_SLOT11_3 MUL_SLOT11_2 MUL_SLOT11_1 MUL_SLOT11_0
$4B DT1_SLOT12_2 DT1_SLOT12_1 DT1_SLOT12_0 MUL_SLOT12_3 MUL_SLOT12_2 MUL_SLOT12_1 MUL_SLOT12_0
$4C DT1_SLOT13_2 DT1_SLOT13_1 DT1_SLOT13_0 MUL_SLOT13_3 MUL_SLOT13_2 MUL_SLOT13_1 MUL_SLOT13_0
$4D DT1_SLOT14_2 DT1_SLOT14_1 DT1_SLOT14_0 MUL_SLOT14_3 MUL_SLOT14_2 MUL_SLOT14_1 MUL_SLOT14_0
$4E DT1_SLOT15_2 DT1_SLOT15_1 DT1_SLOT15_0 MUL_SLOT15_3 MUL_SLOT15_2 MUL_SLOT15_1 MUL_SLOT15_0
$4F DT1_SLOT16_2 DT1_SLOT16_1 DT1_SLOT16_0 MUL_SLOT16_3 MUL_SLOT16_2 MUL_SLOT16_1 MUL_SLOT16_0
$50 DT1_SLOT17_2 DT1_SLOT17_1 DT1_SLOT17_0 MUL_SLOT17_3 MUL_SLOT17_2 MUL_SLOT17_1 MUL_SLOT17_0
$51 DT1_SLOT18_2 DT1_SLOT18_1 DT1_SLOT18_0 MUL_SLOT18_3 MUL_SLOT18_2 MUL_SLOT18_1 MUL_SLOT18_0
$52 DT1_SLOT19_2 DT1_SLOT19_1 DT1_SLOT19_0 MUL_SLOT19_3 MUL_SLOT19_2 MUL_SLOT19_1 MUL_SLOT19_0
$53 DT1_SLOT20_2 DT1_SLOT20_1 DT1_SLOT20_0 MUL_SLOT20_3 MUL_SLOT20_2 MUL_SLOT20_1 MUL_SLOT20_0
$54 DT1_SLOT21_2 DT1_SLOT21_1 DT1_SLOT21_0 MUL_SLOT21_3 MUL_SLOT21_2 MUL_SLOT21_1 MUL_SLOT21_0
$55 DT1_SLOT22_2 DT1_SLOT22_1 DT1_SLOT22_0 MUL_SLOT22_3 MUL_SLOT22_2 MUL_SLOT22_1 MUL_SLOT22_0
$56 DT1_SLOT23_2 DT1_SLOT23_1 DT1_SLOT23_0 MUL_SLOT23_3 MUL_SLOT23_2 MUL_SLOT23_1 MUL_SLOT23_0
$57 DT1_SLOT24_2 DT1_SLOT24_1 DT1_SLOT24_0 MUL_SLOT24_3 MUL_SLOT24_2 MUL_SLOT24_1 MUL_SLOT24_0
$58 DT1_SLOT25_2 DT1_SLOT25_1 DT1_SLOT25_0 MUL_SLOT25_3 MUL_SLOT25_2 MUL_SLOT25_1 MUL_SLOT25_0
$59 DT1_SLOT26_2 DT1_SLOT26_1 DT1_SLOT26_0 MUL_SLOT26_3 MUL_SLOT26_2 MUL_SLOT26_1 MUL_SLOT26_0
$5A DT1_SLOT27_2 DT1_SLOT27_1 DT1_SLOT27_0 MUL_SLOT27_3 MUL_SLOT27_2 MUL_SLOT27_1 MUL_SLOT27_0
$5B DT1_SLOT28_2 DT1_SLOT28_1 DT1_SLOT28_0 MUL_SLOT28_3 MUL_SLOT28_2 MUL_SLOT28_1 MUL_SLOT28_0
$5C DT1_SLOT29_2 DT1_SLOT29_1 DT1_SLOT29_0 MUL_SLOT29_3 MUL_SLOT29_2 MUL_SLOT29_1 MUL_SLOT29_0
$5D DT1_SLOT30_2 DT1_SLOT30_1 DT1_SLOT30_0 MUL_SLOT30_3 MUL_SLOT30_2 MUL_SLOT30_1 MUL_SLOT30_0
$5E DT1_SLOT31_2 DT1_SLOT31_1 DT1_SLOT31_0 MUL_SLOT31_3 MUL_SLOT31_2 MUL_SLOT31_1 MUL_SLOT31_0
$5F DT1_SLOT32_2 DT1_SLOT32_1 DT1_SLOT32_0 MUL_SLOT32_3 MUL_SLOT32_2 MUL_SLOT32_1 MUL_SLOT32_0
$60 TL_SLOT1_6 TL_SLOT1_5 TL_SLOT1_4 TL_SLOT1_3 TL_SLOT1_2 TL_SLOT1_1 TL_SLOT1_0
$61 TL_SLOT2_6 TL_SLOT2_5 TL_SLOT2_4 TL_SLOT2_3 TL_SLOT2_2 TL_SLOT2_1 TL_SLOT2_0
$62 TL_SLOT3_6 TL_SLOT3_5 TL_SLOT3_4 TL_SLOT3_3 TL_SLOT3_2 TL_SLOT3_1 TL_SLOT3_0
$63 TL_SLOT4_6 TL_SLOT4_5 TL_SLOT4_4 TL_SLOT4_3 TL_SLOT4_2 TL_SLOT4_1 TL_SLOT4_0
$64 TL_SLOT5_6 TL_SLOT5_5 TL_SLOT5_4 TL_SLOT5_3 TL_SLOT5_2 TL_SLOT5_1 TL_SLOT5_0
$65 TL_SLOT6_6 TL_SLOT6_5 TL_SLOT6_4 TL_SLOT6_3 TL_SLOT6_2 TL_SLOT6_1 TL_SLOT6_0
$66 TL_SLOT7_6 TL_SLOT7_5 TL_SLOT7_4 TL_SLOT7_3 TL_SLOT7_2 TL_SLOT7_1 TL_SLOT7_0
$67 TL_SLOT8_6 TL_SLOT8_5 TL_SLOT8_4 TL_SLOT8_3 TL_SLOT8_2 TL_SLOT8_1 TL_SLOT8_0
$68 TL_SLOT9_6 TL_SLOT9_5 TL_SLOT9_4 TL_SLOT9_3 TL_SLOT9_2 TL_SLOT9_1 TL_SLOT9_0
$69 TL_SLOT10_6 TL_SLOT10_5 TL_SLOT10_4 TL_SLOT10_3 TL_SLOT10_2 TL_SLOT10_1 TL_SLOT10_0
$6A TL_SLOT11_6 TL_SLOT11_5 TL_SLOT11_4 TL_SLOT11_3 TL_SLOT11_2 TL_SLOT11_1 TL_SLOT11_0
$6B TL_SLOT12_6 TL_SLOT12_5 TL_SLOT12_4 TL_SLOT12_3 TL_SLOT12_2 TL_SLOT12_1 TL_SLOT12_0
$6C TL_SLOT13_6 TL_SLOT13_5 TL_SLOT13_4 TL_SLOT13_3 TL_SLOT13_2 TL_SLOT13_1 TL_SLOT13_0
$6D TL_SLOT14_6 TL_SLOT14_5 TL_SLOT14_4 TL_SLOT14_3 TL_SLOT14_2 TL_SLOT14_1 TL_SLOT14_0
$6E TL_SLOT15_6 TL_SLOT15_5 TL_SLOT15_4 TL_SLOT15_3 TL_SLOT15_2 TL_SLOT15_1 TL_SLOT15_0
$6F TL_SLOT16_6 TL_SLOT16_5 TL_SLOT16_4 TL_SLOT16_3 TL_SLOT16_2 TL_SLOT16_1 TL_SLOT16_0
$70 TL_SLOT17_6 TL_SLOT17_5 TL_SLOT17_4 TL_SLOT17_3 TL_SLOT17_2 TL_SLOT17_1 TL_SLOT17_0
$71 TL_SLOT18_6 TL_SLOT18_5 TL_SLOT18_4 TL_SLOT18_3 TL_SLOT18_2 TL_SLOT18_1 TL_SLOT18_0
$72 TL_SLOT19_6 TL_SLOT19_5 TL_SLOT19_4 TL_SLOT19_3 TL_SLOT19_2 TL_SLOT19_1 TL_SLOT19_0
$73 TL_SLOT20_6 TL_SLOT20_5 TL_SLOT20_4 TL_SLOT20_3 TL_SLOT20_2 TL_SLOT20_1 TL_SLOT20_0
$74 TL_SLOT21_6 TL_SLOT21_5 TL_SLOT21_4 TL_SLOT21_3 TL_SLOT21_2 TL_SLOT21_1 TL_SLOT21_0
$75 TL_SLOT22_6 TL_SLOT22_5 TL_SLOT22_4 TL_SLOT22_3 TL_SLOT22_2 TL_SLOT22_1 TL_SLOT22_0
$76 TL_SLOT23_6 TL_SLOT23_5 TL_SLOT23_4 TL_SLOT23_3 TL_SLOT23_2 TL_SLOT23_1 TL_SLOT23_0
$77 TL_SLOT24_6 TL_SLOT24_5 TL_SLOT24_4 TL_SLOT24_3 TL_SLOT24_2 TL_SLOT24_1 TL_SLOT24_0
$78 TL_SLOT25_6 TL_SLOT25_5 TL_SLOT25_4 TL_SLOT25_3 TL_SLOT25_2 TL_SLOT25_1 TL_SLOT25_0
$79 TL_SLOT26_6 TL_SLOT26_5 TL_SLOT26_4 TL_SLOT26_3 TL_SLOT26_2 TL_SLOT26_1 TL_SLOT26_0
$7A TL_SLOT27_6 TL_SLOT27_5 TL_SLOT27_4 TL_SLOT27_3 TL_SLOT27_2 TL_SLOT27_1 TL_SLOT27_0
$7B TL_SLOT28_6 TL_SLOT28_5 TL_SLOT28_4 TL_SLOT28_3 TL_SLOT28_2 TL_SLOT28_1 TL_SLOT28_0
$7C TL_SLOT29_6 TL_SLOT29_5 TL_SLOT29_4 TL_SLOT29_3 TL_SLOT29_2 TL_SLOT29_1 TL_SLOT29_0
$7D TL_SLOT30_6 TL_SLOT30_5 TL_SLOT30_4 TL_SLOT30_3 TL_SLOT30_2 TL_SLOT30_1 TL_SLOT30_0
$7E TL_SLOT31_6 TL_SLOT31_5 TL_SLOT31_4 TL_SLOT31_3 TL_SLOT31_2 TL_SLOT31_1 TL_SLOT31_0
$7F TL_SLOT32_6 TL_SLOT32_5 TL_SLOT32_4 TL_SLOT32_3 TL_SLOT32_2 TL_SLOT32_1 TL_SLOT32_0
$80 KS_SLOT1_1 KS_SLOT1_0 AR_SLOT1_4 AR_SLOT1_3 AR_SLOT1_2 AR_SLOT1_1 AR_SLOT1_0
$81 KS_SLOT2_1 KS_SLOT2_0 AR_SLOT2_4 AR_SLOT2_3 AR_SLOT2_2 AR_SLOT2_1 AR_SLOT2_0
$82 KS_SLOT3_1 KS_SLOT3_0 AR_SLOT3_4 AR_SLOT3_3 AR_SLOT3_2 AR_SLOT3_1 AR_SLOT3_0
$83 KS_SLOT4_1 KS_SLOT4_0 AR_SLOT4_4 AR_SLOT4_3 AR_SLOT4_2 AR_SLOT4_1 AR_SLOT4_0
$84 KS_SLOT5_1 KS_SLOT5_0 AR_SLOT5_4 AR_SLOT5_3 AR_SLOT5_2 AR_SLOT5_1 AR_SLOT5_0
$85 KS_SLOT6_1 KS_SLOT6_0 AR_SLOT6_4 AR_SLOT6_3 AR_SLOT6_2 AR_SLOT6_1 AR_SLOT6_0
$86 KS_SLOT7_1 KS_SLOT7_0 AR_SLOT7_4 AR_SLOT7_3 AR_SLOT7_2 AR_SLOT7_1 AR_SLOT7_0
$87 KS_SLOT8_1 KS_SLOT8_0 AR_SLOT8_4 AR_SLOT8_3 AR_SLOT8_2 AR_SLOT8_1 AR_SLOT8_0
$88 KS_SLOT9_1 KS_SLOT9_0 AR_SLOT9_4 AR_SLOT9_3 AR_SLOT9_2 AR_SLOT9_1 AR_SLOT9_0
$89 KS_SLOT10_1 KS_SLOT10_0 AR_SLOT10_4 AR_SLOT10_3 AR_SLOT10_2 AR_SLOT10_1 AR_SLOT10_0
$8A KS_SLOT11_1 KS_SLOT11_0 AR_SLOT11_4 AR_SLOT11_3 AR_SLOT11_2 AR_SLOT11_1 AR_SLOT11_0
$8B KS_SLOT12_1 KS_SLOT12_0 AR_SLOT12_4 AR_SLOT12_3 AR_SLOT12_2 AR_SLOT12_1 AR_SLOT12_0
$8C KS_SLOT13_1 KS_SLOT13_0 AR_SLOT13_4 AR_SLOT13_3 AR_SLOT13_2 AR_SLOT13_1 AR_SLOT13_0
$8D KS_SLOT14_1 KS_SLOT14_0 AR_SLOT14_4 AR_SLOT14_3 AR_SLOT14_2 AR_SLOT14_1 AR_SLOT14_0
$8E KS_SLOT15_1 KS_SLOT15_0 AR_SLOT15_4 AR_SLOT15_3 AR_SLOT15_2 AR_SLOT15_1 AR_SLOT15_0
$8F KS_SLOT16_1 KS_SLOT16_0 AR_SLOT16_4 AR_SLOT16_3 AR_SLOT16_2 AR_SLOT16_1 AR_SLOT16_0
$90 KS_SLOT17_1 KS_SLOT17_0 AR_SLOT17_4 AR_SLOT17_3 AR_SLOT17_2 AR_SLOT17_1 AR_SLOT17_0
$91 KS_SLOT18_1 KS_SLOT18_0 AR_SLOT18_4 AR_SLOT18_3 AR_SLOT18_2 AR_SLOT18_1 AR_SLOT18_0
$92 KS_SLOT19_1 KS_SLOT19_0 AR_SLOT19_4 AR_SLOT19_3 AR_SLOT19_2 AR_SLOT19_1 AR_SLOT19_0
$93 KS_SLOT20_1 KS_SLOT20_0 AR_SLOT20_4 AR_SLOT20_3 AR_SLOT20_2 AR_SLOT20_1 AR_SLOT20_0
$94 KS_SLOT21_1 KS_SLOT21_0 AR_SLOT21_4 AR_SLOT21_3 AR_SLOT21_2 AR_SLOT21_1 AR_SLOT21_0
$95 KS_SLOT22_1 KS_SLOT22_0 AR_SLOT22_4 AR_SLOT22_3 AR_SLOT22_2 AR_SLOT22_1 AR_SLOT22_0
$96 KS_SLOT23_1 KS_SLOT23_0 AR_SLOT23_4 AR_SLOT23_3 AR_SLOT23_2 AR_SLOT23_1 AR_SLOT23_0
$97 KS_SLOT24_1 KS_SLOT24_0 AR_SLOT24_4 AR_SLOT24_3 AR_SLOT24_2 AR_SLOT24_1 AR_SLOT24_0
$98 KS_SLOT25_1 KS_SLOT25_0 AR_SLOT25_4 AR_SLOT25_3 AR_SLOT25_2 AR_SLOT25_1 AR_SLOT25_0
$99 KS_SLOT26_1 KS_SLOT26_0 AR_SLOT26_4 AR_SLOT26_3 AR_SLOT26_2 AR_SLOT26_1 AR_SLOT26_0
$9A KS_SLOT27_1 KS_SLOT27_0 AR_SLOT27_4 AR_SLOT27_3 AR_SLOT27_2 AR_SLOT27_1 AR_SLOT27_0
$9B KS_SLOT28_1 KS_SLOT28_0 AR_SLOT28_4 AR_SLOT28_3 AR_SLOT28_2 AR_SLOT28_1 AR_SLOT28_0
$9C KS_SLOT29_1 KS_SLOT29_0 AR_SLOT29_4 AR_SLOT29_3 AR_SLOT29_2 AR_SLOT29_1 AR_SLOT29_0
$9D KS_SLOT30_1 KS_SLOT30_0 AR_SLOT30_4 AR_SLOT30_3 AR_SLOT30_2 AR_SLOT30_1 AR_SLOT30_0
$9E KS_SLOT31_1 KS_SLOT31_0 AR_SLOT31_4 AR_SLOT31_3 AR_SLOT31_2 AR_SLOT31_1 AR_SLOT31_0
$9F KS_SLOT32_1 KS_SLOT32_0 AR_SLOT32_4 AR_SLOT32_3 AR_SLOT32_2 AR_SLOT32_1 AR_SLOT32_0
$A0 AMS_SLOT1_EN D1R_SLOT1_4 D1R_SLOT1_3 D1R_SLOT1_2 D1R_SLOT1_1 D1R_SLOT1_0
$A1 AMS_SLOT2_EN D1R_SLOT2_4 D1R_SLOT2_3 D1R_SLOT2_2 D1R_SLOT2_1 D1R_SLOT2_0
$A2 AMS_SLOT3_EN D1R_SLOT3_4 D1R_SLOT3_3 D1R_SLOT3_2 D1R_SLOT3_1 D1R_SLOT3_0
$A3 AMS_SLOT4_EN D1R_SLOT4_4 D1R_SLOT4_3 D1R_SLOT4_2 D1R_SLOT4_1 D1R_SLOT4_0
$A4 AMS_SLOT5_EN D1R_SLOT5_4 D1R_SLOT5_3 D1R_SLOT5_2 D1R_SLOT5_1 D1R_SLOT5_0
$A5 AMS_SLOT6_EN D1R_SLOT6_4 D1R_SLOT6_3 D1R_SLOT6_2 D1R_SLOT6_1 D1R_SLOT6_0
$A6 AMS_SLOT7_EN D1R_SLOT7_4 D1R_SLOT7_3 D1R_SLOT7_2 D1R_SLOT7_1 D1R_SLOT7_0
$A7 AMS_SLOT8_EN D1R_SLOT8_4 D1R_SLOT8_3 D1R_SLOT8_2 D1R_SLOT8_1 D1R_SLOT8_0
$A8 AMS_SLOT9_EN D1R_SLOT9_4 D1R_SLOT9_3 D1R_SLOT9_2 D1R_SLOT9_1 D1R_SLOT9_0
$A9 AMS_SLOT10_EN D1R_SLOT10_4 D1R_SLOT10_3 D1R_SLOT10_2 D1R_SLOT10_1 D1R_SLOT10_0
$AA AMS_SLOT11_EN D1R_SLOT11_4 D1R_SLOT11_3 D1R_SLOT11_2 D1R_SLOT11_1 D1R_SLOT11_0
$AB AMS_SLOT12_EN D1R_SLOT12_4 D1R_SLOT12_3 D1R_SLOT12_2 D1R_SLOT12_1 D1R_SLOT12_0
$AC AMS_SLOT13_EN D1R_SLOT13_4 D1R_SLOT13_3 D1R_SLOT13_2 D1R_SLOT13_1 D1R_SLOT13_0
$AD AMS_SLOT14_EN D1R_SLOT14_4 D1R_SLOT14_3 D1R_SLOT14_2 D1R_SLOT14_1 D1R_SLOT14_0
$AE AMS_SLOT15_EN D1R_SLOT15_4 D1R_SLOT15_3 D1R_SLOT15_2 D1R_SLOT15_1 D1R_SLOT15_0
$AF AMS_SLOT16_EN D1R_SLOT16_4 D1R_SLOT16_3 D1R_SLOT16_2 D1R_SLOT16_1 D1R_SLOT16_0
$B0 AMS_SLOT17_EN D1R_SLOT17_4 D1R_SLOT17_3 D1R_SLOT17_2 D1R_SLOT17_1 D1R_SLOT17_0
$B1 AMS_SLOT18_EN D1R_SLOT18_4 D1R_SLOT18_3 D1R_SLOT18_2 D1R_SLOT18_1 D1R_SLOT18_0
$B2 AMS_SLOT19_EN D1R_SLOT19_4 D1R_SLOT19_3 D1R_SLOT19_2 D1R_SLOT19_1 D1R_SLOT19_0
$B3 AMS_SLOT20_EN D1R_SLOT20_4 D1R_SLOT20_3 D1R_SLOT20_2 D1R_SLOT20_1 D1R_SLOT20_0
$B4 AMS_SLOT21_EN D1R_SLOT21_4 D1R_SLOT21_3 D1R_SLOT21_2 D1R_SLOT21_1 D1R_SLOT21_0
$B5 AMS_SLOT22_EN D1R_SLOT22_4 D1R_SLOT22_3 D1R_SLOT22_2 D1R_SLOT22_1 D1R_SLOT22_0
$B6 AMS_SLOT23_EN D1R_SLOT23_4 D1R_SLOT23_3 D1R_SLOT23_2 D1R_SLOT23_1 D1R_SLOT23_0
$B7 AMS_SLOT24_EN D1R_SLOT24_4 D1R_SLOT24_3 D1R_SLOT24_2 D1R_SLOT24_1 D1R_SLOT24_0
$B8 AMS_SLOT25_EN D1R_SLOT25_4 D1R_SLOT25_3 D1R_SLOT25_2 D1R_SLOT25_1 D1R_SLOT25_0
$B9 AMS_SLOT26_EN D1R_SLOT26_4 D1R_SLOT26_3 D1R_SLOT26_2 D1R_SLOT26_1 D1R_SLOT26_0
$BA AMS_SLOT27_EN D1R_SLOT27_4 D1R_SLOT27_3 D1R_SLOT27_2 D1R_SLOT27_1 D1R_SLOT27_0
$BB AMS_SLOT28_EN D1R_SLOT28_4 D1R_SLOT28_3 D1R_SLOT28_2 D1R_SLOT28_1 D1R_SLOT28_0
$BC AMS_SLOT29_EN D1R_SLOT29_4 D1R_SLOT29_3 D1R_SLOT29_2 D1R_SLOT29_1 D1R_SLOT29_0
$BD AMS_SLOT30_EN D1R_SLOT30_4 D1R_SLOT30_3 D1R_SLOT30_2 D1R_SLOT30_1 D1R_SLOT30_0
$BE AMS_SLOT31_EN D1R_SLOT31_4 D1R_SLOT31_3 D1R_SLOT31_2 D1R_SLOT31_1 D1R_SLOT31_0
$BF AMS_SLOT32_EN D1R_SLOT32_4 D1R_SLOT32_3 D1R_SLOT32_2 D1R_SLOT32_1 D1R_SLOT32_0
$C0 DT2_SLOT1_1 DT2_SLOT1_0 D2R_SLOT1_4 D2R_SLOT1_3 D2R_SLOT1_2 D2R_SLOT1_1 D2R_SLOT1_0
$C1 DT2_SLOT2_1 DT2_SLOT2_0 D2R_SLOT2_4 D2R_SLOT2_3 D2R_SLOT2_2 D2R_SLOT2_1 D2R_SLOT2_0
$C2 DT2_SLOT3_1 DT2_SLOT3_0 D2R_SLOT3_4 D2R_SLOT3_3 D2R_SLOT3_2 D2R_SLOT3_1 D2R_SLOT3_0
$C3 DT2_SLOT4_1 DT2_SLOT4_0 D2R_SLOT4_4 D2R_SLOT4_3 D2R_SLOT4_2 D2R_SLOT4_1 D2R_SLOT4_0
$C4 DT2_SLOT5_1 DT2_SLOT5_0 D2R_SLOT5_4 D2R_SLOT5_3 D2R_SLOT5_2 D2R_SLOT5_1 D2R_SLOT5_0
$C5 DT2_SLOT6_1 DT2_SLOT6_0 D2R_SLOT6_4 D2R_SLOT6_3 D2R_SLOT6_2 D2R_SLOT6_1 D2R_SLOT6_0
$C6 DT2_SLOT7_1 DT2_SLOT7_0 D2R_SLOT7_4 D2R_SLOT7_3 D2R_SLOT7_2 D2R_SLOT7_1 D2R_SLOT7_0
$C7 DT2_SLOT8_1 DT2_SLOT8_0 D2R_SLOT8_4 D2R_SLOT8_3 D2R_SLOT8_2 D2R_SLOT8_1 D2R_SLOT8_0
$C8 DT2_SLOT9_1 DT2_SLOT9_0 D2R_SLOT9_4 D2R_SLOT9_3 D2R_SLOT9_2 D2R_SLOT9_1 D2R_SLOT9_0
$C9 DT2_SLOT10_1 DT2_SLOT10_0 D2R_SLOT10_4 D2R_SLOT10_3 D2R_SLOT10_2 D2R_SLOT10_1 D2R_SLOT10_0
$CA DT2_SLOT11_1 DT2_SLOT11_0 D2R_SLOT11_4 D2R_SLOT11_3 D2R_SLOT11_2 D2R_SLOT11_1 D2R_SLOT11_0
$CB DT2_SLOT12_1 DT2_SLOT12_0 D2R_SLOT12_4 D2R_SLOT12_3 D2R_SLOT12_2 D2R_SLOT12_1 D2R_SLOT12_0
$CC DT2_SLOT13_1 DT2_SLOT13_0 D2R_SLOT13_4 D2R_SLOT13_3 D2R_SLOT13_2 D2R_SLOT13_1 D2R_SLOT13_0
$CD DT2_SLOT14_1 DT2_SLOT14_0 D2R_SLOT14_4 D2R_SLOT14_3 D2R_SLOT14_2 D2R_SLOT14_1 D2R_SLOT14_0
$CE DT2_SLOT15_1 DT2_SLOT15_0 D2R_SLOT15_4 D2R_SLOT15_3 D2R_SLOT15_2 D2R_SLOT15_1 D2R_SLOT15_0
$CF DT2_SLOT16_1 DT2_SLOT16_0 D2R_SLOT16_4 D2R_SLOT16_3 D2R_SLOT16_2 D2R_SLOT16_1 D2R_SLOT16_0
$D0 DT2_SLOT17_1 DT2_SLOT17_0 D2R_SLOT17_4 D2R_SLOT17_3 D2R_SLOT17_2 D2R_SLOT17_1 D2R_SLOT17_0
$D1 DT2_SLOT18_1 DT2_SLOT18_0 D2R_SLOT18_4 D2R_SLOT18_3 D2R_SLOT18_2 D2R_SLOT18_1 D2R_SLOT18_0
$D2 DT2_SLOT19_1 DT2_SLOT19_0 D2R_SLOT19_4 D2R_SLOT19_3 D2R_SLOT19_2 D2R_SLOT19_1 D2R_SLOT19_0
$D3 DT2_SLOT20_1 DT2_SLOT20_0 D2R_SLOT20_4 D2R_SLOT20_3 D2R_SLOT20_2 D2R_SLOT20_1 D2R_SLOT20_0
$D4 DT2_SLOT21_1 DT2_SLOT21_0 D2R_SLOT21_4 D2R_SLOT21_3 D2R_SLOT21_2 D2R_SLOT21_1 D2R_SLOT21_0
$D5 DT2_SLOT22_1 DT2_SLOT22_0 D2R_SLOT22_4 D2R_SLOT22_3 D2R_SLOT22_2 D2R_SLOT22_1 D2R_SLOT22_0
$D6 DT2_SLOT23_1 DT2_SLOT23_0 D2R_SLOT23_4 D2R_SLOT23_3 D2R_SLOT23_2 D2R_SLOT23_1 D2R_SLOT23_0
$D7 DT2_SLOT24_1 DT2_SLOT24_0 D2R_SLOT24_4 D2R_SLOT24_3 D2R_SLOT24_2 D2R_SLOT24_1 D2R_SLOT24_0
$D8 DT2_SLOT25_1 DT2_SLOT25_0 D2R_SLOT25_4 D2R_SLOT25_3 D2R_SLOT25_2 D2R_SLOT25_1 D2R_SLOT25_0
$D9 DT2_SLOT26_1 DT2_SLOT26_0 D2R_SLOT26_4 D2R_SLOT26_3 D2R_SLOT26_2 D2R_SLOT26_1 D2R_SLOT26_0
$DA DT2_SLOT27_1 DT2_SLOT27_0 D2R_SLOT27_4 D2R_SLOT27_3 D2R_SLOT27_2 D2R_SLOT27_1 D2R_SLOT27_0
$DB DT2_SLOT28_1 DT2_SLOT28_0 D2R_SLOT28_4 D2R_SLOT28_3 D2R_SLOT28_2 D2R_SLOT28_1 D2R_SLOT28_0
$DC DT2_SLOT29_1 DT2_SLOT29_0 D2R_SLOT29_4 D2R_SLOT29_3 D2R_SLOT29_2 D2R_SLOT29_1 D2R_SLOT29_0
$DD DT2_SLOT30_1 DT2_SLOT30_0 D2R_SLOT30_4 D2R_SLOT30_3 D2R_SLOT30_2 D2R_SLOT30_1 D2R_SLOT30_0
$DE DT2_SLOT31_1 DT2_SLOT31_0 D2R_SLOT31_4 D2R_SLOT31_3 D2R_SLOT31_2 D2R_SLOT31_1 D2R_SLOT31_0
$DF DT2_SLOT32_1 DT2_SLOT32_0 D2R_SLOT32_4 D2R_SLOT32_3 D2R_SLOT32_2 D2R_SLOT32_1 D2R_SLOT32_0
$E0 D1L_SLOT1_3 D1L_SLOT1_2 D1L_SLOT1_1 D1L_SLOT1_0 RR_SLOT1_3 RR_SLOT1_2 RR_SLOT1_1 RR_SLOT1_0
$E1 D1L_SLOT2_3 D1L_SLOT2_2 D1L_SLOT2_1 D1L_SLOT2_0 RR_SLOT2_3 RR_SLOT2_2 RR_SLOT2_1 RR_SLOT2_0
$E2 D1L_SLOT3_3 D1L_SLOT3_2 D1L_SLOT3_1 D1L_SLOT3_0 RR_SLOT3_3 RR_SLOT3_2 RR_SLOT3_1 RR_SLOT3_0
$E3 D1L_SLOT4_3 D1L_SLOT4_2 D1L_SLOT4_1 D1L_SLOT4_0 RR_SLOT4_3 RR_SLOT4_2 RR_SLOT4_1 RR_SLOT4_0
$E4 D1L_SLOT5_3 D1L_SLOT5_2 D1L_SLOT5_1 D1L_SLOT5_0 RR_SLOT5_3 RR_SLOT5_2 RR_SLOT5_1 RR_SLOT5_0
$E5 D1L_SLOT6_3 D1L_SLOT6_2 D1L_SLOT6_1 D1L_SLOT6_0 RR_SLOT6_3 RR_SLOT6_2 RR_SLOT6_1 RR_SLOT6_0
$E6 D1L_SLOT7_3 D1L_SLOT7_2 D1L_SLOT7_1 D1L_SLOT7_0 RR_SLOT7_3 RR_SLOT7_2 RR_SLOT7_1 RR_SLOT7_0
$E7 D1L_SLOT8_3 D1L_SLOT8_2 D1L_SLOT8_1 D1L_SLOT8_0 RR_SLOT8_3 RR_SLOT8_2 RR_SLOT8_1 RR_SLOT8_0
$E8 D1L_SLOT9_3 D1L_SLOT9_2 D1L_SLOT9_1 D1L_SLOT9_0 RR_SLOT9_3 RR_SLOT9_2 RR_SLOT9_1 RR_SLOT9_0
$E9 D1L_SLOT10_3 D1L_SLOT10_2 D1L_SLOT10_1 D1L_SLOT10_0 RR_SLOT10_3 RR_SLOT10_2 RR_SLOT10_1 RR_SLOT10_0
$EA D1L_SLOT11_3 D1L_SLOT11_2 D1L_SLOT11_1 D1L_SLOT11_0 RR_SLOT11_3 RR_SLOT11_2 RR_SLOT11_1 RR_SLOT11_0
$EB D1L_SLOT12_3 D1L_SLOT12_2 D1L_SLOT12_1 D1L_SLOT12_0 RR_SLOT12_3 RR_SLOT12_2 RR_SLOT12_1 RR_SLOT12_0
$EC D1L_SLOT13_3 D1L_SLOT13_2 D1L_SLOT13_1 D1L_SLOT13_0 RR_SLOT13_3 RR_SLOT13_2 RR_SLOT13_1 RR_SLOT13_0
$ED D1L_SLOT14_3 D1L_SLOT14_2 D1L_SLOT14_1 D1L_SLOT14_0 RR_SLOT14_3 RR_SLOT14_2 RR_SLOT14_1 RR_SLOT14_0
$EE D1L_SLOT15_3 D1L_SLOT15_2 D1L_SLOT15_1 D1L_SLOT15_0 RR_SLOT15_3 RR_SLOT15_2 RR_SLOT15_1 RR_SLOT15_0
$EF D1L_SLOT16_3 D1L_SLOT16_2 D1L_SLOT16_1 D1L_SLOT16_0 RR_SLOT16_3 RR_SLOT16_2 RR_SLOT16_1 RR_SLOT16_0
$F0 D1L_SLOT17_3 D1L_SLOT17_2 D1L_SLOT17_1 D1L_SLOT17_0 RR_SLOT17_3 RR_SLOT17_2 RR_SLOT17_1 RR_SLOT17_0
$F1 D1L_SLOT18_3 D1L_SLOT18_2 D1L_SLOT18_1 D1L_SLOT18_0 RR_SLOT18_3 RR_SLOT18_2 RR_SLOT18_1 RR_SLOT18_0
$F2 D1L_SLOT19_3 D1L_SLOT19_2 D1L_SLOT19_1 D1L_SLOT19_0 RR_SLOT19_3 RR_SLOT19_2 RR_SLOT19_1 RR_SLOT19_0
$F3 D1L_SLOT20_3 D1L_SLOT20_2 D1L_SLOT20_1 D1L_SLOT20_0 RR_SLOT20_3 RR_SLOT20_2 RR_SLOT20_1 RR_SLOT20_0
$F4 D1L_SLOT21_3 D1L_SLOT21_2 D1L_SLOT21_1 D1L_SLOT21_0 RR_SLOT21_3 RR_SLOT21_2 RR_SLOT21_1 RR_SLOT21_0
$F5 D1L_SLOT22_3 D1L_SLOT22_2 D1L_SLOT22_1 D1L_SLOT22_0 RR_SLOT22_3 RR_SLOT22_2 RR_SLOT22_1 RR_SLOT22_0
$F6 D1L_SLOT23_3 D1L_SLOT23_2 D1L_SLOT23_1 D1L_SLOT23_0 RR_SLOT23_3 RR_SLOT23_2 RR_SLOT23_1 RR_SLOT23_0
$F7 D1L_SLOT24_3 D1L_SLOT24_2 D1L_SLOT24_1 D1L_SLOT24_0 RR_SLOT24_3 RR_SLOT24_2 RR_SLOT24_1 RR_SLOT24_0
$F8 D1L_SLOT25_3 D1L_SLOT25_2 D1L_SLOT25_1 D1L_SLOT25_0 RR_SLOT25_3 RR_SLOT25_2 RR_SLOT25_1 RR_SLOT25_0
$F9 D1L_SLOT26_3 D1L_SLOT26_2 D1L_SLOT26_1 D1L_SLOT26_0 RR_SLOT26_3 RR_SLOT26_2 RR_SLOT26_1 RR_SLOT26_0
$FA D1L_SLOT27_3 D1L_SLOT27_2 D1L_SLOT27_1 D1L_SLOT27_0 RR_SLOT27_3 RR_SLOT27_2 RR_SLOT27_1 RR_SLOT27_0
$FB D1L_SLOT28_3 D1L_SLOT28_2 D1L_SLOT28_1 D1L_SLOT28_0 RR_SLOT28_3 RR_SLOT28_2 RR_SLOT28_1 RR_SLOT28_0
$FC D1L_SLOT29_3 D1L_SLOT29_2 D1L_SLOT29_1 D1L_SLOT29_0 RR_SLOT29_3 RR_SLOT29_2 RR_SLOT29_1 RR_SLOT29_0
$FD D1L_SLOT30_3 D1L_SLOT30_2 D1L_SLOT30_1 D1L_SLOT30_0 RR_SLOT30_3 RR_SLOT30_2 RR_SLOT30_1 RR_SLOT30_0
$FE D1L_SLOT31_3 D1L_SLOT31_2 D1L_SLOT31_1 D1L_SLOT31_0 RR_SLOT31_3 RR_SLOT31_2 RR_SLOT31_1 RR_SLOT31_0
$FF D1L_SLOT32_3 D1L_SLOT32_2 D1L_SLOT32_1 D1L_SLOT32_0 RR_SLOT32_3 RR_SLOT32_2 RR_SLOT32_1 RR_SLOT32_0








・YM2151のレジスタ(読み取り専用)

YM2151は内部に1バイトの読み取り専用レジスタを持っています。
セットしたアドレスによらず、YM2151のステータス情報を返します。
(テストモードの機能で内部レジスタのデータを読み取ることもできるようです。)

bit7
bit6
bit5
bit4
bit3
bit2
bit1
bit0
B-----IST_BIST_B


B:
WRITE BUSY FLAG
ライトビジーフラグです。
このフラグがHの時にYM2151のレジスタへ値を書き込むことは禁止されています。

B -> H : データ書き込み中のため、YM2151へのライトアクセスは禁止
B -> L : YM2151へのライトアクセス可能


IST:
タイマーオーバーフローステータスフラグです。
このフラグがHの時、YM2151の内部タイマーのオーバーフローフラグがHになったことを示します。

IST_A -> H : タイマーAがオーバーフローしています。
IST_A -> L : タイマーAがオーバーフローしていません。
IST_B -> H : タイマーBがオーバーフローしています。
IST_B -> L : タイマーBがオーバーフローしていません。


スロット、チャンネル、モジュレータ・キャリアの関係

チャンネルとモジュレータ・キャリアからスロット番号を参照するための表です。
CH No. M1(OP1) M2(OP3) C1(OP2) C2(OP4)
CH1 SLOT1 SLOT9 SLOT17 SLOT25
CH2 SLOT2 SLOT10 SLOT18 SLOT26
CH3 SLOT3 SLOT11 SLOT19 SLOT27
CH4 SLOT4 SLOT12 SLOT20 SLOT28
CH5 SLOT5 SLOT13 SLOT21 SLOT29
CH6 SLOT6 SLOT14 SLOT22 SLOT30
CH7 SLOT7 SLOT15 SLOT23 SLOT31
CH8 SLOT8 SLOT16 SLOT24 SLOT32


・タイミング図

YM2151への書き込みタイミング




 

アドレス・データの書き込みタイミングです。
アドレスを書き込む場合は4番ピンの「A0」をLにします。
データを書き込む場合は「A0」をHにします。

YM2151のステータスレジスタの読み取りタイミング




読み取りの場合、YM2151は常にステータスレジスタの内容を返すため、アドレスの書き込みは不要です。


YM2151のレジスタへ値をセットする




YM2151は、アドレスバスとデータバスを共用しています。
YM2151へアドレス値を書き込んでからデータ値を書き込むことで、任意の場所の内部レジスタへデータをセットできます。

レジスタへデータを際には、原則事前にYM2151の「WRITE BUSY FLAG」(ステータスレジスタの第7ビット)がLとなっていることをチェックする必要があります。

1度YM2151のレジスタへデータを書き込むと、68入力クロック分の待機時間が必要です。
ピン数の節約などの理由でステータスレジスタを読まない場合、十分なウェイト時間が必要です。

-- YM2151のレジスタへのデータセット手順 (ステータスレジスタをチェックする場合) --

①A0ピンをHにする
②~RDピンをLにする
③~CSピンをLにする
④D7ピンがHの場合(BUSY中)、~CS, ~RDピンをHにして一定時間待機後②へ
⑤~RDピンをHにする
⑥A0ピンをLにする
⑦書き込み先アドレスをD0~D7へセットする
⑧~WEピンをLにする
⑨~WEピンをHにする
⑩A0ピンをHにする
⑪書き込むデータをD0~D7へセットする
⑫~WEピンをLにする
⑬~WEピンをHにする
⑭~CSピンをHにする


-- YM2151のレジスタへのデータセット手順 (ステータスレジスタをチェックしない場合) --

①YM2151の入力クロック68サイクル分以上待機する(余裕をもって多く待機することをおすすめします。)
②~CSピンをLにする
③A0ピンをLにする
④書き込み先アドレスをD0~D7へセットする
⑤~WEピンをLにする
⑥~WEピンをHにする
⑦A0ピンをHにする
⑧書き込むデータをD0~D7へセットする
⑨~WEピンをLにする
⑩~WEピンをHにする
⑪~CSピンをHにする





・使い方(制御)

YM2151は、256バイト分のレジスタアドレス空間を持ち、そのほとんどが発音に必要なレジスタとなっています。

音色の設定がほとんどできないPSG音源や、音色をレジスタでほとんど制御しないPCMサンプリング音源と比較するとレジスタ数が多いため、FM音源の難しさを感じます。

YM2151はレジスタ数こそは多いものの、特殊な用途のレジスタは少なめです。


-- レジスタの種類 --

大雑把に分けると、

・タイマー系レジスタ
・トーン出力・音程設定レジスタ
・LFO関連レジスタ
・コネクション(アルゴリズム)・倍率レジスタ
・エンベロープレジスタ

があります。


このうち、FM音源特有のレジスタは、
・LFO関連レジスタ
・コネクション(アルゴリズム)・倍率レジスタ
の2種類くらいなので、実はそんなに難しくないかもしれません。


-FM音源について-

FM音源IC内のレジスタがどのような機能を持つのかを理解するには、FM音源がどのような仕組みで音色を作り出すかを知る必要があります。
ネット上には、とても分かりやすくFM音源について解説しているページが多数あるので、詳しく知りたい方は各自で調べてみてください。


FM音源について少しかみ砕いて説明すると、
FM音源の場合、「オペレータ」という1つのブロックの発振器を複数連結して1つの音色を作り上げます。

「オペレータ」どうしのつなぎ方で音色の特徴が変化します。直列(オペレータでオペレータに変調をかける)にしたり、並列(オペレータの出力値の加算)にしたりと色々な組み合わせができます。
この組み合わせを「コネクション」や「アルゴリズム」と呼びます。1つの音色にたくさんオペレータを連結すれば、より音作りの幅が広がります。

「オペレータ」を直列に接続することで、オペレータでオペレータに変調をかけることができます。わかりにくいので変調をかける方のオペレータを「モジュレータ」、変調をかけられる方のオペレータを「キャリア」と呼びます。

オペレータ(モジュレータ、キャリア)ごとに発振周波数比を変えることで、単一オペレータだけでは実現できない波形を作り出せます。
接続した複数のオペレータの発振周波数比が同じであれば、基本周波数が変わっても、出力波形は周期(周波数)が変わるだけで形は変わりません。

音色の音程を変える際、いちいちオペレータごとに周波数比が変わらないように計算してFM音源ICのオペレータ発振レジスタに値を書き込むのは、非常に億劫です。
そのため、通常のFM音源ICには、基音周波数と周波数比を指定する(倍率レジスタ)だけで自動的に各オペレータの発振周波数を設定してくれる機能があります。
この機能があれば、音程を変えたい時に基本周波数設定レジスタへピッチデータを一度書き込むだけで済みます。


ここで、オペレータについてもう少し詳しく見てみます。

原則オペレータ単体では、基本的な波形(YM2151の場合は正弦波のみ)しか出力できません。
しかし、出力の振幅の時間変化(エンベロープ)は個別に制御できます。

音の振幅の時間変化のイメージとして、
ピアノみたいな音であれば、最大音量から徐々に減衰していき、
ストリング系の音であれば、だんだん音量が上がってある一定の音量となる感じです。

音の振幅のエンベロープの場合は、アルゴリズムの最終段にあるオペレータ(キャリア)に対してエンベロープを設定してあげればよいわけです。


オペレータを直列に接続した場合、モジュレータのエンベロープ制御は、変調度合いの時間制御となります。

例えば、アルゴリズムを「モジュレータ」→「キャリア」として、モジュレータの出力レベルをキーオンから時間がたつにつれ減衰するようなエンベロープに設定するとします。
すると、キャリアの出力はキーオンから時間がたつにつれて非正弦波から正弦波へ近づきます。
FM音源は、少ないパラメータでこのような著しい波形の時間変化を可能とします。




ここまでで、FM音源がどのように波形を生成するかをイメージできれば良いでしょう。

YM2151をはじめとするFM音源ICのオペレータは、さらにLFOや自己フィードバックが可能です。

LFO

LFO(ロー・フリークエンシー・オシレータ : 低周波発振器)は、その名の通り低い周波数で発振する発振器です。

発振器といえば先ほど説明したオペレータを思い浮かべると思いますが、
LFOの場合は「すべてのオペレータ(スロット)へ位相変調・振幅変調ができる」という特徴があります。(少なくともYM2151の場合です。LFOが複数あるFM音源ICの場合は違います)
LFOの出力を直接音声として出力できません。

LFOの役割はモジュレータと大差がないようにも思えますが、LFOの場合は非常に低い周波数(数mHz ~ 数十Hz程度)の発振しかできません。

逆にモジュレータは、基本周波数の1/2の周波数程度までしか低い周波数で発振できません。

LFOは音色となる波形の生成というよりは、ビブラートやピッチベンドなどの演奏上のエフェクトを付けるための機能といえるでしょう。


モジュレータの自己フィードバック

モジュレータの中には自己フィードバックをかけて自分自身を変調できるものもあります。
(YM2151の場合は、4つあるオペレータのうち1つ)

これにより、原則オペレータ単体(1つのモジュレータ)では基本的な波形しか生成できなかったのが、
1つのモジュレータで、複雑な波形を生成できるようになりました。

フィードバックレベルを調整することで、基本波を歪ませたり発散させてノイズを出したりできます。


以上がFM音源の概要です。
音作りは、最低限「アルゴリズム」、「オペレータの発振倍率」、「エンベロープ」の設定さえできれば可能です。
加えて、「LFO」、「自己フィードバックレベル」を設定すると楽器としての音作りの幅が広がります。

ちなみにですが、オペレータ番号はチャンネルごとに割り当てられたオペレータの順番を表します。
チャンネル関係になくICの中にあるすべてのオペレータの通し番号は「スロット番号」と呼びます。

一概にこの通りではないですが、

   スロット数 = オペレータ数 * チャンネル数

の関係が成り立ちます。



・タイマー系レジスタ

・CLKA1($10 : bit0~7)   タイマーAのオーバーフロー周期上位8bit

・CLKA2($11 : bit0~1)   タイマーAのオーバーフロー周期下位2bit

・CLKB($12 : bit0~7)   タイマーBのオーバーフロー周期

・TIM_CONF($14) 
    CSM($14 : bit7)   タイマーAによるオートキーオンの有無
    F_RESET_B($14 : bit5)   タイマーBのオーバーフローフラグクリア
    F_RESET_A($14 : bit4)   タイマーAのオーバーフローフラグクリア
    IRQ_EN_B($14 : bit3)   タイマーBのオーバーフローフラグ立ち有効無効
    IRQ_EN_A($14 : bit2)   タイマーAのオーバーフローフラグ立ち有効無効
    LOAD_B($14 : bit1)      タイマーBの動作の有無
    LOAD_A($14 : bit0)      タイマーAの動作の有無


YM2151は、2つのタイマーを内蔵しています。
それぞれのタイマーを設定することで、一定時間ごとに割り込み要求信号を~IRQピン(2番ピン)から出力します。

YM2151をマイコンを用いて発音する場合、必ずしも使う必要がない機能です。

・トーン出力・音程設定系レジスタ

・KON($08)
    SN_0 ~ SN_1($08 : bit3~6)      スロット1~4のキーオン・キーオフ(チャンネルはCH($08 : bit0~2)で指定)

・LR_FB_CON($2X)    (X = 0~7 : チャンネル1~8)
    RL_R($2X : bit7)      チャンネル1~8のR出力の有無
    RL_L($2X : bit6)      チャンネル1~8のL出力の有無

・KC($2X)    (X = 8~F : チャンネル1~8)
    OCT($2X : bit4~6)      チャンネル1~8の音程(オクターブ)設定
    NOTE($2X : bit0~3)      チャンネル1~8の音程(ノート)設定

・KF($3X)    (X = 0~7 : チャンネル1~8)
    KF($3X : bit2~7)      チャンネル1~8の音程(キーフラクション)設定


キーオン・キーオフは、スロットごとにセットできます。
YM2151の場合、チャンネル音量レジスタがありません。4つのオペレータのうち、キャリア出力部分のみ(アルゴリズムによって操作するTLレジスタの数が異なります)のスロット出力トータルレベルレジスタ(TL : $60~$7F)を操作してください。
YM2151は、ステレオ出力に対応していますがパンポットの設定はできません。パン制御を行いたい場合、2チャンネル分を左右に割り当て使用する必要があります。

・LFO関連のレジスタ

・TEST($01)
    LFO_RESET($01 : bit1)    LFOリセット

・LFRQ($18 : bit0~7)    LFO周波数設定

・PMD_AMD($19)
    LFO_MD_F($19 : bit7)    位相変調 or 振幅変調パラメータセット先指定ビット
    LFO_MD($19 : bit0~6)    位相変調 or 振幅変調パラメータ (LFO_MD_F($19 : bit7)でセット先指定)

・CT_W($1B)
    LFO_W($1B : bit0~1)    LFO波形設定ビット

・PMS_AMS($3X)    (X = 8~F : チャンネル1~8)
    PMS($3X : bit4~6)      LFO位相変調感度設定
    AMS($3X : bit0~1)      LFO振幅変調感度設定

・AMS_EN_D1R($A0+X)    (X = 0~1F : スロット1~32)
    AMS_EN($A0+X : bit7)    LFO振幅変調の有無

・コネクション(アルゴリズム)・倍率関係のレジスタ

・NOISE($0F)
    NE($0F : bit7)    スロット32をノイズジェネレータとするか?
    NFRQ($0F : bit0~4)    ノイズ周波数設定

・LR_FB_CON($2X)    (X = 0~7 : チャンネル1~8)
    FL($2X : bit3~5)             M1(OP1)のフィードバックレベル設定
    CONECT($2X : bit0~2)    アルゴリズム(コネクション)の設定

・DT1_MUL($40+X)    (X = 0~1F : スロット1~32)
    DT1($40+X : bit4~6)    スロット周波数倍率微調整
    MUL($40+X : bit0~3)    スロット周波数倍率設定

・TL($60+X)    (X = 0~1F : スロット1~32)   
    TL($60+X : bit0~6)    スロット出力レベル設定

・DT2_D2R($C0+X)    (X = 0~1F : スロット1~32)
    DT2($C0+X : bit6~7)    スロット周波数倍率粗調整


キャリアのスロット出力トータルレベルレジスタ(TL : $60~$7F)は、チャンネル音量となります。モジュレータのスロット出力トータルレベルレジスタは、変調度となります。


・エンベロープレジスタ

・KS_AR($80+X)    (X = 0~1F : スロット1~32)
    KS($80+X : bit6~7)    ピッチによるエンベロープ補正設定ビット
    AR($80+X : bit0~3)    アタックレート設定ビット

・AMS_EN_D1R($A0+X)    (X = 0~1F : スロット1~32)
    D1R($A0+X : bit0~4)    ファーストディケイレート設定ビット

・DT2_D2R($C0+X)    (X = 0~1F : スロット1~32)
    D2R($C0+X : bit0~4)    セカンドディケイレート設定ビット

・D1L_RR($E0+X)    (X = 0~1F : スロット1~32)
    D1L($E0+X : bit4~7)    ファーストディケイレベル設定ビット
    RR($E0+X : bit0~3)    リリースレート設定ビット


・使い方(制御)


--YM2151のリセット直後からの基本的な発音手順--
①音色を決定するパラメータ群をYM2151へセットする。
②音程設定レジスタに音程をセットする。
③キーオン・キーオフレジスタへキーオンしたいオペレータを選択してセットする。

ここでは、3段階のステップに分けていますが、①の音色パラメータ群のセットでは数十バイト分のパラメータがあります。音色を変更しない場合、YM2151の起動後に一度だけ書き込めばよいです。

①音色を決定するパラメータ群をYM2151へセットする。

FM音源は波形生成の原理上、多数のパラメータが必要です。
このパラメータの変化でどのような音が出るか予測することが難しく、FM音源ICへ手を出しにくくする要因となっています。

パラメータについては後述します。

②音程設定レジスタに音程をセットする。

音程設定レジスタは2種類あります。

・KC($2X)    (X = 8~F : チャンネル1~8)
    OCT($2X : bit4~6)      チャンネル1~8の音程(オクターブ)設定
    NOTE($2X : bit0~3)      チャンネル1~8の音程(ノート)設定

・KF($3X)    (X = 0~7 : チャンネル1~8)
    KF($3X : bit2~7)      チャンネル1~8の音程(キーフラクション)設定

KCは12階音とオクターブを決定し、KFは音程の微調整ができます。

音程NOTE
C#0 (0x0)
D1 (0x1)
D#2 (0x2)
E4 (0x4)
F5 (0x5)
F#6 (0x6)
G8 (0x8)
G#9 (0x9)
A10 (0x10)
A#12 (0x12)
B13 (0x13)
C14 (0x14)

KCの音階設定ビットの数値と音階の関係は、連続的ではありませんので注意してください。計算によって求めても良いですが、オクターブとノートのテーブルを用意すると楽に音程をYM2151へセットできます。


音程の微調整は、約1.6(100/64)セントずつ設定できます。
クロック入力周波数が3.58MHzでない時の音程の調整や、ピッチベンドなどに利用します。


③キーオン・キーオフレジスタへキーオンしたいオペレータを選択してセットする。

キーオン・キーオフレジスタにキーオンしたいオペレータのON/OFFフラグを操作することで、キーオン・キーオフすることができます。

・KON($08)
    SN_0 ~ SN_1($08 : bit3~6)      スロット1~4のキーオン・キーオフ(チャンネルはCH($08 : bit0~2)で指定)

キーオンすると、エンベロープ関連レジスタにセットされたパラメータに基づいて、各オペレータの出力振幅が時間的に変化を開始します。

キーオフするまでは、エンベロープ制御が持続するので、もう一度エンベロープ波形の先頭から振幅制御をしたい場合は、KONレジスタのオペレータON/OFFフラグをLにする必要があります。

----YM2151のパラメータについて----

FM音源は波形生成の原理上、多数のパラメータが必要です。
このパラメータの変化でどのような音が出るか予測することが難しく、FM音源ICへ手を出しにくくする要因となっています。

地道にパラメータを変更していき音色を探るのも面白いのですが、多くの時間が必要です。
幸いにもYM2151の場合、VOPMexというYM2151を忠実に再現したシミュレータが開発されており、プリセットパラメータも用意されています。


PPSE部  Softwareのページ  VOPMex

VOPMexは、もともとはSam氏が開発したVSTプラグインのVOPMをosoumen氏が改良・修正したVSTプラグインのようです。

VOPMexには、阿保 剛 氏が作成したYM2151のプリセットパラメータが内蔵されているので、このVSTプラグインの導入後すぐにPC上で発音できます。

VAL-SOUND


VOPMexは、VSTプラグインなので単体では起動できません。
VST対応のDAWを導入しても良いですが、今回は音色のシミュレートとパラメータの取得さえできればよいので、「VSTHost」を使います。

VSTHost

VSTHostを起動してVOPMexを読み込むことで利用できます。





実物のYM2151で演奏するにはパラメータが必要なので、VOPMex内の「Export」を押して書き出します。



VOPMの書き出しパラメータは次のような構造をしています。
1行目
LFRQ, AMD, PMD, WF(LFO波形), NFRQ

2行目
PAN(bit7,bit6)(※), FL(フィードバックレベル), CON(アルゴリズム), AMS, PMS, SLOT(bit6~bit3)(KON($08)のSNビットに相当), NE(bit7)

3行目
M1-AR, M1-D1R, M1-D2R, M1-RR, M1-D1L, M1-TL, M1-KS, M1-MUL, M1-DT1, M1-DT2, M1-AMS-EN

4行目
C1-AR, C1-D1R, C1-D2R, C1-RR, C1-D1L, C1-TL, C1-KS, C1-MUL, C1-DT1, C1-DT2, C1-AMS-EN

5行目
M2-AR, M2-D1R, M2-D2R, M2-RR, M2-D1L, M2-TL, M2-KS, M2-MUL, M2-DT1, M2-DT2, M2-AMS-EN

6行目
C2-AR, C2-D1R, C2-D2R, C2-RR, C2-D1L, C2-TL, C2-KS, C2-MUL, C2-DT1, C2-DT2, C2-AMS-EN

※VOPMのパラメータ「PAN」はデフォルトでモノラル出力(64 = 0x40)となっているので、YM2151実機でステレオ出力にする場合は192 = 0xC0にする必要があります。

このパラメータをそのままYM2151へ与えると、シミュレートした時と同じような音色を出すことができます。
ただし、YM2151のレジスタにパラメータを与える場合、1バイト分のレジスタに複数のパラメータが入る場合がほとんどのため、事前にデータの整理をするかプログラム上でビット演算などする必要があります。




・使い方(回路)


YM2151の消費電力は最大630mWほどあり、通電中に手で触るとそれなりに熱さを感じます。
複数のYM2151を使った音源装置を製作する場合、電源容量に注意してください。
(YM2151を4つ使用した場合、USBからの給電は難しくなります。)


YM2151のクロック源として基本的に3.58MHz(3.579545MHz)のクリスタルオシレータを使用します。
4MHzのクリスタルオシレータを用いると、音程がずれるためKFレジスタなどで補正が必要になります。



・DAC周り

YM2151は単体での音声出力はできず、基本的には専用の外付けDACが必要です。

YM2151で使用可能な外付けDAC
・YM3012 (ステレオDAC)
・YM3014 (モノラルDAC)

これらのDACもYM2151と同様に入手方法が限られるため、偽物に注意してください。



・YM2151とYM3012の接続例


YM3012を使う場合、YM2151から出力されるΦ1, SO, SH1, SH2の4種類の信号をYM3012のCLOCKΦ1, SD, SAM2, SAM1へ入力します。

Φ1はシリアルデータのクロック信号出力、SOはシリアルデータ出力、SH1はCH1ホールド兼CH2シリアルデータラッチ、SH2はCH2ホールド兼CH1シリアルデータラッチとなります。

基本的には
SO -> SD
Φ1 -> CLOCKΦ1
SH1 -> SAM1
SH2 -> SAM2
という感じで接続します。

出力するチャンネルのLとRを入れ替えたい場合、
SH1 -> SAM2
SH2 -> SAM1
とします。

YM2151のDACとしてYM3012を使う場合は、これらの信号を深く理解しなくても良いです。




上記の回路は、MSX拡張シンセサイザユニット「YAMAHA SFG-01」のDAC~LPF周りの回路を元に作成しました。


YM3012は、外付けオペアンプがいくつか必要になります。
上記回路例では、基準電圧生成用に1回路、DACバッファ用に1回路、サンプルホールド用に2回路、LPF用に2回路分のオペアンプを使っています。

基準電圧生成用やDACバッファ用およびサンプルホールド用のオペアンプは、スルーレートが4V/μs以上のものを使用します。

※参考  NJM4558族の標準スルーレート
NJM4558    SR:1V/μs  ×
NJM4559    SR:2V/μs  ×
NJM4556A  SR:3V/μs  △
NJM4560    SR:4V/μs  ○

YAMAHA SFG-01の場合NJM4556S、YAMAHA TX81Zの場合NJM072Dを使用しているようです。


YM3012のデータシートにはサンプルホールド用コンデンサは560pF ~ 3300pF、
YM3012の前の抵抗は100Ω ~ 1KΩあたりを使うと良いとあります。
これらの最適な値はVDDで決まるとありますが、具体的な決め方は書かれていません。
オペアンプのスルーレートやサンプリング周波数でも変わってくると思うので、
ここでは詳しい値の決め方は省きます。

NJM4560を使用した時、1500pと270Ωを推奨とあるので、この条件で設計するのが無難でしょう。

(YAMAHA SFG-01の場合0.01μFと大きい値となっているが、NJM4560ではなくNJM4556を使用しているのが関係している?)


・LPFのカットオフ周波数

YM2151のサンプリングレートfs[Hz]は、

   fs = Φ1 / (チャンネル数 * 1フレームのシリアルデータ数)
      = (ΦM / 2) / (2 *16)
      = ΦM / 64   [Hz]

YM2151に入力するクロックΦMの周波数が3.58MHzの場合、サンプリング周波数は、

   fs = ΦM / 64
      = 3.58M / 64
      =55.9KHz

となります。

よって、クロックΦMの周波数が3.58MHzの時には、約28KHz(55.9/2 KHz)以上の周波数成分をカットする必要があります。
理想LPFであれば28KHz以上でカットできますが、基本的にアナログLPFのカット特性は理想LPFのカット特性からかけ離れているので、余裕をもってカットオフ周波数(フィルタ通過後に源信号が-3dBになる周波数)を15~20KHz付近にもつアナログLPF用意してあげます。

YAMAHA SFG-01は19KHz付近、TX81Z(YM2414を使用しているがサンプリング周波数は55.9KHz)は16KHz付近にカットオフ周波数をもつフィルタを用意しています。


・YM2151と市販のDACとの接続例


YM3012などの専用DACがどうしても入手できなかったり、複数のYM2151の出力を1つのDACにまとめたい場合、市販のDACを用いる手段もあります。

そこで今回はYM2151の出力フォーマットと市販のDACとの接続例を紹介します。


YM2151をはじめとするYAMAHAのFM音源ICは、DACを内蔵しているものと外部に専用のDACを接続しなければいけない物の2種類あります。

厄介なことに外付けDACを必要とするFM音源は独自フォーマットで音声データ出力しているため、市販のDACは使用できません。

市販のDACを接続して音を出すようにするには、音声データを変換する必要があります。



・YM2151の音声データ出力フォーマット

YM2151は1フレーム(片チャンネル分1サンプル 、以降フレームはこの事を指す)分の16bitデータを任意の回数シフト(出力値の倍率だと1,1/2 , 1/4,1/8 …)し10bitのデータとして送り出します。
この10bitのデータのMSB(Bit9)は常に符号ビットとなります。
シフト数は0~6bitの7種類なので、3bitでシフト情報を送り出します。


例えば-512 ~ 511の数値の場合、10bit以内の2進データなのでシフトせずに出力するのでデータは無劣化となります。
逆に-32768 ~ -16385 または 16384 ~ 32767の数値の場合、16bitの数値データとなるので6bit分シフトして10bitに丸め出力します。

ほとんどの市販のDACは1フレーム16bitのデータを受信してアナログ値を出力するので、YM2151が出力した値10bit+シフト3bitのデータを16bitへ変換する必要があります。

シフト回路が必要になるので、回路規模が少し増えます。
CPLDやFPGAを使うのが良いでしょう。



・データのシフト
YM2151は16bitのデータを10bitに丸めるため、任意の数だけシフトします。数値的にはx2^(-N)となります。
YM2151が送信する実際のシフトデータ3bitはどのようになっているかというと、1~7の値を取ります。0は送信しません。

アナログ値Vout[V]と入力データは次のような関係があります。

   Vout = ((1/2) * Vmax) + ((1/4) * Vmax * (-1 + D9 + D8*2^(-1) + D7*2^(-2) + … + D0*2^(-9) + 2^(-10)) * 2^(-N))   [V]

   N = ~S2*4 + ~S1*2 + ~S0      : 「~」はビット反転記号
      = 7 - (S2*4 + S1*2 + S0)

D0 ~ D9 : 入力データ10bit(D9は符号ビットでD9=1で正、D9=0で負、)
S0 ~ S2 : シフト数データ3bit  (S=1~7)



・変換回路


ロジックICを使用したYM2151 -> MSBファースト2'コンプリメント16bitDAC用変換回路を作ってみました。
μPD6376は過去に秋月電子で販売されていたステレオDACです。
スーパーファミコンにも使用されているので、部品取りなどして入手すると良さそうです。

あまり深く考えずに作ったので正常に動作するかはわかりません。(LRCKがBCKの立ち下がりに同期していない等の問題があります。)
変換イメージの参考程度にとらえてください。




変換する上でポイントとなるのが、符号ビット、LRCKの生成、シフト回路の3つです。


・符号ビットの扱い
YM2151が出力するデータの符号はD9(MSB)=1で正値、D9(MSB)=0で負値となっています。
市販のDACは2の補数値を入力しなければいけないため、D15(MSB)=0で正値、D15(MSB)=1で負値となるように変換します。
幸いにもビット反転をすれば良さそうなのがすぐ分かります。


・LRCKの生成
YM2151が出力した10bit+3bitのデータを1フレーム分のサンプルとしてラッチするために、SH1とSH2の2つの信号がYM2151から出力されています。

DAC側はSH1の立ち下がりでCH2のフレームデータを内部へラッチします。同様にSH2の立ち下がりでCH1のフレームデータを内部へラッチします。

専用DACのYM3012はさらにSH1がHの間CH1のサンプルホールドコンデンサへアナログ値を出力し、SH2がHの間CH2のサンプルホールドコンデンサへアナログ値を出力します。
市販のDACの場合、サンプルホールド回路が必要なものと不要なものがあり、今回はサンプルホールド回路が不要のものを想定したため、SH1, SH2の立ち下がりタイミングのみ考えます。

市販のDAC(MSBファースト2'コンプリメントDAC)は、BCK, DATA, LRCKの3線で受信してアナログ値へ変換することが多いです。(さらにフレームごとにラッチするためのクロックWCLKや、サンプリング周波数の192倍、256倍、384倍、…を必要とするDACもあります。)

BCKはDAC内のシフトレジスタのシフトクロック、DATAはシリアルデータ、LRCKは出力チャンネル指定兼フレームラッチ信号です。

LRCKがHへ立ち上がるとCH1へのアナログ出力、Lへ立ち上がるとCH2へのアナログ出力というような感じで制御するための信号です。

変換回路例では、YM2151から出力されたSH1とSH2をLRCKへ変換するためにRS-FFとD-FFを使っています。


・シフト回路
10bitのデータを任意のビット数シフトするシフト回路を用意します。

パラレルでシフト回路を作成するとそれなりに回路規模が大きくなってしまうので、今回はDACへデータ送信する時にシリアルでシフト演算をしています。

YM2151が出力するシリアルデータは、ダミー3bit+数値10bit+シフト数3bit = 16bitで1フレーム分となっています。

Φ1をDACのシリアルデータのクロック源として使う場合、シリアルシフトする際にパラレル入力とパラレル出力ができるシフトレジスタを用意しなければいけません。
よって、Φ1の2倍のクロック周波数のΦMを使うことにします。

ΦMが32クロック分でDACへ1フレームを送信します。
上記回路例では、シフトレジスタへのパラレルデータのラッチに1クロック(ダミー1クロック)、16bitのシフトに16クロック、ダミーを15クロックの構成となっています。



出力後のフィルタの設計はYM3012を使ったときのものと同様で良いでしょう。



・YM2151とCPLD+汎用DACとの接続例

最後にCPLDを使ってYM2151 -> MSBファースト2'コンプリメント16bitDAC用変換回路を紹介します。
今回はXILINXのXC9500シリーズを使った例を挙げておきます。
開発ソフトの「XILINX ISE」は現時点(2022/06/23)でまだダウンロード可能です。また、パラレルポートがあるPCであれば、JTAGライタを自作してCPLDへ書き込みできます。

・VHDL


--YM2151 -> MSBファースト2'コンプリメント16bitDAC用変換
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity main is
    Port (
            BCK : out  STD_LOGIC;--DACへ
            OUT_DATA : out  STD_LOGIC;--DACへ
            LRCK : out  STD_LOGIC;--DACへ

            P1 : in  STD_LOGIC;--YM2151から入力
            IN_DATA : in  STD_LOGIC;--YM2151から入力
            SH1 : in  STD_LOGIC;--YM2151から入力
            SH2 : in  STD_LOGIC--YM2151から入力
    );
end main;

architecture Behavioral of main is

signal IN_SHIFT_REG : STD_LOGIC_VECTOR(12 downto 0);--入力シフトレジスタ13bit
--bit12~10はシフト数、bit9~0はデータ

signal OUT_SHIFT_REG : STD_LOGIC_VECTOR(14 downto 0);--出力シフトレジスタ13bit

signal IN_WCLK : STD_LOGIC;--入力ワードクロック
signal OUT_DATA_TEMP : STD_LOGIC_VECTOR(15 downto 0);--16bitへ変換後一時保存

signal LRCK_TEMP : STD_LOGIC;--LRCK状態一時保存
signal LRCK_SHIFT_OUT : STD_LOGIC;--出力シフトレジスタへデータをラッチする用(LRCKと同値)

begin

    --入力シフトレジスタ
    process (P1) begin
        if (P1'event and P1= '1') then
            IN_SHIFT_REG(12) <= IN_DATA;
            IN_SHIFT_REG(11 downto 0) <= IN_SHIFT_REG(12 downto 1);
        end if;
    end process;

    --ワードクロック生成
    IN_WCLK <= SH1 nor SH2;

    --13bitから16bitへ変換
    process (IN_WCLK) begin
        if (IN_WCLK'event and IN_WCLK= '1') then
            case IN_SHIFT_REG(12 downto 10) is
                when "001" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0));
                when "010" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & '0');
                when "011" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & "00");
                when "100" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & "000");
                when "101" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & "0000");
                when "110" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & (not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & "00000");
                when "111" => OUT_DATA_TEMP <= ((not IN_SHIFT_REG(9)) & IN_SHIFT_REG(8 downto 0) & "000000");
                when others => OUT_DATA_TEMP <= "----------------";
            end case;
        end if;
    end process;

    --LRCK生成(非同期)
    process (SH1,SH2) begin
        if (SH1= '1') then
            LRCK_TEMP <= '1';
        elsif (SH2= '1') then
            LRCK_TEMP <= '0';
        else
            LRCK_TEMP <= LRCK_TEMP;
        end if;
    end process;

    --出力シフトレジスタ(MSBから出力)
    process (P1, LRCK_TEMP) begin
        if (P1'event and P1= '1') then
            --LRCKの出力
            LRCK <= LRCK_TEMP;--LRCKは出力シフト用のクロック(BCK= P1の反転)の立下りに同・・            LRCK_SHIFT_OUT <= LRCK_TEMP;
            LRCK_SHIFT_OUT <= LRCK_TEMP;
            --出力シフトレジスタへデータをセット
            if (LRCK_SHIFT_OUT = LRCK_TEMP) then
                OUT_DATA <= OUT_SHIFT_REG(14);
                OUT_SHIFT_REG(14 downto 1) <= OUT_SHIFT_REG(13 downto 0);
                --OUT_SHIFT_REG(0) <= '-';
            else
                OUT_DATA <= OUT_DATA_TEMP(15);
                OUT_SHIFT_REG <= OUT_DATA_TEMP(14 downto 0);
            end if;
        end if;
    end process;

    --BCKの出力(入力クロックと逆位相)
    BCK <= not P1;

end Behavioral;



回路規模はそこそこありますが、FPGAであれば気にすることなく書き込める規模です。
CPLDの場合、マクロセル数等が少ないものは入りきらない場合があります。

上記VHDL例では、XC9500シリーズのCPLDでマクロセルが48以上のもので動作します。
(XC9536では無理なので、XC9572やXC95144などのCPLDを用意してください。)

変換回路自体の仕組みは、「YM2151とYM3012・YM3014の以外のDACとの接続例」で紹介したものとは違いますが、変換の流れは同じなのでVHDLの中身については説明しません。(大きく違うのはシフト演算の方法ぐらいです)



・XC95144xl TQ100を使用した場合の回路
「YM2151とYM3012・YM3014の以外のDACとの接続例」で紹介した回路のロジックIC部分をまるまるCPLDに置き換えているのでだいぶすっきりしました。





DAC出力後のフィルタの設計はYM3012を使ったときのものと同様で良いでしょう。








・使用例  - MIDIの受信 -


YM2151を1個使用してMIDIを受信する場合の回路とプログラム(Arduino ATmega328p用)を紹介します。






音色パラメータはエクスクルーシブメッセージで送信できます。
パラメータ構造はYAMAHA VCEDフォーマットに近いですが、いくつか異なる部分があるので、
詳細はプログラム内のコメントを参照してください。


プログラムチェンジ(登録先)→「Program Change parameter Change」→「VCED parameter Change」または「VCED Bulk Dump」→プログラムチェンジ(登録先)
の順で送信することで、プログラムナンバーとOPMパラメータが結び付けられ、該当チャンネルの音色が反映されます。
別のチャンネルにも同じ音を割り当てたい時は、プログラムチェンジを該当チャンネルで行うことで音色が反映されます。

ただし、パーカッションチャンネル(ch10)のみVCEDナンバーの上位7bitが#define DRUM_VCED、
下位7bitがノートオン時のノートナンバーで指定された音色がキーオン時に登録され、発音します。
(プログラム例ではDRUM_VCED=0x08としています。)



↑OPM系で使用できる音色パラメータ付きMIDIのサンプルです。
音色パラメータの送信&反映手順の参考として用意しておきます。

※2024_10_28:軽微な不具合を修正しました。対応するMIDIファイルも要修正(VCEDバルク入力のデータがずれていた問題を修正)
さらにYM2164 (OPP),YM2414 (OPZ)でも演奏できるようにしました。
プログラム中の変数"unsigned char test[CH>>3]"で使用する音源ICを選択してください。(LFO未使用の場合、OPMでもTEST_OPP_OPZにすることが可能)

↓以前のバージョンはこちら



// YM2151・YM2164でMIDI演奏プログラム_Ver.1.1
// ©oy
// https://oykenkyu.blogspot.com/2022/05/ym2151.html

//YM2151 (OPM), YM2164 (OPP), YM2414 (OPZ)の3種類が使用可能
//ただし、YM2414 (OPZ)はOPMにあるレジスタ以外は操作対象外。さらにエンベロープの互換性が無いので、音色によっては崩れる。
//YM2414 (OPZ)を接続する場合は19番ピンSH2を2.2KΩの抵抗でプルアップしてください。
//下部の変数"unsigned char test[CH>>3]"で使用する音源ICを選択(LFO未使用の場合、OPMでもTEST_OPP_OPZにすることが可能)

// HardwareSerial.h内の「SERIAL_RX_BUFFER_SIZE」を64から256へ変更してください。「SERIAL_TX_BUFFER_SIZE」は変更しなくてよいです。

#include "avr/io.h"
#include "avr/interrupt.h"

 #define SERIALSPEED 31250 // UARTのボーレート(MIDIのボーレートは31250bps)
//#define SERIALSPEED 38400 // UARTのボーレート(デバッグ用)
#define XTAL 16000000     // 水晶振動子の周波数
// #define XTAL 24576000 //水晶振動子の周波数(ドラムパートでもたつく場合に有効です。ATmega328pによっては動作しない場合があります。)

#define YM2151_N 1 // YM2151の個数(ATmega328の場合SRAMの関係上最大1つ  SRAMがより多いマイコンを推奨  ただし、setupとym2151_writeの改変が必要)

#define VCED_N 12      // midi VCED保存可能最大パラメータ数
#define PC_N 12        // midi プログラムチェンジ-VCED紐付け保存可能最大数
#define DRUM_VCED 0x08 //(ch10(midiチャンネル0xX9)のノートナンバーNNはVCEDナンバー (hdata=0x08, ldata=0xNN)に割り当て

#define CH 16          // YM2151最大チャンネル数(YM2151は1つで8チャンネル分発音できます。)
#define MIDI_MAX_TR CH // MIDIトラック数

#define FM_DRUM_CH 7 // 音源内のドラムチャンネル

// YM2151オペレータ名と順番(レジスタ配置順とOP順が違うので注意)
#define OP1 0
#define OP2 2
#define OP3 1
#define OP4 3
#define M1 0
#define C1 2
#define M2 1
#define C2 3

// YM2151オペレータ名(アドレス計算用)
#define OP1_A 0
#define OP2_A 16
#define OP3_A 8
#define OP4_A 24
#define M1_A 0
#define C1_A 16
#define M2_A 8
#define C2_A 24

// YM2151レジスタ
#define TEST_OPM 0x01      // YM2151テストレジスタ
#define TEST_OPP_OPZ 0x09      // YM2164テストレジスタ
#define LFO_RESET 0x02 // テストレジスタ_LFOリセット

#define VOL_2164 0x00      // YM2164ボリュームレジスタ
#define VOL_2164_REG 0x7F      // YM2164ボリュームレジスタビット

#define KON 0x08    // キーオン・キーオフレジスタ
#define SN 0x78     // キーオン・キーオフレジスタ_スロット指定
#define CH_REG 0x07 // キーオン・キーオフレジスタ_スロット指定

#define NOISE 0x0F // ノイズレジスタ
#define NE 0x80    // ノイズレジスタ_ノイズ有効
#define NFRQ 0x1F  // ノイズレジスタ_ノイズ周波数

#define CLKA1 0x10 // タイマーA周期設定レジスタ上位8bit

#define CLKA2 0x11       // タイマーA周期設定レジスタ下位2bit
#define CLKA2_CLKA2 0x03 // タイマーA周期設定レジスタ下位2bit設定

#define CLKB 0x12 // タイマーB周期設定レジスタ

#define TIM_CONF 0x14  // タイマー動作設定レジスタ
#define CSM 0x80       // タイマー動作設定レジスタ_タイマーAによるオートキーオン有効
#define F_RESET_B 0x20 // タイマー動作設定レジスタ_タイマーBオーバーフローフラグクリア
#define F_RESET_A 0x10 // タイマー動作設定レジスタ_タイマーAオーバーフローフラグクリア
#define IRQ_EN_B 0x08  // タイマー動作設定レジスタ_タイマーBによる割り込み要求出力を有効
#define IRQ_EN_A 0x04  // タイマー動作設定レジスタ_タイマーAによる割り込み要求出力を有効
#define LOAD_B 0x02    // タイマー動作設定レジスタ_タイマーBのカウント動作を有効
#define LOAD_A 0x01    // タイマー動作設定レジスタ_タイマーAのカウント動作を有効

#define LFRQ 0x18 // LFO周波数設定レジスタ

#define PMD_AMD 0x19  // 位相変調・振幅変調深度設定レジスタ
#define LFO_MD_F 0x80 // 位相変調・振幅変調深度設定レジスタ_位相変調深度レジスタへ書き込み
#define LFO_MD 0x7F   // 位相変調・振幅変調深度設定レジスタ_変調深度

#define CT_W 0x1B  // 汎用端子出力ビット・LFO変調波形設定レジスタ
#define CT2 0x80   // 汎用端子出力ビット・LFO変調波形設定レジスタ_汎用出力ポートCT2への出力値
#define CT1 0x40   // 汎用端子出力ビット・LFO変調波形設定レジスタ_汎用出力ポートCT1への出力値
#define LFO_W 0x03 // 汎用端子出力ビット・LFO変調波形設定レジスタ_LFO変調波形

#define LR_FB_CON 0x20 // チャンネル出力・フィードバック・アルゴリズム設定レジスタ
#define RL 0xC0        // チャンネル出力・フィードバック・アルゴリズム設定レジスタ_RLチャンネルの出力を有効
#define RL_R 0x80      // チャンネル出力・フィードバック・アルゴリズム設定レジスタ_Rチャンネルの出力を有効
#define RL_L 0x40      // チャンネル出力・フィードバック・アルゴリズム設定レジスタ_Lチャンネルの出力を有効
#define FL 0x38        // チャンネル出力・フィードバック・アルゴリズム設定レジスタ_フィードバックレベル
#define CONECT 0x07    // チャンネル出力・フィードバック・アルゴリズム設定レジスタ_コネクション

#define KC 0x28   // 音程設定レジスタ
#define OCT 0x70  // 音程設定レジスタ_オクターブ設定
#define NOTE 0x0F // 音程設定レジスタ_ノート設定

#define KF 0x30    // キーフラクションレジスタ
#define KF_KF 0xFC // キーフラクションレジスタ設定

#define PMS_AMS 0x38 // LFO位相変調・振幅変調感度設定レジスタ
#define PMS 0x70     // LFO位相変調設定
#define AMS 0x03     // LFO振幅変調設定

#define DT1_MUL 0x40 // スロット倍率微調整・スロット倍率設定レジスタ
#define DT1 0x70     // スロット倍率微調整・スロット倍率設定レジスタ_スロット倍率微調整設定
#define MUL 0x0F     // スロット倍率微調整・スロット倍率設定レジスタ_スロット倍率設定

#define TL 0x60    // スロット出力レベル設定レジスタ
#define TL_TL 0x7F // スロット出力レベル設定

#define KS_AR 0x80 // ピッチによるエンベロープ補正・エンベロープアタックレート設定レジスタ
#define KS 0xC0    // ピッチによるエンベロープ補正・エンベロープアタックレート設定レジスタ_ピッチによるエンベロープ補正設定
#define AR 0x1F    // ピッチによるエンベロープ補正・エンベロープアタックレート設定レジスタ_エンベロープアタックレート設定

#define AMS_EN_D1R 0xA0 // AMS有効フラグ・エンベロープファーストディケイレート設定レジスタ
#define AMS_EN 0x80     // AMS有効フラグ・エンベロープファーストディケイレート設定レジスタ_AMS有効フラグ設定
#define D1R 0x1F        // AMS有効フラグ・エンベロープファーストディケイレート設定レジスタ_エンベロープファーストディケイレート設定

#define DT2_D2R 0xC0 // スロット倍率粗調整・エンベロープセカンドディケイレート設定レジスタ
#define DT2 0xC0     // スロット倍率粗調整・エンベロープセカンドディケイレート設定レジスタ_スロット倍率粗調整設定
#define D2R 0x1F     // スロット倍率粗調整・エンベロープセカンドディケイレート設定レジスタ_エンベロープセカンドディケイレート設定

#define D1L_RR 0xE0 // エンベロープファーストディケイレベル・エンベロープセカンドディケイレート設定レジスタ
#define D1L 0xF0    // エンベロープファーストディケイレベル・エンベロープセカンドディケイレート設定レジスタ_エンベロープファーストディケイレベル設定
#define RR 0x0F     // エンベロープファーストディケイレベル・エンベロープセカンドディケイレート設定レジスタ_エンベロープセカンドディケイレート設定

//テストレジスタアドレス保存 YM2151とYM2164の混在用
//使用する音源ICをここで選択(LFO未使用の場合、OPMでもTEST_OPP_OPZにすることが可能)
unsigned char test[CH>>3] = {TEST_OPM, TEST_OPM};//OPMの場合TEST_OPM、OPPとOPZの場合TEST_OPP_OPZに設定


// YM2151レジスタ保存
union Ym2151_data_st
{
  struct
  {
    unsigned char reserve_00;
    unsigned char test;
    unsigned char reserve_02;
    unsigned char reserve_03;
    unsigned char reserve_04;
    unsigned char reserve_05;
    unsigned char reserve_06;
    unsigned char reserve_07;
    unsigned char kon;
    unsigned char reserve_09;
    unsigned char reserve_0A;
    unsigned char reserve_0B;
    unsigned char reserve_0C;
    unsigned char reserve_0D;
    unsigned char reserve_0E;
    unsigned char noise;
    unsigned char clka1;
    unsigned char clka2;
    unsigned char clkb;
    unsigned char reserve_13;
    unsigned char tim_conf;
    unsigned char reserve_15;
    unsigned char reserve_16;
    unsigned char reserve_17;
    unsigned char lfrq;
    unsigned char pmd_amd;
    unsigned char reserve_1A;
    unsigned char ct_w;
    unsigned char reserve_1C;
    unsigned char reserve_1D;
    unsigned char reserve_1E;
    unsigned char reserve_1F;
    unsigned char lr_fb_conf[8];
    unsigned char kc[8];
    unsigned char kf[8];
    unsigned char pms_ams[8];
    unsigned char dt1_mul[32];
    unsigned char tl[32];
    unsigned char ks_ar[32];
    unsigned char ams_en_d1r[32];
    unsigned char dt2_d2r[32];
    unsigned char d1l_rr[32];
  } reg;
  unsigned char data[256];
};

// YM2151_保存用
Ym2151_data_st ym2151_data[YM2151_N];

unsigned char ym2151_slot_en[CH];                                                       // スロットONOFF保存用(ノートオンで使用)
unsigned char ym2151_main_vol[CH];                                                      // 音量保存用(オペレータごとの最終出力演算に使用)
unsigned char ym2151_lfo_rst_en[CH] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // LFOをリセットするか? 0:no, 1~255:yes

// VCED保存用
unsigned char vced_ram[VCED_N][64];
// VCEDナンバー保存(Program Change parameter Changeで受信した値と内部値の変換)
unsigned int vced_tr[VCED_N]; // 0bXXNNNNNN NNNNNNNN,[N:データ、X:未使用]
unsigned char vced_tr_c = 0;  // 登録用カウンタ、最大VCED_N-1

// プログラムチェンジとVCEDナンバー保存の紐付け
unsigned int vced_nc[PC_N];
unsigned char pc_nc[PC_N];
unsigned char vced_pc_c = 0; // 登録用カウンタ、最大PC_N-1

// YM2151のノートデータへ変換
const unsigned char PROGMEM ym2151_note_conv[96] = {
    0x00, 0x01, 0x02, 0x04, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0C, 0x0D, 0x0E,
    0x10, 0x11, 0x12, 0x14, 0x15, 0x16, 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E,
    0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E,
    0x30, 0x31, 0x32, 0x34, 0x35, 0x36, 0x38, 0x39, 0x3A, 0x3C, 0x3D, 0x3E,
    0x40, 0x41, 0x42, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D, 0x4E,
    0x50, 0x51, 0x52, 0x54, 0x55, 0x56, 0x58, 0x59, 0x5A, 0x5C, 0x5D, 0x5E,
    0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x68, 0x69, 0x6A, 0x6C, 0x6D, 0x6E,
    0x70, 0x71, 0x72, 0x74, 0x75, 0x76, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E};

// YM2151のレベル変換 127*(1-log127(vol))
const unsigned char PROGMEM ym2151_tl_vol_conv[128] = {
    127, 127, 109, 98, 91, 85, 80, 76, 72, 69, 67, 64, 62, 60, 58, 56,
    54, 53, 51, 50, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37,
    36, 35, 35, 34, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 27, 26,
    26, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 19, 19, 18,
    18, 18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 13, 13, 13, 12,
    12, 12, 11, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8, 8, 8, 8,
    7, 7, 7, 7, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4,
    3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0};
// YM2151パラメータ
// パラメータ構造
// LFRQ,AMD,PMD,WF,NFRQ
//,PAN(bit7,bit6),FL,CON,AMS,PMS,SLOT(bit6~bit3),NE(bit7)
//,M1-AR,M1-D1R,M1-D2R,M1-RR,M1-D1L,M1-TL,M1-KS,M1-MUL,M1-DT1,M1-DT2,M1-AMS-EN
//,C1-AR,C1-D1R,C1-D2R,C1-RR,C1-D1L,C1-TL,C1-KS,C1-MUL,C1-DT1,C1-DT2,C1-AMS-EN
//,M2-AR,M2-D1R,M2-D2R,M2-RR,M2-D1L,M2-TL,M2-KS,M2-MUL,M2-DT1,M2-DT2,M2-AMS-EN
//,C2-AR,C2-D1R,C2-D2R,C2-RR,C2-D1L,C2-TL,C2-KS,C2-MUL,C2-DT1,C2-DT2,C2-AMS-EN
// TRPS(音程:標準は24),reserve,reserve,reserve,reserve,reserve,reserve,reserve
const unsigned char PROGMEM ym2151_parameter[][64] = {
  //0 YAMAHA HD-81 P.ORGAN
  {
    0, 0, 0, 2, 0,
    0b11000000, 0, 4, 0, 0, 120, 0,
    20, 2, 0, 4, 0, 34, 1, 8, 0, 0, 0,
    14, 2, 0, 7, 0, 31, 0, 4, 0, 0, 0,
    18, 2, 0, 4, 0, 27, 1, 0, 0, 0, 0,
    13, 2, 0, 7, 0, 8, 0, 0, 0, 0, 0,
    48, 0, 0, 0, 0, 0, 0, 0
  },
  //1 YAMAHA HD-81 PIANO
  {
    0, 0, 0, 0, 0,
    0b11000000, 0, 4, 0, 0, 120, 0,
    31, 6, 6, 7, 4, 39, 1, 3, 0, 0, 0,
    30, 3, 3, 5, 4, 16, 3, 1, 0, 0, 0,
    31, 5, 5, 6, 4, 44, 1, 1, 0, 0, 0,
    30, 3, 3, 5, 4, 13, 3, 1, 0, 0, 0,
    36, 0, 0, 0, 0, 0, 0, 0
  },
  //2 YAMAHA HD-81 W.WIND(H)
  {
    0, 0, 0, 0, 0,
    0b11000000, 7, 4, 0, 0, 64, 0,
    31, 31, 0, 3, 4, 127, 0, 4, 0, 0, 0,
    31, 16, 0, 8, 1, 127, 1, 2, 0, 0, 0,
    15, 31, 0, 3, 4, 127, 0, 4, 0, 0, 0,
    15, 16, 0, 8, 1, 6, 1, 2, 0, 0, 0,
    24, 0, 0, 0, 0, 0, 0, 0
  },
  //3 YAMAHA HD-81 W.WIND
  {
    0, 0, 0, 0, 0,
    0b11000000, 6, 4, 0, 0, 120, 0,
    29, 31, 0, 3, 4, 32, 0, 4, 0, 0, 0,
    14, 16, 0, 8, 1, 29, 1, 1, 0, 0, 0,
    30, 31, 0, 3, 4, 33, 0, 4, 0, 0, 0,
    14, 16, 0, 8, 1, 11, 1, 1, 0, 0, 0,
    36, 0, 0, 0, 0, 0, 0, 0
  },
  //4 YAMAHA HD-81 BRASS
  {
    0, 0, 0, 0, 0,
    0b11000000, 7, 5, 0, 0, 120, 0,
    13, 5, 0, 4, 0, 33, 2, 1, 0, 0, 0,
    16, 4, 0, 8, 0, 16, 0, 1, 0, 0, 0,
    16, 3, 0, 8, 0, 18, 0, 1, 0, 0, 0,
    16, 6, 0, 8, 4, 18, 1, 1, 0, 0, 0,
    36, 0, 0, 0, 0, 0, 0, 0
  },
  //5 YAMAHA HD-81 BRASS(L)
  {
    0, 0, 0, 0, 0,
    0b11000000, 7, 5, 0, 0, 120, 0,
    10, 5, 0, 4, 2, 29, 2, 0, 0, 0, 0,
    17, 4, 0, 8, 0, 16, 0, 0, 0, 0, 0,
    17, 3, 0, 8, 0, 18, 0, 0, 0, 0, 0,
    17, 6, 0, 8, 4, 18, 1, 0, 0, 0, 0,
    48, 0, 0, 0, 0, 0, 0, 0
  },
  //6 YAMAHA HD-81 STRING
  {
    0, 0, 0, 0, 0,
    0b11000000, 7, 2, 0, 0, 120, 0,
    18, 4, 0, 4, 0, 29, 1, 0, 0, 0, 0,
    17, 6, 0, 4, 0, 48, 1, 2, 0, 0, 0,
    16, 8, 0, 4, 0, 63, 1, 2, 0, 0, 0,
    11, 6, 0, 8, 0, 6, 1, 0, 0, 0, 0,
    48, 0, 0, 0, 0, 0, 0, 0
  },
  //7 YAMAHA HD-81 STRING(L)
  {
    0, 0, 0, 0, 0,
    0b11000000, 7, 2, 0, 0, 120, 0,
    18, 4, 0, 4, 0, 30, 1, 0, 0, 0, 0,
    17, 6, 0, 4, 0, 48, 1, 2, 0, 0, 0,
    16, 8, 0, 4, 0, 63, 1, 2, 0, 0, 0,
    11, 6, 0, 8, 0, 6, 1, 0, 0, 0, 0,
    48, 0, 0, 0, 0, 0, 0, 0
  }
};

// デフォルトトーン[ch0,ch1,ch2,…,]
// 増やすと半音高くなります。(出力音程 = def_tone + (midiの音程))
char def_tone[CH] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

// デフォルトキー
char def_key = 0;

// MIDIチャンネルからYM2151のチャンネルへ変換
char ym2151_tr[16] = {0, 1, 2, 3, 4, 5, 6, 8, 9, 7, 10, 11, 12, 13, 14, 15}; // YM2151が1個
// char ym2151_tr[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 15, 10, 11, 12, 13, 14, 9};//YM2151が2個
//--------------------------------------------------------------

// ドラム発音用
unsigned char drum_note_num = 0; // ドラムチャンネルセット保存用

// MIDI受信用
char midi_main_vel[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127}; // メインベロシティ(YM2151では未使用)(ノートベロシティと乗算して使用)
char midi_pan[16] = {64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64};                      // パン
char midi_vel[16] = {127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127};      // ベロシティ

unsigned char midi_buf[256];     // 受信バッファ
int ex_mess_en = 0;              // midi_read()で使用
unsigned int dat_ph = 0;         // midi_read()で使用
unsigned char read_buf_h;        // midi_read()で使用
unsigned int stop_byte = 0;      // midi_read()で使用
unsigned char read_buf = 0;      // midi_read()で使用
unsigned char running_ch = 0x90; // ランニングステータスチャンネル

unsigned char midiprog[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; // MIDIプログラムチェンジ保存
unsigned char sas_en = 0;                                                            // サスティン有効フラグ

/////////////////////////////関数

// YM2151へ書き込み
inline void ym2151_write(unsigned char cs, unsigned char adr, unsigned char data);

// 音程のセット
void ptc_set(unsigned char ch, unsigned int notenum);
// メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan);
// チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan);
// チャンネルノートオン
void note_on(unsigned char ch);
// チャンネルノートオフ
void note_off(unsigned char ch);
// 音色セット
void inst_set(unsigned char ch, unsigned char inst);

// MIDI受信関係
// 1バイトずつmidi_readを実行し、1グループのmidiメッセージを受信し終えるとmidi_comを実行
void midi_read(char read_buf);
// MIDIデータ入力処理
inline void midi_com(unsigned char *in_midi_mess);

// セットアップ
void setup()
{

  Serial.begin(SERIALSPEED * (16000000.0 / XTAL)); // シリアル通信開始(MIDI受信)

  //Serial.print("OK\r\n");
  delay(100);

  // ポート設定
  //  out_put
  DDRD = 0xFC;  // pin2~pin7_YM2151:D2~D7
  PORTD = 0x00; // pin2~pin7_YM2151:D2~D7

  DDRB = 0x3F;  // pin8~pin9_YM2151:D0~D1   pin10~pin13_YM2151:~RD, ~WE, A0, ~RST
  PORTB = 0x3C; // pin8~pin9_YM2151:D0~D1   pin10~pin13_YM2151:~RD, ~WE, A0, ~RST

  DDRC = 0x0F;  // pin14~pin17_YM2151:~CS0 ~ ~CS3
  PORTC = 0x0F; // pin14~pin17_YM2151:~CS0 ~ ~CS3

  // リセット
  PORTB &= ~0x20; // pin13_YM2151: ~RST = L
  delay(200);
  PORTB |= 0x20; // pin13_YM2151: ~RST = H
  delay(200);

  // VCED保存用初期化
  for (int i = 0; i < VCED_N; i++)
  {
    vced_tr[i] = 0; // 0bXXNNNNNN NNNNNNNN,[N:データ、X:未使用]
  }
  // プログラムチェンジとVCEDナンバー保存の紐付け初期化
  for (int i = 0; i < PC_N; i++)
  {
    vced_nc[i] = 0; // 0bXXNNNNNN NNNNNNNN,[N:データ、X:未使用]
    pc_nc[i] = i;
  }

  // テストレジスタ初期化
  for (int i = 0; i < (CH >> 3); i++)
  {
    ym2151_regset_write(i, test[i], 0xFF, 0x00);
    if(test[i] == TEST_OPP_OPZ)
    {
      //YM2164 (OPP),YM2414 (OPZ)だった場合、チャンネルボリュームを設定(TLで音量制御するため、この以降チャンネルボリュームは不変)
      for (int j = 0; j < 8; j++)
      {
        ym2151_regset_write(i, VOL_2164, VOL_2164_REG + j, 0x00);

      }
      //YM2414 (OPZ)向け
      ym2151_regset_write(i, 0x0A, 0xFF, 0x04);//opz_test2(?,?,?,?,音割れ,ステレオ可,?,サンプルホールド端子出力しない)
      ym2151_regset_write(i, 0x1C, 0xFF, 0x00);//opz_test(?,?,?,?,?,?,?,?)
      ym2151_regset_write(i, 0x1E, 0xFF, 0x00);//opz_test(?,?,?,?,?,?,?,?)
      ym2151_regset_write(i, 0x15, 0xFF, 0x01);//opz_test(?,?,?,?,?,?,不明,ステレオ可)
    }
   
  }

  // YM2151キーオフ
  for (int i = 0; i < CH; i++)
  {
    note_off(i);
  }
  // パラメータ初期化
  for (int i = 0; i < VCED_N; i++)
  {
    //intparam_set_vced(ch, inst);
    intparam_set_vced(i, 1);//内部音色パラメータをVCED保存領域にセット
  }
  for (int i = 0; i < CH; i++)
  {
    ym2151_vced_set(i, 0);//VCED反映
  }

 
   

  /*// パラメータ初期化
  for (int i = 0; i < (YM2151_N<<3); i++)
  {
    ym2151_param_set_all_test(i >> 3,i & 0x07,0);
  }*/

  // 初期チャンネル音量
  for (int i = 0; i < CH; i++)
  {
    vel_pan_set(i, 0, 64);
  }
}

void loop()
{
  while (1)
  {
    if (Serial.available() > 0)
    {
      midi_read(Serial.read()); // イベント処理
    }
  }
}

// YM2151へ書き込み
void ym2151_write(unsigned char cs, unsigned char adr, unsigned char data)
{
  unsigned char temp_data = 0x80; // ステータスレジスタの内容を一時保存
  DDRD &= 0x03;                   // pin2~pin7_YM2151:D2~D7を高Z
  DDRB &= 0xFC;                   // pin8~pin9_YM2151:D0~D1を高Z
  DDRB |= 0x3C;                   // YM2151:~RST,A0,~WE,~RDを出力に設定

  PORTB |= 0x10; // pin13_YM2151: A0 = H
  PORTB |= 0x04; // pin12_YM2151: ~RD = H
  PORTB |= 0x08; // pin11_YM2151: ~WR = H

  PORTD &= 0x80; // pin11_YM2151: D7プルアップ無効
  // チップセレクト
  PORTC = (PORTC & 0xF0) | (0x0F & (~(1 << cs))); // pin14~pin17_YM2151:~CS0 ~ ~CS3
  delayMicroseconds(8);
  // ビジーフラグがLになるまで待機
  /*for (int time_out = 0; time_out <= 10; time_out++)
  {

    if (time_out == 10)
    {
      PORTC = (PORTC & 0xF0) | (0x0F); // pin14~pin17_YM2151:~CS0 ~ ~CS3 = L
      return;
    }
    PORTB &= ~0x04; // pin12_YM2151: ~RD = L
    delayMicroseconds(8);
    temp_data = PIND;
    // delayMicroseconds(10);
    PORTB |= 0x04; // pin12_YM2151: ~RD = H
    delayMicroseconds(8);
    if ((temp_data >> 7) == 0x00)
    {
      break;
    }
  }*/
  delayMicroseconds(80);
  // アドレスのセット
  PORTB &= ~0x10; // pin13_YM2151: A0 = L
  // delayMicroseconds(5);
  DDRD |= 0xFC; // pin2~pin7_YM2151:D2~D7を出力ポートにする
  DDRB |= 0x03; // pin8~pin9_YM2151:D0~D1を出力ポートにする
  // delayMicroseconds(5);
  PORTB = (PORTB & 0xFC) | (0x03 & adr); // pin9,8_YM2151:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & adr); // pin7~2_YM2151:D7~D2

  delayMicroseconds(8);
  PORTB &= ~0x08; // pin11_YM2151: ~WR = L
  delayMicroseconds(8);
  PORTB |= 0x08; // pin11_YM2151: ~WR = H
  delayMicroseconds(8);

  // データのセット
  PORTB |= 0x10; // pin13_YM2151: A0 = H
  // delayMicroseconds(5);

  PORTB = (PORTB & 0xFC) | (0x03 & data); // pin9,8_YM2151:D1,D0
  PORTD = (PORTD & 0x03) | (0xFC & data); // pin7~2_YM2151:D7~D2

  delayMicroseconds(8);
  PORTB &= ~0x08; // pin11_YM2151: ~WR = L
  delayMicroseconds(8);
  PORTB |= 0x08; // pin11_YM2151: ~WR = H
  delayMicroseconds(8);

  PORTC = (PORTC & 0xF0) | (0x0F); // pin14~pin17_YM2151:~CS0 ~ ~CS3 = L
  delayMicroseconds(8);
}

// YM2151のレジスタビットへ値をセット
void ym2151_regset(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data)
{
  if (cs >= YM2151_N)
  {
    return;
  }

  // regはマスクビット群
  //(reg & (-reg))は、複数のHビットのうち右端のHビットのみ残す
  //(reg & (-reg)) * dataは、実質ビットシフト

  // レジスタの保存
  // ym2151_data[cs].data[adr] = (ym2151_data[cs].data[adr] & (~reg)) | (reg & ((reg & (-reg)) * data));

  ym2151_data[cs].data[adr] = (ym2151_data[cs].data[adr] & (~reg)) | (reg & ((reg & (reg ^ (reg << 1))) * data));
}

// YM2151のレジスタビットへ値をセット(ym2151への書き込みも行う)
void ym2151_regset_write(unsigned char cs, unsigned char adr, unsigned char reg, unsigned char data)
{
  if (cs >= YM2151_N)
  {
    return;
  }

  // regはマスクビット群
  //(reg & (-reg))は、複数のHビットのうち右端のHビットのみ残す
  //(reg & (-reg)) * dataは、実質ビットシフト

  // レジスタの保存
  // ym2151_data[cs].data[adr] = (ym2151_data[cs].data[adr] & (~reg)) | (reg & ((reg & (-reg)) * data));
  ym2151_data[cs].data[adr] = (ym2151_data[cs].data[adr] & (~reg)) | (reg & ((reg & (reg ^ (reg << 1))) * data));
  // ym2151へ書き込み
  ym2151_write(cs, adr, ym2151_data[cs].data[adr]);
}

// YM2151のレジスタ仮想読み込み(マイコン内のRAMから読み込み)
unsigned char ym2151_regread(unsigned char cs, unsigned char adr, unsigned char reg)
{
  return (reg & ym2151_data[cs].data[adr]) / (reg & (reg ^ (reg << 1)));
}

// ym2151パラメータセット(LFO、ノイズ含むすべて)
void ym2151_param_set_all(unsigned char cs, unsigned char ch, int inst)
{
  // 入力データ構造
  // LFRQ,AMD,PMD,WF,NFRQ
  //,PAN(bit7,bit6),FL,CON,AMS,PMS,SLOT(bit6~bit3),NE(bit7)
  //,M1-AR,M1-D1R,M1-D2R,M1-RR,M1-D1L,M1-TL,M1-KS,M1-MUL,M1-DT1,M1-DT2,M1-AMS-EN(bit7)
  //,C1-AR,C1-D1R,C1-D2R,C1-RR,C1-D1L,C1-TL,C1-KS,C1-MUL,C1-DT1,C1-DT2,C1-AMS-EN
  //,M2-AR,M2-D1R,M2-D2R,M2-RR,M2-D1L,M2-TL,M2-KS,M2-MUL,M2-DT1,M2-DT2,M2-AMS-EN
  //,C2-AR,C2-D1R,C2-D2R,C2-RR,C2-D1L,C2-TL,C2-KS,C2-MUL,C2-DT1,C2-DT2,C2-AMS-EN
  // reserve,reserve,reserve,reserve,reserve,reserve,reserve,reserve

  ym2151_regset_write(cs, LFRQ, 0xFF, pgm_read_byte_near((int)(ym2151_parameter[inst] + 0)));                            // LFRQ
  ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, 0x7F & pgm_read_byte_near((int)(ym2151_parameter[inst] + 1)));     // AMD
  ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, LFO_MD_F | pgm_read_byte_near((int)(ym2151_parameter[inst] + 2))); // PMD
  ym2151_regset_write(cs, CT_W, LFO_W, pgm_read_byte_near((int)(ym2151_parameter[inst] + 3)));                           // WF
  ym2151_regset_write(cs, NOISE, NFRQ, pgm_read_byte_near((int)(ym2151_parameter[inst] + 4)));                           // NFRQ

  ym2151_regset_write(cs, LR_FB_CON + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 5)) & RL) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)) << 3) & FL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)) & CONECT)); // PAN(bit7,bit6), FL, CON
  // ym2151_regset_write(cs, LR_FB_CON + ch, FL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)));//FL
  // ym2151_regset_write(cs, LR_FB_CON + ch, CONECT, pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)));//CON
  ym2151_regset_write(cs, PMS_AMS + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 8)) & AMS) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)) << 4) & PMS)); // AMS,PMS
  // ym2151_regset_write(cs, PMS_AMS + ch, PMS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)));//PMS
  ym2151_slot_en[(cs << 3) | ch] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 10));         // SLOT(bit6~bit3)
  ym2151_regset_write(cs, NOISE, NE, pgm_read_byte_near((int)(ym2151_parameter[inst] + 11)) >> 7); // NE(bit7)

  // M1
  ym2151_regset_write(cs, KS_AR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 12)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)) << 6));           // M1-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 13)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)) & AMS_EN)); // M1-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 14)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)) << 6));        // M1-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 15)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)) << 4));          // M1-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)));//M1-D1L
  ym2151_regset_write(cs, TL + ch, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 17))); // M1-TL
  // ym2151_regset_write(cs, KS_AR + ch, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)));//M1-KS
  ym2151_regset_write(cs, DT1_MUL + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 19)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)) << 4)); // M1-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)));//M1-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)));//M1-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)));//M1-AMS-EN(bit7)

  // C1
  ym2151_regset_write(cs, KS_AR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 23)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)) << 6));           // C1-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 24)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)) & AMS_EN)); // C1-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 25)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)) << 6));        // C1-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 26)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)) << 4));          // C1-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 16, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)));//C1-D1L
  ym2151_regset_write(cs, TL + ch + 16, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 28))); // C1-TL
  // ym2151_regset_write(cs, KS_AR + ch + 16, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)));//C1-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 30)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)) << 4)); // C1-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 16, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)));//C1-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 16, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)));//C1-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)));//C1-AMS-EN(bit7)

  // M2
  ym2151_regset_write(cs, KS_AR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 34)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)) << 6));           // M2-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 35)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)) & AMS_EN)); // M2-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 36)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)) << 6));        // M2-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 37)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)) << 4));          // M2-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 8, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)));//M2-D1L
  ym2151_regset_write(cs, TL + ch + 8, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 39))); // M2-TL
  // ym2151_regset_write(cs, KS_AR + ch + 8, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)));//M2-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 41)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)) << 4)); // M2-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 8, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)));//M2-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 8, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)));//M2-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)));//M2-AMS-EN(bit7)

  // C2
  ym2151_regset_write(cs, KS_AR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 45)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)) << 6));           // C2-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 46)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)) & AMS_EN)); // C2-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 47)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)) << 6));        // C2-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 48)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)) << 4));          // C2-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 24, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)));//C2-D1L
  ym2151_regset_write(cs, TL + ch + 24, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 50))); // C2-TL
  // ym2151_regset_write(cs, KS_AR + ch + 24, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)));//C2-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 52)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)) << 4)); // C2-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 24, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)));//C2-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 24, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)));//C2-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)));//C2-AMS-EN(bit7)
}

// ym2151パラメータセット(LFO、ノイズ含むすべて)
void ym2151_param_set_all_test(unsigned char cs, unsigned char ch, int inst)
{
  // 入力データ構造
  // LFRQ,AMD,PMD,WF,NFRQ
  //,PAN(bit7,bit6),FL,CON,AMS,PMS,SLOT(bit6~bit3),NE(bit7)
  //,M1-AR,M1-D1R,M1-D2R,M1-RR,M1-D1L,M1-TL,M1-KS,M1-MUL,M1-DT1,M1-DT2,M1-AMS-EN(bit7)
  //,C1-AR,C1-D1R,C1-D2R,C1-RR,C1-D1L,C1-TL,C1-KS,C1-MUL,C1-DT1,C1-DT2,C1-AMS-EN
  //,M2-AR,M2-D1R,M2-D2R,M2-RR,M2-D1L,M2-TL,M2-KS,M2-MUL,M2-DT1,M2-DT2,M2-AMS-EN
  //,C2-AR,C2-D1R,C2-D2R,C2-RR,C2-D1L,C2-TL,C2-KS,C2-MUL,C2-DT1,C2-DT2,C2-AMS-EN
  // reserve,reserve,reserve,reserve,reserve,reserve,reserve,reserve

/*
  ym2151_regset_write(cs, LFRQ, 0xFF, pgm_read_byte_near((int)(ym2151_parameter[inst] + 0)));                            // LFRQ
  ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, 0x7F & pgm_read_byte_near((int)(ym2151_parameter[inst] + 1)));     // AMD
  ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, LFO_MD_F | pgm_read_byte_near((int)(ym2151_parameter[inst] + 2))); // PMD
  ym2151_regset_write(cs, CT_W, LFO_W, pgm_read_byte_near((int)(ym2151_parameter[inst] + 3)));                           // WF
  ym2151_regset_write(cs, NOISE, NFRQ, pgm_read_byte_near((int)(ym2151_parameter[inst] + 4)));                           // NFRQ
*/
  ym2151_regset_write(cs, LR_FB_CON + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 5)) & RL) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)) << 3) & FL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)) & CONECT)); // PAN(bit7,bit6), FL, CON
  // ym2151_regset_write(cs, LR_FB_CON + ch, FL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)));//FL
  // ym2151_regset_write(cs, LR_FB_CON + ch, CONECT, pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)));//CON
  ym2151_regset_write(cs, PMS_AMS + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 8)) & AMS) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)) << 4) & PMS)); // AMS,PMS
  // ym2151_regset_write(cs, PMS_AMS + ch, PMS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)));//PMS
  ym2151_slot_en[(cs << 3) | ch] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 10));         // SLOT(bit6~bit3)
  ym2151_regset_write(cs, NOISE, NE, pgm_read_byte_near((int)(ym2151_parameter[inst] + 11)) >> 7); // NE(bit7)

  // M1
  ym2151_regset_write(cs, KS_AR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 12)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)) << 6));           // M1-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 13)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)) & AMS_EN)); // M1-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 14)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)) << 6));        // M1-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 15)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)) << 4));          // M1-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)));//M1-D1L
  ym2151_regset_write(cs, TL + ch, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 17))); // M1-TL
  // ym2151_regset_write(cs, KS_AR + ch, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)));//M1-KS
  ym2151_regset_write(cs, DT1_MUL + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 19)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)) << 4)); // M1-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)));//M1-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)));//M1-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)));//M1-AMS-EN(bit7)

  // C1
  ym2151_regset_write(cs, KS_AR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 23)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)) << 6));           // C1-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 24)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)) & AMS_EN)); // C1-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 25)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)) << 6));        // C1-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 26)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)) << 4));          // C1-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 16, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)));//C1-D1L
  ym2151_regset_write(cs, TL + ch + 16, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 28))); // C1-TL
  // ym2151_regset_write(cs, KS_AR + ch + 16, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)));//C1-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 30)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)) << 4)); // C1-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 16, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)));//C1-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 16, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)));//C1-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)));//C1-AMS-EN(bit7)

  // M2
  ym2151_regset_write(cs, KS_AR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 34)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)) << 6));           // M2-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 35)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)) & AMS_EN)); // M2-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 36)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)) << 6));        // M2-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 37)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)) << 4));          // M2-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 8, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)));//M2-D1L
  ym2151_regset_write(cs, TL + ch + 8, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 39))); // M2-TL
  // ym2151_regset_write(cs, KS_AR + ch + 8, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)));//M2-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 41)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)) << 4)); // M2-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 8, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)));//M2-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 8, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)));//M2-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)));//M2-AMS-EN(bit7)

  // C2
  ym2151_regset_write(cs, KS_AR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 45)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)) << 6));           // C2-AR,KS
  ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 46)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)) & AMS_EN)); // C2-D1R,AMS-EN(bit7)
  ym2151_regset_write(cs, DT2_D2R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 47)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)) << 6));        // C2-D2R,DT2
  ym2151_regset_write(cs, D1L_RR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 48)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)) << 4));          // C2-RR,D1L
  // ym2151_regset_write(cs, D1L_RR + ch + 24, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)));//C2-D1L
  ym2151_regset_write(cs, TL + ch + 24, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 50))); // C2-TL
  // ym2151_regset_write(cs, KS_AR + ch + 24, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)));//C2-KS
  ym2151_regset_write(cs, DT1_MUL + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 52)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)) << 4)); // C2-MUL,DT1
  // ym2151_regset_write(cs, DT1_MUL + ch + 24, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)));//C2-DT1
  // ym2151_regset_write(cs, DT2_D2R + ch + 24, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)));//C2-DT2
  // ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)));//C2-AMS-EN(bit7)
}



// 音程のセット
void ptc_set(unsigned char ch, unsigned int notenum)
{
  unsigned char cs_temp = ch >> 3;
  unsigned char ch_temp = ch & 0x07;
  if (ch != FM_DRUM_CH)
  { // ドラムチャンネル以外
    ym2151_data[cs_temp].reg.kc[ch_temp] = pgm_read_byte_near((int)(ym2151_note_conv + notenum + def_tone[ch] - 25));
    ym2151_write(cs_temp, KC + ch_temp, pgm_read_byte_near((int)(ym2151_note_conv + notenum + def_tone[ch] - 25)));
  }
  else
  {
    // ドラムチャンネル
    // VCEDパラメータのセット(処理が重いため、テンポが速い曲は注意)
    ym2151_vced_set(FM_DRUM_CH, (unsigned int)(((unsigned int)DRUM_VCED << 7) | (unsigned int)notenum));
    ym2151_write(cs_temp, KC + ch_temp, pgm_read_byte_near((int)(ym2151_note_conv + 64 + def_tone[ch] - 25)));
  }
}

// メインの音量とパンのセット
void main_vel_pan_set(unsigned char vel, unsigned char pan)
{
  // 何もしません。
}

// チャンネルの音量とパンのセット
void vel_pan_set(unsigned char ch, unsigned char vel, unsigned char pan)
{
  // YM2151はチャンネル音量設定ができないためTL値の変更で代用(低音量で音色が変わるので注意)
  // アルゴリズムによって音量制御レジスタ(TL)の個数が変わる
  unsigned char conect_temp = 0; // アルゴリズムの種類一時保存
  unsigned char cs_temp = ch >> 3;
  unsigned char ch_temp = ch & 0x07;

  ym2151_main_vol[ch] = vel;

  conect_temp = ym2151_data[cs_temp].reg.lr_fb_conf[ch_temp] & 0x07;

  if (ch == 7)
  {
    //(ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)
  }
  if ((conect_temp & 0x04) == 0)
  {
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (127));
    }
    else
    {
      //-/ym2151_write(cs_temp, TL + ch_temp + (C2*8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + ((127 - vel)>>1)));
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }

    ym2151_write(cs_temp, TL + ch_temp + (M1 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (M1 * 8)]);
    ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)]);
    ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)]);
  }
  else if (conect_temp == 4)
  {
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }

    ym2151_write(cs_temp, TL + ch_temp + (M1 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (M1 * 8)]);
    ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)]);
  }
  else if (conect_temp == 7)
  {
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (M1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (M1 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (M1 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (M1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }

    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }
  }
  else
  {
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (M2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (M2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }

    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C1 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C1 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }
    if (((unsigned int)ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)) > 127)
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (127));
    }
    else
    {
      ym2151_write(cs_temp, TL + ch_temp + (C2 * 8), TL_TL & (ym2151_data[cs_temp].reg.tl[ch_temp + (C2 * 8)] + pgm_read_byte_near(ym2151_tl_vol_conv + vel)));
    }

    ym2151_write(cs_temp, TL + ch_temp + (M1 * 8), ym2151_data[cs_temp].reg.tl[ch_temp + (M1 * 8)]);
  }

  // パン
  if(test[cs_temp] == TEST_OPP_OPZ)
  {
    // パンが5以下か122以上のとき左右の出力のオンオフを行う
    if (pan <= 5)
    {
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x01);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x30+ch_temp, 0x01, 0x00);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x00);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x01);
    }
    else if (pan >= 122)
    {
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x01);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x30+ch_temp, 0x01, 0x00);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x00);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x02);
    }
    else
    {
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x01);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x30+ch_temp, 0x01, 0x01);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, 0x15, 0xFF, 0x00);//YM2414 OPZ用
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x03);
    }
  }
  else
  {
    // パンが5以下か122以上のとき左右の出力のオンオフを行う
    if (pan <= 5)
    {
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x01);
    }
    else if (pan >= 122)
    {
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x02);
    }
    else
    {
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, RL, 0x03);
    }
  }
 
 
}

// チャンネルノートオン
void note_on(unsigned char ch)
{
  unsigned char cs_temp = ch >> 3;

  // LFOのリセット
  if (ym2151_lfo_rst_en[ch] != 0)
  {
    // LFOをリセットするチャンネルの場合リセット
    ym2151_write(cs_temp, test[cs_temp], LFO_RESET);
    ym2151_write(cs_temp, test[cs_temp], 0);
  }

  if (ch != FM_DRUM_CH)
  {
    // ドラムチャンネル以外
    ym2151_data[cs_temp].reg.kon = (ym2151_slot_en[ch] & SN) | (ch & CH_REG);
    ym2151_write(cs_temp, KON, ym2151_data[cs_temp].reg.kon);
  }
  else
  {
    // ドラムチャンネル
    ym2151_data[cs_temp].reg.kon = (ym2151_slot_en[ch] & SN) | (ch & CH_REG);
    ym2151_write(cs_temp, KON, ym2151_data[cs_temp].reg.kon);
  }
}

// チャンネルノートオフ
void note_off(unsigned char ch)
{
  unsigned char cs_temp = ch >> 3;
 
   if (ch != FM_DRUM_CH)
  { // ドラムチャンネル以外
    ym2151_data[cs_temp].reg.kon = ch & CH_REG;
    ym2151_write(cs_temp, KON, ym2151_data[cs_temp].reg.kon);
  }
  else
  {
    // ドラムチャンネル
    ym2151_data[cs_temp].reg.kon = ch & CH_REG;
    ym2151_write(cs_temp, KON, ym2151_data[cs_temp].reg.kon);
  }
}

// 音色セット
void inst_set(unsigned char ch, unsigned char inst)
{
  ym2151_param_set_all(ch >> 3, ch & 0x07, inst);
}

// MIDI受信用関数/////////////////////////////
// 1バイトずつmidi_readを実行し、1グループのmidiメッセージを受信し終えるとmidi_comを実行
void midi_read(char read_buf)
{
  if (ex_mess_en == 1)
  {
    if ((unsigned char)read_buf == 0xF7)
    { // エクスクルーシブ・メッセージ終了
      ex_mess_en = 0;
      stop_byte = dat_ph;
    }
  }
  else if (((read_buf >> 7) & 0x01) == 1)
  {
    // データ始まり検出したら
    dat_ph = 0;
    ex_mess_en = 0;
    read_buf_h = 0xF0 & read_buf;

    if (((0x80 <= (unsigned char)read_buf_h) && (0xB0 >= (unsigned char)read_buf_h)) || ((unsigned char)read_buf_h == 0xE0))
    {
      // 3バイト読み取り
      running_ch = read_buf; // ランニングステータスチャンネルセット
      stop_byte = 2;
    }
    else if (((unsigned char)read_buf_h == 0xC0) || ((unsigned char)read_buf_h == 0xD0))
    {
      // 2バイト読み取り
      running_ch = read_buf;
      stop_byte = 1;
    }
    else
    {
      switch ((unsigned char)read_buf)
      {
      case 0xF0: // エクスクルーシブ・メッセージ
        ex_mess_en = 1;
        stop_byte = 254;
        // Serial.print("ex_mess_en\r\n");//デバッグ用*/
        break;
      case 0xF1: // MIDIタイムコード
      case 0xF3: // ソング・セレクト
        stop_byte = 1;
        break;
      case 0xF2: // ソング・ポジション・ポインター
        stop_byte = 2;
        break;
      case 0xF6: // チューン・リクエスト
      case 0xF7: // エンド・オブ・エクスクルーシブ
      case 0xF8: // タイミング・クロック(MIDIクロック)
      case 0xFA: // スタート
      case 0xFB: // コンティニュー
      case 0xFC: // ストップ
      case 0xFF: // システム・リセット
        stop_byte = 0;
        break;
      case 0xFE: // アクティブ・センシングc
        stop_byte = 0;
        break;
      }
    }
  }
  else if (dat_ph == 0)
  {
    // ランニングステータス//////
    stop_byte = 2;
    midi_buf[0] = running_ch;
    dat_ph++;
    //////////////////////////
  }
  midi_buf[dat_ph] = read_buf; // 受信データ格納
  if (dat_ph >= stop_byte)
  {
    // 指定バイト数受信完了

    stop_byte = 3;
    ex_mess_en = 0;
    if ((midi_buf[0] == 0xFE) && (dat_ph == 0))
    {
      dat_ph = 0; // アクティブセンシングは処理しない
    }
    else
    {
      midi_com(midi_buf);
    }

    dat_ph = 0;
    midi_buf[0] = 0;
    return;
  }
  if ((((midi_buf[0] >> 7) & 0x01) == 1) || (ex_mess_en == 1))
  {
    // 1バイト目がメッセージ以外読み取りしない
    dat_ph++;
  }
}

// MIDIデータ入力処理
inline void midi_com(unsigned char *in_midi_mess)
{
  unsigned char in_midi_data_buf_h0 = in_midi_mess[0] >> 4;   // 上位4ビットのみを右へ4ビットシフト
  unsigned char in_midi_data_buf_l0 = in_midi_mess[0] & 0x0f; // 下位4ビットのみ チャンネル
  unsigned char in_midi_data_buf_1 = in_midi_mess[1];
  unsigned char in_midi_data_buf_2 = in_midi_mess[2];

  unsigned char cs_temp;
  unsigned char ch_temp;
  // 80
  if (in_midi_data_buf_h0 == 0x08)
  {
    // ノートオフ
    if (in_midi_data_buf_l0 < MIDI_MAX_TR)
    {
      note_off(ym2151_tr[in_midi_data_buf_l0]);
    }
  }
  // 90
  else if (in_midi_data_buf_h0 == 0x09)
  {
    if (in_midi_mess[2] == 0)
    {
      // ノートオフ
      if (in_midi_data_buf_l0 < MIDI_MAX_TR)
      {
        note_off(ym2151_tr[in_midi_data_buf_l0]);
      }
    }
    else
    {
      midi_vel[ym2151_tr[in_midi_data_buf_l0]] = in_midi_data_buf_2; // ベロシティ保存
      // ノートオン
      if (in_midi_data_buf_l0 < MIDI_MAX_TR)
      {
        ptc_set(ym2151_tr[in_midi_data_buf_l0], in_midi_mess[1] + def_key);                                                                                                                                      // チャンネル, 音程
        vel_pan_set(ym2151_tr[in_midi_data_buf_l0], ((int)((int)midi_vel[ym2151_tr[in_midi_data_buf_l0]] * (int)midi_main_vel[ym2151_tr[in_midi_data_buf_l0]]) >> 7), midi_pan[ym2151_tr[in_midi_data_buf_l0]]); // チャンネル, 音量, パン
        note_on(ym2151_tr[in_midi_data_buf_l0]);                                                                                                                                                                 // ノートオン
      }
    }
  }
  else if (in_midi_data_buf_h0 == 0x0A)
  {
    // ポリフォニックキープレッシャー
    // 何もしない
  }
  else if (in_midi_data_buf_h0 == 0x0B)
  {
    // コントロールチェンジ
    switch (in_midi_mess[1])
    {
    case 0x07: // チャンネルボリューム
      midi_main_vel[ym2151_tr[in_midi_data_buf_l0]] = in_midi_mess[2];
      break;
    case 0x0A: // パン
      midi_pan[ym2151_tr[in_midi_data_buf_l0]] = in_midi_mess[2];
      break;
    case 0x78:
    case 0x79:
      for (int i = 0; i < MIDI_MAX_TR; i++)
      {
        note_off(i);
      }
      break;
    case 0x40: // サスティン
      if (in_midi_mess[2] == 0x7F)
      {
        sas_en = 1;
      }
      else
      {
        sas_en = 0;
      }
      break;
    default:
      break;
    }
  }
  else if (in_midi_data_buf_h0 == 0x0C)
  {
    // プログラムチェンジ
    midiprog[in_midi_data_buf_l0] = in_midi_mess[1];
    ym2151_vced_set(ym2151_tr[in_midi_data_buf_l0], fond_vced(midiprog[in_midi_data_buf_l0])); // 指定チャンネルにvcedパラメータを反映
  }
  else if (in_midi_data_buf_h0 == 0x0E)
  {
    // ピッチベンド
    // 何もしない
  }
  else if (in_midi_data_buf_h0 == 0x0F)
  {
    // システムメッセージ
    if (in_midi_mess[0] == 0xFE)
    {
      // アクティブセンシング
    }
    if (in_midi_mess[0] == 0xF0)
    {
      // エクスクルーシブ・メッセージ
      if ((in_midi_mess[1] == 0x43)) // YAMAHA
      {
        // FM音源パラメータ変更(DX-27やTX81Zとの互換性はありません。)
        // DX-27 EXM
        //          sub-st/ch  param group  sw          data
        // F0, 43, (0001nnnn), (00001000), (0mmmmmmm), (0ddddddd), F7
        //[0],[1],[2],       [3],         [4],        [5],        [6]

        // TX81Z EXM
        //          sub-st/ch  param group  param no.   data
        // F0, 43, (0001nnnn), (00010010), (0mmmmmmm), (0ddddddd), F7
        //[0],[1],[2],       [3],         [4],        [5],        [6]

        cs_temp = (ym2151_tr[in_midi_mess[2] & 0x0F]) >> 3;
        ch_temp = ym2151_tr[in_midi_mess[2] & 0x0F] & 0x07;
        unsigned char op_temp = OP4; // オペレータ番号による先頭位置計算用

        // VCEDフォーマット(DX-27やTX81Zのサービスマニュアルを参照、ただしパラメータの互換性が無いものもあり)
        // TX81Zのオペレータ番号とYM2151のオペレータ番号は違うので注意!(YM2414のOP4~OP1 → YM2151のOP1~OP4)




        // Serial.print("YAMAHA\r\n");
        //  Program Change parameter Change(VCEDで受信したパラメータ群と発音チャンネルを結びつけます)
        //          basicRcvch,param group,   ,PGMchangeNo, data(high),  data(low),
        //  F0, 43, (0001nnnn), (00010000), 7F, (0kkkkkkk), (0hhhhhhh), (0lllllll), F7
        //[0],[1],[2]       , [3]       ,[4],        [5],        [6],        [7], [8]
        if (((in_midi_mess[2] & 0x10) == 0x10) && (in_midi_mess[3] == 0x10) && (in_midi_mess[4] == 0x7F))
        {
          // Serial.print("PCPC\r\n");
          int f_c = 0; // 検索用カウンタ
          // プログラムチェンジ、VCED、内部パラメータナンバー(TX81Z互換)、SRAM保存用の4つを結びつける

          // プログラムチェンジを受信した時、プログラムチェンジ値より内部パラメータナンバーをさがし、内部パラメータナンバーからSRAM保存用に変換して現在登録されているVCEDを該当chに反映させる。
          // VCEDを受信した時、chより内部パラメータナンバーをさがし、内部パラメータナンバーをSRAM保存用に変換してVCEDを更新する

          // PCPCで受信したチャンネルは未使用

          for (f_c = 0; f_c < PC_N; f_c++)
          {
            // もし、受信したPCPCに登録済みプログラムチェンジナンバーがある場合、
            // 紐付け更新
            if (pc_nc[f_c] == in_midi_mess[5])
            {
              vced_nc[f_c] = ((unsigned int)in_midi_mess[6] << 7) | ((unsigned int)in_midi_mess[7]);
              vced_num_set(vced_nc[f_c]);
              continue;
            }
          }
          if (f_c >= PC_N)
          {
            // 受信したPCPCに登録済みプログラムチェンジナンバーが無い場合、
            // 新しくプログラムチェンジナンバーとVCEDナンバーを紐付け
            if (vced_pc_c >= PC_N)
            {
              vced_pc_c = 0;
            }
            vced_nc[vced_pc_c] = ((unsigned int)in_midi_mess[6] << 7) | ((unsigned int)in_midi_mess[7]);
            vced_num_set(vced_nc[vced_pc_c]);
            pc_nc[vced_pc_c] = in_midi_mess[5];
            vced_pc_c++;
          }

          // 紐付け完了後、すべてのチャンネルの現在のプログラムチェンジナンバーより、該当チャンネルのVCEDを更新
          //
          for (f_c = 0; f_c < 16; f_c++)
          {
            if (midiprog[f_c] == in_midi_mess[5])
            {
              ym2151_vced_set(ym2151_tr[f_c], fond_vced(midiprog[f_c])); // 指定チャンネルにvcedパラメータを反映
            }
          }
        }
       
        //VCED parameter Change
        //          sub-st/ch  param group  param no.   data
        // F0, 43, (0001nnnn), (00010010), (0mmmmmmm), (0ddddddd), F7
        //[0],[1], [2],       [3],         [4],        [5],        [6]

        // VCED1つ変更
        else if (((in_midi_mess[2] & 0x10) == 0x10) && ((in_midi_mess[3] == 0x08) || (in_midi_mess[3] == 0x12)))
        {
          // 受信したチャンネルのプログラムチェンジより、VCEDナンバーを見つけてパラメータを変更する
          unsigned char vced_itn = vced_itn_get(fond_vced(midiprog[in_midi_mess[2] & 0x0F]));
          vced_ram[vced_itn][in_midi_mess[4]] = in_midi_mess[5];
          ym2151_vced_t_set(ym2151_tr[in_midi_mess[2] & 0x0F], fond_vced(midiprog[in_midi_mess[2] & 0x0F]), in_midi_mess[4]);

        }

        // VCED一括変更(Arduinoの場合、UART受信バッファのサイズを変更しないと動作しない可能性があります。UART送信等出来なくなります。)
       
        //不具合修正前
        //          sub-st/ch  prm group ,size, data [0] , ... ,data [92]  ,sum(未チェック)
        // F0, 43, (0000nnnn), (00000000), 5D, (0ddddddd), ... ,(0ddddddd),(0sssssss), F7
        //[0],[1], [2],       [3],        [4], [5],      , ... ,  [98]    ,[99]      , [100]
        // パラメータ数は93(0x5D)バイト、メッセージバイト数(F0~F7)までは101バイト
        // ただし以下のコードでは、68バイト目以降は未使用なのでパラメータ数は63(0x3F)バイトでも良い
       
        //不具合修正後
        //          sub-st/ch,           , prm group ,size, data [0] , ... ,data [92]  ,sum(未チェック)
        // F0, 43, (0000nnnn), (00000011), (00000000), 5D, (0ddddddd), ... ,(0ddddddd),(0sssssss), F7
        //[0],[1], [2]       , [3]       ,  [5]      ,[5], [6]       , ... ,  [98]    ,[99]      , [100]
        // パラメータ数は93(0x5D)バイト、メッセージバイト数(F0~F7)までは101バイト
        //

        else if (((in_midi_mess[2] & 0x10) == 0x00) && ((in_midi_mess[3] == 0x03) && (in_midi_mess[4] == 0x00) && (in_midi_mess[5] >= 0x3F) && (in_midi_mess[5] <= 0x5D)))
        {
          // 受信したチャンネルのプログラムチェンジより、VCEDナンバーを見つけてパラメータを変更する
          unsigned char vced_itn = vced_itn_get(fond_vced(midiprog[in_midi_mess[2] & 0x0F]));

          for (int f_c = 0; f_c < 63; f_c++)
          {
            //vced_ram[vced_itn][f_c] = in_midi_mess[f_c + 5];//不具合修正
            vced_ram[vced_itn][f_c] = in_midi_mess[f_c + 6];
          }

          ym2151_vced_set(ym2151_tr[in_midi_mess[2] & 0x0F], fond_vced(midiprog[in_midi_mess[2] & 0x0F])); // 指定チャンネルにvcedパラメータを反映
        }
      }

      else if (in_midi_mess[1] == 0x7E && in_midi_mess[2] == 0x7F)
      {
        // ユニバーサル
        if (in_midi_mess[3] == 0x09)
        {
          // GM
          if (in_midi_mess[4] == 0x01)
          {
            // GMシステム・オン
            for (int i = 0; i < MIDI_MAX_TR; i++)
            {
              note_off(i);
            }
          }
          if (in_midi_mess[4] == 0x02)
          {
            // GMシステム・オフ
            for (int i = 0; i < MIDI_MAX_TR; i++)
            {
              note_off(i);
            }
          }
        }
      }
    }
  }
}

// 14bit値VCEDナンバーとvcedパラメータ保存番号と紐付け
void vced_num_set(unsigned int vced_n)
{
  for (int f_c = 0; f_c < VCED_N; f_c++)
  {
    // もし、紐付け済み14bit値VCEDナンバーがある場合、
    // 何もしない
    if (vced_tr[f_c] == vced_n)
    {
      return;
    }
  }
  // 無い場合、新しく紐付け(登録数上限に達したら削除し登録)
  if (vced_tr_c >= VCED_N)
  {
    vced_tr_c = 0;
  }
  vced_tr[vced_tr_c] = vced_n;
  vced_tr_c++;
}
// 14bit値VCEDナンバーからvcedパラメータ保存番号を見つけて返す
unsigned char vced_itn_get(unsigned int vced_n)
{
  unsigned char f_c = 0;
  unsigned char ret_vced_itn = 0;
  for (f_c = 0; f_c < VCED_N; f_c++)
  {
    // もし、紐付け済み14bit値VCEDナンバーがある場合、
    // vcedパラメータ保存番号を返す
    if (vced_tr[f_c] == vced_n)
    {
      ret_vced_itn = f_c;
      continue;
    }
  }
  return ret_vced_itn;
}

// 指定チャンネルにvcedパラメータを反映
void ym2151_vced_set(unsigned char ch, unsigned int vced_n)
{
  unsigned char cs_temp = ch >> 3;
  unsigned char ch_temp = ch & 0x07;
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP4_A, RR, 0x0F);//リリース時間を最小にする
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP3_A, RR, 0x0F);
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP2_A, RR, 0x0F);
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP1_A, RR, 0x0F);
 
  // vced_nは14bit値
  unsigned char vced_itr = vced_itn_get(vced_n); // 14bit値VCEDナンバーからvcedパラメータ保存番号を見つける
  vel_pan_set(ch, 0, 64);
  note_off(ch);

  ym2151_slot_en[ch] = 0x78;         // SLOT(bit6~bit3)
 

  // OP1
  // 0:AR(0-31)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP1_A, AR, vced_ram[vced_itr][0]);
  // 1:D1R(0-31)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP1_A, D1R, vced_ram[vced_itr][1]);
  // 2:D2R(0-31)
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP1_A, D2R, vced_ram[vced_itr][2]);
  // 3:RR(0-15:DX27)(1-15:TX81Z)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP1_A, RR, vced_ram[vced_itr][3]);
  // 4:D1L(0-15)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP1_A, D1L, vced_ram[vced_itr][4]);
  // 5:LS(0-99)
  // VCEDフォーマット非従順、値を無視
  // 6:RS(0-3)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP1_A, KS, 0x03 & vced_ram[vced_itr][9]);
  // 7:EBS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 8:AME(0-1)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP1_A, AMS_EN, vced_ram[vced_itr][8]);
  // 9:KVS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 10:OUT(0-99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  ym2151_regset_write(cs_temp, TL + ch_temp + OP1_A, TL_TL, vced_ram[vced_itr][10]);
  // 11:F(0-63)
  // MUL(4bit)とDT2(2bit) (予想)
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP1_A, MUL, vced_ram[vced_itr][11] >> 2);
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP1_A, DT2, vced_ram[vced_itr][11]);
  // 12:DET(0-6)
  // VCEDフォーマット非従順、本来はcenter=3だが、DT1=0~7として入力
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP1_A, DT1, vced_ram[vced_itr][12]);

  // OP2
  // 13:AR(0-31)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP2_A, AR, vced_ram[vced_itr][13]);
  // 14:D1R(0-31)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP2_A, D1R, vced_ram[vced_itr][14]);
  // 15:D2R(0-31)
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP2_A, D2R, vced_ram[vced_itr][15]);
  // 16:RR(0-15:DX27)(1-15:TX81Z)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP2_A, RR, vced_ram[vced_itr][16]);
  // 17:D1L(0-15)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP2_A, D1L, vced_ram[vced_itr][17]);
  // 18:LS(0-99)
  // VCEDフォーマット非従順、値を無視
  // 19:RS(0-3)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP2_A, KS, 0x03 & vced_ram[vced_itr][19]);
  // 20:EBS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 21:AME(0-1)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP2_A, AMS_EN, vced_ram[vced_itr][21]);
  // 22:KVS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 23:OUT(0-99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  ym2151_regset_write(cs_temp, TL + ch_temp + OP2_A, TL_TL, vced_ram[vced_itr][23]);
  // 24:F(0-63)
  // MUL(4bit)とDT2(2bit) (予想)
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP2_A, MUL, vced_ram[vced_itr][24] >> 2);
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP2_A, DT2, vced_ram[vced_itr][24]);
  // 25:DET(0-6)
  // VCEDフォーマット非従順、本来はcenter=3だが、DT1=0~7として入力
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP2_A, DT1, vced_ram[vced_itr][25]);

  // OP3
  // 26:AR(0-31)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP3_A, AR, vced_ram[vced_itr][26]);
  // 27:D1R(0-31)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP3_A, D1R, vced_ram[vced_itr][27]);
  // 28:D2R(0-31)
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP3_A, D2R, vced_ram[vced_itr][28]);
  // 29:RR(0-15:DX27)(1-15:TX81Z)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP3_A, RR, vced_ram[vced_itr][29]);
  // 30:D1L(0-15)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP3_A, D1L, vced_ram[vced_itr][30]);
  // 31:LS(0-99)
  // VCEDフォーマット非従順、値を無視
  // 32:RS(0-3)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP3_A, KS, 0x03 & vced_ram[vced_itr][32]);
  // 33:EBS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 34:AME(0-1)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP3_A, AMS_EN, vced_ram[vced_itr][34]);
  // 35:KVS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 36:OUT(0-99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  ym2151_regset_write(cs_temp, TL + ch_temp + OP3_A, TL_TL, vced_ram[vced_itr][36]);
  // 37:F(0-63)
  // MUL(4bit)とDT2(2bit) (予想)
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP3_A, MUL, vced_ram[vced_itr][37] >> 2);
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP3_A, DT2, vced_ram[vced_itr][37]);
  // 38:DET(0-6)
  // VCEDフォーマット非従順、本来はcenter=3だが、DT1=0~7として入力
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP3_A, DT1, vced_ram[vced_itr][38]);

  // OP4
  // 39:AR(0-31)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP4_A, AR, vced_ram[vced_itr][39]);
  // 40:D1R(0-31)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP4_A, D1R, vced_ram[vced_itr][40]);
  // 41:D2R(0-31)
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP4_A, D2R, vced_ram[vced_itr][41]);
  // 42:RR(0-15:DX27)(1-15:TX81Z)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP4_A, RR, vced_ram[vced_itr][42]);
  // 43:D1L(0-15)
  ym2151_regset_write(cs_temp, D1L_RR + ch_temp + OP4_A, D1L, vced_ram[vced_itr][43]);
  // 44:LS(0-99)
  // VCEDフォーマット非従順、値を無視
  // 45:RS(0-3)
  ym2151_regset_write(cs_temp, KS_AR + ch_temp + OP4_A, KS, 0x03 & vced_ram[vced_itr][45]);
  // 46:EBS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 47:AME(0-1)
  ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + OP4_A, AMS_EN, vced_ram[vced_itr][47]);
  // 48:KVS(0-7)
  // VCEDフォーマット非従順、値を無視
  // 49:OUT(0-99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  ym2151_regset_write(cs_temp, TL + ch_temp + OP4_A, TL_TL, vced_ram[vced_itr][49]);
  // 50:F(0-63)
  // MUL(4bit)とDT2(2bit) (予想)
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP4_A, MUL, vced_ram[vced_itr][50] >> 2);
  ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + OP4_A, DT2, vced_ram[vced_itr][50]);
  // 51:DET(0-6)
  // VCEDフォーマット非従順、本来はcenter=3だが、DT1=0~7として入力
  ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + OP4_A, DT1, vced_ram[vced_itr][51]);

  // 52:ALG(0-7)
  ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, CONECT, vced_ram[vced_itr][52]);
  // 53:FBL(0~7)
  ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, FL, vced_ram[vced_itr][53]);

  // 54:LFS(0~99)
  // VCEDフォーマット非従順、本来は0~99を0~255にスケーリングしなければいけない。
  // LFO関係はチャンネルに限らず共通なので、0の時無視する
  if (vced_ram[vced_itr][54] != 0)
  {
    ym2151_regset_write(cs_temp, LFRQ, 0xFF, vced_ram[vced_itr][54]);

    // LFOをリセットするチャンネルに登録
    ym2151_lfo_rst_en[ch] = 1;
  }
  else
  {
    // LFOをリセットするチャンネル解除
    ym2151_lfo_rst_en[ch] = 0;
  }
  // 55:LFD(0~99)
  // VCEDフォーマット非従順、値を無視

  // 56:PMD(0~99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  // LFO関係はチャンネルに限らず共通なので、0の時無視する
  if (vced_ram[vced_itr][56] != 0)
  {
    ym2151_regset_write(cs_temp, PMD_AMD, LFO_MD_F | LFO_MD, LFO_MD_F | vced_ram[vced_itr][56]);
  }

  // 57:AMD(0~99)
  // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
  // LFO関係はチャンネルに限らず共通なので、0の時無視する
  if (vced_ram[vced_itr][57] != 0)
  {
    ym2151_regset_write(cs_temp, PMD_AMD, LFO_MD_F | LFO_MD, 0x7F & vced_ram[vced_itr][57]);
  }
  // 58:SYNQ(0~1)
  // VCEDフォーマット非従順、値を無視
  // 59:LW(0~3)
  // LFO関係はチャンネルに限らず共通なので、LFRQ=0の時無視する
  if (vced_ram[vced_itr][54] != 0)
  {
    ym2151_regset_write(cs_temp, CT_W, LFO_W, vced_ram[vced_itr][59]);
  }
  // 60:PMS(0~7)
  ym2151_regset_write(cs_temp, PMS_AMS + ch_temp, PMS, vced_ram[vced_itr][60]);
  // 61:AMS(0~7)
  // VCEDフォーマット非従順、本来は0~7を0~3にスケーリングしなければいけない。
  ym2151_regset_write(cs_temp, PMS_AMS + ch_temp, AMS, vced_ram[vced_itr][61]);
  // 62:TRPS,MID.C(0~48)//62:TRPS,MID.C(0~48) center = 24
  def_tone[ch] = vced_ram[vced_itr][62] - 24;

  // 以降無視
}

// 指定チャンネルにvcedパラメータを1つ反映
void ym2151_vced_t_set(unsigned char ch, unsigned int vced_n, unsigned int vced_pn)
{
  unsigned char op_temp = OP1;
  unsigned char cs_temp = ch >> 3;
  unsigned char ch_temp = ch & 0x07;
  unsigned int vced_pn_temp = vced_pn;
  // vced_nは14bit値
  unsigned char vced_itr = vced_itn_get(vced_n); // 14bit値VCEDナンバーからvcedパラメータ保存番号を見つける

  // オペレータに対するパラメータ変更
  if ((vced_pn_temp >= 0) && (vced_pn_temp <= 51))
  {
    // オペレータ番号による先頭位置計算
    if ((vced_pn_temp >= 13) && (vced_pn_temp <= 25))
    {
      // OP2
      op_temp = OP2;
      vced_pn_temp -= 13;
    }
    else if ((vced_pn_temp >= 26) && (vced_pn_temp <= 38))
    {
      // OP3
      op_temp = OP3;
      vced_pn_temp -= 26;
    }
    else if ((vced_pn_temp >= 39) && (vced_pn_temp <= 51))
    {
      // OP4
      op_temp = OP4;
      vced_pn_temp -= 39;
    }
    switch (vced_pn_temp)
    {
    case 0: // 0:AR(0-31)
      ym2151_regset_write(cs_temp, KS_AR + ch_temp + (op_temp * 8), AR, vced_ram[vced_itr][vced_pn]);
      break;
    case 1: // 1:D1R(0-31)
      ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + (op_temp * 8), D1R, vced_ram[vced_itr][vced_pn]);
      break;
    case 2: // 2:D2R(0-31)
      ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + (op_temp * 8), D2R, vced_ram[vced_itr][vced_pn]);
      break;
    case 3: // 3:RR(0-15:DX27)(1-15:TX81Z)
      ym2151_regset_write(cs_temp, D1L_RR + ch_temp + (op_temp * 8), RR, vced_ram[vced_itr][vced_pn]);
      break;
    case 4: // 4:D1L(0-15)
      ym2151_regset_write(cs_temp, D1L_RR + ch_temp + (op_temp * 8), D1L, vced_ram[vced_itr][vced_pn]);
      break;
    case 5: // 5:LS(0-99)
      // VCEDフォーマット非従順、値を無視
      break;
    case 6: // 6:RS(0-3)
      // VCEDフォーマット非従順、値を無視
      ym2151_regset_write(cs_temp, KS_AR + ch_temp + (op_temp * 8), KS, 0x03 & vced_ram[vced_itr][vced_pn]);
      break;
    case 7: // 7:EBS(0-7)
      // VCEDフォーマット非従順、値を無視
      break;
    case 8: // 8:AME(0-1)
      ym2151_regset_write(cs_temp, AMS_EN_D1R + ch_temp + (op_temp * 8), AMS_EN, vced_ram[vced_itr][vced_pn]);
      break;
    case 9: // 9:KVS(0-7)
      // VCEDフォーマット非従順、値を無視
      break;
    case 10: // 10:OUT(0-99)
      // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
      ym2151_regset_write(cs_temp, TL + ch_temp + (op_temp * 8), TL_TL, vced_ram[vced_itr][vced_pn]);
      break;
    case 11: // 11:F(0-63)
      // MUL(4bit)とDT2(2bit) (予想)
      ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + (op_temp * 8), MUL, vced_ram[vced_itr][vced_pn] >> 2);
      ym2151_regset_write(cs_temp, DT2_D2R + ch_temp + (op_temp * 8), DT2, vced_ram[vced_itr][vced_pn]);
      break;
    case 12: // 12:DET(0-6)
      // VCEDフォーマット非従順、本来はcenter=3だが、DT1=0~7として入力
      ym2151_regset_write(cs_temp, DT1_MUL + ch_temp + (op_temp * 8), DT1, vced_ram[vced_itr][vced_pn]);
      break;

    default:
      break;
    }
  }
  else // チャンネルごとに対するパラメータ変更(例外あり)
  {

    switch (vced_pn)
    {
    case 52: // 52:ALG(0-7)
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, CONECT, vced_ram[vced_itr][vced_pn]);
      break;
    case 53: // 53:FBL(0~7)
      ym2151_regset_write(cs_temp, LR_FB_CON + ch_temp, FL, vced_ram[vced_itr][vced_pn]);
      break;
    case 54: // 54:LFS(0~99)
      // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
      // LFO関係はチャンネルに限らず共通なので注意
      if (vced_ram[vced_itr][54] != 0)
      {
        ym2151_regset_write(cs_temp, LFRQ, 0xFF, vced_ram[vced_itr][vced_pn]);
        // LFOをリセットするチャンネルに登録
        ym2151_lfo_rst_en[ch] = 1;
      }
      else
      {
        // LFOをリセットするチャンネル解除
        ym2151_lfo_rst_en[ch] = 0;
      }
      break;

    case 55: // 55:LFD(0~99)
      // VCEDフォーマット非従順、値を無視
      break;
    case 56: // 56:PMD(0~99)
      // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
      // LFO関係はチャンネルに限らず共通なので注意
      if (vced_ram[vced_itr][56] != 0)
      {
        ym2151_regset_write(cs_temp, PMD_AMD, LFO_MD_F | LFO_MD, LFO_MD_F | vced_ram[vced_itr][vced_pn]);
      }
      break;
    case 57: // 57:AMD(0~99)
      // VCEDフォーマット非従順、本来は0~99を0~127にスケーリングしなければいけない。
      // LFO関係はチャンネルに限らず共通なので注意
      if (vced_ram[vced_itr][57] != 0)
      {
        ym2151_regset_write(cs_temp, PMD_AMD, LFO_MD_F | LFO_MD, 0x7F & vced_ram[vced_itr][vced_pn]);
      }
      break;
    case 58: // 58:SYNQ(0~1)
      // VCEDフォーマット非従順、値を無視
      break;
    case 59: // 59:LW(0~3)
      ym2151_regset_write(cs_temp, CT_W, LFO_W, vced_ram[vced_itr][vced_pn]);
      break;
    case 60: // 60:PMS(0~7)
      ym2151_regset_write(cs_temp, PMS_AMS + ch_temp, PMS, vced_ram[vced_itr][vced_pn]);
      break;
    case 61: // 61:AMS(0~7)
      ym2151_regset_write(cs_temp, PMS_AMS + ch_temp, AMS, vced_ram[vced_itr][vced_pn]);
      break;
    case 62: // 62:TRPS,MID.C(0~48) center = 24
      def_tone[ch] = vced_ram[vced_itr][vced_pn] - 24;
      break;

    default:
      break;
    }
  }
}

// プログラムチェンジナンバーよりVCEDナンバー(14bit値)をさがし、返す
unsigned int fond_vced(unsigned char pc_num)
{
  unsigned int ret_vced_n = 0; // 見つからない場合は0を返す
  for (int f_c = 0; f_c < PC_N; f_c++)
  {
    if (pc_nc[f_c] == pc_num)
    {
      ret_vced_n = vced_nc[f_c];
      continue;
    }
  }
  return ret_vced_n;
}

//起動時に内部パラメータをVCED保存領域にセットする
void intparam_set_vced(unsigned char vced_itr, unsigned char inst)
{
  // 入力データ構造
  // LFRQ,AMD,PMD,WF,NFRQ
  //,PAN(bit7,bit6),FL,CON,AMS,PMS,SLOT(bit6~bit3),NE(bit7)
  //,M1-AR,M1-D1R,M1-D2R,M1-RR,M1-D1L,M1-TL,M1-KS,M1-MUL,M1-DT1,M1-DT2,M1-AMS-EN(bit7)
  //,C1-AR,C1-D1R,C1-D2R,C1-RR,C1-D1L,C1-TL,C1-KS,C1-MUL,C1-DT1,C1-DT2,C1-AMS-EN
  //,M2-AR,M2-D1R,M2-D2R,M2-RR,M2-D1L,M2-TL,M2-KS,M2-MUL,M2-DT1,M2-DT2,M2-AMS-EN
  //,C2-AR,C2-D1R,C2-D2R,C2-RR,C2-D1L,C2-TL,C2-KS,C2-MUL,C2-DT1,C2-DT2,C2-AMS-EN
  // TRPS(音程:標準は24),reserve,reserve,reserve,reserve,reserve,reserve,reserve

  //LFRQ
  vced_ram[vced_itr][54] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 0));
  // AMD
  vced_ram[vced_itr][57] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 1));
  // PMD
  vced_ram[vced_itr][56] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 2));
  // WF
  vced_ram[vced_itr][59] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 3));
  // NFRQ
  //無視

  //PAN(bit7,bit6)
  //無視
  //FL
  vced_ram[vced_itr][53] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 6));
  //CONECT
  vced_ram[vced_itr][52] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 7));
  //PMS
  vced_ram[vced_itr][60] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 8));
  //AMS
  vced_ram[vced_itr][61] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 9));
  //SLOT
  //無視
  //NE
  //無視

 
  //ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, 0x7F & pgm_read_byte_near((int)(ym2151_parameter[inst] + 1)));     // AMD
  //ym2151_regset_write(cs, PMD_AMD, LFO_MD_F | LFO_MD, LFO_MD_F | pgm_read_byte_near((int)(ym2151_parameter[inst] + 2))); // PMD
  //ym2151_regset_write(cs, CT_W, LFO_W, pgm_read_byte_near((int)(ym2151_parameter[inst] + 3)));                           // WF
  //ym2151_regset_write(cs, NOISE, NFRQ, pgm_read_byte_near((int)(ym2151_parameter[inst] + 4)));                           // NFRQ

  //ym2151_regset_write(cs, LR_FB_CON + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 5)) & RL) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)) << 3) & FL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)) & CONECT)); // PAN(bit7,bit6), FL, CON
  //// ym2151_regset_write(cs, LR_FB_CON + ch, FL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 6)));//FL
  //// ym2151_regset_write(cs, LR_FB_CON + ch, CONECT, pgm_read_byte_near((int)(ym2151_parameter[inst] + 7)));//CON
  //ym2151_regset_write(cs, PMS_AMS + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 8)) & AMS) | ((pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)) << 4) & PMS)); // AMS,PMS
  //// ym2151_regset_write(cs, PMS_AMS + ch, PMS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 9)));//PMS
  //ym2151_slot_en[(cs << 3) | ch] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 10));         // SLOT(bit6~bit3)
  //ym2151_regset_write(cs, NOISE, NE, pgm_read_byte_near((int)(ym2151_parameter[inst] + 11)) >> 7); // NE(bit7)

  // OP1-M1-AR
  vced_ram[vced_itr][0] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 12));
  // OP1-M1-D1R
  vced_ram[vced_itr][1] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 13));
  // OP1-M1-D2R
  vced_ram[vced_itr][2] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 14));
  // OP1-M1-RR
  vced_ram[vced_itr][3] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 15));
  // OP1-M1-D1L
  vced_ram[vced_itr][4] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 16));
  // OP1-M1-TL
  vced_ram[vced_itr][10] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 17));
  // OP1-M1-KS
  vced_ram[vced_itr][6] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 18));
  // OP1-M1-MUL-DT2
  vced_ram[vced_itr][11] = (pgm_read_byte_near((int)(ym2151_parameter[inst] + 19)) << 2) | pgm_read_byte_near((int)(ym2151_parameter[inst] + 21));
  // OP1-M1-DT1
  vced_ram[vced_itr][12] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 20));
  // OP1-M1-AMS_EN
  vced_ram[vced_itr][8] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)) >> 7;
 


  // M1
  //ym2151_regset_write(cs, KS_AR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 12)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)) << 6));           // M1-AR,KS
  //ym2151_regset_write(cs, AMS_EN_D1R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 13)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)) & AMS_EN)); // M1-D1R,AMS-EN(bit7)
  //ym2151_regset_write(cs, DT2_D2R + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 14)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)) << 6));        // M1-D2R,DT2
  //ym2151_regset_write(cs, D1L_RR + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 15)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)) << 4));          // M1-RR,D1L
  //// ym2151_regset_write(cs, D1L_RR + ch, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 16)));//M1-D1L
  //ym2151_regset_write(cs, TL + ch, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 17))); // M1-TL
  //// ym2151_regset_write(cs, KS_AR + ch, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 18)));//M1-KS
  //ym2151_regset_write(cs, DT1_MUL + ch, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 19)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)) << 4)); // M1-MUL,DT1
  //// ym2151_regset_write(cs, DT1_MUL + ch, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 20)));//M1-DT1
  //// ym2151_regset_write(cs, DT2_D2R + ch, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 21)));//M1-DT2
  //// ym2151_regset_write(cs, AMS_EN_D1R + ch, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 22)));//M1-AMS-EN(bit7)

  // OP2-C1-AR
  vced_ram[vced_itr][13] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 23));
  // OP2-C1-D1R
  vced_ram[vced_itr][14] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 24));
  // OP2-C1-D2R
  vced_ram[vced_itr][15] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 25));
  // OP2-C1-RR
  vced_ram[vced_itr][16] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 26));
  // OP2-C1-D1L
  vced_ram[vced_itr][17] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 27));
  // OP2-C1-TL
  vced_ram[vced_itr][23] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 28));
  // OP2-C1-KS
  vced_ram[vced_itr][19] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 29));
  // OP2-C1-MUL-DT2
  vced_ram[vced_itr][24] = (pgm_read_byte_near((int)(ym2151_parameter[inst] + 30)) << 2) | pgm_read_byte_near((int)(ym2151_parameter[inst] + 32));
  // OP2-C1-DT1
  vced_ram[vced_itr][25] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 31));
  // OP2-C1-AMS_EN
  vced_ram[vced_itr][21] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)) >> 7;
 

  // C1
  //ym2151_regset_write(cs, KS_AR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 23)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)) << 6));           // C1-AR,KS
  //ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 24)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)) & AMS_EN)); // C1-D1R,AMS-EN(bit7)
  //ym2151_regset_write(cs, DT2_D2R + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 25)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)) << 6));        // C1-D2R,DT2
  //ym2151_regset_write(cs, D1L_RR + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 26)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)) << 4));          // C1-RR,D1L
  //// ym2151_regset_write(cs, D1L_RR + ch + 16, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 27)));//C1-D1L
  //ym2151_regset_write(cs, TL + ch + 16, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 28))); // C1-TL
  //// ym2151_regset_write(cs, KS_AR + ch + 16, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 29)));//C1-KS
  //ym2151_regset_write(cs, DT1_MUL + ch + 16, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 30)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)) << 4)); // C1-MUL,DT1
  //// ym2151_regset_write(cs, DT1_MUL + ch + 16, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 31)));//C1-DT1
  //// ym2151_regset_write(cs, DT2_D2R + ch + 16, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 32)));//C1-DT2
  //// ym2151_regset_write(cs, AMS_EN_D1R + ch + 16, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 33)));//C1-AMS-EN(bit7)

  // OP3-M2-AR
  vced_ram[vced_itr][26] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 34));
  // OP3-M2-D1R
  vced_ram[vced_itr][27] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 35));
  // OP3-M2-D2R
  vced_ram[vced_itr][28] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 36));
  // OP3-M2-RR
  vced_ram[vced_itr][29] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 37));
  // OP3-M2-D1L
  vced_ram[vced_itr][30] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 38));
  // OP3-M2-TL
  vced_ram[vced_itr][36] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 39));
  // OP3-M2-KS
  vced_ram[vced_itr][32] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 40));
  // OP3-M2-MUL-DT2
  vced_ram[vced_itr][37] = (pgm_read_byte_near((int)(ym2151_parameter[inst] + 41)) << 2) | pgm_read_byte_near((int)(ym2151_parameter[inst] + 43));
  // OP3-M2-DT1
  vced_ram[vced_itr][38] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 42));
  // OP3-M2-AMS_EN
  vced_ram[vced_itr][34] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)) >> 7;
 

  // M2
  //ym2151_regset_write(cs, KS_AR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 34)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)) << 6));           // M2-AR,KS
  //ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 35)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)) & AMS_EN)); // M2-D1R,AMS-EN(bit7)
  //ym2151_regset_write(cs, DT2_D2R + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 36)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)) << 6));        // M2-D2R,DT2
  //ym2151_regset_write(cs, D1L_RR + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 37)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)) << 4));          // M2-RR,D1L
  //// ym2151_regset_write(cs, D1L_RR + ch + 8, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 38)));//M2-D1L
  //ym2151_regset_write(cs, TL + ch + 8, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 39))); // M2-TL
  //// ym2151_regset_write(cs, KS_AR + ch + 8, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 40)));//M2-KS
  //ym2151_regset_write(cs, DT1_MUL + ch + 8, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 41)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)) << 4)); // M2-MUL,DT1
  //// ym2151_regset_write(cs, DT1_MUL + ch + 8, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 42)));//M2-DT1
  //// ym2151_regset_write(cs, DT2_D2R + ch + 8, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 43)));//M2-DT2
  //// ym2151_regset_write(cs, AMS_EN_D1R + ch + 8, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 44)));//M2-AMS-EN(bit7)

// OP4-C2-AR
  vced_ram[vced_itr][39] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 45));
  // OP4-C2-D1R
  vced_ram[vced_itr][40] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 46));
  // OP4-C2-D2R
  vced_ram[vced_itr][41] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 47));
  // OP4-C2-RR
  vced_ram[vced_itr][42] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 48));
  // OP4-C2-D1L
  vced_ram[vced_itr][43] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 49));
  // OP4-C2-TL
  vced_ram[vced_itr][49] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 50));
  // OP4-C2-KS
  vced_ram[vced_itr][45] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 51));
  // OP4-C2-MUL-DT2
  vced_ram[vced_itr][50] = (pgm_read_byte_near((int)(ym2151_parameter[inst] + 52)) << 2) | pgm_read_byte_near((int)(ym2151_parameter[inst] + 54));
  // OP4-C2-DT1
  vced_ram[vced_itr][51] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 53));
  // OP4-C2-AMS_EN
  vced_ram[vced_itr][47] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)) >> 7;
 
  // C2
  //ym2151_regset_write(cs, KS_AR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 45)) & AR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)) << 6));           // C2-AR,KS
  //ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 46)) & D1R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)) & AMS_EN)); // C2-D1R,AMS-EN(bit7)
  //ym2151_regset_write(cs, DT2_D2R + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 47)) & D2R) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)) << 6));        // C2-D2R,DT2
  //ym2151_regset_write(cs, D1L_RR + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 48)) & RR) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)) << 4));          // C2-RR,D1L
  //// ym2151_regset_write(cs, D1L_RR + ch + 24, D1L, pgm_read_byte_near((int)(ym2151_parameter[inst] + 49)));//C2-D1L
  //ym2151_regset_write(cs, TL + ch + 24, TL_TL, pgm_read_byte_near((int)(ym2151_parameter[inst] + 50))); // C2-TL
  //// ym2151_regset_write(cs, KS_AR + ch + 24, KS, pgm_read_byte_near((int)(ym2151_parameter[inst] + 51)));//C2-KS
  //ym2151_regset_write(cs, DT1_MUL + ch + 24, 0xFF, (pgm_read_byte_near((int)(ym2151_parameter[inst] + 52)) & MUL) | (pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)) << 4)); // C2-MUL,DT1
  //// ym2151_regset_write(cs, DT1_MUL + ch + 24, DT1, pgm_read_byte_near((int)(ym2151_parameter[inst] + 53)));//C2-DT1
  //// ym2151_regset_write(cs, DT2_D2R + ch + 24, DT2, pgm_read_byte_near((int)(ym2151_parameter[inst] + 54)));//C2-DT2
  //// ym2151_regset_write(cs, AMS_EN_D1R + ch + 24, AMS_EN, pgm_read_byte_near((int)(ym2151_parameter[inst] + 55)));//C2-AMS-EN(bit7)

  vced_ram[vced_itr][62] = pgm_read_byte_near((int)(ym2151_parameter[inst] + 56));//音程
}










上記動画で使用した音色パラメータを置いておきます。






・参考


YM2151の偽物・互換品リスト
EMMA Italian Dumping team   YM2151



YM2151テストレジスタの詳細
MSX Assembly Page
Yamaha YM2151 OPM test register info   by Jarek Burczynski



YM2151の出力の詳細
如是我聞~これからFPGAの話をしよう~  FM音源のDACインターフェース解析の更新 [IPコア]




・関連記事









/*検索エンジン用単語リスト*/
使い方
sound generator
how to use
usage




おわり