Sync to master

This commit is contained in:
cam900 2022-09-25 18:32:34 +09:00
parent 0a49d4bfd0
commit 8e1ce1abb1
21 changed files with 238 additions and 3298 deletions

View File

@ -384,7 +384,6 @@ class DivEngine {
sample(-1),
wave(-1),
pos(0),
dir(false),
pBegin(-1),
pEnd(-1),
dir(false) {}

View File

@ -405,7 +405,7 @@ struct DivInstrumentAmiga {
reversed(0),
loopStart(-1),
loopEnd(-1),
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
loopMode(DIV_SAMPLE_LOOP_MAX) {}
};
short initSample;

View File

@ -27,7 +27,7 @@
#define NOTE_ES5506(c,note) (parent->calcBaseFreq(chipClock,chan[c].pcm.freqOffs,note,false))
#define rWrite(a,...) {if(!skipRegisterWrites) {hostIntf32.emplace(4,(a),__VA_ARGS__); }}
#define rRead(a,...) {hostIntf32.emplace(4,(a),__VA_ARGS__);}
#define rRead(a,st,...) {hostIntf32.emplace(st,4,(a),__VA_ARGS__);}
#define immWrite(a,...) {hostIntf32.emplace(4,(a),__VA_ARGS__);}
#define pageWrite(p,a,...) \
if (!skipRegisterWrites) { \
@ -47,13 +47,13 @@
rWrite((a),__VA_ARGS__); \
}
#define pageReadMask(p,pm,a,...) \
#define pageReadMask(p,pm,a,st,...) \
if (!skipRegisterWrites) { \
if ((curPage&(pm))!=((p)&(pm))) { \
curPage=(curPage&~(pm))|((p)&(pm)); \
rWrite(0xf,curPage,(pm)); \
} \
rRead((a),__VA_ARGS__); \
rRead(st,(a),__VA_ARGS__); \
}
@ -112,90 +112,16 @@ const char** DivPlatformES5506::getRegisterSheet() {
return regCheatSheetES5506;
}
const char* DivPlatformES5506::getEffectName(unsigned char effect) {
switch (effect) {
case 0x10:
return "10xx: Change waveform or sample, transwave index";
break;
case 0x11:
return "11xx: Set filter mode (00 to 03)";
break;
case 0x12:
return "120x: Set pause (bit 0)";
break;
case 0x13:
return "130x: Set transwave slice mode (bit 0)";
break;
case 0x14:
return "14xx: Set filter coefficient K1 low byte";
break;
case 0x15:
return "15xx: Set filter coefficient K1 high byte";
break;
case 0x16:
return "16xx: Set filter coefficient K2 low byte";
break;
case 0x17:
return "17xx: Set filter coefficient K2 high byte";
break;
case 0x18:
return "18xx: Set filter coefficient K1 slide up";
break;
case 0x19:
return "19xx: Set filter coefficient K1 slide down";
break;
case 0x1a:
return "1axx: Set filter coefficient K2 slide up";
break;
case 0x1b:
return "1bxx: Set filter coefficient K2 slide down";
break;
case 0x20:
return "20xx: Set envelope count (000 to 0FF)";
break;
case 0x21:
return "21xx: Set envelope count (100 to 1FF)";
break;
case 0x22:
return "22xx: Set envelope left volume ramp (signed)";
break;
case 0x23:
return "23xx: Set envelope right volume ramp (signed)";
break;
case 0x24:
return "24xx: Set envelope filter coefficient k1 ramp (signed)";
break;
case 0x25:
return "25xx: Set envelope filter coefficient k1 ramp (signed, slower)";
break;
case 0x26:
return "26xx: Set envelope filter coefficient k2 ramp (signed)";
break;
case 0x27:
return "27xx: Set envelope filter coefficient k2 ramp (signed, slower)";
break;
default:
if ((effect&0xf0)==0x30) {
return "3xxx: Set filter coefficient K1";
} else if ((effect&0xf0)==0x40) {
return "4xxx: Set filter coefficient K2";
} else if ((effect&0xf0)==0x50) {
return "5xxx: Set transwave slice point";
}
break;
}
return NULL;
}
void DivPlatformES5506::acquire(short* bufL, short* bufR, size_t start, size_t len) {
for (size_t h=start; h<start+len; h++) {
// convert 32 bit access to 8 bit host interface
while (!hostIntf32.empty()) {
QueuedHostIntf w=hostIntf32.front();
if (w.isRead && (w.read!=NULL)) {
hostIntf8.emplace(0,w.addr,w.read,w.mask);
hostIntf8.emplace(1,w.addr,w.read,w.mask);
hostIntf8.emplace(2,w.addr,w.read,w.mask);
hostIntf8.emplace(3,w.addr,w.read,w.mask,w.delay);
hostIntf8.emplace(w.state,0,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,1,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,2,w.addr,w.read,w.mask);
hostIntf8.emplace(w.state,3,w.addr,w.read,w.mask,w.delay);
} else {
hostIntf8.emplace(0,w.addr,w.val,w.mask);
hostIntf8.emplace(1,w.addr,w.val,w.mask);
@ -244,6 +170,7 @@ void DivPlatformES5506::e_pin(bool state)
if (w.delay>0) {
cycle+=w.delay;
}
queuedReadState.emplace(w.read,w.state);
isReaded=true;
} else {
isReaded=false;
@ -272,116 +199,127 @@ void DivPlatformES5506::e_pin(bool state)
}
}
}
if (isReaded) {
isReaded=false;
if (irqTrigger) {
irqTrigger=false;
if ((irqv&0x80)==0) {
unsigned char ch=irqv&0x1f;
if (chan[ch].isReverseLoop) { // Reversed loop
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0000:0x0040)|0x08,0x78);
chan[ch].isReverseLoop=false;
if (!queuedReadState.empty()) {
QueuedReadState w=queuedReadState.front();
const unsigned char state=w.state;
if (state&0x80) {
if (irqTrigger) {
if ((irqv&0x80)==0) {
queuedRead.emplace(irqv&0x1f);
}
if (chan[ch].transwaveIRQ) {
if ((chan[ch].cr&0x37)==0x34) { // IRQE = 1, BLE = 1, LPE = 0, LEI = 1
DivInstrument* ins=parent->getIns(chan[ch].ins);
if (!ins->amiga.useNoteMap && ins->amiga.transWave.enable) {
const int next=chan[ch].pcm.next;
if (next>=0 && next<(int)ins->amiga.transWaveMap.size()) {
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
int sample=transWaveInd.ind;
if (sample>=0 && sample<parent->song.sampleLen) {
chan[ch].pcm.index=sample;
chan[ch].transWave.ind=next;
DivSample* s=parent->getSample(sample);
// get frequency offset
double off=1.0;
double center=(double)s->centerRate;
if (center<1) {
off=1.0;
} else {
off=(double)center/8363.0;
}
// get loop mode, transwave loop
double loopStart=(double)s->loopStart;
double loopEnd=(double)s->loopEnd;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
loopMode=transWaveInd.loopMode;
} else if ((chan[ch].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
}
// get loop position
loopStart=(double)transWaveInd.loopStart;
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[ch].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[ch].transWave.slicePos(double(chan[ch].transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
// get reversed
bool reversed=ins->amiga.reversed;
if (transWaveInd.reversed!=2) {
reversed=transWaveInd.reversed;
}
const unsigned int start=s->offES5506<<10;
const unsigned int length=s->samples-1;
const unsigned int end=start+(length<<11);
const double nextFreqOffs=PITCH_OFFSET*off;
chan[ch].pcm.loopMode=loopMode;
chan[ch].pcm.reversed=reversed;
chan[ch].pcm.bank=(s->offES5506>>22)&3;
chan[ch].pcm.start=start;
chan[ch].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
chan[ch].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
chan[ch].pcm.end=end;
chan[ch].pcm.length=length;
pageWrite(0x20|ch,0x01,chan[ch].pcm.loopStart);
pageWrite(0x20|ch,0x02,chan[ch].pcm.loopEnd);
pageWrite(0x20|ch,0x03,(chan[ch].pcm.reversed)?chan[ch].pcm.loopEnd:chan[ch].pcm.loopStart);
unsigned int loopFlag=(chan[ch].pcm.bank<<14)|(chan[ch].pcm.reversed?0x0040:0x0000);
chan[ch].isReverseLoop=false;
switch (chan[ch].pcm.loopMode) {
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
default:
break;
case DIV_SAMPLE_LOOPMODE_FORWARD: // Foward loop
loopFlag|=0x0008;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
loopFlag|=0x0038;
chan[ch].isReverseLoop=true;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
loopFlag|=0x0018;
break;
}
// Set loop mode & Bank
pageWriteMask(0x00|ch,0x5f,0x00,loopFlag,0xfcfc);
if (chan[ch].pcm.nextFreqOffs!=nextFreqOffs) {
chan[ch].pcm.nextFreqOffs=nextFreqOffs;
chan[ch].noteChanged.offs=1;
}
irqTrigger=false;
}
} else {
unsigned char ch=w.state&0x1f;
if (chan[ch].transwaveIRQ) {
if ((chan[ch].cr&0x37)==0x34) { // IRQE = 1, BLE = 1, LPE = 0, LEI = 1
DivInstrument* ins=parent->getIns(chan[ch].ins);
if (!ins->amiga.useNoteMap && ins->amiga.transWave.enable) {
const int next=chan[ch].pcm.next;
if (next>=0 && next<(int)ins->amiga.transWaveMap.size()) {
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
int sample=transWaveInd.ind;
if (sample>=0 && sample<parent->song.sampleLen) {
chan[ch].pcm.index=sample;
chan[ch].transWave.ind=next;
DivSample* s=parent->getSample(sample);
// get frequency offset
double off=1.0;
double center=(double)s->centerRate;
if (center<1) {
off=1.0;
} else {
off=(double)center/8363.0;
}
// get loop mode, transwave loop
double loopStart=(double)s->loopStart;
double loopEnd=(double)s->loopEnd;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOP_MAX) {
loopMode=transWaveInd.loopMode;
} else if ((chan[ch].pcm.loopMode==DIV_SAMPLE_LOOP_MAX) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOP_PINGPONG;
}
// get loop position
loopStart=(double)transWaveInd.loopStart;
loopEnd=(double)transWaveInd.loopEnd;
if (ins->amiga.transWave.sliceEnable) { // sliced loop position?
chan[ch].transWave.updateSize(s->samples,loopStart,loopEnd);
chan[ch].transWave.slicePos(double(chan[ch].transWave.slice)/4095.0);
loopStart=transWaveInd.sliceStart;
loopEnd=transWaveInd.sliceEnd;
}
// get reversed
bool reversed=ins->amiga.reversed;
if (transWaveInd.reversed!=2) {
reversed=transWaveInd.reversed;
}
const unsigned int start=s->offES5506<<10;
const unsigned int length=s->samples-1;
const unsigned int end=start+(length<<11);
const double nextFreqOffs=PITCH_OFFSET*off;
chan[ch].pcm.loopMode=loopMode;
chan[ch].pcm.reversed=reversed;
chan[ch].pcm.bank=(s->offES5506>>22)&3;
chan[ch].pcm.start=start;
chan[ch].pcm.loopStart=(start+(unsigned int)(loopStart*2048.0))&0xfffff800;
chan[ch].pcm.loopEnd=(start+(unsigned int)((loopEnd-1.0)*2048.0))&0xffffff80;
chan[ch].pcm.end=end;
chan[ch].pcm.length=length;
pageWrite(0x20|ch,0x01,chan[ch].pcm.loopStart);
pageWrite(0x20|ch,0x02,chan[ch].pcm.loopEnd);
pageWrite(0x20|ch,0x03,(chan[ch].pcm.reversed)?chan[ch].pcm.loopEnd:chan[ch].pcm.loopStart);
unsigned int loopFlag=(chan[ch].pcm.bank<<14)|(chan[ch].pcm.reversed?0x0040:0x0000);
chan[ch].isReverseLoop=false;
switch (chan[ch].pcm.loopMode) {
case DIV_SAMPLE_LOOP_MAX: // One shot (no loop)
default:
break;
case DIV_SAMPLE_LOOP_FORWARD: // Foward loop
loopFlag|=0x0008;
break;
case DIV_SAMPLE_LOOP_BACKWARD: // Backward loop: IRQ enable
loopFlag|=0x0038;
chan[ch].isReverseLoop=true;
break;
case DIV_SAMPLE_LOOP_PINGPONG: // Pingpong loop: Hardware support
loopFlag|=0x0018;
break;
}
// Set loop mode & Bank
pageWriteMask(0x00|ch,0x5f,0x00,loopFlag,0xfcfc);
if (chan[ch].pcm.nextFreqOffs!=nextFreqOffs) {
chan[ch].pcm.nextFreqOffs=nextFreqOffs;
chan[ch].noteChanged.offs=1;
}
}
chan[ch].pcmChanged.changed=0;
}
chan[ch].pcmChanged.changed=0;
}
chan[ch].transwaveIRQ=false;
}
if (chan[ch].isTranswave) {
pageReadMask(0x00|ch,0x5f,0x00,&chan[ch].cr);
chan[ch].transwaveIRQ=true;
chan[ch].isTranswave=false;
}
chan[ch].transwaveIRQ=false;
}
}
queuedReadState.pop();
}
if (!queuedRead.empty()) {
unsigned char ch=queuedRead.front()&0x1f;
if (chan[ch].isReverseLoop) { // Reversed loop
pageWriteMask(0x00|ch,0x5f,0x00,(chan[ch].pcm.reversed?0x0000:0x0040)|0x08,0x78);
chan[ch].isReverseLoop=false;
}
if (chan[ch].isTranswave) {
pageReadMask(0x00|ch,0x5f,0x00,ch,&chan[ch].cr);
chan[ch].transwaveIRQ=true;
chan[ch].isTranswave=false;
}
queuedRead.pop();
}
isReaded=false;
}
void DivPlatformES5506::irqb(bool state) {
rRead(0x0e,&irqv,0x9f);
rRead(0x0e,0x80,&irqv,0x9f);
irqTrigger=true;
}
@ -392,7 +330,7 @@ void DivPlatformES5506::tick(bool sysTick) {
signed int k1=chan[i].k1Prev,k2=chan[i].k2Prev;
// volume/panning macros
if (chan[i].std.vol.had) {
const unsigned int nextVol=((chan[i].vol&0xff)*MIN(0xffff,(0xffff*chan[i].std.vol.val)/chan[i].volMacroMax))/0xff;
const unsigned int nextVol=VOL_SCALE_LOG((0xffff*chan[i].vol)/0xff,(0xffff*chan[i].std.vol.val)/chan[i].volMacroMax,0xffff);
if (chan[i].outVol!=nextVol) {
chan[i].outVol=nextVol;
if (!isMuted[i]) {
@ -401,7 +339,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
if (chan[i].std.panL.had) {
const unsigned int nextLVol=(((ins->es5506.lVol*(chan[i].lVol&0xff))/0xff)*MIN(0xffff,(0xffff*chan[i].std.panL.val)/chan[i].panMacroMax))/0xffff;
const unsigned int nextLVol=VOL_SCALE_LOG((0xffff*chan[i].lVol)/0xff,(0xffff*chan[i].std.panL.val)/chan[i].panMacroMax,0xffff);
if (chan[i].outLVol!=nextLVol) {
chan[i].outLVol=nextLVol;
if (!isMuted[i]) {
@ -410,7 +348,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
}
if (chan[i].std.panR.had) {
const unsigned int nextRVol=(((ins->es5506.rVol*(chan[i].rVol&0xff))/0xff)*MIN(0xffff,(0xffff*chan[i].std.panR.val)/chan[i].panMacroMax))/0xffff;
const unsigned int nextRVol=VOL_SCALE_LOG((0xffff*chan[i].rVol)/0xff,(0xffff*chan[i].std.panR.val)/chan[i].panMacroMax,0xffff);
if (chan[i].outRVol!=nextRVol) {
chan[i].outRVol=nextRVol;
if (!isMuted[i]) {
@ -458,18 +396,19 @@ void DivPlatformES5506::tick(bool sysTick) {
}
if (chan[i].std.ex1.had) {
switch (chan[i].std.ex1.mode) {
case 0: // relative
if (chan[i].k1Offs!=chan[i].std.ex1.val) {
chan[i].k1Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k1=1;
}
break;
case 1: // absolute
case 0: // absolute
if (chan[i].filter.k1!=(chan[i].std.ex1.val&0xffff)) {
chan[i].filter.k1=chan[i].std.ex1.val&0xffff;
chan[i].filterChanged.k1=1;
}
break;
case 1: // relative
if (chan[i].k1Offs!=chan[i].std.ex1.val) {
chan[i].k1Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k1=1;
}
break;
/*
case 2: { // delta
const signed int next_k1=CLAMP(chan[i].k1Offs+chan[i].std.ex1.val,-65535,65535);
if (chan[i].k1Offs!=next_k1) {
@ -478,24 +417,26 @@ void DivPlatformES5506::tick(bool sysTick) {
}
break;
}
*/
default:
break;
}
}
if (chan[i].std.ex2.had) {
switch (chan[i].std.ex2.mode) {
case 0: // relative
if (chan[i].k2Offs!=chan[i].std.ex1.val) {
chan[i].k2Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k2=1;
}
break;
case 1: // absolute
case 0: // absolute
if (chan[i].filter.k2!=(chan[i].std.ex2.val&0xffff)) {
chan[i].filter.k2=chan[i].std.ex2.val&0xffff;
chan[i].filterChanged.k2=1;
}
break;
case 1: // relative
if (chan[i].k2Offs!=chan[i].std.ex1.val) {
chan[i].k2Offs=chan[i].std.ex1.val;
chan[i].filterChanged.k2=1;
}
break;
/*
case 2: { // delta
const signed int next_k2=CLAMP(chan[i].k2Offs+chan[i].std.ex2.val,-65535,65535);
if (chan[i].k2Offs!=next_k2) {
@ -504,6 +445,7 @@ void DivPlatformES5506::tick(bool sysTick) {
}
break;
}
*/
default:
break;
}
@ -621,13 +563,13 @@ void DivPlatformES5506::tick(bool sysTick) {
if (chan[i].volChanged.changed) {
if (!isMuted[i]) { // calculate volume (16 bit)
if (chan[i].volChanged.lVol) {
chan[i].resLVol=(chan[i].outVol*chan[i].outLVol)/0xffff;
chan[i].resLVol=VOL_SCALE_LOG(chan[i].outVol,chan[i].outLVol,0xffff);
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x02,chan[i].resLVol);
}
}
if (chan[i].volChanged.rVol) {
chan[i].resRVol=(chan[i].outVol*chan[i].outRVol)/0xffff;
chan[i].resRVol=VOL_SCALE_LOG(chan[i].outVol,chan[i].outRVol,0xffff);
if (!chan[i].keyOn) {
pageWrite(0x00|i,0x04,chan[i].resRVol);
}
@ -655,11 +597,11 @@ void DivPlatformES5506::tick(bool sysTick) {
// get loop mode, transwave loop
double loopStart=(double)s->loopStart;
double loopEnd=(double)s->loopEnd;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOP_MAX) {
loopMode=transWaveInd.loopMode;
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOP_PINGPONG;
}
// get loop position
loopStart=(double)transWaveInd.loopStart;
@ -693,13 +635,13 @@ void DivPlatformES5506::tick(bool sysTick) {
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<120)) ||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (next>=0 && next<(int)ins->amiga.transWaveMap.size())) ||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (next>=0 && next<parent->song.sampleLen))) {
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[next];
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[next];
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[next];
int sample=next;
if (ins->amiga.transWave.enable) {
sample=transWaveInd.ind;
} else if (ins->amiga.useNoteMap) {
sample=noteMapind.ind;
sample=noteMapind.map;
}
if (sample>=0 && sample<parent->song.sampleLen) {
sampleVaild=true;
@ -723,12 +665,12 @@ void DivPlatformES5506::tick(bool sysTick) {
// get loop mode, transwave loop
double loopStart=(double)s->loopStart;
double loopEnd=(double)s->loopEnd;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
if (ins->amiga.transWave.enable) {
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOP_MAX) {
loopMode=transWaveInd.loopMode;
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
} else if ((chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOP_PINGPONG;
}
// get loop position
loopStart=(double)transWaveInd.loopStart;
@ -805,8 +747,8 @@ void DivPlatformES5506::tick(bool sysTick) {
}
if (chan[i].pcmChanged.position) {
if (!chan[i].keyOn) {
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX)?chan[i].pcm.start:chan[i].pcm.loopStart);
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX)?chan[i].pcm.end:chan[i].pcm.loopEnd);
}
chan[i].pcmChanged.position=0;
}
@ -815,17 +757,17 @@ void DivPlatformES5506::tick(bool sysTick) {
unsigned int loopFlag=(chan[i].pcm.bank<<14)|(chan[i].pcm.reversed?0x0040:0x0000);
chan[i].isReverseLoop=false;
switch (chan[i].pcm.loopMode) {
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
case DIV_SAMPLE_LOOP_MAX: // One shot (no loop)
default:
break;
case DIV_SAMPLE_LOOPMODE_FORWARD: // Foward loop
case DIV_SAMPLE_LOOP_FORWARD: // Foward loop
loopFlag|=0x0008;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
case DIV_SAMPLE_LOOP_BACKWARD: // Backward loop: IRQ enable
loopFlag|=0x0038;
chan[i].isReverseLoop=true;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
case DIV_SAMPLE_LOOP_PINGPONG: // Pingpong loop: Hardware support
loopFlag|=0x0018;
break;
}
@ -844,14 +786,14 @@ void DivPlatformES5506::tick(bool sysTick) {
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].filter.mode<<8),0x0300);
}
if (chan[i].filterChanged.k2) {
if (chan[i].std.ex2.mode!=1) { // Relative
if (chan[i].std.ex2.mode!=0) { // Relative
k2=CLAMP(chan[i].filter.k2+chan[i].k2Offs,0,65535);
} else {
k2=chan[i].filter.k2;
}
}
if (chan[i].filterChanged.k1) {
if (chan[i].std.ex1.mode!=1) { // Relative
if (chan[i].std.ex1.mode!=0) { // Relative
k1=CLAMP(chan[i].filter.k1+chan[i].k1Offs,0,65535);
} else {
k1=chan[i].filter.k1;
@ -921,8 +863,8 @@ void DivPlatformES5506::tick(bool sysTick) {
pageWrite(0x00|i,0x07,0xffff); // Set K1 and K2 to 0xffff
pageWrite(0x00|i,0x09,0xffff,~0,(chanMax+1)*4*2); // needs to 4 sample period delay
pageWrite(0x00|i,0x01,chan[i].freq);
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.start:chan[i].pcm.loopStart);
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT)?chan[i].pcm.end:chan[i].pcm.loopEnd);
pageWrite(0x20|i,0x01,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX)?chan[i].pcm.start:chan[i].pcm.loopStart);
pageWrite(0x20|i,0x02,(chan[i].pcm.loopMode==DIV_SAMPLE_LOOP_MAX)?chan[i].pcm.end:chan[i].pcm.loopEnd);
// initialize envelope
pageWrite(0x00|i,0x03,((unsigned char)chan[i].envelope.lVRamp)<<8);
pageWrite(0x00|i,0x05,((unsigned char)chan[i].envelope.rVRamp)<<8);
@ -930,14 +872,14 @@ void DivPlatformES5506::tick(bool sysTick) {
pageWrite(0x00|i,0x08,(((unsigned char)chan[i].envelope.k2Ramp)<<8)|(chan[i].envelope.k2Slow?1:0));
// initialize filter
pageWriteMask(0x00|i,0x5f,0x00,(chan[i].pcm.bank<<14)|(chan[i].filter.mode<<8),0xc300);
if ((chan[i].std.ex2.mode!=1) && (chan[i].std.ex2.had)) {
if ((chan[i].std.ex2.mode!=0) && (chan[i].std.ex2.had)) {
k2=CLAMP(chan[i].filter.k2+chan[i].k2Offs,0,65535);
} else {
k2=chan[i].filter.k2;
}
pageWrite(0x00|i,0x07,k2);
chan[i].k2Prev=k2;
if ((chan[i].std.ex1.mode!=1) && (chan[i].std.ex1.had)) {
if ((chan[i].std.ex1.mode!=0) && (chan[i].std.ex1.had)) {
k1=CLAMP(chan[i].filter.k1+chan[i].k1Offs,0,65535);
} else {
k1=chan[i].filter.k1;
@ -949,17 +891,17 @@ void DivPlatformES5506::tick(bool sysTick) {
unsigned int loopFlag=chan[i].pcm.reversed?0x0040:0x0000;
chan[i].isReverseLoop=false;
switch (chan[i].pcm.loopMode) {
case DIV_SAMPLE_LOOPMODE_ONESHOT: // One shot (no loop)
case DIV_SAMPLE_LOOP_MAX: // One shot (no loop)
default:
break;
case DIV_SAMPLE_LOOPMODE_FORWARD: // Foward loop
case DIV_SAMPLE_LOOP_FORWARD: // Foward loop
loopFlag|=0x0008;
break;
case DIV_SAMPLE_LOOPMODE_BACKWARD: // Backward loop: IRQ enable
case DIV_SAMPLE_LOOP_BACKWARD: // Backward loop: IRQ enable
loopFlag|=0x0038;
chan[i].isReverseLoop=true;
break;
case DIV_SAMPLE_LOOPMODE_PINGPONG: // Pingpong loop: Hardware support
case DIV_SAMPLE_LOOP_PINGPONG: // Pingpong loop: Hardware support
loopFlag|=0x0018;
break;
}
@ -1001,13 +943,13 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (((ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (c.value>=0 && c.value<120)) ||
((!ins->amiga.useNoteMap && ins->amiga.transWave.enable) && (ins->amiga.transWave.ind>=0 && ins->amiga.transWave.ind<(int)ins->amiga.transWaveMap.size())) ||
((!ins->amiga.useNoteMap && !ins->amiga.transWave.enable) && (ins->amiga.initSample>=0 && ins->amiga.initSample<parent->song.sampleLen))) {
DivInstrumentAmiga::NoteMap& noteMapind=ins->amiga.noteMap[c.value];
DivInstrumentAmiga::SampleMap& noteMapind=ins->amiga.noteMap[c.value];
DivInstrumentAmiga::TransWaveMap& transWaveInd=ins->amiga.transWaveMap[ins->amiga.transWave.ind];
int sample=ins->amiga.initSample;
if (ins->amiga.transWave.enable) {
sample=transWaveInd.ind;
} else if (ins->amiga.useNoteMap) {
sample=noteMapind.ind;
sample=noteMapind.map;
}
if (sample>=0 && sample<parent->song.sampleLen) {
sampleVaild=true;
@ -1033,12 +975,12 @@ int DivPlatformES5506::dispatch(DivCommand c) {
// get loop mode, transwave loop
double loopStart=(double)s->loopStart;
double loopEnd=(double)s->loopEnd;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOPMODE_ONESHOT;
DivSampleLoopMode loopMode=s->isLoopable()?s->loopMode:DIV_SAMPLE_LOOP_MAX;
if (ins->amiga.transWave.enable) {
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT) {
if (transWaveInd.loopMode!=DIV_SAMPLE_LOOP_MAX) {
loopMode=transWaveInd.loopMode;
} else if ((chan[c.chan].pcm.loopMode==DIV_SAMPLE_LOOPMODE_ONESHOT) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOPMODE_PINGPONG;
} else if ((chan[c.chan].pcm.loopMode==DIV_SAMPLE_LOOP_MAX) || (!s->isLoopable())) { // default
loopMode=DIV_SAMPLE_LOOP_PINGPONG;
}
// get loop position
loopStart=(double)transWaveInd.loopStart;
@ -1093,10 +1035,10 @@ int DivPlatformES5506::dispatch(DivCommand c) {
chan[c.chan].outVol=(0xffff*chan[c.chan].vol)/0xff;
}
if (!chan[c.chan].std.panL.will) {
chan[c.chan].outLVol=(ins->es5506.lVol*chan[c.chan].lVol)/0xff;
chan[c.chan].outLVol=(0xffff*chan[c.chan].lVol)/0xff;
}
if (!chan[c.chan].std.panR.will) {
chan[c.chan].outRVol=(ins->es5506.rVol*chan[c.chan].rVol)/0xff;
chan[c.chan].outRVol=(0xffff*chan[c.chan].rVol)/0xff;
}
chan[c.chan].active=true;
chan[c.chan].keyOn=true;
@ -1142,7 +1084,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (chan[c.chan].lVol!=(unsigned int)(c.value)) {
chan[c.chan].lVol=c.value;
if (!chan[c.chan].std.panL.has) {
chan[c.chan].outLVol=(ins->es5506.lVol*c.value)/0xff;
chan[c.chan].outLVol=(0xffff*c.value)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged.lVol=1;
}
@ -1152,7 +1094,7 @@ int DivPlatformES5506::dispatch(DivCommand c) {
if (chan[c.chan].rVol!=(unsigned int)(c.value2)) {
chan[c.chan].rVol=c.value2;
if (!chan[c.chan].std.panR.has) {
chan[c.chan].outRVol=(ins->es5506.rVol*c.value2)/0xff;
chan[c.chan].outRVol=(0xffff*c.value2)/0xff;
if (!isMuted[c.chan]) {
chan[c.chan].volChanged.rVol=1;
}
@ -1336,6 +1278,8 @@ DivMacroInt* DivPlatformES5506::getChanMacroInt(int ch) {
void DivPlatformES5506::reset() {
while (!hostIntf32.empty()) hostIntf32.pop();
while (!hostIntf8.empty()) hostIntf8.pop();
while (!queuedRead.empty()) queuedRead.pop();
while (!queuedReadState.empty()) queuedReadState.pop();
for (int i=0; i<32; i++) {
chan[i]=DivPlatformES5506::Channel();
chan[i].std.setEngine(parent);

View File

@ -27,7 +27,7 @@
#include <queue>
#include "../macroInt.h"
#include "../sample.h"
#include "sound/es550x/es5506.hpp"
#include "vgsound_emu/src/es550x/es5506.hpp"
class DivPlatformES5506: public DivDispatch, public es550x_intf {
struct Channel {
@ -60,7 +60,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
length(0),
loopStart(0),
loopEnd(0),
loopMode(DIV_SAMPLE_LOOPMODE_ONESHOT) {}
loopMode(DIV_SAMPLE_LOOP_MAX) {}
} pcm;
int freq, baseFreq, nextFreq, pitch, pitch2, note, nextNote, currNote, ins, wave;
unsigned int volMacroMax, panMacroMax;
@ -213,6 +213,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
signed short* sampleMem; // ES5506 uses 16 bit data bus for samples
size_t sampleMemLen;
struct QueuedHostIntf {
unsigned char state;
unsigned char step;
unsigned char addr;
unsigned int val;
@ -221,6 +222,7 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
unsigned short delay;
bool isRead;
QueuedHostIntf(unsigned char s, unsigned char a, unsigned int v, unsigned int m=(unsigned int)(~0), unsigned short d=0):
state(0),
step(s),
addr(a),
val(v),
@ -228,7 +230,8 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
read(NULL),
delay(0),
isRead(false) {}
QueuedHostIntf(unsigned char s, unsigned char a, unsigned int* r, unsigned int m=(unsigned int)(~0), unsigned short d=0):
QueuedHostIntf(unsigned char st, unsigned char s, unsigned char a, unsigned int* r, unsigned int m=(unsigned int)(~0), unsigned short d=0):
state(st),
step(s),
addr(a),
val(0),
@ -237,8 +240,17 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
delay(d),
isRead(true) {}
};
struct QueuedReadState {
unsigned int* read;
unsigned char state;
QueuedReadState(unsigned int* r, unsigned char s):
read(r),
state(s) {}
};
std::queue<QueuedHostIntf> hostIntf32;
std::queue<QueuedHostIntf> hostIntf8;
std::queue<unsigned char> queuedRead;
std::queue<QueuedReadState> queuedReadState;
int cycle, curPage;
unsigned char maskedVal;
unsigned int irqv;
@ -288,7 +300,6 @@ class DivPlatformES5506: public DivDispatch, public es550x_intf {
virtual size_t getSampleMemUsage(int index = 0) override;
virtual void renderSamples() override;
virtual const char** getRegisterSheet() override;
virtual const char* getEffectName(unsigned char effect) override;
virtual int init(DivEngine* parent, int channels, int sugRate, unsigned int flags) override;
virtual void quit() override;
DivPlatformES5506():

View File

@ -1,453 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504 emulation core
see es550x.cpp for more info
*/
#include "es5504.hpp"
// Internal functions
void es5504_core::tick()
{
m_voice_update = false;
m_voice_end = false;
// /CAS, E
if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock
{
// /CAS
if (m_cas.tick())
{
// /CAS high, E low: get sample address
if (m_cas.falling_edge())
{
// /CAS low, E low: fetch sample
if (!m_e.current_edge())
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
// E
if (m_clkin.falling_edge()) // falling edge triggers E clock
{
if (m_e.tick())
{
m_intf.e_pin(m_e.current_edge());
if (m_e.rising_edge()) // Host access
{
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
voice_tick();
}
if (m_e.falling_edge()) // Voice memory
{
m_host_intf.m_host_access = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
if (m_e.current_edge()) // Host interface
{
if (m_host_intf.m_host_access)
{
if (m_host_intf.m_rw && (m_e.cycle() == 2)) // Read
{
m_hd = read(m_ha);
m_host_intf.m_host_access = false;
}
else if ((!m_host_intf.m_rw) && (m_e.cycle() == 2)) // Write
write(m_ha, m_hd);
}
}
else if (!m_e.current_edge())
{
if (m_e.cycle() == 2)
{
// reset host access state
m_hd = 0;
m_host_intf.m_host_access_strobe = false;
}
}
}
}
}
// less cycle accurate, but less CPU heavy routine
void es5504_core::tick_perf()
{
m_voice_update = false;
m_voice_end = false;
// update
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
}
void es5504_core::voice_tick()
{
// Voice updates every 2 E clock cycle (= 1 CHSTRB cycle or 4 BCLK clock cycle)
m_voice_update = bitfield(m_voice_fetch++, 0);
if (m_voice_update)
{
// Update voice
m_voice[m_voice_cycle].tick(m_voice_cycle);
// Refresh output (Multiplexed analog output)
m_ch[m_voice[m_voice_cycle].m_cr.ca] = m_voice[m_voice_cycle].m_ch;
if ((++m_voice_cycle) > std::min<u8>(24, m_active)) // ~ 25 voices
{
m_voice_end = true;
m_voice_cycle = 0;
}
m_voice_fetch = 0;
}
}
void es5504_core::voice_t::fetch(u8 voice, u8 cycle)
{
m_alu.m_sample[cycle] = m_host.m_intf.read_sample(voice, bitfield(m_cr.ca, 0, 3), bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer));
}
void es5504_core::voice_t::tick(u8 voice)
{
m_ch = 0;
// Filter execute
m_filter.tick(m_alu.interpolation());
if (m_alu.busy())
{
// Send to output
m_ch = ((sign_ext<s32>(m_filter.m_o4_1, 16) >> 3) * m_volume) >> 12; // Analog multiplied in real chip, 13/12 bit ladder DAC
// ALU execute
if (m_alu.tick())
{
m_alu.loop_exec();
}
// ADC check
adc_exec();
}
// Update IRQ
m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice);
}
// ADC; Correct?
void es5504_core::voice_t::adc_exec()
{
if (m_cr.adc)
m_host.m_adc = m_host.m_intf.adc_r() & ~0x7;
}
void es5504_core::reset()
{
es550x_shared_core::reset();
for (auto & elem : m_voice)
elem.reset();
m_adc = 0;
std::fill(std::begin(m_ch), std::end(m_ch), 0);
}
void es5504_core::voice_t::reset()
{
es550x_shared_core::es550x_voice_t::reset();
m_volume = 0;
m_ch = 0;
}
// Accessors
u16 es5504_core::host_r(u8 address)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
if (m_e.rising_edge()) // update directly
m_hd = read(m_ha, true);
else
{
m_host_intf.m_rw_strobe = true;
m_host_intf.m_host_access_strobe = true;
}
}
return m_hd;
}
void es5504_core::host_w(u8 address, u16 data)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
m_hd = data;
if (m_e.rising_edge()) // update directly
write(m_ha, m_hd, true);
else
{
m_host_intf.m_rw_strobe = false;
m_host_intf.m_host_access_strobe = true;
}
}
}
u16 es5504_core::read(u8 address, bool cpu_access)
{
return regs_r(m_page, address, cpu_access);
}
void es5504_core::write(u8 address, u16 data, bool cpu_access)
{
regs_w(m_page, address, data, cpu_access);
}
u16 es5504_core::regs_r(u8 page, u8 address, bool cpu_access)
{
u16 ret = 0xffff;
address = bitfield(address, 0, 4); // 4 bit address for CPU access
if (address >= 12) // Global registers
{
switch (address)
{
case 12: // A/D (A to D Convert/Test)
ret = (ret & ~0xfffb) | (m_adc & 0xfffb);
break;
case 13: // ACT (Number of voices)
ret = (ret & ~0x1f) | bitfield(m_active, 0, 5);
break;
case 14: // IRQV (Interrupting voice vector)
ret = (ret & ~0x9f) | (m_irqv.irqb ? 0x80 : 0) | bitfield(m_irqv.voice, 0, 5);
if (cpu_access)
{
m_irqv.clear();
if (bitfield(ret, 7) != m_irqv.irqb)
m_voice[m_irqv.voice].m_alu.irq_update(m_intf, m_irqv);
}
break;
case 15: // PAGE (Page select register)
ret = (ret & ~0x3f) | bitfield(m_page, 0, 6);
break;
}
}
else // Voice specific registers
{
const u8 voice = bitfield(page, 0, 5); // Voice select
if (voice < 25)
{
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 56
{
switch (address)
{
case 1: // O4(n-1) (Filter 4 Temp Register)
ret = v.m_filter.m_o4_1;
break;
case 2: // O3(n-2) (Filter 3 Temp Register #2)
ret = v.m_filter.m_o3_2;
break;
case 3: // O3(n-1) (Filter 3 Temp Register #1)
ret = v.m_filter.m_o3_1;
break;
case 4: // O2(n-2) (Filter 2 Temp Register #2)
ret = v.m_filter.m_o2_2;
break;
case 5: // O2(n-1) (Filter 2 Temp Register #1)
ret = v.m_filter.m_o2_1;
break;
case 6: // O1(n-1) (Filter 1 Temp Register)
ret = v.m_filter.m_o1_1;
break;
}
}
else // Page 0 - 24
{
switch (address)
{
case 0: // CR (Control Register)
ret = (ret & ~0xff) |
(v.m_alu.m_cr.stop0 ? 0x01 : 0x00)
| (v.m_alu.m_cr.stop1 ? 0x02 : 0x00)
| (v.m_cr.adc ? 0x04 : 0x00)
| (v.m_alu.m_cr.lpe ? 0x08 : 0x00)
| (v.m_alu.m_cr.ble ? 0x10 : 0x00)
| (v.m_alu.m_cr.irqe ? 0x20 : 0x00)
| (v.m_alu.m_cr.dir ? 0x40 : 0x00)
| (v.m_alu.m_cr.irq ? 0x80 : 0x00);
break;
case 1: // FC (Frequency Control)
ret = (ret & ~0xfffe) | (v.m_alu.m_fc << 1);
break;
case 2: // STRT-H (Loop Start Register High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_start, 16, 13);
break;
case 3: // STRT-L (Loop Start Register Low)
ret = (ret & ~0xffe0) | (v.m_alu.m_start & 0xffe0);
break;
case 4: // END-H (Loop End Register High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_end, 16, 13);
break;
case 5: // END-L (Loop End Register Low)
ret = (ret & ~0xffe0) | (v.m_alu.m_end & 0xffe0);
break;
case 6: // K2 (Filter Cutoff Coefficient #2)
ret = (ret & ~0xfff0) | (v.m_filter.m_k2 & 0xfff0);
break;
case 7: // K1 (Filter Cutoff Coefficient #1)
ret = (ret & ~0xfff0) | (v.m_filter.m_k1 & 0xfff0);
break;
case 8: // Volume
ret = (ret & ~0xfff0) | ((v.m_volume << 4) & 0xfff0);
break;
case 9: // CA (Filter Config, Channel Assign)
ret = (ret & ~0x3f) |
bitfield(v.m_cr.ca, 0, 4)
| (bitfield(v.m_filter.m_lp, 0, 2) << 4);
break;
case 10: // ACCH (Accumulator High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_accum, 16, 13);
break;
case 11: // ACCL (Accumulator Low)
ret = bitfield(v.m_alu.m_accum, 0, 16);
break;
}
}
}
}
return ret;
}
void es5504_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access)
{
address = bitfield(address, 0, 4); // 4 bit address for CPU access
if (address >= 12) // Global registers
{
switch (address)
{
case 12: // A/D (A to D Convert/Test)
if (bitfield(m_adc, 0)) // Writable ADC
{
m_adc = (m_adc & 7) | (data & ~7);
m_intf.adc_w(m_adc & ~7);
}
m_adc = (m_adc & ~3) | (data & 3);
break;
case 13: // ACT (Number of voices)
m_active = std::min<u8>(24, bitfield(data, 0, 5));
break;
case 14: // IRQV (Interrupting voice vector)
// Read only
break;
case 15: // PAGE (Page select register)
m_page = bitfield(data, 0, 6);
break;
}
}
else // Voice specific registers
{
const u8 voice = bitfield(page, 0, 5); // Voice select
if (voice < 25)
{
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 56
{
switch (address)
{
case 1: // O4(n-1) (Filter 4 Temp Register)
v.m_filter.m_o4_1 = sign_ext<s32>(data, 16);
break;
case 2: // O3(n-2) (Filter 3 Temp Register #2)
v.m_filter.m_o3_2 = sign_ext<s32>(data, 16);
break;
case 3: // O3(n-1) (Filter 3 Temp Register #1)
v.m_filter.m_o3_1 = sign_ext<s32>(data, 16);
break;
case 4: // O2(n-2) (Filter 2 Temp Register #2)
v.m_filter.m_o2_2 = sign_ext<s32>(data, 16);
break;
case 5: // O2(n-1) (Filter 2 Temp Register #1)
v.m_filter.m_o2_1 = sign_ext<s32>(data, 16);
break;
case 6: // O1(n-1) (Filter 1 Temp Register)
v.m_filter.m_o1_1 = sign_ext<s32>(data, 16);
break;
}
}
else // Page 0 - 24
{
switch (address)
{
case 0: // CR (Control Register)
v.m_alu.m_cr.stop0 = bitfield(data, 0);
v.m_alu.m_cr.stop1 = bitfield(data, 1);
v.m_cr.adc = bitfield(data, 2);
v.m_alu.m_cr.lpe = bitfield(data, 3);
v.m_alu.m_cr.ble = bitfield(data, 4);
v.m_alu.m_cr.irqe = bitfield(data, 5);
v.m_alu.m_cr.dir = bitfield(data, 6);
v.m_alu.m_cr.irq = bitfield(data, 7);
break;
case 1: // FC (Frequency Control)
v.m_alu.m_fc = bitfield(data, 1, 15);
break;
case 2: // STRT-H (Loop Start Register High)
v.m_alu.m_start = (v.m_alu.m_start & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 3: // STRT-L (Loop Start Register Low)
v.m_alu.m_start = (v.m_alu.m_start & ~0xffe0) | (data & 0xffe0);
break;
case 4: // END-H (Loop End Register High)
v.m_alu.m_end = (v.m_alu.m_end & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 5: // END-L (Loop End Register Low)
v.m_alu.m_end = (v.m_alu.m_end & ~0xffe0) | (data & 0xffe0);
break;
case 6: // K2 (Filter Cutoff Coefficient #2)
v.m_filter.m_k2 = data & 0xfff0;
break;
case 7: // K1 (Filter Cutoff Coefficient #1)
v.m_filter.m_k1 = data & 0xfff0;
break;
case 8: // Volume
v.m_volume = bitfield(data, 4, 12);
break;
case 9: // CA (Filter Config, Channel Assign)
v.m_cr.ca = bitfield(data, 0, 4);
v.m_filter.m_lp = bitfield(data, 4, 2);
break;
case 10: // ACCH (Accumulator High)
v.m_alu.m_accum = (v.m_alu.m_accum & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 11: // ACCL (Accumulator Low)
v.m_alu.m_accum = (v.m_alu.m_accum & ~0xffff) | data;
break;
}
}
}
}
}

View File

@ -1,97 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504 emulation core
See es550x.cpp for more info
*/
#include "es550x.hpp"
#ifndef _VGSOUND_EMU_ES5504_HPP
#define _VGSOUND_EMU_ES5504_HPP
#pragma once
// ES5504 specific
class es5504_core : public es550x_shared_core
{
public:
// constructor
es5504_core(es550x_intf &intf)
: es550x_shared_core(intf)
, m_voice{*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this}
{
}
// host interface
u16 host_r(u8 address);
void host_w(u8 address, u16 data);
// internal state
virtual void reset() override;
virtual void tick() override;
// less cycle accurate, but also less cpu heavy update routine
void tick_perf();
// 16 analog output channels
s32 out(u8 ch) { return m_ch[ch & 0xf]; }
//-----------------------------------------------------------------
//
// for preview/debug purpose only, not for serious emulators
//
//-----------------------------------------------------------------
// bypass chips host interface for debug purpose only
u16 read(u8 address, bool cpu_access = false);
void write(u8 address, u16 data, bool cpu_access = false);
u16 regs_r(u8 page, u8 address, bool cpu_access = false);
void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false);
u16 regs_r(u8 page, u8 address) { u8 prev = m_page; m_page = page; u16 ret = read(address, false); m_page = prev; return ret; }
// per-voice outputs
s32 voice_out(u8 voice) { return (voice < 25) ? m_voice[voice].m_ch : 0; }
protected:
virtual inline u8 max_voices() override { return 25; }
virtual void voice_tick() override;
private:
// es5504 voice structs
struct voice_t : es550x_voice_t
{
// constructor
voice_t(es5504_core &host)
: es550x_voice_t(20, 9, false)
, m_host(host)
{}
// internal state
virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override;
virtual void tick(u8 voice) override;
void adc_exec();
// registers
es5504_core &m_host;
u16 m_volume = 0; // 12 bit Volume
s32 m_ch = 0; // channel outputs
};
voice_t m_voice[25]; // 25 voices
u16 m_adc = 0; // ADC register
s32 m_ch[16] = {0}; // 16 channel outputs
};
#endif

View File

@ -1,606 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5505 emulation core
see es550x.cpp for more info
*/
#include "es5505.hpp"
// Internal functions
void es5505_core::tick()
{
m_voice_update = false;
m_voice_end = false;
// CLKIN
if (m_clkin.tick())
{
// SERBCLK
if (m_clkin.m_edge.m_changed) // BCLK is freely running clock
{
if (m_bclk.tick())
{
m_intf.bclk(m_bclk.current_edge());
// Serial output
if (m_bclk.falling_edge())
{
// SERLRCLK
if (m_lrclk.tick())
m_intf.lrclk(m_lrclk.current_edge());
}
// SERWCLK
if (m_lrclk.m_edge.m_changed)
m_wclk = 0;
if (m_bclk.falling_edge())
{
if (m_wclk == ((m_sermode.sony_bb) ? 1 : 0))
{
if (m_lrclk.current_edge())
{
for (int i = 0; i < 4; i++)
{
// copy output
m_output[i] = m_output_temp[i];
m_output_latch[i] = m_ch[i];
m_output_temp[i].reset();
// clamp to 16 bit (upper 5 bits are overflow guard bits)
m_output_latch[i].m_left = clamp<s32>(m_output_latch[i].m_left, -0x8000, 0x7fff);
m_output_latch[i].m_right = clamp<s32>(m_output_latch[i].m_right, -0x8000, 0x7fff);
// set signed
if (m_output_latch[i].m_left < 0)
m_output_temp[i].m_left = -1;
if (m_output_latch[i].m_right < 0)
m_output_temp[i].m_right = -1;
}
}
m_wclk_lr = m_lrclk.current_edge();
m_output_bit = 16;
}
s8 output_bit = --m_output_bit;
if (m_output_bit >= 0)
{
for (int i = 0; i < 4; i++)
{
if (m_wclk_lr) // Right output
m_output_temp[i].m_right = (m_output_temp[i].m_right << 1) | bitfield(m_output_latch[i].m_right, output_bit);
else // Left output
m_output_temp[i].m_left = (m_output_temp[i].m_left << 1) | bitfield(m_output_latch[i].m_left, output_bit);
}
}
m_wclk++;
}
}
}
// /CAS, E
if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock
{
// /CAS
if (m_cas.tick())
{
// /CAS high, E low: get sample address
if (m_cas.falling_edge())
{
// /CAS low, E low: fetch sample
if (!m_e.current_edge())
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
// E
if (m_e.tick())
{
m_intf.e_pin(m_e.current_edge());
if (m_e.rising_edge()) // Host access
{
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
voice_tick();
}
else if (m_e.falling_edge()) // Voice memory
{
m_host_intf.m_host_access = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
if (m_e.current_edge()) // Host interface
{
if (m_host_intf.m_host_access)
{
if (m_host_intf.m_rw && (m_e.cycle() == 2)) // Read
{
m_hd = read(m_ha);
m_host_intf.m_host_access = false;
}
else if ((!m_host_intf.m_rw) && (m_e.cycle() == 2)) // Write
write(m_ha, m_hd);
}
}
else if (!m_e.current_edge())
{
if (m_e.cycle() == 2)
{
// reset host access state
m_hd = 0;
m_host_intf.m_host_access_strobe = false;
}
}
}
}
}
}
// less cycle accurate, but less CPU heavy routine
void es5505_core::tick_perf()
{
m_voice_update = false;
m_voice_end = false;
// output
for (int c = 0; c < 4; c++)
{
m_output[c].m_left = clamp<s32>(m_ch[c].m_left, -0x8000, 0x7fff);
m_output[c].m_right = clamp<s32>(m_ch[c].m_right, -0x8000, 0x7fff);
}
// update
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
}
void es5505_core::voice_tick()
{
// Voice updates every 2 E clock cycle (or 4 BCLK clock cycle)
m_voice_update = bitfield(m_voice_fetch++, 0);
if (m_voice_update)
{
// Update voice
m_voice[m_voice_cycle].tick(m_voice_cycle);
// Refresh output
if ((++m_voice_cycle) > clamp<u8>(m_active, 7, 31)) // 8 ~ 32 voices
{
m_voice_end = true;
m_voice_cycle = 0;
for (auto & elem : m_ch)
elem.reset();
for (auto & elem : m_voice)
{
m_ch[bitfield(elem.m_cr.ca, 0, 2)].m_left += elem.m_ch.m_left;
m_ch[bitfield(elem.m_cr.ca, 0, 2)].m_right += elem.m_ch.m_right;
elem.m_ch.reset();
}
}
m_voice_fetch = 0;
}
}
void es5505_core::voice_t::fetch(u8 voice, u8 cycle)
{
m_alu.m_sample[cycle] = m_host.m_intf.read_sample(voice, bitfield(m_cr.bs, 0), bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer));
}
void es5505_core::voice_t::tick(u8 voice)
{
m_ch.reset();
// Filter execute
m_filter.tick(m_alu.interpolation());
if (m_alu.busy())
{
// Send to output
m_ch.m_left = volume_calc(m_lvol, sign_ext<s32>(m_filter.m_o4_1, 16));
m_ch.m_right = volume_calc(m_rvol, sign_ext<s32>(m_filter.m_o4_1, 16));
// ALU execute
if (m_alu.tick())
m_alu.loop_exec();
}
// Update IRQ
m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice);
}
// volume calculation
s32 es5505_core::voice_t::volume_calc(u8 volume, s32 in)
{
u8 exponent = bitfield(volume, 4, 4);
u8 mantissa = bitfield(volume, 0, 4);
return exponent ? (in * s32(0x10 | mantissa)) >> (20 - exponent) : 0;
}
void es5505_core::reset()
{
es550x_shared_core::reset();
for (auto & elem : m_voice)
elem.reset();
m_sermode.reset();
m_bclk.reset();
m_lrclk.reset();
m_wclk = 0;
m_wclk_lr = false;
m_output_bit = 0;
for (auto & elem : m_ch)
elem.reset();
for (auto & elem : m_output)
elem.reset();
for (auto & elem : m_output_temp)
elem.reset();
for (auto & elem : m_output_latch)
elem.reset();
}
void es5505_core::voice_t::reset()
{
es550x_shared_core::es550x_voice_t::reset();
m_lvol = 0;
m_rvol = 0;
m_ch.reset();
}
// Accessors
u16 es5505_core::host_r(u8 address)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
if (m_e.rising_edge()) // update directly
m_hd = read(m_ha, true);
else
{
m_host_intf.m_rw_strobe = true;
m_host_intf.m_host_access_strobe = true;
}
}
return m_hd;
}
void es5505_core::host_w(u8 address, u16 data)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
m_hd = data;
if (m_e.rising_edge()) // update directly
write(m_ha, m_hd, true);
else
{
m_host_intf.m_rw_strobe = false;
m_host_intf.m_host_access_strobe = true;
}
}
}
u16 es5505_core::read(u8 address, bool cpu_access)
{
return regs_r(m_page, address, cpu_access);
}
void es5505_core::write(u8 address, u16 data, bool cpu_access)
{
regs_w(m_page, address, data, cpu_access);
}
u16 es5505_core::regs_r(u8 page, u8 address, bool cpu_access)
{
u16 ret = 0xffff;
address = bitfield(address, 0, 4); // 4 bit address for CPU access
if (address >= 13) // Global registers
{
switch (address)
{
case 13: // ACT (Number of voices)
ret = (ret & ~0x1f) | bitfield(m_active, 0, 5);
break;
case 14: // IRQV (Interrupting voice vector)
ret = (ret & ~0x9f) | (m_irqv.irqb ? 0x80 : 0) | bitfield(m_irqv.voice, 0, 5);
if (cpu_access)
{
m_irqv.clear();
if (bitfield(ret, 7) != m_irqv.irqb)
m_voice[m_irqv.voice].m_alu.irq_update(m_intf, m_irqv);
}
break;
case 15: // PAGE (Page select register)
ret = (ret & ~0x7f) | bitfield(m_page, 0, 7);
break;
}
}
else
{
if (bitfield(page, 6)) // Channel registers
{
switch (address)
{
case 0: // CH0L (Channel 0 Left)
case 2: // CH1L (Channel 1 Left)
case 4: // CH2L (Channel 2 Left)
if (!cpu_access) // CPU can't read here
ret = m_ch[bitfield(address, 0, 2)].m_left;
break;
case 1: // CH0R (Channel 0 Right)
case 3: // CH1R (Channel 1 Right)
case 5: // CH2R (Channel 2 Right)
if (!cpu_access) // CPU can't read here
ret = m_ch[bitfield(address, 0, 2)].m_right;
break;
case 6: // CH3L (Channel 3 Left)
if ((!cpu_access) || m_sermode.adc)
ret = m_ch[3].m_left;
break;
case 7: // CH3R (Channel 3 Right)
if ((!cpu_access) || m_sermode.adc)
ret = m_ch[3].m_right;
break;
case 8: // SERMODE (Serial Mode)
ret = (ret & ~0xf807) |
(m_sermode.adc ? 0x01 : 0x00)
| (m_sermode.test ? 0x02 : 0x00)
| (m_sermode.sony_bb ? 0x04 : 0x00)
| (bitfield(m_sermode.msb, 0, 5) << 11);
break;
case 9: // PAR (Port A/D Register)
ret = (ret & ~0x3f) | (m_intf.adc_r() & ~0x3f);
break;
}
}
else // Voice specific registers
{
const u8 voice = bitfield(page, 0, 5); // Voice select
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 63
{
switch (address)
{
case 1: // O4(n-1) (Filter 4 Temp Register)
ret = v.m_filter.m_o4_1;
break;
case 2: // O3(n-2) (Filter 3 Temp Register #2)
ret = v.m_filter.m_o3_2;
break;
case 3: // O3(n-1) (Filter 3 Temp Register #1)
ret = v.m_filter.m_o3_1;
break;
case 4: // O2(n-2) (Filter 2 Temp Register #2)
ret = v.m_filter.m_o2_2;
break;
case 5: // O2(n-1) (Filter 2 Temp Register #1)
ret = v.m_filter.m_o2_1;
break;
case 6: // O1(n-1) (Filter 1 Temp Register)
ret = v.m_filter.m_o1_1;
break;
}
}
else // Page 0 - 31
{
switch (address)
{
case 0: // CR (Control Register)
ret = (ret & ~0xfff) |
(v.m_alu.m_cr.stop0 ? 0x01 : 0x00)
| (v.m_alu.m_cr.stop1 ? 0x02 : 0x00)
| (bitfield(v.m_cr.bs, 0) ? 0x04 : 0x00)
| (v.m_alu.m_cr.lpe ? 0x08 : 0x00)
| (v.m_alu.m_cr.ble ? 0x10 : 0x00)
| (v.m_alu.m_cr.irqe ? 0x20 : 0x00)
| (v.m_alu.m_cr.dir ? 0x40 : 0x00)
| (v.m_alu.m_cr.irq ? 0x80 : 0x00)
| (bitfield(v.m_cr.ca, 0, 2) << 8)
| (bitfield(v.m_filter.m_lp, 0, 2) << 10);
break;
case 1: // FC (Frequency Control)
ret = (ret & ~0xfffe) | (bitfield(v.m_alu.m_fc, 0, 15) << 1);
break;
case 2: // STRT-H (Loop Start Register High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_start, 16, 13);
break;
case 3: // STRT-L (Loop Start Register Low)
ret = (ret & ~0xffe0) | (v.m_alu.m_start & 0xffe0);
break;
case 4: // END-H (Loop End Register High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_end, 16, 13);
break;
case 5: // END-L (Loop End Register Low)
ret = (ret & ~0xffe0) | (v.m_alu.m_end & 0xffe0);
break;
case 6: // K2 (Filter Cutoff Coefficient #2)
ret = (ret & ~0xfff0) | (v.m_filter.m_k2 & 0xfff0);
break;
case 7: // K1 (Filter Cutoff Coefficient #1)
ret = (ret & ~0xfff0) | (v.m_filter.m_k1 & 0xfff0);
break;
case 8: // LVOL (Left Volume)
ret = (ret & ~0xff00) | ((v.m_lvol << 8) & 0xff00);
break;
case 9: // RVOL (Right Volume)
ret = (ret & ~0xff00) | ((v.m_rvol << 8) & 0xff00);
break;
case 10: // ACCH (Accumulator High)
ret = (ret & ~0x1fff) | bitfield(v.m_alu.m_accum, 16, 13);
break;
case 11: // ACCL (Accumulator Low)
ret = bitfield(v.m_alu.m_accum, 0, 16);
break;
}
}
}
}
return ret;
}
void es5505_core::regs_w(u8 page, u8 address, u16 data, bool cpu_access)
{
address = bitfield(address, 0, 4); // 4 bit address for CPU access
if (address >= 12) // Global registers
{
switch (address)
{
case 13: // ACT (Number of voices)
m_active = std::max<u8>(7, bitfield(data, 0, 5));
break;
case 14: // IRQV (Interrupting voice vector)
// Read only
break;
case 15: // PAGE (Page select register)
m_page = bitfield(data, 0, 7);
break;
}
}
else // Voice specific registers
{
if (bitfield(page, 6)) // Channel registers
{
switch (address)
{
case 0: // CH0L (Channel 0 Left)
if (m_sermode.test)
m_ch[0].m_left = data;
break;
case 1: // CH0R (Channel 0 Right)
if (m_sermode.test)
m_ch[0].m_right = data;
break;
case 2: // CH1L (Channel 1 Left)
if (m_sermode.test)
m_ch[1].m_left = data;
break;
case 3: // CH1R (Channel 1 Right)
if (m_sermode.test)
m_ch[1].m_right = data;
break;
case 4: // CH2L (Channel 2 Left)
if (m_sermode.test)
m_ch[2].m_left = data;
break;
case 5: // CH2R (Channel 2 Right)
if (m_sermode.test)
m_ch[2].m_right = data;
break;
case 6: // CH3L (Channel 3 Left)
if (m_sermode.test)
m_ch[3].m_left = data;
break;
case 7: // CH3R (Channel 3 Right)
if (m_sermode.test)
m_ch[3].m_right = data;
break;
case 8: // SERMODE (Serial Mode)
m_sermode.adc = bitfield(data, 0);
m_sermode.test = bitfield(data, 1);
m_sermode.sony_bb = bitfield(data, 2);
m_sermode.msb = bitfield(data, 11, 5);
break;
case 9: // PAR (Port A/D Register)
// Read only
break;
}
}
else // Voice specific registers
{
const u8 voice = bitfield(page, 0, 5); // Voice select
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 56
{
switch (address)
{
case 1: // O4(n-1) (Filter 4 Temp Register)
v.m_filter.m_o4_1 = sign_ext<s32>(data, 16);
break;
case 2: // O3(n-2) (Filter 3 Temp Register #2)
v.m_filter.m_o3_2 = sign_ext<s32>(data, 16);
break;
case 3: // O3(n-1) (Filter 3 Temp Register #1)
v.m_filter.m_o3_1 = sign_ext<s32>(data, 16);
break;
case 4: // O2(n-2) (Filter 2 Temp Register #2)
v.m_filter.m_o2_2 = sign_ext<s32>(data, 16);
break;
case 5: // O2(n-1) (Filter 2 Temp Register #1)
v.m_filter.m_o2_1 = sign_ext<s32>(data, 16);
break;
case 6: // O1(n-1) (Filter 1 Temp Register)
v.m_filter.m_o1_1 = sign_ext<s32>(data, 16);
break;
}
}
else // Page 0 - 24
{
switch (address)
{
case 0: // CR (Control Register)
v.m_alu.m_cr.stop0 = bitfield(data, 0);
v.m_alu.m_cr.stop1 = bitfield(data, 1);
v.m_cr.bs = bitfield(data, 2);
v.m_alu.m_cr.lpe = bitfield(data, 3);
v.m_alu.m_cr.ble = bitfield(data, 4);
v.m_alu.m_cr.irqe = bitfield(data, 5);
v.m_alu.m_cr.dir = bitfield(data, 6);
v.m_alu.m_cr.irq = bitfield(data, 7);
v.m_cr.ca = bitfield(data, 8, 2);
v.m_filter.m_lp = bitfield(data, 10, 2);
break;
case 1: // FC (Frequency Control)
v.m_alu.m_fc = bitfield(data, 1, 15);
break;
case 2: // STRT-H (Loop Start Register High)
v.m_alu.m_start = (v.m_alu.m_start & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 3: // STRT-L (Loop Start Register Low)
v.m_alu.m_start = (v.m_alu.m_start & ~0xffe0) | (data & 0xffe0);
break;
case 4: // END-H (Loop End Register High)
v.m_alu.m_end = (v.m_alu.m_end & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 5: // END-L (Loop End Register Low)
v.m_alu.m_end = (v.m_alu.m_end & ~0xffe0) | (data & 0xffe0);
break;
case 6: // K2 (Filter Cutoff Coefficient #2)
v.m_filter.m_k2 = data & 0xfff0;
break;
case 7: // K1 (Filter Cutoff Coefficient #1)
v.m_filter.m_k1 = data & 0xfff0;
break;
case 8: // LVOL (Left Volume)
v.m_lvol = bitfield(data, 8, 8);
break;
case 9: // RVOL (Right Volume)
v.m_rvol = bitfield(data, 8, 8);
break;
case 10: // ACCH (Accumulator High)
v.m_alu.m_accum = (v.m_alu.m_accum & ~0x1fff0000) | (bitfield<u32>(data, 0, 13) << 16);
break;
case 11: // ACCL (Accumulator Low)
v.m_alu.m_accum = (v.m_alu.m_accum & ~0xffff) | data;
break;
}
}
}
}
}

View File

@ -1,152 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504 emulation core
See es550x.cpp for more info
*/
#include "es550x.hpp"
#ifndef _VGSOUND_EMU_ES5505_HPP
#define _VGSOUND_EMU_ES5505_HPP
#pragma once
// ES5505 specific
class es5505_core : public es550x_shared_core
{
public:
// constructor
es5505_core(es550x_intf &intf)
: es550x_shared_core(intf)
, m_voice{*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this}
{
}
// host interface
u16 host_r(u8 address);
void host_w(u8 address, u16 data);
// internal state
virtual void reset() override;
virtual void tick() override;
// less cycle accurate, but also less cpu heavy update routine
void tick_perf();
// clock outputs
bool bclk() { return m_bclk.current_edge(); }
bool bclk_rising_edge() { return m_bclk.rising_edge(); }
bool bclk_falling_edge() { return m_bclk.falling_edge(); }
// Input mode for Channel 3
void lin(s32 in) { if (m_sermode.adc) { m_ch[3].m_left = in; } }
void rin(s32 in) { if (m_sermode.adc) { m_ch[3].m_right = in; } }
// 4 stereo output channels
s32 lout(u8 ch) { return m_ch[ch & 0x3].m_left; }
s32 rout(u8 ch) { return m_ch[ch & 0x3].m_right; }
//-----------------------------------------------------------------
//
// for preview/debug purpose only, not for serious emulators
//
//-----------------------------------------------------------------
// bypass chips host interface for debug purpose only
u16 read(u8 address, bool cpu_access = false);
void write(u8 address, u16 data, bool cpu_access = false);
u16 regs_r(u8 page, u8 address, bool cpu_access = false);
void regs_w(u8 page, u8 address, u16 data, bool cpu_access = false);
u16 regs_r(u8 page, u8 address) { u8 prev = m_page; m_page = page; u16 ret = read(address, false); m_page = prev; return ret; }
// per-voice outputs
s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].m_ch.m_left : 0; }
s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].m_ch.m_right : 0; }
protected:
virtual inline u8 max_voices() override { return 32; }
virtual void voice_tick() override;
private:
struct output_t
{
void reset()
{
m_left = 0;
m_right = 0;
};
s32 m_left = 0;
s32 m_right = 0;
};
// es5505 voice structs
struct voice_t : es550x_voice_t
{
// constructor
voice_t(es5505_core &host)
: es550x_voice_t(20, 9, false)
, m_host(host)
{}
// internal state
virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override;
virtual void tick(u8 voice) override;
s32 volume_calc(u8 volume, s32 in);
// registers
es5505_core &m_host;
u8 m_lvol = 0; // Left volume
u8 m_rvol = 0; // Right volume
output_t m_ch; // channel output
};
struct sermode_t
{
sermode_t()
: adc(0)
, test(0)
, sony_bb(0)
, msb(0)
{};
void reset()
{
adc = 0;
test = 0;
sony_bb = 0;
msb = 0;
}
u8 adc : 1; // A/D
u8 test : 1; // Test
u8 sony_bb : 1; // Sony/BB format serial output
u8 msb : 5; // Serial output MSB
};
voice_t m_voice[32]; // 32 voices
// Serial related stuffs
sermode_t m_sermode; // Serial mode register
clock_pulse_t<s8, 4, 0> m_bclk; // BCLK clock (CLKIN / 4), freely running clock
clock_pulse_t<s8, 16, 1> m_lrclk; // LRCLK
s16 m_wclk = 0; // WCLK
bool m_wclk_lr = false; // WCLK, L/R output select
s8 m_output_bit = 0; // Bit position in output
output_t m_ch[4]; // 4 stereo output channels
output_t m_output[4]; // Serial outputs
output_t m_output_temp[4]; // temporary signal for serial output
output_t m_output_latch[4]; // output latch
};
#endif

View File

@ -1,794 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5506 emulation core
see es550x.cpp for more info
*/
#include "es5506.hpp"
// Internal functions
void es5506_core::tick()
{
m_voice_update = false;
m_voice_end = false;
// CLKIN
if (m_clkin.tick())
{
// BCLK
if (m_clkin.m_edge.m_changed && (!m_mode.bclk_en)) // BCLK is freely running clock
{
if (m_bclk.tick())
{
m_intf.bclk(m_bclk.current_edge());
// Serial output
if (!m_mode.lrclk_en)
{
if (m_bclk.falling_edge())
{
// LRCLK
if (m_lrclk.tick())
{
m_intf.lrclk(m_lrclk.current_edge());
if (m_lrclk.rising_edge())
{
m_w_st_curr = m_w_st;
m_w_end_curr = m_w_end;
}
if (m_lrclk.falling_edge()) // update width
m_lrclk.set_width_latch(m_lr_end);
}
}
}
// WCLK
if (!m_mode.wclk_en)
{
if (!m_mode.lrclk_en)
{
if (m_lrclk.m_edge.m_changed)
m_wclk = 0;
}
if (m_bclk.falling_edge())
{
if (m_wclk == m_w_st_curr)
{
m_intf.wclk(true);
if (m_lrclk.current_edge())
{
for (int i = 0; i < 6; i++)
{
// copy output
m_output[i] = m_output_temp[i];
m_output_latch[i] = m_ch[i];
m_output_temp[i].reset();
// clamp to 20 bit (upper 3 bits are overflow guard bits)
m_output_latch[i].m_left = clamp<s32>(m_output_latch[i].m_left, -0x80000, 0x7ffff);
m_output_latch[i].m_right = clamp<s32>(m_output_latch[i].m_right, -0x80000, 0x7ffff);
// set signed
if (m_output_latch[i].m_left < 0)
m_output_temp[i].m_left = -1;
if (m_output_latch[i].m_right < 0)
m_output_temp[i].m_right = -1;
}
}
m_wclk_lr = m_lrclk.current_edge();
m_output_bit = 20;
}
if (m_wclk < m_w_end_curr)
{
s8 output_bit = --m_output_bit;
if (m_output_bit >= 0)
{
for (int i = 0; i < 6; i++)
{
if (m_wclk_lr) // Right output
m_output_temp[i].m_right = (m_output_temp[i].m_right << 1) | bitfield(m_output_latch[i].m_right, output_bit);
else // Left output
m_output_temp[i].m_left = (m_output_temp[i].m_left << 1) | bitfield(m_output_latch[i].m_left, output_bit);
}
}
}
if (m_wclk == m_w_end_curr)
m_intf.wclk(false);
m_wclk++;
}
}
}
}
// /CAS, E
if (m_clkin.falling_edge()) // falling edge triggers /CAS, E clock
{
// /CAS
if (m_cas.tick())
{
// single OTTO master mode, /CAS high, E low: get sample address
// single OTTO early mode, /CAS falling, E high: get sample address
if (m_cas.falling_edge())
{
if (!m_e.current_edge())
{
// single OTTO master mode, /CAS low, E low: fetch sample
if (m_mode.master)
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
else if (m_e.current_edge())
{
// dual OTTO slave mode, /CAS low, E high: fetch sample
if (m_mode.dual && (!m_mode.master)) // Dual OTTO, slave mode
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
}
}
}
// E
if (m_e.tick())
{
m_intf.e_pin(m_e.current_edge());
if (m_e.rising_edge())
{
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
}
else if (m_e.falling_edge())
{
m_host_intf.m_host_access = false;
voice_tick();
}
if (m_e.current_edge()) // Host interface
{
if (m_host_intf.m_host_access)
{
if (m_host_intf.m_rw && (m_e.cycle() == 0)) // Read
{
m_hd = read(m_ha);
m_host_intf.m_host_access = false;
}
else if ((!m_host_intf.m_rw) && (m_e.cycle() == 2)) // Write
write(m_ha, m_hd);
}
}
else if (!m_e.current_edge())
{
if (m_e.cycle() == 2)
{
// reset host access state
m_hd = 0;
m_host_intf.m_host_access_strobe = false;
}
}
}
}
}
}
// less cycle accurate, but less CPU heavy routine
void es5506_core::tick_perf()
{
m_voice_update = false;
m_voice_end = false;
// output
if (((!m_mode.lrclk_en) && (!m_mode.bclk_en) && (!m_mode.wclk_en)) && (m_w_st < m_w_end))
{
const int output_bits = 20 - (m_w_end - m_w_st);
if (output_bits < 20)
{
for (int c = 0; c < 6; c++)
{
m_output[c].m_left = clamp<s32>(m_ch[c].m_left, -0x80000, 0x7ffff) >> output_bits;
m_output[c].m_right = clamp<s32>(m_ch[c].m_right, -0x80000, 0x7ffff) >> output_bits;
}
}
}
else
{
for (int c = 0; c < 6; c++)
{
m_output[c].m_left = 0;
m_output[c].m_right = 0;
}
}
// update
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
// falling edge
m_e.m_edge.set(false);
m_intf.e_pin(false);
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe = false;
m_voice[m_voice_cycle].fetch(m_voice_cycle, m_voice_fetch);
voice_tick();
// rising edge
m_e.m_edge.set(true);
m_intf.e_pin(true);
m_host_intf.m_rw = m_host_intf.m_rw_strobe;
m_host_intf.m_host_access = m_host_intf.m_host_access_strobe;
}
void es5506_core::voice_tick()
{
// Voice updates every 2 E clock cycle (or 4 BCLK clock cycle)
m_voice_update = bitfield(m_voice_fetch++, 0);
if (m_voice_update)
{
// Update voice
m_voice[m_voice_cycle].tick(m_voice_cycle);
// Refresh output
if ((++m_voice_cycle) > clamp<u8>(m_active, 4, 31)) // 5 ~ 32 voices
{
m_voice_end = true;
m_voice_cycle = 0;
for (auto & elem : m_ch)
elem.reset();
for (auto & elem : m_voice)
{
const u8 ca = bitfield(elem.m_cr.ca, 0, 3);
if (ca < 6)
{
m_ch[ca].m_left += elem.m_ch.m_left;
m_ch[ca].m_right += elem.m_ch.m_right;
}
elem.m_ch.reset();
}
}
m_voice_fetch = 0;
}
}
void es5506_core::voice_t::fetch(u8 voice, u8 cycle)
{
m_alu.m_sample[cycle] = m_host.m_intf.read_sample(voice, bitfield(m_cr.bs, 0, 1), bitfield(m_alu.get_accum_integer() + cycle, 0, m_alu.m_integer));
if (m_cr.cmpd) // Decompress (Upper 8 bit is used for compressed format)
m_alu.m_sample[cycle] = decompress(bitfield(m_alu.m_sample[cycle], 8, 8));
}
void es5506_core::voice_t::tick(u8 voice)
{
m_ch.reset();
// Filter execute
m_filter.tick(m_alu.interpolation());
if (m_alu.busy())
{
if (!m_mute)
{
// Send to output
m_ch.m_left = volume_calc(m_lvol, sign_ext<s32>(m_filter.m_o4_1, 16));
m_ch.m_right = volume_calc(m_rvol, sign_ext<s32>(m_filter.m_o4_1, 16));
}
// ALU execute
if (m_alu.tick())
m_alu.loop_exec();
}
// Envelope
if (m_ecount != 0)
{
// Left and Right volume
if (bitfield(m_lvramp, 0, 8) != 0)
m_lvol = clamp<s32>(m_lvol + sign_ext<s32>(bitfield(m_lvramp, 0, 8), 8), 0, 0xffff);
if (bitfield(m_rvramp, 0, 8) != 0)
m_rvol = clamp<s32>(m_rvol + sign_ext<s32>(bitfield(m_rvramp, 0, 8), 8), 0, 0xffff);
// Filter coeffcient
if ((m_k1ramp.ramp != 0) && ((m_k1ramp.slow == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
m_filter.m_k1 = clamp<s32>(m_filter.m_k1 + sign_ext<s32>(m_k1ramp.ramp, 8), 0, 0xffff);
if ((m_k2ramp.ramp != 0) && ((m_k2ramp.slow == 0) || (bitfield(m_filtcount, 0, 3) == 0)))
m_filter.m_k2 = clamp<s32>(m_filter.m_k2 + sign_ext<s32>(m_k2ramp.ramp, 8), 0, 0xffff);
m_ecount--;
}
m_filtcount = bitfield(m_filtcount + 1, 0, 3);
// Update IRQ
m_alu.irq_exec(m_host.m_intf, m_host.m_irqv, voice);
}
// Compressed format
s16 es5506_core::voice_t::decompress(u8 sample)
{
u8 exponent = bitfield(sample, 5, 3);
u8 mantissa = bitfield(sample, 0, 5);
return (exponent > 0) ?
s16(((bitfield(mantissa, 4) ? 0x10 : ~0x1f) | bitfield(mantissa, 0, 4)) << (4 + (exponent - 1))) :
s16(((bitfield(mantissa, 4) ? ~0xf : 0) | bitfield(mantissa, 0, 4)) << 4);
}
// volume calculation
s32 es5506_core::voice_t::volume_calc(u16 volume, s32 in)
{
u8 exponent = bitfield(volume, 12, 4);
u8 mantissa = bitfield(volume, 4, 8);
return (in * s32(0x100 | mantissa)) >> (20 - exponent);
}
void es5506_core::reset()
{
es550x_shared_core::reset();
for (auto & elem : m_voice)
elem.reset();
m_read_latch = 0xffffffff;
m_write_latch = 0xffffffff;
m_w_st = 0;
m_w_end = 0;
m_lr_end = 0;
m_w_st_curr = 0;
m_w_end_curr = 0;
m_mode.reset();
m_bclk.reset();
m_lrclk.reset(32);
m_wclk = 0;
m_wclk_lr = false;
m_output_bit = 0;
for (auto & elem : m_ch)
elem.reset();
for (auto & elem : m_output)
elem.reset();
for (auto & elem : m_output_temp)
elem.reset();
for (auto & elem : m_output_latch)
elem.reset();
}
void es5506_core::voice_t::reset()
{
es550x_shared_core::es550x_voice_t::reset();
m_lvol = 0;
m_lvramp = 0;
m_rvol = 0;
m_rvramp = 0;
m_ecount = 0;
m_k2ramp.reset();
m_k1ramp.reset();
m_filtcount = 0;
m_ch.reset();
m_mute = false;
}
// Accessors
u8 es5506_core::host_r(u8 address)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
if (m_e.rising_edge()) // update directly
m_hd = read(m_ha, true);
else
{
m_host_intf.m_rw_strobe = true;
m_host_intf.m_host_access_strobe = true;
}
}
return m_hd;
}
void es5506_core::host_w(u8 address, u8 data)
{
if (!m_host_intf.m_host_access)
{
m_ha = address;
m_hd = data;
if (m_e.rising_edge()) // update directly
write(m_ha, m_hd, true);
else
{
m_host_intf.m_rw_strobe = false;
m_host_intf.m_host_access_strobe = true;
}
}
}
u8 es5506_core::read(u8 address, bool cpu_access)
{
const u8 byte = bitfield(address, 0, 2); // byte select
const u8 shift = 24 - (byte << 3);
if (byte != 0) // Return already latched register if not highest byte is accessing
return bitfield(m_read_latch, shift, 8);
address = bitfield(address, 2, 4); // 4 bit address for CPU access
// get read register
m_read_latch = regs_r(m_page, address, cpu_access);
return bitfield(m_read_latch, 24, 8);
}
void es5506_core::write(u8 address, u8 data, bool cpu_access)
{
const u8 byte = bitfield(address, 0, 2); // byte select
const u8 shift = 24 - (byte << 3);
address = bitfield(address, 2, 4); // 4 bit address for CPU access
// Update register latch
m_write_latch = (m_write_latch & ~(0xff << shift)) | (u32(data) << shift);
if (byte != 3) // Wait until lowest byte is writed
return;
regs_w(m_page, address, m_write_latch, cpu_access);
// Reset latch
m_write_latch = 0;
}
u32 es5506_core::regs_r(u8 page, u8 address, bool cpu_access)
{
u32 read_latch = 0xffffffff;
if (address >= 13) // Global registers
{
switch (address)
{
case 13: // POT (Pot A/D Register)
read_latch = (read_latch & ~0x3ff) | bitfield(m_intf.adc_r(), 0, 10);
break;
case 14: // IRQV (Interrupting voice vector)
read_latch = (read_latch & ~0x9f) | (m_irqv.irqb ? 0x80 : 0) | bitfield(m_irqv.voice, 0, 5);
if (cpu_access)
{
m_irqv.clear();
if (bitfield(read_latch, 7) != m_irqv.irqb)
m_voice[m_irqv.voice].m_alu.irq_update(m_intf, m_irqv);
}
break;
case 15: // PAGE (Page select register)
read_latch = (read_latch & ~0x7f) | bitfield(m_page, 0, 7);
break;
}
}
else
{
if (bitfield(page, 6)) // Channel registers are Write only
{
if (!cpu_access) // CPU can't read here
{
switch (address)
{
case 0: // CH0L (Channel 0 Left)
case 2: // CH1L (Channel 1 Left)
case 4: // CH2L (Channel 2 Left)
case 6: // CH3L (Channel 3 Left)
case 8: // CH4L (Channel 4 Left)
case 10: // CH5L (Channel 5 Left)
read_latch = m_ch[bitfield(address, 1, 3)].m_left;
break;
case 1: // CH0R (Channel 0 Right)
case 3: // CH1R (Channel 1 Right)
case 5: // CH2R (Channel 2 Right)
case 7: // CH3R (Channel 3 Right)
case 9: // CH4R (Channel 4 Right)
case 11: // CH5R (Channel 5 Right)
read_latch = m_ch[bitfield(address, 1, 3)].m_right;
break;
}
}
}
else
{
const u8 voice = bitfield(page, 0, 5); // Voice select
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 63
{
switch (address)
{
case 0: // CR (Control Register)
read_latch = (read_latch & ~0xffff) |
(v.m_alu.m_cr.stop0 ? 0x0001 : 0x0000)
| (v.m_alu.m_cr.stop1 ? 0x0002 : 0x0000)
| (v.m_alu.m_cr.lei ? 0x0004 : 0x0000)
| (v.m_alu.m_cr.lpe ? 0x0008 : 0x0000)
| (v.m_alu.m_cr.ble ? 0x0010 : 0x0000)
| (v.m_alu.m_cr.irqe ? 0x0020 : 0x0000)
| (v.m_alu.m_cr.dir ? 0x0040 : 0x0000)
| (v.m_alu.m_cr.irq ? 0x0080 : 0x0000)
| (bitfield(v.m_filter.m_lp, 0, 2) << 8)
| (bitfield(v.m_cr.ca, 0, 3) << 10)
| (v.m_cr.cmpd ? 0x2000 : 0x0000)
| (bitfield(v.m_cr.bs, 0, 2) << 14);
break;
case 1: // START (Loop Start Register)
read_latch = (read_latch & ~0xfffff800) | (v.m_alu.m_start & 0xfffff800);
break;
case 2: // END (Loop End Register)
read_latch = (read_latch & ~0xffffff80) | (v.m_alu.m_end & 0xffffff80);
break;
case 3: // ACCUM (Accumulator Register)
read_latch = v.m_alu.m_accum;
break;
case 4: // O4(n-1) (Filter 4 Temp Register)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o4_1, 0, 18);
else
read_latch = v.m_filter.m_o4_1;
break;
case 5: // O3(n-2) (Filter 3 Temp Register #2)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o3_2, 0, 18);
else
read_latch = v.m_filter.m_o3_2;
break;
case 6: // O3(n-1) (Filter 3 Temp Register #1)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o3_1, 0, 18);
else
read_latch = v.m_filter.m_o3_1;
break;
case 7: // O2(n-2) (Filter 2 Temp Register #2)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o2_2, 0, 18);
else
read_latch = v.m_filter.m_o2_2;
break;
case 8: // O2(n-1) (Filter 2 Temp Register #1)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o2_1, 0, 18);
else
read_latch = v.m_filter.m_o2_1;
break;
case 9: // O1(n-1) (Filter 1 Temp Register)
if (cpu_access)
read_latch = (read_latch & ~0x3ffff) | bitfield(v.m_filter.m_o1_1, 0, 18);
else
read_latch = v.m_filter.m_o1_1;
break;
case 10: // W_ST (Word Clock Start Register)
read_latch = (read_latch & ~0x7f) | bitfield(m_w_st, 0, 7);
break;
case 11: // W_END (Word Clock End Register)
read_latch = (read_latch & ~0x7f) | bitfield(m_w_end, 0, 7);
break;
case 12: // LR_END (Left/Right Clock End Register)
read_latch = (read_latch & ~0x7f) | bitfield(m_lr_end, 0, 7);
break;
}
}
else // Page 0 - 31
{
switch (address)
{
case 0: // CR (Control Register)
read_latch = (read_latch & ~0xffff) |
(v.m_alu.m_cr.stop0 ? 0x0001 : 0x0000)
| (v.m_alu.m_cr.stop1 ? 0x0002 : 0x0000)
| (v.m_alu.m_cr.lei ? 0x0004 : 0x0000)
| (v.m_alu.m_cr.lpe ? 0x0008 : 0x0000)
| (v.m_alu.m_cr.ble ? 0x0010 : 0x0000)
| (v.m_alu.m_cr.irqe ? 0x0020 : 0x0000)
| (v.m_alu.m_cr.dir ? 0x0040 : 0x0000)
| (v.m_alu.m_cr.irq ? 0x0080 : 0x0000)
| (bitfield(v.m_filter.m_lp, 0, 2) << 8)
| (bitfield(v.m_cr.ca, 0, 3) << 10)
| (v.m_cr.cmpd ? 0x2000 : 0x0000)
| (bitfield(v.m_cr.bs, 0, 2) << 14);
break;
case 1: // FC (Frequency Control)
read_latch = (read_latch & ~0x1ffff) | bitfield(v.m_alu.m_fc, 0, 17);
break;
case 2: // LVOL (Left Volume)
read_latch = (read_latch & ~0xffff) | bitfield(v.m_lvol, 0, 16);
break;
case 3: // LVRAMP (Left Volume Ramp)
read_latch = (read_latch & ~0xff00) | (bitfield(v.m_lvramp, 0, 8) << 8);
break;
case 4: // RVOL (Right Volume)
read_latch = (read_latch & ~0xffff) | bitfield(v.m_rvol, 0, 16);
break;
case 5: // RVRAMP (Right Volume Ramp)
read_latch = (read_latch & ~0xff00) | (bitfield(v.m_rvramp, 0, 8) << 8);
break;
case 6: // ECOUNT (Envelope Counter)
read_latch = (read_latch & ~0x01ff) | bitfield(v.m_ecount, 0, 9);
break;
case 7: // K2 (Filter Cutoff Coefficient #2)
read_latch = (read_latch & ~0xffff) | bitfield(v.m_filter.m_k2, 0, 16);
break;
case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp)
read_latch = (read_latch & ~0xff01) | (bitfield(v.m_k2ramp.ramp, 0, 8) << 8) | (v.m_k2ramp.slow ? 0x0001 : 0x0000);
break;
case 9: // K1 (Filter Cutoff Coefficient #1)
read_latch = (read_latch & ~0xffff) | bitfield(v.m_filter.m_k1, 0, 16);
break;
case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp)
read_latch = (read_latch & ~0xff01) | (bitfield(v.m_k1ramp.ramp, 0, 8) << 8) | (v.m_k1ramp.slow ? 0x0001 : 0x0000);
break;
case 11: // ACT (Number of voices)
read_latch = (read_latch & ~0x1f) | bitfield(m_active, 0, 5);
break;
case 12: // MODE (Global Mode)
read_latch = (read_latch & ~0x1f) |
(m_mode.lrclk_en ? 0x01 : 0x00)
| (m_mode.wclk_en ? 0x02 : 0x00)
| (m_mode.bclk_en ? 0x04 : 0x00)
| (m_mode.master ? 0x08 : 0x00)
| (m_mode.dual ? 0x10 : 0x00);
break;
}
}
}
}
return read_latch;
}
void es5506_core::regs_w(u8 page, u8 address, u32 data, bool cpu_access)
{
if (address >= 13) // Global registers
{
switch (address)
{
case 13: // POT (Pot A/D Register)
// Read only
break;
case 14: // IRQV (Interrupting voice vector)
// Read only
break;
case 15: // PAGE (Page select register)
m_page = bitfield(data, 0, 7);
break;
}
}
else
{
if (bitfield(page, 6)) // Channel registers are Write only, and for test purposes
{
switch (address)
{
case 0: // CH0L (Channel 0 Left)
case 2: // CH1L (Channel 1 Left)
case 4: // CH2L (Channel 2 Left)
case 6: // CH3L (Channel 3 Left)
case 8: // CH4L (Channel 4 Left)
case 10: // CH5L (Channel 5 Left)
m_ch[bitfield(address, 1, 3)].m_left = sign_ext<s32>(bitfield(data, 0, 23), 23);
break;
case 1: // CH0R (Channel 0 Right)
case 3: // CH1R (Channel 1 Right)
case 5: // CH2R (Channel 2 Right)
case 7: // CH3R (Channel 3 Right)
case 9: // CH4R (Channel 4 Right)
case 11: // CH5R (Channel 5 Right)
m_ch[bitfield(address, 1, 3)].m_right = sign_ext<s32>(bitfield(data, 0, 23), 23);
break;
}
}
else
{
const u8 voice = bitfield(page, 0, 5); // Voice select
voice_t &v = m_voice[voice];
if (bitfield(page, 5)) // Page 32 - 63
{
switch (address)
{
case 0: // CR (Control Register)
v.m_alu.m_cr.stop0 = bitfield(data, 0);
v.m_alu.m_cr.stop1 = bitfield(data, 1);
v.m_alu.m_cr.lei = bitfield(data, 2);
v.m_alu.m_cr.lpe = bitfield(data, 3);
v.m_alu.m_cr.ble = bitfield(data, 4);
v.m_alu.m_cr.irqe = bitfield(data, 5);
v.m_alu.m_cr.dir = bitfield(data, 6);
v.m_alu.m_cr.irq = bitfield(data, 7);
v.m_filter.m_lp = bitfield(data, 8, 2);
v.m_cr.ca = std::min<u8>(5, bitfield(data, 10, 3));
v.m_cr.cmpd = bitfield(data, 13);
v.m_cr.bs = bitfield(data, 14, 2);
break;
case 1: // START (Loop Start Register)
v.m_alu.m_start = data & 0xfffff800;
break;
case 2: // END (Loop End Register)
v.m_alu.m_end = data & 0xffffff80;
break;
case 3: // ACCUM (Accumulator Register)
v.m_alu.m_accum = data;
break;
case 4: // O4(n-1) (Filter 4 Temp Register)
v.m_filter.m_o4_1 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 5: // O3(n-2) (Filter 3 Temp Register #2)
v.m_filter.m_o3_2 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 6: // O3(n-1) (Filter 3 Temp Register #1)
v.m_filter.m_o3_1 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 7: // O2(n-2) (Filter 2 Temp Register #2)
v.m_filter.m_o2_2 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 8: // O2(n-1) (Filter 2 Temp Register #1)
v.m_filter.m_o2_1 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 9: // O1(n-1) (Filter 1 Temp Register)
v.m_filter.m_o1_1 = sign_ext<s32>(bitfield(data, 0, 18), 18);
break;
case 10: // W_ST (Word Clock Start Register)
m_w_st = bitfield(data, 0, 7);
break;
case 11: // W_END (Word Clock End Register)
m_w_end = bitfield(data, 0, 7);
break;
case 12: // LR_END (Left/Right Clock End Register)
m_lr_end = bitfield(data, 0, 7);
m_lrclk.set_width(m_lr_end);
break;
}
}
else // Page 0 - 31
{
switch (address)
{
case 0: // CR (Control Register)
v.m_alu.m_cr.stop0 = bitfield(data, 0);
v.m_alu.m_cr.stop1 = bitfield(data, 1);
v.m_alu.m_cr.lei = bitfield(data, 2);
v.m_alu.m_cr.lpe = bitfield(data, 3);
v.m_alu.m_cr.ble = bitfield(data, 4);
v.m_alu.m_cr.irqe = bitfield(data, 5);
v.m_alu.m_cr.dir = bitfield(data, 6);
v.m_alu.m_cr.irq = bitfield(data, 7);
v.m_filter.m_lp = bitfield(data, 8, 2);
v.m_cr.ca = std::min<u8>(5, bitfield(data, 10, 3));
v.m_cr.cmpd = bitfield(data, 13);
v.m_cr.bs = bitfield(data, 14, 2);
break;
case 1: // FC (Frequency Control)
v.m_alu.m_fc = bitfield(data, 0, 17);
break;
case 2: // LVOL (Left Volume)
v.m_lvol = bitfield(data, 0, 16);
break;
case 3: // LVRAMP (Left Volume Ramp)
v.m_lvramp = bitfield(data, 8, 8);
break;
case 4: // RVOL (Right Volume)
v.m_rvol = bitfield(data, 0, 16);
break;
case 5: // RVRAMP (Right Volume Ramp)
v.m_rvramp = bitfield(data, 8, 8);
break;
case 6: // ECOUNT (Envelope Counter)
v.m_ecount = bitfield(data, 0, 9);
break;
case 7: // K2 (Filter Cutoff Coefficient #2)
v.m_filter.m_k2 = bitfield(data, 0, 16);
break;
case 8: // K2RAMP (Filter Cutoff Coefficient #2 Ramp)
v.m_k2ramp.slow = bitfield(data, 0);
v.m_k2ramp.ramp = bitfield(data, 8, 8);
break;
case 9: // K1 (Filter Cutoff Coefficient #1)
v.m_filter.m_k1 = bitfield(data, 0, 16);
break;
case 10: // K1RAMP (Filter Cutoff Coefficient #1 Ramp)
v.m_k1ramp.slow = bitfield(data, 0);
v.m_k1ramp.ramp = bitfield(data, 8, 8);
break;
case 11: // ACT (Number of voices)
m_active = std::max<u8>(4, bitfield(data, 0, 5));
break;
case 12: // MODE (Global Mode)
m_mode.lrclk_en = bitfield(data, 0);
m_mode.wclk_en = bitfield(data, 1);
m_mode.bclk_en = bitfield(data, 2);
m_mode.master = bitfield(data, 3);
m_mode.dual = bitfield(data, 4);
break;
}
}
}
}
}

View File

@ -1,190 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504 emulation core
See es550x.cpp for more info
*/
#include "es550x.hpp"
#ifndef _VGSOUND_EMU_ES5506_HPP
#define _VGSOUND_EMU_ES5506_HPP
#pragma once
// ES5506 specific
class es5506_core : public es550x_shared_core
{
public:
// constructor
es5506_core(es550x_intf &intf)
: es550x_shared_core(intf)
, m_voice{*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this,
*this,*this,*this,*this,*this,*this,*this,*this}
{
}
// host interface
u8 host_r(u8 address);
void host_w(u8 address, u8 data);
// internal state
virtual void reset() override;
virtual void tick() override;
// less cycle accurate, but also less cpu heavy update routine
void tick_perf();
// clock outputs
bool bclk() { return m_bclk.current_edge(); }
bool bclk_rising_edge() { return m_bclk.rising_edge(); }
bool bclk_falling_edge() { return m_bclk.falling_edge(); }
// 6 stereo output channels
s32 lout(u8 ch) { return m_output[std::min<u8>(5, ch & 0x7)].m_left; }
s32 rout(u8 ch) { return m_output[std::min<u8>(5, ch & 0x7)].m_right; }
//-----------------------------------------------------------------
//
// for preview/debug purpose only, not for serious emulators
//
//-----------------------------------------------------------------
// bypass chips host interface for debug purpose only
u8 read(u8 address, bool cpu_access = false);
void write(u8 address, u8 data, bool cpu_access = false);
u32 regs_r(u8 page, u8 address, bool cpu_access = false);
void regs_w(u8 page, u8 address, u32 data, bool cpu_access = false);
u8 regs8_r(u8 page, u8 address) { u8 prev = m_page; m_page = page; u8 ret = read(address, false); m_page = prev; return ret; }
void set_mute(u8 ch, bool mute) { m_voice[ch & 0x1f].m_mute = mute; }
// per-voice outputs
s32 voice_lout(u8 voice) { return (voice < 32) ? m_voice[voice].m_ch.m_left : 0; }
s32 voice_rout(u8 voice) { return (voice < 32) ? m_voice[voice].m_ch.m_right : 0; }
protected:
virtual inline u8 max_voices() override { return 32; }
virtual void voice_tick() override;
private:
struct output_t
{
void reset()
{
m_left = 0;
m_right = 0;
};
s32 m_left = 0;
s32 m_right = 0;
};
// es5506 voice structs
struct voice_t : es550x_voice_t
{
// constructor
voice_t(es5506_core &host)
: es550x_voice_t(21, 11, true)
, m_host(host) {}
// internal state
virtual void reset() override;
virtual void fetch(u8 voice, u8 cycle) override;
virtual void tick(u8 voice) override;
// accessors, getters, setters
s16 decompress(u8 sample);
s32 volume_calc(u16 volume, s32 in);
struct filter_ramp_t
{
filter_ramp_t()
: slow(0)
, ramp(0)
{ };
void reset()
{
slow = 0;
ramp = 0;
};
u16 slow : 1; // Slow mode flag
u16 ramp = 8; // Ramp value
};
// registers
es5506_core &m_host;
s32 m_lvol = 0; // Left volume - 4 bit exponent, 8 bit mantissa, 4 LSBs are used for fine control of ramp increment for hardware envelope
s32 m_lvramp = 0; // Left volume ramp
s32 m_rvol = 0; // Right volume
s32 m_rvramp = 0; // Righr volume ramp
s16 m_ecount = 0; // Envelope counter
filter_ramp_t m_k2ramp; // Filter coefficient 2 Ramp
filter_ramp_t m_k1ramp; // Filter coefficient 1 Ramp
u8 m_filtcount = 0; // Internal counter for slow mode
output_t m_ch; // channel output
bool m_mute = false; // mute flag (for debug purpose)
};
// 5 bit mode
struct mode_t
{
mode_t()
: bclk_en(0)
, wclk_en(0)
, lrclk_en(0)
, master(0)
, dual(0)
{ };
void reset()
{
bclk_en = 1;
wclk_en = 1;
lrclk_en = 1;
master = 0;
dual = 0;
}
u8 bclk_en : 1; // Set BCLK to output
u8 wclk_en : 1; // Set WCLK to output
u8 lrclk_en : 1; // Set LRCLK to output
u8 master : 1; // Set memory mode to master
u8 dual : 1; // Set dual chip config
};
voice_t m_voice[32]; // 32 voices
// Host interfaces
u32 m_read_latch = 0; // 32 bit register latch for host read
u32 m_write_latch = 0; // 32 bit register latch for host write
// Serial register
u8 m_w_st = 0; // Word clock start register
u8 m_w_end = 0; // Word clock end register
u8 m_lr_end = 0; // Left/Right clock end register
mode_t m_mode; // Global mode
// Serial related stuffs
u8 m_w_st_curr = 0; // Word clock start, current status
u8 m_w_end_curr = 0; // Word clock end register
clock_pulse_t<s8, 4, 0> m_bclk; // BCLK clock (CLKIN / 4), freely running clock
clock_pulse_t<s8, 32, 1> m_lrclk; // LRCLK
s16 m_wclk = 0; // WCLK
bool m_wclk_lr = false; // WCLK, L/R output select
s8 m_output_bit = 0; // Bit position in output
output_t m_ch[6]; // 6 stereo output channels
output_t m_output[6]; // Serial outputs
output_t m_output_temp[6]; // temporary signal for serial output
output_t m_output_latch[6]; // output latch
};
#endif

View File

@ -1,78 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504/ES5505/ES5506 emulation core
After ES5503 DOC's appeared, Ensoniq announces ES5504 DOC II, ES5505 OTIS, ES5506 OTTO.
These are not just PCM chip; but with built-in 4 pole filters and variable voice limits.
It can be trades higher sample rate and finer frequency and Tons of voices, or vice-versa.
These are mainly used with their synthesizers, musical stuffs. It's also mainly paired with ES5510 ESP/ES5511 ESP2 for post processing.
ES5506 can be paired with itself, It's called Dual chip configuration and Both chips are can be shares same memory spaces.
ES5505 was also mainly used on Taito's early- to late-90s arcade hardware for their PCM sample based sound system,
paired with ES5510 ESP for post processing. It's configuration is borrowed from Ensoniq's 32 Voice synths powered by these chips.
It's difference is external logic to adds per-voice bankswitching looks like what Konami doing on K007232.
Atari Panther was will be use ES5505, but finally canceled.
Ensoniq's ISA Sound Card for PC, Soundscape used ES5506, "Elite" model has optional daughterboard with ES5510 for digital effects.
Related chips:
ES5530 "OPUS" variant is 2-in-one chip with built-in ES5506 and Sequoia.
ES5540 "OTTOFX" variant is ES5506 and ES5510 merged in single package.
ES5548 "OTTO48" variant is used at late-90s ensoniq synths and musical instruments, 2 ES5506s are merged in single package, or with 48 voices in chip?
Chip difference:
ES5504 to ES5505:
Total voice amount is expanded to 32, rather than 25.
ADC and DAC is completely redesigned. it's has now voice-independent 10 bit and Sony/Burr-Brown format DAC.
Output channel and Volume is changed to 16 mono to 4 stereo, 12 bit Analog to 8 bit Stereo digital, also Floating point-ish format and independent per left and right output.
Channel 3 is can be Input/Output.
Channel output is can be accessible at host for test purpose.
Max sample memory is expanded to 2MWords (1MWords * 2 Banks)
ES5505 to ES5506:
Frequency is more finer now: 11 bit fraction rather than 9 bit.
Output channel and Volume is changed to 4 stereo to 6 stereo, 8 bit to 16 bit, but only 12 bit is used for calculation; 4 LSB is used for envelope ramping.
Transwave flag is added - its helpful for transwave process, with interrupt per voices.
Hardware envelope is added - K1, K2, Volume value is can be modified in run-time. also K1, K2 is expanded to 16 bit for finer envelope ramping.
Filter calculation resolution is expanded to 18 bit.
All channels are output, Serial output is now partially programmable.
Max sample memory is expanded to 8MWords (2MWords * 4 Banks)
Register format between these chips are incompatible.
*/
#include "es550x.hpp"
// Shared functions
void es550x_shared_core::reset()
{
m_host_intf.reset();
m_ha = 0;
m_hd = 0;
m_page = 0;
m_irqv.reset();
m_active = max_voices() - 1;
m_voice_cycle = 0;
m_voice_fetch = 0;
m_voice_update = false;
m_voice_end = false;
m_clkin.reset();
m_cas.reset();
m_e.reset();
}
void es550x_shared_core::es550x_voice_t::reset()
{
m_cr.reset();
m_alu.reset();
m_filter.reset();
}

View File

@ -1,284 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504/ES5505/ES5506 emulation core
See es550x.cpp for more info
*/
#ifndef _VGSOUND_EMU_ES550X_HPP
#define _VGSOUND_EMU_ES550X_HPP
#pragma once
#include <algorithm>
#include <memory>
#include "util.hpp"
// ES5504/ES5505/ES5506 interface
class es550x_intf
{
public:
virtual void e_pin(bool state) {} // E output
virtual void bclk(bool state) {} // BCLK output (serial specific)
virtual void lrclk(bool state) {} // LRCLK output (serial specific)
virtual void wclk(bool state) {} // WCLK output (serial specific)
virtual void irqb(bool state) {} // IRQB output
virtual u16 adc_r() { return 0; } // ADC input
virtual void adc_w(u16 data) {} // ADC output
virtual s16 read_sample(u8 voice, u8 bank, u32 address) { return 0; }
};
// Shared functions for ES5504/ES5505/ES5506
class es550x_shared_core
{
friend class es550x_intf; // es550x specific memory interface
public:
// constructor
es550x_shared_core(es550x_intf &intf)
: m_intf(intf)
{ };
// internal state
virtual void reset();
virtual void tick() {}
// clock outputs
bool _cas() { return m_cas.current_edge(); }
bool _cas_rising_edge() { return m_cas.rising_edge(); }
bool _cas_falling_edge() { return m_cas.falling_edge(); }
bool e() { return m_e.current_edge(); }
bool e_rising_edge() { return m_e.rising_edge(); }
bool e_falling_edge() { return m_e.falling_edge(); }
//-----------------------------------------------------------------
//
// for preview/debug purpose only, not for serious emulators
//
//-----------------------------------------------------------------
// voice cycle
u8 voice_cycle() { return m_voice_cycle; }
// voice update flag
bool voice_update() { return m_voice_update; }
bool voice_end() { return m_voice_end; }
protected:
// Constants
virtual inline u8 max_voices() { return 32; }
// Shared registers, functions
virtual void voice_tick() {} // voice tick
// Interrupt bits
struct es550x_irq_t
{
es550x_irq_t()
: voice(0)
, irqb(1)
{ };
void reset()
{
voice = 0;
irqb = 1;
}
void set(u8 index)
{
irqb = 0;
voice = index;
}
void clear()
{
irqb = 1;
voice = 0;
}
u8 voice : 5;
u8 irqb : 1;
};
// Common control bits
struct es550x_control_t
{
es550x_control_t()
: ca(0)
, adc(0)
, bs(0)
, cmpd(0)
{ };
void reset()
{
ca = 0;
adc = 0;
bs = 0;
cmpd = 0;
}
u8 ca : 4; // Channel assign (4 bit (16 channel or Bank) for ES5504, 2 bit (4 stereo channels) for ES5505, 3 bit (6 stereo channels) for ES5506)
// ES5504 Specific
u8 adc : 1; // Start ADC
// ES5505/ES5506 Specific
u8 bs : 2; // Bank bit (1 bit for ES5505, 2 bit for ES5506)
u8 cmpd : 1; // Use compressed sample format
};
// Accumulator
struct es550x_alu_t
{
es550x_alu_t(u8 integer, u8 fraction, bool transwave)
: m_integer(integer)
, m_fraction(fraction)
, m_total_bits(integer + fraction)
, m_accum_mask(u32(std::min<u64>(~0, u64(u64(1) << u64(integer + fraction)) - 1)))
, m_transwave(transwave)
{}
const u8 m_integer;
const u8 m_fraction;
const u8 m_total_bits;
const u32 m_accum_mask;
const bool m_transwave;
void reset();
bool busy();
bool tick();
void loop_exec();
s32 interpolation();
u32 get_accum_integer();
void irq_exec(es550x_intf &intf, es550x_irq_t &irqv, u8 index);
void irq_update(es550x_intf &intf, es550x_irq_t &irqv) { intf.irqb(irqv.irqb ? false : true); }
struct es550x_alu_cr_t
{
es550x_alu_cr_t()
: stop0(0)
, stop1(0)
, lpe(0)
, ble(0)
, irqe(0)
, dir(0)
, irq(0)
, lei(0)
{ };
void reset()
{
stop0 = 0;
stop1 = 0;
lpe = 0;
ble = 0;
irqe = 0;
dir = 0;
irq = 0;
lei = 0;
}
u8 stop0 : 1; // Stop with ALU
u8 stop1 : 1; // Stop with processor
u8 lpe : 1; // Loop enable
u8 ble : 1; // Bidirectional loop enable
u8 irqe : 1; // IRQ enable
u8 dir : 1; // Playback direction
u8 irq : 1; // IRQ bit
u8 lei : 1; // Loop end ignore (ES5506 specific)
};
es550x_alu_cr_t m_cr;
u32 m_fc = 0; // Frequency - 6 integer, 9 fraction for ES5506/ES5505, 6 integer, 11 fraction for ES5506
u32 m_start = 0; // Start register
u32 m_end = 0; // End register
u32 m_accum = 0; // Accumulator - 20 integer, 9 fraction for ES5506/ES5505, 21 integer, 11 fraction for ES5506
s32 m_sample[2] = {0}; // Samples
};
// Filter
struct es550x_filter_t
{
void reset();
void tick(s32 in);
s32 lp_exec(s32 coeff, s32 in, s32 prev_out);
s32 hp_exec(s32 coeff, s32 in, s32 prev_out, s32 prev_in);
// Registers
u8 m_lp = 0; // Filter mode
// Filter coefficient registers
s32 m_k2 = 0; // Filter coefficient 2 - 12 bit for filter calculation, 4 LSBs are used for fine control of ramp increment for hardware envelope (ES5506)
s32 m_k1 = 0; // Filter coefficient 1
// Filter storage registers
s32 m_o1_1 = 0; // First stage
s32 m_o2_1 = 0; // Second stage
s32 m_o2_2 = 0; // Second stage HP
s32 m_o3_1 = 0; // Third stage
s32 m_o3_2 = 0; // Third stage HP
s32 m_o4_1 = 0; // Final stage
};
// Common voice struct
struct es550x_voice_t
{
es550x_voice_t(u8 integer, u8 fraction, bool transwave)
: m_alu(integer, fraction, transwave)
{}
// internal state
virtual void reset();
virtual void fetch(u8 voice, u8 cycle) = 0;
virtual void tick(u8 voice) = 0;
es550x_control_t m_cr;
es550x_alu_t m_alu;
es550x_filter_t m_filter;
};
// Host interfaces
struct host_interface_flag_t
{
host_interface_flag_t()
: m_host_access(0)
, m_host_access_strobe(0)
, m_rw(0)
, m_rw_strobe(0)
{}
void reset()
{
m_host_access = 0;
m_host_access_strobe = 0;
m_rw = 0;
m_rw_strobe = 0;
}
u8 m_host_access : 1; // Host access trigger
u8 m_host_access_strobe : 1; // Host access strobe
u8 m_rw : 1; // R/W state
u8 m_rw_strobe : 1; // R/W strobe
};
host_interface_flag_t m_host_intf; // Host interface flag
u8 m_ha = 0; // Host address (4 bit)
u16 m_hd = 0; // Host data (16 bit for ES5504/ES5505, 8 bit for ES5506)
u8 m_page = 0; // Page
es550x_irq_t m_irqv; // Voice interrupt vector registers
// Internal states
u8 m_active = max_voices() - 1; // Activated voices (-1, ~25 for ES5504, ~32 for ES5505/ES5506)
u8 m_voice_cycle = 0; // Voice cycle
u8 m_voice_fetch = 0; // Voice fetch cycle
bool m_voice_update = false; // Voice update flag
bool m_voice_end = false; // End of one voice cycle flag
es550x_intf &m_intf; // es550x specific memory interface
clock_pulse_t<s8, 1, 0> m_clkin; // CLKIN clock
clock_pulse_t<s8, 2, 1> m_cas; // /CAS clock (CLKIN / 4), falling edge of CLKIN trigger this clock
clock_pulse_t<s8, 4, 0> m_e; // E clock (CLKIN / 8), falling edge of CLKIN trigger this clock
};
#endif

View File

@ -1,116 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504/ES5505/ES5506 Shared Accumulator emulation core
see es550x.cpp for more info
*/
#include "es550x.hpp"
// Accumulator functions
void es550x_shared_core::es550x_alu_t::reset()
{
m_cr.reset();
m_fc = 0;
m_start = 0;
m_end = 0;
m_accum = 0;
m_sample[0] = m_sample[1] = 0;
}
bool es550x_shared_core::es550x_alu_t::busy()
{
return ((!m_cr.stop0) && (!m_cr.stop1));
}
bool es550x_shared_core::es550x_alu_t::tick()
{
if (m_cr.dir)
m_accum -= m_fc;
else
m_accum += m_fc;
m_accum &= m_accum_mask;
return ((!m_cr.lei)
&& ((( m_cr.dir) && (m_accum < m_start))
|| ((!m_cr.dir) && (m_accum > m_end)))) ? true : false;
}
void es550x_shared_core::es550x_alu_t::loop_exec()
{
if (m_cr.irqe) // Set IRQ
m_cr.irq = 1;
if (m_cr.dir) // Reverse playback
{
if (m_cr.lpe) // Loop enable
{
if (m_cr.ble) // Bidirectional
{
m_cr.dir = 0;
m_accum = m_start + (m_start - m_accum);
}
else// Normal
m_accum = m_end - (m_start - m_accum);
}
else if (m_cr.ble && m_transwave) // m_transwave
{
m_cr.lpe = m_cr.ble = 0;
m_cr.lei = 1; // Loop end ignore
m_accum = m_end - (m_start - m_accum);
}
else // Stop
m_cr.stop0 = 1;
}
else
{
if (m_cr.lpe) // Loop enable
{
if (m_cr.ble) // Bidirectional
{
m_cr.dir = 1;
m_accum = m_end - (m_end - m_accum);
}
else // Normal
m_accum = (m_accum - m_end) + m_start;
}
else if (m_cr.ble && m_transwave) // m_transwave
{
m_cr.lpe = m_cr.ble = 0;
m_cr.lei = 1; // Loop end ignore
m_accum = (m_accum - m_end) + m_start;
}
else // Stop
m_cr.stop0 = 1;
}
}
s32 es550x_shared_core::es550x_alu_t::interpolation()
{
// SF = S1 + ACCfr * (S2 - S1)
return m_sample[0] + ((bitfield<s32>(m_accum, std::max<s8>(0, m_fraction - 9), 9) * (m_sample[1] - m_sample[0])) >> 9);
}
u32 es550x_shared_core::es550x_alu_t::get_accum_integer()
{
return bitfield(m_accum, m_fraction, m_integer);
}
void es550x_shared_core::es550x_alu_t::irq_exec(es550x_intf &intf, es550x_irq_t &irqv, u8 index)
{
const u8 prev = irqv.irqb;
if (m_cr.irq)
{
if (irqv.irqb)
{
irqv.set(index);
m_cr.irq = 0;
}
}
if (prev != irqv.irqb)
irq_update(intf, irqv);
}

View File

@ -1,71 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Ensoniq ES5504/ES5505/ES5506 Shared Filter emulation core
see es550x.cpp for more info
*/
#include "es550x.hpp"
// Filter functions
void es550x_shared_core::es550x_filter_t::reset()
{
m_lp = 0;
m_k2 = 0;
m_k1 = 0;
m_o1_1 = 0;
m_o2_1 = 0;
m_o2_2 = 0;
m_o3_1 = 0;
m_o3_2 = 0;
m_o4_1 = 0;
}
void es550x_shared_core::es550x_filter_t::tick(s32 in)
{
s32 coeff_k1 = s32(bitfield(m_k1, 4, 12)); // 12 MSB used
s32 coeff_k2 = s32(bitfield(m_k2, 4, 12)); // 12 MSB used
// Store previous filter data
m_o2_2 = m_o2_1;
m_o3_2 = m_o3_1;
// First and second stage: LP/K1, LP/K1 Fixed
m_o1_1 = lp_exec(coeff_k1, in, m_o1_1);
m_o2_1 = lp_exec(coeff_k1, m_o1_1, m_o2_1);
switch (m_lp)
{
case 0: // LP3 = 0, LP4 = 0: HP/K2, HP/K2
default:
m_o3_1 = hp_exec(coeff_k2, m_o2_1, m_o3_1, m_o2_2);
m_o4_1 = hp_exec(coeff_k2, m_o3_1, m_o4_1, m_o3_2);
break;
case 1: // LP3 = 0, LP4 = 1: HP/K2, LP/K1
m_o3_1 = lp_exec(coeff_k1, m_o2_1, m_o3_1);
m_o4_1 = hp_exec(coeff_k2, m_o3_1, m_o4_1, m_o3_2);
break;
case 2: // LP3 = 1, LP4 = 0: LP/K2, LP/K2
m_o3_1 = lp_exec(coeff_k2, m_o2_1, m_o3_1);
m_o4_1 = lp_exec(coeff_k2, m_o3_1, m_o4_1);
break;
case 3: // LP3 = 1, LP4 = 1: LP/K2, LP/K1
m_o3_1 = lp_exec(coeff_k1, m_o2_1, m_o3_1);
m_o4_1 = lp_exec(coeff_k2, m_o3_1, m_o4_1);
break;
}
}
s32 es550x_shared_core::es550x_filter_t::lp_exec(s32 coeff, s32 in, s32 prev_out)
{
// Yn = K*(Xn - Yn-1) + Yn-1
return ((coeff * (in - prev_out)) / 4096) + prev_out;
}
s32 es550x_shared_core::es550x_filter_t::hp_exec(s32 coeff, s32 in, s32 prev_out, s32 prev_in)
{
// Yn = Xn - Xn-1 + K*Yn-1
return in - prev_in + ((coeff * prev_out) / 8192) + (prev_out / 2);
}

View File

@ -1,141 +0,0 @@
/*
License: BSD-3-Clause
see https://github.com/cam900/vgsound_emu/blob/vgsound_emu_v1/LICENSE for more details
Copyright holder(s): cam900
Modifiers and Contributors for Furnace: cam900
Various core utilities for vgsound_emu
*/
#ifndef _VGSOUND_EMU_CORE_UTIL_HPP
#define _VGSOUND_EMU_CORE_UTIL_HPP
#pragma once
#include <iterator>
#include <algorithm>
#include <memory>
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef signed char s8;
typedef signed short s16;
typedef signed int s32;
// get bitfield, bitfield(input, position, len)
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
{
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
}
// get sign extended value, sign_ext<type>(input, len)
template<typename T> T sign_ext(T in, u8 len)
{
len = std::max<u8>(0, (8 * sizeof(T)) - len);
return T(T(in) << len) >> len;
}
// std::clamp is only for C++17 or later; I use my own code
template<typename T> T clamp(T in, T min, T max)
{
return std::min(std::max(in, min), max);
}
template<typename T, T InitWidth, u8 InitEdge = 0>
struct clock_pulse_t
{
void reset(T init = InitWidth)
{
m_edge.reset();
m_width = m_width_latch = m_counter = init;
m_cycle = 0;
}
bool tick(T width = 0)
{
bool carry = ((--m_counter) <= 0);
if (carry)
{
if (!width)
m_width = m_width_latch;
else
m_width = width; // reset width
m_counter = m_width;
m_cycle = 0;
}
else
m_cycle++;
m_edge.tick(carry);
return carry;
}
void set_width(T width) { m_width = width; }
void set_width_latch(T width) { m_width_latch = width; }
// Accessors
bool current_edge() { return m_edge.m_current; }
bool rising_edge() { return m_edge.m_rising; }
bool falling_edge() { return m_edge.m_rising; }
T cycle() { return m_cycle; }
struct edge_t
{
edge_t()
: m_current(InitEdge ^ 1)
, m_previous(InitEdge)
, m_rising(0)
, m_falling(0)
, m_changed(0)
{
set(InitEdge);
}
void tick(bool toggle)
{
u8 current = m_current;
if (toggle)
current ^= 1;
set(current);
}
void set(u8 edge)
{
edge &= 1;
m_rising = m_falling = m_changed = 0;
if (m_current != edge)
{
m_changed = 1;
if (m_current && (!edge))
m_falling = 1;
else if ((!m_current) && edge)
m_rising = 1;
m_current = edge;
}
m_previous = m_current;
}
void reset()
{
m_previous = InitEdge;
m_current = InitEdge ^ 1;
set(InitEdge);
}
u8 m_current : 1; // current edge
u8 m_previous : 1; // previous edge
u8 m_rising : 1; // rising edge
u8 m_falling : 1; // falling edge
u8 m_changed : 1; // changed flag
};
edge_t m_edge;
T m_width = InitWidth; // clock pulse width
T m_width_latch = InitWidth; // clock pulse width latch
T m_counter = InitWidth; // clock counter
T m_cycle = 0; // clock cycle
};
#endif

View File

@ -178,12 +178,6 @@ struct DivSample {
bool resampleBlep(double rate);
bool resampleSinc(double rate);
/**
* check if sample is loopable.
* @return whether it was loopable.
*/
bool isLoopable();
/**
* save this sample to a file.
* @param path a path.

View File

@ -180,6 +180,7 @@ void FurnaceGUI::drawDebug() {
ImGui::Text("loopMode: %d (<NULL!>)",(unsigned char)sample->loopMode);
}
ImGui::Text("depth: %d",(unsigned char)sample->depth);
ImGui::Text("length8: %d",sample->length8);
ImGui::Text("length16: %d",sample->length16);
ImGui::Text("length1: %d",sample->length1);

View File

@ -907,7 +907,7 @@ struct FurnaceGUIMacroDesc {
float height;
const char* displayName;
const char** bitfieldBits;
const char** modeName;
const char* modeName;
ImVec4 color;
unsigned int bitOffset;
bool isBitfield, blockMode, bit30;

View File

@ -161,9 +161,8 @@ const char* resampleStrats[DIV_RESAMPLE_MAX]={
"best possible"
};
const char* loopMode[DIV_SAMPLE_LOOPMODE_MAX]={
"Disable",
"Foward",
const char* loopMode[DIV_SAMPLE_LOOP_MAX]={
"Forward",
"Backward",
"Pingpong"
};

View File

@ -247,10 +247,6 @@ const char* n163UpdateBits[8]={
"now", "every waveform changed", NULL
};
const char* es5506FilterModes[4]={
"HP/K2, HP/K2", "HP/K2, LP/K1", "LP/K2, LP/K2", "LP/K2, LP/K1",
};
const char* suControlBits[5]={
"ring mod", "low pass", "high pass", "band pass", NULL
};
@ -263,18 +259,6 @@ const char* panBits[3]={
"right", "left", NULL
};
const char* es5506EnvelopeModes[3]={
"k1 slowdown", "k2 slowdown", NULL
};
const char* es5506ControlModes[2]={
"pause", NULL
};
const char* transwaveControlModes[2]={
"slice", NULL
};
const char* oneBit[2]={
"on", NULL
};
@ -287,6 +271,10 @@ const char* es5506ControlModes[2]={
"pause", NULL
};
const char* transwaveControlModes[2]={
"slice", NULL
};
const int orderedOps[4]={
0, 2, 1, 3
};
@ -388,21 +376,17 @@ String macroHoverES5506FilterMode(int id, float val, void* u) {
String macroLFOWaves(int id, float val, void* u) {
switch (((int)val)&3) {
case 0:
mode="Saw";
break;
return "Saw";
case 1:
mode="Square";
break;
return "Square";
case 2:
mode="Sine";
break;
return "Sine";
case 3:
mode="Random";
break;
return "Random";
default:
break;
return "???";
}
return fmt::sprintf("%d: %s",id,mode);
return "???";
}
void addAALine(ImDrawList* dl, const ImVec2& p1, const ImVec2& p2, const ImU32 color, float thickness=1.0f) {
@ -3242,10 +3226,10 @@ void FurnaceGUI::drawInsEdit() {
}
if (ImGui::BeginTabItem("FM Macros")) {
if (ins->type==DIV_INS_OPLL) {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.algMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DC),&ins->std.fmsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DM),&ins->std.amsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DC),&ins->std.fmsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_DM),&ins->std.amsMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} else {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_ALG),&ins->std.algMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_FB),&ins->std.fbMacro,0,7,96,uiColors[GUI_COLOR_MACRO_OTHER]));
@ -3311,10 +3295,10 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_MULT),&ins->std.opMacros[ordi].multMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_WS),&ins->std.opMacros[ordi].wsMacro,0,7,64,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.opMacros[ordi].susMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SUS),&ins->std.opMacros[ordi].susMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} else if (ins->type==DIV_INS_OPLL) {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER]));
@ -3324,10 +3308,10 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSL),&ins->std.opMacros[ordi].kslMacro,0,3,32,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_MULT),&ins->std.opMacros[ordi].multMacro,0,15,64,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_EGS),&ins->std.opMacros[ordi].egtMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_VIB),&ins->std.opMacros[ordi].vibMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_KSR),&ins->std.opMacros[ordi].ksrMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_EGS),&ins->std.opMacros[ordi].egtMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} else {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_TL),&ins->std.opMacros[ordi].tlMacro,0,maxTl,128,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AR),&ins->std.opMacros[ordi].arMacro,0,maxArDr,64,uiColors[GUI_COLOR_MACRO_OTHER]));
@ -3344,7 +3328,7 @@ void FurnaceGUI::drawInsEdit() {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_AM),&ins->std.opMacros[ordi].amMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
if (ins->type==DIV_INS_FM) {
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SSG),&ins->std.opMacros[ordi].ssgMacro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,ssgEnvBits));
macroList.push_back(FurnaceGUIMacroDesc(FM_NAME(FM_SSG),&ins->std.opMacros[ordi].ssgMacro,0,4,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,ssgEnvBits));
}
}
drawMacros(macroList);
@ -3826,22 +3810,22 @@ void FurnaceGUI::drawInsEdit() {
ImGui::TableNextColumn();
ImGui::Text("%s",noteNames[60+i]);
ImGui::TableNextColumn();
if (ins->amiga.noteMap[i].ind<0 || ins->amiga.noteMap[i].ind>=e->song.sampleLen) {
if (ins->amiga.noteMap[i].map<0 || ins->amiga.noteMap[i].map>=e->song.sampleLen) {
sName="-- empty --";
ins->amiga.noteMap[i].ind=-1;
ins->amiga.noteMap[i].map=-1;
} else {
sName=e->song.sample[ins->amiga.noteMap[i].ind]->name;
sName=e->song.sample[ins->amiga.noteMap[i].map]->name;
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::BeginCombo(fmt::sprintf("##SampleMap_Index_%d",i).c_str(),sName.c_str())) {
String id;
if (ImGui::Selectable("-- empty --",ins->amiga.noteMap[i].ind==-1)) { PARAMETER
ins->amiga.noteMap[i].ind=-1;
if (ImGui::Selectable("-- empty --",ins->amiga.noteMap[i].map==-1)) { PARAMETER
ins->amiga.noteMap[i].map=-1;
}
for (int j=0; j<e->song.sampleLen; j++) {
id=fmt::sprintf("%d: %s",j,e->song.sample[j]->name);
if (ImGui::Selectable(id.c_str(),ins->amiga.noteMap[i].ind==j)) { PARAMETER
ins->amiga.noteMap[i].ind=j;
if (ImGui::Selectable(id.c_str(),ins->amiga.noteMap[i].map==j)) { PARAMETER
ins->amiga.noteMap[i].map=j;
if (ins->amiga.noteMap[i].freq<=0) ins->amiga.noteMap[i].freq=(int)((double)e->song.sample[j]->centerRate*pow(2.0,((double)i-48.0)/12.0));
}
}
@ -4045,7 +4029,7 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->amiga.transWave.enable) {
if (ImGui::BeginTabItem("Transwave Macros")) {
macroList.push_back(FurnaceGUIMacroDesc("Transwave control",&ins->std.fbMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,transwaveControlModes));
macroList.push_back(FurnaceGUIMacroDesc("Transwave control",&ins->std.fbMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,transwaveControlModes));
macroList.push_back(FurnaceGUIMacroDesc("Transwave slice",&ins->std.fmsMacro,0,4095,160,uiColors[GUI_COLOR_MACRO_OTHER]));
drawMacros(macroList);
ImGui::EndTabItem();
@ -4833,14 +4817,14 @@ void FurnaceGUI::drawInsEdit() {
}
if (!panSingleNoBit) {
if (ins->type==DIV_INS_AMIGA && ins->std.panLMacro.mode) {
macroList.push_back(FurnaceGUIMacroDesc("Surround",&ins->std.panRMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Surround",&ins->std.panRMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
} else {
macroList.push_back(FurnaceGUIMacroDesc("Panning (right)",&ins->std.panRMacro,panMin,panMax,CLAMP(31+panMax-panMin,32,160),uiColors[GUI_COLOR_MACRO_OTHER]));
}
}
}
}
macroList.push_back(FurnaceGUIMacroDesc("Pitch",&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,true,macroRelativeMode));
macroList.push_back(FurnaceGUIMacroDesc("Pitch",&ins->std.pitchMacro,-2048,2047,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroRelativeMode));
if (ins->type==DIV_INS_FM ||
ins->type==DIV_INS_OPM ||
ins->type==DIV_INS_STD ||
@ -4873,7 +4857,7 @@ void FurnaceGUI::drawInsEdit() {
}
if (ex1Max>0) {
if (ins->type==DIV_INS_C64) {
macroList.push_back(FurnaceGUIMacroDesc("Filter Mode",&ins->std.ex1Macro,0,ex1Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,filtModeBits));
macroList.push_back(FurnaceGUIMacroDesc("Filter Mode",&ins->std.ex1Macro,0,ex1Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,filtModeBits));
} else if (ins->type==DIV_INS_SAA1099) {
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,saaEnvBits));
} else if (ins->type==DIV_INS_X1_010 && !ins->amiga.useSample) {
@ -4900,7 +4884,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_C64) {
macroList.push_back(FurnaceGUIMacroDesc("Resonance",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("Wave Update",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,n163UpdateBits));
macroList.push_back(FurnaceGUIMacroDesc("Wave Update",&ins->std.ex2Macro,0,ex2Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));
} else if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Speed",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_OTHER]));
} else if (ins->type==DIV_INS_SU) {
@ -4912,12 +4896,12 @@ void FurnaceGUI::drawInsEdit() {
} else if (ins->type==DIV_INS_SNES) {
macroList.push_back(FurnaceGUIMacroDesc("Gain Rate",&ins->std.ex2Macro,0,ex2Max,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
} else {
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,ex2Bit,ayEnvBits));
macroList.push_back(FurnaceGUIMacroDesc("Envelope",&ins->std.ex2Macro,0,ex2Max,ex2Bit?64:160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,ex2Bit,ayEnvBits));
}
}
if (ins->type==DIV_INS_C64) {
macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex3Macro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,c64SpecialBits));
macroList.push_back(FurnaceGUIMacroDesc("Test/Gate",&ins->std.ex4Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex3Macro,0,2,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,c64SpecialBits));
macroList.push_back(FurnaceGUIMacroDesc("Test/Gate",&ins->std.ex4Macro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || (ins->type==DIV_INS_X1_010 && !ins->amiga.useSample)) {
macroList.push_back(FurnaceGUIMacroDesc("AutoEnv Num",&ins->std.ex3Macro,0,15,160,uiColors[GUI_COLOR_MACRO_OTHER]));
@ -4925,14 +4909,14 @@ void FurnaceGUI::drawInsEdit() {
}
if (ins->type==DIV_INS_AY8930) {
// oh my i am running out of macros
macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Noise AND Mask",&ins->std.fbMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
macroList.push_back(FurnaceGUIMacroDesc("Noise OR Mask",&ins->std.fmsMacro,0,8,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true));
}
if (ins->type==DIV_INS_N163) {
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Wave",&ins->std.ex3Macro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Pos",&ins->std.algMacro,0,255,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Len",&ins->std.fbMacro,0,252,160,uiColors[GUI_COLOR_MACRO_OTHER]));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Trigger",&ins->std.fmsMacro,0,2,160,uiColors[GUI_COLOR_MACRO_OTHER],false,false,NULL,NULL,true,n163UpdateBits));
macroList.push_back(FurnaceGUIMacroDesc("WaveLoad Trigger",&ins->std.fmsMacro,0,2,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,n163UpdateBits));
}
if (ins->type==DIV_INS_FDS) {
macroList.push_back(FurnaceGUIMacroDesc("Mod Position",&ins->std.ex3Macro,0,127,160,uiColors[GUI_COLOR_MACRO_OTHER]));

View File

@ -116,16 +116,11 @@ void FurnaceGUI::drawSampleEdit() {
sample->loopStart=-1;
sample->loopEnd=sample->samples;
}
if (sample->loopEnd>sample->samples) {
sample->loopEnd=sample->samples;
}
sample->loopMode=DivSampleLoopMode(mode);
updateSampleTex=true;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Length: %d",sample->samples);
bool doLoop=(sample->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT);
if (doLoop) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
@ -678,16 +673,11 @@ void FurnaceGUI::drawSampleEdit() {
sample->loopStart=-1;
sample->loopEnd=sample->samples;
}
if (sample->loopEnd>sample->samples) {
sample->loopEnd=sample->samples;
}
sample->loopMode=DivSampleLoopMode(mode);
updateSampleTex=true;
}
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("Length: %d",sample->samples);
bool doLoop=(sample->loopMode!=DIV_SAMPLE_LOOPMODE_ONESHOT);
if (doLoop) {
ImGui::TableNextRow();
ImGui::TableNextColumn();