Merge branch 'master' of https://github.com/tildearrow/furnace into es5506_alt

* 'master' of https://github.com/tildearrow/furnace: (53 commits)
  prepare for better backward writing
  VGM export: fix oops
  GUI: drag-and-drop ins/wave/sample loading
  GUI: add "set loop" to sample editor
  MSM6295: VGM export!
  oops
  MSM6295: add rate select effect (20xx)
  update meteor shower
  MSVC is better than GCC right?
  update to-do list
  door into summer
  GUI: implement input for touch events
  GUI: update SDL hints
  fix Termux build
  add another demo song
  add demos/ecolove.fur
  update to-do list
  update demos/README.md
  add new demo songs
  dev99 - major Fractal system change
  ...

# Conflicts:
#	src/engine/dispatch.h
#	src/engine/platform/genesis.cpp
#	src/engine/playback.cpp
#	src/engine/song.h
#	src/engine/vgmOps.cpp
This commit is contained in:
cam900 2022-05-29 13:47:39 +09:00
commit 32152fd89b
72 changed files with 967 additions and 304 deletions

View file

@ -125,51 +125,109 @@ const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
case 0x5f:
return "5Fxx: Set decay 2 of operator 4 (0 to 1F)";
break;
case 0xdf:
return "DFxx: Set sample playback direction (0: normal; 1: reverse)";
break;
}
return NULL;
}
void DivPlatformGenesis::processDAC() {
if (softPCM) {
softPCMTimer+=chipClock/576;
if (softPCMTimer>rate) {
softPCMTimer-=rate;
int sample=0;
for (int i=5; i<7; i++) {
if (chan[i].dacSample!=-1) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (!isMuted[i] && s->samples>0) {
if (parent->song.noOPN2Vol) {
sample+=s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos];
} else {
sample+=(s->data8[chan[i].getDacDirection()?(s->samples-chan[i].dacPos-1):chan[i].dacPos]*dacVolTable[chan[i].outVol])>>7;
}
}
chan[i].dacPeriod+=chan[i].dacRate;
if (chan[i].dacPeriod>=(chipClock/576)) {
if (s->samples>0) {
while (chan[i].dacPeriod>=(chipClock/576)) {
chan[i].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[i].dacPos>=s->loopEnd) || (chan[i].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[i].getDacDirection()) {
chan[i].dacPos=s->loopStart;
} else {
chan[i].dacSample=-1;
chan[i].dacPeriod=0;
break;
}
}
chan[i].dacPeriod-=(chipClock/576);
}
} else {
chan[i].dacSample=-1;
}
}
}
}
//sample>>=1;
if (sample<-128) sample=-128;
if (sample>127) sample=127;
urgentWrite(0x2a,(unsigned char)sample+0x80);
}
} else {
if (!chan[5].dacReady) {
chan[5].dacDelay+=32000;
if (chan[5].dacDelay>=rate) {
chan[5].dacDelay-=rate;
chan[5].dacReady=true;
}
}
if (chan[5].dacMode && chan[5].dacSample!=-1) {
chan[5].dacPeriod+=chan[5].dacRate;
if (chan[5].dacPeriod>=rate) {
DivSample* s=parent->getSample(chan[5].dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (chan[5].dacReady && writes.size()<16) {
int sample;
if (parent->song.noOPN2Vol) {
sample=s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos];
} else {
sample=(s->data8[chan[5].getDacDirection()?(s->samples-chan[5].dacPos-1):chan[5].dacPos]*dacVolTable[chan[5].outVol])>>7;
}
urgentWrite(0x2a,(unsigned char)sample+0x80);
chan[5].dacReady=false;
}
}
chan[5].dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && chan[5].dacPos>=s->loopEnd) || (chan[5].dacPos>=s->samples)) {
if (s->isLoopable() && !chan[5].getDacDirection()) {
chan[5].dacPos=s->loopStart;
} else {
chan[5].dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (chan[5].dacPeriod>=rate) chan[5].dacPeriod-=rate;
} else {
chan[5].dacSample=-1;
}
}
}
}
}
void DivPlatformGenesis::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
static short o[2];
static int os[2];
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
processDAC();
os[0]=0; os[1]=0;
for (int i=0; i<6; i++) {
if (!writes.empty() && --delay<0) {
@ -215,41 +273,7 @@ void DivPlatformGenesis::acquire_ymfm(short* bufL, short* bufR, size_t start, si
ymfm::ym2612::fm_engine* fme=fm_ymfm->debug_engine();
for (size_t h=start; h<start+len; h++) {
if (!dacReady) {
dacDelay+=32000;
if (dacDelay>=rate) {
dacDelay-=rate;
dacReady=true;
}
}
if (dacMode && dacSample!=-1) {
dacPeriod+=dacRate;
if (dacPeriod>=rate) {
DivSample* s=parent->getSample(dacSample);
if (s->samples>0) {
if (!isMuted[5]) {
if (dacReady && writes.size()<16) {
urgentWrite(0x2a,(unsigned char)s->data8[dacPos]+0x80);
dacReady=false;
}
}
dacPos++;
if (((s->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) && dacPos>=s->loopEnd) || (dacPos>=s->samples)) {
if (s->isLoopable()) {
dacPos=s->loopStart;
} else {
dacSample=-1;
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
}
}
}
while (dacPeriod>=rate) dacPeriod-=rate;
} else {
dacSample=-1;
}
}
}
processDAC();
os[0]=0; os[1]=0;
if (!writes.empty()) {
@ -474,7 +498,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
for (int i=0; i<6; i++) {
for (int i=0; i<8; i++) {
if (i==2 && extMode) continue;
if (chan[i].freqChanged) {
if (parent->song.linearPitch==2) {
@ -493,12 +517,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
chan[i].freq=(block<<11)|fNum;
}
if (chan[i].freq>0x3fff) chan[i].freq=0x3fff;
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
if (chan[i].furnaceDac && dacMode) {
if (i<6) {
immWrite(chanOffs[i]+ADDR_FREQH,chan[i].freq>>8);
immWrite(chanOffs[i]+ADDR_FREQ,chan[i].freq&0xff);
}
if (chan[i].furnaceDac && chan[i].dacMode) {
double off=1.0;
if (dacSample>=0 && dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(dacSample);
if (chan[i].dacSample>=0 && chan[i].dacSample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[i].dacSample);
if (s->centerRate<1) {
off=1.0;
} else {
@ -506,14 +532,14 @@ void DivPlatformGenesis::tick(bool sysTick) {
}
}
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
dacRate=chan[i].freq*off;
if (dacRate<1) dacRate=1;
if (dumpWrites) addWrite(0xffff0001,dacRate);
chan[i].dacRate=chan[i].freq*off;
if (chan[i].dacRate<1) chan[i].dacRate=1;
if (dumpWrites) addWrite(0xffff0001,chan[i].dacRate);
}
chan[i].freqChanged=false;
}
if (chan[i].keyOn) {
immWrite(0x28,0xf0|konOffs[i]);
if (i<6) immWrite(0x28,0xf0|konOffs[i]);
chan[i].keyOn=false;
}
}
@ -521,6 +547,7 @@ void DivPlatformGenesis::tick(bool sysTick) {
void DivPlatformGenesis::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (ch>5) return;
for (int j=0; j<4; j++) {
unsigned short baseAddr=chanOffs[ch]|opOffs[j];
DivInstrumentFM::Operator& op=chan[ch].state.op[j];
@ -541,29 +568,33 @@ 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==5) {
if (c.chan>=5) {
if (ins->type==DIV_INS_AMIGA) {
dacMode=1;
chan[c.chan].dacMode=1;
rWrite(0x2b,1<<7);
} else if (chan[c.chan].furnaceDac) {
dacMode=0;
chan[c.chan].dacMode=0;
rWrite(0x2b,0<<7);
}
}
if (c.chan==5 && dacMode) {
if (c.chan>=5 && chan[c.chan].dacMode) {
if (skipRegisterWrites) break;
if (ins->type==DIV_INS_AMIGA) { // Furnace mode
dacSample=ins->amiga.getSample(c.value);
if (dacSample<0 || dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=ins->amiga.getSample(c.value);
if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
chan[c.chan].dacReversed=ins->amiga.getReversed(c.value);
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) {
addWrite(0xffff0000,chan[c.chan].dacSample);
addWrite(0xffff0003,chan[c.chan].getDacDirection());
}
}
dacPos=0;
dacPeriod=0;
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
chan[c.chan].freqChanged=true;
@ -573,23 +604,25 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].note=c.value;
}
dacSample=12*sampleBank+chan[c.chan].note%12;
if (dacSample>=parent->song.sampleLen) {
dacSample=-1;
chan[c.chan].dacSample=12*chan[c.chan].sampleBank+chan[c.chan].note%12;
if (chan[c.chan].dacSample>=parent->song.sampleLen) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
break;
} else {
rWrite(0x2b,1<<7);
if (dumpWrites) addWrite(0xffff0000,dacSample);
if (dumpWrites) addWrite(0xffff0000,chan[c.chan].dacSample);
}
dacPos=0;
dacPeriod=0;
dacRate=MAX(1,parent->getSample(dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(dacSample)->rate);
chan[c.chan].dacPos=0;
chan[c.chan].dacPeriod=0;
chan[c.chan].dacRate=MAX(1,parent->getSample(chan[c.chan].dacSample)->rate);
if (dumpWrites) addWrite(0xffff0001,parent->getSample(chan[c.chan].dacSample)->rate);
chan[c.chan].furnaceDac=false;
chan[c.chan].dacReversed=false;
}
break;
}
if (c.chan>=6) break;
if (chan[c.chan].insChanged) {
chan[c.chan].state=ins->fm;
@ -642,12 +675,12 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_NOTE_OFF:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
if (parent->song.brokenDACMode) {
rWrite(0x2b,0);
if (dacMode) break;
if (chan[c.chan].dacMode) break;
}
}
chan[c.chan].keyOff=true;
@ -655,8 +688,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].active=false;
break;
case DIV_CMD_NOTE_OFF_ENV:
if (c.chan==5) {
dacSample=-1;
if (c.chan>=5) {
chan[c.chan].dacSample=-1;
if (dumpWrites) addWrite(0xffff0002,0);
}
chan[c.chan].keyOff=true;
@ -672,6 +705,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
if (!chan[c.chan].std.vol.has) {
chan[c.chan].outVol=c.value;
}
if (c.chan>=6) break;
for (int i=0; i<4; i++) {
unsigned short baseAddr=chanOffs[c.chan]|opOffs[i];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -698,6 +732,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
chan[c.chan].ins=c.value;
break;
case DIV_CMD_PANNING: {
if (c.chan>5) c.chan=5;
if (c.value==0 && c.value2==0) {
chan[c.chan].pan=3;
} else {
@ -735,7 +770,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
}
break;
}
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
int destFreq=parent->calcBaseFreq(1,1,c.value2,false);
bool return2=false;
if (destFreq>chan[c.chan].baseFreq) {
@ -762,18 +797,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_SAMPLE_MODE: {
dacMode=c.value;
if (c.chan<5) c.chan=5;
chan[c.chan].dacMode=c.value;
rWrite(0x2b,c.value<<7);
break;
}
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
if (c.chan<5) c.chan=5;
chan[c.chan].sampleBank=c.value;
if (chan[c.chan].sampleBank>(parent->song.sample.size()/12)) {
chan[c.chan].sampleBank=parent->song.sample.size()/12;
}
break;
case DIV_CMD_SAMPLE_DIR: {
if (c.chan<5) c.chan=5;
chan[c.chan].dacDirection=c.value;
if (dumpWrites) addWrite(0xffff0003,chan[c.chan].dacDirection);
break;
}
case DIV_CMD_LEGATO: {
if (c.chan==5 && chan[c.chan].furnaceDac && dacMode) {
if (c.chan>=5 && chan[c.chan].furnaceDac && chan[c.chan].dacMode) {
chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false);
} else {
chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
@ -783,16 +826,19 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_LFO: {
if (c.chan>=6) break;
lfoValue=(c.value&7)|((c.value>>4)<<3);
rWrite(0x22,lfoValue);
break;
}
case DIV_CMD_FM_FB: {
if (c.chan>=6) break;
chan[c.chan].state.fb=c.value&7;
rWrite(chanOffs[c.chan]+ADDR_FB_ALG,(chan[c.chan].state.alg&7)|(chan[c.chan].state.fb<<3));
break;
}
case DIV_CMD_FM_MULT: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.mult=c.value2&15;
@ -800,6 +846,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_TL: {
if (c.chan>=6) break;
unsigned short baseAddr=chanOffs[c.chan]|opOffs[orderedOps[c.value]];
DivInstrumentFM::Operator& op=chan[c.chan].state.op[orderedOps[c.value]];
op.tl=c.value2;
@ -815,6 +862,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -831,6 +879,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RS: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -847,6 +896,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_AM: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -863,6 +913,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -879,6 +930,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SL: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -895,6 +947,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_RR: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -911,6 +964,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_D2R: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -927,6 +981,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_DT: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -943,6 +998,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_SSG: {
if (c.chan>=6) break;
if (c.value<0) {
for (int i=0; i<4; i++) {
DivInstrumentFM::Operator& op=chan[c.chan].state.op[i];
@ -959,6 +1015,7 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
break;
}
case DIV_CMD_FM_HARD_RESET:
if (c.chan>=6) break;
chan[c.chan].hardReset=c.value;
break;
case DIV_ALWAYS_SET_VOLUME:
@ -1007,7 +1064,7 @@ void DivPlatformGenesis::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);
@ -1057,18 +1114,18 @@ void DivPlatformGenesis::reset() {
}
lastBusy=60;
dacMode=0;
dacPeriod=0;
dacPos=0;
dacRate=0;
dacDelay=0;
dacReady=true;
dacSample=-1;
sampleBank=0;
lfoValue=8;
softPCMTimer=0;
extMode=false;
if (softPCM) {
chan[5].dacMode=true;
chan[6].dacMode=true;
}
// normal sample direction
if (dumpWrites) addWrite(0xffff0003,0);
// LFO
immWrite(0x22,lfoValue);
@ -1088,7 +1145,7 @@ bool DivPlatformGenesis::keyOffAffectsPorta(int ch) {
}
void DivPlatformGenesis::notifyInsChange(int ins) {
for (int i=0; i<6; i++) {
for (int i=0; i<10; i++) {
if (chan[i].ins==ins) {
chan[i].insChanged=true;
}
@ -1114,6 +1171,10 @@ void DivPlatformGenesis::setYMFM(bool use) {
useYMFM=use;
}
void DivPlatformGenesis::setSoftPCM(bool value) {
softPCM=value;
}
void DivPlatformGenesis::setFlags(unsigned int flags) {
switch (flags) {
case 1: chipClock=COLOR_PAL*12.0/7.0; break;
@ -1152,6 +1213,11 @@ int DivPlatformGenesis::init(DivEngine* p, int channels, int sugRate, unsigned i
fm_ymfm=NULL;
setFlags(flags);
for (int i=0; i<128; i++) {
dacVolTable[127-i]=128*pow(10.0f,(float)(-i)*0.75f/20.0f);
}
dacVolTable[0]=0;
reset();
return 10;
}

View file

@ -41,10 +41,24 @@ class DivPlatformGenesis: public DivDispatch {
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, furnaceDac, inPorta, hardReset;
int vol, outVol;
unsigned char pan;
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
bool dacDirection;
bool dacReversed;
unsigned char sampleBank;
void macroInit(DivInstrument* which) {
std.init(which);
pitch2=0;
}
bool getDacDirection() {
return dacReversed^dacDirection;
}
Channel():
freqH(0),
freqL(0),
@ -65,7 +79,18 @@ class DivPlatformGenesis: public DivDispatch {
inPorta(false),
hardReset(false),
vol(0),
pan(3) {}
outVol(0),
pan(3),
dacMode(false),
dacPeriod(0),
dacRate(0),
dacPos(0),
dacSample(-1),
dacDelay(0),
dacReady(true),
dacDirection(false),
dacReversed(false),
sampleBank(0) {}
};
Channel chan[10];
DivDispatchOscBuffer* oscBuf[10];
@ -86,24 +111,21 @@ class DivPlatformGenesis: public DivDispatch {
DivYM2612Interface iface;
unsigned char regPool[512];
bool dacMode;
int dacPeriod;
int dacRate;
unsigned int dacPos;
int dacSample;
int dacDelay;
bool dacReady;
unsigned char sampleBank;
unsigned char lfoValue;
bool extMode, useYMFM;
int softPCMTimer;
bool extMode, softPCM, useYMFM;
bool ladder;
short oldWrites[512];
short pendingWrites[512];
unsigned char dacVolTable[128];
friend void putDispatchChan(void*,int,int);
inline void processDAC();
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
void acquire_ymfm(short* bufL, short* bufR, size_t start, size_t len);
@ -126,6 +148,7 @@ class DivPlatformGenesis: public DivDispatch {
void setFlags(unsigned int flags);
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void setSoftPCM(bool value);
int getPortaFloor(int ch);
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);

View file

@ -156,16 +156,16 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
case DIV_CMD_SAMPLE_MODE: {
// not ignored actually!
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
dacMode=c.value;
chan[5].dacMode=c.value;
rWrite(0x2b,c.value<<7);
}
break;
}
case DIV_CMD_SAMPLE_BANK:
if (!parent->song.ignoreDACModeOutsideIntendedChannel) {
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
sampleBank=parent->song.sample.size()/12;
chan[5].sampleBank=c.value;
if (chan[5].sampleBank>(parent->song.sample.size()/12)) {
chan[5].sampleBank=parent->song.sample.size()/12;
}
}
break;
@ -484,7 +484,7 @@ void DivPlatformGenesisExt::forceIns() {
chan[i].freqChanged=true;
}
}
if (dacMode) {
if (chan[5].dacMode) {
rWrite(0x2b,0x80);
}
immWrite(0x22,lfoValue);

View file

@ -25,6 +25,7 @@
#define WRITE_VOLUME(ch,v) rWrite(0x20+(ch<<3),(v))
#define WRITE_FEEDBACK(ch,v) rWrite(0x21+(ch<<3),(v))
#define WRITE_OUTPUT(ch,v) rWrite(0x22+(ch<<3),(v))
#define WRITE_LFSR(ch,v) rWrite(0x23+(ch<<3),(v))
#define WRITE_BACKUP(ch,v) rWrite(0x24+(ch<<3),(v))
#define WRITE_CONTROL(ch,v) rWrite(0x25+(ch<<3),(v))
@ -151,13 +152,18 @@ void DivPlatformLynx::acquire(short* bufL, short* bufR, size_t start, size_t len
DivSample* s=parent->getSample(chan[i].sample);
if (s!=NULL) {
if (isMuted[i]) {
WRITE_VOLUME(i,0);
WRITE_OUTPUT(i,0);
chan[i].samplePos++;
} else {
WRITE_VOLUME(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
WRITE_OUTPUT(i,(s->data8[chan[i].samplePos++]*chan[i].outVol)>>7);
}
if (chan[i].samplePos>=(int)s->samples) {
chan[i].sample=-1;
if (s->loopStart>=0 && s->loopStart<(int)s->samples) {
chan[i].samplePos=s->loopStart;
} else {
chan[i].sample=-1;
}
}
}
}
@ -176,8 +182,8 @@ void DivPlatformLynx::tick(bool sysTick) {
chan[i].outVol=((chan[i].vol&127)*MIN(64,chan[i].std.vol.val))>>6;
} else {
chan[i].outVol=((chan[i].vol&127)*MIN(127,chan[i].std.vol.val))>>7;
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
}
WRITE_VOLUME(i,(isMuted[i]?0:(chan[i].outVol&127)));
}
if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
@ -244,11 +250,6 @@ void DivPlatformLynx::tick(bool sysTick) {
}
}
chan[i].sampleFreq=off*parent->calcFreq(chan[i].sampleBaseFreq,chan[i].pitch,false,2,chan[i].pitch2,1,1);
WRITE_FEEDBACK(i,0);
WRITE_LFSR(i,0);
WRITE_OTHER(i,0);
WRITE_CONTROL(i,0x18);
WRITE_BACKUP(i,2);
} else {
if (chan[i].lfsr >= 0) {
WRITE_LFSR(i, (chan[i].lfsr&0xff));
@ -300,7 +301,8 @@ int DivPlatformLynx::dispatch(DivCommand c) {
}
case DIV_CMD_NOTE_OFF:
chan[c.chan].active=false;
WRITE_VOLUME(c.chan, 0);
WRITE_VOLUME(c.chan,0);
WRITE_CONTROL(c.chan,0);
chan[c.chan].macroInit(NULL);
if (chan[c.chan].pcm) {
chan[c.chan].pcm=false;

View file

@ -23,13 +23,18 @@
#include <string.h>
#include <math.h>
#define rWrite(v) if (!skipRegisterWrites) {writes.emplace(0,v); if (dumpWrites) {addWrite(0,v);} }
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
const char** DivPlatformMSM6295::getRegisterSheet() {
return NULL;
}
const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
switch (effect) {
case 0x20:
return "20xx: Set chip output rate (0: clock/132; 1: clock/165)";
break;
}
return NULL;
}
@ -42,7 +47,28 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
if (delay<=0) {
if (!writes.empty()) {
QueuedWrite& w=writes.front();
msm->command_w(w.val);
switch (w.addr) {
case 0: // command
msm->command_w(w.val);
break;
case 8: // chip clock select (VGM)
case 9:
case 10:
case 11:
break;
case 12: // rate select
msm->ss_w(!w.val);
break;
case 14: // enable bankswitch
break;
case 15: // set bank base
break;
case 16: // switch bank
case 17:
case 18:
case 19:
break;
}
writes.pop();
delay=32;
}
@ -92,9 +118,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
} else {
break;
}
@ -107,9 +133,9 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
}
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
chan[c.chan].sample=12*sampleBank+c.value%12;
rWrite((8<<c.chan)); // turn off
rWrite(0x80|chan[c.chan].sample); // set phrase
rWrite((16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
rWrite(0,(8<<c.chan)); // turn off
rWrite(0,0x80|chan[c.chan].sample); // set phrase
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
}
break;
}
@ -117,14 +143,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].macroInit(NULL);
break;
case DIV_CMD_NOTE_OFF_ENV:
chan[c.chan].keyOff=true;
chan[c.chan].keyOn=false;
chan[c.chan].active=false;
rWrite((8<<c.chan)); // turn off
rWrite(0,(8<<c.chan)); // turn off
chan[c.chan].std.release();
break;
case DIV_CMD_ENV_RELEASE:
@ -153,6 +179,10 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
case DIV_CMD_NOTE_PORTA: {
return 2;
}
case DIV_CMD_SAMPLE_FREQ:
rateSel=c.value;
rWrite(12,!rateSel);
break;
case DIV_CMD_SAMPLE_BANK:
sampleBank=c.value;
if (sampleBank>(parent->song.sample.size()/12)) {
@ -190,6 +220,7 @@ void DivPlatformMSM6295::forceIns() {
for (int i=0; i<4; i++) {
chan[i].insChanged=true;
}
rWrite(12,!rateSel);
}
void* DivPlatformMSM6295::getChanState(int ch) {
@ -219,6 +250,7 @@ void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
void DivPlatformMSM6295::reset() {
while (!writes.empty()) writes.pop();
msm->reset();
msm->ss_w(false);
if (dumpWrites) {
addWrite(0xffffffff,0);
}
@ -232,6 +264,7 @@ void DivPlatformMSM6295::reset() {
}
sampleBank=0;
rateSel=false;
delay=0;
}
@ -240,6 +273,10 @@ bool DivPlatformMSM6295::keyOffAffectsArp(int ch) {
return false;
}
float DivPlatformMSM6295::getPostAmp() {
return 3.0f;
}
void DivPlatformMSM6295::notifyInsChange(int ins) {
for (int i=0; i<4; i++) {
if (chan[i].ins==ins) {
@ -302,12 +339,21 @@ void DivPlatformMSM6295::renderSamples() {
}
void DivPlatformMSM6295::setFlags(unsigned int flags) {
if (flags&1) {
chipClock=8448000;
} else {
chipClock=8000000;
switch (flags) {
case 0:
chipClock=4000000/4;
break;
case 1:
chipClock=4224000/4;
break;
case 2:
chipClock=4000000;
break;
case 3:
chipClock=4224000;
break;
}
rate=chipClock/((flags&2)?6:24);
rate=chipClock/3;
for (int i=0; i<4; i++) {
isMuted[i]=false;
oscBuf[i]->rate=rate/22;

View file

@ -101,6 +101,7 @@ class DivPlatformMSM6295: public DivDispatch {
int delay, updateOsc;
bool extMode;
bool rateSel;
short oldWrites[512];
short pendingWrites[512];
@ -119,6 +120,7 @@ class DivPlatformMSM6295: public DivDispatch {
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
bool keyOffAffectsArp(int ch);
float getPostAmp();
void notifyInsChange(int ins);
void notifyInsDeletion(void* ins);
void poke(unsigned int addr, unsigned short val);

View file

@ -735,6 +735,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
if (chan[c.chan].sample>=0 && chan[c.chan].sample<parent->song.sampleLen) {
DivSample* s=parent->getSample(chan[c.chan].sample);
immWrite(8,0);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;
@ -770,6 +771,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
}
DivSample* s=parent->getSample(12*sampleBank+c.value%12);
immWrite(8,0);
immWrite(7,0x01); // reset
immWrite(9,(s->offB>>2)&0xff);
immWrite(10,(s->offB>>10)&0xff);
int end=s->offB+s->lengthB-1;

View file

@ -42,6 +42,7 @@ const char* DivPlatformSMS::getEffectName(unsigned char effect) {
}
void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_t len) {
int o=0;
for (size_t h=start; h<start+len; h++) {
if (!writes.empty()) {
unsigned char w=writes.front();
@ -64,7 +65,10 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
YMPSG_Clock(&sn_nuked);
bufL[h]=YMPSG_GetOutput(&sn_nuked)*8192.0;
o=YMPSG_GetOutput(&sn_nuked);
if (o<-32768) o=-32768;
if (o>32767) o=32767;
bufL[h]=o;
/*
for (int i=0; i<4; i++) {
if (isMuted[i]) {