| @@ -3,3 +3,5 @@ roms/* | |||
| !roms/README | |||
| crater | |||
| crater-dev | |||
| tests/runner | |||
| tests/asm/*.gg | |||
| @@ -1,4 +1,4 @@ | |||
| Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| @@ -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 | |||
| ----- | |||
| @@ -1,50 +1,59 @@ | |||
| # Copyright (C) 2014 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| # Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| # 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-,,$@) | |||
| @@ -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. | |||
| @@ -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); | |||
| } | |||
| @@ -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; | |||
| @@ -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__) | |||
| @@ -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] = ' '; | |||
| } | |||
| @@ -1,4 +1,4 @@ | |||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| /* | |||
| @@ -0,0 +1,16 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -0,0 +1,16 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -0,0 +1,21 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -0,0 +1,17 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -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 | |||
| @@ -0,0 +1,3 @@ | |||
| testfunc: | |||
| sub b | |||
| ret | |||
| @@ -0,0 +1,2 @@ | |||
| .include "05.inc4.asm" | |||
| .rom_product 57005 | |||
| @@ -0,0 +1,3 @@ | |||
| blah: | |||
| ei | |||
| ret | |||
| @@ -0,0 +1,16 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -0,0 +1,13 @@ | |||
| test1:test2: test3: test4: | |||
| inc a | |||
| inc b | |||
| inc c | |||
| ret | |||
| @@ -0,0 +1,31 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -0,0 +1,103 @@ | |||
| ;; Copyright (C) 2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| ;; 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 | |||
| @@ -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 | |||
| @@ -0,0 +1,27 @@ | |||
| # Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| # 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 | |||
| @@ -1,4 +1,274 @@ | |||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com> | |||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||
| // ... | |||
| #include <stdbool.h> | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #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/<src_file> 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; | |||
| } | |||