mirror of
https://github.com/holub/mame
synced 2025-06-06 04:43:45 +03:00
318 lines
8.6 KiB
C
318 lines
8.6 KiB
C
/***************************************************************************
|
|
|
|
x86log.c
|
|
|
|
x86/x64 code logging helpers.
|
|
|
|
Copyright Aaron Giles
|
|
Released for general use under the MAME license
|
|
Visit http://mamedev.org for licensing and usage restrictions.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "x86log.h"
|
|
|
|
#include <stddef.h>
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
/* comment parameters */
|
|
#define MAX_COMMENTS 4000
|
|
#define MAX_DATA_RANGES 1000
|
|
#define COMMENT_POOL_SIZE (MAX_COMMENTS * 40)
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
TYPE DEFINITIONS
|
|
***************************************************************************/
|
|
|
|
/* code logging info */
|
|
typedef struct _log_comment log_comment;
|
|
struct _log_comment
|
|
{
|
|
x86code * base;
|
|
const char * string;
|
|
};
|
|
|
|
|
|
/* data ranges */
|
|
typedef struct _data_range data_range;
|
|
struct _data_range
|
|
{
|
|
x86code * base;
|
|
x86code * end;
|
|
int size;
|
|
};
|
|
|
|
|
|
/* the code logging context */
|
|
struct _x86log_context
|
|
{
|
|
astring * filename; /* name of the file */
|
|
FILE * file; /* file we are logging to */
|
|
|
|
data_range data_range[MAX_DATA_RANGES]; /* list of data ranges */
|
|
int data_range_count; /* number of data ranges */
|
|
|
|
log_comment comment_list[MAX_COMMENTS]; /* list of comments */
|
|
int comment_count; /* number of live comments */
|
|
|
|
char comment_pool[COMMENT_POOL_SIZE];/* string pool to hold comments */
|
|
char * comment_pool_next; /* pointer to next string pool location */
|
|
};
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
FUNCTION PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static void reset_log(x86log_context *log);
|
|
static int x86log_i386_dasm_one_ex(char *buffer, UINT64 eip, const UINT8 *oprom, int mode);
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
EXTERNAL INTERFACES
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
x86log_create_context - create a new context
|
|
-------------------------------------------------*/
|
|
|
|
x86log_context *x86log_create_context(const char *filename)
|
|
{
|
|
x86log_context *log;
|
|
|
|
/* allocate the log */
|
|
log = malloc_or_die(sizeof(*log));
|
|
memset(log, 0, sizeof(*log));
|
|
|
|
/* allocate the filename */
|
|
log->filename = astring_dupc(filename);
|
|
|
|
/* reset things */
|
|
reset_log(log);
|
|
return log;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
x86log_free_context - release a context
|
|
-------------------------------------------------*/
|
|
|
|
void x86log_free_context(x86log_context *log)
|
|
{
|
|
/* close any open files */
|
|
if (log->file != NULL)
|
|
fclose(log->file);
|
|
|
|
/* free the structure */
|
|
astring_free(log->filename);
|
|
free(log);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
x86log_add_comment - add a comment associated
|
|
with a given code pointer
|
|
-------------------------------------------------*/
|
|
|
|
void x86log_add_comment(x86log_context *log, x86code *base, const char *format, ...)
|
|
{
|
|
char *string = log->comment_pool_next;
|
|
log_comment *comment;
|
|
va_list va;
|
|
|
|
assert(log->comment_count < MAX_COMMENTS);
|
|
assert(log->comment_pool_next + strlen(format) + 256 < log->comment_pool + COMMENT_POOL_SIZE);
|
|
|
|
/* we assume comments are registered in order; enforce this */
|
|
assert(log->comment_count == 0 || base >= log->comment_list[log->comment_count - 1].base);
|
|
|
|
/* if we exceed the maxima, skip it */
|
|
if (log->comment_count >= MAX_COMMENTS)
|
|
return;
|
|
if (log->comment_pool_next + strlen(format) + 256 >= log->comment_pool + COMMENT_POOL_SIZE)
|
|
return;
|
|
|
|
/* do the printf to the string pool */
|
|
va_start(va, format);
|
|
log->comment_pool_next += vsprintf(log->comment_pool_next, format, va) + 1;
|
|
va_end(va);
|
|
|
|
/* fill in the new comment */
|
|
comment = &log->comment_list[log->comment_count++];
|
|
comment->base = base;
|
|
comment->string = string;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
x86log_mark_as_data - mark a given range as
|
|
data for logging purposes
|
|
-------------------------------------------------*/
|
|
|
|
void x86log_mark_as_data(x86log_context *log, x86code *base, x86code *end, int size)
|
|
{
|
|
data_range *data;
|
|
|
|
assert(log->data_range_count < MAX_DATA_RANGES);
|
|
assert(end >= base);
|
|
assert(size == 1 || size == 2 || size == 4 || size == 8);
|
|
|
|
/* we assume data ranges are registered in order; enforce this */
|
|
assert(log->data_range_count == 0 || base > log->data_range[log->data_range_count - 1].end);
|
|
|
|
/* if we exceed the maxima, skip it */
|
|
if (log->data_range_count >= MAX_DATA_RANGES)
|
|
return;
|
|
|
|
/* fill in the new range */
|
|
data = &log->data_range[log->data_range_count++];
|
|
data->base = base;
|
|
data->end = end;
|
|
data->size = size;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
x86log_disasm_code_range - disassemble a range
|
|
of code and reset accumulated information
|
|
-------------------------------------------------*/
|
|
|
|
void x86log_disasm_code_range(x86log_context *log, const char *label, x86code *start, x86code *stop)
|
|
{
|
|
const log_comment *lastcomment = &log->comment_list[log->comment_count];
|
|
const log_comment *curcomment = &log->comment_list[0];
|
|
const data_range *lastdata = &log->data_range[log->data_range_count];
|
|
const data_range *curdata = &log->data_range[0];
|
|
x86code *cur = start;
|
|
|
|
/* print the optional label */
|
|
if (label != NULL)
|
|
x86log_printf(log, "\n%s\n", label);
|
|
|
|
/* loop from the start until the cache top */
|
|
while (cur < stop)
|
|
{
|
|
char buffer[100];
|
|
int bytes;
|
|
|
|
/* skip past any past data ranges */
|
|
while (curdata < lastdata && cur > curdata->end)
|
|
curdata++;
|
|
|
|
/* skip past any past comments */
|
|
while (curcomment < lastcomment && cur > curcomment->base)
|
|
curcomment++;
|
|
|
|
/* if we're in a data range, output the next chunk and continue */
|
|
if (cur >= curdata->base && cur <= curdata->end)
|
|
{
|
|
bytes = curdata->size;
|
|
switch (curdata->size)
|
|
{
|
|
default:
|
|
case 1: sprintf(buffer, "db %02X", *cur); break;
|
|
case 2: sprintf(buffer, "dw %04X", *(UINT16 *)cur); break;
|
|
case 4: sprintf(buffer, "dd %08X", *(UINT32 *)cur); break;
|
|
case 8: sprintf(buffer, "dq %08X%08X", ((UINT32 *)cur)[1], ((UINT32 *)cur)[0]); break;
|
|
}
|
|
}
|
|
|
|
/* if we're not in the data range, skip filler opcodes */
|
|
else if (*cur == 0xcc)
|
|
{
|
|
cur++;
|
|
continue;
|
|
}
|
|
|
|
/* otherwise, do a disassembly of the current instruction */
|
|
else
|
|
{
|
|
#ifdef PTR64
|
|
bytes = x86log_i386_dasm_one_ex(buffer, (FPTR)cur, cur, 64) & DASMFLAG_LENGTHMASK;
|
|
#else
|
|
bytes = x86log_i386_dasm_one_ex(buffer, (FPTR)cur, cur, 32) & DASMFLAG_LENGTHMASK;
|
|
#endif
|
|
}
|
|
|
|
/* if we have a matching comment, output it */
|
|
if (curcomment < lastcomment && cur == curcomment->base)
|
|
{
|
|
/* if we have additional matching comments at the same address, output them first */
|
|
for ( ; curcomment + 1 < lastcomment && cur == curcomment[1].base; curcomment++)
|
|
x86log_printf(log, "%p: %-50s; %s\n", cur, "", curcomment->string);
|
|
x86log_printf(log, "%p: %-50s; %s\n", cur, buffer, curcomment->string);
|
|
}
|
|
|
|
/* if we don't, just print the disassembly and move on */
|
|
else
|
|
x86log_printf(log, "%p: %s\n", cur, buffer);
|
|
|
|
/* advance past this instruction */
|
|
cur += bytes;
|
|
}
|
|
|
|
/* reset our state */
|
|
reset_log(log);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
x86log_printf - manually printf information to
|
|
the log file
|
|
-------------------------------------------------*/
|
|
|
|
void x86log_printf(x86log_context *log, const char *format, ...)
|
|
{
|
|
va_list va;
|
|
|
|
/* open the file, creating it if necessary */
|
|
if (log->file == NULL)
|
|
log->file = fopen(astring_c(log->filename), "w");
|
|
if (log->file == NULL)
|
|
return;
|
|
|
|
/* do the printf */
|
|
va_start(va, format);
|
|
vfprintf(log->file, format, va);
|
|
va_end(va);
|
|
|
|
/* flush the file */
|
|
fflush(log->file);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
LOCAL FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
reset_log - reset the state of the log
|
|
-------------------------------------------------*/
|
|
|
|
static void reset_log(x86log_context *log)
|
|
{
|
|
log->data_range_count = 0;
|
|
log->comment_count = 0;
|
|
log->comment_pool_next = log->comment_pool;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
DISASSEMBLERS
|
|
***************************************************************************/
|
|
|
|
#define i386_dasm_one x86log_i386_dasm_one
|
|
#define i386_dasm_one_ex x86log_i386_dasm_one_ex
|
|
#include "cpu/i386/i386dasm.c"
|