diff pmdwin.cpp @ 0:c55ea9478c80

Hello Gensokyo!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 May 2013 10:29:21 +0200
parents
children
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/pmdwin.cpp
@@ -0,0 +1,5616 @@
+//=============================================================================
+//	Professional Music Driver [P.M.D.] version 4.8
+//			Programmed By M.Kajihara
+//			Windows Converted by C60
+//=============================================================================
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#if defined(WIN32) || defined(WIN64)
+#include <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnt.h>
+#else
+#include <sys/mman.h>
+#endif
+#include "jisx0208.h"
+#include "pmdwin.h"
+
+/******************************************************************************
+;   Part Table
+******************************************************************************/
+static const uint8_t part_table[][3] = {       // for PMDB2
+    //      Part<94>ヤ<8d><86>,Partb,<89>ケ<8c>ケ<94>ヤ<8d><86>
+    {    0, 1, 0    },  //A
+    {    1, 2, 0    },  //B
+    {    2, 3, 0    },  //C
+    {    3, 1, 1    },  //D
+    {    4, 2, 1    },  //E
+    {    5, 3, 1    },  //F
+    {    6, 1, 2    },  //G
+    {    7, 2, 2    },  //H
+    {    8, 3, 2    },  //I
+    {    9, 1, 3    },  //J
+    {   10, 3, 4    },  //K
+    {   11, 3, 0    },  //c2
+    {   12, 3, 0    },  //c3
+    {   13, 3, 0    },  //c4
+    {  255, 0, 255  },  //Rhythm
+    {   22, 3, 1    },  //Effect
+};
+
+static char *notes[12] = {
+    "C ", "Db", "D ", "Eb", "E ", "F ", "F#", "G ", "G#", "A ", "Bb", "B "
+}; 
+
+static char *drums[6] = {
+    "Bass   ", "Snare  ", "RideCym", "Hi-Hat ", "Tom-Tom", "Rim    " 
+};
+
+static inline unsigned int av_log2(unsigned int x) {
+    unsigned int ret;
+    __asm__ volatile("or $1, %1 \n\t"
+             "bsr %1, %0 \n\t"
+             :"=a"(ret) : "D"(x));
+    return ret;
+}
+
+/******************************************************************************
+;   <89>ケ<8a>KDATA(FM)
+******************************************************************************/
+static const uint16_t fnum_data[] = {
+    0x026a, //  C
+    0x028f, //  D-
+    0x02b6, //  D
+    0x02df, //  E-
+    0x030b, //  E
+    0x0339, //  F
+    0x036a, //  G-
+    0x039e, //  G
+    0x03d5, //  A-
+    0x0410, //  A
+    0x044e, //  B-
+    0x048f  //  B
+};
+
+/******************************************************************************
+;   <89>ケ<8a>KDATA(PSG)
+******************************************************************************/
+static const uint16_t psg_tune_data[] = {
+    0x0ee8, //  C
+    0x0e12, //  D-
+    0x0d48, //  D
+    0x0c89, //  E-
+    0x0bd5, //  E
+    0x0b2b, //  F
+    0x0a8a, //  G-
+    0x09f3, //  G
+    0x0964, //  A-
+    0x08dd, //  A
+    0x085e, //  B-
+    0x07e6, //  B
+};
+
+
+/******************************************************************************
+;   <82>e<82>l<89>ケ<90>F<82>フ<83>L<83><83><83><8a><83>A<82>フ<83>e<81>[<83>u<83><8b>
+******************************************************************************/
+static const uint8_t carrier_table[] = {
+    0x80, 0x80, 0x80, 0x80, 0xa0, 0xe0, 0xe0, 0xf0,
+    0xee, 0xee, 0xee, 0xee, 0xcc, 0x88, 0x88, 0x00
+};
+
+/******************************************************************************
+;   <83>h<83><89><83><80><83>X<97>p<83><8a><83>Y<83><80><83>f<81>[<83>^
+******************************************************************************/
+static const uint8_t rhydat[][3] = {
+    //  PT,PAN/VOLUME,KEYON
+    {   0x18, 0xdf, 0x01    },  //  <83>o<83>X
+    {   0x19, 0xdf, 0x02    },  //  <83>X<83>l<83>A
+    {   0x1c, 0x5f, 0x10    },  //  <83>^<83><80>[LOW]
+    {   0x1c, 0xdf, 0x10    },  //  <83>^<83><80>[MID]
+    {   0x1c, 0x9f, 0x10    },  //  <83>^<83><80>[HIGH]
+    {   0x1d, 0xd3, 0x20    },  //  <83><8a><83><80>
+    {   0x19, 0xdf, 0x02    },  //  <83>N<83><89><83>b<83>v
+    {   0x1b, 0x9c, 0x88    },  //  C<83>n<83>C<83>n<83>b<83>g
+    {   0x1a, 0x9d, 0x04    },  //  O<83>n<83>C<83>n<83>b<83>g
+    {   0x1a, 0xdf, 0x04    },  //  <83>V<83><93><83>o<83><8b>
+    {   0x1a, 0x5e, 0x04    }   //  RIDE<83>V<83><93><83>o<83><8b>
+};
+
+
+//=============================================================================
+//	TimerAの処理[メイン]
+//=============================================================================
+void PMDWIN::TimerA_main(void)
+{
+	open_work.TimerAflag = 1;
+	open_work.TimerAtime++;
+
+	if(effwork.effon) {
+		effplay();		//		SSG効果音処理
+	}
+	
+	open_work.TimerAflag = 0;
+}
+
+
+//=============================================================================
+//	TimerBの処理[メイン]
+//=============================================================================
+void PMDWIN::TimerB_main(void)
+{
+	open_work.TimerBflag = 1;
+	if(pmdwork.music_flag) {
+		if(pmdwork.music_flag & 1) {
+			mstart();	
+		}
+
+		if(pmdwork.music_flag & 2) {
+			mstop();
+		}
+	}
+	
+	if(open_work.play_flag) {
+		mmain();
+		settempo_b();
+		syousetu_count();
+		pmdwork.lastTimerAtime = open_work.TimerAtime;
+	}
+	open_work.TimerBflag = 0;
+}
+
+
+//=============================================================================
+//	MUSIC PLAYER MAIN [FROM TIMER-B]
+//=============================================================================
+void PMDWIN::mmain(void)
+{
+	int		i;
+
+	pmdwork.loop_work = 3;
+	for(i = 0; i < 3; i++) {	
+		pmdwork.partb = i + 1;
+		psgmain(&SSGPart[i]);
+	}
+  
+	pmdwork.fmsel = 0x100;
+	for(i = 0; i < 3; i++) {
+		pmdwork.partb = i + 1;
+			fmmain(&FMPart[i+3]);
+	}
+
+	pmdwork.fmsel = 0;
+	for(i = 0; i < 3; i++) {
+		pmdwork.partb = i + 1;
+		fmmain(&FMPart[i]);
+	}
+	rhythmmain(&RhythmPart);
+
+	for(i = 0; i < 8; i++) {
+		pmdwork.partb = i;
+    }
+	if(pmdwork.loop_work == 0) return;
+
+	for(i = 0; i < 6; i++) {
+		if(FMPart[i].loopcheck != 3) FMPart[i].loopcheck = 0;
+	}
+
+	for(i = 0; i < 3; i++) {
+		if(SSGPart[i].loopcheck != 3) SSGPart[i].loopcheck = 0;
+	}
+	if(RhythmPart.loopcheck != 3) RhythmPart.loopcheck = 0;
+
+	if(pmdwork.loop_work != 3) {
+		open_work.status2++;
+		if(open_work.status2 == 255) open_work.status2 = 1;
+	} else {
+		open_work.status2 = -1;
+	}
+}
+
+//=============================================================================
+//	FM音源演奏メイン
+//=============================================================================
+void PMDWIN::fmmain(QQ *qq)
+{
+	uchar	*si;
+	
+	if(qq->address == NULL) return;
+	
+	si = qq->address;
+	
+	qq->leng--;
+	
+	if(qq->partmask) {
+		qq->keyoff_flag = -1;
+	} else {
+		// KEYOFF CHECK & Keyoff
+		if((qq->keyoff_flag & 3) == 0) {		// 既にkeyoffしたか?
+			if(qq->leng <= qq->qdat) {
+				keyoff(qq);
+				qq->keyoff_flag = -1;
+			}
+		}
+	}
+	
+	// LENGTH CHECK
+	if(qq->leng == 0) {
+		if(qq->partmask == 0) qq->lfoswi &= 0xf7;
+		
+		while(1) {
+			if(*si > 0x80 && *si != 0xda) {
+				si = commands(qq, si);
+			} else if(*si == 0x80) {
+				qq->address = si;
+				qq->loopcheck = 3;
+				qq->onkai = 255;
+				if(qq->partloop == NULL) {
+					if(qq->partmask) {
+						pmdwork.tieflag = 0;
+						pmdwork.volpush_flag = 0;
+						pmdwork.loop_work &= qq->loopcheck;
+						return;
+					} else {
+						break;
+					}
+				}
+				// "L"があった時
+				si = qq->partloop;
+				qq->loopcheck = 1;
+			} else {
+				if(*si == 0xda) {		// ポルタメント
+					si = porta(qq, ++si);
+					pmdwork.loop_work &= qq->loopcheck;
+					return;
+				} else if(qq->partmask == 0) {
+					//  TONE SET
+					lfoinit(qq, *si);
+					fnumset(qq, oshift(qq, *si++));
+					
+					qq->leng = *si++;
+					si = calc_q(qq, si);
+					
+					if(qq->volpush && qq->onkai != 255) {
+						if(--pmdwork.volpush_flag) {
+							pmdwork.volpush_flag = 0;
+							qq->volpush = 0;
+						}
+					}
+					
+					volset(qq);
+					otodasi(qq);
+					keyon(qq);
+					
+					qq->keyon_flag++;
+					qq->address = si;
+					
+					pmdwork.tieflag = 0;
+					pmdwork.volpush_flag = 0;
+					
+					if(*si == 0xfb) {		// '&'が直後にあったらkeyoffしない
+						qq->keyoff_flag = 2;
+					} else {
+						qq->keyoff_flag = 0;
+					}
+					pmdwork.loop_work &= qq->loopcheck;
+					return;
+				} else {
+					si++;
+					qq->fnum = 0;		//休符に設定
+					qq->onkai = 255;
+					qq->onkai_def = 255;
+					qq->leng = *si++;
+					qq->keyon_flag++;
+					qq->address = si;
+					
+					if(--pmdwork.volpush_flag) {
+						qq->volpush = 0;
+					}
+					
+					pmdwork.tieflag = 0;
+					pmdwork.volpush_flag = 0;
+					break;
+				}
+			}
+		}
+	}
+	
+	if(qq->partmask == 0) {
+	
+		// LFO & Portament & Fadeout 処理 をして終了
+		if(qq->hldelay_c) {
+			if(--qq->hldelay_c == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel +
+						(pmdwork.partb - 1 + 0xb4), qq->fmpan);
+			}
+		}
+		
+		if(qq->sdelay_c) {
+			if(--qq->sdelay_c == 0) {
+				if((qq->keyoff_flag & 1) == 0) {	// 既にkeyoffしたか?
+					keyon(qq);
+				}
+			}
+		}
+		
+		if(qq->lfoswi) {
+			pmdwork.lfo_switch = qq->lfoswi & 8;
+			if(qq->lfoswi & 3) {
+				if(lfo(qq)) {
+					pmdwork.lfo_switch  |= (qq->lfoswi & 3);
+				}
+			}
+			
+			if(qq->lfoswi & 0x30) {
+				lfo_change(qq);
+				if(lfo(qq)) {
+					lfo_change(qq);
+					pmdwork.lfo_switch |= (qq->lfoswi & 0x30);
+				} else {
+					lfo_change(qq);
+				}
+			}
+			
+			if(pmdwork.lfo_switch & 0x19) {
+				if(pmdwork.lfo_switch & 8) {
+					porta_calc(qq);
+					
+				}
+				otodasi(qq);
+			}
+			
+			if(pmdwork.lfo_switch & 0x22) {
+				volset(qq);
+				pmdwork.loop_work &= qq->loopcheck;
+				return;
+			}
+		}
+	}
+	
+	pmdwork.loop_work &= qq->loopcheck;
+}
+
+
+//=============================================================================
+//	KEY OFF
+//=============================================================================
+void PMDWIN::keyoff(QQ *qq)
+{
+	if(qq->onkai == 255) return;
+	kof1(qq);
+}
+
+
+void PMDWIN::kof1(QQ *qq)
+{
+	if(pmdwork.fmsel == 0) {
+		pmdwork.omote_key[pmdwork.partb-1]
+				= (~qq->slotmask) & (pmdwork.omote_key[pmdwork.partb-1]);
+		OPNASetReg(&opna, 0x28, (pmdwork.partb-1) | pmdwork.omote_key[pmdwork.partb-1]);
+	} else {
+		pmdwork.ura_key[pmdwork.partb-1]
+				= (~qq->slotmask) & (pmdwork.ura_key[pmdwork.partb-1]);
+		OPNASetReg(&opna, 0x28, ((pmdwork.partb-1) | pmdwork.ura_key[pmdwork.partb-1]) | 4);
+	}
+}
+
+
+//=============================================================================
+//	FM KEYON
+//=============================================================================
+void PMDWIN::keyon(QQ *qq)
+{
+    int al;
+	
+	if(qq->onkai == 255) return;		// キュウフ ノ トキ
+    uint32_t op = ((pmdwork.partb-1)&7)|(!!pmdwork.fmsel << 2);
+    switch(op) {
+        case 0:
+            pmdstatus[0] = (qq->onkai & 0x0f);
+            break;
+        case 1:
+            pmdstatus[1] = (qq->onkai & 0x0f);
+            break;
+        case 2:
+            pmdstatus[2] = (qq->onkai & 0x0f);
+            break;
+        case 4:
+            pmdstatus[3] = (qq->onkai & 0x0f);
+            break;
+        case 5:
+            pmdstatus[4] = (qq->onkai & 0x0f);
+            break;
+        case 6:
+            pmdstatus[5] = (qq->onkai & 0x0f);
+            break;
+        default:
+            break;
+    }
+	
+	if(pmdwork.fmsel == 0) {
+		al = pmdwork.omote_key[pmdwork.partb-1] | qq->slotmask;
+		if(qq->sdelay_c) {
+			al &= qq->sdelay_m;
+		}
+		
+		pmdwork.omote_key[pmdwork.partb-1] = al;
+		OPNASetReg(&opna, 0x28, (pmdwork.partb-1) | al);
+	} else {
+		al = pmdwork.ura_key[pmdwork.partb-1] | qq->slotmask;
+		if(qq->sdelay_c) {
+			al &= qq->sdelay_m;
+		}
+		
+		pmdwork.ura_key[pmdwork.partb-1] = al;
+		OPNASetReg(&opna, 0x28, ((pmdwork.partb-1) | al) | 4);
+	}
+}
+
+
+//=============================================================================
+//	Set [ FNUM/BLOCK + DETUNE + LFO ]
+//=============================================================================
+void PMDWIN::otodasi(QQ *qq)
+{
+	int		ax, cx;
+	
+	if(qq->fnum == 0) return;
+	if(qq->slotmask == 0) return;
+	
+	cx = (qq->fnum & 0x3800);		// cx=BLOCK
+	ax = (qq->fnum) & 0x7ff;		// ax=FNUM
+
+	// Portament/LFO/Detune SET
+	ax += qq->porta_num + qq->detune;
+	
+	if(pmdwork.partb == 3 && pmdwork.fmsel == 0 && open_work.ch3mode != 0x3f) {
+		ch3_special(qq, ax, cx);
+	} else {
+		if(qq->lfoswi & 1) {
+			ax += qq->lfodat;
+		}
+		
+		if(qq->lfoswi & 0x10) {
+			ax += qq->_lfodat;
+		}
+		
+		fm_block_calc(&cx, &ax);
+		
+		// SET BLOCK/FNUM TO OPN
+		
+		ax |= cx;
+		
+		OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xa4-1, ax >> 8);
+		OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xa4-5, ax & 0xff);
+	}
+}
+
+
+//=============================================================================
+//	FM音源のdetuneでオクターブが変わる時の修正
+//		input	CX:block / AX:fnum+detune
+//		output	CX:block / AX:fnum
+//=============================================================================
+void PMDWIN::fm_block_calc(int *cx, int *ax)
+{
+	while(*ax >= 0x26a) {
+		if(*ax < (0x26a*2)) return;
+		
+		*cx += 0x800;			// oct.up
+		if(*cx != 0x4000) {
+			*ax -= 0x26a;		// 4d2h-26ah
+		} else {				// モウ コレイジョウ アガンナイヨン
+			*cx = 0x3800;
+			if(*ax >= 0x800) 
+				*ax = 0x7ff;	// 4d2h
+			return;
+		}
+	}
+	
+	while(*ax < 0x26a) {
+		*cx -= 0x800;			// oct.down
+		if(*cx >= 0) {
+			*ax += 0x26a;		// 4d2h-26ah
+		} else {				// モウ コレイジョウ サガンナイヨン
+			*cx = 0;
+			if(*ax < 8) {
+				*ax = 8;
+			}
+			return;
+		}
+	}
+}
+
+
+//=============================================================================
+//	パートを判別してch3ならmode設定
+//=============================================================================
+int PMDWIN::ch3_setting(QQ *qq)
+{
+	if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+		ch3mode_set(qq);
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+
+void PMDWIN::cm_clear(int *ah, int *al)
+{
+	*al ^= 0xff;
+	if((pmdwork.slot3_flag &= *al) == 0) {
+		if(pmdwork.slotdetune_flag != 1) {
+			*ah = 0x3f;
+		} else {
+			*ah = 0x7f;
+		}
+	} else {
+		*ah = 0x7f;
+	}
+}
+
+
+//=============================================================================
+//	FM3のmodeを設定する
+//=============================================================================
+void PMDWIN::ch3mode_set(QQ *qq)
+{
+	int		ah, al;
+	
+	if(qq == &FMPart[3-1]) {
+		al = 1;
+	} else {
+		al = 8;
+	}
+	
+	if((qq->slotmask & 0xf0) == 0) {	// s0
+		cm_clear(&ah, &al);
+	} else if(qq->slotmask != 0xf0) {
+		pmdwork.slot3_flag |= al;
+		ah = 0x7f;
+	} else if((qq->volmask & 0x0f) == 0) {
+		cm_clear(&ah, &al);
+	} else if((qq->lfoswi & 1) != 0) {
+		pmdwork.slot3_flag |= al;
+		ah = 0x7f;
+	} else if((qq->_volmask & 0x0f) == 0) {
+		cm_clear(&ah, &al);
+	} else if(qq->lfoswi & 0x10) {
+		pmdwork.slot3_flag |= al;
+		ah = 0x7f;
+	} else {
+		cm_clear(&ah, &al);
+	}
+	
+	if(ah == open_work.ch3mode) return;		// 以前と変更無しなら何もしない
+	open_work.ch3mode = ah;
+	OPNASetReg(&opna, 0x27, ah & 0xcf);			// Resetはしない
+	
+	//	効果音モードに移った場合はそれ以前のFM3パートで音程書き換え
+	if(ah == 0x3f || qq == &FMPart[2]) return;
+	if(FMPart[2].partmask == 0) otodasi(&FMPart[2]);
+}
+
+
+//=============================================================================
+//	ch3=効果音モード を使用する場合の音程設定
+//			input CX:block AX:fnum
+//=============================================================================
+void PMDWIN::ch3_special(QQ *qq, int ax, int cx)
+{
+	int		ax_, bh, ch, si;
+	int		shiftmask = 0x80;
+	
+	si = cx;
+	
+	if((qq->volmask & 0x0f) == 0) {
+		bh = 0xf0;			// all
+	} else {
+		bh = qq->volmask;	// bh=lfo1 mask 4321xxxx
+	}
+	
+	if((qq->_volmask & 0x0f) == 0) {
+		ch = 0xf0;			// all
+	} else {
+		ch = qq->_volmask;	// ch=lfo2 mask 4321xxxx
+	}
+	
+	//	slot	4
+	if(qq->slotmask & 0x80) {
+		ax_ = ax;
+		ax += open_work.slot_detune4;
+		if((bh & shiftmask) && (qq->lfoswi & 0x01))	ax += qq->lfodat;
+		if((ch & shiftmask) && (qq->lfoswi & 0x10))	ax += qq->_lfodat;
+		shiftmask >>= 1;
+		
+		cx = si;
+		fm_block_calc(&cx, &ax);
+		ax |= cx;
+		
+		OPNASetReg(&opna, 0xa6, ax >> 8);
+		OPNASetReg(&opna, 0xa2, ax & 0xff);
+		
+		ax = ax_;
+	}
+	
+	//	slot	3
+	if(qq->slotmask & 0x40) {
+		ax_ = ax;
+		ax += open_work.slot_detune3;
+		if((bh & shiftmask) && (qq->lfoswi & 0x01))	ax += qq->lfodat;
+		if((ch & shiftmask) && (qq->lfoswi & 0x10))	ax += qq->_lfodat;
+		shiftmask >>= 1;
+		
+		cx = si;
+		fm_block_calc(&cx, &ax);
+		ax |= cx;
+		
+		OPNASetReg(&opna, 0xac, ax >> 8);
+		OPNASetReg(&opna, 0xa8, ax & 0xff);
+		
+		ax = ax_;
+	}
+	
+	//	slot	2
+	if(qq->slotmask & 0x20) {
+		ax_ = ax;
+		ax += open_work.slot_detune2;
+		if((bh & shiftmask) && (qq->lfoswi & 0x01))	ax += qq->lfodat;
+		if((ch & shiftmask) && (qq->lfoswi & 0x10))	ax += qq->_lfodat;
+		shiftmask >>= 1;
+		
+		cx = si;
+		fm_block_calc(&cx, &ax);
+		ax |= cx;
+		
+		OPNASetReg(&opna, 0xae, ax >> 8);
+		OPNASetReg(&opna, 0xaa, ax & 0xff);
+		
+		ax = ax_;
+	}
+	
+	//	slot	1
+	if(qq->slotmask & 0x10) {
+		ax_ = ax;
+		ax += open_work.slot_detune1;
+		if((bh & shiftmask) && (qq->lfoswi & 0x01))	ax += qq->lfodat;
+		if((ch & shiftmask) && (qq->lfoswi & 0x10))	ax += qq->_lfodat;
+		cx = si;
+		fm_block_calc(&cx, &ax);
+		ax |= cx;
+		
+		OPNASetReg(&opna, 0xad, ax >> 8);
+		OPNASetReg(&opna, 0xa9, ax & 0xff);
+		
+		ax = ax_;
+	}
+}
+
+
+//=============================================================================
+//	'p' COMMAND [FM PANNING SET]
+//=============================================================================
+uchar * PMDWIN::panset(QQ *qq, uchar *si)
+{
+	panset_main(qq, *si++);
+	return si;
+}
+
+
+void PMDWIN::panset_main(QQ *qq, int al)
+{
+	qq->fmpan = ((qq->fmpan & 0x3f) | ((al << 6) & 0xc0));
+	
+	if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+		//	FM3の場合は 4つのパート総て設定
+		FMPart[2].fmpan = qq->fmpan;
+	}
+	
+	if(qq->partmask == 0) {		// パートマスクされているか?
+		// dl = al;
+		OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xb4-1,
+													calc_panout(qq));
+	}
+}
+
+
+//=============================================================================
+//	0b4h〜に設定するデータを取得 out.dl
+//=============================================================================
+uchar PMDWIN::calc_panout(QQ *qq)
+{
+	int	dl;
+	
+	dl = qq->fmpan;
+	if(qq->hldelay_c) dl &= 0xc0;	// HLFO Delayが残ってる場合はパンのみ設定
+	return dl;
+}
+
+
+//=============================================================================
+//	Pan setting Extend
+//=============================================================================
+uchar * PMDWIN::panset_ex(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *(char *)si++;
+	si++;		// 逆走flagは読み飛ばす
+	
+	if(al > 0) {
+		al = 2; 
+		panset_main(qq, al);
+	} else if(al == 0) {
+		al = 3;
+		panset_main(qq, al);
+	} else {
+		al = 1;
+		panset_main(qq, al);
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	FM音源用 Entry
+//=============================================================================
+void PMDWIN::lfoinit(QQ *qq, int al)
+{
+	int		ah;
+	
+	ah = al & 0x0f;
+	if(ah == 0x0c) {
+		al = qq->onkai_def;
+		ah = al & 0x0f;
+	}
+
+	qq->onkai_def = al;
+
+	if(ah == 0x0f) {				// キューフ ノ トキ ハ INIT シナイヨ
+		lfo_exit(qq);
+		return;
+	}
+	
+	qq->porta_num = 0;				// ポルタメントは初期化
+	
+	if((pmdwork.tieflag & 1) == 0) {
+		lfin1(qq);
+	} else {
+		lfo_exit(qq);
+	}
+}
+
+
+//=============================================================================
+//	FM BLOCK,F-NUMBER SET
+//		INPUTS	-- AL [KEY#,0-7F]
+//=============================================================================
+void PMDWIN::fnumset(QQ *qq, int al)
+{
+	int		ax,bx;
+	
+	if((al & 0x0f) != 0x0f) {		// 音符の場合
+		qq->onkai = al;
+		
+		// BLOCK/FNUM CALICULATE
+		bx = al & 0x0f;		// bx=onkai
+		ax = fnum_data[bx];
+		
+		// BLOCK SET
+		ax |= (((al >> 1) & 0x38) << 8);
+		qq->fnum = ax;
+	} else {						// 休符の場合
+		qq->onkai = 255;
+		if((qq->lfoswi & 0x11) == 0) {
+			qq->fnum = 0;			// 音程LFO未使用
+		}
+	}
+}
+
+
+//=============================================================================
+//	FM音量設定メイン
+//=============================================================================
+void PMDWIN::volset(QQ *qq)
+{
+	int bh, bl, cl, dh;
+	uchar vol_tbl[4] = {0, 0, 0, 0};
+	
+	if(qq->slotmask == 0) return;
+	if(qq->volpush) {
+		cl = qq->volpush - 1;
+	} else {
+		cl = qq->volume;
+	}
+	
+	//--------------------------------------------------------------------
+	//	音量down計算
+	//--------------------------------------------------------------------
+	if(open_work.fm_voldown) {
+		cl = ((256-open_work.fm_voldown) * cl) >> 8;
+	}
+		
+	//------------------------------------------------------------------------
+	//	音量をcarrierに設定 & 音量LFO処理
+	//		input	cl to Volume[0-127]
+	//			bl to SlotMask
+	//------------------------------------------------------------------------
+	
+	bh=0;					// Vol Slot Mask
+	bl=qq->slotmask;		// ch=SlotMask Push
+
+	vol_tbl[0] = 0x80;
+	vol_tbl[1] = 0x80;
+	vol_tbl[2] = 0x80;
+	vol_tbl[3] = 0x80;
+	
+	cl = 255-cl;			// cl=carrierに設定する音量+80H(add)
+	bl &= qq->carrier;		// bl=音量を設定するSLOT xxxx0000b
+	bh |= bl;
+	
+	if(bl & 0x80) vol_tbl[0] = cl;
+	if(bl & 0x40) vol_tbl[1] = cl;
+	if(bl & 0x20) vol_tbl[2] = cl;
+	if(bl & 0x10) vol_tbl[3] = cl;
+	
+	if(cl != 255) {
+		if(qq->lfoswi & 2) {
+			bl = qq->volmask;
+			bl &= qq->slotmask;		// bl=音量LFOを設定するSLOT xxxx0000b
+			bh |= bl;
+			fmlfo_sub(qq, qq->lfodat, bl, vol_tbl);
+		}
+		
+		if(qq->lfoswi & 0x20) {
+			bl = qq->_volmask;
+			bl &= qq->slotmask;
+			bh |= bl;
+			fmlfo_sub(qq, qq->_lfodat, bl, vol_tbl);
+		}
+	}
+	
+	dh = 0x4c - 1 + pmdwork.partb;		// dh=FM Port Address
+	
+	if(bh & 0x80) volset_slot(dh,    qq->slot4, vol_tbl[0]);
+	if(bh & 0x40) volset_slot(dh-8,  qq->slot3, vol_tbl[1]);
+	if(bh & 0x20) volset_slot(dh-4,  qq->slot2, vol_tbl[2]);
+	if(bh & 0x10) volset_slot(dh-12, qq->slot1, vol_tbl[3]);
+}
+
+
+//-----------------------------------------------------------------------------
+//	スロット毎の計算 & 出力 マクロ
+//			in.	dl	元のTL値
+//				dh	Outするレジスタ
+//				al	音量変動値 中心=80h
+//-----------------------------------------------------------------------------
+void PMDWIN::volset_slot(int dh, int dl, int al)
+{
+	if((al += dl) > 255) al = 255;
+	if((al -= 0x80) < 0) al = 0;
+	OPNASetReg(&opna, pmdwork.fmsel + dh, al);
+}
+
+
+//-----------------------------------------------------------------------------
+//	音量LFO用サブ
+//-----------------------------------------------------------------------------
+void PMDWIN::fmlfo_sub(QQ *qq, int al, int bl, uchar *vol_tbl)
+{
+	if(bl & 0x80) vol_tbl[0] = Limit(vol_tbl[0] - al, 255, 0);
+	if(bl & 0x40) vol_tbl[1] = Limit(vol_tbl[1] - al, 255, 0);
+	if(bl & 0x20) vol_tbl[2] = Limit(vol_tbl[2] - al, 255, 0);
+	if(bl & 0x10) vol_tbl[3] = Limit(vol_tbl[3] - al, 255, 0);
+}
+
+
+//=============================================================================
+//	SSG音源 演奏 メイン
+//=============================================================================
+void PMDWIN::psgmain(QQ *qq)
+{
+	uchar	*si;
+    int temp;
+
+	if(qq->address == NULL) return;
+	si = qq->address;
+	
+	qq->leng--;
+
+	// KEYOFF CHECK & Keyoff
+	if(qq->partmask) {
+		qq->keyoff_flag = -1;
+	} else {
+		if((qq->keyoff_flag & 3) == 0) {		// 既にkeyoffしたか?
+			if(qq->leng <= qq->qdat) {
+				keyoffp(qq);
+				qq->keyoff_flag = -1;
+			}
+		}
+	}
+	
+	// LENGTH CHECK
+	if(qq->leng == 0) {
+		qq->lfoswi &= 0xf7;
+		
+		// DATA READ
+		while(1) {
+			if(*si == 0xda && ssgdrum_check(qq, *si) < 0) {
+				si++;
+			} else if(*si > 0x80 && *si != 0xda) {
+				si = commandsp(qq, si);
+			} else if(*si == 0x80) {
+				qq->address = si;
+				qq->loopcheck = 3;
+				qq->onkai = 255;
+				if(qq->partloop == NULL) {
+					if(qq->partmask) {
+						pmdwork.tieflag = 0;
+						pmdwork.volpush_flag = 0;
+						pmdwork.loop_work &= qq->loopcheck;
+						return;
+					} else {
+						break;
+					}
+				}
+				// "L"があった時
+				si = qq->partloop;
+				qq->loopcheck = 1;
+			} else {
+				if(*si == 0xda) {						// ポルタメント
+					si = portap(qq, ++si);
+					pmdwork.loop_work &= qq->loopcheck;
+					return;
+				} else if(qq->partmask) {
+					if(ssgdrum_check(qq, *si) == 0) {
+						si++;
+						qq->fnum = 0;		//休符に設定
+						qq->onkai = 255;
+						qq->leng = *si++;
+						qq->keyon_flag++;
+						qq->address = si;
+						
+						if(--pmdwork.volpush_flag) {
+							qq->volpush = 0;
+						}
+						
+						pmdwork.tieflag = 0;
+						pmdwork.volpush_flag = 0;
+						break;
+					}
+				}
+				
+				//  TONE SET
+				lfoinitp(qq, *si);
+				fnumsetp(qq, oshiftp(qq, *si++));
+				
+				qq->leng = *si++;
+				si = calc_q(qq, si);
+				
+				if(qq->volpush && qq->onkai != 255) {
+					if(--pmdwork.volpush_flag) {
+						pmdwork.volpush_flag = 0;
+						qq->volpush = 0;
+					}
+				}
+				
+				volsetp(qq);
+				otodasip(qq);
+				keyonp(qq);
+				
+				qq->keyon_flag++;
+				qq->address = si;
+				
+				pmdwork.tieflag = 0;
+				pmdwork.volpush_flag = 0;
+				qq->keyoff_flag = 0;
+				
+				if(*si == 0xfb) {		// '&'が直後にあったらkeyoffしない
+					qq->keyoff_flag = 2;
+				}
+				pmdwork.loop_work &= qq->loopcheck;
+				return;
+			}
+		}
+	}
+	
+	pmdwork.lfo_switch = (qq->lfoswi & 8);
+
+	if(qq->lfoswi) {
+		if(qq->lfoswi & 3) {
+			if(lfo(qq)) {
+				pmdwork.lfo_switch |= (qq->lfoswi & 3);
+			}
+		}
+
+		if(qq->lfoswi & 0x30) {
+			lfo_change(qq);
+			if(lfo(qq)) {
+				lfo_change(qq);
+				pmdwork.lfo_switch |= (qq->lfoswi & 0x30);
+			} else {
+				lfo_change(qq);
+			}
+		}
+		
+		if(pmdwork.lfo_switch & 0x19) {
+			if(pmdwork.lfo_switch & 0x08) {
+				porta_calc(qq);
+			}
+
+			// SSG 3ch で休符かつ SSG Drum 発音中は操作しない
+			if(!(qq == &SSGPart[2] && qq->onkai == 255 && open_work.kshot_dat)) {
+				otodasip(qq);
+			}
+		}
+	}
+
+    temp = soft_env(qq);
+	if(temp || pmdwork.lfo_switch & 0x22) {
+		// SSG 3ch で休符かつ SSG Drum 発音中は volume set しない
+		if(!(qq == &SSGPart[2] && qq->onkai == 255 && open_work.kshot_dat)) {
+			volsetp(qq);
+		}
+	}
+	
+	pmdwork.loop_work &= qq->loopcheck;
+}
+
+
+void PMDWIN::keyoffp(QQ *qq)
+{
+	if(qq->onkai == 255) return;		// キュウフ ノ トキ
+	if(qq->envf != -1) {
+		qq->envf = 2;
+	} else {
+		qq->eenv_count = 4;
+	}
+}
+
+
+//=============================================================================
+//	リズムパート 演奏 メイン
+//=============================================================================
+void PMDWIN::rhythmmain(QQ *qq)
+{
+	uchar	*si, *bx;
+	int		al, result;
+	
+	if(qq->address == NULL) return;
+	
+	si = qq->address;
+	
+	if(--qq->leng == 0) {
+		bx = open_work.rhyadr;
+		
+		rhyms00:
+		do {
+			result = 1;
+			al = *bx++;
+			if(al != 0xff) {
+				if(al & 0x80) {
+					bx = rhythmon(qq, bx, al, &result);
+					if(result == 0) continue;
+				} else {
+					open_work.kshot_dat = 0;	//rest
+				}
+				
+				al = *bx++;
+				open_work.rhyadr = bx;
+				qq->leng = al;
+				qq->keyon_flag++;
+				pmdwork.tieflag = 0;
+				pmdwork.volpush_flag = 0;
+				pmdwork.loop_work &= qq->loopcheck;
+				return;
+			}
+		}while(result == 0);
+		
+		while(1) {
+			while((al = *si++) != 0x80) {
+				if(al < 0x80) {
+					qq->address = si;
+
+//					bx = (ushort *)&open_work.radtbl[al];
+//					bx = open_work.rhyadr = &open_work.mmlbuf[*bx];
+					bx = open_work.rhyadr = &open_work.mmlbuf[open_work.radtbl[al]];
+					goto rhyms00;
+				}
+				
+				// al > 0x80
+				si = commandsr(qq, si-1);
+			}
+			
+			qq->address = --si;
+			qq->loopcheck = 3;
+			bx = qq->partloop;
+			if(bx == NULL) {
+				open_work.rhyadr = (uchar *)&pmdwork.rhydmy;
+				pmdwork.tieflag = 0;
+				pmdwork.volpush_flag = 0;
+				pmdwork.loop_work &= qq->loopcheck;
+				return;
+			} else {		//  "L"があった時
+				si = bx;
+				qq->loopcheck = 1;
+			}
+		}
+	}
+	
+	pmdwork.loop_work &= qq->loopcheck;
+}
+
+
+//=============================================================================
+//	PSGリズム ON
+//=============================================================================
+uchar * PMDWIN::rhythmon(QQ *qq, uchar *bx, int al, int *result)
+{
+	int		cl, dl, bx_;
+	
+	if(al & 0x40) {
+		bx = commandsr(qq, bx-1);
+		*result = 0;
+		return bx;
+	}
+	
+	*result = 1;
+	
+	if(qq->partmask) {		// maskされている場合
+		open_work.kshot_dat = 0;
+		return ++bx;
+	}
+	
+	al = ((al << 8) + *bx++) & 0x3fff;
+	open_work.kshot_dat = al;
+	if(al == 0) return bx;
+	open_work.rhyadr = bx;
+	
+	for(cl = 0; cl < 11; cl++) {
+		if(al & (1 << cl)) {
+			OPNASetReg(&opna, rhydat[cl][0], rhydat[cl][1]);
+			if((dl = (rhydat[cl][2] & open_work.rhythmmask))) {
+				if(dl < 0x80) {
+					OPNASetReg(&opna, 0x10, dl);
+				} else {
+					OPNASetReg(&opna, 0x10, 0x84);
+					dl = open_work.rhythmmask & 8;
+					if(dl) {
+						OPNASetReg(&opna, 0x10, dl);
+					}
+				}
+			}
+		}
+	}
+	
+	bx_ = al;
+	al = 0;
+	do {
+		while((bx_ & 1) == 0) {
+			bx_ >>= 1;
+			al++;
+		}
+		eff_main(al);
+		bx_ >>= 1;
+	}while(pmdwork.ppsdrv_flag && bx_);	// PPSDRVなら2音目以上も鳴らしてみる
+	
+	return open_work.rhyadr;
+}
+
+
+//=============================================================================
+//	PSG ドラムス&効果音 ルーチン
+//	From WT298
+//
+//	AL に 効果音No.を入れて CALLする
+//	ppsdrvがあるならそっちを鳴らす
+//=============================================================================
+void PMDWIN::eff_main(int al)
+{
+    int priority;
+    pmdstatus[8] = al;
+	effwork.psgefcnum = al;
+	if (al == 7 || al == 8) return; // PSG hihat sounds godawful, don't play it at all
+    priority = ((al > 10) ? 2 : 1);
+	if(effwork.effon <= priority) {
+		SSGPart[2].partmask |= 2;		// Part Mask
+		efffor(efftbl[al]);		        // 1発目を発音
+		effwork.effon = priority; 
+												//	優先順位を設定(発音開始)
+	}
+}
+
+
+//=============================================================================
+//	こーかおん えんそう めいん
+// 	From VRTC
+//=============================================================================
+void PMDWIN::effplay(void)
+{
+	if(--effwork.effcnt) {
+		effsweep();			// 新しくセットされない
+	} else {
+		efffor(effwork.effadr);
+	}
+}
+
+
+void PMDWIN::efffor(const int *si)
+{
+	int		al, ch, cl;
+	
+	al = *si++;
+	if(al == -1) {
+		effend();
+	} else {
+		effwork.effcnt = al;		// カウント数
+		cl = *si;
+		PSGSetReg(&(opna.psg), 4, *si++);		// 周波数セット
+		ch = *si;
+		PSGSetReg(&(opna.psg), 5, *si++);		// 周波数セット
+		effwork.eswthz = (ch << 8) + cl;
+		
+		open_work.psnoi_last = effwork.eswnhz = *si;
+		PSGSetReg(&(opna.psg), 6, *si++);		// ノイズ
+		
+		PSGSetReg(&(opna.psg), 7, ((*si++ << 2) & 0x24) | (PSGGetReg(&(opna.psg), 0x07) & 0xdb));
+		
+		PSGSetReg(&(opna.psg), 10, *si++);		// ボリューム
+		PSGSetReg(&(opna.psg), 11, *si++);		// エンベロープ周波数
+		PSGSetReg(&(opna.psg), 12, *si++);		// 
+		PSGSetReg(&(opna.psg), 13, *si++);		// エンベロープPATTERN
+		
+		effwork.eswtst = *si++;		// スイープ増分 (TONE)
+		effwork.eswnst = *si++;		// スイープ増分 (NOISE)
+		
+		effwork.eswnct = effwork.eswnst & 15;		// スイープカウント (NOISE)
+		
+		effwork.effadr = (int *)si;
+	}
+}
+
+
+void PMDWIN::effend(void)
+{
+	PSGSetReg(&(opna.psg), 0x0a, 0x00);
+	PSGSetReg(&(opna.psg), 0x07,  ((PSGGetReg(&(opna.psg), 0x07)) & 0xdb) | 0x24);
+	effwork.effon = 0;
+	effwork.psgefcnum = -1;
+}
+
+
+// 普段の処理
+void PMDWIN::effsweep(void)
+{
+	int		dl;
+	
+	effwork.eswthz += effwork.eswtst;
+	PSGSetReg(&(opna.psg), 4, effwork.eswthz & 0xff);
+	PSGSetReg(&(opna.psg), 5, effwork.eswthz >> 8);
+	
+	if(effwork.eswnst == 0) return;		// ノイズスイープ無し
+	if(--effwork.eswnct) return;
+	
+	dl = effwork.eswnst;
+	effwork.eswnct = dl & 15;
+	
+	effwork.eswnhz += dl / 16;
+	
+	PSGSetReg(&(opna.psg), 6, effwork.eswnhz);
+	open_work.psnoi_last = effwork.eswnhz;
+}
+
+
+//=============================================================================
+//	SSGドラムを消してSSGを復活させるかどうかcheck
+//		input	AL <- Command
+//		output	cy=1 : 復活させる
+//=============================================================================
+int PMDWIN::ssgdrum_check(QQ *qq, int al)
+{
+	// SSGマスク中はドラムを止めない
+	// SSGドラムは鳴ってない
+	if((qq->partmask & 1) || ((qq->partmask & 2) == 0)) return 0;
+	
+	// 普通の効果音は消さない
+	if(effwork.effon >= 2) return 0;
+	
+	al = (al & 0x0f);
+	
+	// 休符の時はドラムは止めない
+	if(al == 0x0f) return 0;
+	
+	// SSGドラムはまだ再生中か?
+	if(effwork.effon == 1) {
+		effend();			// SSGドラムを消す
+	}
+	
+	if((qq->partmask &= 0xfd) == 0) return -1;
+	return 0;
+}
+
+
+//=============================================================================
+//	各種特殊コマンド処理
+//=============================================================================
+uchar * PMDWIN::commands(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	switch(al) {
+		case 0xff : si = comat(qq, si); break;
+		case 0xfe : qq->qdata = *si++; qq->qdat3 = 0; break;
+		case 0xfd : qq->volume = *si++; break;
+		case 0xfc : si = comt(si); break;
+		
+		case 0xfb : pmdwork.tieflag |= 1; break;
+		case 0xfa : qq->detune = *(short *)si; si+=2; break;
+		case 0xf9 : si = comstloop(qq, si); break;
+		case 0xf8 : si = comedloop(qq, si); break;
+		case 0xf7 : si = comexloop(qq, si); break;
+		case 0xf6 : qq->partloop = si; break;
+		case 0xf5 : qq->shift = *(char *)si++; break;
+		case 0xf4 : if((qq->volume+=4) > 127) qq->volume = 127; break;
+		case 0xf3 : if(qq->volume < 4) qq->volume=0; else qq->volume-=4; break;
+		case 0xf2 : si = lfoset(qq, si); break;
+		case 0xf1 : si = lfoswitch(qq, si); ch3_setting(qq); break;
+		case 0xf0 : si+=4; break;
+		
+		case 0xef : OPNASetReg(&opna, pmdwork.fmsel + *si, *(si+1)); si+=2; break;
+		case 0xee : si++; break;
+		case 0xed : si++; break;
+		case 0xec : si = panset(qq, si); break;				// FOR SB2
+		case 0xeb : si = rhykey(si); break;
+		case 0xea : si = rhyvs(si); break;
+		case 0xe9 : si = rpnset(si); break;
+		case 0xe8 : si = rmsvs(si); break;
+		//
+		case 0xe7 : qq->shift += *(char *)si++; break;
+		case 0xe6 : si = rmsvs_sft(si); break;
+		case 0xe5 : si = rhyvs_sft(si); break;
+		//
+		case 0xe4 : qq->hldelay = *si++; break;
+		//追加 for V2.3
+		case 0xe3 : if((qq->volume += *si++) > 127) qq->volume = 127; break;
+		case 0xe2 : 
+			if(qq->volume < *si) qq->volume = 0; else qq->volume -= *si;
+			si++;
+			break;
+		//
+		case 0xe1 : si = hlfo_set(qq, si); break;
+		case 0xe0 : open_work.port22h = *si; OPNASetReg(&opna, 0x22, *si++); break;
+		//
+		case 0xdf : open_work.syousetu_lng = *si++; break;
+		//
+		case 0xde : si = vol_one_up_fm(qq, si); break;
+		case 0xdd : si = vol_one_down(qq, si); break;
+		//
+		case 0xdc : open_work.status = *si++; break;
+		case 0xdb : open_work.status += *si++; break;
+		//
+		case 0xda : si = porta(qq, si); break;
+		//
+		case 0xd9 : si++; break;
+		case 0xd8 : si++; break;
+		case 0xd7 : si++; break;
+		//
+		case 0xd6 : qq->mdspd = qq->mdspd2 = *si++; qq->mdepth = *(char *)si++; break;
+		case 0xd5 : qq->detune += *(short *)si; si+=2; break;
+		//
+		case 0xd4 : si++; break;
+		case 0xd3 : si++; break;
+		case 0xd2 :
+            si++;
+			break;
+		//
+		case 0xd1 : si++; break;
+		case 0xd0 : si++; break;
+		//
+		case 0xcf : si = slotmask_set(qq, si); break;
+		case 0xce : si+=6; break;
+		case 0xcd : si+=5; break;
+		case 0xcc : si++; break;
+		case 0xcb : qq->lfo_wave = *si++; break;
+		case 0xca :
+			qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1);
+			break;
+		
+		case 0xc9 : si++; break;
+		case 0xc8 : si = slotdetune_set(qq, si); break;
+		case 0xc7 : si = slotdetune_set2(qq, si); break;
+		case 0xc6 : break; 
+		case 0xc5 : si = volmask_set(qq, si); break;
+		case 0xc4 : qq->qdatb = *si++; break;
+		case 0xc3 : si = panset_ex(qq, si); break;
+		case 0xc2 : qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break;
+		case 0xc1 : break;
+		case 0xc0 : si = fm_mml_part_mask(qq, si); break;
+		case 0xbf : lfo_change(qq); si = lfoset(qq, si); lfo_change(qq);break;
+		case 0xbe : si = _lfoswitch(qq, si); ch3_setting(qq); break;
+		case 0xbd :
+			lfo_change(qq);
+			qq->mdspd = qq->mdspd2 = *si++;
+			qq->mdepth = *(char *)si++;
+			lfo_change(qq);
+			break;
+		
+		case 0xbc : lfo_change(qq); qq->lfo_wave=*si++; lfo_change(qq); break;
+		case 0xbb :
+			lfo_change(qq);
+			qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1);
+			lfo_change(qq);
+			break;
+		
+		case 0xba : si = _volmask_set(qq, si); break;
+		case 0xb9 :
+			lfo_change(qq);
+			qq->delay = qq->delay2 = *si++; lfoinit_main(qq);
+			lfo_change(qq);
+			break;
+			
+		case 0xb8 : si = tl_set(qq, si); break;
+		case 0xb7 : si = mdepth_count(qq, si); break;
+		case 0xb6 : si = fb_set(qq, si); break;
+		case 0xb5 : 
+			qq->sdelay_m = (~(*si++) << 4) & 0xf0;
+			qq->sdelay_c = qq->sdelay = *si++;
+			break;
+		
+		case 0xb4 : si+=16; break;
+		case 0xb3 : qq->qdat2 = *si++; break;
+		case 0xb2 : qq->shift_def = *(char *)si++; break;
+		case 0xb1 : qq->qdat3 = *si++; break;
+		
+		default : 
+			si--;
+			*si = 0x80;
+	}
+	
+	return si;
+}
+
+
+uchar * PMDWIN::commandsp(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	switch(al) {
+		case 0xff : si++; break;
+		case 0xfe : qq->qdata = *si++; qq->qdat3 = 0; break;
+		case 0xfd : qq->volume = *si++; break;
+		case 0xfc : si = comt(si); break;
+		
+		case 0xfb : pmdwork.tieflag |= 1; break;
+		case 0xfa : qq->detune = *(short *)si; si+=2; break;
+		case 0xf9 : si = comstloop(qq, si); break;
+		case 0xf8 : si = comedloop(qq, si); break;
+		case 0xf7 : si = comexloop(qq, si); break;
+		case 0xf6 : qq->partloop = si; break;
+		case 0xf5 : qq->shift = *(char *)si++; break;
+		case 0xf4 : if(qq->volume < 15) qq->volume++; break;
+		case 0xf3 : if(qq->volume > 0) qq->volume--; break;
+		case 0xf2 : si = lfoset(qq, si); break;
+		case 0xf1 : si = lfoswitch(qq, si); break;
+		case 0xf0 : si = psgenvset(qq, si); break;
+		
+		case 0xef : OPNASetReg(&opna, *si, *(si+1)); si+=2; break;
+		case 0xee : open_work.psnoi = *si++; break;
+		case 0xed : qq->psgpat = *si++; break;
+		//
+		case 0xec : si++; break;
+		case 0xeb : si = rhykey(si); break;
+		case 0xea : si = rhyvs(si); break;
+		case 0xe9 : si = rpnset(si); break;
+		case 0xe8 : si = rmsvs(si); break;
+		//
+		case 0xe7 : qq->shift += *(char *)si++; break;
+		case 0xe6 : si = rmsvs_sft(si); break;
+		case 0xe5 : si = rhyvs_sft(si); break;
+		//
+		case 0xe4 : si++; break;
+		//追加 for V2.3
+		case 0xe3 : if((qq->volume + *si) < 16) qq->volume += *si; si++;break;
+		case 0xe2 : if((qq->volume - *si) >= 0) qq->volume -= *si; si++;break;
+		//
+		case 0xe1 : si++; break;
+		case 0xe0 : si++; break;
+		//
+		case 0xdf : open_work.syousetu_lng = *si++; break;
+		//
+		case 0xde : si = vol_one_up_psg(qq, si); break;
+		case 0xdd : si = vol_one_down(qq, si); break;
+		//
+		case 0xdc : open_work.status = *si++; break;
+		case 0xdb : open_work.status += *si++; break;
+		//
+		case 0xda : si = portap(qq, si); break;
+		//
+		case 0xd9 : si++; break;
+		case 0xd8 : si++; break;
+		case 0xd7 : si++; break;
+		//
+		case 0xd6 : qq->mdspd = qq->mdspd2 = *si++; qq->mdepth = *(char *)si++; break;
+		case 0xd5 : qq->detune += *(short *)si; si+=2; break;
+		//
+		case 0xd4 : si++; break;
+		case 0xd3 : si++; break;
+		case 0xd2 :
+            si++;
+			break;
+		//
+		case 0xd1 : si++; break;
+		case 0xd0 : si = psgnoise_move(si); break;
+		//
+		case 0xcf : si++; break;
+		case 0xce : si+=6; break;
+		case 0xcd : si = extend_psgenvset(qq, si); break;
+		case 0xcc :
+			qq->extendmode = (qq->extendmode & 0xfe) | (*si++ & 1);
+			break;
+		
+		case 0xcb : qq->lfo_wave = *si++; break;
+		case 0xca :
+			qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1);
+			break;
+		
+		case 0xc9 :
+			qq->extendmode = (qq->extendmode & 0xfb) | ((*si++ & 1) << 2);
+			break;
+		
+		case 0xc8 : si+=3; break;
+		case 0xc7 : si+=3; break;
+		case 0xc6 : si+=6; break;
+		case 0xc5 : si++; break;
+		case 0xc4 : qq->qdatb = *si++; break;
+		case 0xc3 : si+=2; break;
+		case 0xc2 : qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break;
+		case 0xc1 : break;
+		case 0xc0 : si = ssg_mml_part_mask(qq, si); break;
+		case 0xbf : lfo_change(qq); si = lfoset(qq, si); lfo_change(qq);break;
+		case 0xbe : 
+			qq->lfoswi = (qq->lfoswi & 0x8f) | ((*si++ & 7) << 4);
+			lfo_change(qq); lfoinit_main(qq); lfo_change(qq);
+			break;
+		
+		case 0xbd :
+			lfo_change(qq);
+			qq->mdspd = qq->mdspd2 = *si++;
+			qq->mdepth = *(char *)si++;
+			lfo_change(qq);
+			break;
+		
+		case 0xbc : lfo_change(qq); qq->lfo_wave=*si++; lfo_change(qq); break;
+		case 0xbb :
+			lfo_change(qq);
+			qq->extendmode = (qq->extendmode & 0xfd) | ((*si++ & 1) << 1);
+			lfo_change(qq);
+			break;
+		
+		case 0xba : si++; break;
+		case 0xb9 :
+			lfo_change(qq);
+			qq->delay = qq->delay2 = *si++; lfoinit_main(qq); break;
+			lfo_change(qq);
+			break;
+			
+		case 0xb8 : si+=2; break;
+		case 0xb7 : si = mdepth_count(qq, si); break;
+		case 0xb6 : si++; break;
+		case 0xb5 : si+= 2; break;
+		case 0xb4 : si+=16; break;
+		case 0xb3 : qq->qdat2 = *si++; break;
+		case 0xb2 : qq->shift_def = *(char *)si++; break;
+		case 0xb1 : qq->qdat3 = *si++; break;
+	
+		default : 
+			si--;
+			*si = 0x80;
+	}
+	
+	return si;
+}
+
+
+uchar * PMDWIN::commandsr(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	switch(al) {
+		case 0xff : si++; break;
+		case 0xfe : si++; break;
+		case 0xfd : qq->volume = *si++; break;
+		case 0xfc : si = comt(si); break;
+		
+		case 0xfb : pmdwork.tieflag |= 1; break;
+		case 0xfa : qq->detune = *(short *)si; si+=2; break;
+		case 0xf9 : si = comstloop(qq, si); break;
+		case 0xf8 : si = comedloop(qq, si); break;
+		case 0xf7 : si = comexloop(qq, si); break;
+		case 0xf6 : qq->partloop = si; break;
+		case 0xf5 : si++; break;
+		case 0xf4 : if(qq->volume < 15) qq->volume++; break;
+		case 0xf3 : if(qq->volume > 0) qq->volume--; break;
+		case 0xf2 : si+=4; break;
+		case 0xf1 : si++; break;
+		case 0xf0 : si+=4; break;
+		
+		case 0xef : OPNASetReg(&opna, *si, *(si+1)); si+=2; break;
+		case 0xee : si++; break;
+		case 0xed : si++; break;
+		//
+		case 0xec : si++; break;
+		case 0xeb : si = rhykey(si); break;
+		case 0xea : si = rhyvs(si); break;
+		case 0xe9 : si = rpnset(si); break;
+		case 0xe8 : si = rmsvs(si); break;
+		//
+		case 0xe7 : si++; break;
+		case 0xe6 : si = rmsvs_sft(si); break;
+		case 0xe5 : si = rhyvs_sft(si); break;
+		//
+		case 0xe4 : si++; break;
+		//追加 for V2.3
+		case 0xe3 : if((qq->volume + *si) < 16) qq->volume += *si; si++;break;
+		case 0xe2 : if((qq->volume - *si) >= 0) qq->volume -= *si; si++;break;
+		//
+		case 0xe1 : si++; break;
+		case 0xe0 : si++; break;
+		//
+		case 0xdf : open_work.syousetu_lng = *si++; break;
+		//
+		case 0xde : si = vol_one_up_psg(qq, si); break;
+		case 0xdd : si = vol_one_down(qq, si); break;
+		//
+		case 0xdc : open_work.status = *si++; break;
+		case 0xdb : open_work.status += *si++; break;
+		//
+		case 0xda : si++; break;
+		//
+		case 0xd9 : si++; break;
+		case 0xd8 : si++; break;
+		case 0xd7 : si++; break;
+		//
+		case 0xd6 : si+=2; break;
+		case 0xd5 : qq->detune += *(short *)si; si+=2; break;
+		//
+		case 0xd4 : si++; break;
+		case 0xd3 : si++; break;
+		case 0xd2 :
+            si++;
+			break;
+		//
+		case 0xd1 : si++; break;
+		case 0xd0 : si++; break;
+		//
+		case 0xcf : si++; break;
+		case 0xce : si+=6; break;
+		case 0xcd : si+=5; break;
+		case 0xcc : si++; break;
+		case 0xcb : si++; break;
+		case 0xca : si++; break;
+		case 0xc9 : si++; break;
+		case 0xc8 : si+=3; break;
+		case 0xc7 : si+=3; break;
+		case 0xc6 : si+=6; break;
+		case 0xc5 : si++; break;
+		case 0xc4 : si++; break;
+		case 0xc3 : si+=2; break;
+		case 0xc2 : si++; break;
+		case 0xc1 : break;
+		case 0xc0 : si = rhythm_mml_part_mask(qq, si); break;
+		case 0xbf : si+=4; break;
+		case 0xbe : si++; break;
+		case 0xbd : si+=2; break;
+		case 0xbc : si++; break;
+		case 0xbb : si++; break;
+		case 0xba : si++; break;
+		case 0xb9 : si++; break;
+		case 0xb8 : si+=2; break;
+		case 0xb7 : si++; break;
+		case 0xb6 : si++; break;
+		case 0xb5 : si+= 2; break;
+		case 0xb4 : si+=16; break;
+		case 0xb3 : si++; break;
+		case 0xb2 : si++; break;
+		case 0xb1 : si++; break;
+
+		default : 
+			si--;
+			*si = 0x80;
+	}
+	
+	return si;
+}
+
+//=============================================================================
+//	COMMAND '@' [PROGRAM CHANGE]
+//=============================================================================
+uchar * PMDWIN::comat(QQ *qq, uchar *si)
+{
+	uchar	*bx;
+	int		al, dl;
+
+	qq->voicenum = al = *si++;
+	dl = qq->voicenum;
+	
+	if(qq->partmask == 0) {	// パートマスクされているか?
+		neiroset(qq, dl);
+		return si;
+	} else {
+		bx = toneadr_calc(qq, dl);
+		qq->alg_fb = dl = bx[24];
+		bx += 4;
+		
+		// tl設定 
+		qq->slot1 = bx[0];
+		qq->slot3 = bx[1];
+		qq->slot2 = bx[2];
+		qq->slot4 = bx[3];
+		
+		//	FM3chで、マスクされていた場合、fm3_alg_fbを設定
+		if(pmdwork.partb == 3 && qq->neiromask) {
+			if(pmdwork.fmsel == 0) {
+				// in. dl = alg/fb
+				if((qq->slotmask & 0x10) == 0) {
+					al = pmdwork.fm3_alg_fb & 0x38;		// fbは前の値を使用
+					dl = (dl & 7) | al;
+				}
+				
+				pmdwork.fm3_alg_fb = dl;
+				qq->alg_fb = al;
+			}
+		}
+		
+	}
+	return si;
+}
+
+//=============================================================================
+//	音色の設定
+//		INPUTS	-- [PARTB]
+//			-- dl [TONE_NUMBER]
+//			-- di [PART_DATA_ADDRESS]
+//=============================================================================
+void PMDWIN::neiroset(QQ *qq, int dl)
+{
+	uchar	*bx;
+	int		ah, al, dh;
+	
+	bx = toneadr_calc(qq, dl);
+	if(silence_fmpart(qq)) {
+		// neiromask=0の時 (TLのworkのみ設定)
+		bx += 4;
+		
+		// tl設定
+		qq->slot1 = bx[0];
+		qq->slot3 = bx[1];
+		qq->slot2 = bx[2];
+		qq->slot4 = bx[3];
+		return;
+	}
+	
+	//=========================================================================
+	//	音色設定メイン
+	//=========================================================================
+	//-------------------------------------------------------------------------
+	//	AL/FBを設定
+	//-------------------------------------------------------------------------
+	
+	dh = 0xb0 - 1 + pmdwork.partb;
+	
+	if(pmdwork.af_check) {		// ALG/FBは設定しないmodeか?
+		dl = qq->alg_fb;
+	} else {
+		dl = bx[24];
+	}
+	
+	if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+		if(pmdwork.af_check != 0) {	// ALG/FBは設定しないmodeか?
+			dl = pmdwork.fm3_alg_fb;
+		} else {
+			if((qq->slotmask & 0x10) == 0) {	// slot1を使用しているか?
+				dl = (pmdwork.fm3_alg_fb & 0x38) | (dl & 7);
+			}
+			pmdwork.fm3_alg_fb = dl;
+		}
+	}
+	
+	OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	qq->alg_fb = dl;
+	dl &= 7;		// dl = algo
+	
+	//-------------------------------------------------------------------------
+	//	Carrierの位置を調べる (VolMaskにも設定)
+	//-------------------------------------------------------------------------
+	
+	if((qq->volmask & 0x0f) == 0) {
+		qq->volmask = carrier_table[dl];
+	}
+	
+	if((qq->_volmask & 0x0f) == 0) {
+		qq->_volmask = carrier_table[dl];
+	}
+	
+	qq->carrier = carrier_table[dl];
+	ah = carrier_table[dl+8];	// slot2/3の逆転データ(not済み)
+	al = qq->neiromask;
+	ah &= al;				// AH=TL用のmask / AL=その他用のmask
+	
+	//-------------------------------------------------------------------------
+	//	各音色パラメータを設定 (TLはモジュレータのみ)
+	//-------------------------------------------------------------------------
+    
+	dh = 0x30 - 1 + pmdwork.partb;
+	dl = *bx++;				// DT/ML
+	if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;				// TL
+	if(ah & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(ah & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(ah & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(ah & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;				// KS/AR
+	if(al & 0x08) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x04) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x02) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x01) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;				// AM/DR
+	if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;				// SR
+	if(al & 0x08) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x04) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x02) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x01) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+
+	dl = *bx++;				// SL/RR
+	if(al & 0x80) {
+		OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	}
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+
+/*
+	dl = *bx++;				// SL/RR
+	if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+	
+	dl = *bx++;
+	if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+	dh+=4;
+*/
+
+	//-------------------------------------------------------------------------
+	//	SLOT毎のTLをワークに保存
+	//-------------------------------------------------------------------------
+	bx -= 20;
+	qq->slot1 = bx[0];
+	qq->slot3 = bx[1];
+	qq->slot2 = bx[2];
+	qq->slot4 = bx[3];
+}
+
+
+//=============================================================================
+//	[PartB]のパートの音を完璧に消す (TL=127 and RR=15 and KEY-OFF)
+//		cy=1 ・・・ 全スロットneiromaskされている
+//=============================================================================
+int PMDWIN::silence_fmpart(QQ *qq)
+{
+	int		dh;
+	
+	if(qq->neiromask == 0) {
+		return 1;
+	}
+	
+	dh = pmdwork.partb + 0x40 - 1;
+	
+	if(qq->neiromask & 0x80) {
+		OPNASetReg(&opna, pmdwork.fmsel + dh, 127);
+		OPNASetReg(&opna, (pmdwork.fmsel + 0x40) + dh, 127);
+	}
+	dh += 4;
+	
+	if(qq->neiromask & 0x40) {
+		OPNASetReg(&opna, pmdwork.fmsel + dh, 127);
+		OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127);
+	}
+	dh += 4;
+	
+	if(qq->neiromask & 0x20) {
+		OPNASetReg(&opna, pmdwork.fmsel + dh, 127);
+		OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127);
+	}
+	dh += 4;
+	
+	if(qq->neiromask & 0x10) {
+		OPNASetReg(&opna, pmdwork.fmsel + dh, 127);
+		OPNASetReg(&opna, pmdwork.fmsel + 0x40 + dh, 127);
+	}
+	
+	kof1(qq);
+	return 0;
+}
+
+
+//=============================================================================
+//	TONE DATA START ADDRESS を計算
+//		input	dl	tone_number
+//		output	bx	address
+//=============================================================================
+uchar * PMDWIN::toneadr_calc(QQ *qq, int dl)
+{
+	uchar *bx;
+	
+	if(open_work.prg_flg == 0) {
+        return NULL;
+		//return open_work.tondat + (dl << 5);
+	} else {
+		bx = open_work.prgdat_adr;
+		
+		while(*bx != dl) {
+			bx += 26;
+		}
+		
+		return bx+1;
+	}
+}
+
+
+//=============================================================================
+//	FM音源ハードLFOの設定(V2.4拡張分)
+//=============================================================================
+uchar * PMDWIN::hlfo_set(QQ *qq, uchar *si)
+{
+	qq->fmpan = (qq->fmpan & 0xc0) | *si++;
+	
+	if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+		// 2608の時のみなので part_eはありえない
+		//	FM3の場合は 4つのパート総て設定
+		FMPart[2].fmpan = qq->fmpan;
+	}
+	
+	if(qq->partmask == 0) {		// パートマスクされているか?
+		OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb + 0xb4 - 1,
+			calc_panout(qq));
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	ボリュームを次の一個だけ変更(V2.7拡張分)
+//=============================================================================
+uchar * PMDWIN::vol_one_up_fm(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = (int)qq->volume + 1 + *si++;
+	if(al > 128) al = 128;
+	
+	qq->volpush = al;
+	pmdwork.volpush_flag = 1;
+	return si;
+}
+
+
+//=============================================================================
+//	ポルタメント(FM)
+//=============================================================================
+uchar * PMDWIN::porta(QQ *qq, uchar *si)
+{
+	int		ax, cx, cl, bx, bh;
+	
+	if(qq->partmask) {
+		qq->fnum = 0;		//休符に設定
+		qq->onkai = 255;
+		qq->leng = *(si+2);
+		qq->keyon_flag++;
+		qq->address = si+3;
+		
+		if(--pmdwork.volpush_flag) {
+			qq->volpush = 0;
+		}
+		
+		pmdwork.tieflag = 0;
+		pmdwork.volpush_flag = 0;
+		pmdwork.loop_work &= qq->loopcheck;
+		
+		return si+3;		// 読み飛ばす	(Mask時)
+	}
+	
+	lfoinit(qq, *si);
+	fnumset(qq, oshift(qq, *si++));
+	
+	cx = qq->fnum;
+	cl = qq->onkai;
+	fnumset(qq, oshift(qq, *si++));
+	bx = qq->fnum;			// bx=ポルタメント先のfnum値
+	qq->onkai = cl;
+	qq->fnum = cx;			// cx=ポルタメント元のfnum値
+	
+	bh = (int)((bx / 256) & 0x38) - ((cx / 256) & 0x38);	// 先のoctarb - 元のoctarb
+	if(bh) {
+		bh /= 8;
+		ax = bh * 0x26a;			// ax = 26ah * octarb差
+	} else {
+		ax = 0;
+	}
+		
+	bx  = (bx & 0x7ff) - (cx & 0x7ff);
+	ax += bx;				// ax=26ah*octarb差 + 音程差
+	
+	qq->leng = *si++;
+	si = calc_q(qq, si);
+	
+	qq->porta_num2 = ax / qq->leng;	// 商
+	qq->porta_num3 = ax % qq->leng;	// 余り
+	qq->lfoswi |= 8;				// Porta ON
+	
+	if(qq->volpush && qq->onkai != 255) {
+		if(--pmdwork.volpush_flag) {
+			pmdwork.volpush_flag = 0;
+			qq->volpush = 0;
+		}
+	}
+	
+	volset(qq);
+	otodasi(qq);
+	keyon(qq);
+	
+	qq->keyon_flag++;
+	qq->address = si;
+	
+	pmdwork.tieflag = 0;
+	pmdwork.volpush_flag = 0;
+	
+	if(*si == 0xfb) {		// '&'が直後にあったらkeyoffしない
+		qq->keyoff_flag = 2;
+	} else {
+		qq->keyoff_flag = 0;
+	}
+	pmdwork.loop_work &= qq->loopcheck;
+	return si;
+}
+
+
+//=============================================================================
+//	FM slotmask set
+//=============================================================================
+uchar * PMDWIN::slotmask_set(QQ *qq, uchar *si)
+{
+	uchar	*bx;
+	int		ah, al, bl;
+	
+	ah = al = *si++;
+
+	if(al &= 0x0f) {
+		qq->carrier = al << 4;
+	} else {
+		if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+			bl = pmdwork.fm3_alg_fb;
+		} else {
+			bx = toneadr_calc(qq, qq->voicenum);
+			bl = bx[24];
+		}
+		qq->carrier = carrier_table[bl & 7];
+	}
+	
+	ah &= 0xf0;
+	if(qq->slotmask != ah) {
+		qq->slotmask = ah;
+		if((ah & 0xf0) == 0) {
+			qq->partmask |= 0x20;	// s0の時パートマスク
+		} else {
+			qq->partmask &= 0xdf;	// s0以外の時パートマスク解除
+		}
+		
+		if(ch3_setting(qq)) {		// FM3chの場合のみ ch3modeの変更処理
+			// ch3なら、それ以前のFM3パートでkeyon処理
+			if(qq != &FMPart[2]) {
+				if(FMPart[2].partmask == 0 &&
+						(FMPart[2].keyoff_flag & 1) == 0) {
+					keyon(&FMPart[2]);
+				}
+			}
+		}
+		
+		ah = 0;
+		if(qq->slotmask & 0x80) ah += 0x11;		// slot4
+		if(qq->slotmask & 0x40) ah += 0x44;		// slot3
+		if(qq->slotmask & 0x20) ah += 0x22;		// slot2
+		if(qq->slotmask & 0x10) ah += 0x88;		// slot1
+		qq->neiromask = ah;
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	Slot Detune Set
+//=============================================================================
+uchar * PMDWIN::slotdetune_set(QQ *qq, uchar *si)
+{
+	int		ax, bl;
+	
+	if(pmdwork.partb != 3 || pmdwork.fmsel) {
+		return si+3;
+	}
+	
+	bl = *si++;
+	ax = *(short *)si;
+	si+=2;
+	
+	if(bl & 1) {
+		open_work.slot_detune1 = ax;
+	}
+	
+	if(bl & 2) {
+		open_work.slot_detune2 = ax;
+	}
+	
+	if(bl & 4) {
+		open_work.slot_detune3 = ax;
+	}
+	
+	if(bl & 8) {
+		open_work.slot_detune4 = ax;
+	}
+	
+	if(open_work.slot_detune1 || open_work.slot_detune2 ||
+			open_work.slot_detune3 || open_work.slot_detune4) {
+			pmdwork.slotdetune_flag = 1;
+	} else {
+		pmdwork.slotdetune_flag = 0;
+		open_work.slot_detune1 = 0;
+	}
+	ch3mode_set(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	Slot Detune Set(相対)
+//=============================================================================
+uchar * PMDWIN::slotdetune_set2(QQ *qq, uchar *si)
+{
+	int		ax, bl;
+	
+	if(pmdwork.partb != 3 || pmdwork.fmsel) {
+		return si+3;
+	}
+	
+	bl = *si++;
+	ax = *(short *)si;
+	si+=2;
+	
+	if(bl & 1) {
+		open_work.slot_detune1 += ax;
+	}
+	
+	if(bl & 2) {
+		open_work.slot_detune2 += ax;
+	}
+	
+	if(bl & 4) {
+		open_work.slot_detune3 += ax;
+	}
+	
+	if(bl & 8) {
+		open_work.slot_detune4 += ax;
+	}
+	
+	if(open_work.slot_detune1 || open_work.slot_detune2 ||
+			open_work.slot_detune3 || open_work.slot_detune4) {
+		pmdwork.slotdetune_flag = 1;
+	} else {
+		pmdwork.slotdetune_flag = 0;
+		open_work.slot_detune1 = 0;
+	}
+	ch3mode_set(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	音量マスクslotの設定
+//=============================================================================
+uchar * PMDWIN::volmask_set(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++ & 0x0f;
+	if(al) {
+		al = (al << 4) | 0x0f;
+		qq->volmask = al;
+	} else {
+		qq->volmask = qq->carrier;
+	}
+	ch3_setting(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	0c0hの追加special命令
+//=============================================================================
+uchar * PMDWIN::special_0c0h(QQ *qq, uchar *si, uchar al)
+{
+	switch(al) {
+		case 0xff : open_work.fm_voldown = *si++; break;
+		case 0xfe : si = _vd_fm(qq, si); break;
+		case 0xfd : open_work.ssg_voldown = *si++; break;
+		case 0xfc : si = _vd_ssg(qq, si); break;
+		case 0xfb : si++; break;
+		case 0xfa : si = NULL; break;
+		case 0xf9 : open_work.rhythm_voldown = *si++; break;
+		case 0xf8 : si = _vd_rhythm(qq, si); break;
+		case 0xf7 : si++; break;
+		case 0xf6 : si++; break;
+		case 0xf5 : break;
+		default : 
+			si--;
+			*si = 0x80;
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::_vd_fm(QQ *qq, uchar *si)
+{
+	int		al;
+
+	al = *(char *)si++;
+	if(al) {
+		open_work.fm_voldown = Limit(al + open_work.fm_voldown, 255, 0);
+	} else {
+		open_work.fm_voldown = open_work._fm_voldown;
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::_vd_ssg(QQ *qq, uchar *si)
+{
+	int		al;
+
+	al = *(char *)si++;
+	if(al) {
+		open_work.ssg_voldown = Limit(al + open_work.ssg_voldown, 255, 0);
+	} else {
+		open_work.ssg_voldown = open_work._ssg_voldown;
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::_vd_rhythm(QQ *qq, uchar *si)
+{
+	int		al;
+
+	al = *(char *)si++;
+	if(al) {
+		open_work.rhythm_voldown = Limit(al + open_work.rhythm_voldown, 255, 0);
+	} else {
+		open_work.rhythm_voldown = open_work._rhythm_voldown;
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	演奏中パートのマスクon/off
+//=============================================================================
+uchar * PMDWIN::fm_mml_part_mask(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	if(al >= 2) {
+		si = special_0c0h(qq, si, al);
+	} else if(al) {
+		qq->partmask |= 0x40;
+		if(qq->partmask == 0x40) {
+			silence_fmpart(qq);	// 音消去
+		}
+	} else {
+		if((qq->partmask &= 0xbf) == 0) {
+			neiro_reset(qq);		// 音色再設定
+		}
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::ssg_mml_part_mask(QQ *qq, uchar *si)
+{
+	int		ah, al;
+	
+	al = *si++;
+	if(al >= 2) {
+		si = special_0c0h(qq, si, al);
+	} else if(al) {
+		qq->partmask |= 0x40;
+		if(qq->partmask == 0x40) {
+			ah = ((1 << (pmdwork.partb-1)) | (4 << pmdwork.partb));
+			al = PSGGetReg(&(opna.psg), 0x07);
+			PSGSetReg(&(opna.psg), 0x07, ah | al);		// PSG keyoff
+		}
+	} else {
+		qq->partmask &= 0xbf;
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::rhythm_mml_part_mask(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	if(al >= 2) {
+		si = special_0c0h(qq, si, al);
+	} else if(al) {
+		qq->partmask |= 0x40;
+	} else {
+		qq->partmask &= 0xbf;
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	FM音源の音色を再設定
+//=============================================================================
+void PMDWIN::neiro_reset(QQ *qq)
+{
+	int		dh, al, s1, s2, s3, s4;
+	
+	if(qq->neiromask == 0) return;
+	
+	s1 = qq->slot1;
+	s2 = qq->slot2;
+	s3 = qq->slot3;
+	s4 = qq->slot4;
+	pmdwork.af_check = 1;
+	neiroset(qq, qq->voicenum);		// 音色復帰
+	pmdwork.af_check = 0;
+	qq->slot1 = s1;
+	qq->slot2 = s2;
+	qq->slot3 = s3;
+	qq->slot4 = s4;
+	
+	al = ((~qq->carrier) & qq->slotmask) & 0xf0;
+		// al<- TLを再設定していいslot 4321xxxx
+	if(al) {
+		dh = 0x4c - 1 + pmdwork.partb;	// dh=TL FM Port Address
+		if(al & 0x80) OPNASetReg(&opna, pmdwork.fmsel + dh, s4);
+		
+		dh -= 8;
+		if(al & 0x40) OPNASetReg(&opna, pmdwork.fmsel + dh, s3);
+		
+		dh += 4;
+		if(al & 0x20) OPNASetReg(&opna, pmdwork.fmsel + dh, s2);
+		
+		dh -= 8;
+		if(al & 0x10) OPNASetReg(&opna, pmdwork.fmsel + dh, s1);
+	}
+	
+	dh = pmdwork.partb + 0xb4 - 1;
+	OPNASetReg(&opna, pmdwork.fmsel + dh, calc_panout(qq));
+}
+
+
+uchar * PMDWIN::_lfoswitch(QQ *qq, uchar *si)
+{
+	qq->lfoswi = (qq->lfoswi & 0x8f) | ((*si++ & 7) << 4);
+	lfo_change(qq);
+	lfoinit_main(qq);
+	lfo_change(qq);
+	return si;
+}
+
+
+uchar * PMDWIN::_volmask_set(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++ & 0x0f;
+	if(al) {
+		al = (al << 4) | 0x0f;
+		qq->_volmask = al;
+	} else {
+		qq->_volmask = qq->carrier;
+	}
+	ch3_setting(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	TL変化
+//=============================================================================
+uchar * PMDWIN::tl_set(QQ *qq, uchar *si)
+{
+	int		ah, al, ch, dh, dl;
+	
+	dh = 0x40 - 1 + pmdwork.partb;		// dh=TL FM Port Address
+	al = *(char *)si++;
+	ah = al & 0x0f;
+	ch = (qq->slotmask >> 4) | ((qq->slotmask << 4) & 0xf0);
+	ah &= ch;							// ah=変化させるslot 00004321
+	dl = *(char *)si++;
+	
+	if(al >= 0) {
+		dl &= 127;
+		if(ah & 1) {
+			qq->slot1 = dl;
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+		}
+		
+		dh += 8;
+		if(ah & 2) {
+			qq->slot2 = dl;
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+		}
+		
+		dh -= 4;
+		if(ah & 4) {
+			qq->slot3 = dl;
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+		}
+		
+		dh += 8;
+		if(ah & 8) {
+			qq->slot4 = dl;
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+		}
+	} else {
+		//	相対変化
+		al = dl;
+		if(ah & 1) {
+			if((dl = (int)qq->slot1 + al) < 0) {
+				dl = 0;
+				if(al >= 0) dl = 127;
+			}
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+			qq->slot1 = dl;
+		}
+		
+		dh += 8;
+		if(ah & 2) {
+			if((dl = (int)qq->slot2 + al) < 0) {
+				dl = 0;
+				if(al >= 0) dl = 127;
+			}
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+			qq->slot2 = dl;
+		}
+		
+		dh -= 4;
+		if(ah & 4) {
+			if((dl = (int)qq->slot3 + al) < 0) {
+				dl = 0;
+				if(al >= 0) dl = 127;
+			}
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+			qq->slot3 = dl;
+		}
+		
+		dh += 8;
+		if(ah & 8) {
+			if((dl = (int)qq->slot4 + al) < 0) {
+				dl = 0;
+				if(al >= 0) dl = 127;
+			}
+			if(qq->partmask == 0) {
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			}
+			qq->slot4 = dl;
+		}
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	FB変化
+//=============================================================================
+uchar * PMDWIN::fb_set(QQ *qq, uchar *si)
+{
+	int		al, dh, dl;
+	
+	
+	dh = pmdwork.partb + 0xb0 - 1;	// dh=ALG/FB port address
+	al = *(char *)si++;
+	if(al >= 0) {
+		// in	al 00000xxx 設定するFB
+		al = ((al << 3) & 0xff) | (al >> 5);
+		
+		// in	al 00xxx000 設定するFB
+		if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+			if((qq->slotmask & 0x10) == 0) return si;
+			dl = (pmdwork.fm3_alg_fb & 7) | al;
+			pmdwork.fm3_alg_fb = dl;
+		} else {
+			dl = (qq->alg_fb & 7) | al;
+		}
+		OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+		qq->alg_fb = dl;
+		return si;
+	} else {
+		if((al & 0x40) == 0) al &= 7;
+		if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+			dl = pmdwork.fm3_alg_fb;
+		} else {
+			dl = qq->alg_fb;
+		
+		}
+		
+		dl = (dl >> 3) & 7;
+		if((al += dl) >= 0) {
+			if(al >= 8) {
+				al = 0x38;
+				if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+					if((qq->slotmask & 0x10) == 0) return si;
+					
+					dl = (pmdwork.fm3_alg_fb & 7) | al;
+					pmdwork.fm3_alg_fb = dl;
+				} else {
+					dl = (qq->alg_fb & 7) | al;
+				}
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+				qq->alg_fb = dl;
+				return si;
+			} else {
+				// in	al 00000xxx 設定するFB
+				al = ((al << 3) & 0xff) | (al >> 5);
+				
+				// in	al 00xxx000 設定するFB
+				if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+					if((qq->slotmask & 0x10) == 0) return si;
+					dl = (pmdwork.fm3_alg_fb & 7) | al;
+					pmdwork.fm3_alg_fb = dl;
+				} else {
+					dl = (qq->alg_fb & 7) | al;
+				}
+				OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+				qq->alg_fb = dl;
+				return si;
+			}
+		} else {
+			al = 0;
+			if(pmdwork.partb == 3 && pmdwork.fmsel == 0) {
+				if((qq->slotmask & 0x10) == 0) return si;
+				
+				dl = (pmdwork.fm3_alg_fb & 7) | al;
+				pmdwork.fm3_alg_fb = dl;
+			} else {
+				dl = (qq->alg_fb & 7) | al;
+			}
+			OPNASetReg(&opna, pmdwork.fmsel + dh, dl);
+			qq->alg_fb = dl;
+			return si;
+		}
+	}
+}
+
+
+//=============================================================================
+//	COMMAND 't' [TEMPO CHANGE1]
+//	COMMAND 'T' [TEMPO CHANGE2]
+//	COMMAND 't±' [TEMPO CHANGE 相対1]
+//	COMMAND 'T±' [TEMPO CHANGE 相対2]
+//=============================================================================
+uchar * PMDWIN::comt(uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	if(al < 251) {
+		open_work.tempo_d = al;		// T (FC)
+		open_work.tempo_d_push = al;
+		calc_tb_tempo();
+	
+	} else if(al == 0xff) {
+		al = *si++;					// t (FC FF)
+		if(al < 18) al = 18;
+		open_work.tempo_48 = al;
+		open_work.tempo_48_push = al;
+		calc_tempo_tb();
+	
+	} else if(al == 0xfe) {
+		al = char(*si++);			// T± (FC FE)
+		if(al >= 0) {
+			al += open_work.tempo_d_push;
+		} else {
+			al += open_work.tempo_d_push;
+			if(al < 0) {
+				al = 0;
+			}
+		}
+
+		if(al > 250) al = 250;
+		
+		open_work.tempo_d = al;
+		open_work.tempo_d_push = al;
+		calc_tb_tempo();
+	
+	} else {
+		al = char(*si++);			// t± (FC FD)
+		if(al >= 0) {
+			al += open_work.tempo_48_push;
+			if(al > 255) {
+				al = 255;
+			}
+		} else {
+			al += open_work.tempo_48_push;
+			if(al < 0) al = 18;
+		}
+		open_work.tempo_48 = al;
+		open_work.tempo_48_push = al;
+		calc_tempo_tb();
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	COMMAND '[' [ループ スタート]
+//=============================================================================
+uchar * PMDWIN::comstloop(QQ *qq, uchar *si)
+{
+	uchar *ax = open_work.mmlbuf;
+	ax[*(ushort *)si+1] = 0;
+	si+=2;
+	return si;
+}
+
+
+//=============================================================================
+//	COMMAND	']' [ループ エンド]
+//=============================================================================
+uchar * PMDWIN::comedloop(QQ *qq, uchar *si)
+{
+	int		ah, al, ax;
+	ah = *si++;
+	
+	if(ah) {
+		(*si)++;
+		al = *si++;
+		if(ah == al) {
+			si+=2;
+			return si;
+		}
+	} else {			// 0 ナラ ムジョウケン ループ
+		si++;
+		qq->loopcheck = 1;
+	}
+	
+	ax = *(ushort *)si + 2;
+	si = open_work.mmlbuf + ax;
+	return si;
+}
+
+
+//=============================================================================
+//	COMMAND	':' [ループ ダッシュツ]
+//=============================================================================
+uchar * PMDWIN::comexloop(QQ *qq, uchar *si)
+{
+	uchar	*bx;
+	int		dl;
+	bx = open_work.mmlbuf;
+	bx += *(ushort *)si;
+	si+=2;
+	
+	dl = *bx++ - 1;
+	if(dl != *bx) return si;
+	si = bx + 3;
+	return si;
+}
+
+
+//=============================================================================
+//	LFO パラメータ セット
+//=============================================================================
+uchar * PMDWIN::lfoset(QQ *qq, uchar *si)
+{
+	qq->delay = *si;
+	qq->delay2 = *si++;
+	qq->speed = *si;
+	qq->speed2 = *si++;
+	qq->step = *(char *)si;
+	qq->step2 = *(char *)si++;
+	qq->time = *si;
+	qq->time2 = *si++;
+	lfoinit_main(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	LFO SWITCH
+//=============================================================================
+uchar * PMDWIN::lfoswitch(QQ *qq, uchar *si)
+{
+	int al;
+	
+	al = *si++;
+	
+	if(al & 0xf8) {
+		al = 1;
+	}
+	al &= 7;
+	qq->lfoswi = (qq->lfoswi & 0xf8) | al;
+	lfoinit_main(qq);
+	return si;
+}
+
+
+//=============================================================================
+//	PSG ENVELOPE SET
+//=============================================================================
+uchar * PMDWIN::psgenvset(QQ *qq, uchar *si)
+{
+	qq->eenv_ar = *si; qq->eenv_arc = *si++;
+	qq->eenv_dr = *(char *)si++;
+	qq->eenv_sr = *si; qq->eenv_src = *si++;
+	qq->eenv_rr = *si; qq->eenv_rrc = *si++;
+	if(qq->envf == -1) {
+		qq->envf = 2;		// RR
+		qq->eenv_volume = -15;		// volume
+	}
+	return si;
+}
+
+
+//=============================================================================
+//	"¥?" COMMAND [ OPNA Rhythm Keyon/Dump ]
+//=============================================================================
+uchar * PMDWIN::rhykey(uchar *si)
+{
+    int dl;
+	
+	if((dl = (*si++ & open_work.rhythmmask)) == 0) {
+		return si;
+    }
+
+	if(dl < 0x80) {
+		if(dl & 0x01) OPNASetReg(&opna, 0x18, open_work.rdat[0]);
+		if(dl & 0x02) OPNASetReg(&opna, 0x19, open_work.rdat[1]);
+		if(dl & 0x04) OPNASetReg(&opna, 0x1a, open_work.rdat[2]);
+		if(dl & 0x08) OPNASetReg(&opna, 0x1b, open_work.rdat[3]);
+		if(dl & 0x10) OPNASetReg(&opna, 0x1c, open_work.rdat[4]);
+		if(dl & 0x20) OPNASetReg(&opna, 0x1d, open_work.rdat[5]);
+	}
+	
+	OPNASetReg(&opna, 0x10, dl);
+	if(dl >= 0x80) {
+		open_work.rshot_dat &= (~dl);
+	} else {
+		open_work.rshot_dat |= dl;
+	}
+	
+	return si;
+}
+
+
+//=============================================================================
+//	"¥v?n" COMMAND
+//=============================================================================
+uchar * PMDWIN::rhyvs(uchar *si)
+{
+	int		*bx;
+	int		dh, dl;
+	
+	dl = *si & 0x1f;
+	dh = *si++ >> 5;
+	bx = &open_work.rdat[dh-1];
+	dh = 0x18 - 1 + dh;
+	dl |= (*bx & 0xc0);
+	*bx = dl;
+	
+	OPNASetReg(&opna, dh, dl);
+	
+	return si;
+}
+
+
+uchar * PMDWIN::rhyvs_sft(uchar *si)
+{
+	int		*bx;
+	int		al, dh, dl;
+	
+	bx = &open_work.rdat[*si-1];
+	dh = *si++ + 0x18 - 1;
+	dl = *bx & 0x1f;
+	
+	al = (*(char *)si++ + dl);
+	if(al >= 32) {
+		al = 31;
+	} else if(al < 0) {
+		al = 0;
+	}
+	
+	dl = (al &= 0x1f);
+	dl = *bx = ((*bx & 0xe0) | dl);
+	OPNASetReg(&opna, dh, dl);
+	
+	return si;
+}
+
+
+//=============================================================================
+//	"¥p?" COMMAND
+//=============================================================================
+uchar * PMDWIN::rpnset(uchar *si)
+{
+	int		*bx;
+	int		dh, dl;
+	
+	dl = (*si & 3) << 6;
+	
+	dh = (*si++ >> 5) & 0x07;
+	bx = &open_work.rdat[dh-1];
+	
+	dh += 0x18 - 1;
+	dl |= (*bx & 0x1f);
+	*bx = dl;
+	OPNASetReg(&opna, dh, dl);
+	
+	return si;
+}
+
+
+//=============================================================================
+//	"¥Vn" COMMAND
+//=============================================================================
+uchar * PMDWIN::rmsvs(uchar *si)
+{
+	int		dl;
+	dl = *si++;
+	if(open_work.rhythm_voldown) {
+		dl = ((256 - open_work.rhythm_voldown) * dl) >> 8;
+	}
+	open_work.rhyvol = dl;
+	OPNASetReg(&opna, 0x11, dl);
+	return si;
+}
+
+
+uchar *PMDWIN::rmsvs_sft(uchar *si)
+{
+	int		dl;
+	dl = open_work.rhyvol + *(char *)si++;
+	if(dl >= 64) {
+		if(dl & 0x80) {
+			dl = 0;
+		} else {
+			dl = 63;
+		}
+	}
+	open_work.rhyvol = dl;
+	OPNASetReg(&opna, 0x11, dl);
+	return si;
+}
+
+
+//=============================================================================
+//	ボリュームを次の一個だけ変更(V2.7拡張分)
+//=============================================================================
+uchar * PMDWIN::vol_one_up_psg(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = qq->volume + *si++;
+	if(al > 15) al = 15;
+	qq->volpush = ++al;
+	pmdwork.volpush_flag = 1;
+	return si;
+}
+
+
+uchar * PMDWIN::vol_one_down(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = qq->volume - *si++;
+	if(al < 0) {
+		al = 0;
+	} else {
+		if(al >= 255) al = 254;
+	}
+	
+	qq->volpush = ++al;
+	pmdwork.volpush_flag = 1;
+	return si;
+}
+
+
+//=============================================================================
+//	ポルタメント(PSG)
+//=============================================================================
+uchar * PMDWIN::portap(QQ *qq, uchar *si)
+{
+	int		ax, al_, bx_;
+
+	if(qq->partmask) {
+		qq->fnum = 0;		//休符に設定
+		qq->onkai = 255;
+		qq->leng = *(si+2);
+		qq->keyon_flag++;
+		qq->address = si+3;
+		
+		if(--pmdwork.volpush_flag) {
+			qq->volpush = 0;
+		}
+				
+		pmdwork.tieflag = 0;
+		pmdwork.volpush_flag = 0;
+		pmdwork.loop_work &= qq->loopcheck;
+		return si+3;		// 読み飛ばす	(Mask時)
+	}
+	
+	lfoinitp(qq, *si);
+	fnumsetp(qq, oshiftp(qq, *si++));
+	
+	bx_ = qq->fnum;
+	al_ = qq->onkai;
+	fnumsetp(qq, oshiftp(qq, *si++));
+	ax = qq->fnum; 			// ax = ポルタメント先のpsg_tune値
+	
+	qq->onkai = al_;
+	qq->fnum = bx_;			// bx = ポルタメント元のpsg_tune値
+	ax -= bx_;
+	
+	qq->leng = *si++;
+	si = calc_q(qq, si);
+	
+	qq->porta_num2 = ax / qq->leng;		// 商
+	qq->porta_num3 = ax % qq->leng;		// 余り
+	qq->lfoswi |= 8;				// Porta ON
+	
+	if(qq->volpush && qq->onkai != 255) {
+		if(--pmdwork.volpush_flag) {
+			pmdwork.volpush_flag = 0;
+			qq->volpush = 0;
+		}
+	}
+	
+	volsetp(qq);
+	otodasip(qq);
+	keyonp(qq);
+	
+	qq->keyon_flag++;
+	qq->address = si;
+	
+	pmdwork.tieflag = 0;
+	pmdwork.volpush_flag = 0;
+	qq->keyoff_flag = 0;
+	
+	if(*si == 0xfb) {			// '&'が直後にあったらkeyoffしない
+		qq->keyoff_flag = 2;
+	}
+	
+	pmdwork.loop_work &= qq->loopcheck;
+	return si;
+}
+
+
+//=============================================================================
+//	'w' COMMAND [PSG NOISE ヘイキン シュウハスウ]
+//=============================================================================
+uchar * PMDWIN::psgnoise_move(uchar *si)
+{
+	open_work.psnoi += *(char *)si++;
+	if(open_work.psnoi < 0) open_work.psnoi = 0;
+	if(open_work.psnoi > 31) open_work.psnoi = 31;
+	return si;
+}
+
+
+//=============================================================================
+//	PSG Envelope set (Extend)
+//=============================================================================
+uchar * PMDWIN::extend_psgenvset(QQ *qq, uchar *si)
+{
+	qq->eenv_ar = *si++ & 0x1f;
+	qq->eenv_dr = *si++ & 0x1f;
+	qq->eenv_sr = *si++ & 0x1f;
+	qq->eenv_rr = *si & 0x0f;
+	qq->eenv_sl = ((*si++ >> 4) & 0x0f) ^ 0x0f;
+	qq->eenv_al = *si++ & 0x0f;
+	
+	if(qq->envf != -1) {	// ノーマル>拡張に移行したか?
+		qq->envf = -1;
+		qq->eenv_count = 4;		// RR
+		qq->eenv_volume = 0;	// Volume
+	}
+	return si;
+}
+
+
+uchar * PMDWIN::mdepth_count(QQ *qq, uchar *si)
+{
+	int		al;
+	
+	al = *si++;
+	
+	if(al >= 0x80) {
+		if((al &= 0x7f) == 0) al = 255;
+		qq->_mdc = al;
+		qq->_mdc2 = al;
+		return si;
+	}
+	
+	if(al == 0) al = 255;
+	qq->mdc = al;
+	qq->mdc2 = al;
+	return si;
+}
+
+
+//=============================================================================
+//	LFOとPSG/PCMのソフトウエアエンベロープの初期化
+//=============================================================================
+//=============================================================================
+//	PSG/PCM音源用 Entry
+//=============================================================================
+void PMDWIN::lfoinitp(QQ *qq, int al)
+{
+	int		ah;
+
+	ah = al & 0x0f;
+	
+	if(ah == 0x0c) {
+		al = qq->onkai_def;
+		ah = al & 0x0f;
+	}
+
+	qq->onkai_def = al;
+
+	if(ah == 0x0f) {		// キューフ ノ トキ ハ INIT シナイヨ
+		lfo_exit(qq);
+		return;
+	}
+	
+	qq->porta_num = 0;				// ポルタメントは初期化
+	
+	if(pmdwork.tieflag & 1) {
+		lfo_exit(qq);
+		return;
+	}
+	
+	//------------------------------------------------------------------------
+	//	ソフトウエアエンベロープ初期化
+	//------------------------------------------------------------------------
+	if(qq->envf != -1) {
+		qq->envf = 0;
+		qq->eenv_volume = 0;
+		qq->eenv_ar = qq->eenv_arc;
+		
+		if(qq->eenv_ar == 0) {
+			qq->envf = 1;	// ATTACK=0 ... スグ Decay ニ
+			qq->eenv_volume = qq->eenv_dr;
+		}
+		
+		qq->eenv_sr = qq->eenv_src;
+		qq->eenv_rr = qq->eenv_rrc;
+		lfin1(qq);
+		
+	} else {
+		//	拡張ssg_envelope用
+		
+		qq->eenv_arc = qq->eenv_ar - 16;
+		if(qq->eenv_dr < 16) {
+			qq->eenv_drc = (qq->eenv_dr-16)*2;
+		} else {
+			qq->eenv_drc = qq->eenv_dr-16;
+		}
+		
+		if(qq->eenv_sr < 16) {
+			qq->eenv_src = (qq->eenv_sr-16)*2;
+		} else {
+			qq->eenv_src = qq->eenv_sr-16;
+		}
+		
+		qq->eenv_rrc = (qq->eenv_rr) * 2 - 16;
+		qq->eenv_volume = qq->eenv_al;
+		qq->eenv_count = 1;
+        if(qq->eenv_arc > 0) {
+            qq->eenv_volume += qq->eenv_arc;
+            if(qq->eenv_volume < 15) {
+                qq->eenv_arc = qq->eenv_ar-16;
+            } else {
+                qq->eenv_volume = 15;
+                qq->eenv_count++;
+                if(qq->eenv_sl == 15) qq->eenv_count++;       // SL=0の場合はすぐSRに
+            }
+        } else {
+            if(qq->eenv_ar != 0) qq->eenv_arc++;
+        }
+		lfin1(qq);
+	}
+}
+
+
+void PMDWIN::lfo_exit(QQ *qq)
+{
+	if((qq->lfoswi & 3) != 0) {		// 前が & の場合 -> 1回 LFO処理
+		lfo(qq);
+	}
+	
+	if((qq->lfoswi & 0x30) != 0) {	// 前が & の場合 -> 1回 LFO処理
+		lfo_change(qq);
+		lfo(qq);
+		lfo_change(qq);
+	}
+}
+
+
+//=============================================================================
+//	LFO初期化
+//=============================================================================
+void PMDWIN::lfin1(QQ *qq)
+{
+	qq->hldelay_c = qq->hldelay;
+
+	if(qq->hldelay) {
+		OPNASetReg(&opna, pmdwork.fmsel + pmdwork.partb+0xb4-1, (qq->fmpan) & 0xc0);
+	}
+	
+	qq->sdelay_c = qq->sdelay;
+	
+	if(qq->lfoswi & 3) {	// LFOは未使用
+		if((qq->lfoswi & 4) == 0) {	//keyon非同期か?
+			lfoinit_main(qq);
+		}
+		lfo(qq);
+	}
+	
+	if(qq->lfoswi & 0x30) {	// LFOは未使用
+		if((qq->lfoswi & 0x40) == 0) {	//keyon非同期か?
+			lfo_change(qq);
+			lfoinit_main(qq);
+			lfo_change(qq);
+		}
+		
+		lfo_change(qq);
+		lfo(qq);
+		lfo_change(qq);
+	}
+}
+
+
+void PMDWIN::lfoinit_main(QQ *qq)
+{
+	qq->lfodat = 0;
+	qq->delay = qq->delay2;
+	qq->speed = qq->speed2;
+	qq->step = qq->step2;
+	qq->time = qq->time2;
+	qq->mdc = qq->mdc2;
+	
+	if(qq->lfo_wave == 2 || qq->lfo_wave == 3) {	// 矩形波 or ランダム波?
+		qq->speed = 1;	// delay直後にLFOが掛かるようにする
+	} else {
+		qq->speed++;	// それ以外の場合はdelay直後のspeed値を +1
+	}
+}
+
+
+//=============================================================================
+//	SHIFT[di] 分移調する
+//=============================================================================
+int PMDWIN::oshiftp(QQ *qq, int al)
+{
+	return oshift(qq, al);
+}
+
+
+int PMDWIN::oshift(QQ *qq, int al)
+{
+	int	bl, bh, dl;
+	
+	if(al == 0x0f) return al;
+
+	if(al == 0x0c) {
+		if((al = qq->onkai) >= 0x80) {
+			return 0x0f;
+		} else {
+			return al;	//@暫定対応
+		}
+	}
+
+	dl = qq->shift + qq->shift_def;
+	if(dl == 0) return al;
+	
+	bl = (al & 0x0f);		// bl = ONKAI
+	bh = (al & 0xf0) >> 4;	// bh = OCT
+
+	if(dl < 0) {
+		// - ホウコウ シフト
+		if((bl += dl) < 0) {
+			do {
+				bh--;
+			} while((bl+=12) < 0);
+		}
+		if(bh < 0) bh = 0;
+		return (bh << 4) | bl;
+
+	} else {
+		// + ホウコウ シフト
+		bl += dl;
+		while(bl >= 0x0c) {
+			bh++;
+			bl -= 12;
+		}
+		
+		if(bh > 7) bh = 7;
+		return (bh << 4) | bl;
+	}
+}
+
+
+//=============================================================================
+//	PSG TUNE SET
+//=============================================================================
+void PMDWIN::fnumsetp(QQ *qq, int al)
+{
+	int	ax, bx, cl;
+	
+	if((al & 0x0f) == 0x0f) {		// キュウフ ナラ FNUM ニ 0 ヲ セット
+		qq->onkai = 255;
+		if(qq->lfoswi & 0x11) return;
+		qq->fnum = 0;	// 音程LFO未使用
+		return;
+	}
+	
+	qq->onkai = al;
+	
+	cl = (al >> 4) & 0x0f;	// cl=oct
+	bx = al & 0x0f;			// bx=onkai
+	
+	ax = psg_tune_data[bx];
+	if(cl > 0) {
+		ax = (ax + 1) >> cl;
+	}
+	
+	qq->fnum = ax;
+}
+
+
+//=============================================================================
+//	Q値の計算
+//		break	dx
+//=============================================================================
+uchar * PMDWIN::calc_q(QQ *qq, uchar *si)
+{
+    int dh, dl;
+
+	if(*si == 0xc1) {		// &&
+		si++;
+		qq->qdat = 0;
+		return si;
+	}
+	
+	dl = qq->qdata;
+	if(qq->qdatb) {
+		dl += (qq->leng * qq->qdatb) >> 8;
+	}
+
+#if 0	
+	if(qq->qdat3) {		//	Random-Q
+		ax = rand() % ((qq->qdat3 & 0x7f) + 1);
+		if((qq->qdat3 & 0x80) == 0) {
+			dl += ax;
+		} else {
+			dl -= ax;
+			if(dl < 0) dl = 0;
+		}
+	}
+#endif
+
+	if(qq->qdat2) {
+		if((dh = qq->leng - qq->qdat2) < 0) {
+			qq->qdat = 0;
+			return si;
+		}
+		
+		if(dl < dh) {
+			qq->qdat = dl;
+		} else {
+			qq->qdat = dh;
+		}
+	} else {
+		qq->qdat = dl;
+	}
+
+	return si;
+}
+
+
+//=============================================================================
+//	PSG VOLUME SET
+//=============================================================================
+void PMDWIN::volsetp(QQ *qq)
+{
+	int		ax, dl;
+	
+	if(qq->envf == 3 || (qq->envf == -1 && qq->eenv_count == 0)) return;
+
+	if(qq->volpush) {
+		dl = qq->volpush-1;
+	} else {
+		dl = qq->volume;
+	}
+	
+	//------------------------------------------------------------------------
+	//	音量down計算
+	//------------------------------------------------------------------------
+	dl = ((256-open_work.ssg_voldown) * dl) >> 8;
+	
+	//------------------------------------------------------------------------
+	//	ENVELOPE 計算
+	//------------------------------------------------------------------------
+	if(dl <= 0) {
+		OPNASetReg(&opna, pmdwork.partb+8-1, 0);
+		return;
+	}
+	
+	if(qq->envf == -1) {
+		if(qq->eenv_volume == 0) {
+			OPNASetReg(&opna, pmdwork.partb+8-1, 0);
+			return;
+		}
+		dl = ((((dl * (qq->eenv_volume + 1))) >> 3) + 1) >> 1;
+	} else {
+		dl += qq->eenv_volume;
+		if(dl <= 0) {
+			OPNASetReg(&opna, pmdwork.partb+8-1, 0);
+			return;
+		}
+		if(dl > 15) dl = 15;
+	}
+
+	//--------------------------------------------------------------------
+	//	音量LFO計算
+	//--------------------------------------------------------------------
+	if((qq->lfoswi & 0x22) == 0) {
+		OPNASetReg(&opna, pmdwork.partb+8-1, dl);
+		return;
+	}
+	
+	if(qq->lfoswi & 2) {
+		ax = qq->lfodat;
+	} else {
+		ax = 0;
+	}
+	
+	if(qq->lfoswi & 0x20) {
+		ax += qq->_lfodat;
+	}
+	
+	dl = dl + ax;
+	if(dl <  0) {
+		OPNASetReg(&opna, pmdwork.partb+8-1, 0);
+		return;
+	}
+	if(dl > 15) dl = 15;
+	
+	//------------------------------------------------------------------------
+	//	出力
+	//------------------------------------------------------------------------
+	OPNASetReg(&opna, pmdwork.partb+8-1, dl);
+}
+
+
+//=============================================================================
+//	PSG 音程設定
+//=============================================================================
+void PMDWIN::otodasip(QQ *qq)
+{
+	int		ax, dx;
+	
+	if(qq->fnum == 0) return;
+
+	// PSG Portament set
+	
+	ax = qq->fnum + qq->porta_num;
+	dx = 0;
+
+	// PSG Detune/LFO set
+	if((qq->extendmode & 1) == 0) {
+		ax -= qq->detune;
+		if(qq->lfoswi & 1) {
+			ax -= qq->lfodat;
+		}
+		
+		if(qq->lfoswi & 0x10) {
+			ax -= qq->_lfodat;
+		}
+	} else {
+		// 拡張DETUNE(DETUNE)の計算
+		if(qq->detune) {
+			dx = (ax * qq->detune) >> 12;		// dx:ax=ax * qq->detune
+			if(dx >= 0) {
+				dx++;
+			} else {
+				dx--;
+			}
+			ax -= dx;
+		}
+		// 拡張DETUNE(LFO)の計算
+		if(qq->lfoswi & 0x11) {
+			if(qq->lfoswi & 1) {
+				dx = qq->lfodat;
+			} else {
+				dx = 0;
+			}
+			
+			if(qq->lfoswi & 0x10) {
+				dx += qq->_lfodat;
+			}
+			
+			if(dx) {
+				dx = (ax * dx) >> 12;
+				if(dx >= 0) {
+					dx++;
+				} else {
+					dx--;
+				}
+			}
+			ax -= dx;
+		}
+	}
+	
+	// TONE SET
+	
+	if(ax >= 0x1000) {
+		if(ax >= 0) {
+			ax = 0xfff;
+		} else {
+			ax = 0;
+		}
+	}
+	
+	OPNASetReg(&opna, (pmdwork.partb-1) * 2, ax & 0xff);
+	OPNASetReg(&opna, (pmdwork.partb-1) * 2 + 1, ax >> 8);
+}
+
+
+//=============================================================================
+//	PSG KEYON
+//=============================================================================
+void PMDWIN::keyonp(QQ *qq)
+{
+	int ah, al;
+	
+	if(qq->onkai == 255) return;		// キュウフ ノ トキ
+    int op = ((pmdwork.partb-1)&3);
+    switch(op) {
+        case 0:
+            pmdstatus[6] = (qq->onkai & 0x0f);
+            break;
+        case 1:
+            pmdstatus[7] = (qq->onkai & 0x0f);
+            break;
+        case 2:
+            pmdstatus[8] = (qq->onkai & 0x0f);
+            break;
+        default:
+            break;
+    }
+
+	ah=(1 << (pmdwork.partb -1)) | (1 << (pmdwork.partb +2));
+	al = PSGGetReg(&(opna.psg), 0x07) | ah;
+	ah = ~(ah & qq->psgpat);
+	al &= ah;
+	PSGSetReg(&(opna.psg), 7, al);
+	
+	// PSG ノイズ シュウハスウ ノ セット
+	if(open_work.psnoi != open_work.psnoi_last && effwork.effon == 0) {
+		PSGSetReg(&(opna.psg), 6, open_work.psnoi);
+		open_work.psnoi_last = open_work.psnoi;
+	}
+}
+
+
+//=============================================================================
+//	LFO処理
+//		Don't Break cl
+//		output		cy=1	変化があった
+//=============================================================================
+int PMDWIN::lfo(QQ *qq)
+{
+	int		ax, ch;
+	
+	if(qq->delay) {
+		qq->delay--;
+		return 0;
+	}
+	
+	if(qq->extendmode & 2) {	// TimerAと合わせるか?
+								// そうじゃないなら無条件にlfo処理
+		ch = open_work.TimerAtime - pmdwork.lastTimerAtime;
+		if(ch == 0) return 0;
+		ax = qq->lfodat;
+		
+		for(; ch > 0; ch--) {
+			lfo_main(qq);
+		}
+	} else {
+		
+		ax = qq->lfodat;
+		lfo_main(qq);
+	}
+	
+	if(ax == qq->lfodat) {
+		return 0;
+	}
+	return 1;
+}
+
+
+void PMDWIN::lfo_main(QQ *qq)
+{
+	int		al, ax;
+	
+	if(qq->speed != 1) {
+		if(qq->speed != 255) qq->speed--;
+		return;
+	}
+	
+	qq->speed = qq->speed2;
+	
+	if(qq->lfo_wave == 0 || qq->lfo_wave == 4 || qq->lfo_wave == 5) {
+		//	三角波		lfowave = 0,4,5
+		if(qq->lfo_wave == 5) {
+			ax = abs(qq->step) * qq->step;
+		} else {
+			ax = qq->step;
+		}
+
+		if((qq->lfodat += ax) == 0) {
+			md_inc(qq);
+		}
+	
+		al = qq->time;
+		if(al != 255) {
+			if(--al == 0) {
+				al = qq->time2;
+				if(qq->lfo_wave != 4) {
+					al += al;	// lfowave=0,5の場合 timeを反転時2倍にする
+				}
+				qq->time = al;
+				qq->step = -qq->step;
+				return;
+			}
+		}
+		qq->time = al;
+
+	} else if(qq->lfo_wave == 2) {
+		//	矩形波		lfowave = 2
+		qq->lfodat = (qq->step * qq->time);
+		md_inc(qq);
+		qq->step = -qq->step;
+
+	} else if(qq->lfo_wave == 6) {
+		//	ワンショット	lfowave = 6
+		if(qq->time) {
+			if(qq->time != 255) {
+				qq->time--;
+			}
+			qq->lfodat += qq->step;
+		}
+	} else if(qq->lfo_wave == 1) {
+		//ノコギリ波	lfowave = 1
+		qq->lfodat += qq->step;
+		al = qq->time;
+		if(al != -1) {		
+			al--;
+			if(al == 0)  {
+				qq->lfodat = -qq->lfodat;
+				md_inc(qq);
+				al = (qq->time2) * 2;
+			}
+		}
+		qq->time = al;
+
+	} else {
+		//	ランダム波	lfowave = 3
+		ax = abs(qq->step) * qq->time;
+		qq->lfodat = ax - (lfg_rand() % (ax * 2));
+		md_inc(qq);
+	}
+}
+
+
+//=============================================================================
+//	MDコマンドの値によってSTEP値を変更
+//=============================================================================
+void PMDWIN::md_inc(QQ *qq)
+{
+	int		al;
+		
+	if(--qq->mdspd) return;
+	
+	qq->mdspd = qq->mdspd2;
+	
+	if(qq->mdc == 0) return;		// count = 0
+	if(qq->mdc <= 127) {
+		qq->mdc--;
+	}
+
+	if(qq->step < 0) {
+		al = qq->mdepth - qq->step;
+		if(al >= 0) {
+			qq->step = -al;
+		} else {
+			if(qq->mdepth < 0) {
+				qq->step = 0;
+			} else {
+				qq->step = -127;
+			}
+		}
+	} else {
+		al = qq->step + qq->mdepth;
+		if(al >= 0) {
+			qq->step = al;
+		} else {
+			if(qq->mdepth < 0) {
+				qq->step = 0;
+			} else {
+				qq->step = 127;
+			}
+		}
+	}
+}
+
+
+void PMDWIN::swap(int *a, int *b)
+{
+	int		temp;
+	
+	temp = *a;
+	*a = *b;
+	*b = temp;
+}
+
+
+//=============================================================================
+//	LFO1<->LFO2 change
+//=============================================================================
+void PMDWIN::lfo_change(QQ *qq)
+{
+	swap(&qq->lfodat, &qq->_lfodat);
+	qq->lfoswi = ((qq->lfoswi & 0x0f) << 4) + (qq->lfoswi >> 4);
+	qq->extendmode = ((qq->extendmode & 0x0f) << 4) + (qq->extendmode >> 4);
+	
+	swap(&qq->delay, &qq->_delay);
+	swap(&qq->speed, &qq->_speed);
+	swap(&qq->step, &qq->_step);
+	swap(&qq->time, &qq->_time);
+	swap(&qq->delay2, &qq->_delay2);
+	swap(&qq->speed2, &qq->_speed2);
+	swap(&qq->step2, &qq->_step2);
+	swap(&qq->time2, &qq->_time2);
+	swap(&qq->mdepth, &qq->_mdepth);
+	swap(&qq->mdspd, &qq->_mdspd);
+	swap(&qq->mdspd2, &qq->_mdspd2);
+	swap(&qq->lfo_wave, &qq->_lfo_wave);
+	swap(&qq->mdc, &qq->_mdc);
+	swap(&qq->mdc2, &qq->_mdc2);
+}
+
+
+//=============================================================================
+//	ポルタメント計算なのね
+//=============================================================================
+void PMDWIN::porta_calc(QQ *qq)
+{
+	qq->porta_num += qq->porta_num2;
+	if(qq->porta_num3 == 0) return;
+	if(qq->porta_num3 > 0) {
+		qq->porta_num3--;
+		qq->porta_num++;
+	} else {
+		qq->porta_num3++;
+		qq->porta_num--;
+	}
+}
+
+
+//=============================================================================
+//  PSG/PCMのソフトウエアエンベロープ
+//=============================================================================
+int PMDWIN::soft_env(QQ *qq)
+{
+    int     i, cl;
+
+    if(qq->extendmode & 4) {
+        if(open_work.TimerAtime == pmdwork.lastTimerAtime) return 0;
+
+        cl = 0;
+        for(i = 0; i < open_work.TimerAtime - pmdwork.lastTimerAtime; i++) {
+            if(soft_env_main(qq)) {
+                cl = 1;
+            }
+        }
+        return cl;
+    } else {
+        return soft_env_main(qq);
+    }
+}
+
+
+int PMDWIN::soft_env_main(QQ *qq)
+{
+    int     dl;
+
+    if(qq->envf == -1) {
+        return ext_ssgenv_main(qq);
+    }
+
+    dl = qq->eenv_volume;
+    soft_env_sub(qq);
+    if(dl == qq->eenv_volume) {
+        return 0;
+    } else {
+        return -1;
+    }
+}
+
+
+int PMDWIN::soft_env_sub(QQ *qq)
+{
+    if(qq->envf == 0) {
+        // Attack
+        if(--qq->eenv_ar != 0) {
+            return 0;
+        }
+
+        qq->envf = 1;
+        qq->eenv_volume = qq->eenv_dr;
+        return 1;
+    }
+
+    if(qq->envf != 2) {
+        // Decay
+        if(qq->eenv_sr == 0) return 0;  // DR=0の時は減衰しない
+        if(--qq->eenv_sr != 0) return 0;
+        qq->eenv_sr = qq->eenv_src;
+        qq->eenv_volume--;
+
+        if(qq->eenv_volume >= -15 || qq->eenv_volume < 15) return 0;
+        qq->eenv_volume = -15;
+        return 0;
+    }
+
+
+    // Release
+    if(qq->eenv_rr == 0) {              // RR=0の時はすぐに音消し
+        qq->eenv_volume = -15;
+        return 0;
+    }
+
+    if(--qq->eenv_rr != 0) return 0;
+    qq->eenv_rr = qq->eenv_rrc;
+    qq->eenv_volume--;
+
+    if(qq->eenv_volume >= -15 && qq->eenv_volume < 15) return 0;
+    qq->eenv_volume = -15;
+    return 0;
+}
+
+
+//  拡張版
+int PMDWIN::ext_ssgenv_main(QQ *qq)
+{
+    int     dl;
+
+    if(qq->eenv_count == 0) return 0;
+
+    dl = qq->eenv_volume;
+    esm_sub(qq, qq->eenv_count);
+
+    if(dl == qq->eenv_volume) return 0;
+    return -1;
+}
+
+
+void PMDWIN::esm_sub(QQ *qq, int ah)
+{
+    if(--ah == 0) {
+        //  [[[ Attack Rate ]]]
+        if(qq->eenv_arc > 0) {
+            qq->eenv_volume += qq->eenv_arc;
+            if(qq->eenv_volume < 15) {
+                qq->eenv_arc = qq->eenv_ar-16;
+                return;
+            }
+
+            qq->eenv_volume = 15;
+            qq->eenv_count++;
+            if(qq->eenv_sl != 15) return;       // SL=0の場合はすぐSRに
+            qq->eenv_count++;
+            return;
+        } else {
+            if(qq->eenv_ar == 0) return;
+            qq->eenv_arc++;
+            return;
+        }
+    }
+
+    if(--ah == 0) {
+        //  [[[ Decay Rate ]]]
+        if(qq->eenv_drc > 0) {  // 0以下の場合はカウントCHECK
+            qq->eenv_volume -= qq->eenv_drc;
+            if(qq->eenv_volume < 0 || qq->eenv_volume < qq->eenv_sl) {
+                qq->eenv_volume = qq->eenv_sl;
+                qq->eenv_count++;
+                return;
+            }
+
+            if(qq->eenv_dr < 16) {
+                qq->eenv_drc = (qq->eenv_dr - 16) * 2;
+            } else {
+                qq->eenv_drc = qq->eenv_dr - 16;
+            }
+            return;
+        }
+
+        if(qq->eenv_dr == 0) return;
+        qq->eenv_drc++;
+        return;
+    }
+
+    if(--ah == 0) {
+        //  [[[ Sustain Rate ]]]
+        if(qq->eenv_src > 0) {  // 0以下の場合はカウントCHECK
+            if((qq->eenv_volume -= qq->eenv_src) < 0) {
+                qq->eenv_volume = 0;
+            }
+
+            if(qq->eenv_sr < 16) {
+                qq->eenv_src = (qq->eenv_sr - 16) * 2;
+            } else {
+                qq->eenv_src = qq->eenv_sr - 16;
+            }
+            return;
+        }
+
+        if(qq->eenv_sr == 0) return;    // SR=0?
+        qq->eenv_src++;
+        return;
+    }
+
+    //  [[[ Release Rate ]]]
+    if(qq->eenv_rrc > 0) {  // 0以下の場合はカウントCHECK
+        if((qq->eenv_volume -= qq->eenv_rrc) < 0) {
+            qq->eenv_volume = 0;
+        }
+
+        qq->eenv_rrc = (qq->eenv_rr) * 2 - 16;
+        return;
+    }
+
+    if(qq->eenv_rr == 0) return;
+    qq->eenv_rrc++;
+}
+
+
+//=============================================================================
+//	テンポ設定
+//=============================================================================
+void PMDWIN::settempo_b(void)
+{
+	if(open_work.tempo_d != open_work.TimerB_speed) {
+		open_work.TimerB_speed = open_work.tempo_d;
+		OPNASetReg(&opna, 0x26, open_work.TimerB_speed);
+	}
+}
+
+
+//=============================================================================
+//	小節のカウント
+//=============================================================================
+void PMDWIN::syousetu_count(void)
+{
+	if(open_work.opncount + 1 == open_work.syousetu_lng) {
+		open_work.syousetu++;
+		open_work.opncount = 0;
+	} else {
+		open_work.opncount++;
+	}
+}
+
+
+//=============================================================================
+//	OPN割り込み許可処理
+//=============================================================================
+void PMDWIN::opnint_start(void)
+{
+	memset(FMPart, 0, sizeof(FMPart));
+	memset(SSGPart, 0, sizeof(SSGPart));
+	memset(&RhythmPart, 0, sizeof(RhythmPart));
+
+	open_work.rhythmmask = 255;
+	pmdwork.rhydmy = 255;
+	data_init();
+	opn_init();
+	
+	PSGSetReg(&(opna.psg), 0x07, 0xbf);
+	mstop();
+	setint();
+	OPNASetReg(&opna, 0x29, 0x83);
+}
+
+
+//=============================================================================
+//	DATA AREA の イニシャライズ
+//=============================================================================
+void PMDWIN::data_init(void)
+{
+	int		i;
+	int		partmask, keyon_flag;
+	
+	for(i = 0; i < 6; i++) {
+		partmask = FMPart[i].partmask;
+		keyon_flag = FMPart[i].keyon_flag;
+		memset(&FMPart[i], 0, sizeof(QQ));
+		FMPart[i].partmask = partmask & 0x0f;
+		FMPart[i].keyon_flag = keyon_flag;
+		FMPart[i].onkai = 255;
+		FMPart[i].onkai_def = 255;
+	}
+	
+	for(i = 0; i < 3; i++) {
+		partmask = SSGPart[i].partmask;
+		keyon_flag = SSGPart[i].keyon_flag;
+		memset(&SSGPart[i], 0, sizeof(QQ));
+		SSGPart[i].partmask = partmask & 0x0f;
+		SSGPart[i].keyon_flag = keyon_flag;
+		SSGPart[i].onkai = 255;
+		SSGPart[i].onkai_def = 255;
+	}
+
+	partmask = RhythmPart.partmask;
+	keyon_flag = RhythmPart.keyon_flag;
+	memset(&RhythmPart, 0, sizeof(QQ));
+	RhythmPart.partmask = partmask & 0x0f;
+	RhythmPart.keyon_flag = keyon_flag;
+	RhythmPart.onkai = 255;
+	RhythmPart.onkai_def = 255;
+
+	pmdwork.tieflag = 0;
+	open_work.status = 0;
+	open_work.status2 = 0;
+	open_work.syousetu = 0;
+	open_work.opncount = 0;
+	open_work.TimerAtime = 0;
+	pmdwork.lastTimerAtime = 0;
+
+	pmdwork.omote_key[0] = 0;
+	pmdwork.omote_key[1] = 0;
+	pmdwork.omote_key[2] = 0;
+	pmdwork.ura_key[0] = 0;
+	pmdwork.ura_key[1] = 0;
+	pmdwork.ura_key[2] = 0;
+
+	pmdwork.fm3_alg_fb = 0;
+	pmdwork.af_check = 0;
+
+	open_work.kshot_dat = 0;
+	open_work.rshot_dat = 0;
+
+	pmdwork.slotdetune_flag = 0;
+	open_work.slot_detune1 = 0;
+	open_work.slot_detune2 = 0;
+	open_work.slot_detune3 = 0;
+	open_work.slot_detune4 = 0;
+	
+	pmdwork.slot3_flag = 0;
+	open_work.ch3mode = 0x3f;
+
+	pmdwork.fmsel = 0;
+
+	open_work.syousetu_lng = 96;
+
+	open_work.fm_voldown = open_work._fm_voldown;
+	open_work.ssg_voldown = open_work._ssg_voldown;
+	open_work.rhythm_voldown = open_work._rhythm_voldown;
+}
+
+
+//=============================================================================
+//	OPN INIT
+//=============================================================================
+void PMDWIN::opn_init(void)
+{
+	int		i;
+	OPNASetReg(&opna, 0x29, 0x83);
+	open_work.psnoi = 0;
+//@	if(effwork.effon == 0) {
+		PSGSetReg(&(opna.psg), 0x06, 0x00);
+		open_work.psnoi_last = 0;
+//@	}
+	
+	//------------------------------------------------------------------------
+	//	PAN/HARDLFO DEFAULT
+	//------------------------------------------------------------------------
+	OPNASetReg(&opna, 0xb4, 0xc0);
+	OPNASetReg(&opna, 0xb5, 0xc0);
+	OPNASetReg(&opna, 0xb6, 0xc0);
+	OPNASetReg(&opna, 0x1b4, 0xc0);
+	OPNASetReg(&opna, 0x1b5, 0xc0);
+	OPNASetReg(&opna, 0x1b6, 0xc0);
+	
+	open_work.port22h = 0x00;
+	OPNASetReg(&opna, 0x22, 0x00);
+	
+	//------------------------------------------------------------------------
+	//	Rhythm Default = Pan : Mid , Vol : 15
+	//------------------------------------------------------------------------
+	for(i = 0; i < 6; i++) {
+		open_work.rdat[i] = 0xcf;
+	}
+	OPNASetReg(&opna, 0x10, 0xff);
+	
+	//------------------------------------------------------------------------
+	//	リズムトータルレベル セット
+	//------------------------------------------------------------------------
+	open_work.rhyvol = 48*4*(256-open_work.rhythm_voldown)/1024;
+	OPNASetReg(&opna, 0x11, open_work.rhyvol);
+}
+
+
+//=============================================================================
+//	MUSIC STOP
+//=============================================================================
+void PMDWIN::mstop_f(void)
+{
+    int i;
+	if(open_work.TimerAflag || open_work.TimerBflag) {
+		pmdwork.music_flag |= 2;
+	} else {
+		mstop();
+	}
+
+	//memset(wavbuf2, 0, sizeof(wavbuf2));
+    for(i=0; i<nbufsample; i++) wavbuf2[i].left = 0;
+	upos = 0;
+}
+
+
+void PMDWIN::mstop(void)
+{
+	pmdwork.music_flag &= 0xfd;
+	open_work.play_flag = 0;
+	open_work.status2 = -1;
+	silence();
+}
+
+
+//=============================================================================
+//	ALL SILENCE
+//=============================================================================
+void PMDWIN::silence(void)
+{
+	OPNASetReg(&opna, 0x80, 0xff);			// FM Release = 15
+	OPNASetReg(&opna, 0x81, 0xff);
+	OPNASetReg(&opna, 0x82, 0xff);
+	OPNASetReg(&opna, 0x84, 0xff);
+	OPNASetReg(&opna, 0x85, 0xff);
+	OPNASetReg(&opna, 0x86, 0xff);
+	OPNASetReg(&opna, 0x88, 0xff);
+	OPNASetReg(&opna, 0x89, 0xff);
+	OPNASetReg(&opna, 0x8a, 0xff);
+	OPNASetReg(&opna, 0x8c, 0xff);
+	OPNASetReg(&opna, 0x8d, 0xff);
+	OPNASetReg(&opna, 0x8e, 0xff);
+	
+	OPNASetReg(&opna, 0x180, 0xff);
+	OPNASetReg(&opna, 0x181, 0xff);
+	OPNASetReg(&opna, 0x184, 0xff);
+	OPNASetReg(&opna, 0x185, 0xff);
+	OPNASetReg(&opna, 0x188, 0xff);
+	OPNASetReg(&opna, 0x189, 0xff);
+	OPNASetReg(&opna, 0x18c, 0xff);
+	OPNASetReg(&opna, 0x18d, 0xff);
+	
+	OPNASetReg(&opna, 0x182, 0xff);
+	OPNASetReg(&opna, 0x186, 0xff);
+	OPNASetReg(&opna, 0x18a, 0xff);
+	OPNASetReg(&opna, 0x18e, 0xff);
+	
+	OPNASetReg(&opna, 0x28, 0x00);			// FM KEYOFF
+	OPNASetReg(&opna, 0x28, 0x01);
+	OPNASetReg(&opna, 0x28, 0x02);
+	OPNASetReg(&opna, 0x28, 0x04);			// FM KEYOFF [URA]
+	OPNASetReg(&opna, 0x28, 0x05);
+	OPNASetReg(&opna, 0x28, 0x06);
+
+// 2003.11.30 プチノイズ対策のため
+//@	if(effwork.effon == 0) {
+		PSGSetReg(&(opna.psg), 0x07, 0xbf);
+		PSGSetReg(&(opna.psg), 0x08, 0);	// 2003.11.30 プチノイズ対策のため
+		PSGSetReg(&(opna.psg), 0x09, 0);	// 2003.11.30 プチノイズ対策のため
+		PSGSetReg(&(opna.psg), 0x0a, 0);	// 2003.11.30 プチノイズ対策のため
+//@	} else {
+//@		PSGSetReg(&(opna.psg), 0x07, (PSGGetReg(&(opna.psg), 0x07) & 0x3f) | 0x9b);
+//@	}
+	
+	OPNASetReg(&opna, 0x10, 0xff);			// Rhythm dump
+	OPNASetReg(&opna, 0x110, 0x80);			// TA/TB/EOS を RESET
+	OPNASetReg(&opna, 0x110, 0x18);			// TIMERB/A/EOSのみbit変化あり
+}
+
+
+//=============================================================================
+//	演奏開始(function)
+//=============================================================================
+void PMDWIN::mstart_f(void)
+{
+	if(open_work.TimerAflag || open_work.TimerBflag) {
+		pmdwork.music_flag |= 1;			//	TA/TB処理中は 実行しない
+		return;
+	}
+
+	mstart();
+}
+
+
+//=============================================================================
+//	演奏開始
+//=============================================================================
+void PMDWIN::mstart(void)
+{
+	// TimerB = 0 に設定し、Timer Reset(曲の長さを毎回そろえるため)
+	open_work.tempo_d = 0;
+	settempo_b();	
+	OPNASetReg(&opna, 0x27, 0);	// TIMER RESET(timerA,Bとも)
+
+	//------------------------------------------------------------------------
+	//	演奏停止
+	//------------------------------------------------------------------------
+	pmdwork.music_flag &= 0xfe;
+	mstop();
+
+	//------------------------------------------------------------------------
+	//	バッファ初期化
+	//------------------------------------------------------------------------
+	pos2 = (char *)wavbuf2;	// buf に余っているサンプルの先頭位置
+	us2	= 0;				// buf に余っているサンプル数
+	upos = 0;				// 演奏開始からの時間(μsec)
+	
+	//------------------------------------------------------------------------
+	//	演奏準備
+	//------------------------------------------------------------------------
+	data_init();
+	play_init();
+
+	//------------------------------------------------------------------------
+	//	OPN初期化
+	//------------------------------------------------------------------------
+	opn_init();
+
+	//------------------------------------------------------------------------
+	//	音楽の演奏を開始
+	//------------------------------------------------------------------------
+	setint();
+	open_work.play_flag = 1;
+}
+
+
+//=============================================================================
+//	各パートのスタートアドレス及び初期値をセット
+//=============================================================================
+void PMDWIN::play_init(void)
+{
+	int		i;
+	ushort	*p;
+	
+	//2.6追加分
+	if(*open_work.mmlbuf != 2*(max_part2+1)) {
+		open_work.prgdat_adr = open_work.mmlbuf + *(ushort *)(&open_work.mmlbuf[2*(max_part2+1)]);
+		open_work.prg_flg = 1;
+	} else {
+		open_work.prg_flg = 0;
+	}
+	
+	p = (ushort *)open_work.mmlbuf;
+
+	//	Part 0,1,2,3,4,5(FM1〜6)の時
+	for(i = 0; i < 6; i++) {
+		if(open_work.mmlbuf[*p] == 0x80) {		//先頭が80hなら演奏しない
+			FMPart[i].address = NULL;
+		} else {
+			FMPart[i].address = &open_work.mmlbuf[*p];
+		}
+		
+		FMPart[i].leng = 1;
+		FMPart[i].keyoff_flag = -1;		// 現在keyoff中
+		FMPart[i].mdc = -1;				// MDepth Counter (無限)
+		FMPart[i].mdc2 = -1;			// 同上
+		FMPart[i]._mdc = -1;			// 同上
+		FMPart[i]._mdc2 = -1;			// 同上
+		FMPart[i].onkai = 255;			// rest
+		FMPart[i].onkai_def = 255;		// rest
+		FMPart[i].volume = 108;			// FM  VOLUME DEFAULT= 108
+		FMPart[i].fmpan = 0xc0;			// FM PAN = Middle
+		FMPart[i].slotmask = 0xf0;		// FM SLOT MASK
+		FMPart[i].neiromask = 0xff;		// FM Neiro MASK
+		p++;
+	}
+	
+	//	Part 6,7,8(PSG1〜3)の時
+	for(i = 0; i < 3; i++) {
+		if(open_work.mmlbuf[*p] == 0x80) {		//先頭が80hなら演奏しない
+			SSGPart[i].address = NULL;
+		} else {
+			SSGPart[i].address = &open_work.mmlbuf[*p];
+		}
+		
+		SSGPart[i].leng = 1;
+		SSGPart[i].keyoff_flag = -1;	// 現在keyoff中
+		SSGPart[i].mdc = -1;			// MDepth Counter (無限)
+		SSGPart[i].mdc2 = -1;			// 同上
+		SSGPart[i]._mdc = -1;			// 同上
+		SSGPart[i]._mdc2 = -1;			// 同上
+		SSGPart[i].onkai = 255;			// rest
+		SSGPart[i].onkai_def = 255;		// rest
+		SSGPart[i].volume = 8;			// PSG VOLUME DEFAULT= 8
+		SSGPart[i].psgpat = 7;			// PSG = TONE
+		SSGPart[i].envf = 3;			// PSG ENV = NONE/normal
+		p++;
+    }
+    p++;
+	
+	//	Part 10(Rhythm)の時
+	if(open_work.mmlbuf[*p] == 0x80) {		//先頭が80hなら演奏しない
+		RhythmPart.address = NULL;
+	} else {
+		RhythmPart.address = &open_work.mmlbuf[*p];
+	}
+	
+	RhythmPart.leng = 1;
+	RhythmPart.keyoff_flag = -1;	// 現在keyoff中
+	RhythmPart.mdc = -1;			// MDepth Counter (無限)
+	RhythmPart.mdc2 = -1;			// 同上
+	RhythmPart._mdc = -1;			// 同上
+	RhythmPart._mdc2 = -1;			// 同上
+	RhythmPart.onkai = 255;			// rest
+	RhythmPart.onkai_def = 255;		// rest
+	RhythmPart.volume = 15;			// PPSDRV volume
+	p++;
+	
+	//------------------------------------------------------------------------
+	//	Rhythm のアドレステーブルをセット
+	//------------------------------------------------------------------------
+	
+	open_work.radtbl = (ushort *)&open_work.mmlbuf[*p];
+	open_work.rhyadr = (uchar *)&pmdwork.rhydmy;
+}
+
+
+//=============================================================================
+//	インタラプト 設定
+//	FM音源専用
+//=============================================================================
+void PMDWIN::setint(void)
+{
+	//
+	// OPN割り込み初期設定
+	//
+	
+	open_work.tempo_d = 200;
+	open_work.tempo_d_push = 200;
+	
+	calc_tb_tempo();
+	settempo_b();
+	
+	OPNASetReg(&opna, 0x25, 0x00);			// TIMER A SET (9216μs固定)
+	OPNASetReg(&opna, 0x24, 0x00);			// 一番遅くて丁度いい
+	OPNASetReg(&opna, 0x27, 0x3f);			// TIMER ENABLE
+	
+	//
+	// 小節カウンタリセット
+	//
+	
+	open_work.opncount = 0;
+	open_work.syousetu = 0;
+	open_work.syousetu_lng = 96;
+}
+
+
+//=============================================================================
+//	T->t 変換
+//		input	[tempo_d]
+//		output	[tempo_48]
+//=============================================================================
+void PMDWIN::calc_tb_tempo(void)
+{
+//	TEMPO = 0x112C / [ 256 - TB ]	timerB -> tempo
+	int temp;
+	
+	if(256 - open_work.tempo_d == 0) {
+		temp = 255;
+	} else {
+		temp = (0x112c * 2 / (256 - open_work.tempo_d) + 1) / 2;
+		if(temp > 255) temp = 255;
+	}
+	
+	open_work.tempo_48 = temp;
+	open_work.tempo_48_push = temp;
+}
+
+
+//=============================================================================
+//	t->T 変換
+//		input	[tempo_48]
+//		output	[tempo_d]
+//=============================================================================
+void PMDWIN::calc_tempo_tb(void)
+{
+	int		al;
+
+	//	TB = 256 - [ 112CH / TEMPO ]	tempo -> timerB
+	
+	if(open_work.tempo_48 >= 18) {
+		al = 256 - 0x112c / open_work.tempo_48;
+		if(0x112c % open_work.tempo_48 >= 128) {
+			al--;
+		}
+		//al = 256 - (0x112c * 2 / open_work.tempo_48 + 1) / 2;
+	} else {
+		al = 0;
+	}
+	open_work.tempo_d = al;
+	open_work.tempo_d_push = al;
+}
+
+
+//=============================================================================
+//	曲の読み込みその3(メモリから、カレントディレクトリあり)
+//=============================================================================
+int PMDWIN::music_load3(uchar *musdata, unsigned int size)
+{
+	if(size > sizeof(mdataarea)) {
+		return ERR_WRONG_MUSIC_FILE;
+	}
+	
+	// 020120 ヘッダ解析のみ Towns に対応
+	if((musdata[0] > 0x0f && musdata[0] != 0xff) || (musdata[1] != 0x18 && musdata[1] != 0x1a) || musdata[2]) {
+		return ERR_WRONG_MUSIC_FILE;		// not PMD data
+	}
+	
+	// メモリを 0x00 で初期化する
+	//memset(mdataarea, 0x00, sizeof(mdataarea));
+	memcpy(mdataarea, musdata, size);
+	return PMDWIN_OK;
+}
+
+
+//=============================================================================
+//	初期化
+//=============================================================================
+bool PMDWIN::init(void)
+{
+    int i;
+    struct timeval tv;
+	open_work.rate = SOUND_44K;
+
+	// OPEN_WORK メンバの初期化
+	open_work.rhyvol = 0x3c;
+	open_work.TimerBflag = 0;
+	open_work.TimerAflag = 0;
+	open_work.TimerB_speed = 0x100;
+	open_work.port22h = 0;
+
+	if(OPNAInit(&opna, OPNAClock, SOUND_44K, true) == false) return false;
+	SetVolumeFM(&opna, 0);
+	SetVolumePSG(&(opna.psg), 0);
+
+    // Init RNG state
+    gettimeofday(&tv, NULL);
+    lfg_srand(tv.tv_sec);
+
+    for(i=0; i<nbufsample; i++) {
+        wavbuf2[i].left = 0;
+    }
+    pos2 = (char *)wavbuf2;
+    us2 = 0;
+    upos = 0;
+
+	//----------------------------------------------------------------
+	//	変数の初期化
+	//----------------------------------------------------------------
+	
+	open_work.MusPart[ 0] = &FMPart[0];
+	open_work.MusPart[ 1] = &FMPart[1];
+	open_work.MusPart[ 2] = &FMPart[2];
+	open_work.MusPart[ 3] = &FMPart[3];
+	open_work.MusPart[ 4] = &FMPart[4];
+	open_work.MusPart[ 5] = &FMPart[5];
+	open_work.MusPart[ 6] = &SSGPart[0];
+	open_work.MusPart[ 7] = &SSGPart[1];
+	open_work.MusPart[ 8] = &SSGPart[2];
+	open_work.MusPart[ 9] = &RhythmPart;
+
+	mdataarea[0] = 0;
+	for(i = 0; i < 12; i++) {
+		mdataarea[i*2+1] = 0x18;
+		mdataarea[i*2+2] = 0;
+	}
+	mdataarea[25] = 0x80;
+	
+	open_work.fm_voldown = fmvd_init;		// FM_VOLDOWN
+	open_work._fm_voldown = fmvd_init;		// FM_VOLDOWN
+	open_work.ssg_voldown = 0;				// SSG_VOLDOWN
+	open_work._ssg_voldown = 0;				// SSG_VOLDOWN
+	open_work.rhythm_voldown = 0;			// RHYTHM_VOLDOWN
+	open_work._rhythm_voldown = 0;			// RHYTHM_VOLDOWN
+	
+	memset(FMPart, 0, sizeof(FMPart));
+	memset(SSGPart, 0, sizeof(SSGPart));
+	memset(&RhythmPart, 0, sizeof(RhythmPart));
+
+	pmdwork.music_flag =  0;
+    pmdwork.ppsdrv_flag = false;    // PPSDRV FLAG
+	
+	//----------------------------------------------------------------
+	//	曲データ,音色データ格納番地を設定
+	//----------------------------------------------------------------
+	open_work.mmlbuf = &mdataarea[1];
+	
+	//----------------------------------------------------------------
+	//	効果音/FMINT/EFCINTを初期化
+	//----------------------------------------------------------------
+	effwork.effon = 0;
+	effwork.psgefcnum = 0xff;
+	
+	//----------------------------------------------------------------
+	//	088/188/288/388 (同INT番号のみ) を初期設定
+	//----------------------------------------------------------------
+	OPNASetReg(&opna, 0x29, 0x00);
+	OPNASetReg(&opna, 0x24, 0x00);
+	OPNASetReg(&opna, 0x25, 0x00);
+	OPNASetReg(&opna, 0x26, 0x00);
+	OPNASetReg(&opna, 0x27, 0x3f);
+	
+	//----------------------------------------------------------------
+	//	OPN割り込み開始
+	//----------------------------------------------------------------
+	opnint_start();	
+
+	return true;
+}
+
+
+//=============================================================================
+//	合成周波数の設定
+//=============================================================================
+void PMDWIN::setpcmrate(unsigned int rate)
+{
+	if(rate == SOUND_55K || rate == SOUND_55K_2) {
+		open_work.rate = SOUND_44K;
+		open_work.fmcalc55k = true;
+	} else {
+		open_work.rate = rate;
+		open_work.fmcalc55k = false;
+	}
+	OPNASetRate(&opna, open_work.rate, open_work.fmcalc55k);
+}
+
+
+//=============================================================================
+// Mask to enable/disable usage of FM (bit 0), SSG (bit 1) and Rhythm (bit 2)
+// on the OPNA. Mostly for testing purposes.
+//=============================================================================
+void PMDWIN::setdevmask(uint8_t mask)
+{
+    opna.devmask = mask;
+}
+
+
+void PMDWIN::setchanmask(uint32_t mask)
+{
+    OPNASetChannelMask(&opna, mask);
+}
+
+
+//=============================================================================
+//	演奏開始
+//=============================================================================
+void PMDWIN::music_start(void)
+{
+	mstart_f();
+}
+
+
+//=============================================================================
+//	演奏停止
+//=============================================================================
+void PMDWIN::music_stop(void)
+{
+	mstop_f();
+}
+
+
+//=============================================================================
+//	PCM データ(wave データ)の取得
+//=============================================================================
+void PMDWIN::getpcmdata(short *buf, int nsamples)
+{
+	int	copysamples = 0;				// コピー済みのサンプル数
+	int				i, us;
+
+	do {
+		if(nsamples - copysamples <= us2) {
+			memcpy(buf, pos2, (nsamples - copysamples)*2);
+			us2 -= (nsamples - copysamples);
+			pos2 += (nsamples - copysamples)*2;
+			copysamples = nsamples;
+		} else {
+			memcpy(buf, pos2, us2 * 2);
+			buf += us2;
+			copysamples += us2;
+			pos2 = (char *)wavbuf2;
+
+			if(ReadStatus(&opna) & 0x01) {
+				TimerA_main();
+			}
+
+			if(ReadStatus(&opna) & 0x02) {
+				TimerB_main();
+			}
+
+			OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30);	// TIMER RESET(timerA,Bとも)
+
+
+			us = OPNAGetNextEvent(&opna);
+			us2 = (int)((float)us * open_work.rate / 1000000.0f);
+			OPNATimerCount(&opna, us);
+
+            int32_t tmpbuf[8192];
+            for(i=0; i<us2; i++) tmpbuf[i] = 0;
+			OPNAMix(&opna, (int32_t*)tmpbuf, us2);
+			upos += us;
+			
+			for(i = 0; i < us2; i++) {
+				wavbuf2[i].left = Limit(tmpbuf[i], 32767, -32768);
+            }
+		}
+	} while(copysamples < nsamples);
+}
+
+
+//=============================================================================
+//	FM で 55kHz合成、一次補完するかどうかの設定
+//=============================================================================
+void PMDWIN::setfmcalc55k(bool flag)
+{
+	open_work.fmcalc55k = flag;
+	OPNASetRate(&opna, open_work.rate, open_work.fmcalc55k);
+}
+
+
+unsigned int PMDWIN::getstatus(char *buf, size_t bufsize)
+{
+    char *p = buf;
+    unsigned int i = 0, len;
+    while(i < 6) {
+        *p++ = 'F';
+        *p++ = 'M';
+        *p++ = (i + 0x30);
+        *p++ = ':';
+        *p++ = ' ';
+        *p++ = notes[pmdstatus[i]][0];
+        *p++ = notes[pmdstatus[i]][1];
+        *p++ = ' ';
+        i++;
+    }
+    while(i < 9) {
+        *p++ = 'P';
+        *p++ = 'S';
+        *p++ = 'G';
+        *p++ = (i + 0x30 - 6);
+        *p++ = ':';
+        *p++ = ' ';
+        *p++ = notes[pmdstatus[i]][0];
+        *p++ = notes[pmdstatus[i]][1];
+        *p++ = ' ';
+        i++;
+    }
+    p -= 3;
+    *p++ = (pmdstatus[8] + 0x30);
+    *p++ = '\0';
+    len = (p - buf);
+    return len;
+}
+
+
+//=============================================================================
+//	メモの取得(内部動作)
+//=============================================================================
+char* PMDWIN::_getmemo(char *dest, uchar *musdata, int size, int al)
+{
+	uchar	*si, *mmlbuf;
+	int		i, dx;
+	int		maxsize;
+	
+	if(musdata == NULL || size == 0) {
+		mmlbuf = open_work.mmlbuf;
+		maxsize = sizeof(mdataarea) - 1;
+	} else {
+		mmlbuf = &musdata[1];
+		maxsize = size - 1;
+	}
+	if(maxsize < 2) {
+		*dest = '\0';			// 	曲データが不正
+		return NULL;
+	}
+	
+	if(mmlbuf[0] != 0x1a || mmlbuf[1] != 0x00) {
+		*dest = '\0';			// 	音色がないfile=メモのアドレス取得不能
+		return dest;
+	}
+	
+	if(maxsize < 0x18+1) {
+		*dest = '\0';			// 	曲データが不正
+		return NULL;
+	}
+	
+	if(maxsize < *(ushort *)&mmlbuf[0x18] - 4 + 3) {
+		*dest = '\0';			// 	曲データが不正
+		return NULL;
+	}
+
+	si = &mmlbuf[*(ushort *)&mmlbuf[0x18] - 4];
+	if(*(si + 2) != 0x40) {
+		if(*(si + 3) != 0xfe || *(si + 2) < 0x41) {
+			*dest = '\0';			// 	音色がないfile=メモのアドレス取得不能
+			return dest;
+		}
+	}
+	
+	if(*(si + 2) >= 0x42) {
+		al++;
+	}
+	
+	if(*(si + 2) >= 0x48) {
+		al++;
+	}
+	
+	if(al < 0) {
+		*dest = '\0';				//	登録なし
+		return dest;
+	}
+	
+	si = &mmlbuf[*(ushort *)si];
+	
+	for(i = 0; i <= al; i++) {
+		if(maxsize < si - mmlbuf + 1) {
+			*dest = '\0';			// 	曲データが不正
+			return NULL;
+		}
+		
+		dx = *(ushort *)si;
+		if(dx == 0) {
+			*dest = '\0';
+			return dest;
+		}
+		
+		if(maxsize < dx) {
+			*dest = '\0';			// 	曲データが不正
+			return NULL;
+		}
+		
+		if(mmlbuf[dx] == '/') {
+			*dest = '\0';
+			return dest;
+		}
+		
+		si += 2;
+	}
+	
+	for(i = dx; i < maxsize; i++) {
+		if(mmlbuf[i] == '\0') break;
+	}
+
+	// 終端の \0 がない場合
+	if(i >= maxsize) {
+		memcpy(dest, &mmlbuf[dx], maxsize - dx);
+		dest[maxsize - dx - 1] = '\0';
+	} else {
+        memcpy(dest, &mmlbuf[dx], i);
+        dest[i] = '\0';
+	}
+	return dest;
+}
+
+/*
+   Conversion between SJIS codes (s1,s2) and JISX0208 codes (c1,c2):
+   Example. (s1,s2) = 0x8140, (c1,c2) = 0x2121.
+   0x81 <= s1 <= 0x9F || 0xE0 <= s1 <= 0xEA,
+   0x40 <= s2 <= 0x7E || 0x80 <= s2 <= 0xFC,
+   0x21 <= c1 <= 0x74, 0x21 <= c2 <= 0x7E.
+   Invariant:
+     94*2*(s1 < 0xE0 ? s1-0x81 : s1-0xC1) + (s2 < 0x80 ? s2-0x40 : s2-0x41)
+     = 94*(c1-0x21)+(c2-0x21)
+   Conversion (s1,s2) -> (c1,c2):
+     t1 := (s1 < 0xE0 ? s1-0x81 : s1-0xC1)
+     t2 := (s2 < 0x80 ? s2-0x40 : s2-0x41)
+     c1 := 2*t1 + (t2 < 0x5E ? 0 : 1) + 0x21
+     c2 := (t2 < 0x5E ? t2 : t2-0x5E) + 0x21
+   Conversion (c1,c2) -> (s1,s2):
+     t1 := (c1 - 0x21) >> 1
+     t2 := ((c1 - 0x21) & 1) * 0x5E + (c2 - 0x21)
+     s1 := (t1 < 0x1F ? t1+0x81 : t1+0xC1)
+     s2 := (t2 < 0x3F ? t2+0x40 : t2+0x41)
+ */
+
+static int sjis_to_ucs4(uint32_t *pwc, const uint8_t *s, int n)
+{
+  uint8_t c = *s;
+  if (c <= 0x80) {
+    if (c == 0x5c)
+      *pwc = (uint32_t) 0x00a5;
+    else if (c == 0x7e)
+      *pwc = (uint32_t) 0x203e;
+    else
+      *pwc = (uint32_t) c;
+    return 1;
+  } else if (c >= 0xa1 && c <= 0xdf) {
+    *pwc = (uint32_t) c + 0xfec0;
+    return 1;
+  } else {
+    uint8_t s1, s2;
+    s1 = c;
+    if ((s1 >= 0x81 && s1 <= 0x9f) || (s1 >= 0xe0 && s1 <= 0xea)) {
+      if (n < 2)
+        return -2;
+      s2 = s[1];
+      if ((s2 >= 0x40 && s2 <= 0x7e) || (s2 >= 0x80 && s2 <= 0xfc)) {
+        uint8_t t1 = (s1 < 0xe0 ? s1-0x81 : s1-0xc1);
+        uint8_t t2 = (s2 < 0x80 ? s2-0x40 : s2-0x41);
+        uint8_t buf[2];
+        buf[0] = 2*t1 + (t2 < 0x5e ? 0 : 1) + 0x21;
+        buf[1] = (t2 < 0x5e ? t2 : t2-0x5e) + 0x21;
+        return jisx0208_mbtowc(pwc,buf,2);
+      }
+    } else if (s1 >= 0xf0 && s1 <= 0xf9) {
+      /* User-defined range. See
+       * Ken Lunde's "CJKV Information Processing", table 4-66, p. 206. */
+      if (n < 2)
+        return -2;
+      s2 = s[1];
+      if ((s2 >= 0x40 && s2 <= 0x7e) || (s2 >= 0x80 && s2 <= 0xfc)) {
+        *pwc = 0xe000 + 188*(s1 - 0xf0) + (s2 < 0x80 ? s2-0x40 : s2-0x41);
+        return 2;
+      }
+    }
+    return -1;
+  }
+}
+
+static int ucs4_to_utf8(uint8_t *utf8, uint32_t ucs4)
+{
+  if (ucs4 <= 0x0000007fL) {
+    utf8[0] = ucs4;
+    return 1;
+  } else if (ucs4 <= 0x000007ffL) {
+    utf8[0] = 0xc0 | ((ucs4 >>  6) & 0x1f);
+    utf8[1] = 0x80 | ((ucs4 >>  0) & 0x3f);
+    return 2;
+  } else if (ucs4 <= 0x0000ffffL) {
+    utf8[0] = 0xe0 | ((ucs4 >> 12) & 0x0f);
+    utf8[1] = 0x80 | ((ucs4 >>  6) & 0x3f);
+    utf8[2] = 0x80 | ((ucs4 >>  0) & 0x3f);
+    return 3;
+  }
+
+  /* default */
+  utf8[0] = '?';
+  return 1;
+}
+
+static size_t sjis_to_utf8(uint8_t *outbuf, uint8_t *inbuf, size_t insize) {
+    uint32_t wc = 0;
+    uint8_t *utf8str = outbuf;
+    uint8_t *utf8str_start = utf8str;
+    int ret;
+    while(insize > 0) {
+        ret = sjis_to_ucs4(&wc, inbuf, insize);
+        if(ret < 0) {
+            inbuf++; insize--;
+            continue;
+        }
+        utf8str += ucs4_to_utf8(utf8str, wc);
+        inbuf += ret;
+        insize -= ret;
+    }
+    return (utf8str - utf8str_start);
+}
+
+char* PMDWIN::_getmemo3(char *dest, uchar *musdata, int size, int al)
+{
+    char buf[mdata_def*1024];
+    _getmemo((char *)buf, musdata, size, al);
+    sjis_to_utf8((uint8_t*)dest, (uint8_t*)buf, strlen(buf)+1);
+    return dest;
+}
+
+
+//=============================================================================
+//	パートのマスク
+//=============================================================================
+int PMDWIN::maskon(unsigned int ch)
+{
+	int		ah, fmseltmp;
+	
+	
+	if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) {
+		return ERR_WRONG_PARTNO;		// part number error
+	}
+
+	if(part_table[ch][0] < 0) {
+		open_work.rhythmmask = 0;	// Rhythm音源をMask
+		OPNASetReg(&opna, 0x10, 0xff);	// Rhythm音源を全部Dump
+	} else {
+		fmseltmp = pmdwork.fmsel;
+		if(open_work.MusPart[ch]->partmask == 0 && open_work.play_flag) {
+			if(part_table[ch][2] == 0) {
+				pmdwork.partb = part_table[ch][1];
+				pmdwork.fmsel = 0;
+				silence_fmpart(open_work.MusPart[ch]);	// 音を完璧に消す
+			} else if(part_table[ch][2] == 1) {
+				pmdwork.partb = part_table[ch][1];
+				pmdwork.fmsel = 0x100;
+				silence_fmpart(open_work.MusPart[ch]);	// 音を完璧に消す
+			} else if(part_table[ch][2] == 2) {
+				pmdwork.partb = part_table[ch][1];
+				ah =  1 << (pmdwork.partb - 1);
+				ah |= (ah << 3);
+												// PSG keyoff
+				PSGSetReg(&(opna.psg), 0x07, ah | PSGGetReg(&(opna.psg), 0x07));
+			} else if(part_table[ch][2] == 4) {
+				if(effwork.psgefcnum < 11) {
+					effend();
+				}
+            }
+		}
+		open_work.MusPart[ch]->partmask |= 1;
+		pmdwork.fmsel = fmseltmp;
+	}
+	return PMDWIN_OK;
+}
+
+
+//=============================================================================
+//	パートのマスク解除
+//=============================================================================
+int PMDWIN::maskoff(unsigned int ch)
+{
+	int		fmseltmp;
+	
+	if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) {
+		return ERR_WRONG_PARTNO;		// part number error
+	}
+
+	if(part_table[ch][0] < 0) {
+		open_work.rhythmmask = 0xff;
+	} else {
+		if(open_work.MusPart[ch]->partmask == 0) return ERR_NOT_MASKED;	// マスクされていない
+											// 効果音でまだマスクされている
+		if((open_work.MusPart[ch]->partmask &= 0xfe) != 0) return ERR_EFFECT_USED;
+											// 曲が止まっている
+		if(open_work.play_flag == 0) return ERR_MUSIC_STOPPED;
+
+		fmseltmp = pmdwork.fmsel;
+		if(open_work.MusPart[ch]->address != NULL) {
+			if(part_table[ch][2] == 0) {		// FM音源(表)
+				pmdwork.fmsel = 0;
+				pmdwork.partb = part_table[ch][1];
+				neiro_reset(open_work.MusPart[ch]);
+			} else if(part_table[ch][2] == 1) {	// FM音源(裏)
+				pmdwork.fmsel = 0x100;
+				pmdwork.partb = part_table[ch][1];
+				neiro_reset(open_work.MusPart[ch]);
+			}
+		}
+		pmdwork.fmsel = fmseltmp;
+
+	}
+	return PMDWIN_OK;
+}
+
+
+//=============================================================================
+//	FM Volume Down の設定
+//=============================================================================
+void PMDWIN::setfmvoldown(int voldown)
+{
+	open_work.fm_voldown = open_work._fm_voldown = voldown;
+}
+
+
+//=============================================================================
+//	SSG Volume Down の設定
+//=============================================================================
+void PMDWIN::setssgvoldown(int voldown)
+{
+	open_work.ssg_voldown = open_work._ssg_voldown = voldown;
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の設定
+//=============================================================================
+void PMDWIN::setrhythmvoldown(int voldown)
+{
+	open_work.rhythm_voldown = open_work._rhythm_voldown = voldown;
+	open_work.rhyvol = 48*4*(256-open_work.rhythm_voldown)/1024;
+	OPNASetReg(&opna, 0x11, open_work.rhyvol);
+
+}
+
+
+//=============================================================================
+//	FM Volume Down の取得
+//=============================================================================
+int PMDWIN::getfmvoldown(void)
+{
+	return open_work.fm_voldown;
+}
+
+
+//=============================================================================
+//	FM Volume Down の取得(その2)
+//=============================================================================
+int PMDWIN::getfmvoldown2(void)
+{
+	return open_work._fm_voldown;
+}
+
+
+//=============================================================================
+//	SSG Volume Down の取得
+//=============================================================================
+int PMDWIN::getssgvoldown(void)
+{
+	return open_work.ssg_voldown;
+}
+
+
+//=============================================================================
+//	SSG Volume Down の取得(その2)
+//=============================================================================
+int PMDWIN::getssgvoldown2(void)
+{
+	return open_work._ssg_voldown;
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の取得
+//=============================================================================
+int PMDWIN::getrhythmvoldown(void)
+{
+	return open_work.rhythm_voldown;
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の取得(その2)
+//=============================================================================
+int PMDWIN::getrhythmvoldown2(void)
+{
+	return open_work._rhythm_voldown;
+}
+
+
+//=============================================================================
+//	再生位置の移動(pos : ms)
+//=============================================================================
+void PMDWIN::setpos(int pos)
+{
+	uint64_t	_pos;
+	int		us;
+
+	_pos = (uint64_t)pos * 1000;			// (ms -> usec への変換)
+
+	if(upos > _pos) {
+		mstart();
+		pos2 = (char *)wavbuf2;	// buf に余っているサンプルの先頭位置
+		us2	= 0;				// buf に余っているサンプル数
+		upos = 0;				// 演奏開始からの時間(μsec)
+	}
+
+	while(upos < _pos) {
+		if(ReadStatus(&opna) & 0x01) {
+			TimerA_main();
+		}
+
+		if(ReadStatus(&opna) & 0x02) {
+			TimerB_main();
+		}
+
+		OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30);	// TIMER RESET(timerA,Bとも)
+
+		us = OPNAGetNextEvent(&opna);
+		OPNATimerCount(&opna, us);
+		upos += us;			
+	}
+
+	if(open_work.status2 == -1) {
+		silence();
+	}
+}
+
+
+//=============================================================================
+//	再生位置の移動(pos : count 単位)
+//=============================================================================
+void PMDWIN::setpos2(int pos)
+{
+	int		us;
+
+	if(open_work.syousetu_lng * open_work.syousetu + open_work.opncount > pos) {
+		mstart();
+		pos2 = (char *)wavbuf2;	// buf に余っているサンプルの先頭位置
+		us2	= 0;				// buf に余っているサンプル数
+	}
+
+	while(open_work.syousetu_lng * open_work.syousetu + open_work.opncount < pos) {
+		if(ReadStatus(&opna) & 0x01) {
+			TimerA_main();
+		}
+
+		if(ReadStatus(&opna) & 0x02) {
+			TimerB_main();
+		}
+
+		OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30);	// TIMER RESET(timerA,Bとも)
+
+		us = OPNAGetNextEvent(&opna);
+		OPNATimerCount(&opna, us);
+	}
+
+	if(open_work.status2 == -1) {
+		silence();
+	}
+}
+
+
+//=============================================================================
+//	再生位置の取得(pos : ms)
+//=============================================================================
+int PMDWIN::getpos(void)
+{
+	return (int)(upos / 1000);
+}
+
+
+//=============================================================================
+//	再生位置の取得(pos : count 単位)
+//=============================================================================
+int PMDWIN::getpos2(void)
+{
+	return open_work.syousetu_lng * open_work.syousetu + open_work.opncount;
+}
+
+
+//=============================================================================
+//	曲の長さの取得(pos : ms)
+//=============================================================================
+bool PMDWIN::getlength(uint *length, uint *loop)
+{
+	int		us;
+	mstart();
+	upos = 0;				// 演奏開始からの時間(μsec)
+	*length = 0;
+
+	do {
+		if(ReadStatus(&opna) & 0x01) {
+			TimerA_main();
+		}
+
+		if(ReadStatus(&opna) & 0x02) {
+			TimerB_main();
+		}
+
+		OPNASetReg(&opna, 0x27, open_work.ch3mode | 0x30);	// TIMER RESET(timerA,Bとも)
+
+		us = OPNAGetNextEvent(&opna);
+		OPNATimerCount(&opna, us);
+		upos += us;
+		if(open_work.status2 == 1 && *length == 0) {	// ループ時
+			*length = (int)(upos / 1000);
+		} else if(open_work.status2 == -1) {			// ループなし終了時
+			*length = (int)(upos / 1000);
+			*loop = 0;
+			mstop();
+			return true;
+		} else if(getpos2() >= 65536) {		// 65536クロック以上なら強制終了
+			*length = (int)(upos / 1000);
+			*loop = *length;
+			return true;
+		}
+	} while(open_work.status2 < 2);
+
+	*loop = (int)(upos / 1000) - *length;
+	mstop();
+	return true;
+}
+
+
+//=============================================================================
+//	ループ回数の取得
+//=============================================================================
+int PMDWIN::getloopcount(void)
+{
+	return open_work.status2;
+}
+
+
+//=============================================================================
+//	OPEN_WORKのポインタの取得
+//=============================================================================
+OPEN_WORK* PMDWIN::getopenwork(void)
+{
+	return &open_work;
+}
+
+
+//=============================================================================
+//	パートワークのポインタの取得
+//=============================================================================
+QQ* PMDWIN::getpartwork(unsigned int ch)
+{
+	if(ch >= sizeof(open_work.MusPart) / sizeof(QQ *)) {
+		return NULL;
+	}
+	return open_work.MusPart[ch];
+}
+
+PMDWIN* pmdwin;
+#ifdef WIN32
+//=============================================================================
+//	DLL Export Functions
+//=============================================================================
+BOOL DllMain(HINSTANCE hModule,  DWORD  ul_reason_for_call, LPVOID lpReserved)
+{
+    switch (ul_reason_for_call)
+	{
+		case DLL_PROCESS_ATTACH:
+			//DisableThreadLibraryCalls(hModule); 
+			pmdwin = NULL;
+			break;
+
+		case DLL_THREAD_ATTACH:
+			break;
+
+		case DLL_THREAD_DETACH:
+			break;
+
+		case DLL_PROCESS_DETACH:
+			break;
+    }
+    return TRUE;
+}
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+//=============================================================================
+//	バーション取得
+//=============================================================================
+DLLEXPORT int getversion(void)
+{
+	return	DLLVersion;
+}
+
+
+//=============================================================================
+//	初期化
+//=============================================================================
+DLLEXPORT bool pmdwininit(void)
+{
+	bool result;
+
+	if(pmdwin == NULL) {
+		//pmdwin = new PMDWIN;
+#if defined(WIN32) || defined(WIN64)
+        pmdwin = (PMDWIN*)VirtualAlloc(NULL, sizeof(PMDWIN), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+#else
+        pmdwin = (PMDWIN*)mmap(NULL, sizeof(PMDWIN), PROT_READ|PROT_WRITE,
+                               MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0);
+#endif
+	}
+	result = pmdwin->init();
+	return result;
+}
+
+
+DLLEXPORT void pmdwinfree(void)
+{
+#if defined(WIN32) || defined(WIN64)
+    VirtualFree(pmdwin, 0, MEM_RELEASE);
+#else
+    munmap(pmdwin, sizeof(PMDWIN));
+#endif
+}
+
+
+//=============================================================================
+//	合成周波数の設定
+//=============================================================================
+DLLEXPORT void setpcmrate(unsigned int rate)
+{
+	pmdwin->setpcmrate(rate);
+}
+
+
+DLLEXPORT void setdevmask(uint8_t mask)
+{
+    pmdwin->setdevmask(mask);
+}
+
+DLLEXPORT void setchanmask(uint32_t mask)
+{
+    pmdwin->setchanmask(mask);
+}
+
+
+//=============================================================================
+//	曲の読み込みその2(メモリから)
+//=============================================================================
+DLLEXPORT int music_load3(uchar *musdata, int size)
+{
+	return pmdwin->music_load3(musdata, size);
+}
+
+
+//=============================================================================
+//	演奏開始
+//=============================================================================
+DLLEXPORT void music_start(void)
+{
+	pmdwin->music_start();
+}
+
+
+//=============================================================================
+//	演奏停止
+//=============================================================================
+DLLEXPORT void music_stop(void)
+{
+	pmdwin->music_stop();
+}
+
+
+//=============================================================================
+//	PCM データ(wave データ)の取得
+//=============================================================================
+DLLEXPORT void getpcmdata(short *buf, int nsamples)
+{
+	pmdwin->getpcmdata(buf, nsamples);
+}
+
+
+//=============================================================================
+//	FM で 55kHz合成、一次補完するかどうかの設定
+//=============================================================================
+DLLEXPORT void setfmcalc55k(bool flag)
+{
+	pmdwin->setfmcalc55k(flag);
+}
+
+
+DLLEXPORT unsigned int getstatus(char *buf, size_t bufsize)
+{
+    return pmdwin->getstatus(buf, bufsize);
+}
+
+
+//=============================================================================
+//	メモの取得
+//=============================================================================
+DLLEXPORT char * getmemo(char *dest, uchar *musdata, int size, int al)
+{
+	return pmdwin->_getmemo(dest, musdata, size, al);
+}
+
+
+//=============================================================================
+//	メモの取得(2バイト半角→半角文字に変換+ESCシーケンスの除去)
+//=============================================================================
+DLLEXPORT char * getmemo3(char *dest, uchar *musdata, int size, int al)
+{
+	return pmdwin->_getmemo3(dest, musdata, size, al);
+}
+
+
+//=============================================================================
+//	パートのマスク
+//=============================================================================
+DLLEXPORT int maskon(unsigned int ch)
+{
+	return pmdwin->maskon(ch);
+}
+
+
+//=============================================================================
+//	パートのマスク解除
+//=============================================================================
+DLLEXPORT int maskoff(unsigned int ch)
+{
+	return pmdwin->maskoff(ch);
+}
+
+
+//=============================================================================
+//	FM Volume Down の設定
+//=============================================================================
+DLLEXPORT void setfmvoldown(int voldown)
+{
+	pmdwin->setfmvoldown(voldown);
+}
+
+
+//=============================================================================
+//	SSG Volume Down の設定
+//=============================================================================
+DLLEXPORT void setssgvoldown(int voldown)
+{
+	pmdwin->setssgvoldown(voldown);
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の設定
+//=============================================================================
+DLLEXPORT void setrhythmvoldown(int voldown)
+{
+	pmdwin->setrhythmvoldown(voldown);
+}
+
+
+//=============================================================================
+//	FM Volume Down の取得
+//=============================================================================
+DLLEXPORT int getfmvoldown(void)
+{
+	return pmdwin->getfmvoldown();
+}
+
+
+//=============================================================================
+//	FM Volume Down の取得(その2)
+//=============================================================================
+DLLEXPORT int getfmvoldown2(void)
+{
+	return pmdwin->getfmvoldown2();
+}
+
+
+//=============================================================================
+//	SSG Volume Down の取得
+//=============================================================================
+DLLEXPORT int getssgvoldown(void)
+{
+	return pmdwin->getssgvoldown();
+}
+
+
+//=============================================================================
+//	SSG Volume Down の取得(その2)
+//=============================================================================
+DLLEXPORT int getssgvoldown2(void)
+{
+	return pmdwin->getssgvoldown2();
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の取得
+//=============================================================================
+DLLEXPORT int getrhythmvoldown(void)
+{
+	return pmdwin->getrhythmvoldown();
+}
+
+
+//=============================================================================
+//	Rhythm Volume Down の取得(その2)
+//=============================================================================
+DLLEXPORT int getrhythmvoldown2(void)
+{
+	return pmdwin->getrhythmvoldown2();
+}
+
+
+//=============================================================================
+//	再生位置の移動(pos : ms)
+//=============================================================================
+DLLEXPORT void setpos(int pos)
+{
+	pmdwin->setpos(pos);
+}
+
+
+//=============================================================================
+//	再生位置の移動(pos : count 単位)
+//=============================================================================
+DLLEXPORT void setpos2(int pos)
+{
+	pmdwin->setpos2(pos);
+}
+
+
+//=============================================================================
+//	再生位置の取得(pos : ms)
+//=============================================================================
+DLLEXPORT int getpos(void)
+{
+	return pmdwin->getpos();
+}
+
+
+//=============================================================================
+//	再生位置の取得(pos : count 単位)
+//=============================================================================
+DLLEXPORT int getpos2(void)
+{
+	return pmdwin->getpos2();
+}
+
+
+//=============================================================================
+//	曲の長さの取得(pos : ms)
+//=============================================================================
+DLLEXPORT bool getlength(uint *length, uint *loop)
+{
+	return pmdwin->getlength(length, loop);
+}
+
+
+//=============================================================================
+//	ループ回数の取得
+//=============================================================================
+DLLEXPORT int getloopcount(void)
+{
+	return pmdwin->getloopcount();
+}
+
+
+//=============================================================================
+//	OPEN_WORKのポインタの取得
+//=============================================================================
+DLLEXPORT OPEN_WORK * getopenwork(void)
+{
+	return pmdwin->getopenwork();
+}
+
+
+//=============================================================================
+//	パートワークのポインタの取得
+//=============================================================================
+DLLEXPORT QQ * getpartwork(unsigned int ch)
+{
+	return pmdwin->getpartwork(ch);
+}
+
+
+#ifdef __cplusplus
+}
+#endif