now fix it damn it

This commit is contained in:
tildearrow 2022-05-01 22:52:22 -05:00
parent e873070d84
commit 7b31f6a3e5
14 changed files with 268 additions and 496 deletions

View file

@ -250,6 +250,13 @@ src/engine/platform/sound/nes/mmc5.c
src/engine/platform/sound/vera_psg.c src/engine/platform/sound/vera_psg.c
src/engine/platform/sound/vera_pcm.c src/engine/platform/sound/vera_pcm.c
src/engine/platform/sound/nes_nsfplay/nes_apu.cpp
src/engine/platform/sound/nes_nsfplay/nes_dmc.cpp
src/engine/platform/sound/nes_nsfplay/nes_fds.cpp
src/engine/platform/sound/nes_nsfplay/nes_mmc5.cpp
src/engine/platform/sound/nes_nsfplay/nes_n106.cpp
src/engine/platform/sound/nes_nsfplay/nes_vrc6.cpp
src/engine/platform/sound/c64/sid.cc src/engine/platform/sound/c64/sid.cc
src/engine/platform/sound/c64/voice.cc src/engine/platform/sound/c64/voice.cc
src/engine/platform/sound/c64/wave.cc src/engine/platform/sound/c64/wave.cc

View file

@ -0,0 +1,4 @@
namespace xgm {
const unsigned int DEFAULT_CLOCK=1789773;
const unsigned int DEFAULT_RATE=1789773;
};

View file

@ -3,6 +3,7 @@
// //
#include <assert.h> #include <assert.h>
#include "nes_apu.h" #include "nes_apu.h"
#include "common.h"
namespace xgm namespace xgm
{ {
@ -86,9 +87,9 @@ namespace xgm
} }
INT32 NES_APU::calc_sqr (int i, UINT32 clocks) int NES_APU::calc_sqr (int i, unsigned int clocks)
{ {
static const INT16 sqrtbl[4][16] = { static const short sqrtbl[4][16] = {
{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
@ -102,7 +103,7 @@ namespace xgm
scounter[i] += freq[i] + 1; scounter[i] += freq[i] + 1;
} }
INT32 ret = 0; int ret = 0;
if (length_counter[i] > 0 && if (length_counter[i] > 0 &&
freq[i] >= 8 && freq[i] >= 8 &&
sfreq[i] < 0x800 sfreq[i] < 0x800
@ -115,7 +116,7 @@ namespace xgm
return ret; return ret;
} }
bool NES_APU::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_APU::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
if (0x4000 <= adr && adr < 0x4008) if (0x4000 <= adr && adr < 0x4008)
{ {
@ -131,26 +132,26 @@ namespace xgm
return false; return false;
} }
void NES_APU::Tick (UINT32 clocks) void NES_APU::Tick (unsigned int clocks)
{ {
out[0] = calc_sqr(0, clocks); out[0] = calc_sqr(0, clocks);
out[1] = calc_sqr(1, clocks); out[1] = calc_sqr(1, clocks);
} }
// ツ青カツ青ャツつウツづェツづゥツ波ツ形ツづ個振ツ閉敖づ0-8191 // ツ青カツ青ャツつウツづェツづゥツ波ツ形ツづ個振ツ閉敖づ0-8191
UINT32 NES_APU::Render (INT32 b[2]) unsigned int NES_APU::Render (int b[2])
{ {
out[0] = (mask & 1) ? 0 : out[0]; out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1]; out[1] = (mask & 2) ? 0 : out[1];
INT32 m[2]; int m[2];
if(option[OPT_NONLINEAR_MIXER]) if(option[OPT_NONLINEAR_MIXER])
{ {
INT32 voltage = square_table[out[0] + out[1]]; int voltage = square_table[out[0] + out[1]];
m[0] = out[0] << 6; m[0] = out[0] << 6;
m[1] = out[1] << 6; m[1] = out[1] << 6;
INT32 ref = m[0] + m[1]; int ref = m[0] + m[1];
if (ref > 0) if (ref > 0)
{ {
m[0] = (m[0] * voltage) / ref; m[0] = (m[0] * voltage) / ref;
@ -191,7 +192,7 @@ namespace xgm
square_table[0] = 0; square_table[0] = 0;
for(int i=1;i<32;i++) for(int i=1;i<32;i++)
square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100));
square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear square_linear = square_table[15]; // match linear scale to one full volume square of nonlinear
@ -200,7 +201,7 @@ namespace xgm
sm[c][t] = 128; sm[c][t] = 128;
} }
NES_APU::NES_APU () NES_APU::~NES_APU ()
{ {
} }
@ -267,7 +268,7 @@ namespace xgm
rate = r ? r : DEFAULT_RATE; rate = r ? r : DEFAULT_RATE;
} }
void NES_APU::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) void NES_APU::SetStereoMix(int trk, short mixl, short mixr)
{ {
if (trk < 0) return; if (trk < 0) return;
if (trk > 1) return; if (trk > 1) return;
@ -275,32 +276,11 @@ namespace xgm
sm[1][trk] = mixr; sm[1][trk] = mixr;
} }
ITrackInfo *NES_APU::GetTrackInfo(int trk) bool NES_APU::Write (unsigned int adr, unsigned int val, unsigned int id)
{
trkinfo[trk]._freq = freq[trk];
if(freq[trk])
trkinfo[trk].freq = clock/16/(freq[trk] + 1);
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[trk];
trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10)+(envelope_loop[trk]?0x20:0);
trkinfo[trk].key =
enable[trk] &&
length_counter[trk] > 0 &&
freq[trk] >= 8 &&
sfreq[trk] < 0x800 &&
(envelope_disable[trk] ? volume[trk] : (envelope_counter[trk] > 0));
trkinfo[trk].tone = duty[trk];
trkinfo[trk].max_volume = 15;
return &trkinfo[trk];
}
bool NES_APU::Write (UINT32 adr, UINT32 val, UINT32 id)
{ {
int ch; int ch;
static const UINT8 length_table[32] = { static const unsigned char length_table[32] = {
0x0A, 0xFE, 0x0A, 0xFE,
0x14, 0x02, 0x14, 0x02,
0x28, 0x04, 0x28, 0x04,

View file

@ -1,12 +1,11 @@
#ifndef _NES_APU_H_ #ifndef _NES_APU_H_
#define _NES_APU_H_ #define _NES_APU_H_
#include "../device.h"
#include "nes_dmc.h" #include "nes_dmc.h"
namespace xgm namespace xgm
{ {
/** Upper half of APU **/ /** Upper half of APU **/
class NES_APU : public ISoundChip class NES_APU
{ {
public: public:
enum enum
@ -24,15 +23,15 @@ namespace xgm
protected: protected:
int option[OPT_END]; // 各種オプション int option[OPT_END]; // 各種オプション
int mask; int mask;
INT32 sm[2][2]; int sm[2][2];
UINT32 gclock; unsigned int gclock;
UINT8 reg[0x20]; unsigned char reg[0x20];
INT32 out[2]; int out[2];
double rate, clock; double rate, clock;
INT32 square_table[32]; // nonlinear mixer int square_table[32]; // nonlinear mixer
INT32 square_linear; // linear mix approximation int square_linear; // linear mix approximation
int scounter[2]; // frequency divider int scounter[2]; // frequency divider
int sphase[2]; // phase counter int sphase[2]; // phase counter
@ -61,26 +60,24 @@ namespace xgm
bool enable[2]; bool enable[2];
void sweep_sqr (int ch); // calculates target sweep frequency void sweep_sqr (int ch); // calculates target sweep frequency
INT32 calc_sqr (int ch, UINT32 clocks); int calc_sqr (int ch, unsigned int clocks);
TrackInfoBasic trkinfo[2];
public: public:
NES_APU (); NES_APU ();
NES_APU (); ~NES_APU ();
void FrameSequence(int s); void FrameSequence(int s);
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual void SetRate (double rate); virtual void SetRate (double rate);
virtual void SetClock (double clock); virtual void SetClock (double clock);
virtual void SetOption (int id, int b); virtual void SetOption (int id, int b);
virtual void SetMask(int m){ mask = m; } virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
}; };
} // namespace } // namespace

View file

@ -1,10 +1,12 @@
#include "nes_dmc.h" #include "nes_dmc.h"
#include "nes_apu.h" #include "nes_apu.h"
#include "common.h"
#include <assert.h>
#include <cstdlib> #include <cstdlib>
namespace xgm namespace xgm
{ {
const UINT32 NES_DMC::wavlen_table[2][16] = { const unsigned int NES_DMC::wavlen_table[2][16] = {
{ // NTSC { // NTSC
4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068 4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068
}, },
@ -12,7 +14,7 @@ namespace xgm
4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778 4, 8, 14, 30, 60, 88, 118, 148, 188, 236, 354, 472, 708, 944, 1890, 3778
}}; }};
const UINT32 NES_DMC::freq_table[2][16] = { const unsigned int NES_DMC::freq_table[2][16] = {
{ // NTSC { // NTSC
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54
}, },
@ -20,7 +22,7 @@ namespace xgm
398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50 398, 354, 316, 298, 276, 236, 210, 198, 176, 148, 132, 118, 98, 78, 66, 50
}}; }};
const UINT32 BITREVERSE[256] = { const unsigned int BITREVERSE[256] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
@ -67,11 +69,11 @@ namespace xgm
} }
NES_DMC::NES_DMC () NES_DMC::~NES_DMC ()
{ {
} }
void NES_DMC::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) void NES_DMC::SetStereoMix(int trk, short mixl, short mixr)
{ {
if (trk < 0) return; if (trk < 0) return;
if (trk > 2) return; if (trk > 2) return;
@ -79,47 +81,6 @@ namespace xgm
sm[1][trk] = mixr; sm[1][trk] = mixr;
} }
ITrackInfo *NES_DMC::GetTrackInfo(int trk)
{
switch(trk)
{
case 0:
trkinfo[trk].max_volume = 255;
trkinfo[0].key = (linear_counter>0 && length_counter[0]>0 && enable[0]);
trkinfo[0].volume = 0;
trkinfo[0]._freq = tri_freq;
if(trkinfo[0]._freq)
trkinfo[0].freq = clock/32/(trkinfo[0]._freq + 1);
else
trkinfo[0].freq = 0;
trkinfo[0].tone = -1;
trkinfo[0].output = out[0];
break;
case 1:
trkinfo[1].max_volume = 15;
trkinfo[1].volume = noise_volume+(envelope_disable?0:0x10)+(envelope_loop?0x20:0);
trkinfo[1].key = length_counter[1]>0 && enable[1] &&
(envelope_disable ? (noise_volume>0) : (envelope_counter>0));
trkinfo[1]._freq = reg[0x400e - 0x4008]&0xF;
trkinfo[1].freq = clock/double(wavlen_table[pal][trkinfo[1]._freq] * ((noise_tap&(1<<6)) ? 93 : 1));
trkinfo[1].tone = noise_tap & (1<<6);
trkinfo[1].output = out[1];
break;
case 2:
trkinfo[2].max_volume = 127;
trkinfo[2].volume = reg[0x4011 - 0x4008]&0x7F;
trkinfo[2].key = dlength > 0;
trkinfo[2]._freq = reg[0x4010 - 0x4008]&0xF;
trkinfo[2].freq = clock/double(freq_table[pal][trkinfo[2]._freq]);
trkinfo[2].tone = (0xc000|(adr_reg<<6));
trkinfo[2].output = (damp<<1)|dac_lsb;
break;
default:
return NULL;
}
return &trkinfo[trk];
}
void NES_DMC::FrameSequence(int s) void NES_DMC::FrameSequence(int s)
{ {
//DEBUG_OUT("FrameSequence: %d¥n",s); //DEBUG_OUT("FrameSequence: %d¥n",s);
@ -134,7 +95,6 @@ namespace xgm
if (s == 0 && (frame_sequence_steps == 4)) if (s == 0 && (frame_sequence_steps == 4))
{ {
if (frame_irq_enable) frame_irq = true; if (frame_irq_enable) frame_irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable);
} }
// 240hz clock // 240hz clock
@ -194,9 +154,9 @@ namespace xgm
} }
// 三角波チャンネルの計算 戻り値は0-15 // 三角波チャンネルの計算 戻り値は0-15
UINT32 NES_DMC::calc_tri (UINT32 clocks) unsigned int NES_DMC::calc_tri (unsigned int clocks)
{ {
static UINT32 tritbl[32] = static unsigned int tritbl[32] =
{ {
15,14,13,12,11,10, 9, 8, 15,14,13,12,11,10, 9, 8,
7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0,
@ -215,7 +175,7 @@ namespace xgm
} }
} }
UINT32 ret = tritbl[tphase]; unsigned int ret = tritbl[tphase];
return ret; return ret;
} }
@ -223,20 +183,20 @@ namespace xgm
// 低サンプリングレートで合成するとエイリアスノイズが激しいので // 低サンプリングレートで合成するとエイリアスノイズが激しいので
// ノイズだけはこの関数内で高クロック合成し、簡易なサンプリングレート // ノイズだけはこの関数内で高クロック合成し、簡易なサンプリングレート
// 変換を行っている。 // 変換を行っている。
UINT32 NES_DMC::calc_noise(UINT32 clocks) unsigned int NES_DMC::calc_noise(unsigned int clocks)
{ {
UINT32 env = envelope_disable ? noise_volume : envelope_counter; unsigned int env = envelope_disable ? noise_volume : envelope_counter;
if (length_counter[1] < 1) env = 0; if (length_counter[1] < 1) env = 0;
UINT32 last = (noise & 0x4000) ? 0 : env; unsigned int last = (noise & 0x4000) ? 0 : env;
if (clocks < 1) return last; if (clocks < 1) return last;
// simple anti-aliasing (noise requires it, even when oversampling is off) // simple anti-aliasing (noise requires it, even when oversampling is off)
UINT32 count = 0; unsigned int count = 0;
UINT32 accum = counter[1] * last; // samples pending from previous calc unsigned int accum = counter[1] * last; // samples pending from previous calc
UINT32 accum_clocks = counter[1]; unsigned int accum_clocks = counter[1];
#ifdef _DEBUG #ifdef _DEBUG
INT32 start_clocks = counter[1]; int start_clocks = counter[1];
#endif #endif
if (counter[1] < 0) // only happens on startup when using the randomize noise option if (counter[1] < 0) // only happens on startup when using the randomize noise option
{ {
@ -249,7 +209,7 @@ namespace xgm
while (counter[1] < 0) while (counter[1] < 0)
{ {
// tick the noise generator // tick the noise generator
UINT32 feedback = (noise&1) ^ ((noise&noise_tap)?1:0); unsigned int feedback = (noise&1) ^ ((noise&noise_tap)?1:0);
noise = (noise>>1) | (feedback<<14); noise = (noise>>1) | (feedback<<14);
last = (noise & 0x4000) ? 0 : env; last = (noise & 0x4000) ? 0 : env;
@ -270,13 +230,13 @@ namespace xgm
if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal
#endif #endif
UINT32 average = accum / accum_clocks; unsigned int average = accum / accum_clocks;
assert(average <= 15); // above this would indicate overflow assert(average <= 15); // above this would indicate overflow
return average; return average;
} }
// Tick the DMC for the number of clocks, and return output counter; // Tick the DMC for the number of clocks, and return output counter;
UINT32 NES_DMC::calc_dmc (UINT32 clocks) unsigned int NES_DMC::calc_dmc (unsigned int clocks)
{ {
counter[2] -= clocks; counter[2] -= clocks;
assert (dfreq > 0); // prevent infinite loop assert (dfreq > 0); // prevent infinite loop
@ -300,8 +260,7 @@ namespace xgm
{ {
if (dlength > 0) if (dlength > 0)
{ {
memory->Read (daddress, data); memory (daddress, data);
cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4
// (checking for the 3-cycle case would require sub-instruction emulation) // (checking for the 3-cycle case would require sub-instruction emulation)
data &= 0xFF; // read 8 bits data &= 0xFF; // read 8 bits
if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data]; if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data];
@ -319,7 +278,6 @@ namespace xgm
else if (mode & 2) // IRQ and not looped else if (mode & 2) // IRQ and not looped
{ {
irq = true; irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true);
} }
} }
} }
@ -334,7 +292,7 @@ namespace xgm
return (damp<<1) + dac_lsb; return (damp<<1) + dac_lsb;
} }
void NES_DMC::TickFrameSequence (UINT32 clocks) void NES_DMC::TickFrameSequence (unsigned int clocks)
{ {
frame_sequence_count += clocks; frame_sequence_count += clocks;
while (frame_sequence_count > frame_sequence_length) while (frame_sequence_count > frame_sequence_length)
@ -347,28 +305,28 @@ namespace xgm
} }
} }
void NES_DMC::Tick (UINT32 clocks) void NES_DMC::Tick (unsigned int clocks)
{ {
out[0] = calc_tri(clocks); out[0] = calc_tri(clocks);
out[1] = calc_noise(clocks); out[1] = calc_noise(clocks);
out[2] = calc_dmc(clocks); out[2] = calc_dmc(clocks);
} }
UINT32 NES_DMC::Render (INT32 b[2]) unsigned int NES_DMC::Render (int b[2])
{ {
out[0] = (mask & 1) ? 0 : out[0]; out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1]; out[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2]; out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3]; int m[3];
m[0] = tnd_table[0][out[0]][0][0]; m[0] = tnd_table[0][out[0]][0][0];
m[1] = tnd_table[0][0][out[1]][0]; m[1] = tnd_table[0][0][out[1]][0];
m[2] = tnd_table[0][0][0][out[2]]; m[2] = tnd_table[0][0][0][out[2]];
if (option[OPT_NONLINEAR_MIXER]) if (option[OPT_NONLINEAR_MIXER])
{ {
INT32 ref = m[0] + m[1] + m[2]; int ref = m[0] + m[1] + m[2];
INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]]; int voltage = tnd_table[1][out[0]][out[1]][out[2]];
if (ref) if (ref)
{ {
for (int i=0; i < 3; ++i) for (int i=0; i < 3; ++i)
@ -391,7 +349,7 @@ namespace xgm
dmc_pop = false; dmc_pop = false;
// prevent overflow, keep headspace at edges // prevent overflow, keep headspace at edges
const INT32 OFFSET_MAX = (1 << 30) - (4 << 16); const int OFFSET_MAX = (1 << 30) - (4 << 16);
if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX; if (dmc_pop_offset > OFFSET_MAX) dmc_pop_offset = OFFSET_MAX;
if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX; if (dmc_pop_offset < -OFFSET_MAX) dmc_pop_offset = -OFFSET_MAX;
} }
@ -425,7 +383,7 @@ namespace xgm
void NES_DMC::SetRate (double r) void NES_DMC::SetRate (double r)
{ {
rate = (UINT32)(r?r:DEFAULT_RATE); rate = (unsigned int)(r?r:DEFAULT_RATE);
} }
void NES_DMC::SetPal (bool is_pal) void NES_DMC::SetPal (bool is_pal)
@ -455,7 +413,7 @@ namespace xgm
for(int t=0; t<16 ; t++) { for(int t=0; t<16 ; t++) {
for(int n=0; n<16; n++) { for(int n=0; n<16; n++) {
for(int d=0; d<128; d++) { for(int d=0; d<128; d++) {
tnd_table[0][t][n][d] = (UINT32)(MASTER*(3.0*t+2.0*n+d)/208.0); tnd_table[0][t][n][d] = (unsigned int)(MASTER*(3.0*t+2.0*n+d)/208.0);
} }
} }
} }
@ -466,7 +424,7 @@ namespace xgm
for(int n=0; n<16; n++) { for(int n=0; n<16; n++) {
for(int d=0; d<128; d++) { for(int d=0; d<128; d++) {
if(t!=0||n!=0||d!=0) if(t!=0||n!=0||d!=0)
tnd_table[1][t][n][d] = (UINT32)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd))); tnd_table[1][t][n][d] = (unsigned int)((MASTER*159.79)/(100.0+1.0/((double)t/wt+(double)n/wn+(double)d/wd)));
} }
} }
} }
@ -510,7 +468,6 @@ namespace xgm
frame_sequence_count = 0; frame_sequence_count = 0;
frame_sequence_steps = 4; frame_sequence_steps = 4;
frame_sequence_step = 0; frame_sequence_step = 0;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
for (i = 0; i < 0x0F; i++) for (i = 0; i < 0x0F; i++)
Write (0x4008 + i, 0); Write (0x4008 + i, 0);
@ -520,7 +477,6 @@ namespace xgm
Write (0x4015, 0x00); Write (0x4015, 0x00);
if (option[OPT_UNMUTE_ON_RESET]) if (option[OPT_UNMUTE_ON_RESET])
Write (0x4015, 0x0f); Write (0x4015, 0x0f);
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
out[0] = out[1] = out[2] = 0; out[0] = out[1] = out[2] = 0;
damp = 0; damp = 0;
@ -551,7 +507,7 @@ namespace xgm
SetRate(rate); SetRate(rate);
} }
void NES_DMC::SetMemory (IDevice * r) void NES_DMC::SetMemory (std::function<void(unsigned short, unsigned int&)> r)
{ {
memory = r; memory = r;
} }
@ -566,9 +522,9 @@ namespace xgm
} }
} }
bool NES_DMC::Write (UINT32 adr, UINT32 val, UINT32 id) bool NES_DMC::Write (unsigned int adr, unsigned int val, unsigned int id)
{ {
static const UINT8 length_table[32] = { static const unsigned char length_table[32] = {
0x0A, 0xFE, 0x0A, 0xFE,
0x14, 0x02, 0x14, 0x02,
0x28, 0x04, 0x28, 0x04,
@ -612,7 +568,6 @@ namespace xgm
} }
irq = false; irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
reg[adr-0x4008] = val; reg[adr-0x4008] = val;
return true; return true;
@ -623,7 +578,6 @@ namespace xgm
//DEBUG_OUT("4017 = %02X¥n", val); //DEBUG_OUT("4017 = %02X¥n", val);
frame_irq_enable = ((val & 0x40) != 0x40); frame_irq_enable = ((val & 0x40) != 0x40);
if (frame_irq_enable) frame_irq = false; if (frame_irq_enable) frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
frame_sequence_count = 0; frame_sequence_count = 0;
if (val & 0x80) if (val & 0x80)
@ -708,7 +662,6 @@ namespace xgm
if (!(mode & 2)) if (!(mode & 2))
{ {
irq = false; irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
} }
dfreq = freq_table[pal][val&15]; dfreq = freq_table[pal][val&15];
break; break;
@ -739,7 +692,7 @@ namespace xgm
return true; return true;
} }
bool NES_DMC::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_DMC::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
if (adr == 0x4015) if (adr == 0x4015)
{ {
@ -751,7 +704,6 @@ namespace xgm
; ;
frame_irq = false; frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
return true; return true;
} }
else if (0x4008<=adr&&adr<=0x4014) else if (0x4008<=adr&&adr<=0x4014)
@ -762,10 +714,4 @@ namespace xgm
else else
return false; return false;
} }
// IRQ support requires CPU read access
void NES_DMC::SetCPU(NES_CPU* cpu_)
{
cpu = cpu_;
}
} // namespace } // namespace

View file

@ -1,16 +1,13 @@
#ifndef _NES_DMC_H_ #ifndef _NES_DMC_H_
#define _NES_DMC_H_ #define _NES_DMC_H_
#include <functional>
#include "../device.h"
#include "../Audio/MedianFilter.h"
#include "../CPU/nes_cpu.h"
namespace xgm namespace xgm
{ {
class NES_APU; // forward declaration class NES_APU; // forward declaration
/** Bottom Half of APU **/ /** Bottom Half of APU **/
class NES_DMC:public ISoundChip class NES_DMC
{ {
public: public:
enum enum
@ -28,46 +25,46 @@ namespace xgm
}; };
protected: protected:
const int GETA_BITS; const int GETA_BITS;
static const UINT32 freq_table[2][16]; static const unsigned int freq_table[2][16];
static const UINT32 wavlen_table[2][16]; static const unsigned int wavlen_table[2][16];
UINT32 tnd_table[2][16][16][128]; unsigned int tnd_table[2][16][16][128];
int option[OPT_END]; int option[OPT_END];
int mask; int mask;
INT32 sm[2][3]; int sm[2][3];
UINT8 reg[0x10]; unsigned int reg[0x10];
UINT32 len_reg; unsigned int len_reg;
UINT32 adr_reg; unsigned int adr_reg;
IDevice *memory; std::function<void(unsigned short, unsigned int&)> memory;
UINT32 out[3]; unsigned int out[3];
UINT32 daddress; unsigned int daddress;
UINT32 dlength; unsigned int dlength;
UINT32 data; unsigned int data;
bool empty; bool empty;
INT16 damp; short damp;
int dac_lsb; int dac_lsb;
bool dmc_pop; bool dmc_pop;
INT32 dmc_pop_offset; int dmc_pop_offset;
INT32 dmc_pop_follow; int dmc_pop_follow;
double clock; double clock;
UINT32 rate; unsigned int rate;
int pal; int pal;
int mode; int mode;
bool irq; bool irq;
INT32 counter[3]; // frequency dividers int counter[3]; // frequency dividers
int tphase; // triangle phase int tphase; // triangle phase
UINT32 nfreq; // noise frequency unsigned int nfreq; // noise frequency
UINT32 dfreq; // DPCM frequency unsigned int dfreq; // DPCM frequency
UINT32 tri_freq; unsigned int tri_freq;
int linear_counter; int linear_counter;
int linear_counter_reload; int linear_counter_reload;
bool linear_counter_halt; bool linear_counter_halt;
bool linear_counter_control; bool linear_counter_control;
int noise_volume; int noise_volume;
UINT32 noise, noise_tap; unsigned int noise, noise_tap;
// noise envelope // noise envelope
bool envelope_loop; bool envelope_loop;
@ -80,8 +77,6 @@ namespace xgm
bool enable[2]; // tri/noise enable bool enable[2]; // tri/noise enable
int length_counter[2]; // 0=tri, 1=noise int length_counter[2]; // 0=tri, 1=noise
TrackInfoBasic trkinfo[3];
// frame sequencer // frame sequencer
NES_APU* apu; // apu is clocked by DMC's frame sequencer NES_APU* apu; // apu is clocked by DMC's frame sequencer
int frame_sequence_count; // current cycle count int frame_sequence_count; // current cycle count
@ -91,37 +86,32 @@ namespace xgm
bool frame_irq; bool frame_irq;
bool frame_irq_enable; bool frame_irq_enable;
NES_CPU* cpu; // IRQ needs CPU access inline unsigned int calc_tri (unsigned int clocks);
inline unsigned int calc_dmc (unsigned int clocks);
inline UINT32 calc_tri (UINT32 clocks); inline unsigned int calc_noise (unsigned int clocks);
inline UINT32 calc_dmc (UINT32 clocks);
inline UINT32 calc_noise (UINT32 clocks);
public: public:
NES_DMC (); NES_DMC ();
NES_DMC (); ~NES_DMC ();
void InitializeTNDTable(double wt, double wn, double wd); void InitializeTNDTable(double wt, double wn, double wd);
void SetPal (bool is_pal); void SetPal (bool is_pal);
void SetAPU (NES_APU* apu_); void SetAPU (NES_APU* apu_);
void SetMemory (IDevice * r); void SetMemory (std::function<void(unsigned short, unsigned int&)> r);
void FrameSequence(int s); void FrameSequence(int s);
int GetDamp(){ return (damp<<1)|dac_lsb ; } int GetDamp(){ return (damp<<1)|dac_lsb ; }
void TickFrameSequence (UINT32 clocks); void TickFrameSequence (unsigned int clocks);
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double rate); virtual void SetRate (double rate);
virtual void SetClock (double rate); virtual void SetClock (double rate);
virtual void SetOption (int, int); virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m; } virtual void SetMask(int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
void SetCPU(NES_CPU* cpu_);
}; };
} }

View file

@ -1,5 +1,7 @@
#include <cstring> #include <cstring>
#include <math.h>
#include "nes_fds.h" #include "nes_fds.h"
#include "common.h"
namespace xgm { namespace xgm {
@ -22,11 +24,11 @@ NES_FDS::NES_FDS ()
Reset(); Reset();
} }
NES_FDS::NES_FDS () NES_FDS::~NES_FDS ()
{ {
} }
void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr) void NES_FDS::SetStereoMix(int trk, short mixl, short mixr)
{ {
if (trk < 0) return; if (trk < 0) return;
if (trk > 1) return; if (trk > 1) return;
@ -34,20 +36,6 @@ void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 mixr)
sm[1] = mixr; sm[1] = mixr;
} }
ITrackInfo *NES_FDS::GetTrackInfo(int trk)
{
trkinfo.max_volume = 32;
trkinfo.volume = last_vol;
trkinfo.key = last_vol > 0;
trkinfo._freq = last_freq;
trkinfo.freq = (double(last_freq) * clock) / (65536.0 * 64.0);
trkinfo.tone = env_out[EMOD];
for(int i=0;i<64;i++)
trkinfo.wave[i] = wave[TWAV][i];
return &trkinfo;
}
void NES_FDS::SetClock (double c) void NES_FDS::SetClock (double c)
{ {
clock = c; clock = c;
@ -61,8 +49,8 @@ void NES_FDS::SetRate (double r)
double cutoff = double(option[OPT_CUTOFF]); double cutoff = double(option[OPT_CUTOFF]);
double leak = 0.0; double leak = 0.0;
if (cutoff > 0) if (cutoff > 0)
leak = ::exp(-2.0 * 3.14159 * cutoff / rate); leak = exp(-2.0 * 3.14159 * cutoff / rate);
rc_k = INT32(leak * double(1<<RC_BITS)); rc_k = int(leak * double(1<<RC_BITS));
rc_l = (1<<RC_BITS) - rc_k; rc_l = (1<<RC_BITS) - rc_k;
} }
@ -126,7 +114,7 @@ void NES_FDS::Reset ()
Write(0x4089, 0x00); // wav write disable, max global volume} Write(0x4089, 0x00); // wav write disable, max global volume}
} }
void NES_FDS::Tick (UINT32 clocks) void NES_FDS::Tick (unsigned int clocks)
{ {
// clock envelopes // clock envelopes
if (!env_halt && !wav_halt && (master_env_speed != 0)) if (!env_halt && !wav_halt && (master_env_speed != 0))
@ -136,7 +124,7 @@ void NES_FDS::Tick (UINT32 clocks)
if (!env_disable[i]) if (!env_disable[i])
{ {
env_timer[i] += clocks; env_timer[i] += clocks;
UINT32 period = ((env_speed[i]+1) * master_env_speed) << 3; unsigned int period = ((env_speed[i]+1) * master_env_speed) << 3;
while (env_timer[i] >= period) while (env_timer[i] >= period)
{ {
// clock the envelope // clock the envelope
@ -158,22 +146,22 @@ void NES_FDS::Tick (UINT32 clocks)
if (!mod_halt) if (!mod_halt)
{ {
// advance phase, adjust for modulator // advance phase, adjust for modulator
UINT32 start_pos = phase[TMOD] >> 16; unsigned int start_pos = phase[TMOD] >> 16;
phase[TMOD] += (clocks * freq[TMOD]); phase[TMOD] += (clocks * freq[TMOD]);
UINT32 end_pos = phase[TMOD] >> 16; unsigned int end_pos = phase[TMOD] >> 16;
// wrap the phase to the 64-step table (+ 16 bit accumulator) // wrap the phase to the 64-step table (+ 16 bit accumulator)
phase[TMOD] = phase[TMOD] & 0x3FFFFF; phase[TMOD] = phase[TMOD] & 0x3FFFFF;
// execute all clocked steps // execute all clocked steps
for (UINT32 p = start_pos; p < end_pos; ++p) for (unsigned int p = start_pos; p < end_pos; ++p)
{ {
INT32 wv = wave[TMOD][p & 0x3F]; int wv = wave[TMOD][p & 0x3F];
if (wv == 4) // 4 resets mod position if (wv == 4) // 4 resets mod position
mod_pos = 0; mod_pos = 0;
else else
{ {
const INT32 BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 }; const int BIAS[8] = { 0, 1, 2, 4, 0, -4, -2, -1 };
mod_pos += BIAS[wv]; mod_pos += BIAS[wv];
mod_pos &= 0x7F; // 7-bit clamp mod_pos &= 0x7F; // 7-bit clamp
} }
@ -184,16 +172,16 @@ void NES_FDS::Tick (UINT32 clocks)
if (!wav_halt) if (!wav_halt)
{ {
// complex mod calculation // complex mod calculation
INT32 mod = 0; int mod = 0;
if (env_out[EMOD] != 0) // skip if modulator off if (env_out[EMOD] != 0) // skip if modulator off
{ {
// convert mod_pos to 7-bit signed // convert mod_pos to 7-bit signed
INT32 pos = (mod_pos < 64) ? mod_pos : (mod_pos-128); int pos = (mod_pos < 64) ? mod_pos : (mod_pos-128);
// multiply pos by gain, // multiply pos by gain,
// shift off 4 bits but with odd "rounding" behaviour // shift off 4 bits but with odd "rounding" behaviour
INT32 temp = pos * env_out[EMOD]; int temp = pos * env_out[EMOD];
INT32 rem = temp & 0x0F; int rem = temp & 0x0F;
temp >>= 4; temp >>= 4;
if ((rem > 0) && ((temp & 0x80) == 0)) if ((rem > 0) && ((temp & 0x80) == 0))
{ {
@ -216,7 +204,7 @@ void NES_FDS::Tick (UINT32 clocks)
} }
// advance wavetable position // advance wavetable position
INT32 f = freq[TWAV] + mod; int f = freq[TWAV] + mod;
phase[TWAV] = phase[TWAV] + (clocks * f); phase[TWAV] = phase[TWAV] + (clocks * f);
phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap
@ -225,7 +213,7 @@ void NES_FDS::Tick (UINT32 clocks)
} }
// output volume caps at 32 // output volume caps at 32
INT32 vol_out = env_out[EVOL]; int vol_out = env_out[EVOL];
if (vol_out > 32) vol_out = 32; if (vol_out > 32) vol_out = 32;
// final output // final output
@ -242,32 +230,32 @@ void NES_FDS::Tick (UINT32 clocks)
last_vol = vol_out; last_vol = vol_out;
} }
UINT32 NES_FDS::Render (INT32 b[2]) unsigned int NES_FDS::Render (int b[2])
{ {
// 8 bit approximation of master volume // 8 bit approximation of master volume
const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223) const double MASTER_VOL = 2.4 * 1223.0; // max FDS vol vs max APU square (arbitrarily 1223)
const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol const double MAX_OUT = 32.0f * 63.0f; // value that should map to master vol
const INT32 MASTER[4] = { const int MASTER[4] = {
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 2.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 3.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f), int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) }; int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 5.0f) };
INT32 v = fout * MASTER[master_vol] >> 8; int v = fout * MASTER[master_vol] >> 8;
// lowpass RC filter // lowpass RC filter
INT32 rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS; int rc_out = ((rc_accum * rc_k) + (v * rc_l)) >> RC_BITS;
rc_accum = rc_out; rc_accum = rc_out;
v = rc_out; v = rc_out;
// output mix // output mix
INT32 m = mask ? 0 : v; int m = mask ? 0 : v;
b[0] = (m * sm[0]) >> 7; b[0] = (m * sm[0]) >> 7;
b[1] = (m * sm[1]) >> 7; b[1] = (m * sm[1]) >> 7;
return 2; return 2;
} }
bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id) bool NES_FDS::Write (unsigned int adr, unsigned int val, unsigned int id)
{ {
// $4023 master I/O enable/disable // $4023 master I/O enable/disable
if (adr == 0x4023) if (adr == 0x4023)
@ -368,7 +356,7 @@ bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id)
return false; return false;
} }
bool NES_FDS::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_FDS::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
if (adr >= 0x4040 && adr <= 0x407F) if (adr >= 0x4040 && adr <= 0x407F)
{ {

View file

@ -1,17 +1,9 @@
#ifndef _NES_FDS_H_ #ifndef _NES_FDS_H_
#define _NES_FDS_H_ #define _NES_FDS_H_
#include "../device.h"
namespace xgm { namespace xgm {
class TrackInfoFDS : public TrackInfoBasic class NES_FDS
{
public:
INT16 wave[64];
virtual IDeviceInfo *Clone(){ return new TrackInfoFDS(*this); }
};
class NES_FDS : public ISoundChip
{ {
public: public:
enum enum
@ -25,57 +17,55 @@ public:
protected: protected:
double rate, clock; double rate, clock;
int mask; int mask;
INT32 sm[2]; // stereo mix int sm[2]; // stereo mix
INT32 fout; // current output int fout; // current output
TrackInfoFDS trkinfo;
int option[OPT_END]; int option[OPT_END];
bool master_io; bool master_io;
UINT32 master_vol; unsigned int master_vol;
UINT32 last_freq; // for trackinfo unsigned int last_freq; // for trackinfo
UINT32 last_vol; // for trackinfo unsigned int last_vol; // for trackinfo
// two wavetables // two wavetables
enum { TMOD=0, TWAV=1 }; enum { TMOD=0, TWAV=1 };
INT32 wave[2][64]; int wave[2][64];
UINT32 freq[2]; unsigned int freq[2];
UINT32 phase[2]; unsigned int phase[2];
bool wav_write; bool wav_write;
bool wav_halt; bool wav_halt;
bool env_halt; bool env_halt;
bool mod_halt; bool mod_halt;
UINT32 mod_pos; unsigned int mod_pos;
UINT32 mod_write_pos; unsigned int mod_write_pos;
// two ramp envelopes // two ramp envelopes
enum { EMOD=0, EVOL=1 }; enum { EMOD=0, EVOL=1 };
bool env_mode[2]; bool env_mode[2];
bool env_disable[2]; bool env_disable[2];
UINT32 env_timer[2]; unsigned int env_timer[2];
UINT32 env_speed[2]; unsigned int env_speed[2];
UINT32 env_out[2]; unsigned int env_out[2];
UINT32 master_env_speed; unsigned int master_env_speed;
// 1-pole RC lowpass filter // 1-pole RC lowpass filter
INT32 rc_accum; int rc_accum;
INT32 rc_k; int rc_k;
INT32 rc_l; int rc_l;
public: public:
NES_FDS (); NES_FDS ();
virtual NES_FDS (); ~NES_FDS ();
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double); virtual void SetRate (double);
virtual void SetClock (double); virtual void SetClock (double);
virtual void SetOption (int, int); virtual void SetOption (int, int);
virtual void SetMask(int m){ mask = m&1; } virtual void SetMask(int m){ mask = m&1; }
virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
}; };
} // namespace xgm } // namespace xgm

View file

@ -1,11 +1,11 @@
#include "nes_mmc5.h" #include "nes_mmc5.h"
#include "common.h"
namespace xgm namespace xgm
{ {
NES_MMC5::NES_MMC5 () NES_MMC5::NES_MMC5 ()
{ {
cpu = NULL;
SetClock (DEFAULT_CLOCK); SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE); SetRate (DEFAULT_RATE);
option[OPT_NONLINEAR_MIXER] = true; option[OPT_NONLINEAR_MIXER] = true;
@ -15,19 +15,19 @@ namespace xgm
// square nonlinear mix, same as 2A03 // square nonlinear mix, same as 2A03
square_table[0] = 0; square_table[0] = 0;
for(int i=1;i<32;i++) for(int i=1;i<32;i++)
square_table[i]=(INT32)((8192.0*95.88)/(8128.0/i+100)); square_table[i]=(int)((8192.0*95.88)/(8128.0/i+100));
// 2A03 style nonlinear pcm mix with double the bits // 2A03 style nonlinear pcm mix with double the bits
//pcm_table[0] = 0; //pcm_table[0] = 0;
//INT32 wd = 22638; //int wd = 22638;
//for(int d=1;d<256; ++d) //for(int d=1;d<256; ++d)
// pcm_table[d] = (INT32)((8192.0*159.79)/(100.0+1.0/((double)d/wd))); // pcm_table[d] = (int)((8192.0*159.79)/(100.0+1.0/((double)d/wd)));
// linear pcm mix (actual hardware seems closer to this) // linear pcm mix (actual hardware seems closer to this)
pcm_table[0] = 0; pcm_table[0] = 0;
double pcm_scale = 32.0; double pcm_scale = 32.0;
for (int d=1; d<256; ++d) for (int d=1; d<256; ++d)
pcm_table[d] = (INT32)(double(d) * pcm_scale); pcm_table[d] = (int)(double(d) * pcm_scale);
// stereo mix // stereo mix
for(int c=0;c<2;++c) for(int c=0;c<2;++c)
@ -35,7 +35,7 @@ namespace xgm
sm[c][t] = 128; sm[c][t] = 128;
} }
NES_MMC5::NES_MMC5 () NES_MMC5::~NES_MMC5 ()
{ {
} }
@ -124,9 +124,9 @@ namespace xgm
} }
} }
INT32 NES_MMC5::calc_sqr (int i, UINT32 clocks) int NES_MMC5::calc_sqr (int i, unsigned int clocks)
{ {
static const INT16 sqrtbl[4][16] = { static const short sqrtbl[4][16] = {
{0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0},
@ -140,7 +140,7 @@ namespace xgm
scounter[i] -= (freq[i] + 1); scounter[i] -= (freq[i] + 1);
} }
INT32 ret = 0; int ret = 0;
if (length_counter[i] > 0) if (length_counter[i] > 0)
{ {
// note MMC5 does not silence the highest 8 frequencies like APU, // note MMC5 does not silence the highest 8 frequencies like APU,
@ -153,7 +153,7 @@ namespace xgm
return ret; return ret;
} }
void NES_MMC5::TickFrameSequence (UINT32 clocks) void NES_MMC5::TickFrameSequence (unsigned int clocks)
{ {
frame_sequence_count += clocks; frame_sequence_count += clocks;
while (frame_sequence_count > 7458) while (frame_sequence_count > 7458)
@ -163,28 +163,28 @@ namespace xgm
} }
} }
void NES_MMC5::Tick (UINT32 clocks) void NES_MMC5::Tick (unsigned int clocks)
{ {
out[0] = calc_sqr(0, clocks); out[0] = calc_sqr(0, clocks);
out[1] = calc_sqr(1, clocks); out[1] = calc_sqr(1, clocks);
out[2] = pcm; out[2] = pcm;
} }
UINT32 NES_MMC5::Render (INT32 b[2]) unsigned int NES_MMC5::Render (int b[2])
{ {
out[0] = (mask & 1) ? 0 : out[0]; out[0] = (mask & 1) ? 0 : out[0];
out[1] = (mask & 2) ? 0 : out[1]; out[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2]; out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3]; int m[3];
if(option[OPT_NONLINEAR_MIXER]) if(option[OPT_NONLINEAR_MIXER])
{ {
// squares nonlinear // squares nonlinear
INT32 voltage = square_table[out[0] + out[1]]; int voltage = square_table[out[0] + out[1]];
m[0] = out[0] << 6; m[0] = out[0] << 6;
m[1] = out[1] << 6; m[1] = out[1] << 6;
INT32 ref = m[0] + m[1]; int ref = m[0] + m[1];
if (ref > 0) if (ref > 0)
{ {
m[0] = (m[0] * voltage) / ref; m[0] = (m[0] * voltage) / ref;
@ -224,11 +224,11 @@ namespace xgm
return 2; return 2;
} }
bool NES_MMC5::Write (UINT32 adr, UINT32 val, UINT32 id) bool NES_MMC5::Write (unsigned int adr, unsigned int val, unsigned int id)
{ {
int ch; int ch;
static const UINT8 length_table[32] = { static const unsigned char length_table[32] = {
0x0A, 0xFE, 0x0A, 0xFE,
0x14, 0x02, 0x14, 0x02,
0x28, 0x04, 0x28, 0x04,
@ -329,19 +329,8 @@ namespace xgm
return true; return true;
} }
bool NES_MMC5::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_MMC5::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
// in PCM read mode, reads from $8000-$C000 automatically load the PCM output
if (pcm_mode && (0x8000 <= adr) && (adr < 0xC000) && cpu)
{
pcm_mode = false; // prevent recursive entry
UINT32 pcm_read;
cpu->Read(adr, pcm_read);
pcm_read &= 0xFF;
if (pcm_read != 0)
pcm = pcm_read;
pcm_mode = true;
}
if ((0x5000 <= adr) && (adr < 0x5008)) if ((0x5000 <= adr) && (adr < 0x5008))
{ {
@ -373,50 +362,11 @@ namespace xgm
return false; return false;
} }
void NES_MMC5::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) void NES_MMC5::SetStereoMix(int trk, short mixl, short mixr)
{ {
if (trk < 0) return; if (trk < 0) return;
if (trk > 2) return; if (trk > 2) return;
sm[0][trk] = mixl; sm[0][trk] = mixl;
sm[1][trk] = mixr; sm[1][trk] = mixr;
} }
ITrackInfo *NES_MMC5::GetTrackInfo(int trk)
{
assert(trk<3);
if (trk < 2) // square
{
trkinfo[trk]._freq = freq[trk];
if(freq[trk])
trkinfo[trk].freq = clock/16/(freq[trk] + 1);
else
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[trk];
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = volume[trk]+(envelope_disable[trk]?0:0x10);
trkinfo[trk].key = (envelope_disable[trk]?(volume[trk]>0): (envelope_counter[trk]>0));
trkinfo[trk].tone = duty[trk];
}
else // pcm
{
trkinfo[trk]._freq = 0;
trkinfo[trk].freq = 0;
trkinfo[trk].output = out[2];
trkinfo[trk].max_volume = 255;
trkinfo[trk].volume = pcm;
trkinfo[trk].key = 0;
trkinfo[trk].tone = pcm_mode ? 1 : 0;
}
return &trkinfo[trk];
}
// pcm read mode requires CPU read access
void NES_MMC5::SetCPU(NES_CPU* cpu_)
{
cpu = cpu_;
}
}// namespace }// namespace

View file

@ -1,11 +1,9 @@
#ifndef _NES_MMC5_H_ #ifndef _NES_MMC5_H_
#define _NES_MMC5_H_ #define _NES_MMC5_H_
#include "../device.h"
#include "../CPU/nes_cpu.h"
namespace xgm namespace xgm
{ {
class NES_MMC5:public ISoundChip class NES_MMC5
{ {
public: public:
enum enum
@ -14,21 +12,20 @@ namespace xgm
protected: protected:
int option[OPT_END]; int option[OPT_END];
int mask; int mask;
INT32 sm[2][3]; // stereo panning int sm[2][3]; // stereo panning
UINT8 ram[0x6000 - 0x5c00]; unsigned char ram[0x6000 - 0x5c00];
UINT8 reg[8]; unsigned char reg[8];
UINT8 mreg[2]; unsigned char mreg[2];
UINT8 pcm; // PCM channel unsigned char pcm; // PCM channel
bool pcm_mode; // PCM channel bool pcm_mode; // PCM channel
NES_CPU* cpu; // PCM channel reads need CPU access
UINT32 scounter[2]; // frequency divider unsigned int scounter[2]; // frequency divider
UINT32 sphase[2]; // phase counter unsigned int sphase[2]; // phase counter
UINT32 duty[2]; unsigned int duty[2];
UINT32 volume[2]; unsigned int volume[2];
UINT32 freq[2]; unsigned int freq[2];
INT32 out[3]; int out[3];
bool enable[2]; bool enable[2];
bool envelope_disable[2]; // エンベロープ有効フラグ bool envelope_disable[2]; // エンベロープ有効フラグ
@ -43,30 +40,26 @@ namespace xgm
int frame_sequence_count; int frame_sequence_count;
double clock, rate; double clock, rate;
INT32 calc_sqr (int i, UINT32 clocks); int calc_sqr (int i, unsigned int clocks);
INT32 square_table[32]; int square_table[32];
INT32 pcm_table[256]; int pcm_table[256];
TrackInfoBasic trkinfo[3];
public: public:
NES_MMC5 (); NES_MMC5 ();
NES_MMC5 (); ~NES_MMC5 ();
void FrameSequence (); void FrameSequence ();
void TickFrameSequence (UINT32 clocks); void TickFrameSequence (unsigned int clocks);
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetOption (int id, int b); virtual void SetOption (int id, int b);
virtual void SetClock (double); virtual void SetClock (double);
virtual void SetRate (double); virtual void SetRate (double);
virtual void SetMask (int m){ mask = m; } virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
void SetCPU(NES_CPU* cpu_);
}; };
} }

View file

@ -1,5 +1,6 @@
#include <cstring> #include <cstring>
#include "nes_n106.h" #include "nes_n106.h"
#include "common.h"
namespace xgm { namespace xgm {
@ -18,11 +19,11 @@ NES_N106::NES_N106 ()
Reset(); Reset();
} }
NES_N106::NES_N106 () NES_N106::~NES_N106 ()
{ {
} }
void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr) void NES_N106::SetStereoMix (int trk, short mixl, short mixr)
{ {
if (trk < 0 || trk >= 8) return; if (trk < 0 || trk >= 8) return;
trk = 7-trk; // displayed channels are inverted trk = 7-trk; // displayed channels are inverted
@ -30,42 +31,6 @@ void NES_N106::SetStereoMix (int trk, INT16 mixl, INT16 mixr)
sm[1][trk] = mixr; sm[1][trk] = mixr;
} }
ITrackInfo *NES_N106::GetTrackInfo (int trk)
{
int channels = get_channels();
int channel = 7-trk; // invert the track display
TrackInfoN106* t = &trkinfo[channel];
if (trk >= channels)
{
t->max_volume = 15;
t->volume = 0;
t->_freq = 0;
t->wavelen = 0;
t->tone = -1;
t->output = 0;
t->key = false;
t->freq = 0;
}
else
{
t->max_volume = 15;
t->volume = get_vol(channel);
t->_freq = get_freq(channel);
t->wavelen = get_len(channel);
t->tone = get_off(channel);
t->output = fout[channel];
t->key = (t->volume > 0) && (t->_freq > 0);
t->freq = (double(t->_freq) * clock) / double(15 * 65536 * channels * t->wavelen);
for (int i=0; i < t->wavelen; ++i)
t->wave[i] = get_sample((i+t->tone)&0xFF);
}
return t;
}
void NES_N106::SetClock (double c) void NES_N106::SetClock (double c)
{ {
@ -120,7 +85,7 @@ void NES_N106::Reset ()
Write(0xF800, 0x00); // select $00 without auto-increment Write(0xF800, 0x00); // select $00 without auto-increment
} }
void NES_N106::Tick (UINT32 clocks) void NES_N106::Tick (unsigned int clocks)
{ {
if (master_disable) return; if (master_disable) return;
@ -132,24 +97,24 @@ void NES_N106::Tick (UINT32 clocks)
{ {
int channel = 7-tick_channel; int channel = 7-tick_channel;
UINT32 phase = get_phase(channel); unsigned int phase = get_phase(channel);
UINT32 freq = get_freq(channel); unsigned int freq = get_freq(channel);
UINT32 len = get_len(channel); unsigned int len = get_len(channel);
UINT32 off = get_off(channel); unsigned int off = get_off(channel);
INT32 vol = get_vol(channel); int vol = get_vol(channel);
// accumulate 24-bit phase // accumulate 24-bit phase
phase = (phase + freq) & 0x00FFFFFF; phase = (phase + freq) & 0x00FFFFFF;
// wrap phase if wavelength exceeded // wrap phase if wavelength exceeded
UINT32 hilen = len << 16; unsigned int hilen = len << 16;
while (phase >= hilen) phase -= hilen; while (phase >= hilen) phase -= hilen;
// write back phase // write back phase
set_phase(phase, channel); set_phase(phase, channel);
// fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03) // fetch sample (note: N163 output is centred at 8, and inverted w.r.t 2A03)
INT32 sample = 8 - get_sample(((phase >> 16) + off) & 0xFF); int sample = 8 - get_sample(((phase >> 16) + off) & 0xFF);
fout[channel] = sample * vol; fout[channel] = sample * vol;
// cycle to next channel every 15 clocks // cycle to next channel every 15 clocks
@ -160,7 +125,7 @@ void NES_N106::Tick (UINT32 clocks)
} }
} }
UINT32 NES_N106::Render (INT32 b[2]) unsigned int NES_N106::Render (int b[2])
{ {
b[0] = 0; b[0] = 0;
b[1] = 0; b[1] = 0;
@ -217,7 +182,7 @@ UINT32 NES_N106::Render (INT32 b[2])
} }
// mix together, increase output level by 8 bits, roll off 7 bits from sm // mix together, increase output level by 8 bits, roll off 7 bits from sm
INT32 MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 }; int MIX[9] = { 256/1, 256/1, 256/2, 256/3, 256/4, 256/5, 256/6, 256/6, 256/6 };
b[0] = (b[0] * MIX[channels]) >> 7; b[0] = (b[0] * MIX[channels]) >> 7;
b[1] = (b[1] * MIX[channels]) >> 7; b[1] = (b[1] * MIX[channels]) >> 7;
// when approximating the serial multiplex as a straight mix, once the // when approximating the serial multiplex as a straight mix, once the
@ -233,14 +198,14 @@ UINT32 NES_N106::Render (INT32 b[2])
// and lower volumes on others. Using 6.0x as a rough "one size fits all". // and lower volumes on others. Using 6.0x as a rough "one size fits all".
const double MASTER_VOL = 6.0 * 1223.0; const double MASTER_VOL = 6.0 * 1223.0;
const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value const double MAX_OUT = 15.0 * 15.0 * 256.0; // max digital value
const INT32 GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f); const int GAIN = int((MASTER_VOL / MAX_OUT) * 256.0f);
b[0] = (b[0] * GAIN) >> 8; b[0] = (b[0] * GAIN) >> 8;
b[1] = (b[1] * GAIN) >> 8; b[1] = (b[1] * GAIN) >> 8;
return 2; return 2;
} }
bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id) bool NES_N106::Write (unsigned int adr, unsigned int val, unsigned int id)
{ {
if (adr == 0xE000) // master disable if (adr == 0xE000) // master disable
{ {
@ -286,7 +251,7 @@ bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id)
return false; return false;
} }
bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_N106::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
if (adr == 0x4800) // register read if (adr == 0x4800) // register read
{ {
@ -302,7 +267,7 @@ bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id)
// register decoding/encoding functions // register decoding/encoding functions
// //
inline UINT32 NES_N106::get_phase (int channel) inline unsigned int NES_N106::get_phase (int channel)
{ {
// 24-bit phase stored in channel regs 1/3/5 // 24-bit phase stored in channel regs 1/3/5
channel = channel << 3; channel = channel << 3;
@ -311,7 +276,7 @@ inline UINT32 NES_N106::get_phase (int channel)
+ (reg[0x45 + channel] << 16); + (reg[0x45 + channel] << 16);
} }
inline UINT32 NES_N106::get_freq (int channel) inline unsigned int NES_N106::get_freq (int channel)
{ {
// 19-bit frequency stored in channel regs 0/2/4 // 19-bit frequency stored in channel regs 0/2/4
channel = channel << 3; channel = channel << 3;
@ -320,28 +285,28 @@ inline UINT32 NES_N106::get_freq (int channel)
+ ((reg[0x44 + channel] & 0x03) << 16); + ((reg[0x44 + channel] & 0x03) << 16);
} }
inline UINT32 NES_N106::get_off (int channel) inline unsigned int NES_N106::get_off (int channel)
{ {
// 8-bit offset stored in channel reg 6 // 8-bit offset stored in channel reg 6
channel = channel << 3; channel = channel << 3;
return reg[0x46 + channel]; return reg[0x46 + channel];
} }
inline UINT32 NES_N106::get_len (int channel) inline unsigned int NES_N106::get_len (int channel)
{ {
// 6-bit<<3 length stored obscurely in channel reg 4 // 6-bit<<3 length stored obscurely in channel reg 4
channel = channel << 3; channel = channel << 3;
return 256 - (reg[0x44 + channel] & 0xFC); return 256 - (reg[0x44 + channel] & 0xFC);
} }
inline INT32 NES_N106::get_vol (int channel) inline int NES_N106::get_vol (int channel)
{ {
// 4-bit volume stored in channel reg 7 // 4-bit volume stored in channel reg 7
channel = channel << 3; channel = channel << 3;
return reg[0x47 + channel] & 0x0F; return reg[0x47 + channel] & 0x0F;
} }
inline INT32 NES_N106::get_sample (UINT32 index) inline int NES_N106::get_sample (unsigned int index)
{ {
// every sample becomes 2 samples in regs // every sample becomes 2 samples in regs
return (index&1) ? return (index&1) ?
@ -355,7 +320,7 @@ inline int NES_N106::get_channels ()
return ((reg[0x7F] >> 4) & 0x07) + 1; return ((reg[0x7F] >> 4) & 0x07) + 1;
} }
inline void NES_N106::set_phase (UINT32 phase, int channel) inline void NES_N106::set_phase (unsigned int phase, int channel)
{ {
// 24-bit phase stored in channel regs 1/3/5 // 24-bit phase stored in channel regs 1/3/5
channel = channel << 3; channel = channel << 3;

View file

@ -1,18 +1,10 @@
#ifndef _NES_N106_H_ #ifndef _NES_N106_H_
#define _NES_N106_H_ #define _NES_N106_H_
#include "../device.h"
namespace xgm { namespace xgm {
class TrackInfoN106 : public TrackInfoBasic
{
public:
int wavelen;
INT16 wave[256];
virtual IDeviceInfo *Clone(){ return new TrackInfoN106(*this); }
};
class NES_N106:public ISoundChip class NES_N106
{ {
public: public:
enum enum
@ -26,13 +18,12 @@ public:
protected: protected:
double rate, clock; double rate, clock;
int mask; int mask;
INT32 sm[2][8]; // stereo mix int sm[2][8]; // stereo mix
INT32 fout[8]; // current output int fout[8]; // current output
TrackInfoN106 trkinfo[8];
int option[OPT_END]; int option[OPT_END];
bool master_disable; bool master_disable;
UINT32 reg[0x80]; // all state is contained here unsigned int reg[0x80]; // all state is contained here
unsigned int reg_select; unsigned int reg_select;
bool reg_advance; bool reg_advance;
int tick_channel; int tick_channel;
@ -42,31 +33,30 @@ protected:
int render_subclock; int render_subclock;
// convenience functions to interact with regs // convenience functions to interact with regs
inline UINT32 get_phase (int channel); inline unsigned int get_phase (int channel);
inline UINT32 get_freq (int channel); inline unsigned int get_freq (int channel);
inline UINT32 get_off (int channel); inline unsigned int get_off (int channel);
inline UINT32 get_len (int channel); inline unsigned int get_len (int channel);
inline INT32 get_vol (int channel); inline int get_vol (int channel);
inline INT32 get_sample (UINT32 index); inline int get_sample (unsigned int index);
inline int get_channels (); inline int get_channels ();
// for storing back the phase after modifying // for storing back the phase after modifying
inline void set_phase (UINT32 phase, int channel); inline void set_phase (unsigned int phase, int channel);
public: public:
NES_N106 (); NES_N106 ();
NES_N106 (); ~NES_N106 ();
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual void SetRate (double); virtual void SetRate (double);
virtual void SetClock (double); virtual void SetClock (double);
virtual void SetOption (int, int); virtual void SetOption (int, int);
virtual void SetMask (int m); virtual void SetMask (int m);
virtual void SetStereoMix (int trk, INT16 mixl, INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
}; };
} // namespace xgm } // namespace xgm

View file

@ -1,4 +1,5 @@
#include "nes_vrc6.h" #include "nes_vrc6.h"
#include "common.h"
namespace xgm namespace xgm
{ {
@ -16,11 +17,11 @@ namespace xgm
sm[c][t] = 128; sm[c][t] = 128;
} }
NES_VRC6::NES_VRC6 () NES_VRC6::~NES_VRC6 ()
{ {
} }
void NES_VRC6::SetStereoMix(int trk, xgm::INT16 mixl, xgm::INT16 mixr) void NES_VRC6::SetStereoMix(int trk, short mixl, short mixr)
{ {
if (trk < 0) return; if (trk < 0) return;
if (trk > 2) return; if (trk > 2) return;
@ -28,32 +29,6 @@ namespace xgm
sm[1][trk] = mixr; sm[1][trk] = mixr;
} }
ITrackInfo *NES_VRC6::GetTrackInfo(int trk)
{
if(trk<2)
{
trkinfo[trk].max_volume = 15;
trkinfo[trk].volume = volume[trk];
trkinfo[trk]._freq = freq2[trk];
trkinfo[trk].freq = freq2[trk]?clock/16/(freq2[trk]+1):0;
trkinfo[trk].tone = duty[trk];
trkinfo[trk].key = (volume[trk]>0)&&enable[trk]&&!gate[trk];
return &trkinfo[trk];
}
else if(trk==2)
{
trkinfo[2].max_volume = 255;
trkinfo[2].volume = volume[2];
trkinfo[2]._freq = freq2[2];
trkinfo[2].freq = freq2[2]?clock/14/(freq2[2]+1):0;
trkinfo[2].tone = -1;
trkinfo[2].key = (enable[2]>0);
return &trkinfo[2];
}
else
return NULL;
}
void NES_VRC6::SetClock (double c) void NES_VRC6::SetClock (double c)
{ {
clock = c; clock = c;
@ -91,9 +66,9 @@ namespace xgm
phase[0] = 2; phase[0] = 2;
} }
INT16 NES_VRC6::calc_sqr (int i, UINT32 clocks) short NES_VRC6::calc_sqr (int i, unsigned int clocks)
{ {
static const INT16 sqrtbl[8][16] = { static const short sqrtbl[8][16] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1},
@ -121,7 +96,7 @@ namespace xgm
|| sqrtbl[duty[i]][phase[i]])? volume[i] : 0; || sqrtbl[duty[i]][phase[i]])? volume[i] : 0;
} }
INT16 NES_VRC6::calc_saw (UINT32 clocks) short NES_VRC6::calc_saw (unsigned int clocks)
{ {
if (!enable[2]) if (!enable[2])
return 0; return 0;
@ -151,16 +126,16 @@ namespace xgm
return phase[2] >> 3; return phase[2] >> 3;
} }
void NES_VRC6::Tick (UINT32 clocks) void NES_VRC6::Tick (unsigned int clocks)
{ {
out[0] = calc_sqr(0,clocks); out[0] = calc_sqr(0,clocks);
out[1] = calc_sqr(1,clocks); out[1] = calc_sqr(1,clocks);
out[2] = calc_saw(clocks); out[2] = calc_saw(clocks);
} }
UINT32 NES_VRC6::Render (INT32 b[2]) unsigned int NES_VRC6::Render (int b[2])
{ {
INT32 m[3]; int m[3];
m[0] = out[0]; m[0] = out[0];
m[1] = out[1]; m[1] = out[1];
m[2] = out[2]; m[2] = out[2];
@ -182,14 +157,14 @@ namespace xgm
//b[1] >>= (7 - 7); //b[1] >>= (7 - 7);
// master volume adjustment // master volume adjustment
const INT32 MASTER = INT32(256.0 * 1223.0 / 1920.0); const int MASTER = int(256.0 * 1223.0 / 1920.0);
b[0] = (b[0] * MASTER) >> 8; b[0] = (b[0] * MASTER) >> 8;
b[1] = (b[1] * MASTER) >> 8; b[1] = (b[1] * MASTER) >> 8;
return 2; return 2;
} }
bool NES_VRC6::Write (UINT32 adr, UINT32 val, UINT32 id) bool NES_VRC6::Write (unsigned int adr, unsigned int val, unsigned int id)
{ {
int ch, cmap[4] = { 0, 0, 1, 2 }; int ch, cmap[4] = { 0, 0, 1, 2 };
@ -255,7 +230,7 @@ namespace xgm
return true; return true;
} }
bool NES_VRC6::Read (UINT32 adr, UINT32 & val, UINT32 id) bool NES_VRC6::Read (unsigned int adr, unsigned int & val, unsigned int id)
{ {
return false; return false;
} }

View file

@ -1,11 +1,10 @@
#ifndef _NES_VRC6_H_ #ifndef _NES_VRC6_H_
#define _NES_VRC6_H_ #define _NES_VRC6_H_
#include "../device.h"
namespace xgm namespace xgm
{ {
class NES_VRC6:public ISoundChip class NES_VRC6
{ {
public: public:
enum enum
@ -13,42 +12,40 @@ namespace xgm
OPT_END OPT_END
}; };
protected: protected:
UINT32 counter[3]; // frequency divider unsigned int counter[3]; // frequency divider
UINT32 phase[3]; // phase counter unsigned int phase[3]; // phase counter
UINT32 freq2[3]; // adjusted frequency unsigned int freq2[3]; // adjusted frequency
int count14; // saw 14-stage counter int count14; // saw 14-stage counter
//int option[OPT_END]; //int option[OPT_END];
int mask; int mask;
INT32 sm[2][3]; // stereo mix int sm[2][3]; // stereo mix
int duty[2]; int duty[2];
int volume[3]; int volume[3];
int enable[3]; int enable[3];
int gate[3]; int gate[3];
UINT32 freq[3]; unsigned int freq[3];
INT16 calc_sqr (int i, UINT32 clocks); short calc_sqr (int i, unsigned int clocks);
INT16 calc_saw (UINT32 clocks); short calc_saw (unsigned int clocks);
bool halt; bool halt;
int freq_shift; int freq_shift;
double clock, rate; double clock, rate;
INT32 out[3]; int out[3];
TrackInfoBasic trkinfo[3];
public: public:
NES_VRC6 (); NES_VRC6 ();
NES_VRC6 (); ~NES_VRC6 ();
virtual void Reset (); virtual void Reset ();
virtual void Tick (UINT32 clocks); virtual void Tick (unsigned int clocks);
virtual UINT32 Render (INT32 b[2]); virtual unsigned int Render (int b[2]);
virtual bool Read (UINT32 adr, UINT32 & val, UINT32 id=0); virtual bool Read (unsigned int adr, unsigned int & val, unsigned int id=0);
virtual bool Write (UINT32 adr, UINT32 val, UINT32 id=0); virtual bool Write (unsigned int adr, unsigned int val, unsigned int id=0);
virtual void SetClock (double); virtual void SetClock (double);
virtual void SetRate (double); virtual void SetRate (double);
virtual void SetOption (int, int); virtual void SetOption (int, int);
virtual void SetMask (int m){ mask = m; } virtual void SetMask (int m){ mask = m; }
virtual void SetStereoMix (int trk, xgm::INT16 mixl, xgm::INT16 mixr); virtual void SetStereoMix (int trk, short mixl, short mixr);
virtual ITrackInfo *GetTrackInfo(int trk);
}; };
} // namespace } // namespace