diff --git a/CMakeLists.txt b/CMakeLists.txt index cb5a3068..0bd77ebc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -424,6 +424,8 @@ src/engine/platform/sound/ymfm/ymfm_ssg.cpp src/engine/platform/sound/lynx/Mikey.cpp +src/engine/platform/sound/pokey/mzpokeysnd.c + src/engine/platform/sound/qsound.c src/engine/platform/sound/swan.cpp @@ -508,6 +510,7 @@ src/engine/platform/pcspkr.cpp src/engine/platform/segapcm.cpp src/engine/platform/qsound.cpp src/engine/platform/x1_010.cpp +src/engine/platform/pokey.cpp src/engine/platform/lynx.cpp src/engine/platform/su.cpp src/engine/platform/swan.cpp diff --git a/papers/doc/7-systems/pokey.md b/papers/doc/7-systems/pokey.md new file mode 100644 index 00000000..b05c4367 --- /dev/null +++ b/papers/doc/7-systems/pokey.md @@ -0,0 +1,34 @@ +# POKEY + +a sound and input chip developed by Atari for their 8-bit computers (Atari 400, 800, XL/XE and so on). 4 channels of signature Atari sounds. + +# effects + +- 10xx: set waveform. + - 0: harsh noise (poly5+17) + - 1: square buzz (poly5) + - 2: weird noise (poly4+5) + - 3: square buzz (poly5) + - 4: soft noise (poly17) + - 5: square + - 6: bass (poly4) + - 7: buzz (poly4) +- 11xx: set AUDCTL. `xx` is a bitmask. + - bit 7: 9-bit poly mode. shortens noise. + - bit 6: high channel 1 clock (~1.79MHz on NTSC). + - overrides 15KHz mode. + - bit 5: high channel 3 clock (~1.79MHz on NTSC). + - overrides 15KHz mode. + - bit 4: join channels 1 and 2 for a wide period range. + - use with conjunction with bit 6. + - channel 2 becomes inaccessible when this is on. + - bit 3: join channels 3 and 4 for a wide period range. + - use with conjunction with bit 5. + - channel 4 becomes inaccessible when this is on. + - bit 2: high-pass filter (channels 1 and 3). + - filtered output on channel 1 (I suggest you to set channel 3 volume to 0). + - use for PWM effects (not automatic!). + - bit 1: high-pass filter (channels 2 and 4). + - filtered output on channel 2 (I suggest you to set channel 4 volume to 0). + - use for PWM effects (not automatic!). + - bit 0: 15KHz mode. diff --git a/src/engine/dispatchContainer.cpp b/src/engine/dispatchContainer.cpp index a389a89e..b578f988 100644 --- a/src/engine/dispatchContainer.cpp +++ b/src/engine/dispatchContainer.cpp @@ -57,6 +57,7 @@ #include "platform/su.h" #include "platform/swan.h" #include "platform/lynx.h" +#include "platform/pokey.h" #include "platform/zxbeeper.h" #include "platform/bubsyswsg.h" #include "platform/n163.h" @@ -338,6 +339,9 @@ void DivDispatchContainer::init(DivSystem sys, DivEngine* eng, int chanCount, do case DIV_SYSTEM_LYNX: dispatch=new DivPlatformLynx; break; + case DIV_SYSTEM_POKEY: + dispatch=new DivPlatformPOKEY; + break; case DIV_SYSTEM_QSOUND: dispatch=new DivPlatformQSound; break; diff --git a/src/engine/platform/arcade.cpp b/src/engine/platform/arcade.cpp index e1ce592b..e7324998 100644 --- a/src/engine/platform/arcade.cpp +++ b/src/engine/platform/arcade.cpp @@ -353,9 +353,9 @@ void DivPlatformArcade::tick(bool sysTick) { chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; if (!parent->song.oldArpStrategy) { if (chan[i].fixedArp) { - chan[i].freq=(chan[i].baseNoteOverride<<7)+(chan[i].pitch>>1)-64+chan[i].pitch2; + chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2; } else { - chan[i].freq+=chan[i].arpOff<<7; + chan[i].freq+=chan[i].arpOff<<6; } } if (chan[i].freq<0) chan[i].freq=0; diff --git a/src/engine/platform/pokey.cpp b/src/engine/platform/pokey.cpp new file mode 100644 index 00000000..654ceaf4 --- /dev/null +++ b/src/engine/platform/pokey.cpp @@ -0,0 +1,446 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "pokey.h" +#include "../engine.h" +#include "../../ta-log.h" + +#define rWrite(a,v) if (!skipRegisterWrites) {writes.emplace(a,v); if (dumpWrites) {addWrite(a,v);} } + +#define CHIP_DIVIDER 1 + +const char* regCheatSheetPOKEY[]={ + "AUDF1", "0", + "AUDC1", "1", + "AUDF2", "2", + "AUDC2", "3", + "AUDF3", "4", + "AUDC3", "5", + "AUDF4", "6", + "AUDC4", "7", + "AUDCTL", "8", + NULL +}; + +// LLsLSsLLsSLsLLn +const unsigned char snapPeriodLong[15]={ + 0, 1, 1, 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, 11, 11, 11, 11, 17, 17 +}; + +// LsSLsLLnLLsLSsL +const unsigned char snapPeriodLong16[15]={ + 0, 0, 3, 3, 3, 5, 6, 6, 8, 9, 9, 11, 11, 14, 14 +}; + +const unsigned char snapPeriodShort16[15]={ + 1, 1, 1, 4, 4, 4, 4, 4, 10, 10, 10, 10, 13, 13, 13 +}; + +const unsigned char waveMap[8]={ + 0, 1, 2, 3, 4, 5, 6, 6 +}; + +const char** DivPlatformPOKEY::getRegisterSheet() { + return regCheatSheetPOKEY; +} + +void DivPlatformPOKEY::acquire(short* bufL, short* bufR, size_t start, size_t len) { + for (size_t h=start; hcalcArp(chan[i].note,chan[i].std.arp.val)); + } + chan[i].freqChanged=true; + } + if (chan[i].std.duty.had) { + audctl=chan[i].std.duty.val; + audctlChanged=true; + } + if (chan[i].std.wave.had) { + chan[i].wave=chan[i].std.wave.val; + chan[i].ctlChanged=true; + chan[i].freqChanged=true; + } + if (chan[i].std.pitch.had) { + if (chan[i].std.pitch.mode) { + chan[i].pitch2+=chan[i].std.pitch.val; + CLAMP_VAR(chan[i].pitch2,-32768,32767); + } else { + chan[i].pitch2=chan[i].std.pitch.val; + } + chan[i].freqChanged=true; + } + } + + if (audctlChanged) { + audctlChanged=false; + rWrite(8,audctl); + for (int i=0; i<4; i++) { + chan[i].freqChanged=true; + chan[i].ctlChanged=true; + } + } + + for (int i=0; i<4; i++) { + if (chan[i].freqChanged || chan[i].keyOn || chan[i].keyOff) { + 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==0 && !(audctl&64)) || (i==2 && !(audctl&32)) || i==1 || i==3) { + chan[i].freq/=7; + switch (chan[i].wave) { + case 6: + chan[i].freq/=5; + chan[i].freq>>=1; + break; + case 7: + if (audctl&1) { + chan[i].freq/=5; + } else { + chan[i].freq/=15; + } + chan[i].freq>>=1; + break; + default: + chan[i].freq>>=2; + break; + } + } else if ((i==0 && audctl&64) || (i==2 && audctl&32)) { + switch (chan[i].wave) { + case 6: + chan[i].freq<<=1; + chan[i].freq/=5; + break; + case 7: + chan[i].freq<<=1; + chan[i].freq/=15; + break; + } + } + + if (audctl&1 && !((i==0 && audctl&64) || (i==2 && audctl&32))) { + chan[i].freq>>=2; + } + + if (--chan[i].freq<0) chan[i].freq=0; + + // snap buzz periods + int minFreq8=255; + if (chan[i].wave==7) { + if ((i==0 && audctl&64) || (i==2 && audctl&32)) { + chan[i].freq=15*(chan[i].freq/15)+snapPeriodLong16[(chan[i].freq%15)]+1; + } else { + if (!(audctl&1)) chan[i].freq=15*(chan[i].freq/15)+snapPeriodLong[(chan[i].freq%15)]; + } + } else if (chan[i].wave==6) { + if ((i==0 && audctl&64) || (i==2 && audctl&32)) { + chan[i].freq=15*(chan[i].freq/15)+snapPeriodShort16[(chan[i].freq%15)]+1; + } else { + if (!(audctl&1)) chan[i].freq=15*(chan[i].freq/15)+snapPeriodShort[(chan[i].freq%15)]; + } + minFreq8=251; + } + + if ((i==0 && audctl&16) || (i==2 && audctl&8)) { + if (chan[i].freq>65535) chan[i].freq=65535; + } else { + if (chan[i].freq>minFreq8) chan[i].freq=minFreq8; + } + + // write frequency + if ((i==1 && audctl&16) || (i==3 && audctl&8)) { + // ignore - channel is paired + } else { + rWrite(i<<1,chan[i].freq&0xff); + if ((i==0 && audctl&16) || (i==2 && audctl&8)) { + rWrite((1+i)<<1,chan[i].freq>>8); + } + } + + if (chan[i].keyOff) { + chan[i].ctlChanged=true; + } + if (chan[i].keyOn) chan[i].keyOn=false; + if (chan[i].keyOff) chan[i].keyOff=false; + chan[i].freqChanged=false; + } + if (chan[i].ctlChanged) { + unsigned char val=((chan[i].active && !isMuted[i])?(chan[i].outVol&15):0)|(waveMap[chan[i].wave&7]<<5); + chan[i].ctlChanged=false; + if ((i==1 && audctl&16) || (i==3 && audctl&8)) { + // ignore - channel is paired + } else if ((i==0 && audctl&16) || (i==0 && audctl&8)) { + rWrite(1+(i<<1),0); + rWrite(3+(i<<1),val); + } else { + + rWrite(1+(i<<1),val); + } + } + } +} + +int DivPlatformPOKEY::dispatch(DivCommand c) { + switch (c.cmd) { + case DIV_CMD_NOTE_ON: { + DivInstrument* ins=parent->getIns(chan[c.chan].ins,DIV_INS_POKEY); + if (c.value!=DIV_NOTE_NULL) { + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + chan[c.chan].freqChanged=true; + chan[c.chan].note=c.value; + } + chan[c.chan].active=true; + chan[c.chan].keyOn=true; + chan[c.chan].macroInit(ins); + if (!parent->song.brokenOutVol && !chan[c.chan].std.vol.will) { + chan[c.chan].outVol=chan[c.chan].vol; + chan[c.chan].ctlChanged=true; + } + chan[c.chan].insChanged=false; + break; + } + case DIV_CMD_NOTE_OFF: + chan[c.chan].active=false; + chan[c.chan].keyOff=true; + chan[c.chan].macroInit(NULL); + break; + case DIV_CMD_NOTE_OFF_ENV: + case DIV_CMD_ENV_RELEASE: + chan[c.chan].std.release(); + break; + case DIV_CMD_INSTRUMENT: + if (chan[c.chan].ins!=c.value || c.value2==1) { + chan[c.chan].ins=c.value; + 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].ctlChanged=true; + } + } + } + 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].ctlChanged=true; + break; + case DIV_CMD_STD_NOISE_MODE: + audctl=c.value&0xff; + audctlChanged=true; + break; + case DIV_CMD_NOTE_PORTA: { + int destFreq=NOTE_PERIODIC(c.value2); + bool return2=false; + if (destFreq>chan[c.chan].baseFreq) { + chan[c.chan].baseFreq+=c.value; + if (chan[c.chan].baseFreq>=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } else { + chan[c.chan].baseFreq-=c.value; + if (chan[c.chan].baseFreq<=destFreq) { + chan[c.chan].baseFreq=destFreq; + return2=true; + } + } + chan[c.chan].freqChanged=true; + if (return2) { + chan[c.chan].inPorta=false; + return 2; + } + break; + } + case DIV_CMD_LEGATO: + chan[c.chan].baseFreq=NOTE_PERIODIC(c.value); + 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_POKEY)); + } + if (!chan[c.chan].inPorta && c.value && !parent->song.brokenPortaArp && chan[c.chan].std.arp.will && !NEW_ARP_STRAT) chan[c.chan].baseFreq=NOTE_PERIODIC(chan[c.chan].note); + chan[c.chan].inPorta=c.value; + break; + case DIV_CMD_GET_VOLMAX: + return 15; + break; + case DIV_CMD_MACRO_OFF: + chan[c.chan].std.mask(c.value,true); + break; + case DIV_CMD_MACRO_ON: + chan[c.chan].std.mask(c.value,false); + break; + case DIV_ALWAYS_SET_VOLUME: + return 1; + break; + default: + break; + } + return 1; +} + +void DivPlatformPOKEY::muteChannel(int ch, bool mute) { + isMuted[ch]=mute; + chan[ch].ctlChanged=true; +} + +void DivPlatformPOKEY::forceIns() { + for (int i=0; i<4; i++) { + chan[i].insChanged=true; + chan[i].ctlChanged=true; + chan[i].freqChanged=true; + } +} + +void* DivPlatformPOKEY::getChanState(int ch) { + return &chan[ch]; +} + +DivMacroInt* DivPlatformPOKEY::getChanMacroInt(int ch) { + return &chan[ch].std; +} + +DivDispatchOscBuffer* DivPlatformPOKEY::getOscBuffer(int ch) { + return oscBuf[ch]; +} + +unsigned char* DivPlatformPOKEY::getRegisterPool() { + return regPool; +} + +int DivPlatformPOKEY::getRegisterPoolSize() { + return 9; +} + +void DivPlatformPOKEY::reset() { + while (!writes.empty()) writes.pop(); + memset(regPool,0,16); + for (int i=0; i<4; i++) { + chan[i]=DivPlatformPOKEY::Channel(); + chan[i].std.setEngine(parent); + } + if (dumpWrites) { + addWrite(0xffffffff,0); + } + + ResetPokeyState(&pokey); + + audctl=0; + audctlChanged=true; +} + +bool DivPlatformPOKEY::keyOffAffectsArp(int ch) { + return true; +} + +float DivPlatformPOKEY::getPostAmp() { + return 2.0f; +} + +void DivPlatformPOKEY::notifyInsDeletion(void* ins) { + for (int i=0; i<4; i++) { + chan[i].std.notifyInsDeletion((DivInstrument*)ins); + } +} + +void DivPlatformPOKEY::setFlags(const DivConfig& flags) { + if (flags.getInt("clockSel",0)) { + chipClock=COLOR_PAL*2.0/5.0; + } else { + chipClock=COLOR_NTSC/2.0; + } + CHECK_CUSTOM_CLOCK; + rate=chipClock; + for (int i=0; i<4; i++) { + oscBuf[i]->rate=rate/16; + } +} + +void DivPlatformPOKEY::poke(unsigned int addr, unsigned short val) { + rWrite(addr,val); +} + +void DivPlatformPOKEY::poke(std::vector& wlist) { + for (DivRegWrite& i: wlist) rWrite(i.addr,i.val); +} + +int DivPlatformPOKEY::init(DivEngine* p, int channels, int sugRate, const DivConfig& flags) { + parent=p; + dumpWrites=false; + skipRegisterWrites=false; + for (int i=0; i<4; i++) { + isMuted[i]=false; + oscBuf[i]=new DivDispatchOscBuffer; + } + + MZPOKEYSND_Init(&pokey); + + setFlags(flags); + reset(); + return 6; +} + +void DivPlatformPOKEY::quit() { + for (int i=0; i<4; i++) { + delete oscBuf[i]; + } +} + +DivPlatformPOKEY::~DivPlatformPOKEY() { +} diff --git a/src/engine/platform/pokey.h b/src/engine/platform/pokey.h new file mode 100644 index 00000000..34751677 --- /dev/null +++ b/src/engine/platform/pokey.h @@ -0,0 +1,78 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2022 tildearrow and contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _POKEY_H +#define _POKEY_H + +#include "../dispatch.h" +#include + +extern "C" { +#include "sound/pokey/mzpokeysnd.h" +} + +class DivPlatformPOKEY: public DivDispatch { + struct Channel: public SharedChannel { + unsigned char wave; + bool ctlChanged; + Channel(): + SharedChannel(15), + wave(5), + ctlChanged(true) {} + }; + Channel chan[4]; + DivDispatchOscBuffer* oscBuf[4]; + bool isMuted[4]; + struct QueuedWrite { + unsigned char addr; + unsigned char val; + QueuedWrite(unsigned char a, unsigned char v): addr(a), val(v) {} + }; + std::queue writes; + unsigned char audctl; + bool audctlChanged; + PokeyState pokey; + unsigned char regPool[16]; + friend void putDispatchChip(void*,int); + friend void putDispatchChan(void*,int,int); + public: + void acquire(short* bufL, short* bufR, size_t start, size_t len); + int dispatch(DivCommand c); + void* getChanState(int chan); + DivMacroInt* getChanMacroInt(int ch); + DivDispatchOscBuffer* getOscBuffer(int chan); + unsigned char* getRegisterPool(); + int getRegisterPoolSize(); + void reset(); + void forceIns(); + void tick(bool sysTick=true); + void muteChannel(int ch, bool mute); + bool keyOffAffectsArp(int ch); + float getPostAmp(); + 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(); + ~DivPlatformPOKEY(); +}; + +#endif diff --git a/src/engine/platform/sound/pokey/mzpokeysnd.c b/src/engine/platform/sound/pokey/mzpokeysnd.c index 1dee5422..6395f31d 100644 --- a/src/engine/platform/sound/pokey/mzpokeysnd.c +++ b/src/engine/platform/sound/pokey/mzpokeysnd.c @@ -28,6 +28,7 @@ #include #include +#include "pokey.h" #include "mzpokeysnd.h" #define CONSOLE_VOL 8 @@ -52,212 +53,27 @@ static const double pokeymix[61+CONSOLE_VOL] = { /* Nonlinear POKEY mixing array 120.000000,120.0,120.0,120.0,120.0,120.0,120.0,120.0,120.0}; #endif -#define SND_FILTER_SIZE 2048 - -/* Filter */ -static int pokey_frq; /* Hz - for easier resampling */ -static int filter_size; -static double filter_data[SND_FILTER_SIZE]; -static int audible_frq; - -static const int pokey_frq_ideal = 1789790; /* Hz - True */ - -/* Flags and quality */ -static int snd_quality = 0; - /* Poly tables */ static int poly4tbl[15]; static int poly5tbl[31]; static unsigned char poly17tbl[131071]; static int poly9tbl[511]; - -struct stPokeyState; - -typedef int (*readout_t)(struct stPokeyState* ps); -typedef void (*event_t)(struct stPokeyState* ps, int p5v, int p4v, int p917v); - -#ifdef NONLINEAR_MIXING -/* Change queue event value type */ -typedef double qev_t; -#else -typedef unsigned char qev_t; -#endif - -#ifdef SYNCHRONIZED_SOUND -static double ticks_per_sample; -static double samp_pos; -#endif /* SYNCHRONIZED_SOUND */ - -/* State variables for single Pokey Chip */ -typedef struct stPokeyState -{ - int curtick; - /* Poly positions */ - int poly4pos; - int poly5pos; - int poly17pos; - int poly9pos; - - /* Change queue */ - qev_t ovola; - int qet[1322]; /* maximal length of filter */ - qev_t qev[1322]; - int qebeg; - int qeend; - - /* Main divider (64khz/15khz) */ - int mdivk; /* 28 for 64khz, 114 for 15khz */ - - /* Main switches */ - int selpoly9; - int c0_hf; - int c1_f0; - int c2_hf; - int c3_f2; - - /* SKCTL for two-tone mode */ - int skctl; - - /* Main output state */ - qev_t outvol_all; - int forcero; /* Force readout */ - - /* channel 0 state */ - - readout_t readout_0; - event_t event_0; - - int c0divpos; - int c0divstart; /* AUDF0 recalculated */ - int c0divstart_p; /* start value when c1_f0 */ - int c0diva; /* AUDF0 register */ - - int c0t1; /* D - 5bit, Q goes to sw3 */ - int c0t2; /* D - out sw2, Q goes to sw4 and t3 */ - int c0t3; /* D - out t2, q goes to xor */ - - int c0sw1; /* in1 - 4bit, in2 - 17bit, out goes to sw2 */ - int c0sw2; /* in1 - /Q t2, in2 - out sw1, out goes to t2 */ - int c0sw3; /* in1 - +5, in2 - Q t1, out goes to C t2 */ - int c0sw4; /* hi-pass sw */ - int c0vo; /* volume only */ - -#ifndef NONLINEAR_MIXING - int c0stop; /* channel counter stopped */ -#endif - - int vol0; - - int outvol_0; - - /* channel 1 state */ - - readout_t readout_1; - event_t event_1; - - int c1divpos; - int c1divstart; - int c1diva; - - int c1t1; - int c1t2; - int c1t3; - - int c1sw1; - int c1sw2; - int c1sw3; - int c1sw4; - int c1vo; - -#ifndef NONLINEAR_MIXING - int c1stop; /* channel counter stopped */ -#endif - - int vol1; - - int outvol_1; - - /* channel 2 state */ - - readout_t readout_2; - event_t event_2; - - int c2divpos; - int c2divstart; - int c2divstart_p; /* start value when c1_f0 */ - int c2diva; - - int c2t1; - int c2t2; - - int c2sw1; - int c2sw2; - int c2sw3; - int c2vo; - -#ifndef NONLINEAR_MIXING - int c2stop; /* channel counter stopped */ -#endif - - int vol2; - - int outvol_2; - - /* channel 3 state */ - - readout_t readout_3; - event_t event_3; - - int c3divpos; - int c3divstart; - int c3diva; - - int c3t1; - int c3t2; - - int c3sw1; - int c3sw2; - int c3sw3; - int c3vo; - -#ifndef NONLINEAR_MIXING - int c3stop; /* channel counter stopped */ -#endif - - int vol3; - - int outvol_3; - - /* GTIA speaker */ - - int speaker; - -} PokeyState; - -// TODO: make this fully struct-ized -PokeyState pokey_states[1]; - -static struct { - double s16; - double s8; -} volume; - /* Forward declarations for ResetPokeyState */ -static int readout0_normal(PokeyState* ps); -static void event0_pure(PokeyState* ps, int p5v, int p4v, int p917v); +int readout0_normal(PokeyState* ps); +void event0_pure(PokeyState* ps, int p5v, int p4v, int p917v); -static int readout1_normal(PokeyState* ps); -static void event1_pure(PokeyState* ps, int p5v, int p4v, int p917v); +int readout1_normal(PokeyState* ps); +void event1_pure(PokeyState* ps, int p5v, int p4v, int p917v); -static int readout2_normal(PokeyState* ps); -static void event2_pure(PokeyState* ps, int p5v, int p4v, int p917v); +int readout2_normal(PokeyState* ps); +void event2_pure(PokeyState* ps, int p5v, int p4v, int p917v); -static int readout3_normal(PokeyState* ps); -static void event3_pure(PokeyState* ps, int p5v, int p4v, int p917v); +int readout3_normal(PokeyState* ps); +void event3_pure(PokeyState* ps, int p5v, int p4v, int p917v); -static void ResetPokeyState(PokeyState* ps) +void ResetPokeyState(PokeyState* ps) { /* Poly positions */ ps->poly4pos = 0; @@ -265,11 +81,6 @@ static void ResetPokeyState(PokeyState* ps) ps->poly9pos = 0; ps->poly17pos = 0; - /* Change queue */ - ps->ovola = 0; - ps->qebeg = 0; - ps->qeend = 0; - /* Global Pokey controls */ ps->mdivk = 28; @@ -389,172 +200,8 @@ static void ResetPokeyState(PokeyState* ps) ps->vol3 = 0; ps->outvol_3 = 0; - - /* GTIA speaker */ - ps->speaker = 0; } - -static double read_resam_all(PokeyState* ps) -{ - int i = ps->qebeg; - qev_t avol,bvol; - double sum; - - if(ps->qebeg == ps->qeend) - { - return ps->ovola * filter_data[0]; /* if no events in the queue */ - } - - avol = ps->ovola; - sum = 0; - - /* Separate two loop cases, for wrap-around and without */ - if(ps->qeend < ps->qebeg) /* With wrap */ - { - while(iqev[i]; - sum += (avol-bvol)*filter_data[ps->curtick - ps->qet[i]]; - avol = bvol; - ++i; - } - i=0; - } - - /* without wrap */ - while(iqeend) - { - bvol = ps->qev[i]; - sum += (avol-bvol)*filter_data[ps->curtick - ps->qet[i]]; - avol = bvol; - ++i; - } - - sum += avol*filter_data[0]; - return sum; -} - -#ifdef SYNCHRONIZED_SOUND -/* linear interpolation of filter data */ -static double interp_filter_data(int pos, double frac) -{ - if (pos+1 >= filter_size) { - return 0.0; - } - return (frac)*filter_data[pos+1]+(1-frac)*(filter_data[pos]-filter_data[filter_size-1]); -} - -/* returns the filtered output sample value using an interpolated filter */ -/* frac is the fractional distance of the output sample point between - * input sample values */ -static double interp_read_resam_all(PokeyState* ps, double frac) -{ - int i = ps->qebeg; - qev_t avol,bvol; - double sum; - - if (ps->qebeg == ps->qeend) - { - return ps->ovola * interp_filter_data(0,frac); /* if no events in the queue */ - } - - avol = ps->ovola; - sum = 0; - - /* Separate two loop cases, for wrap-around and without */ - if (ps->qeend < ps->qebeg) /* With wrap */ - { - while (i < filter_size) - { - bvol = ps->qev[i]; - sum += (avol-bvol)*interp_filter_data(ps->curtick - ps->qet[i],frac); - avol = bvol; - ++i; - } - i = 0; - } - - /* without wrap */ - while (i < ps->qeend) - { - bvol = ps->qev[i]; - sum += (avol-bvol)*interp_filter_data(ps->curtick - ps->qet[i],frac); - avol = bvol; - ++i; - } - - sum += avol*interp_filter_data(0,frac); - - return sum; -} -#endif /* SYNCHRONIZED_SOUND */ - -static void add_change(PokeyState* ps, qev_t a) -{ - ps->qev[ps->qeend] = a; - ps->qet[ps->qeend] = ps->curtick; /*0;*/ - ++ps->qeend; - if(ps->qeend >= filter_size) - ps->qeend = 0; -} - -static void bump_qe_subticks(PokeyState* ps, int subticks) -{ - /* Remove too old events from the queue while bumping */ - int i = ps->qebeg; - /* we must avoid curtick overflow in a 32-bit int, will happen in 20 min */ - static const int tickoverflowlimit = 1000000000; - ps->curtick += subticks; - if (ps->curtick > tickoverflowlimit) { - ps->curtick -= tickoverflowlimit/2; - for (i=0; iqet[i] > tickoverflowlimit/2) { - ps->qet[i] -= tickoverflowlimit/2; - } - } - } - - - if(ps->qeend < ps->qebeg) /* Loop with wrap */ - { - while(iqet[i] += subticks;*/ - if(ps->curtick - ps->qet[i] >= filter_size - 1) - { - ps->ovola = ps->qev[i]; - ++ps->qebeg; - if(ps->qebeg >= filter_size) - ps->qebeg = 0; - } - else { - return; - } - ++i; - } - i=0; - } - /* loop without wrap */ - while(iqeend) - { - /*ps->qet[i] += subticks;*/ - if(ps->curtick - ps->qet[i] >= filter_size - 1) - { - ps->ovola = ps->qev[i]; - ++ps->qebeg; - if(ps->qebeg >= filter_size) - ps->qebeg = 0; - } - else { - return; - } - ++i; - } -} - - - static void build_poly4(void) { unsigned char c; @@ -571,44 +218,44 @@ static void build_poly4(void) static void build_poly5(void) { - unsigned char c; - unsigned char i; - unsigned char poly5 = 1; + unsigned char c; + unsigned char i; + unsigned char poly5 = 1; - for(i = 0; i < 31; i++) { - poly5tbl[i] = ~poly5; /* Inversion! Attention! */ - c = ((poly5 >> 2) ^ (poly5 >> 4)) & 1; - poly5 = ((poly5 << 1) & 31) + c; - } + for(i = 0; i < 31; i++) { + poly5tbl[i] = ~poly5; /* Inversion! Attention! */ + c = ((poly5 >> 2) ^ (poly5 >> 4)) & 1; + poly5 = ((poly5 << 1) & 31) + c; + } } static void build_poly17(void) { - unsigned int c; - unsigned int i; - unsigned int poly17 = 1; + unsigned int c; + unsigned int i; + unsigned int poly17 = 1; - for(i = 0; i < 131071; i++) { - poly17tbl[i] = (unsigned char) poly17; - c = ((poly17 >> 11) ^ (poly17 >> 16)) & 1; - poly17 = ((poly17 << 1) & 131071) + c; - } + for(i = 0; i < 131071; i++) { + poly17tbl[i] = (unsigned char) poly17; + c = ((poly17 >> 11) ^ (poly17 >> 16)) & 1; + poly17 = ((poly17 << 1) & 131071) + c; + } } static void build_poly9(void) { - unsigned int c; - unsigned int i; - unsigned int poly9 = 1; + unsigned int c; + unsigned int i; + unsigned int poly9 = 1; - for(i = 0; i < 511; i++) { - poly9tbl[i] = (unsigned char) poly9; - c = ((poly9 >> 3) ^ (poly9 >> 8)) & 1; - poly9 = ((poly9 << 1) & 511) + c; - } + for(i = 0; i < 511; i++) { + poly9tbl[i] = (unsigned char) poly9; + c = ((poly9 >> 3) ^ (poly9 >> 8)) & 1; + poly9 = ((poly9 << 1) & 511) + c; + } } -static void advance_polies(PokeyState* ps, int tacts) +void advance_polies(PokeyState* ps, int tacts) { ps->poly4pos = (tacts + ps->poly4pos) % 15; ps->poly5pos = (tacts + ps->poly5pos) % 31; @@ -622,19 +269,19 @@ static void advance_polies(PokeyState* ps, int tacts) ************************************/ -static int readout0_vo(PokeyState* ps) +int readout0_vo(PokeyState* ps) { return ps->vol0; } -static int readout0_hipass(PokeyState* ps) +int readout0_hipass(PokeyState* ps) { if(ps->c0t2 ^ ps->c0t3) return ps->vol0; else return 0; } -static int readout0_normal(PokeyState* ps) +int readout0_normal(PokeyState* ps) { if(ps->c0t2) return ps->vol0; @@ -647,19 +294,19 @@ static int readout0_normal(PokeyState* ps) ************************************/ -static int readout1_vo(PokeyState* ps) +int readout1_vo(PokeyState* ps) { return ps->vol1; } -static int readout1_hipass(PokeyState* ps) +int readout1_hipass(PokeyState* ps) { if(ps->c1t2 ^ ps->c1t3) return ps->vol1; else return 0; } -static int readout1_normal(PokeyState* ps) +int readout1_normal(PokeyState* ps) { if(ps->c1t2) return ps->vol1; @@ -672,12 +319,12 @@ static int readout1_normal(PokeyState* ps) ************************************/ -static int readout2_vo(PokeyState* ps) +int readout2_vo(PokeyState* ps) { return ps->vol2; } -static int readout2_normal(PokeyState* ps) +int readout2_normal(PokeyState* ps) { if(ps->c2t2) return ps->vol2; @@ -690,12 +337,12 @@ static int readout2_normal(PokeyState* ps) ************************************/ -static int readout3_vo(PokeyState* ps) +int readout3_vo(PokeyState* ps) { return ps->vol3; } -static int readout3_normal(PokeyState* ps) +int readout3_normal(PokeyState* ps) { if(ps->c3t2) return ps->vol3; @@ -709,39 +356,39 @@ static int readout3_normal(PokeyState* ps) ************************************/ -static void event0_pure(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_pure(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c0t2 = !ps->c0t2; ps->c0t1 = p5v; } -static void event0_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c0t1) ps->c0t2 = !ps->c0t2; ps->c0t1 = p5v; } -static void event0_p4(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_p4(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c0t2 = p4v; ps->c0t1 = p5v; } -static void event0_p917(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_p917(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c0t2 = p917v; ps->c0t1 = p5v; } -static void event0_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c0t1) ps->c0t2 = p4v; ps->c0t1 = p5v; } -static void event0_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event0_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c0t1) ps->c0t2 = p917v; @@ -754,39 +401,39 @@ static void event0_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) ************************************/ -static void event1_pure(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_pure(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c1t2 = !ps->c1t2; ps->c1t1 = p5v; } -static void event1_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c1t1) ps->c1t2 = !ps->c1t2; ps->c1t1 = p5v; } -static void event1_p4(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_p4(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c1t2 = p4v; ps->c1t1 = p5v; } -static void event1_p917(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_p917(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c1t2 = p917v; ps->c1t1 = p5v; } -static void event1_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c1t1) ps->c1t2 = p4v; ps->c1t1 = p5v; } -static void event1_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event1_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c1t1) ps->c1t2 = p917v; @@ -799,7 +446,7 @@ static void event1_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) ************************************/ -static void event2_pure(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_pure(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c2t2 = !ps->c2t2; ps->c2t1 = p5v; @@ -807,7 +454,7 @@ static void event2_pure(PokeyState* ps, int p5v, int p4v, int p917v) ps->c0t3 = ps->c0t2; } -static void event2_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c2t1) ps->c2t2 = !ps->c2t2; @@ -816,7 +463,7 @@ static void event2_p5(PokeyState* ps, int p5v, int p4v, int p917v) ps->c0t3 = ps->c0t2; } -static void event2_p4(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_p4(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c2t2 = p4v; ps->c2t1 = p5v; @@ -824,7 +471,7 @@ static void event2_p4(PokeyState* ps, int p5v, int p4v, int p917v) ps->c0t3 = ps->c0t2; } -static void event2_p917(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_p917(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c2t2 = p917v; ps->c2t1 = p5v; @@ -832,7 +479,7 @@ static void event2_p917(PokeyState* ps, int p5v, int p4v, int p917v) ps->c0t3 = ps->c0t2; } -static void event2_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c2t1) ps->c2t2 = p4v; @@ -841,7 +488,7 @@ static void event2_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) ps->c0t3 = ps->c0t2; } -static void event2_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event2_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c2t1) ps->c2t2 = p917v; @@ -856,7 +503,7 @@ static void event2_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) ************************************/ -static void event3_pure(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_pure(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c3t2 = !ps->c3t2; ps->c3t1 = p5v; @@ -864,7 +511,7 @@ static void event3_pure(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void event3_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c3t1) ps->c3t2 = !ps->c3t2; @@ -873,7 +520,7 @@ static void event3_p5(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void event3_p4(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_p4(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c3t2 = p4v; ps->c3t1 = p5v; @@ -881,7 +528,7 @@ static void event3_p4(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void event3_p917(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_p917(PokeyState* ps, int p5v, int p4v, int p917v) { ps->c3t2 = p917v; ps->c3t1 = p5v; @@ -889,7 +536,7 @@ static void event3_p917(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void event3_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c3t1) ps->c3t2 = p4v; @@ -898,7 +545,7 @@ static void event3_p4_p5(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void event3_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) +void event3_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) { if(ps->c3t1) ps->c3t2 = p917v; @@ -907,7 +554,7 @@ static void event3_p917_p5(PokeyState* ps, int p5v, int p4v, int p917v) ps->c1t3 = ps->c1t2; } -static void advance_ticks(PokeyState* ps, int ticks) +void advance_ticks(PokeyState* ps, int ticks) { int ta,tbe, tbe0, tbe1, tbe2, tbe3; int p5v,p4v,p917v; @@ -925,21 +572,13 @@ static void advance_ticks(PokeyState* ps, int ticks) { ps->forcero = 0; #ifdef NONLINEAR_MIXING -#ifdef SYNCHRONIZED_SOUND - outvol_new = pokeymix[ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3 + ps->speaker]; -#else outvol_new = pokeymix[ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3]; -#endif /* SYNCHRONIZED_SOUND */ #else outvol_new = ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3; -#ifdef SYNCHRONIZED_SOUND - outvol_new += ps->speaker; -#endif /* SYNCHRONIZED_SOUND */ #endif /* NONLINEAR_MIXING */ if(outvol_new != ps->outvol_all) { ps->outvol_all = outvol_new; - add_change(ps, outvol_new); } } @@ -995,7 +634,6 @@ static void advance_ticks(PokeyState* ps, int ticks) #endif advance_polies(ps,ta); - bump_qe_subticks(ps,ta); if(need) { @@ -1097,130 +735,24 @@ static void advance_ticks(PokeyState* ps, int ticks) } #ifdef NONLINEAR_MIXING -#ifdef SYNCHRONIZED_SOUND - outvol_new = pokeymix[ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3 + ps->speaker]; -#else outvol_new = pokeymix[ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3]; -#endif /* SYNCHRONIZED_SOUND */ #else outvol_new = ps->outvol_0 + ps->outvol_1 + ps->outvol_2 + ps->outvol_3; -#ifdef SYNCHRONIZED_SOUND - outvol_new += ps->speaker; -#endif /* SYNCHRONIZED_SOUND */ #endif /* NONLINEAR_MIXING */ if(outvol_new != ps->outvol_all) { ps->outvol_all = outvol_new; - add_change(ps, outvol_new); } } } } -static double generate_sample(PokeyState* ps) +double generate_sample(PokeyState* ps) { - /*unsigned long ta = (subticks+pokey_frq)/POKEYSND_playback_freq; - subticks = (subticks+pokey_frq)%POKEYSND_playback_freq;*/ - - advance_ticks(ps, pokey_frq/POKEYSND_playback_freq); - return read_resam_all(ps); + advance_ticks(ps, 1); + return ps->outvol_all; } -/****************************************** - filter table generator by Krzysztof Nikiel - ******************************************/ - -static int remez_filter_table(double resamp_rate, /* output_rate/input_rate */ - double *cutoff, int quality) -{ - int i; - static const int orders[] = {600, 800, 1000, 1200}; - static const struct { - int stop; /* stopband ripple */ - double weight; /* stopband weight */ - double twidth[sizeof(orders)/sizeof(orders[0])]; - } paramtab[] = - { - {70, 90, {4.9e-3, 3.45e-3, 2.65e-3, 2.2e-3}}, - {55, 25, {3.4e-3, 2.7e-3, 2.05e-3, 1.7e-3}}, - {40, 6.0, {2.6e-3, 1.8e-3, 1.5e-3, 1.2e-3}}, - {-1, 0, {0, 0, 0, 0}} - }; - static const double passtab[] = {0.5, 0.6, 0.7}; - int ripple = 0, order = 0; - int size; - double weights[2], desired[2], bands[4]; - static const int interlevel = 5; - double step = 1.0 / interlevel; - - *cutoff = 0.95 * 0.5 * resamp_rate; - - if (quality >= (int) (sizeof(passtab) / sizeof(passtab[0]))) - quality = (int) (sizeof(passtab) / sizeof(passtab[0])) - 1; - - for (ripple = 0; paramtab[ripple].stop > 0; ripple++) - { - for (order = 0; order < (int) (sizeof(orders)/sizeof(orders[0])); order++) - { - if ((*cutoff - paramtab[ripple].twidth[order]) - > passtab[quality] * 0.5 * resamp_rate) - /* transition width OK */ - goto found; - } - } - - /* not found -- use shortest transition */ - ripple--; - order--; - -found: - - size = orders[order] + 1; - - if (size > SND_FILTER_SIZE) /* static table too short */ - return 0; - - desired[0] = 1; - desired[1] = 0; - - weights[0] = 1; - weights[1] = paramtab[ripple].weight; - - bands[0] = 0; - bands[2] = *cutoff; - bands[1] = bands[2] - paramtab[ripple].twidth[order]; - bands[3] = 0.5; - - bands[1] *= (double)interlevel; - bands[2] *= (double)interlevel; - REMEZ_CreateFilter(filter_data, (size / interlevel) + 1, 2, bands, desired, weights, REMEZ_BANDPASS); - for (i = size - interlevel; i >= 0; i -= interlevel) - { - int s; - double h1 = filter_data[i/interlevel]; - double h2 = filter_data[i/interlevel+1]; - - for (s = 0; s < interlevel; s++) - { - double d = (double)s * step; - filter_data[i+s] = (h1*(1.0 - d) + h2 * d) * step; - } - } - - /* compute reversed cumulative sum table */ - for (i = size - 2; i >= 0; i--) - filter_data[i] += filter_data[i + 1]; - - return size; -} - -void mzpokeysnd_process_8(void* sndbuffer, int sndn); -void mzpokeysnd_process_16(void* sndbuffer, int sndn); -void Update_pokey_sound_mz(UWORD addr, UBYTE val, UBYTE gain); -#ifdef VOL_ONLY_SOUND -static void Update_vol_only_sound_mz( void ); -#endif - /*****************************************************************************/ /* Module: MZPOKEYSND_Init() */ /* Purpose: to handle the power-up initialization functions */ @@ -1235,66 +767,18 @@ static void Update_vol_only_sound_mz( void ); /* */ /*****************************************************************************/ -#ifdef SYNCHRONIZED_SOUND -static void generate_sync(unsigned int num_ticks); - -static void init_syncsound(void) +int MZPOKEYSND_Init(PokeyState* ps) { - double samples_per_frame = (double)POKEYSND_playback_freq/(Atari800_tv_mode == Atari800_TV_PAL ? Atari800_FPS_PAL : Atari800_FPS_NTSC); - unsigned int ticks_per_frame = Atari800_tv_mode*114; - ticks_per_sample = (double)ticks_per_frame / samples_per_frame; - samp_pos = 0.0; - POKEYSND_GenerateSync = generate_sync; -} -#endif /* SYNCHRONIZED_SOUND */ - -int MZPOKEYSND_Init(size_t freq17, int playback_freq, - int flags, int quality - , int clear_regs - ) -{ - double cutoff; - - snd_quality = quality; - - POKEYSND_Update_ptr = Update_pokey_sound_mz; -#ifdef VOL_ONLY_SOUND - POKEYSND_UpdateVolOnly = Update_vol_only_sound_mz; -#endif - -#ifdef VOL_ONLY_SOUND - POKEYSND_samp_freq=playback_freq; -#endif /* VOL_ONLY_SOUND */ - - POKEYSND_Process_ptr = (flags & POKEYSND_BIT16) ? mzpokeysnd_process_16 : mzpokeysnd_process_8; - - pokey_frq = (int)(((double)pokey_frq_ideal/POKEYSND_playback_freq) + 0.5) - * POKEYSND_playback_freq; - filter_size = remez_filter_table((double)POKEYSND_playback_freq/pokey_frq, - &cutoff, quality); - audible_frq = (int ) (cutoff * pokey_frq); - build_poly4(); build_poly5(); build_poly9(); build_poly17(); - - if (clear_regs) - { - ResetPokeyState(pokey_states); - } - -#ifdef SYNCHRONIZED_SOUND - init_syncsound(); -#endif - volume.s8 = POKEYSND_volume * 0xff / 256.0; - volume.s16 = POKEYSND_volume * 0xffff / 256.0; - - return 0; /* OK */ + ResetPokeyState(ps); + return 0; /* OK */ } -static void Update_readout_0(PokeyState* ps) +void Update_readout_0(PokeyState* ps) { if(ps->c0vo) ps->readout_0 = readout0_vo; @@ -1304,7 +788,7 @@ static void Update_readout_0(PokeyState* ps) ps->readout_0 = readout0_normal; } -static void Update_readout_1(PokeyState* ps) +void Update_readout_1(PokeyState* ps) { if(ps->c1vo) ps->readout_1 = readout1_vo; @@ -1314,7 +798,7 @@ static void Update_readout_1(PokeyState* ps) ps->readout_1 = readout1_normal; } -static void Update_readout_2(PokeyState* ps) +void Update_readout_2(PokeyState* ps) { if(ps->c2vo) ps->readout_2 = readout2_vo; @@ -1322,7 +806,7 @@ static void Update_readout_2(PokeyState* ps) ps->readout_2 = readout2_normal; } -static void Update_readout_3(PokeyState* ps) +void Update_readout_3(PokeyState* ps) { if(ps->c3vo) ps->readout_3 = readout3_vo; @@ -1330,7 +814,7 @@ static void Update_readout_3(PokeyState* ps) ps->readout_3 = readout3_normal; } -static void Update_event0(PokeyState* ps) +void Update_event0(PokeyState* ps) { if(ps->c0sw3) { @@ -1358,7 +842,7 @@ static void Update_event0(PokeyState* ps) } } -static void Update_event1(PokeyState* ps) +void Update_event1(PokeyState* ps) { if(ps->c1sw3) { @@ -1386,7 +870,7 @@ static void Update_event1(PokeyState* ps) } } -static void Update_event2(PokeyState* ps) +void Update_event2(PokeyState* ps) { if(ps->c2sw3) { @@ -1414,7 +898,7 @@ static void Update_event2(PokeyState* ps) } } -static void Update_event3(PokeyState* ps) +void Update_event3(PokeyState* ps) { if(ps->c3sw3) { @@ -1442,7 +926,7 @@ static void Update_event3(PokeyState* ps) } } -static void Update_c0divstart(PokeyState* ps) +void Update_c0divstart(PokeyState* ps) { if(ps->c1_f0) { @@ -1466,7 +950,7 @@ static void Update_c0divstart(PokeyState* ps) } } -static void Update_c1divstart(PokeyState* ps) +void Update_c1divstart(PokeyState* ps) { if(ps->c1_f0) { @@ -1479,7 +963,7 @@ static void Update_c1divstart(PokeyState* ps) ps->c1divstart = (ps->c1diva + 1) * ps->mdivk; } -static void Update_c2divstart(PokeyState* ps) +void Update_c2divstart(PokeyState* ps) { if(ps->c3_f2) { @@ -1503,7 +987,7 @@ static void Update_c2divstart(PokeyState* ps) } } -static void Update_c3divstart(PokeyState* ps) +void Update_c3divstart(PokeyState* ps) { if(ps->c3_f2) { @@ -1516,7 +1000,7 @@ static void Update_c3divstart(PokeyState* ps) ps->c3divstart = (ps->c3diva + 1) * ps->mdivk; } -static void Update_audctl(PokeyState* ps, unsigned char val) +void Update_audctl(PokeyState* ps, unsigned char val) { int nc0_hf,nc2_hf,nc1_f0,nc3_f2,nc0sw4,nc1sw4,new_divk; int recalc0=0; @@ -1654,33 +1138,33 @@ static void Update_audctl(PokeyState* ps, unsigned char val) } /* SKCTL for two-tone mode */ -static void Update_skctl(PokeyState* ps, unsigned char val) +void Update_skctl(PokeyState* ps, unsigned char val) { ps->skctl = val; } /* if using nonlinear mixing, don't stop ultrasounds */ #ifdef NONLINEAR_MIXING -static void Update_c0stop(PokeyState* ps) +void Update_c0stop(PokeyState* ps) { ps->outvol_0 = ps->readout_0(ps); } -static void Update_c1stop(PokeyState* ps) +void Update_c1stop(PokeyState* ps) { ps->outvol_1 = ps->readout_1(ps); } -static void Update_c2stop(PokeyState* ps) +void Update_c2stop(PokeyState* ps) { ps->outvol_2 = ps->readout_2(ps); } -static void Update_c3stop(PokeyState* ps) +void Update_c3stop(PokeyState* ps) { ps->outvol_3 = ps->readout_3(ps); } #else -static void Update_c0stop(PokeyState* ps) +void Update_c0stop(PokeyState* ps) { - int lim = pokey_frq/2/audible_frq; + int lim = 1; int hfa = 0; ps->c0stop = 0; @@ -1732,9 +1216,9 @@ static void Update_c0stop(PokeyState* ps) ps->outvol_0 = ps->vol0; } -static void Update_c1stop(PokeyState* ps) +void Update_c1stop(PokeyState* ps) { - int lim = pokey_frq/2/audible_frq; + int lim = 1; int hfa = 0; ps->c1stop = 0; @@ -1757,9 +1241,9 @@ static void Update_c1stop(PokeyState* ps) ps->outvol_1 = ps->vol1; } -static void Update_c2stop(PokeyState* ps) +void Update_c2stop(PokeyState* ps) { - int lim = pokey_frq/2/audible_frq; + int lim = 1; int hfa = 0; ps->c2stop = 0; @@ -1812,9 +1296,9 @@ static void Update_c2stop(PokeyState* ps) ps->outvol_2 = ps->vol2; } -static void Update_c3stop(PokeyState* ps) +void Update_c3stop(PokeyState* ps) { - int lim = pokey_frq/2/audible_frq; + int lim = 1; int hfa = 0; ps->c3stop = 0; @@ -1850,10 +1334,8 @@ static void Update_c3stop(PokeyState* ps) /* Outputs: Adjusts local globals - no return value */ /* */ /*****************************************************************************/ -void Update_pokey_sound_mz(UWORD addr, UBYTE val, UBYTE gain) +void Update_pokey_sound_mz(PokeyState* ps, unsigned short addr, unsigned char val, unsigned char gain) { - PokeyState* ps = pokey_states; - switch(addr & 0x0f) { case POKEY_OFFSET_AUDF1: @@ -2155,140 +1637,36 @@ void Update_pokey_sound_mz(UWORD addr, UBYTE val, UBYTE gain) #define MAX_SAMPLE 152 -void mzpokeysnd_process_8(void* sndbuffer, int sndn) +void mzpokeysnd_process_8(PokeyState* ps, void* sndbuffer, int sndn) { int i; int nsam = sndn; - UBYTE *buffer = (UBYTE *) sndbuffer; + unsigned char *buffer = (unsigned char *) sndbuffer; /* if there are two pokeys, then the signal is stereo we assume even sndn */ while(nsam >= 1) { -#ifdef VOL_ONLY_SOUND - if( POKEYSND_sampbuf_rptr!=POKEYSND_sampbuf_ptr ) - { int l; - if( POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]>0 ) - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]-=1280; - while( (l=POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr])<=0 ) - { POKEYSND_sampout=POKEYSND_sampbuf_val[POKEYSND_sampbuf_rptr]; - POKEYSND_sampbuf_rptr++; - if( POKEYSND_sampbuf_rptr>=POKEYSND_SAMPBUF_MAX ) - POKEYSND_sampbuf_rptr=0; - if( POKEYSND_sampbuf_rptr!=POKEYSND_sampbuf_ptr ) - { - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]+=l; - } - else break; - } - } -#endif - -#ifdef VOL_ONLY_SOUND - buffer[0] = (UBYTE)floor((generate_sample(pokey_states) + POKEYSND_sampout) + buffer[0] = (unsigned char)floor(generate_sample(ps) * (255.0 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) + 128 + 0.5 + 0.5 * rand() / RAND_MAX - 0.25); -#else - buffer[0] = (UBYTE)floor(generate_sample(pokey_states) - * (255.0 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) + 128 + 0.5 + 0.5 * rand() / RAND_MAX - 0.25); -#endif buffer += 1; nsam -= 1; } } -void mzpokeysnd_process_16(void* sndbuffer, int sndn) +void mzpokeysnd_process_16(PokeyState* ps, void* sndbuffer, int sndn) { int i; int nsam = sndn; - SWORD *buffer = (SWORD *) sndbuffer; + short *buffer = (short *) sndbuffer; /* if there are two pokeys, then the signal is stereo we assume even sndn */ while(nsam >= (int) 1) { -#ifdef VOL_ONLY_SOUND - if( POKEYSND_sampbuf_rptr!=POKEYSND_sampbuf_ptr ) - { int l; - if( POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]>0 ) - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]-=1280; - while( (l=POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr])<=0 ) - { POKEYSND_sampout=POKEYSND_sampbuf_val[POKEYSND_sampbuf_rptr]; - POKEYSND_sampbuf_rptr++; - if( POKEYSND_sampbuf_rptr>=POKEYSND_SAMPBUF_MAX ) - POKEYSND_sampbuf_rptr=0; - if( POKEYSND_sampbuf_rptr!=POKEYSND_sampbuf_ptr ) - { - POKEYSND_sampbuf_cnt[POKEYSND_sampbuf_rptr]+=l; - } - else break; - } - } -#endif -#ifdef VOL_ONLY_SOUND - buffer[0] = (SWORD)floor((generate_sample(pokey_states) + POKEYSND_sampout) + buffer[0] = (short)floor(generate_sample(ps) * (65535.0 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) + 0.5 + 0.5 * rand() / RAND_MAX - 0.25); -#else - buffer[0] = (SWORD)floor(generate_sample(pokey_states) - * (65535.0 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) + 0.5 + 0.5 * rand() / RAND_MAX - 0.25); -#endif buffer += 1; nsam -= 1; } } - -#ifdef SYNCHRONIZED_SOUND -static void generate_sync(unsigned int num_ticks) -{ - double new_samp_pos; - unsigned int ticks; - UBYTE *buffer = POKEYSND_process_buffer + POKEYSND_process_buffer_fill; - UBYTE *buffer_end = POKEYSND_process_buffer + POKEYSND_process_buffer_length; - unsigned int i; - - for (;;) { - double int_part; - new_samp_pos = samp_pos + ticks_per_sample; - new_samp_pos = modf(new_samp_pos, &int_part); - ticks = (unsigned int)int_part; - if (ticks > num_ticks) { - samp_pos -= num_ticks; - break; - } - if (buffer >= buffer_end) - break; - - samp_pos = new_samp_pos; - num_ticks -= ticks; - - /* advance pokey to the new position and produce a sample */ - advance_ticks(pokey_states, ticks); - if (POKEYSND_snd_flags & POKEYSND_BIT16) { - *((SWORD *)buffer) = (SWORD)floor( - interp_read_resam_all(pokey_states, samp_pos) - * (volume.s16 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) - + 0.5 + 0.5 * rand() / RAND_MAX - 0.25 - ); - buffer += 2; - } - else - *buffer++ = (UBYTE)floor( - interp_read_resam_all(pokey_states, samp_pos) - * (volume.s8 / 2 / MAX_SAMPLE / 4 * M_PI * 0.95) - + 128 + 0.5 + 0.5 * rand() / RAND_MAX - 0.25 - ); - } - - POKEYSND_process_buffer_fill = buffer - POKEYSND_process_buffer; - if (num_ticks > 0) { - /* remaining ticks */ - advance_ticks(pokey_states, num_ticks); - } -} -#endif /* SYNCHRONIZED_SOUND */ - - -#ifdef VOL_ONLY_SOUND -static void Update_vol_only_sound_mz( void ) -{ -} -#endif diff --git a/src/engine/platform/sound/pokey/mzpokeysnd.h b/src/engine/platform/sound/pokey/mzpokeysnd.h index 908a1dd1..4f795087 100644 --- a/src/engine/platform/sound/pokey/mzpokeysnd.h +++ b/src/engine/platform/sound/pokey/mzpokeysnd.h @@ -3,11 +3,158 @@ #include -int MZPOKEYSND_Init(size_t freq17, - int playback_freq, - int flags, - int quality - , int clear_regs - ); +struct stPokeyState; + +typedef int (*readout_t)(struct stPokeyState* ps); +typedef void (*event_t)(struct stPokeyState* ps, int p5v, int p4v, int p917v); + +#ifdef NONLINEAR_MIXING +/* Change queue event value type */ +typedef double qev_t; +#else +typedef unsigned char qev_t; +#endif + +/* State variables for single Pokey Chip */ +typedef struct stPokeyState +{ + int curtick; + /* Poly positions */ + int poly4pos; + int poly5pos; + int poly17pos; + int poly9pos; + + /* Main divider (64khz/15khz) */ + int mdivk; /* 28 for 64khz, 114 for 15khz */ + + /* Main switches */ + int selpoly9; + int c0_hf; + int c1_f0; + int c2_hf; + int c3_f2; + + /* SKCTL for two-tone mode */ + int skctl; + + /* Main output state */ + qev_t outvol_all; + int forcero; /* Force readout */ + + /* channel 0 state */ + + readout_t readout_0; + event_t event_0; + + int c0divpos; + int c0divstart; /* AUDF0 recalculated */ + int c0divstart_p; /* start value when c1_f0 */ + int c0diva; /* AUDF0 register */ + + int c0t1; /* D - 5bit, Q goes to sw3 */ + int c0t2; /* D - out sw2, Q goes to sw4 and t3 */ + int c0t3; /* D - out t2, q goes to xor */ + + int c0sw1; /* in1 - 4bit, in2 - 17bit, out goes to sw2 */ + int c0sw2; /* in1 - /Q t2, in2 - out sw1, out goes to t2 */ + int c0sw3; /* in1 - +5, in2 - Q t1, out goes to C t2 */ + int c0sw4; /* hi-pass sw */ + int c0vo; /* volume only */ + +#ifndef NONLINEAR_MIXING + int c0stop; /* channel counter stopped */ +#endif + + int vol0; + + int outvol_0; + + /* channel 1 state */ + + readout_t readout_1; + event_t event_1; + + int c1divpos; + int c1divstart; + int c1diva; + + int c1t1; + int c1t2; + int c1t3; + + int c1sw1; + int c1sw2; + int c1sw3; + int c1sw4; + int c1vo; + +#ifndef NONLINEAR_MIXING + int c1stop; /* channel counter stopped */ +#endif + + int vol1; + + int outvol_1; + + /* channel 2 state */ + + readout_t readout_2; + event_t event_2; + + int c2divpos; + int c2divstart; + int c2divstart_p; /* start value when c1_f0 */ + int c2diva; + + int c2t1; + int c2t2; + + int c2sw1; + int c2sw2; + int c2sw3; + int c2vo; + +#ifndef NONLINEAR_MIXING + int c2stop; /* channel counter stopped */ +#endif + + int vol2; + + int outvol_2; + + /* channel 3 state */ + + readout_t readout_3; + event_t event_3; + + int c3divpos; + int c3divstart; + int c3diva; + + int c3t1; + int c3t2; + + int c3sw1; + int c3sw2; + int c3sw3; + int c3vo; + +#ifndef NONLINEAR_MIXING + int c3stop; /* channel counter stopped */ +#endif + + int vol3; + + int outvol_3; +} PokeyState; + +void mzpokeysnd_process_8(PokeyState* ps, void* sndbuffer, int sndn); +void mzpokeysnd_process_16(PokeyState* ps, void* sndbuffer, int sndn); +void Update_pokey_sound_mz(PokeyState* ps, unsigned short addr, unsigned char val, unsigned char gain); + +void ResetPokeyState(PokeyState* ps); + +int MZPOKEYSND_Init(PokeyState* ps); #endif /* MZPOKEYSND_H_ */ diff --git a/src/engine/platform/sound/pokey/pokey.h b/src/engine/platform/sound/pokey/pokey.h index 777573da..22fd108d 100644 --- a/src/engine/platform/sound/pokey/pokey.h +++ b/src/engine/platform/sound/pokey/pokey.h @@ -75,15 +75,4 @@ #define POKEY_CHIP4 12 #define POKEY_SAMPLE 127 -/* structures to hold the 9 pokey control bytes */ -extern UBYTE POKEY_AUDF[4 * POKEY_MAXPOKEYS]; /* AUDFx (D200, D202, D204, D206) */ -extern UBYTE POKEY_AUDC[4 * POKEY_MAXPOKEYS]; /* AUDCx (D201, D203, D205, D207) */ -extern UBYTE POKEY_AUDCTL[POKEY_MAXPOKEYS]; /* AUDCTL (D208) */ - -extern int POKEY_DivNIRQ[4], POKEY_DivNMax[4]; -extern int POKEY_Base_mult[POKEY_MAXPOKEYS]; /* selects either 64Khz or 15Khz clock mult */ - -extern UBYTE POKEY_poly9_lookup[POKEY_POLY9_SIZE]; -extern UBYTE POKEY_poly17_lookup[16385]; - #endif /* POKEY_H_ */ diff --git a/src/engine/platform/tx81z.cpp b/src/engine/platform/tx81z.cpp index 0598dfb0..88266114 100644 --- a/src/engine/platform/tx81z.cpp +++ b/src/engine/platform/tx81z.cpp @@ -306,9 +306,9 @@ void DivPlatformTX81Z::tick(bool sysTick) { chan[i].freq=chan[i].baseFreq+(chan[i].pitch>>1)-64+chan[i].pitch2; if (!parent->song.oldArpStrategy) { if (chan[i].fixedArp) { - chan[i].freq=(chan[i].baseNoteOverride<<7)+(chan[i].pitch>>1)-64+chan[i].pitch2; + chan[i].freq=(chan[i].baseNoteOverride<<6)+(chan[i].pitch>>1)-64+chan[i].pitch2; } else { - chan[i].freq+=chan[i].arpOff<<7; + chan[i].freq+=chan[i].arpOff<<6; } } if (chan[i].freq<0) chan[i].freq=0; diff --git a/src/engine/sysDef.cpp b/src/engine/sysDef.cpp index 9192e9c8..1da237df 100644 --- a/src/engine/sysDef.cpp +++ b/src/engine/sysDef.cpp @@ -1124,7 +1124,12 @@ void DivEngine::registerSystems() { {"Channel 1", "Channel 2", "Channel 3", "Channel 4"}, {"CH1", "CH2", "CH3", "CH4"}, {DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE, DIV_CH_WAVE}, - {DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY} + {DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY, DIV_INS_POKEY}, + {}, + { + {0x10, {DIV_CMD_WAVE, "10xx: Set waveform (0 to 7)"}}, + {0x11, {DIV_CMD_STD_NOISE_MODE, "11xx: Set AUDCTL"}}, + } ); sysDefs[DIV_SYSTEM_RF5C68]=new DivSysDef( diff --git a/src/gui/guiConst.cpp b/src/gui/guiConst.cpp index 40098ec7..c442a22d 100644 --- a/src/gui/guiConst.cpp +++ b/src/gui/guiConst.cpp @@ -958,6 +958,7 @@ const int availableSystems[]={ DIV_SYSTEM_TIA, DIV_SYSTEM_SAA1099, DIV_SYSTEM_AY8930, + DIV_SYSTEM_POKEY, DIV_SYSTEM_LYNX, DIV_SYSTEM_QSOUND, DIV_SYSTEM_X1_010, @@ -1063,6 +1064,7 @@ const int chipsSpecial[]={ DIV_SYSTEM_SOUND_UNIT, DIV_SYSTEM_TIA, DIV_SYSTEM_AY8930, + DIV_SYSTEM_POKEY, DIV_SYSTEM_LYNX, DIV_SYSTEM_VERA, DIV_SYSTEM_PET, diff --git a/src/gui/insEdit.cpp b/src/gui/insEdit.cpp index cd1d60b5..b5e1cf71 100644 --- a/src/gui/insEdit.cpp +++ b/src/gui/insEdit.cpp @@ -255,6 +255,10 @@ const char* c64SpecialBits[3]={ "sync", "ring", NULL }; +const char* pokeyCtlBits[9]={ + "15KHz", "filter 2+4", "filter 1+3", "16-bit 3+4", "16-bit 1+2", "high3", "high1", "poly9", NULL +}; + const char* mikeyFeedbackBits[11] = { "0", "1", "2", "3", "4", "5", "7", "10", "11", "int", NULL }; @@ -4979,6 +4983,10 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_AY || ins->type==DIV_INS_AY8930 || ins->type==DIV_INS_FM || ins->type==DIV_INS_OPM) { dutyLabel="Noise Freq"; } + if (ins->type==DIV_INS_POKEY) { + dutyLabel="AUDCTL"; + dutyMax=8; + } if (ins->type==DIV_INS_MIKEY) { dutyLabel="Duty/Int"; dutyMax=ins->amiga.useSample?0:10; @@ -5086,7 +5094,7 @@ void FurnaceGUI::drawInsEdit() { if (ins->type==DIV_INS_K007232) waveMax=0; if (ins->type==DIV_INS_GA20) waveMax=0; if (ins->type==DIV_INS_POKEMINI) waveMax=0; - if (ins->type==DIV_INS_SU) waveMax=7; + if (ins->type==DIV_INS_SU || ins->type==DIV_INS_POKEY) waveMax=7; if (ins->type==DIV_INS_PET) { waveMax=8; waveBitMode=true; @@ -5220,6 +5228,8 @@ void FurnaceGUI::drawInsEdit() { if (dutyMax>0) { if (ins->type==DIV_INS_MIKEY) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits)); + } else if (ins->type==DIV_INS_POKEY) { + macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,pokeyCtlBits)); } else if (ins->type==DIV_INS_MSM5232) { macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,msm5232ControlBits)); } else if (ins->type==DIV_INS_ES5506) { diff --git a/src/gui/presets.cpp b/src/gui/presets.cpp index 818318c8..917b74de 100644 --- a/src/gui/presets.cpp +++ b/src/gui/presets.cpp @@ -826,6 +826,19 @@ void FurnaceGUI::initSystemPresets() { CH(DIV_SYSTEM_AY8910, 64, 0, "clockSel=5") } ); + ENTRY( + "Atari 800", { + CH(DIV_SYSTEM_POKEY, 64, 0, "clockSel=1") + }, + "tickRate=50" + ); + ENTRY( + "Atari 800 (stereo)", { + CH(DIV_SYSTEM_POKEY, 64, -127, "clockSel=1"), + CH(DIV_SYSTEM_POKEY, 64, 127, "clockSel=1"), + }, + "tickRate=50" + ); ENTRY( "Atari ST", { CH(DIV_SYSTEM_AY8910, 64, 0,