diff --git a/src/Chip8.Core/FontSet.cs b/src/Chip8.Core/FontSet.cs new file mode 100644 index 0000000..4a108be --- /dev/null +++ b/src/Chip8.Core/FontSet.cs @@ -0,0 +1,48 @@ +namespace Chip8.Core; + +internal static class FontSet +{ + public const int glyphBytes = 5; + + public const int bigGlyphBytes = 10; + + public static readonly byte[] glyphs = + { + 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 + 0x20, 0x60, 0x20, 0x20, 0x70, // 1 + 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 + 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 + 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 + 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 + 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 + 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 + 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 + 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 + 0xF0, 0x90, 0xF0, 0x90, 0x90, // A + 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B + 0xF0, 0x80, 0x80, 0x80, 0xF0, // C + 0xE0, 0x90, 0x90, 0x90, 0xE0, // D + 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E + 0xF0, 0x80, 0xF0, 0x80, 0x80, // F + }; + + public static readonly byte[] bigGlyphs = + { + 0x3C, 0x7E, 0xE7, 0xC3, 0xC3, 0xC3, 0xC3, 0xE7, 0x7E, 0x3C, // 0 + 0x18, 0x38, 0x58, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, // 1 + 0x3E, 0x7F, 0xC3, 0x06, 0x0C, 0x18, 0x30, 0x60, 0xFF, 0xFF, // 2 + 0x3C, 0x7E, 0xC3, 0x03, 0x0E, 0x0E, 0x03, 0xC3, 0x7E, 0x3C, // 3 + 0x06, 0x0E, 0x1E, 0x36, 0x66, 0xC6, 0xFF, 0xFF, 0x06, 0x06, // 4 + 0xFF, 0xFF, 0xC0, 0xC0, 0xFC, 0xFE, 0x03, 0xC3, 0x7E, 0x3C, // 5 + 0x3E, 0x7C, 0xE0, 0xC0, 0xFC, 0xFE, 0xC3, 0xC3, 0x7E, 0x3C, // 6 + 0xFF, 0xFF, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x60, 0x60, // 7 + 0x3C, 0x7E, 0xC3, 0xC3, 0x7E, 0x7E, 0xC3, 0xC3, 0x7E, 0x3C, // 8 + 0x3C, 0x7E, 0xC3, 0xC3, 0x7F, 0x3F, 0x03, 0x07, 0x7E, 0x7C, // 9 + 0x18, 0x3C, 0x66, 0xC3, 0xC3, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, // A + 0xFC, 0xFE, 0xC3, 0xC3, 0xFE, 0xFE, 0xC3, 0xC3, 0xFE, 0xFC, // B + 0x3C, 0x7E, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0x7E, 0x3C, // C + 0xFC, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0xFC, // D + 0xFF, 0xFF, 0xC0, 0xC0, 0xFC, 0xFC, 0xC0, 0xC0, 0xFF, 0xFF, // E + 0xFF, 0xFF, 0xC0, 0xC0, 0xFC, 0xFC, 0xC0, 0xC0, 0xC0, 0xC0, // F + }; +} diff --git a/src/Chip8.Core/MachineSnapshot.cs b/src/Chip8.Core/MachineSnapshot.cs new file mode 100644 index 0000000..7ce649e --- /dev/null +++ b/src/Chip8.Core/MachineSnapshot.cs @@ -0,0 +1,71 @@ +using System; +using System.Globalization; +using System.Text; + +namespace Chip8.Core; + +public sealed class MachineSnapshot +{ + public ushort pc { get; init; } + public ushort i { get; init; } + public byte sp { get; init; } + public byte delayTimer { get; init; } + public byte soundTimer { get; init; } + public int planeMask { get; init; } + public bool hires { get; init; } + public bool exited { get; init; } + public byte[] v { get; init; } = Array.Empty(); + public ushort[] stack { get; init; } = Array.Empty(); + + public static MachineSnapshot capture(Machine machine) + { + var stackFrames = new ushort[machine.sp]; + Array.Copy(machine.stack, stackFrames, machine.sp); + return new MachineSnapshot + { + pc = machine.pc, + i = machine.i, + sp = machine.sp, + delayTimer = machine.delayTimer, + soundTimer = machine.soundTimer, + planeMask = machine.planeMask, + hires = machine.hires, + exited = machine.exited, + v = (byte[])machine.v.Clone(), + stack = stackFrames, + }; + } + + public string format() + { + var builder = new StringBuilder(); + builder.Append("PC=0x").Append(pc.ToString("X4", CultureInfo.InvariantCulture)); + builder.Append(" I=0x").Append(i.ToString("X4", CultureInfo.InvariantCulture)); + builder.Append(" SP=").Append(sp); + builder.Append(" DT=").Append(delayTimer); + builder.Append(" ST=").Append(soundTimer); + builder.Append(" plane=").Append(planeMask); + builder.Append(" hires=").Append(hires ? "1" : "0"); + builder.Append('\n'); + + for (int register = 0; register < v.Length; register++) + { + builder.Append('V').Append(register.ToString("X1", CultureInfo.InvariantCulture)); + builder.Append('=').Append(v[register].ToString("X2", CultureInfo.InvariantCulture)); + builder.Append((register & 0x7) == 0x7 ? '\n' : ' '); + } + + builder.Append("stack=["); + for (int frame = 0; frame < stack.Length; frame++) + { + if (frame > 0) + { + builder.Append(' '); + } + builder.Append("0x").Append(stack[frame].ToString("X4", CultureInfo.InvariantCulture)); + } + builder.Append("]\n"); + + return builder.ToString(); + } +} diff --git a/src/Chip8.Core/Quirks.cs b/src/Chip8.Core/Quirks.cs new file mode 100644 index 0000000..1b3e84c --- /dev/null +++ b/src/Chip8.Core/Quirks.cs @@ -0,0 +1,56 @@ +namespace Chip8.Core; + +public sealed class Quirks +{ + public bool shiftUsesVy { get; set; } + + public bool jumpWithVx { get; set; } + + public bool loadStoreIncrementsI { get; set; } + + public bool resetVfOnLogic { get; set; } + + public bool clipSprites { get; set; } + + public bool addToIndexSetsCarry { get; set; } + + public static Quirks cosmacVip() => new() + { + shiftUsesVy = true, + jumpWithVx = false, + loadStoreIncrementsI = true, + resetVfOnLogic = true, + clipSprites = true, + addToIndexSetsCarry = false, + }; + + public static Quirks modern() => new() + { + shiftUsesVy = false, + jumpWithVx = false, + loadStoreIncrementsI = false, + resetVfOnLogic = false, + clipSprites = true, + addToIndexSetsCarry = false, + }; + + public static Quirks superChip() => new() + { + shiftUsesVy = false, + jumpWithVx = true, + loadStoreIncrementsI = false, + resetVfOnLogic = false, + clipSprites = true, + addToIndexSetsCarry = false, + }; + + public static Quirks xoChip() => new() + { + shiftUsesVy = false, + jumpWithVx = false, + loadStoreIncrementsI = false, + resetVfOnLogic = false, + clipSprites = false, + addToIndexSetsCarry = false, + }; +}