OSDN Git Service

[FMGEN] Initial implementation of OPN2 (YM2612) from OPNA.
[csp-qt/common_source_project-fm7.git] / source / src / vm / fmgen / opna.cpp
index bb81a23..00628c7 100644 (file)
@@ -9,22 +9,16 @@
 #include "opna.h"
 #include "fmgeninl.h"
 
+#include "../../common.h"
 #include "../../fileio.h"
 
+#include "../../statesub.h"
+
+
 #define BUILD_OPN
 #define BUILD_OPNA
 #define BUILD_OPNB
-#if defined(_USE_AGAR)
-# ifndef MAX_PATH
-#  define MAX_PATH AG_PATHNAME_MAX
-# endif
-#endif
-
-#ifdef USE_QT
-# ifndef MAX_PATH
-#   define MAX_PATH 2048
-# endif
-#endif
+#define BUILD_OPN2
 
 
 //     TOFIX:
@@ -38,9 +32,9 @@
 //
 //#define NO_BITTYPE_EMULATION
 
-#ifdef BUILD_OPNA
-#include "../../fileio.h"
-#endif
+//#ifdef BUILD_OPNA
+//#include "file.h"
+//#endif
 
 namespace FM
 {
@@ -48,13 +42,16 @@ namespace FM
 // ---------------------------------------------------------------------------
 //     OPNBase
 
-#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB)
+#if defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) || defined(BUILD_OPN2)
 
 uint32 OPNBase::lfotable[8];                   // OPNA/B \97p
 
 OPNBase::OPNBase()
 {
+       is_ay3_891x = false;
        prescale = 0;
+       static int __num = 0;
+       chip_num = __num++;
 }
 
 //     \83p\83\89\83\81\81[\83^\83Z\83b\83g
@@ -166,13 +163,19 @@ bool OPNBase::Init(uint c, uint r)
 }
 
 //     \89¹\97Ê\90Ý\92è
-void OPNBase::SetVolumeFM(int db)
+void OPNBase::SetVolumeFM(int db_l, int db_r)
 {
-       db = Min(db, 20);
-       if (db > -192)
-               fmvolume = int(16384.0 * pow(10.0, db / 40.0));
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+       
+       if (db_l > -192)
+               fmvolume_l = int(16384.0 * pow(10.0, db_l / 40.0));
+       else
+               fmvolume_l = 0;
+       if (db_r > -192)
+               fmvolume_r = int(16384.0 * pow(10.0, db_r / 40.0));
        else
-               fmvolume = 0;
+               fmvolume_r = 0;
 }
 
 //     \83^\83C\83}\81[\8e\9e\8aÔ\8f\88\97\9d
@@ -194,43 +197,73 @@ void OPNBase::Intr(bool value)
 // ---------------------------------------------------------------------------
 //     \83X\83e\81[\83g\83Z\81[\83u
 //
-#define OPN_BASE_STATE_VERSION 1
+#define OPN_BASE_STATE_VERSION 4
+
+void OPNBase::DeclState(void *f)
+{
+       Timer::DeclState(f);
+       DECL_STATE_ENTRY_INT32(fmvolume_l);
+       DECL_STATE_ENTRY_INT32(fmvolume_r);
+       DECL_STATE_ENTRY_UINT32(clock);
+       DECL_STATE_ENTRY_UINT32(rate);
+       DECL_STATE_ENTRY_UINT32(psgrate);
+       DECL_STATE_ENTRY_UINT32(status);
+       DECL_STATE_ENTRY_BOOL(interrupt);
+       DECL_STATE_ENTRY_UINT8(prescale);
+       chip.DeclState(f);
+       psg.DeclState(f);
+}
 
 void OPNBase::SaveState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
        
-       state_fio->FputUint32(OPN_BASE_STATE_VERSION);
-       
-       Timer::SaveState(f);
-       state_fio->FputInt32(fmvolume);
-       state_fio->FputUint32(clock);
-       state_fio->FputUint32(rate);
-       state_fio->FputUint32(psgrate);
-       state_fio->FputUint32(status);
-       state_fio->FputBool(interrupt);
-       state_fio->FputUint8(prescale);
+       if(state_entry != NULL) {
+               state_entry->save_state(state_fio);
+       }
        chip.SaveState(f);
        psg.SaveState(f);
+//     state_fio->FputUint32_BE(OPN_BASE_STATE_VERSION);
+       
+//     Timer::SaveState(f);
+//     state_fio->FputInt32_BE(fmvolume_l);
+//     state_fio->FputInt32_BE(fmvolume_r);
+//     state_fio->FputUint32_BE(clock);
+//     state_fio->FputUint32_BE(rate);
+//     state_fio->FputUint32_BE(psgrate);
+//     state_fio->FputUint32_BE(status);
+//     state_fio->FputBool(interrupt);
+//     state_fio->FputUint8(prescale);
 }
 
 bool OPNBase::LoadState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
-       
-       if(state_fio->FgetUint32() != OPN_BASE_STATE_VERSION) {
-               return false;
+       bool mb = false;
+       if(state_entry != NULL) {
+               mb = state_entry->load_state(state_fio);
        }
-       if(!Timer::LoadState(f)) {
-               return false;
+       if(!mb) return false;
+//     if(state_fio->FgetUint32_BE() != OPN_BASE_STATE_VERSION) {
+//             return false;
+//     }
+//     if(!Timer::LoadState(f)) {
+//             return false;
+//     }
+//     fmvolume_l = state_fio->FgetInt32_BE();
+//     fmvolume_r = state_fio->FgetInt32_BE();
+//     clock = state_fio->FgetUint32_BE();
+//     rate = state_fio->FgetUint32_BE();
+//     psgrate = state_fio->FgetUint32_BE();
+//     status = state_fio->FgetUint32_BE();
+//     interrupt = state_fio->FgetBool();
+//     prescale = state_fio->FgetUint8();
+       {
+               // Make force-restore around prescaler and timers. 20180625 K.O
+               uint bak = prescale;
+               prescale = 10;
+               SetPrescaler(bak);
        }
-       fmvolume = state_fio->FgetInt32();
-       clock = state_fio->FgetUint32();
-       rate = state_fio->FgetUint32();
-       psgrate = state_fio->FgetUint32();
-       status = state_fio->FgetUint32();
-       interrupt = state_fio->FgetBool();
-       prescale = state_fio->FgetUint8();
        if(!chip.LoadState(f)) {
                return false;
        }
@@ -240,7 +273,7 @@ bool OPNBase::LoadState(void *f)
        return true;
 }
 
-#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB)
+#endif // defined(BUILD_OPN) || defined(BUILD_OPNA) || defined (BUILD_OPNB) || defined(BUILD_OPN2)
 
 // ---------------------------------------------------------------------------
 //     YM2203
@@ -249,8 +282,8 @@ bool OPNBase::LoadState(void *f)
 
 OPN::OPN()
 {
-       SetVolumeFM(0);
-       SetVolumePSG(0);
+       SetVolumeFM(0, 0);
+       SetVolumePSG(0, 0);
 
        csmch = &ch[2];
 
@@ -269,8 +302,8 @@ bool OPN::Init(uint c, uint r, bool ip, const char*)
        
        Reset();
 
-       SetVolumeFM(0);
-       SetVolumePSG(0);
+       SetVolumeFM(0, 0);
+       SetVolumePSG(0, 0);
        SetChannelMask(0);
        return true;
 }
@@ -315,7 +348,6 @@ void OPN::SetReg(uint addr, uint data)
                return;
        
        int c = addr & 3;
-
        switch (addr)
        {
        case  0: case  1: case  2: case  3: case  4: case  5: case  6: case  7:
@@ -336,7 +368,8 @@ void OPN::SetReg(uint addr, uint data)
                break;
        
        case 0x28:              // Key On/Off
-               if ((data & 3) < 3)     ch[data & 3].KeyControl(data >> 4);
+               if ((data & 3) < 3)
+                       ch[data & 3].KeyControl(data >> 4);
                break;
 
        case 0x2d: case 0x2e: case 0x2f:
@@ -345,7 +378,7 @@ void OPN::SetReg(uint addr, uint data)
 
        // F-Number
        case 0xa0: case 0xa1: case 0xa2:
-               if (c <= 2)     fnum[c] = data + fnum2[c] * 0x100; 
+               fnum[c] = data + fnum2[c] * 0x100; 
                break;
        
        case 0xa4: case 0xa5: case 0xa6:
@@ -353,7 +386,7 @@ void OPN::SetReg(uint addr, uint data)
                break;
 
        case 0xa8: case 0xa9: case 0xaa:
-               if(c < 3) fnum3[c] = data + fnum2[c+3] * 0x100; 
+               fnum3[c] = data + fnum2[c+3] * 0x100; 
                break;
        
        case 0xac: case 0xad: case 0xae:
@@ -405,7 +438,8 @@ void OPN::SetChannelMask(uint mask)
 //     \8d\87\90¬(2ch)
 void OPN::Mix(Sample* buffer, int nsamples)
 {
-#define IStoSample(s)  ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
+#define IStoSampleL(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume_l) >> 14)
+#define IStoSampleR(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume_r) >> 14)
        
        psg.Mix(buffer, nsamples);
        
@@ -429,32 +463,59 @@ void OPN::Mix(Sample* buffer, int nsamples)
                for (Sample* dest = buffer; dest < limit; dest+=2)
                {
                        ISample s = 0;
+                       ISample s_l, s_r;
                        if (actch & 0x01) s  = ch[0].Calc();
                        if (actch & 0x04) s += ch[1].Calc();
                        if (actch & 0x10) s += ch[2].Calc();
-                       s = IStoSample(s);
-                       StoreSample(dest[0], s);
-                       StoreSample(dest[1], s);
+                       s_l = IStoSampleL(s);
+                       s_r = IStoSampleR(s);
+                       StoreSample(dest[0], s_l);
+                       StoreSample(dest[1], s_r);
                }
        }
-#undef IStoSample
+#undef IStoSampleL
+#undef IStoSampleR
 }
 
 // ---------------------------------------------------------------------------
 //     \83X\83e\81[\83g\83Z\81[\83u
 //
-#define OPN_STATE_VERSION      1
+#define OPN_STATE_VERSION      2
+
+void OPN::DeclState(void *f)
+{
+
+       p_logger = (CSP_Logger *)f;
+       state_entry = new csp_state_utils(OPN_STATE_VERSION, chip_num, _T("FMGEN::OPN::"), p_logger);
+
+       OPNBase::DeclState(f);
+       
+       for(int i = 0; i < 3; i++) {
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum[i]), i);
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum3[i]), i);
+       }               
+       DECL_STATE_ENTRY_1D_ARRAY(fnum2, sizeof(fnum2));
+       for(int i = 0; i < 3; i++) {
+               ch[i].DeclState(f);
+       }
+}
 
 void OPN::SaveState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
-       
-       state_fio->FputUint32(OPN_STATE_VERSION);
-       
        OPNBase::SaveState(f);
-       state_fio->Fwrite(fnum, sizeof(fnum), 1);
-       state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
-       state_fio->Fwrite(fnum2, sizeof(fnum2), 1);
+//     state_fio->FputUint32_BE(OPN_STATE_VERSION);
+//     
+//     OPNBase::SaveState(f);
+//     for(int i = 0; i < 3; i++) {
+//             state_fio->FputUint32_BE(fnum[i]);
+//     }               
+//     for(int i = 0; i < 3; i++) {
+//             state_fio->FputUint32_BE(fnum3[i]);
+//     }               
+//     //state_fio->Fwrite(fnum, sizeof(fnum), 1);
+//     //state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
+//     state_fio->Fwrite(fnum2, sizeof(fnum2), 1);
        for(int i = 0; i < 3; i++) {
                ch[i].SaveState(f);
        }
@@ -464,15 +525,24 @@ bool OPN::LoadState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
        
-       if(state_fio->FgetUint32() != OPN_STATE_VERSION) {
-               return false;
-       }
-       if(!OPNBase::LoadState(f)) {
-               return false;
-       }
-       state_fio->Fread(fnum, sizeof(fnum), 1);
-       state_fio->Fread(fnum3, sizeof(fnum3), 1);
-       state_fio->Fread(fnum2, sizeof(fnum2), 1);
+       bool mb = false;
+       mb = OPNBase::LoadState(f);
+       if(!mb) return false;
+//     if(state_fio->FgetUint32_BE() != OPN_STATE_VERSION) {
+//             return false;
+//     }
+//     if(!OPNBase::LoadState(f)) {
+//             return false;
+//     }
+//     for(int i = 0; i < 3; i++) {
+//             fnum[i] = state_fio->FgetUint32_BE();
+//     }               
+//     for(int i = 0; i < 3; i++) {
+//             fnum3[i] = state_fio->FgetUint32_BE();
+//     }               
+//     //state_fio->Fread(fnum, sizeof(fnum), 1);
+//     //state_fio->Fread(fnum3, sizeof(fnum3), 1);
+//     state_fio->Fread(fnum2, sizeof(fnum2), 1);
        for(int i = 0; i < 3; i++) {
                if(!ch[i].LoadState(f)) {
                        return false;
@@ -502,7 +572,8 @@ OPNABase::OPNABase()
        startaddr = 0;
        deltan = 256;
 
-       adpcmvol = 0;
+       adpcmvol_l = 0;
+       adpcmvol_r = 0;
        control2 = 0;
 
        MakeTable2();
@@ -527,8 +598,8 @@ bool OPNABase::Init(uint c, uint r, bool)
        
        Reset();
 
-       SetVolumeFM(0);
-       SetVolumePSG(0);
+       SetVolumeFM(0, 0);
+       SetVolumePSG(0, 0);
        SetChannelMask(0);
        return true;
 }
@@ -575,7 +646,8 @@ void OPNABase::Reset()
        adpcmd = 127;
        adpcmx = 0;
        adpcmreadbuf = 0;
-       apout0 = apout1 = adpcmout = 0;
+       apout0_l = apout1_l = adpcmout_l = 0;
+       apout0_r = apout1_r = adpcmout_r = 0;
        lfocount = 0;
        adpcmplay = false;
        adplc = 0;
@@ -783,7 +855,8 @@ void OPNABase::SetADPCMBReg(uint addr, uint data)
 
        case 0x0b:              // Level Control
                adpcmlevel = data; 
-               adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
+               adpcmvolume_l = (adpcmvol_l * adpcmlevel) >> 12;
+               adpcmvolume_r = (adpcmvol_r * adpcmlevel) >> 12;
                break;
 
        case 0x0c:              // Limit Address L
@@ -796,7 +869,8 @@ void OPNABase::SetADPCMBReg(uint addr, uint data)
        case 0x10:              // Flag Control
                if (data & 0x80)
                {
-                       status = 0;
+                       // for Firecracker Music collection (Hi-speed PCM loader)
+                       status &= 0x03;
                        UpdateStatus();
                }
                else
@@ -1047,6 +1121,8 @@ int OPNABase::ReadRAMN()
                        memaddr = startaddr;
                        data = adpcmx;
                        adpcmx = 0, adpcmd = 127;
+                       // for PC-8801FA/MA shop demonstration
+                       SetStatus(adpcmnotice);
                        return data;
                }
                else
@@ -1079,10 +1155,15 @@ uint OPNABase::ReadStatusEx()
 //
 inline void OPNABase::DecodeADPCMB()
 {
-       apout0 = apout1;
-       int n = (ReadRAMN() * adpcmvolume) >> 13;
-       apout1 = adpcmout + n;
-       adpcmout = n;
+       apout0_l = apout1_l;
+       apout0_r = apout1_r;
+       int ram = ReadRAMN();
+       int s_l = (ram * adpcmvolume_l) >> 13;
+       int s_r = (ram * adpcmvolume_r) >> 13;
+       apout1_l = adpcmout_l + s_l;
+       apout1_r = adpcmout_r + s_r;
+       adpcmout_l = s_l;
+       adpcmout_r = s_r;
 }
 
 // ---------------------------------------------------------------------------
@@ -1090,11 +1171,11 @@ inline void OPNABase::DecodeADPCMB()
 //     
 void OPNABase::ADPCMBMix(Sample* dest, uint count)
 {
-       uint maskl = control2 & 0x80 ? -1 : 0;
-       uint maskr = control2 & 0x40 ? -1 : 0;
+       uint mask_l = control2 & 0x80 ? -1 : 0;
+       uint mask_r = control2 & 0x40 ? -1 : 0;
        if (adpcmmask_)
        {
-               maskl = maskr = 0;
+               mask_l = mask_r = 0;
        }
        
        if (adpcmplay)
@@ -1111,22 +1192,25 @@ void OPNABase::ADPCMBMix(Sample* dest, uint count)
                                        if (!adpcmplay)
                                                break;
                                }
-                               int s = (adplc * apout0 + (8192-adplc) * apout1) >> 13;
-                               StoreSample(dest[0], s & maskl);
-                               StoreSample(dest[1], s & maskr);
+                               int s_l = (adplc * apout0_l + (8192-adplc) * apout1_l) >> 13;
+                               int s_r = (adplc * apout0_r + (8192-adplc) * apout1_r) >> 13;
+                               StoreSample(dest[0], s_l & mask_l);
+                               StoreSample(dest[1], s_r & mask_r);
                                dest += 2;
                                adplc -= adpld;
                        }
-                       for (; count>0 && apout0; count--)
+                       for (; count>0 && (apout0_l || apout0_r); count--)
                        {
                                if (adplc < 0)
                                {
-                                       apout0 = apout1, apout1 = 0;
+                                       apout0_l = apout1_l, apout1_l = 0;
+                                       apout0_r = apout1_r, apout1_r = 0;
                                        adplc += 8192;
                                }
-                               int s = (adplc * apout1) >> 13;
-                               StoreSample(dest[0], s & maskl);
-                               StoreSample(dest[1], s & maskr);
+                               int s_l = (adplc * apout1_l) >> 13;
+                               int s_r = (adplc * apout1_r) >> 13;
+                               StoreSample(dest[0], s_l & mask_l);
+                               StoreSample(dest[1], s_r & mask_r);
                                dest += 2;
                                adplc -= adpld;
                        }
@@ -1136,19 +1220,22 @@ void OPNABase::ADPCMBMix(Sample* dest, uint count)
                        int t = (-8192*8192)/adpld;
                        for (; count>0; count--)
                        {
-                               int s = apout0 * (8192+adplc);
+                               int s_l = apout0_l * (8192+adplc);
+                               int s_r = apout0_r * (8192+adplc);
                                while (adplc < 0)
                                {
                                        DecodeADPCMB();
                                        if (!adpcmplay)
                                                goto stop;
-                                       s -= apout0 * Max(adplc, t);
+                                       s_l -= apout0_l * Max(adplc, t);
+                                       s_r -= apout0_r * Max(adplc, t);
                                        adplc -= t;
                                }
                                adplc -= 8192;
-                               s >>= 13;
-                               StoreSample(dest[0], s & maskl);
-                               StoreSample(dest[1], s & maskr);
+                               s_l >>= 13;
+                               s_r >>= 13;
+                               StoreSample(dest[0], s_l & mask_l);
+                               StoreSample(dest[1], s_r & mask_r);
                                dest += 2;
                        }
 stop:
@@ -1157,7 +1244,8 @@ stop:
        }
        if (!adpcmplay)
        {
-               apout0 = apout1 = adpcmout = 0;
+               apout0_l = apout1_l = adpcmout_l = 0;
+               apout0_r = apout1_r = adpcmout_r = 0;
                adplc = 0;
        }
 }
@@ -1169,7 +1257,7 @@ stop:
 //
 void OPNABase::FMMix(Sample* buffer, int nsamples)
 {
-       if (fmvolume > 0)
+       if (fmvolume_l > 0 || fmvolume_r > 0)
        {
                // \8f\80\94õ
                // Set F-Number
@@ -1254,7 +1342,8 @@ inline void OPNABase::LFO()
 // ---------------------------------------------------------------------------
 //     \8d\87\90¬
 //
-#define IStoSample(s)  ((Limit(s, 0x7fff, -0x8000) * fmvolume) >> 14)
+#define IStoSampleL(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume_l) >> 14)
+#define IStoSampleR(s) ((Limit(s, 0x7fff, -0x8000) * fmvolume_r) >> 14)
 
 void OPNABase::Mix6(Sample* buffer, int nsamples, int activech)
 {
@@ -1276,60 +1365,134 @@ void OPNABase::Mix6(Sample* buffer, int nsamples, int activech)
                        LFO(), MixSubSL(activech, idest);
                else
                        MixSubS(activech, idest);
-               StoreSample(dest[0], IStoSample(ibuf[2] + ibuf[3]));
-               StoreSample(dest[1], IStoSample(ibuf[1] + ibuf[3]));
+               StoreSample(dest[0], IStoSampleL(ibuf[2] + ibuf[3]));
+               StoreSample(dest[1], IStoSampleR(ibuf[1] + ibuf[3]));
        }
 }
 
 // ---------------------------------------------------------------------------
 //     \83X\83e\81[\83g\83Z\81[\83u
 //
-#define OPNA_BASE_STATE_VERSION        1
+#define OPNA_BASE_STATE_VERSION        2
+
+void OPNABase::DeclState(void *f)
+{
+
+       OPNBase::DeclState(f);
+       
+       DECL_STATE_ENTRY_1D_ARRAY(pan, sizeof(pan));
+       DECL_STATE_ENTRY_1D_ARRAY(fnum2, sizeof(fnum2));
+       DECL_STATE_ENTRY_UINT8(reg22);
+       DECL_STATE_ENTRY_UINT32(reg29);
+       DECL_STATE_ENTRY_UINT32(stmask);
+       DECL_STATE_ENTRY_UINT32(statusnext);
+       DECL_STATE_ENTRY_UINT32(lfocount);
+       DECL_STATE_ENTRY_UINT32(lfodcount);
+       //state_fio->Fwrite(fnum, sizeof(fnum), 1);
+       //state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
+       for(int i = 0; i < 6; i++) {
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum[i]), i);
+       }               
+       for(int i = 0; i < 3; i++) {
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum3[i]), i);
+       }               
+       DECL_STATE_ENTRY_1D_ARRAY(adpcmbuf, 0x40000);
+       DECL_STATE_ENTRY_UINT32(adpcmmask);
+       DECL_STATE_ENTRY_UINT32(adpcmnotice);
+       DECL_STATE_ENTRY_UINT32(startaddr);
+       DECL_STATE_ENTRY_UINT32(stopaddr);
+       DECL_STATE_ENTRY_UINT32(memaddr);
+       DECL_STATE_ENTRY_UINT32(limitaddr);
+       DECL_STATE_ENTRY_INT32(adpcmlevel);
+       DECL_STATE_ENTRY_INT32(adpcmvolume_l);
+       DECL_STATE_ENTRY_INT32(adpcmvolume_r);
+       DECL_STATE_ENTRY_INT32(adpcmvol_l);
+       DECL_STATE_ENTRY_INT32(adpcmvol_r);
+       DECL_STATE_ENTRY_UINT32(deltan);
+       DECL_STATE_ENTRY_INT32(adplc);
+       DECL_STATE_ENTRY_INT32(adpld);
+       DECL_STATE_ENTRY_UINT32(adplbase);
+       DECL_STATE_ENTRY_INT32(adpcmx);
+       DECL_STATE_ENTRY_INT32(adpcmd);
+       DECL_STATE_ENTRY_INT32(adpcmout_l);
+       DECL_STATE_ENTRY_INT32(adpcmout_r);
+       DECL_STATE_ENTRY_INT32(apout0_l);
+       DECL_STATE_ENTRY_INT32(apout0_r);
+       DECL_STATE_ENTRY_INT32(apout1_l);
+       DECL_STATE_ENTRY_INT32(apout1_r);
+       DECL_STATE_ENTRY_UINT32(adpcmreadbuf);
+       DECL_STATE_ENTRY_BOOL(adpcmplay);
+       DECL_STATE_ENTRY_INT8(granuality);
+       DECL_STATE_ENTRY_BOOL(adpcmmask_);
+       DECL_STATE_ENTRY_UINT8(control1);
+       DECL_STATE_ENTRY_UINT8(control2);
+       DECL_STATE_ENTRY_1D_ARRAY(adpcmreg, sizeof(adpcmreg));
+       DECL_STATE_ENTRY_INT32(rhythmmask_);
 
+       for(int i = 0; i < 6; i++) {
+               ch[i].DeclState(f);
+       }
+}
 void OPNABase::SaveState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
+       if(state_entry != NULL) {
+               state_entry->save_state(state_fio);
+       }
+       chip.SaveState(f);
+       psg.SaveState(f);
        
-       state_fio->FputUint32(OPNA_BASE_STATE_VERSION);
+//     state_fio->FputUint32_BE(OPNA_BASE_STATE_VERSION);
        
-       OPNBase::SaveState(f);
-       state_fio->Fwrite(pan, sizeof(pan), 1);
-       state_fio->Fwrite(fnum2, sizeof(fnum2), 1);
-       state_fio->FputUint8(reg22);
-       state_fio->FputUint32(reg29);
-       state_fio->FputUint32(stmask);
-       state_fio->FputUint32(statusnext);
-       state_fio->FputUint32(lfocount);
-       state_fio->FputUint32(lfodcount);
-       state_fio->Fwrite(fnum, sizeof(fnum), 1);
-       state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
-       state_fio->Fwrite(adpcmbuf, 0x40000, 1);
-       state_fio->FputUint32(adpcmmask);
-       state_fio->FputUint32(adpcmnotice);
-       state_fio->FputUint32(startaddr);
-       state_fio->FputUint32(stopaddr);
-       state_fio->FputUint32(memaddr);
-       state_fio->FputUint32(limitaddr);
-       state_fio->FputInt32(adpcmlevel);
-       state_fio->FputInt32(adpcmvolume);
-       state_fio->FputInt32(adpcmvol);
-       state_fio->FputUint32(deltan);
-       state_fio->FputInt32(adplc);
-       state_fio->FputInt32(adpld);
-       state_fio->FputUint32(adplbase);
-       state_fio->FputInt32(adpcmx);
-       state_fio->FputInt32(adpcmd);
-       state_fio->FputInt32(adpcmout);
-       state_fio->FputInt32(apout0);
-       state_fio->FputInt32(apout1);
-       state_fio->FputUint32(adpcmreadbuf);
-       state_fio->FputBool(adpcmplay);
-       state_fio->FputInt8(granuality);
-       state_fio->FputBool(adpcmmask_);
-       state_fio->FputUint8(control1);
-       state_fio->FputUint8(control2);
-       state_fio->Fwrite(adpcmreg, sizeof(adpcmreg), 1);
-       state_fio->FputInt32(rhythmmask_);
+//     OPNBase::SaveState(f);
+//     state_fio->Fwrite(pan, sizeof(pan), 1);
+//     state_fio->Fwrite(fnum2, sizeof(fnum2), 1);
+//     state_fio->FputUint8(reg22);
+//     state_fio->FputUint32_BE(reg29);
+//     state_fio->FputUint32_BE(stmask);
+//     state_fio->FputUint32_BE(statusnext);
+//     state_fio->FputUint32_BE(lfocount);
+//     state_fio->FputUint32_BE(lfodcount);
+//     //state_fio->Fwrite(fnum, sizeof(fnum), 1);
+//     //state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
+//     for(int i = 0; i < 6; i++) {
+//             state_fio->FputUint32_BE(fnum[i]);
+//     }               
+//     for(int i = 0; i < 3; i++) {
+//             state_fio->FputUint32_BE(fnum3[i]);
+//     }               
+//     state_fio->Fwrite(adpcmbuf, 0x40000, 1);
+//     state_fio->FputUint32_BE(adpcmmask);
+//     state_fio->FputUint32_BE(adpcmnotice);
+//     state_fio->FputUint32_BE(startaddr);
+//     state_fio->FputUint32_BE(stopaddr);
+//     state_fio->FputUint32_BE(memaddr);
+//     state_fio->FputUint32_BE(limitaddr);
+//     state_fio->FputInt32_BE(adpcmlevel);
+//     state_fio->FputInt32_BE(adpcmvolume_l);
+//     state_fio->FputInt32_BE(adpcmvolume_r);
+//     state_fio->FputInt32_BE(adpcmvol_l);
+//     state_fio->FputInt32_BE(adpcmvol_r);
+//     state_fio->FputUint32_BE(deltan);
+//     state_fio->FputInt32_BE(adplc);
+//     state_fio->FputInt32_BE(adpld);
+//     state_fio->FputUint32_BE(adplbase);
+//     state_fio->FputInt32_BE(adpcmx);
+//     state_fio->FputInt32_BE(adpcmd);
+//     state_fio->FputInt32_BE(adpcmout_l);
+//     state_fio->FputInt32_BE(adpcmout_r);
+//     state_fio->FputInt32_BE(apout0_l);
+//     state_fio->FputInt32_BE(apout0_r);
+//     state_fio->FputInt32_BE(apout1_l);
+//     state_fio->FputInt32_BE(apout1_r);
+//     state_fio->FputUint32_BE(adpcmreadbuf);
+//     state_fio->FputBool(adpcmplay);
+//     state_fio->FputInt8(granuality);
+//     state_fio->FputBool(adpcmmask_);
+//     state_fio->FputUint8(control1);
+//     state_fio->FputUint8(control2);
+//     state_fio->Fwrite(adpcmreg, sizeof(adpcmreg), 1);
+//     state_fio->FputInt32_BE(rhythmmask_);
        for(int i = 0; i < 6; i++) {
                ch[i].SaveState(f);
        }
@@ -1339,49 +1502,77 @@ bool OPNABase::LoadState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
        
-       if(state_fio->FgetUint32() != OPNA_BASE_STATE_VERSION) {
+       bool mb = false;
+       if(state_entry != NULL) {
+               mb = state_entry->load_state(state_fio);
+       }
+       if(!mb) return false;
+       {
+               // Make force-restore around prescaler and timers. 20180625 K.O
+               uint bak = prescale;
+               prescale = 10;
+               SetPrescaler(bak);
+       }
+       if(!chip.LoadState(f)) {
                return false;
        }
-       if(!OPNBase::LoadState(f)) {
+       if(!psg.LoadState(f)) {
                return false;
        }
-       state_fio->Fread(pan, sizeof(pan), 1);
-       state_fio->Fread(fnum2, sizeof(fnum2), 1);
-       reg22 = state_fio->FgetUint8();
-       reg29 = state_fio->FgetUint32();
-       stmask = state_fio->FgetUint32();
-       statusnext = state_fio->FgetUint32();
-       lfocount = state_fio->FgetUint32();
-       lfodcount = state_fio->FgetUint32();
-       state_fio->Fread(fnum, sizeof(fnum), 1);
-       state_fio->Fread(fnum3, sizeof(fnum3), 1);
-       state_fio->Fread(adpcmbuf, 0x40000, 1);
-       adpcmmask = state_fio->FgetUint32();
-       adpcmnotice = state_fio->FgetUint32();
-       startaddr = state_fio->FgetUint32();
-       stopaddr = state_fio->FgetUint32();
-       memaddr = state_fio->FgetUint32();
-       limitaddr = state_fio->FgetUint32();
-       adpcmlevel = state_fio->FgetInt32();
-       adpcmvolume = state_fio->FgetInt32();
-       adpcmvol = state_fio->FgetInt32();
-       deltan = state_fio->FgetUint32();
-       adplc = state_fio->FgetInt32();
-       adpld = state_fio->FgetInt32();
-       adplbase = state_fio->FgetUint32();
-       adpcmx = state_fio->FgetInt32();
-       adpcmd = state_fio->FgetInt32();
-       adpcmout = state_fio->FgetInt32();
-       apout0 = state_fio->FgetInt32();
-       apout1 = state_fio->FgetInt32();
-       adpcmreadbuf = state_fio->FgetUint32();
-       adpcmplay = state_fio->FgetBool();
-       granuality = state_fio->FgetInt8();
-       adpcmmask_ = state_fio->FgetBool();
-       control1 = state_fio->FgetUint8();
-       control2 = state_fio->FgetUint8();
-       state_fio->Fread(adpcmreg, sizeof(adpcmreg), 1);
-       rhythmmask_ = state_fio->FgetInt32();
+//     if(state_fio->FgetUint32_BE() != OPNA_BASE_STATE_VERSION) {
+//             return false;
+//     }
+//     if(!OPNBase::LoadState(f)) {
+//             return false;
+//     }
+//     state_fio->Fread(pan, sizeof(pan), 1);
+//     state_fio->Fread(fnum2, sizeof(fnum2), 1);
+//     reg22 = state_fio->FgetUint8();
+//     reg29 = state_fio->FgetUint32_BE();
+//     stmask = state_fio->FgetUint32_BE();
+//     statusnext = state_fio->FgetUint32_BE();
+//     lfocount = state_fio->FgetUint32_BE();
+//     lfodcount = state_fio->FgetUint32_BE();
+//     //state_fio->Fread(fnum, sizeof(fnum), 1);
+//     //state_fio->Fread(fnum3, sizeof(fnum3), 1);
+//     for(int i = 0; i < 6; i++) {
+//             fnum[i] = state_fio->FgetUint32_BE();
+//     }               
+//     for(int i = 0; i < 3; i++) {
+//             fnum3[i] = state_fio->FgetUint32_BE();
+//     }               
+//     state_fio->Fread(adpcmbuf, 0x40000, 1);
+//     adpcmmask = state_fio->FgetUint32_BE();
+//     adpcmnotice = state_fio->FgetUint32_BE();
+//     startaddr = state_fio->FgetUint32_BE();
+//     stopaddr = state_fio->FgetUint32_BE();
+//     memaddr = state_fio->FgetUint32_BE();
+//     limitaddr = state_fio->FgetUint32_BE();
+//     adpcmlevel = state_fio->FgetInt32_BE();
+//     adpcmvolume_l = state_fio->FgetInt32_BE();
+//     adpcmvolume_r = state_fio->FgetInt32_BE();
+//     adpcmvol_l = state_fio->FgetInt32_BE();
+//     adpcmvol_r = state_fio->FgetInt32_BE();
+//     deltan = state_fio->FgetUint32_BE();
+//     adplc = state_fio->FgetInt32_BE();
+//     adpld = state_fio->FgetInt32_BE();
+//     adplbase = state_fio->FgetUint32_BE();
+//     adpcmx = state_fio->FgetInt32_BE();
+//     adpcmd = state_fio->FgetInt32_BE();
+//     adpcmout_l = state_fio->FgetInt32_BE();
+//     adpcmout_r = state_fio->FgetInt32_BE();
+//     apout0_l = state_fio->FgetInt32_BE();
+//     apout0_r = state_fio->FgetInt32_BE();
+//     apout1_l = state_fio->FgetInt32_BE();
+//     apout1_r = state_fio->FgetInt32_BE();
+//     adpcmreadbuf = state_fio->FgetUint32_BE();
+//     adpcmplay = state_fio->FgetBool();
+//     granuality = state_fio->FgetInt8();
+//     adpcmmask_ = state_fio->FgetBool();
+//     control1 = state_fio->FgetUint8();
+//     control2 = state_fio->FgetUint8();
+///    state_fio->Fread(adpcmreg, sizeof(adpcmreg), 1);
+//     rhythmmask_ = state_fio->FgetInt32_BE();
        for(int i = 0; i < 6; i++) {
                if(!ch[i].LoadState(f)) {
                        return false;
@@ -1408,11 +1599,13 @@ OPNA::OPNA()
                rhythm[i].sample = 0;
                rhythm[i].pos = 0;
                rhythm[i].size = 0;
-               rhythm[i].volume = 0;
+               rhythm[i].volume_l = 0;
+               rhythm[i].volume_r = 0;
                rhythm[i].level = 0;
                rhythm[i].pan = 0;
        }
-       rhythmtvol = 0;
+       rhythmtvol_l = 0;
+       rhythmtvol_r = 0;
        adpcmmask = 0x3ffff;
        adpcmnotice = 4;
        csmch = &ch[2];
@@ -1449,10 +1642,10 @@ bool OPNA::Init(uint c, uint r, bool ipflag, const _TCHAR* path)
        
        Reset();
 
-       SetVolumeADPCM(0);
-       SetVolumeRhythmTotal(0);
+       SetVolumeADPCM(0, 0);
+       SetVolumeRhythmTotal(0, 0);
        for (int i=0; i<6; i++)
-               SetVolumeRhythm(i, 0);
+               SetVolumeRhythm(i, 0, 0);
        return true;
 }
 
@@ -1502,50 +1695,61 @@ bool OPNA::LoadRhythmSample(const _TCHAR* path)
        {
                FILEIO file;
                uint32 fsize;
-               _TCHAR buf[MAX_PATH] = _T("");
+               _TCHAR buf[_MAX_PATH] = _T("");
+               memset(buf, 0x00, sizeof(buf));
                if (path)
-                       _tcsncpy(buf, path, MAX_PATH);
-               _tcsncat(buf, _T("2608_"), MAX_PATH);
-               _tcsncat(buf, rhythmname[i], MAX_PATH);
-               _tcsncat(buf, _T(".WAV"), MAX_PATH);
+                       _tcsncpy(buf, path, _MAX_PATH - 1);
+               _tcsncat(buf, _T("2608_"), _MAX_PATH - 1);
+               _tcsncat(buf, rhythmname[i], _MAX_PATH- 1);
+               _tcsncat(buf, _T(".WAV"), _MAX_PATH - 1);
 
                if (!file.Fopen(buf, FILEIO_READ_BINARY))
                {
                        if (i != 5)
                                break;
-                       if (path)
-                               _tcsncpy(buf, path, MAX_PATH);
-                       _tcsncpy(buf, _T("2608_RYM.WAV"), MAX_PATH);
+                       memset(buf, 0x00, sizeof(buf));
+                       if (path) {
+                               _tcsncpy(buf, path, _MAX_PATH - 1);
+                       }
+                       _tcsncat(buf, _T("2608_RYM.WAV"), _MAX_PATH - 1);
                        if (!file.Fopen(buf, FILEIO_READ_BINARY))
                                break;
                }
                
-               struct
-               {
-                       uint32 chunksize;
-                       uint16 tag;
-                       uint16 nch;
-                       uint32 rate;
-                       uint32 avgbytes;
-                       uint16 align;
-                       uint16 bps;
-                       uint16 size;
-               } whdr;
-
-               file.Fseek(0x10, FILEIO_SEEK_SET);
+               wav_header_t whdr;
+               wav_chunk_t chunk;
+
                file.Fread(&whdr, sizeof(whdr), 1);
                
                uint8 subchunkname[4];
-               fsize = 4 + whdr.chunksize - sizeof(whdr);
-               do 
+               bool is_eof = false;
+               file.Fseek(EndianFromLittle_DWORD(whdr.fmt_chunk.size) - 16, FILEIO_SEEK_CUR);
+               while(1) 
                {
-                       file.Fseek(fsize, FILEIO_SEEK_CUR);
-                       file.Fread(&subchunkname, 4, 1);
-                       file.Fread(&fsize, 4, 1);
-               } while (memcmp("data", subchunkname, 4));
-
+                       if(file.Fread(&chunk, sizeof(chunk), 1) != 1) {
+                               is_eof = true;
+                               break;
+                       }
+                       if(strncmp(chunk.id, "data", 4) == 0) {
+                                       break;
+                       }
+                       file.Fseek(EndianFromLittle_DWORD(chunk.size), FILEIO_SEEK_CUR);
+               }
+               if(is_eof) {
+//                     fsize = 8192;
+//                     rhythm[i].rate = whdr.sample_rate;
+//                     rhythm[i].step = rhythm[i].rate * 1024 / rate;
+//                     rhythm[i].pos = rhythm[i].size = fsize * 1024;
+//                     delete rhythm[i].sample;
+//                     rhythm[i].sample = new int16[fsize];
+//                     memset(rhythm[i].sample, 0x00, fsize * 2);
+                       file.Fclose();
+                       break;
+               }
+               fsize = EndianFromLittle_DWORD(chunk.size);
+               
                fsize /= 2;
-               if (fsize >= 0x100000 || whdr.tag != 1 || whdr.nch != 1)
+               if ((fsize >= 0x100000) || (EndianFromLittle_WORD(whdr.format_id) != 1) || (EndianFromLittle_WORD(whdr.channels) != 1))
                        break;
                fsize = Max(fsize, (1<<31)/1024);
                
@@ -1553,15 +1757,27 @@ bool OPNA::LoadRhythmSample(const _TCHAR* path)
                rhythm[i].sample = new int16[fsize];
                if (!rhythm[i].sample)
                        break;
+               for(int __iptr = 0; __iptr < fsize; __iptr++) {
+                       union {
+                               int16_t s16;
+                               struct {
+                                       uint8_t l, h;
+                               } b;
+                       } pair16;
+                       pair16.b.l = file.FgetUint8();
+                       pair16.b.h = file.FgetUint8();
+                       rhythm[i].sample[__iptr] = pair16.s16;
+               }
+               //file.Fread(rhythm[i].sample, fsize * 2, 1);
                
-               file.Fread(rhythm[i].sample, fsize * 2, 1);
-               
-               rhythm[i].rate = whdr.rate;
+               rhythm[i].rate = EndianFromLittle_DWORD(whdr.sample_rate);
                rhythm[i].step = rhythm[i].rate * 1024 / rate;
                rhythm[i].pos = rhythm[i].size = fsize * 1024;
+               file.Fclose();
        }
        if (i != 6)
        {
+//             printf("NG %d\n", i);
                for (i=0; i<6; i++)
                {
                        delete[] rhythm[i].sample;
@@ -1569,6 +1785,7 @@ bool OPNA::LoadRhythmSample(const _TCHAR* path)
                }
                return false;
        }
+//     printf("OK\n");
        return true;
 }
 
@@ -1630,6 +1847,13 @@ void OPNA::SetReg(uint addr, uint data)
                OPNABase::SetADPCMBReg(addr - 0x100, data);
                break;
 
+       case 0x0127:
+               // for PC-8801FA/MA shop demonstration
+               if ((control1 & 0x10) && (status & adpcmnotice)) {
+                       ResetStatus(adpcmnotice);
+               }
+               break;
+
        default:
                OPNABase::SetReg(addr, data);
                break;
@@ -1642,7 +1866,7 @@ void OPNA::SetReg(uint addr, uint data)
 //
 void OPNA::RhythmMix(Sample* buffer, uint count)
 {
-       if (rhythmtvol < 128 && rhythm[0].sample && (rhythmkey & 0x3f))
+       if ((rhythmtvol_l < 128 || rhythmtvol_r < 128) && rhythm[0].sample && (rhythmkey & 0x3f))
        {
                Sample* limit = buffer + count * 2;
                for (int i=0; i<6; i++)
@@ -1650,22 +1874,25 @@ void OPNA::RhythmMix(Sample* buffer, uint count)
                        Rhythm& r = rhythm[i];
                        if ((rhythmkey & (1 << i)) && r.level < 128)
                        {
-                               int db = Limit(rhythmtl+rhythmtvol+r.level+r.volume, 127, -31);
-                               int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4;
-                               int maskl = -((r.pan >> 1) & 1);
-                               int maskr = -(r.pan & 1);
+                               int db_l = Limit(rhythmtl+rhythmtvol_l+r.level+r.volume_l, 127, -31);
+                               int db_r = Limit(rhythmtl+rhythmtvol_r+r.level+r.volume_r, 127, -31);
+                               int vol_l = tltable[FM_TLPOS+(db_l << (FM_TLBITS-7))] >> 4;
+                               int vol_r = tltable[FM_TLPOS+(db_r << (FM_TLBITS-7))] >> 4;
+                               int mask_l = -((r.pan >> 1) & 1);
+                               int mask_r = -(r.pan & 1);
 
                                if (rhythmmask_ & (1 << i))
                                {
-                                       maskl = maskr = 0;
+                                       mask_l = mask_r = 0;
                                }
                                
                                for (Sample* dest = buffer; dest<limit && r.pos < r.size; dest+=2)
                                {
-                                       int sample = (r.sample[r.pos / 1024] * vol) >> 12;
+                                       int sample_l = (r.sample[r.pos / 1024] * vol_l) >> 12;
+                                       int sample_r = (r.sample[r.pos / 1024] * vol_r) >> 12;
                                        r.pos += r.step;
-                                       StoreSample(dest[0], sample & maskl);
-                                       StoreSample(dest[1], sample & maskr);
+                                       StoreSample(dest[0], sample_l & mask_l);
+                                       StoreSample(dest[1], sample_r & mask_r);
                                }
                        }
                }
@@ -1675,27 +1902,40 @@ void OPNA::RhythmMix(Sample* buffer, uint count)
 // ---------------------------------------------------------------------------
 //     \89¹\97Ê\90Ý\92è
 //
-void OPNA::SetVolumeRhythmTotal(int db)
+void OPNA::SetVolumeRhythmTotal(int db_l, int db_r)
 {
-       db = Min(db, 20);
-       rhythmtvol = -(db * 2 / 3);
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       rhythmtvol_l = -(db_l * 2 / 3);
+       rhythmtvol_r = -(db_r * 2 / 3);
 }
 
-void OPNA::SetVolumeRhythm(int index, int db)
+void OPNA::SetVolumeRhythm(int index, int db_l, int db_r)
 {
-       db = Min(db, 20);
-       rhythm[index].volume = -(db * 2 / 3);
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       rhythm[index].volume_l = -(db_l * 2 / 3);
+       rhythm[index].volume_r = -(db_r * 2 / 3);
 }
 
-void OPNA::SetVolumeADPCM(int db)
+void OPNA::SetVolumeADPCM(int db_l, int db_r)
 {
-       db = Min(db, 20);
-       if (db > -192)
-               adpcmvol = int(65536.0 * pow(10.0, db / 40.0));
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       if (db_l > -192)
+               adpcmvol_l = int(65536.0 * pow(10.0, db_l / 40.0));
        else
-               adpcmvol = 0;
+               adpcmvol_l = 0;
+       if (db_r > -192)
+               adpcmvol_r = int(65536.0 * pow(10.0, db_r / 40.0));
+       else
+               adpcmvol_r = 0;
 
-       adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
+       adpcmvolume_l = (adpcmvol_l * adpcmlevel) >> 12;
+       adpcmvolume_r = (adpcmvol_r * adpcmlevel) >> 12;
 }
 
 // ---------------------------------------------------------------------------
@@ -1714,43 +1954,66 @@ void OPNA::Mix(Sample* buffer, int nsamples)
 // ---------------------------------------------------------------------------
 //     \83X\83e\81[\83g\83Z\81[\83u
 //
-#define OPNA_STATE_VERSION     1
+#define OPNA_STATE_VERSION     4
+
+void OPNA::DeclState(void *f)
+{
+       p_logger = (CSP_Logger *)f;
+       state_entry = new csp_state_utils(OPNA_STATE_VERSION, chip_num, _T("FMGEN::OPNA::"), p_logger);
 
+       OPNABase::DeclState(f);
+       
+       for(int i = 0; i < 6; i++) {
+               DECL_STATE_ENTRY_UINT8_MEMBER((rhythm[i].pan), i);
+               DECL_STATE_ENTRY_INT8_MEMBER((rhythm[i].level), i);
+               DECL_STATE_ENTRY_UINT32_MEMBER((rhythm[i].pos), i);
+       }
+       DECL_STATE_ENTRY_INT8(rhythmtl);
+       DECL_STATE_ENTRY_INT32(rhythmtvol_l);
+       DECL_STATE_ENTRY_INT32(rhythmtvol_r);
+       DECL_STATE_ENTRY_UINT8(rhythmkey);
+}
 void OPNA::SaveState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
        
-       state_fio->FputUint32(OPNA_STATE_VERSION);
-       
        OPNABase::SaveState(f);
-       for(int i = 0; i < 6; i++) {
-               state_fio->FputUint8(rhythm[i].pan);
-               state_fio->FputInt8(rhythm[i].level);
-               state_fio->FputUint32(rhythm[i].pos);
-       }
-       state_fio->FputInt8(rhythmtl);
-       state_fio->FputInt32(rhythmtvol);
-       state_fio->FputUint8(rhythmkey);
+//     state_fio->FputUint32_BE(OPNA_STATE_VERSION);
+       
+//     OPNABase::SaveState(f);
+//     for(int i = 0; i < 6; i++) {
+//             state_fio->FputUint8(rhythm[i].pan);
+//             state_fio->FputInt8(rhythm[i].level);
+//             state_fio->FputUint32_BE(rhythm[i].pos);
+//     }
+//     state_fio->FputInt8(rhythmtl);
+//     state_fio->FputInt32_BE(rhythmtvol_l);
+//     state_fio->FputInt32_BE(rhythmtvol_r);
+//     state_fio->FputUint8(rhythmkey);
 }
 
 bool OPNA::LoadState(void *f)
 {
        FILEIO *state_fio = (FILEIO *)f;
        
-       if(state_fio->FgetUint32() != OPNA_STATE_VERSION) {
-               return false;
-       }
-       if(!OPNABase::LoadState(f)) {
-               return false;
-       }
-       for(int i = 0; i < 6; i++) {
-               rhythm[i].pan = state_fio->FgetUint8();
-               rhythm[i].level = state_fio->FgetInt8();
-               rhythm[i].pos = state_fio->FgetUint32();
-       }
-       rhythmtl = state_fio->FgetInt8();
-       rhythmtvol = state_fio->FgetInt32();
-       rhythmkey = state_fio->FgetUint8();
+       bool mb = false;
+       mb = OPNABase::LoadState(f);
+       if(!mb) return false;
+//     if(state_fio->FgetUint32_BE() != OPNA_STATE_VERSION) {
+//             return false;
+//     }
+//     if(!OPNABase::LoadState(f)) {
+//             return false;
+//     }
+//     for(int i = 0; i < 6; i++) {
+//             rhythm[i].pan = state_fio->FgetUint8();
+//             rhythm[i].level = state_fio->FgetInt8();
+//             rhythm[i].pos = state_fio->FgetUint32_BE();
+//     }
+//     rhythmtl = state_fio->FgetInt8();
+//     rhythmtvol_l = state_fio->FgetInt32_BE();
+//     rhythmtvol_r = state_fio->FgetInt32_BE();
+//     rhythmkey = state_fio->FgetUint8();
        return true;
 }
 
@@ -1773,10 +2036,10 @@ OPNB::OPNB()
        {
                adpcma[i].pan = 0;
                adpcma[i].level = 0;
-               adpcma[i].volume = 0;
+               adpcma[i].volume_l = 0;
+               adpcma[i].volume_r = 0;
                adpcma[i].pos = 0;
                adpcma[i].step = 0;
-               adpcma[i].volume = 0;
                adpcma[i].start = 0;
                adpcma[i].stop = 0;
                adpcma[i].adpcmx = 0;
@@ -1784,7 +2047,8 @@ OPNB::OPNB()
        }
        adpcmatl = 0;
        adpcmakey = 0;
-       adpcmatvol = 0;
+       adpcmatvol_l = 0;
+       adpcmatvol_r = 0;
        adpcmmask = 0;
        adpcmnotice = 0x8000;
        granuality = -1;
@@ -1828,12 +2092,12 @@ bool OPNB::Init(uint c, uint r, bool ipflag,
        
        Reset();
 
-       SetVolumeFM(0);
-       SetVolumePSG(0);
-       SetVolumeADPCMB(0);
-       SetVolumeADPCMATotal(0);
+       SetVolumeFM(0, 0);
+       SetVolumePSG(0, 0);
+       SetVolumeADPCMB(0, 0);
+       SetVolumeADPCMATotal(0, 0);
        for (i=0; i<6; i++)
-               SetVolumeADPCMA(i, 0);
+               SetVolumeADPCMA(i, 0, 0);
        SetChannelMask(0);
        return true;
 }
@@ -1853,10 +2117,10 @@ void OPNB::Reset()
        {
                adpcma[i].pan = 0;
                adpcma[i].level = 0;
-               adpcma[i].volume = 0;
+               adpcma[i].volume_l = 0;
+               adpcma[i].volume_r = 0;
                adpcma[i].pos = 0;
                adpcma[i].step = 0;
-               adpcma[i].volume = 0;
                adpcma[i].start = 0;
                adpcma[i].stop = 0;
                adpcma[i].adpcmx = 0;
@@ -1986,7 +2250,8 @@ void OPNB::SetReg(uint addr, uint data)
 
        case 0x1b:              // Level Control
                adpcmlevel = data; 
-               adpcmvolume = (adpcmvol * adpcmlevel) >> 12;
+               adpcmvolume_l = (adpcmvol_l * adpcmlevel) >> 12;
+               adpcmvolume_r = (adpcmvol_r * adpcmlevel) >> 12;
                break;
 
        case 0x1c:              // Flag Control
@@ -2055,7 +2320,7 @@ void OPNB::ADPCMAMix(Sample* buffer, uint count)
                -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16
        };
 
-       if (adpcmatvol < 128 && (adpcmakey & 0x3f))
+       if ((adpcmatvol_l < 128 || adpcmatvol_r < 128) && (adpcmakey & 0x3f))
        {
                Sample* limit = buffer + count * 2;
                for (int i=0; i<6; i++)
@@ -2063,15 +2328,17 @@ void OPNB::ADPCMAMix(Sample* buffer, uint count)
                        ADPCMA& r = adpcma[i];
                        if ((adpcmakey & (1 << i)) && r.level < 128)
                        {
-                               uint maskl = r.pan & 2 ? -1 : 0;
-                               uint maskr = r.pan & 1 ? -1 : 0;
+                               uint mask_l = r.pan & 2 ? -1 : 0;
+                               uint mask_r = r.pan & 1 ? -1 : 0;
                                if (rhythmmask_ & (1 << i))
                                {
-                                       maskl = maskr = 0;
+                                       mask_l = mask_r = 0;
                                }
 
-                               int db = Limit(adpcmatl+adpcmatvol+r.level+r.volume, 127, -31);
-                               int vol = tltable[FM_TLPOS+(db << (FM_TLBITS-7))] >> 4;
+                               int db_l = Limit(adpcmatl+adpcmatvol_l+r.level+r.volume_l, 127, -31);
+                               int db_r = Limit(adpcmatl+adpcmatvol_r+r.level+r.volume_r, 127, -31);
+                               int vol_l = tltable[FM_TLPOS+(db_l << (FM_TLBITS-7))] >> 4;
+                               int vol_r = tltable[FM_TLPOS+(db_r << (FM_TLBITS-7))] >> 4;
                                
                                Sample* dest = buffer;
                                for ( ; dest<limit; dest+=2) 
@@ -2103,9 +2370,10 @@ void OPNB::ADPCMAMix(Sample* buffer, uint count)
                                                r.adpcmd += decode_tableA1[data];
                                                r.adpcmd = Limit(r.adpcmd, 48*16, 0);
                                        }
-                                       int sample = (r.adpcmx * vol) >> 10;
-                                       StoreSample(dest[0], sample & maskl);
-                                       StoreSample(dest[1], sample & maskr);
+                                       int sample_l = (r.adpcmx * vol_l) >> 10;
+                                       int sample_r = (r.adpcmx * vol_r) >> 10;
+                                       StoreSample(dest[0], sample_l & mask_l);
+                                       StoreSample(dest[1], sample_r & mask_r);
                                }
                        }
                }
@@ -2115,25 +2383,37 @@ void OPNB::ADPCMAMix(Sample* buffer, uint count)
 // ---------------------------------------------------------------------------
 //     \89¹\97Ê\90Ý\92è
 //
-void OPNB::SetVolumeADPCMATotal(int db)
+void OPNB::SetVolumeADPCMATotal(int db_l, int db_r)
 {
-       db = Min(db, 20);
-       adpcmatvol = -(db * 2 / 3);
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       adpcmatvol_l = -(db_l * 2 / 3);
+       adpcmatvol_r = -(db_r * 2 / 3);
 }
 
-void OPNB::SetVolumeADPCMA(int index, int db)
+void OPNB::SetVolumeADPCMA(int index, int db_l, int db_r)
 {
-       db = Min(db, 20);
-       adpcma[index].volume = -(db * 2 / 3);
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       adpcma[index].volume_l = -(db_l * 2 / 3);
+       adpcma[index].volume_r = -(db_r * 2 / 3);
 }
 
-void OPNB::SetVolumeADPCMB(int db)
+void OPNB::SetVolumeADPCMB(int db_l, int db_r)
 {
-       db = Min(db, 20);
-       if (db > -192)
-               adpcmvol = int(65536.0 * pow(10.0, db / 40.0));
+       db_l = Min(db_l, 20);
+       db_r = Min(db_r, 20);
+
+       if (db_l > -192)
+               adpcmvol_l = int(65536.0 * pow(10.0, db_l / 40.0));
        else
-               adpcmvol = 0;
+               adpcmvol_l = 0;
+       if (db_r > -192)
+               adpcmvol_r = int(65536.0 * pow(10.0, db_r / 40.0));
+       else
+               adpcmvol_r = 0;
 }
 
 // ---------------------------------------------------------------------------
@@ -2151,4 +2431,561 @@ void OPNB::Mix(Sample* buffer, int nsamples)
 
 #endif // BUILD_OPNB
 
+// ---------------------------------------------------------------------------
+//     YM2612 common part
+// ---------------------------------------------------------------------------
+
+#if defined(BUILD_OPN2)
+
+int OPN2Base::amtable[FM_LFOENTS] = { -1, };
+int OPN2Base::pmtable[FM_LFOENTS];
+
+int32 OPN2Base::tltable[FM_TLENTS+FM_TLPOS];
+bool OPN2Base::tablehasmade = false;
+
+OPN2Base::OPN2Base()
+{
+
+       MakeTable2();
+       BuildLFOTable();
+       for (int i=0; i<6; i++)
+       {
+               ch[i].SetChip(&chip);
+               ch[i].SetType(typeN);
+       }
+}
+
+OPN2Base::~OPN2Base()
+{
+}
+
+// ---------------------------------------------------------------------------
+//     \8f\89\8aú\89»
+//
+bool OPN2Base::Init(uint c, uint r, bool)
+{
+       RebuildTimeTable();
+       
+       Reset();
+
+       SetVolumeFM(0, 0);
+       SetVolumePSG(0, 0);
+       SetChannelMask(0);
+       return true;
+}
+
+// ---------------------------------------------------------------------------
+//     \83e\81[\83u\83\8b\8dì\90¬
+//
+void OPN2Base::MakeTable2()
+{
+       if (!tablehasmade)
+       {
+               for (int i=-FM_TLPOS; i<FM_TLENTS; i++)
+               {
+                       tltable[i+FM_TLPOS] = uint(65536. * pow(2.0, i * -16. / FM_TLENTS))-1;
+               }
+
+               tablehasmade = true;
+       }
+}
+
+// ---------------------------------------------------------------------------
+//     \83\8a\83Z\83b\83g
+//
+void OPN2Base::Reset()
+{
+       int i;
+       
+       OPNBase::Reset();
+       for (i=0x20; i<0x28; i++) SetReg(i, 0);
+       for (i=0x30; i<0xc0; i++) SetReg(i, 0);
+       for (i=0x130; i<0x1c0; i++) SetReg(i, 0);
+       for (i=0x100; i<0x110; i++) SetReg(i, 0);
+       for (i=0x10; i<0x20; i++) SetReg(i, 0);
+       for (i=0; i<6; i++)
+       {
+               pan[i] = 3;
+               ch[i].Reset();
+       }
+       
+       stmask = ~0x1c;
+       statusnext = 0;
+       lfocount = 0;
+       status = 0;
+       UpdateStatus();
+}
+
+// ---------------------------------------------------------------------------
+//     \83T\83\93\83v\83\8a\83\93\83O\83\8c\81[\83g\95Ï\8dX
+//
+bool OPN2Base::SetRate(uint c, uint r, bool)
+{
+       c /= 2;         // \8f]\97\88\94Å\82Æ\82Ì\8cÝ\8a·\90«\82ð\8fd\8e\8b\82µ\82½\82¯\82è\82á\83R\83\81\83\93\83g\83A\83E\83g\82µ\82æ\82¤
+       
+       OPNBase::Init(c, r);
+
+       RebuildTimeTable();
+
+       lfodcount = reg22 & 0x08 ? lfotable[reg22 & 7] : 0;
+       return true;
+}
+
+
+// ---------------------------------------------------------------------------
+//     \83`\83\83\83\93\83l\83\8b\83}\83X\83N\82Ì\90Ý\92è
+//
+void OPN2Base::SetChannelMask(uint mask)
+{
+       for (int i=0; i<6; i++)
+               ch[i].Mute(!!(mask & (1 << i)));
+}
+
+// ---------------------------------------------------------------------------
+//     \83\8c\83W\83X\83^\83A\83\8c\83C\82É\83f\81[\83^\82ð\90Ý\92è
+//
+void OPN2Base::SetReg(uint addr, uint data)
+{
+       int     c = addr & 3;
+       switch (addr)
+       {
+               uint modified;
+
+       // Timer -----------------------------------------------------------------
+               case 0x24: case 0x25:
+                       SetTimerA(addr, data);
+                       break;
+
+               case 0x26:
+                       SetTimerB(data);
+                       break;
+
+               case 0x27:
+                       SetTimerControl(data);
+                       break;
+
+       // Misc ------------------------------------------------------------------
+       case 0x28:              // Key On/Off
+               if ((data & 3) < 3)
+               {
+                       c = (data & 3) + (data & 4 ? 3 : 0);
+                       ch[c].KeyControl(data >> 4);
+               }
+               break;
+
+       // Status Mask -----------------------------------------------------------
+       case 0x29:
+               reg29 = data;
+//             UpdateStatus(); //?
+               break;
+       
+       // Prescaler -------------------------------------------------------------
+       case 0x2d: case 0x2e: case 0x2f:
+               SetPrescaler(addr-0x2d);
+               break;
+       
+       // F-Number --------------------------------------------------------------
+       case 0x1a0:     case 0x1a1: case 0x1a2:
+               c += 3;
+       case 0xa0:      case 0xa1: case 0xa2:
+               fnum[c] = data + fnum2[c] * 0x100;
+               ch[c].SetFNum(fnum[c]);
+               break;
+
+       case 0x1a4:     case 0x1a5: case 0x1a6:
+               c += 3;
+       case 0xa4 : case 0xa5: case 0xa6:
+               fnum2[c] = uint8(data);
+               break;
+
+       case 0xa8:      case 0xa9: case 0xaa:
+               fnum3[c] = data + fnum2[c+6] * 0x100;
+               break;
+
+       case 0xac : case 0xad: case 0xae:
+               fnum2[c+6] = uint8(data);
+               break;
+               
+       // Algorithm -------------------------------------------------------------
+       
+       case 0x1b0:     case 0x1b1:  case 0x1b2:
+               c += 3;
+       case 0xb0:      case 0xb1:  case 0xb2:
+               ch[c].SetFB((data >> 3) & 7);
+               ch[c].SetAlgorithm(data & 7);
+               break;
+       
+       case 0x1b4: case 0x1b5: case 0x1b6:
+               c += 3;
+       case 0xb4: case 0xb5: case 0xb6:
+               pan[c] = (data >> 6) & 3;
+               ch[c].SetMS(data);
+               break;
+
+       // LFO -------------------------------------------------------------------
+       case 0x22:
+               modified = reg22 ^ data;
+               reg22 = data;
+               if (modified & 0x8)
+                       lfocount = 0;
+               lfodcount = reg22 & 8 ? lfotable[reg22 & 7] : 0;
+               break;
+
+       // PSG -------------------------------------------------------------------
+       case  0: case  1: case  2: case  3: case  4: case  5: case  6: case  7:
+       case  8: case  9: case 10: case 11: case 12: case 13: case 14: case 15:
+               break;
+
+       // \89¹\90F ------------------------------------------------------------------
+       default:
+               if (c < 3)
+               {
+                       if (addr & 0x100)
+                               c += 3;
+                       OPNBase::SetParameter(&ch[c], addr, data);
+               }
+               break;
+       }
+}
+
+// ---------------------------------------------------------------------------
+//     \83X\83e\81[\83^\83X\83t\83\89\83O\90Ý\92è
+//
+void OPN2Base::SetStatus(uint bits)
+{
+       if (!(status & bits))
+       {
+//             LOG2("SetStatus(%.2x %.2x)\n", bits, stmask);
+               status |= bits & stmask;
+               UpdateStatus();
+       }
+//     else
+//             LOG1("SetStatus(%.2x) - ignored\n", bits);
+}
+
+void OPN2Base::ResetStatus(uint bits)
+{
+       status &= ~bits;
+//     LOG1("ResetStatus(%.2x)\n", bits);
+       UpdateStatus();
+}
+
+inline void OPN2Base::UpdateStatus()
+{
+//     LOG2("%d:INT = %d\n", Diag::GetCPUTick(), (status & stmask & reg29) != 0);
+       Intr((status & stmask & reg29) != 0);
+}
+
+// ---------------------------------------------------------------------------
+//     \8d\87\90¬
+//     in:             buffer          \8d\87\90¬\90æ
+//                     nsamples        \8d\87\90¬\83T\83\93\83v\83\8b\90\94
+//
+void OPN2Base::FMMix(Sample* buffer, int nsamples)
+{
+       if (fmvolume_l > 0 || fmvolume_r > 0)
+       {
+               // \8f\80\94õ
+               // Set F-Number
+               if (!(regtc & 0xc0))
+                       csmch->SetFNum(fnum[csmch-ch]);
+               else
+               {
+                       // \8cø\89Ê\89¹\83\82\81[\83h
+                       csmch->op[0].SetFNum(fnum3[1]); csmch->op[1].SetFNum(fnum3[2]);
+                       csmch->op[2].SetFNum(fnum3[0]); csmch->op[3].SetFNum(fnum[2]);
+               }
+               
+               int act = (((ch[2].Prepare() << 2) | ch[1].Prepare()) << 2) | ch[0].Prepare();
+               if (reg29 & 0x80)
+                       act |= (ch[3].Prepare() | ((ch[4].Prepare() | (ch[5].Prepare() << 2)) << 2)) << 6;
+               if (!(reg22 & 0x08))
+                       act &= 0x555;
+
+               if (act & 0x555)
+               {
+                       Mix6(buffer, nsamples, act);
+               }
+       }
+}
+
+// ---------------------------------------------------------------------------
+
+void OPN2Base::MixSubSL(int activech, ISample** dest)
+{
+       if (activech & 0x001) (*dest[0]  = ch[0].CalcL());
+       if (activech & 0x004) (*dest[1] += ch[1].CalcL());
+       if (activech & 0x010) (*dest[2] += ch[2].CalcL());
+       if (activech & 0x040) (*dest[3] += ch[3].CalcL());
+       if (activech & 0x100) (*dest[4] += ch[4].CalcL());
+       if (activech & 0x400) (*dest[5] += ch[5].CalcL());
+}
+
+inline void OPN2Base::MixSubS(int activech, ISample** dest)
+{
+       if (activech & 0x001) (*dest[0]  = ch[0].Calc());
+       if (activech & 0x004) (*dest[1] += ch[1].Calc());
+       if (activech & 0x010) (*dest[2] += ch[2].Calc());
+       if (activech & 0x040) (*dest[3] += ch[3].Calc());
+       if (activech & 0x100) (*dest[4] += ch[4].Calc());
+       if (activech & 0x400) (*dest[5] += ch[5].Calc());
+}
+
+// ---------------------------------------------------------------------------
+
+void OPN2Base::BuildLFOTable()
+{
+       if (amtable[0] == -1)
+       {
+               for (int c=0; c<256; c++)
+               {
+                       int v;
+                       if (c < 0x40)           v = c * 2 + 0x80;
+                       else if (c < 0xc0)      v = 0x7f - (c - 0x40) * 2 + 0x80;
+                       else                            v = (c - 0xc0) * 2;
+                       pmtable[c] = v;
+
+                       if (c < 0x80)           v = 0xff - c * 2;
+                       else                            v = (c - 0x80) * 2;
+                       amtable[c] = v & ~3;
+               }
+       }
+}
+
+// ---------------------------------------------------------------------------
+
+inline void OPN2Base::LFO()
+{
+//     LOG3("%4d - %8d, %8d\n", c, lfocount, lfodcount);
+
+//     Operator::SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
+//     Operator::SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
+       chip.SetPML(pmtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
+       chip.SetAML(amtable[(lfocount >> (FM_LFOCBITS+1)) & 0xff]);
+       lfocount += lfodcount;
+}
+
+// ---------------------------------------------------------------------------
+//     \8d\87\90¬
+//
+//#define IStoSampleL(s)       ((Limit(s, 0x7fff, -0x8000) * fmvolume_l) >> 14)
+//#define IStoSampleR(s)       ((Limit(s, 0x7fff, -0x8000) * fmvolume_r) >> 14)
+
+void OPN2Base::Mix6(Sample* buffer, int nsamples, int activech)
+{
+       // Mix
+       ISample ibuf[4];
+       ISample* idest[6];
+       idest[0] = &ibuf[pan[0]];
+       idest[1] = &ibuf[pan[1]];
+       idest[2] = &ibuf[pan[2]];
+       idest[3] = &ibuf[pan[3]];
+       idest[4] = &ibuf[pan[4]];
+       idest[5] = &ibuf[pan[5]];
+
+       Sample* limit = buffer + nsamples * 2;
+       for (Sample* dest = buffer; dest < limit; dest+=2)
+       {
+               ibuf[1] = ibuf[2] = ibuf[3] = 0;
+               if (activech & 0xaaa)
+                       LFO(), MixSubSL(activech, idest);
+               else
+                       MixSubS(activech, idest);
+               StoreSample(dest[0], IStoSampleL(ibuf[2] + ibuf[3]));
+               StoreSample(dest[1], IStoSampleR(ibuf[1] + ibuf[3]));
+       }
+}
+// ---------------------------------------------------------------------------
+//     \83X\83e\81[\83g\83Z\81[\83u
+//
+#define OPN2_BASE_STATE_VERSION        1
+
+void OPN2Base::DeclState(void *f)
+{
+
+       OPNBase::DeclState(f);
+       
+       DECL_STATE_ENTRY_1D_ARRAY(pan, sizeof(pan));
+       DECL_STATE_ENTRY_1D_ARRAY(fnum2, sizeof(fnum2));
+       DECL_STATE_ENTRY_UINT8(reg22);
+       DECL_STATE_ENTRY_UINT32(reg29);
+       DECL_STATE_ENTRY_UINT32(stmask);
+       DECL_STATE_ENTRY_UINT32(statusnext);
+       DECL_STATE_ENTRY_UINT32(lfocount);
+       DECL_STATE_ENTRY_UINT32(lfodcount);
+       //state_fio->Fwrite(fnum, sizeof(fnum), 1);
+       //state_fio->Fwrite(fnum3, sizeof(fnum3), 1);
+       for(int i = 0; i < 6; i++) {
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum[i]), i);
+       }               
+       for(int i = 0; i < 3; i++) {
+               DECL_STATE_ENTRY_UINT32_MEMBER((fnum3[i]), i);
+       }               
+       for(int i = 0; i < 6; i++) {
+               ch[i].DeclState(f);
+       }
+}
+
+void OPN2Base::SaveState(void *f)
+{
+       FILEIO *state_fio = (FILEIO *)f;
+       if(state_entry != NULL) {
+               state_entry->save_state(state_fio);
+       }
+       chip.SaveState(f);
+       for(int i = 0; i < 6; i++) {
+               ch[i].SaveState(f);
+       }
+}
+
+bool OPN2Base::LoadState(void *f)
+{
+       FILEIO *state_fio = (FILEIO *)f;
+       
+       bool mb = false;
+       if(state_entry != NULL) {
+               mb = state_entry->load_state(state_fio);
+       }
+       if(!mb) return false;
+       {
+               // Make force-restore around prescaler and timers. 20180625 K.O
+               uint bak = prescale;
+               prescale = 10;
+               SetPrescaler(bak);
+       }
+       if(!chip.LoadState(f)) {
+               return false;
+       }
+       for(int i = 0; i < 6; i++) {
+               if(!ch[i].LoadState(f)) {
+                       return false;
+               }
+       }
+       return true;
+}
+#endif // defined(BUILD_OPN2)
+
+// ---------------------------------------------------------------------------
+//     YM2612(OPN2)
+// ---------------------------------------------------------------------------
+
+#ifdef BUILD_OPN2
+
+// ---------------------------------------------------------------------------
+//     \83\8c\83W\83X\83^\8eæ\93¾
+//
+uint OPN2::GetReg(uint addr)
+{
+       return 0;
+}
+
+// ---------------------------------------------------------------------------
+//     \8d\\92z
+//
+OPN2::OPN2()
+{
+       csmch = &ch[2]; // ToDo: Check register.
+}
+// ---------------------------------------------------------------------------
+
+OPN2::~OPN2()
+{
+}
+
+// ---------------------------------------------------------------------------
+//     \8f\89\8aú\89»
+//
+bool OPN2::Init(uint c, uint r, bool ipflag, const _TCHAR* path)
+{
+       rate = 8000;
+       if (!SetRate(c, r, ipflag))
+               return false;
+       if (!OPN2Base::Init(c, r, ipflag))
+               return false;
+       
+       Reset();
+       return true;
+}
+// ---------------------------------------------------------------------------
+//     \83\8a\83Z\83b\83g
+//
+void OPN2::Reset()
+{
+       reg29 = 0x1f;
+       OPN2Base::Reset();
+}
+// ---------------------------------------------------------------------------
+//     \83T\83\93\83v\83\8a\83\93\83O\83\8c\81[\83g\95Ï\8dX
+//
+bool OPN2::SetRate(uint c, uint r, bool ipflag)
+{
+       if (!OPN2Base::SetRate(c, r, ipflag))
+               return false;
+       return true;
+}
+
+// ---------------------------------------------------------------------------
+//     \83\8c\83W\83X\83^\83A\83\8c\83C\82É\83f\81[\83^\82ð\90Ý\92è
+//
+void OPN2::SetReg(uint addr, uint data)
+{
+       addr &= 0x1ff;
+
+       switch (addr)
+       {
+       case 0x29:
+               reg29 = data;
+//             UpdateStatus(); //?
+               break;
+       
+       default:
+               OPN2Base::SetReg(addr, data);
+               break;
+       }
+}
+
+
+
+// ---------------------------------------------------------------------------
+//     \8d\87\90¬
+//     in:             buffer          \8d\87\90¬\90æ
+//                     nsamples        \8d\87\90¬\83T\83\93\83v\83\8b\90\94
+//
+void OPN2::Mix(Sample* buffer, int nsamples)
+{
+       FMMix(buffer, nsamples);
+}
+
+// ---------------------------------------------------------------------------
+//     \83X\83e\81[\83g\83Z\81[\83u
+//
+#define OPN2_STATE_VERSION     1
+
+void OPN2::DeclState(void *f)
+{
+       p_logger = (CSP_Logger *)f;
+       state_entry = new csp_state_utils(OPN2_STATE_VERSION, chip_num, _T("FMGEN::OPN2::"), p_logger);
+
+       OPN2Base::DeclState(f);
+}
+
+void OPN2::SaveState(void *f)
+{
+       FILEIO *state_fio = (FILEIO *)f;
+       OPN2Base::SaveState(f);
+}
+
+bool OPN2::LoadState(void *f)
+{
+       FILEIO *state_fio = (FILEIO *)f;
+       
+       bool mb = false;
+       mb = OPN2Base::LoadState(f);
+       if(!mb) return false;
+
+       return true;
+}
+
+#endif // BUILD_OPN2
+
 }      // namespace FM