mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-27 23:13: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/ymz280b.cpp
|
||||||
|
|
||||||
|
src/engine/platform/sound/rf5c68.cpp
|
||||||
|
|
||||||
src/engine/platform/oplAInterface.cpp
|
src/engine/platform/oplAInterface.cpp
|
||||||
src/engine/platform/ym2608Interface.cpp
|
src/engine/platform/ym2608Interface.cpp
|
||||||
src/engine/platform/ym2610Interface.cpp
|
src/engine/platform/ym2610Interface.cpp
|
||||||
|
@ -388,6 +390,7 @@ src/engine/platform/vrc6.cpp
|
||||||
src/engine/platform/scc.cpp
|
src/engine/platform/scc.cpp
|
||||||
src/engine/platform/ymz280b.cpp
|
src/engine/platform/ymz280b.cpp
|
||||||
src/engine/platform/namcowsg.cpp
|
src/engine/platform/namcowsg.cpp
|
||||||
|
src/engine/platform/rf5c68.cpp
|
||||||
src/engine/platform/dummy.cpp
|
src/engine/platform/dummy.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
1
TODO.md
1
TODO.md
|
@ -1,6 +1,5 @@
|
||||||
# to-do for 0.6pre1
|
# to-do for 0.6pre1
|
||||||
|
|
||||||
- RF5C68 system
|
|
||||||
- ADPCM chips
|
- ADPCM chips
|
||||||
- Game Boy envelope macro/sequence
|
- Game Boy envelope macro/sequence
|
||||||
- drag-and-drop ins/wave/sample loading
|
- drag-and-drop ins/wave/sample loading
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "platform/mmc5.h"
|
#include "platform/mmc5.h"
|
||||||
#include "platform/scc.h"
|
#include "platform/scc.h"
|
||||||
#include "platform/ymz280b.h"
|
#include "platform/ymz280b.h"
|
||||||
|
#include "platform/rf5c68.h"
|
||||||
#include "platform/dummy.h"
|
#include "platform/dummy.h"
|
||||||
#include "../ta-log.h"
|
#include "../ta-log.h"
|
||||||
#include "platform/zxbeeper.h"
|
#include "platform/zxbeeper.h"
|
||||||
|
@ -356,6 +357,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
||||||
dispatch=new DivPlatformYMZ280B;
|
dispatch=new DivPlatformYMZ280B;
|
||||||
((DivPlatformYMZ280B*)dispatch)->setChipModel(280);
|
((DivPlatformYMZ280B*)dispatch)->setChipModel(280);
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_RF5C68:
|
||||||
|
dispatch=new DivPlatformRF5C68;
|
||||||
|
break;
|
||||||
case DIV_SYSTEM_SOUND_UNIT:
|
case DIV_SYSTEM_SOUND_UNIT:
|
||||||
dispatch=new DivPlatformSoundUnit;
|
dispatch=new DivPlatformSoundUnit;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1150,14 +1150,12 @@ void DivPlatformGenesis::setYMFM(bool use) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformGenesis::setFlags(unsigned int flags) {
|
void DivPlatformGenesis::setFlags(unsigned int flags) {
|
||||||
if (flags==3) {
|
switch (flags) {
|
||||||
chipClock=COLOR_NTSC*12.0/7.0;
|
case 1: chipClock=COLOR_PAL*12.0/7.0; break;
|
||||||
} else if (flags==2) {
|
case 2: chipClock=8000000.0; break;
|
||||||
chipClock=8000000.0;
|
case 3: chipClock=COLOR_NTSC*12.0/7.0; break;
|
||||||
} else if (flags==1) {
|
case 4: chipClock=COLOR_NTSC*9.0/4.0; break;
|
||||||
chipClock=COLOR_PAL*12.0/7.0;
|
default: chipClock=COLOR_NTSC*15.0/7.0; break;
|
||||||
} else {
|
|
||||||
chipClock=COLOR_NTSC*15.0/7.0;
|
|
||||||
}
|
}
|
||||||
ladder=flags&0x80000000;
|
ladder=flags&0x80000000;
|
||||||
OPN2_SetChipType(ladder?ym3438_mode_ym2612:0);
|
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 length8, length16, length1, lengthDPCM, lengthZ, lengthQSoundA, lengthA, lengthB, lengthBRR, lengthVOX;
|
||||||
unsigned int off8, off16, off1, offDPCM, offZ, offQSoundA, offA, offB, offBRR, offVOX;
|
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;
|
unsigned int samples;
|
||||||
|
|
||||||
|
@ -248,6 +248,7 @@ struct DivSample {
|
||||||
offQSound(0),
|
offQSound(0),
|
||||||
offX1_010(0),
|
offX1_010(0),
|
||||||
offSU(0),
|
offSU(0),
|
||||||
|
offRF5C68(0),
|
||||||
samples(0) {}
|
samples(0) {}
|
||||||
~DivSample();
|
~DivSample();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1401,7 +1401,7 @@ void DivEngine::registerSystems() {
|
||||||
);
|
);
|
||||||
|
|
||||||
sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef(
|
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.",
|
"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"},
|
{"Channel 1", "Channel 2", "Channel 3", "Channel 4", "Channel 5", "Channel 6", "Channel 7", "Channel 8"},
|
||||||
{"CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", "CH8"},
|
{"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 char baseAddr2=isSecond?0x80:0;
|
||||||
unsigned short baseAddr2S=isSecond?0x8000:0;
|
unsigned short baseAddr2S=isSecond?0x8000:0;
|
||||||
unsigned char smsAddr=isSecond?0x30:0x50;
|
unsigned char smsAddr=isSecond?0x30:0x50;
|
||||||
|
unsigned char rf5c68Addr=isSecond?0xb1:0xb0;
|
||||||
if (write.addr==0xffffffff) { // Furnace fake reset
|
if (write.addr==0xffffffff) { // Furnace fake reset
|
||||||
switch (sys) {
|
switch (sys) {
|
||||||
case DIV_SYSTEM_YM2612:
|
case DIV_SYSTEM_YM2612:
|
||||||
|
@ -480,6 +481,13 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
w->writeC(0);
|
w->writeC(0);
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_RF5C68:
|
||||||
|
w->writeC(rf5c68Addr);
|
||||||
|
w->writeC(7);
|
||||||
|
w->writeC(0);
|
||||||
|
w->writeC(rf5c68Addr);
|
||||||
|
w->writeC(8);
|
||||||
|
w->writeC(0xff);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -766,6 +774,11 @@ void DivEngine::performVGMWrite(SafeWriter* w, DivSystem sys, DivRegWrite& write
|
||||||
w->writeC(write.addr&0xff);
|
w->writeC(write.addr&0xff);
|
||||||
w->writeC(write.val&0xff);
|
w->writeC(write.val&0xff);
|
||||||
break;
|
break;
|
||||||
|
case DIV_SYSTEM_RF5C68:
|
||||||
|
w->writeC(rf5c68Addr);
|
||||||
|
w->writeC(write.addr&0xff);
|
||||||
|
w->writeC(write.val);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logW("write not handled!");
|
logW("write not handled!");
|
||||||
break;
|
break;
|
||||||
|
@ -888,6 +901,7 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
||||||
DivDispatch* writeX1010[2]={NULL,NULL};
|
DivDispatch* writeX1010[2]={NULL,NULL};
|
||||||
DivDispatch* writeQSound[2]={NULL,NULL};
|
DivDispatch* writeQSound[2]={NULL,NULL};
|
||||||
DivDispatch* writeZ280[2]={NULL,NULL};
|
DivDispatch* writeZ280[2]={NULL,NULL};
|
||||||
|
DivDispatch* writeRF5C68[2]={NULL,NULL};
|
||||||
|
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
willExport[i]=false;
|
willExport[i]=false;
|
||||||
|
@ -1262,6 +1276,24 @@ SafeWriter* DivEngine::saveVGM(bool* sysToExport, bool loop, int version) {
|
||||||
howManyChips++;
|
howManyChips++;
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
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
|
// initialize streams
|
||||||
int streamID=0;
|
int streamID=0;
|
||||||
for (int i=0; i<song.systemLen; i++) {
|
for (int i=0; i<song.systemLen; i++) {
|
||||||
|
|
|
@ -886,6 +886,7 @@ const int availableSystems[]={
|
||||||
DIV_SYSTEM_NAMCO,
|
DIV_SYSTEM_NAMCO,
|
||||||
DIV_SYSTEM_NAMCO_15XX,
|
DIV_SYSTEM_NAMCO_15XX,
|
||||||
DIV_SYSTEM_NAMCO_CUS30,
|
DIV_SYSTEM_NAMCO_CUS30,
|
||||||
|
DIV_SYSTEM_RF5C68,
|
||||||
0 // don't remove this last one!
|
0 // don't remove this last one!
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -266,6 +266,12 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
cat.systems.push_back(FurnaceGUISysDef(
|
||||||
|
"Ricoh RF5C68", {
|
||||||
|
DIV_SYSTEM_RF5C68, 64, 0, 0,
|
||||||
|
0
|
||||||
|
}
|
||||||
|
));
|
||||||
sysCategories.push_back(cat);
|
sysCategories.push_back(cat);
|
||||||
|
|
||||||
cat=FurnaceGUISysCategory("Wavetable","chips which use user-specified waveforms to generate sound.");
|
cat=FurnaceGUISysCategory("Wavetable","chips which use user-specified waveforms to generate sound.");
|
||||||
|
@ -405,6 +411,22 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
0
|
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(
|
cat.systems.push_back(FurnaceGUISysDef(
|
||||||
"Sega Master System", {
|
"Sega Master System", {
|
||||||
DIV_SYSTEM_SMS, 64, 0, 0,
|
DIV_SYSTEM_SMS, 64, 0, 0,
|
||||||
|
@ -853,6 +875,13 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
0
|
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(
|
cat.systems.push_back(FurnaceGUISysDef(
|
||||||
"Commander X16", {
|
"Commander X16", {
|
||||||
DIV_SYSTEM_VERA, 64, 0, 0,
|
DIV_SYSTEM_VERA, 64, 0, 0,
|
||||||
|
@ -884,6 +913,22 @@ void FurnaceGUI::initSystemPresets() {
|
||||||
0
|
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(
|
cat.systems.push_back(FurnaceGUISysDef(
|
||||||
"Neo Geo MVS", {
|
"Neo Geo MVS", {
|
||||||
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
|
DIV_SYSTEM_YM2610_FULL, 64, 0, 0,
|
||||||
|
|
|
@ -26,18 +26,21 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DIV_SYSTEM_YM2612:
|
case DIV_SYSTEM_YM2612:
|
||||||
case DIV_SYSTEM_YM2612_EXT: {
|
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;
|
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;
|
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;
|
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;
|
copyOfFlags=(flags&0x80000000)|3;
|
||||||
}
|
}
|
||||||
|
if (ImGui::RadioButton("Sega System 32 (8.05MHz)",(flags&7)==4)) {
|
||||||
|
copyOfFlags=(flags&0x80000000)|4;
|
||||||
|
}
|
||||||
bool ladder=flags&0x80000000;
|
bool ladder=flags&0x80000000;
|
||||||
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
|
if (ImGui::Checkbox("Enable DAC distortion",&ladder)) {
|
||||||
copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0);
|
copyOfFlags=(flags&(~0x80000000))|(ladder?0x80000000:0);
|
||||||
|
@ -385,6 +388,31 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
||||||
}
|
}
|
||||||
break;
|
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_GB:
|
||||||
case DIV_SYSTEM_SWAN:
|
case DIV_SYSTEM_SWAN:
|
||||||
case DIV_SYSTEM_VERA:
|
case DIV_SYSTEM_VERA:
|
||||||
|
|
Loading…
Reference in a new issue