gbemu: refactor testing infrastructure

Similar to gbasm, break the testing into different files for sanity's
sake. This patch also adds a "test" register state which is compared
to the state of the actual CPU registers at the end of the test.

Finally, break the inc and dec tests, but add "ld r8, r8" and "ld dr,
d8" tests.
This commit is contained in:
2017-05-31 21:42:08 -07:00
parent 30df5164d6
commit 8dbcf353ab
5 changed files with 291 additions and 73 deletions

View File

@@ -23,15 +23,31 @@ struct gbasm_test r8_r8 = {
/* /*
* Data for LD R8, D8 tests * Data for LD R8, D8 tests
*/ */
static char ld_r8_d8_src[1024] = { 0 }; static char ld_r8_d8_src[] = "ld a, $12\n"
static uint8_t ld_r8_d8_output[0x40]; "ld b, $0\n"
"ld c, $0xff\n"
"ld d, $127\n"
"ld e, $0xfe\n"
"ld h, $-1\n"
"ld l, $-128\n"
"halt";
static uint8_t ld_r8_d8_output[] = {
0x3e, 0x0c,
0x06, 0x00,
0x0e, 0xff,
0x16, 0x7f,
0x1e, 0xfe,
0x26, 0xff,
0x2e, 0x80,
0x76,
};
struct gbasm_test r8_d8 = { struct gbasm_test r8_d8 = {
.init = init_r8_d8,
.name = "r8/d8", .name = "r8/d8",
.asm_source = ld_r8_d8_src, .asm_source = ld_r8_d8_src,
.expected_output = ld_r8_d8_output, .expected_output = ld_r8_d8_output,
.expected_output_len = 0, .expected_output_len = 15,
}; };
static const struct gbasm_test *tests[] = { static const struct gbasm_test *tests[] = {
@@ -105,26 +121,3 @@ void init_r8_r8(void)
gen_r8_r8_src(); gen_r8_r8_src();
gen_r8_r8_output(); gen_r8_r8_output();
} }
void gen_r8_d8_src(void)
{
snprintf(ld_r8_d8_src,
sizeof(ld_r8_d8_src),
"ld a, $12\n"
"ld b, $0\n"
"ld c, $0xff\n"
"ld d, $127\n"
"ld e, $0xfe\n"
"ld h, $-1\n"
"ld l, $-128");
}
void gen_r8_d8_output(void)
{
}
void init_r8_d8(void)
{
gen_r8_d8_src();
gen_r8_d8_output();
}

73
src/gbemu/tests/cpu/ld.c Normal file
View File

@@ -0,0 +1,73 @@
#include "gbemu/tests/cpu/test.h"
static struct gb_cpu_test test_ld_r8 = {
.init = NULL,
.expected_regs_type = GB_CPU_TEST_REGS_8,
.expected = {
.regs_8 = {
.a = 0x01,
.b = 0x02,
.c = 0x03,
.d = 0x03,
.e = 0x02,
.f = 0x00,
.h = 0x01,
.l = 0x00,
.sp = 0x0000,
.pc = 10,
}
},
.src_prog =
"inc a\n"
"inc b\n"
"inc b\n"
"inc c\n"
"inc c\n"
"inc c\n"
"ld d, c\n"
"ld e, b\n"
"ld h, a\n"
"halt",
.name = "/ld/r8",
};
static struct gb_cpu_test test_ld_d8 = {
.init = NULL,
.expected_regs_type = GB_CPU_TEST_REGS_8,
.expected = {
.regs_8 = {
.a = 0xaa,
.b = 0x55,
.c = 0xff,
.d = 0xb0,
.e = 0xde,
.f = 0x00,
.h = 0xbe,
.l = 0xef,
.sp = 0x0000,
.pc = 15,
}
},
.src_prog =
"ld a, $0xaa\n"
"ld b, $0x55\n"
"ld c, $0xff\n"
"ld d, $0xb0\n"
"ld e, $0xde\n"
"ld h, $0xbe\n"
"ld l, $0xef\n"
"halt",
.name = "/ld/d8",
};
struct gb_cpu_test* ld_tests[] = {
&test_ld_r8,
&test_ld_d8,
NULL
};

8
src/gbemu/tests/cpu/ld.h Normal file
View File

@@ -0,0 +1,8 @@
#ifndef GB_CPU_TEST_LD_H
#define GB_CPU_TEST_LD_H
#include "gbemu/tests/cpu/test.h"
extern struct gb_cpu_test* ld_tests;
#endif

View File

@@ -0,0 +1,67 @@
#ifndef GBEMU_CPU_TEST
#define GBEMU_CPU_TEST
#include "gbemu/cpu.h"
#include "common/common.h"
#include <stdint.h>
/**
* A structure (which is only used for testing) which provides an abstraction
* for comparing to the real CPU state, in case that definition should change.
*/
struct gb_cpu_test_regs_16 {
uint16_t bc;
uint16_t de;
uint16_t hl;
uint16_t af;
uint16_t sp;
uint16_t pc;
};
/**
* A structure (which is only used for testing) which provides an abstraction
* for comparing to the real CPU state, in case that definition should change.
*/
struct gb_cpu_test_regs_8 {
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
uint8_t e;
uint8_t h;
uint8_t l;
uint8_t f;
/**
* The SP and PC registers are only accessible via their 16-bit
* representations.
*/
uint16_t sp;
uint16_t pc;
};
enum gb_cpu_test_regs_type {
GB_CPU_TEST_REGS_8,
GB_CPU_TEST_REGS_16,
};
struct gb_cpu_test {
void (* init)(void);
/**
* As a convenience for writing tests, support writing the expected
* state of the registers in either their 8-bit or 16-bit forms.
*/
enum gb_cpu_test_regs_type expected_regs_type;
union {
struct gb_cpu_test_regs_8 regs_8;
struct gb_cpu_test_regs_16 regs_16;
} expected;
const char *src_prog;
const char *name;
};
#endif

View File

@@ -1,6 +1,11 @@
#include "gbasm/assemble.h" #include "gbasm/assemble.h"
#include "gbasm/emitter.h" #include "gbasm/emitter.h"
#include "gbemu/cpu.h" #include "gbemu/cpu.h"
#include "gbemu/tests/cpu/test.h"
#include "gbemu/tests/cpu/ld.h"
#include "common/common.h" #include "common/common.h"
#include <string.h> /* memset */ #include <string.h> /* memset */
@@ -8,6 +13,38 @@
static uint8_t mem[4096] = { 0 }; static uint8_t mem[4096] = { 0 };
static struct gb_cpu_test **tests[] = {
&ld_tests,
};
static void gb_cpu_test_cmp_regs_8(const struct lr35902_state *cpu,
const struct gb_cpu_test_regs_8 *test_regs)
{
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_A), ==, test_regs->a);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_B), ==, test_regs->b);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_C), ==, test_regs->c);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_D), ==, test_regs->d);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_E), ==, test_regs->e);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_H), ==, test_regs->h);
g_assert_cmpint(lr35902_get_reg_8(cpu, LR35902_REG_L), ==, test_regs->l);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_SP), ==, test_regs->sp);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_PC), ==, test_regs->pc);
}
static void gb_cpu_test_cmp_regs_16(const struct lr35902_state *cpu,
const struct gb_cpu_test_regs_16 *test_regs)
{
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_A), ==, test_regs->af);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_B), ==, test_regs->bc);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_C), ==, test_regs->de);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_D), ==, test_regs->hl);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_SP), ==, test_regs->sp);
g_assert_cmpint(lr35902_get_reg_16(cpu, LR35902_REG_PC), ==, test_regs->pc);
}
static uint8_t mem_read(struct lr35902_state *cpu, uint16_t addr) static uint8_t mem_read(struct lr35902_state *cpu, uint16_t addr)
{ {
return mem[addr]; return mem[addr];
@@ -18,15 +55,20 @@ static void mem_write(struct lr35902_state *cpu, uint16_t addr, uint8_t val)
mem[addr] = val; mem[addr] = val;
} }
static void test_inc(const void *test_data) static void run_cpu_test(const void *test_data)
{ {
const struct gb_cpu_test *test = test_data;
struct emitter emitter; struct emitter emitter;
struct buffer_emitter buffer_emitter; struct buffer_emitter buffer_emitter;
struct lr35902_state cpu; struct lr35902_state cpu;
char *src; char *src;
int rc; int rc;
src = strdup("inc a\n inc b\n halt\n"); if (test->init != NULL) {
test->init();
}
src = strdup(test->src_prog);
memset(mem, 0, sizeof(mem)); memset(mem, 0, sizeof(mem));
buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem)); buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem));
@@ -38,56 +80,91 @@ static void test_inc(const void *test_data)
lr35902_cycle(&cpu); lr35902_cycle(&cpu);
} }
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_A), ==, 1); switch(test->expected_regs_type) {
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_B), ==, 1); case GB_CPU_TEST_REGS_8:
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_C), ==, 0); gb_cpu_test_cmp_regs_8(&cpu, &test->expected.regs_8);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_D), ==, 0); break;
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_E), ==, 0); case GB_CPU_TEST_REGS_16:
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_H), ==, 0); gb_cpu_test_cmp_regs_16(&cpu, &test->expected.regs_16);
break;
free(src); default:
} g_assert(0);
static void test_dec(const void *test_data)
{
struct emitter emitter;
struct buffer_emitter buffer_emitter;
struct lr35902_state cpu;
char *src;
int rc;
src = strdup("dec a\n inc c\n dec c\n dec de\n halt\n");
memset(mem, 0, sizeof(mem));
buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem));
rc = gbasm_assemble(src, &emitter);
g_assert_cmpint(rc, ==, 0);
lr35902_init(&cpu, mem_read, mem_write);
while (!cpu.halted) {
lr35902_cycle(&cpu);
} }
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_A), ==, 0xff);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_B), ==, 0);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_C), ==, 0);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_D), ==, 0xff);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_E), ==, 0xff);
g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_H), ==, 0);
g_assert_cmpint(lr35902_get_reg_16(&cpu, LR35902_REG_DE), ==, 0xffff);
free(src); free(src);
} }
/* static void test_inc(const void *test_data) */
/* { */
/* struct emitter emitter; */
/* struct buffer_emitter buffer_emitter; */
/* struct lr35902_state cpu; */
/* char *src; */
/* int rc; */
/* src = strdup("inc a\n inc b\n halt\n"); */
/* memset(mem, 0, sizeof(mem)); */
/* buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem)); */
/* rc = gbasm_assemble(src, &emitter); */
/* g_assert_cmpint(rc, ==, 0); */
/* lr35902_init(&cpu, mem_read, mem_write); */
/* while (!cpu.halted) { */
/* lr35902_cycle(&cpu); */
/* } */
/* free(src); */
/* } */
/* static void test_dec(const void *test_data) */
/* { */
/* struct emitter emitter; */
/* struct buffer_emitter buffer_emitter; */
/* struct lr35902_state cpu; */
/* char *src; */
/* int rc; */
/* src = strdup("dec a\n inc c\n dec c\n dec de\n halt\n"); */
/* memset(mem, 0, sizeof(mem)); */
/* buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem)); */
/* rc = gbasm_assemble(src, &emitter); */
/* g_assert_cmpint(rc, ==, 0); */
/* lr35902_init(&cpu, mem_read, mem_write); */
/* while (!cpu.halted) { */
/* lr35902_cycle(&cpu); */
/* } */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_A), ==, 0xff); */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_B), ==, 0); */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_C), ==, 0); */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_D), ==, 0xff); */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_E), ==, 0xff); */
/* g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_H), ==, 0); */
/* g_assert_cmpint(lr35902_get_reg_16(&cpu, LR35902_REG_DE), ==, 0xffff); */
/* free(src); */
/* } */
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char test_name_buff[256];
g_test_init(&argc, &argv, NULL); g_test_init(&argc, &argv, NULL);
g_test_add_data_func("/gbemu/cpu/inc", NULL, test_inc); for (int i = 0; i < ARRAY_SIZE(tests); i++) {
g_test_add_data_func("/gbemu/cpu/dec", NULL, test_dec); for (int j = 0; tests[i][j] != NULL; j++) {
snprintf(test_name_buff, sizeof(test_name_buff),
"/gbemu/cpu%s",
tests[i][j]->name);
g_test_add_data_func(test_name_buff, tests[i][j], run_cpu_test);
}
}
return g_test_run(); return g_test_run();
} }