Guided tour through MESS
MESS is lacking what many software projects, in particular open-source projects, also fail to give: a good introduction to find into the concepts of what you can see there as program code. This is rarely a malevolence of the implemententors; it is usually caused by one or more of these reasons:
- lack of time: We have to fix bug#18274 and have no more time to write the docs
- outdated: Docs have been written with quite some efforts, but since version x.y most of them do not apply any longer, and no one really has the energy to make them catch up (Sisyphus)
- different level of understanding: What may be plain to see for the developer is often cryptic to the beginner.
- hidden knowledge: Similar to the last point, the developer does not even know that he assumes knowledge which the reader need not have.
I'll try to fight the last two points with this guided tour through MESS. We will discuss on the example of the TI-99/4A emulation how MESS works and how to implement new parts.
MESS is a sister project of MAME, the Multi Arcade Machine Emulator. It uses the same software core but adds new structures and support for typical devices used with computers. MAME introduced some terms which are also used inside MESS (which is reasonable, or we would need a dictionary all the time) but which can be confusing or misleading sometimes. Accordingly, I will present some of the more important terms here.
|Game||Refers to the emulated system (consider the viewpoint of MAME); GAME_NOT_WORKING is a flag to mark a system as non-functional|
|Driver||The complete system; ti99_4a is a driver, as is geneve.|
|Device||An instance of the device_t class. Managed by the core.|
|Image||Representation of memory contents stored in a file, usually referring to media like disks, cartridges|
|Dump||File of ROM contents. Good dump = verified to be correct; overdump = dump that is longer than the original contents|
|Cartridges||Representation of pluggable software components, like the TI-99/4A command modules.|
|CHD||Compressed Hunk of Data, storage format for mass storage devices, allows for uncompressed or compressed and checksummed content.|
|Input ports||Any kind of system component that changes state on external action, like a dip switch or a key on the keyboard.|
One of the most important things to keep in mind is that MAME and MESS follow a declarative concept. This means that we want to tell the system what its components are, instead of implementing their behavior. Of course, in some situations we need to really implement behavior by code lines, but I'll give you an example for understanding.
Suppose you have a circuit like a 74LS138. This is a component appearing quite frequently in many systems; it translates a three-bit number into one of several active lines. With a 000 at its inputs, line 1 gets active; a 001 turns on line 2, and so on, until 111 which turns on line 8.
You may now decide to implement the behavior of this circuit. This is quite easy, considering the features of the C and C++ languages; maybe you could choose a switch statement. When your code is complete you do not see anything like a 74LS138 anymore but only its functionality.
MESS encourages a different strategy. The 74LS138 can be emulated as a single circuit. Whereever you need it, you can include this as a black box, just connecting the incoming and outgoing lines. The implementation work is only done once. Of course, for a simple 74LS138 this may seem like nonsense, but think of disk controller chips or complete CPUs.
Therefore, what you will repeatedly see in all the MESS emulations is the following:
- Specify which parts are used.
- Specify how the parts are connected with each other.
You will often encounter structures of configuration data instead of program code. MESS and MAME are, in fact, electronic circuit emulators, and if these circuit emulations are properly done, the complete machine will run correctly. Even the main structure of the emulated machine is just a dataset of components that will be used.
Eventually, the emulators will reach a state where you can define a system in a completely declarative way, for example, by encoding schematics into XML. But at this time (in 2012) this is still in far future.
MESS can be started from the command line or from a frontend like QMC2. The frontend supplies the same parameters to the emulator as would be required on a command line. A simple command line could look like this:
The executable program file is mess (in Windows: mess.exe), sometimes also mess64 (for 64 bit systems) or messd (special version containing symbols for debugging) or mess64d. If you want to find bugs in the emulator (not in your TI programs running on the emulator!) you should use a debug build. For normal usage the standard build is recommended which is also much smaller.
Within the whole TI emulation part you will not find any int main(...). The main function belongs to the core, and this is a area where I would recommended to go not before you finished our tour and completely understood the concepts. For now, just keep in mind that the program starts somewhere in the core and will later reach the TI emulation.
The first thing to do for the system is to process the argument ti99_4ae. This is a name of a driver which it must look up in its tables. These tables were created during build time, so this is not of interest for you. The file where we should look at next is ti99_4x.c. (It could have been named in any way - the important point are the data which it registers in the core.)
At the end of ti99_4x we can find the following lines:
COMP( 1979, ti99_4, 0, 0, ti99_4_60hz, ti99_4, 0, "Texas Instruments", "TI99/4 Home Computer (US)" , 0) COMP( 1980, ti99_4e, ti99_4, 0, ti99_4_50hz, ti99_4, 0, "Texas Instruments", "TI99/4 Home Computer (Europe)" , 0) COMP( 1981, ti99_4a, 0, 0, ti99_4a_60hz, ti99_4a, 0, "Texas Instruments", "TI99/4A Home Computer (US)" , 0) COMP( 1981, ti99_4ae, ti99_4a, 0, ti99_4a_50hz, ti99_4a, 0, "Texas Instruments", "TI99/4A Home Computer (Europe)" , 0) COMP( 1994, ti99_4ev, ti99_4a, 0, ti99_4ev_60hz,ti99_4a, 0, "Texas Instruments", "TI99/4A Home Computer with EVPC" , 0)
COMP is not a command, but it is a macro which is transformed to a structure that is loaded into the core at startup time. In the fourth line you can find the character string that we provided on the command line.
The components of the lines have the following meaning:
- Year of the release of the system; just an information item
- The driver name
- The parent driver name.
- Compatibility information (not used here)
- Machine name - this one points to configuration data
- A pointer to the configuration of input ports
- Initialization information (not used here)
- Company; just information
- System name; used in frontends
There is already one important thing to note. Drivers (see above for the meaning - it is the complete system) may have children. Sometimes you have systems that closely resemble each other, except for some small differences. If they share the same ROM dumps you can avoid to provide the dumps for each system again. In our case, the ti99_4ae driver is a child of ti99_4a; it only differs in the video refresh rate. Therefore we do not need to provide the emulator with the dumps for ti99_4a and ti99_4ae. If the emulator cannot find a ROM dump for a driver, it checks whether there is a parent which can deliver the missing dumps. As all dumps are the same, it suffices to provide the ROMs for ti99_4a only.
The next thing the core has to find out is whether it must load any ROM dumps, and how they are named. For a TI-99/4A we need dumps for the console ROMs and GROMs. This is also declared inside ti99_4x.c:
ROM_START(ti99_4a) ROM_REGION16_BE(0x2000, "maincpu", 0) ROM_LOAD16_WORD("994arom.bin", 0x0000, 0x2000, CRC(db8f33e5) SHA1(6541705116598ab462ea9403c00656d6353ceb85)) ROM_REGION(0x10000, region_grom, 0) ROM_LOAD("994agrom.bin", 0x0000, 0x6000, CRC(af5c2449) SHA1(0c5eaad0093ed89e9562a2c0ee6a370bdc9df439)) ROM_END
As just explained, we do not have separate ROMs for ti99_4ae, so the core looks up the parent name and finds this structure. Again, ROM_START is a macro, like all the other capitalized terms. It does not execute anything but transforms the data as shown here into a structure that is registered in the core. The CRC and SHA1 declarations are used to verify that the ROMs are unchanged.
What if I want to use a modified ROM? - Well, this is not directly possible. You would need to calculate the new CRC and SHA1 values (MESS prints them on startup when they differ) and recompile the emulator. If it were allowed to modify the ROMs, debugging would become a nightmare. Imagine your TI emulation locks up - we could not be sure whether the bug is in the emulation or whether the dump has been modified. Sorry ... you're on your own here.
Both ROM files must be stored in a ZIP file named after the driver name, in this case ti99_4a.zip. The lines should be read in this way:
- This is ROM content for the memory region "maincpu" (see later).
- It is 16 bit (our ROMs are connected to the 16 bit bus), and the byte order is big-endian (in the TI, the most significant byte has a lower address than the least significant byte; if you store >1234 at >A000, then >12 is at >A000 and >34 is at >A001). PCs use little-endian, in contrast.
- Load the contents of the file "994arom.bin" into the region, >2000 bytes (8192).
- Another region we use is region_grom (somewhere else declared as a string "console_groms").
- Its size is 64 KiB (yes, each GROMs only has 6 KiB contents, but they nevertheless occupy the whole address space) and it is 8-bit memory.
- We load the contents of "994agrom.bin" into the region, size is >6000 bytes (24576; the first three GROMs). The remainder of the region is filled with zeros.
We could also clip the GROM region, since we put cartridge GROMs in their own regions. (Maybe I'll do that in a later release.) For now it does not hurt to waste 40960 bytes.
static MACHINE_CONFIG_START( ti99_4a_50hz, ti99_4x ) MCFG_CPU_ADD("maincpu", TMS9900, 3000000) MCFG_CPU_PROGRAM_MAP(memmap) MCFG_CPU_IO_MAP(cru_map) MCFG_MACHINE_START( ti99_4a ) MCFG_MACHINE_RESET( ti99_4a ) /* Video hardware */ MCFG_TI_TMS991x_ADD_PAL(VIDEO_SYSTEM_TAG, TMS9929A, ti99_4_tms9928a_interface) /* Main board */ MCFG_TMS9901_ADD(TMS9901_TAG, tms9901_wiring_ti99_4a, 3000000) MCFG_DMUX_ADD( DATAMUX_TAG, dmux_devices ) MCFG_TI99_GROMPORT_ADD( GROMPORT_TAG, console_cartslot ) /* Software list */ MCFG_SOFTWARE_LIST_ADD("cart_list_ti99", "ti99_cart") /* Peripheral expansion box */ MCFG_PERIBOX_ADD( PERIBOX_TAG, peribox_conf ) /* sound hardware */ MCFG_SPEAKER_STANDARD_MONO("mono") MCFG_SOUND_ADD(TISOUNDCHIP_TAG, SN94624, 3579545/8) /* 3.579545 MHz */ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.75) MCFG_TI_SOUND_ADD( TISOUND_TAG ) /* Cassette drives */ MCFG_CASSETTE_ADD( CASSETTE_TAG, default_cassette_interface ) MCFG_CASSETTE_ADD( CASSETTE2_TAG, default_cassette_interface ) MCFG_SOUND_WAVE_ADD(WAVE_TAG, CASSETTE_TAG) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) /* GROM devices */ MCFG_GROM_ADD( GROM0_TAG, grom0_config ) MCFG_GROM_ADD( GROM1_TAG, grom1_config ) MCFG_GROM_ADD( GROM2_TAG, grom2_config ) /* User controller */ MCFG_MECMOUSE_ADD( MECMOUSE_TAG, 50 ) MACHINE_CONFIG_END
To be continued