diff --git a/papers/doc/7-systems/opz.md b/papers/doc/7-systems/opz.md index c7812740e..61e931394 100644 --- a/papers/doc/7-systems/opz.md +++ b/papers/doc/7-systems/opz.md @@ -52,6 +52,9 @@ no plans have been made for TX81Z MIDI passthrough, because: - `2Bxy`: set EG shift of operator. - `x` is the operator (1-4). a value of 0 means "all operators". - `y` is the value. +- `2Cxy` set fine multiplier of operator. + - `x` is the operator (1-4). a value of 0 means "all operators". + - `y` is the value. - `2Fxx`: enable envelope hard reset. - this works by inserting a quick release and tiny delay before a new note. - `3xyy`: set fixed frequency of operator 1/2. @@ -97,4 +100,4 @@ no plans have been made for TX81Z MIDI passthrough, because: - `5Cxx`: set D2R/SR of operator 1. - `5Dxx`: set D2R/SR of operator 2. - `5Exx`: set D2R/SR of operator 3. -- `5Fxx`: set D2R/SR of operator 4. \ No newline at end of file +- `5Fxx`: set D2R/SR of operator 4. diff --git a/src/engine/engine.h b/src/engine/engine.h index d15838942..5d6d066c0 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -383,6 +383,7 @@ class DivEngine { bool loadDMF(unsigned char* file, size_t len); bool loadFur(unsigned char* file, size_t len); bool loadMod(unsigned char* file, size_t len); + bool loadFTM(unsigned char* file, size_t len); void loadDMP(SafeReader& reader, std::vector& ret, String& stripPath); void loadTFI(SafeReader& reader, std::vector& ret, String& stripPath); diff --git a/src/engine/fileOps.cpp b/src/engine/fileOps.cpp index b543e3e12..b45baa7ab 100644 --- a/src/engine/fileOps.cpp +++ b/src/engine/fileOps.cpp @@ -26,6 +26,7 @@ #define DIV_READ_SIZE 131072 #define DIV_DMF_MAGIC ".DelekDefleMask." #define DIV_FUR_MAGIC "-Furnace module-" +#define DIV_FTM_MAGIC "FamiTracker Module" struct InflateBlock { unsigned char* buf; @@ -1617,8 +1618,6 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) { return true; } - - bool DivEngine::loadMod(unsigned char* file, size_t len) { struct InvalidHeaderException {}; bool success=false; @@ -2029,10 +2028,43 @@ bool DivEngine::loadMod(unsigned char* file, size_t len) { return success; } +bool DivEngine::loadFTM(unsigned char* file, size_t len) { + SafeReader reader=SafeReader(file,len); + warnings=""; + try { + DivSong ds; + + if (!reader.seek(18,SEEK_SET)) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + ds.version=(unsigned short)reader.readS(); + logI("module version %d (0x%.4x)",ds.version,ds.version); + + if (ds.version>0x0440) { + logE("incompatible version %x!",ds.version); + lastError="incompatible version"; + delete[] file; + return false; + } + + + } catch (EndOfFileException& e) { + logE("premature end of file!"); + lastError="incomplete file"; + delete[] file; + return false; + } + delete[] file; + return true; +} + bool DivEngine::load(unsigned char* f, size_t slen) { unsigned char* file; size_t len; - if (slen<16) { + if (slen<18) { logE("too small!"); lastError="file is too small"; delete[] f; @@ -2137,6 +2169,8 @@ bool DivEngine::load(unsigned char* f, size_t slen) { // step 2: try loading as .fur or .dmf if (memcmp(file,DIV_DMF_MAGIC,16)==0) { return loadDMF(file,len); + } else if (memcmp(file,DIV_FTM_MAGIC,18)==0) { + return loadFTM(file,len); } else if (memcmp(file,DIV_FUR_MAGIC,16)==0) { return loadFur(file,len); } diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index f36b729a1..2c6161e53 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -650,6 +650,134 @@ int DivPlatformArcade::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_DT2: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } case DIV_CMD_FM_AM_DEPTH: { amDepth=c.value; immWrite(0x19,amDepth); diff --git a/src/engine/platform/genesis.cpp b/src/engine/platform/genesis.cpp index 5325c982b..67970298a 100644 --- a/src/engine/platform/genesis.cpp +++ b/src/engine/platform/genesis.cpp @@ -780,7 +780,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) { unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); } - } else { + } else if (c.value<4) { DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; op.ar=c.value2&31; unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; @@ -788,6 +788,134 @@ int DivPlatformGenesis::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/genesisext.cpp b/src/engine/platform/genesisext.cpp index d5c98a56c..ae4d74cd9 100644 --- a/src/engine/platform/genesisext.cpp +++ b/src/engine/platform/genesisext.cpp @@ -235,6 +235,134 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_GET_VOLMAX: return 127; break; diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 788cfe245..277de8835 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -604,8 +604,10 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { case DIV_CMD_FM_MULT: { unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; - op.mult=c.value2&15; - rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + if (!op.egt) { + op.mult=c.value2&15; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } break; } case DIV_CMD_FM_TL: { @@ -639,6 +641,221 @@ int DivPlatformTX81Z::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_DT2: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt2=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,(op.d2r&31)|(op.dt2<<6)); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + if (!op.egt) { + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + if (!op.egt) { + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + } + } + break; + } + case DIV_CMD_FM_EG_SHIFT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ksl=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ksl=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + break; + } + case DIV_CMD_FM_REV: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dam=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dam=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_EGS_REV,(op.dam&7)|(op.ksl<<6)); + } + break; + } + case DIV_CMD_FM_WS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ws=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ws=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + break; + } + case DIV_CMD_FM_FINE: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + if (!op.egt) { + op.dvb=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + if (!op.egt) { + op.dvb=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + } + break; + } + case DIV_CMD_FM_FIXFREQ: { + if (c.value<0 || c.value>3) break; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.egt=(c.value2>0); + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.egt<<5)|(op.rs<<6)); + if (op.egt) { + rWrite(baseAddr+ADDR_MULT_DT,((c.value2>>4)&15)|((c.value2>>8)&7)); + rWrite(baseAddr+ADDR_WS_FINE,(c.value2&15)|(op.ws<<4)); + } else { + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|((op.egt?(op.dt&7):dtTable[op.dt&7])<<4)); + rWrite(baseAddr+ADDR_WS_FINE,(op.dvb&15)|(op.ws<<4)); + } + break; + } case DIV_CMD_FM_AM_DEPTH: { amDepth=c.value; immWrite(0x19,amDepth); diff --git a/src/engine/platform/ym2610.cpp b/src/engine/platform/ym2610.cpp index a45266086..85aa36339 100644 --- a/src/engine/platform/ym2610.cpp +++ b/src/engine/platform/ym2610.cpp @@ -1088,6 +1088,134 @@ int DivPlatformYM2610::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610b.cpp b/src/engine/platform/ym2610b.cpp index a66f0b575..83aa0fb9c 100644 --- a/src/engine/platform/ym2610b.cpp +++ b/src/engine/platform/ym2610b.cpp @@ -1066,6 +1066,134 @@ int DivPlatformYM2610B::dispatch(DivCommand c) { } break; } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + break; + } case DIV_CMD_FM_HARD_RESET: chan[c.chan].hardReset=c.value; break; diff --git a/src/engine/platform/ym2610bext.cpp b/src/engine/platform/ym2610bext.cpp index 87c635736..03b91fccc 100644 --- a/src/engine/platform/ym2610bext.cpp +++ b/src/engine/platform/ym2610bext.cpp @@ -175,17 +175,146 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { - DivInstrumentFM::Operator op=ins->fm.op[i]; + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[2]|opOffs[i]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); } } else { - DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[2].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } break; } diff --git a/src/engine/platform/ym2610ext.cpp b/src/engine/platform/ym2610ext.cpp index 013e7b954..1ffbd74fe 100644 --- a/src/engine/platform/ym2610ext.cpp +++ b/src/engine/platform/ym2610ext.cpp @@ -175,17 +175,146 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) { break; } case DIV_CMD_FM_AR: { - DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM); if (c.value<0) { for (int i=0; i<4; i++) { - DivInstrumentFM::Operator op=ins->fm.op[i]; + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[1]|opOffs[i]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); } } else { - DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]]; + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.ar=c.value2&31; unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; - rWrite(baseAddr+0x50,(c.value2&31)|(op.rs<<6)); + rWrite(baseAddr+0x50,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_RS: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.rs=c.value2&3; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6)); + } + break; + } + case DIV_CMD_FM_AM: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.am=c.value2&1; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_DR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.dr=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7)); + } + break; + } + case DIV_CMD_FM_SL: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.sl=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_RR: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.rr=c.value2&15; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4)); + } + break; + } + case DIV_CMD_FM_D2R: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.d2r=c.value2&31; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31); + } + break; + } + case DIV_CMD_FM_DT: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.dt=c.value&7; + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.dt=c.value2&7; + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4)); + } + break; + } + case DIV_CMD_FM_SSG: { + if (c.value<0) { + for (int i=0; i<4; i++) { + DivInstrumentFM::Operator& op=chan[1].state.op[i]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[1]|opOffs[i]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); + } + } else if (c.value<4) { + DivInstrumentFM::Operator& op=chan[1].state.op[orderedOps[c.value]]; + op.ssgEnv=8^(c.value2&15); + unsigned short baseAddr=chanOffs[1]|opOffs[orderedOps[c.value]]; + rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15); } break; } diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 70768fe0f..1a3714019 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -496,6 +496,7 @@ void DivEngine::registerSystems() { OP_EFFECT_SINGLE(0x28,DIV_CMD_FM_REV,4,7); OP_EFFECT_SINGLE(0x2a,DIV_CMD_FM_WS,4,7); OP_EFFECT_SINGLE(0x2b,DIV_CMD_FM_EG_SHIFT,4,3); + OP_EFFECT_SINGLE(0x2c,DIV_CMD_FM_FINE,4,15); default: return false; }