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_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/voice.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 "nes_apu.h"
#include "common.h"
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, 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},
@ -102,7 +103,7 @@ namespace xgm
scounter[i] += freq[i] + 1;
}
INT32 ret = 0;
int ret = 0;
if (length_counter[i] > 0 &&
freq[i] >= 8 &&
sfreq[i] < 0x800
@ -115,7 +116,7 @@ namespace xgm
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)
{
@ -131,26 +132,26 @@ namespace xgm
return false;
}
void NES_APU::Tick (UINT32 clocks)
void NES_APU::Tick (unsigned int clocks)
{
out[0] = calc_sqr(0, clocks);
out[1] = calc_sqr(1, clocks);
}
// ツ青カツ青ャツつウツづェツづゥツ波ツ形ツづ個振ツ閉敖づ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[1] = (mask & 2) ? 0 : out[1];
INT32 m[2];
int m[2];
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[1] = out[1] << 6;
INT32 ref = m[0] + m[1];
int ref = m[0] + m[1];
if (ref > 0)
{
m[0] = (m[0] * voltage) / ref;
@ -191,7 +192,7 @@ namespace xgm
square_table[0] = 0;
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
@ -200,7 +201,7 @@ namespace xgm
sm[c][t] = 128;
}
NES_APU::NES_APU ()
NES_APU::~NES_APU ()
{
}
@ -267,7 +268,7 @@ namespace xgm
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 > 1) return;
@ -275,32 +276,11 @@ namespace xgm
sm[1][trk] = mixr;
}
ITrackInfo *NES_APU::GetTrackInfo(int trk)
{
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)
bool NES_APU::Write (unsigned int adr, unsigned int val, unsigned int id)
{
int ch;
static const UINT8 length_table[32] = {
static const unsigned char length_table[32] = {
0x0A, 0xFE,
0x14, 0x02,
0x28, 0x04,

View file

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

View file

@ -1,10 +1,12 @@
#include "nes_dmc.h"
#include "nes_apu.h"
#include "common.h"
#include <assert.h>
#include <cstdlib>
namespace xgm
{
const UINT32 NES_DMC::wavlen_table[2][16] = {
const unsigned int NES_DMC::wavlen_table[2][16] = {
{ // NTSC
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
}};
const UINT32 NES_DMC::freq_table[2][16] = {
const unsigned int NES_DMC::freq_table[2][16] = {
{ // NTSC
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
}};
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,
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,
@ -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 > 2) return;
@ -79,47 +81,6 @@ namespace xgm
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)
{
//DEBUG_OUT("FrameSequence: %d¥n",s);
@ -134,7 +95,6 @@ namespace xgm
if (s == 0 && (frame_sequence_steps == 4))
{
if (frame_irq_enable) frame_irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, frame_irq & frame_irq_enable);
}
// 240hz clock
@ -194,9 +154,9 @@ namespace xgm
}
// 三角波チャンネルの計算 戻り値は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,
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;
}
@ -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;
UINT32 last = (noise & 0x4000) ? 0 : env;
unsigned int last = (noise & 0x4000) ? 0 : env;
if (clocks < 1) return last;
// simple anti-aliasing (noise requires it, even when oversampling is off)
UINT32 count = 0;
UINT32 accum = counter[1] * last; // samples pending from previous calc
UINT32 accum_clocks = counter[1];
unsigned int count = 0;
unsigned int accum = counter[1] * last; // samples pending from previous calc
unsigned int accum_clocks = counter[1];
#ifdef _DEBUG
INT32 start_clocks = counter[1];
int start_clocks = counter[1];
#endif
if (counter[1] < 0) // only happens on startup when using the randomize noise option
{
@ -249,7 +209,7 @@ namespace xgm
while (counter[1] < 0)
{
// 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);
last = (noise & 0x4000) ? 0 : env;
@ -270,13 +230,13 @@ namespace xgm
if (start_clocks >= 0) assert(accum_clocks == clocks); // these should be equal
#endif
UINT32 average = accum / accum_clocks;
unsigned int average = accum / accum_clocks;
assert(average <= 15); // above this would indicate overflow
return average;
}
// 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;
assert (dfreq > 0); // prevent infinite loop
@ -300,8 +260,7 @@ namespace xgm
{
if (dlength > 0)
{
memory->Read (daddress, data);
cpu->StealCycles(4); // DMC read takes 3 or 4 CPU cycles, usually 4
memory (daddress, data);
// (checking for the 3-cycle case would require sub-instruction emulation)
data &= 0xFF; // read 8 bits
if (option[OPT_DPCM_REVERSE]) data = BITREVERSE[data];
@ -319,7 +278,6 @@ namespace xgm
else if (mode & 2) // IRQ and not looped
{
irq = true;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, true);
}
}
}
@ -334,7 +292,7 @@ namespace xgm
return (damp<<1) + dac_lsb;
}
void NES_DMC::TickFrameSequence (UINT32 clocks)
void NES_DMC::TickFrameSequence (unsigned int clocks)
{
frame_sequence_count += clocks;
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[1] = calc_noise(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[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3];
int m[3];
m[0] = tnd_table[0][out[0]][0][0];
m[1] = tnd_table[0][0][out[1]][0];
m[2] = tnd_table[0][0][0][out[2]];
if (option[OPT_NONLINEAR_MIXER])
{
INT32 ref = m[0] + m[1] + m[2];
INT32 voltage = tnd_table[1][out[0]][out[1]][out[2]];
int ref = m[0] + m[1] + m[2];
int voltage = tnd_table[1][out[0]][out[1]][out[2]];
if (ref)
{
for (int i=0; i < 3; ++i)
@ -391,7 +349,7 @@ namespace xgm
dmc_pop = false;
// 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;
}
@ -425,7 +383,7 @@ namespace xgm
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)
@ -455,7 +413,7 @@ namespace xgm
for(int t=0; t<16 ; t++) {
for(int n=0; n<16; n++) {
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 d=0; d<128; d++) {
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_steps = 4;
frame_sequence_step = 0;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
for (i = 0; i < 0x0F; i++)
Write (0x4008 + i, 0);
@ -520,7 +477,6 @@ namespace xgm
Write (0x4015, 0x00);
if (option[OPT_UNMUTE_ON_RESET])
Write (0x4015, 0x0f);
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
out[0] = out[1] = out[2] = 0;
damp = 0;
@ -551,7 +507,7 @@ namespace xgm
SetRate(rate);
}
void NES_DMC::SetMemory (IDevice * r)
void NES_DMC::SetMemory (std::function<void(unsigned short, unsigned int&)> 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,
0x14, 0x02,
0x28, 0x04,
@ -612,7 +568,6 @@ namespace xgm
}
irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
reg[adr-0x4008] = val;
return true;
@ -623,7 +578,6 @@ namespace xgm
//DEBUG_OUT("4017 = %02X¥n", val);
frame_irq_enable = ((val & 0x40) != 0x40);
if (frame_irq_enable) frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
frame_sequence_count = 0;
if (val & 0x80)
@ -708,7 +662,6 @@ namespace xgm
if (!(mode & 2))
{
irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_DMC, false);
}
dfreq = freq_table[pal][val&15];
break;
@ -739,7 +692,7 @@ namespace xgm
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)
{
@ -751,7 +704,6 @@ namespace xgm
;
frame_irq = false;
cpu->UpdateIRQ(NES_CPU::IRQD_FRAME, false);
return true;
}
else if (0x4008<=adr&&adr<=0x4014)
@ -762,10 +714,4 @@ namespace xgm
else
return false;
}
// IRQ support requires CPU read access
void NES_DMC::SetCPU(NES_CPU* cpu_)
{
cpu = cpu_;
}
} // namespace

View file

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

View file

@ -1,5 +1,7 @@
#include <cstring>
#include <math.h>
#include "nes_fds.h"
#include "common.h"
namespace xgm {
@ -22,11 +24,11 @@ NES_FDS::NES_FDS ()
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 > 1) return;
@ -34,20 +36,6 @@ void NES_FDS::SetStereoMix(int trk, INT16 mixl, INT16 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)
{
clock = c;
@ -61,8 +49,8 @@ void NES_FDS::SetRate (double r)
double cutoff = double(option[OPT_CUTOFF]);
double leak = 0.0;
if (cutoff > 0)
leak = ::exp(-2.0 * 3.14159 * cutoff / rate);
rc_k = INT32(leak * double(1<<RC_BITS));
leak = exp(-2.0 * 3.14159 * cutoff / rate);
rc_k = int(leak * double(1<<RC_BITS));
rc_l = (1<<RC_BITS) - rc_k;
}
@ -126,7 +114,7 @@ void NES_FDS::Reset ()
Write(0x4089, 0x00); // wav write disable, max global volume}
}
void NES_FDS::Tick (UINT32 clocks)
void NES_FDS::Tick (unsigned int clocks)
{
// clock envelopes
if (!env_halt && !wav_halt && (master_env_speed != 0))
@ -136,7 +124,7 @@ void NES_FDS::Tick (UINT32 clocks)
if (!env_disable[i])
{
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)
{
// clock the envelope
@ -158,22 +146,22 @@ void NES_FDS::Tick (UINT32 clocks)
if (!mod_halt)
{
// advance phase, adjust for modulator
UINT32 start_pos = phase[TMOD] >> 16;
unsigned int start_pos = phase[TMOD] >> 16;
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)
phase[TMOD] = phase[TMOD] & 0x3FFFFF;
// 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
mod_pos = 0;
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 &= 0x7F; // 7-bit clamp
}
@ -184,16 +172,16 @@ void NES_FDS::Tick (UINT32 clocks)
if (!wav_halt)
{
// complex mod calculation
INT32 mod = 0;
int mod = 0;
if (env_out[EMOD] != 0) // skip if modulator off
{
// 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,
// shift off 4 bits but with odd "rounding" behaviour
INT32 temp = pos * env_out[EMOD];
INT32 rem = temp & 0x0F;
int temp = pos * env_out[EMOD];
int rem = temp & 0x0F;
temp >>= 4;
if ((rem > 0) && ((temp & 0x80) == 0))
{
@ -216,7 +204,7 @@ void NES_FDS::Tick (UINT32 clocks)
}
// advance wavetable position
INT32 f = freq[TWAV] + mod;
int f = freq[TWAV] + mod;
phase[TWAV] = phase[TWAV] + (clocks * f);
phase[TWAV] = phase[TWAV] & 0x3FFFFF; // wrap
@ -225,7 +213,7 @@ void NES_FDS::Tick (UINT32 clocks)
}
// output volume caps at 32
INT32 vol_out = env_out[EVOL];
int vol_out = env_out[EVOL];
if (vol_out > 32) vol_out = 32;
// final output
@ -242,32 +230,32 @@ void NES_FDS::Tick (UINT32 clocks)
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
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 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 / 3.0f),
int((MASTER_VOL / MAX_OUT) * 256.0 * 2.0f / 4.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
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;
v = rc_out;
// output mix
INT32 m = mask ? 0 : v;
int m = mask ? 0 : v;
b[0] = (m * sm[0]) >> 7;
b[1] = (m * sm[1]) >> 7;
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
if (adr == 0x4023)
@ -368,7 +356,7 @@ bool NES_FDS::Write (UINT32 adr, UINT32 val, UINT32 id)
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)
{

View file

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

View file

@ -1,11 +1,11 @@
#include "nes_mmc5.h"
#include "common.h"
namespace xgm
{
NES_MMC5::NES_MMC5 ()
{
cpu = NULL;
SetClock (DEFAULT_CLOCK);
SetRate (DEFAULT_RATE);
option[OPT_NONLINEAR_MIXER] = true;
@ -15,19 +15,19 @@ namespace xgm
// square nonlinear mix, same as 2A03
square_table[0] = 0;
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
//pcm_table[0] = 0;
//INT32 wd = 22638;
//int wd = 22638;
//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)
pcm_table[0] = 0;
double pcm_scale = 32.0;
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
for(int c=0;c<2;++c)
@ -35,7 +35,7 @@ namespace xgm
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, 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},
@ -140,7 +140,7 @@ namespace xgm
scounter[i] -= (freq[i] + 1);
}
INT32 ret = 0;
int ret = 0;
if (length_counter[i] > 0)
{
// note MMC5 does not silence the highest 8 frequencies like APU,
@ -153,7 +153,7 @@ namespace xgm
return ret;
}
void NES_MMC5::TickFrameSequence (UINT32 clocks)
void NES_MMC5::TickFrameSequence (unsigned int clocks)
{
frame_sequence_count += clocks;
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[1] = calc_sqr(1, clocks);
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[1] = (mask & 2) ? 0 : out[1];
out[2] = (mask & 4) ? 0 : out[2];
INT32 m[3];
int m[3];
if(option[OPT_NONLINEAR_MIXER])
{
// squares nonlinear
INT32 voltage = square_table[out[0] + out[1]];
int voltage = square_table[out[0] + out[1]];
m[0] = out[0] << 6;
m[1] = out[1] << 6;
INT32 ref = m[0] + m[1];
int ref = m[0] + m[1];
if (ref > 0)
{
m[0] = (m[0] * voltage) / ref;
@ -224,11 +224,11 @@ namespace xgm
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;
static const UINT8 length_table[32] = {
static const unsigned char length_table[32] = {
0x0A, 0xFE,
0x14, 0x02,
0x28, 0x04,
@ -329,19 +329,8 @@ namespace xgm
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))
{
@ -373,50 +362,11 @@ namespace xgm
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 > 2) return;
sm[0][trk] = mixl;
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

View file

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

View file

@ -1,5 +1,6 @@
#include <cstring>
#include "nes_n106.h"
#include "common.h"
namespace xgm {
@ -18,11 +19,11 @@ NES_N106::NES_N106 ()
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;
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;
}
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)
{
@ -120,7 +85,7 @@ void NES_N106::Reset ()
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;
@ -132,24 +97,24 @@ void NES_N106::Tick (UINT32 clocks)
{
int channel = 7-tick_channel;
UINT32 phase = get_phase(channel);
UINT32 freq = get_freq(channel);
UINT32 len = get_len(channel);
UINT32 off = get_off(channel);
INT32 vol = get_vol(channel);
unsigned int phase = get_phase(channel);
unsigned int freq = get_freq(channel);
unsigned int len = get_len(channel);
unsigned int off = get_off(channel);
int vol = get_vol(channel);
// accumulate 24-bit phase
phase = (phase + freq) & 0x00FFFFFF;
// wrap phase if wavelength exceeded
UINT32 hilen = len << 16;
unsigned int hilen = len << 16;
while (phase >= hilen) phase -= hilen;
// write back phase
set_phase(phase, channel);
// 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;
// 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[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
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[1] = (b[1] * MIX[channels]) >> 7;
// 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".
const double MASTER_VOL = 6.0 * 1223.0;
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[1] = (b[1] * GAIN) >> 8;
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
{
@ -286,7 +251,7 @@ bool NES_N106::Write (UINT32 adr, UINT32 val, UINT32 id)
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
{
@ -302,7 +267,7 @@ bool NES_N106::Read (UINT32 adr, UINT32 & val, UINT32 id)
// 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
channel = channel << 3;
@ -311,7 +276,7 @@ inline UINT32 NES_N106::get_phase (int channel)
+ (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
channel = channel << 3;
@ -320,28 +285,28 @@ inline UINT32 NES_N106::get_freq (int channel)
+ ((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
channel = channel << 3;
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
channel = channel << 3;
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
channel = channel << 3;
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
return (index&1) ?
@ -355,7 +320,7 @@ inline int NES_N106::get_channels ()
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
channel = channel << 3;

View file

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

View file

@ -1,4 +1,5 @@
#include "nes_vrc6.h"
#include "common.h"
namespace xgm
{
@ -16,11 +17,11 @@ namespace xgm
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 > 2) return;
@ -28,32 +29,6 @@ namespace xgm
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)
{
clock = c;
@ -91,9 +66,9 @@ namespace xgm
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, 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;
}
INT16 NES_VRC6::calc_saw (UINT32 clocks)
short NES_VRC6::calc_saw (unsigned int clocks)
{
if (!enable[2])
return 0;
@ -151,16 +126,16 @@ namespace xgm
return phase[2] >> 3;
}
void NES_VRC6::Tick (UINT32 clocks)
void NES_VRC6::Tick (unsigned int clocks)
{
out[0] = calc_sqr(0,clocks);
out[1] = calc_sqr(1,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[1] = out[1];
m[2] = out[2];
@ -182,14 +157,14 @@ namespace xgm
//b[1] >>= (7 - 7);
// 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[1] = (b[1] * MASTER) >> 8;
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 };
@ -255,7 +230,7 @@ namespace xgm
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;
}

View file

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