Controlling a Pinball Machine Using Linux
Not everything in the pinball machine control system has to execute as frequently as the low-level hardware I/O operations. Game play itself—how the machine responds to switch detections, lighting different lamps and incrementing the player scores—operates just fine as an ordinary user process. In a sense, it is really a supervisory controller of the low-level I/O processing.
The kernel module should work for every game based on the AS2518 MPU. You can download the source code from the Pinball Machine Reverse-Engineering Kit Project on SourceForge.net and compile it for your kernel. It will then be up to you to write the supervisory control software to play the particular game you are hacking. Table 1 lists other source code files in this package.
Table 1. Source Code for the Pinball Machine Reverse-Engineering Kit
|Source Code File||Purpose|
|analyze_testbed_output.php||Analyzes a game using the parsed text file output of user_pmrek.exe and the saved system activity records.|
|common_functions.php||Functions shared by PHP programs.|
|Makefile_pmrek||GNU Make command file to compile kernel module and executables.|
|pmrek_bash_profile||Appended to auto-login user's bash profile; calls start_testbed.|
|pmrek.c||Linux 2.6 kernel module for hardware control process.|
|pmrek.h||Header file containing definitions and data structures.|
|pmrek.sql||MySQL script to create database, tables and access permissions.|
|start_testbed||Shell script for running standalone testbed system; runs testbed.exe and restarts if terminated for upgrade.|
|testbed.c||Supervisory process for controlling kernel module, playing Evel Knievel, logging and analyzing process data; compiles into the executable testbed.exe.|
|testbed_performance.php||Creates summary statistics of all games analyzed.|
|user_pmrek.c||Utility program for parsing output of testbed.exe, displaying data structure sizes and simulating operation of the kernel module; compiles into the executable user_pmrek.exe.|
You are free to modify the C program testbed.c I wrote for Evel Knievel. It uses the ncurses screen handling package to provide a console color display and user input. A diagnostic display shows the disposition of the switch matrix, the lamps and the most recently fired solenoid. It also shows the player scores, as well as run-time statistics such as the average cycle frequency and execution time of the kernel workqueue process. Keyboard commands can be entered to turn the continuous solenoid on or off, fire momentary solenoids, turn feature lamps on or off and adjust the workqueue delay. Figure 6 shows a game in progress. Note the closed switches; these are drop targets that have been struck.
The supervisory program receives events passed from the kernel module by reading /dev/pmrek, which it has opened using the system call open(), just like any other file. Commands are then sent back to the module by writing to it. I tried to make the main functions correspond to my impression of the key events in a game of pinball. They are listed in Table 2.
Table 2. Supervisory Control Program Functions
|game_add_player()||Called when the credit button is pressed (and there are credits) to start a new game or add more players.|
|game_ball_end()||Called when the outhole switch is detected while a ball is in play to initiate the bonus countdown, advance to the next ball, the next player or end the game.|
|game_collect_bonus()||Called after a ball ends to count down the current player's bonus.|
|game_segment_display()||Emulation of a seven-segment digital display on the computer screen for player scores, match count, credits and ball in play.|
|game_lamp_update()||Called after processing switch detections to update the disposition of all the feature lamps at once.|
|game_play_tune()||Plays various tunes by firing the chime momentary solenoids in predefined sequences.|
|game_switch_response()||Called for each valid switch detection retrieved from the kernel module; initiates all other events related to normal game operation.|
|game_watchdog()||Called every second to detect game faults, including missed switch detections, and either reprocesses the switch response or terminates the program.|
|process_output_file()||Called by the forked child process after a game is completed to analyze the log file recorded during the game play.|
|termination_handler()||Signal handler for cleanly ending the program; closes data log file and puts the kernel module into an idle state.|
|main()||Main program initializes kernel module data structures, computer screen and loops until a termination signal is caught; main loop processes user keyboard input, reads events from kernel module, calls game process functions, writes log file to disk and updates computer screen display.|
You should be able to adapt this code to your particular game by tweaking the functions game_switch_response() and game_lamp_update(). How do you write the program without peeking at the original manufacturer's source code? There are plenty of clues painted on the play field itself, telling you what each switch scores and so on. Of course, you also can create your own rules, perhaps improving on weaknesses in the original design.
The diagnostic display is great for testing, but the player scores are too small. By default, the console simulates the large digital displays on the original back box, as shown in Figure 7. You can get to the diagnostic display by pressing the Self Test switch inside the pinball machine coin door.
We took the game to Pinball at the Zoo in Kalamazoo, Michigan in April 2005. Hundreds of people played the game, which collected statistical data that I used in my Master's thesis. After each game completes, a PHP program reads through the log file created by the game program. It generates an HTML document summarizing the event history of the game and statistics about its real-time performance. These results are then stored in a MySQL database to facilitate analysis of overall performance. Figure 8 is a block diagram of the setup. Figure 9 shows the game in action.