Merge branch 'master' into master
This commit is contained in:
commit
eb95024fb9
321 changed files with 78416 additions and 69816 deletions
|
|
@ -761,6 +761,25 @@ int DivPlatformArcade::dispatch(DivCommand c) {
|
|||
immWrite(0x19,0x80|pmDepth);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -113,14 +113,15 @@ const unsigned char dacLogTableAY[256]={
|
|||
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
|
||||
};
|
||||
|
||||
void DivPlatformAY8910::runDAC() {
|
||||
void DivPlatformAY8910::runDAC(int runRate) {
|
||||
if (runRate==0) runRate=dacRate;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&8) && chan[i].dac.sample!=-1) {
|
||||
chan[i].dac.period+=chan[i].dac.rate;
|
||||
bool end=false;
|
||||
bool changed=false;
|
||||
int prevOut=chan[i].dac.out;
|
||||
while (chan[i].dac.period>dacRate && !end) {
|
||||
while (chan[i].dac.period>runRate && !end) {
|
||||
DivSample* s=parent->getSample(chan[i].dac.sample);
|
||||
if (s->samples<=0 || chan[i].dac.pos<0 || chan[i].dac.pos>=(int)s->samples) {
|
||||
chan[i].dac.sample=-1;
|
||||
|
|
@ -143,7 +144,7 @@ void DivPlatformAY8910::runDAC() {
|
|||
end=true;
|
||||
break;
|
||||
}
|
||||
chan[i].dac.period-=dacRate;
|
||||
chan[i].dac.period-=runRate;
|
||||
}
|
||||
if (changed && !end) {
|
||||
if (!isMuted[i]) {
|
||||
|
|
@ -154,44 +155,79 @@ void DivPlatformAY8910::runDAC() {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::runTFX() {
|
||||
if (selCore) return;
|
||||
void DivPlatformAY8910::runTFX(int runRate) {
|
||||
/*
|
||||
developer's note: if you are checking for intellivision
|
||||
make sure to add "&& selCore"
|
||||
because for some reason, the register remap doesn't work
|
||||
when the user uses AtomicSSG core
|
||||
*/
|
||||
float counterRatio=1.0;
|
||||
if (runRate!=0) counterRatio=(double)rate/(double)runRate;
|
||||
int timerPeriod, output;
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].active && (chan[i].curPSGMode.val&16) && !(chan[i].curPSGMode.val&8) && chan[i].tfx.mode!=-1) {
|
||||
chan[i].tfx.counter += 1;
|
||||
if (chan[i].tfx.mode == -1 && !isMuted[i]) {
|
||||
/*
|
||||
bug: if in the timer FX macro the user enables
|
||||
and then disables PWM while there is no volume macro
|
||||
there is now a random chance that the resulting output
|
||||
is silent or has volume set incorrectly
|
||||
i've tried to implement a fix, but it seems to be
|
||||
ineffective, so...
|
||||
TODO: actually implement a proper fix
|
||||
*/
|
||||
if (intellivision && chan[i].curPSGMode.getEnvelope()) {
|
||||
immWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||
continue;
|
||||
} else {
|
||||
immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
chan[i].tfx.counter += counterRatio;
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 0) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
chan[i].tfx.out ^= 1;
|
||||
output = MAX(0, ((chan[i].tfx.out) ? (chan[i].outVol&15) : (chan[i].tfx.lowBound-(15-chan[i].outVol))));
|
||||
output &= 15;
|
||||
output = ((chan[i].tfx.out) ? chan[i].outVol : (chan[i].tfx.lowBound-(15-chan[i].outVol)));
|
||||
// TODO: fix this stupid crackling noise that happens
|
||||
// everytime the volume changes
|
||||
output = (output <= 0) ? 0 : output; // underflow
|
||||
output = (output >= 15) ? 15 : output; // overflow
|
||||
output &= 15; // i don't know if i need this but i'm too scared to remove it
|
||||
if (!isMuted[i]) {
|
||||
immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2));
|
||||
if (intellivision && selCore) {
|
||||
immWrite(0x0b+i,(output&0xc)<<2);
|
||||
} else {
|
||||
immWrite(0x08+i,output|(chan[i].curPSGMode.getEnvelope()<<2));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 1) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
if (!isMuted[i]) {
|
||||
immWrite(0xd, ayEnvMode);
|
||||
if (intellivision && selCore) {
|
||||
immWrite(0xa, ayEnvMode);
|
||||
} else {
|
||||
immWrite(0xd, ayEnvMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chan[i].tfx.counter >= chan[i].tfx.period && chan[i].tfx.mode == 2) {
|
||||
chan[i].tfx.counter = 0;
|
||||
}
|
||||
if (chan[i].tfx.mode == -1 && !isMuted[i]) {
|
||||
if (intellivision && chan[i].curPSGMode.getEnvelope()) {
|
||||
immWrite(0x08+i,(chan[i].outVol&0xc)<<2);
|
||||
} else {
|
||||
immWrite(0x08+i,(chan[i].outVol&15)|((chan[i].curPSGMode.getEnvelope())<<2));
|
||||
}
|
||||
chan[i].tfx.counter -= chan[i].tfx.period;
|
||||
}
|
||||
}
|
||||
if (chan[i].tfx.num > 0) {
|
||||
timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num;
|
||||
} else {
|
||||
timerPeriod = chan[i].freq*chan[i].tfx.den;
|
||||
}
|
||||
timerPeriod = chan[i].freq*chan[i].tfx.den/chan[i].tfx.num;
|
||||
} else {
|
||||
timerPeriod = chan[i].freq*chan[i].tfx.den;
|
||||
}
|
||||
if (chan[i].tfx.num > 0 && chan[i].tfx.den > 0) chan[i].tfx.period=timerPeriod+chan[i].tfx.offset;
|
||||
// stupid pitch correction because:
|
||||
// YM2149 half-clock and Sunsoft 5B: timers run an octave too high
|
||||
// on AtomicSSG core timers run 2 octaves too high
|
||||
if (clockSel || sunsoft) chan[i].tfx.period = chan[i].tfx.period * 2;
|
||||
if (selCore) chan[i].tfx.period = chan[i].tfx.period * 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -294,12 +330,9 @@ void DivPlatformAY8910::acquire(short** buf, size_t len) {
|
|||
|
||||
void DivPlatformAY8910::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
writes.clear();
|
||||
int rate=(int)(chipClock/sRate);
|
||||
for (size_t i=0; i<len; i++) {
|
||||
for (int h=0; h<rate; h++) {
|
||||
runDAC();
|
||||
runTFX();
|
||||
}
|
||||
runDAC(sRate);
|
||||
runTFX(sRate);
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite& w=writes.front();
|
||||
stream.push_back(DivDelayedWrite(i,w.addr,w.val));
|
||||
|
|
@ -388,12 +421,13 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
|||
if (chan[i].std.phaseReset.had) {
|
||||
if (chan[i].std.phaseReset.val==1) {
|
||||
chan[i].tfx.counter = 0;
|
||||
chan[i].tfx.out = 0;
|
||||
if (chan[i].nextPSGMode.val&8) {
|
||||
if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||
//if (dumpWrites) addWrite(0xffff0002+(i<<8),0);
|
||||
if (chan[i].dac.sample<0 || chan[i].dac.sample>=parent->song.sampleLen) {
|
||||
if (dumpWrites) {
|
||||
rWrite(0x08+i,0);
|
||||
addWrite(0xffff0000+(i<<8),chan[i].dac.sample);
|
||||
//addWrite(0xffff0000+(i<<8),chan[i].dac.sample);
|
||||
}
|
||||
if (chan[i].dac.setPos) {
|
||||
chan[i].dac.setPos=false;
|
||||
|
|
@ -483,7 +517,7 @@ void DivPlatformAY8910::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
chan[i].dac.rate=((double)rate*((sunsoft||clockSel)?8.0:16.0))/(double)(MAX(1,off*chan[i].freq));
|
||||
if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dac.rate);
|
||||
//if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dac.rate);
|
||||
}
|
||||
if (chan[i].freq<0) chan[i].freq=0;
|
||||
if (chan[i].freq>4095) chan[i].freq=4095;
|
||||
|
|
@ -570,12 +604,12 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
}
|
||||
if (chan[c.chan].dac.sample<0 || chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dac.sample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
//if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
break;
|
||||
} else {
|
||||
if (dumpWrites) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample);
|
||||
//addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample);
|
||||
}
|
||||
}
|
||||
if (chan[c.chan].dac.setPos) {
|
||||
|
|
@ -603,10 +637,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
chan[c.chan].dac.sample=12*sampleBank+chan[c.chan].note%12;
|
||||
if (chan[c.chan].dac.sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].dac.sample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
//if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
break;
|
||||
} else {
|
||||
if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample);
|
||||
//if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dac.sample);
|
||||
}
|
||||
if (chan[c.chan].dac.setPos) {
|
||||
chan[c.chan].dac.setPos=false;
|
||||
|
|
@ -617,7 +651,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
chan[c.chan].dac.rate=parent->getSample(chan[c.chan].dac.sample)->rate*2048;
|
||||
if (dumpWrites) {
|
||||
rWrite(0x08+c.chan,0);
|
||||
addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dac.rate);
|
||||
//addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dac.rate);
|
||||
}
|
||||
chan[c.chan].dac.furnaceDAC=false;
|
||||
}
|
||||
|
|
@ -652,7 +686,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].dac.sample=-1;
|
||||
if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
//if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0);
|
||||
chan[c.chan].nextPSGMode.val&=~8;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].active=false;
|
||||
|
|
@ -726,12 +760,10 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
break;
|
||||
}
|
||||
case DIV_CMD_STD_NOISE_MODE:
|
||||
if (c.value&0xf0 && !(chan[c.chan].nextPSGMode.val&8)) {
|
||||
chan[c.chan].nextPSGMode.val|=16;
|
||||
chan[c.chan].tfx.mode = (c.value&3);
|
||||
}
|
||||
if (!(chan[c.chan].nextPSGMode.val&8)) {
|
||||
if (c.value<16) {
|
||||
chan[c.chan].nextPSGMode.val|=16;
|
||||
chan[c.chan].tfx.mode=(((c.value&0xf0)>>4)&3)-1;
|
||||
if ((c.value&15)<16) {
|
||||
chan[c.chan].nextPSGMode.val=(c.value+1)&7;
|
||||
chan[c.chan].nextPSGMode.val|=chan[c.chan].curPSGMode.val&16;
|
||||
if (chan[c.chan].active) {
|
||||
|
|
@ -805,9 +837,16 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
updateOutSel(true);
|
||||
immWrite(14+(c.value?1:0),(c.value?portBVal:portAVal));
|
||||
break;
|
||||
case DIV_CMD_AY_AUTO_PWM:
|
||||
chan[c.chan].tfx.offset=c.value;
|
||||
case DIV_CMD_AY_NOISE_MASK_AND:
|
||||
chan[c.chan].tfx.num=c.value>>4;
|
||||
chan[c.chan].tfx.den=c.value&15;
|
||||
break;
|
||||
case DIV_CMD_AY_AUTO_PWM: {
|
||||
// best way i could find to do signed :/
|
||||
signed char signVal=c.value;
|
||||
chan[c.chan].tfx.offset=signVal;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_SAMPLE_MODE:
|
||||
if (c.value>0) {
|
||||
chan[c.chan].nextPSGMode.val|=8;
|
||||
|
|
@ -828,7 +867,7 @@ int DivPlatformAY8910::dispatch(DivCommand c) {
|
|||
case DIV_CMD_SAMPLE_POS:
|
||||
chan[c.chan].dac.pos=c.value;
|
||||
chan[c.chan].dac.setPos=true;
|
||||
if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dac.pos);
|
||||
//if (dumpWrites) addWrite(0xffff0005,chan[c.chan].dac.pos);
|
||||
break;
|
||||
case DIV_CMD_MACRO_OFF:
|
||||
chan[c.chan].std.mask(c.value,true);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
} dac;
|
||||
|
||||
struct TFX {
|
||||
int period, counter, offset, den, num, mode, lowBound, out;
|
||||
int period;
|
||||
float counter;
|
||||
int offset, den, num, mode, lowBound, out;
|
||||
TFX():
|
||||
period(0),
|
||||
counter(0),
|
||||
|
|
@ -156,8 +158,8 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void runDAC();
|
||||
void runTFX();
|
||||
void runDAC(int runRate=0);
|
||||
void runTFX(int runRate=0);
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
|
|
|
|||
|
|
@ -651,7 +651,7 @@ void DivPlatformGB::reset() {
|
|||
immWrite(0x26,0x8f);
|
||||
lastPan=0xff;
|
||||
immWrite(0x25,procMute());
|
||||
immWrite(0x24,0x77);
|
||||
immWrite(0x24,0xff);
|
||||
|
||||
antiClickPeriodCount=0;
|
||||
antiClickWavePos=0;
|
||||
|
|
|
|||
|
|
@ -1463,6 +1463,26 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
if (c.chan>=6) break;
|
||||
chan[c.chan].hardReset=c.value;
|
||||
|
|
|
|||
|
|
@ -39,14 +39,25 @@ const char** DivPlatformMSM6295::getRegisterSheet() {
|
|||
}
|
||||
|
||||
u8 DivPlatformMSM6295::read_byte(u32 address) {
|
||||
if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) {
|
||||
if (adpcmMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (isBanked) {
|
||||
if (address<0x400) {
|
||||
return adpcmMem[(bank[(address>>8)&0x3]<<16)|(address&0x3ff)];
|
||||
unsigned int bankedAddress=(bank[(address>>8)&0x3]<<16)|(address&0x3ff);
|
||||
if (bankedAddress>=getSampleMemCapacity(0)) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmMem[bankedAddress&0xffffff];
|
||||
}
|
||||
return adpcmMem[(bank[(address>>16)&0x3]<<16)|(address&0xffff)];
|
||||
unsigned int bankedAddress=(bank[(address>>16)&0x3]<<16)|(address&0xffff);
|
||||
if (bankedAddress>=getSampleMemCapacity(0)) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmMem[bankedAddress&0xffffff];
|
||||
}
|
||||
if (address>=getSampleMemCapacity(0)) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmMem[address&0x3ffff];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct _nla_table nla_table;
|
|||
|
||||
#define CHIP_DIVIDER 16
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {doWrite(a,v); regPool[(a)&0x7f]=v; if (dumpWrites) {addWrite(a,v);} }
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite((a),v)); if (dumpWrites) {addWrite((a),v);} }
|
||||
|
||||
const char* regCheatSheetNES[]={
|
||||
"S0Volume", "4000",
|
||||
|
|
@ -86,10 +86,10 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
|
|||
unsigned char next=((unsigned char)s->data8[dacPos]+0x80)>>1; \
|
||||
if (dacAntiClickOn && dacAntiClick<next) { \
|
||||
dacAntiClick+=8; \
|
||||
rWrite(0x4011,dacAntiClick); \
|
||||
doWrite(0x4011,dacAntiClick); \
|
||||
} else { \
|
||||
dacAntiClickOn=false; \
|
||||
rWrite(0x4011,next); \
|
||||
doWrite(0x4011,next); \
|
||||
} \
|
||||
} \
|
||||
dacPos++; \
|
||||
|
|
@ -108,6 +108,13 @@ void DivPlatformNES::doWrite(unsigned short addr, unsigned char data) {
|
|||
void DivPlatformNES::acquire_puNES(short** buf, size_t len) {
|
||||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
doWrite(w.addr,w.val);
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
apu_tick(nes,NULL);
|
||||
nes->apu.odd_cycle=!nes->apu.odd_cycle;
|
||||
|
|
@ -134,6 +141,13 @@ void DivPlatformNES::acquire_NSFPlay(short** buf, size_t len) {
|
|||
int out2[2];
|
||||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
doWrite(w.addr,w.val);
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
nes1_NP->Tick(8);
|
||||
nes2_NP->TickFrameSequence(8);
|
||||
|
|
@ -161,6 +175,13 @@ void DivPlatformNES::acquire_NSFPlayE(short** buf, size_t len) {
|
|||
int out2[2];
|
||||
for (size_t i=0; i<len; i++) {
|
||||
doPCM;
|
||||
|
||||
if (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
doWrite(w.addr,w.val);
|
||||
regPool[w.addr&0x1f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
e1_NP->Tick(8);
|
||||
e2_NP->TickFrameSequence(8);
|
||||
|
|
@ -297,7 +318,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
if (chan[i].sweepChanged) {
|
||||
chan[i].sweepChanged=false;
|
||||
if (i==0) {
|
||||
//rWrite(16+i*5,chan[i].sweep);
|
||||
// rWrite(16+i*5,chan[i].sweep);
|
||||
}
|
||||
}
|
||||
if (i<3) if (chan[i].std.phaseReset.had) {
|
||||
|
|
@ -338,7 +359,7 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
//rWrite(16+i*5+2,8);
|
||||
// rWrite(16+i*5+2,8);
|
||||
if (i==2) { // triangle
|
||||
rWrite(0x4000+i*4,0x00);
|
||||
} else {
|
||||
|
|
@ -400,6 +421,34 @@ void DivPlatformNES::tick(bool sysTick) {
|
|||
logV("switching bank to %d",dpcmBank);
|
||||
if (dumpWrites) addWrite(0xffff0004,dpcmBank);
|
||||
}
|
||||
|
||||
// sample custom loop point...
|
||||
DivSample* lsamp=parent->getSample(dacSample);
|
||||
|
||||
// how it works:
|
||||
// when the initial sample info is written (see above) and playback is launched,
|
||||
// the parameters (start point in memory and length) are locked until sample end
|
||||
// is reached.
|
||||
|
||||
// thus, if we write new data after just several APU clock cycles, it will be used only when
|
||||
// sample finishes one full loop.
|
||||
|
||||
// thus we can write sample's loop point as "start address" and sample's looped part length
|
||||
// as "full sample length".
|
||||
|
||||
// APU will play full sample once and then repeatedly cycle through the looped part.
|
||||
|
||||
// sources:
|
||||
// https://www.nesdev.org/wiki/APU_DMC
|
||||
// https://www.youtube.com/watch?v=vB4P8x2Am6Y
|
||||
|
||||
if (lsamp->loopEnd>lsamp->loopStart && goingToLoop) {
|
||||
int loopStartAddr=sampleOffDPCM[dacSample]+(lsamp->loopStart>>3);
|
||||
int loopLen=(lsamp->loopEnd-lsamp->loopStart)>>3;
|
||||
|
||||
rWrite(0x4012,(loopStartAddr>>6)&0xff);
|
||||
rWrite(0x4013,(loopLen>>4)&0xff);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (nextDPCMFreq>=0) {
|
||||
|
|
@ -450,14 +499,14 @@ int DivPlatformNES::dispatch(DivCommand c) {
|
|||
}
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(c.value);
|
||||
dacSample=(int)ins->amiga.getSample(c.value);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
chan[c.chan].sampleNote=c.value;
|
||||
c.value=ins->amiga.getFreq(c.value);
|
||||
chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote;
|
||||
}
|
||||
} else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) {
|
||||
dacSample=ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
dacSample=(int)ins->amiga.getSample(chan[c.chan].sampleNote);
|
||||
if (ins->type==DIV_INS_AMIGA) {
|
||||
c.value=ins->amiga.getFreq(chan[c.chan].sampleNote);
|
||||
}
|
||||
|
|
@ -790,6 +839,7 @@ float DivPlatformNES::getPostAmp() {
|
|||
}
|
||||
|
||||
void DivPlatformNES::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformNES::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "sound/nes_nsfplay/nes_apu.h"
|
||||
#include "sound/nes_nsfplay/5e01_apu.h"
|
||||
#include "../../fixedQueue.h"
|
||||
|
||||
class DivPlatformNES: public DivDispatch {
|
||||
struct Channel: public SharedChannel<signed char> {
|
||||
|
|
@ -44,6 +45,13 @@ class DivPlatformNES: public DivDispatch {
|
|||
Channel chan[5];
|
||||
DivDispatchOscBuffer* oscBuf[5];
|
||||
bool isMuted[5];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,128> writes;
|
||||
int dacPeriod, dacRate, dpcmPos;
|
||||
unsigned int dacPos, dacAntiClick;
|
||||
int dacSample;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ uint8_t DivOPLAInterface::ymfm_external_read(ymfm::access_class type, uint32_t a
|
|||
if (adpcmBMem==NULL) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmBMem[address&0xffffff];
|
||||
return adpcmBMem[address&0x3ffff];
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -165,8 +165,12 @@ int DivPlatformSMS::snCalcFreq(int ch) {
|
|||
if (ch==3) CHIP_DIVIDER=noiseDivider;
|
||||
int easyStartingPeriod=16;
|
||||
int easyThreshold=round(128.0*12.0*log((chipClock/(easyStartingPeriod*CHIP_DIVIDER))/(0.0625*parent->song.tuning))/log(2.0))-384+64;
|
||||
if (parent->song.linearPitch==2 && easyNoise && chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2>(easyThreshold)) {
|
||||
int ret=(((easyStartingPeriod<<7))-(chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2-(easyThreshold)))>>7;
|
||||
int curFreq=chan[ch].baseFreq+chan[ch].pitch+chan[ch].pitch2+(chan[ch].arpOff<<7);
|
||||
if (chan[ch].fixedArp) {
|
||||
curFreq=chan[ch].baseNoteOverride<<7;
|
||||
}
|
||||
if (parent->song.linearPitch==2 && easyNoise && curFreq>easyThreshold) {
|
||||
int ret=(((easyStartingPeriod<<7))-(curFreq-(easyThreshold)))>>7;
|
||||
if (ret<0) ret=0;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@
|
|||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWrite(c,a,v) {rWrite((a)+(c)*16,v)}
|
||||
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v,d)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define chWriteDelay(c,a,v,d) {rWrite((a)+(c)*16,v,d)}
|
||||
#define sampleTableAddr(c) (sampleTableBase+(c)*4)
|
||||
#define waveTableAddr(c) (sampleTableBase+8*4+(c)*9*16)
|
||||
|
||||
|
|
@ -77,7 +79,7 @@ void DivPlatformSNES::acquire(short** buf, size_t len) {
|
|||
dsp.write(w.addr,w.val);
|
||||
regPool[w.addr&0x7f]=w.val;
|
||||
writes.pop();
|
||||
delay=(w.addr==0x5c)?8:1;
|
||||
delay=w.delay;
|
||||
}
|
||||
}
|
||||
dsp.set_output(out,1);
|
||||
|
|
@ -253,7 +255,18 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,koff);
|
||||
// TODO: improve
|
||||
if (antiClick) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (koff&(1<<i)) {
|
||||
chWrite(i,5,0);
|
||||
chWrite(i,7,0x9f);
|
||||
chan[i].shallWriteEnv=true;
|
||||
}
|
||||
}
|
||||
rWriteDelay(0x7e,0,64);
|
||||
}
|
||||
rWriteDelay(0x5c,koff,8);
|
||||
}
|
||||
if (writeControl) {
|
||||
unsigned char control=(noiseFreq&0x1f)|(echoOn?0:0x20);
|
||||
|
|
@ -314,10 +327,7 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (koff!=0) {
|
||||
rWrite(0x5c,0);
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
rWriteDelay(0x5c,0,8);
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].shallWriteVol) {
|
||||
|
|
@ -325,6 +335,9 @@ void DivPlatformSNES::tick(bool sysTick) {
|
|||
chan[i].shallWriteVol=false;
|
||||
}
|
||||
}
|
||||
if (kon!=0) {
|
||||
rWrite(0x4c,kon);
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSNES::dispatch(DivCommand c) {
|
||||
|
|
@ -841,6 +854,8 @@ void DivPlatformSNES::reset() {
|
|||
memcpy(sampleMem,copyOfSampleMem,65536);
|
||||
dsp.init(sampleMem);
|
||||
dsp.set_output(NULL,0);
|
||||
dsp.setupInterpolation(!interpolationOff);
|
||||
|
||||
memset(regPool,0,128);
|
||||
// this can't be 0 or channel 1 won't play
|
||||
// this can't be 0x100 either as that's used by SPC700 page 1 and the stack
|
||||
|
|
@ -1023,6 +1038,9 @@ void DivPlatformSNES::setFlags(const DivConfig& flags) {
|
|||
initEchoFIR[7]=flags.getInt("echoFilter7",0);
|
||||
|
||||
initEchoMask=flags.getInt("echoMask",0);
|
||||
|
||||
interpolationOff=flags.getBool("interpolationOff",false);
|
||||
antiClick=flags.getBool("antiClick",true);
|
||||
}
|
||||
|
||||
int DivPlatformSNES::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,8 @@ class DivPlatformSNES: public DivDispatch {
|
|||
bool writeEcho;
|
||||
bool writeDryVol;
|
||||
bool echoOn;
|
||||
bool interpolationOff;
|
||||
bool antiClick;
|
||||
|
||||
bool initEchoOn;
|
||||
signed char initEchoVolL;
|
||||
|
|
@ -81,8 +83,10 @@ class DivPlatformSNES: public DivDispatch {
|
|||
struct QueuedWrite {
|
||||
unsigned char addr;
|
||||
unsigned char val;
|
||||
QueuedWrite(): addr(0), val(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {}
|
||||
unsigned char delay;
|
||||
unsigned char padding;
|
||||
QueuedWrite(): addr(0), val(0), delay(0), padding(0) {}
|
||||
QueuedWrite(unsigned char a, unsigned char v, unsigned char d=0): addr(a), val(v), delay(d), padding(0) {}
|
||||
};
|
||||
FixedQueue<QueuedWrite,256> writes;
|
||||
|
||||
|
|
|
|||
|
|
@ -135,24 +135,30 @@ static short const gauss [512] =
|
|||
1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305,
|
||||
};
|
||||
|
||||
void SPC_DSP::setupInterpolation(bool interpolate){for(int i=0;i<voice_count;i++){m.voices[i].interpolate=interpolate;}}
|
||||
|
||||
inline int SPC_DSP::interpolate( voice_t const* v )
|
||||
{
|
||||
// Make pointers into gaussian based on fractional position between samples
|
||||
int offset = v->interp_pos >> 4 & 0xFF;
|
||||
short const* fwd = gauss + 255 - offset;
|
||||
short const* rev = gauss + offset; // mirror left half of gaussian
|
||||
if (v->interpolate) {
|
||||
int offset = v->interp_pos >> 4 & 0xFF;
|
||||
short const* fwd = gauss + 255 - offset;
|
||||
short const* rev = gauss + offset; // mirror left half of gaussian
|
||||
|
||||
int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
|
||||
int out;
|
||||
out = (fwd [ 0] * in [0]) >> 11;
|
||||
out += (fwd [256] * in [1]) >> 11;
|
||||
out += (rev [256] * in [2]) >> 11;
|
||||
out = (int16_t) out;
|
||||
out += (rev [ 0] * in [3]) >> 11;
|
||||
int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
|
||||
int out;
|
||||
out = (fwd [ 0] * in [0]) >> 11;
|
||||
out += (fwd [256] * in [1]) >> 11;
|
||||
out += (rev [256] * in [2]) >> 11;
|
||||
out = (int16_t) out;
|
||||
out += (rev [ 0] * in [3]) >> 11;
|
||||
|
||||
CLAMP16( out );
|
||||
out &= ~1;
|
||||
return out;
|
||||
CLAMP16( out );
|
||||
out &= ~1;
|
||||
return out;
|
||||
} else {
|
||||
return v->buf [(v->interp_pos >> 12) + v->buf_pos]; //Furnace addition -- no interpolation
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ public:
|
|||
// output buffer could hold.
|
||||
int sample_count() const;
|
||||
|
||||
// Furnace addition: disable/enable Gaussian interpolation
|
||||
void setupInterpolation(bool interpolate);
|
||||
|
||||
// Emulation
|
||||
|
||||
// Resets DSP to power-on state
|
||||
|
|
@ -122,6 +125,7 @@ public:
|
|||
int hidden_env; // used by GAIN mode 7, very obscure quirk
|
||||
uint8_t t_envx_out;
|
||||
sample_t out[2]; // Furnace addition, for per-channel oscilloscope
|
||||
bool interpolate; // Furnace addition, to disable interpolation
|
||||
};
|
||||
|
||||
// Furnace addition, gets a voice
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
// Chip revisions
|
||||
// 0: V 0.3.0
|
||||
// 1: V 47.0.0 (9-bit volume, phase reset on mute)
|
||||
// 2: V 47.0.2 (Pulse Width XOR on Saw and Triangle)
|
||||
|
||||
#include "vera_psg.h"
|
||||
|
||||
|
|
@ -88,8 +89,8 @@ render(struct VERA_PSG* psg, int16_t *left, int16_t *right)
|
|||
uint8_t v = 0;
|
||||
switch (ch->waveform) {
|
||||
case WF_PULSE: v = (ch->phase >> 10) > ch->pw ? 0 : 63; break;
|
||||
case WF_SAWTOOTH: v = ch->phase >> 11; break;
|
||||
case WF_TRIANGLE: v = (ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F); break;
|
||||
case WF_SAWTOOTH: v = (ch->phase >> 11) ^ (psg->chipType < 2 ? 0 : (ch->pw ^ 0x3f) & 0x3f); break;
|
||||
case WF_TRIANGLE: v = ((ch->phase & 0x10000) ? (~(ch->phase >> 10) & 0x3F) : ((ch->phase >> 10) & 0x3F)) ^ (psg->chipType < 2 ? 0 : (ch->pw ^ 0x3f) & 0x3f); break;
|
||||
case WF_NOISE: v = ch->noiseval; break;
|
||||
}
|
||||
int8_t sv = (v ^ 0x20);
|
||||
|
|
|
|||
|
|
@ -858,6 +858,9 @@ int DivPlatformTX81Z::dispatch(DivCommand c) {
|
|||
immWrite(0x17,0x80|pmDepth);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
// TODO: if OPZ supports op mask
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -532,7 +532,7 @@ void DivPlatformVERA::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformVERA::setFlags(const DivConfig& flags) {
|
||||
psg->chipType=flags.getInt("chipType",1);
|
||||
psg->chipType=flags.getInt("chipType",2);
|
||||
chipClock=25000000;
|
||||
CHECK_CUSTOM_CLOCK;
|
||||
rate=chipClock/512;
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ void DivPlatformYM2203::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -255,6 +256,7 @@ void DivPlatformYM2203::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -311,6 +313,16 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
fmOut[i]=0;
|
||||
}
|
||||
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
immWrite(i.addr&15,i.val);
|
||||
}
|
||||
ay->getRegisterWrites().clear();
|
||||
|
||||
while (true) {
|
||||
bool canWeWrite=fm_lle.prescaler_latch[1]&1;
|
||||
|
||||
|
|
@ -444,6 +456,10 @@ void DivPlatformYM2203::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::tick(bool sysTick) {
|
||||
// PSG
|
||||
ay->tick(sysTick);
|
||||
|
|
@ -1004,6 +1020,26 @@ int DivPlatformYM2203::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ class DivPlatformYM2203: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -325,6 +325,7 @@ void DivPlatformYM2608::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -440,6 +441,7 @@ void DivPlatformYM2608::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -680,6 +682,10 @@ void DivPlatformYM2608::acquire_lle(short** buf, size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::tick(bool sysTick) {
|
||||
// FM
|
||||
for (int i=0; i<6; i++) {
|
||||
|
|
@ -1537,6 +1543,26 @@ int DivPlatformYM2608::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ class DivPlatformYM2608: public DivPlatformOPN {
|
|||
|
||||
public:
|
||||
void acquire(short** buf, size_t len);
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
|
|
|
|||
|
|
@ -260,6 +260,7 @@ void DivPlatformYM2610::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -373,6 +374,7 @@ void DivPlatformYM2610::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1507,6 +1509,26 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -324,6 +324,7 @@ void DivPlatformYM2610B::acquire_combo(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -439,6 +440,7 @@ void DivPlatformYM2610B::acquire_ymfm(short** buf, size_t len) {
|
|||
for (size_t h=0; h<len; h++) {
|
||||
// AY -> OPN
|
||||
ay->runDAC();
|
||||
ay->runTFX(rate);
|
||||
ay->flushWrites();
|
||||
for (DivRegWrite& i: ay->getRegisterWrites()) {
|
||||
if (i.addr>15) continue;
|
||||
|
|
@ -1576,6 +1578,26 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_FM_OPMASK:
|
||||
if (c.chan>=psgChanOffs) break;
|
||||
switch (c.value>>4) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
chan[c.chan].opMask&=~(1<<((c.value>>4)-1));
|
||||
if (c.value&15) {
|
||||
chan[c.chan].opMask|=(1<<((c.value>>4)-1));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
chan[c.chan].opMask=c.value&15;
|
||||
break;
|
||||
}
|
||||
if (chan[c.chan].active) {
|
||||
chan[c.chan].opMaskChanged=true;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_FM_HARD_RESET:
|
||||
chan[c.chan].hardReset=c.value;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@ class DivPlatformYM2610Base: public DivPlatformOPN {
|
|||
}
|
||||
|
||||
public:
|
||||
void fillStream(std::vector<DivDelayedWrite>& stream, int sRate, size_t len) {
|
||||
ay->fillStream(stream,sRate,len);
|
||||
}
|
||||
|
||||
void reset() {
|
||||
writeADPCMAOff=0;
|
||||
writeADPCMAOn=0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue