Merge branch 'master' into ZSMv1

This commit is contained in:
ZeroByteOrg 2022-06-23 13:45:12 -05:00
commit ae7b271e1d
128 changed files with 8022 additions and 1597 deletions

View file

@ -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.");
}
}

View file

@ -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),

View file

@ -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++) {

View file

@ -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);

View file

@ -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;
}

View file

@ -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

View file

@ -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));
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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

View 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

View file

@ -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);

View file

@ -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

View file

@ -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) {

View file

@ -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"

View file

@ -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;
}

View file

@ -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:

View file

@ -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;
}

View file

@ -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

View file

@ -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) {

View file

@ -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)";

View file

@ -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++) {

View file

@ -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];

View file

@ -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;
}
}

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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);

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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) {

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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() {

View file

@ -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

View file

@ -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() {

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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) {

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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();

View file

@ -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) {

View file

@ -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) {

View file

@ -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

View file

@ -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;
}

View file

@ -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:

View file

@ -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() {

View file

@ -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)));

View file

@ -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;
}

View file

@ -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);

View file

@ -248,6 +248,7 @@ struct DivSample {
offQSound(0),
offX1_010(0),
offSU(0),
offYMZ280B(0),
offRF5C68(0),
samples(0) {}
~DivSample();

View file

@ -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

View file

@ -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;
}
}

View file

@ -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) {

View file

@ -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),