Mercurial > repos > blastem
comparison debug.c @ 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 |
comparison
equal
deleted
inserted
replaced
2364:c822bb628fc3 | 2365:8c060849a503 |
---|---|
22 #define Z80_OPTS opts | 22 #define Z80_OPTS opts |
23 #else | 23 #else |
24 #define Z80_OPTS options | 24 #define Z80_OPTS options |
25 #endif | 25 #endif |
26 | 26 |
27 static debug_root *roots; | 27 static debug_func *funcs; |
28 static uint32_t num_roots, root_storage; | 28 static uint32_t num_funcs, func_storage; |
29 | 29 |
30 debug_root *find_root(void *cpu) | 30 static debug_func* alloc_func(void) |
31 { | 31 { |
32 for (uint32_t i = 0; i < num_roots; i++) | 32 if (num_funcs == func_storage) { |
33 { | 33 func_storage = func_storage ? func_storage * 2 : 4; |
34 if (roots[i].cpu_context == cpu) { | 34 funcs = realloc(funcs, sizeof(debug_func) * func_storage); |
35 return roots + i; | 35 } |
36 } | 36 return funcs + num_funcs++; |
37 } | 37 } |
38 if (num_roots == root_storage) { | 38 |
39 root_storage = root_storage ? root_storage * 2 : 5; | 39 static debug_val new_native_func(debug_native_func impl, int max_args, int min_args) |
40 roots = realloc(roots, root_storage * sizeof(debug_root)); | 40 { |
41 } | 41 debug_func *f = alloc_func(); |
42 num_roots++; | 42 f->impl.native = impl; |
43 memset(roots + num_roots - 1, 0, sizeof(debug_root)); | 43 f->max_args = max_args; |
44 roots[num_roots-1].cpu_context = cpu; | 44 f->min_args = min_args; |
45 return roots + num_roots - 1; | 45 f->is_native = 1; |
46 } | 46 return (debug_val) { |
47 | 47 .v = { |
48 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type) | 48 .u32 = f - funcs |
49 { | 49 }, |
50 while (*cur) { | 50 .type = DBG_VAL_FUNC |
51 if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) { | 51 }; |
52 break; | 52 } |
53 } | 53 |
54 cur = &((*cur)->next); | 54 debug_val user_var_get(debug_var *var) |
55 } | 55 { |
56 return cur; | 56 return var->val; |
57 } | 57 } |
58 | 58 |
59 bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index) | 59 void user_var_set(debug_var *var, debug_val val) |
60 { | 60 { |
61 while (*cur) { | 61 var->val = val; |
62 if ((*cur)->index == index) { | 62 } |
63 break; | 63 |
64 } | 64 static void new_user_variable(debug_root *root, const char *name, debug_val val) |
65 cur = &((*cur)->next); | 65 { |
66 } | 66 debug_var *var = calloc(1, sizeof(debug_var)); |
67 return cur; | 67 var->get = user_var_get; |
68 var->set = user_var_set; | |
69 var->val = val; | |
70 root->variables = tern_insert_ptr(root->variables, name, var); | |
71 } | |
72 | |
73 static void new_readonly_variable(debug_root *root, const char *name, debug_val val) | |
74 { | |
75 debug_var *var = calloc(1, sizeof(debug_var)); | |
76 var->get = user_var_get; | |
77 var->set = NULL; | |
78 var->val = val; | |
79 root->variables = tern_insert_ptr(root->variables, name, var); | |
68 } | 80 } |
69 | 81 |
70 static debug_array *arrays; | 82 static debug_array *arrays; |
71 static uint32_t num_arrays, array_storage; | 83 static uint32_t num_arrays, array_storage; |
72 static debug_array *alloc_array(void) | 84 static debug_array *alloc_array(void) |
134 return NULL; | 146 return NULL; |
135 } | 147 } |
136 return arrays + val.v.u32; | 148 return arrays + val.v.u32; |
137 } | 149 } |
138 | 150 |
139 debug_val user_var_get(debug_var *var) | 151 static uint8_t debug_cast_int(debug_val val, uint32_t *out) |
140 { | 152 { |
141 return var->val; | 153 if (val.type == DBG_VAL_U32) { |
142 } | 154 *out = val.v.u32; |
143 | 155 return 1; |
144 void user_var_set(debug_var *var, debug_val val) | 156 } |
145 { | 157 if (val.type == DBG_VAL_F32) { |
146 var->val = val; | 158 *out = val.v.f32; |
147 } | 159 return 1; |
148 | 160 } |
149 static void new_user_variable(debug_root *root, const char *name, debug_val val) | 161 return 0; |
150 { | 162 } |
151 debug_var *var = calloc(1, sizeof(debug_var)); | 163 |
152 var->get = user_var_get; | 164 static uint8_t debug_cast_float(debug_val val, float *out) |
153 var->set = user_var_set; | 165 { |
154 var->val = val; | 166 if (val.type == DBG_VAL_U32) { |
155 root->variables = tern_insert_ptr(root->variables, name, var); | 167 *out = val.v.u32; |
156 } | 168 return 1; |
157 | 169 } |
158 static void new_readonly_variable(debug_root *root, const char *name, debug_val val) | 170 if (val.type == DBG_VAL_F32) { |
159 { | 171 *out = val.v.f32; |
160 debug_var *var = calloc(1, sizeof(debug_var)); | 172 return 1; |
161 var->get = user_var_get; | 173 } |
162 var->set = NULL; | 174 return 0; |
163 var->val = val; | 175 } |
164 root->variables = tern_insert_ptr(root->variables, name, var); | 176 |
177 static uint8_t debug_cast_bool(debug_val val) | |
178 { | |
179 switch(val.type) | |
180 { | |
181 case DBG_VAL_U32: return val.v.u32 != 0; | |
182 case DBG_VAL_F32: return val.v.f32 != 0.0f; | |
183 case DBG_VAL_ARRAY: return get_array(val)->size != 0; | |
184 default: return 1; | |
185 } | |
165 } | 186 } |
166 | 187 |
167 static debug_val debug_int(uint32_t i) | 188 static debug_val debug_int(uint32_t i) |
168 { | 189 { |
169 debug_val ret; | 190 debug_val ret; |
178 .type = DBG_VAL_F32, | 199 .type = DBG_VAL_F32, |
179 .v = { | 200 .v = { |
180 .f32 = f | 201 .f32 = f |
181 } | 202 } |
182 }; | 203 }; |
204 } | |
205 | |
206 debug_val debug_sin(debug_val *args, int num_args) | |
207 { | |
208 float f; | |
209 if (!debug_cast_float(args[0], &f)) { | |
210 return debug_float(0.0f); | |
211 } | |
212 return debug_float(sinf(f)); | |
213 } | |
214 | |
215 static debug_root *roots; | |
216 static uint32_t num_roots, root_storage; | |
217 | |
218 debug_root *find_root(void *cpu) | |
219 { | |
220 for (uint32_t i = 0; i < num_roots; i++) | |
221 { | |
222 if (roots[i].cpu_context == cpu) { | |
223 return roots + i; | |
224 } | |
225 } | |
226 if (num_roots == root_storage) { | |
227 root_storage = root_storage ? root_storage * 2 : 5; | |
228 roots = realloc(roots, root_storage * sizeof(debug_root)); | |
229 } | |
230 debug_root *root = roots + num_roots++; | |
231 memset(root, 0, sizeof(debug_root)); | |
232 root->cpu_context = cpu; | |
233 new_readonly_variable(root, "sin", new_native_func(debug_sin, 1, 1)); | |
234 return root; | |
235 } | |
236 | |
237 bp_def ** find_breakpoint(bp_def ** cur, uint32_t address, uint8_t type) | |
238 { | |
239 while (*cur) { | |
240 if ((*cur)->type == type && (*cur)->address == (((*cur)->mask) & address)) { | |
241 break; | |
242 } | |
243 cur = &((*cur)->next); | |
244 } | |
245 return cur; | |
246 } | |
247 | |
248 bp_def ** find_breakpoint_idx(bp_def ** cur, uint32_t index) | |
249 { | |
250 while (*cur) { | |
251 if ((*cur)->index == index) { | |
252 break; | |
253 } | |
254 cur = &((*cur)->next); | |
255 } | |
256 return cur; | |
183 } | 257 } |
184 | 258 |
185 static const char *token_type_names[] = { | 259 static const char *token_type_names[] = { |
186 "TOKEN_NONE", | 260 "TOKEN_NONE", |
187 "TOKEN_INT", | 261 "TOKEN_INT", |
953 } | 1027 } |
954 | 1028 |
955 uint8_t eval_expr(debug_root *root, expr *e, debug_val *out) | 1029 uint8_t eval_expr(debug_root *root, expr *e, debug_val *out) |
956 { | 1030 { |
957 debug_val right; | 1031 debug_val right; |
1032 debug_val *args; | |
1033 debug_func *func; | |
1034 int num_args; | |
958 switch(e->type) | 1035 switch(e->type) |
959 { | 1036 { |
960 case EXPR_SCALAR: | 1037 case EXPR_SCALAR: |
961 if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY) { | 1038 if (e->op.type == TOKEN_NAME || e->op.type == TOKEN_ARRAY) { |
962 debug_var *var = tern_find_ptr(root->variables, e->op.v.str); | 1039 debug_var *var = tern_find_ptr(root->variables, e->op.v.str); |
1141 if (!root) { | 1218 if (!root) { |
1142 fprintf(stderr, "%s is not a valid namespace\n", e->op.v.str); | 1219 fprintf(stderr, "%s is not a valid namespace\n", e->op.v.str); |
1143 return 0; | 1220 return 0; |
1144 } | 1221 } |
1145 return eval_expr(root, e->left, out); | 1222 return eval_expr(root, e->left, out); |
1223 case EXPR_FUNCALL: | |
1224 if (!eval_expr(root, e->left, out)) { | |
1225 return 0; | |
1226 } | |
1227 if (out->type != DBG_VAL_FUNC) { | |
1228 fprintf(stderr, "Funcall expression requires function"); | |
1229 return 0; | |
1230 } | |
1231 func = funcs + out->v.u32; | |
1232 num_args = e->op.v.num; | |
1233 if (func->min_args > 0 && num_args < func->min_args) { | |
1234 fprintf(stderr, "Function requires at least %d args, but %d given\n", func->min_args, num_args); | |
1235 return 0; | |
1236 } | |
1237 if (func->max_args >= 0 && num_args > func->max_args) { | |
1238 fprintf(stderr, "Function requires no more than %d args, but %d given\n", func->max_args, num_args); | |
1239 return 0; | |
1240 } | |
1241 args = calloc(num_args, sizeof(debug_val)); | |
1242 for (int i = 0; i < num_args; i++) | |
1243 { | |
1244 if (!eval_expr(root, e->right + i, args + i)) { | |
1245 return 0; | |
1246 } | |
1247 } | |
1248 if (func->is_native) { | |
1249 *out = func->impl.native(args, num_args); | |
1250 } else { | |
1251 //TODO: Implement me | |
1252 } | |
1253 return 1; | |
1146 default: | 1254 default: |
1147 return 0; | 1255 return 0; |
1148 } | 1256 } |
1149 } | 1257 } |
1150 | 1258 |
1225 return 0; | 1333 return 0; |
1226 } | 1334 } |
1227 write_word(address, value, (void **)context->mem_pointers, &context->options->gen, context); | 1335 write_word(address, value, (void **)context->mem_pointers, &context->options->gen, context); |
1228 } | 1336 } |
1229 return 1; | 1337 return 1; |
1230 } | |
1231 | |
1232 static uint8_t debug_cast_int(debug_val val, uint32_t *out) | |
1233 { | |
1234 if (val.type == DBG_VAL_U32) { | |
1235 *out = val.v.u32; | |
1236 return 1; | |
1237 } | |
1238 if (val.type == DBG_VAL_F32) { | |
1239 *out = val.v.f32; | |
1240 return 1; | |
1241 } | |
1242 return 0; | |
1243 } | |
1244 | |
1245 static uint8_t debug_cast_float(debug_val val, float *out) | |
1246 { | |
1247 if (val.type == DBG_VAL_U32) { | |
1248 *out = val.v.u32; | |
1249 return 1; | |
1250 } | |
1251 if (val.type == DBG_VAL_F32) { | |
1252 *out = val.v.f32; | |
1253 return 1; | |
1254 } | |
1255 return 0; | |
1256 } | |
1257 | |
1258 static uint8_t debug_cast_bool(debug_val val) | |
1259 { | |
1260 switch(val.type) | |
1261 { | |
1262 case DBG_VAL_U32: return val.v.u32 != 0; | |
1263 case DBG_VAL_F32: return val.v.f32 != 0.0f; | |
1264 case DBG_VAL_ARRAY: return get_array(val)->size != 0; | |
1265 default: return 1; | |
1266 } | |
1267 } | 1338 } |
1268 | 1339 |
1269 static debug_val m68k_dreg_get(debug_var *var) | 1340 static debug_val m68k_dreg_get(debug_var *var) |
1270 { | 1341 { |
1271 m68k_context *context = var->ptr; | 1342 m68k_context *context = var->ptr; |