From 51aca30cf72ce0f9db514a358c95cc7e67d1fa9b Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Thu, 25 Jul 2024 11:03:58 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/NesEmulator/CoreSupporter.cs | 17 + .../Assets/Script/NesEmulator/NesEmulator.cs | 18 +- .../Assets/VirtualNes.Core/APU.cs | 13 +- .../VirtualNes.Core/ApuEX/APU_INTERNAL.cs | 27 ++ .../Assets/VirtualNes.Core/CPU.cs | 229 ++++++++++++- .../CoreLibs/EnumRenderMethod.cs | 18 ++ .../CoreLibs/EnumRenderMethod.cs.meta | 11 + .../VirtualNes.Core/CoreLibs/NESCOMMAND.cs | 23 ++ .../CoreLibs/NESCOMMAND.cs.meta | 11 + .../VirtualNes.Core/CoreLibs/NesConfig.cs | 58 ++++ .../CoreLibs/NesConfig.cs.meta | 11 + .../Assets/VirtualNes.Core/CoreLibs/State.cs | 59 ++++ .../VirtualNes.Core/CoreLibs/State.cs.meta | 11 + .../Assets/VirtualNes.Core/MMU.cs | 18 ++ .../Assets/VirtualNes.Core/MMU.cs.meta | 11 + .../Assets/VirtualNes.Core/Mapper/Mapper.cs | 3 +- .../Assets/VirtualNes.Core/NES.cs | 301 +++++++++++++++--- .../Assets/VirtualNes.Core/PAD.cs | 8 +- .../Assets/VirtualNes.Core/PPU.cs | 27 +- .../Assets/VirtualNes.Core/ROM.cs | 55 ++++ .../VirtualNes.Core/Supporter/Supporter.cs | 11 + 21 files changed, 871 insertions(+), 69 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index 495360b..57019b9 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -2,6 +2,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.IO; +using System.Net.Http; using UnityEngine; using VirtualNes.Core; @@ -45,5 +46,21 @@ namespace AxibugEmuOnline.Client { return File.Open($"{Application.streamingAssetsPath}/Disksys.rom", FileMode.Open, FileAccess.Read); } + + public void SaveSRAMToFile(byte[] sramContent, string romName) + { + string sramDirectoryPath = $"{Application.persistentDataPath}/sav"; + Directory.CreateDirectory(sramDirectoryPath); + romName = Path.GetFileNameWithoutExtension(romName); + File.WriteAllBytes($"{sramDirectoryPath}/{romName}.sav", sramContent); + } + + public void SaveDISKToFile(byte[] diskFileContent, string romName) + { + string diskFileDirectoryPath = $"{Application.persistentDataPath}/dsv"; + Directory.CreateDirectory(diskFileDirectoryPath); + romName = Path.GetFileNameWithoutExtension(romName); + File.WriteAllBytes($"{diskFileDirectoryPath}/{romName}.dsv", diskFileContent); + } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index 28d0241..a22cab9 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -6,6 +6,8 @@ namespace AxibugEmuOnline.Client { public class NesEmulator : MonoBehaviour { + private NES m_nesIns; + private void Start() { StartGame("Kirby.nes"); @@ -13,9 +15,23 @@ namespace AxibugEmuOnline.Client public void StartGame(string romName) { + StopGame(); + Supporter.Setup(new CoreSupporter()); Debuger.Setup(new CoreDebuger()); - NES nes = new NES(romName); + m_nesIns = new NES(romName); + m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET); + } + + public void StopGame() + { + m_nesIns?.Dispose(); + m_nesIns = null; + } + + private void Update() + { + m_nesIns?.EmulateFrame(true); } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs index 510f7b7..5d76dd9 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs @@ -8,7 +8,7 @@ namespace VirtualNes.Core { private NES nes; private byte exsound_select; - private APU_INTERNAL @internal; + private APU_INTERNAL @internal = new APU_INTERNAL(); private int last_data; private int last_diff; protected short[] m_SoundBuffer = new short[256]; @@ -19,8 +19,6 @@ namespace VirtualNes.Core public APU(NES parent) { - @internal = new APU_INTERNAL(); - exsound_select = 0; nes = parent; @@ -36,6 +34,15 @@ namespace VirtualNes.Core for (int i = 0; i < m_bMute.Length; i++) m_bMute[i] = true; } + + public void Dispose() + { + } + + internal void SyncDPCM(int cycles) + { + @internal.Sync(cycles); + } } public struct QUEUEDATA diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index afb9abc..ca1a0f3 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -3,12 +3,39 @@ namespace VirtualNes.Core public class APU_INTERNAL : APU_INTERFACE { private NES nes; + private int FrameCycle; + private byte FrameIRQoccur; 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) + { + //TODO : ʵ + return false; + } + + private void UpdateFrame() + { + //TODO : ʵ + } + public override void Reset(float fClock, int nRate) { throw new System.NotImplementedException(); diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs index e2780ad..ea35cfc 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs @@ -1,12 +1,221 @@ -namespace VirtualNes.Core +#undef DPCM_SYNCCLOCK + +using Codice.CM.Client.Differences; +using System; +using System.Runtime.CompilerServices; + +namespace VirtualNes.Core { public class CPU { - protected NES m_nes; + // 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; + private R6502 R; + private byte[] ZN_Table = new byte[256]; public CPU(NES parent) { - m_nes = parent; + nes = parent; + m_bClockProcess = false; + + } + + public void Dispose() { } + + internal long EXEC(int request_cycles) + { + byte opcode = 0; + int OLD_cycles = TOTAL_cycles; + int exec_cycles = 0; + byte nmi_request = 0, irq_request = 0; + + ushort EA = 0; + ushort ET = 0; + ushort WT = 0; + byte 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: + MR_IM(ref DT, ref R); ADC(ref WT, ref DT, ref R); + ADD_CYCLE(2, ref exec_cycles); + break; + case 0x65: + MR_ZP(ref EA, ref DT, ref R); ADC(ref WT, ref DT, ref R); + ADD_CYCLE(3, ref exec_cycles); + break; + case 0x75: + MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R); + ADD_CYCLE(4, ref exec_cycles); + break; + } + } + _execute_exit: +#if !DPCM_SYNCCLOCK + apu.SyncDPCM(TOTAL_cycles - OLD_cycles); +#endif + return TOTAL_cycles - OLD_cycles; + } + + + + internal void SetClockProcess(bool bEnable) + { + m_bClockProcess = bEnable; + } + + internal byte OP6502(ushort addr) + { + return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MR_IM(ref byte DT, ref R6502 R) + { + DT = OP6502(R.PC++); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R) + { + EA = OP6502(R.PC++); + DT = ZPRD(ref EA); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private byte ZPRD(ref ushort A) + { + return MMU.RAM[A]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ADC(ref ushort WT, ref byte DT, ref R6502 R) + { + WT = (ushort)(R.A + DT + (R.P & C_FLAG)); + TST_FLAG(WT > 0xFF, C_FLAG, ref R); + var temp = ((~(R.A ^ DT)) & (R.A ^ WT) & 0x80); + TST_FLAG(temp != 0, V_FLAG, ref R); + R.A = (byte)WT; + SET_ZN_FLAG(R.A, ref R); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void TST_FLAG(bool F, byte V, ref R6502 R) + { + byte temp = (byte)~V; + R.P &= temp; + + if (F) R.P |= V; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void SET_ZN_FLAG(byte A, ref R6502 R) + { + byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG))); + R.P &= temp; + R.P |= ZN_Table[A]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void ADD_CYCLE(int V, ref int exec_cycles) + { + exec_cycles += V; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R) + { + DT = OP6502(R.PC++); + EA = (ushort)(DT + R.X); + DT = ZPRD(ref EA); } } @@ -44,13 +253,13 @@ public struct R6502 { - ushort PC; - byte A; - byte P; - byte X; - byte Y; - byte S; + public ushort PC; + public byte A; + public byte P; + public byte X; + public byte Y; + public byte S; - byte Int_Pending; + public byte Int_Pending; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs new file mode 100644 index 0000000..8bdaf21 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs.meta new file mode 100644 index 0000000..396fe6d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9c379fb6535bd23449474dee5018652c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs new file mode 100644 index 0000000..e6ed2f8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs @@ -0,0 +1,23 @@ +namespace VirtualNes.Core +{ + public enum NESCOMMAND + { + NESCMD_NONE = 0, + NESCMD_HWRESET, + NESCMD_SWRESET, + NESCMD_EXCONTROLLER, // Commandparam + NESCMD_DISK_THROTTLE_ON, + NESCMD_DISK_THROTTLE_OFF, + NESCMD_DISK_EJECT, + NESCMD_DISK_0A, + NESCMD_DISK_0B, + NESCMD_DISK_1A, + NESCMD_DISK_1B, + NESCMD_DISK_2A, + NESCMD_DISK_2B, + NESCMD_DISK_3A, + NESCMD_DISK_3B, + + NESCMD_SOUND_MUTE, // CommandParam + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs.meta new file mode 100644 index 0000000..db767eb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NESCOMMAND.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d7e8126382c9728429056ba33afc85eb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs new file mode 100644 index 0000000..b4166fb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs @@ -0,0 +1,58 @@ +namespace VirtualNes.Core +{ + public struct 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 GetNTSC() + { + return 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 GetPAL() + { + return 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs.meta new file mode 100644 index 0000000..d583d21 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4865f8871b37b0041b77060cf3c62664 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs new file mode 100644 index 0000000..3bbca28 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs @@ -0,0 +1,59 @@ +using Codice.CM.Client.Differences; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs.meta new file mode 100644 index 0000000..4557eb3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 447095b8c8ae4c74885562c127998e9e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs new file mode 100644 index 0000000..e4dab0e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VirtualNes +{ + public static class MMU + { + // CPU 儊儌儕僶儞僋 + public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵 + + // NES儊儌儕 + public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM + public static byte[] WARM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta new file mode 100644 index 0000000..a77dd40 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 863989820a4fb1d49a7c0c883c5a7078 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs index 77eabba..c190f75 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs @@ -6,7 +6,8 @@ using System.Threading.Tasks; namespace VirtualNes.Core { - public class Mapper + public abstract class Mapper { + internal virtual void Clock(int cycles) { } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs index 66a5e55..9f59b77 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs @@ -1,6 +1,8 @@ +using Codice.CM.Client.Differences; using System; using System.Collections.Generic; using System.IO; +using System.Text; using VirtualNes.Core.Debug; namespace VirtualNes.Core @@ -13,10 +15,9 @@ namespace VirtualNes.Core public ROM rom; public PAD pad; public Mapper mapper; - public NesConfig NesCfg; - - protected List m_CheatCode = new List(); + public NesConfig nescfg; + private List m_CheatCode = new List(); private bool m_bDiskThrottle; private int m_CommandRequest; private int m_nSnapNo; @@ -30,12 +31,32 @@ namespace VirtualNes.Core 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 long base_cycles; + private long emul_cycles; public NES(string fname) { @@ -75,7 +96,7 @@ namespace VirtualNes.Core bVideoMode = false; - NesCfg = NesConfig.GetNTSC(); + nescfg = NesConfig.GetNTSC(); CheatInitial(); @@ -102,65 +123,241 @@ namespace VirtualNes.Core } } + public void Command(NESCOMMAND cmd) + { + CommandParam(cmd, 0); + } + + public bool CommandParam(NESCOMMAND cmd, int param) + { + switch (cmd) + { + case NESCOMMAND.NESCMD_NONE: break; + case NESCOMMAND.NESCMD_HWRESET: + Reset(); + m_CommandRequest = (int)cmd; + break; + case NESCOMMAND.NESCMD_SWRESET: + SoftReset(); + m_CommandRequest = (int)cmd; + break; + default: + throw new NotImplementedException($"{cmd} not impl right now"); + } + + return true; + } + public void CheatInitial() { m_CheatCode.Clear(); } - } - public struct 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 ScanlingCycles; // 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 GetNTSC() + public void EmulateFrame(bool bDraw) { - return new NesConfig + int scanline = 0; + if (rom.IsNSF()) { - BaseClock = 21477270.0f, - CpuClock = 1789772.5f, - TotalScanLines = 262, - ScanlingCycles = 1364, - HDrawCycles = 1024, - HBlankCycles = 340, - ScanlineEndCycles = 4, - FrameCycles = 1364 * 262, - FrameIrqCycles = 29830, - FrameRate = 60, - FramePeriod = 1000.0f / 60.0f - }; + EmulateNSF(); + return; + } + + CheatCodeProcess(); + + NES_scanline = scanline; + + if (RenderMethod != EnumRenderMethod.TILE_RENDER) + { + bZapper = false; + while (true) + { + ppu.SetRenderScanline(scanline); + + if (scanline == 0) + { + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + EmulationCPU(nescfg.ScanlineCycles); + } + } + } + } } - public static NesConfig GetPAL() + internal void EmulationCPU(int basecycles) { - return new NesConfig + int cycles; + + base_cycles += basecycles; + cycles = (int)((base_cycles / 12) - emul_cycles); + + if (cycles > 0) { - BaseClock = 26601714.0f, - CpuClock = 1662607.125f, - TotalScanLines = 312, - ScanlingCycles = 1278, - HDrawCycles = 960, - HBlankCycles = 318, - ScanlineEndCycles = 2, - FrameCycles = 1278 * 312, - FrameIrqCycles = 33252, - FrameRate = 50, - FramePeriod = 1000.0f / 50.0f - }; + emul_cycles += cpu.EXEC(cycles); + } + } + + internal void Reset() + { + SaveSRAM(); + SaveDISK(); + SaveTurboFile(); + + //todo : ʵ + } + + internal void SoftReset() + { + //todo : ʵ + } + + internal void EmulateNSF() + { + //todo : ʵNSFģ + throw new NotImplementedException("EmulateNSF"); + } + + internal void CheatCodeProcess() + { + //todo : ʵ + } + + 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.WARM[i] != 0x00) + break; + } + + if (i < SAVERAM_SIZE) + { + var romName = rom.GetRomName(); + + Debuger.Log($"Saving SAVERAM...[{romName}]"); + + Supporter.SaveSRAMToFile(MMU.WARM, 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() + { + //todo : ʵ + } + + 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) + { + //todo : ʵTape (ĿǼ¼ҲPlay,ȼܵ) } } + } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index a71c5b3..0758a47 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -1,4 +1,6 @@ -namespace VirtualNes.Core +using System; + +namespace VirtualNes.Core { public class PAD { @@ -32,6 +34,10 @@ padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0; micbitsync = 0; } + + public void Dispose() + { + } } public enum VSType diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index dd9e29f..0e35183 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -1,4 +1,6 @@ -namespace VirtualNes.Core +using System; + +namespace VirtualNes.Core { public class PPU { @@ -9,6 +11,10 @@ private int nVSColorMap; private byte VSSecurityData; private byte[] Bit2Rev = new byte[256]; + private int ScanlineNo; + /// 作为lpScreen数组的索引 + private int lpScanline; + public PPU(NES nes) { m_nes = nes; @@ -31,5 +37,24 @@ Bit2Rev[i] = c; } } + + public void Dispose() + { + } + + internal void SetRenderScanline(int scanline) + { + ScanlineNo = scanline; + if (scanline < 240) + { + lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline; + } + } + + private enum Screen + { + SCREEN_WIDTH = 256 + 16, + SCREEN_HEIGHT = 240 + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs index a0d3a5f..8e910f0 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs @@ -3,6 +3,7 @@ using System; using System.IO; using System.Linq; using VirtualNes.Core.Debug; +using static UnityEditor.PlayerSettings; namespace VirtualNes.Core { @@ -282,11 +283,30 @@ namespace VirtualNes.Core } } + 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 IsSAVERAM() + { + return (header.control1 & (byte)EnumRomControlByte1.ROM_SAVERAM) > 0; + } + protected void FileNameCheck(string fname) { if (fname.Contains("(E)")) @@ -295,6 +315,41 @@ namespace VirtualNes.Core return; } } + + internal string GetRomName() + { + return name; + } + + internal int GetMapperNo() + { + return mapper; + } + + internal byte[] GetPROM() + { + return lpPRG; + } + + internal byte[] GetDISK() + { + return lpDisk; + } + + internal int GetDiskNo() + { + return diskno; + } + + internal ulong GetGameID() + { + return fdsgameID; + } + + internal ulong GetMakerID() + { + return fdsmakerID; + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs index 884d34e..8f07b85 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs @@ -28,6 +28,15 @@ namespace VirtualNes.Core 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 interface ISupporterImpl @@ -35,5 +44,7 @@ namespace VirtualNes.Core 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); } } From 652616663c090ff21cae637653595357cddaa2fa Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Thu, 25 Jul 2024 14:03:52 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E7=BF=BB=E8=AF=91=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets/VirtualNes.Core/APU.cs | 7 +- .../VirtualNes.Core/ApuEX/APU_INTERNAL.cs | 47 +++++++ .../Assets/VirtualNes.Core/ApuEX/DPCM.cs | 35 +++++ .../Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta | 11 ++ .../Assets/VirtualNes.Core/ApuEX/NOISE.cs | 42 ++++++ .../VirtualNes.Core/ApuEX/NOISE.cs.meta | 11 ++ .../Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs | 51 +++++++ .../VirtualNes.Core/ApuEX/RECTANGLE.cs.meta | 11 ++ .../Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs | 29 ++++ .../VirtualNes.Core/ApuEX/TRIANGLE.cs.meta | 11 ++ .../Assets/VirtualNes.Core/CPU.cs | 57 ++++++-- .../Assets/VirtualNes.Core/MMU.cs | 29 +++- .../Assets/VirtualNes.Core/Mapper/Mapper.cs | 84 +++++++++++- .../Assets/VirtualNes.Core/NES.cs | 129 ++++++++++++++++++ .../Assets/VirtualNes.Core/PAD.cs | 67 +++++++-- .../Assets/VirtualNes.Core/PPU.cs | 85 +++++++++++- .../Assets/VirtualNes.Core/PadEX/EXPAD.cs | 19 +++ .../Assets/VirtualNes.Core/ROM.cs | 5 + 18 files changed, 704 insertions(+), 26 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs index 5d76dd9..e0a0488 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs @@ -15,7 +15,7 @@ namespace VirtualNes.Core protected int[] lowpass_filter = new int[4]; protected QUEUE queue; protected QUEUE exqueue; - protected bool[] m_bMute = new bool[16]; + protected bool[] m_bMute = new bool[16]; public APU(NES parent) { @@ -43,6 +43,11 @@ namespace VirtualNes.Core { @internal.Sync(cycles); } + + internal byte Read(ushort addr) + { + return @internal.SyncRead(addr); + } } public struct QUEUEDATA diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index ca1a0f3..5315c25 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -1,11 +1,26 @@ +using System; + namespace VirtualNes.Core { public class APU_INTERNAL : APU_INTERFACE { 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(); + + + public void SetParent(NES parent) { nes = parent; @@ -55,5 +70,37 @@ namespace VirtualNes.Core { throw new System.NotImplementedException(); } + + 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; + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs new file mode 100644 index 0000000..1f48eb1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs @@ -0,0 +1,35 @@ +using Codice.CM.Client.Differences; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VirtualNes.Core +{ + public class DPCM + { + public byte[] reg = new byte[4]; + public byte enable; + public byte looping; + public byte cur_byte; + public byte dpcm_value; + + public int freq; + public int phaseacc; + public int output; + + ushort address, cache_addr; + public int dmalength, cache_dmalength; + public int dpcm_output_real, dpcm_output_fake, dpcm_output_old, dpcm_output_offset; + + // For sync + public byte[] sync_reg = new byte[4]; + public byte sync_enable; + public byte sync_looping; + public byte sync_irq_gen; + public byte sync_irq_enable; + public int sync_cycles, sync_cache_cycles; + public int sync_dmalength, sync_cache_dmalength; + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta new file mode 100644 index 0000000..08b468c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e6289a516ac91b541b2b1807bb07e2b0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs new file mode 100644 index 0000000..709483d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VirtualNes.Core +{ + public class NOISE + { + public byte[] reg = new byte[4]; // register + + public byte enable; // enable + public byte holdnote; // holdnote + public byte volume; // volume + public byte xor_tap; + public int shift_reg; + + // For Render + public int phaseacc; + public int freq; + public int len_count; + + public int nowvolume; + public int output; + + // For Envelope + public byte env_fixed; + public byte env_decay; + public byte env_count; + public byte dummy0; + public int env_vol; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_output_enable; + public byte sync_enable; + public byte sync_holdnote; + public byte dummy1; + public int sync_len_count; + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta new file mode 100644 index 0000000..69ea5b5 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8680ce7dbdceb504dbda3b98dbdb1297 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs new file mode 100644 index 0000000..69abd8f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VirtualNes.Core +{ + public class RECTANGLE + { + public byte[] reg = new byte[4]; // register + + public byte enable; // enable + public byte holdnote; // holdnote + public byte volume; // volume + public byte complement; + + // For Render + public int phaseacc; + public int freq; + public int freqlimit; + public int adder; + public int duty; + public int len_count; + + public int nowvolume; + + // For Envelope + public byte env_fixed; + public byte env_decay; + public byte env_count; + public byte dummy0; + public int env_vol; + + // For Sweep + public byte swp_on; + public byte swp_inc; + public byte swp_shift; + public byte swp_decay; + public byte swp_count; + public byte[] dummy1 = new byte[3]; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_output_enable; + public byte sync_enable; + public byte sync_holdnote; + public byte dummy2; + public int sync_len_count; + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta new file mode 100644 index 0000000..9cbc24b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6e50831f6c445fe489d7e1737269296e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs new file mode 100644 index 0000000..80f4c4a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs @@ -0,0 +1,29 @@ +namespace VirtualNes.Core +{ + public class TRIANGLE + { + public byte[] reg = new byte[4]; + + public byte enable; + public byte holdnote; + public byte counter_start; + public byte dummy0; + + public int phaseacc; + public int freq; + public int len_count; + public int lin_count; + public int adder; + + public int nowvolume; + + // For sync; + public byte[] sync_reg = new byte[4]; + public byte sync_enable; + public byte sync_holdnote; + public byte sync_counter_start; + // public byte dummy1; + public int sync_len_count; + public int sync_lin_count; + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta new file mode 100644 index 0000000..4cfe6b6 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/TRIANGLE.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4ed2788da33fe474facc1d7ce1b34d03 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs index ea35cfc..833748e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs @@ -135,6 +135,13 @@ namespace VirtualNes.Core case 0x75: MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R); ADD_CYCLE(4, ref exec_cycles); + break; + case 0x6D: + MR_AB(ref EA, ref DT, ref R);ADC(ref WT, ref DT, ref R); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0x7D: + break; } } @@ -157,26 +164,51 @@ namespace VirtualNes.Core return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ushort OP6502W(ushort addr) + { + var bytePage = MMU.CPU_MEM_BANK[addr >> 13]; + + return BitConverter.ToUInt16(bytePage, addr & 0x1FFF); + } + + 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 MR_IM(ref byte DT, ref R6502 R) { DT = OP6502(R.PC++); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R) { EA = OP6502(R.PC++); DT = ZPRD(ref EA); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private byte ZPRD(ref ushort A) { return MMU.RAM[A]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ADC(ref ushort WT, ref byte DT, ref R6502 R) { WT = (ushort)(R.A + DT + (R.P & C_FLAG)); @@ -187,7 +219,6 @@ namespace VirtualNes.Core SET_ZN_FLAG(R.A, ref R); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void TST_FLAG(bool F, byte V, ref R6502 R) { byte temp = (byte)~V; @@ -196,7 +227,6 @@ namespace VirtualNes.Core if (F) R.P |= V; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void SET_ZN_FLAG(byte A, ref R6502 R) { byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG))); @@ -204,19 +234,30 @@ namespace VirtualNes.Core R.P |= ZN_Table[A]; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ADD_CYCLE(int V, ref int exec_cycles) { exec_cycles += V; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R) { DT = OP6502(R.PC++); EA = (ushort)(DT + R.X); DT = ZPRD(ref EA); } + + private void MR_AB(ref ushort EA, ref byte DT, ref R6502 R) + { + EA = OP6502W(R.PC); + R.PC += 2; + DT = RD6502(EA); + } + + internal void ClrIRQ(byte mask) + { + byte temp = (byte)~mask; + R.Int_Pending &= temp; + } } public enum StatusFlag6502 : int diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs index e4dab0e..fad8a8f 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs @@ -1,4 +1,5 @@ -using System; +using Codice.CM.Client.Differences; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -11,8 +12,34 @@ namespace VirtualNes // CPU 儊儌儕僶儞僋 public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵 + // PPU 儊儌儕僶儞僋 + public static byte[][] PPU_MEM_BANK = new byte[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[] WARM = 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 } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs index c190f75..132b313 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs @@ -1,4 +1,5 @@ -using System; +using Codice.CM.Client.Differences; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,6 +9,85 @@ namespace VirtualNes.Core { public abstract class Mapper { - internal virtual void Clock(int cycles) { } + 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) + { + switch (no) + { + default: + throw new NotImplementedException($"Mapper#{no} is not Impl"); + } + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs index 9f59b77..9e569ab 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs @@ -358,6 +358,135 @@ namespace VirtualNes.Core { //todo : ʵTape (ĿǼ¼ҲPlay,ȼܵ) } + + 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; + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index 0758a47..7dba9d2 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -4,18 +4,21 @@ namespace VirtualNes.Core { public class PAD { - protected NES nes; - protected int excontroller_select; - protected EXPAD expad; - protected bool bStrobe; - protected bool bSwapButton; - protected bool bSwapPlayer; - protected bool bZapperMode; - protected VSType nVSSwapType; - protected byte[] padbit = new byte[4]; - protected byte micbit; - protected byte[] padbitsync = new byte[4]; - protected byte micbitsync; + 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; + + public uint pad1bit, pad2bit, pad3bit, pad4bit; public PAD(NES parent) { @@ -35,9 +38,47 @@ namespace VirtualNes.Core micbitsync = 0; } - public void Dispose() + 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() { } } public enum VSType diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index 0e35183..0d5aea4 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -1,9 +1,39 @@ -using System; +using Codice.CM.Client.Differences; +using System; namespace VirtualNes.Core { public class PPU { + // 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 m_nes; private byte[] lpScreen; private byte[] lpColormode; @@ -42,6 +72,59 @@ namespace VirtualNes.Core { } + 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; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs index 38e7a47..56e5f6f 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs @@ -1,3 +1,4 @@ +using System; using System.Collections; using System.Collections.Generic; using UnityEngine; @@ -6,5 +7,23 @@ 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs index 8e910f0..46e12ff 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs @@ -350,6 +350,11 @@ namespace VirtualNes.Core { return fdsmakerID; } + + internal bool IsVSUNISYSTEM() + { + return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0; + } } From 88214310d776bf401de6ed7a61ceb3df9fb07cc7 Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Thu, 25 Jul 2024 18:34:52 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E8=BF=AD=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets/Script/NesEmulator/NesEmulator.cs | 3 +- .../Assets/VirtualNes.Core/APU.cs | 69 +++- .../VirtualNes.Core/ApuEX/APU_INTERNAL.cs | 307 +++++++++++++++++- .../Assets/VirtualNes.Core/CPU.cs | 236 ++++++++++++-- .../Assets/VirtualNes.Core/MMU.cs | 21 +- .../Assets/VirtualNes.Core/NES.cs | 107 +++++- .../Assets/VirtualNes.Core/PPU.cs | 200 +++++++++++- 7 files changed, 883 insertions(+), 60 deletions(-) diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index a22cab9..f06c119 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -10,7 +10,8 @@ namespace AxibugEmuOnline.Client private void Start() { - StartGame("Kirby.nes"); + + //StartGame("Kirby.nes"); } public void StartGame(string romName) diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs index e0a0488..429bc4c 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs @@ -1,11 +1,12 @@ -using Codice.CM.Common; -using System; -using System.Collections; +using System; +using VirtualNes.Core.Debug; namespace VirtualNes.Core { public class APU { + public const uint QUEUE_LENGTH = 8192; + private NES nes; private byte exsound_select; private APU_INTERNAL @internal = new APU_INTERNAL(); @@ -13,8 +14,8 @@ namespace VirtualNes.Core private int last_diff; protected short[] m_SoundBuffer = new short[256]; protected int[] lowpass_filter = new int[4]; - protected QUEUE queue; - protected QUEUE exqueue; + protected QUEUE queue = new QUEUE(); + protected QUEUE exqueue = new QUEUE(); protected bool[] m_bMute = new bool[16]; public APU(NES parent) @@ -28,8 +29,6 @@ namespace VirtualNes.Core Array.Clear(m_SoundBuffer, 0, m_SoundBuffer.Length); Array.Clear(lowpass_filter, 0, lowpass_filter.Length); - queue = QUEUE.GetDefault(); - exqueue = QUEUE.GetDefault(); for (int i = 0; i < m_bMute.Length; i++) m_bMute[i] = true; @@ -48,6 +47,49 @@ namespace VirtualNes.Core { 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 struct QUEUEDATA @@ -58,19 +100,10 @@ namespace VirtualNes.Core public byte reserved; } - public struct QUEUE + public class QUEUE { public int rdptr; public int wrptr; - QUEUEDATA[] data; - - public static QUEUE GetDefault() - { - var res = new QUEUE(); - res.rdptr = 0; - res.wrptr = 0; - res.data = new QUEUEDATA[8192]; - return res; - } + public QUEUEDATA[] data = new QUEUEDATA[8192]; } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index 5315c25..033b9f8 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -4,6 +4,26 @@ namespace VirtualNes.Core { public class APU_INTERNAL : APU_INTERFACE { + 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; @@ -19,7 +39,8 @@ namespace VirtualNes.Core private NOISE ch3 = new NOISE(); private DPCM ch4 = new DPCM(); - + // $4015 Reg + private byte reg4015, sync_reg4015; public void SetParent(NES parent) { @@ -48,7 +69,27 @@ namespace VirtualNes.Core private void UpdateFrame() { - //TODO : ʵ + 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) @@ -102,5 +143,267 @@ namespace VirtualNes.Core } 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; + } + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs index 833748e..0fca7d8 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs @@ -1,13 +1,14 @@ #undef DPCM_SYNCCLOCK -using Codice.CM.Client.Differences; using System; -using System.Runtime.CompilerServices; +using UnityEngine.UI; 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 @@ -42,7 +43,7 @@ namespace VirtualNes.Core private int DMA_cycles; private Mapper mapper; private APU apu; - private R6502 R; + private R6502 R = new R6502(); private byte[] ZN_Table = new byte[256]; public CPU(NES parent) @@ -97,18 +98,18 @@ namespace VirtualNes.Core nmi_request = irq_request = 0; opcode = OP6502(R.PC++); - if (R.Int_Pending != 0) + if (R.INT_pending != 0) { - if ((R.Int_Pending & NMI_FLAG) != 0) + if ((R.INT_pending & NMI_FLAG) != 0) { nmi_request = 0xFF; byte temp = unchecked((byte)(~NMI_FLAG)); - R.Int_Pending &= temp; + R.INT_pending &= temp; } - else if ((R.Int_Pending & IRQ_MASK) != 0) + else if ((R.INT_pending & IRQ_MASK) != 0) { byte temp = unchecked((byte)(~IRQ_TRIGGER2)); - R.Int_Pending &= temp; + R.INT_pending &= temp; if ( ((R.P & I_FLAG) == 0) && @@ -117,7 +118,7 @@ namespace VirtualNes.Core { irq_request = 0xFF; temp = unchecked((byte)(~IRQ_TRIGGER)); - R.Int_Pending &= temp; + R.INT_pending &= temp; } } } @@ -125,23 +126,80 @@ namespace VirtualNes.Core switch (opcode) { case 0x69: - MR_IM(ref DT, ref R); ADC(ref WT, ref DT, ref R); + MR_IM(ref DT); ADC(ref WT, ref DT); ADD_CYCLE(2, ref exec_cycles); break; case 0x65: - MR_ZP(ref EA, ref DT, ref R); ADC(ref WT, ref DT, ref R); + MR_ZP(ref EA, ref DT); ADC(ref WT, ref DT); ADD_CYCLE(3, ref exec_cycles); break; case 0x75: - MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R); + MR_ZX(ref DT, ref EA); ADC(ref WT, ref DT); ADD_CYCLE(4, ref exec_cycles); break; case 0x6D: - MR_AB(ref EA, ref DT, ref R);ADC(ref WT, ref DT, ref R); + MR_AB(ref EA, ref DT); ADC(ref WT, ref DT); ADD_CYCLE(4, ref exec_cycles); break; case 0x7D: - + MR_AX(ref ET, ref EA, ref DT); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0x79: + MR_AY(ref ET, ref EA, ref DT); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0x61: + MR_IX(ref DT, ref EA); ADC(ref WT, ref DT); + ADD_CYCLE(6, ref exec_cycles); + break; + case 0x71: + MR_IY(ref DT, ref ET, ref EA); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0xE9: + MR_IM(ref DT); SBC(ref WT, ref DT); + ADD_CYCLE(2, ref exec_cycles); + break; + case 0xE5: + MR_ZP(ref EA, ref DT); SBC(ref WT, ref DT); + ADD_CYCLE(3, ref exec_cycles); + break; + case 0xF5: + MR_ZX(ref DT, ref EA); SBC(ref WT, ref DT); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0xED: + MR_AB(ref EA, ref DT); SBC(ref WT, ref DT); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0xFD: + MR_AX(ref ET, ref EA, ref DT); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0xF9: // SBC $????,Y + MR_AY(ref ET, ref EA, ref DT); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(4, ref exec_cycles); + break; + case 0xE1: // SBC ($??,X) + MR_IX(ref DT, ref EA); SBC(ref WT, ref DT); + ADD_CYCLE(6, ref exec_cycles); + break; + case 0xF1: // SBC ($??),Y + MR_IY(ref DT, ref ET, ref EA); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); + ADD_CYCLE(5, ref exec_cycles); + break; + case 0xC6: // DEC $?? + MR_ZP(ref EA, ref DT); DEC(ref DT); MW_ZP(EA, DT); + ADD_CYCLE(5, ref exec_cycles); + break; + case 0xD6: // DEC $??,X + MR_ZX(ref DT, ref EA); DEC(ref DT); MW_ZP(EA, DT); + ADD_CYCLE(6, ref exec_cycles); + break; + case 0xCE: // DEC $???? + MR_AB(ref EA, ref DT); DEC(ref DT); MW_EA(EA, DT); + ADD_CYCLE(6, ref exec_cycles); break; } } @@ -152,8 +210,6 @@ namespace VirtualNes.Core return TOTAL_cycles - OLD_cycles; } - - internal void SetClockProcess(bool bEnable) { m_bClockProcess = bEnable; @@ -193,33 +249,41 @@ namespace VirtualNes.Core return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; } - private void MR_IM(ref byte DT, ref R6502 R) + private void MR_IM(ref byte DT) { DT = OP6502(R.PC++); } - private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R) + private void MR_ZP(ref ushort EA, ref byte DT) { EA = OP6502(R.PC++); - DT = ZPRD(ref EA); + DT = ZPRD(EA); } - private byte ZPRD(ref ushort A) + private byte ZPRD(ushort A) { return MMU.RAM[A]; } - private void ADC(ref ushort WT, ref byte DT, ref R6502 R) + private ushort ZPRDW(int A) { - WT = (ushort)(R.A + DT + (R.P & C_FLAG)); - TST_FLAG(WT > 0xFF, C_FLAG, ref R); - var temp = ((~(R.A ^ DT)) & (R.A ^ WT) & 0x80); - TST_FLAG(temp != 0, V_FLAG, ref R); - R.A = (byte)WT; - SET_ZN_FLAG(R.A, ref R); + ushort ram1 = MMU.RAM[A]; + ushort ram2 = MMU.RAM[A + 1]; + ram2 <<= 8; + return (ushort)(ram1 + ram2); } - private void TST_FLAG(bool F, byte V, ref R6502 R) + private void ADC(ref ushort WT, ref byte DT) + { + 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; @@ -227,7 +291,7 @@ namespace VirtualNes.Core if (F) R.P |= V; } - private void SET_ZN_FLAG(byte A, ref R6502 R) + private void SET_ZN_FLAG(byte A) { byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG))); R.P &= temp; @@ -239,24 +303,126 @@ namespace VirtualNes.Core exec_cycles += V; } - private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R) + private void MR_ZX(ref byte DT, ref ushort EA) { DT = OP6502(R.PC++); EA = (ushort)(DT + R.X); - DT = ZPRD(ref EA); + DT = ZPRD(EA); } - private void MR_AB(ref ushort EA, ref byte DT, ref R6502 R) + private void MR_AB(ref ushort EA, ref byte DT) { EA = OP6502W(R.PC); R.PC += 2; DT = RD6502(EA); } + private void MR_AX(ref ushort ET, ref ushort EA, ref byte DT) + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (byte)(ET + R.X); + DT = RD6502(EA); + } + + private void CHECK_EA(ref ushort EA, ref ushort ET, ref int exec_cycles) + { + if ((ET & 0xFF00) != (EA & 0xFF00)) ADD_CYCLE(1, ref exec_cycles); + } + + private void MR_AY(ref ushort ET, ref ushort EA, ref byte DT) + { + ET = OP6502W(R.PC); + R.PC += 2; + EA = (ushort)(ET + R.Y); + DT = RD6502(EA); + } + + private void MR_IX(ref byte DT, ref ushort EA) + { + DT = OP6502(R.PC++); + EA = ZPRDW(DT + R.X); + DT = RD6502(EA); + } + + private void MR_IY(ref byte DT, ref ushort ET, ref ushort EA) + { + DT = OP6502(R.PC++); + ET = ZPRDW(DT); + EA = (ushort)(ET + R.Y); + DT = RD6502(EA); + } + + private void SBC(ref ushort WT, ref byte DT) + { + 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(ref byte DT) + { + DT--; + SET_ZN_FLAG(DT); + } + + private void MW_ZP(ushort EA, byte DT) + { + ZPWR(EA, DT); + } + + private void ZPWR(ushort a, byte v) + { + MMU.RAM[a] = v; + } + + private void MW_EA(ushort EA, byte DT) + { + WR6502(EA, DT); + } + internal void ClrIRQ(byte mask) { byte temp = (byte)~mask; - R.Int_Pending &= temp; + 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; } } @@ -292,7 +458,7 @@ namespace VirtualNes.Core IRQ_VECTOR = 0xFFFE } - public struct R6502 + public class R6502 { public ushort PC; public byte A; @@ -301,6 +467,6 @@ namespace VirtualNes.Core public byte Y; public byte S; - public byte Int_Pending; + public byte INT_pending; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs index fad8a8f..b8cf455 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs @@ -40,6 +40,25 @@ namespace VirtualNes 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 + public static ushort loopy_x; // tile x offset + + // 儊儌儕僞僀僾 + // 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偺儈儔乕 + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs index 9e569ab..b1a30f9 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs @@ -487,6 +487,111 @@ namespace VirtualNes.Core return ret; } - } + 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 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); + CPUREG[addr & 0xFF] = data; + m_TapeIn = data; + break; + case 0x17: + 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; + } + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index 0d5aea4..4f608b3 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -5,6 +5,66 @@ namespace VirtualNes.Core { public class PPU { + 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; // 堘偆丠 @@ -34,7 +94,7 @@ namespace VirtualNes.Core public const byte SP_PRIORITY_BIT = 0x20; public const byte SP_COLOR_BIT = 0x03; - private NES m_nes; + private NES nes; private byte[] lpScreen; private byte[] lpColormode; private bool bVSMode; @@ -47,7 +107,7 @@ namespace VirtualNes.Core public PPU(NES nes) { - m_nes = nes; + this.nes = nes; lpScreen = null; lpColormode = null; @@ -134,6 +194,142 @@ namespace VirtualNes.Core } } + 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)); + } + } + private enum Screen { SCREEN_WIDTH = 256 + 16, From 323e8c66a2c7324471dac7d68786597f124dd62e Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Fri, 26 Jul 2024 17:52:33 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E8=BF=AD=E4=BB=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Script/NesEmulator/CoreSupporter.cs | 2 + .../Assets/Script/NesEmulator/NesEmulator.cs | 3 +- .../Assets/VirtualNes.Core/APU.cs | 82 + .../Assets/VirtualNes.Core/ApuEX/APU_FDS.cs | 45 + .../VirtualNes.Core/ApuEX/APU_FDS.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/APU_FME7.cs | 27 + .../VirtualNes.Core/ApuEX/APU_FME7.cs.meta | 11 + .../VirtualNes.Core/ApuEX/APU_INTERFACE.cs | 22 +- .../VirtualNes.Core/ApuEX/APU_INTERNAL.cs | 41 +- .../Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs | 32 + .../VirtualNes.Core/ApuEX/APU_MMC5.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/APU_N106.cs | 25 + .../VirtualNes.Core/ApuEX/APU_N106.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs | 310 +++ .../VirtualNes.Core/ApuEX/APU_VRC6.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs | 25 + .../VirtualNes.Core/ApuEX/APU_VRC7.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/DPCM.cs | 9 +- .../Assets/VirtualNes.Core/ApuEX/NOISE.cs | 8 +- .../Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs | 42 +- .../Assets/VirtualNes.Core/CPU.cs | 1727 ++++++++++++++++- .../Assets/VirtualNes.Core/Cheat.cs | 20 + .../{CHEATCODE.cs.meta => Cheat.cs.meta} | 0 .../Assets/VirtualNes.Core/CoreLibs/CRC.cs | 29 +- .../VirtualNes.Core/CoreLibs/Emu2413.cs | 145 ++ .../VirtualNes.Core/CoreLibs/Emu2413.cs.meta | 11 + .../CoreLibs/EnumRenderMethod.cs | 8 +- .../VirtualNes.Core/CoreLibs/MemoryUtility.cs | 17 + .../CoreLibs/MemoryUtility.cs.meta | 11 + .../VirtualNes.Core/CoreLibs/NesConfig.cs | 63 +- .../VirtualNes.Core/CoreLibs/ROMClasses.cs | 9 +- .../VirtualNes.Core/CoreLibs/RomPatch.cs | 7 +- .../Assets/VirtualNes.Core/CoreLibs/State.cs | 7 +- .../Assets/VirtualNes.Core/MMU.cs | 192 +- .../Assets/VirtualNes.Core/Mapper/Mapper.cs | 11 +- .../Assets/VirtualNes.Core/NES.cs | 248 ++- .../Assets/VirtualNes.Core/PAD.cs | 354 +++- .../Assets/VirtualNes.Core/PPU.cs | 49 +- .../Assets/VirtualNes.Core/PadEX/EXPAD.cs | 7 +- .../PadEX/EXPAD_CrazyClimber.cs | 9 + .../PadEX/EXPAD_CrazyClimber.cs.meta | 11 + .../PadEX/EXPAD_ExcitingBoxing.cs | 9 + .../PadEX/EXPAD_ExcitingBoxing.cs.meta | 11 + .../PadEX/EXPAD_FamlyTrainer.cs | 9 + .../PadEX/EXPAD_FamlyTrainer.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Gyromite.cs | 9 + .../PadEX/EXPAD_Gyromite.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_HyperShot.cs | 9 + .../PadEX/EXPAD_HyperShot.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Keyboard.cs | 9 + .../PadEX/EXPAD_Keyboard.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Mahjang.cs | 9 + .../PadEX/EXPAD_Mahjang.cs.meta | 11 + .../PadEX/EXPAD_OekakidsTablet.cs | 9 + .../PadEX/EXPAD_OekakidsTablet.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Paddle.cs | 9 + .../PadEX/EXPAD_Paddle.cs.meta | 11 + .../PadEX/EXPAD_SpaceShadowGun.cs | 9 + .../PadEX/EXPAD_SpaceShadowGun.cs.meta | 11 + .../PadEX/EXPAD_Supor_Keyboard.cs | 9 + .../PadEX/EXPAD_Supor_Keyboard.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Toprider.cs | 9 + .../PadEX/EXPAD_Toprider.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_TurboFile.cs | 9 + .../PadEX/EXPAD_TurboFile.cs.meta | 11 + .../PadEX/EXPAD_VSUnisystem.cs | 9 + .../PadEX/EXPAD_VSUnisystem.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_VSZapper.cs | 9 + .../PadEX/EXPAD_VSZapper.cs.meta | 11 + .../VirtualNes.Core/PadEX/EXPAD_Zapper.cs | 18 + .../PadEX/EXPAD_Zapper.cs.meta | 11 + .../Assets/VirtualNes.Core/ROM.cs | 61 +- .../Supporter/EmulatorConfig.meta | 8 + .../Supporter/EmulatorConfig/CfgController.cs | 6 + .../EmulatorConfig/CfgController.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgEmulator.cs | 18 + .../EmulatorConfig/CfgEmulator.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgExtraSound.cs | 6 + .../EmulatorConfig/CfgExtraSound.cs.meta | 11 + .../EmulatorConfig/CfgGeneral.cs} | 3 +- .../EmulatorConfig/CfgGeneral.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgGraphics.cs | 6 + .../EmulatorConfig/CfgGraphics.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgLanguage.cs | 6 + .../EmulatorConfig/CfgLanguage.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgLauncher.cs | 6 + .../EmulatorConfig/CfgLauncher.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgMovie.cs | 6 + .../Supporter/EmulatorConfig/CfgMovie.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgNetPlay.cs | 6 + .../EmulatorConfig/CfgNetPlay.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgPath.cs | 6 + .../Supporter/EmulatorConfig/CfgPath.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgShortCut.cs | 6 + .../EmulatorConfig/CfgShortCut.cs.meta | 11 + .../Supporter/EmulatorConfig/CfgSound.cs | 19 + .../Supporter/EmulatorConfig/CfgSound.cs.meta | 11 + .../EmulatorConfig/EmulatorConfig.cs | 20 + .../EmulatorConfig/EmulatorConfig.cs.meta | 11 + .../VirtualNes.Core/Supporter/Supporter.cs | 6 +- .../ProjectSettings/ProjectSettings.asset | 2 +- 101 files changed, 4073 insertions(+), 244 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs rename AxibugEmuOnline.Client/Assets/VirtualNes.Core/{CHEATCODE.cs.meta => Cheat.cs.meta} (100%) create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs.meta rename AxibugEmuOnline.Client/Assets/VirtualNes.Core/{CHEATCODE.cs => Supporter/EmulatorConfig/CfgGeneral.cs} (55%) create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index 57019b9..0c3fb5c 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -62,5 +62,7 @@ namespace AxibugEmuOnline.Client romName = Path.GetFileNameWithoutExtension(romName); File.WriteAllBytes($"{diskFileDirectoryPath}/{romName}.dsv", diskFileContent); } + + public EmulatorConfig Config { get; private set; } = new EmulatorConfig(); } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index f06c119..a22cab9 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -10,8 +10,7 @@ namespace AxibugEmuOnline.Client private void Start() { - - //StartGame("Kirby.nes"); + StartGame("Kirby.nes"); } public void StartGame(string romName) diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs index 429bc4c..1cb299e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/APU.cs @@ -10,6 +10,12 @@ namespace VirtualNes.Core 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]; @@ -17,6 +23,7 @@ namespace VirtualNes.Core 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) { @@ -90,6 +97,81 @@ namespace VirtualNes.Core } 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."); + } + } } public struct QUEUEDATA diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs new file mode 100644 index 0000000..56943b5 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs @@ -0,0 +1,45 @@ +using System; + +namespace VirtualNes.Core +{ + public class APU_FDS : APU_INTERFACE + { + private FDSSOUND fds = new FDSSOUND(); + private FDSSOUND fds_sync = new FDSSOUND(); + + public override void Reset(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Setup(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Write(ushort addr, byte data) + { + throw new System.NotImplementedException(); + } + + public override int Process(int channel) + { + throw new System.NotImplementedException(); + } + + internal void SyncWrite(ushort addr, byte data) + { + WriteSub(addr, data, fds_sync, 1789772.5d); + } + + private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate) + { + throw new NotImplementedException(); + } + + private class FDSSOUND + { + //todo : 实现 + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs.meta new file mode 100644 index 0000000..5580ec2 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5e16912525198924a860e53ab4ef0c81 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs new file mode 100644 index 0000000..e567bb2 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs @@ -0,0 +1,27 @@ +using System; + +namespace VirtualNes.Core +{ + public class APU_FME7 : APU_INTERFACE + { + public override void Reset(float fClock, int nRate) + { + throw new NotImplementedException(); + } + + public override void Setup(float fClock, int nRate) + { + throw new NotImplementedException(); + } + + public override void Write(ushort addr, byte data) + { + throw new NotImplementedException(); + } + + public override int Process(int channel) + { + throw new NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs.meta new file mode 100644 index 0000000..4edc65e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03e0258857a7134438a497aec27ea607 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERFACE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERFACE.cs index 14f7d8b..391406c 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERFACE.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERFACE.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VirtualNes.Core +namespace VirtualNes.Core { public abstract class APU_INTERFACE { + 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); @@ -24,5 +22,15 @@ namespace VirtualNes.Core public virtual int GetStateSize() { return 0; } public virtual void SaveState(byte[] p) { } public virtual void LoadState(byte[] p) { } + + public static int INT2FIX(int x) + { + return x << 16; + } + + public static int FIX2INT(int x) + { + return x >> 16; + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index 033b9f8..5f1fb96 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -1,5 +1,3 @@ -using System; - namespace VirtualNes.Core { public class APU_INTERNAL : APU_INTERFACE @@ -63,8 +61,43 @@ namespace VirtualNes.Core private bool SyncUpdateDPCM(int cycles) { - //TODO : ʵ - return false; + 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() diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs new file mode 100644 index 0000000..0b0d4de --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs @@ -0,0 +1,32 @@ +using System; + +namespace VirtualNes.Core +{ + public class APU_MMC5 : APU_INTERFACE + { + public override void Reset(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Setup(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Write(ushort addr, byte data) + { + throw new System.NotImplementedException(); + } + + public override int Process(int channel) + { + throw new System.NotImplementedException(); + } + + internal void SyncWrite(ushort addr, byte data) + { + throw new NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs.meta new file mode 100644 index 0000000..250e590 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 15b983a12234c3c47baefb9fa2751351 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs new file mode 100644 index 0000000..e34a56f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs @@ -0,0 +1,25 @@ +namespace VirtualNes.Core +{ + public class APU_N106 : APU_INTERFACE + { + public override void Reset(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Setup(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Write(ushort addr, byte data) + { + throw new System.NotImplementedException(); + } + + public override int Process(int channel) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs.meta new file mode 100644 index 0000000..692dc26 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a47ed257e942d4478215338d8fe4c35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs new file mode 100644 index 0000000..0c42555 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs @@ -0,0 +1,310 @@ +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; + } + + private class RECTANGLE + { + 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 = default; + gate = default; + volume = default; + + phaseacc = default; + freq = default; + output_vol = default; + + adder = default; + duty_pos = default; + } + } + + private class SAWTOOTH + { + 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 = default; + volume = default; + + phaseacc = default; + freq = default; + output_vol = default; + + adder = default; + accum = default; + phaseaccum = default; + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs.meta new file mode 100644 index 0000000..e9b6f8a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC6.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 190f3271accd30f4eb5b13590417d265 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs new file mode 100644 index 0000000..f15b9d2 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs @@ -0,0 +1,25 @@ +namespace VirtualNes.Core +{ + public class APU_VRC7 : APU_INTERFACE + { + public override void Reset(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Setup(float fClock, int nRate) + { + throw new System.NotImplementedException(); + } + + public override void Write(ushort addr, byte data) + { + throw new System.NotImplementedException(); + } + + public override int Process(int channel) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs.meta new file mode 100644 index 0000000..15e1c81 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 180a87918f9d49e4fad978014f1d594f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs index 1f48eb1..54246a2 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/DPCM.cs @@ -1,11 +1,4 @@ -using Codice.CM.Client.Differences; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VirtualNes.Core +namespace VirtualNes.Core { public class DPCM { diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs index 709483d..8b2dc0e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/NOISE.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VirtualNes.Core +namespace VirtualNes.Core { public class NOISE { diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs index 69abd8f..8d3688e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/RECTANGLE.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace VirtualNes.Core { @@ -47,5 +43,43 @@ namespace VirtualNes.Core public byte sync_holdnote; public byte dummy2; public int sync_len_count; + + public void ZeroMemory() + { + Array.Clear(reg, 0, reg.Length); + enable = 0; + holdnote = 0; + volume = 0; + complement = 0; + + phaseacc = 0; + freq = 0; + freqlimit = 0; + adder = 0; + duty = 0; + len_count = 0; + + nowvolume = 0; + + env_fixed = 0; + env_decay = 0; + env_count = 0; + dummy0 = 0; + env_vol = 0; + + swp_on = 0; + swp_inc = 0; + swp_shift = 0; + swp_decay = 0; + swp_count = 0; + Array.Clear(dummy1, 0, dummy1.Length); + + Array.Clear(sync_reg, 0, sync_reg.Length); + sync_output_enable = 0; + sync_enable = 0; + sync_holdnote = 0; + dummy2 = 0; + sync_len_count = 0; + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs index 0fca7d8..2b5fad9 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CPU.cs @@ -1,7 +1,6 @@ #undef DPCM_SYNCCLOCK using System; -using UnityEngine.UI; namespace VirtualNes.Core { @@ -45,6 +44,7 @@ namespace VirtualNes.Core private APU apu; private R6502 R = new R6502(); private byte[] ZN_Table = new byte[256]; + private Memory STACK; public CPU(NES parent) { @@ -55,17 +55,24 @@ namespace VirtualNes.Core 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; - int exec_cycles = 0; byte nmi_request = 0, irq_request = 0; + bool bClockProcess = m_bClockProcess; - ushort EA = 0; - ushort ET = 0; - ushort WT = 0; - byte DT = 0; + exec_cycles = 0; + EA = 0; + ET = 0; + WT = 0; + DT = 0; while (request_cycles > 0) { @@ -126,81 +133,1008 @@ namespace VirtualNes.Core switch (opcode) { case 0x69: - MR_IM(ref DT); ADC(ref WT, ref DT); - ADD_CYCLE(2, ref exec_cycles); + MR_IM(); ADC(); + ADD_CYCLE(2); break; case 0x65: - MR_ZP(ref EA, ref DT); ADC(ref WT, ref DT); - ADD_CYCLE(3, ref exec_cycles); + MR_ZP(); ADC(); + ADD_CYCLE(3); break; case 0x75: - MR_ZX(ref DT, ref EA); ADC(ref WT, ref DT); - ADD_CYCLE(4, ref exec_cycles); + MR_ZX(); ADC(); + ADD_CYCLE(4); break; case 0x6D: - MR_AB(ref EA, ref DT); ADC(ref WT, ref DT); - ADD_CYCLE(4, ref exec_cycles); + MR_AB(); ADC(); + ADD_CYCLE(4); break; case 0x7D: - MR_AX(ref ET, ref EA, ref DT); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(4, ref exec_cycles); + MR_AX(); ADC(); CHECK_EA(); + ADD_CYCLE(4); break; case 0x79: - MR_AY(ref ET, ref EA, ref DT); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(4, ref exec_cycles); + MR_AY(); ADC(); CHECK_EA(); + ADD_CYCLE(4); break; case 0x61: - MR_IX(ref DT, ref EA); ADC(ref WT, ref DT); - ADD_CYCLE(6, ref exec_cycles); + MR_IX(); ADC(); + ADD_CYCLE(6); break; case 0x71: - MR_IY(ref DT, ref ET, ref EA); ADC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(4, ref exec_cycles); + MR_IY(); ADC(); CHECK_EA(); + ADD_CYCLE(4); break; case 0xE9: - MR_IM(ref DT); SBC(ref WT, ref DT); - ADD_CYCLE(2, ref exec_cycles); + MR_IM(); SBC(); + ADD_CYCLE(2); break; case 0xE5: - MR_ZP(ref EA, ref DT); SBC(ref WT, ref DT); - ADD_CYCLE(3, ref exec_cycles); + MR_ZP(); SBC(); + ADD_CYCLE(3); break; case 0xF5: - MR_ZX(ref DT, ref EA); SBC(ref WT, ref DT); - ADD_CYCLE(4, ref exec_cycles); + MR_ZX(); SBC(); + ADD_CYCLE(4); break; case 0xED: - MR_AB(ref EA, ref DT); SBC(ref WT, ref DT); - ADD_CYCLE(4, ref exec_cycles); + MR_AB(); SBC(); + ADD_CYCLE(4); break; case 0xFD: - MR_AX(ref ET, ref EA, ref DT); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(4, ref exec_cycles); + MR_AX(); SBC(); CHECK_EA(); + ADD_CYCLE(4); break; - case 0xF9: // SBC $????,Y - MR_AY(ref ET, ref EA, ref DT); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(4, ref exec_cycles); + case 0xF9: // SBC $????Y + MR_AY(); SBC(); CHECK_EA(); + ADD_CYCLE(4); break; - case 0xE1: // SBC ($??,X) - MR_IX(ref DT, ref EA); SBC(ref WT, ref DT); - ADD_CYCLE(6, ref exec_cycles); + case 0xE1: // SBC ($??X) + MR_IX(); SBC(); + ADD_CYCLE(6); break; - case 0xF1: // SBC ($??),Y - MR_IY(ref DT, ref ET, ref EA); SBC(ref WT, ref DT); CHECK_EA(ref EA, ref ET, ref exec_cycles); - ADD_CYCLE(5, ref exec_cycles); + case 0xF1: // SBC ($??)Y + MR_IY(); SBC(); CHECK_EA(); + ADD_CYCLE(5); break; case 0xC6: // DEC $?? - MR_ZP(ref EA, ref DT); DEC(ref DT); MW_ZP(EA, DT); - ADD_CYCLE(5, ref exec_cycles); + MR_ZP(); DEC(); MW_ZP(); + ADD_CYCLE(5); break; - case 0xD6: // DEC $??,X - MR_ZX(ref DT, ref EA); DEC(ref DT); MW_ZP(EA, DT); - ADD_CYCLE(6, ref exec_cycles); + case 0xD6: // DEC $??X + MR_ZX(); DEC(); MW_ZP(); + ADD_CYCLE(6); break; case 0xCE: // DEC $???? - MR_AB(ref EA, ref DT); DEC(ref DT); MW_EA(EA, DT); - ADD_CYCLE(6, ref exec_cycles); + 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("Illegal Opcode"); + } + else + { + R.PC--; + ADD_CYCLE(4); + } + break; + } + + 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: @@ -210,6 +1144,571 @@ namespace VirtualNes.Core 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].Span; + 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.Span[(++R.S) & 0xFF]; + } + + private void PUSH(byte V) + { + STACK.Span[(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; @@ -217,14 +1716,17 @@ namespace VirtualNes.Core internal byte OP6502(ushort addr) { - return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; } + private byte[] shortTemp = new byte[2]; internal ushort OP6502W(ushort addr) { var bytePage = MMU.CPU_MEM_BANK[addr >> 13]; - - return BitConverter.ToUInt16(bytePage, addr & 0x1FFF); + var spanByte = bytePage.Span; + shortTemp[0] = spanByte[addr & 0x1FFF]; + shortTemp[1] = spanByte[(addr & 0x1FFF) + 1]; + return BitConverter.ToUInt16(shortTemp, 0); } internal byte RD6502(ushort addr) @@ -242,19 +1744,32 @@ namespace VirtualNes.Core else { // Dummy access - mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]); + mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]); } // Quick bank read - return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; } - private void MR_IM(ref byte DT) + private void AND() + { + R.A &= DT; + SET_ZN_FLAG(R.A); + } + + private void MR_IM() { DT = OP6502(R.PC++); } - private void MR_ZP(ref ushort EA, ref byte DT) + 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); @@ -273,7 +1788,7 @@ namespace VirtualNes.Core return (ushort)(ram1 + ram2); } - private void ADC(ref ushort WT, ref byte DT) + private void ADC() { WT = (ushort)(R.A + DT + (R.P & C_FLAG)); TST_FLAG(WT > 0xFF, C_FLAG); @@ -298,26 +1813,26 @@ namespace VirtualNes.Core R.P |= ZN_Table[A]; } - private void ADD_CYCLE(int V, ref int exec_cycles) + private void ADD_CYCLE(int V) { exec_cycles += V; } - private void MR_ZX(ref byte DT, ref ushort EA) + private void MR_ZX() { DT = OP6502(R.PC++); EA = (ushort)(DT + R.X); DT = ZPRD(EA); } - private void MR_AB(ref ushort EA, ref byte DT) + private void MR_AB() { EA = OP6502W(R.PC); R.PC += 2; DT = RD6502(EA); } - private void MR_AX(ref ushort ET, ref ushort EA, ref byte DT) + private void MR_AX() { ET = OP6502W(R.PC); R.PC += 2; @@ -325,12 +1840,12 @@ namespace VirtualNes.Core DT = RD6502(EA); } - private void CHECK_EA(ref ushort EA, ref ushort ET, ref int exec_cycles) + private void CHECK_EA() { - if ((ET & 0xFF00) != (EA & 0xFF00)) ADD_CYCLE(1, ref exec_cycles); + if ((ET & 0xFF00) != (EA & 0xFF00)) ADD_CYCLE(1); } - private void MR_AY(ref ushort ET, ref ushort EA, ref byte DT) + private void MR_AY() { ET = OP6502W(R.PC); R.PC += 2; @@ -338,22 +1853,35 @@ namespace VirtualNes.Core DT = RD6502(EA); } - private void MR_IX(ref byte DT, ref ushort EA) + private void MR_IX() { DT = OP6502(R.PC++); EA = ZPRDW(DT + R.X); DT = RD6502(EA); } - private void MR_IY(ref byte DT, ref ushort ET, ref ushort 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 SBC(ref ushort WT, ref byte DT) + 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; @@ -363,13 +1891,43 @@ namespace VirtualNes.Core SET_ZN_FLAG(R.A); } - private void DEC(ref byte DT) + private void DEC() { DT--; SET_ZN_FLAG(DT); } - private void MW_ZP(ushort EA, byte 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); } @@ -379,7 +1937,7 @@ namespace VirtualNes.Core MMU.RAM[a] = v; } - private void MW_EA(ushort EA, byte DT) + private void MW_EA() { WR6502(EA, DT); } @@ -424,6 +1982,37 @@ namespace VirtualNes.Core { 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 Memory(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; + } } public enum StatusFlag6502 : int diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs new file mode 100644 index 0000000..f1d4766 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs @@ -0,0 +1,20 @@ +namespace VirtualNes.Core +{ + public class CHEATCODE + { + 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CHEATCODE.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs.meta similarity index 100% rename from AxibugEmuOnline.Client/Assets/VirtualNes.Core/CHEATCODE.cs.meta rename to AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/CRC.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/CRC.cs index 4275dcf..c6dfd65 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/CRC.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/CRC.cs @@ -1,22 +1,17 @@ -using Codice.CM.Client.Differences; using System; -using System.Collections; -using System.Collections.Generic; -using Unity.VisualScripting.Antlr3.Runtime.Tree; -using UnityEngine; namespace VirtualNes.Core { public static class CRC { const int CHAR_BIT = 8; - const ulong CRCPOLY1 = 0x04C11DB7UL; - const ulong CRCPOLY2 = 0xEDB88320UL; + const uint CRCPOLY1 = 0x04C11DB7U; + const uint CRCPOLY2 = 0xEDB88320U; static bool m_Init; static bool m_InitRev; - static ulong[] m_CrcTable = new ulong[byte.MaxValue + 1]; - static ulong[] m_CrcTableRev = new ulong[byte.MaxValue + 1]; + 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) { @@ -35,7 +30,7 @@ namespace VirtualNes.Core } return ~r & 0xFFFFFFFFUL; } - public static ulong CrcRev(int size, Span c) + public static uint CrcRev(int size, Span c) { if (!m_InitRev) { @@ -43,41 +38,41 @@ namespace VirtualNes.Core m_InitRev = true; } - ulong r = 0xFFFFFFFFUL; + uint r = 0xFFFFFFFFU; int step = 0; while (--size >= 0) { r = (r >> CHAR_BIT) ^ m_CrcTableRev[(byte)r ^ c[step]]; step++; } - return r ^ 0xFFFFFFFFUL; + return r ^ 0xFFFFFFFFU; } static void MakeTable() { int i, j; - ulong r; + uint r; for (i = 0; i <= byte.MaxValue; i++) { - r = (ulong)i << (32 - CHAR_BIT); + 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 & 0xFFFFFFFFUL; + m_CrcTable[i] = r & 0xFFFFFFFFU; } } static void MakeTableRev() { int i, j; - ulong r; + uint r; for (i = 0; i <= byte.MaxValue; i++) { - r = (ulong)i; + r = (uint)i; for (j = 0; j < CHAR_BIT; j++) { if ((r & 1) > 0) r = (r >> 1) ^ CRCPOLY2; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs new file mode 100644 index 0000000..964fa3b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs @@ -0,0 +1,145 @@ +using System; + +namespace VirtualNes.Core +{ + public class OPLL_PATCH + { + public uint TL, FB, EG, ML, AR, DR, SL, RR, KR, KL, AM, PM, 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 UInt32 plfo_pm; + public UInt32 plfo_am; + } + + 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; + } + + public static class Emu2413API + { + public static void OPLL_init(UInt32 c, UInt32 r) + { + makePmTable(); + makeAmTable(); + makeDB2LinTable(); + makeAdjustTable(); + makeTllTable(); + makeRksTable(); + makeSinTable(); + makeDefaultPatch(); + OPLL_setClock(c, r); + } + + private static void OPLL_setClock(uint c, uint r) + { + throw new NotImplementedException(); + } + + private static void makeDefaultPatch() + { + throw new NotImplementedException(); + } + + private static void makeSinTable() + { + throw new NotImplementedException(); + } + + private static void makeRksTable() + { + throw new NotImplementedException(); + } + + private static void makeTllTable() + { + throw new NotImplementedException(); + } + + private static void makeAdjustTable() + { + throw new NotImplementedException(); + } + + private static void makeDB2LinTable() + { + throw new NotImplementedException(); + } + + private static void makeAmTable() + { + throw new NotImplementedException(); + } + + private static void makePmTable() + { + throw new NotImplementedException(); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs.meta new file mode 100644 index 0000000..c25181d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/Emu2413.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f5ecddbb6b69204478d799a484d8c47c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs index 8bdaf21..ae8adf4 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/EnumRenderMethod.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace VirtualNes.Core +namespace VirtualNes.Core { // 昤夋曽幃 public enum EnumRenderMethod diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs new file mode 100644 index 0000000..71a82ca --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs @@ -0,0 +1,17 @@ +using System.Runtime.CompilerServices; + +namespace VirtualNes.Core +{ + public static class MemoryUtility + { + public static void ZEROMEMORY(byte[] array, uint length) + { + memset(array, 0, length); + } + + public static void memset(byte[] array, byte value, uint length) + { + Unsafe.InitBlock(ref array[0], value, length); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs.meta new file mode 100644 index 0000000..a9afc7a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8586eb710dc81124593eb5adfa08d73b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs index b4166fb..b23149f 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/NesConfig.cs @@ -1,11 +1,11 @@ namespace VirtualNes.Core { - public struct NesConfig + 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 TotalScanlines; // NTSC: 262 PAL: 312 public int ScanlineCycles; // NTSC:1364 PAL:1362 @@ -19,40 +19,33 @@ public int FrameRate; // NTSC:60(59.94) PAL:50 public float FramePeriod; // NTSC:16.683 PAL:20.0 - public static NesConfig GetNTSC() + public static NesConfig NESCONFIG_NTSC = new NesConfig { - return 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 GetPAL() + 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 { - return 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 - }; - } + 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/ROMClasses.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/ROMClasses.cs index e316501..bf81c40 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/ROMClasses.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/ROMClasses.cs @@ -1,7 +1,4 @@ using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; namespace VirtualNes.Core { @@ -27,7 +24,7 @@ namespace VirtualNes.Core NSF } - public struct NSFHEADER + public class NSFHEADER { byte[] ID; byte Version; @@ -43,7 +40,7 @@ namespace VirtualNes.Core byte[] BankSwitch; ushort SpeedPAL; byte NTSC_PALbits; - byte ExtraChipSelect; + public byte ExtraChipSelect; byte[] Expansion; // must be 0 @@ -65,7 +62,7 @@ namespace VirtualNes.Core } } - public struct NESHEADER + public class NESHEADER { public byte[] ID; public byte PRG_PAGE_SIZE; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/RomPatch.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/RomPatch.cs index ed4d453..2b309cd 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/RomPatch.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/RomPatch.cs @@ -1,13 +1,8 @@ -using System.Collections; -using System.Collections.Generic; -using System.Runtime.Remoting.Messaging; -using UnityEngine; - namespace VirtualNes.Core { public static class RomPatch { - public static void DoPatch(ref ulong crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header) + public static void DoPatch(ref uint crc, ref byte[] lpPRG, ref byte[] lpCHR, ref int mapper, ref NESHEADER header) { // Mapper 000 if (crc == 0x57970078) diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs index 3bbca28..2bbf1f0 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/State.cs @@ -1,9 +1,4 @@ -using Codice.CM.Client.Differences; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; namespace VirtualNes.Core { diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs index b8cf455..b3d409f 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs @@ -1,26 +1,22 @@ -using Codice.CM.Client.Differences; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; namespace VirtualNes { public static class MMU { // CPU 儊儌儕僶儞僋 - public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵 - + public static Memory[] CPU_MEM_BANK = new Memory[8]; // 8K扨埵 + public static byte[] CPU_MEM_TYPE = new byte[8]; + public static int[] CPU_MEM_PAGE = new int[8]; // 僗僥乕僩僙乕僽梡 // PPU 儊儌儕僶儞僋 - public static byte[][] PPU_MEM_BANK = new byte[12][]; // 1K扨埵 + public static Memory[] PPU_MEM_BANK = new Memory[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[] WARM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾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 @@ -42,6 +38,17 @@ namespace VirtualNes 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; @@ -60,5 +67,170 @@ namespace VirtualNes public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕 public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕 + internal static void SetPROM_Bank(byte page, Memory ptr, byte type) + { + CPU_MEM_BANK[page] = ptr; + CPU_MEM_TYPE[page] = type; + CPU_MEM_PAGE[page] = 0; + } + + internal static void SetPROM_8K_Bank(byte page, int bank) + { + bank %= PROM_8K_SIZE; + CPU_MEM_BANK[page] = new Memory(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, Memory 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 Memory(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 Memory(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 Memory(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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs index 132b313..75c6b55 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs @@ -1,9 +1,4 @@ -using Codice.CM.Client.Differences; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; namespace VirtualNes.Core { @@ -30,7 +25,7 @@ namespace VirtualNes.Core // $6000-$7FFF WRAM if (addr >= 0x6000 && addr <= 0x7FFF) { - return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; } return (byte)(addr >> 8); @@ -39,7 +34,7 @@ namespace VirtualNes.Core { if (addr >= 0x6000 && addr <= 0x7FFF) { - MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data; + MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = data; } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs index b1a30f9..118698e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs @@ -1,4 +1,3 @@ -using Codice.CM.Client.Differences; using System; using System.Collections.Generic; using System.IO; @@ -18,10 +17,14 @@ namespace VirtualNes.Core public NesConfig nescfg; private List m_CheatCode = new List(); + private List m_GenieCode = new List(); private bool m_bDiskThrottle; private int m_CommandRequest; 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; @@ -96,7 +99,7 @@ namespace VirtualNes.Core bVideoMode = false; - nescfg = NesConfig.GetNTSC(); + nescfg = NesConfig.NESCONFIG_NTSC; CheatInitial(); @@ -203,18 +206,203 @@ namespace VirtualNes.Core SaveDISK(); SaveTurboFile(); - //todo : ʵ + // RAM Clear + MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length); + if (rom.GetPROM_CRC() == 0x29401686) + { // Minna no Taabou no Nakayoshi Dai Sakusen(J) + MemoryUtility.memset(MMU.RAM, 0xFF, (uint)MMU.RAM.Length); + } + + // RAM set + if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20) + { + MemoryUtility.memset(MMU.WRAM, 0xFF, (uint)MMU.WRAM.Length); + } + + MemoryUtility.ZEROMEMORY(MMU.CRAM, (uint)MMU.CRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.VRAM, (uint)MMU.VRAM.Length); + + MemoryUtility.ZEROMEMORY(MMU.SPRAM, (uint)MMU.SPRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.BGPAL, (uint)MMU.BGPAL.Length); + MemoryUtility.ZEROMEMORY(MMU.SPPAL, (uint)MMU.SPPAL.Length); + + MemoryUtility.ZEROMEMORY(MMU.CPUREG, (uint)MMU.CPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.PPUREG, (uint)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 SetRenderMethod(EnumRenderMethod type) + { + RenderMethod = type; } internal void SoftReset() { - //todo : ʵ + pad.Reset(); + cpu.Reset(); + apu.Reset(); + + if (rom.IsNSF()) + { + mapper.Reset(); + } + + m_bDiskThrottle = false; + + base_cycles = emul_cycles = 0; } internal void EmulateNSF() { - //todo : ʵNSFģ - throw new NotImplementedException("EmulateNSF"); + R6502 reg = null; + + ppu.Reset(); + mapper.VSync(); + + //DEBUGOUT( "Frame\n" ); + + if (m_bNsfPlaying) + { + if (m_bNsfInit) + { + MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)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() @@ -239,7 +427,7 @@ namespace VirtualNes.Core for (i = 0; i < SAVERAM_SIZE; i++) { - if (MMU.WARM[i] != 0x00) + if (MMU.WRAM[i] != 0x00) break; } @@ -249,7 +437,7 @@ namespace VirtualNes.Core Debuger.Log($"Saving SAVERAM...[{romName}]"); - Supporter.SaveSRAMToFile(MMU.WARM, romName); + Supporter.SaveSRAMToFile(MMU.WRAM, romName); } } @@ -382,7 +570,7 @@ namespace VirtualNes.Core case 0x05: // $A000-$BFFF case 0x06: // $C000-$DFFF case 0x07: // $E000-$FFFF - return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]; + return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; } return 0x00; // Warning\h @@ -508,23 +696,47 @@ namespace VirtualNes.Core } else { - mapper->WriteLow(addr, data); + mapper.WriteLow(addr, data); } break; case 0x03: // $6000-$7FFF - mapper->WriteLow(addr, data); + 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); + 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].Span[addr & 0x1FFF] == m_GenieCode[i].cmp) + { + MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = m_GenieCode[i].data; + } + } + else + { + // 6character codes + addr |= 0x8000; + MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = m_GenieCode[i].data; + } + } + } + private void WriteReg(ushort addr, byte data) { switch (addr & 0xFF) @@ -561,20 +773,20 @@ namespace VirtualNes.Core case 0x16: mapper.ExWrite(addr, data); // For VS-Unisystem pad.Write(addr, data); - CPUREG[addr & 0xFF] = data; + MMU.CPUREG[addr & 0xFF] = data; m_TapeIn = data; break; case 0x17: - CPUREG[addr & 0xFF] = data; - pad->Write(addr, data); - apu->Write(addr, data); + MMU.CPUREG[addr & 0xFF] = data; + pad.Write(addr, data); + apu.Write(addr, data); break; // VirtuaNESŗL|[g case 0x18: - apu->Write(addr, data); + apu.Write(addr, data); break; default: - mapper->ExWrite(addr, data); + mapper.ExWrite(addr, data); break; } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index 7dba9d2..46aac06 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -1,4 +1,5 @@ -using System; +using Codice.CM.Client.Differences; +using System; namespace VirtualNes.Core { @@ -17,6 +18,10 @@ namespace VirtualNes.Core 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; @@ -79,6 +84,326 @@ namespace VirtualNes.Core 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); + } + } + + private 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 enum VSType @@ -92,4 +417,31 @@ namespace VirtualNes.Core 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index 4f608b3..f5b64b3 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -95,15 +95,26 @@ namespace VirtualNes.Core 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 byte[] lpScreen; + /// 作为lpScreen数组的索引 + private int lpScanline; + private int ScanlineNo; private byte[] lpColormode; + private bool bVSMode; private int nVSColorMap; private byte VSSecurityData; private byte[] Bit2Rev = new byte[256]; - private int ScanlineNo; - /// 作为lpScreen数组的索引 - private int lpScanline; + public PPU(NES nes) { @@ -128,9 +139,7 @@ namespace VirtualNes.Core } } - public void Dispose() - { - } + public void Dispose() { } internal byte Read(ushort addr) { @@ -178,7 +187,7 @@ namespace VirtualNes.Core } addr &= 0xEFFF; } - MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10][addr & 0x03FF]; + MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10].Span[addr & 0x03FF]; break; } @@ -314,7 +323,7 @@ namespace VirtualNes.Core } if (MMU.PPU_MEM_TYPE[vaddr >> 10] != MMU.BANKTYPE_VROM) { - MMU.PPU_MEM_BANK[vaddr >> 10][vaddr & 0x03FF] = data; + MMU.PPU_MEM_BANK[vaddr >> 10].Span[vaddr & 0x03FF] = data; } break; } @@ -330,6 +339,30 @@ namespace VirtualNes.Core } } + 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, 0x3F, (int)(Screen.SCREEN_WIDTH) * (int)(Screen.SCREEN_HEIGHT)); + if (lpColormode != null) + MemoryUtility.memset(lpColormode, 0, (int)(Screen.SCREEN_HEIGHT)); + } + private enum Screen { SCREEN_WIDTH = 256 + 16, diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs index 56e5f6f..44105d1 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - namespace VirtualNes.Core { public class EXPAD @@ -23,7 +18,7 @@ namespace VirtualNes.Core 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 void SetSyncData(int type, int data) { } public virtual int GetSyncData(int type) { return 0x00; } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs new file mode 100644 index 0000000..5c8c274 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs.meta new file mode 100644 index 0000000..e3a9670 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_CrazyClimber.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d7ac655210edb74ea198ad8802cb545 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs new file mode 100644 index 0000000..9a4d47e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs.meta new file mode 100644 index 0000000..8e05b0c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_ExcitingBoxing.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2ac6f1c51a9c1b64e970bc5fe5e00b9a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs new file mode 100644 index 0000000..d7ea8d6 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs.meta new file mode 100644 index 0000000..7e30479 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_FamlyTrainer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5a2cfbbbe2b90bd4ba754bc9a8f014af +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs new file mode 100644 index 0000000..f16d93f --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs.meta new file mode 100644 index 0000000..50334ac --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Gyromite.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c49edbe29e34f0245b621a7920d4981e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs new file mode 100644 index 0000000..e854ad6 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs.meta new file mode 100644 index 0000000..cded73b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_HyperShot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ac43bf6e282394c46b8ea717595d8664 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs new file mode 100644 index 0000000..4ade50a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs.meta new file mode 100644 index 0000000..c894781 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Keyboard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1f56b9a9d01afad4b967b5d606f7ef11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs new file mode 100644 index 0000000..765c262 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs.meta new file mode 100644 index 0000000..b1541a1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Mahjang.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 146c88b5d0fab5b43ba7e570f0ff116b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs new file mode 100644 index 0000000..679ce77 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs.meta new file mode 100644 index 0000000..bdc8d51 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_OekakidsTablet.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 47337ac29c001d047a911c028e305b43 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs new file mode 100644 index 0000000..a35efbe --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs.meta new file mode 100644 index 0000000..ab492a0 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Paddle.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e48f82e9b202e64fa7ebe3816804ae9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs new file mode 100644 index 0000000..2ee29b4 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs.meta new file mode 100644 index 0000000..a0bd98c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_SpaceShadowGun.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 01ac1bc69454b414cb5a9db1d12f1aac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs new file mode 100644 index 0000000..633ab77 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs.meta new file mode 100644 index 0000000..0060455 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Supor_Keyboard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5adc370589ec4bf42b7ea0c20acb2a48 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs new file mode 100644 index 0000000..58b6fd8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs.meta new file mode 100644 index 0000000..8ec93a3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Toprider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0b1053511d6f224a840b0e9df4f9889 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs new file mode 100644 index 0000000..283a583 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs.meta new file mode 100644 index 0000000..ac3ba2e --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_TurboFile.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b6b41e81a80079489f60eaa5be220cd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs new file mode 100644 index 0000000..1ba2c98 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs.meta new file mode 100644 index 0000000..5f0b371 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSUnisystem.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d343a07cca05ea642be6db80607ea23a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs new file mode 100644 index 0000000..9058c4a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs.meta new file mode 100644 index 0000000..34798b6 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_VSZapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6803ff5b7be6edc49ba2b71256aa16a3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs new file mode 100644 index 0000000..5dd97fb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VirtualNes.Core +{ + /// + /// 光枪 + /// + public class EXPAD_Zapper : EXPAD + { + public EXPAD_Zapper(NES parent) : base(parent) + { + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs.meta new file mode 100644 index 0000000..75db078 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PadEX/EXPAD_Zapper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c378fd8c53bb8084f979b05d2405bb9c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs index 46e12ff..51b6a87 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs @@ -1,9 +1,6 @@ -using Codice.CM.Client.Differences; -using System; +using System; using System.IO; -using System.Linq; using VirtualNes.Core.Debug; -using static UnityEditor.PlayerSettings; namespace VirtualNes.Core { @@ -22,13 +19,13 @@ namespace VirtualNes.Core protected byte[] lpTrainer; protected byte[] lpDiskBios; protected byte[] lpDisk; - protected ulong crc; - protected ulong crcall; - protected ulong crcvrom; + protected uint crc; + protected uint crcall; + protected uint crcvrom; protected int mapper; protected int diskno; - protected ulong fdsmakerID; - protected ulong fdsgameID; + protected uint fdsmakerID; + protected uint fdsgameID; public ROM(string fname) { @@ -256,7 +253,7 @@ namespace VirtualNes.Core crc = crcall = crcvrom = 0; fdsmakerID = lpPRG[0x1F]; - fdsgameID = (ulong)((lpPRG[0x20] << 24) | (lpPRG[0x21] << 16) | (lpPRG[0x22] << 8) | (lpPRG[0x23] << 0)); + fdsgameID = (uint)((lpPRG[0x20] << 24) | (lpPRG[0x21] << 16) | (lpPRG[0x22] << 8) | (lpPRG[0x23] << 0)); } } else //NSF @@ -301,6 +298,10 @@ namespace VirtualNes.Core { return bNSF; } + public bool IsPAL() + { + return bPAL; + } public bool IsSAVERAM() { @@ -331,6 +332,11 @@ namespace VirtualNes.Core return lpPRG; } + internal byte[] GetVROM() + { + return lpCHR; + } + internal byte[] GetDISK() { return lpDisk; @@ -355,6 +361,41 @@ namespace VirtualNes.Core { return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0; } + + internal uint GetPROM_CRC() + { + return crc; + } + + 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; + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig.meta new file mode 100644 index 0000000..8a060f7 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fd0714f580724604da063207fe69e274 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs new file mode 100644 index 0000000..9650980 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgController + { + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs.meta new file mode 100644 index 0000000..750d782 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3328948cdc73baa4fb90c995f39f454f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs new file mode 100644 index 0000000..ac0f43d --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs.meta new file mode 100644 index 0000000..1a5035c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgEmulator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: aae682cf38878ae40a449ab1a13fc7fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs new file mode 100644 index 0000000..9698aee --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs.meta new file mode 100644 index 0000000..3a37df9 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgExtraSound.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c707e4c8c38fa2e40a884047d0582b7e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CHEATCODE.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs similarity index 55% rename from AxibugEmuOnline.Client/Assets/VirtualNes.Core/CHEATCODE.cs rename to AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs index aaf5b92..ec6eb1d 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CHEATCODE.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs @@ -1,6 +1,7 @@ namespace VirtualNes.Core { - public class CHEATCODE + public class CfgGeneral { + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs.meta new file mode 100644 index 0000000..84efe80 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGeneral.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 797171ffbac5b8748923ba914141781d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs new file mode 100644 index 0000000..21851f8 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgGraphics + { + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs.meta new file mode 100644 index 0000000..73cbd53 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c5d72263cee06c74e94d67af00befbdf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs new file mode 100644 index 0000000..9ad0ca3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs.meta new file mode 100644 index 0000000..71f37e3 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLanguage.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c23af0b4dd8a4e04a89d1a380c54688f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs new file mode 100644 index 0000000..cf1e5eb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs.meta new file mode 100644 index 0000000..e752de0 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgLauncher.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8a033c816849b204c8cda167cd1fddb0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs new file mode 100644 index 0000000..d8d718c --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgMovie + { + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs.meta new file mode 100644 index 0000000..cb49734 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fdbdc9850f494ad44a16ec1ec82157cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs new file mode 100644 index 0000000..d5efce1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs.meta new file mode 100644 index 0000000..f781314 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgNetPlay.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6a81c95a03cbc4f4298e034f2b5cab7c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs new file mode 100644 index 0000000..a89aa6b --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs @@ -0,0 +1,6 @@ +namespace VirtualNes.Core +{ + public class CfgPath + { + } +} \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs.meta new file mode 100644 index 0000000..c9bd97a --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9368c1964e431040a469e2fb7b1c710 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs new file mode 100644 index 0000000..ac133b9 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs.meta new file mode 100644 index 0000000..8af3ee4 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgShortCut.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b4bc2f955a549544e9f03a73b98455fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs new file mode 100644 index 0000000..2db18fb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/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; } = 22050; + 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/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs.meta new file mode 100644 index 0000000..1ac8256 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgSound.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f13850718ff124445ad8696ef491313d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs new file mode 100644 index 0000000..961e389 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs @@ -0,0 +1,20 @@ +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(); + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs.meta new file mode 100644 index 0000000..897ae92 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/EmulatorConfig.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8e0eb25b08646f64eaf7930288788c68 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs index 8f07b85..cc417af 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs @@ -1,7 +1,4 @@ -using System.Collections; -using System.Collections.Generic; using System.IO; -using UnityEngine; namespace VirtualNes.Core { @@ -37,6 +34,8 @@ namespace VirtualNes.Core { s_support.SaveDISKToFile(diskFileContent, romName); } + + public static EmulatorConfig Config => s_support.Config; } public interface ISupporterImpl @@ -46,5 +45,6 @@ namespace VirtualNes.Core Stream OpenFile_DISKSYS(); void SaveSRAMToFile(byte[] sramContent, string romName); void SaveDISKToFile(byte[] diskFileContent, string romName); + EmulatorConfig Config { get; } } } diff --git a/AxibugEmuOnline.Client/ProjectSettings/ProjectSettings.asset b/AxibugEmuOnline.Client/ProjectSettings/ProjectSettings.asset index 8638a73..c3d97d1 100644 --- a/AxibugEmuOnline.Client/ProjectSettings/ProjectSettings.asset +++ b/AxibugEmuOnline.Client/ProjectSettings/ProjectSettings.asset @@ -649,7 +649,7 @@ PlayerSettings: managedStrippingLevel: {} incrementalIl2cppBuild: {} suppressCommonWarnings: 1 - allowUnsafeCode: 0 + allowUnsafeCode: 1 useDeterministicCompilation: 1 useReferenceAssemblies: 1 enableRoslynAnalyzers: 1