Merge pull request 'master' (#26) from Alienjack/AxibugEmuOnline:master into master

Reviewed-on: #26
This commit is contained in:
sin365 2024-08-07 11:19:45 +08:00
commit 20f28469b2
48 changed files with 23576 additions and 1489 deletions

3
.gitignore vendored
View File

@ -14,3 +14,6 @@
/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset /AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset
/AxibugEmuOnline.Client/Logs /AxibugEmuOnline.Client/Logs
/virtuanessrc097-master/save /virtuanessrc097-master/save
/virtuanessrc097-master/.vs
/virtuanessrc097-master/Debug
/virtuanessrc097-master/VirtuaNES.ini

File diff suppressed because it is too large Load Diff

View File

@ -323,6 +323,10 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z propertyPath: m_LocalEulerAnglesHint.z
value: 0 value: 0
objectReference: {fileID: 0} objectReference: {fileID: 0}
- target: {fileID: 4232056521131536012, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: RomName
value: tortoise4.nes
objectReference: {fileID: 0}
- target: {fileID: 4232056521131536013, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3} - target: {fileID: 4232056521131536013, guid: f8bea3f8aa351bb46ada33b2274729ea, type: 3}
propertyPath: m_Name propertyPath: m_Name
value: NesEmulator value: NesEmulator

View File

@ -1,16 +1,17 @@
using System; using UnityEngine;
using System.Diagnostics;
using UnityEngine;
using VirtualNes.Core; using VirtualNes.Core;
namespace AxibugEmuOnline.Client
{
public class AudioProvider : MonoBehaviour public class AudioProvider : MonoBehaviour
{ {
public NesEmulator NesEmu;
[SerializeField] [SerializeField]
private AudioSource m_as; private AudioSource m_as;
private SoundBuffer _buffer = new SoundBuffer(4096); private SoundBuffer _buffer = new SoundBuffer(4096);
public void Start()
public void Initialize()
{ {
var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false); var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false);
@ -25,7 +26,9 @@ public class AudioProvider : MonoBehaviour
{ {
int step = channels; int step = channels;
var bufferCount = _buffer.Available(); if (NesEmu == null || NesEmu.NesCore == null) return;
ProcessSound(NesEmu.NesCore, (uint)(data.Length / channels));
for (int i = 0; i < data.Length; i += step) for (int i = 0; i < data.Length; i += step)
{ {
@ -36,12 +39,13 @@ public class AudioProvider : MonoBehaviour
data[i] = rawFloat; data[i] = rawFloat;
for (int fill = 1; fill < step; fill++) for (int fill = 1; fill < step; fill++)
data[i + fill] = rawFloat; data[i + fill] = rawFloat;
} }
} }
public void ProcessSound(NES nes) void ProcessSound(NES nes, uint feedCount)
{ {
nes.apu.Process(_buffer, (uint)(Supporter.Config.sound.nRate * Time.deltaTime)); nes.apu.Process(_buffer, feedCount);
} }
} }
}

View File

@ -98,5 +98,11 @@ namespace AxibugEmuOnline.Client
var db = Resources.Load<RomDB>("NES/ROMDB"); var db = Resources.Load<RomDB>("NES/ROMDB");
return db.GetMapperNo(rom.GetPROM_CRC(), out mapperNo); return db.GetMapperNo(rom.GetPROM_CRC(), out mapperNo);
} }
public ControllerState GetControllerState()
{
var mapper = NesControllerMapper.Get();
return mapper.CreateState();
}
} }
} }

View File

@ -0,0 +1,124 @@
using System;
using System.IO;
using UnityEngine;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client
{
public class NesControllerMapper
{
private static readonly string ConfigFilePath = $"{Application.persistentDataPath}/NES/ControllerMapper.json";
public MapperSetter Player1 = new MapperSetter();
public MapperSetter Player2 = new MapperSetter();
public MapperSetter Player3 = new MapperSetter();
public MapperSetter Player4 = new MapperSetter();
public NesControllerMapper()
{
Player1.UP.keyCode = KeyCode.W;
Player1.DOWN.keyCode = KeyCode.S;
Player1.LEFT.keyCode = KeyCode.A;
Player1.RIGHT.keyCode = KeyCode.D;
Player1.B.keyCode = KeyCode.J;
Player1.A.keyCode = KeyCode.K;
Player1.SELECT.keyCode = KeyCode.V;
Player1.START.keyCode = KeyCode.B;
}
public void Save()
{
var jsonStr = JsonUtility.ToJson(this);
File.WriteAllText(ConfigFilePath, jsonStr);
}
public ControllerState CreateState()
{
var state1 = Player1.GetButtons();
var state2 = Player2.GetButtons();
var state3 = Player3.GetButtons();
var state4 = Player4.GetButtons();
return new ControllerState(state1, state2, state3, state4);
}
private static NesControllerMapper s_setting;
public static NesControllerMapper Get()
{
if (s_setting == null)
{
try
{
var json = File.ReadAllText($"{Application.persistentDataPath}/Nes/ControllerMapper.json");
s_setting = JsonUtility.FromJson<NesControllerMapper>(json);
}
catch
{
s_setting = new NesControllerMapper();
}
}
return s_setting;
}
[Serializable]
public class Mapper
{
public EnumButtonType buttonType;
public KeyCode keyCode;
public Mapper(EnumButtonType buttonType)
{
this.buttonType = buttonType;
}
}
[Serializable]
public class MapperSetter
{
public Mapper UP = new Mapper(EnumButtonType.UP);
public Mapper DOWN = new Mapper(EnumButtonType.DOWN);
public Mapper LEFT = new Mapper(EnumButtonType.LEFT);
public Mapper RIGHT = new Mapper(EnumButtonType.RIGHT);
public Mapper A = new Mapper(EnumButtonType.A);
public Mapper B = new Mapper(EnumButtonType.B);
public Mapper SELECT = new Mapper(EnumButtonType.SELECT);
public Mapper START = new Mapper(EnumButtonType.START);
public Mapper MIC = new Mapper(EnumButtonType.MIC);
public EnumButtonType GetButtons()
{
EnumButtonType res = 0;
if (Input.GetKey(UP.keyCode))
res |= EnumButtonType.UP;
if (Input.GetKey(DOWN.keyCode))
res |= EnumButtonType.DOWN;
if (Input.GetKey(LEFT.keyCode))
res |= EnumButtonType.LEFT;
if (Input.GetKey(RIGHT.keyCode))
res |= EnumButtonType.RIGHT;
if (Input.GetKey(A.keyCode))
res |= EnumButtonType.A;
if (Input.GetKey(B.keyCode))
res |= EnumButtonType.B;
if (Input.GetKey(SELECT.keyCode))
res |= EnumButtonType.SELECT;
if (Input.GetKey(START.keyCode))
res |= EnumButtonType.START;
if (Input.GetKey(MIC.keyCode))
res |= EnumButtonType.MIC;
return res;
}
}
}
}

View File

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

View File

@ -9,15 +9,24 @@ namespace AxibugEmuOnline.Client
{ {
public class NesEmulator : MonoBehaviour public class NesEmulator : MonoBehaviour
{ {
private NES m_nesIns; public NES NesCore { get; private set; }
public VideoProvider VideoProvider; public VideoProvider VideoProvider;
public AudioProvider AudioProvider; public AudioProvider AudioProvider;
#if UNITY_EDITOR
public string RomName;
#endif
private void Start() private void Start()
{ {
Application.targetFrameRate = 60; Application.targetFrameRate = 60;
StartGame("tstd2.nes"); VideoProvider.NesEmu = this;
AudioProvider.NesEmu = this;
#if UNITY_EDITOR
StartGame(RomName);
#endif
} }
public void StartGame(string romName) public void StartGame(string romName)
@ -29,33 +38,32 @@ namespace AxibugEmuOnline.Client
try try
{ {
m_nesIns = new NES(romName); NesCore = new NES(romName);
} }
catch (Exception ex) catch (Exception ex)
{ {
m_nesIns = null; NesCore = null;
Debug.LogError(ex); Debug.LogError(ex);
} }
} }
public void StopGame() public void StopGame()
{ {
m_nesIns?.Dispose(); NesCore?.Dispose();
m_nesIns = null; NesCore = null;
} }
private void Update() private void Update()
{ {
if (m_nesIns != null) if (NesCore != null)
{ {
m_nesIns.EmulateFrame(true); var controlState = Supporter.GetControllerState();
NesCore.pad.Sync(controlState);
NesCore.EmulateFrame(true);
var screenBuffer = m_nesIns.ppu.GetScreenPtr(); var screenBuffer = NesCore.ppu.GetScreenPtr();
var lineColorMode = m_nesIns.ppu.GetLineColorMode(); var lineColorMode = NesCore.ppu.GetLineColorMode();
VideoProvider.SetDrawData(screenBuffer, lineColorMode, 256, 240); VideoProvider.SetDrawData(screenBuffer, lineColorMode, 256, 240);
AudioProvider.ProcessSound(m_nesIns);
} }
} }

View File

@ -10,7 +10,7 @@ GameObject:
m_Component: m_Component:
- component: {fileID: 4785916497946256520} - component: {fileID: 4785916497946256520}
- component: {fileID: 9003897287163669553} - component: {fileID: 9003897287163669553}
- component: {fileID: 7558824780418593440} - component: {fileID: 8726979175317618791}
m_Layer: 0 m_Layer: 0
m_Name: AudioProvider m_Name: AudioProvider
m_TagString: Untagged m_TagString: Untagged
@ -30,7 +30,7 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: [] m_Children: []
m_Father: {fileID: 4232056521131536011} m_Father: {fileID: 4232056521131536011}
m_RootOrder: 1 m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &9003897287163669553 --- !u!114 &9003897287163669553
MonoBehaviour: MonoBehaviour:
@ -44,8 +44,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: a6a09b6a4cf4c2d4f994a13fd7e89d6f, type: 3} m_Script: {fileID: 11500000, guid: a6a09b6a4cf4c2d4f994a13fd7e89d6f, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
m_as: {fileID: 7558824780418593440} m_as: {fileID: 8726979175317618791}
--- !u!82 &7558824780418593440 --- !u!82 &8726979175317618791
AudioSource: AudioSource:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0} m_CorrespondingSourceObject: {fileID: 0}
@ -141,6 +141,78 @@ AudioSource:
m_PreInfinity: 2 m_PreInfinity: 2
m_PostInfinity: 2 m_PostInfinity: 2
m_RotationOrder: 4 m_RotationOrder: 4
--- !u!1 &3545890545112170401
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1038087993597378172}
- component: {fileID: 3032498056073774270}
- component: {fileID: 634277252673086327}
m_Layer: 5
m_Name: Viewer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!224 &1038087993597378172
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3545890545112170401}
m_LocalRotation: {x: 1, y: 0, z: 0, w: 0}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4232056520494431727}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 180, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 128, y: 256}
m_Pivot: {x: 0, y: 0}
--- !u!222 &3032498056073774270
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3545890545112170401}
m_CullTransparentMesh: 1
--- !u!114 &634277252673086327
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3545890545112170401}
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!1 &4232056520112715746 --- !u!1 &4232056520112715746
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -168,8 +240,7 @@ Transform:
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: m_Children: []
- {fileID: 4232056520494431727}
m_Father: {fileID: 4232056521131536011} m_Father: {fileID: 4232056521131536011}
m_RootOrder: 0 m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -217,8 +288,9 @@ RectTransform:
m_LocalScale: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0, y: 0, z: 0}
m_Children: m_Children:
- {fileID: 4232056521759880275} - {fileID: 4232056521759880275}
m_Father: {fileID: 4232056520112715745} - {fileID: 1038087993597378172}
m_RootOrder: 0 m_Father: {fileID: 4232056521131536011}
m_RootOrder: 3
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0} m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}
@ -315,7 +387,9 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1} m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: m_Children:
- {fileID: 4232056520112715745} - {fileID: 4232056520112715745}
- {fileID: 393435831810118449}
- {fileID: 4785916497946256520} - {fileID: 4785916497946256520}
- {fileID: 4232056520494431727}
m_Father: {fileID: 0} m_Father: {fileID: 0}
m_RootOrder: 0 m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@ -333,6 +407,7 @@ MonoBehaviour:
m_EditorClassIdentifier: m_EditorClassIdentifier:
VideoProvider: {fileID: 4232056520112715744} VideoProvider: {fileID: 4232056520112715744}
AudioProvider: {fileID: 9003897287163669553} AudioProvider: {fileID: 9003897287163669553}
RomName:
--- !u!1 &4232056521759880276 --- !u!1 &4232056521759880276
GameObject: GameObject:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0
@ -405,3 +480,47 @@ MonoBehaviour:
y: 0 y: 0
width: 1 width: 1
height: 1 height: 1
--- !u!1 &7856060136050839404
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 393435831810118449}
- component: {fileID: 499856625911497759}
m_Layer: 0
m_Name: PatternViewer
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
--- !u!4 &393435831810118449
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7856060136050839404}
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: 4232056521131536011}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &499856625911497759
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 7856060136050839404}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: c7a50c189f5be5b4ea54de444f8488a0, type: 3}
m_Name:
m_EditorClassIdentifier:
img: {fileID: 634277252673086327}

View File

@ -7,10 +7,18 @@ using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using VirtualNes.Core; using VirtualNes.Core;
namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator namespace AxibugEmuOnline.Client
{ {
public static class PaletteDefine public static class PaletteDefine
{ {
public struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
public class PALBUF public class PALBUF
{ {
public byte r; public byte r;
@ -108,6 +116,33 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
new PALBUF(0x00, 0x00, 0x00), new PALBUF(0x00, 0x00, 0x00),
}; };
#region 256
// Color
public static RGBQUAD[][] m_cpPalette = new RGBQUAD[8][]
{
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
};
// Monochrome
public static RGBQUAD[][] m_mpPalette = new RGBQUAD[8][]
{
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
new RGBQUAD[64*2],
};
#endregion
#region #region
// Color // Color
public static uint[][] m_cnPalette = new uint[8][] public static uint[][] m_cnPalette = new uint[8][]
@ -161,6 +196,18 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
}; };
#endregion #endregion
public static RGBQUAD[] GetPaletteData()
{
RGBQUAD[] rgb = new RGBQUAD[256];
for (int i = 0; i < 64; i++)
{
rgb[i] = m_cpPalette[0][i];
rgb[i + 0x40] = m_mpPalette[0][i];
}
return rgb;
}
static PaletteDefine() static PaletteDefine()
{ {
int Rbit = 0, Gbit = 0, Bbit = 0; int Rbit = 0, Gbit = 0, Bbit = 0;
@ -186,6 +233,13 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
Gs = (uint)(PalConvTbl[j][1] * m_PaletteBuf[i].g * m_nScanlineColor / 100.0f); Gs = (uint)(PalConvTbl[j][1] * m_PaletteBuf[i].g * m_nScanlineColor / 100.0f);
Bs = (uint)(PalConvTbl[j][2] * m_PaletteBuf[i].b * m_nScanlineColor / 100.0f); Bs = (uint)(PalConvTbl[j][2] * m_PaletteBuf[i].b * m_nScanlineColor / 100.0f);
m_cpPalette[j][i + 0x00].rgbRed = (byte)Rn;
m_cpPalette[j][i + 0x00].rgbGreen = (byte)Gn;
m_cpPalette[j][i + 0x00].rgbBlue = (byte)Bn;
m_cpPalette[j][i + 0x40].rgbRed = (byte)Rs;
m_cpPalette[j][i + 0x40].rgbGreen = (byte)Gs;
m_cpPalette[j][i + 0x40].rgbBlue = (byte)Bs;
m_cnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft); m_cnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft);
m_csPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft); m_csPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft);
@ -216,6 +270,13 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
if (Gs > 0xFF) Gs = 0xFF; if (Gs > 0xFF) Gs = 0xFF;
if (Bs > 0xFF) Bs = 0xFF; if (Bs > 0xFF) Bs = 0xFF;
m_mpPalette[j][i + 0x00].rgbRed = (byte)Rn;
m_mpPalette[j][i + 0x00].rgbGreen = (byte)Gn;
m_mpPalette[j][i + 0x00].rgbBlue = (byte)Bn;
m_mpPalette[j][i + 0x40].rgbRed = (byte)Rs;
m_mpPalette[j][i + 0x40].rgbGreen = (byte)Gs;
m_mpPalette[j][i + 0x40].rgbBlue = (byte)Bs;
m_mnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft); m_mnPalette[j][i] = ((Rn >> (8 - Rbit)) << Rsft) | ((Gn >> (8 - Gbit)) << Gsft) | ((Bn >> (8 - Bbit)) << Bsft);
m_msPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft); m_msPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft);
} }

View File

@ -0,0 +1,85 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using VirtualNes;
using static AxibugEmuOnline.Client.PaletteDefine;
namespace AxibugEmuOnline.Client
{
public class PatternViewer : MonoBehaviour
{
public RawImage img;
private Color32[] m_lpPattern = new Color32[128 * 256];
private Texture2D m_texture;
private Dictionary<byte, RGBQUAD> colors = new Dictionary<byte, RGBQUAD>();
private void Awake()
{
m_texture = new Texture2D(128, 256);
}
private void Update()
{
Paint();
}
private void OnEnable()
{
img.gameObject.SetActive(true);
}
private void OnDisable()
{
img.gameObject.SetActive(false);
}
public void Paint()
{
img.texture = m_texture;
var pal = MMU.SPPAL;
var palette = PaletteDefine.GetPaletteData();
colors[0] = palette[pal[0]];
colors[1] = palette[pal[1]];
colors[2] = palette[pal[2]];
colors[3] = palette[pal[3]];
for (int i = 0; i < 8; i++)
{
var Ptn = MMU.PPU_MEM_BANK[i];
int lpPtn = 0;
for (int p = 0; p < 64; p++)
{
int lpScn = i * 32 * 128 + (p & 15) * 8 + (p / 16) * 8 * 128;
for (int y = 0; y < 8; y++)
{
byte chr_l = Ptn[lpPtn + y];
byte chr_h = Ptn[lpPtn + y + 8];
m_lpPattern[lpScn + 0] = ToColor32(colors, (((chr_h >> 6) & 2) | ((chr_l >> 7) & 1)));
m_lpPattern[lpScn + 4] = ToColor32(colors, (((chr_h >> 2) & 2) | ((chr_l >> 3) & 1)));
m_lpPattern[lpScn + 1] = ToColor32(colors, (((chr_h >> 5) & 2) | ((chr_l >> 6) & 1)));
m_lpPattern[lpScn + 5] = ToColor32(colors, (((chr_h >> 1) & 2) | ((chr_l >> 2) & 1)));
m_lpPattern[lpScn + 2] = ToColor32(colors, (((chr_h >> 4) & 2) | ((chr_l >> 5) & 1)));
m_lpPattern[lpScn + 6] = ToColor32(colors, (((chr_h >> 0) & 2) | ((chr_l >> 1) & 1)));
m_lpPattern[lpScn + 3] = ToColor32(colors, (((chr_h >> 3) & 2) | ((chr_l >> 4) & 1)));
m_lpPattern[lpScn + 7] = ToColor32(colors, (((chr_h << 1) & 2) | ((chr_l >> 0) & 1)));
// Next line
lpScn += 128;
}
// Next pattern
lpPtn += 16;
}
}
m_texture.SetPixels32(m_lpPattern);
m_texture.Apply();
}
private Color32 ToColor32(Dictionary<byte, RGBQUAD> map, int v)
{
var raw = map[(byte)v];
return new Color32(raw.rgbRed, raw.rgbGreen, raw.rgbBlue, 255);
}
}
}

View File

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

View File

@ -1,14 +1,14 @@
using AxibugEmuOnline.Client.Assets.Script.NesEmulator;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client namespace AxibugEmuOnline.Client
{ {
public class VideoProvider : MonoBehaviour public class VideoProvider : MonoBehaviour
{ {
public NesEmulator NesEmu;
public RawImage Image; public RawImage Image;
private UInt32[] wrapTexBuffer; private UInt32[] wrapTexBuffer;

View File

@ -60,7 +60,16 @@ namespace VirtualNes.Core
m_bMute[i] = true; m_bMute[i] = true;
} }
public void Dispose() { } public void Dispose()
{
@internal.Dispose();
vrc6.Dispose();
vrc7.Dispose();
mmc5.Dispose();
fds.Dispose();
n106.Dispose();
fme7.Dispose();
}
private int[] vol = new int[24]; private int[] vol = new int[24];
static double cutofftemp = (2.0 * 3.141592653579 * 40.0); static double cutofftemp = (2.0 * 3.141592653579 * 40.0);

View File

@ -1,36 +1,219 @@
using System; using Codice.CM.Client.Differences;
using System;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
public class APU_MMC5 : APU_INTERFACE public class APU_MMC5 : APU_INTERFACE
{ {
public const int RECTANGLE_VOL_SHIFT = 8;
public const int DAOUT_VOL_SHIFT = 6;
SYNCRECTANGLE sch0 = new SYNCRECTANGLE(); SYNCRECTANGLE sch0 = new SYNCRECTANGLE();
SYNCRECTANGLE sch1 = new SYNCRECTANGLE(); SYNCRECTANGLE sch1 = new SYNCRECTANGLE();
RECTANGLE ch0 = new RECTANGLE();
RECTANGLE ch1 = new RECTANGLE();
byte reg5010;
byte reg5011;
byte reg5015;
byte sync_reg5015;
int FrameCycle;
float cpu_clock;
int cycle_rate;
// Tables
static int[] vbl_length = new int[32];
static int[] duty_lut = new int[4];
static int[] decay_lut = new int[16];
static int[] vbl_lut = new int[32];
public APU_MMC5()
{
// 仮設定
Reset(APU_INTERFACE.APU_CLOCK, 22050);
}
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
//todo : 实现 sch0.ZeroMemory();
sch1.ZeroMemory();
reg5010 = reg5011 = reg5015 = 0;
sync_reg5015 = 0;
FrameCycle = 0;
Setup(fClock, nRate);
for (ushort addr = 0x5000; addr <= 0x5015; addr++)
{
Write(addr, 0);
}
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
//todo : 实现 cpu_clock = fClock;
cycle_rate = (int)(fClock * 65536.0f / nRate);
// Create Tables
int i;
int samples = (int)(nRate / 60.0f);
for (i = 0; i < 16; i++)
decay_lut[i] = (i + 1) * samples * 5;
for (i = 0; i < 32; i++)
vbl_lut[i] = vbl_length[i] * samples * 5;
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
//todo : 实现 switch (addr)
}
public override int Process(int channel)
{ {
//todo : 实现 // MMC5 CH0 rectangle
return 0; case 0x5000:
ch0.reg[0] = data;
ch0.volume = (byte)(data & 0x0F);
ch0.holdnote = (byte)(data & 0x20);
ch0.fixed_envelope = (byte)(data & 0x10);
ch0.env_decay = decay_lut[data & 0x0F];
ch0.duty_flip = duty_lut[data >> 6];
break;
case 0x5001:
ch0.reg[1] = data;
break;
case 0x5002:
ch0.reg[2] = data;
ch0.freq = INT2FIX(((ch0.reg[3] & 0x07) << 8) + data + 1);
break;
case 0x5003:
ch0.reg[3] = data;
ch0.vbl_length = vbl_lut[data >> 3];
ch0.env_vol = 0;
ch0.freq = INT2FIX(((data & 0x07) << 8) + ch0.reg[2] + 1);
if ((reg5015 & 0x01) != 0)
ch0.enable = 0xFF;
break;
// MMC5 CH1 rectangle
case 0x5004:
ch1.reg[0] = data;
ch1.volume = (byte)(data & 0x0F);
ch1.holdnote = (byte)(data & 0x20);
ch1.fixed_envelope = (byte)(data & 0x10);
ch1.env_decay = decay_lut[data & 0x0F];
ch1.duty_flip = duty_lut[data >> 6];
break;
case 0x5005:
ch1.reg[1] = data;
break;
case 0x5006:
ch1.reg[2] = data;
ch1.freq = INT2FIX(((ch1.reg[3] & 0x07) << 8) + data + 1);
break;
case 0x5007:
ch1.reg[3] = data;
ch1.vbl_length = vbl_lut[data >> 3];
ch1.env_vol = 0;
ch1.freq = INT2FIX(((data & 0x07) << 8) + ch1.reg[2] + 1);
if ((reg5015 & 0x02) != 0)
ch1.enable = 0xFF;
break;
case 0x5010:
reg5010 = data;
break;
case 0x5011:
reg5011 = data;
break;
case 0x5012:
case 0x5013:
case 0x5014:
break;
case 0x5015:
reg5015 = data;
if ((reg5015 & 0x01) != 0)
{
ch0.enable = 0xFF;
}
else
{
ch0.enable = 0;
ch0.vbl_length = 0;
}
if ((reg5015 & 0x02) != 0)
{
ch1.enable = 0xFF;
}
else
{
ch1.enable = 0;
ch1.vbl_length = 0;
}
break;
}
} }
internal void SyncWrite(ushort addr, byte data) internal void SyncWrite(ushort addr, byte data)
{ {
//todo : 实现 switch (addr)
{
// MMC5 CH0 rectangle
case 0x5000:
sch0.reg[0] = data;
sch0.holdnote = (byte)(data & 0x20);
break;
case 0x5001:
case 0x5002:
sch0.reg[addr & 3] = data;
break;
case 0x5003:
sch0.reg[3] = data;
sch0.vbl_length = vbl_length[data >> 3];
if ((sync_reg5015 & 0x01) != 0)
sch0.enable = 0xFF;
break;
// MMC5 CH1 rectangle
case 0x5004:
sch1.reg[0] = data;
sch1.holdnote = (byte)(data & 0x20);
break;
case 0x5005:
case 0x5006:
sch1.reg[addr & 3] = data;
break;
case 0x5007:
sch1.reg[3] = data;
sch1.vbl_length = vbl_length[data >> 3];
if ((sync_reg5015 & 0x02) != 0)
sch1.enable = 0xFF;
break;
case 0x5010:
case 0x5011:
case 0x5012:
case 0x5013:
case 0x5014:
break;
case 0x5015:
sync_reg5015 = data;
if ((sync_reg5015 & 0x01) != 0)
{
sch0.enable = 0xFF;
}
else
{
sch0.enable = 0;
sch0.vbl_length = 0;
}
if ((sync_reg5015 & 0x02) != 0)
{
sch1.enable = 0xFF;
}
else
{
sch1.enable = 0;
sch1.vbl_length = 0;
}
break;
}
} }
internal byte SyncRead(ushort addr) internal byte SyncRead(ushort addr)
@ -46,6 +229,118 @@ namespace VirtualNes.Core
return data; return data;
} }
public override bool Sync(int cycles)
{
FrameCycle += cycles;
if (FrameCycle >= 7457 * 5 / 2)
{
FrameCycle -= 7457 * 5 / 2;
if (sch0.enable != 0 && sch0.holdnote == 0)
{
if ((sch0.vbl_length) != 0)
{
sch0.vbl_length--;
}
}
if (sch1.enable != 0 && sch1.holdnote == 0)
{
if ((sch1.vbl_length) != 0)
{
sch1.vbl_length--;
}
}
}
return false;
}
public override int Process(int channel)
{
switch (channel)
{
case 0:
return RectangleRender(ch0);
case 1:
return RectangleRender(ch1);
case 2:
return reg5011 << DAOUT_VOL_SHIFT;
}
return 0;
}
private int RectangleRender(RECTANGLE ch)
{
if (ch.enable == 0 || ch.vbl_length <= 0)
return 0;
// vbl length counter
if (ch.holdnote == 0)
ch.vbl_length -= 5;
// envelope unit
ch.env_phase -= 5 * 4;
while (ch.env_phase < 0)
{
ch.env_phase += ch.env_decay;
if ((ch.holdnote) != 0)
ch.env_vol = (byte)((ch.env_vol + 1) & 0x0F);
else if (ch.env_vol < 0x0F)
ch.env_vol++;
}
if (ch.freq < INT2FIX(8))
return 0;
int volume;
if ((ch.fixed_envelope) != 0)
volume = ch.volume;
else
volume = (0x0F - ch.env_vol);
int output = volume << RECTANGLE_VOL_SHIFT;
ch.phaseacc -= cycle_rate;
if (ch.phaseacc >= 0)
{
if (ch.adder < ch.duty_flip)
ch.output_vol = output;
else
ch.output_vol = -output;
return ch.output_vol;
}
if (ch.freq > cycle_rate)
{
ch.phaseacc += ch.freq;
ch.adder = (ch.adder + 1) & 0x0F;
if (ch.adder < ch.duty_flip)
ch.output_vol = output;
else
ch.output_vol = -output;
}
else
{
// 加重平均
int num_times, total;
num_times = total = 0;
while (ch.phaseacc < 0)
{
ch.phaseacc += ch.freq;
ch.adder = (ch.adder + 1) & 0x0F;
if (ch.adder < ch.duty_flip)
total += output;
else
total -= output;
num_times++;
}
ch.output_vol = total / num_times;
}
return ch.output_vol;
}
public class SYNCRECTANGLE public class SYNCRECTANGLE
{ {
// For sync // For sync
@ -54,6 +349,38 @@ namespace VirtualNes.Core
public byte holdnote; public byte holdnote;
public byte[] dummy = new byte[2]; public byte[] dummy = new byte[2];
public int vbl_length; public int vbl_length;
public void ZeroMemory()
{
Array.Clear(reg, 0, reg.Length);
enable = 0;
holdnote = 0;
Array.Clear(dummy, 0, dummy.Length);
vbl_length = 0;
}
}
public class RECTANGLE
{
public byte[] reg = new byte[4];
public byte enable;
public int vbl_length;
public int phaseacc;
public int freq;
public int output_vol;
public byte fixed_envelope;
public byte holdnote;
public byte volume;
public byte env_vol;
public int env_phase;
public int env_decay;
public int adder;
public int duty_flip;
} }
} }
} }

View File

@ -1,25 +1,95 @@
namespace VirtualNes.Core using System;
using System.Net;
namespace VirtualNes.Core
{ {
public class APU_VRC7 : APU_INTERFACE public class APU_VRC7 : APU_INTERFACE
{ {
OPLL VRC7_OPLL;
byte address;
public APU_VRC7()
{
Emu2413API.OPLL_init(3579545, 22050); // 仮のサンプリングレート
VRC7_OPLL = Emu2413API.OPLL_new();
if (VRC7_OPLL != null)
{
Emu2413API.OPLL_reset(VRC7_OPLL);
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
VRC7_OPLL.masterVolume = 128;
}
// 仮設定
Reset(APU_CLOCK, 22050);
}
public override void Dispose()
{
if (VRC7_OPLL != null)
{
Emu2413API.OPLL_delete(VRC7_OPLL);
VRC7_OPLL = null;
// OPLL_close(); // 無くても良い(中身無し)
}
}
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
//todo : 实现 if (VRC7_OPLL != null)
{
Emu2413API.OPLL_reset(VRC7_OPLL);
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
VRC7_OPLL.masterVolume = 128;
}
address = 0;
Setup(fClock, nRate);
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
//todo : 实现 Emu2413API.OPLL_setClock((UInt32)(fClock * 2.0f), (UInt32)nRate);
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
//todo : 实现 if (VRC7_OPLL != null)
{
if (addr == 0x9010)
{
address = data;
}
else if (addr == 0x9030)
{
Emu2413API.OPLL_writeReg(VRC7_OPLL, address, data);
}
}
} }
public override int Process(int channel) public override int Process(int channel)
{ {
//todo : 实现 if (VRC7_OPLL != null)
return Emu2413API.OPLL_calc(VRC7_OPLL);
return 0;
}
float[] blkmul = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f };
public override int GetFreq(int channel)
{
if (VRC7_OPLL != null && channel < 8)
{
int fno = ((VRC7_OPLL.reg[0x20 + channel] & 0x01) << 8) + VRC7_OPLL.reg[0x10 + channel];
int blk = (VRC7_OPLL.reg[0x20 + channel] >> 1) & 0x07;
if ((VRC7_OPLL.reg[0x20 + channel] & 0x10) != 0)
{
return (int)((256.0d * (double)fno * blkmul[blk]) / ((double)(1 << 18) / (3579545.0 / 72.0)));
}
}
return 0; return 0;
} }
} }

View File

@ -34,6 +34,13 @@ namespace VirtualNes.Core
m_length = length; m_length = length;
} }
public void SetArray(byte[] array, int offset)
{
m_rawArray = array;
m_offset = offset;
m_length = array.Length - offset;
}
public byte this[int index] public byte this[int index]
{ {
get get

View File

@ -80,10 +80,36 @@ namespace VirtualNes.Core
public UInt32 noiseB_idx; public UInt32 noiseB_idx;
public UInt32 noiseA_dphase; public UInt32 noiseA_dphase;
public UInt32 noiseB_dphase; public UInt32 noiseB_dphase;
public int masterVolume; /* 0min -- 64 -- 127 max (Liner) */
} }
public static class Emu2413API public static class Emu2413API
{ {
/* Bits for Pitch and Amp modulator */
public const int PM_PG_BITS = 8;
public const int PM_PG_WIDTH = 1 << PM_PG_BITS;
public const int PM_DP_BITS = 16;
public const int PM_DP_WIDTH = (1 << PM_DP_BITS);
public const int AM_PG_BITS = 8;
public const int AM_PG_WIDTH = (1 << AM_PG_BITS);
public const int AM_DP_BITS = 16;
public const int AM_DP_WIDTH = (1 << AM_DP_BITS);
/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */
public const int PM_AMP_BITS = 8;
public const int PM_AMP = (1 << PM_AMP_BITS);
/* PM speed(Hz) and depth(cent) */
public const double PM_SPEED = 6.4d;
public const double PM_DEPTH = 13.75d;
public const int OPLL_2413_TONE = 0;
public const int OPLL_VRC7_TONE = 1;
static int[] pmtable = new int[PM_PG_WIDTH];
static int[] amtable = new int[AM_PG_WIDTH];
public static void OPLL_init(UInt32 c, UInt32 r) public static void OPLL_init(UInt32 c, UInt32 r)
{ {
makePmTable(); makePmTable();
@ -97,7 +123,7 @@ namespace VirtualNes.Core
OPLL_setClock(c, r); OPLL_setClock(c, r);
} }
private static void OPLL_setClock(uint c, uint r) internal static void OPLL_setClock(uint c, uint r)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -138,6 +164,39 @@ namespace VirtualNes.Core
} }
private static void makePmTable() private static void makePmTable()
{
int i;
for (i = 0; i < PM_PG_WIDTH; i++)
pmtable[i] = (int)(PM_AMP * Math.Pow(2, PM_DEPTH * Math.Sin(2.0 * Math.PI * i / PM_PG_WIDTH) / 1200));
}
internal static OPLL OPLL_new()
{
throw new NotImplementedException();
}
internal static void OPLL_reset(OPLL vRC7_OPLL)
{
throw new NotImplementedException();
}
internal static void OPLL_reset_patch(OPLL vRC7_OPLL, int oPLL_VRC7_TONE)
{
throw new NotImplementedException();
}
internal static void OPLL_delete(OPLL vRC7_OPLL)
{
throw new NotImplementedException();
}
internal static void OPLL_writeReg(OPLL opll, UInt32 reg, UInt32 data)
{
throw new NotImplementedException();
}
internal static int OPLL_calc(OPLL opll)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -78,6 +78,13 @@ namespace VirtualNes
CPU_MEM_PAGE[page] = 0; CPU_MEM_PAGE[page] = 0;
} }
internal static void SetPROM_Bank(byte page, ByteArrayRef ptr, byte type)
{
CPU_MEM_BANK[page] = ptr;
CPU_MEM_TYPE[page] = type;
CPU_MEM_PAGE[page] = 0;
}
internal static void SetPROM_8K_Bank(byte page, int bank) internal static void SetPROM_8K_Bank(byte page, int bank)
{ {
bank %= PROM_8K_SIZE; bank %= PROM_8K_SIZE;

View File

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

View File

@ -132,6 +132,7 @@ namespace VirtualNes.Core
} }
} }
private ByteArrayRef _PROM_BANK = new ByteArrayRef();
//void Mapper001::Write(WORD addr, BYTE data) //void Mapper001::Write(WORD addr, BYTE data)
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
@ -145,11 +146,13 @@ namespace VirtualNes.Core
{ {
if (wram_bank != 0) if (wram_bank != 0)
{ {
SetPROM_Bank(3, &WRAM[0x2000], BANKTYPE_RAM); _PROM_BANK.SetArray(WRAM, 0x2000);
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
} }
else else
{ {
SetPROM_Bank(3, &WRAM[0x0000], BANKTYPE_RAM); _PROM_BANK.SetArray(WRAM, 0x0000);
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
} }
wram_bank = wram_count = 0; wram_bank = wram_count = 0;
} }
@ -290,11 +293,13 @@ namespace VirtualNes.Core
{ {
if (((reg[1] & 0x18) == 0)) if (((reg[1] & 0x18) == 0))
{ {
SetPROM_Bank(3, &WRAM[0x0000], BANKTYPE_RAM); _PROM_BANK.SetArray(WRAM, 0x0000);
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
} }
else else
{ {
SetPROM_Bank(3, &WRAM[0x2000], BANKTYPE_RAM); _PROM_BANK.SetArray(WRAM, 0x2000);
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,16 +1,19 @@
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Mapper005 Nintendo MMC5 // // Mapper005 Nintendo MMC5 //
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
using static VirtualNes.MMU; using System;
using static VirtualNes.Core.CPU; using static VirtualNes.Core.CPU;
using INT = System.Int32; using static VirtualNes.Core.PPU;
using static VirtualNes.MMU;
using BYTE = System.Byte; using BYTE = System.Byte;
using Codice.CM.Client.Differences; using INT = System.Int32;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
public class Mapper005 : Mapper public class Mapper005 : Mapper
{ {
public const int MMC5_IRQ_METAL = 1 << 0;
BYTE sram_size; BYTE sram_size;
BYTE prg_size; // $5100 BYTE prg_size; // $5100
@ -35,8 +38,18 @@ namespace VirtualNes.Core
BYTE chr_mode; // $5120-$512B use BYTE chr_mode; // $5120-$512B use
//BYTE chr_page[2][8]; //BYTE chr_page[2][8];
BYTE[,] chr_page = new byte[2, 8]; // $5120-$512B BYTE[,] chr_page = new byte[2, 8]; // $5120-$512B
LPBYTE BG_MEM_BANK[8]; // BGパターン用バンク // BGパターン用バンク
BYTE BG_MEM_PAGE[8]; ByteArrayRef[] BG_MEM_BANK = new ByteArrayRef[8]{
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
new ByteArrayRef(),
};
BYTE[] BG_MEM_PAGE = new byte[8];
BYTE irq_status; // $5204(R) BYTE irq_status; // $5204(R)
BYTE irq_enable; // $5204(W) BYTE irq_enable; // $5204(W)
@ -50,7 +63,7 @@ namespace VirtualNes.Core
public override void Reset() public override void Reset()
{ {
INT i; byte i;
prg_size = 3; prg_size = 3;
chr_size = 3; chr_size = 3;
@ -83,8 +96,8 @@ namespace VirtualNes.Core
chr_mode = 0; chr_mode = 0;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
chr_page[0][i] = i; chr_page[0, i] = i;
chr_page[1][i] = 4 + (i & 0x03); chr_page[1, i] = (byte)(4 + (i & 0x03));
} }
SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1); SetPROM_32K_Bank(PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1, PROM_8K_SIZE - 1);
@ -92,7 +105,7 @@ namespace VirtualNes.Core
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
BG_MEM_BANK[i] = VROM + 0x0400 * i; BG_MEM_BANK[i].SetArray(VROM, 0x0400 * i);
BG_MEM_PAGE[i] = i; BG_MEM_PAGE[i] = i;
} }
@ -130,7 +143,7 @@ namespace VirtualNes.Core
if (crc == 0x95ca9ec7) if (crc == 0x95ca9ec7)
{ // Castlevania 3 - Dracula's Curse(U) { // Castlevania 3 - Dracula's Curse(U)
nes.SetRenderMethod(NES::TILE_RENDER); nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
} }
if (crc == 0xcd9acf43) if (crc == 0xcd9acf43)
@ -161,15 +174,15 @@ namespace VirtualNes.Core
case 0x5204: case 0x5204:
data = irq_status; data = irq_status;
// irq_status = 0; // irq_status = 0;
irq_status &= ~0x80; irq_status = (byte)(irq_status & ~0x80);
nes.cpu.ClrIRQ(IRQ_MAPPER); nes.cpu.ClrIRQ(IRQ_MAPPER);
break; break;
case 0x5205: case 0x5205:
data = mult_a * mult_b; data = (byte)(mult_a * mult_b);
break; break;
case 0x5206: case 0x5206:
data = (BYTE)(((WORD)mult_a * (WORD)mult_b) >> 8); data = (BYTE)((mult_a * mult_b) >> 8);
break; break;
} }
@ -191,39 +204,33 @@ namespace VirtualNes.Core
//void Mapper005::WriteLow(WORD addr, BYTE data) //void Mapper005::WriteLow(WORD addr, BYTE data)
public override void WriteLow(ushort addr, byte data) public override void WriteLow(ushort addr, byte data)
{ {
INT i; byte i;
#if FALSE//0
if( addr >= 0x5000 && addr <=0x50FF ) {
DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
}
#endif
switch (addr) switch (addr)
{ {
case 0x5100: case 0x5100:
prg_size = data & 0x03; prg_size = (byte)(data & 0x03);
break; break;
case 0x5101: case 0x5101:
chr_size = data & 0x03; chr_size = (byte)(data & 0x03);
break; break;
case 0x5102: case 0x5102:
sram_we_a = data & 0x03; sram_we_a = (byte)(data & 0x03);
break; break;
case 0x5103: case 0x5103:
sram_we_b = data & 0x03; sram_we_b = (byte)(data & 0x03);
break; break;
case 0x5104: case 0x5104:
graphic_mode = data & 0x03; graphic_mode = (byte)(data & 0x03);
break; break;
case 0x5105: case 0x5105:
nametable_mode = data; nametable_mode = data;
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
nametable_type[i] = data & 0x03; nametable_type[i] = (byte)(data & 0x03);
SetVRAM_1K_Bank(8 + i, nametable_type[i]); SetVRAM_1K_Bank((byte)(8 + i), nametable_type[i]);
data >>= 2; data >>= 2;
} }
break; break;
@ -232,11 +239,11 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
fill_chr = data; fill_chr = data;
break; break;
case 0x5107: case 0x5107:
fill_pal = data & 0x03; fill_pal = (byte)(data & 0x03);
break; break;
case 0x5113: case 0x5113:
SetBank_SRAM(3, data & 0x07); SetBank_SRAM(3, (byte)(data & 0x07));
break; break;
case 0x5114: case 0x5114:
@ -255,7 +262,7 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
case 0x5126: case 0x5126:
case 0x5127: case 0x5127:
chr_mode = 0; chr_mode = 0;
chr_page[0][addr & 0x07] = data; chr_page[0, addr & 0x07] = data;
SetBank_PPU(); SetBank_PPU();
break; break;
@ -264,8 +271,8 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
case 0x512A: case 0x512A:
case 0x512B: case 0x512B:
chr_mode = 1; chr_mode = 1;
chr_page[1][(addr & 0x03) + 0] = data; chr_page[1, (addr & 0x03) + 0] = data;
chr_page[1][(addr & 0x03) + 4] = data; chr_page[1, (addr & 0x03) + 4] = data;
SetBank_PPU(); SetBank_PPU();
break; break;
@ -276,7 +283,7 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
split_scroll = data; split_scroll = data;
break; break;
case 0x5202: case 0x5202:
split_page = data & 0x3F; split_page = (byte)(data & 0x3F);
break; break;
case 0x5203: case 0x5203:
@ -402,30 +409,31 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
case 4: case 4:
if (prg_size == 3) if (prg_size == 3)
{ {
SetBank_SRAM(4, data & 0x07); SetBank_SRAM(4, (byte)(data & 0x07));
} }
break; break;
case 5: case 5:
if (prg_size == 1 || prg_size == 2) if (prg_size == 1 || prg_size == 2)
{ {
SetBank_SRAM(4, (data & 0x06) + 0); SetBank_SRAM(4, (byte)((data & 0x06) + 0));
SetBank_SRAM(5, (data & 0x06) + 1); SetBank_SRAM(5, (byte)((data & 0x06) + 1));
} }
else if (prg_size == 3) else if (prg_size == 3)
{ {
SetBank_SRAM(5, data & 0x07); SetBank_SRAM(5, (byte)(data & 0x07));
} }
break; break;
case 6: case 6:
if (prg_size == 2 || prg_size == 3) if (prg_size == 2 || prg_size == 3)
{ {
SetBank_SRAM(6, data & 0x07); SetBank_SRAM(6, (byte)(data & 0x07));
} }
break; break;
} }
} }
} }
private ByteArrayRef _prom_bank = new ByteArrayRef();
void SetBank_SRAM(BYTE page, BYTE data) void SetBank_SRAM(BYTE page, BYTE data)
{ {
if (sram_size == 0) data = (byte)((data > 3) ? 8 : 0); if (sram_size == 0) data = (byte)((data > 3) ? 8 : 0);
@ -435,7 +443,9 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
if (data != 8) if (data != 8)
{ {
SetPROM_Bank(page, &WRAM[0x2000 * data], BANKTYPE_RAM); int offset = 0x2000 * data;
_prom_bank.SetArray(WRAM, offset, WRAM.Length - offset);
SetPROM_Bank(page, _prom_bank, BANKTYPE_RAM);
CPU_MEM_PAGE[page] = data; CPU_MEM_PAGE[page] = data;
} }
else else
@ -444,7 +454,7 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
} }
} }
void Mapper005::SetBank_PPU() void SetBank_PPU()
{ {
INT i; INT i;
@ -454,27 +464,27 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
switch (chr_size) switch (chr_size)
{ {
case 0: case 0:
SetVROM_8K_Bank(chr_page[0][7]); SetVROM_8K_Bank(chr_page[0, 7]);
break; break;
case 1: case 1:
SetVROM_4K_Bank(0, chr_page[0][3]); SetVROM_4K_Bank(0, chr_page[0, 3]);
SetVROM_4K_Bank(4, chr_page[0][7]); SetVROM_4K_Bank(4, chr_page[0, 7]);
break; break;
case 2: case 2:
SetVROM_2K_Bank(0, chr_page[0][1]); SetVROM_2K_Bank(0, chr_page[0, 1]);
SetVROM_2K_Bank(2, chr_page[0][3]); SetVROM_2K_Bank(2, chr_page[0, 3]);
SetVROM_2K_Bank(4, chr_page[0][5]); SetVROM_2K_Bank(4, chr_page[0, 5]);
SetVROM_2K_Bank(6, chr_page[0][7]); SetVROM_2K_Bank(6, chr_page[0, 7]);
break; break;
case 3: case 3:
SetVROM_8K_Bank(chr_page[0][0], SetVROM_8K_Bank(chr_page[0, 0],
chr_page[0][1], chr_page[0, 1],
chr_page[0][2], chr_page[0, 2],
chr_page[0][3], chr_page[0, 3],
chr_page[0][4], chr_page[0, 4],
chr_page[0][5], chr_page[0, 5],
chr_page[0][6], chr_page[0, 6],
chr_page[0][7]); chr_page[0, 7]);
break; break;
} }
} }
@ -486,46 +496,46 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
case 0: case 0:
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
BG_MEM_BANK[i] = VROM + 0x2000 * (chr_page[1][7] % VROM_8K_SIZE) + 0x0400 * i; BG_MEM_BANK[i].SetArray(VROM, 0x2000 * (chr_page[1, 7] % VROM_8K_SIZE) + 0x0400 * i);
BG_MEM_PAGE[i] = (chr_page[1][7] % VROM_8K_SIZE) * 8 + i; BG_MEM_PAGE[i] = (byte)((chr_page[1, 7] % VROM_8K_SIZE) * 8 + i);
} }
break; break;
case 1: case 1:
for (i = 0; i < 4; i++) for (i = 0; i < 4; i++)
{ {
BG_MEM_BANK[i + 0] = VROM + 0x1000 * (chr_page[1][3] % VROM_4K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 0].SetArray(VROM, 0x1000 * (chr_page[1, 3] % VROM_4K_SIZE) + 0x0400 * i);
BG_MEM_BANK[i + 4] = VROM + 0x1000 * (chr_page[1][7] % VROM_4K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 4].SetArray(VROM, 0x1000 * (chr_page[1, 7] % VROM_4K_SIZE) + 0x0400 * i);
BG_MEM_PAGE[i + 0] = (chr_page[1][3] % VROM_4K_SIZE) * 4 + i; BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 3] % VROM_4K_SIZE) * 4 + i);
BG_MEM_PAGE[i + 4] = (chr_page[1][7] % VROM_4K_SIZE) * 4 + i; BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 7] % VROM_4K_SIZE) * 4 + i);
} }
break; break;
case 2: case 2:
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
{ {
BG_MEM_BANK[i + 0] = VROM + 0x0800 * (chr_page[1][1] % VROM_2K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 0].SetArray(VROM, 0x0800 * (chr_page[1, 1] % VROM_2K_SIZE) + 0x0400 * i);
BG_MEM_BANK[i + 2] = VROM + 0x0800 * (chr_page[1][3] % VROM_2K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 2].SetArray(VROM, 0x0800 * (chr_page[1, 3] % VROM_2K_SIZE) + 0x0400 * i);
BG_MEM_BANK[i + 4] = VROM + 0x0800 * (chr_page[1][5] % VROM_2K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 4].SetArray(VROM, 0x0800 * (chr_page[1, 5] % VROM_2K_SIZE) + 0x0400 * i);
BG_MEM_BANK[i + 6] = VROM + 0x0800 * (chr_page[1][7] % VROM_2K_SIZE) + 0x0400 * i; BG_MEM_BANK[i + 6].SetArray(VROM, 0x0800 * (chr_page[1, 7] % VROM_2K_SIZE) + 0x0400 * i);
BG_MEM_PAGE[i + 0] = (chr_page[1][1] % VROM_2K_SIZE) * 2 + i; BG_MEM_PAGE[i + 0] = (byte)((chr_page[1, 1] % VROM_2K_SIZE) * 2 + i);
BG_MEM_PAGE[i + 2] = (chr_page[1][3] % VROM_2K_SIZE) * 2 + i; BG_MEM_PAGE[i + 2] = (byte)((chr_page[1, 3] % VROM_2K_SIZE) * 2 + i);
BG_MEM_PAGE[i + 4] = (chr_page[1][5] % VROM_2K_SIZE) * 2 + i; BG_MEM_PAGE[i + 4] = (byte)((chr_page[1, 5] % VROM_2K_SIZE) * 2 + i);
BG_MEM_PAGE[i + 6] = (chr_page[1][7] % VROM_2K_SIZE) * 2 + i; BG_MEM_PAGE[i + 6] = (byte)((chr_page[1, 7] % VROM_2K_SIZE) * 2 + i);
} }
break; break;
case 3: case 3:
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
BG_MEM_BANK[i] = VROM + 0x0400 * (chr_page[1][i] % VROM_1K_SIZE); BG_MEM_BANK[i].SetArray(VROM, 0x0400 * (chr_page[1, i] % VROM_1K_SIZE));
BG_MEM_PAGE[i] = (chr_page[1][i] % VROM_1K_SIZE) + i; BG_MEM_PAGE[i] = (byte)((chr_page[1, i] % VROM_1K_SIZE) + i);
} }
break; break;
} }
} }
} }
void Mapper005::HSync(INT scanline) public override void HSync(int scanline)
{ {
if (irq_type & MMC5_IRQ_METAL) if ((irq_type & MMC5_IRQ_METAL) != 0)
{ {
if (irq_scanline == irq_line) if (irq_scanline == irq_line)
{ {
@ -540,14 +550,14 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
irq_status |= 0x40; irq_status |= 0x40;
irq_clear = 0; irq_clear = 0;
} }
else if (irq_type & MMC5_IRQ_METAL) else if ((irq_type & MMC5_IRQ_METAL) != 0)
{ {
irq_scanline = 0; irq_scanline = 0;
irq_status &= ~0x80; irq_status = (byte)(irq_status & ~0x80);
irq_status &= ~0x40; irq_status = (byte)(irq_status & ~0x40);
} }
if (!(irq_type & MMC5_IRQ_METAL)) if ((irq_type & MMC5_IRQ_METAL) == 0)
{ {
if (irq_scanline == irq_line) if (irq_scanline == irq_line)
{ {
@ -557,28 +567,17 @@ DEBUGOUT( "$%04X=%02X C:%10d\n", addr, data, nes.cpu.GetTotalCycles() );
if (++irq_clear > 2) if (++irq_clear > 2)
{ {
irq_scanline = 0; irq_scanline = 0;
irq_status &= ~0x80; irq_status = (byte)(irq_status & ~0x80);
irq_status &= ~0x40; irq_status = (byte)(irq_status & ~0x40);
nes.cpu.ClrIRQ(IRQ_MAPPER); nes.cpu.ClrIRQ(IRQ_MAPPER);
} }
} }
if ((irq_enable & 0x80) && (irq_status & 0x80) && (irq_status & 0x40)) if ((irq_enable & 0x80) != 0 && (irq_status & 0x80) != 0 && (irq_status & 0x40) != 0)
{ {
nes.cpu.SetIRQ(IRQ_MAPPER); nes.cpu.SetIRQ(IRQ_MAPPER);
/// nes.cpu.IRQ_NotPending(); /// nes.cpu.IRQ_NotPending();
#if 0
{
LPBYTE lpScn = nes.ppu.GetScreenPtr();
lpScn = lpScn+(256+16)*scanline;
for( INT i = 0; i < 256+16; i++ ) {
lpScn[i] = 22;
}
}
#endif
} }
// For Split mode! // For Split mode!
@ -618,7 +617,7 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
//void Mapper005::PPU_ExtLatchX(INT x) //void Mapper005::PPU_ExtLatchX(INT x)
public override void PPU_ExtLatchX(int x) public override void PPU_ExtLatchX(int x)
{ {
split_x = x; split_x = (byte)x;
} }
//void Mapper005::PPU_ExtLatch(WORD addr, BYTE& chr_l, BYTE& chr_h, BYTE& attr ) //void Mapper005::PPU_ExtLatch(WORD addr, BYTE& chr_l, BYTE& chr_h, BYTE& attr )
@ -631,15 +630,15 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
tile_yofs = nes.ppu.GetTILEY(); tile_yofs = nes.ppu.GetTILEY();
bSplit = FALSE; bSplit = false;
if (split_control & 0x80) if ((split_control & 0x80) != 0)
{ {
if (!(split_control & 0x40)) if ((split_control & 0x40) == 0)
{ {
// Left side // Left side
if ((split_control & 0x1F) > split_x) if ((split_control & 0x1F) > split_x)
{ {
bSplit = TRUE; bSplit = true;
} }
} }
else else
@ -647,7 +646,7 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
// Right side // Right side
if ((split_control & 0x1F) <= split_x) if ((split_control & 0x1F) <= split_x)
{ {
bSplit = TRUE; bSplit = true;
} }
} }
} }
@ -660,13 +659,13 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
if (graphic_mode == 1) if (graphic_mode == 1)
{ {
// ExGraphic mode // ExGraphic mode
ntbladr = 0x2000 + (addr & 0x0FFF); ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
// Get Nametable // Get Nametable
tileadr = fill_chr * 0x10 + tile_yofs; tileadr = (ushort)(fill_chr * 0x10 + tile_yofs);
// Get TileBank // Get TileBank
tilebank = 0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE); tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE));
// Attribute // Attribute
attr = (fill_pal << 2) & 0x0C; attr = (byte)((fill_pal << 2) & 0x0C);
// Get Pattern // Get Pattern
chr_l = VROM[tilebank + tileadr]; chr_l = VROM[tilebank + tileadr];
chr_h = VROM[tilebank + tileadr + 8]; chr_h = VROM[tilebank + tileadr + 8];
@ -674,11 +673,11 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
else else
{ {
// Normal // Normal
tileofs = (PPUREG[0] & PPU_BGTBL_BIT) ? 0x1000 : 0x0000; tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000);
tileadr = tileofs + fill_chr * 0x10 + tile_yofs; tileadr = (ushort)(tileofs + fill_chr * 0x10 + tile_yofs);
attr = (fill_pal << 2) & 0x0C; attr = (byte)((fill_pal << 2) & 0x0C);
// Get Pattern // Get Pattern
if (chr_type) if (chr_type != 0)
{ {
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
@ -693,13 +692,13 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
else if (graphic_mode == 1) else if (graphic_mode == 1)
{ {
// ExGraphic mode // ExGraphic mode
ntbladr = 0x2000 + (addr & 0x0FFF); ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
// Get Nametable // Get Nametable
tileadr = (WORD)PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs; tileadr = (ushort)(PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs);
// Get TileBank // Get TileBank
tilebank = 0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE); tilebank = (uint)(0x1000 * ((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0x3F) % VROM_4K_SIZE));
// Get Attribute // Get Attribute
attr = (VRAM[0x0800 + (ntbladr & 0x03FF)] & 0xC0) >> 4; attr = (byte)((VRAM[0x0800 + (ntbladr & 0x03FF)] & 0xC0) >> 4);
// Get Pattern // Get Pattern
chr_l = VROM[tilebank + tileadr]; chr_l = VROM[tilebank + tileadr];
chr_h = VROM[tilebank + tileadr + 8]; chr_h = VROM[tilebank + tileadr + 8];
@ -707,18 +706,18 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
else else
{ {
// Normal or ExVRAM // Normal or ExVRAM
tileofs = (PPUREG[0] & PPU_BGTBL_BIT) ? 0x1000 : 0x0000; tileofs = (ushort)((PPUREG[0] & PPU_BGTBL_BIT) != 0 ? 0x1000 : 0x0000);
ntbladr = 0x2000 + (addr & 0x0FFF); ntbladr = (ushort)(0x2000 + (addr & 0x0FFF));
attradr = 0x23C0 + (addr & 0x0C00) + ((addr & 0x0380) >> 4) + ((addr & 0x001C) >> 2); attradr = (ushort)(0x23C0 + (addr & 0x0C00) + ((addr & 0x0380) >> 4) + ((addr & 0x001C) >> 2));
// Get Nametable // Get Nametable
tileadr = tileofs + PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs; tileadr = (ushort)(tileofs + PPU_MEM_BANK[ntbladr >> 10][ntbladr & 0x03FF] * 0x10 + tile_yofs);
// Get Attribute // Get Attribute
attr = PPU_MEM_BANK[attradr >> 10][attradr & 0x03FF]; attr = PPU_MEM_BANK[attradr >> 10][attradr & 0x03FF];
if (ntbladr & 0x0002) attr >>= 2; if ((ntbladr & 0x0002) != 0) attr >>= 2;
if (ntbladr & 0x0040) attr >>= 4; if ((ntbladr & 0x0040) != 0) attr >>= 4;
attr = (attr & 0x03) << 2; attr = (byte)((attr & 0x03) << 2);
// Get Pattern // Get Pattern
if (chr_type) if (chr_type != 0)
{ {
chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF]; chr_l = PPU_MEM_BANK[tileadr >> 10][tileadr & 0x03FF];
chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8]; chr_h = PPU_MEM_BANK[tileadr >> 10][(tileadr & 0x03FF) + 8];
@ -732,16 +731,16 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
} }
else else
{ {
ntbladr = ((split_addr & 0x03E0) | (split_x & 0x1F)) & 0x03FF; ntbladr = (ushort)(((split_addr & 0x03E0) | (split_x & 0x1F)) & 0x03FF);
// Get Split TileBank // Get Split TileBank
tilebank = 0x1000 * ((INT)split_page % VROM_4K_SIZE); tilebank = (uint)(0x1000 * (split_page % VROM_4K_SIZE));
tileadr = (ushort)VRAM[0x0800 + ntbladr] * 0x10 + split_yofs; tileadr = (ushort)(VRAM[0x0800 + ntbladr] * 0x10 + split_yofs);
// Get Attribute // Get Attribute
attradr = 0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2); attradr = (ushort)(0x03C0 + ((ntbladr & 0x0380) >> 4) + ((ntbladr & 0x001C) >> 2));
attr = VRAM[0x0800 + attradr]; attr = VRAM[0x0800 + attradr];
if (ntbladr & 0x0002) attr >>= 2; if ((ntbladr & 0x0002) != 0) attr >>= 2;
if (ntbladr & 0x0040) attr >>= 4; if ((ntbladr & 0x0040) != 0) attr >>= 4;
attr = (attr & 0x03) << 2; attr = (byte)((attr & 0x03) << 2);
// Get Pattern // Get Pattern
chr_l = VROM[tilebank + tileadr]; chr_l = VROM[tilebank + tileadr];
chr_h = VROM[tilebank + tileadr + 8]; chr_h = VROM[tilebank + tileadr + 8];
@ -781,7 +780,7 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
{ {
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
p[24 + j * 8 + i] = chr_page[j][i]; p[24 + j * 8 + i] = chr_page[j, i];
} }
} }
// for( i = 0; i < 8; i++ ) { // for( i = 0; i < 8; i++ ) {
@ -823,7 +822,7 @@ LPBYTE lpScn = nes.ppu.GetScreenPtr();
{ {
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
{ {
chr_page[j][i] = p[24 + j * 8 + i]; chr_page[j, i] = p[24 + j * 8 + i];
} }
} }
// // BGバンクの再設定処理 // // BGバンクの再設定処理

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ namespace VirtualNes.Core
BYTE irq_latch; BYTE irq_latch;
BYTE irq_request; BYTE irq_request;
int MMC4prg, MMC4chr; int MMC4prg, MMC4chr;
public Mapper245(NES parent) : base(parent) public Mapper245(NES parent) : base(parent)
{ {
} }
@ -164,9 +165,9 @@ namespace VirtualNes.Core
void SetBank_PPU() void SetBank_PPU()
{ {
if ((VROM_1K_SIZE) != 0) if (VROM_1K_SIZE != 0)
{ {
if (((reg[0] & 0x80)! + 0) != 0) if ((reg[0] & 0x80) != 0)
{ {
SetVROM_8K_Bank(chr4, chr5, chr6, chr7, SetVROM_8K_Bank(chr4, chr5, chr6, chr7,
chr23 + 1, chr23, chr01 + 1, chr01); chr23 + 1, chr23, chr01 + 1, chr01);

View File

@ -1,5 +1,6 @@
using Codice.CM.Client.Differences; using Codice.CM.Client.Differences;
using System; using System;
using System.Collections.Generic;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
@ -25,6 +26,19 @@ namespace VirtualNes.Core
public uint pad1bit, pad2bit, pad3bit, pad4bit; public uint pad1bit, pad2bit, pad3bit, pad4bit;
private static int[] ren10fps = new int[6] { 1, 1, 1, 0, 0, 0 };
private static int[] ren15fps = new int[4] { 1, 1, 0, 0 };
private static int[] ren20fps = new int[3] { 1, 1, 0 };
private static int[] ren30fps = new int[2] { 1, 0 };
private static int[] renmask = new int[4] { 6, 4, 3, 2 };
public static Dictionary<int, int[]> rentbl = new Dictionary<int, int[]>()
{
{0,ren10fps },
{1,ren15fps },
{2,ren20fps },
{3,ren30fps },
};
public PAD(NES parent) public PAD(NES parent)
{ {
nes = parent; nes = parent;
@ -405,6 +419,104 @@ namespace VirtualNes.Core
} }
} }
public void Sync(ControllerState state)
{
padbit[0] = SyncSub(0, state);
padbit[1] = SyncSub(1, state);
padbit[2] = SyncSub(2, state);
padbit[3] = SyncSub(3, state);
// Mic
micbit = 0;
if (state.HasButton(1, EnumButtonType.MIC)) micbit |= 4;
// For Excontroller
if (expad != null)
{
expad.Sync();
}
}
private byte SyncSub(int no, ControllerState state)
{
ushort bit = 0;
// Up
if (state.HasButton(no, EnumButtonType.UP))
bit |= 1 << 4;
// Down
if (state.HasButton(no, EnumButtonType.DOWN))
bit |= 1 << 5;
// Left
if (state.HasButton(no, EnumButtonType.LEFT))
bit |= 1 << 6;
// Right
if (state.HasButton(no, EnumButtonType.RIGHT))
bit |= 1 << 7;
// 同時入力を禁止する
// if( (bit&((1<<4)|(1<<5))) == ((1<<4)|(1<<5)) )
// bit &= ~((1<<4)|(1<<5));
if ((bit & ((1 << 6) | (1 << 7))) == ((1 << 6) | (1 << 7)))
bit = (byte)(bit & ~((1 << 6) | (1 << 7)));
// A
if (state.HasButton(no, EnumButtonType.A)) bit |= 1 << 0;
// B
if (state.HasButton(no, EnumButtonType.B)) bit |= 1 << 1;
// Select
if (state.HasButton(no, EnumButtonType.SELECT)) bit |= 1 << 2;
// Start
if (state.HasButton(no, EnumButtonType.START)) bit |= 1 << 3;
// A rapid setup
if ((bit & (1 << 8)) != 0)
{
int spd = Supporter.Config.controller.nRapid[no][0];
if (spd >= 3) spd = 3;
int[] tbl = rentbl[spd];
if (padcnt[no][0] >= renmask[spd])
padcnt[no][0] = 0;
if ((tbl[padcnt[no][0]]) != 0)
bit |= (1 << 0);
else
bit = (byte)(bit & ~(1 << 0));
padcnt[no][0]++;
}
else
{
padcnt[no][0] = 0;
}
// B rapid setup
if ((bit & (1 << 9)) != 0)
{
int spd = Supporter.Config.controller.nRapid[no][1];
if (spd >= 3) spd = 3;
int[] tbl = rentbl[spd];
if (padcnt[no][1] >= renmask[spd])
padcnt[no][1] = 0;
if ((tbl[padcnt[no][1]]) != 0)
bit |= (1 << 1);
else
bit = (byte)(bit & ~(1 << 1));
padcnt[no][1]++;
}
else
{
padcnt[no][1] = 0;
}
return (byte)(bit & 0xFF);
}
internal bool IsZapperMode() internal bool IsZapperMode()
{ {
return bZapperMode; return bZapperMode;

View File

@ -1147,6 +1147,16 @@ namespace VirtualNes.Core
bChrLatch = bMode; bChrLatch = bMode;
} }
internal void SetExtNameTableMode(bool bMode)
{
bExtNameTable = bMode;
}
internal void SetExtMonoMode(bool bMode)
{
bExtMono = bMode;
}
public struct Sprite public struct Sprite
{ {
public byte y public byte y

View File

@ -246,6 +246,7 @@ namespace VirtualNes.Core
if (Supporter.TryGetMapperNo(this, out int mapperNo)) if (Supporter.TryGetMapperNo(this, out int mapperNo))
{ {
Debuger.Log($"ROMDB Set Mapper #{mapper:000} to #{mapperNo:000}");
mapper = mapperNo; mapper = mapperNo;
} }

View File

@ -0,0 +1,51 @@
using System;
namespace VirtualNes.Core
{
public struct ControllerState
{
private uint raw0;
private uint raw1;
private uint raw2;
private uint raw3;
public ControllerState(
EnumButtonType player0_buttons,
EnumButtonType player1_buttons,
EnumButtonType player2_buttons,
EnumButtonType player3_buttons)
{
raw0 = (uint)player0_buttons;
raw1 = (uint)player1_buttons;
raw2 = (uint)player2_buttons;
raw3 = (uint)player3_buttons;
}
public bool HasButton(int player, EnumButtonType button)
{
uint raw = 0;
switch (player)
{
case 0: raw = raw0; break;
case 1: raw = raw1; break;
case 2: raw = raw2; break;
case 3: raw = raw3; break;
}
return (raw & (uint)button) == (uint)button;
}
}
[Flags]
public enum EnumButtonType
{
UP = 1,
DOWN = 2,
LEFT = 4,
RIGHT = 8,
A = 16,
B = 32,
SELECT = 64,
START = 128,
MIC = 256
}
}

View File

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

View File

@ -1,6 +1,27 @@
namespace VirtualNes.Core using Codice.CM.Client.Differences;
namespace VirtualNes.Core
{ {
public class CfgController public class CfgController
{ {
public ushort[][] nButton = new ushort[4][]
{
new ushort[64],new ushort[64], new ushort[64], new ushort[64],
};
public ushort[][] nRapid = new ushort[4][]
{
new ushort[2],new ushort[2],new ushort[2],new ushort[2],
};
// 0:Crazy Climber
// 1:Famly Trainer
// 2:Exciting Boxing
// 3:Mahjang
public ushort[][] nExButton = new ushort[4][]
{
new ushort[64],new ushort[64], new ushort[64], new ushort[64],
};
public ushort[] nVSUnisystem = new ushort[64];
} }
} }

View File

@ -1,4 +1,6 @@
namespace VirtualNes.Core using System;
namespace VirtualNes.Core
{ {
public class EmulatorConfig public class EmulatorConfig
{ {

View File

@ -54,6 +54,11 @@ namespace VirtualNes.Core
return s_support.TryGetMapperNo(rom, out mapperNo); return s_support.TryGetMapperNo(rom, out mapperNo);
} }
public static ControllerState GetControllerState()
{
return s_support.GetControllerState();
}
public static EmulatorConfig Config => s_support.Config; public static EmulatorConfig Config => s_support.Config;
} }
@ -70,5 +75,6 @@ namespace VirtualNes.Core
void SaveFile(byte[] fileData, string directPath, string fileName); void SaveFile(byte[] fileData, string directPath, string fileName);
Stream OpenFile(string directPath, string fileName); Stream OpenFile(string directPath, string fileName);
bool TryGetMapperNo(ROM rom, out int mapperNo); bool TryGetMapperNo(ROM rom, out int mapperNo);
ControllerState GetControllerState();
} }
} }

View File

@ -82,7 +82,7 @@ PlayerSettings:
androidFullscreenMode: 1 androidFullscreenMode: 1
defaultIsNativeResolution: 1 defaultIsNativeResolution: 1
macRetinaSupport: 1 macRetinaSupport: 1
runInBackground: 0 runInBackground: 1
captureSingleScreen: 0 captureSingleScreen: 0
muteOtherAudioSources: 0 muteOtherAudioSources: 0
Prepare IOS For Recording: 0 Prepare IOS For Recording: 0