554 lines
14 KiB
C#
554 lines
14 KiB
C#
using System.IO;
|
|
|
|
namespace MyNes.Core
|
|
{
|
|
[WithExternalSound]
|
|
internal abstract class Namcot106 : Board
|
|
{
|
|
private int irq_counter;
|
|
|
|
private bool irq_enable;
|
|
|
|
private bool disables_chr_ram_A;
|
|
|
|
private bool disables_chr_ram_B;
|
|
|
|
private bool enable_mirroring_switch;
|
|
|
|
private bool enable_N106_sound;
|
|
|
|
private int temp_nmt;
|
|
|
|
private Namcot106Chnl[] sound_channels;
|
|
|
|
private byte soundReg;
|
|
|
|
public int enabledChannels;
|
|
|
|
private int enabledChannels1;
|
|
|
|
private int channelIndex;
|
|
|
|
private byte temp_val;
|
|
|
|
private byte temp_i;
|
|
|
|
public byte[] EXRAM;
|
|
|
|
private bool[] sound_channels_enable;
|
|
|
|
private double soundOut;
|
|
|
|
private int sound_out_div;
|
|
|
|
internal override void HardReset()
|
|
{
|
|
base.HardReset();
|
|
EXRAM = new byte[128];
|
|
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaE000);
|
|
enable_mirroring_switch = (enable_N106_sound = base.MapperNumber == 19);
|
|
switch (SHA1.ToUpper())
|
|
{
|
|
case "97E7E61EECB73CB1EA0C15AE51E65EA56301A685":
|
|
case "3D554F55411AB2DDD1A87E7583E643970DB784F3":
|
|
case "7FA51058307DB50825C2D3A3A98C0DA554BC3C92":
|
|
case "1C476C795CFC17E987C22FFD6F09BAF1396ED2C9":
|
|
enable_mirroring_switch = false;
|
|
enable_N106_sound = false;
|
|
break;
|
|
}
|
|
if (enable_N106_sound)
|
|
{
|
|
sound_channels = new Namcot106Chnl[8];
|
|
sound_channels_enable = new bool[8];
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
sound_channels[i] = new Namcot106Chnl(this);
|
|
sound_channels[i].HardReset();
|
|
}
|
|
soundReg = 0;
|
|
enabledChannels = 0;
|
|
channelIndex = 0;
|
|
APUApplyChannelsSettings();
|
|
}
|
|
}
|
|
|
|
internal override void WriteEX(ref ushort address, ref byte data)
|
|
{
|
|
switch (address & 0xF800)
|
|
{
|
|
case 18432:
|
|
if (soundReg >= 64)
|
|
{
|
|
switch (soundReg & 0x7F)
|
|
{
|
|
case 64:
|
|
sound_channels[0].WriteA(ref data);
|
|
break;
|
|
case 66:
|
|
sound_channels[0].WriteB(ref data);
|
|
break;
|
|
case 68:
|
|
sound_channels[0].WriteC(ref data);
|
|
break;
|
|
case 70:
|
|
sound_channels[0].WriteD(ref data);
|
|
break;
|
|
case 71:
|
|
sound_channels[0].WriteE(ref data);
|
|
break;
|
|
case 72:
|
|
sound_channels[1].WriteA(ref data);
|
|
break;
|
|
case 74:
|
|
sound_channels[1].WriteB(ref data);
|
|
break;
|
|
case 76:
|
|
sound_channels[1].WriteC(ref data);
|
|
break;
|
|
case 78:
|
|
sound_channels[1].WriteD(ref data);
|
|
break;
|
|
case 79:
|
|
sound_channels[1].WriteE(ref data);
|
|
break;
|
|
case 80:
|
|
sound_channels[2].WriteA(ref data);
|
|
break;
|
|
case 82:
|
|
sound_channels[2].WriteB(ref data);
|
|
break;
|
|
case 84:
|
|
sound_channels[2].WriteC(ref data);
|
|
break;
|
|
case 86:
|
|
sound_channels[2].WriteD(ref data);
|
|
break;
|
|
case 87:
|
|
sound_channels[2].WriteE(ref data);
|
|
break;
|
|
case 88:
|
|
sound_channels[3].WriteA(ref data);
|
|
break;
|
|
case 90:
|
|
sound_channels[3].WriteB(ref data);
|
|
break;
|
|
case 92:
|
|
sound_channels[3].WriteC(ref data);
|
|
break;
|
|
case 94:
|
|
sound_channels[3].WriteD(ref data);
|
|
break;
|
|
case 95:
|
|
sound_channels[3].WriteE(ref data);
|
|
break;
|
|
case 96:
|
|
sound_channels[4].WriteA(ref data);
|
|
break;
|
|
case 98:
|
|
sound_channels[4].WriteB(ref data);
|
|
break;
|
|
case 100:
|
|
sound_channels[4].WriteC(ref data);
|
|
break;
|
|
case 102:
|
|
sound_channels[4].WriteD(ref data);
|
|
break;
|
|
case 103:
|
|
sound_channels[4].WriteE(ref data);
|
|
break;
|
|
case 104:
|
|
sound_channels[5].WriteA(ref data);
|
|
break;
|
|
case 106:
|
|
sound_channels[5].WriteB(ref data);
|
|
break;
|
|
case 108:
|
|
sound_channels[5].WriteC(ref data);
|
|
break;
|
|
case 110:
|
|
sound_channels[5].WriteD(ref data);
|
|
break;
|
|
case 111:
|
|
sound_channels[5].WriteE(ref data);
|
|
break;
|
|
case 112:
|
|
sound_channels[6].WriteA(ref data);
|
|
break;
|
|
case 114:
|
|
sound_channels[6].WriteB(ref data);
|
|
break;
|
|
case 116:
|
|
sound_channels[6].WriteC(ref data);
|
|
break;
|
|
case 118:
|
|
sound_channels[6].WriteD(ref data);
|
|
break;
|
|
case 119:
|
|
sound_channels[6].WriteE(ref data);
|
|
break;
|
|
case 120:
|
|
sound_channels[7].WriteA(ref data);
|
|
break;
|
|
case 122:
|
|
sound_channels[7].WriteB(ref data);
|
|
break;
|
|
case 124:
|
|
sound_channels[7].WriteC(ref data);
|
|
break;
|
|
case 126:
|
|
sound_channels[7].WriteD(ref data);
|
|
break;
|
|
case 127:
|
|
sound_channels[7].WriteE(ref data);
|
|
enabledChannels = (data & 0x70) >> 4;
|
|
channelIndex = 0;
|
|
enabledChannels1 = enabledChannels + 1;
|
|
temp_i = 7;
|
|
while (temp_i >= 0 && enabledChannels1 > 0)
|
|
{
|
|
sound_channels[temp_i].Enabled = true;
|
|
enabledChannels1--;
|
|
temp_i--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
EXRAM[soundReg & 0x7F] = data;
|
|
if ((soundReg & 0x80) == 128)
|
|
{
|
|
soundReg = (byte)(((uint)(soundReg + 1) & 0x7Fu) | 0x80u);
|
|
}
|
|
break;
|
|
case 20480:
|
|
NesEmu.IRQFlags &= -9;
|
|
irq_counter = (irq_counter & 0x7F00) | data;
|
|
break;
|
|
case 22528:
|
|
NesEmu.IRQFlags &= -9;
|
|
irq_counter = (irq_counter & 0xFF) | ((data & 0x7F) << 8);
|
|
irq_enable = (data & 0x80) == 128;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void ReadEX(ref ushort address, out byte value)
|
|
{
|
|
switch (address & 0xF800)
|
|
{
|
|
case 18432:
|
|
value = EXRAM[soundReg & 0x7F];
|
|
if ((soundReg & 0x80) == 128)
|
|
{
|
|
soundReg = (byte)(((uint)(soundReg + 1) & 0x7Fu) | 0x80u);
|
|
}
|
|
break;
|
|
case 20480:
|
|
NesEmu.IRQFlags &= -9;
|
|
value = (byte)((uint)irq_counter & 0xFFu);
|
|
break;
|
|
case 22528:
|
|
NesEmu.IRQFlags &= -9;
|
|
value = (byte)((irq_enable ? 128u : 0u) | (uint)((irq_counter & 0x7F00) >> 8));
|
|
break;
|
|
default:
|
|
value = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void WritePRG(ref ushort address, ref byte data)
|
|
{
|
|
switch (address & 0xF800)
|
|
{
|
|
case 32768:
|
|
if (!disables_chr_ram_A)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area0000);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area0000);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area0000);
|
|
Switch01KCHR(data, CHRArea.Area0000);
|
|
}
|
|
break;
|
|
case 34816:
|
|
if (!disables_chr_ram_A)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area0400);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area0400);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area0400);
|
|
Switch01KCHR(data, CHRArea.Area0400);
|
|
}
|
|
break;
|
|
case 36864:
|
|
if (!disables_chr_ram_A)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area0800);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area0800);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area0800);
|
|
Switch01KCHR(data, CHRArea.Area0800);
|
|
}
|
|
break;
|
|
case 38912:
|
|
if (!disables_chr_ram_A)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area0C00);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area0C00);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area0C00);
|
|
Switch01KCHR(data, CHRArea.Area0C00);
|
|
}
|
|
break;
|
|
case 40960:
|
|
if (!disables_chr_ram_B)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area1000);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area1000);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area1000);
|
|
Switch01KCHR(data, CHRArea.Area1000);
|
|
}
|
|
break;
|
|
case 43008:
|
|
if (!disables_chr_ram_B)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area1400);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area1400);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area1400);
|
|
Switch01KCHR(data, CHRArea.Area1400);
|
|
}
|
|
break;
|
|
case 45056:
|
|
if (!disables_chr_ram_B)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area1800);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area1800);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area1800);
|
|
Switch01KCHR(data, CHRArea.Area1800);
|
|
}
|
|
break;
|
|
case 47104:
|
|
if (!disables_chr_ram_B)
|
|
{
|
|
Toggle01KCHR_RAM(data >= 224, CHRArea.Area1C00);
|
|
Switch01KCHR((data >= 224) ? (data - 224) : data, CHRArea.Area1C00);
|
|
}
|
|
else
|
|
{
|
|
Toggle01KCHR_RAM(ram: false, CHRArea.Area1C00);
|
|
Switch01KCHR(data, CHRArea.Area1C00);
|
|
}
|
|
break;
|
|
case 49152:
|
|
if (enable_mirroring_switch)
|
|
{
|
|
NMT_AREA_BLK_INDEX[0] = data;
|
|
}
|
|
break;
|
|
case 51200:
|
|
if (enable_mirroring_switch)
|
|
{
|
|
NMT_AREA_BLK_INDEX[1] = data;
|
|
}
|
|
break;
|
|
case 53248:
|
|
if (enable_mirroring_switch)
|
|
{
|
|
NMT_AREA_BLK_INDEX[2] = data;
|
|
}
|
|
break;
|
|
case 55296:
|
|
if (enable_mirroring_switch)
|
|
{
|
|
NMT_AREA_BLK_INDEX[3] = data;
|
|
}
|
|
break;
|
|
case 57344:
|
|
Switch08KPRG(data & 0x3F, PRGArea.Area8000);
|
|
break;
|
|
case 59392:
|
|
Switch08KPRG(data & 0x3F, PRGArea.AreaA000);
|
|
disables_chr_ram_A = (data & 0x40) == 64;
|
|
disables_chr_ram_B = (data & 0x80) == 128;
|
|
break;
|
|
case 61440:
|
|
Switch08KPRG(data & 0x3F, PRGArea.AreaC000);
|
|
break;
|
|
case 63488:
|
|
soundReg = data;
|
|
break;
|
|
}
|
|
}
|
|
|
|
internal override void ReadNMT(ref ushort address, out byte data)
|
|
{
|
|
if (enable_mirroring_switch)
|
|
{
|
|
temp_nmt = NMT_AREA_BLK_INDEX[(address >> 10) & 3];
|
|
if (temp_nmt >= 224)
|
|
{
|
|
data = NMT_RAM[(temp_nmt - 224) & 1][address & 0x3FF];
|
|
}
|
|
else
|
|
{
|
|
data = CHR_ROM[temp_nmt][address & 0x3FF];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
base.ReadNMT(ref address, out data);
|
|
}
|
|
}
|
|
|
|
internal override void WriteNMT(ref ushort address, ref byte data)
|
|
{
|
|
if (enable_mirroring_switch)
|
|
{
|
|
temp_nmt = NMT_AREA_BLK_INDEX[(address >> 10) & 3];
|
|
if (temp_nmt >= 224)
|
|
{
|
|
NMT_RAM[(temp_nmt - 224) & 1][address & 0x3FF] = data;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
base.WriteNMT(ref address, ref data);
|
|
}
|
|
}
|
|
|
|
internal override void OnCPUClock()
|
|
{
|
|
if (irq_enable)
|
|
{
|
|
if (irq_counter == 32767)
|
|
{
|
|
NesEmu.IRQFlags |= 8;
|
|
irq_counter = 0;
|
|
}
|
|
else
|
|
{
|
|
irq_counter++;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override void OnAPUClockSingle()
|
|
{
|
|
if (sound_channels != null)
|
|
{
|
|
for (int i = 0; i < sound_channels.Length; i++)
|
|
{
|
|
sound_channels[i].ClockSingle();
|
|
}
|
|
}
|
|
}
|
|
|
|
internal override double APUGetSample()
|
|
{
|
|
soundOut = 0.0;
|
|
sound_out_div = 0;
|
|
if (enabledChannels > 0)
|
|
{
|
|
for (int i = 0; i < sound_channels.Length; i++)
|
|
{
|
|
if (sound_channels[i].Enabled && sound_channels_enable[i])
|
|
{
|
|
if (sound_channels[i].clocks > 0)
|
|
{
|
|
sound_channels[i].output = sound_channels[i].output_av / sound_channels[i].clocks;
|
|
}
|
|
sound_channels[i].clocks = (sound_channels[i].output_av = 0);
|
|
soundOut += sound_channels[i].output;
|
|
sound_out_div++;
|
|
}
|
|
}
|
|
soundOut = soundOut / 8.0 / 225.0;
|
|
}
|
|
return soundOut;
|
|
}
|
|
|
|
internal override void APUApplyChannelsSettings()
|
|
{
|
|
base.APUApplyChannelsSettings();
|
|
sound_channels_enable[0] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT1;
|
|
sound_channels_enable[1] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT2;
|
|
sound_channels_enable[2] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT3;
|
|
sound_channels_enable[3] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT4;
|
|
sound_channels_enable[4] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT5;
|
|
sound_channels_enable[5] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT6;
|
|
sound_channels_enable[6] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT7;
|
|
sound_channels_enable[7] = MyNesMain.RendererSettings.Audio_ChannelEnabled_NMT8;
|
|
}
|
|
|
|
internal override void WriteStateData(ref BinaryWriter stream)
|
|
{
|
|
base.WriteStateData(ref stream);
|
|
stream.Write(irq_counter);
|
|
stream.Write(irq_enable);
|
|
stream.Write(disables_chr_ram_A);
|
|
stream.Write(disables_chr_ram_B);
|
|
stream.Write(enable_mirroring_switch);
|
|
stream.Write(enable_N106_sound);
|
|
stream.Write(temp_nmt);
|
|
if (enable_N106_sound)
|
|
{
|
|
for (int i = 0; i < sound_channels.Length; i++)
|
|
{
|
|
sound_channels[i].SaveState(stream);
|
|
}
|
|
}
|
|
stream.Write(soundReg);
|
|
stream.Write(enabledChannels);
|
|
stream.Write(enabledChannels1);
|
|
stream.Write(channelIndex);
|
|
stream.Write(temp_val);
|
|
stream.Write(temp_i);
|
|
stream.Write(EXRAM);
|
|
}
|
|
|
|
internal override void ReadStateData(ref BinaryReader stream)
|
|
{
|
|
base.ReadStateData(ref stream);
|
|
irq_counter = stream.ReadInt32();
|
|
irq_enable = stream.ReadBoolean();
|
|
disables_chr_ram_A = stream.ReadBoolean();
|
|
disables_chr_ram_B = stream.ReadBoolean();
|
|
enable_mirroring_switch = stream.ReadBoolean();
|
|
enable_N106_sound = stream.ReadBoolean();
|
|
temp_nmt = stream.ReadInt32();
|
|
if (enable_N106_sound)
|
|
{
|
|
for (int i = 0; i < sound_channels.Length; i++)
|
|
{
|
|
sound_channels[i].LoadState(stream);
|
|
}
|
|
}
|
|
soundReg = stream.ReadByte();
|
|
enabledChannels = stream.ReadInt32();
|
|
enabledChannels1 = stream.ReadInt32();
|
|
channelIndex = stream.ReadInt32();
|
|
temp_val = stream.ReadByte();
|
|
temp_i = stream.ReadByte();
|
|
stream.Read(EXRAM, 0, EXRAM.Length);
|
|
}
|
|
}
|
|
}
|