mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-05 04:15:05 +00:00
commit
8eb4fe365b
10 changed files with 1257 additions and 1 deletions
|
@ -310,6 +310,8 @@ src/engine/platform/sound/vic20sound.c
|
|||
|
||||
src/engine/platform/sound/vrcvi/vrcvi.cpp
|
||||
|
||||
src/engine/platform/sound/scc/scc.cpp
|
||||
|
||||
src/engine/platform/ym2610Interface.cpp
|
||||
|
||||
src/engine/blip_buf.c
|
||||
|
@ -367,6 +369,7 @@ src/engine/platform/n163.cpp
|
|||
src/engine/platform/pet.cpp
|
||||
src/engine/platform/vic20.cpp
|
||||
src/engine/platform/vrc6.cpp
|
||||
src/engine/platform/scc.cpp
|
||||
src/engine/platform/dummy.cpp
|
||||
)
|
||||
|
||||
|
|
1
TODO.md
1
TODO.md
|
@ -9,7 +9,6 @@
|
|||
- OPNA system
|
||||
- ZX beeper system
|
||||
- Y8950 system
|
||||
- SCC/SCC+ system
|
||||
- maybe YMU759 ADPCM channel
|
||||
- ADPCM chips
|
||||
- Game Boy envelope macro/sequence
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "platform/vrc6.h"
|
||||
#include "platform/fds.h"
|
||||
#include "platform/mmc5.h"
|
||||
#include "platform/scc.h"
|
||||
#include "platform/dummy.h"
|
||||
#include "../ta-log.h"
|
||||
#include "song.h"
|
||||
|
@ -314,6 +315,14 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
|||
case DIV_SYSTEM_MMC5:
|
||||
dispatch=new DivPlatformMMC5;
|
||||
break;
|
||||
case DIV_SYSTEM_SCC:
|
||||
dispatch=new DivPlatformSCC;
|
||||
((DivPlatformSCC*)dispatch)->setChipModel(false);
|
||||
break;
|
||||
case DIV_SYSTEM_SCC_PLUS:
|
||||
dispatch=new DivPlatformSCC;
|
||||
((DivPlatformSCC*)dispatch)->setChipModel(true);
|
||||
break;
|
||||
case DIV_SYSTEM_SOUND_UNIT:
|
||||
dispatch=new DivPlatformSoundUnit;
|
||||
break;
|
||||
|
|
387
src/engine/platform/scc.cpp
Normal file
387
src/engine/platform/scc.cpp
Normal file
|
@ -0,0 +1,387 @@
|
|||
/**
|
||||
* 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 "scc.h"
|
||||
#include "../engine.h"
|
||||
#include <math.h>
|
||||
|
||||
#define CHIP_DIVIDER 32
|
||||
|
||||
#define rWrite(a,v) {if(!skipRegisterWrites) {scc->scc_w(true,a,v); regPool[a]=v; if(dumpWrites) addWrite(a,v); }}
|
||||
|
||||
const char* regCheatSheetSCC[]={
|
||||
"Ch1_Wave", "00",
|
||||
"Ch2_Wave", "20",
|
||||
"Ch3_Wave", "40",
|
||||
"Ch4_5_Wave", "60",
|
||||
"Ch1_FreqL", "80",
|
||||
"Ch1_FreqH", "81",
|
||||
"Ch2_FreqL", "82",
|
||||
"Ch2_FreqH", "83",
|
||||
"Ch3_FreqL", "84",
|
||||
"Ch3_FreqH", "85",
|
||||
"Ch4_FreqL", "86",
|
||||
"Ch4_FreqH", "87",
|
||||
"Ch5_FreqL", "88",
|
||||
"Ch5_FreqH", "89",
|
||||
"Ch1_Vol", "8a",
|
||||
"Ch2_Vol", "8b",
|
||||
"Ch3_Vol", "8c",
|
||||
"Ch4_Vol", "8d",
|
||||
"Ch5_Vol", "8e",
|
||||
"Output", "8f",
|
||||
"Test", "e0",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char* regCheatSheetSCCPlus[]={
|
||||
"Ch1_Wave", "00",
|
||||
"Ch2_Wave", "20",
|
||||
"Ch3_Wave", "40",
|
||||
"Ch4_Wave", "60",
|
||||
"Ch5_Wave", "80",
|
||||
"Ch1_FreqL", "a0",
|
||||
"Ch1_FreqH", "a1",
|
||||
"Ch2_FreqL", "a2",
|
||||
"Ch2_FreqH", "a3",
|
||||
"Ch3_FreqL", "a4",
|
||||
"Ch3_FreqH", "a5",
|
||||
"Ch4_FreqL", "a6",
|
||||
"Ch4_FreqH", "a7",
|
||||
"Ch5_FreqL", "a8",
|
||||
"Ch5_FreqH", "a9",
|
||||
"Ch1_Vol", "aa",
|
||||
"Ch2_Vol", "ab",
|
||||
"Ch3_Vol", "ac",
|
||||
"Ch4_Vol", "ad",
|
||||
"Ch5_Vol", "ae",
|
||||
"Output", "af",
|
||||
"Test", "c0",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char** DivPlatformSCC::getRegisterSheet() {
|
||||
return isPlus ? regCheatSheetSCCPlus : regCheatSheetSCC;
|
||||
}
|
||||
|
||||
const char* DivPlatformSCC::getEffectName(unsigned char effect) {
|
||||
switch (effect) {
|
||||
case 0x10:
|
||||
return "10xx: Change waveform";
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||
for (size_t h=start; h<start+len; h++) {
|
||||
for (int i=0; i<16; i++) {
|
||||
scc->tick();
|
||||
}
|
||||
short out=(short)scc->out()<<5;
|
||||
bufL[h]=bufR[h]=out;
|
||||
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->data[oscBuf[i]->needle++]=scc->chan_out(i)<<7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::updateWave(int ch) {
|
||||
int dstCh=(!isPlus && ch>=4)?3:ch;
|
||||
for (int i=0; i<32; i++) {
|
||||
rWrite(dstCh*32+i,(unsigned char)chan[ch].ws.output[i]-128);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::tick(bool sysTick) {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].std.next();
|
||||
if (chan[i].std.vol.had) {
|
||||
chan[i].outVol=((chan[i].vol&15)*MIN(15,chan[i].std.vol.val))/15;
|
||||
rWrite(regBase+10+i,chan[i].outVol);
|
||||
}
|
||||
if (chan[i].std.arp.had) {
|
||||
if (!chan[i].inPorta) {
|
||||
if (chan[i].std.arp.mode) {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(chan[i].std.arp.val);
|
||||
} else {
|
||||
chan[i].baseFreq=NOTE_PERIODIC(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_PERIODIC(chan[i].note);
|
||||
chan[i].freqChanged=true;
|
||||
}
|
||||
}
|
||||
if (chan[i].std.wave.had) {
|
||||
if (chan[i].wave!=chan[i].std.wave.val || chan[i].ws.activeChanged()) {
|
||||
chan[i].wave=chan[i].std.wave.val;
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (chan[i].active) {
|
||||
if (chan[i].ws.tick()) {
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
if (chan[i].freqChanged) {
|
||||
chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,true)+chan[i].pitch2;
|
||||
if (chan[i].freq<1) chan[i].freq=1;
|
||||
if (chan[i].freq>4096) chan[i].freq=4096;
|
||||
rWrite(regBase+0+i*2,(chan[i].freq-1)&0xff);
|
||||
rWrite(regBase+1+i*2,(chan[i].freq-1)>>8);
|
||||
chan[i].freqChanged=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DivPlatformSCC::dispatch(DivCommand c) {
|
||||
switch (c.cmd) {
|
||||
case DIV_CMD_NOTE_ON: {
|
||||
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_SCC);
|
||||
if (c.value!=DIV_NOTE_NULL) {
|
||||
chan[c.chan].baseFreq=NOTE_PERIODIC(c.value);
|
||||
chan[c.chan].freqChanged=true;
|
||||
chan[c.chan].note=c.value;
|
||||
}
|
||||
chan[c.chan].active=true;
|
||||
chan[c.chan].macroInit(ins);
|
||||
if (!isMuted[c.chan]) {
|
||||
rWrite(regBase+15,regPool[regBase+15]|(1<<c.chan));
|
||||
}
|
||||
if (chan[c.chan].wave<0) {
|
||||
chan[c.chan].wave=0;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
}
|
||||
chan[c.chan].ws.init(ins,32,255,chan[c.chan].insChanged);
|
||||
chan[c.chan].insChanged=false;
|
||||
break;
|
||||
}
|
||||
case DIV_CMD_NOTE_OFF:
|
||||
chan[c.chan].active=false;
|
||||
chan[c.chan].macroInit(NULL);
|
||||
rWrite(regBase+15,regPool[regBase+15]&~(1<<c.chan));
|
||||
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;
|
||||
rWrite(regBase+10+c.chan,c.value);
|
||||
}
|
||||
}
|
||||
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_PITCH:
|
||||
chan[c.chan].pitch=c.value;
|
||||
chan[c.chan].freqChanged=true;
|
||||
break;
|
||||
case DIV_CMD_WAVE:
|
||||
chan[c.chan].wave=c.value;
|
||||
chan[c.chan].ws.changeWave1(chan[c.chan].wave);
|
||||
break;
|
||||
case DIV_CMD_NOTE_PORTA: {
|
||||
int destFreq=NOTE_PERIODIC(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_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;
|
||||
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_SCC));
|
||||
}
|
||||
chan[c.chan].inPorta=c.value;
|
||||
break;
|
||||
case DIV_CMD_GET_VOLMAX:
|
||||
return 15;
|
||||
break;
|
||||
case DIV_ALWAYS_SET_VOLUME:
|
||||
return 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::muteChannel(int ch, bool mute) {
|
||||
isMuted[ch]=mute;
|
||||
if (mute) {
|
||||
rWrite(regBase+15,regPool[regBase+15]&~(1<<ch));
|
||||
} else if (chan[ch].active) {
|
||||
rWrite(regBase+15,regPool[regBase+15]|(1<<ch));
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::forceIns() {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].insChanged=true;
|
||||
chan[i].freqChanged=true;
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
|
||||
void* DivPlatformSCC::getChanState(int ch) {
|
||||
return &chan[ch];
|
||||
}
|
||||
|
||||
DivDispatchOscBuffer* DivPlatformSCC::getOscBuffer(int ch) {
|
||||
return oscBuf[ch];
|
||||
}
|
||||
|
||||
unsigned char* DivPlatformSCC::getRegisterPool() {
|
||||
return (unsigned char*)regPool;
|
||||
}
|
||||
|
||||
int DivPlatformSCC::getRegisterPoolSize() {
|
||||
return 225;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::reset() {
|
||||
memset(regPool,0,225);
|
||||
scc->reset();
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i]=DivPlatformSCC::Channel();
|
||||
chan[i].std.setEngine(parent);
|
||||
chan[i].ws.setEngine(parent);
|
||||
chan[i].ws.init(NULL,32,255,false);
|
||||
chan[i].vol=15;
|
||||
chan[i].outVol=15;
|
||||
rWrite(regBase+10+i,15);
|
||||
}
|
||||
if (dumpWrites) {
|
||||
addWrite(0xffffffff,0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DivPlatformSCC::isStereo() {
|
||||
return false;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::notifyWaveChange(int wave) {
|
||||
for (int i=0; i<5; i++) {
|
||||
if (chan[i].wave==wave) {
|
||||
chan[i].ws.changeWave1(chan[i].wave);
|
||||
updateWave(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::notifyInsDeletion(void* ins) {
|
||||
for (int i=0; i<5; i++) {
|
||||
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
|
||||
}
|
||||
}
|
||||
|
||||
void DivPlatformSCC::poke(unsigned int addr, unsigned short val) {
|
||||
rWrite(addr,val);
|
||||
}
|
||||
|
||||
void DivPlatformSCC::poke(std::vector<DivRegWrite>& wlist) {
|
||||
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
|
||||
}
|
||||
|
||||
void DivPlatformSCC::setChipModel(bool isplus) {
|
||||
isPlus=isplus;
|
||||
}
|
||||
|
||||
int DivPlatformSCC::init(DivEngine* p, int channels, int sugRate, unsigned int flags) {
|
||||
parent=p;
|
||||
dumpWrites=false;
|
||||
skipRegisterWrites=false;
|
||||
writeOscBuf=0;
|
||||
for (int i=0; i<5; i++) {
|
||||
isMuted[i]=false;
|
||||
oscBuf[i]=new DivDispatchOscBuffer;
|
||||
}
|
||||
chipClock=COLOR_NTSC;
|
||||
rate=chipClock/16;
|
||||
for (int i=0; i<5; i++) {
|
||||
oscBuf[i]->rate=rate;
|
||||
}
|
||||
if (isPlus) {
|
||||
scc=new k052539_scc_core;
|
||||
regBase=0xa0;
|
||||
} else {
|
||||
scc=new k051649_scc_core;
|
||||
regBase=0x80;
|
||||
}
|
||||
reset();
|
||||
return 5;
|
||||
}
|
||||
|
||||
void DivPlatformSCC::quit() {
|
||||
for (int i=0; i<5; i++) {
|
||||
delete oscBuf[i];
|
||||
}
|
||||
if (scc!=NULL) {
|
||||
delete scc;
|
||||
}
|
||||
}
|
||||
|
||||
DivPlatformSCC::~DivPlatformSCC() {
|
||||
}
|
91
src/engine/platform/scc.h
Normal file
91
src/engine/platform/scc.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* 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 _SCC_H
|
||||
#define _SCC_H
|
||||
|
||||
#include "../dispatch.h"
|
||||
#include <queue>
|
||||
#include "../macroInt.h"
|
||||
#include "../waveSynth.h"
|
||||
#include "sound/scc/scc.hpp"
|
||||
|
||||
class DivPlatformSCC: public DivDispatch {
|
||||
struct Channel {
|
||||
int freq, baseFreq, pitch, pitch2, note, ins;
|
||||
bool active, insChanged, freqChanged, inPorta;
|
||||
signed char vol, outVol, wave;
|
||||
signed char waveROM[32] = {0}; // 4 bit PROM per channel on bubble system
|
||||
DivMacroInt std;
|
||||
DivWaveSynth ws;
|
||||
void macroInit(DivInstrument* which) {
|
||||
std.init(which);
|
||||
pitch2=0;
|
||||
}
|
||||
Channel():
|
||||
freq(0),
|
||||
baseFreq(0),
|
||||
pitch(0),
|
||||
pitch2(0),
|
||||
note(0),
|
||||
ins(-1),
|
||||
active(false),
|
||||
insChanged(true),
|
||||
freqChanged(false),
|
||||
inPorta(false),
|
||||
vol(15),
|
||||
outVol(15),
|
||||
wave(-1) {}
|
||||
};
|
||||
Channel chan[5];
|
||||
DivDispatchOscBuffer* oscBuf[5];
|
||||
bool isMuted[5];
|
||||
unsigned char writeOscBuf;
|
||||
|
||||
scc_core* scc;
|
||||
bool isPlus;
|
||||
unsigned char regBase;
|
||||
unsigned char regPool[225];
|
||||
void updateWave(int ch);
|
||||
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 notifyWaveChange(int wave);
|
||||
void notifyInsDeletion(void* ins);
|
||||
void poke(unsigned int addr, unsigned short val);
|
||||
void poke(std::vector<DivRegWrite>& wlist);
|
||||
const char** getRegisterSheet();
|
||||
const char* getEffectName(unsigned char effect);
|
||||
int init(DivEngine* parent, int channels, int sugRate, unsigned int flags);
|
||||
void setChipModel(bool isPlus);
|
||||
void quit();
|
||||
~DivPlatformSCC();
|
||||
};
|
||||
|
||||
#endif
|
612
src/engine/platform/sound/scc/scc.cpp
Normal file
612
src/engine/platform/sound/scc/scc.cpp
Normal file
|
@ -0,0 +1,612 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Konami SCC emulation core
|
||||
|
||||
Konami SCC means "Sound Creative Chip", it's actually MSX MegaROM/RAM Mapper with 5 channel Wavetable sound generator.
|
||||
|
||||
It was first appeared at 1987, F-1 Spirit and Nemesis 2/Gradius 2 for MSX. then several MSX cartridges used that until 1990, Metal Gear 2: Solid Snake.
|
||||
Even after MSX is discontinued, it was still used at some low-end arcade and amusement hardwares.
|
||||
and some Third-party MSX utilities still support this due to its market shares.
|
||||
|
||||
There's 2 SCC types:
|
||||
|
||||
K051649 (or simply known as SCC)
|
||||
This chip is used for MSX MegaROM Mapper, some arcade machines.
|
||||
Channel 4 and 5 must be share waveform, other channels has its own waveforms.
|
||||
|
||||
K052539 (also known as SCC+)
|
||||
This chip is used for MSX MegaRAM Mapper (Konami Sound Cartridges for Snatcher/SD Snatcher).
|
||||
All channels can be has its own waveforms, and also has backward compatibility mode with K051649.
|
||||
|
||||
Based on:
|
||||
https://www.msx.org/wiki/MegaROM_Mappers
|
||||
https://www.msx.org/wiki/Konami_051649
|
||||
https://www.msx.org/wiki/Konami_052539
|
||||
http://bifi.msxnet.org/msxnet/tech/scc
|
||||
http://bifi.msxnet.org/msxnet/tech/soundcartridge
|
||||
|
||||
K051649 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaROM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R Bank page 0
|
||||
c000-dfff mirror of 4000-5fff
|
||||
|
||||
6000-7fff xxxx xxxx R Bank page 1
|
||||
e000-ffff mirror of 6000-7fff
|
||||
|
||||
8000-9fff xxxx xxxx R Bank page 2
|
||||
0000-1fff mirror of 8000-9fff
|
||||
|
||||
a000-bfff xxxx xxxx R Bank page 3
|
||||
2000-3fff mirror of a000-bfff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 --xx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 --xx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 --xx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 --xx xxxx W Bank select, Page 3
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98e0 x--- ---- W Waveform rotate flag for channel 4
|
||||
-x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98e1-98ff Mirror of 98e0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
K052539 Register Layout
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
4000-bfff MegaRAM Mapper
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
4000-5fff xxxx xxxx R/W Bank page 0
|
||||
c000-dfff xxxx xxxx R/W ""
|
||||
|
||||
6000-7fff xxxx xxxx R/W Bank page 1
|
||||
e000-ffff xxxx xxxx R/W ""
|
||||
|
||||
8000-9fff xxxx xxxx R/W Bank page 2
|
||||
0000-1fff xxxx xxxx R/W ""
|
||||
|
||||
a000-bfff xxxx xxxx R/W Bank page 3
|
||||
2000-3fff xxxx xxxx R/W ""
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
5000-57ff, 7000-77ff, 9000-97ff, b000-b7ff Bank select
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
5000 xxxx xxxx W Bank select, Page 0
|
||||
5001-57ff Mirror of 5000
|
||||
|
||||
7000 xxxx xxxx W Bank select, Page 1
|
||||
7001-77ff Mirror of 7000
|
||||
|
||||
9000 xxxx xxxx W Bank select, Page 2
|
||||
--11 1111 W SCC Enable (SCC Compatible mode)
|
||||
9001-97ff Mirror of 9000
|
||||
|
||||
b000 xxxx xxxx W Bank select, Page 3
|
||||
1--- ---- W SCC+ Enable (SCC+ mode)
|
||||
b001-b7ff Mirror of b000
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
bffe-bfff Mapper configuration
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
bffe --x- ---- W SCC operation mode
|
||||
--0- ---- W SCC Compatible mode
|
||||
--1- ---- W SCC+ mode
|
||||
---x ---- W RAM write/Bank select toggle for all Bank pages
|
||||
---0 ---- W Bank select enable
|
||||
---1 ---- W RAM write enable
|
||||
---0 -x-- W RAM write/Bank select toggle for Bank page 2
|
||||
---0 --x- W RAM write/Bank select toggle for Bank page 1
|
||||
---0 ---x W RAM write/Bank select toggle for Bank page 0
|
||||
bfff Mirror of bffe
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-9fff SCC Compatible mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
9800-987f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
9800-981f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
9820-983f xxxx xxxx R/W Channel 1 ""
|
||||
9840-985f xxxx xxxx R/W Channel 2 ""
|
||||
9860-987f xxxx xxxx R/W Channel 3/4 ""
|
||||
|
||||
9880-9889 Pitch
|
||||
|
||||
9880 xxxx xxxx W Channel 0 Pitch LSB
|
||||
9881 ---- xxxx W Channel 0 Pitch MSB
|
||||
9882 xxxx xxxx W Channel 1 Pitch LSB
|
||||
9883 ---- xxxx W Channel 1 Pitch MSB
|
||||
9884 xxxx xxxx W Channel 2 Pitch LSB
|
||||
9885 ---- xxxx W Channel 2 Pitch MSB
|
||||
9886 xxxx xxxx W Channel 3 Pitch LSB
|
||||
9887 ---- xxxx W Channel 3 Pitch MSB
|
||||
9888 xxxx xxxx W Channel 4 Pitch LSB
|
||||
9889 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
9888-988e Volume
|
||||
|
||||
988a ---- xxxx W Channel 0 Volume
|
||||
988b ---- xxxx W Channel 1 Volume
|
||||
988c ---- xxxx W Channel 2 Volume
|
||||
988d ---- xxxx W Channel 3 Volume
|
||||
988e ---- xxxx W Channel 4 Volume
|
||||
|
||||
988f ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
9890-989f Mirror of 9880-988f
|
||||
|
||||
98a0-98bf xxxx xxxx R Channel 4 Waveform
|
||||
|
||||
98c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
98c1-98df Mirror of 98c0
|
||||
|
||||
9900-9fff Mirror of 9800-98ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-bfff SCC+ mode register
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
b800-b89f Waveform
|
||||
|
||||
Address Bit R/W Description
|
||||
7654 3210
|
||||
|
||||
b800-b81f xxxx xxxx R/W Channel 0 Waveform (32 byte, 8 bit signed)
|
||||
b820-b83f xxxx xxxx R/W Channel 1 ""
|
||||
b840-b85f xxxx xxxx R/W Channel 2 ""
|
||||
b860-b87f xxxx xxxx R/W Channel 3 ""
|
||||
b880-b89f xxxx xxxx R/W Channel 3 ""
|
||||
|
||||
b8a0-b8a9 Pitch
|
||||
|
||||
b8a0 xxxx xxxx W Channel 0 Pitch LSB
|
||||
b8a1 ---- xxxx W Channel 0 Pitch MSB
|
||||
b8a2 xxxx xxxx W Channel 1 Pitch LSB
|
||||
b8a3 ---- xxxx W Channel 1 Pitch MSB
|
||||
b8a4 xxxx xxxx W Channel 2 Pitch LSB
|
||||
b8a5 ---- xxxx W Channel 2 Pitch MSB
|
||||
b8a6 xxxx xxxx W Channel 3 Pitch LSB
|
||||
b8a7 ---- xxxx W Channel 3 Pitch MSB
|
||||
b8a8 xxxx xxxx W Channel 4 Pitch LSB
|
||||
b8a9 ---- xxxx W Channel 4 Pitch MSB
|
||||
|
||||
b8a8-b8ae Volume
|
||||
|
||||
b8aa ---- xxxx W Channel 0 Volume
|
||||
b8ab ---- xxxx W Channel 1 Volume
|
||||
b8ac ---- xxxx W Channel 2 Volume
|
||||
b8ad ---- xxxx W Channel 3 Volume
|
||||
b8ae ---- xxxx W Channel 4 Volume
|
||||
|
||||
b8af ---x ---- W Channel 4 Output enable/disable flag
|
||||
---- x--- W Channel 3 Output enable/disable flag
|
||||
---- -x-- W Channel 2 Output enable/disable flag
|
||||
---- --x- W Channel 1 Output enable/disable flag
|
||||
---- ---x W Channel 0 Output enable/disable flag
|
||||
|
||||
b8b0-b8bf Mirror of b8a0-b8af
|
||||
|
||||
b8c0 -x-- ---- W Waveform rotate flag for all channels
|
||||
--x- ---- W Reset waveform position after pitch writes
|
||||
---- --x- W 8 bit frequency
|
||||
---- --0x W 4 bit frequency
|
||||
|
||||
b8c1-b8df Mirror of b8c0
|
||||
|
||||
b900-bfff Mirror of b800-b8ff
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
SCC Frequency calculation:
|
||||
if 8 bit frequency then
|
||||
Frequency = Input clock / ((bit 0 to 7 of Pitch input) + 1)
|
||||
else if 4 bit frequency then
|
||||
Frequency = Input clock / ((bit 8 to 11 of Pitch input) + 1)
|
||||
else
|
||||
Frequency = Input clock / (Pitch input + 1)
|
||||
|
||||
*/
|
||||
|
||||
#include "scc.hpp"
|
||||
|
||||
// shared SCC features
|
||||
void scc_core::tick()
|
||||
{
|
||||
m_out = 0;
|
||||
for (auto & elem : m_voice)
|
||||
{
|
||||
elem.tick();
|
||||
m_out += elem.out;
|
||||
}
|
||||
}
|
||||
|
||||
void scc_core::voice_t::tick()
|
||||
{
|
||||
if (pitch >= 9) // or voice is halted
|
||||
{
|
||||
// update counter - Post decrement
|
||||
u16 temp = counter;
|
||||
if (m_host.m_test.freq_4bit) // 4 bit frequency mode
|
||||
{
|
||||
counter = (counter & ~0x0ff) | (bitfield(bitfield(counter, 0, 8) - 1, 0, 8) << 0);
|
||||
counter = (counter & ~0xf00) | (bitfield(bitfield(counter, 8, 4) - 1, 0, 4) << 8);
|
||||
}
|
||||
else
|
||||
counter = bitfield(counter - 1, 0, 12);
|
||||
|
||||
// handle counter carry
|
||||
bool carry = m_host.m_test.freq_8bit ? (bitfield(temp, 0, 8) == 0) :
|
||||
(m_host.m_test.freq_4bit ? (bitfield(temp, 8, 4) == 0) :
|
||||
(bitfield(temp, 0, 12) == 0));
|
||||
if (carry)
|
||||
{
|
||||
addr = bitfield(addr + 1, 0, 5);
|
||||
counter = pitch;
|
||||
}
|
||||
}
|
||||
// get output
|
||||
if (enable)
|
||||
out = (wave[addr] * volume) >> 4; // scale to 11 bit digital output
|
||||
else
|
||||
out = 0;
|
||||
}
|
||||
|
||||
void scc_core::reset()
|
||||
{
|
||||
for (auto & elem : m_voice)
|
||||
elem.reset();
|
||||
|
||||
m_test.reset();
|
||||
m_out = 0;
|
||||
std::fill(std::begin(m_reg), std::end(m_reg), 0);
|
||||
}
|
||||
|
||||
void scc_core::voice_t::reset()
|
||||
{
|
||||
std::fill(std::begin(wave), std::end(wave), 0);
|
||||
enable = false;
|
||||
pitch = 0;
|
||||
volume = 0;
|
||||
addr = 0;
|
||||
counter = 0;
|
||||
out = 0;
|
||||
}
|
||||
|
||||
// SCC accessors
|
||||
u8 scc_core::wave_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
u8 ret = 0xff;
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return ret;
|
||||
|
||||
u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(wave_addr + m_voice[voice].addr, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (voice == 3) // rotate voice 3~4 flag
|
||||
{
|
||||
if (m_test.rotate4 || m_test.rotate) // rotate flag
|
||||
wave_addr = bitfield(bitfield(address, 0, 5) + m_voice[3 + m_test.rotate].addr, 0, 5);
|
||||
}
|
||||
}
|
||||
ret = m_voice[voice].wave[wave_addr];
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scc_core::wave_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
if (m_test.rotate) // write protected
|
||||
return;
|
||||
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (voice > 4)
|
||||
return;
|
||||
|
||||
const u8 wave_addr = bitfield(address, 0, 5);
|
||||
|
||||
if (!is_sccplus)
|
||||
{
|
||||
if (((voice >= 3) && m_test.rotate4) || (voice >= 4)) // Ignore if write protected, or voice 4
|
||||
return;
|
||||
if (voice >= 3) // voice 3, 4 shares waveform
|
||||
{
|
||||
m_voice[3].wave[wave_addr] = data;
|
||||
m_voice[4].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
else
|
||||
m_voice[voice].wave[wave_addr] = data;
|
||||
}
|
||||
|
||||
void scc_core::freq_vol_enable_w(u8 address, u8 data)
|
||||
{
|
||||
const u8 voice_freq = bitfield(address, 1, 3);
|
||||
const u8 voice_reg = bitfield(address, 0, 4);
|
||||
// *0-*f Pitch, Volume, Enable
|
||||
switch (voice_reg)
|
||||
{
|
||||
case 0x0: // 0x*0 Voice 0 Pitch LSB
|
||||
case 0x2: // 0x*2 Voice 1 Pitch LSB
|
||||
case 0x4: // 0x*4 Voice 2 Pitch LSB
|
||||
case 0x6: // 0x*6 Voice 3 Pitch LSB
|
||||
case 0x8: // 0x*8 Voice 4 Pitch LSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0x0ff) | data;
|
||||
break;
|
||||
case 0x1: // 0x*1 Voice 0 Pitch MSB
|
||||
case 0x3: // 0x*3 Voice 1 Pitch MSB
|
||||
case 0x5: // 0x*5 Voice 2 Pitch MSB
|
||||
case 0x7: // 0x*7 Voice 3 Pitch MSB
|
||||
case 0x9: // 0x*9 Voice 4 Pitch MSB
|
||||
if (m_test.resetpos) // Reset address
|
||||
m_voice[voice_freq].addr = 0;
|
||||
m_voice[voice_freq].pitch = (m_voice[voice_freq].pitch & ~0xf00) | (u16(bitfield(data, 0, 4)) << 8);
|
||||
break;
|
||||
case 0xa: // 0x*a Voice 0 Volume
|
||||
case 0xb: // 0x*b Voice 1 Volume
|
||||
case 0xc: // 0x*c Voice 2 Volume
|
||||
case 0xd: // 0x*d Voice 3 Volume
|
||||
case 0xe: // 0x*e Voice 4 Volume
|
||||
m_voice[voice_reg - 0xa].volume = bitfield(data, 0, 4);
|
||||
break;
|
||||
case 0xf: // 0x*f Enable/Disable flag
|
||||
m_voice[0].enable = bitfield(data, 0);
|
||||
m_voice[1].enable = bitfield(data, 1);
|
||||
m_voice[2].enable = bitfield(data, 2);
|
||||
m_voice[3].enable = bitfield(data, 3);
|
||||
m_voice[4].enable = bitfield(data, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void k051649_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b111: // 0xe0-0xff Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
m_test.rotate4 = bitfield(data, 7);
|
||||
break;
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
void k052539_scc_core::scc_w(bool is_sccplus, u8 address, u8 data)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
wave_w(true, address, data);
|
||||
break;
|
||||
case 0b101: // 0xa0-0xbf Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3/4 Waveform
|
||||
wave_w(false, address, data);
|
||||
break;
|
||||
case 0b100: // 0x80-0x9f Pitch, Volume, Enable
|
||||
freq_vol_enable_w(address, data);
|
||||
break;
|
||||
case 0b110: // 0xc0-0xdf Test register
|
||||
m_test.freq_4bit = bitfield(data, 0);
|
||||
m_test.freq_8bit = bitfield(data, 1);
|
||||
m_test.resetpos = bitfield(data, 5);
|
||||
m_test.rotate = bitfield(data, 6);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_reg[address] = data;
|
||||
}
|
||||
|
||||
u8 k051649_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
u8 k052539_scc_core::scc_r(bool is_sccplus, u8 address)
|
||||
{
|
||||
const u8 voice = bitfield(address, 5, 3);
|
||||
const u8 wave = bitfield(address, 0, 5);
|
||||
u8 ret = 0xff;
|
||||
if (is_sccplus)
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b100: // 0x80-0x9f Voice 4 Waveform
|
||||
ret = wave_r(true, address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (voice)
|
||||
{
|
||||
case 0b000: // 0x00-0x1f Voice 0 Waveform
|
||||
case 0b001: // 0x20-0x3f Voice 1 Waveform
|
||||
case 0b010: // 0x40-0x5f Voice 2 Waveform
|
||||
case 0b011: // 0x60-0x7f Voice 3 Waveform
|
||||
case 0b101: // 0xa0-0xbf Voice 4 Waveform
|
||||
ret = wave_r(false, (std::min<u8>(4, voice) << 5) | wave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
137
src/engine/platform/sound/scc/scc.hpp
Normal file
137
src/engine/platform/sound/scc/scc.hpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
License: BSD-3-Clause
|
||||
see https://github.com/cam900/vgsound_emu/LICENSE for more details
|
||||
|
||||
Copyright holder(s): cam900
|
||||
Konami SCC emulation core
|
||||
|
||||
See scc.cpp for more info.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#ifndef _VGSOUND_EMU_SCC_HPP
|
||||
#define _VGSOUND_EMU_SCC_HPP
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace scc
|
||||
{
|
||||
typedef unsigned char u8;
|
||||
typedef signed char s8;
|
||||
typedef unsigned short u16;
|
||||
typedef signed short s16;
|
||||
typedef unsigned int u32;
|
||||
typedef signed int s32;
|
||||
|
||||
// get bitfield, bitfield(input, position, len)
|
||||
template<typename T> T bitfield(T in, u8 pos, u8 len = 1)
|
||||
{
|
||||
return (in >> pos) & (len ? (T(1 << len) - 1) : 1);
|
||||
}
|
||||
}
|
||||
|
||||
using namespace scc;
|
||||
// shared for SCCs
|
||||
class scc_core
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
scc_core()
|
||||
: m_voice{*this,*this,*this,*this,*this}
|
||||
{};
|
||||
virtual ~scc_core(){};
|
||||
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) = 0;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) = 0;
|
||||
|
||||
// internal state
|
||||
virtual void reset();
|
||||
void tick();
|
||||
|
||||
// getters
|
||||
s32 out() { return m_out; } // output to DA0...DA10 pin
|
||||
s32 chan_out(u8 ch) { return m_voice[ch].out; }
|
||||
u8 reg(u8 address) { return m_reg[address]; }
|
||||
|
||||
protected:
|
||||
// voice structs
|
||||
struct voice_t
|
||||
{
|
||||
// constructor
|
||||
voice_t(scc_core &host) : m_host(host) {};
|
||||
|
||||
// internal state
|
||||
void reset();
|
||||
void tick();
|
||||
|
||||
// registers
|
||||
scc_core &m_host;
|
||||
s8 wave[32] = {0}; // internal waveform
|
||||
bool enable = false; // output enable flag
|
||||
u16 pitch = 0; // pitch
|
||||
u8 volume = 0; // volume
|
||||
u8 addr = 0; // waveform pointer
|
||||
u16 counter = 0; // frequency counter
|
||||
s32 out = 0; // current output
|
||||
};
|
||||
voice_t m_voice[5]; // 5 voices
|
||||
|
||||
// accessor
|
||||
u8 wave_r(bool is_sccplus, u8 address);
|
||||
void wave_w(bool is_sccplus, u8 address, u8 data);
|
||||
void freq_vol_enable_w(u8 address, u8 data);
|
||||
|
||||
struct test_t
|
||||
{
|
||||
// constructor
|
||||
test_t()
|
||||
: freq_4bit(0)
|
||||
, freq_8bit(0)
|
||||
, resetpos(0)
|
||||
, rotate(0)
|
||||
, rotate4(0)
|
||||
{ };
|
||||
|
||||
void reset()
|
||||
{
|
||||
freq_4bit = 0;
|
||||
freq_8bit = 0;
|
||||
resetpos = 0;
|
||||
rotate = 0;
|
||||
rotate4 = 0;
|
||||
}
|
||||
|
||||
u8 freq_4bit : 1; // 4 bit frequency
|
||||
u8 freq_8bit : 1; // 8 bit frequency
|
||||
u8 resetpos : 1; // reset counter after pitch writes
|
||||
u8 rotate : 1; // rotate and write protect waveform for all channels
|
||||
u8 rotate4 : 1; // same as above but for channel 4 only
|
||||
};
|
||||
|
||||
test_t m_test; // test register
|
||||
s32 m_out = 0; // output to DA0...10
|
||||
|
||||
u8 m_reg[256] = {0}; // register pool
|
||||
};
|
||||
|
||||
// SCC core
|
||||
class k051649_scc_core : public scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
class k052539_scc_core : public k051649_scc_core
|
||||
{
|
||||
public:
|
||||
// accessors
|
||||
virtual u8 scc_r(bool is_sccplus, u8 address) override;
|
||||
virtual void scc_w(bool is_sccplus, u8 address, u8 data) override;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -859,6 +859,8 @@ const int availableSystems[]={
|
|||
DIV_SYSTEM_VRC6,
|
||||
DIV_SYSTEM_FDS,
|
||||
DIV_SYSTEM_MMC5,
|
||||
DIV_SYSTEM_SCC,
|
||||
DIV_SYSTEM_SCC_PLUS,
|
||||
0 // don't remove this last one!
|
||||
};
|
||||
|
||||
|
|
|
@ -425,6 +425,20 @@ void FurnaceGUI::initSystemPresets() {
|
|||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"MSX + SCC", {
|
||||
DIV_SYSTEM_AY8910, 64, 0, 16,
|
||||
DIV_SYSTEM_SCC, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"MSX + SCC+", {
|
||||
DIV_SYSTEM_AY8910, 64, 0, 16,
|
||||
DIV_SYSTEM_SCC_PLUS, 64, 0, 0,
|
||||
0
|
||||
}
|
||||
));
|
||||
cat.systems.push_back(FurnaceGUISysDef(
|
||||
"ZX Spectrum (48K)", {
|
||||
DIV_SYSTEM_AY8910, 64, 0, 2,
|
||||
|
|
|
@ -367,6 +367,8 @@ void FurnaceGUI::drawSysConf(int chan, DivSystem type, unsigned int& flags, bool
|
|||
case DIV_SYSTEM_YM2610B_EXT:
|
||||
case DIV_SYSTEM_YMU759:
|
||||
case DIV_SYSTEM_PET:
|
||||
case DIV_SYSTEM_SCC:
|
||||
case DIV_SYSTEM_SCC_PLUS:
|
||||
ImGui::Text("nothing to configure");
|
||||
break;
|
||||
default:
|
||||
|
|
Loading…
Reference in a new issue