Merge branch 'master' into ZSMv1
This commit is contained in:
commit
ae7b271e1d
128 changed files with 8022 additions and 1597 deletions
|
|
@ -1431,7 +1431,10 @@ void DivEngine::stop() {
|
|||
freelance=false;
|
||||
playing=false;
|
||||
extValuePresent=false;
|
||||
endOfSong=false; // what?
|
||||
stepPlay=0;
|
||||
curOrder=prevOrder;
|
||||
curRow=prevRow;
|
||||
remainingLoops=-1;
|
||||
sPreview.sample=-1;
|
||||
sPreview.wave=-1;
|
||||
|
|
@ -2067,6 +2070,8 @@ int DivEngine::addSample() {
|
|||
sample->name=fmt::sprintf("Sample %d",sampleCount);
|
||||
song.sample.push_back(sample);
|
||||
song.sampleLen=sampleCount+1;
|
||||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
saveLock.unlock();
|
||||
renderSamples();
|
||||
BUSY_END;
|
||||
|
|
@ -2183,6 +2188,7 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
return -1;
|
||||
#else
|
||||
SF_INFO si;
|
||||
memset(&si,0,sizeof(SF_INFO));
|
||||
SNDFILE* f=sf_open(path,SFM_READ,&si);
|
||||
if (f==NULL) {
|
||||
BUSY_END;
|
||||
|
|
@ -2200,8 +2206,22 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
BUSY_END;
|
||||
return -1;
|
||||
}
|
||||
short* buf=new short[si.channels*si.frames];
|
||||
if (sf_readf_short(f,buf,si.frames)!=si.frames) {
|
||||
void* buf=NULL;
|
||||
sf_count_t sampleLen=sizeof(short);
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
logD("sample is 8-bit unsigned");
|
||||
buf=new unsigned char[si.channels*si.frames];
|
||||
sampleLen=sizeof(unsigned char);
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
logD("sample is 32-bit float");
|
||||
buf=new float[si.channels*si.frames];
|
||||
sampleLen=sizeof(float);
|
||||
} else {
|
||||
logD("sample is 16-bit signed");
|
||||
buf=new short[si.channels*si.frames];
|
||||
sampleLen=sizeof(short);
|
||||
}
|
||||
if (sf_read_raw(f,buf,si.frames*si.channels*sampleLen)!=(si.frames*si.channels*sampleLen)) {
|
||||
logW("sample read size mismatch!");
|
||||
}
|
||||
DivSample* sample=new DivSample;
|
||||
|
|
@ -2215,19 +2235,41 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
sample->depth=16;
|
||||
}
|
||||
sample->init(si.frames);
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=buf[i+j];
|
||||
if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((int)((unsigned char*)buf)[i+j])-128;
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data8[index++]=averaged;
|
||||
}
|
||||
averaged/=si.channels;
|
||||
if (((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_PCM_U8)) {
|
||||
sample->data8[index++]=averaged>>8;
|
||||
} else {
|
||||
delete[] (unsigned char*)buf;
|
||||
} else if ((si.format&SF_FORMAT_SUBMASK)==SF_FORMAT_FLOAT) {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
float averaged=0.0f;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((float*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
averaged*=32767.0;
|
||||
if (averaged<-32768.0) averaged=-32768.0;
|
||||
if (averaged>32767.0) averaged=32767.0;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (float*)buf;
|
||||
} else {
|
||||
for (int i=0; i<si.frames*si.channels; i+=si.channels) {
|
||||
int averaged=0;
|
||||
for (int j=0; j<si.channels; j++) {
|
||||
averaged+=((short*)buf)[i+j];
|
||||
}
|
||||
averaged/=si.channels;
|
||||
sample->data16[index++]=averaged;
|
||||
}
|
||||
delete[] (short*)buf;
|
||||
}
|
||||
delete[] buf;
|
||||
|
||||
sample->rate=si.samplerate;
|
||||
if (sample->rate<4000) sample->rate=4000;
|
||||
if (sample->rate>96000) sample->rate=96000;
|
||||
|
|
@ -2265,6 +2307,8 @@ int DivEngine::addSampleFromFile(const char* path) {
|
|||
|
||||
void DivEngine::delSample(int index) {
|
||||
BUSY_BEGIN;
|
||||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
saveLock.lock();
|
||||
if (index>=0 && index<(int)song.sample.size()) {
|
||||
delete song.sample[index];
|
||||
|
|
@ -2479,6 +2523,8 @@ bool DivEngine::moveWaveUp(int which) {
|
|||
bool DivEngine::moveSampleUp(int which) {
|
||||
if (which<1 || which>=(int)song.sample.size()) return false;
|
||||
BUSY_BEGIN;
|
||||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
DivSample* prev=song.sample[which];
|
||||
saveLock.lock();
|
||||
song.sample[which]=song.sample[which-1];
|
||||
|
|
@ -2516,6 +2562,8 @@ bool DivEngine::moveWaveDown(int which) {
|
|||
bool DivEngine::moveSampleDown(int which) {
|
||||
if (which<0 || which>=((int)song.sample.size())-1) return false;
|
||||
BUSY_BEGIN;
|
||||
sPreview.sample=-1;
|
||||
sPreview.pos=0;
|
||||
DivSample* prev=song.sample[which];
|
||||
saveLock.lock();
|
||||
song.sample[which]=song.sample[which+1];
|
||||
|
|
@ -2980,6 +3028,8 @@ bool DivEngine::initAudioBackend() {
|
|||
if (!output->midiIn->openDevice(inName)) {
|
||||
logW("could not open MIDI input device!");
|
||||
}
|
||||
} else {
|
||||
logV("no MIDI input device selected.");
|
||||
}
|
||||
}
|
||||
if (output->midiOut) {
|
||||
|
|
@ -2990,6 +3040,8 @@ bool DivEngine::initAudioBackend() {
|
|||
if (!output->midiOut->openDevice(outName)) {
|
||||
logW("could not open MIDI output device!");
|
||||
}
|
||||
} else {
|
||||
logV("no MIDI output device selected.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ struct DivChannelState {
|
|||
int note, oldNote, lastIns, pitch, portaSpeed, portaNote;
|
||||
int volume, volSpeed, cut, rowDelay, volMax;
|
||||
int delayOrder, delayRow, retrigSpeed, retrigTick;
|
||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoDir, vibratoFine;
|
||||
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
|
||||
int tremoloDepth, tremoloRate, tremoloPos;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
|
|
@ -112,6 +112,7 @@ struct DivChannelState {
|
|||
vibratoDepth(0),
|
||||
vibratoRate(0),
|
||||
vibratoPos(0),
|
||||
vibratoPosGiant(0),
|
||||
vibratoDir(0),
|
||||
vibratoFine(15),
|
||||
tremoloDepth(0),
|
||||
|
|
|
|||
|
|
@ -22,38 +22,6 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "fmshared_OPM.h"
|
||||
|
||||
static unsigned short chanOffs[8]={
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
|
||||
};
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x08, 0x10, 0x18
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define NOTE_LINEAR(x) (((x)<<6)+baseFreqOff+log2(parent->song.tuning/440.0)*12.0*64.0)
|
||||
|
||||
const char* regCheatSheetOPM[]={
|
||||
"Test", "00",
|
||||
"NoteCtl", "08",
|
||||
|
|
@ -198,7 +166,7 @@ void DivPlatformArcade::acquire_nuked(short* bufL, short* bufR, size_t start, si
|
|||
OPM_Write(&fm,1,w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
//printf("write: %x = %.2x\n",w.addr,w.val);
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
} else {
|
||||
OPM_Write(&fm,0,w.addr);
|
||||
w.addrOrVal=true;
|
||||
|
|
@ -239,7 +207,7 @@ void DivPlatformArcade::acquire_ymfm(short* bufL, short* bufR, size_t start, siz
|
|||
fm_ymfm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=1;
|
||||
}
|
||||
}
|
||||
|
|
@ -938,7 +906,7 @@ void DivPlatformArcade::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformArcade::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,256);
|
||||
if (useYMFM) {
|
||||
fm_ymfm->reset();
|
||||
|
|
@ -978,15 +946,20 @@ void DivPlatformArcade::reset() {
|
|||
}
|
||||
|
||||
void DivPlatformArcade::setFlags(unsigned int flags) {
|
||||
if (flags==2) {
|
||||
chipClock=4000000.0;
|
||||
baseFreqOff=-122;
|
||||
} else if (flags==1) {
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
baseFreqOff=12;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
baseFreqOff=0;
|
||||
switch (flags&0xff) {
|
||||
default:
|
||||
case 0:
|
||||
chipClock=COLOR_NTSC;
|
||||
baseFreqOff=0;
|
||||
break;
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
baseFreqOff=12;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=4000000.0;
|
||||
baseFreqOff=-122;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/64;
|
||||
for (int i=0; i<8; i++) {
|
||||
|
|
|
|||
|
|
@ -19,19 +19,23 @@
|
|||
|
||||
#ifndef _ARCADE_H
|
||||
#define _ARCADE_H
|
||||
#include "../dispatch.h"
|
||||
#include "fmshared_OPM.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../instrument.h"
|
||||
#include <queue>
|
||||
#include "../../../extern/opm/opm.h"
|
||||
#include "sound/ymfm/ymfm_opm.h"
|
||||
#include "../macroInt.h"
|
||||
|
||||
class DivArcadeInterface: public ymfm::ymfm_interface {
|
||||
|
||||
};
|
||||
|
||||
class DivPlatformArcade: public DivDispatch {
|
||||
class DivPlatformArcade: public DivPlatformOPM {
|
||||
protected:
|
||||
const unsigned short chanOffs[8]={
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
|
|
@ -71,31 +75,18 @@ class DivPlatformArcade: public DivDispatch {
|
|||
};
|
||||
Channel chan[8];
|
||||
DivDispatchOscBuffer* oscBuf[8];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
opm_t fm;
|
||||
int delay, baseFreqOff;
|
||||
int baseFreqOff;
|
||||
int pcmL, pcmR, pcmCycles;
|
||||
unsigned char lastBusy;
|
||||
unsigned char amDepth, pmDepth;
|
||||
|
||||
ymfm::ym2151* fm_ymfm;
|
||||
ymfm::ym2151::output_data out_ymfm;
|
||||
DivArcadeInterface iface;
|
||||
|
||||
unsigned char regPool[256];
|
||||
|
||||
bool extMode, useYMFM;
|
||||
|
||||
bool isMuted[8];
|
||||
|
||||
short oldWrites[256];
|
||||
short pendingWrites[256];
|
||||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(regRemap(a),v); if (dumpWrites) {addWrite(regRemap(a),v);} }
|
||||
|
||||
#define CHIP_DIVIDER ((sunsoft||clockSel)?16:8)
|
||||
#define CHIP_DIVIDER (extMode?extDiv:((sunsoft||clockSel)?16:8))
|
||||
|
||||
const char* regCheatSheetAY[]={
|
||||
"FreqL_A", "0",
|
||||
|
|
@ -489,9 +489,9 @@ void DivPlatformAY8910::muteChannel(int ch, bool mute) {
|
|||
isMuted[ch]=mute;
|
||||
if (isMuted[ch]) {
|
||||
rWrite(0x08+ch,0);
|
||||
} else if (intellivision && (chan[ch].psgMode&4)) {
|
||||
} else if (intellivision && (chan[ch].psgMode&4) && chan[ch].active) {
|
||||
rWrite(0x08+ch,(chan[ch].vol&0xc)<<2);
|
||||
} else {
|
||||
} else if (chan[ch].active) {
|
||||
rWrite(0x08+ch,(chan[ch].outVol&15)|((chan[ch].psgMode&4)<<2));
|
||||
}
|
||||
}
|
||||
|
|
@ -565,8 +565,6 @@ void DivPlatformAY8910::reset() {
|
|||
|
||||
delay=0;
|
||||
|
||||
extMode=false;
|
||||
|
||||
ioPortA=false;
|
||||
ioPortB=false;
|
||||
portAVal=0;
|
||||
|
|
@ -595,50 +593,69 @@ void DivPlatformAY8910::poke(std::vector<DivRegWrite>& wlist) {
|
|||
for (DivRegWrite& i: wlist) immWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::setFlags(unsigned int flags) {
|
||||
clockSel=(flags>>7)&1;
|
||||
switch (flags&15) {
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*2.0/5.0;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=1750000;
|
||||
break;
|
||||
case 3:
|
||||
chipClock=2000000;
|
||||
break;
|
||||
case 4:
|
||||
chipClock=1500000;
|
||||
break;
|
||||
case 5:
|
||||
chipClock=1000000;
|
||||
break;
|
||||
case 6:
|
||||
chipClock=COLOR_NTSC/4.0;
|
||||
break;
|
||||
case 7:
|
||||
chipClock=COLOR_PAL*3.0/8.0;
|
||||
break;
|
||||
case 8:
|
||||
chipClock=COLOR_PAL*3.0/16.0;
|
||||
break;
|
||||
case 9:
|
||||
chipClock=COLOR_PAL/4.0;
|
||||
break;
|
||||
case 10:
|
||||
chipClock=2097152;
|
||||
break;
|
||||
case 11:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 12:
|
||||
chipClock=3600000;
|
||||
break;
|
||||
default:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
break;
|
||||
void DivPlatformAY8910::setExtClockDiv(unsigned int eclk, unsigned char ediv) {
|
||||
if (extMode) {
|
||||
extClock=eclk;
|
||||
extDiv=ediv;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformAY8910::setFlags(unsigned int flags) {
|
||||
if (extMode) {
|
||||
chipClock=extClock;
|
||||
rate=chipClock/extDiv;
|
||||
} else {
|
||||
clockSel=(flags>>7)&1;
|
||||
switch (flags&15) {
|
||||
default:
|
||||
case 0:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
break;
|
||||
case 1:
|
||||
chipClock=COLOR_PAL*2.0/5.0;
|
||||
break;
|
||||
case 2:
|
||||
chipClock=1750000;
|
||||
break;
|
||||
case 3:
|
||||
chipClock=2000000;
|
||||
break;
|
||||
case 4:
|
||||
chipClock=1500000;
|
||||
break;
|
||||
case 5:
|
||||
chipClock=1000000;
|
||||
break;
|
||||
case 6:
|
||||
chipClock=COLOR_NTSC/4.0;
|
||||
break;
|
||||
case 7:
|
||||
chipClock=COLOR_PAL*3.0/8.0;
|
||||
break;
|
||||
case 8:
|
||||
chipClock=COLOR_PAL*3.0/16.0;
|
||||
break;
|
||||
case 9:
|
||||
chipClock=COLOR_PAL/4.0;
|
||||
break;
|
||||
case 10:
|
||||
chipClock=2097152;
|
||||
break;
|
||||
case 11:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 12:
|
||||
chipClock=3600000;
|
||||
break;
|
||||
case 13:
|
||||
chipClock=20000000/16;
|
||||
break;
|
||||
case 14:
|
||||
chipClock=1536000;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/8;
|
||||
}
|
||||
rate=chipClock/8;
|
||||
for (int i=0; i<3; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,9 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
int delay;
|
||||
|
||||
bool extMode;
|
||||
unsigned int extClock;
|
||||
unsigned char extDiv;
|
||||
|
||||
bool stereo, sunsoft, intellivision, clockSel;
|
||||
bool ioPortA, ioPortB;
|
||||
unsigned char portAVal, portBVal;
|
||||
|
|
@ -88,6 +91,7 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void setExtClockDiv(unsigned int eclk=COLOR_NTSC, unsigned char ediv=8);
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
|
|
@ -111,5 +115,10 @@ class DivPlatformAY8910: public DivDispatch {
|
|||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformAY8910(bool useExtMode=false, unsigned int eclk=COLOR_NTSC, unsigned char ediv=8):
|
||||
DivDispatch(),
|
||||
extMode(useExtMode),
|
||||
extClock(eclk),
|
||||
extDiv(ediv) {}
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ void DivPlatformAY8930::muteChannel(int ch, bool mute) {
|
|||
isMuted[ch]=mute;
|
||||
if (isMuted[ch]) {
|
||||
rWrite(0x08+ch,0);
|
||||
} else {
|
||||
} else if (chan[ch].active) {
|
||||
rWrite(0x08+ch,(chan[ch].outVol&31)|((chan[ch].psgMode&4)<<3));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ void DivPlatformBubSysWSG::reset() {
|
|||
for (int i=0; i<2; i++) {
|
||||
chan[i]=DivPlatformBubSysWSG::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent,8);
|
||||
chan[i].ws.init(NULL,32,15,false);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
|
|
|
|||
|
|
@ -17,13 +17,32 @@
|
|||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define ADDR_MULT_DT 0x40
|
||||
#define ADDR_TL 0x60
|
||||
#define ADDR_RS_AR 0x80
|
||||
#define ADDR_AM_DR 0xa0
|
||||
#define ADDR_DT2_D2R 0xc0
|
||||
#define ADDR_SL_RR 0xe0
|
||||
#define ADDR_NOTE 0x28
|
||||
#define ADDR_KF 0x30
|
||||
#define ADDR_FMS_AMS 0x38
|
||||
#define ADDR_LR_FB_ALG 0x20
|
||||
#ifndef _FMSHARED_OPM_H
|
||||
#define _FMSHARED_OPM_H
|
||||
|
||||
#include "fmsharedbase.h"
|
||||
|
||||
#define NOTE_LINEAR(x) (((x)<<6)+baseFreqOff+log2(parent->song.tuning/440.0)*12.0*64.0)
|
||||
|
||||
class DivPlatformOPM: public DivPlatformFMBase {
|
||||
protected:
|
||||
const unsigned short ADDR_MULT_DT=0x40;
|
||||
const unsigned short ADDR_TL=0x60;
|
||||
const unsigned short ADDR_RS_AR=0x80;
|
||||
const unsigned short ADDR_AM_DR=0xa0;
|
||||
const unsigned short ADDR_DT2_D2R=0xc0;
|
||||
const unsigned short ADDR_SL_RR=0xe0;
|
||||
const unsigned short ADDR_NOTE=0x28;
|
||||
const unsigned short ADDR_KF=0x30;
|
||||
const unsigned short ADDR_FMS_AMS=0x38;
|
||||
const unsigned short ADDR_LR_FB_ALG=0x20;
|
||||
|
||||
const unsigned short opOffs[4]={
|
||||
0x00, 0x08, 0x10, 0x18
|
||||
};
|
||||
|
||||
DivPlatformOPM():
|
||||
DivPlatformFMBase() {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,17 +20,7 @@
|
|||
#ifndef _FMSHARED_OPN_H
|
||||
#define _FMSHARED_OPN_H
|
||||
|
||||
#define ADDR_MULT_DT 0x30
|
||||
#define ADDR_TL 0x40
|
||||
#define ADDR_RS_AR 0x50
|
||||
#define ADDR_AM_DR 0x60
|
||||
#define ADDR_DT2_D2R 0x70
|
||||
#define ADDR_SL_RR 0x80
|
||||
#define ADDR_SSG 0x90
|
||||
#define ADDR_FREQ 0xa0
|
||||
#define ADDR_FREQH 0xa4
|
||||
#define ADDR_FB_ALG 0xb0
|
||||
#define ADDR_LRAF 0xb4
|
||||
#include "fmsharedbase.h"
|
||||
|
||||
#define PLEASE_HELP_ME(_targetChan) \
|
||||
int boundaryBottom=parent->calcBaseFreq(chipClock,CHIP_FREQBASE,0,false); \
|
||||
|
|
@ -93,4 +83,34 @@
|
|||
return 2; \
|
||||
}
|
||||
|
||||
#endif
|
||||
class DivPlatformOPN: public DivPlatformFMBase {
|
||||
protected:
|
||||
const unsigned short ADDR_MULT_DT=0x30;
|
||||
const unsigned short ADDR_TL=0x40;
|
||||
const unsigned short ADDR_RS_AR=0x50;
|
||||
const unsigned short ADDR_AM_DR=0x60;
|
||||
const unsigned short ADDR_DT2_D2R=0x70;
|
||||
const unsigned short ADDR_SL_RR=0x80;
|
||||
const unsigned short ADDR_SSG=0x90;
|
||||
const unsigned short ADDR_FREQ=0xa0;
|
||||
const unsigned short ADDR_FREQH=0xa4;
|
||||
const unsigned short ADDR_FB_ALG=0xb0;
|
||||
const unsigned short ADDR_LRAF=0xb4;
|
||||
|
||||
const unsigned short opOffs[4]={
|
||||
0x00, 0x04, 0x08, 0x0c
|
||||
};
|
||||
|
||||
double fmFreqBase;
|
||||
unsigned int fmDivBase;
|
||||
unsigned int ayDiv;
|
||||
|
||||
DivPlatformOPN(double f=9440540.0, unsigned int d=72, unsigned int a=32):
|
||||
DivPlatformFMBase(),
|
||||
fmFreqBase(f),
|
||||
fmDivBase(d),
|
||||
ayDiv(a) {}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
96
src/engine/platform/fmsharedbase.h
Normal file
96
src/engine/platform/fmsharedbase.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _FMSHARED_BASE_H
|
||||
#define _FMSHARED_BASE_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <deque>
|
||||
|
||||
class DivPlatformFMBase: public DivDispatch {
|
||||
protected:
|
||||
const bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
const unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
const int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::deque<QueuedWrite> writes;
|
||||
|
||||
unsigned char lastBusy;
|
||||
int delay;
|
||||
|
||||
unsigned char regPool[512];
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
inline void rWrite(unsigned short a, short v) {
|
||||
if (!skipRegisterWrites) {
|
||||
pendingWrites[a]=v;
|
||||
}
|
||||
}
|
||||
inline void immWrite(unsigned short a, unsigned char v) {
|
||||
if (!skipRegisterWrites) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
if (dumpWrites) {
|
||||
addWrite(a,v);
|
||||
}
|
||||
}
|
||||
}
|
||||
inline void urgentWrite(unsigned short a, unsigned char v) {
|
||||
if (!skipRegisterWrites) {
|
||||
if (writes.empty()) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
} else if (writes.size()>16 || writes.front().addrOrVal) {
|
||||
writes.push_back(QueuedWrite(a,v));
|
||||
} else {
|
||||
writes.push_front(QueuedWrite(a,v));
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(a,v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformFMBase():
|
||||
DivDispatch(),
|
||||
lastBusy(0),
|
||||
delay(0) {}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -22,17 +22,11 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "genesisshared.h"
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
#define IS_REALLY_MUTED(x) (isMuted[x] && (x<5 || !softPCM || (isMuted[5] && isMuted[6])))
|
||||
|
||||
static unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
#define CHIP_DIVIDER 72
|
||||
#define CHIP_FREQBASE 9440540
|
||||
|
||||
const char* DivPlatformGenesis::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
|
|
@ -1247,12 +1241,13 @@ void DivPlatformGenesis::setSoftPCM(bool value) {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::setFlags(unsigned int flags) {
|
||||
switch (flags) {
|
||||
switch (flags&(~0x80000000)) {
|
||||
default:
|
||||
case 0: chipClock=COLOR_NTSC*15.0/7.0; break;
|
||||
case 1: chipClock=COLOR_PAL*12.0/7.0; break;
|
||||
case 2: chipClock=8000000.0; break;
|
||||
case 3: chipClock=COLOR_NTSC*12.0/7.0; break;
|
||||
case 4: chipClock=COLOR_NTSC*9.0/4.0; break;
|
||||
default: chipClock=COLOR_NTSC*15.0/7.0; break;
|
||||
}
|
||||
ladder=flags&0x80000000;
|
||||
OPN2_SetChipType(ladder?ym3438_mode_ym2612:0);
|
||||
|
|
|
|||
|
|
@ -19,19 +19,26 @@
|
|||
|
||||
#ifndef _GENESIS_H
|
||||
#define _GENESIS_H
|
||||
#include "../dispatch.h"
|
||||
#include <deque>
|
||||
#include "fmshared_OPN.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../../../extern/Nuked-OPN2/ym3438.h"
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "sms.h"
|
||||
|
||||
class DivYM2612Interface: public ymfm::ymfm_interface {
|
||||
|
||||
};
|
||||
|
||||
class DivPlatformGenesis: public DivDispatch {
|
||||
class DivPlatformGenesis: public DivPlatformOPN {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
const unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
|
|
@ -92,21 +99,11 @@ class DivPlatformGenesis: public DivDispatch {
|
|||
Channel chan[10];
|
||||
DivDispatchOscBuffer* oscBuf[10];
|
||||
bool isMuted[10];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::deque<QueuedWrite> writes;
|
||||
ym3438_t fm;
|
||||
int delay;
|
||||
unsigned char lastBusy;
|
||||
|
||||
ymfm::ym2612* fm_ymfm;
|
||||
ymfm::ym2612::output_data out_ymfm;
|
||||
DivYM2612Interface iface;
|
||||
unsigned char regPool[512];
|
||||
|
||||
unsigned char lfoValue;
|
||||
|
||||
|
|
@ -115,9 +112,6 @@ class DivPlatformGenesis: public DivDispatch {
|
|||
bool extMode, softPCM, useYMFM;
|
||||
bool ladder;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
unsigned char dacVolTable[128];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
@ -153,6 +147,8 @@ class DivPlatformGenesis: public DivDispatch {
|
|||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformGenesis():
|
||||
DivPlatformOPN(9440540.0, 72, 32) {}
|
||||
~DivPlatformGenesis();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,10 +21,8 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "genesisshared.h"
|
||||
|
||||
#define CHIP_DIVIDER 72
|
||||
#define CHIP_FREQBASE 9440540
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
int DivPlatformGenesisExt::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
static unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x04, 0x08, 0x0c
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.push_back(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define urgentWrite(a,v) if (!skipRegisterWrites) { \
|
||||
if (writes.empty()) { \
|
||||
writes.push_back(QueuedWrite(a,v)); \
|
||||
} else if (writes.size()>16 || writes.front().addrOrVal) { \
|
||||
writes.push_back(QueuedWrite(a,v)); \
|
||||
} else { \
|
||||
writes.push_front(QueuedWrite(a,v)); \
|
||||
} \
|
||||
if (dumpWrites) { \
|
||||
addWrite(a,v); \
|
||||
} \
|
||||
}
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
|
@ -380,7 +380,7 @@ void DivPlatformMSM6258::setFlags(unsigned int flags) {
|
|||
chipClock=4000000;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/128;
|
||||
rate=chipClock/256;
|
||||
for (int i=0; i<1; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,10 +26,6 @@
|
|||
|
||||
class DivPlatformMSM6258: public DivDispatch {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
|
|
@ -77,12 +73,10 @@ class DivPlatformMSM6258: public DivDispatch {
|
|||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
okim6258_device* msm;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmMem;
|
||||
|
|
@ -93,11 +87,6 @@ class DivPlatformMSM6258: public DivDispatch {
|
|||
|
||||
int delay, updateOsc, sample, samplePos;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
#define rWriteDelay(a,v,d) if (!skipRegisterWrites) {writes.emplace(a,v,d); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
const char** DivPlatformMSM6295::getRegisterSheet() {
|
||||
return NULL;
|
||||
|
|
@ -38,8 +39,11 @@ const char* DivPlatformMSM6295::getEffectName(unsigned char effect) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
u8 DivMSM6295Interface::read_byte(u32 address) {
|
||||
return adpcmMem[address&0xffff];
|
||||
u8 DivPlatformMSM6295::read_byte(u32 address) {
|
||||
if (adpcmMem==NULL || address>=getSampleMemCapacity(0)) {
|
||||
return 0;
|
||||
}
|
||||
return adpcmMem[address&0x3ffff];
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
|
|
@ -49,7 +53,7 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
QueuedWrite& w=writes.front();
|
||||
switch (w.addr) {
|
||||
case 0: // command
|
||||
msm->command_w(w.val);
|
||||
msm.command_w(w.val);
|
||||
break;
|
||||
case 8: // chip clock select (VGM)
|
||||
case 9:
|
||||
|
|
@ -57,7 +61,7 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
case 11:
|
||||
break;
|
||||
case 12: // rate select
|
||||
msm->ss_w(!w.val);
|
||||
msm.ss_w(!w.val);
|
||||
break;
|
||||
case 14: // enable bankswitch
|
||||
break;
|
||||
|
|
@ -70,21 +74,21 @@ void DivPlatformMSM6295::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
break;
|
||||
}
|
||||
writes.pop();
|
||||
delay=32;
|
||||
delay=w.delay;
|
||||
}
|
||||
} else {
|
||||
delay--;
|
||||
}
|
||||
|
||||
msm->tick();
|
||||
msm.tick();
|
||||
|
||||
bufL[h]=msm->out()<<4;
|
||||
bufL[h]=msm.out()<<4;
|
||||
|
||||
if (++updateOsc>=22) {
|
||||
updateOsc=0;
|
||||
// TODO: per-channel osc
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm->m_voice[i].m_muted?0:(msm->m_voice[i].m_out<<6);
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=msm.m_voice[i].m_muted?0:(msm.m_voice[i].m_out<<6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -118,7 +122,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
rWrite(0,(8<<c.chan)); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
} else {
|
||||
|
|
@ -133,7 +137,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
}
|
||||
//DivSample* s=parent->getSample(12*sampleBank+c.value%12);
|
||||
chan[c.chan].sample=12*sampleBank+c.value%12;
|
||||
rWrite(0,(8<<c.chan)); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
rWrite(0,0x80|chan[c.chan].sample); // set phrase
|
||||
rWrite(0,(16<<c.chan)|(8-chan[c.chan].outVol)); // turn on
|
||||
}
|
||||
|
|
@ -143,14 +147,14 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWrite(0,(8<<c.chan)); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].keyOn=false;
|
||||
chan[c.chan].active=false;
|
||||
rWrite(0,(8<<c.chan)); // turn off
|
||||
rWriteDelay(0,(8<<c.chan),60); // turn off
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
|
|
@ -188,7 +192,6 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
if (sampleBank>(parent->song.sample.size()/12)) {
|
||||
sampleBank=parent->song.sample.size()/12;
|
||||
}
|
||||
iface.sampleBank=sampleBank;
|
||||
break;
|
||||
case DIV_CMD_LEGATO: {
|
||||
break;
|
||||
|
|
@ -212,7 +215,7 @@ int DivPlatformMSM6295::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformMSM6295::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
msm->m_voice[ch].m_muted=mute;
|
||||
msm.m_voice[ch].m_muted=mute;
|
||||
}
|
||||
|
||||
void DivPlatformMSM6295::forceIns() {
|
||||
|
|
@ -253,8 +256,8 @@ void DivPlatformMSM6295::poke(std::vector<DivRegWrite>& wlist) {
|
|||
|
||||
void DivPlatformMSM6295::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
msm->reset();
|
||||
msm->ss_w(false);
|
||||
msm.reset();
|
||||
msm.ss_w(rateSelInit);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
|
@ -268,7 +271,8 @@ void DivPlatformMSM6295::reset() {
|
|||
}
|
||||
|
||||
sampleBank=0;
|
||||
rateSel=false;
|
||||
rateSel=rateSelInit;
|
||||
rWrite(12,!rateSelInit);
|
||||
|
||||
delay=0;
|
||||
}
|
||||
|
|
@ -343,7 +347,9 @@ void DivPlatformMSM6295::renderSamples() {
|
|||
}
|
||||
|
||||
void DivPlatformMSM6295::setFlags(unsigned int flags) {
|
||||
switch (flags) {
|
||||
rateSelInit=(flags>>7)&1;
|
||||
switch (flags&0x7f) {
|
||||
default:
|
||||
case 0:
|
||||
chipClock=4000000/4;
|
||||
break;
|
||||
|
|
@ -383,22 +389,27 @@ void DivPlatformMSM6295::setFlags(unsigned int flags) {
|
|||
case 12:
|
||||
chipClock=1500000;
|
||||
break;
|
||||
default:
|
||||
chipClock=4000000/4;
|
||||
case 13:
|
||||
chipClock=3000000;
|
||||
break;
|
||||
case 14:
|
||||
chipClock=COLOR_NTSC/3.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/3;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate/22;
|
||||
}
|
||||
if (rateSel!=rateSelInit) {
|
||||
rWrite(12,!rateSelInit);
|
||||
rateSel=rateSelInit;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformMSM6295::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
adpcmMem=new unsigned char[getSampleMemCapacity(0)];
|
||||
adpcmMemLen=0;
|
||||
iface.adpcmMem=adpcmMem;
|
||||
iface.sampleBank=0;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
updateOsc=0;
|
||||
|
|
@ -406,7 +417,6 @@ int DivPlatformMSM6295::init(DivEngine* p, int channels, int sugRate, unsigned i
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
msm=new msm6295_core(iface);
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 4;
|
||||
|
|
@ -416,7 +426,6 @@ void DivPlatformMSM6295::quit() {
|
|||
for (int i=0; i<4; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
delete msm;
|
||||
delete[] adpcmMem;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,60 +24,31 @@
|
|||
#include <queue>
|
||||
#include "sound/oki/msm6295.hpp"
|
||||
|
||||
class DivMSM6295Interface: public vgsound_emu_mem_intf {
|
||||
public:
|
||||
unsigned char* adpcmMem;
|
||||
int sampleBank;
|
||||
u8 read_byte(u32 address);
|
||||
DivMSM6295Interface(): adpcmMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformMSM6295: public DivDispatch {
|
||||
class DivPlatformMSM6295: public DivDispatch, public vgsound_emu_mem_intf {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
unsigned char freqH, freqL;
|
||||
int freq, baseFreq, pitch, pitch2, portaPauseFreq, note, ins;
|
||||
unsigned char psgMode, autoEnvNum, autoEnvDen;
|
||||
int note, ins;
|
||||
signed char konCycles;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, portaPause, inPorta, furnacePCM, hardReset;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, furnacePCM, hardReset;
|
||||
int vol, outVol;
|
||||
int sample;
|
||||
unsigned char pan;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freqH(0),
|
||||
freqL(0),
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
portaPauseFreq(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
psgMode(1),
|
||||
autoEnvNum(0),
|
||||
autoEnvDen(0),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
portaPause(false),
|
||||
inPorta(false),
|
||||
furnacePCM(false),
|
||||
hardReset(false),
|
||||
vol(0),
|
||||
outVol(15),
|
||||
sample(-1),
|
||||
pan(3) {}
|
||||
sample(-1) {}
|
||||
};
|
||||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
|
|
@ -85,57 +56,59 @@ class DivPlatformMSM6295: public DivDispatch {
|
|||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
unsigned short delay;
|
||||
QueuedWrite(unsigned short a, unsigned char v, unsigned short d=32):
|
||||
addr(a),
|
||||
val(v),
|
||||
delay(d) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
msm6295_core* msm;
|
||||
unsigned char regPool[512];
|
||||
msm6295_core msm;
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmMem;
|
||||
size_t adpcmMemLen;
|
||||
DivMSM6295Interface iface;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay, updateOsc;
|
||||
|
||||
bool extMode;
|
||||
bool rateSel;
|
||||
bool rateSel=false, rateSelInit=false;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
DivMacroInt* getChanMacroInt(int ch);
|
||||
DivDispatchOscBuffer* getOscBuffer(int chan);
|
||||
unsigned char* getRegisterPool();
|
||||
int getRegisterPoolSize();
|
||||
void reset();
|
||||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool keyOffAffectsArp(int ch);
|
||||
float getPostAmp();
|
||||
void notifyInsChange(int ins);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
void setFlags(unsigned int flags);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index);
|
||||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
virtual u8 read_byte(u32 address) override;
|
||||
virtual void acquire(short* bufL, short* bufR, size_t start, size_t len) override;
|
||||
virtual int dispatch(DivCommand c) override;
|
||||
virtual void* getChanState(int chan) override;
|
||||
virtual DivMacroInt* getChanMacroInt(int ch) override;
|
||||
virtual DivDispatchOscBuffer* getOscBuffer(int chan) override;
|
||||
virtual unsigned char* getRegisterPool() override;
|
||||
virtual int getRegisterPoolSize() override;
|
||||
virtual void reset() override;
|
||||
virtual void forceIns() override;
|
||||
virtual void tick(bool sysTick=true) override;
|
||||
virtual void muteChannel(int ch, bool mute) override;
|
||||
virtual bool keyOffAffectsArp(int ch) override;
|
||||
virtual float getPostAmp() override;
|
||||
virtual void notifyInsChange(int ins) override;
|
||||
virtual void notifyInsDeletion(void* ins) override;
|
||||
virtual void poke(unsigned int addr, unsigned short val) override;
|
||||
virtual void poke(std::vector<DivRegWrite>& wlist) override;
|
||||
virtual void setFlags(unsigned int flags) override;
|
||||
virtual const char** getRegisterSheet() override;
|
||||
virtual const char* getEffectName(unsigned char effect) override;
|
||||
virtual const void* getSampleMem(int index) override;
|
||||
virtual size_t getSampleMemCapacity(int index) override;
|
||||
virtual size_t getSampleMemUsage(int index) override;
|
||||
virtual void renderSamples() override;
|
||||
|
||||
virtual int init(DivEngine* parent, int channels, int sugRate, unsigned int flags) override;
|
||||
virtual void quit() override;
|
||||
DivPlatformMSM6295():
|
||||
DivDispatch(),
|
||||
vgsound_emu_mem_intf(),
|
||||
msm(*this) {}
|
||||
~DivPlatformMSM6295();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -164,9 +164,6 @@ const char* DivPlatformNamcoWSG::getEffectName(unsigned char effect) {
|
|||
}
|
||||
|
||||
void DivPlatformNamcoWSG::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
short* buf[2]={
|
||||
bufL+start, bufR+start
|
||||
};
|
||||
while (!writes.empty()) {
|
||||
QueuedWrite w=writes.front();
|
||||
switch (devType) {
|
||||
|
|
@ -186,7 +183,15 @@ void DivPlatformNamcoWSG::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
regPool[w.addr&0x3f]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
namco->sound_stream_update(buf,len);
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
short* buf[2]={
|
||||
bufL+h, bufR+h
|
||||
};
|
||||
namco->sound_stream_update(buf,1);
|
||||
for (int i=0; i<chans; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=namco->m_channel_list[i].last_out*chans;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformNamcoWSG::updateWave(int ch) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ const char** DivPlatformNES::getRegisterSheet() {
|
|||
const char* DivPlatformNES::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x11:
|
||||
return "Write to delta modulation counter (0 to 7F)";
|
||||
return "11xx: Write to delta modulation counter (0 to 7F)";
|
||||
break;
|
||||
case 0x12:
|
||||
return "12xx: Set duty cycle/noise mode (pulse: 0 to 3; noise: 0 or 1)";
|
||||
|
|
|
|||
|
|
@ -277,8 +277,13 @@ void DivPlatformOPL::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
regPool[w.addr&511]=w.val;
|
||||
writes.pop();
|
||||
}
|
||||
|
||||
OPL3_Generate(&fm,o); os[0]+=o[0]; os[1]+=o[1];
|
||||
|
||||
if (downsample) {
|
||||
OPL3_GenerateResampled(&fm,o);
|
||||
} else {
|
||||
OPL3_Generate(&fm,o);
|
||||
}
|
||||
os[0]+=o[0]; os[1]+=o[1];
|
||||
|
||||
if (adpcmChan>=0) {
|
||||
adpcmB->clock();
|
||||
|
|
@ -677,6 +682,9 @@ void DivPlatformOPL::muteChannel(int ch, bool mute) {
|
|||
fm.channel[outChanMap[ch]].muted=mute;
|
||||
}
|
||||
int ops=(slots[3][ch]!=255 && chan[ch].state.ops==4 && oplType==3)?4:2;
|
||||
if (ch&1 && ch<12) {
|
||||
if (chan[ch-1].fourOp) return;
|
||||
}
|
||||
chan[ch].fourOp=(ops==4);
|
||||
update4OpMask=true;
|
||||
for (int i=0; i<ops; i++) {
|
||||
|
|
@ -778,7 +786,7 @@ int DivPlatformOPL::dispatch(DivCommand c) {
|
|||
immWrite(11,(end>>2)&0xff);
|
||||
immWrite(12,(end>>10)&0xff);
|
||||
immWrite(7,(s->loopStart>=0)?0xb0:0xa0); // start/repeat
|
||||
int freq=(65536.0*(double)s->rate)/(double)rate;
|
||||
int freq=(65536.0*(double)s->rate)/(double)chipRateBase;
|
||||
immWrite(16,freq&0xff);
|
||||
immWrite(17,(freq>>8)&0xff);
|
||||
}
|
||||
|
|
@ -1508,7 +1516,12 @@ void DivPlatformOPL::reset() {
|
|||
fm_ymfm->reset();
|
||||
}
|
||||
*/
|
||||
OPL3_Reset(&fm,rate);
|
||||
if (downsample) {
|
||||
const unsigned int downsampledRate=(unsigned int)(49716.0*(double(rate)/chipRateBase));
|
||||
OPL3_Reset(&fm,downsampledRate);
|
||||
} else {
|
||||
OPL3_Reset(&fm,rate);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
|
|
@ -1625,6 +1638,7 @@ void DivPlatformOPL::setYMFM(bool use) {
|
|||
|
||||
void DivPlatformOPL::setOPLType(int type, bool drums) {
|
||||
pretendYMU=false;
|
||||
downsample=false;
|
||||
adpcmChan=-1;
|
||||
switch (type) {
|
||||
case 1: case 2: case 8950:
|
||||
|
|
@ -1654,10 +1668,13 @@ void DivPlatformOPL::setOPLType(int type, bool drums) {
|
|||
if (type==759) {
|
||||
pretendYMU=true;
|
||||
adpcmChan=16;
|
||||
} else if (type==4) {
|
||||
downsample=true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (type==759) {
|
||||
chipType=type;
|
||||
if (type==759 || type==4) {
|
||||
oplType=3;
|
||||
} else if (type==8950) {
|
||||
oplType=1;
|
||||
|
|
@ -1692,17 +1709,73 @@ void DivPlatformOPL::setFlags(unsigned int flags) {
|
|||
rate=chipClock/36;
|
||||
}*/
|
||||
|
||||
if (oplType==3) {
|
||||
chipClock=COLOR_NTSC*4.0;
|
||||
rate=chipClock/288;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
rate=chipClock/72;
|
||||
}
|
||||
|
||||
if (pretendYMU) {
|
||||
rate=48000;
|
||||
chipClock=rate*288;
|
||||
switch (chipType) {
|
||||
default:
|
||||
case 1: case 2: case 8950:
|
||||
switch (flags&0xff) {
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=4000000.0;
|
||||
break;
|
||||
case 0x03:
|
||||
chipClock=3000000.0;
|
||||
break;
|
||||
case 0x04:
|
||||
chipClock=38400*13*8; // 31948800/8
|
||||
break;
|
||||
case 0x05:
|
||||
chipClock=3500000.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/72;
|
||||
chipRateBase=double(rate);
|
||||
break;
|
||||
case 3:
|
||||
switch (flags&0xff) {
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC*4.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*16.0/5.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=14000000.0;
|
||||
break;
|
||||
case 0x03:
|
||||
chipClock=16000000.0;
|
||||
break;
|
||||
case 0x04:
|
||||
chipClock=15000000.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/288;
|
||||
chipRateBase=double(rate);
|
||||
break;
|
||||
case 4:
|
||||
switch (flags&0xff) {
|
||||
case 0x02:
|
||||
chipClock=33868800.0;
|
||||
break;
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC*8.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*32.0/5.0;
|
||||
break;
|
||||
}
|
||||
chipRateBase=double(chipClock)/684.0;
|
||||
rate=chipClock/768;
|
||||
break;
|
||||
case 759:
|
||||
rate=48000;
|
||||
chipRateBase=double(rate);
|
||||
chipClock=rate*288;
|
||||
break;
|
||||
}
|
||||
|
||||
for (int i=0; i<18; i++) {
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ class DivPlatformOPL: public DivDispatch {
|
|||
const unsigned char** slots;
|
||||
const unsigned short* chanMap;
|
||||
const unsigned char* outChanMap;
|
||||
double chipFreqBase;
|
||||
int delay, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
|
||||
double chipFreqBase, chipRateBase;
|
||||
int delay, chipType, oplType, chans, melodicChans, totalChans, adpcmChan, sampleBank;
|
||||
unsigned char lastBusy;
|
||||
unsigned char drumState;
|
||||
unsigned char drumVol[5];
|
||||
|
|
@ -107,7 +107,7 @@ class DivPlatformOPL: public DivDispatch {
|
|||
|
||||
unsigned char lfoValue;
|
||||
|
||||
bool useYMFM, update4OpMask, pretendYMU;
|
||||
bool useYMFM, update4OpMask, pretendYMU, downsample;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
|
|
|||
|
|
@ -21,15 +21,15 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) {regPool[(a)]=(v)&0xff; if((a)==10) {chan.sreg=(v); chan.cnt=2;}}
|
||||
|
||||
#define CHIP_DIVIDER 16
|
||||
#define SAMP_DIVIDER 4
|
||||
|
||||
const char* regCheatSheet6522[]={
|
||||
"T2L", "08",
|
||||
"T2H", "09",
|
||||
"SR", "0A",
|
||||
"ACR", "0B",
|
||||
"PCR", "0C",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -46,26 +46,45 @@ const char* DivPlatformPET::getEffectName(unsigned char effect) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// high-level emulation of 6522 shift register and driver software for now
|
||||
void DivPlatformPET::rWrite(unsigned int addr, unsigned char val) {
|
||||
bool hwSROutput=((regPool[11]>>2)&7)==4;
|
||||
switch (addr) {
|
||||
case 9:
|
||||
// simulate phase reset from switching between hw/sw shift registers
|
||||
if ((regPool[9]==0)^(val==0)) {
|
||||
chan.sreg=chan.wave;
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
chan.sreg=val;
|
||||
if (hwSROutput) chan.cnt=2;
|
||||
break;
|
||||
}
|
||||
regPool[addr]=val;
|
||||
}
|
||||
|
||||
void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
// high-level emulation of 6522 shift register for now
|
||||
int t2=regPool[8]*2+4;
|
||||
if (((regPool[11]>>2)&7)==4) {
|
||||
bool hwSROutput=((regPool[11]>>2)&7)==4;
|
||||
if (chan.enable) {
|
||||
int reload=regPool[8]*2+4;
|
||||
if (!hwSROutput) {
|
||||
reload+=regPool[9]*512;
|
||||
}
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
int cycs=SAMP_DIVIDER;
|
||||
while (cycs>0) {
|
||||
int adv=MIN(cycs,chan.cnt);
|
||||
chan.cnt-=adv;
|
||||
cycs-=adv;
|
||||
if (chan.cnt==0) {
|
||||
chan.out=(chan.sreg&1)*32767;
|
||||
chan.sreg=(chan.sreg>>1)|((chan.sreg&1)<<7);
|
||||
chan.cnt=t2;
|
||||
}
|
||||
if (SAMP_DIVIDER>chan.cnt) {
|
||||
chan.out=(chan.sreg&1)*32767;
|
||||
chan.sreg=(chan.sreg>>1)|((chan.sreg&1)<<7);
|
||||
chan.cnt+=reload-SAMP_DIVIDER;
|
||||
} else {
|
||||
chan.cnt-=SAMP_DIVIDER;
|
||||
}
|
||||
bufL[h]=chan.out;
|
||||
bufR[h]=chan.out;
|
||||
oscBuf->data[oscBuf->needle++]=chan.out;
|
||||
}
|
||||
// emulate driver writes to PCR
|
||||
if (!hwSROutput) regPool[12]=chan.out?0xe0:0xc0;
|
||||
} else {
|
||||
chan.out=0;
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
|
|
@ -78,11 +97,10 @@ void DivPlatformPET::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
|
||||
void DivPlatformPET::writeOutVol() {
|
||||
if (chan.active && !isMuted && chan.outVol>0) {
|
||||
if (regPool[11]!=16) {
|
||||
rWrite(11,16);
|
||||
rWrite(10,chan.wave);
|
||||
}
|
||||
chan.enable=true;
|
||||
rWrite(11,regPool[9]==0?16:0);
|
||||
} else {
|
||||
chan.enable=false;
|
||||
rWrite(11,0);
|
||||
}
|
||||
}
|
||||
|
|
@ -118,21 +136,22 @@ void DivPlatformPET::tick(bool sysTick) {
|
|||
chan.freqChanged=true;
|
||||
}
|
||||
if (chan.freqChanged || chan.keyOn || chan.keyOff) {
|
||||
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER);
|
||||
if (chan.freq>257) chan.freq=257;
|
||||
if (chan.freq<2) chan.freq=2;
|
||||
rWrite(8,chan.freq-2);
|
||||
chan.freq=parent->calcFreq(chan.baseFreq,chan.pitch,true,0,chan.pitch2,chipClock,CHIP_DIVIDER)-2;
|
||||
if (chan.freq>65535) chan.freq=65535;
|
||||
if (chan.freq<0) chan.freq=0;
|
||||
rWrite(8,chan.freq&0xff);
|
||||
rWrite(9,chan.freq>>8);
|
||||
if (chan.keyOn) {
|
||||
if (!chan.std.vol.will) {
|
||||
chan.outVol=chan.vol;
|
||||
writeOutVol();
|
||||
}
|
||||
chan.keyOn=false;
|
||||
}
|
||||
if (chan.keyOff) {
|
||||
rWrite(11,0);
|
||||
chan.keyOff=false;
|
||||
}
|
||||
// update mode setting and channel enable
|
||||
writeOutVol();
|
||||
chan.freqChanged=false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
class DivPlatformPET: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, enable;
|
||||
int vol, outVol, wave;
|
||||
unsigned char sreg;
|
||||
int cnt;
|
||||
|
|
@ -49,6 +49,7 @@ class DivPlatformPET: public DivDispatch {
|
|||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
enable(false),
|
||||
vol(1),
|
||||
outVol(1),
|
||||
wave(0b00001111),
|
||||
|
|
@ -85,6 +86,7 @@ class DivPlatformPET: public DivDispatch {
|
|||
~DivPlatformPET();
|
||||
private:
|
||||
void writeOutVol();
|
||||
void rWrite(unsigned int addr, unsigned char val);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -332,7 +332,7 @@ void DivPlatformSCC::reset() {
|
|||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformSCC::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent,128);
|
||||
chan[i].ws.init(NULL,32,255,false);
|
||||
chan[i].vol=15;
|
||||
chan[i].outVol=15;
|
||||
|
|
@ -377,6 +377,27 @@ void DivPlatformSCC::setChipModel(bool isplus) {
|
|||
isPlus=isplus;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::setFlags(unsigned int flags) {
|
||||
switch (flags&0x7f) {
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*2.0/5.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=3000000.0/2.0;
|
||||
break;
|
||||
case 0x03:
|
||||
chipClock=4000000.0/2.0;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/8;
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSCC::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
|
|
@ -386,11 +407,7 @@ int DivPlatformSCC::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
rate=chipClock/8;
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
setFlags(flags);
|
||||
if (isPlus) {
|
||||
scc=new k052539_scc_core;
|
||||
regBase=0xa0;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ class DivPlatformSCC: public DivDispatch {
|
|||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void setChipModel(bool isPlus);
|
||||
void quit();
|
||||
|
|
|
|||
|
|
@ -21,15 +21,21 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(v) {if (!skipRegisterWrites) {writes.push(v); if (dumpWrites) {addWrite(0x200,v);}}}
|
||||
#define rWrite(a,v) {if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(0x200+a,v);}}}
|
||||
|
||||
const char* regCheatSheetSN[]={
|
||||
"DATA", "0",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char* regCheatSheetGG[]={
|
||||
"DATA", "0",
|
||||
"Stereo", "1",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSMS::getRegisterSheet() {
|
||||
return regCheatSheetSN;
|
||||
return stereo?regCheatSheetGG:regCheatSheetSN;
|
||||
}
|
||||
|
||||
const char* DivPlatformSMS::getEffectName(unsigned char effect) {
|
||||
|
|
@ -45,8 +51,10 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
int o=0;
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
if (!writes.empty()) {
|
||||
unsigned char w=writes.front();
|
||||
YMPSG_Write(&sn_nuked,w);
|
||||
QueuedWrite w=writes.front();
|
||||
if (w.addr==0) {
|
||||
YMPSG_Write(&sn_nuked,w.val);
|
||||
}
|
||||
writes.pop();
|
||||
}
|
||||
YMPSG_Clock(&sn_nuked);
|
||||
|
|
@ -68,7 +76,7 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
o=YMPSG_GetOutput(&sn_nuked);
|
||||
if (o<-32768) o=-32768;
|
||||
if (o>32767) o=32767;
|
||||
bufL[h]=o;
|
||||
bufL[h]=bufR[h]=o;
|
||||
for (int i=0; i<4; i++) {
|
||||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
|
|
@ -81,12 +89,20 @@ void DivPlatformSMS::acquire_nuked(short* bufL, short* bufR, size_t start, size_
|
|||
|
||||
void DivPlatformSMS::acquire_mame(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
while (!writes.empty()) {
|
||||
unsigned char w=writes.front();
|
||||
sn->write(w);
|
||||
QueuedWrite w=writes.front();
|
||||
if (stereo && (w.addr==1))
|
||||
sn->stereo_w(w.val);
|
||||
else if (w.addr==0) {
|
||||
sn->write(w.val);
|
||||
}
|
||||
writes.pop();
|
||||
}
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
sn->sound_stream_update(bufL+h,1);
|
||||
short* outs[2]={
|
||||
&bufL[h],
|
||||
&bufR[h]
|
||||
};
|
||||
sn->sound_stream_update(outs,1);
|
||||
for (int i=0; i<4; i++) {
|
||||
if (isMuted[i]) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=0;
|
||||
|
|
@ -105,23 +121,17 @@ void DivPlatformSMS::acquire(short* bufL, short* bufR, size_t start, size_t len)
|
|||
}
|
||||
}
|
||||
|
||||
int DivPlatformSMS::acquireOne() {
|
||||
short v;
|
||||
sn->sound_stream_update(&v,1);
|
||||
return v;
|
||||
}
|
||||
|
||||
void DivPlatformSMS::tick(bool sysTick) {
|
||||
for (int i=0; i<4; i++) {
|
||||
int CHIP_DIVIDER=64;
|
||||
if (i==3 && isRealSN) CHIP_DIVIDER=60;
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (i==3) CHIP_DIVIDER=noiseDivider;
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=MIN(15,chan[i].std.vol.val)-(15-(chan[i].vol&15));
|
||||
if (chan[i].outVol<0) chan[i].outVol=0;
|
||||
// old formula
|
||||
// ((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))>>4;
|
||||
rWrite(0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
|
||||
rWrite(0,0x90|(i<<5)|(isMuted[i]?15:(15-(chan[i].outVol&15))));
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
|
|
@ -160,6 +170,13 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (stereo) {
|
||||
if (chan[i].std.panL.had) {
|
||||
lastPan&=~(0x11<<i);
|
||||
lastPan|=((chan[i].std.panL.val&1)<<i)|(((chan[i].std.panL.val>>1)&1)<<(i+4));
|
||||
rWrite(1,lastPan);
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
|
|
@ -172,12 +189,12 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
for (int i=0; i<3; i++) {
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,64);
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true,0,chan[i].pitch2,chipClock,toneDivider);
|
||||
if (chan[i].freq>1023) chan[i].freq=1023;
|
||||
if (chan[i].freq<8) chan[i].freq=1;
|
||||
//if (chan[i].actualNote>0x5d) chan[i].freq=0x01;
|
||||
rWrite(0x80|i<<5|(chan[i].freq&15));
|
||||
rWrite(chan[i].freq>>4);
|
||||
rWrite(0,0x80|i<<5|(chan[i].freq&15));
|
||||
rWrite(0,chan[i].freq>>4);
|
||||
// what?
|
||||
/*if (i==2 && snNoiseMode&2) {
|
||||
chan[3].baseFreq=chan[2].baseFreq;
|
||||
|
|
@ -187,24 +204,24 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
}
|
||||
if (chan[3].freqChanged || updateSNMode) {
|
||||
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,isRealSN?60:64);
|
||||
chan[3].freq=parent->calcFreq(chan[3].baseFreq,chan[3].pitch,true,0,chan[3].pitch2,chipClock,noiseDivider);
|
||||
if (chan[3].freq>1023) chan[3].freq=1023;
|
||||
if (chan[3].actualNote>0x5d) chan[3].freq=0x01;
|
||||
if (snNoiseMode&2) { // take period from channel 3
|
||||
if (updateSNMode || resetPhase) {
|
||||
if (snNoiseMode&1) {
|
||||
rWrite(0xe7);
|
||||
rWrite(0,0xe7);
|
||||
} else {
|
||||
rWrite(0xe3);
|
||||
rWrite(0,0xe3);
|
||||
}
|
||||
if (updateSNMode) {
|
||||
rWrite(0xdf);
|
||||
rWrite(0,0xdf);
|
||||
}
|
||||
}
|
||||
|
||||
if (chan[3].freqChanged) {
|
||||
rWrite(0xc0|(chan[3].freq&15));
|
||||
rWrite(chan[3].freq>>4);
|
||||
rWrite(0,0xc0|(chan[3].freq&15));
|
||||
rWrite(0,chan[3].freq>>4);
|
||||
}
|
||||
} else { // 3 fixed values
|
||||
unsigned char value;
|
||||
|
|
@ -221,7 +238,7 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
value=2-value;
|
||||
if (value!=oldValue || updateSNMode || resetPhase) {
|
||||
oldValue=value;
|
||||
rWrite(0xe0|value|((snNoiseMode&1)<<2));
|
||||
rWrite(0,0xe0|value|((snNoiseMode&1)<<2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -231,8 +248,8 @@ void DivPlatformSMS::tick(bool sysTick) {
|
|||
}
|
||||
|
||||
int DivPlatformSMS::dispatch(DivCommand c) {
|
||||
int CHIP_DIVIDER=64;
|
||||
if (c.chan==3 && isRealSN) CHIP_DIVIDER=60;
|
||||
double CHIP_DIVIDER=toneDivider;
|
||||
if (c.chan==3) CHIP_DIVIDER=noiseDivider;
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON:
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
|
|
@ -242,7 +259,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
chan[c.chan].actualNote=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
|
||||
rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
|
||||
chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_STD));
|
||||
if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) {
|
||||
chan[c.chan].outVol=chan[c.chan].vol;
|
||||
|
|
@ -250,7 +267,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
break;
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
rWrite(0x9f|c.chan<<5);
|
||||
rWrite(0,0x9f|c.chan<<5);
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
|
|
@ -267,7 +284,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
}
|
||||
if (chan[c.chan].active) rWrite(0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
|
||||
if (chan[c.chan].active) rWrite(0,0x90|c.chan<<5|(isMuted[c.chan]?15:(15-(chan[c.chan].vol&15))));
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
|
|
@ -307,6 +324,19 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
snNoiseMode=(c.value&1)|((c.value&16)>>3);
|
||||
updateSNMode=true;
|
||||
break;
|
||||
case DIV_CMD_PANNING: {
|
||||
if (stereo) {
|
||||
if (c.chan>3) c.chan=3;
|
||||
lastPan&=~(0x11<<c.chan);
|
||||
int pan=0;
|
||||
if (c.value>0) pan|=0x10;
|
||||
if (c.value2>0) pan|=0x01;
|
||||
if (pan==0) pan=0x11;
|
||||
lastPan|=pan<<c.chan;
|
||||
rWrite(1,lastPan);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO:
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
|
|
@ -335,7 +365,7 @@ int DivPlatformSMS::dispatch(DivCommand c) {
|
|||
|
||||
void DivPlatformSMS::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (chan[ch].active) rWrite(0x90|ch<<5|(isMuted[ch]?15:(15-(chan[ch].outVol&15))));
|
||||
if (chan[ch].active) rWrite(0,0x90|ch<<5|(isMuted[ch]?15:(15-(chan[ch].outVol&15))));
|
||||
}
|
||||
|
||||
void DivPlatformSMS::forceIns() {
|
||||
|
|
@ -370,11 +400,19 @@ void DivPlatformSMS::reset() {
|
|||
addWrite(0xffffffff,0);
|
||||
}
|
||||
sn->device_start();
|
||||
YMPSG_Init(&sn_nuked,isRealSN);
|
||||
YMPSG_Init(&sn_nuked,isRealSN,12,isRealSN?13:15,isRealSN?16383:32767);
|
||||
snNoiseMode=3;
|
||||
rWrite(0xe7);
|
||||
rWrite(0,0xe7);
|
||||
updateSNMode=false;
|
||||
oldValue=0xff;
|
||||
lastPan=0xff;
|
||||
if (stereo) {
|
||||
rWrite(1,0xff);
|
||||
}
|
||||
}
|
||||
|
||||
bool DivPlatformSMS::isStereo() {
|
||||
return stereo;
|
||||
}
|
||||
|
||||
bool DivPlatformSMS::keyOffAffectsArp(int ch) {
|
||||
|
|
@ -396,45 +434,109 @@ void DivPlatformSMS::notifyInsDeletion(void* ins) {
|
|||
}
|
||||
|
||||
void DivPlatformSMS::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(val);
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSMS::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.val);
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformSMS::setFlags(unsigned int flags) {
|
||||
if ((flags&3)==3) {
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
} else if ((flags&3)==2) {
|
||||
chipClock=4000000;
|
||||
} else if ((flags&3)==1) {
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
switch (flags&0xff03) {
|
||||
default:
|
||||
case 0x0000:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 0x0001:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
break;
|
||||
case 0x0002:
|
||||
chipClock=4000000;
|
||||
break;
|
||||
case 0x0003:
|
||||
chipClock=COLOR_NTSC/2.0;
|
||||
break;
|
||||
case 0x0100:
|
||||
chipClock=3000000;
|
||||
break;
|
||||
case 0x0101:
|
||||
chipClock=2000000;
|
||||
break;
|
||||
case 0x0102:
|
||||
chipClock=COLOR_NTSC/8.0;
|
||||
break;
|
||||
}
|
||||
resetPhase=!(flags&16);
|
||||
|
||||
divider=16;
|
||||
toneDivider=64.0;
|
||||
noiseDivider=64.0;
|
||||
if (sn!=NULL) delete sn;
|
||||
switch ((flags>>2)&3) {
|
||||
case 1: // TI
|
||||
sn=new sn76496_base_device(0x4000, 0x4000, 0x01, 0x02, true, 1, false, true);
|
||||
isRealSN=true;
|
||||
break;
|
||||
case 2: // TI+Atari
|
||||
sn=new sn76496_base_device(0x4000, 0x0f35, 0x01, 0x02, true, 1, false, true);
|
||||
isRealSN=true;
|
||||
break;
|
||||
case 3: // Game Gear (not fully emulated yet!)
|
||||
sn=new sn76496_base_device(0x8000, 0x8000, 0x01, 0x08, false, 1, false, false);
|
||||
isRealSN=false;
|
||||
break;
|
||||
switch (flags&0xcc) {
|
||||
default: // Sega
|
||||
sn=new sn76496_base_device(0x8000, 0x8000, 0x01, 0x08, false, 1, false, false);
|
||||
case 0x00:
|
||||
sn=new segapsg_device();
|
||||
isRealSN=false;
|
||||
stereo=false;
|
||||
break;
|
||||
case 0x04: // TI SN76489
|
||||
sn=new sn76489_device();
|
||||
isRealSN=true;
|
||||
stereo=false;
|
||||
noiseDivider=60.0; // 64 for match to tone frequency on non-Sega PSG but compatibility
|
||||
break;
|
||||
case 0x08: // TI+Atari
|
||||
sn=new sn76496_base_device(0x4000, 0x0f35, 0x01, 0x02, true, false, 1/*8*/, false, true);
|
||||
isRealSN=true;
|
||||
stereo=false;
|
||||
noiseDivider=60.0;
|
||||
break;
|
||||
case 0x0c: // Game Gear (not fully emulated yet!)
|
||||
sn=new gamegear_device();
|
||||
isRealSN=false;
|
||||
stereo=true;
|
||||
break;
|
||||
case 0x40: // TI SN76489A
|
||||
sn=new sn76489a_device();
|
||||
isRealSN=false; // TODO
|
||||
stereo=false;
|
||||
noiseDivider=60.0;
|
||||
break;
|
||||
case 0x44: // TI SN76496
|
||||
sn=new sn76496_device();
|
||||
isRealSN=false; // TODO
|
||||
stereo=false;
|
||||
noiseDivider=60.0;
|
||||
break;
|
||||
case 0x48: // NCR 8496
|
||||
sn=new ncr8496_device();
|
||||
isRealSN=false;
|
||||
stereo=false;
|
||||
noiseDivider=60.0;
|
||||
break;
|
||||
case 0x4c: // Tandy PSSJ 3-voice sound
|
||||
sn=new pssj3_device();
|
||||
isRealSN=false;
|
||||
stereo=false;
|
||||
noiseDivider=60.0;
|
||||
break;
|
||||
case 0x80: // TI SN94624
|
||||
sn=new sn94624_device();
|
||||
isRealSN=true;
|
||||
stereo=false;
|
||||
divider=2;
|
||||
toneDivider=8.0;
|
||||
noiseDivider=7.5;
|
||||
break;
|
||||
case 0x84: // TI SN76494
|
||||
sn=new sn76494_device();
|
||||
isRealSN=false; // TODO
|
||||
stereo=false;
|
||||
divider=2;
|
||||
toneDivider=8.0;
|
||||
noiseDivider=7.5;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/16;
|
||||
rate=chipClock/divider;
|
||||
for (int i=0; i<4; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
|
|
@ -450,6 +552,7 @@ int DivPlatformSMS::init(DivEngine* p, int channels, int sugRate, unsigned int f
|
|||
skipRegisterWrites=false;
|
||||
resetPhase=false;
|
||||
oldValue=0xff;
|
||||
lastPan=0xff;
|
||||
for (int i=0; i<4; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
|
|
|
|||
|
|
@ -58,21 +58,31 @@ class DivPlatformSMS: public DivDispatch {
|
|||
Channel chan[4];
|
||||
DivDispatchOscBuffer* oscBuf[4];
|
||||
bool isMuted[4];
|
||||
unsigned char lastPan;
|
||||
unsigned char oldValue;
|
||||
unsigned char snNoiseMode;
|
||||
int divider=16;
|
||||
double toneDivider=64.0;
|
||||
double noiseDivider=64.0;
|
||||
bool updateSNMode;
|
||||
bool resetPhase;
|
||||
bool isRealSN;
|
||||
bool stereo;
|
||||
bool nuked;
|
||||
sn76496_base_device* sn;
|
||||
ympsg_t sn_nuked;
|
||||
std::queue<unsigned char> writes;
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
void acquire_nuked(short* bufL, short* bufR, size_t start, size_t len);
|
||||
void acquire_mame(short* bufL, short* bufR, size_t start, size_t len);
|
||||
public:
|
||||
int acquireOne();
|
||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||
int dispatch(DivCommand c);
|
||||
void* getChanState(int chan);
|
||||
|
|
@ -82,6 +92,7 @@ class DivPlatformSMS: public DivDispatch {
|
|||
void forceIns();
|
||||
void tick(bool sysTick=true);
|
||||
void muteChannel(int ch, bool mute);
|
||||
bool isStereo();
|
||||
bool keyOffAffectsArp(int ch);
|
||||
bool keyOffAffectsPorta(int ch);
|
||||
int getPortaFloor(int ch);
|
||||
|
|
|
|||
|
|
@ -172,10 +172,11 @@ void namco_audio_device::build_decoded_waveform(uint8_t *rgnbase)
|
|||
|
||||
|
||||
/* generate sound by oversampling */
|
||||
uint32_t namco_audio_device::namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq)
|
||||
uint32_t namco_audio_device::namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq, int16_t& last_out)
|
||||
{
|
||||
for (int sampindex = 0; sampindex < size; sampindex++)
|
||||
{
|
||||
last_out=wave[WAVEFORM_POSITION(counter)];
|
||||
buffer[sampindex]+=wave[WAVEFORM_POSITION(counter)];
|
||||
counter += freq;
|
||||
}
|
||||
|
|
@ -700,7 +701,7 @@ void namco_audio_device::sound_stream_update(short** outputs, int len)
|
|||
const int16_t *lw = &m_waveform[lv][voice->waveform_select * 32];
|
||||
|
||||
/* generate sound into the buffer */
|
||||
c = namco_update_one(lmix, len, lw, voice->counter, voice->frequency);
|
||||
c = namco_update_one(lmix, len, lw, voice->counter, voice->frequency, voice->last_out);
|
||||
}
|
||||
|
||||
/* only update if we have non-zero right volume */
|
||||
|
|
@ -709,7 +710,7 @@ void namco_audio_device::sound_stream_update(short** outputs, int len)
|
|||
const int16_t *rw = &m_waveform[rv][voice->waveform_select * 32];
|
||||
|
||||
/* generate sound into the buffer */
|
||||
c = namco_update_one(rmix, len, rw, voice->counter, voice->frequency);
|
||||
c = namco_update_one(rmix, len, rw, voice->counter, voice->frequency, voice->last_out);
|
||||
}
|
||||
|
||||
/* update the counter for this voice */
|
||||
|
|
@ -789,7 +790,7 @@ void namco_audio_device::sound_stream_update(short** outputs, int len)
|
|||
const int16_t *w = &m_waveform[v][voice->waveform_select * 32];
|
||||
|
||||
/* generate sound into buffer and update the counter for this voice */
|
||||
voice->counter = namco_update_one(buffer, len, w, voice->counter, voice->frequency);
|
||||
voice->counter = namco_update_one(buffer, len, w, voice->counter, voice->frequency, voice->last_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ public:
|
|||
uint32_t noise_counter;
|
||||
int32_t noise_hold;
|
||||
int32_t waveform_select;
|
||||
int16_t last_out;
|
||||
};
|
||||
|
||||
namco_audio_device(uint32_t clock);
|
||||
|
|
@ -43,7 +44,7 @@ public:
|
|||
|
||||
void build_decoded_waveform( uint8_t *rgnbase );
|
||||
void update_namco_waveform(int offset, uint8_t data);
|
||||
uint32_t namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq);
|
||||
uint32_t namco_update_one(short* buffer, int size, const int16_t *wave, uint32_t counter, uint32_t freq, int16_t& last_out);
|
||||
|
||||
/* waveform region */
|
||||
uint8_t* m_wave_ptr;
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@
|
|||
10/12/2019: Michael Zapf
|
||||
* READY line handling by own emu_timer, not depending on sound_stream_update
|
||||
|
||||
additional modifications by tildearrow for furnace
|
||||
additional modifications by tildearrow, cam900 for furnace
|
||||
|
||||
TODO: * Implement the TMS9919 - any difference to sn94624?
|
||||
* Implement the T6W28; has registers in a weird order, needs writes
|
||||
|
|
@ -150,18 +150,20 @@
|
|||
|
||||
sn76496_base_device::sn76496_base_device(
|
||||
int feedbackmask,
|
||||
int noise_start,
|
||||
int noise_start,
|
||||
int noisetap1,
|
||||
int noisetap2,
|
||||
bool negate,
|
||||
bool stereo,
|
||||
int clockdivider,
|
||||
bool ncr,
|
||||
bool sega)
|
||||
: m_feedback_mask(feedbackmask)
|
||||
, m_noise_start(noise_start)
|
||||
, m_noise_start(noise_start)
|
||||
, m_whitenoise_tap1(noisetap1)
|
||||
, m_whitenoise_tap2(noisetap2)
|
||||
, m_negate(negate)
|
||||
, m_negate(negate)
|
||||
, m_stereo(stereo)
|
||||
, m_clock_divider(clockdivider)
|
||||
, m_ncr_style_psg(ncr)
|
||||
, m_sega_style_psg(sega)
|
||||
|
|
@ -169,10 +171,54 @@ sn76496_base_device::sn76496_base_device(
|
|||
}
|
||||
|
||||
sn76496_device::sn76496_device()
|
||||
: sn76496_base_device(0x8000, 0x8000, 0x01, 0x08, false, 1, false, false)
|
||||
: sn76496_base_device(0x10000, 0x04, 0x08, false, false, 1/*8*/, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
y2404_device::y2404_device()
|
||||
: sn76496_base_device(0x10000, 0x04, 0x08, false, false, 1/*8*/, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
sn76489_device::sn76489_device()
|
||||
: sn76496_base_device(0x4000, 0x01, 0x02, true, false, 1/*8*/, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
sn76489a_device::sn76489a_device()
|
||||
: sn76496_base_device(0x10000, 0x04, 0x08, false, false, 1/*8*/, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
sn76494_device::sn76494_device()
|
||||
: sn76496_base_device(0x10000, 0x04, 0x08, false, false, 1, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
sn94624_device::sn94624_device()
|
||||
: sn76496_base_device(0x4000, 0x01, 0x02, true, false, 1, false, true)
|
||||
{
|
||||
}
|
||||
|
||||
ncr8496_device::ncr8496_device()
|
||||
: sn76496_base_device(0x8000, 0x02, 0x20, true, false, 1/*8*/, true, true)
|
||||
{
|
||||
}
|
||||
|
||||
pssj3_device::pssj3_device()
|
||||
: sn76496_base_device(0x8000, 0x02, 0x20, false, false, 1/*8*/, true, true)
|
||||
{
|
||||
}
|
||||
|
||||
gamegear_device::gamegear_device()
|
||||
: sn76496_base_device(0x8000, 0x01, 0x08, true, true, 1/*8*/, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
segapsg_device::segapsg_device()
|
||||
: sn76496_base_device(0x8000, 0x01, 0x08, true, false, 1/*8*/, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
void sn76496_base_device::device_start()
|
||||
{
|
||||
|
|
@ -199,6 +245,7 @@ void sn76496_base_device::device_start()
|
|||
m_RNG = m_feedback_mask;
|
||||
m_output[3] = m_RNG & 1;
|
||||
|
||||
m_stereo_mask = 0xFF; // all channels enabled
|
||||
m_current_clock = m_clock_divider-1;
|
||||
|
||||
// set gain
|
||||
|
|
@ -225,6 +272,11 @@ void sn76496_base_device::device_start()
|
|||
m_ready_state = true;
|
||||
}
|
||||
|
||||
void sn76496_base_device::stereo_w(u8 data)
|
||||
{
|
||||
if (m_stereo) m_stereo_mask = data;
|
||||
}
|
||||
|
||||
void sn76496_base_device::write(u8 data)
|
||||
{
|
||||
int n, r, c;
|
||||
|
|
@ -285,7 +337,7 @@ inline bool sn76496_base_device::in_noise_mode()
|
|||
return ((m_register[6] & 4)!=0);
|
||||
}
|
||||
|
||||
void sn76496_base_device::sound_stream_update(short* outputs, int outLen)
|
||||
void sn76496_base_device::sound_stream_update(short** outputs, int outLen)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
|
@ -336,13 +388,30 @@ void sn76496_base_device::sound_stream_update(short* outputs, int outLen)
|
|||
}
|
||||
}
|
||||
|
||||
if (m_stereo)
|
||||
{
|
||||
out = ((((m_stereo_mask & 0x10)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
|
||||
+ ((((m_stereo_mask & 0x20)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
|
||||
+ ((((m_stereo_mask & 0x40)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
|
||||
+ ((((m_stereo_mask & 0x80)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
|
||||
|
||||
out2= ((((m_stereo_mask & 0x1)!=0) && (m_output[0]!=0))? m_volume[0] : 0)
|
||||
+ ((((m_stereo_mask & 0x2)!=0) && (m_output[1]!=0))? m_volume[1] : 0)
|
||||
+ ((((m_stereo_mask & 0x4)!=0) && (m_output[2]!=0))? m_volume[2] : 0)
|
||||
+ ((((m_stereo_mask & 0x8)!=0) && (m_output[3]!=0))? m_volume[3] : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
out= ((m_output[0]!=0)? m_volume[0]:0)
|
||||
+((m_output[1]!=0)? m_volume[1]:0)
|
||||
+((m_output[2]!=0)? m_volume[2]:0)
|
||||
+((m_output[3]!=0)? m_volume[3]:0);
|
||||
}
|
||||
|
||||
if (m_negate) { out = -out; out2 = -out2; }
|
||||
|
||||
outputs[sampindex]=out;
|
||||
outputs[0][sampindex]=out;
|
||||
if (m_stereo && (outputs[1] != nullptr))
|
||||
outputs[1][sampindex]=out2;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Nicola Salmoria
|
||||
|
||||
// additional modifications by tildearrow for furnace
|
||||
// additional modifications by tildearrow, cam900 for furnace
|
||||
#ifndef MAME_SOUND_SN76496_H
|
||||
#define MAME_SOUND_SN76496_H
|
||||
|
||||
|
|
@ -14,34 +14,57 @@ class sn76496_base_device {
|
|||
public:
|
||||
void stereo_w(u8 data);
|
||||
void write(u8 data);
|
||||
void device_start();
|
||||
void sound_stream_update(short* outputs, int outLen);
|
||||
inline int32_t get_channel_output(int ch) {
|
||||
return ((m_output[ch]!=0)?m_volume[ch]:0);
|
||||
}
|
||||
void device_start();
|
||||
void sound_stream_update(short** outputs, int outLen);
|
||||
inline int32_t get_channel_output(int ch) {
|
||||
return ((m_output[ch]!=0)?m_volume[ch]:0);
|
||||
}
|
||||
//DECLARE_READ_LINE_MEMBER( ready_r ) { return m_ready_state ? 1 : 0; }
|
||||
|
||||
sn76496_base_device(
|
||||
int feedbackmask,
|
||||
int noise_start,
|
||||
int noise_start,
|
||||
int noisetap1,
|
||||
int noisetap2,
|
||||
bool negate,
|
||||
bool stereo,
|
||||
int clockdivider,
|
||||
bool ncr,
|
||||
bool sega);
|
||||
|
||||
sn76496_base_device(
|
||||
int feedbackmask,
|
||||
int noisetap1,
|
||||
int noisetap2,
|
||||
bool negate,
|
||||
bool stereo,
|
||||
int clockdivider,
|
||||
bool ncr,
|
||||
bool sega)
|
||||
: sn76496_base_device(
|
||||
feedbackmask,
|
||||
feedbackmask,
|
||||
noisetap1,
|
||||
noisetap2,
|
||||
negate,
|
||||
stereo,
|
||||
clockdivider,
|
||||
ncr,
|
||||
sega)
|
||||
{}
|
||||
|
||||
private:
|
||||
inline bool in_noise_mode();
|
||||
|
||||
bool m_ready_state;
|
||||
|
||||
const int32_t m_feedback_mask; // mask for feedback
|
||||
const int32_t m_noise_start; // noise start value
|
||||
const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14)
|
||||
const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13)
|
||||
bool m_negate; // output negate flag
|
||||
const int32_t m_clock_divider; // clock divider
|
||||
const int32_t m_feedback_mask; // mask for feedback
|
||||
const int32_t m_noise_start; // noise start value
|
||||
const int32_t m_whitenoise_tap1; // mask for white noise tap 1 (higher one, usually bit 14)
|
||||
const int32_t m_whitenoise_tap2; // mask for white noise tap 2 (lower one, usually bit 13)
|
||||
bool m_negate; // output negate flag
|
||||
const bool m_stereo; // whether we're dealing with stereo or not
|
||||
const int32_t m_clock_divider; // clock divider
|
||||
const bool m_ncr_style_psg; // flag to ignore writes to regs 1,3,5,6,7 with bit 7 low
|
||||
const bool m_sega_style_psg; // flag to make frequency zero acts as if it is one more than max (0x3ff+1) or if it acts like 0; the initial register is pointing to 0x3 instead of 0x0; the volume reg is preloaded with 0xF instead of 0x0
|
||||
|
||||
|
|
@ -51,6 +74,7 @@ private:
|
|||
int32_t m_volume[4]; // db volume of voice 0-2 and noise
|
||||
uint32_t m_RNG; // noise generator LFSR
|
||||
int32_t m_current_clock;
|
||||
int32_t m_stereo_mask; // the stereo output mask
|
||||
int32_t m_period[4]; // Length of 1/2 of waveform
|
||||
int32_t m_count[4]; // Position within the waveform
|
||||
int32_t m_output[4]; // 1-bit output of each channel, pre-volume
|
||||
|
|
@ -63,4 +87,67 @@ public:
|
|||
sn76496_device();
|
||||
};
|
||||
|
||||
// Y2404 not verified yet. todo: verify; (don't be fooled by the Y, it's a TI chip, not Yamaha)
|
||||
class y2404_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
y2404_device();
|
||||
};
|
||||
|
||||
// SN76489 not verified yet. todo: verify;
|
||||
class sn76489_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
sn76489_device();
|
||||
};
|
||||
|
||||
// SN76489A: whitenoise verified, phase verified, periodic verified (by plgdavid)
|
||||
class sn76489a_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
sn76489a_device();
|
||||
};
|
||||
|
||||
// SN76494 not verified, (according to datasheet: same as sn76489a but without the /8 divider)
|
||||
class sn76494_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
sn76494_device();
|
||||
};
|
||||
|
||||
// SN94624 whitenoise verified, phase verified, period verified; verified by PlgDavid
|
||||
class sn94624_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
sn94624_device();
|
||||
};
|
||||
|
||||
// NCR8496 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
|
||||
class ncr8496_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
ncr8496_device();
|
||||
};
|
||||
|
||||
// PSSJ-3 whitenoise verified, phase verified; verified by ValleyBell & NewRisingSun
|
||||
class pssj3_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
pssj3_device();
|
||||
};
|
||||
|
||||
// Verified by Justin Kerk
|
||||
class gamegear_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
gamegear_device();
|
||||
};
|
||||
|
||||
// todo: verify; from smspower wiki, assumed to have same invert as gamegear
|
||||
class segapsg_device : public sn76496_base_device
|
||||
{
|
||||
public:
|
||||
segapsg_device();
|
||||
};
|
||||
|
||||
#endif // MAME_SOUND_SN76496_H
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ public:
|
|||
|
||||
protected:
|
||||
// simulate the DAC discontinuity
|
||||
constexpr int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); }
|
||||
int32_t dac_discontinuity(int32_t value) const { return (value < 0) ? (value - 2) : (value + 3); }
|
||||
|
||||
// internal state
|
||||
uint16_t m_address; // address register
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ int DivPlatformSwan::dispatch(DivCommand c) {
|
|||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.had) {
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
calcAndWriteOutVol(c.chan,15);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,43 +22,11 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "fmshared_OPM.h"
|
||||
|
||||
// actually 0x40 but the upper bit of data selects address
|
||||
#define ADDR_WS_FINE 0x100
|
||||
// actually 0xc0 but bit 5 of data selects address
|
||||
#define ADDR_EGS_REV 0x120
|
||||
|
||||
static unsigned short chanOffs[8]={
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
|
||||
};
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x08, 0x10, 0x18
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define NOTE_LINEAR(x) (((x)<<6)+baseFreqOff+log2(parent->song.tuning/440.0)*12.0*64.0)
|
||||
|
||||
const char* regCheatSheetOPZ[]={
|
||||
"Test", "00",
|
||||
"NoteCtl", "08",
|
||||
|
|
@ -233,7 +201,7 @@ void DivPlatformTX81Z::acquire(short* bufL, short* bufR, size_t start, size_t le
|
|||
fm_ymfm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm_ymfm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1052,7 +1020,7 @@ void DivPlatformTX81Z::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformTX81Z::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,330);
|
||||
fm_ymfm->reset();
|
||||
if (dumpWrites) {
|
||||
|
|
|
|||
|
|
@ -19,18 +19,22 @@
|
|||
|
||||
#ifndef _TX81Z_H
|
||||
#define _TX81Z_H
|
||||
#include "../dispatch.h"
|
||||
#include "fmshared_OPM.h"
|
||||
#include "../macroInt.h"
|
||||
#include "../instrument.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opz.h"
|
||||
#include "../macroInt.h"
|
||||
|
||||
class DivTXInterface: public ymfm::ymfm_interface {
|
||||
|
||||
};
|
||||
|
||||
class DivPlatformTX81Z: public DivDispatch {
|
||||
class DivPlatformTX81Z: public DivPlatformOPM {
|
||||
protected:
|
||||
const unsigned short chanOffs[8]={
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
DivMacroInt std;
|
||||
|
|
@ -69,31 +73,18 @@ class DivPlatformTX81Z: public DivDispatch {
|
|||
};
|
||||
Channel chan[8];
|
||||
DivDispatchOscBuffer* oscBuf[8];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
int delay, baseFreqOff;
|
||||
int baseFreqOff;
|
||||
int pcmL, pcmR, pcmCycles;
|
||||
unsigned char lastBusy;
|
||||
unsigned char amDepth, pmDepth;
|
||||
|
||||
ymfm::ym2414* fm_ymfm;
|
||||
ymfm::ym2414::output_data out_ymfm;
|
||||
DivTXInterface iface;
|
||||
|
||||
unsigned char regPool[330];
|
||||
|
||||
bool extMode;
|
||||
|
||||
bool isMuted[8];
|
||||
|
||||
short oldWrites[330];
|
||||
short pendingWrites[330];
|
||||
|
||||
int octave(int freq);
|
||||
int toFreq(int freq);
|
||||
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ int DivPlatformVIC20::dispatch(DivCommand c) {
|
|||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.had) {
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
calcAndWriteOutVol(c.chan,15);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,16 +23,8 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
#include "ym2203shared.h"
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
static unsigned char konOffs[3]={
|
||||
0, 1, 2
|
||||
};
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
const char* regCheatSheetYM2203[]={
|
||||
// SSG
|
||||
|
|
@ -299,7 +291,7 @@ void DivPlatformYM2203::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
fm->write(0x0,w.addr);
|
||||
fm->write(0x1,w.val);
|
||||
regPool[w.addr&0xff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=6;
|
||||
}
|
||||
}
|
||||
|
|
@ -959,7 +951,7 @@ void DivPlatformYM2203::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2203::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,256);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
|
@ -989,6 +981,10 @@ void DivPlatformYM2203::reset() {
|
|||
|
||||
extMode=false;
|
||||
|
||||
// set prescaler
|
||||
immWrite(0x2d,0xff);
|
||||
immWrite(prescale,0xff);
|
||||
|
||||
ay->reset();
|
||||
ay->getRegisterWrites().clear();
|
||||
ay->flushWrites();
|
||||
|
|
@ -1021,25 +1017,58 @@ void DivPlatformYM2203::setSkipRegisterWrites(bool value) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2203::setFlags(unsigned int flags) {
|
||||
unsigned char ayFlags=16;
|
||||
if (flags==3) {
|
||||
chipClock=3000000.0;
|
||||
ayFlags=20;
|
||||
} else if (flags==2) {
|
||||
chipClock=4000000.0;
|
||||
ayFlags=19;
|
||||
} else if (flags==1) {
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
ayFlags=17;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC;
|
||||
ayFlags=16;
|
||||
// Clock flags
|
||||
switch (flags&0x1f) {
|
||||
default:
|
||||
case 0x00:
|
||||
chipClock=COLOR_NTSC;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_PAL*4.0/5.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=4000000.0;
|
||||
break;
|
||||
case 0x03:
|
||||
chipClock=3000000.0;
|
||||
break;
|
||||
case 0x04:
|
||||
chipClock=38400*13*8; // 31948800/8
|
||||
break;
|
||||
case 0x05:
|
||||
chipClock=3000000.0/2.0;
|
||||
break;
|
||||
}
|
||||
// Prescaler flags
|
||||
switch ((flags>>5)&0x3) {
|
||||
default:
|
||||
case 0x00: // /6
|
||||
prescale=0x2d;
|
||||
fmFreqBase=4720270.0,
|
||||
fmDivBase=36,
|
||||
ayDiv=16;
|
||||
break;
|
||||
case 0x01: // /3
|
||||
prescale=0x2e;
|
||||
fmFreqBase=4720270.0/2.0,
|
||||
fmDivBase=18,
|
||||
ayDiv=8;
|
||||
break;
|
||||
case 0x02: // /2
|
||||
prescale=0x2f;
|
||||
fmFreqBase=4720270.0/3.0,
|
||||
fmDivBase=12,
|
||||
ayDiv=4;
|
||||
break;
|
||||
}
|
||||
ay->setFlags(ayFlags);
|
||||
rate=fm->sample_rate(chipClock);
|
||||
for (int i=0; i<6; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
immWrite(0x2d,0xff);
|
||||
immWrite(prescale,0xff);
|
||||
ay->setExtClockDiv(chipClock,ayDiv);
|
||||
ay->setFlags(16);
|
||||
}
|
||||
|
||||
int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
|
|
@ -1053,13 +1082,13 @@ int DivPlatformYM2203::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
fm=new ymfm::ym2203(iface);
|
||||
fm->set_fidelity(ymfm::OPN_FIDELITY_MIN);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay=new DivPlatformAY8910(true,chipClock,ayDiv);
|
||||
ay->init(p,3,sugRate,16);
|
||||
ay->toggleRegisterDump(true);
|
||||
setFlags(flags);
|
||||
|
||||
reset();
|
||||
return 16;
|
||||
return 6;
|
||||
}
|
||||
|
||||
void DivPlatformYM2203::quit() {
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
#ifndef _YM2203_H
|
||||
#define _YM2203_H
|
||||
#include "../dispatch.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "ay.h"
|
||||
|
|
@ -30,12 +29,16 @@ class DivYM2203Interface: public ymfm::ymfm_interface {
|
|||
|
||||
};
|
||||
|
||||
class DivPlatformYM2203: public DivDispatch {
|
||||
class DivPlatformYM2203: public DivPlatformOPN {
|
||||
protected:
|
||||
const unsigned short chanOffs[3]={
|
||||
0x00, 0x01, 0x02
|
||||
};
|
||||
|
||||
const unsigned char konOffs[3]={
|
||||
0, 1, 2
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
|
|
@ -79,29 +82,16 @@ class DivPlatformYM2203: public DivDispatch {
|
|||
Channel chan[6];
|
||||
DivDispatchOscBuffer* oscBuf[6];
|
||||
bool isMuted[6];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2203* fm;
|
||||
ymfm::ym2203::output_data fmout;
|
||||
DivYM2203Interface iface;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
unsigned char prescale;
|
||||
|
||||
short oldWrites[256];
|
||||
short pendingWrites[256];
|
||||
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
||||
public:
|
||||
|
|
@ -128,6 +118,9 @@ class DivPlatformYM2203: public DivDispatch {
|
|||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformYM2203():
|
||||
DivPlatformOPN(4720270.0, 36, 16),
|
||||
prescale(0x2d) {}
|
||||
~DivPlatformYM2203();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2203shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
int DivPlatformYM2203Ext::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
|
|
@ -516,7 +516,7 @@ int DivPlatformYM2203Ext::init(DivEngine* parent, int channels, int sugRate, uns
|
|||
}
|
||||
|
||||
reset();
|
||||
return 19;
|
||||
return 9;
|
||||
}
|
||||
|
||||
void DivPlatformYM2203Ext::quit() {
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x04, 0x08, 0x0c
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_FREQBASE 4720270
|
||||
|
|
@ -24,16 +24,8 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
#include "ym2610shared.h"
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
static unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
const char* regCheatSheetYM2608[]={
|
||||
// SSG
|
||||
|
|
@ -450,7 +442,7 @@ void DivPlatformYM2608::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
fm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=4;
|
||||
}
|
||||
}
|
||||
|
|
@ -1283,7 +1275,7 @@ void DivPlatformYM2608::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2608::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,512);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
|
@ -1333,6 +1325,10 @@ void DivPlatformYM2608::reset() {
|
|||
// enable 6 channel mode
|
||||
immWrite(0x29,0x80);
|
||||
|
||||
// set prescaler
|
||||
immWrite(0x2d,0xff);
|
||||
immWrite(prescale,0xff);
|
||||
|
||||
ay->reset();
|
||||
ay->getRegisterWrites().clear();
|
||||
ay->flushWrites();
|
||||
|
|
@ -1402,6 +1398,49 @@ void DivPlatformYM2608::renderSamples() {
|
|||
adpcmBMemLen=memPos+256;
|
||||
}
|
||||
|
||||
void DivPlatformYM2608::setFlags(unsigned int flags) {
|
||||
// Clock flags
|
||||
switch (flags&0x1f) {
|
||||
default:
|
||||
case 0x00:
|
||||
chipClock=8000000.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=38400*13*16; // 31948800/4
|
||||
break;
|
||||
}
|
||||
// Prescaler flags
|
||||
switch ((flags>>5)&0x3) {
|
||||
default:
|
||||
case 0x00: // /6
|
||||
prescale=0x2d;
|
||||
fmFreqBase=9440540.0,
|
||||
fmDivBase=72,
|
||||
ayDiv=32;
|
||||
break;
|
||||
case 0x01: // /3
|
||||
prescale=0x2e;
|
||||
fmFreqBase=9440540.0/2.0,
|
||||
fmDivBase=36,
|
||||
ayDiv=16;
|
||||
break;
|
||||
case 0x02: // /2
|
||||
prescale=0x2f;
|
||||
fmFreqBase=9440540.0/3.0,
|
||||
fmDivBase=24,
|
||||
ayDiv=8;
|
||||
break;
|
||||
}
|
||||
rate=fm->sample_rate(chipClock);
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
immWrite(0x2d,0xff);
|
||||
immWrite(prescale,0xff);
|
||||
ay->setExtClockDiv(chipClock,ayDiv);
|
||||
ay->setFlags(16);
|
||||
}
|
||||
|
||||
int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
adpcmBMem=new unsigned char[getSampleMemCapacity(0)];
|
||||
|
|
@ -1414,17 +1453,13 @@ int DivPlatformYM2608::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=8000000;
|
||||
fm=new ymfm::ym2608(iface);
|
||||
fm->set_fidelity(ymfm::OPN_FIDELITY_MIN);
|
||||
rate=fm->sample_rate(chipClock);
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay=new DivPlatformAY8910(true,chipClock,ayDiv);
|
||||
ay->init(p,3,sugRate,16);
|
||||
ay->toggleRegisterDump(true);
|
||||
setFlags(flags);
|
||||
reset();
|
||||
return 16;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
#ifndef _YM2608_H
|
||||
#define _YM2608_H
|
||||
#include "../dispatch.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "ay.h"
|
||||
|
|
@ -35,12 +34,16 @@ class DivYM2608Interface: public ymfm::ymfm_interface {
|
|||
DivYM2608Interface(): adpcmBMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformYM2608: public DivDispatch {
|
||||
class DivPlatformYM2608: public DivPlatformOPN {
|
||||
protected:
|
||||
const unsigned short chanOffs[6]={
|
||||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
const unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
|
|
@ -86,17 +89,8 @@ class DivPlatformYM2608: public DivDispatch {
|
|||
Channel chan[16];
|
||||
DivDispatchOscBuffer* oscBuf[16];
|
||||
bool isMuted[16];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2608* fm;
|
||||
ymfm::ym2608::output_data fmout;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char* adpcmBMem;
|
||||
size_t adpcmBMemLen;
|
||||
|
|
@ -106,13 +100,9 @@ class DivPlatformYM2608: public DivDispatch {
|
|||
unsigned char sampleBank;
|
||||
unsigned char writeRSSOff, writeRSSOn;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
unsigned char prescale;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
@ -142,8 +132,12 @@ class DivPlatformYM2608: public DivDispatch {
|
|||
size_t getSampleMemCapacity(int index);
|
||||
size_t getSampleMemUsage(int index);
|
||||
void renderSamples();
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformYM2608():
|
||||
DivPlatformOPN(9440540.0, 72, 32),
|
||||
prescale(0x2d) {}
|
||||
~DivPlatformYM2608();
|
||||
};
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
int DivPlatformYM2608Ext::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
|
|
|
|||
|
|
@ -24,19 +24,8 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
static unsigned char konOffs[4]={
|
||||
1, 2, 5, 6
|
||||
};
|
||||
|
||||
static unsigned char bchOffs[4]={
|
||||
1, 2, 4, 5
|
||||
};
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
const char* regCheatSheetYM2610[]={
|
||||
// SSG
|
||||
|
|
@ -494,7 +483,7 @@ void DivPlatformYM2610::acquire(short* bufL, short* bufR, size_t start, size_t l
|
|||
fm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=4;
|
||||
}
|
||||
}
|
||||
|
|
@ -1330,7 +1319,7 @@ void DivPlatformYM2610::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,512);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
|
@ -1402,6 +1391,22 @@ void DivPlatformYM2610::setSkipRegisterWrites(bool value) {
|
|||
ay->setSkipRegisterWrites(value);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610::setFlags(unsigned int flags) {
|
||||
switch (flags&0xff) {
|
||||
default:
|
||||
case 0x00:
|
||||
chipClock=8000000.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=24167829/3;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<14; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610Base::init(p, channels, sugRate, flags);
|
||||
dumpWrites=false;
|
||||
|
|
@ -1410,15 +1415,11 @@ int DivPlatformYM2610::init(DivEngine* p, int channels, int sugRate, unsigned in
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=8000000;
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<14; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
fm=new ymfm::ym2610(iface);
|
||||
setFlags(flags);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay=new DivPlatformAY8910(true,chipClock,32);
|
||||
ay->init(p,3,sugRate,16);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 14;
|
||||
|
|
|
|||
|
|
@ -19,9 +19,8 @@
|
|||
|
||||
#ifndef _YM2610_H
|
||||
#define _YM2610_H
|
||||
#include "../dispatch.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "ay.h"
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
|
|
@ -35,7 +34,7 @@ class DivYM2610Interface: public ymfm::ymfm_interface {
|
|||
DivYM2610Interface(): adpcmAMem(NULL), adpcmBMem(NULL), sampleBank(0) {}
|
||||
};
|
||||
|
||||
class DivPlatformYM2610Base: public DivDispatch {
|
||||
class DivPlatformYM2610Base: public DivPlatformOPN {
|
||||
protected:
|
||||
unsigned char* adpcmAMem;
|
||||
size_t adpcmAMemLen;
|
||||
|
|
@ -50,6 +49,8 @@ class DivPlatformYM2610Base: public DivDispatch {
|
|||
void renderSamples();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
DivPlatformYM2610Base():
|
||||
DivPlatformOPN(9440540.0, 72, 32) {}
|
||||
};
|
||||
|
||||
class DivPlatformYM2610: public DivPlatformYM2610Base {
|
||||
|
|
@ -58,6 +59,14 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
0x01, 0x02, 0x101, 0x102
|
||||
};
|
||||
|
||||
const unsigned char konOffs[4]={
|
||||
1, 2, 5, 6
|
||||
};
|
||||
|
||||
const unsigned char bchOffs[4]={
|
||||
1, 2, 4, 5
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
|
|
@ -103,29 +112,15 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
Channel chan[14];
|
||||
DivDispatchOscBuffer* oscBuf[14];
|
||||
bool isMuted[14];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2610* fm;
|
||||
ymfm::ym2610::output_data fmout;
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
friend void putDispatchChan(void*,int,int);
|
||||
|
|
@ -151,6 +146,7 @@ class DivPlatformYM2610: public DivPlatformYM2610Base {
|
|||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2610();
|
||||
|
|
|
|||
|
|
@ -23,15 +23,8 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
|
||||
#include "fmshared_OPN.h"
|
||||
|
||||
static unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
const char* regCheatSheetYM2610B[]={
|
||||
// SSG
|
||||
|
|
@ -472,7 +465,7 @@ void DivPlatformYM2610B::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
fm->write(0x0+((w.addr>>8)<<1),w.addr);
|
||||
fm->write(0x1+((w.addr>>8)<<1),w.val);
|
||||
regPool[w.addr&0x1ff]=w.val;
|
||||
writes.pop();
|
||||
writes.pop_front();
|
||||
delay=4;
|
||||
}
|
||||
}
|
||||
|
|
@ -1308,7 +1301,7 @@ void DivPlatformYM2610B::poke(std::vector<DivRegWrite>& wlist) {
|
|||
}
|
||||
|
||||
void DivPlatformYM2610B::reset() {
|
||||
while (!writes.empty()) writes.pop();
|
||||
while (!writes.empty()) writes.pop_front();
|
||||
memset(regPool,0,512);
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
|
|
@ -1380,6 +1373,22 @@ void DivPlatformYM2610B::setSkipRegisterWrites(bool value) {
|
|||
ay->setSkipRegisterWrites(value);
|
||||
}
|
||||
|
||||
void DivPlatformYM2610B::setFlags(unsigned int flags) {
|
||||
switch (flags&0xff) {
|
||||
default:
|
||||
case 0x00:
|
||||
chipClock=8000000.0;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=24167829/3;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
DivPlatformYM2610Base::init(p, channels, sugRate, flags);
|
||||
dumpWrites=false;
|
||||
|
|
@ -1388,15 +1397,11 @@ int DivPlatformYM2610B::init(DivEngine* p, int channels, int sugRate, unsigned i
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=8000000;
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<16; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
fm=new ymfm::ym2610b(iface);
|
||||
setFlags(flags);
|
||||
// YM2149, 2MHz
|
||||
ay=new DivPlatformAY8910;
|
||||
ay->init(p,3,sugRate,19);
|
||||
ay=new DivPlatformAY8910(true,chipClock,32);
|
||||
ay->init(p,3,sugRate,16);
|
||||
ay->toggleRegisterDump(true);
|
||||
reset();
|
||||
return 16;
|
||||
|
|
|
|||
|
|
@ -19,12 +19,10 @@
|
|||
|
||||
#ifndef _YM2610B_H
|
||||
#define _YM2610B_H
|
||||
#include "../dispatch.h"
|
||||
#include "ym2610.h"
|
||||
#include "../macroInt.h"
|
||||
#include <queue>
|
||||
#include "sound/ymfm/ymfm_opn.h"
|
||||
|
||||
#include "ym2610.h"
|
||||
|
||||
class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
||||
protected:
|
||||
|
|
@ -32,6 +30,10 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
0x00, 0x01, 0x02, 0x100, 0x101, 0x102
|
||||
};
|
||||
|
||||
const unsigned char konOffs[6]={
|
||||
0, 1, 2, 4, 5, 6
|
||||
};
|
||||
|
||||
struct Channel {
|
||||
DivInstrumentFM state;
|
||||
unsigned char freqH, freqL;
|
||||
|
|
@ -77,27 +79,15 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
Channel chan[16];
|
||||
DivDispatchOscBuffer* oscBuf[16];
|
||||
bool isMuted[16];
|
||||
struct QueuedWrite {
|
||||
unsigned short addr;
|
||||
unsigned char val;
|
||||
bool addrOrVal;
|
||||
QueuedWrite(unsigned short a, unsigned char v): addr(a), val(v), addrOrVal(false) {}
|
||||
};
|
||||
std::queue<QueuedWrite> writes;
|
||||
ymfm::ym2610b* fm;
|
||||
ymfm::ym2610b::output_data fmout;
|
||||
unsigned char regPool[512];
|
||||
unsigned char lastBusy;
|
||||
|
||||
DivPlatformAY8910* ay;
|
||||
unsigned char sampleBank;
|
||||
|
||||
int delay;
|
||||
|
||||
bool extMode;
|
||||
|
||||
short oldWrites[512];
|
||||
short pendingWrites[512];
|
||||
double fmFreqBase=9440540;
|
||||
unsigned char ayDiv=32;
|
||||
|
||||
double NOTE_OPNB(int ch, int note);
|
||||
double NOTE_ADPCMB(int note);
|
||||
|
|
@ -124,6 +114,7 @@ class DivPlatformYM2610B: public DivPlatformYM2610Base {
|
|||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
~DivPlatformYM2610B();
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
int DivPlatformYM2610BExt::dispatch(DivCommand c) {
|
||||
if (c.chan<2) {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#include "ym2610shared.h"
|
||||
#include "fmshared_OPN.h"
|
||||
#define CHIP_FREQBASE fmFreqBase
|
||||
#define CHIP_DIVIDER fmDivBase
|
||||
|
||||
int DivPlatformYM2610Ext::dispatch(DivCommand c) {
|
||||
if (c.chan<1) {
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
/**
|
||||
* Furnace Tracker - multi-system chiptune tracker
|
||||
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
static unsigned short opOffs[4]={
|
||||
0x00, 0x04, 0x08, 0x0c
|
||||
};
|
||||
static bool isOutput[8][4]={
|
||||
// 1 3 2 4
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,false,true},
|
||||
{false,false,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{false,true ,true ,true},
|
||||
{true ,true ,true ,true},
|
||||
};
|
||||
static unsigned char dtTable[8]={
|
||||
7,6,5,0,1,2,3,4
|
||||
};
|
||||
|
||||
static int orderedOps[4]={
|
||||
0,2,1,3
|
||||
};
|
||||
|
||||
#define rWrite(a,v) if (!skipRegisterWrites) {pendingWrites[a]=v;}
|
||||
#define immWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} }
|
||||
|
||||
#define CHIP_FREQBASE 9440540
|
||||
|
|
@ -435,6 +435,42 @@ void DivPlatformYMZ280B::setChipModel(int type) {
|
|||
chipType=type;
|
||||
}
|
||||
|
||||
void DivPlatformYMZ280B::setFlags(unsigned int flags) {
|
||||
switch (chipType) {
|
||||
default:
|
||||
case 280:
|
||||
switch (flags&0xff) {
|
||||
case 0x00:
|
||||
chipClock=16934400;
|
||||
break;
|
||||
case 0x01:
|
||||
chipClock=COLOR_NTSC*4.0;
|
||||
break;
|
||||
case 0x02:
|
||||
chipClock=COLOR_PAL*16.0/5.0;
|
||||
break;
|
||||
case 0x03:
|
||||
chipClock=16000000;
|
||||
break;
|
||||
case 0x04:
|
||||
chipClock=50000000.0/3.0;
|
||||
break;
|
||||
case 0x05:
|
||||
chipClock=14000000;
|
||||
break;
|
||||
}
|
||||
rate=chipClock/384;
|
||||
break;
|
||||
case 759:
|
||||
rate=32000;
|
||||
chipClock=rate*384;
|
||||
break;
|
||||
}
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformYMZ280B::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
|
|
@ -444,18 +480,12 @@ int DivPlatformYMZ280B::init(DivEngine* p, int channels, int sugRate, unsigned i
|
|||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
setFlags(flags);
|
||||
|
||||
rate=(chipType==759)?32000:44100;
|
||||
chipClock=rate*384;
|
||||
sampleMem=new unsigned char[getSampleMemCapacity()];
|
||||
sampleMemLen=0;
|
||||
ymz280b.device_start(sampleMem);
|
||||
setFlags(flags);
|
||||
reset();
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ class DivPlatformYMZ280B: public DivDispatch {
|
|||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
void setFlags(unsigned int flags);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
}
|
||||
o=sampleOut;
|
||||
bufL[h]=o?16384:0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=o?16384:-16384;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -76,6 +77,7 @@ void DivPlatformZXBeeper::acquire(short* bufL, short* bufR, size_t start, size_t
|
|||
if (++curChan>=6) curChan=0;
|
||||
|
||||
bufL[h]=o?16384:0;
|
||||
oscBuf[0]->data[oscBuf[0]->needle++]=o?16384:-16384;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +258,7 @@ DivMacroInt* DivPlatformZXBeeper::getChanMacroInt(int ch) {
|
|||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformZXBeeper::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
return (ch<1)?oscBuf[ch]:NULL;
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformZXBeeper::getRegisterPool() {
|
||||
|
|
|
|||
|
|
@ -988,6 +988,10 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
if (chan[i].vibratoDepth>0) {
|
||||
chan[i].vibratoPos+=chan[i].vibratoRate;
|
||||
if (chan[i].vibratoPos>=64) chan[i].vibratoPos-=64;
|
||||
|
||||
chan[i].vibratoPosGiant+=chan[i].vibratoRate;
|
||||
if (chan[i].vibratoPos>=512) chan[i].vibratoPos-=512;
|
||||
|
||||
switch (chan[i].vibratoDir) {
|
||||
case 1: // up
|
||||
dispatchCmd(DivCommand(DIV_CMD_PITCH,i,chan[i].pitch+(MAX(0,(chan[i].vibratoDepth*vibTable[chan[i].vibratoPos]*chan[i].vibratoFine)>>4)/15)));
|
||||
|
|
|
|||
|
|
@ -82,7 +82,8 @@ short SafeReader::readS() {
|
|||
logD("SR: reading short %x:",curSeek);
|
||||
#endif
|
||||
if (curSeek+2>len) throw EndOfFileException(this,len);
|
||||
short ret=*(short*)(&buf[curSeek]);
|
||||
short ret;
|
||||
memcpy(&ret,&buf[curSeek],2);
|
||||
#ifdef READ_DEBUG
|
||||
logD("SR: %.4x",ret);
|
||||
#endif
|
||||
|
|
@ -92,7 +93,8 @@ short SafeReader::readS() {
|
|||
|
||||
short SafeReader::readS_BE() {
|
||||
if (curSeek+2>len) throw EndOfFileException(this,len);
|
||||
short ret=*(short*)(&buf[curSeek]);
|
||||
short ret;
|
||||
memcpy(&ret,&buf[curSeek],2);
|
||||
curSeek+=2;
|
||||
return ((ret>>8)&0xff)|(ret<<8);
|
||||
}
|
||||
|
|
@ -102,7 +104,8 @@ int SafeReader::readI() {
|
|||
logD("SR: reading int %x:",curSeek);
|
||||
#endif
|
||||
if (curSeek+4>len) throw EndOfFileException(this,len);
|
||||
int ret=*(int*)(&buf[curSeek]);
|
||||
int ret;
|
||||
memcpy(&ret,&buf[curSeek],4);
|
||||
curSeek+=4;
|
||||
#ifdef READ_DEBUG
|
||||
logD("SR: %.8x",ret);
|
||||
|
|
@ -112,28 +115,32 @@ int SafeReader::readI() {
|
|||
|
||||
int SafeReader::readI_BE() {
|
||||
if (curSeek+4>len) throw EndOfFileException(this,len);
|
||||
unsigned int ret=*(unsigned int*)(&buf[curSeek]);
|
||||
unsigned int ret;
|
||||
memcpy(&ret,&buf[curSeek],4);
|
||||
curSeek+=4;
|
||||
return (int)((ret>>24)|((ret&0xff0000)>>8)|((ret&0xff00)<<8)|((ret&0xff)<<24));
|
||||
}
|
||||
|
||||
int64_t SafeReader::readL() {
|
||||
if (curSeek+8>len) throw EndOfFileException(this,len);
|
||||
int64_t ret=*(int64_t*)(&buf[curSeek]);
|
||||
int64_t ret;
|
||||
memcpy(&ret,&buf[curSeek],8);
|
||||
curSeek+=8;
|
||||
return ret;
|
||||
}
|
||||
|
||||
float SafeReader::readF() {
|
||||
if (curSeek+4>len) throw EndOfFileException(this,len);
|
||||
float ret=*(float*)(&buf[curSeek]);
|
||||
float ret;
|
||||
memcpy(&ret,&buf[curSeek],4);
|
||||
curSeek+=4;
|
||||
return ret;
|
||||
}
|
||||
|
||||
double SafeReader::readD() {
|
||||
if (curSeek+8>len) throw EndOfFileException(this,len);
|
||||
double ret=*(double*)(&buf[curSeek]);
|
||||
double ret;
|
||||
memcpy(&ret,&buf[curSeek],8);
|
||||
curSeek+=8;
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,14 @@ bool DivSample::save(const char* path) {
|
|||
|
||||
si.channels=1;
|
||||
si.samplerate=rate;
|
||||
si.format=SF_FORMAT_PCM_16|SF_FORMAT_WAV;
|
||||
switch (depth) {
|
||||
case 8: // 8-bit
|
||||
si.format=SF_FORMAT_PCM_U8|SF_FORMAT_WAV;
|
||||
break;
|
||||
default: // 16-bit
|
||||
si.format=SF_FORMAT_PCM_16|SF_FORMAT_WAV;
|
||||
break;
|
||||
}
|
||||
|
||||
f=sf_open(path,SFM_WRITE,&si);
|
||||
|
||||
|
|
@ -77,7 +84,21 @@ bool DivSample::save(const char* path) {
|
|||
}
|
||||
sf_command(f, SFC_SET_INSTRUMENT, &inst, sizeof(inst));
|
||||
|
||||
sf_writef_short(f,data16,samples);
|
||||
switch (depth) {
|
||||
case 8: {
|
||||
// convert from signed to unsigned
|
||||
unsigned char* buf=new unsigned char[length8];
|
||||
for (size_t i=0; i<length8; i++) {
|
||||
buf[i]=data8[i]^0x80;
|
||||
}
|
||||
sf_write_raw(f,buf,length8);
|
||||
delete[] buf;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
sf_write_raw(f,data16,length16);
|
||||
break;
|
||||
}
|
||||
|
||||
sf_close(f);
|
||||
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ struct DivSample {
|
|||
offQSound(0),
|
||||
offX1_010(0),
|
||||
offSU(0),
|
||||
offYMZ280B(0),
|
||||
offRF5C68(0),
|
||||
samples(0) {}
|
||||
~DivSample();
|
||||
|
|
|
|||
|
|
@ -237,28 +237,45 @@ struct DivSong {
|
|||
// - 1: PAL
|
||||
// - 2: Dendy
|
||||
// - SMS/SN76489:
|
||||
// - bit 0-1: clock rate
|
||||
// - 0: NTSC (3.58MHz)
|
||||
// - 1: PAL (3.55MHz)
|
||||
// - 2: Other (4MHz)
|
||||
// - 3: half NTSC (1.79MHz)
|
||||
// - bit 2-3: noise type
|
||||
// - 0: Sega VDP (16-bit noise)
|
||||
// - 1: real SN76489 (15-bit noise)
|
||||
// - 2: real SN76489 with Atari-like short noise buzz (15-bit noise)
|
||||
// - 3: Game Gear (16-bit noise, stereo)
|
||||
// - bit 0-1, 8-15: clock rate
|
||||
// - 0000: 3.58MHz (NTSC)
|
||||
// - 0001: 3.55MHz (PAL)
|
||||
// - 0002: 4MHz (Other)
|
||||
// - 0003: 1.79MHz (half NTSC)
|
||||
// - 0100: 3MHz
|
||||
// - 0101: 2MHz
|
||||
// - 0102: 447KHz (NTSC / 8)
|
||||
// - bit 2-3, 6-7: chip type
|
||||
// - 00: Sega VDP (16-bit noise)
|
||||
// - 04: real SN76489 (15-bit noise)
|
||||
// - 08: real SN76489 with Atari-like short noise buzz (15-bit noise)
|
||||
// - 0c: Game Gear (16-bit noise, stereo)
|
||||
// - 40: real SN76489A (17-bit noise)
|
||||
// - 44: real SN76496 (17-bit noise)
|
||||
// - 48: NCR 8496 (16-bit noise)
|
||||
// - 4c: Tandy PSSJ-3 (16-bit noise)
|
||||
// - 80: real SN94624 (15-bit noise)
|
||||
// - 84: real SN76494 (17-bit noise)
|
||||
// - bit 4: disable noise phase reset
|
||||
// - YM2612:
|
||||
// - bit 0-1: clock rate
|
||||
// - YM2612/YM3438:
|
||||
// - bit 0-30: clock rate
|
||||
// - 0: Genesis NTSC (7.67MHz)
|
||||
// - 1: Genesis PAL (7.61MHz)
|
||||
// - 2: 8MHz
|
||||
// - 2: FM Towns (8MHz)
|
||||
// - 3: AtGames Genesis (6.13MHz)
|
||||
// - 4: Sega System 32 (8.06MHz)
|
||||
// - bit 31: DAC distortion
|
||||
// - 0: disable
|
||||
// - 1: enable
|
||||
// - YM2151:
|
||||
// - bit 0-1: clock rate
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 3.58MHz (NTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - YM2610(B):
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 8MHz (Neo Geo MVS)
|
||||
// - 1: 8.06MHz (Neo Geo AES)
|
||||
// - AY-3-8910/AY8930:
|
||||
// - bit 0-3: clock rate
|
||||
// - 0: 1.79MHz (MSX NTSC)
|
||||
|
|
@ -274,6 +291,8 @@ struct DivSong {
|
|||
// - 10: 2.097152MHz (Game Boy)
|
||||
// - 11: 3.58MHz (Darky)
|
||||
// - 12: 3.6MHz (Darky)
|
||||
// - 13: 1.25MHz
|
||||
// - 14: 1.536MHz
|
||||
// - bit 4-5: chip type (ignored on AY8930)
|
||||
// - 0: AY-3-8910 or similar
|
||||
// - 1: YM2149
|
||||
|
|
@ -287,9 +306,9 @@ struct DivSong {
|
|||
// - 1: low (internally divided to half)
|
||||
// - SAA1099:
|
||||
// - bit 0-1: clock rate
|
||||
// - 0: 8MHz (SAM Coupé, Game Blaster)
|
||||
// - 1: 7.15MHz
|
||||
// - 2: 7.09MHz
|
||||
// - 0: 8MHz (SAM Coupé)
|
||||
// - 1: 7.15MHz (Game Blaster, NTSC)
|
||||
// - 2: 7.09MHz (PAL)
|
||||
// - Amiga:
|
||||
// - bit 0: clock rate
|
||||
// - 0: 7.15MHz (NTSC)
|
||||
|
|
@ -330,6 +349,80 @@ struct DivSong {
|
|||
// - bit 4: stereo
|
||||
// - 0: mono
|
||||
// - 1: stereo
|
||||
// - YM2203:
|
||||
// - bit 0-4: clock rate
|
||||
// - 0: 3.58MHz (MTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - 3: 3MHz
|
||||
// - 4: 3.9936MHz (PC-88, PC-98)
|
||||
// - 5: 1.5MHz
|
||||
// - bit 5-6: output rate
|
||||
// - 0: FM: clock / 72, SSG: clock / 16
|
||||
// - 1: FM: clock / 36, SSG: clock / 8
|
||||
// - 2: FM: clock / 24, SSG: clock / 4
|
||||
// - YM2608:
|
||||
// - bit 0-4: clock rate
|
||||
// - 0: 8MHz
|
||||
// - 1: 7.987MHz (PC-88, PC-98)
|
||||
// - bit 5-6: output rate
|
||||
// - 0: FM: clock / 144, SSG: clock / 32
|
||||
// - 1: FM: clock / 72, SSG: clock / 16
|
||||
// - 2: FM: clock / 48, SSG: clock / 8
|
||||
// - YM3526, YM3812, Y8950:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 3.58MHz (MTSC)
|
||||
// - 1: 3.55MHz (PAL)
|
||||
// - 2: 4MHz
|
||||
// - 3: 3MHz
|
||||
// - 4: 3.9936MHz (PC-88, PC-98)
|
||||
// - 5: 3.5MHz
|
||||
// - YMF262:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 14.32MHz (MTSC)
|
||||
// - 1: 14.19MHz (PAL)
|
||||
// - 2: 14MHz
|
||||
// - 3: 16MHz
|
||||
// - 4: 15MHz
|
||||
// - YMF289B: (TODO)
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 33.8688MHz
|
||||
// - 1: 28.64MHz (MTSC)
|
||||
// - 2: 28.38MHz (PAL)
|
||||
// - MSM6295:
|
||||
// - bit 0-6: clock rate
|
||||
// - 0: 1MHz
|
||||
// - 1: 1.056MHz
|
||||
// - 2: 4MHz
|
||||
// - 3: 4.224MHz
|
||||
// - 4: 3.58MHz (NTSC)
|
||||
// - 5: 1.79MHz (Half NTSC)
|
||||
// - 6: 1.023MHz
|
||||
// - 7: 0.895MHz (Quarter NTSC)
|
||||
// - 8: 2MHz
|
||||
// - 9: 2.112MHz
|
||||
// - 10: 0.875MHz
|
||||
// - 11: 0.9375MHz
|
||||
// - 12: 1.5MHz
|
||||
// - 13: 3MHz
|
||||
// - 14: 1.193MHz
|
||||
// - bit 7: Output rate
|
||||
// - 0: clock / 132
|
||||
// - 1: clock / 165
|
||||
// - SCC/+:
|
||||
// - bit 0-6: clock rate
|
||||
// - 0: 1.79MHz (MSX NTSC)
|
||||
// - 1: 1.77MHz (PAL)
|
||||
// - 2: 1.5MHz
|
||||
// - 3: 2MHz
|
||||
// - YMZ280B:
|
||||
// - bit 0-7: clock rate
|
||||
// - 0: 16.9344MHz
|
||||
// - 1: 14.32MHz (MTSC)
|
||||
// - 2: 14.19MHz (PAL)
|
||||
// - 3: 16MHz
|
||||
// - 4: 16.67MHz
|
||||
// - 5: 14MHz
|
||||
unsigned int systemFlags[32];
|
||||
|
||||
// song information
|
||||
|
|
|
|||
|
|
@ -1653,13 +1653,34 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
|||
w->write(writeX1010[i]->getSampleMem(),writeX1010[i]->getSampleMemUsage());
|
||||
}
|
||||
if (writeZ280[i]!=NULL && writeZ280[i]->getSampleMemUsage()>0) {
|
||||
// In VGM, YMZ280B's 16-bit PCM has an endianness swapped
|
||||
// which have been fixed in the upstream MAME since 2013
|
||||
// in order to get Konami FireBeat working
|
||||
// The reason given for VGM not applying this change was
|
||||
// "It matches OPL4 and MAME probably did an endianness optimization"
|
||||
size_t sampleMemLen=writeZ280[i]->getSampleMemUsage();
|
||||
unsigned char* sampleMem=new unsigned char[sampleMemLen];
|
||||
memcpy(sampleMem,writeZ280[i]->getSampleMem(),sampleMemLen);
|
||||
for (int i=0; i<song.sampleLen; i++) {
|
||||
DivSample* s=song.sample[i];
|
||||
if (s->depth==16) {
|
||||
unsigned int pos=s->offYMZ280B;
|
||||
for (unsigned int j=0; j<s->samples; j++) {
|
||||
unsigned char lo=sampleMem[pos+j*2];
|
||||
unsigned char hi=sampleMem[pos+j*2+1];
|
||||
sampleMem[pos+j*2]=hi;
|
||||
sampleMem[pos+j*2+1]=lo;
|
||||
}
|
||||
}
|
||||
}
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(0x86);
|
||||
w->writeI((writeZ280[i]->getSampleMemUsage()+8)|(i*0x80000000));
|
||||
w->writeI(writeZ280[i]->getSampleMemCapacity());
|
||||
w->writeI(0);
|
||||
w->write(writeZ280[i]->getSampleMem(),writeZ280[i]->getSampleMemUsage());
|
||||
w->write(sampleMem,sampleMemLen);
|
||||
delete[] sampleMem;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -243,8 +243,13 @@ void DivWaveSynth::changeWave2(int num) {
|
|||
first=true;
|
||||
}
|
||||
|
||||
void DivWaveSynth::setEngine(DivEngine* engine) {
|
||||
void DivWaveSynth::setEngine(DivEngine* engine, int waveFloor) {
|
||||
e=engine;
|
||||
memset(wave1,waveFloor,256);
|
||||
memset(wave2,waveFloor,256);
|
||||
for (int i=0; i<256; i++) {
|
||||
output[i]=waveFloor;
|
||||
}
|
||||
}
|
||||
|
||||
void DivWaveSynth::init(DivInstrument* which, int w, int h, bool insChanged) {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ class DivWaveSynth {
|
|||
* @param insChanged whether the instrument has changed.
|
||||
*/
|
||||
void init(DivInstrument* which, int width, int height, bool insChanged=false);
|
||||
void setEngine(DivEngine* engine);
|
||||
void setEngine(DivEngine* engine, int waveFloor=0);
|
||||
DivWaveSynth():
|
||||
e(NULL),
|
||||
pos(0),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue