From e5fa62d209dad4bb0ed0c16f9835a0a2dc25c79a Mon Sep 17 00:00:00 2001 From: "ALIENJACK\\alien" Date: Tue, 30 Jul 2024 11:57:09 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=AD=E4=BB=A3=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Assets/Scene/EmuTest.unity | 221 ++++- .../Script/NesEmulator/CoreSupporter.cs | 28 + .../Assets/Script/NesEmulator/NesEmulator.cs | 23 +- .../New Render Texture.renderTexture | 38 + .../New Render Texture.renderTexture.meta | 8 + .../Script/NesEmulator/VideoProvider.cs | 25 + .../Script/NesEmulator/VideoProvider.cs.meta | 11 + .../Assets/VirtualNes.Core/ApuEX/APU_FDS.cs | 11 +- .../Assets/VirtualNes.Core/ApuEX/APU_FME7.cs | 9 +- .../VirtualNes.Core/ApuEX/APU_INTERNAL.cs | 9 +- .../Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs | 11 +- .../Assets/VirtualNes.Core/ApuEX/APU_N106.cs | 9 +- .../Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs | 9 +- .../Assets/VirtualNes.Core/Cheat.cs | 16 + .../VirtualNes.Core/CoreLibs/MemoryUtility.cs | 19 +- .../Assets/VirtualNes.Core/MMU.cs | 3 + .../Assets/VirtualNes.Core/Mapper/Mapper.cs | 5 +- .../VirtualNes.Core/Mapper/Mapper004.cs | 576 +++++++++++++ .../VirtualNes.Core/Mapper/Mapper004.cs.meta | 11 + .../Assets/VirtualNes.Core/NES.cs | 770 +++++++++++++++++- .../Assets/VirtualNes.Core/PAD.cs | 36 + .../Assets/VirtualNes.Core/PPU.cs | 750 ++++++++++++++++- .../Assets/VirtualNes.Core/ROM.cs | 7 +- .../Supporter/EmulatorConfig/CfgGraphics.cs | 33 + .../Supporter/EmulatorConfig/CfgMovie.cs | 10 +- .../Supporter/EmulatorConfig/CfgPath.cs | 21 +- .../VirtualNes.Core/Supporter/Supporter.cs | 18 + 27 files changed, 2625 insertions(+), 62 deletions(-) create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture.meta create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs create mode 100644 AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs.meta create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs create mode 100644 AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs.meta diff --git a/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity b/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity index 2d477ba..c3cf300 100644 --- a/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity +++ b/AxibugEmuOnline.Client/Assets/Scene/EmuTest.unity @@ -152,6 +152,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3} m_Name: m_EditorClassIdentifier: + VideoProvider: {fileID: 1295252151} --- !u!4 &149545948 Transform: m_ObjectHideFlags: 0 @@ -162,7 +163,8 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_Children: [] + m_Children: + - {fileID: 1295252150} m_Father: {fileID: 0} m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -259,6 +261,78 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &861747459 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 861747460} + - component: {fileID: 861747462} + - component: {fileID: 861747461} + m_Layer: 5 + m_Name: RawImage + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &861747460 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 861747459} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1727033528} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &861747461 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 861747459} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Texture: {fileID: 8400000, guid: ffe34aaf87e4b9942b4c2ac05943d444, type: 2} + m_UVRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 +--- !u!222 &861747462 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 861747459} + m_CullTransparentMesh: 1 --- !u!1 &1232273651 GameObject: m_ObjectHideFlags: 0 @@ -342,6 +416,51 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1295252149 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1295252150} + - component: {fileID: 1295252151} + m_Layer: 0 + m_Name: VideoProvider + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1295252150 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1295252149} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1727033528} + m_Father: {fileID: 149545948} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1295252151 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1295252149} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 83fbe375412d1af4482ae76e81c1dda2, type: 3} + m_Name: + m_EditorClassIdentifier: + Image: {fileID: 861747461} --- !u!1 &1359344831 GameObject: m_ObjectHideFlags: 0 @@ -408,3 +527,103 @@ Transform: m_Father: {fileID: 0} m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1727033527 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1727033528} + - component: {fileID: 1727033531} + - component: {fileID: 1727033530} + - component: {fileID: 1727033529} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1727033528 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1727033527} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 861747460} + m_Father: {fileID: 1295252150} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &1727033529 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1727033527} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1727033530 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1727033527} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &1727033531 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1727033527} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs index 0c3fb5c..7cb639b 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/CoreSupporter.cs @@ -64,5 +64,33 @@ namespace AxibugEmuOnline.Client } public EmulatorConfig Config { get; private set; } = new EmulatorConfig(); + + public void PrepareDirectory(string directPath) + { + Directory.CreateDirectory($"{Application.persistentDataPath}/{directPath}"); + } + + public void SaveFile(byte[] fileData, string directPath, string fileName) + { + PrepareDirectory(directPath); + + var fileFullpath = $"{Application.persistentDataPath}/{directPath}/{fileName}"; + File.WriteAllBytes(fileFullpath, fileData); + } + + public Stream OpenFile(string directPath, string fileName) + { + try + { + var data = File.ReadAllBytes($"{Application.persistentDataPath}/{directPath}/{fileName}"); + if (data == null) return null; + return new MemoryStream(data); + } + catch + { + return null; + } + + } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs index a22cab9..411c3c0 100644 --- a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/NesEmulator.cs @@ -1,3 +1,4 @@ +using System; using UnityEngine; using VirtualNes.Core; using VirtualNes.Core.Debug; @@ -8,6 +9,8 @@ namespace AxibugEmuOnline.Client { private NES m_nesIns; + public VideoProvider VideoProvider; + private void Start() { StartGame("Kirby.nes"); @@ -19,8 +22,17 @@ namespace AxibugEmuOnline.Client Supporter.Setup(new CoreSupporter()); Debuger.Setup(new CoreDebuger()); - m_nesIns = new NES(romName); - m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET); + + try + { + m_nesIns = new NES(romName); + m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET); + } + catch (Exception ex) + { + m_nesIns = null; + Debug.LogError(ex); + } } public void StopGame() @@ -31,7 +43,12 @@ namespace AxibugEmuOnline.Client private void Update() { - m_nesIns?.EmulateFrame(true); + if (m_nesIns != null) + { + m_nesIns.EmulateFrame(true); + var screenBuffer = m_nesIns.ppu.GetScreenPtr(); + VideoProvider.SetDrawData(screenBuffer, PPU.SCREEN_WIDTH, PPU.SCREEN_HEIGHT); + } } } } diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture new file mode 100644 index 0000000..6721306 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture @@ -0,0 +1,38 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!84 &8400000 +RenderTexture: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: New Render Texture + m_ImageContentsHash: + serializedVersion: 2 + Hash: 00000000000000000000000000000000 + m_ForcedFallbackFormat: 4 + m_DownscaleFallback: 0 + m_IsAlphaChannelOptional: 0 + serializedVersion: 3 + m_Width: 272 + m_Height: 240 + m_AntiAliasing: 1 + m_MipCount: -1 + m_DepthFormat: 2 + m_ColorFormat: 8 + m_MipMap: 0 + m_GenerateMips: 1 + m_SRGB: 0 + m_UseDynamicScale: 0 + m_BindMS: 0 + m_EnableCompatibleFormat: 1 + m_TextureSettings: + serializedVersion: 2 + m_FilterMode: 1 + m_Aniso: 0 + m_MipBias: 0 + m_WrapU: 1 + m_WrapV: 1 + m_WrapW: 1 + m_Dimension: 2 + m_VolumeDepth: 1 diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture.meta b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture.meta new file mode 100644 index 0000000..bfd7ec1 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/New Render Texture.renderTexture.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ffe34aaf87e4b9942b4c2ac05943d444 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 8400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs new file mode 100644 index 0000000..f077271 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +namespace AxibugEmuOnline.Client +{ + public class VideoProvider : MonoBehaviour + { + public RawImage Image; + + private Texture2D wrapTex; + + public void SetDrawData(byte[] data, int width, int height) + { + if (wrapTex == null) wrapTex = new Texture2D(width, height); + + var colors = data.Select(d => new Color((d / 255f), (d / 255f), (d / 255f), 1)).ToArray(); + wrapTex.SetPixels(colors); + wrapTex.Apply(); + Graphics.Blit(wrapTex, Image.texture as RenderTexture); + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs.meta b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs.meta new file mode 100644 index 0000000..a7c1dff --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/Script/NesEmulator/VideoProvider.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 83fbe375412d1af4482ae76e81c1dda2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs index 56943b5..d273fc4 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FDS.cs @@ -9,22 +9,23 @@ namespace VirtualNes.Core public override void Reset(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Setup(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Write(ushort addr, byte data) { - throw new System.NotImplementedException(); + //todo : 实现 } public override int Process(int channel) { - throw new System.NotImplementedException(); + //todo : 实现 + return 0; } internal void SyncWrite(ushort addr, byte data) @@ -34,7 +35,7 @@ namespace VirtualNes.Core private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate) { - throw new NotImplementedException(); + //todo : 实现 } private class FDSSOUND diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs index e567bb2..2871744 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_FME7.cs @@ -6,22 +6,23 @@ namespace VirtualNes.Core { public override void Reset(float fClock, int nRate) { - throw new NotImplementedException(); + //todo : 实现 } public override void Setup(float fClock, int nRate) { - throw new NotImplementedException(); + //todo : 实现 } public override void Write(ushort addr, byte data) { - throw new NotImplementedException(); + //todo : 实现 } public override int Process(int channel) { - throw new NotImplementedException(); + //todo : 实现 + return 0; } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs index 5f1fb96..1b88755 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_INTERNAL.cs @@ -127,22 +127,23 @@ namespace VirtualNes.Core public override void Reset(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : ʵ } public override void Setup(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : ʵ } public override void Write(ushort addr, byte data) { - throw new System.NotImplementedException(); + //todo : ʵ } public override int Process(int channel) { - throw new System.NotImplementedException(); + //todo : ʵ + return 0; } internal byte SyncRead(ushort addr) diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs index 0b0d4de..30ffb1e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_MMC5.cs @@ -6,27 +6,28 @@ namespace VirtualNes.Core { public override void Reset(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Setup(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Write(ushort addr, byte data) { - throw new System.NotImplementedException(); + //todo : 实现 } public override int Process(int channel) { - throw new System.NotImplementedException(); + //todo : 实现 + return 0; } internal void SyncWrite(ushort addr, byte data) { - throw new NotImplementedException(); + //todo : 实现 } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs index e34a56f..2fe943e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_N106.cs @@ -4,22 +4,23 @@ { public override void Reset(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Setup(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Write(ushort addr, byte data) { - throw new System.NotImplementedException(); + //todo : 实现 } public override int Process(int channel) { - throw new System.NotImplementedException(); + //todo : 实现 + return 0; } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs index f15b9d2..448ddd0 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ApuEX/APU_VRC7.cs @@ -4,22 +4,23 @@ { public override void Reset(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Setup(float fClock, int nRate) { - throw new System.NotImplementedException(); + //todo : 实现 } public override void Write(ushort addr, byte data) { - throw new System.NotImplementedException(); + //todo : 实现 } public override int Process(int channel) { - throw new System.NotImplementedException(); + //todo : 实现 + return 0; } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs index f1d4766..5778026 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Cheat.cs @@ -2,6 +2,22 @@ { public class CHEATCODE { + // 埲壓偺俀偮偼OR儅僗僋 + public const int CHEAT_ENABLE = 1 << 0; + public const int CHEAT_KEYDISABLE = 1 << 1; + + // 彂偒崬傒庬椶 + public const int CHEAT_TYPE_ALWAYS = 0; // 忢偵彂偒崬傒 + public const int CHEAT_TYPE_ONCE = 1; // 侾夞偩偗彂偒崬傒 + public const int CHEAT_TYPE_GREATER = 2; // 僨乕僞傛傝戝偒偄帪 + public const int CHEAT_TYPE_LESS = 3; // 僨乕僞傛傝彫偝偄帪 + + // 僨乕僞挿 + public const int CHEAT_LENGTH_1BYTE = 0; + public const int CHEAT_LENGTH_2BYTE = 1; + public const int CHEAT_LENGTH_3BYTE = 2; + public const int CHEAT_LENGTH_4BYTE = 3; + public byte enable; public byte type; public byte length; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs index 71a82ca..b6a45c3 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/CoreLibs/MemoryUtility.cs @@ -1,17 +1,26 @@ -using System.Runtime.CompilerServices; +using System; +using System.Runtime.CompilerServices; namespace VirtualNes.Core { public static class MemoryUtility { - public static void ZEROMEMORY(byte[] array, uint length) + public static void ZEROMEMORY(byte[] array, int length) { - memset(array, 0, length); + Array.Clear(array, 0, array.Length); } - public static void memset(byte[] array, byte value, uint length) + public static void memset(byte[] array, byte value, int length) { - Unsafe.InitBlock(ref array[0], value, length); + memset(array, 0, value, length); + } + + public static void memset(byte[] array, int offset, byte value, int length) + { + for (int i = offset; i < length; i++) + { + array[i] = value; + } } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs index b3d409f..790f5ed 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs @@ -67,6 +67,9 @@ namespace VirtualNes public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕 public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕 + // Frame-IRQ儗僕僗僞($4017) + public static int FrameIRQ; + internal static void SetPROM_Bank(byte page, Memory ptr, byte type) { CPU_MEM_BANK[page] = ptr; diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs index 9dfefae..542201a 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper.cs @@ -5,6 +5,7 @@ namespace VirtualNes.Core public abstract class Mapper { protected NES nes; + public Mapper(NES parent) { nes = parent; @@ -81,8 +82,10 @@ namespace VirtualNes.Core //todo : 实现加载mapper switch (no) { + case 4: + return new Mapper004(parent); default: - throw new NotImplementedException($"Mapper#{no} is not Impl"); + throw new NotImplementedException($"Mapper#{no:000} is not Impl"); } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs new file mode 100644 index 0000000..0106263 --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs @@ -0,0 +1,576 @@ +using Codice.CM.Client.Differences; +using System; + +namespace VirtualNes.Core +{ + public class Mapper004 : Mapper + { + private const int MMC3_IRQ_KLAX = 1; + private const int MMC3_IRQ_SHOUGIMEIKAN = 2; + private const int MMC3_IRQ_DAI2JISUPER = 3; + private const int MMC3_IRQ_DBZ2 = 4; + private const int MMC3_IRQ_ROCKMAN3 = 5; + + protected byte[] reg = new byte[8]; + protected byte prg0, prg1; + protected byte chr01, chr23, chr4, chr5, chr6, chr7; + protected byte we_sram; + + protected byte irq_type; + protected byte irq_enable; + protected byte irq_counter; + protected byte irq_latch; + protected byte irq_request; + protected byte irq_preset; + protected byte irq_preset_vbl; + + protected byte vs_patch; + protected byte vs_index; + + private byte[] VS_TKO_Security = new byte[32] + { + 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, + 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, + 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, + 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 + }; + + public Mapper004(NES parent) : base(parent) { } + + public override void Reset() + { + for (int i = 0; i < 8; i++) + { + reg[i] = 0x00; + } + + prg0 = 0; + prg1 = 1; + SetBank_CPU(); + + chr01 = 0; + chr23 = 2; + chr4 = 4; + chr5 = 5; + chr6 = 6; + chr7 = 7; + SetBank_PPU(); + + we_sram = 0; // Disable + irq_enable = 0; // Disable + irq_counter = 0; + irq_latch = 0xFF; + irq_request = 0; + irq_preset = 0; + irq_preset_vbl = 0; + + // IRQ僞僀僾愝掕 + nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK); + irq_type = 0; + + uint crc = nes.rom.GetPROM_CRC(); + + if (crc == 0x5c707ac4) + { // Mother(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xcb106f49) + { // F-1 Sensation(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x1170392a) + { // Karakuri Kengou Den - Musashi Road - Karakuri Nin Hashiru!(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x14a01c70) + { // Gun-Dec(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xeffeea40) + { // For Klax(J) + irq_type = MMC3_IRQ_KLAX; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xc17ae2dc) + { // God Slayer - Haruka Tenkuu no Sonata(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x126ea4a0) + { // Summer Carnival '92 - Recca(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x1f2f4861) + { // J League Fighting Soccer - The King of Ace Strikers(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x5a6860f1) + { // Shougi Meikan '92(J) + irq_type = MMC3_IRQ_SHOUGIMEIKAN; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xae280e20) + { // Shougi Meikan '93(J) + irq_type = MMC3_IRQ_SHOUGIMEIKAN; + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xe19a2473) + { // Sugoro Quest - Dice no Senshi Tachi(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x702d9b33) + { // Star Wars - The Empire Strikes Back(Victor)(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xa9a0d729) + { // Dai Kaijuu - Deburas(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xc5fea9f2) + { // Dai 2 Ji - Super Robot Taisen(J) + irq_type = MMC3_IRQ_DAI2JISUPER; + } + if (crc == 0xd852c2f7) + { // Time Zone(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0xecfd3c69) + { // Taito Chase H.Q.(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x7a748058) + { // Tom & Jerry (and Tuffy)(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xaafe699c) + { // Ninja Ryukenden 3 - Yomi no Hakobune(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x6cc62c06) + { // Hoshi no Kirby - Yume no Izumi no Monogatari(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x877dba77) + { // My Life My Love - Boku no Yume - Watashi no Negai(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x6f96ed15) + { // Max Warrior - Wakusei Kaigenrei(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x8685f366) + { // Matendouji(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x8635fed1) + { // Mickey Mouse 3 - Yume Fuusen(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x26ff3ea2) + { // Yume Penguin Monogatari(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x7671bc51) + { // Red Ariimaa 2(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0xade11141) + { // Wanpaku Kokkun no Gourmet World(J) + nes.SetIrqType(NES.IRQMETHOD.IRQ_HSYNC); + } + if (crc == 0x7c7ab58e) + { // Walkuere no Bouken - Toki no Kagi Densetsu(J) + nes.SetRenderMethod(EnumRenderMethod.POST_RENDER); + } + if (crc == 0x26ff3ea2) + { // Yume Penguin Monogatari(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + if (crc == 0x126ea4a0) + { // Summer Carnival '92 Recca(J) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0x1d2e5018 // Rockman 3(J) + || crc == 0x6b999aaf) + { // Megaman 3(U) + irq_type = MMC3_IRQ_ROCKMAN3; + } + + if (crc == 0xd88d48d7) + { // Kick Master(U) + irq_type = MMC3_IRQ_ROCKMAN3; + } + + if (crc == 0xA67EA466) + { // Alien 3(U) + nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER); + } + + if (crc == 0xe763891b) + { // DBZ2 + irq_type = MMC3_IRQ_DBZ2; + } + + // VS-Unisystem + vs_patch = 0; + vs_index = 0; + + if (crc == 0xeb2dba63 // VS TKO Boxing + || crc == 0x98cfe016) + { // VS TKO Boxing (Alt) + vs_patch = 1; + } + if (crc == 0x135adf7c) + { // VS Atari RBI Baseball + vs_patch = 2; + } + if (crc == 0xf9d3b0a3 // VS Super Xevious + || crc == 0x9924980a // VS Super Xevious (b1) + || crc == 0x66bb838f) + { // VS Super Xevious (b2) + vs_patch = 3; + } + } + + private void SetBank_PPU() + { + if (MMU.VROM_1K_SIZE != 0) + { + if ((reg[0] & 0x80) != 0) + { + MMU.SetVROM_8K_Bank(chr4, chr5, chr6, chr7, + chr01, chr01 + 1, chr23, chr23 + 1); + } + else + { + MMU.SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1, + chr4, chr5, chr6, chr7); + } + } + else + { + if ((reg[0] & 0x80) != 0) + { + MMU.SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(0, chr4 & 0x07); + MMU.SetCRAM_1K_Bank(1, chr5 & 0x07); + MMU.SetCRAM_1K_Bank(2, chr6 & 0x07); + MMU.SetCRAM_1K_Bank(3, chr7 & 0x07); + } + else + { + MMU.SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07); + MMU.SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07); + MMU.SetCRAM_1K_Bank(4, chr4 & 0x07); + MMU.SetCRAM_1K_Bank(5, chr5 & 0x07); + MMU.SetCRAM_1K_Bank(6, chr6 & 0x07); + MMU.SetCRAM_1K_Bank(7, chr7 & 0x07); + } + } + } + + public override byte ReadLow(ushort addr) + { + if (vs_patch == 0) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + return MMU.XRAM[addr - 0x4000]; + } + } + else if (vs_patch == 1) + { + // VS TKO Boxing Security + if (addr == 0x5E00) + { + vs_index = 0; + return 0x00; + } + else if (addr == 0x5E01) + { + return VS_TKO_Security[(vs_index++) & 0x1F]; + } + } + else if (vs_patch == 2) + { + // VS Atari RBI Baseball Security + if (addr == 0x5E00) + { + vs_index = 0; + return 0x00; + } + else if (addr == 0x5E01) + { + if (vs_index++ == 9) + return 0x6F; + else + return 0xB4; + } + } + else if (vs_patch == 3) + { + // VS Super Xevious + switch (addr) + { + case 0x54FF: + return 0x05; + case 0x5678: + if (vs_index != 0) + return 0x00; + else + return 0x01; + case 0x578f: + if (vs_index != 0) + return 0xD1; + else + return 0x89; + case 0x5567: + if (vs_index != 0) + { + vs_index = 0; + return 0x3E; + } + else + { + vs_index = 1; + return 0x37; + } + default: + break; + } + } + + return base.ReadLow(addr); + } + + public override void WriteLow(ushort addr, byte data) + { + if (addr >= 0x5000 && addr <= 0x5FFF) + { + MMU.XRAM[addr - 0x4000] = data; + } + else + { + base.WriteLow(addr, data); + } + } + + public override void Write(ushort addr, byte data) + { + switch (addr & 0xE001) + { + case 0x8000: + reg[0] = data; + SetBank_CPU(); + SetBank_PPU(); + break; + case 0x8001: + reg[1] = data; + + switch (reg[0] & 0x07) + { + case 0x00: + chr01 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x01: + chr23 = (byte)(data & 0xFE); + SetBank_PPU(); + break; + case 0x02: + chr4 = data; + SetBank_PPU(); + break; + case 0x03: + chr5 = data; + SetBank_PPU(); + break; + case 0x04: + chr6 = data; + SetBank_PPU(); + break; + case 0x05: + chr7 = data; + SetBank_PPU(); + break; + case 0x06: + prg0 = data; + SetBank_CPU(); + break; + case 0x07: + prg1 = data; + SetBank_CPU(); + break; + } + break; + case 0xA000: + reg[2] = data; + if (!nes.rom.Is4SCREEN()) + { + if ((data & 0x01) != 0) MMU.SetVRAM_Mirror(MMU.VRAM_HMIRROR); + else MMU.SetVRAM_Mirror(MMU.VRAM_VMIRROR); + } + break; + case 0xA001: + reg[3] = data; + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() ); + break; + case 0xC000: + //DEBUGOUT( "MPRWR A=%04X D=%02X L=%3d CYC=%d\n", addr&0xFFFF, data&0xFF, nes->GetScanline(), nes->cpu->GetTotalCycles() ); + reg[4] = data; + if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3) + { + irq_counter = data; + } + else + { + irq_latch = data; + } + if (irq_type == MMC3_IRQ_DBZ2) + { + irq_latch = 0x07; + } + break; + case 0xC001: + reg[5] = data; + if (irq_type == MMC3_IRQ_KLAX || irq_type == MMC3_IRQ_ROCKMAN3) + { + irq_latch = data; + } + else + { + if ((nes.GetScanline() < 240) || (irq_type == MMC3_IRQ_SHOUGIMEIKAN)) + { + irq_counter |= 0x80; + irq_preset = 0xFF; + } + else + { + irq_counter |= 0x80; + irq_preset_vbl = 0xFF; + irq_preset = 0; + } + } + break; + case 0xE000: + reg[6] = data; + irq_enable = 0; + irq_request = 0; + + nes.cpu.ClrIRQ(CPU.IRQ_MAPPER); + break; + case 0xE001: + reg[7] = data; + irq_enable = 1; + irq_request = 0; + + // nes->cpu->ClrIRQ( IRQ_MAPPER ); + break; + } + } + + public override void Clock(int cycles) + { + + } + + public override void HSync(int scanline) + { + if (irq_type == MMC3_IRQ_KLAX) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if (irq_counter == 0) + { + irq_counter = irq_latch; + irq_request = 0xFF; + } + if (irq_counter > 0) + { + irq_counter--; + } + } + } + if (irq_request != 0) + { + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + else if (irq_type == MMC3_IRQ_ROCKMAN3) + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_enable != 0) + { + if ((--irq_counter) == 0) + { + irq_request = 0xFF; + irq_counter = irq_latch; + } + } + } + if (irq_request != 0) + { + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + } + else + { + if ((scanline >= 0 && scanline <= 239) && nes.ppu.IsDispON()) + { + if (irq_preset_vbl != 0) + { + irq_counter = irq_latch; + irq_preset_vbl = 0; + } + if (irq_preset != 0) + { + irq_counter = irq_latch; + irq_preset = 0; + if (irq_type == MMC3_IRQ_DAI2JISUPER && scanline == 0) + { + irq_counter--; + } + } + else if (irq_counter > 0) + { + irq_counter--; + } + + if (irq_counter == 0) + { + if (irq_enable != 0) + { + irq_request = 0xFF; + + nes.cpu.SetIRQ(CPU.IRQ_MAPPER); + } + irq_preset = 0xFF; + } + } + } + } + + private void SetBank_CPU() + { + if ((reg[0] & 0x40) != 0) + { + MMU.SetPROM_32K_Bank(MMU.PROM_8K_SIZE - 2, prg1, prg0, MMU.PROM_8K_SIZE - 1); + } + else + { + MMU.SetPROM_32K_Bank(prg0, prg1, MMU.PROM_8K_SIZE - 2, MMU.PROM_8K_SIZE - 1); + } + } + } +} diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs.meta b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs.meta new file mode 100644 index 0000000..cdf97fb --- /dev/null +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Mapper/Mapper004.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1be6fcc00c619e84cbf0d5d92701b2e3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs index 997017e..d6359e2 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/NES.cs @@ -1,8 +1,15 @@ +using Codice.CM.Client.Differences; +using Google.Protobuf.Collections; using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Numerics; using System.Text; +using UnityEngine.UIElements; +using UnityEngine; using VirtualNes.Core.Debug; +using Unity.VisualScripting.Antlr3.Runtime.Tree; namespace VirtualNes.Core { @@ -30,6 +37,9 @@ namespace VirtualNes.Core private bool m_bMoviePlay; private bool m_bMovieRec; private Stream m_fpMovie; + private uint m_MovieControl; + private int m_MovieStepTotal; + private int m_MovieStep; private bool m_bTapePlay; private bool m_bTapeRec; private Stream m_fpTape; @@ -60,9 +70,107 @@ namespace VirtualNes.Core private int NES_scanline; private EnumRenderMethod RenderMethod; private bool bZapper; + private int ZapperX; + private int ZapperY; private long base_cycles; private long emul_cycles; + private byte[] m_PadImg = new byte[226] + { + 28, 8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, + 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, + 0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, + 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + private byte[] m_KeyImg0 = new byte[6] + { + 2, 2, + 0x2A, 0x2A, + 0x2A, 0x2A, + }; + + private byte[] m_KeyImg1 = new byte[8] + { + 3, 3, + 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, + }; + + private byte[] m_KeyImg2 = new byte[18] + { + 4, 4, + 0xFF, 0x2A, 0x2A, 0xFF, + 0x2A, 0x2A, 0x2A, 0x2A, + 0x2A, 0x2A, 0x2A, 0x2A, + 0xFF, 0x2A, 0x2A, 0xFF, + }; + + private byte[] Font6x8 = new byte[768] + { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00, + 0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00, + 0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00,0x48,0xB0,0x50,0x20,0x50,0x68,0x90,0x00, + 0x40,0xA0,0xA8,0x68,0x90,0x90,0x68,0x00,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00, + 0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00, + 0x70,0x88,0x98,0xA8,0xC8,0x88,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0xF8,0x00, + 0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00, + 0x30,0x50,0x90,0x90,0xF8,0x10,0x10,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0xF0,0x00, + 0x70,0x88,0x80,0xF0,0x88,0x88,0x70,0x00,0xF8,0x08,0x10,0x10,0x20,0x20,0x20,0x00, + 0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00, + 0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x40,0x00, + 0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00, + 0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00, + 0x30,0x48,0x88,0x98,0xA8,0xA8,0x78,0x00,0x20,0x50,0x50,0x88,0xF8,0x88,0x88,0x00, + 0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00, + 0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00,0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00, + 0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00, + 0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0xF8,0x00, + 0x38,0x08,0x08,0x08,0x08,0x88,0x70,0x00,0x88,0x88,0x90,0xE0,0x90,0x88,0x88,0x00, + 0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x88,0xD8,0xA8,0xA8,0xA8,0xA8,0xA8,0x00, + 0x88,0xC8,0xA8,0xA8,0xA8,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + 0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00, + 0xF0,0x88,0x88,0xF0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00, + 0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00, + 0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x00,0x88,0xA8,0xA8,0xA8,0xA8,0xD8,0x88,0x00, + 0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00, + 0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00, + 0x88,0x50,0xF8,0x20,0xF8,0x20,0x20,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00, + 0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00, + 0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0xF8,0x00, + 0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00,0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00, + 0x08,0x08,0x08,0x78,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xF8,0x80,0x78,0x00, + 0x18,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0xF0,0x00, + 0x80,0x80,0x80,0xF0,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00, + 0x20,0x00,0x20,0x20,0x20,0x20,0xC0,0x00,0x80,0x80,0x88,0x90,0xE0,0x90,0x88,0x00, + 0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00, + 0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00, + 0x00,0x00,0xF0,0x88,0xF0,0x80,0x80,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0x08,0x00, + 0x00,0x00,0xB8,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00, + 0x20,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x70,0x00, + 0x00,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x00,0x00,0x88,0xA8,0xA8,0xD8,0x88,0x00, + 0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x78,0x08,0xF0,0x00, + 0x00,0x00,0xF8,0x08,0x70,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + }; + public NES(string fname) { Debuger.Log("VirtuaNES - CSharpCore\n"); @@ -113,6 +221,11 @@ namespace VirtualNes.Core Debuger.Log("Allocating PPU..."); ppu = new PPU(this); + var screenBuffer = new byte[PPU.SCREEN_WIDTH * PPU.SCREEN_HEIGHT]; + var colormode = new byte[PPU.SCREEN_HEIGHT]; + + ppu.SetScreenPtr(screenBuffer, colormode); + Debuger.Log("Allocating APU..."); apu = new APU(this); @@ -121,10 +234,124 @@ namespace VirtualNes.Core Debuger.Log("Loading ROM Image..."); rom = new ROM(fname); + + mapper = Mapper.CreateMapper(this, rom.GetMapperNo()); + + Debuger.Log("OK"); + + Debuger.Log($"{rom.GetRomName()}"); + Debuger.Log($"Mapper : #{rom.GetMapperNo():D3}"); + Debuger.Log($"PRG SIZE : {16 * rom.GetPROM_SIZE():4:0000}K"); + Debuger.Log($"CHR SIZE : {8 * rom.GetVROM_SIZE():4:0000}K"); + + Debuger.Log($"V MIRROR :{rom.IsVMIRROR()}"); + Debuger.Log($"4 SCREEN :{rom.Is4SCREEN()}"); + Debuger.Log($"SAVE RAM :{rom.IsSAVERAM()}"); + Debuger.Log($"TRAINER :{rom.IsTRAINER()}"); + Debuger.Log($"VS-Unisystem :{rom.IsVSUNISYSTEM()}"); + + NesSub_MemoryInitial(); + LoadSRAM(); + LoadDISK(); } catch (Exception ex) { Debuger.LogError(ex.ToString()); + throw ex; + } + } + + private void LoadDISK() + { + //todo : ŵȡ֧ + } + + private void LoadSRAM() + { + if (rom.IsNSF()) + return; + + MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length); + + if (!rom.IsSAVERAM()) + return; + + var saveFileDir = Supporter.Config.path.szSavePath; + var saveFileName = $"{rom.GetRomName()}.sav"; + + var fp = Supporter.OpenFile(saveFileDir, saveFileName); + + try + { + if (fp == null) + { + throw new Exception("not find ram file to read"); + } + + Debuger.Log("Loading SAVERAM..."); + + int size = (int)fp.Length; + if (size <= 128 * 1024) + { + fp.Read(MMU.WRAM, 0, size); + } + Debuger.Log("OK."); + fp.Close(); + } + catch (Exception ex) + { + fp?.Close(); + fp = null; + } + } + + private void NesSub_MemoryInitial() + { + int i; + + // NA + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); + MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.DRAM, MMU.DRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.ERAM, MMU.ERAM.Length); + MemoryUtility.ZEROMEMORY(MMU.XRAM, MMU.XRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length); + + MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length); + MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length); + + MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length); + + MMU.FrameIRQ = 0xC0; + + MMU.PROM = MMU.VROM = null; + + // 0 Zh~΍ + MMU.PROM_8K_SIZE = MMU.PROM_16K_SIZE = MMU.PROM_32K_SIZE = 1; + MMU.VROM_1K_SIZE = MMU.VROM_2K_SIZE = MMU.VROM_4K_SIZE = MMU.VROM_8K_SIZE = 1; + + // ftHgoNݒ + for (i = 0; i < 8; i++) + { + MMU.CPU_MEM_BANK[i] = null; + MMU.CPU_MEM_TYPE[i] = MMU.BANKTYPE_ROM; + MMU.CPU_MEM_PAGE[i] = 0; + } + + // RAM/WRAM + MMU.SetPROM_Bank(0, MMU.RAM, MMU.BANKTYPE_RAM); + MMU.SetPROM_Bank(3, MMU.WRAM, MMU.BANKTYPE_RAM); + + // _~[ + MMU.SetPROM_Bank(1, MMU.XRAM, MMU.BANKTYPE_ROM); + MMU.SetPROM_Bank(2, MMU.XRAM, MMU.BANKTYPE_ROM); + + for (i = 0; i < 8; i++) + { + MMU.CRAM_USED[i] = 0; } } @@ -201,9 +428,391 @@ namespace VirtualNes.Core } else if (scanline < 240) { + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + if (RenderMethod == EnumRenderMethod.POST_ALL_RENDER) + EmulationCPU(nescfg.ScanlineCycles); + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + ppu.DummyScanline(scanline); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + } + } + ppu.ScanlineNext(); // ̈ʒuŃX^[n͉ʂႤ + if (RenderMethod == EnumRenderMethod.PRE_ALL_RENDER) + EmulationCPU(nescfg.ScanlineCycles); + mapper.HSync(scanline); + ppu.ScanlineStart(); + } + else + { + if (RenderMethod == EnumRenderMethod.POST_RENDER) + EmulationCPU(nescfg.HDrawCycles); + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + ppu.DummyScanline(scanline); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + } + } + } + if (RenderMethod == EnumRenderMethod.PRE_RENDER) + EmulationCPU(nescfg.HDrawCycles); + ppu.ScanlineNext(); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 32); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + } + else if (scanline == 240) + { + mapper.VSync(); + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + EmulationCPU(nescfg.ScanlineCycles); + mapper.HSync(scanline); + } + else + { + EmulationCPU(nescfg.HDrawCycles); + mapper.HSync(scanline); + EmulationCPU(nescfg.HBlankCycles); + } + } + else if (scanline <= nescfg.TotalScanlines - 1) + { + pad.VSync(); + + // VBLANK + if (scanline == nescfg.TotalScanlines - 1) + { + ppu.VBlankEnd(); + } + if (RenderMethod < EnumRenderMethod.POST_RENDER) + { + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.ScanlineCycles); + mapper.HSync(scanline); + } + else + { + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.HDrawCycles); + mapper.HSync(scanline); + EmulationCPU(nescfg.HBlankCycles); + } + + if (scanline == nescfg.TotalScanlines - 1) + { + break; + } + } + if (pad.IsZapperMode()) + { + if (scanline == ZapperY) + bZapper = true; + else + bZapper = false; + } + + scanline++; + NES_scanline = scanline; + } + } + else + { + bZapper = false; + while (true) + { + ppu.SetRenderScanline(scanline); + + if (scanline == 0) + { + // _~[XLC + // H-Draw (4fetches*32) + EmulationCPU(FETCH_CYCLES * 128); + ppu.FrameStart(); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else if (scanline < 240) + { + // XN[`(Scanline 1`239) + if (bDraw) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + if (pad.IsZapperMode() && scanline == ZapperY) + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + if (!ppu.IsSprite0(scanline)) + { + // H-Draw (4fetches*32) + EmulationCPU(FETCH_CYCLES * 128); + ppu.DummyScanline(scanline); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + else + { + ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip); + ppu.ScanlineNext(); + EmulationCPU(FETCH_CYCLES * 10); + mapper.HSync(scanline); + EmulationCPU(FETCH_CYCLES * 22); + ppu.ScanlineStart(); + EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles); + } + } + } + } + else if (scanline == 240) + { + // _~[XLC (Scanline 240) + mapper.VSync(); + + EmulationCPU(nescfg.HDrawCycles); + // H-Sync + mapper.HSync(scanline); + + EmulationCPU(nescfg.HBlankCycles); + } + else if (scanline <= nescfg.TotalScanlines - 1) + { + pad.VSync(); + + // VBLANK + if (scanline == nescfg.TotalScanlines - 1) + { + ppu.VBlankEnd(); + } + if (scanline == 241) + { + ppu.VBlankStart(); + if ((MMU.PPUREG[0] & PPU.PPU_VBLANK_BIT) != 0) + { + cpu.NMI(); + } + } + EmulationCPU(nescfg.HDrawCycles); + + // H-Sync + mapper.HSync(scanline); + + EmulationCPU(nescfg.HBlankCycles); + + if (scanline == nescfg.TotalScanlines - 1) + { + break; + } + } + if (pad.IsZapperMode()) + { + if (scanline == ZapperY) + bZapper = true; + else + bZapper = false; + } + + scanline++; + NES_scanline = scanline; + } + } + + if (bDraw) + { + DrawPad(); + } + } + + private void DrawPad() + { + if (m_bMoviePlay) + { + int offset_h = 12; + int offset_v = Supporter.Config.graphics.bAllLine ? (240 - 18) : (240 - 22); + + if (Supporter.Config.movie.bPadDisplay) + { + uint dwData = pad.GetSyncData(); + for (int i = 0; i < 4; i++) + { + byte Data = (byte)(dwData >> (i * 8)); + if ((m_MovieControl & (1 << i)) != 0) + { + DrawBitmap(offset_h, offset_v, m_PadImg); + + // KEY + if ((Data & (1 << 4)) != 0) DrawBitmap(offset_h + 3, offset_v + 1, m_KeyImg0); // U + if ((Data & (1 << 5)) != 0) DrawBitmap(offset_h + 3, offset_v + 5, m_KeyImg0); // D + if ((Data & (1 << 6)) != 0) DrawBitmap(offset_h + 1, offset_v + 3, m_KeyImg0); // L + if ((Data & (1 << 7)) != 0) DrawBitmap(offset_h + 5, offset_v + 3, m_KeyImg0); // R + + // START,SELECT + if ((Data & (1 << 2)) != 0) DrawBitmap(offset_h + 9, offset_v + 5, m_KeyImg1); // SELECT + if ((Data & (1 << 3)) != 0) DrawBitmap(offset_h + 13, offset_v + 5, m_KeyImg1); // START + + // A,B + if ((Data & (1 << 0)) != 0) DrawBitmap(offset_h + 23, offset_v + 3, m_KeyImg2); // A + if ((Data & (1 << 1)) != 0) DrawBitmap(offset_h + 18, offset_v + 3, m_KeyImg2); // B + + offset_h += 30; + } } } + + if (Supporter.Config.movie.bTimeDisplay) + { + // Time display + int t = m_MovieStep; + int h = t / 216000; + t -= h * 216000; + int m = t / 3600; + t -= m * 3600; + int s = t / 60; + t -= s * 60; + + string szTemp = $"{h:00}:{m:00}:{s:00}.{t * 100 / 60:00}"; + DrawString(256 - 80 + 0, offset_v - 1, szTemp, 0x1F); + DrawString(256 - 80 + 0, offset_v + 1, szTemp, 0x1F); + DrawString(256 - 80 - 1, offset_v + 0, szTemp, 0x1F); + DrawString(256 - 80 + 1, offset_v + 0, szTemp, 0x1F); + DrawString(256 - 80, offset_v, szTemp, 0x30); + } + } + } + + internal void DrawString(int x, int y, string str, byte col) + { + foreach (var @char in str) + { + DrawFont(x, y, (byte)@char, col); + x += 6; + } + } + + internal void DrawFont(int x, int y, byte chr, byte col) + { + int i; + int pFnt; + int pPtr; + var Scn = ppu.GetScreenPtr(); + int pScn = 8; + + + if (chr < 0x20 || chr > 0x7F) + return; + chr -= 0x20; + pFnt = chr * 8; + pPtr = pScn + (256 + 16) * y + x; + for (i = 0; i < 8; i++) + { + if ((Font6x8[pFnt + i] & 0x80) != 0) Scn[pPtr + 0] = col; + if ((Font6x8[pFnt + i] & 0x40) != 0) Scn[pPtr + 1] = col; + if ((Font6x8[pFnt + i] & 0x20) != 0) Scn[pPtr + 2] = col; + if ((Font6x8[pFnt + i] & 0x10) != 0) Scn[pPtr + 3] = col; + if ((Font6x8[pFnt + i] & 0x08) != 0) Scn[pPtr + 4] = col; + if ((Font6x8[pFnt + i] & 0x04) != 0) Scn[pPtr + 5] = col; + pPtr += (256 + 16); + } + } + + private void DrawBitmap(int x, int y, byte[] bitMap) + { + int i, j; + int h, v; + var Scn = ppu.GetScreenPtr(); + int pScn = 8 + (256 + 16) * y + x; + int pPtr; + + int lpBitmap = 0; + h = bitMap[lpBitmap++]; + v = bitMap[lpBitmap++]; + + for (j = 0; j < v; j++) + { + pPtr = pScn; + for (i = 0; i < h; i++) + { + if (bitMap[lpBitmap] != 0xFF) + { + Scn[pPtr] = bitMap[lpBitmap]; + } + lpBitmap++; + pPtr++; + } + pScn += 256 + 16; } } @@ -227,27 +836,27 @@ namespace VirtualNes.Core SaveTurboFile(); // RAM Clear - MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length); + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); if (rom.GetPROM_CRC() == 0x29401686) - { // Minna no Taabou no Nakayoshi Dai Sakusen(J) - MemoryUtility.memset(MMU.RAM, 0xFF, (uint)MMU.RAM.Length); + { // Minna no Taabou no Nakayoshi Dai Sakusen(J) + MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length); } // RAM set if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20) { - MemoryUtility.memset(MMU.WRAM, 0xFF, (uint)MMU.WRAM.Length); + MemoryUtility.memset(MMU.WRAM, 0xFF, MMU.WRAM.Length); } - MemoryUtility.ZEROMEMORY(MMU.CRAM, (uint)MMU.CRAM.Length); - MemoryUtility.ZEROMEMORY(MMU.VRAM, (uint)MMU.VRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.VRAM, 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.SPRAM, MMU.SPRAM.Length); + MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length); + MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length); - MemoryUtility.ZEROMEMORY(MMU.CPUREG, (uint)MMU.CPUREG.Length); - MemoryUtility.ZEROMEMORY(MMU.PPUREG, (uint)MMU.PPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length); + MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length); m_bDiskThrottle = false; @@ -365,7 +974,7 @@ namespace VirtualNes.Core { if (m_bNsfInit) { - MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length); + MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length); if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0) { MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000); @@ -427,7 +1036,54 @@ namespace VirtualNes.Core internal void CheatCodeProcess() { - //todo : ʵ + foreach (var it in m_CheatCode) + { + if ((it.enable & CHEATCODE.CHEAT_ENABLE) == 0) + continue; + + switch (it.type) + { + case CHEATCODE.CHEAT_TYPE_ALWAYS: + CheatWrite(it.length, it.address, it.data); + break; + case CHEATCODE.CHEAT_TYPE_ONCE: + CheatWrite(it.length, it.address, it.data); + it.enable = 0; + break; + case CHEATCODE.CHEAT_TYPE_GREATER: + if (CheatRead(it.length, it.address) > it.data) + { + CheatWrite(it.length, it.address, it.data); + } + break; + case CHEATCODE.CHEAT_TYPE_LESS: + if (CheatRead(it.length, it.address) < it.data) + { + CheatWrite(it.length, it.address, it.data); + } + break; + } + } + } + + private uint CheatRead(byte length, ushort addr) + { + uint data = 0; + for (int i = 0; i <= length; i++) + { + data |= (uint)(Read((ushort)(addr + i)) * (1 << (i * 8))); + } + + return data; + } + + private void CheatWrite(int length, ushort addr, uint data) + { + for (int i = 0; i <= length; i++) + { + Write((ushort)(addr + i), (byte)(data & 0xFF)); + data >>= 8; + } } public void Dispose() @@ -515,7 +1171,23 @@ namespace VirtualNes.Core private void SaveTurboFile() { - //todo : ʵ + int i; + + if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE) + return; + + for (i = 0; i < MMU.ERAM.Length; i++) + { + if (MMU.ERAM[i] != 0x00) + break; + } + + if (i < MMU.ERAM.Length) + { + Debuger.Log("Saving TURBOFILE..."); + + Supporter.SaveFile(MMU.ERAM, Supporter.Config.path.szSavePath, "TurboFile.vtf"); + } } internal void Clock(int cycles) @@ -564,7 +1236,53 @@ namespace VirtualNes.Core internal void Tape(int cycles) { - //todo : ʵTape (ĿǼ¼ҲPlay,ȼܵ) + if (!(IsTapePlay() || IsTapeRec())) + { + return; + } + + if ((m_TapeCycles -= (double)cycles) > 0) + return; + + m_TapeCycles += (nescfg.CpuClock / 32000.0); + // m_TapeCycles += (nescfg->CpuClock / 22050.0); // xă_ۂ + + if (m_bTapePlay) + { + int data = m_fpTape.ReadByte(); + if (data != -1) //EOF + { + if ((data & 0xFF) >= 0x8C) + { + m_TapeOut = 0x02; + } + else + if ((data & 0xFF) <= 0x74) + { + m_TapeOut = 0x00; + } + } + else + { + TapeStop(); + } + } + if (m_bTapeRec) + { + m_fpTape.WriteByte((m_TapeIn & 7) == 7 ? (byte)0x90 : (byte)0x70); + } + } + + private void TapeStop() + { + if (!m_bBarcode) + { + cpu.SetClockProcess(false); + } + + m_bTapePlay = m_bTapeRec = false; + m_fpTape?.Dispose(); + m_fpTape = null; } internal byte Read(ushort addr) @@ -593,7 +1311,7 @@ namespace VirtualNes.Core return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; } - return 0x00; // Warning\h + return 0x00; // Warning\h } private byte ReadReg(ushort addr) @@ -825,5 +1543,25 @@ namespace VirtualNes.Core { return bFrameIRQ; } + + internal EnumRenderMethod GetRenderMethod() + { + return RenderMethod; + } + + internal void SetIrqType(IRQMETHOD nType) + { + nIRQtype = (int)nType; + } + + internal int GetScanline() + { + return NES_scanline; + } + + public enum IRQMETHOD + { + IRQ_HSYNC = 0, IRQ_CLOCK = 1 + } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs index 46aac06..df417bb 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PAD.cs @@ -404,6 +404,42 @@ namespace VirtualNes.Core expad.Reset(); } } + + internal bool IsZapperMode() + { + return bZapperMode; + } + + internal void VSync() + { + padbitsync[0] = padbit[0]; + padbitsync[1] = padbit[1]; + padbitsync[2] = padbit[2]; + padbitsync[3] = padbit[3]; + micbitsync = micbit; + } + + internal uint GetSyncData() + { + uint ret; + ret = (uint)(padbit[0] | (padbit[1] << 8) | (padbit[2] << 16) | (padbit[3] << 24)); + ret |= (uint)(micbit << 8); + return ret; + } + + internal void SetSyncData(uint data) + { + micbit = (byte)((data & 0x00000400) >> 8); + padbit[0] = (byte)data; + padbit[1] = (byte)(data >> 8); + padbit[2] = (byte)(data >> 16); + padbit[3] = (byte)(data >> 24); + } + + internal int GetExController() + { + return excontroller_select; + } } public enum VSType diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs index ef7e7fa..fff8fe7 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/PPU.cs @@ -1,10 +1,18 @@ using Codice.CM.Client.Differences; +using Microsoft.Win32; using System; +using System.IO; +using Unity.VisualScripting.Antlr3.Runtime.Tree; +using UnityEngine; +using static VirtualNes.Core.PPU; namespace VirtualNes.Core { public class PPU { + public const int SCREEN_WIDTH = 256 + 16; + public const int SCREEN_HEIGHT = 240; + private static byte[][] CreateCOLORMAP() { byte[][] res = new byte[5][]; @@ -199,7 +207,7 @@ namespace VirtualNes.Core ScanlineNo = scanline; if (scanline < 240) { - lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline; + lpScanline = (SCREEN_WIDTH) * scanline; } } @@ -358,9 +366,9 @@ namespace VirtualNes.Core loopy_shift = 0; if (lpScreen != null) - MemoryUtility.memset(lpScreen, 0x3F, (int)(Screen.SCREEN_WIDTH) * (int)(Screen.SCREEN_HEIGHT)); + MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH * SCREEN_HEIGHT); if (lpColormode != null) - MemoryUtility.memset(lpColormode, 0, (int)(Screen.SCREEN_HEIGHT)); + MemoryUtility.memset(lpColormode, 0, SCREEN_HEIGHT); } internal void FrameStart() @@ -374,7 +382,7 @@ namespace VirtualNes.Core if (lpScreen != null) { - MemoryUtility.memset(lpScreen, 0x3F, (int)Screen.SCREEN_WIDTH); + MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH); } if (lpColormode != null) { @@ -425,10 +433,738 @@ namespace VirtualNes.Core } } - private enum Screen + private byte[] BGwrite = new byte[33 + 1]; + private byte[] BGmono = new byte[33 + 1]; + private byte[] SPwrite = new byte[33 + 1]; + + internal void Scanline(int scanline, bool bMax, bool bLeftClip) { - SCREEN_WIDTH = 256 + 16, - SCREEN_HEIGHT = 240 + int pScn = 0; + int pBGw = 0; + byte chr_h = 0, chr_l = 0, attr = 0; + + MemoryUtility.ZEROMEMORY(BGwrite, BGwrite.Length); + MemoryUtility.ZEROMEMORY(BGmono, BGmono.Length); + + // Linecolor mode + lpColormode[scanline] = (byte)(((MMU.PPUREG[1] & PPU_BGCOLOR_BIT) >> 5) | ((MMU.PPUREG[1] & PPU_COLORMODE_BIT) << 7)); + + // Render BG + if ((MMU.PPUREG[1] & PPU_BGDISP_BIT) == 0) + { + MemoryUtility.memset(lpScreen, lpScanline, MMU.BGPAL[0], SCREEN_WIDTH); + if (nes.GetRenderMethod() == EnumRenderMethod.TILE_RENDER) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4 * 32); + } + } + else + { + if (nes.GetRenderMethod() != EnumRenderMethod.TILE_RENDER) + { + if (!bExtLatch) + { + // Without Extension Latch + pScn = lpScanline + (8 - loopy_shift); + pBGw = 0; + + int tileofs = (MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8; + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int attradr = 0x23C0 + (MMU.loopy_v & 0x0C00) + ((MMU.loopy_v & 0x0380) >> 4); + int ntbl_x = ntbladr & 0x001F; + int attrsft = (ntbladr & 0x0040) >> 4; + var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span; + + int tileadr; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + attradr &= 0x3FF; + + + for (int i = 0; i < 33; i++) + { + + tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + + if (cache_tile == tileadr && cache_attr == attr) + { + lpScreen[pScn + 0] = lpScreen[pScn - 8]; + lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1]; + lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2]; + lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3]; + + lpScreen[pScn + 4] = lpScreen[pScn - 4]; + lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1]; + lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2]; + lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3]; + + BGwrite[pBGw + 0] = BGwrite[pBGw - 1]; + } + else + { + cache_tile = tileadr; + cache_attr = attr; + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8]; + BGwrite[pBGw] = (byte)(chr_h | chr_l); + + int pBGPAL = attr; + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)]; + lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)]; + lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c1 >> 6))]; + lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)]; + lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)]; + lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)]; + lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)]; + lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)]; + } + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)(tileadr)); + } + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4); + pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span; + } + else + { + ntbladr++; + } + } + } + else + { + // With Extension Latch(For MMC5) + pScn = lpScanline + (8 - loopy_shift); + pBGw = 0; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int ntbl_x = ntbladr & 0x1F; + + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + byte exattr = 0; + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + nes.mapper.PPU_ExtLatchX(i); + nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr); + attr = (byte)(exattr & 0x0C); + + if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr) + { + cache_tile = ((chr_h << 8) + chr_l); + cache_attr = attr; + BGwrite[pBGw] = (byte)(chr_h | chr_l); + + int pBGPAL = attr; + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)]; + lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)]; + lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + (c2 >> 6)]; + lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)]; + lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)]; + lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)]; + lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)]; + lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)]; + } + } + else + { + lpScreen[pScn + 0] = lpScreen[pScn - 8]; + lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1]; + lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2]; + lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3]; + + lpScreen[pScn + 4] = lpScreen[pScn - 4]; + lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1]; + lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2]; + lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3]; + + BGwrite[pBGw + 0] = BGwrite[pBGw - 1]; + } + pScn += 8; + pBGw++; + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + } + else + { + ntbladr++; + } + } + } + } + else + { + if (!bExtLatch) + { + // Without Extension Latch + if (!bExtNameTable) + { + pScn = lpScanline + (8 - loopy_shift); + pBGw = 0; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int attradr = 0x03C0 + ((MMU.loopy_v & 0x0380) >> 4); + int ntbl_x = ntbladr & 0x001F; + int attrsft = (ntbladr & 0x0040) >> 4; + var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span; + + int tileadr = 0; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y; + + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + + attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2); + + if (cache_tile != tileadr || cache_attr != attr) + { + cache_tile = tileadr; + cache_attr = attr; + + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8]; + lpScreen[pBGw] = (byte)(chr_l | chr_h); + + int pBGPAL = attr; + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)]; + lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)]; + lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c2 >> 6))]; + lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)]; + lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)]; + lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)]; + lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)]; + lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)]; + } + } + else + { + lpScreen[pScn + 0] = lpScreen[pScn - 8]; + lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1]; + lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2]; + lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3]; + + lpScreen[pScn + 4] = lpScreen[pScn - 4]; + lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1]; + lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2]; + lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3]; + + BGwrite[pBGw + 0] = BGwrite[pBGw - 1]; + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)(tileadr)); + } + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4); + pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span; + } + else + { + ntbladr++; + } + } + } + else + { + pScn = lpScanline + (8 - loopy_shift); + pBGw = 0; + + int ntbladr; + int tileadr; + int cache_tile = unchecked((int)(0xFFFF0000)); + byte cache_attr = 0xFF; + + chr_h = chr_l = attr = 0; + + ushort loopy_v_tmp = MMU.loopy_v; + + for (int i = 0; i < 33; i++) + { + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + + ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + MMU.PPU_MEM_BANK[ntbladr >> 10].Span[ntbladr & 0x03FF] * 0x10 + ((MMU.loopy_v & 0x7000) >> 12); + attr = (byte)(((MMU.PPU_MEM_BANK[ntbladr >> 10].Span[0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2)] >> (((ntbladr & 0x40) >> 4) + (ntbladr & 0x02))) & 3) << 2); + + if (cache_tile != tileadr || cache_attr != attr) + { + cache_tile = tileadr; + cache_attr = attr; + + chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF]; + chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8]; + BGwrite[pBGw] = (byte)(chr_l | chr_h); + + int pBGPAL = attr; + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)]; + lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)]; + lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + (c2 >> 6)]; + lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)]; + lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)]; + lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)]; + lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)]; + lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)]; + } + } + else + { + lpScreen[pScn + 0] = lpScreen[pScn - 8]; + lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1]; + lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2]; + lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3]; + + lpScreen[pScn + 4] = lpScreen[pScn - 4]; + lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1]; + lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2]; + lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3]; + + BGwrite[pBGw + 0] = BGwrite[pBGw - 1]; + } + pScn += 8; + pBGw++; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)tileadr); + } + + if ((MMU.loopy_v & 0x1F) == 0x1F) + { + MMU.loopy_v ^= 0x041F; + } + else + { + MMU.loopy_v++; + } + } + MMU.loopy_v = loopy_v_tmp; + } + } + else + { + // With Extension Latch(For MMC5) + pScn = lpScanline + (8 - loopy_shift); + pBGw = 0; + + int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF); + int ntbl_x = ntbladr & 0x1F; + + int cache_tile = unchecked((int)0xFFFF0000); + byte cache_attr = 0xFF; + + byte exattr = 0; + chr_h = chr_l = attr = 0; + + for (int i = 0; i < 33; i++) + { + if (i != 0) + { + nes.EmulationCPU(NES.FETCH_CYCLES * 4); + } + nes.mapper.PPU_ExtLatchX(i); + nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr); + attr = (byte)(exattr & 0x0C); + + if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr) + { + cache_tile = ((chr_h << 8) + chr_l); + cache_attr = attr; + BGwrite[pBGw] = (byte)(chr_l | chr_h); + + int pBGPAL = attr; + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + ((c1 >> 6))]; + lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)]; + lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c2 >> 6))]; + lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)]; + lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)]; + lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)]; + lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)]; + lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)]; + } + } + else + { + lpScreen[pScn + 0] = lpScreen[pScn - 8]; + lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1]; + lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2]; + lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3]; + + lpScreen[pScn + 4] = lpScreen[pScn - 4]; + lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1]; + lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2]; + lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3]; + + BGwrite[pBGw + 0] = BGwrite[pBGw - 1]; + } + pScn += 8; + pBGw++; + + if (++ntbl_x == 32) + { + ntbl_x = 0; + ntbladr ^= 0x41F; + } + else + { + ntbladr++; + } + } + } + } + if ((MMU.PPUREG[1] & PPU_BGCLIP_BIT) == 0 && bLeftClip) + { + pScn = lpScanline + 8; + for (int i = 0; i < 8; i++) + { + lpScreen[i] = MMU.BGPAL[0]; + } + } + } + + // Render sprites + var temp = ~PPU_SPMAX_FLAG; + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & temp); + + // 昞帵婜娫奜偱偁傟偽僉儍儞僙儖 + if (scanline > 239) + return; + + if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0) + { + return; + } + + int spmax = 0; + int spraddr = 0, sp_y = 0, sp_h = 0; + chr_h = chr_l = 0; + + + pBGw = 0; + int pSPw = 0; + int pBit2Rev = 0; + + MemoryUtility.ZEROMEMORY(SPwrite, SPwrite.Length); + + spmax = 0; + Sprite sp = new Sprite(MMU.SPRAM, 0); + sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7; + + // Left clip + if (bLeftClip && ((MMU.PPUREG[1] & PPU_SPCLIP_BIT) == 0)) + { + SPwrite[0] = 0xFF; + } + + for (int i = 0; i < 64; i++, sp.AddOffset(1)) + { + sp_y = scanline - (sp.y + 1); + // 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋 + if (sp_y != (sp_y & sp_h)) + continue; + + if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0) + { + // 8x8 Sprite + spraddr = ((MMU.PPUREG[0] & PPU_SPTBL_BIT) << 9) + (sp.tile << 4); + if ((sp.attr & SP_VMIRROR_BIT) == 0) + spraddr += sp_y; + else + spraddr += 7 - sp_y; + } + else + { + // 8x16 Sprite + spraddr = ((sp.tile & 1) << 12) + ((sp.tile & 0xFE) << 4); + if ((sp.attr & SP_VMIRROR_BIT) == 0) + spraddr += ((sp_y & 8) << 1) + (sp_y & 7); + else + spraddr += ((~sp_y & 8) << 1) + (7 - (sp_y & 7)); + } + // Character pattern + chr_l = MMU.PPU_MEM_BANK[spraddr >> 10].Span[spraddr & 0x3FF]; + chr_h = MMU.PPU_MEM_BANK[spraddr >> 10].Span[(spraddr & 0x3FF) + 8]; + + // Character latch(For MMC2/MMC4) + if (bChrLatch) + { + nes.mapper.PPU_ChrLatch((ushort)spraddr); + } + + // pattern mask + if ((sp.attr & SP_HMIRROR_BIT) != 0) + { + chr_l = Bit2Rev[pBit2Rev + chr_l]; + chr_h = Bit2Rev[pBit2Rev + chr_h]; + } + byte SPpat = (byte)(chr_l | chr_h); + + // Sprite hitcheck + if (i == 0 && (MMU.PPUREG[2] & PPU_SPHIT_FLAG) == 0) + { + int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3; + int BGsft = 8 - ((loopy_shift + sp.x) & 7); + + var temp1 = BGwrite[pBGw + BGpos + 0] << 8; + var temp2 = BGwrite[pBGw + BGpos + 1]; + byte BGmsk = (byte)((temp1 | temp2) >> BGsft); + + if ((SPpat & BGmsk) != 0) + { + MMU.PPUREG[2] |= PPU_SPHIT_FLAG; + } + } + + // Sprite mask + int SPpos = sp.x / 8; + int SPsft = 8 - (sp.x & 7); + byte SPmsk = (byte)((SPwrite[pSPw + SPpos + 0] << 8 | SPwrite[pSPw + SPpos + 1]) >> SPsft); + ushort SPwrt = (ushort)(SPpat << SPsft); + SPwrite[pSPw + SPpos + 0] = (byte)(SPwrite[pSPw + SPpos + 0] | SPwrt >> 8); + SPwrite[pSPw + SPpos + 1] = (byte)(SPwrite[pSPw + SPpos + 1] | SPwrt & 0xFF); + SPpat = (byte)(SPpat & ~SPmsk); + + if ((sp.attr & SP_PRIORITY_BIT) != 0) + { + // BG > SP priority + int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3; + int BGsft = 8 - ((loopy_shift + sp.x) & 7); + byte BGmsk = (byte)(((BGwrite[pBGw + BGpos + 0] << 8) | BGwrite[pBGw + BGpos + 1]) >> BGsft); + + SPpat = (byte)(SPpat & ~BGmsk); + } + + // Attribute + int pSPPAL = (sp.attr & SP_COLOR_BIT) << 2; + // Ptr + pScn = lpScanline + sp.x + 8; + + if (!bExtMono) + { + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + if ((SPpat & 0x80) != 0) lpScreen[pScn + 0] = MMU.SPPAL[pSPPAL + (c1 >> 6)]; + if ((SPpat & 0x08) != 0) lpScreen[pScn + 4] = MMU.SPPAL[pSPPAL + ((c1 >> 2) & 3)]; + if ((SPpat & 0x40) != 0) lpScreen[pScn + 1] = MMU.SPPAL[pSPPAL + ((c2 >> 6))]; + if ((SPpat & 0x04) != 0) lpScreen[pScn + 5] = MMU.SPPAL[pSPPAL + ((c2 >> 2) & 3)]; + if ((SPpat & 0x20) != 0) lpScreen[pScn + 2] = MMU.SPPAL[pSPPAL + ((c1 >> 4) & 3)]; + if ((SPpat & 0x02) != 0) lpScreen[pScn + 6] = MMU.SPPAL[pSPPAL + (c1 & 3)]; + if ((SPpat & 0x10) != 0) lpScreen[pScn + 3] = MMU.SPPAL[pSPPAL + ((c2 >> 4) & 3)]; + if ((SPpat & 0x01) != 0) lpScreen[pScn + 7] = MMU.SPPAL[pSPPAL + (c2 & 3)]; + } + else + { + // Monocrome effect (for Final Fantasy) + byte mono = BGmono[((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3]; + + int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA); + int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA); + if ((SPpat & 0x80) != 0) lpScreen[pScn + 0] = (byte)(MMU.SPPAL[pSPPAL + (c1 >> 6)] | mono); + if ((SPpat & 0x08) != 0) lpScreen[pScn + 4] = (byte)(MMU.SPPAL[pSPPAL + ((c1 >> 2) & 3)] | mono); + if ((SPpat & 0x40) != 0) lpScreen[pScn + 1] = (byte)(MMU.SPPAL[pSPPAL + (c2 >> 6)] | mono); + if ((SPpat & 0x04) != 0) lpScreen[pScn + 5] = (byte)(MMU.SPPAL[pSPPAL + ((c2 >> 2) & 3)] | mono); + if ((SPpat & 0x20) != 0) lpScreen[pScn + 2] = (byte)(MMU.SPPAL[pSPPAL + ((c1 >> 4) & 3)] | mono); + if ((SPpat & 0x02) != 0) lpScreen[pScn + 6] = (byte)(MMU.SPPAL[pSPPAL + (c1 & 3)] | mono); + if ((SPpat & 0x10) != 0) lpScreen[pScn + 3] = (byte)(MMU.SPPAL[pSPPAL + ((c2 >> 4) & 3)] | mono); + if ((SPpat & 0x01) != 0) lpScreen[pScn + 7] = (byte)(MMU.SPPAL[pSPPAL + (c2 & 3)] | mono); + } + + if (++spmax > 8 - 1) + { + if (!bMax) + break; + } + } + if (spmax > 8 - 1) + { + MMU.PPUREG[2] |= PPU_SPMAX_FLAG; + } + } + + internal bool IsSprite0(int scanline) + { + // 僗僾儔僀僩orBG旕昞帵偼僉儍儞僙儖(僸僢僩偟側偄) + if ((MMU.PPUREG[1] & (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) + return false; + + // 婛偵僸僢僩偟偰偄偨傜僉儍儞僙儖 + if ((MMU.PPUREG[2] & PPU_SPHIT_FLAG) != 0) + return false; + + if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0) + { + // 8x8 + if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 7 + 1))) + return false; + } + else + { + // 8x16 + if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 15 + 1))) + return false; + } + + return true; + } + + internal void DummyScanline(int scanline) + { + int i; + int spmax; + int sp_h; + + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPMAX_FLAG); + + // 僗僾儔僀僩旕昞帵偼僉儍儞僙儖 + if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0) + return; + + // 昞帵婜娫奜偱偁傟偽僉儍儞僙儖 + if (scanline < 0 || scanline > 239) + return; + + Sprite sp = new Sprite(MMU.SPRAM, 0); + sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7; + + spmax = 0; + // Sprite Max check + for (i = 0; i < 64; i++, sp.AddOffset(1)) + { + // 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋 + if ((scanline < sp.y + 1) || (scanline > (sp.y + sp_h + 1))) + { + continue; + } + + if (++spmax > 8 - 1) + { + MMU.PPUREG[2] |= PPU_SPMAX_FLAG; + break; + } + } + } + + internal void VBlankEnd() + { + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_VBLANK_FLAG); + // VBlank扙弌帪偵僋儕傾偝傟傞 + // 僄僉僒僀僩僶僀僋偱廳梫 + MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPHIT_FLAG); + } + + internal void VBlankStart() + { + MMU.PPUREG[2] |= PPU_VBLANK_FLAG; + } + + public byte[] GetScreenPtr() + { + return lpScreen; + } + + internal void SetScreenPtr(byte[] screenBuffer, byte[] colormode) + { + lpScreen = screenBuffer; + lpColormode = colormode; + } + + internal bool IsDispON() + { + return (MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0; + } + + public struct Sprite + { + public byte y + { + get => raw[offset + 0]; + set => raw[offset + 0] = value; + } + + public byte tile + { + get => raw[offset + 1]; + set => raw[offset + 1] = value; + } + public byte attr + { + get => raw[offset + 2]; + set => raw[offset + 2] = value; + } + public byte x + { + get => raw[offset + 3]; + set => raw[offset + 3] = value; + } + + private byte[] raw; + private int offset; + + public Sprite(byte[] raw, int offset) + { + this.raw = raw; + this.offset = offset * 4; + } + + public void AddOffset(int offset) + { + this.offset += offset * 4; + } } } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs index 51b6a87..b7aacbd 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/ROM.cs @@ -218,7 +218,7 @@ namespace VirtualNes.Core } Supporter.GetFilePathInfo(fname, out fullpath, out path); - + name = Path.GetFileNameWithoutExtension(fullpath); if (!bNSF) { mapper = (header.control1 >> 4) | (header.control2 & 0xF0); @@ -396,6 +396,11 @@ namespace VirtualNes.Core { return nsfheader; } + + internal string GetRomPath() + { + return path; + } } diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs index 21851f8..d3e1f64 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgGraphics.cs @@ -2,5 +2,38 @@ { public class CfgGraphics { + public bool bAspect = false; + public bool bAllSprite = true; + public bool bAllLine = false; + public bool bFPSDisp = false; + public bool bTVFrame = false; + public bool bScanline = false; + public int nScanlineColor = 75; + public bool bSyncDraw = false; + public bool bFitZoom = false; + + public bool bLeftClip = true; + + public bool bWindowVSync = false; + + public bool bSyncNoSleep = false; + + public bool bDiskAccessLamp = false; + + public bool bDoubleSize = false; + public bool bSystemMemory = false; + public bool bUseHEL = false; + + public bool bNoSquareList = false; + + public int nGraphicsFilter = 0; + + public uint dwDisplayWidth = 640; + public uint dwDisplayHeight = 480; + public uint dwDisplayDepth = 16; + public uint dwDisplayRate = 0; + + public bool bPaletteFile = false; + public char[] szPaletteFile = new char[260]; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs index d8d718c..0e0dcbf 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgMovie.cs @@ -1,6 +1,14 @@ -namespace VirtualNes.Core +using System; + +namespace VirtualNes.Core { public class CfgMovie { + public byte[] bUsePlayer = new byte[4] { 0xFF, 0x00, 0x00, 0x00 }; + public bool bRerecord = true; + public bool bLoopPlay = false; + public bool bResetRec = false; + public bool bPadDisplay = false; + public bool bTimeDisplay = false; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs index a89aa6b..6a28259 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/EmulatorConfig/CfgPath.cs @@ -1,6 +1,25 @@ -namespace VirtualNes.Core +using System; + +namespace VirtualNes.Core { public class CfgPath { + public bool bRomPath = true; + public bool bSavePath = true; + public bool bStatePath = true; + public bool bSnapshotPath = true; + public bool bMoviePath = true; + public bool bWavePath = true; + public bool bCheatPath = true; + public bool bIpsPath = true; + + public string szRomPath = "roms"; + public string szSavePath = "save"; + public string szStatePath = "state"; + public string szSnapshotPath = "snapshot"; + public string szMoviePath = "movie"; + public string szWavePath = "wave"; + public string szCheatPath = "cheatcode"; + public string szIpsPath = "ips"; } } \ No newline at end of file diff --git a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs index cc417af..6f6d40e 100644 --- a/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs +++ b/AxibugEmuOnline.Client/Assets/VirtualNes.Core/Supporter/Supporter.cs @@ -35,6 +35,20 @@ namespace VirtualNes.Core s_support.SaveDISKToFile(diskFileContent, romName); } + public static void PrepareDirectory(string directPath) + { + s_support.PrepareDirectory(directPath); + } + + public static void SaveFile(byte[] fileData, string directPath, string fileName) + { + s_support.SaveFile(fileData, directPath, fileName); + } + public static Stream OpenFile(string directPath, string fileName) + { + return s_support.OpenFile(directPath, fileName); + } + public static EmulatorConfig Config => s_support.Config; } @@ -46,5 +60,9 @@ namespace VirtualNes.Core void SaveSRAMToFile(byte[] sramContent, string romName); void SaveDISKToFile(byte[] diskFileContent, string romName); EmulatorConfig Config { get; } + + void PrepareDirectory(string directPath); + void SaveFile(byte[] fileData, string directPath, string fileName); + Stream OpenFile(string directPath, string fileName); } }