Sunday, April 19, 2009

Understanding WPC DMD Emulation in PinMAME

Bitmap Generation (updateDisplay and wpcdmd_update)

The updateDisplay() function in core.c calls wpcdmd_update() in wpc.c to create a color bitmap based on the DMD frame data stored in dmdlocals.DMDFrames[].
static void updateDisplay(struct mame_bitmap *bitmap, const struct rectangle
*cliprect, const struct core_dispLayout *layout, int *pos)
*bitmap is a pointer to the display bitmap structure and contains the following elements:
width = 320, height = 256, depth = 16, **line,, *base, rowpixels = 352, rowbytes = 704, plot = pp_16, read = rp_16, plot_box = pb_16

*cliprect is a pointer to the rectangle structure and contains the following elements:
min_x = 0, max_x = 255, min_y = 0, max_y = 255

*layout is a pointer to the global structure wpc_dispDMD with the parameters:
top =0, left = 0, start = 32, length = 128, type = 14, *ptr = wpcdmd_update

*pos is a pointer

When emulating a WPC rom, this function seems to mainly just call the layout->ptr function


wpcdmd_update(bitmap, cliprect, layout)
defined in "wpc.c" by


PINMAME_VIDEO_UPDATE(wpcdmd_update)

where PINMAME_VIDEO_UPDATE is defined in "core.h" as

#define PINMAME_VIDEO_UPDATE(name) int (name)(struct mame_bitmap *bitmap, const struct rectangle *cliprect, const struct core_dispLayout *layout)
The wpcdmd_update() function creates a temporary (64+2)x(256+2) byte array called dotCol. It fills the array with pixel intensity data based on the last three frames of DMD data and then calls the function video_update_core_dmd().

To generate the pixel data, an iterative loop is called to fill the dotCol array and a pointer *line is set to point to the start of a row of data. Row 0 is not written and line initially points to the first byte in Row 1.

The data is read in parallel from three different DMD frame buffers 16 bytes at a time. Each byte is used to generate 8 different pixel values in dotCol. Data is generated by reading three bytes (one from each DMD frame buffer). The first pixel value is generated by summing the least significant bits all three bytes to create a 2-bit value. The next pixel is generated by summing the second least significant bits, and so on, until eight pixel values are created.

After the frame buffers are read 16 times, 128 pixels of data will be created to complete a row in dotCol. This process is repeated for 32 iterations to complete the image. The image is then passed with a function call to


video_update_core_dmd(bitmap, cliprect, dotCol, layout);
This function now copies the image data from temporary buffer dotCol to bitmap. Before copying, row 0 and row 33 of dotCol are filled with 0's using the C++ memset() function. Also as each line is drawn dotCol[row][128] is set to zero. For each of the 128 pixel values in the line, a pixel in the corresponding row of the bitmap is assigned a color table value stored in dmdColor[].
By default, antialiasing is applied during the bitmap generation. The actual bitmap is twice as large as the DMD display. The DMD pixel values are placed at even row and column locations and the color is indexed using the dotCol value for that pixel. Odd row and column locations in the bitmap are colored using an antialias colormap. The colormap index for the antialias pixels are indexed by summing the dotCol values of the neigboring pixels.

Organization of the Color Palette

The color palette is initialized in "core.c".

1: DMD Off (20% DMD On)
2: DMD 33% (33% DMD On)
3: DMD 66% (67% DMD On)
4: DMD On
5-12: 8 Lamp On Colors (Black, White, Green, Red, Orange, Yellow, LBlue, LPurple)
13-20: 8 Lamp Off Colors (25% of Lamp On)
21-26: 6 AntiAlias Shades (Black - (dmd_antialias)% DMD On)
28: AntiAlias On 1 (72% DMD On)
29: AntiAlias On 2 (33% DMD On)
30: AntiAlias Off 1 (72% DMD Off)
31: AntiAlias Off 2 (33% DMD Off)
32-47: 16 AntiAlias Shades (AA Off 2 - AA On 2)
48-63: 16 AntiAlias Shades (AA Off1 - AA On 1)
64-79: 16 AntiAlias Shades (DMD Off - DMD On)




Compiling PinMAME With Visual C++

The file setup_vc.txt in the pinmame_cvs directory provides information on compiling pinmame with Visual C++. The first step is to obtain a copy of Microsoft Visual C++. Microsoft provides a free version of their compiler known as the 'Express' version which does not include all features but is suitable for compiling pinmame.

http://www.microsoft.com/express/vc/


In addition, you must install the DirectX Software Developer's Kit as pinmame requires the "d3d.h" and "dsound.h" header files. I tried downloading the latest version (version 9) of DirectX but it did not include the required "d3d.h" file. Instead it had "d3d9.h" and "d3dx9.h". There's a note in setup_vc.txt that indicates a version with DirectInput 7 is required. This can be found here:

http://www.microsoft.com/downloads/details.aspx?familyid=529f03be-1339-48c4-bd5a-8506e5acf571
Rather than modify the source code, I installed this older version of the DirectX SDK. Installing this will create the system environment variable $(DXSDK_DIR).

The nice guys maintaining Pinmame also provide files to help get you up and running quickly. The file PinMAME_VC2002.vcproj contains the required project information to load all the source files into Visual C++ 2002. They also include a bat file to update the project directory to Visual C++ 2008. To do this, run
copy_vc2002_to_vc2008.bat
in the pinmame_cvs directory.

After installing Microsoft Visual C++ 2008 Express and the DirectX SDK, run Visual C++ and open the project PINMAME_VC2008.vcproj. We now need to add links to the DirectX SDK header files and libraries.

  1. Go to the Project->Properties menu. This will open another window with properties for this project.
  2. Select Configuration Properties->C/C++->Additional Include Directories.
  3. Add the directory "$(DXSDK_DIR)include".
  4. Then select Configuation Properties->Linker->Additional Library Directories.
  5. Add the directory "$(DXSDK_DIR)lib\x86".

After adding these changes, close the Properties menu and select Build->Build Solution. This should cause the source files to compile successfully. Then to link and build the application, select Build->Build Project.

This should create a pinmame executable file that can be run at the command line or through the debugger.

Getting the PinMAME Source

A link to the source code for Pinmame can be found at
http://www.pinmame.com/
on the downloads page:
http://pinmame.cvs.sourceforge.net/pinmame/pinmame/

This is a link to a CVS repository where the code can be viewed but not easily downloaded. A google search and some experimentation yielded the following method for obtaining the source:
  1. Obtain a stable version of CVS. The version I used for windows was available from GNU:
    http://ftp.gnu.org/non-gnu/cvs/binary/stable/x86-woe/cvs-1-11-22.zip
  2. Determine the latest revison that is tagged in the repository by opening the whatsnew.txt file log and looking at the CVS tag. You can do that easily by following this link.
    http://pinmame.cvs.sourceforge.net/viewvc/pinmame/pinmame/whatsnew.txt?view=log
    You should see something like 'release_X_Y' which corresponds to pinmame version X.Y. This tag will be used in the next step.
  3. Open the start menu in the text box, type 'cmd' to open a command line shell and 'cd' to the directory containing the cvs executable.

  4. Check out the source code from the sourceforge repository using the command:

    cvs -z3 -d:pserver:anonymous@pinmame.cvs.sourceforge.net:/cvsroot/pinmame co -r release_2_1 -d pinmame_cvs_2_1 pinmame

    To check out a later version of pinmame use the tag found earlier in place of 'release_2_1'. In this command -z3 tells CVS to use file compression for downloading files over the internet, -r [tag] specifies the revision, -d [dir] is the local directory that will be created. The password for anonymous users is blank so just press enter if prompted. If all went well there should now be a pinmame_cvs directory that contains the source code.