/*************************************************************************** debugcmt.c Debugger code-comment management functions. Copyright Nicola Salmoria and the MAME Team. Visit http://mamedev.org for licensing and usage restrictions. **************************************************************************** Debugger comment file format: (from comment struct - assuming (MAX_COMMENT_LINE_LENGTH == 128)) 0 - valid byte 1:4 - address 5:133 - comment 134:138 - color 139:142 - instruction crc ***************************************************************************/ #include "driver.h" #include "xmlfile.h" #include "debugcmt.h" #include "debugcpu.h" #include "debugvw.h" #include "info.h" #include /*************************************************************************** DEBUGGING ***************************************************************************/ #define VERBOSE 0 #define LOG(x) do { if (VERBOSE) logerror x; } while (0) /*************************************************************************** CONSTANTS ***************************************************************************/ #define COMMENT_VERSION (1) /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ typedef struct _debug_comment debug_comment; struct _debug_comment { UINT8 is_valid; UINT32 address; char text[DEBUG_COMMENT_MAX_LINE_LENGTH]; rgb_t color; UINT32 crc; }; /* in debugcpu.h -- typedef struct _debug_cpu_comment_group debug_cpu_comment_group; */ struct _debug_cpu_comment_group { int comment_count; UINT32 change_count; debug_comment * comment_info[DEBUG_COMMENT_MAX_NUM]; }; /*************************************************************************** FUNCTION PROTOTYPES ***************************************************************************/ static int debug_comment_load_xml(running_machine *machine, mame_file *file); static void debug_comment_exit(running_machine *machine); static void debug_comment_free(running_machine *machine); /*************************************************************************** INITIALIZATION ***************************************************************************/ /*------------------------------------------------------------------------- debug_comment_init - initializes the comment memory and loads any existing comment file -------------------------------------------------------------------------*/ int debug_comment_init(running_machine *machine) { const device_config *cpu; /* allocate memory for the comments */ for (cpu = machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu)) { cpu_debug_data *cpudata = cpu_get_debug_data(cpu); cpudata->comments = auto_alloc_clear(machine, debug_cpu_comment_group); } /* automatically load em up */ debug_comment_load(machine); add_exit_callback(machine, debug_comment_exit); return 1; } /*------------------------------------------------------------------------- debug_comment_add - adds a comment to the list at the given address. use debug_comment_get_opcode_crc32(addr) to get the proper crc32 -------------------------------------------------------------------------*/ int debug_comment_add(const device_config *device, offs_t addr, const char *comment, rgb_t color, UINT32 c_crc) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; int insert_point = comments->comment_count; int match = 0; int i = 0; /* Create a new item to insert into the list */ debug_comment *insert_me = alloc_or_die(debug_comment); insert_me->color = color; insert_me->is_valid = 1; insert_me->address = addr; insert_me->crc = c_crc; strcpy(insert_me->text, comment); /* Find the insert point */ for (i = 0; i < comments->comment_count; i++) { if (insert_me->address < comments->comment_info[i]->address) { insert_point = i; break; } else if (insert_me->address == comments->comment_info[i]->address && insert_me->crc == comments->comment_info[i]->crc) { insert_point = i; match = 1; break; } } /* Got an exact match? Just replace */ if (match == 1) { free(comments->comment_info[insert_point]); comments->comment_info[insert_point] = insert_me; comments->change_count++; /* force an update of disassembly views */ debug_view_update_type(device->machine, DVT_DISASSEMBLY); return 1; } /* Otherwise insert */ /* First, shift the list down */ for (i = comments->comment_count; i >= insert_point; i--) comments->comment_info[i] = comments->comment_info[i-1]; /* do the insertion */ comments->comment_info[insert_point] = insert_me; comments->comment_count++; comments->change_count++; /* force an update of disassembly views */ debug_view_update_type(device->machine, DVT_DISASSEMBLY); return 1; } /*------------------------------------------------------------------------- debug_comment_remove - removes a comment at a given address use debug_comment_get_opcode_crc32(addr) to get the proper crc32 -------------------------------------------------------------------------*/ int debug_comment_remove(const device_config *device, offs_t addr, UINT32 c_crc) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; int remove_index = -1; int i; for (i = 0; i < comments->comment_count; i++) if (comments->comment_info[i]->address == addr) /* got an address match */ if (comments->comment_info[i]->crc == c_crc) remove_index = i; /* The comment doesn't exist? */ if (remove_index == -1) return 0; /* Okay, it's there, now remove it */ free(comments->comment_info[remove_index]); for (i = remove_index; i < comments->comment_count-1; i++) comments->comment_info[i] = comments->comment_info[i+1]; comments->comment_count--; comments->change_count++; /* force an update of disassembly views */ debug_view_update_type(device->machine, DVT_DISASSEMBLY); return 1; } /*------------------------------------------------------------------------- debug_comment_get_text - returns the comment for a given addresses use debug_comment_get_opcode_crc32(addr) to get the proper crc32 -------------------------------------------------------------------------*/ const char *debug_comment_get_text(const device_config *device, offs_t addr, UINT32 c_crc) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; int i; /* inefficient - should use bsearch - but will be a little tricky with multiple comments per addr */ for (i = 0; i < comments->comment_count; i++) if (comments->comment_info[i]->address == addr) /* got an address match */ { /* now check the bank information to be sure */ if (comments->comment_info[i]->crc == c_crc) return comments->comment_info[i]->text; } return 0x00; } /*------------------------------------------------------------------------- debug_comment_get_count - returns the number of comments for a given cpu number -------------------------------------------------------------------------*/ int debug_comment_get_count(const device_config *device) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; return comments->comment_count; } /*------------------------------------------------------------------------- debug_comment_get_change_count - returns the change counter for a given cpu number -------------------------------------------------------------------------*/ UINT32 debug_comment_get_change_count(const device_config *device) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; return comments->change_count; } /*------------------------------------------------------------------------- debug_comment_all_change_count - returns the change counter for all cpu's -------------------------------------------------------------------------*/ UINT32 debug_comment_all_change_count(running_machine *machine) { const device_config *cpu; UINT32 retVal = 0; for (cpu = machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu)) { debug_cpu_comment_group *comments = cpu_get_debug_data(cpu)->comments; retVal += comments->change_count; } return retVal; } /*------------------------------------------------------------------------- debug_comment_get_opcode_crc32 - magic function that takes all the current state of the debugger and returns a crc32 for the opcode at the requested address. -------------------------------------------------------------------------*/ UINT32 debug_comment_get_opcode_crc32(const device_config *device, offs_t address) { const address_space *space = cpu_get_address_space(device, ADDRESS_SPACE_PROGRAM); int i; UINT32 crc; UINT8 opbuf[64], argbuf[64]; char buff[256]; offs_t numbytes; int maxbytes = cpu_get_max_opcode_bytes(device); UINT32 addrmask = space->logaddrmask; memset(opbuf, 0x00, sizeof(opbuf)); memset(argbuf, 0x00, sizeof(argbuf)); // fetch the bytes up to the maximum for (i = 0; i < maxbytes; i++) { opbuf[i] = debug_read_opcode(space, address + i, 1, FALSE); argbuf[i] = debug_read_opcode(space, address + i, 1, TRUE); } numbytes = debug_cpu_disassemble(device, buff, address & addrmask, opbuf, argbuf) & DASMFLAG_LENGTHMASK; numbytes = memory_address_to_byte(space, numbytes); crc = crc32(0, argbuf, numbytes); return crc; } /*------------------------------------------------------------------------- debug_comment_dump - debugging function to dump junk to the command line -------------------------------------------------------------------------*/ void debug_comment_dump(const device_config *device, offs_t addr) { debug_cpu_comment_group *comments = cpu_get_debug_data(device)->comments; int i; int ff = 0; if (addr == -1) { for (i = 0; i < comments->comment_count; i++) if (comments->comment_info[i]->is_valid) logerror("%d : %s (%d %d)\n", i, comments->comment_info[i]->text, comments->comment_info[i]->address, comments->comment_info[i]->crc); } else { UINT32 c_crc = debug_comment_get_opcode_crc32(device, addr); for (i = 0; i < comments->comment_count; i++) if (comments->comment_info[i]->address == addr) /* got an address match */ { /* now check the bank information to be sure */ if (comments->comment_info[i]->crc == c_crc) { logerror("%d : %s (%d %d)\n", addr, comments->comment_info[addr]->text, comments->comment_info[addr]->address, comments->comment_info[addr]->crc); ff = 1; } } if (!ff) logerror("No comment exists for address : 0x%x\n", addr); } } /*------------------------------------------------------------------------- debug_comment_save - comment file saving -------------------------------------------------------------------------*/ int debug_comment_save(running_machine *machine) { int j; char crc_buf[20]; xml_data_node *root = xml_file_create(); xml_data_node *commentnode, *systemnode; int total_comments = 0; const device_config *cpu; /* if we don't have a root, bail */ if (root == NULL) return 0; /* create a comment node */ commentnode = xml_add_child(root, "mamecommentfile", NULL); if (commentnode == NULL) goto error; xml_set_attribute_int(commentnode, "version", COMMENT_VERSION); /* create a system node */ systemnode = xml_add_child(commentnode, "system", NULL); if (systemnode == NULL) goto error; xml_set_attribute(systemnode, "name", machine->gamedrv->name); /* for each cpu */ for (cpu = machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu)) { debug_cpu_comment_group *comments = cpu_get_debug_data(cpu)->comments; xml_data_node *curnode = xml_add_child(systemnode, "cpu", NULL); if (curnode == NULL) goto error; xml_set_attribute(curnode, "tag", cpu->tag); for (j = 0; j < comments->comment_count; j++) { xml_data_node *datanode = xml_add_child(curnode, "comment", xml_normalize_string(comments->comment_info[j]->text)); if (datanode == NULL) goto error; xml_set_attribute_int(datanode, "address", comments->comment_info[j]->address); xml_set_attribute_int(datanode, "color", comments->comment_info[j]->color); sprintf(crc_buf, "%08X", comments->comment_info[j]->crc); xml_set_attribute(datanode, "crc", crc_buf); total_comments++; } } /* flush the file */ if (total_comments > 0) { file_error filerr; astring *fname; mame_file *fp; fname = astring_assemble_2(astring_alloc(), machine->basename, ".cmt"); filerr = mame_fopen(SEARCHPATH_COMMENT, astring_c(fname), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &fp); astring_free(fname); if (filerr == FILERR_NONE) { xml_file_write(root, mame_core_file(fp)); mame_fclose(fp); } } /* free and get out of here */ xml_file_free(root); return 1; error: xml_file_free(root); return 0; } /*------------------------------------------------------------------------- debug_comment_load(_xml) - comment file loading -------------------------------------------------------------------------*/ int debug_comment_load(running_machine *machine) { file_error filerr; mame_file *fp; astring *fname; fname = astring_assemble_2(astring_alloc(), machine->basename, ".cmt"); filerr = mame_fopen(SEARCHPATH_COMMENT, astring_c(fname), OPEN_FLAG_READ, &fp); astring_free(fname); if (filerr != FILERR_NONE) return 0; debug_comment_load_xml(machine, fp); mame_fclose(fp); return 1; } static int debug_comment_load_xml(running_machine *machine, mame_file *fp) { int j; xml_data_node *root, *commentnode, *systemnode, *cpunode, *datanode; const char *name; int version; /* read the file */ root = xml_file_read(mame_core_file(fp), NULL); if (root == NULL) goto error; /* find the config node */ commentnode = xml_get_sibling(root->child, "mamecommentfile"); if (commentnode == NULL) goto error; /* validate the config data version */ version = xml_get_attribute_int(commentnode, "version", 0); if (version != COMMENT_VERSION) goto error; /* check to make sure the file is applicable */ systemnode = xml_get_sibling(commentnode->child, "system"); name = xml_get_attribute_string(systemnode, "name", ""); if (strcmp(name, machine->gamedrv->name) != 0) goto error; for (cpunode = xml_get_sibling(systemnode->child, "cpu"); cpunode; cpunode = xml_get_sibling(cpunode->next, "cpu")) { const device_config *cpu = cputag_get_cpu(machine, xml_get_attribute_string(cpunode, "tag", "")); if (cpu != NULL) { debug_cpu_comment_group *comments = cpu_get_debug_data(cpu)->comments; j = 0; for (datanode = xml_get_sibling(cpunode->child, "comment"); datanode; datanode = xml_get_sibling(datanode->next, "comment")) { /* Malloc the comment */ comments->comment_info[j] = (debug_comment*) malloc(sizeof(debug_comment)); comments->comment_info[j]->address = xml_get_attribute_int(datanode, "address", 0); comments->comment_info[j]->color = xml_get_attribute_int(datanode, "color", 0); sscanf(xml_get_attribute_string(datanode, "crc", 0), "%08X", &comments->comment_info[j]->crc); strcpy(comments->comment_info[j]->text, datanode->value); comments->comment_info[j]->is_valid = 1; j++; } comments->comment_count = j; } } /* free the parser */ xml_file_free(root); return 1; error: if (root) xml_file_free(root); return 0; } /*------------------------------------------------------------------------- debug_comment_exit - saves the comments and frees memory -------------------------------------------------------------------------*/ static void debug_comment_exit(running_machine *machine) { debug_comment_save(machine); debug_comment_free(machine); } /*------------------------------------------------------------------------- debug_comment_free - cleans up memory -------------------------------------------------------------------------*/ static void debug_comment_free(running_machine *machine) { const device_config *cpu; for (cpu = machine->firstcpu; cpu != NULL; cpu = cpu_next(cpu)) { debug_cpu_comment_group *comments = cpu_get_debug_data(cpu)->comments; if (comments != NULL) { int j; for (j = 0; j < comments->comment_count; j++) free(comments->comment_info[j]); comments->comment_count = 0; } } }