mirror of
https://github.com/pevans/erc-c.git
synced 2025-02-17 07:32:05 +00:00
Add resume command, finder function, arg parser, execute
This commit is contained in:
parent
a614c1e5df
commit
ec253905ad
@ -1,24 +1,8 @@
|
|||||||
#ifndef _VM_DEBUG_H_
|
#ifndef _VM_DEBUG_H_
|
||||||
#define _VM_DEBUG_H_
|
#define _VM_DEBUG_H_
|
||||||
|
|
||||||
typedef struct {
|
struct vm_debug_args;
|
||||||
/*
|
typedef struct vm_debug_args 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[256];
|
|
||||||
} vm_debug_args;
|
|
||||||
|
|
||||||
typedef void (*vm_debug_func)(vm_debug_args *);
|
typedef void (*vm_debug_func)(vm_debug_args *);
|
||||||
|
|
||||||
@ -52,9 +36,38 @@ typedef struct {
|
|||||||
char *desc;
|
char *desc;
|
||||||
} vm_debug_cmd;
|
} 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) \
|
#define DEBUG_CMD(x) \
|
||||||
void vm_debug_cmd_##x (vm_debug_args *args)
|
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(help);
|
||||||
|
extern DEBUG_CMD(resume);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
136
src/vm_debug.c
136
src/vm_debug.c
@ -2,18 +2,149 @@
|
|||||||
* vm_debug.c
|
* vm_debug.c
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
#include "vm_debug.h"
|
#include "vm_debug.h"
|
||||||
#include "vm_di.h"
|
#include "vm_di.h"
|
||||||
|
#include "vm_reflect.h"
|
||||||
|
|
||||||
vm_debug_cmd cmdtable[] = {
|
vm_debug_cmd cmdtable[] = {
|
||||||
{ "help", "h", 0, vm_debug_cmd_help, "",
|
{ "help", "h", 0, vm_debug_cmd_help, "",
|
||||||
"Print out this list of commands", },
|
"Print out this list of commands", },
|
||||||
|
{ "resume", "r", 0, vm_debug_cmd_resume, "",
|
||||||
|
"Resume execution", },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CMDTABLE_SIZE (sizeof(cmdtable) / sizeof(vm_debug_cmd))
|
#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)
|
DEBUG_CMD(help)
|
||||||
{
|
{
|
||||||
FILE *stream = (FILE *)vm_di_get(VM_OUTPUT);
|
FILE *stream = (FILE *)vm_di_get(VM_OUTPUT);
|
||||||
@ -25,3 +156,8 @@ DEBUG_CMD(help)
|
|||||||
cmd->argdesc, cmd->desc);
|
cmd->argdesc, cmd->desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG_CMD(resume)
|
||||||
|
{
|
||||||
|
vm_reflect_pause(NULL);
|
||||||
|
}
|
||||||
|
@ -2,13 +2,20 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "apple2.h"
|
||||||
|
#include "apple2.reflect.h"
|
||||||
#include "vm_debug.h"
|
#include "vm_debug.h"
|
||||||
#include "vm_di.h"
|
#include "vm_di.h"
|
||||||
|
#include "vm_reflect.h"
|
||||||
|
|
||||||
static char buf[BUFSIZ];
|
static char buf[BUFSIZ];
|
||||||
static FILE *stream = NULL;
|
static FILE *stream = NULL;
|
||||||
static vm_debug_args args;
|
static vm_debug_args args;
|
||||||
|
|
||||||
|
static apple2 *mach;
|
||||||
|
static vm_reflect *ref;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
setup()
|
setup()
|
||||||
{
|
{
|
||||||
@ -24,6 +31,15 @@ setup()
|
|||||||
|
|
||||||
// Writing to stream will now write to buf
|
// Writing to stream will now write to buf
|
||||||
setvbuf(stream, buf, _IOFBF, BUFSIZ);
|
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
|
static void
|
||||||
@ -31,6 +47,8 @@ teardown()
|
|||||||
{
|
{
|
||||||
memset(buf, 0, BUFSIZ);
|
memset(buf, 0, BUFSIZ);
|
||||||
fclose(stream);
|
fclose(stream);
|
||||||
|
|
||||||
|
apple2_free(mach);
|
||||||
}
|
}
|
||||||
|
|
||||||
TestSuite(vm_debug, .init = setup, .fini = teardown);
|
TestSuite(vm_debug, .init = setup, .fini = teardown);
|
||||||
@ -43,5 +61,36 @@ Test(vm_debug, cmd_help)
|
|||||||
{
|
{
|
||||||
vm_debug_cmd_help(&args);
|
vm_debug_cmd_help(&args);
|
||||||
cr_assert_neq(strlen(buf), 0);
|
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);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user