changeset 2359:04d29635d238

Support for arrays in debugger language
author Michael Pavone <pavone@retrodev.com>
date Sat, 28 Oct 2023 14:39:19 -0700
parents 4b2ac43c106e
children 053ba4551c62
files debug.c debug.h vdp.c vdp.h
diffstat 4 files changed, 378 insertions(+), 72 deletions(-) [+]
line wrap: on
line diff
--- a/debug.c	Sat Oct 28 00:52:10 2023 -0700
+++ b/debug.c	Sat Oct 28 14:39:19 2023 -0700
@@ -167,11 +167,14 @@
 		};
 	}
 	*end = start + 1;
+	token_type type = TOKEN_NAME;
 	while (**end && !isspace(**end))
 	{
 		uint8_t done = 0;
 		switch (**end)
 		{
+		case '[':
+			type = TOKEN_ARRAY;
 		case '+':
 		case '-':
 		case '*':
@@ -185,7 +188,6 @@
 		case '>':
 		case '<':
 		case '.':
-		case '[':
 		case ']':
 		case '(':
 		case ')':
@@ -202,7 +204,7 @@
 	memcpy(name, start, *end - start);
 	name[*end-start] = 0;
 	return (token) {
-		.type = TOKEN_NAME,
+		.type = type,
 		.v = {
 			.str = name
 		}
@@ -249,9 +251,18 @@
 		*end = after_first;
 		return ret;
 	}
-	if (first.type == TOKEN_LBRACKET) {
+	if (first.type == TOKEN_LBRACKET || first.type == TOKEN_ARRAY) {
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_MEM;
+		if (first.type == TOKEN_ARRAY) {
+			//current token is the array name
+			//consume the bracket token
+			parse_token(after_first, &after_first);
+			ret->right = calloc(1, sizeof(expr));
+			ret->right->type = EXPR_SCALAR;
+			ret->right->op = first;
+		}
+
 		ret->left = parse_expression(after_first, end);
 		if (!ret->left) {
 			fprintf(stderr, "Expression expected after `[`\n");
@@ -396,9 +407,18 @@
 		ret->left = target;
 		return ret;
 	}
-	if (first.type == TOKEN_LBRACKET) {
+	if (first.type == TOKEN_LBRACKET || first.type == TOKEN_ARRAY) {
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_MEM;
+		if (first.type == TOKEN_ARRAY) {
+			//current token is the array name
+			//consume the bracket token
+			parse_token(after_first, &after_first);
+			ret->right = calloc(1, sizeof(expr));
+			ret->right->type = EXPR_SCALAR;
+			ret->right->op = first;
+		}
+
 		ret->left = parse_expression(after_first, end);
 		if (!ret->left) {
 			fprintf(stderr, "Expression expected after `[`\n");
@@ -512,9 +532,18 @@
 		ret->left = target;
 		return ret;
 	}
-	if (first.type == TOKEN_LBRACKET) {
+	if (first.type == TOKEN_LBRACKET || first.type == TOKEN_ARRAY) {
 		expr *ret = calloc(1, sizeof(expr));
 		ret->type = EXPR_MEM;
+		if (first.type == TOKEN_ARRAY) {
+			//current token is the array name
+			//consume the bracket token
+			parse_token(after_first, &after_first);
+			ret->right = calloc(1, sizeof(expr));
+			ret->right->type = EXPR_SCALAR;
+			ret->right->op = first;
+		}
+
 		ret->left = parse_expression(after_first, end);
 		if (!ret->left) {
 			fprintf(stderr, "Expression expected after `[`\n");
@@ -622,6 +651,15 @@
 	}
 }
 
+static debug_array* full_array_resolve(debug_root *root, const char *name)
+{
+	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;
@@ -726,6 +764,14 @@
 		if (!eval_expr(root, e->left, out)) {
 			return 0;
 		}
+		if (e->right) {
+			debug_array *array = full_array_resolve(root, e->right->op.v.str);
+			if (!array || *out >= array->size) {
+				return 0;
+			}
+			*out = array->get(root, array, *out);
+			return 1;
+		}
 		return root->read_mem(root, out, e->op.v.op[0]);
 	default:
 		return 0;
@@ -921,6 +967,127 @@
 	return 0;
 }
 
+static uint32_t debug_vram_get(debug_root *root, debug_array *array, uint32_t index)
+{
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	return gen->vdp->vdpmem[index];
+}
+
+static void debug_vram_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->vdp->vdpmem[index] = value;
+}
+
+static uint32_t debug_vsram_get(debug_root *root, debug_array *array, uint32_t index)
+{
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	return gen->vdp->vsram[index] & VSRAM_BITS;
+}
+
+static void debug_vsram_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->vdp->vsram[index] = value;
+}
+
+static uint32_t debug_cram_get(debug_root *root, debug_array *array, uint32_t index)
+{
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	return gen->vdp->cram[index] & CRAM_BITS;
+}
+
+static void debug_cram_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->vdp->cram[index] = value;
+}
+
+static uint32_t debug_vreg_get(debug_root *root, debug_array *array, uint32_t index)
+{
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	return gen->vdp->regs[index];
+}
+
+static void debug_vreg_set(debug_root *root, debug_array *array, uint32_t index, uint32_t value)
+{
+	m68k_context *m68k = root->cpu_context;
+	genesis_context *gen = m68k->system;
+	vdp_reg_write(gen->vdp, index, value);
+}
+
+static debug_array* resolve_vdp_array(debug_root *root, const char *name)
+{
+	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;
+	}
+	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;
+}
+
+static debug_array* resolve_genesis_array(debug_root *root, const char *name)
+{
+	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 == 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;
+			}
+		}
+	}
+	return NULL;
+}
+
+static debug_array* resolve_null_array(debug_root *root, const char *name)
+{
+	return NULL;
+}
+
 void ambiguous_iter(char *key, tern_val val, uint8_t valtype, void *data)
 {
 	char *prefix = data;
@@ -1615,6 +1782,7 @@
 	char *name = NULL;
 	char size = 0;
 	uint32_t address;
+	debug_array *array = NULL;
 	switch (cmd->args[0].parsed->type)
 	{
 	case EXPR_SCALAR:
@@ -1640,6 +1808,21 @@
 			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 (!array) {
+				fprintf(stderr, "Failed to resolve array %s\n", cmd->args[0].parsed->right->op.v.str);
+				return 1;
+			}
+			if (!array->set) {
+				fprintf(stderr, "Array %s is read-only\n", cmd->args[0].parsed->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);
+				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]);
@@ -1675,6 +1858,8 @@
 				fprintf(stderr, "Failed to set %s\n", name);
 			}
 		}
+	} 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);
 	}
@@ -1699,6 +1884,76 @@
 	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]);
+		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);
+	}
+	array->size = cmd->num_args - 1;
+	for (uint32_t i = 1; i < cmd->num_args; 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);
+	}
+	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);
+	if (!array) {
+		fprintf(stderr, "Failed to resolve array %s\n", cmd->args[0].parsed->op.v.str);
+		return 1;
+	}
+	if (!array->append) {
+		fprintf(stderr, "Array %s doesn't support appending\n", cmd->args[0].parsed->op.v.str);
+		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);
+	return 1;
+}
+
 static uint8_t cmd_frames(debug_root *root, parsed_command *cmd)
 {
 	current_system->enter_debugger_frames = cmd->args[0].value;
@@ -2232,6 +2487,28 @@
 	},
 	{
 		.names = (const char *[]){
+			"array", NULL
+		},
+		.usage = "array NAME [VALUE...]",
+		.desc = "Create a new array called NAME if it doesn't already exist. The array is initialized with the remaining parameters",
+		.impl = cmd_array,
+		.min_args = 1,
+		.max_args = -1,
+		.skip_eval = 1
+	},
+	{
+		.names = (const char *[]){
+			"append", NULL
+		},
+		.usage = "append NAME VALUE",
+		.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
+	},
+	{
+		.names = (const char *[]){
 			"frames", NULL
 		},
 		.usage = "frames EXPRESSION",
@@ -2867,6 +3144,7 @@
 			//check if this is the main CPU
 			if (context->system == current_system) {
 				root->resolve = resolve_genesis;
+				root->array_resolve = resolve_genesis_array;
 				add_commands(root, genesis_commands, NUM_GENESIS);
 				if (current_system->type == SYSTEM_SEGACD) {
 					add_segacd_maincpu_labels(root->disasm);
@@ -2879,6 +3157,7 @@
 			}
 		default:
 			root->resolve = resolve_m68k;
+			root->array_resolve = resolve_null_array;
 		}
 		tern_foreach(root->disasm->labels, symbol_map, root);
 	}
@@ -3397,6 +3676,7 @@
 		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;
--- a/debug.h	Sat Oct 28 00:52:10 2023 -0700
+++ b/debug.h	Sat Oct 28 14:39:19 2023 -0700
@@ -15,6 +15,7 @@
 	TOKEN_NONE,
 	TOKEN_NUM,
 	TOKEN_NAME,
+	TOKEN_ARRAY,
 	TOKEN_OPER,
 	TOKEN_SIZE,
 	TOKEN_LBRACKET,
@@ -106,7 +107,22 @@
 	uint32_t       index;
 } 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);
+
+struct debug_array{
+	debug_array_get    get;
+	debug_array_set    set;
+	debug_array_append append;
+	uint32_t           *data;
+	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 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);
@@ -118,8 +134,10 @@
 	tern_node      *commands;
 	tern_node      *symbols;
 	tern_node      *variables;
+	tern_node      *arrays;
 	disasm_context *disasm;
 	resolver       resolve;
+	array_resolver array_resolve;
 	reader         read_mem;
 	setter         set;
 	writer         write_mem;
--- a/vdp.c	Sat Oct 28 00:52:10 2023 -0700
+++ b/vdp.c	Sat Oct 28 14:39:19 2023 -0700
@@ -925,8 +925,6 @@
 	}
 }
 
-#define CRAM_BITS 0xEEE
-#define VSRAM_BITS 0x7FF
 #define VSRAM_DIRTY_BITS 0xF800
 
 //rough estimate of slot number at which border display starts
@@ -4602,6 +4600,75 @@
 	return hv;
 }
 
+void vdp_reg_write(vdp_context *context, uint16_t reg, uint16_t value)
+{
+	uint8_t mode_5 = context->regs[REG_MODE_2] & BIT_MODE_5;
+	if (reg < (mode_5 ? VDP_REGS : 0xB)) {
+		//printf("register %d set to %X\n", reg, value & 0xFF);
+		if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
+			vdp_latch_hv(context);
+		} else if (reg == REG_BG_COLOR) {
+			value &= 0x3F;
+		} else if (reg == REG_MODE_2 && context->type != VDP_GENESIS) {
+			// only the Genesis VDP does anything with this bit
+			// so just clear it to prevent Mode 5 selection if we're not emulating that chip
+			value &= ~BIT_MODE_5;
+		}
+		/*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
+			printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
+		}*/
+		uint8_t buffer[2] = {reg, value};
+		event_log(EVENT_VDP_REG, context->cycles, sizeof(buffer), buffer);
+		context->regs[reg] = value;
+		if (reg == REG_MODE_4) {
+			context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
+			if (!context->double_res) {
+				context->flags2 &= ~FLAG2_EVEN_FIELD;
+			}
+		}
+		if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
+			update_video_params(context);
+		}
+	} else if (reg == REG_KMOD_CTRL) {
+		if (!(value & 0xFF)) {
+			context->system->enter_debugger = 1;
+		}
+	} else if (reg == REG_KMOD_MSG) {
+		char c = value;
+		if (c) {
+			context->kmod_buffer_length++;
+			if ((context->kmod_buffer_length + 1) > context->kmod_buffer_storage) {
+				context->kmod_buffer_storage = context->kmod_buffer_length ? 128 : context->kmod_buffer_length * 2;
+				context->kmod_msg_buffer = realloc(context->kmod_msg_buffer, context->kmod_buffer_storage);
+			}
+			context->kmod_msg_buffer[context->kmod_buffer_length - 1] = c;
+		} else if (context->kmod_buffer_length) {
+			context->kmod_msg_buffer[context->kmod_buffer_length] = 0;
+			if (is_stdout_enabled()) {
+				init_terminal();
+				printf("KDEBUG MESSAGE: %s\n", context->kmod_msg_buffer);
+			} else {
+				// GDB remote debugging is enabled, use stderr instead
+				fprintf(stderr, "KDEBUG MESSAGE: %s\n", context->kmod_msg_buffer);
+			}
+			context->kmod_buffer_length = 0;
+		}
+	} else if (reg == REG_KMOD_TIMER) {
+		if (!(value & 0x80)) {
+			if (is_stdout_enabled()) {
+				init_terminal();
+				printf("KDEBUG TIMER: %d\n", (context->cycles - context->timer_start_cycle) / 7);
+			} else {
+				// GDB remote debugging is enabled, use stderr instead
+				fprintf(stderr, "KDEBUG TIMER: %d\n", (context->cycles - context->timer_start_cycle) / 7);
+			}
+		}
+		if (value & 0xC0) {
+			context->timer_start_cycle = context->cycles;
+		}
+	}
+}
+
 int vdp_control_port_write(vdp_context * context, uint16_t value, uint32_t cpu_cycle)
 {
 	//printf("control port write: %X at %d\n", value, context->cycles);
@@ -4655,71 +4722,8 @@
 		context->cd = (context->cd & 0x3C) | (value >> 14);
 		if ((value & 0xC000) == 0x8000) {
 			//Register write
-			uint8_t reg = (value >> 8) & 0x1F;
-			if (reg < (mode_5 ? VDP_REGS : 0xB)) {
-				//printf("register %d set to %X\n", reg, value & 0xFF);
-				if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) {
-					vdp_latch_hv(context);
-				} else if (reg == REG_BG_COLOR) {
-					value &= 0x3F;
-				} else if (reg == REG_MODE_2 && context->type != VDP_GENESIS) {
-					// only the Genesis VDP does anything with this bit
-					// so just clear it to prevent Mode 5 selection if we're not emulating that chip
-					value &= ~BIT_MODE_5;
-				}
-				/*if (reg == REG_MODE_4 && ((value ^ context->regs[reg]) & BIT_H40)) {
-					printf("Mode changed from H%d to H%d @ %d, frame: %d\n", context->regs[reg] & BIT_H40 ? 40 : 32, value & BIT_H40 ? 40 : 32, context->cycles, context->frame);
-				}*/
-				uint8_t buffer[2] = {reg, value};
-				event_log(EVENT_VDP_REG, context->cycles, sizeof(buffer), buffer);
-				context->regs[reg] = value;
-				if (reg == REG_MODE_4) {
-					context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES);
-					if (!context->double_res) {
-						context->flags2 &= ~FLAG2_EVEN_FIELD;
-					}
-				}
-				if (reg == REG_MODE_1 || reg == REG_MODE_2 || reg == REG_MODE_4) {
-					update_video_params(context);
-				}
-			} else if (reg == REG_KMOD_CTRL) {
-				if (!(value & 0xFF)) {
-					context->system->enter_debugger = 1;
-				}
-			} else if (reg == REG_KMOD_MSG) {
-				char c = value;
-				if (c) {
-					context->kmod_buffer_length++;
-					if ((context->kmod_buffer_length + 1) > context->kmod_buffer_storage) {
-						context->kmod_buffer_storage = context->kmod_buffer_length ? 128 : context->kmod_buffer_length * 2;
-						context->kmod_msg_buffer = realloc(context->kmod_msg_buffer, context->kmod_buffer_storage);
-					}
-					context->kmod_msg_buffer[context->kmod_buffer_length - 1] = c;
-				} else if (context->kmod_buffer_length) {
-					context->kmod_msg_buffer[context->kmod_buffer_length] = 0;
-					if (is_stdout_enabled()) {
-						init_terminal();
-						printf("KDEBUG MESSAGE: %s\n", context->kmod_msg_buffer);
-					} else {
-						// GDB remote debugging is enabled, use stderr instead
-						fprintf(stderr, "KDEBUG MESSAGE: %s\n", context->kmod_msg_buffer);
-					}
-					context->kmod_buffer_length = 0;
-				}
-			} else if (reg == REG_KMOD_TIMER) {
-				if (!(value & 0x80)) {
-					if (is_stdout_enabled()) {
-						init_terminal();
-						printf("KDEBUG TIMER: %d\n", (context->cycles - context->timer_start_cycle) / 7);
-					} else {
-						// GDB remote debugging is enabled, use stderr instead
-						fprintf(stderr, "KDEBUG TIMER: %d\n", (context->cycles - context->timer_start_cycle) / 7);
-					}
-				}
-				if (value & 0xC0) {
-					context->timer_start_cycle = context->cycles;
-				}
-			}
+			uint16_t reg = (value >> 8) & 0x1F;
+			vdp_reg_write(context, reg, value);
 		} else if (mode_5) {
 			context->flags |= FLAG_PENDING;
 			//Should these be taken care of here or after the second write?
--- a/vdp.h	Sat Oct 28 00:52:10 2023 -0700
+++ b/vdp.h	Sat Oct 28 14:39:19 2023 -0700
@@ -32,6 +32,9 @@
 #define MAX_SPRITES_FRAME_H32 64
 #define SAT_CACHE_SIZE (MAX_SPRITES_FRAME * 4)
 
+#define CRAM_BITS 0xEEE
+#define VSRAM_BITS 0x7FF
+
 #define FBUF_SHADOW 0x0001
 #define FBUF_HILIGHT 0x0010
 #define FBUF_MODE4 0x0100
@@ -318,5 +321,6 @@
 void vdp_dma_started(void);
 void vdp_replay_event(vdp_context *context, uint8_t event, event_reader *reader);
 uint16_t vdp_status(vdp_context *context);
+void vdp_reg_write(vdp_context *context, uint16_t reg, uint16_t value);
 
 #endif //VDP_H_