实现NesCore LoadState和SaveState

This commit is contained in:
ALIENJACK\alien 2024-09-13 17:39:48 +08:00
parent fea833734d
commit 12c02781a0
31 changed files with 918 additions and 31 deletions

View File

@ -359,7 +359,7 @@ namespace AxibugEmuOnline.Client.Manager
void RecvHostSyn_RoomFrameAllInputData(byte[] reqData)
{
Protobuf_Room_Syn_RoomFrameAllInputData msg = ProtoBufHelper.DeSerizlize<Protobuf_Room_Syn_RoomFrameAllInputData>(reqData);
netReplay.InData(new ReplayStep() { FrameStartID = (int)msg.FrameID, InPut = msg.InputData }, (int)msg.ServerFrameID);
//netReplay.InData(new ReplayStep() { FrameStartID = (int)msg.FrameID, InPut = msg.InputData }, (int)msg.ServerFrameID);
}
public void SendScreen(byte[] RenderBuffer)

View File

@ -5,15 +5,16 @@ namespace AxibugEmuOnline.Client
{
public class InGameUI : CommandExecuter
{
public static InGameUI Instance { get; private set; }
public RomFile RomFile => m_rom;
public override bool Enable => gameObject.activeInHierarchy;
private RomFile m_rom;
private object m_core;
private object m_state;
private InGameUI_SaveState m_saveMenu;
private InGameUI_SaveState m_saveStateMenu;
private InGameUI_LoadState m_loadStateMenu;
protected override void Awake()
{
@ -27,15 +28,47 @@ namespace AxibugEmuOnline.Client
Instance = null;
}
/// <summary>
/// 获取模拟器核心对象
/// </summary>
/// <typeparam name="T">模拟器核心对象类型</typeparam>
public T GetCore<T>() => (T)m_core;
/// <summary> 保存快速快照 </summary>
public void SaveQuickState(object state)
{
m_state = state;
}
/// <summary>
/// 读取快速快照
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public bool GetQuickState<T>(out T state)
{
state = default(T);
if (m_state is T)
{
state = (T)m_state;
return true;
}
else
{
return false;
}
}
public void Show(RomFile currentRom, object core)
{
m_saveMenu = new InGameUI_SaveState(this);
CommandDispatcher.Instance.RegistController(this);
m_saveStateMenu = new InGameUI_SaveState(this);
m_loadStateMenu = new InGameUI_LoadState(this);
m_rom = currentRom;
m_core = core;
gameObject.SetActiveEx(true);
}
@ -43,14 +76,12 @@ namespace AxibugEmuOnline.Client
{
CommandDispatcher.Instance.UnRegistController(this);
m_rom = null;
m_core = null;
gameObject.SetActiveEx(false);
}
protected override void OnCmdOptionMenu()
{
OptionUI.Instance.Pop(new List<OptionMenu> { m_saveMenu });
OptionUI.Instance.Pop(new List<OptionMenu> { m_saveStateMenu, m_loadStateMenu });
}
}
}

View File

@ -0,0 +1,32 @@
using AxibugEmuOnline.Client.ClientCore;
using System.Diagnostics;
using VirtualNes.Core;
namespace AxibugEmuOnline.Client
{
public class InGameUI_LoadState : ExecuteMenu
{
private InGameUI m_gameUI;
public InGameUI_LoadState(InGameUI gameUI) : base("śÁČĄżěŐŐ", null)
{
m_gameUI = gameUI;
}
public override void OnExcute()
{
Stopwatch sw = Stopwatch.StartNew();
switch (m_gameUI.RomFile.Platform)
{
case EnumPlatform.NES:
if (m_gameUI.GetQuickState<State>(out var quickState))
{
m_gameUI.GetCore<NesEmulator>().NesCore.LoadState(quickState);
}
break;
}
sw.Stop();
App.log.Info($"{m_gameUI.RomFile.Platform}====>żěŐŐźÓÔŘşÄĘą:{sw.Elapsed.TotalMilliseconds}ms");
}
}
}

View File

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

View File

@ -19,6 +19,7 @@ namespace AxibugEmuOnline.Client
{
case EnumPlatform.NES:
var state = m_gameUI.GetCore<NesEmulator>().NesCore.GetState();
m_gameUI.SaveQuickState(state);
App.log.Info($"{m_gameUI.RomFile.Platform}===>快照大小{state.ToBytes().Length}");
break;
}

View File

@ -168,6 +168,9 @@ namespace AxibugEmuOnline.Client
{
public string Name { get; protected set; }
public Sprite Icon { get; protected set; }
public virtual bool Visible => true;
public virtual bool Enable => true;
public OptionMenu(string name, Sprite icon = null)
{
Name = name;

View File

@ -543,6 +543,11 @@ namespace VirtualNes.Core
@internal.GetFrameIRQ(ref Cycle, ref Count, ref Type, ref IRQ, ref Occur);
}
internal void SetFrameIRQ(int Cycle, byte Count, byte Type, byte IRQ, byte Occur)
{
@internal.SetFrameIRQ(Cycle, Count, Type, IRQ, Occur);
}
internal void SaveState(StateBuffer buffer)
{
// 時間軸を同期させる為Flushする
@ -579,7 +584,7 @@ namespace VirtualNes.Core
// N106
if ((exsound_select & 0x10) != 0)
{
n106.SaveState(buffer);
n106.SaveState(buffer);
buffer.Position += (n106.GetSize() + 15) & (~0x0F); // Padding
}
// FME7
@ -589,6 +594,49 @@ namespace VirtualNes.Core
buffer.Position += (fme7.GetSize() + 15) & (~0x0F); // Padding
}
}
internal void LoadState(StateReader buffer)
{
@internal.LoadState(buffer);
buffer.Skip((@internal.GetSize() + 15) & (~0x0F));
// VRC6
if ((exsound_select & 0x01) != 0)
{
vrc6.LoadState(buffer);
buffer.Skip((int)((vrc6.GetSize() + 15) & (~0x0F))); // Padding
}
// VRC7 (not support)
if ((exsound_select & 0x02) != 0)
{
vrc7.LoadState(buffer);
buffer.Skip((vrc7.GetSize() + 15) & (~0x0F)); // Padding
}
// FDS
if ((exsound_select & 0x04) != 0)
{
fds.LoadState(buffer);
buffer.Skip((fds.GetSize() + 15) & (~0x0F)); // Padding
}
// MMC5
if ((exsound_select & 0x08) != 0)
{
mmc5.LoadState(buffer);
buffer.Skip((mmc5.GetSize() + 15) & (~0x0F)); // Padding
}
// N106
if ((exsound_select & 0x10) != 0)
{
n106.LoadState(buffer);
buffer.Skip((n106.GetSize() + 15) & (~0x0F)); // Padding
}
// FME7
if ((exsound_select & 0x20) != 0)
{
fme7.LoadState(buffer);
buffer.Skip((fme7.GetSize() + 15) & (~0x0F)); // Padding
}
}
}
public struct QUEUEDATA

View File

@ -514,6 +514,36 @@ namespace VirtualNes.Core
buffer.Write(now_freq);
buffer.Write(output);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(0x80);
volenv_mode = buffer.Read_byte();
volenv_gain = buffer.Read_byte();
volenv_decay = buffer.Read_byte();
volenv_phaseacc = buffer.Read_double();
swpenv_mode = buffer.Read_byte();
swpenv_gain = buffer.Read_byte();
swpenv_decay = buffer.Read_byte();
swpenv_phaseacc = buffer.Read_double();
envelope_enable = buffer.Read_byte();
envelope_speed = buffer.Read_byte();
wave_setup = buffer.Read_byte();
master_volume = buffer.Read_int();
main_wavetable = buffer.Read_ints(64);
main_enable = buffer.Read_byte();
main_frequency = buffer.Read_int();
main_addr = buffer.Read_int();
lfo_wavetable = buffer.Read_bytes(64);
lfo_enable = buffer.Read_byte();
lfo_frequency = buffer.Read_int();
lfo_addr = buffer.Read_int();
lfo_phaseacc = buffer.Read_double();
sweep_bias = buffer.Read_int();
now_volume = buffer.Read_int();
now_freq = buffer.Read_int();
output = buffer.Read_int();
}
}
}
}

View File

@ -398,6 +398,17 @@ namespace VirtualNes.Core
buffer.Write(envtbl_index);
buffer.Write(envstep_index);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(3);
volume = buffer.Read_byte();
freq = buffer.Read_int();
phaseacc = buffer.Read_int();
envadr = buffer.Read_int();
envtbl_index = buffer.Read_byte();
envstep_index = buffer.Read_byte();
}
}
public class NOISE : IStateBufferObject
@ -424,6 +435,14 @@ namespace VirtualNes.Core
buffer.Write(noiserange);
buffer.Write(noiseout);
}
public void LoadState(StateReader buffer)
{
freq = buffer.Read_int();
phaseacc = buffer.Read_int();
noiserange = buffer.Read_int();
noiseout = buffer.Read_byte();
}
}
public class CHANNEL : IStateBufferObject
@ -469,6 +488,19 @@ namespace VirtualNes.Core
buffer.Write(phaseacc);
buffer.Write(output_vol);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(3);
enable = buffer.Read_byte();
env_on = buffer.Read_byte();
noise_on = buffer.Read_byte();
adder = buffer.Read_byte();
volume = buffer.Read_byte();
freq = buffer.Read_int();
phaseacc = buffer.Read_int();
output_vol = buffer.Read_int();
}
}
}
}

View File

@ -23,7 +23,7 @@ namespace VirtualNes.Core
public virtual bool Sync(int cycles) { return false; }
public virtual int GetFreq(int channel) { return 0; }
public virtual void SaveState(StateBuffer buffer) { }
public virtual void LoadState(byte[] p) { }
public virtual void LoadState(StateReader buffer) { }
public static int INT2FIX(int x)
{

View File

@ -1203,6 +1203,15 @@ namespace VirtualNes.Core
occur = FrameIRQoccur;
}
internal void SetFrameIRQ(int cycle, byte count, byte type, byte IRQ, byte occur)
{
FrameCycle = cycle;
FrameCount = count;
FrameType = type;
FrameIRQ = IRQ;
FrameIRQoccur = occur;
}
public override uint GetSize()
{
return sizeof(byte) +
@ -1219,7 +1228,7 @@ namespace VirtualNes.Core
ch4.GetSize();
}
public unsafe override void SaveState(StateBuffer p)
public override void SaveState(StateBuffer p)
{
p.Write(reg4015);
p.Write(sync_reg4015);
@ -1352,6 +1361,39 @@ namespace VirtualNes.Core
buffer.Write(dummy2);
buffer.Write(sync_len_count);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
holdnote = buffer.Read_byte();
volume = buffer.Read_byte();
complement = buffer.Read_byte();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
freqlimit = buffer.Read_int();
adder = buffer.Read_int();
duty = buffer.Read_int();
len_count = buffer.Read_int();
nowvolume = buffer.Read_int();
env_fixed = buffer.Read_byte();
env_decay = buffer.Read_byte();
env_count = buffer.Read_byte();
dummy0 = buffer.Read_byte();
env_vol = buffer.Read_int();
swp_on = buffer.Read_byte();
swp_inc = buffer.Read_byte();
swp_shift = buffer.Read_byte();
swp_decay = buffer.Read_byte();
swp_count = buffer.Read_byte();
dummy1 = buffer.Read_bytes(3);
sync_reg = buffer.Read_bytes(4);
sync_output_enable = buffer.Read_byte();
sync_enable = buffer.Read_byte();
sync_holdnote = buffer.Read_byte();
dummy2 = buffer.Read_byte();
sync_len_count = buffer.Read_int();
}
}
public class TRIANGLE : IStateBufferObject
{
@ -1428,6 +1470,27 @@ namespace VirtualNes.Core
buffer.Write(sync_len_count);
buffer.Write(sync_lin_count);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
holdnote = buffer.Read_byte();
counter_start = buffer.Read_byte();
dummy0 = buffer.Read_byte();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
len_count = buffer.Read_int();
lin_count = buffer.Read_int();
adder = buffer.Read_int();
nowvolume = buffer.Read_int();
sync_reg = buffer.Read_bytes(4);
sync_enable = buffer.Read_byte();
sync_holdnote = buffer.Read_byte();
sync_counter_start = buffer.Read_byte();
sync_len_count = buffer.Read_int();
sync_lin_count = buffer.Read_int();
}
}
public class DPCM : IStateBufferObject
{
@ -1487,6 +1550,35 @@ namespace VirtualNes.Core
buffer.Write(sync_dmalength);
buffer.Write(sync_cache_dmalength);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
looping = buffer.Read_byte();
cur_byte = buffer.Read_byte();
dpcm_value = buffer.Read_byte();
freq = buffer.Read_int();
phaseacc = buffer.Read_int();
output = buffer.Read_int();
address = buffer.Read_ushort();
cache_addr = buffer.Read_ushort();
dmalength = buffer.Read_int();
cache_dmalength = buffer.Read_int();
dpcm_output_real = buffer.Read_int();
dpcm_output_fake = buffer.Read_int();
dpcm_output_old = buffer.Read_int();
dpcm_output_offset = buffer.Read_int();
sync_reg = buffer.Read_bytes(4);
sync_enable = buffer.Read_byte();
sync_looping = buffer.Read_byte();
sync_irq_gen = buffer.Read_byte();
sync_irq_enable = buffer.Read_byte();
sync_cycles = buffer.Read_int();
sync_cache_cycles = buffer.Read_int();
sync_dmalength = buffer.Read_int();
sync_cache_dmalength = buffer.Read_int();
}
}
public class NOISE : IStateBufferObject
{
@ -1582,6 +1674,32 @@ namespace VirtualNes.Core
buffer.Write(dummy1);
buffer.Write(sync_len_count);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
holdnote = buffer.Read_byte();
volume = buffer.Read_byte();
xor_tap = buffer.Read_byte();
shift_reg = buffer.Read_int();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
len_count = buffer.Read_int();
nowvolume = buffer.Read_int();
output = buffer.Read_int();
env_fixed = buffer.Read_byte();
env_decay = buffer.Read_byte();
env_count = buffer.Read_byte();
dummy0 = buffer.Read_byte();
env_vol = buffer.Read_int();
sync_reg = buffer.Read_bytes(4);
sync_output_enable = buffer.Read_byte();
sync_enable = buffer.Read_byte();
sync_holdnote = buffer.Read_byte();
dummy1 = buffer.Read_byte();
sync_len_count = buffer.Read_int();
}
}
}
}

View File

@ -421,6 +421,15 @@ namespace VirtualNes.Core
buffer.Write(dummy);
buffer.Write(vbl_length);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
holdnote = buffer.Read_byte();
dummy = buffer.Read_bytes(2);
vbl_length = buffer.Read_int();
}
}
public class RECTANGLE : IStateBufferObject
@ -467,6 +476,24 @@ namespace VirtualNes.Core
buffer.Write(adder);
buffer.Write(duty_flip);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(4);
enable = buffer.Read_byte();
vbl_length = buffer.Read_int();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
output_vol = buffer.Read_int();
fixed_envelope = buffer.Read_byte();
holdnote = buffer.Read_byte();
volume = buffer.Read_byte();
env_vol = buffer.Read_byte();
env_phase = buffer.Read_int();
env_decay = buffer.Read_int();
adder = buffer.Read_int();
duty_flip = buffer.Read_int();
}
}
}
}

View File

@ -243,6 +243,19 @@ namespace VirtualNes.Core
buffer.Write(vol);
buffer.Write(databuf);
}
public void LoadState(StateReader buffer)
{
phaseacc = buffer.Read_int();
freq = buffer.Read_uint();
phase = buffer.Read_uint();
tonelen = buffer.Read_uint();
output = buffer.Read_int();
toneadr = buffer.Read_byte();
volupdate = buffer.Read_byte();
vol = buffer.Read_byte();
databuf = buffer.Read_byte();
}
}
}
}

View File

@ -304,6 +304,19 @@ namespace VirtualNes.Core
buffer.Write(adder);
buffer.Write(duty_pos);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(3);
enable = buffer.Read_byte();
gate = buffer.Read_byte();
volume = buffer.Read_byte();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
output_vol = buffer.Read_int();
adder = buffer.Read_byte();
duty_pos = buffer.Read_byte();
}
}
public class SAWTOOTH : IStateBufferObject
@ -353,6 +366,19 @@ namespace VirtualNes.Core
buffer.Write(accum);
buffer.Write(phaseaccum);
}
public void LoadState(StateReader buffer)
{
reg = buffer.Read_bytes(3);
enable = buffer.Read_byte();
volume = buffer.Read_byte();
phaseacc = buffer.Read_int();
freq = buffer.Read_int();
output_vol = buffer.Read_int();
adder = buffer.Read_byte();
accum = buffer.Read_byte();
phaseaccum = buffer.Read_byte();
}
}
}
}

View File

@ -2022,10 +2022,20 @@ namespace VirtualNes.Core
r = R;
}
internal void SetContext(R6502 r)
{
R = r;
}
internal int GetDmaCycles()
{
return DMA_cycles;
}
internal void SetDmaCycles(int cycles)
{
DMA_cycles = cycles;
}
}
public enum StatusFlag6502 : int

View File

@ -53,6 +53,11 @@ namespace VirtualNes.Core
}
}
public void WriteTo(T[] source, int start, int length)
{
Array.Copy(source, 0, m_rawArray, Offset + start, length);
}
public Span<T> Span(int start, int length)
{
return new Span<T>(m_rawArray, start + Offset, length);

View File

@ -1872,12 +1872,15 @@ namespace VirtualNes.Core
state.dskBLOCK.BlockVersion = 0x0210;
state.dskBLOCK.BlockSize = 0;
state.dskdata = new List<uint>();
for (int i = 16; i < DiskSize; i++)
{
if (lpWrite[i] != 0)
{
state.dskdata = (uint)(i & 0x00FFFFFF);
state.dskdata |= ((uint)lpDisk[i] & 0xFF) << 24;
uint data = (uint)(i & 0x00FFFFFF);
data |= ((uint)lpDisk[i] & 0xFF) << 24;
state.dskdata.Add(data);
}
}
}
@ -1898,7 +1901,191 @@ namespace VirtualNes.Core
public void LoadState(State state)
{
//HEADER
{
state.HEADER.ID = "VirtuaNES ST";
state.HEADER.BlockVersion = 0x0200;
if (rom.GetMapperNo() != 20)
rom.SetPROM_CRC(state.HEADER.Ext0);
else
{
rom.SetGameID(state.HEADER.Ext0);
rom.SetMakerID(state.HEADER.Ext1);
rom.SetDiskNo(state.HEADER.Ext2);
}
}
//REGISTER STATE
{
R6502 R = new R6502();
R.PC = state.reg.cpureg.PC;
R.A = state.reg.cpureg.A;
R.X = state.reg.cpureg.X;
R.Y = state.reg.cpureg.Y;
R.S = state.reg.cpureg.S;
R.P = state.reg.cpureg.P;
R.INT_pending = state.reg.cpureg.I;
cpu.SetContext(R);
apu.SetFrameIRQ(
state.reg.cpureg.FrameIRQ_cycles,
state.reg.cpureg.FrameIRQ_count,
state.reg.cpureg.FrameIRQ_type,
state.reg.cpureg.FrameIRQ,
state.reg.cpureg.FrameIRQ_occur
);
cpu.SetDmaCycles(state.reg.cpureg.DMA_cycles);
emul_cycles = state.reg.cpureg.emul_cycles;
base_cycles = state.reg.cpureg.base_cycles;
// LOAD PPU STATE
MMU.PPUREG[0] = state.reg.ppureg.reg0;
MMU.PPUREG[1] = state.reg.ppureg.reg1;
MMU.PPUREG[2] = state.reg.ppureg.reg2;
MMU.PPUREG[3] = state.reg.ppureg.reg3;
MMU.PPU7_Temp = state.reg.ppureg.reg7;
MMU.loopy_t = state.reg.ppureg.loopy_t;
MMU.loopy_v = state.reg.ppureg.loopy_v;
MMU.loopy_x = state.reg.ppureg.loopy_x;
MMU.PPU56Toggle = state.reg.ppureg.toggle56;
}
//RAM STATE
{
// SAVE RAM STATE
MemoryUtility.memcpy(MMU.RAM, state.ram.RAM, state.ram.RAM.Length);
MemoryUtility.memcpy(MMU.BGPAL, state.ram.BGPAL, state.ram.BGPAL.Length);
MemoryUtility.memcpy(MMU.SPPAL, state.ram.SPPAL, state.ram.SPPAL.Length);
MemoryUtility.memcpy(MMU.SPRAM, state.ram.SPRAM, state.ram.SPRAM.Length);
if (rom.IsSAVERAM())
{
Array.Copy(state.WRAM, MMU.WRAM, SAVERAM_SIZE);
}
}
//BANK STATE
{
// SAVE CPU MEMORY BANK DATA
// BANK0,1,2はバンクセーブに関係なし
// VirtuaNES0.30から
// バンクはSRAM使用に関わらずセーブ
for (int i = 3; i < 8; i++)
{
MMU.CPU_MEM_TYPE[i] = state.mmu.CPU_MEM_TYPE[i];
MMU.CPU_MEM_PAGE[i] = state.mmu.CPU_MEM_PAGE[i];
}
// SAVE VRAM MEMORY DATA
for (int i = 0; i < 12; i++)
{
MMU.PPU_MEM_TYPE[i] = state.mmu.PPU_MEM_TYPE[i];
MMU.PPU_MEM_PAGE[i] = state.mmu.PPU_MEM_PAGE[i];
}
for (int i = 0; i < 8; i++)
{
MMU.CRAM_USED[i] = state.mmu.CRAM_USED[i];
}
// WRITE CPU RAM MEMORY BANK
int stateStep = 0;
var stateCPU_MEM_BANK = state.CPU_MEM_BANK.ToArray();
for (int i = 3; i < 8; i++)
{
if (state.mmu.CPU_MEM_TYPE[i] != MMU.BANKTYPE_ROM)
{
var sourceData = new Span<byte>(stateCPU_MEM_BANK, stateStep * 8 * 1024, 8 * 1024);
MMU.CPU_MEM_BANK[i].WriteTo(sourceData.ToArray(), 0, 8 * 1024);
stateStep++;
}
}
Array.Copy(state.VRAM, MMU.VRAM, state.VRAM.Length);
stateStep = 0;
var stateCRAM = state.CRAM.ToArray();
// LOAD CRAM MEMORY
for (int i = 0; i < 8; i++)
{
if (MMU.CRAM_USED[i] != 0)
{
var sourceData = stateCRAM.AsSpan(stateStep * 4 * 1024, 4 * 1024).ToArray();
Array.Copy(sourceData, 0, MMU.CRAM, 0x1000 * i, 4 * 1024);
}
}
}
// 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.LoadState(state.mmc.mmcdata);
}
}
//CONTROLLER STATE
{
pad.pad1bit = state.ctr.pad1bit;
pad.pad2bit = state.ctr.pad2bit;
pad.pad3bit = state.ctr.pad3bit;
pad.pad4bit = state.ctr.pad4bit;
pad.SetStrobe(state.ctr.strobe == 0 ? false : true);
}
//SND STATE
{
state.snd = SNDSTAT.GetDefault();
var buffer = new StateReader(state.snd.snddata);
apu.LoadState(buffer);
}
// DISKIMAGE STATE
if (rom.GetMapperNo() == 20)
{
var lpDisk = rom.GetPROM();
var lpWrite = rom.GetDISK();
int DiskSize = 16 + 65500 * rom.GetDiskNo();
Array.Clear(lpWrite, 0, DiskSize);
for (int i = 0; i < state.dsk.DifferentSize; i++)
{
var pos = state.dskdata[i];
byte data = (byte)(pos >> 24);
pos &= 0x00FFFFFF;
if (pos >= 16 && pos < DiskSize)
{
lpDisk[pos] = data;
lpWrite[pos] = 0xFF;
}
}
}
// EXCTR STATE
if (pad.GetExController() != 0)
{
pad.SetSyncExData(state.exctr.data);
}
}
internal void SetZapperPos(int x, int y)
{
ZapperX = x; ZapperY = y;
}
public enum IRQMETHOD

View File

@ -515,6 +515,11 @@ namespace VirtualNes.Core
return bStrobe;
}
internal void SetStrobe(bool v)
{
bStrobe = v;
}
internal uint GetSyncExData()
{
uint data = 0;
@ -571,6 +576,63 @@ namespace VirtualNes.Core
}
return data;
}
internal void SetSyncExData(uint data)
{
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;
if ((data & 0x80000000) != 0)
{
x = -1;
y = -1;
}
else
{
x = (int)(data & 0xFF);
y = (int)((data & 0xFF00) >> 8);
}
expad.SetSyncData(0, x);
expad.SetSyncData(1, y);
nes.SetZapperPos(x, y);
}
if (excontroller_select != (int)EXCONTROLLER.EXCONTROLLER_SPACESHADOWGUN)
{
if ((data & 0x0010000) != 0)
expad.SetSyncData(2, 1);
else
expad.SetSyncData(2, 0);
}
else
{
expad.SetSyncData(2, (byte)(data >> 16));
}
break;
case EXCONTROLLER.EXCONTROLLER_CRAZYCLIMBER:
expad.SetSyncData(0, (int)data);
break;
case EXCONTROLLER.EXCONTROLLER_TOPRIDER:
expad.SetSyncData(0, (int)data);
break;
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_A:
case EXCONTROLLER.EXCONTROLLER_FAMILYTRAINER_B:
expad.SetSyncData(0, (int)data);
break;
case EXCONTROLLER.EXCONTROLLER_EXCITINGBOXING:
expad.SetSyncData(0, (int)data);
break;
case EXCONTROLLER.EXCONTROLLER_MAHJANG:
expad.SetSyncData(0, (int)data);
break;
default:
break;
}
}
}
public enum VSType

View File

@ -353,16 +353,31 @@ namespace VirtualNes.Core
return diskno;
}
internal void SetDiskNo(int v)
{
diskno = v;
}
internal uint GetGameID()
{
return fdsgameID;
}
internal void SetGameID(uint id)
{
fdsgameID = id;
}
internal uint GetMakerID()
{
return fdsmakerID;
}
internal void SetMakerID(uint id)
{
fdsmakerID = id;
}
internal bool IsVSUNISYSTEM()
{
return (header.control2 & (byte)EnumRomControlByte2.ROM_VSUNISYSTEM) != 0;
@ -373,6 +388,11 @@ namespace VirtualNes.Core
return crc;
}
public void SetPROM_CRC(uint v)
{
crc = v;
}
internal byte GetPROM_SIZE()
{
return header.PRG_PAGE_SIZE;

View File

@ -3,11 +3,19 @@
public struct BLOCKHDR : IStateBufferObject
{
public readonly bool Valid => !string.IsNullOrEmpty(ID);
/// <summary> 总是8个字节 </summary>
public string ID;
public ushort Reserved;
public ushort BlockVersion;
public uint BlockSize;
public readonly uint GetSize()
{
return (uint)(8 + sizeof(ushort) + sizeof(ushort) + sizeof(uint));
}
public readonly void SaveState(StateBuffer buffer)
{
if (Valid)
@ -19,9 +27,12 @@
}
}
public readonly uint GetSize()
public void LoadState(StateReader buffer)
{
return (uint)(ID.Length + sizeof(ushort) + sizeof(ushort) + sizeof(uint));
ID = buffer.Read_string(8);
Reserved = buffer.Read_ushort();
BlockVersion = buffer.Read_ushort();
BlockSize = buffer.Read_uint();
}
}
}

View File

@ -24,5 +24,14 @@ namespace VirtualNes.Core
buffer.Write(pad4bit);
buffer.Write(strobe);
}
public void LoadState(StateReader buffer)
{
pad1bit = buffer.Read_uint();
pad2bit = buffer.Read_uint();
pad3bit = buffer.Read_uint();
pad4bit = buffer.Read_uint();
strobe = buffer.Read_byte();
}
}
}

View File

@ -13,5 +13,10 @@
{
buffer.Write(DifferentSize);
}
public void LoadState(StateReader buffer)
{
DifferentSize = buffer.Read_int();
}
}
}

View File

@ -13,5 +13,10 @@
{
buffer.Write(data);
}
public void LoadState(StateReader buffer)
{
data = buffer.Read_uint();
}
}
}

View File

@ -16,6 +16,13 @@ namespace VirtualNes.Core
/// <summary> 2字节 </summary>
public ushort Ext2;
public readonly uint GetSize()
{
return (uint)(ID.Length + sizeof(ushort) + sizeof(uint) + sizeof(ushort) + sizeof(ushort));
}
public readonly void SaveState(StateBuffer buffer)
{
buffer.Write(ID);
@ -25,9 +32,13 @@ namespace VirtualNes.Core
buffer.Write(Ext2);
}
public readonly uint GetSize()
public void LoadState(StateReader buffer)
{
return (uint)(ID.Length + sizeof(ushort) + sizeof(uint) + sizeof(ushort) + sizeof(ushort));
ID = buffer.Read_string(12);
BlockVersion = buffer.Read_ushort();
Ext0 = buffer.Read_uint();
Ext1 = buffer.Read_ushort();
Ext2 = buffer.Read_ushort();
}
}
}

View File

@ -9,14 +9,19 @@
return new MMCSTAT() { mmcdata = new byte[256] };
}
public uint GetSize()
public readonly uint GetSize()
{
return (uint)mmcdata.Length;
return 256;
}
public void SaveState(StateBuffer buffer)
public readonly void SaveState(StateBuffer buffer)
{
buffer.Write(mmcdata);
}
public void LoadState(StateReader buffer)
{
mmcdata = buffer.Read_bytes(256);
}
}
}

View File

@ -34,5 +34,14 @@
buffer.Write(PPU_MEM_PAGE);
buffer.Write(CRAM_USED);
}
public void LoadState(StateReader buffer)
{
CPU_MEM_TYPE = buffer.Read_bytes(8);
CPU_MEM_PAGE = buffer.Read_ushorts(8);
PPU_MEM_TYPE = buffer.Read_bytes(12);
PPU_MEM_PAGE = buffer.Read_ushorts(12);
CRAM_USED = buffer.Read_bytes(8);
}
}
}

View File

@ -34,5 +34,13 @@ namespace VirtualNes.Core
buffer.Write(SPPAL);
buffer.Write(SPRAM);
}
public void LoadState(StateReader buffer)
{
RAM = buffer.Read_bytes(2 * 1024);
BGPAL = buffer.Read_bytes(16);
SPPAL = buffer.Read_bytes(16);
SPRAM = buffer.Read_bytes(256);
}
}
}

View File

@ -5,15 +5,23 @@
public CPUSTAT cpureg;
public PPUSTAT ppureg;
public void SaveState(StateBuffer buffer)
public readonly uint GetSize()
{
return cpureg.GetSize() + ppureg.GetSize();
}
public readonly void SaveState(StateBuffer buffer)
{
cpureg.SaveState(buffer);
ppureg.SaveState(buffer);
}
public uint GetSize()
public void LoadState(StateReader buffer)
{
return cpureg.GetSize() + ppureg.GetSize();
cpureg.LoadState(buffer);
ppureg.LoadState(buffer);
}
}
@ -60,6 +68,25 @@
buffer.Write(emul_cycles);
buffer.Write(base_cycles);
}
public void LoadState(StateReader buffer)
{
PC = buffer.Read_ushort();
A = buffer.Read_byte();
X = buffer.Read_byte();
Y = buffer.Read_byte();
S = buffer.Read_byte();
P = buffer.Read_byte();
I = buffer.Read_byte();
FrameIRQ = buffer.Read_byte();
FrameIRQ_occur = buffer.Read_byte();
FrameIRQ_count = buffer.Read_byte();
FrameIRQ_type = buffer.Read_byte();
FrameIRQ_cycles = buffer.Read_int();
DMA_cycles = buffer.Read_int();
emul_cycles = buffer.Read_long();
base_cycles = buffer.Read_long();
}
}
public struct PPUSTAT : IStateBufferObject
@ -92,5 +119,18 @@
buffer.Write(loopy_v);
buffer.Write(loopy_x);
}
public void LoadState(StateReader buffer)
{
reg0 = buffer.Read_byte();
reg1 = buffer.Read_byte();
reg2 = buffer.Read_byte();
reg3 = buffer.Read_byte();
reg7 = buffer.Read_byte();
toggle56 = buffer.Read_byte();
loopy_t = buffer.Read_ushort();
loopy_v = buffer.Read_ushort();
loopy_x = buffer.Read_ushort();
}
}
}

View File

@ -15,14 +15,19 @@ namespace VirtualNes.Core
return new SNDSTAT() { snddata = new byte[0x800] };
}
public uint GetSize()
public readonly uint GetSize()
{
return (uint)snddata.Length;
}
public void SaveState(StateBuffer buffer)
public readonly void SaveState(StateBuffer buffer)
{
buffer.Write(snddata);
}
public void LoadState(StateReader buffer)
{
snddata = buffer.Read_bytes(0x800);
}
}
}

View File

@ -31,7 +31,7 @@ namespace VirtualNes.Core
public BLOCKHDR dskBLOCK;
public DISKDATA dsk;
public uint dskdata;
public List<uint> dskdata;
public BLOCKHDR exctrBLOCK;
public EXCTRSTAT exctr;
@ -87,7 +87,10 @@ namespace VirtualNes.Core
{
dskBLOCK.SaveState(buffer);
dsk.SaveState(buffer);
buffer.Write(dskdata);
foreach (var data in dskdata)
{
buffer.Write(data);
}
}
if (exctrBLOCK.Valid)

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace VirtualNes.Core
@ -31,15 +32,11 @@ namespace VirtualNes.Core
}
public void Write(sbyte[] sbytes)
{
foreach(var value in 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);
@ -80,11 +77,134 @@ namespace VirtualNes.Core
{
Write(BitConverter.GetBytes(value));
}
public void Write(uint value)
{
Write(BitConverter.GetBytes(value));
}
}
public class StateReader
{
private MemoryStream m_dataStream;
public StateReader(byte[] bytes)
{
m_dataStream = new MemoryStream(bytes);
}
public void Skip(uint count)
{
m_dataStream.Seek(count, SeekOrigin.Current);
}
public void Skip(long count)
{
m_dataStream.Seek(count, SeekOrigin.Current);
}
public byte[] Read_bytes(int length)
{
var result = new byte[length];
m_dataStream.Read(result, 0, length);
return result;
}
public sbyte[] Read_sbytes(int length)
{
var result = new sbyte[length];
for (int i = 0; i < length; i++)
{
result[i] = (sbyte)m_dataStream.ReadByte();
}
return result;
}
public byte Read_byte()
{
return (byte)m_dataStream.ReadByte();
}
public ushort[] Read_ushorts(int length)
{
ushort[] result = new ushort[length];
for (int i = 0; i < length; i++)
{
int byte1 = m_dataStream.ReadByte();
int byte2 = m_dataStream.ReadByte();
result[i] = (ushort)(byte1 << 8 | byte2);
}
return result;
}
public int[] Read_ints(int length)
{
int[] result = new int[length];
for (int i = 0; i < length; i++)
{
int byte1 = m_dataStream.ReadByte();
int byte2 = m_dataStream.ReadByte();
int byte3 = m_dataStream.ReadByte();
int byte4 = m_dataStream.ReadByte();
result[i] = byte1 << 24 | byte2 << 16 | byte3 << 8 | byte4;
}
return result;
}
public string Read_string(int length)
{
var result = Read_bytes(length);
return Encoding.ASCII.GetString(result);
}
public double Read_double()
{
var result = Read_bytes(4);
return BitConverter.ToDouble(result, 0);
}
public ushort Read_ushort()
{
var b1 = Read_byte();
var b2 = Read_byte();
return (ushort)(b1 << 8 | b2);
}
public int Read_int()
{
var b1 = Read_byte();
var b2 = Read_byte();
var b3 = Read_byte();
var b4 = Read_byte();
return b1 << 24 | b2 << 16 | b3 << 8 | b4;
}
public sbyte Read_sbyte(sbyte value)
{
return (sbyte)m_dataStream.ReadByte();
}
public long Read_long()
{
var b1 = Read_byte();
var b2 = Read_byte();
var b3 = Read_byte();
var b4 = Read_byte();
var b5 = Read_byte();
var b6 = Read_byte();
var b7 = Read_byte();
var b8 = Read_byte();
return b1 << 56 | b2 << 48 | b3 << 40 | b4 << 32 | b5 << 24 | b6 << 16 | b7 << 8 | b8;
}
public uint Read_uint()
{
return (uint)Read_int();
}
}
public interface IStateBufferObject
{
uint GetSize();
void SaveState(StateBuffer buffer);
void LoadState(StateReader buffer);
}
}