迭代中

This commit is contained in:
ALIENJACK\alien 2024-07-30 11:57:09 +08:00
parent d7846c7182
commit e5fa62d209
27 changed files with 2625 additions and 62 deletions

View File

@ -152,6 +152,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3} m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
VideoProvider: {fileID: 1295252151}
--- !u!4 &149545948 --- !u!4 &149545948
Transform: Transform:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -162,7 +163,8 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: [] m_Children:
- {fileID: 1295252150}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 3 m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -259,6 +261,78 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 0 m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 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 --- !u!1 &1232273651
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -342,6 +416,51 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 2 m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 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 --- !u!1 &1359344831
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -408,3 +527,103 @@ Transform:
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 1 m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} 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

View File

@ -64,5 +64,33 @@ namespace AxibugEmuOnline.Client
} }
public EmulatorConfig Config { get; private set; } = new EmulatorConfig(); 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;
}
}
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using UnityEngine; using UnityEngine;
using VirtualNes.Core; using VirtualNes.Core;
using VirtualNes.Core.Debug; using VirtualNes.Core.Debug;
@ -8,6 +9,8 @@ namespace AxibugEmuOnline.Client
{ {
private NES m_nesIns; private NES m_nesIns;
public VideoProvider VideoProvider;
private void Start() private void Start()
{ {
StartGame("Kirby.nes"); StartGame("Kirby.nes");
@ -19,8 +22,17 @@ namespace AxibugEmuOnline.Client
Supporter.Setup(new CoreSupporter()); Supporter.Setup(new CoreSupporter());
Debuger.Setup(new CoreDebuger()); 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() public void StopGame()
@ -31,7 +43,12 @@ namespace AxibugEmuOnline.Client
private void Update() 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);
}
} }
} }
} }

View File

@ -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

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: ffe34aaf87e4b9942b4c2ac05943d444
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 8400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 83fbe375412d1af4482ae76e81c1dda2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -9,22 +9,23 @@ namespace VirtualNes.Core
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new System.NotImplementedException(); //todo : 实现
return 0;
} }
internal void SyncWrite(ushort addr, byte data) 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) private void WriteSub(ushort addr, byte data, FDSSOUND ch, double rate)
{ {
throw new NotImplementedException(); //todo : 实现
} }
private class FDSSOUND private class FDSSOUND

View File

@ -6,22 +6,23 @@ namespace VirtualNes.Core
{ {
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new NotImplementedException(); //todo : 实现
return 0;
} }
} }
} }

View File

@ -127,22 +127,23 @@ namespace VirtualNes.Core
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new System.NotImplementedException(); //todo : 实现
return 0;
} }
internal byte SyncRead(ushort addr) internal byte SyncRead(ushort addr)

View File

@ -6,27 +6,28 @@ namespace VirtualNes.Core
{ {
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new System.NotImplementedException(); //todo : 实现
return 0;
} }
internal void SyncWrite(ushort addr, byte data) internal void SyncWrite(ushort addr, byte data)
{ {
throw new NotImplementedException(); //todo : 实现
} }
} }
} }

View File

@ -4,22 +4,23 @@
{ {
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new System.NotImplementedException(); //todo : 实现
return 0;
} }
} }
} }

View File

@ -4,22 +4,23 @@
{ {
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
throw new System.NotImplementedException(); //todo : 实现
} }
public override int Process(int channel) public override int Process(int channel)
{ {
throw new System.NotImplementedException(); //todo : 实现
return 0;
} }
} }
} }

View File

@ -2,6 +2,22 @@
{ {
public class CHEATCODE 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 enable;
public byte type; public byte type;
public byte length; public byte length;

View File

@ -1,17 +1,26 @@
using System.Runtime.CompilerServices; using System;
using System.Runtime.CompilerServices;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
public static class MemoryUtility 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;
}
} }
} }
} }

View File

@ -67,6 +67,9 @@ namespace VirtualNes
public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕 public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕
public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕 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<byte> ptr, byte type) internal static void SetPROM_Bank(byte page, Memory<byte> ptr, byte type)
{ {
CPU_MEM_BANK[page] = ptr; CPU_MEM_BANK[page] = ptr;

View File

@ -5,6 +5,7 @@ namespace VirtualNes.Core
public abstract class Mapper public abstract class Mapper
{ {
protected NES nes; protected NES nes;
public Mapper(NES parent) public Mapper(NES parent)
{ {
nes = parent; nes = parent;
@ -81,8 +82,10 @@ namespace VirtualNes.Core
//todo : 实现加载mapper //todo : 实现加载mapper
switch (no) switch (no)
{ {
case 4:
return new Mapper004(parent);
default: default:
throw new NotImplementedException($"Mapper#{no} is not Impl"); throw new NotImplementedException($"Mapper#{no:000} is not Impl");
} }
} }
} }

View File

@ -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);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1be6fcc00c619e84cbf0d5d92701b2e3
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,8 +1,15 @@
using Codice.CM.Client.Differences;
using Google.Protobuf.Collections;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Numerics;
using System.Text; using System.Text;
using UnityEngine.UIElements;
using UnityEngine;
using VirtualNes.Core.Debug; using VirtualNes.Core.Debug;
using Unity.VisualScripting.Antlr3.Runtime.Tree;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
@ -30,6 +37,9 @@ namespace VirtualNes.Core
private bool m_bMoviePlay; private bool m_bMoviePlay;
private bool m_bMovieRec; private bool m_bMovieRec;
private Stream m_fpMovie; private Stream m_fpMovie;
private uint m_MovieControl;
private int m_MovieStepTotal;
private int m_MovieStep;
private bool m_bTapePlay; private bool m_bTapePlay;
private bool m_bTapeRec; private bool m_bTapeRec;
private Stream m_fpTape; private Stream m_fpTape;
@ -60,9 +70,107 @@ namespace VirtualNes.Core
private int NES_scanline; private int NES_scanline;
private EnumRenderMethod RenderMethod; private EnumRenderMethod RenderMethod;
private bool bZapper; private bool bZapper;
private int ZapperX;
private int ZapperY;
private long base_cycles; private long base_cycles;
private long emul_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) public NES(string fname)
{ {
Debuger.Log("VirtuaNES - CSharpCore\n"); Debuger.Log("VirtuaNES - CSharpCore\n");
@ -113,6 +221,11 @@ namespace VirtualNes.Core
Debuger.Log("Allocating PPU..."); Debuger.Log("Allocating PPU...");
ppu = new PPU(this); 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..."); Debuger.Log("Allocating APU...");
apu = new APU(this); apu = new APU(this);
@ -121,10 +234,124 @@ namespace VirtualNes.Core
Debuger.Log("Loading ROM Image..."); Debuger.Log("Loading ROM Image...");
rom = new ROM(fname); 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) catch (Exception ex)
{ {
Debuger.LogError(ex.ToString()); 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;
// メモリクリア
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 除算防止対策
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;
// デフォルトバンク設定
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) 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(); // これの位置でラスター系は画面が違う
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)
{
// ダミースキャンライン
// 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)
{
// スクリーン描画(Scanline 1239)
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)
{
// ダミースキャンライン (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(); SaveTurboFile();
// RAM Clear // RAM Clear
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length); MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
if (rom.GetPROM_CRC() == 0x29401686) if (rom.GetPROM_CRC() == 0x29401686)
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J) { // Minna no Taabou no Nakayoshi Dai Sakusen(J)
MemoryUtility.memset(MMU.RAM, 0xFF, (uint)MMU.RAM.Length); MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length);
} }
// RAM set // RAM set
if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20) 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.CRAM, MMU.CRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.VRAM, (uint)MMU.VRAM.Length); MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.SPRAM, (uint)MMU.SPRAM.Length); MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length);
MemoryUtility.ZEROMEMORY(MMU.BGPAL, (uint)MMU.BGPAL.Length); MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.SPPAL, (uint)MMU.SPPAL.Length); MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length);
MemoryUtility.ZEROMEMORY(MMU.CPUREG, (uint)MMU.CPUREG.Length); MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length);
MemoryUtility.ZEROMEMORY(MMU.PPUREG, (uint)MMU.PPUREG.Length); MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length);
m_bDiskThrottle = false; m_bDiskThrottle = false;
@ -365,7 +974,7 @@ namespace VirtualNes.Core
{ {
if (m_bNsfInit) if (m_bNsfInit)
{ {
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length); MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0) if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0)
{ {
MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000); MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000);
@ -427,7 +1036,54 @@ namespace VirtualNes.Core
internal void CheatCodeProcess() 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() public void Dispose()
@ -515,7 +1171,23 @@ namespace VirtualNes.Core
private void SaveTurboFile() 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) internal void Clock(int cycles)
@ -564,7 +1236,53 @@ namespace VirtualNes.Core
internal void Tape(int cycles) 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); // 遅すぎてダメっぽい
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) internal byte Read(ushort addr)
@ -593,7 +1311,7 @@ namespace VirtualNes.Core
return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]; return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF];
} }
return 0x00; // Warning梊杊 return 0x00; // Warning予防
} }
private byte ReadReg(ushort addr) private byte ReadReg(ushort addr)
@ -825,5 +1543,25 @@ namespace VirtualNes.Core
{ {
return bFrameIRQ; 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
}
} }
} }

View File

@ -404,6 +404,42 @@ namespace VirtualNes.Core
expad.Reset(); 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 public enum VSType

View File

@ -1,10 +1,18 @@
using Codice.CM.Client.Differences; using Codice.CM.Client.Differences;
using Microsoft.Win32;
using System; using System;
using System.IO;
using Unity.VisualScripting.Antlr3.Runtime.Tree;
using UnityEngine;
using static VirtualNes.Core.PPU;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
public class PPU public class PPU
{ {
public const int SCREEN_WIDTH = 256 + 16;
public const int SCREEN_HEIGHT = 240;
private static byte[][] CreateCOLORMAP() private static byte[][] CreateCOLORMAP()
{ {
byte[][] res = new byte[5][]; byte[][] res = new byte[5][];
@ -199,7 +207,7 @@ namespace VirtualNes.Core
ScanlineNo = scanline; ScanlineNo = scanline;
if (scanline < 240) if (scanline < 240)
{ {
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline; lpScanline = (SCREEN_WIDTH) * scanline;
} }
} }
@ -358,9 +366,9 @@ namespace VirtualNes.Core
loopy_shift = 0; loopy_shift = 0;
if (lpScreen != null) 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) if (lpColormode != null)
MemoryUtility.memset(lpColormode, 0, (int)(Screen.SCREEN_HEIGHT)); MemoryUtility.memset(lpColormode, 0, SCREEN_HEIGHT);
} }
internal void FrameStart() internal void FrameStart()
@ -374,7 +382,7 @@ namespace VirtualNes.Core
if (lpScreen != null) if (lpScreen != null)
{ {
MemoryUtility.memset(lpScreen, 0x3F, (int)Screen.SCREEN_WIDTH); MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH);
} }
if (lpColormode != null) 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, int pScn = 0;
SCREEN_HEIGHT = 240 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;
}
} }
} }
} }

View File

@ -218,7 +218,7 @@ namespace VirtualNes.Core
} }
Supporter.GetFilePathInfo(fname, out fullpath, out path); Supporter.GetFilePathInfo(fname, out fullpath, out path);
name = Path.GetFileNameWithoutExtension(fullpath);
if (!bNSF) if (!bNSF)
{ {
mapper = (header.control1 >> 4) | (header.control2 & 0xF0); mapper = (header.control1 >> 4) | (header.control2 & 0xF0);
@ -396,6 +396,11 @@ namespace VirtualNes.Core
{ {
return nsfheader; return nsfheader;
} }
internal string GetRomPath()
{
return path;
}
} }

View File

@ -2,5 +2,38 @@
{ {
public class CfgGraphics 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];
} }
} }

View File

@ -1,6 +1,14 @@
namespace VirtualNes.Core using System;
namespace VirtualNes.Core
{ {
public class CfgMovie 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;
} }
} }

View File

@ -1,6 +1,25 @@
namespace VirtualNes.Core using System;
namespace VirtualNes.Core
{ {
public class CfgPath 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";
} }
} }

View File

@ -35,6 +35,20 @@ namespace VirtualNes.Core
s_support.SaveDISKToFile(diskFileContent, romName); s_support.SaveDISKToFile(diskFileContent, romName);
} }
public static void PrepareDirectory(string directPath)
{
s_support.PrepareDirectory(directPath);
}
public static void SaveFile(byte[] fileData, string directPath, string fileName)
{
s_support.SaveFile(fileData, directPath, fileName);
}
public static Stream OpenFile(string directPath, string fileName)
{
return s_support.OpenFile(directPath, fileName);
}
public static EmulatorConfig Config => s_support.Config; public static EmulatorConfig Config => s_support.Config;
} }
@ -46,5 +60,9 @@ namespace VirtualNes.Core
void SaveSRAMToFile(byte[] sramContent, string romName); void SaveSRAMToFile(byte[] sramContent, string romName);
void SaveDISKToFile(byte[] diskFileContent, string romName); void SaveDISKToFile(byte[] diskFileContent, string romName);
EmulatorConfig Config { get; } EmulatorConfig Config { get; }
void PrepareDirectory(string directPath);
void SaveFile(byte[] fileData, string directPath, string fileName);
Stream OpenFile(string directPath, string fileName);
} }
} }