changeset 2362:b6c5a0fa3dfc

Refactor debugger a bit. Allow types other than integers to be passed in expressions and stored in arrays. Better namespace support
author Michael Pavone <pavone@retrodev.com>
date Mon, 06 Nov 2023 22:41:33 -0800
parents 3350b3c8faa8
children b865f33fd47e
files debug.c debug.h z80inst.c z80inst.h
diffstat 4 files changed, 1345 insertions(+), 998 deletions(-) [+]
line wrap: on
line diff
--- a/debug.c	Mon Oct 30 00:07:56 2023 -0700
+++ b/debug.c	Mon Nov 06 22:41:33 2023 -0800
@@ -24,9 +24,8 @@
 #define Z80_OPTS options
 #endif
 
-static debug_root roots[5];
-static uint32_t num_roots;
-#define MAX_DEBUG_ROOTS (sizeof(roots)/sizeof(*roots))
+static debug_root *roots;
+static uint32_t num_roots, root_storage;
 
 debug_root *find_root(void *cpu)
 {
@@ -36,13 +35,14 @@
 			return roots + i;
 		}
 	}
-	if (num_roots < MAX_DEBUG_ROOTS) {
-		num_roots++;
-		memset(roots + num_roots - 1, 0, sizeof(debug_root));
-		roots[num_roots-1].cpu_context = cpu;
-		return roots + num_roots - 1;
+	if (num_roots == root_storage) {
+		root_storage = root_storage ? root_storage * 2 : 5;
+		roots = realloc(roots, root_storage * sizeof(debug_root));
 	}
-	return NULL;
+	num_roots++;
+	memset(roots + num_roots - 1, 0, sizeof(debug_root));
+	roots[num_roots-1].cpu_context = cpu;
+	return roots + num_roots - 1;
 }
 
 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type)
@@ -67,6 +67,111 @@
 	return cur;
 }
 
+static debug_array *arrays;
+static uint32_t num_arrays, array_storage;
+static debug_array *alloc_array(void)
+{
+	if (num_arrays == array_storage) {
+		array_storage = array_storage ? array_storage * 2 : 4;
+		arrays = realloc(arrays, sizeof(debug_array) * array_storage);
+	}
+	return arrays + num_arrays++;
+}
+
+static debug_val new_fixed_array(void *base, debug_array_get get, debug_array_set set, uint32_t size)
+{
+	debug_array *array = alloc_array();
+	array->get = get;
+	array->set = set;
+	array->append = NULL;
+	array->base = base;
+	array->size = array->storage = size;
+	debug_val ret;
+	ret.type = DBG_VAL_ARRAY;
+	ret.v.u32 = array - arrays;
+	return ret;
+}
+
+static debug_val user_array_get(debug_array *array, uint32_t index)
+{
+	debug_val *data = array->base;
+	return data[index];
+}
+
+static void user_array_set(debug_array *array, uint32_t index, debug_val value)
+{
+	debug_val *data = array->base;
+	data[index] = value;
+}
+
+static void user_array_append(debug_array *array, debug_val value)
+{
+	if (array->size == array->storage) {
+		array->storage *= 2;
+		array->base = realloc(array->base, sizeof(debug_val) * array->storage);
+	}
+	debug_val *data = array->base;
+	data[array->size++] = value;
+}
+
+static debug_val new_user_array(uint32_t size)
+{
+	debug_array *array = alloc_array();
+	array->get = user_array_get;
+	array->set = user_array_set;
+	array->append = user_array_append;
+	array->size = size;
+	array->storage = size ? size : 4;
+	debug_val ret;
+	ret.type = DBG_VAL_ARRAY;
+	ret.v.u32 = array - arrays;
+	return ret;
+}
+
+debug_array *get_array(debug_val val)
+{
+	if (val.type != DBG_VAL_ARRAY) {
+		return NULL;
+	}
+	return arrays + val.v.u32;
+}
+
+debug_val user_var_get(debug_var *var)
+{
+	return var->val;
+}
+
+void user_var_set(debug_var *var, debug_val val)
+{
+	var->val = val;
+}
+
+static void new_user_variable(debug_root *root, const char *name, debug_val val)
+{
+	debug_var *var = calloc(1, sizeof(debug_var));
+	var->get = user_var_get;
+	var->set = user_var_set;
+	var->val = val;
+	root->variables = tern_insert_ptr(root->variables, name, var);
+}
+
+static void new_readonly_variable(debug_root *root, const char *name, debug_val val)
+{
+	debug_var *var = calloc(1, sizeof(debug_var));
+	var->get = user_var_get;
+	var->set = NULL;
+	var->val = val;
+	root->variables = tern_insert_ptr(root->variables, name, var);
+}
+
+static debug_val debug_int(uint32_t i)
+{
+	debug_val ret;
+	ret.type = DBG_VAL_U32;
+	ret.v.u32 = i;
+	return ret;
+}
+
 static const char *token_type_names[] = {
 	"TOKEN_NONE",
 	"TOKEN_NUM",
@@ -227,6 +332,39 @@
 static expr *parse_scalar_or_muldiv(char *start, char **end);
 static expr *parse_expression(char *start, char **end);
 
+static void handle_namespace(expr *e)
+{
+	if (e->op.type != TOKEN_NAME && e->op.type != TOKEN_ARRAY) {
+		return;
+	}
+	char *start = e->op.v.str;
+	char *orig_start = start;
+	for (char *cur = start; *cur; ++cur)
+	{
+		if (*cur == ':') {
+			char *ns = malloc(cur - start + 1);
+			memcpy(ns, start, cur - start);
+			ns[cur - start] = 0;
+			expr *inner = calloc(1, sizeof(expr));
+			inner->type = EXPR_SCALAR;
+			inner->op.type = TOKEN_NAME;
+			start = cur + 1;
+			inner->op.v.str = start;
+			e->left = inner;
+			e->type = EXPR_NAMESPACE;
+			e->op.v.str = ns;
+			e = inner;
+		}
+	}
+	if (start != orig_start) {
+		//We've split the original string up into
+		//a bunch of individually allocated fragments
+		//this is just a little stup of the original
+		e->op.v.str = strdup(e->op.v.str);
+		free(orig_start);
+	}
+}
+
 static expr *parse_scalar(char *start, char **end)
 {
 	char *after_first;
@@ -261,6 +399,7 @@
 			ret->right = calloc(1, sizeof(expr));
 			ret->right->type = EXPR_SCALAR;
 			ret->right->op = first;
+			handle_namespace(ret->right);
 		}
 
 		ret->left = parse_expression(after_first, end);
@@ -306,6 +445,7 @@
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_SCALAR;
 		ret->op = first;
+		handle_namespace(ret);
 		*end = after_first;
 		return ret;
 	}
@@ -314,6 +454,7 @@
 	ret->left = calloc(1, sizeof(expr));
 	ret->left->type = EXPR_SCALAR;
 	ret->left->op = second;
+	handle_namespace(ret->left);
 	ret->op = first;
 	return ret;
 }
@@ -417,6 +558,7 @@
 			ret->right = calloc(1, sizeof(expr));
 			ret->right->type = EXPR_SCALAR;
 			ret->right->op = first;
+			handle_namespace(ret->right);
 		}
 
 		ret->left = parse_expression(after_first, end);
@@ -466,6 +608,7 @@
 		bin->left = calloc(1, sizeof(expr));
 		bin->left->type = EXPR_SCALAR;
 		bin->left->op = first;
+		handle_namespace(bin->left);
 		bin->op = second;
 		switch (second.v.op[0])
 		{
@@ -499,11 +642,13 @@
 		value->left = calloc(1, sizeof(expr));
 		value->left->type = EXPR_SCALAR;
 		value->left->op = first;
+		handle_namespace(value->left);
 		return maybe_muldiv(value, after_second, end);
 	} else {
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_SCALAR;
 		ret->op = first;
+		handle_namespace(ret);
 		*end = after_first;
 		return ret;
 	}
@@ -542,6 +687,7 @@
 			ret->right = calloc(1, sizeof(expr));
 			ret->right->type = EXPR_SCALAR;
 			ret->right->op = first;
+			handle_namespace(ret->right);
 		}
 
 		ret->left = parse_expression(after_first, end);
@@ -590,6 +736,7 @@
 		bin->left = calloc(1, sizeof(expr));
 		bin->left->type = EXPR_SCALAR;
 		bin->left->op = first;
+		handle_namespace(bin->left);
 		bin->op = second;
 		switch (second.v.op[0])
 		{
@@ -638,6 +785,7 @@
 		value->left = calloc(1, sizeof(expr));
 		value->left->type = EXPR_SCALAR;
 		value->left->op = first;
+		handle_namespace(value->left);
 		return maybe_binary(value, after_second, end);
 	} else {
 		if (second.type == TOKEN_NAME) {
@@ -646,42 +794,27 @@
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_SCALAR;
 		ret->op = first;
+		handle_namespace(ret);
 		*end = after_first;
 		return ret;
 	}
 }
 
-static debug_array* full_array_resolve(debug_root *root, const char *name)
+uint8_t eval_expr(debug_root *root, expr *e, debug_val *out)
 {
-	debug_array *ret = root->array_resolve(root, name);
-	if (!ret) {
-		ret = tern_find_ptr(root->arrays, name);
-	}
-	return ret;
-}
-
-uint8_t eval_expr(debug_root *root, expr *e, uint32_t *out)
-{
-	uint32_t right;
+	debug_val right;
 	switch(e->type)
 	{
 	case EXPR_SCALAR:
-		if (e->op.type == TOKEN_NAME) {
-			if (root->resolve(root, e->op.v.str, out)) {
-				return 1;
+		if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY) {
+			debug_var *var = tern_find_ptr(root->variables, e->op.v.str);
+			if (!var) {
+				return 0;
 			}
-			tern_val v;
-			if (tern_find(root->variables, e->op.v.str, &v)) {
-				*out = v.intval;
-				return 1;
-			}
-			if (tern_find(root->symbols, e->op.v.str, &v)) {
-				*out = v.intval;
-				return 1;
-			}
-			return 0;
+			*out = var->get(var);
+			return 1;
 		} else {
-			*out = e->op.v.num;
+			*out = debug_int(e->op.v.num);
 			return 1;
 		}
 	case EXPR_UNARY:
@@ -691,13 +824,22 @@
 		switch (e->op.v.op[0])
 		{
 		case '!':
-			*out = !*out;
+			if (out->type != DBG_VAL_U32) { fprintf(stderr, "operator ! is only defined for integers"); return 0; }
+			out->v.u32 = !out->v.u32;
 			break;
 		case '~':
-			*out = ~*out;
+			if (out->type != DBG_VAL_U32) { fprintf(stderr, "operator ~ is only defined for integers"); return 0; }
+			out->v.u32 = ~out->v.u32;
 			break;
 		case '-':
-			*out = -*out;
+			if (out->type == DBG_VAL_U32) {
+				out->v.u32 = -out->v.u32;
+			} else if (out->type == DBG_VAL_F32) {
+				out->v.f32 = -out->v.f32;
+			} else {
+				fprintf(stderr, "operator ~ is only defined for integers and floats");
+				return 0;
+			}
 			break;
 		default:
 			return 0;
@@ -707,56 +849,114 @@
 		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;
-		case '>':
-			*out = e->op.v.op[1] ? *out >= right : *out > right;
-			break;
-		case '<':
-			*out = e->op.v.op[1] ? *out <= right : *out < right;
-			break;
-		default:
-			return 0;
+		if (out->type != right.type) {
+			if (out->type == DBG_VAL_F32) {
+				if (right.type == DBG_VAL_U32) {
+					right.type = DBG_VAL_F32;
+					float v = right.v.u32;
+					right.v.f32 = v;
+				} else {
+					fprintf(stderr, "Invalid type on right side of binary operator\n");
+					return 0;
+				}
+			} else if (out->type == DBG_VAL_U32) {
+				if (right.type == DBG_VAL_F32) {
+					out->type = DBG_VAL_F32;
+					float v = out->v.u32;
+					out->v.f32 = v;
+				} else {
+					fprintf(stderr, "Invalid type on right side of binary operator\n");
+					return 0;
+				}
+			}
+		}
+		if (out->type == DBG_VAL_U32) {
+			switch (e->op.v.op[0])
+			{
+			case '+':
+				out->v.u32 += right.v.u32;
+				break;
+			case '-':
+				out->v.u32 -= right.v.u32;
+				break;
+			case '*':
+				out->v.u32 *= right.v.u32;
+				break;
+			case '/':
+				out->v.u32 /= right.v.u32;
+				break;
+			case '&':
+				out->v.u32 &= right.v.u32;
+				break;
+			case '|':
+				out->v.u32 |= right.v.u32;
+				break;
+			case '^':
+				out->v.u32 ^= right.v.u32;
+				break;
+			case '=':
+				out->v.u32 = out->v.u32 == right.v.u32;
+				break;
+			case '!':
+				out->v.u32 = out->v.u32 != right.v.u32;
+				break;
+			case '>':
+				out->v.u32 = e->op.v.op[1] ? out->v.u32 >= right.v.u32 : out->v.u32 > right.v.u32;
+				break;
+			case '<':
+				out->v.u32 = e->op.v.op[1] ? out->v.u32 <= right.v.u32 : out->v.u32 < right.v.u32;
+				break;
+			default:
+				return 0;
+			}
+		} else if (out->type == DBG_VAL_F32) {
+			switch (e->op.v.op[0])
+			{
+			case '+':
+				out->v.f32 += right.v.f32;
+				break;
+			case '-':
+				out->v.f32 -= right.v.f32;
+				break;
+			case '*':
+				out->v.f32 *= right.v.f32;
+				break;
+			case '/':
+				out->v.f32 /= right.v.f32;
+				break;
+			case '=':
+				out->v.u32 = out->v.f32 == right.v.f32;
+				out->type = DBG_VAL_U32;
+				break;
+			case '!':
+				out->v.u32 = out->v.f32 != right.v.f32;
+				out->type = DBG_VAL_U32;
+				break;
+			case '>':
+				out->v.u32 = e->op.v.op[1] ? out->v.f32 >= right.v.f32 : out->v.f32 > right.v.f32;
+				out->type = DBG_VAL_U32;
+				break;
+			case '<':
+				out->v.u32 = e->op.v.op[1] ? out->v.f32 <= right.v.f32 : out->v.f32 < right.v.f32;
+				out->type = DBG_VAL_U32;
+				break;
+			default:
+				return 0;
+			}
 		}
 		return 1;
 	case EXPR_SIZE:
 		if (!eval_expr(root, e->left, out)) {
 			return 0;
 		}
+		if (out->type != DBG_VAL_U32) { fprintf(stderr, "Size expressions are only defined for integers"); return 0; }
 		switch (e->op.v.op[0])
 		{
 		case 'b':
-			*out &= 0xFF;
+			out->v.u32 &= 0xFF;
 			break;
 		case 'w':
-			*out &= 0xFFFF;
+			out->v.u32 &= 0xFFFF;
 			break;
 		}
 		return 1;
@@ -764,15 +964,30 @@
 		if (!eval_expr(root, e->left, out)) {
 			return 0;
 		}
+		if (out->type != DBG_VAL_U32) { fprintf(stderr, "Array index must be integer"); return 0; }
 		if (e->right) {
-			debug_array *array = full_array_resolve(root, e->right->op.v.str);
-			if (!array || *out >= array->size) {
+			if (!eval_expr(root, e->right, &right)) {
+				return 0;
+			}
+			debug_array *array = get_array(right);
+			if (!array) {
+				fprintf(stderr, "Attempt to index into value that is not an array");
 				return 0;
 			}
-			*out = array->get(root, array, *out);
+			if (out->v.u32 >= array->size) {
+				return 0;
+			}
+			*out = array->get(array, out->v.u32);
 			return 1;
 		}
-		return root->read_mem(root, out, e->op.v.op[0]);
+		return root->read_mem(root, &out->v.u32, e->op.v.op[0]);
+	case EXPR_NAMESPACE:
+		root = tern_find_ptr(root->other_roots, e->op.v.str);
+		if (!root) {
+			fprintf(stderr, "%s is not a valid namespace\n", e->op.v.str);
+			return 0;
+		}
+		return eval_expr(root, e->left, out);
 	default:
 		return 0;
 	}
@@ -859,364 +1074,470 @@
 	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, "sp")) {
-		*out = context->aregs[7];
-	} 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 set_m68k(debug_root *root, const char *name, uint32_t value)
+static uint8_t debug_cast_int(debug_val val, uint32_t *out)
 {
-	m68k_context *context = root->cpu_context;
-	if ((name[0] == 'd' || name[0] == 'D') && name[1] >= '0' && name[1] <= '7' && !name[2]) {
-		context->dregs[name[1]-'0'] = value;
-	} else if ((name[0] == 'a' || name[0] == 'A') && name[1] >= '0' && name[1] <= '7' && !name[2]) {
-		context->aregs[name[1]-'0'] = value;
-	} else if (!strcasecmp(name, "sr")) {
-		context->status = value >> 8;
-		for (int flag = 0; flag < 5; flag++) {
-			context->flags[flag] = (value & (1 << (4 - flag))) != 0;
-		}
-	} else if (!strcasecmp(name, "sp")) {
-		context->aregs[7] = value;
-	} else if (!strcasecmp(name, "usp")) {
-		context->aregs[context->status & 0x20 ? 8 : 7] = value;
-	} else if (!strcasecmp(name, "ssp")) {
-		context->aregs[context->status & 0x20 ? 7 : 8] = value;
-	} else {
-		return 0;
-	}
-	return 1;
-}
-
-static uint8_t resolve_vdp(debug_root *root, const char *name, uint32_t *out)
-{
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	vdp_context *vdp = gen->vdp;
-	if (!strcasecmp(name, "vcounter")) {
-		*out = vdp->vcounter;
-	} else if (!strcasecmp(name, "hcounter")) {
-		*out = vdp->hslot;
-	} else if (!strcasecmp(name, "address")) {
-		*out = vdp->address;
-	}else if (!strcasecmp(name, "cd")) {
-		*out = vdp->cd;
-	} else if (!strcasecmp(name, "status")) {
-		*out = vdp_status(vdp);
-	} else {
-		return 0;
-	}
-	return 1;
-}
-
-static uint8_t resolve_genesis(debug_root *root, const char *name, uint32_t *out)
-{
-
-	for (const char *cur = name; *cur; ++cur)
-	{
-		if (*cur == ':') {
-			if (cur - name == 3 && !memcmp(name, "vdp", 3)) {
-				return resolve_vdp(root, cur + 1, out);
-			} else if (cur - name == 3 && !memcmp(name, "sub", 3)) {
-				m68k_context *m68k = root->cpu_context;
-				genesis_context *gen = m68k->system;
-				if (gen->expansion) {
-					segacd_context *cd = gen->expansion;
-					root = find_m68k_root(cd->m68k);
-					return root->resolve(root, cur + 1, out);
-				} else {
-					return 0;
-				}
-			} else {
-				return 0;
-			}
-		}
-	}
-	if (resolve_m68k(root, name, out)) {
+	if (val.type == DBG_VAL_U32) {
+		*out = val.v.u32;
 		return 1;
 	}
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	if (!strcmp(name, "f") || !strcmp(name, "frame")) {
-		*out = gen->vdp->frame;
+	if (val.type == DBG_VAL_F32) {
+		*out = val.v.f32;
 		return 1;
 	}
 	return 0;
 }
 
-static uint32_t debug_vram_get(debug_root *root, debug_array *array, uint32_t index)
+static uint8_t debug_cast_bool(debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->vdp->vdpmem[index];
+	switch(val.type)
+	{
+	case DBG_VAL_U32: return val.v.u32 != 0;
+	case DBG_VAL_F32: return val.v.f32 != 0.0f;
+	case DBG_VAL_ARRAY: return get_array(val)->size != 0;
+	default: return 1;
+	}
+}
+
+static debug_val m68k_dreg_get(debug_var *var)
+{
+	m68k_context *context = var->ptr;
+	return debug_int(context->dregs[var->val.v.u32]);
 }
 
-static void debug_vram_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static void m68k_dreg_set(debug_var *var, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->vdp->vdpmem[index] = value;
+	m68k_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "M68K register d%d can only be set to an integer\n", var->val.v.u32);
+		return;
+	}
+	context->dregs[var->val.v.u32] = ival;
 }
 
-static uint32_t debug_vsram_get(debug_root *root, debug_array *array, uint32_t index)
+static debug_val m68k_areg_get(debug_var *var)
+{
+	m68k_context *context = var->ptr;
+	return debug_int(context->aregs[var->val.v.u32]);
+}
+
+static void m68k_areg_set(debug_var *var, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->vdp->vsram[index] & VSRAM_BITS;
+	m68k_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "M68K register a%d can only be set to an integer\n", var->val.v.u32);
+		return;
+	}
+	context->aregs[var->val.v.u32] = ival;
 }
 
-static void debug_vsram_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_val m68k_sr_get(debug_var *var)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->vdp->vsram[index] = value;
+	m68k_context *context = var->ptr;
+	debug_val ret;
+	ret.v.u32 = context->status << 8;
+	for (int flag = 0; flag < 5; flag++)
+	{
+		ret.v.u32 |= context->flags[flag] << (4-flag);
+	}
+	ret.type = DBG_VAL_U32;
+	return ret;
 }
 
-static uint32_t debug_cram_get(debug_root *root, debug_array *array, uint32_t index)
+static void m68k_sr_set(debug_var *var, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->vdp->cram[index] & CRAM_BITS;
+	m68k_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "M68K register sr can only be set to an integer\n");
+		return;
+	}
+	context->status = ival >> 8;
+	for (int flag = 0; flag < 5; flag++) {
+		context->flags[flag] = (ival & (1 << (4 - flag))) != 0;
+	}
 }
 
-static void debug_cram_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_val m68k_cycle_get(debug_var *var)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->vdp->cram[index] = value;
+	m68k_context *context = var->ptr;
+	return debug_int(context->current_cycle);
+}
+
+static debug_val m68k_usp_get(debug_var *var)
+{
+	m68k_context *context = var->ptr;
+	return debug_int(context->status & 0x20 ? context->aregs[8] : context->aregs[7]);
 }
 
-static uint32_t debug_vreg_get(debug_root *root, debug_array *array, uint32_t index)
+static void m68k_usp_set(debug_var *var, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->vdp->regs[index];
+	m68k_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "M68K register usp can only be set to an integer\n");
+		return;
+	}
+	context->aregs[context->status & 0x20 ? 8 : 7] = ival;
 }
 
-static void debug_vreg_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_val m68k_ssp_get(debug_var *var)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	vdp_reg_write(gen->vdp, index, value);
+	m68k_context *context = var->ptr;
+	return debug_int(context->status & 0x20 ? context->aregs[7] : context->aregs[8]);
 }
 
-static debug_array* resolve_vdp_array(debug_root *root, const char *name)
+static void m68k_ssp_set(debug_var *var, debug_val val)
 {
-	static debug_array vram = {
-		.get = debug_vram_get, .set = debug_vram_set,
-		.size = VRAM_SIZE, .storage = VRAM_SIZE
-	};
-	static debug_array vsram = {
-		.get = debug_vsram_get, .set = debug_vsram_set,
-	};
-	static debug_array cram = {
-		.get = debug_cram_get, .set = debug_cram_set,
-		.storage = CRAM_SIZE, .size = CRAM_SIZE
-	};
-	static debug_array regs = {
-		.get = debug_vreg_get, .set = debug_vreg_set,
-		.storage = VDP_REGS, .size = VDP_REGS
-	};
-	if (!strcasecmp(name, "vram")) {
-		return &vram;
+	m68k_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "M68K register ssp can only be set to an integer\n");
+		return;
 	}
-	if (!strcasecmp(name, "vsram")) {
-		m68k_context *m68k = root->cpu_context;
-		genesis_context *gen = m68k->system;
-		vsram.storage = vsram.size = gen->vdp->vsram_size;
-		return &vsram;
-	}
-	if (!strcasecmp(name, "cram")) {
-		return &cram;
-	}
-	if (!strcasecmp(name, "reg")) {
-		return &regs;
-	}
-	return NULL;
+	context->aregs[context->status & 0x20 ? 7 : 8] = ival;
+}
+
+static debug_val root_address_get(debug_var *var)
+{
+	debug_root *root = var->ptr;
+	return debug_int(root->address);
 }
 
-static uint32_t debug_part1_get(debug_root *root, debug_array *array, uint32_t index)
+static void m68k_names(debug_root *root)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->ym->part1_regs[index];
+	debug_var *var;
+	for (char i = 0; i < 8; i++)
+	{
+		char rname[3] = {'d', '0' + i, 0};
+		var = calloc(1, sizeof(debug_var));
+		var->get = m68k_dreg_get;
+		var->set = m68k_dreg_set;
+		var->ptr = root->cpu_context;
+		var->val.v.u32 = i;
+		root->variables = tern_insert_ptr(root->variables, rname, var);
+		rname[0] = 'D';
+		root->variables = tern_insert_ptr(root->variables, rname, var);
+
+		var = calloc(1, sizeof(debug_var));
+		var->get = m68k_areg_get;
+		var->set = m68k_areg_set;
+		var->ptr = root->cpu_context;
+		var->val.v.u32 = i;
+		rname[0] = 'a';
+		root->variables = tern_insert_ptr(root->variables, rname, var);
+		rname[0] = 'A';
+		root->variables = tern_insert_ptr(root->variables, rname, var);
+		if (i == 7) {
+			root->variables = tern_insert_ptr(root->variables, "sp", var);
+			root->variables = tern_insert_ptr(root->variables, "SP", var);
+		}
+	}
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = m68k_sr_get;
+	var->set = m68k_sr_set;
+	var->ptr = root->cpu_context;
+	root->variables = tern_insert_ptr(root->variables, "sr", var);
+	root->variables = tern_insert_ptr(root->variables, "SR", var);
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = root_address_get;
+	var->ptr = root;
+	root->variables = tern_insert_ptr(root->variables, "pc", var);
+	root->variables = tern_insert_ptr(root->variables, "PC", var);
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = m68k_usp_get;
+	var->set = m68k_usp_set;
+	var->ptr = root->cpu_context;
+	root->variables = tern_insert_ptr(root->variables, "usp", var);
+	root->variables = tern_insert_ptr(root->variables, "USP", var);
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = m68k_ssp_get;
+	var->set = m68k_ssp_set;
+	var->ptr = root->cpu_context;
+	root->variables = tern_insert_ptr(root->variables, "ssp", var);
+	root->variables = tern_insert_ptr(root->variables, "SSP", var);
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = m68k_cycle_get;
+	var->ptr = root->cpu_context;
+	root->variables = tern_insert_ptr(root->variables, "cycle", var);
 }
 
-static void debug_part1_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_val vcounter_get(debug_var *var)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	uint8_t old_part = gen->ym->selected_part;
-	uint8_t old_reg = gen->ym->selected_reg;
-	gen->ym->selected_part = 0;
-	gen->ym->selected_reg = index;
-	ym_data_write(gen->ym, value);
-	gen->ym->selected_part = old_part;
-	gen->ym->selected_reg = old_reg;
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp->vcounter);
+}
+
+static debug_val hcounter_get(debug_var *var)
+{
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp->hslot);
 }
 
-static uint32_t debug_part2_get(debug_root *root, debug_array *array, uint32_t index)
+static debug_val vdp_address_get(debug_var *var)
+{
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp->address);
+}
+
+static void vdp_address_set(debug_var *var, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->ym->part2_regs[index];
+	vdp_context *vdp = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "vdp address can only be set to an integer\n");
+		return;
+	}
+	vdp->address = ival & 0x1FFFF;
+}
+
+static debug_val vdp_cd_get(debug_var *var)
+{
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp->cd);
 }
 
-static void debug_part2_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static void vdp_cd_set(debug_var *var, debug_val val)
+{
+	vdp_context *vdp = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "vdp cd can only be set to an integer\n");
+		return;
+	}
+	vdp->cd = ival & 0x3F;
+}
+
+static debug_val vdp_status_get(debug_var *var)
+{
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp_status(vdp));
+}
+
+static debug_val debug_vram_get(debug_array *array, uint32_t index)
+{
+	vdp_context *vdp = array->base;
+	return debug_int(vdp->vdpmem[index]);
+}
+
+static void debug_vram_set(debug_array *array, uint32_t index, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	uint8_t old_part = gen->ym->selected_part;
-	uint8_t old_reg = gen->ym->selected_reg;
-	gen->ym->selected_part = 1;
-	gen->ym->selected_reg = index;
-	ym_data_write(gen->ym, value);
-	gen->ym->selected_part = old_part;
-	gen->ym->selected_reg = old_reg;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "vram can only be set to integers\n");
+		return;
+	}
+	vdp_context *vdp = array->base;
+	vdp->vdpmem[index] = ival;
+}
+
+static debug_val debug_vsram_get(debug_array *array, uint32_t index)
+{
+	vdp_context *vdp = array->base;
+	return debug_int(vdp->vsram[index] & VSRAM_BITS);
+}
+
+static void debug_vsram_set(debug_array *array, uint32_t index, debug_val val)
+{
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "vsram can only be set to integers\n");
+		return;
+	}
+	vdp_context *vdp = array->base;
+	vdp->vsram[index] = ival;
+}
+
+static debug_val debug_cram_get(debug_array *array, uint32_t index)
+{
+	vdp_context *vdp = array->base;
+	return debug_int(vdp->cram[index] & CRAM_BITS);
 }
 
-static debug_array* resolve_ym2612_array(debug_root *root, const char *name)
+static void debug_cram_set(debug_array *array, uint32_t index, debug_val val)
+{
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "cram can only be set to integers\n");
+		return;
+	}
+	vdp_context *vdp = array->base;
+	vdp->cram[index] = ival;
+}
+
+static debug_val debug_vreg_get(debug_array *array, uint32_t index)
 {
-	static debug_array part1 = {
-		.get = debug_part1_get, .set = debug_part1_set,
-		.storage = YM_REG_END, .size = YM_REG_END
-	};
-	static debug_array part2 = {
-		.get = debug_part2_get, .set = debug_part2_set,
-		.storage = YM_REG_END, .size = YM_REG_END
-	};
-	if (!strcasecmp(name, "part1")) {
-		return &part1;
+	vdp_context *vdp = array->base;
+	return debug_int(vdp->regs[index]);
+}
+
+static void debug_vreg_set(debug_array *array, uint32_t index, debug_val val)
+{
+	vdp_context *vdp = array->base;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "vdp registers can only be set to integers\n");
+		return;
 	}
-	if (!strcasecmp(name, "part2")) {
-		return &part2;
-	}
-	return NULL;
+	vdp_reg_write(vdp, index, ival);
 }
 
-static uint32_t debug_psgfreq_get(debug_root *root, debug_array *array, uint32_t index)
-{
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->psg->counter_load[index];
-}
-
-static void debug_psgfreq_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_root* find_vdp_root(vdp_context *context)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->psg->counter_load[index] = value;
-}
+	debug_root *root = find_root(context);
+	debug_var *var = calloc(1, sizeof(debug_var));
+	var->get = vcounter_get;
+	var->ptr = context;
+	root->variables = tern_insert_ptr(root->variables, "vcounter", var);
+
+	var = calloc(1, sizeof(debug_var));
+	var->get = hcounter_get;
+	var->ptr = context;
+	root->variables = tern_insert_ptr(root->variables, "hcounter", var);
 
-static uint32_t debug_psgcount_get(debug_root *root, debug_array *array, uint32_t index)
-{
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->psg->counters[index];
-}
+	var = calloc(1, sizeof(debug_var));
+	var->get = vdp_address_get;
+	var->set = vdp_address_set;
+	var->ptr = context;
+	root->variables = tern_insert_ptr(root->variables, "address", var);
 
-static void debug_psgcount_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
-{
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->psg->counters[index] = value;
+	var = calloc(1, sizeof(debug_var));
+	var->get = vdp_cd_get;
+	var->set = vdp_cd_set;
+	var->ptr = context;
+	root->variables = tern_insert_ptr(root->variables, "cd", var);
+
+	new_readonly_variable(root, "vram", new_fixed_array(context, debug_vram_get, debug_vram_set, VRAM_SIZE));
+	new_readonly_variable(root, "vsram", new_fixed_array(context, debug_vsram_get, debug_vsram_set, context->vsram_size));
+	new_readonly_variable(root, "cram", new_fixed_array(context, debug_cram_get, debug_cram_set, CRAM_SIZE));
+	new_readonly_variable(root, "reg", new_fixed_array(context, debug_vreg_get, debug_vreg_set, VDP_REGS));
+
+	return root;
 }
 
-static uint32_t debug_psgvol_get(debug_root *root, debug_array *array, uint32_t index)
+static debug_val debug_part1_get(debug_array *array, uint32_t index)
+{
+	ym2612_context *ym = array->base;
+	return debug_int(ym->part1_regs[index]);
+}
+
+static void debug_part1_set(debug_array *array, uint32_t index, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	return gen->psg->volume[index];
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "ym2612 registers can only be set to integers\n");
+		return;
+	}
+	ym2612_context *ym = array->base;
+	uint8_t old_part = ym->selected_part;
+	uint8_t old_reg = ym->selected_reg;
+	ym->selected_part = 0;
+	ym->selected_reg = index;
+	ym_data_write(ym, ival);
+	ym->selected_part = old_part;
+	ym->selected_reg = old_reg;
 }
 
-static void debug_psgvol_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+static debug_val debug_part2_get(debug_array *array, uint32_t index)
+{
+	ym2612_context *ym = array->base;
+	return debug_int(ym->part2_regs[index]);
+}
+
+static void debug_part2_set(debug_array *array, uint32_t index, debug_val val)
 {
-	m68k_context *m68k = root->cpu_context;
-	genesis_context *gen = m68k->system;
-	gen->psg->volume[index] = value;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "ym2612 registers can only be set to integers\n");
+		return;
+	}
+	ym2612_context *ym = array->base;
+	uint8_t old_part = ym->selected_part;
+	uint8_t old_reg = ym->selected_reg;
+	ym->selected_part = 1;
+	ym->selected_reg = index;
+	ym_data_write(ym, ival);
+	ym->selected_part = old_part;
+	ym->selected_reg = old_reg;
+}
+
+static debug_root* find_ym2612_root(ym2612_context *context)
+{
+	debug_root *root = find_root(context);
+
+	new_readonly_variable(root, "part1", new_fixed_array(context, debug_part1_get, debug_part1_set, YM_REG_END));
+	new_readonly_variable(root, "part2", new_fixed_array(context, debug_part2_get, debug_part2_set, YM_REG_END));
+
+	return root;
 }
 
-static debug_array* resolve_psg_array(debug_root *root, const char *name)
+
+static debug_val debug_psgfreq_get(debug_array *array, uint32_t index)
+{
+	psg_context *psg = array->base;
+	return debug_int(psg->counter_load[index]);
+}
+
+static void debug_psgfreq_set(debug_array *array, uint32_t index, debug_val val)
 {
-	static debug_array freq = {
-		.get = debug_psgfreq_get, .set = debug_psgfreq_set,
-		.storage = 4, .size = 4
-	};
-	static debug_array counter = {
-		.get = debug_psgcount_get, .set = debug_psgcount_set,
-		.storage = 4, .size = 4
-	};
-	static debug_array volume = {
-		.get = debug_psgvol_get, .set = debug_psgvol_set,
-		.storage = 4, .size = 4
-	};
-	if (!strcasecmp("frequency", name)) {
-		return &freq;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "psg registers can only be set to integers\n");
+		return;
 	}
-	if (!strcasecmp("counter", name)) {
-		return &counter;
-	}
-	if (!strcasecmp("volume", name)) {
-		return &volume;
-	}
-	return NULL;
+	psg_context *psg = array->base;
+	psg->counter_load[index] = ival;
+}
+
+static debug_val debug_psgcount_get(debug_array *array, uint32_t index)
+{
+	psg_context *psg = array->base;
+	return debug_int(psg->counters[index]);
 }
 
-static debug_array* resolve_genesis_array(debug_root *root, const char *name)
+static void debug_psgcount_set(debug_array *array, uint32_t index, debug_val val)
 {
-	for (const char *cur = name; *cur; ++cur)
-	{
-		if (*cur == ':') {
-			if (cur - name == 3 && !memcmp(name, "vdp", 3)) {
-				return resolve_vdp_array(root, cur + 1);
-			} else if (cur - name == 2 && !memcmp(name, "ym", 2)) {
-				return resolve_ym2612_array(root, cur + 1);
-			} else if (cur - name == 3 && !memcmp(name, "psg", 3)) {
-				return resolve_psg_array(root, cur + 1);
-			} /*else if (cur - name == 3 && !memcmp(name, "sub", 3)) {
-				m68k_context *m68k = root->cpu_context;
-				genesis_context *gen = m68k->system;
-				if (gen->expansion) {
-					segacd_context *cd = gen->expansion;
-					root = find_m68k_root(cd->m68k);
-					return root->resolve(root, cur + 1, out);
-				} else {
-					return NULL;
-				}
-			}*/ else {
-				return NULL;
-			}
-		}
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "psg registers can only be set to integers\n");
+		return;
 	}
-	return NULL;
+	psg_context *psg = array->base;
+	psg->counters[index] = ival;
+}
+
+static debug_val debug_psgvol_get(debug_array *array, uint32_t index)
+{
+	psg_context *psg = array->base;
+	return debug_int(psg->volume[index]);
 }
 
-static debug_array* resolve_null_array(debug_root *root, const char *name)
+static void debug_psgvol_set(debug_array *array, uint32_t index, debug_val val)
 {
-	return NULL;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "psg registers can only be set to integers\n");
+		return;
+	}
+	psg_context *psg = array->base;
+	psg->volume[index] = ival;
+}
+
+debug_root *find_psg_root(psg_context *psg)
+{
+	debug_root *root = find_root(psg);
+
+	new_readonly_variable(root, "frequency", new_fixed_array(psg, debug_psgfreq_get, debug_psgfreq_set, 4));
+	new_readonly_variable(root, "counter", new_fixed_array(psg, debug_psgcount_get, debug_psgcount_set, 4));
+	new_readonly_variable(root, "volume", new_fixed_array(psg, debug_psgvol_get, debug_psgvol_set, 4));
+
+	return root;
 }
 
 void ambiguous_iter(char *key, tern_val val, uint8_t valtype, void *data)
@@ -1660,26 +1981,38 @@
 	}
 }
 
-static void do_print(debug_root *root, char *format_str, char *raw, uint32_t value)
+static void do_print(debug_root *root, char *format_str, char *raw, debug_val value)
 {
-	if (format_str[5] == 's') {
-		char tmp[128];
-		int j;
-		uint32_t addr = value;
-		for (j = 0; j < sizeof(tmp)-1; j++, addr++)
-		{
-			uint32_t tmp_addr = addr;
-			root->read_mem(root, &tmp_addr, 'b');
-			char c = tmp_addr;
-			if (c < 0x20 || c > 0x7F) {
-				break;
+	switch(value.type)
+	{
+	case DBG_VAL_U32:
+		if (format_str[5] == 's') {
+			char tmp[128];
+			int j;
+			uint32_t addr = value.v.u32;
+			for (j = 0; j < sizeof(tmp)-1; j++, addr++)
+			{
+				uint32_t tmp_addr = addr;
+				root->read_mem(root, &tmp_addr, 'b');
+				char c = tmp_addr;
+				if (c < 0x20 || c > 0x7F) {
+					break;
+				}
+				tmp[j] = c;
 			}
-			tmp[j] = c;
+			tmp[j] = 0;
+			printf(format_str, raw, tmp);
+		} else {
+			printf(format_str, raw, value.v.u32);
 		}
-		tmp[j] = 0;
-		printf(format_str, raw, tmp);
-	} else {
-		printf(format_str, raw, value);
+		break;
+	case DBG_VAL_F32: {
+		char tmp = format_str[5];
+		format_str[5] = 'f';
+		printf(format_str, raw, value.v.f32);
+		format_str[5] = tmp;
+		break;
+	}
 	}
 }
 
@@ -1766,28 +2099,44 @@
 				free(fmt);
 				return 1;
 			}
-			uint32_t val;
+			debug_val val;
 			if (!eval_expr(root, arg, &val)) {
 				free(fmt);
 				return 1;
 			}
 			if (cur[1] == 's') {
-				char tmp[128];
-				int j;
-				for (j = 0; j < sizeof(tmp)-1; j++, val++)
-				{
-					uint32_t addr = val;
-					root->read_mem(root, &addr, 'b');
-					char c = addr;
-					if (c < 0x20 || c > 0x7F) {
-						break;
+				if (val.type == DBG_VAL_STRING) {
+					//TODO: implement me
+				} else {
+					char tmp[128];
+					uint32_t address;
+					if (!debug_cast_int(val, &address)) {
+						fprintf(stderr, "Format char 's' accepts only integers and strings\n");
+						free(fmt);
+						return 1;
 					}
-					tmp[j] = c;
+					int j;
+					for (j = 0; j < sizeof(tmp)-1; j++, address++)
+					{
+						uint32_t addr = address;
+						root->read_mem(root, &addr, 'b');
+						char c = addr;
+						if (c < 0x20 || c > 0x7F) {
+							break;
+						}
+						tmp[j] = c;
+					}
+					tmp[j] = 0;
+					printf(format_str, tmp);
 				}
-				tmp[j] = 0;
-				printf(format_str, tmp);
 			} else {
-				printf(format_str, val);
+				uint32_t ival;
+				if (!debug_cast_int(val, &ival)) {
+					fprintf(stderr, "Format char '%c' only accepts integers\n", cur[1]);
+					free(fmt);
+					return 1;
+				}
+				printf(format_str, ival);
 			}
 			cur += 2;
 		} else {
@@ -1817,9 +2166,14 @@
 static uint8_t cmd_delete_display(debug_root *root, parsed_command *cmd)
 {
 	disp_def **cur = &root->displays;
+	uint32_t index;
+	if (!debug_cast_int(cmd->args[0].value, &index)) {
+		fprintf(stderr, "Argument to deletedisplay must be an integer\n");
+		return 1;
+	}
 	while (*cur)
 	{
-		if ((*cur)->index == cmd->args[0].value) {
+		if ((*cur)->index == index) {
 			disp_def *del_disp = *cur;
 			*cur = del_disp->next;
 			free(del_disp->format);
@@ -1851,9 +2205,14 @@
 
 static uint8_t cmd_command(debug_root *root, parsed_command *cmd)
 {
-	bp_def **target = find_breakpoint_idx(&root->breakpoints, cmd->args[0].value);
+	uint32_t index;
+	if (!debug_cast_int(cmd->args[0].value, &index)) {
+		fprintf(stderr, "Argument to commands must be an integer\n");
+		return 1;
+	}
+	bp_def **target = find_breakpoint_idx(&root->breakpoints, index);
 	if (!target) {
-		fprintf(stderr, "Breakpoint %d does not exist!\n", cmd->args[0].value);
+		fprintf(stderr, "Breakpoint %d does not exist!\n", index);
 		return 1;
 	}
 	for (uint32_t i = 0; i < (*target)->num_commands; i++)
@@ -1880,12 +2239,12 @@
 
 static uint8_t cmd_if(debug_root *root, parsed_command *cmd)
 {
-	return execute_block(root, cmd->args[0].value ? &cmd->block : &cmd->else_block);
+	return execute_block(root, debug_cast_bool(cmd->args[0].value) ? &cmd->block : &cmd->else_block);
 }
 
 static uint8_t cmd_while(debug_root *root, parsed_command *cmd)
 {
-	if (!cmd->args[0].value) {
+	if (!debug_cast_bool(cmd->args[0].value)) {
 		return execute_block(root, &cmd->else_block);
 	}
 	int debugging = 1;
@@ -1895,7 +2254,7 @@
 			fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw);
 			return 1;
 		}
-	} while (cmd->args[0].value);
+	} while (debug_cast_bool(cmd->args[0].value));
 	return debugging;
 }
 
@@ -1912,98 +2271,133 @@
 {
 	char *name = NULL;
 	char size = 0;
-	uint32_t address;
+	debug_root *set_root = root;
+	expr *set_expr = cmd->args[0].parsed;
+	while (set_expr->type == EXPR_NAMESPACE)
+	{
+		set_root = tern_find_ptr(set_root->other_roots, set_expr->op.v.str);
+		if (!set_root) {
+			fprintf(stderr, "%s is not a valid namespace\n", set_expr->op.v.str);
+			return 1;
+		}
+		set_expr = set_expr->left;
+	}
+	debug_val address;
 	debug_array *array = NULL;
-	switch (cmd->args[0].parsed->type)
+	switch (set_expr->type)
 	{
 	case EXPR_SCALAR:
-		if (cmd->args[0].parsed->op.type == TOKEN_NAME) {
-			name = cmd->args[0].parsed->op.v.str;
+		if (set_expr->op.type == TOKEN_NAME) {
+			name = set_expr->op.v.str;
 		} else {
 			fputs("First argument to set must be a name or memory expression, not a number", stderr);
 			return 1;
 		}
 		break;
 	case EXPR_SIZE:
-		size = cmd->args[0].parsed->op.v.op[0];
-		if (cmd->args[0].parsed->left->op.type == TOKEN_NAME) {
-			name = cmd->args[0].parsed->left->op.v.str;
+		size = set_expr->op.v.op[0];
+		if (set_expr->left->op.type == TOKEN_NAME) {
+			name = set_expr->left->op.v.str;
 		} else {
 			fputs("First argument to set must be a name or memory expression, not a number", stderr);
 			return 1;
 		}
 		break;
 	case EXPR_MEM:
-		size = cmd->args[0].parsed->op.v.op[0];
-		if (!eval_expr(root, cmd->args[0].parsed->left, &address)) {
+		size = set_expr->op.v.op[0];
+		if (!eval_expr(root, set_expr->left, &address)) {
 			fprintf(stderr, "Failed to eval %s\n", cmd->args[0].raw);
 			return 1;
 		}
-		if (cmd->args[0].parsed->right) {
-			array = full_array_resolve(root, cmd->args[0].parsed->right->op.v.str);
+		if (address.type != DBG_VAL_U32) {
+			fprintf(stderr, "Index in array expression must be integer\n");
+			return 1;
+		}
+		if (set_expr->right) {
+			debug_val right;
+			if (!eval_expr(root, set_expr->right, &right)) {
+				return 1;
+			}
+			array = get_array(right);
 			if (!array) {
-				fprintf(stderr, "Failed to resolve array %s\n", cmd->args[0].parsed->right->op.v.str);
+				fprintf(stderr, "%s does not refer to an array\n", cmd->args[0].raw);
 				return 1;
 			}
 			if (!array->set) {
-				fprintf(stderr, "Array %s is read-only\n", cmd->args[0].parsed->right->op.v.str);
+				fprintf(stderr, "Array %s is read-only\n", set_expr->right->op.v.str);
 				return 1;
 			}
-			if (address >= array->size) {
-				fprintf(stderr, "Address %X is out of bounds for array %s\n", address, cmd->args[0].parsed->right->op.v.str);
+			if (address.v.u32 >= array->size) {
+				fprintf(stderr, "Address %X is out of bounds for array %s\n", address.v.u32, set_expr->right->op.v.str);
 				return 1;
 			}
 		}
 		break;
 	default:
-		fprintf(stderr, "First argument to set must be a name or memory expression, got %s\n", expr_type_names[cmd->args[0].parsed->type]);
+		fprintf(stderr, "First argument to set must be a name or memory expression, got %s\n", expr_type_names[set_expr->type]);
 		return 1;
 	}
 	if (!eval_expr(root, cmd->args[1].parsed, &cmd->args[1].value)) {
 		fprintf(stderr, "Failed to eval %s\n", cmd->args[1].raw);
 		return 1;
 	}
-	uint32_t value = cmd->args[1].value;
-	if (name && size && size != 'l') {
-		uint32_t old;
-		if (!root->resolve(root, name, &old)) {
-			fprintf(stderr, "Failed to eval %s\n", name);
+	debug_val value = cmd->args[1].value;
+	if (name) {
+		debug_var *var = tern_find_ptr(set_root->variables, name);
+		if (!var) {
+			fprintf(stderr, "%s is not defined\n", name);
+			return 1;
+		}
+		if (!var->set) {
+			fprintf(stderr, "%s is read-only\n", name);
 			return 1;
 		}
-		if (size == 'b') {
-			old &= 0xFFFFFF00;
-			value &= 0xFF;
-			value |= old;
-		} else {
-			old &= 0xFFFF0000;
-			value &= 0xFFFF;
-			value |= old;
-		}
-	}
-	if (name) {
-		if (!root->set(root, name, value)) {
-			tern_val v;
-			if (tern_find(root->variables, name, &v)) {
-				root->variables = tern_insert_int(root->variables, name, value);
+		if (size && size != 'l') {
+			debug_val old = var->get(var);
+			if (size == 'b') {
+				old.v.u32 &= 0xFFFFFF00;
+				value.v.u32 &= 0xFF;
+				value.v.u32 |= old.v.u32;
 			} else {
-				fprintf(stderr, "Failed to set %s\n", name);
+				old.v.u32 &= 0xFFFF0000;
+				value.v.u32 &= 0xFFFF;
+				value.v.u32 |= old.v.u32;
 			}
 		}
+		var->set(var, value);
 	} else if (array) {
-		array->set(root, array, address, value);
-	} else if (!root->write_mem(root, address, value, size)) {
-		fprintf(stderr, "Failed to write to address %X\n", address);
+		array->set(array, address.v.u32, value);
+	} else if (!root->write_mem(root, address.v.u32, value.v.u32, size)) {
+		fprintf(stderr, "Failed to write to address %X\n", address.v.u32);
 	}
 	return 1;
 }
 
 static uint8_t cmd_variable(debug_root *root, parsed_command *cmd)
 {
-	if (cmd->args[0].parsed->type != EXPR_SCALAR || cmd->args[0].parsed->op.type != TOKEN_NAME) {
-		fprintf(stderr, "First argument to variable must be a name, got %s\n", expr_type_names[cmd->args[0].parsed->type]);
+	debug_root *set_root = root;
+	expr *set_expr = cmd->args[0].parsed;
+	while (set_expr->type == EXPR_NAMESPACE)
+	{
+		set_root = tern_find_ptr(set_root->other_roots, set_expr->op.v.str);
+		if (!set_root) {
+			fprintf(stderr, "%s is not a valid namespace\n", set_expr->op.v.str);
+			return 1;
+		}
+		set_expr = set_expr->left;
+	}
+	if (set_expr->type != EXPR_SCALAR || set_expr->op.type != TOKEN_NAME) {
+		fprintf(stderr, "First argument to variable must be a name, got %s\n", expr_type_names[set_expr->type]);
 		return 1;
 	}
-	uint32_t value = 0;
+	debug_var *var = tern_find_ptr(set_root->variables, set_expr->op.v.str);
+	if (var) {
+		fprintf(stderr, "%s is already defined\n", set_expr->op.v.str);
+		return 1;
+	}
+	debug_val value;
+	value.type = DBG_VAL_U32;
+	value.v.u32 = 0;
 	if (cmd->num_args > 1) {
 		if (!eval_expr(root, cmd->args[1].parsed, &cmd->args[1].value)) {
 			fprintf(stderr, "Failed to eval %s\n", cmd->args[1].raw);
@@ -2011,83 +2405,85 @@
 		}
 		value = cmd->args[1].value;
 	}
-	root->variables = tern_insert_int(root->variables, cmd->args[0].parsed->op.v.str, value);
+	new_user_variable(set_root, set_expr->op.v.str, value);
 	return 1;
 }
 
-static uint32_t user_array_get(debug_root *root, debug_array *array, uint32_t index)
-{
-	return array->data[index];
-}
-
-static void user_array_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
-{
-	array->data[index] = value;
-}
-
-static void user_array_append(debug_root *root, debug_array *array, uint32_t value)
-{
-	if (array->size == array->storage) {
-		array->storage *= 2;
-		array->data = realloc(array->data, sizeof(uint32_t) * array->storage);
-	}
-	array->data[array->size++] = value;
-}
-
 static uint8_t cmd_array(debug_root *root, parsed_command *cmd)
 {
-	if (cmd->args[0].parsed->type != EXPR_SCALAR || cmd->args[0].parsed->op.type != TOKEN_NAME) {
-		fprintf(stderr, "First argument to array must be a name, got %s\n", expr_type_names[cmd->args[0].parsed->type]);
+	debug_root *set_root = root;
+	expr *set_expr = cmd->args[0].parsed;
+	while (set_expr->type == EXPR_NAMESPACE)
+	{
+		set_root = tern_find_ptr(set_root->other_roots, set_expr->op.v.str);
+		if (!set_root) {
+			fprintf(stderr, "%s is not a valid namespace\n", set_expr->op.v.str);
+			return 1;
+		}
+		set_expr = set_expr->left;
+	}
+	if (set_expr->type != EXPR_SCALAR || set_expr->op.type != TOKEN_NAME) {
+		fprintf(stderr, "First argument to array must be a name, got %s\n", expr_type_names[set_expr->type]);
 		return 1;
 	}
-	debug_array *array = tern_find_ptr(root->arrays, cmd->args[0].parsed->op.v.str);
-	if (!array) {
-		array = calloc(1, sizeof(debug_array));
-		array->get = user_array_get;
-		array->set = user_array_set;
-		array->append = user_array_append;
-		array->storage = cmd->num_args > 1 ? cmd->num_args - 1 : 4;
-		array->data = calloc(array->storage, sizeof(uint32_t));
-		root->arrays = tern_insert_ptr(root->arrays, cmd->args[0].parsed->op.v.str, array);
+	debug_var *var = tern_find_ptr(set_root->variables, set_expr->op.v.str);
+	debug_array *array;
+	if (var) {
+		debug_val val = var->get(var);
+		array = get_array(val);
+		if (!array) {
+			fprintf(stderr, "%s is already defined as a non-array value\n", set_expr->op.v.str);
+			return 1;
+		}
+	} else {
+		var = calloc(1, sizeof(debug_var));
+		var->get = user_var_get;
+		var->set = user_var_set;
+		var->val = new_user_array(cmd->num_args - 1);
+		set_root->variables = tern_insert_ptr(set_root->variables, set_expr->op.v.str, var);
+		array = get_array(var->val);
 	}
-	array->size = cmd->num_args - 1;
-	for (uint32_t i = 1; i < cmd->num_args; i++)
+	if (array->set == user_array_set) {
+		array->size = cmd->num_args - 1;
+		if (array->storage < array->size) {
+			array->storage = array->size;
+			array->base = realloc(array->base, sizeof(debug_val) * array->storage);
+		}
+	}
+	for (uint32_t i = 1; i < cmd->num_args && i < array->size; i++)
 	{
 		if (!eval_expr(root, cmd->args[i].parsed, &cmd->args[i].value)) {
 			fprintf(stderr, "Failed to eval %s\n", cmd->args[i].raw);
 			return 1;
 		}
-		array->set(root, array, i - 1, cmd->args[i].value);
+		array->set(array, i - 1, cmd->args[i].value);
 	}
 	return 1;
 }
 
 static uint8_t cmd_append(debug_root *root, parsed_command *cmd)
 {
-	if (cmd->args[0].parsed->type != EXPR_SCALAR || cmd->args[0].parsed->op.type != TOKEN_NAME) {
-		fprintf(stderr, "First argument to append must be a name, got %s\n", expr_type_names[cmd->args[0].parsed->type]);
-		return 1;
-	}
-	debug_array *array = full_array_resolve(root, cmd->args[0].parsed->op.v.str);
+	debug_array *array = get_array(cmd->args[0].value);
 	if (!array) {
-		fprintf(stderr, "Failed to resolve array %s\n", cmd->args[0].parsed->op.v.str);
+		fprintf(stderr, "%s is not an array\n", cmd->args[0].raw);
 		return 1;
 	}
 	if (!array->append) {
-		fprintf(stderr, "Array %s doesn't support appending\n", cmd->args[0].parsed->op.v.str);
+		fprintf(stderr, "Array %s doesn't support appending\n", cmd->args[0].raw);
 		return 1;
 	}
-	if (!eval_expr(root, cmd->args[1].parsed, &cmd->args[1].value)) {
-		fprintf(stderr, "Failed to eval %s\n", cmd->args[1].raw);
-		return 1;
-	}
-	array->append(root, array, cmd->args[1].value);
+	array->append(array, cmd->args[1].value);
 	return 1;
 }
 
 static uint8_t cmd_frames(debug_root *root, parsed_command *cmd)
 {
-	current_system->enter_debugger_frames = cmd->args[0].value;
+	uint32_t frames;
+	if (!debug_cast_int(cmd->args[0].value, &frames)) {
+		fprintf(stderr, "Argument to frames must be an integer\n");
+		return 1;
+	}
+	current_system->enter_debugger_frames = frames;
 	return 0;
 }
 
@@ -2113,9 +2509,14 @@
 		fprintf(stderr, "Failed to evaluate breakpoint number: %s\n", cmd->args[0].raw);
 		return 1;
 	}
-	bp_def **target = find_breakpoint_idx(&root->breakpoints, cmd->args[0].value);
+	uint32_t index;
+	if (!debug_cast_int(cmd->args[0].value, &index)) {
+		fprintf(stderr, "First argument to condition must be an integer\n");
+		return 1;
+	}
+	bp_def **target = find_breakpoint_idx(&root->breakpoints, index);
 	if (!*target) {
-		fprintf(stderr, "Failed to find breakpoint %u\n", cmd->args[0].value);
+		fprintf(stderr, "Failed to find breakpoint %u\n", index);
 		return 1;
 	}
 	free_expr((*target)->condition);
@@ -2186,9 +2587,14 @@
 
 static uint8_t cmd_delete_m68k(debug_root *root, parsed_command *cmd)
 {
-	bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, cmd->args[0].value);
+	uint32_t index;
+	if (!debug_cast_int(cmd->args[0].value, &index)) {
+		fprintf(stderr, "Argument to delete must be an integer\n");
+		return 1;
+	}
+	bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, index);
 	if (!*this_bp) {
-		fprintf(stderr, "Breakpoint %d does not exist\n", cmd->args[0].value);
+		fprintf(stderr, "Breakpoint %d does not exist\n", index);
 		return 1;
 	}
 	bp_def *tmp = *this_bp;
@@ -2209,15 +2615,20 @@
 
 static uint8_t cmd_breakpoint_m68k(debug_root *root, parsed_command *cmd)
 {
-	insert_breakpoint(root->cpu_context, cmd->args[0].value, debugger);
+	uint32_t address;
+	if (!debug_cast_int(cmd->args[0].value, &address)) {
+		fprintf(stderr, "Argument to breakpoint must be an integer\n");
+		return 1;
+	}
+	insert_breakpoint(root->cpu_context, address, debugger);
 	bp_def *new_bp = calloc(1, sizeof(bp_def));
 	new_bp->next = root->breakpoints;
-	new_bp->address = cmd->args[0].value;
+	new_bp->address = address;
 	new_bp->mask = 0xFFFFFF;
 	new_bp->index = root->bp_index++;
 	new_bp->type = BP_TYPE_CPU;
 	root->breakpoints = new_bp;
-	printf("68K Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value);
+	printf("68K Breakpoint %d set at %X\n", new_bp->index, address);
 	return 1;
 }
 
@@ -2233,9 +2644,9 @@
 	int debugging = 1;
 	if (*this_bp) {
 		if ((*this_bp)->condition) {
-			uint32_t condres;
+			debug_val condres;
 			if (eval_expr(root, (*this_bp)->condition, &condres)) {
-				if (!condres) {
+				if (!condres.v.u32) {
 					return;
 				}
 			} else {
@@ -2266,8 +2677,18 @@
 	bp_def *new_bp = calloc(1, sizeof(bp_def));
 	new_bp->next = root->breakpoints;
 	if (cmd->num_args) {
-		new_bp->address = cmd->args[0].value;
-		new_bp->mask = cmd->num_args > 1 ? cmd->args[1].value : 0xFF;
+		if (!debug_cast_int(cmd->args[0].value, &new_bp->address)) {
+			fprintf(stderr, "Arguments to vdpregbreak must be integers if provided\n");
+			return 1;
+		}
+		if (cmd->num_args > 1) {
+			if (!debug_cast_int(cmd->args[1].value, &new_bp->mask)) {
+				fprintf(stderr, "Arguments to vdpregbreak must be integers if provided\n");
+				return 1;
+			}
+		} else {
+			new_bp->mask = 0xFF;
+		}
 	}
 	new_bp->index = root->bp_index++;
 	new_bp->type = BP_TYPE_VDPREG;
@@ -2281,7 +2702,12 @@
 
 static uint8_t cmd_advance_m68k(debug_root *root, parsed_command *cmd)
 {
-	insert_breakpoint(root->cpu_context, cmd->args[0].value, debugger);
+	uint32_t address;
+	if (!debug_cast_int(cmd->args[0].value, &address)) {
+		fprintf(stderr, "Argument to advance must be an integer\n");
+		return 1;
+	}
+	insert_breakpoint(root->cpu_context, address, debugger);
 	return 0;
 }
 
@@ -2417,7 +2843,10 @@
 	m68k_context *context = root->cpu_context;
 	uint32_t address = root->address;
 	if (cmd->num_args) {
-		address = cmd->args[0].value;
+		if (!debug_cast_int(cmd->args[0].value, &address)) {
+			fprintf(stderr, "Argument to disassemble must be an integer if provided\n");
+			return 1;
+		}
 	}
 	char disasm_buf[1024];
 	m68kinst inst;
@@ -2458,7 +2887,7 @@
 	m68k_context *context = root->cpu_context;
 	genesis_context * gen = context->system;
 	if (cmd->num_args) {
-		ym_print_channel_info(gen->ym, cmd->args[0].value - 1);
+		ym_print_channel_info(gen->ym, cmd->args[0].value.v.u32 - 1);
 	} else {
 		for (int i = 0; i < 6; i++) {
 			ym_print_channel_info(gen->ym, i);
@@ -2697,8 +3126,7 @@
 		.desc = "Increase the size of array NAME by 1 and set the last element to VALUE",
 		.impl = cmd_append,
 		.min_args = 2,
-		.max_args = 2,
-		.skip_eval = 1
+		.max_args = 2
 	},
 	{
 		.names = (const char *[]){
@@ -2970,9 +3398,14 @@
 
 static uint8_t cmd_delete_z80(debug_root *root, parsed_command *cmd)
 {
-	bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, cmd->args[0].value);
+	uint32_t index;
+	if (!debug_cast_int(cmd->args[0].value, &index)) {
+		fprintf(stderr, "Argument to delete must be an integer\n");
+		return 1;
+	}
+	bp_def **this_bp = find_breakpoint_idx(&root->breakpoints, index);
 	if (!*this_bp) {
-		fprintf(stderr, "Breakpoint %d does not exist\n", cmd->args[0].value);
+		fprintf(stderr, "Breakpoint %d does not exist\n", index);
 		return 1;
 	}
 	bp_def *tmp = *this_bp;
@@ -2991,21 +3424,31 @@
 
 static uint8_t cmd_breakpoint_z80(debug_root *root, parsed_command *cmd)
 {
-	zinsert_breakpoint(root->cpu_context, cmd->args[0].value, (uint8_t *)zdebugger);
+	uint32_t address;
+	if (!debug_cast_int(cmd->args[0].value, &address)) {
+		fprintf(stderr, "Argument to breakpoint must be an integer\n");
+		return 1;
+	}
+	zinsert_breakpoint(root->cpu_context, address, (uint8_t *)zdebugger);
 	bp_def *new_bp = calloc(1, sizeof(bp_def));
 	new_bp->next = root->breakpoints;
-	new_bp->address = cmd->args[0].value;
+	new_bp->address = address;
 	new_bp->mask = 0xFFFF;
 	new_bp->type = BP_TYPE_CPU;
 	new_bp->index = root->bp_index++;
 	root->breakpoints = new_bp;
-	printf("Z80 Breakpoint %d set at %X\n", new_bp->index, cmd->args[0].value);
+	printf("Z80 Breakpoint %d set at %X\n", new_bp->index, address);
 	return 1;
 }
 
 static uint8_t cmd_advance_z80(debug_root *root, parsed_command *cmd)
 {
-	zinsert_breakpoint(root->cpu_context, cmd->args[0].value, (uint8_t *)zdebugger);
+	uint32_t address;
+	if (!debug_cast_int(cmd->args[0].value, &address)) {
+		fprintf(stderr, "Argument to advance must be an integer\n");
+		return 1;
+	}
+	zinsert_breakpoint(root->cpu_context, address, (uint8_t *)zdebugger);
 	return 0;
 }
 
@@ -3118,7 +3561,10 @@
 	z80_context *context = root->cpu_context;
 	uint32_t address = root->address;
 	if (cmd->num_args) {
-		address = cmd->args[0].value;
+		if (!debug_cast_int(cmd->args[0].value, &address)) {
+			fprintf(stderr, "Argument to disassemble must be an integer if provided\n");
+			return 1;
+		}
 	}
 	char disasm_buf[1024];
 	z80inst inst;
@@ -3332,6 +3778,12 @@
 	}
 }
 
+static debug_val debug_frame_get(debug_var *var)
+{
+	vdp_context *vdp = var->ptr;
+	return debug_int(vdp->frame);
+}
+
 debug_root *find_m68k_root(m68k_context *context)
 {
 	debug_root *root = find_root(context);
@@ -3340,29 +3792,40 @@
 		add_commands(root, m68k_commands, NUM_68K);
 		root->read_mem = read_m68k;
 		root->write_mem = write_m68k;
-		root->set = set_m68k;
 		root->disasm = create_68000_disasm();
+		m68k_names(root);
+		debug_var *var;
 		switch (current_system->type)
 		{
 		case SYSTEM_GENESIS:
 		case SYSTEM_SEGACD:
 			//check if this is the main CPU
 			if (context->system == current_system) {
-				root->resolve = resolve_genesis;
-				root->array_resolve = resolve_genesis_array;
+				genesis_context *gen = context->system;
+				root->other_roots = tern_insert_ptr(root->other_roots, "z80", find_z80_root(gen->z80));
+				root->other_roots = tern_insert_ptr(root->other_roots, "vdp", find_vdp_root(gen->vdp));
+				root->other_roots = tern_insert_ptr(root->other_roots, "ym", find_ym2612_root(gen->ym));
+				root->other_roots = tern_insert_ptr(root->other_roots, "psg", find_psg_root(gen->psg));
 				add_commands(root, genesis_commands, NUM_GENESIS);
+				var = calloc(1, sizeof(debug_var));
+				var->get = debug_frame_get;
+				var->ptr = gen->vdp;
+				root->variables = tern_insert_ptr(root->variables, "frame", var);
 				if (current_system->type == SYSTEM_SEGACD) {
 					add_segacd_maincpu_labels(root->disasm);
 					add_commands(root, scd_main_commands, NUM_SCD_MAIN);
+					segacd_context *scd = gen->expansion;
+					root->other_roots = tern_insert_ptr(root->other_roots, "sub", find_m68k_root(scd->m68k));
 				}
 				break;
 			} else {
 				add_segacd_subcpu_labels(root->disasm);
 				add_commands(root, scd_sub_commands, NUM_SCD_SUB);
+				segacd_context *scd = context->system;
+				root->other_roots = tern_insert_ptr(root->other_roots, "main", find_m68k_root(scd->genesis->m68k));
 			}
 		default:
-			root->resolve = resolve_m68k;
-			root->array_resolve = resolve_null_array;
+			break;
 		}
 		tern_foreach(root->disasm->labels, symbol_map, root);
 	}
@@ -3392,473 +3855,321 @@
 	return 1;
 }
 
-static uint8_t resolve_z80(debug_root *root, const char *name, uint32_t *out)
+static debug_val z80_reg8_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->regs[var->val.v.u32]);
+}
+
+static void z80_reg8_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register %s can only be set to an integer\n", z80_regs[var->val.v.u32]);
+		return;
+	}
+	context->regs[var->val.v.u32] = ival;
+}
+
+
+static debug_val z80_alt_reg8_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->alt_regs[var->val.v.u32]);
+}
+
+static void z80_alt_reg8_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register %s' can only be set to an integer\n", z80_regs[var->val.v.u32]);
+		return;
+	}
+	context->alt_regs[var->val.v.u32] = ival;
+}
+
+static debug_val z80_flags_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	debug_val ret;
+	ret.type = DBG_VAL_U32;
+	ret.v.u32 = context->flags[ZF_S] << 7;
+	ret.v.u32 |= context->flags[ZF_Z] << 6;
+	ret.v.u32 |= context->flags[ZF_H] << 4;
+	ret.v.u32 |= context->flags[ZF_PV] << 2;
+	ret.v.u32 |= context->flags[ZF_N] << 1;
+	ret.v.u32 |= context->flags[ZF_C];
+	return ret;
+}
+
+static debug_val z80_alt_flags_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	debug_val ret;
+	ret.type = DBG_VAL_U32;
+	ret.v.u32 = context->alt_flags[ZF_S] << 7;
+	ret.v.u32 |= context->alt_flags[ZF_Z] << 6;
+	ret.v.u32 |= context->alt_flags[ZF_H] << 4;
+	ret.v.u32 |= context->alt_flags[ZF_PV] << 2;
+	ret.v.u32 |= context->alt_flags[ZF_N] << 1;
+	ret.v.u32 |= context->alt_flags[ZF_C];
+	return ret;
+}
+
+static void z80_flags_set(debug_var *var, debug_val val)
 {
-	z80_context *context = root->cpu_context;
-	switch (tolower(name[0]))
-	{
-	case 'a':
-		if (!name[1]) {
-			*out = context->regs[Z80_A];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_A];
-		} else if (tolower(name[1]) == 'f') {
-			if (!name[2]) {
-				*out = context->regs[Z80_A] << 8;
-				*out |= context->flags[ZF_S] << 7;
-				*out |= context->flags[ZF_Z] << 6;
-				*out |= context->flags[ZF_H] << 4;
-				*out |= context->flags[ZF_PV] << 2;
-				*out |= context->flags[ZF_N] << 1;
-				*out |= context->flags[ZF_C];
-			} else if (name[2] == '\'' && !name[3]) {
-				*out = context->alt_regs[Z80_A] << 8;
-				*out |= context->alt_flags[ZF_S] << 7;
-				*out |= context->alt_flags[ZF_Z] << 6;
-				*out |= context->alt_flags[ZF_H] << 4;
-				*out |= context->alt_flags[ZF_PV] << 2;
-				*out |= context->alt_flags[ZF_N] << 1;
-				*out |= context->alt_flags[ZF_C];
-			} else {
-				return 0;
-			}
-		} else {
-			return 0;
-		}
-		break;
-	case 'b':
-		if (!name[1]) {
-			*out = context->regs[Z80_B];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_B];
-		} else if (tolower(name[1]) == 'c') {
-			if (!name[2]) {
-				*out = context->regs[Z80_B] << 8 | context->regs[Z80_C];
-			} else if (name[2] == '\'' && !name[3]) {
-				*out = context->alt_regs[Z80_B] << 8 | context->alt_regs[Z80_C];
-			} else {
-				return 0;
-			}
-		}
-		break;
-	case 'c':
-		if (!name[1]) {
-			*out = context->regs[Z80_C];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_C];
-		} else if (!strcmp(name + 1, "ycle")) {
-			*out = context->current_cycle;
-		} else {
-			return 0;
-		}
-		break;
-	case 'd':
-		if (!name[1]) {
-			*out = context->regs[Z80_D];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_D];
-		} else if (tolower(name[1]) == 'e') {
-			if (!name[2]) {
-				*out = context->regs[Z80_D] << 8 | context->regs[Z80_E];
-			} else if (name[2] == '\'' && !name[3]) {
-				*out = context->alt_regs[Z80_D] << 8 | context->alt_regs[Z80_E];
-			} else {
-				return 0;
-			}
-		}
-		break;
-	case 'e':
-		if (!name[1]) {
-			*out = context->regs[Z80_E];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_E];
-		} else {
-			return 0;
-		}
-		break;
-	case 'f':
-		if (!name[1]) {
-			*out = context->flags[ZF_S] << 7;
-			*out |= context->flags[ZF_Z] << 6;
-			*out |= context->flags[ZF_H] << 4;
-			*out |= context->flags[ZF_PV] << 2;
-			*out |= context->flags[ZF_N] << 1;
-			*out |= context->flags[ZF_C];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_flags[ZF_S] << 7;
-			*out |= context->alt_flags[ZF_Z] << 6;
-			*out |= context->alt_flags[ZF_H] << 4;
-			*out |= context->alt_flags[ZF_PV] << 2;
-			*out |= context->alt_flags[ZF_N] << 1;
-			*out |= context->alt_flags[ZF_C];
-		} else {
-			return 0;
-		}
-		break;
-	case 'h':
-		if (!name[1]) {
-			*out = context->regs[Z80_H];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_H];
-		} else if (tolower(name[1]) == 'l') {
-			if (!name[2]) {
-				*out = context->regs[Z80_H] << 8 | context->regs[Z80_L];
-			} else if (name[2] == '\'' && !name[3]) {
-				*out = context->alt_regs[Z80_H] << 8 | context->alt_regs[Z80_L];
-			} else {
-				return 0;
-			}
-		}
-		break;
-	case 'i':
-		switch (tolower(name[1]))
-		{
-		case 0:
-			*out = context->regs[Z80_I];
-			break;
-		case 'f':
-			if (name[2] != 'f' || name[3] < '1' || name[4]) {
-				return 0;
-			}
-			if (name[3] == '1') {
-				*out = context->iff1;
-			} else if (name[3] == '2') {
-				*out = context->iff2;
-			} else {
-				return 0;
-			}
-			break;
-		case 'm':
-			if (name[2]) {
-				return 0;
-			}
-			*out = context->im;
-			break;
-		case 'n':
-			if (strcasecmp(name +2, "t_cycle")) {
-				return 0;
-			}
-			*out = context->int_cycle;
-			break;
-		case 'r':
-			if (name[2]) {
-				return 0;
-			}
-			*out = context->regs[Z80_I] << 8 | context->regs[Z80_R];
-			break;
-		case 'x':
-			switch (tolower(name[2]))
-			{
-			case 0:
-				*out = context->regs[Z80_IXH] << 8 | context->regs[Z80_IXL];
-				break;
-			case 'h':
-				if (name[3]) {
-					return 0;
-				}
-				*out = context->regs[Z80_IXH];
-			case 'l':
-				if (name[3]) {
-					return 0;
-				}
-				*out = context->regs[Z80_IXL];
-			default:
-				return 0;
-			}
-			break;
-		case 'y':
-			switch (tolower(name[2]))
-			{
-			case 0:
-				*out = context->regs[Z80_IYH] << 8 | context->regs[Z80_IYL];
-				break;
-			case 'h':
-				if (name[3]) {
-					return 0;
-				}
-				*out = context->regs[Z80_IYH];
-			case 'l':
-				if (name[3]) {
-					return 0;
-				}
-				*out = context->regs[Z80_IYL];
-			default:
-				return 0;
-			}
-			break;
-		default:
-			return 0;
-		}
-		break;
-	case 'l':
-		if (!name[1]) {
-			*out = context->regs[Z80_L];
-		} else if (name[1] == '\'' && !name[2]) {
-			*out = context->alt_regs[Z80_L];
-		} else {
-			return 0;
-		}
-		break;
-	case 'p':
-		if (tolower(name[1]) != 'c' || name[2]) {
-			return 0;
-		}
-		*out = root->address;
-		break;
-	case 'r':
-		if (name[1]) {
-			return 0;
-		}
-		*out = context->regs[Z80_R];
-	case 's':
-		if (tolower(name[1]) != 'p' || name[2]) {
-			return 0;
-		}
-		*out = context->sp;
-		break;
-	default:
-		return 0;
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register F can only be set to an integer\n");
+		return;
+	}
+	context->flags[ZF_S] = ival >> 7 & 1;
+	context->flags[ZF_Z] = ival >> 6 & 1;
+	context->flags[ZF_H] = ival >> 4 & 1;
+	context->flags[ZF_PV] = ival >> 2 & 1;
+	context->flags[ZF_N] = ival >> 1 & 1;
+	context->flags[ZF_C] = ival & 1;
+}
+
+static void z80_alt_flags_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register F' can only be set to an integer\n");
+		return;
+	}
+	context->alt_flags[ZF_S] = ival >> 7 & 1;
+	context->alt_flags[ZF_Z] = ival >> 6 & 1;
+	context->alt_flags[ZF_H] = ival >> 4 & 1;
+	context->alt_flags[ZF_PV] = ival >> 2 & 1;
+	context->alt_flags[ZF_N] = ival >> 1 & 1;
+	context->alt_flags[ZF_C] = ival & 1;
+}
+
+static debug_val z80_regpair_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	debug_val ret;
+	if (var->val.v.u32 == Z80_AF) {
+		ret = z80_flags_get(var);
+		ret.v.u32 |= context->regs[Z80_A] << 8;
+	} else {
+		ret = debug_int(context->regs[z80_high_reg(var->val.v.u32)] << 8 | context->regs[z80_low_reg(var->val.v.u32)]);
 	}
-	return 1;
+	return ret;
+}
+
+static void z80_regpair_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register %s can only be set to an integer\n", z80_regs[var->val.v.u32]);
+		return;
+	}
+	if (var->val.v.u32 == Z80_AF) {
+		context->regs[Z80_A] = ival >> 8;
+		z80_flags_set(var, val);
+	} else {
+		context->regs[z80_high_reg(var->val.v.u32)] = ival >> 8;
+		context->regs[z80_low_reg(var->val.v.u32)] = ival;
+	}
+}
+
+static debug_val z80_alt_regpair_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	debug_val ret;
+	if (var->val.v.u32 == Z80_AF) {
+		ret = z80_alt_flags_get(var);
+		ret.v.u32 |= context->alt_regs[Z80_A] << 8;
+	} else {
+		ret = debug_int(context->alt_regs[z80_high_reg(var->val.v.u32)] << 8 | context->alt_regs[z80_low_reg(var->val.v.u32)]);
+	}
+	return ret;
+}
+
+static void z80_alt_regpair_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register %s' can only be set to an integer\n", z80_regs[var->val.v.u32]);
+		return;
+	}
+	if (var->val.v.u32 == Z80_AF) {
+		context->regs[Z80_A] = ival >> 8;
+		z80_alt_flags_set(var, val);
+	} else {
+		context->alt_regs[z80_high_reg(var->val.v.u32)] = ival >> 8;
+		context->alt_regs[z80_low_reg(var->val.v.u32)] = ival;
+	}
 }
 
-static uint8_t resolve_sms(debug_root *root, const char *name, uint32_t *out)
+static debug_val z80_sp_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->sp);
+}
+
+static void z80_sp_set(debug_var *var, debug_val val)
 {
-	if (resolve_z80(root, name, out)) {
-		return 1;
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register sp can only be set to an integer\n");
+		return;
+	}
+	context->sp = ival;
+}
+
+static debug_val z80_im_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->im);
+}
+
+static void z80_im_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	uint32_t ival;
+	if (!debug_cast_int(val, &ival)) {
+		fprintf(stderr, "Z80 register im can only be set to an integer\n");
+		return;
 	}
-	z80_context *z80 = root->cpu_context;
-	sms_context *sms = z80->system;
-	if (!strcmp(name, "f") || !strcmp(name, "frame")) {
-		*out = sms->vdp->frame;
-		return 1;
-	}
-	return 0;
+	context->im = ival & 3;
+}
+
+static debug_val z80_iff1_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->iff1);
+}
+
+static void z80_iff1_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	context->iff1 = debug_cast_bool(val);
+}
+
+static debug_val z80_iff2_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->iff2);
+}
+
+static void z80_iff2_set(debug_var *var, debug_val val)
+{
+	z80_context *context = var->ptr;
+	context->iff2 = debug_cast_bool(val);
+}
+
+static debug_val z80_cycle_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->current_cycle);
+}
+
+static debug_val z80_pc_get(debug_var *var)
+{
+	z80_context *context = var->ptr;
+	return debug_int(context->pc);
 }
 
-static uint8_t set_z80(debug_root *root, const char *name, uint32_t value)
+static void z80_names(debug_root *root)
 {
-	z80_context *context = root->cpu_context;
-	switch (tolower(name[0]))
+	debug_var *var;
+	for (int i = 0; i < Z80_UNUSED; i++)
 	{
-	case 'a':
-		if (!name[1]) {
-			context->regs[Z80_A] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_A] = value;
-		} else if (tolower(name[1]) == 'f') {
-			if (!name[2]) {
-				context->regs[Z80_A] = value >> 8;
-				context->flags[ZF_S] = value >> 7 & 1;
-				context->flags[ZF_Z] = value >> 6 & 1;
-				context->flags[ZF_H] = value >> 4 & 1;
-				context->flags[ZF_PV] = value >> 2 & 1;
-				context->flags[ZF_N] = value >> 1 & 1;
-				context->flags[ZF_C] = value & 1;
-			} else if (name[2] == '\'' && !name[3]) {
-				context->alt_regs[Z80_A] = value >> 8;
-				context->alt_flags[ZF_S] = value >> 7 & 1;
-				context->alt_flags[ZF_Z] = value >> 6 & 1;
-				context->alt_flags[ZF_H] = value >> 4 & 1;
-				context->alt_flags[ZF_PV] = value >> 2 & 1;
-				context->alt_flags[ZF_N] = value >> 1 & 1;
-				context->alt_flags[ZF_C] = value & 1;
-			} else {
-				return 0;
-			}
+		var = calloc(1, sizeof(debug_var));
+		var->ptr = root->cpu_context;
+		if (i < Z80_BC) {
+			var->get = z80_reg8_get;
+			var->set = z80_reg8_set;
+		} else if (i == Z80_SP) {
+			var->get = z80_sp_get;
+			var->set = z80_sp_set;
 		} else {
-			return 0;
-		}
-		break;
-	case 'b':
-		if (!name[1]) {
-			context->regs[Z80_B] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_B] = value;
-		} else if (tolower(name[1]) == 'c') {
-			if (!name[2]) {
-				context->regs[Z80_B] = value >> 8;
-				context->regs[Z80_C] = value;
-			} else if (name[2] == '\'' && !name[3]) {
-				context->alt_regs[Z80_B] = value >> 8;
-				context->alt_regs[Z80_C] = value;
-			} else {
-				return 0;
-			}
+			var->get = z80_regpair_get;
+			var->set = z80_regpair_set;
 		}
-		break;
-	case 'c':
-		if (!name[1]) {
-			context->regs[Z80_C] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_C] = value;
-		} else {
-			return 0;
-		}
-		break;
-	case 'd':
-		if (!name[1]) {
-			context->regs[Z80_D] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_D] = value;
-		} else if (tolower(name[1]) == 'e') {
-			if (!name[2]) {
-				context->regs[Z80_D] = value >> 8;
-				context->regs[Z80_E] = value;
-			} else if (name[2] == '\'' && !name[3]) {
-				context->alt_regs[Z80_D] = value >> 8;
-				context->alt_regs[Z80_E] = value;
-			} else {
-				return 0;
-			}
-		}
-		break;
-	case 'e':
-		if (!name[1]) {
-			context->regs[Z80_E] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_E] = value;
-		} else {
-			return 0;
-		}
-		break;
-	case 'f':
-		if (!name[1]) {
-			context->flags[ZF_S] = value >> 7 & 1;
-			context->flags[ZF_Z] = value >> 6 & 1;
-			context->flags[ZF_H] = value >> 4 & 1;
-			context->flags[ZF_PV] = value >> 2 & 1;
-			context->flags[ZF_N] = value >> 1 & 1;
-			context->flags[ZF_C] = value & 1;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_flags[ZF_S] = value >> 7 & 1;
-			context->alt_flags[ZF_Z] = value >> 6 & 1;
-			context->alt_flags[ZF_H] = value >> 4 & 1;
-			context->alt_flags[ZF_PV] = value >> 2 & 1;
-			context->alt_flags[ZF_N] = value >> 1 & 1;
-			context->alt_flags[ZF_C] = value & 1;
-		} else {
-			return 0;
+		var->val.v.u32 = i;
+		root->variables = tern_insert_ptr(root->variables, z80_regs[i], var);
+		size_t name_size = strlen(z80_regs[i]);
+		char *name = malloc(name_size + 2);
+		char *d = name;
+		for (const char *c = z80_regs[i]; *c; c++, d++)
+		{
+			*d = toupper(*c);
 		}
-		break;
-	case 'h':
-		if (!name[1]) {
-			context->regs[Z80_H] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_H] = value;
-		} else if (tolower(name[1]) == 'e') {
-			if (!name[2]) {
-				context->regs[Z80_H] = value >> 8;
-				context->regs[Z80_L] = value;
-			} else if (name[2] == '\'' && !name[3]) {
-				context->alt_regs[Z80_H] = value >> 8;
-				context->alt_regs[Z80_L] = value;
+		name[name_size] = 0;
+		root->variables = tern_insert_ptr(root->variables, name, var);
+
+		if (i < Z80_IXL || (i > Z80_R && i < Z80_IX && i != Z80_SP)) {
+			memcpy(name, z80_regs[i], name_size);
+			name[name_size] = '\'';
+			name[name_size + 1] = 0;
+			var = calloc(1, sizeof(debug_var));
+			var->ptr = root->cpu_context;
+			if (i < Z80_BC) {
+				var->get = z80_alt_reg8_get;
+				var->set = z80_alt_reg8_set;
 			} else {
-				return 0;
+				var->get = z80_alt_regpair_get;
+				var->set = z80_alt_regpair_set;
 			}
-		}
-		break;
-	case 'i':
-		switch (tolower(name[1]))
-		{
-		case 0:
-			context->regs[Z80_I] = value;
-			break;
-		case 'f':
-			if (name[2] != 'f' || name[3] < '1' || name[4]) {
-				return 0;
-			}
-			if (name[3] == '1') {
-				context->iff1 = value != 0;
-			} else if (name[3] == '2') {
-				context->iff2 = value != 0;
-			} else {
-				return 0;
-			}
-			break;
-		case 'm':
-			if (name[2]) {
-				return 0;
-			}
-			context->im = value & 3;
-			break;
-		case 'r':
-			if (name[2]) {
-				return 0;
-			}
-			context->regs[Z80_I] = value >> 8;
-			context->regs[Z80_R] = value;
-			break;
-		case 'x':
-			switch (tolower(name[2]))
+			var->val.v.u32 = i;
+			root->variables = tern_insert_ptr(root->variables, name, var);
+			d = name;
+			for (const char *c = z80_regs[i]; *c; c++, d++)
 			{
-			case 0:
-				context->regs[Z80_IXH] = value >> 8;
-				context->regs[Z80_IXL] = value;
-				break;
-			case 'h':
-				if (name[3]) {
-					return 0;
-				}
-				context->regs[Z80_IXH] = value;
-			case 'l':
-				if (name[3]) {
-					return 0;
-				}
-				context->regs[Z80_IXL] = value;
-			default:
-				return 0;
+				*d = toupper(*c);
 			}
-			break;
-		case 'y':
-			switch (tolower(name[2]))
-			{
-			case 0:
-				context->regs[Z80_IYH] = value >> 8;
-				context->regs[Z80_IYL] = value;
-				break;
-			case 'h':
-				if (name[3]) {
-					return 0;
-				}
-				context->regs[Z80_IYH] = value;
-			case 'l':
-				if (name[3]) {
-					return 0;
-				}
-				context->regs[Z80_IYL] = value;
-			default:
-				return 0;
-			}
-			break;
-		default:
-			return 0;
+			root->variables = tern_insert_ptr(root->variables, name, var);
 		}
-		break;
-	case 'l':
-		if (!name[1]) {
-			context->regs[Z80_L] = value;
-		} else if (name[1] == '\'' && !name[2]) {
-			context->alt_regs[Z80_L] = value;
-		} else {
-			return 0;
-		}
-		break;
-	case 'r':
-		if (name[1]) {
-			return 0;
-		}
-		context->regs[Z80_R] = value;
-	case 's':
-		if (tolower(name[1]) != 'p' || name[2]) {
-			return 0;
-		}
-		context->sp = value;
-		break;
-	default:
-		return 0;
+		free(name);
 	}
-	return 1;
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_flags_get;
+	var->set = z80_flags_set;
+	root->variables = tern_insert_ptr(root->variables, "f", var);
+	root->variables = tern_insert_ptr(root->variables, "F", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_alt_flags_get;
+	var->set = z80_alt_flags_set;
+	root->variables = tern_insert_ptr(root->variables, "f'", var);
+	root->variables = tern_insert_ptr(root->variables, "F'", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_im_get;
+	var->set = z80_im_set;
+	root->variables = tern_insert_ptr(root->variables, "im", var);
+	root->variables = tern_insert_ptr(root->variables, "IM", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_iff1_get;
+	var->set = z80_iff1_set;
+	root->variables = tern_insert_ptr(root->variables, "iff1", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_iff2_get;
+	var->set = z80_iff2_set;
+	root->variables = tern_insert_ptr(root->variables, "iff2", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_cycle_get;
+	root->variables = tern_insert_ptr(root->variables, "cycle", var);
+	var = calloc(1, sizeof(debug_var));
+	var->ptr = root->cpu_context;
+	var->get = z80_pc_get;
+	root->variables = tern_insert_ptr(root->variables, "pc", var);
+	root->variables = tern_insert_ptr(root->variables, "PC", var);
 }
 
 debug_root *find_z80_root(z80_context *context)
@@ -3867,24 +4178,35 @@
 	if (root && !root->commands) {
 		add_commands(root, common_commands, NUM_COMMON);
 		add_commands(root, z80_commands, NUM_Z80);
+		z80_names(root);
+		genesis_context *gen;
+		sms_context *sms;
+		debug_var *var;
+		//TODO: populate names
 		switch (current_system->type)
 		{
 		case SYSTEM_GENESIS:
 		case SYSTEM_SEGACD:
+			gen = context->system;
 			add_commands(root, gen_z80_commands, NUM_GEN_Z80);
-			root->resolve = resolve_z80;
+			root->other_roots = tern_insert_ptr(root->other_roots, "m68k", find_m68k_root(gen->m68k));
+			//root->resolve = resolve_z80;
 			break;
 		case SYSTEM_SMS:
-			root->resolve = resolve_sms;
+			sms = context->system;
 			add_commands(root, sms_commands, NUM_SMS);
+			root->other_roots = tern_insert_ptr(root->other_roots, "vdp", find_vdp_root(sms->vdp));
+			root->other_roots = tern_insert_ptr(root->other_roots, "psg", find_psg_root(sms->psg));
+			var = calloc(1, sizeof(debug_var));
+			var->get = debug_frame_get;
+			var->ptr = sms->vdp;
+			root->variables = tern_insert_ptr(root->variables, "frame", var);
 			break;
-		default:
-			root->resolve = resolve_z80;
+		//default:
+			//root->resolve = resolve_z80;
 		}
-		root->array_resolve = resolve_null_array;
 		root->read_mem = read_z80;
 		root->write_mem = write_z80;
-		root->set = set_z80;
 		root->disasm = create_z80_disasm();
 	}
 	return root;
@@ -3906,9 +4228,9 @@
 	bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU);
 	if (*this_bp) {
 		if ((*this_bp)->condition) {
-			uint32_t condres;
+			debug_val condres;
 			if (eval_expr(root, (*this_bp)->condition, &condres)) {
-				if (!condres) {
+				if (!condres.v.u32) {
 					return context;
 				}
 			} else {
@@ -3995,9 +4317,9 @@
 	bp_def ** this_bp = find_breakpoint(&root->breakpoints, address, BP_TYPE_CPU);
 	if (*this_bp) {
 		if ((*this_bp)->condition) {
-			uint32_t condres;
+			debug_val condres;
 			if (eval_expr(root, (*this_bp)->condition, &condres)) {
-				if (!condres) {
+				if (!condres.v.u32) {
 					return;
 				}
 			} else {
--- a/debug.h	Mon Oct 30 00:07:56 2023 -0700
+++ b/debug.h	Mon Nov 06 22:41:33 2023 -0800
@@ -39,7 +39,8 @@
 	EXPR_UNARY,
 	EXPR_BINARY,
 	EXPR_SIZE,
-	EXPR_MEM
+	EXPR_MEM,
+	EXPR_NAMESPACE
 } expr_type;
 
 typedef struct expr expr;
@@ -51,10 +52,26 @@
 	token     op;
 };
 
+enum {
+	DBG_VAL_U32,
+	DBG_VAL_F32,
+	DBG_VAL_ARRAY,
+	DBG_VAL_STRING,
+	DBG_VAL_FUNC
+};
+
 typedef struct {
-	char     *raw;
-	expr     *parsed;
-	uint32_t value;
+	union {
+		uint32_t u32;
+		float    f32;
+	} v;
+	uint32_t type;
+} debug_val;
+
+typedef struct {
+	char      *raw;
+	expr      *parsed;
+	debug_val value;
 } command_arg;
 
 typedef struct debug_root debug_root;
@@ -117,22 +134,31 @@
 } bp_def;
 
 typedef struct debug_array debug_array;
-typedef uint32_t (*debug_array_get)(debug_root *root, debug_array *array, uint32_t index);
-typedef void (*debug_array_set)(debug_root *root, debug_array *array, uint32_t index, uint32_t value);
-typedef void (*debug_array_append)(debug_root *root, debug_array *array, uint32_t value);
+typedef debug_val (*debug_array_get)(debug_array *array, uint32_t index);
+typedef void (*debug_array_set)(debug_array *array, uint32_t index, debug_val value);
+typedef void (*debug_array_append)(debug_array *array, debug_val value);
 
 struct debug_array{
 	debug_array_get    get;
 	debug_array_set    set;
 	debug_array_append append;
-	uint32_t           *data;
+	void               *base;
 	uint32_t           size;
 	uint32_t           storage;
 };
 
-typedef uint8_t (*resolver)(debug_root *root, const char *name, uint32_t *out);
-typedef debug_array* (*array_resolver)(debug_root *root, const char *name);
-typedef uint8_t (*setter)(debug_root *root, const char *name, uint32_t value);
+typedef struct debug_var debug_var;
+typedef debug_val (*debug_var_get)(debug_var *var);
+typedef void (*debug_var_set)(debug_var *var, debug_val val);
+
+struct debug_var {
+	debug_var_get get;
+	debug_var_set set;
+	void          *ptr;
+	debug_val     val;
+};
+
+typedef debug_var *(*resolver)(debug_root *root, const char *name);
 typedef uint8_t (*reader)(debug_root *root, uint32_t *out, char size);
 typedef uint8_t (*writer)(debug_root *root, uint32_t address, uint32_t value, char size);
 
@@ -141,14 +167,11 @@
 	bp_def         *breakpoints;
 	disp_def       *displays;
 	tern_node      *commands;
+	tern_node      *variables;
 	tern_node      *symbols;
-	tern_node      *variables;
-	tern_node      *arrays;
+	tern_node      *other_roots;
 	disasm_context *disasm;
-	resolver       resolve;
-	array_resolver array_resolve;
 	reader         read_mem;
-	setter         set;
 	writer         write_mem;
 	parsed_command last_cmd;
 	uint32_t       bp_index;
--- a/z80inst.c	Mon Oct 30 00:07:56 2023 -0700
+++ b/z80inst.c	Mon Nov 06 22:41:33 2023 -0800
@@ -1279,7 +1279,7 @@
 			decoded->ea_reg = *(++istream);
 		}
 	}
-	
+
 	if ((decoded->addr_mode & 0x1F) == Z80_IMMED && decoded->op != Z80_RST && decoded->op != Z80_IM) {
 		decoded->immed = *(++istream);
 		if ((decoded->reg >= Z80_BC && decoded->reg < Z80_UNUSED) || decoded->op == Z80_CALL || decoded->op == Z80_CALLCC || decoded->op == Z80_JP || decoded->op == Z80_JPCC) {
@@ -1370,7 +1370,7 @@
 	"otdr"
 };
 
-char * z80_regs[Z80_USE_IMMED] = {
+const char *z80_regs[Z80_USE_IMMED] = {
 	"c",
 	"b",
 	"e",
--- a/z80inst.h	Mon Oct 30 00:07:56 2023 -0700
+++ b/z80inst.h	Mon Nov 06 22:41:33 2023 -0800
@@ -1,6 +1,6 @@
 /*
  Copyright 2013 Michael Pavone
- This file is part of BlastEm. 
+ This file is part of BlastEm.
  BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
 */
 #ifndef Z80INST_H_
@@ -137,6 +137,8 @@
 	uint16_t  opcode_bytes;
 } z80inst;
 
+extern const char *z80_regs[Z80_USE_IMMED];
+
 uint8_t * z80_decode(uint8_t * istream, z80inst * decoded);
 int z80_disasm(z80inst * decoded, char * dst, uint16_t address);
 uint8_t z80_high_reg(uint8_t reg);