/** * Furnace Tracker - multi-system chiptune tracker * Copyright (C) 2021-2023 tildearrow and contributors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dispatch.h" #include "engine.h" #include "instrument.h" #include "song.h" #include "../ta-log.h" DivSysDef* DivEngine::sysDefs[DIV_MAX_CHIP_DEFS]; DivSystem DivEngine::sysFileMapFur[DIV_MAX_CHIP_DEFS]; DivSystem DivEngine::sysFileMapDMF[DIV_MAX_CHIP_DEFS]; DivSystem DivEngine::systemFromFileFur(unsigned char val) { return sysFileMapFur[val]; } unsigned char DivEngine::systemToFileFur(DivSystem val) { if (sysDefs[val]==NULL) return 0; return sysDefs[val]->id; } DivSystem DivEngine::systemFromFileDMF(unsigned char val) { return sysFileMapDMF[val]; } unsigned char DivEngine::systemToFileDMF(DivSystem val) { if (sysDefs[val]==NULL) return 0; return sysDefs[val]->id_DMF; } int DivEngine::getChannelCount(DivSystem sys) { if (sysDefs[sys]==NULL) return 0; return sysDefs[sys]->channels; } int DivEngine::getTotalChannelCount() { return chans; } std::vector& DivEngine::getPossibleInsTypes() { return possibleInsTypes; } // for pre-dev103 modules String DivEngine::getSongSystemLegacyName(DivSong& ds, bool isMultiSystemAcceptable) { switch (ds.systemLen) { case 0: return "help! what's going on!"; case 1: if (ds.system[0]==DIV_SYSTEM_AY8910) { switch (ds.systemFlags[0].getInt("chipType",0)) { case 0: // AY-3-8910 switch (ds.systemFlags[0].getInt("clockSel",0)) { case 0: // AY-3-8910, 1.79MHz case 1: // AY-3-8910, 1.77MHz case 2: // AY-3-8910, 1.75MHz return "ZX Spectrum"; case 3: // AY-3-8910, 2MHz return "Fujitsu Micro-7"; case 4: // AY-3-8910, 1.5MHz return "Vectrex"; case 5: // AY-3-8910, 1MHz return "Amstrad CPC"; default: return "AY-3-8910"; } break; case 1: // YM2149 switch (ds.systemFlags[0].getInt("clockSel",0)) { case 0: // YM2149, 1.79MHz return "MSX"; case 3: // YM2149, 2MHz return "Atari ST"; default: return "Yamaha YM2149"; } break; case 2: // 5B switch (ds.systemFlags[0].getInt("clockSel",0)) { case 6: // 5B NTSC return "Sunsoft 5B standalone"; case 8: // 5B PAL return "Sunsoft 5B standalone (PAL)"; default: return "Overclocked Sunsoft 5B"; } break; case 3: // AY-3-8914 switch (ds.systemFlags[0].getInt("clockSel",0)) { case 0: // AY-3-8914, 1.79MHz return "Intellivision"; case 3: // AY-3-8914, 2MHz return "Intellivision (PAL)"; default: return "Intellivision"; } break; } } else if (ds.system[0]==DIV_SYSTEM_SMS) { switch (ds.systemFlags[0].getInt("chipType",0)) { case 0: switch (ds.systemFlags[0].getInt("clockSel",0)) { case 0: case 1: return "Sega Master System"; } break; case 1: switch (ds.systemFlags[0].getInt("clockSel",0)) { case 2: return "BBC Micro"; } break; } } else if (ds.system[0]==DIV_SYSTEM_YM2612) { switch (ds.systemFlags[0].getInt("clockSel",0)) { case 2: return "FM Towns"; } } else if (ds.system[0]==DIV_SYSTEM_YM2151) { switch (ds.systemFlags[0].getInt("clockSel",0)) { case 2: return "Sharp X68000"; } } else if (ds.system[0]==DIV_SYSTEM_SAA1099) { switch (ds.systemFlags[0].getInt("clockSel",0)) { case 0: return "SAM Coupé"; } } return getSystemName(ds.system[0]); case 2: if (ds.system[0]==DIV_SYSTEM_YM2612 && ds.system[1]==DIV_SYSTEM_SMS) { return "Sega Genesis/Mega Drive"; } if (ds.system[0]==DIV_SYSTEM_YM2612_EXT && ds.system[1]==DIV_SYSTEM_SMS) { return "Sega Genesis Extended Channel 3"; } if (ds.system[0]==DIV_SYSTEM_OPLL && ds.system[1]==DIV_SYSTEM_SMS) { return "NTSC-J Sega Master System"; } if (ds.system[0]==DIV_SYSTEM_OPLL_DRUMS && ds.system[1]==DIV_SYSTEM_SMS) { return "NTSC-J Sega Master System + drums"; } if (ds.system[0]==DIV_SYSTEM_OPLL && ds.system[1]==DIV_SYSTEM_AY8910) { return "MSX-MUSIC"; } if (ds.system[0]==DIV_SYSTEM_OPLL_DRUMS && ds.system[1]==DIV_SYSTEM_AY8910) { return "MSX-MUSIC + drums"; } if (ds.system[0]==DIV_SYSTEM_C64_6581 && ds.system[1]==DIV_SYSTEM_C64_6581) { return "Commodore 64 with dual 6581"; } if (ds.system[0]==DIV_SYSTEM_C64_8580 && ds.system[1]==DIV_SYSTEM_C64_8580) { return "Commodore 64 with dual 8580"; } if (ds.system[0]==DIV_SYSTEM_YM2151 && ds.system[1]==DIV_SYSTEM_SEGAPCM_COMPAT) { return "YM2151 + SegaPCM Arcade (compatibility)"; } if (ds.system[0]==DIV_SYSTEM_YM2151 && ds.system[1]==DIV_SYSTEM_SEGAPCM) { return "YM2151 + SegaPCM Arcade"; } if (ds.system[0]==DIV_SYSTEM_SAA1099 && ds.system[1]==DIV_SYSTEM_SAA1099) { return "Creative Music System"; } if (ds.system[0]==DIV_SYSTEM_GB && ds.system[1]==DIV_SYSTEM_AY8910) { return "Game Boy with AY expansion"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_VRC6) { return "Famicom + Konami VRC6"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_VRC7) { return "Famicom + Konami VRC7"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_OPLL) { return "Family Noraebang"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_FDS) { return "Famicom Disk System"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_N163) { return "Famicom + Namco 163"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_MMC5) { return "Famicom + MMC5"; } if (ds.system[0]==DIV_SYSTEM_NES && ds.system[1]==DIV_SYSTEM_AY8910) { return "Famicom + Sunsoft 5B"; } if (ds.system[0]==DIV_SYSTEM_AY8910 && ds.system[1]==DIV_SYSTEM_AY8910) { return "Bally Midway MCR"; } if (ds.system[0]==DIV_SYSTEM_YM2151 && ds.system[1]==DIV_SYSTEM_VERA) { return "Commander X16"; } break; case 3: if (ds.system[0]==DIV_SYSTEM_AY8910 && ds.system[1]==DIV_SYSTEM_AY8910 && ds.system[2]==DIV_SYSTEM_BUBSYS_WSG) { return "Konami Bubble System"; } break; } if (isMultiSystemAcceptable) return "multi-system"; String ret=""; for (int i=0; i0) ret+=" + "; ret+=getSystemName(ds.system[i]); } return ret; } const char* DivEngine::getSystemName(DivSystem sys) { if (sysDefs[sys]==NULL) return "Unknown"; return sysDefs[sys]->name; } const char* DivEngine::getSystemNameJ(DivSystem sys) { if (sysDefs[sys]==NULL) return "不明"; if (sysDefs[sys]->nameJ==NULL) return ""; return sysDefs[sys]->nameJ; /* switch (sys) { case DIV_SYSTEM_NULL: return "不明"; case DIV_SYSTEM_YMU759: return ""; case DIV_SYSTEM_GENESIS: return "セガメガドライブ"; case DIV_SYSTEM_SMS: case DIV_SYSTEM_SMS_OPLL: return "セガマスターシステム"; case DIV_SYSTEM_GB: return "ゲームボーイ"; case DIV_SYSTEM_PCE: return "PCエンジン"; case DIV_SYSTEM_NES: return "ファミリーコンピュータ"; case DIV_SYSTEM_C64_6581: return "コモドール64 (6581)"; case DIV_SYSTEM_C64_8580: return "コモドール64 (8580)"; case DIV_SYSTEM_ARCADE: return "Arcade"; case DIV_SYSTEM_GENESIS_EXT: return ""; case DIV_SYSTEM_YM2610: return "業務用ネオジオ"; case DIV_SYSTEM_YM2610_EXT: return "業務用ネオジオ"; case DIV_SYSTEM_YM2610_FULL: return "業務用ネオジオ"; case DIV_SYSTEM_YM2610_FULL_EXT: return "業務用ネオジオ"; case DIV_SYSTEM_AY8910: return ""; case DIV_SYSTEM_AMIGA: return ""; case DIV_SYSTEM_YM2151: return ""; case DIV_SYSTEM_YM2612: return ""; case DIV_SYSTEM_TIA: return ""; case DIV_SYSTEM_SAA1099: return ""; case DIV_SYSTEM_AY8930: return ""; default: // TODO return ""; } */ } const DivSysDef* DivEngine::getSystemDef(DivSystem sys) { return sysDefs[sys]; } bool DivEngine::isFMSystem(DivSystem sys) { if (sysDefs[sys]==NULL) return false; return sysDefs[sys]->isFM; } bool DivEngine::isSTDSystem(DivSystem sys) { if (sysDefs[sys]==NULL) return false; return sysDefs[sys]->isSTD; } const char* DivEngine::getChannelName(int chan) { if (chan<0 || chan>chans) return "??"; if (!curSubSong->chanName[chan].empty()) return curSubSong->chanName[chan].c_str(); if (sysDefs[sysOfChan[chan]]==NULL) return "??"; const char* ret=sysDefs[sysOfChan[chan]]->chanNames[dispatchChanOfChan[chan]]; if (ret==NULL) return "??"; return ret; } const char* DivEngine::getChannelShortName(int chan) { if (chan<0 || chan>chans) return "??"; if (!curSubSong->chanShortName[chan].empty()) return curSubSong->chanShortName[chan].c_str(); if (sysDefs[sysOfChan[chan]]==NULL) return "??"; const char* ret=sysDefs[sysOfChan[chan]]->chanShortNames[dispatchChanOfChan[chan]]; if (ret==NULL) return "??"; return ret; } int DivEngine::getChannelType(int chan) { if (chan<0 || chan>chans) return DIV_CH_NOISE; if (sysDefs[sysOfChan[chan]]==NULL) return DIV_CH_NOISE; return sysDefs[sysOfChan[chan]]->chanTypes[dispatchChanOfChan[chan]]; } DivInstrumentType DivEngine::getPreferInsType(int chan) { if (chan<0 || chan>chans) return DIV_INS_STD; if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_STD; return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][0]; } DivInstrumentType DivEngine::getPreferInsSecondType(int chan) { if (chan<0 || chan>chans) return DIV_INS_NULL; if (sysDefs[sysOfChan[chan]]==NULL) return DIV_INS_NULL; return sysDefs[sysOfChan[chan]]->chanInsType[dispatchChanOfChan[chan]][1]; } int DivEngine::minVGMVersion(DivSystem which) { if (sysDefs[which]==NULL) return 0; return sysDefs[which]->vgmVersion; } #define IS_YM2610 (sysOfChan[ch]==DIV_SYSTEM_YM2610 || sysOfChan[ch]==DIV_SYSTEM_YM2610_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL || sysOfChan[ch]==DIV_SYSTEM_YM2610_FULL_EXT || sysOfChan[ch]==DIV_SYSTEM_YM2610B || sysOfChan[ch]==DIV_SYSTEM_YM2610B_EXT) #define IS_OPM_LIKE (sysOfChan[ch]==DIV_SYSTEM_YM2151 || sysOfChan[ch]==DIV_SYSTEM_OPZ) #define OP_EFFECT_MULTI(x,c,op,mask) \ case x: \ dispatchCmd(DivCommand(c,ch,op,effectVal&mask)); \ break; #define OP_EFFECT_SINGLE(x,c,maxOp,mask) \ case x: \ if ((effectVal>>4)>=0 && (effectVal>>4)<=maxOp) { \ dispatchCmd(DivCommand(c,ch,(effectVal>>4)-1,effectVal&mask)); \ } \ break; // define systems like: // sysDefs[DIV_SYSTEM_ID]=new DivSysDef( // "Name", "Name (japanese, optional)", fileID, fileID_DMF, channels, isFM, isSTD, vgmVersion, // {"Channel Names", ...}, // {"Channel Short Names", ...}, // {chanTypes, ...}, // {chanPreferInsType, ...}, // {chanPreferInsType2, ...}, (optional) // {{effect, {DIV_CMD_xx, "Description"}}, ...}, (effect handler, optional) // {{effect, {DIV_CMD_xx, "Description"}}, ...} (post effect handler, optional) // ); template int constVal(unsigned char, unsigned char) { return val; }; int effectVal(unsigned char, unsigned char val) { return val; }; int negEffectVal(unsigned char, unsigned char val) { return -(int)val; }; template int effectValAnd(unsigned char, unsigned char val) { return val&mask; }; template int effectValShift(unsigned char, unsigned char val) { return val< int effectOpVal(unsigned char, unsigned char val) { if ((val>>4)>maxOp) throw DivDoNotHandleEffect(); return (val>>4)-1; }; template int effectOpValNoZero(unsigned char, unsigned char val) { if ((val>>4)<1 || (val>>4)>maxOp) throw DivDoNotHandleEffect(); return (val>>4)-1; }; template int effectValLong(unsigned char cmd, unsigned char val) { return ((((unsigned int)cmd)&((1<<(bits-8))-1))<<8)|((unsigned int)val); }; template int effectValLongShift(unsigned char cmd, unsigned char val) { return (((((unsigned int)cmd)&((1<<(bits-8))-1))<<8)|((unsigned int)val))<, effectVal}}, {0x2f, {DIV_CMD_AY_IO_WRITE, "2Fxx: Write to I/O port B", constVal<1>, effectVal}}, }; EffectHandlerMap ay8930PostEffectHandlerMap(ayPostEffectHandlerMap); ay8930PostEffectHandlerMap.insert({ {0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle (0 to 8)", [](unsigned char, unsigned char val) -> int { return 0x10+(val&15); }}}, {0x27, {DIV_CMD_AY_NOISE_MASK_AND, "27xx: Set noise AND mask"}}, {0x28, {DIV_CMD_AY_NOISE_MASK_OR, "28xx: Set noise OR mask"}}, {0x2d, {DIV_CMD_AY_IO_WRITE, "2Dxx: NOT TO BE EMPLOYED BY THE COMPOSER", constVal<255>, effectVal}}, }); EffectHandlerMap fmEffectHandlerMap={ {0x30, {DIV_CMD_FM_HARD_RESET, "30xx: Toggle hard envelope reset on new notes"}}, }; EffectHandlerMap fmExtChEffectHandlerMap(fmEffectHandlerMap); fmExtChEffectHandlerMap.insert({ {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle extended channel 3 mode"}}, }); EffectHandlerMap fmOPN2EffectHandlerMap(fmExtChEffectHandlerMap); fmOPN2EffectHandlerMap.insert({ {0x17, {DIV_CMD_SAMPLE_MODE, "17xx: Toggle PCM mode (LEGACY)"}}, {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}}, }); EffectHandlerMap fmOPLDrumsEffectHandlerMap(fmEffectHandlerMap); fmOPLDrumsEffectHandlerMap.insert({ {0x18, {DIV_CMD_FM_EXTCH, "18xx: Toggle drums mode (1: enabled; 0: disabled)"}}, }); EffectHandlerMap fmOPNPostEffectHandlerMap={ {0x11, {DIV_CMD_FM_FB, "11xx: Set feedback (0 to 7)"}}, {0x12, {DIV_CMD_FM_TL, "12xx: Set level of operator 1 (0 highest, 7F lowest)", constVal<0>, effectVal}}, {0x13, {DIV_CMD_FM_TL, "13xx: Set level of operator 2 (0 highest, 7F lowest)", constVal<1>, effectVal}}, {0x14, {DIV_CMD_FM_TL, "14xx: Set level of operator 3 (0 highest, 7F lowest)", constVal<2>, effectVal}}, {0x15, {DIV_CMD_FM_TL, "15xx: Set level of operator 4 (0 highest, 7F lowest)", constVal<3>, effectVal}}, {0x16, {DIV_CMD_FM_MULT, "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)", effectOpValNoZero<4>, effectValAnd<15>}}, {0x19, {DIV_CMD_FM_AR, "19xx: Set attack of all operators (0 to 1F)", constVal<-1>, effectValAnd<31>}}, {0x1a, {DIV_CMD_FM_AR, "1Axx: Set attack of operator 1 (0 to 1F)", constVal<0>, effectValAnd<31>}}, {0x1b, {DIV_CMD_FM_AR, "1Bxx: Set attack of operator 2 (0 to 1F)", constVal<1>, effectValAnd<31>}}, {0x1c, {DIV_CMD_FM_AR, "1Cxx: Set attack of operator 3 (0 to 1F)", constVal<2>, effectValAnd<31>}}, {0x1d, {DIV_CMD_FM_AR, "1Dxx: Set attack of operator 4 (0 to 1F)", constVal<3>, effectValAnd<31>}}, {0x50, {DIV_CMD_FM_AM, "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)", effectOpVal<4>, effectValAnd<1>}}, {0x51, {DIV_CMD_FM_SL, "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)", effectOpVal<4>, effectValAnd<15>}}, {0x52, {DIV_CMD_FM_RR, "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)", effectOpVal<4>, effectValAnd<15>}}, {0x53, {DIV_CMD_FM_DT, "53xy: Set detune (x: operator from 1 to 4 (0 for all ops); y: detune where 3 is center)", effectOpVal<4>, effectValAnd<7>}}, {0x54, {DIV_CMD_FM_RS, "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, {0x56, {DIV_CMD_FM_DR, "56xx: Set decay of all operators (0 to 1F)", constVal<-1>, effectValAnd<31>}}, {0x57, {DIV_CMD_FM_DR, "57xx: Set decay of operator 1 (0 to 1F)", constVal<0>, effectValAnd<31>}}, {0x58, {DIV_CMD_FM_DR, "58xx: Set decay of operator 2 (0 to 1F)", constVal<1>, effectValAnd<31>}}, {0x59, {DIV_CMD_FM_DR, "59xx: Set decay of operator 3 (0 to 1F)", constVal<2>, effectValAnd<31>}}, {0x5a, {DIV_CMD_FM_DR, "5Axx: Set decay of operator 4 (0 to 1F)", constVal<3>, effectValAnd<31>}}, {0x5b, {DIV_CMD_FM_D2R, "5Bxx: Set decay 2 of all operators (0 to 1F)", constVal<-1>, effectValAnd<31>}}, {0x5c, {DIV_CMD_FM_D2R, "5Cxx: Set decay 2 of operator 1 (0 to 1F)", constVal<0>, effectValAnd<31>}}, {0x5d, {DIV_CMD_FM_D2R, "5Dxx: Set decay 2 of operator 2 (0 to 1F)", constVal<1>, effectValAnd<31>}}, {0x5e, {DIV_CMD_FM_D2R, "5Exx: Set decay 2 of operator 3 (0 to 1F)", constVal<2>, effectValAnd<31>}}, {0x5f, {DIV_CMD_FM_D2R, "5Fxx: Set decay 2 of operator 4 (0 to 1F)", constVal<3>, effectValAnd<31>}}, }; EffectHandlerMap fmOPMPostEffectHandlerMap(fmOPNPostEffectHandlerMap); fmOPMPostEffectHandlerMap.insert({ {0x10, {DIV_CMD_STD_NOISE_FREQ, "10xx: Set noise frequency (xx: value; 0 disables noise)"}}, {0x17, {DIV_CMD_FM_LFO, "17xx: Set LFO speed"}}, {0x18, {DIV_CMD_FM_LFO_WAVE, "18xx: Set LFO waveform (0 saw, 1 square, 2 triangle, 3 noise)"}}, {0x1e, {DIV_CMD_FM_AM_DEPTH, "1Exx: Set AM depth (0 to 7F)", effectValAnd<127>}}, {0x1f, {DIV_CMD_FM_PM_DEPTH, "1Fxx: Set PM depth (0 to 7F)", effectValAnd<127>}}, {0x55, {DIV_CMD_FM_SSG, "55xy: Set detune 2 (x: operator from 1 to 4 (0 for all ops); y: detune from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, }); EffectHandlerMap fmOPZPostEffectHandlerMap(fmOPMPostEffectHandlerMap); fmOPZPostEffectHandlerMap.insert({ {0x24, {DIV_CMD_FM_LFO2, "24xx: Set LFO 2 speed"}}, {0x25, {DIV_CMD_FM_LFO2_WAVE, "25xx: Set LFO 2 waveform (0 saw, 1 square, 2 triangle, 3 noise)"}}, {0x26, {DIV_CMD_FM_AM2_DEPTH, "26xx: Set AM 2 depth (0 to 7F)", effectValAnd<127>}}, {0x27, {DIV_CMD_FM_PM2_DEPTH, "27xx: Set PM 2 depth (0 to 7F)", effectValAnd<127>}}, {0x28, {DIV_CMD_FM_REV, "28xy: Set reverb (x: operator from 1 to 4 (0 for all ops); y: reverb from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2a, {DIV_CMD_FM_WS, "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 7)", effectOpVal<4>, effectValAnd<7>}}, {0x2b, {DIV_CMD_FM_EG_SHIFT, "2Bxy: Set envelope generator shift (x: operator from 1 to 4 (0 for all ops); y: shift from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, {0x2c, {DIV_CMD_FM_FINE, "2Cxy: Set fine multiplier (x: operator from 1 to 4 (0 for all ops); y: fine)", effectOpVal<4>, effectValAnd<15>}}, }); const EffectHandler fmOPZFixFreqHandler[4]={ {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency of operator 1 (x: octave from 0 to 7; y: frequency)", constVal<0>, effectValLong<11>}, {DIV_CMD_FM_FIXFREQ, "3xyy: Set fixed frequency of operator 2 (x: octave from 8 to F; y: frequency)", constVal<1>, effectValLong<11>}, {DIV_CMD_FM_FIXFREQ, "4xyy: Set fixed frequency of operator 3 (x: octave from 0 to 7; y: frequency)", constVal<2>, effectValLong<11>}, {DIV_CMD_FM_FIXFREQ, "4xyy: Set fixed frequency of operator 4 (x: octave from 8 to F; y: frequency)", constVal<3>, effectValLong<11>}, }; for (int i=0; i<32; i++) { fmOPZPostEffectHandlerMap.emplace(0x30+i,fmOPZFixFreqHandler[i/8]); } fmOPNPostEffectHandlerMap.insert({ {0x10, {DIV_CMD_FM_LFO, "10xy: Setup LFO (x: enable; y: speed)"}}, {0x55, {DIV_CMD_FM_SSG, "55xy: Set SSG envelope (x: operator from 1 to 4 (0 for all ops); y: 0-7 on, 8 off)", effectOpVal<4>, effectValAnd<15>}}, }); EffectHandlerMap fmOPN2PostEffectHandlerMap(fmOPNPostEffectHandlerMap); fmOPNPostEffectHandlerMap.insert(ayPostEffectHandlerMap.begin(), ayPostEffectHandlerMap.end()); EffectHandlerMap fmOPNAPostEffectHandlerMap(fmOPNPostEffectHandlerMap); fmOPNAPostEffectHandlerMap.insert({ {0x31, {DIV_CMD_ADPCMA_GLOBAL_VOLUME, "1Fxx: Set ADPCM-A global volume (0 to 3F)"}}, }); EffectHandlerMap fmOPLLPostEffectHandlerMap={ {0x11, {DIV_CMD_FM_FB, "11xx: Set feedback (0 to 7)"}}, {0x12, {DIV_CMD_FM_TL, "12xx: Set level of operator 1 (0 highest, 3F lowest)", constVal<0>, effectVal}}, {0x13, {DIV_CMD_FM_TL, "13xx: Set level of operator 2 (0 highest, 3F lowest)", constVal<1>, effectVal}}, {0x16, {DIV_CMD_FM_MULT, "16xy: Set operator multiplier (x: operator from 1 to 2; y: multiplier)", effectOpValNoZero<2>, effectValAnd<15>}}, {0x19, {DIV_CMD_FM_AR, "19xx: Set attack of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, {0x1a, {DIV_CMD_FM_AR, "1Axx: Set attack of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, {0x1b, {DIV_CMD_FM_AR, "1Bxx: Set attack of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, {0x50, {DIV_CMD_FM_AM, "50xy: Set AM (x: operator from 1 to 2 (0 for all ops); y: AM)", effectOpVal<2>, effectValAnd<1>}}, {0x51, {DIV_CMD_FM_SL, "51xy: Set sustain level (x: operator from 1 to 2 (0 for all ops); y: sustain)", effectOpVal<2>, effectValAnd<15>}}, {0x52, {DIV_CMD_FM_RR, "52xy: Set release (x: operator from 1 to 2 (0 for all ops); y: release)", effectOpVal<2>, effectValAnd<15>}}, {0x53, {DIV_CMD_FM_VIB, "53xy: Set vibrato (x: operator from 1 to 2 (0 for all ops); y: enabled)", effectOpVal<2>, effectValAnd<1>}}, {0x54, {DIV_CMD_FM_RS, "54xy: Set envelope scale (x: operator from 1 to 2 (0 for all ops); y: scale from 0 to 3)", effectOpVal<2>, effectValAnd<3>}}, {0x55, {DIV_CMD_FM_SUS, "55xy: Set envelope sustain (x: operator from 1 to 2 (0 for all ops); y: enabled)", effectOpVal<2>, effectValAnd<1>}}, {0x56, {DIV_CMD_FM_DR, "56xx: Set decay of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, {0x57, {DIV_CMD_FM_DR, "57xx: Set decay of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, {0x58, {DIV_CMD_FM_DR, "58xx: Set decay of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, {0x5b, {DIV_CMD_FM_KSR, "5Bxy: Set whether key will scale envelope (x: operator from 1 to 2 (0 for all ops); y: enabled)", effectOpVal<2>, effectValAnd<1>}}, }; EffectHandlerMap fmOPLPostEffectHandlerMap={ {0x10, {DIV_CMD_FM_LFO, "10xx: Set global AM depth (0: 1dB, 1: 4.8dB)", effectValAnd<1>}}, {0x11, {DIV_CMD_FM_FB, "11xx: Set feedback (0 to 7)"}}, {0x12, {DIV_CMD_FM_TL, "12xx: Set level of operator 1 (0 highest, 3F lowest)", constVal<0>, effectVal}}, {0x13, {DIV_CMD_FM_TL, "13xx: Set level of operator 2 (0 highest, 3F lowest)", constVal<1>, effectVal}}, {0x14, {DIV_CMD_FM_TL, "14xx: Set level of operator 3 (0 highest, 3F lowest)", constVal<2>, effectVal}}, {0x15, {DIV_CMD_FM_TL, "15xx: Set level of operator 4 (0 highest, 3F lowest)", constVal<3>, effectVal}}, {0x16, {DIV_CMD_FM_MULT, "16xy: Set operator multiplier (x: operator from 1 to 4; y: multiplier)", effectOpValNoZero<4>, effectValAnd<15>}}, {0x17, {DIV_CMD_FM_LFO, "17xx: Set global vibrato depth (0: normal, 1: double)", [](unsigned char, unsigned char val) -> int { return (val&1)+2; }}}, {0x19, {DIV_CMD_FM_AR, "19xx: Set attack of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, {0x1a, {DIV_CMD_FM_AR, "1Axx: Set attack of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, {0x1b, {DIV_CMD_FM_AR, "1Bxx: Set attack of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, {0x1c, {DIV_CMD_FM_AR, "1Cxx: Set attack of operator 3 (0 to F)", constVal<2>, effectValAnd<15>}}, {0x1d, {DIV_CMD_FM_AR, "1Dxx: Set attack of operator 4 (0 to F)", constVal<3>, effectValAnd<15>}}, {0x2a, {DIV_CMD_FM_WS, "2Axy: Set waveform (x: operator from 1 to 4 (0 for all ops); y: waveform from 0 to 3 in OPL2 and 0 to 7 in OPL3)", effectOpVal<4>, effectValAnd<7>}}, {0x50, {DIV_CMD_FM_AM, "50xy: Set AM (x: operator from 1 to 4 (0 for all ops); y: AM)", effectOpVal<4>, effectValAnd<1>}}, {0x51, {DIV_CMD_FM_SL, "51xy: Set sustain level (x: operator from 1 to 4 (0 for all ops); y: sustain)", effectOpVal<4>, effectValAnd<15>}}, {0x52, {DIV_CMD_FM_RR, "52xy: Set release (x: operator from 1 to 4 (0 for all ops); y: release)", effectOpVal<4>, effectValAnd<15>}}, {0x53, {DIV_CMD_FM_VIB, "53xy: Set vibrato (x: operator from 1 to 4 (0 for all ops); y: enabled)", effectOpVal<4>, effectValAnd<1>}}, {0x54, {DIV_CMD_FM_RS, "54xy: Set envelope scale (x: operator from 1 to 4 (0 for all ops); y: scale from 0 to 3)", effectOpVal<4>, effectValAnd<3>}}, {0x55, {DIV_CMD_FM_SUS, "55xy: Set envelope sustain (x: operator from 1 to 4 (0 for all ops); y: enabled)", effectOpVal<4>, effectValAnd<1>}}, {0x56, {DIV_CMD_FM_DR, "56xx: Set decay of all operators (0 to F)", constVal<-1>, effectValAnd<15>}}, {0x57, {DIV_CMD_FM_DR, "57xx: Set decay of operator 1 (0 to F)", constVal<0>, effectValAnd<15>}}, {0x58, {DIV_CMD_FM_DR, "58xx: Set decay of operator 2 (0 to F)", constVal<1>, effectValAnd<15>}}, {0x59, {DIV_CMD_FM_DR, "59xx: Set decay of operator 3 (0 to F)", constVal<2>, effectValAnd<15>}}, {0x5a, {DIV_CMD_FM_DR, "5Axx: Set decay of operator 4 (0 to F)", constVal<3>, effectValAnd<15>}}, {0x5b, {DIV_CMD_FM_KSR, "5Bxy: Set whether key will scale envelope (x: operator from 1 to 4 (0 for all ops); y: enabled)", effectOpVal<4>, effectValAnd<1>}}, }; EffectHandlerMap c64PostEffectHandlerMap={ {0x10, {DIV_CMD_WAVE, "10xx: Set waveform (bit 0: triangle; bit 1: saw; bit 2: pulse; bit 3: noise)"}}, {0x11, {DIV_CMD_C64_CUTOFF, "11xx: Set coarse cutoff (not recommended; use 4xxx instead)"}}, {0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set coarse pulse width (not recommended; use 3xxx instead)"}}, {0x13, {DIV_CMD_C64_RESONANCE, "13xx: Set resonance (0 to F)"}}, {0x14, {DIV_CMD_C64_FILTER_MODE, "14xx: Set filter mode (bit 0: low pass; bit 1: band pass; bit 2: high pass)"}}, {0x15, {DIV_CMD_C64_RESET_TIME, "15xx: Set envelope reset time"}}, {0x1a, {DIV_CMD_C64_RESET_MASK, "1Axx: Disable envelope reset for this channel (1 disables; 0 enables)"}}, {0x1b, {DIV_CMD_C64_FILTER_RESET, "1Bxy: Reset cutoff (x: on new note; y: now)"}}, {0x1c, {DIV_CMD_C64_DUTY_RESET, "1Cxy: Reset pulse width (x: on new note; y: now)"}}, {0x1e, {DIV_CMD_C64_EXTENDED, "1Exy: Change additional parameters"}}, }; const EffectHandler c64FineDutyHandler(DIV_CMD_C64_FINE_DUTY, "3xxx: Set pulse width (0 to FFF)", effectValLong<12>); const EffectHandler c64FineCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, "4xxx: Set cutoff (0 to 7FF)", effectValLong<11>); for (int i=0; i<16; i++) c64PostEffectHandlerMap.emplace(0x30+i,c64FineDutyHandler); for (int i=0; i<8; i++) c64PostEffectHandlerMap.emplace(0x40+i,c64FineCutoffHandler); EffectHandlerMap waveOnlyEffectHandlerMap={ {0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}}, }; EffectHandlerMap segaPCMPostEffectHandlerMap={ {0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set PCM frequency"}} }; // SysDefs // this chip uses YMZ ADPCM, but the emulator uses ADPCM-B because I got it wrong back then. sysDefs[DIV_SYSTEM_YMU759]=new DivSysDef( "Yamaha YMU759 (MA-2)", NULL, 0x01, 0x01, 17, true, false, 0, false, (1U<", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GENESIS_EXT]=new DivSysDef( "Sega Genesis Extended Channel 3", NULL, 0x42, 0x42, 13, true, true, 0, true, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_SMS]=new DivSysDef( "TI SN76489", NULL, 0x03, 0x03, 4, false, true, 0x150, false, 0, "a square/noise sound chip found on the Sega Master System, ColecoVision, Tandy, TI's own 99/4A and a few other places.", {"Square 1", "Square 2", "Square 3", "Noise"}, {"S1", "S2", "S3", "NO"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE}, {DIV_INS_STD, DIV_INS_STD, DIV_INS_STD, DIV_INS_STD}, {}, { {0x20, {DIV_CMD_STD_NOISE_MODE, "20xy: Set noise mode (x: preset freq/ch3 freq; y: thin pulse/noise)"}} } ); sysDefs[DIV_SYSTEM_SMS_OPLL]=new DivSysDef( "Sega Master System + FM Expansion", NULL, 0x43, 0x43, 13, true, true, 0, true, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_GB]=new DivSysDef( "Game Boy", NULL, 0x04, 0x04, 4, false, true, 0x161, false, 0, "the most popular portable game console of the era.", {"Pulse 1", "Pulse 2", "Wavetable", "Noise"}, {"S1", "S2", "WA", "NO"}, {DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_WAVE, DIV_CH_NOISE}, {DIV_INS_GB, DIV_INS_GB, DIV_INS_GB, DIV_INS_GB}, {}, { {0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}}, {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set noise length (0: long; 1: short)"}}, {0x12, {DIV_CMD_STD_NOISE_MODE, "12xx: Set duty cycle (0 to 3)"}}, {0x13, {DIV_CMD_GB_SWEEP_TIME, "13xy: Setup sweep (x: time; y: shift)"}}, {0x14, {DIV_CMD_GB_SWEEP_DIR, "14xx: Set sweep direction (0: up; 1: down)"}} } ); sysDefs[DIV_SYSTEM_PCE]=new DivSysDef( "PC Engine/TurboGrafx-16", NULL, 0x05, 0x05, 6, false, true, 0x161, false, 1U<,effectVal}}, {0x14, {DIV_CMD_NES_SWEEP, "14xy: Sweep down (x: time; y: shift)",constVal<1>,effectVal}}, {0x15, {DIV_CMD_NES_ENV_MODE, "15xx: Set envelope mode (0: envelope, 1: length, 2: looping, 3: constant)"}}, {0x16, {DIV_CMD_NES_LENGTH, "16xx: Set length counter (refer to manual for a list of values)"}}, {0x17, {DIV_CMD_NES_COUNT_MODE, "17xx: Set frame counter mode (0: 4-step, 1: 5-step)"}}, {0x18, {DIV_CMD_SAMPLE_MODE, "18xx: Select PCM/DPCM mode (0: PCM; 1: DPCM)"}}, {0x19, {DIV_CMD_NES_LINEAR_LENGTH, "19xx: Set triangle linear counter (0 to 7F; 80 and higher halt)"}}, {0x20, {DIV_CMD_SAMPLE_FREQ, "20xx: Set DPCM frequency (0 to F)"}} } ); sysDefs[DIV_SYSTEM_NES_VRC7]=new DivSysDef( "NES + Konami VRC7", NULL, 0x46, 0x46, 11, true, true, 0, true, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_NES_FDS]=new DivSysDef( "Famicom Disk System", NULL, 0, 0x86, 6, false, true, 0, true, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_C64_6581]=new DivSysDef( "Commodore 64 (6581)", NULL, 0x47, 0x47, 3, false, true, 0, false, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64}, {}, {}, c64PostEffectHandlerMap ); sysDefs[DIV_SYSTEM_C64_8580]=new DivSysDef( "Commodore 64 (8580)", NULL, 0x07, 0x07, 3, false, true, 0, false, 0, "this computer is powered by the SID chip, which had synthesizer features like a filter and ADSR.\nthis is the newer revision of the chip.", {"Channel 1", "Channel 2", "Channel 3"}, {"CH1", "CH2", "CH3"}, {DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE}, {DIV_INS_C64, DIV_INS_C64, DIV_INS_C64}, {}, {}, c64PostEffectHandlerMap ); sysDefs[DIV_SYSTEM_ARCADE]=new DivSysDef( "DefleCade", NULL, 0x08, 0x08, 13, true, false, 0, true, 0, "", {}, {}, {}, {} ); sysDefs[DIV_SYSTEM_YM2610]=new DivSysDef( "Neo Geo CD", NULL, 0x09, 0x09, 13, true, true, 0x151, false, (1U<,effectVal}}, {0x31, {DIV_CMD_SNES_ECHO_FIR, "31xx: Set echo filter coefficient 1",constVal<1>,effectVal}}, {0x32, {DIV_CMD_SNES_ECHO_FIR, "32xx: Set echo filter coefficient 2",constVal<2>,effectVal}}, {0x33, {DIV_CMD_SNES_ECHO_FIR, "33xx: Set echo filter coefficient 3",constVal<3>,effectVal}}, {0x34, {DIV_CMD_SNES_ECHO_FIR, "34xx: Set echo filter coefficient 4",constVal<4>,effectVal}}, {0x35, {DIV_CMD_SNES_ECHO_FIR, "35xx: Set echo filter coefficient 5",constVal<5>,effectVal}}, {0x36, {DIV_CMD_SNES_ECHO_FIR, "36xx: Set echo filter coefficient 6",constVal<6>,effectVal}}, {0x37, {DIV_CMD_SNES_ECHO_FIR, "37xx: Set echo filter coefficient 7",constVal<7>,effectVal}}, }, { {0x10, {DIV_CMD_WAVE, "10xx: Set waveform"}}, {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Toggle noise mode"}}, {0x12, {DIV_CMD_SNES_ECHO, "12xx: Toggle echo on this channel"}}, {0x13, {DIV_CMD_SNES_PITCH_MOD, "13xx: Toggle pitch modulation"}}, {0x14, {DIV_CMD_SNES_INVERT, "14xy: Toggle invert (x: left; y: right)"}}, {0x15, {DIV_CMD_SNES_GAIN_MODE, "15xx: Set envelope mode (0: ADSR, 1: gain/direct, 2: dec, 3: exp, 4: inc, 5: bent)"}}, {0x16, {DIV_CMD_SNES_GAIN, "16xx: Set gain (00 to 7F if direct; 00 to 1F otherwise)"}}, {0x1d, {DIV_CMD_STD_NOISE_FREQ, "1Dxx: Set noise frequency (00 to 1F)"}}, {0x20, {DIV_CMD_FM_AR, "20xx: Set attack (0 to F)"}}, {0x21, {DIV_CMD_FM_DR, "21xx: Set decay (0 to 7)"}}, {0x22, {DIV_CMD_FM_SL, "22xx: Set sustain (0 to 7)"}}, {0x23, {DIV_CMD_FM_RR, "23xx: Set release (00 to 1F)"}}, } ); sysDefs[DIV_SYSTEM_VRC6]=new DivSysDef( "Konami VRC6", NULL, 0x88, 0, 3, false, true, 0, false, 1U<); for (int i=0; i<16; i++) { lynxEffectHandlerMap.emplace(0x30+i, lynxLFSRHandler); } sysDefs[DIV_SYSTEM_LYNX]=new DivSysDef( "Atari Lynx", NULL, 0xa8, 0, 4, false, true, 0x172, false, 1U<); for (int i=0; i<16; i++) { qSoundEffectHandlerMap.emplace(0x30+i, qSoundEchoDelayHandler); } sysDefs[DIV_SYSTEM_QSOUND]=new DivSysDef( "Capcom QSound", NULL, 0xe0, 0, 19, false, true, 0x161, false, (1U<}}, {0x14, {DIV_CMD_ES5506_FILTER_K1, "14xx: Set filter coefficient K1 low byte (00 to FF)",effectValShift<0>,constVal<0x00ff>}}, {0x15, {DIV_CMD_ES5506_FILTER_K1, "15xx: Set filter coefficient K1 high byte (00 to FF)",effectValShift<8>,constVal<0xff00>}}, {0x16, {DIV_CMD_ES5506_FILTER_K2, "16xx: Set filter coefficient K2 low byte (00 to FF)",effectValShift<0>,constVal<0x00ff>}}, {0x17, {DIV_CMD_ES5506_FILTER_K2, "17xx: Set filter coefficient K2 high byte (00 to FF)",effectValShift<8>,constVal<0xff00>}}, {0x18, {DIV_CMD_ES5506_FILTER_K1_SLIDE, "18xx: Set filter coefficient K1 slide up (00 to FF)",effectVal,constVal<0>}}, {0x19, {DIV_CMD_ES5506_FILTER_K1_SLIDE, "19xx: Set filter coefficient K1 slide down (00 to FF)",effectVal,constVal<1>}}, {0x1a, {DIV_CMD_ES5506_FILTER_K2_SLIDE, "1Axx: Set filter coefficient K2 slide up (00 to FF)",effectVal,constVal<0>}}, {0x1b, {DIV_CMD_ES5506_FILTER_K2_SLIDE, "1Bxx: Set filter coefficient K2 slide down (00 to FF)",effectVal,constVal<1>}}, {0x22, {DIV_CMD_ES5506_ENVELOPE_LVRAMP, "22xx: Set envelope left volume ramp (signed) (00 to FF)",effectVal}}, {0x23, {DIV_CMD_ES5506_ENVELOPE_RVRAMP, "23xx: Set envelope right volume ramp (signed) (00 to FF)",effectVal}}, {0x24, {DIV_CMD_ES5506_ENVELOPE_K1RAMP, "24xx: Set envelope filter coefficient k1 ramp (signed) (00 to FF)",effectVal,constVal<0>}}, {0x25, {DIV_CMD_ES5506_ENVELOPE_K1RAMP, "25xx: Set envelope filter coefficient k1 ramp (signed, slower) (00 to FF)",effectVal,constVal<1>}}, {0x26, {DIV_CMD_ES5506_ENVELOPE_K2RAMP, "26xx: Set envelope filter coefficient k2 ramp (signed) (00 to FF)",effectVal,constVal<0>}}, {0x27, {DIV_CMD_ES5506_ENVELOPE_K2RAMP, "27xx: Set envelope filter coefficient k2 ramp (signed, slower) (00 to FF)",effectVal,constVal<1>}}, {0xdf, {DIV_CMD_SAMPLE_DIR, "DFxx: Set sample playback direction (0: normal; 1: reverse)"}} }; EffectHandlerMap es5506PostEffectHandlerMap={ {0x12, {DIV_CMD_ES5506_PAUSE, "120x: Set pause (bit 0)",effectValAnd<1>}} }; const EffectHandler es5506ECountHandler(DIV_CMD_ES5506_ENVELOPE_COUNT, "2xxx: Set envelope count (000 to 1FF)", effectValLong<9>); const EffectHandler es5506K1Handler(DIV_CMD_ES5506_FILTER_K1, "3xxx: Set filter coefficient K1 (000 to FFF)", effectValLongShift<12,4>,constVal<0xfff0>); const EffectHandler es5506K2Handler(DIV_CMD_ES5506_FILTER_K2, "4xxx: Set filter coefficient K2 (000 to FFF)", effectValLongShift<12,4>,constVal<0xfff0>); for (int i=0; i<2; i++) es5506PreEffectHandlerMap.emplace(0x20+i,es5506ECountHandler); for (int i=0; i<16; i++) es5506PreEffectHandlerMap.emplace(0x30+i, es5506K1Handler); for (int i=0; i<16; i++) es5506PreEffectHandlerMap.emplace(0x40+i, es5506K2Handler); // TODO: custom sample format sysDefs[DIV_SYSTEM_ES5506]=new DivSysDef( "Ensoniq ES5506", NULL, 0xb1, 0, 32, false, true, 0/*0x171*/, false, (1U<, effectVal}}, {0x16, {DIV_CMD_SU_SWEEP_PERIOD_HIGH, "16xx: Set frequency sweep period high byte", constVal<0>, effectVal}}, {0x17, {DIV_CMD_SU_SWEEP_PERIOD_LOW, "17xx: Set volume sweep period low byte", constVal<1>, effectVal}}, {0x18, {DIV_CMD_SU_SWEEP_PERIOD_HIGH, "18xx: Set volume sweep period high byte", constVal<1>, effectVal}}, {0x19, {DIV_CMD_SU_SWEEP_PERIOD_LOW, "19xx: Set cutoff sweep period low byte", constVal<2>, effectVal}}, {0x1a, {DIV_CMD_SU_SWEEP_PERIOD_HIGH, "1Axx: Set cutoff sweep period high byte", constVal<2>, effectVal}}, {0x1b, {DIV_CMD_SU_SWEEP_BOUND, "1Bxx: Set frequency sweep boundary", constVal<0>, effectVal}}, {0x1c, {DIV_CMD_SU_SWEEP_BOUND, "1Cxx: Set volume sweep boundary", constVal<1>, effectVal}}, {0x1d, {DIV_CMD_SU_SWEEP_BOUND, "1Dxx: Set cutoff sweep boundary", constVal<2>, effectVal}}, {0x1e, {DIV_CMD_SU_SYNC_PERIOD_LOW, "1Exx: Set phase reset period low byte"}}, {0x1f, {DIV_CMD_SU_SYNC_PERIOD_HIGH, "1Fxx: Set phase reset period high byte"}}, {0x20, {DIV_CMD_SU_SWEEP_ENABLE, "20xx: Toggle frequency sweep (bit 0-6: speed; bit 7: direction is up)", constVal<0>, effectVal}}, {0x21, {DIV_CMD_SU_SWEEP_ENABLE, "21xx: Toggle volume sweep (bit 0-4: speed; bit 5: direciton is up; bit 6: loop; bit 7: alternate)", constVal<1>, effectVal}}, {0x22, {DIV_CMD_SU_SWEEP_ENABLE, "22xx: Toggle cutoff sweep (bit 0-6: speed; bit 7: direction is up)", constVal<2>, effectVal}}, }; const EffectHandler suCutoffHandler(DIV_CMD_C64_FINE_CUTOFF, "4xxx: Set cutoff (0 to FFF)", effectValLong<12>); for (int i=0; i<16; i++) { suEffectHandlerMap.emplace(0x40+i, suCutoffHandler); } sysDefs[DIV_SYSTEM_SOUND_UNIT]=new DivSysDef( "tildearrow Sound Unit", NULL, 0xb5, 0, 8, false, true, 0, false, 1U<id!=0) { sysFileMapFur[sysDefs[i]->id]=(DivSystem)i; } if (sysDefs[i]->id_DMF!=0) { sysFileMapDMF[sysDefs[i]->id_DMF]=(DivSystem)i; } } systemsRegistered=true; }