Files

169 lines
4.8 KiB
C#

using Chip8.Core;
using Xunit;
namespace Chip8.Tests;
public class OpcodeTests
{
[Fact]
public void clearScreenBlanksDisplay()
{
var machine = TestHarness.withOpcode(0x00E0);
machine.display[100] = true;
machine.step();
Assert.All(machine.display, pixel => Assert.False(pixel));
}
[Fact]
public void jumpSetsProgramCounter()
{
var machine = TestHarness.withOpcode(0x1ABC);
machine.step();
Assert.Equal(0xABC, machine.pc);
}
[Fact]
public void callPushesReturnAddressAndReturnRestoresIt()
{
var machine = new Machine(Quirks.modern(), 1);
machine.memory[0x200] = 0x22; machine.memory[0x201] = 0x06; // 2206 call 0x206
machine.memory[0x206] = 0x00; machine.memory[0x207] = 0xEE; // 00EE ret
machine.step();
Assert.Equal(0x206, machine.pc);
Assert.Equal(1, machine.sp);
Assert.Equal(0x202, machine.stack[0]);
machine.step();
Assert.Equal(0x202, machine.pc);
Assert.Equal(0, machine.sp);
}
[Fact]
public void skipIfEqualImmediate()
{
var taken = TestHarness.withOpcode(0x3042);
taken.v[0] = 0x42; taken.step();
Assert.Equal(0x204, taken.pc);
var skipped = TestHarness.withOpcode(0x3042);
skipped.v[0] = 0x00; skipped.step();
Assert.Equal(0x202, skipped.pc);
}
[Fact]
public void skipIfNotEqualImmediate()
{
var taken = TestHarness.withOpcode(0x4042);
taken.v[0] = 0x00; taken.step();
Assert.Equal(0x204, taken.pc);
}
[Fact]
public void skipIfRegistersEqualAndNotEqual()
{
var equal = TestHarness.withOpcode(0x5010);
equal.v[0] = 9; equal.v[1] = 9; equal.step();
Assert.Equal(0x204, equal.pc);
var notEqual = TestHarness.withOpcode(0x9010);
notEqual.v[0] = 9; notEqual.v[1] = 8; notEqual.step();
Assert.Equal(0x204, notEqual.pc);
}
[Fact]
public void loadImmediateAndSetIndex()
{
var load = TestHarness.withOpcode(0x6A55);
load.step();
Assert.Equal(0x55, load.v[0xA]);
var index = TestHarness.withOpcode(0xA123);
index.step();
Assert.Equal(0x123, index.i);
}
[Fact]
public void jumpWithOffsetHonoursQuirk()
{
var classic = TestHarness.withOpcode(0xB300, Quirks.modern());
classic.v[0] = 0x12; classic.step();
Assert.Equal(0x312, classic.pc);
var superchip = TestHarness.withOpcode(0xB300, Quirks.superChip());
superchip.v[3] = 0x12; superchip.step();
Assert.Equal(0x312, superchip.pc);
}
[Fact]
public void randomMaskedByImmediate()
{
var machine = TestHarness.withOpcode(0xC000);
machine.step();
Assert.Equal(0, machine.v[0]);
}
[Fact]
public void drawSetsPixelsAndReportsCollision()
{
var machine = new Machine(Quirks.modern(), 1);
machine.memory[0x200] = 0xD0; machine.memory[0x201] = 0x11; // DXYN draw 1 row at (V0,V1)
machine.i = 0x300;
machine.memory[0x300] = 0b1010_0000;
machine.v[0] = 0; machine.v[1] = 0;
machine.step();
Assert.True(machine.display[0]);
Assert.False(machine.display[1]);
Assert.True(machine.display[2]);
Assert.Equal(0, machine.v[0xF]);
machine.pc = 0x200;
machine.step();
Assert.False(machine.display[0]);
Assert.Equal(1, machine.v[0xF]);
}
[Fact]
public void drawClipsAtRightAndBottomEdges()
{
var machine = new Machine(Quirks.modern(), 1);
machine.memory[0x200] = 0xD0; machine.memory[0x201] = 0x11; // DXYN draw 1 row at (V0,V1)
machine.i = 0x300;
machine.memory[0x300] = 0b1111_1111;
machine.v[0] = 62; machine.v[1] = 0;
machine.step();
Assert.True(machine.display[62]);
Assert.True(machine.display[63]);
Assert.Equal(2, AsciiScreen.litPixelCount(machine.display));
}
[Fact]
public void skipOnKeyStateBothDirections()
{
var pressed = TestHarness.withOpcode(0xE09E);
pressed.v[0] = 0x5; pressed.pressKey(0x5); pressed.step();
Assert.Equal(0x204, pressed.pc);
var notPressed = TestHarness.withOpcode(0xE0A1);
notPressed.v[0] = 0x5; notPressed.step();
Assert.Equal(0x204, notPressed.pc);
}
[Fact]
public void awaitKeyBlocksUntilReleaseThenStores()
{
var machine = TestHarness.withOpcode(0xF00A);
machine.step();
Assert.Equal(0x200, machine.pc);
Assert.True(machine.waitingForKey);
machine.pressKey(0x9);
machine.step();
Assert.Equal(0x200, machine.pc);
machine.releaseKey(0x9);
machine.step();
Assert.Equal(0x202, machine.pc);
Assert.Equal(0x9, machine.v[0]);
Assert.False(machine.waitingForKey);
}
}