changeset 2365:8c060849a503

Basic function call support in debug language
author Michael Pavone <pavone@retrodev.com>
date Wed, 08 Nov 2023 23:47:26 -0800
parents c822bb628fc3
children 1e36d8a2633c
files debug.c debug.h
diffstat 2 files changed, 172 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
--- a/debug.c	Wed Nov 08 00:09:33 2023 -0800
+++ b/debug.c	Wed Nov 08 23:47:26 2023 -0800
@@ -24,47 +24,59 @@
 #define Z80_OPTS options
 #endif
 
-static debug_root *roots;
-static uint32_t num_roots, root_storage;
-
-debug_root *find_root(void *cpu)
+static debug_func *funcs;
+static uint32_t num_funcs, func_storage;
+
+static debug_func* alloc_func(void)
 {
-	for (uint32_t i = 0; i < num_roots; i++)
-	{
-		if (roots[i].cpu_context == cpu) {
-			return roots + i;
-		}
+	if (num_funcs == func_storage) {
+		func_storage = func_storage ? func_storage * 2 : 4;
+		funcs = realloc(funcs, sizeof(debug_func) * func_storage);
 	}
-	if (num_roots == root_storage) {
-		root_storage = root_storage ? root_storage * 2 : 5;
-		roots = realloc(roots, root_storage * sizeof(debug_root));
-	}
-	num_roots++;
-	memset(roots + num_roots - 1, 0, sizeof(debug_root));
-	roots[num_roots-1].cpu_context = cpu;
-	return roots + num_roots - 1;
+	return funcs + num_funcs++;
+}
+
+static debug_val new_native_func(debug_native_func impl, int max_args, int min_args)
+{
+	debug_func *f = alloc_func();
+	f->impl.native = impl;
+	f->max_args = max_args;
+	f->min_args = min_args;
+	f->is_native = 1;
+	return (debug_val) {
+		.v = {
+			.u32 = f - funcs
+		},
+		.type = DBG_VAL_FUNC
+	};
 }
 
-bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type)
+debug_val user_var_get(debug_var *var)
 {
-	while (*cur) {
-		if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) {
-			break;
-		}
-		cur = &((*cur)->next);
-	}
-	return cur;
+	return var->val;
+}
+
+void user_var_set(debug_var *var, debug_val val)
+{
+	var->val = val;
 }
 
-bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index)
+static void new_user_variable(debug_root *root, const char *name, debug_val val)
 {
-	while (*cur) {
-		if ((*cur)->index == index) {
-			break;
-		}
-		cur = &((*cur)->next);
-	}
-	return cur;
+	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_array *arrays;
@@ -136,32 +148,41 @@
 	return arrays + val.v.u32;
 }
 
-debug_val user_var_get(debug_var *var)
+static uint8_t debug_cast_int(debug_val val, uint32_t *out)
 {
-	return var->val;
+	if (val.type == DBG_VAL_U32) {
+		*out = val.v.u32;
+		return 1;
+	}
+	if (val.type == DBG_VAL_F32) {
+		*out = val.v.f32;
+		return 1;
+	}
+	return 0;
 }
 
-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)
+static uint8_t debug_cast_float(debug_val val, float *out)
 {
-	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);
+	if (val.type == DBG_VAL_U32) {
+		*out = val.v.u32;
+		return 1;
+	}
+	if (val.type == DBG_VAL_F32) {
+		*out = val.v.f32;
+		return 1;
+	}
+	return 0;
 }
 
-static void new_readonly_variable(debug_root *root, const char *name, debug_val val)
+static uint8_t debug_cast_bool(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);
+	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 debug_int(uint32_t i)
@@ -182,6 +203,59 @@
 	};
 }
 
+debug_val debug_sin(debug_val *args, int num_args)
+{
+	float f;
+	if (!debug_cast_float(args[0], &f)) {
+		return debug_float(0.0f);
+	}
+	return debug_float(sinf(f));
+}
+
+static debug_root *roots;
+static uint32_t num_roots, root_storage;
+
+debug_root *find_root(void *cpu)
+{
+	for (uint32_t i = 0; i < num_roots; i++)
+	{
+		if (roots[i].cpu_context == cpu) {
+			return roots + i;
+		}
+	}
+	if (num_roots == root_storage) {
+		root_storage = root_storage ? root_storage * 2 : 5;
+		roots = realloc(roots, root_storage * sizeof(debug_root));
+	}
+	debug_root *root = roots + num_roots++;
+	memset(root, 0, sizeof(debug_root));
+	root->cpu_context = cpu;
+	new_readonly_variable(root, "sin", new_native_func(debug_sin, 1, 1));
+	return root;
+}
+
+bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type)
+{
+	while (*cur) {
+		if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) {
+			break;
+		}
+		cur = &((*cur)->next);
+	}
+	return cur;
+}
+
+bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index)
+{
+	while (*cur) {
+		if ((*cur)->index == index) {
+			break;
+		}
+		cur = &((*cur)->next);
+	}
+	return cur;
+}
+
 static const char *token_type_names[] = {
 	"TOKEN_NONE",
 	"TOKEN_INT",
@@ -955,6 +1029,9 @@
 uint8_t eval_expr(debug_root *root, expr *e, debug_val *out)
 {
 	debug_val right;
+	debug_val *args;
+	debug_func *func;
+	int num_args;
 	switch(e->type)
 	{
 	case EXPR_SCALAR:
@@ -1143,6 +1220,37 @@
 			return 0;
 		}
 		return eval_expr(root, e->left, out);
+	case EXPR_FUNCALL:
+		if (!eval_expr(root, e->left, out)) {
+			return 0;
+		}
+		if (out->type != DBG_VAL_FUNC) {
+			fprintf(stderr, "Funcall expression requires function");
+			return 0;
+		}
+		func = funcs + out->v.u32;
+		num_args = e->op.v.num;
+		if (func->min_args > 0 && num_args < func->min_args) {
+			fprintf(stderr, "Function requires at least %d args, but %d given\n", func->min_args, num_args);
+			return 0;
+		}
+		if (func->max_args >= 0 && num_args > func->max_args) {
+			fprintf(stderr, "Function requires no more than %d args, but %d given\n", func->max_args, num_args);
+			return 0;
+		}
+		args = calloc(num_args, sizeof(debug_val));
+		for (int i = 0; i < num_args; i++)
+		{
+			if (!eval_expr(root, e->right + i, args + i)) {
+				return 0;
+			}
+		}
+		if (func->is_native) {
+			*out = func->impl.native(args, num_args);
+		} else {
+			//TODO: Implement me
+		}
+		return 1;
 	default:
 		return 0;
 	}
@@ -1229,43 +1337,6 @@
 	return 1;
 }
 
-static uint8_t debug_cast_int(debug_val val, uint32_t *out)
-{
-	if (val.type == DBG_VAL_U32) {
-		*out = val.v.u32;
-		return 1;
-	}
-	if (val.type == DBG_VAL_F32) {
-		*out = val.v.f32;
-		return 1;
-	}
-	return 0;
-}
-
-static uint8_t debug_cast_float(debug_val val, float *out)
-{
-	if (val.type == DBG_VAL_U32) {
-		*out = val.v.u32;
-		return 1;
-	}
-	if (val.type == DBG_VAL_F32) {
-		*out = val.v.f32;
-		return 1;
-	}
-	return 0;
-}
-
-static uint8_t debug_cast_bool(debug_val val)
-{
-	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;
--- a/debug.h	Wed Nov 08 00:09:33 2023 -0800
+++ b/debug.h	Wed Nov 08 23:47:26 2023 -0800
@@ -151,6 +151,18 @@
 	uint32_t           storage;
 };
 
+typedef debug_val (*debug_native_func)(debug_val *args, int num_args);
+typedef struct {
+	union {
+		debug_native_func native;
+		parsed_command    *commands;
+	} impl;
+	uint32_t num_commands;
+	int      max_args;
+	int      min_args;
+	uint8_t  is_native;
+} debug_func;
+
 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);