test(core): add PPM export and disassembler tests
This commit is contained in:
1 parent
18ba343fb0
commit
a72e1d6e07
2 files changed
+181
No files matched your search
@@ -0,0 +1,95 @@
|
|||||||
|
using Chip8.Core;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Chip8.Tests;
|
||||||
|
|
||||||
|
public class DisassemblerTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0x00E0, "CLS")]
|
||||||
|
[InlineData(0x00EE, "RET")]
|
||||||
|
[InlineData(0x00C4, "SCD 4")]
|
||||||
|
[InlineData(0x00D6, "SCU 6")]
|
||||||
|
[InlineData(0x00FB, "SCR")]
|
||||||
|
[InlineData(0x00FC, "SCL")]
|
||||||
|
[InlineData(0x00FD, "EXIT")]
|
||||||
|
[InlineData(0x00FE, "LORES")]
|
||||||
|
[InlineData(0x00FF, "HIRES")]
|
||||||
|
[InlineData(0x1234, "JP 0x234")]
|
||||||
|
[InlineData(0x2ABC, "CALL 0xABC")]
|
||||||
|
[InlineData(0x3A20, "SE VA, 0x20")]
|
||||||
|
[InlineData(0x4B30, "SNE VB, 0x30")]
|
||||||
|
[InlineData(0x5120, "SE V1, V2")]
|
||||||
|
[InlineData(0x5232, "SAVE V2-V3")]
|
||||||
|
[InlineData(0x5233, "LOAD V2-V3")]
|
||||||
|
[InlineData(0x6A55, "LD VA, 0x55")]
|
||||||
|
[InlineData(0x7A01, "ADD VA, 0x01")]
|
||||||
|
[InlineData(0x8010, "LD V0, V1")]
|
||||||
|
[InlineData(0x8014, "ADD V0, V1")]
|
||||||
|
[InlineData(0x8016, "SHR V0")]
|
||||||
|
[InlineData(0x8017, "SUBN V0, V1")]
|
||||||
|
[InlineData(0x801E, "SHL V0")]
|
||||||
|
[InlineData(0x9120, "SNE V1, V2")]
|
||||||
|
[InlineData(0xA300, "LD I, 0x300")]
|
||||||
|
[InlineData(0xB300, "JP V0, 0x300")]
|
||||||
|
[InlineData(0xC0FF, "RND V0, 0xFF")]
|
||||||
|
[InlineData(0xD125, "DRW V1, V2, 5")]
|
||||||
|
[InlineData(0xD120, "DRW V1, V2, 0")]
|
||||||
|
[InlineData(0xE09E, "SKP V0")]
|
||||||
|
[InlineData(0xE0A1, "SKNP V0")]
|
||||||
|
[InlineData(0xF007, "LD V0, DT")]
|
||||||
|
[InlineData(0xF00A, "LD V0, K")]
|
||||||
|
[InlineData(0xF029, "LD F, V0")]
|
||||||
|
[InlineData(0xF030, "LD HF, V0")]
|
||||||
|
[InlineData(0xF033, "LD B, V0")]
|
||||||
|
[InlineData(0xF055, "LD [I], V0")]
|
||||||
|
[InlineData(0xF065, "LD V0, [I]")]
|
||||||
|
[InlineData(0xF075, "LD R, V0")]
|
||||||
|
[InlineData(0xF085, "LD V0, R")]
|
||||||
|
[InlineData(0xF201, "PLANE 2")]
|
||||||
|
[InlineData(0xF002, "AUDIO")]
|
||||||
|
[InlineData(0xF03A, "PITCH V0")]
|
||||||
|
public void describesKnownOpcodes(int opcode, string expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, Disassembler.describe((ushort)opcode));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void describesLongLoadWithExtraWord()
|
||||||
|
{
|
||||||
|
Assert.Equal("LD I, 0x1234", Disassembler.describe(0xF000, 0x1234));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void unknownOpcodeRendersAsDataWord()
|
||||||
|
{
|
||||||
|
Assert.Equal("DW 0x8ABF", Disassembler.describe(0x8ABF));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void disassemblesProgramAndConsumesLongLoadAsFourBytes()
|
||||||
|
{
|
||||||
|
var program = new byte[] { 0xF0, 0x00, 0x12, 0x34, 0x00, 0xE0 };
|
||||||
|
var lines = Disassembler.disassemble(program);
|
||||||
|
|
||||||
|
Assert.Equal(2, lines.Count);
|
||||||
|
|
||||||
|
Assert.Equal(0x200, lines[0].address);
|
||||||
|
Assert.Equal(4, lines[0].byteLength);
|
||||||
|
Assert.Equal("LD I, 0x1234", lines[0].mnemonic);
|
||||||
|
|
||||||
|
Assert.Equal(0x204, lines[1].address);
|
||||||
|
Assert.Equal(2, lines[1].byteLength);
|
||||||
|
Assert.Equal("CLS", lines[1].mnemonic);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void renderIncludesAddressAndOpcode()
|
||||||
|
{
|
||||||
|
var program = new byte[] { 0x12, 0x34 };
|
||||||
|
string text = Disassembler.render(program);
|
||||||
|
Assert.Contains("0x0200:", text);
|
||||||
|
Assert.Contains("1234", text);
|
||||||
|
Assert.Contains("JP 0x234", text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Chip8.Core;
|
||||||
|
using Chip8.Core.Export;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Chip8.Tests;
|
||||||
|
|
||||||
|
public class PpmTests
|
||||||
|
{
|
||||||
|
static int headerLength(byte[] ppm)
|
||||||
|
{
|
||||||
|
int newlines = 0;
|
||||||
|
for (int index = 0; index < ppm.Length; index++)
|
||||||
|
{
|
||||||
|
if (ppm[index] == (byte)'\n' && ++newlines == 3)
|
||||||
|
{
|
||||||
|
return index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void monochromeImageHasCorrectHeaderAndSize()
|
||||||
|
{
|
||||||
|
var pixels = new bool[2 * 2];
|
||||||
|
byte[] ppm = PpmImage.fromMonochrome(pixels, 2, 2, Palette.monochrome());
|
||||||
|
|
||||||
|
string header = Encoding.ASCII.GetString(ppm, 0, headerLength(ppm));
|
||||||
|
Assert.Equal("P6\n2 2\n255\n", header);
|
||||||
|
Assert.Equal(headerLength(ppm) + 2 * 2 * 3, ppm.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void litAndUnlitPixelsUsePaletteColors()
|
||||||
|
{
|
||||||
|
var pixels = new bool[] { true, false, false, true };
|
||||||
|
var palette = new Palette(new Rgb(1, 2, 3), new Rgb(10, 20, 30));
|
||||||
|
byte[] ppm = PpmImage.fromMonochrome(pixels, 2, 2, palette);
|
||||||
|
int start = headerLength(ppm);
|
||||||
|
|
||||||
|
Assert.Equal(10, ppm[start]);
|
||||||
|
Assert.Equal(20, ppm[start + 1]);
|
||||||
|
Assert.Equal(30, ppm[start + 2]);
|
||||||
|
Assert.Equal(1, ppm[start + 3]);
|
||||||
|
Assert.Equal(2, ppm[start + 4]);
|
||||||
|
Assert.Equal(3, ppm[start + 5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void scaleMultipliesDimensions()
|
||||||
|
{
|
||||||
|
var pixels = new bool[1];
|
||||||
|
byte[] ppm = PpmImage.fromMonochrome(pixels, 1, 1, Palette.monochrome(), scale: 4);
|
||||||
|
string header = Encoding.ASCII.GetString(ppm, 0, headerLength(ppm));
|
||||||
|
Assert.Equal("P6\n4 4\n255\n", header);
|
||||||
|
Assert.Equal(headerLength(ppm) + 4 * 4 * 3, ppm.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void videoExportUsesColorPlanes()
|
||||||
|
{
|
||||||
|
var video = new Video();
|
||||||
|
video.setResolution(true);
|
||||||
|
var sprite = new byte[] { 0x80 };
|
||||||
|
video.drawSprite(sprite, 0, 0, 0, 1, 0x2, false);
|
||||||
|
|
||||||
|
byte[] ppm = PpmImage.fromVideo(video, Palette.octo());
|
||||||
|
int start = headerLength(ppm);
|
||||||
|
Rgb colorTwo = Palette.octo()[2];
|
||||||
|
|
||||||
|
Assert.Equal(colorTwo.r, ppm[start]);
|
||||||
|
Assert.Equal(colorTwo.g, ppm[start + 1]);
|
||||||
|
Assert.Equal(colorTwo.b, ppm[start + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void fromMachinePicksMonochromeForChip8()
|
||||||
|
{
|
||||||
|
var machine = new Machine(Quirks.modern(), 1);
|
||||||
|
machine.display[0] = true;
|
||||||
|
byte[] ppm = PpmImage.fromMachine(machine, Palette.monochrome());
|
||||||
|
int start = headerLength(ppm);
|
||||||
|
Assert.Equal(0xFF, ppm[start]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user