summaryrefslogtreecommitdiffstatshomepage
path: root/docs/source/usingmame/usingmame.rst
blob: 4fa014fe3da67186c097cc343be558eef3fcd31f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
Using MAME
----------

If you want to dive right in and skip the command line, there's a nice graphical
way to use MAME without the need to download and set up a front end. Simply
start MAME with no parameters, by double-clicking the **mame.exe** file or
running it directly from the command line. If you're looking to harness the
full power of MAME, keep reading further.

On macOS and \*nix-based platforms, please be sure to set your font up to match
your locale before starting, otherwise you may not be able to read the text due
to missing glyphs.

If you are a new MAME user, you could find this emulator a bit complex at
first. Let's take a moment to talk about software lists, as they can simplify
matters quite a bit. If the content you are trying to play is a documented
entry on one of the MAME software lists, starting the content is as easy as

    **mame.exe** *<system>* *<software>*

For instance:

    **mame.exe nes metroidu**

will load the USA version of Metroid for the Nintendo Entertainment System.


Alternatively, you could start MAME with

    **mame.exe nes**

and choose the *software list* from the cartridge slot. From there, you could
pick any software list-compatible software you have in your roms folders. Please
note that many older dumps of cartridges and discs may either be bad or require
renaming to match up to the software list in order to work in this way.


If you are loading an arcade board or other non-software list content, things
are only a little more complicated:

The basic usage, from command line, is

    **mame.exe** *<system>* *<media>* *<software>* *<options>*

where

* *<system>* is the short name of the system you want to emulate (e.g. nes, c64,
  etc.)
* *<media>* is the switch for the media you want to load (if it's a cartridge,
  try **-cart** or **-cart1**; if it's a floppy disk, try **-flop** or
  **-flop1**; if it's a CD-ROM, try **-cdrom**)
* *<software>* is the program / game you want to load (and it can be given
  either as the fullpath to the file to load, or as the shortname of the file in
  our software lists)
* *<options>* is any additional command line option for controllers, video,
  sound, etc.

Remember that if you type a *<system>* name which does not correspond to any
emulated system, MAME will suggest some possible choices which are close to
what you typed; and if you don't know which *<media>* switch are available, you
can always launch

    **mame.exe** *<system>* **-listmedia**

If you don't know what *<options>* are available, there are a few things you
can do. First of all, you can check the command line options section of this
manual. You can also try one of the many :ref:`frontends` available for MAME.


Alternatively, you should keep in mind the following command line options,
which might be very useful on occasion:


    **mame.exe -help**

gives a basic summary of command line options for MAME, as explained above.


    **mame.exe -showusage**

gives you the (quite long) list of available command line options for MAME.
The main options are described, in the :ref:`mame-commandline-universal` section
of this manual.


    **mame.exe -showconfig**

gives you a (quite long) list of available configuration options for MAME.
These options can always be modified at command line, or by editing them in
mame.ini which is the main configuration file for MAME. You can find a
description of some configuration options in the
:ref:`mame-commandline-universal` section of the manual (in most cases, each
configuration option has a corresponding command line option to configure and
modify it).


    **mame.exe -createconfig**

creates a brand new **mame.ini** file, with default configuration settings.
Notice that mame.ini is basically a plain text file, so you can open it with any
text editor (e.g. Notepad, Emacs or TextEdit) and configure every option you
need. However, no particular tweaks are needed to start, so you can leave most
of the options unaltered.

If you execute **mame -createconfig** when you already have an existing
mame.ini from a previous MAME version, MAME automatically updates the
pre-existing mame.ini by copying changed options into it.


Once you are more confident with MAME options, you may want to adjust the
configuration of your setup a bit more. In this case, keep in mind the order in
which options are read; see :ref:`advanced-multi-CFG` for details.
44 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
/*********************************************************************

    debugcon.c

    Debugger console engine.

*********************************************************************/

#include "emu.h"
#include "debugcon.h"
#include "debugcpu.h"
#include "debugvw.h"
#include "textbuf.h"
#include "debugger.h"
#include <ctype.h>



/***************************************************************************
    CONSTANTS
***************************************************************************/

#define CONSOLE_BUF_SIZE    (1024 * 1024)
#define CONSOLE_MAX_LINES   (CONSOLE_BUF_SIZE / 20)

#define ERRORLOG_BUF_SIZE   (1024 * 1024)
#define ERRORLOG_MAX_LINES  (ERRORLOG_BUF_SIZE / 20)



/***************************************************************************
    TYPE DEFINITIONS
***************************************************************************/

struct debug_command
{
	debug_command * next;
	char            command[32];
	const char *    params;
	const char *    help;
	void            (*handler)(running_machine &machine, int ref, int params, const char **param);
	void            (*handler_ex)(int ref);
	UINT32          flags;
	int             ref;
	int             minparams;
	int             maxparams;
};



/***************************************************************************
    LOCAL VARIABLES
***************************************************************************/

static text_buffer *console_textbuf;
static text_buffer *errorlog_textbuf;

static debug_command *commandlist;



/***************************************************************************
    FUNCTION PROTOTYPES
***************************************************************************/

static void debug_console_exit(running_machine &machine);



/***************************************************************************

    Initialization and tear down

***************************************************************************/

/*-------------------------------------------------
    debug_console_init - initializes the console
    system
-------------------------------------------------*/

void debug_console_init(running_machine &machine)
{
	/* allocate text buffers */
	console_textbuf = text_buffer_alloc(CONSOLE_BUF_SIZE, CONSOLE_MAX_LINES);
	if (!console_textbuf)
		return;

	errorlog_textbuf = text_buffer_alloc(ERRORLOG_BUF_SIZE, ERRORLOG_MAX_LINES);
	if (!errorlog_textbuf)
		return;

	/* print the opening lines */
	debug_console_printf(machine, "%s debugger version %s\n", emulator_info::get_appname(), build_version);
	debug_console_printf(machine, "Currently targeting %s (%s)\n", machine.system().name, machine.system().description);

	/* request callback upon exiting */
	machine.add_notifier(MACHINE_NOTIFY_EXIT, machine_notify_delegate(FUNC(debug_console_exit), &machine));
}


/*-------------------------------------------------
    debug_console_exit - frees the console
    system
-------------------------------------------------*/

static void debug_console_exit(running_machine &machine)
{
	/* free allocated memory */
	if (console_textbuf)
		text_buffer_free(console_textbuf);
	console_textbuf = nullptr;

	if (errorlog_textbuf)
		text_buffer_free(errorlog_textbuf);
	errorlog_textbuf = nullptr;

	/* free the command list */
	commandlist = nullptr;
}



/***************************************************************************

    Command Handling

***************************************************************************/

/*-------------------------------------------------
    trim_parameter - executes a
    command
-------------------------------------------------*/

static void trim_parameter(char **paramptr, int keep_quotes)
{
	char *param = *paramptr;
	size_t len = strlen(param);
	int repeat;

	/* loop until all adornments are gone */
	do
	{
		repeat = 0;

		/* check for begin/end quotes */
		if (len >= 2 && param[0] == '"' && param[len - 1] == '"')
		{
			if (!keep_quotes)
			{
				param[len - 1] = 0;
				param++;
				len -= 2;
			}
		}

		/* check for start/end braces */
		else if (len >= 2 && param[0] == '{' && param[len - 1] == '}')
		{
			param[len - 1] = 0;
			param++;
			len -= 2;
			repeat = 1;
		}

		/* check for leading spaces */
		else if (len >= 1 && param[0] == ' ')
		{
			param++;
			len--;
			repeat = 1;
		}

		/* check for trailing spaces */
		else if (len >= 1 && param[len - 1] == ' ')
		{
			param[len - 1] = 0;
			len--;
			repeat = 1;
		}
	} while (repeat);

	*paramptr = param;
}


/*-------------------------------------------------
    internal_execute_command - executes a
    command
-------------------------------------------------*/

static CMDERR internal_execute_command(running_machine &machine, int execute, int params, char **param)
{
	debug_command *cmd, *found = nullptr;
	int i, foundcount = 0;
	char *p, *command;
	size_t len;

	/* no params is an error */
	if (params == 0)
		return CMDERR_NONE;

	/* the first parameter has the command and the real first parameter; separate them */
	for (p = param[0]; *p && isspace((UINT8)*p); p++) { }
	for (command = p; *p && !isspace((UINT8)*p); p++) { }
	if (*p != 0)
	{
		*p++ = 0;
		for ( ; *p && isspace((UINT8)*p); p++) { }
		if (*p != 0)
			param[0] = p;
		else
			params = 0;
	}
	else
	{
		params = 0;
		param[0] = nullptr;
	}

	/* search the command list */
	len = strlen(command);
	for (cmd = commandlist; cmd != nullptr; cmd = cmd->next)
		if (!strncmp(command, cmd->command, len))
		{
			foundcount++;
			found = cmd;
			if (strlen(cmd->command) == len)
			{
				foundcount = 1;
				break;
			}
		}

	/* error if not found */
	if (!found)
		return MAKE_CMDERR_UNKNOWN_COMMAND(0);
	if (foundcount > 1)
		return MAKE_CMDERR_AMBIGUOUS_COMMAND(0);

	/* NULL-terminate and trim space around all the parameters */
	for (i = 1; i < params; i++)
		*param[i]++ = 0;

	/* now go back and trim quotes and braces and any spaces they reveal*/
	for (i = 0; i < params; i++)
		trim_parameter(&param[i], found->flags & CMDFLAG_KEEP_QUOTES);

	/* see if we have the right number of parameters */
	if (params < found->minparams)
		return MAKE_CMDERR_NOT_ENOUGH_PARAMS(0);
	if (params > found->maxparams)
		return MAKE_CMDERR_TOO_MANY_PARAMS(0);

	/* execute the handler */
	if (execute)
		(*found->handler)(machine, found->ref, params, (const char **)param);
	return CMDERR_NONE;
}


/*-------------------------------------------------
    internal_parse_command - parses a command
    and either executes or just validates it
-------------------------------------------------*/

static CMDERR internal_parse_command(running_machine &machine, const char *original_command, int execute)
{
	char command[MAX_COMMAND_LENGTH], parens[MAX_COMMAND_LENGTH];
	char *params[MAX_COMMAND_PARAMS] = { nullptr };
	CMDERR result;
	char *command_start;
	char *p, c = 0;

	/* make a copy of the command */
	strcpy(command, original_command);

	/* loop over all semicolon-separated stuff */
	for (p = command; *p != 0; )
	{
		int paramcount = 0, foundend = FALSE, instring = FALSE, isexpr = FALSE, parendex = 0;

		/* find a semicolon or the end */
		for (params[paramcount++] = p; !foundend; p++)
		{
			c = *p;
			if (instring)
			{
				if (c == '"' && p[-1] != '\\')
					instring = FALSE;
			}
			else
			{
				switch (c)
				{
					case '"':   instring = TRUE; break;
					case '(':
					case '[':
					case '{':   parens[parendex++] = c; break;
					case ')':   if (parendex == 0 || parens[--parendex] != '(') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
					case ']':   if (parendex == 0 || parens[--parendex] != '[') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
					case '}':   if (parendex == 0 || parens[--parendex] != '{') return MAKE_CMDERR_UNBALANCED_PARENS(p - command); break;
					case ',':   if (parendex == 0) params[paramcount++] = p; break;
					case ';':   if (parendex == 0) foundend = TRUE; break;
					case '-':   if (parendex == 0 && paramcount == 1 && p[1] == '-') isexpr = TRUE; *p = c; break;
					case '+':   if (parendex == 0 && paramcount == 1 && p[1] == '+') isexpr = TRUE; *p = c; break;
					case '=':   if (parendex == 0 && paramcount == 1) isexpr = TRUE; *p = c; break;
					case 0:     foundend = TRUE; break;
					default:    *p = tolower((UINT8)c); break;
				}
			}
		}

		/* check for unbalanced parentheses or quotes */
		if (instring)
			return MAKE_CMDERR_UNBALANCED_QUOTES(p - command);
		if (parendex != 0)
			return MAKE_CMDERR_UNBALANCED_PARENS(p - command);

		/* NULL-terminate if we ended in a semicolon */
		p--;
		if (c == ';') *p++ = 0;

		/* process the command */
		command_start = params[0];

		/* allow for "do" commands */
		if (tolower((UINT8)command_start[0] == 'd') && tolower((UINT8)command_start[1] == 'o') && isspace((UINT8)command_start[2]))
		{
			isexpr = TRUE;
			command_start += 3;
		}

		/* if it smells like an assignment expression, treat it as such */
		if (isexpr && paramcount == 1)
		{
			try
			{
				UINT64 expresult;
				parsed_expression expression(debug_cpu_get_visible_symtable(machine), command_start, &expresult);
			}
			catch (expression_error &err)
			{
				return MAKE_CMDERR_EXPRESSION_ERROR(err);
			}
		}
		else
		{
			result = internal_execute_command(machine, execute, paramcount, &params[0]);
			if (result != CMDERR_NONE)
				return MAKE_CMDERR(CMDERR_ERROR_CLASS(result), command_start - command);
		}
	}
	return CMDERR_NONE;
}


/*-------------------------------------------------
    debug_console_execute_command - execute a
    command string
-------------------------------------------------*/

CMDERR debug_console_execute_command(running_machine &machine, const char *command, int echo)
{
	CMDERR result;

	/* echo if requested */
	if (echo)
		debug_console_printf(machine, ">%s\n", command);

	/* parse and execute */
	result = internal_parse_command(machine, command, TRUE);

	/* display errors */
	if (result != CMDERR_NONE)
	{
		if (!echo)
			debug_console_printf(machine, ">%s\n", command);
		debug_console_printf(machine, " %*s^\n", CMDERR_ERROR_OFFSET(result), "");
		debug_console_printf(machine, "%s\n", debug_cmderr_to_string(result));
	}

	/* update all views */
	if (echo)
	{
		machine.debug_view().update_all();
		machine.debugger().refresh_display();
	}
	return result;
}


/*-------------------------------------------------
    debug_console_validate_command - validate a
    command string
-------------------------------------------------*/

CMDERR debug_console_validate_command(running_machine &machine, const char *command)
{
	return internal_parse_command(machine, command, FALSE);
}


/*-------------------------------------------------
    debug_console_register_command - register a
    command handler
-------------------------------------------------*/

void debug_console_register_command(running_machine &machine, const char *command, UINT32 flags, int ref, int minparams, int maxparams, void (*handler)(running_machine &machine, int ref, int params, const char **param))
{
	debug_command *cmd;

	assert_always(machine.phase() == MACHINE_PHASE_INIT, "Can only call debug_console_register_command() at init time!");
	assert_always((machine.debug_flags & DEBUG_FLAG_ENABLED) != 0, "Cannot call debug_console_register_command() when debugger is not running");

	cmd = auto_alloc_clear(machine, <debug_command>());

	/* fill in the command */
	strcpy(cmd->command, command);
	cmd->flags = flags;
	cmd->ref = ref;
	cmd->minparams = minparams;
	cmd->maxparams = maxparams;
	cmd->handler = handler;

	/* link it */
	cmd->next = commandlist;
	commandlist = cmd;
}



/***************************************************************************

    Error Handling

***************************************************************************/

/*-------------------------------------------------
    debug_cmderr_to_string - return a friendly
    string for a given command error
-------------------------------------------------*/

const char *debug_cmderr_to_string(CMDERR error)
{
	switch (CMDERR_ERROR_CLASS(error))
	{
		case CMDERR_UNKNOWN_COMMAND:        return "unknown command";
		case CMDERR_AMBIGUOUS_COMMAND:      return "ambiguous command";
		case CMDERR_UNBALANCED_PARENS:      return "unbalanced parentheses";
		case CMDERR_UNBALANCED_QUOTES:      return "unbalanced quotes";
		case CMDERR_NOT_ENOUGH_PARAMS:      return "not enough parameters for command";
		case CMDERR_TOO_MANY_PARAMS:        return "too many parameters for command";
		case CMDERR_EXPRESSION_ERROR:       return "error in assignment expression";
		default:                            return "unknown error";
	}
}



/***************************************************************************

    Console Management

***************************************************************************/

/*-------------------------------------------------
    debug_console_printf - printfs the given
    arguments using the format to the debug
    console
-------------------------------------------------*/

void CLIB_DECL debug_console_printf(running_machine &machine, const char *format, ...)
{
	std::string buffer;
	va_list arg;

	va_start(arg, format);
	strvprintf(buffer, format, arg);
	va_end(arg);

	text_buffer_print(console_textbuf, buffer.c_str());

	/* force an update of any console views */
	machine.debug_view().update_all(DVT_CONSOLE);
}


/*-------------------------------------------------
    debug_console_vprintf - printfs the given
    arguments using the format to the debug
    console
-------------------------------------------------*/

void CLIB_DECL debug_console_vprintf(running_machine &machine, const char *format, va_list args)
{
	std::string buffer;

	strvprintf(buffer, format, args);
	text_buffer_print(console_textbuf, buffer.c_str());

	/* force an update of any console views */
	machine.debug_view().update_all(DVT_CONSOLE);
}


/*-------------------------------------------------
    debug_console_printf_wrap - printfs the given
    arguments using the format to the debug
    console
-------------------------------------------------*/

void CLIB_DECL debug_console_printf_wrap(running_machine &machine, int wrapcol, const char *format, ...)
{
	std::string buffer;
	va_list arg;

	va_start(arg, format);
	strvprintf(buffer, format, arg);
	va_end(arg);

	text_buffer_print_wrap(console_textbuf, buffer.c_str(), wrapcol);

	/* force an update of any console views */
	machine.debug_view().update_all(DVT_CONSOLE);
}


/*-------------------------------------------------
    debug_console_get_textbuf - return a pointer
    to the console text buffer
-------------------------------------------------*/

text_buffer *debug_console_get_textbuf(void)
{
	return console_textbuf;
}


/*-------------------------------------------------
    debug_errorlog_write_line - writes a line to
    the errorlog ring buffer
-------------------------------------------------*/

void debug_errorlog_write_line(const running_machine &machine, const char *line)
{
	if (errorlog_textbuf)
		text_buffer_print(errorlog_textbuf, line);

	/* force an update of any log views */
	machine.debug_view().update_all(DVT_LOG);
}


/*-------------------------------------------------
    debug_errorlog_get_textbuf - return a pointer
    to the errorlog text buffer
-------------------------------------------------*/

text_buffer *debug_errorlog_get_textbuf(void)
{
	return errorlog_textbuf;
}