From ec253905adf47b9c3eb26e144376b3c4e2a059ba Mon Sep 17 00:00:00 2001 From: Peter Evans Date: Sat, 24 Feb 2018 16:30:46 -0600 Subject: [PATCH] Add resume command, finder function, arg parser, execute --- include/vm_debug.h | 49 ++++++++++------ src/vm_debug.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ tests/vm_debug.c | 51 ++++++++++++++++- 3 files changed, 217 insertions(+), 19 deletions(-) diff --git a/include/vm_debug.h b/include/vm_debug.h index 9d97e85..51a54b0 100644 --- a/include/vm_debug.h +++ b/include/vm_debug.h @@ -1,24 +1,8 @@ #ifndef _VM_DEBUG_H_ #define _VM_DEBUG_H_ -typedef struct { - /* - * Most commands that need an argument will simply use addr1, but a - * few have more than one address--hence addr2. - */ - int addr1; - int addr2; - - /* - * If we have a thing we want to work with, but want to leave what - * that is up to the helper func, then you can write it into the - * target. - * - * If a command uses target, followed by an address, that address - * will be in addr1. - */ - char target[256]; -} vm_debug_args; +struct vm_debug_args; +typedef struct vm_debug_args vm_debug_args; typedef void (*vm_debug_func)(vm_debug_args *); @@ -52,9 +36,38 @@ typedef struct { char *desc; } vm_debug_cmd; +struct vm_debug_args { + /* + * Most commands that need an argument will simply use addr1, but a + * few have more than one address--hence addr2. + */ + int addr1; + int addr2; + + /* + * If we have a thing we want to work with, but want to leave what + * that is up to the helper func, then you can write it into the + * target. + * + * If a command uses target, followed by an address, that address + * will be in addr1. + */ + char *target; + + /* + * The command our arguments are attached to; from here we can call + * the handler with ourselves. (Very meta.) + */ + vm_debug_cmd *cmd; +}; + #define DEBUG_CMD(x) \ void vm_debug_cmd_##x (vm_debug_args *args) +extern vm_debug_cmd *vm_debug_find_cmd(const char *); +extern void vm_debug_execute(const char *); + extern DEBUG_CMD(help); +extern DEBUG_CMD(resume); #endif diff --git a/src/vm_debug.c b/src/vm_debug.c index 45afd7a..eaa37d9 100644 --- a/src/vm_debug.c +++ b/src/vm_debug.c @@ -2,18 +2,149 @@ * vm_debug.c */ +#include #include +#include +#include #include "vm_debug.h" #include "vm_di.h" +#include "vm_reflect.h" vm_debug_cmd cmdtable[] = { { "help", "h", 0, vm_debug_cmd_help, "", "Print out this list of commands", }, + { "resume", "r", 0, vm_debug_cmd_resume, "", + "Resume execution", }, }; #define CMDTABLE_SIZE (sizeof(cmdtable) / sizeof(vm_debug_cmd)) +char * +vm_debug_next_arg(char **str) +{ + char *tok; + + while ((tok = strsep(str, " "))) { + if (tok == NULL) { + return NULL; + } + + if (*tok == '\0') { + continue; + } + + break; + } + + return tok; +} + +int +vm_debug_addr(const char *str) +{ + int addr; + + if (str == NULL) { + return -1; + } + + addr = strtol(str, NULL, 16); + if (addr == 0 && errno == EINVAL) { + return -1; + } + + return addr; +} + +void +vm_debug_execute(const char *str) +{ + char *tok, *ebuf; + vm_debug_cmd *cmd; + vm_debug_args args; + + ebuf = strdup(str); + cmd = NULL; + + tok = vm_debug_next_arg(&ebuf); + + // No input + if (tok == NULL) { + return; + } + + cmd = vm_debug_find_cmd(tok); + + // No command found + if (cmd == NULL) { + return; + } + + args.addr1 = 0; + args.addr2 = 0; + args.target = NULL; + + switch (cmd->nargs) { + case 2: + args.target = vm_debug_next_arg(&ebuf); + + // This _may_ be -1 if we have a string target for argument + // 1, as in the writestate command + args.addr1 = vm_debug_addr(args.target); + + args.addr2 = vm_debug_addr(vm_debug_next_arg(&ebuf)); + + // But if this is -1, then something went wrong + if (args.addr2 == -1) { + return; + } + + break; + + case 1: + args.addr1 = vm_debug_addr(vm_debug_next_arg(&ebuf)); + + // Oh no + if (args.addr1 == -1) { + return; + } + + break; + + case 0: + default: + break; + } + + cmd->handler(&args); +} + +static int +cmd_compar(const void *k, const void *elem) +{ + const char *key = (const char *)k; + const vm_debug_cmd *cmd = (const vm_debug_cmd *)elem; + + if (strlen(key) < 3) { + return strcmp(key, cmd->abbrev); + } + + return strcmp(key, cmd->name); +} + +/* + * Return the cmd struct for a command that matches str, which can + * either be an abbreviation (if 1 or 2 characters) or a full name (if + * otherwise). If no matching cmd can be found, return NULL. + */ +vm_debug_cmd * +vm_debug_find_cmd(const char *str) +{ + return (vm_debug_cmd *)bsearch(str, &cmdtable, CMDTABLE_SIZE, + sizeof(vm_debug_cmd), cmd_compar); +} + DEBUG_CMD(help) { FILE *stream = (FILE *)vm_di_get(VM_OUTPUT); @@ -25,3 +156,8 @@ DEBUG_CMD(help) cmd->argdesc, cmd->desc); } } + +DEBUG_CMD(resume) +{ + vm_reflect_pause(NULL); +} diff --git a/tests/vm_debug.c b/tests/vm_debug.c index 9b4be2f..83a7367 100644 --- a/tests/vm_debug.c +++ b/tests/vm_debug.c @@ -2,13 +2,20 @@ #include #include + +#include "apple2.h" +#include "apple2.reflect.h" #include "vm_debug.h" #include "vm_di.h" +#include "vm_reflect.h" static char buf[BUFSIZ]; static FILE *stream = NULL; static vm_debug_args args; +static apple2 *mach; +static vm_reflect *ref; + static void setup() { @@ -24,6 +31,15 @@ setup() // Writing to stream will now write to buf setvbuf(stream, buf, _IOFBF, BUFSIZ); + + ref = vm_reflect_create(); + vm_di_set(VM_REFLECT, ref); + + mach = apple2_create(100, 100); + vm_di_set(VM_MACHINE, mach); + vm_di_set(VM_CPU, mach->cpu); + + apple2_reflect_init(); } static void @@ -31,6 +47,8 @@ teardown() { memset(buf, 0, BUFSIZ); fclose(stream); + + apple2_free(mach); } TestSuite(vm_debug, .init = setup, .fini = teardown); @@ -43,5 +61,36 @@ Test(vm_debug, cmd_help) { vm_debug_cmd_help(&args); cr_assert_neq(strlen(buf), 0); - printf("%s", buf); +} + +Test(vm_debug, execute) +{ + vm_debug_execute("help"); + cr_assert_neq(strlen(buf), 0); +} + +Test(vm_debug, find_cmd) +{ + vm_debug_cmd *cmd; + + // Can we find anything? + cr_assert_neq(vm_debug_find_cmd("resume"), NULL); + + // Make sure NULL is returned for bad commands + cr_assert_eq(vm_debug_find_cmd("THIS_DOESNT_EXIST"), NULL); + + // Find a second command... + cmd = vm_debug_find_cmd("help"); + cr_assert_neq(cmd, NULL); + + // and see if we can find the short version + cr_assert_eq(vm_debug_find_cmd("h"), cmd); +} + +Test(vm_debug, cmd_resume) +{ + mach->paused = true; + vm_debug_cmd_resume(&args); + + cr_assert_eq(mach->paused, false); }