An emulator, assembler, and disassembler for the Sega Game Gear
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 

265 lignes
6.4 KiB

  1. /* Copyright (C) 2014-2016 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <stdbool.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <unistd.h>
  8. #include "../src/logging.h"
  9. #include "../src/util.h"
  10. #define ASM_FULL "asm/full/"
  11. #define ASM_OUTFILE ASM_FULL ".output.gg"
  12. /* Helper macros for reporting test passings/failures */
  13. #define PASS_TEST() \
  14. do { \
  15. printf("."); \
  16. fflush(stdout); \
  17. passed_tests++; \
  18. pending_nl = true; \
  19. } while(0);
  20. #define FAIL_TEST(format, ...) \
  21. do { \
  22. printf("F\n"); \
  23. fprintf(stderr, "***** FAILURE *****\n" format "\n", __VA_ARGS__); \
  24. failed_tests++; \
  25. pending_nl = false; \
  26. } while(0);
  27. #define READY_STDOUT() \
  28. do { \
  29. if (pending_nl) { \
  30. printf("\n"); \
  31. pending_nl = false; \
  32. } \
  33. } while(0);
  34. static int passed_tests = 0, failed_tests = 0;
  35. static bool pending_nl = false;
  36. /*
  37. Prints out the test report. Called before exiting using atexit().
  38. */
  39. static void finalize() {
  40. READY_STDOUT()
  41. if (failed_tests)
  42. printf("fail (%d/%d)\n", passed_tests, passed_tests + failed_tests);
  43. else
  44. printf("pass (%d/%d)\n", passed_tests, passed_tests);
  45. }
  46. /*
  47. Compare two files. If they are identical, then return true. Otherwise,
  48. return false and print an error message showing the difference.
  49. */
  50. static bool diff_files(const char *expected_path, const char *actual_path)
  51. {
  52. bool same = false;
  53. FILE *expected = NULL, *actual = NULL;
  54. if (!(expected = fopen(expected_path, "rb"))) {
  55. FAIL_TEST("missing reference file: %s", expected_path)
  56. goto cleanup;
  57. }
  58. if (!(actual = fopen(actual_path, "rb"))) {
  59. FAIL_TEST("missing output file: %s", actual_path)
  60. goto cleanup;
  61. }
  62. size_t len = 0;
  63. int e, a;
  64. while ((e = fgetc(expected)) != EOF) {
  65. a = fgetc(actual);
  66. if (a == EOF) {
  67. FAIL_TEST("files differ: output file too short (index %zu)", len)
  68. goto cleanup;
  69. }
  70. if (e != a) {
  71. FAIL_TEST("files differ: 0x%02X != 0x%02X (expected vs. actual at "
  72. "index %zu)", e, a, len)
  73. goto cleanup;
  74. }
  75. len++;
  76. }
  77. if (fgetc(actual) != EOF) {
  78. FAIL_TEST("files differ: junk at end of output file (index %zu)", len)
  79. goto cleanup;
  80. }
  81. same = true;
  82. cleanup:
  83. if (expected)
  84. fclose(expected);
  85. if (actual)
  86. fclose(actual);
  87. return same;
  88. }
  89. /*
  90. Run a single ASM->ROM test, converting the given source file to a temporary
  91. output file, compared against the reference file.
  92. */
  93. static bool run_full_asm_test(const char *src_file, const char *ref_file)
  94. {
  95. char *cmd_prefix = "../crater --assemble " ASM_FULL;
  96. char *cmd = cr_malloc(sizeof(char) *
  97. (strlen(cmd_prefix) + strlen(ASM_OUTFILE) + strlen(src_file)) + 2);
  98. // Construct the command by concatenating:
  99. // ../crater --assemble asm/full/<src_file> asm/full/.output.gg
  100. stpcpy(stpcpy(stpcpy(cmd, cmd_prefix), src_file), " " ASM_OUTFILE);
  101. unlink(ASM_OUTFILE);
  102. system(cmd);
  103. free(cmd);
  104. // Construct the full reference file path in a temporary variable and diff
  105. // it with the output file:
  106. char *ref_path = malloc(sizeof(char) *
  107. (strlen(ASM_FULL) + strlen(ref_file) + 1));
  108. stpcpy(stpcpy(ref_path, ASM_FULL), ref_file);
  109. bool diff = diff_files(ref_path, ASM_OUTFILE);
  110. free(ref_path);
  111. return diff;
  112. }
  113. /*
  114. Run all "full"/"complete" ASM->ROM tests.
  115. */
  116. static bool run_full_asm_tests()
  117. {
  118. FILE *fp = fopen(ASM_FULL "manifest", "r");
  119. if (!fp) {
  120. ERROR_ERRNO("couldn't open manifest file")
  121. return false;
  122. }
  123. char *line = NULL, *split;
  124. size_t cap = 0, lineno = 0;
  125. ssize_t len;
  126. while ((len = getline(&line, &cap, fp)) > 0) {
  127. lineno++;
  128. line[--len] = '\0';
  129. if (!len)
  130. continue;
  131. // TODO: validate chars
  132. split = strchr(line, ' ');
  133. if (!split || strchr(split + 1, ' ')) {
  134. READY_STDOUT()
  135. ERROR("bad format in manifest file on line %zu", lineno)
  136. return false;
  137. }
  138. *(split++) = '\0';
  139. if (!run_full_asm_test(line, split)) {
  140. fprintf(stderr, "test: %s -> %s\n", line, split);
  141. return false;
  142. }
  143. PASS_TEST()
  144. }
  145. unlink(ASM_OUTFILE);
  146. free(line);
  147. return true;
  148. }
  149. /*
  150. Run tests for the Z80 CPU.
  151. */
  152. static bool test_cpu()
  153. {
  154. // TODO
  155. return true;
  156. }
  157. /*
  158. Run tests for the VDP.
  159. */
  160. static bool test_vdp()
  161. {
  162. // TODO
  163. return true;
  164. }
  165. /*
  166. Run tests for the SN76489 PSG.
  167. */
  168. static bool test_psg()
  169. {
  170. // TODO
  171. return true;
  172. }
  173. /*
  174. Run tests for the assembler.
  175. */
  176. static bool test_asm()
  177. {
  178. return run_full_asm_tests();
  179. }
  180. /*
  181. Run tests for the disassembler.
  182. */
  183. static bool test_dis()
  184. {
  185. // TODO
  186. return true;
  187. }
  188. /*
  189. Run integration tests (i.e., multiple components working together).
  190. */
  191. static bool test_integrate()
  192. {
  193. // TODO
  194. return true;
  195. }
  196. /*
  197. Main function.
  198. */
  199. int main(int argc, char *argv[])
  200. {
  201. if (argc != 2)
  202. FATAL("a single component name is required")
  203. const char *component = argv[1], *name;
  204. bool (*func)();
  205. if (!strcmp(component, "cpu")) {
  206. name = "Z80 CPU";
  207. func = test_cpu;
  208. } else if (!strcmp(component, "vdp")) {
  209. name = "VDP";
  210. func = test_vdp;
  211. } else if (!strcmp(component, "psg")) {
  212. name = "SN76489 PSG";
  213. func = test_psg;
  214. } else if (!strcmp(component, "asm")) {
  215. name = "assembler";
  216. func = test_asm;
  217. } else if (!strcmp(component, "dis")) {
  218. name = "disassembler";
  219. func = test_dis;
  220. } else if (!strcmp(component, "integrate")) {
  221. name = "integration";
  222. func = test_integrate;
  223. } else {
  224. FATAL("unknown component: %s", component)
  225. }
  226. printf("crater: running %s tests\n", name);
  227. atexit(finalize);
  228. return func() ? EXIT_SUCCESS : EXIT_FAILURE;
  229. }