Game Drivers: Difference between revisions

From MAMEDEV Wiki
(New page: Probably the most common question when it comes down to how MAME works is: how do I write a driver? In order to understand how to write a driver, you need to understand how drivers are con...)
 
No edit summary
 
(One intermediate revision by one other user not shown)
Line 1: Line 1:
Probably the most common question when it comes down to how MAME works is: how do I write a driver? In order to understand how to write a driver, you need to understand how drivers are connected into MAME, and what the various driver-related pieces of the puzzle are. This article should provide you with a basic overview of what goes into a driver at the topmost level. Future articles will address many of the details beyond that.  
Probably the most common question when it comes down to how MAME works is: how do I write a driver? In order to understand how to write a driver, you need to understand how drivers are connected into MAME, and what the various driver-related pieces of the puzzle are. This article should provide you with a basic overview of what goes into a driver at the topmost level. Future articles will address many of the details beyond that.


So, to get started, you will want to take a peek at the code in '''driver.c'''. Essentially all this module does is produce a list of pointers to all the drivers supported by MAME. The problem is that for each driver, we need to first declare it:  
So, to get started, you will want to take a peek at the code in '''src/mame/mame.lst'''. Essentially all this module does is produce a list of references to all the drivers supported by MAME:  


  extern struct GameDriver driver_puckman;
  ...
  extern struct GameDriver driver_puckmana;
  @source:tvgame.cpp
tvgame                          // 2011
  ...
  ...


and after all the drivers have been declared, then we need to declare a list of the drivers like this:
The '''@source:''' directive tells the build scripts the name of the source file where the following system drivers are defined.  The following lines (up to the next '''@source:''' directive) list the "short names" of the system drivers defined in that source file.
 
const struct GameDriver *drivers[] =
{
    &driver_puckman,
    &driver_puckmana,
    ...,
    0
};


This is a very annoying fact of using C, because it means for each driver we would need to add two things to '''driver.c''', a declaration at the top, and an entry in the list at the bottom. In fact, we did this for many years before we came up with some whizzy preprocessor magic to simplify things. If you're a C guru, you can probably figure it out, but if not, never fear. The important thing is that to specify a driver to be added to the master list, you only need to declare it once using the special DRIVER macro, like so:
With those lines inserted into the list alongside all the other entries, your driver is now referenced by the master driver list in MAME!  Of course, this now means that you actually need to have a system driver with the same short name living elsewhere in MAME in order to successfully link the application. So how exactly do you define a system driver?


  DRIVER( puckman )
The system driver structure contains some basic information used to find or list the system, as well as references to the classes, structures and functions that describe the hardware. System driver structures are instantiated using the '''GAME'''/'''GAMEL''', '''COMP''', '''CONS''' and '''SYST''' macros (used for arcade games, computers, consoles and other systems, respectively). If you look at the bottom of any of the files in the '''src/mame/drivers''' directory, you will see a number of these macros describing the drivers defined in that file:
DRIVER( puckmana )
  ...


With those entries inserted into the list alongside all the other entries, your driver is now officially referenced by the master driver list in MAME! Of course, this now means that you actually need to have a GameDriver object with the same name living elsewhere in MAME in order to successfully link the application. So how exactly do you define a game driver?
CONS( 2011, tvgame, 0, 0, tvgame, tvgame, tvgame_state, empty_init, "Mr. Isizu", "Z80 TV Game System", 0 )


The basic GameDriver structure is pretty simple. It provides a very basic list of information about a given driver, which mostly amounts to descriptive data (game name, manufacturer, year, etc.) and a few pointers to other structures which actually describe the hardware of the particular game. Rather than defining and populating a GameDriver structure yourself, you should use one of the provided macros which fill in all the details in a more easy-to-use fashion. If you look at the bottom of any of the files in the '''drivers''' directory, you will see a number of these macros specifying the drivers defined in that file:  
The main disadvantage to using a macro like this is that it is not immediately obvious what each argument is for. A number of them even have the same value ('''tvgame'''). This is something you get used to, however.  Going through the arguments from left to right, here is what is being defined:


  GAME( 1980, puckman0,       pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The first argument ('''2011''') is the release year for the system. In general, if the system shows a copyright year, it's used as the definitive year. It isn't necessarily completely accurate, but it's usually close. If the system doesn't display a copyright notice with a year, we need to rely on other sources like manuals, magazine advertisements and catalogues.
  GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
...


The main disadvantage to using a macro like this is that it is not immediately obvious what each parameter is for. A number of them have the same name even ("pacman"). This is something you get used to, however. Going through the entries from left to right, here is what is being defined:
* The second argument ('''tvgame''') is the short name of the system driver itself. This must match the short name listed in '''src/mame/mame.lst'''. The actual name of the variable will have '''driver_''' prepended to it.  In this example, the system driver variable's name will be '''driver_tvgame'''.  Short names are restricted to sixteen characters length, using lowercase English letters (“a” to “z”), digits (“0” to “9”) and underscores (“_”). The short name is when searching for external files required to run the system (like ROMs or artwork), and when creating files to save persistent state related to the system (like input mappings and non-volatile RAM contents). It’s also used for launching the system from the command line.


GAME( <u>1980</u>, puckman, 0,      pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The third and fourth arguments (both '''0''' in this example) are used for parent/clone and software compatibility relationships. They're set to zero when they're not used.  Parent/clone relationships are used to group multiple versions of the same system.  The parent will generally be the latest English-language release with the widest release. Note that parent/clone relationships mainly exist to group different versions of the same system and are somewhat arbitrary.  There’s no implication that the parent is the original or somehow superior to its clones.
  GAME( <u>1980</u>, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The first parameter (1980) is obviously the year that MAME specifies for the game. In general, we try to use the copyright year as shown in the game itself as the "definitive" year. It may not be perfectly accurate, but it is usually close, and this definition allows us to at least be consistent across games. For games that don't display a copyright, we have to guess at the year using anecdotal evidence collected elsewhere.
* The fifth argument ('''tvgame''') is the name of the machine configuration function, which creates the devices in the system and makes connections between them.  Machine configuration functions are worth a whole article themselves, so they won't be discussed in much detail here. It is quite common for multiple system drivers to all share one machine configuration function, especially for arcade platforms that had a common set of hardware for a number of games. The machine configuration function is a member function of the state class (seventh argument) with a standard signature.  In this case, the machine configuration function will be:


  GAME( 1980, <u>puckman</u>,  0,      pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
  void tvgame_state::tvgame(machine_config &config)
GAME( 1980, <u>puckmana</u>, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The second parameter (puckman/puckmana) is the name of the driver itself. This name should match the name you use in the DRIVER() macro in '''driver.c'''. Both the GAME() macro here and the DRIVER() macro in driver.c will prepend "driver_" before the name you specify here in order to keep a consistent naming scheme on all the game drivers in the system. Thus, in the example above, this will expand to driver_puckman and driver_puckmana, which will match the naming scheme described in the previous section.  
* The sixth argument (also '''tvgame''') is the name of the input port definitions. Input ports will be described elsewhere, but in short, they describe various external inputs to the system (for example controls and DIP switches).  Input ports are constructed at runtime by code, and this macro prepends '''construct_ioport_''' to the specified name to get the function name to use. In the example above, the macro will generate a pointer to the function '''construct_ioport_tvgame'''.


GAME( 1980, puckman, <u>0</u>,      pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The seventh argument ('''tvgame_state''') is the name of the driver state class. This class must derive from '''driver_device''', which is itself a specialisation of '''device_t'''.  The '''driver_device''' class provides virtual member functions that the state class can override to handle different phases of an emulation session. The state class will also have member functions to build machine configurations, describe address maps, provide glue logic, and emulate functionality that isn't provided by reusable device classes.
  GAME( 1980, puckmana, <u>puckman</u>, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The third parameter (0/puckman) is the name of the "parent" driver. In MAME, a "parent" driver is the driver for the most recent version of a game. The parent driver always specifies 0 for this parameter. All other versions of a game are considered "clones", and must specify the name of the parent in this field. In the example above, puckman is the parent driver, and puckmana is the clone driver. As with the driver name parameter, the GAME macro will automatically prepend "driver_" before the name you specify here.  
* The eighth argument ('''empty_init''') is the name of an initialisation function. In this case it's unused, but some systems use this for tasks like unscrambling ROM code.  Like the machine configuration function, this must be a member function of the state class. The '''empty_init''' member function is provided by '''driver_device''' for systems that don't need to do anything in an initialisation function.


GAME( 1980, puckman,  0,      <u>pacman</u>, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The ninth argument ('''"Mr. Isizu"''') is called the “manufacturer”.  It’s the brand name displayed in the internal user interface, and used for searching and filtering. This may be the manufacturer, licensee, distributor, publisher or developer as appropriate.  In this case it’s the developer/publisher of the homebrew system.
  GAME( 1980, puckmana, puckman, <u>pacman</u>, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The fourth parameter (pacman) is the name of the machine driver, which describes the hardware that makes up the arcade machine. Machine drivers are worth a whole article themselves, so they won't be discussed in much detail here. It is quite common for multiple game drivers to all share one machine driver, especially on systems that had a common set of hardware for a number of games. One interesting aspect of machine drivers is that, unlike game drivers which are represented by a GameDriver struct, machine drivers are not represented by a struct but rather are constructed at runtime by code that is generated by macros. This means that this fourth parameter in the GAME macro points not to a struct, but rather to a "constructor" function. Again, in order to maintain consistency, the GAME() macro will prepend "construct_" to the front of any name you specify here. In the example above, the macro will expand this parameter into a pointer to the function construct_pacman, which will be defined later.  
* The tenth argument ('''"Z80 TV Game System"''') is the display name of the system.  For game systems, this is taken from the title screen if possible.  If the system doesn’t have a title screen, it may be taken from branding on the unit itself, a manufacturers nameplate, documentation, packaging, marketing material, or identification strings in the ROM. Display names must be unique across all systems in MAME.  For identically-named systems, disambiguation text is added to the name. This may be the manufacturer, revision/version, release region, hardware platform, or an arbitrary “set number”.


  GAME( 1980, puckman0,       pacman, <u>pacman</u>, 0, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The eleventh and final argument ('''0''' in this case) is a flags bit field.  It can be used to flag certain requirements or emulation issues.  In this case there are no flags set.  Many of these flags cause a warning to be displayed on starting emulation.  Additional flags can be set in the state class.  Possible values include:
  GAME( 1980, puckmana, puckman, pacman, <u>pacman</u>, 0, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
** '''MACHINE_NOT_WORKING''' – the system is not fully emulated. This causes a red warning to be displayed when starting emulation.
** '''MACHINE_SUPPORTS_SAVE''' – MAME supports saving and loading state for this system, and rewind features.  If this is not set, MAME may warn the user that save states are not supported. If this is set, the '''-autosave''' option will automatically load system state on start and save it on exit.
** '''MACHINE_NO_COCKTAIL''' – screen flipping in cocktail mode is not emulated (yellow colour).
** '''MACHINE_IS_BIOS_ROOT''' – this represents a system with no software loaded.  This is often set for a system representing an arcade mainboard that supports interchangeable games when no game is present.
** '''MACHINE_REQUIRES_ARTWORK''' – warns the user that the machine requires external artwork files to be usable (yellow colour).
** '''MACHINE_CLICKABLE_ARTWORK''' – ensures a mouse cursor is displayed when the mouse is captured so the user can interact with the system by clicking artwork elements.
** '''MACHINE_UNOFFICIAL''' – this system represents a common user modification to a system that was not supported by the manufacturer.
** '''MACHINE_NO_SOUND_HW''' – displays a notice when starting emulation that the system has no sound output hardware, so it’s normal to hear no sound.
** '''MACHINE_MECHANICAL''' – warns the user that the system relies on mechanical features.  For example this is set for pinball games, coin pushers and crane games.
** '''MACHINE_IS_INCOMPLETE''' – warns the user that the system is an incomplete prototype.
** '''MACHINE_UNEMULATED_PROTECTION''' – warns the user that the system has unemulated protection features (red colour).
** '''MACHINE_WRONG_COLORS''' and '''MACHINE_IMPERFECT_COLORS''' – displays a red or yellow warning indicating that colours are incorrect when starting.  These flags are mutually exclusive.
** '''MACHINE_IMPERFECT_GRAPHICS''' – warns the user that graphics are imperfectly emulated (yellow colour).
** '''MACHINE_NO_SOUND''' and '''MACHINE_IMPERFECT_SOUND''' – warns the user that sound hardware is unemulated (red colour) or imperfectly emulated (yellow colour). These flags are mutually exclusive.
** '''MACHINE_IMPERFECT_CONTROLS''' – warns the user that controls are imperfectly emulated (yellow colour).
** '''MACHINE_NODEVICE_MICROPHONE''', '''MACHINE_NODEVICE_PRINTER''' and '''MACHINE_NODEVICE_LAN''' – warn the user about unemulated hardware features (red colour).  Additional unemulated hardware features may indicated with emulation flags on the state class.
** '''MACHINE_IMPERFECT_TIMING'''  – warns the user that the system has timing issues (yellow colour).  This could manifest in various ways, including incorrect speed.
** '''MACHINE_IS_SKELETON''' – combination of '''MACHINE_NO_SOUND''' and '''MACHINE_NOT_WORKING'''.
** '''MACHINE_IS_SKELETON_MECHANICAL''' – combination of '''MACHINE_IS_SKELETON''', '''MACHINE_MECHANICAL''' and '''MACHINE_REQUIRES_ARTWORK'''.


The fifth parameter (also pacman) is the name of the input port definitions. Input ports will be described elsewhere, but in short, they describe how all of the various inputs to the game (controls and DIP switches) are mapped. Like the machine driver, input ports are constructed at runtime by code, and this macro prepends "consruct_ipt_" to the front of the name you specify here. In the example above, the macro will generate a pointer to the function construct_ipt_pacman.
For mostly historical reasons, the macro for arcade games has slightly different parameters. Here are some examples:


  GAME( 1980, puckman,  0,       pacman, pacman, <u>0</u>, ROT90, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
  GAME( 1994, superx,  0,     superx, superx, rshark_state, empty_init, ROT270, "Dooyong (NTC license)",     "Super-X (NTC)",     MACHINE_SUPPORTS_SAVE )
  GAME( 1980, puckmana, puckman, pacman, pacman, <u>0</u>, ROT90, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )
  GAME( 1994, superxm, superx, superx, superx, rshark_state, empty_init, ROT270, "Dooyong (Mitchell license)", "Super-X (Mitchell)", MACHINE_SUPPORTS_SAVE )


The sixth parameter (0 in this case, but present in a number of other drivers) is the name of the driver-specific initialization function. This function is intended to be used to perform code decryption, if necessary, or to dynamically alter the memory maps of the CPUs in ways that are specific to the particular game driver. The reason this is useful is because most of the game's hardware is controlled by the machine driver definition, which is often shared among multiple game drivers. But in reality, even when games ran on the same hardware, there were often minor differences between them. The init function allows for driver-specific tweaks to be applied before the virtual hardware is set up. The macro will prepend "init_" to the front of the name you give here.  
* The first argument ('''1994''' in both cases) is the release year.


GAME( 1980, puckman,  0,      pacman, pacman, 0, <u>ROT90</u>, "Namco", "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The second argument ('''superx'''/'''superxm''') is the short name of the system. This is a good time to mention a convention: where there are multiple versions of a system, the clones’ short names will be the parent’s short name with suffixes appended.
  GAME( 1980, puckmana, puckman, pacman, pacman, 0, <u>ROT90</u>, "Namco", "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The seventh parameter (ROT90 here) specifies the orientation of the game's primary monitor. All games are actually designed to output their display to a standard TV-shaped display. Games like '''Pac Man''', however, have the monitor rotated 90 degrees clockwise in the cabinet. All the graphics in the game are internally rotated to account for this so that it looks right-side up after rotation. Similarly, there are a number of games that were designed to have the monitor rotated 90 degrees counterclockwise. There are also some games that were designed to be reflected from a mirror, and so everything is drawn in mirror image. All of these transformations can be described by this orientation parameter.  
* The third argument is the parent.  It’s '''0''' for '''superx''' because, which has no parent, and '''superx''' for '''superxm''' – this means that '''superx''' is the parent of '''superxm''' and '''superxm''' is a clone of '''superx'''.


The orientation parameter actually consists of 3 bits, which are all controllable independently. The first bit (ORIENTATION_FLIP_X) indicates that the display should be drawn in a mirror image left-to-right. The second bit (ORIENTATION_FLIP_Y) indicates that the display should be drawn in a mirror image top-to-bottom. And the third bit (ORIENTATION_SWAP_XY) indicates that the display should be drawn in a mirror image across the diagonal from the top-left corner to the bottom-right corner. If you think about things hard enough, you will realize that combining these features together, under the assumption that SWAP_XY happens first, can describe all possible rotations and mirrorings. For example, a 90 degree clockwise rotation is a SWAP_XY combined with a FLIP_X.
* The fourth argument ('''superx''' in both cases) is the name of the machine configuration function. Both versions of this game use the same machine configuration function, as they run on identical hardware. Remember the the machine configuration function is a member of the state class – in this case it will be:


For the most part, this complexity is hidden from you, so you can use the predefined combination macros ROT0, ROT90, ROT180, and ROT270 to indicate no rotation, 90 degrees clockwise rotation, 180 degrees rotation (flip X and Y), and 90 degrees counterclockwise rotation, respectively.
void rshark_state::superx(machine_config &config)


GAME( 1980, puckman,  0,      pacman, pacman, 0, ROT90, <u>"Namco"</u>, "PuckMan (Japan set 1)", GAME_SUPPORTS_SAVE )
* The fifth argument (also '''superx''' in both cases) is the name of the input port definitions. The macros will generate a pointers to the function '''construct_ioport_superx'''.
  GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, <u>"Namco"</u>, "PuckMan (Japan set 2)", GAME_SUPPORTS_SAVE )


The 8th parameter ("Namco") is the name of the manufacturer. Unfortunately, this isn't always as easy to determine as it seems. Many times, games were licensed from one manufacturer to another. For example, '''Pac Man''' was licensed to Midway for release in the U.S. In this case, the manufacturer should be specified as "[Namco] (Midway license)". Things get even messier for bootlegs. Half the time, bootleggers just tweaked a character or two in the name, and aren't legitimate companies worth mentioning. For the most part, bootlegs use "bootleg" or "hack" as the manufacturer name.  
* The sixth argument ('''rshark_state''' in both cases) is the name of the driver state class. Both these systems use the same state class.


GAME( 1980, puckman,  0,      pacman, pacman, 0, ROT90, "Namco", <u>"PuckMan (Japan set 1)"</u>, GAME_SUPPORTS_SAVE )
* The seventh argument ('''empty_init''' in both cases) is the name of an initialisation function. Neither of these systems need to do anything in an initialisation function, so they use '''empty_init''' from '''driver_device'''.
  GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", <u>"PuckMan (Japan set 2)"</u>, GAME_SUPPORTS_SAVE )


The 9th parameter ("PuckMan (Japan set X)") is the full friendly name of the game. This name should represent the name of the game as it is displayed in the attract mode, if possible, followed in parentheses by any version or region information. In this case, the region (Japan) is specified, along with some distinguishing details between multiple versions. In this case, as with many arcade games, the actual version numbers are never specified, so we are left to make up our own versioning scheme without really knowing the full details of the differences.  
* The eighth argument ('''ROT270''' in both cases) specifies orientation transforms to be applied to all video screens in the system.  Many systems have screens mounted vertically, and some systems’ screens are viewed via mirrors. Possible values are '''ROT0''', '''ROT90''', '''ROT180''' and '''ROT270''' to rotate by 0°, 90°, 180° or 270°, '''ORIENTATION_FLIP_X''' and '''ORIENTATION_FLIP_Y''' to flip horizontally or vertically, and '''ORIENTATION_SWAP_XY''' to flip around a diagonal line from upper left to lower right (the '''ROT''' constants are made by combining '''ORIENTATION_FLIP_''' constants).  This argument is logically combined with the system flags – it’s a separate argument for arcade games for historical reasons.  For systems that have multiple screens in different orientations, orientation flags are applied to the screens in the machine configuration function.  Both these systems use a single 4:3 monitor rotated 270° so the raster is scanned top to bottom left to right.


GAME( 1980, puckman,  0,      pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)", <u>GAME_SUPPORTS_SAVE</u> )
* The ninth argument ('''Dooyong (NTC license)''' and '''Dooyong (Mitchell license)''') is the manufacturer. The systems (software and hardware) were developed by Dooyong, but licensed to different companies (NTC and Mitchell) for distribution.
  GAME( 1980, puckmana, puckman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)", <u>GAME_SUPPORTS_SAVE</u> )


The final parameter (GAME_SUPPORTS_SAVE) is one or more flags describing the status of the driver. If no flags apply, a value of 0 is simply placed in this field. If multiple flags are desired, they are ORed together. The current set of flags and their meanings are listed below:
* The tenth argument ('''Super-X (NTC)''' and '''Super-X (Mitchell)''') is the display name.  Since both these systems have the same name, we add the licensee for disambiguation.


* '''GAME_NOT_WORKING''' &mdash; means that the game is not fully working; this could be due to anything from incomplete emulation to a very subtle bug that prevents the game from being played through to the end
* The eleventh and final argument ('''MACHINE_SUPPORTS_SAVE''' in both cases) is the system flags bit field.  Saving and restoring state is supported for both these systems in MAME.
* '''GAME_UNEMULATED_PROTECTION''' &mdash; means that there is some form of anti-piracy protection in the game which is preventing it from working fully; this flag is almost always used in conjunction with GAME_NOT_WORKING
* '''GAME_WRONG_COLORS''' &mdash; means that the color decoding used by this game is not understood at all; often this is due to a missing color PROM which is usually needed to understand how the colors are mapped
* '''GAME_IMPERFECT_COLORS''' &mdash; means that color decoding is generally understood, but there may be some edge cases or other issues that prevent the color from being correct in all cases, such as missing shadows or hilights
* '''GAME_IMPREFECT_GRAPHICS''' &mdash; means that there are some known problems with the graphics display; again, this can range from very obvious problems (such as nothing being displayed!) down to very subtle problems (incorrect priorities and other issues)  
* '''GAME_NO_COCKTAIL''' &mdash; means that the game supports cocktail mode, but either the mechanism for enabling it is not understood or else the driver author was too lazy to implement support in the video system for it
* '''GAME_NO_SOUND''' &mdash; means that the game has no sound support whatsoever; often this is because the sound hardware is not known
* '''GAME_IMPERFECT_SOUND''' &mdash; means that the sound emulation for a game is incorrect in some way; this can range from some missing sound chip emulation to missing filters to any of a number of other audio problems
* '''GAME_SUPPORTS_SAVE''' &mdash; means that the driver has been verified at the code level and via testing to support save states; if you use the -autosave option on the command line, games with this flag will be automatically saved when you exit and will automatically pick up from that save state the next time you run them
* '''NOT_A_DRIVER''' &mdash; means that the given game entry is not a game in itself, but rather a BIOS entry that serves as a parent to a collection of games that share the same core BIOS

Latest revision as of 13:42, 8 March 2020

Probably the most common question when it comes down to how MAME works is: how do I write a driver? In order to understand how to write a driver, you need to understand how drivers are connected into MAME, and what the various driver-related pieces of the puzzle are. This article should provide you with a basic overview of what goes into a driver at the topmost level. Future articles will address many of the details beyond that.

So, to get started, you will want to take a peek at the code in src/mame/mame.lst. Essentially all this module does is produce a list of references to all the drivers supported by MAME:

...
@source:tvgame.cpp
tvgame                          // 2011
...

The @source: directive tells the build scripts the name of the source file where the following system drivers are defined. The following lines (up to the next @source: directive) list the "short names" of the system drivers defined in that source file.

With those lines inserted into the list alongside all the other entries, your driver is now referenced by the master driver list in MAME! Of course, this now means that you actually need to have a system driver with the same short name living elsewhere in MAME in order to successfully link the application. So how exactly do you define a system driver?

The system driver structure contains some basic information used to find or list the system, as well as references to the classes, structures and functions that describe the hardware. System driver structures are instantiated using the GAME/GAMEL, COMP, CONS and SYST macros (used for arcade games, computers, consoles and other systems, respectively). If you look at the bottom of any of the files in the src/mame/drivers directory, you will see a number of these macros describing the drivers defined in that file:

CONS( 2011, tvgame, 0, 0, tvgame, tvgame, tvgame_state, empty_init, "Mr. Isizu", "Z80 TV Game System", 0 )

The main disadvantage to using a macro like this is that it is not immediately obvious what each argument is for. A number of them even have the same value (tvgame). This is something you get used to, however. Going through the arguments from left to right, here is what is being defined:

  • The first argument (2011) is the release year for the system. In general, if the system shows a copyright year, it's used as the definitive year. It isn't necessarily completely accurate, but it's usually close. If the system doesn't display a copyright notice with a year, we need to rely on other sources like manuals, magazine advertisements and catalogues.
  • The second argument (tvgame) is the short name of the system driver itself. This must match the short name listed in src/mame/mame.lst. The actual name of the variable will have driver_ prepended to it. In this example, the system driver variable's name will be driver_tvgame. Short names are restricted to sixteen characters length, using lowercase English letters (“a” to “z”), digits (“0” to “9”) and underscores (“_”). The short name is when searching for external files required to run the system (like ROMs or artwork), and when creating files to save persistent state related to the system (like input mappings and non-volatile RAM contents). It’s also used for launching the system from the command line.
  • The third and fourth arguments (both 0 in this example) are used for parent/clone and software compatibility relationships. They're set to zero when they're not used. Parent/clone relationships are used to group multiple versions of the same system. The parent will generally be the latest English-language release with the widest release. Note that parent/clone relationships mainly exist to group different versions of the same system and are somewhat arbitrary. There’s no implication that the parent is the original or somehow superior to its clones.
  • The fifth argument (tvgame) is the name of the machine configuration function, which creates the devices in the system and makes connections between them. Machine configuration functions are worth a whole article themselves, so they won't be discussed in much detail here. It is quite common for multiple system drivers to all share one machine configuration function, especially for arcade platforms that had a common set of hardware for a number of games. The machine configuration function is a member function of the state class (seventh argument) with a standard signature. In this case, the machine configuration function will be:
void tvgame_state::tvgame(machine_config &config)
  • The sixth argument (also tvgame) is the name of the input port definitions. Input ports will be described elsewhere, but in short, they describe various external inputs to the system (for example controls and DIP switches). Input ports are constructed at runtime by code, and this macro prepends construct_ioport_ to the specified name to get the function name to use. In the example above, the macro will generate a pointer to the function construct_ioport_tvgame.
  • The seventh argument (tvgame_state) is the name of the driver state class. This class must derive from driver_device, which is itself a specialisation of device_t. The driver_device class provides virtual member functions that the state class can override to handle different phases of an emulation session. The state class will also have member functions to build machine configurations, describe address maps, provide glue logic, and emulate functionality that isn't provided by reusable device classes.
  • The eighth argument (empty_init) is the name of an initialisation function. In this case it's unused, but some systems use this for tasks like unscrambling ROM code. Like the machine configuration function, this must be a member function of the state class. The empty_init member function is provided by driver_device for systems that don't need to do anything in an initialisation function.
  • The ninth argument ("Mr. Isizu") is called the “manufacturer”. It’s the brand name displayed in the internal user interface, and used for searching and filtering. This may be the manufacturer, licensee, distributor, publisher or developer as appropriate. In this case it’s the developer/publisher of the homebrew system.
  • The tenth argument ("Z80 TV Game System") is the display name of the system. For game systems, this is taken from the title screen if possible. If the system doesn’t have a title screen, it may be taken from branding on the unit itself, a manufacturers nameplate, documentation, packaging, marketing material, or identification strings in the ROM. Display names must be unique across all systems in MAME. For identically-named systems, disambiguation text is added to the name. This may be the manufacturer, revision/version, release region, hardware platform, or an arbitrary “set number”.
  • The eleventh and final argument (0 in this case) is a flags bit field. It can be used to flag certain requirements or emulation issues. In this case there are no flags set. Many of these flags cause a warning to be displayed on starting emulation. Additional flags can be set in the state class. Possible values include:
    • MACHINE_NOT_WORKING – the system is not fully emulated. This causes a red warning to be displayed when starting emulation.
    • MACHINE_SUPPORTS_SAVE – MAME supports saving and loading state for this system, and rewind features. If this is not set, MAME may warn the user that save states are not supported. If this is set, the -autosave option will automatically load system state on start and save it on exit.
    • MACHINE_NO_COCKTAIL – screen flipping in cocktail mode is not emulated (yellow colour).
    • MACHINE_IS_BIOS_ROOT – this represents a system with no software loaded. This is often set for a system representing an arcade mainboard that supports interchangeable games when no game is present.
    • MACHINE_REQUIRES_ARTWORK – warns the user that the machine requires external artwork files to be usable (yellow colour).
    • MACHINE_CLICKABLE_ARTWORK – ensures a mouse cursor is displayed when the mouse is captured so the user can interact with the system by clicking artwork elements.
    • MACHINE_UNOFFICIAL – this system represents a common user modification to a system that was not supported by the manufacturer.
    • MACHINE_NO_SOUND_HW – displays a notice when starting emulation that the system has no sound output hardware, so it’s normal to hear no sound.
    • MACHINE_MECHANICAL – warns the user that the system relies on mechanical features. For example this is set for pinball games, coin pushers and crane games.
    • MACHINE_IS_INCOMPLETE – warns the user that the system is an incomplete prototype.
    • MACHINE_UNEMULATED_PROTECTION – warns the user that the system has unemulated protection features (red colour).
    • MACHINE_WRONG_COLORS and MACHINE_IMPERFECT_COLORS – displays a red or yellow warning indicating that colours are incorrect when starting. These flags are mutually exclusive.
    • MACHINE_IMPERFECT_GRAPHICS – warns the user that graphics are imperfectly emulated (yellow colour).
    • MACHINE_NO_SOUND and MACHINE_IMPERFECT_SOUND – warns the user that sound hardware is unemulated (red colour) or imperfectly emulated (yellow colour). These flags are mutually exclusive.
    • MACHINE_IMPERFECT_CONTROLS – warns the user that controls are imperfectly emulated (yellow colour).
    • MACHINE_NODEVICE_MICROPHONE, MACHINE_NODEVICE_PRINTER and MACHINE_NODEVICE_LAN – warn the user about unemulated hardware features (red colour). Additional unemulated hardware features may indicated with emulation flags on the state class.
    • MACHINE_IMPERFECT_TIMING – warns the user that the system has timing issues (yellow colour). This could manifest in various ways, including incorrect speed.
    • MACHINE_IS_SKELETON – combination of MACHINE_NO_SOUND and MACHINE_NOT_WORKING.
    • MACHINE_IS_SKELETON_MECHANICAL – combination of MACHINE_IS_SKELETON, MACHINE_MECHANICAL and MACHINE_REQUIRES_ARTWORK.

For mostly historical reasons, the macro for arcade games has slightly different parameters. Here are some examples:

GAME( 1994, superx,  0,      superx, superx, rshark_state, empty_init, ROT270, "Dooyong (NTC license)",      "Super-X (NTC)",      MACHINE_SUPPORTS_SAVE )
GAME( 1994, superxm, superx, superx, superx, rshark_state, empty_init, ROT270, "Dooyong (Mitchell license)", "Super-X (Mitchell)", MACHINE_SUPPORTS_SAVE )
  • The first argument (1994 in both cases) is the release year.
  • The second argument (superx/superxm) is the short name of the system. This is a good time to mention a convention: where there are multiple versions of a system, the clones’ short names will be the parent’s short name with suffixes appended.
  • The third argument is the parent. It’s 0 for superx because, which has no parent, and superx for superxm – this means that superx is the parent of superxm and superxm is a clone of superx.
  • The fourth argument (superx in both cases) is the name of the machine configuration function. Both versions of this game use the same machine configuration function, as they run on identical hardware. Remember the the machine configuration function is a member of the state class – in this case it will be:
void rshark_state::superx(machine_config &config)
  • The fifth argument (also superx in both cases) is the name of the input port definitions. The macros will generate a pointers to the function construct_ioport_superx.
  • The sixth argument (rshark_state in both cases) is the name of the driver state class. Both these systems use the same state class.
  • The seventh argument (empty_init in both cases) is the name of an initialisation function. Neither of these systems need to do anything in an initialisation function, so they use empty_init from driver_device.
  • The eighth argument (ROT270 in both cases) specifies orientation transforms to be applied to all video screens in the system. Many systems have screens mounted vertically, and some systems’ screens are viewed via mirrors. Possible values are ROT0, ROT90, ROT180 and ROT270 to rotate by 0°, 90°, 180° or 270°, ORIENTATION_FLIP_X and ORIENTATION_FLIP_Y to flip horizontally or vertically, and ORIENTATION_SWAP_XY to flip around a diagonal line from upper left to lower right (the ROT constants are made by combining ORIENTATION_FLIP_ constants). This argument is logically combined with the system flags – it’s a separate argument for arcade games for historical reasons. For systems that have multiple screens in different orientations, orientation flags are applied to the screens in the machine configuration function. Both these systems use a single 4:3 monitor rotated 270° so the raster is scanned top to bottom left to right.
  • The ninth argument (Dooyong (NTC license) and Dooyong (Mitchell license)) is the manufacturer. The systems (software and hardware) were developed by Dooyong, but licensed to different companies (NTC and Mitchell) for distribution.
  • The tenth argument (Super-X (NTC) and Super-X (Mitchell)) is the display name. Since both these systems have the same name, we add the licensee for disambiguation.
  • The eleventh and final argument (MACHINE_SUPPORTS_SAVE in both cases) is the system flags bit field. Saving and restoring state is supported for both these systems in MAME.