changeset 2176:035a54aa633f

Merge
author Michael Pavone <pavone@retrodev.com>
date Sat, 06 Aug 2022 22:10:54 -0700
parents 8c28c5466d70 (diff) 42a0f6db81c3 (current diff)
children 44596610b2a0
files
diffstat 2 files changed, 761 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/debug.c	Thu Aug 04 23:40:13 2022 -0700
+++ b/debug.c	Sat Aug 06 22:10:54 2022 -0700
@@ -64,6 +64,660 @@
 	return cur;
 }
 
+typedef enum {
+	TOKEN_NONE,
+	TOKEN_NUM,
+	TOKEN_NAME,
+	TOKEN_OPER,
+	TOKEN_SIZE,
+	TOKEN_LBRACKET,
+	TOKEN_RBRACKET,
+	TOKEN_LPAREN,
+	TOKEN_RPAREN
+} token_type;
+
+static const char *token_type_names[] = {
+	"TOKEN_NONE",
+	"TOKEN_NUM",
+	"TOKEN_NAME",
+	"TOKEN_OPER",
+	"TOKEN_SIZE",
+	"TOKEN_LBRACKET",
+	"TOKEN_RBRACKET",
+	"TOKEN_LPAREN",
+	"TOKEN_RPAREN"
+};
+
+typedef struct {
+	token_type type;
+	union {
+		char     *str;
+		char     op[3];
+		uint32_t num;
+	} v;
+} token;
+
+static token parse_token(char *start, char **end)
+{
+	while(*start && isblank(*start) && *start != '\n')
+	{
+		++start;
+	}
+	if (!*start || *start == '\n') {
+		return (token){
+			.type = TOKEN_NONE
+		};
+		*end = start;
+	}
+	if (*start == '$' || (*start == '0' && start[1] == 'x')) {
+		return (token) {
+			.type = TOKEN_NUM,
+			.v = {
+				.num = strtol(start + (*start == '$' ? 1 : 2), end, 16)
+			}
+		};
+	}
+	if (isdigit(*start)) {
+		return (token) {
+			.type = TOKEN_NUM,
+			.v = {
+				.num = strtol(start, end, 10)
+			}
+		};
+	}
+	switch (*start)
+	{
+	case '+':
+	case '-':
+	case '*':
+	case '/':
+	case '&':
+	case '|':
+	case '^':
+	case '~':
+	case '=':
+	case '!':
+		if (*start == '!' && start[1] == '=') {
+			*end = start + 2;
+			return (token) {
+				.type = TOKEN_OPER,
+				.v = {
+					.op = {*start, start[1], 0}
+				}
+			};
+		}
+		*end = start + 1;
+		return (token) {
+			.type = TOKEN_OPER,
+			.v = {
+				.op = {*start, 0}
+			}
+		};
+	case '.':
+		*end = start + 2;
+		return (token) {
+			.type = TOKEN_SIZE,
+			.v = {
+				.op = {start[1], 0}
+			}
+		};
+	case '[':
+		*end = start + 1;
+		return (token) {
+			.type = TOKEN_LBRACKET
+		};
+	case ']':
+		*end = start + 1;
+		return (token) {
+			.type = TOKEN_RBRACKET
+		};
+	case '(':
+		*end = start + 1;
+		return (token) {
+			.type = TOKEN_LPAREN
+		};
+	case ')':
+		*end = start + 1;
+		return (token) {
+			.type = TOKEN_RPAREN
+		};
+	}
+	*end = start + 1;
+	while (**end && !isblank(**end))
+	{
+		uint8_t done = 0;
+		switch (**end)
+		{
+		case '+':
+		case '-':
+		case '*':
+		case '/':
+		case '&':
+		case '|':
+		case '^':
+		case '~':
+		case '=':
+		case '!':
+		case '.':
+			done = 1;
+			break;
+		}
+		if (done) {
+			break;
+		}
+			
+		++*end;
+	}
+	char *name = malloc(*end - start + 1);
+	memcpy(name, start, *end - start);
+	name[*end-start] = 0;
+	return (token) {
+		.type = TOKEN_NAME,
+		.v = {
+			.str = name
+		}
+	};
+}
+
+typedef enum {
+	EXPR_NONE,
+	EXPR_SCALAR,
+	EXPR_UNARY,
+	EXPR_BINARY,
+	EXPR_SIZE,
+	EXPR_MEM
+} expr_type;
+
+typedef struct expr expr;
+
+struct expr {
+	expr_type type;
+	expr      *left;
+	expr      *right;
+	token     op;
+};
+
+static void free_expr(expr *e)
+{
+	if (!e) {
+		return;
+	}
+	free_expr(e->left);
+	free_expr(e->right);
+	if (e->op.type == TOKEN_NAME) {
+		free(e->op.v.str);
+	}
+	free(e);
+}
+
+static expr *parse_scalar_or_muldiv(char *start, char **end);
+static expr *parse_expression(char *start, char **end);
+
+static expr *parse_scalar(char *start, char **end)
+{
+	char *after_first;
+	token first = parse_token(start, &after_first);
+	if (!first.type) {
+		return NULL;
+	}
+	if (first.type == TOKEN_SIZE) {
+		fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op);
+		return NULL;
+	}
+	if (first.type == TOKEN_OPER) {
+		expr *target = parse_scalar(after_first, end);
+		if (!target) {
+			fprintf(stderr, "Unary expression %s needs value\n", first.v.op);
+			return NULL;
+		}
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_UNARY;
+		ret->op = first;
+		ret->left = target;
+		*end = after_first;
+		return ret;
+	}
+	if (first.type == TOKEN_LBRACKET) {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_MEM;
+		ret->left = parse_expression(after_first, end);
+		if (!ret->left) {
+			fprintf(stderr, "Expression expected after `[`\n");
+			free(ret);
+			return NULL;
+		}
+		token rbrack = parse_token(*end, end);
+		if (rbrack.type != TOKEN_RBRACKET) {
+			fprintf(stderr, "Missing closing `]`");
+			free_expr(ret);
+			return NULL;
+		}
+		char *after_size;
+		token size = parse_token(*end, &after_size);
+		if (size.type == TOKEN_SIZE) {
+			*end = after_size;
+			ret->op = size;
+		}
+		return ret;
+	}
+	if (first.type == TOKEN_LPAREN) {
+		expr *ret = parse_expression(after_first, end);
+		if (!ret) {
+			fprintf(stderr, "Expression expected after `(`\n");
+			return NULL;
+		}
+		token rparen = parse_token(*end, end);
+		if (rparen.type != TOKEN_RPAREN) {
+			fprintf(stderr, "Missing closing `)`");
+			free_expr(ret);
+			return NULL;
+		}
+		return ret;
+	}
+	if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) {
+		fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
+		return NULL;
+	}
+	token second = parse_token(after_first, end);
+	if (second.type != TOKEN_SIZE) {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_SCALAR;
+		ret->op = first;
+		*end = after_first;
+		return ret;
+	}
+	expr *ret = calloc(1, sizeof(expr));
+	ret->type = EXPR_SIZE;
+	ret->left = calloc(1, sizeof(expr));
+	ret->left->type = EXPR_SCALAR;
+	ret->left->op = second;
+	ret->op = first;
+	return ret;
+}
+
+static expr *maybe_binary(expr *left, char *start, char **end)
+{
+	char *after_first;
+	token first = parse_token(start, &after_first);
+	if (first.type != TOKEN_OPER) {
+		*end = start;
+		return left;
+	}
+	expr *bin = calloc(1, sizeof(expr));
+	bin->left = left;
+	bin->op = first;
+	bin->type = EXPR_BINARY;
+	switch (first.v.op[0])
+	{
+	case '*':
+	case '/':
+	case '&':
+	case '|':
+	case '^':
+		bin->right = parse_scalar(after_first, end);
+		return maybe_binary(bin, *end, end);
+	case '+':
+	case '-':
+		bin->right = parse_scalar_or_muldiv(after_first, end);
+		return maybe_binary(bin, *end, end);
+	case '=':
+	case '!':
+		bin->right = parse_expression(after_first, end);
+		return bin;
+	default:
+		bin->left = NULL;
+		free(bin);
+		return left;
+	}
+}
+
+static expr *maybe_muldiv(expr *left, char *start, char **end)
+{
+	char *after_first;
+	token first = parse_token(start, &after_first);
+	if (first.type != TOKEN_OPER) {
+		*end = start;
+		return left;
+	}
+	expr *bin = calloc(1, sizeof(expr));
+	bin->left = left;
+	bin->op = first;
+	bin->type = EXPR_BINARY;
+	switch (first.v.op[0])
+	{
+	case '*':
+	case '/':
+	case '&':
+	case '|':
+	case '^':
+		bin->right = parse_scalar(after_first, end);
+		return maybe_binary(bin, *end, end);
+	default:
+		bin->left = NULL;
+		free(bin);
+		return left;
+	}
+}
+
+static expr *parse_scalar_or_muldiv(char *start, char **end)
+{
+	char *after_first;
+	token first = parse_token(start, &after_first);
+	if (!first.type) {
+		return NULL;
+	}
+	if (first.type == TOKEN_SIZE) {
+		fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op);
+		return NULL;
+	}
+	if (first.type == TOKEN_OPER) {
+		expr *target = parse_scalar(after_first, end);
+		if (!target) {
+			fprintf(stderr, "Unary expression %s needs value\n", first.v.op);
+			return NULL;
+		}
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_UNARY;
+		ret->op = first;
+		ret->left = target;
+		return ret;
+	}
+	if (first.type == TOKEN_LBRACKET) {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_MEM;
+		ret->left = parse_expression(after_first, end);
+		if (!ret->left) {
+			fprintf(stderr, "Expression expected after `[`\n");
+			free(ret);
+			return NULL;
+		}
+		token rbrack = parse_token(*end, end);
+		if (rbrack.type != TOKEN_RBRACKET) {
+			fprintf(stderr, "Missing closing `]`");
+			free_expr(ret);
+			return NULL;
+		}
+		char *after_size;
+		token size = parse_token(*end, &after_size);
+		if (size.type == TOKEN_SIZE) {
+			*end = after_size;
+			ret->op = size;
+		}
+		return maybe_muldiv(ret, *end, end);
+	}
+	if (first.type == TOKEN_LPAREN) {
+		expr *ret = parse_expression(after_first, end);
+		if (!ret) {
+			fprintf(stderr, "Expression expected after `(`\n");
+			return NULL;
+		}
+		token rparen = parse_token(*end, end);
+		if (rparen.type != TOKEN_RPAREN) {
+			fprintf(stderr, "Missing closing `)`");
+			free_expr(ret);
+			return NULL;
+		}
+		return maybe_muldiv(ret, *end, end);
+	}
+	if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) {
+		fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
+		return NULL;
+	}
+	char *after_second;
+	token second = parse_token(after_first, &after_second);
+	if (second.type == TOKEN_OPER) {
+		expr *ret;
+		expr *bin = calloc(1, sizeof(expr));
+		bin->type = EXPR_BINARY;
+		bin->left = calloc(1, sizeof(expr));
+		bin->left->type = EXPR_SCALAR;
+		bin->left->op = first;
+		bin->op = second;
+		switch (second.v.op[0])
+		{
+		case '*':
+		case '/':
+		case '&':
+		case '|':
+		case '^':
+			bin->right = parse_scalar(after_second, end);
+			return maybe_muldiv(bin, *end, end);
+		case '+':
+		case '-':
+		case '=':
+		case '!':
+			ret = bin->left;
+			bin->left = NULL;
+			free_expr(bin);
+			return ret;
+		default:
+			fprintf(stderr, "%s is not a valid binary operator\n", second.v.op);
+			free(bin->left);
+			free(bin);
+			return NULL;
+		}
+	} else if (second.type == TOKEN_SIZE) {
+		expr *value = calloc(1, sizeof(expr));
+		value->type = EXPR_SIZE;
+		value->op = second;
+		value->left = calloc(1, sizeof(expr));
+		value->left->type = EXPR_SCALAR;
+		value->left->op = first;
+		return maybe_muldiv(value, after_second, end);
+	} else {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_SCALAR;
+		ret->op = first;
+		*end = after_first;
+		return ret;
+	}
+}
+
+static expr *parse_expression(char *start, char **end)
+{
+	char *after_first;
+	token first = parse_token(start, &after_first);
+	if (!first.type) {
+		return NULL;
+	}
+	if (first.type == TOKEN_SIZE) {
+		fprintf(stderr, "Unexpected TOKEN_SIZE '.%s'\n", first.v.op);
+		return NULL;
+	}
+	if (first.type == TOKEN_OPER) {
+		expr *target = parse_scalar(after_first, end);
+		if (!target) {
+			fprintf(stderr, "Unary expression %s needs value\n", first.v.op);
+			return NULL;
+		}
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_UNARY;
+		ret->op = first;
+		ret->left = target;
+		return ret;
+	}
+	if (first.type == TOKEN_LBRACKET) {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_MEM;
+		ret->left = parse_expression(after_first, end);
+		if (!ret->left) {
+			fprintf(stderr, "Expression expected after `[`\n");
+			free(ret);
+			return NULL;
+		}
+		token rbrack = parse_token(*end, end);
+		if (rbrack.type != TOKEN_RBRACKET) {
+			fprintf(stderr, "Missing closing `]`");
+			free_expr(ret);
+			return NULL;
+		}
+		char *after_size;
+		token size = parse_token(*end, &after_size);
+		if (size.type == TOKEN_SIZE) {
+			*end = after_size;
+			ret->op = size;
+		}
+		return maybe_binary(ret, *end, end);
+	}
+	if (first.type == TOKEN_LPAREN) {
+		expr *ret = parse_expression(after_first, end);
+		if (!ret) {
+			fprintf(stderr, "Expression expected after `(`\n");
+			return NULL;
+		}
+		token rparen = parse_token(*end, end);
+		if (rparen.type != TOKEN_RPAREN) {
+			fprintf(stderr, "Missing closing `)`");
+			free_expr(ret);
+			return NULL;
+		}
+		return maybe_binary(ret, *end, end);
+	}
+	if (first.type != TOKEN_NUM && first.type != TOKEN_NAME) {
+		fprintf(stderr, "Unexpected token %s\n", token_type_names[first.type]);
+		return NULL;
+	}
+	char *after_second;
+	token second = parse_token(after_first, &after_second);
+	if (second.type == TOKEN_OPER) {
+		expr *bin = calloc(1, sizeof(expr));
+		bin->type = EXPR_BINARY;
+		bin->left = calloc(1, sizeof(expr));
+		bin->left->type = EXPR_SCALAR;
+		bin->left->op = first;
+		bin->op = second;
+		switch (second.v.op[0])
+		{
+		case '*':
+		case '/':
+		case '&':
+		case '|':
+		case '^':
+			bin->right = parse_scalar(after_second, end);
+			return maybe_binary(bin, *end, end);
+		case '+':
+		case '-':
+			bin->right = parse_scalar_or_muldiv(after_second, end);
+			return maybe_binary(bin, *end, end);
+		case '=':
+		case '!':
+			bin->right = parse_expression(after_second, end);
+			return bin;
+		default:
+			fprintf(stderr, "%s is not a valid binary operator\n", second.v.op);
+			free(bin->left);
+			free(bin);
+			return NULL;
+		}
+	} else if (second.type == TOKEN_SIZE) {
+		expr *value = calloc(1, sizeof(expr));
+		value->type = EXPR_SIZE;
+		value->op = second;
+		value->left = calloc(1, sizeof(expr));
+		value->left->type = EXPR_SCALAR;
+		value->left->op = first;
+		return maybe_binary(value, after_second, end);
+	} else {
+		expr *ret = calloc(1, sizeof(expr));
+		ret->type = EXPR_SCALAR;
+		ret->op = first;
+		*end = after_first;
+		return ret;
+	}
+}
+
+uint8_t eval_expr(debug_root *root, expr *e, uint32_t *out)
+{
+	uint32_t right;
+	switch(e->type)
+	{
+	case EXPR_SCALAR:
+		if (e->op.type == TOKEN_NAME) {
+			return root->resolve(root, e->op.v.str, out);
+		} else {
+			*out = e->op.v.num;
+			return 1;
+		}
+	case EXPR_UNARY:
+		if (!eval_expr(root, e->left, out)) {
+			return 0;
+		}
+		switch (e->op.v.op[0])
+		{
+		case '!':
+			*out = !*out;
+			break;
+		case '~':
+			*out = ~*out;
+			break;
+		case '-':
+			*out = -*out;
+			break;
+		default:
+			return 0;
+		}
+		return 1;
+	case EXPR_BINARY:
+		if (!eval_expr(root, e->left, out) || !eval_expr(root, e->right, &right)) {
+			return 0;
+		}
+		switch (e->op.v.op[0])
+		{
+		case '+':
+			*out += right;
+			break;
+		case '-':
+			*out -= right;
+			break;
+		case '*':
+			*out *= right;
+			break;
+		case '/':
+			*out /= right;
+			break;
+		case '&':
+			*out &= right;
+			break;
+		case '|':
+			*out |= right;
+			break;
+		case '^':
+			*out ^= right;
+			break;
+		case '=':
+			*out = *out == right;
+			break;
+		case '!':
+			*out = *out != right;
+			break;
+		default:
+			return 0;
+		}
+		return 1;
+	case EXPR_SIZE:
+		if (!eval_expr(root, e->left, out)) {
+			return 0;
+		}
+		switch (e->op.v.op[0])
+		{
+		case 'b':
+			*out &= 0xFF;
+			break;
+		case 'w':
+			*out &= 0xFFFF;
+			break;
+		}
+		return 1;
+	case EXPR_MEM:
+		if (!eval_expr(root, e->left, out)) {
+			return 0;
+		}
+		return root->read_mem(root, out, e->op.v.op[0]);
+	default:
+		return 0;
+	}
+}
+
 void add_display(disp_def ** head, uint32_t *index, char format_char, char * param)
 {
 	disp_def * ndisp = malloc(sizeof(*ndisp));
@@ -116,17 +770,70 @@
 	return read_byte(address, (void **)context->mem_pointers, &context->options->gen, context);
 }
 
-uint16_t m68k_read_word(uint32_t address, m68k_context *context)
+static uint16_t m68k_read_word(uint32_t address, m68k_context *context)
 {
 	return read_word(address, (void **)context->mem_pointers, &context->options->gen, context);
 }
 
-uint32_t m68k_read_long(uint32_t address, m68k_context *context)
+static uint32_t m68k_read_long(uint32_t address, m68k_context *context)
 {
 	return m68k_read_word(address, context) << 16 | m68k_read_word(address + 2, context);
 }
 
-void debugger_print(m68k_context *context, char format_char, char *param, uint32_t address)
+static uint8_t read_m68k(debug_root *root, uint32_t *out, char size)
+{
+	m68k_context *context = root->cpu_context;
+	if (size == 'b') {
+		*out = m68k_read_byte(*out, context);
+	} else if (size == 'l') {
+		*out = m68k_read_long(*out, context);
+	} else {
+		*out = m68k_read_word(*out, context);
+	}
+	return 1;
+}
+
+static uint8_t resolve_m68k(debug_root *root, const char *name, uint32_t *out)
+{
+	m68k_context *context = root->cpu_context;
+	if ((name[0] == 'd' || name[0] == 'D') && name[1] >= '0' && name[1] <= '7' && !name[2]) {
+		*out = context->dregs[name[1]-'0'];
+	} else if ((name[0] == 'a' || name[0] == 'A') && name[1] >= '0' && name[1] <= '7' && !name[2]) {
+		*out = context->aregs[name[1]-'0'];
+	} else if (!strcasecmp(name, "sr")) {
+		*out = context->status << 8;
+		for (int flag = 0; flag < 5; flag++) {
+			*out |= context->flags[flag] << (4-flag);
+		}
+	} else if(!strcasecmp(name, "cycle")) {
+		*out = context->current_cycle;
+	} else if (!strcasecmp(name, "pc")) {
+		*out = root->address;
+	} else if (!strcasecmp(name, "usp")) {
+		*out = context->status & 0x20 ? context->aregs[8] : context->aregs[7];
+	} else if (!strcasecmp(name, "ssp")) {
+		*out = context->status & 0x20 ? context->aregs[7] : context->aregs[8];
+	} else {
+		return 0;
+	}
+	return 1;
+}
+
+static uint8_t resolve_genesis(debug_root *root, const char *name, uint32_t *out)
+{
+	if (resolve_m68k(root, name, out)) {
+		return 1;
+	}
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	if (!strcmp(name, "f") || !strcmp(name, "frame")) {
+		*out = gen->vdp->frame;
+		return 1;
+	}
+	return 0;
+}
+
+void debugger_print(debug_root *root, char format_char, char *param)
 {
 	uint32_t value;
 	char format[8];
@@ -145,75 +852,50 @@
 	default:
 		fprintf(stderr, "Unrecognized format character: %c\n", format_char);
 	}
-	if (param[0] == 'd' && param[1] >= '0' && param[1] <= '7') {
-		value = context->dregs[param[1]-'0'];
-		if (param[2] == '.') {
-			if (param[3] == 'w') {
-				value &= 0xFFFF;
-			} else if (param[3] == 'b') {
-				value &= 0xFF;
+	char *after;
+	uint8_t at_least_one = 0;
+	while (*param && *param != '\n')
+	{
+		at_least_one = 1;
+		expr *e = parse_expression(param, &after);
+		if (e) {
+			if (!eval_expr(root, e, &value)) {
+				fprintf(stderr, "Failed to eval %s\n", param);
 			}
-		}
-	} else if (param[0] == 'a' && param[1] >= '0' && param[1] <= '7') {
-		value = context->aregs[param[1]-'0'];
-		if (param[2] == '.') {
-			if (param[3] == 'w') {
-				value &= 0xFFFF;
-			} else if (param[3] == 'b') {
-				value &= 0xFF;
-			}
-		}
-	} else if (param[0] == 's' && param[1] == 'r') {
-		value = (context->status << 8);
-		for (int flag = 0; flag < 5; flag++) {
-			value |= context->flags[flag] << (4-flag);
+			free_expr(e);
+		} else {
+			fprintf(stderr, "Failed to parse %s\n", param);
 		}
-	} else if(param[0] == 'c') {
-		value = context->current_cycle;
-	} else if(param[0] == 'f') {
-		genesis_context *gen = context->system;
-		value = gen->vdp->frame;
-	} else if (param[0] == 'p' && param[1] == 'c') {
-		value = address;
-	} else if ((param[0] == '0' && param[1] == 'x') || param[0] == '$') {
-		char *after;
-		uint32_t p_addr = strtol(param+(param[0] == '0' ? 2 : 1), &after, 16);
-		if (after[0] == '.' && after[1] == 'l') {
-			value = m68k_read_long(p_addr, context);
-		} else if (after[0] == '.' && after[1] == 'b') {
-			value = m68k_read_byte(p_addr, context);
+		char *tmp_param = malloc(after-param+1);
+		memcpy(tmp_param, param, after-param);
+		tmp_param[after-param] = 0;
+		param = after;
+		if (format_char == 's') {
+			char tmp[128];
+			int i;
+			for (i = 0; i < sizeof(tmp)-1; i++, value++)
+			{
+				uint32_t addr = value;
+				root->read_mem(root, &addr, 'b');
+				char c = addr;
+				if (c < 0x20 || c > 0x7F) {
+					break;
+				}
+				tmp[i] = c;
+			}
+			tmp[i] = 0;
+			printf(format, tmp_param, tmp);
 		} else {
-			value = m68k_read_word(p_addr, context);
+			printf(format, tmp_param, value);
 		}
-	} else if(param[0] == '(' && (param[1] == 'a' || param[1] == 'd') && param[2] >= '0' && param[2] <= '7' && param[3] == ')') {
-		uint8_t reg = param[2] - '0';
-		uint32_t p_addr = param[1] == 'a' ? context->aregs[reg] : context->dregs[reg];
-		if (param[4] == '.' && param[5] == 'l') {
-			value = m68k_read_long(p_addr, context);
-		} else if (param[4] == '.' && param[5] == 'b') {
-			value = m68k_read_byte(p_addr, context);
-		} else {
-			value = m68k_read_word(p_addr, context);
+		free(tmp_param);
+		while (*param && isblank(*param) && *param != '\n')
+		{
+			++param;
 		}
-	} else {
-		fprintf(stderr, "Unrecognized parameter to p: %s\n", param);
-		return;
 	}
-	if (format_char == 's') {
-		char tmp[128];
-		int i;
-		for (i = 0; i < sizeof(tmp)-1; i++, value++)
-		{
-			char c = m68k_read_byte(value, context);
-			if (c < 0x20 || c > 0x7F) {
-				break;
-			}
-			tmp[i] = c;
-		}
-		tmp[i] = 0;
-		printf(format, param, tmp);
-	} else {
-		printf(format, param, value);
+	if (!at_least_one) {
+		fprintf(stderr, "Missing argument to print/%c\n", format_char);
 	}
 }
 
@@ -740,6 +1422,7 @@
 	if (!root) {
 		return 0;
 	}
+	root->address = address;
 	switch(input_buf[0])
 	{
 		case 'c':
@@ -842,7 +1525,7 @@
 					fputs("display command requires a parameter\n", stderr);
 					break;
 				}
-				debugger_print(context, format_char, param, address);
+				debugger_print(root, format_char, param);
 				add_display(&root->displays, &root->disp_index, format_char, param);
 			} else {
 				param = find_param(input_buf);
@@ -874,7 +1557,7 @@
 			}
 			param = find_param(input_buf);
 			if (param) {
-				debugger_print(context, format_char, param, address);
+				debugger_print(root, format_char, param);
 			} else {
 				m68k_disasm(&inst, input_buf);
 				printf("%X: %s\n", address, input_buf);
@@ -1146,7 +1829,7 @@
 		remove_breakpoint(context, address);
 	}
 	for (disp_def * cur = root->displays; cur; cur = cur->next) {
-		debugger_print(context, cur->format_char, cur->param, address);
+		debugger_print(root, cur->format_char, cur->param);
 	}
 	m68k_disasm(&inst, input_buf);
 	printf("%X: %s\n", address, input_buf);
--- a/debug.h	Thu Aug 04 23:40:13 2022 -0700
+++ b/debug.h	Sat Aug 06 22:10:54 2022 -0700
@@ -23,15 +23,22 @@
 	uint32_t      index;
 } bp_def;
 
-typedef struct {
+typedef struct debug_root debug_root;
+typedef uint8_t (*resolver)(debug_root *root, const char *name, uint32_t *out);
+typedef uint8_t (*reader)(debug_root *root, uint32_t *out, char size);
+
+struct debug_root {
 	void     *cpu_context;
 	bp_def   *breakpoints;
 	disp_def *displays;
+	resolver resolve;
+	reader   read_mem;
 	uint32_t bp_index;
 	uint32_t disp_index;
 	uint32_t branch_t;
 	uint32_t branch_f;
-} debug_root;
+	uint32_t address;
+};
 
 debug_root *find_root(void *cpu);
 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address);