| @@ -1,190 +1,42 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | ||||
| Released under the terms of the MIT License. See LICENSE for details. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <dirent.h> | |||||
| #include <errno.h> | |||||
| #include <stdbool.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | |||||
| #include <sys/types.h> | |||||
| #include "src/errors.h" | |||||
| #include "src/config.h" | |||||
| #include "src/logging.h" | |||||
| #include "src/rom.h" | #include "src/rom.h" | ||||
| #include "src/version.h" | |||||
| #define ROMS_DIR "roms" | |||||
| /* Print command-line help/usage. */ | |||||
| static void print_help(const char *arg1) | |||||
| { | |||||
| printf("%s [-h] [-v] [-f] [-s <n>] [<rom_path>] ...\n" | |||||
| "\n" | |||||
| "basic options:\n" | |||||
| " -h, --help show this help message and exit\n" | |||||
| " -v, --version show crater's version number and exit\n" | |||||
| " -f, --fullscreen enable fullscreen mode\n" | |||||
| " -s, --scale <n> scale the game screen by an integer factor\n" | |||||
| " (applies to windowed mode only)\n" | |||||
| " <rom_path> path to the rom file to execute; if not given, will look\n" | |||||
| " in the roms/ directory and prompt the user\n" | |||||
| "\n" | |||||
| "advanced options:\n" | |||||
| " -g, --debug display information about emulation state while running,\n" | |||||
| " including register and memory values; can also pause\n" | |||||
| " emulation, set breakpoints, and change state\n" | |||||
| " -a, --assemble <in> <out> convert z80 assembly source code into a\n" | |||||
| " binary file that can be run by crater\n" | |||||
| " -d, --disassemble <in> <out> convert a binary file into z80 assembly code\n", | |||||
| arg1); | |||||
| } | |||||
| /* Print crater's version. */ | |||||
| static void print_version() | |||||
| { | |||||
| printf("crater %s\n", CRATER_VERSION); | |||||
| } | |||||
| /* Parse the command-line arguments for any special flags. */ | |||||
| static void parse_args(int argc, char *argv[]) | |||||
| { | |||||
| char *arg; | |||||
| int i; | |||||
| for (i = 1; i < argc; i++) { | |||||
| arg = argv[i]; | |||||
| if (arg[0] != '-') | |||||
| continue; | |||||
| do | |||||
| arg++; | |||||
| while (arg[0] == '-'); | |||||
| if (!strcmp(arg, "h") || !strcmp(arg, "help")) { | |||||
| print_help(argv[0]); | |||||
| exit(0); | |||||
| } else if (!strcmp(arg, "v") || !strcmp(arg, "version")) { | |||||
| print_version(); | |||||
| exit(0); | |||||
| // f fullscreen | |||||
| // s scale | |||||
| // g debug | |||||
| // a assemble | |||||
| // d disassemble | |||||
| } else { | |||||
| FATAL("unknown argument: %s", argv[i]) | |||||
| } | |||||
| } | |||||
| } | |||||
| /* Return whether the given string ends with the given suffix. */ | |||||
| static bool ends_with(const char *input, const char *suffix) | |||||
| { | |||||
| size_t ilen = strlen(input), slen = strlen(suffix); | |||||
| if (ilen < slen) | |||||
| return false; | |||||
| return strcmp(input + (ilen - slen), suffix) == 0; | |||||
| } | |||||
| /* Load all potential ROM files in roms/ into a data structure. */ | |||||
| static int get_rom_paths(char ***path_ptr) | |||||
| { | |||||
| DIR *dirp; | |||||
| struct dirent *entry; | |||||
| char **paths = NULL, *path; | |||||
| int psize = 8, npaths = 0; | |||||
| dirp = opendir(ROMS_DIR); | |||||
| if (dirp) { | |||||
| paths = malloc(sizeof(char*) * psize); | |||||
| if (!paths) | |||||
| OUT_OF_MEMORY() | |||||
| while ((entry = readdir(dirp))) { | |||||
| path = entry->d_name; | |||||
| if (ends_with(path, ".gg") || ends_with(path, ".bin")) { | |||||
| if (npaths >= psize) { | |||||
| paths = realloc(paths, sizeof(char*) * (psize *= 2)); | |||||
| if (!paths) | |||||
| OUT_OF_MEMORY() | |||||
| } | |||||
| paths[npaths] = malloc(sizeof(char*) * | |||||
| (strlen(path) + strlen(ROMS_DIR) + 1)); | |||||
| if (!paths[npaths]) | |||||
| OUT_OF_MEMORY() | |||||
| strcpy(paths[npaths], ROMS_DIR "/"); | |||||
| strcat(paths[npaths], path); | |||||
| npaths++; | |||||
| } | |||||
| } | |||||
| closedir(dirp); | |||||
| } else { | |||||
| WARN_ERRNO("couldn't open 'roms/'") | |||||
| } | |||||
| *path_ptr = paths; | |||||
| return npaths; | |||||
| } | |||||
| /* Find all potential ROM files in the roms/ directory, then ask the user which | |||||
| one they want to run. */ | |||||
| static char* get_rom_path_from_user() | |||||
| { | |||||
| char **paths, *path, *input = NULL; | |||||
| int npaths, i; | |||||
| long int index; | |||||
| size_t size = 0; | |||||
| ssize_t len; | |||||
| npaths = get_rom_paths(&paths); | |||||
| for (i = 0; i < npaths; i++) | |||||
| printf("[%2d] %s\n", i + 1, paths[i]); | |||||
| if (npaths) | |||||
| printf("Enter a ROM number from above, or the path to a ROM image: "); | |||||
| else | |||||
| printf("Enter the path to a ROM image: "); | |||||
| len = getline(&input, &size, stdin); | |||||
| if (!input) | |||||
| OUT_OF_MEMORY() | |||||
| if (len > 0 && input[len - 1] == '\n') | |||||
| input[len - 1] = '\0'; | |||||
| index = strtol(input, NULL, 10); | |||||
| if (index < 1 || index > npaths) | |||||
| path = input; | |||||
| else | |||||
| path = paths[index - 1]; | |||||
| for (i = 0; i < npaths; i++) { | |||||
| if (paths[i] != path) | |||||
| free(paths[i]); | |||||
| } | |||||
| if (paths) | |||||
| free(paths); | |||||
| return path; | |||||
| } | |||||
| /* | |||||
| Main function. | |||||
| */ | |||||
| int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||||
| { | { | ||||
| char *rom_path; | |||||
| rom_type *rom; | |||||
| Config *config; | |||||
| ROM *rom; | |||||
| int retval; | |||||
| retval = config_create(&config, argc, argv); | |||||
| if (retval != CONFIG_OK) | |||||
| return retval == CONFIG_EXIT_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE; | |||||
| parse_args(argc, argv); | |||||
| printf("crater: a Sega Game Gear emulator\n\n"); | printf("crater: a Sega Game Gear emulator\n\n"); | ||||
| rom_path = argc > 1 ? argv[1] : get_rom_path_from_user(); | |||||
| if (rom_path[0] == '\0') | |||||
| FATAL("no image given") | |||||
| if (!(rom = rom_open(rom_path))) { | |||||
| #ifdef DEBUG_MODE | |||||
| config_dump_args(config); | |||||
| #endif | |||||
| if (!(rom = rom_open(config->rom_path))) { | |||||
| if (errno == ENOMEM) | if (errno == ENOMEM) | ||||
| OUT_OF_MEMORY() | OUT_OF_MEMORY() | ||||
| else | else | ||||
| FATAL_ERRNO("couldn't load ROM image '%s'", rom_path) | |||||
| FATAL_ERRNO("couldn't load ROM image '%s'", config->rom_path) | |||||
| } | } | ||||
| if (argc <= 1) | |||||
| free(rom_path); | |||||
| printf("Loaded ROM image: %s.\n", rom->name); | printf("Loaded ROM image: %s.\n", rom->name); | ||||
| // TODO: start from here | // TODO: start from here | ||||
| rom_close(rom); | rom_close(rom); | ||||
| return 0; | |||||
| config_destroy(config); | |||||
| return EXIT_SUCCESS; | |||||
| } | } | ||||
| @@ -0,0 +1,340 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #include <dirent.h> | |||||
| #include <errno.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| #include <sys/types.h> | |||||
| #include "config.h" | |||||
| #include "logging.h" | |||||
| #include "version.h" | |||||
| #define ROMS_DIR "roms" | |||||
| /* | |||||
| Print command-line help/usage. | |||||
| */ | |||||
| static void print_help(const char *arg1) | |||||
| { | |||||
| printf("%s [-h] [-v] [-f] [-s <n>] [<rom_path>] ...\n" | |||||
| "\n" | |||||
| "basic options:\n" | |||||
| " -h, --help show this help message and exit\n" | |||||
| " -v, --version show crater's version number and exit\n" | |||||
| " -f, --fullscreen enable fullscreen mode\n" | |||||
| " -s, --scale <n> scale the game screen by an integer factor\n" | |||||
| " (applies to windowed mode only)\n" | |||||
| " <rom_path> path to the rom file to execute; if not given, will look\n" | |||||
| " in the roms/ directory and prompt the user\n" | |||||
| "\n" | |||||
| "advanced options:\n" | |||||
| " -g, --debug display information about emulation state while running,\n" | |||||
| " including register and memory values; can also pause\n" | |||||
| " emulation, set breakpoints, and change state\n" | |||||
| " -a, --assemble <in> <out> convert z80 assembly source code into a\n" | |||||
| " binary file that can be run by crater\n" | |||||
| " -d, --disassemble <in> <out> convert a binary file into z80 assembly code\n", | |||||
| arg1); | |||||
| } | |||||
| /* | |||||
| Print crater's version. | |||||
| */ | |||||
| static void print_version() | |||||
| { | |||||
| printf("crater %s\n", CRATER_VERSION); | |||||
| } | |||||
| /* | |||||
| Return whether the given string ends with the given suffix. | |||||
| */ | |||||
| static bool ends_with(const char *input, const char *suffix) | |||||
| { | |||||
| size_t ilen = strlen(input), slen = strlen(suffix); | |||||
| if (ilen < slen) | |||||
| return false; | |||||
| return strcmp(input + (ilen - slen), suffix) == 0; | |||||
| } | |||||
| /* | |||||
| Load all potential ROM files in roms/ into a data structure. | |||||
| */ | |||||
| static int get_rom_paths(char ***path_ptr) | |||||
| { | |||||
| DIR *dirp; | |||||
| struct dirent *entry; | |||||
| char **paths = NULL, *path; | |||||
| int psize = 8, npaths = 0; | |||||
| dirp = opendir(ROMS_DIR); | |||||
| if (dirp) { | |||||
| paths = malloc(sizeof(char*) * psize); | |||||
| if (!paths) | |||||
| OUT_OF_MEMORY() | |||||
| while ((entry = readdir(dirp))) { | |||||
| path = entry->d_name; | |||||
| if (ends_with(path, ".gg") || ends_with(path, ".bin")) { | |||||
| if (npaths >= psize) { | |||||
| paths = realloc(paths, sizeof(char*) * (psize *= 2)); | |||||
| if (!paths) | |||||
| OUT_OF_MEMORY() | |||||
| } | |||||
| paths[npaths] = malloc(sizeof(char*) * | |||||
| (strlen(path) + strlen(ROMS_DIR) + 1)); | |||||
| if (!paths[npaths]) | |||||
| OUT_OF_MEMORY() | |||||
| strcpy(paths[npaths], ROMS_DIR "/"); | |||||
| strcat(paths[npaths], path); | |||||
| npaths++; | |||||
| } | |||||
| } | |||||
| closedir(dirp); | |||||
| } else { | |||||
| WARN_ERRNO("couldn't open 'roms/'") | |||||
| } | |||||
| *path_ptr = paths; | |||||
| return npaths; | |||||
| } | |||||
| /* | |||||
| Find all potential ROM files in the roms/ directory, then ask the user | |||||
| which one they want to run. | |||||
| */ | |||||
| static char* get_rom_path_from_user() | |||||
| { | |||||
| char **paths, *path, *input = NULL; | |||||
| int npaths, i; | |||||
| long int index; | |||||
| size_t size = 0; | |||||
| ssize_t len; | |||||
| npaths = get_rom_paths(&paths); | |||||
| for (i = 0; i < npaths; i++) | |||||
| printf("[%2d] %s\n", i + 1, paths[i]); | |||||
| if (npaths) | |||||
| printf("Enter a ROM number from above, or the path to a ROM image: "); | |||||
| else | |||||
| printf("Enter the path to a ROM image: "); | |||||
| len = getline(&input, &size, stdin); | |||||
| if (!input) | |||||
| OUT_OF_MEMORY() | |||||
| if (len > 0 && input[len - 1] == '\n') | |||||
| input[len - 1] = '\0'; | |||||
| index = strtol(input, NULL, 10); | |||||
| if (index < 1 || index > npaths) | |||||
| path = input; | |||||
| else | |||||
| path = paths[index - 1]; | |||||
| for (i = 0; i < npaths; i++) { | |||||
| if (paths[i] != path) | |||||
| free(paths[i]); | |||||
| } | |||||
| if (paths) | |||||
| free(paths); | |||||
| return path; | |||||
| } | |||||
| /* | |||||
| Parse the command-line arguments for any special flags. | |||||
| */ | |||||
| static int parse_args(Config *config, int argc, char *argv[]) | |||||
| { | |||||
| char *arg, *path; | |||||
| int i, paths_read = 0; | |||||
| for (i = 1; i < argc; i++) { | |||||
| arg = argv[i]; | |||||
| if (arg[0] != '-') { | |||||
| // Parsing a path or other variable: | |||||
| if (paths_read >= 2) { | |||||
| ERROR("too many arguments given - emulator mode accepts one " | |||||
| "ROM file,\nand assembler mode accepts one input file " | |||||
| "and one output file") | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| path = malloc(sizeof(char) * (strlen(arg) + 1)); | |||||
| strcpy(path, arg); | |||||
| if (paths_read == 1) { | |||||
| /* If this is the second path given, it can only be an output | |||||
| file for the assembler. If the assembler is not enabled by | |||||
| subsequent arguments, we'll throw an error. */ | |||||
| config->dst_path = path; | |||||
| } else { | |||||
| /* Otherwise, put the argument in the expected place. If we put | |||||
| it in rom_path and the assembler is enabled by later | |||||
| arguments, we'll move it. */ | |||||
| if (config->assemble || config->disassemble) | |||||
| config->src_path = path; | |||||
| else | |||||
| config->rom_path = path; | |||||
| } | |||||
| paths_read++; | |||||
| continue; | |||||
| } | |||||
| // Parsing an option: | |||||
| do | |||||
| arg++; | |||||
| while (arg[0] == '-'); | |||||
| if (!strcmp(arg, "h") || !strcmp(arg, "help")) { | |||||
| print_help(argv[0]); | |||||
| return CONFIG_EXIT_SUCCESS; | |||||
| } else if (!strcmp(arg, "v") || !strcmp(arg, "version")) { | |||||
| print_version(); | |||||
| return CONFIG_EXIT_SUCCESS; | |||||
| } else if (!strcmp(arg, "f") || !strcmp(arg, "fullscreen")) { | |||||
| config->fullscreen = true; | |||||
| } else if (!strcmp(arg, "s") || !strcmp(arg, "scale")) { | |||||
| if (i++ >= argc) { | |||||
| ERROR("the scale option requires an argument") | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| arg = argv[i]; | |||||
| long scale = strtol(arg, NULL, 10); | |||||
| if (scale <= 0 || scale > SCALE_MAX) { | |||||
| ERROR("scale factor of %s is not an integer or is out of range", arg) | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| } else if (!strcmp(arg, "g") || !strcmp(arg, "debug")) { | |||||
| config->debug = true; | |||||
| } else if (!strcmp(arg, "a") || !strcmp(arg, "assemble")) { | |||||
| if (paths_read >= 1) { | |||||
| config->src_path = config->rom_path; | |||||
| config->rom_path = NULL; | |||||
| } | |||||
| config->assemble = true; | |||||
| } else if (!strcmp(arg, "d") || !strcmp(arg, "disassemble")) { | |||||
| if (paths_read >= 1) { | |||||
| config->src_path = config->rom_path; | |||||
| config->rom_path = NULL; | |||||
| } | |||||
| config->disassemble = true; | |||||
| } else { | |||||
| ERROR("unknown argument: %s", argv[i]) | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| } | |||||
| if (!config->assemble && !config->disassemble && paths_read >= 2) { | |||||
| ERROR("too many arguments given - emulator mode accepts one ROM file") | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| if (!config->assemble && !config->disassemble && paths_read == 0) { | |||||
| path = get_rom_path_from_user(); | |||||
| if (path[0] == '\0') { | |||||
| ERROR("no ROM image given") | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| config->rom_path = path; | |||||
| } | |||||
| return CONFIG_OK; | |||||
| } | |||||
| /* | |||||
| Ensure that the combination of arguments in a config object are valid. | |||||
| */ | |||||
| bool sanity_check(Config* config) | |||||
| { | |||||
| if (config->fullscreen && config->scale) { | |||||
| ERROR("cannot specify a scale in fullscreen mode") | |||||
| return false; | |||||
| } else if (config->assemble && config->disassemble) { | |||||
| ERROR("cannot assemble and disassemble at the same time") | |||||
| return false; | |||||
| } else if ((config->assemble || config->disassemble) && | |||||
| (config->debug || config->fullscreen || config->scale)) { | |||||
| ERROR("cannot specify emulator options in assembler mode") | |||||
| return false; | |||||
| } else if ((config->assemble || config->disassemble) && | |||||
| !(config->src_path && config->dst_path)) { | |||||
| ERROR("assembler mode requires both an input and an output file") | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| /* | |||||
| Create a new config object and load default values into it. | |||||
| */ | |||||
| int config_create(Config** config_ptr, int argc, char* argv[]) | |||||
| { | |||||
| Config *config; | |||||
| int retval; | |||||
| if (!(config = malloc(sizeof(Config)))) { | |||||
| OUT_OF_MEMORY() | |||||
| return CONFIG_EXIT_FAILURE; | |||||
| } | |||||
| config->debug = false; | |||||
| config->assemble = false; | |||||
| config->disassemble = false; | |||||
| config->fullscreen = false; | |||||
| config->scale = 1; | |||||
| config->rom_path = NULL; | |||||
| config->src_path = NULL; | |||||
| config->dst_path = NULL; | |||||
| retval = parse_args(config, argc, argv); | |||||
| if (retval == CONFIG_OK && !sanity_check(config)) | |||||
| retval = CONFIG_EXIT_FAILURE; | |||||
| if (retval != CONFIG_OK) { | |||||
| config_destroy(config); | |||||
| return retval; | |||||
| } | |||||
| *config_ptr = config; | |||||
| return CONFIG_OK; | |||||
| } | |||||
| /* | |||||
| Destroy a config object previously created with config_create(). | |||||
| */ | |||||
| void config_destroy(Config *config) | |||||
| { | |||||
| if (config->rom_path) | |||||
| free(config->rom_path); | |||||
| if (config->src_path) | |||||
| free(config->src_path); | |||||
| if (config->dst_path) | |||||
| free(config->dst_path); | |||||
| free(config); | |||||
| } | |||||
| #ifdef DEBUG_MODE | |||||
| /* | |||||
| DEBUG FUNCTION: Print out all config arguments to stdout. | |||||
| */ | |||||
| void config_dump_args(Config* config) | |||||
| { | |||||
| DEBUG("Dumping arguments:") | |||||
| DEBUG("- fullscreen: %d", config->fullscreen) | |||||
| DEBUG("- scale: %d", config->scale) | |||||
| DEBUG("- debug: %d", config->debug) | |||||
| DEBUG("- assemble: %d", config->assemble) | |||||
| DEBUG("- disassemble: %d", config->disassemble) | |||||
| if (config->rom_path) | |||||
| DEBUG("- rom_path: %s", config->rom_path) | |||||
| else | |||||
| DEBUG("- rom_path: NULL") | |||||
| if (config->src_path) | |||||
| DEBUG("- src_path: %s", config->src_path) | |||||
| else | |||||
| DEBUG("- src_path: NULL") | |||||
| if (config->dst_path) | |||||
| DEBUG("- dst_path: %s", config->dst_path) | |||||
| else | |||||
| DEBUG("- dst_path: NULL") | |||||
| } | |||||
| #endif | |||||
| @@ -0,0 +1,38 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #pragma once | |||||
| #include <stdbool.h> | |||||
| #define CONFIG_OK 0 | |||||
| #define CONFIG_EXIT_SUCCESS 1 | |||||
| #define CONFIG_EXIT_FAILURE 2 | |||||
| /* | |||||
| We need some sort of maximum scale - with a native resolution of 160 x 144, | |||||
| a scale factor of 1024 will let us go up to 163,840 x 147,456 pixels. | |||||
| No one has a screen this large. | |||||
| */ | |||||
| #define SCALE_MAX 1024 | |||||
| /* Structs */ | |||||
| typedef struct { | |||||
| bool debug; | |||||
| bool assemble; | |||||
| bool disassemble; | |||||
| bool fullscreen; | |||||
| unsigned scale; | |||||
| char *rom_path; | |||||
| char *src_path; | |||||
| char *dst_path; | |||||
| } Config; | |||||
| /* Functions */ | |||||
| int config_create(Config**, int, char*[]); | |||||
| void config_destroy(Config*); | |||||
| #ifdef DEBUG_MODE | |||||
| void config_dump_args(Config*); | |||||
| #endif | |||||
| @@ -1,27 +0,0 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #pragma once | |||||
| #include <errno.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| /* Internal usage only */ | |||||
| #define LOG_MSG(level, extra, after, ...) { \ | |||||
| fprintf(stderr, level ": " __VA_ARGS__); \ | |||||
| extra; \ | |||||
| fprintf(stderr, ".\n"); \ | |||||
| after; \ | |||||
| } | |||||
| #define PRINT_ERRNO() fprintf(stderr, ": %s", strerror(errno)) | |||||
| /* Public error logging macros */ | |||||
| #define FATAL(...) LOG_MSG("Error", {}, exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define FATAL_ERRNO(...) LOG_MSG("Error", PRINT_ERRNO(), exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define WARN(...) LOG_MSG("Warning", {}, {}, __VA_ARGS__) | |||||
| #define WARN_ERRNO(...) LOG_MSG("Warning", PRINT_ERRNO(), {}, __VA_ARGS__) | |||||
| #define OUT_OF_MEMORY() FATAL("couldn't allocate enough memory") | |||||
| @@ -0,0 +1,35 @@ | |||||
| /* Copyright (C) 2014-2015 Ben Kurtovic <ben.kurtovic@gmail.com> | |||||
| Released under the terms of the MIT License. See LICENSE for details. */ | |||||
| #pragma once | |||||
| #include <errno.h> | |||||
| #include <stdio.h> | |||||
| #include <stdlib.h> | |||||
| #include <string.h> | |||||
| /* Internal usage only */ | |||||
| #define LOG_MSG(level, extra, tail, after, ...) { \ | |||||
| fprintf(stderr, level ": " __VA_ARGS__); \ | |||||
| extra; \ | |||||
| fprintf(stderr, tail ? ".\n" : "\n"); \ | |||||
| after; \ | |||||
| } | |||||
| #define PRINT_ERRNO() fprintf(stderr, ": %s", strerror(errno)) | |||||
| /* Public logging macros */ | |||||
| #define FATAL(...) LOG_MSG("Error", {}, 1, exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define FATAL_ERRNO(...) LOG_MSG("Error", PRINT_ERRNO(), 1, exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define ERROR(...) LOG_MSG("Error", {}, 1, {}, __VA_ARGS__) | |||||
| #define ERROR_ERRNO(...) LOG_MSG("Error", PRINT_ERRNO(), 1, {}, __VA_ARGS__) | |||||
| #define WARN(...) LOG_MSG("Warning", {}, 1, {}, __VA_ARGS__) | |||||
| #define WARN_ERRNO(...) LOG_MSG("Warning", PRINT_ERRNO(), 1, {}, __VA_ARGS__) | |||||
| #ifdef DEBUG_MODE | |||||
| #define DEBUG(...) LOG_MSG("[DEBUG]", {}, 0, {}, __VA_ARGS__) | |||||
| #define DEBUG_ERRNO(...) LOG_MSG("[DEBUG]", PRINT_ERRNO(), 0, {}, __VA_ARGS__) | |||||
| #endif | |||||
| #define OUT_OF_MEMORY() FATAL("couldn't allocate enough memory") | |||||
| @@ -9,11 +9,13 @@ | |||||
| #include "rom.h" | #include "rom.h" | ||||
| /* Create and return a ROM object located at the given path. Return NULL if | |||||
| there was an error; errno will be set appropriately. */ | |||||
| rom_type* rom_open(const char *path) | |||||
| /* | |||||
| Create and return a ROM object located at the given path. Return NULL if | |||||
| there was an error; errno will be set appropriately. | |||||
| */ | |||||
| ROM* rom_open(const char *path) | |||||
| { | { | ||||
| rom_type *rom; | |||||
| ROM *rom; | |||||
| FILE* fp; | FILE* fp; | ||||
| struct stat s; | struct stat s; | ||||
| @@ -31,7 +33,7 @@ rom_type* rom_open(const char *path) | |||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| if (!(rom = malloc(sizeof(rom_type)))) { | |||||
| if (!(rom = malloc(sizeof(ROM)))) { | |||||
| fclose(fp); | fclose(fp); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| @@ -44,8 +46,10 @@ rom_type* rom_open(const char *path) | |||||
| return rom; | return rom; | ||||
| } | } | ||||
| /* Free a ROM object previously created with rom_open(). */ | |||||
| void rom_close(rom_type *rom) | |||||
| /* | |||||
| Free a ROM object previously created with rom_open(). | |||||
| */ | |||||
| void rom_close(ROM *rom) | |||||
| { | { | ||||
| free(rom); | free(rom); | ||||
| } | } | ||||
| @@ -8,9 +8,9 @@ | |||||
| typedef struct { | typedef struct { | ||||
| char* name; | char* name; | ||||
| char* data; | char* data; | ||||
| } rom_type; | |||||
| } ROM; | |||||
| /* Functions */ | /* Functions */ | ||||
| rom_type* rom_open(const char*); | |||||
| void rom_close(rom_type*); | |||||
| ROM* rom_open(const char*); | |||||
| void rom_close(ROM*); | |||||
| @@ -10,4 +10,4 @@ | |||||
| typedef struct { | typedef struct { | ||||
| int pc; | int pc; | ||||
| } z80_type; | |||||
| } Z80; | |||||