Merge pull request 'dev_4VirtualNes' (#19) from Alienjack/AxibugEmuOnline:dev_4VirtualNes into dev_4VirtualNes

Reviewed-on: sin365/AxibugEmuOnline#19
This commit is contained in:
sin365 2024-07-31 21:39:54 +08:00
commit 507ef45d3a
555 changed files with 16742 additions and 172 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@
/AxibugEmuOnline.Client/ProjectSettings/ProjectVersion.txt
/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset
/AxibugEmuOnline.Client/Logs
/virtuanessrc097-master/save

View File

@ -123,49 +123,6 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1 &149545946
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 149545948}
- component: {fileID: 149545947}
m_Layer: 0
m_Name: NesEmulator
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!114 &149545947
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 149545946}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!4 &149545948
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 149545946}
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: 0}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &708549044
GameObject:
m_ObjectHideFlags: 0
@ -408,3 +365,60 @@ Transform:
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1001 &4232056520998800727
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
m_TransformParent: {fileID: 0}
m_Modifications:
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_RootOrder
value: 3
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536011, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536013, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_Name
value: NesEmulator
objectReference: {fileID: 0}
m_RemovedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}

View File

@ -7,6 +7,7 @@ namespace AxibugEmuOnline.Client
{
public class CoreDebuger : IDebugerImpl
{
public void Log(string message)
{
Debug.Log(message);

View File

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

View File

@ -1,3 +1,5 @@
using System;
using System.IO;
using UnityEngine;
using VirtualNes.Core;
using VirtualNes.Core.Debug;
@ -8,6 +10,8 @@ namespace AxibugEmuOnline.Client
{
private NES m_nesIns;
public VideoProvider VideoProvider;
private void Start()
{
StartGame("Kirby.nes");
@ -19,8 +23,16 @@ 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);
}
catch (Exception ex)
{
m_nesIns = null;
Debug.LogError(ex);
}
}
public void StopGame()
@ -29,9 +41,18 @@ namespace AxibugEmuOnline.Client
m_nesIns = null;
}
private void Update()
{
m_nesIns?.EmulateFrame(true);
if (m_nesIns != null)
{
m_nesIns.EmulateFrame(true);
var screenBuffer = m_nesIns.ppu.GetScreenPtr();
var lineColorMode = m_nesIns.ppu.GetLineColorMode();
VideoProvider.SetDrawData(screenBuffer, lineColorMode, 256, 240);
}
}
}
}

View File

@ -0,0 +1,264 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4232056520112715746
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4232056520112715745}
- component: {fileID: 4232056520112715744}
m_Layer: 0
m_Name: VideoProvider
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4232056520112715745
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520112715746}
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: 4232056520494431727}
m_Father: {fileID: 4232056521131536011}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4232056520112715744
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520112715746}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 83fbe375412d1af4482ae76e81c1dda2, type: 3}
m_Name:
m_EditorClassIdentifier:
Image: {fileID: 4232056521759880274}
--- !u!1 &4232056520494431712
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4232056520494431727}
- component: {fileID: 4232056520494431724}
- component: {fileID: 4232056520494431725}
- component: {fileID: 4232056520494431726}
m_Layer: 5
m_Name: Canvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4232056520494431727
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520494431712}
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: 4232056521759880275}
m_Father: {fileID: 4232056520112715745}
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!223 &4232056520494431724
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520494431712}
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
--- !u!114 &4232056520494431725
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520494431712}
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!114 &4232056520494431726
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056520494431712}
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!1 &4232056521131536013
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4232056521131536011}
- component: {fileID: 4232056521131536012}
m_Layer: 0
m_Name: NesEmulator
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4232056521131536011
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521131536013}
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: 4232056520112715745}
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4232056521131536012
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521131536013}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3}
m_Name:
m_EditorClassIdentifier:
VideoProvider: {fileID: 4232056520112715744}
--- !u!1 &4232056521759880276
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4232056521759880275}
- component: {fileID: 4232056521759880273}
- component: {fileID: 4232056521759880274}
m_Layer: 5
m_Name: RawImage
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &4232056521759880275
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521759880276}
m_LocalRotation: {x: 1, y: 0, z: 0, w: 0}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4232056520494431727}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 180, 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: 272, y: 240}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &4232056521759880273
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521759880276}
m_CullTransparentMesh: 1
--- !u!114 &4232056521759880274
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4232056521759880276}
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

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f8bea3f8aa351bb46ada33b2274729ea
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

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,242 @@
using Codice.CM.Client.Differences;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
{
public static class PaletteDefine
{
public class PALBUF
{
public byte r;
public byte g;
public byte b;
public PALBUF(byte r, byte g, byte b)
{
this.r = r;
this.g = g;
this.b = b;
}
}
// スキャンラインカラー
private static int m_nScanlineColor => Supporter.Config.graphics.nScanlineColor;
public static float[][] PalConvTbl = new float[8][]
{
new float[3]{1.00f, 1.00f, 1.00f},
new float[3]{1.00f, 0.80f, 0.73f},
new float[3]{0.73f, 1.00f, 0.70f},
new float[3]{0.76f, 0.78f, 0.58f},
new float[3]{0.86f, 0.80f, 1.00f},
new float[3]{0.83f, 0.68f, 0.85f},
new float[3]{0.67f, 0.77f, 0.83f},
new float[3]{0.68f, 0.68f, 0.68f},
};
public static PALBUF[] m_PaletteBuf = new PALBUF[64]
{
new PALBUF(0x7F, 0x7F, 0x7F),
new PALBUF(0x20, 0x00, 0xB0),
new PALBUF(0x28, 0x00, 0xB8),
new PALBUF(0x60, 0x10, 0xA0),
new PALBUF(0x98, 0x20, 0x78),
new PALBUF(0xB0, 0x10, 0x30),
new PALBUF(0xA0, 0x30, 0x00),
new PALBUF(0x78, 0x40, 0x00),
new PALBUF(0x48, 0x58, 0x00),
new PALBUF(0x38, 0x68, 0x00),
new PALBUF(0x38, 0x6C, 0x00),
new PALBUF(0x30, 0x60, 0x40),
new PALBUF(0x30, 0x50, 0x80),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0xBC, 0xBC, 0xBC),
new PALBUF(0x40, 0x60, 0xF8),
new PALBUF(0x40, 0x40, 0xFF),
new PALBUF(0x90, 0x40, 0xF0),
new PALBUF(0xD8, 0x40, 0xC0),
new PALBUF(0xD8, 0x40, 0x60),
new PALBUF(0xE0, 0x50, 0x00),
new PALBUF(0xC0, 0x70, 0x00),
new PALBUF(0x88, 0x88, 0x00),
new PALBUF(0x50, 0xA0, 0x00),
new PALBUF(0x48, 0xA8, 0x10),
new PALBUF(0x48, 0xA0, 0x68),
new PALBUF(0x40, 0x90, 0xC0),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0xFF, 0xFF, 0xFF),
new PALBUF(0x60, 0xA0, 0xFF),
new PALBUF(0x50, 0x80, 0xFF),
new PALBUF(0xA0, 0x70, 0xFF),
new PALBUF(0xF0, 0x60, 0xFF),
new PALBUF(0xFF, 0x60, 0xB0),
new PALBUF(0xFF, 0x78, 0x30),
new PALBUF(0xFF, 0xA0, 0x00),
new PALBUF(0xE8, 0xD0, 0x20),
new PALBUF(0x98, 0xE8, 0x00),
new PALBUF(0x70, 0xF0, 0x40),
new PALBUF(0x70, 0xE0, 0x90),
new PALBUF(0x60, 0xD0, 0xE0),
new PALBUF(0x60, 0x60, 0x60),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0xFF, 0xFF, 0xFF),
new PALBUF(0x90, 0xD0, 0xFF),
new PALBUF(0xA0, 0xB8, 0xFF),
new PALBUF(0xC0, 0xB0, 0xFF),
new PALBUF(0xE0, 0xB0, 0xFF),
new PALBUF(0xFF, 0xB8, 0xE8),
new PALBUF(0xFF, 0xC8, 0xB8),
new PALBUF(0xFF, 0xD8, 0xA0),
new PALBUF(0xFF, 0xF0, 0x90),
new PALBUF(0xC8, 0xF0, 0x80),
new PALBUF(0xA0, 0xF0, 0xA0),
new PALBUF(0xA0, 0xFF, 0xC8),
new PALBUF(0xA0, 0xFF, 0xF0),
new PALBUF(0xA0, 0xA0, 0xA0),
new PALBUF(0x00, 0x00, 0x00),
new PALBUF(0x00, 0x00, 0x00),
};
#region
// Color
public static uint[][] m_cnPalette = new uint[8][]
{
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
};
// Color/Scanline
public static uint[][] m_csPalette = new uint[8][]
{
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
};
// Monochrome
public static uint[][] m_mnPalette = new uint[8][]
{
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
};
// Monochrome/Scanline
public static uint[][] m_msPalette = new uint[8][]
{
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
new uint[256],
};
#endregion
static PaletteDefine()
{
int Rbit = 0, Gbit = 0, Bbit = 0;
int Rsft = 0, Gsft = 0, Bsft = 0;
GetBitMask(0xFF0000, ref Rsft, ref Rbit);
GetBitMask(0x00FF00, ref Gsft, ref Gbit);
GetBitMask(0x0000FF, ref Bsft, ref Bbit);
for (int j = 0; j < 8; j++)
{
for (int i = 0; i < 64; i++)
{
uint Rn, Gn, Bn;
uint Rs, Gs, Bs;
// Normal
Rn = (uint)(PalConvTbl[j][0] * m_PaletteBuf[i].r);
Gn = (uint)(PalConvTbl[j][1] * m_PaletteBuf[i].g);
Bn = (uint)(PalConvTbl[j][2] * m_PaletteBuf[i].b);
// Scanline
Rs = (uint)(PalConvTbl[j][0] * m_PaletteBuf[i].r * m_nScanlineColor / 100.0f);
Gs = (uint)(PalConvTbl[j][1] * m_PaletteBuf[i].g * m_nScanlineColor / 100.0f);
Bs = (uint)(PalConvTbl[j][2] * m_PaletteBuf[i].b * m_nScanlineColor / 100.0f);
m_cnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft);
m_csPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft);
// Monochrome
Rn = (uint)(m_PaletteBuf[i & 0x30].r);
Gn = (uint)(m_PaletteBuf[i & 0x30].g);
Bn = (uint)(m_PaletteBuf[i & 0x30].b);
Rn =
Gn =
Bn = (uint)(0.299f * Rn + 0.587f * Gn + 0.114f * Bn);
Rn = (uint)(PalConvTbl[j][0] * Rn);
Gn = (uint)(PalConvTbl[j][1] * Gn);
Bn = (uint)(PalConvTbl[j][2] * Bn);
if (Rn > 0xFF) Rs = 0xFF;
if (Gn > 0xFF) Gs = 0xFF;
if (Bn > 0xFF) Bs = 0xFF;
// Scanline
Rs = (uint)(m_PaletteBuf[i & 0x30].r * m_nScanlineColor / 100.0f);
Gs = (uint)(m_PaletteBuf[i & 0x30].g * m_nScanlineColor / 100.0f);
Bs = (uint)(m_PaletteBuf[i & 0x30].b * m_nScanlineColor / 100.0f);
Rs =
Gs =
Bs = (uint)(0.299f * Rs + 0.587f * Gs + 0.114f * Bs);
Rs = (uint)(PalConvTbl[j][0] * Rs);
Gs = (uint)(PalConvTbl[j][1] * Gs);
Bs = (uint)(PalConvTbl[j][2] * Bs);
if (Rs > 0xFF) Rs = 0xFF;
if (Gs > 0xFF) Gs = 0xFF;
if (Bs > 0xFF) Bs = 0xFF;
m_mnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft);
m_msPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft);
}
}
}
// ビット位置の取得
static void GetBitMask(uint val, ref int shift, ref int bits)
{
shift = 0;
while (((val & (1 << shift)) == 0) && (shift < 32))
{
shift++;
}
bits = 32;
while (((val & (1 << (bits - 1))) == 0) && (bits > 0))
{
bits--;
}
bits = bits - shift;
}
}
}

View File

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

View File

@ -0,0 +1,82 @@
using AxibugEmuOnline.Client.Assets.Script.NesEmulator;
using Codice.CM.Client.Differences;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.UI;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client
{
public class VideoProvider : MonoBehaviour
{
public RawImage Image;
private Color32[] wrapTexBuffer;
private IntPtr wrapTexBufferPointer;
private Texture2D wrapTex;
public void SetDrawData(byte[] screenData, byte[] lineColorMode, int screenWidth, int screenHeight)
{
if (wrapTex == null)
{
wrapTex = new Texture2D(screenWidth, screenHeight, TextureFormat.BGRA32, false);
wrapTexBuffer = new Color32[screenWidth * screenHeight];
// 固定数组,防止垃圾回收器移动它
GCHandle handle = GCHandle.Alloc(wrapTexBuffer, GCHandleType.Pinned);
// 获取数组的指针
wrapTexBufferPointer = handle.AddrOfPinnedObject();
}
uint[] pPal;
int pScn = 0;
int width;
var Dst = wrapTexBuffer;
var pDst = 0;
for (int line = 0; line < screenHeight; line++)
{
if ((lineColorMode[line] & 0x80) == 0)
{
pPal = PaletteDefine.m_cnPalette[lineColorMode[line] & 0x07];
}
else
{
pPal = PaletteDefine.m_mnPalette[lineColorMode[line] & 0x07];
}
width = screenWidth;
while (width > 0)
{
var edx = screenData[pScn + 8];
byte index = (byte)(edx & 0xFF);
var colorData = pPal[index];
var rawData = BitConverter.GetBytes(colorData);
Dst[pDst] = new Color32(rawData[0], rawData[1], rawData[2], 255);
pScn += 1;
pDst += 1;
width -= 1;
}
pScn += PPU.SCREEN_WIDTH - screenWidth;
}
//wrapTex.SetPixels32(wrapTexBuffer);
wrapTex.LoadRawTextureData(wrapTexBufferPointer, screenWidth * screenHeight * 4);
wrapTex.Apply();
Graphics.Blit(wrapTex, Image.mainTexture 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)
{
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

View File

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

View File

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

View File

@ -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 : 实现
}
}
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#undef DPCM_SYNCCLOCK
using System;
using VirtualNes.Core.Debug;
namespace VirtualNes.Core
{
@ -42,7 +43,7 @@ namespace VirtualNes.Core
private int DMA_cycles;
private Mapper mapper;
private APU apu;
private R6502 R = new R6502();
internal R6502 R = new R6502();
private byte[] ZN_Table = new byte[256];
private Memory<byte> STACK;
@ -132,75 +133,77 @@ namespace VirtualNes.Core
switch (opcode)
{
case 0x69:
case 0x69: // ADC #$??
MR_IM(); ADC();
ADD_CYCLE(2);
break;
case 0x65:
case 0x65: // ADC $??
MR_ZP(); ADC();
ADD_CYCLE(3);
break;
case 0x75:
case 0x75: // ADC $??,X
MR_ZX(); ADC();
ADD_CYCLE(4);
break;
case 0x6D:
case 0x6D: // ADC $????
MR_AB(); ADC();
ADD_CYCLE(4);
break;
case 0x7D:
case 0x7D: // ADC $????,X
MR_AX(); ADC(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0x79:
case 0x79: // ADC $????,Y
MR_AY(); ADC(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0x61:
case 0x61: // ADC ($??,X)
MR_IX(); ADC();
ADD_CYCLE(6);
break;
case 0x71:
case 0x71: // ADC ($??),Y
MR_IY(); ADC(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0xE9:
case 0xE9: // SBC #$??
MR_IM(); SBC();
ADD_CYCLE(2);
break;
case 0xE5:
case 0xE5: // SBC $??
MR_ZP(); SBC();
ADD_CYCLE(3);
break;
case 0xF5:
case 0xF5: // SBC $??,X
MR_ZX(); SBC();
ADD_CYCLE(4);
break;
case 0xED:
case 0xED: // SBC $????
MR_AB(); SBC();
ADD_CYCLE(4);
break;
case 0xFD:
case 0xFD: // SBC $????,X
MR_AX(); SBC(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0xF9: // SBC $????Y
case 0xF9: // SBC $????,Y
MR_AY(); SBC(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0xE1: // SBC ($??X)
case 0xE1: // SBC ($??,X)
MR_IX(); SBC();
ADD_CYCLE(6);
break;
case 0xF1: // SBC ($??)Y
case 0xF1: // SBC ($??),Y
MR_IY(); SBC(); CHECK_EA();
ADD_CYCLE(5);
break;
case 0xC6: // DEC $??
MR_ZP(); DEC(); MW_ZP();
ADD_CYCLE(5);
break;
case 0xD6: // DEC $??X
case 0xD6: // DEC $??,X
MR_ZX(); DEC(); MW_ZP();
ADD_CYCLE(6);
break;
@ -208,10 +211,11 @@ namespace VirtualNes.Core
MR_AB(); DEC(); MW_EA();
ADD_CYCLE(6);
break;
case 0xDE: // DEC $????X
case 0xDE: // DEC $????,X
MR_AX(); DEC(); MW_EA();
ADD_CYCLE(7);
break;
case 0xCA: // DEX
DEX();
ADD_CYCLE(2);
@ -220,11 +224,12 @@ namespace VirtualNes.Core
DEY();
ADD_CYCLE(2);
break;
case 0xE6: // INC $??
MR_ZP(); INC(); MW_ZP();
ADD_CYCLE(5);
break;
case 0xF6: // INC $??X
case 0xF6: // INC $??,X
MR_ZX(); INC(); MW_ZP();
ADD_CYCLE(6);
break;
@ -232,10 +237,11 @@ namespace VirtualNes.Core
MR_AB(); INC(); MW_EA();
ADD_CYCLE(6);
break;
case 0xFE: // INC $????X
case 0xFE: // INC $????,X
MR_AX(); INC(); MW_EA();
ADD_CYCLE(7);
break;
case 0xE8: // INX
INX();
ADD_CYCLE(2);
@ -244,6 +250,7 @@ namespace VirtualNes.Core
INY();
ADD_CYCLE(2);
break;
case 0x29: // AND #$??
MR_IM(); AND();
ADD_CYCLE(2);
@ -252,7 +259,7 @@ namespace VirtualNes.Core
MR_ZP(); AND();
ADD_CYCLE(3);
break;
case 0x35: // AND $??X
case 0x35: // AND $??,X
MR_ZX(); AND();
ADD_CYCLE(4);
break;
@ -260,22 +267,23 @@ namespace VirtualNes.Core
MR_AB(); AND();
ADD_CYCLE(4);
break;
case 0x3D: // AND $????X
case 0x3D: // AND $????,X
MR_AX(); AND(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0x39: // AND $????Y
case 0x39: // AND $????,Y
MR_AY(); AND(); CHECK_EA();
ADD_CYCLE(4);
break;
case 0x21: // AND ($??X)
case 0x21: // AND ($??,X)
MR_IX(); AND();
ADD_CYCLE(6);
break;
case 0x31: // AND ($??)Y
case 0x31: // AND ($??),Y
MR_IY(); AND(); CHECK_EA();
ADD_CYCLE(5);
break;
case 0x0A: // ASL A
ASL_A();
ADD_CYCLE(2);
@ -705,7 +713,7 @@ namespace VirtualNes.Core
ADD_CYCLE(6);
break;
// 僼儔僌惂屼宯
// フラグ制御系
case 0x18: // CLC
CLC();
ADD_CYCLE(2);
@ -736,7 +744,7 @@ namespace VirtualNes.Core
ADD_CYCLE(2);
break;
// 僗僞僢僋宯
// スタック系
case 0x48: // PHA
PUSH(R.A);
ADD_CYCLE(3);
@ -755,7 +763,7 @@ namespace VirtualNes.Core
ADD_CYCLE(4);
break;
// 偦偺懠
// その他
case 0x00: // BRK
BRK();
ADD_CYCLE(7);
@ -765,7 +773,7 @@ namespace VirtualNes.Core
ADD_CYCLE(2);
break;
// 枹岞奐柦椷孮
// 未公開命令群
case 0x0B: // ANC #$??
case 0x2B: // ANC #$??
MR_IM(); ANC();
@ -1105,7 +1113,7 @@ namespace VirtualNes.Core
default:
if (!Supporter.Config.emulator.bIllegalOp)
{
throw new Exception("Illegal Opcode");
throw new Exception("IllegalOp");
}
else
{
@ -1113,6 +1121,8 @@ namespace VirtualNes.Core
ADD_CYCLE(4);
}
break;
// default:
// __assume(0);
}
if (nmi_request != 0)
@ -1143,7 +1153,6 @@ namespace VirtualNes.Core
#endif
return TOTAL_cycles - OLD_cycles;
}
private void _IRQ()
{
PUSH((byte)(R.PC >> 8));
@ -1169,7 +1178,7 @@ namespace VirtualNes.Core
return (ushort)(nes.Read(addr) + nes.Read((ushort)(addr + 1)) * 0x100);
}
var temp = MMU.CPU_MEM_BANK[addr >> 13].Span;
var temp = MMU.CPU_MEM_BANK[addr >> 13];
shortTemp[0] = temp[addr & 0x1FFF];
shortTemp[1] = temp[(addr & 0x1FFF) + 1];
return BitConverter.ToUInt16(shortTemp, 0);
@ -1716,14 +1725,14 @@ namespace VirtualNes.Core
internal byte OP6502(ushort addr)
{
return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF];
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
}
private byte[] shortTemp = new byte[2];
internal ushort OP6502W(ushort addr)
{
var bytePage = MMU.CPU_MEM_BANK[addr >> 13];
var spanByte = bytePage.Span;
var spanByte = bytePage;
shortTemp[0] = spanByte[addr & 0x1FFF];
shortTemp[1] = spanByte[(addr & 0x1FFF) + 1];
return BitConverter.ToUInt16(shortTemp, 0);
@ -1744,11 +1753,11 @@ namespace VirtualNes.Core
else
{
// Dummy access
mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF]);
mapper.Read(addr, MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF]);
}
// Quick bank read
return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF];
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
}
private void AND()
@ -1836,7 +1845,7 @@ namespace VirtualNes.Core
{
ET = OP6502W(R.PC);
R.PC += 2;
EA = (byte)(ET + R.X);
EA = (ushort)(ET + R.X);
DT = RD6502(EA);
}

View File

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

View File

@ -0,0 +1,28 @@
namespace VirtualNes.Core
{
public class ByteArrayRef
{
private byte[] m_rawArray;
private int m_offset;
private int m_length;
public ByteArrayRef(byte[] array, int offset, int length)
{
m_rawArray = array;
m_offset = offset;
m_length = length;
}
public byte this[int index]
{
get
{
return m_rawArray[m_offset + index];
}
set
{
m_rawArray[(m_offset + index)] = value;
}
}
}
}

View File

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

View File

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

View File

@ -1,4 +1,6 @@
namespace VirtualNes.Core.Debug
using System.Collections.Generic;
namespace VirtualNes.Core.Debug
{
public static class Debuger
{

View File

@ -1,15 +1,16 @@
using System;
using VirtualNes.Core;
namespace VirtualNes
{
public static class MMU
{
// CPU 儊儌儕僶儞僋
public static Memory<byte>[] CPU_MEM_BANK = new Memory<byte>[8]; // 8K扨埵
public static ByteArrayRef[] CPU_MEM_BANK = new ByteArrayRef[8]; // 8K扨埵
public static byte[] CPU_MEM_TYPE = new byte[8];
public static int[] CPU_MEM_PAGE = new int[8]; // 僗僥乕僩僙乕僽梡
// PPU 儊儌儕僶儞僋
public static Memory<byte>[] PPU_MEM_BANK = new Memory<byte>[12]; // 1K扨埵
public static ByteArrayRef[] PPU_MEM_BANK = new ByteArrayRef[12]; // 1K扨埵
public static byte[] PPU_MEM_TYPE = new byte[12];
public static int[] PPU_MEM_PAGE = new int[12]; // 僗僥乕僩僙乕僽梡
public static byte[] CRAM_USED = new byte[16]; // 僗僥乕僩僙乕僽梡
@ -67,9 +68,12 @@ namespace VirtualNes
public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕
public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕
internal static void SetPROM_Bank(byte page, Memory<byte> ptr, byte type)
// Frame-IRQ儗僕僗僞($4017)
public static int FrameIRQ;
internal static void SetPROM_Bank(byte page, byte[] ptr, byte type)
{
CPU_MEM_BANK[page] = ptr;
CPU_MEM_BANK[page] = new ByteArrayRef(ptr, 0, ptr.Length);
CPU_MEM_TYPE[page] = type;
CPU_MEM_PAGE[page] = 0;
}
@ -77,7 +81,7 @@ namespace VirtualNes
internal static void SetPROM_8K_Bank(byte page, int bank)
{
bank %= PROM_8K_SIZE;
CPU_MEM_BANK[page] = new Memory<byte>(MMU.PROM, 0x2000 * bank, MMU.PROM.Length - 0x2000 * bank);
CPU_MEM_BANK[page] = new ByteArrayRef(MMU.PROM, 0x2000 * bank, MMU.PROM.Length - 0x2000 * bank);
CPU_MEM_TYPE[page] = BANKTYPE_ROM;
CPU_MEM_PAGE[page] = bank;
}
@ -105,7 +109,7 @@ namespace VirtualNes
}
// PPU VROM bank
internal static void SetVROM_Bank(byte page, Memory<byte> ptr, byte type)
internal static void SetVROM_Bank(byte page, ByteArrayRef ptr, byte type)
{
PPU_MEM_BANK[page] = ptr;
PPU_MEM_TYPE[page] = type;
@ -115,7 +119,7 @@ namespace VirtualNes
internal static void SetVROM_1K_Bank(byte page, int bank)
{
bank %= VROM_1K_SIZE;
PPU_MEM_BANK[page] = new Memory<byte>(VROM, 0x0400 * bank, VROM.Length - (0x0400 * bank));
PPU_MEM_BANK[page] = new ByteArrayRef(VROM, 0x0400 * bank, VROM.Length - (0x0400 * bank));
PPU_MEM_TYPE[page] = BANKTYPE_VROM;
PPU_MEM_PAGE[page] = bank;
}
@ -158,7 +162,7 @@ namespace VirtualNes
internal static void SetCRAM_1K_Bank(byte page, int bank)
{
bank &= 0x1F;
PPU_MEM_BANK[page] = new Memory<byte>(MMU.CRAM, 0x0400 * bank, MMU.CRAM.Length - 0x0400 * bank);
PPU_MEM_BANK[page] = new ByteArrayRef(MMU.CRAM, 0x0400 * bank, MMU.CRAM.Length - 0x0400 * bank);
PPU_MEM_TYPE[page] = BANKTYPE_CRAM;
PPU_MEM_PAGE[page] = bank;
@ -190,7 +194,7 @@ namespace VirtualNes
internal static void SetVRAM_1K_Bank(byte page, int bank)
{
bank &= 3;
PPU_MEM_BANK[page] = new Memory<byte>(VRAM, 0x0400 * bank, VRAM.Length - 0x0400 * bank);
PPU_MEM_BANK[page] = new ByteArrayRef(VRAM, 0x0400 * bank, VRAM.Length - 0x0400 * bank);
PPU_MEM_TYPE[page] = BANKTYPE_VRAM;
PPU_MEM_PAGE[page] = bank;
}

View File

@ -5,6 +5,7 @@ namespace VirtualNes.Core
public abstract class Mapper
{
protected NES nes;
public Mapper(NES parent)
{
nes = parent;
@ -25,7 +26,7 @@ namespace VirtualNes.Core
// $6000-$7FFF WRAM
if (addr >= 0x6000 && addr <= 0x7FFF)
{
return MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF];
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
}
return (byte)(addr >> 8);
@ -34,7 +35,7 @@ namespace VirtualNes.Core
{
if (addr >= 0x6000 && addr <= 0x7FFF)
{
MMU.CPU_MEM_BANK[addr >> 13].Span[addr & 0x1FFF] = data;
MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF] = data;
}
}
@ -78,10 +79,13 @@ namespace VirtualNes.Core
public static Mapper CreateMapper(NES parent, int no)
{
//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");
}
}
}

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:

File diff suppressed because it is too large Load Diff

View File

@ -330,7 +330,7 @@ namespace VirtualNes.Core
}
}
private void SetExController(EXCONTROLLER type)
internal void SetExController(EXCONTROLLER type)
{
excontroller_select = (int)type;
@ -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

View File

@ -1,10 +1,10 @@
using Codice.CM.Client.Differences;
using System;
namespace VirtualNes.Core
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][];
@ -187,7 +187,7 @@ namespace VirtualNes.Core
}
addr &= 0xEFFF;
}
MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10].Span[addr & 0x03FF];
MMU.PPU7_Temp = MMU.PPU_MEM_BANK[addr >> 10][addr & 0x03FF];
break;
}
@ -199,7 +199,7 @@ namespace VirtualNes.Core
ScanlineNo = scanline;
if (scanline < 240)
{
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline;
lpScanline = (SCREEN_WIDTH) * scanline;
}
}
@ -323,7 +323,7 @@ namespace VirtualNes.Core
}
if (MMU.PPU_MEM_TYPE[vaddr >> 10] != MMU.BANKTYPE_VROM)
{
MMU.PPU_MEM_BANK[vaddr >> 10].Span[vaddr & 0x03FF] = data;
MMU.PPU_MEM_BANK[vaddr >> 10][vaddr & 0x03FF] = data;
}
break;
}
@ -358,15 +358,810 @@ 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);
}
private enum Screen
internal void FrameStart()
{
SCREEN_WIDTH = 256 + 16,
SCREEN_HEIGHT = 240
if ((MMU.PPUREG[1] & (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) != 0)
{
MMU.loopy_v = MMU.loopy_t;
loopy_shift = MMU.loopy_x;
loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12);
}
if (lpScreen != null)
{
MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH);
}
if (lpColormode != null)
{
lpColormode[0] = 0;
}
}
internal void ScanlineNext()
{
if ((MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0)
{
if ((MMU.loopy_v & 0x7000) == 0x7000)
{
MMU.loopy_v &= 0x8FFF;
if ((MMU.loopy_v & 0x03E0) == 0x03A0)
{
MMU.loopy_v ^= 0x0800;
MMU.loopy_v &= 0xFC1F;
}
else
{
if ((MMU.loopy_v & 0x03E0) == 0x03E0)
{
MMU.loopy_v &= 0xFC1F;
}
else
{
MMU.loopy_v += 0x0020;
}
}
}
else
{
MMU.loopy_v += 0x1000;
}
loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12);
}
}
internal void ScanlineStart()
{
if ((MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0)
{
MMU.loopy_v = (ushort)((MMU.loopy_v & 0xFBE0) | (MMU.loopy_t & 0x041F));
loopy_shift = MMU.loopy_x;
loopy_y = (ushort)((MMU.loopy_v & 0x7000) >> 12);
nes.mapper.PPU_Latch((ushort)(0x2000 + (MMU.loopy_v & 0x0FFF)));
}
}
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)
{
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];
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][tileadr & 0x03FF];
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(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];
}
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];
int tileadr = 0;
int cache_tile = unchecked((int)(0xFFFF0000));
byte cache_attr = 0xFF;
chr_h = chr_l = attr = 0;
for (int i = 0; i < 33; i++)
{
tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y;
if (i != 0)
{
nes.EmulationCPU(NES.FETCH_CYCLES * 4);
}
attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2);
if (cache_tile != tileadr || cache_attr != attr)
{
cache_tile = tileadr;
cache_attr = attr;
chr_l = MMU.PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
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];
}
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][ntbladr & 0x03FF] * 0x10 + ((MMU.loopy_v & 0x7000) >> 12);
attr = (byte)(((MMU.PPU_MEM_BANK[ntbladr >> 10][0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2)] >> (((ntbladr & 0x40) >> 4) + (ntbladr & 0x02))) & 3) << 2);
if (cache_tile != tileadr || cache_attr != attr)
{
cache_tile = tileadr;
cache_attr = attr;
chr_l = MMU.PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
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][spraddr & 0x3FF];
chr_h = MMU.PPU_MEM_BANK[spraddr >> 10][(spraddr & 0x3FF) + 8];
// Character latch(For MMC2/MMC4)
if (bChrLatch)
{
nes.mapper.PPU_ChrLatch((ushort)spraddr);
}
// pattern mask
if ((sp.attr & SP_HMIRROR_BIT) != 0)
{
chr_l = 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;
}
public byte[] GetLineColorMode()
{
return lpColormode;
}
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);
name = Path.GetFileNameWithoutExtension(fullpath);
if (!bNSF)
{
mapper = (header.control1 >> 4) | (header.control2 & 0xF0);
@ -347,12 +347,12 @@ namespace VirtualNes.Core
return diskno;
}
internal ulong GetGameID()
internal uint GetGameID()
{
return fdsgameID;
}
internal ulong GetMakerID()
internal uint GetMakerID()
{
return fdsmakerID;
}
@ -396,6 +396,11 @@ namespace VirtualNes.Core
{
return nsfheader;
}
internal string GetRomPath()
{
return path;
}
}

View File

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

View File

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

View File

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

View File

@ -17,4 +17,35 @@
public CfgExtraSound extsound { get; private set; } = new CfgExtraSound();
public CfgNetPlay netplay { get; private set; } = new CfgNetPlay();
}
public static class GameOption
{
// Default保存
public static int defRenderMethod;
public static int defIRQtype;
public static bool defFrameIRQ;
public static bool defVideoMode;
// データ
public static int nRenderMethod;
public static int nIRQtype;
public static bool bFrameIRQ;
public static bool bVideoMode;
public static void Load(uint crc)
{
nRenderMethod = defRenderMethod;
nIRQtype = defIRQtype;
bFrameIRQ = defFrameIRQ;
bVideoMode = defVideoMode;
}
public static void Load(uint gid, uint mid)
{
nRenderMethod = defRenderMethod;
nIRQtype = defIRQtype;
bFrameIRQ = defFrameIRQ;
bVideoMode = defVideoMode;
}
}
}

View File

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

View File

@ -0,0 +1,49 @@
namespace VirtualNes.Core
{
public class VSDIPSWITCH
{
public string name;
public ushort value;
}
public static class VsUnisystem
{
public static VSDIPSWITCH[] vsdip_default = new VSDIPSWITCH[]
{
new VSDIPSWITCH{name="Unknown", value= 0x0100},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x01},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x0200},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x02},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x0400},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x04},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x0800},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x08},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x1000},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x10},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x2000},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x20},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x4000},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x40},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name="Unknown", value= 0x8000},
new VSDIPSWITCH{name="Off", value= 0x00},
new VSDIPSWITCH{name="On", value= 0x80},
new VSDIPSWITCH{name=null, value= 0xFF},
new VSDIPSWITCH{name=null, value= 0 },
};
}
}

View File

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

167
virtuanessrc097-master/.gitignore vendored Normal file
View File

@ -0,0 +1,167 @@
VirtuaNES.exe
VirtuaNES.ini
#################
## Eclipse
#################
.vs/
debug/
*.pydevproject
.project
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.classpath
.settings/
.loadpath
# External tool builders
.externalToolBuilders/
# Locally stored "Eclipse launch configurations"
*.launch
# CDT-specific
.cproject
# PDT-specific
.buildpath
#################
## Visual Studio
#################
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
.builds
*.dotCover
## TODO: If you have NuGet Package Restore enabled, uncomment this
#packages/
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# ReSharper is a .NET coding add-in
_ReSharper*
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Others
[Bb]in
[Oo]bj
sql
TestResults
*.Cache
ClientBin
stylecop.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
############
## Windows
############
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
#############
## Python
#############
*.py[co]
# Packages
*.egg
*.egg-info
dist
build
eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
#Translations
*.mo
#Mr Developer
.mr.developer.cfg
# Mac crap
.DS_Store

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More