mirror of
https://github.com/tildearrow/furnace.git
synced 2024-11-30 16:33:01 +00:00
commit
f587d0dde6
8 changed files with 753 additions and 5 deletions
|
@ -425,6 +425,7 @@ src/engine/platform/sound/ymfm/ymfm_ssg.cpp
|
||||||
src/engine/platform/sound/lynx/Mikey.cpp
|
src/engine/platform/sound/lynx/Mikey.cpp
|
||||||
|
|
||||||
src/engine/platform/sound/pokey/mzpokeysnd.c
|
src/engine/platform/sound/pokey/mzpokeysnd.c
|
||||||
|
src/engine/platform/sound/pokey/AltASAP.cpp
|
||||||
|
|
||||||
src/engine/platform/sound/qsound.c
|
src/engine/platform/sound/qsound.c
|
||||||
|
|
||||||
|
|
|
@ -341,6 +341,7 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_POKEY:
|
case DIV_SYSTEM_POKEY:
|
||||||
dispatch=new DivPlatformPOKEY;
|
dispatch=new DivPlatformPOKEY;
|
||||||
|
((DivPlatformPOKEY*)dispatch)->setAltASAP(eng->getConfInt("pokeyCore",0)==1);
|
||||||
break;
|
break;
|
||||||
case DIV_SYSTEM_QSOUND:
|
case DIV_SYSTEM_QSOUND:
|
||||||
dispatch=new DivPlatformQSound;
|
dispatch=new DivPlatformQSound;
|
||||||
|
|
|
@ -65,6 +65,15 @@ const char** DivPlatformPOKEY::getRegisterSheet() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t len) {
|
||||||
|
if (useAltASAP) {
|
||||||
|
acquireASAP(bufL, start, len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
acquireMZ(bufL, start, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DivPlatformPOKEY::acquireMZ(short* buf, size_t start, size_t len) {
|
||||||
for (size_t h=start; h<start+len; h++) {
|
for (size_t h=start; h<start+len; h++) {
|
||||||
while (!writes.empty()) {
|
while (!writes.empty()) {
|
||||||
QueuedWrite w=writes.front();
|
QueuedWrite w=writes.front();
|
||||||
|
@ -73,7 +82,7 @@ void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t le
|
||||||
writes.pop();
|
writes.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
mzpokeysnd_process_16(&pokey,&bufL[h],1);
|
mzpokeysnd_process_16(&pokey,&buf[h],1);
|
||||||
|
|
||||||
if (++oscBufDelay>=14) {
|
if (++oscBufDelay>=14) {
|
||||||
oscBufDelay=0;
|
oscBufDelay=0;
|
||||||
|
@ -85,6 +94,23 @@ void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t le
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivPlatformPOKEY::acquireASAP(short* buf, size_t start, size_t len) {
|
||||||
|
while (!writes.empty()) {
|
||||||
|
QueuedWrite w=writes.front();
|
||||||
|
altASAP->write(w.addr, w.val);
|
||||||
|
writes.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t h=start; h<start+len; h++) {
|
||||||
|
if (++oscBufDelay>=14) {
|
||||||
|
oscBufDelay=0;
|
||||||
|
buf[h]=altASAP->sampleAudio(oscBuf);
|
||||||
|
} else {
|
||||||
|
buf[h]=altASAP->sampleAudio();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void DivPlatformPOKEY::tick(bool sysTick) {
|
void DivPlatformPOKEY::tick(bool sysTick) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
chan[i].std.next();
|
chan[i].std.next();
|
||||||
|
@ -370,7 +396,12 @@ DivDispatchOscBuffer* DivPlatformPOKEY::getOscBuffer(int ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char* DivPlatformPOKEY::getRegisterPool() {
|
unsigned char* DivPlatformPOKEY::getRegisterPool() {
|
||||||
return regPool;
|
if (useAltASAP) {
|
||||||
|
return const_cast<unsigned char*>(altASAP->getRegisterPool());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return regPool;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int DivPlatformPOKEY::getRegisterPoolSize() {
|
int DivPlatformPOKEY::getRegisterPoolSize() {
|
||||||
|
@ -388,7 +419,12 @@ void DivPlatformPOKEY::reset() {
|
||||||
addWrite(0xffffffff,0);
|
addWrite(0xffffffff,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetPokeyState(&pokey);
|
if (useAltASAP) {
|
||||||
|
altASAP->reset();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ResetPokeyState(&pokey);
|
||||||
|
}
|
||||||
|
|
||||||
audctl=0;
|
audctl=0;
|
||||||
audctlChanged=true;
|
audctlChanged=true;
|
||||||
|
@ -419,6 +455,10 @@ void DivPlatformPOKEY::setFlags(const DivConfig& flags) {
|
||||||
for (int i=0; i<4; i++) {
|
for (int i=0; i<4; i++) {
|
||||||
oscBuf[i]->rate=rate/14;
|
oscBuf[i]->rate=rate/14;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (useAltASAP) {
|
||||||
|
altASAP=std::make_unique<AltASAP::Pokey>(chipClock, chipClock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DivPlatformPOKEY::poke(unsigned int addr, unsigned short val) {
|
void DivPlatformPOKEY::poke(unsigned int addr, unsigned short val) {
|
||||||
|
@ -439,7 +479,9 @@ int DivPlatformPOKEY::init(DivEngine* p, int channels, int sugRate, const DivCon
|
||||||
oscBuf[i]=new DivDispatchOscBuffer;
|
oscBuf[i]=new DivDispatchOscBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
MZPOKEYSND_Init(&pokey);
|
if (!useAltASAP) {
|
||||||
|
MZPOKEYSND_Init(&pokey);
|
||||||
|
}
|
||||||
|
|
||||||
setFlags(flags);
|
setFlags(flags);
|
||||||
reset();
|
reset();
|
||||||
|
@ -452,5 +494,9 @@ void DivPlatformPOKEY::quit() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DivPlatformPOKEY::setAltASAP(bool value) {
|
||||||
|
useAltASAP=value;
|
||||||
|
}
|
||||||
|
|
||||||
DivPlatformPOKEY::~DivPlatformPOKEY() {
|
DivPlatformPOKEY::~DivPlatformPOKEY() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ extern "C" {
|
||||||
#include "sound/pokey/mzpokeysnd.h"
|
#include "sound/pokey/mzpokeysnd.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "sound/pokey/AltASAP.hpp"
|
||||||
|
|
||||||
|
|
||||||
class DivPlatformPOKEY: public DivDispatch {
|
class DivPlatformPOKEY: public DivDispatch {
|
||||||
struct Channel: public SharedChannel<int> {
|
struct Channel: public SharedChannel<int> {
|
||||||
unsigned char wave;
|
unsigned char wave;
|
||||||
|
@ -49,11 +52,15 @@ class DivPlatformPOKEY: public DivDispatch {
|
||||||
bool audctlChanged;
|
bool audctlChanged;
|
||||||
unsigned char oscBufDelay;
|
unsigned char oscBufDelay;
|
||||||
PokeyState pokey;
|
PokeyState pokey;
|
||||||
|
std::unique_ptr<AltASAP::Pokey> altASAP;
|
||||||
|
bool useAltASAP;
|
||||||
unsigned char regPool[16];
|
unsigned char regPool[16];
|
||||||
friend void putDispatchChip(void*,int);
|
friend void putDispatchChip(void*,int);
|
||||||
friend void putDispatchChan(void*,int,int);
|
friend void putDispatchChan(void*,int,int);
|
||||||
public:
|
public:
|
||||||
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
void acquire(short* bufL, short* bufR, size_t start, size_t len);
|
||||||
|
void acquireMZ(short* buf, size_t start, size_t len);
|
||||||
|
void acquireASAP(short* buf, size_t start, size_t len);
|
||||||
int dispatch(DivCommand c);
|
int dispatch(DivCommand c);
|
||||||
void* getChanState(int chan);
|
void* getChanState(int chan);
|
||||||
DivMacroInt* getChanMacroInt(int ch);
|
DivMacroInt* getChanMacroInt(int ch);
|
||||||
|
@ -73,6 +80,7 @@ class DivPlatformPOKEY: public DivDispatch {
|
||||||
const char** getRegisterSheet();
|
const char** getRegisterSheet();
|
||||||
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
int init(DivEngine* parent, int channels, int sugRate, const DivConfig& flags);
|
||||||
void quit();
|
void quit();
|
||||||
|
void setAltASAP(bool useAltASAP);
|
||||||
~DivPlatformPOKEY();
|
~DivPlatformPOKEY();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
642
src/engine/platform/sound/pokey/AltASAP.cpp
Normal file
642
src/engine/platform/sound/pokey/AltASAP.cpp
Normal file
|
@ -0,0 +1,642 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2022 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* Original author: Piotr Fusik (http://asap.sourceforge.net)
|
||||||
|
* Rewritten based on Mikey emulation by Waldemar Pawlaszek
|
||||||
|
*
|
||||||
|
* 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 "AltASAP.hpp"
|
||||||
|
#include <array>
|
||||||
|
#include <vector>
|
||||||
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace AltASAP
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
static constexpr int64_t CNT_MAX = std::numeric_limits<int64_t>::max() & ~7;
|
||||||
|
static constexpr int MuteFrequency = 1;
|
||||||
|
static constexpr int MuteInit = 2;
|
||||||
|
static constexpr int MuteSerialInput = 8;
|
||||||
|
|
||||||
|
struct PokeyBase
|
||||||
|
{
|
||||||
|
int64_t mPolyIndex;
|
||||||
|
int mAudctl;
|
||||||
|
int mSkctl;
|
||||||
|
bool mInit;
|
||||||
|
std::array<uint8_t, 511> mPoly9Lookup;
|
||||||
|
std::array<uint8_t, 16385> mPoly17Lookup;
|
||||||
|
|
||||||
|
|
||||||
|
PokeyBase() : mPolyIndex{ 15 * 31 * 131071 }, mAudctl{ 0 }, mSkctl{ 3 }, mInit{ false }, mPoly9Lookup{}, mPoly17Lookup{}
|
||||||
|
{
|
||||||
|
int reg = 0x1ff;
|
||||||
|
for ( int i = 0; i < 511; i++ )
|
||||||
|
{
|
||||||
|
reg = ( ( ( reg >> 5 ^ reg ) & 1 ) << 8 ) + ( reg >> 1 );
|
||||||
|
mPoly9Lookup[i] = reg & 0xff;
|
||||||
|
}
|
||||||
|
reg = 0x1ffff;
|
||||||
|
for ( int i = 0; i < 16385; i++ )
|
||||||
|
{
|
||||||
|
reg = ( ( ( reg >> 5 ^ reg ) & 0xff ) << 9 ) + ( reg >> 8 );
|
||||||
|
mPoly17Lookup[i] = reg >> 1 & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
"Queue" holding event timepoints.
|
||||||
|
- 4 channel timer fire points
|
||||||
|
- 1 sample point
|
||||||
|
Three LSBs are used to encode event kind: 0-3 are channels, 4 is sampling.
|
||||||
|
Channel sequence is 2,3,0,1
|
||||||
|
*/
|
||||||
|
class ActionQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
ActionQueue() : mTab{ CNT_MAX | 0, CNT_MAX | 1, CNT_MAX | 2, CNT_MAX | 3, CNT_MAX | 4 }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert( int idx, int64_t value )
|
||||||
|
{
|
||||||
|
assert( idx < (int)mTab.size() );
|
||||||
|
idx ^= 2;
|
||||||
|
mTab[idx] = value | idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertSampling( int64_t value )
|
||||||
|
{
|
||||||
|
mTab[4] = value | 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void disable( int idx )
|
||||||
|
{
|
||||||
|
assert( idx < (int)mTab.size() );
|
||||||
|
idx ^= 2;
|
||||||
|
mTab[idx] = CNT_MAX | idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enqueued( int idx )
|
||||||
|
{
|
||||||
|
assert( idx < (int)mTab.size() );
|
||||||
|
idx ^= 2;
|
||||||
|
return mTab[idx] < CNT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t pop()
|
||||||
|
{
|
||||||
|
int64_t min1 = std::min( mTab[0], mTab[1] );
|
||||||
|
int64_t min2 = std::min( mTab[2], mTab[3] );
|
||||||
|
int64_t min3 = std::min( min1, mTab[4] );
|
||||||
|
int64_t min4 = std::min( min2, min3 );
|
||||||
|
|
||||||
|
return min4 ^ 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<int64_t, 5> mTab;
|
||||||
|
};
|
||||||
|
|
||||||
|
class AudioChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioChannel( ActionQueue& queue, uint32_t number ) : mQueue{ queue }, mNumber{ number }, mAudf{ 0 }, mAudc{ 0 }, mPeriodCycles{ 28 }, mMute{ MuteFrequency }, mDelta{ 0 }, mOut{ 0 }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t getOutput() const
|
||||||
|
{
|
||||||
|
return (int16_t)mDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillRegisterPool( uint8_t* regs )
|
||||||
|
{
|
||||||
|
regs[0] = (uint8_t)mAudf;
|
||||||
|
regs[1] = (uint8_t)mAudc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void renewTrigger( int64_t cycle )
|
||||||
|
{
|
||||||
|
overrideTrigger( cycle + mPeriodCycles );
|
||||||
|
}
|
||||||
|
|
||||||
|
void overrideTrigger( int64_t cycle )
|
||||||
|
{
|
||||||
|
mQueue.insert( mNumber, cycle << 3 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void doStimer( int64_t cycle )
|
||||||
|
{
|
||||||
|
if ( mQueue.enqueued( mNumber ) )
|
||||||
|
renewTrigger( cycle );
|
||||||
|
}
|
||||||
|
|
||||||
|
void trigger( int64_t cycle, PokeyBase const& pokey )
|
||||||
|
{
|
||||||
|
renewTrigger( cycle );
|
||||||
|
if ( ( mAudc & 0xb0 ) == 0xa0 )
|
||||||
|
mOut ^= 1;
|
||||||
|
else if ( ( mAudc & 0x10 ) != 0 || pokey.mInit )
|
||||||
|
return;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int64_t poly = cycle + pokey.mPolyIndex - mNumber;
|
||||||
|
if ( mAudc < 0x80 && ( 0x65bd44e0 & 1 << ( poly % 31 ) ) == 0 ) // 0000011100100010101111011010011
|
||||||
|
return;
|
||||||
|
if ( ( mAudc & 0x20 ) != 0 )
|
||||||
|
mOut ^= 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
uint32_t newOut;
|
||||||
|
if ( ( mAudc & 0x40 ) != 0 )
|
||||||
|
newOut = 0x5370u >> (int)( poly % 15 ); // 000011101100101
|
||||||
|
else if ( pokey.mAudctl < 0x80 )
|
||||||
|
{
|
||||||
|
poly %= 131071;
|
||||||
|
newOut = pokey.mPoly17Lookup[poly >> 3] >> ( poly & 7 );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
newOut = pokey.mPoly9Lookup[poly % 511];
|
||||||
|
newOut &= 1;
|
||||||
|
if ( mOut == newOut )
|
||||||
|
return;
|
||||||
|
mOut = newOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mute() const
|
||||||
|
{
|
||||||
|
return mMute;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t audf() const
|
||||||
|
{
|
||||||
|
return mAudf;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t audc() const
|
||||||
|
{
|
||||||
|
return mAudc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAudf( uint32_t value )
|
||||||
|
{
|
||||||
|
mAudf = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAudc( int64_t cycle, uint32_t value )
|
||||||
|
{
|
||||||
|
if ( mAudc == value )
|
||||||
|
return;
|
||||||
|
mAudc = value;
|
||||||
|
int32_t volume = value & 0x0f;
|
||||||
|
if ( ( value & 0x10 ) != 0 )
|
||||||
|
{
|
||||||
|
mDelta = volume;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
muteUltrasound( cycle );
|
||||||
|
if ( mDelta > 0 )
|
||||||
|
mDelta = volume;
|
||||||
|
else
|
||||||
|
mDelta = -volume;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle()
|
||||||
|
{
|
||||||
|
mDelta = -mDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPeriodCycles( int64_t value )
|
||||||
|
{
|
||||||
|
mPeriodCycles = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMute( bool enable, int mask, int64_t cycle )
|
||||||
|
{
|
||||||
|
if ( enable )
|
||||||
|
{
|
||||||
|
mMute |= mask;
|
||||||
|
mQueue.disable( mNumber );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mMute &= ~mask;
|
||||||
|
if ( mMute == 0 && !mQueue.enqueued( mNumber ) )
|
||||||
|
overrideTrigger( cycle );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void muteUltrasound( int64_t cycle )
|
||||||
|
{
|
||||||
|
static constexpr int UltrasoundCycles = 112;
|
||||||
|
setMute( mPeriodCycles <= UltrasoundCycles && ( mAudc & 0xb0 ) == 0xa0, MuteFrequency, cycle );
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ActionQueue& mQueue;
|
||||||
|
uint32_t mNumber;
|
||||||
|
uint32_t mAudf;
|
||||||
|
uint32_t mAudc;
|
||||||
|
int64_t mPeriodCycles;
|
||||||
|
|
||||||
|
uint32_t mMute;
|
||||||
|
int32_t mDelta;
|
||||||
|
uint32_t mOut;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class PokeyPimpl : public PokeyBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
PokeyPimpl( uint32_t pokeyClock, uint32_t sampleRate ) : PokeyBase{}, mQueue{ std::make_unique<ActionQueue>() },
|
||||||
|
mAudioChannels{ AudioChannel{ *mQueue, 0u }, AudioChannel{ *mQueue, 1u }, AudioChannel{ *mQueue, 2u }, AudioChannel{ *mQueue, 3u } },
|
||||||
|
mRegisterPool{}, mPokeyClock{ pokeyClock * 8 }, mTick{}, mNextTick{}, mSampleRate{ sampleRate }, mSamplesRemainder{},
|
||||||
|
mReloadCycles1{ 28 }, mReloadCycles3{ 28 }, mDivCycles{ 28 },
|
||||||
|
mTicksPerSample{ mPokeyClock / mSampleRate, mPokeyClock % mSampleRate }
|
||||||
|
{
|
||||||
|
std::fill_n( mRegisterPool.data(), mRegisterPool.size(), (uint8_t)0xff );
|
||||||
|
enqueueSampling();
|
||||||
|
}
|
||||||
|
|
||||||
|
~PokeyPimpl() {}
|
||||||
|
|
||||||
|
void write( uint8_t address, uint8_t value )
|
||||||
|
{
|
||||||
|
auto cycle = mTick >> 3;
|
||||||
|
|
||||||
|
switch ( address & 0xf )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
if ( value == mAudioChannels[0].audf() )
|
||||||
|
break;
|
||||||
|
mAudioChannels[0].setAudf( value );
|
||||||
|
switch ( mAudctl & 0x50 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
mAudioChannels[0].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( value + ( mAudioChannels[1].audf() << 8 ) + 1 ) );
|
||||||
|
mReloadCycles1 = mDivCycles * ( value + 1 );
|
||||||
|
mAudioChannels[1].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
mAudioChannels[0].setPeriodCycles( value + 4 );
|
||||||
|
break;
|
||||||
|
case 0x50:
|
||||||
|
mAudioChannels[1].setPeriodCycles( value + ( mAudioChannels[1].audf() << 8 ) + 7 );
|
||||||
|
mReloadCycles1 = value + 4;
|
||||||
|
mAudioChannels[1].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[0].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x01:
|
||||||
|
mAudioChannels[0].setAudc( cycle, value );
|
||||||
|
break;
|
||||||
|
case 0x02:
|
||||||
|
if ( value == mAudioChannels[1].audf() )
|
||||||
|
break;
|
||||||
|
mAudioChannels[1].setAudf( value );
|
||||||
|
switch ( mAudctl & 0x50 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
case 0x40:
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + ( value << 8 ) + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x50:
|
||||||
|
mAudioChannels[1].setPeriodCycles( mAudioChannels[0].audf() + ( value << 8 ) + 7 );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[1].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
mAudioChannels[1].setAudc( cycle, value );
|
||||||
|
break;
|
||||||
|
case 0x04:
|
||||||
|
if ( value == mAudioChannels[2].audf() )
|
||||||
|
break;
|
||||||
|
mAudioChannels[2].setAudf( value );
|
||||||
|
switch ( mAudctl & 0x28 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
mAudioChannels[2].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( value + ( mAudioChannels[3].audf() << 8 ) + 1 ) );
|
||||||
|
mReloadCycles3 = mDivCycles * ( value + 1 );
|
||||||
|
mAudioChannels[3].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
mAudioChannels[2].setPeriodCycles( value + 4 );
|
||||||
|
break;
|
||||||
|
case 0x28:
|
||||||
|
mAudioChannels[3].setPeriodCycles( value + ( mAudioChannels[3].audf() << 8 ) + 7 );
|
||||||
|
mReloadCycles3 = value + 4;
|
||||||
|
mAudioChannels[3].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[2].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x05:
|
||||||
|
mAudioChannels[2].setAudc( cycle, value );
|
||||||
|
break;
|
||||||
|
case 0x06:
|
||||||
|
if ( value == mAudioChannels[3].audf() )
|
||||||
|
break;
|
||||||
|
mAudioChannels[3].setAudf( value );
|
||||||
|
switch ( mAudctl & 0x28 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
case 0x20:
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( value + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + ( value << 8 ) + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x28:
|
||||||
|
mAudioChannels[3].setPeriodCycles( mAudioChannels[2].audf() + ( value << 8 ) + 7 );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[3].muteUltrasound( cycle );
|
||||||
|
break;
|
||||||
|
case 0x07:
|
||||||
|
mAudioChannels[3].setAudc( cycle, value );
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
if ( value == mAudctl )
|
||||||
|
break;
|
||||||
|
mAudctl = value;
|
||||||
|
mDivCycles = ( value & 1 ) != 0 ? 114 : 28;
|
||||||
|
switch ( value & 0x50 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
mAudioChannels[0].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + 1 ) );
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[1].audf() + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x10:
|
||||||
|
mAudioChannels[0].setPeriodCycles( (int64_t)mDivCycles << 8 );
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[0].audf() + ( mAudioChannels[1].audf() << 8 ) + 1 ) );
|
||||||
|
mReloadCycles1 = mDivCycles * ( mAudioChannels[0].audf() + 1 );
|
||||||
|
break;
|
||||||
|
case 0x40:
|
||||||
|
mAudioChannels[0].setPeriodCycles( mAudioChannels[0].audf() + 4 );
|
||||||
|
mAudioChannels[1].setPeriodCycles( mDivCycles * ( mAudioChannels[1].audf() + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x50:
|
||||||
|
mAudioChannels[0].setPeriodCycles( 256 );
|
||||||
|
mAudioChannels[1].setPeriodCycles( mAudioChannels[0].audf() + ( mAudioChannels[1].audf() << 8 ) + 7 );
|
||||||
|
mReloadCycles1 = mAudioChannels[0].audf() + 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[0].muteUltrasound( cycle );
|
||||||
|
mAudioChannels[1].muteUltrasound( cycle );
|
||||||
|
switch ( value & 0x28 )
|
||||||
|
{
|
||||||
|
case 0x00:
|
||||||
|
mAudioChannels[2].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + 1 ) );
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[3].audf() + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
mAudioChannels[2].setPeriodCycles( (int64_t)mDivCycles << 8 );
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[2].audf() + ( mAudioChannels[3].audf() << 8 ) + 1 ) );
|
||||||
|
mReloadCycles3 = mDivCycles * ( mAudioChannels[2].audf() + 1 );
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
mAudioChannels[2].setPeriodCycles( mAudioChannels[2].audf() + 4 );
|
||||||
|
mAudioChannels[3].setPeriodCycles( mDivCycles * ( mAudioChannels[3].audf() + 1 ) );
|
||||||
|
break;
|
||||||
|
case 0x28:
|
||||||
|
mAudioChannels[2].setPeriodCycles( 256 );
|
||||||
|
mAudioChannels[3].setPeriodCycles( mAudioChannels[2].audf() + ( mAudioChannels[3].audf() << 8 ) + 7 );
|
||||||
|
mReloadCycles3 = mAudioChannels[2].audf() + 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert( false );
|
||||||
|
}
|
||||||
|
mAudioChannels[2].muteUltrasound( cycle );
|
||||||
|
mAudioChannels[3].muteUltrasound( cycle );
|
||||||
|
initMute( cycle );
|
||||||
|
break;
|
||||||
|
case 0x09:
|
||||||
|
for ( int i = 0; i < 4; i++ )
|
||||||
|
mAudioChannels[i].doStimer( cycle );
|
||||||
|
break;
|
||||||
|
case 0x0f:
|
||||||
|
{
|
||||||
|
if ( value == mSkctl )
|
||||||
|
break;
|
||||||
|
mSkctl = value;
|
||||||
|
bool init = ( value & 3 ) == 0;
|
||||||
|
if ( mInit && !init )
|
||||||
|
mPolyIndex = ( ( mAudctl & 0x80 ) != 0 ? 15 * 31 * 511 - 1 : 15 * 31 * 131071 - 1 ) - cycle;
|
||||||
|
mInit = init;
|
||||||
|
initMute( cycle );
|
||||||
|
mAudioChannels[2].setMute( ( value & 0x10 ) != 0, MuteSerialInput, cycle );
|
||||||
|
mAudioChannels[3].setMute( ( value & 0x10 ) != 0, MuteSerialInput, cycle );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t sampleAudio( DivDispatchOscBuffer** oscb )
|
||||||
|
{
|
||||||
|
for ( ;; )
|
||||||
|
{
|
||||||
|
int64_t value = mQueue->pop();
|
||||||
|
if ( ( value & 7 ) == 6 ) // 6 == 4 ^ 2
|
||||||
|
{
|
||||||
|
int16_t ch0 = mAudioChannels[0].getOutput();
|
||||||
|
int16_t ch1 = mAudioChannels[1].getOutput();
|
||||||
|
int16_t ch2 = mAudioChannels[2].getOutput();
|
||||||
|
int16_t ch3 = mAudioChannels[3].getOutput();
|
||||||
|
|
||||||
|
if ( oscb != nullptr )
|
||||||
|
{
|
||||||
|
oscb[0]->data[oscb[0]->needle++]=ch0;
|
||||||
|
oscb[1]->data[oscb[1]->needle++]=ch1;
|
||||||
|
oscb[2]->data[oscb[2]->needle++]=ch2;
|
||||||
|
oscb[3]->data[oscb[3]->needle++]=ch3;
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueueSampling();
|
||||||
|
return ch0 + ch1 + ch2 + ch3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fireTimer( value );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const* getRegisterPool()
|
||||||
|
{
|
||||||
|
for ( size_t i = 0; i < mAudioChannels.size(); ++i )
|
||||||
|
{
|
||||||
|
mAudioChannels[i].fillRegisterPool( mRegisterPool.data() + 2 * i );
|
||||||
|
}
|
||||||
|
|
||||||
|
mRegisterPool[8] = mAudctl;
|
||||||
|
|
||||||
|
return mRegisterPool.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void initMute( int64_t cycle )
|
||||||
|
{
|
||||||
|
mAudioChannels[0].setMute( mInit && ( mAudctl & 0x40 ) == 0, MuteInit, cycle );
|
||||||
|
mAudioChannels[1].setMute( mInit && ( mAudctl & 0x50 ) != 0x50, MuteInit, cycle );
|
||||||
|
mAudioChannels[2].setMute( mInit && ( mAudctl & 0x20 ) == 0, MuteInit, cycle );
|
||||||
|
mAudioChannels[3].setMute( mInit && ( mAudctl & 0x28 ) != 0x28, MuteInit, cycle );
|
||||||
|
}
|
||||||
|
|
||||||
|
void fireTimer( int64_t tick )
|
||||||
|
{
|
||||||
|
mTick = tick & ~7;
|
||||||
|
size_t ch = tick & 3;
|
||||||
|
auto cycle = tick >> 3;
|
||||||
|
|
||||||
|
switch ( ch )
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
if ( ( mSkctl & 0x88 ) == 8 ) // two-tone, sending 1 (i.e. timer1)
|
||||||
|
mAudioChannels[1].renewTrigger( cycle );
|
||||||
|
mAudioChannels[0].trigger( cycle, *this );
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if ( ( mAudctl & 0x10 ) != 0 )
|
||||||
|
mAudioChannels[0].overrideTrigger( cycle + mReloadCycles1 );
|
||||||
|
else if ( ( mSkctl & 8 ) != 0 ) // two-tone
|
||||||
|
mAudioChannels[0].renewTrigger( cycle );
|
||||||
|
mAudioChannels[1].trigger( cycle, *this );
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ( ( mAudctl & 4 ) != 0 && mAudioChannels[0].getOutput() > 0 && mAudioChannels[0].mute() == 0 )
|
||||||
|
mAudioChannels[0].toggle();
|
||||||
|
mAudioChannels[2].trigger( cycle, *this );
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if ( ( mAudctl & 8 ) != 0 )
|
||||||
|
mAudioChannels[2].overrideTrigger( cycle + mReloadCycles3 );
|
||||||
|
if ( ( mAudctl & 2 ) != 0 && mAudioChannels[1].getOutput() > 0 && mAudioChannels[1].mute() == 0 )
|
||||||
|
mAudioChannels[1].toggle();
|
||||||
|
mAudioChannels[3].trigger( cycle, *this );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void enqueueSampling()
|
||||||
|
{
|
||||||
|
mTick = mNextTick & ~7;
|
||||||
|
mNextTick = mNextTick + mTicksPerSample.first;
|
||||||
|
mSamplesRemainder += mTicksPerSample.second;
|
||||||
|
if ( mSamplesRemainder > mSampleRate )
|
||||||
|
{
|
||||||
|
mSamplesRemainder %= mSampleRate;
|
||||||
|
mNextTick += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
mQueue->insertSampling( mNextTick & ~7 );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unique_ptr<ActionQueue> mQueue;
|
||||||
|
std::array<AudioChannel, 4> mAudioChannels;
|
||||||
|
|
||||||
|
std::array<uint8_t, 4 * 2 + 1> mRegisterPool;
|
||||||
|
|
||||||
|
uint32_t mPokeyClock;
|
||||||
|
uint64_t mTick;
|
||||||
|
uint64_t mNextTick;
|
||||||
|
uint32_t mSampleRate;
|
||||||
|
uint32_t mSamplesRemainder;
|
||||||
|
int mReloadCycles1;
|
||||||
|
int mReloadCycles3;
|
||||||
|
int mDivCycles;
|
||||||
|
std::pair<uint32_t, uint32_t> mTicksPerSample;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Pokey::Pokey( uint32_t pokeyClock, uint32_t sampleRate ) : mPokey{ std::make_unique<PokeyPimpl>( mPokeyClock, mSampleRate ) }, mPokeyClock{ pokeyClock }, mSampleRate{ sampleRate }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Pokey::~Pokey()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pokey::write( uint8_t address, uint8_t value )
|
||||||
|
{
|
||||||
|
mPokey->write( address, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t Pokey::sampleAudio( DivDispatchOscBuffer** oscb )
|
||||||
|
{
|
||||||
|
return mPokey->sampleAudio( oscb ) * 160; //just some magick value to match the audio level of mzpokeysnd
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t const* Pokey::getRegisterPool()
|
||||||
|
{
|
||||||
|
return mPokey->getRegisterPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pokey::reset()
|
||||||
|
{
|
||||||
|
mPokey = std::make_unique<PokeyPimpl>( mPokeyClock, mSampleRate );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
35
src/engine/platform/sound/pokey/AltASAP.hpp
Normal file
35
src/engine/platform/sound/pokey/AltASAP.hpp
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
// can you forgive me
|
||||||
|
#include "../../../dispatch.h"
|
||||||
|
|
||||||
|
namespace AltASAP
|
||||||
|
{
|
||||||
|
|
||||||
|
class PokeyPimpl;
|
||||||
|
|
||||||
|
class Pokey
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Pokey( uint32_t pokeyClock, uint32_t sampleRate );
|
||||||
|
~Pokey();
|
||||||
|
|
||||||
|
void write( uint8_t address, uint8_t value );
|
||||||
|
int16_t sampleAudio( DivDispatchOscBuffer** oscb = nullptr );
|
||||||
|
|
||||||
|
uint8_t const* getRegisterPool();
|
||||||
|
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::unique_ptr<PokeyPimpl> mPokey;
|
||||||
|
uint32_t mPokeyClock;
|
||||||
|
uint32_t mSampleRate;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -1178,6 +1178,7 @@ class FurnaceGUI {
|
||||||
int nesCore;
|
int nesCore;
|
||||||
int fdsCore;
|
int fdsCore;
|
||||||
int c64Core;
|
int c64Core;
|
||||||
|
int pokeyCore;
|
||||||
int pcSpeakerOutMethod;
|
int pcSpeakerOutMethod;
|
||||||
String yrw801Path;
|
String yrw801Path;
|
||||||
String tg100Path;
|
String tg100Path;
|
||||||
|
@ -1310,6 +1311,7 @@ class FurnaceGUI {
|
||||||
nesCore(0),
|
nesCore(0),
|
||||||
fdsCore(0),
|
fdsCore(0),
|
||||||
c64Core(1),
|
c64Core(1),
|
||||||
|
pokeyCore(0),
|
||||||
pcSpeakerOutMethod(0),
|
pcSpeakerOutMethod(0),
|
||||||
yrw801Path(""),
|
yrw801Path(""),
|
||||||
tg100Path(""),
|
tg100Path(""),
|
||||||
|
|
|
@ -102,6 +102,11 @@ const char* c64Cores[]={
|
||||||
"reSIDfp"
|
"reSIDfp"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const char* pokeyCores[]={
|
||||||
|
"Atari800 (mzpokeysnd)",
|
||||||
|
"ASAP (C++ port)"
|
||||||
|
};
|
||||||
|
|
||||||
const char* pcspkrOutMethods[]={
|
const char* pcspkrOutMethods[]={
|
||||||
"evdev SND_TONE",
|
"evdev SND_TONE",
|
||||||
"KIOCSOUND on /dev/tty1",
|
"KIOCSOUND on /dev/tty1",
|
||||||
|
@ -1069,6 +1074,10 @@ void FurnaceGUI::drawSettings() {
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2);
|
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2);
|
||||||
|
|
||||||
|
ImGui::Text("POKEY core");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Combo("##POKEYCore",&settings.pokeyCore,pokeyCores,2);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::Text("PC Speaker strategy");
|
ImGui::Text("PC Speaker strategy");
|
||||||
|
@ -2336,6 +2345,7 @@ void FurnaceGUI::syncSettings() {
|
||||||
settings.nesCore=e->getConfInt("nesCore",0);
|
settings.nesCore=e->getConfInt("nesCore",0);
|
||||||
settings.fdsCore=e->getConfInt("fdsCore",0);
|
settings.fdsCore=e->getConfInt("fdsCore",0);
|
||||||
settings.c64Core=e->getConfInt("c64Core",1);
|
settings.c64Core=e->getConfInt("c64Core",1);
|
||||||
|
settings.pokeyCore=e->getConfInt("pokeyCore",0);
|
||||||
settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0);
|
settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0);
|
||||||
settings.yrw801Path=e->getConfString("yrw801Path","");
|
settings.yrw801Path=e->getConfString("yrw801Path","");
|
||||||
settings.tg100Path=e->getConfString("tg100Path","");
|
settings.tg100Path=e->getConfString("tg100Path","");
|
||||||
|
@ -2461,6 +2471,7 @@ void FurnaceGUI::syncSettings() {
|
||||||
clampSetting(settings.nesCore,0,1);
|
clampSetting(settings.nesCore,0,1);
|
||||||
clampSetting(settings.fdsCore,0,1);
|
clampSetting(settings.fdsCore,0,1);
|
||||||
clampSetting(settings.c64Core,0,1);
|
clampSetting(settings.c64Core,0,1);
|
||||||
|
clampSetting(settings.pokeyCore,0,1);
|
||||||
clampSetting(settings.pcSpeakerOutMethod,0,4);
|
clampSetting(settings.pcSpeakerOutMethod,0,4);
|
||||||
clampSetting(settings.mainFont,0,6);
|
clampSetting(settings.mainFont,0,6);
|
||||||
clampSetting(settings.patFont,0,6);
|
clampSetting(settings.patFont,0,6);
|
||||||
|
@ -2604,7 +2615,8 @@ void FurnaceGUI::commitSettings() {
|
||||||
settings.snCore!=e->getConfInt("snCore",0) ||
|
settings.snCore!=e->getConfInt("snCore",0) ||
|
||||||
settings.nesCore!=e->getConfInt("nesCore",0) ||
|
settings.nesCore!=e->getConfInt("nesCore",0) ||
|
||||||
settings.fdsCore!=e->getConfInt("fdsCore",0) ||
|
settings.fdsCore!=e->getConfInt("fdsCore",0) ||
|
||||||
settings.c64Core!=e->getConfInt("c64Core",1)
|
settings.c64Core!=e->getConfInt("c64Core",1) ||
|
||||||
|
settings.pokeyCore!=e->getConfInt("pokeyCore",0)
|
||||||
);
|
);
|
||||||
|
|
||||||
e->setConf("mainFontSize",settings.mainFontSize);
|
e->setConf("mainFontSize",settings.mainFontSize);
|
||||||
|
@ -2624,6 +2636,7 @@ void FurnaceGUI::commitSettings() {
|
||||||
e->setConf("nesCore",settings.nesCore);
|
e->setConf("nesCore",settings.nesCore);
|
||||||
e->setConf("fdsCore",settings.fdsCore);
|
e->setConf("fdsCore",settings.fdsCore);
|
||||||
e->setConf("c64Core",settings.c64Core);
|
e->setConf("c64Core",settings.c64Core);
|
||||||
|
e->setConf("pokeyCore",settings.pokeyCore);
|
||||||
e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod);
|
e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod);
|
||||||
e->setConf("yrw801Path",settings.yrw801Path);
|
e->setConf("yrw801Path",settings.yrw801Path);
|
||||||
e->setConf("tg100Path",settings.tg100Path);
|
e->setConf("tg100Path",settings.tg100Path);
|
||||||
|
|
Loading…
Reference in a new issue