From dd55300b3104eaf958cc38a5529e2d96e83d97ae Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 27 Aug 2026 21:54:59 +0200 Subject: [PATCH] test(core): add machine state and ROM loading tests --- tests/Chip8.Tests/MachineTests.cs | 121 ++++++++++++++++++++++++++++++ tests/Chip8.Tests/RomTests.cs | 36 +++++++++ 2 files changed, 157 insertions(+) create mode 100644 tests/Chip8.Tests/MachineTests.cs create mode 100644 tests/Chip8.Tests/RomTests.cs diff --git a/tests/Chip8.Tests/MachineTests.cs b/tests/Chip8.Tests/MachineTests.cs new file mode 100644 index 0000000..9ff5c16 --- /dev/null +++ b/tests/Chip8.Tests/MachineTests.cs @@ -0,0 +1,121 @@ +using System; +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class MachineTests +{ + [Fact] + public void resetLoadsFontAndProgramCounter() + { + var machine = new Machine(Quirks.modern(), 1); + Assert.Equal(Machine.programStart, machine.pc); + Assert.Equal(0xF0, machine.memory[0x50]); + Assert.Equal(0x80, machine.memory[0x50 + 5 * 0xF + 4]); + } + + [Fact] + public void loadRomCopiesBytesToProgramArea() + { + var machine = new Machine(Quirks.modern(), 1); + machine.loadRom(new byte[] { 0x12, 0x34, 0x56 }); + Assert.Equal(0x12, machine.memory[0x200]); + Assert.Equal(0x34, machine.memory[0x201]); + Assert.Equal(0x56, machine.memory[0x202]); + } + + [Fact] + public void loadRomRejectsOversizedImage() + { + var machine = new Machine(Quirks.modern(), 1); + Assert.Throws(() => machine.loadRom(new byte[Machine.memorySize])); + } + + [Fact] + public void delayAndSoundTimersCountDownAndGateBeep() + { + var machine = TestHarness.withOpcode(0xF018); + machine.v[0] = 3; + machine.step(); + Assert.Equal(3, machine.soundTimer); + Assert.True(machine.soundActive); + + machine.tickTimers(); + machine.tickTimers(); + machine.tickTimers(); + Assert.Equal(0, machine.soundTimer); + Assert.False(machine.soundActive); + + machine.tickTimers(); + Assert.Equal(0, machine.soundTimer); + } + + [Fact] + public void readDelayTimerIntoRegister() + { + var machine = TestHarness.withOpcode(0xF007); + machine.delayTimer = 0x2A; + machine.step(); + Assert.Equal(0x2A, machine.v[0]); + } + + [Fact] + public void addToIndex() + { + var machine = TestHarness.withOpcode(0xF01E); + machine.i = 0x200; machine.v[0] = 0x05; + machine.step(); + Assert.Equal(0x205, machine.i); + } + + [Fact] + public void fontCharacterAddress() + { + var machine = TestHarness.withOpcode(0xF029); + machine.v[0] = 0xB; + machine.step(); + Assert.Equal(0x50 + 0xB * 5, machine.i); + Assert.Equal(0xE0, machine.memory[machine.i]); + } + + [Fact] + public void binaryCodedDecimal() + { + var machine = TestHarness.withOpcode(0xF033); + machine.i = 0x300; machine.v[0] = 234; + machine.step(); + Assert.Equal(2, machine.memory[0x300]); + Assert.Equal(3, machine.memory[0x301]); + Assert.Equal(4, machine.memory[0x302]); + } + + [Fact] + public void storeAndLoadRegistersHonourIncrementQuirk() + { + var modern = TestHarness.withOpcode(0xF255, Quirks.modern()); + modern.i = 0x300; + modern.v[0] = 0xAA; modern.v[1] = 0xBB; modern.v[2] = 0xCC; + modern.step(); + Assert.Equal(0xAA, modern.memory[0x300]); + Assert.Equal(0xBB, modern.memory[0x301]); + Assert.Equal(0xCC, modern.memory[0x302]); + Assert.Equal(0x300, modern.i); + + var cosmac = TestHarness.withOpcode(0xF265, Quirks.cosmacVip()); + cosmac.i = 0x300; + cosmac.memory[0x300] = 0x11; cosmac.memory[0x301] = 0x22; + cosmac.step(); + Assert.Equal(0x11, cosmac.v[0]); + Assert.Equal(0x22, cosmac.v[1]); + Assert.Equal(0x303, cosmac.i); + } + + [Fact] + public void systemAddressOpcodeIsInert() + { + var machine = TestHarness.withOpcode(0x0123); + machine.step(); + Assert.Equal(0x202, machine.pc); + } +} diff --git a/tests/Chip8.Tests/RomTests.cs b/tests/Chip8.Tests/RomTests.cs new file mode 100644 index 0000000..5bd1703 --- /dev/null +++ b/tests/Chip8.Tests/RomTests.cs @@ -0,0 +1,36 @@ +using Chip8.Core; +using Xunit; + +namespace Chip8.Tests; + +public class RomTests +{ + [Theory] + [InlineData("2-ibm-logo.ch8", 40, "ibm.txt", 230)] + [InlineData("3-corax+.ch8", 500, "corax.txt", 503)] + [InlineData("4-flags.ch8", 1000, "flags.txt", 495)] + [InlineData("test_opcode.ch8", 3000, "opcode.txt", 626)] + [InlineData("BC_test.ch8", 4000, "bctest.txt", 172)] + public void romRendersExpectedFramebuffer(string rom, int cycles, string golden, int litPixels) + { + var machine = TestHarness.runRom(rom, cycles); + string rendered = AsciiScreen.render(machine.display); + + Assert.Equal(litPixels, AsciiScreen.litPixelCount(machine.display)); + Assert.Equal(TestHarness.goldenText(golden), rendered); + } + + [Fact] + public void corax89PassesUnderCosmacQuirksToo() + { + var machine = TestHarness.runRom("test_opcode.ch8", 3000, Quirks.cosmacVip()); + Assert.Equal(TestHarness.goldenText("opcode.txt"), AsciiScreen.render(machine.display)); + } + + [Fact] + public void beepRomActivatesSoundTimer() + { + var machine = TestHarness.runRom("7-beep.ch8", 40); + Assert.True(machine.soundActive); + } +}