Merge pull request #528 from tildearrow/preset1

Prepare for reducing duplicates for 4op FM related codes, Add and correct bunch of presets, Add various clock, type options for chips
This commit is contained in:
tildearrow 2022-06-17 22:53:56 -05:00 committed by GitHub
commit 1598047aca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 2246 additions and 1011 deletions

View File

@ -130,7 +130,7 @@ static void YMPSG_ClockInternal1(ympsg_t *chip)
else if (noise_of && !chip->noise_of)
{
noise_bit1 = (chip->noise >> chip->noise_tap2) & 1;
noise_bit2 = (chip->noise >> 12) & 1;
noise_bit2 = (chip->noise >> chip->noise_tap1) & 1;
noise_bit1 ^= noise_bit2;
noise_next = ((noise_bit1 && ((chip->noise_data >> 2) & 1)) || ((chip->noise & chip->noise_size) == 0));
chip->noise <<= 1;
@ -257,13 +257,14 @@ uint16_t YMPSG_Read(ympsg_t *chip)
return data;
}
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn)
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t noise_tap2, uint32_t noise_size)
{
uint32_t i;
memset(chip, 0, sizeof(ympsg_t));
YMPSG_SetIC(chip, 1);
chip->noise_tap2 = real_sn ? 13 : 15;
chip->noise_size = real_sn ? 16383 : 32767;
chip->noise_tap1 = noise_tap1;
chip->noise_tap2 = noise_tap2;
chip->noise_size = noise_size;
for (i = 0; i < 17; i++)
{
chip->vol_table[i]=(real_sn?tipsg_vol[i]:ympsg_vol[i]) * 8192.0f;

View File

@ -46,8 +46,9 @@ typedef struct {
uint8_t sign_l;
uint8_t noise_sign_l;
uint16_t noise;
uint8_t noise_tap1;
uint8_t noise_tap2;
uint16_t noise_size;
uint32_t noise_size;
uint8_t test;
uint8_t volume_out[4];
@ -68,7 +69,7 @@ typedef struct {
void YMPSG_Write(ympsg_t *chip, uint8_t data);
uint16_t YMPSG_Read(ympsg_t *chip);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn);
void YMPSG_Init(ympsg_t *chip, uint8_t real_sn, uint8_t noise_tap1, uint8_t noise_tap2, uint32_t noise_size);
void YMPSG_SetIC(ympsg_t *chip, uint32_t ic);
void YMPSG_Clock(ympsg_t *chip);
int YMPSG_GetOutput(ympsg_t *chip);

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

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

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

@ -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();
@ -781,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);
}
@ -1511,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);
}
@ -1628,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:
@ -1657,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;
@ -1695,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

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

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

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

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

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

@ -3540,7 +3540,12 @@ void FurnaceGUI::drawInsEdit() {
int panMax=0;
bool panSingle=false;
bool panSingleNoBit=false;
if (ins->type==DIV_INS_FM || ins->type==DIV_INS_OPL || ins->type==DIV_INS_GB || ins->type==DIV_INS_OPZ || ins->type==DIV_INS_VERA) {
if (ins->type==DIV_INS_STD ||//Game Gear
ins->type==DIV_INS_FM ||
ins->type==DIV_INS_OPL ||
ins->type==DIV_INS_GB ||
ins->type==DIV_INS_OPZ ||
ins->type==DIV_INS_VERA) {
panMax=1;
panSingle=true;
}

File diff suppressed because it is too large Load Diff

View File

@ -28,19 +28,19 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
case DIV_SYSTEM_YM2612_EXT:
case DIV_SYSTEM_YM2612_FRAC:
case DIV_SYSTEM_YM2612_FRAC_EXT: {
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&7)==0)) {
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&(~0x80000000))==0)) {
copyOfFlags=(flags&0x80000000)|0;
}
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&7)==1)) {
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&(~0x80000000))==1)) {
copyOfFlags=(flags&0x80000000)|1;
}
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&7)==2)) {
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&(~0x80000000))==2)) {
copyOfFlags=(flags&0x80000000)|2;
}
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&7)==3)) {
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&(~0x80000000))==3)) {
copyOfFlags=(flags&0x80000000)|3;
}
if (ImGui::RadioButton("Sega System 32 (8.05MHz)",(flags&7)==4)) {
if (ImGui::RadioButton("Sega System 32 (8.05MHz)",(flags&(~0x80000000))==4)) {
copyOfFlags=(flags&0x80000000)|4;
}
bool ladder=flags&0x80000000;
@ -51,43 +51,61 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
}
case DIV_SYSTEM_SMS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
copyOfFlags=(flags&(~3))|0;
if (ImGui::RadioButton("3.58MHz (NTSC)",(flags&0xff03)==0x0000)) {
copyOfFlags=(flags&(~0xff03))|0x0000;
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&3)==1)) {
copyOfFlags=(flags&(~3))|1;
if (ImGui::RadioButton("3.55MHz (PAL)",(flags&0xff03)==0x0001)) {
copyOfFlags=(flags&(~0xff03))|0x0001;
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&3)==2)) {
copyOfFlags=(flags&(~3))|2;
if (ImGui::RadioButton("4MHz (BBC Micro)",(flags&0xff03)==0x0002)) {
copyOfFlags=(flags&(~0xff03))|0x0002;
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&3)==3)) {
copyOfFlags=(flags&(~3))|3;
if (ImGui::RadioButton("1.79MHz (Half NTSC)",(flags&0xff03)==0x0003)) {
copyOfFlags=(flags&(~0xff03))|0x0003;
}
if (ImGui::RadioButton("3MHz (Exed Exes)",(flags&0xff03)==0x0100)) {
copyOfFlags=(flags&(~0xff03))|0x0100;
}
if (ImGui::RadioButton("2MHz (Sega System 1)",(flags&0xff03)==0x0101)) {
copyOfFlags=(flags&(~0xff03))|0x0101;
}
if (ImGui::RadioButton("447KHz (TI-99/4A)",(flags&0xff03)==0x0102)) {
copyOfFlags=(flags&(~0xff03))|0x0102;
}
ImGui::Text("Chip type:");
if (ImGui::RadioButton("Sega VDP/Master System",((flags>>2)&3)==0)) {
copyOfFlags=(flags&(~12))|0;
if (ImGui::RadioButton("Sega VDP/Master System",(flags&0xcc)==0x00)) {
copyOfFlags=(flags&(~0xcc))|0x00;
}
if (ImGui::RadioButton("TI SN76489",((flags>>2)&3)==1)) {
copyOfFlags=(flags&(~12))|4;
if (ImGui::RadioButton("TI SN76489",(flags&0xcc)==0x04)) {
copyOfFlags=(flags&(~0xcc))|0x04;
}
if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",((flags>>2)&3)==2)) {
copyOfFlags=(flags&(~12))|8;
if (ImGui::RadioButton("TI SN76489 with Atari-like short noise",(flags&0xcc)==0x08)) {
copyOfFlags=(flags&(~0xcc))|0x08;
}
if (ImGui::RadioButton("Game Gear",(flags&0xcc)==0x0c)) {
copyOfFlags=(flags&(~0xcc))|0x0c;
}
if (ImGui::RadioButton("TI SN76489A",(flags&0xcc)==0x40)) {
copyOfFlags=(flags&(~0xcc))|0x40;
}
if (ImGui::RadioButton("TI SN76496",(flags&0xcc)==0x44)) {
copyOfFlags=(flags&(~0xcc))|0x44;
}
if (ImGui::RadioButton("NCR 8496",(flags&0xcc)==0x48)) {
copyOfFlags=(flags&(~0xcc))|0x48;
}
if (ImGui::RadioButton("Tandy PSSJ 3-voice sound",(flags&0xcc)==0x4c)) {
copyOfFlags=(flags&(~0xcc))|0x4c;
}
if (ImGui::RadioButton("TI SN94624",(flags&0xcc)==0x80)) {
copyOfFlags=(flags&(~0xcc))|0x80;
}
if (ImGui::RadioButton("TI SN76494",(flags&0xcc)==0x84)) {
copyOfFlags=(flags&(~0xcc))|0x84;
}
/*if (ImGui::RadioButton("Game Gear",(flags>>2)==3)) {
copyOfFlags=(flags&3)|12);
}*/
bool noPhaseReset=flags&16;
if (ImGui::Checkbox("Disable noise period change phase reset",&noPhaseReset)) {
copyOfFlags=(flags&(~16))|(noPhaseReset<<4);
}
break;
}
@ -97,37 +115,29 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&15)==0)) {
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("PAL (3.55MHz)",(flags&15)==1)) {
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("BBC Micro (4MHz)",(flags&15)==2)) {
copyOfFlags=(flags&(~15))|2;
}
if (ImGui::RadioButton("Half NTSC (1.79MHz)",(flags&15)==3)) {
copyOfFlags=(flags&(~15))|3;
}
if (type!=DIV_SYSTEM_VRC7) {
ImGui::Text("Patch set:");
if (ImGui::RadioButton("Yamaha YM2413",((flags>>4)&15)==0)) {
copyOfFlags=(flags&(~0xf0))|0;
}
if (ImGui::RadioButton("Yamaha YMF281",((flags>>4)&15)==1)) {
copyOfFlags=(flags&(~0xf0))|0x10;
}
if (ImGui::RadioButton("Yamaha YM2423",((flags>>4)&15)==2)) {
copyOfFlags=(flags&(~0xf0))|0x20;
}
if (ImGui::RadioButton("Konami VRC7",((flags>>4)&15)==3)) {
copyOfFlags=(flags&(~0xf0))|0x30;
}
}
break;
@ -135,15 +145,12 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
case DIV_SYSTEM_YM2151:
if (ImGui::RadioButton("NTSC/X16 (3.58MHz)",flags==0)) {
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (3.55MHz)",flags==1)) {
copyOfFlags=1;
}
if (ImGui::RadioButton("X1/X68000 (4MHz)",flags==2)) {
copyOfFlags=2;
}
break;
case DIV_SYSTEM_NES:
@ -152,30 +159,37 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
case DIV_SYSTEM_MMC5:
if (ImGui::RadioButton("NTSC (1.79MHz)",flags==0)) {
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (1.67MHz)",flags==1)) {
copyOfFlags=1;
}
if (ImGui::RadioButton("Dendy (1.77MHz)",flags==2)) {
copyOfFlags=2;
}
break;
case DIV_SYSTEM_C64_8580:
case DIV_SYSTEM_C64_6581:
if (ImGui::RadioButton("NTSC (1.02MHz)",flags==0)) {
copyOfFlags=0;
}
if (ImGui::RadioButton("PAL (0.99MHz)",flags==1)) {
copyOfFlags=1;
}
if (ImGui::RadioButton("SSI 2001 (0.89MHz)",flags==2)) {
copyOfFlags=2;
}
break;
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT:
if (ImGui::RadioButton("8MHz (Neo Geo MVS)",(flags&0xff)==0)) {
copyOfFlags=(flags&(~0xff))|0;
}
if (ImGui::RadioButton("8.06MHz (Neo Geo AES)",(flags&0xff)==1)) {
copyOfFlags=(flags&(~0xff))|1;
}
break;
case DIV_SYSTEM_AY8910:
@ -183,87 +197,74 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (ZX Spectrum NTSC/MSX)",(flags&15)==0)) {
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("1.77MHz (ZX Spectrum)",(flags&15)==1)) {
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("1.75MHz (ZX Spectrum)",(flags&15)==2)) {
copyOfFlags=(flags&(~15))|2;
}
if (ImGui::RadioButton("2MHz (Atari ST/Sharp X1)",(flags&15)==3)) {
copyOfFlags=(flags&(~15))|3;
}
if (ImGui::RadioButton("1.5MHz (Vectrex)",(flags&15)==4)) {
copyOfFlags=(flags&(~15))|4;
}
if (ImGui::RadioButton("1MHz (Amstrad CPC)",(flags&15)==5)) {
copyOfFlags=(flags&(~15))|5;
}
if (ImGui::RadioButton("0.89MHz (Pre-divided Sunsoft 5B)",(flags&15)==6)) {
copyOfFlags=(flags&(~15))|6;
}
if (ImGui::RadioButton("1.67MHz (?)",(flags&15)==7)) {
copyOfFlags=(flags&(~15))|7;
}
if (ImGui::RadioButton("0.83MHz (Pre-divided Sunsoft 5B on PAL)",(flags&15)==8)) {
copyOfFlags=(flags&(~15))|8;
}
if (ImGui::RadioButton("1.10MHz (Gamate/VIC-20 PAL)",(flags&15)==9)) {
copyOfFlags=(flags&(~15))|9;
}
if (ImGui::RadioButton("2^21Hz (Game Boy)",(flags&15)==10)) {
copyOfFlags=(flags&(~15))|10;
}
if (ImGui::RadioButton("3.58MHz (Darky)",(flags&15)==11)) {
copyOfFlags=(flags&(~15))|11;
}
if (ImGui::RadioButton("3.6MHz (Darky)",(flags&15)==12)) {
copyOfFlags=(flags&(~15))|12;
}
if (ImGui::RadioButton("1.25MHz (Mag Max)",(flags&15)==13)) {
copyOfFlags=(flags&(~15))|13;
}
if (ImGui::RadioButton("1.536MHz (Kyugo)",(flags&15)==14)) {
copyOfFlags=(flags&(~15))|14;
}
if (type==DIV_SYSTEM_AY8910) {
ImGui::Text("Chip type:");
if (ImGui::RadioButton("AY-3-8910",(flags&0x30)==0)) {
copyOfFlags=(flags&(~0x30))|0;
}
if (ImGui::RadioButton("YM2149(F)",(flags&0x30)==16)) {
copyOfFlags=(flags&(~0x30))|16;
}
if (ImGui::RadioButton("Sunsoft 5B",(flags&0x30)==32)) {
copyOfFlags=(flags&(~0x30))|32;
}
if (ImGui::RadioButton("AY-3-8914",(flags&0x30)==48)) {
copyOfFlags=(flags&(~0x30))|48;
}
}
bool stereo=flags&0x40;
ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)==32));
if (ImGui::Checkbox("Stereo##_AY_STEREO",&stereo)) {
copyOfFlags=(flags&(~0x40))|(stereo?0x40:0);
}
ImGui::EndDisabled();
bool clockSel=flags&0x80;
ImGui::BeginDisabled((type==DIV_SYSTEM_AY8910) && ((flags&0x30)!=16));
if (ImGui::Checkbox("Half Clock divider##_AY_CLKSEL",&clockSel)) {
copyOfFlags=(flags&(~0x80))|(clockSel?0x80:0);
}
ImGui::EndDisabled();
break;
@ -375,18 +376,57 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
}
break;
}
case DIV_SYSTEM_OPN: {
if (ImGui::RadioButton("NTSC (3.58MHz)",(flags&3)==0)) {
copyOfFlags=(flags&0x80000000)|0;
case DIV_SYSTEM_OPN:
case DIV_SYSTEM_OPN_EXT: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("3.58MHz (NTSC)",(flags&31)==0)) {
copyOfFlags=(flags&(~31))|0;
}
if (ImGui::RadioButton("PAL (3.54MHz)",(flags&3)==1)) {
copyOfFlags=(flags&0x80000000)|1;
if (ImGui::RadioButton("3.54MHz (PAL)",(flags&31)==1)) {
copyOfFlags=(flags&(~31))|1;
}
if (ImGui::RadioButton("Arcade (4MHz)",(flags&3)==2)) {
copyOfFlags=(flags&0x80000000)|2;
if (ImGui::RadioButton("4MHz",(flags&31)==2)) {
copyOfFlags=(flags&(~31))|2;
}
if (ImGui::RadioButton("PC-9801-26K? TODO: CONFIRM (3MHz)",(flags&3)==3)) {
copyOfFlags=(flags&0x80000000)|3;
if (ImGui::RadioButton("3MHz",(flags&31)==3)) {
copyOfFlags=(flags&(~31))|3;
}
if (ImGui::RadioButton("3.9936MHz (PC-88/PC-98)",(flags&31)==4)) {
copyOfFlags=(flags&(~31))|4;
}
if (ImGui::RadioButton("1.5MHz",(flags&31)==5)) {
copyOfFlags=(flags&(~31))|5;
}
ImGui::Text("Output rate:");
if (ImGui::RadioButton("FM: clock / 72, SSG: clock / 16",(flags&96)==0)) {
copyOfFlags=(flags&(~96))|0;
}
if (ImGui::RadioButton("FM: clock / 36, SSG: clock / 8",(flags&96)==32)) {
copyOfFlags=(flags&(~96))|32;
}
if (ImGui::RadioButton("FM: clock / 24, SSG: clock / 4",(flags&96)==64)) {
copyOfFlags=(flags&(~96))|64;
}
break;
}
case DIV_SYSTEM_PC98:
case DIV_SYSTEM_PC98_EXT: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("8MHz (Arcade)",(flags&31)==0)) {
copyOfFlags=(flags&(~31))|0;
}
if (ImGui::RadioButton("7.987MHz (PC-88/PC-98)",(flags&31)==1)) {
copyOfFlags=(flags&(~31))|1;
}
ImGui::Text("Output rate:");
if (ImGui::RadioButton("FM: clock / 144, SSG: clock / 32",(flags&96)==0)) {
copyOfFlags=(flags&(~96))|0;
}
if (ImGui::RadioButton("FM: clock / 72, SSG: clock / 16",(flags&96)==32)) {
copyOfFlags=(flags&(~96))|32;
}
if (ImGui::RadioButton("FM: clock / 48, SSG: clock / 8",(flags&96)==64)) {
copyOfFlags=(flags&(~96))|64;
}
break;
}
@ -394,24 +434,19 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("8MHz (FM Towns)",(flags&15)==0)) {
copyOfFlags=(flags&(~15))|0;
}
if (ImGui::RadioButton("10MHz (Sega System 18)",(flags&15)==1)) {
copyOfFlags=(flags&(~15))|1;
}
if (ImGui::RadioButton("12.5MHz (Sega CD/System 32)",(flags&15)==2)) {
copyOfFlags=(flags&(~15))|2;
}
ImGui::Text("Chip type:");
if (ImGui::RadioButton("RF5C68 (10-bit output)",((flags>>4)&15)==0)) {
copyOfFlags=(flags&(~240))|0;
}
if (ImGui::RadioButton("RF5C164 (16-bit output)",((flags>>4)&15)==1)) {
copyOfFlags=(flags&(~240))|16;
}
break;
}
@ -433,44 +468,143 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
}
case DIV_SYSTEM_MSM6295: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1MHz",flags==0)) {
copyOfFlags=0;
if (ImGui::RadioButton("1MHz",(flags&127)==0)) {
copyOfFlags=(flags&(~127))|0;
}
if (ImGui::RadioButton("1.056MHz",flags==1)) {
copyOfFlags=1;
if (ImGui::RadioButton("1.056MHz",(flags&127)==1)) {
copyOfFlags=(flags&(~127))|1;
}
if (ImGui::RadioButton("4MHz",flags==2)) {
copyOfFlags=2;
if (ImGui::RadioButton("4MHz",(flags&127)==2)) {
copyOfFlags=(flags&(~127))|2;
}
if (ImGui::RadioButton("4.224MHz",flags==3)) {
copyOfFlags=3;
if (ImGui::RadioButton("4.224MHz",(flags&127)==3)) {
copyOfFlags=(flags&(~127))|3;
}
if (ImGui::RadioButton("3.58MHz",flags==4)) {
copyOfFlags=4;
if (ImGui::RadioButton("3.58MHz",(flags&127)==4)) {
copyOfFlags=(flags&(~127))|4;
}
if (ImGui::RadioButton("1.79MHz",flags==5)) {
copyOfFlags=5;
if (ImGui::RadioButton("1.79MHz",(flags&127)==5)) {
copyOfFlags=(flags&(~127))|5;
}
if (ImGui::RadioButton("1.02MHz",flags==6)) {
copyOfFlags=6;
if (ImGui::RadioButton("1.02MHz",(flags&127)==6)) {
copyOfFlags=(flags&(~127))|6;
}
if (ImGui::RadioButton("0.89MHz",flags==7)) {
copyOfFlags=7;
if (ImGui::RadioButton("0.89MHz",(flags&127)==7)) {
copyOfFlags=(flags&(~127))|7;
}
if (ImGui::RadioButton("2MHz",flags==8)) {
copyOfFlags=8;
if (ImGui::RadioButton("2MHz",(flags&127)==8)) {
copyOfFlags=(flags&(~127))|8;
}
if (ImGui::RadioButton("2.112MHz",flags==9)) {
copyOfFlags=9;
if (ImGui::RadioButton("2.112MHz",(flags&127)==9)) {
copyOfFlags=(flags&(~127))|9;
}
if (ImGui::RadioButton("0.875MHz",flags==10)) {
copyOfFlags=10;
if (ImGui::RadioButton("0.875MHz",(flags&127)==10)) {
copyOfFlags=(flags&(~127))|10;
}
if (ImGui::RadioButton("0.9375MHz",flags==11)) {
copyOfFlags=11;
if (ImGui::RadioButton("0.9375MHz",(flags&127)==11)) {
copyOfFlags=(flags&(~127))|11;
}
if (ImGui::RadioButton("1.5MHz",flags==12)) {
copyOfFlags=12;
if (ImGui::RadioButton("1.5MHz",(flags&127)==12)) {
copyOfFlags=(flags&(~127))|12;
}
if (ImGui::RadioButton("3MHz",(flags&127)==13)) {
copyOfFlags=(flags&(~127))|13;
}
if (ImGui::RadioButton("1.193MHz (Atari)",(flags&127)==14)) {
copyOfFlags=(flags&(~127))|14;
}
ImGui::Text("Output rate:");
if (ImGui::RadioButton("clock / 132",(flags&128)==0)) {
copyOfFlags=(flags&(~128))|0;
}
if (ImGui::RadioButton("clock / 165",(flags&128)==128)) {
copyOfFlags=(flags&(~128))|128;
}
break;
}
case DIV_SYSTEM_SCC:
case DIV_SYSTEM_SCC_PLUS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("1.79MHz (NTSC/MSX)",(flags&127)==0)) {
copyOfFlags=(flags&(~127))|0;
}
if (ImGui::RadioButton("1.77MHz (PAL)",(flags&127)==1)) {
copyOfFlags=(flags&(~127))|1;
}
if (ImGui::RadioButton("1.5MHz (Arcade)",(flags&127)==2)) {
copyOfFlags=(flags&(~127))|2;
}
if (ImGui::RadioButton("2MHz",(flags&127)==3)) {
copyOfFlags=(flags&(~127))|3;
}
break;
}
case DIV_SYSTEM_OPL:
case DIV_SYSTEM_OPL_DRUMS:
case DIV_SYSTEM_OPL2:
case DIV_SYSTEM_OPL2_DRUMS:
case DIV_SYSTEM_Y8950:
case DIV_SYSTEM_Y8950_DRUMS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("3.58MHz (NTSC)",(flags&255)==0)) {
copyOfFlags=(flags&(~255))|0;
}
if (ImGui::RadioButton("3.54MHz (PAL)",(flags&255)==1)) {
copyOfFlags=(flags&(~255))|1;
}
if (ImGui::RadioButton("4MHz",(flags&255)==2)) {
copyOfFlags=(flags&(~255))|2;
}
if (ImGui::RadioButton("3MHz",(flags&255)==3)) {
copyOfFlags=(flags&(~255))|3;
}
if (ImGui::RadioButton("3.9936MHz (PC-88/PC-98)",(flags&255)==4)) {
copyOfFlags=(flags&(~255))|4;
}
if (ImGui::RadioButton("3.5MHz",(flags&255)==5)) {
copyOfFlags=(flags&(~255))|5;
}
break;
}
case DIV_SYSTEM_OPL3:
case DIV_SYSTEM_OPL3_DRUMS: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("14.32MHz (MTSC)",(flags&255)==0)) {
copyOfFlags=(flags&(~255))|0;
}
if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==1)) {
copyOfFlags=(flags&(~255))|1;
}
if (ImGui::RadioButton("14MHz",(flags&255)==2)) {
copyOfFlags=(flags&(~255))|2;
}
if (ImGui::RadioButton("16MHz",(flags&255)==3)) {
copyOfFlags=(flags&(~255))|3;
}
if (ImGui::RadioButton("15MHz",(flags&255)==4)) {
copyOfFlags=(flags&(~255))|4;
}
break;
}
case DIV_SYSTEM_YMZ280B: {
ImGui::Text("Clock rate:");
if (ImGui::RadioButton("16.9344MHz",(flags&255)==0)) {
copyOfFlags=(flags&(~255))|0;
}
if (ImGui::RadioButton("14.32MHz (MTSC)",(flags&255)==1)) {
copyOfFlags=(flags&(~255))|1;
}
if (ImGui::RadioButton("14.19MHz (PAL)",(flags&255)==3)) {
copyOfFlags=(flags&(~255))|2;
}
if (ImGui::RadioButton("16MHz",(flags&255)==3)) {
copyOfFlags=(flags&(~255))|3;
}
if (ImGui::RadioButton("16.67MHz",(flags&255)==4)) {
copyOfFlags=(flags&(~255))|4;
}
if (ImGui::RadioButton("14MHz",(flags&255)==5)) {
copyOfFlags=(flags&(~255))|5;
}
break;
}
@ -478,17 +612,8 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
case DIV_SYSTEM_SWAN:
case DIV_SYSTEM_VERA:
case DIV_SYSTEM_BUBSYS_WSG:
case DIV_SYSTEM_YM2610:
case DIV_SYSTEM_YM2610_EXT:
case DIV_SYSTEM_YM2610_FULL:
case DIV_SYSTEM_YM2610_FULL_EXT:
case DIV_SYSTEM_YM2610B:
case DIV_SYSTEM_YM2610B_EXT:
case DIV_SYSTEM_YMU759:
case DIV_SYSTEM_PET:
case DIV_SYSTEM_SCC:
case DIV_SYSTEM_SCC_PLUS:
case DIV_SYSTEM_YMZ280B:
ImGui::Text("nothing to configure");
break;
default:

View File

@ -153,6 +153,7 @@ TAParamResult pVersion(String) {
printf("- puNES by FHorse (GPLv2)\n");
printf("- reSID by Dag Lem (GPLv2)\n");
printf("- Stella by Stella Team (GPLv2)\n");
printf("- vgsound_emu by cam900 (BSD 3-clause)\n");
return TA_PARAM_QUIT;
}