From 8dbcf353abe608df5107532486f038d606dbd458 Mon Sep 17 00:00:00 2001 From: Max Regan Date: Wed, 31 May 2017 21:42:08 -0700 Subject: [PATCH] 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. --- src/gbasm/tests/ld.c | 47 +++++------ src/gbemu/tests/cpu/ld.c | 73 ++++++++++++++++ src/gbemu/tests/cpu/ld.h | 8 ++ src/gbemu/tests/cpu/test.h | 67 +++++++++++++++ src/gbemu/tests/test.c | 169 +++++++++++++++++++++++++++---------- 5 files changed, 291 insertions(+), 73 deletions(-) create mode 100644 src/gbemu/tests/cpu/ld.c create mode 100644 src/gbemu/tests/cpu/ld.h create mode 100644 src/gbemu/tests/cpu/test.h diff --git a/src/gbasm/tests/ld.c b/src/gbasm/tests/ld.c index 2a0545e..de9a6db 100644 --- a/src/gbasm/tests/ld.c +++ b/src/gbasm/tests/ld.c @@ -23,15 +23,31 @@ struct gbasm_test r8_r8 = { /* * Data for LD R8, D8 tests */ -static char ld_r8_d8_src[1024] = { 0 }; -static uint8_t ld_r8_d8_output[0x40]; +static char 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\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 = { - .init = init_r8_d8, .name = "r8/d8", .asm_source = ld_r8_d8_src, .expected_output = ld_r8_d8_output, - .expected_output_len = 0, + .expected_output_len = 15, }; static const struct gbasm_test *tests[] = { @@ -105,26 +121,3 @@ void init_r8_r8(void) gen_r8_r8_src(); 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(); -} diff --git a/src/gbemu/tests/cpu/ld.c b/src/gbemu/tests/cpu/ld.c new file mode 100644 index 0000000..54aa4db --- /dev/null +++ b/src/gbemu/tests/cpu/ld.c @@ -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 +}; diff --git a/src/gbemu/tests/cpu/ld.h b/src/gbemu/tests/cpu/ld.h new file mode 100644 index 0000000..ee97f9f --- /dev/null +++ b/src/gbemu/tests/cpu/ld.h @@ -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 diff --git a/src/gbemu/tests/cpu/test.h b/src/gbemu/tests/cpu/test.h new file mode 100644 index 0000000..ba1fbc8 --- /dev/null +++ b/src/gbemu/tests/cpu/test.h @@ -0,0 +1,67 @@ +#ifndef GBEMU_CPU_TEST +#define GBEMU_CPU_TEST + +#include "gbemu/cpu.h" + +#include "common/common.h" + +#include + +/** + * 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 diff --git a/src/gbemu/tests/test.c b/src/gbemu/tests/test.c index 33fca78..6368901 100644 --- a/src/gbemu/tests/test.c +++ b/src/gbemu/tests/test.c @@ -1,6 +1,11 @@ #include "gbasm/assemble.h" #include "gbasm/emitter.h" + #include "gbemu/cpu.h" + +#include "gbemu/tests/cpu/test.h" +#include "gbemu/tests/cpu/ld.h" + #include "common/common.h" #include /* memset */ @@ -8,6 +13,38 @@ 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) { return mem[addr]; @@ -18,18 +55,23 @@ static void mem_write(struct lr35902_state *cpu, uint16_t addr, uint8_t 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 buffer_emitter buffer_emitter; struct lr35902_state cpu; - char *src; + char *src; 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)); buffer_emitter_init(&emitter, &buffer_emitter, mem, sizeof(mem)); - + rc = gbasm_assemble(src, &emitter); g_assert_cmpint(rc, ==, 0); @@ -38,56 +80,91 @@ static void test_inc(const void *test_data) lr35902_cycle(&cpu); } - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_A), ==, 1); - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_B), ==, 1); - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_C), ==, 0); - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_D), ==, 0); - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_E), ==, 0); - g_assert_cmpint(lr35902_get_reg_8(&cpu, LR35902_REG_H), ==, 0); - - 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); + switch(test->expected_regs_type) { + case GB_CPU_TEST_REGS_8: + gb_cpu_test_cmp_regs_8(&cpu, &test->expected.regs_8); + break; + case GB_CPU_TEST_REGS_16: + gb_cpu_test_cmp_regs_16(&cpu, &test->expected.regs_16); + break; + default: + g_assert(0); } - 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); } + +/* 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) { + char test_name_buff[256]; + g_test_init(&argc, &argv, NULL); - - g_test_add_data_func("/gbemu/cpu/inc", NULL, test_inc); - g_test_add_data_func("/gbemu/cpu/dec", NULL, test_dec); - + + for (int i = 0; i < ARRAY_SIZE(tests); i++) { + 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(); }