forked from sin365/AxibugEmuOnline
迭代中
This commit is contained in:
parent
d7846c7182
commit
e5fa62d209
@ -152,6 +152,7 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: 39557e19783acee499ace6c68549e8f8, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
VideoProvider: {fileID: 1295252151}
|
||||
--- !u!4 &149545948
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -162,7 +163,8 @@ Transform:
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Children:
|
||||
- {fileID: 1295252150}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@ -259,6 +261,78 @@ Transform:
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
|
||||
--- !u!1 &861747459
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 861747460}
|
||||
- component: {fileID: 861747462}
|
||||
- component: {fileID: 861747461}
|
||||
m_Layer: 5
|
||||
m_Name: RawImage
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &861747460
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 861747459}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1727033528}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &861747461
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 861747459}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Texture: {fileID: 8400000, guid: ffe34aaf87e4b9942b4c2ac05943d444, type: 2}
|
||||
m_UVRect:
|
||||
serializedVersion: 2
|
||||
x: 0
|
||||
y: 0
|
||||
width: 1
|
||||
height: 1
|
||||
--- !u!222 &861747462
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 861747459}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!1 &1232273651
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -342,6 +416,51 @@ Transform:
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1295252149
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1295252150}
|
||||
- component: {fileID: 1295252151}
|
||||
m_Layer: 0
|
||||
m_Name: VideoProvider
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1295252150
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1295252149}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 1727033528}
|
||||
m_Father: {fileID: 149545948}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &1295252151
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1295252149}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 83fbe375412d1af4482ae76e81c1dda2, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
Image: {fileID: 861747461}
|
||||
--- !u!1 &1359344831
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -408,3 +527,103 @@ Transform:
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 1
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!1 &1727033527
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1727033528}
|
||||
- component: {fileID: 1727033531}
|
||||
- component: {fileID: 1727033530}
|
||||
- component: {fileID: 1727033529}
|
||||
m_Layer: 5
|
||||
m_Name: Canvas
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1727033528
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1727033527}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_Children:
|
||||
- {fileID: 861747460}
|
||||
m_Father: {fileID: 1295252150}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0, y: 0}
|
||||
--- !u!114 &1727033529
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1727033527}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_IgnoreReversedGraphics: 1
|
||||
m_BlockingObjects: 0
|
||||
m_BlockingMask:
|
||||
serializedVersion: 2
|
||||
m_Bits: 4294967295
|
||||
--- !u!114 &1727033530
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1727033527}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_UiScaleMode: 0
|
||||
m_ReferencePixelsPerUnit: 100
|
||||
m_ScaleFactor: 1
|
||||
m_ReferenceResolution: {x: 800, y: 600}
|
||||
m_ScreenMatchMode: 0
|
||||
m_MatchWidthOrHeight: 0
|
||||
m_PhysicalUnit: 3
|
||||
m_FallbackScreenDPI: 96
|
||||
m_DefaultSpriteDPI: 96
|
||||
m_DynamicPixelsPerUnit: 1
|
||||
m_PresetInfoIsWorld: 0
|
||||
--- !u!223 &1727033531
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1727033527}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 0
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using VirtualNes.Core;
|
||||
using VirtualNes.Core.Debug;
|
||||
@ -8,6 +9,8 @@ namespace AxibugEmuOnline.Client
|
||||
{
|
||||
private NES m_nesIns;
|
||||
|
||||
public VideoProvider VideoProvider;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
StartGame("Kirby.nes");
|
||||
@ -19,9 +22,18 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
Supporter.Setup(new CoreSupporter());
|
||||
Debuger.Setup(new CoreDebuger());
|
||||
|
||||
try
|
||||
{
|
||||
m_nesIns = new NES(romName);
|
||||
m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_nesIns = null;
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopGame()
|
||||
{
|
||||
@ -31,7 +43,12 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
private void Update()
|
||||
{
|
||||
m_nesIns?.EmulateFrame(true);
|
||||
if (m_nesIns != null)
|
||||
{
|
||||
m_nesIns.EmulateFrame(true);
|
||||
var screenBuffer = m_nesIns.ppu.GetScreenPtr();
|
||||
VideoProvider.SetDrawData(screenBuffer, PPU.SCREEN_WIDTH, PPU.SCREEN_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffe34aaf87e4b9942b4c2ac05943d444
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 8400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83fbe375412d1af4482ae76e81c1dda2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 : 实现
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,9 @@ namespace VirtualNes
|
||||
public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕
|
||||
public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕
|
||||
|
||||
// Frame-IRQ儗僕僗僞($4017)
|
||||
public static int FrameIRQ;
|
||||
|
||||
internal static void SetPROM_Bank(byte page, Memory<byte> ptr, byte type)
|
||||
{
|
||||
CPU_MEM_BANK[page] = ptr;
|
||||
|
@ -5,6 +5,7 @@ namespace VirtualNes.Core
|
||||
public abstract class Mapper
|
||||
{
|
||||
protected NES nes;
|
||||
|
||||
public Mapper(NES parent)
|
||||
{
|
||||
nes = parent;
|
||||
@ -81,8 +82,10 @@ namespace VirtualNes.Core
|
||||
//todo : 实现加载mapper
|
||||
switch (no)
|
||||
{
|
||||
case 4:
|
||||
return new Mapper004(parent);
|
||||
default:
|
||||
throw new NotImplementedException($"Mapper#{no} is not Impl");
|
||||
throw new NotImplementedException($"Mapper#{no:000} is not Impl");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1be6fcc00c619e84cbf0d5d92701b2e3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,8 +1,15 @@
|
||||
using Codice.CM.Client.Differences;
|
||||
using Google.Protobuf.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using UnityEngine.UIElements;
|
||||
using UnityEngine;
|
||||
using VirtualNes.Core.Debug;
|
||||
using Unity.VisualScripting.Antlr3.Runtime.Tree;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
@ -30,6 +37,9 @@ namespace VirtualNes.Core
|
||||
private bool m_bMoviePlay;
|
||||
private bool m_bMovieRec;
|
||||
private Stream m_fpMovie;
|
||||
private uint m_MovieControl;
|
||||
private int m_MovieStepTotal;
|
||||
private int m_MovieStep;
|
||||
private bool m_bTapePlay;
|
||||
private bool m_bTapeRec;
|
||||
private Stream m_fpTape;
|
||||
@ -60,9 +70,107 @@ namespace VirtualNes.Core
|
||||
private int NES_scanline;
|
||||
private EnumRenderMethod RenderMethod;
|
||||
private bool bZapper;
|
||||
private int ZapperX;
|
||||
private int ZapperY;
|
||||
private long base_cycles;
|
||||
private long emul_cycles;
|
||||
|
||||
private byte[] m_PadImg = new byte[226]
|
||||
{
|
||||
28, 8,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
|
||||
0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
|
||||
0x0F, 0x0F, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x00, 0x0F,
|
||||
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
private byte[] m_KeyImg0 = new byte[6]
|
||||
{
|
||||
2, 2,
|
||||
0x2A, 0x2A,
|
||||
0x2A, 0x2A,
|
||||
};
|
||||
|
||||
private byte[] m_KeyImg1 = new byte[8]
|
||||
{
|
||||
3, 3,
|
||||
0x2A, 0x2A, 0x2A,
|
||||
0x2A, 0x2A, 0x2A,
|
||||
};
|
||||
|
||||
private byte[] m_KeyImg2 = new byte[18]
|
||||
{
|
||||
4, 4,
|
||||
0xFF, 0x2A, 0x2A, 0xFF,
|
||||
0x2A, 0x2A, 0x2A, 0x2A,
|
||||
0x2A, 0x2A, 0x2A, 0x2A,
|
||||
0xFF, 0x2A, 0x2A, 0xFF,
|
||||
};
|
||||
|
||||
private byte[] Font6x8 = new byte[768]
|
||||
{
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,
|
||||
0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x50,0xF8,0x50,0xF8,0x50,0x50,0x00,
|
||||
0x20,0x78,0xA0,0x70,0x28,0xF0,0x20,0x00,0x48,0xB0,0x50,0x20,0x50,0x68,0x90,0x00,
|
||||
0x40,0xA0,0xA8,0x68,0x90,0x90,0x68,0x00,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x10,0x20,0x40,0x40,0x40,0x20,0x10,0x00,0x40,0x20,0x10,0x10,0x10,0x20,0x40,0x00,
|
||||
0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x20,0x40,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x08,0x10,0x10,0x20,0x40,0x40,0x80,0x00,
|
||||
0x70,0x88,0x98,0xA8,0xC8,0x88,0x70,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0xF8,0x00,
|
||||
0x70,0x88,0x08,0x30,0x40,0x80,0xF8,0x00,0x70,0x88,0x08,0x30,0x08,0x88,0x70,0x00,
|
||||
0x30,0x50,0x90,0x90,0xF8,0x10,0x10,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0xF0,0x00,
|
||||
0x70,0x88,0x80,0xF0,0x88,0x88,0x70,0x00,0xF8,0x08,0x10,0x10,0x20,0x20,0x20,0x00,
|
||||
0x70,0x88,0x88,0x70,0x88,0x88,0x70,0x00,0x70,0x88,0x88,0x78,0x08,0x88,0x70,0x00,
|
||||
0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x40,0x00,
|
||||
0x10,0x20,0x40,0x80,0x40,0x20,0x10,0x00,0x00,0x00,0xF8,0x00,0xF8,0x00,0x00,0x00,
|
||||
0x40,0x20,0x10,0x08,0x10,0x20,0x40,0x00,0x70,0x88,0x08,0x10,0x20,0x00,0x20,0x00,
|
||||
0x30,0x48,0x88,0x98,0xA8,0xA8,0x78,0x00,0x20,0x50,0x50,0x88,0xF8,0x88,0x88,0x00,
|
||||
0xF0,0x88,0x88,0xF0,0x88,0x88,0xF0,0x00,0x70,0x88,0x80,0x80,0x80,0x88,0x70,0x00,
|
||||
0xF0,0x88,0x88,0x88,0x88,0x88,0xF0,0x00,0xF8,0x80,0x80,0xF0,0x80,0x80,0xF8,0x00,
|
||||
0xF8,0x80,0x80,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x80,0xB8,0x88,0x88,0x70,0x00,
|
||||
0x88,0x88,0x88,0xF8,0x88,0x88,0x88,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,
|
||||
0x38,0x08,0x08,0x08,0x08,0x88,0x70,0x00,0x88,0x88,0x90,0xE0,0x90,0x88,0x88,0x00,
|
||||
0x80,0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x88,0xD8,0xA8,0xA8,0xA8,0xA8,0xA8,0x00,
|
||||
0x88,0xC8,0xA8,0xA8,0xA8,0x98,0x88,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
|
||||
0xF0,0x88,0x88,0xF0,0x80,0x80,0x80,0x00,0x70,0x88,0x88,0x88,0xA8,0x90,0x68,0x00,
|
||||
0xF0,0x88,0x88,0xF0,0x88,0x88,0x88,0x00,0x70,0x88,0x80,0x70,0x08,0x88,0x70,0x00,
|
||||
0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,
|
||||
0x88,0x88,0x88,0x50,0x50,0x50,0x20,0x00,0x88,0xA8,0xA8,0xA8,0xA8,0xD8,0x88,0x00,
|
||||
0x88,0x88,0x50,0x20,0x50,0x88,0x88,0x00,0x88,0x88,0x88,0x70,0x20,0x20,0x20,0x00,
|
||||
0xF8,0x08,0x10,0x20,0x40,0x80,0xF8,0x00,0x70,0x40,0x40,0x40,0x40,0x40,0x70,0x00,
|
||||
0x88,0x50,0xF8,0x20,0xF8,0x20,0x20,0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x70,0x00,
|
||||
0x20,0x50,0x88,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x00,
|
||||
0x80,0xC0,0xE0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x70,0x08,0x78,0x88,0xF8,0x00,
|
||||
0x80,0x80,0x80,0xF0,0x88,0x88,0xF0,0x00,0x00,0x00,0x78,0x80,0x80,0x80,0x78,0x00,
|
||||
0x08,0x08,0x08,0x78,0x88,0x88,0x78,0x00,0x00,0x00,0x70,0x88,0xF8,0x80,0x78,0x00,
|
||||
0x18,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0xF0,0x00,
|
||||
0x80,0x80,0x80,0xF0,0x88,0x88,0x88,0x00,0x20,0x00,0x20,0x20,0x20,0x20,0x20,0x00,
|
||||
0x20,0x00,0x20,0x20,0x20,0x20,0xC0,0x00,0x80,0x80,0x88,0x90,0xE0,0x90,0x88,0x00,
|
||||
0x20,0x20,0x20,0x20,0x20,0x20,0x30,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,
|
||||
0x00,0x00,0xF0,0x88,0x88,0x88,0x88,0x00,0x00,0x00,0x70,0x88,0x88,0x88,0x70,0x00,
|
||||
0x00,0x00,0xF0,0x88,0xF0,0x80,0x80,0x00,0x00,0x00,0x78,0x88,0x78,0x08,0x08,0x00,
|
||||
0x00,0x00,0xB8,0xC0,0x80,0x80,0x80,0x00,0x00,0x00,0x78,0x80,0x70,0x08,0xF0,0x00,
|
||||
0x20,0x20,0xF8,0x20,0x20,0x20,0x20,0x00,0x00,0x00,0x88,0x88,0x88,0x88,0x70,0x00,
|
||||
0x00,0x00,0x88,0x88,0x50,0x50,0x20,0x00,0x00,0x00,0x88,0xA8,0xA8,0xD8,0x88,0x00,
|
||||
0x00,0x00,0x88,0x50,0x20,0x50,0x88,0x00,0x00,0x00,0x88,0x88,0x78,0x08,0xF0,0x00,
|
||||
0x00,0x00,0xF8,0x08,0x70,0x80,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
|
||||
};
|
||||
|
||||
public NES(string fname)
|
||||
{
|
||||
Debuger.Log("VirtuaNES - CSharpCore\n");
|
||||
@ -113,6 +221,11 @@ namespace VirtualNes.Core
|
||||
Debuger.Log("Allocating PPU...");
|
||||
ppu = new PPU(this);
|
||||
|
||||
var screenBuffer = new byte[PPU.SCREEN_WIDTH * PPU.SCREEN_HEIGHT];
|
||||
var colormode = new byte[PPU.SCREEN_HEIGHT];
|
||||
|
||||
ppu.SetScreenPtr(screenBuffer, colormode);
|
||||
|
||||
Debuger.Log("Allocating APU...");
|
||||
apu = new APU(this);
|
||||
|
||||
@ -121,10 +234,124 @@ namespace VirtualNes.Core
|
||||
|
||||
Debuger.Log("Loading ROM Image...");
|
||||
rom = new ROM(fname);
|
||||
|
||||
mapper = Mapper.CreateMapper(this, rom.GetMapperNo());
|
||||
|
||||
Debuger.Log("OK");
|
||||
|
||||
Debuger.Log($"{rom.GetRomName()}");
|
||||
Debuger.Log($"Mapper : #{rom.GetMapperNo():D3}");
|
||||
Debuger.Log($"PRG SIZE : {16 * rom.GetPROM_SIZE():4:0000}K");
|
||||
Debuger.Log($"CHR SIZE : {8 * rom.GetVROM_SIZE():4:0000}K");
|
||||
|
||||
Debuger.Log($"V MIRROR :{rom.IsVMIRROR()}");
|
||||
Debuger.Log($"4 SCREEN :{rom.Is4SCREEN()}");
|
||||
Debuger.Log($"SAVE RAM :{rom.IsSAVERAM()}");
|
||||
Debuger.Log($"TRAINER :{rom.IsTRAINER()}");
|
||||
Debuger.Log($"VS-Unisystem :{rom.IsVSUNISYSTEM()}");
|
||||
|
||||
NesSub_MemoryInitial();
|
||||
LoadSRAM();
|
||||
LoadDISK();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debuger.LogError(ex.ToString());
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDISK()
|
||||
{
|
||||
//todo : エナオ忞巐チネ。ヨァウヨ
|
||||
}
|
||||
|
||||
private void LoadSRAM()
|
||||
{
|
||||
if (rom.IsNSF())
|
||||
return;
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.WRAM, MMU.WRAM.Length);
|
||||
|
||||
if (!rom.IsSAVERAM())
|
||||
return;
|
||||
|
||||
var saveFileDir = Supporter.Config.path.szSavePath;
|
||||
var saveFileName = $"{rom.GetRomName()}.sav";
|
||||
|
||||
var fp = Supporter.OpenFile(saveFileDir, saveFileName);
|
||||
|
||||
try
|
||||
{
|
||||
if (fp == null)
|
||||
{
|
||||
throw new Exception("not find ram file to read");
|
||||
}
|
||||
|
||||
Debuger.Log("Loading SAVERAM...");
|
||||
|
||||
int size = (int)fp.Length;
|
||||
if (size <= 128 * 1024)
|
||||
{
|
||||
fp.Read(MMU.WRAM, 0, size);
|
||||
}
|
||||
Debuger.Log("OK.");
|
||||
fp.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
fp?.Close();
|
||||
fp = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void NesSub_MemoryInitial()
|
||||
{
|
||||
int i;
|
||||
|
||||
// メモリクリア
|
||||
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,10 +428,392 @@ namespace VirtualNes.Core
|
||||
}
|
||||
else if (scanline < 240)
|
||||
{
|
||||
if (RenderMethod < EnumRenderMethod.POST_RENDER)
|
||||
{
|
||||
if (RenderMethod == EnumRenderMethod.POST_ALL_RENDER)
|
||||
EmulationCPU(nescfg.ScanlineCycles);
|
||||
if (bDraw)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pad.IsZapperMode() && scanline == ZapperY)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ppu.IsSprite0(scanline))
|
||||
{
|
||||
ppu.DummyScanline(scanline);
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
}
|
||||
}
|
||||
}
|
||||
ppu.ScanlineNext(); // これの位置でラスター系は画面が違う
|
||||
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 1~239)
|
||||
if (bDraw)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
EmulationCPU(FETCH_CYCLES * 22);
|
||||
ppu.ScanlineStart();
|
||||
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pad.IsZapperMode() && scanline == ZapperY)
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
EmulationCPU(FETCH_CYCLES * 22);
|
||||
ppu.ScanlineStart();
|
||||
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ppu.IsSprite0(scanline))
|
||||
{
|
||||
// H-Draw (4fetches*32)
|
||||
EmulationCPU(FETCH_CYCLES * 128);
|
||||
ppu.DummyScanline(scanline);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
EmulationCPU(FETCH_CYCLES * 22);
|
||||
ppu.ScanlineStart();
|
||||
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
|
||||
}
|
||||
else
|
||||
{
|
||||
ppu.Scanline(scanline, Supporter.Config.graphics.bAllSprite, Supporter.Config.graphics.bLeftClip);
|
||||
ppu.ScanlineNext();
|
||||
EmulationCPU(FETCH_CYCLES * 10);
|
||||
mapper.HSync(scanline);
|
||||
EmulationCPU(FETCH_CYCLES * 22);
|
||||
ppu.ScanlineStart();
|
||||
EmulationCPU(FETCH_CYCLES * 10 + nescfg.ScanlineEndCycles);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (scanline == 240)
|
||||
{
|
||||
// ダミースキャンライン (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;
|
||||
}
|
||||
}
|
||||
|
||||
internal void EmulationCPU(int basecycles)
|
||||
@ -227,27 +836,27 @@ namespace VirtualNes.Core
|
||||
SaveTurboFile();
|
||||
|
||||
// RAM Clear
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
|
||||
if (rom.GetPROM_CRC() == 0x29401686)
|
||||
{ // Minna no Taabou no Nakayoshi Dai Sakusen(J)
|
||||
MemoryUtility.memset(MMU.RAM, 0xFF, (uint)MMU.RAM.Length);
|
||||
MemoryUtility.memset(MMU.RAM, 0xFF, MMU.RAM.Length);
|
||||
}
|
||||
|
||||
// RAM set
|
||||
if (!rom.IsSAVERAM() && rom.GetMapperNo() != 20)
|
||||
{
|
||||
MemoryUtility.memset(MMU.WRAM, 0xFF, (uint)MMU.WRAM.Length);
|
||||
MemoryUtility.memset(MMU.WRAM, 0xFF, MMU.WRAM.Length);
|
||||
}
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.CRAM, (uint)MMU.CRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.VRAM, (uint)MMU.VRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.CRAM, MMU.CRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.VRAM, MMU.VRAM.Length);
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPRAM, (uint)MMU.SPRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.BGPAL, (uint)MMU.BGPAL.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPPAL, (uint)MMU.SPPAL.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPRAM, MMU.SPRAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.BGPAL, MMU.BGPAL.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.SPPAL, MMU.SPPAL.Length);
|
||||
|
||||
MemoryUtility.ZEROMEMORY(MMU.CPUREG, (uint)MMU.CPUREG.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.PPUREG, (uint)MMU.PPUREG.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.CPUREG, MMU.CPUREG.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.PPUREG, MMU.PPUREG.Length);
|
||||
|
||||
m_bDiskThrottle = false;
|
||||
|
||||
@ -365,7 +974,7 @@ namespace VirtualNes.Core
|
||||
{
|
||||
if (m_bNsfInit)
|
||||
{
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, (uint)MMU.RAM.Length);
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, MMU.RAM.Length);
|
||||
if ((rom.GetNsfHeader().ExtraChipSelect & 0x04) == 0)
|
||||
{
|
||||
MemoryUtility.ZEROMEMORY(MMU.RAM, 0x2000);
|
||||
@ -427,7 +1036,54 @@ namespace VirtualNes.Core
|
||||
|
||||
internal void CheatCodeProcess()
|
||||
{
|
||||
//todo : 实现作弊码
|
||||
foreach (var it in m_CheatCode)
|
||||
{
|
||||
if ((it.enable & CHEATCODE.CHEAT_ENABLE) == 0)
|
||||
continue;
|
||||
|
||||
switch (it.type)
|
||||
{
|
||||
case CHEATCODE.CHEAT_TYPE_ALWAYS:
|
||||
CheatWrite(it.length, it.address, it.data);
|
||||
break;
|
||||
case CHEATCODE.CHEAT_TYPE_ONCE:
|
||||
CheatWrite(it.length, it.address, it.data);
|
||||
it.enable = 0;
|
||||
break;
|
||||
case CHEATCODE.CHEAT_TYPE_GREATER:
|
||||
if (CheatRead(it.length, it.address) > it.data)
|
||||
{
|
||||
CheatWrite(it.length, it.address, it.data);
|
||||
}
|
||||
break;
|
||||
case CHEATCODE.CHEAT_TYPE_LESS:
|
||||
if (CheatRead(it.length, it.address) < it.data)
|
||||
{
|
||||
CheatWrite(it.length, it.address, it.data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private uint CheatRead(byte length, ushort addr)
|
||||
{
|
||||
uint data = 0;
|
||||
for (int i = 0; i <= length; i++)
|
||||
{
|
||||
data |= (uint)(Read((ushort)(addr + i)) * (1 << (i * 8)));
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private void CheatWrite(int length, ushort addr, uint data)
|
||||
{
|
||||
for (int i = 0; i <= length; i++)
|
||||
{
|
||||
Write((ushort)(addr + i), (byte)(data & 0xFF));
|
||||
data >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@ -515,7 +1171,23 @@ namespace VirtualNes.Core
|
||||
|
||||
private void SaveTurboFile()
|
||||
{
|
||||
//todo : 实现
|
||||
int i;
|
||||
|
||||
if (pad.GetExController() != (int)EXCONTROLLER.EXCONTROLLER_TURBOFILE)
|
||||
return;
|
||||
|
||||
for (i = 0; i < MMU.ERAM.Length; i++)
|
||||
{
|
||||
if (MMU.ERAM[i] != 0x00)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < MMU.ERAM.Length)
|
||||
{
|
||||
Debuger.Log("Saving TURBOFILE...");
|
||||
|
||||
Supporter.SaveFile(MMU.ERAM, Supporter.Config.path.szSavePath, "TurboFile.vtf");
|
||||
}
|
||||
}
|
||||
|
||||
internal void Clock(int cycles)
|
||||
@ -564,7 +1236,53 @@ namespace VirtualNes.Core
|
||||
|
||||
internal void Tape(int cycles)
|
||||
{
|
||||
//todo : 实现Tape (目测是记录玩家操作再Play,优先级很低)
|
||||
if (!(IsTapePlay() || IsTapeRec()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((m_TapeCycles -= (double)cycles) > 0)
|
||||
return;
|
||||
|
||||
m_TapeCycles += (nescfg.CpuClock / 32000.0);
|
||||
// m_TapeCycles += (nescfg->CpuClock / 22050.0); // 遅すぎてダメっぽい
|
||||
|
||||
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)
|
||||
@ -825,5 +1543,25 @@ namespace VirtualNes.Core
|
||||
{
|
||||
return bFrameIRQ;
|
||||
}
|
||||
|
||||
internal EnumRenderMethod GetRenderMethod()
|
||||
{
|
||||
return RenderMethod;
|
||||
}
|
||||
|
||||
internal void SetIrqType(IRQMETHOD nType)
|
||||
{
|
||||
nIRQtype = (int)nType;
|
||||
}
|
||||
|
||||
internal int GetScanline()
|
||||
{
|
||||
return NES_scanline;
|
||||
}
|
||||
|
||||
public enum IRQMETHOD
|
||||
{
|
||||
IRQ_HSYNC = 0, IRQ_CLOCK = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -1,10 +1,18 @@
|
||||
using Codice.CM.Client.Differences;
|
||||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System.IO;
|
||||
using Unity.VisualScripting.Antlr3.Runtime.Tree;
|
||||
using UnityEngine;
|
||||
using static VirtualNes.Core.PPU;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class PPU
|
||||
{
|
||||
public const int SCREEN_WIDTH = 256 + 16;
|
||||
public const int SCREEN_HEIGHT = 240;
|
||||
|
||||
private static byte[][] CreateCOLORMAP()
|
||||
{
|
||||
byte[][] res = new byte[5][];
|
||||
@ -199,7 +207,7 @@ namespace VirtualNes.Core
|
||||
ScanlineNo = scanline;
|
||||
if (scanline < 240)
|
||||
{
|
||||
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline;
|
||||
lpScanline = (SCREEN_WIDTH) * scanline;
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,9 +366,9 @@ namespace VirtualNes.Core
|
||||
loopy_shift = 0;
|
||||
|
||||
if (lpScreen != null)
|
||||
MemoryUtility.memset(lpScreen, 0x3F, (int)(Screen.SCREEN_WIDTH) * (int)(Screen.SCREEN_HEIGHT));
|
||||
MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH * SCREEN_HEIGHT);
|
||||
if (lpColormode != null)
|
||||
MemoryUtility.memset(lpColormode, 0, (int)(Screen.SCREEN_HEIGHT));
|
||||
MemoryUtility.memset(lpColormode, 0, SCREEN_HEIGHT);
|
||||
}
|
||||
|
||||
internal void FrameStart()
|
||||
@ -374,7 +382,7 @@ namespace VirtualNes.Core
|
||||
|
||||
if (lpScreen != null)
|
||||
{
|
||||
MemoryUtility.memset(lpScreen, 0x3F, (int)Screen.SCREEN_WIDTH);
|
||||
MemoryUtility.memset(lpScreen, 0x3F, SCREEN_WIDTH);
|
||||
}
|
||||
if (lpColormode != null)
|
||||
{
|
||||
@ -425,10 +433,738 @@ namespace VirtualNes.Core
|
||||
}
|
||||
}
|
||||
|
||||
private enum Screen
|
||||
private byte[] BGwrite = new byte[33 + 1];
|
||||
private byte[] BGmono = new byte[33 + 1];
|
||||
private byte[] SPwrite = new byte[33 + 1];
|
||||
|
||||
internal void Scanline(int scanline, bool bMax, bool bLeftClip)
|
||||
{
|
||||
SCREEN_WIDTH = 256 + 16,
|
||||
SCREEN_HEIGHT = 240
|
||||
int pScn = 0;
|
||||
int pBGw = 0;
|
||||
byte chr_h = 0, chr_l = 0, attr = 0;
|
||||
|
||||
MemoryUtility.ZEROMEMORY(BGwrite, BGwrite.Length);
|
||||
MemoryUtility.ZEROMEMORY(BGmono, BGmono.Length);
|
||||
|
||||
// Linecolor mode
|
||||
lpColormode[scanline] = (byte)(((MMU.PPUREG[1] & PPU_BGCOLOR_BIT) >> 5) | ((MMU.PPUREG[1] & PPU_COLORMODE_BIT) << 7));
|
||||
|
||||
// Render BG
|
||||
if ((MMU.PPUREG[1] & PPU_BGDISP_BIT) == 0)
|
||||
{
|
||||
MemoryUtility.memset(lpScreen, lpScanline, MMU.BGPAL[0], SCREEN_WIDTH);
|
||||
if (nes.GetRenderMethod() == EnumRenderMethod.TILE_RENDER)
|
||||
{
|
||||
nes.EmulationCPU(NES.FETCH_CYCLES * 4 * 32);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nes.GetRenderMethod() != EnumRenderMethod.TILE_RENDER)
|
||||
{
|
||||
if (!bExtLatch)
|
||||
{
|
||||
// Without Extension Latch
|
||||
pScn = lpScanline + (8 - loopy_shift);
|
||||
pBGw = 0;
|
||||
|
||||
int tileofs = (MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8;
|
||||
int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF);
|
||||
int attradr = 0x23C0 + (MMU.loopy_v & 0x0C00) + ((MMU.loopy_v & 0x0380) >> 4);
|
||||
int ntbl_x = ntbladr & 0x001F;
|
||||
int attrsft = (ntbladr & 0x0040) >> 4;
|
||||
var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span;
|
||||
|
||||
int tileadr;
|
||||
int cache_tile = unchecked((int)(0xFFFF0000));
|
||||
byte cache_attr = 0xFF;
|
||||
|
||||
chr_h = chr_l = attr = 0;
|
||||
|
||||
attradr &= 0x3FF;
|
||||
|
||||
|
||||
for (int i = 0; i < 33; i++)
|
||||
{
|
||||
|
||||
tileadr = tileofs + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y;
|
||||
attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2);
|
||||
|
||||
if (cache_tile == tileadr && cache_attr == attr)
|
||||
{
|
||||
lpScreen[pScn + 0] = lpScreen[pScn - 8];
|
||||
lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1];
|
||||
lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2];
|
||||
lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3];
|
||||
|
||||
lpScreen[pScn + 4] = lpScreen[pScn - 4];
|
||||
lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1];
|
||||
lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2];
|
||||
lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3];
|
||||
|
||||
BGwrite[pBGw + 0] = BGwrite[pBGw - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
cache_tile = tileadr;
|
||||
cache_attr = attr;
|
||||
chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF];
|
||||
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8];
|
||||
BGwrite[pBGw] = (byte)(chr_h | chr_l);
|
||||
|
||||
int pBGPAL = attr;
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)];
|
||||
lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)];
|
||||
lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c1 >> 6))];
|
||||
lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)];
|
||||
lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)];
|
||||
lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)];
|
||||
lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)];
|
||||
lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)];
|
||||
}
|
||||
}
|
||||
pScn += 8;
|
||||
pBGw++;
|
||||
|
||||
// Character latch(For MMC2/MMC4)
|
||||
if (bChrLatch)
|
||||
{
|
||||
nes.mapper.PPU_ChrLatch((ushort)(tileadr));
|
||||
}
|
||||
|
||||
if (++ntbl_x == 32)
|
||||
{
|
||||
ntbl_x = 0;
|
||||
ntbladr ^= 0x41F;
|
||||
attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4);
|
||||
pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span;
|
||||
}
|
||||
else
|
||||
{
|
||||
ntbladr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// With Extension Latch(For MMC5)
|
||||
pScn = lpScanline + (8 - loopy_shift);
|
||||
pBGw = 0;
|
||||
|
||||
int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF);
|
||||
int ntbl_x = ntbladr & 0x1F;
|
||||
|
||||
int cache_tile = unchecked((int)(0xFFFF0000));
|
||||
byte cache_attr = 0xFF;
|
||||
|
||||
byte exattr = 0;
|
||||
chr_h = chr_l = attr = 0;
|
||||
|
||||
for (int i = 0; i < 33; i++)
|
||||
{
|
||||
nes.mapper.PPU_ExtLatchX(i);
|
||||
nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr);
|
||||
attr = (byte)(exattr & 0x0C);
|
||||
|
||||
if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr)
|
||||
{
|
||||
cache_tile = ((chr_h << 8) + chr_l);
|
||||
cache_attr = attr;
|
||||
BGwrite[pBGw] = (byte)(chr_h | chr_l);
|
||||
|
||||
int pBGPAL = attr;
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)];
|
||||
lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)];
|
||||
lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + (c2 >> 6)];
|
||||
lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)];
|
||||
lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)];
|
||||
lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)];
|
||||
lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)];
|
||||
lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lpScreen[pScn + 0] = lpScreen[pScn - 8];
|
||||
lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1];
|
||||
lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2];
|
||||
lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3];
|
||||
|
||||
lpScreen[pScn + 4] = lpScreen[pScn - 4];
|
||||
lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1];
|
||||
lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2];
|
||||
lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3];
|
||||
|
||||
BGwrite[pBGw + 0] = BGwrite[pBGw - 1];
|
||||
}
|
||||
pScn += 8;
|
||||
pBGw++;
|
||||
|
||||
if (++ntbl_x == 32)
|
||||
{
|
||||
ntbl_x = 0;
|
||||
ntbladr ^= 0x41F;
|
||||
}
|
||||
else
|
||||
{
|
||||
ntbladr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!bExtLatch)
|
||||
{
|
||||
// Without Extension Latch
|
||||
if (!bExtNameTable)
|
||||
{
|
||||
pScn = lpScanline + (8 - loopy_shift);
|
||||
pBGw = 0;
|
||||
|
||||
int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF);
|
||||
int attradr = 0x03C0 + ((MMU.loopy_v & 0x0380) >> 4);
|
||||
int ntbl_x = ntbladr & 0x001F;
|
||||
int attrsft = (ntbladr & 0x0040) >> 4;
|
||||
var pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span;
|
||||
|
||||
int tileadr = 0;
|
||||
int cache_tile = unchecked((int)(0xFFFF0000));
|
||||
byte cache_attr = 0xFF;
|
||||
|
||||
chr_h = chr_l = attr = 0;
|
||||
|
||||
for (int i = 0; i < 33; i++)
|
||||
{
|
||||
tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + pNTBL[ntbladr & 0x03FF] * 0x10 + loopy_y;
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
nes.EmulationCPU(NES.FETCH_CYCLES * 4);
|
||||
}
|
||||
|
||||
attr = (byte)(((pNTBL[attradr + (ntbl_x >> 2)] >> ((ntbl_x & 2) + attrsft)) & 3) << 2);
|
||||
|
||||
if (cache_tile != tileadr || cache_attr != attr)
|
||||
{
|
||||
cache_tile = tileadr;
|
||||
cache_attr = attr;
|
||||
|
||||
chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF];
|
||||
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8];
|
||||
lpScreen[pBGw] = (byte)(chr_l | chr_h);
|
||||
|
||||
int pBGPAL = attr;
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)];
|
||||
lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)];
|
||||
lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c2 >> 6))];
|
||||
lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)];
|
||||
lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)];
|
||||
lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)];
|
||||
lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)];
|
||||
lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lpScreen[pScn + 0] = lpScreen[pScn - 8];
|
||||
lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1];
|
||||
lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2];
|
||||
lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3];
|
||||
|
||||
lpScreen[pScn + 4] = lpScreen[pScn - 4];
|
||||
lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1];
|
||||
lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2];
|
||||
lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3];
|
||||
|
||||
BGwrite[pBGw + 0] = BGwrite[pBGw - 1];
|
||||
}
|
||||
pScn += 8;
|
||||
pBGw++;
|
||||
|
||||
// Character latch(For MMC2/MMC4)
|
||||
if (bChrLatch)
|
||||
{
|
||||
nes.mapper.PPU_ChrLatch((ushort)(tileadr));
|
||||
}
|
||||
|
||||
if (++ntbl_x == 32)
|
||||
{
|
||||
ntbl_x = 0;
|
||||
ntbladr ^= 0x41F;
|
||||
attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4);
|
||||
pNTBL = MMU.PPU_MEM_BANK[ntbladr >> 10].Span;
|
||||
}
|
||||
else
|
||||
{
|
||||
ntbladr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pScn = lpScanline + (8 - loopy_shift);
|
||||
pBGw = 0;
|
||||
|
||||
int ntbladr;
|
||||
int tileadr;
|
||||
int cache_tile = unchecked((int)(0xFFFF0000));
|
||||
byte cache_attr = 0xFF;
|
||||
|
||||
chr_h = chr_l = attr = 0;
|
||||
|
||||
ushort loopy_v_tmp = MMU.loopy_v;
|
||||
|
||||
for (int i = 0; i < 33; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
nes.EmulationCPU(NES.FETCH_CYCLES * 4);
|
||||
}
|
||||
|
||||
ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF);
|
||||
tileadr = ((MMU.PPUREG[0] & PPU_BGTBL_BIT) << 8) + MMU.PPU_MEM_BANK[ntbladr >> 10].Span[ntbladr & 0x03FF] * 0x10 + ((MMU.loopy_v & 0x7000) >> 12);
|
||||
attr = (byte)(((MMU.PPU_MEM_BANK[ntbladr >> 10].Span[0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2)] >> (((ntbladr & 0x40) >> 4) + (ntbladr & 0x02))) & 3) << 2);
|
||||
|
||||
if (cache_tile != tileadr || cache_attr != attr)
|
||||
{
|
||||
cache_tile = tileadr;
|
||||
cache_attr = attr;
|
||||
|
||||
chr_l = MMU.PPU_MEM_BANK[tileadr >> 10].Span[tileadr & 0x03FF];
|
||||
chr_h = MMU.PPU_MEM_BANK[tileadr >> 10].Span[(tileadr & 0x03FF) + 8];
|
||||
BGwrite[pBGw] = (byte)(chr_l | chr_h);
|
||||
|
||||
int pBGPAL = attr;
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + (c1 >> 6)];
|
||||
lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)];
|
||||
lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + (c2 >> 6)];
|
||||
lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)];
|
||||
lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)];
|
||||
lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)];
|
||||
lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)];
|
||||
lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lpScreen[pScn + 0] = lpScreen[pScn - 8];
|
||||
lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1];
|
||||
lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2];
|
||||
lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3];
|
||||
|
||||
lpScreen[pScn + 4] = lpScreen[pScn - 4];
|
||||
lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1];
|
||||
lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2];
|
||||
lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3];
|
||||
|
||||
BGwrite[pBGw + 0] = BGwrite[pBGw - 1];
|
||||
}
|
||||
pScn += 8;
|
||||
pBGw++;
|
||||
|
||||
// Character latch(For MMC2/MMC4)
|
||||
if (bChrLatch)
|
||||
{
|
||||
nes.mapper.PPU_ChrLatch((ushort)tileadr);
|
||||
}
|
||||
|
||||
if ((MMU.loopy_v & 0x1F) == 0x1F)
|
||||
{
|
||||
MMU.loopy_v ^= 0x041F;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMU.loopy_v++;
|
||||
}
|
||||
}
|
||||
MMU.loopy_v = loopy_v_tmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// With Extension Latch(For MMC5)
|
||||
pScn = lpScanline + (8 - loopy_shift);
|
||||
pBGw = 0;
|
||||
|
||||
int ntbladr = 0x2000 + (MMU.loopy_v & 0x0FFF);
|
||||
int ntbl_x = ntbladr & 0x1F;
|
||||
|
||||
int cache_tile = unchecked((int)0xFFFF0000);
|
||||
byte cache_attr = 0xFF;
|
||||
|
||||
byte exattr = 0;
|
||||
chr_h = chr_l = attr = 0;
|
||||
|
||||
for (int i = 0; i < 33; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
nes.EmulationCPU(NES.FETCH_CYCLES * 4);
|
||||
}
|
||||
nes.mapper.PPU_ExtLatchX(i);
|
||||
nes.mapper.PPU_ExtLatch((ushort)ntbladr, ref chr_l, ref chr_h, ref exattr);
|
||||
attr = (byte)(exattr & 0x0C);
|
||||
|
||||
if (cache_tile != ((chr_h << 8) + chr_l) || cache_attr != attr)
|
||||
{
|
||||
cache_tile = ((chr_h << 8) + chr_l);
|
||||
cache_attr = attr;
|
||||
BGwrite[pBGw] = (byte)(chr_l | chr_h);
|
||||
|
||||
int pBGPAL = attr;
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
lpScreen[pScn + 0] = MMU.BGPAL[pBGPAL + ((c1 >> 6))];
|
||||
lpScreen[pScn + 4] = MMU.BGPAL[pBGPAL + ((c1 >> 2) & 3)];
|
||||
lpScreen[pScn + 1] = MMU.BGPAL[pBGPAL + ((c2 >> 6))];
|
||||
lpScreen[pScn + 5] = MMU.BGPAL[pBGPAL + ((c2 >> 2) & 3)];
|
||||
lpScreen[pScn + 2] = MMU.BGPAL[pBGPAL + ((c1 >> 4) & 3)];
|
||||
lpScreen[pScn + 6] = MMU.BGPAL[pBGPAL + (c1 & 3)];
|
||||
lpScreen[pScn + 3] = MMU.BGPAL[pBGPAL + ((c2 >> 4) & 3)];
|
||||
lpScreen[pScn + 7] = MMU.BGPAL[pBGPAL + (c2 & 3)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lpScreen[pScn + 0] = lpScreen[pScn - 8];
|
||||
lpScreen[pScn + 0 + 1] = lpScreen[pScn - 8 + 1];
|
||||
lpScreen[pScn + 0 + 2] = lpScreen[pScn - 8 + 2];
|
||||
lpScreen[pScn + 0 + 3] = lpScreen[pScn - 8 + 3];
|
||||
|
||||
lpScreen[pScn + 4] = lpScreen[pScn - 4];
|
||||
lpScreen[pScn + 4 + 1] = lpScreen[pScn - 4 + 1];
|
||||
lpScreen[pScn + 4 + 2] = lpScreen[pScn - 4 + 2];
|
||||
lpScreen[pScn + 4 + 3] = lpScreen[pScn - 4 + 3];
|
||||
|
||||
BGwrite[pBGw + 0] = BGwrite[pBGw - 1];
|
||||
}
|
||||
pScn += 8;
|
||||
pBGw++;
|
||||
|
||||
if (++ntbl_x == 32)
|
||||
{
|
||||
ntbl_x = 0;
|
||||
ntbladr ^= 0x41F;
|
||||
}
|
||||
else
|
||||
{
|
||||
ntbladr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((MMU.PPUREG[1] & PPU_BGCLIP_BIT) == 0 && bLeftClip)
|
||||
{
|
||||
pScn = lpScanline + 8;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
lpScreen[i] = MMU.BGPAL[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render sprites
|
||||
var temp = ~PPU_SPMAX_FLAG;
|
||||
MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & temp);
|
||||
|
||||
// 昞帵婜娫奜偱偁傟偽僉儍儞僙儖
|
||||
if (scanline > 239)
|
||||
return;
|
||||
|
||||
if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int spmax = 0;
|
||||
int spraddr = 0, sp_y = 0, sp_h = 0;
|
||||
chr_h = chr_l = 0;
|
||||
|
||||
|
||||
pBGw = 0;
|
||||
int pSPw = 0;
|
||||
int pBit2Rev = 0;
|
||||
|
||||
MemoryUtility.ZEROMEMORY(SPwrite, SPwrite.Length);
|
||||
|
||||
spmax = 0;
|
||||
Sprite sp = new Sprite(MMU.SPRAM, 0);
|
||||
sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7;
|
||||
|
||||
// Left clip
|
||||
if (bLeftClip && ((MMU.PPUREG[1] & PPU_SPCLIP_BIT) == 0))
|
||||
{
|
||||
SPwrite[0] = 0xFF;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; i++, sp.AddOffset(1))
|
||||
{
|
||||
sp_y = scanline - (sp.y + 1);
|
||||
// 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋
|
||||
if (sp_y != (sp_y & sp_h))
|
||||
continue;
|
||||
|
||||
if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0)
|
||||
{
|
||||
// 8x8 Sprite
|
||||
spraddr = ((MMU.PPUREG[0] & PPU_SPTBL_BIT) << 9) + (sp.tile << 4);
|
||||
if ((sp.attr & SP_VMIRROR_BIT) == 0)
|
||||
spraddr += sp_y;
|
||||
else
|
||||
spraddr += 7 - sp_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 8x16 Sprite
|
||||
spraddr = ((sp.tile & 1) << 12) + ((sp.tile & 0xFE) << 4);
|
||||
if ((sp.attr & SP_VMIRROR_BIT) == 0)
|
||||
spraddr += ((sp_y & 8) << 1) + (sp_y & 7);
|
||||
else
|
||||
spraddr += ((~sp_y & 8) << 1) + (7 - (sp_y & 7));
|
||||
}
|
||||
// Character pattern
|
||||
chr_l = MMU.PPU_MEM_BANK[spraddr >> 10].Span[spraddr & 0x3FF];
|
||||
chr_h = MMU.PPU_MEM_BANK[spraddr >> 10].Span[(spraddr & 0x3FF) + 8];
|
||||
|
||||
// Character latch(For MMC2/MMC4)
|
||||
if (bChrLatch)
|
||||
{
|
||||
nes.mapper.PPU_ChrLatch((ushort)spraddr);
|
||||
}
|
||||
|
||||
// pattern mask
|
||||
if ((sp.attr & SP_HMIRROR_BIT) != 0)
|
||||
{
|
||||
chr_l = Bit2Rev[pBit2Rev + chr_l];
|
||||
chr_h = Bit2Rev[pBit2Rev + chr_h];
|
||||
}
|
||||
byte SPpat = (byte)(chr_l | chr_h);
|
||||
|
||||
// Sprite hitcheck
|
||||
if (i == 0 && (MMU.PPUREG[2] & PPU_SPHIT_FLAG) == 0)
|
||||
{
|
||||
int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3;
|
||||
int BGsft = 8 - ((loopy_shift + sp.x) & 7);
|
||||
|
||||
var temp1 = BGwrite[pBGw + BGpos + 0] << 8;
|
||||
var temp2 = BGwrite[pBGw + BGpos + 1];
|
||||
byte BGmsk = (byte)((temp1 | temp2) >> BGsft);
|
||||
|
||||
if ((SPpat & BGmsk) != 0)
|
||||
{
|
||||
MMU.PPUREG[2] |= PPU_SPHIT_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
// Sprite mask
|
||||
int SPpos = sp.x / 8;
|
||||
int SPsft = 8 - (sp.x & 7);
|
||||
byte SPmsk = (byte)((SPwrite[pSPw + SPpos + 0] << 8 | SPwrite[pSPw + SPpos + 1]) >> SPsft);
|
||||
ushort SPwrt = (ushort)(SPpat << SPsft);
|
||||
SPwrite[pSPw + SPpos + 0] = (byte)(SPwrite[pSPw + SPpos + 0] | SPwrt >> 8);
|
||||
SPwrite[pSPw + SPpos + 1] = (byte)(SPwrite[pSPw + SPpos + 1] | SPwrt & 0xFF);
|
||||
SPpat = (byte)(SPpat & ~SPmsk);
|
||||
|
||||
if ((sp.attr & SP_PRIORITY_BIT) != 0)
|
||||
{
|
||||
// BG > SP priority
|
||||
int BGpos = ((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3;
|
||||
int BGsft = 8 - ((loopy_shift + sp.x) & 7);
|
||||
byte BGmsk = (byte)(((BGwrite[pBGw + BGpos + 0] << 8) | BGwrite[pBGw + BGpos + 1]) >> BGsft);
|
||||
|
||||
SPpat = (byte)(SPpat & ~BGmsk);
|
||||
}
|
||||
|
||||
// Attribute
|
||||
int pSPPAL = (sp.attr & SP_COLOR_BIT) << 2;
|
||||
// Ptr
|
||||
pScn = lpScanline + sp.x + 8;
|
||||
|
||||
if (!bExtMono)
|
||||
{
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
if ((SPpat & 0x80) != 0) lpScreen[pScn + 0] = MMU.SPPAL[pSPPAL + (c1 >> 6)];
|
||||
if ((SPpat & 0x08) != 0) lpScreen[pScn + 4] = MMU.SPPAL[pSPPAL + ((c1 >> 2) & 3)];
|
||||
if ((SPpat & 0x40) != 0) lpScreen[pScn + 1] = MMU.SPPAL[pSPPAL + ((c2 >> 6))];
|
||||
if ((SPpat & 0x04) != 0) lpScreen[pScn + 5] = MMU.SPPAL[pSPPAL + ((c2 >> 2) & 3)];
|
||||
if ((SPpat & 0x20) != 0) lpScreen[pScn + 2] = MMU.SPPAL[pSPPAL + ((c1 >> 4) & 3)];
|
||||
if ((SPpat & 0x02) != 0) lpScreen[pScn + 6] = MMU.SPPAL[pSPPAL + (c1 & 3)];
|
||||
if ((SPpat & 0x10) != 0) lpScreen[pScn + 3] = MMU.SPPAL[pSPPAL + ((c2 >> 4) & 3)];
|
||||
if ((SPpat & 0x01) != 0) lpScreen[pScn + 7] = MMU.SPPAL[pSPPAL + (c2 & 3)];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Monocrome effect (for Final Fantasy)
|
||||
byte mono = BGmono[((sp.x & 0xF8) + ((loopy_shift + (sp.x & 7)) & 8)) >> 3];
|
||||
|
||||
int c1 = ((chr_l >> 1) & 0x55) | (chr_h & 0xAA);
|
||||
int c2 = (chr_l & 0x55) | ((chr_h << 1) & 0xAA);
|
||||
if ((SPpat & 0x80) != 0) lpScreen[pScn + 0] = (byte)(MMU.SPPAL[pSPPAL + (c1 >> 6)] | mono);
|
||||
if ((SPpat & 0x08) != 0) lpScreen[pScn + 4] = (byte)(MMU.SPPAL[pSPPAL + ((c1 >> 2) & 3)] | mono);
|
||||
if ((SPpat & 0x40) != 0) lpScreen[pScn + 1] = (byte)(MMU.SPPAL[pSPPAL + (c2 >> 6)] | mono);
|
||||
if ((SPpat & 0x04) != 0) lpScreen[pScn + 5] = (byte)(MMU.SPPAL[pSPPAL + ((c2 >> 2) & 3)] | mono);
|
||||
if ((SPpat & 0x20) != 0) lpScreen[pScn + 2] = (byte)(MMU.SPPAL[pSPPAL + ((c1 >> 4) & 3)] | mono);
|
||||
if ((SPpat & 0x02) != 0) lpScreen[pScn + 6] = (byte)(MMU.SPPAL[pSPPAL + (c1 & 3)] | mono);
|
||||
if ((SPpat & 0x10) != 0) lpScreen[pScn + 3] = (byte)(MMU.SPPAL[pSPPAL + ((c2 >> 4) & 3)] | mono);
|
||||
if ((SPpat & 0x01) != 0) lpScreen[pScn + 7] = (byte)(MMU.SPPAL[pSPPAL + (c2 & 3)] | mono);
|
||||
}
|
||||
|
||||
if (++spmax > 8 - 1)
|
||||
{
|
||||
if (!bMax)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (spmax > 8 - 1)
|
||||
{
|
||||
MMU.PPUREG[2] |= PPU_SPMAX_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool IsSprite0(int scanline)
|
||||
{
|
||||
// 僗僾儔僀僩orBG旕昞帵偼僉儍儞僙儖(僸僢僩偟側偄)
|
||||
if ((MMU.PPUREG[1] & (PPU_SPDISP_BIT | PPU_BGDISP_BIT)) != (PPU_SPDISP_BIT | PPU_BGDISP_BIT))
|
||||
return false;
|
||||
|
||||
// 婛偵僸僢僩偟偰偄偨傜僉儍儞僙儖
|
||||
if ((MMU.PPUREG[2] & PPU_SPHIT_FLAG) != 0)
|
||||
return false;
|
||||
|
||||
if ((MMU.PPUREG[0] & PPU_SP16_BIT) == 0)
|
||||
{
|
||||
// 8x8
|
||||
if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 7 + 1)))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 8x16
|
||||
if ((scanline < MMU.SPRAM[0] + 1) || (scanline > (MMU.SPRAM[0] + 15 + 1)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void DummyScanline(int scanline)
|
||||
{
|
||||
int i;
|
||||
int spmax;
|
||||
int sp_h;
|
||||
|
||||
MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPMAX_FLAG);
|
||||
|
||||
// 僗僾儔僀僩旕昞帵偼僉儍儞僙儖
|
||||
if ((MMU.PPUREG[1] & PPU_SPDISP_BIT) == 0)
|
||||
return;
|
||||
|
||||
// 昞帵婜娫奜偱偁傟偽僉儍儞僙儖
|
||||
if (scanline < 0 || scanline > 239)
|
||||
return;
|
||||
|
||||
Sprite sp = new Sprite(MMU.SPRAM, 0);
|
||||
sp_h = (MMU.PPUREG[0] & PPU_SP16_BIT) != 0 ? 15 : 7;
|
||||
|
||||
spmax = 0;
|
||||
// Sprite Max check
|
||||
for (i = 0; i < 64; i++, sp.AddOffset(1))
|
||||
{
|
||||
// 僗僉儍儞儔僀儞撪偵SPRITE偑懚嵼偡傞偐傪僠僃僢僋
|
||||
if ((scanline < sp.y + 1) || (scanline > (sp.y + sp_h + 1)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (++spmax > 8 - 1)
|
||||
{
|
||||
MMU.PPUREG[2] |= PPU_SPMAX_FLAG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void VBlankEnd()
|
||||
{
|
||||
MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_VBLANK_FLAG);
|
||||
// VBlank扙弌帪偵僋儕傾偝傟傞
|
||||
// 僄僉僒僀僩僶僀僋偱廳梫
|
||||
MMU.PPUREG[2] = (byte)(MMU.PPUREG[2] & ~PPU_SPHIT_FLAG);
|
||||
}
|
||||
|
||||
internal void VBlankStart()
|
||||
{
|
||||
MMU.PPUREG[2] |= PPU_VBLANK_FLAG;
|
||||
}
|
||||
|
||||
public byte[] GetScreenPtr()
|
||||
{
|
||||
return lpScreen;
|
||||
}
|
||||
|
||||
internal void SetScreenPtr(byte[] screenBuffer, byte[] colormode)
|
||||
{
|
||||
lpScreen = screenBuffer;
|
||||
lpColormode = colormode;
|
||||
}
|
||||
|
||||
internal bool IsDispON()
|
||||
{
|
||||
return (MMU.PPUREG[1] & (PPU_BGDISP_BIT | PPU_SPDISP_BIT)) != 0;
|
||||
}
|
||||
|
||||
public struct Sprite
|
||||
{
|
||||
public byte y
|
||||
{
|
||||
get => raw[offset + 0];
|
||||
set => raw[offset + 0] = value;
|
||||
}
|
||||
|
||||
public byte tile
|
||||
{
|
||||
get => raw[offset + 1];
|
||||
set => raw[offset + 1] = value;
|
||||
}
|
||||
public byte attr
|
||||
{
|
||||
get => raw[offset + 2];
|
||||
set => raw[offset + 2] = value;
|
||||
}
|
||||
public byte x
|
||||
{
|
||||
get => raw[offset + 3];
|
||||
set => raw[offset + 3] = value;
|
||||
}
|
||||
|
||||
private byte[] raw;
|
||||
private int offset;
|
||||
|
||||
public Sprite(byte[] raw, int offset)
|
||||
{
|
||||
this.raw = raw;
|
||||
this.offset = offset * 4;
|
||||
}
|
||||
|
||||
public void AddOffset(int offset)
|
||||
{
|
||||
this.offset += offset * 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ namespace VirtualNes.Core
|
||||
}
|
||||
|
||||
Supporter.GetFilePathInfo(fname, out fullpath, out path);
|
||||
|
||||
name = Path.GetFileNameWithoutExtension(fullpath);
|
||||
if (!bNSF)
|
||||
{
|
||||
mapper = (header.control1 >> 4) | (header.control2 & 0xF0);
|
||||
@ -396,6 +396,11 @@ namespace VirtualNes.Core
|
||||
{
|
||||
return nsfheader;
|
||||
}
|
||||
|
||||
internal string GetRomPath()
|
||||
{
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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];
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user