Add a MESS Skeleton Driver

This article will teach you how to create a skeleton driver for MESS, the first step for your own driver.

For a very basic driver, we need to add one file (the actual driver) and modify two files, so that MESS knows about your driver.

Creating the driver

As an example, we'll create a driver for the hypothetical system “MX-1290” from Epson. Create a new file in mess/drivers and name it mx1290.c. Open this file and add the line

#include "emu.h"

Every driver includes this file. Then, add a line corresponding to the CPU needed

#include "cpu/z80/z80.h"

Now we basically need to add various macros to describe our system and functions that get called by the MAME/MESS core.

First, we add a driver state using the C++ class

class mx1290_state : public driver_device
{
public:
	mx1290_state(running_machine &machine, const driver_device_config_base &config)
		: driver_device(machine, config) { }
 
};

In here you will put all devices and local variables (if any).

Now, we add some functions:

DRIVER_INIT_MEMBER( mx1290_state, mx1290)
{
}

This function is called once at the beginning of the emulation. We don't need to put code in here now. Next, we'll add two functions for the video hardware:

VIDEO_START_MEMBER( mx1290_state, mx1290 )
{
}
 
UINT32 mx1290_state::screen_update_mx1290(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
    return 0;
}

Those get called at the start and when the graphics need updating.

An example READ8_MEMBER

READ8_MEMBER( mx1290_state::mx1290_port1_r )
{
    logerror("Read from port1 @%x\n", activecpu_get_pc());
    return 0xff;
}

An example WRITE8_MEMBER

WRITE8_MEMBER( mx1290_state::mx1290_port1_w )
{
    logerror("Write to port1 @%x: %02x\n", activecpu_get_pc(), data);
}

We will reference these two functions later in our address maps, which describe the memory layout of the system.

The memory map of our system

static ADDRESS_MAP_START( mx1290_mem, AS_PROGRAM, 8, mx1290_state )
    AM_RANGE( 0x0000, 0x0fff ) AM_ROM AM_REGION("maincpu", 0)
    AM_RANGE( 0x1000, 0x1fff ) AM_RAM
ADDRESS_MAP_END

The I/O port map of our system

static ADDRESS_MAP_START( mx1290_io, AS_IO, 8, mx1290_state )
    ADDRESS_MAP_GLOBAL_MASK(0xff)
    AM_RANGE( 0x00, 0x0f ) AM_READWRITE( mx1290_port1_r, mx1290_port1_w )
ADDRESS_MAP_END

We have defined two ranges now in program memory now: A ROM region, where the firmware or BIOS is located, and a RAM region. We also added a map for the I/O ports, when the system writes or reads from the address 0x00 to 0x0f our READ8_MEMBER mx1290_port1_r and the WRITE8_MEMBER mx1290_port1_w are called. The read member expects data from us, and the write member will give us data.

We also need to define some input ports, i. e. Buttons etc. that the system has. This is done using a macro:

static INPUT_PORTS_START( mx1290 )
INPUT_PORTS_END

Another macro finally describes the whole system:

A machine driver definition

static MACHINE_DRIVER_START( mx1290, mx1290_state )
    /* basic machine hardware */
    MCFG_CPU_ADD_TAG("maincpu", Z80, MAIN_CLOCK / 4)
    MCFG_CPU_PROGRAM_MAP(mx1290_mem)
    MCFG_CPU_IO_MAP(mx1290_io)
 
    /* video hardware */
    MCFG_SCREEN_ADD("screen", RASTER)
    MCFG_SCREEN_REFRESH_RATE(50)
    MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500))
    MCFG_SCREEN_SIZE(640, 480)
    MCFG_SCREEN_VISIBLE_AREA(0, 640-1, 0, 480-1)
    MCFG_SCREEN_UPDATE_DRIVER(mx1290_state, screen_update_mx1290)
 
    MCFG_PALETTE_ADD_BLACK_AND_WHITE("palette")
 
    MCFG_VIDEO_START_OVERRIDE(mx1290_state, mx1290)
MACHINE_DRIVER_END

This macro tells MESS what CPU our system uses and references our memory maps and video update functions. Note that we use a placeholder for the frequency our system runs at, this allows us to accurately describe how the clock is derived. For this to work, you need to #define MAIN_CLOCK 4000000 somewhere in an include file or at the top of the driver file. In our example, the real CPU clock would then be 1 MHz.

Finally, we can add some ROM loading macros.

A ROM loading definition

ROM_START( mx1290 )
    ROM_REGION( 0x1000, "maincpu", 0 )
    ROM_LOAD( "mx_v11.u22", 0x0000, 0x1000, CRC(6d84119d) SHA1(de60ead727b9317154742efd8e1206e9f9bb695b) )
ROM_END

This macro creates a memory region with the size 0x1000 in maincpu (that we referenced in our memory map) and loads one ROM with the filename mx_v11.u22 into this region at offset 0x0000, length 0x1000, CRC32 of 6d84119d and SHA1 of de60ead727b9317154742efd8e1206e9f9bb695b.

We are almost finished now, we only need some glue macro to combine everything above now. This is doing using either the COMP or CONS macros, which are used for computers and consoles respectively. Lets say this is a computer system, so we can add:

/*    YEAR  NAME   PARENT  COMPAT  MACHINE INPUT   CLASS          INIT  COMPANY  FULLNAME   FLAGS */
COMP( 1986, mx1290,     0,      0, mx1290, mx1290, mx1290_state, mx1290, "Epson", "MX-1290", MACHINE_IS_SKELETON )

Name is the system name and used later when we add the actual driver to MESS. Machine is the MACHINE_DRIVER macro and Input are our input definitions.

The full driver now looks like this:

An example driver

/* Epson MX-1290 driver by Anonymous */
 
#include "emu.h"
#include "cpu/z80/z80.h"
 
#define MAIN_CLOCK 4000000 /* 4 MHz */
 
class mx1290_state : public driver_device
{
public:
	mx1290_state(running_machine &machine, const driver_device_config_base &config)
		: driver_device(machine, config) { }
 
 
    DECLARE_DRIVER_INIT(mx1290);
    DECLARE_VIDEO_START(mx1290);
    DECLARE_READ8_MEMBER(mx1290_port1_r);
    DECLARE_WRITE8_MEMBER(mx1290_port1_w);
    UINT32 screen_update_mx1290(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
};
 
/* Driver initialization */
DRIVER_INIT_MEMBER( mx1290_state, mx1290)
{
}
 
/* Video hardware */
VIDEO_START_MEMBER( mx1290_state, mx1290 )
{
}
 
UINT32 mx1290_state::screen_update_mx1290(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect)
{
    return 0;
}
 
/* Port 1 code */
READ8_MEMBER( mx1290_state::mx1290_port1_r )
{
    logerror("Read from port1 @%x\n", activecpu_get_pc());
    return 0xff;
}
 
WRITE8_MEMBER( mx1290_state::mx1290_port1_w )
{
    logerror("Write to port1 @%x: %02x\n", activecpu_get_pc(), data);
}
 
/* Address maps */
static ADDRESS_MAP_START( mx1290_mem, AS_PROGRAM, 8, mx1290_state )
    AM_RANGE( 0x0000, 0x0fff ) AM_ROM AM_REGION("maincpu", 0)
    AM_RANGE( 0x1000, 0x1fff ) AM_RAM
ADDRESS_MAP_END
 
static ADDRESS_MAP_START(mx1290_io, AS_IO, 8, mx1290_state )
    ADDRESS_MAP_GLOBAL_MASK( 0xff )
    AM_RANGE( 0x00, 0x0f ) AM_READWRITE( mx1290_port1_r, mx1290_port1_w )
ADDRESS_MAP_END
 
/* Input ports */
static INPUT_PORTS_START( mx1290 )
INPUT_PORTS_END
 
/* Machine driver */
static MACHINE_DRIVER_START( mx1290, mx1290_state )
    /* basic machine hardware */
    MCFG_CPU_ADD_TAG("maincpu", Z80, MAIN_CLOCK / 4)
    MCFG_CPU_PROGRAM_MAP(mx1290_mem)
    MCFG_CPU_IO_MAP(mx1290_io)
 
    /* video hardware */
    MCFG_SCREEN_ADD("screen", RASTER)
    MCFG_SCREEN_REFRESH_RATE(50)
    MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500))
    MCFG_SCREEN_SIZE(640, 480)
    MCFG_SCREEN_VISIBLE_AREA(0, 640-1, 0, 480-1)
    MCFG_SCREEN_UPDATE_DRIVER(mx1290_state, screen_update_mx1290)
 
    MCFG_PALETTE_ADD_BLACK_AND_WHITE("palette")
 
    MCFG_VIDEO_START_OVERRIDE(mx1290_state, mx1290)
MACHINE_DRIVER_END
 
/* ROM definition */
ROM_START( mx1290 )
    ROM_REGION( 0x1000, "maincpu", 0 )
    ROM_LOAD( "mx_v11.u22", 0x0000, 0x1000, CRC(6d84119d) SHA1(de60ead727b9317154742efd8e1206e9f9bb695b) )
ROM_END
 
/* Driver */
 
/*    YEAR  NAME   PARENT  COMPAT  MACHINE INPUT   CLASS          INIT  COMPANY  FULLNAME   FLAGS */
COMP( 1986, mx1290,     0,      0, mx1290, mx1290, mx1290_state, mx1290, "Epson", "MX-1290", MACHINE_IS_SKELETON )

Adding the driver

Adding the driver to MESS involves editing two files, src/mame/mess.lst and scripts/target/mame/mess.lua.

scripts/target/mame/mess.lua

Open the file and scroll to the section “manufacturer-specific groupings for drivers”. Look for the manufacturer of your emulated system. If you can't find it, add a new section. Lets add our system from Epson, our driver is called mx1290.c. Scroll down to the last manufacturer and add the following lines:

createMESSProjects(_target, _subtarget, "epson")
files {
    MAME_DIR .. "src/mess/drivers/mx1290.c",
}

If the manufacturer already exists, just add the second line only.

src/mame/mess.lst

Open the file and scroll to an appropriate section (consoles are listed first, computers after that, grouped by manufacturer again). Add the following line:

mx1290

Note that this is the system name that you specified with the COMP or CONS macros and independent of the name of your source file.

Testing your new driver

Make a clean build and compile MESS with mingw32-make SUBTARGET=mess. We enabled symbols, as it helps greatly with debugging in gdb. To run your new driver, type mess mx1290.

Further development

Note that usually the VIDEO_START and VIDEO_UPDATE functions will be moved into their own file in mess/video/mx1290.c and the READ/WRITE handlers are normally found in mess/machine/mx1290.c. The function prototypes for those will then be in a file called mess/includes/mx1290.h which is included into the main driver file.