diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs index 2871744..a7d59ed 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs @@ -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; + } + } } }