Compare commits
7 Commits
ed93542f48
...
20f28469b2
Author | SHA1 | Date | |
---|---|---|---|
20f28469b2 | |||
|
8952b842b1 | ||
|
b8c116aa28 | ||
|
580de2d245 | ||
|
aad4c1775a | ||
|
66f0cc5bd7 | ||
|
e4da00136d |
3
.gitignore
vendored
3
.gitignore
vendored
@ -14,3 +14,6 @@
|
||||
/AxibugEmuOnline.Client/ProjectSettings/AutoStreamingSettings.asset
|
||||
/AxibugEmuOnline.Client/Logs
|
||||
/virtuanessrc097-master/save
|
||||
/virtuanessrc097-master/.vs
|
||||
/virtuanessrc097-master/Debug
|
||||
/virtuanessrc097-master/VirtuaNES.ini
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -323,6 +323,10 @@ PrefabInstance:
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 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}
|
||||
propertyPath: m_Name
|
||||
value: NesEmulator
|
||||
|
@ -1,47 +1,51 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using UnityEngine;
|
||||
using UnityEngine;
|
||||
using VirtualNes.Core;
|
||||
|
||||
public class AudioProvider : MonoBehaviour
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
[SerializeField]
|
||||
private AudioSource m_as;
|
||||
|
||||
private SoundBuffer _buffer = new SoundBuffer(4096);
|
||||
|
||||
public void Initialize()
|
||||
public class AudioProvider : MonoBehaviour
|
||||
{
|
||||
var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false);
|
||||
public NesEmulator NesEmu;
|
||||
|
||||
dummy.SetData(new float[] { 1 }, 0);
|
||||
m_as.clip = dummy; //just to let unity play the audiosource
|
||||
m_as.loop = true;
|
||||
m_as.spatialBlend = 1;
|
||||
m_as.Play();
|
||||
}
|
||||
[SerializeField]
|
||||
private AudioSource m_as;
|
||||
|
||||
void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
int step = channels;
|
||||
|
||||
var bufferCount = _buffer.Available();
|
||||
|
||||
for (int i = 0; i < data.Length; i += step)
|
||||
private SoundBuffer _buffer = new SoundBuffer(4096);
|
||||
public void Start()
|
||||
{
|
||||
float rawFloat = 0;
|
||||
if (_buffer.TryRead(out byte rawData))
|
||||
rawFloat = rawData / 255f;
|
||||
var dummy = AudioClip.Create("dummy", 1, 1, AudioSettings.outputSampleRate, false);
|
||||
|
||||
data[i] = rawFloat;
|
||||
for (int fill = 1; fill < step; fill++)
|
||||
data[i + fill] = rawFloat;
|
||||
dummy.SetData(new float[] { 1 }, 0);
|
||||
m_as.clip = dummy; //just to let unity play the audiosource
|
||||
m_as.loop = true;
|
||||
m_as.spatialBlend = 1;
|
||||
m_as.Play();
|
||||
}
|
||||
|
||||
void OnAudioFilterRead(float[] data, int channels)
|
||||
{
|
||||
int step = channels;
|
||||
|
||||
if (NesEmu == null || NesEmu.NesCore == null) return;
|
||||
|
||||
ProcessSound(NesEmu.NesCore, (uint)(data.Length / channels));
|
||||
|
||||
for (int i = 0; i < data.Length; i += step)
|
||||
{
|
||||
float rawFloat = 0;
|
||||
if (_buffer.TryRead(out byte rawData))
|
||||
rawFloat = rawData / 255f;
|
||||
|
||||
data[i] = rawFloat;
|
||||
for (int fill = 1; fill < step; fill++)
|
||||
data[i + fill] = rawFloat;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessSound(NES nes, uint feedCount)
|
||||
{
|
||||
nes.apu.Process(_buffer, feedCount);
|
||||
}
|
||||
}
|
||||
|
||||
public void ProcessSound(NES nes)
|
||||
{
|
||||
nes.apu.Process(_buffer, (uint)(Supporter.Config.sound.nRate * Time.deltaTime));
|
||||
}
|
||||
}
|
||||
}
|
@ -98,5 +98,11 @@ namespace AxibugEmuOnline.Client
|
||||
var db = Resources.Load<RomDB>("NES/ROMDB");
|
||||
return db.GetMapperNo(rom.GetPROM_CRC(), out mapperNo);
|
||||
}
|
||||
|
||||
public ControllerState GetControllerState()
|
||||
{
|
||||
var mapper = NesControllerMapper.Get();
|
||||
return mapper.CreateState();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66fc8233a79cd254f8d005452dcd4ac0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -9,15 +9,24 @@ namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class NesEmulator : MonoBehaviour
|
||||
{
|
||||
private NES m_nesIns;
|
||||
public NES NesCore { get; private set; }
|
||||
|
||||
public VideoProvider VideoProvider;
|
||||
public AudioProvider AudioProvider;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public string RomName;
|
||||
#endif
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Application.targetFrameRate = 60;
|
||||
StartGame("tstd2.nes");
|
||||
VideoProvider.NesEmu = this;
|
||||
AudioProvider.NesEmu = this;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
StartGame(RomName);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void StartGame(string romName)
|
||||
@ -29,33 +38,32 @@ namespace AxibugEmuOnline.Client
|
||||
|
||||
try
|
||||
{
|
||||
m_nesIns = new NES(romName);
|
||||
NesCore = new NES(romName);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
m_nesIns = null;
|
||||
NesCore = null;
|
||||
Debug.LogError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopGame()
|
||||
{
|
||||
m_nesIns?.Dispose();
|
||||
m_nesIns = null;
|
||||
NesCore?.Dispose();
|
||||
NesCore = null;
|
||||
}
|
||||
|
||||
|
||||
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 lineColorMode = m_nesIns.ppu.GetLineColorMode();
|
||||
var screenBuffer = NesCore.ppu.GetScreenPtr();
|
||||
var lineColorMode = NesCore.ppu.GetLineColorMode();
|
||||
VideoProvider.SetDrawData(screenBuffer, lineColorMode, 256, 240);
|
||||
|
||||
AudioProvider.ProcessSound(m_nesIns);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ GameObject:
|
||||
m_Component:
|
||||
- component: {fileID: 4785916497946256520}
|
||||
- component: {fileID: 9003897287163669553}
|
||||
- component: {fileID: 7558824780418593440}
|
||||
- component: {fileID: 8726979175317618791}
|
||||
m_Layer: 0
|
||||
m_Name: AudioProvider
|
||||
m_TagString: Untagged
|
||||
@ -30,7 +30,7 @@ Transform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4232056521131536011}
|
||||
m_RootOrder: 1
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &9003897287163669553
|
||||
MonoBehaviour:
|
||||
@ -44,8 +44,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: a6a09b6a4cf4c2d4f994a13fd7e89d6f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
m_as: {fileID: 7558824780418593440}
|
||||
--- !u!82 &7558824780418593440
|
||||
m_as: {fileID: 8726979175317618791}
|
||||
--- !u!82 &8726979175317618791
|
||||
AudioSource:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
@ -141,6 +141,78 @@ AudioSource:
|
||||
m_PreInfinity: 2
|
||||
m_PostInfinity: 2
|
||||
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
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -168,8 +240,7 @@ 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:
|
||||
- {fileID: 4232056520494431727}
|
||||
m_Children: []
|
||||
m_Father: {fileID: 4232056521131536011}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@ -217,8 +288,9 @@ RectTransform:
|
||||
m_LocalScale: {x: 0, y: 0, z: 0}
|
||||
m_Children:
|
||||
- {fileID: 4232056521759880275}
|
||||
m_Father: {fileID: 4232056520112715745}
|
||||
m_RootOrder: 0
|
||||
- {fileID: 1038087993597378172}
|
||||
m_Father: {fileID: 4232056521131536011}
|
||||
m_RootOrder: 3
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 0, y: 0}
|
||||
@ -315,7 +387,9 @@ Transform:
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_Children:
|
||||
- {fileID: 4232056520112715745}
|
||||
- {fileID: 393435831810118449}
|
||||
- {fileID: 4785916497946256520}
|
||||
- {fileID: 4232056520494431727}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@ -333,6 +407,7 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier:
|
||||
VideoProvider: {fileID: 4232056520112715744}
|
||||
AudioProvider: {fileID: 9003897287163669553}
|
||||
RomName:
|
||||
--- !u!1 &4232056521759880276
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@ -405,3 +480,47 @@ MonoBehaviour:
|
||||
y: 0
|
||||
width: 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}
|
||||
|
@ -7,10 +7,18 @@ using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using VirtualNes.Core;
|
||||
|
||||
namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public static class PaletteDefine
|
||||
{
|
||||
public struct RGBQUAD
|
||||
{
|
||||
public byte rgbBlue;
|
||||
public byte rgbGreen;
|
||||
public byte rgbRed;
|
||||
public byte rgbReserved;
|
||||
}
|
||||
|
||||
public class PALBUF
|
||||
{
|
||||
public byte r;
|
||||
@ -108,6 +116,33 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
|
||||
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 ピクセルフォーマットに変換したパレット
|
||||
// Color
|
||||
public static uint[][] m_cnPalette = new uint[8][]
|
||||
@ -161,6 +196,18 @@ namespace AxibugEmuOnline.Client.Assets.Script.NesEmulator
|
||||
};
|
||||
#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()
|
||||
{
|
||||
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);
|
||||
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_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 (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_msPalette[j][i] = ((Rs >> (8 - Rbit)) << Rsft) | ((Gs >> (8 - Gbit)) << Gsft) | ((Bs >> (8 - Bbit)) << Bsft);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7a50c189f5be5b4ea54de444f8488a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,14 +1,14 @@
|
||||
using AxibugEmuOnline.Client.Assets.Script.NesEmulator;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using VirtualNes.Core;
|
||||
|
||||
namespace AxibugEmuOnline.Client
|
||||
{
|
||||
public class VideoProvider : MonoBehaviour
|
||||
{
|
||||
public NesEmulator NesEmu;
|
||||
|
||||
public RawImage Image;
|
||||
|
||||
private UInt32[] wrapTexBuffer;
|
||||
|
@ -60,7 +60,16 @@ namespace VirtualNes.Core
|
||||
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];
|
||||
static double cutofftemp = (2.0 * 3.141592653579 * 40.0);
|
||||
|
@ -1,36 +1,219 @@
|
||||
using System;
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
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 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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
//todo : 实现
|
||||
}
|
||||
|
||||
public override int Process(int channel)
|
||||
{
|
||||
//todo : 实现
|
||||
return 0;
|
||||
switch (addr)
|
||||
{
|
||||
// MMC5 CH0 rectangle
|
||||
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)
|
||||
{
|
||||
//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)
|
||||
@ -46,6 +229,118 @@ namespace VirtualNes.Core
|
||||
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
|
||||
{
|
||||
// For sync
|
||||
@ -54,6 +349,38 @@ namespace VirtualNes.Core
|
||||
public byte holdnote;
|
||||
public byte[] dummy = new byte[2];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,95 @@
|
||||
namespace VirtualNes.Core
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
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)
|
||||
{
|
||||
//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)
|
||||
{
|
||||
//todo : 实现
|
||||
Emu2413API.OPLL_setClock((UInt32)(fClock * 2.0f), (UInt32)nRate);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,13 @@ namespace VirtualNes.Core
|
||||
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]
|
||||
{
|
||||
get
|
||||
|
@ -80,10 +80,36 @@ namespace VirtualNes.Core
|
||||
public UInt32 noiseB_idx;
|
||||
public UInt32 noiseA_dphase;
|
||||
public UInt32 noiseB_dphase;
|
||||
|
||||
public int masterVolume; /* 0min -- 64 -- 127 max (Liner) */
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
makePmTable();
|
||||
@ -97,7 +123,7 @@ namespace VirtualNes.Core
|
||||
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();
|
||||
}
|
||||
@ -138,6 +164,39 @@ namespace VirtualNes.Core
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -78,6 +78,13 @@ namespace VirtualNes
|
||||
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)
|
||||
{
|
||||
bank %= PROM_8K_SIZE;
|
||||
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c57bc13f96a8d064a885b65c6aebc351
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -8,404 +8,409 @@ using BYTE = System.Byte;
|
||||
using Codice.CM.Client.Differences;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class Mapper001 : Mapper
|
||||
{
|
||||
|
||||
uint last_addr;
|
||||
|
||||
BYTE patch;
|
||||
BYTE wram_patch;
|
||||
BYTE wram_bank;
|
||||
BYTE wram_count;
|
||||
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE shift, regbuf;
|
||||
|
||||
public Mapper001(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[1] = reg[2] = reg[3] = 0;
|
||||
shift = regbuf = 0;
|
||||
|
||||
patch = 0;
|
||||
wram_patch = 0;
|
||||
|
||||
if (PROM_16K_SIZE < 32)
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 16 - 1);
|
||||
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
// SetVROM_8K_Bank( 0 );
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xb8e16bd0)
|
||||
{ // Snow Bros.(J)
|
||||
patch = 2;
|
||||
}
|
||||
// if( crc == 0x9b565541 ) { // Triathron, The(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
if (crc == 0xc96c6f04)
|
||||
{ // Venus Senki(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
// if( crc == 0x5e3f7004 ) { // Softball Tengoku(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x4d2edf70)
|
||||
{ // Night Rider(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xcd2a73f0)
|
||||
{ // Pirates!(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
patch = 2;
|
||||
}
|
||||
|
||||
// if( crc == 0x09efe54b ) { // Majaventure - Mahjong Senki(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
|
||||
if (crc == 0x11469ce3)
|
||||
{ // Viva! Las Vegas(J)
|
||||
}
|
||||
if (crc == 0xd878ebf5)
|
||||
{ // Ninja Ryukenden(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
// if( crc == 0x7bd7b849 ) { // Nekketsu Koukou - Dodgeball Bu(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x466efdc2)
|
||||
{ // Final Fantasy(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
}
|
||||
if (crc == 0xc9556b36)
|
||||
{ // Final Fantasy I&II(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 2;
|
||||
}
|
||||
|
||||
if (crc == 0x717e1169)
|
||||
{ // Cosmic Wars(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xC05D2034)
|
||||
{ // Snake's Revenge(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xb8747abf // Best Play - Pro Yakyuu Special(J)
|
||||
|| crc == 0x29449ba9 // Nobunaga no Yabou - Zenkoku Ban(J)
|
||||
|| crc == 0x2b11e0b0 // Nobunaga no Yabou - Zenkoku Ban(J)(alt)
|
||||
|| crc == 0x4642dda6 // Nobunaga's Ambition(U)
|
||||
|| crc == 0xfb69743a // Aoki Ookami to Shiroki Mejika - Genghis Khan(J)
|
||||
|| crc == 0x2225c20f // Genghis Khan(U)
|
||||
|| crc == 0xabbf7217 // Sangokushi(J)
|
||||
)
|
||||
{
|
||||
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 1;
|
||||
wram_bank = 0;
|
||||
wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper001::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
// DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF );
|
||||
|
||||
if (wram_patch == 1 && addr == 0xBFFF)
|
||||
{
|
||||
wram_count++;
|
||||
wram_bank += (byte)(data & 0x01);
|
||||
if (wram_count == 5)
|
||||
{
|
||||
if (wram_bank != 0)
|
||||
{
|
||||
SetPROM_Bank(3, &WRAM[0x2000], BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_Bank(3, &WRAM[0x0000], BANKTYPE_RAM);
|
||||
}
|
||||
wram_bank = wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
if ((addr & 0x6000) != (last_addr & 0x6000))
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
}
|
||||
last_addr = addr;
|
||||
}
|
||||
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
// reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data & 0x01) != 0) regbuf |= (byte)(1 << shift);
|
||||
if (++shift < 5)
|
||||
return;
|
||||
addr = (ushort)((addr & 0x7FFF) >> 13);
|
||||
reg[addr] = regbuf;
|
||||
|
||||
// DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF );
|
||||
|
||||
regbuf = 0;
|
||||
shift = 0;
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
// For Normal Cartridge
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!((reg[0] & 0x08) != 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank(reg[3] >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, reg[3]);
|
||||
SetPROM_16K_Bank(6, PROM_16K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, reg[3]);
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
INT PROM_BASE = 0;
|
||||
if (PROM_16K_SIZE >= 32)
|
||||
{
|
||||
PROM_BASE = reg[1] & 0x10;
|
||||
}
|
||||
|
||||
// For FinalFantasy I&II
|
||||
if (wram_patch == 2)
|
||||
{
|
||||
if (((reg[1] & 0x18) == 0))
|
||||
{
|
||||
SetPROM_Bank(3, &WRAM[0x0000], BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetPROM_Bank(3, &WRAM[0x2000], BANKTYPE_RAM);
|
||||
}
|
||||
}
|
||||
|
||||
// Register #0
|
||||
if (addr == 0)
|
||||
{
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
// Register #3
|
||||
if (((reg[0] & 0x08) == 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper001::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
p[4] = shift;
|
||||
p[5] = regbuf;
|
||||
|
||||
p[6] = wram_bank;
|
||||
p[7] = wram_count;
|
||||
}
|
||||
|
||||
//void Mapper001::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
shift = p[4];
|
||||
regbuf = p[5];
|
||||
|
||||
wram_bank = p[6];
|
||||
wram_count = p[7];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
{
|
||||
public class Mapper001 : Mapper
|
||||
{
|
||||
|
||||
uint last_addr;
|
||||
|
||||
BYTE patch;
|
||||
BYTE wram_patch;
|
||||
BYTE wram_bank;
|
||||
BYTE wram_count;
|
||||
|
||||
BYTE[] reg = new byte[4];
|
||||
BYTE shift, regbuf;
|
||||
|
||||
public Mapper001(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[1] = reg[2] = reg[3] = 0;
|
||||
shift = regbuf = 0;
|
||||
|
||||
patch = 0;
|
||||
wram_patch = 0;
|
||||
|
||||
if (PROM_16K_SIZE < 32)
|
||||
{
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 16 - 1);
|
||||
|
||||
patch = 1;
|
||||
}
|
||||
|
||||
if (VROM_8K_SIZE != 0)
|
||||
{
|
||||
// SetVROM_8K_Bank( 0 );
|
||||
}
|
||||
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0xb8e16bd0)
|
||||
{ // Snow Bros.(J)
|
||||
patch = 2;
|
||||
}
|
||||
// if( crc == 0x9b565541 ) { // Triathron, The(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
if (crc == 0xc96c6f04)
|
||||
{ // Venus Senki(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
// if( crc == 0x5e3f7004 ) { // Softball Tengoku(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x4d2edf70)
|
||||
{ // Night Rider(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
}
|
||||
if (crc == 0xcd2a73f0)
|
||||
{ // Pirates!(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
patch = 2;
|
||||
}
|
||||
|
||||
// if( crc == 0x09efe54b ) { // Majaventure - Mahjong Senki(J)
|
||||
// nes.SetFrameIRQmode( FALSE );
|
||||
// }
|
||||
|
||||
if (crc == 0x11469ce3)
|
||||
{ // Viva! Las Vegas(J)
|
||||
}
|
||||
if (crc == 0xd878ebf5)
|
||||
{ // Ninja Ryukenden(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.POST_ALL_RENDER);
|
||||
}
|
||||
|
||||
// if( crc == 0x7bd7b849 ) { // Nekketsu Koukou - Dodgeball Bu(J)
|
||||
// }
|
||||
|
||||
if (crc == 0x466efdc2)
|
||||
{ // Final Fantasy(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
}
|
||||
if (crc == 0xc9556b36)
|
||||
{ // Final Fantasy I&II(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtMonoMode(true);
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 2;
|
||||
}
|
||||
|
||||
if (crc == 0x717e1169)
|
||||
{ // Cosmic Wars(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xC05D2034)
|
||||
{ // Snake's Revenge(U)
|
||||
nes.SetRenderMethod(EnumRenderMethod.PRE_ALL_RENDER);
|
||||
}
|
||||
|
||||
if (crc == 0xb8747abf // Best Play - Pro Yakyuu Special(J)
|
||||
|| crc == 0x29449ba9 // Nobunaga no Yabou - Zenkoku Ban(J)
|
||||
|| crc == 0x2b11e0b0 // Nobunaga no Yabou - Zenkoku Ban(J)(alt)
|
||||
|| crc == 0x4642dda6 // Nobunaga's Ambition(U)
|
||||
|| crc == 0xfb69743a // Aoki Ookami to Shiroki Mejika - Genghis Khan(J)
|
||||
|| crc == 0x2225c20f // Genghis Khan(U)
|
||||
|| crc == 0xabbf7217 // Sangokushi(J)
|
||||
)
|
||||
{
|
||||
|
||||
nes.SetSAVERAM_SIZE(16 * 1024);
|
||||
wram_patch = 1;
|
||||
wram_bank = 0;
|
||||
wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private ByteArrayRef _PROM_BANK = new ByteArrayRef();
|
||||
//void Mapper001::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
// DEBUGOUT( "MMC1 %04X=%02X\n", addr&0xFFFF,data&0xFF );
|
||||
|
||||
if (wram_patch == 1 && addr == 0xBFFF)
|
||||
{
|
||||
wram_count++;
|
||||
wram_bank += (byte)(data & 0x01);
|
||||
if (wram_count == 5)
|
||||
{
|
||||
if (wram_bank != 0)
|
||||
{
|
||||
_PROM_BANK.SetArray(WRAM, 0x2000);
|
||||
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
_PROM_BANK.SetArray(WRAM, 0x0000);
|
||||
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
|
||||
}
|
||||
wram_bank = wram_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
if ((addr & 0x6000) != (last_addr & 0x6000))
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
}
|
||||
last_addr = addr;
|
||||
}
|
||||
|
||||
if ((data & 0x80) != 0)
|
||||
{
|
||||
shift = regbuf = 0;
|
||||
// reg[0] = 0x0C; // D3=1,D2=1
|
||||
reg[0] |= 0x0C; // D3=1,D2=1 残りはリセットされない
|
||||
return;
|
||||
}
|
||||
|
||||
if ((data & 0x01) != 0) regbuf |= (byte)(1 << shift);
|
||||
if (++shift < 5)
|
||||
return;
|
||||
addr = (ushort)((addr & 0x7FFF) >> 13);
|
||||
reg[addr] = regbuf;
|
||||
|
||||
// DEBUGOUT( "MMC1 %d=%02X\n", addr&0xFFFF,regbuf&0xFF );
|
||||
|
||||
regbuf = 0;
|
||||
shift = 0;
|
||||
|
||||
if (patch != 1)
|
||||
{
|
||||
// For Normal Cartridge
|
||||
switch (addr)
|
||||
{
|
||||
case 0:
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if (!((reg[0] & 0x08) != 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank(reg[3] >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, reg[3]);
|
||||
SetPROM_16K_Bank(6, PROM_16K_SIZE - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, reg[3]);
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For 512K/1M byte Cartridge
|
||||
INT PROM_BASE = 0;
|
||||
if (PROM_16K_SIZE >= 32)
|
||||
{
|
||||
PROM_BASE = reg[1] & 0x10;
|
||||
}
|
||||
|
||||
// For FinalFantasy I&II
|
||||
if (wram_patch == 2)
|
||||
{
|
||||
if (((reg[1] & 0x18) == 0))
|
||||
{
|
||||
_PROM_BANK.SetArray(WRAM, 0x0000);
|
||||
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
|
||||
}
|
||||
else
|
||||
{
|
||||
_PROM_BANK.SetArray(WRAM, 0x2000);
|
||||
SetPROM_Bank(3, _PROM_BANK, BANKTYPE_RAM);
|
||||
}
|
||||
}
|
||||
|
||||
// Register #0
|
||||
if (addr == 0)
|
||||
{
|
||||
if ((reg[0] & 0x02) != 0)
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x01) != 0) SetVRAM_Mirror(VRAM_MIRROR4H);
|
||||
else SetVRAM_Mirror(VRAM_MIRROR4L);
|
||||
}
|
||||
}
|
||||
// Register #1
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank lower($0000-$0FFF)
|
||||
SetVROM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// CHR 8K bank($0000-$1FFF)
|
||||
SetVROM_8K_Bank(reg[1] >> 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(0, reg[1]);
|
||||
}
|
||||
}
|
||||
// Register #2
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
// CHR 4K bank higher($1000-$1FFF)
|
||||
SetVROM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// For Romancia
|
||||
if ((reg[0] & 0x10) != 0)
|
||||
{
|
||||
SetCRAM_4K_Bank(4, reg[2]);
|
||||
}
|
||||
}
|
||||
// Register #3
|
||||
if (((reg[0] & 0x08) == 0))
|
||||
{
|
||||
// PRG 32K bank ($8000-$FFFF)
|
||||
SetPROM_32K_Bank((reg[3] & (0xF + PROM_BASE)) >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x04) != 0)
|
||||
{
|
||||
// PRG 16K bank ($8000-$BFFF)
|
||||
SetPROM_16K_Bank(4, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(6, PROM_BASE + 16 - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// PRG 16K bank ($C000-$FFFF)
|
||||
SetPROM_16K_Bank(6, PROM_BASE + (reg[3] & 0x0F));
|
||||
if (PROM_16K_SIZE >= 32) SetPROM_16K_Bank(4, PROM_BASE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//void Mapper001::SaveState(LPBYTE p)
|
||||
public override void SaveState(byte[] p)
|
||||
{
|
||||
p[0] = reg[0];
|
||||
p[1] = reg[1];
|
||||
p[2] = reg[2];
|
||||
p[3] = reg[3];
|
||||
p[4] = shift;
|
||||
p[5] = regbuf;
|
||||
|
||||
p[6] = wram_bank;
|
||||
p[7] = wram_count;
|
||||
}
|
||||
|
||||
//void Mapper001::LoadState(LPBYTE p)
|
||||
public override void LoadState(byte[] p)
|
||||
{
|
||||
reg[0] = p[0];
|
||||
reg[1] = p[1];
|
||||
reg[2] = p[2];
|
||||
reg[3] = p[3];
|
||||
shift = p[4];
|
||||
regbuf = p[5];
|
||||
|
||||
wram_bank = p[6];
|
||||
wram_count = p[7];
|
||||
}
|
||||
|
||||
public override bool IsStateSave()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c921e7f594a988845856d30f6a925157
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb072eff593853c41974a6d3b8bc1f93
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -12,33 +12,33 @@ namespace VirtualNes.Core
|
||||
public class Mapper003 : Mapper
|
||||
{
|
||||
|
||||
public Mapper003(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
switch (PROM_16K_SIZE)
|
||||
{
|
||||
case 1: // 16K only
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 0);
|
||||
break;
|
||||
case 2: // 32K
|
||||
SetPROM_32K_Bank(0);
|
||||
break;
|
||||
}
|
||||
// nes.SetRenderMethod( NES::TILE_RENDER );
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x2b72fe7e)
|
||||
{ // Ganso Saiyuuki - Super Monkey Dai Bouken(J)
|
||||
nes.SetRenderMethod( EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtNameTableMode(true);
|
||||
}
|
||||
|
||||
// if( crc == 0xE44D95B5 ) { // ひみつw
|
||||
// }
|
||||
}
|
||||
|
||||
public Mapper003(NES parent) : base(parent) { }
|
||||
|
||||
public override void Reset()
|
||||
{
|
||||
switch (PROM_16K_SIZE)
|
||||
{
|
||||
case 1: // 16K only
|
||||
SetPROM_16K_Bank(4, 0);
|
||||
SetPROM_16K_Bank(6, 0);
|
||||
break;
|
||||
case 2: // 32K
|
||||
SetPROM_32K_Bank(0);
|
||||
break;
|
||||
}
|
||||
// nes.SetRenderMethod( NES::TILE_RENDER );
|
||||
uint crc = nes.rom.GetPROM_CRC();
|
||||
|
||||
if (crc == 0x2b72fe7e)
|
||||
{ // Ganso Saiyuuki - Super Monkey Dai Bouken(J)
|
||||
nes.SetRenderMethod(EnumRenderMethod.TILE_RENDER);
|
||||
nes.ppu.SetExtNameTableMode(true);
|
||||
}
|
||||
|
||||
// if( crc == 0xE44D95B5 ) { // ひみつw
|
||||
// }
|
||||
}
|
||||
|
||||
#if FALSE//0
|
||||
void Mapper003::WriteLow( WORD addr, BYTE data )
|
||||
{
|
||||
@ -55,12 +55,12 @@ void Mapper003::WriteLow( WORD addr, BYTE data )
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//void Mapper003::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetVROM_8K_Bank(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//void Mapper003::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
SetVROM_8K_Bank(data);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d63147469fd9c5540882a5a89799462b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 26977049b3ea7ad4fa3b5c35a325ae48
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0672fe1374d484f4ab1c824d0882bfdd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 063872d968d91234a9a22755e3db7aab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7e39136ad8af00f4c95499cab3a0aa45
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60942642eeee2e04fbe2fad967c2400d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0beeffcb64a5c124c9ef8d2b428f723e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d46cb68121988e498159e8a53b0cb90
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0d9dccfbd35bbf458c8f5d100e2f89b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 660200e3200da7d4eb9925d2b63afef5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9a00ec8072aa26648bcd7bef203e9a39
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -22,6 +22,7 @@ namespace VirtualNes.Core
|
||||
BYTE irq_latch;
|
||||
BYTE irq_request;
|
||||
int MMC4prg, MMC4chr;
|
||||
|
||||
public Mapper245(NES parent) : base(parent)
|
||||
{
|
||||
}
|
||||
@ -30,92 +31,92 @@ namespace VirtualNes.Core
|
||||
//void Mapper245::Reset()
|
||||
public override void Reset()
|
||||
{
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
we_sram = 0; // Disable
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_request = 0;
|
||||
|
||||
for (INT i = 0; i < 8; i++)
|
||||
{
|
||||
reg[i] = 0x00;
|
||||
}
|
||||
|
||||
prg0 = 0;
|
||||
prg1 = 1;
|
||||
|
||||
SetPROM_32K_Bank(0, 1, PROM_8K_SIZE - 2, PROM_8K_SIZE - 1);
|
||||
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(0);
|
||||
}
|
||||
|
||||
we_sram = 0; // Disable
|
||||
irq_enable = 0; // Disable
|
||||
irq_counter = 0;
|
||||
irq_latch = 0;
|
||||
irq_request = 0;
|
||||
|
||||
nes.SetIrqType(NES.IRQMETHOD.IRQ_CLOCK);
|
||||
}
|
||||
|
||||
//void Mapper245::Write(WORD addr, BYTE data)
|
||||
public override void Write(ushort addr, byte data)
|
||||
{
|
||||
switch (addr & 0xF7FF)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
switch (reg[0])
|
||||
{
|
||||
case 0x00:
|
||||
reg[3] = (byte)((data & 2) << 5);
|
||||
SetPROM_8K_Bank(6, 0x3E | reg[3]);
|
||||
SetPROM_8K_Bank(7, 0x3F | reg[3]);
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
break;
|
||||
}
|
||||
SetPROM_8K_Bank(4, prg0 | reg[3]);
|
||||
SetPROM_8K_Bank(5, prg1 | reg[3]);
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_counter = data;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
irq_latch = data;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
switch (addr & 0xF7FF)
|
||||
{
|
||||
case 0x8000:
|
||||
reg[0] = data;
|
||||
break;
|
||||
case 0x8001:
|
||||
reg[1] = data;
|
||||
switch (reg[0])
|
||||
{
|
||||
case 0x00:
|
||||
reg[3] = (byte)((data & 2) << 5);
|
||||
SetPROM_8K_Bank(6, 0x3E | reg[3]);
|
||||
SetPROM_8K_Bank(7, 0x3F | reg[3]);
|
||||
break;
|
||||
case 0x06:
|
||||
prg0 = data;
|
||||
break;
|
||||
case 0x07:
|
||||
prg1 = data;
|
||||
break;
|
||||
}
|
||||
SetPROM_8K_Bank(4, prg0 | reg[3]);
|
||||
SetPROM_8K_Bank(5, prg1 | reg[3]);
|
||||
break;
|
||||
case 0xA000:
|
||||
reg[2] = data;
|
||||
if (!nes.rom.Is4SCREEN())
|
||||
{
|
||||
if ((data & 0x01) != 0) SetVRAM_Mirror(VRAM_HMIRROR);
|
||||
else SetVRAM_Mirror(VRAM_VMIRROR);
|
||||
}
|
||||
break;
|
||||
case 0xA001:
|
||||
|
||||
break;
|
||||
case 0xC000:
|
||||
reg[4] = data;
|
||||
irq_counter = data;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xC001:
|
||||
reg[5] = data;
|
||||
irq_latch = data;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE000:
|
||||
reg[6] = data;
|
||||
irq_enable = 0;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
case 0xE001:
|
||||
reg[7] = data;
|
||||
irq_enable = 1;
|
||||
irq_request = 0;
|
||||
nes.cpu.ClrIRQ(IRQ_MAPPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,27 +131,27 @@ namespace VirtualNes.Core
|
||||
//void Mapper245::HSync(INT scanline)
|
||||
public override void HSync(int scanline)
|
||||
{
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0 && irq_request == 0)
|
||||
{
|
||||
if (scanline == 0)
|
||||
{
|
||||
if (irq_counter != 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
if ((irq_counter--) == 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((scanline >= 0 && scanline <= 239))
|
||||
{
|
||||
if (nes.ppu.IsDispON())
|
||||
{
|
||||
if (irq_enable != 0 && irq_request == 0)
|
||||
{
|
||||
if (scanline == 0)
|
||||
{
|
||||
if (irq_counter != 0)
|
||||
{
|
||||
irq_counter--;
|
||||
}
|
||||
}
|
||||
if ((irq_counter--) == 0)
|
||||
{
|
||||
irq_request = 0xFF;
|
||||
irq_counter = irq_latch;
|
||||
nes.cpu.SetIRQ(IRQ_MAPPER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if( irq_request && (nes.GetIrqType() == NES::IRQ_HSYNC) ) {
|
||||
// nes.cpu.IRQ_NotPending();
|
||||
@ -164,43 +165,43 @@ namespace VirtualNes.Core
|
||||
|
||||
void SetBank_PPU()
|
||||
{
|
||||
if ((VROM_1K_SIZE) != 0)
|
||||
{
|
||||
if (((reg[0] & 0x80)! + 0) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(chr4, chr5, chr6, chr7,
|
||||
chr23 + 1, chr23, chr01 + 1, chr01);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1,
|
||||
chr4, chr5, chr6, chr7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(0, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(1, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(2, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(3, chr7 & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(4, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(5, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(6, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(7, chr7 & 0x07);
|
||||
}
|
||||
if (VROM_1K_SIZE != 0)
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetVROM_8K_Bank(chr4, chr5, chr6, chr7,
|
||||
chr23 + 1, chr23, chr01 + 1, chr01);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetVROM_8K_Bank(chr01, chr01 + 1, chr23, chr23 + 1,
|
||||
chr4, chr5, chr6, chr7);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((reg[0] & 0x80) != 0)
|
||||
{
|
||||
SetCRAM_1K_Bank(4, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(5, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(6, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(7, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(0, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(1, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(2, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(3, chr7 & 0x07);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCRAM_1K_Bank(0, (chr01 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(1, (chr01 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(2, (chr23 + 0) & 0x07);
|
||||
SetCRAM_1K_Bank(3, (chr23 + 1) & 0x07);
|
||||
SetCRAM_1K_Bank(4, chr4 & 0x07);
|
||||
SetCRAM_1K_Bank(5, chr5 & 0x07);
|
||||
SetCRAM_1K_Bank(6, chr6 & 0x07);
|
||||
SetCRAM_1K_Bank(7, chr7 & 0x07);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Codice.CM.Client.Differences;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
@ -25,6 +26,19 @@ namespace VirtualNes.Core
|
||||
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
return bZapperMode;
|
||||
|
@ -1147,6 +1147,16 @@ namespace VirtualNes.Core
|
||||
bChrLatch = bMode;
|
||||
}
|
||||
|
||||
internal void SetExtNameTableMode(bool bMode)
|
||||
{
|
||||
bExtNameTable = bMode;
|
||||
}
|
||||
|
||||
internal void SetExtMonoMode(bool bMode)
|
||||
{
|
||||
bExtMono = bMode;
|
||||
}
|
||||
|
||||
public struct Sprite
|
||||
{
|
||||
public byte y
|
||||
|
@ -246,6 +246,7 @@ namespace VirtualNes.Core
|
||||
|
||||
if (Supporter.TryGetMapperNo(this, out int mapperNo))
|
||||
{
|
||||
Debuger.Log($"ROMDB Set Mapper #{mapper:000} to #{mapperNo:000}");
|
||||
mapper = mapperNo;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 88eb13b75812fc040ad7eb146af2bb80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@ -1,6 +1,27 @@
|
||||
namespace VirtualNes.Core
|
||||
using Codice.CM.Client.Differences;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
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];
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace VirtualNes.Core
|
||||
using System;
|
||||
|
||||
namespace VirtualNes.Core
|
||||
{
|
||||
public class EmulatorConfig
|
||||
{
|
||||
|
@ -54,6 +54,11 @@ namespace VirtualNes.Core
|
||||
return s_support.TryGetMapperNo(rom, out mapperNo);
|
||||
}
|
||||
|
||||
public static ControllerState GetControllerState()
|
||||
{
|
||||
return s_support.GetControllerState();
|
||||
}
|
||||
|
||||
public static EmulatorConfig Config => s_support.Config;
|
||||
}
|
||||
|
||||
@ -70,5 +75,6 @@ namespace VirtualNes.Core
|
||||
void SaveFile(byte[] fileData, string directPath, string fileName);
|
||||
Stream OpenFile(string directPath, string fileName);
|
||||
bool TryGetMapperNo(ROM rom, out int mapperNo);
|
||||
ControllerState GetControllerState();
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ PlayerSettings:
|
||||
androidFullscreenMode: 1
|
||||
defaultIsNativeResolution: 1
|
||||
macRetinaSupport: 1
|
||||
runInBackground: 0
|
||||
runInBackground: 1
|
||||
captureSingleScreen: 0
|
||||
muteOtherAudioSources: 0
|
||||
Prepare IOS For Recording: 0
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user