test(core): add quirks and save-state tests

This commit is contained in:
dualfroz committed 2026-08-28 11:08:22 +02:00
1 parent 219c7b895c
commit dc1d25a0cf
2 files changed
+208

No files matched your search

+104
View File
@@ -0,0 +1,104 @@
using Chip8.Core;
using Xunit;
namespace Chip8.Tests;
public class QuirksTests
{
static Machine drawRowAt(int originX, Quirks quirks)
{
var machine = new Machine(quirks, 1);
machine.memory[0x200] = 0xD0;
machine.memory[0x201] = 0x11;
machine.i = 0x300;
machine.memory[0x300] = 0xFF;
machine.v[0] = (byte)originX;
machine.v[1] = 0;
machine.step();
return machine;
}
[Fact]
public void clipQuirkTruncatesSpriteAtRightEdge()
{
var clipping = new Quirks { clipSprites = true };
var machine = drawRowAt(62, clipping);
Assert.Equal(2, AsciiScreen.litPixelCount(machine.display));
Assert.True(machine.display[62]);
Assert.True(machine.display[63]);
Assert.False(machine.display[0]);
}
[Fact]
public void wrapQuirkWrapsSpriteAroundRightEdge()
{
var wrapping = new Quirks { clipSprites = false };
var machine = drawRowAt(62, wrapping);
Assert.Equal(8, AsciiScreen.litPixelCount(machine.display));
Assert.True(machine.display[62]);
Assert.True(machine.display[63]);
Assert.True(machine.display[0]);
Assert.True(machine.display[5]);
Assert.False(machine.display[6]);
}
[Fact]
public void shiftQuirkSelectsSourceRegister()
{
var usesVx = TestHarness.withOpcode(0x8016, new Quirks { shiftUsesVy = false });
usesVx.v[0] = 0b0000_0100;
usesVx.v[1] = 0xFF;
usesVx.step();
Assert.Equal(0b0000_0010, usesVx.v[0]);
var usesVy = TestHarness.withOpcode(0x8016, new Quirks { shiftUsesVy = true });
usesVy.v[0] = 0x00;
usesVy.v[1] = 0b0000_0100;
usesVy.step();
Assert.Equal(0b0000_0010, usesVy.v[0]);
}
[Fact]
public void loadStoreIncrementQuirkAdvancesIndex()
{
var noIncrement = TestHarness.withOpcode(0xF255, new Quirks { loadStoreIncrementsI = false });
noIncrement.i = 0x300;
noIncrement.step();
Assert.Equal(0x300, noIncrement.i);
var increment = TestHarness.withOpcode(0xF255, new Quirks { loadStoreIncrementsI = true });
increment.i = 0x300;
increment.step();
Assert.Equal(0x303, increment.i);
}
[Fact]
public void jumpQuirkSelectsOffsetRegister()
{
var withV0 = TestHarness.withOpcode(0xB300, new Quirks { jumpWithVx = false });
withV0.v[0] = 0x10;
withV0.v[3] = 0x20;
withV0.step();
Assert.Equal(0x310, withV0.pc);
var withVx = TestHarness.withOpcode(0xB300, new Quirks { jumpWithVx = true });
withVx.v[0] = 0x10;
withVx.v[3] = 0x20;
withVx.step();
Assert.Equal(0x320, withVx.pc);
}
[Fact]
public void vfResetQuirkClearsFlagOnLogic()
{
var noReset = TestHarness.withOpcode(0x8011, new Quirks { resetVfOnLogic = false });
noReset.v[0xF] = 0x5;
noReset.step();
Assert.Equal(0x5, noReset.v[0xF]);
var reset = TestHarness.withOpcode(0x8011, new Quirks { resetVfOnLogic = true });
reset.v[0xF] = 0x5;
reset.step();
Assert.Equal(0, reset.v[0xF]);
}
}
+104
View File
@@ -0,0 +1,104 @@
using System.IO;
using Chip8.Core;
using Xunit;
namespace Chip8.Tests;
public class SaveStateTests
{
[Fact]
public void loadStateRestoresRegistersMemoryAndDisplay()
{
var machine = TestHarness.withProgram(Platform.Chip8, 0x7001, 0x1200);
machine.step();
machine.v[3] = 0xAB;
machine.i = 0x321;
machine.memory[0x400] = 0x42;
machine.display[5] = true;
byte[] state = machine.saveState();
machine.v[3] = 0x00;
machine.i = 0x000;
machine.memory[0x400] = 0x99;
machine.display[5] = false;
machine.pc = 0x2FF;
machine.loadState(state);
Assert.Equal(0xAB, machine.v[3]);
Assert.Equal(0x321, machine.i);
Assert.Equal(0x42, machine.memory[0x400]);
Assert.True(machine.display[5]);
Assert.Equal(0x202, machine.pc);
}
[Fact]
public void restoreBuildsANewMachineFromState()
{
var machine = TestHarness.withProgram(Platform.SuperChip, 0x6009);
machine.step();
byte[] state = machine.saveState();
Machine restored = Machine.restore(state);
Assert.Equal(Platform.SuperChip, restored.platform);
Assert.Equal(machine.v[0], restored.v[0]);
Assert.Equal(machine.pc, restored.pc);
}
[Fact]
public void restoredMachineExecutesIdenticallyToTheOriginal()
{
var original = TestHarness.withProgram(Platform.Chip8, 0x7001, 0x1200);
for (int step = 0; step < 20; step++)
{
original.step();
}
Machine resumed = Machine.restore(original.saveState());
for (int step = 0; step < 15; step++)
{
original.step();
resumed.step();
}
Assert.Equal(original.pc, resumed.pc);
Assert.Equal(original.v[0], resumed.v[0]);
Assert.Equal(original.i, resumed.i);
}
[Fact]
public void xoChipStatePreservesPlanesAndAudio()
{
var machine = TestHarness.withProgram(Platform.XoChip, 0xF201, 0xA400, 0xD001);
machine.memory[0x400] = 0x80;
machine.audioPattern[3] = 0xC3;
for (int step = 0; step < 3; step++)
{
machine.step();
}
Assert.Equal(2, machine.video.colorAt(0, 0));
Machine restored = Machine.restore(machine.saveState());
Assert.Equal(2, restored.planeMask);
Assert.Equal(2, restored.video.colorAt(0, 0));
Assert.Equal(0xC3, restored.audioPattern[3]);
}
[Fact]
public void loadStateRejectsAMismatchedPlatform()
{
var chip8 = new Machine(Quirks.modern(), 1);
byte[] state = chip8.saveState();
var xoChip = new Machine(null, 1, Platform.XoChip);
Assert.Throws<InvalidDataException>(() => xoChip.loadState(state));
}
[Fact]
public void restoreRejectsGarbage()
{
Assert.Throws<InvalidDataException>(() => Machine.restore(new byte[] { 1, 2, 3, 4, 5 }));
}
}