| @@ -28,8 +28,7 @@ Only OS X and Linux are tested. You'll need a modern compiler that supports C11 | |||||
| `brew install sdl2`; using apt, you can `apt-get install libsdl2-dev`. | `brew install sdl2`; using apt, you can `apt-get install libsdl2-dev`. | ||||
| Run `make` to create `./crater`. To build the development version with debug | Run `make` to create `./crater`. To build the development version with debug | ||||
| symbols and extra diagnostic info (they can exist simultaneously), run | |||||
| `make DEBUG=1`, which creates `./crater-dev`. | |||||
| symbols and no optimizations, run `make DEBUG=1`, which creates `./crater-dev`. | |||||
| crater has a number of test cases. Run the entire suite with `make test`; | 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 | individual components can be tested by doing `make test-{component}`, where | ||||
| @@ -51,9 +50,8 @@ Add or symlink ROMs to `roms/` at your leisure. Note that they must end in | |||||
| Add `--fullscreen` (`-f`) to enable fullscreen mode, or `--scale <n>` | Add `--fullscreen` (`-f`) to enable fullscreen mode, or `--scale <n>` | ||||
| (`-s <n>`) to scale the game screen by an integer factor. | (`-s <n>`) to scale the game screen by an integer factor. | ||||
| Add `--debug` (`-g`) to display detailed information about emulation state | |||||
| while running, including register values and memory contents. You can also | |||||
| pause emulation to set breakpoints and change state. | |||||
| Add `--debug` (`-g`) to show logging information while running. Pass it twice | |||||
| (`-g -g`) to show more detailed logs, including an emulator trace. | |||||
| `./crater -h` gives (fairly basic) command-line usage, and `./crater -v` gives | `./crater -h` gives (fairly basic) command-line usage, and `./crater -v` gives | ||||
| the current version. | the current version. | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| @@ -24,15 +24,15 @@ int main(int argc, char *argv[]) | |||||
| if (retval != CONFIG_OK) | if (retval != CONFIG_OK) | ||||
| return retval == CONFIG_EXIT_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE; | return retval == CONFIG_EXIT_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE; | ||||
| #ifdef DEBUG_MODE | |||||
| config_dump_args(config); | |||||
| #endif | |||||
| SET_LOG_LEVEL(config->debug) | |||||
| if (DEBUG_LEVEL) | |||||
| config_dump_args(config); | |||||
| if (config->assemble || config->disassemble) { | |||||
| if (config->assemble) | |||||
| retval = assemble_file(config->src_path, config->dst_path); | |||||
| else | |||||
| retval = disassemble_file(config->src_path, config->dst_path); | |||||
| if (config->assemble) { | |||||
| retval = assemble_file(config->src_path, config->dst_path); | |||||
| retval = retval ? EXIT_SUCCESS : EXIT_FAILURE; | |||||
| } else if (config->disassemble) { | |||||
| retval = disassemble_file(config->src_path, config->dst_path); | |||||
| retval = retval ? EXIT_SUCCESS : EXIT_FAILURE; | retval = retval ? EXIT_SUCCESS : EXIT_FAILURE; | ||||
| } else { | } else { | ||||
| ROM *rom; | ROM *rom; | ||||
| @@ -8,17 +8,16 @@ DEVEXT = -dev | |||||
| TESTS = cpu vdp psg asm dis integrate | TESTS = cpu vdp psg asm dis integrate | ||||
| CC = clang | CC = clang | ||||
| FLAGS = -O2 -Wall -Wextra -pedantic -std=c11 | |||||
| FLAGS = -Wall -Wextra -pedantic -std=c11 | |||||
| CFLAGS = $(shell sdl2-config --cflags) | CFLAGS = $(shell sdl2-config --cflags) | ||||
| LIBS = $(shell sdl2-config --libs) | LIBS = $(shell sdl2-config --libs) | ||||
| DFLAGS = -g -DDEBUG_MODE | |||||
| DFLAGS = -g | |||||
| RFLAGS = -O2 | |||||
| MKDIR = mkdir -p | MKDIR = mkdir -p | ||||
| RM = rm -rf | RM = rm -rf | ||||
| ASM_UP = scripts/update_asm_instructions.py | ASM_UP = scripts/update_asm_instructions.py | ||||
| MODE = release | |||||
| BNRY = $(PROGRAM) | |||||
| FLGS = $(FLAGS) | |||||
| SDRS = $(shell find $(SOURCES) -type d | xargs echo) | SDRS = $(shell find $(SOURCES) -type d | xargs echo) | ||||
| SRCS = $(filter-out %.inc.c,$(foreach d,. $(SDRS),$(wildcard $(addprefix $(d)/*,.c)))) | SRCS = $(filter-out %.inc.c,$(foreach d,. $(SDRS),$(wildcard $(addprefix $(d)/*,.c)))) | ||||
| OBJS = $(patsubst %.c,%.o,$(addprefix $(BUILD)/$(MODE)/,$(SRCS))) | OBJS = $(patsubst %.c,%.o,$(addprefix $(BUILD)/$(MODE)/,$(SRCS))) | ||||
| @@ -27,9 +26,13 @@ DIRS = $(sort $(dir $(OBJS))) | |||||
| TCPS = $(addprefix test-,$(TESTS)) | TCPS = $(addprefix test-,$(TESTS)) | ||||
| ifdef DEBUG | ifdef DEBUG | ||||
| BNRY := $(BNRY)$(DEVEXT) | |||||
| FLGS += $(DFLAGS) | |||||
| BNRY := $(PROGRAM)$(DEVEXT) | |||||
| FLGS += $(DFLAGS) $(FLAGS) | |||||
| MODE = debug | MODE = debug | ||||
| else | |||||
| BNRY := $(PROGRAM) | |||||
| FLGS += $(RFLAGS) $(FLAGS) | |||||
| MODE = release | |||||
| endif | endif | ||||
| export CC | export CC | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| @@ -38,6 +38,7 @@ static size_t bounding_rom_size(size_t size) | |||||
| */ | */ | ||||
| static ErrorInfo* resolve_defaults(AssemblerState *state) | static ErrorInfo* resolve_defaults(AssemblerState *state) | ||||
| { | { | ||||
| DEBUG("Resolving defaults") | |||||
| if (!state->rom_size) { | if (!state->rom_size) { | ||||
| state->rom_size = ROM_SIZE_MIN; | state->rom_size = ROM_SIZE_MIN; | ||||
| @@ -82,6 +83,7 @@ static ErrorInfo* resolve_symbols(AssemblerState *state) | |||||
| ASMInstruction *inst = state->instructions; | ASMInstruction *inst = state->instructions; | ||||
| const ASMSymbol *symbol; | const ASMSymbol *symbol; | ||||
| DEBUG("Resolving symbols") | |||||
| while (inst) { | while (inst) { | ||||
| if (inst->symbol) { | if (inst->symbol) { | ||||
| symbol = asm_symtable_find(state->symtable, inst->symbol); | symbol = asm_symtable_find(state->symtable, inst->symbol); | ||||
| @@ -145,6 +147,7 @@ static void write_header(const ASMHeaderInfo *info, uint8_t *binary) | |||||
| */ | */ | ||||
| static void serialize_binary(const AssemblerState *state, uint8_t *binary) | static void serialize_binary(const AssemblerState *state, uint8_t *binary) | ||||
| { | { | ||||
| DEBUG("Serializing binary data") | |||||
| memset(binary, 0xFF, state->rom_size); | memset(binary, 0xFF, state->rom_size); | ||||
| const ASMInstruction *inst = state->instructions; | const ASMInstruction *inst = state->instructions; | ||||
| @@ -188,16 +191,13 @@ size_t assemble(const LineBuffer *source, uint8_t **binary_ptr, ErrorInfo **ei_p | |||||
| asm_symtable_init(&state.symtable); | asm_symtable_init(&state.symtable); | ||||
| #ifdef DEBUG_MODE | |||||
| asm_lines_print(state.lines); | |||||
| #endif | |||||
| if (TRACE_LEVEL) | |||||
| asm_lines_print(state.lines); | |||||
| if ((error_info = tokenize(&state))) | if ((error_info = tokenize(&state))) | ||||
| goto error; | goto error; | ||||
| if ((error_info = resolve_defaults(&state))) | if ((error_info = resolve_defaults(&state))) | ||||
| goto error; | goto error; | ||||
| if ((error_info = resolve_symbols(&state))) | if ((error_info = resolve_symbols(&state))) | ||||
| goto error; | goto error; | ||||
| @@ -240,6 +240,7 @@ bool assemble_file(const char *src_path, const char *dst_path) | |||||
| return false; | return false; | ||||
| } | } | ||||
| DEBUG("Writing output file") | |||||
| bool success = write_binary_file(dst_path, binary, size); | bool success = write_binary_file(dst_path, binary, size); | ||||
| free(binary); | free(binary); | ||||
| return success; | return success; | ||||
| @@ -373,7 +373,7 @@ static inline bool is_header_offset_valid(uint16_t offset) | |||||
| ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | ErrorInfo* preprocess(AssemblerState *state, const LineBuffer *source) | ||||
| { | { | ||||
| ErrorInfo* ei = NULL; | ErrorInfo* ei = NULL; | ||||
| DEBUG("Running preprocessor:") | |||||
| DEBUG("Running preprocessor") | |||||
| if ((ei = build_asm_lines(source, &state->lines, NULL, &state->includes, 0))) | if ((ei = build_asm_lines(source, &state->lines, NULL, &state->includes, 0))) | ||||
| return ei; | return ei; | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| @@ -196,17 +196,16 @@ bool asm_deftable_remove( | |||||
| return hash_table_remove(tab, key, size); | return hash_table_remove(tab, key, size); | ||||
| } | } | ||||
| #ifdef DEBUG_MODE | |||||
| /* | /* | ||||
| DEBUG FUNCTION: Print out an ASMLine list to stdout. | |||||
| @TRACE_LEVEL | |||||
| Print out an ASMLine list to stdout. | |||||
| */ | */ | ||||
| void asm_lines_print(const ASMLine *line) | void asm_lines_print(const ASMLine *line) | ||||
| { | { | ||||
| DEBUG("Dumping ASMLines:") | |||||
| TRACE("Dumping ASMLines:") | |||||
| while (line) { | while (line) { | ||||
| DEBUG("- %-40.*s [%s:%02zu]", (int) line->length, line->data, | |||||
| TRACE("- %-40.*s [%s:%02zu]", (int) line->length, line->data, | |||||
| line->filename, line->original->lineno) | line->filename, line->original->lineno) | ||||
| line = line->next; | line = line->next; | ||||
| } | } | ||||
| } | } | ||||
| #endif | |||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #pragma once | #pragma once | ||||
| @@ -112,6 +112,4 @@ const ASMDefine* asm_deftable_find(const ASMDefineTable*, const char*, size_t); | |||||
| void asm_deftable_insert(ASMDefineTable*, ASMDefine*); | void asm_deftable_insert(ASMDefineTable*, ASMDefine*); | ||||
| bool asm_deftable_remove(ASMDefineTable*, const char*, size_t); | bool asm_deftable_remove(ASMDefineTable*, const char*, size_t); | ||||
| #ifdef DEBUG_MODE | |||||
| void asm_lines_print(const ASMLine*); | void asm_lines_print(const ASMLine*); | ||||
| #endif | |||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| @@ -468,6 +468,7 @@ ErrorInfo* tokenize(AssemblerState *state) | |||||
| const ASMLine *line = state->lines; | const ASMLine *line = state->lines; | ||||
| size_t offset = 0; | size_t offset = 0; | ||||
| DEBUG("Running tokenizer") | |||||
| init_layout_info(&li, state); | init_layout_info(&li, state); | ||||
| memset(si.slots, -1, MMU_NUM_ROM_BANKS); | memset(si.slots, -1, MMU_NUM_ROM_BANKS); | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <dirent.h> | #include <dirent.h> | ||||
| @@ -24,15 +24,14 @@ static void print_help(const char *arg1) | |||||
| " -h, --help show this help message and exit\n" | " -h, --help show this help message and exit\n" | ||||
| " -v, --version show crater's version number and exit\n" | " -v, --version show crater's version number and exit\n" | ||||
| " -f, --fullscreen enable fullscreen mode\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" | " <rom_path> path to the rom file to execute; if not given, will look\n" | ||||
| " in the roms/ directory and prompt the user\n" | " in the roms/ directory and prompt the user\n" | ||||
| "\n" | "\n" | ||||
| "advanced options:\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" | |||||
| " -g, --debug show logging information while running; add twice (-g -g)\n" | |||||
| " to show more detailed logs, including an emulator trace\n" | |||||
| " -s, --scale <n> scale the game screen by an integer factor\n" | |||||
| " (applies to windowed mode only)\n" | |||||
| " -a, --assemble <in> [<out>] convert z80 assembly source code into a\n" | " -a, --assemble <in> [<out>] convert z80 assembly source code into a\n" | ||||
| " binary file that can be run by crater\n" | " binary file that can be run by crater\n" | ||||
| " -d, --disassemble <in> [<out>] convert a binary file into z80 assembly\n" | " -d, --disassemble <in> [<out>] convert a binary file into z80 assembly\n" | ||||
| @@ -202,7 +201,7 @@ static int parse_args(Config *config, int argc, char *argv[]) | |||||
| } | } | ||||
| config->scale = scale; | config->scale = scale; | ||||
| } else if (!strcmp(arg, "g") || !strcmp(arg, "debug")) { | } else if (!strcmp(arg, "g") || !strcmp(arg, "debug")) { | ||||
| config->debug = true; | |||||
| config->debug++; | |||||
| } else if (!strcmp(arg, "a") || !strcmp(arg, "assemble")) { | } else if (!strcmp(arg, "a") || !strcmp(arg, "assemble")) { | ||||
| if (paths_read >= 1) { | if (paths_read >= 1) { | ||||
| config->src_path = config->rom_path; | config->src_path = config->rom_path; | ||||
| @@ -277,7 +276,7 @@ static bool sanity_check(Config* config) | |||||
| } else if (config->assemble && config->disassemble) { | } else if (config->assemble && config->disassemble) { | ||||
| ERROR("cannot assemble and disassemble at the same time") | ERROR("cannot assemble and disassemble at the same time") | ||||
| return false; | return false; | ||||
| } else if (assembler && (config->debug || config->fullscreen || config->scale > 1)) { | |||||
| } else if (assembler && (config->fullscreen || config->scale > 1)) { | |||||
| ERROR("cannot specify emulator options in assembler mode") | ERROR("cannot specify emulator options in assembler mode") | ||||
| return false; | return false; | ||||
| } else if (assembler && !config->src_path) { | } else if (assembler && !config->src_path) { | ||||
| @@ -309,7 +308,7 @@ int config_create(Config** config_ptr, int argc, char* argv[]) | |||||
| Config *config = cr_malloc(sizeof(Config)); | Config *config = cr_malloc(sizeof(Config)); | ||||
| int retval; | int retval; | ||||
| config->debug = false; | |||||
| config->debug = 0; | |||||
| config->assemble = false; | config->assemble = false; | ||||
| config->disassemble = false; | config->disassemble = false; | ||||
| config->fullscreen = false; | config->fullscreen = false; | ||||
| @@ -342,21 +341,20 @@ void config_destroy(Config *config) | |||||
| free(config); | free(config); | ||||
| } | } | ||||
| #ifdef DEBUG_MODE | |||||
| /* | /* | ||||
| DEBUG FUNCTION: Print out all config arguments to stdout. | |||||
| @DEBUG_LEVEL | |||||
| Print out all config arguments to stdout. | |||||
| */ | */ | ||||
| void config_dump_args(const Config* config) | void config_dump_args(const Config* config) | ||||
| { | { | ||||
| DEBUG("Dumping arguments:") | DEBUG("Dumping arguments:") | ||||
| DEBUG("- fullscreen: %s", config->fullscreen ? "true" : "false") | |||||
| DEBUG("- scale: %d", config->scale) | |||||
| DEBUG("- debug: %s", config->debug ? "true" : "false") | |||||
| DEBUG("- debug: %d", config->debug) | |||||
| DEBUG("- assemble: %s", config->assemble ? "true" : "false") | DEBUG("- assemble: %s", config->assemble ? "true" : "false") | ||||
| DEBUG("- disassemble: %s", config->disassemble ? "true" : "false") | DEBUG("- disassemble: %s", config->disassemble ? "true" : "false") | ||||
| DEBUG("- fullscreen: %s", config->fullscreen ? "true" : "false") | |||||
| DEBUG("- scale: %d", config->scale) | |||||
| DEBUG("- rom_path: %s", config->rom_path ? config->rom_path : "(null)") | DEBUG("- rom_path: %s", config->rom_path ? config->rom_path : "(null)") | ||||
| DEBUG("- src_path: %s", config->src_path ? config->src_path : "(null)") | DEBUG("- src_path: %s", config->src_path ? config->src_path : "(null)") | ||||
| DEBUG("- dst_path: %s", config->dst_path ? config->dst_path : "(null)") | DEBUG("- dst_path: %s", config->dst_path ? config->dst_path : "(null)") | ||||
| DEBUG("- overwrite: %s", config->overwrite ? "true" : "false") | DEBUG("- overwrite: %s", config->overwrite ? "true" : "false") | ||||
| } | } | ||||
| #endif | |||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #pragma once | #pragma once | ||||
| @@ -21,7 +21,7 @@ | |||||
| /* Structs */ | /* Structs */ | ||||
| typedef struct { | typedef struct { | ||||
| bool debug; | |||||
| int debug; | |||||
| bool assemble; | bool assemble; | ||||
| bool disassemble; | bool disassemble; | ||||
| bool fullscreen; | bool fullscreen; | ||||
| @@ -36,7 +36,4 @@ typedef struct { | |||||
| int config_create(Config**, int, char*[]); | int config_create(Config**, int, char*[]); | ||||
| void config_destroy(Config*); | void config_destroy(Config*); | ||||
| #ifdef DEBUG_MODE | |||||
| void config_dump_args(const Config*); | void config_dump_args(const Config*); | ||||
| #endif | |||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <signal.h> | #include <signal.h> | ||||
| @@ -36,9 +36,8 @@ void iomanager_emulate(GameGear *gg) | |||||
| while (!caught_signal) { | while (!caught_signal) { | ||||
| if (gamegear_simulate(gg)) { | if (gamegear_simulate(gg)) { | ||||
| ERROR("caught exception: %s", gamegear_get_exception(gg)) | ERROR("caught exception: %s", gamegear_get_exception(gg)) | ||||
| #ifdef DEBUG_MODE | |||||
| z80_dump_registers(&gg->cpu); | |||||
| #endif | |||||
| if (DEBUG_LEVEL) | |||||
| z80_dump_registers(&gg->cpu); | |||||
| break; | break; | ||||
| } | } | ||||
| usleep(1000 * 1000 / 60); | usleep(1000 * 1000 / 60); | ||||
| @@ -46,9 +45,8 @@ void iomanager_emulate(GameGear *gg) | |||||
| if (caught_signal) { | if (caught_signal) { | ||||
| WARN("caught signal, stopping...") | WARN("caught signal, stopping...") | ||||
| #ifdef DEBUG_MODE | |||||
| z80_dump_registers(&gg->cpu); | |||||
| #endif | |||||
| if (DEBUG_LEVEL) | |||||
| z80_dump_registers(&gg->cpu); | |||||
| } | } | ||||
| DEBUG("IOManager unpowering GameGear") | DEBUG("IOManager unpowering GameGear") | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #pragma once | #pragma once | ||||
| @@ -10,30 +10,38 @@ | |||||
| /* Internal usage only */ | /* Internal usage only */ | ||||
| #define LOG_MSG_(dest, level, extra, after, ...) \ | |||||
| do { \ | |||||
| fprintf(dest, level ": " __VA_ARGS__); \ | |||||
| extra; \ | |||||
| fprintf(dest, "\n"); \ | |||||
| after; \ | |||||
| #define LOG_MSG_(dest, level, type, extra, after, ...) \ | |||||
| do { \ | |||||
| if (logging_level_ >= level) { \ | |||||
| fprintf(dest, type " " __VA_ARGS__); \ | |||||
| extra; \ | |||||
| fprintf(dest, "\n"); \ | |||||
| after; \ | |||||
| } \ | |||||
| } while (0); | } while (0); | ||||
| #define LOG_ERR_(...) LOG_MSG_(stderr, __VA_ARGS__) | #define LOG_ERR_(...) LOG_MSG_(stderr, __VA_ARGS__) | ||||
| #define LOG_OUT_(...) LOG_MSG_(stdout, __VA_ARGS__) | #define LOG_OUT_(...) LOG_MSG_(stdout, __VA_ARGS__) | ||||
| #define PRINT_ERRNO_() fprintf(stderr, ": %s", strerror(errno)) | |||||
| #define PRINT_ERR_ fprintf(stderr, ": %s", strerror(errno)) | |||||
| #define EXIT_FAIL_ exit(EXIT_FAILURE) | |||||
| #define DEBUG_TEXT_ "\x1b[0m\x1b[37m[DEBUG]\x1b[0m" | |||||
| #define TRACE_TEXT_ "\x1b[1m\x1b[33m[TRACE]\x1b[0m" | |||||
| unsigned logging_level_; | |||||
| /* Public logging macros */ | /* Public logging macros */ | ||||
| #define FATAL(...) LOG_ERR_("fatal", {}, exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define FATAL_ERRNO(...) LOG_ERR_("fatal", PRINT_ERRNO_(), exit(EXIT_FAILURE), __VA_ARGS__) | |||||
| #define ERROR(...) LOG_ERR_("error", {}, {}, __VA_ARGS__) | |||||
| #define ERROR_ERRNO(...) LOG_ERR_("error", PRINT_ERRNO_(), {}, __VA_ARGS__) | |||||
| #define WARN(...) LOG_ERR_("warning", {}, {}, __VA_ARGS__) | |||||
| #define WARN_ERRNO(...) LOG_ERR_("warning", PRINT_ERRNO_(), {}, __VA_ARGS__) | |||||
| #ifdef DEBUG_MODE | |||||
| #define DEBUG(...) LOG_OUT_("[DEBUG]", {}, {}, __VA_ARGS__) | |||||
| #else | |||||
| #define DEBUG(...) {} | |||||
| #endif | |||||
| #define FATAL(...) LOG_ERR_(0, "fatal:", {}, EXIT_FAIL_, __VA_ARGS__) | |||||
| #define FATAL_ERRNO(...) LOG_ERR_(0, "fatal:", PRINT_ERR_, EXIT_FAIL_, __VA_ARGS__) | |||||
| #define ERROR(...) LOG_ERR_(0, "error:", {}, {}, __VA_ARGS__) | |||||
| #define ERROR_ERRNO(...) LOG_ERR_(0, "error:", PRINT_ERR_, {}, __VA_ARGS__) | |||||
| #define WARN(...) LOG_ERR_(0, "warning:", {}, {}, __VA_ARGS__) | |||||
| #define WARN_ERRNO(...) LOG_ERR_(0, "warning:", PRINT_ERR_, {}, __VA_ARGS__) | |||||
| #define DEBUG(...) LOG_OUT_(1, DEBUG_TEXT_, {}, {}, __VA_ARGS__) | |||||
| #define TRACE(...) LOG_OUT_(2, TRACE_TEXT_, {}, {}, __VA_ARGS__) | |||||
| #define SET_LOG_LEVEL(level) logging_level_ = (level); | |||||
| #define DEBUG_LEVEL (logging_level_ >= 1) | |||||
| #define TRACE_LEVEL (logging_level_ >= 2) | |||||
| @@ -1,9 +1,10 @@ | |||||
| /* 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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include <string.h> | #include <string.h> | ||||
| #include "mmu.h" | |||||
| #include "logging.h" | #include "logging.h" | ||||
| #include "util.h" | #include "util.h" | ||||
| #include "z80.h" | #include "z80.h" | ||||
| @@ -31,6 +32,27 @@ void mmu_free(MMU *mmu) | |||||
| } | } | ||||
| /* | /* | ||||
| @DEBUG_LEVEL | |||||
| Print out the bank mapping. | |||||
| */ | |||||
| static void dump_bank_table(const MMU *mmu, const uint8_t *data) | |||||
| { | |||||
| char buffer[49]; | |||||
| size_t group, elem, bank; | |||||
| DEBUG("Dumping MMU bank table:") | |||||
| for (group = 0; group < MMU_NUM_ROM_BANKS / 8; group++) { | |||||
| for (elem = 0; elem < 8; elem++) { | |||||
| bank = 8 * group + elem; | |||||
| snprintf(buffer + 6 * elem, 7, "%02zX=%02zX ", bank, | |||||
| (mmu->rom_banks[bank] - data) >> 14); | |||||
| } | |||||
| buffer[47] = '\0'; | |||||
| DEBUG("- %s", buffer) | |||||
| } | |||||
| } | |||||
| /* | |||||
| Load a block of ROM into the MMU. | Load a block of ROM into the MMU. | ||||
| size must be a multiple of MMU_ROM_BANK_SIZE (16 KB), the load will fail | size must be a multiple of MMU_ROM_BANK_SIZE (16 KB), the load will fail | ||||
| @@ -55,19 +77,8 @@ void mmu_load_rom(MMU *mmu, const uint8_t *data, size_t size) | |||||
| mmu->rom_banks[mirror] = data + (bank * MMU_ROM_BANK_SIZE); | mmu->rom_banks[mirror] = data + (bank * MMU_ROM_BANK_SIZE); | ||||
| } | } | ||||
| #ifdef DEBUG_MODE | |||||
| char temp_str[64]; | |||||
| DEBUG("Dumping MMU bank table:") | |||||
| for (size_t group = 0; group < MMU_NUM_ROM_BANKS / 8; group++) { | |||||
| for (size_t elem = 0; elem < 8; elem++) { | |||||
| bank = 8 * group + elem; | |||||
| snprintf(temp_str + 6 * elem, 7, "%02zX=%02zX ", bank, | |||||
| (mmu->rom_banks[bank] - data) >> 14); | |||||
| } | |||||
| temp_str[47] = '\0'; | |||||
| DEBUG("- %s", temp_str) | |||||
| } | |||||
| #endif | |||||
| if (DEBUG_LEVEL) | |||||
| dump_bank_table(mmu, data); | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include <ctype.h> | #include <ctype.h> | ||||
| @@ -18,11 +18,29 @@ | |||||
| static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x3FF0, 0x1FF0}; | static size_t header_locations[NUM_LOCATIONS] = {0x7FF0, 0x3FF0, 0x1FF0}; | ||||
| #ifdef DEBUG_MODE | |||||
| /* | /* | ||||
| DEBUG FUNCTION: Print out the header to stdout. | |||||
| @DEBUG_LEVEL | |||||
| Given a ROM size, return a pretty string. | |||||
| */ | */ | ||||
| static void print_header(const uint8_t *header) | |||||
| static const char* size_to_string(size_t size) | |||||
| { | |||||
| static char buffer[SIZE_CODE_BUF]; | |||||
| if (!size) | |||||
| strncpy(buffer, "unknown", SIZE_CODE_BUF); | |||||
| else if (size >= (1 << 20)) | |||||
| snprintf(buffer, SIZE_CODE_BUF, "%zu MB", size >> 20); | |||||
| else | |||||
| snprintf(buffer, SIZE_CODE_BUF, "%zu KB", size >> 10); | |||||
| return buffer; | |||||
| } | |||||
| /* | |||||
| @DEBUG_LEVEL | |||||
| Print out the raw header to stdout. | |||||
| */ | |||||
| static void print_header_dump(const uint8_t *header) | |||||
| { | { | ||||
| char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE]; | char header_hex[3 * HEADER_SIZE], header_chr[3 * HEADER_SIZE]; | ||||
| @@ -40,26 +58,27 @@ static void print_header(const uint8_t *header) | |||||
| DEBUG("- header dump (hex): %s", header_hex) | DEBUG("- header dump (hex): %s", header_hex) | ||||
| DEBUG("- header dump (chr): %s", header_chr) | DEBUG("- header dump (chr): %s", header_chr) | ||||
| } | } | ||||
| #endif | |||||
| #ifdef DEBUG_MODE | |||||
| /* | /* | ||||
| DEBUG FUNCTION: Given a ROM size, return a pretty string. | |||||
| @DEBUG_LEVEL | |||||
| Print out the analyzed header to stdout. | |||||
| */ | */ | ||||
| static const char* size_to_string(size_t size) | |||||
| static void print_header_contents(const ROM *rom, const uint8_t *header) | |||||
| { | { | ||||
| static char buffer[SIZE_CODE_BUF]; | |||||
| if (!size) | |||||
| strncpy(buffer, "unknown", SIZE_CODE_BUF); | |||||
| else if (size >= (1 << 20)) | |||||
| snprintf(buffer, SIZE_CODE_BUF, "%zu MB", size >> 20); | |||||
| DEBUG("- header info:") | |||||
| if (rom->reported_checksum == rom->expected_checksum) | |||||
| DEBUG(" - checksum: 0x%04X (valid)", rom->reported_checksum) | |||||
| else | else | ||||
| snprintf(buffer, SIZE_CODE_BUF, "%zu KB", size >> 10); | |||||
| return buffer; | |||||
| DEBUG(" - checksum: 0x%04X (invalid, expected 0x%04X)", | |||||
| rom->reported_checksum, rom->expected_checksum) | |||||
| DEBUG(" - product code: %u (%s)", rom->product_code, | |||||
| rom_product(rom) ? rom_product(rom) : "unknown") | |||||
| DEBUG(" - version: %u", rom->version) | |||||
| DEBUG(" - region code: %u (%s)", rom->region_code, | |||||
| rom_region(rom) ? rom_region(rom) : "unknown") | |||||
| DEBUG(" - reported size: %s", | |||||
| size_to_string(size_code_to_bytes(header[0xF] & 0xF))) | |||||
| } | } | ||||
| #endif | |||||
| /* | /* | ||||
| Parse a ROM image's header, and return whether or not it is valid. | Parse a ROM image's header, and return whether or not it is valid. | ||||
| @@ -88,9 +107,8 @@ static const char* size_to_string(size_t size) | |||||
| */ | */ | ||||
| static bool parse_header(ROM *rom, const uint8_t *header) | static bool parse_header(ROM *rom, const uint8_t *header) | ||||
| { | { | ||||
| #ifdef DEBUG_MODE | |||||
| print_header(header); | |||||
| #endif | |||||
| if (DEBUG_LEVEL) | |||||
| print_header_dump(header); | |||||
| rom->reported_checksum = header[0xA] + (header[0xB] << 8); | rom->reported_checksum = header[0xA] + (header[0xB] << 8); | ||||
| rom->expected_checksum = compute_checksum(rom->data, rom->size, header[0xF]); | rom->expected_checksum = compute_checksum(rom->data, rom->size, header[0xF]); | ||||
| @@ -99,19 +117,8 @@ static bool parse_header(ROM *rom, const uint8_t *header) | |||||
| rom->version = header[0xE] & 0x0F; | rom->version = header[0xE] & 0x0F; | ||||
| rom->region_code = header[0xF] >> 4; | rom->region_code = header[0xF] >> 4; | ||||
| DEBUG("- header info:") | |||||
| if (rom->reported_checksum == rom->expected_checksum) | |||||
| DEBUG(" - checksum: 0x%04X (valid)", rom->reported_checksum) | |||||
| else | |||||
| DEBUG(" - checksum: 0x%04X (invalid, expected 0x%04X)", | |||||
| rom->reported_checksum, rom->expected_checksum) | |||||
| DEBUG(" - product code: %u (%s)", rom->product_code, | |||||
| rom_product(rom) ? rom_product(rom) : "unknown") | |||||
| DEBUG(" - version: %u", rom->version) | |||||
| DEBUG(" - region code: %u (%s)", rom->region_code, | |||||
| rom_region(rom) ? rom_region(rom) : "unknown") | |||||
| DEBUG(" - reported size: %s", | |||||
| size_to_string(size_code_to_bytes(header[0xF] & 0xF))) | |||||
| if (DEBUG_LEVEL) | |||||
| print_header_contents(rom, header); | |||||
| return true; | return true; | ||||
| } | } | ||||
| @@ -1,8 +1,9 @@ | |||||
| /* 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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #include "logging.h" | |||||
| #include "z80.h" | #include "z80.h" | ||||
| #include "disassembler.h" | |||||
| #include "logging.h" | |||||
| #define REG_AF 0 | #define REG_AF 0 | ||||
| #define REG_BC 1 | #define REG_BC 1 | ||||
| @@ -19,7 +20,6 @@ | |||||
| #define FLAG_ZERO 6 | #define FLAG_ZERO 6 | ||||
| #define FLAG_SIGN 7 | #define FLAG_SIGN 7 | ||||
| #ifdef DEBUG_MODE | |||||
| #define BINARY_FMT "0b%u%u%u%u%u%u%u%u" // Used by z80_dump_registers() | #define BINARY_FMT "0b%u%u%u%u%u%u%u%u" // Used by z80_dump_registers() | ||||
| #define BINARY_VAL(data) \ | #define BINARY_VAL(data) \ | ||||
| (data & (1 << 7) ? 1 : 0), \ | (data & (1 << 7) ? 1 : 0), \ | ||||
| @@ -30,7 +30,6 @@ | |||||
| (data & (1 << 2) ? 1 : 0), \ | (data & (1 << 2) ? 1 : 0), \ | ||||
| (data & (1 << 1) ? 1 : 0), \ | (data & (1 << 1) ? 1 : 0), \ | ||||
| (data & (1 << 0) ? 1 : 0) | (data & (1 << 0) ? 1 : 0) | ||||
| #endif | |||||
| /* | /* | ||||
| Initialize a Z80 object. | Initialize a Z80 object. | ||||
| @@ -92,6 +91,7 @@ static inline uint16_t get_pair(Z80 *z80, uint8_t pair) | |||||
| case REG_HL: return (z80->regfile.h << 8) + z80->regfile.l; | case REG_HL: return (z80->regfile.h << 8) + z80->regfile.l; | ||||
| default: FATAL("invalid call: get_pair(z80, %u)", pair) | default: FATAL("invalid call: get_pair(z80, %u)", pair) | ||||
| } | } | ||||
| return 0; | |||||
| } | } | ||||
| /* | /* | ||||
| @@ -164,6 +164,16 @@ static inline void increment_refresh_counter(Z80 *z80) | |||||
| #include "z80_ops.inc.c" | #include "z80_ops.inc.c" | ||||
| /* | /* | ||||
| @TRACE_LEVEL | |||||
| Trace the instruction about to be executed by the CPU. | |||||
| */ | |||||
| static inline void trace_instruction(const Z80 *z80) | |||||
| { | |||||
| TRACE("PC @ 0x%04X", z80->regfile.pc) | |||||
| // TODO | |||||
| } | |||||
| /* | |||||
| Emulate the given number of cycles of the Z80, or until an exception. | Emulate the given number of cycles of the Z80, or until an exception. | ||||
| The return value indicates whether the exception flag is set. If it is, | The return value indicates whether the exception flag is set. If it is, | ||||
| @@ -175,6 +185,8 @@ bool z80_do_cycles(Z80 *z80, double cycles) | |||||
| cycles -= z80->pending_cycles; | cycles -= z80->pending_cycles; | ||||
| while (cycles > 0 && !z80->except) { | while (cycles > 0 && !z80->except) { | ||||
| uint8_t opcode = mmu_read_byte(z80->mmu, z80->regfile.pc); | uint8_t opcode = mmu_read_byte(z80->mmu, z80->regfile.pc); | ||||
| if (TRACE_LEVEL) | |||||
| trace_instruction(z80); | |||||
| increment_refresh_counter(z80); | increment_refresh_counter(z80); | ||||
| cycles -= (*instruction_lookup_table[opcode])(z80, opcode); | cycles -= (*instruction_lookup_table[opcode])(z80, opcode); | ||||
| } | } | ||||
| @@ -183,9 +195,9 @@ bool z80_do_cycles(Z80 *z80, double cycles) | |||||
| return z80->except; | return z80->except; | ||||
| } | } | ||||
| #ifdef DEBUG_MODE | |||||
| /* | /* | ||||
| DEBUG FUNCTION: Print out all register values to stdout. | |||||
| @DEBUG_LEVEL | |||||
| Print out all register values to stdout. | |||||
| */ | */ | ||||
| void z80_dump_registers(const Z80 *z80) | void z80_dump_registers(const Z80 *z80) | ||||
| { | { | ||||
| @@ -233,4 +245,3 @@ void z80_dump_registers(const Z80 *z80) | |||||
| DEBUG("- IFF1: %u", rf->iff1) | DEBUG("- IFF1: %u", rf->iff1) | ||||
| DEBUG("- IFF2: %u", rf->iff2) | DEBUG("- IFF2: %u", rf->iff2) | ||||
| } | } | ||||
| #endif | |||||
| @@ -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. */ | Released under the terms of the MIT License. See LICENSE for details. */ | ||||
| #pragma once | #pragma once | ||||
| @@ -35,7 +35,4 @@ typedef struct { | |||||
| void z80_init(Z80*, MMU*); | void z80_init(Z80*, MMU*); | ||||
| void z80_power(Z80*); | void z80_power(Z80*); | ||||
| bool z80_do_cycles(Z80*, double); | bool z80_do_cycles(Z80*, double); | ||||
| #ifdef DEBUG_MODE | |||||
| void z80_dump_registers(const Z80*); | void z80_dump_registers(const Z80*); | ||||
| #endif | |||||