diff --git a/.gitignore b/.gitignore index 18eb94a..bdf39ee 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ roms/* !roms/README crater crater-dev +tests/runner +tests/asm/*.gg diff --git a/LICENSE b/LICENSE index 0764864..1811091 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 2014-2015 Ben Kurtovic +Copyright (C) 2014-2016 Ben Kurtovic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 948fb94..fcd8097 100644 --- a/README.md +++ b/README.md @@ -24,12 +24,19 @@ Installing ---------- Only OS X and Linux are tested. You'll need a modern compiler that supports C11 -(clang preferred) and SDL 2. Using Homebrew, you can `brew install sdl2`; using -apt, you can `apt-get install libsdl2-dev`. +([clang][clang] preferred) and [SDL 2][sdl2]. Using Homebrew, you can +`brew install sdl2`; using apt, you can `apt-get install libsdl2-dev`. Run `make` to create `./crater`. To build the development version with debug -symbols (they can exist simultaneously), run `make DEBUG=1`, which creates -`./crater-dev`. This also enables the printing of debugging info to stdout. +symbols and extra diagnostic info (they can exist simultaneously), run +`make DEBUG=1`, which creates `./crater-dev`. + +crater has a number of test cases. Run the entire suite with `make test`; +individual components can be tested by doing `make test-{component}`, where +`{component}` is one of `cpu`, `vdp`, `psg`, `asm`, `dis`, or `integrate`. + +[clang]: http://clang.llvm.org/ +[sdl2]: https://www.libsdl.org/ Usage ----- diff --git a/makefile b/makefile index fdf785b..6793148 100644 --- a/makefile +++ b/makefile @@ -1,50 +1,59 @@ -# Copyright (C) 2014 Ben Kurtovic +# Copyright (C) 2014-2016 Ben Kurtovic # Released under the terms of the MIT License. See LICENSE for details. PROGRAM = crater SOURCES = src BUILD = build DEVEXT = -dev +TESTS = cpu vdp psg asm dis integrate CC = clang FLAGS = -O2 -Wall -Wextra -pedantic -std=c11 CFLAGS = $(shell sdl2-config --cflags) LIBS = $(shell sdl2-config --libs) +DFLAGS = -g -DDEBUG_MODE MKDIR = mkdir -p RM = rm -rf ASM_UP = scripts/update_asm_instructions.py MODE = release BNRY = $(PROGRAM) +FLGS = $(FLAGS) SDRS = $(shell find $(SOURCES) -type d | xargs echo) SRCS = $(filter-out %.inc.c,$(foreach d,. $(SDRS),$(wildcard $(addprefix $(d)/*,.c)))) OBJS = $(patsubst %.c,%.o,$(addprefix $(BUILD)/$(MODE)/,$(SRCS))) DEPS = $(OBJS:%.o=%.d) DIRS = $(sort $(dir $(OBJS))) +TCPS = $(addprefix test-,$(TESTS)) ifdef DEBUG - BNRY := $(BNRY)$(DEVEXT) - FLAGS += -g -DDEBUG_MODE - MODE = debug + BNRY := $(BNRY)$(DEVEXT) + FLGS += $(DFLAGS) + MODE = debug endif -.PHONY: all clean test test-all test-z80 test-asm test-dasm +export CC +export FLAGS +export RM + +.PHONY: all clean test tests test-prereqs test-make-prereqs $(TCPS) all: $(BNRY) clean: $(RM) $(BUILD) $(PROGRAM) $(PROGRAM)$(DEVEXT) + @$(MAKE) -C tests clean $(DIRS): $(MKDIR) $@ $(BNRY): $(OBJS) - $(CC) $(FLAGS) $(LIBS) $(OBJS) -o $@ + $(CC) $(FLGS) $(LIBS) $(OBJS) -o $@ $(OBJS): | $(DIRS) $(BUILD)/$(MODE)/%.o: %.c - $(CC) $(FLAGS) $(CFLAGS) -MMD -MP -c $< -o $@ + $(CC) $(FLGS) $(CFLAGS) -MMD -MP -c $< -o $@ -include $(DEPS) @@ -52,16 +61,16 @@ ASM_INST = $(SOURCES)/assembler/instructions $(ASM_INST).inc.c: $(ASM_INST).yml $(ASM_UP) python $(ASM_UP) -test: test-all test-z80 test-asm test-dasm +test-prereqs: $(PROGRAM) + @: # No-op; prevents make from cluttering output with "X is up to date" -test-all: - @echo "running all tests" +test-make-prereqs: + @$(MAKE) test-prereqs DEBUG= -test-z80: - @echo "running Z80 CPU tests" +test: test-make-prereqs + @$(MAKE) -C tests -s all -test-asm: - @echo "running assembler tests" +tests: test -test-dasm: - @echo "running disassembler tests" +$(TCPS): test-make-prereqs + @$(MAKE) -C tests -s $(subst test-,,$@) diff --git a/src/assembler/instructions.c b/src/assembler/instructions.c index 04e0cea..e268ebf 100644 --- a/src/assembler/instructions.c +++ b/src/assembler/instructions.c @@ -98,8 +98,6 @@ static ASMErrorDesc parse_inst_##mnemonic( \ #define INST_INDEX_PREFIX(n) INST_PREFIX_(INST_INDEX(n).reg) -/* ----------------------------- END WORK BLOCK ---------------------------- */ - /* Fill an instruction's byte array with the given data. diff --git a/src/assembler/tokenizer.c b/src/assembler/tokenizer.c index c55b51e..2028a45 100644 --- a/src/assembler/tokenizer.c +++ b/src/assembler/tokenizer.c @@ -276,6 +276,21 @@ static bool parse_space( } /* + Parse a string like parse_string(), but null-terminate it. +*/ +static bool parse_cstring( + char **result, size_t *length, const char *arg, ssize_t size) +{ + if (!parse_string(result, length, arg, size)) + return false; + + (*length)++; + *result = cr_realloc(*result, sizeof(char) * (*length)); + (*result)[*length - 1] = '\0'; + return true; +} + +/* Parse data encoded in a line into an ASMData object. On success, return NULL and store the instruction in *data_ptr. On failure, @@ -285,7 +300,7 @@ static ErrorInfo* parse_data( const ASMLine *line, ASMData **data_ptr, size_t offset) { const char *directive; - parser_func parser = (parser_func) parse_string; + parser_func parser; if (IS_DIRECTIVE(line, DIR_BYTE)) { directive = DIR_BYTE; @@ -295,10 +310,13 @@ static ErrorInfo* parse_data( parser = parse_space; } else if (IS_DIRECTIVE(line, DIR_ASCII)) { directive = DIR_ASCII; + parser = (parser_func) parse_string; } else if (IS_DIRECTIVE(line, DIR_ASCIZ)) { directive = DIR_ASCIZ; + parser = (parser_func) parse_cstring; } else if (IS_DIRECTIVE(line, DIR_ASCIIZ)) { directive = DIR_ASCIIZ; + parser = (parser_func) parse_cstring; } else { return error_info_create(line, ET_PREPROC, ED_PP_UNKNOWN); } diff --git a/src/config.c b/src/config.c index 1777772..d13fb14 100644 --- a/src/config.c +++ b/src/config.c @@ -90,7 +90,7 @@ static int get_rom_paths(char ***path_ptr) } closedir(dirp); } else { - WARN_ERRNO("couldn't open 'roms/'") + WARN_ERRNO("couldn't open '" ROMS_DIR "/'") } *path_ptr = paths; return npaths; diff --git a/src/logging.h b/src/logging.h index a40e3cd..16e2a90 100644 --- a/src/logging.h +++ b/src/logging.h @@ -10,12 +10,13 @@ /* Internal usage only */ -#define LOG_MSG_(dest, level, extra, after, ...) { \ +#define LOG_MSG_(dest, level, extra, after, ...) \ + do { \ fprintf(dest, level ": " __VA_ARGS__); \ extra; \ fprintf(dest, "\n"); \ after; \ - } + } while (0); #define LOG_ERR_(...) LOG_MSG_(stderr, __VA_ARGS__) #define LOG_OUT_(...) LOG_MSG_(stdout, __VA_ARGS__) diff --git a/src/rom.c b/src/rom.c index 2083dd6..1185f7b 100644 --- a/src/rom.c +++ b/src/rom.c @@ -32,7 +32,7 @@ static void print_header(const uint8_t *header) snprintf(&header_chr[3 * i], 3, "%2c", header[i]); else { header_chr[3 * i] = ' '; - header_chr[3 * i + 1] = '?'; + header_chr[3 * i + 1] = '.'; } header_hex[3 * i + 2] = header_chr[3 * i + 2] = ' '; } diff --git a/src/z80_ops.inc.c b/src/z80_ops.inc.c index 787f590..835f5ff 100644 --- a/src/z80_ops.inc.c +++ b/src/z80_ops.inc.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic +/* Copyright (C) 2014-2016 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ /* diff --git a/tests/asm/01-empty.asm b/tests/asm/01-empty.asm new file mode 100644 index 0000000..e69de29 diff --git a/tests/asm/02-headers1.asm b/tests/asm/02-headers1.asm new file mode 100644 index 0000000..db2169e --- /dev/null +++ b/tests/asm/02-headers1.asm @@ -0,0 +1,16 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 02-headers1.asm +; Basic test for headers and other directives, mostly using default values + +.rom_size auto +.rom_header auto +.rom_product 0 +.rom_version 0 +.rom_region "GG Export" +.rom_checksum off +.rom_declsize auto +.cross_blocks auto diff --git a/tests/asm/03-headers2.asm b/tests/asm/03-headers2.asm new file mode 100644 index 0000000..59502df --- /dev/null +++ b/tests/asm/03-headers2.asm @@ -0,0 +1,16 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 03-headers2.asm +; Header/directive test using non-default values + +.rom_size "64 KB" +.rom_header $7FF0 +.rom_product 101893 +.rom_version 3 +.rom_region "GG International" +.rom_checksum on +.rom_declsize "32 KB" +.cross_blocks off diff --git a/tests/asm/04-basic.asm b/tests/asm/04-basic.asm new file mode 100644 index 0000000..fd2ec81 --- /dev/null +++ b/tests/asm/04-basic.asm @@ -0,0 +1,21 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 04-basic.asm +; Basic instruction test + +.org $0000 +main: + di + ld a, $23 + inc a + call foo + +foo: + push bc + xor c + ret z + rrca + ret diff --git a/tests/asm/05-includes.asm b/tests/asm/05-includes.asm new file mode 100644 index 0000000..42c3b29 --- /dev/null +++ b/tests/asm/05-includes.asm @@ -0,0 +1,17 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 05-includes.asm +; Source file inclusion test, involving cross-file label references and complex +; origins + +.include "05.inc1.asm" + +.org $0000 +main: + di + ld c, $FA + inc c + call bar diff --git a/tests/asm/05.inc1.asm b/tests/asm/05.inc1.asm new file mode 100644 index 0000000..1a2fe8c --- /dev/null +++ b/tests/asm/05.inc1.asm @@ -0,0 +1,13 @@ +.org $0100 + +.include "05.inc2.asm" +.include "05.inc3.asm" + +bar: + push de + call blah + xor d + ret pe + call testfunc + exx + ret diff --git a/tests/asm/05.inc2.asm b/tests/asm/05.inc2.asm new file mode 100644 index 0000000..83b7a98 --- /dev/null +++ b/tests/asm/05.inc2.asm @@ -0,0 +1,3 @@ +testfunc: + sub b + ret diff --git a/tests/asm/05.inc3.asm b/tests/asm/05.inc3.asm new file mode 100644 index 0000000..aa42dc4 --- /dev/null +++ b/tests/asm/05.inc3.asm @@ -0,0 +1,2 @@ +.include "05.inc4.asm" +.rom_product 57005 diff --git a/tests/asm/05.inc4.asm b/tests/asm/05.inc4.asm new file mode 100644 index 0000000..4e2f059 --- /dev/null +++ b/tests/asm/05.inc4.asm @@ -0,0 +1,3 @@ +blah: + ei + ret diff --git a/tests/asm/06-formatting.asm b/tests/asm/06-formatting.asm new file mode 100644 index 0000000..b15f805 --- /dev/null +++ b/tests/asm/06-formatting.asm @@ -0,0 +1,16 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 06-formatting.asm +; Complex formatting test, involving strange indentation and newline usage + +foo: .inCLUde "06.inc.asm" + +.ORG $0100 + +ayy: lmao: + di + InC a +ret diff --git a/tests/asm/06.inc.asm b/tests/asm/06.inc.asm new file mode 100644 index 0000000..199e02b --- /dev/null +++ b/tests/asm/06.inc.asm @@ -0,0 +1,13 @@ +test1:test2: test3: test4: + + + + inc a + +inc b +inc c + + ret + + + diff --git a/tests/asm/07-data.asm b/tests/asm/07-data.asm new file mode 100644 index 0000000..c6ff13b --- /dev/null +++ b/tests/asm/07-data.asm @@ -0,0 +1,31 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 07-data.asm +; Test of data directives to fill ROM with non-code bytes + +.org $1000 + +str1: .ascii "Hello, world!" +str2: .asciz "World, hello!" +str3: .asciiz "foobar" + +arr1: .byte 1 2 3 4 5 6 +arr2: .byte 14 $14 $FF 255 $F0 +arr3: .byte 8, $10, 4 5 6, 8, 10 + +void1: .space 3 +void2: .space 6 $D0 +void3: .space 128 $DE + +.org $0000 +main: + di +loop: + jp loop + +.org $0066 +nmi: + retn diff --git a/tests/asm/08-instructions.asm b/tests/asm/08-instructions.asm new file mode 100644 index 0000000..a9859b6 --- /dev/null +++ b/tests/asm/08-instructions.asm @@ -0,0 +1,103 @@ +;; Copyright (C) 2016 Ben Kurtovic +;; Released under the terms of the MIT License. See LICENSE for details. + +; ----- CRATER UNIT TESTING SUITE --------------------------------------------- + +; 08-instructions.asm +; Exhaustive test of instruction syntax + +.define COUNTER $C010 +.define OFFSET 6 +.define OFFSET2 157 + +.org $1000 + +str1: .ascii "Hello, world!" + +.org $0000 +main: + di + call inst + halt + +.org $0066 +nmi: + retn + +.org $2000 +inst: + inc a + inc c + inc hl + inc sp + inc (hl) + inc ( hl ) + inc ix + inc iyl + inc (ix) + inc ( ix ) + inc (iy+0) + inc (ix+3) + inc (ix+OFFSET) + inc (ix-OFFSET) + inc ( ix + 7 ) + inc (ix-8) + inc (ix - +9) + inc (iy+127) + inc (iy-128) + + add a, e + add a, 5 + add a, $12 + add a, (hl) + add a, (ix+4) + add hl, bc + add ix, de + + adc a, e + adc a, 5 + adc a, $12 + adc a, (hl) + adc a, (ix+4) + adc hl, bc + + rlca + rrca + rla + rra + daa + cpl + scf + ccf + halt + exx + ei + di + cpd + cpdr + cpi + cpir + ind + indr + ini + inir + ldd + lddr + ldi + ldir + otdr + otir + outd + outi + rrd + rld + + ld hl, COUNTER ; H contains $C0, L contains $10 + ld d, (hl) ; D contains $FF (garbage) + ld c, $3B ; C contains $3B + ld (hl), c ; memory address $C010 contains $3B + + ld hl, str1 + ld b, (hl) ; B contains 'H' + ld hl, (str1) ; H contains 'e', L contains 'H' + ld (hl), b ; error, writing to cartridge ROM diff --git a/tests/asm/manifest b/tests/asm/manifest new file mode 100644 index 0000000..75a5147 --- /dev/null +++ b/tests/asm/manifest @@ -0,0 +1,8 @@ +01-empty.asm 01-empty.gg +02-headers1.asm 02-headers1.gg +03-headers2.asm 03-headers2.gg +04-basic.asm 04-basic.gg +05-includes.asm 05-includes.gg +06-formatting.asm 06-formatting.gg +07-data.asm 07-data.gg +08-instructions.asm 08-instructions.gg diff --git a/tests/asm/roms.tar.gz b/tests/asm/roms.tar.gz new file mode 100644 index 0000000..10d3d10 Binary files /dev/null and b/tests/asm/roms.tar.gz differ diff --git a/tests/z80/01_basic_math.asm b/tests/cpu/01_basic_math.asm similarity index 100% rename from tests/z80/01_basic_math.asm rename to tests/cpu/01_basic_math.asm diff --git a/tests/z80/_header.asm b/tests/cpu/_header.asm similarity index 100% rename from tests/z80/_header.asm rename to tests/cpu/_header.asm diff --git a/tests/makefile b/tests/makefile new file mode 100644 index 0000000..fb7b410 --- /dev/null +++ b/tests/makefile @@ -0,0 +1,27 @@ +# Copyright (C) 2014-2016 Ben Kurtovic +# Released under the terms of the MIT License. See LICENSE for details. + +RUNNER = runner +COMPONENTS = cpu vdp psg asm dis integrate + +.PHONY: all clean $(COMPONENTS) + +all: $(COMPONENTS) + +clean: + $(RM) $(RUNNER) + $(RM) asm/*.gg + +$(RUNNER): $(RUNNER).c + $(CC) $(FLAGS) $< -o $@ + +$(COMPONENTS): $(RUNNER) + ./$(RUNNER) $@ + +asm: asm-unarchive + +asm-archive: + tar -czf asm/roms.tar.gz asm/*.gg + +asm-unarchive: + tar -xf asm/roms.tar.gz diff --git a/tests/runner.c b/tests/runner.c index 1763ea4..10c27ab 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -1,4 +1,274 @@ -/* Copyright (C) 2014-2015 Ben Kurtovic +/* Copyright (C) 2014-2016 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ -// ... +#include +#include +#include +#include +#include + +#include "../src/logging.h" +#include "../src/util.h" + +#define ASM_PREFIX "asm/" +#define ASM_OUTFILE ASM_PREFIX ".output.gg" + +/* Helper macros for reporting test passings/failures */ + +#define PASS_TEST() \ + do { \ + printf("."); \ + fflush(stdout); \ + passed_tests++; \ + pending_nl = true; \ + } while(0); + +#define FAIL_MSG "***** FAILURE *****" +#define FAIL_TEST(format, ...) \ + do { \ + printf("F\n"); \ + fprintf(stderr, FAIL_MSG "\n" format "\n", __VA_ARGS__); \ + failed_tests++; \ + pending_nl = false; \ + } while(0); + +#define READY_STDOUT() \ + do { \ + if (pending_nl) { \ + printf("\n"); \ + pending_nl = false; \ + } \ + } while(0); + +static int passed_tests = 0, failed_tests = 0; +static bool pending_nl = false; + +/* + Prints out the test report. Called before exiting using atexit(). +*/ +static void finalize() { + READY_STDOUT() + if (failed_tests) + printf("fail (%d/%d)\n", passed_tests, passed_tests + failed_tests); + else + printf("pass (%d/%d)\n", passed_tests, passed_tests); +} + +/* + Compare two files. If they are identical, then return true. Otherwise, + return false and print an error message showing the difference. +*/ +static bool diff_files(const char *expected_path, const char *actual_path) +{ + bool same = false; + FILE *expected = NULL, *actual = NULL; + + if (!(actual = fopen(actual_path, "rb"))) { + FAIL_TEST("missing output file: %s", actual_path) + goto cleanup; + } + if (!(expected = fopen(expected_path, "rb"))) { + FAIL_TEST("missing reference file: %s", expected_path) + goto cleanup; + } + + size_t len = 0; + int e, a; + while ((e = fgetc(expected)) != EOF) { + a = fgetc(actual); + if (a == EOF) { + FAIL_TEST("files differ: output file too short (index %zu)", len) + goto cleanup; + } + if (e != a) { + FAIL_TEST("files differ: 0x%02X != 0x%02X (expected vs. actual at " + "index %zu)", e, a, len) + goto cleanup; + } + len++; + } + + if (fgetc(actual) != EOF) { + FAIL_TEST("files differ: junk at end of output file (index %zu)", len) + goto cleanup; + } + + same = true; + + cleanup: + if (expected) + fclose(expected); + if (actual) + fclose(actual); + return same; +} + +/* + Return whether the given character is valid within a filename. +*/ +static bool is_valid_filename_char(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-'; +} + +/* + Run a single ASM->ROM test, converting the given source file to a temporary + output file, compared against the reference file. +*/ +static bool run_asm_test(const char *src_file, const char *ref_file) +{ + char *cmd_prefix = "../crater --assemble " ASM_PREFIX; + char *cmd = cr_malloc(sizeof(char) * + (strlen(cmd_prefix) + strlen(ASM_OUTFILE) + strlen(src_file)) + 2); + + // Construct the command by concatenating: + // ../crater --assemble asm/ asm/.output.gg + stpcpy(stpcpy(stpcpy(cmd, cmd_prefix), src_file), " " ASM_OUTFILE); + unlink(ASM_OUTFILE); + system(cmd); + free(cmd); + + // Construct the full reference file path in a temporary variable and diff + // it with the output file: + char *ref_path = malloc(sizeof(char) * + (strlen(ASM_PREFIX) + strlen(ref_file) + 1)); + stpcpy(stpcpy(ref_path, ASM_PREFIX), ref_file); + bool diff = diff_files(ref_path, ASM_OUTFILE); + free(ref_path); + return diff; +} + +/* --------------------------- Main test runners --------------------------- */ + +/* + Run tests for the Z80 CPU. +*/ +static bool test_cpu() +{ + // TODO + return true; +} + +/* + Run tests for the VDP. +*/ +static bool test_vdp() +{ + // TODO + return true; +} + +/* + Run tests for the SN76489 PSG. +*/ +static bool test_psg() +{ + // TODO + return true; +} + +/* + Run tests for the assembler. +*/ +static bool test_asm() +{ + FILE *fp = fopen(ASM_PREFIX "manifest", "r"); + if (!fp) { + ERROR_ERRNO("couldn't open manifest file") + return false; + } + + char *line = NULL, *split, c; + size_t cap = 0, lineno = 0, i; + ssize_t len; + + while ((len = getline(&line, &cap, fp)) > 0) { + lineno++; + line[--len] = '\0'; + if (!len) + continue; + + i = 0; + while ((c = line[i++])) { + if (!is_valid_filename_char(c) && c != ' ') { + READY_STDOUT() + ERROR("bad character in manifest file on line %zu", lineno) + return false; + } + } + + split = strchr(line, ' '); + if (!split || strchr(split + 1, ' ')) { + READY_STDOUT() + ERROR("bad format in manifest file on line %zu", lineno) + return false; + } + + *(split++) = '\0'; + if (!run_asm_test(line, split)) { + fprintf(stderr, "in test: %s -> %s\n", line, split); + return false; + } + PASS_TEST() + } + + unlink(ASM_OUTFILE); + free(line); + return true; +} + +/* + Run tests for the disassembler. +*/ +static bool test_dis() +{ + // TODO + return true; +} + +/* + Run integration tests (i.e., multiple components working together). +*/ +static bool test_integrate() +{ + // TODO + return true; +} + +/* + Main function. +*/ +int main(int argc, char *argv[]) +{ + if (argc != 2) + FATAL("a single component name is required") + + const char *component = argv[1], *name; + bool (*func)(); + + if (!strcmp(component, "cpu")) { + name = "Z80 CPU"; + func = test_cpu; + } else if (!strcmp(component, "vdp")) { + name = "VDP"; + func = test_vdp; + } else if (!strcmp(component, "psg")) { + name = "SN76489 PSG"; + func = test_psg; + } else if (!strcmp(component, "asm")) { + name = "assembler"; + func = test_asm; + } else if (!strcmp(component, "dis")) { + name = "disassembler"; + func = test_dis; + } else if (!strcmp(component, "integrate")) { + name = "integration"; + func = test_integrate; + } else { + FATAL("unknown component: %s", component) + } + + printf("crater: running %s tests\n", name); + atexit(finalize); + return func() ? EXIT_SUCCESS : EXIT_FAILURE; +}