240 lines
5.3 KiB
C++
240 lines
5.3 KiB
C++
#include "nes_vrc6.h"
|
|
#include "common.h"
|
|
|
|
namespace xgm
|
|
{
|
|
|
|
NES_VRC6::NES_VRC6 ()
|
|
{
|
|
SetClock (DEFAULT_CLOCK);
|
|
SetRate (DEFAULT_RATE);
|
|
|
|
halt = false;
|
|
freq_shift = 0;
|
|
|
|
for(int c=0;c<2;++c)
|
|
for(int t=0;t<3;++t)
|
|
sm[c][t] = 128;
|
|
}
|
|
|
|
NES_VRC6::~NES_VRC6 ()
|
|
{
|
|
}
|
|
|
|
void NES_VRC6::SetStereoMix(int trk, short mixl, short mixr)
|
|
{
|
|
if (trk < 0) return;
|
|
if (trk > 2) return;
|
|
sm[0][trk] = mixl;
|
|
sm[1][trk] = mixr;
|
|
}
|
|
|
|
void NES_VRC6::SetClock (double c)
|
|
{
|
|
clock = c;
|
|
}
|
|
|
|
void NES_VRC6::SetRate (double r)
|
|
{
|
|
rate = r ? r : DEFAULT_RATE;
|
|
}
|
|
|
|
void NES_VRC6::SetOption (int id, int val)
|
|
{
|
|
if(id<OPT_END)
|
|
{
|
|
//option[id] = val;
|
|
}
|
|
}
|
|
|
|
void NES_VRC6::Reset ()
|
|
{
|
|
Write (0x9003, 0);
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
Write (0x9000 + i, 0);
|
|
Write (0xa000 + i, 0);
|
|
Write (0xb000 + i, 0);
|
|
}
|
|
count14 = 0;
|
|
mask = 0;
|
|
counter[0] = 0;
|
|
counter[1] = 0;
|
|
counter[2] = 0;
|
|
phase[0] = 0;
|
|
phase[0] = 1;
|
|
phase[0] = 2;
|
|
}
|
|
|
|
short NES_VRC6::calc_sqr (int i, unsigned int clocks)
|
|
{
|
|
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},
|
|
{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, 1, 1, 1, 1, 1},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1},
|
|
{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}
|
|
};
|
|
|
|
if (!enable[i])
|
|
return 0;
|
|
|
|
if (!halt)
|
|
{
|
|
counter[i] += clocks;
|
|
while(counter[i] > freq2[i])
|
|
{
|
|
phase[i] = (phase[i] + 1) & 15;
|
|
counter[i] -= (freq2[i] + 1);
|
|
}
|
|
}
|
|
|
|
return (gate[i]
|
|
|| sqrtbl[duty[i]][phase[i]])? volume[i] : 0;
|
|
}
|
|
|
|
short NES_VRC6::calc_saw (unsigned int clocks)
|
|
{
|
|
if (!enable[2])
|
|
return 0;
|
|
|
|
if (!halt)
|
|
{
|
|
counter[2] += clocks;
|
|
while(counter[2] > freq2[2])
|
|
{
|
|
counter[2] -= (freq2[2] + 1);
|
|
|
|
// accumulate saw
|
|
++count14;
|
|
if (count14 >= 14)
|
|
{
|
|
count14 = 0;
|
|
phase[2] = 0;
|
|
}
|
|
else if (0 == (count14 & 1)) // only accumulate on even ticks
|
|
{
|
|
phase[2] = (phase[2] + volume[2]) & 0xFF; // note 8-bit wrapping behaviour
|
|
}
|
|
}
|
|
}
|
|
|
|
// only top 5 bits of saw are output
|
|
return phase[2] >> 3;
|
|
}
|
|
|
|
void NES_VRC6::Tick (unsigned int clocks)
|
|
{
|
|
out[0] = calc_sqr(0,clocks);
|
|
out[1] = calc_sqr(1,clocks);
|
|
out[2] = calc_saw(clocks);
|
|
}
|
|
|
|
unsigned int NES_VRC6::Render (int b[2])
|
|
{
|
|
int m[3];
|
|
m[0] = out[0];
|
|
m[1] = out[1];
|
|
m[2] = out[2];
|
|
|
|
// note: signal is inverted compared to 2A03
|
|
|
|
m[0] = (mask & 1) ? 0 : -m[0];
|
|
m[1] = (mask & 2) ? 0 : -m[1];
|
|
m[2] = (mask & 4) ? 0 : -m[2];
|
|
|
|
b[0] = m[0] * sm[0][0];
|
|
b[0] += m[1] * sm[0][1];
|
|
b[0] += m[2] * sm[0][2];
|
|
//b[0] >>= (7 - 7);
|
|
|
|
b[1] = m[0] * sm[1][0];
|
|
b[1] += m[1] * sm[1][1];
|
|
b[1] += m[2] * sm[1][2];
|
|
//b[1] >>= (7 - 7);
|
|
|
|
// master volume adjustment
|
|
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 (unsigned int adr, unsigned int val, unsigned int id)
|
|
{
|
|
int ch, cmap[4] = { 0, 0, 1, 2 };
|
|
|
|
switch (adr)
|
|
{
|
|
case 0x9000:
|
|
case 0xa000:
|
|
ch = cmap[(adr >> 12) & 3];
|
|
volume[ch] = val & 15;
|
|
duty[ch] = (val >> 4) & 7;
|
|
gate[ch] = (val >> 7) & 1;
|
|
break;
|
|
case 0xb000:
|
|
volume[2] = val & 63;
|
|
break;
|
|
|
|
case 0x9001:
|
|
case 0xa001:
|
|
case 0xb001:
|
|
ch = cmap[(adr >> 12) & 3];
|
|
freq[ch] = (freq[ch] & 0xf00) | val;
|
|
freq2[ch] = (freq[ch] >> freq_shift);
|
|
if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch];
|
|
break;
|
|
|
|
case 0x9002:
|
|
case 0xa002:
|
|
case 0xb002:
|
|
ch = cmap[(adr >> 12) & 3];
|
|
freq[ch] = ((val & 0xf) << 8) + (freq[ch] & 0xff);
|
|
freq2[ch] = (freq[ch] >> freq_shift);
|
|
if (counter[ch] > freq2[ch]) counter[ch] = freq2[ch];
|
|
if (!enable[ch]) // if enable is being turned on, phase should be reset
|
|
{
|
|
if (ch == 2)
|
|
{
|
|
count14 = 0; // reset saw
|
|
}
|
|
phase[ch] = 0;
|
|
}
|
|
enable[ch] = (val >> 7) & 1;
|
|
break;
|
|
|
|
case 0x9003:
|
|
halt = val & 1;
|
|
freq_shift =
|
|
(val & 4) ? 8 :
|
|
(val & 2) ? 4 :
|
|
0;
|
|
freq2[0] = (freq[0] >> freq_shift);
|
|
freq2[1] = (freq[1] >> freq_shift);
|
|
freq2[2] = (freq[2] >> freq_shift);
|
|
if (counter[0] > freq2[0]) counter[0] = freq2[0];
|
|
if (counter[1] > freq2[1]) counter[1] = freq2[1];
|
|
if (counter[2] > freq2[2]) counter[2] = freq2[2];
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool NES_VRC6::Read (unsigned int adr, unsigned int & val, unsigned int id)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
} // namespace
|