YM2610(B): use f-num/block baseFreq calculation

also allow for different octave boundary f-nums depending on chip clock
This commit is contained in:
tildearrow 2022-05-10 02:22:08 -05:00
parent da71a6a804
commit 5e14177e4e
12 changed files with 198 additions and 170 deletions

View file

@ -993,19 +993,24 @@ double DivEngine::calcBaseFreq(double clock, double divider, int note, bool peri
} }
unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) { unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, int note, int bits) {
double tuning=song.tuning;
if (tuning<400.0) tuning=400.0;
if (tuning>500.0) tuning=500.0;
int bf=calcBaseFreq(clock,divider,note,false); int bf=calcBaseFreq(clock,divider,note,false);
int boundaryBottom=tuning*pow(2.0,0.25)*(divider/clock);
int boundaryTop=2.0*tuning*pow(2.0,0.25)*(divider/clock);
int block=note/12; int block=note/12;
if (block<0) block=0; if (block<0) block=0;
if (block>7) block=7; if (block>7) block=7;
bf>>=block; bf>>=block;
if (bf<0) bf=0; if (bf<0) bf=0;
// octave boundaries // octave boundaries
while (bf>0 && bf<644 && block>0) { while (bf>0 && bf<boundaryBottom && block>0) {
bf<<=1; bf<<=1;
block--; block--;
} }
if (bf>1288) { if (bf>boundaryTop) {
while (block<7) { while (block<7 && bf>boundaryTop) {
bf>>=1; bf>>=1;
block++; block++;
} }
@ -1013,6 +1018,7 @@ unsigned short DivEngine::calcBaseFreqFNumBlock(double clock, double divider, in
bf=(1<<bits)-1; bf=(1<<bits)-1;
} }
} }
logV("f-num: %d block: %d",bf,block);
return bf|(block<<bits); return bf|(block<<bits);
} }

View file

@ -173,12 +173,14 @@ bool DivEngine::loadDMF(unsigned char* file, size_t len) {
ds.legacyVolumeSlides=false; ds.legacyVolumeSlides=false;
} }
// Neo Geo detune // Neo Geo detune is caused by Defle running Neo Geo at the wrong clock.
/*
if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT if (ds.system[0]==DIV_SYSTEM_YM2610 || ds.system[0]==DIV_SYSTEM_YM2610_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT || ds.system[0]==DIV_SYSTEM_YM2610_FULL || ds.system[0]==DIV_SYSTEM_YM2610_FULL_EXT
|| ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) { || ds.system[0]==DIV_SYSTEM_YM2610B || ds.system[0]==DIV_SYSTEM_YM2610B_EXT) {
ds.tuning=443.23; ds.tuning=443.23;
} }
*/
logI("reading module data..."); logI("reading module data...");
if (ds.version>0x0c) { if (ds.version>0x0c) {

View file

@ -725,6 +725,8 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
} }
break; break;
} }
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
@ -747,13 +749,13 @@ int DivPlatformGenesis::dispatch(DivCommand c) {
// check for octave boundary // check for octave boundary
// what the heck! // what the heck!
if (!chan[c.chan].portaPause) { if (!chan[c.chan].portaPause) {
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true; chan[c.chan].portaPause=true;
break; break;
} }
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true; chan[c.chan].portaPause=true;
break; break;
} }

View file

@ -127,6 +127,8 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11); int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
@ -148,18 +150,18 @@ int DivPlatformGenesisExt::dispatch(DivCommand c) {
} }
// what the heck! // what the heck!
if (!opChan[ch].portaPause) { if (!opChan[ch].portaPause) {
if ((newFreq&0x7ff)>1288 && (newFreq&0xf800)<0x3800) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) { if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(644)|((newFreq+0x800)&0xf800); opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true; opChan[ch].portaPause=true;
break; break;
} else { } else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800); newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
} }
} }
if ((newFreq&0x7ff)<644 && (newFreq&0xf800)>0) { if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) { if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(1287)|((newFreq-0x800)&0xf800); opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true; opChan[ch].portaPause=true;
break; break;
} else { } else {

View file

@ -456,7 +456,7 @@ double DivPlatformYM2610::NOTE_OPNB(int ch, int note) {
return NOTE_PERIODIC(note); return NOTE_PERIODIC(note);
} }
// FM // FM
return NOTE_FREQUENCY(note); return NOTE_FNUM_BLOCK(note,11);
} }
double DivPlatformYM2610::NOTE_ADPCMB(int note) { double DivPlatformYM2610::NOTE_ADPCMB(int note) {
@ -559,15 +559,15 @@ void DivPlatformYM2610::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
} else { } else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
} }
} }
chan[i].freqChanged=true; chan[i].freqChanged=true;
} else { } else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) { if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
@ -742,11 +742,20 @@ void DivPlatformYM2610::tick(bool sysTick) {
for (int i=0; i<4; i++) { for (int i=0; i<4; i++) {
if (i==1 && extMode) continue; if (i==1 && extMode) continue;
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2);
if (chan[i].freq>262143) chan[i].freq=262143; int block=(chan[i].baseFreq&0xf800)>>11;
int freqt=toFreq(chan[i].freq)+chan[i].pitch2; if (fNum<0) fNum=0;
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); if (fNum>2047) {
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); while (block<7) {
fNum>>=1;
block++;
}
if (fNum>2047) fNum=2047;
}
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);
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (chan[i].keyOn) { if (chan[i].keyOn) {
@ -756,47 +765,6 @@ void DivPlatformYM2610::tick(bool sysTick) {
} }
} }
int DivPlatformYM2610::octave(int freq) {
if (freq>=622.0f*128) {
return 128;
} else if (freq>=622.0f*64) {
return 64;
} else if (freq>=622.0f*32) {
return 32;
} else if (freq>=622.0f*16) {
return 16;
} else if (freq>=622.0f*8) {
return 8;
} else if (freq>=622.0f*4) {
return 4;
} else if (freq>=622.0f*2) {
return 2;
} else {
return 1;
}
return 1;
}
int DivPlatformYM2610::toFreq(int freq) {
if (freq>=622.0f*128) {
return 0x3800|((freq>>7)&0x7ff);
} else if (freq>=622.0f*64) {
return 0x3000|((freq>>6)&0x7ff);
} else if (freq>=622.0f*32) {
return 0x2800|((freq>>5)&0x7ff);
} else if (freq>=622.0f*16) {
return 0x2000|((freq>>4)&0x7ff);
} else if (freq>=622.0f*8) {
return 0x1800|((freq>>3)&0x7ff);
} else if (freq>=622.0f*4) {
return 0x1000|((freq>>2)&0x7ff);
} else if (freq>=622.0f*2) {
return 0x800|((freq>>1)&0x7ff);
} else {
return freq&0x7ff;
}
}
int DivPlatformYM2610::dispatch(DivCommand c) { int DivPlatformYM2610::dispatch(DivCommand c) {
if (c.chan>3 && c.chan<7) { if (c.chan>3 && c.chan<7) {
c.chan-=4; c.chan-=4;
@ -928,7 +896,7 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
chan[c.chan].portaPause=false; chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
@ -1048,33 +1016,48 @@ int DivPlatformYM2610::dispatch(DivCommand c) {
} }
break; break;
} }
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int destFreq=NOTE_FREQUENCY(c.value2); int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) { if (newFreq>=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} else { } else {
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) { if (newFreq<=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} }
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) { if (!chan[c.chan].portaPause) {
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true; chan[c.chan].portaPause=true;
break; break;
} }
} }
chan[c.chan].baseFreq=newFreq;
chan[c.chan].portaPause=false; chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
if (return2) return 2; chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break; break;
} }
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:

View file

@ -61,7 +61,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
struct Channel { struct Channel {
DivInstrumentFM state; DivInstrumentFM state;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, note, ins; int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen; unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles; signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
@ -80,6 +80,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
baseFreq(0), baseFreq(0),
pitch(0), pitch(0),
pitch2(0), pitch2(0),
portaPauseFreq(0),
note(0), note(0),
ins(-1), ins(-1),
psgMode(1), psgMode(1),
@ -125,8 +126,6 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
short oldWrites[512]; short oldWrites[512];
short pendingWrites[512]; short pendingWrites[512];
int octave(int freq);
int toFreq(int freq);
double NOTE_OPNB(int ch, int note); double NOTE_OPNB(int ch, int note);
double NOTE_ADPCMB(int note); double NOTE_ADPCMB(int note);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);

View file

@ -436,7 +436,7 @@ double DivPlatformYM2610B::NOTE_OPNB(int ch, int note) {
return NOTE_PERIODIC(note); return NOTE_PERIODIC(note);
} }
// FM // FM
return NOTE_FREQUENCY(note); return NOTE_FNUM_BLOCK(note,11);
} }
double DivPlatformYM2610B::NOTE_ADPCMB(int note) { double DivPlatformYM2610B::NOTE_ADPCMB(int note) {
@ -538,15 +538,15 @@ void DivPlatformYM2610B::tick(bool sysTick) {
if (chan[i].std.arp.had) { if (chan[i].std.arp.had) {
if (!chan[i].inPorta) { if (!chan[i].inPorta) {
if (chan[i].std.arp.mode) { if (chan[i].std.arp.mode) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].std.arp.val,11);
} else { } else {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+(signed char)chan[i].std.arp.val); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note+(signed char)chan[i].std.arp.val,11);
} }
} }
chan[i].freqChanged=true; chan[i].freqChanged=true;
} else { } else {
if (chan[i].std.arp.mode && chan[i].std.arp.finished) { if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note); chan[i].baseFreq=NOTE_FNUM_BLOCK(chan[i].note,11);
chan[i].freqChanged=true; chan[i].freqChanged=true;
} }
} }
@ -720,11 +720,20 @@ void DivPlatformYM2610B::tick(bool sysTick) {
for (int i=0; i<6; i++) { for (int i=0; i<6; i++) {
if (i==2 && extMode) continue; if (i==2 && extMode) continue;
if (chan[i].freqChanged) { if (chan[i].freqChanged) {
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,octave(chan[i].baseFreq),chan[i].pitch2); int fNum=parent->calcFreq(chan[i].baseFreq&0x7ff,chan[i].pitch,false,4,chan[i].pitch2);
if (chan[i].freq>262143) chan[i].freq=262143; int block=(chan[i].baseFreq&0xf800)>>11;
int freqt=toFreq(chan[i].freq)+chan[i].pitch2; if (fNum<0) fNum=0;
immWrite(chanOffs[i]+ADDR_FREQH,freqt>>8); if (fNum>2047) {
immWrite(chanOffs[i]+ADDR_FREQ,freqt&0xff); while (block<7) {
fNum>>=1;
block++;
}
if (fNum>2047) fNum=2047;
}
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);
chan[i].freqChanged=false; chan[i].freqChanged=false;
} }
if (chan[i].keyOn) { if (chan[i].keyOn) {
@ -734,47 +743,6 @@ void DivPlatformYM2610B::tick(bool sysTick) {
} }
} }
int DivPlatformYM2610B::octave(int freq) {
if (freq>=622.0f*128) {
return 128;
} else if (freq>=622.0f*64) {
return 64;
} else if (freq>=622.0f*32) {
return 32;
} else if (freq>=622.0f*16) {
return 16;
} else if (freq>=622.0f*8) {
return 8;
} else if (freq>=622.0f*4) {
return 4;
} else if (freq>=622.0f*2) {
return 2;
} else {
return 1;
}
return 1;
}
int DivPlatformYM2610B::toFreq(int freq) {
if (freq>=622.0f*128) {
return 0x3800|((freq>>7)&0x7ff);
} else if (freq>=622.0f*64) {
return 0x3000|((freq>>6)&0x7ff);
} else if (freq>=622.0f*32) {
return 0x2800|((freq>>5)&0x7ff);
} else if (freq>=622.0f*16) {
return 0x2000|((freq>>4)&0x7ff);
} else if (freq>=622.0f*8) {
return 0x1800|((freq>>3)&0x7ff);
} else if (freq>=622.0f*4) {
return 0x1000|((freq>>2)&0x7ff);
} else if (freq>=622.0f*2) {
return 0x800|((freq>>1)&0x7ff);
} else {
return freq&0x7ff;
}
}
int DivPlatformYM2610B::dispatch(DivCommand c) { int DivPlatformYM2610B::dispatch(DivCommand c) {
if (c.chan>5 && c.chan<9) { if (c.chan>5 && c.chan<9) {
c.chan-=6; c.chan-=6;
@ -906,7 +874,7 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
chan[c.chan].insChanged=false; chan[c.chan].insChanged=false;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value); chan[c.chan].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
chan[c.chan].portaPause=false; chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
chan[c.chan].note=c.value; chan[c.chan].note=c.value;
@ -1026,33 +994,48 @@ int DivPlatformYM2610B::dispatch(DivCommand c) {
} }
break; break;
} }
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int destFreq=NOTE_FREQUENCY(c.value2); int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
if (chan[c.chan].portaPause) {
chan[c.chan].baseFreq=chan[c.chan].portaPauseFreq;
}
if (destFreq>chan[c.chan].baseFreq) { if (destFreq>chan[c.chan].baseFreq) {
newFreq=chan[c.chan].baseFreq+c.value*octave(chan[c.chan].baseFreq); newFreq=chan[c.chan].baseFreq+c.value;
if (newFreq>=destFreq) { if (newFreq>=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} else { } else {
newFreq=chan[c.chan].baseFreq-c.value*octave(chan[c.chan].baseFreq); newFreq=chan[c.chan].baseFreq-c.value;
if (newFreq<=destFreq) { if (newFreq<=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} }
// check for octave boundary
// what the heck!
if (!chan[c.chan].portaPause) { if (!chan[c.chan].portaPause) {
if (octave(chan[c.chan].baseFreq)!=octave(newFreq)) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
chan[c.chan].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
chan[c.chan].portaPause=true;
break;
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
chan[c.chan].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
chan[c.chan].portaPause=true; chan[c.chan].portaPause=true;
break; break;
} }
} }
chan[c.chan].baseFreq=newFreq;
chan[c.chan].portaPause=false; chan[c.chan].portaPause=false;
chan[c.chan].freqChanged=true; chan[c.chan].freqChanged=true;
if (return2) return 2; chan[c.chan].baseFreq=newFreq;
if (return2) {
chan[c.chan].inPorta=false;
return 2;
}
break; break;
} }
case DIV_CMD_SAMPLE_BANK: case DIV_CMD_SAMPLE_BANK:

View file

@ -35,7 +35,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
struct Channel { struct Channel {
DivInstrumentFM state; DivInstrumentFM state;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, note, ins; int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
unsigned char psgMode, autoEnvNum, autoEnvDen; unsigned char psgMode, autoEnvNum, autoEnvDen;
signed char konCycles; signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
@ -54,6 +54,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
baseFreq(0), baseFreq(0),
pitch(0), pitch(0),
pitch2(0), pitch2(0),
portaPauseFreq(0),
note(0), note(0),
ins(-1), ins(-1),
psgMode(1), psgMode(1),
@ -98,8 +99,6 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
short oldWrites[512]; short oldWrites[512];
short pendingWrites[512]; short pendingWrites[512];
int octave(int freq);
int toFreq(int freq);
double NOTE_OPNB(int ch, int note); double NOTE_OPNB(int ch, int note);
double NOTE_ADPCMB(int note); double NOTE_ADPCMB(int note);
friend void putDispatchChan(void*,int,int); friend void putDispatchChan(void*,int,int);

View file

@ -120,31 +120,51 @@ int DivPlatformYM2610BExt::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2); int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) { if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) { if (newFreq>=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} else { } else {
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) { if (newFreq<=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} }
// what the heck!
if (!opChan[ch].portaPause) { if (!opChan[ch].portaPause) {
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true; opChan[ch].portaPause=true;
break; break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
} }
} }
opChan[ch].baseFreq=newFreq;
opChan[ch].portaPause=false; opChan[ch].portaPause=false;
opChan[ch].freqChanged=true; opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2; if (return2) return 2;
break; break;
} }
@ -364,14 +384,20 @@ void DivPlatformYM2610BExt::tick(bool sysTick) {
unsigned char writeMask=2; unsigned char writeMask=2;
if (extMode) for (int i=0; i<4; i++) { if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) { if (opChan[i].freqChanged) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
if (opChan[i].freq>262143) opChan[i].freq=262143; int block=(opChan[i].baseFreq&0xf800)>>11;
int freqt=toFreq(opChan[i].freq); if (fNum<0) fNum=0;
opChan[i].freqH=freqt>>8; if (fNum>2047) {
opChan[i].freqL=freqt&0xff; while (block<7) {
immWrite(opChanOffsH[i],opChan[i].freqH); fNum>>=1;
immWrite(opChanOffsL[i],opChan[i].freqL); block++;
opChan[i].freqChanged=false; }
if (fNum>2047) fNum=2047;
}
opChan[i].freq=(block<<11)|fNum;
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
immWrite(opChanOffsH[i],opChan[i].freq>>8);
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
} }
writeMask|=opChan[i].active<<(4+i); writeMask|=opChan[i].active<<(4+i);
if (opChan[i].keyOn) { if (opChan[i].keyOn) {

View file

@ -25,12 +25,12 @@ class DivPlatformYM2610BExt: public DivPlatformYM2610B {
struct OpChannel { struct OpChannel {
DivMacroInt std; DivMacroInt std;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, ins; int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles; signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
int vol; int vol;
unsigned char pan; unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
}; };
OpChannel opChan[4]; OpChannel opChan[4];
bool isOpMuted[4]; bool isOpMuted[4];

View file

@ -63,7 +63,7 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
opChan[ch].insChanged=false; opChan[ch].insChanged=false;
if (c.value!=DIV_NOTE_NULL) { if (c.value!=DIV_NOTE_NULL) {
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
opChan[ch].portaPause=false; opChan[ch].portaPause=false;
opChan[ch].freqChanged=true; opChan[ch].freqChanged=true;
} }
@ -120,36 +120,56 @@ int DivPlatformYM2610Ext::dispatch(DivCommand c) {
break; break;
} }
case DIV_CMD_NOTE_PORTA: { case DIV_CMD_NOTE_PORTA: {
int destFreq=NOTE_FREQUENCY(c.value2); int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false);
int boundaryTop=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,12,false);
int destFreq=NOTE_FNUM_BLOCK(c.value2,11);
int newFreq; int newFreq;
bool return2=false; bool return2=false;
if (opChan[ch].portaPause) {
opChan[ch].baseFreq=opChan[ch].portaPauseFreq;
}
if (destFreq>opChan[ch].baseFreq) { if (destFreq>opChan[ch].baseFreq) {
newFreq=opChan[ch].baseFreq+c.value*octave(opChan[ch].baseFreq); newFreq=opChan[ch].baseFreq+c.value;
if (newFreq>=destFreq) { if (newFreq>=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} else { } else {
newFreq=opChan[ch].baseFreq-c.value*octave(opChan[ch].baseFreq); newFreq=opChan[ch].baseFreq-c.value;
if (newFreq<=destFreq) { if (newFreq<=destFreq) {
newFreq=destFreq; newFreq=destFreq;
return2=true; return2=true;
} }
} }
// what the heck!
if (!opChan[ch].portaPause) { if (!opChan[ch].portaPause) {
if (octave(opChan[ch].baseFreq)!=octave(newFreq)) { if ((newFreq&0x7ff)>boundaryTop && (newFreq&0xf800)<0x3800) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=(boundaryBottom)|((newFreq+0x800)&0xf800);
opChan[ch].portaPause=true; opChan[ch].portaPause=true;
break; break;
} else {
newFreq=(newFreq>>1)|((newFreq+0x800)&0xf800);
}
}
if ((newFreq&0x7ff)<boundaryBottom && (newFreq&0xf800)>0) {
if (parent->song.fbPortaPause) {
opChan[ch].portaPauseFreq=newFreq=(boundaryTop-1)|((newFreq-0x800)&0xf800);
opChan[ch].portaPause=true;
break;
} else {
newFreq=(newFreq<<1)|((newFreq-0x800)&0xf800);
}
} }
} }
opChan[ch].baseFreq=newFreq;
opChan[ch].portaPause=false; opChan[ch].portaPause=false;
opChan[ch].freqChanged=true; opChan[ch].freqChanged=true;
opChan[ch].baseFreq=newFreq;
if (return2) return 2; if (return2) return 2;
break; break;
} }
case DIV_CMD_LEGATO: { case DIV_CMD_LEGATO: {
opChan[ch].baseFreq=NOTE_FREQUENCY(c.value); opChan[ch].baseFreq=NOTE_FNUM_BLOCK(c.value,11);
opChan[ch].freqChanged=true; opChan[ch].freqChanged=true;
break; break;
} }
@ -364,14 +384,20 @@ void DivPlatformYM2610Ext::tick(bool sysTick) {
unsigned char writeMask=2; unsigned char writeMask=2;
if (extMode) for (int i=0; i<4; i++) { if (extMode) for (int i=0; i<4; i++) {
if (opChan[i].freqChanged) { if (opChan[i].freqChanged) {
opChan[i].freq=parent->calcFreq(opChan[i].baseFreq,opChan[i].pitch,false,octave(opChan[i].baseFreq),opChan[i].pitch2); int fNum=parent->calcFreq(opChan[i].baseFreq&0x7ff,opChan[i].pitch,false,4,opChan[i].pitch2);
if (opChan[i].freq>262143) opChan[i].freq=262143; int block=(opChan[i].baseFreq&0xf800)>>11;
int freqt=toFreq(opChan[i].freq); if (fNum<0) fNum=0;
opChan[i].freqH=freqt>>8; if (fNum>2047) {
opChan[i].freqL=freqt&0xff; while (block<7) {
immWrite(opChanOffsH[i],opChan[i].freqH); fNum>>=1;
immWrite(opChanOffsL[i],opChan[i].freqL); block++;
opChan[i].freqChanged=false; }
if (fNum>2047) fNum=2047;
}
opChan[i].freq=(block<<11)|fNum;
if (opChan[i].freq>0x3fff) opChan[i].freq=0x3fff;
immWrite(opChanOffsH[i],opChan[i].freq>>8);
immWrite(opChanOffsL[i],opChan[i].freq&0xff);
} }
writeMask|=opChan[i].active<<(4+i); writeMask|=opChan[i].active<<(4+i);
if (opChan[i].keyOn) { if (opChan[i].keyOn) {

View file

@ -25,12 +25,12 @@ class DivPlatformYM2610Ext: public DivPlatformYM2610 {
struct OpChannel { struct OpChannel {
DivMacroInt std; DivMacroInt std;
unsigned char freqH, freqL; unsigned char freqH, freqL;
int freq, baseFreq, pitch, pitch2, ins; int freq, baseFreq, pitch, pitch2, portaPauseFreq, ins;
signed char konCycles; signed char konCycles;
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause; bool active, insChanged, freqChanged, keyOn, keyOff, portaPause;
int vol; int vol;
unsigned char pan; unsigned char pan;
OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {} OpChannel(): freqH(0), freqL(0), freq(0), baseFreq(0), pitch(0), pitch2(0), portaPauseFreq(0), ins(-1), active(false), insChanged(true), freqChanged(false), keyOn(false), keyOff(false), portaPause(false), vol(0), pan(3) {}
}; };
OpChannel opChan[4]; OpChannel opChan[4];
bool isOpMuted[4]; bool isOpMuted[4];