mame/src/emu/debug/debugcmt.c
Aaron Giles 63d10ee9bf Massive API cleanup/change. The primary goal is that all CPU-
related APIs now take a device pointer instead of an index.
All functions that take a CPU device are prefixed with cpu_*
All functions that are globally related to cpu execution
are prefixed with cpuexec_*. Below is a list of some of the 
mappings:

  cpu_boost_interleave     -> cpuexec_boost_interleave
  cpunum_suspend           -> cpu_suspend
  cpunum_resume            -> cpu_resume
  cpunum_is_suspended      -> cpu_is_suspended
  cpunum_get_clock         -> cpu_get_clock
  cpunum_set_clock         -> cpu_set_clock
  cpunum_get_clockscale    -> cpu_get_clockscale
  cpunum_set_clockscale    -> cpu_set_clockscale
  cpunum_get_localtime     -> cpu_get_local_time
  cpunum_gettotalcycles    -> cpu_get_total_cycles
  activecpu_eat_cycles     -> cpu_eat_cycles
  activecpu_adjust_icount  -> cpu_adjust_icount
  cpu_trigger              -> cpuexec_trigger
  cpu_triggertime          -> cpuexec_triggertime
  cpunum_set_input_line    -> cpu_set_input_line
  cpunum_set_irq_callback  -> cpu_set_irq_callback

In addition, a number of functions retain the same name but
now require a specific CPU parameter to be passed in:

  cpu_yield
  cpu_spin
  cpu_spinuntil_time
  cpu_spinuntil_int
  cpu_spinuntil_trigger
  cpu_triggerint

Merged cpuint.c into cpuexec.c. One side-effect of this
change is that driver reset callbacks are called AFTER the
CPUs and devices are reset. This means that if you make
changes to the CPU state and expect the reset vectors to
recognize the changes in your reset routine, you will need
to manually reset the CPU after making the change (since it
has already been reset).

Added a number of inline helper functions to cpuintrf.h for
managing addresses

Removed cpu_gettotalcpu(). This information is rarely needed
outside of the core and can be obtained by looking at the
machine->cpu[] array.

Changed CPU interrupt acknowledge callbacks to pass a CPU 
device instead of machine/cpunum pair.

Changed VBLANK and periodic timer callbacks to pass a CPU
device instead of machine/cpunum pair.

Renamed all information getters from cpu_* to cpu_get_* and
from cputype_* to cputype_get_*.
2008-11-13 06:59:57 +00:00

578 lines
16 KiB
C

/***************************************************************************
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 "deprecat.h"
#include "xmlfile.h"
#include "debugcmt.h"
#include "debugcpu.h"
#include "debugvw.h"
#include "info.h"
#include <zlib.h>
/***************************************************************************
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;
};
typedef struct _comment_group comment_group;
struct _comment_group
{
int comment_count;
UINT32 change_count;
debug_comment *comment_info[DEBUG_COMMENT_MAX_NUM];
};
/***************************************************************************
GLOBAL VARIABLES
***************************************************************************/
static comment_group *debug_comments;
/***************************************************************************
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(void);
/***************************************************************************
Initialization
***************************************************************************/
/*-------------------------------------------------------------------------
debug_comment_init - initializes the comment memory and
loads any existing comment file
-------------------------------------------------------------------------*/
int debug_comment_init(running_machine *machine)
{
int numcpu;
for (numcpu = 0; numcpu < ARRAY_LENGTH(machine->cpu); numcpu++)
if (machine->cpu[numcpu] == NULL)
break;
if (numcpu > 0)
{
/* allocate enough comment groups for the total # of cpu's */
debug_comments = (comment_group*) auto_malloc(numcpu * sizeof(comment_group));
memset(debug_comments, 0, numcpu * sizeof(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(int cpu_num, offs_t addr, const char *comment, rgb_t color, UINT32 c_crc)
{
int i = 0;
int insert_point = debug_comments[cpu_num].comment_count;
int match = 0;
/* Create a new item to insert into the list */
debug_comment *insert_me = (debug_comment*) malloc_or_die(sizeof(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 < debug_comments[cpu_num].comment_count; i++)
{
if (insert_me->address < debug_comments[cpu_num].comment_info[i]->address)
{
insert_point = i;
break;
}
else if (insert_me->address == debug_comments[cpu_num].comment_info[i]->address &&
insert_me->crc == debug_comments[cpu_num].comment_info[i]->crc)
{
insert_point = i;
match = 1;
break;
}
}
/* Got an exact match? Just replace */
if (match == 1)
{
free(debug_comments[cpu_num].comment_info[insert_point]);
debug_comments[cpu_num].comment_info[insert_point] = insert_me;
debug_comments[cpu_num].change_count++;
/* force an update of disassembly views */
debug_view_update_type(DVT_DISASSEMBLY);
return 1;
}
/* Otherwise insert */
/* First, shift the list down */
for (i = debug_comments[cpu_num].comment_count; i >= insert_point; i--)
debug_comments[cpu_num].comment_info[i] = debug_comments[cpu_num].comment_info[i-1];
/* do the insertion */
debug_comments[cpu_num].comment_info[insert_point] = insert_me;
debug_comments[cpu_num].comment_count++;
debug_comments[cpu_num].change_count++;
/* force an update of disassembly views */
debug_view_update_type(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(int cpu_num, offs_t addr, UINT32 c_crc)
{
int i;
int remove_index = -1;
for (i = 0; i < debug_comments[cpu_num].comment_count; i++)
{
if (debug_comments[cpu_num].comment_info[i]->address == addr) /* got an address match */
{
if (debug_comments[cpu_num].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(debug_comments[cpu_num].comment_info[remove_index]);
for (i = remove_index; i < debug_comments[cpu_num].comment_count-1; i++)
{
debug_comments[cpu_num].comment_info[i] = debug_comments[cpu_num].comment_info[i+1];
}
debug_comments[cpu_num].comment_count--;
debug_comments[cpu_num].change_count++;
/* force an update of disassembly views */
debug_view_update_type(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(int cpu_num, offs_t addr, UINT32 c_crc)
{
int i;
/* inefficient - should use bsearch - but will be a little tricky with multiple comments per addr */
for (i = 0; i < debug_comments[cpu_num].comment_count; i++)
{
if (debug_comments[cpu_num].comment_info[i]->address == addr) /* got an address match */
{
/* now check the bank information to be sure */
if (debug_comments[cpu_num].comment_info[i]->crc == c_crc)
{
return debug_comments[cpu_num].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(int cpu_num)
{
return debug_comments[cpu_num].comment_count;
}
/*-------------------------------------------------------------------------
debug_comment_get_change_count - returns the change counter
for a given cpu number
-------------------------------------------------------------------------*/
UINT32 debug_comment_get_change_count(int cpu_num)
{
return debug_comments[cpu_num].change_count;
}
/*-------------------------------------------------------------------------
debug_comment_all_change_count - returns the change counter
for all cpu's
-------------------------------------------------------------------------*/
UINT32 debug_comment_all_change_count(void)
{
int i ;
UINT32 retVal = 0;
for (i = 0; i < ARRAY_LENGTH(Machine->cpu); i++)
if (Machine->cpu[i] != NULL)
retVal += debug_comments[i].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(offs_t address)
{
const debug_cpu_info *info = debug_get_cpu_info(cpunum_get_active());
int i;
UINT32 crc;
UINT8 opbuf[64], argbuf[64];
char buff[256];
offs_t numbytes;
int maxbytes = cpu_get_max_opcode_bytes(Machine->activecpu);
UINT32 addrmask = (debug_get_cpu_info(cpunum_get_active()))->space[ADDRESS_SPACE_PROGRAM].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(address + i, 1, FALSE);
argbuf[i] = debug_read_opcode(address + i, 1, TRUE);
}
numbytes = cpu_dasm(Machine->activecpu, buff, address & addrmask, opbuf, argbuf) & DASMFLAG_LENGTHMASK;
numbytes = ADDR2BYTE(numbytes, info, ADDRESS_SPACE_PROGRAM);
crc = crc32(0, argbuf, numbytes);
return crc;
}
/*-------------------------------------------------------------------------
debug_comment_dump - debugging function to dump junk to the command line
-------------------------------------------------------------------------*/
void debug_comment_dump(int cpu_num, offs_t addr)
{
int i;
int ff = 0;
if (addr == -1)
{
for (i = 0; i < debug_comments[cpu_num].comment_count; i++)
{
if (debug_comments[cpu_num].comment_info[i]->is_valid)
{
logerror("%d : %s (%d %d)\n", i, debug_comments[cpu_num].comment_info[i]->text,
debug_comments[cpu_num].comment_info[i]->address,
debug_comments[cpu_num].comment_info[i]->crc);
}
}
}
else
{
UINT32 c_crc = debug_comment_get_opcode_crc32(addr);
for (i = 0; i < debug_comments[cpu_num].comment_count; i++)
{
if (debug_comments[cpu_num].comment_info[i]->address == addr) /* got an address match */
{
/* now check the bank information to be sure */
if (debug_comments[cpu_num].comment_info[i]->crc == c_crc)
{
logerror("%d : %s (%d %d)\n", addr,
debug_comments[cpu_num].comment_info[addr]->text,
debug_comments[cpu_num].comment_info[addr]->address,
debug_comments[cpu_num].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 i, j;
char crc_buf[20];
xml_data_node *root = xml_file_create();
xml_data_node *commentnode, *systemnode;
int total_comments = 0;
/* if we don't have a root, bail */
if (!root)
return 0;
/* create a comment node */
commentnode = xml_add_child(root, "mamecommentfile", NULL);
if (!commentnode)
goto error;
xml_set_attribute_int(commentnode, "version", COMMENT_VERSION);
/* create a system node */
systemnode = xml_add_child(commentnode, "system", NULL);
if (!systemnode)
goto error;
xml_set_attribute(systemnode, "name", machine->gamedrv->name);
/* for each cpu */
for (i = 0; i < ARRAY_LENGTH(machine->cpu); i++)
if (machine->cpu[i] != NULL)
{
xml_data_node *curnode = xml_add_child(systemnode, "cpu", NULL);
if (!curnode)
goto error;
xml_set_attribute_int(curnode, "num", i);
for (j = 0; j < debug_comments[i].comment_count; j++)
{
xml_data_node *datanode = xml_add_child(curnode, "comment", xml_normalize_string(debug_comments[i].comment_info[j]->text));
if (!datanode)
goto error;
xml_set_attribute_int(datanode, "address", debug_comments[i].comment_info[j]->address);
xml_set_attribute_int(datanode, "color", debug_comments[i].comment_info[j]->color);
sprintf(crc_buf, "%08X", debug_comments[i].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 i, 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)
goto error;
/* find the config node */
commentnode = xml_get_sibling(root->child, "mamecommentfile");
if (!commentnode)
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;
i = 0;
for (cpunode = xml_get_sibling(systemnode->child, "cpu"); cpunode; cpunode = xml_get_sibling(cpunode->next, "cpu"))
{
j = 0;
for (datanode = xml_get_sibling(cpunode->child, "comment"); datanode; datanode = xml_get_sibling(datanode->next, "comment"))
{
/* Malloc the comment */
debug_comments[i].comment_info[j] = (debug_comment*) malloc(sizeof(debug_comment));
debug_comments[i].comment_info[j]->address = xml_get_attribute_int(datanode, "address", 0);
debug_comments[i].comment_info[j]->color = xml_get_attribute_int(datanode, "color", 0);
sscanf(xml_get_attribute_string(datanode, "crc", 0), "%08X", &debug_comments[i].comment_info[j]->crc);
strcpy(debug_comments[i].comment_info[j]->text, datanode->value);
debug_comments[i].comment_info[j]->is_valid = 1;
j++;
}
debug_comments[i].comment_count = j;
i++;
}
/* 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();
}
/*-------------------------------------------------------------------------
debug_comment_free - cleans up memory
-------------------------------------------------------------------------*/
static void debug_comment_free(void)
{
int i, j;
for (i = 0; i < ARRAY_LENGTH(Machine->cpu); i++)
if (Machine->cpu[i] != NULL)
{
for (j = 0; j < debug_comments[i].comment_count; j++)
{
free(debug_comments[i].comment_info[j]);
}
debug_comments[i].comment_count = 0;
}
}