dev_4VirtualNes #18
@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using VirtualNes.Core;
|
using VirtualNes.Core;
|
||||||
|
|
||||||
@ -45,5 +46,21 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
return File.Open($"{Application.streamingAssetsPath}/Disksys.rom", FileMode.Open, FileAccess.Read);
|
return File.Open($"{Application.streamingAssetsPath}/Disksys.rom", FileMode.Open, FileAccess.Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SaveSRAMToFile(byte[] sramContent, string romName)
|
||||||
|
{
|
||||||
|
string sramDirectoryPath = $"{Application.persistentDataPath}/sav";
|
||||||
|
Directory.CreateDirectory(sramDirectoryPath);
|
||||||
|
romName = Path.GetFileNameWithoutExtension(romName);
|
||||||
|
File.WriteAllBytes($"{sramDirectoryPath}/{romName}.sav", sramContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveDISKToFile(byte[] diskFileContent, string romName)
|
||||||
|
{
|
||||||
|
string diskFileDirectoryPath = $"{Application.persistentDataPath}/dsv";
|
||||||
|
Directory.CreateDirectory(diskFileDirectoryPath);
|
||||||
|
romName = Path.GetFileNameWithoutExtension(romName);
|
||||||
|
File.WriteAllBytes($"{diskFileDirectoryPath}/{romName}.dsv", diskFileContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ namespace AxibugEmuOnline.Client
|
|||||||
{
|
{
|
||||||
public class NesEmulator : MonoBehaviour
|
public class NesEmulator : MonoBehaviour
|
||||||
{
|
{
|
||||||
|
private NES m_nesIns;
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
StartGame("Kirby.nes");
|
StartGame("Kirby.nes");
|
||||||
@ -13,9 +15,23 @@ namespace AxibugEmuOnline.Client
|
|||||||
|
|
||||||
public void StartGame(string romName)
|
public void StartGame(string romName)
|
||||||
{
|
{
|
||||||
|
StopGame();
|
||||||
|
|
||||||
Supporter.Setup(new CoreSupporter());
|
Supporter.Setup(new CoreSupporter());
|
||||||
Debuger.Setup(new CoreDebuger());
|
Debuger.Setup(new CoreDebuger());
|
||||||
NES nes = new NES(romName);
|
m_nesIns = new NES(romName);
|
||||||
|
m_nesIns.Command(NESCOMMAND.NESCMD_HWRESET);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopGame()
|
||||||
|
{
|
||||||
|
m_nesIns?.Dispose();
|
||||||
|
m_nesIns = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
m_nesIns?.EmulateFrame(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
private NES nes;
|
private NES nes;
|
||||||
private byte exsound_select;
|
private byte exsound_select;
|
||||||
private APU_INTERNAL @internal;
|
private APU_INTERNAL @internal = new APU_INTERNAL();
|
||||||
private int last_data;
|
private int last_data;
|
||||||
private int last_diff;
|
private int last_diff;
|
||||||
protected short[] m_SoundBuffer = new short[256];
|
protected short[] m_SoundBuffer = new short[256];
|
||||||
@ -19,8 +19,6 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
public APU(NES parent)
|
public APU(NES parent)
|
||||||
{
|
{
|
||||||
@internal = new APU_INTERNAL();
|
|
||||||
|
|
||||||
exsound_select = 0;
|
exsound_select = 0;
|
||||||
|
|
||||||
nes = parent;
|
nes = parent;
|
||||||
@ -36,6 +34,15 @@ namespace VirtualNes.Core
|
|||||||
for (int i = 0; i < m_bMute.Length; i++)
|
for (int i = 0; i < m_bMute.Length; i++)
|
||||||
m_bMute[i] = true;
|
m_bMute[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SyncDPCM(int cycles)
|
||||||
|
{
|
||||||
|
@internal.Sync(cycles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct QUEUEDATA
|
public struct QUEUEDATA
|
||||||
|
@ -3,12 +3,39 @@ namespace VirtualNes.Core
|
|||||||
public class APU_INTERNAL : APU_INTERFACE
|
public class APU_INTERNAL : APU_INTERFACE
|
||||||
{
|
{
|
||||||
private NES nes;
|
private NES nes;
|
||||||
|
private int FrameCycle;
|
||||||
|
private byte FrameIRQoccur;
|
||||||
|
|
||||||
public void SetParent(NES parent)
|
public void SetParent(NES parent)
|
||||||
{
|
{
|
||||||
nes = parent;
|
nes = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override bool Sync(int cycles)
|
||||||
|
{
|
||||||
|
FrameCycle -= cycles * 2;
|
||||||
|
if (FrameCycle <= 0)
|
||||||
|
{
|
||||||
|
FrameCycle += 14915;
|
||||||
|
|
||||||
|
UpdateFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = FrameIRQoccur | (SyncUpdateDPCM(cycles) ? 1 : 0);
|
||||||
|
return result != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SyncUpdateDPCM(int cycles)
|
||||||
|
{
|
||||||
|
//TODO : ʵÏÖ
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFrame()
|
||||||
|
{
|
||||||
|
//TODO : ʵÏÖ
|
||||||
|
}
|
||||||
|
|
||||||
public override void Reset(float fClock, int nRate)
|
public override void Reset(float fClock, int nRate)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
throw new System.NotImplementedException();
|
||||||
|
@ -1,12 +1,221 @@
|
|||||||
namespace VirtualNes.Core
|
#undef DPCM_SYNCCLOCK
|
||||||
|
|
||||||
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class CPU
|
public class CPU
|
||||||
{
|
{
|
||||||
protected NES m_nes;
|
// 6502 status flags
|
||||||
|
public const byte C_FLAG = 0x01; // 1: Carry
|
||||||
|
public const byte Z_FLAG = 0x02; // 1: Zero
|
||||||
|
public const byte I_FLAG = 0x04; // 1: Irq disabled
|
||||||
|
public const byte D_FLAG = 0x08; // 1: Decimal mode flag (NES unused)
|
||||||
|
public const byte B_FLAG = 0x10; // 1: Break
|
||||||
|
public const byte R_FLAG = 0x20; // 1: Reserved (Always 1)
|
||||||
|
public const byte V_FLAG = 0x40; // 1: Overflow
|
||||||
|
public const byte N_FLAG = 0x80; // 1: Negative
|
||||||
|
|
||||||
|
// Interrupt
|
||||||
|
public const byte NMI_FLAG = 0x01;
|
||||||
|
public const byte IRQ_FLAG = 0x02;
|
||||||
|
|
||||||
|
public const byte IRQ_FRAMEIRQ = 0x04;
|
||||||
|
public const byte IRQ_DPCM = 0x08;
|
||||||
|
public const byte IRQ_MAPPER = 0x10;
|
||||||
|
public const byte IRQ_MAPPER2 = 0x20;
|
||||||
|
public const byte IRQ_TRIGGER = 0x40; // one shot(媽IRQ())
|
||||||
|
public const byte IRQ_TRIGGER2 = 0x80; // one shot(媽IRQ_NotPending())
|
||||||
|
|
||||||
|
public static readonly byte IRQ_MASK = unchecked((byte)(~(NMI_FLAG | IRQ_FLAG)));
|
||||||
|
|
||||||
|
// Vector
|
||||||
|
public const ushort NMI_VECTOR = 0xFFFA;
|
||||||
|
public const ushort RES_VECTOR = 0xFFFC;
|
||||||
|
public const ushort IRQ_VECTOR = 0xFFFE;
|
||||||
|
|
||||||
|
private NES nes;
|
||||||
|
private bool m_bClockProcess;
|
||||||
|
private int TOTAL_cycles;
|
||||||
|
private int DMA_cycles;
|
||||||
|
private Mapper mapper;
|
||||||
|
private APU apu;
|
||||||
|
private R6502 R;
|
||||||
|
private byte[] ZN_Table = new byte[256];
|
||||||
|
|
||||||
public CPU(NES parent)
|
public CPU(NES parent)
|
||||||
{
|
{
|
||||||
m_nes = parent;
|
nes = parent;
|
||||||
|
m_bClockProcess = false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() { }
|
||||||
|
|
||||||
|
internal long EXEC(int request_cycles)
|
||||||
|
{
|
||||||
|
byte opcode = 0;
|
||||||
|
int OLD_cycles = TOTAL_cycles;
|
||||||
|
int exec_cycles = 0;
|
||||||
|
byte nmi_request = 0, irq_request = 0;
|
||||||
|
|
||||||
|
ushort EA = 0;
|
||||||
|
ushort ET = 0;
|
||||||
|
ushort WT = 0;
|
||||||
|
byte DT = 0;
|
||||||
|
|
||||||
|
while (request_cycles > 0)
|
||||||
|
{
|
||||||
|
exec_cycles = 0;
|
||||||
|
if (DMA_cycles > 0)
|
||||||
|
{
|
||||||
|
if (request_cycles <= DMA_cycles)
|
||||||
|
{
|
||||||
|
DMA_cycles -= request_cycles;
|
||||||
|
TOTAL_cycles += request_cycles;
|
||||||
|
|
||||||
|
mapper.Clock(request_cycles);
|
||||||
|
#if DPCM_SYNCCLOCK
|
||||||
|
apu.SyncDPCM(request_cycles);
|
||||||
|
#endif
|
||||||
|
if (m_bClockProcess)
|
||||||
|
{
|
||||||
|
nes.Clock(request_cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto _execute_exit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
exec_cycles += DMA_cycles;
|
||||||
|
DMA_cycles = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nmi_request = irq_request = 0;
|
||||||
|
opcode = OP6502(R.PC++);
|
||||||
|
|
||||||
|
if (R.Int_Pending != 0)
|
||||||
|
{
|
||||||
|
if ((R.Int_Pending & NMI_FLAG) != 0)
|
||||||
|
{
|
||||||
|
nmi_request = 0xFF;
|
||||||
|
byte temp = unchecked((byte)(~NMI_FLAG));
|
||||||
|
R.Int_Pending &= temp;
|
||||||
|
}
|
||||||
|
else if ((R.Int_Pending & IRQ_MASK) != 0)
|
||||||
|
{
|
||||||
|
byte temp = unchecked((byte)(~IRQ_TRIGGER2));
|
||||||
|
R.Int_Pending &= temp;
|
||||||
|
if (
|
||||||
|
((R.P & I_FLAG) == 0)
|
||||||
|
&&
|
||||||
|
(opcode != 0x40)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
irq_request = 0xFF;
|
||||||
|
temp = unchecked((byte)(~IRQ_TRIGGER));
|
||||||
|
R.Int_Pending &= temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case 0x69:
|
||||||
|
MR_IM(ref DT, ref R); ADC(ref WT, ref DT, ref R);
|
||||||
|
ADD_CYCLE(2, ref exec_cycles);
|
||||||
|
break;
|
||||||
|
case 0x65:
|
||||||
|
MR_ZP(ref EA, ref DT, ref R); ADC(ref WT, ref DT, ref R);
|
||||||
|
ADD_CYCLE(3, ref exec_cycles);
|
||||||
|
break;
|
||||||
|
case 0x75:
|
||||||
|
MR_ZX(ref DT, ref EA, ref R); ADC(ref WT, ref DT, ref R);
|
||||||
|
ADD_CYCLE(4, ref exec_cycles);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_execute_exit:
|
||||||
|
#if !DPCM_SYNCCLOCK
|
||||||
|
apu.SyncDPCM(TOTAL_cycles - OLD_cycles);
|
||||||
|
#endif
|
||||||
|
return TOTAL_cycles - OLD_cycles;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
internal void SetClockProcess(bool bEnable)
|
||||||
|
{
|
||||||
|
m_bClockProcess = bEnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte OP6502(ushort addr)
|
||||||
|
{
|
||||||
|
return MMU.CPU_MEM_BANK[addr >> 13][addr & 0x1FFF];
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void MR_IM(ref byte DT, ref R6502 R)
|
||||||
|
{
|
||||||
|
DT = OP6502(R.PC++);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void MR_ZP(ref ushort EA, ref byte DT, ref R6502 R)
|
||||||
|
{
|
||||||
|
EA = OP6502(R.PC++);
|
||||||
|
DT = ZPRD(ref EA);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private byte ZPRD(ref ushort A)
|
||||||
|
{
|
||||||
|
return MMU.RAM[A];
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ADC(ref ushort WT, ref byte DT, ref R6502 R)
|
||||||
|
{
|
||||||
|
WT = (ushort)(R.A + DT + (R.P & C_FLAG));
|
||||||
|
TST_FLAG(WT > 0xFF, C_FLAG, ref R);
|
||||||
|
var temp = ((~(R.A ^ DT)) & (R.A ^ WT) & 0x80);
|
||||||
|
TST_FLAG(temp != 0, V_FLAG, ref R);
|
||||||
|
R.A = (byte)WT;
|
||||||
|
SET_ZN_FLAG(R.A, ref R);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void TST_FLAG(bool F, byte V, ref R6502 R)
|
||||||
|
{
|
||||||
|
byte temp = (byte)~V;
|
||||||
|
R.P &= temp;
|
||||||
|
|
||||||
|
if (F) R.P |= V;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void SET_ZN_FLAG(byte A, ref R6502 R)
|
||||||
|
{
|
||||||
|
byte temp = unchecked((byte)(~(Z_FLAG | N_FLAG)));
|
||||||
|
R.P &= temp;
|
||||||
|
R.P |= ZN_Table[A];
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void ADD_CYCLE(int V, ref int exec_cycles)
|
||||||
|
{
|
||||||
|
exec_cycles += V;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private void MR_ZX(ref byte DT, ref ushort EA, ref R6502 R)
|
||||||
|
{
|
||||||
|
DT = OP6502(R.PC++);
|
||||||
|
EA = (ushort)(DT + R.X);
|
||||||
|
DT = ZPRD(ref EA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,13 +253,13 @@
|
|||||||
|
|
||||||
public struct R6502
|
public struct R6502
|
||||||
{
|
{
|
||||||
ushort PC;
|
public ushort PC;
|
||||||
byte A;
|
public byte A;
|
||||||
byte P;
|
public byte P;
|
||||||
byte X;
|
public byte X;
|
||||||
byte Y;
|
public byte Y;
|
||||||
byte S;
|
public byte S;
|
||||||
|
|
||||||
byte Int_Pending;
|
public byte Int_Pending;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
// 昤夋曽幃
|
||||||
|
public enum EnumRenderMethod
|
||||||
|
{
|
||||||
|
POST_ALL_RENDER = 0, // 僗僉儍儞儔僀儞暘偺柦椷幚峴屻丆儗儞僟儕儞僌
|
||||||
|
PRE_ALL_RENDER = 1, // 儗儞僟儕儞僌偺幚峴屻丆僗僉儍儞儔僀儞暘偺柦椷幚峴
|
||||||
|
POST_RENDER = 2, // 昞帵婜娫暘偺柦椷幚峴屻丆儗儞僟儕儞僌
|
||||||
|
PRE_RENDER = 3, // 儗儞僟儕儞僌幚峴屻丆昞帵婜娫暘偺柦椷幚峴
|
||||||
|
TILE_RENDER = 4 // 僞僀儖儀乕僗儗儞僟儕儞僌
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9c379fb6535bd23449474dee5018652c
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public enum NESCOMMAND
|
||||||
|
{
|
||||||
|
NESCMD_NONE = 0,
|
||||||
|
NESCMD_HWRESET,
|
||||||
|
NESCMD_SWRESET,
|
||||||
|
NESCMD_EXCONTROLLER, // Commandparam
|
||||||
|
NESCMD_DISK_THROTTLE_ON,
|
||||||
|
NESCMD_DISK_THROTTLE_OFF,
|
||||||
|
NESCMD_DISK_EJECT,
|
||||||
|
NESCMD_DISK_0A,
|
||||||
|
NESCMD_DISK_0B,
|
||||||
|
NESCMD_DISK_1A,
|
||||||
|
NESCMD_DISK_1B,
|
||||||
|
NESCMD_DISK_2A,
|
||||||
|
NESCMD_DISK_2B,
|
||||||
|
NESCMD_DISK_3A,
|
||||||
|
NESCMD_DISK_3B,
|
||||||
|
|
||||||
|
NESCMD_SOUND_MUTE, // CommandParam
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d7e8126382c9728429056ba33afc85eb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,58 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct NesConfig
|
||||||
|
{
|
||||||
|
public float BaseClock; // NTSC:21477270.0 PAL:21281364.0
|
||||||
|
public float CpuClock; // NTSC: 1789772.5 PAL: 1773447.0
|
||||||
|
|
||||||
|
public int TotalScanLines; // NTSC: 262 PAL: 312
|
||||||
|
|
||||||
|
public int ScanlineCycles; // NTSC:1364 PAL:1362
|
||||||
|
|
||||||
|
public int HDrawCycles; // NTSC:1024 PAL:1024
|
||||||
|
public int HBlankCycles; // NTSC: 340 PAL: 338
|
||||||
|
public int ScanlineEndCycles; // NTSC: 4 PAL: 2
|
||||||
|
|
||||||
|
public int FrameCycles; // NTSC:29829.52 PAL:35468.94
|
||||||
|
public int FrameIrqCycles; // NTSC:29829.52 PAL:35468.94
|
||||||
|
|
||||||
|
public int FrameRate; // NTSC:60(59.94) PAL:50
|
||||||
|
public float FramePeriod; // NTSC:16.683 PAL:20.0
|
||||||
|
|
||||||
|
public static NesConfig GetNTSC()
|
||||||
|
{
|
||||||
|
return new NesConfig
|
||||||
|
{
|
||||||
|
BaseClock = 21477270.0f,
|
||||||
|
CpuClock = 1789772.5f,
|
||||||
|
TotalScanLines = 262,
|
||||||
|
ScanlineCycles = 1364,
|
||||||
|
HDrawCycles = 1024,
|
||||||
|
HBlankCycles = 340,
|
||||||
|
ScanlineEndCycles = 4,
|
||||||
|
FrameCycles = 1364 * 262,
|
||||||
|
FrameIrqCycles = 29830,
|
||||||
|
FrameRate = 60,
|
||||||
|
FramePeriod = 1000.0f / 60.0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NesConfig GetPAL()
|
||||||
|
{
|
||||||
|
return new NesConfig
|
||||||
|
{
|
||||||
|
BaseClock = 26601714.0f,
|
||||||
|
CpuClock = 1662607.125f,
|
||||||
|
TotalScanLines = 312,
|
||||||
|
ScanlineCycles = 1278,
|
||||||
|
HDrawCycles = 960,
|
||||||
|
HBlankCycles = 318,
|
||||||
|
ScanlineEndCycles = 2,
|
||||||
|
FrameCycles = 1278 * 312,
|
||||||
|
FrameIrqCycles = 33252,
|
||||||
|
FrameRate = 50,
|
||||||
|
FramePeriod = 1000.0f / 50.0f
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4865f8871b37b0041b77060cf3c62664
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,59 @@
|
|||||||
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class DISKFILEHDR
|
||||||
|
{
|
||||||
|
public byte[] ID = new byte[12]; // "VirtuaNES DI"
|
||||||
|
public ushort BlockVersion; // 0x0200:0.30 0x0210:0.31
|
||||||
|
public ushort Reserved;
|
||||||
|
public ulong ProgID; // 僾儘僌儔儉ID
|
||||||
|
public ushort MakerID; // 儊乕僇乕ID
|
||||||
|
public ushort DiskNo; // 僨傿僗僋悢
|
||||||
|
public ulong DifferentSize; // 憡堘悢
|
||||||
|
|
||||||
|
|
||||||
|
public byte[] ToBytes()
|
||||||
|
{
|
||||||
|
byte[] res = new byte[36];
|
||||||
|
Array.Copy(ID, res, ID.Length);
|
||||||
|
var temp = BitConverter.GetBytes(BlockVersion);
|
||||||
|
res[12] = temp[0];
|
||||||
|
res[13] = temp[1];
|
||||||
|
temp = BitConverter.GetBytes(Reserved);
|
||||||
|
res[14] = temp[0];
|
||||||
|
res[15] = temp[1];
|
||||||
|
temp = BitConverter.GetBytes(ProgID);
|
||||||
|
res[16] = temp[0];
|
||||||
|
res[17] = temp[1];
|
||||||
|
res[18] = temp[2];
|
||||||
|
res[19] = temp[3];
|
||||||
|
res[20] = temp[4];
|
||||||
|
res[21] = temp[5];
|
||||||
|
res[22] = temp[6];
|
||||||
|
res[23] = temp[7];
|
||||||
|
temp = BitConverter.GetBytes(MakerID);
|
||||||
|
res[24] = temp[0];
|
||||||
|
res[25] = temp[1];
|
||||||
|
temp = BitConverter.GetBytes(DiskNo);
|
||||||
|
res[26] = temp[0];
|
||||||
|
res[27] = temp[1];
|
||||||
|
temp = BitConverter.GetBytes(ProgID);
|
||||||
|
res[28] = temp[0];
|
||||||
|
res[29] = temp[1];
|
||||||
|
res[30] = temp[2];
|
||||||
|
res[31] = temp[3];
|
||||||
|
res[32] = temp[4];
|
||||||
|
res[33] = temp[5];
|
||||||
|
res[34] = temp[6];
|
||||||
|
res[35] = temp[7];
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 447095b8c8ae4c74885562c127998e9e
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
18
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs
Normal file
18
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes
|
||||||
|
{
|
||||||
|
public static class MMU
|
||||||
|
{
|
||||||
|
// CPU 儊儌儕僶儞僋
|
||||||
|
public static byte[][] CPU_MEM_BANK = new byte[8][]; // 8K扨埵
|
||||||
|
|
||||||
|
// NES儊儌儕
|
||||||
|
public static byte[] RAM = new byte[8 * 1024]; // NES撪憻RAM
|
||||||
|
public static byte[] WARM = new byte[128 * 1024]; // 儚乕僋/僶僢僋傾僢僾RAM
|
||||||
|
}
|
||||||
|
}
|
11
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta
Normal file
11
AxibugEmuOnline.Client/Assets/VirtualNes.Core/MMU.cs.meta
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 863989820a4fb1d49a7c0c883c5a7078
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -6,7 +6,8 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class Mapper
|
public abstract class Mapper
|
||||||
{
|
{
|
||||||
|
internal virtual void Clock(int cycles) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
using Codice.CM.Client.Differences;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using VirtualNes.Core.Debug;
|
using VirtualNes.Core.Debug;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
@ -13,10 +15,9 @@ namespace VirtualNes.Core
|
|||||||
public ROM rom;
|
public ROM rom;
|
||||||
public PAD pad;
|
public PAD pad;
|
||||||
public Mapper mapper;
|
public Mapper mapper;
|
||||||
public NesConfig NesCfg;
|
public NesConfig nescfg;
|
||||||
|
|
||||||
protected List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
|
||||||
|
|
||||||
|
private List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
||||||
private bool m_bDiskThrottle;
|
private bool m_bDiskThrottle;
|
||||||
private int m_CommandRequest;
|
private int m_CommandRequest;
|
||||||
private int m_nSnapNo;
|
private int m_nSnapNo;
|
||||||
@ -30,12 +31,32 @@ namespace VirtualNes.Core
|
|||||||
private double m_TapeCycles;
|
private double m_TapeCycles;
|
||||||
private byte m_TapeIn;
|
private byte m_TapeIn;
|
||||||
private byte m_TapeOut;
|
private byte m_TapeOut;
|
||||||
|
|
||||||
|
// For Barcode
|
||||||
|
private bool m_bBarcode;
|
||||||
|
private byte m_BarcodeOut;
|
||||||
|
private byte m_BarcodePtr;
|
||||||
|
private int m_BarcodeCycles;
|
||||||
|
private byte[] m_BarcodeData = new byte[256];
|
||||||
|
|
||||||
|
// For Barcode
|
||||||
private bool m_bBarcode2;
|
private bool m_bBarcode2;
|
||||||
|
private int m_Barcode2seq;
|
||||||
|
private int m_Barcode2ptr;
|
||||||
|
private int m_Barcode2cnt;
|
||||||
|
private byte m_Barcode2bit;
|
||||||
|
private byte[] m_Barcode2data = new byte[32];
|
||||||
|
|
||||||
private int m_TurboFileBank;
|
private int m_TurboFileBank;
|
||||||
private int SAVERAM_SIZE;
|
private int SAVERAM_SIZE;
|
||||||
private int nIRQtype;
|
private int nIRQtype;
|
||||||
private bool bFrameIRQ;
|
private bool bFrameIRQ;
|
||||||
private bool bVideoMode;
|
private bool bVideoMode;
|
||||||
|
private int NES_scanline;
|
||||||
|
private EnumRenderMethod RenderMethod;
|
||||||
|
private bool bZapper;
|
||||||
|
private long base_cycles;
|
||||||
|
private long emul_cycles;
|
||||||
|
|
||||||
public NES(string fname)
|
public NES(string fname)
|
||||||
{
|
{
|
||||||
@ -75,7 +96,7 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
bVideoMode = false;
|
bVideoMode = false;
|
||||||
|
|
||||||
NesCfg = NesConfig.GetNTSC();
|
nescfg = NesConfig.GetNTSC();
|
||||||
|
|
||||||
CheatInitial();
|
CheatInitial();
|
||||||
|
|
||||||
@ -102,65 +123,241 @@ namespace VirtualNes.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Command(NESCOMMAND cmd)
|
||||||
|
{
|
||||||
|
CommandParam(cmd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CommandParam(NESCOMMAND cmd, int param)
|
||||||
|
{
|
||||||
|
switch (cmd)
|
||||||
|
{
|
||||||
|
case NESCOMMAND.NESCMD_NONE: break;
|
||||||
|
case NESCOMMAND.NESCMD_HWRESET:
|
||||||
|
Reset();
|
||||||
|
m_CommandRequest = (int)cmd;
|
||||||
|
break;
|
||||||
|
case NESCOMMAND.NESCMD_SWRESET:
|
||||||
|
SoftReset();
|
||||||
|
m_CommandRequest = (int)cmd;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{cmd} not impl right now");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void CheatInitial()
|
public void CheatInitial()
|
||||||
{
|
{
|
||||||
m_CheatCode.Clear();
|
m_CheatCode.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EmulateFrame(bool bDraw)
|
||||||
|
{
|
||||||
|
int scanline = 0;
|
||||||
|
if (rom.IsNSF())
|
||||||
|
{
|
||||||
|
EmulateNSF();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct NesConfig
|
CheatCodeProcess();
|
||||||
|
|
||||||
|
NES_scanline = scanline;
|
||||||
|
|
||||||
|
if (RenderMethod != EnumRenderMethod.TILE_RENDER)
|
||||||
{
|
{
|
||||||
public float BaseClock; // NTSC:21477270.0 PAL:21281364.0
|
bZapper = false;
|
||||||
public float CpuClock; // NTSC: 1789772.5 PAL: 1773447.0
|
while (true)
|
||||||
|
|
||||||
public int TotalScanLines; // NTSC: 262 PAL: 312
|
|
||||||
|
|
||||||
public int ScanlingCycles; // NTSC:1364 PAL:1362
|
|
||||||
|
|
||||||
public int HDrawCycles; // NTSC:1024 PAL:1024
|
|
||||||
public int HBlankCycles; // NTSC: 340 PAL: 338
|
|
||||||
public int ScanlineEndCycles; // NTSC: 4 PAL: 2
|
|
||||||
|
|
||||||
public int FrameCycles; // NTSC:29829.52 PAL:35468.94
|
|
||||||
public int FrameIrqCycles; // NTSC:29829.52 PAL:35468.94
|
|
||||||
|
|
||||||
public int FrameRate; // NTSC:60(59.94) PAL:50
|
|
||||||
public float FramePeriod; // NTSC:16.683 PAL:20.0
|
|
||||||
|
|
||||||
public static NesConfig GetNTSC()
|
|
||||||
{
|
{
|
||||||
return new NesConfig
|
ppu.SetRenderScanline(scanline);
|
||||||
{
|
|
||||||
BaseClock = 21477270.0f,
|
|
||||||
CpuClock = 1789772.5f,
|
|
||||||
TotalScanLines = 262,
|
|
||||||
ScanlingCycles = 1364,
|
|
||||||
HDrawCycles = 1024,
|
|
||||||
HBlankCycles = 340,
|
|
||||||
ScanlineEndCycles = 4,
|
|
||||||
FrameCycles = 1364 * 262,
|
|
||||||
FrameIrqCycles = 29830,
|
|
||||||
FrameRate = 60,
|
|
||||||
FramePeriod = 1000.0f / 60.0f
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public static NesConfig GetPAL()
|
if (scanline == 0)
|
||||||
{
|
{
|
||||||
return new NesConfig
|
if (RenderMethod < EnumRenderMethod.POST_RENDER)
|
||||||
{
|
{
|
||||||
BaseClock = 26601714.0f,
|
EmulationCPU(nescfg.ScanlineCycles);
|
||||||
CpuClock = 1662607.125f,
|
|
||||||
TotalScanLines = 312,
|
|
||||||
ScanlingCycles = 1278,
|
|
||||||
HDrawCycles = 960,
|
|
||||||
HBlankCycles = 318,
|
|
||||||
ScanlineEndCycles = 2,
|
|
||||||
FrameCycles = 1278 * 312,
|
|
||||||
FrameIrqCycles = 33252,
|
|
||||||
FrameRate = 50,
|
|
||||||
FramePeriod = 1000.0f / 50.0f
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void EmulationCPU(int basecycles)
|
||||||
|
{
|
||||||
|
int cycles;
|
||||||
|
|
||||||
|
base_cycles += basecycles;
|
||||||
|
cycles = (int)((base_cycles / 12) - emul_cycles);
|
||||||
|
|
||||||
|
if (cycles > 0)
|
||||||
|
{
|
||||||
|
emul_cycles += cpu.EXEC(cycles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Reset()
|
||||||
|
{
|
||||||
|
SaveSRAM();
|
||||||
|
SaveDISK();
|
||||||
|
SaveTurboFile();
|
||||||
|
|
||||||
|
//todo : 实现
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SoftReset()
|
||||||
|
{
|
||||||
|
//todo : 实现
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void EmulateNSF()
|
||||||
|
{
|
||||||
|
//todo : 实现NSF模拟
|
||||||
|
throw new NotImplementedException("EmulateNSF");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void CheatCodeProcess()
|
||||||
|
{
|
||||||
|
//todo : 实现作弊码
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
cpu?.Dispose();
|
||||||
|
ppu?.Dispose();
|
||||||
|
apu?.Dispose();
|
||||||
|
pad?.Dispose();
|
||||||
|
rom?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveSRAM()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
if (rom.IsNSF()) return;
|
||||||
|
if (rom.IsSAVERAM()) return;
|
||||||
|
|
||||||
|
for (i = 0; i < SAVERAM_SIZE; i++)
|
||||||
|
{
|
||||||
|
if (MMU.WARM[i] != 0x00)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < SAVERAM_SIZE)
|
||||||
|
{
|
||||||
|
var romName = rom.GetRomName();
|
||||||
|
|
||||||
|
Debuger.Log($"Saving SAVERAM...[{romName}]");
|
||||||
|
|
||||||
|
Supporter.SaveSRAMToFile(MMU.WARM, romName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveDISK()
|
||||||
|
{
|
||||||
|
if (rom.GetMapperNo() != 20)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
Stream fp = null;
|
||||||
|
DISKFILEHDR ifh;
|
||||||
|
byte[] lpDisk = rom.GetPROM();
|
||||||
|
byte[] lpWrite = rom.GetDISK();
|
||||||
|
long DiskSize = 16 + 65500 * rom.GetDiskNo();
|
||||||
|
ulong data;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ifh = new DISKFILEHDR();
|
||||||
|
ifh.ID = ASCIIEncoding.ASCII.GetBytes("VirtuaNES DI");
|
||||||
|
ifh.BlockVersion = 0x0210;
|
||||||
|
ifh.ProgID = rom.GetGameID();
|
||||||
|
ifh.MakerID = (ushort)rom.GetMakerID();
|
||||||
|
ifh.DiskNo = (ushort)rom.GetDiskNo();
|
||||||
|
|
||||||
|
for (i = 16; i < DiskSize; i++)
|
||||||
|
{
|
||||||
|
if (lpWrite[i] > 0)
|
||||||
|
ifh.DifferentSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifh.DifferentSize == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<byte> contents = new List<byte>();
|
||||||
|
contents.AddRange(ifh.ToBytes());
|
||||||
|
|
||||||
|
for (i = 16; i < DiskSize; i++)
|
||||||
|
{
|
||||||
|
if (lpWrite[i] > 0)
|
||||||
|
{
|
||||||
|
data = (ulong)(i & 0x00FFFFFF);
|
||||||
|
data |= ((ulong)lpDisk[i] & 0xFF) << 24;
|
||||||
|
contents.AddRange(BitConverter.GetBytes(data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Supporter.SaveDISKToFile(contents.ToArray(), rom.GetRomName());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debuger.LogError(ex.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveTurboFile()
|
||||||
|
{
|
||||||
|
//todo : 实现
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Clock(int cycles)
|
||||||
|
{
|
||||||
|
Tape(cycles);
|
||||||
|
Barcode(cycles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Barcode(int cycles)
|
||||||
|
{
|
||||||
|
if (m_bBarcode)
|
||||||
|
{
|
||||||
|
m_BarcodeCycles += cycles;
|
||||||
|
if (m_BarcodeCycles > 1000)
|
||||||
|
{
|
||||||
|
m_BarcodeCycles = 0;
|
||||||
|
// 掆巭丠
|
||||||
|
if (m_BarcodeData[m_BarcodePtr] != 0xFF)
|
||||||
|
{
|
||||||
|
m_BarcodeOut = m_BarcodeData[m_BarcodePtr++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_bBarcode = false;
|
||||||
|
m_BarcodeOut = 0;
|
||||||
|
Debuger.Log("Barcode data trasnfer complete!!");
|
||||||
|
|
||||||
|
if (!(IsTapePlay() || IsTapeRec()))
|
||||||
|
{
|
||||||
|
cpu.SetClockProcess(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTapeRec()
|
||||||
|
{
|
||||||
|
return m_bTapeRec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsTapePlay()
|
||||||
|
{
|
||||||
|
return m_bTapePlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Tape(int cycles)
|
||||||
|
{
|
||||||
|
//todo : 实现Tape (目测是记录玩家操作再Play,优先级很低)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace VirtualNes.Core
|
using System;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class PAD
|
public class PAD
|
||||||
{
|
{
|
||||||
@ -32,6 +34,10 @@
|
|||||||
padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0;
|
padbitsync[0] = padbitsync[1] = padbitsync[2] = padbitsync[3] = 0;
|
||||||
micbitsync = 0;
|
micbitsync = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum VSType
|
public enum VSType
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
namespace VirtualNes.Core
|
using System;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class PPU
|
public class PPU
|
||||||
{
|
{
|
||||||
@ -9,6 +11,10 @@
|
|||||||
private int nVSColorMap;
|
private int nVSColorMap;
|
||||||
private byte VSSecurityData;
|
private byte VSSecurityData;
|
||||||
private byte[] Bit2Rev = new byte[256];
|
private byte[] Bit2Rev = new byte[256];
|
||||||
|
private int ScanlineNo;
|
||||||
|
/// <summary> 作为lpScreen数组的索引 </summary>
|
||||||
|
private int lpScanline;
|
||||||
|
|
||||||
public PPU(NES nes)
|
public PPU(NES nes)
|
||||||
{
|
{
|
||||||
m_nes = nes;
|
m_nes = nes;
|
||||||
@ -31,5 +37,24 @@
|
|||||||
Bit2Rev[i] = c;
|
Bit2Rev[i] = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetRenderScanline(int scanline)
|
||||||
|
{
|
||||||
|
ScanlineNo = scanline;
|
||||||
|
if (scanline < 240)
|
||||||
|
{
|
||||||
|
lpScanline = (int)(Screen.SCREEN_WIDTH) * scanline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum Screen
|
||||||
|
{
|
||||||
|
SCREEN_WIDTH = 256 + 16,
|
||||||
|
SCREEN_HEIGHT = 240
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using VirtualNes.Core.Debug;
|
using VirtualNes.Core.Debug;
|
||||||
|
using static UnityEditor.PlayerSettings;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -282,11 +283,30 @@ namespace VirtualNes.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
lpPRG = null;
|
||||||
|
lpCHR = null;
|
||||||
|
lpTrainer = null;
|
||||||
|
lpDiskBios = null;
|
||||||
|
lpDisk = null;
|
||||||
|
}
|
||||||
|
|
||||||
public bool IsTRAINER()
|
public bool IsTRAINER()
|
||||||
{
|
{
|
||||||
return (header.control1 & (byte)EnumRomControlByte1.ROM_TRAINER) > 0;
|
return (header.control1 & (byte)EnumRomControlByte1.ROM_TRAINER) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsNSF()
|
||||||
|
{
|
||||||
|
return bNSF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsSAVERAM()
|
||||||
|
{
|
||||||
|
return (header.control1 & (byte)EnumRomControlByte1.ROM_SAVERAM) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
protected void FileNameCheck(string fname)
|
protected void FileNameCheck(string fname)
|
||||||
{
|
{
|
||||||
if (fname.Contains("(E)"))
|
if (fname.Contains("(E)"))
|
||||||
@ -295,6 +315,41 @@ namespace VirtualNes.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal string GetRomName()
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetMapperNo()
|
||||||
|
{
|
||||||
|
return mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte[] GetPROM()
|
||||||
|
{
|
||||||
|
return lpPRG;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal byte[] GetDISK()
|
||||||
|
{
|
||||||
|
return lpDisk;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetDiskNo()
|
||||||
|
{
|
||||||
|
return diskno;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong GetGameID()
|
||||||
|
{
|
||||||
|
return fdsgameID;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal ulong GetMakerID()
|
||||||
|
{
|
||||||
|
return fdsmakerID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +28,15 @@ namespace VirtualNes.Core
|
|||||||
return s_support.OpenFile_DISKSYS();
|
return s_support.OpenFile_DISKSYS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void SaveSRAMToFile(byte[] sramContent, string romName)
|
||||||
|
{
|
||||||
|
s_support.SaveSRAMToFile(sramContent, romName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveDISKToFile(byte[] diskFileContent, string romName)
|
||||||
|
{
|
||||||
|
s_support.SaveDISKToFile(diskFileContent, romName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ISupporterImpl
|
public interface ISupporterImpl
|
||||||
@ -35,5 +44,7 @@ namespace VirtualNes.Core
|
|||||||
Stream OpenRom(string fname);
|
Stream OpenRom(string fname);
|
||||||
void GetRomPathInfo(string fname, out string fullPath, out string directPath);
|
void GetRomPathInfo(string fname, out string fullPath, out string directPath);
|
||||||
Stream OpenFile_DISKSYS();
|
Stream OpenFile_DISKSYS();
|
||||||
|
void SaveSRAMToFile(byte[] sramContent, string romName);
|
||||||
|
void SaveDISKToFile(byte[] diskFileContent, string romName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user