完成APU_FME7
This commit is contained in:
parent
680a8d4eee
commit
209c31fd72
@ -1,28 +1,401 @@
|
||||
using System;
|
||||
using static VirtualNes.Core.APU_INTERNAL;
|
||||
using System.Net;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class APU_FME7 : APU_INTERFACE
|
||||
{
|
||||
// Envelope tables
|
||||
byte[] envelope_pulse0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
byte[] envelope_pulse1 = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x00
|
||||
};
|
||||
byte[] envelope_pulse2 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x1F
|
||||
};
|
||||
byte[] envelope_pulse3 = {
|
||||
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x1F
|
||||
};
|
||||
sbyte[] envstep_pulse = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 0
|
||||
};
|
||||
|
||||
byte[] envelope_sawtooth0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
byte[] envelope_sawtooth1 = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
||||
};
|
||||
sbyte[] envstep_sawtooth = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, -15
|
||||
};
|
||||
|
||||
byte[] envelope_triangle0 = {
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
|
||||
};
|
||||
byte[] envelope_triangle1 = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18,
|
||||
0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
|
||||
0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
|
||||
};
|
||||
sbyte[] envstep_triangle = {
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, -31
|
||||
};
|
||||
|
||||
byte[][] envelope_table;
|
||||
sbyte[][] envstep_table;
|
||||
|
||||
ENVELOPE envelope = new ENVELOPE();
|
||||
NOISE noise = new NOISE();
|
||||
CHANNEL[] op = new CHANNEL[3] { new CHANNEL(), new CHANNEL(), new CHANNEL() };
|
||||
byte address;
|
||||
int[] vol_table = new int[0x20];
|
||||
int cycle_rate;
|
||||
|
||||
float cpu_clock;
|
||||
|
||||
public APU_FME7()
|
||||
{
|
||||
envelope_table = new byte[16][]
|
||||
{
|
||||
envelope_pulse0, envelope_pulse0, envelope_pulse0, envelope_pulse0,
|
||||
envelope_pulse1, envelope_pulse1, envelope_pulse1, envelope_pulse1,
|
||||
envelope_sawtooth0, envelope_pulse0, envelope_triangle0, envelope_pulse2,
|
||||
envelope_sawtooth1, envelope_pulse3, envelope_triangle1, envelope_pulse1
|
||||
};
|
||||
|
||||
envstep_table = new sbyte[16][]
|
||||
{
|
||||
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
|
||||
envstep_pulse, envstep_pulse, envstep_pulse, envstep_pulse,
|
||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse,
|
||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse
|
||||
};
|
||||
|
||||
Reset(APU_CLOCK, 22050);
|
||||
}
|
||||
|
||||
public override void Reset(float fClock, int nRate)
|
||||
{
|
||||
//todo : 实现
|
||||
int i;
|
||||
|
||||
envelope.ZeroMemory();
|
||||
noise.ZeroMemory();
|
||||
|
||||
foreach (var item in op)
|
||||
{
|
||||
item.ZeroMemory();
|
||||
}
|
||||
|
||||
envelope.envtbl = envelope_table[0];
|
||||
envelope.envstep = envstep_table[0];
|
||||
|
||||
noise.noiserange = 1;
|
||||
noise.noiseout = 0xFF;
|
||||
|
||||
address = 0;
|
||||
|
||||
// Volume to voltage
|
||||
double @out = 0x1FFF;
|
||||
for (i = 31; i > 1; i--)
|
||||
{
|
||||
vol_table[i] = (int)(@out + 0.5);
|
||||
@out /= 1.188502227; /* = 10 ^ (1.5/20) = 1.5dB */
|
||||
}
|
||||
vol_table[1] = 0;
|
||||
vol_table[0] = 0;
|
||||
|
||||
Setup(fClock, nRate);
|
||||
}
|
||||
|
||||
public override void Setup(float fClock, int nRate)
|
||||
{
|
||||
//todo : 实现
|
||||
cpu_clock = fClock;
|
||||
cycle_rate = (int)((fClock / 16.0f) * (1 << 16) / nRate);
|
||||
}
|
||||
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
//todo : 实现
|
||||
if (addr == 0xC000)
|
||||
{
|
||||
address = data;
|
||||
}
|
||||
else if (addr == 0xE000)
|
||||
{
|
||||
byte chaddr = address;
|
||||
switch (chaddr)
|
||||
{
|
||||
case 0x00:
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x05:
|
||||
{
|
||||
CHANNEL ch = op[chaddr >> 1];
|
||||
ch.reg[chaddr & 0x01] = data;
|
||||
ch.freq = INT2FIX(((ch.reg[1] & 0x0F) << 8) + ch.reg[0] + 1);
|
||||
}
|
||||
break;
|
||||
case 0x06:
|
||||
noise.freq = INT2FIX((data & 0x1F) + 1);
|
||||
break;
|
||||
case 0x07:
|
||||
{
|
||||
for (byte i = 0; i < 3; i++)
|
||||
{
|
||||
op[i].enable = (byte)(data & (1 << i));
|
||||
op[i].noise_on = (byte)(data & (8 << i));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0A:
|
||||
{
|
||||
CHANNEL ch = op[chaddr & 3];
|
||||
ch.reg[2] = data;
|
||||
ch.env_on = (byte)(data & 0x10);
|
||||
ch.volume = (byte)((data & 0x0F) * 2);
|
||||
}
|
||||
break;
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
envelope.reg[chaddr - 0x0B] = data;
|
||||
envelope.freq = INT2FIX(((envelope.reg[1] & 0x0F) << 8) + envelope.reg[0] + 1);
|
||||
break;
|
||||
case 0x0D:
|
||||
envelope.envtbl = envelope_table[data & 0x0F];
|
||||
envelope.envstep = envstep_table[data & 0x0F];
|
||||
envelope.envadr = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
//todo : 实现
|
||||
if (channel < 3)
|
||||
{
|
||||
return ChannelRender(op[channel]);
|
||||
}
|
||||
else if (channel == 3)
|
||||
{
|
||||
// 必ずch3を1回呼んでからch0-2を呼ぶ事
|
||||
EnvelopeRender();
|
||||
NoiseRender();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public override int GetFreq(int channel)
|
||||
{
|
||||
if (channel < 3)
|
||||
{
|
||||
CHANNEL ch = op[channel];
|
||||
|
||||
if (ch.enable != 0 || ch.freq == 0)
|
||||
return 0;
|
||||
if (ch.env_on != 0)
|
||||
{
|
||||
if (envelope.volume == 0)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ch.volume == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EnvelopeRender()
|
||||
{
|
||||
if (envelope.freq == 0)
|
||||
return;
|
||||
envelope.phaseacc -= cycle_rate;
|
||||
if (envelope.phaseacc >= 0)
|
||||
return;
|
||||
while (envelope.phaseacc < 0)
|
||||
{
|
||||
envelope.phaseacc += envelope.freq;
|
||||
envelope.envadr += envelope.envstep[envelope.envadr];
|
||||
}
|
||||
envelope.volume = envelope.envtbl[envelope.envadr];
|
||||
}
|
||||
void NoiseRender()
|
||||
{
|
||||
if (noise.freq == 0)
|
||||
return;
|
||||
noise.phaseacc -= cycle_rate;
|
||||
if (noise.phaseacc >= 0)
|
||||
return;
|
||||
while (noise.phaseacc < 0)
|
||||
{
|
||||
noise.phaseacc += noise.freq;
|
||||
if (((noise.noiserange + 1) & 0x02) != 0)
|
||||
noise.noiseout = (byte)(~noise.noiseout);
|
||||
if ((noise.noiserange & 0x01) != 0)
|
||||
noise.noiserange ^= 0x28000;
|
||||
noise.noiserange >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
int ChannelRender(CHANNEL ch)
|
||||
{
|
||||
int output, volume;
|
||||
|
||||
if (ch.enable != 0)
|
||||
return 0;
|
||||
if (ch.freq == 0)
|
||||
return 0;
|
||||
|
||||
ch.phaseacc -= cycle_rate;
|
||||
while (ch.phaseacc < 0)
|
||||
{
|
||||
ch.phaseacc += ch.freq;
|
||||
ch.adder++;
|
||||
}
|
||||
|
||||
output = volume = 0;
|
||||
volume = ch.env_on != 0 ? vol_table[envelope.volume] : vol_table[ch.volume + 1];
|
||||
|
||||
if ((ch.adder & 0x01) != 0)
|
||||
{
|
||||
output += volume;
|
||||
}
|
||||
else
|
||||
{
|
||||
output -= volume;
|
||||
}
|
||||
if (ch.noise_on == 0)
|
||||
{
|
||||
if (noise.noiseout != 0)
|
||||
output += volume;
|
||||
else
|
||||
output -= volume;
|
||||
}
|
||||
|
||||
ch.output_vol = output;
|
||||
|
||||
return ch.output_vol;
|
||||
}
|
||||
|
||||
public class ENVELOPE
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
public byte volume;
|
||||
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
public int envadr;
|
||||
|
||||
public byte[] envtbl;
|
||||
public sbyte[] envstep;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, 3);
|
||||
volume = 0;
|
||||
freq = 0;
|
||||
phaseacc = 0;
|
||||
envadr = 0;
|
||||
envtbl = null;
|
||||
envstep = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class NOISE
|
||||
{
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
public int noiserange;
|
||||
public byte noiseout;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
freq = 0; phaseacc = 0; noiserange = 0; noiseout = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class CHANNEL
|
||||
{
|
||||
public byte[] reg = new byte[3];
|
||||
public byte enable;
|
||||
public byte env_on;
|
||||
public byte noise_on;
|
||||
public byte adder;
|
||||
public byte volume;
|
||||
|
||||
public int freq;
|
||||
public int phaseacc;
|
||||
|
||||
public int output_vol;
|
||||
|
||||
public void ZeroMemory()
|
||||
{
|
||||
Array.Clear(reg, 0, reg.Length);
|
||||
enable = 0;
|
||||
env_on = 0;
|
||||
noise_on = 0;
|
||||
adder = 0;
|
||||
volume = 0; freq = 0;
|
||||
phaseacc = 0;
|
||||
output_vol = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user