HOWTO Build EMame

Prequisites

It is assumed that you've already installed the ER5 SDK. Note that the ER5 SDK may be installed on any drive. Nowhere in this document or in any of the scripts will be refered to absolute drive letters but rather in paths relative to the root directory.

Getting the source code

EMame is based on XMame so you'll need the XMame source distribution to build EMame. Also, you'll need the zlib package as well. Here's where you can get them:
1) Latest EMame version
2) XMame version 0.37b7.1
3) The source code for zlib 113 (failing that, look for it on the Zlib home page)

First, unzip XMame on `\` afterwards rename the directory `\xmame-0.37b7.1` to `\mame` (note: not `\emame` !).
Next unzip zlib into `\mame\src\zlib` and finally EMame into `\`. Note that emame will overwrite 3 exisiting files in \mame\src and subdirectories of \mame\src\cpu.

Your directory structure should now look something like this (most sub directories are not shown):

> `\mame\App (<-- EmameApp.App)`
> `\mame\cab`
> `\mame\contrib`
> `\mame\doc`
> `\mame\src`
> `\mame\src\cpu`
> `\mame\src\drivers`
> `\mame\src\epoc (<-- Emame.exe)`
> `\mame\src\machine`
> `\mame\src\msdos`
> `\mame\src\sndhrdw`
> `\mame\src\sound`
> `\mame\src\unix`
> `\mame\src\vidhrdw`
> `\mame\src\zlib`

Building instructions

There are 2 seperate builds: `EMameApp.app` and `EMame.exe`, these are discussed below`.`

EMameApp.app

For a wins build the steps are:

> `cd \mame\app\group`
> `bitmap`
> `makmake emameapp wins`
> `nmake -f emameapp.wins`

For marm the steps are quite similar:

> `cd \mame\app\group`
> `bitmap`
> `makmake emameapp marm`
> `nmake -f emameapp.marm`

EMame.exe

Building EMame.exe is a complex process. I strongly recommended you build it as is shown below. If you interested in how the actual build comes together read the next section.

> `cd \mame\src\epoc\group`
> `winsdeb`
> `open \mame\src\epoc\group\emame.dsp in DevStudio 5`
> `build project`

and for marm

> `cd \mame\src\epoc\group`
> `marmrel`

You may safely ignore all the warnings you see flying by, start worrying if you encounter errors. If that happens send me an email!

The gory details

You may safely skip this section if you are only interested in the binaries. However, reading on will gain you some handy insights in the EMame build process.

About the MAME build process

The MAME build process revolves around makfiles. These makefile will build the following libraries:`zlib`, `sound`,`sndhrdw`, `vidhrdw`, `cpus`, `drivers`, `machine`, `core` and `unix`. Of these `Unix` and `zlib` are of no particular interest to us. `Unix` contains the OS dependent source code and is therefor not used in EMame and `Zlib` is a straitforward standalone library. `Core` contains the stuf that is needed regardsless of what game or hardware MAME support; it consists of all the source files in `\mame\src`. Ultimately, these libraries are combined into one big executable file.

By default, MAME compiles almost everything by including support of nearly all games and all hardware it needs to emulate. The end result is an executable file of many MBs. This is unwanted in EMame for two reasons. Firstly, when running EMame on say the Series 5mx most games are not playable because the game runs on hardware that is more CPU intensive to emulate. The other reason is that we'd like to keep the executable file size as small as possible by only including what's needed.

In order to understand how to steer to MAME build process we need to understand how MAME is designed to support a particular game. This support revolves around the concept of game drivers. Each game driver declares a global variable of type `GameDriver` which decribes what is needed (rom files, hardware, CPUs etc) to run this game. The MAME `core` maintains a global table of all these game drivers variables in `\mame\drivers.c`.
Each game driver has compile and link dependencies against source files in the modules: `machine`, `sound`, `vidhrdw` and `drivers`. This can easily be seen whan looking at the UN*X makefiles. For example, for pacman the makefile includes the following entry:

> `$(OBJ)/pacman.a: \`
> `$(OBJ)/drivers/pacman.o \`
> `$(OBJ)/machine/pacplus.o \`
> `$(OBJ)/machine/theglob.o \`
> `$(OBJ)/drivers/jrpacman.o $(OBJ)/vidhrdw/jrpacman.o \`
> `$(OBJ)/vidhrdw/pengo.o $(OBJ)/drivers/pengo.o \`

Actually, this pacman library (.a extentions in UN*X are libraries, the .lib equivilant of windows) supports more than one game. It contains pacman clones and games running on the same hardare as well. Again, for each such game there is global variable in one of source files in the drivers directory. In the pacman example above you will find the supported games in `\mame\drivers\pacman.c`, `\mame\drivers\jrpacman.c` and `\mame\drivers\pengo.c`. Listed below is a snippet from `pacman.c`:

> `GAME( 1980, pacman, 0, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 1)" )`
> `GAME( 1980, pacmanjp, pacman, pacman, pacman, 0, ROT90, "Namco", "PuckMan (Japan set 2)" )`
> `GAME( 1980, pacmanm, pacman, pacman, pacman, 0, ROT90, "[Namco] (Midway license)", "Pac-Man (Midway)" )`
> `...`
> `<snip>`
> `...`
> `GAME( 1983, vanvans, vanvan, vanvan, vanvans, 0, ROT270, "Sanritsu", "Van Van Car (Sanritsu)" )`
> `GAME( 1982, alibaba, 0, alibaba, alibaba, 0, ROT90, "Sega", "Ali Baba and 40 Thieves" )`

`GAME` is a fancy macro that expands into the declaration of a global variable of type `GameDriver`. The second parameter in this macro is the name of the game which is unique throughout MAME; if the game is clone, the third parament specifies the parent game. So, the first entry is a game called 'pacman' and the second is 'pacmanjp' which is a clone of pacman.

Apart from each game needing a particular set of source files to satisfy compile and link dependencies, they also have dynamic dependencies on CPU cores. CPU cores come in two flavours, general purpose CPUs, such as the Z80 or the M68000, and sound CPUs. The first ones live in sub directories of `\mame\src\cpu`, the second ones in the directory `\mame\src\sndintrf`.
For each general purpose CPU there is data structure, called `cpu_interface`, to describe the attributes of the CPU core. In the MAME `core` in `\mame\src\cpuintrf.c` there is a global table holding an entry for each supported general purpose CPU. In similar vein, the data structure for sound CPUs is called `snd_interface` and the MAME `core` maintains a table of them in `\mame\src\sndintrf.c`.
For each CPU core (either general purpose or sound) there is macro defined to say whether MAME should include support for this CPU. For example, to include support for the Z80 you must somwhere include the definition `HAS_Z80=1`. In MAME, all these macros are defined in the makefiles.
In `cpuintrf.c` (and in similar vein in sndintrf.c) each entry specifying a CPU core is guarded by an #ifdef with the CPU core's define. A snippet from `cpuintrf.c` is shown below:

> `struct cpu_interface cpuintf[] =`
> `{`
> `CPU0(DUMMY, Dummy, 1, 0,1.00,0, -1, -1, 16, 0,16,LE,1, 1,16 ),`
> `#if (HAS_Z80)`
> `CPU1(Z80, z80, 1,255,1.00,Z80_IGNORE_INT, Z80_IRQ_INT, Z80_NMI_INT, 16, 0,16,LE,1, 4,16 ),`
> `#endif`
> `#if (HAS_Z80GB)`
> `CPU0(Z80GB, z80gb, 5,255,1.00,Z80GB_IGNORE_INT, 0, 1, 16, 0,16,LE,1, 4,16 ),`
> `#endif`
> `...`
> `<snip>`
> `...`
> `};`

The macro's `CPU0` and `CPU1` will expand into an `cpu_interface` entry which describes a particular CPU core. They will refer to functions which are defined in the CPU's according sub directory in `\mame\src\cpu`. Only if `HAS_Z80` has been defined, the datastructure for the Z80 CPU will be both defined and included in the `cpuintrf` table. If not defined then there is no need to include any of the sources from the `\mame\src\cpu\z80` directory.
Games that run on a Z80 CPU will specify in their game driver (to be precise, it is specificied in data structure called `MachineDriver` to which the game driver hold a reference) that they run on CPU 1. When the MAME core is requested to run a game it will simply offset the CPU number found via the game driver into the cpuintf table to find the appropriate CPU core.
The exact same holds for sound CPUs. The game driver specifies the sound CPUs (if any) by their number and the MAME `core` will offset this number in the table in `sndintrf.c` to get the sound CPU core.

To summarise:
1) In `\mame\src\drivers.c` there is a global table of all built in game drivers.
2) In `\mame\src\cpuintrf.c` there is a global table of all built in general purpose CPU cores (Z80, 68000 etc.).
3) In `\mame\src\sndintrf.c` there is a global table of all built in sound CPU cores.
4) Each game driver has link dependencies on source files from `machine`, `sound`, `vidhrdw` and `drivers`.
5) Each driver needs one or more general purpose CPUs and or sound CPUs.
6) There is no compile or link dependency between the game driver and the CPUs it needs. The game driver simply refers to the CPU by number and the MAME core resolves this to a CPU core via the CPU table in either `\mame\src\cpuintrf.c` or `\mame\src\sndintrf.c`.
7) For each CPU there is macro that specifies whether the CPU should be included in the build. Depending on this macro an entry will be included in a global table for this CPU core.

The EMame build process

As is clear from the above, the way to steer the final executable size by only including in the three tables for game drivers, general purpose CPUs and sound CPUs the entries that are actually needed.

I will spare you the details of all my failed attempts in setting up a suitbale build process for EMame. Instead, I'll describe the build process that succeded at the end. Note that since building `EMameApp.app` is so trivial it won't be discussed here, the focus is only on how to build `emame.exe`.

The build process I finally end up with will first build one huge static library, called `mameengine.lib`, which has the exact same support for game drivers and CPUs as MAME itself. The EPOC dependent source is not included in this library but rather specified in the `emame.mmp` project. Together with EPOC dependent source, `emame.mmp` also includes a local copy of `driver.c`, `cpuintrf.c` and `sndintrf.c`. In these local copies we selectively build the three global tables with the global variables of games drivers and cpu interfaces we actually want to support. This `emame.mmp` project will link against the huge `mameengine.lib`.
The major benefit of this approach is that I can control the supported games quite easily in the three copied source files (`driver.c`, `cpuintrf.c` and `sndintrf.c`) and let the linker do all the hard work in figuring out what to include in the final executable file. Also, the latter will guarantee we get the smallest executable size possible given the games drivers and CPUs we've included in the three global tables!

The three basic steps to do the above are:
1) Generating EPOC mmp files;
2) Building static library mameengine.lib;
3) Building emame.exe.

Note that the first step is particularly tricky and costed me some blood, sweat and tears. When building from source this first step can be ommited as I've included the generated mmp files in the source distribution. It is only usefull in upgrading EMame to newer versions of MAME as it allows for mmp file generation using a (semi-) automated process.

1) Generating EPOC mmp files

Most mmp files in EMame are generated from both static information and information gathered dynamcically. The static information is specified in the individual files in `\mame\src\epoc\group\tmpl` and they contain the obvious things like target type, target name etc. The dynamcic information is obtained from parsing the MAME makefiles. The information extracted from these makefiles are: a list of macros needed to compile MAME and a list of all the .c files that need to be compiled. The list of macros is generated into the file `\mame\src\epoc\group\userdefs` which is a merge of the macros defined in `\mame\src\epoc\group\tmpl\userdefs` and the macros parsed from the makefile. The pearl script `\mame\src\epoc\group\genmmp.pl` will do the makefile parsing and merges that information with the template files. The script generates mmp files for: `core`, `zlib`, `sound`, `sndhrdw`, `cpus`, `machines`, `vidhrdw` and `drivers`. The emame.mmp file is hand written.
It turns out that the makefiles generated by the ER5 toolchain include `$(USERDEFS)` to the compile flags. To define your own flags you have to include an extra line in the makefile with a `USERDEFS=...` statement.

Apart from `emame.mmp`, all the mmp files specify a static link library as target. Contrary to popular belief, you can build static libraries in ER5, that is, if you know how... :-) Apart from the norm target types: `exe`, `app` and `exedll`, the ER5 toolchain allows a target type called `lib`. Generating a makefile for target type lib result in an almost correct makefile. The makefile for wins is only incorrect in that it performs an unnecesary file copy, apart from that the makefile is perfectly usefull and produces the wanted library. The marm makefile is not usefull in its generated form. For example, calling makmake on `\mame\src\epoc\group\zlib.mmp` results in a makefile which contains the following snippet:

> `"$(EPOCTRGREL)\ZLIB.lib" : "$(EPOCBLDREL)\ZLIB.in"`
> `dlltool --output-lib "$(EPOCTRGREL)\ZLIB.lib" \`
> `--dllname "ZLIB.LIB" \`
> `"$(EPOCBLDREL)\ZLIB.in"`
> `copy "$(EPOCBLDREL)\ADLER32.o" "$(EPOCTRGREL)\ZLIB.o"`

The dlltool step is needed for dynamic link libraries but should be skipped for static link libraries. Also, the file copy is incorrect. The section should have read:

> `"$(EPOCTRGREL)\ZLIB.lib" : "$(EPOCBLDREL)\ZLIB.in"`
> `copy "$(EPOCBLDREL)\ZLIB.in" "$(EPOCTRGREL)\ZLIB.lib"`

If patched like this, zlib.marm will correctly produce library `\epoc32\release\marm\rel\zlib.lib`.

The pearl script (yes, my pearl programming skills have improved darmatically) `\mame\src\epoc\group\patchmakefile.pl` is doing precisely that for marm makefiles. Note that the shell script `\mame\src\epoc\group\makmake.cmd` will call the `patchmakefile.pl` if appropriate, making the patch work transparent.

2) Building static library emameengine.lib

After building all the individual libraries for core, sound etc they have to combined into one library. Why? Well, it turns out that the gnu linker is quite particular in the way it links objects files and multiple libraries together. Unfortunately, there is a mutual dependency between the core.lib and the cpus.lib making it difficult if not impossible to satistfy the link without fully including one of two libraries in the final executable.
The easiest way I found to build emameengine.lib is to simply link all the object files from in the individual libraries. Since genmmp.pl is already intimately familiar with which object files are generated, it will generate a list of object files in `mameengineobjs.wins` and `mameengineobjs.marm`. Both files specifiy object files for the same source set, however, wins object files have extention `.obj` whereas marm object files have extention `.o`.

Mameengineobj.wins and mameengineobj.marm are included in the makefiles `\mame\src\epoc\group\mameengine.wins` and `\mame\src\epoc\group\mameengine.marm` respectively. The latter two are hand written.
Finally building mameengine simply requires `nmake -f mameengine.wins` or `nmake -f mameengine.marm`.

3) Building emame.exe

Emame.exe is build from a hand written emame.mmp file. I couldn't get the generatered normally makefiles to work satisfactory for both wins and marm. For marm the problem is the way emame.exe links against static libraries, for wins the order of libraries was relevant.
For wins I use the .dsp file, so I manually patched both emame.dsp and emame.marm. If you want to know the changes I recommend you generate them using makmake and run windiff.

Some notes:

- ZLib: I got an error compiling `\mame\src\zlib\contrib\minizip\zip.c` because `DEF_MEM_LEVEL` was not defined. This macro is defined in `\mame\src\zlib\zutil.h` but that header file doesn't get included. Added `-DDEF_MEM_LEVEL=8` to the file `USERDEFS` solved it.
- CPUS: The gcc compiler that comes with the ER5 SDK is not able to compile `\mame\src\cpu\m6809\m6809.c` and `\mame\src\cpu\hd6309\hd6309.c`. These CPUs get excluded because the patched file will #ifdef the source file out. This is not an elegant solution but by far the simplest.
- Unzip: There is a problem with the way the cache works in `\mame\src\unzip.c`. It allocates some datastructures on the heap when a game is started und uses global data to refer to these data structures. Unfortunately, it wants to access these data in the next game as well. However, each game is run in a new thread which results in its heap being destroyed when game (and thus the tread) terminates. The easiest solution is to disable the cache. This is done in the pacthed unzip.c