From 3f4c7dc46cda75ba5fe05ebda22c078d26c6fbf8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Feb 2024 02:57:48 -0500 Subject: [PATCH 1/6] second attempt at MinGW backtrace --- CMakeLists.txt | 2 + papers/format.md | 1 + src/engine/platform/dave.cpp | 631 ++++++++++++++++++ src/engine/platform/dave.h | 103 +++ src/engine/platform/sound/dave/dave.cpp | 813 ++++++++++++++++++++++++ src/engine/platform/sound/dave/dave.hpp | 251 ++++++++ 6 files changed, 1801 insertions(+) create mode 100644 src/engine/platform/dave.cpp create mode 100644 src/engine/platform/dave.h create mode 100644 src/engine/platform/sound/dave/dave.cpp create mode 100644 src/engine/platform/sound/dave/dave.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2490246a3..f12a98629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -606,6 +606,8 @@ src/engine/platform/sound/ted-sound.c src/engine/platform/sound/c140_c219.c +src/engine/platform/sound/dave/dave.cpp + src/engine/platform/oplAInterface.cpp src/engine/platform/ym2608Interface.cpp src/engine/platform/ym2610Interface.cpp diff --git a/papers/format.md b/papers/format.md index 149c49869..43a4dd78d 100644 --- a/papers/format.md +++ b/papers/format.md @@ -238,6 +238,7 @@ size | description | - 0xd1: ESFM - 18 channels | - 0xd2: Ensoniq ES5503 (hard pan) - 32 channels (UNAVAILABLE) | - 0xd4: PowerNoise - 4 channels + | - 0xd5: Dave - 6 channels (UNAVAILABLE) | - 0xde: YM2610B extended - 19 channels | - 0xe0: QSound - 19 channels | - 0xfc: Pong - 1 channel diff --git a/src/engine/platform/dave.cpp b/src/engine/platform/dave.cpp new file mode 100644 index 000000000..c98469b36 --- /dev/null +++ b/src/engine/platform/dave.cpp @@ -0,0 +1,631 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 "dave.h" +#include "../engine.h" +#include "furIcons.h" +#include + +//#define rWrite(a,v) pendingWrites[a]=v; +#define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } +#define chWrite(c,a,v) \ + if (!skipRegisterWrites) { \ + if (curChan!=c) { \ + curChan=c; \ + rWrite(0,curChan); \ + } \ + regPool[16+((c)<<4)+((a)&0x0f)]=v; \ + rWrite(a,v); \ + } + +#define CHIP_DIVIDER 32 + +const char* regCheatSheetDave[]={ + "Freq0", "00", + "Control0", "01", + "Freq1", "02", + "Control1", "03", + "Freq2", "04", + "Control2", "05", + "Control3", "06", + "SoundCtrl", "07", + "Vol0L", "08", + "Vol1L", "09", + "Vol2L", "0A", + "Vol3L", "0B", + "Vol0R", "0C", + "Vol1R", "0D", + "Vol2R", "0E", + "Vol3R", "0F", + "ClockDiv", "1F", + NULL +}; + +const char** DivPlatformDave::getRegisterSheet() { + return regCheatSheetDave; +} + +void DivPlatformDave::acquire(short** buf, size_t len) { + for (size_t h=0; hrate) { + DivSample* s=parent->getSample(chan[i].dacSample); + if (s->samples<=0) { + chan[i].dacSample=-1; + continue; + } + chWrite(i,0x07,0); + signed char dacData=((signed char)((unsigned char)s->data8[chan[i].dacPos]^0x80))>>3; + chan[i].dacOut=CLAMP(dacData,-16,15); + if (!isMuted[i]) { + chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].outVol)); + chWrite(i,0x06,chan[i].dacOut&0x1f); + } else { + chWrite(i,0x04,0xc0); + chWrite(i,0x06,0x10); + } + chan[i].dacPos++; + if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->loopEnd) { + chan[i].dacPos=s->loopStart; + } else if (chan[i].dacPos>=s->samples) { + chan[i].dacSample=-1; + } + chan[i].dacPeriod-=rate; + } + } + } + + // PCE part + cycles=0; + while (!writes.empty() && cycles<24) { + QueuedWrite w=writes.front(); + pce->Write(cycles,w.addr,w.val); + regPool[w.addr&0x0f]=w.val; + //cycles+=2; + writes.pop(); + } + memset(tempL,0,24*sizeof(int)); + memset(tempR,0,24*sizeof(int)); + pce->Update(24); + pce->ResetTS(0); + + for (int i=0; i<6; i++) { + oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1],-32768,32767); + } + + tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); + tempR[0]=(tempR[0]>>1)+(tempR[0]>>2); + + if (tempL[0]<-32768) tempL[0]=-32768; + if (tempL[0]>32767) tempL[0]=32767; + if (tempR[0]<-32768) tempR[0]=-32768; + if (tempR[0]>32767) tempR[0]=32767; + + //printf("tempL: %d tempR: %d\n",tempL,tempR); + buf[0][h]=tempL[0]; + buf[1][h]=tempR[0]; + } +} + +// TODO: in octave 6 the noise table changes to a tonal one +static unsigned char noiseFreq[12]={ + 4,13,15,18,21,23,25,27,29,31,0,2 +}; + +void DivPlatformDave::tick(bool sysTick) { + for (int i=0; i<6; i++) { + chan[i].std.next(); + if (chan[i].std.vol.had) { + chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); + if (chan[i].furnaceDac && chan[i].pcm) { + // ignore for now + } else { + chWrite(i,0x04,0x80|chan[i].outVol); + } + } + if (chan[i].std.duty.had && i>=4) { + chan[i].noise=chan[i].std.duty.val; + chan[i].freqChanged=true; + } + if (NEW_ARP_STRAT) { + chan[i].handleArp(); + } else if (chan[i].std.arp.had) { + if (!chan[i].inPorta) { + int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val); + chan[i].baseFreq=NOTE_PERIODIC(noiseSeek); + if (noiseSeek<0) noiseSeek=0; + chan[i].noiseSeek=noiseSeek; + } + chan[i].freqChanged=true; + } + if (chan[i].std.wave.had && !chan[i].pcm) { + // TODO: this + } + if (chan[i].std.panL.had) { + chan[i].pan&=0x0f; + chan[i].pan|=(chan[i].std.panL.val&15)<<4; + } + if (chan[i].std.panR.had) { + chan[i].pan&=0xf0; + chan[i].pan|=chan[i].std.panR.val&15; + } + if (chan[i].std.panL.had || chan[i].std.panR.had) { + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } + 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].std.phaseReset.had && chan[i].std.phaseReset.val==1) { + if (chan[i].furnaceDac && chan[i].pcm) { + if (chan[i].active && chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { + chan[i].dacPos=0; + chan[i].dacPeriod=0; + chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].vol)); + addWrite(0xffff0000+(i<<8),chan[i].dacSample); + chan[i].keyOn=true; + } + } + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); + chan[i].freq=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].furnaceDac && chan[i].pcm) { + double off=1.0; + if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { + DivSample* s=parent->getSample(chan[i].dacSample); + if (s->centerRate<1) { + off=1.0; + } else { + off=8363.0/(double)s->centerRate; + } + } + chan[i].dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); + if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dacRate); + } + if (chan[i].freq<1) chan[i].freq=1; + if (chan[i].freq>4095) chan[i].freq=4095; + chWrite(i,0x02,chan[i].freq&0xff); + chWrite(i,0x03,chan[i].freq>>8); + + if (i>=4) { + int noiseSeek=(chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff))+chan[i].pitch2; + if (!parent->song.properNoiseLayout && noiseSeek<0) noiseSeek=0; + if (!NEW_ARP_STRAT) { + noiseSeek=chan[i].noiseSeek; + } + chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); + } + if (chan[i].keyOn) { + //rWrite(16+i*5,0x80); + //chWrite(i,0x04,0x80|chan[i].vol); + } + if (chan[i].keyOff) { + chWrite(i,0x04,0); + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + } + if (updateLFO) { + rWrite(0x08,lfoSpeed); + rWrite(0x09,lfoMode); + updateLFO=false; + } +} + +int DivPlatformDave::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); + chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:31; + if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { + chan[c.chan].pcm=true; + } else if (chan[c.chan].furnaceDac) { + chan[c.chan].pcm=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + } + if (chan[c.chan].pcm) { + if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { + chan[c.chan].furnaceDac=true; + if (skipRegisterWrites) break; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; + c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); + } + if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { + chan[c.chan].dacSample=-1; + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + break; + } else { + if (dumpWrites) { + chWrite(c.chan,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol)); + addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dacSample); + } + } + chan[c.chan].dacPos=0; + chan[c.chan].dacPeriod=0; + 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 (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + //chan[c.chan].keyOn=true; + } else { + chan[c.chan].furnaceDac=false; + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + if (skipRegisterWrites) break; + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].note=c.value; + } + chan[c.chan].dacSample=12*sampleBank+chan[c.chan].note%12; + if (chan[c.chan].dacSample>=parent->song.sampleLen) { + chan[c.chan].dacSample=-1; + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + break; + } else { + if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dacSample); + } + chan[c.chan].dacPos=0; + chan[c.chan].dacPeriod=0; + chan[c.chan].dacRate=parent->getSample(chan[c.chan].dacSample)->rate; + if (dumpWrites) { + chWrite(c.chan,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol)); + addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dacRate); + } + } + break; + } + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + 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].noiseSeek=c.value; + if (chan[c.chan].noiseSeek<0) chan[c.chan].noiseSeek=0; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chWrite(c.chan,0x04,0x80|chan[c.chan].vol); + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].dacSample=-1; + if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); + chan[c.chan].pcm=false; + 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; + chan[c.chan].insChanged=true; + } + 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; + if (chan[c.chan].active && !chan[c.chan].pcm) { + chWrite(c.chan,0x04,0x80|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_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].keyOn=true; + break; + case DIV_CMD_PCE_LFO_MODE: + if (c.value==0) { + lfoMode=0; + } else { + lfoMode=c.value; + } + updateLFO=true; + break; + case DIV_CMD_PCE_LFO_SPEED: + lfoSpeed=255-c.value; + updateLFO=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); + 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_STD_NOISE_MODE: + chan[c.chan].noise=c.value; + chan[c.chan].freqChanged=true; + break; + case DIV_CMD_SAMPLE_MODE: + chan[c.chan].pcm=c.value; + break; + case DIV_CMD_SAMPLE_BANK: + sampleBank=c.value; + if (sampleBank>(parent->song.sample.size()/12)) { + sampleBank=parent->song.sample.size()/12; + } + break; + case DIV_CMD_PANNING: { + chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); + chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan); + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((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_PCE)); + } + 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 31; + 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_CMD_MACRO_RESTART: + chan[c.chan].std.restart(c.value); + break; + default: + break; + } + return 1; +} + +void DivPlatformDave::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan); + if (!isMuted[ch] && (chan[ch].pcm && chan[ch].dacSample!=-1)) { + chWrite(ch,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[ch].outVol)); + chWrite(ch,0x06,chan[ch].dacOut&0x1f); + } +} + +void DivPlatformDave::forceIns() { + for (int i=0; i<6; i++) { + chan[i].insChanged=true; + chan[i].freqChanged=true; + updateWave(i); + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } +} + +void* DivPlatformDave::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformDave::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +unsigned short DivPlatformDave::getPan(int ch) { + return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); +} + +DivChannelPair DivPlatformDave::getPaired(int ch) { + if (ch==1 && lfoMode>0) { + return DivChannelPair("mod",0); + } + return DivChannelPair(); +} + +DivChannelModeHints DivPlatformDave::getModeHints(int ch) { + DivChannelModeHints ret; + if (ch<4) return ret; + ret.count=1; + ret.hint[0]=ICON_FUR_NOISE; + ret.type[0]=0; + + if (chan[ch].noise) ret.type[0]=4; + + return ret; +} + +DivSamplePos DivPlatformDave::getSamplePos(int ch) { + if (ch>=6) return DivSamplePos(); + if (!chan[ch].pcm) return DivSamplePos(); + return DivSamplePos( + chan[ch].dacSample, + chan[ch].dacPos, + chan[ch].dacRate + ); +} + +DivDispatchOscBuffer* DivPlatformDave::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +int DivPlatformDave::mapVelocity(int ch, float vel) { + return round(31.0*pow(vel,0.22)); +} + +unsigned char* DivPlatformDave::getRegisterPool() { + return regPool; +} + +int DivPlatformDave::getRegisterPoolSize() { + return 112; +} + +void DivPlatformDave::reset() { + writes.clear(); + memset(regPool,0,128); + for (int i=0; i<6; i++) { + chan[i]=DivPlatformDave::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + pce->Power(0); + lastPan=0xff; + memset(tempL,0,32*sizeof(int)); + memset(tempR,0,32*sizeof(int)); + cycles=0; + curChan=-1; + sampleBank=0; + lfoMode=0; + lfoSpeed=255; + // set global volume + rWrite(0,0); + rWrite(0x01,0xff); + // set LFO + updateLFO=true; + // set per-channel initial panning + for (int i=0; i<6; i++) { + chWrite(i,0x05,isMuted[i]?0:chan[i].pan); + } + delay=500; +} + +int DivPlatformDave::getOutputCount() { + return 2; +} + +bool DivPlatformDave::keyOffAffectsArp(int ch) { + return true; +} + +void DivPlatformDave::notifyInsDeletion(void* ins) { + for (int i=0; i<6; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformDave::setFlags(const DivConfig& flags) { + chipClock=8000000.0; + CHECK_CUSTOM_CLOCK; + antiClickEnabled=!flags.getBool("noAntiClick",false); + rate=chipClock/12; + for (int i=0; i<6; i++) { + oscBuf[i]->rate=rate; + } + + if (pce!=NULL) { + delete pce; + pce=NULL; + } + pce=new PCE_PSG(tempL,tempR,flags.getInt("chipType",0)?PCE_PSG::REVISION_HUC6280A:PCE_PSG::REVISION_HUC6280); +} + +void DivPlatformDave::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformDave::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformDave::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + updateLFO=false; + for (int i=0; i<6; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + pce=NULL; + setFlags(flags); + reset(); + return 6; +} + +void DivPlatformDave::quit() { + for (int i=0; i<6; i++) { + delete oscBuf[i]; + } + if (pce!=NULL) { + delete pce; + pce=NULL; + } +} + +DivPlatformDave::~DivPlatformDave() { +} diff --git a/src/engine/platform/dave.h b/src/engine/platform/dave.h new file mode 100644 index 000000000..e61d5ab4d --- /dev/null +++ b/src/engine/platform/dave.h @@ -0,0 +1,103 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2024 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 _PCE_H +#define _PCE_H + +#include "../dispatch.h" +#include "../../fixedQueue.h" +#include "sound/dave/dave.hpp" + +class DivPlatformDave: public DivDispatch { + struct Channel: public SharedChannel { + int dacPeriod, dacRate, dacOut; + unsigned int dacPos; + int dacSample; + unsigned char pan; + bool noise, pcm, furnaceDac, deferredWaveUpdate; + signed short wave; + int macroVolMul, noiseSeek; + Channel(): + SharedChannel(31), + dacPeriod(0), + dacRate(0), + dacOut(0), + dacPos(0), + dacSample(-1), + pan(255), + noise(false), + pcm(false), + furnaceDac(false), + deferredWaveUpdate(false), + wave(-1), + macroVolMul(31), + noiseSeek(0) {} + }; + Channel chan[6]; + DivDispatchOscBuffer* oscBuf[6]; + bool isMuted[6]; + bool antiClickEnabled; + bool updateLFO; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(): addr(0), val(9) {} + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + FixedQueue writes; + unsigned char lastPan; + + int cycles, curChan, delay; + int tempL[32]; + int tempR[32]; + unsigned char sampleBank, lfoMode, lfoSpeed; + Ep128::Dave* dave; + unsigned char regPool[128]; + 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); + unsigned short getPan(int chan); + DivChannelPair getPaired(int chan); + DivChannelModeHints getModeHints(int chan); + DivSamplePos getSamplePos(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + int mapVelocity(int ch, float vel); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + int getOutputCount(); + bool keyOffAffectsArp(int ch); + void setFlags(const DivConfig& flags); + void notifyInsDeletion(void* ins); + void poke(unsigned int addr, unsigned short val); + void poke(std::vector& wlist); + const char** getRegisterSheet(); + int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags); + void quit(); + ~DivPlatformDave(); +}; + +#endif diff --git a/src/engine/platform/sound/dave/dave.cpp b/src/engine/platform/sound/dave/dave.cpp new file mode 100644 index 000000000..59d2f5f20 --- /dev/null +++ b/src/engine/platform/sound/dave/dave.cpp @@ -0,0 +1,813 @@ + +// ep128emu -- portable Enterprise 128 emulator +// Copyright (C) 2003-2016 Istvan Varga +// https://github.com/istvan-v/ep128emu/ +// +// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#include "dave.hpp" +#include + +#define EP128EMU_UNLIKELY(x) x + +// Generate polynomial counter of log2(m0) + 1 bits length, and store +// (m0 * 2 - 1) samples at 'tabptr' in reverse order. +// log2(m1) is the second bit to be used in the XOR operation when +// calculating the next bit of output. + +static void calculate_polycnt(uint8_t *tabptr, uint32_t m0, uint32_t m1) +{ + uint32_t sr = 0xFFFFFFFFU; + int n = int(m0 << 1) - 1; + while (--n >= 0) { + uint8_t b0 = uint8_t(bool(sr & m0)); + uint8_t b1 = uint8_t(bool(sr & m1)); + tabptr[n] = b0; + sr = (sr << 1) | uint32_t(b0 ^ b1); + } +} + +namespace Ep128 { + DaveTables::DaveTables() + { + static const uint32_t polycnt_params[14] = { + 0x00000008U, 0x00000004U, // 4-bit: poly = 11001 + 0x00000010U, 0x00000004U, // 5-bit: poly = 101001 + 0x00000040U, 0x00000020U, // 7-bit: poly = 11000001 + 0x00000100U, 0x00000010U, // 9-bit: poly = 1000100001 + 0x00000400U, 0x00000100U, // 11-bit: poly = 101000000001 + 0x00004000U, 0x00002000U, // 15-bit: poly = 1100000000000001 + 0x00010000U, 0x00002000U // 17-bit: poly = 100100000000000001 + }; + polycnt4_table = new uint8_t[15 + 31 + 127 + 511 + 2047 + 32767 + 131071]; + polycnt5_table = &(polycnt4_table[15]); + polycnt7_table = &(polycnt5_table[31]); + polycnt9_table = &(polycnt7_table[127]); + polycnt11_table = &(polycnt9_table[511]); + polycnt15_table = &(polycnt11_table[2047]); + polycnt17_table = &(polycnt15_table[32767]); + uint8_t *bufp = polycnt4_table; + for (int i = 0; i < 14; i += 2) { + calculate_polycnt(bufp, polycnt_params[i], polycnt_params[i + 1]); + bufp = bufp + ((polycnt_params[i] << 1) - 1U); + } + } + + DaveTables::~DaveTables() + { + delete[] polycnt4_table; + } + + // handle timer interrupts + + inline void Dave::triggerIntSnd() + { + // trigger interrupt on edge only + if (int_snd_active) + return; + // mark as active + int_snd_active = 1; + interruptRequest(); + } + + inline void Dave::triggerInt1Hz() + { + // trigger interrupt on edge only + if (int_1hz_active) + return; + // mark as active + int_1hz_active = 1; + interruptRequest(); + } + + // run DAVE emulation, and also trigger any sound or timer interrupts + + uint32_t Dave::runOneCycle_() + { + // update polynomial counters + if (--polycnt4_phase < 0) // 4-bit + polycnt4_phase = 14; + polycnt4_state = (int) t.polycnt4_table[polycnt4_phase]; + if (--polycnt5_phase < 0) // 5-bit + polycnt5_phase = 30; + polycnt5_state = (int) t.polycnt5_table[polycnt5_phase]; + if (!noise_polycnt_is_7bit) { + // channel 3 uses the variable length polynomial counter + if (--polycnt7_phase < 0) // 7-bit + polycnt7_phase = 126; + polycnt7_state = (int) t.polycnt7_table[polycnt7_phase]; + // channel 3 polynomial counter: updated on negative edge + if (*chn3_clk_source < chn3_clk_source_prv) { + if (--polycntVL_phase < 0) // variable length + polycntVL_phase = polycntVL_maxphase; + polycntVL_state = (int) polycntVL_table[polycntVL_phase]; + chn3_state1 = polycntVL_state; // input signal to channel 3 + if (!chn3_lp_2) + chn3_state2 = polycntVL_state; + } + chn3_clk_source_prv = *chn3_clk_source; + } + else { + // channel 3 uses the 7-bit polynomial counter + if (*chn3_clk_source < chn3_clk_source_prv) { + // update on negative edge + if (--polycnt7_phase < 0) // 7-bit + polycnt7_phase = 126; + polycnt7_state = (int) t.polycnt7_table[polycnt7_phase]; + chn3_state1 = polycnt7_state; // input signal to channel 3 + if (!chn3_lp_2) + chn3_state2 = polycnt7_state; + } + chn3_clk_source_prv = *chn3_clk_source; + if (--polycntVL_phase < 0) // variable length + polycntVL_phase = polycntVL_maxphase; + polycntVL_state = (int) polycntVL_table[polycntVL_phase]; + } + + // update the phase of all oscillators + clk_62500_phase--; + clk_1000_phase--; + chn0_phase -= chn0_run; + chn1_phase -= chn1_run; + chn2_phase -= chn2_run; + + // reload phase counters if necessary + if (EP128EMU_UNLIKELY(clk_1000_phase < 0)) { + clk_50_phase--; + // trigger interrupts if enabled + if ((*int_snd_phase) < 0) { + // will reload counter later + int_snd_state = (int_snd_state & 1) ^ 1; // invert state + if (enable_int_snd) + triggerIntSnd(); + } + clk_1000_phase = clk_1000_frq; + if (EP128EMU_UNLIKELY(clk_50_phase < 0)) { + clk_50_phase = clk_50_frq; + clk_1_phase--; + if (EP128EMU_UNLIKELY(clk_1_phase < 0)) { + clk_1_phase = clk_1_frq; // reload counter + int_1hz_state = (int_1hz_state & 1) ^ 1; // invert state + if (enable_int_1hz) + triggerInt1Hz(); + } + } + } + else if (EP128EMU_UNLIKELY((*int_snd_phase) < 0)) { + // trigger interrupt if enabled + int_snd_state = (int_snd_state & 1) ^ 1; // invert state + if (enable_int_snd) + triggerIntSnd(); + } + + // calculate oscillator outputs + if (clk_62500_phase < 0) { + // simple 31250 Hz oscillator + clk_62500_phase = clk_62500_frq; // reload counter + clk_62500_state = (clk_62500_state & 1) ^ 1; // invert state + } + // ---- channel 3 ---- + chn3_prv = chn3_state; // save previous output + if (chn3_lp_2 && (chn2_state < chn2_prv)) { + // lowpass filter holds signal until negative edge in channel 2 + chn3_state2 = chn3_state1; + } + if (chn3_hp_0 && (chn0_state < chn0_prv)) { + // highpass filter: sets level to 0 on negative edge in channel 0 + chn3_state2 = 0; + } + // store final output signal in chn3_state + chn3_state = chn3_state2; + if (chn3_rm_1) { + // ring modulation: XNOR by channel 1 + chn3_state ^= (chn1_state ^ 1); + } + // ---- channel 2 ---- + chn2_prv = chn2_state; // save previous output + if (chn2_phase < 0) { + chn2_phase = chn2_frqcode; // reload counter + if (chn2_input_polycnt == NULL) { + // square wave + chn2_state1 = (chn2_state1 & 1) ^ 1; + } + else { + // get input from polynomial counter + chn2_state1 = *chn2_input_polycnt; + } + } + if (chn2_hp_3 && (chn3_state < chn3_prv)) { + // highpass filter: sets level to 0 on negative edge in channel 3 + chn2_state1 = 0; + } + // store final output signal in chn2_state + chn2_state = chn2_state1; + if (chn2_rm_0) { + // ring modulation: XNOR by channel 0 + chn2_state ^= (chn0_state ^ 1); + } + // ---- channel 1 ---- + chn1_prv = chn1_state; // save previous output + if (chn1_phase < 0) { + chn1_phase = chn1_frqcode; // reload counter + if (chn1_input_polycnt == NULL) { + // square wave + chn1_state1 = (chn1_state1 & 1) ^ 1; + } + else { + // get input from polynomial counter + chn1_state1 = *chn1_input_polycnt; + } + } + if (chn1_hp_2 && (chn2_state < chn2_prv)) { + // highpass filter: sets level to 0 on negative edge in channel 2 + chn1_state1 = 0; + } + // store final output signal in chn1_state + chn1_state = chn1_state1; + if (chn1_rm_3) { + // ring modulation: XNOR by channel 3 + chn1_state ^= (chn3_state ^ 1); + } + // ---- channel 0 ---- + chn0_prv = chn0_state; // save previous output + if (chn0_phase < 0) { + chn0_phase = chn0_frqcode; // reload counter + if (chn0_input_polycnt == NULL) { + // square wave + chn0_state1 = (chn0_state1 & 1) ^ 1; + } + else { + // get input from polynomial counter + chn0_state1 = *chn0_input_polycnt; + } + } + if (chn0_hp_1 && (chn1_state < chn1_prv)) { + // highpass filter: sets level to 0 on negative edge in channel 1 + chn0_state1 = 0; + } + // store final output signal in chn0_state + chn0_state = chn0_state1; + if (chn0_rm_2) { + // ring modulation: XNOR by channel 2 + chn0_state ^= (chn2_state ^ 1); + } + + // and now the final DAC output (left/right) values + // total output range (not including tape feedback): 0 to 252 + unsigned int lval = + ((tape_feedback & tape_input) == 0 ? 0U : 0x3FU); // tape feedback + unsigned int rval = lval; + if (dac_mode_left) { + lval += (chn0_left << 2); + if (dac_mode_right) { + // simplest case: both channels in DAC mode + rval += (chn0_right << 2); + } + else { + // left channel is in DAC mode, but right is not + if (chn0_state) + rval += chn0_right; + if (chn1_state) + rval += chn1_right; + if (chn2_state) + rval += chn2_right; + if (chn3_state) + rval += chn3_right; + } + } + else if (dac_mode_right) { + // right channel is in DAC mode, but left is not + rval += (chn0_right << 2); + if (chn0_state) + lval += chn0_left; + if (chn1_state) + lval += chn1_left; + if (chn2_state) + lval += chn2_left; + if (chn3_state) + lval += chn3_left; + } + else { + // neither channel is in DAC mode + if (chn0_state) { + lval += chn0_left; + rval += chn0_right; + } + if (chn1_state) { + lval += chn1_left; + rval += chn1_right; + } + if (chn2_state) { + lval += chn2_left; + rval += chn2_right; + } + if (chn3_state) { + lval += chn3_left; + rval += chn3_right; + } + } + + audioOutput = uint32_t(lval + (rval << 16)) << 7; + return audioOutput; + } + + // returns pointer to the polynomial counter for channels 0, 1, and 2 + // selected by 'n' (allowed values for 'n' are 0x00, 0x10, 0x20, and 0x30) + + int * Dave::findPolycntForToneChannel(int n) + { + switch (n) { + case 0x10: + return (&polycnt4_state); // 4-bit + case 0x20: + return (&polycnt5_state); // 5-bit + case 0x30: + if (!noise_polycnt_is_7bit) { + return (&polycnt7_state); // 7-bit + } + else { + return (&polycntVL_state); // variable length + } + } + // default to square wave + return (int*) NULL; + } + + // write to DAVE registers + + void Dave::writePort(uint16_t addr, uint8_t value) + { + switch (uint8_t(addr & 0x1F)) { + case 0x00: + // channel 0 frequency + chn0_frqcode = (chn0_frqcode & 0x0F00) | (int) value; + break; + case 0x01: + // channel 0 frequency and mode + chn0_frqcode = (chn0_frqcode & 0x00FF) | (((int) value & 0x0F) << 8); + // select distortion mode + chn0_input_polycnt = findPolycntForToneChannel((int) value & 0x30); + chn0_hp_1 = ((int) value & 0x40 ? 1 : 0); // highpass + chn0_rm_2 = ((int) value & 0x80 ? 1 : 0); // ringmod + break; + case 0x02: + // channel 1 frequency + chn1_frqcode = (chn1_frqcode & 0x0F00) | (int) value; + break; + case 0x03: + // channel 1 frequency and mode + chn1_frqcode = (chn1_frqcode & 0x00FF) | (((int) value & 0x0F) << 8); + // select distortion mode + chn1_input_polycnt = findPolycntForToneChannel((int) value & 0x30); + chn1_hp_2 = ((int) value & 0x40 ? 1 : 0); // highpass + chn1_rm_3 = ((int) value & 0x80 ? 1 : 0); // ringmod + break; + case 0x04: + // channel 2 frequency + chn2_frqcode = (chn2_frqcode & 0x0F00) | (int) value; + break; + case 0x05: + // channel 2 frequency and mode + chn2_frqcode = (chn2_frqcode & 0x00FF) | (((int) value & 0x0F) << 8); + // select distortion mode + chn2_input_polycnt = findPolycntForToneChannel((int) value & 0x30); + chn2_hp_3 = ((int) value & 0x40 ? 1 : 0); // highpass + chn2_rm_0 = ((int) value & 0x80 ? 1 : 0); // ringmod + break; + case 0x06: + // channel 3 parameters + switch ((int) value & 0x03) { + // polynomial counter clock source + case 0x00: chn3_clk_source = &clk_62500_state; break; + case 0x01: chn3_clk_source = &chn0_state; break; + case 0x02: chn3_clk_source = &chn1_state; break; + case 0x03: chn3_clk_source = &chn2_state; break; + } + // select variable length polynomial counter + switch ((int) value & 0x0C) { + case 0x00: + polycntVL_table = t.polycnt17_table; // 17-bit + polycntVL_maxphase = 131070; + break; + case 0x04: + polycntVL_table = t.polycnt15_table; // 15-bit + polycntVL_maxphase = 32766; + break; + case 0x08: + polycntVL_table = t.polycnt11_table; // 11-bit + polycntVL_maxphase = 2046; + break; + case 0x0C: + polycntVL_table = t.polycnt9_table; // 9-bit + polycntVL_maxphase = 510; + break; + } + // wrap the phase of variable length polynomial counter to table length + polycntVL_phase = polycntVL_phase % (polycntVL_maxphase + 1); + // bit 4: swap 7-bit and variable length polynomial counters if set + if ((int) value & 0x10) { + noise_polycnt_is_7bit = 1; + chn3_input_polycnt = &polycnt7_state; + if (chn0_input_polycnt == &polycnt7_state) + chn0_input_polycnt = &polycntVL_state; + if (chn1_input_polycnt == &polycnt7_state) + chn1_input_polycnt = &polycntVL_state; + if (chn2_input_polycnt == &polycnt7_state) + chn2_input_polycnt = &polycntVL_state; + } + else { + noise_polycnt_is_7bit = 0; + chn3_input_polycnt = &polycntVL_state; + if (chn0_input_polycnt == &polycntVL_state) + chn0_input_polycnt = &polycnt7_state; + if (chn1_input_polycnt == &polycntVL_state) + chn1_input_polycnt = &polycnt7_state; + if (chn2_input_polycnt == &polycntVL_state) + chn2_input_polycnt = &polycnt7_state; + } + chn3_lp_2 = ((int) value & 0x20 ? 1 : 0); // lowpass with channel 2 + chn3_hp_0 = ((int) value & 0x40 ? 1 : 0); // highpass with channel 0 + chn3_rm_1 = ((int) value & 0x80 ? 1 : 0); // ring mod. with channel 1 + break; + case 0x07: + // sound/interrupt control register + if ((int) value & 0x01) { + chn0_run = 0; // channel 0 sync + chn0_state1 = 0; + } + else if (!chn0_run) { + chn0_phase = chn0_frqcode; // reset phase + chn0_run = 1; + } + if ((int) value & 0x02) { + chn1_run = 0; // channel 1 sync + chn1_state1 = 0; + } + else if (!chn1_run) { + chn1_phase = chn1_frqcode; // reset phase + chn1_run = 1; + } + if ((int) value & 0x04) { + chn2_run = 0; // channel 2 sync + chn2_state1 = 0; + } + else if (!chn2_run) { + chn2_phase = chn2_frqcode; // reset phase + chn2_run = 1; + } + dac_mode_left = ((int) value & 0x08 ? 1 : 0); // analogue mode + dac_mode_right = ((int) value & 0x10 ? 1 : 0); + switch ((int) value & 0x60) { + // sound interrupt mode + case 0x00: + int_snd_phase = &clk_1000_phase; + break; + case 0x20: + int_snd_phase = &clk_50_phase; + break; + case 0x40: + int_snd_phase = &chn0_phase; + break; + case 0x60: + int_snd_phase = &chn1_phase; + break; + } + break; + case 0x08: + // channel 0 left volume + chn0_left = int(value & 0x3F); + break; + case 0x09: + // channel 1 left volume + chn1_left = int(value & 0x3F); + break; + case 0x0A: + // channel 2 left volume + chn2_left = int(value & 0x3F); + break; + case 0x0B: + // channel 3 left volume + chn3_left = int(value & 0x3F); + break; + case 0x0C: + // channel 0 right volume + chn0_right = int(value & 0x3F); + break; + case 0x0D: + // channel 1 right volume + chn1_right = int(value & 0x3F); + break; + case 0x0E: + // channel 2 right volume + chn2_right = int(value & 0x3F); + break; + case 0x0F: + // channel 3 right volume + chn3_right = int(value & 0x3F); + break; + case 0x10: + // memory page 0 + page0Segment = value; + setMemoryPage(0, value); + break; + case 0x11: + // memory page 1 + page1Segment = value; + setMemoryPage(1, value); + break; + case 0x12: + // memory page 2 + page2Segment = value; + setMemoryPage(2, value); + break; + case 0x13: + // memory page 3 + page3Segment = value; + setMemoryPage(3, value); + break; + case 0x14: + // interrupt control register + { + int prv = (int_snd_active | int_1hz_active + | int_1_active | int_2_active); + uint8_t tmp = (uint8_t) value ^ (uint8_t) 0x55; + // sound/timer interrupt + enable_int_snd = (tmp & (uint8_t) 0x01 ? 0 : 1); + if (tmp & (uint8_t) 0x03) + int_snd_active = 0; + // 1 Hz interrupt + enable_int_1hz = (tmp & (uint8_t) 0x04 ? 0 : 1); + if (tmp & (uint8_t) 0x0C) + int_1hz_active = 0; + // INT 1 (video interrupt) + enable_int_1 = (tmp & (uint8_t) 0x10 ? 0 : 1); + if (tmp & (uint8_t) 0x30) + int_1_active = 0; + // INT 2 + enable_int_2 = (tmp & (uint8_t) 0x40 ? 0 : 1); + if (tmp & (uint8_t) 0xC0) + int_2_active = 0; + if (prv && !(int_snd_active | int_1hz_active + | int_1_active | int_2_active)) { + // no more active interrupts: clear request to CPU + clearInterruptRequest(); + } + } + break; + case 0x15: + // select keyboard row + keyboardRow = int(value & 0x0F); + tape_feedback = int(!(value & 0x20)); // tape control + setRemote1State(value & 0x40 ? 1 : 0); + setRemote2State(value & 0x80 ? 1 : 0); + break; + case 0x1F: // system configuration register + { + // CPU wait cycle control + setMemoryWaitMode(int(value & 0x0C) >> 2); + // input clock frequency (note: the frequency is always 8 MHz, + // this bit sets the assumed value) + // 0: 8 MHz, dave_clock_freq = input_freq / 32 + // 1: 12 MHz, dave_clock_freq = input_freq / 48 + clockDiv = ((value & 0x02) == 0 ? 2 : 3); + } + break; + } + } + + // read from DAVE registers + + uint8_t Dave::readPort(uint16_t addr) const + { + switch (uint8_t(addr & 0x1F)) { + case 0x10: + return page0Segment; + case 0x11: + return page1Segment; + case 0x12: + return page2Segment; + case 0x13: + return page3Segment; + case 0x14: + { + // interrupt state + return uint8_t((int_snd_state | (int_snd_active << 1)) + | ((int_1hz_state | (int_1hz_active << 1)) << 2) + | ((int_1_state | (int_1_active << 1)) << 4) + | ((int_2_state | (int_2_active << 1)) << 6)); + } + break; + case 0x15: + // read currently selected keyboard row + return (keyboardRow < 10 ? keyboardState[keyboardRow] : 0xFF); + case 0x16: + { + // tape input + uint8_t n = + uint8_t(((tape_input_level - 1) & 0x40) | ((tape_input - 1) & 0x80) + | 0x0F); + if (keyboardRow < 5) { + if (keyboardRow == 0) { + // EnterMice buttons (left and right) + n &= uint8_t(0xF9 | (mouseInput >> 3)); + // EXT1 joystick fire buttons + n &= uint8_t(0xF8 | (keyboardState[14] >> 4)); + } + else { + if (!(mouseInput & 0x80)) // EnterMice data input on column K + n &= uint8_t(0xFD | ((mouseInput << 1) >> (keyboardRow - 1))); + // EXT1 joystick (mapped to row 14) + n &= uint8_t(0xFE | (keyboardState[14] >> (4 - keyboardRow))); + } + } + else if (keyboardRow < 10) { + // external joystick 2 (mapped to keyboard row 15) + if (keyboardRow == 5) // fire buttons + n &= uint8_t(0xF8 | (keyboardState[15] >> 4)); + else + n &= uint8_t(0xFE | (keyboardState[15] >> (9 - keyboardRow))); + } + return n; + } + } + // anything else is either handled elsewhere, or is write-only + return 0xFF; + } + + // set hardware interrupt 1 state, and (possibly) trigger interrupt + + void Dave::setInt1State(int new_state) + { + int prv = int_1_state; + // set new state + int_1_state = (new_state ? 1 : 0); + // on negative edge, trigger CPU interrupt + // (assuming it is enabled, and not active already) + if (!enable_int_1) + return; // disabled + if (int_1_state || !prv) + return; // not on negative edge + if (int_1_active) + return; // already active + // now active + int_1_active = 1; + // send request to CPU + interruptRequest(); + } + + // set hardware interrupt 2 state, and (possibly) trigger interrupt + + void Dave::setInt2State(int new_state) + { + int prv = int_2_state; + // set new state + int_2_state = (new_state ? 1 : 0); + // on negative edge, trigger CPU interrupt + // (assuming it is enabled, and not active already) + if (!enable_int_2) + return; // disabled + if (int_2_state || !prv) + return; // not on negative edge + if (int_2_active) + return; // already active + // now active + int_2_active = 1; + // send request to CPU + interruptRequest(); + } + + Dave::Dave() + { + clockDiv = 2; + clockCnt = 1; + polycnt4_state = 0; + polycnt5_state = 0; + polycnt7_state = 0; + polycntVL_state = 0; + clk_62500_state = 0; + chn0_state = 0; + chn0_prv = 0; + chn0_state1 = 0; + chn0_frqcode = 0; + chn0_run = 1; + chn1_state = 0; + chn1_prv = 0; + chn1_state1 = 0; + chn1_frqcode = 0; + chn1_run = 1; + chn2_state = 0; + chn2_prv = 0; + chn2_state1 = 0; + chn2_frqcode = 0; + chn2_run = 1; + chn3_state = 0; + chn3_prv = 0; + chn3_state1 = 0; + chn3_state2 = 0; + chn3_clk_source_prv = 0; + int_snd_state = 0; + int_1hz_state = 0; + int_1_state = 1; + int_2_state = 1; + int_snd_active = 0; + int_1hz_active = 0; + int_1_active = 0; + int_2_active = 0; + audioOutput = 0; + tape_input = 0; + tape_input_level = 0; + this->reset(true); + } + + Dave::~Dave() + { + } + + void Dave::reset(bool isColdReset) + { + polycnt4_phase = 0; + polycnt5_phase = 0; + polycnt7_phase = 0; + polycntVL_phase = 0; + chn0_phase = 0; + chn1_phase = 0; + chn2_phase = 0; + clk_62500_phase = 0; + clk_1000_phase = 0; + clk_50_phase = 0; + clk_1_phase = 0; + // initialize registers + // this will also reset many variables to the default value + for (uint16_t i = 0x00; i < 0x20; i++) + writePort(i, 0); + // clear all interrupts + writePort(0x14, 0xAA); + if (isColdReset) { + // reset keyboard state + for (int i = 0; i < 16; i++) + keyboardState[i] = 0xFF; + } + mouseInput = 0xFF; + } + + void Dave::setTapeInput(int state, int level) + { + tape_input = (state ? 1 : 0); + tape_input_level = (level > 0 ? 1 : 0); + } + + void Dave::setKeyboardState(int keyCode, int state) + { + int row = (keyCode & 0x78) >> 3; + uint8_t mask = uint8_t(1 << (keyCode & 0x07)); + if (!state) + keyboardState[row] |= mask; + else + keyboardState[row] &= (~mask); + } + + // -------------------------------------------------------------------------- + + void Dave::setMemoryPage(uint8_t page, uint8_t segment) + { + (void) page; + (void) segment; + } + + void Dave::setMemoryWaitMode(int mode) + { + (void) mode; + } + + void Dave::setRemote1State(int state) + { + (void) state; + } + + void Dave::setRemote2State(int state) + { + (void) state; + } + + void Dave::interruptRequest() + { + } + + void Dave::clearInterruptRequest() + { + } + +} // namespace Ep128 + diff --git a/src/engine/platform/sound/dave/dave.hpp b/src/engine/platform/sound/dave/dave.hpp new file mode 100644 index 000000000..8e44ca49a --- /dev/null +++ b/src/engine/platform/sound/dave/dave.hpp @@ -0,0 +1,251 @@ + +// ep128emu -- portable Enterprise 128 emulator +// Copyright (C) 2003-2016 Istvan Varga +// https://github.com/istvan-v/ep128emu/ +// +// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +#ifndef EP128EMU_DAVE_HPP +#define EP128EMU_DAVE_HPP + +#include + +namespace Ep128 { + + class DaveTables { + public: + // tables for polynomial counters + // (note: the table data is stored in reverse order) + uint8_t *polycnt4_table; // length = 15, poly = 0x0000000C + uint8_t *polycnt5_table; // length = 31, poly = 0x00000014 + uint8_t *polycnt7_table; // length = 127, poly = 0x00000060 + uint8_t *polycnt9_table; // length = 511, poly = 0x00000110 + uint8_t *polycnt11_table; // length = 2047, poly = 0x00000500 + uint8_t *polycnt15_table; // length = 32767, poly = 0x00006000 + uint8_t *polycnt17_table; // length = 131071, poly = 0x00012000 + // ---------------- + DaveTables(); + ~DaveTables(); + }; + + class Dave { + private: + DaveTables t; + int clockDiv; // 2 if bit 1 of port 0xBF is 0, 3 otherwise + int clockCnt; // counts from 'clockDiv' towards zero + // variable length counter uses one of the 9, 11, 15, and 17 bit tables + uint8_t *polycntVL_table; + // polynomial counters + int polycnt4_phase; // 4-bit counter phase (14 -> 0) + int polycnt5_phase; // 5-bit counter phase (30 -> 0) + int polycnt7_phase; // 7-bit counter phase (126 -> 0) + int polycntVL_phase; // variable length counter phase ... + int polycntVL_maxphase; // ... counts from this value to zero + int polycnt4_state; // 4-bit counter output + int polycnt5_state; // 5-bit counter output + int polycnt7_state; // 7-bit counter output + int polycntVL_state; // variable length counter output + // fixed frequency counters (f = 250000 / (n + 1)) + static const int clk_62500_frq = 3; + static const int clk_1000_frq = 249; + static const int clk_50_frq = 19; // clocked by the 1 kHz counter + static const int clk_1_frq = 49; // clocked by the 50 Hz counter + int clk_62500_phase; + int clk_1000_phase; + int clk_50_phase; + int clk_1_phase; + int clk_62500_state; + // channel 0 parameters + int chn0_state; // current output state + int chn0_prv; // previous output state + int chn0_state1; // oscillator output + int chn0_phase; // phase (frqcode -> 0) + int chn0_frqcode; // frequency code (0 - 4095) + int *chn0_input_polycnt; // polynomial counter + int chn0_hp_1; // enable highpass filter + int chn0_rm_2; // enable ring modulation + int chn0_run; // 1: oscillator is running + int chn0_left; // left volume (0 - 63) + int chn0_right; // right volume (0 - 63) + // channel 1 parameters + int chn1_state; // current output state + int chn1_prv; // previous output state + int chn1_state1; // oscillator output + int chn1_phase; // phase (frqcode -> 0) + int chn1_frqcode; // frequency code (0 - 4095) + int *chn1_input_polycnt; // polynomial counter + int chn1_hp_2; // enable highpass filter + int chn1_rm_3; // enable ring modulation + int chn1_run; // 1: oscillator is running + int chn1_left; // left volume (0 - 63) + int chn1_right; // right volume (0 - 63) + // channel 2 parameters + int chn2_state; // current output state + int chn2_prv; // previous output state + int chn2_state1; // oscillator output + int chn2_phase; // phase (frqcode -> 0) + int chn2_frqcode; // frequency code (0 - 4095) + int *chn2_input_polycnt; // polynomial counter + int chn2_hp_3; // enable highpass filter + int chn2_rm_0; // enable ring modulation + int chn2_run; // 1: oscillator is running + int chn2_left; // left volume (0 - 63) + int chn2_right; // right volume (0 - 63) + // channel 3 (noise) parameters + int chn3_state; // current output state + int chn3_prv; // previous output state + int chn3_state1; // polynomial counter output + int chn3_state2; // lowpass filter output + int *chn3_clk_source; // clock input signal + int chn3_clk_source_prv; // previous clock input + int noise_polycnt_is_7bit; // 0xA6 port bit 4 + int *chn3_input_polycnt; // polynomial counter + int chn3_lp_2; // enable lowpass filter + int chn3_hp_0; // enable highpass filter + int chn3_rm_1; // enable ring modulation + int chn3_left; // left volume (0 - 63) + int chn3_right; // right volume (0 - 63) + // enable DAC mode for left/right channel + int dac_mode_left; + int dac_mode_right; + // interrupts + int *int_snd_phase; + int enable_int_snd; + int enable_int_1hz; + int enable_int_1; + int enable_int_2; + int int_snd_state; + int int_1hz_state; + int int_1_state; + int int_2_state; + int int_snd_active; + int int_1hz_active; + int int_1_active; + int int_2_active; + uint32_t audioOutput; + uint8_t page0Segment; + uint8_t page1Segment; + uint8_t page2Segment; + uint8_t page3Segment; + int tape_feedback; + int tape_input; + int tape_input_level; + int keyboardRow; + uint8_t keyboardState[16]; + // b0..b3 = current nibble + // b4 = button 1 (left) state (0 = pressed) + // b5 = button 2 (right) state + uint8_t mouseInput; // b7 == 1: mouse data bits inactive + // ---------------- + inline void triggerIntSnd(); + inline void triggerInt1Hz(); + int * findPolycntForToneChannel(int n); + uint32_t runOneCycle_(); + public: + Dave(); + virtual ~Dave(); + protected: + virtual void setMemoryPage(uint8_t page, uint8_t segment); + virtual void setMemoryWaitMode(int mode); + virtual void setRemote1State(int state); + virtual void setRemote2State(int state); + virtual void interruptRequest(); + virtual void clearInterruptRequest(); + public: + /*! + * Run DAVE emulation for 2 us (clock frequency = 500 kHz). + * Return value is audio output in left_channel + (right_channel << 16) + * format, where the range for a single channel is 0 to 40320 (sum of 4 + * sound generators and tape feedback, 0 to 8064 each). + */ + inline uint32_t runOneCycle() + { + if (--clockCnt > 0) + return audioOutput; + clockCnt = clockDiv; + return runOneCycle_(); + } + /*! + * Write to a DAVE register. + */ + void writePort(uint16_t addr, uint8_t value); + /*! + * Read from a DAVE register. + */ + uint8_t readPort(uint16_t addr) const; + /*! + * Set hardware interrupt 1 state, and (possibly) trigger interrupt. + */ + void setInt1State(int new_state); + /*! + * Set hardware interrupt 2 state, and (possibly) trigger interrupt. + */ + void setInt2State(int new_state); + /*! + * Set tape input state, and level (0: low, 1: high). + */ + void setTapeInput(int state, int level); + /*! + * Set state of key 'keyCode' (0 to 127, see table below) to pressed + * (state != 0) or released (state == 0). + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | 0x05 | 0x06 | 0x07 | + * | 0x08 | 0x09 | 0x0A | 0x0B | 0x0C | 0x0D | 0x0E | 0x0F | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x00 | N | \ | B | C | V | X | Z | SHF_L | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x08 | H | LOCK | G | D | F | S | A | CTRL | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x10 | U | Q | Y | R | T | E | W | TAB | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x18 | 7 | 1 | 6 | 4 | 5 | 3 | 2 | ESC | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x20 | F4 | F8 | F3 | F6 | F5 | F7 | F2 | F1 | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x28 | 8 | | 9 | - | 0 | ^ | ERASE | | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x30 | J | | K | ; | L | : | ] | | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x38 | STOP | DOWN | RIGHT | UP | HOLD | LEFT | ENTER | ALT | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x40 | M | DEL | , | / | . | SHF_R | SPACE | INS | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x48 | I | | O | @ | P | [ | | | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x70 | JOY1R | JOY1L | JOY1D | JOY1U | JOY1F | JOY1F2| JOY1F3| | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + * | 0x78 | JOY2R | JOY2L | JOY2D | JOY2U | JOY2F | JOY2F2| JOY2F3| | + * +------+-------+-------+-------+-------+-------+-------+-------+-------+ + */ + void setKeyboardState(int keyCode, int state); + inline void setMouseInput(uint8_t value) + { + mouseInput = value; + } + inline void clearMouseInput() + { + // clear data bits, but not the buttons + mouseInput = mouseInput | 0xCF; + } + /*! + * Reset DAVE. + */ + void reset(bool isColdReset = false); + }; + +} // namespace Ep128 + +#endif // EP128EMU_DAVE_HPP + From 78bbc59c75cb253d59343089f18c10d95477b115 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Feb 2024 17:30:19 -0500 Subject: [PATCH 2/6] try using -gcodeview I thought GCC was unable to generate .pdb --- CMakeLists.txt | 7 +++ src/engine/instrument.cpp | 2 + src/engine/instrument.h | 1 + src/engine/platform/dave.cpp | 95 +----------------------------------- src/engine/song.h | 1 + src/engine/sysDef.cpp | 15 ++++++ src/gui/gui.h | 1 + src/gui/guiConst.cpp | 10 ++-- src/icon/furIcons.h | 3 +- 9 files changed, 35 insertions(+), 100 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f12a98629..74d2c2ea5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -708,6 +708,7 @@ src/engine/platform/ted.cpp src/engine/platform/c140.cpp src/engine/platform/esfm.cpp src/engine/platform/powernoise.cpp +src/engine/platform/dave.cpp src/engine/platform/pcmdac.cpp src/engine/platform/dummy.cpp @@ -918,6 +919,12 @@ set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} ${CLI_SOURCES} src/main.cpp) if (USE_BACKWARD) list(APPEND USED_SOURCES src/backtrace.cpp) if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-gcodeview GCC_CODEVIEW) + if (GCC_CODEVIEW) + set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") + add_compile_options(-gcodeview) + endif() list(APPEND DEPENDENCIES_LIBRARIES dbghelp psapi) endif() find_library(EXECINFO_IS_LIBRARY execinfo) diff --git a/src/engine/instrument.cpp b/src/engine/instrument.cpp index 990af8da3..31be38638 100644 --- a/src/engine/instrument.cpp +++ b/src/engine/instrument.cpp @@ -1062,6 +1062,8 @@ void DivInstrument::putInsData2(SafeWriter* w, bool fui, const DivSong* song, bo case DIV_INS_POWERNOISE_SLOPE: featurePN=true; break; + case DIV_INS_DAVE: + break; case DIV_INS_MAX: break; case DIV_INS_NULL: diff --git a/src/engine/instrument.h b/src/engine/instrument.h index 80bd8f251..14cc1cfec 100644 --- a/src/engine/instrument.h +++ b/src/engine/instrument.h @@ -88,6 +88,7 @@ enum DivInstrumentType: unsigned short { DIV_INS_ESFM=55, DIV_INS_POWERNOISE=56, DIV_INS_POWERNOISE_SLOPE=57, + DIV_INS_DAVE=58, DIV_INS_MAX, DIV_INS_NULL }; diff --git a/src/engine/platform/dave.cpp b/src/engine/platform/dave.cpp index c98469b36..3adb368af 100644 --- a/src/engine/platform/dave.cpp +++ b/src/engine/platform/dave.cpp @@ -24,15 +24,6 @@ //#define rWrite(a,v) pendingWrites[a]=v; #define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } -#define chWrite(c,a,v) \ - if (!skipRegisterWrites) { \ - if (curChan!=c) { \ - curChan=c; \ - rWrite(0,curChan); \ - } \ - regPool[16+((c)<<4)+((a)&0x0f)]=v; \ - rWrite(a,v); \ - } #define CHIP_DIVIDER 32 @@ -73,16 +64,8 @@ void DivPlatformDave::acquire(short** buf, size_t len) { chan[i].dacSample=-1; continue; } - chWrite(i,0x07,0); signed char dacData=((signed char)((unsigned char)s->data8[chan[i].dacPos]^0x80))>>3; chan[i].dacOut=CLAMP(dacData,-16,15); - if (!isMuted[i]) { - chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].outVol)); - chWrite(i,0x06,chan[i].dacOut&0x1f); - } else { - chWrite(i,0x04,0xc0); - chWrite(i,0x06,0x10); - } chan[i].dacPos++; if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->loopEnd) { chan[i].dacPos=s->loopStart; @@ -95,22 +78,11 @@ void DivPlatformDave::acquire(short** buf, size_t len) { } // PCE part - cycles=0; - while (!writes.empty() && cycles<24) { + while (!writes.empty()) { QueuedWrite w=writes.front(); - pce->Write(cycles,w.addr,w.val); - regPool[w.addr&0x0f]=w.val; - //cycles+=2; + regPool[w.addr&0x1f]=w.val; writes.pop(); } - memset(tempL,0,24*sizeof(int)); - memset(tempR,0,24*sizeof(int)); - pce->Update(24); - pce->ResetTS(0); - - for (int i=0; i<6; i++) { - oscBuf[i]->data[oscBuf[i]->needle++]=CLAMP(pce->channel[i].blip_prev_samp[0]+pce->channel[i].blip_prev_samp[1],-32768,32767); - } tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); tempR[0]=(tempR[0]>>1)+(tempR[0]>>2); @@ -126,11 +98,6 @@ void DivPlatformDave::acquire(short** buf, size_t len) { } } -// TODO: in octave 6 the noise table changes to a tonal one -static unsigned char noiseFreq[12]={ - 4,13,15,18,21,23,25,27,29,31,0,2 -}; - void DivPlatformDave::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); @@ -139,7 +106,6 @@ void DivPlatformDave::tick(bool sysTick) { if (chan[i].furnaceDac && chan[i].pcm) { // ignore for now } else { - chWrite(i,0x04,0x80|chan[i].outVol); } } if (chan[i].std.duty.had && i>=4) { @@ -169,7 +135,6 @@ void DivPlatformDave::tick(bool sysTick) { chan[i].pan|=chan[i].std.panR.val&15; } if (chan[i].std.panL.had || chan[i].std.panR.had) { - chWrite(i,0x05,isMuted[i]?0:chan[i].pan); } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -185,8 +150,6 @@ void DivPlatformDave::tick(bool sysTick) { if (chan[i].active && chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { chan[i].dacPos=0; chan[i].dacPeriod=0; - chWrite(i,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[i].vol)); - addWrite(0xffff0000+(i<<8),chan[i].dacSample); chan[i].keyOn=true; } } @@ -205,27 +168,13 @@ void DivPlatformDave::tick(bool sysTick) { } } chan[i].dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); - if (dumpWrites) addWrite(0xffff0001+(i<<8),chan[i].dacRate); } if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>4095) chan[i].freq=4095; - chWrite(i,0x02,chan[i].freq&0xff); - chWrite(i,0x03,chan[i].freq>>8); - if (i>=4) { - int noiseSeek=(chan[i].fixedArp?chan[i].baseNoteOverride:(chan[i].note+chan[i].arpOff))+chan[i].pitch2; - if (!parent->song.properNoiseLayout && noiseSeek<0) noiseSeek=0; - if (!NEW_ARP_STRAT) { - noiseSeek=chan[i].noiseSeek; - } - chWrite(i,0x07,chan[i].noise?(0x80|(parent->song.properNoiseLayout?(noiseSeek&31):noiseFreq[noiseSeek%12])):0); - } if (chan[i].keyOn) { - //rWrite(16+i*5,0x80); - //chWrite(i,0x04,0x80|chan[i].vol); } if (chan[i].keyOff) { - chWrite(i,0x04,0); } if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; @@ -270,8 +219,6 @@ int DivPlatformDave::dispatch(DivCommand c) { break; } else { if (dumpWrites) { - chWrite(c.chan,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol)); - addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dacSample); } } chan[c.chan].dacPos=0; @@ -307,8 +254,6 @@ int DivPlatformDave::dispatch(DivCommand c) { chan[c.chan].dacPeriod=0; chan[c.chan].dacRate=parent->getSample(chan[c.chan].dacSample)->rate; if (dumpWrites) { - chWrite(c.chan,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[c.chan].vol)); - addWrite(0xffff0001+(c.chan<<8),chan[c.chan].dacRate); } } break; @@ -319,12 +264,9 @@ int DivPlatformDave::dispatch(DivCommand c) { chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); chan[c.chan].freqChanged=true; chan[c.chan].note=c.value; - chan[c.chan].noiseSeek=c.value; - if (chan[c.chan].noiseSeek<0) chan[c.chan].noiseSeek=0; } chan[c.chan].active=true; chan[c.chan].keyOn=true; - chWrite(c.chan,0x04,0x80|chan[c.chan].vol); chan[c.chan].macroInit(ins); if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; @@ -356,7 +298,6 @@ int DivPlatformDave::dispatch(DivCommand c) { if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; if (chan[c.chan].active && !chan[c.chan].pcm) { - chWrite(c.chan,0x04,0x80|chan[c.chan].outVol); } } } @@ -410,22 +351,8 @@ int DivPlatformDave::dispatch(DivCommand c) { } break; } - case DIV_CMD_STD_NOISE_MODE: - chan[c.chan].noise=c.value; - chan[c.chan].freqChanged=true; - break; - case DIV_CMD_SAMPLE_MODE: - chan[c.chan].pcm=c.value; - break; - case DIV_CMD_SAMPLE_BANK: - sampleBank=c.value; - if (sampleBank>(parent->song.sample.size()/12)) { - sampleBank=parent->song.sample.size()/12; - } - break; case DIV_CMD_PANNING: { chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); - chWrite(c.chan,0x05,isMuted[c.chan]?0:chan[c.chan].pan); break; } case DIV_CMD_LEGATO: @@ -460,10 +387,7 @@ int DivPlatformDave::dispatch(DivCommand c) { void DivPlatformDave::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - chWrite(ch,0x05,isMuted[ch]?0:chan[ch].pan); if (!isMuted[ch] && (chan[ch].pcm && chan[ch].dacSample!=-1)) { - chWrite(ch,0x04,parent->song.disableSampleMacro?0xdf:(0xc0|chan[ch].outVol)); - chWrite(ch,0x06,chan[ch].dacOut&0x1f); } } @@ -471,8 +395,6 @@ void DivPlatformDave::forceIns() { for (int i=0; i<6; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; - updateWave(i); - chWrite(i,0x05,isMuted[i]?0:chan[i].pan); } } @@ -543,7 +465,6 @@ void DivPlatformDave::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } - pce->Power(0); lastPan=0xff; memset(tempL,0,32*sizeof(int)); memset(tempR,0,32*sizeof(int)); @@ -559,7 +480,6 @@ void DivPlatformDave::reset() { updateLFO=true; // set per-channel initial panning for (int i=0; i<6; i++) { - chWrite(i,0x05,isMuted[i]?0:chan[i].pan); } delay=500; } @@ -586,12 +506,6 @@ void DivPlatformDave::setFlags(const DivConfig& flags) { for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; } - - if (pce!=NULL) { - delete pce; - pce=NULL; - } - pce=new PCE_PSG(tempL,tempR,flags.getInt("chipType",0)?PCE_PSG::REVISION_HUC6280A:PCE_PSG::REVISION_HUC6280); } void DivPlatformDave::poke(unsigned int addr, unsigned short val) { @@ -611,7 +525,6 @@ int DivPlatformDave::init(DivEngine* p, int channels, int sugRate, const DivConf isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; } - pce=NULL; setFlags(flags); reset(); return 6; @@ -621,10 +534,6 @@ void DivPlatformDave::quit() { for (int i=0; i<6; i++) { delete oscBuf[i]; } - if (pce!=NULL) { - delete pce; - pce=NULL; - } } DivPlatformDave::~DivPlatformDave() { diff --git a/src/engine/song.h b/src/engine/song.h index a4b60238a..932e3924d 100644 --- a/src/engine/song.h +++ b/src/engine/song.h @@ -134,6 +134,7 @@ enum DivSystem { DIV_SYSTEM_C219, DIV_SYSTEM_ESFM, DIV_SYSTEM_POWERNOISE, + DIV_SYSTEM_DAVE, }; enum DivEffectType: unsigned short { diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 65b4cf1f0..684226515 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -2002,6 +2002,21 @@ void DivEngine::registerSystems() { {} ); + sysDefs[DIV_SYSTEM_DAVE]=new DivSysDef( + "Dave", NULL, 0xd5, 0, 6, false, true, 0, false, 0, 0, 0, + "this chip was featured in the Enterprise 128 computer. it is similar to POKEY, but with stereo output.", + {"Channel 1", "Channel 2", "Channel 3", "Noise", "DAC Left", "DAC Right"}, + {"CH1", "CH2", "CH3", "NO", "L", "R"}, + {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_NOISE, DIV_CH_PCM, DIV_CH_PCM}, + {DIV_INS_DAVE, DIV_INS_DAVE, DIV_INS_DAVE, DIV_INS_DAVE, DIV_INS_AMIGA, DIV_INS_AMIGA}, + {}, + { + {0x10, {DIV_CMD_WAVE, "10xx: Set waveform (0 to 7)"}}, + {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set AUDCTL"}}, + {0x12, {DIV_CMD_STD_NOISE_FREQ, "12xx: Toggle two-tone mode"}}, + } + ); + sysDefs[DIV_SYSTEM_DUMMY]=new DivSysDef( "Dummy System", NULL, 0xfd, 0, 8, false, true, 0, false, 0, 0, 0, "this is a system designed for testing purposes.", diff --git a/src/gui/gui.h b/src/gui/gui.h index badd269ed..2c7966e4a 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -294,6 +294,7 @@ enum FurnaceGUIColors { GUI_COLOR_INSTR_ESFM, GUI_COLOR_INSTR_POWERNOISE, GUI_COLOR_INSTR_POWERNOISE_SLOPE, + GUI_COLOR_INSTR_DAVE, GUI_COLOR_INSTR_UNKNOWN, GUI_COLOR_CHANNEL_BG, diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 039bfd5bc..70f2eaa73 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -178,6 +178,7 @@ const char* insTypes[DIV_INS_MAX+1][3]={ {"FM (ESFM)",ICON_FA_AREA_CHART,ICON_FUR_INS_ESFM}, {"PowerNoise (noise)",ICON_FUR_NOISE,ICON_FUR_INS_POWERNOISE}, {"PowerNoise (slope)",ICON_FUR_SAW,ICON_FUR_INS_POWERNOISE_SAW}, + {"Dave",ICON_FA_BAR_CHART,ICON_FUR_INS_DAVE}, {NULL,ICON_FA_QUESTION,ICON_FA_QUESTION} }; @@ -991,6 +992,7 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={ D(GUI_COLOR_INSTR_ESFM,"",ImVec4(0.1f,0.9f,1.0f,1.0f)), D(GUI_COLOR_INSTR_POWERNOISE,"",ImVec4(1.0f,1.0f,0.8f,1.0f)), D(GUI_COLOR_INSTR_POWERNOISE_SLOPE,"",ImVec4(1.0f,0.6f,0.3f,1.0f)), + D(GUI_COLOR_INSTR_DAVE,"",ImVec4(0.7f,0.7f,0.8f,1.0f)), 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)), @@ -1244,6 +1246,7 @@ const int chipsFM[]={ DIV_SYSTEM_OPL3_DRUMS, DIV_SYSTEM_OPZ, DIV_SYSTEM_ESFM, + DIV_SYSTEM_DAVE, 0 // don't remove this last one! }; @@ -1299,6 +1302,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_MMC5, DIV_SYSTEM_SM8521, DIV_SYSTEM_POWERNOISE, + DIV_SYSTEM_DAVE, 0 // don't remove this last one! }; @@ -1343,9 +1347,3 @@ const char* chipCategoryNames[]={ "Sample", NULL }; - -// NORMAL, LETTER, -const char* insIcons[]={ - ICON_FA_AREA_CHART, - -}; diff --git a/src/icon/furIcons.h b/src/icon/furIcons.h index fb1c8a7b5..fe70ba1f5 100644 --- a/src/icon/furIcons.h +++ b/src/icon/furIcons.h @@ -1,7 +1,7 @@ // not auto-generated. update every time you change icons.ttf! #define ICON_MIN_FUR 0xe0f0 -#define ICON_MAX_FUR 0xe15b +#define ICON_MAX_FUR 0xe15c // test #define ICON_FUR_TEST0 u8"\ue0f0" @@ -69,6 +69,7 @@ #define ICON_FUR_INS_ESFM u8"\ue143" #define ICON_FUR_INS_POWERNOISE u8"\ue15a" #define ICON_FUR_INS_POWERNOISE_SAW u8"\ue15b" +#define ICON_FUR_INS_DAVE u8"\ue15c" // sample editor #define ICON_FUR_SAMPLE_APPLY_SILENCE u8"\ue136" From 1ea36ba13f09bbbb929fb97ae7b8fda264b71616 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Feb 2024 17:46:09 -0500 Subject: [PATCH 3/6] warn on lack of gcodeview --- CMakeLists.txt | 3 +++ scripts/release-win64.sh | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d2c2ea5..3c4d4eca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -924,6 +924,9 @@ if (USE_BACKWARD) if (GCC_CODEVIEW) set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") add_compile_options(-gcodeview) + message(STATUS "Enabling -gcodeview flag for backward-cpp.") + else() + message(WARNING "Could not enable -gcodeview! backward-cpp will not work.") endif() list(APPEND DEPENDENCIES_LIBRARIES dbghelp psapi) endif() diff --git a/scripts/release-win64.sh b/scripts/release-win64.sh index edd4007fc..8ec97a258 100755 --- a/scripts/release-win64.sh +++ b/scripts/release-win64.sh @@ -15,7 +15,7 @@ fi cd winbuild # TODO: potential Arch-ism? -x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Wno-deprecated-declarations -Werror" .. || exit 1 +x86_64-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -Wno-deprecated-declarations -Werror" -DUSE_BACKWARD=ON .. || exit 1 make -j8 || exit 1 cd .. From d7d6067d5893adb404ffd8a6bc46a677ad286b81 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Feb 2024 18:11:04 -0500 Subject: [PATCH 4/6] set -gcodeview on Clang as well since it works better there --- CMakeLists.txt | 22 ++++++++++++---------- src/engine/dispatchContainer.cpp | 4 ++++ src/gui/guiConst.cpp | 2 +- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c4d4eca4..2fcb74921 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -918,17 +918,19 @@ set(USED_SOURCES ${ENGINE_SOURCES} ${AUDIO_SOURCES} ${CLI_SOURCES} src/main.cpp) if (USE_BACKWARD) list(APPEND USED_SOURCES src/backtrace.cpp) - if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag(-gcodeview GCC_CODEVIEW) - if (GCC_CODEVIEW) - set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") - add_compile_options(-gcodeview) - message(STATUS "Enabling -gcodeview flag for backward-cpp.") - else() - message(WARNING "Could not enable -gcodeview! backward-cpp will not work.") + if (WIN32) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag(-gcodeview GCC_CODEVIEW) + if (GCC_CODEVIEW) + set(CMAKE_EXE_LINKER_FLAGS "-Wl,--pdb= ") + add_compile_options(-gcodeview) + message(STATUS "Enabling -gcodeview flag for backward-cpp.") + else() + message(WARNING "Could not enable -gcodeview! backward-cpp will not work.") + endif() + list(APPEND DEPENDENCIES_LIBRARIES dbghelp psapi) endif() - list(APPEND DEPENDENCIES_LIBRARIES dbghelp psapi) endif() find_library(EXECINFO_IS_LIBRARY execinfo) if (EXECINFO_IS_LIBRARY) diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index 06049a69f..8118c4a54 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -84,6 +84,7 @@ #include "platform/pcmdac.h" #include "platform/esfm.h" #include "platform/powernoise.h" +#include "platform/dave.h" #include "platform/dummy.h" #include "../ta-log.h" #include "song.h" @@ -652,6 +653,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_POWERNOISE: dispatch=new DivPlatformPowerNoise; break; + case DIV_SYSTEM_DAVE: + dispatch=new DivPlatformDave; + break; case DIV_SYSTEM_DUMMY: dispatch=new DivPlatformDummy; break; diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 70f2eaa73..eaa3996b2 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -1211,6 +1211,7 @@ const int availableSystems[]={ DIV_SYSTEM_ESFM, DIV_SYSTEM_PONG, DIV_SYSTEM_POWERNOISE, + DIV_SYSTEM_DAVE, 0 // don't remove this last one! }; @@ -1246,7 +1247,6 @@ const int chipsFM[]={ DIV_SYSTEM_OPL3_DRUMS, DIV_SYSTEM_OPZ, DIV_SYSTEM_ESFM, - DIV_SYSTEM_DAVE, 0 // don't remove this last one! }; From b3b84d41b6312d910be840a6954da28a30742589 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 3 Feb 2024 23:45:47 -0500 Subject: [PATCH 5/6] enable backward-cpp for 32-bit Windows releases --- scripts/release-win32.sh | 2 +- src/engine/platform/dave.cpp | 210 +++++++++-------------------------- src/engine/platform/dave.h | 35 +++--- src/gui/insEdit.cpp | 3 +- 4 files changed, 67 insertions(+), 183 deletions(-) diff --git a/scripts/release-win32.sh b/scripts/release-win32.sh index 604e17251..388ceb5aa 100755 --- a/scripts/release-win32.sh +++ b/scripts/release-win32.sh @@ -15,7 +15,7 @@ fi cd win32build # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2 -march=i586" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -march=i586" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=OFF -DWITH_RENDER_DX11=ON -DUSE_BACKWARD=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2 -march=i586" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type -march=i586" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=OFF -DWITH_RENDER_DX11=ON -DUSE_BACKWARD=ON -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF .. || exit 1 make -j8 || exit 1 cd .. diff --git a/src/engine/platform/dave.cpp b/src/engine/platform/dave.cpp index 3adb368af..fbe91ae33 100644 --- a/src/engine/platform/dave.cpp +++ b/src/engine/platform/dave.cpp @@ -55,8 +55,8 @@ const char** DivPlatformDave::getRegisterSheet() { void DivPlatformDave::acquire(short** buf, size_t len) { for (size_t h=0; hrate) { DivSample* s=parent->getSample(chan[i].dacSample); @@ -77,24 +77,19 @@ void DivPlatformDave::acquire(short** buf, size_t len) { } } - // PCE part while (!writes.empty()) { QueuedWrite w=writes.front(); + dave->writePort(w.addr,w.val); regPool[w.addr&0x1f]=w.val; writes.pop(); } - tempL[0]=(tempL[0]>>1)+(tempL[0]>>2); - tempR[0]=(tempR[0]>>1)+(tempR[0]>>2); - - if (tempL[0]<-32768) tempL[0]=-32768; - if (tempL[0]>32767) tempL[0]=32767; - if (tempR[0]<-32768) tempR[0]=-32768; - if (tempR[0]>32767) tempR[0]=32767; + unsigned int next=dave->runOneCycle(); + unsigned short nextL=next&0xffff; + unsigned short nextR=next>>16; - //printf("tempL: %d tempR: %d\n",tempL,tempR); - buf[0][h]=tempL[0]; - buf[1][h]=tempR[0]; + buf[0][h]=(short)nextL; + buf[1][h]=(short)nextR; } } @@ -102,39 +97,32 @@ void DivPlatformDave::tick(bool sysTick) { for (int i=0; i<6; i++) { chan[i].std.next(); if (chan[i].std.vol.had) { - chan[i].outVol=VOL_SCALE_LOG_BROKEN(chan[i].vol&31,MIN(31,chan[i].std.vol.val),31); - if (chan[i].furnaceDac && chan[i].pcm) { - // ignore for now - } else { - } + chan[i].outVol=VOL_SCALE_LINEAR(chan[i].vol&63,MIN(63,chan[i].std.vol.val),63); + chan[i].writeVol=true; } - if (chan[i].std.duty.had && i>=4) { - chan[i].noise=chan[i].std.duty.val; + if (chan[i].std.duty.had) { chan[i].freqChanged=true; } if (NEW_ARP_STRAT) { chan[i].handleArp(); } else if (chan[i].std.arp.had) { if (!chan[i].inPorta) { - int noiseSeek=parent->calcArp(chan[i].note,chan[i].std.arp.val); - chan[i].baseFreq=NOTE_PERIODIC(noiseSeek); - if (noiseSeek<0) noiseSeek=0; - chan[i].noiseSeek=noiseSeek; + chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val); } chan[i].freqChanged=true; } - if (chan[i].std.wave.had && !chan[i].pcm) { - // TODO: this + if (chan[i].std.wave.had) { + chan[i].wave=chan[i].std.wave.val; + chan[i].freqChanged=true; } if (chan[i].std.panL.had) { - chan[i].pan&=0x0f; - chan[i].pan|=(chan[i].std.panL.val&15)<<4; + chan[i].panL=chan[i].std.panL.val&63; } if (chan[i].std.panR.had) { - chan[i].pan&=0xf0; - chan[i].pan|=chan[i].std.panR.val&15; + chan[i].panR=chan[i].std.panR.val&63; } if (chan[i].std.panL.had || chan[i].std.panR.had) { + chan[i].writeVol=true; } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { @@ -146,7 +134,7 @@ void DivPlatformDave::tick(bool sysTick) { chan[i].freqChanged=true; } if (chan[i].std.phaseReset.had && chan[i].std.phaseReset.val==1) { - if (chan[i].furnaceDac && chan[i].pcm) { + if (i>=4) { if (chan[i].active && chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { chan[i].dacPos=0; chan[i].dacPeriod=0; @@ -154,24 +142,28 @@ void DivPlatformDave::tick(bool sysTick) { } } } + + if (chan[i].writeVol) { + if (chan[i].active) { + rWrite(8+i,(63+chan[i].outVol*chan[i].panL)>>6); + rWrite(12+i,(63+chan[i].outVol*chan[i].panR)>>6); + } else { + rWrite(8+i,0); + rWrite(12+i,0); + } + } + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); chan[i].freq=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].furnaceDac && chan[i].pcm) { - double off=1.0; - if (chan[i].dacSample>=0 && chan[i].dacSamplesong.sampleLen) { - DivSample* s=parent->getSample(chan[i].dacSample); - if (s->centerRate<1) { - off=1.0; - } else { - off=8363.0/(double)s->centerRate; - } - } - chan[i].dacRate=((double)chipClock/2)/MAX(1,off*chan[i].freq); - } if (chan[i].freq<1) chan[i].freq=1; if (chan[i].freq>4095) chan[i].freq=4095; + if (i<3) { + rWrite((i<<1),chan[i].freq&0xff); + rWrite(1+(i<<1),(chan[i].freq>>8)|((chan[i].wave&3)<<4)); + } + if (chan[i].keyOn) { } if (chan[i].keyOff) { @@ -181,83 +173,14 @@ void DivPlatformDave::tick(bool sysTick) { chan[i].freqChanged=false; } } - if (updateLFO) { - rWrite(0x08,lfoSpeed); - rWrite(0x09,lfoMode); - updateLFO=false; - } } int DivPlatformDave::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); - chan[c.chan].macroVolMul=ins->type==DIV_INS_AMIGA?64:31; - if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { - chan[c.chan].pcm=true; - } else if (chan[c.chan].furnaceDac) { - chan[c.chan].pcm=false; - chan[c.chan].sampleNote=DIV_NOTE_NULL; - chan[c.chan].sampleNoteDelta=0; - } - if (chan[c.chan].pcm) { - if (ins->type==DIV_INS_AMIGA || ins->amiga.useSample) { - chan[c.chan].furnaceDac=true; - if (skipRegisterWrites) break; - if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].dacSample=ins->amiga.getSample(c.value); - chan[c.chan].sampleNote=c.value; - c.value=ins->amiga.getFreq(c.value); - chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; - } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { - chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); - c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); - } - if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { - chan[c.chan].dacSample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); - break; - } else { - if (dumpWrites) { - } - } - chan[c.chan].dacPos=0; - chan[c.chan].dacPeriod=0; - 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 (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { - chan[c.chan].outVol=chan[c.chan].vol; - } - //chan[c.chan].keyOn=true; - } else { - chan[c.chan].furnaceDac=false; - chan[c.chan].sampleNote=DIV_NOTE_NULL; - chan[c.chan].sampleNoteDelta=0; - if (skipRegisterWrites) break; - if (c.value!=DIV_NOTE_NULL) { - chan[c.chan].note=c.value; - } - chan[c.chan].dacSample=12*sampleBank+chan[c.chan].note%12; - if (chan[c.chan].dacSample>=parent->song.sampleLen) { - chan[c.chan].dacSample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); - break; - } else { - if (dumpWrites) addWrite(0xffff0000+(c.chan<<8),chan[c.chan].dacSample); - } - chan[c.chan].dacPos=0; - chan[c.chan].dacPeriod=0; - chan[c.chan].dacRate=parent->getSample(chan[c.chan].dacSample)->rate; - if (dumpWrites) { - } - } - break; - } + // TODO: handle DAC + if (c.chan>=4) break; chan[c.chan].sampleNote=DIV_NOTE_NULL; chan[c.chan].sampleNoteDelta=0; if (c.value!=DIV_NOTE_NULL) { @@ -267,6 +190,7 @@ int DivPlatformDave::dispatch(DivCommand c) { } chan[c.chan].active=true; chan[c.chan].keyOn=true; + chan[c.chan].writeVol=true; chan[c.chan].macroInit(ins); if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { chan[c.chan].outVol=chan[c.chan].vol; @@ -275,11 +199,9 @@ int DivPlatformDave::dispatch(DivCommand c) { break; } case DIV_CMD_NOTE_OFF: - chan[c.chan].dacSample=-1; - if (dumpWrites) addWrite(0xffff0002+(c.chan<<8),0); - chan[c.chan].pcm=false; chan[c.chan].active=false; chan[c.chan].keyOff=true; + chan[c.chan].writeVol=true; chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: @@ -297,7 +219,7 @@ int DivPlatformDave::dispatch(DivCommand c) { chan[c.chan].vol=c.value; if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; - if (chan[c.chan].active && !chan[c.chan].pcm) { + if (chan[c.chan].active) { } } } @@ -316,18 +238,6 @@ int DivPlatformDave::dispatch(DivCommand c) { chan[c.chan].wave=c.value; chan[c.chan].keyOn=true; break; - case DIV_CMD_PCE_LFO_MODE: - if (c.value==0) { - lfoMode=0; - } else { - lfoMode=c.value; - } - updateLFO=true; - break; - case DIV_CMD_PCE_LFO_SPEED: - lfoSpeed=255-c.value; - updateLFO=true; - break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); bool return2=false; @@ -352,7 +262,8 @@ int DivPlatformDave::dispatch(DivCommand c) { break; } case DIV_CMD_PANNING: { - chan[c.chan].pan=(c.value&0xf0)|(c.value2>>4); + chan[c.chan].panL=c.value>>2; + chan[c.chan].panR=c.value2>>2; break; } case DIV_CMD_LEGATO: @@ -387,8 +298,7 @@ int DivPlatformDave::dispatch(DivCommand c) { void DivPlatformDave::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - if (!isMuted[ch] && (chan[ch].pcm && chan[ch].dacSample!=-1)) { - } + // TODO } void DivPlatformDave::forceIns() { @@ -407,31 +317,20 @@ DivMacroInt* DivPlatformDave::getChanMacroInt(int ch) { } unsigned short DivPlatformDave::getPan(int ch) { - return ((chan[ch].pan&0xf0)<<4)|(chan[ch].pan&15); + return (chan[ch].panL<<2)|chan[ch].panR; } DivChannelPair DivPlatformDave::getPaired(int ch) { - if (ch==1 && lfoMode>0) { - return DivChannelPair("mod",0); - } return DivChannelPair(); } DivChannelModeHints DivPlatformDave::getModeHints(int ch) { DivChannelModeHints ret; - if (ch<4) return ret; - ret.count=1; - ret.hint[0]=ICON_FUR_NOISE; - ret.type[0]=0; - - if (chan[ch].noise) ret.type[0]=4; - return ret; } DivSamplePos DivPlatformDave::getSamplePos(int ch) { - if (ch>=6) return DivSamplePos(); - if (!chan[ch].pcm) return DivSamplePos(); + if (ch<4 || ch>=6) return DivSamplePos(); return DivSamplePos( chan[ch].dacSample, chan[ch].dacPos, @@ -452,12 +351,12 @@ unsigned char* DivPlatformDave::getRegisterPool() { } int DivPlatformDave::getRegisterPoolSize() { - return 112; + return 32; } void DivPlatformDave::reset() { writes.clear(); - memset(regPool,0,128); + memset(regPool,0,32); for (int i=0; i<6; i++) { chan[i]=DivPlatformDave::Channel(); chan[i].std.setEngine(parent); @@ -465,23 +364,16 @@ void DivPlatformDave::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + dave->reset(true); lastPan=0xff; - memset(tempL,0,32*sizeof(int)); - memset(tempR,0,32*sizeof(int)); cycles=0; curChan=-1; - sampleBank=0; - lfoMode=0; - lfoSpeed=255; // set global volume rWrite(0,0); rWrite(0x01,0xff); - // set LFO - updateLFO=true; // set per-channel initial panning for (int i=0; i<6; i++) { } - delay=500; } int DivPlatformDave::getOutputCount() { @@ -501,8 +393,7 @@ void DivPlatformDave::notifyInsDeletion(void* ins) { void DivPlatformDave::setFlags(const DivConfig& flags) { chipClock=8000000.0; CHECK_CUSTOM_CLOCK; - antiClickEnabled=!flags.getBool("noAntiClick",false); - rate=chipClock/12; + rate=chipClock/16; for (int i=0; i<6; i++) { oscBuf[i]->rate=rate; } @@ -520,7 +411,7 @@ int DivPlatformDave::init(DivEngine* p, int channels, int sugRate, const DivConf parent=p; dumpWrites=false; skipRegisterWrites=false; - updateLFO=false; + dave=new Ep128::Dave; for (int i=0; i<6; i++) { isMuted[i]=false; oscBuf[i]=new DivDispatchOscBuffer; @@ -534,6 +425,7 @@ void DivPlatformDave::quit() { for (int i=0; i<6; i++) { delete oscBuf[i]; } + delete dave; } DivPlatformDave::~DivPlatformDave() { diff --git a/src/engine/platform/dave.h b/src/engine/platform/dave.h index e61d5ab4d..0af0e2a26 100644 --- a/src/engine/platform/dave.h +++ b/src/engine/platform/dave.h @@ -17,8 +17,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef _PCE_H -#define _PCE_H +#ifndef _DAVE_H +#define _DAVE_H #include "../dispatch.h" #include "../../fixedQueue.h" @@ -29,31 +29,25 @@ class DivPlatformDave: public DivDispatch { int dacPeriod, dacRate, dacOut; unsigned int dacPos; int dacSample; - unsigned char pan; - bool noise, pcm, furnaceDac, deferredWaveUpdate; - signed short wave; - int macroVolMul, noiseSeek; + unsigned char panL; + unsigned char panR; + unsigned char wave; + bool writeVol; Channel(): - SharedChannel(31), + SharedChannel(63), dacPeriod(0), dacRate(0), dacOut(0), dacPos(0), dacSample(-1), - pan(255), - noise(false), - pcm(false), - furnaceDac(false), - deferredWaveUpdate(false), - wave(-1), - macroVolMul(31), - noiseSeek(0) {} + panL(63), + panR(63), + wave(0), + writeVol(false) {} }; Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; bool isMuted[6]; - bool antiClickEnabled; - bool updateLFO; struct QueuedWrite { unsigned char addr; unsigned char val; @@ -63,12 +57,9 @@ class DivPlatformDave: public DivDispatch { FixedQueue writes; unsigned char lastPan; - int cycles, curChan, delay; - int tempL[32]; - int tempR[32]; - unsigned char sampleBank, lfoMode, lfoSpeed; + int cycles, curChan; Ep128::Dave* dave; - unsigned char regPool[128]; + unsigned char regPool[32]; friend void putDispatchChip(void*,int); friend void putDispatchChan(void*,int,int); public: diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index f38dba6a1..4c1d4af82 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -6726,7 +6726,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_PCE || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_SM8521) { volMax=31; } - if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_VERA || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_ESFM) { + if (ins->type==DIV_INS_OPL || ins->type==DIV_INS_OPL_DRUMS || ins->type==DIV_INS_VERA || ins->type==DIV_INS_VRC6_SAW || ins->type==DIV_INS_ESFM || ins->type==DIV_INS_DAVE) { volMax=63; } if (ins->type==DIV_INS_AMIGA) { @@ -6953,6 +6953,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POWERNOISE) waveMax=0; if (ins->type==DIV_INS_POWERNOISE_SLOPE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; + if (ins->type==DIV_INS_DAVE) waveMax=3; if (ins->type==DIV_INS_PET) { waveMax=8; waveBitMode=true; From 4df1c5969851c413910d91a1cb5889128bf1f3b7 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sun, 4 Feb 2024 02:05:00 -0500 Subject: [PATCH 6/6] also enable backward on Windows XP --- scripts/release-winxp.sh | 2 +- src/engine/platform/dave.cpp | 201 +++++++++++++++++++++++++++-------- src/engine/platform/dave.h | 14 ++- src/engine/sysDef.cpp | 2 +- src/gui/insEdit.cpp | 23 +++- src/gui/presets.cpp | 12 +++ 6 files changed, 201 insertions(+), 53 deletions(-) diff --git a/scripts/release-winxp.sh b/scripts/release-winxp.sh index 71ff675ea..5901f1643 100755 --- a/scripts/release-winxp.sh +++ b/scripts/release-winxp.sh @@ -15,7 +15,7 @@ fi cd xpbuild # TODO: potential Arch-ism? -i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF .. || exit 1 +i686-w64-mingw32-cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_C_FLAGS="-O2" -DCMAKE_CXX_FLAGS="-O2 -Wall -Wextra -Wno-unused-parameter -Wno-cast-function-type" -DBUILD_SHARED_LIBS=OFF -DSUPPORT_XP=ON -DWITH_RENDER_DX11=OFF -DSDL_SSE2=OFF -DSDL_SSE3=OFF -DENABLE_SSE=OFF -DENABLE_SSE2=OFF -DENABLE_AVX=OFF -DENABLE_AVX2=OFF -DUSE_BACKWARD=ON .. || exit 1 make -j8 || exit 1 cd .. diff --git a/src/engine/platform/dave.cpp b/src/engine/platform/dave.cpp index fbe91ae33..d316ae58f 100644 --- a/src/engine/platform/dave.cpp +++ b/src/engine/platform/dave.cpp @@ -25,7 +25,7 @@ //#define rWrite(a,v) pendingWrites[a]=v; #define rWrite(a,v) if (!skipRegisterWrites) {writes.push(QueuedWrite(a,v)); if (dumpWrites) {addWrite(a,v);} } -#define CHIP_DIVIDER 32 +#define CHIP_DIVIDER 8 const char* regCheatSheetDave[]={ "Freq0", "00", @@ -48,36 +48,54 @@ const char* regCheatSheetDave[]={ NULL }; +const unsigned char snapPeriodLong[15]={ + 0, 1, 3, 3, 3, 6, 6, 7, 7, 10, 10, 12, 12, 13, 13 +}; + +const unsigned char snapPeriodShort[15]={ + 2, 2, 2, 2, 5, 5, 5, 8, 8, 8, 11, 11, 11, 11, 11 +}; + +const unsigned char waveMap[8]={ + 0, 1, 1, 2, 3, 0, 0, 0 +}; + const char** DivPlatformDave::getRegisterSheet() { return regCheatSheetDave; } void DivPlatformDave::acquire(short** buf, size_t len) { for (size_t h=0; hrate) { DivSample* s=parent->getSample(chan[i].dacSample); if (s->samples<=0) { chan[i].dacSample=-1; + writeControl=true; + chan[0].writeVol=true; continue; } - signed char dacData=((signed char)((unsigned char)s->data8[chan[i].dacPos]^0x80))>>3; - chan[i].dacOut=CLAMP(dacData,-16,15); + signed char dacData=(s->data8[chan[i].dacPos]*chan[i].outVol)>>8; + chan[i].dacOut=dacData+32; chan[i].dacPos++; + if (!isMuted[i]) { + rWrite(8+((i-4)<<2),chan[i].dacOut&0x3f); + } if (s->isLoopable() && chan[i].dacPos>=(unsigned int)s->loopEnd) { chan[i].dacPos=s->loopStart; } else if (chan[i].dacPos>=s->samples) { chan[i].dacSample=-1; + writeControl=true; + chan[0].writeVol=true; } chan[i].dacPeriod-=rate; } } } - while (!writes.empty()) { + if (!writes.empty()) { QueuedWrite w=writes.front(); dave->writePort(w.addr,w.val); regPool[w.addr&0x1f]=w.val; @@ -101,18 +119,24 @@ void DivPlatformDave::tick(bool sysTick) { chan[i].writeVol=true; } if (chan[i].std.duty.had) { + chan[i].noiseFreq=chan[i].std.duty.val&3; chan[i].freqChanged=true; } if (NEW_ARP_STRAT) { chan[i].handleArp(); } else if (chan[i].std.arp.had) { if (!chan[i].inPorta) { - chan[i].baseFreq=parent->calcArp(chan[i].note,chan[i].std.arp.val); + if (i>=4) { + chan[i].baseFreq=parent->calcBaseFreq(1,1,parent->calcArp(chan[i].note,chan[i].std.arp.val),false); + } else { + chan[i].baseFreq=NOTE_PERIODIC(parent->calcArp(chan[i].note,chan[i].std.arp.val)); + } } chan[i].freqChanged=true; } if (chan[i].std.wave.had) { - chan[i].wave=chan[i].std.wave.val; + chan[i].wave=chan[i].std.wave.val&7; + if (i==3 && chan[i].wave>3) chan[i].wave=3; chan[i].freqChanged=true; } if (chan[i].std.panL.had) { @@ -124,6 +148,13 @@ void DivPlatformDave::tick(bool sysTick) { if (chan[i].std.panL.had || chan[i].std.panR.had) { chan[i].writeVol=true; } + if (chan[i].std.ex1.had) { + chan[i].highPass=chan[i].std.ex1.val&1; + chan[i].ringMod=chan[i].std.ex1.val&2; + chan[i].swapCounters=chan[i].std.ex1.val&4; + chan[i].lowPass=chan[i].std.ex1.val&8; + chan[i].freqChanged=true; + } if (chan[i].std.pitch.had) { if (chan[i].std.pitch.mode) { chan[i].pitch2+=chan[i].std.pitch.val; @@ -140,53 +171,135 @@ void DivPlatformDave::tick(bool sysTick) { chan[i].dacPeriod=0; chan[i].keyOn=true; } + } else { + chan[i].resetPhase=true; + writeControl=true; } } if (chan[i].writeVol) { - if (chan[i].active) { - rWrite(8+i,(63+chan[i].outVol*chan[i].panL)>>6); - rWrite(12+i,(63+chan[i].outVol*chan[i].panR)>>6); - } else { - rWrite(8+i,0); - rWrite(12+i,0); + if (i<4) { + if (chan[i].active && !isMuted[i]) { + if (i!=0 || chan[4].dacSample<0) { + rWrite(8+i,(63+chan[i].outVol*chan[i].panL)>>6); + } + if (i!=0 || chan[5].dacSample<0) { + rWrite(12+i,(63+chan[i].outVol*chan[i].panR)>>6); + } + } else { + if (i!=0 || chan[4].dacSample<0) { + rWrite(8+i,0); + } + if (i!=0 || chan[5].dacSample<0) { + rWrite(12+i,0); + } + } } + chan[i].writeVol=false; } if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { - //DivInstrument* ins=parent->getIns(chan[i].ins,DIV_INS_PCE); - chan[i].freq=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>4095) chan[i].freq=4095; + if (i>=4) { + chan[i].freq=parent->calcFreq(chan[i].baseFreq,chan[i].pitch,chan[i].fixedArp?chan[i].baseNoteOverride:chan[i].arpOff,chan[i].fixedArp,false,0,chan[i].pitch2,1,1); + } else { + chan[i].freq=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 (i<3) { - rWrite((i<<1),chan[i].freq&0xff); - rWrite(1+(i<<1),(chan[i].freq>>8)|((chan[i].wave&3)<<4)); + switch (chan[i].wave) { + case 0: + chan[i].freq>>=2; + break; + case 1: + chan[i].freq/=5; + chan[i].freq>>=1; + break; + case 2: + chan[i].freq/=15; + chan[i].freq>>=1; + break; + case 3: + chan[i].freq/=63; + break; + case 4: + chan[i].freq>>=5; + break; + } } - if (chan[i].keyOn) { + if (i<4) { + if (chan[i].freq<1) chan[i].freq=1; + if (chan[i].freq>4095) chan[i].freq=4095; } - if (chan[i].keyOff) { + + if (i<3) { + if (chan[i].wave==1) { // short 1 + chan[i].freq=15*(chan[i].freq/15)+snapPeriodShort[(chan[i].freq%15)]; + } else if (chan[i].wave==2) { // long 1 + chan[i].freq=15*(chan[i].freq/15)+snapPeriodLong[(chan[i].freq%15)]; + } else if (chan[i].wave==3) { // long 2 (30, 61, 92, 123... result in silence) + if ((chan[i].freq%30)==(chan[i].freq/30)-1) chan[i].freq++; + } + rWrite((i<<1),chan[i].freq&0xff); + rWrite(1+(i<<1),(chan[i].freq>>8)|((waveMap[chan[i].wave])<<4)|(chan[i].highPass?0x40:0)|(chan[i].ringMod?0x80:0)); + } else if (i==3) { + rWrite(6,(chan[i].noiseFreq&3)|((chan[i].wave&3)<<2)|(chan[i].swapCounters?0x10:0)|(chan[i].lowPass?0x20:0)|(chan[i].highPass?0x40:0)|(chan[i].ringMod?0x80:0)); } + if (chan[i].keyOn) chan[i].keyOn=false; if (chan[i].keyOff) chan[i].keyOff=false; chan[i].freqChanged=false; } } + + if (writeControl) { + rWrite(7,(chan[0].resetPhase?1:0)|(chan[1].resetPhase?2:0)|(chan[2].resetPhase?4:0)|((chan[4].dacSample>=0)?8:0)|((chan[5].dacSample>=0)?16:0)); + rWrite(7,((chan[4].dacSample>=0)?8:0)|((chan[5].dacSample>=0)?16:0)); + chan[0].resetPhase=false; + chan[1].resetPhase=false; + chan[2].resetPhase=false; + chan[3].resetPhase=false; + writeControl=false; + } } int DivPlatformDave::dispatch(DivCommand c) { switch (c.cmd) { case DIV_CMD_NOTE_ON: { - DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_PCE); - // TODO: handle DAC - if (c.chan>=4) break; - chan[c.chan].sampleNote=DIV_NOTE_NULL; - chan[c.chan].sampleNoteDelta=0; - 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; + DivInstrument* ins=NULL; + // DAC + if (c.chan>=4) { + ins=parent->getIns(chan[c.chan].ins,DIV_INS_AMIGA); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(c.value); + chan[c.chan].sampleNote=c.value; + c.value=ins->amiga.getFreq(c.value); + chan[c.chan].sampleNoteDelta=c.value-chan[c.chan].sampleNote; + } else if (chan[c.chan].sampleNote!=DIV_NOTE_NULL) { + chan[c.chan].dacSample=ins->amiga.getSample(chan[c.chan].sampleNote); + c.value=ins->amiga.getFreq(chan[c.chan].sampleNote); + } + if (chan[c.chan].dacSample<0 || chan[c.chan].dacSample>=parent->song.sampleLen) { + chan[c.chan].dacSample=-1; + chan[0].writeVol=true; + } + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=parent->calcBaseFreq(1,1,c.value,false); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].dacPos=0; + chan[c.chan].dacPeriod=0; + writeControl=true; + } else { + ins=parent->getIns(chan[c.chan].ins,DIV_INS_DAVE); + chan[c.chan].sampleNote=DIV_NOTE_NULL; + chan[c.chan].sampleNoteDelta=0; + 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; @@ -202,6 +315,11 @@ int DivPlatformDave::dispatch(DivCommand c) { chan[c.chan].active=false; chan[c.chan].keyOff=true; chan[c.chan].writeVol=true; + if (c.chan>=4) { + chan[c.chan].dacSample=-1; + chan[0].writeVol=true; + writeControl=true; + } chan[c.chan].macroInit(NULL); break; case DIV_CMD_NOTE_OFF_ENV: @@ -220,6 +338,7 @@ int DivPlatformDave::dispatch(DivCommand c) { if (!chan[c.chan].std.vol.has) { chan[c.chan].outVol=c.value; if (chan[c.chan].active) { + chan[c.chan].writeVol=true; } } } @@ -236,7 +355,7 @@ int DivPlatformDave::dispatch(DivCommand c) { break; case DIV_CMD_WAVE: chan[c.chan].wave=c.value; - chan[c.chan].keyOn=true; + chan[c.chan].freqChanged=true; break; case DIV_CMD_NOTE_PORTA: { int destFreq=NOTE_PERIODIC(c.value2+chan[c.chan].sampleNoteDelta); @@ -267,13 +386,13 @@ int DivPlatformDave::dispatch(DivCommand c) { break; } case DIV_CMD_LEGATO: - chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta+((HACKY_LEGATO_MESS)?(chan[c.chan].std.arp.val):(0))); + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value+chan[c.chan].sampleNoteDelta); 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_PCE)); + if (parent->song.resetMacroOnPorta) chan[c.chan].macroInit(parent->getIns(chan[c.chan].ins,DIV_INS_DAVE)); } 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; @@ -298,14 +417,16 @@ int DivPlatformDave::dispatch(DivCommand c) { void DivPlatformDave::muteChannel(int ch, bool mute) { isMuted[ch]=mute; - // TODO + chan[ch].writeVol=true; } void DivPlatformDave::forceIns() { for (int i=0; i<6; i++) { chan[i].insChanged=true; chan[i].freqChanged=true; + chan[i].writeVol=true; } + writeControl=true; } void* DivPlatformDave::getChanState(int ch) { @@ -364,16 +485,8 @@ void DivPlatformDave::reset() { if (dumpWrites) { addWrite(0xffffffff,0); } + writeControl=false; dave->reset(true); - lastPan=0xff; - cycles=0; - curChan=-1; - // set global volume - rWrite(0,0); - rWrite(0x01,0xff); - // set per-channel initial panning - for (int i=0; i<6; i++) { - } } int DivPlatformDave::getOutputCount() { diff --git a/src/engine/platform/dave.h b/src/engine/platform/dave.h index 0af0e2a26..9fea7a3b1 100644 --- a/src/engine/platform/dave.h +++ b/src/engine/platform/dave.h @@ -29,10 +29,11 @@ class DivPlatformDave: public DivDispatch { int dacPeriod, dacRate, dacOut; unsigned int dacPos; int dacSample; + unsigned char noiseFreq; unsigned char panL; unsigned char panR; unsigned char wave; - bool writeVol; + bool writeVol, highPass, ringMod, swapCounters, lowPass, resetPhase; Channel(): SharedChannel(63), dacPeriod(0), @@ -40,10 +41,16 @@ class DivPlatformDave: public DivDispatch { dacOut(0), dacPos(0), dacSample(-1), + noiseFreq(0), panL(63), panR(63), wave(0), - writeVol(false) {} + writeVol(false), + highPass(false), + ringMod(false), + swapCounters(false), + lowPass(false), + resetPhase(false) {} }; Channel chan[6]; DivDispatchOscBuffer* oscBuf[6]; @@ -55,9 +62,8 @@ class DivPlatformDave: public DivDispatch { QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} }; FixedQueue writes; - unsigned char lastPan; + bool writeControl; - int cycles, curChan; Ep128::Dave* dave; unsigned char regPool[32]; friend void putDispatchChip(void*,int); diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 684226515..96b5228e2 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -2003,7 +2003,7 @@ void DivEngine::registerSystems() { ); sysDefs[DIV_SYSTEM_DAVE]=new DivSysDef( - "Dave", NULL, 0xd5, 0, 6, false, true, 0, false, 0, 0, 0, + "Dave", NULL, 0xd5, 0, 6, false, true, 0, false, 1U<type==DIV_INS_POWERNOISE_SLOPE) { dutyMax=0; } + if (ins->type==DIV_INS_DAVE) { + dutyLabel="Noise Freq"; + dutyMax=3; + } const char* waveLabel="Waveform"; int waveMax=(ins->type==DIV_INS_VERA)?3:(MAX(1,e->song.waveLen-1)); @@ -6953,7 +6961,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POWERNOISE) waveMax=0; if (ins->type==DIV_INS_POWERNOISE_SLOPE) waveMax=0; if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; - if (ins->type==DIV_INS_DAVE) waveMax=3; + if (ins->type==DIV_INS_DAVE) waveMax=4; if (ins->type==DIV_INS_PET) { waveMax=8; waveBitMode=true; @@ -7020,12 +7028,15 @@ void FurnaceGUI::drawInsEdit() { ex1Max=5; ex2Max=11; } + if (ins->type==DIV_INS_DAVE) { + ex1Max=4; + } int panMin=0; int panMax=0; bool panSingle=false; bool panSingleNoBit=false; - if (ins->type==DIV_INS_STD ||//Game Gear + if (ins->type==DIV_INS_STD || // Game Gear ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM || ins->type==DIV_INS_GB || @@ -7097,6 +7108,9 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_POWERNOISE_SLOPE) { panMax=15; } + if (ins->type==DIV_INS_DAVE) { + panMax=63; + } if (volMax>0) { macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME])); @@ -7186,7 +7200,8 @@ void FurnaceGUI::drawInsEdit() { ins->type==DIV_INS_TED || ins->type==DIV_INS_ESFM || ins->type==DIV_INS_POWERNOISE || - ins->type==DIV_INS_POWERNOISE_SLOPE) { + ins->type==DIV_INS_POWERNOISE_SLOPE || + ins->type==DIV_INS_DAVE) { macroList.push_back(FurnaceGUIMacroDesc("Phase Reset",&ins->std.phaseResetMacro,0,1,32,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true)); } if (ex1Max>0) { @@ -7220,6 +7235,8 @@ void FurnaceGUI::drawInsEdit() { macroList.push_back(FurnaceGUIMacroDesc("Special",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,snesModeBits)); } else if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc("Group Attack",&ins->std.ex1Macro,0,ex1Max,96,uiColors[GUI_COLOR_MACRO_OTHER])); + } else if (ins->type==DIV_INS_DAVE) { + macroList.push_back(FurnaceGUIMacroDesc("Control",&ins->std.ex1Macro,0,ex1Max,64,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,daveControlBits)); } else { macroList.push_back(FurnaceGUIMacroDesc("Duty",&ins->std.ex1Macro,0,ex1Max,160,uiColors[GUI_COLOR_MACRO_OTHER])); } diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 0c8894615..768304330 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -952,6 +952,12 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_SAA1099, 1.0f, 0, "") } ); + ENTRY( + "Enterprise 128", { + CH(DIV_SYSTEM_DAVE, 1.0f, 0, "") + }, + "tickRate=50" + ); ENTRY( "BBC Micro", { CH(DIV_SYSTEM_SMS, 1.0f, 0, @@ -2778,6 +2784,12 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_POWERNOISE, 1.0f, 0, "") } ); + ENTRY( + "Dave", { + CH(DIV_SYSTEM_DAVE, 1.0f, 0, "") + }, + "tickRate=50" + ); CATEGORY_END; CATEGORY_BEGIN("DefleMask-compatible","these configurations are compatible with DefleMask.\nselect this if you need to save as .dmf or work with that program.");