Last updated March 19, 2010
No, unless you want to rewrite all of the game rules, minus that bug. FreeWPC doesn't let you alter the software for existing games: it lets create brand new software for them. Thus, anything is possible, but it takes a lot of time to create something that is fun and playable.
The easiest way to get started is to run the software in an emulator, such as Visual PinMAME or Visual Pinball. You can download a precompiled ROM, install it, and play around with the system. If you're brave and you understand the risks, you can even put the ROM into a real machine.
Another approach is to run the native mode programs: this is sort of like a ROM and emulator combined into one. If you're familiar with programming, you can run debuggers like 'gdb' in this mode to help track down problems.
Most everything is basically working at this point, although there will still likely be bugs. Sound handling is still weak. Some prebuilt ROMs are not very playable, as the focus has been on the system as a whole and not developing a replacement ruleset.
FreeWPC has been tested enough that there is little risk of causing harm to your pinball machine, but that does not mean it won't happen. If you change the code on your own, be especially careful, as small errors can (trust me!) cause bigger problems!
FreeWPC is stable in the sense that release versions aren't known to crash under normal conditions. FreeWPC has gone through extensive testing in simulation, but this doesn't mean it's perfect.
This stability is certainly good enough for playing under PinMAME, and reasonably good enough to put in a real machine if you are knowledgeable.
Initially, Twilight Zone was the main machine used for development. TZ was chosen based on my experience with the machine, and also, it seemed like a good way to test out a lot of features that I thought FreeWPC ought to have. TZ has a lot going on, so getting it to work should prove the system out.
Recently, my focus has switched to World Cup Soccer, since I actually have one of those machines. However, others have used it on Twilight Zone and Corvette.
As proof that all variations of WPC are supported, Funhouse, T2, and Attack From Mars ROMs have also been built and run under PinMAME. No actual rules exist for any of these games; they have a minimal attract mode and test mode only.
Note that real WPC hardware has a watchdog feature that will restart the CPU in the event of runaway code. PinMAME doesn't emulate it at all, but FreeWPC does use it, and that is good protection against a large class of errors. If the code crashes after a few milliseconds, blanking is asserted which automatically kills all of the I/O, so that coils won't be left stuck on.
The periodic timer handler constantly checks that task scheduling is working. No task is allowed to run for longer than 128ms. If that occurs, the system is rebooted.
There are various sanity checks throughout the code that will also throw a fatal error if something is not right, causing a quick reboot.
On any fatal error, the hardware is immediately reinitialized so that all solenoids and flashers are left off. The error is logged into the bookkeeping system for later retrieval, then the system resets itself.
No, at least not that I am aware of. If anyone sees differently, please let me know immediately. I will work hard to ensure that FreeWPC truly remains free and steers clear of any patent-related issues.
Yes, although this hasn't been fully implemented. There is support for cross-compiling between the 6809 and native Linux, so some of the hooks for this are already there. If the platform uses a 6809 also, then chances are better. If it's a different MPU, odds are GCC doesn't support it.
Some work has been done to port FreeWPC to Sega Whitestar, the WPC sound board, the P-ROC CPU and power driver board, and Pinball 2000. Further work here is expected.
It is indeed slow, running at 2MHz, but it is adequate. About 30-40% of the processing time is spent in the periodic interrupt, which takes care of all the important, realtime functions. The remaining CPU time is available for other tasks to run.
Great care is taken to make sure that realtime functions are as optimal as possible, otherwise it would indeed be impossible.
The other performance critical routines are those that update the dot matrix display. Many of the C routines have been rewritten in assembler to get better performance, at the expense of portability. As it is now, I don't think they can be optimized much more.
First, you need to add a machine description file for the game in the machine subdirectory; see Twilight Zone's version for an example of what goes in there. Create a Makefile there also that has the name of the md file and any other C files you want to write. To build it, make sure MACHINE is defined to the machine name you chose; this is best done by creating a .config file before running make.
The md file format is best explained by reading the TZ version, and modifying it as necessary. It's modeled after the Windows INI format. See below for more details.
FreeWPC is self-contained and provides its own multitasking OS. Tasks are not preemptible; that is, they run until they exit via task_exit(), or yield control via task_sleep() or task_yield(). Then the next task starts. There is no notion of priority at all; all tasks are equal.
Real-time tasks are a special class triggered by an interrupt (IRQ) and run with higher priority than all others. RTTs cannot sleep, in particular, and are statically scheduled to run at a certain frequency.
There are also a number of 'periodic' functions defined throughout the code that are intended to be continuously but not necessarily realtime. They don't execute in a task context and therefore can't sleep.
There is a malloc() function for dynamic allocation, but it is not used anymore. All memory is statically allocated at compile time.
When you build the native mode version that includes built-in emulation, the OS functions are generally bypassed in favor of something else on your host platform. Task emulation uses the GNU pth library, which is most similar to FreeWPC's version.
Callsets are an implementation of static event publish/subscribe. A majority of the game code is written much like a modern day GUI, with callback functions that are invoked as various things occur.
Each instance of CALLSET_ENTRY defines a callback function that is invoked when some event occurs. It takes two parameters: an arbitrary string denoting the name of the module, and the name of the event to be monitored. The two are concatenated to form a unique name for the function.
Events are generated (or "thrown") via callset_invoke(), with the name of the event as the single parameter. This causes all corresponding CALLSET_ENTRY functions to be invoked. The order of invocation is random.
During the compile, all of the sources are scanned to match invokes and entry points. A file build/callset.c is automatically generated that implements the event catching.
The script tools/gencallset does all of this.
Early versions of FreeWPC required that lots of constant information about a machine be provided in the form of C code. Sometimes, information had to be specified multiple times; also, the format varied greatly.
Much of this type of information is now described in the .md file format instead. As part of the build process, the contents of the md file are turned into the underlying C code that is needed. The benefits are that a lot of the redundancy has been removed, and some syntactic sugar was added so that the representation is cleaner and easier to understand. It is way more English-like than C code, for sure.
For example, an entry that defines the properties of the Start Button would generate a #define named SW_START_BUTTON for its switch number, an event named sw_start_button that represents C code to be executed when the switch is triggered, and a string entry "START BUTTON" into the switch name table. Autogenerating these ensures consistency throughout the code but also makes it easier on the programmer.
The script tools/genmachine does all of this.
Gendefines are #defines that are automatically generated by scanning the source code to see all uses of the #define. They may be deprecated in the future; they are currently only used for task group IDs (GIDs).
All fonts were derived automatically from freely available X11 fonts, or from free TrueType fonts on the web. The included fontgen and fontgen2 scripts convert these to .fon files. A .fon file is just a C data structure that represents the font in a way that FreeWPC understands. The script uses netpbm utilities to generate the font data.
Actually, one of the fonts -- mono5, was the first font to be used and was created by hand, way before the advent of fontgen.
The graphics format has changed a few times; what is described here is the latest version.
The standard format for FreeWPC graphics images are now PGM files. Each machine contains an image load map file that says which PGMs to include, and gives each a C identifier to be used as its name from the code. At build time, the PGMs are converted into an internal format and placed into a special section of the ROM. The image loader contains some preliminary support for image compression, however, the number of images in current ROMs are so low that space has not been an issue yet.
FreeWPC does use the memory protection feature, which causes a special area of the regular RAM to be read-only thanks to the WPC memory protection circuit. Note that using FreeWPC will cause any protected data you already have in PinMAME or a real machine to be wiped once you load FreeWPC, and vice-versa.
Access functions use checksumming to detect errors, and will reset any bogus fields back to factory defaults to ensure sanity.
Note that protected RAM is also emulated in native mode, by reading/writing the file 'freewpc.mem'.
The core system files are in the kernel directory. This has all of the hardware-related functionality, plus a few other key functions. This code is put into the fixed area of ROM that doesn't have to be bank switched to be accessed.
Other common files are in common. These functions have to be bank switched. This is for occasional functions that are common to all games, like the ball device code, match algorithm, etc.
Game specific files are in machine/gamename.
The system starts in a platform-specific file (start.s for the 6809), then branches into kernel/init.c. Unless you're delving into the extreme low-level details, freewpc_init() in this file is a good place to start.
All switches are polled every 2ms by the IRQ handler. At IRQ time, the purpose is to determine which switches have changed in a way that might require servicing.
Later, an idle function will scan for these changes, perform additional debouncing and opto-inverting, and throw events for those switches that require servicing. To add a handler for a switch, you only need to add a CALLSET_ENTRY for the switch event, e.g. sw_start_button.
The lamp matrix is strobed every 2ms. WPC hardware allows only a single column of 8 lamps to be enabled at any time. The IRQ function rapidly switches between the 8 columns to give the illusion that all 64 lamps are being controlled simultaneously. So the entire matrix is actually updated only once per 16ms. This works well because the lamps have some "memory" after being strobed, causing them to glow a little bit longer than the 2ms.
Less frequently, another realtime task will toggle the states of all lamps that are in 'flashing' state. All of these flashing lamps flash at exactly the same rate, in step with each other.
Lamps whose state is shared among different game modes can be periodically redrawn, by declaring a 'lamp_update' CALLSET_ENTRY.
Lamp effects, used for temporary light shows or effects that are more complicated than simple flashing, are drawn onto additional, virtual lamp matrices. These are all overlaid to produce the final image that is drawn onto the physical lamps.
Task-level code can manipulate solenoids (and other drives, like motors) directly using the functions in include/sys/sol.h. There are numerous ways to do this:
The sol_request() style APIs are generally used to pulse a device for a fixed period of time. Requests are queued and only one drive runs at a time, to lessen stress on the power circuit and to prevent software crashes from having a really bad effect. (The exception are flashlamps, which are allowed to run concurrently.)
Drives controlled in this way can specify a duration and a duty cycle, for running the drive at less than 100% power.
Alternatively, a machine can define customized driver functions which can operate in parallel, and implement more complex state machines. A set of basic drivers is provided that can be used as templates for the common cases. Each driver defines its own APIs for operating that device. Most drivers provide at least enable() and disable() APIs, but there may be others. (For example, the WCS soccerball can spin in two directions.) There are standard drivers for jets, slingshots, diverters, etc.
Absolutely not. More than 98% of the code is in C. The machine description files are even in a more human-readable language. This should make it easier for more people to contribute.
The assembly portions have to do with multitasking, bank switching, and dot matrix drawing. The OS-level code is very stable and isn't likely to change much anymore. The DMD routines change occasionally.
Knowledge of the 6809 is useful, though. The performance of the 6809 C compiler is not perfect, and it's good to inspect the assembly output sometimes to see what it's doing. In most cases, bad code can be improved by rewriting it. Occasionally, patterns are seen which can only be fixed in the compiler itself, but this is very rare.
FreeWPC requires a UNIX-like development system, such as Linux or Cygwin. It probably will work with any of the BSDs just fine, too. Standard system utilities like GCC, bash, perl, and make are required. Also, you'll need a copy of the 6809 GCC compiler, unless you only want to compile in native mode.
FreeWPC comes with an S-record converter to build the final ROM images, and a few other utilities which may be of interest outside this project.
It also uses some other Unix tools that you probably have, too, like the bc calculator program to figure out how much padding is needed on the ROM files.
Contact brian@oddchange.com.
Copy one of the ZIP files into your mame roms directory, renaming it to match what pinmame will look for. If there are sound ROMs, you'll need to put these into the ZIP manually.
If you download a source code package, you can set TARGET_ROMPATH in your configuration file to where PinMAME ROMs are kept, and 'make' will also install it for you.
PinMAME does a lot of things wrong, but does them well enough so that no one will notice. Through the use of schematics and talking with former WMS employees, some of those details have been figured out. PinMAME is getting better and better over time, too.
There are many things that can go wrong on real hardware that can never happen in an emulator. This is why I warn about putting in a real machine. Just because it works on PinMAME doesn't mean it will work elsewhere. I learned a lot of things I didn't know before I put the software into my World Cup Soccer, and continue to do so. Most of the differences relate to timing.
First, welcome to the club :-) Embedded programming isn't easy. There's nothing like GDB to help you on the 6809, but there are ways of debugging that have been incredibly useful to me so far.
PinMAME, at least on Unix, has a debug mode that can be activated using the -d option. This will let you single-step and view memory, although you'll be working in 6809 assembly and not C. Use the map file at build/freewpc.map to correlate addresses to objects.
xpinmame's tracing mode will dump executed addresses to a file. FreeWPC has a script named 'tracer' that can parse these files and report lots of useful info.
You can add debug prints to the code. Use the dbprintf() function to write to the WPC debugger port. On Unix systems with a patched xpinmame and the wpcdebug utility, these are printed in realtime to a separate console window. On Windows, they are sent over the WPC parallel port, which PinMAME sends to a file in the 'nvram' directory. However, you won't be able to view these files until you exit PinMAME.