feat(core): add machine register/memory/timer state
This commit is contained in:
1 parent
7ee6ff9587
commit
b032789592
1 file changed
+203
@@ -0,0 +1,203 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Chip8.Core;
|
||||
|
||||
public sealed partial class Machine
|
||||
{
|
||||
static readonly byte[] stateMagic = Encoding.ASCII.GetBytes("C8ST");
|
||||
const byte stateVersion = 1;
|
||||
|
||||
public byte[] saveState()
|
||||
{
|
||||
using var stream = new MemoryStream();
|
||||
using var writer = new BinaryWriter(stream, Encoding.ASCII);
|
||||
|
||||
writer.Write(stateMagic);
|
||||
writer.Write(stateVersion);
|
||||
writer.Write((byte)platform);
|
||||
writeQuirks(writer);
|
||||
writeBody(writer);
|
||||
|
||||
writer.Flush();
|
||||
return stream.ToArray();
|
||||
}
|
||||
|
||||
public void loadState(byte[] data)
|
||||
{
|
||||
using var stream = new MemoryStream(data);
|
||||
using var reader = new BinaryReader(stream, Encoding.ASCII);
|
||||
|
||||
readHeader(reader);
|
||||
var savedPlatform = (Platform)reader.ReadByte();
|
||||
if (savedPlatform != platform)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
"state platform " + savedPlatform + " does not match machine platform " + platform);
|
||||
}
|
||||
|
||||
readQuirksInto(reader, quirks);
|
||||
readBody(reader);
|
||||
}
|
||||
|
||||
public static Machine restore(byte[] data)
|
||||
{
|
||||
using var stream = new MemoryStream(data);
|
||||
using var reader = new BinaryReader(stream, Encoding.ASCII);
|
||||
|
||||
readHeader(reader);
|
||||
var savedPlatform = (Platform)reader.ReadByte();
|
||||
Quirks savedQuirks = readQuirks(reader);
|
||||
|
||||
var machine = new Machine(savedQuirks, null, savedPlatform);
|
||||
machine.readBody(reader);
|
||||
return machine;
|
||||
}
|
||||
|
||||
static void readHeader(BinaryReader reader)
|
||||
{
|
||||
byte[] magic = reader.ReadBytes(stateMagic.Length);
|
||||
for (int index = 0; index < stateMagic.Length; index++)
|
||||
{
|
||||
if (magic.Length <= index || magic[index] != stateMagic[index])
|
||||
{
|
||||
throw new InvalidDataException("not a chip8-sharp save state (bad magic)");
|
||||
}
|
||||
}
|
||||
|
||||
byte version = reader.ReadByte();
|
||||
if (version != stateVersion)
|
||||
{
|
||||
throw new InvalidDataException("unsupported save state version " + version);
|
||||
}
|
||||
}
|
||||
|
||||
void writeQuirks(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(quirks.shiftUsesVy);
|
||||
writer.Write(quirks.jumpWithVx);
|
||||
writer.Write(quirks.loadStoreIncrementsI);
|
||||
writer.Write(quirks.resetVfOnLogic);
|
||||
writer.Write(quirks.clipSprites);
|
||||
writer.Write(quirks.addToIndexSetsCarry);
|
||||
}
|
||||
|
||||
static Quirks readQuirks(BinaryReader reader)
|
||||
{
|
||||
var target = new Quirks();
|
||||
readQuirksInto(reader, target);
|
||||
return target;
|
||||
}
|
||||
|
||||
static void readQuirksInto(BinaryReader reader, Quirks target)
|
||||
{
|
||||
target.shiftUsesVy = reader.ReadBoolean();
|
||||
target.jumpWithVx = reader.ReadBoolean();
|
||||
target.loadStoreIncrementsI = reader.ReadBoolean();
|
||||
target.resetVfOnLogic = reader.ReadBoolean();
|
||||
target.clipSprites = reader.ReadBoolean();
|
||||
target.addToIndexSetsCarry = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
void writeBody(BinaryWriter writer)
|
||||
{
|
||||
writer.Write(memory.Length);
|
||||
writer.Write(memory);
|
||||
writer.Write(v);
|
||||
writer.Write((byte)stack.Length);
|
||||
foreach (ushort frame in stack)
|
||||
{
|
||||
writer.Write(frame);
|
||||
}
|
||||
|
||||
foreach (bool lit in display)
|
||||
{
|
||||
writer.Write(lit);
|
||||
}
|
||||
foreach (bool down in keypad)
|
||||
{
|
||||
writer.Write(down);
|
||||
}
|
||||
foreach (bool down in keypadPrevious)
|
||||
{
|
||||
writer.Write(down);
|
||||
}
|
||||
|
||||
writer.Write(rplFlags);
|
||||
writer.Write(audioPattern);
|
||||
|
||||
writer.Write(i);
|
||||
writer.Write(pc);
|
||||
writer.Write(sp);
|
||||
writer.Write(delayTimer);
|
||||
writer.Write(soundTimer);
|
||||
writer.Write(planeMask);
|
||||
writer.Write(audioPitch);
|
||||
writer.Write(exited);
|
||||
writer.Write(drawPending);
|
||||
writer.Write(waitingForKey);
|
||||
|
||||
video.writeTo(writer);
|
||||
}
|
||||
|
||||
void readBody(BinaryReader reader)
|
||||
{
|
||||
int memoryLength = reader.ReadInt32();
|
||||
if (memoryLength != memory.Length)
|
||||
{
|
||||
throw new InvalidDataException(
|
||||
"state memory size " + memoryLength + " does not match machine memory size " + memory.Length);
|
||||
}
|
||||
|
||||
readExact(reader, memory);
|
||||
readExact(reader, v);
|
||||
|
||||
int stackLength = reader.ReadByte();
|
||||
if (stackLength != stack.Length)
|
||||
{
|
||||
throw new InvalidDataException("state stack depth " + stackLength + " does not match " + stack.Length);
|
||||
}
|
||||
for (int frame = 0; frame < stackLength; frame++)
|
||||
{
|
||||
stack[frame] = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
readBooleans(reader, display);
|
||||
readBooleans(reader, keypad);
|
||||
readBooleans(reader, keypadPrevious);
|
||||
|
||||
readExact(reader, rplFlags);
|
||||
readExact(reader, audioPattern);
|
||||
|
||||
i = reader.ReadUInt16();
|
||||
pc = reader.ReadUInt16();
|
||||
sp = reader.ReadByte();
|
||||
delayTimer = reader.ReadByte();
|
||||
soundTimer = reader.ReadByte();
|
||||
planeMask = reader.ReadInt32();
|
||||
audioPitch = reader.ReadByte();
|
||||
exited = reader.ReadBoolean();
|
||||
drawPending = reader.ReadBoolean();
|
||||
waitingForKey = reader.ReadBoolean();
|
||||
|
||||
video.readFrom(reader);
|
||||
}
|
||||
|
||||
static void readExact(BinaryReader reader, byte[] target)
|
||||
{
|
||||
int read = reader.Read(target, 0, target.Length);
|
||||
if (read != target.Length)
|
||||
{
|
||||
throw new InvalidDataException("save state truncated (expected " + target.Length + " bytes, got " + read + ")");
|
||||
}
|
||||
}
|
||||
|
||||
static void readBooleans(BinaryReader reader, bool[] target)
|
||||
{
|
||||
for (int index = 0; index < target.Length; index++)
|
||||
{
|
||||
target[index] = reader.ReadBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user