# HG changeset patch # User Michael Pavone # Date 1698529159 25200 # Node ID 04d29635d238b388b8f439c58bde5bc139405319 # Parent 4b2ac43c106e857431b6f84baa419e9e916e5224 Support for arrays in debugger language diff -r 4b2ac43c106e -r 04d29635d238 debug.c --- 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 ®s; + } + 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; diff -r 4b2ac43c106e -r 04d29635d238 debug.h --- 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; diff -r 4b2ac43c106e -r 04d29635d238 vdp.c --- 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? diff -r 4b2ac43c106e -r 04d29635d238 vdp.h --- 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_