view 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 source

//=============================================================================
//	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