/* -*-c-*- -------------- mixvm_command.c : * Implementation of the functions declared in mixvm_command.h * ------------------------------------------------------------------ * Copyright (C) 2000 jose antonio ortega ruiz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include #include #include #include #include "mixvm_command.h" /* The names of functions that actually do the manipulation. */ #define DEC_FUN(name) \ static int cmd_##name (char *arg) DEC_FUN (help_); DEC_FUN (shell_); DEC_FUN (load_); DEC_FUN (run_); DEC_FUN (next_); DEC_FUN (pc_); DEC_FUN (psym_); DEC_FUN (preg_); DEC_FUN (pflags_); DEC_FUN (pall_); DEC_FUN (pmem_); DEC_FUN (sreg_); DEC_FUN (scmp_); DEC_FUN (sover_); DEC_FUN (smem_); DEC_FUN (ssym_); DEC_FUN (sbp_); DEC_FUN (sbpa_); DEC_FUN (cbp_); DEC_FUN (cbpa_); DEC_FUN (cabp_); DEC_FUN (compile_); DEC_FUN (edit_); DEC_FUN (weval_); DEC_FUN (w2d_); DEC_FUN (tron_); DEC_FUN (troff_); DEC_FUN (quit_); /* A structure which contains information on the commands this program can understand. */ typedef struct { const char *name; /* User printable name of the function. */ Function *func; /* Function to call to do the job. */ const char *doc; /* Documentation for this function. */ const char *usage; /* Usage */ } COMMAND; COMMAND commands[] = { { "help", cmd_help_, N_("Display this text"), "help [COMMAND]" }, { "?", cmd_help_, N_("Synonym for `help'"), "? [COMMAND]" }, { "!", cmd_shell_, N_("Execute shell command"), "! COMMAND" }, { "load", cmd_load_, N_("Load a MIX code file"), "load FILENAME" }, { "run", cmd_run_, N_("Run loaded or given MIX code file"), "run [FILENAME]" }, { "next", cmd_next_, N_("Execute next instruction(s)"), "next [NO_OF_INS]"}, { "pc", cmd_pc_, N_("Print program counter value"), "pc" }, { "psym", cmd_psym_, N_("Print symbol value"), "psym [SYMBOLNAME]" }, { "preg", cmd_preg_, N_("Print register value"), "preg [A | X | J | I[1-6]]" }, { "pflags", cmd_pflags_, N_("Print comparison and overflow flags"), "pflags" }, { "pall", cmd_pall_, N_("Print all registers and flags"), "pall" }, { "pmem", cmd_pmem_, N_("Print memory contents in address range"), "pmem FROM[-TO]" }, { "sreg", cmd_sreg_, N_("Set register value"), "preg A | X | J | I[1-6] VALUE" }, { "scmp", cmd_scmp_, N_("Set comparison flag value"), "scmp L | E | G" }, { "sover", cmd_sover_, N_("Set overflow flag value"), "sover T | F" }, { "smem", cmd_smem_, N_("Set memory contents in given address"), "smem ADDRESS VALUE" }, { "ssym", cmd_ssym_, N_("Set a symbol\'s value"), "ssym SYMBOL WEXPR" }, { "sbp", cmd_sbp_, N_("Set break point at given line"), "sbp LINENO" }, { "cbp", cmd_cbp_, N_("Clear break point at given line"), "cbp LINENO" }, { "sbpa", cmd_sbpa_, N_("Set break point at given address"), "sbpa ADDRESS" }, { "cbpa", cmd_cbpa_, N_("Clear break point at given address"), "cbpa ADDRESS" }, { "cabp", cmd_cabp_, N_("Clear all breakpoints"), "cabp" }, { "compile", cmd_compile_, N_("Compile a source file"), "compile FILENAME"}, { "edit", cmd_edit_, N_("Edit a source file"), "edit FILENAME"}, { "weval", cmd_weval_, N_("Evaluate a given W-expres sion"), "weval WEXPR"}, { "w2d", cmd_w2d_, N_("Convert a MIX word to its decimal value"), "w2d WORD" }, { "tron", cmd_tron_, N_("Turn on instruction tracing."), "tron" }, { "troff", cmd_troff_, N_("Turn off instruction tracing."), "troff" }, { "quit", cmd_quit_, N_("Quit the program"), "quit" }, { (char *)NULL, (Function *)NULL, (char *)NULL } }; /* readline functions */ static char * mixvm_cmd_generator_ (char *text, int state); /* Attempt to complete on the contents of TEXT. START and END bound the region of rl_line_buffer that contains the word to complete. TEXT is the word to complete. We can use the entire contents of rl_line_buffer in case we want to do some simple parsing. Return the array of matches, or NULL if there aren't any. */ static char ** mixvm_cmd_completion_ (char *text, int start, int end) { char **matches; matches = (char **)NULL; /* If this word is at the start of the line, then it is a command to complete. Otherwise it is the name of a file in the current directory. */ if (start == 0) matches = completion_matches (text, mixvm_cmd_generator_); return (matches); } /* Generator function for command completion. STATE lets us know whether to start from scratch; without any state (i.e. STATE == 0), then we start at the top of the list. */ static char * mixvm_cmd_generator_ (char *text, int state) { static int list_index, len; const char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next name which partially matches from the command list. */ while ( (name = commands[list_index].name) != NULL) { list_index++; if (strncmp (name, text, len) == 0) return (g_strdup (name)); } /* If no names matched, then return NULL. */ return ((char *)NULL); } /* command functions */ static COMMAND * find_command_ (const char *name) { int i; for (i = 0; commands[i].name; i++) if (strcmp (name, commands[i].name) == 0) return (&commands[i]); return ((COMMAND *)NULL); } /* the virtual machine and dump context */ static mix_vm_t *vm_ = NULL; static mix_dump_context_t *dc_ = NULL; /* the w-expression evaluator */ static mix_eval_t *eval_ = NULL; /* trace status */ static gboolean is_tracing_ = FALSE; /* uptime */ static mix_time_t uptime_ = 0; static mix_time_t prog_time_ = 0; static int cmd_help_ (char *arg) { static const int NO_OF_COLS = 6; int i; int printed = 0; for (i = 0; commands[i].name; i++) { if (!*arg || (strcmp (arg, commands[i].name) == 0)) { printf (_("%s\t\t%s. Usage: %s\n"), commands[i].name, _(commands[i].doc), commands[i].usage); printed++; } } if (!printed) { printf (_("No commands match `%s'. Possibilities are:\n"), arg); for (i = 0; commands[i].name; i++) { if (printed == NO_OF_COLS) { printed = 0; printf ("\n"); } printf ("%s\t", commands[i].name); printed++; } if (printed) printf ("\n"); } return TRUE; } static int cmd_load_ (char *arg) { errno = 0; if (arg == NULL || *arg == '\0') { fputs (_("Missing file name\n"), stderr); return TRUE; } mix_eval_remove_symbols_from_table (eval_, mix_vm_get_symbol_table (vm_)); if (!mix_vm_load_file (vm_, arg) ) { fprintf (stderr, _("Cannot load %s: "), arg); if ( errno == 0 ) fputs (_("Wrong file format\n"), stderr); else perror (NULL); return TRUE + 1; } mix_eval_set_symbols_from_table (eval_, mix_vm_get_symbol_table (vm_)); fprintf (stderr, _("Program loaded. Start address: %d\n"), mix_vm_get_prog_count (vm_)); prog_time_ = 0; return TRUE; } /* trace current instruction */ static void trace_ (void) { enum {BUFFER_LEN = 128}; static gchar STRINS[BUFFER_LEN]; const mix_src_file_t *file = mix_vm_get_src_file (vm_); const gchar *line = "\n"; mix_address_t loc = mix_vm_get_prog_count (vm_); mix_word_t ins = mix_vm_get_addr_contents (vm_, loc); mix_ins_t fins; mix_word_to_ins_uncheck (ins, fins); mix_ins_to_string_in_buffer (&fins, STRINS, BUFFER_LEN); if (file != NULL) { gulong b = mix_vm_get_break_lineno (vm_); if (b > 0) line = mix_src_file_get_line (file, b); } printf ("%d: [%s]\t%s", (gint)loc, STRINS, line); } static int run_and_trace_ (void) { int k = MIX_VM_OK; if (!is_tracing_) return mix_vm_run (vm_); else while (k == MIX_VM_OK) { trace_ (); k = mix_vm_exec_next (vm_); } return k; } static void print_time_ (void) { mix_time_t lapse = mix_vm_get_uptime(vm_) - uptime_; uptime_ += lapse; prog_time_ += lapse; printf(_("Elapsed time: %ld /Total program time: %ld (Total uptime: %ld)\n"), lapse, prog_time_, uptime_); } static int cmd_run_ (char *arg) { if (arg != NULL && *arg != '\0' && cmd_load_ (arg) != TRUE) return TRUE; puts (_("Running ...")); if (mix_vm_is_halted (vm_)) { mix_vm_reset_program (vm_); prog_time_ = 0; } switch (run_and_trace_ ()) { case MIX_VM_HALT: puts (_("... done")); break; case MIX_VM_BREAK: { gulong line = mix_vm_get_break_lineno (vm_); if (line != 0) printf (_("... stopped: breakpoint at line %ld (address %d)\n"), line, mix_vm_get_prog_count (vm_)); else printf (_("... stopped: breakpoint at address %d\n"), mix_vm_get_prog_count (vm_)); } break; case MIX_VM_ERROR: puts (_("... error executing loaded file")); break; default: g_assert_not_reached (); break; } print_time_ (); return TRUE; } static int cmd_next_ (char *arg) { int ins_no = 1; int k; if ( strlen (arg) != 0 ) { int k = 0; while (isdigit (arg[k])) k++; if (arg[k] != '\0') { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("next"); } ins_no = atoi (arg); } if (mix_vm_is_halted (vm_)) { mix_vm_reset_program (vm_); prog_time_ = 0; } while ( ins_no-- > 0 ) { if (is_tracing_) trace_ (); k = mix_vm_exec_next (vm_); if (k == MIX_VM_HALT) { fprintf (stderr, _("End of program reached at address %d\n"), mix_vm_get_prog_count (vm_)); break; } else if (k == MIX_VM_ERROR) { fprintf (stderr, _("Error at address %d\n"), mix_vm_get_prog_count (vm_)); break; } } print_time_ (); return TRUE; } static int cmd_quit_ (char *arg) { puts ("Quitting ..."); if ( vm_ ) mix_vm_delete (vm_); if ( dc_ ) mix_dump_context_delete (dc_); return FALSE; } static int cmd_pc_ (char *arg) { printf ("Current address: %d\n", mix_vm_get_prog_count (vm_)); return TRUE; } static int cmd_psym_ (char *arg) { const mix_symbol_table_t *table = mix_eval_symbol_table (eval_); if ( table == NULL ) fputs (_("Symbol table not available\n"), stderr); else if (arg != NULL && *arg != '\0') { if ( mix_symbol_table_is_defined (table, arg) ) { mix_word_print (mix_symbol_table_value (table, arg), NULL); putc ('\n', stdout); } else printf (_("%s: symbol not defined\n"), arg); } else mix_symbol_table_print (table, MIX_SYM_ROWS, stdout, TRUE); return TRUE; } static int cmd_preg_ (char *arg) { mix_dump_context_set_opt (dc_, MIX_DUMP_NONE); if ( strlen (arg) == 0 ) mix_dump_context_add_opt (dc_, MIX_DUMP_rALL); else switch (*arg) { case 'A': mix_dump_context_add_opt (dc_, MIX_DUMP_rA); break; case 'X': mix_dump_context_add_opt (dc_, MIX_DUMP_rX); break; case 'J': mix_dump_context_add_opt (dc_, MIX_DUMP_rJ); break; case 'I': { if ( strlen (arg) == 1 ) mix_dump_context_add_opt (dc_, MIX_DUMP_rIa); else { static gint32 opt[] = { MIX_DUMP_rI1, MIX_DUMP_rI2, MIX_DUMP_rI3, MIX_DUMP_rI4, MIX_DUMP_rI5, MIX_DUMP_rI6 }; int i = arg[1] - '1'; if ( i < 0 || i > 5 ) { fprintf (stderr, _("Invalid I index: %d"), i); return TRUE; } mix_dump_context_add_opt (dc_, opt[i]); } } break; default: fprintf (stderr, _("Invalid argument: %s\n"), arg); return TRUE; } mix_vm_dump (vm_, dc_); return TRUE; } static int cmd_pflags_ (char *arg) { mix_dump_context_set_opt (dc_, MIX_DUMP_CMP | MIX_DUMP_OVER); mix_vm_dump (vm_, dc_); return TRUE; } static int cmd_pall_ (char *arg) { mix_dump_context_set_opt (dc_, MIX_DUMP_ALL_NOMEM); mix_vm_dump (vm_, dc_); return TRUE; } static int cmd_pmem_ (char *arg) { glong begin = MIX_SHORT_ZERO, end = MIX_SHORT_ZERO; int i = 0; gboolean error = FALSE; if ( strlen (arg) == 0 ) { fputs (_("Missing memory address\n"), stderr); return TRUE; } while (isdigit (arg[i])) i++; while (isspace (arg[i])) i++; if (arg[i] == '\0') begin = end = atol (arg); else if (arg[i] == '-') { arg[i++] = '\0'; begin = atol (arg); arg = arg + i; i = 0; while (isdigit (arg[i])) i++; while (isspace (arg[i])) i++; if (arg[i] != '\0') error = TRUE; else end = atol (arg); } else error = TRUE; if (error) { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_("pmem"); } if ( end < begin || end > MIX_VM_CELL_NO - 1 ) { fprintf (stderr, _("Invalid range: %ld-%ld\n"), begin, end); return TRUE; } mix_dump_context_set_opt (dc_, MIX_DUMP_CELLS); mix_dump_context_range (dc_, mix_short_new (begin), mix_short_new (end + 1)); mix_vm_dump (vm_, dc_); return TRUE; } static int cmd_sreg_ (char *arg) { int i = 0; char reg = arg[0]; gboolean ok = TRUE; long value; i = (reg == 'I') ? 2 : 1; ok = strlen (arg) > 2 && isspace (arg[i]); if (ok) { while (isspace (arg[i])) i++; ok = isdigit (arg[i]) || arg[i] == '+' || arg[i] == '-'; if (ok) { value = atol (arg + i); if (arg[i] == '+' || arg[i] == '-') i++; while (isdigit (arg[i])) i++; ok = (arg[i] == '\0'); if (ok) switch (reg) { case 'A': mix_vm_set_rA (vm_, mix_word_new (value)); break; case 'X': mix_vm_set_rX (vm_, mix_word_new (value)); break; case 'J': if ( value >= 0 ) mix_vm_set_rJ (vm_, mix_short_new (value)); else ok = FALSE; break; case 'I': { guint k = arg[1] - '0'; if ( k < 7 ) mix_vm_set_rI (vm_, k, mix_short_new (value)); else ok = FALSE; } break; default: ok = FALSE; } } } if (!ok) { fprintf (stderr, _("Invalid argument: %s\n"), arg); cmd_help_ ("sreg"); } return TRUE; } static int cmd_scmp_ (char *arg) { gboolean ok = (strlen (arg) == 1); if (ok) switch (arg[0]) { case 'L': mix_vm_set_cmpflag (vm_, mix_LESS); break; case 'E': mix_vm_set_cmpflag (vm_, mix_EQ); break; case 'G': mix_vm_set_cmpflag (vm_, mix_GREAT); break; default: ok = FALSE; } if (!ok) { fprintf (stderr, _("Invalid argument: %s\n"), arg); cmd_help_ ("scmp"); } return TRUE; } static int cmd_sover_ (char *arg) { gboolean ok = (strlen (arg) == 1); if (ok) switch (arg[0]) { case 'T': mix_vm_set_overflow (vm_, TRUE); break; case 'F': mix_vm_set_overflow (vm_, FALSE); break; default: ok = FALSE; } if (!ok) { fprintf (stderr, _("Invalid argument: %s\n"), arg); cmd_help_ ("sover"); } return TRUE; } static int cmd_smem_ (char *arg) { gboolean ok = (strlen (arg) > 2 && isdigit (arg[0])); gulong addr; glong value; int k = 0; if (ok) { while (isdigit (arg[k])) k++; ok = isspace (arg[k]); if (ok) { arg[k++] = '\0'; addr = atol (arg); ok = addr < MIX_VM_CELL_NO; } if (ok) { while (isspace (arg[k])) k++; value = atol (arg + k); if ( arg[k] == '+' || arg[k] == '-' ) k++; while (isdigit (arg[k])) k++; ok = arg[k] == '\0'; } } if (ok) mix_vm_set_addr_contents (vm_, mix_short_new (addr), mix_word_new (value)); else { fprintf (stderr, "Invalid argument: %s\n", arg); cmd_help_ ("smem"); } return TRUE; } static int cmd_ssym_ (char *arg) { if (arg == NULL || strlen(arg) == 0) { fprintf (stderr, _("Missing arguments\n")); return cmd_help_ ("ssym"); } else { gchar *a = g_strdup (arg); gchar *s = strtok (a, " \t"); gchar *w = strtok (NULL, " \t"); if (w != NULL && strtok (NULL, " \t") == NULL) { cmd_weval_ (w); if (mix_eval_last_error (eval_) == MIX_EVAL_OK) mix_eval_set_symbol (eval_, s, mix_eval_value (eval_)); } else { fprintf (stderr, _("Wrong argument number\n")); cmd_help_ ("ssym"); } g_free (a); return TRUE; } } static int cmd_sbp_ (char *arg) { glong lineno; glong k = 0; while (isdigit (arg[k])) k++; if (arg[k] != '\0') { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("sbp"); } lineno = atol (arg); switch (k = mix_vm_set_breakpoint (vm_, lineno)) { case MIX_VM_BP_INV_LINE: fprintf (stderr, _("Line number %ld too high\n"), lineno); break; case MIX_VM_BP_ERROR: fputs (_("Could not set breakpoint. Internal error\n"), stderr); break; case MIX_VM_BP_NDEBUG: fputs (_("Could not set breakpoint. No debug info available\n"), stderr); break; default: fprintf (stderr, _("Breakpoint set at line %ld\n"), k); break; } return TRUE; } static int cmd_sbpa_ (char *arg) { glong address; glong k = 0; while (isdigit (arg[k])) k++; if (arg[k] != '\0') { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("sbpa"); } address = atol (arg); switch (mix_vm_set_breakpoint_address (vm_, address)) { case MIX_VM_BP_INV_ADDRESS: fprintf (stderr, _("Invalid address %ld\n"), address); break; case MIX_VM_BP_ERROR: fputs (_("Could not set breakpoint. Internal error\n"), stderr); break; default: fprintf (stderr, _("Breakpoint set at address %ld\n"), address); break; } return TRUE; } static int cmd_cbp_ (char *arg) { glong lineno; int k = 0; while (isdigit (arg[k])) k++; if (arg[k] != '\0') { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("cbp"); } lineno = atol (arg); switch (mix_vm_clear_breakpoint (vm_, lineno)) { case MIX_VM_BP_INV_LINE: fprintf (stderr, _("No breakpoint set at line %ld\n"), lineno); break; case MIX_VM_BP_ERROR: fputs (_("Could not set breakpoint. Internal error\n"), stderr); break; case MIX_VM_BP_NDEBUG: fputs (_("No debug info available\n"), stderr); break; case MIX_VM_BP_OK: fprintf (stderr, _("Breakpoint cleared at line %ld\n"), lineno); break; default: g_assert_not_reached (); break; } return TRUE; } static int cmd_cbpa_ (char *arg) { glong address; glong k = 0; while (isdigit (arg[k])) k++; if (arg[k] != '\0') { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("cbpa"); } address = atol (arg); switch (mix_vm_clear_breakpoint_address (vm_, address)) { case MIX_VM_BP_INV_ADDRESS: fprintf (stderr, _("Invalid address %ld\n"), address); break; case MIX_VM_BP_ERROR: fputs (_("Could not clear breakpoint. Internal error\n"), stderr); break; default: fprintf (stderr, _("Breakpoint cleared at address %ld\n"), address); break; } return TRUE; } static int cmd_cabp_ (char *arg) { if (strlen (arg) != 0) { fprintf (stderr, _("Invalid argument: %s\n"), arg); return cmd_help_ ("cabp"); } mix_vm_clear_all_breakpoints (vm_); return TRUE; } static int cmd_shell_ (char *arg) { system (arg); return TRUE; } static int cmd_compile_ (char *arg) { gchar *line; if ( strlen (arg) == 0 ) return cmd_help_ ("compile"); line = g_strconcat ("mixasm -g ", arg, NULL); if ( system (line) == EXIT_SUCCESS ) fputs (_("Successful compilation\n"), stderr); g_free (line); return TRUE; } static int cmd_edit_ (char *arg) { static const gchar * envars[] = { "MDK_EDITOR", "X_EDITOR", "EDITOR", "VISUAL" }; static const guint s = sizeof (envars) / sizeof (envars[0]); static const gchar *editor = NULL; if ( strlen (arg) == 0 ) return cmd_help_ ("edit"); if (!editor) { int k; for (k = 0; k < s; k++) if ( (editor = getenv (envars[k])) != NULL ) break; } if (!editor) { int k; fputs (_("Cannot find editor ("), stderr); for (k = 0; k < s; k++) fprintf (stderr, "%s ", envars[k]); fputs (_("undefined)\n"), stderr); } else { gchar *line = g_strconcat (editor, " ", arg, NULL); system (line); g_free (line); } return TRUE; } static int cmd_weval_ (char *arg) { if ( strlen (arg) == 0 ) return cmd_help_ ("weval"); if (mix_eval_expression_with_loc (eval_, arg, mix_vm_get_prog_count (vm_)) == MIX_EVAL_OK) { mix_word_print (mix_eval_value (eval_), NULL); putc ('\n', stdout); } else { gint pos = mix_eval_last_error_pos (eval_); gint k, len = strlen (arg); g_assert(pos > -1 && pos <= len); for (k = 0; kfunc)) (word)); }