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.
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 to MESS involves editing two files, src/mame/mess.lst
and 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.
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.
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
.
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.