An emulator, assembler, and disassembler for the Sega Game Gear
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

166 lines
4.0 KiB

  1. /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  2. Released under the terms of the MIT License. See LICENSE for details. */
  3. #include <ctype.h>
  4. #include <errno.h>
  5. #include <stdbool.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <sys/stat.h>
  10. #include "rom.h"
  11. #include "logging.h"
  12. #define NUM_LOCATIONS 3
  13. #define MAGIC_LEN 8
  14. #define HEADER_SIZE 16
  15. static size_t header_locations[NUM_LOCATIONS] = {0x7ff0, 0x1ff0, 0x3ff0};
  16. static const char header_magic[MAGIC_LEN + 1] = "TMR SEGA";
  17. /*
  18. Return whether or not the given ROM image size is valid.
  19. */
  20. static bool validate_size(off_t size)
  21. {
  22. if (size & (size - 1))
  23. return false; // Ensure size is a power of two
  24. off_t kbytes = size >> 10;
  25. return kbytes >= 8 && kbytes <= 1024;
  26. }
  27. #ifdef DEBUG_MODE
  28. /*
  29. DEBUG FUNCTION: Print out the header to stdout.
  30. */
  31. static void print_header(const char *header)
  32. {
  33. char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE];
  34. for (int i = 0; i < HEADER_SIZE; i++) {
  35. snprintf(&header_hex[3 * i], 3, "%02x", header[i]);
  36. if (isprint(header[i]))
  37. snprintf(&header_chr[3 * i], 3, "%2c", header[i]);
  38. else {
  39. header_chr[3 * i] = ' ';
  40. header_chr[3 * i + 1] = '?';
  41. }
  42. header_hex[3 * i + 2] = header_chr[3 * i + 2] = ' ';
  43. }
  44. header_hex[3 * HEADER_SIZE - 1] = header_chr[3 * HEADER_SIZE - 1] = '\0';
  45. DEBUG("- header dump (hex): %s", header_hex)
  46. DEBUG("- header dump (chr): %s", header_chr)
  47. }
  48. #endif
  49. /*
  50. Read a ROM image's header, and return whether or not it is valid.
  51. */
  52. static bool read_header(ROM *rom)
  53. {
  54. size_t location, i;
  55. const char *header;
  56. DEBUG("- looking for header:")
  57. for (i = 0; i < NUM_LOCATIONS; i++) {
  58. location = header_locations[i];
  59. DEBUG(" - trying location 0x%zx:", location)
  60. header = &rom->data[location];
  61. if (memcmp(header, header_magic, MAGIC_LEN)) {
  62. DEBUG(" - magic not present")
  63. }
  64. else {
  65. DEBUG(" - magic found")
  66. #ifdef DEBUG_MODE
  67. print_header(header);
  68. #endif
  69. // TODO: parse header
  70. return true;
  71. }
  72. }
  73. DEBUG(" - could not find header")
  74. return false;
  75. }
  76. /*
  77. Create and load a ROM image located at the given path.
  78. rom_ptr will point to the new object if created successfully, and NULL will
  79. be returned. Otherwise, rom_ptr will not be modified and an error string
  80. will be returned. The error string should not be freed.
  81. */
  82. const char* rom_open(ROM **rom_ptr, const char *path)
  83. {
  84. ROM *rom;
  85. FILE* fp;
  86. struct stat st;
  87. if (!(fp = fopen(path, "r")))
  88. return strerror(errno);
  89. if (fstat(fileno(fp), &st)) {
  90. fclose(fp);
  91. return strerror(errno);
  92. }
  93. if (!(st.st_mode & S_IFREG)) {
  94. fclose(fp);
  95. return (st.st_mode & S_IFDIR) ? rom_err_isdir : rom_err_notfile;
  96. }
  97. if (!(rom = malloc(sizeof(ROM))))
  98. OUT_OF_MEMORY()
  99. // Set defaults:
  100. rom->name = NULL;
  101. rom->data = NULL;
  102. rom->size = 0;
  103. // Set rom->name:
  104. if (!(rom->name = malloc(sizeof(char) * (strlen(path) + 1))))
  105. OUT_OF_MEMORY()
  106. strcpy(rom->name, path);
  107. DEBUG("Loading ROM %s:", rom->name)
  108. // Set rom->size:
  109. DEBUG("- size: %lld", st.st_size)
  110. if (!validate_size(st.st_size)) {
  111. rom_close(rom);
  112. fclose(fp);
  113. return rom_err_badsize;
  114. }
  115. rom->size = st.st_size;
  116. // Set rom->data:
  117. if (!(rom->data = malloc(sizeof(char) * st.st_size)))
  118. OUT_OF_MEMORY()
  119. if (!(fread(rom->data, st.st_size, 1, fp))) {
  120. rom_close(rom);
  121. fclose(fp);
  122. return rom_err_badread;
  123. }
  124. fclose(fp);
  125. // Parse the header:
  126. if (!read_header(rom)) {
  127. rom_close(rom);
  128. return rom_err_badheader;
  129. }
  130. *rom_ptr = rom;
  131. return NULL;
  132. }
  133. /*
  134. Free a ROM object previously created with rom_open().
  135. */
  136. void rom_close(ROM *rom)
  137. {
  138. if (rom->name)
  139. free(rom->name);
  140. if (rom->data)
  141. free(rom->data);
  142. free(rom);
  143. }