Add NEC D65010G031 support

chip flag and instrument color is placeholder
This commit is contained in:
cam900 2023-03-05 14:01:44 +09:00
parent aa5c3ec28a
commit dbd1f56a10
17 changed files with 532 additions and 4 deletions

View file

@ -459,6 +459,8 @@ src/engine/platform/sound/ga20/iremga20.cpp
src/engine/platform/sound/sm8521.c
src/engine/platform/sound/d65010g031.c
src/engine/platform/oplAInterface.cpp
src/engine/platform/ym2608Interface.cpp
src/engine/platform/ym2610Interface.cpp
@ -544,6 +546,7 @@ src/engine/platform/snes.cpp
src/engine/platform/k007232.cpp
src/engine/platform/ga20.cpp
src/engine/platform/sm8521.cpp
src/engine/platform/d65010g031.cpp
src/engine/platform/pcmdac.cpp
src/engine/platform/dummy.cpp
)

View file

@ -76,6 +76,7 @@
#include "platform/k007232.h"
#include "platform/ga20.h"
#include "platform/sm8521.h"
#include "platform/d65010g031.h"
#include "platform/pcmdac.h"
#include "platform/dummy.h"
#include "../ta-log.h"
@ -493,6 +494,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
case DIV_SYSTEM_SM8521:
dispatch=new DivPlatformSM8521;
break;
case DIV_SYSTEM_D65010G031:
dispatch=new DivPlatformD65010G031;
break;
case DIV_SYSTEM_PCM_DAC:
dispatch=new DivPlatformPCMDAC;
break;

View file

@ -928,6 +928,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song) {
checkForWL=true;
if (ws.enabled) featureWS=true;
break;
case DIV_INS_D65010G031:
break;
case DIV_INS_MAX:
break;

View file

@ -79,6 +79,7 @@ enum DivInstrumentType: unsigned short {
DIV_INS_GA20=46,
DIV_INS_POKEMINI=47,
DIV_INS_SM8521=48,
DIV_INS_D65010G031=49,
DIV_INS_MAX,
DIV_INS_NULL
};

View file

@ -0,0 +1,270 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 "d65010g031.h"
#include "../engine.h"
#include <math.h>
#define rWrite(a,v) {regPool[(a)]=(v)&0xff; d65010g031_write(&d65010g031,a,v);}
#define CHIP_DIVIDER 1024
const char* regCheatSheetD65010G031[]={
"CH1_Pitch", "00",
"CH2_Pitch", "01",
"CH3_Pitch", "02",
NULL
};
const char** DivPlatformD65010G031::getRegisterSheet() {
return regCheatSheetD65010G031;
}
void DivPlatformD65010G031::acquire(short** buf, size_t len) {
for (size_t h=0; h<len; h++) {
short samp;
samp=d65010g031_sound_tick(&d65010g031,1);
buf[0][h]=samp<<12;
for (int i=0; i<3; i++) {
oscBuf[i]->data[oscBuf[i]->needle++]=(d65010g031.square[i].out<<12);
}
}
}
void DivPlatformD65010G031::tick(bool sysTick) {
for (int i=0; i<3; i++) {
chan[i].std.next();
if (NEW_ARP_STRAT) {
chan[i].handleArp();
} else if (chan[i].std.arp.had) {
if (!chan[i].inPorta) {
chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val));
}
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,-32768,32767);
} else {
chan[i].pitch2=chan[i].std.pitch.val;
}
chan[i].freqChanged=true;
}
if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) {
chan[i].freq=0x3f-parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,true,0,chan[i].pitch2,chipClock,CHIP_DIVIDER);
if (chan[i].freq<1) chan[i].freq=1;
if (chan[i].freq>62) chan[i].freq=62;
if (isMuted[i]) chan[i].keyOn=false;
if (chan[i].keyOn) {
rWrite(i,chan[i].freq);
chan[i].keyOn=false;
} else if (chan[i].freqChanged && chan[i].active && !isMuted[i]) {
rWrite(i,chan[i].freq);
}
if (chan[i].keyOff) {
rWrite(i,0);
chan[i].keyOff=false;
}
chan[i].freqChanged=false;
}
}
}
int DivPlatformD65010G031::dispatch(DivCommand c) {
switch (c.cmd) {
case DIV_CMD_NOTE_ON: {
DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_VIC);
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].keyOn=true;
chan[c.chan].macroInit(ins);
break;
}
case DIV_CMD_NOTE_OFF:
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_GET_VOLUME:
return chan[c.chan].vol;
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_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+((HACKY_LEGATO_MESS)?(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_VIC));
}
if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note);
chan[c.chan].inPorta=c.value;
break;
case DIV_CMD_GET_VOLMAX:
return 15;
break;
case DIV_CMD_MACRO_OFF:
chan[c.chan].std.mask(c.value,true);
break;
case DIV_CMD_MACRO_ON:
chan[c.chan].std.mask(c.value,false);
break;
case DIV_ALWAYS_SET_VOLUME:
return 1;
break;
default:
break;
}
return 1;
}
void DivPlatformD65010G031::muteChannel(int ch, bool mute) {
isMuted[ch]=mute;
if (mute) {
chan[ch].keyOff=true;
} else if (chan[ch].active) {
chan[ch].keyOn=true;
}
}
void DivPlatformD65010G031::forceIns() {
for (int i=0; i<3; i++) {
chan[i].insChanged=true;
chan[i].freqChanged=true;
}
}
void* DivPlatformD65010G031::getChanState(int ch) {
return &chan[ch];
}
DivMacroInt* DivPlatformD65010G031::getChanMacroInt(int ch) {
return &chan[ch].std;
}
DivDispatchOscBuffer* DivPlatformD65010G031::getOscBuffer(int ch) {
return oscBuf[ch];
}
unsigned char* DivPlatformD65010G031::getRegisterPool() {
return regPool;
}
int DivPlatformD65010G031::getRegisterPoolSize() {
return 3;
}
void DivPlatformD65010G031::reset() {
memset(regPool,0,3);
for (int i=0; i<3; i++) {
chan[i]=Channel();
chan[i].std.setEngine(parent);
}
d65010g031_reset(&d65010g031);
}
int DivPlatformD65010G031::getOutputCount() {
return 1;
}
void DivPlatformD65010G031::notifyInsDeletion(void* ins) {
for (int i=0; i<3; i++) {
chan[i].std.notifyInsDeletion((DivInstrument*)ins);
}
}
void DivPlatformD65010G031::setFlags(const DivConfig& flags) {
chipClock=COLOR_NTSC*5.0;
CHECK_CUSTOM_CLOCK;
rate=chipClock/1024;
for (int i=0; i<3; i++) {
oscBuf[i]->rate=rate;
}
}
void DivPlatformD65010G031::poke(unsigned int addr, unsigned short val) {
rWrite(addr,val);
}
void DivPlatformD65010G031::poke(std::vector<DivRegWrite>& wlist) {
for (DivRegWrite& i: wlist) rWrite(i.addr,i.val);
}
int DivPlatformD65010G031::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) {
parent=p;
dumpWrites=false;
skipRegisterWrites=false;
for (int i=0; i<3; i++) {
isMuted[i]=false;
oscBuf[i]=new DivDispatchOscBuffer;
}
setFlags(flags);
reset();
return 4;
}
void DivPlatformD65010G031::quit() {
for (int i=0; i<3; i++) {
delete oscBuf[i];
}
}
DivPlatformD65010G031::~DivPlatformD65010G031() {
}

View file

@ -0,0 +1,63 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2023 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 _D65010G031_H
#define _D65010G031_H
#include "../dispatch.h"
#include "sound/d65010g031.h"
#include <queue>
class DivPlatformD65010G031: public DivDispatch {
struct Channel: public SharedChannel<int> {
Channel():
SharedChannel<int>(15) {}
};
Channel chan[3];
DivDispatchOscBuffer* oscBuf[3];
bool isMuted[3];
unsigned char regPool[3];
d65010g031_t d65010g031;
friend void putDispatchChip(void*,int);
friend void putDispatchChan(void*,int,int);
public:
void acquire(short** buf, size_t len);
int dispatch(DivCommand c);
void* getChanState(int chan);
DivMacroInt* getChanMacroInt(int ch);
DivDispatchOscBuffer* getOscBuffer(int chan);
unsigned char* getRegisterPool();
int getRegisterPoolSize();
void reset();
void forceIns();
void tick(bool sysTick=true);
void muteChannel(int ch, bool mute);
void setFlags(const DivConfig& flags);
void notifyInsDeletion(void* ins);
int getOutputCount();
void poke(unsigned int addr, unsigned short val);
void poke(std::vector<DivRegWrite>& wlist);
const char** getRegisterSheet();
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
void quit();
~DivPlatformD65010G031();
};
#endif

View file

@ -0,0 +1,86 @@
/*
============================================================================
NEC D65010G031 sound emulator
by cam900
This file is licensed under zlib license.
============================================================================
zlib License
(C) 2023-present cam900 and contributors
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
============================================================================
TODO:
- needs hardware test
*/
#include "d65010g031.h"
#include <stdlib.h>
static int d65010g031_max(int a, int b) { return (a > b) ? a : b; }
int d65010g031_square_tick(struct d65010g031_square_t *square, const int cycle)
{
if (square->period > 0)
{
int period = d65010g031_max(1, (0x3f - square->period));
square->counter += cycle;
while (square->counter >= period)
{
square->counter -= period;
square->out ^= 1;
}
return square->out;
}
return 0;
}
int d65010g031_sound_tick(struct d65010g031_t *d65010g031, const int cycle)
{
int out = 0;
for (int i = 0; i < 3; i++)
{
out += d65010g031_square_tick(&d65010g031->square[i], cycle);
}
return out;
}
void d65010g031_reset(struct d65010g031_t *d65010g031)
{
for (int i = 0; i < 3; i++)
{
d65010g031->square[i].period = 0;
d65010g031->square[i].counter = 0;
d65010g031->square[i].out = 0;
}
}
void d65010g031_write(struct d65010g031_t *d65010g031, const unsigned char a, const unsigned char d)
{
if (a < 3)
{
d65010g031->square[a].period = d & 0x3f;
}
}

View file

@ -0,0 +1,71 @@
/*
============================================================================
NEC D65010G031 sound emulator
by cam900
This file is licensed under zlib license.
============================================================================
zlib License
(C) 2023-present cam900 and contributors
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
============================================================================
TODO:
- needs hardware test
*/
#ifndef _D65010G031_EMU_H
#define _D65010G031_EMU_H
#ifdef __cplusplus
extern "C"
{
#endif
struct d65010g031_square_t
{
unsigned char period; // Period (0 = Off)
int counter; // clock counter
signed short out; // output
};
struct d65010g031_t
{
struct d65010g031_square_t square[3];
};
int d65010g031_square_tick(struct d65010g031_square_t *square, const int cycle);
int d65010g031_sound_tick(struct d65010g031_t *d65010g031, const int cycle);
void d65010g031_reset(struct d65010g031_t *d65010g031);
void d65010g031_write(struct d65010g031_t *d65010g031, const unsigned char a, const unsigned char d);
#ifdef __cplusplus
} // extern "C"
#endif
#endif // _D65010G031_EMU_H

View file

@ -125,7 +125,8 @@ enum DivSystem {
DIV_SYSTEM_YM2610B_CSM,
DIV_SYSTEM_YM2203_CSM,
DIV_SYSTEM_YM2608_CSM,
DIV_SYSTEM_SM8521
DIV_SYSTEM_SM8521,
DIV_SYSTEM_D65010G031
};
struct DivGroovePattern {

View file

@ -1833,6 +1833,15 @@ void DivEngine::registerSystems() {
namcoEffectHandlerMap
);
sysDefs[DIV_SYSTEM_D65010G031]=new DivSysDef(
"NEC D65010G031", NULL, 0xfe/*placeholder*/, 0, 3, false, true, 0, false, 0,
"Used at Casio PV1000, Nothing but 3 square wave channels.",
{"Channel 1", "Channel 2", "Channel 3"},
{"CH1", "CH2", "CH3"},
{DIV_CH_PULSE, DIV_CH_PULSE, DIV_CH_PULSE},
{DIV_INS_D65010G031, DIV_INS_D65010G031, DIV_INS_D65010G031}
);
sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef(
"Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0,
"this is a system designed for testing purposes.",

View file

@ -432,6 +432,10 @@ void FurnaceGUI::drawInsList(bool asChild) {
break;
case DIV_INS_SM8521:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_SM8521]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);/*placeholder*/
break;
case DIV_INS_D65010G031:
ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_INSTR_D65010G031]);
name=fmt::sprintf(ICON_FA_GAMEPAD "##_INS%d",i);
break;
default:

View file

@ -190,6 +190,7 @@ enum FurnaceGUIColors {
GUI_COLOR_INSTR_GA20,
GUI_COLOR_INSTR_POKEMINI,
GUI_COLOR_INSTR_SM8521,
GUI_COLOR_INSTR_D65010G031,
GUI_COLOR_INSTR_UNKNOWN,
GUI_COLOR_CHANNEL_BG,

View file

@ -130,6 +130,7 @@ const char* insTypes[DIV_INS_MAX+1]={
"GA20",
"Pokémon Mini",
"SM8521",
"D65010G031",
NULL
};
@ -820,6 +821,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
D(GUI_COLOR_INSTR_GA20,"",ImVec4(0.1f,1.0f,0.4f,1.0f)),
D(GUI_COLOR_INSTR_POKEMINI,"",ImVec4(1.0f,1.0f,0.3f,1.0f)),
D(GUI_COLOR_INSTR_SM8521,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),
D(GUI_COLOR_INSTR_D65010G031,"",ImVec4(0.5f,0.55f,0.6f,1.0f)),/*placeholder*/
D(GUI_COLOR_INSTR_UNKNOWN,"",ImVec4(0.3f,0.3f,0.3f,1.0f)),
D(GUI_COLOR_CHANNEL_BG,"",ImVec4(0.4f,0.6f,0.8f,1.0f)),
@ -1001,6 +1003,7 @@ const int availableSystems[]={
DIV_SYSTEM_K007232,
DIV_SYSTEM_GA20,
DIV_SYSTEM_SM8521,
DIV_SYSTEM_D65010G031,
DIV_SYSTEM_PCM_DAC,
DIV_SYSTEM_PONG,
0 // don't remove this last one!
@ -1050,6 +1053,7 @@ const int chipsSquare[]={
DIV_SYSTEM_VIC20,
DIV_SYSTEM_MSM5232,
DIV_SYSTEM_T6W28,
DIV_SYSTEM_D65010G031,
0 // don't remove this last one!
};

View file

@ -5048,7 +5048,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_ES5506) {
volMax=4095;
}
if (ins->type==DIV_INS_MSM6258) {
if (ins->type==DIV_INS_MSM6258 || ins->type==DIV_INS_D65010G031) {
volMax=0;
}
if (ins->type==DIV_INS_MSM6295) {
@ -5118,7 +5118,7 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_AMIGA || ins->type==DIV_INS_SCC ||
ins->type==DIV_INS_PET || ins->type==DIV_INS_VIC || ins->type==DIV_INS_SEGAPCM ||
ins->type==DIV_INS_FM || ins->type==DIV_INS_K007232 || ins->type==DIV_INS_GA20 ||
ins->type==DIV_INS_SM8521) {
ins->type==DIV_INS_SM8521 || ins->type==DIV_INS_D65010G031) {
dutyMax=0;
}
if (ins->type==DIV_INS_VBOY) {
@ -5190,7 +5190,9 @@ void FurnaceGUI::drawInsEdit() {
if (ins->type==DIV_INS_C64 || ins->type==DIV_INS_SAA1099) {
waveBitMode=true;
}
if (ins->type==DIV_INS_STD || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_NES || ins->type==DIV_INS_T6W28) waveMax=0;
if (ins->type==DIV_INS_STD || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_NES ||
ins->type==DIV_INS_T6W28 || ins->type==DIV_INS_D65010G031)
waveMax=0;
if (ins->type==DIV_INS_TIA || ins->type==DIV_INS_VIC || ins->type==DIV_INS_OPLL) waveMax=15;
if (ins->type==DIV_INS_C64) waveMax=4;
if (ins->type==DIV_INS_SAA1099) waveMax=2;

View file

@ -2365,6 +2365,11 @@ void FurnaceGUI::initSystemPresets() {
CH(DIV_SYSTEM_PONG, 1.0f, 0, "")
}
);
ENTRY(
"NEC D65010G031", {
CH(DIV_SYSTEM_D65010G031, 1.0f, 0, "")
}
);
CATEGORY_END;
CATEGORY_BEGIN("Sample","chips/systems which use PCM or ADPCM samples for sound synthesis.");

View file

@ -1929,6 +1929,7 @@ void FurnaceGUI::drawSettings() {
UI_COLOR_CONFIG(GUI_COLOR_INSTR_GA20,"GA20");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_POKEMINI,"Pokémon Mini");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_SM8521,"SM8521");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_D65010G031,"D65010G031");
UI_COLOR_CONFIG(GUI_COLOR_INSTR_UNKNOWN,"Other/Unknown");
ImGui::TreePop();
}

View file

@ -1723,6 +1723,7 @@ bool FurnaceGUI::drawSysConf(int chan, DivSystem type, DivConfig& flags, bool mo
case DIV_SYSTEM_PET:
case DIV_SYSTEM_VBOY:
case DIV_SYSTEM_GA20:
case DIV_SYSTEM_D65010G031:
ImGui::Text("nothing to configure");
break;
case DIV_SYSTEM_VERA: