9 #include <readline/readline.h>
14 * cli driver for dcpu16 core
16 * Justin Wind <justin.wind@gmail.com>
17 * 2012 04 10 - implementation started
21 static const char * const src_id_
= "$Id$";
23 /* global invocation options */
30 #define VERBOSE_PRINTF(...) do { if (opt_.verbose) printf(__VA_ARGS__); } while (0)
32 static void usage_(char *prog
, unsigned int full
) {
33 FILE *f
= full
? stdout
: stderr
;
34 char *x
= strrchr(prog
, '/');
40 fprintf(f
, "%s -- \n\n",
43 fprintf(f
, "Usage: %s [file]\n",
47 fprintf(f
, "\nOptions:\n"
48 "\t [file] -- ram image to load initially\n"
49 "\t -h -- this screen\n"
50 "\t -v -- verbose execution tracing\n");
52 fprintf(f
, "\n%78s\n", src_id_
);
56 /* simplified strtoul with range checking */
58 int str_to_word_(char *s
) {
65 l
= strtoul(s
, &ep
, 0);
68 || !(*s
&& *ep
== '\0') ) {
69 /* out of range of conversion, or invalid character encountered */
73 if (l
>= DCPU16_RAM
) {
74 /* out of range for our needs */
82 /* clears the instance and loads an image into ram starting at addr */
84 int file_load_(struct dcpu16
*vm
, char *filename
, DCPU16_WORD addr
) {
88 assert(addr
< DCPU16_RAM
);
92 f
= fopen(filename
, "rb");
94 fprintf(stderr
, "%s('%s'):%s\n", "fopen", filename
, strerror(errno
));
98 r
= fread(vm
->ram
+ addr
, sizeof(DCPU16_WORD
), DCPU16_RAM
- addr
, f
);
99 VERBOSE_PRINTF("read %zu words", r
);
100 if (addr
) VERBOSE_PRINTF(" starting at 0x%04x", addr
);
101 VERBOSE_PRINTF("\n");
104 fprintf(stderr
, "%s('%s'):%s\n", "fread", filename
, strerror(errno
));
110 /* the commands the vm shell can execute */
116 int (*func
)(struct dcpu16
*, int c
, char **v
);
117 void (*help
)(FILE *f
, unsigned int);
120 #define COMMAND_IMPL(x) static int command_##x##_(struct dcpu16 *vm, int token_count, char **token_vector)
121 #define COMMAND_HELP(x) static void command_##x##_help_(FILE *f, unsigned int summary)
122 #define COMMAND_ENTRY(x, y, z) { #x, y, z, command_##x##_, command_##x##_help_ }
126 (void)vm
, (void)token_count
, (void)token_vector
;
127 VERBOSE_PRINTF("done\n");
131 fprintf(f
, "quit\n");
134 fprintf(f
, "\tExits the emulator.\n");
141 if (token_count
> 1) {
142 addr
= str_to_word_(token_vector
[1]);
144 fprintf(stderr
, "address '%s' is not a valid word: %s\n", token_vector
[1], strerror(errno
));
149 if (file_load_(vm
, token_vector
[0], addr
)) {
150 fprintf(stderr
, "failed to load '%s'\n", token_vector
[0]);
153 printf("loaded '%s'", token_vector
[0]);
154 if (addr
) printf(" starting at 0x%04x", addr
);
160 fprintf(f
, "load file [addr]\n");
163 fprintf(f
, "Usage: load file [addr]\n"
164 "\tAttempts to load binary image from 'file' at addr.\n");
172 for (i
= 0; i
< token_count
; i
++) {
173 addr
[i
] = str_to_word_(token_vector
[i
]);
175 fprintf(stderr
, "address '%s' is not a valid word: %s\n", token_vector
[i
], strerror(errno
));
179 if (token_count
< 1) addr
[0] = vm
->pc
;
180 if (token_count
< 2) addr
[1] = addr
[0];
182 if (addr
[1] < addr
[0]) {
183 fprintf(stderr
, "\t'addr_start' must be before addr_end\n");
187 dcpu16_dump_ram(vm
, addr
[0], addr
[1]);
192 fprintf(f
, "dump [addr_start [addr_end]]\n");
195 fprintf(f
, "\tDisplays contents of ram from addr_start to addr_end.\n");
199 COMMAND_IMPL(disassemble
) {
203 for (i
= 0; i
< token_count
; i
++) {
204 addr
[i
] = str_to_word_(token_vector
[i
]);
206 fprintf(stderr
, "address '%s' is not a valid word: %s\n", token_vector
[i
], strerror(errno
));
210 if (token_count
< 1) addr
[0] = vm
->pc
;
211 if (token_count
< 2) addr
[1] = addr
[0];
213 if (addr
[1] < addr
[0]) {
214 fprintf(stderr
, "\t'addr_start' must be before addr_end\n");
218 for (i
= addr
[0]; i
<= addr
[1]; i
++)
219 dcpu16_disassemble_print(vm
, i
);
223 COMMAND_HELP(disassemble
) {
224 fprintf(f
, "disassemble [addr_start [addr_end]]\n");
227 fprintf(f
, "\tDisplays contents of ram parsed into instructions.\n");
238 count
= strtoul(token_vector
[0], &ep
, 0);
240 || !(*token_vector
[0] && *ep
== '\0') ) {
241 fprintf(stderr
, "count '%s' is not a valid number: %s\n", token_vector
[0], strerror(errno
));
246 fprintf(stderr
, "count must be positive\n");
251 VERBOSE_PRINTF("executing next cycle, instruction: ");
252 dcpu16_disassemble_print(vm
, vm
->pc
), printf("\n");
257 dcpu16_state_print(vm
);
263 fprintf(f
, "step [count]\n");
266 fprintf(f
, "\tExecutes the next instruction, or the next count instructions.\n");
270 /* catch sigint while running, stop running */
271 static volatile unsigned int running_
= 0;
273 void sigint_handler_(int sig
) {
279 (void)token_count
, (void)token_vector
;
283 /* install our new interrupt signal handler */
284 if ( (osig
= signal(SIGINT
, sigint_handler_
)) ) {
285 fprintf(stderr
, "%s():%s\n", "signal", strerror(errno
));
292 dcpu16_state_print(vm
);
295 /* restore the old interrupt signal handler */
296 if (signal(SIGINT
, osig
) == SIG_ERR
) {
297 fprintf(stderr
, "%s():%s\n", "sigaction", strerror(errno
));
301 VERBOSE_PRINTF("interrupted...\n");
309 fprintf(f
, "\tBegins executing continuously.\n");
312 /* gather all these together into a searchable table */
313 /* help command gets some assistance in declarations */
317 static struct command_ command_table_
[] = {
318 COMMAND_ENTRY(help
, 0, 1),
319 COMMAND_ENTRY(quit
, 0, -1),
320 COMMAND_ENTRY(load
, 1, 2),
321 COMMAND_ENTRY(dump
, 0, 2),
322 COMMAND_ENTRY(disassemble
, 0, 2),
323 COMMAND_ENTRY(step
, 0, 1),
324 COMMAND_ENTRY(run
, 0, 0),
325 { NULL
, 0, 0, NULL
, NULL
}
333 while (token_count
) {
334 for (c
= command_table_
; c
->func
; c
++) {
335 if (strcasecmp(*token_vector
, c
->name
) == 0) {
347 for (c
= command_table_
; c
->func
; c
++) {
355 fprintf(f
, "help [command]\n");
359 fprintf(f
, "Usage: help [command]\n"
360 "\tDisplays a list of available commands, or help on a specific command.\n");
363 int main(int argc
, char **argv
) {
365 char *line
, *line_prev
;
368 const char prompt_fmt
[] = "PC:%04x> ";
370 while ( (c
= getopt(argc
, argv
, "hv")) != EOF
) {
385 if (opt_
.verbose
< 1) {
386 dcpu16_warn_cb_set(NULL
);
387 dcpu16_trace_cb_set(NULL
);
388 } else if (opt_
.verbose
< 2) {
389 dcpu16_trace_cb_set(NULL
);
394 if ((vm
= dcpu16_new()) == NULL
) {
395 fprintf(stderr
, "could not allocate new dcpu instance\n");
396 exit(EX_UNAVAILABLE
);
400 file_load_(vm
, *argv
, 0);
403 /* show state, read commands */
404 for (line_prev
= NULL
,
405 snprintf(prompt
, sizeof prompt
, prompt_fmt
, vm
->pc
),
406 dcpu16_state_print(vm
);
408 (line
= readline(prompt
));
411 snprintf(prompt
, sizeof prompt
, prompt_fmt
, vm
->pc
),
412 dcpu16_state_print(vm
)) {
413 const char whitespace
[] = " \t";
414 char *rest
, *line_start
, *command
;
417 char *token_vector
[] = { NULL
, NULL
};
420 /* skip whitespaces */
421 line_start
= line
+ strspn(line
, whitespace
);
424 /* a new command, it will be the prior command now */
428 /* empty command, read another line if there's no prior command to repeat */
429 if (line_prev
== NULL
|| *line_prev
== '\0') {
433 /* otherwise discard new line and repeat prior */
435 line_start
= line_prev
+ strspn(line
, whitespace
);
436 VERBOSE_PRINTF("repeating previous command '%s'\n", line_start
);
440 command
= strtok_r(line_start
, whitespace
, &rest
);
442 /* look up command */
443 /* FIXME: tokenize 'rest' into proper argv */
446 token_count
++, token_vector
[0] = rest
;
447 for (c
= command_table_
; c
->name
; c
++) {
448 if (strcasecmp(command
, c
->name
) == 0) {
449 if (c
->args_min
> token_count
) {
450 fprintf(stderr
, "%s: not enough arguments\n", c
->name
);
456 && token_count
> c
->args_max
) {
457 fprintf(stderr
, "%s: too many arguments\n", c
->name
);
462 r
= c
->func(vm
, token_count
, token_vector
);
470 fprintf(stderr
, "didn't recognize '%s'\n", command
);
473 printf("\nfinished\n");