forked from sin365/AxibugEmuOnline
NEScore StateSave机制 实现
This commit is contained in:
parent
2f5b5d9841
commit
5ce82c9406
@ -8,6 +8,7 @@ using VirtualNes.Core.Debug;
|
|||||||
|
|
||||||
namespace AxibugEmuOnline.Client
|
namespace AxibugEmuOnline.Client
|
||||||
{
|
{
|
||||||
|
|
||||||
public class NesEmulator : MonoBehaviour
|
public class NesEmulator : MonoBehaviour
|
||||||
{
|
{
|
||||||
public NES NesCore { get; private set; }
|
public NES NesCore { get; private set; }
|
||||||
|
@ -17,6 +17,7 @@ namespace AxibugEmuOnline.Client
|
|||||||
private int TexBufferSize;
|
private int TexBufferSize;
|
||||||
|
|
||||||
private Texture2D pPal;
|
private Texture2D pPal;
|
||||||
|
|
||||||
public void SetDrawData(uint[] screenData, byte[] lineColorMode, int screenWidth, int screenHeight)
|
public void SetDrawData(uint[] screenData, byte[] lineColorMode, int screenWidth, int screenHeight)
|
||||||
{
|
{
|
||||||
if (wrapTex == null)
|
if (wrapTex == null)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using VirtualNes.Core.Debug;
|
using VirtualNes.Core.Debug;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
@ -536,6 +537,58 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void GetFrameIRQ(ref int Cycle, ref byte Count, ref byte Type, ref byte IRQ, ref byte Occur)
|
||||||
|
{
|
||||||
|
@internal.GetFrameIRQ(ref Cycle, ref Count, ref Type, ref IRQ, ref Occur);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
// 時間軸を同期させる為Flushする
|
||||||
|
QueueFlush();
|
||||||
|
|
||||||
|
@internal.SaveState(buffer);
|
||||||
|
buffer.Position += (@internal.GetSize() + 15) & (~0x0F);
|
||||||
|
|
||||||
|
// VRC6
|
||||||
|
if ((exsound_select & 0x01) != 0)
|
||||||
|
{
|
||||||
|
vrc6.SaveState(buffer);
|
||||||
|
buffer.Position += (vrc6.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
}
|
||||||
|
// VRC7 (not support)
|
||||||
|
if ((exsound_select & 0x02) != 0)
|
||||||
|
{
|
||||||
|
vrc7.SaveState(buffer);
|
||||||
|
buffer.Position += (vrc7.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
}
|
||||||
|
// FDS
|
||||||
|
if ((exsound_select & 0x04) != 0)
|
||||||
|
{
|
||||||
|
fds.SaveState(buffer);
|
||||||
|
buffer.Position += (fds.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
|
||||||
|
}
|
||||||
|
// MMC5
|
||||||
|
if ((exsound_select & 0x08) != 0)
|
||||||
|
{
|
||||||
|
mmc5.SaveState(buffer);
|
||||||
|
buffer.Position += (mmc5.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
}
|
||||||
|
// N106
|
||||||
|
if ((exsound_select & 0x10) != 0)
|
||||||
|
{
|
||||||
|
n106.SaveState(buffer);
|
||||||
|
buffer.Position += (n106.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
}
|
||||||
|
// FME7
|
||||||
|
if ((exsound_select & 0x20) != 0)
|
||||||
|
{
|
||||||
|
fme7.SaveState(buffer);
|
||||||
|
buffer.Position += (fme7.GetSize() + 15) & (~0x0F); // Padding
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct QUEUEDATA
|
public struct QUEUEDATA
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -403,7 +404,18 @@ namespace VirtualNes.Core
|
|||||||
return fds.now_freq;
|
return fds.now_freq;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FDSSOUND
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
return fds.GetSize() + fds_sync.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
fds.SaveState(buffer);
|
||||||
|
fds_sync.SaveState(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FDSSOUND : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[0x80];
|
public byte[] reg = new byte[0x80];
|
||||||
public byte volenv_mode; // Volume Envelope
|
public byte volenv_mode; // Volume Envelope
|
||||||
@ -467,6 +479,41 @@ namespace VirtualNes.Core
|
|||||||
now_freq = 0;
|
now_freq = 0;
|
||||||
output = 0;
|
output = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(volenv_mode);
|
||||||
|
buffer.Write(volenv_gain);
|
||||||
|
buffer.Write(volenv_decay);
|
||||||
|
buffer.Write(volenv_phaseacc);
|
||||||
|
buffer.Write(swpenv_mode);
|
||||||
|
buffer.Write(swpenv_gain);
|
||||||
|
buffer.Write(swpenv_decay);
|
||||||
|
buffer.Write(swpenv_phaseacc);
|
||||||
|
buffer.Write(envelope_enable);
|
||||||
|
buffer.Write(envelope_speed);
|
||||||
|
buffer.Write(wave_setup);
|
||||||
|
buffer.Write(master_volume);
|
||||||
|
buffer.Write(main_wavetable);
|
||||||
|
buffer.Write(main_enable);
|
||||||
|
buffer.Write(main_frequency);
|
||||||
|
buffer.Write(main_addr);
|
||||||
|
buffer.Write(lfo_wavetable);
|
||||||
|
buffer.Write(lfo_enable);
|
||||||
|
buffer.Write(lfo_frequency);
|
||||||
|
buffer.Write(lfo_addr);
|
||||||
|
buffer.Write(lfo_phaseacc);
|
||||||
|
buffer.Write(sweep_bias);
|
||||||
|
buffer.Write(now_volume);
|
||||||
|
buffer.Write(now_freq);
|
||||||
|
buffer.Write(output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using static VirtualNes.Core.APU_FME7;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -91,7 +92,7 @@ namespace VirtualNes.Core
|
|||||||
byte[][] envelope_table;
|
byte[][] envelope_table;
|
||||||
sbyte[][] envstep_table;
|
sbyte[][] envstep_table;
|
||||||
|
|
||||||
ENVELOPE envelope = new ENVELOPE();
|
ENVELOPE envelope;
|
||||||
NOISE noise = new NOISE();
|
NOISE noise = new NOISE();
|
||||||
CHANNEL[] op = new CHANNEL[3] { new CHANNEL(), new CHANNEL(), new CHANNEL() };
|
CHANNEL[] op = new CHANNEL[3] { new CHANNEL(), new CHANNEL(), new CHANNEL() };
|
||||||
byte address;
|
byte address;
|
||||||
@ -117,6 +118,7 @@ namespace VirtualNes.Core
|
|||||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse,
|
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse,
|
||||||
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse
|
envstep_sawtooth, envstep_pulse, envstep_triangle, envstep_pulse
|
||||||
};
|
};
|
||||||
|
envelope = new ENVELOPE(envelope_table, envstep_table);
|
||||||
|
|
||||||
Reset(APU_CLOCK, 22050);
|
Reset(APU_CLOCK, 22050);
|
||||||
}
|
}
|
||||||
@ -133,8 +135,8 @@ namespace VirtualNes.Core
|
|||||||
item.ZeroMemory();
|
item.ZeroMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
envelope.envtbl = envelope_table[0];
|
envelope.envtbl_index = 0;
|
||||||
envelope.envstep = envstep_table[0];
|
envelope.envstep_index = 0;
|
||||||
|
|
||||||
noise.noiserange = 1;
|
noise.noiserange = 1;
|
||||||
noise.noiseout = 0xFF;
|
noise.noiseout = 0xFF;
|
||||||
@ -211,8 +213,8 @@ namespace VirtualNes.Core
|
|||||||
envelope.freq = INT2FIX(((envelope.reg[1] & 0x0F) << 8) + envelope.reg[0] + 1);
|
envelope.freq = INT2FIX(((envelope.reg[1] & 0x0F) << 8) + envelope.reg[0] + 1);
|
||||||
break;
|
break;
|
||||||
case 0x0D:
|
case 0x0D:
|
||||||
envelope.envtbl = envelope_table[data & 0x0F];
|
envelope.envtbl_index = (byte)(data & 0x0F);
|
||||||
envelope.envstep = envstep_table[data & 0x0F];
|
envelope.envstep_index = (byte)(data & 0x0F);
|
||||||
envelope.envadr = 0;
|
envelope.envadr = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -332,7 +334,22 @@ namespace VirtualNes.Core
|
|||||||
return ch.output_vol;
|
return ch.output_vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ENVELOPE
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)(1 + envelope.GetSize() + noise.GetSize() + op[0].GetSize() * op.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(address);
|
||||||
|
|
||||||
|
envelope.SaveState(buffer);
|
||||||
|
noise.SaveState(buffer);
|
||||||
|
foreach (var oneOp in op)
|
||||||
|
oneOp.SaveState(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ENVELOPE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[3];
|
public byte[] reg = new byte[3];
|
||||||
public byte volume;
|
public byte volume;
|
||||||
@ -341,8 +358,19 @@ namespace VirtualNes.Core
|
|||||||
public int phaseacc;
|
public int phaseacc;
|
||||||
public int envadr;
|
public int envadr;
|
||||||
|
|
||||||
public byte[] envtbl;
|
public byte envtbl_index;
|
||||||
public sbyte[] envstep;
|
public byte envstep_index;
|
||||||
|
|
||||||
|
byte[][] ref_envtbl;
|
||||||
|
sbyte[][] ref_envstep;
|
||||||
|
public byte[] envtbl => ref_envtbl[envtbl_index];
|
||||||
|
public sbyte[] envstep => ref_envstep[envstep_index];
|
||||||
|
public ENVELOPE(byte[][] envtbl, sbyte[][] envstep)
|
||||||
|
{
|
||||||
|
ref_envtbl = envtbl;
|
||||||
|
ref_envstep = envstep;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void ZeroMemory()
|
public void ZeroMemory()
|
||||||
{
|
{
|
||||||
@ -351,12 +379,28 @@ namespace VirtualNes.Core
|
|||||||
freq = 0;
|
freq = 0;
|
||||||
phaseacc = 0;
|
phaseacc = 0;
|
||||||
envadr = 0;
|
envadr = 0;
|
||||||
envtbl = null;
|
envtbl_index = 0;
|
||||||
envstep = null;
|
envstep_index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(envadr);
|
||||||
|
buffer.Write(envtbl_index);
|
||||||
|
buffer.Write(envstep_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class NOISE
|
public class NOISE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public int freq;
|
public int freq;
|
||||||
public int phaseacc;
|
public int phaseacc;
|
||||||
@ -367,9 +411,22 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
freq = 0; phaseacc = 0; noiserange = 0; noiseout = 0;
|
freq = 0; phaseacc = 0; noiserange = 0; noiseout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(noiserange);
|
||||||
|
buffer.Write(noiseout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class CHANNEL
|
public class CHANNEL : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[3];
|
public byte[] reg = new byte[3];
|
||||||
public byte enable;
|
public byte enable;
|
||||||
@ -394,6 +451,24 @@ namespace VirtualNes.Core
|
|||||||
phaseacc = 0;
|
phaseacc = 0;
|
||||||
output_vol = 0;
|
output_vol = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(env_on);
|
||||||
|
buffer.Write(noise_on);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(output_vol);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
namespace VirtualNes.Core
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public abstract class APU_INTERFACE
|
public abstract class APU_INTERFACE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public const float APU_CLOCK = 1789772.5f;
|
public const float APU_CLOCK = 1789772.5f;
|
||||||
|
|
||||||
@ -19,8 +22,7 @@
|
|||||||
public virtual void VSync() { }
|
public virtual void VSync() { }
|
||||||
public virtual bool Sync(int cycles) { return false; }
|
public virtual bool Sync(int cycles) { return false; }
|
||||||
public virtual int GetFreq(int channel) { return 0; }
|
public virtual int GetFreq(int channel) { return 0; }
|
||||||
public virtual int GetStateSize() { return 0; }
|
public virtual void SaveState(StateBuffer buffer) { }
|
||||||
public virtual void SaveState(byte[] p) { }
|
|
||||||
public virtual void LoadState(byte[] p) { }
|
public virtual void LoadState(byte[] p) { }
|
||||||
|
|
||||||
public static int INT2FIX(int x)
|
public static int INT2FIX(int x)
|
||||||
@ -32,5 +34,11 @@
|
|||||||
{
|
{
|
||||||
return x >> 16;
|
return x >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public virtual uint GetSize()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -1190,7 +1193,49 @@ namespace VirtualNes.Core
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public class RECTANGLE
|
|
||||||
|
internal void GetFrameIRQ(ref int cycle, ref byte count, ref byte type, ref byte IRQ, ref byte occur)
|
||||||
|
{
|
||||||
|
cycle = FrameCycle;
|
||||||
|
count = (byte)FrameCount;
|
||||||
|
type = (byte)FrameType;
|
||||||
|
IRQ = FrameIRQ;
|
||||||
|
occur = FrameIRQoccur;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
return sizeof(byte) +
|
||||||
|
sizeof(byte) +
|
||||||
|
sizeof(int) +
|
||||||
|
sizeof(int) +
|
||||||
|
sizeof(int) +
|
||||||
|
sizeof(byte) +
|
||||||
|
sizeof(byte) +
|
||||||
|
ch0.GetSize() +
|
||||||
|
ch1.GetSize() +
|
||||||
|
ch2.GetSize() +
|
||||||
|
ch3.GetSize() +
|
||||||
|
ch4.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe override void SaveState(StateBuffer p)
|
||||||
|
{
|
||||||
|
p.Write(reg4015);
|
||||||
|
p.Write(sync_reg4015);
|
||||||
|
p.Write(FrameCycle);
|
||||||
|
p.Write(FrameCount);
|
||||||
|
p.Write(FrameType);
|
||||||
|
p.Write(FrameIRQ);
|
||||||
|
p.Write(FrameIRQoccur);
|
||||||
|
ch0.SaveState(p);
|
||||||
|
ch1.SaveState(p);
|
||||||
|
ch2.SaveState(p);
|
||||||
|
ch3.SaveState(p);
|
||||||
|
ch4.SaveState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RECTANGLE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[4]; // register
|
public byte[] reg = new byte[4]; // register
|
||||||
|
|
||||||
@ -1269,8 +1314,46 @@ namespace VirtualNes.Core
|
|||||||
dummy2 = 0;
|
dummy2 = 0;
|
||||||
sync_len_count = 0;
|
sync_len_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 64;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(holdnote);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(complement);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(freqlimit);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(duty);
|
||||||
|
buffer.Write(len_count);
|
||||||
|
buffer.Write(nowvolume);
|
||||||
|
buffer.Write(env_fixed);
|
||||||
|
buffer.Write(env_decay);
|
||||||
|
buffer.Write(env_count);
|
||||||
|
buffer.Write(dummy0);
|
||||||
|
buffer.Write(env_vol);
|
||||||
|
buffer.Write(swp_on);
|
||||||
|
buffer.Write(swp_inc);
|
||||||
|
buffer.Write(swp_shift);
|
||||||
|
buffer.Write(swp_decay);
|
||||||
|
buffer.Write(swp_count);
|
||||||
|
buffer.Write(dummy1);
|
||||||
|
buffer.Write(sync_reg);
|
||||||
|
buffer.Write(sync_output_enable);
|
||||||
|
buffer.Write(sync_enable);
|
||||||
|
buffer.Write(sync_holdnote);
|
||||||
|
buffer.Write(dummy2);
|
||||||
|
buffer.Write(sync_len_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class TRIANGLE
|
public class TRIANGLE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[4];
|
public byte[] reg = new byte[4];
|
||||||
|
|
||||||
@ -1296,6 +1379,7 @@ namespace VirtualNes.Core
|
|||||||
public int sync_len_count;
|
public int sync_len_count;
|
||||||
public int sync_lin_count;
|
public int sync_lin_count;
|
||||||
|
|
||||||
|
|
||||||
internal void ZeroMemory()
|
internal void ZeroMemory()
|
||||||
{
|
{
|
||||||
Array.Clear(reg, 0, reg.Length);
|
Array.Clear(reg, 0, reg.Length);
|
||||||
@ -1318,8 +1402,34 @@ namespace VirtualNes.Core
|
|||||||
sync_len_count = 0;
|
sync_len_count = 0;
|
||||||
sync_lin_count = 0;
|
sync_lin_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 47;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(holdnote);
|
||||||
|
buffer.Write(counter_start);
|
||||||
|
buffer.Write(dummy0);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(len_count);
|
||||||
|
buffer.Write(lin_count);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(nowvolume);
|
||||||
|
buffer.Write(sync_reg);
|
||||||
|
buffer.Write(sync_enable);
|
||||||
|
buffer.Write(sync_holdnote);
|
||||||
|
buffer.Write(sync_counter_start);
|
||||||
|
buffer.Write(sync_len_count);
|
||||||
|
buffer.Write(sync_lin_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class DPCM
|
public class DPCM : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[4];
|
public byte[] reg = new byte[4];
|
||||||
public byte enable;
|
public byte enable;
|
||||||
@ -1343,8 +1453,42 @@ namespace VirtualNes.Core
|
|||||||
public byte sync_irq_enable;
|
public byte sync_irq_enable;
|
||||||
public int sync_cycles, sync_cache_cycles;
|
public int sync_cycles, sync_cache_cycles;
|
||||||
public int sync_dmalength, sync_cache_dmalength;
|
public int sync_dmalength, sync_cache_dmalength;
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 72;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(looping);
|
||||||
|
buffer.Write(cur_byte);
|
||||||
|
buffer.Write(dpcm_value);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(output);
|
||||||
|
buffer.Write(address);
|
||||||
|
buffer.Write(cache_addr);
|
||||||
|
buffer.Write(dmalength);
|
||||||
|
buffer.Write(cache_dmalength);
|
||||||
|
buffer.Write(dpcm_output_real);
|
||||||
|
buffer.Write(dpcm_output_fake);
|
||||||
|
buffer.Write(dpcm_output_old);
|
||||||
|
buffer.Write(dpcm_output_offset);
|
||||||
|
buffer.Write(sync_reg);
|
||||||
|
buffer.Write(sync_enable);
|
||||||
|
buffer.Write(sync_looping);
|
||||||
|
buffer.Write(sync_irq_gen);
|
||||||
|
buffer.Write(sync_irq_enable);
|
||||||
|
buffer.Write(sync_cycles);
|
||||||
|
buffer.Write(sync_cache_cycles);
|
||||||
|
buffer.Write(sync_dmalength);
|
||||||
|
buffer.Write(sync_cache_dmalength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public class NOISE
|
public class NOISE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[4]; // register
|
public byte[] reg = new byte[4]; // register
|
||||||
|
|
||||||
@ -1377,6 +1521,7 @@ namespace VirtualNes.Core
|
|||||||
public byte dummy1;
|
public byte dummy1;
|
||||||
public int sync_len_count;
|
public int sync_len_count;
|
||||||
|
|
||||||
|
|
||||||
internal void ZeroMemory()
|
internal void ZeroMemory()
|
||||||
{
|
{
|
||||||
Array.Clear(reg, 0, reg.Length);
|
Array.Clear(reg, 0, reg.Length);
|
||||||
@ -1406,6 +1551,37 @@ namespace VirtualNes.Core
|
|||||||
dummy1 = 0;
|
dummy1 = 0;
|
||||||
sync_len_count = 0;
|
sync_len_count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 52;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(holdnote);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(xor_tap);
|
||||||
|
buffer.Write(shift_reg);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(len_count);
|
||||||
|
buffer.Write(nowvolume);
|
||||||
|
buffer.Write(output);
|
||||||
|
buffer.Write(env_fixed);
|
||||||
|
buffer.Write(env_decay);
|
||||||
|
buffer.Write(env_count);
|
||||||
|
buffer.Write(dummy0);
|
||||||
|
buffer.Write(env_vol);
|
||||||
|
buffer.Write(sync_reg);
|
||||||
|
buffer.Write(sync_output_enable);
|
||||||
|
buffer.Write(sync_enable);
|
||||||
|
buffer.Write(sync_holdnote);
|
||||||
|
buffer.Write(dummy1);
|
||||||
|
buffer.Write(sync_len_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,7 +370,27 @@ namespace VirtualNes.Core
|
|||||||
return ch.output_vol;
|
return ch.output_vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SYNCRECTANGLE
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
//3*sizeof(BYTE) + sizeof(ch0) + sizeof(ch1) + sizeof(sch0) + sizeof(sch1); 源代码似乎少了sync_reg5015的大小
|
||||||
|
return 3 + ch0.GetSize() + ch1.GetSize() + 1 + sch0.GetSize() + sch1.GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg5010);
|
||||||
|
buffer.Write(reg5011);
|
||||||
|
buffer.Write(reg5015);
|
||||||
|
|
||||||
|
ch0.SaveState(buffer);
|
||||||
|
ch1.SaveState(buffer);
|
||||||
|
|
||||||
|
buffer.Write(sync_reg5015);
|
||||||
|
sch0.SaveState(buffer);
|
||||||
|
sch1.SaveState(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SYNCRECTANGLE : IStateBufferObject
|
||||||
{
|
{
|
||||||
// For sync
|
// For sync
|
||||||
public byte[] reg = new byte[4];
|
public byte[] reg = new byte[4];
|
||||||
@ -387,9 +407,23 @@ namespace VirtualNes.Core
|
|||||||
Array.Clear(dummy, 0, dummy.Length);
|
Array.Clear(dummy, 0, dummy.Length);
|
||||||
vbl_length = 0;
|
vbl_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(holdnote);
|
||||||
|
buffer.Write(dummy);
|
||||||
|
buffer.Write(vbl_length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RECTANGLE
|
public class RECTANGLE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[4];
|
public byte[] reg = new byte[4];
|
||||||
public byte enable;
|
public byte enable;
|
||||||
@ -410,6 +444,29 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
public int adder;
|
public int adder;
|
||||||
public int duty_flip;
|
public int duty_flip;
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 45;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(vbl_length);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(output_vol);
|
||||||
|
buffer.Write(fixed_envelope);
|
||||||
|
buffer.Write(holdnote);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(env_vol);
|
||||||
|
buffer.Write(env_phase);
|
||||||
|
buffer.Write(env_decay);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(duty_flip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,101 +1,130 @@
|
|||||||
using RECTANGLE = VirtualNes.Core.APU_VRC6.RECTANGLE;
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
using RECTANGLE = VirtualNes.Core.APU_VRC6.RECTANGLE;
|
||||||
using SAWTOOTH = VirtualNes.Core.APU_VRC6.SAWTOOTH;
|
using SAWTOOTH = VirtualNes.Core.APU_VRC6.SAWTOOTH;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
public class APU_N106 : APU_INTERFACE
|
public class APU_N106 : APU_INTERFACE
|
||||||
{
|
{
|
||||||
RECTANGLE ch0 = new RECTANGLE();
|
CHANNEL[] op = new CHANNEL[8];
|
||||||
RECTANGLE ch1 = new RECTANGLE();
|
|
||||||
SAWTOOTH ch2 = new SAWTOOTH();
|
const int CHANNEL_VOL_SHIFT = 6;
|
||||||
float cpu_clock;
|
float cpu_clock;
|
||||||
int cycle_rate;
|
uint cycle_rate;
|
||||||
|
|
||||||
|
byte addrinc;
|
||||||
|
byte address;
|
||||||
|
byte channel_use;
|
||||||
|
|
||||||
|
byte[] tone = new byte[0x100];
|
||||||
|
|
||||||
public APU_N106()
|
public APU_N106()
|
||||||
{
|
{
|
||||||
Reset(APU_CLOCK, 22050);
|
// 仮設定
|
||||||
|
cpu_clock = APU_CLOCK;
|
||||||
|
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * 22050.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Reset(float fClock, int nRate)
|
public override void Reset(float fClock, int nRate)
|
||||||
{
|
{
|
||||||
ch0.ZeroMemory();
|
for (int i = 0; i < 8; i++)
|
||||||
ch1.ZeroMemory();
|
{
|
||||||
ch2.ZeroMemory();
|
op[i].ZeroMemory();
|
||||||
|
op[i].tonelen = 0x10 << 18;
|
||||||
|
}
|
||||||
|
|
||||||
|
address = 0;
|
||||||
|
addrinc = 1;
|
||||||
|
channel_use = 8;
|
||||||
|
|
||||||
Setup(fClock, nRate);
|
Setup(fClock, nRate);
|
||||||
|
|
||||||
|
// TONEの初期化はしない...
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Setup(float fClock, int nRate)
|
public override void Setup(float fClock, int nRate)
|
||||||
{
|
{
|
||||||
cpu_clock = fClock;
|
cpu_clock = fClock;
|
||||||
cycle_rate = (int)(fClock * 65536.0f / nRate);
|
cycle_rate = (uint)(cpu_clock * 12.0f * (1 << 20) / (45.0f * nRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(ushort addr, byte data)
|
public override void Write(ushort addr, byte data)
|
||||||
{
|
{
|
||||||
switch (addr)
|
if (addr == 0x4800)
|
||||||
{
|
{
|
||||||
// VRC6 CH0 rectangle
|
// tone[address*2+0] = (INT)(data&0x0F);
|
||||||
case 0x9000:
|
// tone[address*2+1] = (INT)(data >>4);
|
||||||
ch0.reg[0] = data;
|
tone[address * 2 + 0] = (byte)(data & 0x0F);
|
||||||
ch0.gate = (byte)(data & 0x80);
|
tone[address * 2 + 1] = (byte)(data >> 4);
|
||||||
ch0.volume = (byte)(data & 0x0F);
|
|
||||||
ch0.duty_pos = (byte)((data >> 4) & 0x07);
|
if (address >= 0x40)
|
||||||
break;
|
{
|
||||||
case 0x9001:
|
int no = (address - 0x40) >> 3;
|
||||||
ch0.reg[1] = data;
|
uint tonelen = 0;
|
||||||
ch0.freq = INT2FIX((((ch0.reg[2] & 0x0F) << 8) | data) + 1);
|
ref CHANNEL ch = ref op[no];
|
||||||
break;
|
|
||||||
case 0x9002:
|
switch (address & 7)
|
||||||
ch0.reg[2] = data;
|
{
|
||||||
ch0.enable = (byte)(data & 0x80);
|
case 0x00:
|
||||||
ch0.freq = INT2FIX((((data & 0x0F) << 8) | ch0.reg[1]) + 1);
|
ch.freq = (uint)((ch.freq & ~0x000000FF) | data);
|
||||||
break;
|
break;
|
||||||
// VRC6 CH1 rectangle
|
case 0x02:
|
||||||
case 0xA000:
|
ch.freq = (uint)((ch.freq & ~0x0000FF00) | ((uint)data << 8));
|
||||||
ch1.reg[0] = data;
|
break;
|
||||||
ch1.gate = (byte)(data & 0x80);
|
case 0x04:
|
||||||
ch1.volume = (byte)(data & 0x0F);
|
ch.freq = (uint)((ch.freq & ~0x00030000) | (((uint)data & 0x03) << 16));
|
||||||
ch1.duty_pos = (byte)((data >> 4) & 0x07);
|
tonelen = (uint)((0x20 - (data & 0x1c)) << 18);
|
||||||
break;
|
ch.databuf = (byte)((data & 0x1c) >> 2);
|
||||||
case 0xA001:
|
if (ch.tonelen != tonelen)
|
||||||
ch1.reg[1] = data;
|
{
|
||||||
ch1.freq = INT2FIX((((ch1.reg[2] & 0x0F) << 8) | data) + 1);
|
ch.tonelen = tonelen;
|
||||||
break;
|
ch.phase = 0;
|
||||||
case 0xA002:
|
}
|
||||||
ch1.reg[2] = data;
|
break;
|
||||||
ch1.enable = (byte)(data & 0x80);
|
case 0x06:
|
||||||
ch1.freq = INT2FIX((((data & 0x0F) << 8) | ch1.reg[1]) + 1);
|
ch.toneadr = data;
|
||||||
break;
|
break;
|
||||||
// VRC6 CH2 sawtooth
|
case 0x07:
|
||||||
case 0xB000:
|
ch.vol = (byte)(data & 0x0f);
|
||||||
ch2.reg[1] = data;
|
ch.volupdate = 0xFF;
|
||||||
ch2.phaseaccum = (byte)(data & 0x3F);
|
if (no == 7)
|
||||||
break;
|
channel_use = (byte)(((data >> 4) & 0x07) + 1);
|
||||||
case 0xB001:
|
break;
|
||||||
ch2.reg[1] = data;
|
}
|
||||||
ch2.freq = INT2FIX((((ch2.reg[2] & 0x0F) << 8) | data) + 1);
|
}
|
||||||
break;
|
|
||||||
case 0xB002:
|
if (addrinc != 0)
|
||||||
ch2.reg[2] = data;
|
{
|
||||||
ch2.enable = (byte)(data & 0x80);
|
address = (byte)((address + 1) & 0x7f);
|
||||||
ch2.freq = INT2FIX((((data & 0x0F) << 8) | ch2.reg[1]) + 1);
|
}
|
||||||
// ch2.adder = 0; // クリアするとノイズの原因になる
|
|
||||||
// ch2.accum = 0; // クリアするとノイズの原因になる
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
else if (addr == 0xF800)
|
||||||
|
{
|
||||||
|
address = (byte)(data & 0x7F);
|
||||||
|
addrinc = (byte)(data & 0x80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override byte Read(ushort addr)
|
||||||
|
{
|
||||||
|
// $4800 dummy read!!
|
||||||
|
if (addr == 0x0000)
|
||||||
|
{
|
||||||
|
if (addrinc != 0)
|
||||||
|
{
|
||||||
|
address = (byte)((address + 1) & 0x7F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (byte)(addr >> 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int Process(int channel)
|
public override int Process(int channel)
|
||||||
{
|
{
|
||||||
switch (channel)
|
if (channel >= (8 - channel_use) && channel < 8)
|
||||||
{
|
{
|
||||||
case 0:
|
return ChannelRender(ref op[channel]);
|
||||||
return RectangleRender(ch0);
|
|
||||||
case 1:
|
|
||||||
return RectangleRender(ch1);
|
|
||||||
case 2:
|
|
||||||
return SawtoothRender(ch2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -103,143 +132,117 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
public override int GetFreq(int channel)
|
public override int GetFreq(int channel)
|
||||||
{
|
{
|
||||||
if (channel == 0 || channel == 1)
|
if (channel < 8)
|
||||||
{
|
{
|
||||||
RECTANGLE ch;
|
channel &= 7;
|
||||||
if (channel == 0) ch = ch0;
|
if (channel < (8 - channel_use))
|
||||||
else ch = ch1;
|
|
||||||
if (ch.enable == 0 || ch.gate != 0 || ch.volume == 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
if (ch.freq < INT2FIX(8))
|
|
||||||
|
ref CHANNEL ch = ref op[channel & 0x07];
|
||||||
|
if (ch.freq == 0 || ch.vol == 0)
|
||||||
return 0;
|
return 0;
|
||||||
return (int)((256.0f * cpu_clock / (FIX2INT(ch.freq) * 16.0f)));
|
int temp = channel_use * (8 - ch.databuf) * 4 * 45;
|
||||||
}
|
if (temp == 0)
|
||||||
if (channel == 2)
|
|
||||||
{
|
|
||||||
SAWTOOTH ch = ch2;
|
|
||||||
if (ch.enable == 0 || ch.phaseaccum == 0)
|
|
||||||
return 0;
|
return 0;
|
||||||
if (ch.freq < INT2FIX(8))
|
return (int)(256.0 * (double)cpu_clock * 12.0 * ch.freq / ((double)0x40000 * temp));
|
||||||
return 0;
|
|
||||||
return (int)(256.0f * cpu_clock / (FIX2INT(ch.freq) * 14.0f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RectangleRender(RECTANGLE ch)
|
private int ChannelRender(ref CHANNEL ch)
|
||||||
{
|
{
|
||||||
// Enable?
|
uint phasespd = (uint)(channel_use << 20);
|
||||||
if (ch.enable == 0)
|
|
||||||
{
|
|
||||||
ch.output_vol = 0;
|
|
||||||
ch.adder = 0;
|
|
||||||
return ch.output_vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Digitized output
|
ch.phaseacc -= (int)cycle_rate;
|
||||||
if (ch.gate != 0)
|
|
||||||
{
|
|
||||||
ch.output_vol = ch.volume << APU_VRC6.RECTANGLE_VOL_SHIFT;
|
|
||||||
return ch.output_vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 一定以上の周波数は処理しない(無駄)
|
|
||||||
if (ch.freq < INT2FIX(8))
|
|
||||||
{
|
|
||||||
ch.output_vol = 0;
|
|
||||||
return ch.output_vol;
|
|
||||||
}
|
|
||||||
|
|
||||||
ch.phaseacc -= cycle_rate;
|
|
||||||
if (ch.phaseacc >= 0)
|
if (ch.phaseacc >= 0)
|
||||||
return ch.output_vol;
|
|
||||||
|
|
||||||
int output = ch.volume << APU_VRC6.RECTANGLE_VOL_SHIFT;
|
|
||||||
|
|
||||||
if (ch.freq > cycle_rate)
|
|
||||||
{
|
{
|
||||||
// add 1 step
|
if (ch.volupdate != 0)
|
||||||
ch.phaseacc += ch.freq;
|
|
||||||
ch.adder = (byte)((ch.adder + 1) & 0x0F);
|
|
||||||
if (ch.adder <= ch.duty_pos)
|
|
||||||
ch.output_vol = output;
|
|
||||||
else
|
|
||||||
ch.output_vol = -output;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// average calculate
|
|
||||||
int num_times, total;
|
|
||||||
num_times = total = 0;
|
|
||||||
while (ch.phaseacc < 0)
|
|
||||||
{
|
{
|
||||||
ch.phaseacc += ch.freq;
|
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
||||||
ch.adder = (byte)((ch.adder + 1) & 0x0F);
|
ch.volupdate = 0;
|
||||||
if (ch.adder <= ch.duty_pos)
|
|
||||||
total += output;
|
|
||||||
else
|
|
||||||
total += -output;
|
|
||||||
num_times++;
|
|
||||||
}
|
}
|
||||||
ch.output_vol = total / num_times;
|
return ch.output;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ch.output_vol;
|
while (ch.phaseacc < 0)
|
||||||
|
{
|
||||||
|
ch.phaseacc += (int)phasespd;
|
||||||
|
ch.phase += ch.freq;
|
||||||
|
}
|
||||||
|
while (ch.tonelen != 0 && (ch.phase >= ch.tonelen))
|
||||||
|
{
|
||||||
|
ch.phase -= ch.tonelen;
|
||||||
|
}
|
||||||
|
|
||||||
|
ch.output = (tone[((ch.phase >> 18) + ch.toneadr) & 0xFF] * ch.vol) << CHANNEL_VOL_SHIFT;
|
||||||
|
|
||||||
|
return ch.output;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SawtoothRender(SAWTOOTH ch)
|
public override uint GetSize()
|
||||||
{
|
{
|
||||||
// Digitized output
|
return (uint)(3 * sizeof(byte) + 8 * op[0].GetSize() + tone.Length);
|
||||||
if (ch.enable == 0)
|
}
|
||||||
|
|
||||||
|
public override void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(addrinc);
|
||||||
|
buffer.Write(address);
|
||||||
|
buffer.Write(channel_use);
|
||||||
|
|
||||||
|
foreach (var oneOp in op)
|
||||||
|
oneOp.SaveState(buffer);
|
||||||
|
|
||||||
|
buffer.Write(tone);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CHANNEL : IStateBufferObject
|
||||||
|
{
|
||||||
|
public int phaseacc;
|
||||||
|
|
||||||
|
public uint freq;
|
||||||
|
public uint phase;
|
||||||
|
public uint tonelen;
|
||||||
|
|
||||||
|
public int output;
|
||||||
|
|
||||||
|
public byte toneadr;
|
||||||
|
public byte volupdate;
|
||||||
|
|
||||||
|
public byte vol;
|
||||||
|
public byte databuf;
|
||||||
|
|
||||||
|
internal void ZeroMemory()
|
||||||
{
|
{
|
||||||
ch.output_vol = 0;
|
phaseacc = 0;
|
||||||
return ch.output_vol;
|
freq = 0;
|
||||||
|
phase = 0;
|
||||||
|
tonelen = 0;
|
||||||
|
output = 0;
|
||||||
|
toneadr = 0;
|
||||||
|
volupdate = 0;
|
||||||
|
vol = 0;
|
||||||
|
databuf = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 一定以上の周波数は処理しない(無駄)
|
public uint GetSize()
|
||||||
if (ch.freq < INT2FIX(9))
|
|
||||||
{
|
{
|
||||||
return ch.output_vol;
|
return 4 * 5 + 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
ch.phaseacc -= cycle_rate / 2;
|
public void SaveState(StateBuffer buffer)
|
||||||
if (ch.phaseacc >= 0)
|
|
||||||
return ch.output_vol;
|
|
||||||
|
|
||||||
if (ch.freq > cycle_rate / 2)
|
|
||||||
{
|
{
|
||||||
// add 1 step
|
buffer.Write(phaseacc);
|
||||||
ch.phaseacc += ch.freq;
|
buffer.Write(freq);
|
||||||
if (++ch.adder >= 7)
|
buffer.Write(phase);
|
||||||
{
|
buffer.Write(tonelen);
|
||||||
ch.adder = 0;
|
buffer.Write(output);
|
||||||
ch.accum = 0;
|
buffer.Write(toneadr);
|
||||||
}
|
buffer.Write(volupdate);
|
||||||
ch.accum += ch.phaseaccum;
|
buffer.Write(vol);
|
||||||
ch.output_vol = ch.accum << APU_VRC6.SAWTOOTH_VOL_SHIFT;
|
buffer.Write(databuf);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// average calculate
|
|
||||||
int num_times, total;
|
|
||||||
num_times = total = 0;
|
|
||||||
while (ch.phaseacc < 0)
|
|
||||||
{
|
|
||||||
ch.phaseacc += ch.freq;
|
|
||||||
if (++ch.adder >= 7)
|
|
||||||
{
|
|
||||||
ch.adder = 0;
|
|
||||||
ch.accum = 0;
|
|
||||||
}
|
|
||||||
ch.accum += ch.phaseaccum;
|
|
||||||
total += ch.accum << APU_VRC6.SAWTOOTH_VOL_SHIFT;
|
|
||||||
num_times++;
|
|
||||||
}
|
|
||||||
ch.output_vol = (total / num_times);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ch.output_vol;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -245,7 +246,18 @@ namespace VirtualNes.Core
|
|||||||
return ch.output_vol;
|
return ch.output_vol;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RECTANGLE
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
return ch0.GetSize() + ch1.GetSize() + ch2.GetSize();
|
||||||
|
}
|
||||||
|
public override void SaveState(StateBuffer p)
|
||||||
|
{
|
||||||
|
ch0.SaveState(p);
|
||||||
|
ch1.SaveState(p);
|
||||||
|
ch2.SaveState(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RECTANGLE : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[3];
|
public byte[] reg = new byte[3];
|
||||||
|
|
||||||
@ -274,9 +286,27 @@ namespace VirtualNes.Core
|
|||||||
adder = 0;
|
adder = 0;
|
||||||
duty_pos = 0;
|
duty_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(gate);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(output_vol);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(duty_pos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SAWTOOTH
|
public class SAWTOOTH : IStateBufferObject
|
||||||
{
|
{
|
||||||
public byte[] reg = new byte[3];
|
public byte[] reg = new byte[3];
|
||||||
|
|
||||||
@ -305,6 +335,24 @@ namespace VirtualNes.Core
|
|||||||
accum = 0;
|
accum = 0;
|
||||||
phaseaccum = 0;
|
phaseaccum = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg);
|
||||||
|
buffer.Write(enable);
|
||||||
|
buffer.Write(volume);
|
||||||
|
buffer.Write(phaseacc);
|
||||||
|
buffer.Write(freq);
|
||||||
|
buffer.Write(output_vol);
|
||||||
|
buffer.Write(adder);
|
||||||
|
buffer.Write(accum);
|
||||||
|
buffer.Write(phaseaccum);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using VirtualNes.Core.Emu2413;
|
using VirtualNes.Core.Emu2413;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
@ -92,5 +93,15 @@ namespace VirtualNes.Core
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override uint GetSize()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
//not impl
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2021,6 +2021,11 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
r = R;
|
r = R;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal int GetDmaCycles()
|
||||||
|
{
|
||||||
|
return DMA_cycles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StatusFlag6502 : int
|
public enum StatusFlag6502 : int
|
||||||
|
@ -53,6 +53,11 @@ namespace VirtualNes.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Span<T> Span(int start, int length)
|
||||||
|
{
|
||||||
|
return new Span<T>(m_rawArray, start + Offset, length);
|
||||||
|
}
|
||||||
|
|
||||||
public static implicit operator ArrayRef<T>(T[] array)
|
public static implicit operator ArrayRef<T>(T[] array)
|
||||||
{
|
{
|
||||||
return new ArrayRef<T>(array);
|
return new ArrayRef<T>(array);
|
||||||
|
@ -40,5 +40,10 @@ namespace VirtualNes.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void memcpy(Array dst, Array src, int length)
|
||||||
|
{
|
||||||
|
Array.Copy(src, dst, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -60,12 +60,12 @@ namespace VirtualNes
|
|||||||
public const byte BANKTYPE_CRAM = 0x01;
|
public const byte BANKTYPE_CRAM = 0x01;
|
||||||
public const byte BANKTYPE_VRAM = 0x80;
|
public const byte BANKTYPE_VRAM = 0x80;
|
||||||
|
|
||||||
// 儈儔乕僞僀僾
|
// =ミラータイプ;
|
||||||
public const byte VRAM_HMIRROR = 0x00; // Horizontal
|
public const byte VRAM_HMIRROR = 0x00; // Horizontal
|
||||||
public const byte VRAM_VMIRROR = 0x01; // Virtical
|
public const byte VRAM_VMIRROR = 0x01; // Virtical
|
||||||
public const byte VRAM_MIRROR4 = 0x02; // All screen
|
public const byte VRAM_MIRROR4 = 0x02; // All screen
|
||||||
public const byte VRAM_MIRROR4L = 0x03; // PA10 L屌掕 $2000-$23FF偺儈儔乕
|
public const byte VRAM_MIRROR4L = 0x03; // PA10 L固定 $2000-$23FFのミラー
|
||||||
public const byte VRAM_MIRROR4H = 0x04; // PA10 H屌掕 $2400-$27FF偺儈儔乕
|
public const byte VRAM_MIRROR4H = 0x04; // PA10 H固定 $2400-$27FFのミラー
|
||||||
|
|
||||||
// Frame-IRQ儗僕僗僞($4017)
|
// Frame-IRQ儗僕僗僞($4017)
|
||||||
public static int FrameIRQ;
|
public static int FrameIRQ;
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
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.Runtime.ConstrainedExecution;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
using VirtualNes.Core.Debug;
|
using VirtualNes.Core.Debug;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
@ -21,7 +26,6 @@ namespace VirtualNes.Core
|
|||||||
private List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
private List<CHEATCODE> m_CheatCode = new List<CHEATCODE>();
|
||||||
private List<GENIECODE> m_GenieCode = new List<GENIECODE>();
|
private List<GENIECODE> m_GenieCode = new List<GENIECODE>();
|
||||||
private bool m_bDiskThrottle;
|
private bool m_bDiskThrottle;
|
||||||
private int m_CommandRequest;
|
|
||||||
private int m_nSnapNo;
|
private int m_nSnapNo;
|
||||||
private bool m_bNsfPlaying;
|
private bool m_bNsfPlaying;
|
||||||
private bool m_bNsfInit;
|
private bool m_bNsfInit;
|
||||||
@ -173,7 +177,6 @@ namespace VirtualNes.Core
|
|||||||
Debuger.Log("VirtuaNES - CSharpCore\n");
|
Debuger.Log("VirtuaNES - CSharpCore\n");
|
||||||
|
|
||||||
m_bDiskThrottle = false;
|
m_bDiskThrottle = false;
|
||||||
m_CommandRequest = 0;
|
|
||||||
|
|
||||||
m_nSnapNo = 0;
|
m_nSnapNo = 0;
|
||||||
|
|
||||||
@ -451,31 +454,6 @@ 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();
|
||||||
@ -1673,6 +1651,256 @@ namespace VirtualNes.Core
|
|||||||
return m_BarcodeOut;
|
return m_BarcodeOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public State GetState()
|
||||||
|
{
|
||||||
|
State state = new State();
|
||||||
|
|
||||||
|
//HEADER
|
||||||
|
{
|
||||||
|
state.HEADER.ID = "VirtuaNES ST";
|
||||||
|
state.HEADER.BlockVersion = 0x0200;
|
||||||
|
|
||||||
|
if (rom.GetMapperNo() != 20)
|
||||||
|
state.HEADER.Ext0 = rom.GetPROM_CRC();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.HEADER.Ext0 = rom.GetGameID();
|
||||||
|
state.HEADER.Ext1 = (ushort)rom.GetMakerID();
|
||||||
|
state.HEADER.Ext2 = (ushort)rom.GetDiskNo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//REGISTER STATE
|
||||||
|
{
|
||||||
|
state.regBLOCK.ID = "REG DATA";
|
||||||
|
state.regBLOCK.BlockVersion = 0x0210;
|
||||||
|
state.regBLOCK.BlockSize = state.reg.GetSize();
|
||||||
|
|
||||||
|
R6502 R = null;
|
||||||
|
cpu.GetContext(ref R);
|
||||||
|
|
||||||
|
state.reg.cpureg.PC = R.PC;
|
||||||
|
state.reg.cpureg.A = R.A;
|
||||||
|
state.reg.cpureg.X = R.X;
|
||||||
|
state.reg.cpureg.Y = R.Y;
|
||||||
|
state.reg.cpureg.S = R.S;
|
||||||
|
state.reg.cpureg.P = R.P;
|
||||||
|
state.reg.cpureg.I = R.INT_pending;
|
||||||
|
|
||||||
|
int cycles = 0;
|
||||||
|
apu.GetFrameIRQ(ref cycles,
|
||||||
|
ref state.reg.cpureg.FrameIRQ_count,
|
||||||
|
ref state.reg.cpureg.FrameIRQ_type,
|
||||||
|
ref state.reg.cpureg.FrameIRQ,
|
||||||
|
ref state.reg.cpureg.FrameIRQ_occur);
|
||||||
|
state.reg.cpureg.FrameIRQ_cycles = cycles; // 参照がINTな為(ぉ
|
||||||
|
|
||||||
|
state.reg.cpureg.DMA_cycles = cpu.GetDmaCycles();
|
||||||
|
state.reg.cpureg.emul_cycles = emul_cycles;
|
||||||
|
state.reg.cpureg.base_cycles = base_cycles;
|
||||||
|
|
||||||
|
// SAVE PPU STATE
|
||||||
|
state.reg.ppureg.reg0 = MMU.PPUREG[0];
|
||||||
|
state.reg.ppureg.reg1 = MMU.PPUREG[1];
|
||||||
|
state.reg.ppureg.reg2 = MMU.PPUREG[2];
|
||||||
|
state.reg.ppureg.reg3 = MMU.PPUREG[3];
|
||||||
|
state.reg.ppureg.reg7 = MMU.PPU7_Temp;
|
||||||
|
state.reg.ppureg.loopy_t = MMU.loopy_t;
|
||||||
|
state.reg.ppureg.loopy_v = MMU.loopy_v;
|
||||||
|
state.reg.ppureg.loopy_x = MMU.loopy_x;
|
||||||
|
state.reg.ppureg.toggle56 = MMU.PPU56Toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//RAM STATE
|
||||||
|
{
|
||||||
|
state.ram = RAMSTAT.GetDefault();
|
||||||
|
uint size = 0;
|
||||||
|
|
||||||
|
// SAVE RAM STATE
|
||||||
|
MemoryUtility.memcpy(state.ram.RAM, MMU.RAM, state.ram.RAM.Length);
|
||||||
|
MemoryUtility.memcpy(state.ram.BGPAL, MMU.BGPAL, state.ram.BGPAL.Length);
|
||||||
|
MemoryUtility.memcpy(state.ram.SPPAL, MMU.SPPAL, state.ram.SPPAL.Length);
|
||||||
|
MemoryUtility.memcpy(state.ram.SPRAM, MMU.SPRAM, state.ram.SPRAM.Length);
|
||||||
|
|
||||||
|
// S-RAM STATE(使用/未使用に関わらず存在すればセーブする)
|
||||||
|
if (rom.IsSAVERAM())
|
||||||
|
{
|
||||||
|
size = (uint)SAVERAM_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Header
|
||||||
|
state.ramBLOCK.ID = "RAM DATA";
|
||||||
|
state.ramBLOCK.BlockVersion = 0x0100;
|
||||||
|
state.ramBLOCK.BlockSize = size + state.ram.GetSize();
|
||||||
|
|
||||||
|
if (rom.IsSAVERAM())
|
||||||
|
{
|
||||||
|
state.WRAM = new byte[SAVERAM_SIZE];
|
||||||
|
Array.Copy(MMU.WRAM, state.WRAM, SAVERAM_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//BANK STATE
|
||||||
|
{
|
||||||
|
state.mmu = MMUSTAT.GetDefault();
|
||||||
|
uint size = 0;
|
||||||
|
|
||||||
|
// SAVE CPU MEMORY BANK DATA
|
||||||
|
// BANK0,1,2はバンクセーブに関係なし
|
||||||
|
// VirtuaNES0.30から
|
||||||
|
// バンク3はSRAM使用に関わらずセーブ
|
||||||
|
for (int i = 3; i < 8; i++)
|
||||||
|
{
|
||||||
|
state.mmu.CPU_MEM_TYPE[i] = MMU.CPU_MEM_TYPE[i];
|
||||||
|
state.mmu.CPU_MEM_PAGE[i] = (ushort)MMU.CPU_MEM_PAGE[i];
|
||||||
|
|
||||||
|
if (MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_RAM
|
||||||
|
|| MMU.CPU_MEM_TYPE[i] == MMU.BANKTYPE_DRAM)
|
||||||
|
{
|
||||||
|
size += 8 * 1024; // 8K BANK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAVE VRAM MEMORY DATA
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
state.mmu.PPU_MEM_TYPE[i] = MMU.PPU_MEM_TYPE[i];
|
||||||
|
state.mmu.PPU_MEM_PAGE[i] = (ushort)MMU.PPU_MEM_PAGE[i];
|
||||||
|
}
|
||||||
|
size += 4 * 1024; // 1K BANK x 4 (VRAM)
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
state.mmu.CRAM_USED[i] = MMU.CRAM_USED[i];
|
||||||
|
if (MMU.CRAM_USED[i] != 0)
|
||||||
|
{
|
||||||
|
size += 4 * 1024; // 4K BANK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Header
|
||||||
|
state.mmuBLOCK.ID = "MMU DATA";
|
||||||
|
state.mmuBLOCK.BlockVersion = 0x0200;
|
||||||
|
state.mmuBLOCK.BlockSize = size + state.mmu.GetSize();
|
||||||
|
|
||||||
|
state.CPU_MEM_BANK = new List<byte>();
|
||||||
|
// WRITE CPU RAM MEMORY BANK
|
||||||
|
for (int i = 3; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM)
|
||||||
|
{
|
||||||
|
state.CPU_MEM_BANK.AddRange(MMU.CPU_MEM_BANK[i].Span(0, 8 * 1024).ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WRITE VRAM MEMORY(常に4K分すべて書き込む)
|
||||||
|
state.VRAM = new byte[4 * 1024];
|
||||||
|
Array.Copy(MMU.VRAM, state.VRAM, state.VRAM.Length);
|
||||||
|
|
||||||
|
state.CRAM = new List<byte>();
|
||||||
|
// WRITE CRAM MEMORY
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if (MMU.CRAM_USED[i] != 0)
|
||||||
|
{
|
||||||
|
var bytes = new byte[4 * 1024];
|
||||||
|
Array.Copy(MMU.CRAM, 0x1000 * i, bytes, 0, bytes.Length);
|
||||||
|
state.CRAM.AddRange(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MMC STATE
|
||||||
|
{
|
||||||
|
state.mmc = MMCSTAT.GetDefault();
|
||||||
|
|
||||||
|
// Create Header
|
||||||
|
state.mmcBLOCK.ID = "MMC DATA";
|
||||||
|
state.mmcBLOCK.BlockVersion = 0x0100;
|
||||||
|
state.mmcBLOCK.BlockSize = state.mmc.GetSize();
|
||||||
|
|
||||||
|
if (mapper.IsStateSave())
|
||||||
|
{
|
||||||
|
mapper.SaveState(state.mmc.mmcdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//CONTROLLER STATE
|
||||||
|
{
|
||||||
|
// Create Header
|
||||||
|
state.ctrBLOCK.ID = "CTR DATA";
|
||||||
|
state.ctrBLOCK.BlockVersion = 0x0100;
|
||||||
|
state.ctrBLOCK.BlockSize = state.ctr.GetSize();
|
||||||
|
|
||||||
|
state.ctr.pad1bit = pad.pad1bit;
|
||||||
|
state.ctr.pad2bit = pad.pad2bit;
|
||||||
|
state.ctr.pad3bit = pad.pad3bit;
|
||||||
|
state.ctr.pad4bit = pad.pad4bit;
|
||||||
|
state.ctr.strobe = (byte)(pad.GetStrobe() ? 0xFF : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//SND STATE
|
||||||
|
{
|
||||||
|
state.snd = SNDSTAT.GetDefault();
|
||||||
|
|
||||||
|
// Create Header
|
||||||
|
state.sndBLOCK.ID = "SND DATA";
|
||||||
|
state.sndBLOCK.BlockVersion = 0x0100;
|
||||||
|
state.sndBLOCK.BlockSize = state.snd.GetSize();
|
||||||
|
|
||||||
|
StateBuffer buffer = new StateBuffer();
|
||||||
|
apu.SaveState(buffer);
|
||||||
|
Array.Copy(buffer.Data.ToArray(), state.snd.snddata, buffer.Data.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DISKIMAGE STATE
|
||||||
|
if (rom.GetMapperNo() == 20)
|
||||||
|
{
|
||||||
|
var lpDisk = rom.GetPROM();
|
||||||
|
var lpWrite = rom.GetDISK();
|
||||||
|
int DiskSize = 16 + 65500 * rom.GetDiskNo();
|
||||||
|
|
||||||
|
|
||||||
|
// 相違数をカウント
|
||||||
|
for (int i = 16; i < DiskSize; i++)
|
||||||
|
{
|
||||||
|
if (lpWrite[i] != 0)
|
||||||
|
state.dsk.DifferentSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.dskBLOCK.ID = "DISKDATA";
|
||||||
|
state.dskBLOCK.BlockVersion = 0x0210;
|
||||||
|
state.dskBLOCK.BlockSize = 0;
|
||||||
|
|
||||||
|
for (int i = 16; i < DiskSize; i++)
|
||||||
|
{
|
||||||
|
if (lpWrite[i] != 0)
|
||||||
|
{
|
||||||
|
state.dskdata = (uint)(i & 0x00FFFFFF);
|
||||||
|
state.dskdata |= ((uint)lpDisk[i] & 0xFF) << 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EXCTR STATE
|
||||||
|
if (pad.GetExController() != 0)
|
||||||
|
{
|
||||||
|
state.exctrBLOCK.ID = "EXCTRDAT";
|
||||||
|
state.exctrBLOCK.BlockVersion = 0x0100;
|
||||||
|
state.exctrBLOCK.BlockSize = state.exctr.GetSize();
|
||||||
|
|
||||||
|
// Some excontrollers will default 0
|
||||||
|
state.exctr.data = pad.GetSyncExData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LoadState(State state)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public enum IRQMETHOD
|
public enum IRQMETHOD
|
||||||
{
|
{
|
||||||
IRQ_HSYNC = 0, IRQ_CLOCK = 1
|
IRQ_HSYNC = 0, IRQ_CLOCK = 1
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace VirtualNes.Core
|
namespace VirtualNes.Core
|
||||||
{
|
{
|
||||||
@ -507,6 +509,68 @@ namespace VirtualNes.Core
|
|||||||
{
|
{
|
||||||
return excontroller_select;
|
return excontroller_select;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal bool GetStrobe()
|
||||||
|
{
|
||||||
|
return bStrobe;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal uint GetSyncExData()
|
||||||
|
{
|
||||||
|
uint data = 0;
|
||||||
|
|
||||||
|
switch ((EXCONTROLLER)excontroller_select)
|
||||||
|
{
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_ZAPPER:
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_PADDLE:
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN:
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_OEKAKIDS_TABLET:
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_VSZAPPER:
|
||||||
|
{
|
||||||
|
int x, y;
|
||||||
|
x = expad.GetSyncData(0);
|
||||||
|
y = expad.GetSyncData(1);
|
||||||
|
if (x == -1 || y == -1)
|
||||||
|
{
|
||||||
|
data = 0x80000000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = (uint)((x & 0xFF) | ((y & 0xFF) << 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (excontroller_select != (int)EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN)
|
||||||
|
{
|
||||||
|
if (expad.GetSyncData(2) != 0)
|
||||||
|
data |= 0x0010000;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data |= (uint)(expad.GetSyncData(2) << 16);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER:
|
||||||
|
data = (uint)expad.GetSyncData(0);
|
||||||
|
break;
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_TOPRIDER:
|
||||||
|
data = (uint)expad.GetSyncData(0);
|
||||||
|
break;
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A:
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B:
|
||||||
|
data = (uint)expad.GetSyncData(0);
|
||||||
|
break;
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING:
|
||||||
|
data = (uint)expad.GetSyncData(0);
|
||||||
|
break;
|
||||||
|
case EXCONTROLLER.EXCONTROLLER_MAHJANG:
|
||||||
|
data = (uint)expad.GetSyncData(0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum VSType
|
public enum VSType
|
||||||
|
8
AxibugEmuOnline.Client/Assets/VirtualNes.Core/State.meta
Normal file
8
AxibugEmuOnline.Client/Assets/VirtualNes.Core/State.meta
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 915f7790a5c8e34469efbee412b66d3a
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,23 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct BLOCKHDR : IStateBufferObject
|
||||||
|
{
|
||||||
|
public string ID;
|
||||||
|
public ushort Reserved;
|
||||||
|
public ushort BlockVersion;
|
||||||
|
public uint BlockSize;
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(ID);
|
||||||
|
buffer.Write(Reserved);
|
||||||
|
buffer.Write(BlockVersion);
|
||||||
|
buffer.Write(BlockSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)(ID.Length + sizeof(ushort) + sizeof(ushort) + sizeof(uint));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: d7e8126382c9728429056ba33afc85eb
|
guid: 4c00b92c189cbc841a1f8e26749ba6df
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
@ -0,0 +1,28 @@
|
|||||||
|
using Codice.CM.Client.Differences;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct CTRSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public uint pad1bit;
|
||||||
|
public uint pad2bit;
|
||||||
|
public uint pad3bit;
|
||||||
|
public uint pad4bit;
|
||||||
|
public byte strobe;
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return sizeof(uint) * 4 + sizeof(byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(pad1bit);
|
||||||
|
buffer.Write(pad2bit);
|
||||||
|
buffer.Write(pad3bit);
|
||||||
|
buffer.Write(pad4bit);
|
||||||
|
buffer.Write(strobe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: be39d1ba53756fc4fa9cbdc7d7fc34a2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct DISKDATA
|
||||||
|
{
|
||||||
|
public int DifferentSize;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f364c4c9681ea6b4fad0c68f42f9b230
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,17 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct EXCTRSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public uint data;
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return sizeof(uint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ed6b363609705fd41a70255c4bbc7dd2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct FILEHDR2 : IStateBufferObject
|
||||||
|
{
|
||||||
|
public string ID;
|
||||||
|
/// <summary> 2字节 </summary>
|
||||||
|
public ushort BlockVersion;
|
||||||
|
/// <summary> 4字节 </summary>
|
||||||
|
public uint Ext0;
|
||||||
|
/// <summary> 2字节 </summary>
|
||||||
|
public ushort Ext1;
|
||||||
|
/// <summary> 2字节 </summary>
|
||||||
|
public ushort Ext2;
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(ID);
|
||||||
|
buffer.Write(BlockVersion);
|
||||||
|
buffer.Write(Ext1);
|
||||||
|
buffer.Write(Ext2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)(ID.Length + sizeof(ushort) + sizeof(uint) + sizeof(ushort) + sizeof(ushort));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 86729820f1e0c0d4cbd58955e1238fd6
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,22 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct MMCSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public byte[] mmcdata;
|
||||||
|
|
||||||
|
public static MMCSTAT GetDefault()
|
||||||
|
{
|
||||||
|
return new MMCSTAT() { mmcdata = new byte[256] };
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)mmcdata.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(mmcdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 61532b9db0674494099449267f13b2c5
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,38 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct MMUSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public byte[] CPU_MEM_TYPE;
|
||||||
|
public ushort[] CPU_MEM_PAGE;
|
||||||
|
public byte[] PPU_MEM_TYPE;
|
||||||
|
public ushort[] PPU_MEM_PAGE;
|
||||||
|
public byte[] CRAM_USED;
|
||||||
|
|
||||||
|
public static MMUSTAT GetDefault()
|
||||||
|
{
|
||||||
|
var res = new MMUSTAT();
|
||||||
|
|
||||||
|
res.CPU_MEM_TYPE = new byte[8];
|
||||||
|
res.CPU_MEM_PAGE = new ushort[8];
|
||||||
|
res.PPU_MEM_TYPE = new byte[12];
|
||||||
|
res.PPU_MEM_PAGE = new ushort[12];
|
||||||
|
res.CRAM_USED = new byte[8];
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)(CPU_MEM_TYPE.Length + CPU_MEM_PAGE.Length + PPU_MEM_TYPE.Length + PPU_MEM_PAGE.Length + CRAM_USED.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(CPU_MEM_TYPE);
|
||||||
|
buffer.Write(CPU_MEM_PAGE);
|
||||||
|
buffer.Write(PPU_MEM_TYPE);
|
||||||
|
buffer.Write(PPU_MEM_PAGE);
|
||||||
|
buffer.Write(CRAM_USED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: bae9e1d800e0de741b04ec6979bc086f
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct RAMSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
/// <summary> Internal NES RAM </summary>
|
||||||
|
public byte[] RAM;
|
||||||
|
/// <summary> BG Palette </summary>
|
||||||
|
public byte[] BGPAL;
|
||||||
|
/// <summary> SP Palette </summary>
|
||||||
|
public byte[] SPPAL;
|
||||||
|
/// <summary> Sprite RAM </summary>
|
||||||
|
public byte[] SPRAM;
|
||||||
|
|
||||||
|
public static RAMSTAT GetDefault()
|
||||||
|
{
|
||||||
|
var res = new RAMSTAT();
|
||||||
|
res.RAM = new byte[2 * 1024];
|
||||||
|
res.BGPAL = new byte[16];
|
||||||
|
res.SPPAL = new byte[16];
|
||||||
|
res.SPRAM = new byte[256];
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)(RAM.Length + BGPAL.Length + SPPAL.Length + SPRAM.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(RAM);
|
||||||
|
buffer.Write(BGPAL);
|
||||||
|
buffer.Write(SPPAL);
|
||||||
|
buffer.Write(SPRAM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1acd6e5360be08b4e88b477d7983aa1d
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,96 @@
|
|||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct REGSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public CPUSTAT cpureg;
|
||||||
|
public PPUSTAT ppureg;
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
cpureg.SaveState(buffer);
|
||||||
|
ppureg.SaveState(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return cpureg.GetSize() + ppureg.GetSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CPUSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public ushort PC;
|
||||||
|
public byte A;
|
||||||
|
public byte X;
|
||||||
|
public byte Y;
|
||||||
|
public byte S;
|
||||||
|
public byte P;
|
||||||
|
public byte I; // Interrupt pending flag
|
||||||
|
|
||||||
|
public byte FrameIRQ;
|
||||||
|
public byte FrameIRQ_occur;
|
||||||
|
public byte FrameIRQ_count;
|
||||||
|
public byte FrameIRQ_type;
|
||||||
|
public int FrameIRQ_cycles;
|
||||||
|
public int DMA_cycles;
|
||||||
|
|
||||||
|
public long emul_cycles;
|
||||||
|
public long base_cycles;
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(PC);
|
||||||
|
buffer.Write(A);
|
||||||
|
buffer.Write(X);
|
||||||
|
buffer.Write(Y);
|
||||||
|
buffer.Write(S);
|
||||||
|
buffer.Write(P);
|
||||||
|
buffer.Write(I);
|
||||||
|
buffer.Write(FrameIRQ);
|
||||||
|
buffer.Write(FrameIRQ_occur);
|
||||||
|
buffer.Write(FrameIRQ_count);
|
||||||
|
buffer.Write(FrameIRQ_type);
|
||||||
|
buffer.Write(FrameIRQ_cycles);
|
||||||
|
buffer.Write(DMA_cycles);
|
||||||
|
buffer.Write(emul_cycles);
|
||||||
|
buffer.Write(base_cycles);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct PPUSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public byte reg0;
|
||||||
|
public byte reg1;
|
||||||
|
public byte reg2;
|
||||||
|
public byte reg3;
|
||||||
|
public byte reg7;
|
||||||
|
public byte toggle56;
|
||||||
|
|
||||||
|
public ushort loopy_t;
|
||||||
|
public ushort loopy_v;
|
||||||
|
public ushort loopy_x;
|
||||||
|
|
||||||
|
public readonly uint GetSize()
|
||||||
|
{
|
||||||
|
return 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(reg0);
|
||||||
|
buffer.Write(reg1);
|
||||||
|
buffer.Write(reg2);
|
||||||
|
buffer.Write(reg3);
|
||||||
|
buffer.Write(reg7);
|
||||||
|
buffer.Write(toggle56);
|
||||||
|
buffer.Write(loopy_t);
|
||||||
|
buffer.Write(loopy_v);
|
||||||
|
buffer.Write(loopy_x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1350a5cfb70265848b3af2a05a2709eb
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct SNDSTAT : IStateBufferObject
|
||||||
|
{
|
||||||
|
public byte[] snddata;
|
||||||
|
|
||||||
|
public static SNDSTAT GetDefault()
|
||||||
|
{
|
||||||
|
return new SNDSTAT() { snddata = new byte[0x800] };
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint GetSize()
|
||||||
|
{
|
||||||
|
return (uint)snddata.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SaveState(StateBuffer buffer)
|
||||||
|
{
|
||||||
|
buffer.Write(snddata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1906102fbfecacf4a988a89e6d373c10
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
39
AxibugEmuOnline.Client/Assets/VirtualNes.Core/State/State.cs
Normal file
39
AxibugEmuOnline.Client/Assets/VirtualNes.Core/State/State.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public struct State
|
||||||
|
{
|
||||||
|
public FILEHDR2 HEADER;
|
||||||
|
|
||||||
|
public BLOCKHDR regBLOCK;
|
||||||
|
public REGSTAT reg;
|
||||||
|
|
||||||
|
public BLOCKHDR ramBLOCK;
|
||||||
|
public RAMSTAT ram;
|
||||||
|
/// <summary> Maybe null cause by rom IsSaveRAM() </summary>
|
||||||
|
public byte[] WRAM;
|
||||||
|
|
||||||
|
public BLOCKHDR mmuBLOCK;
|
||||||
|
public MMUSTAT mmu;
|
||||||
|
public List<byte> CPU_MEM_BANK;
|
||||||
|
public byte[] VRAM;
|
||||||
|
public List<byte> CRAM;
|
||||||
|
|
||||||
|
public BLOCKHDR mmcBLOCK;
|
||||||
|
public MMCSTAT mmc;
|
||||||
|
|
||||||
|
public BLOCKHDR ctrBLOCK;
|
||||||
|
public CTRSTAT ctr;
|
||||||
|
|
||||||
|
public BLOCKHDR sndBLOCK;
|
||||||
|
public SNDSTAT snd;
|
||||||
|
|
||||||
|
public BLOCKHDR dskBLOCK;
|
||||||
|
public DISKDATA dsk;
|
||||||
|
public uint dskdata;
|
||||||
|
|
||||||
|
public BLOCKHDR exctrBLOCK;
|
||||||
|
public EXCTRSTAT exctr;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b685425b0082cf246bf59e849aef0d0b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -0,0 +1,88 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace VirtualNes.Core
|
||||||
|
{
|
||||||
|
public class StateBuffer
|
||||||
|
{
|
||||||
|
public List<byte> Data = new List<byte>();
|
||||||
|
|
||||||
|
public long Position
|
||||||
|
{
|
||||||
|
get => Data.Count - 1;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
var gap = value - Position;
|
||||||
|
if (gap > 0)
|
||||||
|
{
|
||||||
|
Data.AddRange(new byte[gap]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Data.RemoveRange((int)Position, (int)gap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(byte[] bytes)
|
||||||
|
{
|
||||||
|
Data.AddRange(bytes);
|
||||||
|
}
|
||||||
|
public void Write(sbyte[] sbytes)
|
||||||
|
{
|
||||||
|
foreach(var value in sbytes)
|
||||||
|
{
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Write(byte[] bytes, int length)
|
||||||
|
{
|
||||||
|
Data.AddRange(bytes);
|
||||||
|
}
|
||||||
|
public void Write(byte value)
|
||||||
|
{
|
||||||
|
Data.Add(value);
|
||||||
|
}
|
||||||
|
public void Write(ushort[] values)
|
||||||
|
{
|
||||||
|
foreach (var value in values)
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
public void Write(int[] values)
|
||||||
|
{
|
||||||
|
foreach (var value in values)
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
public void Write(string value)
|
||||||
|
{
|
||||||
|
Write(Encoding.ASCII.GetBytes(value));
|
||||||
|
}
|
||||||
|
public void Write(double value)
|
||||||
|
{
|
||||||
|
Write(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
public void Write(ushort value)
|
||||||
|
{
|
||||||
|
Write(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
public void Write(int value)
|
||||||
|
{
|
||||||
|
Write(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
public void Write(sbyte value)
|
||||||
|
{
|
||||||
|
Write(value);
|
||||||
|
}
|
||||||
|
public void Write(long value)
|
||||||
|
{
|
||||||
|
Write(BitConverter.GetBytes(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IStateBufferObject
|
||||||
|
{
|
||||||
|
uint GetSize();
|
||||||
|
void SaveState(StateBuffer buffer);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f215860b2526aa04a819da176e190437
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
@ -658,7 +658,7 @@ PlayerSettings:
|
|||||||
additionalCompilerArguments: {}
|
additionalCompilerArguments: {}
|
||||||
platformArchitecture: {}
|
platformArchitecture: {}
|
||||||
scriptingBackend:
|
scriptingBackend:
|
||||||
Android: 1
|
Android: 0
|
||||||
Standalone: 1
|
Standalone: 1
|
||||||
il2cppCompilerConfiguration: {}
|
il2cppCompilerConfiguration: {}
|
||||||
managedStrippingLevel: {}
|
managedStrippingLevel: {}
|
||||||
|
Loading…
Reference in New Issue
Block a user