mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-26 14:33:01 +00:00
Merge branch 'master' into es5506_alt
This commit is contained in:
commit
a793bed56d
65 changed files with 4981 additions and 395 deletions
|
@ -424,6 +424,9 @@ src/engine/platform/sound/ymfm/ymfm_ssg.cpp
|
|||
|
||||
src/engine/platform/sound/lynx/Mikey.cpp
|
||||
|
||||
src/engine/platform/sound/pokey/mzpokeysnd.c
|
||||
src/engine/platform/sound/pokey/AltASAP.cpp
|
||||
|
||||
src/engine/platform/sound/qsound.c
|
||||
|
||||
src/engine/platform/sound/swan.cpp
|
||||
|
@ -508,6 +511,7 @@ src/engine/platform/pcspkr.cpp
|
|||
src/engine/platform/segapcm.cpp
|
||||
src/engine/platform/qsound.cpp
|
||||
src/engine/platform/x1_010.cpp
|
||||
src/engine/platform/pokey.cpp
|
||||
src/engine/platform/lynx.cpp
|
||||
src/engine/platform/su.cpp
|
||||
src/engine/platform/swan.cpp
|
||||
|
|
8
TODO.md
8
TODO.md
|
@ -1,9 +1,3 @@
|
|||
# to-do for 0.6pre2
|
||||
|
||||
- POKEY
|
||||
- Pokémon Mini
|
||||
- register layout
|
||||
- confirm emulation
|
||||
- (maybe) YM2612 CSM (no DualPCM)
|
||||
- port op macro code to all other OPN chips
|
||||
- bug fixes
|
||||
- bug fixes
|
||||
|
|
BIN
demos/amiga/furnace0.6crk.fur
Normal file
BIN
demos/amiga/furnace0.6crk.fur
Normal file
Binary file not shown.
BIN
demos/ay8910/remark_music.fur
Normal file
BIN
demos/ay8910/remark_music.fur
Normal file
Binary file not shown.
BIN
demos/misc/rf5wapianoroll.fur
Normal file
BIN
demos/misc/rf5wapianoroll.fur
Normal file
Binary file not shown.
BIN
demos/sms/thunderblade-type-ii.fur
Normal file
BIN
demos/sms/thunderblade-type-ii.fur
Normal file
Binary file not shown.
BIN
demos/snes/Breezy.fur
Normal file
BIN
demos/snes/Breezy.fur
Normal file
Binary file not shown.
BIN
demos/specs2/object.fur
Normal file
BIN
demos/specs2/object.fur
Normal file
Binary file not shown.
34
papers/doc/7-systems/pokey.md
Normal file
34
papers/doc/7-systems/pokey.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# POKEY
|
||||
|
||||
a sound and input chip developed by Atari for their 8-bit computers (Atari 400, 800, XL/XE and so on). 4 channels of signature Atari sounds.
|
||||
|
||||
# effects
|
||||
|
||||
- 10xx: set waveform.
|
||||
- 0: harsh noise (poly5+17)
|
||||
- 1: square buzz (poly5)
|
||||
- 2: weird noise (poly4+5)
|
||||
- 3: square buzz (poly5)
|
||||
- 4: soft noise (poly17)
|
||||
- 5: square
|
||||
- 6: bass (poly4)
|
||||
- 7: buzz (poly4)
|
||||
- 11xx: set AUDCTL. `xx` is a bitmask.
|
||||
- bit 7: 9-bit poly mode. shortens noise.
|
||||
- bit 6: high channel 1 clock (~1.79MHz on NTSC).
|
||||
- overrides 15KHz mode.
|
||||
- bit 5: high channel 3 clock (~1.79MHz on NTSC).
|
||||
- overrides 15KHz mode.
|
||||
- bit 4: join channels 1 and 2 for a wide period range.
|
||||
- use with conjunction with bit 6.
|
||||
- channel 2 becomes inaccessible when this is on.
|
||||
- bit 3: join channels 3 and 4 for a wide period range.
|
||||
- use with conjunction with bit 5.
|
||||
- channel 4 becomes inaccessible when this is on.
|
||||
- bit 2: high-pass filter (channels 1 and 3).
|
||||
- filtered output on channel 1 (I suggest you to set channel 3 volume to 0).
|
||||
- use for PWM effects (not automatic!).
|
||||
- bit 1: high-pass filter (channels 2 and 4).
|
||||
- filtered output on channel 2 (I suggest you to set channel 4 volume to 0).
|
||||
- use for PWM effects (not automatic!).
|
||||
- bit 0: 15KHz mode.
|
|
@ -215,8 +215,8 @@ size | description
|
|||
| - 0x8a: FDS - 1 channel
|
||||
| - 0x8b: MMC5 - 3 channels
|
||||
| - 0x8c: Namco 163 - 8 channels
|
||||
| - 0x8d: OPN (YM2203) - 6 channels
|
||||
| - 0x8e: PC-98 (YM2608) - 16 channels
|
||||
| - 0x8d: YM2203 - 6 channels
|
||||
| - 0x8e: YM2608 - 16 channels
|
||||
| - 0x8f: OPL (YM3526) - 9 channels
|
||||
| - 0x90: OPL2 (YM3812) - 9 channels
|
||||
| - 0x91: OPL3 (YMF262) - 18 channels
|
||||
|
@ -256,8 +256,8 @@ size | description
|
|||
| - 0xb3: Yamaha Y8950 drums - 12 channels
|
||||
| - 0xb4: Konami SCC+ - 5 channels
|
||||
| - 0xb5: tildearrow Sound Unit - 8 channels
|
||||
| - 0xb6: OPN extended - 9 channels
|
||||
| - 0xb7: PC-98 extended - 19 channels
|
||||
| - 0xb6: YM2203 extended - 9 channels
|
||||
| - 0xb7: YM2608 extended - 19 channels
|
||||
| - 0xb8: YMZ280B - 8 channels
|
||||
| - 0xb9: Namco WSG - 3 channels
|
||||
| - 0xba: Namco 15xx - 8 channels
|
||||
|
@ -269,8 +269,8 @@ size | description
|
|||
| - 0xc0: PCM DAC - 1 channel
|
||||
| - 0xc1: YM2612 CSM - 10 channels
|
||||
| - 0xc2: Neo Geo CSM (YM2610) - 18 channels
|
||||
| - 0xc3: OPN CSM - 10 channels
|
||||
| - 0xc4: PC-98 CSM - 20 channels
|
||||
| - 0xc3: YM2203 CSM - 10 channels
|
||||
| - 0xc4: YM2608 CSM - 20 channels
|
||||
| - 0xc5: YM2610B CSM - 20 channels
|
||||
| - 0xc6: K007232 - 2 channels
|
||||
| - 0xc7: GA20 - 4 channels
|
||||
|
@ -1441,7 +1441,7 @@ chips which aren't on this list don't have any flags.
|
|||
- bit 4-6: channels (int)
|
||||
- bit 7: multiplex (bool)
|
||||
|
||||
## 0x8d: OPN (YM2203) and 0xb6: OPN extended
|
||||
## 0x8d: YM2203 and 0xb6: YM2203 extended
|
||||
|
||||
- bit 0-4: clockSel (int)
|
||||
- 0: NTSC
|
||||
|
@ -1455,7 +1455,7 @@ chips which aren't on this list don't have any flags.
|
|||
- 1: /3
|
||||
- 2: /2
|
||||
|
||||
## 0x8e: PC-98 (YM2608) and 0xb7: PC-98 extended
|
||||
## 0x8e: YM2608 and 0xb7: YM2608 extended
|
||||
|
||||
- bit 0-4: clockSel (int)
|
||||
- 0: 8MHz
|
||||
|
|
|
@ -54,6 +54,10 @@ String DivConfig::toBase64() {
|
|||
return taEncodeBase64(data);
|
||||
}
|
||||
|
||||
const std::map<String,String>& DivConfig::configMap() {
|
||||
return conf;
|
||||
}
|
||||
|
||||
void DivConfig::parseLine(const char* line) {
|
||||
String key="";
|
||||
String value="";
|
||||
|
@ -171,6 +175,15 @@ String DivConfig::getString(String key, String fallback) const {
|
|||
return fallback;
|
||||
}
|
||||
|
||||
bool DivConfig::has(String key) {
|
||||
try {
|
||||
String test=conf.at(key);
|
||||
} catch (std::out_of_range& e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivConfig::set(String key, bool value) {
|
||||
if (value) {
|
||||
conf[key]="true";
|
||||
|
|
|
@ -35,6 +35,9 @@ class DivConfig {
|
|||
String toBase64();
|
||||
bool save(const char* path);
|
||||
|
||||
// get the map
|
||||
const std::map<String,String>& configMap();
|
||||
|
||||
// get a config value
|
||||
bool getBool(String key, bool fallback) const;
|
||||
int getInt(String key, int fallback) const;
|
||||
|
@ -42,6 +45,9 @@ class DivConfig {
|
|||
double getDouble(String key, double fallback) const;
|
||||
String getString(String key, String fallback) const;
|
||||
|
||||
// check for existence
|
||||
bool has(String key);
|
||||
|
||||
// set a config value
|
||||
void set(String key, bool value);
|
||||
void set(String key, int value);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#ifdef _WIN32
|
||||
#include "winStuff.h"
|
||||
#define CONFIG_FILE "\\furnace.cfg"
|
||||
#define LOG_FILE "\\furnace.log"
|
||||
#else
|
||||
#ifdef __HAIKU__
|
||||
#include <support/SupportDefs.h>
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "platform/su.h"
|
||||
#include "platform/swan.h"
|
||||
#include "platform/lynx.h"
|
||||
#include "platform/pokey.h"
|
||||
#include "platform/zxbeeper.h"
|
||||
#include "platform/bubsyswsg.h"
|
||||
#include "platform/n163.h"
|
||||
|
@ -201,12 +202,18 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
|
||||
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(false);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_CSM:
|
||||
dispatch=new DivPlatformGenesisExt;
|
||||
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
|
||||
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(false);
|
||||
((DivPlatformGenesisExt*)dispatch)->setCSMChannel(6);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
dispatch=new DivPlatformGenesis;
|
||||
((DivPlatformGenesis*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
|
||||
((DivPlatformGenesis*)dispatch)->setSoftPCM(true);
|
||||
break;
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
dispatch=new DivPlatformGenesisExt;
|
||||
((DivPlatformGenesisExt*)dispatch)->setYMFM(eng->getConfInt("ym2612Core",0));
|
||||
((DivPlatformGenesisExt*)dispatch)->setSoftPCM(true);
|
||||
|
@ -269,16 +276,16 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_TIA:
|
||||
dispatch=new DivPlatformTIA;
|
||||
break;
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
dispatch=new DivPlatformYM2203;
|
||||
break;
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
dispatch=new DivPlatformYM2203Ext;
|
||||
break;
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_YM2608:
|
||||
dispatch=new DivPlatformYM2608;
|
||||
break;
|
||||
case DIV_SYSTEM_PC98_EXT:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
dispatch=new DivPlatformYM2608Ext;
|
||||
break;
|
||||
case DIV_SYSTEM_OPLL:
|
||||
|
@ -339,6 +346,10 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_LYNX:
|
||||
dispatch=new DivPlatformLynx;
|
||||
break;
|
||||
case DIV_SYSTEM_POKEY:
|
||||
dispatch=new DivPlatformPOKEY;
|
||||
((DivPlatformPOKEY*)dispatch)->setAltASAP(eng->getConfInt("pokeyCore",1)==1);
|
||||
break;
|
||||
case DIV_SYSTEM_QSOUND:
|
||||
dispatch=new DivPlatformQSound;
|
||||
break;
|
||||
|
|
|
@ -3295,7 +3295,7 @@ DivSample* DivEngine::sampleFromFile(const char* path) {
|
|||
#endif
|
||||
}
|
||||
|
||||
DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign) {
|
||||
DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles) {
|
||||
if (song.sample.size()>=256) {
|
||||
lastError="too many samples!";
|
||||
return NULL;
|
||||
|
@ -3461,6 +3461,14 @@ DivSample* DivEngine::sampleFromFileRaw(const char* path, DivSampleDepth depth,
|
|||
}
|
||||
delete[] buf;
|
||||
|
||||
// swap nibbles if needed
|
||||
if (swapNibbles) {
|
||||
unsigned char* b=(unsigned char*)sample->getCurBuf();
|
||||
for (unsigned int i=0; i<sample->getCurBufLen(); i++) {
|
||||
b[i]=(b[i]<<4)|(b[i]>>4);
|
||||
}
|
||||
}
|
||||
|
||||
BUSY_END;
|
||||
return sample;
|
||||
}
|
||||
|
@ -4215,7 +4223,7 @@ bool DivEngine::deinitAudioBackend(bool dueToSwitchMaster) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DivEngine::init() {
|
||||
void DivEngine::preInit() {
|
||||
// register systems
|
||||
if (!systemsRegistered) registerSystems();
|
||||
|
||||
|
@ -4223,8 +4231,13 @@ bool DivEngine::init() {
|
|||
initConfDir();
|
||||
logD("config path: %s",configPath.c_str());
|
||||
|
||||
String logPath=configPath+DIR_SEPARATOR_STR+"furnace.log";
|
||||
startLogFile(logPath.c_str());
|
||||
|
||||
loadConf();
|
||||
}
|
||||
|
||||
bool DivEngine::init() {
|
||||
loadSampleROMs();
|
||||
|
||||
// set default system preset
|
||||
|
|
|
@ -796,7 +796,7 @@ class DivEngine {
|
|||
DivSample* sampleFromFile(const char* path);
|
||||
|
||||
// get raw sample
|
||||
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign);
|
||||
DivSample* sampleFromFileRaw(const char* path, DivSampleDepth depth, int channels, bool bigEndian, bool unsign, bool swapNibbles);
|
||||
|
||||
// delete sample
|
||||
void delSample(int index);
|
||||
|
@ -1012,6 +1012,9 @@ class DivEngine {
|
|||
// quit dispatch
|
||||
void quitDispatch();
|
||||
|
||||
// pre-initialize the engine.
|
||||
void preInit();
|
||||
|
||||
// initialize the engine.
|
||||
bool init();
|
||||
|
||||
|
|
|
@ -1207,8 +1207,8 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
|
|||
break;
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
switch (oldFlags&0x7fffffff) {
|
||||
case 0:
|
||||
newFlags.set("clockSel",0);
|
||||
|
@ -1295,8 +1295,8 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
|
|||
newFlags.set("channels",(int)((oldFlags>>4)&7));
|
||||
if (oldFlags&128) newFlags.set("multiplex",true);
|
||||
break;
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
switch (oldFlags&31) {
|
||||
case 0:
|
||||
newFlags.set("clockSel",0);
|
||||
|
@ -1329,8 +1329,8 @@ void DivEngine::convertOldFlags(unsigned int oldFlags, DivConfig& newFlags, DivS
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT:
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
switch (oldFlags&31) {
|
||||
case 0:
|
||||
newFlags.set("clockSel",0);
|
||||
|
@ -2484,14 +2484,14 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
case DIV_SYSTEM_YM2610_FULL_EXT:
|
||||
case DIV_SYSTEM_YM2610B:
|
||||
case DIV_SYSTEM_YM2610B_EXT:
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
opnCount++;
|
||||
break;
|
||||
default:
|
||||
|
@ -2514,12 +2514,12 @@ bool DivEngine::loadFur(unsigned char* file, size_t len) {
|
|||
if (ds.version<125) {
|
||||
for (int i=0; i<ds.systemLen; i++) {
|
||||
if (ds.system[i]==DIV_SYSTEM_YM2612_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2612_FRAC_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2612_DUALPCM_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2610_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2610_FULL_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2610B_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_OPN_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_PC98_EXT) {
|
||||
ds.system[i]==DIV_SYSTEM_YM2203_EXT ||
|
||||
ds.system[i]==DIV_SYSTEM_YM2608_EXT) {
|
||||
ds.systemFlags[i].set("noExtMacros",true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
float* DivFilterTables::cubicTable=NULL;
|
||||
float* DivFilterTables::sincTable=NULL;
|
||||
float* DivFilterTables::sincTable8=NULL;
|
||||
float* DivFilterTables::sincIntegralTable=NULL;
|
||||
|
||||
// portions from Schism Tracker (scripts/lutgen.c)
|
||||
|
@ -44,7 +45,7 @@ float* DivFilterTables::getCubicTable() {
|
|||
return cubicTable;
|
||||
}
|
||||
|
||||
float* DivFilterTables:: getSincTable() {
|
||||
float* DivFilterTables::getSincTable() {
|
||||
if (sincTable==NULL) {
|
||||
logD("initializing sinc table.");
|
||||
sincTable=new float[65536];
|
||||
|
@ -64,6 +65,26 @@ float* DivFilterTables:: getSincTable() {
|
|||
return sincTable;
|
||||
}
|
||||
|
||||
float* DivFilterTables::getSincTable8() {
|
||||
if (sincTable8==NULL) {
|
||||
logD("initializing sinc table (8).");
|
||||
sincTable8=new float[32768];
|
||||
|
||||
sincTable8[0]=1.0f;
|
||||
for (int i=1; i<32768; i++) {
|
||||
int mapped=((i&8191)<<2)|(i>>13);
|
||||
double x=(double)i*M_PI/8192.0;
|
||||
sincTable8[mapped]=sin(x)/x;
|
||||
}
|
||||
|
||||
for (int i=0; i<32768; i++) {
|
||||
int mapped=((i&8191)<<2)|(i>>13);
|
||||
sincTable8[mapped]*=pow(cos(M_PI*(double)i/65536.0),2.0);
|
||||
}
|
||||
}
|
||||
return sincTable8;
|
||||
}
|
||||
|
||||
float* DivFilterTables::getSincIntegralTable() {
|
||||
if (sincIntegralTable==NULL) {
|
||||
logD("initializing sinc integral table.");
|
||||
|
|
|
@ -21,6 +21,7 @@ class DivFilterTables {
|
|||
public:
|
||||
static float* cubicTable;
|
||||
static float* sincTable;
|
||||
static float* sincTable8;
|
||||
static float* sincIntegralTable;
|
||||
|
||||
/**
|
||||
|
@ -35,6 +36,12 @@ class DivFilterTables {
|
|||
*/
|
||||
static float* getSincTable();
|
||||
|
||||
/**
|
||||
* get a 8192x4 one-side sine-windowed sinc table.
|
||||
* @return the table.
|
||||
*/
|
||||
static float* getSincTable8();
|
||||
|
||||
/**
|
||||
* get a 8192x8 one-side sine-windowed sinc integral table.
|
||||
* @return the table.
|
||||
|
|
|
@ -353,9 +353,9 @@ void DivPlatformArcade::tick(bool sysTick) {
|
|||
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
if (!parent->song.oldArpStrategy) {
|
||||
if (chan[i].fixedArp) {
|
||||
chan[i].freq=(chan[i].baseNoteOverride<<7)+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
} else {
|
||||
chan[i].freq+=chan[i].arpOff<<7;
|
||||
chan[i].freq+=chan[i].arpOff<<6;
|
||||
}
|
||||
}
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
|
|
|
@ -830,6 +830,9 @@ void DivPlatformAY8910::setFlags(const DivConfig& flags) {
|
|||
case 14:
|
||||
chipClock=1536000;
|
||||
break;
|
||||
case 15:
|
||||
chipClock=38400*13*4; // 31948800/16
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
break;
|
||||
|
|
|
@ -83,6 +83,8 @@
|
|||
return 2; \
|
||||
}
|
||||
|
||||
#define IS_EXTCH_MUTED (isOpMuted[0] && isOpMuted[1] && isOpMuted[2] && isOpMuted[3])
|
||||
|
||||
class DivPlatformOPN: public DivPlatformFMBase {
|
||||
protected:
|
||||
const unsigned short ADDR_MULT_DT=0x30;
|
||||
|
@ -147,17 +149,21 @@ class DivPlatformOPN: public DivPlatformFMBase {
|
|||
double fmFreqBase;
|
||||
unsigned int fmDivBase;
|
||||
unsigned int ayDiv;
|
||||
unsigned char csmChan;
|
||||
unsigned char lfoValue;
|
||||
bool extSys;
|
||||
|
||||
DivConfig ayFlags;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
DivPlatformOPN(double f=9440540.0, unsigned int d=72, unsigned int a=32, bool isExtSys=false):
|
||||
DivPlatformOPN(double f=9440540.0, unsigned int d=72, unsigned int a=32, bool isExtSys=false, unsigned char cc=255):
|
||||
DivPlatformFMBase(),
|
||||
fmFreqBase(f),
|
||||
fmDivBase(d),
|
||||
ayDiv(a),
|
||||
csmChan(cc),
|
||||
lfoValue(0),
|
||||
extSys(isExtSys) {}
|
||||
|
||||
};
|
||||
|
|
|
@ -479,7 +479,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<7; i++) {
|
||||
for (int i=0; i<csmChan; i++) {
|
||||
if (i==2 && extMode) continue;
|
||||
if (chan[i].freqChanged) {
|
||||
if (parent->song.linearPitch==2) {
|
||||
|
@ -554,7 +554,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_FM);
|
||||
if (c.chan==7 && extMode && softPCM) { // CSM
|
||||
if (c.chan==csmChan && extMode) { // CSM
|
||||
chan[c.chan].macroInit(ins);
|
||||
chan[c.chan].insChanged=false;
|
||||
|
||||
|
@ -691,7 +691,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
if (c.chan>=5 && c.chan<7) {
|
||||
if (c.chan>=5 && c.chan<csmChan) {
|
||||
chan[c.chan].dacSample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002,0);
|
||||
if (parent->song.brokenDACMode) {
|
||||
|
@ -786,7 +786,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (c.chan==7) {
|
||||
if (c.chan==csmChan) {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
|
@ -855,7 +855,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
if (c.chan==7) {
|
||||
if (c.chan==csmChan) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
} else if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
|
||||
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
|
||||
|
@ -1222,7 +1222,7 @@ void DivPlatformGenesis::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
int DivPlatformGenesis::getPortaFloor(int ch) {
|
||||
return (ch>5)?12:0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DivPlatformGenesis::setYMFM(bool use) {
|
||||
|
|
|
@ -86,8 +86,6 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
ymfm::ym2612* fm_ymfm;
|
||||
ymfm::ym2612::output_data out_ymfm;
|
||||
DivYM2612Interface iface;
|
||||
|
||||
unsigned char lfoValue;
|
||||
|
||||
int softPCMTimer;
|
||||
|
||||
|
@ -133,7 +131,7 @@ class DivPlatformGenesis: public DivPlatformOPN {
|
|||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
DivPlatformGenesis():
|
||||
DivPlatformOPN(9440540.0, 72, 32) {}
|
||||
DivPlatformOPN(9440540.0, 72, 32, false, 7) {}
|
||||
~DivPlatformGenesis();
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
|
||||
#define IS_EXTCH_MUTED (isOpMuted[0] && isOpMuted[1] && isOpMuted[2] && isOpMuted[3])
|
||||
|
||||
int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
|
@ -220,14 +219,14 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
|||
rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
case DIV_CMD_FM_TL: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
|
@ -454,7 +453,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (writeSomething) {
|
||||
if (chan[7].active) { // CSM
|
||||
if (chan[csmChan].active) { // CSM
|
||||
writeMask^=0xf0;
|
||||
}
|
||||
/*printf(
|
||||
|
@ -588,18 +587,18 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (extMode && softPCM) {
|
||||
if (chan[7].freqChanged) {
|
||||
chan[7].freq=parent->calcFreq(chan[7].baseFreq,chan[7].pitch,chan[7].fixedArp?chan[7].baseNoteOverride:chan[7].arpOff,chan[7].fixedArp,true,0,chan[7].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[7].freq<1) chan[7].freq=1;
|
||||
if (chan[7].freq>1024) chan[7].freq=1024;
|
||||
int wf=0x400-chan[7].freq;
|
||||
if (extMode) {
|
||||
if (chan[csmChan].freqChanged) {
|
||||
chan[csmChan].freq=parent->calcFreq(chan[csmChan].baseFreq,chan[csmChan].pitch,chan[csmChan].fixedArp?chan[csmChan].baseNoteOverride:chan[csmChan].arpOff,chan[csmChan].fixedArp,true,0,chan[csmChan].pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan[csmChan].freq<1) chan[csmChan].freq=1;
|
||||
if (chan[csmChan].freq>1024) chan[csmChan].freq=1024;
|
||||
int wf=0x400-chan[csmChan].freq;
|
||||
immWrite(0x24,wf>>2);
|
||||
immWrite(0x25,wf&3);
|
||||
chan[7].freqChanged=false;
|
||||
chan[csmChan].freqChanged=false;
|
||||
}
|
||||
|
||||
if (chan[7].keyOff || chan[7].keyOn) {
|
||||
if (chan[csmChan].keyOff || chan[csmChan].keyOn) {
|
||||
writeNoteOn=true;
|
||||
for (int i=0; i<4; i++) {
|
||||
writeMask|=opChan[i].active<<(4+i);
|
||||
|
@ -608,7 +607,7 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (writeNoteOn) {
|
||||
if (chan[7].active) { // CSM
|
||||
if (chan[csmChan].active) { // CSM
|
||||
writeMask^=0xf0;
|
||||
}
|
||||
/*printf(
|
||||
|
@ -621,14 +620,14 @@ void DivPlatformGenesisExt::tick(bool sysTick) {
|
|||
immWrite(0x28,writeMask);
|
||||
}
|
||||
|
||||
if (extMode && softPCM) {
|
||||
if (chan[7].keyOn) {
|
||||
if (extMode) {
|
||||
if (chan[csmChan].keyOn) {
|
||||
immWrite(0x27,0x81);
|
||||
chan[7].keyOn=false;
|
||||
chan[csmChan].keyOn=false;
|
||||
}
|
||||
if (chan[7].keyOff) {
|
||||
if (chan[csmChan].keyOff) {
|
||||
immWrite(0x27,0x40);
|
||||
chan[7].keyOff=false;
|
||||
chan[csmChan].keyOff=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -686,10 +685,10 @@ void DivPlatformGenesisExt::forceIns() {
|
|||
opChan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (extMode && softPCM && chan[7].active) { // CSM
|
||||
chan[7].insChanged=true;
|
||||
chan[7].freqChanged=true;
|
||||
chan[7].keyOn=true;
|
||||
if (extMode && chan[csmChan].active) { // CSM
|
||||
chan[csmChan].insChanged=true;
|
||||
chan[csmChan].freqChanged=true;
|
||||
chan[csmChan].keyOn=true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -701,7 +700,7 @@ void* DivPlatformGenesisExt::getChanState(int ch) {
|
|||
|
||||
DivMacroInt* DivPlatformGenesisExt::getChanMacroInt(int ch) {
|
||||
if (ch>=6) return &chan[ch-3].std;
|
||||
if (ch>=2) return NULL; // currently not implemented
|
||||
if (ch>=2) return &opChan[ch-2].std;
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
@ -747,6 +746,10 @@ int DivPlatformGenesisExt::getPortaFloor(int ch) {
|
|||
return (ch>8)?12:0;
|
||||
}
|
||||
|
||||
void DivPlatformGenesisExt::setCSMChannel(unsigned char ch) {
|
||||
csmChan=ch;
|
||||
}
|
||||
|
||||
int DivPlatformGenesisExt::init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags) {
|
||||
DivPlatformGenesis::init(parent,channels,sugRate,flags);
|
||||
for (int i=0; i<4; i++) {
|
||||
|
|
|
@ -42,6 +42,7 @@ class DivPlatformGenesisExt: public DivPlatformGenesis {
|
|||
bool keyOffAffectsPorta(int ch);
|
||||
void notifyInsChange(int ins);
|
||||
int getPortaFloor(int ch);
|
||||
void setCSMChannel(unsigned char ch);
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
~DivPlatformGenesisExt();
|
||||
|
|
|
@ -529,10 +529,10 @@ void DivPlatformK007232::renderSamples(int sysID) {
|
|||
}
|
||||
|
||||
const int length=s->getLoopEndPosition(DIV_SAMPLE_DEPTH_8BIT);
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-1,length);
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-2,length);
|
||||
if (actualLength>0) {
|
||||
if (actualLength>131072-1) {
|
||||
actualLength=131072-1;
|
||||
if (actualLength>131072-2) {
|
||||
actualLength=131072-2;
|
||||
}
|
||||
if ((memPos&0xfe0000)!=((memPos+actualLength+1)&0xfe0000)) {
|
||||
memPos=(memPos+0x1ffff)&0xfe0000;
|
||||
|
|
|
@ -356,7 +356,7 @@ void DivPlatformMSM6295::renderSamples(int sysID) {
|
|||
// sample data
|
||||
size_t memPos=128*8;
|
||||
int sampleCount=parent->song.sampleLen;
|
||||
if (sampleCount>128) sampleCount=128;
|
||||
if (sampleCount>127) sampleCount=127;
|
||||
for (int i=0; i<sampleCount; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
if (!s->renderOn[0][sysID]) {
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include "pcmdac.h"
|
||||
#include "../engine.h"
|
||||
#include "../filter.h"
|
||||
#include <math.h>
|
||||
|
||||
// to ease the driver, freqency register is a 8.16 counter relative to output sample rate
|
||||
|
@ -103,7 +104,50 @@ void DivPlatformPCMDAC::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
}
|
||||
}
|
||||
if (chan[0].audPos>=0 && chan[0].audPos<(int)s->samples) {
|
||||
output=s->data16[chan[0].audPos];
|
||||
int s_4=((chan[0].audPos-4)>=0)?s->data16[chan[0].audPos-4]:0;
|
||||
int s_3=((chan[0].audPos-3)>=0)?s->data16[chan[0].audPos-3]:0;
|
||||
int s_2=((chan[0].audPos-2)>=0)?s->data16[chan[0].audPos-2]:0;
|
||||
int s_1=((chan[0].audPos-1)>=0)?s->data16[chan[0].audPos-1]:0;
|
||||
int s0=s->data16[chan[0].audPos];
|
||||
int s1=((chan[0].audPos+1)<(int)s->samples)?s->data16[chan[0].audPos+1]:0;
|
||||
int s2=((chan[0].audPos+2)<(int)s->samples)?s->data16[chan[0].audPos+2]:0;
|
||||
int s3=((chan[0].audPos+3)<(int)s->samples)?s->data16[chan[0].audPos+3]:0;
|
||||
switch (interp) {
|
||||
case 1: // linear
|
||||
output=s0+((s1-s0)*(chan[0].audSub&0xffff)>>16);
|
||||
break;
|
||||
case 2: { // cubic
|
||||
float* cubicTable=DivFilterTables::getCubicTable();
|
||||
float* t=&cubicTable[((chan[0].audSub&0xffff)>>6)<<2];
|
||||
float result=(float)s_1*t[0]+(float)s0*t[1]+(float)s1*t[2]+(float)s2*t[3];
|
||||
if (result<-32768) result=-32768;
|
||||
if (result>32767) result=32767;
|
||||
output=result;
|
||||
break;
|
||||
}
|
||||
case 3: { // sinc
|
||||
float* sincTable=DivFilterTables::getSincTable8();
|
||||
float* t1=&sincTable[(8191-((chan[0].audSub&0xffff)>>3))<<2];
|
||||
float* t2=&sincTable[((chan[0].audSub&0xffff)>>3)<<2];
|
||||
float result=(
|
||||
s_4*t2[3]+
|
||||
s_3*t2[2]+
|
||||
s_2*t2[1]+
|
||||
s_1*t2[0]+
|
||||
s0*t1[0]+
|
||||
s1*t1[1]+
|
||||
s2*t1[2]+
|
||||
s3*t1[3]
|
||||
);
|
||||
if (result<-32768) result=-32768;
|
||||
if (result>32767) result=32767;
|
||||
output=result;
|
||||
break;
|
||||
}
|
||||
default: // none
|
||||
output=s0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
chan[0].sample=-1;
|
||||
|
@ -398,6 +442,8 @@ void DivPlatformPCMDAC::setFlags(const DivConfig& flags) {
|
|||
chipClock=rate;
|
||||
outDepth=(flags.getInt("outDepth",15))&15;
|
||||
outStereo=flags.getBool("stereo",true);
|
||||
interp=flags.getInt("interpolation",0);
|
||||
oscBuf->rate=rate;
|
||||
}
|
||||
|
||||
int DivPlatformPCMDAC::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
@ -55,6 +55,12 @@ class DivPlatformPCMDAC: public DivDispatch {
|
|||
DivDispatchOscBuffer* oscBuf;
|
||||
bool isMuted;
|
||||
int outDepth;
|
||||
// valid values:
|
||||
// - 0: none
|
||||
// - 1: linear
|
||||
// - 2: cubic spline
|
||||
// - 3: sinc
|
||||
int interp;
|
||||
bool outStereo;
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
|
|
|
@ -26,7 +26,20 @@
|
|||
#define CHIP_DIVIDER 1
|
||||
|
||||
const char* regCheatSheetPokeMini[]={
|
||||
"Period", "0",
|
||||
"TMR3_SCALE", "1C",
|
||||
"TMR3_OSC", "1D",
|
||||
"TMR3_CTRL_L", "48",
|
||||
"TMR3_CTRL_H", "49",
|
||||
"TMR3_PRE_L", "4A",
|
||||
"TMR3_PRE_H", "4B",
|
||||
"TMR3_PVT_L", "4C",
|
||||
"TMR3_PVT_H", "4D",
|
||||
"TMR3_CNT_L", "4E",
|
||||
"TMR3_CNT_H", "4F",
|
||||
"IO_DIR", "60",
|
||||
"IO_DATA", "61",
|
||||
"AUD_CTRL", "70",
|
||||
"AUD_VOL", "71",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -42,6 +55,37 @@ const char** DivPlatformPokeMini::getRegisterSheet() {
|
|||
return regCheatSheetPokeMini;
|
||||
}
|
||||
|
||||
void DivPlatformPokeMini::rWrite(unsigned char addr, unsigned char val) {
|
||||
if (addr<128) regPool[addr]=val;
|
||||
switch (addr) {
|
||||
case 0x1c:
|
||||
// ignore
|
||||
break;
|
||||
case 0x1d:
|
||||
// ignore
|
||||
break;
|
||||
case 0x48: case 0x49:
|
||||
on=val&4;
|
||||
if (val&2) pos=0;
|
||||
break;
|
||||
case 0x4a:
|
||||
preset=(preset&0xff00)|val;
|
||||
break;
|
||||
case 0x4b:
|
||||
preset=(preset&0xff)|(val<<8);
|
||||
break;
|
||||
case 0x4c:
|
||||
pivot=(pivot&0xff00)|val;
|
||||
break;
|
||||
case 0x4d:
|
||||
pivot=(pivot&0xff)|(val<<8);
|
||||
break;
|
||||
case 0x71:
|
||||
vol=val&3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPokeMini::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
int out=0;
|
||||
for (size_t i=start; i<start+len; i++) {
|
||||
|
@ -72,7 +116,7 @@ void DivPlatformPokeMini::tick(bool sysTick) {
|
|||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol,chan[i].std.vol.val,2);
|
||||
vol=(chan[i].outVol==2)?3:chan[i].outVol;
|
||||
rWrite(0x71,(chan[i].outVol==2)?3:chan[i].outVol);
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
|
@ -100,13 +144,16 @@ void DivPlatformPokeMini::tick(bool sysTick) {
|
|||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
if (chan[i].keyOn) {
|
||||
on=true;
|
||||
rWrite(0x48,4);
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
on=false;
|
||||
rWrite(0x48,0);
|
||||
}
|
||||
preset=chan[i].freq;
|
||||
pivot=(chan[i].duty*preset)>>8;
|
||||
rWrite(0x4a,chan[i].freq&0xff);
|
||||
rWrite(0x4b,chan[i].freq>>8);
|
||||
int pvt=(chan[i].duty*chan[i].freq)>>8;
|
||||
rWrite(0x4c,pvt&0xff);
|
||||
rWrite(0x4d,pvt>>8);
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
|
@ -122,7 +169,7 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
|
|||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
vol=(chan[c.chan].outVol==2)?3:chan[c.chan].outVol;
|
||||
rWrite(0x71,(chan[c.chan].outVol==2)?3:chan[c.chan].outVol);
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
|
||||
|
@ -151,7 +198,7 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
|
|||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
on=chan[c.chan].vol;
|
||||
rWrite(0x71,(chan[c.chan].outVol==2)?3:chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -186,14 +233,13 @@ int DivPlatformPokeMini::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
if (c.chan==3) break;
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_BEEPER));
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEMINI));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
|
@ -239,18 +285,11 @@ DivDispatchOscBuffer* DivPlatformPokeMini::getOscBuffer(int ch) {
|
|||
}
|
||||
|
||||
unsigned char* DivPlatformPokeMini::getRegisterPool() {
|
||||
if (on) {
|
||||
regPool[0]=preset;
|
||||
regPool[1]=preset>>8;
|
||||
} else {
|
||||
regPool[0]=0;
|
||||
regPool[1]=0;
|
||||
}
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformPokeMini::getRegisterPoolSize() {
|
||||
return 2;
|
||||
return 128;
|
||||
}
|
||||
|
||||
void DivPlatformPokeMini::reset() {
|
||||
|
@ -272,7 +311,7 @@ void DivPlatformPokeMini::reset() {
|
|||
pivot=0;
|
||||
elapsedMain=0;
|
||||
|
||||
memset(regPool,0,2);
|
||||
memset(regPool,0,128);
|
||||
}
|
||||
|
||||
bool DivPlatformPokeMini::keyOffAffectsArp(int ch) {
|
||||
|
@ -294,11 +333,11 @@ void DivPlatformPokeMini::notifyInsDeletion(void* ins) {
|
|||
}
|
||||
|
||||
void DivPlatformPokeMini::poke(unsigned int addr, unsigned short val) {
|
||||
// ???
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformPokeMini::poke(std::vector<DivRegWrite>& wlist) {
|
||||
// ???
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformPokeMini::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
@ -37,9 +37,11 @@ class DivPlatformPokeMini: public DivDispatch {
|
|||
int pos;
|
||||
unsigned char timerScale, vol;
|
||||
unsigned short preset, pivot;
|
||||
unsigned char regPool[2];
|
||||
unsigned char regPool[128];
|
||||
unsigned short elapsedMain;
|
||||
|
||||
void rWrite(unsigned char addr, unsigned char val);
|
||||
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
|
|
504
src/engine/platform/pokey.cpp
Normal file
504
src/engine/platform/pokey.cpp
Normal file
|
@ -0,0 +1,504 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 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 "pokey.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_DIVIDER 1
|
||||
|
||||
const char* regCheatSheetPOKEY[]={
|
||||
"AUDF1", "0",
|
||||
"AUDC1", "1",
|
||||
"AUDF2", "2",
|
||||
"AUDC2", "3",
|
||||
"AUDF3", "4",
|
||||
"AUDC3", "5",
|
||||
"AUDF4", "6",
|
||||
"AUDC4", "7",
|
||||
"AUDCTL", "8",
|
||||
NULL
|
||||
};
|
||||
|
||||
// LLsLSsLLsSLsLLn
|
||||
const unsigned char snapPeriodLong[15]={
|
||||
0, 1, 1, 3, 3, 6, 6, 7, 7, 10, 10, 12, 12, 13, 13
|
||||
};
|
||||
|
||||
const unsigned char snapPeriodShort[15]={
|
||||
2, 2, 2, 2, 5, 5, 5, 8, 8, 11, 11, 11, 11, 17, 17
|
||||
};
|
||||
|
||||
// LsSLsLLnLLsLSsL
|
||||
const unsigned char snapPeriodLong16[15]={
|
||||
0, 0, 3, 3, 3, 5, 6, 6, 8, 9, 9, 11, 11, 14, 14
|
||||
};
|
||||
|
||||
const unsigned char snapPeriodShort16[15]={
|
||||
1, 1, 1, 4, 4, 4, 4, 4, 10, 10, 10, 10, 13, 13, 13
|
||||
};
|
||||
|
||||
const unsigned char waveMap[8]={
|
||||
0, 1, 2, 3, 4, 5, 6, 6
|
||||
};
|
||||
|
||||
const char** DivPlatformPOKEY::getRegisterSheet() {
|
||||
return regCheatSheetPOKEY;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
if (useAltASAP) {
|
||||
acquireASAP(bufL, start, len);
|
||||
} else {
|
||||
acquireMZ(bufL, start, len);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::acquireMZ(short* buf, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
Update_pokey_sound_mz(&pokey,w.addr,w.val,0);
|
||||
regPool[w.addr&0x0f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
mzpokeysnd_process_16(&pokey,&buf[h],1);
|
||||
|
||||
if (++oscBufDelay>=14) {
|
||||
oscBufDelay=0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=pokey.outvol_0<<11;
|
||||
oscBuf[1]->data[oscBuf[1]->needle++]=pokey.outvol_1<<11;
|
||||
oscBuf[2]->data[oscBuf[2]->needle++]=pokey.outvol_2<<11;
|
||||
oscBuf[3]->data[oscBuf[3]->needle++]=pokey.outvol_3<<11;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::acquireASAP(short* buf, size_t start, size_t len) {
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
altASAP.write(w.addr, w.val);
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
if (++oscBufDelay>=2) {
|
||||
oscBufDelay=0;
|
||||
buf[h]=altASAP.sampleAudio(oscBuf);
|
||||
} else {
|
||||
buf[h]=altASAP.sampleAudio();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&15,MIN(15,chan[i].std.vol.val),15);
|
||||
chan[i].ctlChanged=true;
|
||||
}
|
||||
if (NEW_ARP_STRAT) {
|
||||
chan[i].handleArp();
|
||||
} else if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.duty.had) {
|
||||
audctl=chan[i].std.duty.val;
|
||||
audctlChanged=true;
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
chan[i].ctlChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
if (audctlChanged) {
|
||||
audctlChanged=false;
|
||||
rWrite(8,audctl);
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].ctlChanged=true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<4; i++) {
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
|
||||
|
||||
if ((i==0 && !(audctl&64)) || (i==2 && !(audctl&32)) || i==1 || i==3) {
|
||||
chan[i].freq/=7;
|
||||
switch (chan[i].wave) {
|
||||
case 6:
|
||||
chan[i].freq/=5;
|
||||
chan[i].freq>>=1;
|
||||
break;
|
||||
case 7:
|
||||
if (audctl&1) {
|
||||
chan[i].freq/=5;
|
||||
} else {
|
||||
chan[i].freq/=15;
|
||||
}
|
||||
chan[i].freq>>=1;
|
||||
break;
|
||||
default:
|
||||
chan[i].freq>>=2;
|
||||
break;
|
||||
}
|
||||
} else if ((i==0 && audctl&64) || (i==2 && audctl&32)) {
|
||||
switch (chan[i].wave) {
|
||||
case 6:
|
||||
chan[i].freq<<=1;
|
||||
chan[i].freq/=5;
|
||||
break;
|
||||
case 7:
|
||||
chan[i].freq<<=1;
|
||||
chan[i].freq/=15;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (audctl&1 && !((i==0 && audctl&64) || (i==2 && audctl&32))) {
|
||||
chan[i].freq>>=2;
|
||||
}
|
||||
|
||||
if (--chan[i].freq<0) chan[i].freq=0;
|
||||
|
||||
// snap buzz periods
|
||||
int minFreq8=255;
|
||||
if (chan[i].wave==7) {
|
||||
if ((i==0 && audctl&64) || (i==2 && audctl&32)) {
|
||||
chan[i].freq=15*(chan[i].freq/15)+snapPeriodLong16[(chan[i].freq%15)]+1;
|
||||
} else {
|
||||
if (!(audctl&1)) chan[i].freq=15*(chan[i].freq/15)+snapPeriodLong[(chan[i].freq%15)];
|
||||
}
|
||||
} else if (chan[i].wave==6) {
|
||||
if ((i==0 && audctl&64) || (i==2 && audctl&32)) {
|
||||
chan[i].freq=15*(chan[i].freq/15)+snapPeriodShort16[(chan[i].freq%15)]+1;
|
||||
} else {
|
||||
if (!(audctl&1)) chan[i].freq=15*(chan[i].freq/15)+snapPeriodShort[(chan[i].freq%15)];
|
||||
}
|
||||
minFreq8=251;
|
||||
}
|
||||
|
||||
if ((i==0 && audctl&16) || (i==2 && audctl&8)) {
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
} else {
|
||||
if (chan[i].freq>minFreq8) chan[i].freq=minFreq8;
|
||||
}
|
||||
|
||||
// write frequency
|
||||
if ((i==1 && audctl&16) || (i==3 && audctl&8)) {
|
||||
// ignore - channel is paired
|
||||
} else {
|
||||
rWrite(i<<1,chan[i].freq&0xff);
|
||||
if ((i==0 && audctl&16) || (i==2 && audctl&8)) {
|
||||
rWrite((1+i)<<1,chan[i].freq>>8);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[i].keyOff) {
|
||||
chan[i].ctlChanged=true;
|
||||
}
|
||||
if (chan[i].keyOn) chan[i].keyOn=false;
|
||||
if (chan[i].keyOff) chan[i].keyOff=false;
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
if (chan[i].ctlChanged) {
|
||||
unsigned char val=((chan[i].active && !isMuted[i])?(chan[i].outVol&15):0)|(waveMap[chan[i].wave&7]<<5);
|
||||
chan[i].ctlChanged=false;
|
||||
if ((i==1 && audctl&16) || (i==3 && audctl&8)) {
|
||||
// ignore - channel is paired
|
||||
} else if ((i==0 && audctl&16) || (i==0 && audctl&8)) {
|
||||
rWrite(1+(i<<1),0);
|
||||
rWrite(3+(i<<1),val);
|
||||
} else {
|
||||
|
||||
rWrite(1+(i<<1),val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformPOKEY::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POKEY);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
chan[c.chan].ctlChanged=true;
|
||||
}
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
chan[c.chan].insChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].ctlChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].ctlChanged=true;
|
||||
break;
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
audctl=c.value&0xff;
|
||||
audctlChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_POKEY));
|
||||
}
|
||||
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
break;
|
||||
case DIV_CMD_MACRO_ON:
|
||||
chan[c.chan].std.mask(c.value,false);
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chan[ch].ctlChanged=true;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::forceIns() {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].ctlChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
audctlChanged=true;
|
||||
}
|
||||
|
||||
void* DivPlatformPOKEY::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivMacroInt* DivPlatformPOKEY::getChanMacroInt(int ch) {
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformPOKEY::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformPOKEY::getRegisterPool() {
|
||||
if (useAltASAP) {
|
||||
return const_cast<unsigned char*>(altASAP.getRegisterPool());
|
||||
} else {
|
||||
return regPool;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformPOKEY::getRegisterPoolSize() {
|
||||
return 9;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
memset(regPool,0,16);
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i]=DivPlatformPOKEY::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
||||
if (useAltASAP) {
|
||||
altASAP.reset();
|
||||
} else {
|
||||
ResetPokeyState(&pokey);
|
||||
}
|
||||
|
||||
audctl=0;
|
||||
audctlChanged=true;
|
||||
}
|
||||
|
||||
bool DivPlatformPOKEY::keyOffAffectsArp(int ch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
float DivPlatformPOKEY::getPostAmp() {
|
||||
return 2.0f;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<4; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::setFlags(const DivConfig& flags) {
|
||||
if (flags.getInt("clockSel",0)) {
|
||||
chipClock=COLOR_PAL*2.0/5.0;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
}
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
|
||||
if (useAltASAP) {
|
||||
rate=chipClock/7;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate/2;
|
||||
}
|
||||
altASAP.init(chipClock,rate);
|
||||
} else {
|
||||
rate=chipClock;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate/14;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
int DivPlatformPOKEY::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
oscBufDelay=0;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
|
||||
if (!useAltASAP) {
|
||||
MZPOKEYSND_Init(&pokey);
|
||||
}
|
||||
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 6;
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::quit() {
|
||||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformPOKEY::setAltASAP(bool value) {
|
||||
useAltASAP=value;
|
||||
}
|
||||
|
||||
DivPlatformPOKEY::~DivPlatformPOKEY() {
|
||||
}
|
87
src/engine/platform/pokey.h
Normal file
87
src/engine/platform/pokey.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 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.
|
||||
*/
|
||||
|
||||
#ifndef _POKEY_H
|
||||
#define _POKEY_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
|
||||
extern "C" {
|
||||
#include "sound/pokey/mzpokeysnd.h"
|
||||
}
|
||||
|
||||
#include "sound/pokey/AltASAP.hpp"
|
||||
|
||||
|
||||
class DivPlatformPOKEY: public DivDispatch {
|
||||
struct Channel: public SharedChannel<int> {
|
||||
unsigned char wave;
|
||||
bool ctlChanged;
|
||||
Channel():
|
||||
SharedChannel<int>(15),
|
||||
wave(5),
|
||||
ctlChanged(true) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
unsigned char audctl;
|
||||
bool audctlChanged;
|
||||
unsigned char oscBufDelay;
|
||||
PokeyState pokey;
|
||||
AltASAP::Pokey altASAP;
|
||||
bool useAltASAP;
|
||||
unsigned char regPool[16];
|
||||
friend void putDispatchChip(void*,int);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void acquireMZ(short* buf, size_t start, size_t len);
|
||||
void acquireASAP(short* buf, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
float getPostAmp();
|
||||
void setFlags(const DivConfig& flags);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||
void quit();
|
||||
void setAltASAP(bool useAltASAP);
|
||||
~DivPlatformPOKEY();
|
||||
};
|
||||
|
||||
#endif
|
|
@ -61,6 +61,11 @@ void DivPlatformRF5C68::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
buf[8],buf[9],buf[10],buf[11],buf[12],buf[13],buf[14],buf[15]
|
||||
};
|
||||
size_t pos=start;
|
||||
|
||||
for (int i=0; i<16; i++) {
|
||||
memset(buf[i],0,256*sizeof(short));
|
||||
}
|
||||
|
||||
while (len > 0) {
|
||||
size_t blockLen=MIN(len,256);
|
||||
short* bufPtrs[2]={&bufL[pos],&bufR[pos]};
|
||||
|
|
|
@ -96,24 +96,16 @@ void DivPlatformSegaPCM::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
if (parent->song.newSegaPCM) if (chan[i].std.panL.had) {
|
||||
if (chan[i].isNewSegaPCM) {
|
||||
chan[i].chPanL=chan[i].std.panL.val&127;
|
||||
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
|
||||
} else {
|
||||
chan[i].chVolL=chan[i].std.panL.val&127;
|
||||
}
|
||||
chan[i].chPanL=chan[i].std.panL.val&127;
|
||||
chan[i].chVolL=(chan[i].outVol*chan[i].chPanL)/127;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10002+(i<<3),chan[i].chVolL);
|
||||
}
|
||||
}
|
||||
|
||||
if (parent->song.newSegaPCM) if (chan[i].std.panR.had) {
|
||||
if (chan[i].isNewSegaPCM) {
|
||||
chan[i].chPanR=chan[i].std.panR.val&127;
|
||||
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
|
||||
} else {
|
||||
chan[i].chVolR=chan[i].std.panR.val&127;
|
||||
}
|
||||
chan[i].chPanR=chan[i].std.panR.val&127;
|
||||
chan[i].chVolR=(chan[i].outVol*chan[i].chPanR)/127;
|
||||
if (dumpWrites) {
|
||||
addWrite(0x10003+(i<<3),chan[i].chVolR);
|
||||
}
|
||||
|
@ -287,7 +279,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (parent->song.newSegaPCM && chan[c.chan].isNewSegaPCM) {
|
||||
if (parent->song.newSegaPCM) {
|
||||
chan[c.chan].chVolL=(c.value*chan[c.chan].chPanL)/127;
|
||||
chan[c.chan].chVolR=(c.value*chan[c.chan].chPanR)/127;
|
||||
} else {
|
||||
|
@ -311,7 +303,7 @@ int DivPlatformSegaPCM::dispatch(DivCommand c) {
|
|||
chan[c.chan].ins=c.value;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (parent->song.newSegaPCM && chan[c.chan].isNewSegaPCM) {
|
||||
if (parent->song.newSegaPCM) {
|
||||
chan[c.chan].chPanL=c.value>>1;
|
||||
chan[c.chan].chPanR=c.value2>>1;
|
||||
chan[c.chan].chVolL=(chan[c.chan].outVol*chan[c.chan].chPanL)/127;
|
||||
|
|
653
src/engine/platform/sound/pokey/AltASAP.cpp
Normal file
653
src/engine/platform/sound/pokey/AltASAP.cpp
Normal file
|
@ -0,0 +1,653 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* Original author: Piotr Fusik (http://asap.sourceforge.net)
|
||||
* Rewritten based on Mikey emulation by Waldemar Pawlaszek
|
||||
*
|
||||
* 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 "AltASAP.hpp"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
namespace AltASAP
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static constexpr int64_t CNT_MAX = std::numeric_limits<int64_t>::max() & ~7;
|
||||
static constexpr int MuteFrequency = 1;
|
||||
static constexpr int MuteInit = 2;
|
||||
static constexpr int MuteSerialInput = 8;
|
||||
//just some magick value to match the audio level of mzpokeysnd
|
||||
static constexpr int16_t MAGICK_VOLUME_BOOSTER = 160;
|
||||
static constexpr int16_t MAGICK_OSC_VOLUME_BOOSTER = 4;
|
||||
|
||||
struct PokeyBase
|
||||
{
|
||||
int64_t mPolyIndex;
|
||||
int mAudctl;
|
||||
int mSkctl;
|
||||
bool mInit;
|
||||
std::array<uint8_t, 511> mPoly9Lookup;
|
||||
std::array<uint8_t, 16385> mPoly17Lookup;
|
||||
|
||||
|
||||
PokeyBase() : mPolyIndex{ 15 * 31 * 131071 }, mAudctl{ 0 }, mSkctl{ 3 }, mInit{ false }, mPoly9Lookup{}, mPoly17Lookup{}
|
||||
{
|
||||
int reg = 0x1ff;
|
||||
for ( int i = 0; i < 511; i++ )
|
||||
{
|
||||
reg = ( ( ( reg >> 5 ^ reg ) & 1 ) << 8 ) + ( reg >> 1 );
|
||||
mPoly9Lookup[i] = reg & 0xff;
|
||||
}
|
||||
reg = 0x1ffff;
|
||||
for ( int i = 0; i < 16385; i++ )
|
||||
{
|
||||
reg = ( ( ( reg >> 5 ^ reg ) & 0xff ) << 9 ) + ( reg >> 8 );
|
||||
mPoly17Lookup[i] = reg >> 1 & 0xff;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
"Queue" holding event timepoints.
|
||||
- 4 channel timer fire points
|
||||
- 1 sample point
|
||||
Three LSBs are used to encode event kind: 0-3 are channels, 4 is sampling.
|
||||
Channel sequence is 2,3,0,1
|
||||
*/
|
||||
class ActionQueue
|
||||
{
|
||||
public:
|
||||
|
||||
ActionQueue() : mTab{ CNT_MAX | 0, CNT_MAX | 1, CNT_MAX | 2, CNT_MAX | 3, CNT_MAX | 4 }
|
||||
{
|
||||
}
|
||||
|
||||
void insert( int idx, int64_t value )
|
||||
{
|
||||
assert( idx < (int)mTab.size() );
|
||||
idx ^= 2;
|
||||
mTab[idx] = value | idx;
|
||||
}
|
||||
|
||||
void insertSampling( int64_t value )
|
||||
{
|
||||
mTab[4] = value | 4;
|
||||
}
|
||||
|
||||
void disable( int idx )
|
||||
{
|
||||
assert( idx < (int)mTab.size() );
|
||||
idx ^= 2;
|
||||
mTab[idx] = CNT_MAX | idx;
|
||||
}
|
||||
|
||||
bool enqueued( int idx )
|
||||
{
|
||||
assert( idx < (int)mTab.size() );
|
||||
idx ^= 2;
|
||||
return mTab[idx] < CNT_MAX;
|
||||
}
|
||||
|
||||
int64_t pop()
|
||||
{
|
||||
int64_t min1 = std::min( mTab[0], mTab[1] );
|
||||
int64_t min2 = std::min( mTab[2], mTab[3] );
|
||||
int64_t min3 = std::min( min1, mTab[4] );
|
||||
int64_t min4 = std::min( min2, min3 );
|
||||
|
||||
return min4 ^ 2;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<int64_t, 5> mTab;
|
||||
};
|
||||
|
||||
class AudioChannel
|
||||
{
|
||||
public:
|
||||
AudioChannel( ActionQueue& queue, uint32_t number ) : mQueue{ queue }, mNumber{ number }, mAudf{ 0 }, mAudc{ 0 }, mPeriodCycles{ 28 }, mMute{ MuteFrequency }, mDelta{ 0 }, mOut{ 0 }
|
||||
{
|
||||
}
|
||||
|
||||
int16_t getOutput() const
|
||||
{
|
||||
return (int16_t)mDelta;
|
||||
}
|
||||
|
||||
void fillRegisterPool( uint8_t* regs )
|
||||
{
|
||||
regs[0] = (uint8_t)mAudf;
|
||||
regs[1] = (uint8_t)mAudc;
|
||||
}
|
||||
|
||||
void renewTrigger( int64_t cycle )
|
||||
{
|
||||
overrideTrigger( cycle + mPeriodCycles );
|
||||
}
|
||||
|
||||
void overrideTrigger( int64_t cycle )
|
||||
{
|
||||
mQueue.insert( mNumber, cycle << 3 );
|
||||
}
|
||||
|
||||
void doStimer( int64_t cycle )
|
||||
{
|
||||
if ( mQueue.enqueued( mNumber ) )
|
||||
renewTrigger( cycle );
|
||||
}
|
||||
|
||||
void trigger( int64_t cycle, PokeyBase const& pokey )
|
||||
{
|
||||
renewTrigger( cycle );
|
||||
if ( ( mAudc & 0xb0 ) == 0xa0 )
|
||||
mOut ^= 1;
|
||||
else if ( ( mAudc & 0x10 ) != 0 || pokey.mInit )
|
||||
return;
|
||||
else
|
||||
{
|
||||
int64_t poly = cycle + pokey.mPolyIndex - mNumber;
|
||||
if ( mAudc < 0x80 && ( 0x65bd44e0 & 1 << ( poly % 31 ) ) == 0 ) // 0000011100100010101111011010011
|
||||
return;
|
||||
if ( ( mAudc & 0x20 ) != 0 )
|
||||
mOut ^= 1;
|
||||
else
|
||||
{
|
||||
uint32_t newOut;
|
||||
if ( ( mAudc & 0x40 ) != 0 )
|
||||
newOut = 0x5370u >> (int)( poly % 15 ); // 000011101100101
|
||||
else if ( pokey.mAudctl < 0x80 )
|
||||
{
|
||||
poly %= 131071;
|
||||
newOut = pokey.mPoly17Lookup[poly >> 3] >> ( poly & 7 );
|
||||
}
|
||||
else
|
||||
newOut = pokey.mPoly9Lookup[poly % 511];
|
||||
newOut &= 1;
|
||||
if ( mOut == newOut )
|
||||
return;
|
||||
mOut = newOut;
|
||||
}
|
||||
}
|
||||
toggle();
|
||||
}
|
||||
|
||||
uint32_t mute() const
|
||||
{
|
||||
return mMute;
|
||||
}
|
||||
|
||||
uint32_t audf() const
|
||||
{
|
||||
return mAudf;
|
||||
}
|
||||
|
||||
uint32_t audc() const
|
||||
{
|
||||
return mAudc;
|
||||
}
|
||||
|
||||
void setAudf( uint32_t value )
|
||||
{
|
||||
mAudf = value;
|
||||
}
|
||||
|
||||
void setAudc( int64_t cycle, uint32_t value )
|
||||
{
|
||||
if ( mAudc == value )
|
||||
return;
|
||||
mAudc = value;
|
||||
int32_t volume = value & 0x0f;
|
||||
if ( ( value & 0x10 ) != 0 )
|
||||
{
|
||||
mDelta = volume * MAGICK_VOLUME_BOOSTER;
|
||||
}
|
||||
else
|
||||
{
|
||||
muteUltrasound( cycle );
|
||||
if ( mDelta > 0 )
|
||||
mDelta = volume * MAGICK_VOLUME_BOOSTER;
|
||||
else
|
||||
mDelta = -volume * MAGICK_VOLUME_BOOSTER;
|
||||
}
|
||||
}
|
||||
|
||||
void toggle()
|
||||
{
|
||||
mDelta = -mDelta;
|
||||
}
|
||||
|
||||
void setPeriodCycles( int64_t value )
|
||||
{
|
||||
mPeriodCycles = value;
|
||||
}
|
||||
|
||||
void setMute( bool enable, int mask, int64_t cycle )
|
||||
{
|
||||
if ( enable )
|
||||
{
|
||||
mMute |= mask;
|
||||
mQueue.disable( mNumber );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMute &= ~mask;
|
||||
if ( mMute == 0 && !mQueue.enqueued( mNumber ) )
|
||||
overrideTrigger( cycle );
|
||||
}
|
||||
}
|
||||
|
||||
void muteUltrasound( int64_t cycle )
|
||||
{
|
||||
static constexpr int UltrasoundCycles = 112;
|
||||
setMute( mPeriodCycles <= UltrasoundCycles && ( mAudc & 0xb0 ) == 0xa0, MuteFrequency, cycle );
|
||||
}
|
||||
|
||||
private:
|
||||
ActionQueue& mQueue;
|
||||
uint32_t mNumber;
|
||||
uint32_t mAudf;
|
||||
uint32_t mAudc;
|
||||
int64_t mPeriodCycles;
|
||||
|
||||
uint32_t mMute;
|
||||
int32_t mDelta;
|
||||
uint32_t mOut;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
class PokeyPimpl : public PokeyBase
|
||||
{
|
||||
public:
|
||||
|
||||
PokeyPimpl( uint32_t pokeyClock, uint32_t sampleRate ) : PokeyBase{}, mQueue{ std::make_unique<ActionQueue>() },
|
||||
mAudioChannels{ AudioChannel{ *mQueue, 0u }, AudioChannel{ *mQueue, 1u }, AudioChannel{ *mQueue, 2u }, AudioChannel{ *mQueue, 3u } },
|
||||
mRegisterPool{}, mTick{}, mNextTick{},
|
||||
mReloadCycles1{ 28 }, mReloadCycles3{ 28 }, mDivCycles{ 28 }, mSampleRate{ sampleRate }, mSamplesRemainder{},
|
||||
mTicksPerSample{ ( pokeyClock * 8 ) / mSampleRate, ( pokeyClock * 8 ) % mSampleRate }
|
||||
{
|
||||
std::fill_n( mRegisterPool.data(), mRegisterPool.size(), (uint8_t)0xff );
|
||||
enqueueSampling();
|
||||
}
|
||||
|
||||
~PokeyPimpl() {}
|
||||
|
||||
void write( uint8_t address, uint8_t value )
|
||||
{
|
||||
auto cycle = mTick >> 3;
|
||||
|
||||
switch ( address & 0xf )
|
||||
{
|
||||
case 0x00:
|
||||
if ( value == mAudioChannels[0].audf() )
|
||||
break;
|
||||
mAudioChannels[0].setAudf( value );
|
||||
switch ( mAudctl & 0x50 )
|
||||
{
|
||||
case 0x00:
|
||||
mAudioChannels[0].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||
break;
|
||||
case 0x10:
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( value + ( mAudioChannels[1].audf() << 8 ) + 1 ) );
|
||||
mReloadCycles1 = mDivCycles * ( value + 1 );
|
||||
mAudioChannels[1].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x40:
|
||||
mAudioChannels[0].setPeriodCycles( value + 4 );
|
||||
break;
|
||||
case 0x50:
|
||||
mAudioChannels[1].setPeriodCycles( value + ( mAudioChannels[1].audf() << 8 ) + 7 );
|
||||
mReloadCycles1 = value + 4;
|
||||
mAudioChannels[1].muteUltrasound( cycle );
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[0].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x01:
|
||||
mAudioChannels[0].setAudc( cycle, value );
|
||||
break;
|
||||
case 0x02:
|
||||
if ( value == mAudioChannels[1].audf() )
|
||||
break;
|
||||
mAudioChannels[1].setAudf( value );
|
||||
switch ( mAudctl & 0x50 )
|
||||
{
|
||||
case 0x00:
|
||||
case 0x40:
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||
break;
|
||||
case 0x10:
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + ( value << 8 ) + 1 ) );
|
||||
break;
|
||||
case 0x50:
|
||||
mAudioChannels[1].setPeriodCycles( mAudioChannels[0].audf() + ( value << 8 ) + 7 );
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[1].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x03:
|
||||
mAudioChannels[1].setAudc( cycle, value );
|
||||
break;
|
||||
case 0x04:
|
||||
if ( value == mAudioChannels[2].audf() )
|
||||
break;
|
||||
mAudioChannels[2].setAudf( value );
|
||||
switch ( mAudctl & 0x28 )
|
||||
{
|
||||
case 0x00:
|
||||
mAudioChannels[2].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||
break;
|
||||
case 0x08:
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( value + ( mAudioChannels[3].audf() << 8 ) + 1 ) );
|
||||
mReloadCycles3 = mDivCycles * ( value + 1 );
|
||||
mAudioChannels[3].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x20:
|
||||
mAudioChannels[2].setPeriodCycles( value + 4 );
|
||||
break;
|
||||
case 0x28:
|
||||
mAudioChannels[3].setPeriodCycles( value + ( mAudioChannels[3].audf() << 8 ) + 7 );
|
||||
mReloadCycles3 = value + 4;
|
||||
mAudioChannels[3].muteUltrasound( cycle );
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[2].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x05:
|
||||
mAudioChannels[2].setAudc( cycle, value );
|
||||
break;
|
||||
case 0x06:
|
||||
if ( value == mAudioChannels[3].audf() )
|
||||
break;
|
||||
mAudioChannels[3].setAudf( value );
|
||||
switch ( mAudctl & 0x28 )
|
||||
{
|
||||
case 0x00:
|
||||
case 0x20:
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||
break;
|
||||
case 0x08:
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + ( value << 8 ) + 1 ) );
|
||||
break;
|
||||
case 0x28:
|
||||
mAudioChannels[3].setPeriodCycles( mAudioChannels[2].audf() + ( value << 8 ) + 7 );
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[3].muteUltrasound( cycle );
|
||||
break;
|
||||
case 0x07:
|
||||
mAudioChannels[3].setAudc( cycle, value );
|
||||
break;
|
||||
case 0x08:
|
||||
if ( value == mAudctl )
|
||||
break;
|
||||
mAudctl = value;
|
||||
mDivCycles = ( value & 1 ) != 0 ? 114 : 28;
|
||||
switch ( value & 0x50 )
|
||||
{
|
||||
case 0x00:
|
||||
mAudioChannels[0].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + 1 ) );
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[1].audf() + 1 ) );
|
||||
break;
|
||||
case 0x10:
|
||||
mAudioChannels[0].setPeriodCycles( (int64_t)mDivCycles << 8 );
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + ( mAudioChannels[1].audf() << 8 ) + 1 ) );
|
||||
mReloadCycles1 = mDivCycles * ( mAudioChannels[0].audf() + 1 );
|
||||
break;
|
||||
case 0x40:
|
||||
mAudioChannels[0].setPeriodCycles( mAudioChannels[0].audf() + 4 );
|
||||
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[1].audf() + 1 ) );
|
||||
break;
|
||||
case 0x50:
|
||||
mAudioChannels[0].setPeriodCycles( 256 );
|
||||
mAudioChannels[1].setPeriodCycles( mAudioChannels[0].audf() + ( mAudioChannels[1].audf() << 8 ) + 7 );
|
||||
mReloadCycles1 = mAudioChannels[0].audf() + 4;
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[0].muteUltrasound( cycle );
|
||||
mAudioChannels[1].muteUltrasound( cycle );
|
||||
switch ( value & 0x28 )
|
||||
{
|
||||
case 0x00:
|
||||
mAudioChannels[2].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + 1 ) );
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[3].audf() + 1 ) );
|
||||
break;
|
||||
case 0x08:
|
||||
mAudioChannels[2].setPeriodCycles( (int64_t)mDivCycles << 8 );
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + ( mAudioChannels[3].audf() << 8 ) + 1 ) );
|
||||
mReloadCycles3 = mDivCycles * ( mAudioChannels[2].audf() + 1 );
|
||||
break;
|
||||
case 0x20:
|
||||
mAudioChannels[2].setPeriodCycles( mAudioChannels[2].audf() + 4 );
|
||||
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[3].audf() + 1 ) );
|
||||
break;
|
||||
case 0x28:
|
||||
mAudioChannels[2].setPeriodCycles( 256 );
|
||||
mAudioChannels[3].setPeriodCycles( mAudioChannels[2].audf() + ( mAudioChannels[3].audf() << 8 ) + 7 );
|
||||
mReloadCycles3 = mAudioChannels[2].audf() + 4;
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
mAudioChannels[2].muteUltrasound( cycle );
|
||||
mAudioChannels[3].muteUltrasound( cycle );
|
||||
initMute( cycle );
|
||||
break;
|
||||
case 0x09:
|
||||
for ( int i = 0; i < 4; i++ )
|
||||
mAudioChannels[i].doStimer( cycle );
|
||||
break;
|
||||
case 0x0f:
|
||||
{
|
||||
if ( value == mSkctl )
|
||||
break;
|
||||
mSkctl = value;
|
||||
bool init = ( value & 3 ) == 0;
|
||||
if ( mInit && !init )
|
||||
mPolyIndex = ( ( mAudctl & 0x80 ) != 0 ? 15 * 31 * 511 - 1 : 15 * 31 * 131071 - 1 ) - cycle;
|
||||
mInit = init;
|
||||
initMute( cycle );
|
||||
mAudioChannels[2].setMute( ( value & 0x10 ) != 0, MuteSerialInput, cycle );
|
||||
mAudioChannels[3].setMute( ( value & 0x10 ) != 0, MuteSerialInput, cycle );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t sampleAudio( DivDispatchOscBuffer** oscb )
|
||||
{
|
||||
for ( ;; )
|
||||
{
|
||||
int64_t value = mQueue->pop();
|
||||
if ( ( value & 7 ) == 6 ) // 6 == 4 ^ 2
|
||||
{
|
||||
int16_t ch0 = mAudioChannels[0].getOutput();
|
||||
int16_t ch1 = mAudioChannels[1].getOutput();
|
||||
int16_t ch2 = mAudioChannels[2].getOutput();
|
||||
int16_t ch3 = mAudioChannels[3].getOutput();
|
||||
|
||||
if ( oscb != nullptr )
|
||||
{
|
||||
oscb[0]->data[oscb[0]->needle++]=ch0 * MAGICK_OSC_VOLUME_BOOSTER;
|
||||
oscb[1]->data[oscb[1]->needle++]=ch1 * MAGICK_OSC_VOLUME_BOOSTER;
|
||||
oscb[2]->data[oscb[2]->needle++]=ch2 * MAGICK_OSC_VOLUME_BOOSTER;
|
||||
oscb[3]->data[oscb[3]->needle++]=ch3 * MAGICK_OSC_VOLUME_BOOSTER;
|
||||
}
|
||||
|
||||
enqueueSampling();
|
||||
return ch0 + ch1 + ch2 + ch3;
|
||||
}
|
||||
else
|
||||
{
|
||||
fireTimer( value );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t const* getRegisterPool()
|
||||
{
|
||||
for ( size_t i = 0; i < mAudioChannels.size(); ++i )
|
||||
{
|
||||
mAudioChannels[i].fillRegisterPool( mRegisterPool.data() + 2 * i );
|
||||
}
|
||||
|
||||
mRegisterPool[8] = mAudctl;
|
||||
|
||||
return mRegisterPool.data();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void initMute( int64_t cycle )
|
||||
{
|
||||
mAudioChannels[0].setMute( mInit && ( mAudctl & 0x40 ) == 0, MuteInit, cycle );
|
||||
mAudioChannels[1].setMute( mInit && ( mAudctl & 0x50 ) != 0x50, MuteInit, cycle );
|
||||
mAudioChannels[2].setMute( mInit && ( mAudctl & 0x20 ) == 0, MuteInit, cycle );
|
||||
mAudioChannels[3].setMute( mInit && ( mAudctl & 0x28 ) != 0x28, MuteInit, cycle );
|
||||
}
|
||||
|
||||
void fireTimer( int64_t tick )
|
||||
{
|
||||
mTick = tick & ~7;
|
||||
size_t ch = tick & 3;
|
||||
auto cycle = tick >> 3;
|
||||
|
||||
switch ( ch )
|
||||
{
|
||||
case 0:
|
||||
if ( ( mSkctl & 0x88 ) == 8 ) // two-tone, sending 1 (i.e. timer1)
|
||||
mAudioChannels[1].renewTrigger( cycle );
|
||||
mAudioChannels[0].trigger( cycle, *this );
|
||||
break;
|
||||
case 1:
|
||||
if ( ( mAudctl & 0x10 ) != 0 )
|
||||
mAudioChannels[0].overrideTrigger( cycle + mReloadCycles1 );
|
||||
else if ( ( mSkctl & 8 ) != 0 ) // two-tone
|
||||
mAudioChannels[0].renewTrigger( cycle );
|
||||
mAudioChannels[1].trigger( cycle, *this );
|
||||
break;
|
||||
case 2:
|
||||
if ( ( mAudctl & 4 ) != 0 && mAudioChannels[0].getOutput() > 0 && mAudioChannels[0].mute() == 0 )
|
||||
mAudioChannels[0].toggle();
|
||||
mAudioChannels[2].trigger( cycle, *this );
|
||||
break;
|
||||
case 3:
|
||||
if ( ( mAudctl & 8 ) != 0 )
|
||||
mAudioChannels[2].overrideTrigger( cycle + mReloadCycles3 );
|
||||
if ( ( mAudctl & 2 ) != 0 && mAudioChannels[1].getOutput() > 0 && mAudioChannels[1].mute() == 0 )
|
||||
mAudioChannels[1].toggle();
|
||||
mAudioChannels[3].trigger( cycle, *this );
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void enqueueSampling()
|
||||
{
|
||||
mTick = mNextTick & ~7;
|
||||
mNextTick = mNextTick + mTicksPerSample.first;
|
||||
mSamplesRemainder += mTicksPerSample.second;
|
||||
if ( mSamplesRemainder > mSampleRate )
|
||||
{
|
||||
mSamplesRemainder %= mSampleRate;
|
||||
mNextTick += 1;
|
||||
}
|
||||
|
||||
mQueue->insertSampling( mNextTick & ~7 );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<ActionQueue> mQueue;
|
||||
std::array<AudioChannel, 4> mAudioChannels;
|
||||
|
||||
std::array<uint8_t, 4 * 2 + 1> mRegisterPool;
|
||||
|
||||
uint64_t mTick;
|
||||
uint64_t mNextTick;
|
||||
int64_t mReloadCycles1;
|
||||
int64_t mReloadCycles3;
|
||||
int64_t mDivCycles;
|
||||
uint32_t mSampleRate;
|
||||
uint32_t mSamplesRemainder;
|
||||
std::pair<uint32_t, uint32_t> mTicksPerSample;
|
||||
};
|
||||
|
||||
//Initializing periods with safe defaults
|
||||
Pokey::Pokey() : mPokeyClock{ (uint32_t)COLOR_NTSC / 2 }, mSampleRate{ mPokeyClock / 7 }, mPokey{}
|
||||
{
|
||||
}
|
||||
|
||||
void Pokey::init( uint32_t pokeyClock, uint32_t sampleRate )
|
||||
{
|
||||
mPokey.reset();
|
||||
mPokeyClock = pokeyClock;
|
||||
mSampleRate = sampleRate;
|
||||
}
|
||||
|
||||
void Pokey::reset()
|
||||
{
|
||||
mPokey = std::make_unique<PokeyPimpl>( mPokeyClock, mSampleRate );
|
||||
}
|
||||
|
||||
Pokey::~Pokey()
|
||||
{
|
||||
}
|
||||
|
||||
void Pokey::write( uint8_t address, uint8_t value )
|
||||
{
|
||||
assert( mPokey );
|
||||
mPokey->write( address, value );
|
||||
}
|
||||
|
||||
int16_t Pokey::sampleAudio( DivDispatchOscBuffer** oscb )
|
||||
{
|
||||
assert( mPokey );
|
||||
return mPokey->sampleAudio( oscb );
|
||||
}
|
||||
|
||||
uint8_t const* Pokey::getRegisterPool()
|
||||
{
|
||||
assert( mPokey );
|
||||
return mPokey->getRegisterPool();
|
||||
}
|
||||
|
||||
}
|
35
src/engine/platform/sound/pokey/AltASAP.hpp
Normal file
35
src/engine/platform/sound/pokey/AltASAP.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
// can you forgive me
|
||||
#include "../../../dispatch.h"
|
||||
|
||||
namespace AltASAP
|
||||
{
|
||||
|
||||
class PokeyPimpl;
|
||||
|
||||
class Pokey
|
||||
{
|
||||
public:
|
||||
|
||||
Pokey();
|
||||
void init( uint32_t pokeyClock, uint32_t sampleRate );
|
||||
~Pokey();
|
||||
|
||||
void write( uint8_t address, uint8_t value );
|
||||
int16_t sampleAudio( DivDispatchOscBuffer** oscb = nullptr );
|
||||
|
||||
uint8_t const* getRegisterPool();
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
uint32_t mPokeyClock;
|
||||
uint32_t mSampleRate;
|
||||
std::unique_ptr<PokeyPimpl> mPokey;
|
||||
};
|
||||
|
||||
}
|
1655
src/engine/platform/sound/pokey/mzpokeysnd.c
Normal file
1655
src/engine/platform/sound/pokey/mzpokeysnd.c
Normal file
File diff suppressed because it is too large
Load diff
159
src/engine/platform/sound/pokey/mzpokeysnd.h
Normal file
159
src/engine/platform/sound/pokey/mzpokeysnd.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
#ifndef MZPOKEYSND_H_
|
||||
#define MZPOKEYSND_H_
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
struct stPokeyState;
|
||||
|
||||
typedef int (*readout_t)(struct stPokeyState* ps);
|
||||
typedef void (*event_t)(struct stPokeyState* ps, int p5v, int p4v, int p917v);
|
||||
|
||||
#ifdef NONLINEAR_MIXING
|
||||
/* Change queue event value type */
|
||||
typedef double qev_t;
|
||||
#else
|
||||
typedef unsigned char qev_t;
|
||||
#endif
|
||||
|
||||
/* State variables for single Pokey Chip */
|
||||
typedef struct stPokeyState
|
||||
{
|
||||
int curtick;
|
||||
/* Poly positions */
|
||||
int poly4pos;
|
||||
int poly5pos;
|
||||
int poly17pos;
|
||||
int poly9pos;
|
||||
|
||||
/* Main divider (64khz/15khz) */
|
||||
int mdivk; /* 28 for 64khz, 114 for 15khz */
|
||||
|
||||
/* Main switches */
|
||||
int selpoly9;
|
||||
int c0_hf;
|
||||
int c1_f0;
|
||||
int c2_hf;
|
||||
int c3_f2;
|
||||
|
||||
/* SKCTL for two-tone mode */
|
||||
int skctl;
|
||||
|
||||
/* Main output state */
|
||||
qev_t outvol_all;
|
||||
int forcero; /* Force readout */
|
||||
|
||||
/* channel 0 state */
|
||||
|
||||
readout_t readout_0;
|
||||
event_t event_0;
|
||||
|
||||
int c0divpos;
|
||||
int c0divstart; /* AUDF0 recalculated */
|
||||
int c0divstart_p; /* start value when c1_f0 */
|
||||
int c0diva; /* AUDF0 register */
|
||||
|
||||
int c0t1; /* D - 5bit, Q goes to sw3 */
|
||||
int c0t2; /* D - out sw2, Q goes to sw4 and t3 */
|
||||
int c0t3; /* D - out t2, q goes to xor */
|
||||
|
||||
int c0sw1; /* in1 - 4bit, in2 - 17bit, out goes to sw2 */
|
||||
int c0sw2; /* in1 - /Q t2, in2 - out sw1, out goes to t2 */
|
||||
int c0sw3; /* in1 - +5, in2 - Q t1, out goes to C t2 */
|
||||
int c0sw4; /* hi-pass sw */
|
||||
int c0vo; /* volume only */
|
||||
|
||||
#ifndef NONLINEAR_MIXING
|
||||
int c0stop; /* channel counter stopped */
|
||||
#endif
|
||||
|
||||
int vol0;
|
||||
|
||||
int outvol_0;
|
||||
|
||||
/* channel 1 state */
|
||||
|
||||
readout_t readout_1;
|
||||
event_t event_1;
|
||||
|
||||
int c1divpos;
|
||||
int c1divstart;
|
||||
int c1diva;
|
||||
|
||||
int c1t1;
|
||||
int c1t2;
|
||||
int c1t3;
|
||||
|
||||
int c1sw1;
|
||||
int c1sw2;
|
||||
int c1sw3;
|
||||
int c1sw4;
|
||||
int c1vo;
|
||||
|
||||
#ifndef NONLINEAR_MIXING
|
||||
int c1stop; /* channel counter stopped */
|
||||
#endif
|
||||
|
||||
int vol1;
|
||||
|
||||
int outvol_1;
|
||||
|
||||
/* channel 2 state */
|
||||
|
||||
readout_t readout_2;
|
||||
event_t event_2;
|
||||
|
||||
int c2divpos;
|
||||
int c2divstart;
|
||||
int c2divstart_p; /* start value when c1_f0 */
|
||||
int c2diva;
|
||||
|
||||
int c2t1;
|
||||
int c2t2;
|
||||
|
||||
int c2sw1;
|
||||
int c2sw2;
|
||||
int c2sw3;
|
||||
int c2vo;
|
||||
|
||||
#ifndef NONLINEAR_MIXING
|
||||
int c2stop; /* channel counter stopped */
|
||||
#endif
|
||||
|
||||
int vol2;
|
||||
|
||||
int outvol_2;
|
||||
|
||||
/* channel 3 state */
|
||||
|
||||
readout_t readout_3;
|
||||
event_t event_3;
|
||||
|
||||
int c3divpos;
|
||||
int c3divstart;
|
||||
int c3diva;
|
||||
|
||||
int c3t1;
|
||||
int c3t2;
|
||||
|
||||
int c3sw1;
|
||||
int c3sw2;
|
||||
int c3sw3;
|
||||
int c3vo;
|
||||
|
||||
#ifndef NONLINEAR_MIXING
|
||||
int c3stop; /* channel counter stopped */
|
||||
#endif
|
||||
|
||||
int vol3;
|
||||
|
||||
int outvol_3;
|
||||
} PokeyState;
|
||||
|
||||
void mzpokeysnd_process_16(PokeyState* ps, void* sndbuffer, int sndn);
|
||||
void Update_pokey_sound_mz(PokeyState* ps, unsigned short addr, unsigned char val, unsigned char gain);
|
||||
|
||||
void ResetPokeyState(PokeyState* ps);
|
||||
|
||||
int MZPOKEYSND_Init(PokeyState* ps);
|
||||
|
||||
#endif /* MZPOKEYSND_H_ */
|
78
src/engine/platform/sound/pokey/pokey.h
Normal file
78
src/engine/platform/sound/pokey/pokey.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
#ifndef POKEY_H_
|
||||
#define POKEY_H_
|
||||
|
||||
#define POKEY_OFFSET_AUDF1 0x00
|
||||
#define POKEY_OFFSET_AUDC1 0x01
|
||||
#define POKEY_OFFSET_AUDF2 0x02
|
||||
#define POKEY_OFFSET_AUDC2 0x03
|
||||
#define POKEY_OFFSET_AUDF3 0x04
|
||||
#define POKEY_OFFSET_AUDC3 0x05
|
||||
#define POKEY_OFFSET_AUDF4 0x06
|
||||
#define POKEY_OFFSET_AUDC4 0x07
|
||||
#define POKEY_OFFSET_AUDCTL 0x08
|
||||
#define POKEY_OFFSET_STIMER 0x09
|
||||
#define POKEY_OFFSET_SKRES 0x0a
|
||||
#define POKEY_OFFSET_POTGO 0x0b
|
||||
#define POKEY_OFFSET_SEROUT 0x0d
|
||||
#define POKEY_OFFSET_IRQEN 0x0e
|
||||
#define POKEY_OFFSET_SKCTL 0x0f
|
||||
|
||||
#define POKEY_OFFSET_POT0 0x00
|
||||
#define POKEY_OFFSET_POT1 0x01
|
||||
#define POKEY_OFFSET_POT2 0x02
|
||||
#define POKEY_OFFSET_POT3 0x03
|
||||
#define POKEY_OFFSET_POT4 0x04
|
||||
#define POKEY_OFFSET_POT5 0x05
|
||||
#define POKEY_OFFSET_POT6 0x06
|
||||
#define POKEY_OFFSET_POT7 0x07
|
||||
#define POKEY_OFFSET_ALLPOT 0x08
|
||||
#define POKEY_OFFSET_KBCODE 0x09
|
||||
#define POKEY_OFFSET_RANDOM 0x0a
|
||||
#define POKEY_OFFSET_SERIN 0x0d
|
||||
#define POKEY_OFFSET_IRQST 0x0e
|
||||
#define POKEY_OFFSET_SKSTAT 0x0f
|
||||
|
||||
/* CONSTANT DEFINITIONS */
|
||||
|
||||
/* definitions for AUDCx (D201, D203, D205, D207) */
|
||||
#define POKEY_NOTPOLY5 0x80 /* selects POLY5 or direct CLOCK */
|
||||
#define POKEY_POLY4 0x40 /* selects POLY4 or POLY17 */
|
||||
#define POKEY_PURETONE 0x20 /* selects POLY4/17 or PURE tone */
|
||||
#define POKEY_VOL_ONLY 0x10 /* selects VOLUME OUTPUT ONLY */
|
||||
#define POKEY_VOLUME_MASK 0x0f /* volume mask */
|
||||
|
||||
/* definitions for AUDCTL (D208) */
|
||||
#define POKEY_POLY9 0x80 /* selects POLY9 or POLY17 */
|
||||
#define POKEY_CH1_179 0x40 /* selects 1.78979 MHz for Ch 1 */
|
||||
#define POKEY_CH3_179 0x20 /* selects 1.78979 MHz for Ch 3 */
|
||||
#define POKEY_CH1_CH2 0x10 /* clocks channel 1 w/channel 2 */
|
||||
#define POKEY_CH3_CH4 0x08 /* clocks channel 3 w/channel 4 */
|
||||
#define POKEY_CH1_FILTER 0x04 /* selects channel 1 high pass filter */
|
||||
#define POKEY_CH2_FILTER 0x02 /* selects channel 2 high pass filter */
|
||||
#define POKEY_CLOCK_15 0x01 /* selects 15.6999kHz or 63.9210kHz */
|
||||
|
||||
/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of
|
||||
the 1.79MHz clock */
|
||||
#define POKEY_DIV_64 28 /* divisor for 1.79MHz clock to 64 kHz */
|
||||
#define POKEY_DIV_15 114 /* divisor for 1.79MHz clock to 15 kHz */
|
||||
|
||||
/* the size (in entries) of the 4 polynomial tables */
|
||||
#define POKEY_POLY4_SIZE 0x000f
|
||||
#define POKEY_POLY5_SIZE 0x001f
|
||||
#define POKEY_POLY9_SIZE 0x01ff
|
||||
#define POKEY_POLY17_SIZE 0x0001ffff
|
||||
|
||||
#define POKEY_MAXPOKEYS 2 /* max number of emulated chips */
|
||||
|
||||
/* channel/chip definitions */
|
||||
#define POKEY_CHAN1 0
|
||||
#define POKEY_CHAN2 1
|
||||
#define POKEY_CHAN3 2
|
||||
#define POKEY_CHAN4 3
|
||||
#define POKEY_CHIP1 0
|
||||
#define POKEY_CHIP2 4
|
||||
#define POKEY_CHIP3 8
|
||||
#define POKEY_CHIP4 12
|
||||
#define POKEY_SAMPLE 127
|
||||
|
||||
#endif /* POKEY_H_ */
|
|
@ -306,9 +306,9 @@ void DivPlatformTX81Z::tick(bool sysTick) {
|
|||
chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
if (!parent->song.oldArpStrategy) {
|
||||
if (chan[i].fixedArp) {
|
||||
chan[i].freq=(chan[i].baseNoteOverride<<7)+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2;
|
||||
} else {
|
||||
chan[i].freq+=chan[i].arpOff<<7;
|
||||
chan[i].freq+=chan[i].arpOff<<6;
|
||||
}
|
||||
}
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
|
|
|
@ -41,15 +41,30 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
||||
if (opChan[ch].insChanged) {
|
||||
chan[2].state.alg=ins->fm.alg;
|
||||
chan[2].state.fb=ins->fm.fb;
|
||||
chan[2].state.op[ordch]=ins->fm.op[ordch];
|
||||
}
|
||||
|
||||
if (noExtMacros) {
|
||||
opChan[ch].macroInit(NULL);
|
||||
} else {
|
||||
opChan[ch].macroInit(ins);
|
||||
}
|
||||
if (!opChan[ch].std.vol.will) {
|
||||
opChan[ch].outVol=opChan[ch].vol;
|
||||
}
|
||||
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
|
||||
// TODO: how does this work?!
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (opChan[ch].insChanged) {
|
||||
|
@ -62,13 +77,14 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].mask=op.enable;
|
||||
}
|
||||
if (opChan[ch].insChanged) { // TODO how does this work?
|
||||
rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||
rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
|
||||
}
|
||||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].note=c.value;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
opChan[ch].keyOn=true;
|
||||
|
@ -80,15 +96,28 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].keyOff=true;
|
||||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
opChan[ch].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (!opChan[ch].std.vol.has) {
|
||||
opChan[ch].outVol=c.value;
|
||||
}
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -144,29 +173,28 @@ int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
|||
immWrite(0x27,extMode?0x40:0);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
chan[2].state.fb=c.value&7;
|
||||
rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
case DIV_CMD_FM_TL: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (isOutput[ins->fm.alg][c.value]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,c.value)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,c.value2);
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -360,6 +388,92 @@ void DivPlatformYM2203Ext::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
|
||||
opChan[i].std.next();
|
||||
|
||||
if (opChan[i].std.vol.had) {
|
||||
opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
|
||||
if (opChan[i].std.arp.had) {
|
||||
if (!opChan[i].inPorta) {
|
||||
opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (opChan[i].std.pitch.had) {
|
||||
if (opChan[i].std.pitch.mode) {
|
||||
opChan[i].pitch2+=opChan[i].std.pitch.val;
|
||||
CLAMP_VAR(opChan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
opChan[i].pitch2=opChan[i].std.pitch.val;
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
|
||||
if (m.am.had) {
|
||||
op.am=m.am.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.ar.had) {
|
||||
op.ar=m.ar.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dr.had) {
|
||||
op.dr=m.dr.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.mult.had) {
|
||||
op.mult=m.mult.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.rr.had) {
|
||||
op.rr=m.rr.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.sl.had) {
|
||||
op.sl=m.sl.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (m.rs.had) {
|
||||
op.rs=m.rs.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dt.had) {
|
||||
op.dt=m.dt.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.d2r.had) {
|
||||
op.d2r=m.d2r.val;
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
if (m.ssg.had) {
|
||||
op.ssgEnv=m.ssg.val;
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DivPlatformYM2203::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
|
@ -411,15 +525,17 @@ void DivPlatformYM2203Ext::muteChannel(int ch, bool mute) {
|
|||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[ins->fm.alg][ordch]) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,ordch)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -428,13 +544,23 @@ void DivPlatformYM2203Ext::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
} else {
|
||||
if (isMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
if (KVS(i,j)) {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,chan[i].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,op.tl);
|
||||
}
|
||||
}
|
||||
}
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
|
@ -475,7 +601,7 @@ void* DivPlatformYM2203Ext::getChanState(int ch) {
|
|||
|
||||
DivMacroInt* DivPlatformYM2203Ext::getChanMacroInt(int ch) {
|
||||
if (ch>=6) return ay->getChanMacroInt(ch-6);
|
||||
if (ch>=2) return NULL; // currently not implemented
|
||||
if (ch>=2) return &opChan[ch-2].std;
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
@ -490,7 +616,9 @@ void DivPlatformYM2203Ext::reset() {
|
|||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformOPN::OPNOpChannel();
|
||||
opChan[i].std.setEngine(parent);
|
||||
opChan[i].vol=127;
|
||||
opChan[i].outVol=127;
|
||||
}
|
||||
|
||||
// channel 2 mode
|
||||
|
|
|
@ -950,7 +950,9 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
if (c.chan>=6) break;
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -1200,6 +1202,7 @@ void DivPlatformYM2608::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=9; i<16; i++) {
|
||||
chan[i].insChanged=true;
|
||||
if (i>14) { // ADPCM-B
|
||||
|
@ -1276,6 +1279,7 @@ void DivPlatformYM2608::reset() {
|
|||
}
|
||||
|
||||
lastBusy=60;
|
||||
lfoValue=8;
|
||||
sampleBank=0;
|
||||
writeRSSOff=0;
|
||||
writeRSSOn=0;
|
||||
|
@ -1286,7 +1290,7 @@ void DivPlatformYM2608::reset() {
|
|||
extMode=false;
|
||||
|
||||
// LFO
|
||||
immWrite(0x22,0x08);
|
||||
immWrite(0x22,lfoValue);
|
||||
|
||||
// PCM volume
|
||||
immWrite(0x11,globalRSSVolume); // A
|
||||
|
|
|
@ -41,15 +41,32 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
||||
if (opChan[ch].insChanged) {
|
||||
chan[2].state.alg=ins->fm.alg;
|
||||
chan[2].state.fb=ins->fm.fb;
|
||||
chan[2].state.fms=ins->fm.fms;
|
||||
chan[2].state.ams=ins->fm.ams;
|
||||
chan[2].state.op[ordch]=ins->fm.op[ordch];
|
||||
}
|
||||
|
||||
if (noExtMacros) {
|
||||
opChan[ch].macroInit(NULL);
|
||||
} else {
|
||||
opChan[ch].macroInit(ins);
|
||||
}
|
||||
if (!opChan[ch].std.vol.will) {
|
||||
opChan[ch].outVol=opChan[ch].vol;
|
||||
}
|
||||
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
|
||||
// TODO: how does this work?!
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (opChan[ch].insChanged) {
|
||||
|
@ -62,14 +79,15 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].mask=op.enable;
|
||||
}
|
||||
if (opChan[ch].insChanged) { // TODO how does this work?
|
||||
rWrite(chanOffs[2]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[2]+0xb0,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
}
|
||||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].note=c.value;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
opChan[ch].keyOn=true;
|
||||
|
@ -81,15 +99,28 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].keyOff=true;
|
||||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
opChan[ch].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (!opChan[ch].std.vol.has) {
|
||||
opChan[ch].outVol=c.value;
|
||||
}
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -109,14 +140,13 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
} else {
|
||||
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (parent->song.sharedExtStat) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (ch==i) continue;
|
||||
opChan[i].pan=opChan[ch].pan;
|
||||
}
|
||||
}
|
||||
rWrite(chanOffs[2]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
@ -162,7 +192,8 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -170,20 +201,23 @@ int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
|||
rWrite(chanOffs[2]+ADDR_FB_ALG,(chan[2].state.alg&7)|(chan[2].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
case DIV_CMD_FM_TL: {
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (isOutput[ins->fm.alg][c.value]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,c.value)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,c.value2);
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -377,6 +411,91 @@ void DivPlatformYM2608Ext::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
|
||||
opChan[i].std.next();
|
||||
|
||||
if (opChan[i].std.vol.had) {
|
||||
opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
|
||||
if (opChan[i].std.arp.had) {
|
||||
if (!opChan[i].inPorta) {
|
||||
opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (opChan[i].std.pitch.had) {
|
||||
if (opChan[i].std.pitch.mode) {
|
||||
opChan[i].pitch2+=opChan[i].std.pitch.val;
|
||||
CLAMP_VAR(opChan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
opChan[i].pitch2=opChan[i].std.pitch.val;
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[2].state.op[orderedOps[i]];
|
||||
DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
|
||||
if (m.am.had) {
|
||||
op.am=m.am.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.ar.had) {
|
||||
op.ar=m.ar.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dr.had) {
|
||||
op.dr=m.dr.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.mult.had) {
|
||||
op.mult=m.mult.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.rr.had) {
|
||||
op.rr=m.rr.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.sl.had) {
|
||||
op.sl=m.sl.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (m.rs.had) {
|
||||
op.rs=m.rs.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dt.had) {
|
||||
op.dt=m.dt.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.d2r.had) {
|
||||
op.d2r=m.d2r.val;
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
if (m.ssg.had) {
|
||||
op.ssgEnv=m.ssg.val;
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2608::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
|
@ -428,16 +547,20 @@ void DivPlatformYM2608Ext::muteChannel(int ch, bool mute) {
|
|||
isOpMuted[ch-2]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-2];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch-2].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[2]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator op=chan[2].state.op[ordch];
|
||||
if (isOpMuted[ch-2]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[ins->fm.alg][ordch]) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].vol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,ordch)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-2].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
||||
rWrite(chanOffs[2]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-2].pan<<6))|(chan[2].state.fms&7)|((chan[2].state.ams&3)<<4));
|
||||
}
|
||||
|
||||
void DivPlatformYM2608Ext::forceIns() {
|
||||
|
@ -445,11 +568,11 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==2) { // extended channel
|
||||
if (i==2 && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
@ -472,7 +595,11 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (i==2) {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[0].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
} else {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
}
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -493,6 +620,7 @@ void DivPlatformYM2608Ext::forceIns() {
|
|||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
if (opChan[i].active) {
|
||||
|
@ -511,7 +639,7 @@ void* DivPlatformYM2608Ext::getChanState(int ch) {
|
|||
DivMacroInt* DivPlatformYM2608Ext::getChanMacroInt(int ch) {
|
||||
if (ch>=9 && ch<12) return ay->getChanMacroInt(ch-9);
|
||||
if (ch>=6) return &chan[ch-3].std;
|
||||
if (ch>=2) return NULL; // currently not implemented
|
||||
if (ch>=2) return &opChan[ch-2].std;
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
@ -526,7 +654,9 @@ void DivPlatformYM2608Ext::reset() {
|
|||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformOPN::OPNOpChannelStereo();
|
||||
opChan[i].std.setEngine(parent);
|
||||
opChan[i].vol=127;
|
||||
opChan[i].outVol=127;
|
||||
}
|
||||
|
||||
// channel 2 mode
|
||||
|
|
|
@ -930,7 +930,9 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -1173,6 +1175,7 @@ void DivPlatformYM2610::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=adpcmAChanOffs; i<=adpcmBChanOffs; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
@ -1247,6 +1250,7 @@ void DivPlatformYM2610::reset() {
|
|||
}
|
||||
|
||||
lastBusy=60;
|
||||
lfoValue=8;
|
||||
sampleBank=0;
|
||||
DivPlatformYM2610Base::reset();
|
||||
|
||||
|
@ -1255,7 +1259,7 @@ void DivPlatformYM2610::reset() {
|
|||
extMode=false;
|
||||
|
||||
// LFO
|
||||
immWrite(0x22,0x08);
|
||||
immWrite(0x22,lfoValue);
|
||||
|
||||
// PCM volume
|
||||
immWrite(0x101,globalADPCMAVolume); // A
|
||||
|
|
|
@ -993,7 +993,9 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -1236,6 +1238,7 @@ void DivPlatformYM2610B::forceIns() {
|
|||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=adpcmAChanOffs; i<=adpcmBChanOffs; i++) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
|
@ -1310,6 +1313,7 @@ void DivPlatformYM2610B::reset() {
|
|||
}
|
||||
|
||||
lastBusy=60;
|
||||
lfoValue=8;
|
||||
sampleBank=0;
|
||||
DivPlatformYM2610Base::reset();
|
||||
|
||||
|
@ -1318,7 +1322,7 @@ void DivPlatformYM2610B::reset() {
|
|||
extMode=false;
|
||||
|
||||
// LFO
|
||||
immWrite(0x22,0x08);
|
||||
immWrite(0x22,lfoValue);
|
||||
|
||||
// PCM volume
|
||||
immWrite(0x101,0x3f); // A
|
||||
|
|
|
@ -37,15 +37,32 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
||||
if (opChan[ch].insChanged) {
|
||||
chan[extChanOffs].state.alg=ins->fm.alg;
|
||||
chan[extChanOffs].state.fb=ins->fm.fb;
|
||||
chan[extChanOffs].state.fms=ins->fm.fms;
|
||||
chan[extChanOffs].state.ams=ins->fm.ams;
|
||||
chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch];
|
||||
}
|
||||
|
||||
if (noExtMacros) {
|
||||
opChan[ch].macroInit(NULL);
|
||||
} else {
|
||||
opChan[ch].macroInit(ins);
|
||||
}
|
||||
if (!opChan[ch].std.vol.will) {
|
||||
opChan[ch].outVol=opChan[ch].vol;
|
||||
}
|
||||
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch];
|
||||
// TODO: how does this work?!
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (opChan[ch].insChanged) {
|
||||
|
@ -58,14 +75,15 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
opChan[ch].mask=op.enable;
|
||||
}
|
||||
if (opChan[ch].insChanged) { // TODO how does this work?
|
||||
rWrite(chanOffs[extChanOffs]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].note=c.value;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
opChan[ch].keyOn=true;
|
||||
|
@ -77,15 +95,28 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].keyOff=true;
|
||||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
opChan[ch].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (!opChan[ch].std.vol.has) {
|
||||
opChan[ch].outVol=c.value;
|
||||
}
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -105,14 +136,13 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
} else {
|
||||
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (parent->song.sharedExtStat) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (ch==i) continue;
|
||||
opChan[i].pan=opChan[ch].pan;
|
||||
}
|
||||
}
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
@ -158,7 +188,8 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -166,20 +197,23 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
|||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
case DIV_CMD_FM_TL: {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (isOutput[ins->fm.alg][c.value]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,c.value)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,c.value2);
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -373,6 +407,91 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
|
||||
opChan[i].std.next();
|
||||
|
||||
if (opChan[i].std.vol.had) {
|
||||
opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
|
||||
if (opChan[i].std.arp.had) {
|
||||
if (!opChan[i].inPorta) {
|
||||
opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (opChan[i].std.pitch.had) {
|
||||
if (opChan[i].std.pitch.mode) {
|
||||
opChan[i].pitch2+=opChan[i].std.pitch.val;
|
||||
CLAMP_VAR(opChan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
opChan[i].pitch2=opChan[i].std.pitch.val;
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
|
||||
if (m.am.had) {
|
||||
op.am=m.am.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.ar.had) {
|
||||
op.ar=m.ar.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dr.had) {
|
||||
op.dr=m.dr.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.mult.had) {
|
||||
op.mult=m.mult.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.rr.had) {
|
||||
op.rr=m.rr.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.sl.had) {
|
||||
op.sl=m.sl.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (m.rs.had) {
|
||||
op.rs=m.rs.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dt.had) {
|
||||
op.dt=m.dt.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.d2r.had) {
|
||||
op.d2r=m.d2r.val;
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
if (m.ssg.had) {
|
||||
op.ssgEnv=m.ssg.val;
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2610B::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
|
@ -424,16 +543,20 @@ void DivPlatformYM2610BExt::muteChannel(int ch, bool mute) {
|
|||
isOpMuted[ch-extChanOffs]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch-extChanOffs].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[ins->fm.alg][ordch]) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].vol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,ordch)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
|
||||
void DivPlatformYM2610BExt::forceIns() {
|
||||
|
@ -441,11 +564,11 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==2 && extMode) { // extended channel
|
||||
if (i==extChanOffs && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
@ -468,7 +591,11 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (i==extChanOffs) {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[0].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
} else {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
}
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -484,6 +611,7 @@ void DivPlatformYM2610BExt::forceIns() {
|
|||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
if (opChan[i].active) {
|
||||
|
@ -502,7 +630,7 @@ void* DivPlatformYM2610BExt::getChanState(int ch) {
|
|||
DivMacroInt* DivPlatformYM2610BExt::getChanMacroInt(int ch) {
|
||||
if (ch>=(psgChanOffs+3) && ch<(adpcmAChanOffs+3)) return ay->getChanMacroInt(ch-psgChanOffs-3);
|
||||
if (ch>=(extChanOffs+4)) return &chan[ch-3].std;
|
||||
if (ch>=extChanOffs) return NULL; // currently not implemented
|
||||
if (ch>=extChanOffs) return &opChan[ch-extChanOffs].std;
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
@ -517,7 +645,9 @@ void DivPlatformYM2610BExt::reset() {
|
|||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformOPN::OPNOpChannelStereo();
|
||||
opChan[i].std.setEngine(parent);
|
||||
opChan[i].vol=127;
|
||||
opChan[i].outVol=127;
|
||||
}
|
||||
|
||||
// channel 2 mode
|
||||
|
|
|
@ -37,15 +37,32 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
|
||||
if (opChan[ch].insChanged) {
|
||||
chan[extChanOffs].state.alg=ins->fm.alg;
|
||||
chan[extChanOffs].state.fb=ins->fm.fb;
|
||||
chan[extChanOffs].state.fms=ins->fm.fms;
|
||||
chan[extChanOffs].state.ams=ins->fm.ams;
|
||||
chan[extChanOffs].state.op[ordch]=ins->fm.op[ordch];
|
||||
}
|
||||
|
||||
if (noExtMacros) {
|
||||
opChan[ch].macroInit(NULL);
|
||||
} else {
|
||||
opChan[ch].macroInit(ins);
|
||||
}
|
||||
if (!opChan[ch].std.vol.will) {
|
||||
opChan[ch].outVol=opChan[ch].vol;
|
||||
}
|
||||
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch];
|
||||
// TODO: how does this work?!
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
if (opChan[ch].insChanged) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (opChan[ch].insChanged) {
|
||||
|
@ -58,14 +75,15 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].mask=op.enable;
|
||||
}
|
||||
if (opChan[ch].insChanged) { // TODO how does this work?
|
||||
rWrite(chanOffs[extChanOffs]+0xb0,(ins->fm.alg&7)|(ins->fm.fb<<3));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb0,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
opChan[ch].insChanged=false;
|
||||
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
|
||||
opChan[ch].portaPause=false;
|
||||
opChan[ch].note=c.value;
|
||||
opChan[ch].freqChanged=true;
|
||||
}
|
||||
opChan[ch].keyOn=true;
|
||||
|
@ -77,15 +95,28 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].keyOff=true;
|
||||
opChan[ch].keyOn=false;
|
||||
opChan[ch].active=false;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
if (noExtMacros) break;
|
||||
opChan[ch].std.release();
|
||||
break;
|
||||
case DIV_CMD_VOLUME: {
|
||||
opChan[ch].vol=c.value;
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (!opChan[ch].std.vol.has) {
|
||||
opChan[ch].outVol=c.value;
|
||||
}
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -105,14 +136,13 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
} else {
|
||||
opChan[ch].pan=(c.value2>0)|((c.value>0)<<1);
|
||||
}
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (parent->song.sharedExtStat) {
|
||||
for (int i=0; i<4; i++) {
|
||||
if (ch==i) continue;
|
||||
opChan[i].pan=opChan[ch].pan;
|
||||
}
|
||||
}
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(opChan[ch].pan<<6)|(ins->fm.fms&7)|((ins->fm.ams&3)<<4));
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PITCH: {
|
||||
|
@ -158,7 +188,8 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_FM_LFO: {
|
||||
rWrite(0x22,(c.value&7)|((c.value>>4)<<3));
|
||||
lfoValue=(c.value&7)|((c.value>>4)<<3);
|
||||
rWrite(0x22,lfoValue);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_FB: {
|
||||
|
@ -166,20 +197,23 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
|||
rWrite(chanOffs[extChanOffs]+ADDR_FB_ALG,(chan[extChanOffs].state.alg&7)|(chan[extChanOffs].state.fb<<3));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_MULT: { // TODO
|
||||
case DIV_CMD_FM_MULT: {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
DivInstrumentFM::Operator op=ins->fm.op[orderedOps[c.value]];
|
||||
rWrite(baseAddr+0x30,(c.value2&15)|(dtTable[op.dt&7]<<4));
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
|
||||
op.mult=c.value2&15;
|
||||
rWrite(baseAddr+0x30,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_TL: { // TODO
|
||||
case DIV_CMD_FM_TL: {
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[c.value]];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
if (isOutput[ins->fm.alg][c.value]) {
|
||||
rWrite(baseAddr+0x40,127-(((127-c.value2)*(opChan[ch].vol&0x7f))/127));
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[c.value]];
|
||||
op.tl=c.value2;
|
||||
if (isOpMuted[ch]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,c.value)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,c.value2);
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -373,6 +407,91 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
|
||||
if (extMode && !noExtMacros) for (int i=0; i<4; i++) {
|
||||
opChan[i].std.next();
|
||||
|
||||
if (opChan[i].std.vol.had) {
|
||||
opChan[i].outVol=VOL_SCALE_LOG_BROKEN(opChan[i].vol,MIN(127,opChan[i].std.vol.val),127);
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
|
||||
if (opChan[i].std.arp.had) {
|
||||
if (!opChan[i].inPorta) {
|
||||
opChan[i].baseFreq=NOTE_FNUM_BLOCK(parent->calcArp(opChan[i].note,opChan[i].std.arp.val),11);
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
if (opChan[i].std.pitch.had) {
|
||||
if (opChan[i].std.pitch.mode) {
|
||||
opChan[i].pitch2+=opChan[i].std.pitch.val;
|
||||
CLAMP_VAR(opChan[i].pitch2,-32768,32767);
|
||||
} else {
|
||||
opChan[i].pitch2=opChan[i].std.pitch.val;
|
||||
}
|
||||
opChan[i].freqChanged=true;
|
||||
}
|
||||
|
||||
// param macros
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[orderedOps[i]];
|
||||
DivInstrumentFM::Operator& op=chan[extChanOffs].state.op[orderedOps[i]];
|
||||
DivMacroInt::IntOp& m=opChan[i].std.op[orderedOps[i]];
|
||||
if (m.am.had) {
|
||||
op.am=m.am.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.ar.had) {
|
||||
op.ar=m.ar.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dr.had) {
|
||||
op.dr=m.dr.val;
|
||||
rWrite(baseAddr+ADDR_AM_DR,(op.dr&31)|(op.am<<7));
|
||||
}
|
||||
if (m.mult.had) {
|
||||
op.mult=m.mult.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.rr.had) {
|
||||
op.rr=m.rr.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.sl.had) {
|
||||
op.sl=m.sl.val;
|
||||
rWrite(baseAddr+ADDR_SL_RR,(op.rr&15)|(op.sl<<4));
|
||||
}
|
||||
if (m.tl.had) {
|
||||
op.tl=127-m.tl.val;
|
||||
if (isOpMuted[i]) {
|
||||
rWrite(baseAddr+ADDR_TL,127);
|
||||
} else {
|
||||
rWrite(baseAddr+ADDR_TL,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[i].outVol&0x7f,127));
|
||||
}
|
||||
}
|
||||
if (m.rs.had) {
|
||||
op.rs=m.rs.val;
|
||||
rWrite(baseAddr+ADDR_RS_AR,(op.ar&31)|(op.rs<<6));
|
||||
}
|
||||
if (m.dt.had) {
|
||||
op.dt=m.dt.val;
|
||||
rWrite(baseAddr+ADDR_MULT_DT,(op.mult&15)|(dtTable[op.dt&7]<<4));
|
||||
}
|
||||
if (m.d2r.had) {
|
||||
op.d2r=m.d2r.val;
|
||||
rWrite(baseAddr+ADDR_DT2_D2R,op.d2r&31);
|
||||
}
|
||||
if (m.ssg.had) {
|
||||
op.ssgEnv=m.ssg.val;
|
||||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformYM2610::tick(sysTick);
|
||||
|
||||
bool writeNoteOn=false;
|
||||
|
@ -424,16 +543,20 @@ void DivPlatformYM2610Ext::muteChannel(int ch, bool mute) {
|
|||
isOpMuted[ch-extChanOffs]=mute;
|
||||
|
||||
int ordch=orderedOps[ch-extChanOffs];
|
||||
DivInstrument* ins=parent->getIns(opChan[ch].ins,DIV_INS_FM);
|
||||
unsigned short baseAddr=chanOffs[extChanOffs]|opOffs[ordch];
|
||||
DivInstrumentFM::Operator op=ins->fm.op[ordch];
|
||||
if (isOpMuted[ch]) {
|
||||
DivInstrumentFM::Operator op=chan[extChanOffs].state.op[ordch];
|
||||
if (isOpMuted[ch-extChanOffs]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (isOutput[ins->fm.alg][ordch]) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].vol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(2,ordch)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
immWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[ch-extChanOffs].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
immWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
||||
rWrite(chanOffs[extChanOffs]+0xb4,(IS_EXTCH_MUTED?0:(opChan[ch-extChanOffs].pan<<6))|(chan[extChanOffs].state.fms&7)|((chan[extChanOffs].state.ams&3)<<4));
|
||||
}
|
||||
|
||||
void DivPlatformYM2610Ext::forceIns() {
|
||||
|
@ -441,11 +564,11 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
for (int j=0; j<4; j++) {
|
||||
unsigned short baseAddr=chanOffs[i]|opOffs[j];
|
||||
DivInstrumentFM::Operator& op=chan[i].state.op[j];
|
||||
if (i==1 && extMode) { // extended channel
|
||||
if (i==extChanOffs && extMode) { // extended channel
|
||||
if (isOpMuted[j]) {
|
||||
rWrite(baseAddr+0x40,127);
|
||||
} else if (KVS(i,j)) {
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].vol&0x7f,127));
|
||||
rWrite(baseAddr+0x40,127-VOL_SCALE_LOG_BROKEN(127-op.tl,opChan[j].outVol&0x7f,127));
|
||||
} else {
|
||||
rWrite(baseAddr+0x40,op.tl);
|
||||
}
|
||||
|
@ -468,7 +591,11 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
rWrite(baseAddr+ADDR_SSG,op.ssgEnv&15);
|
||||
}
|
||||
rWrite(chanOffs[i]+ADDR_FB_ALG,(chan[i].state.alg&7)|(chan[i].state.fb<<3));
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
if (i==extChanOffs) {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(IS_EXTCH_MUTED?0:(opChan[0].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
} else {
|
||||
rWrite(chanOffs[i]+ADDR_LRAF,(isMuted[i]?0:(chan[i].pan<<6))|(chan[i].state.fms&7)|((chan[i].state.ams&3)<<4));
|
||||
}
|
||||
if (chan[i].active) {
|
||||
chan[i].keyOn=true;
|
||||
chan[i].freqChanged=true;
|
||||
|
@ -484,6 +611,7 @@ void DivPlatformYM2610Ext::forceIns() {
|
|||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
immWrite(0x22,lfoValue);
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i].insChanged=true;
|
||||
if (opChan[i].active) {
|
||||
|
@ -502,7 +630,7 @@ void* DivPlatformYM2610Ext::getChanState(int ch) {
|
|||
DivMacroInt* DivPlatformYM2610Ext::getChanMacroInt(int ch) {
|
||||
if (ch>=(psgChanOffs+3) && ch<(adpcmAChanOffs+3)) return ay->getChanMacroInt(ch-psgChanOffs-3);
|
||||
if (ch>=(extChanOffs+4)) return &chan[ch-3].std;
|
||||
if (ch>=extChanOffs) return NULL; // currently not implemented
|
||||
if (ch>=extChanOffs) return &opChan[ch-extChanOffs].std;
|
||||
return &chan[ch].std;
|
||||
}
|
||||
|
||||
|
@ -517,7 +645,9 @@ void DivPlatformYM2610Ext::reset() {
|
|||
|
||||
for (int i=0; i<4; i++) {
|
||||
opChan[i]=DivPlatformOPN::OPNOpChannelStereo();
|
||||
opChan[i].std.setEngine(parent);
|
||||
opChan[i].vol=127;
|
||||
opChan[i].outVol=127;
|
||||
}
|
||||
|
||||
// channel 2 mode
|
||||
|
|
|
@ -64,10 +64,10 @@ enum DivSystem {
|
|||
DIV_SYSTEM_FDS,
|
||||
DIV_SYSTEM_MMC5,
|
||||
DIV_SYSTEM_N163,
|
||||
DIV_SYSTEM_OPN,
|
||||
DIV_SYSTEM_OPN_EXT,
|
||||
DIV_SYSTEM_PC98,
|
||||
DIV_SYSTEM_PC98_EXT,
|
||||
DIV_SYSTEM_YM2203,
|
||||
DIV_SYSTEM_YM2203_EXT,
|
||||
DIV_SYSTEM_YM2608,
|
||||
DIV_SYSTEM_YM2608_EXT,
|
||||
DIV_SYSTEM_OPL,
|
||||
DIV_SYSTEM_OPL2,
|
||||
DIV_SYSTEM_OPL3,
|
||||
|
@ -111,8 +111,8 @@ enum DivSystem {
|
|||
DIV_SYSTEM_NAMCO,
|
||||
DIV_SYSTEM_NAMCO_15XX,
|
||||
DIV_SYSTEM_NAMCO_CUS30,
|
||||
DIV_SYSTEM_YM2612_FRAC,
|
||||
DIV_SYSTEM_YM2612_FRAC_EXT,
|
||||
DIV_SYSTEM_YM2612_DUALPCM,
|
||||
DIV_SYSTEM_YM2612_DUALPCM_EXT,
|
||||
DIV_SYSTEM_MSM5232,
|
||||
DIV_SYSTEM_T6W28,
|
||||
DIV_SYSTEM_K007232,
|
||||
|
@ -120,6 +120,11 @@ enum DivSystem {
|
|||
DIV_SYSTEM_PCM_DAC,
|
||||
DIV_SYSTEM_PONG,
|
||||
DIV_SYSTEM_DUMMY,
|
||||
DIV_SYSTEM_YM2612_CSM,
|
||||
DIV_SYSTEM_YM2610_CSM,
|
||||
DIV_SYSTEM_YM2610B_CSM,
|
||||
DIV_SYSTEM_YM2203_CSM,
|
||||
DIV_SYSTEM_YM2608_CSM,
|
||||
DIV_SYSTEM_MAX // boundary for max system number
|
||||
};
|
||||
|
||||
|
|
|
@ -1014,7 +1014,7 @@ void DivEngine::registerSystems() {
|
|||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_OPN]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2203]=new DivSysDef(
|
||||
"Yamaha YM2203 (OPN)", NULL, 0x8d, 0, 6, true, true, 0x151, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)",
|
||||
{"FM 1", "FM 2", "FM 3", "PSG 1", "PSG 2", "PSG 3"},
|
||||
|
@ -1026,7 +1026,7 @@ void DivEngine::registerSystems() {
|
|||
fmOPNPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_OPN_EXT]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2203_EXT]=new DivSysDef(
|
||||
"Yamaha YM2203 (OPN) Extended Channel 3", NULL, 0xb6, 0, 9, true, true, 0x151, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)\nthis one is in Extended Channel mode, which turns the third FM channel into four operators with independent notes/frequencies",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "PSG 1", "PSG 2", "PSG 3"},
|
||||
|
@ -1038,7 +1038,19 @@ void DivEngine::registerSystems() {
|
|||
fmOPNPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_PC98]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2203_CSM]=new DivSysDef(
|
||||
"Yamaha YM2203 (OPN) CSM", NULL, 0xc3, 0, 10, true, true, 0x151, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"cost-reduced version of the OPM with a different register layout and no stereo...\n...but it has a built-in AY-3-8910! (actually an YM2149)\nCSM blah blah",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "CSM Timer", "PSG 1", "PSG 2", "PSG 3"},
|
||||
{"F1", "F2", "O1", "O2", "O3", "O4", "CSM", "S1", "S2", "S3"},
|
||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
|
||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY},
|
||||
{},
|
||||
fmEffectHandlerMap,
|
||||
fmOPNPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2608]=new DivSysDef(
|
||||
"Yamaha YM2608 (OPNA)", NULL, 0x8e, 0, 16, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels.",
|
||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"},
|
||||
|
@ -1050,7 +1062,7 @@ void DivEngine::registerSystems() {
|
|||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_PC98_EXT]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2608_EXT]=new DivSysDef(
|
||||
"Yamaha YM2608 (OPNA) Extended Channel 3", NULL, 0xb7, 0, 19, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels.\nthis one is in Extended Channel mode, which turns the third FM channel into four operators with independent notes/frequencies",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"},
|
||||
|
@ -1062,6 +1074,18 @@ void DivEngine::registerSystems() {
|
|||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2608_CSM]=new DivSysDef(
|
||||
"Yamaha YM2608 (OPNA) CSM", NULL, 0xc4, 0, 20, true, true, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"OPN but twice the FM channels, stereo makes a come-back and has rhythm and ADPCM channels.\nCSM blah blah",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "CSM Timer", "Square 1", "Square 2", "Square 3", "Kick", "Snare", "Top", "HiHat", "Tom", "Rim", "ADPCM"},
|
||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "CSM", "S1", "S2", "S3", "BD", "SD", "TP", "HH", "TM", "RM", "P"},
|
||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_NOISE, DIV_CH_PCM},
|
||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB},
|
||||
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA},
|
||||
fmEffectHandlerMap,
|
||||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_OPL]=new DivSysDef(
|
||||
"Yamaha YM3526 (OPL)", NULL, 0x8f, 0, 9, true, false, 0x151, false, 0,
|
||||
"OPN, but what if you only had two operators, no stereo, no detune and a lower ADSR parameter range?",
|
||||
|
@ -1127,12 +1151,17 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_POKEY]=new DivSysDef(
|
||||
"POKEY", NULL, 0x94, 0, 4, false, true, 0, false, 0,
|
||||
"POKEY", NULL, 0x94, 0, 4, false, true, 0x161, false, 0,
|
||||
"TIA, but better and more flexible.\nused in the Atari 8-bit family of computers (400/800/XL/XE).",
|
||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4"},
|
||||
{"CH1", "CH2", "CH3", "CH4"},
|
||||
{DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE},
|
||||
{DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY}
|
||||
{DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY},
|
||||
{},
|
||||
{
|
||||
{0x10, {DIV_CMD_WAVE, "10xx: Set waveform (0 to 7)"}},
|
||||
{0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set AUDCTL"}},
|
||||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef(
|
||||
|
@ -1265,6 +1294,18 @@ void DivEngine::registerSystems() {
|
|||
fmOPN2PostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2612_CSM]=new DivSysDef(
|
||||
"Yamaha YM2612 (OPN2) CSM", NULL, 0xc1, 0, 10, true, false, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis one includes CSM mode control for special effects on Channel 3.",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "CSM Timer"},
|
||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "CSM"},
|
||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE},
|
||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM},
|
||||
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_NULL},
|
||||
fmOPN2EffectHandlerMap,
|
||||
fmOPN2PostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SCC]=new DivSysDef(
|
||||
"Konami SCC", NULL, 0xa1, 0, 5, false, true, 0x161, false, 0,
|
||||
"a wavetable chip made by Konami for use with the MSX.\nthe last channel shares its wavetable with the previous one though.",
|
||||
|
@ -1336,6 +1377,18 @@ void DivEngine::registerSystems() {
|
|||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2610_CSM]=new DivSysDef(
|
||||
"Yamaha YM2610 (OPNB) CSM", NULL, 0xc2, 0, 18, true, false, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_A)|(1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"this chip was used in SNK's Neo Geo arcade board and video game console.\nit's like OPNA but the rhythm channels are ADPCM channels and two FM channels went missing.\nthis one includes CSM mode control for special effects on Channel 2.",
|
||||
{"FM 1", "FM 2 OP1", "FM 2 OP2", "FM 2 OP3", "FM 2 OP4", "FM 3", "FM 4", "CSM Timer", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
|
||||
{"F1", "O1", "O2", "O3", "O4", "F3", "F4", "CSM", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
|
||||
{DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB},
|
||||
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmEffectHandlerMap,
|
||||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_OPLL_DRUMS]=new DivSysDef(
|
||||
"Yamaha YM2413 (OPLL) with drums", NULL, 0xa7, 0, 11, true, false, 0x150, false, 0,
|
||||
"the OPLL chips but with drums mode turned on.",
|
||||
|
@ -1413,6 +1466,18 @@ void DivEngine::registerSystems() {
|
|||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2610B_CSM]=new DivSysDef(
|
||||
"Yamaha YM2610B (OPNB2) CSM", NULL, 0xc5, 0, 20, true, false, 0x151, false, (1U<<DIV_SAMPLE_DEPTH_ADPCM_A)|(1U<<DIV_SAMPLE_DEPTH_ADPCM_B)|(1U<<DIV_SAMPLE_DEPTH_8BIT),
|
||||
"so Taito asked Yamaha if they could get the two missing FM channels back, and Yamaha gladly provided them with this chip.\nCSM blah blah",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6", "CSM Timer", "PSG 1", "PSG 2", "PSG 3", "ADPCM-A 1", "ADPCM-A 2", "ADPCM-A 3", "ADPCM-A 4", "ADPCM-A 5", "ADPCM-A 6", "ADPCM-B"},
|
||||
{"F1", "F2", "O1", "O2", "O3", "O4", "F4", "F5", "F6", "CSM", "S1", "S2", "S3", "P1", "P2", "P3", "P4", "P5", "P6", "B"},
|
||||
{DIV_CH_FM, DIV_CH_FM, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_OP, DIV_CH_FM, DIV_CH_FM, DIV_CH_FM, DIV_CH_NOISE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM, DIV_CH_PCM},
|
||||
{DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_FM, DIV_INS_AY, DIV_INS_AY, DIV_INS_AY, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMA, DIV_INS_ADPCMB},
|
||||
{DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_NULL, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA, DIV_INS_AMIGA},
|
||||
fmEffectHandlerMap,
|
||||
fmOPNAPostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_SEGAPCM_COMPAT]=new DivSysDef(
|
||||
"SegaPCM (compatible 5-channel mode)", NULL, 0xa9, 0, 5, false, true, 0x151, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this is the same thing as SegaPCM, but only exposes 5 of the channels for compatibility with DefleMask.",
|
||||
|
@ -1687,7 +1752,7 @@ void DivEngine::registerSystems() {
|
|||
}
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2612_FRAC]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2612_DUALPCM]=new DivSysDef(
|
||||
"Yamaha YM2612 (OPN2) with DualPCM", NULL, 0xbe, 0, 7, true, false, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.",
|
||||
{"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2"},
|
||||
|
@ -1699,7 +1764,7 @@ void DivEngine::registerSystems() {
|
|||
fmOPN2PostEffectHandlerMap
|
||||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_YM2612_FRAC_EXT]=new DivSysDef(
|
||||
sysDefs[DIV_SYSTEM_YM2612_DUALPCM_EXT]=new DivSysDef(
|
||||
"Yamaha YM2612 (OPN2) Extended Channel 3 with DualPCM and CSM", NULL, 0xbd, 0, 11, true, false, 0x150, false, 1U<<DIV_SAMPLE_DEPTH_8BIT,
|
||||
"this chip is mostly known for being in the Sega Genesis (but it also was on the FM Towns computer).\nthis system uses software mixing to provide two sample channels.\nthis one is in Extended Channel mode, which turns the third FM channel into four operators with independent notes/frequencies.",
|
||||
{"FM 1", "FM 2", "FM 3 OP1", "FM 3 OP2", "FM 3 OP3", "FM 3 OP4", "FM 4", "FM 5", "FM 6/PCM 1", "PCM 2", "CSM Timer"},
|
||||
|
|
|
@ -34,8 +34,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
switch (sys) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
for (int i=0; i<3; i++) { // set SL and RR to highest
|
||||
w->writeC(2|baseAddr1);
|
||||
w->writeC(0x80+i);
|
||||
|
@ -252,8 +252,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
for (int i=0; i<3; i++) { // set SL and RR to highest
|
||||
w->writeC(5|baseAddr1);
|
||||
w->writeC(0x80+i);
|
||||
|
@ -333,6 +333,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_POKEY:
|
||||
for (int i=0; i<9; i++) {
|
||||
w->writeC(0xbb);
|
||||
w->writeC(i|baseAddr2);
|
||||
w->writeC(0);
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_LYNX:
|
||||
w->writeC(0x4e);
|
||||
w->writeC(0x44);
|
||||
|
@ -600,8 +607,8 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
switch (sys) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
switch (write.addr>>8) {
|
||||
case 0: // port 0
|
||||
w->writeC(2|baseAddr1);
|
||||
|
@ -693,14 +700,14 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
break;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
w->writeC(5|baseAddr1);
|
||||
w->writeC(write.addr&0xff);
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT:
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
switch (write.addr>>8) {
|
||||
case 0: // port 0
|
||||
w->writeC(6|baseAddr1);
|
||||
|
@ -732,6 +739,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(baseAddr2|(write.addr&0xff));
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
case DIV_SYSTEM_POKEY:
|
||||
w->writeC(0xbb);
|
||||
w->writeC(baseAddr2|(write.addr&0x0f));
|
||||
w->writeC(write.val&0xff);
|
||||
break;
|
||||
case DIV_SYSTEM_LYNX:
|
||||
w->writeC(0x4e);
|
||||
w->writeC(write.addr&0xff);
|
||||
|
@ -1208,8 +1220,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
break;
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
if (!hasOPN2) {
|
||||
hasOPN2=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
|
@ -1232,8 +1244,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT:
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT:
|
||||
if (!hasOPN) {
|
||||
hasOPN=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
|
@ -1245,8 +1257,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT:
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT:
|
||||
if (!hasOPNA) {
|
||||
hasOPNA=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
|
@ -1286,6 +1298,17 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_POKEY:
|
||||
if (!hasPOKEY) {
|
||||
hasPOKEY=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
} else if (!(hasPOKEY&0x40000000)) {
|
||||
isSecond[i]=true;
|
||||
willExport[i]=true;
|
||||
hasPOKEY|=0x40000000;
|
||||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_LYNX:
|
||||
if (!hasLynx) {
|
||||
hasLynx=disCont[i].dispatch->chipClock;
|
||||
|
@ -1892,8 +1915,8 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version, bool p
|
|||
switch (song.system[i]) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT:
|
||||
w->writeC(0x90);
|
||||
w->writeC(streamID);
|
||||
w->writeC(0x02);
|
||||
|
|
|
@ -71,6 +71,7 @@ const char* aboutLine[]={
|
|||
"DeMOSic",
|
||||
"DevEd",
|
||||
"Dippy",
|
||||
"dumbut",
|
||||
"FΛDE",
|
||||
"Forte",
|
||||
"Fragmare",
|
||||
|
@ -78,6 +79,7 @@ const char* aboutLine[]={
|
|||
"iyatemu",
|
||||
"JayBOB18",
|
||||
"Jimmy-DS",
|
||||
"Kagamiin~",
|
||||
"kleeder",
|
||||
"jaezu",
|
||||
"Laggy",
|
||||
|
@ -152,6 +154,9 @@ const char* aboutLine[]={
|
|||
"QSound emulator by superctr and Valley Bell",
|
||||
"VICE VIC-20 sound core by Rami Rasanen and viznut",
|
||||
"VERA sound core by Frank van den Hoef",
|
||||
"mzpokeysnd POKEY emulator by Michael Borisov",
|
||||
"ASAP POKEY emulator by Piotr Fusik",
|
||||
"ported by laoo to C++",
|
||||
"K005289 emulator by cam900",
|
||||
"Namco 163 emulator by cam900",
|
||||
"Seta X1-010 emulator by cam900",
|
||||
|
|
|
@ -242,8 +242,8 @@ void putDispatchChip(void* data, int type) {
|
|||
switch (type) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT: {
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT: {
|
||||
GENESIS_CHIP_DEBUG;
|
||||
break;
|
||||
}
|
||||
|
@ -257,8 +257,8 @@ void putDispatchChip(void* data, int type) {
|
|||
SMS_CHIP_DEBUG;
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT: {
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT: {
|
||||
DivPlatformYM2203* ch=(DivPlatformYM2203*)data;
|
||||
ImGui::Text("> YM2203");
|
||||
FM_OPN_CHIP_DEBUG;
|
||||
|
@ -268,8 +268,8 @@ void putDispatchChip(void* data, int type) {
|
|||
ImGui::TextColored(ch->extMode?colorOn:colorOff,">> ExtMode");
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT: {
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT: {
|
||||
DivPlatformYM2608* ch=(DivPlatformYM2608*)data;
|
||||
ImGui::Text("> YM2608");
|
||||
FM_OPN_CHIP_DEBUG;
|
||||
|
@ -560,12 +560,12 @@ void putDispatchChan(void* data, int chanNum, int type) {
|
|||
break;
|
||||
}
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_FRAC: {
|
||||
case DIV_SYSTEM_YM2612_DUALPCM: {
|
||||
GENESIS_CHAN_DEBUG;
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT: {
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT: {
|
||||
if (chanNum>=2 && chanNum<=5) {
|
||||
DivPlatformOPN::OPNOpChannelStereo* ch=(DivPlatformOPN::OPNOpChannelStereo*)data;
|
||||
ImGui::Text("> YM2612 (per operator)");
|
||||
|
@ -579,11 +579,11 @@ void putDispatchChan(void* data, int chanNum, int type) {
|
|||
SMS_CHAN_DEBUG;
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_OPN: {
|
||||
case DIV_SYSTEM_YM2203: {
|
||||
OPN_CHAN_DEBUG;
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_OPN_EXT: {
|
||||
case DIV_SYSTEM_YM2203_EXT: {
|
||||
if (chanNum>=2 && chanNum<=5) {
|
||||
OPN_OPCHAN_DEBUG;
|
||||
} else {
|
||||
|
@ -591,13 +591,13 @@ void putDispatchChan(void* data, int chanNum, int type) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_PC98: {
|
||||
case DIV_SYSTEM_YM2608: {
|
||||
DivPlatformOPN::OPNChannelStereo* ch=(DivPlatformOPN::OPNChannelStereo*)data;
|
||||
ImGui::Text("> YM2608");
|
||||
OPNB_CHAN_DEBUG;
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_PC98_EXT: {
|
||||
case DIV_SYSTEM_YM2608_EXT: {
|
||||
if (chanNum>=2 && chanNum<=5) {
|
||||
DivPlatformOPN::OPNOpChannelStereo* ch=(DivPlatformOPN::OPNOpChannelStereo*)data;
|
||||
ImGui::Text("> YM2608 (per operator)");
|
||||
|
|
|
@ -592,12 +592,14 @@ void FurnaceGUI::updateWindowTitle() {
|
|||
|
||||
void FurnaceGUI::autoDetectSystem() {
|
||||
std::map<DivSystem,int> sysCountMap;
|
||||
std::map<DivSystem,DivConfig> sysConfMap;
|
||||
for (int i=0; i<e->song.systemLen; i++) {
|
||||
try {
|
||||
sysCountMap.at(e->song.system[i])++;
|
||||
} catch (std::exception& ex) {
|
||||
sysCountMap[e->song.system[i]]=1;
|
||||
}
|
||||
sysConfMap[e->song.system[i]]=e->song.systemFlags[i];
|
||||
}
|
||||
|
||||
logV("sysCountMap:");
|
||||
|
@ -607,16 +609,20 @@ void FurnaceGUI::autoDetectSystem() {
|
|||
|
||||
bool isMatch=false;
|
||||
std::map<DivSystem,int> defCountMap;
|
||||
std::map<DivSystem,DivConfig> defConfMap;
|
||||
for (FurnaceGUISysCategory& i: sysCategories) {
|
||||
for (FurnaceGUISysDef& j: i.systems) {
|
||||
defCountMap.clear();
|
||||
for (size_t k=0; k<j.definition.size(); k+=4) {
|
||||
if (j.definition[k]==0) break;
|
||||
defConfMap.clear();
|
||||
for (FurnaceGUISysDefChip& k: j.orig) {
|
||||
try {
|
||||
defCountMap.at((DivSystem)j.definition[k])++;
|
||||
defCountMap.at(k.sys)++;
|
||||
} catch (std::exception& ex) {
|
||||
defCountMap[(DivSystem)j.definition[k]]=1;
|
||||
defCountMap[k.sys]=1;
|
||||
}
|
||||
DivConfig dc;
|
||||
dc.loadFromMemory(k.flags);
|
||||
defConfMap[k.sys]=dc;
|
||||
}
|
||||
if (defCountMap.size()!=sysCountMap.size()) continue;
|
||||
isMatch=true;
|
||||
|
@ -630,6 +636,18 @@ void FurnaceGUI::autoDetectSystem() {
|
|||
isMatch=false;
|
||||
break;
|
||||
}
|
||||
DivConfig& sysDC=sysConfMap.at(k.first);
|
||||
for (std::pair<String,String> l: defConfMap.at(k.first).configMap()) {
|
||||
if (!sysDC.has(l.first)) {
|
||||
isMatch=false;
|
||||
break;
|
||||
}
|
||||
if (sysDC.getString(l.first,"")!=l.second) {
|
||||
isMatch=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isMatch) break;
|
||||
} catch (std::exception& ex) {
|
||||
isMatch=false;
|
||||
break;
|
||||
|
@ -1825,6 +1843,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
|
|||
|
||||
int FurnaceGUI::save(String path, int dmfVersion) {
|
||||
SafeWriter* w;
|
||||
logD("saving file...");
|
||||
if (dmfVersion) {
|
||||
if (dmfVersion<24) dmfVersion=24;
|
||||
w=e->saveDMF(dmfVersion);
|
||||
|
@ -1833,11 +1852,14 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
}
|
||||
if (w==NULL) {
|
||||
lastError=e->getLastError();
|
||||
logE("couldn't save! %s",lastError);
|
||||
return 3;
|
||||
}
|
||||
logV("opening file for writing...");
|
||||
FILE* outFile=ps_fopen(path.c_str(),"wb");
|
||||
if (outFile==NULL) {
|
||||
lastError=strerror(errno);
|
||||
logE("couldn't save! %s",lastError);
|
||||
w->finish();
|
||||
return 1;
|
||||
}
|
||||
|
@ -1918,6 +1940,7 @@ int FurnaceGUI::save(String path, int dmfVersion) {
|
|||
showWarning(e->getWarnings(),GUI_WARN_GENERIC);
|
||||
}
|
||||
pushRecentFile(path);
|
||||
logD("save complete.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -4070,7 +4093,6 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
break;
|
||||
case GUI_FILE_SAVE: {
|
||||
logD("saving: %s",copyOfName.c_str());
|
||||
bool saveWasSuccessful=true;
|
||||
if (save(copyOfName,0)>0) {
|
||||
showError(fmt::sprintf("Error while saving file! (%s)",lastError));
|
||||
|
@ -5019,8 +5041,12 @@ bool FurnaceGUI::loop() {
|
|||
ImGui::Checkbox("Big endian",&pendingRawSampleBigEndian);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::BeginDisabled(pendingRawSampleDepth==DIV_SAMPLE_DEPTH_16BIT);
|
||||
ImGui::Checkbox("Swap nibbles",&pendingRawSampleSwapNibbles);
|
||||
ImGui::EndDisabled();
|
||||
|
||||
if (ImGui::Button("OK")) {
|
||||
DivSample* s=e->sampleFromFileRaw(pendingRawSample.c_str(),(DivSampleDepth)pendingRawSampleDepth,pendingRawSampleChannels,pendingRawSampleBigEndian,pendingRawSampleUnsigned);
|
||||
DivSample* s=e->sampleFromFileRaw(pendingRawSample.c_str(),(DivSampleDepth)pendingRawSampleDepth,pendingRawSampleChannels,pendingRawSampleBigEndian,pendingRawSampleUnsigned,pendingRawSampleSwapNibbles);
|
||||
if (s==NULL) {
|
||||
showError(e->getLastError());
|
||||
} else {
|
||||
|
@ -5053,6 +5079,7 @@ bool FurnaceGUI::loop() {
|
|||
}
|
||||
logD("saving backup...");
|
||||
SafeWriter* w=e->saveFur(true);
|
||||
logV("writing file...");
|
||||
|
||||
if (w!=NULL) {
|
||||
FILE* outFile=ps_fopen(backupPath.c_str(),"wb");
|
||||
|
@ -5067,6 +5094,7 @@ bool FurnaceGUI::loop() {
|
|||
w->finish();
|
||||
}
|
||||
}
|
||||
logD("backup saved.");
|
||||
backupTimer=30.0;
|
||||
return true;
|
||||
});
|
||||
|
@ -5684,6 +5712,7 @@ FurnaceGUI::FurnaceGUI():
|
|||
pendingRawSampleChannels(1),
|
||||
pendingRawSampleUnsigned(false),
|
||||
pendingRawSampleBigEndian(false),
|
||||
pendingRawSampleSwapNibbles(false),
|
||||
globalWinFlags(0),
|
||||
curFileDialog(GUI_FILE_OPEN),
|
||||
warnAction(GUI_WARN_OPEN),
|
||||
|
|
|
@ -948,6 +948,7 @@ struct FurnaceGUISysDef {
|
|||
const char* name;
|
||||
const char* extra;
|
||||
String definition;
|
||||
std::vector<FurnaceGUISysDefChip> orig;
|
||||
FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e=NULL);
|
||||
};
|
||||
|
||||
|
@ -1116,7 +1117,7 @@ class FurnaceGUI {
|
|||
|
||||
String pendingRawSample;
|
||||
int pendingRawSampleDepth, pendingRawSampleChannels;
|
||||
bool pendingRawSampleUnsigned, pendingRawSampleBigEndian;
|
||||
bool pendingRawSampleUnsigned, pendingRawSampleBigEndian, pendingRawSampleSwapNibbles;
|
||||
|
||||
ImGuiWindowFlags globalWinFlags;
|
||||
|
||||
|
@ -1177,6 +1178,7 @@ class FurnaceGUI {
|
|||
int nesCore;
|
||||
int fdsCore;
|
||||
int c64Core;
|
||||
int pokeyCore;
|
||||
int pcSpeakerOutMethod;
|
||||
String yrw801Path;
|
||||
String tg100Path;
|
||||
|
@ -1309,6 +1311,7 @@ class FurnaceGUI {
|
|||
nesCore(0),
|
||||
fdsCore(0),
|
||||
c64Core(1),
|
||||
pokeyCore(1),
|
||||
pcSpeakerOutMethod(0),
|
||||
yrw801Path(""),
|
||||
tg100Path(""),
|
||||
|
|
|
@ -913,8 +913,9 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
const int availableSystems[]={
|
||||
DIV_SYSTEM_YM2612,
|
||||
DIV_SYSTEM_YM2612_EXT,
|
||||
DIV_SYSTEM_YM2612_FRAC,
|
||||
DIV_SYSTEM_YM2612_FRAC_EXT,
|
||||
DIV_SYSTEM_YM2612_CSM,
|
||||
DIV_SYSTEM_YM2612_DUALPCM,
|
||||
DIV_SYSTEM_YM2612_DUALPCM_EXT,
|
||||
DIV_SYSTEM_SMS,
|
||||
DIV_SYSTEM_GB,
|
||||
DIV_SYSTEM_PCE,
|
||||
|
@ -939,10 +940,10 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_YMU759,
|
||||
DIV_SYSTEM_DUMMY,
|
||||
DIV_SYSTEM_SOUND_UNIT,
|
||||
DIV_SYSTEM_OPN,
|
||||
DIV_SYSTEM_OPN_EXT,
|
||||
DIV_SYSTEM_PC98,
|
||||
DIV_SYSTEM_PC98_EXT,
|
||||
DIV_SYSTEM_YM2203,
|
||||
DIV_SYSTEM_YM2203_EXT,
|
||||
DIV_SYSTEM_YM2608,
|
||||
DIV_SYSTEM_YM2608_EXT,
|
||||
DIV_SYSTEM_OPLL,
|
||||
DIV_SYSTEM_OPLL_DRUMS,
|
||||
DIV_SYSTEM_VRC7,
|
||||
|
@ -958,6 +959,7 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_TIA,
|
||||
DIV_SYSTEM_SAA1099,
|
||||
DIV_SYSTEM_AY8930,
|
||||
DIV_SYSTEM_POKEY,
|
||||
DIV_SYSTEM_LYNX,
|
||||
DIV_SYSTEM_QSOUND,
|
||||
DIV_SYSTEM_X1_010,
|
||||
|
@ -994,8 +996,9 @@ const int availableSystems[]={
|
|||
const int chipsFM[]={
|
||||
DIV_SYSTEM_YM2612,
|
||||
DIV_SYSTEM_YM2612_EXT,
|
||||
DIV_SYSTEM_YM2612_FRAC,
|
||||
DIV_SYSTEM_YM2612_FRAC_EXT,
|
||||
DIV_SYSTEM_YM2612_CSM,
|
||||
DIV_SYSTEM_YM2612_DUALPCM,
|
||||
DIV_SYSTEM_YM2612_DUALPCM_EXT,
|
||||
DIV_SYSTEM_YM2151,
|
||||
DIV_SYSTEM_YM2610,
|
||||
DIV_SYSTEM_YM2610_EXT,
|
||||
|
@ -1004,10 +1007,10 @@ const int chipsFM[]={
|
|||
DIV_SYSTEM_YM2610B,
|
||||
DIV_SYSTEM_YM2610B_EXT,
|
||||
DIV_SYSTEM_YMU759,
|
||||
DIV_SYSTEM_OPN,
|
||||
DIV_SYSTEM_OPN_EXT,
|
||||
DIV_SYSTEM_PC98,
|
||||
DIV_SYSTEM_PC98_EXT,
|
||||
DIV_SYSTEM_YM2203,
|
||||
DIV_SYSTEM_YM2203_EXT,
|
||||
DIV_SYSTEM_YM2608,
|
||||
DIV_SYSTEM_YM2608_EXT,
|
||||
DIV_SYSTEM_OPLL,
|
||||
DIV_SYSTEM_OPLL_DRUMS,
|
||||
DIV_SYSTEM_VRC7,
|
||||
|
@ -1064,6 +1067,7 @@ const int chipsSpecial[]={
|
|||
DIV_SYSTEM_SOUND_UNIT,
|
||||
DIV_SYSTEM_TIA,
|
||||
DIV_SYSTEM_AY8930,
|
||||
DIV_SYSTEM_POKEY,
|
||||
DIV_SYSTEM_LYNX,
|
||||
DIV_SYSTEM_VERA,
|
||||
DIV_SYSTEM_PET,
|
||||
|
|
|
@ -255,6 +255,10 @@ const char* c64SpecialBits[3]={
|
|||
"sync", "ring", NULL
|
||||
};
|
||||
|
||||
const char* pokeyCtlBits[9]={
|
||||
"15KHz", "filter 2+4", "filter 1+3", "16-bit 3+4", "16-bit 1+2", "high3", "high1", "poly9", NULL
|
||||
};
|
||||
|
||||
const char* mikeyFeedbackBits[11] = {
|
||||
"0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL
|
||||
};
|
||||
|
@ -5003,6 +5007,10 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) {
|
||||
dutyLabel="Noise Freq";
|
||||
}
|
||||
if (ins->type==DIV_INS_POKEY) {
|
||||
dutyLabel="AUDCTL";
|
||||
dutyMax=8;
|
||||
}
|
||||
if (ins->type==DIV_INS_MIKEY) {
|
||||
dutyLabel="Duty/Int";
|
||||
dutyMax=ins->amiga.useSample?0:10;
|
||||
|
@ -5131,7 +5139,7 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (ins->type==DIV_INS_K007232) waveMax=0;
|
||||
if (ins->type==DIV_INS_GA20) waveMax=0;
|
||||
if (ins->type==DIV_INS_POKEMINI) waveMax=0;
|
||||
if (ins->type==DIV_INS_SU) waveMax=7;
|
||||
if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7;
|
||||
if (ins->type==DIV_INS_PET) {
|
||||
waveMax=8;
|
||||
waveBitMode=true;
|
||||
|
@ -5265,6 +5273,8 @@ void FurnaceGUI::drawInsEdit() {
|
|||
if (dutyMax>0) {
|
||||
if (ins->type==DIV_INS_MIKEY) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits));
|
||||
} else if (ins->type==DIV_INS_POKEY) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,pokeyCtlBits));
|
||||
} else if (ins->type==DIV_INS_MSM5232) {
|
||||
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits));
|
||||
} else if (ins->type==DIV_INS_ES5506) {
|
||||
|
|
|
@ -55,15 +55,21 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_SMS, 32, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Genesis (CSM)", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 32, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Genesis (DualPCM)", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 32, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Genesis (DualPCM, extended channel 3)", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 32, 0, "")
|
||||
}
|
||||
);
|
||||
|
@ -87,6 +93,16 @@ void FurnaceGUI::initSystemPresets() {
|
|||
)
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Genesis (CSM with Sega CD)", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_SMS, 32, 0, ""),
|
||||
CH(DIV_SYSTEM_RF5C68, 64, 0,
|
||||
"clockSel=2\n"
|
||||
"chipType=1\n"
|
||||
)
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Master System", {
|
||||
CH(DIV_SYSTEM_SMS, 64, 0, "")
|
||||
|
@ -114,6 +130,19 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_GB, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Neo Geo Pocket", {
|
||||
CH(DIV_SYSTEM_T6W28, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, -127,
|
||||
"rate=11025\n"
|
||||
"outDepth=5\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 127,
|
||||
"rate=11025\n"
|
||||
"outDepth=5\n"
|
||||
) // don't know what the actual sample rate is
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC Engine/TurboGrafx-16", {
|
||||
CH(DIV_SYSTEM_PCE, 64, 0, "")
|
||||
|
@ -190,6 +219,12 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_TIA, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari 7800 + Ballblazer/Commando", {
|
||||
CH(DIV_SYSTEM_TIA, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari Lynx", {
|
||||
CH(DIV_SYSTEM_LYNX, 64, 0, "")
|
||||
|
@ -311,7 +346,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"MSX", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "chipType=1")
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=0\nchipType=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -411,77 +446,286 @@ void FurnaceGUI::initSystemPresets() {
|
|||
) // variable rate, Mono DAC
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with PC-8801-10)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15"), // external
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15") // ""
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with PC-8801-11)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with PC-8801-11; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with PC-8801-23)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with PC-8801-23; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-88 (with HMB-20 HIBIKI-8800)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, "clockSel=2") // external; 4.0000MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-10)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15"), // external
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15") // ""
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-10; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15"), // external
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15") // ""
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-11)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on internal OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on external OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-11; extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-23)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on internal OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on external OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with PC-8801-23; extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with HMB-20 HIBIKI-8800)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, "clockSel=2") // external; 4.0000MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801mk2SR (with HMB-20 HIBIKI-8800; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, "clockSel=2") // external; 4.0000MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-10)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15"), // external
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15") // ""
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-10; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=4"), // internal
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15"), // external
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=15") // ""
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-11)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-11; extended channel 3 on internal OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-11; extended channel 3 on external OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-11; extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-23)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-23; extended channel 3 on internal OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-23; extended channel 3 on external OPN)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with PC-8801-23; extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1") // external
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with HMB-20 HIBIKI-8800)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, "clockSel=2") // external; 4.0000MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-8801FA (with HMB-20 HIBIKI-8800; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"), // internal
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, "clockSel=2") // external; 4.0000MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-26/K)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=4"), // 3.9936MHz but some compatible card has 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"), // 3.9936MHz but some compatible card has 4MHz
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-26/K; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=4"), // 3.9936MHz but some compatible card has 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"), // 3.9936MHz but some compatible card has 4MHz
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra in drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra in drums mode; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra V)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_Y8950, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra V; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_Y8950, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra V in drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_Y8950_DRUMS, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Orchestra V in drums mode; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_Y8950_DRUMS, 64, 0, "clockSel=4"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-86)", { // -73 also has OPNA
|
||||
CH(DIV_SYSTEM_PC98, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0, // 2x 16-bit Burr Brown DAC
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -495,7 +739,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-86; extended channel 3)", { // -73 also has OPNA
|
||||
CH(DIV_SYSTEM_PC98_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -509,19 +753,19 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-73)", {
|
||||
CH(DIV_SYSTEM_PC98, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with PC-9801-73; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PC98_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_PCSPKR, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -533,7 +777,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -545,7 +789,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -557,7 +801,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NEC PC-98 (with Sound Blaster 16 for PC-9800 w/PC-9801-26/K compatible in drums mode; extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=44100\n"
|
||||
"outDepth=15\n"
|
||||
|
@ -580,29 +824,29 @@ void FurnaceGUI::initSystemPresets() {
|
|||
ENTRY(
|
||||
"ZX Spectrum (128K) with TurboSound FM", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=1")
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (128K) with TurboSound FM (extended channel 3 on first OPN)", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=1")
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (128K) with TurboSound FM (extended channel 3 on second OPN)", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=1")
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"ZX Spectrum (128K) with TurboSound FM (extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=1")
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=1")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -617,6 +861,19 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=5")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari 800", {
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "clockSel=1")
|
||||
},
|
||||
"tickRate=50"
|
||||
);
|
||||
ENTRY(
|
||||
"Atari 800 (stereo)", {
|
||||
CH(DIV_SYSTEM_POKEY, 64, -127, "clockSel=1"),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 127, "clockSel=1"),
|
||||
},
|
||||
"tickRate=50"
|
||||
);
|
||||
ENTRY(
|
||||
"Atari ST", {
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0,
|
||||
|
@ -835,6 +1092,12 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_RF5C68, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"FM Towns (CSM)", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 64, 0, "clockSel=2"), // YM3438
|
||||
CH(DIV_SYSTEM_RF5C68, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Commander X16", {
|
||||
CH(DIV_SYSTEM_VERA, 64, 0, ""),
|
||||
|
@ -859,22 +1122,22 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2203 (OPN)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=3")
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=3")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2203 (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=3")
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=3")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2608 (OPNA)", {
|
||||
CH(DIV_SYSTEM_PC98, 64, 0, "")
|
||||
CH(DIV_SYSTEM_YM2608, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2608 (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_PC98_EXT, 64, 0, "")
|
||||
CH(DIV_SYSTEM_YM2608_EXT, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -907,14 +1170,19 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_YM2612_EXT, 64, 0, "ladderEffect=true")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2612 (OPN2) CSM", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 64, 0, "ladderEffect=true")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2612 (OPN2) with DualPCM", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC, 64, 0, "ladderEffect=true")
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM, 64, 0, "ladderEffect=true")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM2612 (extended channel 3) with DualPCM", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, "ladderEffect=true")
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 64, 0, "ladderEffect=true")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -942,14 +1210,19 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_YM2612_EXT, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM3438 (OPN2C) CSM", {
|
||||
CH(DIV_SYSTEM_YM2612_CSM, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM3438 (OPN2C) with DualPCM", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC, 64, 0, "")
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Yamaha YM3438 (extended channel 3) with DualPCM", {
|
||||
CH(DIV_SYSTEM_YM2612_FRAC_EXT, 64, 0, "")
|
||||
CH(DIV_SYSTEM_YM2612_DUALPCM_EXT, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -1475,13 +1748,13 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Sega Hang-On", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_SEGAPCM, 64, 0, "") // discrete logics, 62.5KHz output rate
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Sega Hang-On (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_SEGAPCM, 64, 0, "") // discrete logics, 62.5KHz output rate
|
||||
}
|
||||
);
|
||||
|
@ -1571,26 +1844,26 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Capcom Arcade", { // 1943, Side arms, etc
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 4 or 1.5MHz; various per games
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5")
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 4 or 1.5MHz; various per games
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Capcom Arcade (extended channel 3 on first OPN)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5")
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Capcom Arcade (extended channel 3 on second OPN)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5")
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Capcom Arcade (extended channel 3 on both OPNs)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5")
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"),
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
|
@ -1625,7 +1898,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NMK 16-bit Arcade", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz; optional
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz; optional
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0,
|
||||
"clockSel=2\n"
|
||||
"rateSel=true\n"
|
||||
|
@ -1638,7 +1911,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"NMK 16-bit Arcade (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz; optional
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz; optional
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0,
|
||||
"clockSel=2\n"
|
||||
"rateSel=true\n"
|
||||
|
@ -1651,21 +1924,21 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Kaneko DJ Boy", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, -127, "clockSel=12"), // 1.5MHz, Left output
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 127, "clockSel=12"), // 1.5MHz, Right output
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Kaneko DJ Boy (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, -127, "clockSel=12"), // 1.5MHz, Left output
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 127, "clockSel=12") // 1.5MHz, Right output
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Kaneko Air Buster", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0,
|
||||
"clockSel=13\n"
|
||||
"rateSel=true\n"
|
||||
|
@ -1674,7 +1947,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Kaneko Air Buster (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0,
|
||||
"clockSel=13\n"
|
||||
"rateSel=true\n"
|
||||
|
@ -1706,29 +1979,29 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Tecmo Ninja Gaiden", { // Ninja Gaiden, Raiga, etc
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Tecmo Ninja Gaiden (extended channel 3 on first OPN)", { // Ninja Gaiden, Raiga, etc
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Tecmo Ninja Gaiden (extended channel 3 on second OPN)", { // Ninja Gaiden, Raiga, etc
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Tecmo Ninja Gaiden (extended channel 3 on both OPNs)", { // Ninja Gaiden, Raiga, etc
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1MHz
|
||||
}
|
||||
);
|
||||
|
@ -1803,68 +2076,95 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_MSM6295, 64, 127, "clockSel=14") // 1.193MHz (3.579545MHz / 3), Right output
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari Marble Madness", {
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari Championship Sprint", {
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari Tetris", {
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Atari I, Robot", {
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "customClock=1512000"),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "customClock=1512000"),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "customClock=1512000"),
|
||||
CH(DIV_SYSTEM_POKEY, 64, 0, "customClock=1512000")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Karnov", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL, 64, 0, "clockSel=3") // 3MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Karnov (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL, 64, 0, "clockSel=3") // 3MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Karnov (drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL_DRUMS, 64, 0, "clockSel=3") // 3MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Karnov (extended channel 3; drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL_DRUMS, 64, 0, "clockSel=3") // 3MHz
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Arcade", { // Bad dudes, Robocop, etc
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1 to 1.056MHz; various per games or optional
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Arcade (extended channel 3)", { // Bad dudes, Robocop, etc
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL2, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1 to 1.056MHz; various per games or optional
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Arcade (drums mode)", { // Bad dudes, Robocop, etc
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1 to 1.056MHz; various per games or optional
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East Arcade (extended channel 3; drums mode)", { // Bad dudes, Robocop, etc
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_OPL2_DRUMS, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "") // 1 to 1.056MHz; various per games or optional
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East PCX", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_PCE, 64, 0, "")
|
||||
// software controlled MSM5205
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Data East PCX (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=5"), // 1.5MHz
|
||||
CH(DIV_SYSTEM_PCE, 64, 0, "")
|
||||
// software controlled MSM5205
|
||||
}
|
||||
|
@ -1872,7 +2172,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
ENTRY(
|
||||
"Data East Dark Seal", { // Dark Seal, Crude Buster, Vapor Trail, etc
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, ""), // 3.580MHz (32.22MHz / 9)
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=2"), // 4.0275MHz (32.22MHz / 8); optional
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=2"), // 4.0275MHz (32.22MHz / 8); optional
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, ""), // 1.007MHz (32.22MHz / 32)
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "clockSel=8") // 2.014MHz (32.22MHz / 16); optional
|
||||
// HuC6280 is for control them, internal sound isn't used
|
||||
|
@ -1881,7 +2181,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
ENTRY(
|
||||
"Data East Dark Seal (extended channel 3)", { // Dark Seal, Crude Buster, Vapor Trail, etc
|
||||
CH(DIV_SYSTEM_YM2151, 64, 0, ""), // 3.580MHz (32.22MHz / 9)
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=2"), // 4.0275MHz (32.22MHz / 8); optional
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=2"), // 4.0275MHz (32.22MHz / 8); optional
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, ""), // 1.007MHz (32.22MHz / 32)
|
||||
CH(DIV_SYSTEM_MSM6295, 64, 0, "clockSel=8") // 2.014MHz (32.22MHz / 16); optional
|
||||
// HuC6280 is for control them, internal sound isn't used
|
||||
|
@ -1996,7 +2296,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Alpha denshi Alpha-68K", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_OPLL, 64, 0, "clockSel=0"), // 3.58MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=7614\n"
|
||||
|
@ -2006,7 +2306,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Alpha denshi Alpha-68K (extended channel 3)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_OPLL, 64, 0, "clockSel=0"), // 3.58MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=7614\n"
|
||||
|
@ -2016,7 +2316,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Alpha denshi Alpha-68K (drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_OPLL_DRUMS, 64, 0, "clockSel=0"), // 3.58MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=7614\n"
|
||||
|
@ -2026,7 +2326,7 @@ void FurnaceGUI::initSystemPresets() {
|
|||
);
|
||||
ENTRY(
|
||||
"Alpha denshi Alpha-68K (extended channel 3; drums mode)", {
|
||||
CH(DIV_SYSTEM_OPN_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_YM2203_EXT, 64, 0, "clockSel=3"), // 3MHz
|
||||
CH(DIV_SYSTEM_OPLL_DRUMS, 64, 0, "clockSel=0"), // 3.58MHz
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=7614\n"
|
||||
|
@ -2034,6 +2334,20 @@ void FurnaceGUI::initSystemPresets() {
|
|||
) // software controlled 8 bit DAC
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Alpha denshi Equites", {
|
||||
CH(DIV_SYSTEM_MSM5232, 64, 0, "customClock=6144000"),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=14"),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=11025\n"
|
||||
"outDepth=5\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=11025\n"
|
||||
"outDepth=5\n"
|
||||
) // don't know what the actual sample rate is
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Neo Geo MVS", {
|
||||
CH(DIV_SYSTEM_YM2610_FULL, 64, 0, "")
|
||||
|
@ -2106,6 +2420,43 @@ void FurnaceGUI::initSystemPresets() {
|
|||
CH(DIV_SYSTEM_YM2610B_EXT, 64, 0, "")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Taito Metal Soldier Isaac II", {
|
||||
CH(DIV_SYSTEM_MSM5232, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=3"),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=3")
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Taito The Fairyland Story", {
|
||||
CH(DIV_SYSTEM_MSM5232, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0,
|
||||
"clockSel=3\n"
|
||||
"chipType=1\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=11025\n"
|
||||
"outDepth=7\n"
|
||||
) // don't know what the actual sample rate is
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Taito Wyvern F-0", {
|
||||
CH(DIV_SYSTEM_MSM5232, 64, 0, ""),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0,
|
||||
"clockSel=3\n"
|
||||
"chipType=1\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_AY8910, 64, 0,
|
||||
"clockSel=3\n"
|
||||
"chipType=1\n"
|
||||
),
|
||||
CH(DIV_SYSTEM_PCM_DAC, 64, 0,
|
||||
"rate=11025\n"
|
||||
"outDepth=7\n"
|
||||
) // don't know what the actual sample rate is
|
||||
}
|
||||
);
|
||||
ENTRY(
|
||||
"Seta 1", {
|
||||
CH(DIV_SYSTEM_X1_010, 64, 0, "")
|
||||
|
@ -2302,9 +2653,9 @@ void FurnaceGUI::initSystemPresets() {
|
|||
FurnaceGUISysDef::FurnaceGUISysDef(const char* n, std::initializer_list<FurnaceGUISysDefChip> def, const char* e):
|
||||
name(n),
|
||||
extra(e) {
|
||||
std::vector<FurnaceGUISysDefChip> uncompiled=def;
|
||||
orig=def;
|
||||
int index=0;
|
||||
for (FurnaceGUISysDefChip& i: uncompiled) {
|
||||
for (FurnaceGUISysDefChip& i: orig) {
|
||||
definition+=fmt::sprintf(
|
||||
"id%d=%d\nvol%d=%d\npan%d=%d\nflags%d=%s\n",
|
||||
index,
|
||||
|
|
|
@ -227,11 +227,11 @@ void FurnaceGUI::drawSampleEdit() {
|
|||
if (sampleDepths[i]==NULL) continue;
|
||||
if (ImGui::Selectable(sampleDepths[i])) {
|
||||
sample->prepareUndo(true);
|
||||
e->lockEngine([sample]() {
|
||||
e->lockEngine([this,sample,i]() {
|
||||
sample->render();
|
||||
sample->depth=(DivSampleDepth)i;
|
||||
e->renderSamples();
|
||||
});
|
||||
sample->depth=(DivSampleDepth)i;
|
||||
e->renderSamplesP();
|
||||
updateSampleTex=true;
|
||||
MARK_MODIFIED;
|
||||
}
|
||||
|
|
|
@ -102,6 +102,11 @@ const char* c64Cores[]={
|
|||
"reSIDfp"
|
||||
};
|
||||
|
||||
const char* pokeyCores[]={
|
||||
"Atari800 (mzpokeysnd)",
|
||||
"ASAP (C++ port)"
|
||||
};
|
||||
|
||||
const char* pcspkrOutMethods[]={
|
||||
"evdev SND_TONE",
|
||||
"KIOCSOUND on /dev/tty1",
|
||||
|
@ -1069,6 +1074,10 @@ void FurnaceGUI::drawSettings() {
|
|||
ImGui::SameLine();
|
||||
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2);
|
||||
|
||||
ImGui::Text("POKEY core");
|
||||
ImGui::SameLine();
|
||||
ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("PC Speaker strategy");
|
||||
|
@ -2336,6 +2345,7 @@ void FurnaceGUI::syncSettings() {
|
|||
settings.nesCore=e->getConfInt("nesCore",0);
|
||||
settings.fdsCore=e->getConfInt("fdsCore",0);
|
||||
settings.c64Core=e->getConfInt("c64Core",1);
|
||||
settings.pokeyCore=e->getConfInt("pokeyCore",1);
|
||||
settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0);
|
||||
settings.yrw801Path=e->getConfString("yrw801Path","");
|
||||
settings.tg100Path=e->getConfString("tg100Path","");
|
||||
|
@ -2461,6 +2471,7 @@ void FurnaceGUI::syncSettings() {
|
|||
clampSetting(settings.nesCore,0,1);
|
||||
clampSetting(settings.fdsCore,0,1);
|
||||
clampSetting(settings.c64Core,0,1);
|
||||
clampSetting(settings.pokeyCore,0,1);
|
||||
clampSetting(settings.pcSpeakerOutMethod,0,4);
|
||||
clampSetting(settings.mainFont,0,6);
|
||||
clampSetting(settings.patFont,0,6);
|
||||
|
@ -2604,7 +2615,8 @@ void FurnaceGUI::commitSettings() {
|
|||
settings.snCore!=e->getConfInt("snCore",0) ||
|
||||
settings.nesCore!=e->getConfInt("nesCore",0) ||
|
||||
settings.fdsCore!=e->getConfInt("fdsCore",0) ||
|
||||
settings.c64Core!=e->getConfInt("c64Core",1)
|
||||
settings.c64Core!=e->getConfInt("c64Core",1) ||
|
||||
settings.pokeyCore!=e->getConfInt("pokeyCore",1)
|
||||
);
|
||||
|
||||
e->setConf("mainFontSize",settings.mainFontSize);
|
||||
|
@ -2624,6 +2636,7 @@ void FurnaceGUI::commitSettings() {
|
|||
e->setConf("nesCore",settings.nesCore);
|
||||
e->setConf("fdsCore",settings.fdsCore);
|
||||
e->setConf("c64Core",settings.c64Core);
|
||||
e->setConf("pokeyCore",settings.pokeyCore);
|
||||
e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod);
|
||||
e->setConf("yrw801Path",settings.yrw801Path);
|
||||
e->setConf("tg100Path",settings.tg100Path);
|
||||
|
|
|
@ -30,8 +30,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
switch (type) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT:
|
||||
case DIV_SYSTEM_YM2612_FRAC:
|
||||
case DIV_SYSTEM_YM2612_FRAC_EXT: {
|
||||
case DIV_SYSTEM_YM2612_DUALPCM:
|
||||
case DIV_SYSTEM_YM2612_DUALPCM_EXT: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
bool ladder=flags.getBool("ladderEffect",0);
|
||||
bool noExtMacros=flags.getBool("noExtMacros",false);
|
||||
|
@ -59,7 +59,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
|
||||
altered=true;
|
||||
}
|
||||
if (type==DIV_SYSTEM_YM2612_EXT || type==DIV_SYSTEM_YM2612_FRAC_EXT) {
|
||||
if (type==DIV_SYSTEM_YM2612_EXT || type==DIV_SYSTEM_YM2612_DUALPCM_EXT) {
|
||||
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
|
||||
altered=true;
|
||||
}
|
||||
|
@ -850,8 +850,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_OPN:
|
||||
case DIV_SYSTEM_OPN_EXT: {
|
||||
case DIV_SYSTEM_YM2203:
|
||||
case DIV_SYSTEM_YM2203_EXT: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
int prescale=flags.getInt("prescale",0);
|
||||
bool noExtMacros=flags.getBool("noExtMacros",false);
|
||||
|
@ -895,7 +895,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
altered=true;
|
||||
}
|
||||
|
||||
if (type==DIV_SYSTEM_OPN_EXT) {
|
||||
if (type==DIV_SYSTEM_YM2203_EXT) {
|
||||
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
|
||||
altered=true;
|
||||
}
|
||||
|
@ -910,8 +910,8 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_PC98:
|
||||
case DIV_SYSTEM_PC98_EXT: {
|
||||
case DIV_SYSTEM_YM2608:
|
||||
case DIV_SYSTEM_YM2608_EXT: {
|
||||
int clockSel=flags.getInt("clockSel",0);
|
||||
int prescale=flags.getInt("prescale",0);
|
||||
bool noExtMacros=flags.getBool("noExtMacros",false);
|
||||
|
@ -939,7 +939,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
altered=true;
|
||||
}
|
||||
|
||||
if (type==DIV_SYSTEM_PC98_EXT) {
|
||||
if (type==DIV_SYSTEM_YM2608_EXT) {
|
||||
if (ImGui::Checkbox("Disable ExtCh FM macros (compatibility)",&noExtMacros)) {
|
||||
altered=true;
|
||||
}
|
||||
|
@ -1243,6 +1243,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
// default to 44100Hz 16-bit stereo
|
||||
int sampRate=flags.getInt("rate",44100);
|
||||
int bitDepth=flags.getInt("outDepth",15)+1;
|
||||
int interpolation=flags.getInt("interpolation",0);
|
||||
bool stereo=flags.getBool("stereo",false);
|
||||
|
||||
ImGui::Text("Output rate:");
|
||||
|
@ -1261,11 +1262,30 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
|
|||
altered=true;
|
||||
}
|
||||
|
||||
ImGui::Text("Interpolation:");
|
||||
if (ImGui::RadioButton("None",interpolation==0)) {
|
||||
interpolation=0;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("Linear",interpolation==1)) {
|
||||
interpolation=1;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("Cubic",interpolation==2)) {
|
||||
interpolation=2;
|
||||
altered=true;
|
||||
}
|
||||
if (ImGui::RadioButton("Sinc",interpolation==3)) {
|
||||
interpolation=3;
|
||||
altered=true;
|
||||
}
|
||||
|
||||
if (altered) {
|
||||
e->lockSave([&]() {
|
||||
flags.set("rate",sampRate);
|
||||
flags.set("outDepth",bitDepth-1);
|
||||
flags.set("stereo",stereo);
|
||||
flags.set("interpolation",interpolation);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
|
129
src/log.cpp
129
src/log.cpp
|
@ -18,6 +18,8 @@
|
|||
*/
|
||||
|
||||
#include "ta-log.h"
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
|
||||
#ifdef IS_MOBILE
|
||||
int logLevel=LOGLEVEL_TRACE;
|
||||
|
@ -25,18 +27,71 @@ int logLevel=LOGLEVEL_TRACE;
|
|||
int logLevel=LOGLEVEL_INFO;
|
||||
#endif
|
||||
|
||||
FILE* logFile;
|
||||
char* logFileBuf;
|
||||
unsigned int logFilePosI;
|
||||
unsigned int logFilePosO;
|
||||
std::thread* logFileThread;
|
||||
std::mutex logFileLock;
|
||||
std::mutex logFileLockI;
|
||||
std::condition_variable logFileNotify;
|
||||
bool logFileAvail=false;
|
||||
|
||||
std::atomic<unsigned short> logPosition;
|
||||
|
||||
LogEntry logEntries[TA_LOG_SIZE];
|
||||
|
||||
static constexpr unsigned int TA_LOG_MASK=TA_LOG_SIZE-1;
|
||||
static constexpr unsigned int TA_LOGFILE_BUF_MASK=TA_LOGFILE_BUF_SIZE-1;
|
||||
|
||||
const char* logTypes[5]={
|
||||
"ERROR",
|
||||
"warning",
|
||||
"info",
|
||||
"debug",
|
||||
"trace"
|
||||
};
|
||||
|
||||
void appendLogBuf(const LogEntry& entry) {
|
||||
logFileLockI.lock();
|
||||
|
||||
std::string toWrite=fmt::sprintf(
|
||||
"%02d:%02d:%02d [%s] %s\n",
|
||||
entry.time.tm_hour,
|
||||
entry.time.tm_min,
|
||||
entry.time.tm_sec,
|
||||
logTypes[entry.loglevel],
|
||||
entry.text
|
||||
);
|
||||
|
||||
const char* msg=toWrite.c_str();
|
||||
size_t len=toWrite.size();
|
||||
|
||||
int remaining=(logFilePosO-logFilePosI-1)&TA_LOGFILE_BUF_SIZE;
|
||||
|
||||
if (len>=(unsigned int)remaining) {
|
||||
printf("line too long to fit in log buffer!\n");
|
||||
logFileLockI.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((logFilePosI+len)>=TA_LOGFILE_BUF_SIZE) {
|
||||
size_t firstWrite=TA_LOGFILE_BUF_SIZE-logFilePosI;
|
||||
memcpy(logFileBuf+logFilePosI,msg,firstWrite);
|
||||
memcpy(logFileBuf,msg+firstWrite,len-firstWrite);
|
||||
} else {
|
||||
memcpy(logFileBuf+logFilePosI,msg,len);
|
||||
}
|
||||
|
||||
logFilePosI=(logFilePosI+len)&TA_LOGFILE_BUF_MASK;
|
||||
logFileLockI.unlock();
|
||||
}
|
||||
|
||||
int writeLog(int level, const char* msg, fmt::printf_args args) {
|
||||
time_t thisMakesNoSense=time(NULL);
|
||||
int pos=logPosition;
|
||||
logPosition=(logPosition+1)&TA_LOG_MASK;
|
||||
int pos=(logPosition.fetch_add(1))&TA_LOG_MASK;
|
||||
|
||||
logEntries[pos].text=fmt::vsprintf(msg,args);
|
||||
logEntries[pos].text.assign(fmt::vsprintf(msg,args));
|
||||
// why do I have to pass a pointer
|
||||
// can't I just pass the time_t directly?!
|
||||
#ifdef _WIN32
|
||||
|
@ -54,6 +109,12 @@ int writeLog(int level, const char* msg, fmt::printf_args args) {
|
|||
logEntries[pos].loglevel=level;
|
||||
logEntries[pos].ready=true;
|
||||
|
||||
// write to log file
|
||||
if (logFileAvail) {
|
||||
appendLogBuf(logEntries[pos]);
|
||||
logFileNotify.notify_one();
|
||||
}
|
||||
|
||||
if (logLevel<level) return 0;
|
||||
switch (level) {
|
||||
case LOGLEVEL_ERROR:
|
||||
|
@ -71,8 +132,70 @@ int writeLog(int level, const char* msg, fmt::printf_args args) {
|
|||
}
|
||||
|
||||
void initLog() {
|
||||
// initalize log buffer
|
||||
logPosition=0;
|
||||
for (int i=0; i<TA_LOG_SIZE; i++) {
|
||||
logEntries[i].text.reserve(128);
|
||||
}
|
||||
|
||||
// initialize log to file thread
|
||||
logFileAvail=false;
|
||||
}
|
||||
|
||||
void _logFileThread() {
|
||||
std::unique_lock<std::mutex> lock(logFileLock);
|
||||
while (true) {
|
||||
unsigned int logFilePosICopy=logFilePosI;
|
||||
if (logFilePosICopy!=logFilePosO) {
|
||||
// write
|
||||
if (logFilePosO>logFilePosICopy) {
|
||||
fwrite(logFileBuf+logFilePosO,1,TA_LOGFILE_BUF_SIZE-logFilePosO,logFile);
|
||||
logFilePosO=0;
|
||||
} else {
|
||||
fwrite(logFileBuf+logFilePosO,1,logFilePosICopy-logFilePosO,logFile);
|
||||
logFilePosO=logFilePosICopy&TA_LOGFILE_BUF_MASK;
|
||||
}
|
||||
} else {
|
||||
// wait
|
||||
if (!logFileAvail) break;
|
||||
fflush(logFile);
|
||||
logFileNotify.wait(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool startLogFile(const char* path) {
|
||||
if (logFileAvail) return true;
|
||||
|
||||
// rotate log file if possible
|
||||
|
||||
// open log file
|
||||
if ((logFile=fopen(path,"w+"))==NULL) {
|
||||
logFileAvail=false;
|
||||
logW("could not open log file! (%s)",strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
logFileBuf=new char[TA_LOGFILE_BUF_SIZE];
|
||||
logFilePosI=0;
|
||||
logFilePosO=0;
|
||||
logFileAvail=true;
|
||||
|
||||
logFileThread=new std::thread(_logFileThread);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool finishLogFile() {
|
||||
if (!logFileAvail) return false;
|
||||
|
||||
logFileAvail=false;
|
||||
|
||||
// flush
|
||||
logFileLockI.lock();
|
||||
logFileNotify.notify_one();
|
||||
logFileThread->join();
|
||||
logFileLockI.unlock();
|
||||
|
||||
fclose(logFile);
|
||||
return true;
|
||||
}
|
||||
|
|
21
src/main.cpp
21
src/main.cpp
|
@ -195,6 +195,8 @@ TAParamResult pVersion(String) {
|
|||
printf("- Stella by Stella Team (GPLv2)\n");
|
||||
printf("- vgsound_emu (second version, modified version) by cam900 (zlib license)\n");
|
||||
printf("- MAME GA20 core by Acho A. Tang, R. Belmont, Valley Bell (BSD 3-clause)\n");
|
||||
printf("- Atari800 mzpokeysnd POKEY emulator by Michael Borisov (GPLv2)\n");
|
||||
printf("- ASAP POKEY emulator by Piotr Fusik ported to C++ by laoo (GPLv2)\n");
|
||||
return TA_PARAM_QUIT;
|
||||
}
|
||||
|
||||
|
@ -416,23 +418,30 @@ int main(int argc, char** argv) {
|
|||
logI("usage: %s file",argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
logI("Furnace version " DIV_VERSION ".");
|
||||
|
||||
e.preInit();
|
||||
|
||||
if (!fileName.empty()) {
|
||||
logI("loading module...");
|
||||
FILE* f=ps_fopen(fileName.c_str(),"rb");
|
||||
if (f==NULL) {
|
||||
reportError(fmt::sprintf("couldn't open file! (%s)",strerror(errno)));
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
if (fseek(f,0,SEEK_END)<0) {
|
||||
reportError(fmt::sprintf("couldn't open file! (couldn't get file size: %s)",strerror(errno)));
|
||||
fclose(f);
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
ssize_t len=ftell(f);
|
||||
if (len==(SIZE_MAX>>1)) {
|
||||
reportError(fmt::sprintf("couldn't open file! (couldn't get file length: %s)",strerror(errno)));
|
||||
fclose(f);
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
if (len<1) {
|
||||
|
@ -442,6 +451,7 @@ int main(int argc, char** argv) {
|
|||
reportError(fmt::sprintf("couldn't open file! (tell error: %s)",strerror(errno)));
|
||||
}
|
||||
fclose(f);
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
unsigned char* file=new unsigned char[len];
|
||||
|
@ -449,23 +459,27 @@ int main(int argc, char** argv) {
|
|||
reportError(fmt::sprintf("couldn't open file! (size error: %s)",strerror(errno)));
|
||||
fclose(f);
|
||||
delete[] file;
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
if (fread(file,1,(size_t)len,f)!=(size_t)len) {
|
||||
reportError(fmt::sprintf("couldn't open file! (read error: %s)",strerror(errno)));
|
||||
fclose(f);
|
||||
delete[] file;
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
fclose(f);
|
||||
if (!e.load(file,(size_t)len)) {
|
||||
reportError(fmt::sprintf("could not open file! (%s)",e.getLastError()));
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!e.init()) {
|
||||
if (consoleMode) {
|
||||
reportError("could not initialize engine!");
|
||||
finishLogFile();
|
||||
return 1;
|
||||
} else {
|
||||
logE("could not initialize engine!");
|
||||
|
@ -479,6 +493,7 @@ int main(int argc, char** argv) {
|
|||
} else {
|
||||
e.benchmarkPlayback();
|
||||
}
|
||||
finishLogFile();
|
||||
return 0;
|
||||
}
|
||||
if (outName!="" || vgmOutName!="" || cmdOutName!="") {
|
||||
|
@ -519,6 +534,7 @@ int main(int argc, char** argv) {
|
|||
e.saveAudio(outName.c_str(),loops,outMode);
|
||||
e.waitAudioFile();
|
||||
}
|
||||
finishLogFile();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -536,6 +552,7 @@ int main(int argc, char** argv) {
|
|||
cli.loop();
|
||||
cli.finish();
|
||||
e.quit();
|
||||
finishLogFile();
|
||||
return 0;
|
||||
} else {
|
||||
#ifdef HAVE_SDL2
|
||||
|
@ -545,6 +562,7 @@ int main(int argc, char** argv) {
|
|||
if (ev.type==SDL_QUIT) break;
|
||||
}
|
||||
e.quit();
|
||||
finishLogFile();
|
||||
return 0;
|
||||
#else
|
||||
while (true) {
|
||||
|
@ -562,6 +580,7 @@ int main(int argc, char** argv) {
|
|||
g.bindEngine(&e);
|
||||
if (!g.init()) {
|
||||
reportError(g.getLastError());
|
||||
finishLogFile();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -584,6 +603,8 @@ int main(int argc, char** argv) {
|
|||
logI("stopping engine.");
|
||||
e.quit();
|
||||
|
||||
finishLogFile();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (coResult==S_OK || coResult==S_FALSE) {
|
||||
CoUninitialize();
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
// this has to be a power of 2
|
||||
#define TA_LOG_SIZE 2048
|
||||
|
||||
// this as well
|
||||
#define TA_LOGFILE_BUF_SIZE 65536
|
||||
|
||||
extern int logLevel;
|
||||
|
||||
extern std::atomic<unsigned short> logPosition;
|
||||
|
@ -76,4 +79,6 @@ template<typename... T> int logE(const char* msg, const T&... args) {
|
|||
}
|
||||
|
||||
void initLog();
|
||||
bool startLogFile(const char* path);
|
||||
bool finishLogFile();
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue