better shell loop
authorJustin Wind <justin.wind@gmail.com>
Thu, 12 Apr 2012 23:14:55 +0000 (16:14 -0700)
committerJustin Wind <justin.wind@gmail.com>
Thu, 12 Apr 2012 23:14:55 +0000 (16:14 -0700)
cleaned up vm-dcpu16.c, made the shell loop simpler and better

dcpu16.c
vm-dcpu16.c

index cfa6d8a2b02e2faa3f11e144db8528b0338a8d5b..2a59d24e1cca31f5e161163fc8578d14fdcdc968 100644 (file)
--- a/dcpu16.c
+++ b/dcpu16.c
@@ -25,6 +25,8 @@
  *  TODO
  *    drop checks for assigning to literals -- it won't affect anything anyhow
  *    debug short literal decoding
+ *    add callbacks queues for set/get, attach your own filters
+ *      such as a display
  */
 
 static const char * const src_id_ = "$Id$";
@@ -312,7 +314,7 @@ OP_IMPL(add) {
     if (val_a < 0x1f) {
         *a = acc;
     }
-    d->o = acc >> 16;
+    d->o = (acc > 0xffff);
 
     d->cycle += 2;
 }
@@ -324,9 +326,9 @@ OP_IMPL(sub) {
 
     if (val_a < 0x1f) {
         *a = acc;
-    }
-    d->o = acc >> 16;
+    d->o = (acc > 0xffff);
 
+    }
     d->cycle += 2;
 }
 
index 0045d480207ad535ebb48b4396d36c7bcd30bc26..522682a6732e871ff50157323a227b85e619e76e 100644 (file)
 #include "dcpu16.h"
 
 /*
- *  cli driver for dcpu16 core
+ *  shell-like driver for dcpu16 core
+ *  provides a basic interface to control a single emulation instance
  *  
  *  Justin Wind <justin.wind@gmail.com>
  *    2012 04 10 - implementation started
+ *    2012 04 12 - cleanup, better shell loop
  *
+ *  TODO
+ *    handle quotes in shell command parsing
+ *    use readline/history.h, since we're using readline anyhow
+ *    ncurses windowing or something, for future display capabilities
  */
 
 static const char * const src_id_ = "$Id$";
@@ -27,6 +33,14 @@ struct options {
     .verbose = 0,
 };
 
+/* global run state, first sigint caught will drop out of run loop and back into shell */
+static volatile unsigned int running_ = 0;
+static
+void sigint_handler_(int sig) {
+    (void)sig;
+    running_ = 0;
+}
+
 #define VERBOSE_PRINTF(...) do { if (opt_.verbose) printf(__VA_ARGS__); } while (0)
 
 static void usage_(char *prog, unsigned int full) {
@@ -37,17 +51,17 @@ static void usage_(char *prog, unsigned int full) {
         prog = x + 1;
 
     if (full)
-        fprintf(f, "%s -- \n\n",
+        fprintf(f, "%s -- dcpu16 emulator core shell\n\n",
                 prog);
 
-    fprintf(f, "Usage: %s [file]\n",
+    fprintf(f, "Usage: %s [-v] [file]\n",
             prog);
 
     if (full) {
         fprintf(f, "\nOptions:\n"
                 "\t [file] -- ram image to load initially\n"
-                "\t -h -- this screen\n"
-                "\t -v -- verbose execution tracing\n");
+                "\t -v -- verbose execution tracing\n"
+                "\t -h -- this screen\n");
 
         fprintf(f, "\n%78s\n", src_id_);
     }
@@ -79,14 +93,49 @@ int str_to_word_(char *s) {
     return l;
 }
 
-/* clears the instance and loads an image into ram starting at addr */
+/* flense a buffer into a newly-allocated argument list */
+/* FIXME: handle quotes */
+static
+int buf_tok_vect_(char ***v, int *c, char *buf) {
+    const char *sep = " \t";
+    const size_t v_grow = 32;
+    size_t v_sz = 32;
+    char *st;
+
+    *c = 0;
+    *v = malloc(v_sz * sizeof **v);
+    if (*v == NULL) {
+        fprintf(stderr, "%s():%s\n", "malloc", strerror(errno));
+        return -1;
+    }
+
+    for ( (*v)[*c] = strtok_r(buf, sep, &st);
+          (*v)[*c];
+          (*v)[*c] = strtok_r(NULL, sep, &st)
+        ) {
+            (*c)++;
+
+            if ((size_t)(*c) == v_sz) {
+                void *tmp_ptr = realloc(*v, (v_sz + v_grow) * sizeof **v);
+                if (tmp_ptr == NULL) {
+                    fprintf(stderr, "%s():%s\n", "realloc", strerror(errno));
+                    free(*v);
+                    *v = NULL;
+                    return -1;
+                }
+                v_sz += v_grow;
+            }
+    }
+
+    return 0;
+}
+
+/* resets the instance and loads an image into ram starting at addr */
 static
 int file_load_(struct dcpu16 *vm, char *filename, DCPU16_WORD addr) {
     FILE *f;
     size_t r;
 
-    assert(addr < DCPU16_RAM);
-
     dcpu16_reset(vm);
 
     f = fopen(filename, "rb");
@@ -107,7 +156,19 @@ int file_load_(struct dcpu16 *vm, char *filename, DCPU16_WORD addr) {
     return 0;
 }
 
-/* the commands the vm shell can execute */
+/*
+    Here follows the various commands the shell can execute.
+
+    At invocation, a command function will have already had its
+    number of arguments vetted, but will need command-specific
+    argument verifications done.
+
+    The arg_vector contains the command as the first entry, and
+    as such, arg_count will always be at least 1.
+    However, the args_min and args_max entries in struct command_
+    only refer to the counts of arguments, not the entries in the
+    argv.
+*/
 
 struct command_ {
     char *name;
@@ -117,51 +178,50 @@ struct command_ {
     void (*help)(FILE *f, unsigned int);
 };
 
-#define COMMAND_IMPL(x) static int command_##x##_(struct dcpu16 *vm, int token_count, char **token_vector)
+#define COMMAND_IMPL(x) static int command_##x##_(struct dcpu16 *vm, int arg_count, char **arg_vector)
 #define COMMAND_HELP(x) static void command_##x##_help_(FILE *f, unsigned int summary)
 #define COMMAND_ENTRY(x, y, z) { #x, y, z, command_##x##_, command_##x##_help_ }
 
 
 COMMAND_IMPL(quit) {
-    (void)vm, (void)token_count, (void)token_vector;
+    (void)vm, (void)arg_count, (void)arg_vector;
     VERBOSE_PRINTF("done\n");
     return -1;
 }
 COMMAND_HELP(quit) {
-    fprintf(f, "quit\n");
+    fprintf(f, "\tquit\n");
     if (summary) return;
 
-    fprintf(f, "\tExits the emulator.\n");
+    fprintf(f, "Exits the emulator.\n");
 }
 
 
 COMMAND_IMPL(load) {
     int addr = 0;
 
-    if (token_count > 1) {
-        addr = str_to_word_(token_vector[1]);
+    if (arg_count > 2) {
+        addr = str_to_word_(arg_vector[2]);
         if (addr < 0) {
-            fprintf(stderr, "address '%s' is not a valid word: %s\n", token_vector[1], strerror(errno));
+            fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[2], strerror(errno));
             return 0;
         }
     }
 
-    if (file_load_(vm, token_vector[0], addr)) {
-        fprintf(stderr, "failed to load '%s'\n", token_vector[0]);
+    if (file_load_(vm, arg_vector[1], addr)) {
+        fprintf(stderr, "failed to load '%s'\n", arg_vector[1]);
         return 0;
     }
-    printf("loaded '%s'", token_vector[0]);
+    printf("loaded '%s'", arg_vector[1]);
     if (addr) printf(" starting at 0x%04x", addr);
     printf("\n");
 
     return 0;
 }
 COMMAND_HELP(load) {
-    fprintf(f, "load file [addr]\n");
+    fprintf(f, "\tload file [addr]\n");
     if (summary) return;
 
-    fprintf(f, "Usage: load file [addr]\n"
-               "\tAttempts to load binary image from 'file' at addr.\n");
+    fprintf(f, "Load binary image from 'file' into ram.\n");
 }
 
 
@@ -169,15 +229,15 @@ COMMAND_IMPL(dump) {
     int addr[2];
     int i;
 
-    for (i = 0; i < token_count; i++) {
-        addr[i] = str_to_word_(token_vector[i]);
+    for (i = 1; i < arg_count; i++) {
+        addr[i] = str_to_word_(arg_vector[i]);
         if (addr[i] < 0) {
-            fprintf(stderr, "address '%s' is not a valid word: %s\n", token_vector[i], strerror(errno));
+            fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[i], strerror(errno));
             return 0;
         }
     }
-    if (token_count < 1) addr[0] = vm->pc;
-    if (token_count < 2) addr[1] = addr[0];
+    if (arg_count < 2) addr[0] = vm->pc;
+    if (arg_count < 3) addr[1] = addr[0];
 
     if (addr[1] < addr[0]) {
         fprintf(stderr, "\t'addr_start' must be before addr_end\n");
@@ -189,10 +249,10 @@ COMMAND_IMPL(dump) {
     return 0;
 }
 COMMAND_HELP(dump) {
-    fprintf(f, "dump [addr_start [addr_end]]\n");
+    fprintf(f, "\tdump [addr_start [addr_end]]\n");
     if (summary) return;
 
-    fprintf(f, "\tDisplays contents of ram from addr_start to addr_end.\n");
+    fprintf(f, "Displays contents of ram from addr_start to addr_end.\n");
 }
 
 
@@ -200,15 +260,15 @@ COMMAND_IMPL(disassemble) {
     int addr[2];
     int i;
 
-    for (i = 0; i < token_count; i++) {
-        addr[i] = str_to_word_(token_vector[i]);
+    for (i = 1; i < arg_count; i++) {
+        addr[i] = str_to_word_(arg_vector[i]);
         if (addr[i] < 0) {
-            fprintf(stderr, "address '%s' is not a valid word: %s\n", token_vector[i], strerror(errno));
+            fprintf(stderr, "address '%s' is not a valid word: %s\n", arg_vector[i], strerror(errno));
             return 0;
         }
     }
-    if (token_count < 1) addr[0] = vm->pc;
-    if (token_count < 2) addr[1] = addr[0];
+    if (arg_count < 2) addr[0] = vm->pc;
+    if (arg_count < 3) addr[1] = addr[0];
 
     if (addr[1] < addr[0]) {
         fprintf(stderr, "\t'addr_start' must be before addr_end\n");
@@ -216,35 +276,35 @@ COMMAND_IMPL(disassemble) {
     }
 
     for (i = addr[0]; i <= addr[1]; i++)
-        dcpu16_disassemble_print(vm, i);
+        dcpu16_disassemble_print(vm, i), printf("\n");
 
     return 0;
 }
 COMMAND_HELP(disassemble) {
-    fprintf(f, "disassemble [addr_start [addr_end]]\n");
+    fprintf(f, "\tdisassemble [addr_start [addr_end]]\n");
     if (summary) return;
 
-    fprintf(f, "\tDisplays contents of ram parsed into instructions.\n");
+    fprintf(f, "Displays contents of ram parsed into instructions.\n");
 }
 
 
 COMMAND_IMPL(step) {
-    unsigned long count;
+    unsigned long count = 1;
     char *ep;
 
-    (void)token_count;
-
-    errno = 0;
-    count = strtoul(token_vector[0], &ep, 0);
-    if (errno
-    ||  !(*token_vector[0] && *ep == '\0') ) {
-        fprintf(stderr, "count '%s' is not a valid number: %s\n", token_vector[0], strerror(errno));
-        return 0;
-    }
+    if (arg_count == 2) {
+        errno = 0;
+        count = strtoul(arg_vector[1], &ep, 0);
+        if (errno
+        ||  !(*arg_vector[0] && *ep == '\0') ) {
+            fprintf(stderr, "count '%s' is not a valid number: %s\n", arg_vector[1], strerror(errno));
+            return 0;
+        }
 
-    if (count <= 0) {
-        fprintf(stderr, "count must be positive\n");
-        return 0;
+        if (count <= 0) {
+            fprintf(stderr, "count must be positive\n");
+            return 0;
+        }
     }
 
     while (count--) {
@@ -260,28 +320,21 @@ COMMAND_IMPL(step) {
     return 0;
 }
 COMMAND_HELP(step) {
-    fprintf(f, "step [count]\n");
+    fprintf(f, "\tstep [count]\n");
     if (summary) return;
 
-    fprintf(f, "\tExecutes the next instruction, or the next count instructions.\n");
+    fprintf(f, "Executes the next instruction, or the next count instructions.\n");
 }
 
 
-/* catch sigint while running, stop running */
-static volatile unsigned int running_ = 0;
-static
-void sigint_handler_(int sig) {
-    (void)sig;
-    running_ = 0;
-}
 COMMAND_IMPL(run) {
     sig_t osig;
-    (void)token_count, (void)token_vector;
+    (void)arg_count, (void)arg_vector;
 
     running_ = 1;
 
     /* install our new interrupt signal handler */
-    if ( (osig = signal(SIGINT, sigint_handler_)) ) {
+    if ( (osig = signal(SIGINT, sigint_handler_)) == SIG_ERR ) {
         fprintf(stderr, "%s():%s\n", "signal", strerror(errno));
         return -1;
     }
@@ -294,7 +347,7 @@ COMMAND_IMPL(run) {
 
     /* restore the old interrupt signal handler */
     if (signal(SIGINT, osig) == SIG_ERR) {
-        fprintf(stderr, "%s():%s\n", "sigaction", strerror(errno));
+        fprintf(stderr, "%s():%s\n", "signal", strerror(errno));
         return -1;
     }
 
@@ -303,19 +356,20 @@ COMMAND_IMPL(run) {
     return 0;
 }
 COMMAND_HELP(run) {
-    fprintf(f, "run\n");
+    fprintf(f, "\trun\n");
     if (summary) return;
 
-    fprintf(f, "\tBegins executing continuously.\n");
+    fprintf(f, "Begins executing continuously.\n");
 }
 
 /* gather all these together into a searchable table */
+
 /* help command gets some assistance in declarations */
 COMMAND_IMPL(help);
 COMMAND_HELP(help);
 
 static struct command_ command_table_[] = {
-    COMMAND_ENTRY(help, 0, 1),
+    COMMAND_ENTRY(help, 0, -1),
     COMMAND_ENTRY(quit, 0, -1),
     COMMAND_ENTRY(load, 1, 2),
     COMMAND_ENTRY(dump, 0, 2),
@@ -329,17 +383,13 @@ COMMAND_IMPL(help) {
     struct command_ *c;
     (void)vm;
 
-    if (token_count) {
-        while (token_count) {
-            for (c = command_table_; c->func; c++) {
-                if (strcasecmp(*token_vector, c->name) == 0) {
-                    if (c->help)
-                        c->help(stdout, 0);
-                    break;
-                }
+    if (arg_count == 2) {
+        for (c = command_table_; c->func; c++) {
+            if (strcasecmp(arg_vector[1], c->name) == 0) {
+                if (c->help)
+                    c->help(stdout, 0);
+                break;
             }
-            token_count--;
-            token_vector++;
         }
         return 0;
     }
@@ -351,21 +401,21 @@ COMMAND_IMPL(help) {
     return 0;
 }
 COMMAND_HELP(help) {
-    if (summary) {
-        fprintf(f, "help [command]\n");
-        return;
-    }
+        fprintf(f, "\thelp [command]\n");
+        if (summary) return;
 
-    fprintf(f, "Usage: help [command]\n"
-               "\tDisplays a list of available commands, or help on a specific command.\n");
+    fprintf(f, "Displays a list of available commands, or help on a specific command.\n");
 }
 
+
 int main(int argc, char **argv) {
-    int c;
-    char *line, *line_prev;
-    struct dcpu16 *vm;
-    char prompt[32];
     const char prompt_fmt[] = "PC:%04x> ";
+    char prompt[32];
+    struct dcpu16 *vm;
+    char *line, *line_prev;
+    char **tok_v, **tok_v_prev;
+    int tok_c, tok_c_prev;
+    int c;
 
     while ( (c = getopt(argc, argv, "hv")) != EOF) {
         switch (c) {
@@ -397,11 +447,16 @@ int main(int argc, char **argv) {
     }
 
     if (argc) {
-        file_load_(vm, *argv, 0);
+        if (file_load_(vm, *argv, 0)) {
+            fprintf(stderr, "couldn't load '%s'\n", *argv);
+            exit(EX_NOINPUT);
+        }
     }
 
     /* show state, read commands */
-    for (line_prev = NULL,
+    for (line = line_prev = NULL,
+         tok_v = tok_v_prev = NULL,
+         tok_c = tok_c_prev= 0,
          snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc),
          dcpu16_state_print(vm);
 
@@ -411,55 +466,60 @@ int main(int argc, char **argv) {
          snprintf(prompt, sizeof prompt, prompt_fmt, vm->pc),
          dcpu16_state_print(vm)) {
         const char whitespace[] = " \t";
-        char *rest, *line_start, *command;
+        char *line_start;
         struct command_ *c;
-        int token_count;
-        char *token_vector[] = { NULL, NULL };
         int r = 0;
 
         /* skip whitespaces */
         line_start = line + strspn(line, whitespace);
 
         if (*line_start) {
-            /* a new command, it will be the prior command now */
-            free(line_prev);
+            /* a new command, remember previous for possible repetition */
+
+            /* turn new line into new arg array */
+            if (buf_tok_vect_(&tok_v, &tok_c, line_start)) {
+                fprintf(stderr, "failed to process command\n");
+                continue;
+            }
+
+            /* and keep track if it all for the next time around */
+            if (line_prev) free(line_prev);
             line_prev = line;
+
+            if (tok_v_prev) free(tok_v_prev);
+            tok_v_prev = tok_v;
+            tok_c_prev = tok_c;
         } else {
-            /* empty command, read another line if there's no prior command to repeat */
-            if (line_prev == NULL || *line_prev == '\0') {
+            /* blank new command, but no prior command to repeat? ask again */
+            if (tok_v_prev == NULL || tok_v_prev[0] == NULL || *(tok_v_prev[0]) == '\0') {
+                free(line);
                 continue;
             }
 
-            /* otherwise discard new line and repeat prior */
+            /* otherwise discard new line and promote prior */
             free(line);
-            line_start = line_prev + strspn(line, whitespace);
-            VERBOSE_PRINTF("repeating previous command '%s'\n", line_start);
+            tok_v = tok_v_prev;
+            tok_c = tok_c_prev;
+            line = line_prev;
         }
 
-        /* first word */
-        command = strtok_r(line_start, whitespace, &rest);
-
         /* look up command */
-        /* FIXME: tokenize 'rest' into proper argv */
-        token_count = 0;
-        if (rest)
-            token_count++, token_vector[0] = rest;
         for (c = command_table_; c->name; c++) {
-            if (strcasecmp(command, c->name) == 0) {
-                if (c->args_min > token_count) {
+            if (strcasecmp(tok_v[0], c->name) == 0) {
+                if (c->args_min > tok_c - 1) {
                     fprintf(stderr, "%s: not enough arguments\n", c->name);
                     c->help(stderr, 1);
                     break;
                 }
 
                 if (c->args_max > 0
-                &&  token_count > c->args_max) {
+                &&  tok_c - 1 > c->args_max) {
                     fprintf(stderr, "%s: too many arguments\n", c->name);
                     c->help(stderr, 1);
                     break;
                 }
 
-                r = c->func(vm, token_count, token_vector);
+                r = c->func(vm, tok_c, tok_v);
                 break;
             }
         }
@@ -467,7 +527,7 @@ int main(int argc, char **argv) {
             break;
 
         if (!c->func)
-            fprintf(stderr, "didn't recognize '%s'\n", command);
+            fprintf(stderr, "didn't recognize '%s'\n", tok_v[0]);
     }
 
     printf("\nfinished\n");