diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index 49ad681..9ed2372 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -1192,5 +1192,222 @@ namespace VirtualNes.Core break; } } + public class RECTANGLE + { + public byte[] reg = new byte[4]; // register + + public byte enable; // enable + public byte holdnote; // holdnote + public byte volume; // volume + public byte complement; + + // For Render + public int phaseacc; + public int freq; + public int freqlimit; + public int adder; + public int duty; + public int len_count; + + public int nowvolume; + + // For Envelope + public byte env_fixed; + public byte env_decay; + public byte env_count; + public byte dummy0; + public int env_vol; + + // For Sweep + public byte swp_on; + public byte swp_inc; + public byte swp_shift; + public byte swp_decay; + public byte swp_count; + public byte[] dummy1 = new byte[3]; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_output_enable; + public byte sync_enable; + public byte sync_holdnote; + public byte dummy2; + public int sync_len_count; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + enable = 0; + holdnote = 0; + volume = 0; + complement = 0; + + phaseacc = 0; + freq = 0; + freqlimit = 0; + adder = 0; + duty = 0; + len_count = 0; + + nowvolume = 0; + + env_fixed = 0; + env_decay = 0; + env_count = 0; + dummy0 = 0; + env_vol = 0; + + swp_on = 0; + swp_inc = 0; + swp_shift = 0; + swp_decay = 0; + swp_count = 0; + Array.Clear(dummy1, 0, dummy1.Length); + + Array.Clear(sync_reg, 0, sync_reg.Length); + sync_output_enable = 0; + sync_enable = 0; + sync_holdnote = 0; + dummy2 = 0; + sync_len_count = 0; + } + } + public class TRIANGLE + { + public byte[] reg = new byte[4]; + + public byte enable; + public byte holdnote; + public byte counter_start; + public byte dummy0; + + public int phaseacc; + public int freq; + public int len_count; + public int lin_count; + public int adder; + + public int nowvolume; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_enable; + public byte sync_holdnote; + public byte sync_counter_start; + // public byte dummy1; + public int sync_len_count; + public int sync_lin_count; + + internal void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + + enable = 0; + holdnote = 0; + counter_start = 0; + dummy0 = 0; + phaseacc = 0; + freq = 0; + len_count = 0; + lin_count = 0; + adder = 0; + nowvolume = 0; + Array.Clear(sync_reg, 0, sync_reg.Length); + sync_enable = 0; + sync_holdnote = 0; + sync_counter_start = 0; + + sync_len_count = 0; + sync_lin_count = 0; + } + } + public class DPCM + { + public byte[] reg = new byte[4]; + public byte enable; + public byte looping; + public byte cur_byte; + public byte dpcm_value; + + public int freq; + public int phaseacc; + public int output; + + public ushort address, cache_addr; + public int dmalength, cache_dmalength; + public int dpcm_output_real, dpcm_output_fake, dpcm_output_old, dpcm_output_offset; + + // For sync + public byte[] sync_reg = new byte[4]; + public byte sync_enable; + public byte sync_looping; + public byte sync_irq_gen; + public byte sync_irq_enable; + public int sync_cycles, sync_cache_cycles; + public int sync_dmalength, sync_cache_dmalength; + } + public class NOISE + { + public byte[] reg = new byte[4]; // register + + public byte enable; // enable + public byte holdnote; // holdnote + public byte volume; // volume + public byte xor_tap; + public int shift_reg; + + // For Render + public int phaseacc; + public int freq; + public int len_count; + + public int nowvolume; + public int output; + + // For Envelope + public byte env_fixed; + public byte env_decay; + public byte env_count; + public byte dummy0; + public int env_vol; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_output_enable; + public byte sync_enable; + public byte sync_holdnote; + public byte dummy1; + public int sync_len_count; + + internal void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + + enable = 0; + holdnote = 0; + volume = 0; + xor_tap = 0; + shift_reg = 0; + + phaseacc = 0; + freq = 0; + len_count = 0; + nowvolume = 0; + output = 0; + + env_fixed = 0; + env_decay = 0; + env_count = 0; + dummy0 = 0; + env_vol = 0; + + Array.Clear(sync_reg, 0, sync_reg.Length); + sync_output_enable = 0; + sync_enable = 0; + sync_holdnote = 0; + dummy1 = 0; + sync_len_count = 0; + } + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs index 2fe943e..197fab7 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs @@ -1,26 +1,245 @@ -namespace VirtualNes.Core +using RECTANGLE = VirtualNes.Core.APU_VRC6.RECTANGLE; +using SAWTOOTH = VirtualNes.Core.APU_VRC6.SAWTOOTH; + +namespace VirtualNes.Core { public class APU_N106 : APU_INTERFACE { + RECTANGLE ch0 = new RECTANGLE(); + RECTANGLE ch1 = new RECTANGLE(); + SAWTOOTH ch2 = new SAWTOOTH(); + float cpu_clock; + int cycle_rate; + + public APU_N106() + { + Reset(APU_CLOCK, 22050); + } + public override void Reset(float fClock, int nRate) { - //todo : 实现 + ch0.ZeroMemory(); + ch1.ZeroMemory(); + ch2.ZeroMemory(); + + Setup(fClock, nRate); } public override void Setup(float fClock, int nRate) { - //todo : 实现 + cpu_clock = fClock; + cycle_rate = (int)(fClock * 65536.0f / nRate); } public override void Write(ushort addr, byte data) { - //todo : 实现 + switch (addr) + { + // VRC6 CH0 rectangle + case 0x9000: + ch0.reg[0] = data; + ch0.gate = (byte)(data & 0x80); + ch0.volume = (byte)(data & 0x0F); + ch0.duty_pos = (byte)((data >> 4) & 0x07); + break; + case 0x9001: + ch0.reg[1] = data; + ch0.freq = INT2FIX((((ch0.reg[2] & 0x0F) << 8) | data) + 1); + break; + case 0x9002: + ch0.reg[2] = data; + ch0.enable = (byte)(data & 0x80); + ch0.freq = INT2FIX((((data & 0x0F) << 8) | ch0.reg[1]) + 1); + break; + // VRC6 CH1 rectangle + case 0xA000: + ch1.reg[0] = data; + ch1.gate = (byte)(data & 0x80); + ch1.volume = (byte)(data & 0x0F); + ch1.duty_pos = (byte)((data >> 4) & 0x07); + break; + case 0xA001: + ch1.reg[1] = data; + ch1.freq = INT2FIX((((ch1.reg[2] & 0x0F) << 8) | data) + 1); + break; + case 0xA002: + ch1.reg[2] = data; + ch1.enable = (byte)(data & 0x80); + ch1.freq = INT2FIX((((data & 0x0F) << 8) | ch1.reg[1]) + 1); + break; + // VRC6 CH2 sawtooth + case 0xB000: + ch2.reg[1] = data; + ch2.phaseaccum = (byte)(data & 0x3F); + break; + case 0xB001: + ch2.reg[1] = data; + ch2.freq = INT2FIX((((ch2.reg[2] & 0x0F) << 8) | data) + 1); + break; + case 0xB002: + ch2.reg[2] = data; + ch2.enable = (byte)(data & 0x80); + ch2.freq = INT2FIX((((data & 0x0F) << 8) | ch2.reg[1]) + 1); + // ch2.adder = 0; // クリアするとノイズの原因になる + // ch2.accum = 0; // クリアするとノイズの原因になる + break; + } } public override int Process(int channel) { - //todo : 实现 + switch (channel) + { + case 0: + return RectangleRender(ch0); + case 1: + return RectangleRender(ch1); + case 2: + return SawtoothRender(ch2); + } + return 0; } + + public override int GetFreq(int channel) + { + if (channel == 0 || channel == 1) + { + RECTANGLE ch; + if (channel == 0) ch = ch0; + else ch = ch1; + if (ch.enable == 0 || ch.gate != 0 || ch.volume == 0) + return 0; + if (ch.freq < INT2FIX(8)) + return 0; + return (int)((256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f))); + } + if (channel == 2) + { + SAWTOOTH ch = ch2; + if (ch.enable == 0 || ch.phaseaccum == 0) + return 0; + if (ch.freq < INT2FIX(8)) + return 0; + return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 14.0f)); + } + + return 0; + } + + int RectangleRender(RECTANGLE ch) + { + // Enable? + if (ch.enable == 0) + { + ch.output_vol = 0; + ch.adder = 0; + return ch.output_vol; + } + + // Digitized output + if (ch.gate != 0) + { + ch.output_vol = ch.volume << APU_VRC6.RECTANGLE_VOL_SHIFT; + return ch.output_vol; + } + + // 一定以上の周波数は処理しない(無駄) + if (ch.freq < INT2FIX(8)) + { + ch.output_vol = 0; + return ch.output_vol; + } + + ch.phaseacc -= cycle_rate; + if (ch.phaseacc >= 0) + return ch.output_vol; + + int output = ch.volume << APU_VRC6.RECTANGLE_VOL_SHIFT; + + if (ch.freq > cycle_rate) + { + // add 1 step + ch.phaseacc += ch.freq; + ch.adder = (byte)((ch.adder + 1) & 0x0F); + if (ch.adder <= ch.duty_pos) + ch.output_vol = output; + else + ch.output_vol = -output; + } + else + { + // average calculate + int num_times, total; + num_times = total = 0; + while (ch.phaseacc < 0) + { + ch.phaseacc += ch.freq; + ch.adder = (byte)((ch.adder + 1) & 0x0F); + if (ch.adder <= ch.duty_pos) + total += output; + else + total += -output; + num_times++; + } + ch.output_vol = total / num_times; + } + + return ch.output_vol; + } + + int SawtoothRender(SAWTOOTH ch) + { + // Digitized output + if (ch.enable == 0) + { + ch.output_vol = 0; + return ch.output_vol; + } + + // 一定以上の周波数は処理しない(無駄) + if (ch.freq < INT2FIX(9)) + { + return ch.output_vol; + } + + ch.phaseacc -= cycle_rate / 2; + if (ch.phaseacc >= 0) + return ch.output_vol; + + if (ch.freq > cycle_rate / 2) + { + // add 1 step + ch.phaseacc += ch.freq; + if (++ch.adder >= 7) + { + ch.adder = 0; + ch.accum = 0; + } + ch.accum += ch.phaseaccum; + ch.output_vol = ch.accum << APU_VRC6.SAWTOOTH_VOL_SHIFT; + } + else + { + // average calculate + int num_times, total; + num_times = total = 0; + while (ch.phaseacc < 0) + { + ch.phaseacc += ch.freq; + if (++ch.adder >= 7) + { + ch.adder = 0; + ch.accum = 0; + } + ch.accum += ch.phaseaccum; + total += ch.accum << APU_VRC6.SAWTOOTH_VOL_SHIFT; + num_times++; + } + ch.output_vol = (total / num_times); + } + + return ch.output_vol; + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs index 0c42555..cfb7b75 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs @@ -245,7 +245,7 @@ namespace VirtualNes.Core return ch.output_vol; } - private class RECTANGLE + public class RECTANGLE { public byte[] reg = new byte[3]; @@ -276,7 +276,7 @@ namespace VirtualNes.Core } } - private class SAWTOOTH + public class SAWTOOTH { public byte[] reg = new byte[3]; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs deleted file mode 100644 index 35835b3..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace VirtualNes.Core -{ - public class DPCM - { - public byte[] reg = new byte[4]; - public byte enable; - public byte looping; - public byte cur_byte; - public byte dpcm_value; - - public int freq; - public int phaseacc; - public int output; - - public ushort address, cache_addr; - public int dmalength, cache_dmalength; - public int dpcm_output_real, dpcm_output_fake, dpcm_output_old, dpcm_output_offset; - - // For sync - public byte[] sync_reg = new byte[4]; - public byte sync_enable; - public byte sync_looping; - public byte sync_irq_gen; - public byte sync_irq_enable; - public int sync_cycles, sync_cache_cycles; - public int sync_dmalength, sync_cache_dmalength; - } -} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta deleted file mode 100644 index 08b468c..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: e6289a516ac91b541b2b1807bb07e2b0 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs deleted file mode 100644 index 0fa41a4..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; - -namespace VirtualNes.Core -{ - public class NOISE - { - public byte[] reg = new byte[4]; // register - - public byte enable; // enable - public byte holdnote; // holdnote - public byte volume; // volume - public byte xor_tap; - public int shift_reg; - - // For Render - public int phaseacc; - public int freq; - public int len_count; - - public int nowvolume; - public int output; - - // For Envelope - public byte env_fixed; - public byte env_decay; - public byte env_count; - public byte dummy0; - public int env_vol; - - // For sync; - public byte[] sync_reg = new byte[4]; - public byte sync_output_enable; - public byte sync_enable; - public byte sync_holdnote; - public byte dummy1; - public int sync_len_count; - - internal void ZeroMemory() - { - Array.Clear(reg, 0, reg.Length); - - enable = 0; - holdnote = 0; - volume = 0; - xor_tap = 0; - shift_reg = 0; - - phaseacc = 0; - freq = 0; - len_count = 0; - nowvolume = 0; - output = 0; - - env_fixed = 0; - env_decay = 0; - env_count = 0; - dummy0 = 0; - env_vol = 0; - - Array.Clear(sync_reg, 0, sync_reg.Length); - sync_output_enable = 0; - sync_enable = 0; - sync_holdnote = 0; - dummy1 = 0; - sync_len_count = 0; - } - } -} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta deleted file mode 100644 index 69ea5b5..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8680ce7dbdceb504dbda3b98dbdb1297 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs deleted file mode 100644 index 8d3688e..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; - -namespace VirtualNes.Core -{ - public class RECTANGLE - { - public byte[] reg = new byte[4]; // register - - public byte enable; // enable - public byte holdnote; // holdnote - public byte volume; // volume - public byte complement; - - // For Render - public int phaseacc; - public int freq; - public int freqlimit; - public int adder; - public int duty; - public int len_count; - - public int nowvolume; - - // For Envelope - public byte env_fixed; - public byte env_decay; - public byte env_count; - public byte dummy0; - public int env_vol; - - // For Sweep - public byte swp_on; - public byte swp_inc; - public byte swp_shift; - public byte swp_decay; - public byte swp_count; - public byte[] dummy1 = new byte[3]; - - // For sync; - public byte[] sync_reg = new byte[4]; - public byte sync_output_enable; - public byte sync_enable; - public byte sync_holdnote; - public byte dummy2; - public int sync_len_count; - - public void ZeroMemory() - { - Array.Clear(reg, 0, reg.Length); - enable = 0; - holdnote = 0; - volume = 0; - complement = 0; - - phaseacc = 0; - freq = 0; - freqlimit = 0; - adder = 0; - duty = 0; - len_count = 0; - - nowvolume = 0; - - env_fixed = 0; - env_decay = 0; - env_count = 0; - dummy0 = 0; - env_vol = 0; - - swp_on = 0; - swp_inc = 0; - swp_shift = 0; - swp_decay = 0; - swp_count = 0; - Array.Clear(dummy1, 0, dummy1.Length); - - Array.Clear(sync_reg, 0, sync_reg.Length); - sync_output_enable = 0; - sync_enable = 0; - sync_holdnote = 0; - dummy2 = 0; - sync_len_count = 0; - } - } -} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta deleted file mode 100644 index 9cbc24b..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6e50831f6c445fe489d7e1737269296e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs deleted file mode 100644 index 191a619..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace VirtualNes.Core -{ - public class TRIANGLE - { - public byte[] reg = new byte[4]; - - public byte enable; - public byte holdnote; - public byte counter_start; - public byte dummy0; - - public int phaseacc; - public int freq; - public int len_count; - public int lin_count; - public int adder; - - public int nowvolume; - - // For sync; - public byte[] sync_reg = new byte[4]; - public byte sync_enable; - public byte sync_holdnote; - public byte sync_counter_start; - // public byte dummy1; - public int sync_len_count; - public int sync_lin_count; - - internal void ZeroMemory() - { - Array.Clear(reg, 0, reg.Length); - - enable = 0; - holdnote = 0; - counter_start = 0; - dummy0 = 0; - phaseacc = 0; - freq = 0; - len_count = 0; - lin_count = 0; - adder = 0; - nowvolume = 0; - Array.Clear(sync_reg, 0, sync_reg.Length); - sync_enable = 0; - sync_holdnote = 0; - sync_counter_start = 0; - - sync_len_count = 0; - sync_lin_count = 0; - } - } -} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta deleted file mode 100644 index 4cfe6b6..0000000 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4ed2788da33fe474facc1d7ce1b34d03 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index 908a01b..46fa90b 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -1157,6 +1157,11 @@ namespace VirtualNes.Core bExtMono = bMode; } + internal int GetScanlineNo() + { + return ScanlineNo; + } + public struct Sprite { public byte y