2022年6月25日土曜日

YM2151の使い方

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のサンプルです。
音色パラメータの送信&反映手順の参考として用意しておきます。



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

// 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 0x01      // テストレジスタ
#define LFO_RESET 0x02 // テストレジスタ_LFOリセット

#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レジスタ保存
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, 0xFF, 0x00);
  }

  // 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;
    }
  }

  // アドレスのセット
  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)]);
  }

  // パン
  // パンが5以下か122以上のとき左右の出力のオンオフを行う
  if (pan <= 5)
  {
    ym2151_regset_write(cs_temp, LR_FB_CON + ch, RL, 0x01);
  }
  else if (pan >= 122)
  {
    ym2151_regset_write(cs_temp, LR_FB_CON + ch, RL, 0x02);
  }
  else
  {
    ym2151_regset_write(cs_temp, LR_FB_CON + ch, 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, LFO_RESET);
    ym2151_write(cs_temp, TEST, 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  param 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)バイトでも良い
        else if (((in_midi_mess[2] & 0x10) == 0x00) && ((in_midi_mess[3] == 0x00) && (in_midi_mess[4] >= 0x3F) && (in_midi_mess[4] <= 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];
          }

          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;
  // vced_nは14bit値
  unsigned char vced_itr = vced_itn_get(vced_n); // 14bit値VCEDナンバーからvcedパラメータ保存番号を見つける

  // オペレータに対するパラメータ変更
  if ((vced_pn >= 0) && (vced_pn <= 51))
  {
    // オペレータ番号による先頭位置計算
    if ((vced_pn >= 13) && (vced_pn <= 25))
    {
      // OP2
      op_temp = OP2;
      vced_pn -= 13;
    }
    else if ((vced_pn >= 26) && (vced_pn <= 38))
    {
      // OP3
      op_temp = OP3;
      vced_pn -= 26;
    }
    else if ((vced_pn >= 39) && (vced_pn <= 51))
    {
      // OP4
      op_temp = OP4;
      vced_pn -= 39;
    }
    switch (vced_pn)
    {
    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コア]




・関連記事













1 件のコメント:

  1. 情報の一覧性が高くて助かります。まとめありがとうございます。、

    返信削除