mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-30 16:33:01 +00:00
Merge pull request #476 from akumanatt/rf5c68
Add RF5C68 and RF5C164 support
This commit is contained in:
commit
08203bf2a3
14 changed files with 1019 additions and 15 deletions
|
@ -321,6 +321,8 @@ src/engine/platform/sound/scc/scc.cpp
|
|||
|
||||
src/engine/platform/sound/ymz280b.cpp
|
||||
|
||||
src/engine/platform/sound/rf5c68.cpp
|
||||
|
||||
src/engine/platform/oplAInterface.cpp
|
||||
src/engine/platform/ym2608Interface.cpp
|
||||
src/engine/platform/ym2610Interface.cpp
|
||||
|
@ -388,6 +390,7 @@ src/engine/platform/vrc6.cpp
|
|||
src/engine/platform/scc.cpp
|
||||
src/engine/platform/ymz280b.cpp
|
||||
src/engine/platform/namcowsg.cpp
|
||||
src/engine/platform/rf5c68.cpp
|
||||
src/engine/platform/dummy.cpp
|
||||
)
|
||||
|
||||
|
|
1
TODO.md
1
TODO.md
|
@ -1,6 +1,5 @@
|
|||
# to-do for 0.6pre1
|
||||
|
||||
- RF5C68 system
|
||||
- ADPCM chips
|
||||
- Game Boy envelope macro/sequence
|
||||
- drag-and-drop ins/wave/sample loading
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
#include "platform/mmc5.h"
|
||||
#include "platform/scc.h"
|
||||
#include "platform/ymz280b.h"
|
||||
#include "platform/rf5c68.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
#include "platform/zxbeeper.h"
|
||||
|
@ -356,6 +357,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
dispatch=new DivPlatformYMZ280B;
|
||||
((DivPlatformYMZ280B*)dispatch)->setChipModel(280);
|
||||
break;
|
||||
case DIV_SYSTEM_RF5C68:
|
||||
dispatch=new DivPlatformRF5C68;
|
||||
break;
|
||||
case DIV_SYSTEM_SOUND_UNIT:
|
||||
dispatch=new DivPlatformSoundUnit;
|
||||
break;
|
||||
|
|
|
@ -1150,14 +1150,12 @@ void DivPlatformGenesis::setYMFM(bool use) {
|
|||
}
|
||||
|
||||
void DivPlatformGenesis::setFlags(unsigned int flags) {
|
||||
if (flags==3) {
|
||||
chipClock=COLOR_NTSC*12.0/7.0;
|
||||
} else if (flags==2) {
|
||||
chipClock=8000000.0;
|
||||
} else if (flags==1) {
|
||||
chipClock=COLOR_PAL*12.0/7.0;
|
||||
} else {
|
||||
chipClock=COLOR_NTSC*15.0/7.0;
|
||||
switch (flags) {
|
||||
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);
|
||||
|
|
434
src/engine/platform/rf5c68.cpp
Normal file
434
src/engine/platform/rf5c68.cpp
Normal file
|
@ -0,0 +1,434 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "rf5c68.h"
|
||||
#include "../engine.h"
|
||||
#include "../../ta-log.h"
|
||||
#include <math.h>
|
||||
|
||||
#define rWrite(a,v) {if(!skipRegisterWrites) {rf5c68.rf5c68_w(a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v);}}
|
||||
|
||||
#define CHIP_FREQBASE 786432
|
||||
|
||||
const char* regCheatSheetRF5C68[]={
|
||||
"Volume", "0",
|
||||
"Panning", "1",
|
||||
"FreqL", "2",
|
||||
"FreqH", "3",
|
||||
"LoopStartL", "4",
|
||||
"LoopStartH", "5",
|
||||
"StartH", "6",
|
||||
"Control", "7",
|
||||
"Disable", "8",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformRF5C68::getRegisterSheet() {
|
||||
return regCheatSheetRF5C68;
|
||||
}
|
||||
|
||||
const char* DivPlatformRF5C68::getEffectName(unsigned char effect) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::chWrite(unsigned char ch, unsigned int addr, unsigned char val) {
|
||||
if (!skipRegisterWrites) {
|
||||
if (curChan!=ch) {
|
||||
curChan=ch;
|
||||
rWrite(7,curChan|0xc0);
|
||||
}
|
||||
regPool[16+((ch)<<4)+((addr)&0x0f)]=val;
|
||||
rWrite(addr,val);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
short buf[16][256];
|
||||
short* chBufPtrs[16]={
|
||||
buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7],
|
||||
buf[8],buf[9],buf[10],buf[11],buf[12],buf[13],buf[14],buf[15]
|
||||
};
|
||||
size_t pos=start;
|
||||
while (len > 0) {
|
||||
size_t blockLen=MIN(len,256);
|
||||
short* bufPtrs[2]={&bufL[pos],&bufR[pos]};
|
||||
rf5c68.sound_stream_update(bufPtrs,chBufPtrs,blockLen);
|
||||
for (int i=0; i<8; i++) {
|
||||
for (size_t j=0; j<blockLen; j++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=buf[i*2][j]+buf[i*2+1][j];
|
||||
}
|
||||
}
|
||||
pos+=blockLen;
|
||||
len-=blockLen;
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::tick(bool sysTick) {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=((chan[i].vol&0xff)*chan[i].std.vol.val)>>6;
|
||||
chWrite(i,0,chan[i].outVol);
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note+chan[i].std.arp.val);
|
||||
}
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
} else {
|
||||
if (chan[i].std.arp.mode && chan[i].std.arp.finished) {
|
||||
chan[i].baseFreq=NOTE_FREQUENCY(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.pitch.had) {
|
||||
if (chan[i].std.pitch.mode) {
|
||||
chan[i].pitch2+=chan[i].std.pitch.val;
|
||||
CLAMP_VAR(chan[i].pitch2,-2048,2048);
|
||||
} else {
|
||||
chan[i].pitch2=chan[i].std.pitch.val;
|
||||
}
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
// panning registers are reversed in this chip
|
||||
if (chan[i].std.panL.had) {
|
||||
chan[i].panning&=0xf0;
|
||||
chan[i].panning|=chan[i].std.panL.val&15;
|
||||
}
|
||||
if (chan[i].std.panR.had) {
|
||||
chan[i].panning&=0x0f;
|
||||
chan[i].panning|=(chan[i].std.panR.val&15)<<4;
|
||||
}
|
||||
if (chan[i].std.panL.had || chan[i].std.panR.had) {
|
||||
chWrite(i,0x05,isMuted[i]?0:chan[i].panning);
|
||||
}
|
||||
if (chan[i].setPos) {
|
||||
// force keyon
|
||||
chan[i].keyOn=true;
|
||||
chan[i].setPos=false;
|
||||
} else {
|
||||
chan[i].audPos=0;
|
||||
}
|
||||
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
|
||||
unsigned char keyon=regPool[8]&~(1<<i);
|
||||
unsigned char keyoff=keyon|(1<<i);
|
||||
DivSample* s=parent->getSample(chan[i].sample);
|
||||
double off=(s->centerRate>=1)?((double)s->centerRate/8363.0):1.0;
|
||||
chan[i].freq=(int)(off*parent->calcFreq(chan[i].baseFreq,chan[i].pitch,false,2,chan[i].pitch2,chipClock,CHIP_FREQBASE));
|
||||
if (chan[i].freq>65535) chan[i].freq=65535;
|
||||
if (chan[i].keyOn) {
|
||||
unsigned int start=s->offRF5C68;
|
||||
unsigned int loop=start+s->length8;
|
||||
if (chan[i].audPos>0) {
|
||||
start=start+MIN(chan[i].audPos,s->length8);
|
||||
}
|
||||
if (s->loopStart>=0) {
|
||||
loop=start+s->loopStart;
|
||||
}
|
||||
start=MIN(start,getSampleMemCapacity()-31);
|
||||
loop=MIN(loop,getSampleMemCapacity()-31);
|
||||
rWrite(8,keyoff); // force keyoff first
|
||||
chWrite(i,6,start>>8);
|
||||
chWrite(i,4,loop&0xff);
|
||||
chWrite(i,5,loop>>8);
|
||||
if (!chan[i].std.vol.had) {
|
||||
chan[i].outVol=chan[i].vol;
|
||||
chWrite(i,0,chan[i].outVol);
|
||||
}
|
||||
rWrite(8,keyon);
|
||||
chan[i].keyOn=false;
|
||||
}
|
||||
if (chan[i].keyOff) {
|
||||
rWrite(8,keyoff);
|
||||
chan[i].keyOff=false;
|
||||
}
|
||||
if (chan[i].freqChanged) {
|
||||
chWrite(i,2,chan[i].freq&0xff);
|
||||
chWrite(i,3,chan[i].freq>>8);
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformRF5C68::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA);
|
||||
chan[c.chan].sample=ins->amiga.getSample(c.value);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value);
|
||||
}
|
||||
if (chan[c.chan].sample<0 || chan[c.chan].sample>=parent->song.sampleLen) {
|
||||
chan[c.chan].sample=-1;
|
||||
}
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].keyOn=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].sample=-1;
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].keyOff=true;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
break;
|
||||
case DIV_CMD_NOTE_OFF_ENV:
|
||||
case DIV_CMD_ENV_RELEASE:
|
||||
chan[c.chan].std.release();
|
||||
break;
|
||||
case DIV_CMD_INSTRUMENT:
|
||||
if (chan[c.chan].ins!=c.value || c.value2==1) {
|
||||
chan[c.chan].ins=c.value;
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_VOLUME:
|
||||
if (chan[c.chan].vol!=c.value) {
|
||||
chan[c.chan].vol=c.value;
|
||||
if (!chan[c.chan].std.vol.has) {
|
||||
chan[c.chan].outVol=c.value;
|
||||
chWrite(c.chan,0,chan[c.chan].outVol);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DIV_CMD_GET_VOLUME:
|
||||
if (chan[c.chan].std.vol.has) {
|
||||
return chan[c.chan].vol;
|
||||
}
|
||||
return chan[c.chan].outVol;
|
||||
break;
|
||||
case DIV_CMD_PANNING:
|
||||
chan[c.chan].panning=(c.value>>4)|(c.value2&0xf0);
|
||||
chWrite(c.chan,1,isMuted[c.chan]?0:chan[c.chan].panning);
|
||||
break;
|
||||
case DIV_CMD_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_FREQUENCY(c.value2);
|
||||
bool return2=false;
|
||||
if (destFreq>chan[c.chan].baseFreq) {
|
||||
chan[c.chan].baseFreq+=c.value;
|
||||
if (chan[c.chan].baseFreq>=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
} else {
|
||||
chan[c.chan].baseFreq-=c.value;
|
||||
if (chan[c.chan].baseFreq<=destFreq) {
|
||||
chan[c.chan].baseFreq=destFreq;
|
||||
return2=true;
|
||||
}
|
||||
}
|
||||
chan[c.chan].freqChanged=true;
|
||||
if (return2) {
|
||||
chan[c.chan].inPorta=false;
|
||||
return 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_LEGATO: {
|
||||
chan[c.chan].baseFreq=NOTE_FREQUENCY(c.value+((chan[c.chan].std.arp.will && !chan[c.chan].std.arp.mode)?(chan[c.chan].std.arp.val-12):(0)));
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_PRE_PORTA:
|
||||
if (chan[c.chan].active && c.value2) {
|
||||
if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA));
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_SAMPLE_POS:
|
||||
chan[c.chan].audPos=c.value;
|
||||
chan[c.chan].setPos=true;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 255;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
chWrite(ch,1,mute?0:chan[ch].panning);
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::forceIns() {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
chan[i].sample=-1;
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformRF5C68::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformRF5C68::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::reset() {
|
||||
memset(regPool,0,144);
|
||||
rf5c68.device_reset();
|
||||
rWrite(0x08,0xff); // keyoff all channels
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i]=DivPlatformRF5C68::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chWrite(i,0,255);
|
||||
chWrite(i,1,isMuted[i]?0:255);
|
||||
}
|
||||
}
|
||||
|
||||
bool DivPlatformRF5C68::isStereo() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::notifyInsChange(int ins) {
|
||||
for (int i=0; i<8; i++) {
|
||||
if (chan[i].ins==ins) {
|
||||
chan[i].insChanged=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::notifyWaveChange(int wave) {
|
||||
// TODO when wavetables are added
|
||||
// TODO they probably won't be added unless the samples reside in RAM
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<8; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::setFlags(unsigned int flags) {
|
||||
switch (flags&0x0f) {
|
||||
case 1: chipClock=10000000; break;
|
||||
case 2: chipClock=12500000; break;
|
||||
default: chipClock=8000000; break;
|
||||
}
|
||||
chipType=flags>>4;
|
||||
rate=chipClock/384;
|
||||
for (int i=0; i<8; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
rf5c68=(chipType==1)?rf5c164_device():rf5c68_device();
|
||||
rf5c68.device_start(sampleMem);
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr&0x0f,val);
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr&0x0f,i.val);
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformRF5C68::getRegisterPool() {
|
||||
return regPool;
|
||||
}
|
||||
|
||||
int DivPlatformRF5C68::getRegisterPoolSize() {
|
||||
return 144;
|
||||
}
|
||||
|
||||
const void* DivPlatformRF5C68::getSampleMem(int index) {
|
||||
return index == 0 ? sampleMem : NULL;
|
||||
}
|
||||
|
||||
size_t DivPlatformRF5C68::getSampleMemCapacity(int index) {
|
||||
return index == 0 ? 65536 : 0;
|
||||
}
|
||||
|
||||
size_t DivPlatformRF5C68::getSampleMemUsage(int index) {
|
||||
return index == 0 ? sampleMemLen : 0;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::renderSamples() {
|
||||
memset(sampleMem,0,getSampleMemCapacity());
|
||||
|
||||
size_t memPos=0;
|
||||
for (int i=0; i<parent->song.sampleLen; i++) {
|
||||
DivSample* s=parent->song.sample[i];
|
||||
int length=s->length8;
|
||||
int actualLength=MIN((int)(getSampleMemCapacity()-memPos)-31,length);
|
||||
if (actualLength>0) {
|
||||
s->offRF5C68=memPos;
|
||||
for (int j=0; j<actualLength; j++) {
|
||||
// convert to signed magnitude
|
||||
signed char val=s->data8[j];
|
||||
CLAMP_VAR(val,-127,126);
|
||||
sampleMem[memPos++]=(val>0)?(val|0x80):(0-val);
|
||||
}
|
||||
// write end of sample marker
|
||||
memset(&sampleMem[memPos],0xff,31);
|
||||
memPos+=31;
|
||||
}
|
||||
if (actualLength<length) {
|
||||
logW("out of RF5C68 PCM memory for sample %d!",i);
|
||||
break;
|
||||
}
|
||||
// align memPos to 256-byte boundary
|
||||
memPos=(memPos+0xff)&~0xff;
|
||||
}
|
||||
sampleMemLen=memPos;
|
||||
}
|
||||
|
||||
int DivPlatformRF5C68::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
|
||||
for (int i=0; i<8; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
sampleMem=new unsigned char[getSampleMemCapacity()];
|
||||
sampleMemLen=0;
|
||||
setFlags(flags);
|
||||
reset();
|
||||
|
||||
return 8;
|
||||
}
|
||||
|
||||
void DivPlatformRF5C68::quit() {
|
||||
delete[] sampleMem;
|
||||
for (int i=0; i<8; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
}
|
105
src/engine/platform/rf5c68.h
Normal file
105
src/engine/platform/rf5c68.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/**
|
||||
* 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 _RF5C68_H
|
||||
#define _RF5C68_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "sound/rf5c68.h"
|
||||
|
||||
class DivPlatformRF5C68: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2;
|
||||
unsigned int audPos;
|
||||
int sample, wave, ins;
|
||||
int note;
|
||||
int panning;
|
||||
bool active, insChanged, freqChanged, keyOn, keyOff, inPorta, setPos;
|
||||
int vol, outVol;
|
||||
DivMacroInt std;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
audPos(0),
|
||||
sample(-1),
|
||||
ins(-1),
|
||||
note(0),
|
||||
panning(255),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
keyOn(false),
|
||||
keyOff(false),
|
||||
inPorta(false),
|
||||
setPos(false),
|
||||
vol(255),
|
||||
outVol(255) {}
|
||||
};
|
||||
Channel chan[8];
|
||||
DivDispatchOscBuffer* oscBuf[8];
|
||||
bool isMuted[8];
|
||||
int chipType;
|
||||
unsigned char curChan;
|
||||
|
||||
unsigned char* sampleMem;
|
||||
size_t sampleMemLen;
|
||||
rf5c68_device rf5c68;
|
||||
unsigned char regPool[144];
|
||||
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);
|
||||
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 isStereo();
|
||||
void setChipModel(int type);
|
||||
void notifyInsChange(int ins);
|
||||
void notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void setFlags(unsigned int flags);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
const void* getSampleMem(int index = 0);
|
||||
size_t getSampleMemCapacity(int index = 0);
|
||||
size_t getSampleMemUsage(int index = 0);
|
||||
void renderSamples();
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void quit();
|
||||
private:
|
||||
void chWrite(unsigned char ch, unsigned int addr, unsigned char val);
|
||||
};
|
||||
|
||||
#endif
|
255
src/engine/platform/sound/rf5c68.cpp
Normal file
255
src/engine/platform/sound/rf5c68.cpp
Normal file
|
@ -0,0 +1,255 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert,Aaron Giles
|
||||
/*********************************************************/
|
||||
/* ricoh RF5C68(or clone) PCM controller */
|
||||
/* */
|
||||
/* TODO: Verify RF5C105,164 (Sega CD/Mega CD) */
|
||||
/* differences */
|
||||
/*********************************************************/
|
||||
|
||||
#include "rf5c68.h"
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// LIVE DEVICE
|
||||
//**************************************************************************
|
||||
|
||||
//-------------------------------------------------
|
||||
// rf5c68_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
rf5c68_device::rf5c68_device(int output_bits)
|
||||
: m_cbank(0)
|
||||
, m_wbank(0)
|
||||
, m_enable(0)
|
||||
, m_output_bits(output_bits)
|
||||
, m_ext_mem(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
rf5c68_device::rf5c68_device()
|
||||
: rf5c68_device(10)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// rf5c164_device - constructor
|
||||
//-------------------------------------------------
|
||||
|
||||
rf5c164_device::rf5c164_device()
|
||||
: rf5c68_device(16)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// device_start - device-specific startup
|
||||
//-------------------------------------------------
|
||||
|
||||
void rf5c68_device::device_start(u8 *ext_mem)
|
||||
{
|
||||
m_ext_mem = ext_mem;
|
||||
}
|
||||
|
||||
void rf5c68_device::device_reset()
|
||||
{
|
||||
m_cbank = 0;
|
||||
m_wbank = 0;
|
||||
m_enable = 0;
|
||||
|
||||
for (pcm_channel &chan : m_chan) {
|
||||
chan.enable = 0;
|
||||
chan.env = 0;
|
||||
chan.pan = 0;
|
||||
chan.start = 0;
|
||||
chan.addr = 0;
|
||||
chan.step = 0;
|
||||
chan.loopst = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// sound_stream_update - handle a stream update
|
||||
//-------------------------------------------------
|
||||
|
||||
void rf5c68_device::sound_stream_update(s16 **outputs, s16 **channel_outputs, u32 samples)
|
||||
{
|
||||
/* bail if not enabled */
|
||||
if (!m_enable)
|
||||
{
|
||||
std::fill_n(outputs[0], samples, 0);
|
||||
std::fill_n(outputs[1], samples, 0);
|
||||
for (unsigned int i = 0; i < NUM_CHANNELS; i++) {
|
||||
std::fill_n(channel_outputs[i*2], samples, 0);
|
||||
std::fill_n(channel_outputs[i*2+1], samples, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mixleft.size() < samples)
|
||||
m_mixleft.resize(samples);
|
||||
if (m_mixright.size() < samples)
|
||||
m_mixright.resize(samples);
|
||||
|
||||
std::fill_n(&m_mixleft[0], samples, 0);
|
||||
std::fill_n(&m_mixright[0], samples, 0);
|
||||
|
||||
/* loop over channels */
|
||||
for (unsigned int i = 0; i < NUM_CHANNELS; i++)
|
||||
{
|
||||
pcm_channel &chan = m_chan[i];
|
||||
/* if this channel is active, accumulate samples */
|
||||
if (chan.enable)
|
||||
{
|
||||
int lv = (chan.pan & 0x0f) * chan.env;
|
||||
int rv = ((chan.pan >> 4) & 0x0f) * chan.env;
|
||||
|
||||
/* loop over the sample buffer */
|
||||
for (u32 j = 0; j < samples; j++)
|
||||
{
|
||||
int sample;
|
||||
|
||||
/* fetch the sample and handle looping */
|
||||
sample = m_ext_mem[(chan.addr >> 11) & 0xffff];
|
||||
if (sample == 0xff)
|
||||
{
|
||||
chan.addr = chan.loopst << 11;
|
||||
sample = m_ext_mem[(chan.addr >> 11) & 0xffff];
|
||||
|
||||
/* if we loop to a loop point, we're effectively dead */
|
||||
if (sample == 0xff)
|
||||
break;
|
||||
}
|
||||
chan.addr += chan.step;
|
||||
|
||||
/* add to the buffer */
|
||||
s32 left_out = ((sample & 0x7f) * lv) >> 5;
|
||||
s32 right_out = ((sample & 0x7f) * rv) >> 5;
|
||||
if ((sample & 0x80) == 0)
|
||||
{
|
||||
left_out = -left_out;
|
||||
right_out = -right_out;
|
||||
}
|
||||
channel_outputs[i*2][j] = (s16)left_out;
|
||||
channel_outputs[i*2+1][j] = (s16)right_out;
|
||||
m_mixleft[j] += left_out;
|
||||
m_mixright[j] += right_out;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fill_n(channel_outputs[i*2], samples, 0);
|
||||
std::fill_n(channel_outputs[i*2+1], samples, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
now clamp and shift the result (output is only 10 bits for RF5C68, 16 bits for RF5C164)
|
||||
reference: Mega CD hardware manual, RF5C68 datasheet
|
||||
*/
|
||||
const u8 output_shift = (m_output_bits > 16) ? 0 : (16 - m_output_bits);
|
||||
const s32 output_nandmask = (1 << output_shift) - 1;
|
||||
for (u32 j = 0; j < samples; j++)
|
||||
{
|
||||
s32 outleft = std::min(std::max(m_mixleft[j], -32768), 32767);
|
||||
s32 outright = std::min(std::max(m_mixright[j], -32768), 32767);
|
||||
outputs[0][j] = outleft & ~output_nandmask;
|
||||
outputs[1][j] = outright & ~output_nandmask;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// RF5C68 write register
|
||||
//-------------------------------------------------
|
||||
|
||||
// TODO: RF5C164 only?
|
||||
u8 rf5c68_device::rf5c68_r(offs_t offset)
|
||||
{
|
||||
u8 shift;
|
||||
|
||||
shift = (offset & 1) ? 11 + 8 : 11;
|
||||
|
||||
// printf("%08x\n",(m_chan[(offset & 0x0e) >> 1].addr));
|
||||
|
||||
return (m_chan[(offset & 0x0e) >> 1].addr) >> (shift);
|
||||
}
|
||||
|
||||
void rf5c68_device::rf5c68_w(offs_t offset, u8 data)
|
||||
{
|
||||
pcm_channel &chan = m_chan[m_cbank];
|
||||
int i;
|
||||
|
||||
/* switch off the address */
|
||||
switch (offset)
|
||||
{
|
||||
case 0x00: /* envelope */
|
||||
chan.env = data;
|
||||
break;
|
||||
|
||||
case 0x01: /* pan */
|
||||
chan.pan = data;
|
||||
break;
|
||||
|
||||
case 0x02: /* FDL */
|
||||
chan.step = (chan.step & 0xff00) | (data & 0x00ff);
|
||||
break;
|
||||
|
||||
case 0x03: /* FDH */
|
||||
chan.step = (chan.step & 0x00ff) | ((data << 8) & 0xff00);
|
||||
break;
|
||||
|
||||
case 0x04: /* LSL */
|
||||
chan.loopst = (chan.loopst & 0xff00) | (data & 0x00ff);
|
||||
break;
|
||||
|
||||
case 0x05: /* LSH */
|
||||
chan.loopst = (chan.loopst & 0x00ff) | ((data << 8) & 0xff00);
|
||||
break;
|
||||
|
||||
case 0x06: /* ST */
|
||||
chan.start = data;
|
||||
if (!chan.enable)
|
||||
chan.addr = chan.start << (8 + 11);
|
||||
break;
|
||||
|
||||
case 0x07: /* control reg */
|
||||
m_enable = (data >> 7) & 1;
|
||||
if (data & 0x40)
|
||||
m_cbank = data & 7;
|
||||
else
|
||||
m_wbank = (data & 0xf) << 12;
|
||||
break;
|
||||
|
||||
case 0x08: /* channel on/off reg */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
m_chan[i].enable = (~data >> i) & 1;
|
||||
if (!m_chan[i].enable)
|
||||
m_chan[i].addr = m_chan[i].start << (8 + 11);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// RF5C68 read memory
|
||||
//-------------------------------------------------
|
||||
|
||||
u8 rf5c68_device::rf5c68_mem_r(offs_t offset)
|
||||
{
|
||||
return m_ext_mem[m_wbank | offset];
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// RF5C68 write memory
|
||||
//-------------------------------------------------
|
||||
|
||||
void rf5c68_device::rf5c68_mem_w(offs_t offset, u8 data)
|
||||
{
|
||||
m_ext_mem[m_wbank | offset] = data;
|
||||
}
|
87
src/engine/platform/sound/rf5c68.h
Normal file
87
src/engine/platform/sound/rf5c68.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
// license:BSD-3-Clause
|
||||
// copyright-holders:Olivier Galibert,Aaron Giles
|
||||
/*********************************************************/
|
||||
/* ricoh RF5C68(or clone) PCM controller */
|
||||
/*********************************************************/
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifndef MAME_SOUND_RF5C68_H
|
||||
#define MAME_SOUND_RF5C68_H
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace rf5c68
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
typedef signed int offs_t;
|
||||
}
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
|
||||
#define RF5C68_SAMPLE_END_CB_MEMBER(_name) void _name(int channel)
|
||||
|
||||
|
||||
// ======================> rf5c68_device
|
||||
|
||||
using namespace rf5c68;
|
||||
class rf5c68_device
|
||||
{
|
||||
public:
|
||||
rf5c68_device();
|
||||
|
||||
u8 rf5c68_r(offs_t offset);
|
||||
void rf5c68_w(offs_t offset, u8 data);
|
||||
|
||||
u8 rf5c68_mem_r(offs_t offset);
|
||||
void rf5c68_mem_w(offs_t offset, u8 data);
|
||||
|
||||
void device_start(u8 *ext_mem);
|
||||
void device_reset();
|
||||
|
||||
void sound_stream_update(s16 **outputs, s16 **channel_outputs, u32 samples);
|
||||
|
||||
protected:
|
||||
rf5c68_device(int output_bits);
|
||||
|
||||
private:
|
||||
static constexpr unsigned NUM_CHANNELS = 8;
|
||||
|
||||
struct pcm_channel
|
||||
{
|
||||
pcm_channel() { }
|
||||
|
||||
u8 enable;
|
||||
u8 env;
|
||||
u8 pan;
|
||||
u8 start;
|
||||
u32 addr;
|
||||
u16 step;
|
||||
u16 loopst;
|
||||
};
|
||||
|
||||
pcm_channel m_chan[NUM_CHANNELS];
|
||||
u8 m_cbank;
|
||||
u16 m_wbank;
|
||||
u8 m_enable;
|
||||
int m_output_bits;
|
||||
std::vector<s32> m_mixleft;
|
||||
std::vector<s32> m_mixright;
|
||||
u8 *m_ext_mem;
|
||||
};
|
||||
|
||||
class rf5c164_device : public rf5c68_device
|
||||
{
|
||||
public:
|
||||
rf5c164_device();
|
||||
};
|
||||
|
||||
#endif // MAME_SOUND_RF5C68_H
|
|
@ -86,7 +86,7 @@ struct DivSample {
|
|||
|
||||
unsigned int length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthBRR, lengthVOX;
|
||||
unsigned int off8, off16, off1, offDPCM, offZ, offQSoundA, offA, offB, offBRR, offVOX;
|
||||
unsigned int offSegaPCM, offQSound, offX1_010, offSU, offYMZ280B;
|
||||
unsigned int offSegaPCM, offQSound, offX1_010, offSU, offYMZ280B, offRF5C68;
|
||||
|
||||
unsigned int samples;
|
||||
|
||||
|
@ -248,6 +248,7 @@ struct DivSample {
|
|||
offQSound(0),
|
||||
offX1_010(0),
|
||||
offSU(0),
|
||||
offRF5C68(0),
|
||||
samples(0) {}
|
||||
~DivSample();
|
||||
};
|
||||
|
|
|
@ -1401,7 +1401,7 @@ void DivEngine::registerSystems() {
|
|||
);
|
||||
|
||||
sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef(
|
||||
"Ricoh RF5C68", NULL, 0x95, 0, 8, false, true, 0, false,
|
||||
"Ricoh RF5C68", NULL, 0x95, 0, 8, false, true, 0x151, false,
|
||||
"this is like SNES' sound chip but without interpolation and the rest of nice bits.",
|
||||
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
|
||||
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
|
||||
|
|
|
@ -29,6 +29,7 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
unsigned char baseAddr2=isSecond?0x80:0;
|
||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||
unsigned char smsAddr=isSecond?0x30:0x50;
|
||||
unsigned char rf5c68Addr=isSecond?0xb1:0xb0;
|
||||
if (write.addr==0xffffffff) { // Furnace fake reset
|
||||
switch (sys) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
|
@ -480,6 +481,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(0);
|
||||
w->writeC(0);
|
||||
break;
|
||||
case DIV_SYSTEM_RF5C68:
|
||||
w->writeC(rf5c68Addr);
|
||||
w->writeC(7);
|
||||
w->writeC(0);
|
||||
w->writeC(rf5c68Addr);
|
||||
w->writeC(8);
|
||||
w->writeC(0xff);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -766,6 +774,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
|||
w->writeC(write.addr&0xff);
|
||||
w->writeC(write.val&0xff);
|
||||
break;
|
||||
case DIV_SYSTEM_RF5C68:
|
||||
w->writeC(rf5c68Addr);
|
||||
w->writeC(write.addr&0xff);
|
||||
w->writeC(write.val);
|
||||
break;
|
||||
default:
|
||||
logW("write not handled!");
|
||||
break;
|
||||
|
@ -888,6 +901,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
|||
DivDispatch* writeX1010[2]={NULL,NULL};
|
||||
DivDispatch* writeQSound[2]={NULL,NULL};
|
||||
DivDispatch* writeZ280[2]={NULL,NULL};
|
||||
DivDispatch* writeRF5C68[2]={NULL,NULL};
|
||||
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
willExport[i]=false;
|
||||
|
@ -1262,6 +1276,24 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
|||
howManyChips++;
|
||||
}
|
||||
break;
|
||||
case DIV_SYSTEM_RF5C68:
|
||||
// here's the dumb part: VGM thinks RF5C68 and RF5C164 are different
|
||||
// chips even though the only difference is the output resolution
|
||||
// these system types are currently handled by reusing isSecond flag
|
||||
// also this system is not dual-able
|
||||
if ((song.systemFlags[i]>>4)==1) {
|
||||
if (!hasRFC1) {
|
||||
hasRFC1=disCont[i].dispatch->chipClock;
|
||||
isSecond[i]=true;
|
||||
willExport[i]=true;
|
||||
writeRF5C68[1]=disCont[i].dispatch;
|
||||
}
|
||||
} else if (!hasRFC) {
|
||||
hasRFC=disCont[i].dispatch->chipClock;
|
||||
willExport[i]=true;
|
||||
writeRF5C68[0]=disCont[i].dispatch;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1598,6 +1630,18 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
|||
}
|
||||
}
|
||||
|
||||
for (int i=0; i<2; i++) {
|
||||
if (writeRF5C68[i]!=NULL && writeRF5C68[i]->getSampleMemUsage()>0) {
|
||||
w->writeC(0x67);
|
||||
w->writeC(0x66);
|
||||
w->writeC(0xc0+i);
|
||||
w->writeI(writeRF5C68[i]->getSampleMemUsage()+8);
|
||||
w->writeI(writeRF5C68[i]->getSampleMemCapacity());
|
||||
w->writeI(0);
|
||||
w->write(writeRF5C68[i]->getSampleMem(),writeRF5C68[i]->getSampleMemUsage());
|
||||
}
|
||||
}
|
||||
|
||||
// initialize streams
|
||||
int streamID=0;
|
||||
for (int i=0; i<song.systemLen; i++) {
|
||||
|
|
|
@ -886,6 +886,7 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_NAMCO,
|
||||
DIV_SYSTEM_NAMCO_15XX,
|
||||
DIV_SYSTEM_NAMCO_CUS30,
|
||||
DIV_SYSTEM_RF5C68,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
|
@ -266,6 +266,12 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Ricoh RF5C68", {
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
sysCategories.push_back(cat);
|
||||
|
||||
cat=FurnaceGUISysCategory("Wavetable","chips which use user-specified waveforms to generate sound.");
|
||||
|
@ -405,6 +411,22 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Sega Genesis (with Sega CD)", {
|
||||
DIV_SYSTEM_YM2612, 64, 0, 0,
|
||||
DIV_SYSTEM_SMS, 24, 0, 0,
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 18,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Sega Genesis (extended channel 3 with Sega CD)", {
|
||||
DIV_SYSTEM_YM2612_EXT, 64, 0, 0,
|
||||
DIV_SYSTEM_SMS, 24, 0, 0,
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 18,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Sega Master System", {
|
||||
DIV_SYSTEM_SMS, 64, 0, 0,
|
||||
|
@ -853,6 +875,13 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));*/
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"FM Towns", {
|
||||
DIV_SYSTEM_YM2612, 64, 0, 2,
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Commander X16", {
|
||||
DIV_SYSTEM_VERA, 64, 0, 0,
|
||||
|
@ -884,6 +913,22 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Sega System 18", {
|
||||
DIV_SYSTEM_YM2612, 64, 0, 2,
|
||||
DIV_SYSTEM_YM2612, 64, 0, 2,
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 1,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Sega System 32", {
|
||||
DIV_SYSTEM_YM2612, 64, 0, 4,
|
||||
DIV_SYSTEM_YM2612, 64, 0, 4,
|
||||
DIV_SYSTEM_RF5C68, 64, 0, 2,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"Neo Geo MVS", {
|
||||
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
|
||||
|
|
|
@ -26,18 +26,21 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
switch (type) {
|
||||
case DIV_SYSTEM_YM2612:
|
||||
case DIV_SYSTEM_YM2612_EXT: {
|
||||
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&3)==0)) {
|
||||
if (ImGui::RadioButton("NTSC (7.67MHz)",(flags&7)==0)) {
|
||||
copyOfFlags=(flags&0x80000000)|0;
|
||||
}
|
||||
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&3)==1)) {
|
||||
if (ImGui::RadioButton("PAL (7.61MHz)",(flags&7)==1)) {
|
||||
copyOfFlags=(flags&0x80000000)|1;
|
||||
}
|
||||
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&3)==2)) {
|
||||
if (ImGui::RadioButton("FM Towns (8MHz)",(flags&7)==2)) {
|
||||
copyOfFlags=(flags&0x80000000)|2;
|
||||
}
|
||||
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&3)==3)) {
|
||||
if (ImGui::RadioButton("AtGames Genesis (6.13MHz)",(flags&7)==3)) {
|
||||
copyOfFlags=(flags&0x80000000)|3;
|
||||
}
|
||||
if (ImGui::RadioButton("Sega System 32 (8.05MHz)",(flags&7)==4)) {
|
||||
copyOfFlags=(flags&0x80000000)|4;
|
||||
}
|
||||
bool ladder=flags&0x80000000;
|
||||
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
|
||||
copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0);
|
||||
|
@ -385,6 +388,31 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
}
|
||||
break;
|
||||
}
|
||||
case DIV_SYSTEM_RF5C68: {
|
||||
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;
|
||||
}
|
||||
case DIV_SYSTEM_GB:
|
||||
case DIV_SYSTEM_SWAN:
|
||||
case DIV_SYSTEM_VERA:
|
||||
|
|
Loading…
Reference in a new issue