#include "doctest.h" #include #include TEST_CASE("simple load and add") { u8 test_ram[] = { 0x3E, 0x10, // LD A, $0x10 0x87, // ADD A, A }; RAM r(test_ram, 3, true); Cpu cpu(&r); CHECK(cpu.state.PC == 0x0); cpu.step(); CHECK(cpu.state.PC == 0x2); CHECK(cpu.state.A == 0x10); cpu.step(); CHECK(cpu.state.PC == 0x3); CHECK(cpu.state.A == 0x20); } TEST_CASE("direct jump") { u8 test_ram[] = { 0xC3, 0x05, 0x00, // JMP $0x0005 0x00, 0x00, // NOP NOP 0xC3, 0x00, 0x00, // JMP $0x0000 }; RAM r(test_ram, 8, true); Cpu cpu(&r); CHECK(cpu.state.PC == 0x0000); cpu.step(); CHECK(cpu.state.PC == 0x0005); cpu.step(); CHECK(cpu.state.PC == 0x0000); } TEST_CASE("LD HL, nn; LD A, [HL]; LD A, n; LD [HL], A") { u8 test_ram[] = { 0x21, 0x20, 0x00, // LD HL, $0x0020 0x7E, // LD A, [HL] 0x3E,0x5A, // LD A, $0x5A 0x77, // LD [HL], A 0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0xA5, // . = 0x0020 }; RAM r(test_ram, 0x0021, false); Cpu cpu(&r); CHECK(cpu.state.PC == 0x0); cpu.step(); CHECK(cpu.state.PC == 0x3); CHECK(cpu.state.HL == 0x0020); CHECK(cpu.state.A == 0x0); cpu.step(); CHECK(cpu.state.PC == 0x4); CHECK(cpu.state.A == 0xA5); cpu.step(); CHECK(cpu.state.PC == 0x6); CHECK(cpu.state.A == 0x5A); cpu.step(); CHECK(cpu.state.PC == 0x7); CHECK(test_ram[0x20] == 0x5A); } TEST_CASE("PUSH rr ; POP rr") { u8 test_ram[] = { /* 0x0000 */ 0x31, 0x10, 0x00, // LD SP, $0x0010 /* 0x0003 */ 0x01, 0xAA, 0x55, // LD BC, $0x55AA /* 0x0006 */ 0xC5, // PUSH BC /* 0x0007 */ 0x01, 0x55, 0xAA, // LD BC, $0xAA55 /* 0x000A */ 0xD1, // POP DE /* 0x000B */ 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x0010 */ 0x12, 0x34, }; RAM r(test_ram, 0x0012, false); Cpu cpu(&r); cpu.step(); CHECK(cpu.state.PC == 0x0003); CHECK(cpu.state.SP == 0x0010); cpu.step(); CHECK(cpu.state.PC == 0x0006); CHECK(cpu.state.BC == 0x55AA); cpu.step(); CHECK(cpu.state.PC == 0x0007); CHECK(cpu.state.SP == 0x000E); CHECK(test_ram[0x000E] == 0xAA); CHECK(test_ram[0x000F] == 0x55); cpu.step(); CHECK(cpu.state.PC == 0x000A); CHECK(cpu.state.BC == 0xAA55); cpu.step(); CHECK(cpu.state.PC == 0x000B); CHECK(cpu.state.DE == 0x55AA); } TEST_CASE("CALL ; RET") { u8 test_ram[] = { /* 0x0000 */ 0x31, 0x10, 0x00, // LD SP, $0x0010 /* 0x0003 */ 0xCD, 0x0A, 0x00, // CALL $0x000A /* 0x0006 */ 0x00, 0x00, 0x00, 0x00, // 4 x NOP /* 0x000A */ 0x00, // NOP /* 0x000B */ 0xC9, // RET /* 0x000C */ 0xaa, 0xaa, 0xaa, 0xaa, /* 0x000D */ 0xaa, 0xaa, 0xaa, /* 0x0010 */ }; RAM r(test_ram, 0x10, false); Cpu cpu(&r); cpu.step(); // LD SP, $0x0010 CHECK(cpu.state.PC == 0x0003); CHECK(cpu.state.SP == 0x0010); cpu.step(); // CALL $0x000A CHECK(cpu.state.PC == 0x000A); CHECK(cpu.state.SP == 0x000E); CHECK(test_ram[0x000F] == 0x00); CHECK(test_ram[0x000E] == 0x06); cpu.step(); // NOP CHECK(cpu.state.PC == 0x000B); CHECK(cpu.state.SP == 0x000E); CHECK(test_ram[0x000F] == 0x00); CHECK(test_ram[0x000E] == 0x06); cpu.step(); // RET CHECK(cpu.state.PC == 0x0006); CHECK(cpu.state.SP == 0x0010); } TEST_CASE("JR e") { u8 test_ram[] = { 0x18, 0x02, 0x00, 0x00, 0x18, (u8)(-0x06), }; RAM r(test_ram, 0x6, true); Cpu cpu(&r); CHECK(cpu.state.PC == 0x0); cpu.step(); CHECK(cpu.state.PC == 0x4); cpu.step(); CHECK(cpu.state.PC == 0x0); } TEST_CASE("RST op leads to correct call") { u8 test_ram[] = { 0x00 }; RAM r(test_ram, 0x1, true); Cpu cpu(&r); u16 expected_pc; SUBCASE("RST $00") { test_ram[0] = 0xC7; expected_pc = 0x00; } SUBCASE("RST $08") { test_ram[0] = 0xCF; expected_pc = 0x08; } SUBCASE("RST $10") { test_ram[0] = 0xD7; expected_pc = 0x10; } SUBCASE("RST $18") { test_ram[0] = 0xDF; expected_pc = 0x18; } SUBCASE("RST $20") { test_ram[0] = 0xE7; expected_pc = 0x20; } SUBCASE("RST $28") { test_ram[0] = 0xEF; expected_pc = 0x28; } SUBCASE("RST $30") { test_ram[0] = 0xF7; expected_pc = 0x30; } SUBCASE("RST $38") { test_ram[0] = 0xFF; expected_pc = 0x38; } cpu.step(); CHECK(cpu.state.PC == expected_pc); } TEST_CASE("DAA") { u8 test_ram[] = { 0x80, // ADD A,B 0x27, // DAA 0x90, // SUB A,B 0x27, // DAA }; RAM r(test_ram, 0x4, true); Cpu cpu(&r); cpu.state.A = 0x45; cpu.state.B = 0x38; cpu.step(); CHECK(cpu.state.A == 0x7D); cpu.step(); CHECK(cpu.state.A == 0x83); cpu.step(); CHECK(cpu.state.A == 0x4B); cpu.step(); CHECK(cpu.state.A == 0x45); } TEST_CASE("HALT exits on interrupt") { u8 test_ram[] = { 0x76, 0x00, 0x00, }; RAM r(test_ram, 0x3, true); Cpu cpu(&r); cpu.state.IE = INT_MASK; cpu.state.IF = 0; cpu.state.IME = IME_ON; CHECK(cpu.state.halted == false); cpu.step(); CHECK(cpu.state.halted == true); CHECK(cpu.state.PC == 0x01); cpu.step(); CHECK(cpu.state.halted == true); CHECK(cpu.state.PC == 0x01); cpu.step(); CHECK(cpu.state.halted == true); CHECK(cpu.state.PC == 0x01); cpu.signalInterrupt(INT_LCDSTAT); CHECK(cpu.state.IF == INT_LCDSTAT); cpu.step(); CHECK(cpu.state.halted == false); CHECK(cpu.state.IF == 0); CHECK(cpu.state.IME == IME_OFF); CHECK(cpu.state.PC == 0x48); }