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.
 
 
 
 
 

301 lines
7.5 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 <limits.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "parse_util.h"
  7. #include "directives.h"
  8. #include "../logging.h"
  9. #include "../util.h"
  10. #define MAX_REGION_SIZE 32
  11. #define DIRECTIVE_PARSE_FUNC(name, type) \
  12. bool dparse_##name(type *result, const ASMLine *line, const char *directive)
  13. /*
  14. All public functions in this file follow the same return conventions:
  15. - Return true on success and false on failure.
  16. - *result is only modified on success.
  17. */
  18. /*
  19. Read in a boolean value and store it in *result.
  20. */
  21. bool parse_bool(bool *result, const char *arg, ssize_t size)
  22. {
  23. if (size <= 0 || size > 5)
  24. return false;
  25. switch (size) {
  26. case 1: // 0, 1
  27. if (*arg == '0' || *arg == '1')
  28. return (*result = *arg - '0'), true;
  29. return false;
  30. case 2: // on
  31. if (!strncmp(arg, "on", 2))
  32. return (*result = true), true;
  33. return false;
  34. case 3: // off
  35. if (!strncmp(arg, "off", 3))
  36. return (*result = false), true;
  37. return false;
  38. case 4: // true
  39. if (!strncmp(arg, "true", 4))
  40. return (*result = true), true;
  41. return false;
  42. case 5: // false
  43. if (!strncmp(arg, "false", 5))
  44. return (*result = false), true;
  45. return false;
  46. }
  47. return false;
  48. }
  49. /*
  50. Read in a 32-bit integer and store it in *result.
  51. */
  52. bool parse_uint32_t(uint32_t *result, const char *arg, ssize_t size)
  53. {
  54. if (size <= 0)
  55. return false;
  56. const char *end = arg + size;
  57. uint64_t value = 0;
  58. if (*arg == '$') {
  59. arg++;
  60. if (arg == end)
  61. return false;
  62. while (arg < end) {
  63. if (*arg >= '0' && *arg <= '9')
  64. value = value * 16 + (*arg - '0');
  65. else if (*arg >= 'a' && *arg <= 'f')
  66. value = (value * 0x10) + 0xA + (*arg - 'a');
  67. else
  68. return false;
  69. if (value > UINT32_MAX)
  70. return false;
  71. arg++;
  72. }
  73. }
  74. else {
  75. while (arg < end) {
  76. if (*arg < '0' || *arg > '9')
  77. return false;
  78. value = (value * 10) + (*arg - '0');
  79. if (value > UINT32_MAX)
  80. return false;
  81. arg++;
  82. }
  83. }
  84. *result = value;
  85. return true;
  86. }
  87. /*
  88. Read in a string, possibly with escape sequences, and store it in *result.
  89. *length is also updated to the size of the string, which is *not*
  90. null-terminated. *result must be free()'d when finished.
  91. */
  92. bool parse_string(char **result, size_t *length, const char *arg, ssize_t size)
  93. {
  94. if (size < 2 || arg[0] != '"' || arg[size - 1] != '"')
  95. return false;
  96. ssize_t i, slashes = 0;
  97. for (i = 1; i < size; i++) {
  98. if (arg[i] == '"' && (slashes % 2) == 0)
  99. break;
  100. // TODO: parse escape codes here
  101. if (arg[i] == '\\')
  102. slashes++;
  103. else
  104. slashes = 0;
  105. }
  106. if (i != size - 1) // Junk present after closing quote
  107. return false;
  108. *length = size - 2;
  109. *result = malloc(sizeof(char) * (*length));
  110. if (!*result)
  111. OUT_OF_MEMORY()
  112. memcpy(*result, arg + 1, *length);
  113. return true;
  114. }
  115. /*
  116. Read in a space-separated sequence of bytes and store it in *result.
  117. *length is also updated to the number of bytes in the array. *result must
  118. be free()'d when finished.
  119. */
  120. bool parse_bytes(uint8_t **result, size_t *length, const char *arg, ssize_t size)
  121. {
  122. if (size <= 0)
  123. return false;
  124. const char *end = arg + size;
  125. uint8_t *bytes = NULL;
  126. size_t nbytes = 0;
  127. while (arg < end) {
  128. const char *start = arg;
  129. while (arg != end && *arg != ' ' && *arg != ',')
  130. arg++;
  131. uint32_t temp;
  132. if (!parse_uint32_t(&temp, start, arg - start) || temp > UINT8_MAX) {
  133. free(bytes);
  134. return false;
  135. }
  136. nbytes++;
  137. bytes = realloc(bytes, sizeof(uint8_t) * nbytes);
  138. if (!bytes)
  139. OUT_OF_MEMORY()
  140. bytes[nbytes - 1] = temp;
  141. if (arg < end - 1 && *arg == ',' && *(arg + 1) == ' ')
  142. arg += 2;
  143. else if (arg++ >= end)
  144. break;
  145. }
  146. *result = bytes;
  147. *length = nbytes;
  148. return true;
  149. }
  150. /*
  151. Read in a boolean argument from the given line and store it in *result.
  152. */
  153. DIRECTIVE_PARSE_FUNC(bool, bool)
  154. {
  155. size_t offset = DIRECTIVE_OFFSET(line, directive) + 1;
  156. return parse_bool(result, line->data + offset, line->length - offset);
  157. }
  158. /*
  159. Read in a 32-bit int argument from the given line and store it in *result.
  160. */
  161. DIRECTIVE_PARSE_FUNC(uint32_t, uint32_t)
  162. {
  163. size_t offset = DIRECTIVE_OFFSET(line, directive) + 1;
  164. return parse_uint32_t(result, line->data + offset, line->length - offset);
  165. }
  166. /*
  167. Read in a 16-bit int argument from the given line and store it in *result.
  168. */
  169. DIRECTIVE_PARSE_FUNC(uint16_t, uint16_t)
  170. {
  171. uint32_t value;
  172. if (dparse_uint32_t(&value, line, directive) && value <= UINT16_MAX)
  173. return (*result = value), true;
  174. return false;
  175. }
  176. /*
  177. Read in an 8-bit int argument from the given line and store it in *result.
  178. */
  179. DIRECTIVE_PARSE_FUNC(uint8_t, uint8_t)
  180. {
  181. uint32_t value;
  182. if (dparse_uint32_t(&value, line, directive) && value <= UINT8_MAX)
  183. return (*result = value), true;
  184. return false;
  185. }
  186. /*
  187. Parse a ROM size string in an ASMLine and store it in *result.
  188. */
  189. DIRECTIVE_PARSE_FUNC(rom_size, uint32_t)
  190. {
  191. const char *arg = line->data + DIRECTIVE_OFFSET(line, directive) + 1;
  192. const char *end = line->data + line->length - 1;
  193. if (end - arg < 5)
  194. return false;
  195. if (*(arg++) != '"' || *(end--) != '"')
  196. return false;
  197. if (*end != 'B' && *end != 'b')
  198. return false;
  199. end--;
  200. uint32_t factor;
  201. if (*end == 'K' || *end == 'k')
  202. factor = 1 << 10;
  203. else if (*end == 'M' || *end == 'm')
  204. factor = 1 << 20;
  205. else
  206. return false;
  207. end--;
  208. if (*end != ' ')
  209. return false;
  210. uint32_t value = 0;
  211. while (arg < end) {
  212. if (*arg < '0' || *arg > '9')
  213. return false;
  214. value = (value * 10) + (*arg - '0');
  215. if (value > UINT16_MAX)
  216. return false;
  217. arg++;
  218. }
  219. *result = value * factor;
  220. return true;
  221. }
  222. /*
  223. Parse a region code string in an ASMLine and store it in *result.
  224. */
  225. DIRECTIVE_PARSE_FUNC(region_string, uint8_t)
  226. {
  227. char buffer[MAX_REGION_SIZE];
  228. size_t offset = DIRECTIVE_OFFSET(line, directive) + 1;
  229. const char *arg = line->data + offset;
  230. ssize_t len = line->length - offset;
  231. if (len <= 2 || len >= MAX_REGION_SIZE + 2) // Account for double quotes
  232. return false;
  233. if (arg[0] != '"' || arg[len - 1] != '"')
  234. return false;
  235. strncpy(buffer, arg + 1, len - 2);
  236. buffer[len - 2] = '\0';
  237. uint8_t code = region_string_to_code(buffer);
  238. if (code)
  239. return (*result = code), true;
  240. return false;
  241. }
  242. /*
  243. Parse a size code in an ASMLine and store it in *result.
  244. */
  245. DIRECTIVE_PARSE_FUNC(size_code, uint8_t)
  246. {
  247. uint32_t bytes;
  248. if (!dparse_uint32_t(&bytes, line, directive)) {
  249. if (!dparse_rom_size(&bytes, line, directive))
  250. return false;
  251. }
  252. uint8_t code = size_bytes_to_code(bytes);
  253. if (code != INVALID_SIZE_CODE)
  254. return (*result = code), true;
  255. return false;
  256. }