AxibugEmuOnline/AxibugEmuOnline.Client/Assets/MyNes.Core/Mapper005.cs
2024-07-03 18:22:22 +08:00

871 lines
23 KiB
C#

using System;
using System.IO;
namespace MyNes.Core
{
[BoardInfo("MMC5", 5, 8, 16)]
[WithExternalSound]
[HassIssues]
internal class Mapper005 : Board
{
private int ram_protectA;
private int ram_protectB;
private int ExRAM_mode;
private int[] CHROffset_spr;
private int[] CHROffsetEX;
private int[] CHROffsetSP;
private int[] chrRegA;
private int[] chrRegB;
private int[] prgReg;
private bool useSRAMmirroring;
private int chr_high;
private int chr_mode;
private int prg_mode;
private bool chr_setB_last;
private byte temp_val;
private byte temp_fill;
private int lastAccessVRAM;
private int paletteNo;
private int shift;
private int EXtilenumber;
private byte multiplicand;
private byte multiplier;
private ushort product;
private bool split_enable;
private bool split_right;
private int split_tile;
private int split_yscroll;
private bool split_doit;
private int split_watch_tile;
private byte irq_line;
private byte irq_enable;
private int irq_pending;
private int irq_current_counter;
private int irq_current_inframe;
private MMC5Sqr snd_1;
private MMC5Sqr snd_2;
private MMC5Pcm snd_3;
private double[] audio_pulse_table;
private double[] audio_tnd_table;
internal override string Issues => MNInterfaceLanguage.IssueMapper5;
internal override void Initialize(IRom rom)
{
base.Initialize(rom);
snd_1 = new MMC5Sqr();
snd_2 = new MMC5Sqr();
snd_3 = new MMC5Pcm();
audio_pulse_table = new double[32];
for (int i = 0; i < 32; i++)
{
audio_pulse_table[i] = 95.52 / (8128.0 / (double)i + 100.0);
}
audio_tnd_table = new double[204];
for (int j = 0; j < 204; j++)
{
audio_tnd_table[j] = 163.67 / (24329.0 / (double)j + 100.0);
}
}
internal override void HardReset()
{
base.HardReset();
switch (SHA1.ToUpper())
{
case "37267833C984F176DB4B0BC9D45DABA0FFF45304":
useSRAMmirroring = true;
break;
case "800AEFE756E85A0A78CCB4DAE68EBBA5DF24BF41":
useSRAMmirroring = true;
break;
}
Console.WriteLine("MMC5: using PRG RAM mirroring = " + useSRAMmirroring);
CHROffset_spr = new int[8];
CHROffsetEX = new int[8];
CHROffsetSP = new int[8];
chrRegA = new int[8];
chrRegB = new int[4];
prgReg = new int[4];
prgReg[3] = PRG_ROM_08KB_Mask;
prg_mode = 3;
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.Area8000);
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaA000);
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaC000);
Switch08KPRG(PRG_ROM_08KB_Mask, PRGArea.AreaE000);
Switch04kCHREX(0, 0);
Switch04kCHRSP(0, 0);
Switch08kCHR_spr(0);
TogglePRGRAMWritableEnable(enable: true);
TogglePRGRAMEnable(enable: true);
APUApplyChannelsSettings();
snd_1.HardReset();
snd_2.HardReset();
snd_3.HardReset();
}
internal override void SoftReset()
{
base.SoftReset();
snd_1.SoftReset();
snd_2.SoftReset();
snd_3.SoftReset();
}
internal override void WriteEX(ref ushort address, ref byte value)
{
if (address >= 23552)
{
if (ExRAM_mode == 2)
{
NMT_RAM[2][address & 0x3FF] = value;
}
else if (ExRAM_mode < 2)
{
if (irq_current_inframe == 64)
{
NMT_RAM[2][address & 0x3FF] = value;
}
else
{
NMT_RAM[2][address & 0x3FF] = 0;
}
}
return;
}
switch (address)
{
case 20480:
snd_1.Write0(ref value);
break;
case 20482:
snd_1.Write2(ref value);
break;
case 20483:
snd_1.Write3(ref value);
break;
case 20484:
snd_2.Write0(ref value);
break;
case 20486:
snd_2.Write2(ref value);
break;
case 20487:
snd_2.Write3(ref value);
break;
case 20496:
snd_3.Write5010(value);
break;
case 20497:
snd_3.Write5011(value);
break;
case 20501:
snd_1.WriteEnabled((value & 1) != 0);
snd_2.WriteEnabled((value & 2) != 0);
break;
case 20736:
prg_mode = value & 3;
break;
case 20737:
chr_mode = value & 3;
break;
case 20738:
ram_protectA = value & 3;
UpdateRamProtect();
break;
case 20739:
ram_protectB = value & 3;
UpdateRamProtect();
break;
case 20740:
ExRAM_mode = value & 3;
break;
case 20741:
Switch01KNMT(value);
break;
case 20755:
if (!useSRAMmirroring)
{
Switch08KPRG(value & 7, PRGArea.Area6000);
}
else
{
Switch08KPRG((value >> 2) & 1, PRGArea.Area6000);
}
break;
case 20756:
if (prg_mode == 3)
{
Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
Switch08KPRG(value & 0x7F, PRGArea.Area8000);
}
break;
case 20757:
switch (prg_mode)
{
case 1:
Toggle16KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
Switch16KPRG((value & 0x7F) >> 1, PRGArea.Area8000);
break;
case 2:
Toggle16KPRG_RAM((value & 0x80) == 0, PRGArea.Area8000);
Switch16KPRG((value & 0x7F) >> 1, PRGArea.Area8000);
break;
case 3:
Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.AreaA000);
Switch08KPRG(value & 0x7F, PRGArea.AreaA000);
break;
}
break;
case 20758:
{
int num = prg_mode;
if ((uint)(num - 2) <= 1u)
{
Toggle08KPRG_RAM((value & 0x80) == 0, PRGArea.AreaC000);
Switch08KPRG(value & 0x7F, PRGArea.AreaC000);
}
break;
}
case 20759:
switch (prg_mode)
{
case 0:
Switch32KPRG((value & 0x7C) >> 2, PRGArea.Area8000);
break;
case 1:
Switch16KPRG((value & 0x7F) >> 1, PRGArea.AreaC000);
break;
case 2:
Switch08KPRG(value & 0x7F, PRGArea.AreaE000);
break;
case 3:
Switch08KPRG(value & 0x7F, PRGArea.AreaE000);
break;
}
break;
case 20768:
chr_setB_last = false;
if (chr_mode == 3)
{
Switch01kCHR_spr(value | chr_high, 0);
}
break;
case 20769:
chr_setB_last = false;
switch (chr_mode)
{
case 2:
Switch02kCHR_spr(value | chr_high, 0);
break;
case 3:
Switch01kCHR_spr(value | chr_high, 1024);
break;
}
break;
case 20770:
chr_setB_last = false;
if (chr_mode == 3)
{
Switch01kCHR_spr(value | chr_high, 2048);
}
break;
case 20771:
chr_setB_last = false;
switch (chr_mode)
{
case 1:
Switch04kCHR_spr(value | chr_high, 0);
break;
case 2:
Switch02kCHR_spr(value | chr_high, 2048);
break;
case 3:
Switch01kCHR_spr(value | chr_high, 3072);
break;
}
break;
case 20772:
chr_setB_last = false;
if (chr_mode == 3)
{
Switch01kCHR_spr(value | chr_high, 4096);
}
break;
case 20773:
chr_setB_last = false;
switch (chr_mode)
{
case 2:
Switch02kCHR_spr(value | chr_high, 4096);
break;
case 3:
Switch01kCHR_spr(value | chr_high, 5120);
break;
}
break;
case 20774:
chr_setB_last = false;
if (chr_mode == 3)
{
Switch01kCHR_spr(value | chr_high, 6144);
}
break;
case 20775:
chr_setB_last = false;
switch (chr_mode)
{
case 0:
Switch08kCHR_spr(value | chr_high);
break;
case 1:
Switch04kCHR_spr(value | chr_high, 4096);
break;
case 2:
Switch02kCHR_spr(value | chr_high, 6144);
break;
case 3:
Switch01kCHR_spr(value | chr_high, 7168);
break;
}
break;
case 20776:
chr_setB_last = true;
if (chr_mode == 3)
{
Switch01KCHR(value | chr_high, CHRArea.Area0000);
Switch01KCHR(value | chr_high, CHRArea.Area1000);
}
break;
case 20777:
chr_setB_last = true;
switch (chr_mode)
{
case 2:
Switch02KCHR(value | chr_high, CHRArea.Area0000);
Switch02KCHR(value | chr_high, CHRArea.Area1000);
break;
case 3:
Switch01KCHR(value | chr_high, CHRArea.Area0400);
Switch01KCHR(value | chr_high, CHRArea.Area1400);
break;
}
break;
case 20778:
chr_setB_last = true;
if (chr_mode == 3)
{
Switch01KCHR(value | chr_high, CHRArea.Area0800);
Switch01KCHR(value | chr_high, CHRArea.Area1800);
}
break;
case 20779:
chr_setB_last = true;
switch (chr_mode)
{
case 0:
Switch04kCHR_bkg(value | chr_high, 0);
Switch04kCHR_bkg(value | chr_high, 4096);
break;
case 1:
Switch04KCHR(value | chr_high, CHRArea.Area0000);
Switch04KCHR(value | chr_high, CHRArea.Area1000);
break;
case 2:
Switch02KCHR(value | chr_high, CHRArea.Area0800);
Switch02KCHR(value | chr_high, CHRArea.Area1800);
break;
case 3:
Switch01KCHR(value | chr_high, CHRArea.Area0C00);
Switch01KCHR(value | chr_high, CHRArea.Area1C00);
break;
}
break;
case 20784:
chr_high = (value & 3) << 8;
break;
case 20742:
{
for (int j = 0; j < 960; j++)
{
NMT_RAM[3][j] = value;
}
break;
}
case 20743:
{
for (int i = 960; i < 1024; i++)
{
temp_fill = (byte)((uint)(2 << (value & 3)) | (value & 3u));
temp_fill |= (byte)((temp_fill & 0xF) << 4);
NMT_RAM[3][i] = temp_fill;
}
break;
}
case 20992:
split_tile = value & 0x1F;
split_enable = (value & 0x80) == 128;
split_right = (value & 0x40) == 64;
break;
case 20993:
split_yscroll = value;
break;
case 20994:
Switch04kCHRSP(value, address & 0);
Switch04kCHRSP(value, address & 0x1000);
break;
case 20995:
irq_line = value;
break;
case 20996:
irq_enable = value;
break;
case 20997:
multiplicand = value;
product = (ushort)(multiplicand * multiplier);
break;
case 20998:
multiplier = value;
product = (ushort)(multiplicand * multiplier);
break;
}
}
internal override void ReadEX(ref ushort address, out byte data)
{
if (address >= 23552 && ExRAM_mode >= 2)
{
data = NMT_RAM[2][address & 0x3FF];
return;
}
switch (address)
{
case 20496:
data = snd_3.Read5010();
break;
case 20996:
data = (byte)(irq_current_inframe | irq_pending);
irq_pending = 0;
NesEmu.IRQFlags &= -9;
break;
case 20997:
data = (byte)(product & 0xFFu);
break;
case 20998:
data = (byte)((product & 0xFF00) >> 8);
break;
case 20501:
data = (byte)((snd_1.ReadEnable() ? 1u : 0u) | (snd_2.ReadEnable() ? 2u : 0u));
data = 0;
break;
default:
data = 0;
break;
}
}
internal override void ReadCHR(ref ushort address, out byte data)
{
if (!NesEmu.ppu_is_sprfetch && split_enable && ExRAM_mode < 2)
{
split_watch_tile = address & 0x3F;
if (!split_right)
{
split_doit = split_watch_tile < split_tile;
}
else
{
split_doit = split_watch_tile >= split_tile;
}
_ = split_doit;
}
if (ExRAM_mode == 1)
{
if (!NesEmu.ppu_is_sprfetch)
{
EXtilenumber = NMT_RAM[2][lastAccessVRAM] & 0x3F;
Switch04kCHREX(EXtilenumber | chr_high, address & 0x1000);
data = CHR_ROM[CHROffsetEX[(address >> 10) & 7]][address & 0x3FF];
}
else
{
data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
}
}
else if (NesEmu.ppu_reg_2000_Sprite_size == 16)
{
if (!NesEmu.ppu_is_sprfetch)
{
data = CHR_ROM[CHR_AREA_BLK_INDEX[(address >> 10) & 7]][address & 0x3FF];
}
else
{
data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
}
}
else if (chr_setB_last)
{
data = CHR_ROM[CHR_AREA_BLK_INDEX[(address >> 10) & 7]][address & 0x3FF];
}
else
{
data = CHR_ROM[CHROffset_spr[(address >> 10) & 7]][address & 0x3FF];
}
}
internal override void ReadNMT(ref ushort address, out byte data)
{
_ = split_doit;
if (ExRAM_mode == 1)
{
if ((address & 0x3FF) <= 959)
{
lastAccessVRAM = address & 0x3FF;
}
else
{
paletteNo = NMT_RAM[2][lastAccessVRAM] & 0xC0;
shift = ((lastAccessVRAM >> 4) & 4) | (lastAccessVRAM & 2);
switch (shift)
{
case 0:
data = (byte)(paletteNo >> 6);
return;
case 2:
data = (byte)(paletteNo >> 4);
return;
case 4:
data = (byte)(paletteNo >> 2);
return;
case 6:
data = (byte)paletteNo;
return;
}
}
}
data = NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF];
}
internal override void WriteNMT(ref ushort address, ref byte value)
{
if (ExRAM_mode == 1 && (address & 0x3FF) <= 959)
{
lastAccessVRAM = address & 0x3FF;
}
NMT_RAM[NMT_AREA_BLK_INDEX[(address >> 10) & 3]][address & 0x3FF] = value;
}
private void UpdateRamProtect()
{
TogglePRGRAMWritableEnable(ram_protectA == 2 && ram_protectB == 1);
}
private void Switch04kCHR_bkg(int index, int where)
{
int num = (where >> 10) & 7;
index <<= 2;
CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHR_AREA_BLK_INDEX[num] = index & CHR_ROM_01KB_Mask;
}
private void Switch01kCHR_spr(int index, int where)
{
CHROffset_spr[(where >> 10) & 7] = index & CHR_ROM_01KB_Mask;
}
private void Switch02kCHR_spr(int index, int where)
{
int num = (where >> 10) & 7;
index <<= 1;
CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[num + 1] = index & CHR_ROM_01KB_Mask;
}
private void Switch04kCHR_spr(int index, int where)
{
int num = (where >> 10) & 7;
index <<= 2;
CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffset_spr[num] = index & CHR_ROM_01KB_Mask;
}
private void Switch08kCHR_spr(int index)
{
index <<= 3;
CHROffset_spr[0] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[1] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[2] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[3] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[4] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[5] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[6] = index & CHR_ROM_01KB_Mask;
index++;
CHROffset_spr[7] = index & CHR_ROM_01KB_Mask;
}
private void Switch04kCHREX(int index, int where)
{
int num = (where >> 10) & 7;
index <<= 2;
CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetEX[num] = index & CHR_ROM_01KB_Mask;
}
private void Switch04kCHRSP(int index, int where)
{
int num = (where >> 10) & 7;
index <<= 2;
CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
num++;
index++;
CHROffsetSP[num] = index & CHR_ROM_01KB_Mask;
}
internal override void OnPPUScanlineTick()
{
irq_current_inframe = ((NesEmu.IsInRender() && NesEmu.IsRenderingOn()) ? 64 : 0);
if (irq_current_inframe == 0)
{
irq_current_inframe = 64;
irq_current_counter = 0;
irq_pending = 0;
NesEmu.IRQFlags &= -9;
return;
}
irq_current_counter++;
if (irq_current_counter == irq_line)
{
irq_pending = 128;
if (irq_enable == 128)
{
NesEmu.IRQFlags |= 8;
}
}
}
internal override void OnAPUClock()
{
base.OnAPUClock();
snd_1.Clock();
snd_2.Clock();
}
internal override void OnAPUClockEnvelope()
{
base.OnAPUClockEnvelope();
snd_1.ClockLength();
snd_2.ClockLength();
snd_1.ClockEnvelope();
snd_2.ClockEnvelope();
}
internal override double APUGetSample()
{
return audio_pulse_table[snd_1.output + snd_2.output] + audio_tnd_table[snd_3.output];
}
internal override void APUApplyChannelsSettings()
{
base.APUApplyChannelsSettings();
snd_1.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_SQ1;
snd_2.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_SQ2;
snd_3.Outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_MMC5_PCM;
}
internal override void WriteStateData(ref BinaryWriter stream)
{
base.WriteStateData(ref stream);
stream.Write(ram_protectA);
stream.Write(ram_protectB);
stream.Write(ExRAM_mode);
for (int i = 0; i < CHROffset_spr.Length; i++)
{
stream.Write(CHROffset_spr[i]);
}
for (int j = 0; j < CHROffsetEX.Length; j++)
{
stream.Write(CHROffsetEX[j]);
}
for (int k = 0; k < CHROffsetSP.Length; k++)
{
stream.Write(CHROffsetSP[k]);
}
for (int l = 0; l < chrRegA.Length; l++)
{
stream.Write(chrRegA[l]);
}
for (int m = 0; m < chrRegB.Length; m++)
{
stream.Write(chrRegB[m]);
}
for (int n = 0; n < prgReg.Length; n++)
{
stream.Write(prgReg[n]);
}
stream.Write(useSRAMmirroring);
stream.Write(chr_high);
stream.Write(chr_mode);
stream.Write(prg_mode);
stream.Write(chr_setB_last);
stream.Write(temp_val);
stream.Write(temp_fill);
stream.Write(lastAccessVRAM);
stream.Write(paletteNo);
stream.Write(shift);
stream.Write(EXtilenumber);
stream.Write(multiplicand);
stream.Write(multiplier);
stream.Write(product);
stream.Write(split_enable);
stream.Write(split_right);
stream.Write(split_tile);
stream.Write(split_yscroll);
stream.Write(split_doit);
stream.Write(split_watch_tile);
stream.Write(irq_line);
stream.Write(irq_enable);
stream.Write(irq_pending);
stream.Write(irq_current_counter);
stream.Write(irq_current_inframe);
snd_1.WriteStateData(ref stream);
snd_2.WriteStateData(ref stream);
snd_3.SaveState(ref stream);
}
internal override void ReadStateData(ref BinaryReader stream)
{
base.ReadStateData(ref stream);
ram_protectA = stream.ReadInt32();
ram_protectB = stream.ReadInt32();
ExRAM_mode = stream.ReadInt32();
for (int i = 0; i < CHROffset_spr.Length; i++)
{
CHROffset_spr[i] = stream.ReadInt32();
}
for (int j = 0; j < CHROffsetEX.Length; j++)
{
CHROffsetEX[j] = stream.ReadInt32();
}
for (int k = 0; k < CHROffsetSP.Length; k++)
{
CHROffsetSP[k] = stream.ReadInt32();
}
for (int l = 0; l < chrRegA.Length; l++)
{
chrRegA[l] = stream.ReadInt32();
}
for (int m = 0; m < chrRegB.Length; m++)
{
chrRegB[m] = stream.ReadInt32();
}
for (int n = 0; n < prgReg.Length; n++)
{
prgReg[n] = stream.ReadInt32();
}
useSRAMmirroring = stream.ReadBoolean();
chr_high = stream.ReadInt32();
chr_mode = stream.ReadInt32();
prg_mode = stream.ReadInt32();
chr_setB_last = stream.ReadBoolean();
temp_val = stream.ReadByte();
temp_fill = stream.ReadByte();
lastAccessVRAM = stream.ReadInt32();
paletteNo = stream.ReadInt32();
shift = stream.ReadInt32();
EXtilenumber = stream.ReadInt32();
multiplicand = stream.ReadByte();
multiplier = stream.ReadByte();
product = stream.ReadUInt16();
split_enable = stream.ReadBoolean();
split_right = stream.ReadBoolean();
split_tile = stream.ReadInt32();
split_yscroll = stream.ReadInt32();
split_doit = stream.ReadBoolean();
split_watch_tile = stream.ReadInt32();
irq_line = stream.ReadByte();
irq_enable = stream.ReadByte();
irq_pending = stream.ReadInt32();
irq_current_counter = stream.ReadInt32();
irq_current_inframe = stream.ReadInt32();
snd_1.ReadStateData(ref stream);
snd_2.ReadStateData(ref stream);
snd_3.LoadState(ref stream);
}
}
}