diff --git a/AxibugEmuOnline.sln b/AxibugEmuOnline.sln index be6aecd..43bce0f 100644 --- a/AxibugEmuOnline.sln +++ b/AxibugEmuOnline.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Web", "Axib EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AxibugEmuOnline.Server", "AxibugEmuOnline.Server\AxibugEmuOnline.Server.csproj", "{9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VirtualNes.Core", "VirtualNes.Core\VirtualNes.Core.csproj", "{8A4771D6-74B9-453C-9932-6B774280E325}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Debug|Any CPU.Build.0 = Debug|Any CPU {9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.ActiveCfg = Release|Any CPU {9F509DB4-CDB4-4CDB-9C10-805C5E644EEF}.Release|Any CPU.Build.0 = Release|Any CPU + {8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A4771D6-74B9-453C-9932-6B774280E325}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A4771D6-74B9-453C-9932-6B774280E325}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Core/VirtualNes.Core/APU.cs b/Core/VirtualNes.Core/APU.cs new file mode 100644 index 0000000..b0e608a --- /dev/null +++ b/Core/VirtualNes.Core/APU.cs @@ -0,0 +1,655 @@ + +using System; +using VirtualNes.Core.Debug; + +namespace VirtualNes.Core +{ + public class APU + { + public const uint QUEUE_LENGTH = 8192; + + // Volume adjust + // Internal sounds + public const uint RECTANGLE_VOL = 0x0F0; + public const uint TRIANGLE_VOL = 0x130; + public const uint NOISE_VOL = 0x0C0; + public const uint DPCM_VOL = 0x0F0; + // Extra sounds + public const uint VRC6_VOL = 0x0F0; + public const uint VRC7_VOL = 0x130; + public const uint FDS_VOL = 0x0F0; + public const uint MMC5_VOL = 0x0F0; + public const uint N106_VOL = 0x088; + public const uint FME7_VOL = 0x130; + + private NES nes; + private byte exsound_select; + private APU_INTERNAL @internal = new APU_INTERNAL(); + private APU_VRC6 vrc6 = new APU_VRC6(); + private APU_VRC7 vrc7 = new APU_VRC7(); + private APU_MMC5 mmc5 = new APU_MMC5(); + private APU_FDS fds = new APU_FDS(); + private APU_N106 n106 = new APU_N106(); + private APU_FME7 fme7 = new APU_FME7(); + private int last_data; + private int last_diff; + protected short[] m_SoundBuffer = new short[256]; + protected int[] lowpass_filter = new int[4]; + protected QUEUE queue = new QUEUE(); + protected QUEUE exqueue = new QUEUE(); + protected bool[] m_bMute = new bool[16]; + protected double elapsed_time; + + public APU(NES parent) + { + exsound_select = 0; + + nes = parent; + @internal.SetParent(parent); + + last_data = last_diff = 0; + + Array.Clear(m_SoundBuffer, 0, m_SoundBuffer.Length); + Array.Clear(lowpass_filter, 0, lowpass_filter.Length); + + for (int i = 0; i < m_bMute.Length; i++) + m_bMute[i] = true; + } + + public void Dispose() + { + @internal.Dispose(); + vrc6.Dispose(); + vrc7.Dispose(); + mmc5.Dispose(); + fds.Dispose(); + n106.Dispose(); + fme7.Dispose(); + } + + private int[] vol = new int[24]; + static double cutofftemp = (2.0 * 3.141592653579 * 40.0); + static double tmp = 0.0; + + public void Process(ISoundDataBuffer lpBuffer, uint dwSize) + { + int nBits = Supporter.Config.sound.nBits; + uint dwLength = (uint)(dwSize / (nBits / 8)); + int output; + QUEUEDATA q = new QUEUEDATA(); + uint writetime; + + var pSoundBuf = m_SoundBuffer; + int nCcount = 0; + + int nFilterType = Supporter.Config.sound.nFilterType; + + if (!Supporter.Config.sound.bEnable) + { + byte empty = (byte)(Supporter.Config.sound.nRate == 8 ? 128 : 0); + for (int i = 0; i < dwSize; i++) + lpBuffer.WriteByte(empty); + return; + } + + // Volume setup + // 0:Master + // 1:Rectangle 1 + // 2:Rectangle 2 + // 3:Triangle + // 4:Noise + // 5:DPCM + // 6:VRC6 + // 7:VRC7 + // 8:FDS + // 9:MMC5 + // 10:N106 + // 11:FME7 + MemoryUtility.ZEROMEMORY(vol, vol.Length); + + var bMute = m_bMute; + var nVolume = Supporter.Config.sound.nVolume; + + int nMasterVolume = bMute[0] ? nVolume[0] : 0; + + // Internal + vol[0] = (int)(bMute[1] ? (RECTANGLE_VOL * nVolume[1] * nMasterVolume) / (100 * 100) : 0); + vol[1] = (int)(bMute[2] ? (RECTANGLE_VOL * nVolume[2] * nMasterVolume) / (100 * 100) : 0); + vol[2] = (int)(bMute[3] ? (TRIANGLE_VOL * nVolume[3] * nMasterVolume) / (100 * 100) : 0); + vol[3] = (int)(bMute[4] ? (NOISE_VOL * nVolume[4] * nMasterVolume) / (100 * 100) : 0); + vol[4] = (int)(bMute[5] ? (DPCM_VOL * nVolume[5] * nMasterVolume) / (100 * 100) : 0); + + // VRC6 + vol[5] = (int)(bMute[6] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0); + vol[6] = (int)(bMute[7] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0); + vol[7] = (int)(bMute[8] ? (VRC6_VOL * nVolume[6] * nMasterVolume) / (100 * 100) : 0); + + // VRC7 + vol[8] = (int)(bMute[6] ? (VRC7_VOL * nVolume[7] * nMasterVolume) / (100 * 100) : 0); + + // FDS + vol[9] = (int)(bMute[6] ? (FDS_VOL * nVolume[8] * nMasterVolume) / (100 * 100) : 0); + + // MMC5 + vol[10] = (int)(bMute[6] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0); + vol[11] = (int)(bMute[7] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0); + vol[12] = (int)(bMute[8] ? (MMC5_VOL * nVolume[9] * nMasterVolume) / (100 * 100) : 0); + + // N106 + vol[13] = (int)(bMute[6] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[14] = (int)(bMute[7] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[15] = (int)(bMute[8] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[16] = (int)(bMute[9] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[17] = (int)(bMute[10] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[18] = (int)(bMute[11] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[19] = (int)(bMute[12] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + vol[20] = (int)(bMute[13] ? (N106_VOL * nVolume[10] * nMasterVolume) / (100 * 100) : 0); + + // FME7 + vol[21] = (int)(bMute[6] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0); + vol[22] = (int)(bMute[7] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0); + vol[23] = (int)(bMute[8] ? (FME7_VOL * nVolume[11] * nMasterVolume) / (100 * 100) : 0); + + // double cycle_rate = ((double)FRAME_CYCLES*60.0/12.0)/(double)Config.sound.nRate; + double cycle_rate = (nes.nescfg.FrameCycles * 60.0 / 12.0) / Supporter.Config.sound.nRate; + + // CPUサイクル数がループしてしまった時の対策処理 + if (elapsed_time > nes.cpu.GetTotalCycles()) + { + QueueFlush(); + } + + while ((dwLength--) != 0) + { + writetime = (uint)elapsed_time; + + while (GetQueue((int)writetime, ref q)) + { + WriteProcess(q.addr, q.data); + } + + while (GetExQueue((int)writetime, ref q)) + { + WriteExProcess(q.addr, q.data); + } + + // 0-4:internal 5-7:VRC6 8:VRC7 9:FDS 10-12:MMC5 13-20:N106 21-23:FME7 + output = 0; + output += @internal.Process(0) * vol[0]; + output += @internal.Process(1) * vol[1]; + output += @internal.Process(2) * vol[2]; + output += @internal.Process(3) * vol[3]; + output += @internal.Process(4) * vol[4]; + + if ((exsound_select & 0x01) != 0) + { + output += vrc6.Process(0) * vol[5]; + output += vrc6.Process(1) * vol[6]; + output += vrc6.Process(2) * vol[7]; + } + if ((exsound_select & 0x02) != 0) + { + output += vrc7.Process(0) * vol[8]; + } + if ((exsound_select & 0x04) != 0) + { + output += fds.Process(0) * vol[9]; + } + if ((exsound_select & 0x08) != 0) + { + output += mmc5.Process(0) * vol[10]; + output += mmc5.Process(1) * vol[11]; + output += mmc5.Process(2) * vol[12]; + } + if ((exsound_select & 0x10) != 0) + { + output += n106.Process(0) * vol[13]; + output += n106.Process(1) * vol[14]; + output += n106.Process(2) * vol[15]; + output += n106.Process(3) * vol[16]; + output += n106.Process(4) * vol[17]; + output += n106.Process(5) * vol[18]; + output += n106.Process(6) * vol[19]; + output += n106.Process(7) * vol[20]; + } + if ((exsound_select & 0x20) != 0) + { + fme7.Process(3); // Envelope & Noise + output += fme7.Process(0) * vol[21]; + output += fme7.Process(1) * vol[22]; + output += fme7.Process(2) * vol[23]; + } + + output >>= 8; + + if (nFilterType == 1) + { + //ローパスフィルターTYPE 1(Simple) + output = (lowpass_filter[0] + output) / 2; + lowpass_filter[0] = output; + } + else if (nFilterType == 2) + { + //ローパスフィルターTYPE 2(Weighted type 1) + output = (lowpass_filter[1] + lowpass_filter[0] + output) / 3; + lowpass_filter[1] = lowpass_filter[0]; + lowpass_filter[0] = output; + } + else if (nFilterType == 3) + { + //ローパスフィルターTYPE 3(Weighted type 2) + output = (lowpass_filter[2] + lowpass_filter[1] + lowpass_filter[0] + output) / 4; + lowpass_filter[2] = lowpass_filter[1]; + lowpass_filter[1] = lowpass_filter[0]; + lowpass_filter[0] = output; + } + else if (nFilterType == 4) + { + //ローパスフィルターTYPE 4(Weighted type 3) + output = (lowpass_filter[1] + lowpass_filter[0] * 2 + output) / 4; + lowpass_filter[1] = lowpass_filter[0]; + lowpass_filter[0] = output; + } + // DC成分のカット(HPF TEST) + { + // static double cutoff = (2.0*3.141592653579*40.0/44100.0); + double cutoff = cutofftemp / Supporter.Config.sound.nRate; + double @in, @out; + + @in = output; + @out = (@in - tmp); + tmp = tmp + cutoff * @out; + + output = (int)@out; + } + + // Limit + if (output > 0x7FFF) + { + output = 0x7FFF; + } + else if (output < -0x8000) + { + output = -0x8000; + } + + if (nBits != 8) + { + byte highByte = (byte)(output >> 8); // 获取高8位 + byte lowByte = (byte)(output & 0xFF); // 获取低8位 + lpBuffer.WriteByte(highByte); + lpBuffer.WriteByte(lowByte); + } + else + { + lpBuffer.WriteByte((byte)((output >> 8) ^ 0x80)); + } + + if (nCcount < 0x0100) + pSoundBuf[nCcount++] = (short)output; + + // elapsedtime += cycle_rate; + elapsed_time += cycle_rate; + } + + + if (elapsed_time > ((nes.nescfg.FrameCycles / 24) + nes.cpu.GetTotalCycles())) + { + elapsed_time = nes.cpu.GetTotalCycles(); + } + if ((elapsed_time + (nes.nescfg.FrameCycles / 6)) < nes.cpu.GetTotalCycles()) + { + elapsed_time = nes.cpu.GetTotalCycles(); + } + } + + private bool GetExQueue(int writetime, ref QUEUEDATA ret) + { + if (exqueue.wrptr == exqueue.rdptr) + { + return false; + } + if (exqueue.data[exqueue.rdptr].time <= writetime) + { + ret = exqueue.data[exqueue.rdptr]; + exqueue.rdptr++; + exqueue.rdptr = (int)(exqueue.rdptr & (QUEUE_LENGTH - 1)); + return true; + } + return false; + } + + private void QueueFlush() + { + while (queue.wrptr != queue.rdptr) + { + WriteProcess(queue.data[queue.rdptr].addr, queue.data[queue.rdptr].data); + queue.rdptr++; + queue.rdptr = (int)(queue.rdptr & (QUEUE_LENGTH - 1)); + } + + while (exqueue.wrptr != exqueue.rdptr) + { + WriteExProcess(exqueue.data[exqueue.rdptr].addr, exqueue.data[exqueue.rdptr].data); + exqueue.rdptr++; + exqueue.rdptr = (int)(exqueue.rdptr & (QUEUE_LENGTH - 1)); + } + } + + private void WriteExProcess(ushort addr, byte data) + { + if ((exsound_select & 0x01) != 0) + { + vrc6.Write(addr, data); + } + if ((exsound_select & 0x02) != 0) + { + vrc7.Write(addr, data); + } + if ((exsound_select & 0x04) != 0) + { + fds.Write(addr, data); + } + if ((exsound_select & 0x08) != 0) + { + mmc5.Write(addr, data); + } + if ((exsound_select & 0x10) != 0) + { + if (addr == 0x0000) + { + byte dummy = n106.Read(addr); + } + else + { + n106.Write(addr, data); + } + } + if ((exsound_select & 0x20) != 0) + { + fme7.Write(addr, data); + } + } + + private void WriteProcess(ushort addr, byte data) + { + // $4018はVirtuaNES固有ポート + if (addr >= 0x4000 && addr <= 0x401F) + { + @internal.Write(addr, data); + } + } + + internal void SyncDPCM(int cycles) + { + @internal.Sync(cycles); + } + + internal byte Read(ushort addr) + { + return @internal.SyncRead(addr); + } + + internal void Write(ushort addr, byte data) + { + // $4018偼VirtuaNES屌桳億乕僩 + if (addr >= 0x4000 && addr <= 0x401F) + { + @internal.SyncWrite(addr, data); + SetQueue(nes.cpu.GetTotalCycles(), addr, data); + } + } + + private void SetQueue(int writetime, ushort addr, byte data) + { + queue.data[queue.wrptr].time = writetime; + queue.data[queue.wrptr].addr = addr; + queue.data[queue.wrptr].data = data; + queue.wrptr++; + + var newwrptr = (int)(queue.wrptr & (QUEUE_LENGTH - 1)); + queue.wrptr = newwrptr; + + if (queue.wrptr == queue.rdptr) + { + Debuger.LogError("queue overflow."); + } + } + + private bool GetQueue(int writetime, ref QUEUEDATA ret) + { + if (queue.wrptr == queue.rdptr) + { + return false; + } + if (queue.data[queue.rdptr].time <= writetime) + { + ret = queue.data[queue.rdptr]; + queue.rdptr++; + var newrdptr = (int)(queue.rdptr & (QUEUE_LENGTH - 1)); + queue.rdptr = newrdptr; + return true; + } + return false; + } + + public void SoundSetup() + { + float fClock = nes.nescfg.CpuClock; + int nRate = Supporter.Config.sound.nRate; + + @internal.Setup(fClock, nRate); + vrc6.Setup(fClock, nRate); + vrc7.Setup(fClock, nRate); + mmc5.Setup(fClock, nRate); + fds.Setup(fClock, nRate); + n106.Setup(fClock, nRate); + fme7.Setup(fClock, nRate); + } + + internal void SelectExSound(byte data) + { + exsound_select = data; + } + + internal void Reset() + { + queue = new QUEUE(); + exqueue = new QUEUE(); + + elapsed_time = 0; + + float fClock = nes.nescfg.CpuClock; + int nRate = Supporter.Config.sound.nRate; + + @internal.Reset(fClock, nRate); + vrc6.Reset(fClock, nRate); + vrc7.Reset(fClock, nRate); + mmc5.Reset(fClock, nRate); + fds.Reset(fClock, nRate); + n106.Reset(fClock, nRate); + fme7.Reset(fClock, nRate); + + SoundSetup(); + } + + internal void ExWrite(ushort addr, byte data) + { + SetExQueue(nes.cpu.GetTotalCycles(), addr, data); + + if ((exsound_select & 0x04) != 0) + { + if (addr >= 0x4040 && addr < 0x4100) + { + fds.SyncWrite(addr, data); + } + } + + if ((exsound_select & 0x08) != 0) + { + if (addr >= 0x5000 && addr <= 0x5015) + { + mmc5.SyncWrite(addr, data); + } + } + } + + private void SetExQueue(int writetime, ushort addr, byte data) + { + exqueue.data[exqueue.wrptr].time = writetime; + exqueue.data[exqueue.wrptr].addr = addr; + exqueue.data[exqueue.wrptr].data = data; + exqueue.wrptr++; + var temp = QUEUE_LENGTH - 1; + exqueue.wrptr = (int)(exqueue.wrptr & temp); + if (exqueue.wrptr == exqueue.rdptr) + { + Debuger.LogError("exqueue overflow."); + } + } + + internal byte ExRead(ushort addr) + { + byte data = 0; + + if ((exsound_select & 0x10) != 0) + { + if (addr == 0x4800) + { + SetExQueue(nes.cpu.GetTotalCycles(), 0, 0); + } + } + if ((exsound_select & 0x04) != 0) + { + if (addr >= 0x4040 && addr < 0x4100) + { + data = fds.SyncRead(addr); + } + } + if ((exsound_select & 0x08) != 0) + { + if (addr >= 0x5000 && addr <= 0x5015) + { + data = mmc5.SyncRead(addr); + } + } + + return data; + } + + internal void GetFrameIRQ(ref int Cycle, ref byte Count, ref byte Type, ref byte IRQ, ref byte Occur) + { + @internal.GetFrameIRQ(ref Cycle, ref Count, ref Type, ref IRQ, ref Occur); + } + + internal void SetFrameIRQ(int Cycle, byte Count, byte Type, byte IRQ, byte Occur) + { + @internal.SetFrameIRQ(Cycle, Count, Type, IRQ, Occur); + } + + internal void SaveState(StateBuffer buffer) + { + // 時間軸を同期させる為Flushする + QueueFlush(); + + @internal.SaveState(buffer); + buffer.Position += (@internal.GetSize() + 15) & (~0x0F); + + // VRC6 + if ((exsound_select & 0x01) != 0) + { + vrc6.SaveState(buffer); + buffer.Position += (vrc6.GetSize() + 15) & (~0x0F); // Padding + } + // VRC7 (not support) + if ((exsound_select & 0x02) != 0) + { + vrc7.SaveState(buffer); + buffer.Position += (vrc7.GetSize() + 15) & (~0x0F); // Padding + } + // FDS + if ((exsound_select & 0x04) != 0) + { + fds.SaveState(buffer); + buffer.Position += (fds.GetSize() + 15) & (~0x0F); // Padding + + } + // MMC5 + if ((exsound_select & 0x08) != 0) + { + mmc5.SaveState(buffer); + buffer.Position += (mmc5.GetSize() + 15) & (~0x0F); // Padding + } + // N106 + if ((exsound_select & 0x10) != 0) + { + n106.SaveState(buffer); + buffer.Position += (n106.GetSize() + 15) & (~0x0F); // Padding + } + // FME7 + if ((exsound_select & 0x20) != 0) + { + fme7.SaveState(buffer); + buffer.Position += (fme7.GetSize() + 15) & (~0x0F); // Padding + } + } + + internal void LoadState(StateReader buffer) + { + @internal.LoadState(buffer); + buffer.Skip((@internal.GetSize() + 15) & (~0x0F)); + + // VRC6 + if ((exsound_select & 0x01) != 0) + { + vrc6.LoadState(buffer); + buffer.Skip((int)((vrc6.GetSize() + 15) & (~0x0F))); // Padding + } + // VRC7 (not support) + if ((exsound_select & 0x02) != 0) + { + vrc7.LoadState(buffer); + buffer.Skip((vrc7.GetSize() + 15) & (~0x0F)); // Padding + } + // FDS + if ((exsound_select & 0x04) != 0) + { + fds.LoadState(buffer); + buffer.Skip((fds.GetSize() + 15) & (~0x0F)); // Padding + } + // MMC5 + if ((exsound_select & 0x08) != 0) + { + mmc5.LoadState(buffer); + buffer.Skip((mmc5.GetSize() + 15) & (~0x0F)); // Padding + } + // N106 + if ((exsound_select & 0x10) != 0) + { + n106.LoadState(buffer); + buffer.Skip((n106.GetSize() + 15) & (~0x0F)); // Padding + } + // FME7 + if ((exsound_select & 0x20) != 0) + { + fme7.LoadState(buffer); + buffer.Skip((fme7.GetSize() + 15) & (~0x0F)); // Padding + } + } + } + + public struct QUEUEDATA + { + public int time; + public ushort addr; + public byte data; + public byte reserved; + } + + public class QUEUE + { + public int rdptr; + public int wrptr; + public QUEUEDATA[] data = new QUEUEDATA[8192]; + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_FDS.cs b/Core/VirtualNes.Core/ApuEX/APU_FDS.cs new file mode 100644 index 0000000..bb44ab2 --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_FDS.cs @@ -0,0 +1,548 @@ +using System; + +namespace VirtualNes.Core +{ + public class APU_FDS : APU_INTERFACE + { + FDSSOUND fds = new FDSSOUND(); + FDSSOUND fds_sync = new FDSSOUND(); + int[] output_buf = new int[8]; + int sampling_rate; + + public APU_FDS() + { + fds.ZeroMemory(); + fds_sync.ZeroMemory(); + + Array.Clear(output_buf, 0, output_buf.Length); + + sampling_rate = 22050; + } + + public override void Reset(float fClock, int nRate) + { + fds.ZeroMemory(); + fds_sync.ZeroMemory(); + + sampling_rate = 22050; + } + + public override void Setup(float fClock, int nRate) + { + sampling_rate = nRate; + } + + int[] tbl_writesub = { 30, 20, 15, 12 }; + + private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate) + { + if (addr < 0x4040 || addr > 0x40BF) + return; + + ch.reg[addr - 0x4040] = data; + if (addr >= 0x4040 && addr <= 0x407F) + { + if (ch.wave_setup != 0) + { + ch.main_wavetable[addr - 0x4040] = 0x20 - (data & 0x3F); + } + } + else + { + switch (addr) + { + case 0x4080: // Volume Envelope + ch.volenv_mode = (byte)(data >> 6); + if ((data & 0x80) != 0) + { + ch.volenv_gain = (byte)(data & 0x3F); + + // 即時反映 + if (ch.main_addr == 0) + { + ch.now_volume = (ch.volenv_gain < 0x21) ? ch.volenv_gain : 0x20; + } + } + // エンベロープ1段階の演算 + ch.volenv_decay = (byte)(data & 0x3F); + ch.volenv_phaseacc = (double)ch.envelope_speed * (double)(ch.volenv_decay + 1) * rate / (232.0 * 960.0); + break; + + case 0x4082: // Main Frequency(Low) + ch.main_frequency = (ch.main_frequency & ~0x00FF) | data; + break; + case 0x4083: // Main Frequency(High) + ch.main_enable = (byte)((~data) & (1 << 7)); + ch.envelope_enable = (byte)((~data) & (1 << 6)); + if (ch.main_enable == 0) + { + ch.main_addr = 0; + ch.now_volume = (ch.volenv_gain < 0x21) ? ch.volenv_gain : 0x20; + } + // ch.main_frequency = (ch.main_frequency&0x00FF)|(((INT)data&0x3F)<<8); + ch.main_frequency = (ch.main_frequency & 0x00FF) | ((data & 0x0F) << 8); + break; + + case 0x4084: // Sweep Envelope + ch.swpenv_mode = (byte)(data >> 6); + if ((data & 0x80) != 0) + { + ch.swpenv_gain = (byte)(data & 0x3F); + } + // エンベロープ1段階の演算 + ch.swpenv_decay = (byte)(data & 0x3F); + ch.swpenv_phaseacc = (double)ch.envelope_speed * (double)(ch.swpenv_decay + 1) * rate / (232.0 * 960.0); + break; + + case 0x4085: // Sweep Bias + if ((data & 0x40) != 0) ch.sweep_bias = (data & 0x3f) - 0x40; + else ch.sweep_bias = data & 0x3f; + ch.lfo_addr = 0; + break; + + case 0x4086: // Effector(LFO) Frequency(Low) + ch.lfo_frequency = (ch.lfo_frequency & (~0x00FF)) | data; + break; + case 0x4087: // Effector(LFO) Frequency(High) + ch.lfo_enable = (byte)((~data & 0x80)); + ch.lfo_frequency = (ch.lfo_frequency & 0x00FF) | ((data & 0x0F) << 8); + break; + + case 0x4088: // Effector(LFO) wavetable + if (ch.lfo_enable == 0) + { + // FIFO? + for (byte i = 0; i < 31; i++) + { + ch.lfo_wavetable[i * 2 + 0] = ch.lfo_wavetable[(i + 1) * 2 + 0]; + ch.lfo_wavetable[i * 2 + 1] = ch.lfo_wavetable[(i + 1) * 2 + 1]; + } + ch.lfo_wavetable[31 * 2 + 0] = (byte)(data & 0x07); + ch.lfo_wavetable[31 * 2 + 1] = (byte)(data & 0x07); + } + break; + + case 0x4089: // Sound control + { + ch.master_volume = tbl_writesub[data & 3]; + ch.wave_setup = (byte)(data & 0x80); + } + break; + + case 0x408A: // Sound control 2 + ch.envelope_speed = data; + break; + + default: + break; + } + } + } + + public override void Write(ushort addr, byte data) + { + WriteSub(addr, data, fds, sampling_rate); + } + + public override byte Read(ushort addr) + { + byte data = (byte)(addr >> 8); + + if (addr >= 0x4040 && addr <= 0x407F) + { + data = (byte)(fds.main_wavetable[addr & 0x3F] | 0x40); + } + else + if (addr == 0x4090) + { + data = (byte)((fds.volenv_gain & 0x3F) | 0x40); + } + else + if (addr == 0x4092) + { + data = (byte)((fds.swpenv_gain & 0x3F) | 0x40); + } + + return data; + } + + int[] tbl_process = { 0, 1, 2, 4, 0, -4, -2, -1 }; + public override int Process(int channel) + { + // Envelope unit + if (fds.envelope_enable != 0 && fds.envelope_speed != 0) + { + // Volume envelope + if (fds.volenv_mode < 2) + { + double decay = ((double)fds.envelope_speed * (double)(fds.volenv_decay + 1) * (double)sampling_rate) / (232.0 * 960.0); + fds.volenv_phaseacc -= 1.0; + while (fds.volenv_phaseacc < 0.0) + { + fds.volenv_phaseacc += decay; + + if (fds.volenv_mode == 0) + { + // 減少モード + if (fds.volenv_gain != 0) + fds.volenv_gain--; + } + else + if (fds.volenv_mode == 1) + { + if (fds.volenv_gain < 0x20) + fds.volenv_gain++; + } + } + } + + // Sweep envelope + if (fds.swpenv_mode < 2) + { + double decay = ((double)fds.envelope_speed * (double)(fds.swpenv_decay + 1) * (double)sampling_rate) / (232.0 * 960.0); + fds.swpenv_phaseacc -= 1.0; + while (fds.swpenv_phaseacc < 0.0) + { + fds.swpenv_phaseacc += decay; + + if (fds.swpenv_mode == 0) + { + // 減少モード + if (fds.swpenv_gain != 0) + fds.swpenv_gain--; + } + else + if (fds.swpenv_mode == 1) + { + if (fds.swpenv_gain < 0x20) + fds.swpenv_gain++; + } + } + } + } + + // Effector(LFO) unit + int sub_freq = 0; + // if( fds.lfo_enable && fds.envelope_speed && fds.lfo_frequency ) { + if (fds.lfo_enable != 0) + { + if (fds.lfo_frequency != 0) + { + fds.lfo_phaseacc -= (1789772.5 * (double)fds.lfo_frequency) / 65536.0; + while (fds.lfo_phaseacc < 0.0) + { + fds.lfo_phaseacc += (double)sampling_rate; + + if (fds.lfo_wavetable[fds.lfo_addr] == 4) + fds.sweep_bias = 0; + else + fds.sweep_bias += tbl_process[fds.lfo_wavetable[fds.lfo_addr]]; + + fds.lfo_addr = (fds.lfo_addr + 1) & 63; + } + } + + if (fds.sweep_bias > 63) + fds.sweep_bias -= 128; + else if (fds.sweep_bias < -64) + fds.sweep_bias += 128; + + int sub_multi = fds.sweep_bias * fds.swpenv_gain; + + if ((sub_multi & 0x0F) != 0) + { + // 16で割り切れない場合 + sub_multi = (sub_multi / 16); + if (fds.sweep_bias >= 0) + sub_multi += 2; // 正の場合 + else + sub_multi -= 1; // 負の場合 + } + else + { + // 16で割り切れる場合 + sub_multi = (sub_multi / 16); + } + // 193を超えると-258する(-64へラップ) + if (sub_multi > 193) + sub_multi -= 258; + // -64を下回ると+256する(192へラップ) + if (sub_multi < -64) + sub_multi += 256; + + sub_freq = (fds.main_frequency) * sub_multi / 64; + } + + // Main unit + int output = 0; + if (fds.main_enable != 0 && fds.main_frequency != 0 && fds.wave_setup == 0) + { + int freq; + int main_addr_old = fds.main_addr; + + freq = (int)((fds.main_frequency + sub_freq) * 1789772.5 / 65536.0); + + fds.main_addr = (fds.main_addr + freq + 64 * sampling_rate) % (64 * sampling_rate); + + // 1周期を超えたらボリューム更新 + if (main_addr_old > fds.main_addr) + fds.now_volume = (fds.volenv_gain < 0x21) ? fds.volenv_gain : 0x20; + + output = fds.main_wavetable[(fds.main_addr / sampling_rate) & 0x3f] * 8 * fds.now_volume * fds.master_volume / 30; + + if (fds.now_volume != 0) + fds.now_freq = freq * 4; + else + fds.now_freq = 0; + } + else + { + fds.now_freq = 0; + output = 0; + } + + // LPF + output = (output_buf[0] * 2 + output) / 3; + output_buf[0] = output; + + fds.output = output; + return fds.output; + } + + internal void SyncWrite(ushort addr, byte data) + { + WriteSub(addr, data, fds_sync, 1789772.5d); + } + + + + internal byte SyncRead(ushort addr) + { + byte data = (byte)(addr >> 8); + + if (addr >= 0x4040 && addr <= 0x407F) + { + data = (byte)(fds_sync.main_wavetable[addr & 0x3F] | 0x40); + } + else + if (addr == 0x4090) + { + data = (byte)((fds_sync.volenv_gain & 0x3F) | 0x40); + } + else + if (addr == 0x4092) + { + data = (byte)((fds_sync.swpenv_gain & 0x3F) | 0x40); + } + + return data; + } + + public override bool Sync(int cycles) + { + // Envelope unit + if (fds_sync.envelope_enable != 0 && fds_sync.envelope_speed != 0) + { + // Volume envelope + double decay; + if (fds_sync.volenv_mode < 2) + { + decay = ((double)fds_sync.envelope_speed * (double)(fds_sync.volenv_decay + 1) * 1789772.5) / (232.0 * 960.0); + fds_sync.volenv_phaseacc -= (double)cycles; + while (fds_sync.volenv_phaseacc < 0.0) + { + fds_sync.volenv_phaseacc += decay; + + if (fds_sync.volenv_mode == 0) + { + // 減少モード + if (fds_sync.volenv_gain != 0) + fds_sync.volenv_gain--; + } + else + if (fds_sync.volenv_mode == 1) + { + // 増加モード + if (fds_sync.volenv_gain < 0x20) + fds_sync.volenv_gain++; + } + } + } + + // Sweep envelope + if (fds_sync.swpenv_mode < 2) + { + decay = ((double)fds_sync.envelope_speed * (double)(fds_sync.swpenv_decay + 1) * 1789772.5) / (232.0 * 960.0); + fds_sync.swpenv_phaseacc -= (double)cycles; + while (fds_sync.swpenv_phaseacc < 0.0) + { + fds_sync.swpenv_phaseacc += decay; + + if (fds_sync.swpenv_mode == 0) + { + // 減少モード + if (fds_sync.swpenv_gain != 0) + fds_sync.swpenv_gain--; + } + else + if (fds_sync.swpenv_mode == 1) + { + // 増加モード + if (fds_sync.swpenv_gain < 0x20) + fds_sync.swpenv_gain++; + } + } + } + } + + return false; + } + + public override int GetFreq(int channel) + { + return fds.now_freq; + } + + public override uint GetSize() + { + return fds.GetSize() + fds_sync.GetSize(); + } + + public override void SaveState(StateBuffer buffer) + { + fds.SaveState(buffer); + fds_sync.SaveState(buffer); + } + + private class FDSSOUND : IStateBufferObject + { + public byte[] reg = new byte[0x80]; + public byte volenv_mode; // Volume Envelope + public byte volenv_gain; + public byte volenv_decay; + public double volenv_phaseacc; + public byte swpenv_mode; // Sweep Envelope + public byte swpenv_gain; + public byte swpenv_decay; + public double swpenv_phaseacc; + // For envelope unit + public byte envelope_enable; // $4083 bit6 + public byte envelope_speed; // $408A + // For $4089 + public byte wave_setup; // bit7 + public int master_volume; // bit1-0 + // For Main unit + public int[] main_wavetable = new int[64]; + public byte main_enable; + public int main_frequency; + public int main_addr; + // For Effector(LFO) unit + public byte[] lfo_wavetable = new byte[64]; + public byte lfo_enable; // 0:Enable 1:Wavetable setup + public int lfo_frequency; + public int lfo_addr; + public double lfo_phaseacc; + // For Sweep unit + public int sweep_bias; + // Misc + public int now_volume; + public int now_freq; + public int output; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + volenv_mode = 0; + volenv_gain = 0; + volenv_decay = 0; + volenv_phaseacc = 0.0; + swpenv_mode = 0; + swpenv_gain = 0; + swpenv_decay = 0; + swpenv_phaseacc = 0.0; + envelope_enable = 0; + envelope_speed = 0; + wave_setup = 0; + master_volume = 0; + Array.Clear(main_wavetable, 0, main_wavetable.Length); + main_enable = 0; + main_frequency = 0; + main_addr = 0; + Array.Clear(lfo_wavetable, 0, lfo_wavetable.Length); + lfo_enable = 0; + lfo_frequency = 0; + lfo_addr = 0; + lfo_phaseacc = 0.0; + sweep_bias = 0; + now_volume = 0; + now_freq = 0; + output = 0; + } + + public uint GetSize() + { + return 512; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(volenv_mode); + buffer.Write(volenv_gain); + buffer.Write(volenv_decay); + buffer.Write(volenv_phaseacc); + buffer.Write(swpenv_mode); + buffer.Write(swpenv_gain); + buffer.Write(swpenv_decay); + buffer.Write(swpenv_phaseacc); + buffer.Write(envelope_enable); + buffer.Write(envelope_speed); + buffer.Write(wave_setup); + buffer.Write(master_volume); + buffer.Write(main_wavetable); + buffer.Write(main_enable); + buffer.Write(main_frequency); + buffer.Write(main_addr); + buffer.Write(lfo_wavetable); + buffer.Write(lfo_enable); + buffer.Write(lfo_frequency); + buffer.Write(lfo_addr); + buffer.Write(lfo_phaseacc); + buffer.Write(sweep_bias); + buffer.Write(now_volume); + buffer.Write(now_freq); + buffer.Write(output); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(0x80); + volenv_mode = buffer.Read_byte(); + volenv_gain = buffer.Read_byte(); + volenv_decay = buffer.Read_byte(); + volenv_phaseacc = buffer.Read_double(); + swpenv_mode = buffer.Read_byte(); + swpenv_gain = buffer.Read_byte(); + swpenv_decay = buffer.Read_byte(); + swpenv_phaseacc = buffer.Read_double(); + envelope_enable = buffer.Read_byte(); + envelope_speed = buffer.Read_byte(); + wave_setup = buffer.Read_byte(); + master_volume = buffer.Read_int(); + main_wavetable = buffer.Read_ints(64); + main_enable = buffer.Read_byte(); + main_frequency = buffer.Read_int(); + main_addr = buffer.Read_int(); + lfo_wavetable = buffer.Read_bytes(64); + lfo_enable = buffer.Read_byte(); + lfo_frequency = buffer.Read_int(); + lfo_addr = buffer.Read_int(); + lfo_phaseacc = buffer.Read_double(); + sweep_bias = buffer.Read_int(); + now_volume = buffer.Read_int(); + now_freq = buffer.Read_int(); + output = buffer.Read_int(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_FME7.cs b/Core/VirtualNes.Core/ApuEX/APU_FME7.cs new file mode 100644 index 0000000..ccd9ec5 --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_FME7.cs @@ -0,0 +1,505 @@ +using System; + +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; + 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 + }; + envelope = new ENVELOPE(envelope_table, envstep_table); + + Reset(APU_CLOCK, 22050); + } + + public override void Reset(float fClock, int nRate) + { + int i; + + envelope.ZeroMemory(); + noise.ZeroMemory(); + + foreach (var item in op) + { + item.ZeroMemory(); + } + + envelope.envtbl_index = 0; + envelope.envstep_index = 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) + { + cpu_clock = fClock; + cycle_rate = (int)((fClock / 16.0f) * (1 << 16) / nRate); + } + + public override void Write(ushort addr, byte data) + { + 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_index = (byte)(data & 0x0F); + envelope.envstep_index = (byte)(data & 0x0F); + envelope.envadr = 0; + break; + } + } + } + + public override int Process(int channel) + { + 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 override uint GetSize() + { + return (uint)(1 + envelope.GetSize() + noise.GetSize() + op[0].GetSize() * op.Length); + } + + public override void SaveState(StateBuffer buffer) + { + buffer.Write(address); + + envelope.SaveState(buffer); + noise.SaveState(buffer); + foreach (var oneOp in op) + oneOp.SaveState(buffer); + } + + public class ENVELOPE : IStateBufferObject + { + public byte[] reg = new byte[3]; + public byte volume; + + public int freq; + public int phaseacc; + public int envadr; + + public byte envtbl_index; + public byte envstep_index; + + byte[][] ref_envtbl; + sbyte[][] ref_envstep; + public byte[] envtbl => ref_envtbl[envtbl_index]; + public sbyte[] envstep => ref_envstep[envstep_index]; + public ENVELOPE(byte[][] envtbl, sbyte[][] envstep) + { + ref_envtbl = envtbl; + ref_envstep = envstep; + } + + + public void ZeroMemory() + { + Array.Clear(reg, 0, 3); + volume = 0; + freq = 0; + phaseacc = 0; + envadr = 0; + envtbl_index = 0; + envstep_index = 0; + } + + public uint GetSize() + { + return 18; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(volume); + buffer.Write(freq); + buffer.Write(phaseacc); + buffer.Write(envadr); + buffer.Write(envtbl_index); + buffer.Write(envstep_index); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(3); + volume = buffer.Read_byte(); + freq = buffer.Read_int(); + phaseacc = buffer.Read_int(); + envadr = buffer.Read_int(); + envtbl_index = buffer.Read_byte(); + envstep_index = buffer.Read_byte(); + } + } + + public class NOISE : IStateBufferObject + { + public int freq; + public int phaseacc; + public int noiserange; + public byte noiseout; + + public void ZeroMemory() + { + freq = 0; phaseacc = 0; noiserange = 0; noiseout = 0; + } + + public uint GetSize() + { + return 13; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(freq); + buffer.Write(phaseacc); + buffer.Write(noiserange); + buffer.Write(noiseout); + } + + public void LoadState(StateReader buffer) + { + freq = buffer.Read_int(); + phaseacc = buffer.Read_int(); + noiserange = buffer.Read_int(); + noiseout = buffer.Read_byte(); + } + } + + public class CHANNEL : IStateBufferObject + { + 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; + } + + public uint GetSize() + { + return 20; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(env_on); + buffer.Write(noise_on); + buffer.Write(adder); + buffer.Write(volume); + buffer.Write(freq); + buffer.Write(phaseacc); + buffer.Write(output_vol); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(3); + enable = buffer.Read_byte(); + env_on = buffer.Read_byte(); + noise_on = buffer.Read_byte(); + adder = buffer.Read_byte(); + volume = buffer.Read_byte(); + freq = buffer.Read_int(); + phaseacc = buffer.Read_int(); + output_vol = buffer.Read_int(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_INTERFACE.cs b/Core/VirtualNes.Core/ApuEX/APU_INTERFACE.cs new file mode 100644 index 0000000..25a1b1f --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_INTERFACE.cs @@ -0,0 +1,41 @@ +namespace VirtualNes.Core +{ + public abstract class APU_INTERFACE : IStateBufferObject + { + public const float APU_CLOCK = 1789772.5f; + + public virtual void Dispose() { } + + public abstract void Reset(float fClock, int nRate); + public abstract void Setup(float fClock, int nRate); + public abstract void Write(ushort addr, byte data); + public abstract int Process(int channel); + public virtual byte Read(ushort addr) + { + return (byte)(addr >> 8); + } + public virtual void WriteSync(ushort addr, byte data) { } + public virtual byte ReadSync(ushort addr) { return 0; } + public virtual void VSync() { } + public virtual bool Sync(int cycles) { return false; } + public virtual int GetFreq(int channel) { return 0; } + public virtual void SaveState(StateBuffer buffer) { } + public virtual void LoadState(StateReader buffer) { } + + public static int INT2FIX(int x) + { + return x << 16; + } + + public static int FIX2INT(int x) + { + return x >> 16; + } + + + public virtual uint GetSize() + { + return 0; + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/Core/VirtualNes.Core/ApuEX/APU_INTERNAL.cs new file mode 100644 index 0000000..5ea318a --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -0,0 +1,1702 @@ + +using System; + +namespace VirtualNes.Core +{ + public class APU_INTERNAL : APU_INTERFACE + { + // Volume shift + public const int RECTANGLE_VOL_SHIFT = 8; + public const int TRIANGLE_VOL_SHIFT = 9; + public const int NOISE_VOL_SHIFT = 8; + public const int DPCM_VOL_SHIFT = 8; + + // Tables + static public int[] freq_limit = new int[8] + { + 0x03FF, 0x0555, 0x0666, 0x071C, 0x0787, 0x07C1, 0x07E0, 0x07F0 + }; + static public int[] duty_lut = new int[4] + { + 2, 4, 8, 12 + }; + static public int[] noise_freq = new int[16]{ + 4, 8, 16, 32, 64, 96, 128, 160, + 202, 254, 380, 508, 762, 1016, 2034, 4068 + }; + + private static int[] vbl_length = new int[32] + { + 5, 127, 10, 1, 19, 2, 40, 3, + 80, 4, 30, 5, 7, 6, 13, 7, + 6, 8, 12, 9, 24, 10, 48, 11, + 96, 12, 36, 13, 8, 14, 16, 15, + }; + + private static int[] dpcm_cycles_pal = new int[16] + { + 397, 353, 315, 297, 265, 235, 209, 198, + 176, 148, 131, 118, 98, 78, 66, 50, + }; + + private static int[] dpcm_cycles = new int[16] + { + 428, 380, 340, 320, 286, 254, 226, 214, + 190, 160, 142, 128, 106, 85, 72, 54, + }; + + private NES nes; + // Frame Counter + private int FrameCycle; + private int FrameCount; + private int FrameType; + private byte FrameIRQ; + private byte FrameIRQoccur; + + // Channels + private RECTANGLE ch0 = new RECTANGLE(); + private RECTANGLE ch1 = new RECTANGLE(); + private TRIANGLE ch2 = new TRIANGLE(); + private NOISE ch3 = new NOISE(); + private DPCM ch4 = new DPCM(); + + // Sound + private float cpu_clock; + private int sampling_rate; + private int cycle_rate; + + // $4015 Reg + private byte reg4015, sync_reg4015; + + private const int TONEDATA_MAX = 16; + private const int TONEDATA_LEN = 32; + private const int CHANNEL_MAX = 3; + private const int TONE_MAX = 4; + + bool[] bToneTableEnable = new bool[TONEDATA_MAX]; + int[,] ToneTable = new int[TONEDATA_MAX, TONEDATA_LEN]; + int[,] ChannelTone = new int[CHANNEL_MAX, TONE_MAX]; + + public void SetParent(NES parent) + { + nes = parent; + } + + public override bool Sync(int cycles) + { + FrameCycle -= cycles * 2; + if (FrameCycle <= 0) + { + FrameCycle += 14915; + + UpdateFrame(); + } + + var result = FrameIRQoccur | (SyncUpdateDPCM(cycles) ? 1 : 0); + return result != 0; + } + + private bool SyncUpdateDPCM(int cycles) + { + bool bIRQ = false; + + if (ch4.sync_enable != 0) + { + ch4.sync_cycles -= cycles; + while (ch4.sync_cycles < 0) + { + ch4.sync_cycles += ch4.sync_cache_cycles; + if (ch4.sync_dmalength != 0) + { + // if( !(--ch4.sync_dmalength) ) { + if (--ch4.sync_dmalength < 2) + { + if (ch4.sync_looping != 0) + { + ch4.sync_dmalength = ch4.sync_cache_dmalength; + } + else + { + ch4.sync_dmalength = 0; + + if (ch4.sync_irq_gen != 0) + { + ch4.sync_irq_enable = 0xFF; + nes.cpu.SetIRQ(CPU.IRQ_DPCM); + } + } + } + } + } + } + if (ch4.sync_irq_enable != 0) + { + bIRQ = true; + } + + return bIRQ; + } + + private void UpdateFrame() + { + if (FrameCount == 0) + { + if ((FrameIRQ & 0xC0) == 0 && nes.GetFrameIRQmode()) + { + FrameIRQoccur = 0xFF; + nes.cpu.SetIRQ(CPU.IRQ_FRAMEIRQ); + } + } + + if (FrameCount == 3) + { + if ((FrameIRQ & 0x80) != 0) + { + FrameCycle += 14915; + } + } + + // Counters Update + nes.Write(0x4018, (byte)FrameCount); + + FrameCount = (FrameCount + 1) & 3; + } + + public override void Reset(float fClock, int nRate) + { + ch0.ZeroMemory(); + ch1.ZeroMemory(); + ch2.ZeroMemory(); + ch3.ZeroMemory(); + + Array.Clear(bToneTableEnable, 0, bToneTableEnable.Length); + Array.Clear(ToneTable, 0, ToneTable.Length); + Array.Clear(ChannelTone, 0, ChannelTone.Length); + + reg4015 = sync_reg4015 = 0; + + // Sweep complement + ch0.complement = 0x00; + ch1.complement = 0xFF; + + // Noise shift register + ch3.shift_reg = 0x4000; + + Setup(fClock, nRate); + + // $4011ϳڻʤ + ushort addr; + for (addr = 0x4000; addr <= 0x4010; addr++) + { + Write(addr, 0x00); + SyncWrite(addr, 0x00); + } + // Write( 0x4001, 0x08 ); // Resetrinc`ɤˤʤ? + // Write( 0x4005, 0x08 ); // Resetrinc`ɤˤʤ? + Write(0x4012, 0x00); + Write(0x4013, 0x00); + Write(0x4015, 0x00); + SyncWrite(0x4012, 0x00); + SyncWrite(0x4013, 0x00); + SyncWrite(0x4015, 0x00); + + // $4017ϕzߤdzڻʤ(ڥ`ɤ0ǤΤڴեȤ) + FrameIRQ = 0xC0; + FrameCycle = 0; + FrameIRQoccur = 0; + FrameCount = 0; + FrameType = 0; + } + + public override void Setup(float fClock, int nRate) + { + cpu_clock = fClock; + sampling_rate = nRate; + + cycle_rate = (int)(fClock * 65536.0f / nRate); + } + + public override void Write(ushort addr, byte data) + { + switch (addr) + { + // CH0,1 rectangle + case 0x4000: + case 0x4001: + case 0x4002: + case 0x4003: + case 0x4004: + case 0x4005: + case 0x4006: + case 0x4007: + WriteRectangle((addr < 0x4004) ? 0 : 1, addr, data); + break; + + // CH2 triangle + case 0x4008: + case 0x4009: + case 0x400A: + case 0x400B: + WriteTriangle(addr, data); + break; + + // CH3 noise + case 0x400C: + case 0x400D: + case 0x400E: + case 0x400F: + WriteNoise(addr, data); + break; + + // CH4 DPCM + case 0x4010: + case 0x4011: + case 0x4012: + case 0x4013: + WriteDPCM(addr, data); + break; + + case 0x4015: + reg4015 = data; + + if ((data & (1 << 0)) == 0) + { + ch0.enable = 0; + ch0.len_count = 0; + } + if ((data & (1 << 1)) == 0) + { + ch1.enable = 0; + ch1.len_count = 0; + } + if ((data & (1 << 2)) == 0) + { + ch2.enable = 0; + ch2.len_count = 0; + ch2.lin_count = 0; + ch2.counter_start = 0; + } + if ((data & (1 << 3)) == 0) + { + ch3.enable = 0; + ch3.len_count = 0; + } + if ((data & (1 << 4)) == 0) + { + ch4.enable = 0; + ch4.dmalength = 0; + } + else + { + ch4.enable = 0xFF; + if (ch4.dmalength == 0) + { + ch4.address = ch4.cache_addr; + ch4.dmalength = ch4.cache_dmalength; + ch4.phaseacc = 0; + } + } + break; + + case 0x4017: + break; + + // VirtuaNESХݩ` + case 0x4018: + UpdateRectangle(ch0, data); + UpdateRectangle(ch1, data); + UpdateTriangle(data); + UpdateNoise(data); + break; + + default: + break; + } + } + + private void UpdateNoise(int type) + { + if (ch3.enable == 0 || ch3.len_count <= 0) + return; + + // Update Length + if (ch3.holdnote == 0) + { + // Holdnote + if ((type & 1) == 0 && ch3.len_count != 0) + { + ch3.len_count--; + } + } + + // Update Envelope + if (ch3.env_count != 0) + { + ch3.env_count--; + } + if (ch3.env_count == 0) + { + ch3.env_count = ch3.env_decay; + + // Holdnote + if (ch3.holdnote != 0) + { + ch3.env_vol = (ch3.env_vol - 1) & 0x0F; + } + else if (ch3.env_vol != 0) + { + ch3.env_vol--; + } + } + + if (ch3.env_fixed == 0) + { + ch3.nowvolume = ch3.env_vol << RECTANGLE_VOL_SHIFT; + } + } + + private void UpdateTriangle(int type) + { + if (ch2.enable == 0) + return; + + if ((type & 1) == 0 && ch2.holdnote == 0) + { + if (ch2.len_count != 0) + { + ch2.len_count--; + } + } + + // if( !ch2.len_count ) { + // ch2.lin_count = 0; + // } + + // Update Length/Linear + if (ch2.counter_start != 0) + { + ch2.lin_count = ch2.reg[0] & 0x7F; + } + else if (ch2.lin_count != 0) + { + ch2.lin_count--; + } + if (ch2.holdnote == 0 && ch2.lin_count != 0) + { + ch2.counter_start = 0; + } + } + + private void UpdateRectangle(RECTANGLE ch, int type) + { + if (ch.enable == 0 || ch.len_count <= 0) + return; + + // Update Length/Sweep + if ((type & 1) == 0) + { + // Update Length + if (ch.len_count != 0 && ch.holdnote == 0) + { + // Holdnote + if (ch.len_count != 0) + { + ch.len_count--; + } + } + + // Update Sweep + if (ch.swp_on != 0 && ch.swp_shift != 0) + { + if (ch.swp_count != 0) + { + ch.swp_count--; + } + if (ch.swp_count == 0) + { + ch.swp_count = ch.swp_decay; + if (ch.swp_inc != 0) + { + // Sweep increment(to higher frequency) + if (ch.complement == 0) + ch.freq += ~(ch.freq >> ch.swp_shift); // CH 0 + else + ch.freq -= (ch.freq >> ch.swp_shift); // CH 1 + } + else + { + // Sweep decrement(to lower frequency) + ch.freq += (ch.freq >> ch.swp_shift); + } + } + } + } + + // Update Envelope + if (ch.env_count != 0) + { + ch.env_count--; + } + if (ch.env_count == 0) + { + ch.env_count = ch.env_decay; + + // Holdnote + if (ch.holdnote != 0) + { + ch.env_vol = (ch.env_vol - 1) & 0x0F; + } + else if (ch.env_vol != 0) + { + ch.env_vol--; + } + } + + if (ch.env_fixed == 0) + { + ch.nowvolume = ch.env_vol << RECTANGLE_VOL_SHIFT; + } + } + + private void WriteDPCM(ushort addr, byte data) + { + ch4.reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch4.freq = INT2FIX(nes.GetVideoMode() ? dpcm_cycles_pal[data & 0x0F] : dpcm_cycles[data & 0x0F]); + // ch4.freq = INT2FIX( dpcm_cycles[data&0x0F] ); + //// ch4.freq = INT2FIX( (dpcm_cycles[data&0x0F]-((data&0x0F)^0x0F)*2-2) ); + ch4.looping = (byte)(data & 0x40); + break; + case 1: + ch4.dpcm_value = (byte)((data & 0x7F) >> 1); + break; + case 2: + ch4.cache_addr = (ushort)(0xC000 + (ushort)(data << 6)); + break; + case 3: + ch4.cache_dmalength = ((data << 4) + 1) << 3; + break; + } + } + + private void WriteNoise(ushort addr, byte data) + { + ch3.reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch3.holdnote = (byte)(data & 0x20); + ch3.volume = (byte)(data & 0x0F); + ch3.env_fixed = (byte)(data & 0x10); + ch3.env_decay = (byte)((data & 0x0F) + 1); + break; + case 1: // Unused + break; + case 2: + ch3.freq = INT2FIX(noise_freq[data & 0x0F]); + ch3.xor_tap = (byte)((data & 0x80) != 0 ? 0x40 : 0x02); + break; + case 3: // Master + ch3.len_count = vbl_length[data >> 3] * 2; + ch3.env_vol = 0x0F; + ch3.env_count = (byte)(ch3.env_decay + 1); + + if ((reg4015 & (1 << 3)) != 0) + ch3.enable = 0xFF; + break; + } + } + + private void WriteTriangle(ushort addr, byte data) + { + ch2.reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch2.holdnote = (byte)(data & 0x80); + break; + case 1: // Unused + break; + case 2: + ch2.freq = INT2FIX(((ch2.reg[3] & 0x07) << 8) + data + 1); + break; + case 3: // Master + ch2.freq = INT2FIX((((data & 0x07) << 8) + ch2.reg[2] + 1)); + ch2.len_count = vbl_length[data >> 3] * 2; + ch2.counter_start = 0x80; + + if ((reg4015 & (1 << 2)) != 0) + ch2.enable = 0xFF; + break; + } + } + + private void WriteRectangle(int no, ushort addr, byte data) + { + RECTANGLE ch = (no == 0) ? ch0 : ch1; + + ch.reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch.holdnote = (byte)(data & 0x20); + ch.volume = (byte)(data & 0x0F); + ch.env_fixed = (byte)(data & 0x10); + ch.env_decay = (byte)((data & 0x0F) + 1); + ch.duty = duty_lut[data >> 6]; + break; + case 1: + ch.swp_on = (byte)(data & 0x80); + ch.swp_inc = (byte)(data & 0x08); + ch.swp_shift = (byte)(data & 0x07); + ch.swp_decay = (byte)(((data >> 4) & 0x07) + 1); + ch.freqlimit = freq_limit[data & 0x07]; + break; + case 2: + ch.freq = (ch.freq & (~0xFF)) + data; + break; + case 3: // Master + ch.freq = ((data & 0x07) << 8) + (ch.freq & 0xFF); + ch.len_count = vbl_length[data >> 3] * 2; + ch.env_vol = 0x0F; + ch.env_count = (byte)(ch.env_decay + 1); + ch.adder = 0; + + if ((reg4015 & (1 << no)) != 0) + ch.enable = 0xFF; + break; + } + } + + public override int Process(int channel) + { + switch (channel) + { + case 0: + return RenderRectangle(ch0); + case 1: + return RenderRectangle(ch1); + case 2: + return RenderTriangle(); + case 3: + return RenderNoise(); + case 4: + return RenderDPCM(); + default: + return 0; + } + } + + private int RenderDPCM() + { + if (ch4.dmalength != 0) + { + ch4.phaseacc -= cycle_rate; + + while (ch4.phaseacc < 0) + { + ch4.phaseacc += ch4.freq; + if ((ch4.dmalength & 7) == 0) + { + ch4.cur_byte = nes.Read(ch4.address); + if (0xFFFF == ch4.address) + ch4.address = 0x8000; + else + ch4.address++; + } + + if ((--ch4.dmalength) == 0) + { + if (ch4.looping != 0) + { + ch4.address = ch4.cache_addr; + ch4.dmalength = ch4.cache_dmalength; + } + else + { + ch4.enable = 0; + break; + } + } + // positive delta + if ((ch4.cur_byte & (1 << ((ch4.dmalength & 7) ^ 7))) != 0) + { + if (ch4.dpcm_value < 0x3F) + ch4.dpcm_value += 1; + } + else + { + // negative delta + if (ch4.dpcm_value > 1) + ch4.dpcm_value -= 1; + } + } + } + + // ץΥå(TEST) + ch4.dpcm_output_real = ((ch4.reg[1] & 0x01) + ch4.dpcm_value * 2) - 0x40; + if (Math.Abs(ch4.dpcm_output_real - ch4.dpcm_output_fake) <= 8) + { + ch4.dpcm_output_fake = ch4.dpcm_output_real; + ch4.output = ch4.dpcm_output_real << DPCM_VOL_SHIFT; + } + else + { + if (ch4.dpcm_output_real > ch4.dpcm_output_fake) + ch4.dpcm_output_fake += 8; + else + ch4.dpcm_output_fake -= 8; + ch4.output = ch4.dpcm_output_fake << DPCM_VOL_SHIFT; + } + return ch4.output; + } + + private int RenderNoise() + { + if (ch3.enable == 0 || ch3.len_count <= 0) + return 0; + + if (ch3.env_fixed != 0) + { + ch3.nowvolume = ch3.volume << RECTANGLE_VOL_SHIFT; + } + + int vol = 256 - ((ch4.reg[1] & 0x01) + ch4.dpcm_value * 2); + + ch3.phaseacc -= cycle_rate; + if (ch3.phaseacc >= 0) + return ch3.output * vol / 256; + + if (ch3.freq > cycle_rate) + { + ch3.phaseacc += ch3.freq; + if (NoiseShiftreg(ch3.xor_tap)) + ch3.output = ch3.nowvolume; + else + ch3.output = -ch3.nowvolume; + + return ch3.output * vol / 256; + } + + int num_times, total; + num_times = total = 0; + while (ch3.phaseacc < 0) + { + ch3.phaseacc += ch3.freq; + if (NoiseShiftreg(ch3.xor_tap)) + ch3.output = ch3.nowvolume; + else + ch3.output = -ch3.nowvolume; + + total += ch3.output; + num_times++; + } + + return (total / num_times) * vol / 256; + } + + private bool NoiseShiftreg(byte xor_tap) + { + int bit0, bit14; + + bit0 = ch3.shift_reg & 1; + if ((ch3.shift_reg & xor_tap) != 0) bit14 = bit0 ^ 1; + else bit14 = bit0 ^ 0; + ch3.shift_reg >>= 1; + ch3.shift_reg |= (bit14 << 14); + return (bit0 ^ 1) != 0; + } + + private int RenderTriangle() + { + int vol; + if (Supporter.Config.sound.bDisableVolumeEffect) + { + vol = 256; + } + else + { + vol = 256 - ((ch4.reg[1] & 0x01) + ch4.dpcm_value * 2); + } + + if (ch2.enable == 0 || (ch2.len_count <= 0) || (ch2.lin_count <= 0)) + { + return ch2.nowvolume * vol / 256; + } + + if (ch2.freq < INT2FIX(8)) + { + return ch2.nowvolume * vol / 256; + } + + if (!(Supporter.Config.sound.bChangeTone && ChannelTone[2, 0] != 0)) + { + ch2.phaseacc -= cycle_rate; + if (ch2.phaseacc >= 0) + { + return ch2.nowvolume * vol / 256; + } + + if (ch2.freq > cycle_rate) + { + ch2.phaseacc += ch2.freq; + ch2.adder = (ch2.adder + 1) & 0x1F; + + if (ch2.adder < 0x10) + { + ch2.nowvolume = (ch2.adder & 0x0F) << TRIANGLE_VOL_SHIFT; + } + else + { + ch2.nowvolume = (0x0F - (ch2.adder & 0x0F)) << TRIANGLE_VOL_SHIFT; + } + + return ch2.nowvolume * vol / 256; + } + + // ƽ + int num_times, total; + num_times = total = 0; + while (ch2.phaseacc < 0) + { + ch2.phaseacc += ch2.freq; + ch2.adder = (ch2.adder + 1) & 0x1F; + + if (ch2.adder < 0x10) + { + ch2.nowvolume = (ch2.adder & 0x0F) << TRIANGLE_VOL_SHIFT; + } + else + { + ch2.nowvolume = (0x0F - (ch2.adder & 0x0F)) << TRIANGLE_VOL_SHIFT; + } + + total += ch2.nowvolume; + num_times++; + } + + return (total / num_times) * vol / 256; + } + else + { + int x = ChannelTone[2, 0] - 1; + int pTone = 0; + + ch2.phaseacc -= cycle_rate; + if (ch2.phaseacc >= 0) + { + return ch2.nowvolume * vol / 256; + } + + if (ch2.freq > cycle_rate) + { + ch2.phaseacc += ch2.freq; + ch2.adder = (ch2.adder + 1) & 0x1F; + var temp = ToneTable[x, pTone + (ch2.adder & 0x1F)]; + ch2.nowvolume = temp * 0x0F; + return ch2.nowvolume * vol / 256; + } + + // ƽ + int num_times, total; + num_times = total = 0; + while (ch2.phaseacc < 0) + { + ch2.phaseacc += ch2.freq; + ch2.adder = (ch2.adder + 1) & 0x1F; + var temp = ToneTable[x, pTone + (ch2.adder & 0x1F)]; + total += temp * 0x0F; + num_times++; + } + + return (total / num_times) * vol / 256; + } + } + + private int RenderRectangle(RECTANGLE ch) + { + if (ch.enable == 0 || ch.len_count <= 0) + return 0; + + // Channel disable? + if ((ch.freq < 8) || (ch.swp_inc == 0 && ch.freq > ch.freqlimit)) + { + return 0; + } + + if (ch.env_fixed != 0) + { + ch.nowvolume = ch.volume << RECTANGLE_VOL_SHIFT; + } + int volume = ch.nowvolume; + + if (!(Supporter.Config.sound.bChangeTone && (ChannelTone[(ch.complement == 0) ? 0 : 1, ch.reg[0] >> 6]) != 0)) + { + // agI + double total; + double sample_weight = ch.phaseacc; + if (sample_weight > cycle_rate) + { + sample_weight = cycle_rate; + } + total = (ch.adder < ch.duty) ? sample_weight : -sample_weight; + + int freq = INT2FIX(ch.freq + 1); + ch.phaseacc -= cycle_rate; + while (ch.phaseacc < 0) + { + ch.phaseacc += freq; + ch.adder = (ch.adder + 1) & 0x0F; + + sample_weight = freq; + if (ch.phaseacc > 0) + { + sample_weight -= ch.phaseacc; + } + total += (ch.adder < ch.duty) ? sample_weight : -sample_weight; + } + return (int)(volume * total / cycle_rate + 0.5); + } + else + { + int x = ChannelTone[(ch.complement == 0) ? 0 : 1, ch.reg[0] >> 6] - 1; + int pTone = 0; + + // Ÿo + ch.phaseacc -= cycle_rate * 2; + if (ch.phaseacc >= 0) + { + var temp = ToneTable[x, pTone + (ch.adder & 0x1F)]; + return temp * volume / ((1 << RECTANGLE_VOL_SHIFT) / 2); + } + + // 1ƥåפ + int freq = INT2FIX(ch.freq + 1); + if (freq > cycle_rate * 2) + { + ch.phaseacc += freq; + ch.adder = (ch.adder + 1) & 0x1F; + var temp = ToneTable[x, pTone + (ch.adder & 0x1F)]; + return temp * volume / ((1 << RECTANGLE_VOL_SHIFT) / 2); + } + + // ƽ + int num_times, total; + num_times = total = 0; + while (ch.phaseacc < 0) + { + ch.phaseacc += freq; + ch.adder = (ch.adder + 1) & 0x1F; + var temp = ToneTable[x, pTone + (ch.adder & 0x1F)]; + total += temp * volume / ((1 << RECTANGLE_VOL_SHIFT) / 2); + num_times++; + } + return total / num_times; + } + } + + internal byte SyncRead(ushort addr) + { + byte data = (byte)(addr >> 8); + + if (addr == 0x4015) + { + data = 0; + if ((ch0.sync_enable != 0) && ch0.sync_len_count > 0) data |= (1 << 0); + if ((ch1.sync_enable != 0) && ch1.sync_len_count > 0) data |= (1 << 1); + if ((ch2.sync_enable != 0) && ch2.sync_len_count > 0) data |= (1 << 2); + if ((ch3.sync_enable != 0) && ch3.sync_len_count > 0) data |= (1 << 3); + if ((ch4.sync_enable != 0) && (ch4.sync_dmalength != 0)) data |= (1 << 4); + if (FrameIRQoccur != 0) data |= (1 << 6); + if (ch4.sync_irq_enable != 0) data |= (1 << 7); + FrameIRQoccur = 0; + + nes.cpu.ClrIRQ(CPU.IRQ_FRAMEIRQ); + } + if (addr == 0x4017) + { + if (FrameIRQoccur != 0) + { + data = 0; + } + else + { + data |= (1 << 6); + } + } + return data; + } + + internal void SyncWrite(ushort addr, byte data) + { + switch (addr) + { + // CH0,1 rectangle + case 0x4000: + case 0x4001: + case 0x4002: + case 0x4003: + case 0x4004: + case 0x4005: + case 0x4006: + case 0x4007: + SyncWriteRectangle((addr < 0x4004) ? 0 : 1, addr, data); + break; + // CH2 triangle + case 0x4008: + case 0x4009: + case 0x400A: + case 0x400B: + SyncWriteTriangle(addr, data); + break; + // CH3 noise + case 0x400C: + case 0x400D: + case 0x400E: + case 0x400F: + SyncWriteNoise(addr, data); + break; + // CH4 DPCM + case 0x4010: + case 0x4011: + case 0x4012: + case 0x4013: + SyncWriteDPCM(addr, data); + break; + + case 0x4015: + sync_reg4015 = data; + + if ((data & (1 << 0)) == 0) + { + ch0.sync_enable = 0; + ch0.sync_len_count = 0; + } + if ((data & (1 << 1)) == 0) + { + ch1.sync_enable = 0; + ch1.sync_len_count = 0; + } + if ((data & (1 << 2)) == 0) + { + ch2.sync_enable = 0; + ch2.sync_len_count = 0; + ch2.sync_lin_count = 0; + ch2.sync_counter_start = 0; + } + if ((data & (1 << 3)) == 0) + { + ch3.sync_enable = 0; + ch3.sync_len_count = 0; + } + if ((data & (1 << 4)) == 0) + { + ch4.sync_enable = 0; + ch4.sync_dmalength = 0; + ch4.sync_irq_enable = 0; + + nes.cpu.ClrIRQ(CPU.IRQ_DPCM); + } + else + { + ch4.sync_enable = 0xFF; + if (ch4.sync_dmalength == 0) + { + // ch4.sync_cycles = ch4.sync_cache_cycles; + ch4.sync_dmalength = ch4.sync_cache_dmalength; + ch4.sync_cycles = 0; + } + } + break; + + case 0x4017: + SyncWrite4017(data); + break; + + // VirtuaNESŗL|[g + case 0x4018: + SyncUpdateRectangle(ch0, data); + SyncUpdateRectangle(ch1, data); + SyncUpdateTriangle(data); + SyncUpdateNoise(data); + break; + default: + break; + } + } + + private void SyncUpdateNoise(int type) + { + if (ch3.sync_enable == 0 || ch3.sync_len_count <= 0) + return; + + // Update Length + if (ch3.sync_len_count != 0 && ch3.sync_holdnote == 0) + { + if ((type & 1) == 0 && ch3.sync_len_count != 0) + { + ch3.sync_len_count--; + } + } + } + + private void SyncUpdateTriangle(int type) + { + if (ch2.sync_enable == 0) + return; + + if ((type & 1) == 0 && ch2.sync_holdnote == 0) + { + if (ch2.sync_len_count != 0) + { + ch2.sync_len_count--; + } + } + + // Update Length/Linear + if (ch2.sync_counter_start != 0) + { + ch2.sync_lin_count = ch2.sync_reg[0] & 0x7F; + } + else if (ch2.sync_lin_count != 0) + { + ch2.sync_lin_count--; + } + if (ch2.sync_holdnote == 0 && ch2.sync_lin_count != 0) + { + ch2.sync_counter_start = 0; + } + } + + private void SyncUpdateRectangle(RECTANGLE ch, int type) + { + if (ch.sync_enable == 0 || ch.sync_len_count <= 0) + return; + + // Update Length + if (ch.sync_len_count != 0 && ch.sync_holdnote == 0) + { + if ((type & 1) == 0 && ch.sync_len_count != 0) + { + ch.sync_len_count--; + } + } + } + + private void SyncWrite4017(byte data) + { + FrameCycle = 0; + FrameIRQ = data; + FrameIRQoccur = 0; + + nes.cpu.ClrIRQ(CPU.IRQ_FRAMEIRQ); + + FrameType = (data & 0x80) != 0 ? 1 : 0; + FrameCount = 0; + if ((data & 0x80) > 0) + { + UpdateFrame(); + } + FrameCount = 1; + FrameCycle = 14915; + } + + private void SyncWriteDPCM(ushort addr, byte data) + { + ch4.reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch4.sync_cache_cycles = nes.GetVideoMode() ? dpcm_cycles_pal[data & 0x0F] * 8 : dpcm_cycles[data & 0x0F] * 8; + ch4.sync_looping = (byte)(data & 0x40); + ch4.sync_irq_gen = (byte)(data & 0x80); + if (ch4.sync_irq_gen == 0) + { + ch4.sync_irq_enable = 0; + nes.cpu.ClrIRQ(CPU.IRQ_DPCM); + } + break; + case 1: + break; + case 2: + break; + case 3: + ch4.sync_cache_dmalength = (data << 4) + 1; + break; + } + } + + private void SyncWriteNoise(ushort addr, byte data) + { + ch3.sync_reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch3.sync_holdnote = (byte)(data & 0x20); + break; + case 1: + break; + case 2: + break; + case 3: // Master + ch3.sync_len_count = vbl_length[data >> 3] * 2; + if ((sync_reg4015 & (1 << 3)) != 0) + ch3.sync_enable = 0xFF; + break; + } + } + + private void SyncWriteTriangle(ushort addr, byte data) + { + ch2.sync_reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch2.sync_holdnote = (byte)(data & 0x80); + break; + case 1: + break; + case 2: + break; + case 3: // Master + ch2.sync_len_count = vbl_length[ch2.sync_reg[3] >> 3] * 2; + ch2.sync_counter_start = 0x80; + + if ((sync_reg4015 & (1 << 2)) != 0) + ch2.sync_enable = 0xFF; + break; + } + } + + private void SyncWriteRectangle(int no, ushort addr, byte data) + { + RECTANGLE ch = (no == 0) ? ch0 : ch1; + + ch.sync_reg[addr & 3] = data; + switch (addr & 3) + { + case 0: + ch.sync_holdnote = (byte)(data & 0x20); + break; + case 1: + case 2: + break; + case 3: // Master + ch.sync_len_count = vbl_length[data >> 3] * 2; + if ((sync_reg4015 & (1 << no)) != 0) + ch.sync_enable = 0xFF; + break; + } + } + + internal void GetFrameIRQ(ref int cycle, ref byte count, ref byte type, ref byte IRQ, ref byte occur) + { + cycle = FrameCycle; + count = (byte)FrameCount; + type = (byte)FrameType; + IRQ = FrameIRQ; + occur = FrameIRQoccur; + } + + internal void SetFrameIRQ(int cycle, byte count, byte type, byte IRQ, byte occur) + { + FrameCycle = cycle; + FrameCount = count; + FrameType = type; + FrameIRQ = IRQ; + FrameIRQoccur = occur; + } + + public override uint GetSize() + { + return sizeof(byte) + + sizeof(byte) + + sizeof(int) + + sizeof(int) + + sizeof(int) + + sizeof(byte) + + sizeof(byte) + + ch0.GetSize() + + ch1.GetSize() + + ch2.GetSize() + + ch3.GetSize() + + ch4.GetSize(); + } + + public override void SaveState(StateBuffer p) + { + p.Write(reg4015); + p.Write(sync_reg4015); + p.Write(FrameCycle); + p.Write(FrameCount); + p.Write(FrameType); + p.Write(FrameIRQ); + p.Write(FrameIRQoccur); + ch0.SaveState(p); + ch1.SaveState(p); + ch2.SaveState(p); + ch3.SaveState(p); + ch4.SaveState(p); + } + + public class RECTANGLE : IStateBufferObject + { + 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 uint GetSize() + { + return 64; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(holdnote); + buffer.Write(volume); + buffer.Write(complement); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(freqlimit); + buffer.Write(adder); + buffer.Write(duty); + buffer.Write(len_count); + buffer.Write(nowvolume); + buffer.Write(env_fixed); + buffer.Write(env_decay); + buffer.Write(env_count); + buffer.Write(dummy0); + buffer.Write(env_vol); + buffer.Write(swp_on); + buffer.Write(swp_inc); + buffer.Write(swp_shift); + buffer.Write(swp_decay); + buffer.Write(swp_count); + buffer.Write(dummy1); + buffer.Write(sync_reg); + buffer.Write(sync_output_enable); + buffer.Write(sync_enable); + buffer.Write(sync_holdnote); + buffer.Write(dummy2); + buffer.Write(sync_len_count); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + holdnote = buffer.Read_byte(); + volume = buffer.Read_byte(); + complement = buffer.Read_byte(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + freqlimit = buffer.Read_int(); + adder = buffer.Read_int(); + duty = buffer.Read_int(); + len_count = buffer.Read_int(); + nowvolume = buffer.Read_int(); + env_fixed = buffer.Read_byte(); + env_decay = buffer.Read_byte(); + env_count = buffer.Read_byte(); + dummy0 = buffer.Read_byte(); + env_vol = buffer.Read_int(); + swp_on = buffer.Read_byte(); + swp_inc = buffer.Read_byte(); + swp_shift = buffer.Read_byte(); + swp_decay = buffer.Read_byte(); + swp_count = buffer.Read_byte(); + dummy1 = buffer.Read_bytes(3); + sync_reg = buffer.Read_bytes(4); + sync_output_enable = buffer.Read_byte(); + sync_enable = buffer.Read_byte(); + sync_holdnote = buffer.Read_byte(); + dummy2 = buffer.Read_byte(); + sync_len_count = buffer.Read_int(); + } + } + public class TRIANGLE : IStateBufferObject + { + 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 uint GetSize() + { + return 47; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(holdnote); + buffer.Write(counter_start); + buffer.Write(dummy0); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(len_count); + buffer.Write(lin_count); + buffer.Write(adder); + buffer.Write(nowvolume); + buffer.Write(sync_reg); + buffer.Write(sync_enable); + buffer.Write(sync_holdnote); + buffer.Write(sync_counter_start); + buffer.Write(sync_len_count); + buffer.Write(sync_lin_count); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + holdnote = buffer.Read_byte(); + counter_start = buffer.Read_byte(); + dummy0 = buffer.Read_byte(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + len_count = buffer.Read_int(); + lin_count = buffer.Read_int(); + adder = buffer.Read_int(); + nowvolume = buffer.Read_int(); + sync_reg = buffer.Read_bytes(4); + sync_enable = buffer.Read_byte(); + sync_holdnote = buffer.Read_byte(); + sync_counter_start = buffer.Read_byte(); + sync_len_count = buffer.Read_int(); + sync_lin_count = buffer.Read_int(); + } + } + public class DPCM : IStateBufferObject + { + 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 uint GetSize() + { + return 72; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(looping); + buffer.Write(cur_byte); + buffer.Write(dpcm_value); + buffer.Write(freq); + buffer.Write(phaseacc); + buffer.Write(output); + buffer.Write(address); + buffer.Write(cache_addr); + buffer.Write(dmalength); + buffer.Write(cache_dmalength); + buffer.Write(dpcm_output_real); + buffer.Write(dpcm_output_fake); + buffer.Write(dpcm_output_old); + buffer.Write(dpcm_output_offset); + buffer.Write(sync_reg); + buffer.Write(sync_enable); + buffer.Write(sync_looping); + buffer.Write(sync_irq_gen); + buffer.Write(sync_irq_enable); + buffer.Write(sync_cycles); + buffer.Write(sync_cache_cycles); + buffer.Write(sync_dmalength); + buffer.Write(sync_cache_dmalength); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + looping = buffer.Read_byte(); + cur_byte = buffer.Read_byte(); + dpcm_value = buffer.Read_byte(); + freq = buffer.Read_int(); + phaseacc = buffer.Read_int(); + output = buffer.Read_int(); + address = buffer.Read_ushort(); + cache_addr = buffer.Read_ushort(); + dmalength = buffer.Read_int(); + cache_dmalength = buffer.Read_int(); + dpcm_output_real = buffer.Read_int(); + dpcm_output_fake = buffer.Read_int(); + dpcm_output_old = buffer.Read_int(); + dpcm_output_offset = buffer.Read_int(); + sync_reg = buffer.Read_bytes(4); + sync_enable = buffer.Read_byte(); + sync_looping = buffer.Read_byte(); + sync_irq_gen = buffer.Read_byte(); + sync_irq_enable = buffer.Read_byte(); + sync_cycles = buffer.Read_int(); + sync_cache_cycles = buffer.Read_int(); + sync_dmalength = buffer.Read_int(); + sync_cache_dmalength = buffer.Read_int(); + } + } + public class NOISE : IStateBufferObject + { + 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; + } + + public uint GetSize() + { + return 52; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(holdnote); + buffer.Write(volume); + buffer.Write(xor_tap); + buffer.Write(shift_reg); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(len_count); + buffer.Write(nowvolume); + buffer.Write(output); + buffer.Write(env_fixed); + buffer.Write(env_decay); + buffer.Write(env_count); + buffer.Write(dummy0); + buffer.Write(env_vol); + buffer.Write(sync_reg); + buffer.Write(sync_output_enable); + buffer.Write(sync_enable); + buffer.Write(sync_holdnote); + buffer.Write(dummy1); + buffer.Write(sync_len_count); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + holdnote = buffer.Read_byte(); + volume = buffer.Read_byte(); + xor_tap = buffer.Read_byte(); + shift_reg = buffer.Read_int(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + len_count = buffer.Read_int(); + nowvolume = buffer.Read_int(); + output = buffer.Read_int(); + env_fixed = buffer.Read_byte(); + env_decay = buffer.Read_byte(); + env_count = buffer.Read_byte(); + dummy0 = buffer.Read_byte(); + env_vol = buffer.Read_int(); + sync_reg = buffer.Read_bytes(4); + sync_output_enable = buffer.Read_byte(); + sync_enable = buffer.Read_byte(); + sync_holdnote = buffer.Read_byte(); + dummy1 = buffer.Read_byte(); + sync_len_count = buffer.Read_int(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_MMC5.cs b/Core/VirtualNes.Core/ApuEX/APU_MMC5.cs new file mode 100644 index 0000000..1185312 --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_MMC5.cs @@ -0,0 +1,499 @@ + +using System; + +namespace VirtualNes.Core +{ + public class APU_MMC5 : APU_INTERFACE + { + public const int RECTANGLE_VOL_SHIFT = 8; + public const int DAOUT_VOL_SHIFT = 6; + + SYNCRECTANGLE sch0 = new SYNCRECTANGLE(); + SYNCRECTANGLE sch1 = new SYNCRECTANGLE(); + RECTANGLE ch0 = new RECTANGLE(); + RECTANGLE ch1 = new RECTANGLE(); + + byte reg5010; + byte reg5011; + byte reg5015; + byte sync_reg5015; + int FrameCycle; + float cpu_clock; + int cycle_rate; + + // Tables + static int[] vbl_length = new int[32]; + static int[] duty_lut = new int[4]; + + static int[] decay_lut = new int[16]; + static int[] vbl_lut = new int[32]; + + public APU_MMC5() + { + // 仮設定 + Reset(APU_INTERFACE.APU_CLOCK, 22050); + } + + public override void Reset(float fClock, int nRate) + { + sch0.ZeroMemory(); + sch1.ZeroMemory(); + + reg5010 = reg5011 = reg5015 = 0; + + sync_reg5015 = 0; + FrameCycle = 0; + + Setup(fClock, nRate); + + for (ushort addr = 0x5000; addr <= 0x5015; addr++) + { + Write(addr, 0); + } + } + + public override void Setup(float fClock, int nRate) + { + cpu_clock = fClock; + cycle_rate = (int)(fClock * 65536.0f / nRate); + + // Create Tables + int i; + int samples = (int)(nRate / 60.0f); + for (i = 0; i < 16; i++) + decay_lut[i] = (i + 1) * samples * 5; + for (i = 0; i < 32; i++) + vbl_lut[i] = vbl_length[i] * samples * 5; + } + + public override void Write(ushort addr, byte data) + { + switch (addr) + { + // MMC5 CH0 rectangle + case 0x5000: + ch0.reg[0] = data; + ch0.volume = (byte)(data & 0x0F); + ch0.holdnote = (byte)(data & 0x20); + ch0.fixed_envelope = (byte)(data & 0x10); + ch0.env_decay = decay_lut[data & 0x0F]; + ch0.duty_flip = duty_lut[data >> 6]; + break; + case 0x5001: + ch0.reg[1] = data; + break; + case 0x5002: + ch0.reg[2] = data; + ch0.freq = INT2FIX(((ch0.reg[3] & 0x07) << 8) + data + 1); + break; + case 0x5003: + ch0.reg[3] = data; + ch0.vbl_length = vbl_lut[data >> 3]; + ch0.env_vol = 0; + ch0.freq = INT2FIX(((data & 0x07) << 8) + ch0.reg[2] + 1); + if ((reg5015 & 0x01) != 0) + ch0.enable = 0xFF; + break; + // MMC5 CH1 rectangle + case 0x5004: + ch1.reg[0] = data; + ch1.volume = (byte)(data & 0x0F); + ch1.holdnote = (byte)(data & 0x20); + ch1.fixed_envelope = (byte)(data & 0x10); + ch1.env_decay = decay_lut[data & 0x0F]; + ch1.duty_flip = duty_lut[data >> 6]; + break; + case 0x5005: + ch1.reg[1] = data; + break; + case 0x5006: + ch1.reg[2] = data; + ch1.freq = INT2FIX(((ch1.reg[3] & 0x07) << 8) + data + 1); + break; + case 0x5007: + ch1.reg[3] = data; + ch1.vbl_length = vbl_lut[data >> 3]; + ch1.env_vol = 0; + ch1.freq = INT2FIX(((data & 0x07) << 8) + ch1.reg[2] + 1); + if ((reg5015 & 0x02) != 0) + ch1.enable = 0xFF; + break; + case 0x5010: + reg5010 = data; + break; + case 0x5011: + reg5011 = data; + break; + case 0x5012: + case 0x5013: + case 0x5014: + break; + case 0x5015: + reg5015 = data; + if ((reg5015 & 0x01) != 0) + { + ch0.enable = 0xFF; + } + else + { + ch0.enable = 0; + ch0.vbl_length = 0; + } + if ((reg5015 & 0x02) != 0) + { + ch1.enable = 0xFF; + } + else + { + ch1.enable = 0; + ch1.vbl_length = 0; + } + break; + } + } + + internal void SyncWrite(ushort addr, byte data) + { + switch (addr) + { + // MMC5 CH0 rectangle + case 0x5000: + sch0.reg[0] = data; + sch0.holdnote = (byte)(data & 0x20); + break; + case 0x5001: + case 0x5002: + sch0.reg[addr & 3] = data; + break; + case 0x5003: + sch0.reg[3] = data; + sch0.vbl_length = vbl_length[data >> 3]; + if ((sync_reg5015 & 0x01) != 0) + sch0.enable = 0xFF; + break; + // MMC5 CH1 rectangle + case 0x5004: + sch1.reg[0] = data; + sch1.holdnote = (byte)(data & 0x20); + break; + case 0x5005: + case 0x5006: + sch1.reg[addr & 3] = data; + break; + case 0x5007: + sch1.reg[3] = data; + sch1.vbl_length = vbl_length[data >> 3]; + if ((sync_reg5015 & 0x02) != 0) + sch1.enable = 0xFF; + break; + case 0x5010: + case 0x5011: + case 0x5012: + case 0x5013: + case 0x5014: + break; + case 0x5015: + sync_reg5015 = data; + if ((sync_reg5015 & 0x01) != 0) + { + sch0.enable = 0xFF; + } + else + { + sch0.enable = 0; + sch0.vbl_length = 0; + } + if ((sync_reg5015 & 0x02) != 0) + { + sch1.enable = 0xFF; + } + else + { + sch1.enable = 0; + sch1.vbl_length = 0; + } + break; + } + } + + internal byte SyncRead(ushort addr) + { + byte data = 0; + + if (addr == 0x5015) + { + if ((sch0.enable != 0) && sch0.vbl_length > 0) data |= (1 << 0); + if ((sch1.enable != 0) && sch1.vbl_length > 0) data |= (1 << 1); + } + + return data; + } + + public override bool Sync(int cycles) + { + FrameCycle += cycles; + if (FrameCycle >= 7457 * 5 / 2) + { + FrameCycle -= 7457 * 5 / 2; + + if (sch0.enable != 0 && sch0.holdnote == 0) + { + if ((sch0.vbl_length) != 0) + { + sch0.vbl_length--; + } + } + if (sch1.enable != 0 && sch1.holdnote == 0) + { + if ((sch1.vbl_length) != 0) + { + sch1.vbl_length--; + } + } + } + + return false; + } + + public override int Process(int channel) + { + switch (channel) + { + case 0: + return RectangleRender(ch0); + case 1: + return RectangleRender(ch1); + case 2: + return reg5011 << DAOUT_VOL_SHIFT; + } + + return 0; + } + + public override int GetFreq(int channel) + { + if (channel == 0 || channel == 1) + { + RECTANGLE ch = null; + if (channel == 0) ch = ch0; + else ch = ch1; + + if (ch.enable == 0 || ch.vbl_length <= 0) + return 0; + if (ch.freq < INT2FIX(8)) + return 0; + if (ch.fixed_envelope != 0) + { + if (ch.volume == 0) + return 0; + } + else + { + if ((0x0F - ch.env_vol) == 0) + return 0; + } + + return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f)); + } + + return 0; + } + + private int RectangleRender(RECTANGLE ch) + { + if (ch.enable == 0 || ch.vbl_length <= 0) + return 0; + + // vbl length counter + if (ch.holdnote == 0) + ch.vbl_length -= 5; + + // envelope unit + ch.env_phase -= 5 * 4; + while (ch.env_phase < 0) + { + ch.env_phase += ch.env_decay; + if ((ch.holdnote) != 0) + ch.env_vol = (byte)((ch.env_vol + 1) & 0x0F); + else if (ch.env_vol < 0x0F) + ch.env_vol++; + } + + if (ch.freq < INT2FIX(8)) + return 0; + + int volume; + if ((ch.fixed_envelope) != 0) + volume = ch.volume; + else + volume = (0x0F - ch.env_vol); + + int output = volume << RECTANGLE_VOL_SHIFT; + + ch.phaseacc -= cycle_rate; + if (ch.phaseacc >= 0) + { + if (ch.adder < ch.duty_flip) + ch.output_vol = output; + else + ch.output_vol = -output; + return ch.output_vol; + } + + if (ch.freq > cycle_rate) + { + ch.phaseacc += ch.freq; + ch.adder = (ch.adder + 1) & 0x0F; + if (ch.adder < ch.duty_flip) + ch.output_vol = output; + else + ch.output_vol = -output; + } + else + { + // 加重平均 + int num_times, total; + num_times = total = 0; + while (ch.phaseacc < 0) + { + ch.phaseacc += ch.freq; + ch.adder = (ch.adder + 1) & 0x0F; + if (ch.adder < ch.duty_flip) + total += output; + else + total -= output; + num_times++; + } + ch.output_vol = total / num_times; + } + + return ch.output_vol; + } + + public override uint GetSize() + { + //3*sizeof(BYTE) + sizeof(ch0) + sizeof(ch1) + sizeof(sch0) + sizeof(sch1); 源代码似乎少了sync_reg5015的大小 + return 3 + ch0.GetSize() + ch1.GetSize() + 1 + sch0.GetSize() + sch1.GetSize(); + } + + public override void SaveState(StateBuffer buffer) + { + buffer.Write(reg5010); + buffer.Write(reg5011); + buffer.Write(reg5015); + + ch0.SaveState(buffer); + ch1.SaveState(buffer); + + buffer.Write(sync_reg5015); + sch0.SaveState(buffer); + sch1.SaveState(buffer); + } + + public class SYNCRECTANGLE : IStateBufferObject + { + // For sync + public byte[] reg = new byte[4]; + public byte enable; + public byte holdnote; + public byte[] dummy = new byte[2]; + public int vbl_length; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + enable = 0; + holdnote = 0; + Array.Clear(dummy, 0, dummy.Length); + vbl_length = 0; + } + + public uint GetSize() + { + return 12; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(holdnote); + buffer.Write(dummy); + buffer.Write(vbl_length); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + holdnote = buffer.Read_byte(); + dummy = buffer.Read_bytes(2); + vbl_length = buffer.Read_int(); + } + } + + public class RECTANGLE : IStateBufferObject + { + public byte[] reg = new byte[4]; + public byte enable; + + public int vbl_length; + + public int phaseacc; + public int freq; + + public int output_vol; + public byte fixed_envelope; + public byte holdnote; + public byte volume; + + public byte env_vol; + public int env_phase; + public int env_decay; + + public int adder; + public int duty_flip; + + public uint GetSize() + { + return 45; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(vbl_length); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(output_vol); + buffer.Write(fixed_envelope); + buffer.Write(holdnote); + buffer.Write(volume); + buffer.Write(env_vol); + buffer.Write(env_phase); + buffer.Write(env_decay); + buffer.Write(adder); + buffer.Write(duty_flip); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(4); + enable = buffer.Read_byte(); + vbl_length = buffer.Read_int(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + output_vol = buffer.Read_int(); + fixed_envelope = buffer.Read_byte(); + holdnote = buffer.Read_byte(); + volume = buffer.Read_byte(); + env_vol = buffer.Read_byte(); + env_phase = buffer.Read_int(); + env_decay = buffer.Read_int(); + adder = buffer.Read_int(); + duty_flip = buffer.Read_int(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_N106.cs b/Core/VirtualNes.Core/ApuEX/APU_N106.cs new file mode 100644 index 0000000..dab850a --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_N106.cs @@ -0,0 +1,256 @@ +namespace VirtualNes.Core +{ + public class APU_N106 : APU_INTERFACE + { + CHANNEL[] op = new CHANNEL[8]; + + const int CHANNEL_VOL_SHIFT = 6; + float cpu_clock; + uint cycle_rate; + + byte addrinc; + byte address; + byte channel_use; + + byte[] tone = new byte[0x100]; + + public APU_N106() + { + // 仮設定 + cpu_clock = APU_CLOCK; + cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * 22050.0f)); + } + + public override void Reset(float fClock, int nRate) + { + for (int i = 0; i < 8; i++) + { + op[i].ZeroMemory(); + op[i].tonelen = 0x10 << 18; + } + + address = 0; + addrinc = 1; + channel_use = 8; + + Setup(fClock, nRate); + + // TONEの初期化はしない... + } + + public override void Setup(float fClock, int nRate) + { + cpu_clock = fClock; + cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * nRate)); + } + + public override void Write(ushort addr, byte data) + { + if (addr == 0x4800) + { + // tone[address*2+0] = (INT)(data&0x0F); + // tone[address*2+1] = (INT)(data >>4); + tone[address * 2 + 0] = (byte)(data & 0x0F); + tone[address * 2 + 1] = (byte)(data >> 4); + + if (address >= 0x40) + { + int no = (address - 0x40) >> 3; + uint tonelen = 0; + ref CHANNEL ch = ref op[no]; + + switch (address & 7) + { + case 0x00: + ch.freq = (uint)((ch.freq & ~0x000000FF) | data); + break; + case 0x02: + ch.freq = (uint)((ch.freq & ~0x0000FF00) | ((uint)data << 8)); + break; + case 0x04: + ch.freq = (uint)((ch.freq & ~0x00030000) | (((uint)data & 0x03) << 16)); + tonelen = (uint)((0x20 - (data & 0x1c)) << 18); + ch.databuf = (byte)((data & 0x1c) >> 2); + if (ch.tonelen != tonelen) + { + ch.tonelen = tonelen; + ch.phase = 0; + } + break; + case 0x06: + ch.toneadr = data; + break; + case 0x07: + ch.vol = (byte)(data & 0x0f); + ch.volupdate = 0xFF; + if (no == 7) + channel_use = (byte)(((data >> 4) & 0x07) + 1); + break; + } + } + + if (addrinc != 0) + { + address = (byte)((address + 1) & 0x7f); + } + } + else if (addr == 0xF800) + { + address = (byte)(data & 0x7F); + addrinc = (byte)(data & 0x80); + } + } + + public override byte Read(ushort addr) + { + // $4800 dummy read!! + if (addr == 0x0000) + { + if (addrinc != 0) + { + address = (byte)((address + 1) & 0x7F); + } + } + + return (byte)(addr >> 8); + } + + public override int Process(int channel) + { + if (channel >= (8 - channel_use) && channel < 8) + { + return ChannelRender(ref op[channel]); + } + + return 0; + } + + public override int GetFreq(int channel) + { + if (channel < 8) + { + channel &= 7; + if (channel < (8 - channel_use)) + return 0; + + ref CHANNEL ch = ref op[channel & 0x07]; + if (ch.freq == 0 || ch.vol == 0) + return 0; + int temp = channel_use * (8 - ch.databuf) * 4 * 45; + if (temp == 0) + return 0; + return (int)(256.0 * cpu_clock * 12.0 * ch.freq / ((double)0x40000 * temp)); + } + + return 0; + } + + private int ChannelRender(ref CHANNEL ch) + { + uint phasespd = (uint)(channel_use << 20); + + ch.phaseacc -= (int)cycle_rate; + if (ch.phaseacc >= 0) + { + if (ch.volupdate != 0) + { + ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT; + ch.volupdate = 0; + } + return ch.output; + } + + while (ch.phaseacc < 0) + { + ch.phaseacc += (int)phasespd; + ch.phase += ch.freq; + } + while (ch.tonelen != 0 && (ch.phase >= ch.tonelen)) + { + ch.phase -= ch.tonelen; + } + + ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT; + + return ch.output; + } + + public override uint GetSize() + { + return (uint)(3 * sizeof(byte) + 8 * op[0].GetSize() + tone.Length); + } + + public override void SaveState(StateBuffer buffer) + { + buffer.Write(addrinc); + buffer.Write(address); + buffer.Write(channel_use); + + foreach (var oneOp in op) + oneOp.SaveState(buffer); + + buffer.Write(tone); + } + + public struct CHANNEL : IStateBufferObject + { + public int phaseacc; + + public uint freq; + public uint phase; + public uint tonelen; + + public int output; + + public byte toneadr; + public byte volupdate; + + public byte vol; + public byte databuf; + + internal void ZeroMemory() + { + phaseacc = 0; + freq = 0; + phase = 0; + tonelen = 0; + output = 0; + toneadr = 0; + volupdate = 0; + vol = 0; + databuf = 0; + } + + public uint GetSize() + { + return 4 * 5 + 4; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(phase); + buffer.Write(tonelen); + buffer.Write(output); + buffer.Write(toneadr); + buffer.Write(volupdate); + buffer.Write(vol); + buffer.Write(databuf); + } + + public void LoadState(StateReader buffer) + { + phaseacc = buffer.Read_int(); + freq = buffer.Read_uint(); + phase = buffer.Read_uint(); + tonelen = buffer.Read_uint(); + output = buffer.Read_int(); + toneadr = buffer.Read_byte(); + volupdate = buffer.Read_byte(); + vol = buffer.Read_byte(); + databuf = buffer.Read_byte(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_VRC6.cs b/Core/VirtualNes.Core/ApuEX/APU_VRC6.cs new file mode 100644 index 0000000..13d2335 --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_VRC6.cs @@ -0,0 +1,383 @@ +using System; + +namespace VirtualNes.Core +{ + public class APU_VRC6 : APU_INTERFACE + { + public const int RECTANGLE_VOL_SHIFT = 8; + public const int SAWTOOTH_VOL_SHIFT = 6; + + private RECTANGLE ch0 = new RECTANGLE(); + private RECTANGLE ch1 = new RECTANGLE(); + private SAWTOOTH ch2 = new SAWTOOTH(); + + private int cycle_rate; + private float cpu_clock; + + public APU_VRC6() + { + Reset(APU_CLOCK, 22050); + } + + public override void Reset(float fClock, int nRate) + { + ch0.ZeroMemory(); + ch1.ZeroMemory(); + ch2.ZeroMemory(); + + Setup(fClock, nRate); + } + + public override void Setup(float fClock, int nRate) + { + cpu_clock = fClock; + cycle_rate = (int)(fClock * 65536.0f / nRate); + } + + public override void Write(ushort addr, byte data) + { + 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) + { + 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 = null; + 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; + } + + private 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 << 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 << 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; + } + + private 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 << 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 << SAWTOOTH_VOL_SHIFT; + num_times++; + } + ch.output_vol = (total / num_times); + } + + return ch.output_vol; + } + + public override uint GetSize() + { + return ch0.GetSize() + ch1.GetSize() + ch2.GetSize(); + } + public override void SaveState(StateBuffer p) + { + ch0.SaveState(p); + ch1.SaveState(p); + ch2.SaveState(p); + } + + public class RECTANGLE : IStateBufferObject + { + public byte[] reg = new byte[3]; + + public byte enable; + public byte gate; + public byte volume; + + public int phaseacc; + public int freq; + public int output_vol; + + public byte adder; + public byte duty_pos; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + enable = 0; + gate = 0; + volume = 0; + + phaseacc = 0; + freq = 0; + output_vol = 0; + + adder = 0; + duty_pos = 0; + } + + public uint GetSize() + { + return 20; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(gate); + buffer.Write(volume); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(output_vol); + buffer.Write(adder); + buffer.Write(duty_pos); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(3); + enable = buffer.Read_byte(); + gate = buffer.Read_byte(); + volume = buffer.Read_byte(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + output_vol = buffer.Read_int(); + adder = buffer.Read_byte(); + duty_pos = buffer.Read_byte(); + } + } + + public class SAWTOOTH : IStateBufferObject + { + public byte[] reg = new byte[3]; + + public byte enable; + public byte volume; + + public int phaseacc; + public int freq; + public int output_vol; + + public byte adder; + public byte accum; + public byte phaseaccum; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + enable = 0; + volume = 0; + + phaseacc = 0; + freq = 0; + output_vol = 0; + + adder = 0; + accum = 0; + phaseaccum = 0; + } + + public uint GetSize() + { + return 20; + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(reg); + buffer.Write(enable); + buffer.Write(volume); + buffer.Write(phaseacc); + buffer.Write(freq); + buffer.Write(output_vol); + buffer.Write(adder); + buffer.Write(accum); + buffer.Write(phaseaccum); + } + + public void LoadState(StateReader buffer) + { + reg = buffer.Read_bytes(3); + enable = buffer.Read_byte(); + volume = buffer.Read_byte(); + phaseacc = buffer.Read_int(); + freq = buffer.Read_int(); + output_vol = buffer.Read_int(); + adder = buffer.Read_byte(); + accum = buffer.Read_byte(); + phaseaccum = buffer.Read_byte(); + } + } + } +} diff --git a/Core/VirtualNes.Core/ApuEX/APU_VRC7.cs b/Core/VirtualNes.Core/ApuEX/APU_VRC7.cs new file mode 100644 index 0000000..c0bcc13 --- /dev/null +++ b/Core/VirtualNes.Core/ApuEX/APU_VRC7.cs @@ -0,0 +1,106 @@ +using System; +using VirtualNes.Core.Emu2413; + +namespace VirtualNes.Core +{ + public class APU_VRC7 : APU_INTERFACE + { + OPLL VRC7_OPLL; + byte address; + + public APU_VRC7() + { + Emu2413API.OPLL_init(3579545, 22050); // 仮のサンプリングレート + VRC7_OPLL = Emu2413API.OPLL_new(); + + if (VRC7_OPLL != null) + { + Emu2413API.OPLL_reset(VRC7_OPLL); + Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE); + VRC7_OPLL.masterVolume = 128; + } + + // 仮設定 + Reset(APU_CLOCK, 22050); + } + + public override void Dispose() + { + if (VRC7_OPLL != null) + { + Emu2413API.OPLL_delete(VRC7_OPLL); + VRC7_OPLL = null; + // OPLL_close(); // 無くても良い(中身無し) + } + } + + public override void Reset(float fClock, int nRate) + { + if (VRC7_OPLL != null) + { + Emu2413API.OPLL_reset(VRC7_OPLL); + Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE); + VRC7_OPLL.masterVolume = 128; + } + + address = 0; + + Setup(fClock, nRate); + } + + public override void Setup(float fClock, int nRate) + { + Emu2413API.OPLL_setClock((UInt32)(fClock * 2.0f), (UInt32)nRate); + } + + public override void Write(ushort addr, byte data) + { + if (VRC7_OPLL != null) + { + if (addr == 0x9010) + { + address = data; + } + else if (addr == 0x9030) + { + Emu2413API.OPLL_writeReg(VRC7_OPLL, address, data); + } + } + } + + public override int Process(int channel) + { + if (VRC7_OPLL != null) + return Emu2413API.OPLL_calc(VRC7_OPLL); + + return 0; + } + + float[] blkmul = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f }; + public override int GetFreq(int channel) + { + if (VRC7_OPLL != null && channel < 8) + { + int fno = ((VRC7_OPLL.reg[0x20 + channel] & 0x01) << 8) + VRC7_OPLL.reg[0x10 + channel]; + int blk = (VRC7_OPLL.reg[0x20 + channel] >> 1) & 0x07; + + if ((VRC7_OPLL.reg[0x20 + channel] & 0x10) != 0) + { + return (int)((256.0d * fno * blkmul[blk]) / ((1 << 18) / (3579545.0 / 72.0))); + } + } + + return 0; + } + + public override uint GetSize() + { + return 0; + } + + public override void SaveState(StateBuffer buffer) + { + //not impl + } + } +} diff --git a/Core/VirtualNes.Core/CPU.cs b/Core/VirtualNes.Core/CPU.cs new file mode 100644 index 0000000..ef9acbb --- /dev/null +++ b/Core/VirtualNes.Core/CPU.cs @@ -0,0 +1,2084 @@ +#undef DPCM_SYNCCLOCK + +using System; + +namespace VirtualNes.Core +{ + public class CPU + { + private static int nmicount; + + // 6502 status flags + public const byte C_FLAG = 0x01; // 1: Carry + public const byte Z_FLAG = 0x02; // 1: Zero + public const byte I_FLAG = 0x04; // 1: Irq disabled + public const byte D_FLAG = 0x08; // 1: Decimal mode flag (NES unused) + public const byte B_FLAG = 0x10; // 1: Break + public const byte R_FLAG = 0x20; // 1: Reserved (Always 1) + public const byte V_FLAG = 0x40; // 1: Overflow + public const byte N_FLAG = 0x80; // 1: Negative + + // Interrupt + public const byte NMI_FLAG = 0x01; + public const byte IRQ_FLAG = 0x02; + + public const byte IRQ_FRAMEIRQ = 0x04; + public const byte IRQ_DPCM = 0x08; + public const byte IRQ_MAPPER = 0x10; + public const byte IRQ_MAPPER2 = 0x20; + public const byte IRQ_TRIGGER = 0x40; // one shot(媽IRQ()) + public const byte IRQ_TRIGGER2 = 0x80; // one shot(媽IRQ_NotPending()) + + public static readonly byte IRQ_MASK = unchecked((byte)(~(NMI_FLAG | IRQ_FLAG))); + + // Vector + public const ushort NMI_VECTOR = 0xFFFA; + public const ushort RES_VECTOR = 0xFFFC; + public const ushort IRQ_VECTOR = 0xFFFE; + + private NES nes; + private bool m_bClockProcess; + private int TOTAL_cycles; + private int DMA_cycles; + private Mapper mapper; + private APU apu; + internal R6502 R = new R6502(); + private byte[] ZN_Table = new byte[256]; + private ArrayRef STACK; + + public CPU(NES parent) + { + nes = parent; + m_bClockProcess = false; + + } + + public void Dispose() { } + + ushort EA = 0; + ushort ET = 0; + ushort WT = 0; + byte DT = 0; + int exec_cycles = 0; + + internal long EXEC(int request_cycles) + { + byte opcode = 0; + int OLD_cycles = TOTAL_cycles; + byte nmi_request = 0, irq_request = 0; + bool bClockProcess = m_bClockProcess; + + exec_cycles = 0; + EA = 0; + ET = 0; + WT = 0; + DT = 0; + + while (request_cycles > 0) + { + exec_cycles = 0; + if (DMA_cycles > 0) + { + if (request_cycles <= DMA_cycles) + { + DMA_cycles -= request_cycles; + TOTAL_cycles += request_cycles; + + mapper.Clock(request_cycles); +#if DPCM_SYNCCLOCK + apu.SyncDPCM(request_cycles); +#endif + if (m_bClockProcess) + { + nes.Clock(request_cycles); + } + + goto _execute_exit; + } + else + { + exec_cycles += DMA_cycles; + DMA_cycles = 0; + } + } + + nmi_request = irq_request = 0; + opcode = OP6502(R.PC++); + + if (R.INT_pending != 0) + { + if ((R.INT_pending & NMI_FLAG) != 0) + { + nmi_request = 0xFF; + byte temp = unchecked((byte)(~NMI_FLAG)); + R.INT_pending &= temp; + } + else if ((R.INT_pending & IRQ_MASK) != 0) + { + byte temp = unchecked((byte)(~IRQ_TRIGGER2)); + R.INT_pending &= temp; + if ( + ((R.P & I_FLAG) == 0) + && + (opcode != 0x40) + ) + { + irq_request = 0xFF; + temp = unchecked((byte)(~IRQ_TRIGGER)); + R.INT_pending &= temp; + } + } + } + + switch (opcode) + { + case 0x69: // ADC #$?? + MR_IM(); ADC(); + ADD_CYCLE(2); + break; + case 0x65: // ADC $?? + MR_ZP(); ADC(); + ADD_CYCLE(3); + break; + case 0x75: // ADC $??,X + MR_ZX(); ADC(); + ADD_CYCLE(4); + break; + case 0x6D: // ADC $???? + MR_AB(); ADC(); + ADD_CYCLE(4); + break; + case 0x7D: // ADC $????,X + MR_AX(); ADC(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x79: // ADC $????,Y + MR_AY(); ADC(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x61: // ADC ($??,X) + MR_IX(); ADC(); + ADD_CYCLE(6); + break; + case 0x71: // ADC ($??),Y + MR_IY(); ADC(); CHECK_EA(); + ADD_CYCLE(4); + break; + + case 0xE9: // SBC #$?? + MR_IM(); SBC(); + ADD_CYCLE(2); + break; + case 0xE5: // SBC $?? + MR_ZP(); SBC(); + ADD_CYCLE(3); + break; + case 0xF5: // SBC $??,X + MR_ZX(); SBC(); + ADD_CYCLE(4); + break; + case 0xED: // SBC $???? + MR_AB(); SBC(); + ADD_CYCLE(4); + break; + case 0xFD: // SBC $????,X + MR_AX(); SBC(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xF9: // SBC $????,Y + MR_AY(); SBC(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xE1: // SBC ($??,X) + MR_IX(); SBC(); + ADD_CYCLE(6); + break; + case 0xF1: // SBC ($??),Y + MR_IY(); SBC(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0xC6: // DEC $?? + MR_ZP(); DEC(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0xD6: // DEC $??,X + MR_ZX(); DEC(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0xCE: // DEC $???? + MR_AB(); DEC(); MW_EA(); + ADD_CYCLE(6); + break; + case 0xDE: // DEC $????,X + MR_AX(); DEC(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0xCA: // DEX + DEX(); + ADD_CYCLE(2); + break; + case 0x88: // DEY + DEY(); + ADD_CYCLE(2); + break; + + case 0xE6: // INC $?? + MR_ZP(); INC(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0xF6: // INC $??,X + MR_ZX(); INC(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0xEE: // INC $???? + MR_AB(); INC(); MW_EA(); + ADD_CYCLE(6); + break; + case 0xFE: // INC $????,X + MR_AX(); INC(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0xE8: // INX + INX(); + ADD_CYCLE(2); + break; + case 0xC8: // INY + INY(); + ADD_CYCLE(2); + break; + + case 0x29: // AND #$?? + MR_IM(); AND(); + ADD_CYCLE(2); + break; + case 0x25: // AND $?? + MR_ZP(); AND(); + ADD_CYCLE(3); + break; + case 0x35: // AND $??,X + MR_ZX(); AND(); + ADD_CYCLE(4); + break; + case 0x2D: // AND $???? + MR_AB(); AND(); + ADD_CYCLE(4); + break; + case 0x3D: // AND $????,X + MR_AX(); AND(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x39: // AND $????,Y + MR_AY(); AND(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x21: // AND ($??,X) + MR_IX(); AND(); + ADD_CYCLE(6); + break; + case 0x31: // AND ($??),Y + MR_IY(); AND(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0x0A: // ASL A + ASL_A(); + ADD_CYCLE(2); + break; + case 0x06: // ASL $?? + MR_ZP(); ASL(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x16: // ASL $??,X + MR_ZX(); ASL(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x0E: // ASL $???? + MR_AB(); ASL(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x1E: // ASL $????,X + MR_AX(); ASL(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0x24: // BIT $?? + MR_ZP(); BIT(); + ADD_CYCLE(3); + break; + case 0x2C: // BIT $???? + MR_AB(); BIT(); + ADD_CYCLE(4); + break; + + case 0x49: // EOR #$?? + MR_IM(); EOR(); + ADD_CYCLE(2); + break; + case 0x45: // EOR $?? + MR_ZP(); EOR(); + ADD_CYCLE(3); + break; + case 0x55: // EOR $??,X + MR_ZX(); EOR(); + ADD_CYCLE(4); + break; + case 0x4D: // EOR $???? + MR_AB(); EOR(); + ADD_CYCLE(4); + break; + case 0x5D: // EOR $????,X + MR_AX(); EOR(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x59: // EOR $????,Y + MR_AY(); EOR(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x41: // EOR ($??,X) + MR_IX(); EOR(); + ADD_CYCLE(6); + break; + case 0x51: // EOR ($??),Y + MR_IY(); EOR(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0x4A: // LSR A + LSR_A(); + ADD_CYCLE(2); + break; + case 0x46: // LSR $?? + MR_ZP(); LSR(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x56: // LSR $??,X + MR_ZX(); LSR(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x4E: // LSR $???? + MR_AB(); LSR(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x5E: // LSR $????,X + MR_AX(); LSR(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0x09: // ORA #$?? + MR_IM(); ORA(); + ADD_CYCLE(2); + break; + case 0x05: // ORA $?? + MR_ZP(); ORA(); + ADD_CYCLE(3); + break; + case 0x15: // ORA $??,X + MR_ZX(); ORA(); + ADD_CYCLE(4); + break; + case 0x0D: // ORA $???? + MR_AB(); ORA(); + ADD_CYCLE(4); + break; + case 0x1D: // ORA $????,X + MR_AX(); ORA(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x19: // ORA $????,Y + MR_AY(); ORA(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0x01: // ORA ($??,X) + MR_IX(); ORA(); + ADD_CYCLE(6); + break; + case 0x11: // ORA ($??),Y + MR_IY(); ORA(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0x2A: // ROL A + ROL_A(); + ADD_CYCLE(2); + break; + case 0x26: // ROL $?? + MR_ZP(); ROL(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x36: // ROL $??,X + MR_ZX(); ROL(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x2E: // ROL $???? + MR_AB(); ROL(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x3E: // ROL $????,X + MR_AX(); ROL(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0x6A: // ROR A + ROR_A(); + ADD_CYCLE(2); + break; + case 0x66: // ROR $?? + MR_ZP(); ROR(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x76: // ROR $??,X + MR_ZX(); ROR(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x6E: // ROR $???? + MR_AB(); ROR(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x7E: // ROR $????,X + MR_AX(); ROR(); MW_EA(); + ADD_CYCLE(7); + break; + + case 0xA9: // LDA #$?? + MR_IM(); LDA(); + ADD_CYCLE(2); + break; + case 0xA5: // LDA $?? + MR_ZP(); LDA(); + ADD_CYCLE(3); + break; + case 0xB5: // LDA $??,X + MR_ZX(); LDA(); + ADD_CYCLE(4); + break; + case 0xAD: // LDA $???? + MR_AB(); LDA(); + ADD_CYCLE(4); + break; + case 0xBD: // LDA $????,X + MR_AX(); LDA(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xB9: // LDA $????,Y + MR_AY(); LDA(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xA1: // LDA ($??,X) + MR_IX(); LDA(); + ADD_CYCLE(6); + break; + case 0xB1: // LDA ($??),Y + MR_IY(); LDA(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0xA2: // LDX #$?? + MR_IM(); LDX(); + ADD_CYCLE(2); + break; + case 0xA6: // LDX $?? + MR_ZP(); LDX(); + ADD_CYCLE(3); + break; + case 0xB6: // LDX $??,Y + MR_ZY(); LDX(); + ADD_CYCLE(4); + break; + case 0xAE: // LDX $???? + MR_AB(); LDX(); + ADD_CYCLE(4); + break; + case 0xBE: // LDX $????,Y + MR_AY(); LDX(); CHECK_EA(); + ADD_CYCLE(4); + break; + + case 0xA0: // LDY #$?? + MR_IM(); LDY(); + ADD_CYCLE(2); + break; + case 0xA4: // LDY $?? + MR_ZP(); LDY(); + ADD_CYCLE(3); + break; + case 0xB4: // LDY $??,X + MR_ZX(); LDY(); + ADD_CYCLE(4); + break; + case 0xAC: // LDY $???? + MR_AB(); LDY(); + ADD_CYCLE(4); + break; + case 0xBC: // LDY $????,X + MR_AX(); LDY(); CHECK_EA(); + ADD_CYCLE(4); + break; + + case 0x85: // STA $?? + EA_ZP(); STA(); MW_ZP(); + ADD_CYCLE(3); + break; + case 0x95: // STA $??,X + EA_ZX(); STA(); MW_ZP(); + ADD_CYCLE(4); + break; + case 0x8D: // STA $???? + EA_AB(); STA(); MW_EA(); + ADD_CYCLE(4); + break; + case 0x9D: // STA $????,X + EA_AX(); STA(); MW_EA(); + ADD_CYCLE(5); + break; + case 0x99: // STA $????,Y + EA_AY(); STA(); MW_EA(); + ADD_CYCLE(5); + break; + case 0x81: // STA ($??,X) + EA_IX(); STA(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x91: // STA ($??),Y + EA_IY(); STA(); MW_EA(); + ADD_CYCLE(6); + break; + + case 0x86: // STX $?? + EA_ZP(); STX(); MW_ZP(); + ADD_CYCLE(3); + break; + case 0x96: // STX $??,Y + EA_ZY(); STX(); MW_ZP(); + ADD_CYCLE(4); + break; + case 0x8E: // STX $???? + EA_AB(); STX(); MW_EA(); + ADD_CYCLE(4); + break; + + case 0x84: // STY $?? + EA_ZP(); STY(); MW_ZP(); + ADD_CYCLE(3); + break; + case 0x94: // STY $??,X + EA_ZX(); STY(); MW_ZP(); + ADD_CYCLE(4); + break; + case 0x8C: // STY $???? + EA_AB(); STY(); MW_EA(); + ADD_CYCLE(4); + break; + + case 0xAA: // TAX + TAX(); + ADD_CYCLE(2); + break; + case 0x8A: // TXA + TXA(); + ADD_CYCLE(2); + break; + case 0xA8: // TAY + TAY(); + ADD_CYCLE(2); + break; + case 0x98: // TYA + TYA(); + ADD_CYCLE(2); + break; + case 0xBA: // TSX + TSX(); + ADD_CYCLE(2); + break; + case 0x9A: // TXS + TXS(); + ADD_CYCLE(2); + break; + + case 0xC9: // CMP #$?? + MR_IM(); CMP_(); + ADD_CYCLE(2); + break; + case 0xC5: // CMP $?? + MR_ZP(); CMP_(); + ADD_CYCLE(3); + break; + case 0xD5: // CMP $??,X + MR_ZX(); CMP_(); + ADD_CYCLE(4); + break; + case 0xCD: // CMP $???? + MR_AB(); CMP_(); + ADD_CYCLE(4); + break; + case 0xDD: // CMP $????,X + MR_AX(); CMP_(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xD9: // CMP $????,Y + MR_AY(); CMP_(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xC1: // CMP ($??,X) + MR_IX(); CMP_(); + ADD_CYCLE(6); + break; + case 0xD1: // CMP ($??),Y + MR_IY(); CMP_(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0xE0: // CPX #$?? + MR_IM(); CPX(); + ADD_CYCLE(2); + break; + case 0xE4: // CPX $?? + MR_ZP(); CPX(); + ADD_CYCLE(3); + break; + case 0xEC: // CPX $???? + MR_AB(); CPX(); + ADD_CYCLE(4); + break; + + case 0xC0: // CPY #$?? + MR_IM(); CPY(); + ADD_CYCLE(2); + break; + case 0xC4: // CPY $?? + MR_ZP(); CPY(); + ADD_CYCLE(3); + break; + case 0xCC: // CPY $???? + MR_AB(); CPY(); + ADD_CYCLE(4); + break; + + case 0x90: // BCC + MR_IM(); BCC(); + ADD_CYCLE(2); + break; + case 0xB0: // BCS + MR_IM(); BCS(); + ADD_CYCLE(2); + break; + case 0xF0: // BEQ + MR_IM(); BEQ(); + ADD_CYCLE(2); + break; + case 0x30: // BMI + MR_IM(); BMI(); + ADD_CYCLE(2); + break; + case 0xD0: // BNE + MR_IM(); BNE(); + ADD_CYCLE(2); + break; + case 0x10: // BPL + MR_IM(); BPL(); + ADD_CYCLE(2); + break; + case 0x50: // BVC + MR_IM(); BVC(); + ADD_CYCLE(2); + break; + case 0x70: // BVS + MR_IM(); BVS(); + ADD_CYCLE(2); + break; + + case 0x4C: // JMP $???? + JMP(); + ADD_CYCLE(3); + break; + case 0x6C: // JMP ($????) + JMP_ID(); + ADD_CYCLE(5); + break; + + case 0x20: // JSR + JSR(); + ADD_CYCLE(6); + break; + + case 0x40: // RTI + RTI(); + ADD_CYCLE(6); + break; + case 0x60: // RTS + RTS(); + ADD_CYCLE(6); + break; + + // フラグ制御系 + case 0x18: // CLC + CLC(); + ADD_CYCLE(2); + break; + case 0xD8: // CLD + CLD(); + ADD_CYCLE(2); + break; + case 0x58: // CLI + CLI(); + ADD_CYCLE(2); + break; + case 0xB8: // CLV + CLV(); + ADD_CYCLE(2); + break; + + case 0x38: // SEC + SEC(); + ADD_CYCLE(2); + break; + case 0xF8: // SED + SED(); + ADD_CYCLE(2); + break; + case 0x78: // SEI + SEI(); + ADD_CYCLE(2); + break; + + // スタック系 + case 0x48: // PHA + PUSH(R.A); + ADD_CYCLE(3); + break; + case 0x08: // PHP + PUSH((byte)(R.P | B_FLAG)); + ADD_CYCLE(3); + break; + case 0x68: // PLA (N-----Z-) + R.A = POP(); + SET_ZN_FLAG(R.A); + ADD_CYCLE(4); + break; + case 0x28: // PLP + R.P = (byte)(POP() | R_FLAG); + ADD_CYCLE(4); + break; + + // その他 + case 0x00: // BRK + BRK(); + ADD_CYCLE(7); + break; + + case 0xEA: // NOP + ADD_CYCLE(2); + break; + + // 未公開命令群 + case 0x0B: // ANC #$?? + case 0x2B: // ANC #$?? + MR_IM(); ANC(); + ADD_CYCLE(2); + break; + + case 0x8B: // ANE #$?? + MR_IM(); ANE(); + ADD_CYCLE(2); + break; + + case 0x6B: // ARR #$?? + MR_IM(); ARR(); + ADD_CYCLE(2); + break; + + case 0x4B: // ASR #$?? + MR_IM(); ASR(); + ADD_CYCLE(2); + break; + + case 0xC7: // DCP $?? + MR_ZP(); DCP(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0xD7: // DCP $??,X + MR_ZX(); DCP(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0xCF: // DCP $???? + MR_AB(); DCP(); MW_EA(); + ADD_CYCLE(6); + break; + case 0xDF: // DCP $????,X + MR_AX(); DCP(); MW_EA(); + ADD_CYCLE(7); + break; + case 0xDB: // DCP $????,Y + MR_AY(); DCP(); MW_EA(); + ADD_CYCLE(7); + break; + case 0xC3: // DCP ($??,X) + MR_IX(); DCP(); MW_EA(); + ADD_CYCLE(8); + break; + case 0xD3: // DCP ($??),Y + MR_IY(); DCP(); MW_EA(); + ADD_CYCLE(8); + break; + + case 0xE7: // ISB $?? + MR_ZP(); ISB(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0xF7: // ISB $??,X + MR_ZX(); ISB(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0xEF: // ISB $???? + MR_AB(); ISB(); MW_EA(); + ADD_CYCLE(5); + break; + case 0xFF: // ISB $????,X + MR_AX(); ISB(); MW_EA(); + ADD_CYCLE(5); + break; + case 0xFB: // ISB $????,Y + MR_AY(); ISB(); MW_EA(); + ADD_CYCLE(5); + break; + case 0xE3: // ISB ($??,X) + MR_IX(); ISB(); MW_EA(); + ADD_CYCLE(5); + break; + case 0xF3: // ISB ($??),Y + MR_IY(); ISB(); MW_EA(); + ADD_CYCLE(5); + break; + + case 0xBB: // LAS $????,Y + MR_AY(); LAS(); CHECK_EA(); + ADD_CYCLE(4); + break; + + + case 0xA7: // LAX $?? + MR_ZP(); LAX(); + ADD_CYCLE(3); + break; + case 0xB7: // LAX $??,Y + MR_ZY(); LAX(); + ADD_CYCLE(4); + break; + case 0xAF: // LAX $???? + MR_AB(); LAX(); + ADD_CYCLE(4); + break; + case 0xBF: // LAX $????,Y + MR_AY(); LAX(); CHECK_EA(); + ADD_CYCLE(4); + break; + case 0xA3: // LAX ($??,X) + MR_IX(); LAX(); + ADD_CYCLE(6); + break; + case 0xB3: // LAX ($??),Y + MR_IY(); LAX(); CHECK_EA(); + ADD_CYCLE(5); + break; + + case 0xAB: // LXA #$?? + MR_IM(); LXA(); + ADD_CYCLE(2); + break; + + case 0x27: // RLA $?? + MR_ZP(); RLA(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x37: // RLA $??,X + MR_ZX(); RLA(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x2F: // RLA $???? + MR_AB(); RLA(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x3F: // RLA $????,X + MR_AX(); RLA(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x3B: // RLA $????,Y + MR_AY(); RLA(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x23: // RLA ($??,X) + MR_IX(); RLA(); MW_EA(); + ADD_CYCLE(8); + break; + case 0x33: // RLA ($??),Y + MR_IY(); RLA(); MW_EA(); + ADD_CYCLE(8); + break; + + case 0x67: // RRA $?? + MR_ZP(); RRA(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x77: // RRA $??,X + MR_ZX(); RRA(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x6F: // RRA $???? + MR_AB(); RRA(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x7F: // RRA $????,X + MR_AX(); RRA(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x7B: // RRA $????,Y + MR_AY(); RRA(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x63: // RRA ($??,X) + MR_IX(); RRA(); MW_EA(); + ADD_CYCLE(8); + break; + case 0x73: // RRA ($??),Y + MR_IY(); RRA(); MW_EA(); + ADD_CYCLE(8); + break; + + case 0x87: // SAX $?? + MR_ZP(); SAX(); MW_ZP(); + ADD_CYCLE(3); + break; + case 0x97: // SAX $??,Y + MR_ZY(); SAX(); MW_ZP(); + ADD_CYCLE(4); + break; + case 0x8F: // SAX $???? + MR_AB(); SAX(); MW_EA(); + ADD_CYCLE(4); + break; + case 0x83: // SAX ($??,X) + MR_IX(); SAX(); MW_EA(); + ADD_CYCLE(6); + break; + + case 0xCB: // SBX #$?? + MR_IM(); SBX(); + ADD_CYCLE(2); + break; + + case 0x9F: // SHA $????,Y + MR_AY(); SHA(); MW_EA(); + ADD_CYCLE(5); + break; + case 0x93: // SHA ($??),Y + MR_IY(); SHA(); MW_EA(); + ADD_CYCLE(6); + break; + + case 0x9B: // SHS $????,Y + MR_AY(); SHS(); MW_EA(); + ADD_CYCLE(5); + break; + + case 0x9E: // SHX $????,Y + MR_AY(); SHX(); MW_EA(); + ADD_CYCLE(5); + break; + + case 0x9C: // SHY $????,X + MR_AX(); SHY(); MW_EA(); + ADD_CYCLE(5); + break; + + case 0x07: // SLO $?? + MR_ZP(); SLO(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x17: // SLO $??,X + MR_ZX(); SLO(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x0F: // SLO $???? + MR_AB(); SLO(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x1F: // SLO $????,X + MR_AX(); SLO(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x1B: // SLO $????,Y + MR_AY(); SLO(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x03: // SLO ($??,X) + MR_IX(); SLO(); MW_EA(); + ADD_CYCLE(8); + break; + case 0x13: // SLO ($??),Y + MR_IY(); SLO(); MW_EA(); + ADD_CYCLE(8); + break; + + case 0x47: // SRE $?? + MR_ZP(); SRE(); MW_ZP(); + ADD_CYCLE(5); + break; + case 0x57: // SRE $??,X + MR_ZX(); SRE(); MW_ZP(); + ADD_CYCLE(6); + break; + case 0x4F: // SRE $???? + MR_AB(); SRE(); MW_EA(); + ADD_CYCLE(6); + break; + case 0x5F: // SRE $????,X + MR_AX(); SRE(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x5B: // SRE $????,Y + MR_AY(); SRE(); MW_EA(); + ADD_CYCLE(7); + break; + case 0x43: // SRE ($??,X) + MR_IX(); SRE(); MW_EA(); + ADD_CYCLE(8); + break; + case 0x53: // SRE ($??),Y + MR_IY(); SRE(); MW_EA(); + ADD_CYCLE(8); + break; + + case 0xEB: // SBC #$?? (Unofficial) + MR_IM(); SBC(); + ADD_CYCLE(2); + break; + + case 0x1A: // NOP (Unofficial) + case 0x3A: // NOP (Unofficial) + case 0x5A: // NOP (Unofficial) + case 0x7A: // NOP (Unofficial) + case 0xDA: // NOP (Unofficial) + case 0xFA: // NOP (Unofficial) + ADD_CYCLE(2); + break; + case 0x80: // DOP (CYCLES 2) + case 0x82: // DOP (CYCLES 2) + case 0x89: // DOP (CYCLES 2) + case 0xC2: // DOP (CYCLES 2) + case 0xE2: // DOP (CYCLES 2) + R.PC++; + ADD_CYCLE(2); + break; + case 0x04: // DOP (CYCLES 3) + case 0x44: // DOP (CYCLES 3) + case 0x64: // DOP (CYCLES 3) + R.PC++; + ADD_CYCLE(3); + break; + case 0x14: // DOP (CYCLES 4) + case 0x34: // DOP (CYCLES 4) + case 0x54: // DOP (CYCLES 4) + case 0x74: // DOP (CYCLES 4) + case 0xD4: // DOP (CYCLES 4) + case 0xF4: // DOP (CYCLES 4) + R.PC++; + ADD_CYCLE(4); + break; + case 0x0C: // TOP + case 0x1C: // TOP + case 0x3C: // TOP + case 0x5C: // TOP + case 0x7C: // TOP + case 0xDC: // TOP + case 0xFC: // TOP + R.PC += 2; + ADD_CYCLE(4); + break; + + case 0x02: /* JAM */ + case 0x12: /* JAM */ + case 0x22: /* JAM */ + case 0x32: /* JAM */ + case 0x42: /* JAM */ + case 0x52: /* JAM */ + case 0x62: /* JAM */ + case 0x72: /* JAM */ + case 0x92: /* JAM */ + case 0xB2: /* JAM */ + case 0xD2: /* JAM */ + case 0xF2: /* JAM */ + default: + if (!Supporter.Config.emulator.bIllegalOp) + { + throw new Exception("IllegalOp"); + } + else + { + R.PC--; + ADD_CYCLE(4); + } + break; + // default: + // __assume(0); + } + + if (nmi_request != 0) + { + _NMI(); + } + else + if (irq_request != 0) + { + _IRQ(); + } + + request_cycles -= exec_cycles; + TOTAL_cycles += exec_cycles; + + mapper.Clock(exec_cycles); +#if DPCM_SYNCCLOCK + apu->SyncDPCM( exec_cycles ); +#endif + if (bClockProcess) + { + nes.Clock(exec_cycles); + } + } + _execute_exit: +#if !DPCM_SYNCCLOCK + apu.SyncDPCM(TOTAL_cycles - OLD_cycles); +#endif + return TOTAL_cycles - OLD_cycles; + } + private void _IRQ() + { + PUSH((byte)(R.PC >> 8)); + PUSH((byte)(R.PC & 0xFF)); + CLR_FLAG(B_FLAG); + PUSH(R.P); + SET_FLAG(I_FLAG); + R.PC = RD6502W(IRQ_VECTOR); + exec_cycles += 7; + } + + + private ushort RD6502W(ushort addr) + { + if (addr < 0x2000) + { + // RAM (Mirror $0800, $1000, $1800) + return BitConverter.ToUInt16(MMU.RAM, addr & 0x07FF); + } + else if (addr < 0x8000) + { + // Others + return (ushort)(nes.Read(addr) + nes.Read((ushort)(addr + 1)) * 0x100); + } + + var temp = MMU.CPU_MEM_BANK[addr >> 13]; + shortTemp[0] = temp[addr & 0x1FFF]; + shortTemp[1] = temp[(addr & 0x1FFF) + 1]; + return BitConverter.ToUInt16(shortTemp, 0); + } + + private void SET_FLAG(byte V) + { + R.P |= (V); + } + + private void CLR_FLAG(byte V) + { + var temp = (byte)(~V); + R.P &= temp; + } + + private void _NMI() + { + PUSH((byte)(R.PC >> 8)); + PUSH((byte)(R.PC & 0xFF)); + CLR_FLAG(B_FLAG); + PUSH(R.P); + SET_FLAG(I_FLAG); + R.PC = RD6502W(NMI_VECTOR); + exec_cycles += 7; + } + + private void SRE() + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT >>= 1; + R.A ^= DT; + SET_ZN_FLAG(R.A); + } + + private void SLO() + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT <<= 1; + R.A |= DT; + SET_ZN_FLAG(R.A); + } + + private void SHY() + { + DT = (byte)(R.Y & ((EA >> 8) + 1)); + } + + private void SHX() + { + DT = (byte)(R.X & ((EA >> 8) + 1)); + } + + private void SHS() + { + R.S = (byte)(R.A & R.X); + DT = (byte)(R.S & ((EA >> 8) + 1)); + } + + private void SHA() + { + DT = (byte)(R.A & R.X & ((EA >> 8) + 1)); + } + + private void SBX() + { + WT = (ushort)((R.A & R.X) - DT); + TST_FLAG(WT < 0x100, C_FLAG); + R.X = (byte)(WT & 0xFF); + SET_ZN_FLAG(R.X); + } + + private void SAX() + { + DT = (byte)(R.A & R.X); + } + + private void RRA() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT = (byte)((DT >> 1) | 0x80); + } + else + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT >>= 1; + } + ADC(); + } + + private void RLA() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT = (byte)((DT << 1) | 1); + } + else + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT <<= 1; + } + R.A &= DT; + SET_ZN_FLAG(R.A); + } + + private void LXA() + { + R.A = R.X = (byte)((R.A | 0xEE) & DT); + SET_ZN_FLAG(R.A); + } + + private void LAX() + { + R.A = DT; + R.X = R.A; + SET_ZN_FLAG(R.A); + } + + private void LAS() + { + R.A = R.X = R.S = (byte)(R.S & DT); + SET_ZN_FLAG(R.A); + } + + private void ISB() + { + DT++; + SBC(); + } + + private void DCP() + { + DT--; + CMP_(); + } + + private void ASR() + { + DT &= R.A; + TST_FLAG((DT & 0x01) != 0, C_FLAG); + R.A = (byte)(DT >> 1); + SET_ZN_FLAG(R.A); + } + + private void ARR() + { + DT &= R.A; + R.A = (byte)((DT >> 1) | ((R.P & C_FLAG) << 7)); + SET_ZN_FLAG(R.A); + TST_FLAG((R.A & 0x40) != 0, C_FLAG); + TST_FLAG(((R.A >> 6) ^ (R.A >> 5)) != 0, V_FLAG); + } + + private void ANE() + { + R.A = (byte)((R.A | 0xEE) & R.X & DT); + SET_ZN_FLAG(R.A); + } + + private void ANC() + { + R.A &= DT; + SET_ZN_FLAG(R.A); + TST_FLAG((R.P & N_FLAG) != 0, C_FLAG); + } + + private void BRK() + { + R.PC++; + PUSH((byte)(R.PC >> 8)); + PUSH((byte)(R.PC & 0xFF)); + SET_FLAG(B_FLAG); + PUSH(R.P); + SET_FLAG(I_FLAG); + R.PC = RD6502W(IRQ_VECTOR); + } + + private byte POP() + { + return STACK[(++R.S) & 0xFF]; + } + + private void PUSH(byte V) + { + STACK[(R.S--) & 0xFF] = V; + } + + private void SEI() + { + R.P |= I_FLAG; + } + + private void SED() + { + R.P |= D_FLAG; + } + + private void SEC() + { + R.P |= C_FLAG; + } + + private void CLV() + { + var temp = unchecked((byte)(~V_FLAG)); + R.P &= temp; + } + + private void CLI() + { + var temp = unchecked((byte)(~I_FLAG)); + R.P &= temp; + } + + private void CLD() + { + var temp = unchecked((byte)(~D_FLAG)); + R.P &= temp; + } + + private void CLC() + { + var temp = unchecked((byte)(~C_FLAG)); + R.P &= temp; + } + + private void RTS() + { + R.PC = POP(); + R.PC |= (ushort)(POP() * 0x0100); + R.PC++; + } + + private void RTI() + { + R.P = (byte)(POP() | R_FLAG); + R.PC = POP(); + R.PC |= (ushort)(POP() * 0x0100); + } + + private void JSR() + { + EA = OP6502W(R.PC); + R.PC++; + PUSH((byte)(R.PC >> 8)); + PUSH((byte)(R.PC & 0xFF)); + R.PC = EA; + } + + private void JMP_ID() + { + WT = OP6502W(R.PC); + EA = RD6502(WT); + WT = (ushort)((WT & 0xFF00) | ((WT + 1) & 0x00FF)); + R.PC = (ushort)(EA + RD6502(WT) * 0x100); + } + + private void JMP() + { + R.PC = OP6502W(R.PC); + } + + private void BVS() + { + if ((R.P & V_FLAG) != 0) + REL_JUMP(); + } + + private void REL_JUMP() + { + ET = R.PC; + EA = (ushort)(R.PC + (sbyte)DT); + R.PC = EA; + ADD_CYCLE(1); + CHECK_EA(); + } + + private void BVC() + { + if ((R.P & V_FLAG) == 0) REL_JUMP(); + } + + private void BPL() + { + if ((R.P & N_FLAG) == 0) REL_JUMP(); + } + + private void BNE() + { + if ((R.P & Z_FLAG) == 0) REL_JUMP(); + } + + private void BMI() + { + if ((R.P & N_FLAG) != 0) REL_JUMP(); + } + + private void BEQ() + { + if ((R.P & Z_FLAG) != 0) REL_JUMP(); + } + + private void BCS() + { + if ((R.P & C_FLAG) != 0) REL_JUMP(); + } + + private void BCC() + { + if ((R.P & C_FLAG) == 0) REL_JUMP(); + } + + private void CPY() + { + WT = (ushort)(R.Y - DT); + TST_FLAG((WT & 0x8000) == 0, C_FLAG); + SET_ZN_FLAG((byte)WT); + } + + private void CPX() + { + WT = (ushort)(R.X - DT); + TST_FLAG((WT & 0x8000) == 0, C_FLAG); + SET_ZN_FLAG((byte)WT); + } + + private void CMP_() + { + WT = (ushort)(R.A - DT); + TST_FLAG((WT & 0x8000) == 0, C_FLAG); + SET_ZN_FLAG((byte)WT); + } + + private void TXS() + { + R.S = R.X; + } + + private void TSX() + { + R.X = R.S; SET_ZN_FLAG(R.X); + } + + private void TYA() + { + R.A = R.Y; SET_ZN_FLAG(R.A); + } + + private void TAY() + { + R.Y = R.A; SET_ZN_FLAG(R.Y); + } + + private void TXA() + { + R.A = R.X; SET_ZN_FLAG(R.A); + } + + private void TAX() + { + R.X = R.A; SET_ZN_FLAG(R.X); + } + + private void STY() + { + DT = R.Y; + } + + private void EA_ZY() + { + DT = OP6502(R.PC++); + EA = (byte)(DT + R.Y); + } + + private void STX() + { + DT = R.X; + } + + private void EA_IY() + { + DT = OP6502(R.PC++); + ET = ZPRDW(DT); + EA = (ushort)(ET + R.Y); + } + + private void EA_IX() + { + DT = OP6502(R.PC++); + EA = ZPRDW(DT + R.X); + } + + private void EA_AY() + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (ushort)(ET + R.Y); + } + + private void EA_AX() + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (ushort)(ET + R.X); + } + + private void EA_AB() + { + EA = OP6502W(R.PC); + R.PC += 2; + } + + private void EA_ZX() + { + DT = OP6502(R.PC++); + EA = (byte)(DT + R.X); + } + + private void STA() + { + DT = R.A; + } + + private void EA_ZP() + { + EA = OP6502(R.PC++); + } + + private void LDY() + { + R.Y = DT; SET_ZN_FLAG(R.Y); + } + + private void MR_ZY() + { + DT = OP6502(R.PC++); + EA = (byte)(DT + R.Y); + DT = ZPRD(EA); + } + + private void LDX() + { + R.X = DT; SET_ZN_FLAG(R.X); + } + + private void LDA() + { + R.A = DT; SET_ZN_FLAG(R.A); + } + + private void ROR() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT = (byte)((DT >> 1) | 0x80); + } + else + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT >>= 1; + } + SET_ZN_FLAG(DT); + } + + private void ROR_A() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((R.A & 0x01) != 0, C_FLAG); + R.A = (byte)((R.A >> 1) | 0x80); + } + else + { + TST_FLAG((R.A & 0x01) != 0, C_FLAG); + R.A >>= 1; + } + SET_ZN_FLAG(R.A); + } + + private void ROL() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT = (byte)((DT << 1) | 0x01); + } + else + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT <<= 1; + } + SET_ZN_FLAG(DT); + } + + private void ROL_A() + { + if ((R.P & C_FLAG) != 0) + { + TST_FLAG((R.A & 0x80) != 0, C_FLAG); + R.A = (byte)((R.A << 1) | 0x01); + } + else + { + TST_FLAG((R.A & 0x80) != 0, C_FLAG); + R.A <<= 1; + } + SET_ZN_FLAG(R.A); + } + + private void ORA() + { + R.A |= DT; + SET_ZN_FLAG(R.A); + } + + private void LSR_A() + { + TST_FLAG((R.A & 0x01) != 0, C_FLAG); + R.A >>= 1; + SET_ZN_FLAG(R.A); + } + + private void LSR() + { + TST_FLAG((DT & 0x01) != 0, C_FLAG); + DT >>= 1; + SET_ZN_FLAG(DT); + } + + private void EOR() + { + R.A ^= DT; + SET_ZN_FLAG(R.A); + } + + internal void SetClockProcess(bool bEnable) + { + m_bClockProcess = bEnable; + } + + internal byte OP6502(ushort addr) + { + return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + private byte[] shortTemp = new byte[2]; + internal ushort OP6502W(ushort addr) + { + var bytePage = MMU.CPU_MEM_BANK[addr >> 13]; + var spanByte = bytePage; + shortTemp[0] = spanByte[addr & 0x1FFF]; + shortTemp[1] = spanByte[(addr & 0x1FFF) + 1]; + return BitConverter.ToUInt16(shortTemp, 0); + } + + internal byte RD6502(ushort addr) + { + if (addr < 0x2000) + { + // RAM (Mirror $0800, $1000, $1800) + return MMU.RAM[addr & 0x07FF]; + } + else if (addr < 0x8000) + { + // Others + return nes.Read(addr); + } + else + { + // Dummy access + mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]); + } + + // Quick bank read + return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + private void AND() + { + R.A &= DT; + SET_ZN_FLAG(R.A); + } + + private void MR_IM() + { + DT = OP6502(R.PC++); + } + + private void BIT() + { + TST_FLAG((DT & R.A) == 0, Z_FLAG); + TST_FLAG((DT & 0x80) != 0, N_FLAG); + TST_FLAG((DT & 0x40) != 0, V_FLAG); + } + + private void MR_ZP() + { + EA = OP6502(R.PC++); + DT = ZPRD(EA); + } + + private byte ZPRD(ushort A) + { + return MMU.RAM[A]; + } + + private ushort ZPRDW(int A) + { + ushort ram1 = MMU.RAM[A]; + ushort ram2 = MMU.RAM[A + 1]; + ram2 <<= 8; + return (ushort)(ram1 + ram2); + } + + private void ADC() + { + WT = (ushort)(R.A + DT + (R.P & C_FLAG)); + TST_FLAG(WT > 0xFF, C_FLAG); + var temp = ((~(R.A ^ DT)) & (R.A ^ WT) & 0x80); + TST_FLAG(temp != 0, V_FLAG); + R.A = (byte)WT; + SET_ZN_FLAG(R.A); + } + + private void TST_FLAG(bool F, byte V) + { + byte temp = (byte)~V; + R.P &= temp; + + if (F) R.P |= V; + } + + private void SET_ZN_FLAG(byte A) + { + byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG))); + R.P &= temp; + R.P |= ZN_Table[A]; + } + + private void ADD_CYCLE(int V) + { + exec_cycles += V; + } + + private void MR_ZX() + { + DT = OP6502(R.PC++); + EA = (ushort)(DT + R.X); + DT = ZPRD(EA); + } + + private void MR_AB() + { + EA = OP6502W(R.PC); + R.PC += 2; + DT = RD6502(EA); + } + + private void MR_AX() + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (ushort)(ET + R.X); + DT = RD6502(EA); + } + + private void CHECK_EA() + { + if ((ET & 0xFF00) != (EA & 0xFF00)) ADD_CYCLE(1); + } + + private void MR_AY() + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (ushort)(ET + R.Y); + DT = RD6502(EA); + } + + private void MR_IX() + { + DT = OP6502(R.PC++); + EA = ZPRDW(DT + R.X); + DT = RD6502(EA); + } + + private void MR_IY() + { + DT = OP6502(R.PC++); + ET = ZPRDW(DT); + EA = (ushort)(ET + R.Y); + DT = RD6502(EA); + } + private void ASL_A() + { + TST_FLAG((R.A & 0x80) != 0, C_FLAG); + R.A <<= 1; + SET_ZN_FLAG(R.A); + } + + private void ASL() + { + TST_FLAG((DT & 0x80) != 0, C_FLAG); + DT <<= 1; + SET_ZN_FLAG(DT); + } + + private void SBC() + { + WT = (ushort)(R.A - DT - (~R.P & C_FLAG)); + bool f = ((R.A ^ DT) & (R.A ^ WT) & (0x80)) != 0; + TST_FLAG(f, V_FLAG); + TST_FLAG(WT < 0x100, C_FLAG); + R.A = (byte)WT; + SET_ZN_FLAG(R.A); + } + + private void DEC() + { + DT--; + SET_ZN_FLAG(DT); + } + + private void DEX() + { + R.X--; + SET_ZN_FLAG(R.X); + } + + private void DEY() + { + R.Y--; + SET_ZN_FLAG(R.Y); + } + + private void INC() + { + DT++; + SET_ZN_FLAG(DT); + } + + private void INX() + { + R.X++; + SET_ZN_FLAG(R.X); + } + + private void INY() + { + R.Y++; + SET_ZN_FLAG(R.Y); + } + + private void MW_ZP() + { + ZPWR(EA, DT); + } + + private void ZPWR(ushort a, byte v) + { + MMU.RAM[a] = v; + } + + private void MW_EA() + { + WR6502(EA, DT); + } + + internal void ClrIRQ(byte mask) + { + byte temp = (byte)~mask; + R.INT_pending &= temp; + } + + internal void WR6502(ushort addr, byte data) + { + if (addr < 0x2000) + { + // RAM (Mirror $0800, $1000, $1800) + MMU.RAM[addr & 0x07FF] = data; + } + else + { + // Others + nes.Write(addr, data); + } + } + + internal void NMI() + { + R.INT_pending |= NMI_FLAG; + nmicount = 0; + } + + internal void SetIRQ(byte mask) + { + R.INT_pending |= mask; + } + + internal int GetTotalCycles() + { + return TOTAL_cycles; + } + + internal void DMA(int cycles) + { + DMA_cycles += cycles; + } + + internal void Reset() + { + apu = nes.apu; + mapper = nes.mapper; + + R.A = 0x00; + R.X = 0x00; + R.Y = 0x00; + R.S = 0xFF; + R.P = Z_FLAG | R_FLAG; + R.PC = RD6502W(RES_VECTOR); + + R.INT_pending = 0; + + TOTAL_cycles = 0; + DMA_cycles = 0; + + // STACK quick access + STACK = new ArrayRef(MMU.RAM, 0x0100, MMU.RAM.Length - 0x100); + + // Zero/Negative FLAG + ZN_Table[0] = Z_FLAG; + for (int i = 1; i < 256; i++) + ZN_Table[i] = (byte)((i & 0x80) != 0 ? N_FLAG : 0); + } + + internal void GetContext(ref R6502 r) + { + r = R; + } + + internal void SetContext(R6502 r) + { + R = r; + } + + internal int GetDmaCycles() + { + return DMA_cycles; + } + + internal void SetDmaCycles(int cycles) + { + DMA_cycles = cycles; + } + } + + public enum StatusFlag6502 : int + { + C_FLAG = 0x01, + Z_FLAG = 0x02, + I_FLAG = 0x04, + D_FLAG = 0x08, + B_FLAG = 0x10, + R_FLAG = 0x20, + V_FLAG = 0x40, + N_FLAG = 0x80 + } + + public enum Interrupt : int + { + NMI_FLAG = 0x01, + IRQ_FLAG = 0x02, + IRQ_FRAMEIRQ = 0x04, + IRQ_DPCM = 0x08, + IRQ_MAPPER = 0x10, + IRQ_MAPPER2 = 0x20, + IRQ_TRIGGER = 0x40, + IRQ_TRIGGER2 = 0x80, + IRQ_MASK = (~(NMI_FLAG | IRQ_FLAG)), + } + + public enum Vector : int + { + NMI_VECTOR = 0xFFFA, + RES_VECTOR = 0xFFFC, + IRQ_VECTOR = 0xFFFE + } + + public class R6502 + { + public ushort PC; + public byte A; + public byte P; + public byte X; + public byte Y; + public byte S; + + public byte INT_pending; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Cheat.cs b/Core/VirtualNes.Core/Cheat.cs new file mode 100644 index 0000000..9d2be19 --- /dev/null +++ b/Core/VirtualNes.Core/Cheat.cs @@ -0,0 +1,36 @@ +namespace VirtualNes.Core +{ + public class CHEATCODE + { + // 埲壓偺俀偮偼OR儅僗僋 + public const int CHEAT_ENABLE = 1 << 0; + public const int CHEAT_KEYDISABLE = 1 << 1; + + // 彂偒崬傒庬椶 + public const int CHEAT_TYPE_ALWAYS = 0; // 忢偵彂偒崬傒 + public const int CHEAT_TYPE_ONCE = 1; // 侾夞偩偗彂偒崬傒 + public const int CHEAT_TYPE_GREATER = 2; // 僨乕僞傛傝戝偒偄帪 + public const int CHEAT_TYPE_LESS = 3; // 僨乕僞傛傝彫偝偄帪 + + // 僨乕僞挿 + public const int CHEAT_LENGTH_1BYTE = 0; + public const int CHEAT_LENGTH_2BYTE = 1; + public const int CHEAT_LENGTH_3BYTE = 2; + public const int CHEAT_LENGTH_4BYTE = 3; + + public byte enable; + public byte type; + public byte length; + public ushort address; + public uint data; + + public string comment; + } + + class GENIECODE + { + public ushort address; + public byte data; + public byte cmp; + }; +} diff --git a/Core/VirtualNes.Core/CoreLibs/ByteArrayRef.cs b/Core/VirtualNes.Core/CoreLibs/ByteArrayRef.cs new file mode 100644 index 0000000..22b927f --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/ByteArrayRef.cs @@ -0,0 +1,77 @@ +using System; + +namespace VirtualNes.Core +{ + public class ArrayRef + { + public T[] RawArray => m_rawArray; + private T[] m_rawArray; + private int m_offset; + private int m_length; + + public int Offset + { + get => m_offset; + set + { + var gap = value - m_offset; + m_length -= gap; + } + } + + public ArrayRef() { } + public ArrayRef(T[] array, int offset, int length) + { + SetArray(array, offset, length); + } + + public ArrayRef(T[] array) : this(array, 0, array.Length) { } + public ArrayRef(T[] array, int offset) : this(array, offset, array.Length - offset) { } + + public void SetArray(T[] array, int offset, int length) + { + m_rawArray = array; + m_offset = offset; + m_length = length; + } + + public void SetArray(T[] array, int offset) + { + m_rawArray = array; + m_offset = offset; + m_length = array.Length - offset; + } + + public T this[int index] + { + get + { + return m_rawArray[m_offset + index]; + } + set + { + m_rawArray[(m_offset + index)] = value; + } + } + + public void WriteTo(T[] source, int start, int length) + { + Array.Copy(source, 0, m_rawArray, Offset + start, length); + } + + public Span Span(int start, int length) + { + return new Span(m_rawArray, start + Offset, length); + } + + public static implicit operator ArrayRef(T[] array) + { + return new ArrayRef(array); + } + + public static implicit operator Span(ArrayRef array) + { + return new Span(array.m_rawArray, array.Offset, array.m_length); + } + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/CRC.cs b/Core/VirtualNes.Core/CoreLibs/CRC.cs new file mode 100644 index 0000000..d111289 --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/CRC.cs @@ -0,0 +1,87 @@ +using System; + +namespace VirtualNes.Core +{ + public static class CRC + { + const int CHAR_BIT = 8; + const uint CRCPOLY1 = 0x04C11DB7U; + const uint CRCPOLY2 = 0xEDB88320U; + + static bool m_Init; + static bool m_InitRev; + static uint[] m_CrcTable = new uint[byte.MaxValue + 1]; + static uint[] m_CrcTableRev = new uint[byte.MaxValue + 1]; + + public static ulong Crc(int size, Span c) + { + if (!m_Init) + { + MakeTable(); + m_Init = true; + } + + ulong r = 0xFFFFFFFFUL; + int step = 0; + while (--size >= 0) + { + r = (r << CHAR_BIT) ^ m_CrcTable[(byte)(r >> (32 - CHAR_BIT)) ^ c[step]]; + step++; + } + return ~r & 0xFFFFFFFFUL; + } + public static uint CrcRev(int size, Span c) + { + if (!m_InitRev) + { + MakeTableRev(); + m_InitRev = true; + } + + uint r = 0xFFFFFFFFU; + int step = 0; + while (--size >= 0) + { + r = (r >> CHAR_BIT) ^ m_CrcTableRev[(byte)r ^ c[step]]; + step++; + } + return r ^ 0xFFFFFFFFU; + } + + static void MakeTable() + { + int i, j; + uint r; + + for (i = 0; i <= byte.MaxValue; i++) + { + r = (uint)i << (32 - CHAR_BIT); + for (j = 0; j < CHAR_BIT; j++) + { + if ((r & 0x80000000UL) > 0) r = (r << 1) ^ CRCPOLY1; + else r <<= 1; + } + m_CrcTable[i] = r & 0xFFFFFFFFU; + } + + } + static void MakeTableRev() + { + int i, j; + uint r; + + for (i = 0; i <= byte.MaxValue; i++) + { + r = (uint)i; + for (j = 0; j < CHAR_BIT; j++) + { + if ((r & 1) > 0) r = (r >> 1) ^ CRCPOLY2; + else r >>= 1; + } + m_CrcTableRev[i] = r; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413.cs b/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413.cs new file mode 100644 index 0000000..f688a4f --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413.cs @@ -0,0 +1,1543 @@ +using System; +namespace VirtualNes.Core.Emu2413 +{ + public static class Emu2413API + { + static sbyte[][] default_inst = Const.Create_Default_Inst(); + + public const int OPLL_TONE_NUM = 2; + + /* Size of Sintable ( 1 -- 18 can be used, but 7 -- 14 recommended.)*/ + public const int PG_BITS = 9; + public const int PG_WIDTH = (1 << PG_BITS); + + /* Phase increment counter */ + public const int DP_BITS = 18; + public const int DP_WIDTH = (1 << DP_BITS); + public const int DP_BASE_BITS = (DP_BITS - PG_BITS); + + /* Dynamic range */ + public const double DB_STEP = 0.375; + public const int DB_BITS = 7; + public const int DB_MUTE = (1 << DB_BITS); + + /* Dynamic range of envelope */ + public const double EG_STEP = 0.375; + public const int EG_BITS = 7; + //public const int EG_MUTE = (1 << EB_BITS); ?? 原文如此 EB_BITS 根本不存在 + public const int EG_MUTE = (1 << EG_BITS); + + /* Dynamic range of total level */ + public const double TL_STEP = 0.75; + public const int TL_BITS = 6; + public const int TL_MUTE = (1 << TL_BITS); + + /* Dynamic range of sustine level */ + public const double SL_STEP = 3.0; + public const int SL_BITS = 4; + public const int SL_MUTE = (1 << SL_BITS); + + static int EG2DB(int d) + { + return (d * (int)(EG_STEP / DB_STEP)); + } + + static uint TL2EG(int d) + { + return (uint)(d * (int)(TL_STEP / EG_STEP)); + } + + static int SL2EG(int d) + { + return (d * (int)(SL_STEP / EG_STEP)); + } + + /* Volume of Noise (dB) */ + public const double DB_NOISE = 24; + + static uint DB_POS(double x) + { + return (uint)(x / DB_STEP); + } + + static uint DB_NEG(double x) + { + return (uint)(DB_MUTE + DB_MUTE + x / DB_STEP); + } + + /* Bits for liner value */ + public const int DB2LIN_AMP_BITS = 10; + public const int SLOT_AMP_BITS = (DB2LIN_AMP_BITS); + + /* Bits for envelope phase incremental counter */ + public const int EG_DP_BITS = 22; + public const int EG_DP_WIDTH = (1 << EG_DP_BITS); + + /* Bits for Pitch and Amp modulator */ + public const int PM_PG_BITS = 8; + public const int PM_PG_WIDTH = 1 << PM_PG_BITS; + public const int PM_DP_BITS = 16; + public const int PM_DP_WIDTH = (1 << PM_DP_BITS); + public const int AM_PG_BITS = 8; + public const int AM_PG_WIDTH = (1 << AM_PG_BITS); + public const int AM_DP_BITS = 16; + public const int AM_DP_WIDTH = (1 << AM_DP_BITS); + + /* Mask */ + static int OPLL_MASK_CH(int x) + { + return 1 << x; + } + public const int OPLL_MASK_HH = 1 << 9; + public const int OPLL_MASK_CYM = (1 << (10)); + public const int OPLL_MASK_TOM = (1 << (11)); + public const int OPLL_MASK_SD = (1 << (12)); + public const int OPLL_MASK_BD = (1 << (13)); + public const int OPLL_MASK_RYTHM = OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD; + + /* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */ + public const int PM_AMP_BITS = 8; + public const int PM_AMP = (1 << PM_AMP_BITS); + + /* PM speed(Hz) and depth(cent) */ + public const double PM_SPEED = 6.4d; + public const double PM_DEPTH = 13.75d; + + /* AM speed(Hz) and depth(dB) */ + public const double AM_SPEED = 3.7; + public const double AM_DEPTH = 4.8; + + /* Cut the lower b bit(s) off. */ + static int HIGHBITS(int c, int b) + { + return c >> b; + } + + /* Leave the lower b bit(s). */ + static int LOWBITS(int c, int b) + { + return c & ((1 << b) - 1); + } + + /* Expand x which is s bits to d bits. */ + static int EXPAND_BITS(int x, int s, int d) + { + return (x << (d - s)); + } + + /* Expand x which is s bits to d bits and fill expanded bits '1' */ + static int EXPAND_BITS_X(int x, int s, int d) + { + return ((x << (d - s)) | ((1 << (d - s)) - 1)); + } + + /* Adjust envelope speed which depends on sampling rate. */ + static uint rate_adjust(int x) + { + return (uint)((double)x * clk / 72 / rate + 0.5); /* +0.5 to round */ + } + + static OPLL_SLOT MOD(this OPLL opll, int x) + { + return opll.ch[x].mod; + } + static OPLL_SLOT CAR(this OPLL opll, int x) + { + return opll.ch[x].car; + } + + /* Sampling rate */ + static uint rate; + /* Input clock */ + static uint clk; + + /* WaveTable for each envelope amp */ + static uint[] fullsintable = new uint[PG_WIDTH]; + static uint[] halfsintable = new uint[PG_WIDTH]; + static uint[] snaretable = new uint[PG_WIDTH]; + + static int[] noiseAtable = new int[64] + { + -1,1,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0, + -1,1,0,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0,-1,1,0,0 + }; + + static int[] noiseBtable = new int[8] + { + -1,1,-1,1,0,0,0,0 + }; + + static uint[][] waveform = new uint[5][] + { + fullsintable, halfsintable,snaretable,null,null + }; + + /* Noise and LFO */ + static uint pm_dphase; + static uint am_dphase; + + /* dB to Liner table */ + static int[] DB2LIN_TABLE = new int[(DB_MUTE + DB_MUTE) * 2]; + + /* Liner to Log curve conversion table (for Attack rate). */ + static uint[] AR_ADJUST_TABLE = new uint[1 << EG_BITS]; + + /* Empty voice data */ + static OPLL_PATCH null_patch = new OPLL_PATCH(); + + /* Basic voice Data */ + static OPLL_PATCH[][] default_patch = Const.Create_Default_Patch(); + + /* Definition of envelope mode */ + enum EnvelopeMode { SETTLE, ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, FINISH }; + + /* Phase incr table for Attack */ + static uint[][] dphaseARTable = new uint[16][] + { + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16], + }; + /* Phase incr table for Decay and Release */ + static uint[][] dphaseDRTable = new uint[16][] + { + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16],new uint[16],new uint[16],new uint[16],new uint[16], + new uint[16], + }; + + /* KSL + TL Table */ + static uint[,,,] tllTable = Const.Create_tllTable(); + static int[,,] rksTable = Const.Create_rksTable(); + + /* Phase incr table for PG */ + static uint[,,] dphaseTable = Const.Create_dphaseTable(); + + public const int OPLL_2413_TONE = 0; + public const int OPLL_VRC7_TONE = 1; + + static int[] pmtable = new int[PM_PG_WIDTH]; + static int[] amtable = new int[AM_PG_WIDTH]; + + static int Min(int i, int j) + { + if (i < j) return i; else return j; + } + + /* Table for AR to LogCurve. */ + static void makeAdjustTable() + { + int i; + + AR_ADJUST_TABLE[0] = (1 << EG_BITS); + for (i = 1; i < 128; i++) + AR_ADJUST_TABLE[i] = (uint)((double)(1 << EG_BITS) - 1 - (1 << EG_BITS) * Math.Log(i) / Math.Log(128)); + } + + /* Table for dB(0 -- (1<= DB_MUTE) DB2LIN_TABLE[i] = 0; + DB2LIN_TABLE[i + DB_MUTE + DB_MUTE] = -DB2LIN_TABLE[i]; + } + } + + /* Liner(+0.0 - +1.0) to dB((1< 0) noiseAtable[i] = (int)DB_POS(0); + else if (noiseAtable[i] < 0) noiseAtable[i] = (int)DB_NEG(0); + else noiseAtable[i] = DB_MUTE - 1; + } + + for (i = 0; i < 8; i++) + { + if (noiseBtable[i] > 0) noiseBtable[i] = (int)DB_POS(0); + else if (noiseBtable[i] < 0) noiseBtable[i] = (int)DB_NEG(0); + else noiseBtable[i] = DB_MUTE - 1; + } + + } + + /* Table for Amp Modulator */ + static void makeAmTable() + { + int i; + + for (i = 0; i < AM_PG_WIDTH; i++) + amtable[i] = (int)((double)AM_DEPTH / 2 / DB_STEP * (1.0 + Math.Sin(2.0 * Math.PI * i / PM_PG_WIDTH))); + } + + static uint[] mltable_makeDphaseTable = new uint[16] { 1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2, 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2 }; + /* Phase increment counter table */ + static void makeDphaseTable() + { + uint fnum, block, ML; + + for (fnum = 0; fnum < 512; fnum++) + for (block = 0; block < 8; block++) + for (ML = 0; ML < 16; ML++) + dphaseTable[fnum, block, ML] = rate_adjust((int)((fnum * mltable_makeDphaseTable[ML]) << (int)block) >> (20 - DP_BITS)); + } + + static uint dB2(double x) + { + return (uint)(x * 2); + } + + static uint[] kltable = new uint[16] + { + dB2( 0.000),dB2( 9.000),dB2(12.000),dB2(13.875),dB2(15.000),dB2(16.125),dB2(16.875),dB2(17.625), + dB2(18.000),dB2(18.750),dB2(19.125),dB2(19.500),dB2(19.875),dB2(20.250),dB2(20.625),dB2(21.000) + }; + + static void makeTllTable() + { + int tmp; + int fnum, block, TL, KL; + + for (fnum = 0; fnum < 16; fnum++) + for (block = 0; block < 8; block++) + for (TL = 0; TL < 64; TL++) + for (KL = 0; KL < 4; KL++) + { + if (KL == 0) + { + tllTable[fnum, block, TL, KL] = TL2EG(TL); + } + else + { + tmp = (int)(kltable[fnum] - dB2(3.000) * (7 - block)); + if (tmp <= 0) + tllTable[fnum, block, TL, KL] = TL2EG(TL); + else + tllTable[fnum, block, TL, KL] = (uint)((tmp >> (3 - KL)) / EG_STEP) + TL2EG(TL); + } + } + } + + /* Rate Table for Attack */ + static void makeDphaseARTable() + { + int AR, Rks, RM, RL; + + for (AR = 0; AR < 16; AR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = AR + (Rks >> 2); + if (RM > 15) RM = 15; + RL = Rks & 3; + switch (AR) + { + case 0: + dphaseARTable[AR][Rks] = 0; + break; + case 15: + dphaseARTable[AR][Rks] = EG_DP_WIDTH; + break; + default: + dphaseARTable[AR][Rks] = rate_adjust((3 * (RL + 4) << (RM + 1))); + break; + } + } + } + + /* Rate Table for Decay */ + static void makeDphaseDRTable() + { + int DR, Rks, RM, RL; + + for (DR = 0; DR < 16; DR++) + for (Rks = 0; Rks < 16; Rks++) + { + RM = DR + (Rks >> 2); + RL = Rks & 3; + if (RM > 15) RM = 15; + switch (DR) + { + case 0: + dphaseDRTable[DR][Rks] = 0; + break; + default: + dphaseDRTable[DR][Rks] = rate_adjust((RL + 4) << (RM - 1)); + break; + } + } + } + + static void makeRksTable() + { + + int fnum8, block, KR; + + for (fnum8 = 0; fnum8 < 2; fnum8++) + for (block = 0; block < 8; block++) + for (KR = 0; KR < 2; KR++) + { + if (KR != 0) + rksTable[fnum8, block, KR] = (block << 1) + fnum8; + else + rksTable[fnum8, block, KR] = block >> 1; + } + } + + private static void makePmTable() + { + int i; + + for (i = 0; i < PM_PG_WIDTH; i++) + pmtable[i] = (int)(PM_AMP * Math.Pow(2, PM_DEPTH * Math.Sin(2.0 * Math.PI * i / PM_PG_WIDTH) / 1200)); + } + + static void dump2patch(ArrayRef dump, ArrayRef patch) + { + patch[0].AM = (uint)((dump[0] >> 7) & 1); + patch[1].AM = (uint)((dump[1] >> 7) & 1); + patch[0].PM = (uint)((dump[0] >> 6) & 1); + patch[1].PM = (uint)((dump[1] >> 6) & 1); + patch[0].EG = (uint)((dump[0] >> 5) & 1); + patch[1].EG = (uint)((dump[1] >> 5) & 1); + patch[0].KR = (uint)((dump[0] >> 4) & 1); + patch[1].KR = (uint)((dump[1] >> 4) & 1); + patch[0].ML = (uint)((dump[0]) & 15); + patch[1].ML = (uint)((dump[1]) & 15); + patch[0].KL = (uint)((dump[2] >> 6) & 3); + patch[1].KL = (uint)((dump[3] >> 6) & 3); + patch[0].TL = (uint)((dump[2]) & 63); + patch[0].FB = (uint)((dump[3]) & 7); + patch[0].WF = (uint)((dump[3] >> 3) & 1); + patch[1].WF = (uint)((dump[3] >> 4) & 1); + patch[0].AR = (uint)((dump[4] >> 4) & 15); + patch[1].AR = (uint)((dump[5] >> 4) & 15); + patch[0].DR = (uint)((dump[4]) & 15); + patch[1].DR = (uint)((dump[5]) & 15); + patch[0].SL = (uint)((dump[6] >> 4) & 15); + patch[1].SL = (uint)((dump[7] >> 4) & 15); + patch[0].RR = (uint)((dump[6]) & 15); + patch[1].RR = (uint)((dump[7]) & 15); + } + + static ArrayRef instSpan = new ArrayRef(); + static ArrayRef patchSpan = new ArrayRef(); + static void makeDefaultPatch() + { + int i, j; + + for (i = 0; i < OPLL_TONE_NUM; i++) + for (j = 0; j < 19; j++) + { + instSpan.SetArray(default_inst[i], j * 16); + patchSpan.SetArray(default_patch[i], j * 2); + dump2patch(instSpan, patchSpan); + } + } + + static uint calc_eg_dphase(OPLL_SLOT slot) + { + + switch ((EnvelopeMode)slot.eg_mode) + { + case EnvelopeMode.ATTACK: + return dphaseARTable[slot.patch.AR][slot.rks]; + + case EnvelopeMode.DECAY: + return dphaseDRTable[slot.patch.DR][slot.rks]; + + case EnvelopeMode.SUSHOLD: + return 0; + + case EnvelopeMode.SUSTINE: + return dphaseDRTable[slot.patch.RR][slot.rks]; + + case EnvelopeMode.RELEASE: + if (slot.sustine != 0) + return dphaseDRTable[5][slot.rks]; + else if (slot.patch.EG != 0) + return dphaseDRTable[slot.patch.RR][slot.rks]; + else + return dphaseDRTable[7][slot.rks]; + + case EnvelopeMode.FINISH: + return 0; + + default: + return 0; + } + } + + public const int SLOT_BD1 = 12; + public const int SLOT_BD2 = 13; + public const int SLOT_HH = 14; + public const int SLOT_SD = 15; + public const int SLOT_TOM = 16; + public const int SLOT_CYM = 17; + + static void UPDATE_PG(OPLL_SLOT S) + { + S.dphase = dphaseTable[S.fnum, S.block, S.patch.ML]; + } + + static void UPDATE_TLL(OPLL_SLOT S) + { + if (S.type == 0) + { + S.tll = tllTable[S.fnum >> 5, S.block, S.patch.TL, S.patch.KL]; + } + else + { + S.tll = tllTable[S.fnum >> 5, S.block, S.volume, S.patch.KL]; + } + } + + static void UPDATE_RKS(OPLL_SLOT S) + { + S.rks = (uint)rksTable[(S.fnum) >> 8, S.block, S.patch.KR]; + } + + static void UPDATE_WF(OPLL_SLOT S) + { + S.sintbl = waveform[S.patch.WF]; + } + + static void UPDATE_EG(OPLL_SLOT S) + { + S.eg_dphase = calc_eg_dphase(S); + } + + static void UPDATE_ALL(OPLL_SLOT S) + { + UPDATE_PG(S); + UPDATE_TLL(S); + UPDATE_RKS(S); + UPDATE_WF(S); + UPDATE_EG(S); /* G should be last */ + } + + /* Force Refresh (When external program changes some parameters). */ + static void OPLL_forceRefresh(OPLL opll) + { + int i; + + if (opll == null) return; + + for (i = 0; i < 18; i++) + { + UPDATE_PG(opll.slot[i]); + UPDATE_RKS(opll.slot[i]); + UPDATE_TLL(opll.slot[i]); + UPDATE_WF(opll.slot[i]); + UPDATE_EG(opll.slot[i]); + } + } + + /* Slot key on */ + static void slotOn(OPLL_SLOT slot) + { + slot.eg_mode = (int)EnvelopeMode.ATTACK; + slot.phase = 0; + slot.eg_phase = 0; + } + + /* Slot key off */ + static void slotOff(OPLL_SLOT slot) + { + if (slot.eg_mode == (int)EnvelopeMode.ATTACK) + slot.eg_phase = (uint)EXPAND_BITS((int)AR_ADJUST_TABLE[HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS); + slot.eg_mode = (int)EnvelopeMode.RELEASE; + } + + /* Channel key on */ + static void keyOn(OPLL opll, int i) + { + if (opll.slot_on_flag[i * 2] == 0) slotOn(opll.MOD(i)); + if (opll.slot_on_flag[i * 2 + 1] == 0) slotOn(opll.CAR(i)); + opll.ch[i].key_status = 1; + } + + static void keyOff(OPLL opll, int i) + { + if (opll.slot_on_flag[i * 2 + 1] != 0) slotOff(opll.CAR(i)); + opll.ch[i].key_status = 0; + } + + static void keyOn_BD(OPLL opll) { keyOn(opll, 6); } + static void keyOn_SD(OPLL opll) { if (opll.slot_on_flag[SLOT_SD] == 0) slotOn(opll.CAR(7)); } + static void keyOn_TOM(OPLL opll) { if (opll.slot_on_flag[SLOT_TOM] == 0) slotOn(opll.MOD(8)); } + static void keyOn_HH(OPLL opll) { if (opll.slot_on_flag[SLOT_HH] == 0) slotOn(opll.MOD(7)); } + static void keyOn_CYM(OPLL opll) { if (opll.slot_on_flag[SLOT_CYM] == 0) slotOn(opll.CAR(8)); } + + /* Drum key off */ + + static void keyOff_BD(OPLL opll) { keyOff(opll, 6); } + static void keyOff_SD(OPLL opll) { if (opll.slot_on_flag[SLOT_SD] != 0) slotOff(opll.CAR(7)); } + static void keyOff_TOM(OPLL opll) { if (opll.slot_on_flag[SLOT_TOM] != 0) slotOff(opll.MOD(8)); } + static void keyOff_HH(OPLL opll) { if (opll.slot_on_flag[SLOT_HH] != 0) slotOff(opll.MOD(7)); } + static void keyOff_CYM(OPLL opll) { if (opll.slot_on_flag[SLOT_CYM] != 0) slotOff(opll.CAR(8)); } + + /* Change a voice */ + static void setPatch(OPLL opll, int i, int num) + { + opll.ch[i].patch_number = num; + opll.MOD(i).patch = opll.patch[num * 2 + 0]; + opll.CAR(i).patch = opll.patch[num * 2 + 1]; + } + + /* Change a rythm voice */ + static void setSlotPatch(OPLL_SLOT slot, OPLL_PATCH patch) + { + slot.patch = patch; + } + + /* Set sustine parameter */ + static void setSustine(OPLL opll, int c, int sustine) + { + opll.CAR(c).sustine = sustine; + if (opll.MOD(c).type != 0) opll.MOD(c).sustine = sustine; + } + + /* Volume : 6bit ( Volume register << 2 ) */ + static void setVolume(OPLL opll, int c, int volume) + { + opll.CAR(c).volume = volume; + } + + static void setSlotVolume(OPLL_SLOT slot, int volume) + { + slot.volume = volume; + } + + /* Set F-Number ( fnum : 9bit ) */ + static void setFnumber(OPLL opll, int c, int fnum) + { + opll.CAR(c).fnum = fnum; + opll.MOD(c).fnum = fnum; + } + + /* Set Block data (block : 3bit ) */ + static void setBlock(OPLL opll, int c, int block) + { + opll.CAR(c).block = block; + opll.MOD(c).block = block; + } + + /* Change Rythm Mode */ + static void setRythmMode(OPLL opll, int mode) + { + opll.rythm_mode = mode; + + if (mode != 0) + { + opll.ch[6].patch_number = 16; + opll.ch[7].patch_number = 17; + opll.ch[8].patch_number = 18; + setSlotPatch(opll.slot[SLOT_BD1], opll.patch[16 * 2 + 0]); + setSlotPatch(opll.slot[SLOT_BD2], opll.patch[16 * 2 + 1]); + setSlotPatch(opll.slot[SLOT_HH], opll.patch[17 * 2 + 0]); + setSlotPatch(opll.slot[SLOT_SD], opll.patch[17 * 2 + 1]); + opll.slot[SLOT_HH].type = 1; + setSlotPatch(opll.slot[SLOT_TOM], opll.patch[18 * 2 + 0]); + setSlotPatch(opll.slot[SLOT_CYM], opll.patch[18 * 2 + 1]); + opll.slot[SLOT_TOM].type = 1; + } + else + { + setPatch(opll, 6, opll.reg[0x36] >> 4); + setPatch(opll, 7, opll.reg[0x37] >> 4); + opll.slot[SLOT_HH].type = 0; + setPatch(opll, 8, opll.reg[0x38] >> 4); + opll.slot[SLOT_TOM].type = 0; + } + + if (opll.slot_on_flag[SLOT_BD1] == 0) + opll.slot[SLOT_BD1].eg_mode = (int)EnvelopeMode.FINISH; + if (opll.slot_on_flag[SLOT_BD2] == 0) + opll.slot[SLOT_BD2].eg_mode = (int)EnvelopeMode.FINISH; + if (opll.slot_on_flag[SLOT_HH] == 0) + opll.slot[SLOT_HH].eg_mode = (int)EnvelopeMode.FINISH; + if (opll.slot_on_flag[SLOT_SD] == 0) + opll.slot[SLOT_SD].eg_mode = (int)EnvelopeMode.FINISH; + if (opll.slot_on_flag[SLOT_TOM] == 0) + opll.slot[SLOT_TOM].eg_mode = (int)EnvelopeMode.FINISH; + if (opll.slot_on_flag[SLOT_CYM] == 0) + opll.slot[SLOT_CYM].eg_mode = (int)EnvelopeMode.FINISH; + } + + static void OPLL_copyPatch(OPLL opll, int num, OPLL_PATCH patch) + { + opll.patch[num].Copy(patch); + } + + static void OPLL_SLOT_reset(OPLL_SLOT slot) + { + slot.sintbl = waveform[0]; + slot.phase = 0; + slot.dphase = 0; + slot.output[0] = 0; + slot.output[1] = 0; + slot.feedback = 0; + slot.eg_mode = (int)EnvelopeMode.SETTLE; + slot.eg_phase = EG_DP_WIDTH; + slot.eg_dphase = 0; + slot.rks = 0; + slot.tll = 0; + slot.sustine = 0; + slot.fnum = 0; + slot.block = 0; + slot.volume = 0; + slot.pgout = 0; + slot.egout = 0; + slot.patch = null_patch; + } + + static OPLL_SLOT OPLL_SLOT_new() + { + OPLL_SLOT slot; + slot = new OPLL_SLOT(); + + return slot; + } + + static void OPLL_SLOT_delete(OPLL_SLOT slot) + { + //free(slot); // c# just do nothing + } + + static void OPLL_CH_reset(OPLL_CH ch) + { + if (ch.mod != null) OPLL_SLOT_reset(ch.mod); + if (ch.car != null) OPLL_SLOT_reset(ch.car); + ch.key_status = 0; + } + + static OPLL_CH OPLL_CH_new() + { + OPLL_CH ch; + OPLL_SLOT mod, car; + + mod = OPLL_SLOT_new(); + if (mod == null) return null; + + car = OPLL_SLOT_new(); + if (car == null) + { + OPLL_SLOT_delete(mod); + return null; + } + + ch = new OPLL_CH(); + if (ch == null) + { + OPLL_SLOT_delete(mod); + OPLL_SLOT_delete(car); + return null; + } + + mod.type = 0; + car.type = 1; + ch.mod = mod; + ch.car = car; + + return ch; + } + + static void OPLL_CH_delete(OPLL_CH ch) + { + OPLL_SLOT_delete(ch.mod); + OPLL_SLOT_delete(ch.car); + //free(ch); C# just do nothing + } + + public static OPLL OPLL_new() + { + OPLL opll; + OPLL_CH[] ch = new OPLL_CH[9]; + OPLL_PATCH[] patch = new OPLL_PATCH[19 * 2]; + int i, j; + + for (i = 0; i < 19 * 2; i++) + { + patch[i] = new OPLL_PATCH(); + } + + for (i = 0; i < 9; i++) + { + ch[i] = OPLL_CH_new(); + } + + opll = new OPLL(); + + for (i = 0; i < 19 * 2; i++) + opll.patch[i] = patch[i]; + + + for (i = 0; i < 9; i++) + { + opll.ch[i] = ch[i]; + opll.slot[i * 2 + 0] = opll.ch[i].mod; + opll.slot[i * 2 + 1] = opll.ch[i].car; + } + + for (i = 0; i < 18; i++) + { + opll.slot[i].SetHost(opll); + } + + opll.mask = 0; + + OPLL_reset(opll); + OPLL_reset_patch(opll, 0); + + opll.masterVolume = 32; + + return opll; + } + + public static void OPLL_delete(OPLL opll) + { + int i; + + for (i = 0; i < 9; i++) + OPLL_CH_delete(opll.ch[i]); + + //for (i = 0; i < 19 * 2; i++) + // free(opll->patch[i]); + + //free(opll); + } + + /* Reset patch datas by system default. */ + public static void OPLL_reset_patch(OPLL opll, int type) + { + int i; + + for (i = 0; i < 19 * 2; i++) + OPLL_copyPatch(opll, i, default_patch[type % OPLL_TONE_NUM][i]); + } + + /* Reset whole of OPLL except patch datas. */ + public static void OPLL_reset(OPLL opll) + { + int i; + + if (opll == null) return; + + opll.adr = 0; + + opll.output[0] = 0; + opll.output[1] = 0; + + opll.pm_phase = 0; + opll.am_phase = 0; + + opll.noise_seed = 0xffff; + opll.noiseA = 0; + opll.noiseB = 0; + opll.noiseA_phase = 0; + opll.noiseB_phase = 0; + opll.noiseA_dphase = 0; + opll.noiseB_dphase = 0; + opll.noiseA_idx = 0; + opll.noiseB_idx = 0; + + for (i = 0; i < 9; i++) + { + OPLL_CH_reset(opll.ch[i]); + setPatch(opll, i, 0); + } + + for (i = 0; i < 0x40; i++) OPLL_writeReg(opll, (uint)i, 0); + } + + public static void OPLL_setClock(uint c, uint r) + { + clk = c; + rate = r; + makeDphaseTable(); + makeDphaseARTable(); + makeDphaseDRTable(); + pm_dphase = rate_adjust((int)(PM_SPEED * PM_DP_WIDTH / (clk / 72))); + am_dphase = rate_adjust((int)(AM_SPEED * AM_DP_WIDTH / (clk / 72))); + } + + public static void OPLL_init(uint c, uint r) + { + makePmTable(); + makeAmTable(); + makeDB2LinTable(); + makeAdjustTable(); + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + OPLL_setClock(c, r); + } + static void OPLL_close() + { + } + + static int wave2_2pi(int e) + { + return (e) >> (SLOT_AMP_BITS - PG_BITS); + } + + static int wave2_4pi(int e) + { + return e; + } + + static int wave2_8pi(int e) + { + return (e) << (2 + PG_BITS - SLOT_AMP_BITS); + } + + /* 16bit rand */ + static uint mrand(uint seed) + { + return ((seed >> 15) ^ ((seed >> 12) & 1)) | ((seed << 1) & 0xffff); + } + + static uint DEC(uint db) + { + if (db < DB_MUTE + DB_MUTE) + { + return (uint)Min((int)(db + DB_POS(0.375 * 2)), DB_MUTE - 1); + } + else + { + return (uint)Min((int)(db + DB_POS(0.375 * 2)), DB_MUTE + DB_MUTE + DB_MUTE - 1); + } + } + + /* Update Noise unit */ + static void update_noise(OPLL opll) + { + opll.noise_seed = mrand(opll.noise_seed); + opll.whitenoise = opll.noise_seed & 1; + + opll.noiseA_phase = (opll.noiseA_phase + opll.noiseA_dphase); + opll.noiseB_phase = (opll.noiseB_phase + opll.noiseB_dphase); + + if (opll.noiseA_phase < (1 << 11)) + { + if (opll.noiseA_phase > 16) opll.noiseA = DB_MUTE - 1; + } + else + { + opll.noiseA_phase &= (1 << 11) - 1; + opll.noiseA_idx = (opll.noiseA_idx + 1) & 63; + opll.noiseA = (uint)noiseAtable[opll.noiseA_idx]; + } + + if (opll.noiseB_phase < (1 << 12)) + { + if (opll.noiseB_phase > 16) opll.noiseB = DB_MUTE - 1; + } + else + { + opll.noiseB_phase &= (1 << 12) - 1; + opll.noiseB_idx = (opll.noiseB_idx + 1) & 7; + opll.noiseB = (uint)noiseBtable[opll.noiseB_idx]; + } + } + + /* Update AM, PM unit */ + static void update_ampm(OPLL opll) + { + opll.pm_phase = (opll.pm_phase + pm_dphase) & (PM_DP_WIDTH - 1); + opll.am_phase = (int)(opll.am_phase + am_dphase) & (AM_DP_WIDTH - 1); + opll.lfo_am = amtable[HIGHBITS(opll.am_phase, AM_DP_BITS - AM_PG_BITS)]; + opll.lfo_pm = pmtable[HIGHBITS((int)(opll.pm_phase), PM_DP_BITS - PM_PG_BITS)]; + } + + /* PG */ + static uint calc_phase(OPLL_SLOT slot) + { + if (slot.patch.PM != 0) + slot.phase = (uint)(slot.phase + (slot.dphase * (slot.plfo_pm)) >> PM_AMP_BITS); + else + slot.phase += slot.dphase; + + slot.phase &= (DP_WIDTH - 1); + + return (uint)HIGHBITS((int)slot.phase, DP_BASE_BITS); + } + + static uint S2E(int x) + { + return (uint)(SL2EG((int)(x / SL_STEP)) << (EG_DP_BITS - EG_BITS)); + } + + static uint[] SL = new uint[16] + { + S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21), + S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(48) + }; + + /* EG */ + static uint calc_envelope(OPLL_SLOT slot) + { + uint egout; + + switch ((EnvelopeMode)slot.eg_mode) + { + + case EnvelopeMode.ATTACK: + slot.eg_phase += slot.eg_dphase; + if ((EG_DP_WIDTH & slot.eg_phase) != 0) + { + egout = 0; + slot.eg_phase = 0; + slot.eg_mode = (int)EnvelopeMode.DECAY; + UPDATE_EG(slot); + } + else + { + egout = AR_ADJUST_TABLE[HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS)]; + } + break; + + case EnvelopeMode.DECAY: + slot.eg_phase += slot.eg_dphase; + egout = (uint)HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS); + if (slot.eg_phase >= SL[slot.patch.SL]) + { + if (slot.patch.EG != 0) + { + slot.eg_phase = SL[slot.patch.SL]; + slot.eg_mode = (int)EnvelopeMode.SUSHOLD; + UPDATE_EG(slot); + } + else + { + slot.eg_phase = SL[slot.patch.SL]; + slot.eg_mode = (int)EnvelopeMode.SUSTINE; + UPDATE_EG(slot); + } + egout = (uint)HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS); + } + break; + + case EnvelopeMode.SUSHOLD: + egout = (uint)HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS); + if (slot.patch.EG == 0) + { + slot.eg_mode = (int)EnvelopeMode.SUSTINE; + UPDATE_EG(slot); + } + break; + + case EnvelopeMode.SUSTINE: + case EnvelopeMode.RELEASE: + slot.eg_phase += slot.eg_dphase; + egout = (uint)HIGHBITS((int)slot.eg_phase, EG_DP_BITS - EG_BITS); + if (egout >= (1 << EG_BITS)) + { + slot.eg_mode = (int)EnvelopeMode.FINISH; + egout = (1 << EG_BITS) - 1; + } + break; + + case EnvelopeMode.FINISH: + egout = (1 << EG_BITS) - 1; + break; + + default: + egout = (1 << EG_BITS) - 1; + break; + } + + if (slot.patch.AM != 0) egout = (uint)(EG2DB((int)(egout + slot.tll)) + (slot.plfo_am)); + else egout = (uint)EG2DB((int)(egout + slot.tll)); + + if (egout >= DB_MUTE) egout = DB_MUTE - 1; + return egout; + } + + static int calc_slot_car(OPLL_SLOT slot, int fm) + { + slot.egout = calc_envelope(slot); + slot.pgout = calc_phase(slot); + if (slot.egout >= (DB_MUTE - 1)) return 0; + + return DB2LIN_TABLE[slot.sintbl[(slot.pgout + wave2_8pi(fm)) & (PG_WIDTH - 1)] + slot.egout]; + } + + static int calc_slot_mod(OPLL_SLOT slot) + { + int fm; + + slot.output[1] = slot.output[0]; + slot.egout = calc_envelope(slot); + slot.pgout = calc_phase(slot); + + if (slot.egout >= (DB_MUTE - 1)) + { + slot.output[0] = 0; + } + else if (slot.patch.FB != 0) + { + fm = (wave2_4pi(slot.feedback) >> (int)(7 - slot.patch.FB)); + slot.output[0] = DB2LIN_TABLE[slot.sintbl[(slot.pgout + fm) & (PG_WIDTH - 1)] + slot.egout]; + } + else + { + slot.output[0] = DB2LIN_TABLE[slot.sintbl[slot.pgout] + slot.egout]; + } + + slot.feedback = (slot.output[1] + slot.output[0]) >> 1; + + return slot.feedback; + } + + static int calc_slot_tom(OPLL_SLOT slot) + { + slot.egout = calc_envelope(slot); + slot.pgout = calc_phase(slot); + if (slot.egout >= (DB_MUTE - 1)) return 0; + + return DB2LIN_TABLE[slot.sintbl[slot.pgout] + slot.egout]; + } + + /* calc SNARE slot */ + static int calc_slot_snare(OPLL_SLOT slot, uint whitenoise) + { + slot.egout = calc_envelope(slot); + slot.pgout = calc_phase(slot); + if (slot.egout >= (DB_MUTE - 1)) return 0; + + if (whitenoise != 0) + return DB2LIN_TABLE[snaretable[slot.pgout] + slot.egout] + DB2LIN_TABLE[slot.egout + 6]; + else + return DB2LIN_TABLE[snaretable[slot.pgout] + slot.egout]; + } + + static int calc_slot_cym(OPLL_SLOT slot, int a, int b, int c) + { + slot.egout = calc_envelope(slot); + if (slot.egout >= (DB_MUTE - 1)) return 0; + + return DB2LIN_TABLE[slot.egout + a] + + ((DB2LIN_TABLE[slot.egout + b] + DB2LIN_TABLE[slot.egout + c]) >> 2); + } + + static int calc_slot_hat(OPLL_SLOT slot, int a, int b, int c, uint whitenoise) + { + slot.egout = calc_envelope(slot); + if (slot.egout >= (DB_MUTE - 1)) return 0; + + if (whitenoise != 0) + { + return DB2LIN_TABLE[slot.egout + a] + + ((DB2LIN_TABLE[slot.egout + b] + DB2LIN_TABLE[slot.egout + c]) >> 2); + } + else + { + return 0; + } + } + + public static short OPLL_calc(OPLL opll) + { + int inst = 0, perc = 0, @out = 0; + int rythmC = 0, rythmH = 0; + int i; + + update_ampm(opll); + update_noise(opll); + + for (i = 0; i < 6; i++) + if ((opll.mask & OPLL_MASK_CH(i)) == 0 && (opll.CAR(i).eg_mode != (int)EnvelopeMode.FINISH)) + inst += calc_slot_car(opll.CAR(i), calc_slot_mod(opll.MOD(i))); + + if (opll.rythm_mode == 0) + { + for (i = 6; i < 9; i++) + if ((opll.mask & OPLL_MASK_CH(i)) == 0 && (opll.CAR(i).eg_mode != (int)EnvelopeMode.FINISH)) + inst += calc_slot_car(opll.CAR(i), calc_slot_mod(opll.MOD(i))); + } + else + { + opll.MOD(7).pgout = calc_phase(opll.MOD(7)); + opll.CAR(8).pgout = calc_phase(opll.CAR(8)); + if (opll.MOD(7).phase < 256) rythmH = (int)DB_NEG(12.0); else rythmH = DB_MUTE - 1; + if (opll.CAR(8).phase < 256) rythmC = (int)DB_NEG(12.0); else rythmC = DB_MUTE - 1; + + if ((opll.mask & OPLL_MASK_BD) == 0 && (opll.CAR(6).eg_mode != (int)EnvelopeMode.FINISH)) + perc += calc_slot_car(opll.CAR(6), calc_slot_mod(opll.MOD(6))); + + if ((opll.mask & OPLL_MASK_HH) == 0 && (opll.MOD(7).eg_mode != (int)EnvelopeMode.FINISH)) + perc += calc_slot_hat(opll.MOD(7), (int)opll.noiseA, (int)opll.noiseB, rythmH, opll.whitenoise); + + if ((opll.mask & OPLL_MASK_SD) == 0 && (opll.CAR(7).eg_mode != (int)EnvelopeMode.FINISH)) + perc += calc_slot_snare(opll.CAR(7), opll.whitenoise); + + if ((opll.mask & OPLL_MASK_TOM) == 0 && (opll.MOD(8).eg_mode != (int)EnvelopeMode.FINISH)) + perc += calc_slot_tom(opll.MOD(8)); + + if ((opll.mask & OPLL_MASK_CYM) == 0 && (opll.CAR(8).eg_mode != (int)EnvelopeMode.FINISH)) + perc += calc_slot_cym(opll.CAR(8), (int)opll.noiseA, (int)opll.noiseB, rythmC); + } + + inst = (inst >> (SLOT_AMP_BITS - 8)); + perc = (perc >> (SLOT_AMP_BITS - 9)); + + @out = ((inst + perc) * opll.masterVolume) >> 2; + + if (@out > 32767) return 32767; + if (@out < -32768) return -32768; + + return (short)@out; + } + + static uint OPLL_setMask(OPLL opll, uint mask) + { + uint ret; + + if (opll != null) + { + ret = opll.mask; + opll.mask = mask; + return ret; + } + else return 0; + } + + static uint OPLL_toggleMask(OPLL opll, uint mask) + { + uint ret; + + if (opll != null) + { + ret = opll.mask; + opll.mask ^= mask; + return ret; + } + else return 0; + } + + public static void OPLL_writeReg(OPLL opll, uint reg, uint data) + { + + int i, v, ch; + + data = data & 0xff; + reg = reg & 0x3f; + + switch (reg) + { + case 0x00: + opll.patch[0].AM = (data >> 7) & 1; + opll.patch[0].PM = (data >> 6) & 1; + opll.patch[0].EG = (data >> 5) & 1; + opll.patch[0].KR = (data >> 4) & 1; + opll.patch[0].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_PG(opll.MOD(i)); + UPDATE_RKS(opll.MOD(i)); + UPDATE_EG(opll.MOD(i)); + } + } + break; + + case 0x01: + opll.patch[1].AM = (data >> 7) & 1; + opll.patch[1].PM = (data >> 6) & 1; + opll.patch[1].EG = (data >> 5) & 1; + opll.patch[1].KR = (data >> 4) & 1; + opll.patch[1].ML = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_PG(opll.CAR(i)); + UPDATE_RKS(opll.CAR(i)); + UPDATE_EG(opll.CAR(i)); + } + } + break; + + case 0x02: + opll.patch[0].KL = (data >> 6) & 3; + opll.patch[0].TL = (data) & 63; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_TLL(opll.MOD(i)); + } + } + break; + + case 0x03: + opll.patch[1].KL = (data >> 6) & 3; + opll.patch[1].WF = (data >> 4) & 1; + opll.patch[0].WF = (data >> 3) & 1; + opll.patch[0].FB = (data) & 7; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_WF(opll.MOD(i)); + UPDATE_WF(opll.CAR(i)); + } + } + break; + + case 0x04: + opll.patch[0].AR = (data >> 4) & 15; + opll.patch[0].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_EG(opll.MOD(i)); + } + } + break; + + case 0x05: + opll.patch[1].AR = (data >> 4) & 15; + opll.patch[1].DR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_EG(opll.CAR(i)); + } + } + break; + + case 0x06: + opll.patch[0].SL = (data >> 4) & 15; + opll.patch[0].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_EG(opll.MOD(i)); + } + } + break; + + case 0x07: + opll.patch[1].SL = (data >> 4) & 15; + opll.patch[1].RR = (data) & 15; + for (i = 0; i < 9; i++) + { + if (opll.ch[i].patch_number == 0) + { + UPDATE_EG(opll.CAR(i)); + } + } + break; + + case 0x0e: + + if (opll.rythm_mode != 0) + { + opll.slot_on_flag[SLOT_BD1] = (opll.reg[0x0e] & 0x10) | (opll.reg[0x26] & 0x10); + opll.slot_on_flag[SLOT_BD2] = (opll.reg[0x0e] & 0x10) | (opll.reg[0x26] & 0x10); + opll.slot_on_flag[SLOT_SD] = (opll.reg[0x0e] & 0x08) | (opll.reg[0x27] & 0x10); + opll.slot_on_flag[SLOT_HH] = (opll.reg[0x0e] & 0x01) | (opll.reg[0x27] & 0x10); + opll.slot_on_flag[SLOT_TOM] = (opll.reg[0x0e] & 0x04) | (opll.reg[0x28] & 0x10); + opll.slot_on_flag[SLOT_CYM] = (opll.reg[0x0e] & 0x02) | (opll.reg[0x28] & 0x10); + } + else + { + opll.slot_on_flag[SLOT_BD1] = (opll.reg[0x26] & 0x10); + opll.slot_on_flag[SLOT_BD2] = (opll.reg[0x26] & 0x10); + opll.slot_on_flag[SLOT_SD] = (opll.reg[0x27] & 0x10); + opll.slot_on_flag[SLOT_HH] = (opll.reg[0x27] & 0x10); + opll.slot_on_flag[SLOT_TOM] = (opll.reg[0x28] & 0x10); + opll.slot_on_flag[SLOT_CYM] = (opll.reg[0x28] & 0x10); + } + + if ((((data >> 5) & 1) ^ (opll.rythm_mode)) != 0) + { + setRythmMode(opll, (int)(data & 32) >> 5); + } + + if (opll.rythm_mode != 0) + { + if ((data & 0x10) != 0) keyOn_BD(opll); else keyOff_BD(opll); + if ((data & 0x8) != 0) keyOn_SD(opll); else keyOff_SD(opll); + if ((data & 0x4) != 0) keyOn_TOM(opll); else keyOff_TOM(opll); + if ((data & 0x2) != 0) keyOn_CYM(opll); else keyOff_CYM(opll); + if ((data & 0x1) != 0) keyOn_HH(opll); else keyOff_HH(opll); + } + + UPDATE_ALL(opll.MOD(6)); + UPDATE_ALL(opll.CAR(6)); + UPDATE_ALL(opll.MOD(7)); + UPDATE_ALL(opll.CAR(7)); + UPDATE_ALL(opll.MOD(8)); + UPDATE_ALL(opll.CAR(8)); + break; + + case 0x0f: + break; + + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + ch = (int)(reg - 0x10); + setFnumber(opll, ch, (int)(data + ((opll.reg[0x20 + ch] & 1) << 8))); + UPDATE_ALL(opll.MOD(ch)); + UPDATE_ALL(opll.CAR(ch)); + switch (reg) + { + case 0x17: + opll.noiseA_dphase = (uint)((data + ((opll.reg[0x27] & 1) << 8)) << ((opll.reg[0x27] >> 1) & 7)); + break; + case 0x18: + opll.noiseB_dphase = (uint)((data + ((opll.reg[0x28] & 1) << 8)) << ((opll.reg[0x28] >> 1) & 7)); + break; + default: + break; + } + break; + + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + + ch = (int)(reg - 0x20); + setFnumber(opll, ch, (int)(((data & 1) << 8) + opll.reg[0x10 + ch])); + setBlock(opll, ch, (int)((data >> 1) & 7)); + opll.slot_on_flag[ch * 2] = opll.slot_on_flag[ch * 2 + 1] = (opll.reg[reg]) & 0x10; + + if (opll.rythm_mode != 0) + { + switch (reg) + { + case 0x26: + opll.slot_on_flag[SLOT_BD1] |= (opll.reg[0x0e]) & 0x10; + opll.slot_on_flag[SLOT_BD2] |= (opll.reg[0x0e]) & 0x10; + break; + + case 0x27: + opll.noiseA_dphase = (uint)((int)((data & 1) << 8 + opll.reg[0x17]) << (int)((data >> 1) & 7)); + opll.slot_on_flag[SLOT_SD] |= (opll.reg[0x0e]) & 0x08; + opll.slot_on_flag[SLOT_HH] |= (opll.reg[0x0e]) & 0x01; + break; + + case 0x28: + opll.noiseB_dphase = (uint)((int)((data & 1) << 8) + opll.reg[0x18]) << (int)((data >> 1) & 7); + opll.slot_on_flag[SLOT_TOM] |= (opll.reg[0x0e]) & 0x04; + opll.slot_on_flag[SLOT_CYM] |= (opll.reg[0x0e]) & 0x02; + break; + + default: + break; + } + } + + if (((opll.reg[reg] ^ data) & 0x20) != 0) setSustine(opll, ch, (int)((data >> 5) & 1)); + if ((data & 0x10) != 0) keyOn(opll, ch); else keyOff(opll, ch); + UPDATE_ALL(opll.MOD(ch)); + UPDATE_ALL(opll.CAR(ch)); + break; + + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + i = (int)((data >> 4) & 15); + v = (int)(data & 15); + if ((opll.rythm_mode) != 0 && (reg >= 0x36)) + { + switch (reg) + { + case 0x37: + setSlotVolume(opll.MOD(7), i << 2); + break; + case 0x38: + setSlotVolume(opll.MOD(8), i << 2); + break; + } + } + else + { + setPatch(opll, (int)(reg - 0x30), i); + } + + setVolume(opll, (int)(reg - 0x30), v << 2); + UPDATE_ALL(opll.MOD((int)(reg - 0x30))); + UPDATE_ALL(opll.CAR((int)(reg - 0x30))); + break; + + default: + break; + + } + + opll.reg[reg] = (byte)data; + } + + static void OPLL_writeIO(OPLL opll, uint adr, uint val) + { + adr &= 0xff; + if (adr == 0x7C) opll.adr = val; + else if (adr == 0x7D) OPLL_writeReg(opll, opll.adr, val); + } + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413_Class.cs b/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413_Class.cs new file mode 100644 index 0000000..2f57abb --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/Emu2413/Emu2413_Class.cs @@ -0,0 +1,208 @@ +using System; + +namespace VirtualNes.Core.Emu2413 +{ + public static class Const + { + internal static sbyte[][] Create_Default_Inst() + { + unchecked + { + sbyte[][] res = new sbyte[Emu2413API.OPLL_TONE_NUM][] + { + new sbyte[] + { + (sbyte)0x00,(sbyte) 0x00, (sbyte)0x00, (sbyte)0x00,(sbyte) 0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61,(sbyte) 0x61, (sbyte)0x1e, (sbyte)0x17,(sbyte) 0xf0, (sbyte)0x7f, (sbyte)0x07, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x13,(sbyte) 0x41, (sbyte)0x0f, (sbyte)0x0d,(sbyte) 0xce, (sbyte)0xd2, (sbyte)0x43, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x03,(sbyte) 0x01, (sbyte)0x99, (sbyte)0x04,(sbyte) 0xff, (sbyte)0xc3, (sbyte)0x03, (sbyte)0x73, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x21,(sbyte) 0x61, (sbyte)0x1b, (sbyte)0x07,(sbyte) 0xaf, (sbyte)0x63, (sbyte)0x40, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x22,(sbyte) 0x21, (sbyte)0x1e, (sbyte)0x06,(sbyte) 0xf0, (sbyte)0x76, (sbyte)0x08, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x31,(sbyte) 0x22, (sbyte)0x16, (sbyte)0x05,(sbyte) 0x90, (sbyte)0x71, (sbyte)0x00, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x21,(sbyte) 0x61, (sbyte)0x1d, (sbyte)0x07,(sbyte) 0x82, (sbyte)0x81, (sbyte)0x10, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x23,(sbyte) 0x21, (sbyte)0x2d, (sbyte)0x16,(sbyte) 0xc0, (sbyte)0x70, (sbyte)0x07, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61,(sbyte) 0x21, (sbyte)0x1b, (sbyte)0x06,(sbyte) 0x64, (sbyte)0x65, (sbyte)0x18, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61,(sbyte) 0x61, (sbyte)0x0c, (sbyte)0x18,(sbyte) 0x85, (sbyte)0xa0, (sbyte)0x79, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x23,(sbyte) 0x21, (sbyte)0x87, (sbyte)0x11,(sbyte) 0xf0, (sbyte)0xa4, (sbyte)0x00, (sbyte)0xf7, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x97,(sbyte) 0xe1, (sbyte)0x28, (sbyte)0x07,(sbyte) 0xff, (sbyte)0xf3, (sbyte)0x02, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61,(sbyte) 0x10, (sbyte)0x0c, (sbyte)0x05,(sbyte) 0xf2, (sbyte)0xc4, (sbyte)0x40, (sbyte)0xc8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x01,(sbyte) 0x01, (sbyte)0x56, (sbyte)0x03,(sbyte) 0xb4, (sbyte)0xb2, (sbyte)0x23, (sbyte)0x58, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61,(sbyte) 0x41, (sbyte)0x89, (sbyte)0x03,(sbyte) 0xf1, (sbyte)0xf4, (sbyte)0xf0, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x04,(sbyte) 0x21, (sbyte)0x28, (sbyte)0x00,(sbyte) 0xdf, (sbyte)0xf8, (sbyte)0xff, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x23,(sbyte) 0x22, (sbyte)0x00, (sbyte)0x00,(sbyte) 0xd8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x25,(sbyte) 0x18, (sbyte)0x00, (sbyte)0x00,(sbyte) 0xf8, (sbyte)0xda, (sbyte)0xf8, (sbyte)0x55, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + }, + new sbyte[] + { + (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x33, (sbyte)0x01, (sbyte)0x09, (sbyte)0x0e, (sbyte)0x94, (sbyte)0x90, (sbyte)0x40, (sbyte)0x01, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x13, (sbyte)0x41, (sbyte)0x0f, (sbyte)0x0d, (sbyte)0xce, (sbyte)0xd3, (sbyte)0x43, (sbyte)0x13, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x01, (sbyte)0x12, (sbyte)0x1b, (sbyte)0x06, (sbyte)0xff, (sbyte)0xd2, (sbyte)0x00, (sbyte)0x32, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x61, (sbyte)0x61, (sbyte)0x1b, (sbyte)0x07, (sbyte)0xaf, (sbyte)0x63, (sbyte)0x20, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x22, (sbyte)0x21, (sbyte)0x1e, (sbyte)0x06, (sbyte)0xf0, (sbyte)0x76, (sbyte)0x08, (sbyte)0x28, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x66, (sbyte)0x21, (sbyte)0x15, (sbyte)0x00, (sbyte)0x93, (sbyte)0x94, (sbyte)0x20, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x21, (sbyte)0x61, (sbyte)0x1c, (sbyte)0x07, (sbyte)0x82, (sbyte)0x81, (sbyte)0x10, (sbyte)0x17, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x23, (sbyte)0x21, (sbyte)0x20, (sbyte)0x1f, (sbyte)0xc0, (sbyte)0x71, (sbyte)0x07, (sbyte)0x47, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x25, (sbyte)0x31, (sbyte)0x26, (sbyte)0x05, (sbyte)0x64, (sbyte)0x41, (sbyte)0x18, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x17, (sbyte)0x21, (sbyte)0x28, (sbyte)0x07, (sbyte)0xff, (sbyte)0x83, (sbyte)0x02, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x97, (sbyte)0x81, (sbyte)0x25, (sbyte)0x07, (sbyte)0xcf, (sbyte)0xc8, (sbyte)0x02, (sbyte)0x14, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x21, (sbyte)0x21, (sbyte)0x54, (sbyte)0x0f, (sbyte)0x80, (sbyte)0x7f, (sbyte)0x07, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x01, (sbyte)0x01, (sbyte)0x56, (sbyte)0x03, (sbyte)0xd3, (sbyte)0xb2, (sbyte)0x43, (sbyte)0x58, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x31, (sbyte)0x21, (sbyte)0x0c, (sbyte)0x03, (sbyte)0x82, (sbyte)0xc0, (sbyte)0x40, (sbyte)0x07, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x21, (sbyte)0x01, (sbyte)0x0c, (sbyte)0x03, (sbyte)0xd4, (sbyte)0xd3, (sbyte)0x40, (sbyte)0x84, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x04, (sbyte)0x21, (sbyte)0x28, (sbyte)0x00, (sbyte)0xdf, (sbyte)0xf8, (sbyte)0xff, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x23, (sbyte)0x22, (sbyte)0x00, (sbyte)0x00, (sbyte)0xa8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0xf8, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + (sbyte)0x25, (sbyte)0x18, (sbyte)0x00, (sbyte)0x00, (sbyte)0xf8, (sbyte)0xa9, (sbyte)0xf8, (sbyte)0x55, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, (sbyte)0x00, + } + }; + + return res; + } + } + + internal static OPLL_PATCH[][] Create_Default_Patch() + { + OPLL_PATCH[][] res = new OPLL_PATCH[Emu2413API.OPLL_TONE_NUM][] + { + new OPLL_PATCH[(16 + 3) * 2], + new OPLL_PATCH[(16 + 3) * 2], + }; + + for (int x = 0; x < Emu2413API.OPLL_TONE_NUM; x++) + for (int y = 0; y < (16 + 3) * 2; y++) + res[x][y] = new OPLL_PATCH(); + + return res; + } + + internal static uint[,,,] Create_tllTable() + { + var res = new uint[16, 8, 1 << Emu2413API.TL_BITS, 4]; + return res; + } + + internal static Int32[,,] Create_rksTable() + { + return new int[2, 8, 2]; + } + + internal static UInt32[,,] Create_dphaseTable() + { + return new uint[512, 8, 16]; + } + } + public class OPLL_PATCH + { + public uint TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, WF; + + public void Copy(OPLL_PATCH other) + { + TL = other.TL; + FB = other.FB; + EG = other.EG; + ML = other.ML; + AR = other.AR; + DR = other.DR; + SL = other.SL; + RR = other.RR; + KR = other.KR; + KL = other.KL; + AM = other.AM; + PM = other.PM; + WF = other.WF; + } + } + + public class OPLL_SLOT + { + public OPLL_PATCH patch; + + public int type; /* 0 : modulator 1 : carrier */ + + /* OUTPUT */ + public Int32 feedback; + public Int32[] output = new Int32[5]; /* Output value of slot */ + + /* for Phase Generator (PG) */ + public UInt32[] sintbl; /* Wavetable */ + public UInt32 phase; /* Phase */ + public UInt32 dphase; /* Phase increment amount */ + public UInt32 pgout; /* output */ + + /* for Envelope Generator (EG) */ + public int fnum; /* F-Number */ + public int block; /* Block */ + public int volume; /* Current volume */ + public int sustine; /* Sustine 1 = ON, 0 = OFF */ + public UInt32 tll; /* Total Level + Key scale level*/ + public UInt32 rks; /* Key scale offset (Rks) */ + public int eg_mode; /* Current state */ + public UInt32 eg_phase; /* Phase */ + public UInt32 eg_dphase; /* Phase increment amount */ + public UInt32 egout; /* output */ + + + /* refer to opll-> */ + public int plfo_pm => m_host.lfo_pm; + public int plfo_am => m_host.lfo_am; + + private OPLL m_host; + public void SetHost(OPLL host) + { + m_host = host; + } + } + + public class OPLL_CH + { + public int patch_number; + public int key_status; + public OPLL_SLOT mod; + public OPLL_SLOT car; + } + + public class OPLL + { + public UInt32 adr; + public Int32[] output = new Int32[2]; + + /* Register */ + public byte[] reg = new byte[0x40]; + public int[] slot_on_flag = new int[18]; + + /* Rythm Mode : 0 = OFF, 1 = ON */ + public int rythm_mode; + + /* Pitch Modulator */ + public UInt32 pm_phase; + public Int32 lfo_pm; + + /* Amp Modulator */ + public Int32 am_phase; + public Int32 lfo_am; + + /* Noise Generator */ + public UInt32 noise_seed; + public UInt32 whitenoise; + public UInt32 noiseA; + public UInt32 noiseB; + public UInt32 noiseA_phase; + public UInt32 noiseB_phase; + public UInt32 noiseA_idx; + public UInt32 noiseB_idx; + public UInt32 noiseA_dphase; + public UInt32 noiseB_dphase; + + /* Channel & Slot */ + public OPLL_CH[] ch = new OPLL_CH[9]; + public OPLL_SLOT[] slot = new OPLL_SLOT[18]; + + /* Voice Data */ + public OPLL_PATCH[] patch = new OPLL_PATCH[19 * 2]; + public int[] patch_update = new int[2]; /* flag for check patch update */ + + public UInt32 mask; + + public int masterVolume; /* 0min -- 64 -- 127 max (Liner) */ + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs b/Core/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs new file mode 100644 index 0000000..262a6da --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs @@ -0,0 +1,12 @@ +namespace VirtualNes.Core +{ + // 昤夋曽幃 + public enum EnumRenderMethod + { + POST_ALL_RENDER = 0, // 僗僉儍儞儔僀儞暘偺柦椷幚峴屻丆儗儞僟儕儞僌 + PRE_ALL_RENDER = 1, // 儗儞僟儕儞僌偺幚峴屻丆僗僉儍儞儔僀儞暘偺柦椷幚峴 + POST_RENDER = 2, // 昞帵婜娫暘偺柦椷幚峴屻丆儗儞僟儕儞僌 + PRE_RENDER = 3, // 儗儞僟儕儞僌幚峴屻丆昞帵婜娫暘偺柦椷幚峴 + TILE_RENDER = 4 // 僞僀儖儀乕僗儗儞僟儕儞僌 + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/ISoundDataBuffer.cs b/Core/VirtualNes.Core/CoreLibs/ISoundDataBuffer.cs new file mode 100644 index 0000000..f95d273 --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/ISoundDataBuffer.cs @@ -0,0 +1,7 @@ +namespace VirtualNes.Core +{ + public interface ISoundDataBuffer + { + void WriteByte(byte value); + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/MemoryUtility.cs b/Core/VirtualNes.Core/CoreLibs/MemoryUtility.cs new file mode 100644 index 0000000..f8a20ce --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/MemoryUtility.cs @@ -0,0 +1,77 @@ +using System; +using System.Runtime.CompilerServices; + +namespace VirtualNes.Core +{ + public static class MemoryUtility + { + public static void ZEROMEMORY(byte[] array, int length) + { + Array.Clear(array, 0, array.Length); + } + public static void ZEROMEMORY(int[] array, int length) + { + Array.Clear(array, 0, array.Length); + } + + public static void memset(byte[] array, byte value, int length) + { + memset(array, 0, value, length); + } + + public unsafe static void memset(byte[] array, int offset, byte value, int length) + { + fixed (byte* ptr = array) + { + var offsetptr = ptr + offset; + + Unsafe.InitBlockUnaligned(offsetptr, value, (uint)length); + } + } + + public unsafe static void memset(uint[] array, int offset, byte value, int length) + { + fixed (uint* ptr = array) + { + var offsetptr = ptr + offset; + for (int i = 0; i < length; i++) + { + offsetptr[i] = value; + } + } + } + + public unsafe static void memset(byte* ptr, int offset, byte value, int length) + { + var offsetptr = ptr + offset; + for (int i = 0; i < length; i++) + { + offsetptr[i] = value; + } + } + + public unsafe static void memset(byte* ptr, byte value, int length) + { + memset(ptr, 0, value, length); + } + + public unsafe static void memset(uint* ptr, int offset, uint value, int length) + { + var offsetptr = ptr + offset; + for (int i = 0; i < length; i++) + { + offsetptr[i] = value; + } + } + + public unsafe static void memset(uint* ptr, uint value, int length) + { + memset(ptr, 0, value, length); + } + + public static void memcpy(Array dst, Array src, int length) + { + Array.Copy(src, dst, length); + } + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/NesConfig.cs b/Core/VirtualNes.Core/CoreLibs/NesConfig.cs new file mode 100644 index 0000000..edb724e --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/NesConfig.cs @@ -0,0 +1,51 @@ +namespace VirtualNes.Core +{ + public class NesConfig + { + public float BaseClock; // NTSC:21477270.0 PAL:21281364.0 + public float CpuClock; // NTSC: 1789772.5 PAL: 1773447.0 + + public int TotalScanlines; // NTSC: 262 PAL: 312 + + public int ScanlineCycles; // NTSC:1364 PAL:1362 + + public int HDrawCycles; // NTSC:1024 PAL:1024 + public int HBlankCycles; // NTSC: 340 PAL: 338 + public int ScanlineEndCycles; // NTSC: 4 PAL: 2 + + public int FrameCycles; // NTSC:29829.52 PAL:35468.94 + public int FrameIrqCycles; // NTSC:29829.52 PAL:35468.94 + + public int FrameRate; // NTSC:60(59.94) PAL:50 + public float FramePeriod; // NTSC:16.683 PAL:20.0 + + public static NesConfig NESCONFIG_NTSC = new NesConfig + { + BaseClock = 21477270.0f, + CpuClock = 1789772.5f, + TotalScanlines = 262, + ScanlineCycles = 1364, + HDrawCycles = 1024, + HBlankCycles = 340, + ScanlineEndCycles = 4, + FrameCycles = 1364 * 262, + FrameIrqCycles = 29830, + FrameRate = 60, + FramePeriod = 1000.0f / 60.0f + }; + public static NesConfig NESCONFIG_PAL = new NesConfig + { + BaseClock = 26601714.0f, + CpuClock = 1662607.125f, + TotalScanlines = 312, + ScanlineCycles = 1278, + HDrawCycles = 960, + HBlankCycles = 318, + ScanlineEndCycles = 2, + FrameCycles = 1278 * 312, + FrameIrqCycles = 33252, + FrameRate = 50, + FramePeriod = 1000.0f / 50.0f + }; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/CoreLibs/ROMClasses.cs b/Core/VirtualNes.Core/CoreLibs/ROMClasses.cs new file mode 100644 index 0000000..bbc74d3 --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/ROMClasses.cs @@ -0,0 +1,140 @@ +using System; + +namespace VirtualNes.Core +{ + public enum EnumRomControlByte1 : byte + { + ROM_VMIRROR = 0x01, + ROM_SAVERAM = 0x02, + ROM_TRAINER = 0x04, + ROM_4SCREEN = 0x08, + } + + public enum EnumRomControlByte2 : byte + { + ROM_VSUNISYSTEM = 0x01 + } + + public enum EnumRomType + { + InValid, + NES, + /// Nintendo Disk System + FDS, + NSF + } + + public class NSFHEADER + { + byte[] ID; + byte Version; + byte TotalSong; + byte StartSong; + ushort LoadAddress; + ushort InitAddress; + ushort PlayAddress; + byte[] SongName; + byte[] ArtistName; + byte[] CopyrightName; + ushort SpeedNTSC; + byte[] BankSwitch; + ushort SpeedPAL; + byte NTSC_PALbits; + public byte ExtraChipSelect; + byte[] Expansion; // must be 0 + + + public static int SizeOf() + { + return 128; + } + + public static NSFHEADER GetDefault() + { + var res = new NSFHEADER(); + res.ID = new byte[5]; + res.SongName = new byte[32]; + res.ArtistName = new byte[32]; + res.CopyrightName = new byte[32]; + res.BankSwitch = new byte[8]; + res.Expansion = new byte[4]; + return res; + } + } + + public class NESHEADER + { + public byte[] ID; + public byte PRG_PAGE_SIZE; + public byte CHR_PAGE_SIZE; + public byte control1; + public byte control2; + public byte[] reserved; + + public bool CheckValid() + { + return GetRomType() != EnumRomType.InValid; + } + + public static int SizeOf() + { + return 16; + } + + public EnumRomType GetRomType() + { + if (ID[0] == 'N' && ID[1] == 'E' && ID[2] == 'S' && ID[3] == 0x1A) + return EnumRomType.NES; + if (ID[0] == 'F' && ID[1] == 'D' && ID[2] == 'S' && ID[3] == 0x1A) + return EnumRomType.FDS; + if (ID[0] == 'N' && ID[1] == 'E' && ID[2] == 'S' && ID[3] == 'M') + return EnumRomType.NSF; + + return EnumRomType.InValid; + } + + public static NESHEADER GetDefault() + { + var res = new NESHEADER(); + res.ID = new byte[4]; + res.reserved = new byte[8]; + return res; + } + + public static NESHEADER Read(Span data) + { + var res = new NESHEADER(); + res.ID = data.Slice(0, 4).ToArray(); + res.PRG_PAGE_SIZE = data[4]; + res.CHR_PAGE_SIZE = data[5]; + res.control1 = data[6]; + res.control2 = data[7]; + res.reserved = data.Slice(8, 8).ToArray(); + + return res; + } + + public byte[] DataToBytes() + { + byte[] res = new byte[16]; + res[0] = ID[0]; + res[1] = ID[1]; + res[2] = ID[2]; + res[3] = ID[3]; + res[4] = PRG_PAGE_SIZE; + res[5] = CHR_PAGE_SIZE; + res[6] = control1; + res[7] = control2; + res[8] = reserved[0]; + res[9] = reserved[1]; + res[10] = reserved[2]; + res[11] = reserved[3]; + res[12] = reserved[4]; + res[13] = reserved[5]; + res[14] = reserved[6]; + res[15] = reserved[7]; + + return res; + } + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/RomPatch.cs b/Core/VirtualNes.Core/CoreLibs/RomPatch.cs new file mode 100644 index 0000000..fb26c67 --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/RomPatch.cs @@ -0,0 +1,420 @@ +namespace VirtualNes.Core +{ + public static class RomPatch + { + public static void DoPatch(ref uint crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header) + { + // Mapper 000 + if (crc == 0x57970078) + { // F-1 Race(J) + lpPRG[0x078C] = 0x6C; + lpPRG[0x3FE1] = 0xFF; + lpPRG[0x3FE6] = 0x00; + } + if (crc == 0xaf2bbcbc // Mach Rider(JU) + || crc == 0x3acd4bf1 // Mach Rider(Alt)(JU) pb`(^^; + || crc == 0x8bbe9bec) + { + lpPRG[0x090D] = 0x6E; + lpPRG[0x7FDF] = 0xFF; + lpPRG[0x7FE4] = 0x00; + + header.control1 = (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + if (crc == 0xe16bb5fe) + { // Zippy Race(J) + header.control1 &= 0xf6; + } + if (crc == 0x85534474) + { // Lode Runner(J) + lpPRG[0x29E9] = 0xEA; // Z[uj[opb` + lpPRG[0x29EA] = 0xEA; + lpPRG[0x29F8] = 0xEA; + lpPRG[0x29F9] = 0xEA; + } + + // Mapper 001 + if (crc == 0x7831b2ff // America Daitouryou Senkyo(J) + || crc == 0x190a3e11 // Be-Bop-Highschool - Koukousei Gokuraku Densetsu(J) + || crc == 0x52449508 // Home Run Nighter - Pennant League!!(J) + || crc == 0x0973f714 // Jangou(J) + || crc == 0x7172f3d4 // Kabushiki Doujou(J) + || crc == 0xa5781280 // Kujaku Ou 2(J) + || crc == 0x8ce9c87b // Money Game, The(J) + || crc == 0xec47296d // Morita Kazuo no Shougi(J) + || crc == 0xcee5857b // Ninjara Hoi!(J) + || crc == 0xe63d9193 // Tanigawa Kouji no Shougi Shinan 3(J) + || crc == 0xd54f5da9 // Tsuppari Wars(J) + || crc == 0x1e0c7ea3) + { // AD&D Dragons of Flame(J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + if (crc == 0x1995ac4e) + { // Ferrari Grand Prix Challenge(J) pb`(^^; + lpPRG[0x1F7AD] = 0xFF; + lpPRG[0x1F7BC] = 0x00; + } + + if (crc == 0x20d22251) + { // Top rider(J) pb`(^^; + lpPRG[0x1F17E] = 0xEA; + lpPRG[0x1F17F] = 0xEA; + } + + if (crc == 0x11469ce3) + { // Viva! Las Vegas(J) pb`(^^; + lpCHR[0x0000] = 0x01; + } + + if (crc == 0x3fccdc7b) + { // Baseball Star - Mezase Sankanou!!(J) pb`(^^; + lpPRG[0x0F666] = 0x9D; + } + + if (crc == 0xdb564628) + { // Mario Open Golf(J) + lpPRG[0x30195] = 0xC0; + } + + // Mapper 002 + if (crc == 0x63af202f) + { // JJ - Tobidase Daisakusen Part 2(J) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + if (crc == 0x99a62e47) + { // Black Bass 2, The(J) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + if (crc == 0x0eaa7515 // Rod Land(J) + || crc == 0x22ab9694) + { // Rod Land(E) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + if (crc == 0x2061772a) + { // Tantei Jinguji Taburou Tokino Sugiyukumamani (J) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + // Mapper 003 + if (crc == 0x29401686) + { // Minna no Taabou no Nakayoshi Dai Sakusen(J) + // lpPRG[0x2B3E] = 0x60; + } + if (crc == 0x932a077a) + { // TwinBee(J) + mapper = 87; + } + if (crc == 0x8218c637) + { // Space Hunter(J) + // header.control1 &= 0xf6; + // header.control1 |= ROM_4SCREEN; + header.control1 = (byte)EnumRomControlByte1.ROM_VMIRROR; + } + if (crc == 0x2bb6a0f8 // Sherlock Holmes - Hakushaku Reijou Yuukai Jiken(J) + || crc == 0x28c11d24 // Sukeban Deka 3(J) + || crc == 0x02863604) + { // Sukeban Deka 3(J)(Alt) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + // Mapper 004 + if (crc == 0x58581770) + { // Rasaaru Ishii no Childs Quest(J) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + if (crc == 0xf3feb3ab // Kunio Kun no Jidaigeki Dayo Zenin Shuugou! (J) + || crc == 0xa524ae9b // Otaku no Seiza - An Adventure in the Otaku Galaxy (J) + || crc == 0x46dc6e57 // SD Gundam - Gachapon Senshi 2 - Capsule Senki (J) + || crc == 0x66b2dec7 // SD Gundam - Gachapon Senshi 3 - Eiyuu Senki (J) + || crc == 0x92b07fd9 // SD Gundam - Gachapon Senshi 4 - New Type Story (J) + || crc == 0x8ee6463a // SD Gundam - Gachapon Senshi 5 - Battle of Universal Century (J) + || crc == 0xaf754426 // Ultraman Club 3 (J) + || crc == 0xfe4e5b11 // Ushio to Tora - Shinen no Daiyou (J) + || crc == 0x57c12c17) + { // Yamamura Misa Suspense - Kyouto Zaiteku Satsujin Jiken (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + if (crc == 0x42e03e4a) + { // RPG Jinsei Game (J) + mapper = 118; + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + if (crc == 0xfd0299c3) + { // METAL MAX(J) + lpPRG[0x3D522] = 0xA9; + lpPRG[0x3D523] = 0x19; + } + if (crc == 0x1d2e5018 // Rockman 3(J) + || crc == 0x6b999aaf) + { // Mega Man 3(U) + // lpPRG[0x3C179] = 0xBA;// + // lpPRG[0x3C9CC] = 0x9E; + } + + // Mapper 005 + if (crc == 0xe91548d8) + { // Shin 4 Nin Uchi Mahjong - Yakuman Tengoku (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + if (crc == 0x255b129c) + { // Gun Sight (J) / Gun Sight (J)[a1] + lpPRG[0x02D0B] = 0x01; + lpPRG[0x0BEC0] = 0x01; + } + + + // Mapper 010 + if (crc == 0xc9cce8f2) + { // Famicom Wars (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 016 + if (crc == 0x983d8175 // Datach - Battle Rush - Build Up Robot Tournament (J) + || crc == 0x894efdbc // Datach - Crayon Shin Chan - Ora to Poi Poi (J) + || crc == 0x19e81461 // Datach - Dragon Ball Z - Gekitou Tenkaichi Budou Kai (J) + || crc == 0xbe06853f // Datach - J League Super Top Players (J) + || crc == 0x0be0a328 // Datach - SD Gundam - Gundam Wars (J) + || crc == 0x5b457641 // Datach - Ultraman Club - Supokon Fight! (J) + || crc == 0xf51a7f46 // Datach - Yuu Yuu Hakusho - Bakutou Ankoku Bujutsu Kai (J) + || crc == 0x31cd9903 // Dragon Ball Z - Kyoushuu! Saiya Jin (J) + || crc == 0xe49fc53e // Dragon Ball Z 2 - Gekishin Freeza!! (J) + || crc == 0x09499f4d // Dragon Ball Z 3 - Ressen Jinzou Ningen (J) + || crc == 0x2e991109 // Dragon Ball Z Gaiden - Saiya Jin Zetsumetsu Keikaku (J) + || crc == 0x170250de) + { // Rokudenashi Blues(J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 019 + if (crc == 0x3296ff7a // Battle Fleet (J) + || crc == 0x429fd177 // Famista '90 (J) + || crc == 0xdd454208 // Hydlide 3 - Yami Kara no Houmonsha (J) + || crc == 0xb1b9e187 // Kaijuu Monogatari (J) + || crc == 0xaf15338f) + { // Mindseeker (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 026 + if (crc == 0x836cc1ab) + { // Mouryou Senki Madara (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 033 + if (crc == 0x547e6cc1) + { // Flintstones - The Rescue of Dino & Hoppy, The(J) + mapper = 48; + } + + // Mapper 065 + if (crc == 0xfd3fc292) + { // Ai Sensei no Oshiete - Watashi no Hoshi (J) + mapper = 32; + } + + // Mapper 068 + if (crc == 0xfde79681) + { // Maharaja (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 069 + if (crc == 0xfeac6916 // Honoo no Toukyuuji - Dodge Danpei 2(J) + || crc == 0x67898319) + { // Barcode World(J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 080 + if (crc == 0x95aaed34 // Mirai Shinwa Jarvas (J) + || crc == 0x17627d4b) + { // Taito Grand Prix - Eikou heno License (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 082 + if (crc == 0x4819a595) + { // Kyuukyoku Harikiri Stadium - Heisei Gannen Ban (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 086 + if (crc == 0xe63f7d0b) + { // Urusei Yatsura - Lum no Wedding Bell(J) + mapper = 101; + } + + // Mapper 118 + if (crc == 0x3b0fb600) + { // Ys 3 - Wonderers From Ys (J) + header.control1 |= (byte)EnumRomControlByte1.ROM_SAVERAM; + } + + // Mapper 180 + if (crc == 0xc68363f6) + { // Crazy Climber(J) + header.control1 &= 0xf6; + } + + // VS-Unisystem + if (crc == 0x70901b25) + { // VS Slalom + mapper = 99; + } + + if (crc == 0xd5d7eac4) + { // VS Dr. Mario + mapper = 1; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0xffbef374 // VS Castlevania + || crc == 0x8c0c2df5) + { // VS Top Gun + mapper = 2; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0xeb2dba63 // VS TKO Boxing + || crc == 0x98cfe016 // VS TKO Boxing (Alt) + || crc == 0x9818f656) + { // VS TKO Boxing (f1) + mapper = 4; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0x135adf7c) + { // VS Atari RBI Baseball + mapper = 4; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0xf9d3b0a3 // VS Super Xevious + || crc == 0x9924980a // VS Super Xevious (b1) + || crc == 0x66bb838f) + { // VS Super Xevious (b2) + mapper = 4; + header.control1 &= 0xF6; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0x17ae56be) + { // VS Freedom Force + mapper = 4; + header.control1 &= 0xF6; + header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0xe2c0a2be) + { // VS Platoon + mapper = 68; + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (crc == 0xcbe85490 // VS Excitebike + || crc == 0x29155e0c // VS Excitebike (Alt) + || crc == 0xff5135a3) + { // VS Hogan's Alley + header.control1 &= 0xF6; + header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN; + } + + if (crc == 0x0b65a917) + { // VS Mach Rider(Endurance Course) + lpPRG[0x7FDF] = 0xFF; + lpPRG[0x7FE4] = 0x00; + } + + if (crc == 0x8a6a9848 // VS Mach Rider(Endurance Course)(Alt) + || crc == 0xae8063ef) + { // VS Mach Rider(Japan, Fighting Course) + lpPRG[0x7FDD] = 0xFF; + lpPRG[0x7FE2] = 0x00; + } + + if (crc == 0x16d3f469) + { // VS Ninja Jajamaru Kun (J) + header.control1 &= 0xf6; + header.control1 |= (byte)EnumRomControlByte1.ROM_VMIRROR; + } + + if (crc == 0xc99ec059) + { // VS Raid on Bungeling Bay(J) + mapper = 99; + header.control1 &= 0xF6; + header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN; + } + if (crc == 0xca85e56d) + { // VS Mighty Bomb Jack(J) + mapper = 99; + header.control1 &= 0xF6; + header.control1 |= (byte)EnumRomControlByte1.ROM_4SCREEN; + } + + + if (crc == 0xeb2dba63 // VS TKO Boxing + || crc == 0x9818f656 // VS TKO Boxing + || crc == 0xed588f00 // VS Duck Hunt + || crc == 0x8c0c2df5 // VS Top Gun + || crc == 0x16d3f469 // VS Ninja Jajamaru Kun + || crc == 0x8850924b // VS Tetris + || crc == 0xcf36261e // VS Sky Kid + || crc == 0xe1aa8214 // VS Star Luster + || crc == 0xec461db9 // VS Pinball + || crc == 0xe528f651 // VS Pinball (alt) + || crc == 0x17ae56be // VS Freedom Force + || crc == 0xe2c0a2be // VS Platoon + || crc == 0xff5135a3 // VS Hogan's Alley + || crc == 0x70901b25 // VS Slalom + || crc == 0x0b65a917 // VS Mach Rider(Endurance Course) + || crc == 0x8a6a9848 // VS Mach Rider(Endurance Course)(Alt) + || crc == 0xae8063ef // VS Mach Rider(Japan, Fighting Course) + || crc == 0xcc2c4b5d // VS Golf + || crc == 0xa93a5aee // VS Stroke and Match Golf + || crc == 0x86167220 // VS Lady Golf + || crc == 0xffbef374 // VS Castlevania + || crc == 0x135adf7c // VS Atari RBI Baseball + || crc == 0xd5d7eac4 // VS Dr. Mario + || crc == 0x46914e3e // VS Soccer + || crc == 0x70433f2c // VS Battle City + || crc == 0x8d15a6e6 // VS bad .nes + || crc == 0x1e438d52 // VS Goonies + || crc == 0xcbe85490 // VS Excitebike + || crc == 0x29155e0c // VS Excitebike (alt) + || crc == 0x07138c06 // VS Clu Clu Land + || crc == 0x43a357ef // VS Ice Climber + || crc == 0x737dd1bf // VS Super Mario Bros + || crc == 0x4bf3972d // VS Super Mario Bros + || crc == 0x8b60cc58 // VS Super Mario Bros + || crc == 0x8192c804 // VS Super Mario Bros + || crc == 0xd99a2087 // VS Gradius + || crc == 0xf9d3b0a3 // VS Super Xevious + || crc == 0x9924980a // VS Super Xevious + || crc == 0x66bb838f // VS Super Xevious + || crc == 0xc99ec059 // VS Raid on Bungeling Bay(J) + || crc == 0xca85e56d) + { // VS Mighty Bomb Jack(J) + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + + if (mapper == 99 || mapper == 151) + { + header.control2 |= (byte)EnumRomControlByte2.ROM_VSUNISYSTEM; + } + } + } +} diff --git a/Core/VirtualNes.Core/CoreLibs/State.cs b/Core/VirtualNes.Core/CoreLibs/State.cs new file mode 100644 index 0000000..908293b --- /dev/null +++ b/Core/VirtualNes.Core/CoreLibs/State.cs @@ -0,0 +1,54 @@ +using System; + +namespace VirtualNes.Core +{ + public class DISKFILEHDR + { + public byte[] ID = new byte[12]; // "VirtuaNES DI" + public ushort BlockVersion; // 0x0200:0.30 0x0210:0.31 + public ushort Reserved; + public ulong ProgID; // 僾儘僌儔儉ID + public ushort MakerID; // 儊乕僇乕ID + public ushort DiskNo; // 僨傿僗僋悢 + public ulong DifferentSize; // 憡堘悢 + + + public byte[] ToBytes() + { + byte[] res = new byte[36]; + Array.Copy(ID, res, ID.Length); + var temp = BitConverter.GetBytes(BlockVersion); + res[12] = temp[0]; + res[13] = temp[1]; + temp = BitConverter.GetBytes(Reserved); + res[14] = temp[0]; + res[15] = temp[1]; + temp = BitConverter.GetBytes(ProgID); + res[16] = temp[0]; + res[17] = temp[1]; + res[18] = temp[2]; + res[19] = temp[3]; + res[20] = temp[4]; + res[21] = temp[5]; + res[22] = temp[6]; + res[23] = temp[7]; + temp = BitConverter.GetBytes(MakerID); + res[24] = temp[0]; + res[25] = temp[1]; + temp = BitConverter.GetBytes(DiskNo); + res[26] = temp[0]; + res[27] = temp[1]; + temp = BitConverter.GetBytes(ProgID); + res[28] = temp[0]; + res[29] = temp[1]; + res[30] = temp[2]; + res[31] = temp[3]; + res[32] = temp[4]; + res[33] = temp[5]; + res[34] = temp[6]; + res[35] = temp[7]; + + return res; + } + } +} diff --git a/Core/VirtualNes.Core/Debuger.cs b/Core/VirtualNes.Core/Debuger.cs new file mode 100644 index 0000000..8c8c090 --- /dev/null +++ b/Core/VirtualNes.Core/Debuger.cs @@ -0,0 +1,26 @@ +namespace VirtualNes.Core.Debug +{ + public static class Debuger + { + private static IDebugerImpl s_debuger; + public static void Setup(IDebugerImpl debuger) + { + s_debuger = debuger; + } + public static void Log(string message) + { + s_debuger.Log(message); + } + + public static void LogError(string message) + { + s_debuger.LogError(message); + } + } + + public interface IDebugerImpl + { + void Log(string message); + void LogError(string message); + } +} diff --git a/Core/VirtualNes.Core/MMU.cs b/Core/VirtualNes.Core/MMU.cs new file mode 100644 index 0000000..3fe1eb0 --- /dev/null +++ b/Core/VirtualNes.Core/MMU.cs @@ -0,0 +1,260 @@ +using VirtualNes.Core; + +namespace VirtualNes +{ + public static class MMU + { + // CPU 儊儌儕僶儞僋 + public static ArrayRef[] CPU_MEM_BANK = new ArrayRef[8]; // 8K扨埵 + public static byte[] CPU_MEM_TYPE = new byte[8]; + public static int[] CPU_MEM_PAGE = new int[8]; // 僗僥乕僩僙乕僽梡 + // PPU 儊儌儕僶儞僋 + public static ArrayRef[] PPU_MEM_BANK = new ArrayRef[12]; // 1K扨埵 + public static byte[] PPU_MEM_TYPE = new byte[12]; + public static int[] PPU_MEM_PAGE = new int[12]; // 僗僥乕僩僙乕僽梡 + public static byte[] CRAM_USED = new byte[16]; // 僗僥乕僩僙乕僽梡 + + // NES儊儌儕 + public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM + public static byte[] WRAM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM + public static byte[] DRAM = new byte[40 * 1024]; // 僨傿僗僋僔僗僥儉RAM + public static byte[] XRAM = new byte[8 * 1024]; // 僟儈乕僶儞僋 + public static byte[] ERAM = new byte[32 * 1024]; // 奼挘婡婍梡RAM + + public static byte[] CRAM = new byte[32 * 1024]; // 僉儍儔僋僞僷僞乕儞RAM + public static byte[] VRAM = new byte[4 * 1024]; // 僱乕儉僥乕僽儖/傾僩儕價儏乕僩RAM + + public static byte[] SPRAM = new byte[0x100]; // 僗僾儔僀僩RAM + public static byte[] BGPAL = new byte[0x10]; // BG僷儗僢僩 + public static byte[] SPPAL = new byte[0x10]; // SP僷儗僢僩 + // 儗僕僗僞 + public static byte[] CPUREG = new byte[0x18]; // Nes $4000-$4017 + public static byte[] PPUREG = new byte[0x04]; // Nes $2000-$2003 + + // PPU撪晹儗僕僗僞 + public static byte PPU56Toggle; // $2005-$2006 Toggle + public static byte PPU7_Temp; // $2007 read buffer + public static ushort loopy_t; // same as $2005/$2006 + public static ushort loopy_v; // same as $2005/$2006 + public static ushort loopy_x; // tile x offset + + // ROM僨乕僞億僀儞僞 + public static byte[] PROM; // PROM ptr + public static byte[] VROM; // VROM ptr + + // For dis... + public static byte PROM_ACCESS; + + // ROM 僶儞僋僒僀僘 + public static int PROM_8K_SIZE, PROM_16K_SIZE, PROM_32K_SIZE; + public static int VROM_1K_SIZE, VROM_2K_SIZE, VROM_4K_SIZE, VROM_8K_SIZE; + + // 儊儌儕僞僀僾 + // For PROM (CPU) + public const byte BANKTYPE_ROM = 0x00; + public const byte BANKTYPE_RAM = 0xFF; + public const byte BANKTYPE_DRAM = 0x01; + public const byte BANKTYPE_MAPPER = 0x80; + // For VROM/VRAM=/CRAM (PPU) + public const byte BANKTYPE_VROM = 0x00; + public const byte BANKTYPE_CRAM = 0x01; + public const byte BANKTYPE_VRAM = 0x80; + + // =ミラータイプ; + public const byte VRAM_HMIRROR = 0x00; // Horizontal + public const byte VRAM_VMIRROR = 0x01; // Virtical + public const byte VRAM_MIRROR4 = 0x02; // All screen + public const byte VRAM_MIRROR4L = 0x03; // PA10 L固定 $2000-$23FFのミラー + public const byte VRAM_MIRROR4H = 0x04; // PA10 H固定 $2400-$27FFのミラー + + // Frame-IRQ儗僕僗僞($4017) + public static int FrameIRQ; + + internal static void SetPROM_Bank(byte page, byte[] ptr, byte type) + { + CPU_MEM_BANK[page] = new ArrayRef(ptr, 0, ptr.Length); + CPU_MEM_TYPE[page] = type; + CPU_MEM_PAGE[page] = 0; + } + + internal static void SetPROM_Bank(byte page, ArrayRef ptr, byte type) + { + CPU_MEM_BANK[page] = ptr; + CPU_MEM_TYPE[page] = type; + CPU_MEM_PAGE[page] = 0; + } + + internal static void SetPROM_4K_Bank(ushort addr, int bank) + { + throw new System.NotImplementedException(); + + bank %= (PROM_8K_SIZE * 2); + + //TODO + //memcpy(&CPU_MEM_BANK[addr >> 13][addr & 0x1FFF], PROM + 0x1000 * bank, 0x1000); + //// memcpy( &CPU_MEM_BANK[addr>>13][addr&0x1FFF], YSRAM+0x1000*bank, 0x1000); + CPU_MEM_TYPE[addr >> 13] = BANKTYPE_ROM; + CPU_MEM_PAGE[addr >> 13] = 0; + } + + + internal static void SetPROM_8K_Bank(byte page, int bank) + { + bank %= PROM_8K_SIZE; + CPU_MEM_BANK[page] = new ArrayRef(MMU.PROM, 0x2000 * bank, MMU.PROM.Length - 0x2000 * bank); + CPU_MEM_TYPE[page] = BANKTYPE_ROM; + CPU_MEM_PAGE[page] = bank; + } + + internal static void SetPROM_16K_Bank(byte page, int bank) + { + SetPROM_8K_Bank((byte)(page + 0), bank * 2 + 0); + SetPROM_8K_Bank((byte)(page + 1), bank * 2 + 1); + } + + internal static void SetPROM_32K_Bank(int bank) + { + SetPROM_8K_Bank(4, bank * 4 + 0); + SetPROM_8K_Bank(5, bank * 4 + 1); + SetPROM_8K_Bank(6, bank * 4 + 2); + SetPROM_8K_Bank(7, bank * 4 + 3); + } + + internal static void SetPROM_32K_Bank(int bank0, int bank1, int bank2, int bank3) + { + SetPROM_8K_Bank(4, bank0); + SetPROM_8K_Bank(5, bank1); + SetPROM_8K_Bank(6, bank2); + SetPROM_8K_Bank(7, bank3); + } + + // PPU VROM bank + internal static void SetVROM_Bank(byte page, ArrayRef ptr, byte type) + { + PPU_MEM_BANK[page] = ptr; + PPU_MEM_TYPE[page] = type; + PPU_MEM_PAGE[page] = 0; + } + + internal static void SetVROM_1K_Bank(byte page, int bank) + { + bank %= VROM_1K_SIZE; + PPU_MEM_BANK[page] = new ArrayRef(VROM, 0x0400 * bank, VROM.Length - (0x0400 * bank)); + PPU_MEM_TYPE[page] = BANKTYPE_VROM; + PPU_MEM_PAGE[page] = bank; + } + + internal static void SetVROM_2K_Bank(byte page, int bank) + { + SetVROM_1K_Bank((byte)(page + 0), bank * 2 + 0); + SetVROM_1K_Bank((byte)(page + 1), bank * 2 + 1); + } + + internal static void SetVROM_4K_Bank(byte page, int bank) + { + SetVROM_1K_Bank((byte)(page + 0), bank * 4 + 0); + SetVROM_1K_Bank((byte)(page + 1), bank * 4 + 1); + SetVROM_1K_Bank((byte)(page + 2), bank * 4 + 2); + SetVROM_1K_Bank((byte)(page + 3), bank * 4 + 3); + } + + internal static void SetVROM_8K_Bank(int bank) + { + for (byte i = 0; i < 8; i++) + { + SetVROM_1K_Bank(i, bank * 8 + i); + } + } + + internal static void SetVROM_8K_Bank(int bank0, int bank1, int bank2, int bank3, + int bank4, int bank5, int bank6, int bank7) + { + SetVROM_1K_Bank(0, bank0); + SetVROM_1K_Bank(1, bank1); + SetVROM_1K_Bank(2, bank2); + SetVROM_1K_Bank(3, bank3); + SetVROM_1K_Bank(4, bank4); + SetVROM_1K_Bank(5, bank5); + SetVROM_1K_Bank(6, bank6); + SetVROM_1K_Bank(7, bank7); + } + + internal static void SetCRAM_1K_Bank(byte page, int bank) + { + bank &= 0x1F; + PPU_MEM_BANK[page] = new ArrayRef(MMU.CRAM, 0x0400 * bank, MMU.CRAM.Length - 0x0400 * bank); + PPU_MEM_TYPE[page] = BANKTYPE_CRAM; + PPU_MEM_PAGE[page] = bank; + + CRAM_USED[bank >> 2] = 0xFF; // CRAM巊梡僼儔僌 + } + + internal static void SetCRAM_2K_Bank(byte page, int bank) + { + SetCRAM_1K_Bank((byte)(page + 0), bank * 2 + 0); + SetCRAM_1K_Bank((byte)(page + 1), bank * 2 + 1); + } + + internal static void SetCRAM_4K_Bank(byte page, int bank) + { + SetCRAM_1K_Bank((byte)(page + 0), bank * 4 + 0); + SetCRAM_1K_Bank((byte)(page + 1), bank * 4 + 1); + SetCRAM_1K_Bank((byte)(page + 2), bank * 4 + 2); + SetCRAM_1K_Bank((byte)(page + 3), bank * 4 + 3); + } + + internal static void SetCRAM_8K_Bank(int bank) + { + for (byte i = 0; i < 8; i++) + { + SetCRAM_1K_Bank(i, bank * 8 + 1); + } + } + + internal static void SetVRAM_1K_Bank(byte page, int bank) + { + bank &= 3; + PPU_MEM_BANK[page] = new ArrayRef(VRAM, 0x0400 * bank, VRAM.Length - 0x0400 * bank); + PPU_MEM_TYPE[page] = BANKTYPE_VRAM; + PPU_MEM_PAGE[page] = bank; + } + + internal static void SetVRAM_Bank(int bank0, int bank1, int bank2, int bank3) + { + SetVRAM_1K_Bank(8, bank0); + SetVRAM_1K_Bank(9, bank1); + SetVRAM_1K_Bank(10, bank2); + SetVRAM_1K_Bank(11, bank3); + } + + internal static void SetVRAM_Mirror(int type) + { + switch (type) + { + case VRAM_HMIRROR: + SetVRAM_Bank(0, 0, 1, 1); + break; + case VRAM_VMIRROR: + SetVRAM_Bank(0, 1, 0, 1); + break; + case VRAM_MIRROR4L: + SetVRAM_Bank(0, 0, 0, 0); + break; + case VRAM_MIRROR4H: + SetVRAM_Bank(1, 1, 1, 1); + break; + case VRAM_MIRROR4: + SetVRAM_Bank(0, 1, 2, 3); + break; + } + } + + internal static void SetVRAM_Mirror(int bank0, int bank1, int bank2, int bank3) + { + SetVRAM_1K_Bank(8, bank0); + SetVRAM_1K_Bank(9, bank1); + SetVRAM_1K_Bank(10, bank2); + SetVRAM_1K_Bank(11, bank3); + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/EEPROM.cs b/Core/VirtualNes.Core/Mapper/EEPROM.cs new file mode 100644 index 0000000..b6347bb --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/EEPROM.cs @@ -0,0 +1,457 @@ +namespace VirtualNes.Core +{ + + + public class X24C01 + { + public const int X24C01_IDLE = 0; // Idle + public const int X24C01_ADDRESS = 1; // Address set + public const int X24C01_READ = 2; // Read + public const int X24C01_WRITE = 3; // Write + public const int X24C01_ACK = 4; // Acknowledge + public const int X24C01_ACK_WAIT = 5; // Acknowledge wait + + int now_state, next_state; + int bitcnt; + byte addr, data; + byte sda; + byte scl_old, sda_old; + + ArrayRef pEEPDATA; + + public X24C01() + { + now_state = X24C01_IDLE; + next_state = X24C01_IDLE; + addr = 0; + data = 0; + sda = 0xFF; + scl_old = 0; + sda_old = 0; + + pEEPDATA = null; + } + + public void Reset(ArrayRef ptr) + { + now_state = X24C01_IDLE; + next_state = X24C01_IDLE; + addr = 0; + data = 0; + sda = 0xFF; + scl_old = 0; + sda_old = 0; + + pEEPDATA = ptr; + } + + public void Write(byte scl_in, byte sda_in) + { + // Clock line + byte scl_rise = (byte)(~scl_old & scl_in); + byte scl_fall = (byte)(scl_old & ~scl_in); + // Data line + byte sda_rise = (byte)(~sda_old & sda_in); + byte sda_fall = (byte)(sda_old & ~sda_in); + + byte scl_old_temp = scl_old; + byte sda_old_temp = sda_old; + + scl_old = scl_in; + sda_old = sda_in; + + // Start condition? + if (scl_old_temp != 0 && sda_fall != 0) + { + now_state = X24C01_ADDRESS; + bitcnt = 0; + addr = 0; + sda = 0xFF; + return; + } + + // Stop condition? + if (scl_old_temp != 0 && sda_rise != 0) + { + now_state = X24C01_IDLE; + sda = 0xFF; + return; + } + + // SCL ____---- RISE + if (scl_rise != 0) + { + switch (now_state) + { + case X24C01_ADDRESS: + if (bitcnt < 7) + { + // 本来はMSB->LSB + addr = (byte)(addr & (~(1 << bitcnt))); + addr = (byte)(addr | ((sda_in != 0 ? 1 : 0) << bitcnt)); + } + else + { + if (sda_in != 0) + { + next_state = X24C01_READ; + data = pEEPDATA[addr & 0x7F]; + } + else + { + next_state = X24C01_WRITE; + } + } + bitcnt++; + break; + case X24C01_ACK: + sda = 0; // ACK + break; + case X24C01_READ: + if (bitcnt < 8) + { + // 本来はMSB->LSB + sda = (byte)((data & (1 << bitcnt)) != 0 ? 1 : 0); + } + bitcnt++; + break; + case X24C01_WRITE: + if (bitcnt < 8) + { + // 本来はMSB->LSB + data = (byte)(data & (~(1 << bitcnt))); + data = (byte)(data | ((sda_in != 0 ? 1 : 0) << bitcnt)); + } + bitcnt++; + break; + + case X24C01_ACK_WAIT: + if (sda_in == 0) + { + next_state = X24C01_IDLE; + } + break; + } + } + + // SCL ----____ FALL + if (scl_fall != 0) + { + switch (now_state) + { + case X24C01_ADDRESS: + if (bitcnt >= 8) + { + now_state = X24C01_ACK; + sda = 0xFF; + } + break; + case X24C01_ACK: + now_state = next_state; + bitcnt = 0; + sda = 0xFF; + break; + case X24C01_READ: + if (bitcnt >= 8) + { + now_state = X24C01_ACK_WAIT; + addr = (byte)((addr + 1) & 0x7F); + } + break; + case X24C01_WRITE: + if (bitcnt >= 8) + { + now_state = X24C01_ACK; + next_state = X24C01_IDLE; + pEEPDATA[addr & 0x7F] = data; + addr = (byte)((addr + 1) & 0x7F); + } + break; + } + } + } + + public byte Read() + { + return sda; + } + + public unsafe void Load(byte* p) + { + //now_state = *((INT*)&p[0]); + now_state = *(int*)p; + //next_state = *((INT*)&p[4]); + next_state = *(int*)p[4]; + //bitcnt = *((INT*)&p[8]); + bitcnt = *(int*)p[8]; + addr = p[12]; + data = p[13]; + sda = p[14]; + scl_old = p[15]; + sda_old = p[16]; + } + + public unsafe void Save(byte* p) + { + *((int*)&p[0]) = now_state; + *((int*)&p[4]) = next_state; + *((int*)&p[8]) = bitcnt; + p[12] = addr; + p[13] = data; + p[14] = sda; + p[15] = scl_old; + p[16] = sda_old; + } + } + + public class X24C02 + { + public const int X24C02_IDLE = 0; // Idle + public const int X24C02_DEVADDR = 1; // Device address set + public const int X24C02_ADDRESS = 2; // Address set + public const int X24C02_READ = 3; // Read + public const int X24C02_WRITE = 4; // Write + public const int X24C02_ACK = 5; // Acknowledge + public const int X24C02_NAK = 6; // Not Acknowledge + public const int X24C02_ACK_WAIT = 7; // Acknowledge wait + + int now_state, next_state; + int bitcnt; + byte addr, data, rw; + byte sda; + byte scl_old, sda_old; + + ArrayRef pEEPDATA; + + public X24C02() + { + now_state = X24C02_IDLE; + next_state = X24C02_IDLE; + addr = 0; + data = 0; + rw = 0; + sda = 0xFF; + scl_old = 0; + sda_old = 0; + + pEEPDATA = null; + } + + public void Reset(ArrayRef ptr) + { + now_state = X24C02_IDLE; + next_state = X24C02_IDLE; + addr = 0; + data = 0; + rw = 0; + sda = 0xFF; + scl_old = 0; + sda_old = 0; + + pEEPDATA = ptr; + } + + public void Write(byte scl_in, byte sda_in) + { + // Clock line + byte scl_rise = (byte)(~scl_old & scl_in); + byte scl_fall = (byte)(scl_old & ~scl_in); + // Data line + byte sda_rise = (byte)(~sda_old & sda_in); + byte sda_fall = (byte)(sda_old & ~sda_in); + + byte scl_old_temp = scl_old; + byte sda_old_temp = sda_old; + + scl_old = scl_in; + sda_old = sda_in; + + // Start condition? + if (scl_old_temp != 0 && sda_fall != 0) + { + now_state = X24C02_DEVADDR; + bitcnt = 0; + sda = 0xFF; + return; + } + + // Stop condition? + if (scl_old_temp != 0 && sda_rise != 0) + { + now_state = X24C02_IDLE; + sda = 0xFF; + return; + } + + // SCL ____---- RISE + if (scl_rise != 0) + { + switch (now_state) + { + case X24C02_DEVADDR: + if (bitcnt < 8) + { + data = (byte)(data & (~(1 << (7 - bitcnt)))); + data = (byte)(data | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt))); + } + bitcnt++; + break; + case X24C02_ADDRESS: + if (bitcnt < 8) + { + addr = (byte)(addr & (~(1 << (7 - bitcnt)))); + addr = (byte)(addr | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt))); + } + bitcnt++; + break; + case X24C02_READ: + if (bitcnt < 8) + { + sda = (byte)((data & (1 << (7 - bitcnt))) != 0 ? 1 : 0); + } + bitcnt++; + break; + case X24C02_WRITE: + if (bitcnt < 8) + { + data = (byte)(data & (~(1 << (7 - bitcnt)))); + data = (byte)(data | ((sda_in != 0 ? 1 : 0) << (7 - bitcnt))); + } + bitcnt++; + break; + case X24C02_NAK: + sda = 0xFF; // NAK + break; + case X24C02_ACK: + sda = 0; // ACK + break; + case X24C02_ACK_WAIT: + if (sda_in == 0) + { + next_state = X24C02_READ; + data = pEEPDATA[addr]; + } + break; + } + } + + // SCL ----____ FALL + if (scl_fall != 0) + { + switch (now_state) + { + case X24C02_DEVADDR: + if (bitcnt >= 8) + { + if ((data & 0xA0) == 0xA0) + { + now_state = X24C02_ACK; + rw = (byte)(data & 0x01); + sda = 0xFF; + if (rw != 0) + { + // Now address read + next_state = X24C02_READ; + data = pEEPDATA[addr]; + } + else + { + next_state = X24C02_ADDRESS; + } + bitcnt = 0; + } + else + { + now_state = X24C02_NAK; + next_state = X24C02_IDLE; + sda = 0xFF; + } + } + break; + case X24C02_ADDRESS: + if (bitcnt >= 8) + { + now_state = X24C02_ACK; + sda = 0xFF; + if (rw != 0) + { + // Readでは絶対来ないが念の為 + next_state = X24C02_IDLE; + } + else + { + // to Data Write + next_state = X24C02_WRITE; + } + bitcnt = 0; + } + break; + case X24C02_READ: + if (bitcnt >= 8) + { + now_state = X24C02_ACK_WAIT; + addr = (byte)((addr + 1) & 0xFF); + } + break; + case X24C02_WRITE: + if (bitcnt >= 8) + { + pEEPDATA[addr] = data; + now_state = X24C02_ACK; + next_state = X24C02_WRITE; + addr = (byte)((addr + 1) & 0xFF); + bitcnt = 0; + } + break; + case X24C02_NAK: + now_state = X24C02_IDLE; + bitcnt = 0; + sda = 0xFF; + break; + case X24C02_ACK: + now_state = next_state; + bitcnt = 0; + sda = 0xFF; + break; + case X24C02_ACK_WAIT: + now_state = next_state; + bitcnt = 0; + sda = 0xFF; + break; + } + } + } + + public byte Read() + { + return sda; + } + + public unsafe void Load(byte* p) + { + //now_state = *((INT*)&p[0]); + next_state = *((int*)&p[4]); + //bitcnt = *((INT*)&p[8]); + bitcnt = *((int*)&p[8]); + addr = p[12]; + data = p[13]; + rw = p[14]; + sda = p[15]; + scl_old = p[16]; + sda_old = p[17]; + } + + public unsafe void Save(byte* p) + { + *((int*)&p[0]) = now_state; + *((int*)&p[4]) = next_state; + *((int*)&p[8]) = bitcnt; + p[12] = addr; + p[13] = data; + p[14] = rw; + p[15] = sda; + p[16] = scl_old; + p[17] = sda_old; + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Mapper/Mapper.cs b/Core/VirtualNes.Core/Mapper/Mapper.cs new file mode 100644 index 0000000..8f08742 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper.cs @@ -0,0 +1,247 @@ +using System; + +namespace VirtualNes.Core +{ + public abstract class Mapper + { + protected NES nes; + + public Mapper(NES parent) + { + nes = parent; + } + + public virtual void Dispose() { } + + public abstract void Reset(); + + // $8000-$FFFF Memory write + public virtual void Write(ushort addr, byte data) { } + // $8000-$FFFF Memory read(Dummy) + public virtual void Read(ushort addr, byte data) { } + + // $4100-$7FFF Lower Memory read/write + public virtual byte ReadLow(ushort addr) + { + // $6000-$7FFF WRAM + if (addr >= 0x6000 && addr <= 0x7FFF) + { + return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + return (byte)(addr >> 8); + } + public virtual void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000 && addr <= 0x7FFF) + { + MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + } + + // $4018-$40FF Extention register read/write + public virtual byte ExRead(ushort addr) { return 0x00; } + public virtual void ExWrite(ushort addr, byte data) { } + + public virtual byte ExCmdRead(EXCMDRD cmd) { return 0x00; } + public virtual void ExCmdWrite(EXCMDWR cmd, byte data) { } + + // H sync/V sync/Clock sync + public virtual void HSync(int scanline) { } + public virtual void VSync() { } + public virtual void Clock(int cycles) { } + // PPU address bus latch + public virtual void PPU_Latch(ushort addr) { } + // PPU Character latch + public virtual void PPU_ChrLatch(ushort addr) { } + // PPU Extension character/palette + public virtual void PPU_ExtLatchX(int x) { } + public virtual void PPU_ExtLatch(ushort addr, ref byte chr_l, ref byte chr_h, ref byte attr) { } + // For State save + public virtual bool IsStateSave() { return false; } + public virtual void SaveState(byte[] p) { } + public virtual void LoadState(byte[] p) { } + + // Extension commands + // For ExCmdRead command + public enum EXCMDRD + { + EXCMDRD_NONE = 0, + EXCMDRD_DISKACCESS, + } + // For ExCmdWrite command + public enum EXCMDWR + { + EXCMDWR_NONE = 0, + EXCMDWR_DISKINSERT, + EXCMDWR_DISKEJECT, + } + + public static Mapper CreateMapper(NES parent, int no) + { + //todo : 实现加载mapper + switch (no) + { + case 0: return new Mapper000(parent); + case 1: return new Mapper001(parent); + case 2: return new Mapper002(parent); + case 3: return new Mapper003(parent); + case 4: return new Mapper004(parent); + case 5: return new Mapper005(parent); + case 6: return new Mapper006(parent); + case 7: return new Mapper007(parent); + case 8: return new Mapper008(parent); + case 9: return new Mapper009(parent); + case 10: return new Mapper010(parent); + case 11: return new Mapper011(parent); + case 12: return new Mapper012(parent); + case 13: return new Mapper013(parent); + case 15: return new Mapper015(parent); + case 16: return new Mapper016(parent); + case 17: return new Mapper017(parent); + case 18: return new Mapper018(parent); + case 19: return new Mapper019(parent); + case 21: return new Mapper021(parent); + case 22: return new Mapper022(parent); + case 23: return new Mapper023(parent); + case 24: return new Mapper024(parent); + case 25: return new Mapper025(parent); + case 26: return new Mapper026(parent); + case 27: return new Mapper027(parent); + case 32: return new Mapper032(parent); + case 33: return new Mapper033(parent); + case 34: return new Mapper034(parent); + case 35: return new Mapper035(parent); + case 40: return new Mapper040(parent); + case 41: return new Mapper041(parent); + case 42: return new Mapper042(parent); + case 43: return new Mapper043(parent); + case 44: return new Mapper044(parent); + case 45: return new Mapper045(parent); + case 46: return new Mapper046(parent); + case 47: return new Mapper047(parent); + case 48: return new Mapper048(parent); + case 50: return new Mapper050(parent); + case 51: return new Mapper051(parent); + case 57: return new Mapper057(parent); + case 58: return new Mapper058(parent); + case 60: return new Mapper060(parent); + case 61: return new Mapper061(parent); + case 62: return new Mapper062(parent); + case 64: return new Mapper064(parent); + case 65: return new Mapper065(parent); + case 66: return new Mapper066(parent); + case 67: return new Mapper067(parent); + case 68: return new Mapper068(parent); + case 69: return new Mapper069(parent); + case 70: return new Mapper070(parent); + case 71: return new Mapper071(parent); + case 72: return new Mapper072(parent); + case 73: return new Mapper073(parent); + case 74: return new Mapper074(parent); + case 75: return new Mapper075(parent); + case 76: return new Mapper076(parent); + case 77: return new Mapper077(parent); + case 78: return new Mapper078(parent); + case 79: return new Mapper079(parent); + case 80: return new Mapper080(parent); + case 82: return new Mapper082(parent); + case 83: return new Mapper083(parent); + case 85: return new Mapper085(parent); + case 86: return new Mapper086(parent); + case 87: return new Mapper087(parent); + case 88: return new Mapper088(parent); + case 89: return new Mapper089(parent); + case 90: return new Mapper090(parent); + case 91: return new Mapper091(parent); + case 92: return new Mapper092(parent); + case 93: return new Mapper093(parent); + case 94: return new Mapper094(parent); + case 95: return new Mapper095(parent); + case 96: return new Mapper096(parent); + case 97: return new Mapper097(parent); + case 99: return new Mapper099(parent); + case 100: return new Mapper100(parent); + case 101: return new Mapper101(parent); + case 105: return new Mapper105(parent); + case 108: return new Mapper108(parent); + case 109: return new Mapper109(parent); + case 110: return new Mapper110(parent); + case 111: return new Mapper111(parent); + case 112: return new Mapper112(parent); + case 113: return new Mapper113(parent); + case 114: return new Mapper114(parent); + case 115: return new Mapper115(parent); + case 116: return new Mapper116(parent); + case 117: return new Mapper117(parent); + case 118: return new Mapper118(parent); + case 119: return new Mapper119(parent); + case 122: return new Mapper122(parent); + case 133: return new Mapper133(parent); + case 134: return new Mapper134(parent); + case 135: return new Mapper135(parent); + case 140: return new Mapper140(parent); + case 142: return new Mapper142(parent); + case 151: return new Mapper151(parent); + case 160: return new Mapper160(parent); + case 162: return new Mapper162(parent); + case 163: return new Mapper163(parent); + case 164: return new Mapper164(parent); + case 165: return new Mapper165(parent); + case 167: return new Mapper167(parent); + case 175: return new Mapper175(parent); + case 176: return new Mapper176(parent); + case 178: return new Mapper178(parent); + case 180: return new Mapper180(parent); + case 181: return new Mapper181(parent); + case 182: return new Mapper182(parent); + case 183: return new Mapper183(parent); + case 185: return new Mapper185(parent); + case 187: return new Mapper187(parent); + case 188: return new Mapper188(parent); + case 189: return new Mapper189(parent); + case 190: return new Mapper190(parent); + case 191: return new Mapper191(parent); + case 192: return new Mapper192(parent); + case 193: return new Mapper193(parent); + case 194: return new Mapper194(parent); + case 195: return new Mapper195(parent); + case 198: return new Mapper198(parent); + case 199: return new Mapper199(parent); + case 200: return new Mapper200(parent); + case 201: return new Mapper201(parent); + case 202: return new Mapper202(parent); + case 216: return new Mapper216(parent); + case 222: return new Mapper222(parent); + case 225: return new Mapper225(parent); + case 226: return new Mapper226(parent); + case 227: return new Mapper227(parent); + case 228: return new Mapper228(parent); + case 229: return new Mapper229(parent); + case 230: return new Mapper230(parent); + case 231: return new Mapper231(parent); + case 232: return new Mapper232(parent); + case 233: return new Mapper233(parent); + case 234: return new Mapper234(parent); + case 235: return new Mapper235(parent); + case 236: return new Mapper236(parent); + case 240: return new Mapper240(parent); + case 241: return new Mapper241(parent); + case 242: return new Mapper242(parent); + case 243: return new Mapper243(parent); + case 244: return new Mapper244(parent); + case 245: return new Mapper245(parent); + case 246: return new Mapper246(parent); + case 248: return new Mapper248(parent); + case 249: return new Mapper249(parent); + case 251: return new Mapper251(parent); + case 252: return new Mapper252(parent); + case 254: return new Mapper254(parent); + case 255: return new Mapper255(parent); + + default: + throw new NotImplementedException($"Mapper#{no:000} is not Impl"); + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper000.cs b/Core/VirtualNes.Core/Mapper/Mapper000.cs new file mode 100644 index 0000000..520f3f9 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper000.cs @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper000 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper000 : Mapper + { + + public Mapper000(NES parent) : base(parent) { } + + public override void Reset() + { + switch (PROM_16K_SIZE) + { + default: + case 1: // 16K only + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + break; + case 2: // 32K + SetPROM_32K_Bank(0); + break; + } + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x4e7db5af) + { // Circus Charlie(J) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0x57970078) + { // F-1 Race(J) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0xaf2bbcbc // Mach Rider(JU) + || crc == 0x3acd4bf1) + { // Mach Rider(Alt)(JU) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper001.cs b/Core/VirtualNes.Core/Mapper/Mapper001.cs new file mode 100644 index 0000000..d2fa06d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper001.cs @@ -0,0 +1,409 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper001 Nintendo MMC1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper001 : Mapper + { + ushort last_addr; + + BYTE patch; + BYTE wram_patch; + BYTE wram_bank; + BYTE wram_count; + + BYTE[] reg = new byte[4]; + BYTE shift, regbuf; + + public Mapper001(NES parent) : base(parent) { } + + public override void Reset() + { + reg[0] = 0x0C; // D3=1,D2=1 + reg[1] = reg[2] = reg[3] = 0; + shift = regbuf = 0; + + patch = 0; + wram_patch = 0; + + if (PROM_16K_SIZE < 32) + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + else + { + // For 512K/1M byte Cartridge + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 16 - 1); + + patch = 1; + } + + if (VROM_8K_SIZE != 0) + { + // SetVROM_8K_Bank( 0 ); + } + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xb8e16bd0) + { // Snow Bros.(J) + patch = 2; + } + // if( crc == 0x9b565541 ) { // Triathron, The(J) + // nes.SetFrameIRQmode( FALSE ); + // } + if (crc == 0xc96c6f04) + { // Venus Senki(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + // if( crc == 0x5e3f7004 ) { // Softball Tengoku(J) + // } + + if (crc == 0x4d2edf70) + { // Night Rider(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xcd2a73f0) + { // Pirates!(U) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + patch = 2; + } + + // if( crc == 0x09efe54b ) { // Majaventure - Mahjong Senki(J) + // nes.SetFrameIRQmode( FALSE ); + // } + + if (crc == 0x11469ce3) + { // Viva! Las Vegas(J) + } + if (crc == 0xd878ebf5) + { // Ninja Ryukenden(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + // if( crc == 0x7bd7b849 ) { // Nekketsu Koukou - Dodgeball Bu(J) + // } + + if (crc == 0x466efdc2) + { // Final Fantasy(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + nes.ppu.SetExtMonoMode(true); + } + if (crc == 0xc9556b36) + { // Final Fantasy I&II(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + nes.ppu.SetExtMonoMode(true); + nes.SetSAVERAM_SIZE(16 * 1024); + wram_patch = 2; + } + + if (crc == 0x717e1169) + { // Cosmic Wars(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + + if (crc == 0xC05D2034) + { // Snake's Revenge(U) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + + if (crc == 0xb8747abf // Best Play - Pro Yakyuu Special(J) + || crc == 0x29449ba9 // Nobunaga no Yabou - Zenkoku Ban(J) + || crc == 0x2b11e0b0 // Nobunaga no Yabou - Zenkoku Ban(J)(alt) + || crc == 0x4642dda6 // Nobunaga's Ambition(U) + || crc == 0xfb69743a // Aoki Ookami to Shiroki Mejika - Genghis Khan(J) + || crc == 0x2225c20f // Genghis Khan(U) + || crc == 0xabbf7217 // Sangokushi(J) + ) + { + + nes.SetSAVERAM_SIZE(16 * 1024); + wram_patch = 1; + wram_bank = 0; + wram_count = 0; + } + } + + //void Mapper001::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF ); + + if (wram_patch == 1 && addr == 0xBFFF) + { + wram_count++; + wram_bank += (byte)(data & 0x01); + if (wram_count == 5) + { + if (wram_bank != 0) + { + SetPROM_Bank(3, new ArrayRef(WRAM, 0x2000), BANKTYPE_RAM); + } + else + { + SetPROM_Bank(3, new ArrayRef(WRAM, 0x0000), BANKTYPE_RAM); + } + wram_bank = wram_count = 0; + } + } + + if (patch != 1) + { + if ((addr & 0x6000) != (last_addr & 0x6000)) + { + shift = regbuf = 0; + } + last_addr = addr; + } + + if ((data & 0x80) != 0) + { + shift = regbuf = 0; + // reg[0] = 0x0C; // D3=1,D2=1 + reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない + return; + } + + if ((data & 0x01) != 0) regbuf |= (byte)(1 << shift); + if (++shift < 5) + return; + addr = (ushort)((addr & 0x7FFF) >> 13); + reg[addr] = regbuf; + + // DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF ); + + regbuf = 0; + shift = 0; + + if (patch != 1) + { + // For Normal Cartridge + switch (addr) + { + case 0: + if ((reg[0] & 0x02) != 0) + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + break; + case 1: + // Register #1 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(0, reg[1]); + } + } + break; + case 2: + // Register #2 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(4, reg[2]); + } + } + break; + case 3: + if (!((reg[0] & 0x08) != 0)) + { + // PRG 32K bank ($8000-$FFFF) + SetPROM_32K_Bank(reg[3] >> 1); + } + else + { + if ((reg[0] & 0x04) != 0) + { + // PRG 16K bank ($8000-$BFFF) + SetPROM_16K_Bank(4, reg[3]); + SetPROM_16K_Bank(6, PROM_16K_SIZE - 1); + } + else + { + // PRG 16K bank ($C000-$FFFF) + SetPROM_16K_Bank(6, reg[3]); + SetPROM_16K_Bank(4, 0); + } + } + break; + } + } + else + { + // For 512K/1M byte Cartridge + INT PROM_BASE = 0; + if (PROM_16K_SIZE >= 32) + { + PROM_BASE = reg[1] & 0x10; + } + + // For FinalFantasy I&II + if (wram_patch == 2) + { + if (((reg[1] & 0x18) == 0)) + { + SetPROM_Bank(3, new ArrayRef(WRAM, 0x0000), BANKTYPE_RAM); + } + else + { + SetPROM_Bank(3, new ArrayRef(WRAM, 0x2000), BANKTYPE_RAM); + } + } + + // Register #0 + if (addr == 0) + { + if ((reg[0] & 0x02) != 0) + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + // Register #1 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(0, reg[1]); + } + } + // Register #2 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(4, reg[2]); + } + } + // Register #3 + if (((reg[0] & 0x08) == 0)) + { + // PRG 32K bank ($8000-$FFFF) + SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1); + } + else + { + if ((reg[0] & 0x04) != 0) + { + // PRG 16K bank ($8000-$BFFF) + SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F)); + if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1); + } + else + { + // PRG 16K bank ($C000-$FFFF) + SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F)); + if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE); + } + } + } + } + + //void Mapper001::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + p[4] = shift; + p[5] = regbuf; + + p[6] = wram_bank; + p[7] = wram_count; + } + + //void Mapper001::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + shift = p[4]; + regbuf = p[5]; + + wram_bank = p[6]; + wram_count = p[7]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper002.cs b/Core/VirtualNes.Core/Mapper/Mapper002.cs new file mode 100644 index 0000000..2b18704 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper002.cs @@ -0,0 +1,70 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper002 UNROM // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper002 : Mapper + { + + BYTE patch; + public Mapper002(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + // if( crc == 0x322c9b09 ) { // Metal Gear (Alt)(J) + //// nes.SetFrameIRQmode( FALSE ); + // } + // if( crc == 0xe7a3867b ) { // Dragon Quest 2(Alt)(J) + // nes.SetFrameIRQmode( FALSE ); + // } + //// if( crc == 0x9622fbd9 ) { // Ballblazer(J) + //// patch = 0; + //// } + if (crc == 0x8c3d54e8 // Ikari(J) + || crc == 0x655efeed // Ikari Warriors(U) + || crc == 0x538218b2) + { // Ikari Warriors(E) + patch = 1; + } + + if (crc == 0xb20c1030) + { // Shanghai(J)(original) + patch = 2; + } + } + + + //void Mapper002::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (!nes.rom.IsSAVERAM()) + { + if (addr >= 0x5000 && patch == 1) + SetPROM_16K_Bank(4, data); + } + else + { + base.WriteLow(addr, data); + } + } + + //void Mapper002::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (patch != 2) + SetPROM_16K_Bank(4, data); + else + SetPROM_16K_Bank(4, data >> 4); + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper003.cs b/Core/VirtualNes.Core/Mapper/Mapper003.cs new file mode 100644 index 0000000..1f6dd67 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper003.cs @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper003 CNROM // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper003 : Mapper + { + + public Mapper003(NES parent) : base(parent) { } + + public override void Reset() + { + switch (PROM_16K_SIZE) + { + case 1: // 16K only + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + break; + case 2: // 32K + SetPROM_32K_Bank(0); + break; + } + // nes.SetRenderMethod( NES::TILE_RENDER ); + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x2b72fe7e) + { // Ganso Saiyuuki - Super Monkey Dai Bouken(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + nes.ppu.SetExtNameTableMode(true); + } + + // if( crc == 0xE44D95B5 ) { // ひみつw + // } + } + +#if FALSE//0 +void Mapper003::WriteLow( WORD addr, BYTE data ) +{ + if( patch ) { + Mapper::WriteLow( addr, data ); + } else { + if( nes.rom.IsSAVERAM() ) { + Mapper::WriteLow( addr, data ); + } else { + if( addr >= 0x4800 ) { + SetVROM_8K_Bank( data & 0x03 ); + } + } + } +} +#endif + + //void Mapper003::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetVROM_8K_Bank(data); + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper004.cs b/Core/VirtualNes.Core/Mapper/Mapper004.cs new file mode 100644 index 0000000..c3e8c87 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper004.cs @@ -0,0 +1,624 @@ +using System; + +namespace VirtualNes.Core +{ + public class Mapper004 : Mapper + { + private const int MMC3_IRQ_KLAX = 1; + private const int MMC3_IRQ_SHOUGIMEIKAN = 2; + private const int MMC3_IRQ_DAI2JISUPER = 3; + private const int MMC3_IRQ_DBZ2 = 4; + private const int MMC3_IRQ_ROCKMAN3 = 5; + + protected byte[] reg = new byte[8]; + protected byte prg0, prg1; + protected byte chr01, chr23, chr4, chr5, chr6, chr7; + protected byte we_sram; + + protected byte irq_type; + protected byte irq_enable; + protected byte irq_counter; + protected byte irq_latch; + protected byte irq_request; + protected byte irq_preset; + protected byte irq_preset_vbl; + + protected byte vs_patch; + protected byte vs_index; + + public override bool IsStateSave() + { + return true; + } + + private byte[] VS_TKO_Security = new byte[32] + { + 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, + 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, + 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, + 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 + }; + + public Mapper004(NES parent) : base(parent) { } + + public override void Reset() + { + for (int i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0xFF; + irq_request = 0; + irq_preset = 0; + irq_preset_vbl = 0; + + // IRQ僞僀僾愝掕 + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + irq_type = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x5c707ac4) + { // Mother(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xcb106f49) + { // F-1 Sensation(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x1170392a) + { // Karakuri Kengou Den - Musashi Road - Karakuri Nin Hashiru!(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x14a01c70) + { // Gun-Dec(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xeffeea40) + { // For Klax(J) + irq_type = MMC3_IRQ_KLAX; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xc17ae2dc) + { // God Slayer - Haruka Tenkuu no Sonata(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x126ea4a0) + { // Summer Carnival '92 - Recca(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x1f2f4861) + { // J League Fighting Soccer - The King of Ace Strikers(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x5a6860f1) + { // Shougi Meikan '92(J) + irq_type = MMC3_IRQ_SHOUGIMEIKAN; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xae280e20) + { // Shougi Meikan '93(J) + irq_type = MMC3_IRQ_SHOUGIMEIKAN; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xe19a2473) + { // Sugoro Quest - Dice no Senshi Tachi(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x702d9b33) + { // Star Wars - The Empire Strikes Back(Victor)(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xa9a0d729) + { // Dai Kaijuu - Deburas(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xc5fea9f2) + { // Dai 2 Ji - Super Robot Taisen(J) + irq_type = MMC3_IRQ_DAI2JISUPER; + } + if (crc == 0xd852c2f7) + { // Time Zone(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xecfd3c69) + { // Taito Chase H.Q.(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x7a748058) + { // Tom & Jerry (and Tuffy)(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xaafe699c) + { // Ninja Ryukenden 3 - Yomi no Hakobune(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x6cc62c06) + { // Hoshi no Kirby - Yume no Izumi no Monogatari(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x877dba77) + { // My Life My Love - Boku no Yume - Watashi no Negai(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x6f96ed15) + { // Max Warrior - Wakusei Kaigenrei(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x8685f366) + { // Matendouji(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x8635fed1) + { // Mickey Mouse 3 - Yume Fuusen(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x26ff3ea2) + { // Yume Penguin Monogatari(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x7671bc51) + { // Red Ariimaa 2(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xade11141) + { // Wanpaku Kokkun no Gourmet World(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x7c7ab58e) + { // Walkuere no Bouken - Toki no Kagi Densetsu(J) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0x26ff3ea2) + { // Yume Penguin Monogatari(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x126ea4a0) + { // Summer Carnival '92 Recca(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0x1d2e5018 // Rockman 3(J) + || crc == 0x6b999aaf) + { // Megaman 3(U) + irq_type = MMC3_IRQ_ROCKMAN3; + } + + if (crc == 0xd88d48d7) + { // Kick Master(U) + irq_type = MMC3_IRQ_ROCKMAN3; + } + + if (crc == 0xA67EA466) + { // Alien 3(U) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0xe763891b) + { // DBZ2 + irq_type = MMC3_IRQ_DBZ2; + } + + // VS-Unisystem + vs_patch = 0; + vs_index = 0; + + if (crc == 0xeb2dba63 // VS TKO Boxing + || crc == 0x98cfe016) + { // VS TKO Boxing (Alt) + vs_patch = 1; + } + if (crc == 0x135adf7c) + { // VS Atari RBI Baseball + vs_patch = 2; + } + if (crc == 0xf9d3b0a3 // VS Super Xevious + || crc == 0x9924980a // VS Super Xevious (b1) + || crc == 0x66bb838f) + { // VS Super Xevious (b2) + vs_patch = 3; + } + } + + private void SetBank_PPU() + { + if (MMU.VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + MMU.SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + MMU.SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + MMU.SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(0, chr4 & 0x07); + MMU.SetCRAM_1K_Bank(1, chr5 & 0x07); + MMU.SetCRAM_1K_Bank(2, chr6 & 0x07); + MMU.SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + MMU.SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(4, chr4 & 0x07); + MMU.SetCRAM_1K_Bank(5, chr5 & 0x07); + MMU.SetCRAM_1K_Bank(6, chr6 & 0x07); + MMU.SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + + public override byte ReadLow(ushort addr) + { + if (vs_patch == 0) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + return MMU.XRAM[addr - 0x4000]; + } + } + else if (vs_patch == 1) + { + // VS TKO Boxing Security + if (addr == 0x5E00) + { + vs_index = 0; + return 0x00; + } + else if (addr == 0x5E01) + { + return VS_TKO_Security[(vs_index++) & 0x1F]; + } + } + else if (vs_patch == 2) + { + // VS Atari RBI Baseball Security + if (addr == 0x5E00) + { + vs_index = 0; + return 0x00; + } + else if (addr == 0x5E01) + { + if (vs_index++ == 9) + return 0x6F; + else + return 0xB4; + } + } + else if (vs_patch == 3) + { + // VS Super Xevious + switch (addr) + { + case 0x54FF: + return 0x05; + case 0x5678: + if (vs_index != 0) + return 0x00; + else + return 0x01; + case 0x578f: + if (vs_index != 0) + return 0xD1; + else + return 0x89; + case 0x5567: + if (vs_index != 0) + { + vs_index = 0; + return 0x3E; + } + else + { + vs_index = 1; + return 0x37; + } + default: + break; + } + } + + return base.ReadLow(addr); + } + + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + MMU.XRAM[addr - 0x4000] = data; + } + else + { + base.WriteLow(addr, data); + } + } + + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR); + else MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() ); + break; + case 0xC000: + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() ); + reg[4] = data; + if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3) + { + irq_counter = data; + } + else + { + irq_latch = data; + } + if (irq_type == MMC3_IRQ_DBZ2) + { + irq_latch = 0x07; + } + break; + case 0xC001: + reg[5] = data; + if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3) + { + irq_latch = data; + } + else + { + if ((nes.GetScanline() < 240) || (irq_type == MMC3_IRQ_SHOUGIMEIKAN)) + { + irq_counter |= 0x80; + irq_preset = 0xFF; + } + else + { + irq_counter |= 0x80; + irq_preset_vbl = 0xFF; + irq_preset = 0; + } + } + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + + // nes->cpu->ClrIRQ( IRQ_MAPPER ); + break; + } + } + + public override void Clock(int cycles) + { + + } + + public override void HSync(int scanline) + { + if (irq_type == MMC3_IRQ_KLAX) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (irq_counter == 0) + { + irq_counter = irq_latch; + irq_request = 0xFF; + } + if (irq_counter > 0) + { + irq_counter--; + } + } + } + if (irq_request != 0) + { + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + else if (irq_type == MMC3_IRQ_ROCKMAN3) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((--irq_counter) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + } + } + } + if (irq_request != 0) + { + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + else + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_preset_vbl != 0) + { + irq_counter = irq_latch; + irq_preset_vbl = 0; + } + if (irq_preset != 0) + { + irq_counter = irq_latch; + irq_preset = 0; + if (irq_type == MMC3_IRQ_DAI2JISUPER && scanline == 0) + { + irq_counter--; + } + } + else if (irq_counter > 0) + { + irq_counter--; + } + + if (irq_counter == 0) + { + if (irq_enable != 0) + { + irq_request = 0xFF; + + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + irq_preset = 0xFF; + } + } + } + } + + private void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + MMU.SetPROM_32K_Bank(MMU.PROM_8K_SIZE - 2, prg1, prg0, MMU.PROM_8K_SIZE - 1); + } + else + { + MMU.SetPROM_32K_Bank(prg0, prg1, MMU.PROM_8K_SIZE - 2, MMU.PROM_8K_SIZE - 1); + } + } + + public override void SaveState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = (byte)irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + p[20] = irq_preset; + p[21] = irq_preset_vbl; + } + + public override void LoadState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = (Byte)p[17]; + irq_latch = p[18]; + irq_request = p[19]; + irq_preset = p[20]; + irq_preset_vbl = p[21]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper005.cs b/Core/VirtualNes.Core/Mapper/Mapper005.cs new file mode 100644 index 0000000..78bc616 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper005.cs @@ -0,0 +1,845 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper005 Nintendo MMC5 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.Core.PPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper005 : Mapper + { + public const int MMC5_IRQ_METAL = 1 << 0; + + BYTE sram_size; + + BYTE prg_size; // $5100 + BYTE chr_size; // $5101 + BYTE sram_we_a, sram_we_b; // $5102-$5103 + BYTE graphic_mode; // $5104 + BYTE nametable_mode; // $5105 + BYTE[] nametable_type = new byte[4]; // $5105 use + + BYTE sram_page; // $5113 + + BYTE fill_chr, fill_pal; // $5106-$5107 + BYTE split_control; // $5200 + BYTE split_scroll; // $5201 + BYTE split_page; // $5202 + + BYTE split_x; + ushort split_addr; + ushort split_yofs; + + BYTE chr_type; + BYTE chr_mode; // $5120-$512B use + //BYTE chr_page[2][8]; + BYTE[,] chr_page = new byte[2, 8]; // $5120-$512B + // BGパターン用バンク + ArrayRef[] BG_MEM_BANK = new ArrayRef[8]{ + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + new ArrayRef(), + }; + BYTE[] BG_MEM_PAGE = new byte[8]; + + BYTE irq_status; // $5204(R) + BYTE irq_enable; // $5204(W) + BYTE irq_line; // $5203 + BYTE irq_scanline; + BYTE irq_clear; // HSyncで使用 + BYTE irq_type; + + BYTE mult_a, mult_b; // $5205-$5206 + public Mapper005(NES parent) : base(parent) { } + + public override void Reset() + { + byte i; + + prg_size = 3; + chr_size = 3; + + sram_we_a = 0x00; + sram_we_b = 0x00; + + graphic_mode = 0; + nametable_mode = 0; + + for (i = 0; i < 4; i++) + { + nametable_type[i] = 0; + } + + fill_chr = fill_pal = 0; + split_control = split_scroll = split_page = 0; + + irq_enable = 0; + irq_status = 0; + irq_scanline = 0; + irq_line = 0; + irq_clear = 0; + + irq_type = 0; + + mult_a = mult_b = 0; + + chr_type = 0; + chr_mode = 0; + for (i = 0; i < 8; i++) + { + chr_page[0, i] = i; + chr_page[1, i] = (byte)(4 + (i & 0x03)); + } + + SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + for (i = 0; i < 8; i++) + { + BG_MEM_BANK[i].SetArray(VROM, 0x0400 * i); + BG_MEM_PAGE[i] = i; + } + + // SRAM設定 + SetBank_SRAM(3, 0); + + sram_size = 0; + nes.SetSAVERAM_SIZE(16 * 1024); + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x2b548d75 // Bandit Kings of Ancient China(U) + || crc == 0xf4cd4998 // Dai Koukai Jidai(J) + || crc == 0x8fa95456 // Ishin no Arashi(J) + || crc == 0x98c8e090 // Nobunaga no Yabou - Sengoku Gunyuu Den(J) + || crc == 0x8e9a5e2f // L'Empereur(Alt)(U) + || crc == 0x57e3218b // L'Empereur(U) + || crc == 0x2f50bd38 // L'Empereur(J) + || crc == 0xb56958d1 // Nobunaga's Ambition 2(U) + || crc == 0xe6c28c5f // Suikoden - Tenmei no Chikai(J) + || crc == 0xcd35e2e9) + { // Uncharted Waters(U) + sram_size = 1; + nes.SetSAVERAM_SIZE(32 * 1024); + } + else + if (crc == 0xf4120e58 // Aoki Ookami to Shiroki Mejika - Genchou Hishi(J) + || crc == 0x286613d8 // Nobunaga no Yabou - Bushou Fuuun Roku(J) + || crc == 0x11eaad26 // Romance of the Three Kingdoms 2(U) + || crc == 0x95ba5733) + { // Sangokushi 2(J) + sram_size = 2; + nes.SetSAVERAM_SIZE(64 * 1024); + } + + if (crc == 0x95ca9ec7) + { // Castlevania 3 - Dracula's Curse(U) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0xcd9acf43) + { // Metal Slader Glory(J) + irq_type = MMC5_IRQ_METAL; + } + + if (crc == 0xe91548d8) + { // Shin 4 Nin Uchi Mahjong - Yakuman Tengoku(J) + chr_type = 1; + } + + nes.ppu.SetExtLatchMode(true); + nes.apu.SelectExSound(8); + } + + //BYTE Mapper005::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + BYTE data = (BYTE)(addr >> 8); + + switch (addr) + { + case 0x5015: + data = nes.apu.ExRead(addr); + break; + + case 0x5204: + data = irq_status; + // irq_status = 0; + irq_status = (byte)(irq_status & ~0x80); + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x5205: + data = (byte)(mult_a * mult_b); + break; + case 0x5206: + data = (BYTE)((mult_a * mult_b) >> 8); + break; + } + + if (addr >= 0x5C00 && addr <= 0x5FFF) + { + if (graphic_mode >= 2) + { // ExRAM mode + data = VRAM[0x0800 + (addr & 0x3FF)]; + } + } + else if (addr >= 0x6000 && addr <= 0x7FFF) + { + data = base.ReadLow(addr); + } + + return data; + } + + //void Mapper005::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + byte i; + + switch (addr) + { + case 0x5100: + prg_size = (byte)(data & 0x03); + break; + case 0x5101: + chr_size = (byte)(data & 0x03); + break; + + case 0x5102: + sram_we_a = (byte)(data & 0x03); + break; + case 0x5103: + sram_we_b = (byte)(data & 0x03); + break; + + case 0x5104: + graphic_mode = (byte)(data & 0x03); + break; + case 0x5105: + nametable_mode = data; + for (i = 0; i < 4; i++) + { + nametable_type[i] = (byte)(data & 0x03); + SetVRAM_1K_Bank((byte)(8 + i), nametable_type[i]); + data >>= 2; + } + break; + + case 0x5106: + fill_chr = data; + break; + case 0x5107: + fill_pal = (byte)(data & 0x03); + break; + + case 0x5113: + SetBank_SRAM(3, (byte)(data & 0x07)); + break; + + case 0x5114: + case 0x5115: + case 0x5116: + case 0x5117: + SetBank_CPU(addr, data); + break; + + case 0x5120: + case 0x5121: + case 0x5122: + case 0x5123: + case 0x5124: + case 0x5125: + case 0x5126: + case 0x5127: + chr_mode = 0; + chr_page[0, addr & 0x07] = data; + SetBank_PPU(); + break; + + case 0x5128: + case 0x5129: + case 0x512A: + case 0x512B: + chr_mode = 1; + chr_page[1, (addr & 0x03) + 0] = data; + chr_page[1, (addr & 0x03) + 4] = data; + SetBank_PPU(); + break; + + case 0x5200: + split_control = data; + break; + case 0x5201: + split_scroll = data; + break; + case 0x5202: + split_page = (byte)(data & 0x3F); + break; + + case 0x5203: + irq_line = data; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x5204: + irq_enable = data; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0x5205: + mult_a = data; + break; + case 0x5206: + mult_b = data; + break; + + default: + if (addr >= 0x5000 && addr <= 0x5015) + { + nes.apu.ExWrite(addr, data); + } + else if (addr >= 0x5C00 && addr <= 0x5FFF) + { + if (graphic_mode == 2) + { // ExRAM + VRAM[0x0800 + (addr & 0x3FF)] = data; + } + else if (graphic_mode != 3) + { // Split,ExGraphic + if ((irq_status & 0x40) != 0) + { + VRAM[0x0800 + (addr & 0x3FF)] = data; + } + else + { + VRAM[0x0800 + (addr & 0x3FF)] = 0; + } + } + } + else if (addr >= 0x6000 && addr <= 0x7FFF) + { + if ((sram_we_a == 0x02) && (sram_we_b == 0x01)) + { + if (CPU_MEM_TYPE[3] == BANKTYPE_RAM) + { + CPU_MEM_BANK[3][addr & 0x1FFF] = data; + } + } + } + break; + } + } + + //void Mapper005::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (sram_we_a == 0x02 && sram_we_b == 0x01) + { + if (addr >= 0x8000 && addr < 0xE000) + { + if (CPU_MEM_TYPE[addr >> 13] == BANKTYPE_RAM) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + } + } + } + + void SetBank_CPU(uint addr, BYTE data) + { + if ((data & 0x80) != 0) + { + // PROM Bank + switch (addr & 7) + { + case 4: + if (prg_size == 3) + { + SetPROM_8K_Bank(4, data & 0x7F); + } + break; + case 5: + if (prg_size == 1 || prg_size == 2) + { + SetPROM_16K_Bank(4, (data & 0x7F) >> 1); + } + else if (prg_size == 3) + { + SetPROM_8K_Bank(5, (data & 0x7F)); + } + break; + case 6: + if (prg_size == 2 || prg_size == 3) + { + SetPROM_8K_Bank(6, (data & 0x7F)); + } + break; + case 7: + if (prg_size == 0) + { + SetPROM_32K_Bank((data & 0x7F) >> 2); + } + else if (prg_size == 1) + { + SetPROM_16K_Bank(6, (data & 0x7F) >> 1); + } + else if (prg_size == 2 || prg_size == 3) + { + SetPROM_8K_Bank(7, (data & 0x7F)); + } + break; + } + } + else + { + // WRAM Bank + switch (addr & 7) + { + case 4: + if (prg_size == 3) + { + SetBank_SRAM(4, (byte)(data & 0x07)); + } + break; + case 5: + if (prg_size == 1 || prg_size == 2) + { + SetBank_SRAM(4, (byte)((data & 0x06) + 0)); + SetBank_SRAM(5, (byte)((data & 0x06) + 1)); + } + else if (prg_size == 3) + { + SetBank_SRAM(5, (byte)(data & 0x07)); + } + break; + case 6: + if (prg_size == 2 || prg_size == 3) + { + SetBank_SRAM(6, (byte)(data & 0x07)); + } + break; + } + } + } + + private ArrayRef _prom_bank = new ArrayRef(); + void SetBank_SRAM(BYTE page, BYTE data) + { + if (sram_size == 0) data = (byte)((data > 3) ? 8 : 0); + if (sram_size == 1) data = (byte)((data > 3) ? 1 : 0); + if (sram_size == 2) data = (byte)((data > 3) ? 8 : data); + if (sram_size == 3) data = (byte)((data > 3) ? 4 : data); + + if (data != 8) + { + int offset = 0x2000 * data; + _prom_bank.SetArray(WRAM, offset, WRAM.Length - offset); + SetPROM_Bank(page, _prom_bank, BANKTYPE_RAM); + CPU_MEM_PAGE[page] = data; + } + else + { + CPU_MEM_TYPE[page] = BANKTYPE_ROM; + } + } + + void SetBank_PPU() + { + INT i; + + if (chr_mode == 0) + { + // PPU SP Bank + switch (chr_size) + { + case 0: + SetVROM_8K_Bank(chr_page[0, 7]); + break; + case 1: + SetVROM_4K_Bank(0, chr_page[0, 3]); + SetVROM_4K_Bank(4, chr_page[0, 7]); + break; + case 2: + SetVROM_2K_Bank(0, chr_page[0, 1]); + SetVROM_2K_Bank(2, chr_page[0, 3]); + SetVROM_2K_Bank(4, chr_page[0, 5]); + SetVROM_2K_Bank(6, chr_page[0, 7]); + break; + case 3: + SetVROM_8K_Bank(chr_page[0, 0], + chr_page[0, 1], + chr_page[0, 2], + chr_page[0, 3], + chr_page[0, 4], + chr_page[0, 5], + chr_page[0, 6], + chr_page[0, 7]); + break; + } + } + else if (chr_mode == 1) + { + // PPU BG Bank + switch (chr_size) + { + case 0: + for (i = 0; i < 8; i++) + { + BG_MEM_BANK[i].SetArray(VROM, 0x2000 * (chr_page[1, 7] % VROM_8K_SIZE) + 0x0400 * i); + BG_MEM_PAGE[i] = (byte)((chr_page[1, 7] % VROM_8K_SIZE) * 8 + i); + } + break; + case 1: + for (i = 0; i < 4; i++) + { + BG_MEM_BANK[i + 0].SetArray(VROM, 0x1000 * (chr_page[1, 3] % VROM_4K_SIZE) + 0x0400 * i); + BG_MEM_BANK[i + 4].SetArray(VROM, 0x1000 * (chr_page[1, 7] % VROM_4K_SIZE) + 0x0400 * i); + BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 3] % VROM_4K_SIZE) * 4 + i); + BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 7] % VROM_4K_SIZE) * 4 + i); + } + break; + case 2: + for (i = 0; i < 2; i++) + { + BG_MEM_BANK[i + 0].SetArray(VROM, 0x0800 * (chr_page[1, 1] % VROM_2K_SIZE) + 0x0400 * i); + BG_MEM_BANK[i + 2].SetArray(VROM, 0x0800 * (chr_page[1, 3] % VROM_2K_SIZE) + 0x0400 * i); + BG_MEM_BANK[i + 4].SetArray(VROM, 0x0800 * (chr_page[1, 5] % VROM_2K_SIZE) + 0x0400 * i); + BG_MEM_BANK[i + 6].SetArray(VROM, 0x0800 * (chr_page[1, 7] % VROM_2K_SIZE) + 0x0400 * i); + BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 1] % VROM_2K_SIZE) * 2 + i); + BG_MEM_PAGE[i + 2] = (byte)((chr_page[1, 3] % VROM_2K_SIZE) * 2 + i); + BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 5] % VROM_2K_SIZE) * 2 + i); + BG_MEM_PAGE[i + 6] = (byte)((chr_page[1, 7] % VROM_2K_SIZE) * 2 + i); + } + break; + case 3: + for (i = 0; i < 8; i++) + { + BG_MEM_BANK[i].SetArray(VROM, 0x0400 * (chr_page[1, i] % VROM_1K_SIZE)); + BG_MEM_PAGE[i] = (byte)((chr_page[1, i] % VROM_1K_SIZE) + i); + } + break; + } + } + } + + public override void HSync(int scanline) + { + if ((irq_type & MMC5_IRQ_METAL) != 0) + { + if (irq_scanline == irq_line) + { + irq_status |= 0x80; + } + } + + // if( nes.ppu.IsDispON() && scanline < 239 ) { + if (nes.ppu.IsDispON() && scanline < 240) + { + irq_scanline++; + irq_status |= 0x40; + irq_clear = 0; + } + else if ((irq_type & MMC5_IRQ_METAL) != 0) + { + irq_scanline = 0; + irq_status = (byte)(irq_status & ~0x80); + irq_status = (byte)(irq_status & ~0x40); + } + + if ((irq_type & MMC5_IRQ_METAL) == 0) + { + if (irq_scanline == irq_line) + { + irq_status |= 0x80; + } + + if (++irq_clear > 2) + { + irq_scanline = 0; + irq_status = (byte)(irq_status & ~0x80); + irq_status = (byte)(irq_status & ~0x40); + + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + } + + if ((irq_enable & 0x80) != 0 && (irq_status & 0x80) != 0 && (irq_status & 0x40) != 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + /// nes.cpu.IRQ_NotPending(); + } + + // For Split mode! + if (scanline == 0) + { + split_yofs = (ushort)(split_scroll & 0x07); + split_addr = (ushort)(((split_scroll & 0xF8) << 2)); + } + else if (nes.ppu.IsDispON()) + { + if (split_yofs == 7) + { + split_yofs = 0; + if ((split_addr & 0x03E0) == 0x03A0) + { + split_addr &= 0x001F; + } + else + { + if ((split_addr & 0x03E0) == 0x03E0) + { + split_addr &= 0x001F; + } + else + { + split_addr += 0x0020; + } + } + } + else + { + split_yofs++; + } + } + } + + //void Mapper005::PPU_ExtLatchX(INT x) + public override void PPU_ExtLatchX(int x) + { + split_x = (byte)x; + } + + //void Mapper005::PPU_ExtLatch(WORD addr, BYTE& chr_l, BYTE& chr_h, BYTE& attr ) + public override void PPU_ExtLatch(ushort addr, ref byte chr_l, ref byte chr_h, ref byte attr) + { + ushort ntbladr, attradr, tileadr, tileofs; + ushort tile_yofs; + uint tilebank; + bool bSplit; + + tile_yofs = nes.ppu.GetTILEY(); + + bSplit = false; + if ((split_control & 0x80) != 0) + { + if ((split_control & 0x40) == 0) + { + // Left side + if ((split_control & 0x1F) > split_x) + { + bSplit = true; + } + } + else + { + // Right side + if ((split_control & 0x1F) <= split_x) + { + bSplit = true; + } + } + } + + if (!bSplit) + { + if (nametable_type[(addr & 0x0C00) >> 10] == 3) + { + // Fill mode + if (graphic_mode == 1) + { + // ExGraphic mode + ntbladr = (ushort)(0x2000 + (addr & 0x0FFF)); + // Get Nametable + tileadr = (ushort)(fill_chr * 0x10 + tile_yofs); + // Get TileBank + tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE)); + // Attribute + attr = (byte)((fill_pal << 2) & 0x0C); + // Get Pattern + chr_l = VROM[tilebank + tileadr]; + chr_h = VROM[tilebank + tileadr + 8]; + } + else + { + // Normal + tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000); + tileadr = (ushort)(tileofs + fill_chr * 0x10 + tile_yofs); + attr = (byte)((fill_pal << 2) & 0x0C); + // Get Pattern + if (chr_type != 0) + { + chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + else + { + chr_l = BG_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = BG_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + } + } + else if (graphic_mode == 1) + { + // ExGraphic mode + ntbladr = (ushort)(0x2000 + (addr & 0x0FFF)); + // Get Nametable + tileadr = (ushort)(PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs); + // Get TileBank + tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE)); + // Get Attribute + attr = (byte)((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0xC0) >> 4); + // Get Pattern + chr_l = VROM[tilebank + tileadr]; + chr_h = VROM[tilebank + tileadr + 8]; + } + else + { + // Normal or ExVRAM + tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000); + ntbladr = (ushort)(0x2000 + (addr & 0x0FFF)); + attradr = (ushort)(0x23C0 + (addr & 0x0C00) + ((addr & 0x0380) >> 4) + ((addr & 0x001C) >> 2)); + // Get Nametable + tileadr = (ushort)(tileofs + PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs); + // Get Attribute + attr = PPU_MEM_BANK[attradr >> 10][attradr & 0x03FF]; + if ((ntbladr & 0x0002) != 0) attr >>= 2; + if ((ntbladr & 0x0040) != 0) attr >>= 4; + attr = (byte)((attr & 0x03) << 2); + // Get Pattern + if (chr_type != 0) + { + chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + else + { + chr_l = BG_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = BG_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + } + } + else + { + ntbladr = (ushort)(((split_addr & 0x03E0) | (split_x & 0x1F)) & 0x03FF); + // Get Split TileBank + tilebank = (uint)(0x1000 * (split_page % VROM_4K_SIZE)); + tileadr = (ushort)(VRAM[0x0800 + ntbladr] * 0x10 + split_yofs); + // Get Attribute + attradr = (ushort)(0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2)); + attr = VRAM[0x0800 + attradr]; + if ((ntbladr & 0x0002) != 0) attr >>= 2; + if ((ntbladr & 0x0040) != 0) attr >>= 4; + attr = (byte)((attr & 0x03) << 2); + // Get Pattern + chr_l = VROM[tilebank + tileadr]; + chr_h = VROM[tilebank + tileadr + 8]; + } + } + + //void Mapper005::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = prg_size; + p[1] = chr_size; + p[2] = sram_we_a; + p[3] = sram_we_b; + p[4] = graphic_mode; + p[5] = nametable_mode; + p[6] = nametable_type[0]; + p[7] = nametable_type[1]; + p[8] = nametable_type[2]; + p[9] = nametable_type[3]; + p[10] = sram_page; + p[11] = fill_chr; + p[12] = fill_pal; + p[13] = split_control; + p[14] = split_scroll; + p[15] = split_page; + p[16] = chr_mode; + p[17] = irq_status; + p[18] = irq_enable; + p[19] = irq_line; + p[20] = irq_scanline; + p[21] = irq_clear; + p[22] = mult_a; + p[23] = mult_b; + + INT i, j; + for (j = 0; j < 2; j++) + { + for (i = 0; i < 8; i++) + { + p[24 + j * 8 + i] = chr_page[j, i]; + } + } + // for( i = 0; i < 8; i++ ) { + // p[40+i] = BG_MEM_PAGE[i]; + // } + } + + //void Mapper005::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + prg_size = p[0]; + chr_size = p[1]; + sram_we_a = p[2]; + sram_we_b = p[3]; + graphic_mode = p[4]; + nametable_mode = p[5]; + nametable_type[0] = p[6]; + nametable_type[1] = p[7]; + nametable_type[2] = p[8]; + nametable_type[3] = p[9]; + sram_page = p[10]; + fill_chr = p[11]; + fill_pal = p[12]; + split_control = p[13]; + split_scroll = p[14]; + split_page = p[15]; + chr_mode = p[16]; + irq_status = p[17]; + irq_enable = p[18]; + irq_line = p[19]; + irq_scanline = p[20]; + irq_clear = p[21]; + mult_a = p[22]; + mult_b = p[23]; + + INT i, j; + + for (j = 0; j < 2; j++) + { + for (i = 0; i < 8; i++) + { + chr_page[j, i] = p[24 + j * 8 + i]; + } + } + // // BGバンクの再設定処理 + // for( i = 0; i < 8; i++ ) { + // BG_MEM_PAGE[i] = p[40+i]%VROM_1K_SIZE; + // } + // for( i = 0; i < 8; i++ ) { + // BG_MEM_BANK[i] = VROM+0x0400*BG_MEM_PAGE[i]; + // } + + SetBank_PPU(); + + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper006.cs b/Core/VirtualNes.Core/Mapper/Mapper006.cs new file mode 100644 index 0000000..9b37ae1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper006.cs @@ -0,0 +1,115 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper006 FFE F4xxx // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper006 : Mapper + { + BYTE irq_enable; + INT irq_counter; + public Mapper006(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 14, 15); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + else + { + SetCRAM_8K_Bank(0); + } + + irq_enable = 0; + irq_counter = 0; + } + + //void Mapper006::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x42FE: + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + case 0x42FF: + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0x4501: + irq_enable = 0; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x4502: + irq_counter = (irq_counter & 0xFF00) | data; + break; + case 0x4503: + irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8); + irq_enable = 0xFF; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + default: + base.WriteLow(addr, data); + break; + } + } + + //void Mapper006::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_16K_Bank(4, (data & 0x3C) >> 2); + SetCRAM_8K_Bank(data & 0x03); + } + + //void Mapper006::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + irq_counter += 133; + if (irq_counter >= 0xFFFF) + { + // nes.cpu.IRQ(); + irq_counter = 0; + + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper006::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 1); + } + + //void Mapper006::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_counter = *(INT*)&p[1]; + irq_counter = BitConverter.ToInt32(p, 1); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper007.cs b/Core/VirtualNes.Core/Mapper/Mapper007.cs new file mode 100644 index 0000000..56316d3 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper007.cs @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper007 AOROM/AMROM // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + +namespace VirtualNes.Core +{ + public class Mapper007 : Mapper + { + + BYTE patch; + public Mapper007(NES parent) : base(parent) { } + + public override void Reset() + { + patch = 0; + + SetPROM_32K_Bank(0); + SetVRAM_Mirror(VRAM_MIRROR4L); + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x3c9fe649) + { // WWF Wrestlemania Challenge(U) + SetVRAM_Mirror(VRAM_VMIRROR); + patch = 1; + } + if (crc == 0x09874777) + { // Marble Madness(U) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0x279710DC // Battletoads (U) + || crc == 0xCEB65B06) + { // Battletoads Double Dragon (U) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + //::memset(WRAM, 0, sizeof(WRAM)); + MemoryUtility.ZEROMEMORY(WRAM, WRAM.Length); + } + } + + //void Mapper007::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank(data & 0x07); + + if (patch != 0) + { + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper008.cs b/Core/VirtualNes.Core/Mapper/Mapper008.cs new file mode 100644 index 0000000..4c53279 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper008.cs @@ -0,0 +1,29 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper008 FFE F3xxx // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper008 : Mapper + { + + public Mapper008(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + SetVROM_8K_Bank(0); + } + + //void Mapper008::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_16K_Bank(4, (data & 0xF8) >> 3); + SetVROM_8K_Bank(data & 0x07); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper009.cs b/Core/VirtualNes.Core/Mapper/Mapper009.cs new file mode 100644 index 0000000..ead96c6 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper009.cs @@ -0,0 +1,127 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper009 Nintendo MMC2 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper009 : Mapper + { + BYTE[] reg = new byte[4]; + BYTE latch_a, latch_b; + + public Mapper009(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + reg[0] = 0; reg[1] = 4; + reg[2] = 0; reg[3] = 0; + + latch_a = 0xFE; + latch_b = 0xFE; + SetVROM_4K_Bank(0, 4); + SetVROM_4K_Bank(4, 0); + + nes.ppu.SetChrLatchMode(true); + } + + //void Mapper009::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0xA000: + SetPROM_8K_Bank(4, data); + break; + case 0xB000: + reg[0] = data; + if (latch_a == 0xFD) + { + SetVROM_4K_Bank(0, reg[0]); + } + break; + case 0xC000: + reg[1] = data; + if (latch_a == 0xFE) + { + SetVROM_4K_Bank(0, reg[1]); + } + break; + case 0xD000: + reg[2] = data; + if (latch_b == 0xFD) + { + SetVROM_4K_Bank(4, reg[2]); + } + break; + case 0xE000: + reg[3] = data; + if (latch_b == 0xFE) + { + SetVROM_4K_Bank(4, reg[3]); + } + break; + case 0xF000: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + } + + //void Mapper009::PPU_ChrLatch(WORD addr) + public override void PPU_ChrLatch(ushort addr) + { + if ((addr & 0x1FF0) == 0x0FD0 && latch_a != 0xFD) + { + latch_a = 0xFD; + SetVROM_4K_Bank(0, reg[0]); + } + else if ((addr & 0x1FF0) == 0x0FE0 && latch_a != 0xFE) + { + latch_a = 0xFE; + SetVROM_4K_Bank(0, reg[1]); + } + else if ((addr & 0x1FF0) == 0x1FD0 && latch_b != 0xFD) + { + latch_b = 0xFD; + SetVROM_4K_Bank(4, reg[2]); + } + else if ((addr & 0x1FF0) == 0x1FE0 && latch_b != 0xFE) + { + latch_b = 0xFE; + SetVROM_4K_Bank(4, reg[3]); + } + } + + //void Mapper009::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + p[4] = latch_a; + p[5] = latch_b; + } + + //void Mapper009::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + latch_a = p[4]; + latch_b = p[5]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper010.cs b/Core/VirtualNes.Core/Mapper/Mapper010.cs new file mode 100644 index 0000000..62ab48a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper010.cs @@ -0,0 +1,127 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper010 Nintendo MMC4 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper010 : Mapper + { + BYTE[] reg = new byte[4]; + BYTE latch_a, latch_b; + + public Mapper010(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + reg[0] = 0; reg[1] = 4; + reg[2] = 0; reg[3] = 0; + + latch_a = 0xFE; + latch_b = 0xFE; + SetVROM_4K_Bank(0, 4); + SetVROM_4K_Bank(4, 0); + + nes.ppu.SetChrLatchMode(true); + } + + //void Mapper010::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0xA000: + SetPROM_16K_Bank(4, data); + break; + case 0xB000: + reg[0] = data; + if (latch_a == 0xFD) + { + SetVROM_4K_Bank(0, reg[0]); + } + break; + case 0xC000: + reg[1] = data; + if (latch_a == 0xFE) + { + SetVROM_4K_Bank(0, reg[1]); + } + break; + case 0xD000: + reg[2] = data; + if (latch_b == 0xFD) + { + SetVROM_4K_Bank(4, reg[2]); + } + break; + case 0xE000: + reg[3] = data; + if (latch_b == 0xFE) + { + SetVROM_4K_Bank(4, reg[3]); + } + break; + case 0xF000: + if ((data & 0x01) != 0) + SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + } + + //void Mapper010::PPU_ChrLatch(WORD addr) + public override void PPU_ChrLatch(ushort addr) + { + if ((addr & 0x1FF0) == 0x0FD0 && latch_a != 0xFD) + { + latch_a = 0xFD; + SetVROM_4K_Bank(0, reg[0]); + } + else if ((addr & 0x1FF0) == 0x0FE0 && latch_a != 0xFE) + { + latch_a = 0xFE; + SetVROM_4K_Bank(0, reg[1]); + } + else if ((addr & 0x1FF0) == 0x1FD0 && latch_b != 0xFD) + { + latch_b = 0xFD; + SetVROM_4K_Bank(4, reg[2]); + } + else if ((addr & 0x1FF0) == 0x1FE0 && latch_b != 0xFE) + { + latch_b = 0xFE; + SetVROM_4K_Bank(4, reg[3]); + } + } + + //void Mapper010::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + p[4] = latch_a; + p[5] = latch_b; + } + + //void Mapper010::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + latch_a = p[4]; + latch_b = p[5]; + } + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper011.cs b/Core/VirtualNes.Core/Mapper/Mapper011.cs new file mode 100644 index 0000000..2ee819a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper011.cs @@ -0,0 +1,39 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper011 Color Dreams // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper011 : Mapper + { + + public Mapper011(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + // SetVROM_8K_Bank( 1 ); + } + SetVRAM_Mirror(VRAM_VMIRROR); + } + + //void Mapper011::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT("WR A:%04X D:%02X\n", addr, data); + SetPROM_32K_Bank(data); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(data >> 4); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper012.cs b/Core/VirtualNes.Core/Mapper/Mapper012.cs new file mode 100644 index 0000000..d9934e5 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper012.cs @@ -0,0 +1,336 @@ +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper012 : Mapper + { + uint vb0, vb1; + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + BYTE irq_preset; + BYTE irq_preset_vbl; + + public Mapper012(NES parent) : base(parent) { } + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + vb0 = 0; + vb1 = 0; + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0xFF; + irq_request = 0; + irq_preset = 0; + irq_preset_vbl = 0; + } + + //void Mapper012::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr > 0x4100 && addr < 0x6000) + { + vb0 = (byte)((data & 0x01) << 8); + vb1 = (byte)((data & 0x10) << 4); + SetBank_PPU(); + } + else + { + base.WriteLow(addr, data); + } + } + + //BYTE Mapper012::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + return 0x01; + } + + //void Mapper012::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_latch = data; + break; + case 0xC001: + reg[5] = data; + if (nes.GetScanline() < 240) + { + irq_counter |= 0x80; + irq_preset = 0xFF; + } + else + { + irq_counter |= 0x80; + irq_preset_vbl = 0xFF; + irq_preset = 0; + } + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + break; + } + } + + //void Mapper012::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_preset_vbl != 0) + { + irq_counter = irq_latch; + irq_preset_vbl = 0; + } + if (irq_preset != 0) + { + irq_counter = irq_latch; + irq_preset = 0; + } + else if (irq_counter > 0) + { + irq_counter--; + } + + if (irq_counter == 0) + { + // Some game set irq_latch to zero to disable irq. So check it here. + if (irq_enable != 0 && irq_latch != 0) + { + irq_request = 0xFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + irq_preset = 0xFF; + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank( + (int)(vb0 + chr4), + (int)(vb0 + chr5), + (int)(vb0 + chr6), + (int)(vb0 + chr7), + (int)(vb1 + chr01), + (int)(vb1 + chr01 + 1), + (int)(vb1 + chr23), + (int)(vb1 + chr23 + 1) + ); + } + else + { + SetVROM_8K_Bank( + (int)(vb0 + chr01), + (int)(vb0 + chr01 + 1), + (int)(vb0 + chr23), + (int)(vb0 + chr23 + 1), + (int)(vb1 + chr4), + (int)(vb1 + chr5), + (int)(vb1 + chr6), + (int)(vb1 + chr7)) + ; + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(0, chr4 & 0x07); + SetCRAM_1K_Bank(1, chr5 & 0x07); + SetCRAM_1K_Bank(2, chr6 & 0x07); + SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(4, chr4 & 0x07); + SetCRAM_1K_Bank(5, chr5 & 0x07); + SetCRAM_1K_Bank(6, chr6 & 0x07); + SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + + //void Mapper012::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = (BYTE)irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + p[20] = irq_preset; + p[21] = irq_preset_vbl; + //*((DWORD*)&p[22]) = vb0; + BitConverter.GetBytes(vb0).CopyTo(p, 22); + //*((DWORD*)&p[26]) = vb1; + BitConverter.GetBytes(vb1).CopyTo(p, 26); + } + + //void Mapper012::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = (byte)p[17]; + irq_latch = p[18]; + irq_request = p[19]; + irq_preset = p[20]; + irq_preset_vbl = p[21]; + //vb0 = *((DWORD*)&p[22]); + vb0 = BitConverter.ToUInt32(p, 22); + //vb1 = *((DWORD*)&p[26]); + vb1 = BitConverter.ToUInt32(p, 26); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper013.cs b/Core/VirtualNes.Core/Mapper/Mapper013.cs new file mode 100644 index 0000000..e16779c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper013.cs @@ -0,0 +1,30 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper013 CPROM // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper013 : Mapper + { + + public Mapper013(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + SetCRAM_4K_Bank(0, 0); + SetCRAM_4K_Bank(4, 0); + } + + //void Mapper013::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank((data & 0x30) >> 4); + SetCRAM_4K_Bank(4, data & 0x03); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper015.cs b/Core/VirtualNes.Core/Mapper/Mapper015.cs new file mode 100644 index 0000000..3fca36c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper015.cs @@ -0,0 +1,90 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper015 100-in-1 chip // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper015 : Mapper + { + + public Mapper015(NES parent) : base(parent) { } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + } + + //void Mapper015::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + if ((data & 0x80) != 0) + { + SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 3); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 2); + } + else + { + SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 2); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 3); + } + if ((data & 0x40) != 0) + SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0x8001: + if ((data & 0x80) != 0) + { + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0); + } + else + { + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1); + } + break; + case 0x8002: + if ((data & 0x80) != 0) + { + SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1); + } + else + { + SetPROM_8K_Bank(4, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(5, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0); + } + break; + case 0x8003: + if ((data & 0x80) != 0) + { + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 1); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 0); + } + else + { + SetPROM_8K_Bank(6, (data & 0x3F) * 2 + 0); + SetPROM_8K_Bank(7, (data & 0x3F) * 2 + 1); + } + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper016.cs b/Core/VirtualNes.Core/Mapper/Mapper016.cs new file mode 100644 index 0000000..c12657b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper016.cs @@ -0,0 +1,438 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper016 Bandai Standard // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper016 : Mapper + { + BYTE patch; // For Famicom Jump 2 + BYTE eeprom_type; // EEPROM type + + BYTE[] reg = new byte[3]; + + BYTE irq_enable; + INT irq_counter; + INT irq_latch; + BYTE irq_type; + + X24C01 x24c01; + X24C02 x24c02; + public Mapper016(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + + reg[0] = reg[1] = reg[2] = 0; + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + + irq_type = 0; + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + + eeprom_type = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x3f15d20d // Famicom Jump 2(J) + || crc == 0xf76aa523) + { // Famicom Jump 2(J)(alt) + patch = 1; + eeprom_type = 0xFF; + + WRAM[0x0BBC] = 0xFF; // SRAM対策 + } + + if (crc == 0x1d6f27f7) + { // Dragon Ball Z 2(Korean Hack) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + eeprom_type = 1; + } + if (crc == 0x6f7247c8) + { // Dragon Ball Z 3(Korean Hack) + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + eeprom_type = 1; + } + + if (crc == 0x7fb799fd) + { // Dragon Ball 2 - Dai Maou Fukkatsu(J) + } + if (crc == 0x6c6c2feb // Dragon Ball 3 - Gokuu Den(J) + || crc == 0x8edeb257) + { // Dragon Ball 3 - Gokuu Den(J)(Alt) + } + if (crc == 0x31cd9903) + { // Dragon Ball Z - Kyoushuu! Saiya Jin(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xe49fc53e // Dragon Ball Z 2 - Gekishin Freeza!!(J) + || crc == 0x1582fee0) + { // Dragon Ball Z 2 - Gekishin Freeza!!(J) [alt] + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + eeprom_type = 1; + } + if (crc == 0x09499f4d) + { // Dragon Ball Z 3 - Ressen Jinzou Ningen(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + eeprom_type = 1; + } + if (crc == 0x2e991109) + { // Dragon Ball Z Gaiden - Saiya Jin Zetsumetsu Keikaku (J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + eeprom_type = 1; + } + if (crc == 0x146fb9c3) + { // SD Gundam Gaiden - Knight Gundam Monogatari(J) + } + + if (crc == 0x73ac76db // SD Gundam Gaiden - Knight Gundam Monogatari 2 - Hikari no Kishi(J) + || crc == 0x81a15eb8) + { // SD Gundam Gaiden - Knight Gundam Monogatari 3 - Densetsu no Kishi Dan(J) + eeprom_type = 1; + } + if (crc == 0x170250de) + { // Rokudenashi Blues(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + eeprom_type = 1; + } + + // DATACH系 + if (crc == 0x0be0a328 // Datach - SD Gundam - Gundam Wars(J) + || crc == 0x19e81461 // Datach - Dragon Ball Z - Gekitou Tenkaichi Budou Kai(J) + || crc == 0x5b457641 // Datach - Ultraman Club - Supokon Fight!(J) + || crc == 0x894efdbc // Datach - Crayon Shin Chan - Ora to Poi Poi(J) + || crc == 0x983d8175 // Datach - Battle Rush - Build Up Robot Tournament(J) + || crc == 0xbe06853f) + { // Datach - J League Super Top Players(J) + eeprom_type = 2; + } + if (crc == 0xf51a7f46) + { // Datach - Yuu Yuu Hakusho - Bakutou Ankoku Bujutsu Kai(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + eeprom_type = 2; + } + + if (eeprom_type == 0) + { + nes.SetSAVERAM_SIZE(128); + x24c01.Reset(WRAM); + } + else + if (eeprom_type == 1) + { + nes.SetSAVERAM_SIZE(256); + x24c02.Reset(WRAM); + } + else + if (eeprom_type == 2) + { + nes.SetSAVERAM_SIZE(384); + x24c02.Reset(WRAM); + x24c01.Reset(new ArrayRef(WRAM, 256)); + } + } + + //BYTE Mapper016::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (patch != 0) + { + return base.ReadLow(addr); + } + else + { + if ((addr & 0x00FF) == 0x0000) + { + BYTE ret = 0; + if (eeprom_type == 0) + { + ret = x24c01.Read(); + } + else + if (eeprom_type == 1) + { + ret = x24c02.Read(); + } + else + if (eeprom_type == 2) + { + ret = (byte)(x24c02.Read() & x24c01.Read()); + } + return (byte)((ret != 0 ? 0x10 : 0) | (nes.GetBarcodeStatus())); + } + } + return 0x00; + } + + //void Mapper016::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (patch == 0) + { + WriteSubA(addr, data); + } + else + { + base.WriteLow(addr, data); + } + } + + public override void Write(ushort addr, byte data) + { + if (patch == 0) + { + WriteSubA(addr, data); + } + else + { + WriteSubB(addr, data); + } + } + + static BYTE eeprom_addinc; + + // Normal mapper #16 + void WriteSubA(ushort addr, BYTE data) + { + switch (addr & 0x000F) + { + case 0x0000: + case 0x0001: + case 0x0002: + case 0x0003: + case 0x0004: + case 0x0005: + case 0x0006: + case 0x0007: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank((byte)(addr & 0x0007), data); + } + if (eeprom_type == 2) + { + reg[0] = data; + x24c01.Write((byte)((data & 0x08) != 0 ? 0xFF : 0), (byte)((reg[1] & 0x40) != 0 ? 0xFF : 0)); + } + break; + + case 0x0008: + SetPROM_16K_Bank(4, data); + break; + + case 0x0009: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x000A: + irq_enable = (byte)(data & 0x01); + irq_counter = irq_latch; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x000B: + irq_latch = (irq_latch & 0xFF00) | data; + irq_counter = (irq_counter & 0xFF00) | data; + break; + case 0x000C: + irq_latch = (data << 8) | (irq_latch & 0x00FF); + irq_counter = (data << 8) | (irq_counter & 0x00FF); + break; + + case 0x000D: + // EEPTYPE0(DragonBallZ) + if (eeprom_type == 0) + { + x24c01.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0)); + } + // EEPTYPE1(DragonBallZ2,Z3,Z Gaiden) + if (eeprom_type == 1) + { + x24c02.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0)); + } + // EEPTYPE2(DATACH) + if (eeprom_type == 2) + { + reg[1] = data; + x24c02.Write((byte)((data & 0x20) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0)); + x24c01.Write((byte)((reg[0] & 0x08) != 0 ? 0xFF : 0), (byte)((data & 0x40) != 0 ? 0xFF : 0)); + } + break; + } + } + + // Famicom Jump 2 + void WriteSubB(ushort addr, BYTE data) + { + switch (addr) + { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: + reg[0] = (byte)(data & 0x01); + SetPROM_8K_Bank(4, reg[0] * 0x20 + reg[2] * 2 + 0); + SetPROM_8K_Bank(5, reg[0] * 0x20 + reg[2] * 2 + 1); + break; + case 0x8004: + case 0x8005: + case 0x8006: + case 0x8007: + reg[1] = (byte)(data & 0x01); + SetPROM_8K_Bank(6, reg[1] * 0x20 + 0x1E); + SetPROM_8K_Bank(7, reg[1] * 0x20 + 0x1F); + break; + case 0x8008: + reg[2] = data; + SetPROM_8K_Bank(4, reg[0] * 0x20 + reg[2] * 2 + 0); + SetPROM_8K_Bank(5, reg[0] * 0x20 + reg[2] * 2 + 1); + SetPROM_8K_Bank(6, reg[1] * 0x20 + 0x1E); + SetPROM_8K_Bank(7, reg[1] * 0x20 + 0x1F); + break; + + case 0x8009: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x800A: + irq_enable = (byte)(data & 0x01); + irq_counter = irq_latch; + + // if( !irq_enable ) { + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + // } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x800B: + irq_latch = (irq_latch & 0xFF00) | data; + break; + case 0x800C: + irq_latch = (data << 8) | (irq_latch & 0x00FF); + break; + + case 0x800D: + break; + } + } + + public override void HSync(int scanline) + { + if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC)) + { + if (irq_counter <= 113) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + // nes.cpu.IRQ(); + //// nes.cpu.IRQ_NotPending(); + // irq_enable = 0; + // irq_counter = 0; + irq_counter &= 0xFFFF; + } + else + { + irq_counter -= 113; + } + } + } + + public override void Clock(int cycles) + { + if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_CLOCK)) + { + if ((irq_counter -= cycles) <= 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + // nes.cpu.IRQ(); + //// nes.cpu.IRQ_NotPending(); + // irq_enable = 0; + // irq_counter = 0; + irq_counter &= 0xFFFF; + } + } + } + + public unsafe override void SaveState(byte[] buffer) + { + fixed (byte* p = buffer) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = irq_enable; + *(INT*)&p[4] = irq_counter; + *(INT*)&p[8] = irq_latch; + + if (eeprom_type == 0) + { + x24c01.Save(&p[16]); + } + else + if (eeprom_type == 1) + { + x24c02.Save(&p[16]); + } + else + if (eeprom_type == 2) + { + x24c02.Save(&p[16]); + x24c01.Save(&p[48]); + } + } + } + + public unsafe override void LoadState(byte[] buffer) + { + fixed (byte* p = buffer) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + irq_enable = p[3]; + irq_counter = *(INT*)&p[4]; + irq_latch = *(INT*)&p[8]; + if (eeprom_type == 0) + { + x24c01.Load(&p[16]); + } + else + if (eeprom_type == 1) + { + //x24c02.Load(&p[16]); + x24c02.Load(p + 16); + } + else + if (eeprom_type == 2) + { + //x24c02.Load(&p[16]); + //x24c01.Load(&p[48]); + x24c02.Load(p + 16); + x24c01.Load(p + 48); + } + } + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper017.cs b/Core/VirtualNes.Core/Mapper/Mapper017.cs new file mode 100644 index 0000000..81fce5e --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper017.cs @@ -0,0 +1,132 @@ +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper017 : Mapper + { + BYTE irq_enable; + INT irq_counter; + INT irq_latch; + public Mapper017(NES parent) : base(parent) + { + } + + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper017::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x42FE: + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + case 0x42FF: + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0x4501: + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x4502: + irq_latch = (irq_latch & 0xFF00) | data; + break; + case 0x4503: + irq_latch = (irq_latch & 0x00FF) | ((INT)data << 8); + irq_counter = irq_latch; + irq_enable = 0xFF; + break; + + case 0x4504: + case 0x4505: + case 0x4506: + case 0x4507: + SetPROM_8K_Bank((byte)(addr & 0x07), data); + break; + + case 0x4510: + case 0x4511: + case 0x4512: + case 0x4513: + case 0x4514: + case 0x4515: + case 0x4516: + case 0x4517: + SetVROM_1K_Bank((byte)(addr & 0x07), data); + break; + + default: + base.WriteLow(addr, data); + break; + } + } + + //void Mapper017::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if (irq_counter >= 0xFFFF - 113) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + // nes.cpu.IRQ(); + // irq_counter = 0; + // irq_enable = 0; + irq_counter &= 0xFFFF; + } + else + { + irq_counter += 113; + } + } + } + + //void Mapper017::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 1); + //*(INT*)&p[5] = irq_latch; + BitConverter.GetBytes(irq_latch).CopyTo(p, 5); + } + + //void Mapper017::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_counter = *(INT*)&p[1]; + irq_counter = BitConverter.ToInt32(p, 1); + //irq_latch = *(INT*)&p[5]; + irq_latch = BitConverter.ToInt32(p, 5); + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper018.cs b/Core/VirtualNes.Core/Mapper/Mapper018.cs new file mode 100644 index 0000000..0c43bb6 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper018.cs @@ -0,0 +1,283 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper018 Jaleco SS8806 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper018 : Mapper + { + BYTE[] reg = new byte[11]; + + BYTE irq_enable; + BYTE irq_mode; + INT irq_latch; + INT irq_counter; + public Mapper018(NES parent) : base(parent) + { + } + + + public override void Reset() + { + for (INT i = 0; i < 11; i++) + { + reg[i] = 0; + } + reg[2] = (byte)(PROM_8K_SIZE - 2); + reg[3] = (byte)(PROM_8K_SIZE - 1); + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + irq_enable = 0; + irq_mode = 0; + irq_counter = 0xFFFF; + irq_latch = 0xFFFF; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xefb1df9e) + { // The Lord of King(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + if (crc == 0x3746f951) + { // Pizza Pop!(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + + // nes.SetRenderMethod( NES::PRE_ALL_RENDER ); + // nes.SetRenderMethod( NES::POST_ALL_RENDER ); + } + + //void Mapper018::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetPROM_8K_Bank(4, reg[0]); + break; + case 0x8001: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetPROM_8K_Bank(4, reg[0]); + break; + case 0x8002: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetPROM_8K_Bank(5, reg[1]); + break; + case 0x8003: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetPROM_8K_Bank(5, reg[1]); + break; + case 0x9000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetPROM_8K_Bank(6, reg[2]); + break; + case 0x9001: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetPROM_8K_Bank(6, reg[2]); + break; + + case 0xA000: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[3]); + break; + case 0xA001: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[3]); + break; + case 0xA002: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[4]); + break; + case 0xA003: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[4]); + break; + + case 0xB000: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[5]); + break; + case 0xB001: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[5]); + break; + case 0xB002: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[6]); + break; + case 0xB003: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[6]); + break; + + case 0xC000: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[7]); + break; + case 0xC001: + reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[7]); + break; + case 0xC002: + reg[8] = (byte)((reg[8] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[8]); + break; + case 0xC003: + reg[8] = (byte)((reg[8] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[8]); + break; + + case 0xD000: + reg[9] = (byte)((reg[9] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[9]); + break; + case 0xD001: + reg[9] = (byte)((reg[9] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[9]); + break; + case 0xD002: + reg[10] = (byte)((reg[10] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[10]); + break; + case 0xD003: + reg[10] = (byte)((reg[10] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[10]); + break; + + case 0xE000: + irq_latch = (irq_latch & 0xFFF0) | (data & 0x0F); + break; + case 0xE001: + irq_latch = (irq_latch & 0xFF0F) | ((data & 0x0F) << 4); + break; + case 0xE002: + irq_latch = (irq_latch & 0xF0FF) | ((data & 0x0F) << 8); + break; + case 0xE003: + irq_latch = (irq_latch & 0x0FFF) | ((data & 0x0F) << 12); + break; + + case 0xF000: + // if( data & 0x01 ) { + irq_counter = irq_latch; + // } else { + // irq_counter = 0; + // } + break; + case 0xF001: + irq_mode = (byte)((data >> 1) & 0x07); + irq_enable = ((byte)(data & 0x01)); + // if( !irq_enable ) { + nes.cpu.ClrIRQ(IRQ_MAPPER); + // } + break; + + case 0xF002: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + } + } + + //void Mapper018::Clock(INT cycles) + public override void Clock(int cycles) + { + bool bIRQ = false; + INT irq_counter_old = irq_counter; + + if (irq_enable != 0 && irq_counter != 0) + { + irq_counter -= cycles; + + switch (irq_mode) + { + case 0: + if (irq_counter <= 0) + { + bIRQ = true; + } + break; + case 1: + if ((irq_counter & 0xF000) != (irq_counter_old & 0xF000)) + { + bIRQ = true; + } + break; + case 2: + case 3: + if ((irq_counter & 0xFF00) != (irq_counter_old & 0xFF00)) + { + bIRQ = true; + } + break; + case 4: + case 5: + case 6: + case 7: + if ((irq_counter & 0xFFF0) != (irq_counter_old & 0xFFF0)) + { + bIRQ = true; + } + break; + } + + if (bIRQ) + { + //// irq_enable = 0; + // irq_counter = irq_latch; + irq_counter = 0; + irq_enable = 0; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper018::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 11; i++) + { + p[i] = reg[i]; + } + p[11] = irq_enable; + p[12] = irq_mode; + //*(INT*)&p[13] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 13); + //*(INT*)&p[17] = irq_latch; + BitConverter.GetBytes(irq_latch).CopyTo(p, 17); + } + + //void Mapper018::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 11; i++) + { + p[i] = reg[i]; + } + irq_enable = p[11]; + irq_mode = p[12]; + //irq_counter = *(INT*)&p[13]; + irq_counter = BitConverter.ToInt32(p, 13); + //irq_latch = *(INT*)&p[17]; + irq_latch = BitConverter.ToInt32(p, 17); + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper019.cs b/Core/VirtualNes.Core/Mapper/Mapper019.cs new file mode 100644 index 0000000..97122ae --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper019.cs @@ -0,0 +1,421 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper019 Namcot 106 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper019 : Mapper + { + BYTE patch; + BYTE exsound_enable; + + BYTE[] reg = new byte[3]; + BYTE[] exram = new byte[128]; + + BYTE irq_enable; + ushort irq_counter; + public Mapper019(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + + reg[0] = reg[1] = reg[2] = 0; + + MemoryUtility.ZEROMEMORY(exram, exram.Length); + + irq_enable = 0; + irq_counter = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE >= 8) + { + SetVROM_8K_Bank(VROM_8K_SIZE - 1); + } + + exsound_enable = 0xFF; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xb62a7b71) + { // Family Circuit '91(J) + patch = 1; + } + + if (crc == 0x02738c68) + { // Wagan Land 2(J) + patch = 3; + } + if (crc == 0x14942c06) + { // Wagan Land 3(J) + patch = 2; + } + + if (crc == 0x968dcf09) + { // Final Lap(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + if (crc == 0x3deac303) + { // Rolling Thunder(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + if (crc == 0xb1b9e187) + { // For Kaijuu Monogatari(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + if (crc == 0x6901346e) + { // For Sangokushi 2 - Haou no Tairiku(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + // if( crc == 0xdd454208 ) { // Hydlide 3(J) + // nes.SetRenderMethod( NES::PRE_ALL_RENDER ); + // } + + if (crc == 0xaf15338f // For Mindseeker(J) + || crc == 0xb1b9e187 // For Kaijuu Monogatari(J) + || crc == 0x96533999 // Dokuganryuu Masamune(J) + // || crc == 0x2b825ce1 // Namco Classic(J) + // || crc == 0x9a2b0641 // Namco Classic 2(J) + || crc == 0x3296ff7a // Battle Fleet(J) + || crc == 0xdd454208) + { // Hydlide 3(J) + exsound_enable = 0; + } + + if (crc == 0x429fd177) + { // Famista '90(J) + exsound_enable = 0; + } + + if (exsound_enable != 0) + { + nes.apu.SelectExSound(0x10); + } + } + + //BYTE Mapper019::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + BYTE data = 0; + + switch (addr & 0xF800) + { + case 0x4800: + if (addr == 0x4800) + { + if (exsound_enable != 0) + { + nes.apu.ExRead(addr); + data = exram[reg[2] & 0x7F]; + } + else + { + data = WRAM[reg[2] & 0x7F]; + } + if ((reg[2] & 0x80) != 0) + reg[2] = (byte)((reg[2] + 1) | 0x80); + return data; + } + break; + case 0x5000: + return (byte)((BYTE)irq_counter & 0x00FF); + case 0x5800: + return (BYTE)((irq_counter >> 8) & 0x7F); + case 0x6000: + case 0x6800: + case 0x7000: + case 0x7800: + return base.ReadLow(addr); + } + + return (BYTE)(addr >> 8); + } + + //void Mapper019::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr & 0xF800) + { + case 0x4800: + if (addr == 0x4800) + { + if (exsound_enable != 0) + { + nes.apu.ExWrite(addr, data); + exram[reg[2] & 0x7F] = data; + } + else + { + WRAM[reg[2] & 0x7F] = data; + } + if ((reg[2] & 0x80) != 0) + reg[2] = (byte)((reg[2] + 1) | 0x80); + } + break; + case 0x5000: + irq_counter = (byte)((irq_counter & 0xFF00) | (ushort)data); + // if( irq_enable ) { + // irq_counter++; + // } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x5800: + irq_counter = (byte)((irq_counter & 0x00FF) | ((ushort)(data & 0x7F) << 8)); + irq_enable = (byte)(data & 0x80); + // if( irq_enable ) { + // irq_counter++; + // } + // if( !irq_enable ) { + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + // } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x6000: + case 0x6800: + case 0x7000: + case 0x7800: + base.WriteLow(addr, data); + break; + } + } + + //void Mapper019::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //if( addr >= 0xC000 ) { + //DEBUGOUT( "W %04X %02X L:%3d\n", addr, data, nes.GetScanline() ); + //} + switch (addr & 0xF800) + { + case 0x8000: + if ((data < 0xE0) || (reg[0] != 0)) + { + SetVROM_1K_Bank(0, data); + } + else + { + SetCRAM_1K_Bank(0, data & 0x1F); + } + break; + case 0x8800: + if ((data < 0xE0) || (reg[0] != 0)) + { + SetVROM_1K_Bank(1, data); + } + else + { + SetCRAM_1K_Bank(1, data & 0x1F); + } + break; + case 0x9000: + if ((data < 0xE0) || (reg[0] != 0)) + { + SetVROM_1K_Bank(2, data); + } + else + { + SetCRAM_1K_Bank(2, data & 0x1F); + } + break; + case 0x9800: + if ((data < 0xE0) || (reg[0] != 0)) + { + SetVROM_1K_Bank(3, data); + } + else + { + SetCRAM_1K_Bank(3, data & 0x1F); + } + break; + case 0xA000: + if ((data < 0xE0) || (reg[1] != 0)) + { + SetVROM_1K_Bank(4, data); + } + else + { + SetCRAM_1K_Bank(4, data & 0x1F); + } + break; + case 0xA800: + if ((data < 0xE0) || (reg[1] != 0)) + { + SetVROM_1K_Bank(5, data); + } + else + { + SetCRAM_1K_Bank(5, data & 0x1F); + } + break; + case 0xB000: + if ((data < 0xE0) || (reg[1] != 0)) + { + SetVROM_1K_Bank(6, data); + } + else + { + SetCRAM_1K_Bank(6, data & 0x1F); + } + break; + case 0xB800: + if ((data < 0xE0) || (reg[1] != 0)) + { + SetVROM_1K_Bank(7, data); + } + else + { + SetCRAM_1K_Bank(7, data & 0x1F); + } + break; + case 0xC000: + if (patch == 0) + { + if (data <= 0xDF) + { + SetVROM_1K_Bank(8, data); + } + else + { + SetVRAM_1K_Bank(8, data & 0x01); + } + } + break; + case 0xC800: + if (patch == 0) + { + if (data <= 0xDF) + { + SetVROM_1K_Bank(9, data); + } + else + { + SetVRAM_1K_Bank(9, data & 0x01); + } + } + break; + case 0xD000: + if (patch == 0) + { + if (data <= 0xDF) + { + SetVROM_1K_Bank(10, data); + } + else + { + SetVRAM_1K_Bank(10, data & 0x01); + } + } + break; + case 0xD800: + if (patch == 0) + { + if (data <= 0xDF) + { + SetVROM_1K_Bank(11, data); + } + else + { + SetVRAM_1K_Bank(11, data & 0x01); + } + } + break; + case 0xE000: + SetPROM_8K_Bank(4, data & 0x3F); + if (patch == 2) + { + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + if (patch == 3) + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xE800: + reg[0] = (byte)(data & 0x40); + reg[1] = (byte)(data & 0x80); + SetPROM_8K_Bank(5, data & 0x3F); + break; + case 0xF000: + SetPROM_8K_Bank(6, data & 0x3F); + break; + case 0xF800: + if (addr == 0xF800) + { + if (exsound_enable != 0) + { + nes.apu.ExWrite(addr, data); + } + reg[2] = data; + } + break; + } + } + + //void Mapper019::Clock(INT cycles) + public override void Clock(int cycles) + { + if (irq_enable != 0) + { + irq_counter = (ushort)(irq_counter + cycles); + if (irq_counter >= 0x7FFF) + { + // irq_counter = 0x7FFF; + // nes.cpu.IRQ_NotPending(); + + irq_enable = 0; + // irq_counter &= 0x7FFF; + irq_counter = 0x7FFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper019::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = irq_enable; + //*(WORD*)&p[4] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 4); + + //::memcpy(&p[8], exram, sizeof(exram)); + Array.Copy(exram, p, exram.Length); + + } + + //void Mapper019::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + irq_enable = p[3]; + //irq_counter = *(WORD*)&p[4]; + irq_counter = BitConverter.ToUInt16(p, 4); + //::memcpy(exram, &p[8], sizeof(exram)); + Array.Copy(p, exram, exram.Length); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper021.cs b/Core/VirtualNes.Core/Mapper/Mapper021.cs new file mode 100644 index 0000000..97a690f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper021.cs @@ -0,0 +1,258 @@ +////////////////// +// Mapper021 Konami VRC4 (Address mask $F006 or $F0C0) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper021 : Mapper + { + BYTE[] reg = new byte[9]; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper021(NES parent) : base(parent) + { + } + + + public override void Reset() + { + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + reg[8] = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper021::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF0CF) + { + case 0x8000: + if ((reg[8] & 0x02) != 0) + { + SetPROM_8K_Bank(6, data); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + + case 0x9000: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x9002: + case 0x9080: + reg[8] = data; + break; + + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB002: + case 0xB040: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + + case 0xB001: + case 0xB004: + case 0xB080: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB003: + case 0xB006: + case 0xB0C0: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC002: + case 0xC040: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + + case 0xC001: + case 0xC004: + case 0xC080: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC003: + case 0xC006: + case 0xC0C0: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD002: + case 0xD040: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + + case 0xD001: + case 0xD004: + case 0xD080: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD003: + case 0xD006: + case 0xD0C0: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE002: + case 0xE040: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + + case 0xE001: + case 0xE004: + case 0xE080: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE003: + case 0xE006: + case 0xE0C0: + reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F)); + break; + case 0xF002: + case 0xF040: + irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4)); + break; + + case 0xF003: + case 0xF0C0: + case 0xF006: + irq_enable = (byte)((irq_enable & 0x01) * 3); + irq_clock = 0; + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF004: + case 0xF080: + irq_enable = (byte)(data & 0x03); + if ((irq_enable & 0x02) != 0) + { + irq_counter = irq_latch; + irq_clock = 0; + } + + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + // case 0xF006: + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + // break; + } + } + + //void Mapper021::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + if ((irq_clock -= cycles) < 0) + { + irq_clock += 0x72; + if (irq_counter == 0xFF) + { + irq_counter = irq_latch; + // irq_enable = (irq_enable & 0x01) * 3; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + } + } + + //void Mapper021::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + p[i] = reg[i]; + } + p[9] = irq_enable; + p[10] = irq_counter; + p[11] = irq_latch; + //*(INT*)&p[12] = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 12); + } + + //void Mapper021::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + reg[i] = p[i]; + } + irq_enable = p[9]; + irq_counter = p[10]; + irq_latch = p[11]; + //irq_clock = *(INT*)&p[12]; + irq_clock = BitConverter.ToInt32(p, 12); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper022.cs b/Core/VirtualNes.Core/Mapper/Mapper022.cs new file mode 100644 index 0000000..eeb4439 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper022.cs @@ -0,0 +1,78 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper022 Konami VRC2 type A // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper022 : Mapper + { + public Mapper022(NES parent) : base(parent) + { + } + + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper022::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + + case 0x9000: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + + case 0xB000: + SetVROM_1K_Bank(0, data >> 1); + break; + + case 0xB001: + SetVROM_1K_Bank(1, data >> 1); + break; + + case 0xC000: + SetVROM_1K_Bank(2, data >> 1); + break; + + case 0xC001: + SetVROM_1K_Bank(3, data >> 1); + break; + + case 0xD000: + SetVROM_1K_Bank(4, data >> 1); + break; + + case 0xD001: + SetVROM_1K_Bank(5, data >> 1); + break; + + case 0xE000: + SetVROM_1K_Bank(6, data >> 1); + break; + + case 0xE001: + SetVROM_1K_Bank(7, data >> 1); + break; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper023.cs b/Core/VirtualNes.Core/Mapper/Mapper023.cs new file mode 100644 index 0000000..26d768b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper023.cs @@ -0,0 +1,274 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper023 Konami VRC2 type B // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper023 : Mapper + { + + ushort addrmask; + + BYTE[] reg = new byte[9]; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper023(NES parent) : base(parent) + { + } + + + public override void Reset() + { + addrmask = 0xFFFF; + + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + reg[8] = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + reg[9] = 1; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + // nes.SetRenderMethod( NES::POST_RENDER ); + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x93794634 // Akumajou Special Boku Dracula Kun(J) + || crc == 0xc7829dae // Akumajou Special Boku Dracula Kun(T-Eng) + || crc == 0xf82dc02f) + { // Akumajou Special Boku Dracula Kun(T-Eng v1.02) + addrmask = 0xF00C; + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + if (crc == 0xdd53c4ae) + { // Tiny Toon Adventures(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + } + + //void Mapper023::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + switch (addr & addrmask) + { + case 0x8000: + case 0x8004: + case 0x8008: + case 0x800C: + if (reg[8] != 0) + { + SetPROM_8K_Bank(6, data); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + + case 0x9000: + if (data != 0xFF) + { + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + } + break; + + case 0x9008: + reg[8] = (byte)(data & 0x02); + break; + + case 0xA000: + case 0xA004: + case 0xA008: + case 0xA00C: + SetPROM_8K_Bank(5, data); + break; + + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB001: + case 0xB004: + reg[0] = ((byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4))); + SetVROM_1K_Bank(0, reg[0]); + break; + + case 0xB002: + case 0xB008: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xB003: + case 0xB00C: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + + case 0xC001: + case 0xC004: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + + case 0xC002: + case 0xC008: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xC003: + case 0xC00C: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + + case 0xD001: + case 0xD004: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + + case 0xD002: + case 0xD008: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xD003: + case 0xD00C: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + + case 0xE001: + case 0xE004: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + + case 0xE002: + case 0xE008: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xE003: + case 0xE00C: + reg[7] = ((byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4))); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xF004: + irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF008: + irq_enable = (byte)(data & 0x03); + irq_counter = irq_latch; + irq_clock = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF00C: + irq_enable = (byte)((irq_enable & 0x01) * 3); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper023::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + irq_clock += cycles * 3; + while (irq_clock >= 341) + { + irq_clock -= 341; + irq_counter++; + if (irq_counter == 0) + { + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + + //void Mapper023::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + p[i] = reg[i]; + } + p[9] = irq_enable; + p[10] = irq_counter; + p[11] = irq_latch; + //*(INT*)&p[12] = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 12); + } + + //void Mapper023::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + reg[i] = p[i]; + } + irq_enable = p[9]; + irq_counter = p[10]; + irq_latch = p[11]; + //irq_clock = *(INT*)&p[12]; + irq_clock = BitConverter.ToInt32(p, 12); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper024.cs b/Core/VirtualNes.Core/Mapper/Mapper024.cs new file mode 100644 index 0000000..dc78cb4 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper024.cs @@ -0,0 +1,177 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper024 Konami VRC6 (Normal) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper024 : Mapper + { + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper024(NES parent) : base(parent) + { + } + + + public override void Reset() + { + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + // nes.SetRenderMethod( NES::PRE_RENDER ); + + nes.apu.SelectExSound(1); + } + + //void Mapper024::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF003) + { + case 0x8000: + SetPROM_16K_Bank(4, data); + break; + + case 0x9000: + case 0x9001: + case 0x9002: + case 0xA000: + case 0xA001: + case 0xA002: + case 0xB000: + case 0xB001: + case 0xB002: + nes.apu.ExWrite(addr, data); + break; + + case 0xB003: + data = (byte)(data & 0x0C); + if (data == 0x00) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 0x04) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 0x08) SetVRAM_Mirror(VRAM_MIRROR4L); + else if (data == 0x0C) SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0xC000: + SetPROM_8K_Bank(6, data); + break; + + case 0xD000: + SetVROM_1K_Bank(0, data); + break; + + case 0xD001: + SetVROM_1K_Bank(1, data); + break; + + case 0xD002: + SetVROM_1K_Bank(2, data); + break; + + case 0xD003: + SetVROM_1K_Bank(3, data); + break; + + case 0xE000: + SetVROM_1K_Bank(4, data); + break; + + case 0xE001: + SetVROM_1K_Bank(5, data); + break; + + case 0xE002: + SetVROM_1K_Bank(6, data); + break; + + case 0xE003: + SetVROM_1K_Bank(7, data); + break; + + case 0xF000: + irq_latch = data; + break; + case 0xF001: + irq_enable = (byte)(data & 0x03); + if ((irq_enable & 0x02) != 0) + { + irq_counter = irq_latch; + irq_clock = 0; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xF002: + irq_enable = (byte)((irq_enable & 0x01) * 3); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper024::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + if ((irq_clock += cycles) >= 0x72) + { + irq_clock -= 0x72; + if (irq_counter == 0xFF) + { + irq_counter = irq_latch; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + } + } + + //void Mapper024::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + p[2] = irq_latch; + //*(INT*)&p[3] = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 3); + } + + //void Mapper024::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + irq_latch = p[2]; + //irq_clock = *(INT*)&p[3]; + irq_clock = BitConverter.ToInt32(p, 3); + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper025.cs b/Core/VirtualNes.Core/Mapper/Mapper025.cs new file mode 100644 index 0000000..65f82b2 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper025.cs @@ -0,0 +1,281 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper025 Konami VRC4 (Normal) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper025 : Mapper + { + BYTE[] reg = new byte[11]; + BYTE irq_enable; + BYTE irq_latch; + BYTE irq_occur; + BYTE irq_counter; + INT irq_clock; + public Mapper025(NES parent) : base(parent) + { + } + + + public override void Reset() + { + for (INT i = 0; i < 11; i++) + { + reg[i] = 0; + } + reg[9] = (byte)(PROM_8K_SIZE - 2); + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_occur = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0xc71d4ce7) + { // Gradius II(J) + // nes.SetRenderMethod( NES::POST_RENDER ); + } + if (crc == 0xa2e68da8) + { // For Racer Mini Yonku - Japan Cup(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xea74c587) + { // For Teenage Mutant Ninja Turtles(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x5f82cb7d) + { // For Teenage Mutant Ninja Turtles 2(J) + } + if (crc == 0x0bbd85ff) + { // For Bio Miracle Bokutte Upa(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + } + + //void Mapper025::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //if( addr >= 0xF000 ) + //DEBUGOUT( "M25 WR $%04X=$%02X L=%3d\n", addr, data, nes.GetScanline() ); + + switch (addr & 0xF000) + { + case 0x8000: + if ((reg[10] & 0x02) != 0) + { + reg[9] = data; + SetPROM_8K_Bank(6, data); + } + else + { + reg[8] = data; + SetPROM_8K_Bank(4, data); + } + break; + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + } + + switch (addr & 0xF00F) + { + case 0x9000: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x9001: + case 0x9004: + if ((reg[10] & 0x02) != (data & 0x02)) + { + BYTE swap = reg[8]; + reg[8] = reg[9]; + reg[9] = swap; + + SetPROM_8K_Bank(4, reg[8]); + SetPROM_8K_Bank(6, reg[9]); + } + reg[10] = data; + break; + + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB002: + case 0xB008: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + + case 0xB001: + case 0xB004: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB003: + case 0xB00C: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC002: + case 0xC008: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + + case 0xC001: + case 0xC004: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC003: + case 0xC00C: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD002: + case 0xD008: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + + case 0xD001: + case 0xD004: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD003: + case 0xD00C: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE002: + case 0xE008: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + + case 0xE001: + case 0xE004: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE003: + case 0xE00C: + reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF002: + case 0xF008: + irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF001: + case 0xF004: + irq_enable = (byte)(data & 0x03); + // irq_counter = 0x100 - irq_latch; + irq_counter = irq_latch; + irq_clock = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF003: + case 0xF00C: + irq_enable = (byte)((irq_enable & 0x01) * 3); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper025::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + irq_clock += cycles * 3; + while (irq_clock >= 341) + { + irq_clock -= 341; + irq_counter++; + if (irq_counter == 0) + { + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + + //void Mapper025::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 11; i++) + { + p[i] = reg[i]; + } + p[11] = irq_enable; + p[12] = irq_occur; + p[13] = irq_latch; + p[14] = irq_counter; + //*((INT*)&p[15]) = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 15); + } + + //void Mapper025::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 11; i++) + { + reg[i] = p[i]; + } + irq_enable = p[11]; + irq_occur = p[12]; + irq_latch = p[13]; + irq_counter = p[14]; + //irq_clock = *((INT*)&p[15]); + irq_clock = BitConverter.ToInt32(p, 15); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper026.cs b/Core/VirtualNes.Core/Mapper/Mapper026.cs new file mode 100644 index 0000000..6761ed7 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper026.cs @@ -0,0 +1,188 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper026 Konami VRC6 (PA0,PA1 reverse) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper026 : Mapper + { + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper026(NES parent) : base(parent) + { + } + + + public override void Reset() + { + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x30e64d03) + { // Esper Dream 2 - Aratanaru Tatakai(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + if (crc == 0x836cc1ab) + { // Mouryou Senki Madara(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + nes.apu.SelectExSound(1); + } + + //void Mapper026::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF003) + { + case 0x8000: + SetPROM_16K_Bank(4, data); + break; + + case 0x9000: + case 0x9001: + case 0x9002: + case 0x9003: + case 0xA000: + case 0xA001: + case 0xA002: + case 0xA003: + case 0xB000: + case 0xB001: + case 0xB002: + addr = (ushort)((addr & 0xfffc) | ((addr & 1) << 1) | ((addr & 2) >> 1)); + nes.apu.ExWrite(addr, data); + break; + + case 0xB003: + data = (byte)(data & 0x7F); + if (data == 0x08 || data == 0x2C) SetVRAM_Mirror(VRAM_MIRROR4H); + else if (data == 0x20) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 0x24) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 0x28) SetVRAM_Mirror(VRAM_MIRROR4L); + break; + + case 0xC000: + SetPROM_8K_Bank(6, data); + break; + + case 0xD000: + SetVROM_1K_Bank(0, data); + break; + + case 0xD001: + SetVROM_1K_Bank(2, data); + break; + + case 0xD002: + SetVROM_1K_Bank(1, data); + break; + + case 0xD003: + SetVROM_1K_Bank(3, data); + break; + + case 0xE000: + SetVROM_1K_Bank(4, data); + break; + + case 0xE001: + SetVROM_1K_Bank(6, data); + break; + + case 0xE002: + SetVROM_1K_Bank(5, data); + break; + + case 0xE003: + SetVROM_1K_Bank(7, data); + break; + + case 0xF000: + irq_latch = data; + break; + case 0xF001: + irq_enable = (byte)((irq_enable & 0x01) * 3); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xF002: + irq_enable = (byte)(data & 0x03); + if ((irq_enable & 0x02) != 0) + { + irq_counter = irq_latch; + irq_clock = 0; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper026::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + if ((irq_clock += cycles) >= 0x72) + { + irq_clock -= 0x72; + if (irq_counter >= 0xFF) + { + irq_counter = irq_latch; + // nes.cpu.IRQ_NotPending(); + //// nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + } + } + + //void Mapper026::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + p[2] = irq_latch; + //*(INT*)&p[3] = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 3); + } + + //void Mapper026::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + irq_latch = p[2]; + //irq_clock = *(INT*)&p[3]; + irq_clock = BitConverter.ToInt32(p, 3); + } + + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper027.cs b/Core/VirtualNes.Core/Mapper/Mapper027.cs new file mode 100644 index 0000000..a345fae --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper027.cs @@ -0,0 +1,231 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper027 Konami VRC4 (World Hero) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper027 : Mapper + { + ushort[] reg = new ushort[9]; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper027(NES parent) : base(parent) + { + } + + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = (byte)i; + } + reg[8] = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x47DCBCC4) + { // Gradius II(sample) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0x468F21FC) + { // Racer Mini 4 ku(sample) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + } + + //void Mapper027::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF0CF) + { + case 0x8000: + if ((reg[8] & 0x02) != 0) + { + SetPROM_8K_Bank(6, data); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + + case 0x9000: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x9002: + case 0x9080: + reg[8] = data; + break; + + case 0xB000: + reg[0] = (ushort)((reg[0] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB001: + reg[0] = (ushort)((reg[0] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + + case 0xB002: + reg[1] = (ushort)((reg[1] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB003: + reg[1] = (ushort)((reg[1] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (ushort)((reg[2] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC001: + reg[2] = (ushort)((reg[2] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + + case 0xC002: + reg[3] = (ushort)((reg[3] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC003: + reg[3] = (ushort)((reg[3] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (ushort)((reg[4] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD001: + reg[4] = (ushort)((reg[4] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + + case 0xD002: + reg[5] = (ushort)((reg[5] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD003: + reg[5] = (ushort)((reg[5] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (ushort)((reg[6] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE001: + reg[6] = (ushort)((reg[6] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + + case 0xE002: + reg[7] = (ushort)((reg[7] & 0xFF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE003: + reg[7] = (ushort)((reg[7] & 0x0F) | (data << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xF001: + irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4)); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF003: + irq_enable = (byte)((irq_enable & 0x01) * 3); + irq_clock = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF002: + irq_enable = (byte)(data & 0x03); + if ((irq_enable & 0x02) != 0) + { + irq_counter = irq_latch; + irq_clock = 0; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper027::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((irq_enable & 0x02) != 0) + { + if (irq_counter == 0xFF) + { + irq_counter = irq_latch; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + } + + //void Mapper027::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + p[i] = (byte)reg[i]; + } + p[9] = irq_enable; + p[10] = irq_counter; + p[11] = irq_latch; + //*(INT*)&p[12] = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 12); + } + + //void Mapper027::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + reg[i] = p[i]; + } + irq_enable = p[9]; + irq_counter = p[10]; + irq_latch = p[11]; + //irq_clock = *(INT*)&p[12]; + irq_clock = BitConverter.ToInt32(p, 12); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper032.cs b/Core/VirtualNes.Core/Mapper/Mapper032.cs new file mode 100644 index 0000000..660d1c3 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper032.cs @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper032 Irem G101 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper032 : Mapper + { + BYTE patch; + + BYTE reg; + public Mapper032(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + reg = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + uint crc = nes.rom.GetPROM_CRC(); + + // For Major League(J) + if (crc == 0xc0fed437) + { + patch = 1; + } + // For Ai Sensei no Oshiete - Watashi no Hoshi(J) + if (crc == 0xfd3fc292) + { + SetPROM_32K_Bank(30, 31, 30, 31); + } + } + + //void Mapper032::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x8000: + if ((reg & 0x02) != 0) + { + SetPROM_8K_Bank(6, data); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + + case 0x9000: + reg = data; + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + } + + switch (addr & 0xF007) + { + case 0xB000: + case 0xB001: + case 0xB002: + case 0xB003: + case 0xB004: + case 0xB005: + SetVROM_1K_Bank((byte)(addr & 0x0007), data); + break; + case 0xB006: + SetVROM_1K_Bank(6, data); + + if (patch != 0 && ((data & 0x40) != 0)) + { + SetVRAM_Mirror(0, 0, 0, 1); + } + break; + case 0xB007: + SetVROM_1K_Bank(7, data); + + if (patch != 0 && ((data & 0x40) != 0)) + { + SetVRAM_Mirror(0, 0, 0, 0); + } + break; + } + } + + //void Mapper032::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + } + + //void Mapper032::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper033.cs b/Core/VirtualNes.Core/Mapper/Mapper033.cs new file mode 100644 index 0000000..c3664dd --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper033.cs @@ -0,0 +1,260 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper033 Taito TC0190 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper033 : Mapper + { + BYTE[] reg = new byte[7]; + + BYTE patch; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper033(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + + reg[0] = 0; + reg[1] = 2; + reg[2] = 4; + reg[3] = 5; + reg[4] = 6; + reg[5] = 7; + reg[6] = 1; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetBank(); + } + + uint crc = nes.rom.GetPROM_CRC(); + // Check For Old #33 games.... (CRC code by NesToy) + if (crc == 0x5e9bc161 // Akira(J) + || crc == 0xecdbafa4 // Bakushou!! Jinsei Gekijou(J) + || crc == 0x59cd0c31 // Don Doko Don(J) + || crc == 0x837c1342 // Golf Ko Open(J) + || crc == 0x42d893e4 // Operation Wolf(J) + || crc == 0x1388aeb9 // Operation Wolf(U) + || crc == 0x07ee6d8f // Power Blazer(J) + || crc == 0x5193fb54 // Takeshi no Sengoku Fuuunji(J) + || crc == 0xa71c3452) + { // Insector X(J) + patch = 1; + } + + nes.SetRenderMethod(EnumRenderMethod.PRE_RENDER); + + if (crc == 0x202df297) + { // Captain Saver(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x63bb86b5) + { // The Jetsons(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + } + + //void Mapper033::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // LOG( "Mapper033 addr=%04X data=%02X", addr&0xFFFF, data&0xFF ); + + switch (addr) + { + case 0x8000: + if (patch != 0) + { + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + SetPROM_8K_Bank(4, data & 0x1F); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + case 0x8001: + if (patch != 0) + { + SetPROM_8K_Bank(5, data & 0x1F); + } + else + { + SetPROM_8K_Bank(5, data); + } + break; + + case 0x8002: + reg[0] = data; + SetBank(); + break; + case 0x8003: + reg[1] = data; + SetBank(); + break; + case 0xA000: + reg[2] = data; + SetBank(); + break; + case 0xA001: + reg[3] = data; + SetBank(); + break; + case 0xA002: + reg[4] = data; + SetBank(); + break; + case 0xA003: + reg[5] = data; + SetBank(); + break; + +#if FLASE//0 + case 0xC003: + case 0xE003: + reg[6] = data; + SetBank(); + break; + + case 0xC000: + irq_counter = data; +// nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + + case 0xC001: + case 0xC002: + case 0xE001: + case 0xE002: + irq_enable = data; +// nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; +#else + case 0xC000: + irq_latch = data; + irq_counter = irq_latch; + break; + case 0xC001: + irq_counter = irq_latch; + break; + case 0xC002: + irq_enable = 1; + break; + case 0xC003: + irq_enable = 0; + break; + + case 0xE001: + case 0xE002: + case 0xE003: + break; +#endif + + case 0xE000: + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + } + + //void Mapper033::HSync(INT scanline) + public override void HSync(int scanline) + { +#if FALSE//0 +// nes.cpu.ClrIRQ( IRQ_MAPPER ); + if( scanline >= 0 && scanline <= 239 ) { + if( nes.ppu.IsDispON() ) { + if( irq_enable ) { + if( irq_counter == 0xFF ) { + irq_enable = 0; + irq_counter = 0; +// nes.cpu.SetIRQ( IRQ_MAPPER ); + nes.cpu.SetIRQ( IRQ_TRIGGER ); + } else { + irq_counter++; + } + } + } + } +#else + if (scanline >= 0 && scanline <= 239 && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (++irq_counter == 0) + { + irq_enable = 0; + irq_counter = 0; + nes.cpu.SetIRQ(IRQ_TRIGGER); + } + } + } +#endif + } + + void SetBank() + { + SetVROM_2K_Bank(0, reg[0]); + SetVROM_2K_Bank(2, reg[1]); + + // if( reg[6] & 0x01 ) { + SetVROM_1K_Bank(4, reg[2]); + SetVROM_1K_Bank(5, reg[3]); + SetVROM_1K_Bank(6, reg[4]); + SetVROM_1K_Bank(7, reg[5]); + // } else { + // SetVROM_2K_Bank( 4, reg[0] ); + // SetVROM_2K_Bank( 6, reg[1] ); + // } + } + + //void Mapper033::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 7; i++) + { + p[i] = reg[i]; + } + + p[7] = irq_enable; + p[8] = irq_counter; + p[9] = irq_latch; + } + + //void Mapper033::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 7; i++) + { + reg[i] = p[i]; + } + + irq_enable = p[7]; + irq_counter = p[8]; + irq_latch = p[9]; + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper034.cs b/Core/VirtualNes.Core/Mapper/Mapper034.cs new file mode 100644 index 0000000..bedefe4 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper034.cs @@ -0,0 +1,51 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper034 Nina-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper034 : Mapper + { + public Mapper034(NES parent) : base(parent) + { + } + + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper034::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x7FFD: + SetPROM_32K_Bank(data); + break; + case 0x7FFE: + SetVROM_4K_Bank(0, data); + break; + case 0x7FFF: + SetVROM_4K_Bank(4, data); + break; + } + } + + //void Mapper034::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank(data); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper035.cs b/Core/VirtualNes.Core/Mapper/Mapper035.cs new file mode 100644 index 0000000..5ce4d20 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper035.cs @@ -0,0 +1,123 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper035 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper035 : Mapper + { + + BYTE[] reg = new byte[8]; + BYTE[] chr = new byte[8]; + ushort IRQCount, IRQa; + public Mapper035(NES parent) : base(parent) + { + } + + + public override void Reset() + { + for (int i = 0; i < 8; i++) + reg[i] = chr[i] = 0; + + IRQCount = IRQa = 0; + + //SetPROM_32K_Bank( 0, 1, PROM_8K_SIZE-2, PROM_8K_SIZE-1 ); + + Sync(); + //setprg8r(0x10,0x6000,0); + SetPROM_8K_Bank(7, PROM_8K_SIZE - 1); + } + + void Sync() + { + int i; + SetPROM_8K_Bank(4, reg[0]); + SetPROM_8K_Bank(5, reg[1]); + SetPROM_8K_Bank(6, reg[2]); + for (i = 0; i < 8; i++) + SetVROM_1K_Bank((byte)i, chr[i]); + SetVRAM_Mirror(reg[3] ^ 1); + } + + //void Mapper035::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000 && addr <= 0x7FFF) + { + XRAM[addr - 0x6000] = data; + } + else + { + base.WriteLow(addr, data); + } + } + //BYTE Mapper035::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (addr >= 0x6000 && addr <= 0x7FFF) + { + return XRAM[addr - 0x6000]; + } + else + { + return base.ReadLow(addr); + } + } + + //void Mapper035::Write(WORD A, BYTE V) + public override void Write(ushort A, byte V) + { + switch (A) + { + case 0x8000: reg[0] = V; break; + case 0x8001: reg[1] = V; break; + case 0x8002: reg[2] = V; break; + case 0x9000: chr[0] = V; break; + case 0x9001: chr[1] = V; break; + case 0x9002: chr[2] = V; break; + case 0x9003: chr[3] = V; break; + case 0x9004: chr[4] = V; break; + case 0x9005: chr[5] = V; break; + case 0x9006: chr[6] = V; break; + case 0x9007: chr[7] = V; break; + case 0xC002: + IRQa = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); break; + case 0xC005: IRQCount = V; break; + case 0xC003: IRQa = 1; break; + case 0xD001: reg[3] = V; break; + } + Sync(); + } + + //void Mapper035::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (IRQa != 0) + { + IRQCount--; + if (IRQCount == 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + IRQa = 0; + } + } + } + } + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper040.cs b/Core/VirtualNes.Core/Mapper/Mapper040.cs new file mode 100644 index 0000000..2e6db50 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper040.cs @@ -0,0 +1,93 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper040 SMB2J // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper040 : Mapper + { + BYTE irq_enable; + INT irq_line; + public Mapper040(NES parent) : base(parent) + { + } + + + public override void Reset() + { + irq_enable = 0; + irq_line = 0; + + SetPROM_8K_Bank(3, 6); + SetPROM_32K_Bank(4, 5, 0, 7); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper040::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE000) + { + case 0x8000: + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xA000: + irq_enable = 0xFF; + irq_line = 37; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC000: + break; + case 0xE000: + SetPROM_8K_Bank(6, data & 0x07); + break; + } + } + + //void Mapper040::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if (--irq_line <= 0) + { + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper040::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_line; + BitConverter.GetBytes(irq_line).CopyTo(p, 1); + } + + //void Mapper040::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_line = *(INT*)&p[1]; + irq_line = BitConverter.ToInt32(p, 1); + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper041.cs b/Core/VirtualNes.Core/Mapper/Mapper041.cs new file mode 100644 index 0000000..be2b5ea --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper041.cs @@ -0,0 +1,76 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper041 Caltron 6-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper041 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper041(NES parent) : base(parent) + { + } + + + public override void Reset() + { + reg[0] = reg[1] = 0; + + SetPROM_32K_Bank(0, 1, 2, 3); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper041::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000 && addr < 0x6800) + { + SetPROM_32K_Bank(addr & 0x07); + reg[0] = (byte)(addr & 0x04); + reg[1] &= 0x03; + reg[1] |= (byte)((addr >> 1) & 0x0C); + SetVROM_8K_Bank(reg[1]); + if ((addr & 0x20) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + } + + //void Mapper041::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (reg[0] != 0) + { + reg[1] &= 0x0C; + reg[1] |= (byte)(addr & 0x03); + SetVROM_8K_Bank(reg[1]); + } + } + + //void Mapper041::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper041::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper042.cs b/Core/VirtualNes.Core/Mapper/Mapper042.cs new file mode 100644 index 0000000..4796d79 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper042.cs @@ -0,0 +1,102 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper042 Mario Baby // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper042 : Mapper + { + BYTE irq_enable; + BYTE irq_counter; + public Mapper042(NES parent) : base(parent) + { + } + + + public override void Reset() + { + irq_enable = 0; + irq_counter = 0; + + SetPROM_8K_Bank(3, 0); + SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper042::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE003) + { + case 0xE000: + SetPROM_8K_Bank(3, data & 0x0F); + break; + + case 0xE001: + if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0xE002: + if ((data & 0x02) != 0) + { + irq_enable = 0xFF; + } + else + { + irq_enable = 0; + irq_counter = 0; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper042::HSync(INT scanline) + public override void HSync(int scanline) + { + nes.cpu.ClrIRQ(IRQ_MAPPER); + if (irq_enable != 0) + { + if (irq_counter < 215) + { + irq_counter++; + } + if (irq_counter == 215) + { + irq_enable = 0; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper042::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + } + + //void Mapper042::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper043.cs b/Core/VirtualNes.Core/Mapper/Mapper043.cs new file mode 100644 index 0000000..ae35ac0 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper043.cs @@ -0,0 +1,141 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper043 SMB2J // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper043 : Mapper + { + BYTE irq_enable; + INT irq_counter; + public Mapper043(NES parent) : base(parent) + { + } + + + public override void Reset() + { + irq_enable = 0xFF; + irq_counter = 0; + + SetPROM_8K_Bank(3, 2); + SetPROM_32K_Bank(1, 0, 4, 9); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //BYTE Mapper043::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (0x5000 <= addr && addr < 0x6000) + { + byte[] pPtr = nes.rom.GetPROM(); + return pPtr[0x2000 * 8 + 0x1000 + (addr - 0x5000)]; + } + return (BYTE)(addr >> 8); + } + + //void Mapper043::ExWrite(WORD addr, BYTE data) + public override void ExWrite(ushort addr, byte data) + { + if ((addr & 0xF0FF) == 0x4022) + { + switch (data & 0x07) + { + case 0x00: + case 0x02: + case 0x03: + case 0x04: + SetPROM_8K_Bank(6, 4); + break; + case 0x01: + SetPROM_8K_Bank(6, 3); + break; + case 0x05: + SetPROM_8K_Bank(6, 7); + break; + case 0x06: + SetPROM_8K_Bank(6, 5); + break; + case 0x07: + SetPROM_8K_Bank(6, 6); + break; + } + } + } + + //void Mapper043::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0xF0FF) == 0x4022) + { + ExWrite(addr, data); + } + } + + //void Mapper043::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr == 0x8122) + { + if ((data & 0x03) != 0) + { + irq_enable = 1; + } + else + { + irq_counter = 0; + irq_enable = 0; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + } + + //void Mapper043::HSync(INT scanline) + public override void HSync(int scanline) + { + nes.cpu.ClrIRQ(IRQ_MAPPER); + if (irq_enable != 0) + { + irq_counter += 341; + if (irq_counter >= 12288) + { + irq_counter = 0; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper043::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 1); + } + + //void Mapper043::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_counter = *(INT*)&p[1]; + irq_counter = BitConverter.ToInt32(p, 1); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper044.cs b/Core/VirtualNes.Core/Mapper/Mapper044.cs new file mode 100644 index 0000000..3f50c7f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper044.cs @@ -0,0 +1,289 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper044 Super HiK 7-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper044 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE patch; + BYTE bank; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper044(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + + if (nes.rom.GetPROM_CRC() == 0x7eef434c) + { + patch = 1; + } + + for (INT i = 0; i < 8; i++) + { + reg[i] = 0; + } + + bank = 0; + prg0 = 0; + prg1 = 1; + + // set VROM banks + if (VROM_1K_SIZE != 0) + { + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + } + else + { + chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0; + } + + SetBank_CPU(); + SetBank_PPU(); + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper044::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + if (patch != 0) + { + bank = (byte)((data & 0x06) >> 1); + } + else + { + bank = (byte)((data & 0x01) << 1); + } + SetBank_CPU(); + SetBank_PPU(); + } + } + + //void Mapper044::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + bank = (byte)(data & 0x07); + if (bank == 7) + { + bank = 6; + } + SetBank_CPU(); + SetBank_PPU(); + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + } + } + + //void Mapper044::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((--irq_counter) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_8K_Bank(4, ((bank == 6) ? 0x1e : 0x0e) | (bank << 4)); + SetPROM_8K_Bank(5, ((bank == 6) ? 0x1f & prg1 : 0x0f & prg1) | (bank << 4)); + SetPROM_8K_Bank(6, ((bank == 6) ? 0x1f & prg0 : 0x0f & prg0) | (bank << 4)); + SetPROM_8K_Bank(7, ((bank == 6) ? 0x1f : 0x0f) | (bank << 4)); + } + else + { + SetPROM_8K_Bank(4, ((bank == 6) ? 0x1f & prg0 : 0x0f & prg0) | (bank << 4)); + SetPROM_8K_Bank(5, ((bank == 6) ? 0x1f & prg1 : 0x0f & prg1) | (bank << 4)); + SetPROM_8K_Bank(6, ((bank == 6) ? 0x1e : 0x0e) | (bank << 4)); + SetPROM_8K_Bank(7, ((bank == 6) ? 0x1f : 0x0f) | (bank << 4)); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_1K_Bank(0, ((bank == 6) ? 0xff & chr4 : 0x7f & chr4) | (bank << 7)); + SetVROM_1K_Bank(1, ((bank == 6) ? 0xff & chr5 : 0x7f & chr5) | (bank << 7)); + SetVROM_1K_Bank(2, ((bank == 6) ? 0xff & chr6 : 0x7f & chr6) | (bank << 7)); + SetVROM_1K_Bank(3, ((bank == 6) ? 0xff & chr7 : 0x7f & chr7) | (bank << 7)); + SetVROM_1K_Bank(4, ((bank == 6) ? 0xff & chr01 : 0x7f & chr01) | (bank << 7)); + SetVROM_1K_Bank(5, ((bank == 6) ? 0xff & (chr01 + 1) : 0x7f & (chr01 + 1)) | (bank << 7)); + SetVROM_1K_Bank(6, ((bank == 6) ? 0xff & chr23 : 0x7f & chr23) | (bank << 7)); + SetVROM_1K_Bank(7, ((bank == 6) ? 0xff & (chr23 + 1) : 0x7f & (chr23 + 1)) | (bank << 7)); + } + else + { + SetVROM_1K_Bank(0, ((bank == 6) ? 0xff & chr01 : 0x7f & chr01) | (bank << 7)); + SetVROM_1K_Bank(1, ((bank == 6) ? 0xff & (chr01 + 1) : 0x7f & (chr01 + 1)) | (bank << 7)); + SetVROM_1K_Bank(2, ((bank == 6) ? 0xff & chr23 : 0x7f & chr23) | (bank << 7)); + SetVROM_1K_Bank(3, ((bank == 6) ? 0xff & (chr23 + 1) : 0x7f & (chr23 + 1)) | (bank << 7)); + SetVROM_1K_Bank(4, ((bank == 6) ? 0xff & chr4 : 0x7f & chr4) | (bank << 7)); + SetVROM_1K_Bank(5, ((bank == 6) ? 0xff & chr5 : 0x7f & chr5) | (bank << 7)); + SetVROM_1K_Bank(6, ((bank == 6) ? 0xff & chr6 : 0x7f & chr6) | (bank << 7)); + SetVROM_1K_Bank(7, ((bank == 6) ? 0xff & chr7 : 0x7f & chr7) | (bank << 7)); + } + } + } + + //void Mapper044::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = bank; + } + + //void Mapper044::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + bank = p[19]; + } + + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper045.cs b/Core/VirtualNes.Core/Mapper/Mapper045.cs new file mode 100644 index 0000000..c50ab6d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper045.cs @@ -0,0 +1,390 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper045 1000000-in-1 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper045 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE patch; + BYTE prg0, prg1, prg2, prg3; + BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7; + BYTE[] p = new byte[4]; + INT[] c = new int[8]; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_latched; + BYTE irq_reset; + public Mapper045(NES parent) : base(parent) + { + } + + + public override void Reset() + { + patch = 0; + for (INT i = 0; i < 8; i++) + { + reg[i] = 0; + } + + prg0 = 0; + prg1 = 1; + prg2 = (byte)(PROM_8K_SIZE - 2); + prg3 = (byte)(PROM_8K_SIZE - 1); + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x58bcacf6 // Kunio 8-in-1 (Pirate Cart) + || crc == 0x9103cfd6 // HIK 7-in-1 (Pirate Cart) + || crc == 0xc082e6d3) + { // Super 8-in-1 (Pirate Cart) + patch = 1; + prg2 = 62; + prg3 = 63; + } + if (crc == 0xe0dd259d) + { // Super 3-in-1 (Pirate Cart) + patch = 2; + } + SetPROM_32K_Bank(prg0, prg1, prg2, prg3); + p[0] = prg0; + p[1] = prg1; + p[2] = prg2; + p[3] = prg3; + + SetVROM_8K_Bank(0); + + // chr0 = c[0] = 0; + // chr1 = c[1] = 0 + // chr2 = c[2] = 0; + // chr3 = c[3] = 0; + // chr4 = c[4] = 0; + // chr5 = c[5] = 0; + // chr6 = c[6] = 0; + // chr7 = c[7] = 0; + + c[0] = chr0 = 0; + c[1] = chr1 = 1; + c[2] = chr2 = 2; + c[3] = chr3 = 3; + c[4] = chr4 = 4; + c[5] = chr5 = 5; + c[6] = chr6 = 6; + c[7] = chr7 = 7; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_latched = 0; + irq_reset = 0; + } + + //void Mapper045::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + // if( addr == 0x6000 ) { + // if( addr == 0x6000 && !(reg[3]&0x40) ) { + if ((reg[3] & 0x40) == 0) + { + reg[reg[5]] = data; + reg[5] = (byte)((reg[5] + 1) & 0x03); + + SetBank_CPU_4(prg0); + SetBank_CPU_5(prg1); + SetBank_CPU_6(prg2); + SetBank_CPU_7(prg3); + SetBank_PPU(); + } + } + + //void Mapper045::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + if ((data & 0x40) != (reg[6] & 0x40)) + { + BYTE swp; + swp = prg0; prg0 = prg2; prg2 = swp; + swp = p[0]; p[0] = p[2]; p[2] = swp; + SetBank_CPU_4(p[0]); + SetBank_CPU_5(p[1]); + } + if (VROM_1K_SIZE != 0) + { + if ((data & 0x80) != (reg[6] & 0x80)) + { + INT swp; + swp = chr4; chr4 = chr0; chr0 = (byte)swp; + swp = chr5; chr5 = chr1; chr1 = (byte)swp; + swp = chr6; chr6 = chr2; chr2 = (byte)swp; + swp = chr7; chr7 = chr3; chr3 = (byte)swp; + swp = c[4]; c[4] = c[0]; c[0] = swp; + swp = c[5]; c[5] = c[1]; c[1] = swp; + swp = c[6]; c[6] = c[2]; c[2] = swp; + swp = c[7]; c[7] = c[3]; c[3] = swp; + SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); + } + } + reg[6] = data; + break; + case 0x8001: + switch (reg[6] & 0x07) + { + case 0x00: + chr0 = (byte)((data & 0xFE) + 0); + chr1 = (byte)((data & 0xFE) + 1); + SetBank_PPU(); + break; + case 0x01: + chr2 = (byte)((data & 0xFE) + 0); + chr3 = (byte)((data & 0xFE) + 1); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + if ((reg[6] & 0x40) != 0) + { + prg2 = (byte)(data & 0x3F); + SetBank_CPU_6(data); + } + else + { + prg0 = (byte)(data & 0x3F); + SetBank_CPU_4(data); + } + break; + case 0x07: + prg1 = (byte)(data & 0x3F); + SetBank_CPU_5(data); + break; + } + break; + case 0xA000: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0xC000: + if (patch == 2) + { + if (data == 0x29 || data == 0x70) + data = 0x07; + } + irq_latch = data; + irq_latched = 1; + if (irq_reset != 0) + { + irq_counter = data; + irq_latched = 0; + } + // irq_counter = data; + break; + case 0xC001: + // irq_latch = data; + irq_counter = irq_latch; + break; + case 0xE000: + irq_enable = 0; + irq_reset = 1; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + irq_enable = 1; + if (irq_latched != 0) + { + irq_counter = irq_latch; + } + break; + } + } + + //void Mapper045::HSync(INT scanline) + public override void HSync(int scanline) + { + irq_reset = 0; + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_counter != 0) + { + irq_counter--; + if (irq_counter == 0) + { + if (irq_enable != 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU_4(INT data) + { + data &= (reg[3] & 0x3F) ^ 0xFF; + data &= 0x3F; + data |= reg[1]; + SetPROM_8K_Bank(4, data); + p[0] = (byte)data; + } + + void SetBank_CPU_5(INT data) + { + data &= (reg[3] & 0x3F) ^ 0xFF; + data &= 0x3F; + data |= reg[1]; + SetPROM_8K_Bank(5, data); + p[1] = (byte)data; + } + + void SetBank_CPU_6(INT data) + { + data &= (reg[3] & 0x3F) ^ 0xFF; + data &= 0x3F; + data |= reg[1]; + SetPROM_8K_Bank(6, data); + p[2] = (byte)data; + } + + void SetBank_CPU_7(INT data) + { + data &= (reg[3] & 0x3F) ^ 0xFF; + data &= 0x3F; + data |= reg[1]; + SetPROM_8K_Bank(7, data); + p[3] = (byte)data; + } + + void SetBank_PPU() + { + BYTE[] table = new byte[] { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x03,0x07,0x0F,0x1F,0x3F,0x7F,0xFF + }; + + c[0] = chr0; + c[1] = chr1; + c[2] = chr2; + c[3] = chr3; + c[4] = chr4; + c[5] = chr5; + c[6] = chr6; + c[7] = chr7; + + for (INT i = 0; i < 8; i++) + { + c[i] &= table[reg[2] & 0x0F]; + c[i] |= reg[0] & ((patch != 1) ? 0xFF : 0xC0); + c[i] += (reg[2] & ((patch != 1) ? 0x10 : 0x30)) << 4; + } + + if ((reg[6] & 0x80) != 0) + { + SetVROM_8K_Bank(c[4], c[5], c[6], c[7], c[0], c[1], c[2], c[3]); + } + else + { + SetVROM_8K_Bank(c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]); + } + } + + //void Mapper045::SaveState(LPBYTE ps) + public override void SaveState(byte[] ps) + { + int i; + for (i = 0; i < 8; i++) + { + ps[i] = reg[i]; + } + for (i = 0; i < 4; i++) + { + ps[i + 8] = p[i]; + } + for (i = 0; i < 8; i++) + { + //*(INT*)&ps[i * 4 + 64] = c[i]; + BitConverter.GetBytes(c[i]).CopyTo(ps, i * 4 + 64); + } + ps[20] = prg0; + ps[21] = prg1; + ps[22] = prg2; + ps[23] = prg3; + ps[24] = chr0; + ps[25] = chr1; + ps[26] = chr2; + ps[27] = chr3; + ps[28] = chr4; + ps[29] = chr5; + ps[30] = chr6; + ps[31] = chr7; + ps[32] = irq_enable; + ps[33] = irq_counter; + ps[34] = irq_latch; + } + + //void Mapper045::LoadState(LPBYTE ps) + public override void LoadState(byte[] ps) + { + int i; + for (i = 0; i < 8; i++) + { + reg[i] = ps[i]; + } + for (i = 0; i < 4; i++) + { + p[i] = ps[i + 8]; + } + for (i = 0; i < 8; i++) + { + //c[i] = *(INT*)&ps[i * 4 + 64]; + c[i] = BitConverter.ToInt32(ps, i * 4 + 64); + } + prg0 = ps[20]; + prg1 = ps[21]; + prg2 = ps[22]; + prg3 = ps[23]; + chr0 = ps[24]; + chr1 = ps[25]; + chr2 = ps[26]; + chr3 = ps[27]; + chr4 = ps[28]; + chr5 = ps[29]; + chr6 = ps[30]; + chr7 = ps[31]; + irq_enable = ps[32]; + irq_counter = ps[33]; + irq_latch = ps[34]; + } + + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper046.cs b/Core/VirtualNes.Core/Mapper/Mapper046.cs new file mode 100644 index 0000000..a649e4d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper046.cs @@ -0,0 +1,85 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper046 Rumble Station // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper046 : Mapper + { + int[] reg = new int[4]; + public Mapper046(NES parent) : base(parent) + { + } + + + public override void Reset() + { + reg[0] = 0; + reg[1] = 0; + reg[2] = 0; + reg[3] = 0; + + SetBank(); + SetVRAM_Mirror(VRAM_VMIRROR); + } + + //void Mapper046::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + reg[0] = data & 0x0F; + reg[1] = (data & 0xF0) >> 4; + SetBank(); + } + + //void Mapper046::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + reg[2] = data & 0x01; + reg[3] = (data & 0x70) >> 4; + SetBank(); + } + + void SetBank() + { + SetPROM_8K_Bank(4, reg[0] * 8 + reg[2] * 4 + 0); + SetPROM_8K_Bank(5, reg[0] * 8 + reg[2] * 4 + 1); + SetPROM_8K_Bank(6, reg[0] * 8 + reg[2] * 4 + 2); + SetPROM_8K_Bank(7, reg[0] * 8 + reg[2] * 4 + 3); + + SetVROM_1K_Bank(0, reg[1] * 64 + reg[3] * 8 + 0); + SetVROM_1K_Bank(1, reg[1] * 64 + reg[3] * 8 + 1); + SetVROM_1K_Bank(2, reg[1] * 64 + reg[3] * 8 + 2); + SetVROM_1K_Bank(3, reg[1] * 64 + reg[3] * 8 + 3); + SetVROM_1K_Bank(4, reg[1] * 64 + reg[3] * 8 + 4); + SetVROM_1K_Bank(5, reg[1] * 64 + reg[3] * 8 + 5); + SetVROM_1K_Bank(6, reg[1] * 64 + reg[3] * 8 + 6); + SetVROM_1K_Bank(7, reg[1] * 64 + reg[3] * 8 + 7); + } + + //void Mapper046::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = (byte)reg[0]; + p[1] = (byte)reg[1]; + p[2] = (byte)reg[2]; + p[3] = (byte)reg[3]; + } + + //void Mapper046::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper047.cs b/Core/VirtualNes.Core/Mapper/Mapper047.cs new file mode 100644 index 0000000..365fe14 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper047.cs @@ -0,0 +1,274 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper047 NES-QJ // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper047 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE patch; + BYTE bank; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper047(NES parent) : base(parent) + { + } + + public override void Reset() + { + patch = 0; + + if (nes.rom.GetPROM_CRC() == 0x7eef434c) + { + patch = 1; + } + + for (INT i = 0; i < 8; i++) + { + reg[i] = 0; + } + + bank = 0; + prg0 = 0; + prg1 = 1; + + // set VROM banks + if (VROM_1K_SIZE != 0) + { + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + } + else + { + chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0; + } + + SetBank_CPU(); + SetBank_PPU(); + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper047::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + if (patch != 0) + { + bank = (byte)((data & 0x06) >> 1); + } + else + { + bank = (byte)((data & 0x01) << 1); + } + SetBank_CPU(); + SetBank_PPU(); + } + } + + //void Mapper047::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + break; + } + } + + //void Mapper047::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((--irq_counter) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_8K_Bank(4, bank * 8 + ((patch != 0 && bank != 2) ? 6 : 14)); + SetPROM_8K_Bank(5, bank * 8 + prg1); + SetPROM_8K_Bank(6, bank * 8 + prg0); + SetPROM_8K_Bank(7, bank * 8 + ((patch != 0 && bank != 2) ? 7 : 15)); + } + else + { + SetPROM_8K_Bank(4, bank * 8 + prg0); + SetPROM_8K_Bank(5, bank * 8 + prg1); + SetPROM_8K_Bank(6, bank * 8 + ((patch != 0 && bank != 2) ? 6 : 14)); + SetPROM_8K_Bank(7, bank * 8 + ((patch != 0 && bank != 2) ? 7 : 15)); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_1K_Bank(0, (bank & 0x02) * 64 + chr4); + SetVROM_1K_Bank(1, (bank & 0x02) * 64 + chr5); + SetVROM_1K_Bank(2, (bank & 0x02) * 64 + chr6); + SetVROM_1K_Bank(3, (bank & 0x02) * 64 + chr7); + SetVROM_1K_Bank(4, (bank & 0x02) * 64 + chr01 + 0); + SetVROM_1K_Bank(5, (bank & 0x02) * 64 + chr01 + 1); + SetVROM_1K_Bank(6, (bank & 0x02) * 64 + chr23 + 0); + SetVROM_1K_Bank(7, (bank & 0x02) * 64 + chr23 + 1); + } + else + { + SetVROM_1K_Bank(0, (bank & 0x02) * 64 + chr01 + 0); + SetVROM_1K_Bank(1, (bank & 0x02) * 64 + chr01 + 1); + SetVROM_1K_Bank(2, (bank & 0x02) * 64 + chr23 + 0); + SetVROM_1K_Bank(3, (bank & 0x02) * 64 + chr23 + 1); + SetVROM_1K_Bank(4, (bank & 0x02) * 64 + chr4); + SetVROM_1K_Bank(5, (bank & 0x02) * 64 + chr5); + SetVROM_1K_Bank(6, (bank & 0x02) * 64 + chr6); + SetVROM_1K_Bank(7, (bank & 0x02) * 64 + chr7); + } + } + } + + //void Mapper047::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = bank; + } + + //void Mapper047::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + bank = p[19]; + } + + + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper048.cs b/Core/VirtualNes.Core/Mapper/Mapper048.cs new file mode 100644 index 0000000..0342608 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper048.cs @@ -0,0 +1,141 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper048 Taito TC190V // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper048 : Mapper + { + BYTE reg; + BYTE irq_enable; + BYTE irq_counter; + public Mapper048(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg = 0; + irq_enable = 0; + irq_counter = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + uint crc = nes.rom.GetPROM_CRC(); + // if( crc == 0x547e6cc1 ) { // Flintstones - The Rescue of Dino & Hoppy(J) + // nes.SetRenderMethod( NES::POST_RENDER ); + // } + } + + //void Mapper048::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + if (reg == 0) + { + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + SetPROM_8K_Bank(4, data); + break; + case 0x8001: + SetPROM_8K_Bank(5, data); + break; + + case 0x8002: + SetVROM_2K_Bank(0, data); + break; + case 0x8003: + SetVROM_2K_Bank(2, data); + break; + case 0xA000: + SetVROM_1K_Bank(4, data); + break; + case 0xA001: + SetVROM_1K_Bank(5, data); + break; + case 0xA002: + SetVROM_1K_Bank(6, data); + break; + case 0xA003: + SetVROM_1K_Bank(7, data); + break; + + case 0xC000: + irq_counter = data; + irq_enable = 0; + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + + case 0xC001: + irq_counter = data; + irq_enable = 1; + // irq_enable = data & 0x01; + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + + case 0xC002: + break; + case 0xC003: + break; + + case 0xE000: + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + reg = 1; + break; + } + } + + //void Mapper048::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (irq_counter == 0xFF) + { + // nes.cpu.IRQ_NotPending(); + // nes.cpu.SetIRQ( IRQ_MAPPER ); + nes.cpu.SetIRQ(IRQ_TRIGGER2); + } + irq_counter++; + } + } + } + } + + //void Mapper048::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + p[1] = irq_enable; + p[2] = irq_counter; + } + + //void Mapper048::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + irq_enable = p[1]; + irq_counter = p[2]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper050.cs b/Core/VirtualNes.Core/Mapper/Mapper050.cs new file mode 100644 index 0000000..f62c2e3 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper050.cs @@ -0,0 +1,98 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper050 SMB2J // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper050 : Mapper + { + BYTE irq_enable; + public Mapper050(NES parent) : base(parent) + { + } + + public override void Reset() + { + irq_enable = 0; + SetPROM_8K_Bank(3, 15); + SetPROM_8K_Bank(4, 8); + SetPROM_8K_Bank(5, 9); + SetPROM_8K_Bank(6, 0); + SetPROM_8K_Bank(7, 11); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper050::ExWrite(WORD addr, BYTE data) + public override void ExWrite(ushort addr, byte data) + { + if ((addr & 0xE060) == 0x4020) + { + if ((addr & 0x0100) != 0) + { + irq_enable = (byte)(data & 0x01); + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + else + { + SetPROM_8K_Bank(6, (data & 0x08) | ((data & 0x01) << 2) | ((data & 0x06) >> 1)); + } + } + } + + //void Mapper050::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0xE060) == 0x4020) + { + if ((addr & 0x0100) != 0) + { + irq_enable = (byte)(data & 0x01); + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + else + { + SetPROM_8K_Bank(6, (data & 0x08) | ((data & 0x01) << 2) | ((data & 0x06) >> 1)); + } + } + } + + //void Mapper050::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if (scanline == 21) + { + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper050::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + } + + //void Mapper050::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper051.cs b/Core/VirtualNes.Core/Mapper/Mapper051.cs new file mode 100644 index 0000000..76c13ae --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper051.cs @@ -0,0 +1,105 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper051 11-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper051 : Mapper + { + int mode, bank; + public Mapper051(NES parent) : base(parent) + { + } + + public override void Reset() + { + bank = 0; + mode = 1; + + SetBank_CPU(); + SetCRAM_8K_Bank(0); + } + + //void Mapper051::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000) + { + mode = ((data & 0x10) >> 3) | ((data & 0x02) >> 1); + SetBank_CPU(); + } + } + + //void Mapper051::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + bank = (data & 0x0f) << 2; + if (0xC000 <= addr && addr <= 0xDFFF) + { + mode = (mode & 0x01) | ((data & 0x10) >> 3); + } + SetBank_CPU(); + } + + void SetBank_CPU() + { + switch (mode) + { + case 0: + SetVRAM_Mirror(VRAM_VMIRROR); + SetPROM_8K_Bank(3, (bank | 0x2c | 3)); + SetPROM_8K_Bank(4, (bank | 0x00 | 0)); + SetPROM_8K_Bank(5, (bank | 0x00 | 1)); + SetPROM_8K_Bank(6, (bank | 0x0c | 2)); + SetPROM_8K_Bank(7, (bank | 0x0c | 3)); + break; + case 1: + SetVRAM_Mirror(VRAM_VMIRROR); + SetPROM_8K_Bank(3, (bank | 0x20 | 3)); + SetPROM_8K_Bank(4, (bank | 0x00 | 0)); + SetPROM_8K_Bank(5, (bank | 0x00 | 1)); + SetPROM_8K_Bank(6, (bank | 0x00 | 2)); + SetPROM_8K_Bank(7, (bank | 0x00 | 3)); + break; + case 2: + SetVRAM_Mirror(VRAM_VMIRROR); + SetPROM_8K_Bank(3, (bank | 0x2e | 3)); + SetPROM_8K_Bank(4, (bank | 0x02 | 0)); + SetPROM_8K_Bank(5, (bank | 0x02 | 1)); + SetPROM_8K_Bank(6, (bank | 0x0e | 2)); + SetPROM_8K_Bank(7, (bank | 0x0e | 3)); + break; + case 3: + SetVRAM_Mirror(VRAM_HMIRROR); + SetPROM_8K_Bank(3, (bank | 0x20 | 3)); + SetPROM_8K_Bank(4, (bank | 0x00 | 0)); + SetPROM_8K_Bank(5, (bank | 0x00 | 1)); + SetPROM_8K_Bank(6, (bank | 0x00 | 2)); + SetPROM_8K_Bank(7, (bank | 0x00 | 3)); + break; + } + } + + //void Mapper051::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = (byte)mode; + p[1] = (byte)bank; + } + + //void Mapper051::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + mode = p[0]; + bank = p[1]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper052.cs b/Core/VirtualNes.Core/Mapper/Mapper052.cs new file mode 100644 index 0000000..6f0793f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper052.cs @@ -0,0 +1,198 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper052 Konami VRC2 type B // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper052 : Mapper + { + + BYTE[] reg = new byte[9]; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper052(NES parent) : base(parent) { } + + public override void Reset() + { + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + reg[8] = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + // nes->SetRenderMethod( NES::POST_RENDER ); + // nes->SetRenderMethod( NES::POST_ALL_RENDER ); + + } + + public override void Write(ushort addr, byte data) + { + if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} D={data & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n"); + if (addr >= 0xf000) Debuger.Log($"MPRWR A={addr & 0xFFFF} RAM={RAM[0x1c0] & 0xFF} L={nes.GetScanline()} CYC={nes.cpu.GetTotalCycles()}\n"); + switch (addr & 0xFFFF) + { + case 0x8000: + if (reg[8] != 0) SetPROM_8K_Bank(6, data); + else SetPROM_8K_Bank(4, data); + break; + case 0x9002: + reg[8] = (byte)(data & 0x02); + break; + + case 0x9004: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB001: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB002: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB003: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC001: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC002: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC003: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD001: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD002: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD003: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE001: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE002: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE003: + reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + break; + case 0xF004: + case 0xFF04: + RAM[0x7f8] = 1; + break; + case 0xF008: + case 0xFF08: + irq_enable = 1; + // irq_latch = ((RAM[0x7f8]*2)+0x11)^0xFF; //Akumajou Special - Boku Dracula-kun + irq_latch = (byte)(((RAM[0x1c0] * 2) + 0x11) ^ 0xFF); //Teenage Mutant Ninja Turtles + irq_counter = irq_latch; + irq_clock = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xF00C: + irq_enable = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + } + } + + public override void HSync(int scanline) + { + // + } + + public override void Clock(int cycles) + { + if (irq_enable != null) + { + irq_clock += cycles * 3; + while (irq_clock >= 341) + { + irq_clock -= 341; + irq_counter++; + if (irq_counter == 0) + { + irq_counter = irq_latch; + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + } + } + + public override void SaveState(byte[] p) + { + // + } + + public override void LoadState(byte[] p) + { + // + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper057.cs b/Core/VirtualNes.Core/Mapper/Mapper057.cs new file mode 100644 index 0000000..57ca106 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper057.cs @@ -0,0 +1,84 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper057 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper057 : Mapper + { + + BYTE reg; + public Mapper057(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 0, 1); + SetVROM_8K_Bank(0); + reg = 0; + } + + //void Mapper057::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: + if ((data & 0x40) != 0) + { + SetVROM_8K_Bank((data & 0x03) + ((reg & 0x10) >> 1) + (reg & 0x07)); + } + break; + case 0x8800: + reg = data; + + if ((data & 0x80) != 0) + { + SetPROM_8K_Bank(4, ((data & 0x40) >> 6) * 4 + 8 + 0); + SetPROM_8K_Bank(5, ((data & 0x40) >> 6) * 4 + 8 + 1); + SetPROM_8K_Bank(6, ((data & 0x40) >> 6) * 4 + 8 + 2); + SetPROM_8K_Bank(7, ((data & 0x40) >> 6) * 4 + 8 + 3); + } + else + { + SetPROM_8K_Bank(4, ((data & 0x60) >> 5) * 2 + 0); + SetPROM_8K_Bank(5, ((data & 0x60) >> 5) * 2 + 1); + SetPROM_8K_Bank(6, ((data & 0x60) >> 5) * 2 + 0); + SetPROM_8K_Bank(7, ((data & 0x60) >> 5) * 2 + 1); + } + + SetVROM_8K_Bank((data & 0x07) + ((data & 0x10) >> 1)); + + if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + + break; + } + } + + //void Mapper057::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + } + + //void Mapper057::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper058.cs b/Core/VirtualNes.Core/Mapper/Mapper058.cs new file mode 100644 index 0000000..1aaf7c7 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper058.cs @@ -0,0 +1,49 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper058 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper058 : Mapper + { + public Mapper058(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 0, 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper058::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0x40) != 0) + { + SetPROM_16K_Bank(4, addr & 0x07); + SetPROM_16K_Bank(6, addr & 0x07); + } + else + { + SetPROM_32K_Bank((addr & 0x06) >> 1); + } + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank((addr & 0x38) >> 3); + } + + if ((data & 0x02) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + } + + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper060.cs b/Core/VirtualNes.Core/Mapper/Mapper060.cs new file mode 100644 index 0000000..4928ee2 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper060.cs @@ -0,0 +1,63 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper060 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper060 : Mapper + { + BYTE patch; + BYTE game_sel; + public Mapper060(NES parent) : base(parent) + { + } + + public override void Reset() + { + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0xf9c484a0) + { // Reset Based 4-in-1(Unl) + SetPROM_16K_Bank(4, game_sel); + SetPROM_16K_Bank(6, game_sel); + SetVROM_8K_Bank(game_sel); + game_sel++; + game_sel &= 3; + } + else + { + patch = 1; + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + } + + //void Mapper060::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (patch != 0) + { + if ((addr & 0x80) != 0) + { + SetPROM_16K_Bank(4, (addr & 0x70) >> 4); + SetPROM_16K_Bank(6, (addr & 0x70) >> 4); + } + else + { + SetPROM_32K_Bank((addr & 0x70) >> 5); + } + + SetVROM_8K_Bank(addr & 0x07); + + if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper061.cs b/Core/VirtualNes.Core/Mapper/Mapper061.cs new file mode 100644 index 0000000..057e9ee --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper061.cs @@ -0,0 +1,43 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper061 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper061 : Mapper + { + public Mapper061(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper061::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0x30) + { + case 0x00: + case 0x30: + SetPROM_32K_Bank(addr & 0x0F); + break; + case 0x10: + case 0x20: + SetPROM_16K_Bank(4, ((addr & 0x0F) << 1) | ((addr & 0x20) >> 4)); + SetPROM_16K_Bank(6, ((addr & 0x0F) << 1) | ((addr & 0x20) >> 4)); + break; + } + + if ((addr & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper062.cs b/Core/VirtualNes.Core/Mapper/Mapper062.cs new file mode 100644 index 0000000..0ad6a94 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper062.cs @@ -0,0 +1,49 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper062 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper062 : Mapper + { + public Mapper062(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper062::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xFF00) + { + case 0x8100: + SetPROM_8K_Bank(4, data); + SetPROM_8K_Bank(5, data + 1); + break; + case 0x8500: + SetPROM_8K_Bank(4, data); + break; + case 0x8700: + SetPROM_8K_Bank(5, data); + break; + SetVROM_1K_Bank(0, data + 0); + SetVROM_1K_Bank(1, data + 1); + SetVROM_1K_Bank(2, data + 2); + SetVROM_1K_Bank(3, data + 3); + SetVROM_1K_Bank(4, data + 4); + SetVROM_1K_Bank(5, data + 5); + SetVROM_1K_Bank(6, data + 6); + SetVROM_1K_Bank(7, data + 7); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper064.cs b/Core/VirtualNes.Core/Mapper/Mapper064.cs new file mode 100644 index 0000000..c403718 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper064.cs @@ -0,0 +1,287 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper064 Tengen Rambo-1 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper064 : Mapper + { + BYTE[] reg = new byte[3]; + BYTE irq_enable; + BYTE irq_mode; + INT irq_counter; + INT irq_counter2; + BYTE irq_latch; + BYTE irq_reset; + public Mapper064(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + reg[0] = reg[1] = reg[2] = 0; + + irq_enable = 0; + irq_mode = 0; + irq_counter = 0; + irq_counter2 = 0; + irq_latch = 0; + irq_reset = 0; + } + + //void Mapper064::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "$%04X:$%02X\n", addr, data ); + switch (addr & 0xF003) + { + case 0x8000: + reg[0] = (byte)(data & 0x0F); + reg[1] = (byte)(data & 0x40); + reg[2] = (byte)(data & 0x80); + break; + + case 0x8001: + switch (reg[0]) + { + case 0x00: + if (reg[2] != 0) + { + SetVROM_1K_Bank(4, data + 0); + SetVROM_1K_Bank(5, data + 1); + } + else + { + SetVROM_1K_Bank(0, data + 0); + SetVROM_1K_Bank(1, data + 1); + } + break; + case 0x01: + if (reg[2] != 0) + { + SetVROM_1K_Bank(6, data + 0); + SetVROM_1K_Bank(7, data + 1); + } + else + { + SetVROM_1K_Bank(2, data + 0); + SetVROM_1K_Bank(3, data + 1); + } + break; + case 0x02: + if (reg[2] != 0) + { + SetVROM_1K_Bank(0, data); + } + else + { + SetVROM_1K_Bank(4, data); + } + break; + case 0x03: + if (reg[2] != 0) + { + SetVROM_1K_Bank(1, data); + } + else + { + SetVROM_1K_Bank(5, data); + } + break; + case 0x04: + if (reg[2] != 0) + { + SetVROM_1K_Bank(2, data); + } + else + { + SetVROM_1K_Bank(6, data); + } + break; + case 0x05: + if (reg[2] != 0) + { + SetVROM_1K_Bank(3, data); + } + else + { + SetVROM_1K_Bank(7, data); + } + break; + case 0x06: + if (reg[1] != 0) + { + SetPROM_8K_Bank(5, data); + } + else + { + SetPROM_8K_Bank(4, data); + } + break; + case 0x07: + if (reg[1] != 0) + { + SetPROM_8K_Bank(6, data); + } + else + { + SetPROM_8K_Bank(5, data); + } + break; + case 0x08: + SetVROM_1K_Bank(1, data); + break; + case 0x09: + SetVROM_1K_Bank(3, data); + break; + case 0x0F: + if (reg[1] != 0) + { + SetPROM_8K_Bank(4, data); + } + else + { + SetPROM_8K_Bank(6, data); + } + break; + } + break; + + case 0xA000: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0xC000: + irq_latch = data; + if (irq_reset != 0) + { + irq_counter = irq_latch; + } + break; + case 0xC001: + irq_reset = 0xFF; + irq_counter = irq_latch; + irq_mode = (byte)(data & 0x01); + break; + case 0xE000: + irq_enable = 0; + if (irq_reset != 0) + { + irq_counter = irq_latch; + } + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + irq_enable = 0xFF; + if (irq_reset != 0) + { + irq_counter = irq_latch; + } + break; + } + } + + //void Mapper064::Clock(INT cycles) + public override void Clock(int cycles) + { + if (irq_mode == 0) + return; + + irq_counter2 += cycles; + while (irq_counter2 >= 4) + { + irq_counter2 -= 4; + if (irq_counter >= 0) + { + irq_counter--; + if (irq_counter < 0) + { + if (irq_enable != 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + //void Mapper064::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_mode != 0) + return; + + irq_reset = 0; + + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_counter >= 0) + { + irq_counter--; + if (irq_counter < 0) + { + if (irq_enable != 0) + { + irq_reset = 1; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + } + + //void Mapper064::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = irq_enable; + p[4] = irq_mode; + p[5] = irq_latch; + p[6] = irq_reset; + //*((INT*)&p[8]) = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 8); + //* ((INT*)&p[12]) = irq_counter2; + BitConverter.GetBytes(irq_counter2).CopyTo(p, 12); + } + + //void Mapper064::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + irq_enable = p[3]; + irq_mode = p[4]; + irq_latch = p[5]; + irq_reset = p[6]; + //irq_counter = *((INT*)&p[8]); + irq_counter = BitConverter.ToInt32(p, 8); + //irq_counter2 = *((INT*)&p[12]); + irq_counter2 = BitConverter.ToInt32(p, 12); + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper065.cs b/Core/VirtualNes.Core/Mapper/Mapper065.cs new file mode 100644 index 0000000..7c93a04 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper065.cs @@ -0,0 +1,193 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper065 Irem H3001 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper065 : Mapper + { + BYTE patch; + + BYTE irq_enable; + INT irq_counter; + INT irq_latch; + public Mapper065(NES parent) : base(parent) + { + } + + public override void Reset() + { + patch = 0; + + // Kaiketsu Yanchamaru 3(J) + if (nes.rom.GetPROM_CRC() == 0xe30b7f64) + { + patch = 1; + } + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + irq_enable = 0; + irq_counter = 0; + } + + //void Mapper065::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + + case 0x9000: + if (patch == 0) + { + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + } + break; + + case 0x9001: + if (patch != 0) + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + + case 0x9003: + if (patch == 0) + { + irq_enable = (byte)(data & 0x8); + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + break; + case 0x9004: + if (patch == 0) + { + irq_counter = irq_latch; + } + break; + case 0x9005: + if (patch != 0) + { + irq_counter = (BYTE)(data << 1); + irq_enable = data; + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + else + { + irq_latch = (irq_latch & 0x00FF) | ((INT)data << 8); + } + break; + + case 0x9006: + if (patch != 0) + { + irq_enable = 1; + } + else + { + irq_latch = (irq_latch & 0xFF00) | data; + } + break; + + case 0xB000: + case 0xB001: + case 0xB002: + case 0xB003: + case 0xB004: + case 0xB005: + case 0xB006: + case 0xB007: + SetVROM_1K_Bank((byte)(addr & 0x0007), data); + break; + + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + case 0xC000: + SetPROM_8K_Bank(6, data); + break; + } + } + + //void Mapper065::HSync(INT scanline) + public override void HSync(int scanline) + { + if (patch != 0) + { + if (irq_enable != 0) + { + if (irq_counter == 0) + { + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter--; + } + } + } + } + + //void Mapper065::Clock(INT cycles) + public override void Clock(int cycles) + { + if (patch == 0) + { + if (irq_enable != 0) + { + if (irq_counter <= 0) + { + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter -= cycles; + } + } + } + } + + //void Mapper065::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 1); + //* (INT*)&p[5] = irq_latch; + BitConverter.GetBytes(irq_latch).CopyTo(p, 5); + } + + //void Mapper065::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_counter = *(INT*)&p[1]; + irq_counter = BitConverter.ToInt32(p, 1); + //irq_latch = *(INT*)&p[5]; + irq_latch = BitConverter.ToInt32(p, 5); + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper066.cs b/Core/VirtualNes.Core/Mapper/Mapper066.cs new file mode 100644 index 0000000..00b4d87 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper066.cs @@ -0,0 +1,44 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper066 Bandai 74161 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper066 : Mapper + { + public Mapper066(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + // if( nes->rom->GetPROM_CRC() == 0xe30552db ) { // Paris-Dakar Rally Special + // nes->SetFrameIRQmode( FALSE ); + // } + } + + //void Mapper066::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000) + { + SetPROM_32K_Bank((data & 0xF0) >> 4); + SetVROM_8K_Bank(data & 0x0F); + } + } + + //void Mapper066::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank((data & 0xF0) >> 4); + SetVROM_8K_Bank(data & 0x0F); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper067.cs b/Core/VirtualNes.Core/Mapper/Mapper067.cs new file mode 100644 index 0000000..137393f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper067.cs @@ -0,0 +1,142 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper067 SunSoft Mapper 3 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper067 : Mapper + { + BYTE irq_enable; + BYTE irq_occur; + BYTE irq_toggle; + INT irq_counter; + public Mapper067(NES parent) : base(parent) + { + } + + public override void Reset() + + { + irq_enable = 0; + irq_toggle = 0; + irq_counter = 0; + irq_occur = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + SetVROM_4K_Bank(0, 0); + SetVROM_4K_Bank(4, VROM_4K_SIZE - 1); + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x7f2a04bf) + { // For Fantasy Zone 2(J) + nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER); + } + } + + //void Mapper067::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF800) + { + case 0x8800: + SetVROM_2K_Bank(0, data); + break; + case 0x9800: + SetVROM_2K_Bank(2, data); + break; + case 0xA800: + SetVROM_2K_Bank(4, data); + break; + case 0xB800: + SetVROM_2K_Bank(6, data); + break; + + case 0xC800: + if (irq_toggle == 0) + { + irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8); + } + else + { + irq_counter = (irq_counter & 0xFF00) | ((INT)data & 0xFF); + } + irq_toggle ^= 1; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xD800: + irq_enable = (byte)(data & 0x10); + irq_toggle = 0; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xE800: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0xF800: + SetPROM_16K_Bank(4, data); + break; + } + } + + //void Mapper067::Clock(INT cycles) + public override void Clock(int cycles) + { + if (irq_enable != 0) + { + if ((irq_counter -= cycles) <= 0) + { + irq_enable = 0; + irq_occur = 0xFF; + irq_counter = 0xFFFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + + // if( irq_occur ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + //void Mapper067::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_occur; + p[2] = irq_toggle; + //*((INT*)&p[3]) = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 3); + } + + //void Mapper067::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_occur = p[1]; + irq_toggle = p[2]; + //irq_counter = *((INT*)&p[3]); + irq_counter = BitConverter.ToInt32(p, 3); + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper068.cs b/Core/VirtualNes.Core/Mapper/Mapper068.cs new file mode 100644 index 0000000..2136edb --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper068.cs @@ -0,0 +1,162 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper068 SunSoft Mapper 4 (After Burner II) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper068 : Mapper + { + BYTE[] reg = new byte[4]; + BYTE coin; + public Mapper068(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg[0] = reg[1] = reg[2] = reg[3] = 0; + coin = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + +#if FALSE //0 +BYTE Mapper068::ExRead( WORD addr ) +{ + if( addr == 0x4020 ) { +DEBUGOUT( "RD $4020:%02X\n", coin ); + return coin; + } + + return addr>>8; +} + +void Mapper068::ExWrite( WORD addr, BYTE data ) +{ + if( addr == 0x4020 ) { +DEBUGOUT( "WR $4020:%02X\n", data ); + coin = data; + } +} +#endif + + //void Mapper068::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x8000: + SetVROM_2K_Bank(0, data); + break; + case 0x9000: + SetVROM_2K_Bank(2, data); + break; + case 0xA000: + SetVROM_2K_Bank(4, data); + break; + case 0xB000: + SetVROM_2K_Bank(6, data); + break; + + case 0xC000: + reg[2] = data; + SetBank(); + break; + case 0xD000: + reg[3] = data; + SetBank(); + break; + case 0xE000: + reg[0] = (byte)((data & 0x10) >> 4); + reg[1] = (byte)(data & 0x03); + SetBank(); + break; + + case 0xF000: + SetPROM_16K_Bank(4, data); + break; + } + } + + void SetBank() + { + if (reg[0] != 0) + { + switch (reg[1]) + { + case 0: + SetVROM_1K_Bank(8, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(9, (INT)reg[3] + 0x80); + SetVROM_1K_Bank(10, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(11, (INT)reg[3] + 0x80); + break; + case 1: + SetVROM_1K_Bank(8, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(9, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(10, (INT)reg[3] + 0x80); + SetVROM_1K_Bank(11, (INT)reg[3] + 0x80); + break; + case 2: + SetVROM_1K_Bank(8, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(9, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(10, (INT)reg[2] + 0x80); + SetVROM_1K_Bank(11, (INT)reg[2] + 0x80); + break; + case 3: + SetVROM_1K_Bank(8, (INT)reg[3] + 0x80); + SetVROM_1K_Bank(9, (INT)reg[3] + 0x80); + SetVROM_1K_Bank(10, (INT)reg[3] + 0x80); + SetVROM_1K_Bank(11, (INT)reg[3] + 0x80); + break; + } + } + else + { + switch (reg[1]) + { + case 0: + SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 1: + SetVRAM_Mirror(VRAM_HMIRROR); + break; + case 2: + SetVRAM_Mirror(VRAM_MIRROR4L); + break; + case 3: + SetVRAM_Mirror(VRAM_MIRROR4H); + break; + } + } + } + + //void Mapper068::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + } + + //void Mapper068::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper069.cs b/Core/VirtualNes.Core/Mapper/Mapper069.cs new file mode 100644 index 0000000..5677c35 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper069.cs @@ -0,0 +1,179 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper069 SunSoft FME-7 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper069 : Mapper + { + BYTE patch; + + BYTE reg; + BYTE irq_enable; + INT irq_counter; + public Mapper069(NES parent) : base(parent) + { + } + + public override void Reset() + + { + reg = 0; + irq_enable = 0; + irq_counter = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + nes.apu.SelectExSound(32); + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xfeac6916) + { // Honoo no Toukyuuji - Dodge Danpei 2(J) + // nes.SetIrqType( NES::IRQ_HSYNC ); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0xad28aef6) + { // Dynamite Batman(J) / Dynamite Batman - Return of the Joker(U) + patch = 1; + } + } + + //void Mapper069::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE000) + { + case 0x8000: + reg = data; + break; + + case 0xA000: + switch (reg & 0x0F) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + SetVROM_1K_Bank((byte)(reg & 0x07), data); + break; + case 0x08: + if (patch == 0 && (data & 0x40) == 0) + { + SetPROM_8K_Bank(3, data); + } + break; + case 0x09: + SetPROM_8K_Bank(4, data); + break; + case 0x0A: + SetPROM_8K_Bank(5, data); + break; + case 0x0B: + SetPROM_8K_Bank(6, data); + break; + + case 0x0C: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x0D: + irq_enable = data; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0x0E: + irq_counter = (irq_counter & 0xFF00) | data; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0x0F: + irq_counter = (irq_counter & 0x00FF) | (data << 8); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + break; + + case 0xC000: + case 0xE000: + nes.apu.ExWrite(addr, data); + break; + } + } + + //void Mapper069::Clock(INT cycles) + public override void Clock(int cycles) + { + //if (irq_enable && (nes.GetIrqType() == NES::IRQ_CLOCK)) + if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC)) + { + irq_counter -= cycles; + if (irq_counter <= 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + irq_enable = 0; + irq_counter = 0xFFFF; + } + } + } + + //void Mapper069::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0 && (nes.GetIrqType() == (int)NES.IRQMETHOD.IRQ_HSYNC)) + { + irq_counter -= 114; + if (irq_counter <= 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + irq_enable = 0; + irq_counter = 0xFFFF; + } + } + } + + //void Mapper069::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + p[1] = irq_enable; + //*(INT*)&p[2] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 2); + } + + //void Mapper069::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + irq_enable = p[1]; + //irq_counter = *(INT*)&p[2]; + irq_counter = BitConverter.ToInt32(p, 2); + } + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper070.cs b/Core/VirtualNes.Core/Mapper/Mapper070.cs new file mode 100644 index 0000000..c1cca87 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper070.cs @@ -0,0 +1,61 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper070 Bandai 74161 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper070 : Mapper + { + BYTE patch; + public Mapper070(NES parent) : base(parent) + { + } + + public override void Reset() + { + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xa59ca2ef) + { // Kamen Rider Club(J) + patch = 1; + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + if (crc == 0x10bb8f9a) + { // Family Trainer - Manhattan Police(J) + patch = 1; + } + if (crc == 0x0cd00488) + { // Space Shadow(J) + patch = 1; + } + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + } + + //void Mapper070::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_16K_Bank(4, (data & 0x70) >> 4); + SetVROM_8K_Bank(data & 0x0F); + + if (patch != 0) + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper071.cs b/Core/VirtualNes.Core/Mapper/Mapper071.cs new file mode 100644 index 0000000..865f345 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper071.cs @@ -0,0 +1,50 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper071 Camerica // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper071 : Mapper + { + public Mapper071(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper071::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0xE000) == 0x6000) + { + SetPROM_16K_Bank(4, data); + } + } + + //void Mapper071::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x9000: + if ((data & 0x10) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + + case 0xC000: + case 0xD000: + case 0xE000: + case 0xF000: + SetPROM_16K_Bank(4, data); + break; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper072.cs b/Core/VirtualNes.Core/Mapper/Mapper072.cs new file mode 100644 index 0000000..c4495d5 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper072.cs @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper072 Jaleco/Type1 lower bank switch // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; + +namespace VirtualNes.Core +{ + public class Mapper072 : Mapper + { + public Mapper072(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper072::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((data & 0x80) != 0) + { + SetPROM_16K_Bank(4, data & 0x0F); + } + else if ((data & 0x40) != 0) + { + SetVROM_8K_Bank(data & 0x0F); + } + else + { + if (addr >= 0xC100 && addr <= 0xC11F && data == 0x20) + { + Debuger.Log($"SOUND CODE:{addr & 0x1F:X2}"); + + // OSDにするべきか… + if (Supporter.Config.sound.bExtraSoundEnable) + { + //TODO : 似乎VirtuaNES有直接播放某个音频文件的功能 + //DirectSound.EsfAllStop(); + //DirectSound.EsfPlay(ESF_MOETENNIS_00 + (addr & 0x1F)); + } + } + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper073.cs b/Core/VirtualNes.Core/Mapper/Mapper073.cs new file mode 100644 index 0000000..e43cb2b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper073.cs @@ -0,0 +1,96 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper073 Konami VRC3 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper073 : Mapper + { + BYTE irq_enable; + INT irq_counter; + public Mapper073(NES parent) : base(parent) + { + } + + public override void Reset() + { + irq_enable = 0; + irq_counter = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper073::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0xF000: + SetPROM_16K_Bank(4, data); + break; + + case 0x8000: + irq_counter = (irq_counter & 0xFFF0) | (data & 0x0F); + break; + case 0x9000: + irq_counter = (irq_counter & 0xFF0F) | ((data & 0x0F) << 4); + break; + case 0xA000: + irq_counter = (irq_counter & 0xF0FF) | ((data & 0x0F) << 8); + break; + case 0xB000: + irq_counter = (irq_counter & 0x0FFF) | ((data & 0x0F) << 12); + break; + case 0xC000: + irq_enable = (byte)(data & 0x02); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xD000: + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper073::Clock(INT cycles) + public override void Clock(int cycles) + { + if (irq_enable != 0) + { + if ((irq_counter += cycles) >= 0xFFFF) + { + irq_enable = 0; + irq_counter &= 0xFFFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper073::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + //*(INT*)&p[1] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 1); + } + + //void Mapper073::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + //irq_counter = *(INT*)&p[1]; + irq_counter = BitConverter.ToInt32(p, 1); + } + + public override bool IsStateSave() + { + return true; + } + } +} + diff --git a/Core/VirtualNes.Core/Mapper/Mapper074.cs b/Core/VirtualNes.Core/Mapper/Mapper074.cs new file mode 100644 index 0000000..ecdd94d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper074.cs @@ -0,0 +1,311 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper074 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper074 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + + BYTE patch; + public Mapper074(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + uint crc = nes.rom.GetPROM_CRC(); + + patch = 0; + if (crc == 0x37ae04a8) + { + patch = 1; + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + } + + //void Mapper074::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + break; + } + + } + + //void Mapper074::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + // SetVROM_8K_Bank( chr4, chr5, chr6, chr7, + // chr01, chr01+1, chr23, chr23+1 ); + SetBank_PPUSUB(4, chr01 + 0); + SetBank_PPUSUB(5, chr01 + 1); + SetBank_PPUSUB(6, chr23 + 0); + SetBank_PPUSUB(7, chr23 + 1); + SetBank_PPUSUB(0, chr4); + SetBank_PPUSUB(1, chr5); + SetBank_PPUSUB(2, chr6); + SetBank_PPUSUB(3, chr7); + } + else + { + // SetVROM_8K_Bank( chr01, chr01+1, chr23, chr23+1, + // chr4, chr5, chr6, chr7 ); + SetBank_PPUSUB(0, chr01 + 0); + SetBank_PPUSUB(1, chr01 + 1); + SetBank_PPUSUB(2, chr23 + 0); + SetBank_PPUSUB(3, chr23 + 1); + SetBank_PPUSUB(4, chr4); + SetBank_PPUSUB(5, chr5); + SetBank_PPUSUB(6, chr6); + SetBank_PPUSUB(7, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(0, chr4 & 0x07); + SetCRAM_1K_Bank(1, chr5 & 0x07); + SetCRAM_1K_Bank(2, chr6 & 0x07); + SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(4, chr4 & 0x07); + SetCRAM_1K_Bank(5, chr5 & 0x07); + SetCRAM_1K_Bank(6, chr6 & 0x07); + SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + + void SetBank_PPUSUB(int bank, int page) + { + if (patch == 0 && (page == 8 || page == 9)) + { + SetCRAM_1K_Bank((byte)bank, page & 7); + } + else if (patch == 1 && page >= 128) + { + SetCRAM_1K_Bank((byte)bank, page & 7); + } + else + { + SetVROM_1K_Bank((byte)bank, page); + } + } + + //void Mapper074::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + } + + //void Mapper074::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper075.cs b/Core/VirtualNes.Core/Mapper/Mapper075.cs new file mode 100644 index 0000000..b22bae7 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper075.cs @@ -0,0 +1,87 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper075 Konami VRC1/Jaleco D65005 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper075 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper075(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + reg[0] = 0; + reg[1] = 1; + } + + //void Mapper075::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + + case 0x9000: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x02) << 3)); + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x04) << 2)); + SetVROM_4K_Bank(0, reg[0]); + SetVROM_4K_Bank(4, reg[1]); + break; + + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + case 0xC000: + SetPROM_8K_Bank(6, data); + break; + + case 0xE000: + reg[0] = (byte)((reg[0] & 0x10) | (data & 0x0F)); + SetVROM_4K_Bank(0, reg[0]); + break; + + case 0xF000: + reg[1] = (byte)((reg[1] & 0x10) | (data & 0x0F)); + SetVROM_4K_Bank(4, reg[1]); + break; + } + } + + //void Mapper075::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper075::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper076.cs b/Core/VirtualNes.Core/Mapper/Mapper076.cs new file mode 100644 index 0000000..0ae16ff --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper076.cs @@ -0,0 +1,79 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper076 Namcot 109 (女神転生) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper076 : Mapper + { + BYTE reg; + public Mapper076(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE >= 8) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper076::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + reg = data; + break; + case 0x8001: + switch (reg & 0x07) + { + case 2: + SetVROM_2K_Bank(0, data); + break; + case 3: + SetVROM_2K_Bank(2, data); + break; + case 4: + SetVROM_2K_Bank(4, data); + break; + case 5: + SetVROM_2K_Bank(6, data); + break; + case 6: + SetPROM_8K_Bank(4, data); + break; + case 7: + SetPROM_8K_Bank(5, data); + break; + } + break; + } + } + + //void Mapper076::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + } + + //void Mapper076::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper077.cs b/Core/VirtualNes.Core/Mapper/Mapper077.cs new file mode 100644 index 0000000..a69be80 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper077.cs @@ -0,0 +1,35 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper077 Irem Early Mapper #0 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper077 : Mapper + { + public Mapper077(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + SetVROM_2K_Bank(0, 0); + SetCRAM_2K_Bank(2, 1); + SetCRAM_2K_Bank(4, 2); + SetCRAM_2K_Bank(6, 3); + } + + //void Mapper077::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank(data & 0x07); + + SetVROM_2K_Bank(0, (data & 0xF0) >> 4); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper078.cs b/Core/VirtualNes.Core/Mapper/Mapper078.cs new file mode 100644 index 0000000..825297c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper078.cs @@ -0,0 +1,41 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper078 Jaleco(Cosmo Carrier) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper078 : Mapper + { + public Mapper078(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper078::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MAP78 WR $%04X=$%02X L=%d\n", addr, data, nes->GetScanline() ); + SetPROM_16K_Bank(4, data & 0x0F); + SetVROM_8K_Bank((data & 0xF0) >> 4); + + if ((addr & 0xFE00) != 0xFE00) + { + if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper079.cs b/Core/VirtualNes.Core/Mapper/Mapper079.cs new file mode 100644 index 0000000..f0be268 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper079.cs @@ -0,0 +1,37 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper079 Nina-3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper079 : Mapper + { + public Mapper079(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper079::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0x0100) != 0) + { + SetPROM_32K_Bank((data >> 3) & 0x01); + SetVROM_8K_Bank(data & 0x07); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper080.cs b/Core/VirtualNes.Core/Mapper/Mapper080.cs new file mode 100644 index 0000000..bfd6263 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper080.cs @@ -0,0 +1,102 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper080 Taito X1-005 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper080 : Mapper + { + public Mapper080(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper080::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x7EF0: + SetVROM_2K_Bank(0, (data >> 1) & 0x3F); + if (PROM_8K_SIZE == 32) + { + if ((data & 0x80) != 0) + { + SetVRAM_1K_Bank(8, 1); + SetVRAM_1K_Bank(9, 1); + } + else + { + SetVRAM_1K_Bank(8, 0); + SetVRAM_1K_Bank(9, 0); + } + } + break; + + case 0x7EF1: + SetVROM_2K_Bank(2, (data >> 1) & 0x3F); + if (PROM_8K_SIZE == 32) + { + if ((data & 0x80) != 0) + { + SetVRAM_1K_Bank(10, 1); + SetVRAM_1K_Bank(11, 1); + } + else + { + SetVRAM_1K_Bank(10, 0); + SetVRAM_1K_Bank(11, 0); + } + } + break; + + case 0x7EF2: + SetVROM_1K_Bank(4, data); + break; + case 0x7EF3: + SetVROM_1K_Bank(5, data); + break; + case 0x7EF4: + SetVROM_1K_Bank(6, data); + break; + case 0x7EF5: + SetVROM_1K_Bank(7, data); + break; + + case 0x7EF6: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + break; + + case 0x7EFA: + case 0x7EFB: + SetPROM_8K_Bank(4, data); + break; + case 0x7EFC: + case 0x7EFD: + SetPROM_8K_Bank(5, data); + break; + case 0x7EFE: + case 0x7EFF: + SetPROM_8K_Bank(6, data); + break; + default: + base.WriteLow(addr, data); + break; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper082.cs b/Core/VirtualNes.Core/Mapper/Mapper082.cs new file mode 100644 index 0000000..5f563d4 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper082.cs @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper082 Taito C075 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper082 : Mapper + { + BYTE reg; + + public Mapper082(NES parent) : base(parent) + { + } + + public override void Reset() + + { + reg = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + SetVRAM_Mirror(VRAM_VMIRROR); + } + + //void Mapper082::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x7EF0: + if (reg != 0) + { + SetVROM_2K_Bank(4, data >> 1); + } + else + { + SetVROM_2K_Bank(0, data >> 1); + } + break; + + case 0x7EF1: + if (reg != 0) + { + SetVROM_2K_Bank(6, data >> 1); + } + else + { + SetVROM_2K_Bank(2, data >> 1); + } + break; + + case 0x7EF2: + if (reg != 0) SetVROM_1K_Bank(0, data); + else SetVROM_1K_Bank(4, data); + break; + case 0x7EF3: + if (reg != 0) SetVROM_1K_Bank(1, data); + else SetVROM_1K_Bank(5, data); + break; + case 0x7EF4: + if (reg != 0) SetVROM_1K_Bank(2, data); + else SetVROM_1K_Bank(6, data); + break; + case 0x7EF5: + if (reg != 0) SetVROM_1K_Bank(3, data); + else SetVROM_1K_Bank(7, data); + break; + + case 0x7EF6: + reg = (byte)(data & 0x02); + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + break; + + case 0x7EFA: + SetPROM_8K_Bank(4, data >> 2); + break; + case 0x7EFB: + SetPROM_8K_Bank(5, data >> 2); + break; + case 0x7EFC: + SetPROM_8K_Bank(6, data >> 2); + break; + default: + base.WriteLow(addr, data); + break; + } + } + + //void Mapper082::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + } + + //void Mapper082::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper083.cs b/Core/VirtualNes.Core/Mapper/Mapper083.cs new file mode 100644 index 0000000..e5083e1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper083.cs @@ -0,0 +1,247 @@ +///////////////////////////// +// Mapper083 Cony // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper083 : Mapper + { + BYTE[] reg = new byte[3]; + INT chr_bank; + BYTE irq_enable; + INT irq_counter; + + BYTE patch; + public Mapper083(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 3; i++) + { + reg[i] = 0x00; + } + + if (PROM_8K_SIZE >= 32) + { + SetPROM_32K_Bank(0, 1, 30, 31); + reg[1] = 0x30; + } + else + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + chr_bank = 0; + + irq_enable = 0; // Disable + irq_counter = 0; + + patch = 0; + if (nes.rom.GetPROM_CRC() == 0x1461D1F8) + { + patch = 1; + } + } + + //BYTE Mapper083::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if ((addr & 0x5100) == 0x5100) + { + return reg[2]; + } + else if (addr >= 0x6000) + { + return base.ReadLow(addr); + } + return (BYTE)(addr >> 8); + } + + //void Mapper083::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + switch (addr) + { + case 0x5101: + case 0x5102: + case 0x5103: + reg[2] = data; + break; + } + + if (addr >= 0x6000) + { + base.WriteLow(addr, data); + } + } + + //void Mapper083::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + switch (addr) + { + case 0x8000: + case 0xB000: + case 0xB0FF: + case 0xB1FF: + reg[0] = data; + chr_bank = (data & 0x30) << 4; + SetPROM_16K_Bank(4, data); + SetPROM_16K_Bank(6, (data & 0x30) | 0x0F); + break; + + case 0x8100: + reg[1] = (byte)(data & 0x80); + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0x8200: + irq_counter = (irq_counter & 0xFF00) | (INT)data; + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + case 0x8201: + irq_counter = (irq_counter & 0x00FF) | ((INT)data << 8); + irq_enable = reg[1]; + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + + case 0x8300: + SetPROM_8K_Bank(4, data); + break; + case 0x8301: + SetPROM_8K_Bank(5, data); + break; + case 0x8302: + SetPROM_8K_Bank(6, data); + break; + + case 0x8310: + if (patch != 0) + { + SetVROM_2K_Bank(0, chr_bank | data); + } + else + { + SetVROM_1K_Bank(0, chr_bank | data); + } + break; + case 0x8311: + if (patch != 0) + { + SetVROM_2K_Bank(2, chr_bank | data); + } + else + { + SetVROM_1K_Bank(1, chr_bank | data); + } + break; + case 0x8312: + SetVROM_1K_Bank(2, chr_bank | data); + break; + case 0x8313: + SetVROM_1K_Bank(3, chr_bank | data); + break; + case 0x8314: + SetVROM_1K_Bank(4, chr_bank | data); + break; + case 0x8315: + SetVROM_1K_Bank(5, chr_bank | data); + break; + case 0x8316: + if (patch != 0) + { + SetVROM_2K_Bank(4, chr_bank | data); + } + else + { + SetVROM_1K_Bank(6, chr_bank | data); + } + break; + case 0x8317: + if (patch != 0) + { + SetVROM_2K_Bank(6, chr_bank | data); + } + else + { + SetVROM_1K_Bank(7, chr_bank | data); + } + break; + + case 0x8318: + SetPROM_16K_Bank(4, (reg[0] & 0x30) | data); + break; + } + } + + //void Mapper083::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if (irq_counter <= 113) + { + // nes.cpu.IRQ(); + irq_enable = 0; + // nes.cpu.SetIRQ( IRQ_MAPPER ); + nes.cpu.SetIRQ(IRQ_TRIGGER); + } + else + { + irq_counter -= 113; + } + } + } + + //void Mapper083::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + //*(INT*)&p[3] = chr_bank; + BitConverter.GetBytes(chr_bank).CopyTo(p, 3); + p[7] = irq_enable; + //*(INT*)&p[8] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 8); + } + + //void Mapper083::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + //chr_bank = *(INT*)&p[3]; + chr_bank = BitConverter.ToInt32(p, 3); + irq_enable = p[7]; + //irq_counter = *(INT*)&p[8]; + irq_counter = BitConverter.ToInt32(p, 8); + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper085.cs b/Core/VirtualNes.Core/Mapper/Mapper085.cs new file mode 100644 index 0000000..9b4838d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper085.cs @@ -0,0 +1,240 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper085 Konami VRC7 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper085 : Mapper + { + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + INT irq_clock; + public Mapper085(NES parent) : base(parent) + { + } + + public override void Reset() + + { + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + else + { + SetCRAM_8K_Bank(0); + } + +#if FALSE//0 +// DWORD crc = nes.rom.GetPROM_CRC(); +// if( crc == 0x1aa0479c ) { // For Tiny Toon Adventures 2 - Montana Land he Youkoso(J) +// nes.SetRenderMethod( NES::PRE_RENDER ); +// } +// if( crc == 0x33ce3ff0 ) { // For Lagrange Point(J) +// nes.SetRenderMethod( NES::TILE_RENDER ); +// } +#endif + nes.apu.SelectExSound(2); + } + + //void Mapper085::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF038) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + case 0x8008: + case 0x8010: + SetPROM_8K_Bank(5, data); + break; + case 0x9000: + SetPROM_8K_Bank(6, data); + break; + + case 0x9010: + case 0x9030: + nes.apu.ExWrite(addr, data); + break; + + case 0xA000: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(0, data); + } + else + { + SetCRAM_1K_Bank(0, data); + } + break; + + case 0xA008: + case 0xA010: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(1, data); + } + else + { + SetCRAM_1K_Bank(1, data); + } + break; + + case 0xB000: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(2, data); + } + else + { + SetCRAM_1K_Bank(2, data); + } + break; + + case 0xB008: + case 0xB010: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(3, data); + } + else + { + SetCRAM_1K_Bank(3, data); + } + break; + + case 0xC000: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(4, data); + } + else + { + SetCRAM_1K_Bank(4, data); + } + break; + + case 0xC008: + case 0xC010: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(5, data); + } + else + { + SetCRAM_1K_Bank(5, data); + } + break; + + case 0xD000: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(6, data); + } + else + { + SetCRAM_1K_Bank(6, data); + } + break; + + case 0xD008: + case 0xD010: + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(7, data); + } + else + { + SetCRAM_1K_Bank(7, data); + } + break; + + case 0xE000: + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0xE008: + case 0xE010: + irq_latch = data; + break; + + case 0xF000: + irq_enable = (byte)(data & 0x03); + irq_counter = irq_latch; + irq_clock = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF008: + case 0xF010: + irq_enable = (byte)((irq_enable & 0x01) * 3); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper085::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + irq_clock += cycles * 4; + while (irq_clock >= 455) + { + irq_clock -= 455; + irq_counter++; + if (irq_counter == 0) + { + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + + //void Mapper085::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + p[2] = irq_latch; + //*((INT*)&p[4]) = irq_clock; + BitConverter.GetBytes(irq_clock).CopyTo(p, 4); + } + + //void Mapper085::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + irq_latch = p[2]; + //irq_clock = *((INT*)&p[4]); + irq_clock = BitConverter.ToInt32(p, 4); + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper086.cs b/Core/VirtualNes.Core/Mapper/Mapper086.cs new file mode 100644 index 0000000..267de95 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper086.cs @@ -0,0 +1,69 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper086 Jaleco Early Mapper #2 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper086 : Mapper + { + BYTE reg, cnt; + public Mapper086(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + SetVROM_8K_Bank(0); + reg = 0xFF; + cnt = 0; + } + + //void Mapper086::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + SetPROM_32K_Bank((data & 0x30) >> 4); + + SetVROM_8K_Bank((data & 0x03) | ((data & 0x40) >> 4)); + } + if (addr == 0x7000) + { + if ((reg & 0x10) == 0 && ((data & 0x10) != 0) && cnt == 0) + { + //DEBUGOUT( "WR:$%02X\n", data ); + if ((data & 0x0F) == 0 // Strike + || (data & 0x0F) == 5) + { // Foul + cnt = 60; // 次の発声を1秒程禁止する + } + + // OSDにするべきか… + if (Supporter.Config.sound.bExtraSoundEnable) + { + //TODO : 似乎VirtuaNES有直接播放某个音频文件的功能 + //DirectSound.EsfAllStop(); + //DirectSound.EsfPlay(ESF_MOEPRO_STRIKE + (data & 0x0F)); + } + } + reg = data; + } + } + + //void Mapper086::VSync() + public override void VSync() + { + if (cnt != 0) + { + cnt--; + } + } + + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper087.cs b/Core/VirtualNes.Core/Mapper/Mapper087.cs new file mode 100644 index 0000000..c9d85ac --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper087.cs @@ -0,0 +1,32 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper087 Konami 74161/32 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper087 : Mapper + { + public Mapper087(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + SetVROM_8K_Bank(0); + } + + //void Mapper087::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + SetVROM_8K_Bank((data & 0x02) >> 1); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper088.cs b/Core/VirtualNes.Core/Mapper/Mapper088.cs new file mode 100644 index 0000000..b45c00a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper088.cs @@ -0,0 +1,104 @@ +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper088 : Mapper + { + BYTE reg; + BYTE patch; + public Mapper088(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE >= 8) + { + SetVROM_8K_Bank(0); + } + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0xc1b6b2a6) + { // Devil Man(J) + patch = 1; + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0xd9803a35) + { // Quinty(J) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + } + + //void Mapper088::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + reg = data; + if (patch != 0) + { + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + break; + case 0x8001: + switch (reg & 0x07) + { + case 0: + SetVROM_2K_Bank(0, data >> 1); + break; + case 1: + SetVROM_2K_Bank(2, data >> 1); + break; + case 2: + SetVROM_1K_Bank(4, data + 0x40); + break; + case 3: + SetVROM_1K_Bank(5, data + 0x40); + break; + case 4: + SetVROM_1K_Bank(6, data + 0x40); + break; + case 5: + SetVROM_1K_Bank(7, data + 0x40); + break; + case 6: + SetPROM_8K_Bank(4, data); + break; + case 7: + SetPROM_8K_Bank(5, data); + break; + } + break; + case 0xC000: + if (data != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + break; + } + } + + //void Mapper088::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + } + + //void Mapper088::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper089.cs b/Core/VirtualNes.Core/Mapper/Mapper089.cs new file mode 100644 index 0000000..4af77b8 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper089.cs @@ -0,0 +1,37 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper089 SunSoft (水戸黄門) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper089 : Mapper + { + public Mapper089(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + } + + //void Mapper089::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0xFF00) == 0xC000) + { + SetPROM_16K_Bank(4, (data & 0x70) >> 4); + + SetVROM_8K_Bank(((data & 0x80) >> 4) | (data & 0x07)); + + if ((data & 0x08) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper090.cs b/Core/VirtualNes.Core/Mapper/Mapper090.cs new file mode 100644 index 0000000..9334113 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper090.cs @@ -0,0 +1,481 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper090 PC-JY-?? // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper090 : Mapper + { + BYTE patch; + + BYTE[] prg_reg = new byte[4]; + BYTE[] nth_reg = new byte[4]; + BYTE[] ntl_reg = new byte[4]; + BYTE[] chh_reg = new byte[8]; + BYTE[] chl_reg = new byte[8]; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_occur; + BYTE irq_preset; + BYTE irq_offset; + + BYTE prg_6000, prg_E000; + BYTE prg_size, chr_size; + BYTE mir_mode, mir_type; + + BYTE key_val; + BYTE mul_val1, mul_val2; + BYTE sw_val; + public Mapper090(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x2a268152) + { + patch = 1; + } + if (crc == 0x2224b882) + { + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_occur = 0; + irq_preset = 0; + irq_offset = 0; + + prg_6000 = 0; + prg_E000 = 0; + prg_size = 0; + chr_size = 0; + mir_mode = 0; + mir_type = 0; + + key_val = 0; + mul_val1 = mul_val2 = 0; + + for (byte i = 0; i < 4; i++) + { + prg_reg[i] = (byte)(PROM_8K_SIZE - 4 + i); + ntl_reg[i] = 0; + nth_reg[i] = 0; + chl_reg[i] = i; + chh_reg[i] = 0; + chl_reg[i + 4] = (byte)(i + 4); + chh_reg[i + 4] = 0; + } + + if (sw_val != 0) + sw_val = 0x00; + else + sw_val = 0xFF; + + // nes.SetRenderMethod( NES::PRE_ALL_RENDER ); + } + + //BYTE Mapper090::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + Debuger.Log($"RD:{addr:X4}"); + + switch (addr) + { + case 0x5000: + return (byte)((sw_val != 0) ? 0x00 : 0xFF); + case 0x5800: + return (BYTE)(mul_val1 * mul_val2); + case 0x5801: + return (BYTE)((mul_val1 * mul_val2) >> 8); + case 0x5803: + return key_val; + } + + if (addr >= 0x6000) + { + return base.ReadLow(addr); + } + + // return sw_val?0x00:0xFF; + return (BYTE)(addr >> 8); + } + + //void Mapper090::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + Debuger.Log($"WR:{addr:X4} {data:X2}"); + + if (addr == 0x5800) + { + mul_val1 = data; + } + else + if (addr == 0x5801) + { + mul_val2 = data; + } + else + if (addr == 0x5803) + { + key_val = data; + } + } + + //void Mapper090::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF007) + { + case 0x8000: + case 0x8001: + case 0x8002: + case 0x8003: + prg_reg[addr & 3] = data; + SetBank_CPU(); + break; + + case 0x9000: + case 0x9001: + case 0x9002: + case 0x9003: + case 0x9004: + case 0x9005: + case 0x9006: + case 0x9007: + chl_reg[addr & 7] = data; + SetBank_PPU(); + break; + + case 0xA000: + case 0xA001: + case 0xA002: + case 0xA003: + case 0xA004: + case 0xA005: + case 0xA006: + case 0xA007: + chh_reg[addr & 7] = data; + SetBank_PPU(); + break; + + case 0xB000: + case 0xB001: + case 0xB002: + case 0xB003: + ntl_reg[addr & 3] = data; + SetBank_VRAM(); + break; + + case 0xB004: + case 0xB005: + case 0xB006: + case 0xB007: + nth_reg[addr & 3] = data; + SetBank_VRAM(); + break; + + case 0xC002: + irq_enable = 0; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC003: + irq_enable = 0xFF; + irq_preset = 0xFF; + break; + case 0xC004: + break; + case 0xC005: + if ((irq_offset & 0x80) != 0) + { + irq_latch = (byte)(data ^ (irq_offset | 1)); + } + else + { + irq_latch = (byte)(data | (irq_offset & 0x27)); + } + irq_preset = 0xFF; + break; + case 0xC006: + if (patch != 0) + { + irq_offset = data; + } + break; + + case 0xD000: + prg_6000 = (byte)(data & 0x80); + prg_E000 = (byte)(data & 0x04); + prg_size = (byte)(data & 0x03); + chr_size = (byte)((data & 0x18) >> 3); + mir_mode = (byte)(data & 0x20); + SetBank_CPU(); + SetBank_PPU(); + SetBank_VRAM(); + break; + + case 0xD001: + mir_type = (byte)(data & 0x03); + SetBank_VRAM(); + break; + + case 0xD003: + break; + } + } + + //void Mapper090::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_preset != 0) + { + irq_counter = irq_latch; + irq_preset = 0; + } + if (irq_counter != 0) + { + irq_counter--; + } + if (irq_counter == 0) + { + if (irq_enable != 0) + { + // irq_occur = 0xFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + //void Mapper090::Clock(INT cycles) + public override void Clock(int cycles) + { + // if( irq_occur ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + void SetBank_CPU() + { + if (prg_size == 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + else + if (prg_size == 1) + { + SetPROM_32K_Bank(prg_reg[1] * 2, prg_reg[1] * 2 + 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + else + if (prg_size == 2) + { + if (prg_E000 != 0) + { + SetPROM_32K_Bank(prg_reg[0], prg_reg[1], prg_reg[2], prg_reg[3]); + } + else + { + if (prg_6000 != 0) + { + SetPROM_8K_Bank(3, prg_reg[3]); + } + SetPROM_32K_Bank(prg_reg[0], prg_reg[1], prg_reg[2], PROM_8K_SIZE - 1); + } + } + else + { + SetPROM_32K_Bank(prg_reg[3], prg_reg[2], prg_reg[1], prg_reg[0]); + } + } + + void SetBank_PPU() + { + INT[] bank = new int[8]; + + for (INT i = 0; i < 8; i++) + { + bank[i] = ((INT)chh_reg[i] << 8) | ((INT)chl_reg[i]); + } + + if (chr_size == 0) + { + SetVROM_8K_Bank(bank[0]); + } + else + if (chr_size == 1) + { + SetVROM_4K_Bank(0, bank[0]); + SetVROM_4K_Bank(4, bank[4]); + } + else + if (chr_size == 2) + { + SetVROM_2K_Bank(0, bank[0]); + SetVROM_2K_Bank(2, bank[2]); + SetVROM_2K_Bank(4, bank[4]); + SetVROM_2K_Bank(6, bank[6]); + } + else + { + SetVROM_8K_Bank(bank[0], bank[1], bank[2], bank[3], bank[4], bank[5], bank[6], bank[7]); + } + } + + void SetBank_VRAM() + { + INT[] bank = new int[4]; + + for (INT i = 0; i < 4; i++) + { + bank[i] = ((INT)nth_reg[i] << 8) | ((INT)ntl_reg[i]); + } + + if (patch == 0 && mir_mode != 0) + { + for (INT i = 0; i < 4; i++) + { + if (nth_reg[i] == 0 && (ntl_reg[i] == (BYTE)i)) + { + mir_mode = 0; + } + } + + if (mir_mode != 0) + { + SetVROM_1K_Bank(8, bank[0]); + SetVROM_1K_Bank(9, bank[1]); + SetVROM_1K_Bank(10, bank[2]); + SetVROM_1K_Bank(11, bank[3]); + } + } + else + { + if (mir_type == 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + if (mir_type == 1) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + } + + //void Mapper090::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + INT i; + + for (i = 0; i < 4; i++) + { + p[i] = prg_reg[i]; + } + for (i = 0; i < 8; i++) + { + p[i + 4] = chh_reg[i]; + } + for (i = 0; i < 8; i++) + { + p[i + 12] = chl_reg[i]; + } + for (i = 0; i < 4; i++) + { + p[i + 20] = nth_reg[i]; + } + for (i = 0; i < 4; i++) + { + p[i + 24] = ntl_reg[i]; + } + p[28] = irq_enable; + p[29] = irq_counter; + p[30] = irq_latch; + p[31] = prg_6000; + p[32] = prg_E000; + p[33] = prg_size; + p[34] = chr_size; + p[35] = mir_mode; + p[36] = mir_type; + p[37] = mul_val1; + p[38] = mul_val2; + p[39] = key_val; + p[40] = irq_occur; + p[41] = irq_preset; + p[42] = irq_offset; + } + + //void Mapper090::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + INT i; + + for (i = 0; i < 4; i++) + { + prg_reg[i] = p[i]; + } + for (i = 0; i < 8; i++) + { + chh_reg[i] = p[i + 4]; + } + for (i = 0; i < 8; i++) + { + chl_reg[i] = p[i + 12]; + } + for (i = 0; i < 4; i++) + { + nth_reg[i] = p[i + 20]; + } + for (i = 0; i < 4; i++) + { + ntl_reg[i] = p[i + 24]; + } + irq_enable = p[28]; + irq_counter = p[29]; + irq_latch = p[30]; + prg_6000 = p[31]; + prg_E000 = p[32]; + prg_size = p[33]; + chr_size = p[34]; + mir_mode = p[35]; + mir_type = p[36]; + mul_val1 = p[37]; + mul_val2 = p[38]; + key_val = p[39]; + irq_occur = p[40]; + irq_preset = p[41]; + irq_offset = p[42]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper091.cs b/Core/VirtualNes.Core/Mapper/Mapper091.cs new file mode 100644 index 0000000..f307104 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper091.cs @@ -0,0 +1,102 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper091 PC-HK-SF3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper091 : Mapper + { + BYTE irq_enable; + BYTE irq_counter; + public Mapper091(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, PROM_8K_SIZE - 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0, 0, 0, 0, 0, 0, 0, 0); + } + + irq_enable = 0; + irq_counter = 0; + + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + //void Mapper091::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + //DEBUGOUT( "$%04X:$%02X(%3d) L=%3d\n", addr, data, data, nes.GetScanline() ); + switch (addr & 0xF003) + { + case 0x6000: + case 0x6001: + case 0x6002: + case 0x6003: + SetVROM_2K_Bank((byte)((addr & 0x03) * 2), data); + break; + + case 0x7000: + SetPROM_8K_Bank(4, data); + break; + case 0x7001: + SetPROM_8K_Bank(5, data); + break; + + case 0x7002: + irq_enable = 0; + irq_counter = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0x7003: + irq_enable = 1; + break; + } + } + + //void Mapper091::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline < 240) && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + irq_counter++; + } + if (irq_counter >= 8) + { + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + + //void Mapper091::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + } + + //void Mapper091::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper092.cs b/Core/VirtualNes.Core/Mapper/Mapper092.cs new file mode 100644 index 0000000..5b8a774 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper092.cs @@ -0,0 +1,72 @@ +///////////////////////////////// +// Mapper092 Jaleco/Type1 Higher bank switch // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper092 : Mapper + { + public Mapper092(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper092::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "A:%04X D:%02X\n", addr, data ); + + data = (byte)(addr & 0xFF); + + if (addr >= 0x9000) + { + if ((data & 0xF0) == 0xD0) + { + SetPROM_16K_Bank(6, data & 0x0F); + } + else if ((data & 0xF0) == 0xE0) + { + SetVROM_8K_Bank(data & 0x0F); + } + } + else + { + if ((data & 0xF0) == 0xB0) + { + SetPROM_16K_Bank(6, data & 0x0F); + } + else if ((data & 0xF0) == 0x70) + { + SetVROM_8K_Bank(data & 0x0F); + } + else if ((data & 0xF0) == 0xC0) + { + INT[] tbl = new int[]{ 3, 4, 5, 6, 0, 1, 2, 7, + 9,10, 8,11,13,12,14,15 }; + + // OSDにするべきか… + if (Supporter.Config.sound.bExtraSoundEnable) + { + //TODO : 似乎VirtuaNES有直接播放某个音频文件的功能 + Debuger.Log($"CODE {data:X2}"); + //DirectSound.EsfAllStop(); + //DirectSound.EsfPlay(ESF_MOEPRO_STRIKE + tbl[data & 0x0F]); + } + } + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper093.cs b/Core/VirtualNes.Core/Mapper/Mapper093.cs new file mode 100644 index 0000000..3cef826 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper093.cs @@ -0,0 +1,34 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper093 SunSoft (Fantasy Zone) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper093 : Mapper + { + public Mapper093(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper093::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + SetPROM_16K_Bank(4, data); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper094.cs b/Core/VirtualNes.Core/Mapper/Mapper094.cs new file mode 100644 index 0000000..e954b1d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper094.cs @@ -0,0 +1,31 @@ +///////////////////////////////// +// Mapper094 Capcom 74161/32 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper094 : Mapper + { + public Mapper094(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper094::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0xFFF0) == 0xFF00) + { + SetPROM_16K_Bank(4, (data >> 2) & 0x7); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper095.cs b/Core/VirtualNes.Core/Mapper/Mapper095.cs new file mode 100644 index 0000000..c3fac90 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper095.cs @@ -0,0 +1,183 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper095 Namcot 106M (Dragon Buster) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper095 : Mapper + { + BYTE reg; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + public Mapper095(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg = 0x00; + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + if (VROM_1K_SIZE != 0) + { + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + } + else + { + chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0; + } + + SetBank_PPU(); + + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + + //void Mapper095::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + if (reg <= 0x05) + { + if ((data & 0x20) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + data &= 0x1F; + } + + switch (reg & 0x07) + { + case 0x00: + if (VROM_1K_SIZE != 0) + { + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x01: + if (VROM_1K_SIZE != 0) + { + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x02: + if (VROM_1K_SIZE != 0) + { + chr4 = data; + SetBank_PPU(); + } + break; + case 0x03: + if (VROM_1K_SIZE != 0) + { + chr5 = data; + SetBank_PPU(); + } + break; + case 0x04: + if (VROM_1K_SIZE != 0) + { + chr6 = data; + SetBank_PPU(); + } + break; + case 0x05: + if (VROM_1K_SIZE != 0) + { + chr7 = data; + SetBank_PPU(); + } + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + } + } + + void SetBank_CPU() + { + if ((reg & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + } + + //void Mapper095::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + p[1] = prg0; + p[2] = prg1; + p[3] = chr01; + p[4] = chr23; + p[5] = chr4; + p[6] = chr5; + p[7] = chr6; + p[8] = chr7; + } + + //void Mapper095::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + prg0 = p[1]; + prg1 = p[2]; + chr01 = p[3]; + chr23 = p[4]; + chr4 = p[5]; + chr5 = p[6]; + chr6 = p[7]; + chr7 = p[8]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper096.cs b/Core/VirtualNes.Core/Mapper/Mapper096.cs new file mode 100644 index 0000000..b297f59 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper096.cs @@ -0,0 +1,72 @@ +//////////////////////////////// +// Mapper096 Bandai 74161 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper096 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper096(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg[0] = reg[1] = 0; + + SetPROM_32K_Bank(0, 1, 2, 3); + SetBank(); + + SetVRAM_Mirror(VRAM_MIRROR4L); + } + + //void Mapper096::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank(data & 0x03); + + reg[0] = (byte)((data & 0x04) >> 2); + SetBank(); + } + + public override void PPU_Latch(ushort addr) + { + if ((addr & 0xF000) == 0x2000) + { + reg[1] = (byte)((addr >> 8) & 0x03); + SetBank(); + } + } + + void SetBank() + { + SetCRAM_4K_Bank(0, reg[0] * 4 + reg[1]); + SetCRAM_4K_Bank(4, reg[0] * 4 + 0x03); + } + + //void Mapper096::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper096::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper097.cs b/Core/VirtualNes.Core/Mapper/Mapper097.cs new file mode 100644 index 0000000..13258c0 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper097.cs @@ -0,0 +1,39 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper097 Irem 74161 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper097 : Mapper + { + public Mapper097(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, PROM_8K_SIZE - 1, 0, 1); + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper097::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr < 0xC000) + { + SetPROM_16K_Bank(6, data & 0x0F); + + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_VMIRROR); + else SetVRAM_Mirror(VRAM_HMIRROR); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper099.cs b/Core/VirtualNes.Core/Mapper/Mapper099.cs new file mode 100644 index 0000000..8cc19d1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper099.cs @@ -0,0 +1,89 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper099 VS-Unisystem // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper099 : Mapper + { + BYTE coin; + public Mapper099(NES parent) : base(parent) + { + } + + public override void Reset() + { + // set CPU bank pointers + if (PROM_8K_SIZE > 2) + { + SetPROM_32K_Bank(0, 1, 2, 3); + } + else if (PROM_8K_SIZE > 1) + { + SetPROM_32K_Bank(0, 1, 0, 1); + } + else + { + SetPROM_32K_Bank(0, 0, 0, 0); + } + + // set VROM bank + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + coin = 0; + } + + //BYTE Mapper099::ExRead(WORD addr) + public override byte ExRead(ushort addr) + { + if (addr == 0x4020) + { + return coin; + } + + return (byte)(addr >> 8); + } + + //void Mapper099::ExWrite(WORD addr, BYTE data) + public override void ExWrite(ushort addr, byte data) + { + if (addr == 0x4016) + { + if ((data & 0x04) != 0) + { + SetVROM_8K_Bank(1); + } + else + { + SetVROM_8K_Bank(0); + } + + if (nes.rom.GetPROM_CRC() == 0xC99EC059) + { // VS Raid on Bungeling Bay(J) + if ((data & 0x02) != 0) + { + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + } + } + + if (addr == 0x4020) + { + coin = data; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper100.cs b/Core/VirtualNes.Core/Mapper/Mapper100.cs new file mode 100644 index 0000000..d7e17b4 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper100.cs @@ -0,0 +1,300 @@ +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper100 : Mapper + { + + BYTE[] reg = new byte[8]; + BYTE prg0, prg1, prg2, prg3; + BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper100(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + prg2 = (byte)(PROM_8K_SIZE - 2); + prg3 = (byte)(PROM_8K_SIZE - 1); + SetBank_CPU(); + + if (VROM_1K_SIZE != 0) + { + chr0 = 0; + chr1 = 1; + chr2 = 2; + chr3 = 3; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + } + else + { + chr0 = chr2 = chr4 = chr5 = chr6 = chr7 = 0; + chr1 = chr3 = 1; + } + + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper100::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0xC7) + { + case 0x00: + if (VROM_1K_SIZE != 0) + { + chr0 = (byte)(data & 0xFE); + chr1 = (byte)(chr0 + 1); + SetBank_PPU(); + } + break; + case 0x01: + if (VROM_1K_SIZE != 0) + { + chr2 = (byte)(data & 0xFE); + chr3 = (byte)(chr2 + 1); + SetBank_PPU(); + } + break; + case 0x02: + if (VROM_1K_SIZE != 0) + { + chr4 = data; + SetBank_PPU(); + } + break; + case 0x03: + if (VROM_1K_SIZE != 0) + { + chr5 = data; + SetBank_PPU(); + } + break; + case 0x04: + if (VROM_1K_SIZE != 0) + { + chr6 = data; + SetBank_PPU(); + } + break; + case 0x05: + if (VROM_1K_SIZE != 0) + { + chr7 = data; + SetBank_PPU(); + } + break; + + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + case 0x46: + prg2 = data; + SetBank_CPU(); + break; + case 0x47: + prg3 = data; + SetBank_CPU(); + break; + + case 0x80: + if (VROM_1K_SIZE != 0) + { + chr4 = (byte)(data & 0xFE); + chr5 = (byte)(chr4 + 1); + SetBank_PPU(); + } + break; + case 0x81: + if (VROM_1K_SIZE != 0) + { + chr6 = (byte)(data & 0xFE); + chr7 = (byte)(chr6 + 1); + SetBank_PPU(); + } + break; + case 0x82: + if (VROM_1K_SIZE != 0) + { + chr0 = data; + SetBank_PPU(); + } + break; + case 0x83: + if (VROM_1K_SIZE != 0) + { + chr1 = data; + SetBank_PPU(); + } + break; + case 0x84: + if (VROM_1K_SIZE != 0) + { + chr2 = data; + SetBank_PPU(); + } + break; + case 0x85: + if (VROM_1K_SIZE != 0) + { + chr3 = data; + SetBank_PPU(); + } + break; + + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 0xFF; + break; + } + } + + //void Mapper100::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg0, prg1, prg2, prg3); + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7); + } + } + + //void Mapper100::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = prg2; + p[11] = prg3; + p[12] = chr0; + p[13] = chr1; + p[14] = chr2; + p[15] = chr3; + p[16] = chr4; + p[17] = chr5; + p[18] = chr6; + p[19] = chr7; + p[20] = irq_enable; + p[21] = irq_counter; + p[22] = irq_latch; + } + + //void Mapper100::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + prg2 = p[10]; + prg3 = p[11]; + chr0 = p[12]; + chr1 = p[13]; + chr2 = p[14]; + chr3 = p[15]; + chr4 = p[16]; + chr5 = p[17]; + chr6 = p[18]; + chr7 = p[19]; + irq_enable = p[20]; + irq_counter = p[21]; + irq_latch = p[22]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper101.cs b/Core/VirtualNes.Core/Mapper/Mapper101.cs new file mode 100644 index 0000000..9dba466 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper101.cs @@ -0,0 +1,38 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper101 Jaleco(Urusei Yatsura) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper101 : Mapper + { + public Mapper101(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + SetVROM_8K_Bank(0); + } + + //void Mapper101::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000) + { + SetVROM_8K_Bank(data & 0x03); + } + } + + //void Mapper101::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetVROM_8K_Bank(data & 0x03); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper105.cs b/Core/VirtualNes.Core/Mapper/Mapper105.cs new file mode 100644 index 0000000..4522a0c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper105.cs @@ -0,0 +1,204 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper105 Nintendo World Championship // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper105 : Mapper + { + BYTE init_state; + BYTE write_count; + BYTE bits; + BYTE[] reg = new byte[4]; + + BYTE irq_enable; + INT irq_counter; + public Mapper105(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + reg[0] = 0x0C; + reg[1] = 0x00; + reg[2] = 0x00; + reg[3] = 0x10; + + bits = 0; + write_count = 0; + + irq_counter = 0; + irq_enable = 0; + init_state = 0; + } + + //void Mapper105::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //WORD reg_num = (addr & 0x7FFF) >> 13; + uint reg_num = (byte)((addr & 0x7FFF) >> 13); + + if ((data & 0x80) != 0) + { + bits = write_count = 0; + if (reg_num == 0) + { + reg[reg_num] |= 0x0C; + } + } + else + { + //bits |= (data & 1) << write_count++; + bits |= (byte)((data & 1) << write_count++); + if (write_count == 5) + { + reg[reg_num] = (byte)(bits & 0x1F); + bits = write_count = 0; + } + } + + if ((reg[0] & 0x02) != 0) + { + if ((reg[0] & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + else + { + if ((reg[0] & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_MIRROR4H); + } + else + { + SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + + switch (init_state) + { + case 0: + case 1: + init_state++; + break; + case 2: + if ((reg[1] & 0x08) != 0) + { + if ((reg[0] & 0x08) != 0) + { + if ((reg[0] & 0x04) != 0) + { + SetPROM_8K_Bank(4, ((reg[3] & 0x07) * 2 + 16)); + SetPROM_8K_Bank(5, ((reg[3] & 0x07) * 2 + 17)); + SetPROM_8K_Bank(6, 30); + SetPROM_8K_Bank(7, 31); + } + else + { + SetPROM_8K_Bank(4, 16); + SetPROM_8K_Bank(5, 17); + SetPROM_8K_Bank(6, ((reg[3] & 0x07) * 2 + 16)); + SetPROM_8K_Bank(7, ((reg[3] & 0x07) * 2 + 17)); + } + } + else + { + SetPROM_8K_Bank(4, ((reg[3] & 0x06) * 2 + 16)); + SetPROM_8K_Bank(5, ((reg[3] & 0x06) * 2 + 17)); + SetPROM_8K_Bank(6, ((reg[3] & 0x06) * 2 + 18)); + SetPROM_8K_Bank(7, ((reg[3] & 0x06) * 2 + 19)); + } + } + else + { + SetPROM_8K_Bank(4, ((reg[1] & 0x06) * 2 + 0)); + SetPROM_8K_Bank(5, ((reg[1] & 0x06) * 2 + 1)); + SetPROM_8K_Bank(6, ((reg[1] & 0x06) * 2 + 2)); + SetPROM_8K_Bank(7, ((reg[1] & 0x06) * 2 + 3)); + } + + if ((reg[1] & 0x10) != 0) + { + irq_counter = 0; + irq_enable = 0; + } + else + { + irq_enable = 1; + } + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + default: + break; + } + } + + //void Mapper105::HSync(INT scanline) + public override void HSync(int scanline) + { + if (scanline != 0) + { + if (irq_enable != 0) + { + irq_counter += 29781; + } + if (((irq_counter | 0x21FFFFFF) & 0x3E000000) == 0x3E000000) + { + // nes.cpu.IRQ_NotPending(); + // nes.cpu.SetIRQ( IRQ_MAPPER ); + nes.cpu.SetIRQ(IRQ_TRIGGER2); + } + } + } + + //void Mapper105::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 4; i++) + { + p[i] = reg[i]; + } + p[8] = init_state; + p[9] = write_count; + p[10] = bits; + p[11] = irq_enable; + //*((INT*)&p[12]) = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 12); + } + + //void Mapper105::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 4; i++) + { + reg[i] = p[i]; + } + init_state = p[8]; + write_count = p[9]; + bits = p[10]; + irq_enable = p[11]; + //irq_counter = *((INT*)&p[12]); + irq_counter = BitConverter.ToInt32(p, 12); + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper107.cs b/Core/VirtualNes.Core/Mapper/Mapper107.cs new file mode 100644 index 0000000..f153449 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper107.cs @@ -0,0 +1,29 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper107 Magic Dragon Mapper // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + +namespace VirtualNes.Core +{ + public class Mapper107 : Mapper + { + public Mapper107(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + } + + //void Mapper107::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_32K_Bank((data >> 1) & 0x03); + SetVROM_8K_Bank(data & 0x07); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper108.cs b/Core/VirtualNes.Core/Mapper/Mapper108.cs new file mode 100644 index 0000000..11afe6e --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper108.cs @@ -0,0 +1,27 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper108 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + +namespace VirtualNes.Core +{ + public class Mapper108 : Mapper + { + public Mapper108(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0xC, 0xD, 0xE, 0xF); + SetPROM_8K_Bank(3, 0); + } + + //void Mapper108::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_8K_Bank(3, data); + } + + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Mapper/Mapper109.cs b/Core/VirtualNes.Core/Mapper/Mapper109.cs new file mode 100644 index 0000000..43101e9 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper109.cs @@ -0,0 +1,128 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper109 SACHEN The Great Wall SA-019 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper109 : Mapper + { + + BYTE reg; + BYTE chr0, chr1, chr2, chr3; + BYTE chrmode0, chrmode1; + public Mapper109(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg = 0; + SetPROM_32K_Bank(0); + + chr0 = 0; + chr1 = 0; + chr2 = 0; + chr3 = 0; + SetBank_PPU(); + chrmode0 = 0; + chrmode1 = 0; + } + + //void Mapper109::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x4100: + reg = data; + break; + case 0x4101: + switch (reg) + { + case 0: + chr0 = data; + SetBank_PPU(); + break; + case 1: + chr1 = data; + SetBank_PPU(); + break; + case 2: + chr2 = data; + SetBank_PPU(); + break; + case 3: + chr3 = data; + SetBank_PPU(); + break; + case 4: + chrmode0 = (byte)(data & 0x01); + SetBank_PPU(); + break; + case 5: + SetPROM_32K_Bank(data & 0x07); + break; + case 6: + chrmode1 = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 7: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + break; + } + + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(0, chr0); + SetVROM_1K_Bank(1, chr1 | ((chrmode1 << 3) & 0x8)); + SetVROM_1K_Bank(2, chr2 | ((chrmode1 << 2) & 0x8)); + SetVROM_1K_Bank(3, chr3 | ((chrmode1 << 1) & 0x8) | (chrmode0 * 0x10)); + SetVROM_1K_Bank(4, VROM_1K_SIZE - 4); + SetVROM_1K_Bank(5, VROM_1K_SIZE - 3); + SetVROM_1K_Bank(6, VROM_1K_SIZE - 2); + SetVROM_1K_Bank(7, VROM_1K_SIZE - 1); + } + } + + //void Mapper109::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + p[1] = chr0; + p[2] = chr1; + p[3] = chr2; + p[4] = chr3; + p[5] = chrmode0; + p[6] = chrmode1; + } + + //void Mapper109::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + chr0 = p[1]; + chr1 = p[2]; + chr2 = p[3]; + chr3 = p[4]; + chrmode0 = p[5]; + chrmode1 = p[6]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper110.cs b/Core/VirtualNes.Core/Mapper/Mapper110.cs new file mode 100644 index 0000000..02a443e --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper110.cs @@ -0,0 +1,84 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper110 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper110 : Mapper + { + BYTE reg0, reg1; + public Mapper110(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + + reg0 = 0; + reg1 = 0; + } + //void Mapper110::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x4100: + reg1 = (byte)(data & 0x07); + break; + case 0x4101: + switch (reg1) + { + case 5: + SetPROM_32K_Bank(data); + break; + case 0: + reg0 = (byte)(data & 0x01); + SetVROM_8K_Bank(reg0); + break; + case 2: + reg0 = data; + SetVROM_8K_Bank(reg0); + break; + case 4: + reg0 = (byte)(reg0 | (data << 1)); + SetVROM_8K_Bank(reg0); + break; + case 6: + reg0 = (byte)(reg0 | (data << 2)); + SetVROM_8K_Bank(reg0); + break; + default: + break; + } + break; + default: + break; + } + } + + //void Mapper110::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg0; + p[1] = reg1; + } + + //void Mapper110::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg0 = p[0]; + reg1 = p[1]; + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper111.cs b/Core/VirtualNes.Core/Mapper/Mapper111.cs new file mode 100644 index 0000000..7317bc1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper111.cs @@ -0,0 +1,289 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper111 Nintendo MMC1 Hack // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper111 : Mapper + { + + ushort last_addr; + + BYTE patch; + BYTE wram_patch; + BYTE wram_bank; + BYTE wram_count; + + BYTE[] reg = new byte[4]; + BYTE shift, regbuf; + + public Mapper111(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg[0] = 0x0C; // D3=1,D2=1 + reg[1] = reg[2] = reg[3] = 0; + shift = regbuf = 0; + + patch = 0; + wram_patch = 0; + + if (PROM_16K_SIZE < 32) + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + else + { + // For 512K/1M byte Cartridge + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 16 - 1); + + patch = 1; + } + + if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + // Ninja Ryukenden(J) + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + //void Mapper111::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF ); + + if ((data & 0x80) != 0) + { + shift = regbuf = 0; + reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない + return; + } + + addr = (ushort)((addr & 0x7FFF) >> 13); + reg[addr] = data; + + // DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF ); + + if (patch != 1) + { + // For Normal Cartridge + switch (addr) + { + case 0: + if ((reg[0] & 0x02) != 0) + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + break; + case 1: + // Register #1 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(0, reg[1]); + } + } + break; + case 2: + // Register #2 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(4, reg[2]); + } + } + break; + case 3: + if ((reg[0] & 0x08) == 0) + { + // PRG 32K bank ($8000-$FFFF) + SetPROM_32K_Bank(reg[3] >> 1); + } + else + { + if ((reg[0] & 0x04) != 0) + { + // PRG 16K bank ($8000-$BFFF) + SetPROM_16K_Bank(4, reg[3]); + SetPROM_16K_Bank(6, PROM_16K_SIZE - 1); + } + else + { + // PRG 16K bank ($C000-$FFFF) + SetPROM_16K_Bank(6, reg[3]); + SetPROM_16K_Bank(4, 0); + } + } + break; + } + } + else + { + // For 512K/1M byte Cartridge + INT PROM_BASE = 0; + if (PROM_16K_SIZE >= 32) + { + PROM_BASE = reg[1] & 0x10; + } + + // Register #0 + if (addr == 0) + { + if ((reg[0] & 0x02) != 0) + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H); + else SetVRAM_Mirror(VRAM_MIRROR4L); + } + } + // Register #1 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank lower($0000-$0FFF) + SetVROM_4K_Bank(0, reg[1]); + } + else + { + // CHR 8K bank($0000-$1FFF) + SetVROM_8K_Bank(reg[1] >> 1); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(0, reg[1]); + } + } + // Register #2 + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x10) != 0) + { + // CHR 4K bank higher($1000-$1FFF) + SetVROM_4K_Bank(4, reg[2]); + } + } + else + { + // For Romancia + if ((reg[0] & 0x10) != 0) + { + SetCRAM_4K_Bank(4, reg[2]); + } + } + // Register #3 + if ((reg[0] & 0x08) == 0) + { + // PRG 32K bank ($8000-$FFFF) + SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1); + } + else + { + if ((reg[0] & 0x04) != 0) + { + // PRG 16K bank ($8000-$BFFF) + SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F)); + if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1); + } + else + { + // PRG 16K bank ($C000-$FFFF) + SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F)); + if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE); + } + } + } + } + + //void Mapper111::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + p[4] = shift; + p[5] = regbuf; + + p[6] = wram_bank; + p[7] = wram_count; + } + + //void Mapper111::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + shift = p[4]; + regbuf = p[5]; + + wram_bank = p[6]; + wram_count = p[7]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper112.cs b/Core/VirtualNes.Core/Mapper/Mapper112.cs new file mode 100644 index 0000000..304b37a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper112.cs @@ -0,0 +1,165 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper112 Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper112 : Mapper + { + BYTE[] reg = new byte[4]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + public Mapper112(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 4; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + } + + //void Mapper112::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0xA000: + reg[1] = data; + switch (reg[0] & 0x07) + { + case 0x00: + prg0 = (byte)((data & (PROM_8K_SIZE - 1))); + SetBank_CPU(); + break; + case 0x01: + prg1 = (byte)((data & (PROM_8K_SIZE - 1))); + SetBank_CPU(); + break; + case 0x02: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x03: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x04: + chr4 = data; + SetBank_PPU(); + break; + case 0x05: + chr5 = data; + SetBank_PPU(); + break; + case 0x06: + chr6 = data; + SetBank_PPU(); + break; + case 0x07: + chr7 = data; + SetBank_PPU(); + break; + } + break; + + case 0xC000: + reg[3] = data; + SetBank_PPU(); + break;//hum 源码居然没有break 语法差异呗 + case 0xE000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + SetBank_PPU(); + break; + } + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + void SetBank_PPU() + { + if ((reg[2] & 0x02) != 0) + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, chr4, chr5, chr6, chr7); + } + else + { + SetVROM_8K_Bank(((reg[3] << 6) & 0x100) + chr01, + ((reg[3] << 6) & 0x100) + chr01 + 1, + ((reg[3] << 5) & 0x100) + chr23, + ((reg[3] << 5) & 0x100) + chr23 + 1, + ((reg[3] << 4) & 0x100) + chr4, + ((reg[3] << 3) & 0x100) + chr5, + ((reg[3] << 2) & 0x100) + chr6, + ((reg[3] << 1) & 0x100) + chr7); + } + } + + //void Mapper112::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 4; i++) + { + p[i] = reg[i]; + } + p[4] = chr01; + p[5] = chr23; + p[6] = chr4; + p[7] = chr5; + p[8] = chr6; + p[9] = chr7; + } + + //void Mapper112::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (byte i = 0; i < 4; i++) + { + reg[i] = p[i]; + } + + chr01 = p[4]; + chr23 = p[5]; + chr4 = p[6]; + chr5 = p[7]; + chr6 = p[8]; + chr7 = p[9]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper113.cs b/Core/VirtualNes.Core/Mapper/Mapper113.cs new file mode 100644 index 0000000..f4f7796 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper113.cs @@ -0,0 +1,75 @@ +//////////////////////////// +// Mapper113 PC-Sachen/Hacker // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper113 : Mapper + { + public Mapper113(NES parent) : base(parent) + { + } + + public override void Reset() + { + // SetPROM_32K_Bank( 0, 1, PROM_8K_SIZE-2, PROM_8K_SIZE-1 ); + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper113::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + //DEBUGOUT( "$%04X:$%02X L=%3d\n", addr, data, nes.GetScanline() ); + switch (addr) + { + case 0x4100: + case 0x4111: + case 0x4120: + case 0x4194: + case 0x4195: + case 0x4900: + if (nes.rom.GetPROM_CRC() == 0xA75AEDE5) + { // HES 6-in-1 + if ((data & 0x80) != 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + } + SetPROM_32K_Bank(data >> 3); + SetVROM_8K_Bank(((data >> 3) & 0x08) + (data & 0x07)); + break; + } + } + + //void Mapper113::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "$%04X:$%02X L=%3d\n", addr, data, nes.GetScanline() ); + switch (addr) + { + case 0x8008: + case 0x8009: + SetPROM_32K_Bank(data >> 3); + SetVROM_8K_Bank(((data >> 3) & 0x08) + (data & 0x07)); + break; + case 0x8E66: + case 0x8E67: + //SetVROM_8K_Bank((data & 0x07) ? 0 : 1); + SetVROM_8K_Bank((data & 0x07) != 0 ? 0 : 1); + break; + case 0xE00A: + SetVRAM_Mirror(VRAM_MIRROR4L); + break; + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper114.cs b/Core/VirtualNes.Core/Mapper/Mapper114.cs new file mode 100644 index 0000000..fefaf5e --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper114.cs @@ -0,0 +1,184 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper114 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper114 : Mapper + { + BYTE reg_m, reg_a; + BYTE[] reg_b = new BYTE[8]; + BYTE reg_c; + BYTE irq_counter; + BYTE irq_occur; + public Mapper114(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + reg_a = reg_c = reg_m = 0; + for (INT i = 0; i < 8; i++) + { + reg_b[i] = 0; + } + irq_counter = 0; + irq_occur = 0; + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + + //void Mapper114::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + reg_m = data; + SetBank_CPU(); + } + + //void Mapper114::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr == 0xE003) + { + irq_counter = data; + } + else + if (addr == 0xE002) + { + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + } + else + { + switch (addr & 0xE000) + { + case 0x8000: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0xA000: + reg_c = 1; + reg_a = data; + break; + case 0xC000: + if (reg_c == 0) + { + break; + } + reg_b[reg_a & 0x07] = data; + switch (reg_a & 0x07) + { + case 0: + case 1: + case 2: + case 3: + case 6: + case 7: + SetBank_PPU(); + break; + case 4: + case 5: + SetBank_CPU(); + break; + } + reg_c = 0; + break; + } + } + } + + //void Mapper114::Clock(INT scanline) + public override void Clock(int cycles) + { + // if( irq_occur ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + //void Mapper114::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_counter != 0) + { + irq_counter--; + if (irq_counter == 0) + { + irq_occur = 0xFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + + void SetBank_CPU() + { + if ((reg_m & 0x80) != 0) + { + SetPROM_16K_Bank(4, reg_m & 0x1F); + } + else + { + SetPROM_8K_Bank(4, reg_b[4]); + SetPROM_8K_Bank(5, reg_b[5]); + } + } + + void SetBank_PPU() + { + SetVROM_2K_Bank(0, reg_b[0] >> 1); + SetVROM_2K_Bank(2, reg_b[2] >> 1); + SetVROM_1K_Bank(4, reg_b[6]); + SetVROM_1K_Bank(5, reg_b[1]); + SetVROM_1K_Bank(6, reg_b[7]); + SetVROM_1K_Bank(7, reg_b[3]); + } + + //void Mapper114::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + p[i] = reg_b[i]; + } + + p[8] = reg_m; + p[9] = reg_a; + p[10] = reg_c; + p[11] = irq_counter; + p[12] = irq_occur; + } + + //void Mapper114::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + reg_b[i] = p[i]; + } + reg_m = p[8]; + reg_a = p[9]; + reg_c = p[10]; + irq_counter = p[11]; + irq_occur = p[12]; + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper115.cs b/Core/VirtualNes.Core/Mapper/Mapper115.cs new file mode 100644 index 0000000..f1231ab --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper115.cs @@ -0,0 +1,301 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper115 CartSaint : Yuu Yuu Hakusho Final // +// JusticePao(?) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper115 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1, prg2, prg3; + BYTE prg0L, prg1L; + BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + + BYTE ExPrgSwitch; + BYTE ExChrSwitch; + public Mapper115(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = prg0L = 0; + prg1 = prg1L = 1; + prg2 = (byte)(PROM_8K_SIZE - 2); + prg3 = (byte)(PROM_8K_SIZE - 1); + + ExPrgSwitch = 0; + ExChrSwitch = 0; + + SetBank_CPU(); + + if (VROM_1K_SIZE != 0) + { + chr0 = 0; + chr1 = 1; + chr2 = 2; + chr3 = 3; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + } + else + { + chr0 = chr2 = chr4 = chr5 = chr6 = chr7 = 0; + chr1 = chr3 = 1; + } + + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + } + + + //void Mapper115::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x6000: + ExPrgSwitch = data; //data + SetBank_CPU(); + break; + case 0x6001: + ExChrSwitch = (byte)(data & 0x1); + SetBank_PPU(); + break; + } + //Mapper::WriteLow(addr, data); + base.WriteLow(addr, data); + } + + + //void Mapper115::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + switch (reg[0] & 0x07) + { + case 0x00: + chr0 = (byte)(data & 0xFE); + chr1 = (byte)(chr0 + 1); + SetBank_PPU(); + break; + case 0x01: + chr2 = (byte)(data & 0xFE); + chr3 = (byte)(chr2 + 1); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = prg0L = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = prg1L = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_enable = 0xFF; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 0xFF; + break; + } + } + + //void Mapper115::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + + void SetBank_CPU() + { + if ((ExPrgSwitch & 0x80) != 0) + { + prg0 = (byte)(((ExPrgSwitch << 1) & 0x1e)); + prg1 = (byte)(prg0 + 1); + + SetPROM_32K_Bank(prg0, prg1, prg0 + 2, prg1 + 2); + } + else + { + prg0 = prg0L; + prg1 = prg1L; + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank((ExChrSwitch << 8) + chr4, (ExChrSwitch << 8) + chr5, + (ExChrSwitch << 8) + chr6, (ExChrSwitch << 8) + chr7, + (ExChrSwitch << 8) + chr0, (ExChrSwitch << 8) + chr1, + (ExChrSwitch << 8) + chr2, (ExChrSwitch << 8) + chr3); + } + else + { + SetVROM_8K_Bank((ExChrSwitch << 8) + chr0, (ExChrSwitch << 8) + chr1, + (ExChrSwitch << 8) + chr2, (ExChrSwitch << 8) + chr3, + (ExChrSwitch << 8) + chr4, (ExChrSwitch << 8) + chr5, + (ExChrSwitch << 8) + chr6, (ExChrSwitch << 8) + chr7); + } + } + } + + //void Mapper115::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = prg2; + p[11] = prg3; + p[12] = chr0; + p[13] = chr1; + p[14] = chr2; + p[15] = chr3; + p[16] = chr4; + p[17] = chr5; + p[18] = chr6; + p[19] = chr7; + p[20] = irq_enable; + p[21] = irq_counter; + p[22] = irq_latch; + p[23] = ExPrgSwitch; + p[24] = prg0L; + p[25] = prg1L; + p[26] = ExChrSwitch; + + } + + //void Mapper115::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + prg2 = p[10]; + prg3 = p[11]; + chr0 = p[12]; + chr1 = p[13]; + chr2 = p[14]; + chr3 = p[15]; + chr4 = p[16]; + chr5 = p[17]; + chr6 = p[18]; + chr7 = p[19]; + irq_enable = p[20]; + irq_counter = p[21]; + irq_latch = p[22]; + ExPrgSwitch = p[23]; + prg0L = p[24]; + prg1L = p[25]; + ExChrSwitch = p[26]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper116.cs b/Core/VirtualNes.Core/Mapper/Mapper116.cs new file mode 100644 index 0000000..5ee4989 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper116.cs @@ -0,0 +1,331 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper116 CartSaint : 幽遊AV強列伝 // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper116 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1, prg2, prg3; + BYTE prg0L, prg1L; + BYTE chr0, chr1, chr2, chr3, chr4, chr5, chr6, chr7; + + BYTE irq_enable; + INT irq_counter; + BYTE irq_latch; + + BYTE ExPrgSwitch; + BYTE ExChrSwitch; + public Mapper116(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = prg0L = 0; + prg1 = prg1L = 1; + prg2 = (byte)(PROM_8K_SIZE - 2); + prg3 = (byte)(PROM_8K_SIZE - 1); + + ExPrgSwitch = 0; + ExChrSwitch = 0; + + SetBank_CPU(); + + if (VROM_1K_SIZE != 0) + { + chr0 = 0; + chr1 = 1; + chr2 = 2; + chr3 = 3; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + } + else + { + chr0 = chr2 = chr4 = chr5 = chr6 = chr7 = 0; + chr1 = chr3 = 1; + } + + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + + // nes.SetFrameIRQmode( FALSE ); + } + + //void Mapper116::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + Debuger.Log($"MPRWR A={addr & 0xFFFF:X4} D={data & 0xFF:X2} L={nes.GetScanline(),3} CYC={nes.cpu.GetTotalCycles()}"); + if ((addr & 0x4100) == 0x4100) + { + ExChrSwitch = data; + SetBank_PPU(); + } + } + + //void Mapper116::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + Debuger.Log($"MPRWR A={addr & 0xFFFF:X4} D={data & 0xFF:X2} L={nes.GetScanline(),3} CYC={nes.cpu.GetTotalCycles()}"); + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + switch (reg[0] & 0x07) + { + case 0x00: + chr0 = (byte)(data & 0xFE); + chr1 = (byte)(chr0 + 1); + SetBank_PPU(); + break; + case 0x01: + chr2 = (byte)(data & 0xFE); + chr3 = (byte)(chr2 + 1); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + // irq_enable = 0xFF; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_counter = irq_latch; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 0xFF; + break; + } + } + + //void Mapper116::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (irq_counter <= 0) + { + if (irq_enable != 0) + { + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + +#if FALSE //0 +DEBUGOUT( "IRQ L=%3d\n", scanline ); +{ +LPBYTE lpScn = nes.ppu.GetScreenPtr(); + + lpScn = lpScn+(256+16)*scanline; + + for( INT i = 0; i < 64; i++ ) { + lpScn[i] = 22; + } +} +#endif + return; + } + } + + if (nes.ppu.IsDispON()) + { + irq_counter--; + } + } + +#if FALSE //0 + if( (scanline >= 0 && scanline <= 239) ) { + if( nes.ppu.IsDispON() ) { + if( irq_enable ) { + if( !(irq_counter--) ) { +// irq_counter = irq_latch; + nes.cpu.IRQ_NotPending(); + } + } + } + } +#endif + } + + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((ExChrSwitch & 0x04) != 0) + { + INT chrbank = 256; + SetVROM_8K_Bank(chrbank + chr4, chrbank + chr5, + chrbank + chr6, chrbank + chr7, + chr0, chr1, + chr2, chr3); + } + else + { + INT chrbank = 0; + SetVROM_8K_Bank(chrbank + chr4, chrbank + chr5, + chrbank + chr6, chrbank + chr7, + chr0, chr1, + chr2, chr3); + } + +#if FALSE //0 + if( reg[0] & 0x80 ) { +// SetVROM_8K_Bank( chrbank+chr4, chrbank+chr5, +// chrbank+chr6, chrbank+chr7, +// chrbank+chr0, chrbank+chr1, +// chrbank+chr2, chrbank+chr3 ); + SetVROM_8K_Bank( chrbank+chr4, chrbank+chr5, + chrbank+chr6, chrbank+chr7, + chr0, chr1, + chr2, chr3 ); + } else { + SetVROM_8K_Bank( chr0, chr1, + chr2, chr3, + chrbank+chr4, chrbank+chr5, + chrbank+chr6, chrbank+chr7 ); + } +#endif + } + } + + //void Mapper116::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = prg2; + p[11] = prg3; + p[12] = chr0; + p[13] = chr1; + p[14] = chr2; + p[15] = chr3; + p[16] = chr4; + p[17] = chr5; + p[18] = chr6; + p[19] = chr7; + p[20] = irq_enable; + p[21] = (byte)irq_counter; + p[22] = irq_latch; + p[23] = ExPrgSwitch; + p[24] = prg0L; + p[25] = prg1L; + p[26] = ExChrSwitch; + } + + //void Mapper116::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + prg2 = p[10]; + prg3 = p[11]; + chr0 = p[12]; + chr1 = p[13]; + chr2 = p[14]; + chr3 = p[15]; + chr4 = p[16]; + chr5 = p[17]; + chr6 = p[18]; + chr7 = p[19]; + irq_enable = p[20]; + irq_counter = p[21]; + irq_latch = p[22]; + ExPrgSwitch = p[23]; + prg0L = p[24]; + prg1L = p[25]; + ExChrSwitch = p[26]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper117.cs b/Core/VirtualNes.Core/Mapper/Mapper117.cs new file mode 100644 index 0000000..c88eb3b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper117.cs @@ -0,0 +1,118 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper117 Sanko Gu(Tw) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper117 : Mapper + { + BYTE irq_enable; + BYTE irq_counter; + public Mapper117(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper117::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + case 0x8001: + SetPROM_8K_Bank(5, data); + break; + case 0x8002: + SetPROM_8K_Bank(6, data); + break; + case 0xA000: + SetVROM_1K_Bank(0, data); + break; + case 0xA001: + SetVROM_1K_Bank(1, data); + break; + case 0xA002: + SetVROM_1K_Bank(2, data); + break; + case 0xA003: + SetVROM_1K_Bank(3, data); + break; + case 0xA004: + SetVROM_1K_Bank(4, data); + break; + case 0xA005: + SetVROM_1K_Bank(5, data); + break; + case 0xA006: + SetVROM_1K_Bank(6, data); + break; + case 0xA007: + SetVROM_1K_Bank(7, data); + break; + case 0xC001: + case 0xC002: + case 0xC003: + irq_counter = data; + break; + case 0xE000: + irq_enable = (byte)(data & 1); + // nes.cpu.ClrIRQ( IRQ_MAPPER ); + break; + } + } + //void Mapper117::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (irq_counter == scanline) + { + irq_counter = 0; + // nes.cpu.IRQ(); + // nes.cpu.SetIRQ( IRQ_MAPPER ); + nes.cpu.SetIRQ(IRQ_TRIGGER); + } + } + } + } + } + + //void Mapper117::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_counter; + p[1] = irq_enable; + } + + //void Mapper117::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_counter = p[0]; + irq_enable = p[1]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper118.cs b/Core/VirtualNes.Core/Mapper/Mapper118.cs new file mode 100644 index 0000000..71d5d4f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper118.cs @@ -0,0 +1,261 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper118 IQS MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper118 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper118(NES parent) : base(parent) + { + } + + public override void Reset() + { + INT i; + + for (i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + if (VROM_1K_SIZE != 0) + { + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + + SetBank_PPU(); + } + else + { + chr01 = 0; + chr23 = 0; + chr4 = 0; + chr5 = 0; + chr6 = 0; + chr7 = 0; + } + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper118::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + if ((reg[0] & 0x80) != 0) + { + if ((reg[0] & 0x07) == 2) + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + } + } + else + { + if ((reg[0] & 0x07) == 0) + { + if ((data & 0x80) != 0) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + } + } + + switch (reg[0] & 0x07) + { + case 0x00: + if (VROM_1K_SIZE != 0) + { + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x01: + if (VROM_1K_SIZE != 0) + { + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x02: + if (VROM_1K_SIZE != 0) + { + chr4 = data; + SetBank_PPU(); + } + break; + case 0x03: + if (VROM_1K_SIZE != 0) + { + chr5 = data; + SetBank_PPU(); + } + break; + case 0x04: + if (VROM_1K_SIZE != 0) + { + chr6 = data; + SetBank_PPU(); + } + break; + case 0x05: + if (VROM_1K_SIZE != 0) + { + chr7 = data; + SetBank_PPU(); + } + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + + case 0xC000: + reg[4] = data; + irq_counter = data; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + break; + } + } + + //void Mapper118::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + } + + //void Mapper118::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + } + + //void Mapper118::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper119.cs b/Core/VirtualNes.Core/Mapper/Mapper119.cs new file mode 100644 index 0000000..2ee8799 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper119.cs @@ -0,0 +1,262 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper119 Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper119 : Mapper + { + BYTE patch; + + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + public Mapper119(NES parent) : base(parent) + { + } + + public override void Reset() + + { + patch = 0; + + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + } + + //void Mapper119::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + if (VROM_1K_SIZE != 0) + { + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x01: + if (VROM_1K_SIZE != 0) + { + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + } + break; + case 0x02: + if (VROM_1K_SIZE != 0) + { + chr4 = data; + SetBank_PPU(); + } + break; + case 0x03: + if (VROM_1K_SIZE != 0) + { + chr5 = data; + SetBank_PPU(); + } + break; + case 0x04: + if (VROM_1K_SIZE != 0) + { + chr6 = data; + SetBank_PPU(); + } + break; + case 0x05: + if (VROM_1K_SIZE != 0) + { + chr7 = data; + SetBank_PPU(); + } + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_counter = irq_latch; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + break; + } + } + + //void Mapper119::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + irq_counter = irq_latch; + // nes.cpu.IRQ(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if ((reg[0] & 0x80) != 0) + { + if ((chr4 & 0x40) != 0) SetCRAM_1K_Bank(0, chr4 & 0x07); else SetVROM_1K_Bank(0, chr4); + if ((chr5 & 0x40) != 0) SetCRAM_1K_Bank(1, chr5 & 0x07); else SetVROM_1K_Bank(1, chr5); + if ((chr6 & 0x40) != 0) SetCRAM_1K_Bank(2, chr6 & 0x07); else SetVROM_1K_Bank(2, chr6); + if ((chr7 & 0x40) != 0) SetCRAM_1K_Bank(3, chr7 & 0x07); else SetVROM_1K_Bank(3, chr7); + + if (((chr01 + 0) & 0x40) != 0) SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); else SetVROM_1K_Bank(4, (chr01 + 0)); + if (((chr01 + 1) & 0x40) != 0) SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); else SetVROM_1K_Bank(5, (chr01 + 1)); + if (((chr23 + 0) & 0x40) != 0) SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); else SetVROM_1K_Bank(6, (chr23 + 0)); + if (((chr23 + 1) & 0x40) != 0) SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); else SetVROM_1K_Bank(7, (chr23 + 1)); + } + else + { + if (((chr01 + 0) & 0x40) != 0) SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); else SetVROM_1K_Bank(0, (chr01 + 0)); + if (((chr01 + 1) & 0x40) != 0) SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); else SetVROM_1K_Bank(1, (chr01 + 1)); + if (((chr23 + 0) & 0x40) != 0) SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); else SetVROM_1K_Bank(2, (chr23 + 0)); + if (((chr23 + 1) & 0x40) != 0) SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); else SetVROM_1K_Bank(3, (chr23 + 1)); + + if ((chr4 & 0x40) != 0) SetCRAM_1K_Bank(4, chr4 & 0x07); else SetVROM_1K_Bank(4, chr4); + if ((chr5 & 0x40) != 0) SetCRAM_1K_Bank(5, chr5 & 0x07); else SetVROM_1K_Bank(5, chr5); + if ((chr6 & 0x40) != 0) SetCRAM_1K_Bank(6, chr6 & 0x07); else SetVROM_1K_Bank(6, chr6); + if ((chr7 & 0x40) != 0) SetCRAM_1K_Bank(7, chr7 & 0x07); else SetVROM_1K_Bank(7, chr7); + } + } + + ///void Mapper119::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + } + + //void Mapper119::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + } + + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper122.cs b/Core/VirtualNes.Core/Mapper/Mapper122.cs new file mode 100644 index 0000000..ac7454b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper122.cs @@ -0,0 +1,32 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper122/184 SunSoft-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper122 : Mapper + { + public Mapper122(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + } + + //void Mapper122::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x6000) + { + SetVROM_4K_Bank(0, data & 0x07); + SetVROM_4K_Bank(4, (data & 0x70) >> 4); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper133.cs b/Core/VirtualNes.Core/Mapper/Mapper133.cs new file mode 100644 index 0000000..aa12ea1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper133.cs @@ -0,0 +1,34 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper133 SACHEN CHEN // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper133 : Mapper + { + public Mapper133(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper133::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x4120) + { + SetPROM_32K_Bank((data & 0x04) >> 2); + SetVROM_8K_Bank(data & 0x03); + } + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper134.cs b/Core/VirtualNes.Core/Mapper/Mapper134.cs new file mode 100644 index 0000000..1e217a1 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper134.cs @@ -0,0 +1,83 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper133 SACHEN CHEN // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper134 : Mapper + { + BYTE cmd, prg, chr; + public Mapper134(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0); + // SetPROM_16K_Bank( 6, 0 ); + // SetPROM_16K_Bank( 6, 1 ); + SetVROM_8K_Bank(0); + } + + //void Mapper134::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr & 0x4101) + { + case 0x4100: + cmd = (byte)(data & 0x07); + break; + case 0x4101: + switch (cmd) + { + case 0: + prg = 0; + chr = 3; + break; + case 4: + chr &= 0x3; + chr |= (byte)((data & 0x07) << 2); + break; + case 5: + prg = (byte)(data & 0x07); + break; + case 6: + chr &= 0x1C; + chr |= (byte)(data & 0x3); + break; + case 7: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + break; + } + SetPROM_32K_Bank(prg); + // SetPROM_16K_Bank( 4, (prg<<1)|0 ); + // SetPROM_16K_Bank( 6, (prg<<1)|1 ); + SetVROM_8K_Bank(chr); + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + + //void Mapper134::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = cmd; + p[1] = prg; + p[2] = chr; + } + + //void Mapper134::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + cmd = p[0]; + prg = p[1]; + chr = p[2]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper135.cs b/Core/VirtualNes.Core/Mapper/Mapper135.cs new file mode 100644 index 0000000..a9e41c8 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper135.cs @@ -0,0 +1,110 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper135 SACHEN CHEN // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper135 : Mapper + { + BYTE cmd; + BYTE chr0l, chr1l, chr0h, chr1h, chrch; + public Mapper135(NES parent) : base(parent) + { + } + + public override void Reset() + { + cmd = 0; + chr0l = chr1l = chr0h = chr1h = chrch = 0; + + SetPROM_32K_Bank(0); + SetBank_PPU(); + } + + //void Mapper135::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr & 0x4101) + { + case 0x4100: + cmd = (byte)(data & 0x07); + break; + case 0x4101: + switch (cmd) + { + case 0: + chr0l = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 1: + chr0h = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 2: + chr1l = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 3: + chr1h = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 4: + chrch = (byte)(data & 0x07); + SetBank_PPU(); + break; + case 5: + SetPROM_32K_Bank((byte)(data & 0x07)); + break; + case 6: + break; + case 7: + switch ((data >> 1) & 0x03) + { + case 0: SetVRAM_Mirror(VRAM_MIRROR4L); break; + case 1: SetVRAM_Mirror(VRAM_HMIRROR); break; + case 2: SetVRAM_Mirror(VRAM_VMIRROR); break; + case 3: SetVRAM_Mirror(VRAM_MIRROR4L); break; + } + break; + } + break; + } + + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + + void SetBank_PPU() + { + SetVROM_2K_Bank(0, 0 | (chr0l << 1) | (chrch << 4)); + SetVROM_2K_Bank(2, 1 | (chr0h << 1) | (chrch << 4)); + SetVROM_2K_Bank(4, 0 | (chr1l << 1) | (chrch << 4)); + SetVROM_2K_Bank(6, 1 | (chr1h << 1) | (chrch << 4)); + } + + //void Mapper135::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = cmd; + p[1] = chr0l; + p[2] = chr0h; + p[3] = chr1l; + p[4] = chr1h; + p[5] = chrch; + } + + //void Mapper135::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + cmd = p[0]; + chr0l = p[1]; + chr0h = p[2]; + chr0l = p[3]; + chr0h = p[4]; + chrch = p[5]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper140.cs b/Core/VirtualNes.Core/Mapper/Mapper140.cs new file mode 100644 index 0000000..0d043a8 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper140.cs @@ -0,0 +1,32 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper140 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper140 : Mapper + { + public Mapper140(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper140::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + SetPROM_32K_Bank((data & 0xF0) >> 4); + SetVROM_8K_Bank(data & 0x0F); + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper142.cs b/Core/VirtualNes.Core/Mapper/Mapper142.cs new file mode 100644 index 0000000..12c1fbd --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper142.cs @@ -0,0 +1,115 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper142 SMB2J // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper142 : Mapper + { + BYTE prg_sel; + BYTE irq_enable; + INT irq_counter; + public Mapper142(NES parent) : base(parent) + { + } + + public override void Reset() + + { + prg_sel = 0; + irq_enable = 0; + irq_counter = 0; + + SetPROM_8K_Bank(3, 0); + SetPROM_8K_Bank(7, 0x0F); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper142::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x8000: + irq_counter = (irq_counter & 0xFFF0) | ((data & 0x0F) << 0); + break; + case 0x9000: + irq_counter = (irq_counter & 0xFF0F) | ((data & 0x0F) << 4); + break; + case 0xA000: + irq_counter = (irq_counter & 0xF0FF) | ((data & 0x0F) << 8); + break; + case 0xB000: + irq_counter = (irq_counter & 0x0FFF) | ((data & 0x0F) << 12); + break; + case 0xC000: + irq_enable = (byte)(data & 0x0F); + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE000: + prg_sel = (byte)(data & 0x0F); + break; + case 0xF000: + switch (prg_sel) + { + case 1: SetPROM_8K_Bank(4, data & 0x0F); break; + case 2: SetPROM_8K_Bank(5, data & 0x0F); break; + case 3: SetPROM_8K_Bank(6, data & 0x0F); break; + case 4: SetPROM_8K_Bank(3, data & 0x0F); break; + } + break; + } + } + + //void Mapper142::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if (irq_counter > (0xFFFF - 113)) + { + irq_counter = 0; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter += 113; + } + } + } + + //void Mapper142::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = prg_sel; + p[0] = irq_enable; + //*(INT*)&p[2] = irq_counter; + BitConverter.GetBytes(irq_counter).CopyTo(p, 2); + } + + //void Mapper142::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + prg_sel = p[0]; + irq_enable = p[1]; + //irq_counter = *(INT*)&p[2]; + irq_counter = BitConverter.ToInt32(p, 2); + } + public override bool IsStateSave() + { + return true; + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper151.cs b/Core/VirtualNes.Core/Mapper/Mapper151.cs new file mode 100644 index 0000000..587366a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper151.cs @@ -0,0 +1,55 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper151 VS-Unisystem // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper151 : Mapper + { + public Mapper151(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); +#if FALSE//0 //hum + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x1E438D52) + { + DirectDraw.SetVsPalette(7); //VS_Goonies + } + if (crc == 0xD99A2087) + { + DirectDraw.SetVsPalette(6); //VS_Gradius + } +#endif + } + + //void Mapper151::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + case 0xC000: + SetPROM_8K_Bank(6, data); + break; + case 0xE000: + SetVROM_4K_Bank(0, data); + break; + case 0xF000: + SetVROM_4K_Bank(4, data); + break; + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper160.cs b/Core/VirtualNes.Core/Mapper/Mapper160.cs new file mode 100644 index 0000000..e24fa78 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper160.cs @@ -0,0 +1,230 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper160 PC-Aladdin // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper160 : Mapper + { + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE refresh_type; + public Mapper160(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + refresh_type = 0; + } + + //void Mapper160::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + case 0x8001: + SetPROM_8K_Bank(5, data); + break; + case 0x8002: + SetPROM_8K_Bank(6, data); + break; + + case 0x9000: + if (data == 0x2B) + { + refresh_type = 1; + } + else if (data == 0xA8) + { + refresh_type = 2; + } + else if (data == 0x1F) + { + refresh_type = 3; + } + else if (data == 0x7C) + { + refresh_type = 4; + } + else if (data == 0x18) + { + refresh_type = 5; + } + else if (data == 0x60) + { + refresh_type = 6; + } + else + { + refresh_type = 0; + } + SetVROM_1K_Bank(0, data); + break; + case 0x9001: + SetVROM_1K_Bank(1, data); + break; + + case 0x9002: + if (refresh_type == 2 && data != 0xE8) + { + refresh_type = 0; + } + SetVROM_1K_Bank(2, data); + break; + + case 0x9003: + SetVROM_1K_Bank(3, data); + break; + case 0x9004: + SetVROM_1K_Bank(4, data); + break; + case 0x9005: + SetVROM_1K_Bank(5, data); + break; + case 0x9006: + SetVROM_1K_Bank(6, data); + break; + case 0x9007: + SetVROM_1K_Bank(7, data); + break; + + case 0xC000: + irq_counter = irq_latch; + irq_enable = irq_latch; + break; + case 0xC001: + irq_latch = data; + break; + case 0xC002: + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC003: + irq_counter = data; + break; + } + } + + //void Mapper160::HSync(INT scanline) + public override void HSync(int scanline) + { + if (scanline == 0 || scanline == 239) + { + switch (refresh_type) + { + case 1: + SetVROM_8K_Bank(0x58, 0x59, 0x5A, 0x5B, 0x58, 0x59, 0x5A, 0x5B); + break; + case 2: + SetVROM_8K_Bank(0x78, 0x79, 0x7A, 0x7B, 0x78, 0x79, 0x7A, 0x7B); + break; + case 3: + SetVROM_8K_Bank(0x7C, 0x7D, 0x7E, 0x7F, 0x7C, 0x7D, 0x7E, 0x7F); + break; + case 5: + SetVROM_8K_Bank(0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77); + break; + case 6: + SetVROM_8K_Bank(0x5C, 0x5D, 0x5E, 0x5F, 0x7C, 0x7D, 0x7E, 0x7F); + break; + } + } + if (scanline == 64) + { + if (refresh_type == 4) + { + if (PPU_MEM_BANK[8][32 * 10 + 16] == 0x0A) + { + SetVROM_1K_Bank(0, 0x68); + SetVROM_1K_Bank(1, 0x69); + SetVROM_1K_Bank(2, 0x6A); + SetVROM_1K_Bank(3, 0x6B); + } + else + { + SetVROM_1K_Bank(0, 0x6C); + SetVROM_1K_Bank(1, 0x6D); + SetVROM_1K_Bank(2, 0x6E); + SetVROM_1K_Bank(3, 0x6F); + } + } + } + if (scanline == 128) + { + if (refresh_type == 4) + { + SetVROM_1K_Bank(0, 0x68); + SetVROM_1K_Bank(1, 0x69); + SetVROM_1K_Bank(2, 0x6A); + SetVROM_1K_Bank(3, 0x6B); + } + else if (refresh_type == 5) + { + SetVROM_8K_Bank(0x74, 0x75, 0x76, 0x77, 0x74, 0x75, 0x76, 0x77); + } + } + if (scanline == 160) + { + if (refresh_type == 6) + { + SetVROM_8K_Bank(0x60, 0x61, 0x5E, 0x5F, 0x7C, 0x7D, 0x7E, 0x7F); + } + } + + if (irq_enable != 0) + { + if (irq_counter == 0xFF) + { + // nes.cpu.IRQ_NotPending(); + irq_enable = 0; + irq_counter = 0; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + } + + //void Mapper160::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + p[2] = irq_latch; + p[3] = refresh_type; + } + + // + //void Mapper160::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + irq_latch = p[2]; + refresh_type = p[3]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper162.cs b/Core/VirtualNes.Core/Mapper/Mapper162.cs new file mode 100644 index 0000000..e301d84 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper162.cs @@ -0,0 +1,128 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper162 Pocket Monster Gold // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; +using BYTE = System.Byte; + +namespace VirtualNes.Core +{ + public class Mapper162 : Mapper + { + BYTE reg5000; + BYTE reg5100; + BYTE reg5200; + BYTE reg5300; + public Mapper162(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg5000 = 0; + reg5100 = 0; + reg5200 = 0; + reg5300 = 7; + SetBank_CPU(); + SetBank_PPU(); + } + + //void Mapper162::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x5000) + { + reg5000 = data; + SetBank_CPU(); + SetBank_PPU(); + } + else if (addr == 0x5100) + { + reg5100 = data; + SetBank_CPU(); + SetBank_PPU(); + } + else if (addr == 0x5200) + { + reg5200 = data; + SetBank_CPU(); + SetBank_PPU(); + } + else if (addr == 0x5300) + { + reg5300 = data; + } + else if (addr >= 0x6000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + else + { + Debuger.Log($"write to {addr:X4}:{data:X2}"); + } + + } + + void SetBank_CPU() + { + BYTE bank = 0; + switch (reg5300) + { + case 4: + bank = (byte)((((reg5000 & 0xF) + ((reg5100 & 3) >> 1)) | ((reg5200 & 1) << 4))); + break; + case 7: + bank = (byte)(((reg5000 & 0xF) | ((reg5200 & 1) << 4))); + break; + } + SetPROM_32K_Bank((byte)bank); + } + + void SetBank_PPU() + { + SetCRAM_8K_Bank(0); + } + + //void Mapper162::HSync(int scanline) + public override void HSync(int scanline) + { + if ((reg5000 & 0x80) != 0 && nes.ppu.IsDispON()) + { + if (scanline < 127) + { + // SetCRAM_4K_Bank(0, 0); + SetCRAM_4K_Bank(4, 0); + } + else if (scanline < 240) + { + // SetCRAM_4K_Bank(0, 1); + SetCRAM_4K_Bank(4, 1); + } + } + } + + + //void Mapper162::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg5000; + p[1] = reg5100; + p[2] = reg5200; + p[3] = reg5300; + } + + //void Mapper162::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg5000 = p[0]; + reg5100 = p[1]; + reg5200 = p[2]; + reg5300 = p[3]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper163.cs b/Core/VirtualNes.Core/Mapper/Mapper163.cs new file mode 100644 index 0000000..6e757da --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper163.cs @@ -0,0 +1,238 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper163 NanJing Games // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper163 : Mapper + { + //BYTE strobe; + //BYTE security; + //BYTE trigger; + //BYTE rom_type; + + //BYTE reg[2]; + BYTE[] reg = new byte[3]; + BYTE strobe, trigger; + ushort security; + BYTE rom_type; + + INT www, index; + public Mapper163(NES parent) : base(parent) + { + } + + static Int32[] index_adjust = new Int32[] { -1, -1, -1, -1, 2, 4, 6, 8 }; + static Int16[] step_table = new Int16[] { +7,8,9,10,11,12,13,14,16,17,19,21,23,25,28,31,34,37,41,45,50,55, +60,66,73,80,88,97,107,118,130,143,157,173,190,209,230,253,279, +307,337,371,408,449,494,544,598,658,724,796,876,963,1060, +1166,1282,1411,1552,1707,1878,2066,2272,2499,2749,3024,3327, +3660,4026,4428,4871,5358,5894,6484,7132,7845,8630,9493,10442, +11487,12635,13899,15289,16818,18500,20350,22385,24623,27086,29794,32767 +}; + + BYTE adpcm_decoder(BYTE data) + { + int sb, delta; + int cur_sample = 0; + if ((data & 8) != 0) sb = 1; else sb = 0; + data &= 7; + delta = (step_table[index] * data) / 4 + step_table[index] / 8; + if (sb == 1) delta = -delta; + cur_sample += delta; + if (cur_sample > 32767) cur_sample = 32767; + else if (cur_sample < -32768) cur_sample = -32768; + else cur_sample = cur_sample; + index += index_adjust[data]; + if (index < 0) index = 0; + if (index > 88) index = 88; + return (byte)(cur_sample & 0xff); + } + + //void Mapper163::Reset() + public override void Reset() + { + + index = 0; + + reg[1] = 0xFF; + strobe = 1; + security = trigger = reg[0] = 0x00; + rom_type = 0; + SetPROM_32K_Bank(15); + + // jedi_table_init(); + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0xb6a10d5d) + { // Hu Lu Jin Gang (NJ039) (Ch) [dump] + SetPROM_32K_Bank(0); + } + if (crc == 0xf52468e7 // San Guo Wu Shuang - Meng Jiang Zhuan (NJ047) (Ch) [dump] + || crc == 0x696D98E3) + { // San Guo Zhi Lv Bu Zhuan (NJ040) (Ch) [dump] + rom_type = 1; + } + if (crc == 0xEFF96E8A) + { // Mo Shou Shi Jie E Mo Lie Ren (NJ097) (Ch) [dump] + rom_type = 2; + } + + www = 0; + } + + ///BYTE Mapper163::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + // DEBUGOUT( "ReadLow - addr= %04x\n", addr ); + + if ((addr >= 0x5000 && addr < 0x6000)) + { + switch (addr & 0x7700) + { + case 0x5100: + return (byte)security; + break; + case 0x5500: + if (trigger != 0) + return (byte)security; + else + return 0; + break; + } + return 4; + } + else if (addr >= 0x6000) + { + return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + return base.ReadLow(addr); + } + + //void Mapper163::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + // if(addr==0x5200) DEBUGOUT( "WriteLow - addr= %04x ; dat= %03x\n", addr, data ); + + if ((addr >= 0x4020 && addr < 0x6000)) + { + if (addr == 0x5101) + { + if (strobe != 0 && data == 0) + { + trigger ^= 1; + // trigger ^= 0xFF; + } + strobe = data; + } + else if (addr == 0x5100 && data == 6) + { + SetPROM_32K_Bank(3); + } + else + { + switch (addr & 0x7300) + { + case 0x5000: + reg[1] = data; + SetPROM_32K_Bank((reg[1] & 0xF) | (reg[0] << 4)); + if ((reg[1] & 0x80) == 0 && (nes.ppu.GetScanlineNo() < 128)) + SetCRAM_8K_Bank(0); + if (rom_type == 1) SetCRAM_8K_Bank(0); + break; + case 0x5100: + reg[2] = data; + // nes.apu.Write(0x4011,(decode(reg[0]&0xf)<<3)); + break; + case 0x5200: + reg[0] = data; + SetPROM_32K_Bank((reg[1] & 0xF) | (reg[0] << 4)); + break; + case 0x5300: + security = data; + break; + } + } + } + else if (addr >= 0x6000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + if ((addr >= 0x7900 && addr <= 0x79FF)) + { + // WAVRAM[addr&0xFF] = data; + // if(addr==0x79FF){ + // memcpy( YWRAM+(www*256), WAVRAM, 256); + // www++; + // } + // nes.apu.Write(0x4011,(adpcm_decoder(data))); + nes.apu.Write(0x4011, data); + } + } + } + + //void Mapper163::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // DEBUGOUT( "Write - addr= %04x ; dat= %03x\n", addr, data ); + } + + //void Mapper163::HSync(int scanline) + public override void HSync(int scanline) + { + if ((reg[1] & 0x80) != 0 && nes.ppu.IsDispON()) + { + if (scanline == 127) + { + SetCRAM_4K_Bank(0, 1); + SetCRAM_4K_Bank(4, 1); + } + if (rom_type == 1) + { + if (scanline < 127) + { + SetCRAM_4K_Bank(0, 0); + SetCRAM_4K_Bank(4, 0); + } + } + else + { + if (scanline == 239) + { + SetCRAM_4K_Bank(0, 0); + SetCRAM_4K_Bank(4, 0); + if (rom_type == 2) SetCRAM_4K_Bank(4, 1); + } + } + } + } + + ///void Mapper163::PPU_Latch(WORD addr) + //{ + // if (DirectInput.m_Sw[DIK_PAUSE]) + // { + // nes.Dump_YWRAM(); + // } + //} + + //void Mapper163::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper163::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper164.cs b/Core/VirtualNes.Core/Mapper/Mapper164.cs new file mode 100644 index 0000000..eb4b0db --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper164.cs @@ -0,0 +1,165 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper164 Pocket Monster Gold // +////////////////////////////////////////////////////////////////////////// +using System; +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper164 : Mapper + { + BYTE reg5000; + BYTE reg5100; + BYTE a3, p_mode; + public Mapper164(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg5000 = 0; + reg5100 = 0; + SetBank_CPU(); + SetBank_PPU(); + nes.ppu.SetExtLatchMode(true); + } + + //void Mapper164::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x5000) + { + p_mode = (byte)(data >> 7); + reg5000 = data; + SetBank_CPU(); + SetBank_PPU(); + } + else if (addr == 0x5100) + { + reg5100 = data; + SetBank_CPU(); + SetBank_PPU(); + } + else if (addr >= 0x6000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + else + { + Debuger.Log($"write to {addr:X4}:{data:X2}"); + } + + } + + + void SetBank_CPU() + { + int mode, @base, bank; + + @base = (reg5100 & 1) << 5; + mode = (reg5000 >> 4) & 0x07; + + switch (mode) + { + case 0: + case 2: + case 4: + case 6: /* NORMAL MODE */ + bank = (reg5000 & 0x0f); + bank += (reg5000 & 0x20) >> 1; + SetPROM_16K_Bank(4, bank + @base); + SetPROM_16K_Bank(6, @base + 0x1f); + Debuger.Log($"-- normal mode: mode={mode}, bank={bank} --"); + break; + case 1: + case 3: /* REG MODE */ + Debuger.Log("-- reg mode --"); + break; + case 5: /* 32K MODE */ + bank = (reg5000 & 0x0f); + SetPROM_32K_Bank(bank + (@base >> 1)); + // DEBUGOUT("-- 32K MODE: bank=%02x --\n", bank); + break; + case 7: /* HALF MODE */ + bank = (reg5000 & 0x0f); + bank += (bank & 0x08) << 1; + SetPROM_16K_Bank(4, bank + @base); + bank = (bank & 0x10) + 0x0f; + SetPROM_16K_Bank(6, @base + 0x1f); + Debuger.Log("-- half mode --"); + break; + default: + break; + }; + + } + + void SetBank_PPU() + { + SetCRAM_8K_Bank(0); + } + + + //void Mapper164::PPU_ExtLatchX(INT x) + public override void PPU_ExtLatchX(int x) + { + a3 = (byte)((x & 1) << 3); + } + + //void Mapper164::PPU_ExtLatch(WORD ntbladr, BYTE& chr_l, BYTE& chr_h, BYTE& attr ) + public override void PPU_ExtLatch(ushort ntbladr, ref byte chr_l, ref byte chr_h, ref byte attr) + { + INT loopy_v = nes.ppu.GetPPUADDR(); + INT loopy_y = nes.ppu.GetTILEY(); + INT tileofs = (PPUREG[0] & PPU.PPU_BGTBL_BIT) << 8; + INT attradr = 0x23C0 + (loopy_v & 0x0C00) + ((loopy_v & 0x0380) >> 4); + INT attrsft = (ntbladr & 0x0040) >> 4; + Span pNTBL = PPU_MEM_BANK[ntbladr >> 10]; + + INT ntbl_x = ntbladr & 0x001F; + INT tileadr; + + attradr &= 0x3FF; + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + + if (p_mode != 0) + { + tileadr = (tileadr & 0xfff7) | a3; + chr_l = chr_h = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + } + else + { + chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + + } + + //void Mapper164::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg5000; + p[1] = reg5100; + p[2] = a3; + p[3] = p_mode; + } + + //void Mapper164::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg5000 = p[0]; + reg5100 = p[1]; + a3 = p[2]; + p_mode = p[3]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper165.cs b/Core/VirtualNes.Core/Mapper/Mapper165.cs new file mode 100644 index 0000000..925b27a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper165.cs @@ -0,0 +1,195 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper165 Fire Emblem Chinese version // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper165 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr0, chr1, chr2, chr3; + BYTE we_sram; + BYTE latch; + public Mapper165(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr0 = 0; + chr1 = 0; + chr2 = 4; + chr3 = 4; + latch = 0xFD; + SetBank_PPU(); + + we_sram = 0; // Disable + + nes.ppu.SetChrLatchMode(true); + } + + //void Mapper165::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr0 = (byte)(data & 0xFC); + if (latch == 0xFD) + SetBank_PPU(); + break; + case 0x01: + chr1 = (byte)(data & 0xFC); + if (latch == 0xFE) + SetBank_PPU(); + break; + + case 0x02: + chr2 = (byte)(data & 0xFC); + if (latch == 0xFD) + SetBank_PPU(); + break; + case 0x04: + chr3 = (byte)(data & 0xFC); + if (latch == 0xFE) + SetBank_PPU(); + break; + + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if ((data & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + default: + break; + } + + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + void SetBank_PPU() + { + if (latch == 0xFD) + { + SetBank_PPUSUB(0, chr0); + SetBank_PPUSUB(4, chr2); + } + else + { + SetBank_PPUSUB(0, chr1); + SetBank_PPUSUB(4, chr3); + } + } + + void SetBank_PPUSUB(int bank, int page) + { + if (page == 0) + { + SetCRAM_4K_Bank((byte)bank, page >> 2); + } + else + { + SetVROM_4K_Bank((byte)bank, page >> 2); + } + } + + //void Mapper165::PPU_ChrLatch(WORD addr) + public override void PPU_ChrLatch(ushort addr) + { + ushort mask = (ushort)(addr & 0x1FF0); + + if (mask == 0x1FD0) + { + latch = 0xFD; + SetBank_PPU(); + } + else if (mask == 0x1FE0) + { + latch = 0xFE; + SetBank_PPU(); + } + + } + + //void Mapper165::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr0; + p[11] = chr1; + p[12] = chr2; + p[13] = chr3; + p[14] = latch; + } + + //void Mapper165::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr0 = p[10]; + chr1 = p[11]; + chr2 = p[12]; + chr3 = p[13]; + latch = p[14]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper167.cs b/Core/VirtualNes.Core/Mapper/Mapper167.cs new file mode 100644 index 0000000..daf4f96 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper167.cs @@ -0,0 +1,134 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper004 Supor Computer V4.0 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper167 : Mapper + { + BYTE[] regs = new byte[4]; + BYTE rom_type; + public Mapper167(NES parent) : base(parent) + { + } + + public override void Reset() + { + uint crc; + + regs[0] = 0; + regs[1] = 0; + regs[2] = 0; + regs[3] = 0; + + crc = nes.rom.GetPROM_CRC(); + if (crc == 0x82F1Fb96) + { + // Subor Computer(Russia) + rom_type = 1; + } + else + { + // Subor Computer(Chinese) + rom_type = 0; + } + + SetBank_CPU(); + SetBank_PPU(); + } + + //void Mapper167::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + int idx; + + idx = (addr >> 13) & 0x03; + regs[idx] = data; + SetBank_CPU(); + SetBank_PPU(); + // DEBUGOUT("write to %04x:%02x\n", addr, data); + } + + + void SetBank_CPU() + { + int @base, bank; + + @base = ((regs[0] ^ regs[1]) & 0x10) << 1; + bank = (regs[2] ^ regs[3]) & 0x1f; + + if ((regs[1] & 0x08) != 0) + { + bank &= 0xfe; + if (rom_type == 0) + { + SetPROM_16K_Bank(4, @base + bank + 1); + SetPROM_16K_Bank(6, @base + bank + 0); + } + else + { + SetPROM_16K_Bank(6, @base + bank + 1); + SetPROM_16K_Bank(4, @base + bank + 0); + } + // DEBUGOUT("32K MODE!\n"); + } + else + { + if ((regs[1] & 0x04) != 0) + { + SetPROM_16K_Bank(4, 0x1f); + SetPROM_16K_Bank(6, @base + bank); + // DEBUGOUT("HIGH 16K MODE!\n"); + } + else + { + SetPROM_16K_Bank(4, @base + bank); + if (rom_type == 0) + { + SetPROM_16K_Bank(6, 0x20); + } + else + { + SetPROM_16K_Bank(6, 0x07); + } + // DEBUGOUT("LOW 16K MODE!\n"); + } + } + + + } + + void SetBank_PPU() + { + SetCRAM_8K_Bank(0); + } + + //void Mapper167::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = regs[0]; + p[1] = regs[1]; + p[2] = regs[2]; + p[3] = regs[3]; + p[4] = rom_type; + } + + //void Mapper167::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + regs[0] = p[0]; + regs[1] = p[1]; + regs[2] = p[2]; + regs[3] = p[3]; + rom_type = p[4]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper168.cs b/Core/VirtualNes.Core/Mapper/Mapper168.cs new file mode 100644 index 0000000..1958386 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper168.cs @@ -0,0 +1,164 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper168 Subor (PPUExtLatch) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper168 : Mapper + { + byte reg5000, reg5200, reg5300; + byte PPU_SW, NT_data; + byte Rom_Type; + public Mapper168(NES parent) : base(parent) { } + + public override bool IsStateSave() + { + return true; + } + public override void Reset() + { + reg5000 = 0; + reg5200 = 0; + reg5300 = 0; + PPU_SW = 0; + NT_data = 0; + nes.ppu.SetExtLatchMode(true); + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + + Rom_Type = 0; + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x0A9808AE) //[Subor] Karaoke (C) + { + Rom_Type = 1; + SetPROM_32K_Bank(0); + nes.SetVideoMode(2 != 0); + } + if (crc == 0x12D61CE8) //[Subor] Subor V11.0 (C) + { + Rom_Type = 2; + } + } + + public override byte ReadLow(ushort addr) + { + if (addr == 0x5300) return 0x8F; //返回0x8F,跳过真人语音发声有关的程序段 + return base.ReadLow(addr); + } + + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x5000) + { + reg5000 = data; + SetBank_CPU(); + } + else if (addr == 0x5200) + { + reg5200 = (byte)(data & 0x7); + SetBank_CPU(); + } + else if (addr == 0x5300) + { + reg5300 = data; + } + else if (addr >= 0x6000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + } + + + public override void Write(ushort addr, byte data) + { + if (Rom_Type == 1) + { //[Subor] Karaoke (C) + SetPROM_32K_Bank(data & 0x1F); + if ((data & 0x40) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + if ((data & 0xC0) != 0) PPU_SW = 1; + else PPU_SW = 0; + } + } + + void SetBank_CPU() + { + if (reg5200 < 4) SetPROM_16K_Bank(4, reg5000); + else SetPROM_32K_Bank(reg5000); + switch (reg5200) + { + case 0: + SetVRAM_Mirror(VRAM_VMIRROR); + PPU_SW = 0; + break; + case 2: + SetVRAM_Mirror(VRAM_VMIRROR); + PPU_SW = 1; + break; + case 1: + case 3: + SetVRAM_Mirror(VRAM_HMIRROR); + PPU_SW = 0; + break; + case 5: + if (reg5000 == 4 && Rom_Type == 2) + { //Special for [Subor] Subor V11.0 (C) - Tank (坦克大战) + nes.ppu.SetExtLatchMode(false); + SetVRAM_Mirror(VRAM_HMIRROR); + } + break; + } + } + + public override void PPU_Latch(ushort addr) + { + if ((addr & 0xF000) == 0x2000) + { + NT_data = (byte)((addr >> 8) & 0x03); + } + } + + public override void PPU_ExtLatch(ushort ntbladr, ref byte chr_l, ref byte chr_h, ref byte attr) + { + INT loopy_v = nes.ppu.GetPPUADDR(); + INT loopy_y = nes.ppu.GetTILEY(); + INT tileofs = (PPUREG[0] & PPU.PPU_BGTBL_BIT) << 8; + INT attradr = 0x23C0 + (loopy_v & 0x0C00) + ((loopy_v & 0x0380) >> 4); + INT attrsft = (ntbladr & 0x0040) >> 4; + ArrayRef pNTBL = PPU_MEM_BANK[ntbladr >> 10]; + INT ntbl_x = ntbladr & 0x001F; + INT tileadr, ntb; + + ntb = (ntbladr >> 10) & 3; + + if (ntb == 2) + tileofs |= 0x1000; + else if (ntb != 0 && PPU_SW != 0) + tileofs |= 0x1000; + else + tileofs |= 0x0000; + + attradr &= 0x3FF; + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + + chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + + public override void SaveState(byte[] p) + { + p[0] = reg5000; + p[1] = reg5200; + } + + public override void LoadState(byte[] p) + { + reg5000 = p[0]; + reg5200 = p[1]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper173.cs b/Core/VirtualNes.Core/Mapper/Mapper173.cs new file mode 100644 index 0000000..c71056b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper173.cs @@ -0,0 +1,243 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper173 Subor // +////////////////////////////////////////////////////////////////////////// +using VirtualNes.Core.Debug; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; +namespace VirtualNes.Core +{ + public class Mapper173 : Mapper + { + + BYTE[] reg = new BYTE[10]; + + INT irq_counter, irq_latch; + BYTE irq_enable, irq_repeat; + BYTE irq_occur; + public Mapper173(NES parent) : base(parent) { } + + public override bool IsStateSave() + { + return true; + } + public override void Reset() + { + // nes.ppu.SetExtLatchMode( TRUE ); + for (INT i = 0; i < 11; i++) reg[i] = 0x00; + + irq_enable = irq_repeat = 0; + irq_counter = irq_latch = 0; + irq_occur = 0; + + SetPROM_32K_Bank(0); + nes.SetVideoMode(2 != 0); + } + + public override byte ExRead(ushort addr) + { + Debuger.Log($"ExRead - addr= {addr}\n"); + + return 0x00; + + switch (addr) + { + case 0x4026: + // + break; + case 0x4033: + //D7: + //D6: + //D5: + //D4: + //D3: + //D2: + //D1: + //D0: + // + break; + case 0x4204: //FDC主状态寄存器(STATUS) + // + break; + case 0x4205: //FDC数据寄存器(DATA)(读???) + // + break; + } + } + + public override void ExWrite(ushort addr, byte data) + { + Debuger.Log($"ExWrite - addr= {addr} ; dat= {data}\n"); + switch (addr) + { + case 0x4020: + reg[0] = data; + break; + case 0x4022: + reg[1] = data; + break; + case 0x4023: + reg[2] = data; + break; + case 0x4026: + reg[3] = data; + break; + case 0x4031: + reg[4] = data; + break; + case 0x4032: + reg[5] = data; + + irq_repeat = (byte)(data & 0x01); + irq_enable = (byte)(data & 0x02); + irq_occur = 0; + if (irq_enable != null) + { + irq_counter = irq_latch; + } + else + { + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + } + break; + case 0x4034: + reg[6] = data; + + irq_latch = (irq_latch & 0xFF00) | data; + break; + case 0x4035: + reg[7] = data; + + irq_latch = (irq_latch & 0x00FF) | (data << 8); + break; + case 0x4040: + SetPROM_4K_Bank(0x8000, data & 0x7F); + break; + case 0x4041: + SetPROM_4K_Bank(0x9000, data & 0x7F); + break; + case 0x4042: + SetPROM_4K_Bank(0xa000, data & 0x7F); + break; + case 0x4043: + SetPROM_4K_Bank(0xb000, data & 0x7F); + break; + case 0x4044: + SetPROM_4K_Bank(0xc000, data & 0x7F); + break; + case 0x4045: + SetPROM_4K_Bank(0xd000, data & 0x7F); + break; + case 0x4046: + SetPROM_4K_Bank(0xe000, data & 0x7F); + break; + case 0x4047: + SetPROM_4K_Bank(0xf000, data & 0x7F); + break; + + case 0x4205: //FDC数据寄存器(DATA)(写???) + // + break; + } + } + + public override byte ReadLow(ushort addr) + { + // DEBUGOUT( "ReadLow - addr= %04x\n", addr ); + + return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + public override void WriteLow(ushort addr, byte data) + { + // DEBUGOUT( "WriteLow - addr= %04x ; dat= %03x\n", addr, data ); + + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + + public override void Write(ushort addr, byte data) + { + // DEBUGOUT( "Write - addr= %04x ; dat= %03x\n", addr, data ); + } + + + public override void HSync(int scanline) + { + // if( (scanline >= 0 && scanline <= 239) ) { + // if( nes.ppu.IsDispON() ) { + // if( irq_enable ) { + // irq_enable = 0; + /// nes.cpu.SetIRQ( IRQ_MAPPER ); + // } + // } + // } + } + + public override void Clock(int cycles) + { + + if (irq_enable != 0) + { + irq_counter -= cycles; + if (irq_counter <= 0) + { + //// irq_counter &= 0xFFFF; + irq_counter += irq_latch; + + if (irq_occur == 0) + { + irq_occur = 0xFF; + if (irq_repeat == 0) + { + irq_enable = 0; + } + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + } + } + + public override void PPU_Latch(ushort addr) + { + // + } + + public override void PPU_ExtLatch(ushort ntbladr, ref byte chr_l, ref byte chr_h, ref byte attr) + { + INT loopy_v = nes.ppu.GetPPUADDR(); + INT loopy_y = nes.ppu.GetTILEY(); + INT tileofs = (PPUREG[0] & PPU.PPU_BGTBL_BIT) << 8; + INT attradr = 0x23C0 + (loopy_v & 0x0C00) + ((loopy_v & 0x0380) >> 4); + INT attrsft = (ntbladr & 0x0040) >> 4; + ArrayRef pNTBL = PPU_MEM_BANK[ntbladr >> 10]; + INT ntbl_x = ntbladr & 0x001F; + INT tileadr, ntb; + + ntb = (ntbladr >> 10) & 3; + + if (ntb == 2) + tileofs |= 0x1000; + // else if(ntb && PPU_SW) + tileofs |= 0x1000; + // else + tileofs |= 0x0000; + + attradr &= 0x3FF; + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + + chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + } + + public override void SaveState(byte[] p) + { + // + } + + public override void LoadState(byte[] p) + { + // + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper175.cs b/Core/VirtualNes.Core/Mapper/Mapper175.cs new file mode 100644 index 0000000..dccd659 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper175.cs @@ -0,0 +1,68 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper175 15-in-1 (Kaiser) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper175 : Mapper + { + BYTE reg_dat; + public Mapper175(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + reg_dat = 0; + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //BYTE Mapper175::Read(WORD addr) + public override void Read(ushort addr, byte data) + { + if (addr == 0xFFFC) + { + SetPROM_16K_Bank(4, reg_dat & 0x0F); + SetPROM_8K_Bank(6, (reg_dat & 0x0F) * 2); + } + } + + //void Mapper175::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8000: + if ((data & 0x04) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA000: + reg_dat = data; + SetPROM_8K_Bank(7, (reg_dat & 0x0F) * 2 + 1); + SetVROM_8K_Bank(reg_dat & 0x0F); + break; + } + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper176.cs b/Core/VirtualNes.Core/Mapper/Mapper176.cs new file mode 100644 index 0000000..2e96916 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper176.cs @@ -0,0 +1,235 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper176 ShuQiYu / HengGe / WaiXing // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + +namespace VirtualNes.Core +{ + public class Mapper176 : Mapper + { + BYTE reg5000; + BYTE reg5001; + BYTE reg5010; + BYTE reg5011; + BYTE reg5013; + BYTE reg5FF1; + BYTE reg5FF2; + BYTE we_sram; + BYTE SBW, sp_rom; + public Mapper176(NES parent) : base(parent) + { + } + + public override void Reset() + { + if (PROM_16K_SIZE > 32) + { + SetPROM_32K_Bank(0, 1, (PROM_8K_SIZE / 2) - 2, (PROM_8K_SIZE / 2) - 1); //For 1M byte Cartridge + } + else + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + // SetPROM_32K_Bank( PROM_8K_SIZE-4, PROM_8K_SIZE-3, PROM_8K_SIZE-2, PROM_8K_SIZE-1 ); + if (VROM_1K_SIZE != 0) SetVROM_8K_Bank(0); + + reg5000 = 0; + reg5001 = 0; + reg5010 = 0; + reg5011 = 0; + reg5013 = 0; + reg5FF1 = 0; + reg5FF2 = 0; + we_sram = 0; + SBW = 0; + sp_rom = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x095D8678 //[ES-0122] Shuang Yue Zhuan (C) + || crc == 0xD5F7AAEF) + { //[ES-XXXX] Shen Feng Jian (C) + sp_rom = 1; + nes.SetSAVERAM_SIZE(32 * 1024); + } + + /* + crc == 0x416C07A1 //[ES-1006] Meng Huan Zhi Xing IV (C) & WXN-梦幻之星 √ + crc == 0x94782FBD //[ES-1057] San Guo Zhi - Xiong Ba Tian Xia (C) + crc == 0xF9863ADF //[ES-1066] Xi Chu Ba Wang (C) + crc == 0xB511C04B //[ES-1071] San Xia Wu Yi (C) + crc == 0x1923A8C5 //[ES-1087] Shui Hu Shen Shou (C) & WXN-水浒神兽(fix) √ + crc == 0x095D8678 //[ES-0122] Shuang Yue Zhuan (C) + crc == 0x8f6ab5ac //WXN-三国忠烈传 √ + crc == 0x99051cb5 //WXN-雄霸天下 √ + crc == 0xf29c8186 //WXN-大富翁2-上海大亨 √ + crc == 0xc768098b //WXN-三侠五义 √ + crc == 0x49f22159 //WXN-超级大富翁 √ + crc == 0xf354d847 //WXN-格兰帝亚 √ + crc == 0x5ee2ef97 //WXN-帝国时代(fix) √ + crc == 0x977d22c3 //WXN-破釜沉舟(fix) √ + crc == 0xf1d803f3 //WXN-西楚霸王(fix) √ + crc == 0x85dd49b6 //WXN-口袋金(fix) √ + crc == 0x97b82f53 //WXN-爆笑三国(fix) √ + crc == 0xce2ea530 //WXN-宠物翡翠(fix) √ + */ + } + + //BYTE Mapper176::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (sp_rom == 1) + { + if (addr >= 0x6000) + { + switch (we_sram) + { + case 0xE4: + case 0xEC: return WRAM[(addr & 0x1FFF) + 0x0000]; + case 0xE5: + case 0xED: return WRAM[(addr & 0x1FFF) + 0x2000]; + case 0xE6: + case 0xEE: return WRAM[(addr & 0x1FFF) + 0x4000]; + case 0xE7: + case 0xEF: return WRAM[(addr & 0x1FFF) + 0x6000]; + default: return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + } + } + return base.ReadLow(addr); + } + + //void Mapper176::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + // DEBUGOUT("Address=%04X Data=%02X\n", addr&0xFFFF, data&0xFF ); + + switch (addr) + { + case 0x5000: + reg5000 = data; + break; + case 0x5001: //[ES-1006] Meng Huan Zhi Xing IV (C) + reg5001 = data; + if (SBW != 0) SetPROM_32K_Bank(reg5001); + break; + case 0x5010: + reg5010 = data; + if (reg5010 == 0x24) SBW = 1; + break; + case 0x5011: + reg5011 = (byte)(data >> 1); + if (SBW != 0) SetPROM_32K_Bank(reg5011); + break; + case 0x5013: + reg5013 = data; + break; + case 0x5ff1: + reg5FF1 = (byte)(data >> 1); + SetPROM_32K_Bank(reg5FF1); + break; + case 0x5ff2: + reg5FF2 = data; + SetVROM_8K_Bank(reg5FF2); + break; + } + + if (sp_rom == 1) + { + if (addr >= 0x6000) + { + switch (we_sram) + { + case 0xE4: //CPU_MEM_BANK + case 0xEC: //CPU_MEM_BANK + WRAM[(addr & 0x1FFF) + 0x0000] = data; + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + case 0xE5: //SRAM + case 0xED: //SRAM + WRAM[(addr & 0x1FFF) + 0x2000] = data; + break; + case 0xE6: + case 0xEE: + WRAM[(addr & 0x1FFF) + 0x4000] = data; + break; + case 0xE7: + case 0xEF: + WRAM[(addr & 0x1FFF) + 0x6000] = data; + break; + default: + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + } + } + } + else + { + if (addr >= 0x6000) + { + // if ( we_sram == 0 ){ + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + // } + } + } + + + } + + //void Mapper176::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // DEBUGOUT("Address=%04X Data=%02X\n", addr&0xFFFF, data&0xFF ); + + if (addr == 0xa000) + { + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + } + if (addr == 0xa001) + { + // we_sram = data & 0x03; + we_sram = data; + } + } + + //void Mapper176::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg5000; + p[1] = reg5001; + p[2] = reg5010; + p[3] = reg5011; + p[4] = reg5013; + p[5] = reg5FF1; + p[6] = reg5FF2; + p[7] = we_sram; + p[8] = SBW; + p[9] = sp_rom; + } + + //void Mapper176::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg5000 = p[0]; + reg5001 = p[1]; + reg5010 = p[2]; + reg5011 = p[3]; + reg5013 = p[4]; + reg5FF1 = p[5]; + reg5FF2 = p[6]; + we_sram = p[7]; + SBW = p[8]; + sp_rom = p[9]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper178.cs b/Core/VirtualNes.Core/Mapper/Mapper178.cs new file mode 100644 index 0000000..fe38bbc --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper178.cs @@ -0,0 +1,75 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper178 Education / WaiXing_FS305 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper178 : Mapper + { + BYTE[] reg = new byte[3]; + BYTE banknum; + BYTE OP_rom; + public Mapper178(NES parent) : base(parent) + { + } + + public override void Reset() + { + reg[0] = 0; + reg[1] = 0; + reg[2] = 0; + banknum = 0; + SetBank_CPU(); + OP_rom = 0; + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x925926BC //[ES-XXXX] Kou Dai Zuan Shi Zhi Chong Wu Xiao Jing Ling 2 (C) + || crc == 0xB0B13DBD) //[ES-XXXX] Chong Wu Fei Cui Zhi Chong Wu Xiao Jing Ling IV (C) + { + OP_rom = 1; + } + } + + //void Mapper178::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + // if( addr >= 0x4000 && addr <= 0x5FFF ) DEBUGOUT("Address=%04X Data=%02X\n", addr&0xFFFF, data&0xFF ); + if (addr == 0x4800) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + else if (addr == 0x4801) + { + reg[0] = (byte)((data >> 1) & 0x0F); + if (OP_rom != 0) reg[0] = (byte)(data << 2); + SetBank_CPU(); + } + else if (addr == 0x4802) + { + reg[1] = (byte)(data << 2); + if (OP_rom != 0) reg[1] = data; + SetBank_CPU(); + } + else if (addr == 0x4803) + { + //unknown + reg[2] = data; + } + else if (addr >= 0x6000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + } + + void SetBank_CPU() + { + banknum = (byte)(reg[0] | reg[1]); + // DEBUGOUT("Bank=%02X\n", banknum&0xFF ); + SetPROM_32K_Bank(banknum); + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper180.cs b/Core/VirtualNes.Core/Mapper/Mapper180.cs new file mode 100644 index 0000000..abd4dfc --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper180.cs @@ -0,0 +1,33 @@ +////////////////////////////////////////////// +// Mapper180 Nichibutsu // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper180 : Mapper + { + public Mapper180(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper180::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_16K_Bank(6, data & 0x07); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper181.cs b/Core/VirtualNes.Core/Mapper/Mapper181.cs new file mode 100644 index 0000000..6d10bcc --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper181.cs @@ -0,0 +1,34 @@ +/////////////////////////////////////////////////////// +// Mapper181 Hacker International Type2 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper181 : Mapper + { + public Mapper181(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper181::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + //DEBUGOUT( "$%04X:$%02X\n", addr, data ); + if (addr == 0x4120) + { + SetPROM_32K_Bank((data & 0x08) >> 3); + SetVROM_8K_Bank(data & 0x07); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper182.cs b/Core/VirtualNes.Core/Mapper/Mapper182.cs new file mode 100644 index 0000000..8dd938a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper182.cs @@ -0,0 +1,121 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper182 PC-SuperDonkeyKong // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper182 : Mapper + { + BYTE reg; + BYTE irq_enable; + BYTE irq_counter; + public Mapper182(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + reg = 0; + irq_enable = 0; + irq_counter = 0; + } + + //void Mapper182::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF003) + { + case 0x8001: + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0xA000: + reg = (byte)(data & 0x07); + break; + case 0xC000: + switch (reg) + { + case 0: + SetVROM_1K_Bank(0, (data & 0xFE) + 0); + SetVROM_1K_Bank(1, (data & 0xFE) + 1); + break; + case 1: + SetVROM_1K_Bank(5, data); + break; + case 2: + SetVROM_1K_Bank(2, (data & 0xFE) + 0); + SetVROM_1K_Bank(3, (data & 0xFE) + 1); + break; + case 3: + SetVROM_1K_Bank(7, data); + break; + case 4: + SetPROM_8K_Bank(4, data); + break; + case 5: + SetPROM_8K_Bank(5, data); + break; + case 6: + SetVROM_1K_Bank(4, data); + break; + case 7: + SetVROM_1K_Bank(6, data); + break; + } + break; + case 0xE003: + irq_enable = data; + irq_counter = data; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper182::HSync(INT scanline) + public override void HSync(int scanline) + { + if (irq_enable != 0) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if ((--irq_counter) == 0) + { + irq_enable = 0; + irq_counter = 0; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + + //void Mapper182::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg; + p[1] = irq_enable; + p[2] = irq_counter; + } + + //void Mapper182::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg = p[0]; + irq_enable = p[1]; + irq_counter = p[2]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper183.cs b/Core/VirtualNes.Core/Mapper/Mapper183.cs new file mode 100644 index 0000000..1406401 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper183.cs @@ -0,0 +1,199 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper183 Gimmick (Bootleg) // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper183 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE irq_enable; + INT irq_counter; + public Mapper183(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + irq_enable = 0; + irq_counter = 0; + } + + //void Mapper183::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr) + { + case 0x8800: + SetPROM_8K_Bank(4, data); + break; + case 0xA800: + SetPROM_8K_Bank(5, data); + break; + case 0xA000: + SetPROM_8K_Bank(6, data); + break; + + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB004: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB008: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB00C: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC004: + reg[2] = (byte)((reg[2] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC008: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC00C: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD004: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD008: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD00C: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE004: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE008: + reg[7] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE00C: + reg[7] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0x9008: + if (data == 1) + { + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + } + break; + + case 0x9800: + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else if (data == 3) SetVRAM_Mirror(VRAM_MIRROR4H); + break; + + case 0xF000: + irq_counter = (irq_counter & 0xFF00) | data; + break; + case 0xF004: + irq_counter = (irq_counter & 0x00FF) | (data << 8); + break; + case 0xF008: + irq_enable = data; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper183::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((irq_enable & 0x02) != 0) + { + if (irq_counter <= 113) + { + irq_counter = 0; + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter -= 113; + } + } + } + + //void Mapper183::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = irq_enable; + BitConverter.GetBytes(irq_counter).CopyTo(p, 9); + //*((INT*)&p[9]) = irq_counter; + } + + //void Mapper183::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + irq_enable = p[8]; + irq_counter = BitConverter.ToInt32(p, 9); + //irq_counter = *((INT*)&p[9]); + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper185.cs b/Core/VirtualNes.Core/Mapper/Mapper185.cs new file mode 100644 index 0000000..11df565 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper185.cs @@ -0,0 +1,66 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper185 Character disable protect // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper185 : Mapper + { + BYTE patch; + public Mapper185(NES parent) : base(parent) + { + } + + public override void Reset() + + { + switch (PROM_16K_SIZE) + { + case 1: // 16K only + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + break; + case 2: // 32K + SetPROM_32K_Bank(0); + break; + } + + for (INT i = 0; i < 0x400; i++) + { + VRAM[0x800 + i] = 0xFF; + } + patch = 0; + + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0xb36457c7) + { // Spy vs Spy(J) + patch = 1; + } + } + + //void Mapper185::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (((patch == 0) && ((data & 0x03) != 0)) || ((patch != 0) && data == 0x21)) + { + SetVROM_8K_Bank(0); + } + else + { + SetVRAM_1K_Bank(0, 2); // use vram bank 2 + SetVRAM_1K_Bank(1, 2); + SetVRAM_1K_Bank(2, 2); + SetVRAM_1K_Bank(3, 2); + SetVRAM_1K_Bank(4, 2); + SetVRAM_1K_Bank(5, 2); + SetVRAM_1K_Bank(6, 2); + SetVRAM_1K_Bank(7, 2); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper187.cs b/Core/VirtualNes.Core/Mapper/Mapper187.cs new file mode 100644 index 0000000..84a9007 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper187.cs @@ -0,0 +1,373 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper187 Street Fighter Zero 2 97 // +////////////////////////////////////////////////////////////////////////// +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper187 : Mapper + { + + BYTE[] prg = new byte[4]; + INT[] chr = new int[8]; + BYTE[] bank = new byte[8]; + + BYTE ext_mode; + BYTE chr_mode; + BYTE ext_enable; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_occur; + BYTE last_write; + public Mapper187(NES parent) : base(parent) + { + } + + public override void Reset() + + { + INT i; + + for (i = 0; i < 8; i++) + { + chr[i] = 0x00; + bank[i] = 0x00; + } + + prg[0] = (byte)(PROM_8K_SIZE - 4); + prg[1] = (byte)(PROM_8K_SIZE - 3); + prg[2] = (byte)(PROM_8K_SIZE - 2); + prg[3] = (byte)(PROM_8K_SIZE - 1); + SetBank_CPU(); + + ext_mode = 0; + chr_mode = 0; + ext_enable = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + + last_write = 0; + + nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER); + } + + //BYTE Mapper187::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + switch (last_write & 0x03) + { + case 0: + return 0x83; + case 1: + return 0x83; + case 2: + return 0x42; + case 3: + return 0x00; + } + return 0; + } + + //void Mapper187::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + last_write = data; + if (addr == 0x5000) + { + ext_mode = data; + if ((data & 0x80) != 0) + { + if ((data & 0x20) != 0) + { + prg[0] = (byte)(((data & 0x1E) << 1) + 0); + prg[1] = (byte)(((data & 0x1E) << 1) + 1); + prg[2] = (byte)(((data & 0x1E) << 1) + 2); + prg[3] = (byte)(((data & 0x1E) << 1) + 3); + } + else + { + prg[2] = (byte)(((data & 0x1F) << 1) + 0); + prg[3] = (byte)(((data & 0x1F) << 1) + 1); + } + } + else + { + prg[0] = bank[6]; + prg[1] = bank[7]; + prg[2] = (byte)(PROM_8K_SIZE - 2); + prg[3] = (byte)(PROM_8K_SIZE - 1); + } + SetBank_CPU(); + } + } + + //void Mapper187::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + last_write = data; + switch (addr) + { + case 0x8003: + ext_enable = 0xFF; + // if( (data&0x80) != (chr_mode&0x80) ) { + // for( INT i = 0; i < 4; i++ ) { + // INT temp = chr[i]; + // chr[i] = chr[i+4]; + // chr[i+4] = temp; + // } + // SetBank_PPU(); + // } + chr_mode = data; + if ((data & 0xF0) == 0) + { + prg[2] = (byte)(PROM_8K_SIZE - 2); + SetBank_CPU(); + } + break; + + case 0x8000: + ext_enable = 0; + // if( (data&0x80) != (chr_mode&0x80) ) { + // for( INT i = 0; i < 4; i++ ) { + // INT temp = chr[i]; + // chr[i] = chr[i+4]; + // chr[i+4] = temp; + // } + // SetBank_PPU(); + // } + chr_mode = data; + break; + + case 0x8001: + if (ext_enable == 0) + { + switch (chr_mode & 7) + { + case 0: + data &= 0xFE; + chr[4] = (INT)data + 0x100; + chr[5] = (INT)data + 0x100 + 1; + // chr[0+((chr_mode&0x80)?4:0)] = data; + // chr[1+((chr_mode&0x80)?4:0)] = data+1; + SetBank_PPU(); + break; + case 1: + data &= 0xFE; + chr[6] = (INT)data + 0x100; + chr[7] = (INT)data + 0x100 + 1; + // chr[2+((chr_mode&0x80)?4:0)] = data; + // chr[3+((chr_mode&0x80)?4:0)] = data+1; + SetBank_PPU(); + break; + case 2: + chr[0] = data; + // chr[0+((chr_mode&0x80)?0:4)] = data; + SetBank_PPU(); + break; + case 3: + chr[1] = data; + // chr[1+((chr_mode&0x80)?0:4)] = data; + SetBank_PPU(); + break; + case 4: + chr[2] = data; + // chr[2+((chr_mode&0x80)?0:4)] = data; + SetBank_PPU(); + break; + case 5: + chr[3] = data; + // chr[3+((chr_mode&0x80)?0:4)] = data; + SetBank_PPU(); + break; + case 6: + if ((ext_mode & 0xA0) != 0xA0) + { + prg[0] = data; + SetBank_CPU(); + } + break; + case 7: + if ((ext_mode & 0xA0) != 0xA0) + { + prg[1] = data; + SetBank_CPU(); + } + break; + default: + break; + } + } + else + { + switch (chr_mode) + { + case 0x2A: + prg[1] = 0x0F; + break; + case 0x28: + prg[2] = 0x17; + break; + case 0x26: + break; + default: + break; + } + SetBank_CPU(); + } + bank[chr_mode & 7] = data; + break; + + case 0xA000: + if ((data & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + break; + + case 0xC000: + irq_counter = data; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC001: + irq_latch = data; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE000: + case 0xE002: + irq_enable = 0; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + case 0xE003: + irq_enable = 1; + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper187::Clock(INT cycles) + public override void Clock(int cycles) + { + // if( irq_occur ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + // void Mapper187::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (irq_counter == 0) + { + irq_counter--; + irq_enable = 0; + irq_occur = 0xFF; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter--; + } + } + } + } + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg[0], prg[1], prg[2], prg[3]); + } + + void SetBank_PPU() + { + SetVROM_8K_Bank(chr[0], chr[1], chr[2], chr[3], + chr[4], chr[5], chr[6], chr[7]); + } + + //void Mapper187::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + int i; + + for (i = 0; i < 4; i++) + { + p[i] = prg[i]; + } + for (i = 0; i < 8; i++) + { + p[4 + i] = bank[i]; + } + for (i = 0; i < 8; i++) + { + //*((INT*)&p[12 + i * sizeof(INT)]) = chr[i]; + BitConverter.GetBytes(chr[i]).CopyTo(p, 12 + i * sizeof(int)); + } + + p[44] = ext_mode; + p[45] = chr_mode; + p[46] = ext_enable; + p[47] = irq_enable; + p[48] = irq_counter; + p[49] = irq_latch; + p[50] = irq_occur; + p[51] = last_write; + } + + //void Mapper187::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + int i; + + for (i = 0; i < 4; i++) + { + prg[i] = p[i]; + } + for (i = 0; i < 8; i++) + { + bank[i] = p[4 + i]; + } + for (i = 0; i < 8; i++) + { + chr[i] = BitConverter.ToInt32(p, 12 + i * sizeof(int)); + //chr[i] = *((INT*)&p[12 + i * sizeof(INT)]); + } + ext_mode = p[44]; + chr_mode = p[45]; + ext_enable = p[46]; + irq_enable = p[47]; + irq_counter = p[48]; + irq_latch = p[49]; + irq_occur = p[50]; + last_write = p[51]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper188.cs b/Core/VirtualNes.Core/Mapper/Mapper188.cs new file mode 100644 index 0000000..b4807d5 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper188.cs @@ -0,0 +1,56 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper188 Bandai Karaoke Studio // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper188 : Mapper + { + public Mapper188(NES parent) : base(parent) + { + } + public override void Reset() + { + if (PROM_8K_SIZE > 16) + { + SetPROM_32K_Bank(0, 1, 14, 15); + } + else + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + //void Mapper188::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (data != 0) + { + if ((data & 0x10) != 0) + { + data &= 0x07; + SetPROM_16K_Bank(4, data); + } + else + { + SetPROM_16K_Bank(4, data + 8); + } + } + else + { + if (PROM_8K_SIZE == 0x10) + { + SetPROM_16K_Bank(4, 7); + } + else + { + SetPROM_16K_Bank(4, 8); + } + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper189.cs b/Core/VirtualNes.Core/Mapper/Mapper189.cs new file mode 100644 index 0000000..22eaf8f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper189.cs @@ -0,0 +1,319 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper189 Street Fighter 2/Yoko version // +// 快打傅説 Street Fighter IV (GOUDER) // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper189 : Mapper + { + BYTE patch; + + BYTE[] reg = new BYTE[2]; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + + // SF4 + BYTE[] protect_dat = new byte[4]; + BYTE lwd; + public Mapper189(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(PROM_8K_SIZE - 4, PROM_8K_SIZE - 3, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + reg[0] = reg[1] = 0; + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + + for (INT i = 0; i < 4; i++) + { + protect_dat[i] = 0; + } + lwd = 0xFF; + + patch = 0; + uint crc = nes.rom.GetPROM_CRC(); + if (crc == 0x20ca2ad3) + { // Street Fighter IV (GOUDER) + patch = 1; + SetPROM_32K_Bank(0); + + // $4000-$5FFF + SetPROM_Bank(2, XRAM, BANKTYPE_ROM); + } + } + + //void Mapper189::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0xFF00) == 0x4100) + { + // Street Fighter 2 YOKO + SetPROM_32K_Bank((data & 0x30) >> 4); + } + else if ((addr & 0xFF00) == 0x6100) + { + // Master Fighter 2 + SetPROM_32K_Bank(data & 0x03); + } + + if (patch != 0) + { + // Street Fighter IV (GOUDER) + BYTE[] a5000xordat = new byte[256]{ + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x49, 0x19, 0x09, 0x59, 0x49, 0x19, 0x09, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x51, 0x41, 0x11, 0x01, 0x51, 0x41, 0x11, 0x01, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x49, 0x19, 0x09, 0x59, 0x49, 0x19, 0x09, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x51, 0x41, 0x11, 0x01, 0x51, 0x41, 0x11, 0x01, + 0x00, 0x10, 0x40, 0x50, 0x00, 0x10, 0x40, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x18, 0x48, 0x58, 0x08, 0x18, 0x48, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x40, 0x50, 0x00, 0x10, 0x40, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x08, 0x18, 0x48, 0x58, 0x08, 0x18, 0x48, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x58, 0x48, 0x18, 0x08, 0x58, 0x48, 0x18, 0x08, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x50, 0x40, 0x10, 0x00, 0x50, 0x40, 0x10, 0x00, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x58, 0x48, 0x18, 0x08, 0x58, 0x48, 0x18, 0x08, + 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x50, 0x40, 0x10, 0x00, 0x50, 0x40, 0x10, 0x00, + 0x01, 0x11, 0x41, 0x51, 0x01, 0x11, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x19, 0x49, 0x59, 0x09, 0x19, 0x49, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x11, 0x41, 0x51, 0x01, 0x11, 0x41, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x19, 0x49, 0x59, 0x09, 0x19, 0x49, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + if ((addr >= 0x4800) && (addr <= 0x4FFF)) + { + SetPROM_32K_Bank(((data & 0x10) >> 3) + (data & 0x1)); + + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x20) != 0) + SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + } + if ((addr >= 0x5000) && (addr <= 0x57FF)) + { + lwd = data; + } + if ((addr >= 0x5800) && (addr <= 0x5FFF)) + { + // XRAM[0x1000+(addr & 3)] = + // $5800 "JMP $xxxx" write + XRAM[0x1800 + (addr & 3)] = + protect_dat[addr & 3] = (byte)(data ^ a5000xordat[lwd]); + } + } + } + + //void Mapper189::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_PPU(); + break; + + case 0x8001: + reg[1] = data; + SetBank_PPU(); + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + } + break; + + case 0xA000: + if ((data & 0x01) != 0) + SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + + case 0xC000: + irq_counter = data; + break; + case 0xC001: + irq_latch = data; + break; + case 0xE000: + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + irq_enable = 0xFF; + break; + } + } + + //void Mapper189::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((--irq_counter) == 0) + { + // if( !(irq_counter--) ) { + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_PPU() + { + if (patch != 0) + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + else + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(0, chr4 & 0x07); + SetCRAM_1K_Bank(1, chr5 & 0x07); + SetCRAM_1K_Bank(2, chr6 & 0x07); + SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(4, chr4 & 0x07); + SetCRAM_1K_Bank(5, chr5 & 0x07); + SetCRAM_1K_Bank(6, chr6 & 0x07); + SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + } + + //void Mapper189::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = chr01; + p[3] = chr23; + p[4] = chr4; + p[5] = chr5; + p[6] = chr6; + p[7] = chr7; + p[8] = irq_enable; + p[9] = irq_counter; + p[10] = irq_latch; + + p[16] = protect_dat[0]; + p[17] = protect_dat[1]; + p[18] = protect_dat[2]; + p[19] = protect_dat[3]; + p[20] = lwd; + } + + //void Mapper189::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + chr01 = p[2]; + chr23 = p[3]; + chr4 = p[4]; + chr5 = p[5]; + chr6 = p[6]; + chr7 = p[7]; + + irq_enable = p[8]; + irq_counter = p[9]; + irq_latch = p[10]; + + protect_dat[0] = p[16]; + protect_dat[1] = p[17]; + protect_dat[2] = p[18]; + protect_dat[3] = p[19]; + lwd = p[20]; + } + + public override bool IsStateSave() + { + return true; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper190.cs b/Core/VirtualNes.Core/Mapper/Mapper190.cs new file mode 100644 index 0000000..fb95f17 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper190.cs @@ -0,0 +1,315 @@ +////////////////////////////////////////////////////////////// +// Mapper190 Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper190 : Mapper + { + BYTE cbase; /* PowerOn OR RESET : cbase=0 */ + BYTE mp190_lcchk; /* PowerOn OR RESET */ + BYTE mp190_lcmd; + BYTE mp190_cmd; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE lowoutdata; + public Mapper190(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + // DWORD crc = nes.rom.GetPROM_CRC(); + // if( crc == 0x6F3D187A ) { + // Temp_Buf=0; //Kof96 + // } else { + // Temp_Buf=1; //ST97 + // } + + irq_enable = 0; + irq_counter = 0; + cbase = 0; /* PowerOn OR RESET : cbase=0 */ + mp190_lcchk = 0; /* PowerOn OR RESET */ + mp190_lcmd = 1; + } + + //void Mapper190::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + /* For Initial Copy Protect check (KOF'96) */ + if (addr == 0x5000) + { + mp190_lcmd = data; + switch (data) + { + case 0xE0: + SetPROM_32K_Bank(0); + break; + case 0xEE: + SetPROM_32K_Bank(3); + break; + } + } + if ((addr == 0x5001) && (mp190_lcmd == 0x00)) + { + SetPROM_32K_Bank(7); + } + if (addr == 0x5080) + { + switch (data) + { + case 0x1: lowoutdata = 0x83; break; + case 0x2: lowoutdata = 0x42; break; + case 0x3: lowoutdata = 0x00; break; + } + } + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + + //BYTE Mapper190::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + switch (addr) + { + case 0x5000: + return lowoutdata; + default: + return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + } + + //void Mapper190::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE003) + { + case 0x8000: + mp190_cmd = data; + if ((mp190_cmd & 0x80) != 0) + cbase = 1; + else + cbase = 0; + break; + case 0x8003: /* for Street Fighter Zero 2 '97 */ + mp190_lcchk = data; + switch (data) + { + case 0x28: + SetPROM_8K_Bank(4, 0x1F); + SetPROM_8K_Bank(5, 0x1F); + SetPROM_8K_Bank(6, 0x17); + SetPROM_8K_Bank(7, 0x1F); + break; + case 0x2A: + SetPROM_8K_Bank(4, 0x1F); + SetPROM_8K_Bank(5, 0x0F); + SetPROM_8K_Bank(6, 0x17); + SetPROM_8K_Bank(7, 0x1F); + break; + case 0x06: + SetPROM_8K_Bank(4, 0x1E); + SetPROM_8K_Bank(5, 0x1F); + SetPROM_8K_Bank(6, 0x1F); + SetPROM_8K_Bank(7, 0x1F); + break; + } + break; + case 0x8001: + if ((mp190_lcchk == 0x6) || (mp190_lcmd == 0x0)) + { + switch (mp190_cmd & 0x07) + { + case 0: + if (cbase == 0) + { + SetVROM_1K_Bank(0, data + 0x100); + SetVROM_1K_Bank(1, data + 0x101); + } + else + { + SetVROM_1K_Bank(4, data + 0x100); + SetVROM_1K_Bank(5, data + 0x101); + } + break; + case 1: + if (cbase == 0) + { + SetVROM_1K_Bank(2, data + 0x100); + SetVROM_1K_Bank(3, data + 0x101); + } + else + { + SetVROM_1K_Bank(6, data + 0x100); + SetVROM_1K_Bank(7, data + 0x101); + } + break; + case 2: + if (cbase == 0) + { + SetVROM_1K_Bank(4, data); + } + else + { + SetVROM_1K_Bank(0, data); + } + break; + case 3: + if (cbase == 0) + { + SetVROM_1K_Bank(5, data); + } + else + { + SetVROM_1K_Bank(1, data); + } + break; + case 4: + if (cbase == 0) + { + SetVROM_1K_Bank(6, data); + } + else + { + SetVROM_1K_Bank(2, data); + } + break; + case 5: + if (cbase == 0) + { + SetVROM_1K_Bank(7, data); + } + else + { + SetVROM_1K_Bank(3, data); + } + break; + case 6: + data = (byte)(data & ((PROM_8K_SIZE * 2) - 1)); + if ((mp190_lcmd & 0x40) != 0) + { + SetPROM_8K_Bank(6, data); + SetPROM_8K_Bank(4, (PROM_8K_SIZE - 1) * 2); + } + else + { + SetPROM_8K_Bank(4, data); + SetPROM_8K_Bank(6, (PROM_8K_SIZE - 1) * 2); + } + break; + case 7: + data = (byte)(data & ((PROM_8K_SIZE * 2) - 1)); + if ((mp190_lcmd & 0x40) != 0) + { + SetPROM_8K_Bank(5, data); + SetPROM_8K_Bank(4, (PROM_8K_SIZE - 1) * 2); + } + else + { + SetPROM_8K_Bank(5, data); + SetPROM_8K_Bank(6, (PROM_8K_SIZE - 1) * 2); + } + break; + } + } + break; + case 0xA000: + if ((data & 0x1) == 0x1) + SetVRAM_Mirror(VRAM_HMIRROR); + else + SetVRAM_Mirror(VRAM_VMIRROR); + break; + case 0xA001: + break; + case 0xC000: + irq_counter = (byte)(data - 1); + break; + case 0xC001: + irq_latch = (byte)(data - 1); + break; + case 0xC002: + irq_counter = data; + break; + case 0xC003: + irq_latch = data; + break; + case 0xE000: + irq_counter = irq_latch; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + irq_enable = 1; + break; + case 0xE002: + irq_counter = irq_latch; + irq_enable = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE003: + irq_enable = 1; + irq_counter = irq_counter; + break; + } + } + + //void Mapper190::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + // nes.cpu.IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + //void Mapper190::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = irq_enable; + p[1] = irq_counter; + p[2] = irq_latch; + + p[3] = cbase; + p[4] = mp190_lcchk; + p[5] = mp190_lcmd; + p[6] = mp190_cmd; + p[7] = lowoutdata; + } + + //void Mapper190::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + irq_enable = p[0]; + irq_counter = p[1]; + irq_latch = p[2]; + + cbase = p[3]; + mp190_lcchk = p[4]; + mp190_lcmd = p[5]; + mp190_cmd = p[6]; + lowoutdata = p[7]; + } + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper191.cs b/Core/VirtualNes.Core/Mapper/Mapper191.cs new file mode 100644 index 0000000..f768698 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper191.cs @@ -0,0 +1,136 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper191 SACHEN Super Cartridge Xin1 (Ver.1-9) // +// SACHEN Q-BOY Support // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper191 : Mapper + { + BYTE[] reg = new BYTE[8]; + BYTE prg0, prg1; + BYTE chr0, chr1, chr2, chr3; + BYTE highbank; + public Mapper191(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + // prg1 = 1; + SetBank_CPU(); + + chr0 = 0; + chr1 = 0; + chr2 = 0; + chr3 = 0; + highbank = 0; + SetBank_PPU(); + } + + //void Mapper191::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x4100: + reg[0] = data; + break; + case 0x4101: + reg[1] = data; + switch (reg[0]) + { + case 0: + chr0 = (byte)(data & 7); + SetBank_PPU(); + break; + case 1: + chr1 = (byte)(data & 7); + SetBank_PPU(); + break; + case 2: + chr2 = (byte)(data & 7); + SetBank_PPU(); + break; + case 3: + chr3 = (byte)(data & 7); + SetBank_PPU(); + break; + case 4: + highbank = (byte)(data & 7); + SetBank_PPU(); + break; + case 5: + prg0 = (byte)(data & 7); + SetBank_CPU(); + break; + case 7: + if ((data & 0x02) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + break; + } + break; + } + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg0); + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + SetVROM_1K_Bank(0, (((highbank << 3) + chr0) << 2) + 0); + SetVROM_1K_Bank(1, (((highbank << 3) + chr0) << 2) + 1); + SetVROM_1K_Bank(2, (((highbank << 3) + chr1) << 2) + 2); + SetVROM_1K_Bank(3, (((highbank << 3) + chr1) << 2) + 3); + SetVROM_1K_Bank(4, (((highbank << 3) + chr2) << 2) + 0); + SetVROM_1K_Bank(5, (((highbank << 3) + chr2) << 2) + 1); + SetVROM_1K_Bank(6, (((highbank << 3) + chr3) << 2) + 2); + SetVROM_1K_Bank(7, (((highbank << 3) + chr3) << 2) + 3); + } + } + + public override bool IsStateSave() + { + return true; + } + + + + //void Mapper191::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = prg0; + p[1] = chr0; + p[2] = chr1; + p[3] = chr2; + p[4] = chr3; + p[5] = highbank; + } + + //void Mapper191::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + prg0 = p[0]; + chr0 = p[1]; + chr1 = p[2]; + chr2 = p[3]; + chr3 = p[4]; + highbank = p[5]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper192.cs b/Core/VirtualNes.Core/Mapper/Mapper192.cs new file mode 100644 index 0000000..3e4f884 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper192.cs @@ -0,0 +1,322 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper192 WaiXingTypeC Base ON Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper192 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + public Mapper192(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (byte i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + } + + + //BYTE Mapper192::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + return XRAM[addr - 0x4000]; + } + else + { + return base.ReadLow(addr); + } + } + + //void Mapper192::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + XRAM[addr - 0x4000] = data; + } + else + { + base.WriteLow(addr, data); + } + } + + //void Mapper192::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = data; + SetBank_PPU(); + break; + case 0x01: + chr23 = data; + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + break; + } + + } + + //void Mapper192::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + // SetVROM_8K_Bank( chr4, chr5, chr6, chr7, + // chr01, chr01+1, chr23, chr23+1 ); + SetBank_PPUSUB(4, chr01 + 0); + SetBank_PPUSUB(5, chr01 + 1); + SetBank_PPUSUB(6, chr23 + 0); + SetBank_PPUSUB(7, chr23 + 1); + SetBank_PPUSUB(0, chr4); + SetBank_PPUSUB(1, chr5); + SetBank_PPUSUB(2, chr6); + SetBank_PPUSUB(3, chr7); + } + else + { + // SetVROM_8K_Bank( chr01, chr01+1, chr23, chr23+1, + // chr4, chr5, chr6, chr7 ); + SetBank_PPUSUB(0, chr01 + 0); + SetBank_PPUSUB(1, chr01 + 1); + SetBank_PPUSUB(2, chr23 + 0); + SetBank_PPUSUB(3, chr23 + 1); + SetBank_PPUSUB(4, chr4); + SetBank_PPUSUB(5, chr5); + SetBank_PPUSUB(6, chr6); + SetBank_PPUSUB(7, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0)); + SetCRAM_1K_Bank(5, (chr01 + 1)); + SetCRAM_1K_Bank(6, (chr23 + 0)); + SetCRAM_1K_Bank(7, (chr23 + 1)); + SetCRAM_1K_Bank(0, chr4); + SetCRAM_1K_Bank(1, chr5); + SetCRAM_1K_Bank(2, chr6); + SetCRAM_1K_Bank(3, chr7); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0)); + SetCRAM_1K_Bank(1, (chr01 + 1)); + SetCRAM_1K_Bank(2, (chr23 + 0)); + SetCRAM_1K_Bank(3, (chr23 + 1)); + SetCRAM_1K_Bank(4, chr4); + SetCRAM_1K_Bank(5, chr5); + SetCRAM_1K_Bank(6, chr6); + SetCRAM_1K_Bank(7, chr7); + } + } + } + + void SetBank_PPUSUB(int bank, int page) + { + if ((page & 0xFC) == 0x08) + { + SetCRAM_1K_Bank((byte)bank, page); + } + else + { + SetVROM_1K_Bank((byte)bank, page); + } + } + + //void Mapper192::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + } + + //void Mapper192::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper193.cs b/Core/VirtualNes.Core/Mapper/Mapper193.cs new file mode 100644 index 0000000..7e8de99 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper193.cs @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper193 MEGA SOFT (NTDEC) : Fighting Hero // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + +namespace VirtualNes.Core +{ + public class Mapper193 : Mapper + { + public Mapper193(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_32K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper193::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + switch (addr) + { + case 0x6000: + SetVROM_2K_Bank(0, ((data >> 1) & 0x7e) + 0); + SetVROM_2K_Bank(2, ((data >> 1) & 0x7e) + 1); + break; + case 0x6001: + SetVROM_2K_Bank(4, data >> 1); + break; + case 0x6002: + SetVROM_2K_Bank(6, data >> 1); + break; + case 0x6003: + SetPROM_32K_Bank(data); + break; + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper194.cs b/Core/VirtualNes.Core/Mapper/Mapper194.cs new file mode 100644 index 0000000..ddba71a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper194.cs @@ -0,0 +1,27 @@ +//////////////////////////////////////////// +// Mapper194 迷宮寺院ダババ // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper194 : Mapper + { + public Mapper194(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(PROM_32K_SIZE - 1); + } + + //void Mapper194::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_8K_Bank(3, data); + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper195.cs b/Core/VirtualNes.Core/Mapper/Mapper195.cs new file mode 100644 index 0000000..3e0ce7a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper195.cs @@ -0,0 +1,323 @@ +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper195 : Mapper + { + + BYTE[] reg = new BYTE[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + public Mapper195(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + } + + + //BYTE Mapper195::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + return XRAM[addr - 0x4000]; + } + else + { + return base.ReadLow(addr); + } + } + + //void Mapper195::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + XRAM[addr - 0x4000] = data; + } + else + { + base.WriteLow(addr, data); + } + } + + //void Mapper195::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes.GetScanline(), nes.cpu.GetTotalCycles() ); + + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = data; + SetBank_PPU(); + break; + case 0x01: + chr23 = data; + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + break; + } + + } + + //void Mapper195::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + // SetVROM_8K_Bank( chr4, chr5, chr6, chr7, + // chr01, chr01+1, chr23, chr23+1 ); + SetBank_PPUSUB(4, chr01 + 0); + SetBank_PPUSUB(5, chr01 + 1); + SetBank_PPUSUB(6, chr23 + 0); + SetBank_PPUSUB(7, chr23 + 1); + SetBank_PPUSUB(0, chr4); + SetBank_PPUSUB(1, chr5); + SetBank_PPUSUB(2, chr6); + SetBank_PPUSUB(3, chr7); + } + else + { + // SetVROM_8K_Bank( chr01, chr01+1, chr23, chr23+1, + // chr4, chr5, chr6, chr7 ); + SetBank_PPUSUB(0, chr01 + 0); + SetBank_PPUSUB(1, chr01 + 1); + SetBank_PPUSUB(2, chr23 + 0); + SetBank_PPUSUB(3, chr23 + 1); + SetBank_PPUSUB(4, chr4); + SetBank_PPUSUB(5, chr5); + SetBank_PPUSUB(6, chr6); + SetBank_PPUSUB(7, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0)); + SetCRAM_1K_Bank(5, (chr01 + 1)); + SetCRAM_1K_Bank(6, (chr23 + 0)); + SetCRAM_1K_Bank(7, (chr23 + 1)); + SetCRAM_1K_Bank(0, chr4); + SetCRAM_1K_Bank(1, chr5); + SetCRAM_1K_Bank(2, chr6); + SetCRAM_1K_Bank(3, chr7); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0)); + SetCRAM_1K_Bank(1, (chr01 + 1)); + SetCRAM_1K_Bank(2, (chr23 + 0)); + SetCRAM_1K_Bank(3, (chr23 + 1)); + SetCRAM_1K_Bank(4, chr4); + SetCRAM_1K_Bank(5, chr5); + SetCRAM_1K_Bank(6, chr6); + SetCRAM_1K_Bank(7, chr7); + } + } + } + + void SetBank_PPUSUB(int bank, int page) + { + if (page <= 3) + { + SetCRAM_1K_Bank((byte)bank, page); + } + else + { + SetVROM_1K_Bank((byte)bank, page); + } + } + + //void Mapper195::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + } + + //void Mapper195::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (byte i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper198.cs b/Core/VirtualNes.Core/Mapper/Mapper198.cs new file mode 100644 index 0000000..d8752cb --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper198.cs @@ -0,0 +1,205 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper198 Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper198 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + + BYTE[] adr5000buf = new BYTE[1024 * 4]; + public Mapper198(NES parent) : base(parent) + { + } + + public override void Reset() + + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + } + + //void Mapper198::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr > 0x4018 && addr < 0x6000) + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + else + adr5000buf[addr & 0xFFF] = data; + } + //BYTE Mapper198::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (addr > 0x4018 && addr < 0x6000) + return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + else + return adr5000buf[addr & 0xFFF]; + } + + //void Mapper198::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + if (data >= 0x50) data &= 0x4F; + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + break; + case 0xC001: + reg[5] = data; + break; + case 0xE000: + reg[6] = data; + break; + case 0xE001: + reg[7] = data; + break; + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + void SetBank_PPU() + { + + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + } + + //void Mapper198::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + } + + //void Mapper198::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + } + + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper199.cs b/Core/VirtualNes.Core/Mapper/Mapper199.cs new file mode 100644 index 0000000..7981595 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper199.cs @@ -0,0 +1,318 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper199 WaiXingTypeG Base ON Nintendo MMC3 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper199 : Mapper + { + BYTE[] reg = new byte[8]; + BYTE[] prg = new byte[4]; + BYTE[] chr = new byte[8]; + BYTE we_sram; + + BYTE JMaddr; + BYTE[] JMaddrDAT = new BYTE[3]; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + + public Mapper199(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (byte i = 0; i < 8; i++) + { + reg[i] = 0x00; + chr[i] = i; + } + prg[0] = 0x00; + prg[1] = 0x01; + prg[2] = (byte)(PROM_8K_SIZE - 2); + prg[3] = (byte)(PROM_8K_SIZE - 1); + SetBank_CPU(); + SetBank_PPU(); + + irq_enable = irq_counter = irq_latch = irq_request = 0; + + JMaddr = 0; + JMaddrDAT[0] = JMaddrDAT[1] = JMaddrDAT[2] = 0; + + we_sram = 0; + nes.SetSAVERAM_SIZE(32 * 1024); + nes.SetVideoMode(true); + } + + + //BYTE Mapper199::ReadLow(WORD addr) + public override byte ReadLow(ushort addr) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + return XRAM[addr - 0x4000]; + } + else if (addr >= 0x6000 && addr <= 0x7FFF) + { + if (JMaddr != 0) + { + switch (addr) + { + case 0x6000: return JMaddrDAT[0]; + case 0x6010: return JMaddrDAT[1]; + case 0x6013: JMaddr = 0; return JMaddrDAT[2]; + } + } + + switch (we_sram) + { + case 0xE4: + case 0xEC: return WRAM[(addr & 0x1FFF) + 0x0000]; + case 0xE5: + case 0xED: return WRAM[(addr & 0x1FFF) + 0x2000]; + case 0xE6: + case 0xEE: return WRAM[(addr & 0x1FFF) + 0x4000]; + case 0xE7: + case 0xEF: return WRAM[(addr & 0x1FFF) + 0x6000]; + default: return CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + } + else + { + return base.ReadLow(addr); + } + } + + //void Mapper199::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + XRAM[addr - 0x4000] = data; + if ((we_sram == 0xA1) || (we_sram == 0xA5) || (we_sram == 0xA9)) + { + JMaddr = 1; + switch (addr) + { + case 0x5000: JMaddrDAT[0] = data; break; + case 0x5010: JMaddrDAT[1] = data; break; + case 0x5013: JMaddrDAT[2] = data; break; + } + } + } + else if (addr >= 0x6000 && addr <= 0x7FFF) + { + + switch (we_sram) + { + case 0xE4: //CPU_MEM_BANK + case 0xEC: //CPU_MEM_BANK + WRAM[(addr & 0x1FFF) + 0x0000] = data; + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + case 0xE5: //SRAM + case 0xED: //SRAM + WRAM[(addr & 0x1FFF) + 0x2000] = data; + break; + case 0xE6: + case 0xEE: + WRAM[(addr & 0x1FFF) + 0x4000] = data; + break; + case 0xE7: + case 0xEF: + WRAM[(addr & 0x1FFF) + 0x6000] = data; + break; + default: + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + } + + } + else + { + base.WriteLow(addr, data); + } + } + + //void Mapper199::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + switch (reg[0] & 0x0f) + { + case 0x00: chr[0] = data; SetBank_PPU(); break; + case 0x01: chr[2] = data; SetBank_PPU(); break; + case 0x02: + case 0x03: + case 0x04: + case 0x05: chr[(reg[0] & 0x07) + 2] = data; SetBank_PPU(); break; + case 0x06: + case 0x07: + case 0x08: + case 0x09: prg[(reg[0] & 0x0f) - 6] = data; SetBank_CPU(); break; + case 0x0A: chr[1] = data; SetBank_PPU(); break; + case 0x0B: chr[3] = data; SetBank_PPU(); break; + } + break; + case 0xA000: + reg[2] = data; + data &= 0x03; + if (data == 0) SetVRAM_Mirror(VRAM_VMIRROR); + else if (data == 1) SetVRAM_Mirror(VRAM_HMIRROR); + else if (data == 2) SetVRAM_Mirror(VRAM_MIRROR4L); + else SetVRAM_Mirror(VRAM_MIRROR4H); + break; + case 0xA001: + // DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() ); + reg[3] = data; + we_sram = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + break; + } + + } + + //void Mapper199::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter -= 1; + } + } + if (irq_counter == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + irq_counter--; + } + } + } + } + + void SetBank_CPU() + { + SetPROM_8K_Bank(4, prg[0 ^ (reg[0] >> 5 & ~(0 << 1) & 2)]); + SetPROM_8K_Bank(5, prg[1 ^ (reg[0] >> 5 & ~(1 << 1) & 2)]); + SetPROM_8K_Bank(6, prg[2 ^ (reg[0] >> 5 & ~(2 << 1) & 2)]); + SetPROM_8K_Bank(7, prg[3 ^ (reg[0] >> 5 & ~(3 << 1) & 2)]); + } + + void SetBank_PPU() + { + uint bank = (uint)((reg[0] & 0x80) >> 5); + for (int x = 0; x < 8; x++) + { + if (chr[x] <= 7) + SetCRAM_1K_Bank((byte)(x ^ bank), chr[x]); + else + SetVROM_1K_Bank((byte)(x ^ bank), chr[x]); + } + } + + //void Mapper199::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + int i; + for (i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + for (i = 8; i < 12; i++) + { + p[i] = prg[i]; + } + for (i = 8; i < 20; i++) + { + p[i] = chr[i]; + } + p[20] = we_sram; + p[21] = JMaddr; + p[22] = JMaddrDAT[0]; + p[23] = JMaddrDAT[1]; + p[24] = JMaddrDAT[2]; + p[25] = irq_enable; + p[26] = irq_counter; + p[27] = irq_latch; + p[28] = irq_request; + } + + //void Mapper199::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + int i; + for (i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + for (i = 8; i < 12; i++) + { + prg[i] = p[i]; + } + for (i = 8; i < 20; i++) + { + chr[i] = p[i]; + } + we_sram = p[20]; + JMaddr = p[21]; + JMaddrDAT[0] = p[22]; + JMaddrDAT[1] = p[23]; + JMaddrDAT[2] = p[24]; + irq_enable = p[25]; + irq_counter = p[26]; + irq_latch = p[27]; + irq_request = p[28]; + } + + public override bool IsStateSave() + { + return true; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper200.cs b/Core/VirtualNes.Core/Mapper/Mapper200.cs new file mode 100644 index 0000000..fa3d691 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper200.cs @@ -0,0 +1,44 @@ +////////////////////////////////////////////// +// Mapper200 1200-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper200 : Mapper + { + public Mapper200(NES parent) : base(parent) + { + } + + public override void Reset() + { + // SetPROM_32K_Bank( 0, 1, PROM_8K_SIZE-2, PROM_8K_SIZE-1 ); + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper200::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetPROM_16K_Bank(4, addr & 0x07); + SetPROM_16K_Bank(6, addr & 0x07); + SetVROM_8K_Bank(addr & 0x07); + + if ((addr & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper201.cs b/Core/VirtualNes.Core/Mapper/Mapper201.cs new file mode 100644 index 0000000..67e066e --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper201.cs @@ -0,0 +1,40 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper201 21-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper201 : Mapper + { + public Mapper201(NES parent) : base(parent) + { + } + + public override void Reset() + { + // SetPROM_32K_Bank( 0, 1, PROM_8K_SIZE-2, PROM_8K_SIZE-1 ); + SetPROM_16K_Bank(4, 0); + SetPROM_16K_Bank(6, 0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper201::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + BYTE bank = (byte)((BYTE)addr & 0x03); + if (!((addr & 0x08) != 0)) + bank = 0; + SetPROM_32K_Bank(bank); + SetVROM_8K_Bank(bank); + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper202.cs b/Core/VirtualNes.Core/Mapper/Mapper202.cs new file mode 100644 index 0000000..02fde1a --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper202.cs @@ -0,0 +1,75 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper202 150-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper202 : Mapper + { + public Mapper202(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_16K_Bank(4, 6); + SetPROM_16K_Bank(6, 7); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper202::ExWrite(WORD addr, BYTE data) + public override void ExWrite(ushort addr, byte data) + { + if (addr >= 0x4020) + { + WriteSub(addr, data); + } + } + + //void Mapper202::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + WriteSub(addr, data); + } + + //void Mapper202::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + WriteSub(addr, data); + } + + void WriteSub(ushort addr, BYTE data) + { + INT bank = (addr >> 1) & 0x07; + + SetPROM_16K_Bank(4, bank); + if ((addr & 0x0C) == 0x0C) + { + SetPROM_16K_Bank(6, bank + 1); + } + else + { + SetPROM_16K_Bank(6, bank); + } + SetVROM_8K_Bank(bank); + + if ((addr & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper216.cs b/Core/VirtualNes.Core/Mapper/Mapper216.cs new file mode 100644 index 0000000..56471f5 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper216.cs @@ -0,0 +1,29 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper216 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper216 : Mapper + { + public Mapper216(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetVROM_8K_Bank(0); + SetPROM_32K_Bank(0); + } + + //void Mapper216::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + SetVROM_8K_Bank((addr & 0x0E) >> 1); + SetPROM_32K_Bank(addr & 1); + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper222.cs b/Core/VirtualNes.Core/Mapper/Mapper222.cs new file mode 100644 index 0000000..c6386b5 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper222.cs @@ -0,0 +1,76 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper222 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper222 : Mapper + { + public Mapper222(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + SetVRAM_Mirror(VRAM_VMIRROR); + } + + //void Mapper222::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF003) + { + case 0x8000: + SetPROM_8K_Bank(4, data); + break; + case 0xA000: + SetPROM_8K_Bank(5, data); + break; + case 0xB000: + SetVROM_1K_Bank(0, data); + break; + case 0xB002: + SetVROM_1K_Bank(1, data); + break; + case 0xC000: + SetVROM_1K_Bank(2, data); + break; + case 0xC002: + SetVROM_1K_Bank(3, data); + break; + case 0xD000: + SetVROM_1K_Bank(4, data); + break; + case 0xD002: + SetVROM_1K_Bank(5, data); + break; + case 0xE000: + SetVROM_1K_Bank(6, data); + break; + case 0xE002: + SetVROM_1K_Bank(7, data); + break; + } + } + + public override void LoadState(byte[] p) + { + // + } + + public override void SaveState(byte[] p) + { + // + } + + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper225.cs b/Core/VirtualNes.Core/Mapper/Mapper225.cs new file mode 100644 index 0000000..769b52c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper225.cs @@ -0,0 +1,79 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper225 72-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper225 : Mapper + { + public Mapper225(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper225::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + BYTE prg_bank = (byte)((addr & 0x0F80) >> 7); + BYTE chr_bank = (byte)(addr & 0x003F); + + SetVROM_1K_Bank(0, (chr_bank * 8 + 0)); + SetVROM_1K_Bank(1, (chr_bank * 8 + 1)); + SetVROM_1K_Bank(2, (chr_bank * 8 + 2)); + SetVROM_1K_Bank(3, (chr_bank * 8 + 3)); + SetVROM_1K_Bank(4, (chr_bank * 8 + 4)); + SetVROM_1K_Bank(5, (chr_bank * 8 + 5)); + SetVROM_1K_Bank(6, (chr_bank * 8 + 6)); + SetVROM_1K_Bank(7, (chr_bank * 8 + 7)); + + if ((addr & 0x2000) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + + if ((addr & 0x1000) != 0) + { + // 16KBbank + if ((addr & 0x0040) != 0) + { + SetPROM_8K_Bank(4, (prg_bank * 4 + 2)); + SetPROM_8K_Bank(5, (prg_bank * 4 + 3)); + SetPROM_8K_Bank(6, (prg_bank * 4 + 2)); + SetPROM_8K_Bank(7, (prg_bank * 4 + 3)); + } + else + { + SetPROM_8K_Bank(4, (prg_bank * 4 + 0)); + SetPROM_8K_Bank(5, (prg_bank * 4 + 1)); + SetPROM_8K_Bank(6, (prg_bank * 4 + 0)); + SetPROM_8K_Bank(7, (prg_bank * 4 + 1)); + } + } + else + { + SetPROM_8K_Bank(4, (prg_bank * 4 + 0)); + SetPROM_8K_Bank(5, (prg_bank * 4 + 1)); + SetPROM_8K_Bank(6, (prg_bank * 4 + 2)); + SetPROM_8K_Bank(7, (prg_bank * 4 + 3)); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper226.cs b/Core/VirtualNes.Core/Mapper/Mapper226.cs new file mode 100644 index 0000000..ded2c98 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper226.cs @@ -0,0 +1,94 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper226 76-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper226 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper226(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + reg[0] = 0; + reg[1] = 0; + } + + //void Mapper226::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0x001) != 0) + { + reg[1] = data; + } + else + { + reg[0] = data; + } + + if ((reg[0] & 0x40) != 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + + BYTE bank = (byte)(((reg[0] & 0x1E) >> 1) | ((reg[0] & 0x80) >> 3) | ((reg[1] & 0x01) << 5)); + + if ((reg[0] & 0x20) != 0) + { + if ((reg[0] & 0x01) != 0) + { + SetPROM_8K_Bank(4, bank * 4 + 2); + SetPROM_8K_Bank(5, bank * 4 + 3); + SetPROM_8K_Bank(6, bank * 4 + 2); + SetPROM_8K_Bank(7, bank * 4 + 3); + } + else + { + SetPROM_8K_Bank(4, bank * 4 + 0); + SetPROM_8K_Bank(5, bank * 4 + 1); + SetPROM_8K_Bank(6, bank * 4 + 0); + SetPROM_8K_Bank(7, bank * 4 + 1); + } + } + else + { + SetPROM_8K_Bank(4, bank * 4 + 0); + SetPROM_8K_Bank(5, bank * 4 + 1); + SetPROM_8K_Bank(6, bank * 4 + 2); + SetPROM_8K_Bank(7, bank * 4 + 3); + } + } + + public override bool IsStateSave() + { + return true; + } + + //void Mapper226::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper226::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper227.cs b/Core/VirtualNes.Core/Mapper/Mapper227.cs new file mode 100644 index 0000000..e12882d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper227.cs @@ -0,0 +1,71 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper227 1200-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper227 : Mapper + { + public Mapper227(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 0, 1); + } + + //void Mapper227::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + BYTE bank = (byte)(((addr & 0x0100) >> 4) | ((addr & 0x0078) >> 3)); + + if ((addr & 0x0001) != 0) + { + SetPROM_32K_Bank(bank); + } + else + { + if ((addr & 0x0004) != 0) + { + SetPROM_8K_Bank(4, bank * 4 + 2); + SetPROM_8K_Bank(5, bank * 4 + 3); + SetPROM_8K_Bank(6, bank * 4 + 2); + SetPROM_8K_Bank(7, bank * 4 + 3); + } + else + { + SetPROM_8K_Bank(4, bank * 4 + 0); + SetPROM_8K_Bank(5, bank * 4 + 1); + SetPROM_8K_Bank(6, bank * 4 + 0); + SetPROM_8K_Bank(7, bank * 4 + 1); + } + } + + if (!((addr & 0x0080) != 0)) + { + if ((addr & 0x0200) != 0) + { + SetPROM_8K_Bank(6, (bank & 0x1C) * 4 + 14); + SetPROM_8K_Bank(7, (bank & 0x1C) * 4 + 15); + } + else + { + SetPROM_8K_Bank(6, (bank & 0x1C) * 4 + 0); + SetPROM_8K_Bank(7, (bank & 0x1C) * 4 + 1); + } + } + if ((addr & 0x0002) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper228.cs b/Core/VirtualNes.Core/Mapper/Mapper228.cs new file mode 100644 index 0000000..57cfc67 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper228.cs @@ -0,0 +1,71 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper228 Action 52 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper228 : Mapper + { + public Mapper228(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper228::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + BYTE prg = (byte)((addr & 0x0780) >> 7); + + switch ((addr & 0x1800) >> 11) + { + case 1: + prg |= 0x10; + break; + case 3: + prg |= 0x20; + break; + } + + if ((addr & 0x0020) != 0) + { + prg <<= 1; + if ((addr & 0x0040) != 0) + { + prg++; + } + SetPROM_8K_Bank(4, prg * 4 + 0); + SetPROM_8K_Bank(5, prg * 4 + 1); + SetPROM_8K_Bank(6, prg * 4 + 0); + SetPROM_8K_Bank(7, prg * 4 + 1); + } + else + { + SetPROM_8K_Bank(4, prg * 4 + 0); + SetPROM_8K_Bank(5, prg * 4 + 1); + SetPROM_8K_Bank(6, prg * 4 + 2); + SetPROM_8K_Bank(7, prg * 4 + 3); + } + + SetVROM_8K_Bank(((addr & 0x000F) << 2) | (data & 0x03)); + + if ((addr & 0x2000) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper229.cs b/Core/VirtualNes.Core/Mapper/Mapper229.cs new file mode 100644 index 0000000..c66aa38 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper229.cs @@ -0,0 +1,53 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper229 31-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper229 : Mapper + { + public Mapper229(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + //void Mapper229::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0x001E) != 0) + { + BYTE prg = (byte)(addr & 0x001F); + + SetPROM_8K_Bank(4, prg * 2 + 0); + SetPROM_8K_Bank(5, prg * 2 + 1); + SetPROM_8K_Bank(6, prg * 2 + 0); + SetPROM_8K_Bank(7, prg * 2 + 1); + + SetVROM_8K_Bank(addr & 0x0FFF); + } + else + { + SetPROM_32K_Bank(0); + SetVROM_8K_Bank(0); + } + + if ((addr & 0x0020) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper230.cs b/Core/VirtualNes.Core/Mapper/Mapper230.cs new file mode 100644 index 0000000..720bd4f --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper230.cs @@ -0,0 +1,73 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper230 22-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper230 : Mapper + { + BYTE rom_sw; + public Mapper230(NES parent) : base(parent) + { + } + + public override void Reset() + { + if (rom_sw != 0) + { + rom_sw = 0; + } + else + { + rom_sw = 1; + } + if (rom_sw != 0) + { + SetPROM_32K_Bank(0, 1, 14, 15); + } + else + { + SetPROM_32K_Bank(16, 17, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + //void Mapper230::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (rom_sw != 0) + { + SetPROM_8K_Bank(4, (data & 0x07) * 2 + 0); + SetPROM_8K_Bank(5, (data & 0x07) * 2 + 1); + } + else + { + if ((data & 0x20) != 0) + { + SetPROM_8K_Bank(4, (data & 0x1F) * 2 + 16); + SetPROM_8K_Bank(5, (data & 0x1F) * 2 + 17); + SetPROM_8K_Bank(6, (data & 0x1F) * 2 + 16); + SetPROM_8K_Bank(7, (data & 0x1F) * 2 + 17); + } + else + { + SetPROM_8K_Bank(4, (data & 0x1E) * 2 + 16); + SetPROM_8K_Bank(5, (data & 0x1E) * 2 + 17); + SetPROM_8K_Bank(6, (data & 0x1E) * 2 + 18); + SetPROM_8K_Bank(7, (data & 0x1E) * 2 + 19); + } + if ((data & 0x40) != 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper231.cs b/Core/VirtualNes.Core/Mapper/Mapper231.cs new file mode 100644 index 0000000..5579341 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper231.cs @@ -0,0 +1,53 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper231 20-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper231 : Mapper + { + public Mapper231(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper231::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0x0020) != 0) + { + SetPROM_32K_Bank((BYTE)(addr >> 1)); + } + else + { + BYTE bank = (byte)(addr & 0x1E); + SetPROM_8K_Bank(4, bank * 2 + 0); + SetPROM_8K_Bank(5, bank * 2 + 1); + SetPROM_8K_Bank(6, bank * 2 + 0); + SetPROM_8K_Bank(7, bank * 2 + 1); + } + + if ((addr & 0x0080) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper232.cs b/Core/VirtualNes.Core/Mapper/Mapper232.cs new file mode 100644 index 0000000..34145ea --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper232.cs @@ -0,0 +1,78 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper232 Quattro Games // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper232 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper232(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + reg[0] = 0x0C; + reg[1] = 0x00; + } + + //void Mapper232::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000) + { + Write(addr, data); + } + } + + //void Mapper232::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + // if( addr == 0x9000 ) { + // reg[0] = (data & 0x18)>>1; + // } else if( addr >= 0xA000 && addr <= 0xFFFF ) { + // reg[1] = data & 0x03; + // } + if (addr <= 0x9FFF) + { + reg[0] = (byte)((data & 0x18) >> 1); + } + else + { + reg[1] = (byte)(data & 0x03); + } + + SetPROM_8K_Bank(4, (reg[0] | reg[1]) * 2 + 0); + SetPROM_8K_Bank(5, (reg[0] | reg[1]) * 2 + 1); + SetPROM_8K_Bank(6, (reg[0] | 0x03) * 2 + 0); + SetPROM_8K_Bank(7, (reg[0] | 0x03) * 2 + 1); + } + + public override bool IsStateSave() + { + return true; + } + + + + //void Mapper232::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper232::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper233.cs b/Core/VirtualNes.Core/Mapper/Mapper233.cs new file mode 100644 index 0000000..d9db8ec --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper233.cs @@ -0,0 +1,59 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper233 42-in-1 // +//////////////////////////////////////////////////////////////////////////using static VirtualNes.MMU; +using static VirtualNes.MMU; +using BYTE = System.Byte; + +namespace VirtualNes.Core +{ + public class Mapper233 : Mapper + { + public Mapper233(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, 2, 3); + } + + //void Mapper233::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((data & 0x20) != 0) + { + SetPROM_8K_Bank(4, (data & 0x1F) * 2 + 0); + SetPROM_8K_Bank(5, (data & 0x1F) * 2 + 1); + SetPROM_8K_Bank(6, (data & 0x1F) * 2 + 0); + SetPROM_8K_Bank(7, (data & 0x1F) * 2 + 1); + } + else + { + BYTE bank = (byte)((data & 0x1E) >> 1); + + SetPROM_8K_Bank(4, bank * 4 + 0); + SetPROM_8K_Bank(5, bank * 4 + 1); + SetPROM_8K_Bank(6, bank * 4 + 2); + SetPROM_8K_Bank(7, bank * 4 + 3); + } + + if ((data & 0xC0) == 0x00) + { + SetVRAM_Mirror(0, 0, 0, 1); + } + else if ((data & 0xC0) == 0x40) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else if ((data & 0xC0) == 0x80) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_MIRROR4H); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper234.cs b/Core/VirtualNes.Core/Mapper/Mapper234.cs new file mode 100644 index 0000000..b73299c --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper234.cs @@ -0,0 +1,104 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper234 Maxi-15 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + + public class Mapper234 : Mapper + { + BYTE[] reg = new byte[2]; + public Mapper234(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, 2, 3); + + reg[0] = 0; + reg[1] = 0; + } + + //void Mapper234::Read(WORD addr, BYTE data) + public override void Read(ushort addr, byte data) + { + if (addr >= 0xFF80 && addr <= 0xFF9F) + { + if (reg[0] != 0) + { + reg[0] = data; + SetBank(); + } + } + + if (addr >= 0xFFE8 && addr <= 0xFFF7) + { + reg[1] = data; + SetBank(); + } + } + + //void Mapper234::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr >= 0xFF80 && addr <= 0xFF9F) + { + if (reg[0] == 0) + { + reg[0] = data; + SetBank(); + } + } + + if (addr >= 0xFFE8 && addr <= 0xFFF7) + { + reg[1] = data; + SetBank(); + } + } + + void SetBank() + { + if ((reg[0] & 0x80) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank((reg[0] & 0x0E) | (reg[1] & 0x01)); + SetVROM_8K_Bank(((reg[0] & 0x0E) << 2) | ((reg[1] >> 4) & 0x07)); + } + else + { + SetPROM_32K_Bank(reg[0] & 0x0F); + SetVROM_8K_Bank(((reg[0] & 0x0F) << 2) | ((reg[1] >> 4) & 0x03)); + } + } + public override bool IsStateSave() + { + return true; + } + //void Mapper234::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + } + + //void Mapper234::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper235.cs b/Core/VirtualNes.Core/Mapper/Mapper235.cs new file mode 100644 index 0000000..455c1b0 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper235.cs @@ -0,0 +1,116 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper235 150-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper235 : Mapper + { + public Mapper235(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 0x2000; i++) + { + DRAM[i] = 0xFF; + } + + SetPROM_32K_Bank(0); + } + + //void Mapper235::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + { + BYTE prg = (byte)(((addr & 0x0300) >> 3) | (addr & 0x001F)); + BYTE bus = 0; + + if (PROM_8K_SIZE == 64 * 2) + { + // 100-in-1 + switch (addr & 0x0300) + { + case 0x0000: break; + case 0x0100: bus = 1; break; + case 0x0200: bus = 1; break; + case 0x0300: bus = 1; break; + } + } + else if (PROM_8K_SIZE == 128 * 2) + { + // 150-in-1 + switch (addr & 0x0300) + { + case 0x0000: break; + case 0x0100: bus = 1; break; + case 0x0200: prg = (byte)((prg & 0x1F) | 0x20); break; + case 0x0300: bus = 1; break; + } + } + else if (PROM_8K_SIZE == 192 * 2) + { + // 150-in-1 + switch (addr & 0x0300) + { + case 0x0000: break; + case 0x0100: bus = 1; break; + case 0x0200: prg = (byte)((prg & 0x1F) | 0x20); break; + case 0x0300: prg = (byte)((prg & 0x1F) | 0x40); break; + } + } + else if (PROM_8K_SIZE == 256 * 2) + { + } + + if ((addr & 0x0800) != 0) + { + if ((addr & 0x1000) != 0) + { + SetPROM_8K_Bank(4, prg * 4 + 2); + SetPROM_8K_Bank(5, prg * 4 + 3); + SetPROM_8K_Bank(6, prg * 4 + 2); + SetPROM_8K_Bank(7, prg * 4 + 3); + } + else + { + SetPROM_8K_Bank(4, prg * 4 + 0); + SetPROM_8K_Bank(5, prg * 4 + 1); + SetPROM_8K_Bank(6, prg * 4 + 0); + SetPROM_8K_Bank(7, prg * 4 + 1); + } + } + else + { + SetPROM_32K_Bank(prg); + } + + if (bus != 0) + { + SetPROM_Bank(4, DRAM, BANKTYPE_ROM); + SetPROM_Bank(5, DRAM, BANKTYPE_ROM); + SetPROM_Bank(6, DRAM, BANKTYPE_ROM); + SetPROM_Bank(7, DRAM, BANKTYPE_ROM); + } + + if ((addr & 0x0400) != 0) + { + SetVRAM_Mirror(VRAM_MIRROR4L); + } + else if ((addr & 0x2000) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Mapper/Mapper236.cs b/Core/VirtualNes.Core/Mapper/Mapper236.cs new file mode 100644 index 0000000..054ea02 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper236.cs @@ -0,0 +1,95 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper236 800-in-1 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + public class Mapper236 : Mapper + { + BYTE bank, mode; + public Mapper236(NES parent) : base(parent) + { + } + + public override void Reset() + + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + bank = mode = 0; + } + + //void Mapper236::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr >= 0x8000 && addr <= 0xBFFF) + { + bank = (byte)(((addr & 0x03) << 4) | (bank & 0x07)); + } + else + { + bank = (byte)((addr & 0x07) | (bank & 0x30)); + mode = (byte)(addr & 0x30); + } + + if ((addr & 0x20) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + + switch (mode) + { + case 0x00: + bank |= 0x08; + SetPROM_8K_Bank(4, bank * 2 + 0); + SetPROM_8K_Bank(5, bank * 2 + 1); + SetPROM_8K_Bank(6, (bank | 0x07) * 2 + 0); + SetPROM_8K_Bank(7, (bank | 0x07) * 2 + 1); + break; + case 0x10: + bank |= 0x37; + SetPROM_8K_Bank(4, bank * 2 + 0); + SetPROM_8K_Bank(5, bank * 2 + 1); + SetPROM_8K_Bank(6, (bank | 0x07) * 2 + 0); + SetPROM_8K_Bank(7, (bank | 0x07) * 2 + 1); + break; + case 0x20: + bank |= 0x08; + SetPROM_8K_Bank(4, (bank & 0xFE) * 2 + 0); + SetPROM_8K_Bank(5, (bank & 0xFE) * 2 + 1); + SetPROM_8K_Bank(6, (bank & 0xFE) * 2 + 2); + SetPROM_8K_Bank(7, (bank & 0xFE) * 2 + 3); + break; + case 0x30: + bank |= 0x08; + SetPROM_8K_Bank(4, bank * 2 + 0); + SetPROM_8K_Bank(5, bank * 2 + 1); + SetPROM_8K_Bank(6, bank * 2 + 0); + SetPROM_8K_Bank(7, bank * 2 + 1); + break; + } + } + + + //void Mapper236::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = bank; + p[1] = mode; + } + + //void Mapper236::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + bank = p[0]; + mode = p[1]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper240.cs b/Core/VirtualNes.Core/Mapper/Mapper240.cs new file mode 100644 index 0000000..8757e8d --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper240.cs @@ -0,0 +1,36 @@ +////////////////////////////// +// Mapper240 Gen Ke Le Zhuan // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper240 : Mapper + { + public Mapper240(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper240::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x4020 && addr < 0x6000) + { + SetPROM_32K_Bank((data & 0xF0) >> 4); + SetVROM_8K_Bank(data & 0xF); + } + } + + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper241.cs b/Core/VirtualNes.Core/Mapper/Mapper241.cs new file mode 100644 index 0000000..60769dc --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper241.cs @@ -0,0 +1,35 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper241 Fon Serm Bon // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper241 : Mapper + { + public Mapper241(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + } + + //void Mapper241::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr == 0x8000) + { + SetPROM_32K_Bank(data); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper242.cs b/Core/VirtualNes.Core/Mapper/Mapper242.cs new file mode 100644 index 0000000..6c3f9a0 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper242.cs @@ -0,0 +1,30 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper242 Wai Xing Zhan Shi // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper242 : Mapper + { + public Mapper242(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0); + } + + //void Mapper242::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if ((addr & 0x01) != 0) + { + SetPROM_32K_Bank((addr & 0xF8) >> 3); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper243.cs b/Core/VirtualNes.Core/Mapper/Mapper243.cs new file mode 100644 index 0000000..a21cf62 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper243.cs @@ -0,0 +1,111 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper243 PC-Sachen/Hacker // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; +using BYTE = System.Byte; + + +namespace VirtualNes.Core +{ + + public class Mapper243 : Mapper + { + BYTE[] reg = new byte[4]; + public Mapper243(NES parent) : base(parent) + { + } + + //void Mapper243::Reset() + public override void Reset() + { + SetPROM_32K_Bank(0); + if (VROM_8K_SIZE > 4) + { + SetVROM_8K_Bank(4); + } + else if (VROM_8K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + SetVRAM_Mirror(VRAM_HMIRROR); + + reg[0] = 0; + reg[1] = 0; + reg[2] = 3; + reg[3] = 0; + } + + //void Mapper243::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0x4101) == 0x4100) + { + reg[0] = data; + } + else if ((addr & 0x4101) == 0x4101) + { + switch (reg[0] & 0x07) + { + case 0: + reg[1] = 0; + reg[2] = 3; + break; + case 4: + reg[2] = (byte)((reg[2] & 0x06) | (data & 0x01)); + break; + case 5: + reg[1] = (byte)(data & 0x01); + break; + case 6: + reg[2] = (byte)((reg[2] & 0x01) | ((data & 0x03) << 1)); + break; + case 7: + reg[3] = (byte)(data & 0x01); + break; + default: + break; + } + + SetPROM_32K_Bank(reg[1]); + SetVROM_8K_Bank(reg[2] * 8 + 0, reg[2] * 8 + 1, reg[2] * 8 + 2, reg[2] * 8 + 3, + reg[2] * 8 + 4, reg[2] * 8 + 5, reg[2] * 8 + 6, reg[2] * 8 + 7); + + if (reg[3] != 0) + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + } + } + + public override bool IsStateSave() + { + return true; + } + + + + //void Mapper243::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + } + + //void Mapper243::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper244.cs b/Core/VirtualNes.Core/Mapper/Mapper244.cs new file mode 100644 index 0000000..8cb692b --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper244.cs @@ -0,0 +1,36 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper244 // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper244 : Mapper + { + public Mapper244(NES parent) : base(parent) + { + } + + //void Mapper244::Reset() + public override void Reset() + { + SetPROM_32K_Bank(0); + } + + //void Mapper244::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + if (addr >= 0x8065 && addr <= 0x80A4) + { + SetPROM_32K_Bank((addr - 0x8065) & 0x3); + } + + if (addr >= 0x80A5 && addr <= 0x80E4) + { + SetVROM_8K_Bank((addr - 0x80A5) & 0x7); + } + } + + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper245.cs b/Core/VirtualNes.Core/Mapper/Mapper245.cs new file mode 100644 index 0000000..6bb97cb --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper245.cs @@ -0,0 +1,256 @@ +////////////////////////////////////////////////////////////// +// Mapper245 Yong Zhe Dou E Long // +////////////////////////////////////////////////////////////////////////// +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper245 : Mapper + { + + BYTE[] reg = new byte[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + int MMC4prg, MMC4chr; + + public Mapper245(NES parent) : base(parent) + { + } + + + //void Mapper245::Reset() + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + if (VROM_1K_SIZE != 0) + { + SetVROM_8K_Bank(0); + } + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + } + + //void Mapper245::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xF7FF) + { + case 0x8000: + reg[0] = data; + break; + case 0x8001: + reg[1] = data; + switch (reg[0]) + { + case 0x00: + reg[3] = (byte)((data & 2) << 5); + SetPROM_8K_Bank(6, 0x3E | reg[3]); + SetPROM_8K_Bank(7, 0x3F | reg[3]); + break; + case 0x06: + prg0 = data; + break; + case 0x07: + prg1 = data; + break; + } + SetPROM_8K_Bank(4, prg0 | reg[3]); + SetPROM_8K_Bank(5, prg1 | reg[3]); + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper245::Clock(INT cycles) + public override void Clock(int cycles) + { + // if( irq_request && (nes.GetIrqType() == NES::IRQ_CLOCK) ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + //void Mapper245::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + // if( irq_request && (nes.GetIrqType() == NES::IRQ_HSYNC) ) { + // nes.cpu.IRQ_NotPending(); + // } + } + + void SetBank_CPU() + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr23 + 1, chr23, chr01 + 1, chr01); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(0, chr4 & 0x07); + SetCRAM_1K_Bank(1, chr5 & 0x07); + SetCRAM_1K_Bank(2, chr6 & 0x07); + SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(4, chr4 & 0x07); + SetCRAM_1K_Bank(5, chr5 & 0x07); + SetCRAM_1K_Bank(6, chr6 & 0x07); + SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + + public override bool IsStateSave() + { + return true; + } + + + //void Mapper245::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = (BYTE)irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + } + + //void Mapper245::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper246.cs b/Core/VirtualNes.Core/Mapper/Mapper246.cs new file mode 100644 index 0000000..6273215 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper246.cs @@ -0,0 +1,62 @@ +////////////////////////////////////////////////////////////////////////// +// Mapper246 Phone Serm Berm // +////////////////////////////////////////////////////////////////////////// + +using static VirtualNes.MMU; + + +namespace VirtualNes.Core +{ + public class Mapper246 : Mapper + { + public Mapper246(NES parent) : base(parent) + { + } + + + //void Mapper246::Reset() + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper246::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x6000 && addr < 0x8000) + { + switch (addr) + { + case 0x6000: + SetPROM_8K_Bank(4, data); + break; + case 0x6001: + SetPROM_8K_Bank(5, data); + break; + case 0x6002: + SetPROM_8K_Bank(6, data); + break; + case 0x6003: + SetPROM_8K_Bank(7, data); + break; + case 0x6004: + SetVROM_2K_Bank(0, data); + break; + case 0x6005: + SetVROM_2K_Bank(2, data); + break; + case 0x6006: + SetVROM_2K_Bank(4, data); + break; + case 0x6007: + SetVROM_2K_Bank(6, data); + break; + default: + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + } + } + + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper248.cs b/Core/VirtualNes.Core/Mapper/Mapper248.cs new file mode 100644 index 0000000..41c12fb --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper248.cs @@ -0,0 +1,232 @@ +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper248 : Mapper + { + BYTE[] reg = new BYTE[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + public Mapper248(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + } + + + + //void Mapper248::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + SetPROM_32K_Bank(2 * data, 2 * data + 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + + //void Mapper248::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) + { + SetVRAM_Mirror(VRAM_HMIRROR); + } + else + { + SetVRAM_Mirror(VRAM_VMIRROR); + } + } + break; + case 0xC000: + irq_enable = 0; + irq_latch = 0xBE; + irq_counter = 0xBE; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC001: + irq_enable = 1; + irq_latch = 0xBE; + irq_counter = 0xBE; + break; + } + + } + + //void Mapper248::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((irq_counter--) == 0) + { + irq_counter = irq_latch; + // nes->cpu->IRQ_NotPending(); + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + } + + void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + + void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + } + + public override bool IsStateSave() + { + return true; + } + + //void Mapper248::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = (BYTE)irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + } + + //void Mapper248::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = (BYTE)p[17]; + irq_latch = p[18]; + irq_request = p[19]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper249.cs b/Core/VirtualNes.Core/Mapper/Mapper249.cs new file mode 100644 index 0000000..c0e92a4 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper249.cs @@ -0,0 +1,382 @@ +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper249 : Mapper + { + int MMC4prg, MMC4chr; + BYTE spdata; + BYTE[] reg = new BYTE[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + BYTE we_sram; + BYTE patch; + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + public Mapper249(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (INT i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + prg0 = 0; + prg1 = 1; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + + SetVROM_8K_Bank(0); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + + // IRQタイプ設定 + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + spdata = 0; + } + + //void Mapper249::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if (addr == 0x5000) + { + switch (data) + { + case 0x00: + spdata = 0; + break; + case 0x02: + spdata = 1; + break; + } + } + + if (addr >= 0x6000 && addr < 0x8000) + { + CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + } + } + + //void Mapper249::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + BYTE m0, m1, m2, m3, m4, m5, m6, m7; + + switch (addr & 0xFF01) + { + case 0x8000: + case 0x8800: + reg[0] = data; + break; + case 0x8001: + case 0x8801: + switch (reg[0] & 0x07) + { + case 0x00: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(0, data & 0xFE); + SetVROM_1K_Bank(1, data | 0x01); + break; + case 0x01: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(2, data & 0xFE); + SetVROM_1K_Bank(3, data | 0x01); + break; + case 0x02: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(4, data); + break; + case 0x03: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(5, data); + break; + case 0x04: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(6, data); + break; + case 0x05: + if (spdata == 1) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + SetVROM_1K_Bank(7, data); + break; + case 0x06: + if (spdata == 1) + { + if (data < 0x20) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = 0; + m6 = 0; + m7 = 0; + data = (byte)((m7 << 7) | (m6 << 6) | (m5 << 5) | (m2 << 4) | (m1 << 3) | (m3 << 2) | (m4 << 1) | m0); + } + else + { + data = (byte)(data - 0x20); + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + } + SetPROM_8K_Bank(4, data); + break; + case 0x07: + if (spdata == 1) + { + if (data < 0x20) + { + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = 0; + m6 = 0; + m7 = 0; + data = (byte)((m7 << 7) | (m6 << 6) | (m5 << 5) | (m2 << 4) | (m1 << 3) | (m3 << 2) | (m4 << 1) | m0); + } + else + { + data = (byte)(data - 0x20); + m0 = (byte)(data & 0x1); + m1 = (byte)((data & 0x02) >> 1); + m2 = (byte)((data & 0x04) >> 2); + m3 = (byte)((data & 0x08) >> 3); + m4 = (byte)((data & 0x10) >> 4); + m5 = (byte)((data & 0x20) >> 5); + m6 = (byte)((data & 0x40) >> 6); + m7 = (byte)((data & 0x80) >> 7); + data = (byte)((m5 << 7) | (m4 << 6) | (m2 << 5) | (m6 << 4) | (m7 << 3) | (m3 << 2) | (m1 << 1) | m0); + } + } + SetPROM_8K_Bank(5, data); + break; + } + break; + case 0xA000: + case 0xA800: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR); + else SetVRAM_Mirror(VRAM_VMIRROR); + } + break; + case 0xA001: + case 0xA801: + reg[3] = data; + break; + case 0xC000: + case 0xC800: + reg[4] = data; + irq_counter = data; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xC001: + case 0xC801: + reg[5] = data; + irq_latch = data; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE000: + case 0xE800: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + case 0xE001: + case 0xE801: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper249::Clock(INT cycles) + public override void Clock(int cycles) + { + // if( irq_request && (nes->GetIrqType() == NES::IRQ_CLOCK) ) { + // nes->cpu->IRQ_NotPending(); + // } + } + + //void Mapper249::HSync(INT scanline) + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(IRQ_MAPPER); + } + } + } + } + // if( irq_request && (nes->GetIrqType() == NES::IRQ_HSYNC) ) { + // nes->cpu->IRQ_NotPending(); + // } + } + + public override bool IsStateSave() + { + return true; + } + + //void Mapper249::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + p[20] = spdata; + } + + //void Mapper249::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + spdata = p[20]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper251.cs b/Core/VirtualNes.Core/Mapper/Mapper251.cs new file mode 100644 index 0000000..2dd93cd --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper251.cs @@ -0,0 +1,141 @@ +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + + +namespace VirtualNes.Core +{ + public class Mapper251 : Mapper + { + BYTE[] reg = new BYTE[11]; + BYTE[] breg = new BYTE[4]; + public Mapper251(NES parent) : base(parent) + { + } + + public override void Reset() + { + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + + SetVRAM_Mirror(VRAM_VMIRROR); + + INT i; + for (i = 0; i < 11; i++) + reg[i] = 0; + for (i = 0; i < 4; i++) + breg[i] = 0; + } + + //void Mapper251::WriteLow(WORD addr, BYTE data) + public override void WriteLow(ushort addr, byte data) + { + if ((addr & 0xE001) == 0x6000) + { + if (reg[9] != null) + { + breg[reg[10]++] = data; + if (reg[10] == 4) + { + reg[10] = 0; + SetBank(); + } + } + } + } + + //void Mapper251::Write(WORD addr, BYTE data) + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[8] = data; + SetBank(); + break; + case 0x8001: + reg[reg[8] & 0x07] = data; + SetBank(); + break; + case 0xA001: + if ((data & 0x80) != 0) + { + reg[9] = 1; + reg[10] = 0; + } + else + { + reg[9] = 0; + } + break; + } + } + + //void Mapper251::SetBank() + public void SetBank() + { + INT[] chr = new INT[6]; + INT[] prg = new int[4]; + + for (INT i = 0; i < 6; i++) + { + chr[i] = (reg[i] | (breg[1] << 4)) & ((breg[2] << 4) | 0x0F); + } + + if ((reg[8] & 0x80) != 0) + { + SetVROM_8K_Bank(chr[2], chr[3], chr[4], chr[5], chr[0], chr[0] + 1, chr[1], chr[1] + 1); + } + else + { + SetVROM_8K_Bank(chr[0], chr[0] + 1, chr[1], chr[1] + 1, chr[2], chr[3], chr[4], chr[5]); + } + + prg[0] = (reg[6] & ((breg[3] & 0x3F) ^ 0x3F)) | (breg[1]); + prg[1] = (reg[7] & ((breg[3] & 0x3F) ^ 0x3F)) | (breg[1]); + prg[2] = prg[3] = ((breg[3] & 0x3F) ^ 0x3F) | (breg[1]); + prg[2] &= PROM_8K_SIZE - 1; + + if ((reg[8] & 0x40) != 0) + { + SetPROM_32K_Bank(prg[2], prg[1], prg[0], prg[3]); + } + else + { + SetPROM_32K_Bank(prg[0], prg[1], prg[2], prg[3]); + } + } + public override bool IsStateSave() + { + return true; + } + + //void Mapper251::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + INT i; + + for (i = 0; i < 11; i++) + { + p[i] = reg[i]; + } + for (i = 0; i < 4; i++) + { + p[i + 11] = breg[i]; + } + } + + //void Mapper251::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + INT i; + for (i = 0; i < 11; i++) + { + reg[i] = p[i]; + } + for (i = 0; i < 4; i++) + { + reg[i] = p[i + 11]; + } + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper252.cs b/Core/VirtualNes.Core/Mapper/Mapper252.cs new file mode 100644 index 0000000..2a27153 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper252.cs @@ -0,0 +1,213 @@ +using System; +using static VirtualNes.Core.CPU; +using static VirtualNes.MMU; +using BYTE = System.Byte; +using INT = System.Int32; + +namespace VirtualNes.Core +{ + public class Mapper252 : Mapper + { + BYTE[] reg = new BYTE[9]; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_occur; + INT irq_clock; + + public Mapper252(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (byte i = 0; i < 8; i++) + { + reg[i] = i; + } + + reg[8] = 0; + + irq_enable = 0; + irq_counter = 0; + irq_latch = 0; + irq_clock = 0; + irq_occur = 0; + + SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + SetVROM_8K_Bank(0); + + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + + public override void Write(ushort addr, byte data) + { + + if ((addr & 0xF000) == 0x8000) + { + SetPROM_8K_Bank(4, data); + return; + } + if ((addr & 0xF000) == 0xA000) + { + SetPROM_8K_Bank(5, data); + return; + } + switch (addr & 0xF00C) + { + case 0xB000: + reg[0] = (byte)((reg[0] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB004: + reg[0] = (byte)((reg[0] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(0, reg[0]); + break; + case 0xB008: + reg[1] = (byte)((reg[1] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(1, reg[1]); + break; + case 0xB00C: + reg[1] = (byte)((reg[1] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(1, reg[1]); + break; + + case 0xC000: + reg[2] = (byte)((reg[2] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC004: + reg[2] = (byte)(((reg[2] & 0x0F) | ((data & 0x0F) << 4))); + SetVROM_1K_Bank(2, reg[2]); + break; + case 0xC008: + reg[3] = (byte)((reg[3] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(3, reg[3]); + break; + case 0xC00C: + reg[3] = (byte)((reg[3] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(3, reg[3]); + break; + + case 0xD000: + reg[4] = (byte)((reg[4] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD004: + reg[4] = (byte)((reg[4] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(4, reg[4]); + break; + case 0xD008: + reg[5] = (byte)((reg[5] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(5, reg[5]); + break; + case 0xD00C: + reg[5] = (byte)((reg[5] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(5, reg[5]); + break; + + case 0xE000: + reg[6] = (byte)((reg[6] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE004: + reg[6] = (byte)((reg[6] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(6, reg[6]); + break; + case 0xE008: + reg[7] = (byte)((reg[7] & 0xF0) | (data & 0x0F)); + SetVROM_1K_Bank(7, reg[7]); + break; + case 0xE00C: + reg[7] = (byte)((reg[7] & 0x0F) | ((data & 0x0F) << 4)); + SetVROM_1K_Bank(7, reg[7]); + break; + + case 0xF000: + irq_latch = (byte)((irq_latch & 0xF0) | (data & 0x0F)); + irq_occur = 0; + break; + case 0xF004: + irq_latch = (byte)((irq_latch & 0x0F) | ((data & 0x0F) << 4)); + irq_occur = 0; + break; + + case 0xF008: + irq_enable = (byte)(data & 0x03); + if ((irq_enable & 0x02) != 0) + { + irq_counter = irq_latch; + irq_clock = 0; + } + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + + case 0xF00C: + irq_enable = (byte)((irq_enable & 0x01) * 3); + irq_occur = 0; + nes.cpu.ClrIRQ(IRQ_MAPPER); + break; + } + } + + //void Mapper252::Clock(INT cycles) + public override void Clock(int cycles) + { + if ((irq_enable & 0x02) != 0) + { + if ((irq_clock += cycles) >= 0x72) + { + irq_clock -= 0x72; + if (irq_counter == 0xFF) + { + irq_occur = 0xFF; + irq_counter = irq_latch; + irq_enable = (byte)((irq_enable & 0x01) * 3); + + nes.cpu.SetIRQ(IRQ_MAPPER); + } + else + { + irq_counter++; + } + } + // if( irq_occur ) { + // nes->cpu->IRQ_NotPending(); + // } + } + } + public override bool IsStateSave() + { + return true; + } + //void Mapper252::SaveState(LPBYTE p) + public override void SaveState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + p[i] = reg[i]; + } + p[9] = irq_enable; + p[10] = irq_counter; + p[11] = irq_latch; + + //*(INT*)&p[12] = irq_clock; + Array.Copy(p, 12, BitConverter.GetBytes(irq_clock), 0, 4); + } + + //void Mapper252::LoadState(LPBYTE p) + public override void LoadState(byte[] p) + { + for (INT i = 0; i < 9; i++) + { + reg[i] = p[i]; + } + irq_enable = p[9]; + irq_counter = p[10]; + irq_latch = p[11]; + //irq_clock = *(INT*)&p[12]; + irq_clock = BitConverter.ToInt32(p, 12); + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper254.cs b/Core/VirtualNes.Core/Mapper/Mapper254.cs new file mode 100644 index 0000000..690d3cf --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper254.cs @@ -0,0 +1,304 @@ +using static VirtualNes.MMU; +using BYTE = System.Byte; + +namespace VirtualNes.Core +{ + public class Mapper254 : Mapper + { + BYTE[] reg = new BYTE[8]; + BYTE prg0, prg1; + BYTE chr01, chr23, chr4, chr5, chr6, chr7; + + BYTE irq_type; + BYTE irq_enable; + BYTE irq_counter; + BYTE irq_latch; + BYTE irq_request; + BYTE protectflag; + public Mapper254(NES parent) : base(parent) + { + } + + public override void Reset() + { + for (int i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + protectflag = 0; + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0; + irq_request = 0; + } + + public override void WriteLow(ushort addr, byte data) + { + switch (addr & 0xF000) + { + case 0x6000: + case 0x7000: + MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + break; + } + } + + + public override byte ReadLow(ushort addr) + { + if (addr >= 0x6000) + { + if (protectflag != 0) + { + return (MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]); + } + else + { + return (byte)((MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]) ^ 0x1); + } + } + return base.ReadLow(addr); + } + + + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + protectflag = 0xFF; + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR); + else MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + break; + case 0xC000: + reg[4] = data; + irq_counter = data; + irq_request = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xC001: + reg[5] = data; + irq_latch = data; + irq_request = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + } + } + + + public override void Clock(int cycles) + { + // if( irq_request && (nes->GetIrqType() == NES::IRQ_CLOCK) ) { + // nes->cpu->IRQ_NotPending(); + // } + } + + + public override void HSync(int scanline) + { + if ((scanline >= 0 && scanline <= 239)) + { + if (nes.ppu.IsDispON()) + { + if (irq_enable != 0 && irq_request == 0) + { + if (scanline == 0) + { + if (irq_counter != 0) + { + irq_counter--; + } + } + if ((irq_counter--) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + } + } + // if( irq_request && (nes->GetIrqType() == NES::IRQ_HSYNC) ) { + // nes->cpu->IRQ_NotPending(); + // } + } + + + public void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + SetPROM_32K_Bank(PROM_8K_SIZE - 2, prg1, prg0, PROM_8K_SIZE - 1); + } + else + { + SetPROM_32K_Bank(prg0, prg1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1); + } + } + + public void SetBank_PPU() + { + if (VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(0, chr4 & 0x07); + SetCRAM_1K_Bank(1, chr5 & 0x07); + SetCRAM_1K_Bank(2, chr6 & 0x07); + SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + SetCRAM_1K_Bank(4, chr4 & 0x07); + SetCRAM_1K_Bank(5, chr5 & 0x07); + SetCRAM_1K_Bank(6, chr6 & 0x07); + SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + public override bool IsStateSave() + { + return true; + } + + public override void SaveState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + p[i] = reg[i]; + } + p[8] = prg0; + p[9] = prg1; + p[10] = chr01; + p[11] = chr23; + p[12] = chr4; + p[13] = chr5; + p[14] = chr6; + p[15] = chr7; + p[16] = irq_enable; + p[17] = irq_counter; + p[18] = irq_latch; + p[19] = irq_request; + p[20] = protectflag; + } + + public override void LoadState(byte[] p) + { + for (int i = 0; i < 8; i++) + { + reg[i] = p[i]; + } + prg0 = p[8]; + prg1 = p[9]; + chr01 = p[10]; + chr23 = p[11]; + chr4 = p[12]; + chr5 = p[13]; + chr6 = p[14]; + chr7 = p[15]; + irq_enable = p[16]; + irq_counter = p[17]; + irq_latch = p[18]; + irq_request = p[19]; + protectflag = p[20]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/Mapper255.cs b/Core/VirtualNes.Core/Mapper/Mapper255.cs new file mode 100644 index 0000000..d5945be --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/Mapper255.cs @@ -0,0 +1,126 @@ +namespace VirtualNes.Core +{ + public class Mapper255 : Mapper + { + byte[] reg = new byte[4]; + public Mapper255(NES parent) : base(parent) + { + } + + public override void Reset() + { + MMU.SetPROM_32K_Bank(0); + MMU.SetVROM_8K_Bank(0); + MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + + reg[0] = 0; + reg[1] = 0; + reg[2] = 0; + reg[3] = 0; + } + + //BYTE Mapper255::ReadLow(WORD addr, BYTE data) + //{ + // if (addr >= 0x5800) + // { + // return reg[addr & 0x0003] & 0x0F; + // } + // else + // { + // return addr >> 8; + // } + //} + public override byte ReadLow(ushort addr) + { + if (addr >= 0x5800) + { + return (byte)(reg[addr & 0x0003] & 0x0F); + } + else + { + return (byte)(addr >> 8); + } + } + + + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5800) + { + reg[addr & 0x0003] = (byte)(data & 0x0F); + } + } + public override void Write(ushort addr, byte data) + { + byte prg = (byte)((addr & 0x0F80) >> 7); + int chr = (byte)((addr & 0x003F)); + int bank = (byte)((addr & 0x4000) >> 14); + + if ((addr & 0x2000) != 0) + { + MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR); + } + else + { + MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + } + + if ((addr & 0x1000) != 0) + { + if ((addr & 0x0040) != 0) + { + MMU.SetPROM_8K_Bank(4, 0x80 * bank + prg * 4 + 2); + MMU.SetPROM_8K_Bank(5, 0x80 * bank + prg * 4 + 3); + MMU.SetPROM_8K_Bank(6, 0x80 * bank + prg * 4 + 2); + MMU.SetPROM_8K_Bank(7, 0x80 * bank + prg * 4 + 3); + } + else + { + MMU.SetPROM_8K_Bank(4, 0x80 * bank + prg * 4 + 0); + MMU.SetPROM_8K_Bank(5, 0x80 * bank + prg * 4 + 1); + MMU.SetPROM_8K_Bank(6, 0x80 * bank + prg * 4 + 0); + MMU.SetPROM_8K_Bank(7, 0x80 * bank + prg * 4 + 1); + } + } + else + { + MMU.SetPROM_8K_Bank(4, 0x80 * bank + prg * 4 + 0); + MMU.SetPROM_8K_Bank(5, 0x80 * bank + prg * 4 + 1); + MMU.SetPROM_8K_Bank(6, 0x80 * bank + prg * 4 + 2); + MMU.SetPROM_8K_Bank(7, 0x80 * bank + prg * 4 + 3); + } + + MMU.SetVROM_1K_Bank(0, 0x200 * bank + chr * 8 + 0); + MMU.SetVROM_1K_Bank(1, 0x200 * bank + chr * 8 + 1); + MMU.SetVROM_1K_Bank(2, 0x200 * bank + chr * 8 + 2); + MMU.SetVROM_1K_Bank(3, 0x200 * bank + chr * 8 + 3); + MMU.SetVROM_1K_Bank(4, 0x200 * bank + chr * 8 + 4); + MMU.SetVROM_1K_Bank(5, 0x200 * bank + chr * 8 + 5); + MMU.SetVROM_1K_Bank(6, 0x200 * bank + chr * 8 + 6); + MMU.SetVROM_1K_Bank(7, 0x200 * bank + chr * 8 + 7); + } + + // For state save + public override bool IsStateSave() + { + return true; + } + + + public override void SaveState(byte[] p) + { + p[0] = reg[0]; + p[1] = reg[1]; + p[2] = reg[2]; + p[3] = reg[3]; + } + + public override void LoadState(byte[] p) + { + reg[0] = p[0]; + reg[1] = p[1]; + reg[2] = p[2]; + reg[3] = p[3]; + } + } +} diff --git a/Core/VirtualNes.Core/Mapper/_Mapper.cs b/Core/VirtualNes.Core/Mapper/_Mapper.cs new file mode 100644 index 0000000..deaf871 --- /dev/null +++ b/Core/VirtualNes.Core/Mapper/_Mapper.cs @@ -0,0 +1,12 @@ +namespace VirtualNes.Core +{ + public class _Mapper : Mapper + { + + public _Mapper(NES parent) : base(parent) { } + + public override void Reset() + { + } + } +} diff --git a/Core/VirtualNes.Core/NES.cs b/Core/VirtualNes.Core/NES.cs new file mode 100644 index 0000000..91cb521 --- /dev/null +++ b/Core/VirtualNes.Core/NES.cs @@ -0,0 +1,2010 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using VirtualNes.Core.Debug; + +namespace VirtualNes.Core +{ + public class NES + { + public const int FETCH_CYCLES = 8; + + public CPU cpu; + public PPU ppu; + public APU apu; + public ROM rom; + public PAD pad; + public Mapper mapper; + public NesConfig nescfg; + + private List m_CheatCode = new List(); + private List m_GenieCode = new List(); + private bool m_bDiskThrottle; + private int m_nSnapNo; + private bool m_bNsfPlaying; + private bool m_bNsfInit; + private int m_nNsfSongNo; + private int m_nNsfSongMode; + private bool m_bMoviePlay; + private bool m_bMovieRec; + private Stream m_fpMovie; + private uint m_MovieControl; + private int m_MovieStepTotal; + private int m_MovieStep; + private bool m_bTapePlay; + private bool m_bTapeRec; + private Stream m_fpTape; + private double m_TapeCycles; + private byte m_TapeIn; + private byte m_TapeOut; + + // For Barcode + private bool m_bBarcode; + private byte m_BarcodeOut; + private byte m_BarcodePtr; + private int m_BarcodeCycles; + private byte[] m_BarcodeData = new byte[256]; + + // For Barcode + private bool m_bBarcode2; + private int m_Barcode2seq; + private int m_Barcode2ptr; + private int m_Barcode2cnt; + private byte m_Barcode2bit; + private byte[] m_Barcode2data = new byte[32]; + + private int m_TurboFileBank; + private int SAVERAM_SIZE; + private int nIRQtype; + private bool bFrameIRQ; + private bool bVideoMode; + private int NES_scanline; + private EnumRenderMethod RenderMethod; + private bool bZapper; + private int ZapperX; + private int ZapperY; + private long base_cycles; + private long emul_cycles; + + // For VS-Unisystem + byte m_VSDipValue; + VSDIPSWITCH[] m_VSDipTable; + + private byte[] m_PadImg = new byte[226] + { + 28, 8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, + 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, + 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, + 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + private byte[] m_KeyImg0 = new byte[6] + { + 2, 2, + 0x2A, 0x2A, + 0x2A, 0x2A, + }; + + private byte[] m_KeyImg1 = new byte[8] + { + 3, 3, + 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, + }; + + private byte[] m_KeyImg2 = new byte[18] + { + 4, 4, + 0xFF, 0x2A, 0x2A, 0xFF, + 0x2A, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x2A, + 0xFF, 0x2A, 0x2A, 0xFF, + }; + + private byte[] Font6x8 = new byte[768] + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + 0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00, + 0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00,0x48,0xB0,0x50,0x20,0x50,0x68,0x90,0x00, + 0x40,0xA0,0xA8,0x68,0x90,0x90,0x68,0x00,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00, + 0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00, + 0x70,0x88,0x98,0xA8,0xC8,0x88,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0xF8,0x00, + 0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00, + 0x30,0x50,0x90,0x90,0xF8,0x10,0x10,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0xF0,0x00, + 0x70,0x88,0x80,0xF0,0x88,0x88,0x70,0x00,0xF8,0x08,0x10,0x10,0x20,0x20,0x20,0x00, + 0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00, + 0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x40,0x00, + 0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00, + 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00, + 0x30,0x48,0x88,0x98,0xA8,0xA8,0x78,0x00,0x20,0x50,0x50,0x88,0xF8,0x88,0x88,0x00, + 0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, + 0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00,0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00, + 0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00, + 0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0xF8,0x00, + 0x38,0x08,0x08,0x08,0x08,0x88,0x70,0x00,0x88,0x88,0x90,0xE0,0x90,0x88,0x88,0x00, + 0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x88,0xD8,0xA8,0xA8,0xA8,0xA8,0xA8,0x00, + 0x88,0xC8,0xA8,0xA8,0xA8,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + 0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00, + 0xF0,0x88,0x88,0xF0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00, + 0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + 0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x00,0x88,0xA8,0xA8,0xA8,0xA8,0xD8,0x88,0x00, + 0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00, + 0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00, + 0x88,0x50,0xF8,0x20,0xF8,0x20,0x20,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00, + 0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00, + 0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0xF8,0x00, + 0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00,0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00, + 0x08,0x08,0x08,0x78,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xF8,0x80,0x78,0x00, + 0x18,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0xF0,0x00, + 0x80,0x80,0x80,0xF0,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00, + 0x20,0x00,0x20,0x20,0x20,0x20,0xC0,0x00,0x80,0x80,0x88,0x90,0xE0,0x90,0x88,0x00, + 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00, + 0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, + 0x00,0x00,0xF0,0x88,0xF0,0x80,0x80,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0x08,0x00, + 0x00,0x00,0xB8,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00, + 0x20,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x70,0x00, + 0x00,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x00,0x00,0x88,0xA8,0xA8,0xD8,0x88,0x00, + 0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x78,0x08,0xF0,0x00, + 0x00,0x00,0xF8,0x08,0x70,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }; + + public NES(string fname) + { + Debuger.Log("VirtuaNES - CSharpCore\n"); + + m_bDiskThrottle = false; + + m_nSnapNo = 0; + + m_bNsfPlaying = false; + + m_bMoviePlay = m_bMovieRec = false; + m_fpMovie = null; + + m_bTapePlay = m_bTapeRec = false; + m_fpTape = null; + m_TapeCycles = 0d; + m_TapeIn = m_TapeOut = 0; + + m_bBarcode2 = false; + + m_TurboFileBank = 0; + + cpu = null; + ppu = null; + apu = null; + rom = null; + pad = null; + mapper = null; + + SAVERAM_SIZE = 8 * 1024; + + nIRQtype = 0; + + bFrameIRQ = true; + + bVideoMode = false; + + nescfg = NesConfig.NESCONFIG_NTSC; + + CheatInitial(); + + try + { + Debuger.Log("Allocating CPU..."); + cpu = new CPU(this); + + Debuger.Log("Allocating PPU..."); + ppu = new PPU(this); + + ppu.InitBuffer(); + + Debuger.Log("Allocating APU..."); + apu = new APU(this); + + Debuger.Log("Allocating PAD..."); + pad = new PAD(this); + + Debuger.Log("Loading ROM Image..."); + rom = new ROM(fname); + + mapper = Mapper.CreateMapper(this, rom.GetMapperNo()); + + Debuger.Log("OK"); + + Debuger.Log($"{rom.GetRomName()}"); + Debuger.Log($"Mapper : #{rom.GetMapperNo():D3}"); + Debuger.Log($"PROM-CRC : #{rom.GetPROM_CRC():X2}"); + Debuger.Log($"VROM-CRC : #{rom.GetVROM_CRC():X2}"); + Debuger.Log($"PRG SIZE : {16 * rom.GetPROM_SIZE():0000}K"); + Debuger.Log($"CHR SIZE : {8 * rom.GetVROM_SIZE():0000}K"); + + Debuger.Log($"V MIRROR :{rom.IsVMIRROR()}"); + Debuger.Log($"4 SCREEN :{rom.Is4SCREEN()}"); + Debuger.Log($"SAVE RAM :{rom.IsSAVERAM()}"); + Debuger.Log($"TRAINER :{rom.IsTRAINER()}"); + Debuger.Log($"VS-Unisystem :{rom.IsVSUNISYSTEM()}"); + + NesSub_MemoryInitial(); + LoadSRAM(); + LoadDISK(); + + { + // Pad饹ڤȳڻߥ󥰤WΤǤ + uint crc = rom.GetPROM_CRC(); + if ( + crc == 0xe792de94 // Best Play - Pro Yakyuu (New) (J) + || crc == 0xf79d684a // Best Play - Pro Yakyuu (Old) (J) + || crc == 0xc2ef3422 // Best Play - Pro Yakyuu 2 (J) + || crc == 0x974e8840 // Best Play - Pro Yakyuu '90 (J) + || crc == 0xb8747abf // Best Play - Pro Yakyuu Special (J) + || crc == 0x9fa1c11f // Castle Excellent (J) + || crc == 0x0b0d4d1b // Derby Stallion - Zenkoku Ban (J) + || crc == 0x728c3d98 // Downtown - Nekketsu Monogatari (J) + || crc == 0xd68a6f33 // Dungeon Kid (J) + || crc == 0x3a51eb04 // Fleet Commander (J) + || crc == 0x7c46998b // Haja no Fuuin (J) + || crc == 0x7e5d2f1a // Itadaki Street - Watashi no Mise ni Yottette (J) + || crc == 0xcee5857b // Ninjara Hoi! (J) + || crc == 0x50ec5e8b // Wizardry - Legacy of Llylgamyn (J) + || crc == 0x343e9146 // Wizardry - Proving Grounds of the Mad Overlord (J) + || crc == 0x33d07e45) + { // Wizardry - The Knight of Diamonds (J) + pad.SetExController(EXCONTROLLER.EXCONTROLLER_TURBOFILE); + } + } + + LoadTurboFile(); + + // VS-UnisystemΥǥեO + if (rom.IsVSUNISYSTEM()) + { + uint crc = rom.GetPROM_CRC(); + + m_VSDipValue = 0; + m_VSDipTable = VsUnisystem.vsdip_default; + } + + Reset(); + + // `ФΥǥեȥץO(Orʹ) + GameOption.defRenderMethod = (int)GetRenderMethod(); + GameOption.defIRQtype = GetIrqType(); + GameOption.defFrameIRQ = GetFrameIRQmode(); + GameOption.defVideoMode = GetVideoMode(); + + // O`ɤO(ȥ꤬oХǥեȤ) + if (rom.GetMapperNo() != 20) + { + GameOption.Load(rom.GetPROM_CRC()); + } + else + { + GameOption.Load(rom.GetGameID(), rom.GetMakerID()); + } + + SetRenderMethod((EnumRenderMethod)GameOption.nRenderMethod); + SetIrqType((IRQMETHOD)GameOption.nIRQtype); + SetFrameIRQmode(GameOption.bFrameIRQ); + SetVideoMode(GameOption.bVideoMode); + } + catch (Exception ex) + { + Debuger.LogError(ex.ToString()); + throw ex; + } + } + + internal int GetIrqType() + { + return nIRQtype; + } + + private void LoadTurboFile() + { + MemoryUtility.ZEROMEMORY(MMU.ERAM, MMU.ERAM.Length); + + if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE) + return; + + var fp = Supporter.OpenFile(Supporter.Config.path.szSavePath, "TurboFile.vtf"); + try + { + if (fp == null) + { + // xxx ե_ޤ + throw new Exception($"Can Not Open File [TurboFile.vtf]"); + } + + long size = fp.Length; + // ե륵ȡ + if (size > 32 * 1024) + { + size = 32 * 1024; + } + + fp.Read(MMU.ERAM, 0, MMU.ERAM.Length); + fp.Close(); + } + catch (Exception ex) + { + fp?.Close(); + Debuger.LogError($"Loading TurboFile Error.\n{ex}"); + } + } + + private void LoadDISK() + { + //todo : ŵȡ֧ + } + + private void LoadSRAM() + { + if (rom.IsNSF()) + return; + + MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length); + + if (!rom.IsSAVERAM()) + return; + + var saveFileDir = Supporter.Config.path.szSavePath; + var saveFileName = $"{rom.GetRomName()}.sav"; + + var fp = Supporter.OpenFile(saveFileDir, saveFileName); + + try + { + if (fp == null) + { + throw new Exception("not find ram file to read"); + } + + Debuger.Log("Loading SAVERAM..."); + + int size = (int)fp.Length; + if (size <= 128 * 1024) + { + fp.Read(MMU.WRAM, 0, size); + } + Debuger.Log("OK."); + fp.Close(); + } + catch (Exception ex) + { + fp?.Close(); + fp = null; + } + } + + private void NesSub_MemoryInitial() + { + int i; + + // NA + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); + MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.DRAM, MMU.DRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.ERAM, MMU.ERAM.Length); + MemoryUtility.ZEROMEMORY(MMU.XRAM, MMU.XRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length); + + MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length); + MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length); + + MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length); + + MMU.FrameIRQ = 0xC0; + + MMU.PROM = MMU.VROM = null; + + // 0 Zh~΍ + MMU.PROM_8K_SIZE = MMU.PROM_16K_SIZE = MMU.PROM_32K_SIZE = 1; + MMU.VROM_1K_SIZE = MMU.VROM_2K_SIZE = MMU.VROM_4K_SIZE = MMU.VROM_8K_SIZE = 1; + + // ftHgoNݒ + for (i = 0; i < 8; i++) + { + MMU.CPU_MEM_BANK[i] = null; + MMU.CPU_MEM_TYPE[i] = MMU.BANKTYPE_ROM; + MMU.CPU_MEM_PAGE[i] = 0; + } + + // RAM/WRAM + MMU.SetPROM_Bank(0, MMU.RAM, MMU.BANKTYPE_RAM); + MMU.SetPROM_Bank(3, MMU.WRAM, MMU.BANKTYPE_RAM); + + // _~[ + MMU.SetPROM_Bank(1, MMU.XRAM, MMU.BANKTYPE_ROM); + MMU.SetPROM_Bank(2, MMU.XRAM, MMU.BANKTYPE_ROM); + + for (i = 0; i < 8; i++) + { + MMU.CRAM_USED[i] = 0; + } + } + + public void CheatInitial() + { + m_CheatCode.Clear(); + } + + public uint FrameCount { get; private set; } + public void EmulateFrame(bool bDraw) + { + int scanline = 0; + if (rom.IsNSF()) + { + EmulateNSF(); + return; + } + + CheatCodeProcess(); + + NES_scanline = scanline; + bool NotTile = RenderMethod != EnumRenderMethod.TILE_RENDER; + + if (RenderMethod != EnumRenderMethod.TILE_RENDER) + { + bZapper = false; + while (true) + { + ppu.SetRenderScanline(scanline); + + if (scanline == 0) + { + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + EmulationCPU(nescfg.ScanlineCycles); + ppu.FrameStart(); + ppu.ScanlineNext(); + mapper.HSync(scanline); + ppu.ScanlineStart(); + } + else + { + EmulationCPU(nescfg.HDrawCycles); + ppu.FrameStart(); + ppu.ScanlineNext(); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 32); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + } + else if (scanline < 240) + { + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + if (RenderMethod == EnumRenderMethod.POST_ALL_RENDER) + EmulationCPU(nescfg.ScanlineCycles); + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + ppu.DummyScanline(scanline); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + } + } + ppu.ScanlineNext(); // ̈ʒuŃX^[n͉ʂႤ + if (RenderMethod == EnumRenderMethod.PRE_ALL_RENDER) + EmulationCPU(nescfg.ScanlineCycles); + + mapper.HSync(scanline); + ppu.ScanlineStart(); + } + else + { + if (RenderMethod == EnumRenderMethod.POST_RENDER) + EmulationCPU(nescfg.HDrawCycles); + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + ppu.DummyScanline(scanline); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + } + } + if (RenderMethod == EnumRenderMethod.PRE_RENDER) + EmulationCPU(nescfg.HDrawCycles); + ppu.ScanlineNext(); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 32); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + } + else if (scanline == 240) + { + mapper.VSync(); + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + EmulationCPU(nescfg.ScanlineCycles); + mapper.HSync(scanline); + } + else + { + EmulationCPU(nescfg.HDrawCycles); + mapper.HSync(scanline); + EmulationCPU(nescfg.HBlankCycles); + } + } + else if (scanline <= nescfg.TotalScanlines - 1) + { + pad.VSync(); + + // VBLANK + if (scanline == nescfg.TotalScanlines - 1) + { + ppu.VBlankEnd(); + } + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.ScanlineCycles); + mapper.HSync(scanline); + } + else + { + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.HDrawCycles); + mapper.HSync(scanline); + EmulationCPU(nescfg.HBlankCycles); + } + + if (scanline == nescfg.TotalScanlines - 1) + { + break; + } + } + if (pad.IsZapperMode()) + { + if (scanline == ZapperY) + bZapper = true; + else + bZapper = false; + } + + scanline++; + NES_scanline = scanline; + } + } + else + { + bZapper = false; + while (true) + { + ppu.SetRenderScanline(scanline); + + if (scanline == 0) + { + // _~[XLC + // H-Draw (4fetches*32) + EmulationCPU(FETCH_CYCLES * 128); + ppu.FrameStart(); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else if (scanline < 240) + { + // XN[`(Scanline 1`239) + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + // H-Draw (4fetches*32) + EmulationCPU(FETCH_CYCLES * 128); + ppu.DummyScanline(scanline); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + } + } + } + else if (scanline == 240) + { + // _~[XLC (Scanline 240) + mapper.VSync(); + + EmulationCPU(nescfg.HDrawCycles); + // H-Sync + mapper.HSync(scanline); + + EmulationCPU(nescfg.HBlankCycles); + } + else if (scanline <= nescfg.TotalScanlines - 1) + { + pad.VSync(); + + // VBLANK + if (scanline == nescfg.TotalScanlines - 1) + { + ppu.VBlankEnd(); + } + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.HDrawCycles); + + // H-Sync + mapper.HSync(scanline); + + EmulationCPU(nescfg.HBlankCycles); + + if (scanline == nescfg.TotalScanlines - 1) + { + break; + } + } + if (pad.IsZapperMode()) + { + if (scanline == ZapperY) + bZapper = true; + else + bZapper = false; + } + + scanline++; + NES_scanline = scanline; + } + } + + FrameCount++; + } + + internal void DrawString(int x, int y, string str, byte col) + { + foreach (var @char in str) + { + DrawFont(x, y, (byte)@char, col); + x += 6; + } + } + + internal unsafe void DrawFont(int x, int y, byte chr, byte col) + { + int i; + int pFnt; + int pPtr; + var Scn = ppu.GetScreenPtr(); + int pScn = 8; + + + if (chr < 0x20 || chr > 0x7F) + return; + chr -= 0x20; + pFnt = chr * 8; + pPtr = pScn + (256 + 16) * y + x; + for (i = 0; i < 8; i++) + { + if ((Font6x8[pFnt + i] & 0x80) != 0) Scn[pPtr + 0] = col; + if ((Font6x8[pFnt + i] & 0x40) != 0) Scn[pPtr + 1] = col; + if ((Font6x8[pFnt + i] & 0x20) != 0) Scn[pPtr + 2] = col; + if ((Font6x8[pFnt + i] & 0x10) != 0) Scn[pPtr + 3] = col; + if ((Font6x8[pFnt + i] & 0x08) != 0) Scn[pPtr + 4] = col; + if ((Font6x8[pFnt + i] & 0x04) != 0) Scn[pPtr + 5] = col; + pPtr += (256 + 16); + } + } + + + int CPU_CALL_COUNT = 0; + internal void EmulationCPU(int basecycles) + { + int cycles; + + base_cycles += basecycles; + cycles = (int)((base_cycles / 12) - emul_cycles); + + if (cycles > 0) + { + var cycleAdd = cpu.EXEC(cycles); + emul_cycles += cycleAdd; + } + + CPU_CALL_COUNT++; + } + + public void Reset() + { + SaveSRAM(); + SaveDISK(); + SaveTurboFile(); + + // RAM Clear + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); + if (rom.GetPROM_CRC() == 0x29401686) + { // Minna no Taabou no Nakayoshi Dai Sakusen(J) + MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length); + } + + // RAM set + if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20) + { + MemoryUtility.memset(MMU.WRAM, 0xFF, MMU.WRAM.Length); + } + + MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length); + + MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length); + MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length); + + MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length); + + m_bDiskThrottle = false; + + SetRenderMethod(EnumRenderMethod.PRE_RENDER); + + if (rom.IsPAL()) + { + SetVideoMode(true); + } + + MMU.PROM = rom.GetPROM(); + MMU.VROM = rom.GetVROM(); + + MMU.PROM_8K_SIZE = rom.GetPROM_SIZE() * 2; + MMU.PROM_16K_SIZE = rom.GetPROM_SIZE(); + MMU.PROM_32K_SIZE = rom.GetPROM_SIZE() / 2; + + MMU.VROM_1K_SIZE = rom.GetVROM_SIZE() * 8; + MMU.VROM_2K_SIZE = rom.GetVROM_SIZE() * 4; + MMU.VROM_4K_SIZE = rom.GetVROM_SIZE() * 2; + MMU.VROM_8K_SIZE = rom.GetVROM_SIZE(); + + // ftHgoN + if (MMU.VROM_8K_SIZE != 0) + { + MMU.SetVROM_8K_Bank(0); + } + else + { + MMU.SetCRAM_8K_Bank(0); + } + + // ~[ + if (rom.Is4SCREEN()) + { + MMU.SetVRAM_Mirror(MMU.VRAM_MIRROR4); + } + else if (rom.IsVMIRROR()) + { + MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + } + else + { + MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR); + } + + apu.SelectExSound(0); + + ppu.Reset(); + mapper.Reset(); + + // Trainer + if (rom.IsTRAINER()) + { + Array.Copy(rom.GetTRAINER(), 0, MMU.WRAM, 0x1000, 512); + } + + pad.Reset(); + cpu.Reset(); + apu.Reset(); + + if (rom.IsNSF()) + { + mapper.Reset(); + } + + base_cycles = emul_cycles = 0; + } + + internal void SetVideoMode(bool bMode) + { + bVideoMode = bMode; + if (!bVideoMode) + { + nescfg = NesConfig.NESCONFIG_NTSC; + } + else + { + nescfg = NesConfig.NESCONFIG_PAL; + } + apu.SoundSetup(); + } + + + + public void SoftReset() + { + pad.Reset(); + cpu.Reset(); + apu.Reset(); + + if (rom.IsNSF()) + { + mapper.Reset(); + } + + m_bDiskThrottle = false; + + base_cycles = emul_cycles = 0; + } + + internal void EmulateNSF() + { + R6502 reg = null; + + ppu.Reset(); + mapper.VSync(); + + //DEBUGOUT( "Frame\n" ); + + if (m_bNsfPlaying) + { + if (m_bNsfInit) + { + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); + if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0) + { + MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000); + } + + apu.Reset(); + apu.Write(0x4015, 0x0F); + apu.Write(0x4017, 0xC0); + apu.ExWrite(0x4080, 0x80); // FDS Volume 0 + apu.ExWrite(0x408A, 0xE8); // FDS Envelope Speed + + cpu.GetContext(ref reg); + reg.PC = 0x4710; // Init Address + reg.A = (byte)m_nNsfSongNo; + reg.X = (byte)m_nNsfSongMode; + reg.Y = 0; + reg.S = 0xFF; + reg.P = CPU.Z_FLAG | CPU.R_FLAG | CPU.I_FLAG; + + // S΍˂Ăă[v(1b) + for (int i = 0; i < nescfg.TotalScanlines * 60; i++) + { + EmulationCPU(nescfg.ScanlineCycles); + cpu.GetContext(ref reg); + + // [vɓƂmF甲 + if (reg.PC == 0x4700) + { + break; + } + } + + m_bNsfInit = false; + } + + cpu.GetContext(ref reg); + // [vɓĂĐݒ肷 + if (reg.PC == 0x4700) + { + reg.PC = 0x4720; // Play Address + reg.A = 0; + reg.S = 0xFF; + } + + for (int i = 0; i < nescfg.TotalScanlines; i++) + { + EmulationCPU(nescfg.ScanlineCycles); + } + } + else + { + cpu.GetContext(ref reg); + reg.PC = 0x4700; // [v + reg.S = 0xFF; + + EmulationCPU(nescfg.ScanlineCycles * nescfg.TotalScanlines); + } + } + + internal void CheatCodeProcess() + { + foreach (var it in m_CheatCode) + { + if ((it.enable & CHEATCODE.CHEAT_ENABLE) == 0) + continue; + + switch (it.type) + { + case CHEATCODE.CHEAT_TYPE_ALWAYS: + CheatWrite(it.length, it.address, it.data); + break; + case CHEATCODE.CHEAT_TYPE_ONCE: + CheatWrite(it.length, it.address, it.data); + it.enable = 0; + break; + case CHEATCODE.CHEAT_TYPE_GREATER: + if (CheatRead(it.length, it.address) > it.data) + { + CheatWrite(it.length, it.address, it.data); + } + break; + case CHEATCODE.CHEAT_TYPE_LESS: + if (CheatRead(it.length, it.address) < it.data) + { + CheatWrite(it.length, it.address, it.data); + } + break; + } + } + } + + private uint CheatRead(byte length, ushort addr) + { + uint data = 0; + for (int i = 0; i <= length; i++) + { + data |= (uint)(Read((ushort)(addr + i)) * (1 << (i * 8))); + } + + return data; + } + + private void CheatWrite(int length, ushort addr, uint data) + { + for (int i = 0; i <= length; i++) + { + Write((ushort)(addr + i), (byte)(data & 0xFF)); + data >>= 8; + } + } + + public void Dispose() + { + cpu?.Dispose(); + ppu?.Dispose(); + apu?.Dispose(); + pad?.Dispose(); + rom?.Dispose(); + } + + private void SaveSRAM() + { + int i; + if (rom.IsNSF()) return; + if (rom.IsSAVERAM()) return; + + for (i = 0; i < SAVERAM_SIZE; i++) + { + if (MMU.WRAM[i] != 0x00) + break; + } + + if (i < SAVERAM_SIZE) + { + var romName = rom.GetRomName(); + + Debuger.Log($"Saving SAVERAM...[{romName}]"); + + Supporter.SaveSRAMToFile(MMU.WRAM, romName); + } + } + + private void SaveDISK() + { + if (rom.GetMapperNo() != 20) + return; + + int i = 0; + Stream fp = null; + DISKFILEHDR ifh; + byte[] lpDisk = rom.GetPROM(); + byte[] lpWrite = rom.GetDISK(); + long DiskSize = 16 + 65500 * rom.GetDiskNo(); + ulong data; + + try + { + ifh = new DISKFILEHDR(); + ifh.ID = ASCIIEncoding.ASCII.GetBytes("VirtuaNES DI"); + ifh.BlockVersion = 0x0210; + ifh.ProgID = rom.GetGameID(); + ifh.MakerID = (ushort)rom.GetMakerID(); + ifh.DiskNo = (ushort)rom.GetDiskNo(); + + for (i = 16; i < DiskSize; i++) + { + if (lpWrite[i] > 0) + ifh.DifferentSize++; + } + + if (ifh.DifferentSize == 0) + return; + + List contents = new List(); + contents.AddRange(ifh.ToBytes()); + + for (i = 16; i < DiskSize; i++) + { + if (lpWrite[i] > 0) + { + data = (ulong)(i & 0x00FFFFFF); + data |= ((ulong)lpDisk[i] & 0xFF) << 24; + contents.AddRange(BitConverter.GetBytes(data)); + } + } + + Supporter.SaveDISKToFile(contents.ToArray(), rom.GetRomName()); + } + catch (Exception ex) + { + Debuger.LogError(ex.ToString()); + } + } + + private void SaveTurboFile() + { + int i; + + if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE) + return; + + for (i = 0; i < MMU.ERAM.Length; i++) + { + if (MMU.ERAM[i] != 0x00) + break; + } + + if (i < MMU.ERAM.Length) + { + Debuger.Log("Saving TURBOFILE..."); + + Supporter.SaveFile(MMU.ERAM, Supporter.Config.path.szSavePath, "TurboFile.vtf"); + } + } + + internal void Clock(int cycles) + { + Tape(cycles); + Barcode(cycles); + } + + private void Barcode(int cycles) + { + if (m_bBarcode) + { + m_BarcodeCycles += cycles; + if (m_BarcodeCycles > 1000) + { + m_BarcodeCycles = 0; + // ~H + if (m_BarcodeData[m_BarcodePtr] != 0xFF) + { + m_BarcodeOut = m_BarcodeData[m_BarcodePtr++]; + } + else + { + m_bBarcode = false; + m_BarcodeOut = 0; + Debuger.Log("Barcode data trasnfer complete!!"); + + if (!(IsTapePlay() || IsTapeRec())) + { + cpu.SetClockProcess(false); + } + } + } + } + } + + public bool IsTapeRec() + { + return m_bTapeRec; + } + + public bool IsTapePlay() + { + return m_bTapePlay; + } + + internal void Tape(int cycles) + { + if (!(IsTapePlay() || IsTapeRec())) + { + return; + } + + if ((m_TapeCycles -= cycles) > 0) + return; + + m_TapeCycles += (nescfg.CpuClock / 32000.0); + // m_TapeCycles += (nescfg.CpuClock / 22050.0); // xă_ۂ + + if (m_bTapePlay) + { + int data = m_fpTape.ReadByte(); + if (data != -1) //EOF + { + if ((data & 0xFF) >= 0x8C) + { + m_TapeOut = 0x02; + } + else + if ((data & 0xFF) <= 0x74) + { + m_TapeOut = 0x00; + } + } + else + { + TapeStop(); + } + } + if (m_bTapeRec) + { + m_fpTape.WriteByte((m_TapeIn & 7) == 7 ? (byte)0x90 : (byte)0x70); + } + } + + private void TapeStop() + { + if (!m_bBarcode) + { + cpu.SetClockProcess(false); + } + + m_bTapePlay = m_bTapeRec = false; + m_fpTape?.Dispose(); + m_fpTape = null; + } + + internal byte Read(ushort addr) + { + switch (addr >> 13) + { + case 0x00: // $0000-$1FFF + return MMU.RAM[addr & 0x07FF]; + case 0x01: // $2000-$3FFF + return ppu.Read((ushort)(addr & 0xE007)); + case 0x02: // $4000-$5FFF + if (addr < 0x4100) + { + return ReadReg(addr); + } + else + { + return mapper.ReadLow(addr); + } + case 0x03: // $6000-$7FFF + return mapper.ReadLow(addr); + case 0x04: // $8000-$9FFF + case 0x05: // $A000-$BFFF + case 0x06: // $C000-$DFFF + case 0x07: // $E000-$FFFF + return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + return 0x00; // Warning\h + } + + private byte ReadReg(ushort addr) + { + switch (addr & 0xFF) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + return apu.Read(addr); + case 0x15: + return apu.Read(addr); + case 0x14: + return (byte)(addr & 0xFF); + case 0x16: + if (rom.IsVSUNISYSTEM()) + { + return pad.Read(addr); + } + else + { + return (byte)(pad.Read(addr) | 0x40 | m_TapeOut); + } + case 0x17: + if (rom.IsVSUNISYSTEM()) + { + return pad.Read(addr); + } + else + { + return (byte)(pad.Read(addr) | apu.Read(addr)); + } + default: + return mapper.ExRead(addr); + } + } + + internal byte Barcode2() + { + byte ret = 0x00; + + if (!m_bBarcode2 || m_Barcode2seq < 0) + return ret; + + switch (m_Barcode2seq) + { + case 0: + m_Barcode2seq++; + m_Barcode2ptr = 0; + ret = 0x04; // d3 + break; + + case 1: + m_Barcode2seq++; + m_Barcode2bit = m_Barcode2data[m_Barcode2ptr]; + m_Barcode2cnt = 0; + ret = 0x04; // d3 + break; + + case 2: + ret = (byte)((m_Barcode2bit & 0x01) != 0 ? 0x00 : 0x04); // Bit rev. + m_Barcode2bit >>= 1; + if (++m_Barcode2cnt > 7) + { + m_Barcode2seq++; + } + break; + case 3: + if (++m_Barcode2ptr > 19) + { + m_bBarcode2 = false; + m_Barcode2seq = -1; + } + else + { + m_Barcode2seq = 1; + } + break; + default: + break; + } + + return ret; + } + public void SetRenderMethod(EnumRenderMethod type) + { + RenderMethod = type; + } + internal void Write(ushort addr, byte data) + { + switch (addr >> 13) + { + case 0x00: // $0000-$1FFF + MMU.RAM[addr & 0x07FF] = data; + break; + case 0x01: // $2000-$3FFF + if (!rom.IsNSF()) + { + ppu.Write((ushort)(addr & 0xE007), data); + } + break; + case 0x02: // $4000-$5FFF + if (addr < 0x4100) + { + WriteReg(addr, data); + } + else + { + mapper.WriteLow(addr, data); + } + break; + case 0x03: // $6000-$7FFF + mapper.WriteLow(addr, data); + break; + case 0x04: // $8000-$9FFF + case 0x05: // $A000-$BFFF + case 0x06: // $C000-$DFFF + case 0x07: // $E000-$FFFF + mapper.Write(addr, data); + + GenieCodeProcess(); + break; + } + } + + private void GenieCodeProcess() + { + ushort addr; + + for (int i = 0; i < m_GenieCode.Count; i++) + { + addr = m_GenieCode[i].address; + if ((addr & 0x8000) != 0) + { + // 8character codes + if (MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] == m_GenieCode[i].cmp) + { + MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data; + } + } + else + { + // 6character codes + addr |= 0x8000; + MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = m_GenieCode[i].data; + } + } + } + + private void WriteReg(ushort addr, byte data) + { + switch (addr & 0xFF) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x15: + apu.Write(addr, data); + MMU.CPUREG[addr & 0xFF] = data; + break; + case 0x14: + ppu.DMA(data); + cpu.DMA(514); // DMA Pending cycle + MMU.CPUREG[addr & 0xFF] = data; + break; + case 0x16: + mapper.ExWrite(addr, data); // For VS-Unisystem + pad.Write(addr, data); + MMU.CPUREG[addr & 0xFF] = data; + m_TapeIn = data; + break; + case 0x17: + MMU.CPUREG[addr & 0xFF] = data; + pad.Write(addr, data); + apu.Write(addr, data); + break; + // VirtuaNESŗL|[g + case 0x18: + apu.Write(addr, data); + break; + default: + mapper.ExWrite(addr, data); + break; + } + } + + internal bool GetVideoMode() + { + return bVideoMode; + } + + internal void SetFrameIRQmode(bool bMode) + { + bFrameIRQ = bMode; + } + + internal bool GetFrameIRQmode() + { + return bFrameIRQ; + } + + internal EnumRenderMethod GetRenderMethod() + { + return RenderMethod; + } + + internal void SetIrqType(IRQMETHOD nType) + { + nIRQtype = (int)nType; + } + + internal int GetScanline() + { + return NES_scanline; + } + + internal void SetSAVERAM_SIZE(int size) + { + SAVERAM_SIZE = size; + } + + internal byte GetBarcodeStatus() + { + return m_BarcodeOut; + } + + public State GetState() + { + State state = new State(); + + //HEADER + { + state.HEADER.ID = "VirtuaNES ST"; + state.HEADER.BlockVersion = 0x0200; + + if (rom.GetMapperNo() != 20) + state.HEADER.Ext0 = rom.GetPROM_CRC(); + else + { + state.HEADER.Ext0 = rom.GetGameID(); + state.HEADER.Ext1 = (ushort)rom.GetMakerID(); + state.HEADER.Ext2 = (ushort)rom.GetDiskNo(); + } + } + + //REGISTER STATE + { + state.regBLOCK.ID = "REG DATA"; + state.regBLOCK.BlockVersion = 0x0210; + state.regBLOCK.BlockSize = state.reg.GetSize(); + + R6502 R = null; + cpu.GetContext(ref R); + + state.reg.cpureg.PC = R.PC; + state.reg.cpureg.A = R.A; + state.reg.cpureg.X = R.X; + state.reg.cpureg.Y = R.Y; + state.reg.cpureg.S = R.S; + state.reg.cpureg.P = R.P; + state.reg.cpureg.I = R.INT_pending; + + int cycles = 0; + apu.GetFrameIRQ(ref cycles, + ref state.reg.cpureg.FrameIRQ_count, + ref state.reg.cpureg.FrameIRQ_type, + ref state.reg.cpureg.FrameIRQ, + ref state.reg.cpureg.FrameIRQ_occur); + state.reg.cpureg.FrameIRQ_cycles = cycles; // դINTʞ飨 + + state.reg.cpureg.DMA_cycles = cpu.GetDmaCycles(); + state.reg.cpureg.emul_cycles = emul_cycles; + state.reg.cpureg.base_cycles = base_cycles; + + // SAVE PPU STATE + state.reg.ppureg.reg0 = MMU.PPUREG[0]; + state.reg.ppureg.reg1 = MMU.PPUREG[1]; + state.reg.ppureg.reg2 = MMU.PPUREG[2]; + state.reg.ppureg.reg3 = MMU.PPUREG[3]; + state.reg.ppureg.reg7 = MMU.PPU7_Temp; + state.reg.ppureg.loopy_t = MMU.loopy_t; + state.reg.ppureg.loopy_v = MMU.loopy_v; + state.reg.ppureg.loopy_x = MMU.loopy_x; + state.reg.ppureg.toggle56 = MMU.PPU56Toggle; + } + + //RAM STATE + { + state.ram = RAMSTAT.GetDefault(); + uint size = 0; + + // SAVE RAM STATE + MemoryUtility.memcpy(state.ram.RAM, MMU.RAM, state.ram.RAM.Length); + MemoryUtility.memcpy(state.ram.BGPAL, MMU.BGPAL, state.ram.BGPAL.Length); + MemoryUtility.memcpy(state.ram.SPPAL, MMU.SPPAL, state.ram.SPPAL.Length); + MemoryUtility.memcpy(state.ram.SPRAM, MMU.SPRAM, state.ram.SPRAM.Length); + + // S-RAM STATE(ʹ/δʹäv餺ڤХ`֤) + if (rom.IsSAVERAM()) + { + size = (uint)SAVERAM_SIZE; + } + + // Create Header + state.ramBLOCK.ID = "RAM DATA"; + state.ramBLOCK.BlockVersion = 0x0100; + state.ramBLOCK.BlockSize = size + state.ram.GetSize(); + + if (rom.IsSAVERAM()) + { + state.WRAM = new byte[SAVERAM_SIZE]; + Array.Copy(MMU.WRAM, state.WRAM, SAVERAM_SIZE); + } + } + + //BANK STATE + { + state.mmu = MMUSTAT.GetDefault(); + uint size = 0; + + // SAVE CPU MEMORY BANK DATA + // BANK0,1,2ϥХ󥯥`֤vSʤ + // VirtuaNES0.30 + // Х󥯣SRAMʹäv餺` + for (int i = 3; i < 8; i++) + { + state.mmu.CPU_MEM_TYPE[i] = MMU.CPU_MEM_TYPE[i]; + state.mmu.CPU_MEM_PAGE[i] = (ushort)MMU.CPU_MEM_PAGE[i]; + + if (MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_RAM + || MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_DRAM) + { + size += 8 * 1024; // 8K BANK + } + } + + // SAVE VRAM MEMORY DATA + for (int i = 0; i < 12; i++) + { + state.mmu.PPU_MEM_TYPE[i] = MMU.PPU_MEM_TYPE[i]; + state.mmu.PPU_MEM_PAGE[i] = (ushort)MMU.PPU_MEM_PAGE[i]; + } + size += 4 * 1024; // 1K BANK x 4 (VRAM) + + for (int i = 0; i < 8; i++) + { + state.mmu.CRAM_USED[i] = MMU.CRAM_USED[i]; + if (MMU.CRAM_USED[i] != 0) + { + size += 4 * 1024; // 4K BANK + } + } + + // Create Header + state.mmuBLOCK.ID = "MMU DATA"; + state.mmuBLOCK.BlockVersion = 0x0200; + state.mmuBLOCK.BlockSize = size + state.mmu.GetSize(); + + state.CPU_MEM_BANK = new List(); + // WRITE CPU RAM MEMORY BANK + for (int i = 3; i < 8; i++) + { + if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM) + { + state.CPU_MEM_BANK.AddRange(MMU.CPU_MEM_BANK[i].Span(0, 8 * 1024).ToArray()); + } + } + + // WRITE VRAM MEMORY(4K֤٤ƕz) + state.VRAM = new byte[4 * 1024]; + Array.Copy(MMU.VRAM, state.VRAM, state.VRAM.Length); + + state.CRAM = new List(); + // WRITE CRAM MEMORY + for (int i = 0; i < 8; i++) + { + if (MMU.CRAM_USED[i] != 0) + { + var bytes = new byte[4 * 1024]; + Array.Copy(MMU.CRAM, 0x1000 * i, bytes, 0, bytes.Length); + state.CRAM.AddRange(bytes); + } + } + } + + // MMC STATE + { + state.mmc = MMCSTAT.GetDefault(); + + // Create Header + state.mmcBLOCK.ID = "MMC DATA"; + state.mmcBLOCK.BlockVersion = 0x0100; + state.mmcBLOCK.BlockSize = state.mmc.GetSize(); + + if (mapper.IsStateSave()) + { + mapper.SaveState(state.mmc.mmcdata); + } + } + + //CONTROLLER STATE + { + // Create Header + state.ctrBLOCK.ID = "CTR DATA"; + state.ctrBLOCK.BlockVersion = 0x0100; + state.ctrBLOCK.BlockSize = state.ctr.GetSize(); + + state.ctr.pad1bit = 0; + state.ctr.pad2bit = 0; + state.ctr.pad3bit = 0; + state.ctr.pad4bit = 0; + state.ctr.strobe = 0; + } + + //SND STATE + { + state.snd = SNDSTAT.GetDefault(); + + // Create Header + state.sndBLOCK.ID = "SND DATA"; + state.sndBLOCK.BlockVersion = 0x0100; + state.sndBLOCK.BlockSize = state.snd.GetSize(); + + StateBuffer buffer = new StateBuffer(); + apu.SaveState(buffer); + Array.Copy(buffer.Data.ToArray(), state.snd.snddata, buffer.Data.Count); + } + + // DISKIMAGE STATE + if (rom.GetMapperNo() == 20) + { + var lpDisk = rom.GetPROM(); + var lpWrite = rom.GetDISK(); + int DiskSize = 16 + 65500 * rom.GetDiskNo(); + + + // `򥫥 + for (int i = 16; i < DiskSize; i++) + { + if (lpWrite[i] != 0) + state.dsk.DifferentSize++; + } + + state.dskBLOCK.ID = "DISKDATA"; + state.dskBLOCK.BlockVersion = 0x0210; + state.dskBLOCK.BlockSize = 0; + + state.dskdata = new List(); + + for (int i = 16; i < DiskSize; i++) + { + if (lpWrite[i] != 0) + { + uint data = (uint)(i & 0x00FFFFFF); + data |= ((uint)lpDisk[i] & 0xFF) << 24; + state.dskdata.Add(data); + } + } + } + + // EXCTR STATE + if (pad.GetExController() != 0) + { + state.exctrBLOCK.ID = "EXCTRDAT"; + state.exctrBLOCK.BlockVersion = 0x0100; + state.exctrBLOCK.BlockSize = state.exctr.GetSize(); + + // Some excontrollers will default 0 + state.exctr.data = pad.GetSyncExData(); + } + + return state; + } + + public void LoadState(State state) + { + FrameCount = 0; + //HEADER + { + state.HEADER.ID = "VirtuaNES ST"; + state.HEADER.BlockVersion = 0x0200; + + if (rom.GetMapperNo() != 20) + rom.SetPROM_CRC(state.HEADER.Ext0); + else + { + rom.SetGameID(state.HEADER.Ext0); + rom.SetMakerID(state.HEADER.Ext1); + rom.SetDiskNo(state.HEADER.Ext2); + } + } + + //REGISTER STATE + { + R6502 R = new R6502(); + R.PC = state.reg.cpureg.PC; + R.A = state.reg.cpureg.A; + R.X = state.reg.cpureg.X; + R.Y = state.reg.cpureg.Y; + R.S = state.reg.cpureg.S; + R.P = state.reg.cpureg.P; + R.INT_pending = state.reg.cpureg.I; + cpu.SetContext(R); + + apu.SetFrameIRQ( + state.reg.cpureg.FrameIRQ_cycles, + state.reg.cpureg.FrameIRQ_count, + state.reg.cpureg.FrameIRQ_type, + state.reg.cpureg.FrameIRQ, + state.reg.cpureg.FrameIRQ_occur + ); + + + cpu.SetDmaCycles(state.reg.cpureg.DMA_cycles); + emul_cycles = state.reg.cpureg.emul_cycles; + base_cycles = state.reg.cpureg.base_cycles; + + // LOAD PPU STATE + MMU.PPUREG[0] = state.reg.ppureg.reg0; + MMU.PPUREG[1] = state.reg.ppureg.reg1; + MMU.PPUREG[2] = state.reg.ppureg.reg2; + MMU.PPUREG[3] = state.reg.ppureg.reg3; + MMU.PPU7_Temp = state.reg.ppureg.reg7; + MMU.loopy_t = state.reg.ppureg.loopy_t; + MMU.loopy_v = state.reg.ppureg.loopy_v; + MMU.loopy_x = state.reg.ppureg.loopy_x; + MMU.PPU56Toggle = state.reg.ppureg.toggle56; + } + + //RAM STATE + { + // SAVE RAM STATE + MemoryUtility.memcpy(MMU.RAM, state.ram.RAM, state.ram.RAM.Length); + MemoryUtility.memcpy(MMU.BGPAL, state.ram.BGPAL, state.ram.BGPAL.Length); + MemoryUtility.memcpy(MMU.SPPAL, state.ram.SPPAL, state.ram.SPPAL.Length); + MemoryUtility.memcpy(MMU.SPRAM, state.ram.SPRAM, state.ram.SPRAM.Length); + + if (rom.IsSAVERAM()) + { + Array.Copy(state.WRAM, MMU.WRAM, SAVERAM_SIZE); + } + } + + //BANK STATE + { + // SAVE CPU MEMORY BANK DATA + // BANK0,1,2ϥХ󥯥`֤vSʤ + // VirtuaNES0.30 + // Х󥯣SRAMʹäv餺` + for (byte i = 3; i < 8; i++) + { + MMU.CPU_MEM_TYPE[i] = state.mmu.CPU_MEM_TYPE[i]; + MMU.CPU_MEM_PAGE[i] = state.mmu.CPU_MEM_PAGE[i]; + if (MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_ROM) + MMU.SetPROM_8K_Bank(i, MMU.CPU_MEM_PAGE[i]); + else + { + MMU.CPU_MEM_BANK[i].SetArray(state.CPU_MEM_BANK.ToArray(), 0); + } + } + + // VRAM + MemoryUtility.memcpy(MMU.VRAM, state.VRAM, 4 * 1024); + // CRAM + for (int i = 0; i < 8; i++) + { + MMU.CRAM_USED[i] = state.mmu.CRAM_USED[i]; + } + // SAVE VRAM MEMORY DATA + for (byte i = 0; i < 12; i++) + { + if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_VROM) + { + MMU.SetVROM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]); + } + else if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_CRAM) + { + MMU.SetCRAM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]); + } + else if (state.mmu.PPU_MEM_TYPE[i] == MMU.BANKTYPE_VRAM) + { + MMU.SetVRAM_1K_Bank(i, state.mmu.PPU_MEM_PAGE[i]); + } + else + { + throw new Exception("Unknown bank types."); + } + } + + // WRITE CPU RAM MEMORY BANK + + int stateStep = 0; + var stateCPU_MEM_BANK = state.CPU_MEM_BANK.ToArray(); + + for (int i = 3; i < 8; i++) + { + if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM) + { + var sourceData = new Span(stateCPU_MEM_BANK, stateStep * 8 * 1024, 8 * 1024); + MMU.CPU_MEM_BANK[i].WriteTo(sourceData.ToArray(), 0, 8 * 1024); + stateStep++; + } + } + + Array.Copy(state.VRAM, MMU.VRAM, state.VRAM.Length); + + stateStep = 0; + var stateCRAM = state.CRAM.ToArray(); + // LOAD CRAM MEMORY + for (int i = 0; i < 8; i++) + { + if (MMU.CRAM_USED[i] != 0) + { + var sourceData = stateCRAM.AsSpan(stateStep * 4 * 1024, 4 * 1024).ToArray(); + Array.Copy(sourceData, 0, MMU.CRAM, 0x1000 * i, 4 * 1024); + } + } + } + + // MMC STATE + { + mapper.LoadState(state.mmc.mmcdata); + } + + //CONTROLLER STATE + { + pad.pad1bit = state.ctr.pad1bit; + pad.pad2bit = state.ctr.pad2bit; + pad.pad3bit = state.ctr.pad3bit; + pad.pad4bit = state.ctr.pad4bit; + pad.SetStrobe(state.ctr.strobe == 0 ? false : true); + } + + //SND STATE + { + var buffer = new StateReader(state.snd.snddata); + apu.LoadState(buffer); + } + + // DISKIMAGE STATE + if (rom.GetMapperNo() == 20) + { + var lpDisk = rom.GetPROM(); + var lpWrite = rom.GetDISK(); + int DiskSize = 16 + 65500 * rom.GetDiskNo(); + + Array.Clear(lpWrite, 0, DiskSize); + + for (int i = 0; i < state.dsk.DifferentSize; i++) + { + var pos = state.dskdata[i]; + byte data = (byte)(pos >> 24); + pos &= 0x00FFFFFF; + + if (pos >= 16 && pos < DiskSize) + { + lpDisk[pos] = data; + lpWrite[pos] = 0xFF; + } + } + } + + // EXCTR STATE + if (pad.GetExController() != 0) + { + pad.SetSyncExData(state.exctr.data); + } + } + + internal void SetZapperPos(int x, int y) + { + ZapperX = x; ZapperY = y; + } + + public enum IRQMETHOD + { + IRQ_HSYNC = 0, IRQ_CLOCK = 1 + } + } +} diff --git a/Core/VirtualNes.Core/PAD.cs b/Core/VirtualNes.Core/PAD.cs new file mode 100644 index 0000000..f8e7fdd --- /dev/null +++ b/Core/VirtualNes.Core/PAD.cs @@ -0,0 +1,674 @@ +using System.Collections.Generic; + +namespace VirtualNes.Core +{ + public class PAD + { + private NES nes; + private int excontroller_select; + private EXPAD expad; + private bool bStrobe; + private bool bSwapButton; + private bool bSwapPlayer; + private bool bZapperMode; + private VSType nVSSwapType; + private byte[] padbit = new byte[4]; + private byte micbit; + private byte[] padbitsync = new byte[4]; + private byte micbitsync; + private bool bBarcodeWorld; + private int[][] padcnt = new int[4][] + { + new int[2],new int[2],new int[2],new int[2], + }; + + public uint pad1bit, pad2bit, pad3bit, pad4bit; + + private static int[] ren10fps = new int[6] { 1, 1, 1, 0, 0, 0 }; + private static int[] ren15fps = new int[4] { 1, 1, 0, 0 }; + private static int[] ren20fps = new int[3] { 1, 1, 0 }; + private static int[] ren30fps = new int[2] { 1, 0 }; + private static int[] renmask = new int[4] { 6, 4, 3, 2 }; + public static Dictionary rentbl = new Dictionary() + { + {0,ren10fps }, + {1,ren15fps }, + {2,ren20fps }, + {3,ren30fps }, + }; + + public PAD(NES parent) + { + nes = parent; + excontroller_select = 0; + expad = null; + bStrobe = false; + bSwapButton = false; + bSwapPlayer = false; + bZapperMode = false; + nVSSwapType = VSType.VS_TYPE0; + + padbit[0] = padbit[1] = padbit[2] = padbit[3] = 0; + micbit = 0; + + padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0; + micbitsync = 0; + } + + internal byte Read(ushort addr) + { + byte data = 0x00; + + if (addr == 0x4016) + { + data = (byte)(pad1bit & 1); + pad1bit >>= 1; + data |= (byte)(((pad3bit & 1)) << 1); + pad3bit >>= 1; + // Mic + if (!nes.rom.IsVSUNISYSTEM()) + { + data |= micbitsync; + } + if (expad != null) + { + data |= expad.Read4016(); + } + } + if (addr == 0x4017) + { + data = (byte)(pad2bit & 1); + pad2bit >>= 1; + data |= (byte)((pad4bit & 1) << 1); + pad4bit >>= 1; + + if (expad != null) + { + data |= expad.Read4017(); + } + + if (bBarcodeWorld) + { + data |= nes.Barcode2(); + } + } + + return data; + } + public void Dispose() { } + + internal void Write(ushort addr, byte data) + { + if (addr == 0x4016) + { + if ((data & 0x01) != 0) + { + bStrobe = true; + } + else if (bStrobe) + { + bStrobe = false; + + Strobe(); + if (expad != null) + { + expad.Strobe(); + } + } + + if (expad != null) + { + expad.Write4016(data); + } + } + if (addr == 0x4017) + { + if (expad != null) + { + expad.Write4017(data); + } + } + } + + private void Strobe() + { + // For VS-Unisystem + if (nes.rom.IsVSUNISYSTEM()) + { + uint pad1 = (uint)(padbitsync[0] & 0xF3); + uint pad2 = (uint)(padbitsync[1] & 0xF3); + uint st1 = (uint)(padbitsync[0] & 0x08) >> 3; + uint st2 = (uint)(padbitsync[1] & 0x08) >> 3; + + switch (nVSSwapType) + { + case VSType.VS_TYPE0: + pad1bit = pad1 | (st1 << 2); + pad2bit = pad2 | (st2 << 2); + break; + case VSType.VS_TYPE1: + pad1bit = pad2 | (st1 << 2); + pad2bit = pad1 | (st2 << 2); + break; + case VSType.VS_TYPE2: + pad1bit = pad1 | (st1 << 2) | (st2 << 3); + pad2bit = pad2; + break; + case VSType.VS_TYPE3: + pad1bit = pad2 | (st1 << 2) | (st2 << 3); + pad2bit = pad1; + break; + case VSType.VS_TYPE4: + pad1bit = pad1 | (st1 << 2) | 0x08; // 0x08=Start Protect + pad2bit = pad2 | (st2 << 2) | 0x08; // 0x08=Start Protect + break; + case VSType.VS_TYPE5: + pad1bit = pad2 | (st1 << 2) | 0x08; // 0x08=Start Protect + pad2bit = pad1 | (st2 << 2) | 0x08; // 0x08=Start Protect + break; + case VSType.VS_TYPE6: + pad1bit = pad1 | (st1 << 2) | (((uint)padbitsync[0] & 0x04) << 1); + pad2bit = pad2 | (st2 << 2) | (((uint)padbitsync[1] & 0x04) << 1); + break; + case VSType.VS_TYPEZ: + pad1bit = 0; + pad2bit = 0; + break; + } + + // Coin 2偲旐傞堊偵徚偡 + micbit = 0; + } + else + { + if (Supporter.Config.emulator.bFourPlayer) + { + // NES type + pad1bit = padbitsync[0] | ((uint)padbitsync[2] << 8) | 0x00080000; + pad2bit = padbitsync[1] | ((uint)padbitsync[3] << 8) | 0x00040000; + } + else + { + // Famicom type + pad1bit = padbitsync[0]; + pad2bit = padbitsync[1]; + } + } + pad3bit = padbitsync[2]; + pad4bit = padbitsync[3]; + } + + internal void Reset() + { + pad1bit = pad2bit = 0; + bStrobe = false; + + bBarcodeWorld = false; + + for (int x = 0; x < 4; x++) + { + for (int y = 0; y < 2; y++) + { + padcnt[x][y] = 0; + } + } + + // Select Extension Devices + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0xfbfc6a6c // Adventures of Bayou Billy, The(E) + || crc == 0xcb275051 // Adventures of Bayou Billy, The(U) + || crc == 0xfb69c131 // Baby Boomer(Unl)(U) + || crc == 0xf2641ad0 // Barker Bill's Trick Shooting(U) + || crc == 0xbc1dce96 // Chiller (Unl)(U) + || crc == 0x90ca616d // Duck Hunt(JUE) + || crc == 0x59e3343f // Freedom Force(U) + || crc == 0x242a270c // Gotcha!(U) + || crc == 0x7b5bd2de // Gumshoe(UE) + || crc == 0x255b129c // Gun Sight(J) + || crc == 0x8963ae6e // Hogan's Alley(JU) + || crc == 0x51d2112f // Laser Invasion(U) + || crc == 0x0a866c94 // Lone Ranger, The(U) + // || crc == 0xe4c04eea // Mad City(J) + || crc == 0x9eef47aa // Mechanized Attack(U) + || crc == 0xc2db7551 // Shooting Range(U) + || crc == 0x163e86c0 // To The Earth(U) + || crc == 0x42d893e4 // Operation Wolf(J) + || crc == 0x1388aeb9 // Operation Wolf(U) + || crc == 0x0d3cf705 // Wild Gunman(J) + || crc == 0x389960db) + { // Wild Gunman(JUE) + SetExController(EXCONTROLLER.EXCONTROLLER_ZAPPER); + } + if (crc == 0x35893b67 // Arkanoid(J) + || crc == 0x6267fbd1) + { // Arkanoid 2(J) + SetExController(EXCONTROLLER.EXCONTROLLER_PADDLE); + } + if (crc == 0xff6621ce // Hyper Olympic(J) + || crc == 0xdb9418e8 // Hyper Olympic(Tonosama Ban)(J) + || crc == 0xac98cd70) + { // Hyper Sports(J) + SetExController(EXCONTROLLER.EXCONTROLLER_HYPERSHOT); + } + if (crc == 0xf9def527 // Family BASIC(Ver2.0) + || crc == 0xde34526e // Family BASIC(Ver2.1a) + || crc == 0xf050b611 // Family BASIC(Ver3) + || crc == 0x3aaeed3f // Family BASIC(Ver3)(Alt) + || crc == 0x868FCD89 // Family BASIC(Ver1.0) + || crc == 0x2D6B7E5A // PLAYBOX BASIC(J) (Prototype_v0.0) + || crc == 0xDA03D908) + { // PLAYBOX BASIC (J) + SetExController(EXCONTROLLER.EXCONTROLLER_KEYBOARD); + } + if (crc == 0x589b6b0d // Supor Computer V3.0 + || crc == 0x8b265862 // Supor English + || crc == 0x41401c6d // Supor Computer V4.0 + || crc == 0x82F1Fb96 // Supor Computer(Russia) V1.0 + || crc == 0xd5d6eac4) + { // EDU(C) Computer + SetExController(EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD); + nes.SetVideoMode(true); + } + if (crc == 0xc68363f6 // Crazy Climber(J) + || crc == 0x2989ead6 // Smash TV(U) [!] + || crc == 0x0b8f8128) + { // Smash TV(E) [!] + SetExController(EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER); + } + if (crc == 0x20d22251) + { // Top rider(J) + SetExController(EXCONTROLLER.EXCONTROLLER_TOPRIDER); + } + if (crc == 0x0cd00488) + { // Space Shadow(J) + SetExController(EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN); + } + + if (crc == 0x8c8fa83b // Family Trainer - Athletic World (J) + || crc == 0x7e704a14 // Family Trainer - Jogging Race (J) + || crc == 0x2330a5d3) + { // Family Trainer - Rairai Kyonshiizu (J) + SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A); + } + if (crc == 0xf8da2506 // Family Trainer - Aerobics Studio (J) + || crc == 0xca26a0f1 // Family Trainer - Dai Undoukai (J) + || crc == 0x28068b8c // Family Trainer - Fuuun Takeshi Jou 2 (J) + || crc == 0x10bb8f9a // Family Trainer - Manhattan Police (J) + || crc == 0xad3df455 // Family Trainer - Meiro Dai Sakusen (J) + || crc == 0x8a5b72c0 // Family Trainer - Running Stadium (J) + || crc == 0x59794f2d) + { // Family Trainer - Totsugeki Fuuun Takeshi Jou (J) + SetExController(EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B); + } + if (crc == 0x9fae4d46 // Ide Yousuke Meijin no Jissen Mahjong (J) + || crc == 0x7b44fb2a) + { // Ide Yousuke Meijin no Jissen Mahjong 2 (J) + SetExController(EXCONTROLLER.EXCONTROLLER_MAHJANG); + } + if (crc == 0x786148b6) + { // Exciting Boxing (J) + SetExController(EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING); + } + if (crc == 0xc3c0811d // Oeka Kids - Anpanman no Hiragana Daisuki (J) + || crc == 0x9d048ea4) + { // Oeka Kids - Anpanman to Oekaki Shiyou!! (J) + SetExController(EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET); + } + + if (crc == 0x67898319) + { // Barcode World (J) + bBarcodeWorld = true; + } + + // VS-Unisystem + if (nes.rom.IsVSUNISYSTEM()) + { + if (crc == 0xff5135a3 // VS Hogan's Alley + || crc == 0xed588f00 // VS Duck Hunt + || crc == 0x17ae56be) + { // VS Freedom Force + SetExController(EXCONTROLLER.EXCONTROLLER_VSZAPPER); + } + else + { + SetExController(EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM); + } + } + + if (crc == 0x21b099f3) + { // Gyromite (JUE) + SetExController(EXCONTROLLER.EXCONTROLLER_GYROMITE); + } + } + + internal void SetExController(EXCONTROLLER type) + { + excontroller_select = (int)type; + + expad?.Dispose(); + expad = null; + + bZapperMode = false; + + // ExPad Instance create + switch (type) + { + case EXCONTROLLER.EXCONTROLLER_ZAPPER: + expad = new EXPAD_Zapper(nes); + bZapperMode = true; + break; + case EXCONTROLLER.EXCONTROLLER_PADDLE: + expad = new EXPAD_Paddle(nes); + break; + case EXCONTROLLER.EXCONTROLLER_HYPERSHOT: + expad = new EXPAD_HyperShot(nes); + break; + case EXCONTROLLER.EXCONTROLLER_KEYBOARD: + expad = new EXPAD_Keyboard(nes); + break; + case EXCONTROLLER.EXCONTROLLER_SUPOR_KEYBOARD: + expad = new EXPAD_Supor_Keyboard(nes); + break; + case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER: + expad = new EXPAD_CrazyClimber(nes); + break; + case EXCONTROLLER.EXCONTROLLER_TOPRIDER: + expad = new EXPAD_Toprider(nes); + break; + case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN: + expad = new EXPAD_SpaceShadowGun(nes); + bZapperMode = true; + break; + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A: + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B: + expad = new EXPAD_FamlyTrainer(nes); + break; + case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING: + expad = new EXPAD_ExcitingBoxing(nes); + break; + case EXCONTROLLER.EXCONTROLLER_MAHJANG: + expad = new EXPAD_Mahjang(nes); + break; + case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET: + expad = new EXPAD_OekakidsTablet(nes); + break; + case EXCONTROLLER.EXCONTROLLER_TURBOFILE: + expad = new EXPAD_TurboFile(nes); + break; + case EXCONTROLLER.EXCONTROLLER_VSUNISYSTEM: + expad = new EXPAD_VSUnisystem(nes); + break; + case EXCONTROLLER.EXCONTROLLER_VSZAPPER: + expad = new EXPAD_VSZapper(nes); + bZapperMode = true; + break; + + case EXCONTROLLER.EXCONTROLLER_GYROMITE: + expad = new EXPAD_Gyromite(nes); + break; + default: + break; + } + + if (expad != null) + { + expad.Reset(); + } + } + + public void Sync(ControllerState state) + { + padbit[0] = SyncSub(0, state); + padbit[1] = SyncSub(1, state); + padbit[2] = SyncSub(2, state); + padbit[3] = SyncSub(3, state); + + // Mic + micbit = 0; + if (state.HasButton(1, EnumButtonType.MIC)) micbit |= 4; + + // For Excontroller + if (expad != null) + { + expad.Sync(); + } + } + + private byte SyncSub(int no, ControllerState state) + { + ushort bit = 0; + + // Up + if (state.HasButton(no, EnumButtonType.UP)) + bit |= 1 << 4; + // Down + if (state.HasButton(no, EnumButtonType.DOWN)) + bit |= 1 << 5; + // Left + if (state.HasButton(no, EnumButtonType.LEFT)) + bit |= 1 << 6; + // Right + if (state.HasButton(no, EnumButtonType.RIGHT)) + bit |= 1 << 7; + + // 同時入力を禁止する + // if( (bit&((1<<4)|(1<<5))) == ((1<<4)|(1<<5)) ) + // bit &= ~((1<<4)|(1<<5)); + if ((bit & ((1 << 6) | (1 << 7))) == ((1 << 6) | (1 << 7))) + bit = (byte)(bit & ~((1 << 6) | (1 << 7))); + + // A + if (state.HasButton(no, EnumButtonType.A)) bit |= 1 << 0; + // B + if (state.HasButton(no, EnumButtonType.B)) bit |= 1 << 1; + + // Select + if (state.HasButton(no, EnumButtonType.SELECT)) bit |= 1 << 2; + // Start + if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3; + + + return (byte)(bit & 0xFF); + } + + internal bool IsZapperMode() + { + return bZapperMode; + } + + internal void VSync() + { + padbitsync[0] = padbit[0]; + padbitsync[1] = padbit[1]; + padbitsync[2] = padbit[2]; + padbitsync[3] = padbit[3]; + micbitsync = micbit; + } + + internal uint GetSyncData() + { + uint ret; + ret = (uint)(padbit[0] | (padbit[1] << 8) | (padbit[2] << 16) | (padbit[3] << 24)); + ret |= (uint)(micbit << 8); + return ret; + } + + internal void SetSyncData(uint data) + { + micbit = (byte)((data & 0x00000400) >> 8); + padbit[0] = (byte)data; + padbit[1] = (byte)(data >> 8); + padbit[2] = (byte)(data >> 16); + padbit[3] = (byte)(data >> 24); + } + + internal int GetExController() + { + return excontroller_select; + } + + internal bool GetStrobe() + { + return bStrobe; + } + + internal void SetStrobe(bool v) + { + bStrobe = v; + } + + internal uint GetSyncExData() + { + uint data = 0; + + switch ((EXCONTROLLER)excontroller_select) + { + case EXCONTROLLER.EXCONTROLLER_ZAPPER: + case EXCONTROLLER.EXCONTROLLER_PADDLE: + case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN: + case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET: + case EXCONTROLLER.EXCONTROLLER_VSZAPPER: + { + int x, y; + x = expad.GetSyncData(0); + y = expad.GetSyncData(1); + if (x == -1 || y == -1) + { + data = 0x80000000; + } + else + { + data = (uint)((x & 0xFF) | ((y & 0xFF) << 8)); + } + } + if (excontroller_select != (int)EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN) + { + if (expad.GetSyncData(2) != 0) + data |= 0x0010000; + } + else + { + data |= (uint)(expad.GetSyncData(2) << 16); + } + break; + case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER: + data = (uint)expad.GetSyncData(0); + break; + case EXCONTROLLER.EXCONTROLLER_TOPRIDER: + data = (uint)expad.GetSyncData(0); + break; + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A: + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B: + data = (uint)expad.GetSyncData(0); + break; + case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING: + data = (uint)expad.GetSyncData(0); + break; + case EXCONTROLLER.EXCONTROLLER_MAHJANG: + data = (uint)expad.GetSyncData(0); + break; + + default: + break; + } + return data; + } + internal void SetSyncExData(uint data) + { + switch ((EXCONTROLLER)excontroller_select) + { + case EXCONTROLLER.EXCONTROLLER_ZAPPER: + case EXCONTROLLER.EXCONTROLLER_PADDLE: + case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN: + case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET: + case EXCONTROLLER.EXCONTROLLER_VSZAPPER: + { + int x, y; + if ((data & 0x80000000) != 0) + { + x = -1; + y = -1; + } + else + { + x = (int)(data & 0xFF); + y = (int)((data & 0xFF00) >> 8); + } + expad.SetSyncData(0, x); + expad.SetSyncData(1, y); + nes.SetZapperPos(x, y); + } + if (excontroller_select != (int)EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN) + { + if ((data & 0x0010000) != 0) + expad.SetSyncData(2, 1); + else + expad.SetSyncData(2, 0); + } + else + { + expad.SetSyncData(2, (byte)(data >> 16)); + } + break; + case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER: + expad.SetSyncData(0, (int)data); + break; + case EXCONTROLLER.EXCONTROLLER_TOPRIDER: + expad.SetSyncData(0, (int)data); + break; + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A: + case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B: + expad.SetSyncData(0, (int)data); + break; + case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING: + expad.SetSyncData(0, (int)data); + break; + case EXCONTROLLER.EXCONTROLLER_MAHJANG: + expad.SetSyncData(0, (int)data); + break; + default: + break; + } + } + } + + public enum VSType + { + VS_TYPE0 = 0, // SELECT1P=START1P/SELECT2P=START2P 1P/2P No reverse + VS_TYPE1, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse + VS_TYPE2, // SELECT1P=START1P/START1P =START2P 1P/2P No reverse + VS_TYPE3, // SELECT1P=START1P/START1P =START2P 1P/2P Reverse + VS_TYPE4, // SELECT1P=START1P/SELECT2P=START2P 1P/2P No reverse (Protection) + VS_TYPE5, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse (Protection) + VS_TYPE6, // SELECT1P=START1P/SELECT2P=START2P 1P/2P Reverse (For Golf) + VS_TYPEZ, // ZAPPER + } + + public enum EXCONTROLLER + { + EXCONTROLLER_NONE = 0, + EXCONTROLLER_PADDLE, + EXCONTROLLER_HYPERSHOT, + EXCONTROLLER_ZAPPER, + EXCONTROLLER_KEYBOARD, + EXCONTROLLER_CRAZYCLIMBER, + EXCONTROLLER_TOPRIDER, + EXCONTROLLER_SPACESHADOWGUN, + + EXCONTROLLER_FAMILYTRAINER_A, + EXCONTROLLER_FAMILYTRAINER_B, + EXCONTROLLER_EXCITINGBOXING, + EXCONTROLLER_MAHJANG, + EXCONTROLLER_OEKAKIDS_TABLET, + EXCONTROLLER_TURBOFILE, + + EXCONTROLLER_VSUNISYSTEM, + EXCONTROLLER_VSZAPPER, + + EXCONTROLLER_GYROMITE, + EXCONTROLLER_STACKUP, + + EXCONTROLLER_SUPOR_KEYBOARD, + } +} diff --git a/Core/VirtualNes.Core/PPU.cs b/Core/VirtualNes.Core/PPU.cs new file mode 100644 index 0000000..69c6f9d --- /dev/null +++ b/Core/VirtualNes.Core/PPU.cs @@ -0,0 +1,1195 @@ +using System; +using System.Runtime.InteropServices; + +namespace VirtualNes.Core +{ + public unsafe class PPU + { + public const int SCREEN_WIDTH = 272; + public const int SCREEN_HEIGHT = 240; + + private GCHandle BGwriteGCH; + private GCHandle BGmonoGCH; + private GCHandle SPwriteGCH; + + private byte* BGwrite; + private byte* BGmono; + private byte* SPwrite; + + private static byte[][] CreateCOLORMAP() + { + byte[][] res = new byte[5][]; + res[0] = new byte[64] + { 0x35, 0xFF, 0x16, 0x22, 0x1C, 0xFF, 0xFF, 0x15, + 0xFF, 0x00, 0x27, 0x05, 0x04, 0x27, 0x08, 0x30, + 0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12, + 0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, + 0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0xFF, 0xFF, + 0xFF, 0x07, 0x34, 0x06, 0x13, 0xFF, 0x26, 0x0F, + 0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17, + 0xFF, 0x11, 0x09, 0xFF, 0xFF, 0x25, 0x18, 0xFF + }; + res[1] = new byte[64] + { 0xFF, 0x27, 0x18, 0xFF, 0x3A, 0x25, 0xFF, 0x31, + 0x16, 0x13, 0x38, 0x34, 0x20, 0x23, 0x31, 0x1A, + 0xFF, 0x21, 0x06, 0xFF, 0x1B, 0x29, 0xFF, 0x22, + 0xFF, 0x24, 0xFF, 0xFF, 0xFF, 0x08, 0xFF, 0x03, + 0xFF, 0x36, 0x26, 0x33, 0x11, 0xFF, 0x10, 0x02, + 0x14, 0xFF, 0x00, 0x09, 0x12, 0x0F, 0xFF, 0x30, + 0xFF, 0xFF, 0x2A, 0x17, 0x0C, 0x01, 0x15, 0x19, + 0xFF, 0x2C, 0x07, 0x37, 0xFF, 0x05, 0xFF, 0xFF + }; + res[2] = new byte[64] + { 0xFF, 0xFF, 0xFF, 0x10, 0x1A, 0x30, 0x31, 0x09, + 0x01, 0x0F, 0x36, 0x08, 0x15, 0xFF, 0xFF, 0xF0, + 0x22, 0x1C, 0xFF, 0x12, 0x19, 0x18, 0x17, 0xFF, + 0x00, 0xFF, 0xFF, 0x02, 0x16, 0x06, 0xFF, 0x35, + 0x23, 0xFF, 0x8B, 0xF7, 0xFF, 0x27, 0x26, 0x20, + 0x29, 0xFF, 0x21, 0x24, 0x11, 0xFF, 0xEF, 0xFF, + 0x2C, 0xFF, 0xFF, 0xFF, 0x07, 0xF9, 0x28, 0xFF, + 0x0A, 0xFF, 0x32, 0x37, 0x13, 0xFF, 0xFF, 0x0C + }; + res[3] = new byte[64] + { 0x18, 0xFF, 0x1C, 0x89, 0x0F, 0xFF, 0x01, 0x17, // 00-07 + 0x10, 0x0F, 0x2A, 0xFF, 0x36, 0x37, 0x1A, 0xFF, // 08-0F + 0x25, 0xFF, 0x12, 0xFF, 0x0F, 0xFF, 0xFF, 0x26, // 10-17 + 0xFF, 0xFF, 0x22, 0xFF, 0xFF, 0x0F, 0x3A, 0x21, // 18-1F + 0x05, 0x0A, 0x07, 0xC2, 0x13, 0xFF, 0x00, 0x15, // 20-27 + 0x0C, 0xFF, 0x11, 0xFF, 0xFF, 0x38, 0xFF, 0xFF, // 28-2F + 0xFF, 0xFF, 0x08, 0x16, 0xFF, 0xFF, 0x30, 0x3C, // 30-37 + 0x0F, 0x27, 0xFF, 0x60, 0x29, 0xFF, 0x30, 0x09 // 38-3F + }; + res[4] = new byte[64] + { + // Super Xevious/Gradius + 0x35, 0xFF, 0x16, 0x22, 0x1C, 0x09, 0xFF, 0x15, // 00-07 + 0x20, 0x00, 0x27, 0x05, 0x04, 0x28, 0x08, 0x30, // 08-0F + 0x21, 0xFF, 0xFF, 0x29, 0x3C, 0xFF, 0x36, 0x12, // 10-17 + 0xFF, 0x2B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, // 18-1F + 0xFF, 0x31, 0xFF, 0x2A, 0x2C, 0x0C, 0x1B, 0xFF, // 20-27 + 0xFF, 0x07, 0x34, 0x06, 0xFF, 0x25, 0x26, 0x0F, // 28-2F + 0xFF, 0x19, 0x10, 0x0A, 0xFF, 0xFF, 0xFF, 0x17, // 30-37 + 0xFF, 0x11, 0x1A, 0xFF, 0x38, 0xFF, 0x18, 0x3A, // 38-3F + }; + + return res; + } + private static byte[][] VSColorMap = CreateCOLORMAP(); + + // PPU Control Register #1 PPU #0 + public const byte PPU_VBLANK_BIT = 0x80; + public const byte PPU_SPHIT_BIT = 0x40; // 堘偆丠 + public const byte PPU_SP16_BIT = 0x20; + public const byte PPU_BGTBL_BIT = 0x10; + public const byte PPU_SPTBL_BIT = 0x08; + public const byte PPU_INC32_BIT = 0x04; + public const byte PPU_NAMETBL_BIT = 0x03; + + // PPU Control Register #2 PPU #1 + public const byte PPU_BGCOLOR_BIT = 0xE0; + public const byte PPU_SPDISP_BIT = 0x10; + public const byte PPU_BGDISP_BIT = 0x08; + public const byte PPU_SPCLIP_BIT = 0x04; + public const byte PPU_BGCLIP_BIT = 0x02; + public const byte PPU_COLORMODE_BIT = 0x01; + + // PPU Status Register PPU #2 + public const byte PPU_VBLANK_FLAG = 0x80; + public const byte PPU_SPHIT_FLAG = 0x40; + public const byte PPU_SPMAX_FLAG = 0x20; + public const byte PPU_WENABLE_FLAG = 0x10; + + // SPRITE Attribute + public const byte SP_VMIRROR_BIT = 0x80; + public const byte SP_HMIRROR_BIT = 0x40; + public const byte SP_PRIORITY_BIT = 0x20; + public const byte SP_COLOR_BIT = 0x03; + + private NES nes; + + private bool bExtLatch; // For MMC5 + private bool bChrLatch; // For MMC2/MMC4 + private bool bExtNameTable; // For Super Monkey no Dai Bouken + private bool bExtMono; // For Final Fantasy + + private ushort loopy_y; + private ushort loopy_shift; + + private GCHandle lpScreenGCH; + private uint* lpScreen; + /// 作为lpScreen数组的索引 + private uint* lpScanline; + private int ScanlineNo; + private byte[] lpColormode; + + private bool bVSMode; + private int nVSColorMap; + private byte VSSecurityData; + private byte[] Bit2Rev = new byte[256]; + + + public PPU(NES nes) + { + this.nes = nes; + lpScreen = null; + lpColormode = null; + + bVSMode = false; + nVSColorMap = -1; + VSSecurityData = 0; + + for (int i = 0; i < 256; i++) + { + byte m = 0x80; + byte c = 0; + for (int j = 0; j < 8; j++) + { + if ((i & (1 << j)) > 0) c |= m; + m >>= 1; + } + Bit2Rev[i] = c; + } + + BGwriteGCH = GCHandle.Alloc(new byte[33 + 1], GCHandleType.Pinned); + BGmonoGCH = GCHandle.Alloc(new byte[33 + 1], GCHandleType.Pinned); + SPwriteGCH = GCHandle.Alloc(new byte[33 + 1], GCHandleType.Pinned); + BGwrite = (byte*)BGwriteGCH.AddrOfPinnedObject(); + BGmono = (byte*)BGmonoGCH.AddrOfPinnedObject(); + SPwrite = (byte*)SPwriteGCH.AddrOfPinnedObject(); + } + + public void Dispose() + { + lpScreenGCH.Free(); + BGwriteGCH.Free(); + BGmonoGCH.Free(); + SPwriteGCH.Free(); + } + + internal byte Read(ushort addr) + { + byte data = 0x00; + + switch (addr) + { + // Write only Register + case 0x2000: // PPU Control Register #1(W) + case 0x2001: // PPU Control Register #2(W) + case 0x2003: // SPR-RAM Address Register(W) + case 0x2005: // PPU Scroll Register(W2) + case 0x2006: // VRAM Address Register(W2) + data = MMU.PPU7_Temp; // 懡暘 + break; + // Read/Write Register + case 0x2002: // PPU Status Register(R) + //DEBUGOUT( "2002 RD L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() ); + data = (byte)(MMU.PPUREG[2] | VSSecurityData); + MMU.PPU56Toggle = 0; + byte temp = unchecked((byte)~PPU_VBLANK_FLAG); + MMU.PPUREG[2] &= temp; + break; + case 0x2004: // SPR_RAM I/O Register(RW) + data = MMU.SPRAM[MMU.PPUREG[3]++]; + break; + case 0x2007: // VRAM I/O Register(RW) + addr = (ushort)(MMU.loopy_v & 0x3FFF); + data = MMU.PPU7_Temp; + if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32; + else MMU.loopy_v++; + if (addr >= 0x3000) + { + if (addr >= 0x3F00) + { + // data &= 0x3F; + if ((addr & 0x0010) == 0) + { + return MMU.BGPAL[addr & 0x000F]; + } + else + { + return MMU.SPPAL[addr & 0x000F]; + } + } + addr &= 0xEFFF; + } + MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10][addr & 0x03FF]; + break; + } + + return data; + } + + internal void SetRenderScanline(int scanline) + { + ScanlineNo = scanline; + if (scanline < 240) + { + lpScanline = lpScreen + SCREEN_WIDTH * scanline; + } + } + + internal void Write(ushort addr, byte data) + { + if (bVSMode && VSSecurityData != 0) + { + if (addr == 0x2000) + { + addr = 0x2001; + } + else if (addr == 0x2001) + { + addr = 0x2000; + } + } + + switch (addr) + { + // Read only Register + case 0x2002: // PPU Status register(R) + break; + // Write Register + case 0x2000: // PPU Control Register #1(W) + // NameTable select + // t:0000110000000000=d:00000011 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0xF3FF) | ((data & 0x03) << 10)); + + if ((data & 0x80) != 0 && (MMU.PPUREG[0] & 0x80) == 0 && (MMU.PPUREG[2] & 0x80) != 0) + { + nes.cpu.NMI(); // hmm... + } + + MMU.PPUREG[0] = data; + break; + case 0x2001: // PPU Control Register #2(W) + MMU.PPUREG[1] = data; + break; + case 0x2003: // SPR-RAM Address Register(W) + MMU.PPUREG[3] = data; + break; + case 0x2004: // SPR_RAM I/O Register(RW) + MMU.SPRAM[MMU.PPUREG[3]++] = data; + break; + + case 0x2005: // PPU Scroll Register(W2) + //DEBUGOUT( "SCR WRT L:%3d C:%8d\n", ScanlineNo, nes->cpu->GetTotalCycles() ); + if (MMU.PPU56Toggle == 0) + { + // First write + // tile X t:0000000000011111=d:11111000 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFFE0) | ((data) >> 3)); + // scroll offset X x=d:00000111 + MMU.loopy_x = (ushort)(data & 0x07); + } + else + { + // Second write + // tile Y t:0000001111100000=d:11111000 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFC1F) | (((data) & 0xF8) << 2)); + // scroll offset Y t:0111000000000000=d:00000111 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0x8FFF) | (((data) & 0x07) << 12)); + } + MMU.PPU56Toggle = (byte)(MMU.PPU56Toggle == 0 ? 1 : 0); + break; + case 0x2006: // VRAM Address Register(W2) + if (MMU.PPU56Toggle == 0) + { + // First write + // t:0011111100000000=d:00111111 + // t:1100000000000000=0 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0x00FF) | (((data) & 0x3F) << 8)); + } + else + { + // Second write + // t:0000000011111111=d:11111111 + MMU.loopy_t = (ushort)((MMU.loopy_t & 0xFF00) | data); + // v=t + MMU.loopy_v = MMU.loopy_t; + nes.mapper.PPU_Latch(MMU.loopy_v); + } + MMU.PPU56Toggle = (byte)(MMU.PPU56Toggle == 0 ? 1 : 0); + break; + case 0x2007: // VRAM I/O Register(RW) + ushort vaddr = (ushort)(MMU.loopy_v & 0x3FFF); + if ((MMU.PPUREG[0] & PPU_INC32_BIT) != 0) MMU.loopy_v += 32; + else MMU.loopy_v++; + + if (vaddr >= 0x3000) + { + if (vaddr >= 0x3F00) + { + data &= 0x3F; + if (bVSMode && nVSColorMap != -1) + { + byte temp = VSColorMap[nVSColorMap][data]; + if (temp != 0xFF) + { + data = (byte)(temp & 0x3F); + } + } + + if ((vaddr & 0x000F) == 0) + { + MMU.BGPAL[0] = MMU.SPPAL[0] = data; + } + else if ((vaddr & 0x0010) == 0) + { + MMU.BGPAL[vaddr & 0x000F] = data; + } + else + { + MMU.SPPAL[vaddr & 0x000F] = data; + } + MMU.BGPAL[0x04] = MMU.BGPAL[0x08] = MMU.BGPAL[0x0C] = MMU.BGPAL[0x00]; + MMU.SPPAL[0x00] = MMU.SPPAL[0x04] = MMU.SPPAL[0x08] = MMU.SPPAL[0x0C] = MMU.BGPAL[0x00]; + return; + } + vaddr &= 0xEFFF; + } + if (MMU.PPU_MEM_TYPE[vaddr >> 10] != MMU.BANKTYPE_VROM) + { + MMU.PPU_MEM_BANK[vaddr >> 10][vaddr & 0x03FF] = data; + } + break; + } + } + + internal void DMA(byte data) + { + ushort addr = (ushort)(data << 8); + + for (ushort i = 0; i < 256; i++) + { + MMU.SPRAM[i] = nes.Read((ushort)(addr + i)); + } + } + + internal void Reset() + { + bExtLatch = false; + bChrLatch = false; + bExtNameTable = false; + bExtMono = false; + + MMU.PPUREG[0] = MMU.PPUREG[1] = 0; + + MMU.PPU56Toggle = 0; + + MMU.PPU7_Temp = 0xFF; // VS Excitebike偱偍偐偟偔側傞($2006傪撉傒偵峴偔僶僌偑偁傞) + // PPU7_Temp = 0; + + MMU.loopy_v = MMU.loopy_t = 0; + MMU.loopy_x = loopy_y = 0; + loopy_shift = 0; + + if (lpScreen != null) + MemoryUtility.memset(lpScreen, 0, 0, SCREEN_WIDTH * SCREEN_HEIGHT); + if (lpColormode != null) + MemoryUtility.memset(lpColormode, 0, SCREEN_HEIGHT); + } + + internal void FrameStart() + { + if ((MMU.PPUREG[1] & (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) != 0) + { + MMU.loopy_v = MMU.loopy_t; + loopy_shift = MMU.loopy_x; + loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12); + } + + if (lpScreen != null) + { + MemoryUtility.memset(lpScreen, 0, 0x3f, SCREEN_WIDTH); + } + if (lpColormode != null) + { + lpColormode[0] = 0; + } + } + + internal void ScanlineNext() + { + if ((MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0) + { + if ((MMU.loopy_v & 0x7000) == 0x7000) + { + MMU.loopy_v &= 0x8FFF; + if ((MMU.loopy_v & 0x03E0) == 0x03A0) + { + MMU.loopy_v ^= 0x0800; + MMU.loopy_v &= 0xFC1F; + } + else + { + if ((MMU.loopy_v & 0x03E0) == 0x03E0) + { + MMU.loopy_v &= 0xFC1F; + } + else + { + MMU.loopy_v += 0x0020; + } + } + } + else + { + MMU.loopy_v += 0x1000; + } + loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12); + } + } + + internal void ScanlineStart() + { + if ((MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0) + { + MMU.loopy_v = (ushort)((MMU.loopy_v & 0xFBE0) | (MMU.loopy_t & 0x041F)); + loopy_shift = MMU.loopy_x; + loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12); + nes.mapper.PPU_Latch((ushort)(0x2000 + (MMU.loopy_v & 0x0FFF))); + } + } + + internal void Scanline(int scanline, bool bMax, bool bLeftClip) + { + byte chr_h = 0, chr_l = 0, attr = 0; + + MemoryUtility.memset(BGwrite, 0, 34); + MemoryUtility.memset(BGmono, 0, 34); + + // Linecolor mode + lpColormode[scanline] = (byte)(((MMU.PPUREG[1] & PPU_BGCOLOR_BIT) >> 5) | ((MMU.PPUREG[1] & PPU_COLORMODE_BIT) << 7)); + + // Render BG + if ((MMU.PPUREG[1] & PPU_BGDISP_BIT) == 0) + { + MemoryUtility.memset(lpScanline, MMU.BGPAL[0], SCREEN_WIDTH); + if (nes.GetRenderMethod() == EnumRenderMethod.TILE_RENDER) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4 * 32); + } + } + else + { + if (nes.GetRenderMethod() != EnumRenderMethod.TILE_RENDER) + { + if (!bExtLatch) + { + // Without Extension Latch + uint* pScn = lpScanline + (8 - loopy_shift); + byte* pBGw = BGwrite; + int tileofs = (MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8; + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int attradr = 0x23C0 + (MMU.loopy_v & 0x0C00) + ((MMU.loopy_v & 0x0380) >> 4); + int ntbl_x = ntbladr & 0x001F; + int attrsft = (ntbladr & 0x0040) >> 4; + var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10]; + + int tileadr; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + attradr &= 0x3FF; + + for (int i = 0; i < 33; i++) + { + tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + + if (cache_tile == tileadr && cache_attr == attr) + { + *(UInt128*)(pScn + 0) = *(UInt128*)(pScn - 8); + *(UInt128*)(pScn + 4) = *(UInt128*)(pScn - 4); + *(pBGw + 0) = *(pBGw - 1); + } + else + { + cache_tile = tileadr; + cache_attr = attr; + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + *pBGw = (byte)(chr_h | chr_l); + + fixed (byte* pBGPAL = &MMU.BGPAL[attr]) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + pScn[0] = pBGPAL[(c1 >> 6)]; + pScn[4] = pBGPAL[(c1 >> 2) & 3]; + pScn[1] = pBGPAL[(c2 >> 6)]; + pScn[5] = pBGPAL[(c2 >> 2) & 3]; + pScn[2] = pBGPAL[(c1 >> 4) & 3]; + pScn[6] = pBGPAL[c1 & 3]; + pScn[3] = pBGPAL[(c2 >> 4) & 3]; + pScn[7] = pBGPAL[c2 & 3]; + } + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)(tileadr)); + } + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4); + pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10]; + } + else + { + ntbladr++; + } + } + } + else + { + // With Extension Latch(For MMC5) + uint* pScn = lpScanline + (8 - loopy_shift); + byte* pBGw = BGwrite; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int ntbl_x = ntbladr & 0x1F; + + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + byte exattr = 0; + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + nes.mapper.PPU_ExtLatchX(i); + nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr); + attr = (byte)(exattr & 0x0C); + + if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr) + { + cache_tile = ((chr_h << 8) + chr_l); + cache_attr = attr; + *pBGw = (byte)(chr_h | chr_l); + + fixed (byte* pBGPAL = &MMU.BGPAL[attr]) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + pScn[0] = pBGPAL[(c1 >> 6)]; + pScn[4] = pBGPAL[(c1 >> 2) & 3]; + pScn[1] = pBGPAL[(c2 >> 6)]; + pScn[5] = pBGPAL[(c2 >> 2) & 3]; + pScn[2] = pBGPAL[(c1 >> 4) & 3]; + pScn[6] = pBGPAL[c1 & 3]; + pScn[3] = pBGPAL[(c2 >> 4) & 3]; + pScn[7] = pBGPAL[c2 & 3]; + } + } + else + { + *(UInt128*)(pScn + 0) = *(UInt128*)(pScn - 8); + *(UInt128*)(pScn + 4) = *(UInt128*)(pScn - 4); + *(pBGw + 0) = *(pBGw - 1); + } + pScn += 8; + pBGw++; + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + } + else + { + ntbladr++; + } + } + } + } + else + { + if (!bExtLatch) + { + // Without Extension Latch + if (!bExtNameTable) + { + uint* pScn = lpScanline + (8 - loopy_shift); + byte* pBGw = BGwrite; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int attradr = 0x03C0 + ((MMU.loopy_v & 0x0380) >> 4); + int ntbl_x = ntbladr & 0x001F; + int attrsft = (ntbladr & 0x0040) >> 4; + var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10]; + + int tileadr = 0; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + + if (cache_tile != tileadr || cache_attr != attr) + { + cache_tile = tileadr; + cache_attr = attr; + + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + *pBGw = (byte)(chr_l | chr_h); + + fixed (byte* pBGPAL = &MMU.BGPAL[attr]) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + pScn[0] = pBGPAL[(c1 >> 6)]; + pScn[4] = pBGPAL[(c1 >> 2) & 3]; + pScn[1] = pBGPAL[(c2 >> 6)]; + pScn[5] = pBGPAL[(c2 >> 2) & 3]; + pScn[2] = pBGPAL[(c1 >> 4) & 3]; + pScn[6] = pBGPAL[c1 & 3]; + pScn[3] = pBGPAL[(c2 >> 4) & 3]; + pScn[7] = pBGPAL[c2 & 3]; + } + } + else + { + *(UInt128*)(pScn + 0) = *(UInt128*)(pScn - 8); + *(UInt128*)(pScn + 4) = *(UInt128*)(pScn - 4); + *(pBGw + 0) = *(pBGw - 1); + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)(tileadr)); + } + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4); + pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10]; + } + else + { + ntbladr++; + } + } + } + else + { + uint* pScn = lpScanline + (8 - loopy_shift); + byte* pBGw = BGwrite; + + int ntbladr; + int tileadr; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + ushort loopy_v_tmp = MMU.loopy_v; + + for (int i = 0; i < 33; i++) + { + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + + ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + MMU.PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + ((MMU.loopy_v & 0x7000) >> 12); + attr = (byte)(((MMU.PPU_MEM_BANK[ntbladr >> 10][0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2)] >> (((ntbladr & 0x40) >> 4) + (ntbladr & 0x02))) & 3) << 2); + + if (cache_tile != tileadr || cache_attr != attr) + { + cache_tile = tileadr; + cache_attr = attr; + + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; + *pBGw = (byte)(chr_l | chr_h); + + fixed (byte* pBGPAL = &MMU.BGPAL[attr]) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + pScn[0] = pBGPAL[(c1 >> 6)]; + pScn[4] = pBGPAL[(c1 >> 2) & 3]; + pScn[1] = pBGPAL[(c2 >> 6)]; + pScn[5] = pBGPAL[(c2 >> 2) & 3]; + pScn[2] = pBGPAL[(c1 >> 4) & 3]; + pScn[6] = pBGPAL[c1 & 3]; + pScn[3] = pBGPAL[(c2 >> 4) & 3]; + pScn[7] = pBGPAL[c2 & 3]; + } + } + else + { + *(UInt128*)(pScn + 0) = *(UInt128*)(pScn - 8); + *(UInt128*)(pScn + 4) = *(UInt128*)(pScn - 4); + *(pBGw + 0) = *(pBGw - 1); + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)tileadr); + } + + if ((MMU.loopy_v & 0x1F) == 0x1F) + { + MMU.loopy_v ^= 0x041F; + } + else + { + MMU.loopy_v++; + } + } + MMU.loopy_v = loopy_v_tmp; + } + } + else + { + // With Extension Latch(For MMC5) + uint* pScn = lpScanline + (8 - loopy_shift); + byte* pBGw = BGwrite; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int ntbl_x = ntbladr & 0x1F; + + int cache_tile = unchecked((int)0xFFFF0000); + byte cache_attr = 0xFF; + + byte exattr = 0; + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + nes.mapper.PPU_ExtLatchX(i); + nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr); + attr = (byte)(exattr & 0x0C); + + if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr) + { + cache_tile = ((chr_h << 8) + chr_l); + cache_attr = attr; + *pBGw = (byte)(chr_l | chr_h); + + fixed (byte* pBGPAL = &MMU.BGPAL[attr]) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + pScn[0] = pBGPAL[(c1 >> 6)]; + pScn[4] = pBGPAL[(c1 >> 2) & 3]; + pScn[1] = pBGPAL[(c2 >> 6)]; + pScn[5] = pBGPAL[(c2 >> 2) & 3]; + pScn[2] = pBGPAL[(c1 >> 4) & 3]; + pScn[6] = pBGPAL[c1 & 3]; + pScn[3] = pBGPAL[(c2 >> 4) & 3]; + pScn[7] = pBGPAL[c2 & 3]; + } + } + else + { + *(UInt128*)(pScn + 0) = *(UInt128*)(pScn - 8); + *(UInt128*)(pScn + 4) = *(UInt128*)(pScn - 4); + *(pBGw + 0) = *(pBGw - 1); + } + pScn += 8; + pBGw++; + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + } + else + { + ntbladr++; + } + } + } + } + if ((MMU.PPUREG[1] & PPU_BGCLIP_BIT) == 0 && bLeftClip) + { + uint* pScn = lpScanline + 8; + for (int i = 0; i < 8; i++) + { + pScn[i] = MMU.BGPAL[0]; + } + } + } + + // Render sprites + var temp = ~PPU_SPMAX_FLAG; + MMU.PPUREG[2] &= (byte)(MMU.PPUREG[2] & temp); + + // 昞帵婜娫奜偱偁傟偽僉儍儞僙儖 + if (scanline > 239) + return; + + if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0) + { + return; + } + + int spmax = 0; + int spraddr = 0, sp_y = 0, sp_h = 0; + chr_h = chr_l = 0; + + fixed (byte* pBit2Rev = &Bit2Rev[0]) + { + byte* pBGw = BGwrite; + byte* pSPw = SPwrite; + MemoryUtility.memset(pSPw, 0, 34); + + spmax = 0; + Sprite sp = new Sprite(MMU.SPRAM, 0); + sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7; + + // Left clip + if (bLeftClip && ((MMU.PPUREG[1] & PPU_SPCLIP_BIT) == 0)) + { + SPwrite[0] = 0xFF; + } + + for (int i = 0; i < 64; i++, sp.AddOffset(1)) + { + sp_y = scanline - (sp.y + 1); + // 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋 + if (sp_y != (sp_y & sp_h)) + continue; + + if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0) + { + // 8x8 Sprite + spraddr = ((MMU.PPUREG[0] & PPU_SPTBL_BIT) << 9) + (sp.tile << 4); + if ((sp.attr & SP_VMIRROR_BIT) == 0) + spraddr += sp_y; + else + spraddr += 7 - sp_y; + } + else + { + // 8x16 Sprite + spraddr = ((sp.tile & 1) << 12) + ((sp.tile & 0xFE) << 4); + if ((sp.attr & SP_VMIRROR_BIT) == 0) + spraddr += ((sp_y & 8) << 1) + (sp_y & 7); + else + spraddr += ((~sp_y & 8) << 1) + (7 - (sp_y & 7)); + } + // Character pattern + chr_l = MMU.PPU_MEM_BANK[spraddr >> 10][spraddr & 0x3FF]; + chr_h = MMU.PPU_MEM_BANK[spraddr >> 10][(spraddr & 0x3FF) + 8]; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)spraddr); + } + + // pattern mask + if ((sp.attr & SP_HMIRROR_BIT) != 0) + { + chr_l = pBit2Rev[chr_l]; + chr_h = pBit2Rev[chr_h]; + } + byte SPpat = (byte)(chr_l | chr_h); + + // Sprite hitcheck + if (i == 0 && (MMU.PPUREG[2] & PPU_SPHIT_FLAG) == 0) + { + int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3; + int BGsft = 8 - ((loopy_shift + sp.x) & 7); + byte BGmsk = (byte)(((pBGw[BGpos + 0] << 8) | pBGw[BGpos + 1]) >> BGsft); + + if ((SPpat & BGmsk) != 0) + { + MMU.PPUREG[2] |= PPU_SPHIT_FLAG; + } + } + + // Sprite mask + int SPpos = sp.x / 8; + int SPsft = 8 - (sp.x & 7); + byte SPmsk = (byte)(((pSPw[SPpos + 0] << 8) | pSPw[SPpos + 1]) >> SPsft); + ushort SPwrt = (ushort)(SPpat << SPsft); + pSPw[SPpos + 0] = (byte)((pSPw[SPpos + 0]) | (SPwrt >> 8)); + pSPw[SPpos + 1] = (byte)((pSPw[SPpos + 1]) | (SPwrt & 0xFF)); + SPpat = (byte)(SPpat & ~SPmsk); + + if ((sp.attr & SP_PRIORITY_BIT) != 0) + { + // BG > SP priority + int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3; + int BGsft = 8 - ((loopy_shift + sp.x) & 7); + byte BGmsk = (byte)(((pBGw[BGpos + 0] << 8) | pBGw[BGpos + 1]) >> BGsft); + + SPpat = (byte)(SPpat & ~BGmsk); + } + + // Attribute + fixed (byte* pSPPAL = &MMU.SPPAL[(sp.attr & SP_COLOR_BIT) << 2]) + { + // Ptr + uint* pScn = lpScanline + sp.x + 8; + + if (!bExtMono) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + if ((SPpat & 0x80) != 0) pScn[0] = pSPPAL[(c1 >> 6)]; + if ((SPpat & 0x08) != 0) pScn[4] = pSPPAL[(c1 >> 2) & 3]; + if ((SPpat & 0x40) != 0) pScn[1] = pSPPAL[(c2 >> 6)]; + if ((SPpat & 0x04) != 0) pScn[5] = pSPPAL[(c2 >> 2) & 3]; + if ((SPpat & 0x20) != 0) pScn[2] = pSPPAL[(c1 >> 4) & 3]; + if ((SPpat & 0x02) != 0) pScn[6] = pSPPAL[c1 & 3]; + if ((SPpat & 0x10) != 0) pScn[3] = pSPPAL[(c2 >> 4) & 3]; + if ((SPpat & 0x01) != 0) pScn[7] = pSPPAL[c2 & 3]; + } + else + { + // Monocrome effect (for Final Fantasy) + byte mono = BGmono[((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3]; + + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + if ((SPpat & 0x80) != 0) pScn[0] = (byte)(pSPPAL[c1 >> 6] | mono); + if ((SPpat & 0x08) != 0) pScn[4] = (byte)(pSPPAL[(c1 >> 2) & 3] | mono); + if ((SPpat & 0x40) != 0) pScn[1] = (byte)(pSPPAL[c2 >> 6] | mono); + if ((SPpat & 0x04) != 0) pScn[5] = (byte)(pSPPAL[(c2 >> 2) & 3] | mono); + if ((SPpat & 0x20) != 0) pScn[2] = (byte)(pSPPAL[(c1 >> 4) & 3] | mono); + if ((SPpat & 0x02) != 0) pScn[6] = (byte)(pSPPAL[c1 & 3] | mono); + if ((SPpat & 0x10) != 0) pScn[3] = (byte)(pSPPAL[(c2 >> 4) & 3] | mono); + if ((SPpat & 0x01) != 0) pScn[7] = (byte)(pSPPAL[c2 & 3] | mono); + } + } + + if (++spmax > 8 - 1) + { + if (!bMax) + break; + } + } + if (spmax > 8 - 1) + { + MMU.PPUREG[2] |= PPU_SPMAX_FLAG; + } + } + } + + internal bool IsSprite0(int scanline) + { + // 僗僾儔僀僩orBG旕昞帵偼僉儍儞僙儖(僸僢僩偟側偄) + if ((MMU.PPUREG[1] & (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) + return false; + + // 婛偵僸僢僩偟偰偄偨傜僉儍儞僙儖 + if ((MMU.PPUREG[2] & PPU_SPHIT_FLAG) != 0) + return false; + + if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0) + { + // 8x8 + if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 7 + 1))) + return false; + } + else + { + // 8x16 + if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 15 + 1))) + return false; + } + + return true; + } + + internal void DummyScanline(int scanline) + { + int i; + int spmax; + int sp_h; + + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPMAX_FLAG); + + // 僗僾儔僀僩旕昞帵偼僉儍儞僙儖 + if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0) + return; + + // 昞帵婜娫奜偱偁傟偽僉儍儞僙儖 + if (scanline < 0 || scanline > 239) + return; + + Sprite sp = new Sprite(MMU.SPRAM, 0); + sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7; + + spmax = 0; + // Sprite Max check + for (i = 0; i < 64; i++, sp.AddOffset(1)) + { + // 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋 + if ((scanline < sp.y + 1) || (scanline > (sp.y + sp_h + 1))) + { + continue; + } + + if (++spmax > 8 - 1) + { + MMU.PPUREG[2] |= PPU_SPMAX_FLAG; + break; + } + } + } + + internal void VBlankEnd() + { + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_VBLANK_FLAG); + // VBlank扙弌帪偵僋儕傾偝傟傞 + // 僄僉僒僀僩僶僀僋偱廳梫 + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPHIT_FLAG); + } + + internal void VBlankStart() + { + MMU.PPUREG[2] |= PPU_VBLANK_FLAG; + } + + public uint* GetScreenPtr() + { + return lpScreen; + } + + public byte[] GetLineColorMode() + { + return lpColormode; + } + + internal void InitBuffer() + { + var screenBuffer = new uint[SCREEN_WIDTH * SCREEN_HEIGHT]; + var colormode = new byte[SCREEN_HEIGHT]; + + lpScreenGCH = GCHandle.Alloc(screenBuffer, GCHandleType.Pinned); + lpScreen = (uint*)lpScreenGCH.AddrOfPinnedObject(); + lpColormode = colormode; + } + + + internal bool IsDispON() + { + return (MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0; + } + + internal void SetExtLatchMode(bool bMode) + { + bExtLatch = bMode; + } + + internal ushort GetPPUADDR() + { + return MMU.loopy_v; + } + + internal ushort GetTILEY() + { + return loopy_y; + } + + internal void SetChrLatchMode(bool bMode) + { + bChrLatch = bMode; + } + + internal void SetExtNameTableMode(bool bMode) + { + bExtNameTable = bMode; + } + + internal void SetExtMonoMode(bool bMode) + { + bExtMono = bMode; + } + + internal int GetScanlineNo() + { + return ScanlineNo; + } + + public struct Sprite + { + public byte y + { + get => raw[offset + 0]; + set => raw[offset + 0] = value; + } + + public byte tile + { + get => raw[offset + 1]; + set => raw[offset + 1] = value; + } + public byte attr + { + get => raw[offset + 2]; + set => raw[offset + 2] = value; + } + public byte x + { + get => raw[offset + 3]; + set => raw[offset + 3] = value; + } + + private byte[] raw; + private int offset; + + public Sprite(byte[] raw, int offset) + { + this.raw = raw; + this.offset = offset * 4; + } + + public void AddOffset(int offset) + { + this.offset += offset * 4; + } + } + } + + [StructLayout(LayoutKind.Explicit, Size = 16)] + public struct UInt128 + { + [FieldOffset(0)] + public UInt32 a; + [FieldOffset(4)] + public UInt32 b; + [FieldOffset(8)] + public UInt32 c; + [FieldOffset(12)] + public UInt32 d; + } +} diff --git a/Core/VirtualNes.Core/PadEX/EXPAD.cs b/Core/VirtualNes.Core/PadEX/EXPAD.cs new file mode 100644 index 0000000..24468c6 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD.cs @@ -0,0 +1,24 @@ +namespace VirtualNes.Core +{ + public class EXPAD + { + protected NES nes; + + public EXPAD(NES parent) + { + nes = parent; + } + + public virtual void Dispose() { } + + public virtual void Reset() { } + public virtual void Strobe() { } + public virtual byte Read4016() { return 0x00; } + public virtual byte Read4017() { return 0x00; } + public virtual void Write4016(byte data) { } + public virtual void Write4017(byte data) { } + public virtual void Sync() { } + public virtual void SetSyncData(int type, int data) { } + public virtual int GetSyncData(int type) { return 0x00; } + } +} diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs b/Core/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs new file mode 100644 index 0000000..83b288e --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_CrazyClimber : EXPAD + { + public EXPAD_CrazyClimber(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs b/Core/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs new file mode 100644 index 0000000..99e5a1d --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_ExcitingBoxing : EXPAD + { + public EXPAD_ExcitingBoxing(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs b/Core/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs new file mode 100644 index 0000000..5fe5815 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_FamlyTrainer : EXPAD + { + public EXPAD_FamlyTrainer(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs new file mode 100644 index 0000000..4b62af2 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_Gyromite : EXPAD + { + public EXPAD_Gyromite(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs b/Core/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs new file mode 100644 index 0000000..ac2e89e --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_HyperShot : EXPAD + { + public EXPAD_HyperShot(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs new file mode 100644 index 0000000..64585c8 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_Keyboard : EXPAD + { + public EXPAD_Keyboard(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs new file mode 100644 index 0000000..841f5d5 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_Mahjang : EXPAD + { + public EXPAD_Mahjang(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs b/Core/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs new file mode 100644 index 0000000..50c7560 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_OekakidsTablet : EXPAD + { + public EXPAD_OekakidsTablet(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Paddle.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Paddle.cs new file mode 100644 index 0000000..3536731 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Paddle.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + public class EXPAD_Paddle : EXPAD + { + public EXPAD_Paddle(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs b/Core/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs new file mode 100644 index 0000000..1d8d370 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_SpaceShadowGun : EXPAD + { + public EXPAD_SpaceShadowGun(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs new file mode 100644 index 0000000..f93347a --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_Supor_Keyboard : EXPAD + { + public EXPAD_Supor_Keyboard(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Toprider.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Toprider.cs new file mode 100644 index 0000000..3a58afb --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Toprider.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_Toprider : EXPAD + { + public EXPAD_Toprider(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs b/Core/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs new file mode 100644 index 0000000..f124e65 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_TurboFile : EXPAD + { + public EXPAD_TurboFile(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs b/Core/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs new file mode 100644 index 0000000..e4dff20 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_VSUnisystem : EXPAD + { + public EXPAD_VSUnisystem(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs b/Core/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs new file mode 100644 index 0000000..8a1480b --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs @@ -0,0 +1,9 @@ +namespace VirtualNes.Core +{ + internal class EXPAD_VSZapper : EXPAD + { + public EXPAD_VSZapper(NES parent) : base(parent) + { + } + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/PadEX/EXPAD_Zapper.cs b/Core/VirtualNes.Core/PadEX/EXPAD_Zapper.cs new file mode 100644 index 0000000..60eaca9 --- /dev/null +++ b/Core/VirtualNes.Core/PadEX/EXPAD_Zapper.cs @@ -0,0 +1,12 @@ +namespace VirtualNes.Core +{ + /// + /// 光枪 + /// + public class EXPAD_Zapper : EXPAD + { + public EXPAD_Zapper(NES parent) : base(parent) + { + } + } +} diff --git a/Core/VirtualNes.Core/ROM.cs b/Core/VirtualNes.Core/ROM.cs new file mode 100644 index 0000000..2f5966c --- /dev/null +++ b/Core/VirtualNes.Core/ROM.cs @@ -0,0 +1,438 @@ +using System; +using System.IO; +using VirtualNes.Core.Debug; + +namespace VirtualNes.Core +{ + public class ROM + { + protected NESHEADER header; + protected NSFHEADER nsfheader; + protected string path; + protected string name; + protected string fullpath; + protected bool bPAL; + protected bool bNSF; + protected int NSF_PAGE_SIZE; + protected byte[] lpPRG; + protected byte[] lpCHR; + protected byte[] lpTrainer; + protected byte[] lpDiskBios; + protected byte[] lpDisk; + protected uint crc; + protected uint crcall; + protected uint crcvrom; + protected int mapper; + protected int diskno; + protected uint fdsmakerID; + protected uint fdsgameID; + + public ROM(string fname) + { + Stream fp = null; + byte[] temp = null; + byte[] bios = null; + long FileSize = 0; + + header = NESHEADER.GetDefault(); + path = string.Empty; + name = string.Empty; + + bPAL = false; + bNSF = false; + NSF_PAGE_SIZE = 0; + + lpPRG = lpCHR = lpTrainer = lpDiskBios = lpDisk = null; + + crc = crcall = 0; + mapper = 0; + diskno = 0; + + try + { + fp = Supporter.OpenRom(fname); + if (fp == null) + { + throw new System.Exception($"Open Rom Failed:[{fname}]"); + } + + FileSize = fp.Length; + if (FileSize < 17) + { + throw new System.Exception($"File too small:[{fname}]"); + } + + temp = new byte[FileSize]; + fp.Read(temp, 0, temp.Length); + + fp.Dispose(); + + header = NESHEADER.Read(temp); + + if (!header.CheckValid()) + throw new Exception($"rom file is not valid:[{fname}]"); + + ulong PRGoffset, CHRoffset; + long PRGsize = 0, CHRsize = 0; + + var romType = header.GetRomType(); + if (romType == EnumRomType.NES) + { + PRGsize = (long)header.PRG_PAGE_SIZE * 0x4000; + CHRsize = (long)header.CHR_PAGE_SIZE * 0x2000; + PRGoffset = (ulong)NESHEADER.SizeOf(); + CHRoffset = PRGoffset + (ulong)PRGsize; + + if (IsTRAINER()) + { + PRGoffset += 512; + CHRoffset += 512; + } + + if (PRGsize <= 0 || (PRGsize + CHRsize) > FileSize) + { + throw new Exception($"Invalid NesHeader:[{fname}]"); + } + + //PRG BANK + lpPRG = new byte[PRGsize]; + Array.Copy(temp, (int)PRGoffset, lpPRG, 0, PRGsize); + + //CHR BANK + if (CHRsize > 0) + { + lpCHR = new byte[CHRsize]; + if (FileSize >= (long)CHRoffset + CHRsize) + { + Array.Copy(temp, (int)CHRoffset, lpCHR, 0, CHRsize); + } + else + { + //CHR Bank太少... + CHRsize -= ((long)CHRoffset + CHRsize - FileSize); + Array.Copy(temp, (int)CHRoffset, lpCHR, 0, CHRsize); + } + } + else + { + lpCHR = null; + } + + if (IsTRAINER()) + { + lpTrainer = new byte[512]; + Array.Copy(temp, NESHEADER.SizeOf(), lpTrainer, 0, 512); + } + else + { + lpTrainer = null; + } + } + else if (romType == EnumRomType.FDS) + { + diskno = header.PRG_PAGE_SIZE; + + if (FileSize < (16 + 65500 * diskno)) + { + throw new Exception($"Illegal Disk Size:[{fname}]"); + } + if (diskno > 8) + { + throw new Exception($"Unsupport disk:[{fname}]"); + } + + header = NESHEADER.GetDefault(); + header.ID[0] = (byte)'N'; + header.ID[1] = (byte)'E'; + header.ID[2] = (byte)'S'; + header.ID[3] = 0x1A; + header.PRG_PAGE_SIZE = (byte)(diskno * 4); + header.CHR_PAGE_SIZE = 0; + header.control1 = 0x40; + header.control2 = 0x10; + + PRGsize = NESHEADER.SizeOf() + 65500 * diskno; + //PRG BANK + lpPRG = new byte[PRGsize]; + lpDisk = new byte[PRGsize]; + lpCHR = null; + + var headerBuffer = header.DataToBytes(); + Array.Copy(headerBuffer, lpPRG, headerBuffer.Length); + Array.Copy(temp, NESHEADER.SizeOf(), lpPRG, NESHEADER.SizeOf(), 65500 * diskno); + + lpPRG[0] = (byte)'F'; + lpPRG[1] = (byte)'D'; + lpPRG[2] = (byte)'S'; + lpPRG[3] = 0x1A; + lpPRG[4] = (byte)diskno; + + fp = Supporter.OpenFile_DISKSYS(); + if (fp == null) + { + throw new Exception($"Not found DISKSYS.ROM for [{fname}]"); + } + + FileSize = fp.Length; + if (FileSize < 17) + { + throw new Exception($"Small File Of DISKSYS.ROM"); + } + + bios = new byte[FileSize]; + fp.Read(bios, 0, (int)FileSize); + fp.Dispose(); + + lpDiskBios = new byte[8 * 1024]; + if (bios[0] == 'N' && bios[1] == 'E' && bios[2] == 'S' && bios[3] == 0x1A) + { + Array.Copy(bios, 0x6010, lpDiskBios, 0, lpDiskBios.Length); + } + else + { + Array.Copy(bios, lpDiskBios, lpDiskBios.Length); + } + bios = null; + } + else if (romType == EnumRomType.NSF) + { + bNSF = true; + header = NESHEADER.GetDefault(); + + nsfheader = NSFHEADER.GetDefault(); + + PRGsize = FileSize - NSFHEADER.SizeOf(); + Debuger.Log($"PRGSIZE:{PRGsize}"); + PRGsize = (PRGsize + 0x0FFF) & ~0x0FFF; + Debuger.Log($"PRGSIZE:{PRGsize}"); + + lpPRG = new byte[PRGsize]; + Array.Copy(temp, NSFHEADER.SizeOf(), lpPRG, 0, FileSize - NSFHEADER.SizeOf()); + + NSF_PAGE_SIZE = (int)(PRGsize >> 12); + Debuger.Log($"PAGESIZE:{NSF_PAGE_SIZE}"); + } + else + { + throw new Exception($"Unsupport format:[{fname}]"); + } + + Supporter.GetFilePathInfo(fname, out fullpath, out path); + name = Path.GetFileNameWithoutExtension(fullpath); + if (!bNSF) + { + mapper = (header.control1 >> 4) | (header.control2 & 0xF0); + crc = crcall = crcvrom = 0; + + if (mapper != 20) + { + Span sTemp = temp; + if (IsTRAINER()) + { + crcall = CRC.CrcRev((int)(512 + PRGsize + CHRsize), sTemp.Slice(NESHEADER.SizeOf())); + crc = CRC.CrcRev((int)(512 + PRGsize), sTemp); + if (CHRsize > 0) + crcvrom = CRC.CrcRev((int)CHRsize, sTemp.Slice((int)(PRGsize + 512 + NESHEADER.SizeOf()))); + } + else + { + crcall = CRC.CrcRev((int)(PRGsize + CHRsize), sTemp.Slice(NESHEADER.SizeOf())); + crc = CRC.CrcRev((int)(PRGsize), sTemp.Slice(NESHEADER.SizeOf())); + if (CHRsize > 0) + crcvrom = CRC.CrcRev((int)CHRsize, sTemp.Slice((int)(PRGsize + NESHEADER.SizeOf()))); + } + + FileNameCheck(fname); + + if (Supporter.TryGetMapperNo(this, out int mapperNo)) + { + Debuger.Log($"ROMDB Set Mapper #{mapper:000} to #{mapperNo:000}"); + mapper = mapperNo; + } + + RomPatch.DoPatch(ref crc, ref lpPRG, ref lpCHR, ref mapper, ref header); + + fdsmakerID = fdsgameID = 0; + } + else //mapper==20 + { + crc = crcall = crcvrom = 0; + + fdsmakerID = lpPRG[0x1F]; + fdsgameID = (uint)((lpPRG[0x20] << 24) | (lpPRG[0x21] << 16) | (lpPRG[0x22] << 8) | (lpPRG[0x23] << 0)); + } + } + else //NSF + { + mapper = 0x0100; // Private mapper + crc = crcall = crcvrom = 0; + fdsmakerID = fdsgameID = 0; + } + + temp = null; + } + catch (Exception ex) + { + fp?.Dispose(); + temp = null; + bios = null; + lpPRG = null; + lpCHR = null; + lpTrainer = null; + lpDiskBios = null; + lpDisk = null; + + throw ex; + } + } + + public void Dispose() + { + lpPRG = null; + lpCHR = null; + lpTrainer = null; + lpDiskBios = null; + lpDisk = null; + } + + public bool IsTRAINER() + { + return (header.control1 & (byte)EnumRomControlByte1.ROM_TRAINER) > 0; + } + + public bool IsNSF() + { + return bNSF; + } + public bool IsPAL() + { + return bPAL; + } + + public bool IsSAVERAM() + { + return (header.control1 & (byte)EnumRomControlByte1.ROM_SAVERAM) > 0; + } + + protected void FileNameCheck(string fname) + { + if (fname.Contains("(E)")) + { + bPAL = true; + return; + } + } + + internal string GetRomName() + { + return name; + } + + internal int GetMapperNo() + { + return mapper; + } + + internal byte[] GetPROM() + { + return lpPRG; + } + + internal byte[] GetVROM() + { + return lpCHR; + } + + internal byte[] GetDISK() + { + return lpDisk; + } + + internal int GetDiskNo() + { + return diskno; + } + + internal void SetDiskNo(int v) + { + diskno = v; + } + + internal uint GetGameID() + { + return fdsgameID; + } + + internal void SetGameID(uint id) + { + fdsgameID = id; + } + + internal uint GetMakerID() + { + return fdsmakerID; + } + + internal void SetMakerID(uint id) + { + fdsmakerID = id; + } + + internal bool IsVSUNISYSTEM() + { + return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0; + } + + public uint GetPROM_CRC() + { + return crc; + } + + public void SetPROM_CRC(uint v) + { + crc = v; + } + + internal byte GetPROM_SIZE() + { + return header.PRG_PAGE_SIZE; + } + + internal byte GetVROM_SIZE() + { + return header.CHR_PAGE_SIZE; + } + + internal bool Is4SCREEN() + { + return (header.control1 & (byte)EnumRomControlByte1.ROM_4SCREEN) != 0; + } + + internal bool IsVMIRROR() + { + return (header.control1 & (byte)EnumRomControlByte1.ROM_VMIRROR) != 0; + } + + internal byte[] GetTRAINER() + { + return lpTrainer; + } + + internal NSFHEADER GetNsfHeader() + { + return nsfheader; + } + + internal string GetRomPath() + { + return path; + } + + internal uint GetVROM_CRC() + { + return crcvrom; + } + } + + +} diff --git a/Core/VirtualNes.Core/State/BLOCKHDR.cs b/Core/VirtualNes.Core/State/BLOCKHDR.cs new file mode 100644 index 0000000..6710398 --- /dev/null +++ b/Core/VirtualNes.Core/State/BLOCKHDR.cs @@ -0,0 +1,38 @@ +namespace VirtualNes.Core +{ + public struct BLOCKHDR : IStateBufferObject + { + public readonly bool Valid => !string.IsNullOrEmpty(ID); + /// 总是8个字节 + public string ID; + public ushort Reserved; + public ushort BlockVersion; + public uint BlockSize; + + + + public readonly uint GetSize() + { + return (uint)(8 + sizeof(ushort) + sizeof(ushort) + sizeof(uint)); + } + + public readonly void SaveState(StateBuffer buffer) + { + if (Valid) + { + buffer.Write(ID); + buffer.Write(Reserved); + buffer.Write(BlockVersion); + buffer.Write(BlockSize); + } + } + + public void LoadState(StateReader buffer) + { + ID = buffer.Read_string(8); + Reserved = buffer.Read_ushort(); + BlockVersion = buffer.Read_ushort(); + BlockSize = buffer.Read_uint(); + } + } +} diff --git a/Core/VirtualNes.Core/State/CTRSTAT.cs b/Core/VirtualNes.Core/State/CTRSTAT.cs new file mode 100644 index 0000000..9a127a7 --- /dev/null +++ b/Core/VirtualNes.Core/State/CTRSTAT.cs @@ -0,0 +1,34 @@ +namespace VirtualNes.Core +{ + public struct CTRSTAT : IStateBufferObject + { + public uint pad1bit; + public uint pad2bit; + public uint pad3bit; + public uint pad4bit; + public byte strobe; + + public readonly uint GetSize() + { + return sizeof(uint) * 4 + sizeof(byte); + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(pad1bit); + buffer.Write(pad2bit); + buffer.Write(pad3bit); + buffer.Write(pad4bit); + buffer.Write(strobe); + } + + public void LoadState(StateReader buffer) + { + pad1bit = buffer.Read_uint(); + pad2bit = buffer.Read_uint(); + pad3bit = buffer.Read_uint(); + pad4bit = buffer.Read_uint(); + strobe = buffer.Read_byte(); + } + } +} diff --git a/Core/VirtualNes.Core/State/DISKDATA.cs b/Core/VirtualNes.Core/State/DISKDATA.cs new file mode 100644 index 0000000..bd3518e --- /dev/null +++ b/Core/VirtualNes.Core/State/DISKDATA.cs @@ -0,0 +1,22 @@ +namespace VirtualNes.Core +{ + public struct DISKDATA : IStateBufferObject + { + public int DifferentSize; + + public uint GetSize() + { + return sizeof(int); + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(DifferentSize); + } + + public void LoadState(StateReader buffer) + { + DifferentSize = buffer.Read_int(); + } + } +} diff --git a/Core/VirtualNes.Core/State/EXCTRSTAT.cs b/Core/VirtualNes.Core/State/EXCTRSTAT.cs new file mode 100644 index 0000000..cb2ba07 --- /dev/null +++ b/Core/VirtualNes.Core/State/EXCTRSTAT.cs @@ -0,0 +1,22 @@ +namespace VirtualNes.Core +{ + public struct EXCTRSTAT : IStateBufferObject + { + public uint data; + + public readonly uint GetSize() + { + return sizeof(uint); + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(data); + } + + public void LoadState(StateReader buffer) + { + data = buffer.Read_uint(); + } + } +} diff --git a/Core/VirtualNes.Core/State/FILEHDR2.cs b/Core/VirtualNes.Core/State/FILEHDR2.cs new file mode 100644 index 0000000..888fe52 --- /dev/null +++ b/Core/VirtualNes.Core/State/FILEHDR2.cs @@ -0,0 +1,40 @@ +namespace VirtualNes.Core +{ + public struct FILEHDR2 : IStateBufferObject + { + public string ID; + /// 2字节 + public ushort BlockVersion; + /// 4字节 + public uint Ext0; + /// 2字节 + public ushort Ext1; + /// 2字节 + public ushort Ext2; + + + + public readonly uint GetSize() + { + return (uint)(ID.Length + sizeof(ushort) + sizeof(uint) + sizeof(ushort) + sizeof(ushort)); + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(ID); + buffer.Write(BlockVersion); + buffer.Write(Ext0); + buffer.Write(Ext1); + buffer.Write(Ext2); + } + + public void LoadState(StateReader buffer) + { + ID = buffer.Read_string(12); + BlockVersion = buffer.Read_ushort(); + Ext0 = buffer.Read_uint(); + Ext1 = buffer.Read_ushort(); + Ext2 = buffer.Read_ushort(); + } + } +} diff --git a/Core/VirtualNes.Core/State/MMCSTAT.cs b/Core/VirtualNes.Core/State/MMCSTAT.cs new file mode 100644 index 0000000..bae7717 --- /dev/null +++ b/Core/VirtualNes.Core/State/MMCSTAT.cs @@ -0,0 +1,27 @@ +namespace VirtualNes.Core +{ + public struct MMCSTAT : IStateBufferObject + { + public byte[] mmcdata; + + public static MMCSTAT GetDefault() + { + return new MMCSTAT() { mmcdata = new byte[256] }; + } + + public readonly uint GetSize() + { + return 256; + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(mmcdata); + } + + public void LoadState(StateReader buffer) + { + mmcdata = buffer.Read_bytes(256); + } + } +} diff --git a/Core/VirtualNes.Core/State/MMUSTAT.cs b/Core/VirtualNes.Core/State/MMUSTAT.cs new file mode 100644 index 0000000..c2d9812 --- /dev/null +++ b/Core/VirtualNes.Core/State/MMUSTAT.cs @@ -0,0 +1,47 @@ +namespace VirtualNes.Core +{ + public struct MMUSTAT : IStateBufferObject + { + public byte[] CPU_MEM_TYPE; + public ushort[] CPU_MEM_PAGE; + public byte[] PPU_MEM_TYPE; + public ushort[] PPU_MEM_PAGE; + public byte[] CRAM_USED; + + public static MMUSTAT GetDefault() + { + var res = new MMUSTAT(); + + res.CPU_MEM_TYPE = new byte[8]; + res.CPU_MEM_PAGE = new ushort[8]; + res.PPU_MEM_TYPE = new byte[12]; + res.PPU_MEM_PAGE = new ushort[12]; + res.CRAM_USED = new byte[8]; + + return res; + } + + public uint GetSize() + { + return (uint)(CPU_MEM_TYPE.Length + CPU_MEM_PAGE.Length + PPU_MEM_TYPE.Length + PPU_MEM_PAGE.Length + CRAM_USED.Length); + } + + public void SaveState(StateBuffer buffer) + { + buffer.Write(CPU_MEM_TYPE); + buffer.Write(CPU_MEM_PAGE); + buffer.Write(PPU_MEM_TYPE); + buffer.Write(PPU_MEM_PAGE); + buffer.Write(CRAM_USED); + } + + public void LoadState(StateReader buffer) + { + CPU_MEM_TYPE = buffer.Read_bytes(8); + CPU_MEM_PAGE = buffer.Read_ushorts(8); + PPU_MEM_TYPE = buffer.Read_bytes(12); + PPU_MEM_PAGE = buffer.Read_ushorts(12); + CRAM_USED = buffer.Read_bytes(8); + } + } +} diff --git a/Core/VirtualNes.Core/State/RAMSTAT.cs b/Core/VirtualNes.Core/State/RAMSTAT.cs new file mode 100644 index 0000000..eeb3ac9 --- /dev/null +++ b/Core/VirtualNes.Core/State/RAMSTAT.cs @@ -0,0 +1,46 @@ + +namespace VirtualNes.Core +{ + public struct RAMSTAT : IStateBufferObject + { + /// Internal NES RAM + public byte[] RAM; + /// BG Palette + public byte[] BGPAL; + /// SP Palette + public byte[] SPPAL; + /// Sprite RAM + public byte[] SPRAM; + + public static RAMSTAT GetDefault() + { + var res = new RAMSTAT(); + res.RAM = new byte[2 * 1024]; + res.BGPAL = new byte[16]; + res.SPPAL = new byte[16]; + res.SPRAM = new byte[256]; + return res; + } + + public readonly uint GetSize() + { + return (uint)(RAM.Length + BGPAL.Length + SPPAL.Length + SPRAM.Length); + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(RAM); + buffer.Write(BGPAL); + buffer.Write(SPPAL); + buffer.Write(SPRAM); + } + + public void LoadState(StateReader buffer) + { + RAM = buffer.Read_bytes(2 * 1024); + BGPAL = buffer.Read_bytes(16); + SPPAL = buffer.Read_bytes(16); + SPRAM = buffer.Read_bytes(256); + } + } +} diff --git a/Core/VirtualNes.Core/State/REGSTAT.cs b/Core/VirtualNes.Core/State/REGSTAT.cs new file mode 100644 index 0000000..080e51f --- /dev/null +++ b/Core/VirtualNes.Core/State/REGSTAT.cs @@ -0,0 +1,136 @@ +namespace VirtualNes.Core +{ + public struct REGSTAT : IStateBufferObject + { + public CPUSTAT cpureg; + public PPUSTAT ppureg; + + + + public readonly uint GetSize() + { + return cpureg.GetSize() + ppureg.GetSize(); + } + + public readonly void SaveState(StateBuffer buffer) + { + cpureg.SaveState(buffer); + ppureg.SaveState(buffer); + } + + public void LoadState(StateReader buffer) + { + cpureg.LoadState(buffer); + ppureg.LoadState(buffer); + } + } + + public struct CPUSTAT : IStateBufferObject + { + public ushort PC; + public byte A; + public byte X; + public byte Y; + public byte S; + public byte P; + public byte I; // Interrupt pending flag + + public byte FrameIRQ; + public byte FrameIRQ_occur; + public byte FrameIRQ_count; + public byte FrameIRQ_type; + public int FrameIRQ_cycles; + public int DMA_cycles; + + public long emul_cycles; + public long base_cycles; + + public readonly uint GetSize() + { + return 32; + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(PC); + buffer.Write(A); + buffer.Write(X); + buffer.Write(Y); + buffer.Write(S); + buffer.Write(P); + buffer.Write(I); + buffer.Write(FrameIRQ); + buffer.Write(FrameIRQ_occur); + buffer.Write(FrameIRQ_count); + buffer.Write(FrameIRQ_type); + buffer.Write(FrameIRQ_cycles); + buffer.Write(DMA_cycles); + buffer.Write(emul_cycles); + buffer.Write(base_cycles); + } + + public void LoadState(StateReader buffer) + { + PC = buffer.Read_ushort(); + A = buffer.Read_byte(); + X = buffer.Read_byte(); + Y = buffer.Read_byte(); + S = buffer.Read_byte(); + P = buffer.Read_byte(); + I = buffer.Read_byte(); + FrameIRQ = buffer.Read_byte(); + FrameIRQ_occur = buffer.Read_byte(); + FrameIRQ_count = buffer.Read_byte(); + FrameIRQ_type = buffer.Read_byte(); + FrameIRQ_cycles = buffer.Read_int(); + DMA_cycles = buffer.Read_int(); + emul_cycles = buffer.Read_long(); + base_cycles = buffer.Read_long(); + } + } + + public struct PPUSTAT : IStateBufferObject + { + public byte reg0; + public byte reg1; + public byte reg2; + public byte reg3; + public byte reg7; + public byte toggle56; + + public ushort loopy_t; + public ushort loopy_v; + public ushort loopy_x; + + public readonly uint GetSize() + { + return 12; + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(reg0); + buffer.Write(reg1); + buffer.Write(reg2); + buffer.Write(reg3); + buffer.Write(reg7); + buffer.Write(toggle56); + buffer.Write(loopy_t); + buffer.Write(loopy_v); + buffer.Write(loopy_x); + } + + public void LoadState(StateReader buffer) + { + reg0 = buffer.Read_byte(); + reg1 = buffer.Read_byte(); + reg2 = buffer.Read_byte(); + reg3 = buffer.Read_byte(); + reg7 = buffer.Read_byte(); + toggle56 = buffer.Read_byte(); + loopy_t = buffer.Read_ushort(); + loopy_v = buffer.Read_ushort(); + loopy_x = buffer.Read_ushort(); + } + } +} diff --git a/Core/VirtualNes.Core/State/SNDSTAT.cs b/Core/VirtualNes.Core/State/SNDSTAT.cs new file mode 100644 index 0000000..4993a81 --- /dev/null +++ b/Core/VirtualNes.Core/State/SNDSTAT.cs @@ -0,0 +1,27 @@ +namespace VirtualNes.Core +{ + public struct SNDSTAT : IStateBufferObject + { + public byte[] snddata; + + public static SNDSTAT GetDefault() + { + return new SNDSTAT() { snddata = new byte[0x800] }; + } + + public readonly uint GetSize() + { + return (uint)snddata.Length; + } + + public readonly void SaveState(StateBuffer buffer) + { + buffer.Write(snddata); + } + + public void LoadState(StateReader buffer) + { + snddata = buffer.Read_bytes(0x800); + } + } +} diff --git a/Core/VirtualNes.Core/State/State.cs b/Core/VirtualNes.Core/State/State.cs new file mode 100644 index 0000000..f9035e3 --- /dev/null +++ b/Core/VirtualNes.Core/State/State.cs @@ -0,0 +1,185 @@ +using System.Collections.Generic; + +namespace VirtualNes.Core +{ + public struct State + { + public FILEHDR2 HEADER; + + public BLOCKHDR regBLOCK; + public REGSTAT reg; + + public BLOCKHDR ramBLOCK; + public RAMSTAT ram; + /// Maybe null cause by rom IsSaveRAM() + public byte[] WRAM; + + public BLOCKHDR mmuBLOCK; + public MMUSTAT mmu; + public List CPU_MEM_BANK; + public byte[] VRAM; + public List CRAM; + + public BLOCKHDR mmcBLOCK; + public MMCSTAT mmc; + + public BLOCKHDR ctrBLOCK; + public CTRSTAT ctr; + + public BLOCKHDR sndBLOCK; + public SNDSTAT snd; + + public BLOCKHDR dskBLOCK; + public DISKDATA dsk; + public List dskdata; + + public BLOCKHDR exctrBLOCK; + public EXCTRSTAT exctr; + + public readonly byte[] ToBytes() + { + StateBuffer buffer = new StateBuffer(); + + HEADER.SaveState(buffer); + + buffer.Write(WRAM != null ? WRAM.Length : 0); + buffer.Write(CPU_MEM_BANK != null ? CPU_MEM_BANK.Count : 0); + buffer.Write(VRAM != null ? VRAM.Length : 0); + buffer.Write(CRAM != null ? CRAM.Count : 0); + buffer.Write(dskdata != null ? dskdata.Count : 0); + + if (regBLOCK.Valid) + { + regBLOCK.SaveState(buffer); + reg.SaveState(buffer); + } + + if (ramBLOCK.Valid) + { + ramBLOCK.SaveState(buffer); + ram.SaveState(buffer); + if (WRAM != null) buffer.Write(WRAM); + } + + if (mmuBLOCK.Valid) + { + mmuBLOCK.SaveState(buffer); + mmu.SaveState(buffer); + buffer.Write(CPU_MEM_BANK.ToArray()); + buffer.Write(VRAM); + buffer.Write(CRAM.ToArray()); + } + + if (mmcBLOCK.Valid) + { + mmcBLOCK.SaveState(buffer); + mmc.SaveState(buffer); + } + + if (ctrBLOCK.Valid) + { + ctrBLOCK.SaveState(buffer); + ctr.SaveState(buffer); + } + + if (sndBLOCK.Valid) + { + sndBLOCK.SaveState(buffer); + snd.SaveState(buffer); + } + + if (dskBLOCK.Valid) + { + dskBLOCK.SaveState(buffer); + dsk.SaveState(buffer); + foreach (var data in dskdata) + { + buffer.Write(data); + } + } + + if (exctrBLOCK.Valid) + { + exctrBLOCK.SaveState(buffer); + exctr.SaveState(buffer); + } + + return buffer.Data.ToArray(); + } + public void FromByte(byte[] data) + { + StateReader buffer = new StateReader(data); + + HEADER.LoadState(buffer); + + var WRAM_Length = buffer.Read_int(); + var CPU_MEM_BANK_Length = buffer.Read_int(); + var VRAM_Length = buffer.Read_int(); + var CRAM_Length = buffer.Read_int(); + var dskdata_Length = buffer.Read_int(); + + while (buffer.Remain > 0) + { + BLOCKHDR block = new BLOCKHDR(); + block.LoadState(buffer); + + switch (block.ID) + { + case "REG DATA": + regBLOCK = block; + reg.LoadState(buffer); + break; + case "RAM DATA": + ramBLOCK = block; + ram.LoadState(buffer); + if (WRAM_Length > 0) + WRAM = buffer.Read_bytes(WRAM_Length); + else + WRAM = new byte[0]; + break; + case "MMU DATA": + mmuBLOCK = block; + mmu.LoadState(buffer); + if (CPU_MEM_BANK_Length > 0) + CPU_MEM_BANK = new List(buffer.Read_bytes(CPU_MEM_BANK_Length)); + else + CPU_MEM_BANK = new List(); + + if (VRAM_Length > 0) + VRAM = buffer.Read_bytes(VRAM_Length); + else + VRAM = new byte[0]; + if (CRAM_Length > 0) + CRAM = new List(buffer.Read_bytes(CRAM_Length)); + else + CRAM = new List(); + break; + case "MMC DATA": + mmcBLOCK = block; + mmc.LoadState(buffer); + break; + case "CTR DATA": + ctrBLOCK = block; + ctr.LoadState(buffer); + break; + case "SND DATA": + sndBLOCK = block; + snd.LoadState(buffer); + break; + case "DISKDATA": + dskBLOCK = block; + dsk.LoadState(buffer); + if (dskdata_Length > 0) + dskdata = new List(buffer.Read_uints(dskdata_Length)); + else + dskdata = new List(); + break; + case "EXCTRDAT": + exctrBLOCK = block; + exctr.LoadState(buffer); + break; + } + } + } + } +} diff --git a/Core/VirtualNes.Core/State/StateBuffer.cs b/Core/VirtualNes.Core/State/StateBuffer.cs new file mode 100644 index 0000000..4ebb968 --- /dev/null +++ b/Core/VirtualNes.Core/State/StateBuffer.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace VirtualNes.Core +{ + public class StateBuffer + { + public List Data = new List(); + + public long Position + { + get => Data.Count - 1; + set + { + var gap = value - Position; + if (gap > 0) + { + Data.AddRange(new byte[gap]); + } + else + { + Data.RemoveRange((int)Position, (int)gap); + } + } + } + + public void Write(byte[] bytes) + { + Data.AddRange(bytes); + } + public void Write(sbyte[] sbytes) + { + foreach (var value in sbytes) + { + Write(value); + } + } + public void Write(byte value) + { + Data.Add(value); + } + public void Write(ushort[] values) + { + foreach (var value in values) + Write(value); + } + public void Write(int[] values) + { + foreach (var value in values) + Write(value); + } + public void Write(string value) + { + if (value == null) return; + + Write(Encoding.ASCII.GetBytes(value)); + } + public void Write(double value) + { + Write(BitConverter.GetBytes(value)); + } + public void Write(ushort value) + { + Write(BitConverter.GetBytes(value)); + } + public void Write(int value) + { + Write(BitConverter.GetBytes(value)); + } + public void Write(sbyte value) + { + Write(value); + } + public void Write(long value) + { + Write(BitConverter.GetBytes(value)); + } + public void Write(uint value) + { + Write(BitConverter.GetBytes(value)); + } + } + public class StateReader + { + private MemoryStream m_dataStream; + + public long Remain => m_dataStream.Length - 1 - m_dataStream.Position; + + public StateReader(byte[] bytes) + { + m_dataStream = new MemoryStream(bytes); + } + + public void Skip(uint count) + { + m_dataStream.Seek(count, SeekOrigin.Current); + } + public void Skip(long count) + { + m_dataStream.Seek(count, SeekOrigin.Current); + } + + public byte[] Read_bytes(int length) + { + var result = new byte[length]; + m_dataStream.Read(result, 0, length); + return result; + } + public sbyte[] Read_sbytes(int length) + { + var result = new sbyte[length]; + for (int i = 0; i < length; i++) + { + result[i] = (sbyte)m_dataStream.ReadByte(); + } + + return result; + } + + public byte Read_byte() + { + return (byte)m_dataStream.ReadByte(); + } + public ushort[] Read_ushorts(int length) + { + ushort[] result = new ushort[length]; + for (int i = 0; i < length; i++) + { + TEMP[0] = (byte)m_dataStream.ReadByte(); + TEMP[1] = (byte)m_dataStream.ReadByte(); + + result[i] = BitConverter.ToUInt16(TEMP, 0); + } + + return result; + } + public int[] Read_ints(int length) + { + int[] result = new int[length]; + for (int i = 0; i < length; i++) + { + TEMP[0] = (byte)m_dataStream.ReadByte(); + TEMP[1] = (byte)m_dataStream.ReadByte(); + TEMP[2] = (byte)m_dataStream.ReadByte(); + TEMP[3] = (byte)m_dataStream.ReadByte(); + + result[i] = BitConverter.ToInt32(TEMP, 0); + } + + return result; + } + + + public string Read_string(int length) + { + var result = Read_bytes(length); + return Encoding.ASCII.GetString(result); + } + public double Read_double() + { + var result = Read_bytes(4); + return BitConverter.ToDouble(result, 0); + } + + byte[] TEMP = new byte[8]; + + public ushort Read_ushort() + { + TEMP[0] = Read_byte(); + TEMP[1] = Read_byte(); + return BitConverter.ToUInt16(TEMP, 0); + } + + public int Read_int() + { + TEMP[0] = Read_byte(); + TEMP[1] = Read_byte(); + TEMP[2] = Read_byte(); + TEMP[3] = Read_byte(); + return BitConverter.ToInt32(TEMP, 0); + } + + public sbyte Read_sbyte(sbyte value) + { + return (sbyte)m_dataStream.ReadByte(); + } + public long Read_long() + { + TEMP[0] = Read_byte(); + TEMP[1] = Read_byte(); + TEMP[2] = Read_byte(); + TEMP[3] = Read_byte(); + TEMP[4] = Read_byte(); + TEMP[5] = Read_byte(); + TEMP[6] = Read_byte(); + TEMP[7] = Read_byte(); + + return BitConverter.ToInt64(TEMP, 0); + } + + public uint Read_uint() + { + return (uint)Read_int(); + } + + public uint[] Read_uints(int length) + { + uint[] ret = new uint[length]; + for (int i = 0; i < length; i++) + { + ret[i] = Read_uint(); + } + return ret; + } + } + + public interface IStateBufferObject + { + uint GetSize(); + void SaveState(StateBuffer buffer); + void LoadState(StateReader buffer); + } +} diff --git a/Core/VirtualNes.Core/Supporter/ControllerState.cs b/Core/VirtualNes.Core/Supporter/ControllerState.cs new file mode 100644 index 0000000..409ba18 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/ControllerState.cs @@ -0,0 +1,74 @@ +using System; + +namespace VirtualNes.Core +{ + public struct ControllerState + { + public uint raw0; + public uint raw1; + public uint raw2; + public uint raw3; + + public bool valid; + + public ControllerState( + EnumButtonType player0_buttons, + EnumButtonType player1_buttons, + EnumButtonType player2_buttons, + EnumButtonType player3_buttons) + { + raw0 = (uint)player0_buttons; + raw1 = (uint)player1_buttons; + raw2 = (uint)player2_buttons; + raw3 = (uint)player3_buttons; + valid = true; + } + + public static bool operator ==(ControllerState left, ControllerState right) + { + return + left.raw0 == right.raw0 && + left.raw1 == right.raw1 && + left.raw2 == right.raw2 && + left.raw3 == right.raw3; + } + + public static bool operator !=(ControllerState left, ControllerState right) + { + return !(left == right); + } + + public override string ToString() + { + return $"{raw0}|{raw1}|{raw2}|{raw3}"; + } + + public bool HasButton(int player, EnumButtonType button) + { + uint raw = 0; + switch (player) + { + case 0: raw = raw0; break; + case 1: raw = raw1; break; + case 2: raw = raw2; break; + case 3: raw = raw3; break; + } + return (raw & (uint)button) == (uint)button; + } + } + + [Flags] + public enum EnumButtonType + { + NONE = 0, + UP = 1, + DOWN = 2, + LEFT = 4, + RIGHT = 8, + A = 16, + B = 32, + SELECT = 64, + START = 128, + MIC = 256 + } +} diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs new file mode 100644 index 0000000..f47ac7b --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs @@ -0,0 +1,27 @@ + + +namespace VirtualNes.Core +{ + public class CfgController + { + public ushort[][] nButton = new ushort[4][] + { + new ushort[64],new ushort[64], new ushort[64], new ushort[64], + }; + public ushort[][] nRapid = new ushort[4][] + { + new ushort[2],new ushort[2],new ushort[2],new ushort[2], + }; + + // 0:Crazy Climber + // 1:Famly Trainer + // 2:Exciting Boxing + // 3:Mahjang + public ushort[][] nExButton = new ushort[4][] + { + new ushort[64],new ushort[64], new ushort[64], new ushort[64], + }; + + public ushort[] nVSUnisystem = new ushort[64]; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs new file mode 100644 index 0000000..d4eca6c --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs @@ -0,0 +1,18 @@ +namespace VirtualNes.Core +{ + public class CfgEmulator + { + public bool bIllegalOp { get; set; } = false; + public bool bAutoFrameSkip { get; set; } = true; + public bool bThrottle { get; set; } = true; + public int nThrottleFPS { get; set; } = 120; + public bool bBackground { get; set; } = false; + public int nPriority { get; set; } = 3; + public bool bFourPlayer { get; set; } = true; + public bool bCrcCheck { get; set; } = true; + public bool bDiskThrottle { get; set; } = true; + public bool bLoadFullscreen { get; set; } = false; + public bool bPNGsnapshot { get; set; } = false; + public bool bAutoIPS { get; set; } = false; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs new file mode 100644 index 0000000..10aeec9 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgExtraSound + { + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs new file mode 100644 index 0000000..496887c --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs @@ -0,0 +1,7 @@ +namespace VirtualNes.Core +{ + public class CfgGeneral + { + + } +} diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs new file mode 100644 index 0000000..5b9817d --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs @@ -0,0 +1,39 @@ +namespace VirtualNes.Core +{ + public class CfgGraphics + { + public bool bAspect = false; + public bool bAllSprite = true; + public bool bAllLine = false; + public bool bFPSDisp = false; + public bool bTVFrame = false; + public bool bScanline = false; + public int nScanlineColor = 75; + public bool bSyncDraw = false; + public bool bFitZoom = false; + + public bool bLeftClip = true; + + public bool bWindowVSync = false; + + public bool bSyncNoSleep = false; + + public bool bDiskAccessLamp = false; + + public bool bDoubleSize = false; + public bool bSystemMemory = false; + public bool bUseHEL = false; + + public bool bNoSquareList = false; + + public int nGraphicsFilter = 0; + + public uint dwDisplayWidth = 640; + public uint dwDisplayHeight = 480; + public uint dwDisplayDepth = 16; + public uint dwDisplayRate = 0; + + public bool bPaletteFile = false; + public char[] szPaletteFile = new char[260]; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs new file mode 100644 index 0000000..64b3018 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgLanguage + { + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs new file mode 100644 index 0000000..e20de84 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgLauncher + { + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs new file mode 100644 index 0000000..92159ef --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs @@ -0,0 +1,12 @@ +namespace VirtualNes.Core +{ + public class CfgMovie + { + public byte[] bUsePlayer = new byte[4] { 0xFF, 0x00, 0x00, 0x00 }; + public bool bRerecord = true; + public bool bLoopPlay = false; + public bool bResetRec = false; + public bool bPadDisplay = false; + public bool bTimeDisplay = false; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs new file mode 100644 index 0000000..55f70e2 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgNetPlay + { + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs new file mode 100644 index 0000000..084cbc1 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs @@ -0,0 +1,23 @@ +namespace VirtualNes.Core +{ + public class CfgPath + { + public bool bRomPath = true; + public bool bSavePath = true; + public bool bStatePath = true; + public bool bSnapshotPath = true; + public bool bMoviePath = true; + public bool bWavePath = true; + public bool bCheatPath = true; + public bool bIpsPath = true; + + public string szRomPath = "roms"; + public string szSavePath = "save"; + public string szStatePath = "state"; + public string szSnapshotPath = "snapshot"; + public string szMoviePath = "movie"; + public string szWavePath = "wave"; + public string szCheatPath = "cheatcode"; + public string szIpsPath = "ips"; + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs new file mode 100644 index 0000000..1ce7e75 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgShortCut + { + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs new file mode 100644 index 0000000..ddeb3cb --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs @@ -0,0 +1,19 @@ +namespace VirtualNes.Core +{ + public class CfgSound + { + public bool bEnable { get; set; } = true; + public int nRate { get; set; } = 48000; + public int nBits { get; set; } = 8; + public int nBufferSize { get; set; } = 4; + public int nFilterType { get; set; } = 0; + public bool bChangeTone { get; set; } = false; + public bool bDisableVolumeEffect { get; set; } = false; + public bool bExtraSoundEnable { get; set; } = true; + public short[] nVolume { get; set; } = new short[16] + { + 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, + }; + + } +} \ No newline at end of file diff --git a/Core/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs b/Core/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs new file mode 100644 index 0000000..5100085 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs @@ -0,0 +1,51 @@ +namespace VirtualNes.Core +{ + public class EmulatorConfig + { + private bool m_bKeyboardDisable; + + public CfgGeneral general { get; private set; } = new CfgGeneral(); + public CfgPath path { get; private set; } = new CfgPath(); + public CfgEmulator emulator { get; private set; } = new CfgEmulator(); + public CfgGraphics graphics { get; private set; } = new CfgGraphics(); + public CfgSound sound { get; private set; } = new CfgSound(); + public CfgShortCut shortcut { get; private set; } = new CfgShortCut(); + public CfgLanguage language { get; private set; } = new CfgLanguage(); + public CfgController controller { get; private set; } = new CfgController(); + public CfgMovie movie { get; private set; } = new CfgMovie(); + public CfgLauncher launcher { get; private set; } = new CfgLauncher(); + public CfgExtraSound extsound { get; private set; } = new CfgExtraSound(); + public CfgNetPlay netplay { get; private set; } = new CfgNetPlay(); + } + + public static class GameOption + { + // Default保存 + public static int defRenderMethod; + public static int defIRQtype; + public static bool defFrameIRQ; + public static bool defVideoMode; + + // データ + public static int nRenderMethod; + public static int nIRQtype; + public static bool bFrameIRQ; + public static bool bVideoMode; + + public static void Load(uint crc) + { + nRenderMethod = defRenderMethod; + nIRQtype = defIRQtype; + bFrameIRQ = defFrameIRQ; + bVideoMode = defVideoMode; + } + + public static void Load(uint gid, uint mid) + { + nRenderMethod = defRenderMethod; + nIRQtype = defIRQtype; + bFrameIRQ = defFrameIRQ; + bVideoMode = defVideoMode; + } + } +} diff --git a/Core/VirtualNes.Core/Supporter/Supporter.cs b/Core/VirtualNes.Core/Supporter/Supporter.cs new file mode 100644 index 0000000..41ec222 --- /dev/null +++ b/Core/VirtualNes.Core/Supporter/Supporter.cs @@ -0,0 +1,86 @@ +using System.IO; + +namespace VirtualNes.Core +{ + public static class Supporter + { + private static ISupporterImpl s_support; + public static void Setup(ISupporterImpl supporter) + { + s_support = supporter; + } + + public static Stream OpenRom(string fname) + { + return s_support.OpenRom(fname); + } + + public static void GetFilePathInfo(string fname, out string fullPath, out string directPath) + { + s_support.GetRomPathInfo(fname, out fullPath, out directPath); + } + + public static Stream OpenFile_DISKSYS() + { + return s_support.OpenFile_DISKSYS(); + } + + public static void SaveSRAMToFile(byte[] sramContent, string romName) + { + s_support.SaveSRAMToFile(sramContent, romName); + } + + public static void SaveDISKToFile(byte[] diskFileContent, string romName) + { + s_support.SaveDISKToFile(diskFileContent, romName); + } + + public static void PrepareDirectory(string directPath) + { + s_support.PrepareDirectory(directPath); + } + + public static void SaveFile(byte[] fileData, string directPath, string fileName) + { + s_support.SaveFile(fileData, directPath, fileName); + } + public static Stream OpenFile(string directPath, string fileName) + { + return s_support.OpenFile(directPath, fileName); + } + + public static bool TryGetMapperNo(ROM rom, out int mapperNo) + { + return s_support.TryGetMapperNo(rom, out mapperNo); + } + + public static ControllerState GetControllerState() + { + return s_support.GetControllerState(); + } + + public static void SampleInput(uint frameCount) + { + s_support.SampleInput(frameCount); + } + + public static EmulatorConfig Config => s_support.Config; + } + + public interface ISupporterImpl + { + Stream OpenRom(string fname); + void GetRomPathInfo(string fname, out string fullPath, out string directPath); + Stream OpenFile_DISKSYS(); + void SaveSRAMToFile(byte[] sramContent, string romName); + void SaveDISKToFile(byte[] diskFileContent, string romName); + EmulatorConfig Config { get; } + + void PrepareDirectory(string directPath); + void SaveFile(byte[] fileData, string directPath, string fileName); + Stream OpenFile(string directPath, string fileName); + bool TryGetMapperNo(ROM rom, out int mapperNo); + ControllerState GetControllerState(); + void SampleInput(uint frameCount); + } +} diff --git a/Core/VirtualNes.Core/VirtualNes.Core.csproj b/Core/VirtualNes.Core/VirtualNes.Core.csproj new file mode 100644 index 0000000..89b8d6a --- /dev/null +++ b/Core/VirtualNes.Core/VirtualNes.Core.csproj @@ -0,0 +1,13 @@ + + + + netstandard2.0 + True + 8.0 + + + + + + + diff --git a/Core/VirtualNes.Core/VirtualNes.Core.csproj.user b/Core/VirtualNes.Core/VirtualNes.Core.csproj.user new file mode 100644 index 0000000..827d439 --- /dev/null +++ b/Core/VirtualNes.Core/VirtualNes.Core.csproj.user @@ -0,0 +1,6 @@ + + + + <_LastSelectedProfileId>G:\Sin365\AxibugEmuOnline\VirtualNes.Core\Properties\PublishProfiles\FolderProfile.pubxml + + \ No newline at end of file diff --git a/Core/VirtualNes.Core/VsUnisystem.cs b/Core/VirtualNes.Core/VsUnisystem.cs new file mode 100644 index 0000000..c7ff3fc --- /dev/null +++ b/Core/VirtualNes.Core/VsUnisystem.cs @@ -0,0 +1,49 @@ +namespace VirtualNes.Core +{ + public class VSDIPSWITCH + { + public string name; + public ushort value; + } + + + public static class VsUnisystem + { + public static VSDIPSWITCH[] vsdip_default = new VSDIPSWITCH[] + { + new VSDIPSWITCH{name="Unknown", value= 0x0100}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x01}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x0200}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x02}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x0400}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x04}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x0800}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x08}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x1000}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x10}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x2000}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x20}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x4000}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x40}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name="Unknown", value= 0x8000}, + new VSDIPSWITCH{name="Off", value= 0x00}, + new VSDIPSWITCH{name="On", value= 0x80}, + new VSDIPSWITCH{name=null, value= 0xFF}, + new VSDIPSWITCH{name=null, value= 0 }, + }; + } +}