Baby Pac-Man PinMAME

A complete technical reference for the modified PinMAME build that runs the Baby Pac-Man ROM and drives a real physical playfield through two Ultimarc USB controller boards.

PinMAME I-PAC Ultimate I/O PAC-Drive Bally 1982 Windows / MinGW

01 Project Overview

Baby Pac-Man (Bally, 1982) is a hybrid arcade/pinball machine — the upper half is a video game screen running the Pac-Man maze, and the lower half is a real pinball playfield with flippers, drop targets, saucers, and solenoids. This project replaces the original MPU board with a modern PC running the game ROM under PinMAME, while driving the original physical hardware through two USB boards.

What PinMAME does here PinMAME emulates the 6800/6809 CPU, all three PIAs, the TMS9928A video chip, and the 6803 sound CPU — running the actual factory ROM. On every frame (60 Hz) it reads switch states from the keyboard/gamepad, fires solenoids via the PAC-Drive, and updates 96 lamp LEDs via the I-PAC Ultimate I/O.

Two Roles, Two Boards

I-PAC Ultimate I/O

96 LED outputs

Drives the entire lamp matrix — all 56 playfield lamps across 30 SCR positions × 2 AC phases. Also accepts up to 48 switch inputs (joystick, buttons, playfield switches).

PAC-Drive

16 solenoid outputs

Drives flippers, saucers, drop target reset, and the outhole kicker. Simple on/off HID device, up to 48V with external supply.

Both boards connect via USB and are accessed through a single Windows DLL, pacdrive32.dll, which is loaded at runtime by the custom pacdrive.c hardware abstraction layer added to PinMAME.

02 System Architecture

The full signal path from ROM execution to physical hardware, running at 60 Hz:

babypac.bat └── pinmame.exe babypac -ultimateio -joystick -dmd_only -scanlines ┌─ Baby Pac-Man ROM (Bally 1982) ──────────────────────────────────────┐ │ 6800 MPU @ ~1MHz · 6809 Video CPU · 6803 Sound CPU │ │ PIA0 ─── lamp strobe → byVP_lampStrobe() → tmpLampMatrix[] │ │ PIA1 ─── solenoid write → pia1b_w() → locals.solenoids │ │ TMS9928A ── video output │ └──────────────────────────────────────────────────────────────────────┘ │ VBLANK interrupt (60 Hz) ── byVP_vblank() in byvidpin.c │ ├── tmpLampMatrix[] ──→ coreGlobals.lampMatrix[8] (8 bytes = 64 bits) └── locals.solenoids ─→ coreGlobals.solenoidsupdate_hW() ── core.c line 1719 │ ┌───────────┴────────────┐ │ │ update_hw_byte() core_getAllSol() lampMatrix[0..7] solenoid UINT64 → Outputs[0..95] → Outputs[96..111] │ │ └───────────┬────────────┘ UpdateOutputs() ── pacdrive.c │ ┌───────────┴────────────┐ │ │ Pac64SetLEDStates() PacSetLEDStates() I-PAC Ultimate I/O PAC-Drive 12 groups × 8 LEDs 16-bit word 96 lamp outputs 16 solenoids │ │ pacdrive32.dll ← (USB HID) ──→ Both boards

Inputs (reverse direction)

Physical switch closes (flipper button, joystick, playfield target…) │ I-PAC Ultimate I/O ← 48 input channels wired to all switches │ sends keyboard key or gamepad button via USB HID │ Windows keyboard/HID event │ PinMAME input system │ SWITCH_UPDATE(byVP) ── byvidpin.c line 316 │ stuffs bits into coreGlobals.swMatrix[] │ 6800 CPU scans switch matrix via PIA
Differential updates UpdateOutputs() caches the previous state of every lamp group (OutputLEDsPrevious[]) and the solenoid word (OutputsSolenoidPrevious). USB commands are only sent when a value actually changes, keeping USB traffic minimal.

03 Key Source Files

src/pacdrive.c
The hardware abstraction layer. Loads pacdrive32.dll, manages the 112-element output array, and sends USB commands to both boards. This is the main file added to PinMAME for this project.
src/pacdrive.h
Function pointer typedefs for the five DLL entry points.
src/wpc/core.c :1719
update_hW() and update_hw_byte() — the bridge that converts the emulated lamp matrix and solenoid state into calls to pacdrive.c. Called once per VBLANK.
src/wpc/byvidpin.c
Baby Pac-Man MPU-133 hardware emulation: PIA read/write handlers, lamp strobe logic (byVP_lampStrobe), solenoid decoding (pia1b_w), and the VBLANK interrupt handler.
src/wpc/byvidpin.h
Input port macro BYVP_babypac_PORT — hardcoded default keyboard key assignments for joystick, flipper, start, coin, and tilt. Also defines all BYVP_SW* switch constants.
src/wpc/byvidgames.c
Game definition: ROM file names, checksums, display type, and FLIP_SWNO(0,1) which sets the right-flipper switch number.
src/mame.c :348
Calls LoadPacDrive() during machine initialization when the -ultimateio flag is present.
src/driver.h :96
Declares the ultimateio field in the pmoptions struct.
cfg/babypac.cfg
Binary MAME config file storing all user-configured key-to-switch mappings (set via the in-game Tab menu). This is where playfield switch assignments live after you've configured them.
docs/BabyPacman-LampMapping-v2.txt
Authoritative lamp matrix reference: all 30 SCR positions × 2 AC phases, verified against official Bally A3 driver board schematics.

04 Startup & Configuration

rem babypac.bat
call setvol 80
pinmame babypac -ultimateio -joystick -dmd_only -scanlines -skip_gameinfo
FlagEffect
-ultimateioEnables the hardware I/O path — calls LoadPacDrive() on startup
-joystickEnables gamepad/joystick input (I-PAC can report as HID gamepad)
-dmd_onlySuppresses the desktop emulator window; video goes to the DMD
-scanlinesApplies CRT scanline filter to the video output
-skip_gameinfoBypasses the splash/info screen at startup

Initialization Sequence

1
mame.c checks flag
if (pmoptions.ultimateio) → LoadPacDrive()
2
Allocate output arrays
malloc(112) for Outputs[]; malloc(12) for OutputLEDsPrevious[]
3
Load the DLL
LoadLibrary("pacdrive32") — must be in same directory as pinmame.exe
4
Resolve 5 function pointers
PacInitialize, PacShutdown, Pac64SetLEDStates, Pac64SetLEDState, PacSetLEDStates
5
Register cleanup
atexit(UnloadPacDrive) — ensures outputs go dark on exit
6
InitOutputs()
Zeros all 112 outputs; sets previous-state caches to 0xFF/65535 to force a full update on first frame
7
PacInitialize()
Enumerates connected Ultimarc USB devices; returns 0 on failure

On exit, UnloadPacDrive() calls InitOutputs() (turns everything off), frees memory, then FreeLibrary(hio).

Both boards must be connected before launch PacInitialize() is called once at startup and enumerates all connected Ultimarc USB devices. If either the I-PAC Ultimate I/O or the PAC-Drive is unplugged when PinMAME starts, LoadPacDrive() returns failure and neither board receives any commands for the entire session — lamps and solenoids will both be dead even if one board is present. Always connect both boards before running babypac.bat.
Failure mode If LoadPacDrive() fails (DLL missing or no device found), PinMAME prints "Unable to communicate with Ultimate IO." and continues running — the game still works as a pure emulator, just without hardware output. The exit(1) call is commented out in mame.c:354.

05 I-PAC Ultimate I/O Board

The I-PAC Ultimate I/O handles all 96 lamp outputs and accepts up to 48 switch inputs. It appears as a standard USB HID device — no special Windows driver needed.

This machine: direct-driven LEDs, no original lamp matrix The original Baby Pac-Man switched its lamps through a strobed SCR matrix. This restoration reuses none of that wiring — all under-playfield wiring was replaced. Each LED is run with its own dedicated pair of wires: one to a shared +5 V common, the other to a single output pin on the Ultimate I/O, which sinks that LED's current to turn it on or off. Every lamp is therefore driven individually, not multiplexed. (In software the ROM's lamp matrix still maps 1:1 onto the outputs — lamp number = pin number — but physically there is no matrix left on the playfield.)

USB Identity

  • Vendor ID0xD209 (Ultimarc)
  • Product ID0x0410
  • USB speedFull Speed USB 2.0
  • DriverNone needed (HID)

I/O Specs

  • LED outputs96 channels
  • Brightness256 levels per channel
  • Switch inputs48 dedicated pins
  • Power in5V DC, 2.1mm barrel
  • Per-LED current20mA max

How the LED command works

Every frame, UpdateOutputs() advances a per-lamp brightness (0–255) toward its ROM target, then pushes all 96 outputs to the board in one bulk USB transfer. Driving real brightness — not just on/off — is what produces the incandescent-style fade. See Incandescent Lamp Fade for the full model and the user-tunable controls.

// pacdrive.c: UpdateOutputs() — lamp section (Approach C)
for (int i = 0; i < 96; i++) {
    int target = Outputs[i] ? 255 : 0;   // ROM on / off
    // ease LedIntensity[i] toward target (exp / s / lin),
    // then map through the perceptual gamma table:
    pushBuf[i] = GammaLUT[LedIntensity[i]];
}
Pac64SetLEDIntensities(hw_id_ultimateio, pushBuf);  // all 96 in one call, ~30 Hz

The board's 256-level PWM per channel is what makes the fade possible. (Earlier builds sent pure on/off group states with Pac64SetLEDStates; the current build sends per-LED brightness with Pac64SetLEDIntensities.) The hw_id_ultimateio constant is 0 — the first device enumerated by the DLL.

Input side

The I-PAC's 48 inputs are wired to every physical switch on the cabinet and playfield. Using WinIPAC V2, each input is assigned a keyboard key or gamepad button. When a switch closes, the I-PAC sends that key to Windows, which PinMAME reads and routes to the switch matrix. See the Switch & Input Mapping section for the full table.

Configuration software WinIPAC V2 (free from Ultimarc) is the utility that programs key assignments into the I-PAC's flash memory. Changes take effect immediately without a reboot. The I-PAC ships with MAME defaults pre-loaded. You can also configure it to report as a dual gamepad or XInput controller.

06 PAC-Drive Board

The PAC-Drive is a simple 16-channel output board used to fire the pinball solenoids — flippers, saucers, drop target reset, and the outhole kicker. Each channel is either on or off; the entire state is sent as a single 16-bit word.

USB Identity

  • Vendor ID0xD209 (Ultimarc)
  • Product ID0x1500
  • Protocol4-byte HID report (type 0x03)
  • DriverNone needed

Output Specs

  • Channels16 (on/off)
  • Max current500mA per channel
  • Voltage (USB)5V
  • Voltage (ext)Up to 48V
  • Multiple boardsUp to 4 on one PC

How the solenoid command works

// pacdrive.c: UpdateOutputs() — solenoid section
unsigned short binary_short = 0;
binary_short |= Outputs[outputNum + 0]  << 0;   // bit 0  → PAC-Drive channel 1
// ... through ...
binary_short |= Outputs[outputNum + 15] << 15;  // bit 15 → PAC-Drive channel 16

if (OutputsSolenoidPrevious != binary_short) {
    OutputsSolenoidPrevious = binary_short;
    PacSetLEDStates(hw_id_pacdrive, binary_short);
}

The hw_id_pacdrive constant is 1 — it's the second device enumerated by the DLL after the Ultimate I/O.

Critical: flyback diodes required Every solenoid coil must have a flyback diode across it. Without a diode, the back-EMF spike when the coil de-energizes will damage the PAC-Drive board. The custom BabyPacDriver-v4 PCB (see Custom Driver PCB) handles this.
Why not PWM hold-power control? The PAC-Drive only does on/off — the API has no PWM mode. This is why the flipper mechanisms must use the traditional dual-winding coil with EOS switch. The EOS switch physically cuts the power winding when the flipper reaches end-of-stroke, leaving only the low-current hold winding active. No software timing needed.

Board Layout — Screw Terminals

The PAC-Drive uses screw terminals along both edges. Output channels are numbered 1–16. There is also a GND terminal. The board is powered entirely by USB — no external power connector for logic. When connecting an external high-voltage supply through the driver PCB, the external supply's ground must be tied back to the USB ground (see Build & Wiring Guide).

TerminalNetNotes
1–16Output channelsOpen-drain. Pulled LOW when software sets the corresponding bit. Pulled HIGH by the driver board's pull-up resistor (RN4) when idle.
GNDGroundUSB ground. Must be tied to common ground bus shared with I-PAC and Meanwell supply.

Ordering the Correct Version

Ultimarc sells several PAC-Drive variants. For this project you need the "ID #1 Special" version (also described as "ID #1 – outputs default OFF"). This version:

The standard PAC-Drive (ID #0) would conflict with the I-PAC Ultimate I/O, which also uses hw_id = 0. Do not substitute the standard version.

Device ID assignment The hardware ID is burned into the board's firmware at the factory — it is not determined by USB enumeration order. The I-PAC Ultimate I/O always appears as hw_id = 0 and the PAC-Drive "ID #1 Special" always appears as hw_id = 1, regardless of which USB port they are plugged into or which is enumerated first.

Wiring PAC-Drive to the Custom Driver PCB

Run individual signal wires from the PAC-Drive's numbered screw terminals to the JP1–JP4 header pins on the BabyPacDriver-v4 PCB. Also bring over a GND connection to JP9 pins 1 & 2, and a 5V source to JP9 pin 3 (see JP9 Power Connector table in Custom Driver PCB).

PAC-Drive terminal→ Driver boardSolenoid
1JP1 pin 1Left Flipper
2JP1 pin 2Right Flipper
3JP1 pin 3Out Hole Kicker
4JP2 pin 1Left Maze Saucer
5JP2 pin 2Right Maze Saucer
6JP2 pin 3Drop Target Reset
7JP3 pin 1Drop Target #1 (Left)
8JP3 pin 2Drop Target #3 (Center)
9JP3 pin 3Drop Target #5 (Right)
10–16JP4 / unassignedUnused — available for expansion

07 Custom Solenoid Driver PCB

The files in Solenoid Driver board schematics/ contain the design for a custom PCB (Eagle schematic + board layout + BOM) that sits between the PAC-Drive's 5V logic outputs and Baby Pac-Man's original high-voltage solenoid coils.

This machine's driver board was hand-built, not ordered For this restoration the solenoid driver board was built by hand from scratch, using the schematics and design in this folder as the basis for the circuit — it was not fabricated or ordered from any vendor. The files below document the design it was modeled on.
BabyPacDriver-v4.sch
Eagle schematic — full circuit for all solenoid driver channels
BabyPacDriver-v4.brd
Eagle board layout — physical PCB design
BabyPacDriver-v4-BOM.xlsx
Bill of materials — all components with part numbers
BabyPacDriver-v4b (2)/
Revised variant of the board

This PCB provides:

Signal Path Through the PCB

Signals travel left to right through the board:

1
PAC-Drive signal inputs — JP1, JP2, JP3, JP4
Four 3-pin headers on the left edge. JP1 carries PAC-Drive outputs 1–3, JP2: 4–6, JP3: 7–9, JP4: 10–12. Signal lines only — no GND pin on these headers. GND is provided separately through JP9.
2
Pull-up resistor network — RN4 (4.7 kΩ)
Each signal line is pulled up to VCC via a resistor in RN4. Holds inputs HIGH when the PAC-Drive line is idle or undriven (compatible with open-drain outputs).
3
SN74HCT04N hex inverters — U1, U2
JP1 + JP2 signals drive U1 inputs (1A–6A). JP3 + JP4 signals drive U2 inputs (1A–6A). The SN74HCT04N has standard push-pull outputs and HCT-family TTL-compatible input thresholds. Runs from VCC (5V). C1 and C2 (0.1 µF each) are bypass capacitors placed at the VCC/GND pins of U1 and U2 respectively to suppress switching noise.
4
Gate resistors — RN1, RN2 (220 Ω each)
Each inverter output drives a MOSFET gate through a 220 Ω series resistor, limiting gate charge current inrush.
5
N-channel MOSFETs — Q1–Q12 (IRLZ34N)
Logic-level N-channel MOSFETs. Drain connects to the high-voltage solenoid side; source to GND. IRLZ34N switches fully on at 5V gate drive. Pull-down resistors RN3 (4.7 kΩ) hold each gate LOW when the inverter output is not asserting HIGH, preventing false triggering.
6
Solenoid output terminals — JP5, JP6, JP7, JP8
Screw terminals on the bottom edge for coil wiring. JP5: channels 1–3 (Q1–Q3), JP6: channels 4–6 (Q4–Q6), JP7: channels 7–9 (Q7–Q9), JP8: channels 10–12 (Q10–Q12, currently unassigned).
Active-LOW input logic PAC-Drive output asserted (1 in software) → signal pulled LOW → SN74HCT04N input LOW → push-pull output HIGH → MOSFET gate HIGH → MOSFET conducts → solenoid fires.

When idle: PAC-Drive output not asserted → RN4 holds signal HIGH → SN74HCT04N input HIGH → output LOW → MOSFET gate LOW (also held by RN3 pull-down) → MOSFET off.

The inversion is intentional: it allows the PAC-Drive's active-LOW outputs to drive the IRLZ34N gates correctly through a standard push-pull CMOS inverter.

MOSFET Pinout — IRLZ34N (TO-220)

Each driver channel uses an IRLZ34N logic-level N-channel MOSFET in TO-220 package. With the flat face toward you and leads pointing down:

  Flat face →  ┌──────────────┐
               │  IRLZ34N     │
               │              │
               └──┬───┬───┬──┘
                  │   │   │
                Pin1 Pin2 Pin3
                Gate Drain Source
                      (tab)
                 ↑         ↑
               Left       Right
PinPositionNetConnects to
1LeftGateGate resistor (RN1 or RN2 output) — receives signal from SN74HCT04N inverter
2 (tab)Middle / heatsink flangeDrainOutput terminal (JP5–JP8) — connects to solenoid coil
3RightSourceGND — common ground bus
Drain = middle pin = output terminal The middle pin and the heatsink tab are electrically the same node (Drain). Wiring the middle pin directly to the output screw terminal is correct — this is how the board is designed.

Power Connector — JP9

JP9 is a 3-pin screw terminal that supplies 5 V logic power to the inverter ICs (U1, U2). It must be connected before any signals are driven.

JP9 PinNetVoltageConnect to
Pin 3VCC+5 V5 V rail — PAC-Drive USB 5V output or regulated 5V supply
Pin 2GND0 VCommon ground shared with PAC-Drive
Pin 1GND0 VCommon ground (second GND pin)
Decoupling capacitors C1 and C2 C1 (0.1 µF ceramic, placed next to U1) and C2 (0.1 µF ceramic, placed next to U2) each bridge VCC to GND at their respective IC's power pins. One cap per IC is standard practice for 74HC-family logic — they supply a local charge reservoir to absorb the brief current spike when the IC's outputs switch, preventing that spike from coupling as noise onto the shared VCC rail.

5V Power Source for JP9

JP9 VCC can be supplied from any of the following. All options require the 5V source's ground to be part of the common ground bus:

The 5V does not need to come from the PAC-Drive itself — the PAC-Drive's screw terminals expose output channels and GND, but those output lines are open-drain (not a 5V source output). Use any 5V supply that shares the common ground.

Bench Testing the Driver Board

Use this procedure to verify each MOSFET channel before connecting any real solenoids. You need: a 5V supply, a multimeter, a 470 Ω resistor, and an LED.

1
Visual inspection
Inspect for cold solder joints, solder bridges, and correct component orientation. Verify MOSFET orientation (Gate/Drain/Source as described above). Check that all pull-up and pull-down resistors are populated.
2
Power-up check
Apply 5V to JP9 pin 3; ground to pins 1 & 2. Measure VCC at the IC power pins of U1 and U2 — should read 5.0V ± 0.25V. Verify GND = 0V at source pins of MOSFETs.
3
Signal switching test
For each channel: with the JP1–JP4 input pin floating (HIGH via pull-up), the corresponding output terminal should read near 0V (MOSFET off, drain floating). Then momentarily jump that input pin to GND — the output terminal should drop to 0V through the MOSFET. A 0.4V → 0.0V change is normal and expected on a floating node.
4
Dummy load test (LED)
Wire a 470 Ω resistor and LED in series between the output terminal and your test supply positive. Jump the input pin to GND — the LED should light. This confirms the MOSFET can sink current (not just show 0V on a floating node).
5
Live solenoid test
Connect a known-good solenoid to the output terminal. Connect both USB boards to the PC. Launch PinMAME with -ultimateio. Trigger the corresponding game event (e.g., press the coin switch to start a game and look for the out-hole kicker to fire). Observe the Setting Solenoid HwID: 1 to value X printf output in the console to confirm software is sending the command.

PAC-Drive Signal Wiring — JP1–JP4

Run individual signal wires from the PAC-Drive's output connector to the 3-pin headers on the driver board. The table below shows the complete path from each header pin through to the solenoid output terminal. The Solenoid Assignment Table cross-references these hardware outputs to the software output numbers and game mechanisms.

Input connector Pin PAC-Drive output Inverter input MOSFET Output terminal
JP111U1 – 1AQ1JP5 pin 1
22U1 – 2AQ2JP5 pin 2
33U1 – 3AQ3JP5 pin 3
JP214U1 – 4AQ4JP6 pin 1
25U1 – 5AQ5JP6 pin 2
36U1 – 6AQ6JP6 pin 3
JP317U2 – 1AQ7JP7 pin 1
28U2 – 2AQ8JP7 pin 2
39U2 – 3AQ9JP7 pin 3
JP4110U2 – 4AQ10JP8 pin 1
211U2 – 5AQ11JP8 pin 2
312U2 – 6AQ12JP8 pin 3

08 pacdrive32.dll API

The DLL is Ultimarc's closed-source Windows library. It is loaded dynamically at startup using LoadLibrary(), and seven function pointers are resolved by name (pacdrive.h):

// pacdrive.h — function pointer typedefs
typedef int  (WINAPI *PACINITIALIZE)   (void);
typedef void (WINAPI *PACSHUTDOWN)     (void);
typedef int  (WINAPI *PAC64SETLEDSTATES) (int id, int group, char data);
typedef int  (WINAPI *PAC64SETLEDSTATE)  (int id, int group, int port, int state);
typedef int  (WINAPI *PACSETLEDSTATES)  (int id, short data);
typedef int  (WINAPI *PAC64SETLEDFADETIME)    (int id, int fadeTime);
typedef int  (WINAPI *PAC64SETLEDINTENSITIES) (int id, unsigned char *data);
FunctionParametersUsed for
PacInitialize() none Enumerate all connected Ultimarc USB devices. Returns 0 on failure.
PacShutdown() none Clean shutdown. Called on exit (via atexit).
Pac64SetLEDStates(id, group, data) id=0, group 1–12, data 0–255 Set one group of 8 lamp LEDs on the Ultimate I/O. Each bit = one LED.
Pac64SetLEDState(id, group, port, state) id=0, group, port 0–7, state 0/1 Set a single LED on/off — state is a boolean, not a brightness. Not used in the main loop.
Pac64SetLEDIntensities(id, data) id=0, data = 96 bytes, 0–255 each The lamp path. Set all 96 LED brightnesses in one bulk transfer. Drives the incandescent fade — see §22.
Pac64SetLEDFadeTime(id, fadeTime) id=0, fadeTime The board's own hardware fade (legacy "Approach A"). Left off — the software fade is finer.
PacSetLEDStates(id, data) id=1, data 0–65535 Set all 16 solenoid outputs on the PAC-Drive in one call. Each bit = one channel.

Device IDs are positional: hw_id_ultimateio = 0 (first device), hw_id_pacdrive = 1 (second device). If you add a second PAC-Drive board, it would be id=2.

09 Output Array Layout

All output state is held in a single flat array Outputs[112] (1 byte per output, value 0 or 1). UpdateOutputs() reads this array and issues USB commands to the appropriate board.

Outputs[0..95] 96 LED outputs → I-PAC Ultimate I/O (index 0 – 95)
Outputs[96..111] 16 solenoid outputs → PAC-Drive (index 96 – 111)
Array rangeCountDestination boardPhysical meaning
Outputs[0..95] 96 I-PAC Ultimate I/O Lamp matrix — 12 groups × 8 bits. Groups 1–6 = Phase A (Strobe 1), Groups 7–12 = Phase B (Strobe 2)
Outputs[96..111] 16 PAC-Drive Solenoid outputs — flippers, saucers, drop targets, out hole. Only 9 currently assigned.

10 Lamp Matrix Flow

Physical hardware

Baby Pac-Man uses 30 SCR (Silicon Controlled Rectifier) drivers labeled Q01–Q30 on the A3 Driver Board (Q10 and Q21 are unpopulated). Each SCR fires on one of two AC phases:

This gives 60 possible lamp positions (30 SCRs × 2 phases), of which 56 are wired. The remaining 4 positions (Q10A, Q10B, Q21A, Q21B) are available for custom additions.

Emulator representation

1
PIA writes fire lamp strobes
byVP_lampStrobe(board, lampadr) — byvidpin.c:149
Accumulates into coreGlobals.tmpLampMatrix[]
2
VBLANK copies to live matrix
memcpy(lampMatrix, tmpLampMatrix, ...) — byvidpin.c:299
8 bytes = 64 bits (lampMatrix[0..3] = Phase A, [4..7] = Phase B)
3
update_hW() unpacks bytes to bits
update_hw_byte(outputStart, lampMatrix[i]) — core.c:1752
Each byte → 8 individual Outputs[] entries
4
UpdateOutputs() re-packs and sends
Pac64SetLEDStates(0, group, binary_byte) — pacdrive.c:154
One call per group that changed
// core.c: update_hW() — lamp section
int outputNum = 1;
for (int i = 0; i < 8; i++) {
    update_hw_byte(outputNum, coreGlobals.lampMatrix[i]);
    outputNum += 8;   // advances 8 outputs per byte
}

// update_hw_byte() — unpacks one byte into 8 output slots
void update_hw_byte(int outputStart, unsigned char value) {
    PacDriveSetOutput(outputStart + 0, value >> 0 & 0x01);
    // ... through ...
    PacDriveSetOutput(outputStart + 7, value >> 7 & 0x01);
}

11 Solenoid Flow

From ROM to coil

1
6800 writes to PIA1 Port B
pia1b_w() — byvidpin.c:218
Bits 0–2: select one of 7 momentary solenoids (pulsed when p1_cb2=0)
Bits 4–7 (inverted): continuous solenoids 8–11 (flipper relay, etc.)
2
Accumulated in locals.solenoids
UINT32, smoothed over BYVP_SOLSMOOTH VBLANK intervals (= 1)
3
VBLANK syncs to global state
coreGlobals.solenoids = locals.solenoids — byvidpin.c:307
4
update_hW() extracts bit fields
UINT64 allSol = core_getAllSol()
Packs solenoids, solenoids2 (flipper), and pulsed states into one 64-bit word
5
PacDriveSetOutput() per solenoid
Maps specific bits of allSol to Outputs[96..105]
6
UpdateOutputs() sends 16-bit word
PacSetLEDStates(1, binary_short) — only when value changes

12 Switch & Input Mapping

There are two independent layers. Getting a switch into the game requires both to be configured correctly.

Layer 1 — Physical pin → key (WinIPAC V2)

Each of the I-PAC's 48 physical input pins is assigned a keyboard key or gamepad button using the WinIPAC V2 utility. This is stored in the I-PAC's own flash memory. PinMAME never sees the physical wiring — it only sees keystrokes.

Layer 2 — Key → switch matrix position (PinMAME)

PinMAME maps each key to a position in coreGlobals.swMatrix[], which the emulated 6800 CPU reads when it scans the switch matrix. There are two sub-layers:

a) Hardcoded defaults — byvidpin.h:53

These are compiled in and apply on a fresh install. They cover the main cabinet controls:

Default KeyFunctionswMatrix destinationPort bit
Left ArrowJoystick LeftswMatrix[0] bit 50x0020
Right ArrowJoystick RightswMatrix[0] bit 40x0010
Up ArrowJoystick UpswMatrix[0] bit 70x0080
Down ArrowJoystick DownswMatrix[0] bit 60x0040
Left ShiftLeft Flippersolenoids2 (not swMatrix)CORE_LLFLIPKEY
Right ShiftRight Flippercore_setSw(1, …)CORE_LRFLIPKEY
1Start Player 1swMatrix[1] bit 5IPT_START1
2Start Player 2swMatrix[1] bit 2IPT_START2
5Coin 1swMatrix[2] bits 0–1IPT_COIN1
6Coin 2swMatrix[2] bits 0–1IPT_COIN2
DeleteBall TiltswMatrix[2] bit 60x1000
HomeSlam TiltswMatrix[2] bit 70x2000
7Self TestswMatrix[0] bit 00x0001
8Video DiagnosticswMatrix[0] bit 30x0008
9CPU DiagnosticswMatrix[0] bit 10x0002
0Sound DiagnosticswMatrix[0] bit 20x0004

b) User-configured mappings — cfg/babypac.cfg

All playfield switches (drop targets, saucers, rollovers, out hole, tilt bob, etc.) are configured through PinMAME's in-game Tab menu → "Input (this game)". Those assignments are stored in the binary cfg/babypac.cfg file. The file format is MAME's proprietary MAMECFG binary format and isn't human-readable, but the Tab menu provides a full visual editor.

Viewing or changing playfield switch assignments Launch the game, press Tab, choose Input (this game). Every switch in Baby Pac-Man's switch matrix is listed by name. Click any entry to re-assign it to a different key or gamepad button.

13 Flipper Wiring & Signal Path

Flippers have a special path that bypasses the normal switch matrix and directly populates coreGlobals.solenoids2.

Signal flow

Left Shift key pressed → inports[CORE_FLIPINPORT] bit 0 set (CORE_LLFLIPKEY = 0x0001) → swFlip |= CORE_SWLLFLIPBUTBIT core.c:968 → coreGlobals.solenoids2 |= CORE_LLFLIPSOLBITS core.c:994 → core_getAllSol() packs solenoids2 → allSol bits 44–46 → update_hW(): PacDriveSetOutput(97, allSol >> 46 & 0x01) Left flipper → Outputs[96] = 1 → UpdateOutputs() → PacSetLEDStates(1, binary_short)PAC-Drive channel 1 fires → Custom driver PCB → Solenoid power winding energizes → EOS switch opens at end of stroke → only hold winding remains

Game definition: FLIP_SWNO(0, 1)

From byvidgames.c:16: INITGAMEVP(babypac, ..., FLIP_SWNO(0,1), ...)

The macro FLIP_SWNO(l, r) encodes which playfield switch numbers the game ROM reads for flipper feedback:

EOS switch — use dual-winding coils The PAC-Drive is on/off only — no PWM. Use dual-winding flipper coils with a mechanical EOS (End-of-Stroke) switch wired in series with the power winding. This is the original Bally design and requires no software changes. The EOS switch cuts the power winding when the flipper reaches full stroke; the hold winding stays on at lower current. Single-winding PWM-style coils cannot be driven safely by the PAC-Drive without significant code and hardware changes.

14 Full Lamp Reference

All 56 active lamps, verified against the official Bally A3 Driver Board schematics and lamp chart. This is the Version 2 mapping — see the corrections section below for errors found in the original mapping document.

Architecture

The lamp matrix uses 30 SCR (Silicon Controlled Rectifier) drivers labeled Q01–Q30 on the A3 Driver Board. Q10 and Q21 are unpopulated. Each SCR controls two lamps via AC phase multiplexing:

Phase A — Strobe 1

Positive half-cycle

Fires during the positive half of the AC waveform. Maps to lampMatrix[0..3] in the emulator.

Phase B — Strobe 2

Negative half-cycle

Fires during the negative half of the AC waveform. Maps to lampMatrix[4..7] in the emulator.

30 SCRs × 2 phases = 60 possible positions. 56 are wired; 4 positions (Q10A, Q10B, Q21A, Q21B) are unused and available for custom modifications.

Complete Mapping by SCR Position

This is the primary wiring reference — use this when tracing wires from the A3 board to the playfield.

SCR Phase A — Strobe 1 (positive half-cycle) Phase B — Strobe 2 (negative half-cycle)
Q01Fruits "R"Fruits "I"
Q02Tunnel "U"Tunnel "N" (Second)
Q03Fruits "S"Inside Lane Left
Q04Spinner Arrow LeftTunnel "L"
Q05Row #1 "M"Row #1 "A" (First)
Q06Saucer Arrow LeftRow #1 "N"
Q07Row #2 "A" (First)Row #2 "M"
Q08Outhole Left Side FlasherTunnel Outlane
Q09Arrow 4KArrow 8K
Q10UNUSEDUNUSED
Q11Row #3 "M"Row #3 "A" (First)
Q12Fruit OutlaneRow #4 "N"
Q13Saucer Arrow RightRow #2 "N"
Q14Shoot AgainRow #3 "N"
Q15Tunnel "T"Tunnel "N" (First)
Q16Energizer #1Row #1 "A" (Second)
Q17Arrow 2KArrow 6K
Q18Row #4 "M"Row #4 "A" (First)
Q19Fruits "T"Inside Lane Right
Q20Row #2 "P"Row #2 "C"
Q21UNUSEDUNUSED
Q22Fruits "F"Fruits "U"
Q23Tunnel "E"Spinner Arrow Right
Q24Energizer #2Row #2 "A" (Second)
Q25Row #3 "A" (Second)Energizer #3
Q26Row #4 "C"Row #4 "P"
Q27Row #1 "P"Row #1 "C"
Q28Extra Ball ArrowOuthole Right Side Flasher
Q29Row #3 "C"Row #3 "P"
Q30Energizer #4Row #4 "A" (Second)

I-PAC Ultimate I/O Pin Assignments

Physical connector pin → lamp mapping for the I-PAC Ultimate I/O board, as wired on this build. Strobe 1 (pins 1–32) and Strobe 2 (pins 33–65) are the two output connector groups.

Five structurally dead outputs Pins 16, 32, 48, 64, and 65 are permanently 0 — no software write can ever set them. Pins 16, 32, 48, and 64 correspond to lampadr=15, which is filtered out by an if (lampadr != 0x0f) guard in byVP_lampStrobe(). Pin 65 falls outside the 8-iteration lamp loop. These outputs are listed as DEAD in the table. The real lamps that appear to be at those pin positions actually fire at the correct pins shown in the "Organized by Function" section.
Strobe 1 — Pins 1–32 Strobe 2 — Pins 33–65
01Fruits "U" 33Fruits "F"
02Right Inside Lane 34Fruits "T"
03Tunnel "N" (1st) 35Tunnel "T"
04Right Spinner Arrow 36Tunnel "E"
05Row #1 "C" 37Row #1 "P"
06Energizer #1 38Row #1 "A" (2nd)
07Row #2 "C" 39Row #2 "P"
08Energizer #2 40Row #2 "A" (2nd)
09Row #3 "C" 41Row #3 "P"
10Energizer #3 42Row #3 "A" (2nd)
11Row #4 "C" 43Row #4 "P"
12Energizer #4 44Row #4 "A" (2nd)
13Arrow 6K 45Arrow 2K
14Outhole Right Side Flasher 46Extra Ball Arrow
15unused — no lamp (confirmed dark) 47unused — no lamp (confirmed dark)
16DEAD — lampadr=15, never driven 48DEAD — lampadr=15, never driven
17Fruits "I" 49Fruits "R"
18Left Inside Lane 50Fruits "S"
19Tunnel "N" (2nd) 51Tunnel "U"
20Left Spinner Arrow 52Tunnel "L"
21Row #1 "M" 53Row #1 "A" (1st)
22Left Saucer Arrow 54Row #1 "N"
23Row #2 "M" 55Row #2 "A" (1st)
24Right Saucer Arrow 56Row #2 "N"
25Row #3 "M" 57Row #3 "A" (1st)
26Shoot Again 58Row #3 "N"
27Row #4 "M" 59Row #4 "A" (1st)
28Fruit Outlane 60Row #4 "N"
29Arrow 8K 61Arrow 4K
30Outhole Left Side Flasher 62Tunnel Outlane
31unused — no lamp (confirmed dark) 63unused — no lamp (confirmed dark)
32DEAD — lampadr=15, never driven 64DEAD — lampadr=15, never driven
(no pair) 65DEAD — beyond 8-iteration lamp loop

Organized by Function

Same data, grouped by lamp purpose — useful for understanding game logic and testing lamp groups.

Energizers (4)

Q16 Ph-AEnergizer #1
Q24 Ph-AEnergizer #2
Q25 Ph-BEnergizer #3
Q30 Ph-AEnergizer #4

FRUITS Letters (6)

Q22 Ph-AF
Q01 Ph-AR
Q22 Ph-BU
Q01 Ph-BI
Q19 Ph-AT
Q03 Ph-AS

TUNNEL Letters (6)

Q15 Ph-AT
Q02 Ph-AU
Q15 Ph-BN (1st)
Q02 Ph-BN (2nd)
Q23 Ph-AE
Q04 Ph-BL

PAC-MAN Row 1 (6)

Q27 Ph-AP
Q05 Ph-AA (1st)
Q27 Ph-BC
Q05 Ph-BM
Q16 Ph-BA (2nd)
Q06 Ph-BN

PAC-MAN Row 2 (6)

Q20 Ph-AP
Q07 Ph-AA (1st)
Q20 Ph-BC
Q07 Ph-BM
Q24 Ph-BA (2nd)
Q13 Ph-BN

PAC-MAN Row 3 (6)

Q29 Ph-BP
Q11 Ph-BA (1st)
Q29 Ph-AC
Q11 Ph-AM
Q25 Ph-AA (2nd)
Q14 Ph-BN

PAC-MAN Row 4 (6)

Q26 Ph-BP
Q18 Ph-BA (1st)
Q26 Ph-AC
Q18 Ph-AM
Q30 Ph-BA (2nd)
Q12 Ph-BN

Scoring Arrows (8)

Q17 Ph-AArrow 2K
Q09 Ph-AArrow 4K
Q17 Ph-BArrow 6K
Q09 Ph-BArrow 8K
Q04 Ph-ASpinner Left
Q23 Ph-BSpinner Right
Q06 Ph-ASaucer Left
Q13 Ph-ASaucer Right

Special Indicators (5)

Q28 Ph-AExtra Ball Arrow
Q14 Ph-AShoot Again
Q19 Ph-BInside Lane Right
Q03 Ph-BInside Lane Left
Q12 Ph-AFruit Outlane

Flashers (3)

Q08 Ph-AOuthole Left
Q28 Ph-BOuthole Right
Q08 Ph-BTunnel Outlane

Unused (4 positions)

Q10 Ph-ANot connected
Q10 Ph-BNot connected
Q21 Ph-ANot connected
Q21 Ph-BNot connected

Technical Hardware Details

A3 Driver Board

  • BoardA3 Driver Board
  • Transistor2N5060 (SCR)
  • Phase controlAC zero-crossing, 120 Hz
  • Total SCRs30 (Q01–Q30)
  • Active lamps56 of 60 positions

Connector Assignments

  • A3J1SCRs Q01–Q18 (odd pins)
  • A3J2SCRs Q08–Q28 (various)
  • A3J3SCRs Q15–Q30 (various)
  • Wire colorsRed, Blu, Grn, Yel, Orn, Brn, Gry, Blk, Wht

Wiring Notes

Corrections from the Original Mapping Document

The original BabyPacman-LampMapping.txt (v1) contained several errors, corrected in v2 against the official Bally schematics:

Error 1 — Energizer #3 wrong phase Originally assigned to Strobe 1 (Phase A). Correct assignment is Q25 Phase B (Strobe 2).
Error 2 — FRUITS phase inversions "U" and "I" were swapped between strobes. Correct: F and R are Phase A; U and I are Phase B.
Error 3 — TUNNEL has two "N" lamps There are TWO separate "N" lamps, both on Phase B: First N is Q15 Ph-B, Second N is Q02 Ph-B. The original document did not distinguish these.
Error 4 — Sequential numbering (1–65) was wrong The original used sequential numbers that did not reflect actual hardware SCR addressing. The correct reference uses SCR positions (Q01–Q30) with explicit phase labels.
Error 5 — Q27 Phase A/B swapped in v2 top table BabyPacman-LampMapping-v2.txt's "Complete Mapping by SCR Position" table has Q27 Phase A and Phase B reversed. The correct assignment (confirmed by I-PAC pin data) is: Q27 Phase A = Row #1 "P", Q27 Phase B = Row #1 "C". The "Organized by Function" section in v2 is correct. The SCR table on this page has been corrected.
Error 6 — Pin assignments corrected by physical testing (confirmed 2026-07) The SCR chart gives each lamp's driver and phase, but the exact I-PAC pin each lamp lands on was verified lamp-by-lamp on the machine — and several differed from the paper derivation. The pin table above is the confirmed result. Highlights:
  • Scoring arrows: 6K = pin 13, 8K = pin 29, 2K = pin 45, 4K = pin 61
  • Extra Ball Arrow = pin 46, Shoot Again = pin 26
  • Outhole side flashers = pins 14 & 30 (both fire together)
  • Structurally dead (lampadr = 15, never driven): pins 16, 32, 48, 64; pin 65 is beyond the 8-iteration lamp loop
  • Drivable but unwired — no lamp present: pins 15, 31, 47, 63

Verification Status

✓ Verified against official Bally lamp chart ✓ Cross-referenced with A3 driver board schematics ✓ Confirmed with wire color codes and connector assignments ✓ Validated SCR positions and phase assignments

This mapping is authoritative and suitable for production hardware builds.

15 Solenoid Assignment Table

Software assignments defined in src/wpc/core.c:1733–1747 inside update_hW(). The PAC-Drive output number and driver board columns show the physical hardware path — see Custom Driver PCB for full wiring details.

Output # Outputs[] index PAC-Drive bit PAC-Drive output Solenoid / Mechanism allSol bit Driver board output
97Outputs[96]bit 0output 1Left Flipperbit 46JP5 pin 1
98Outputs[97]bit 1output 2Right Flipperbit 44JP5 pin 2
99Outputs[98]bit 2output 3Out Hole Kickerbit 0JP5 pin 3
100Outputs[99]bit 3output 4Left Maze Saucerbit 8JP6 pin 1
101Outputs[100]bit 4output 5Right Maze Saucerbit 9JP6 pin 2
102Outputs[101]bit 5output 6Drop Target Resetbit 1JP6 pin 3
103Outputs[102]bit 6output 7Drop Target #1 (Left)bit 2JP7 pin 1
104Outputs[103]bit 7output 8Drop Target #3 (Center)bit 4JP7 pin 2
105Outputs[104]bit 8output 9Drop Target #5 (Right)bit 6JP7 pin 3
106–112Outputs[105..111]bits 9–15outputs 10–16Unused — available for expansionJP8 pins 1–3 + unrouted
About Drop Targets #2 and #4 Only drop targets 1, 3, and 5 (left, center, right) have individual solenoids assigned. Drop targets 2 and 4 do not have separate knockdown coils — they are controlled mechanically by the reset mechanism.

16 Switch Matrix Quick Reference

Column 0 (swMatrix[0]) is the "cabinet" column — special switches handled directly by the SWITCH_UPDATE function. Columns 1+ are the playfield matrix scanned by the CPU.

ConstantValueswMatrix colFunctionDefault key
BYVP_SWSELFTEST-7[0] bit 0Self Test7
BYVP_SWCPUDIAG-6[0] bit 1CPU Diagnostic (triggers NMI)9
BYVP_SWSOUNDDIAG-5[0] bit 2Sound Diagnostic0
BYVP_SWVIDEODIAG-4[0] bit 3Video Diagnostic (triggers Video NMI)8
BYVP_SWJOYRIGHT-3[0] bit 4Joystick Right
BYVP_SWJOYLEFT-2[0] bit 5Joystick Left
BYVP_SWJOYDOWN-1[0] bit 6Joystick Down
BYVP_SWJOYUP0[0] bit 7Joystick Up
sw #1[1] bit 0Right Flipper switch feedbackRight Shift
IPT_START2bit 0x0100[1] bit 2Start Player 22
IPT_START1bit 0x0200[1] bit 5Start Player 11
IPT_COIN2bit 0x0400[2] bit 0Coin 26
IPT_COIN1bit 0x0800[2] bit 1Coin 15
Ball Tiltbit 0x1000[2] bit 6Ball TiltDel
Slam Tiltbit 0x2000[2] bit 7Slam TiltHome

17 How to Modify Assignments

What you want to changeWhere to change itRequires recompile?
Which physical I-PAC pin sends which key/button WinIPAC V2 utility (programs I-PAC flash memory) No
Which key activates which playfield switch in PinMAME Press Tab in PinMAME → "Input (this game)". Saved to cfg/babypac.cfg. No
Default keys for joystick / flipper / start / coin / tilt Edit BYVP_babypac_PORT in src/wpc/byvidpin.h Yes
Which solenoid fires on which allSol bit Edit PacDriveSetOutput() calls in update_hW() in src/wpc/core.c:1733 Yes
Add a new solenoid to an unused PAC-Drive channel Add a PacDriveSetOutput(106..112, ...) line to update_hW() Yes
Enable debug logging of lamp/solenoid USB commands Uncomment the printf lines at pacdrive.c:156 and pacdrive.c:188 Yes
Game DIP switch settings (lives, free play, tunnels, etc.) Press Tab in PinMAME → "DIP Switches". Or edit cfg/babypac.cfg via Tab menu. No
Launch flags (volume, display mode, etc.) Edit babypac.bat No
Rebuilding The project uses MinGW on Windows. The compiled binary is pinmame.exe in the project root. All modified source files are under MinGW/PinMAME/src/.

18 Build & Wiring Guide

This section describes how to assemble the complete hardware system from scratch. It is intended for others who want to replicate this build on a real Baby Pac-Man cabinet.

Parts Required

ItemNotes
Baby Pac-Man cabinet or playfieldOriginal Bally 1982. Needs flippers, drop targets, saucers, out hole, original A3 driver board for lamp SCRs.
Windows PCRuns PinMAME. Any modern PC works. Must have USB ports for both Ultimarc boards. Used for video output and audio.
I-PAC Ultimate I/OUltimarc. VID 0xD209, PID 0x0410. Handles all 96 lamp outputs and all switch inputs. No Windows driver needed — standard USB HID.
PAC-Drive "ID #1 Special"Ultimarc. VID 0xD209, PID 0x1500. The "ID #1 Special" variant is required — do NOT substitute the standard version. Outputs default OFF at power-up.
BabyPacDriver-v4 custom PCBEagle design files in Solenoid Driver board schematics/. Order from any PCB fab. Requires IRLZ34N MOSFETs (×12), SN74HCT04N hex inverters (×2), resistor networks (RN1–RN4), and 0.1 µF bypass caps (C1, C2).
DC solenoid supplyTypically 24–48V DC at adequate current (depends on coil ratings). A Meanwell or similar enclosed supply works well. Must share common ground with USB boards.
5V supply for driver board logicCan be a USB 5V tap, USB hub port, or any regulated 5V source. Must share common ground with all other supplies.
pacdrive32.dllClosed-source Windows DLL from Ultimarc. Place in same directory as pinmame.exe. Available from the Ultimarc website.
MinGW toolchainRequired to rebuild PinMAME from source. Run ..\bin\mingw32-make from the MinGW/PinMAME/src/ directory.

Common Ground — Critical Wiring Requirement

All grounds in the system must be tied to a single common ground bus. Without this, solenoids may not fire, signals will be unreliable, and you risk damaging the hardware.

Tie all grounds together The following must all share a single common ground point:
  • Meanwell power supply negative (−) terminal
  • PAC-Drive board GND screw terminal
  • I-PAC Ultimate I/O GND (shared via USB; ensure USB cable shield is grounded)
  • Custom driver board JP9 pins 1 & 2
  • 5V logic supply GND (if separate from USB)

Step-by-Step Wiring Sequence

1
Establish common ground bus
Run a GND wire from the Meanwell negative terminal to the common bus. Connect PAC-Drive GND terminal to the same bus. This ensures the PAC-Drive's open-drain outputs and the solenoid return path are on the same reference.
2
Power the driver board (JP9)
Connect JP9 pin 3 → 5V source. Connect JP9 pins 1 & 2 → common GND bus. Verify 5V at U1/U2 power pins before connecting any signal lines.
3
Connect PAC-Drive signal wires
Run wires from PAC-Drive terminals 1–9 to driver board JP1–JP3 (see table in PAC-Drive section). Each signal wire carries the open-drain output for one solenoid channel.
4
Wire solenoid coils to output terminals
Each solenoid coil has two wires. One wire goes to the Meanwell positive terminal (or a fused distribution bus from it). The other wire goes to the corresponding driver board output terminal (JP5–JP7, channels 1–9 as shown in the Solenoid Assignment Table). Flyback diodes on the board protect the MOSFETs from back-EMF.
5
Wire lamps to I-PAC Ultimate I/O
Connect the A3 Driver Board's SCR control inputs to the I-PAC's Strobe 1 / Strobe 2 output pins using the Full Lamp Table as the wiring reference. Each I-PAC output pin drives one SCR trigger input on the A3 board.
6
Wire all playfield switches to I-PAC inputs
Connect every switch (flipper buttons, drop targets, saucers, rollover lanes, tilt, etc.) to the I-PAC's 48 input pins. Configure key assignments in WinIPAC V2. Then assign game switch numbers in PinMAME's Tab menu.
7
Bench-test before first power-on with solenoids
Follow the 5-stage driver board bench test described in Custom Driver PCB → Bench Testing. Verify every MOSFET channel switches correctly before applying coil voltage.
8
Connect both USB boards, then launch
Plug in the I-PAC Ultimate I/O and the PAC-Drive before running babypac.bat. Both must be present at startup or neither board will receive commands.

Flipper Coil Requirements

Because the PAC-Drive is strictly on/off (no PWM), flipper coils must be the original-style dual-winding type with a mechanical End-of-Stroke (EOS) switch:

Do not use single-winding PWM coils Single-winding coils require PWM (pulse-width modulation) to reduce holding current after engagement. The PAC-Drive cannot do PWM. Running a single-winding coil continuously at full power will overheat and destroy it within seconds.

19 Troubleshooting

Lamps and solenoids are both completely dead

Most likely: one board was unplugged at startup Both the I-PAC Ultimate I/O and the PAC-Drive must be connected before launching PinMAME. If either is missing, PacInitialize() may return 0, LoadPacDrive() fails, and no USB commands are sent to either board for the entire session. Quit PinMAME, connect both boards, and relaunch.

Flippers don't fire

Lamps work but solenoids don't fire

Solenoids fire but lamps don't work

PacInitialize() returns 0

A specific solenoid fires on the wrong game event

The solenoid-to-bit mapping is defined in src/wpc/core.c:1733 inside update_hW(). Each PacDriveSetOutput(N, ...) call maps a specific bit of allSol to a PAC-Drive channel. See the Solenoid Assignment Table for the full mapping. Edit the relevant line in core.c and rebuild.

A lamp pin appears dead (always 0)

If a pin reads 0 no matter what the game does, check whether it is one of the five structurally dead pins: 16, 32, 48, 64, or 65. These are permanently 0 due to code-level filtering — they cannot be activated without modifying byVP_lampStrobe() and update_hW() in core.c. See the pin table in Full Lamp Reference for details on which real lamps those numbers were mistakenly associated with.

Enabling debug output

Two printf statements in pacdrive.c are commented out in normal builds to avoid console spam. Uncomment them to trace hardware commands in real time:

LocationOutput when enabled
pacdrive.c:158Prints every I-PAC LED group update: Setting LED HwID: 0 Group: N to value V
pacdrive.c:190Prints every solenoid state change: Setting Solenoid HwID: 1 to value V

Rebuild after uncommenting. Commands only print when the state actually changes, so a solenoid that fires and releases will generate exactly two lines.

20 K1 Flipper Enable Relay

What K1 is

The original Baby Pac-Man machine has a relay on the A3 driver board called K1 — Flipper Enable. In the Bally self-test numbering it is solenoid #08 (continuous solenoid). The relay coil is powered from a dedicated supply rail labeled "FLIPPER RELAY +43" (J3 pin 8 on power supply schematic W-1284), distinct from the general solenoid bus at J3 pin 12 ("SOLENOID BUS +43"). The relay's normally-open contacts are wired in series with the +43V line feeding both flipper coils. When the ROM de-energizes K1, +43V is cut to the flippers even if the flipper buttons are held — this is how the machine disables the flippers during the video-game half of the game and during attract mode.

How K1 maps to software

In byvidpin.c, PIA 1 port B carries the continuous solenoids (K1–K4) as bits 6–9 of coreGlobals.solenoids. K1 lands at allSol bit 7. In core.c, there is already commented-out code at line 1740 that previously attempted to route K1 to PAC-Drive output 100:

// PacDriveSetOutput(100, allSol >>  7 & 0x01); // Flipper enable relay

Output 100 was later assigned to Left Maze Saucer. The K1 line has since been moved to output 106 (PAC-Drive terminal 10) and is active in this build.

The problem in this build

Software side implemented (2026-07): core.c now drives PacDriveSetOutput(106, allSol >> 7 & 0x01), so PAC-Drive terminal 10 tracks the K1 enable signal every VBLANK. The physical relay is not yet wired — until it is, that signal has no effect and the flippers still fire on any button press regardless of game phase (they are driven directly by PacDriveSetOutput(97/98, ...) from button state, bypassing the ROM gate). Wire the relay per Option 1 below to make K1 physically cut flipper power during the video-game half and attract mode.

Why this is harmless for now During the video-game segment, the pinball is sitting in the out-hole. Pressing the flipper buttons causes the coils to fire, but there is no ball on the playfield to be affected. The machine still plays correctly. This is Option 3 — see below.

Options for controlling flipper enable

Option 1 — Hardware K1 relay (full original behavior)

Wire a real relay (or the original K1 relay if salvaged) to PAC-Drive terminal 10, with its normally-open contacts in series with the +43V supply to both flipper coils. Then enable the software output:

Step 1 — Drive the K1 output in src/wpc/core.calready done in this build (in update_hW(), right after the flipper outputs):

PacDriveSetOutput(106, allSol >>  7 & 0x01); // K1 Flipper Enable Relay

Step 2 — Hardware wiring:

With this wired, when K1 is energized (ROM playing pinball phase), +43V reaches the flippers. When K1 is de-energized (video game or attract), the relay opens and no amount of button pressing will fire the coils.

Note on relay selection: The relay coil must be compatible with the driver board's MOSFET output (12V or 24V coil recommended, depending on your supply). The original Bally K1 relay ran on +43V — if you use the original relay, feed the FLIPPER RELAY +43V rail to the coil side as well, not through the MOSFET output. In that case wire the MOSFET output to a low-voltage relay that in turn switches the +43V to the coil.

Option 2 — Software gate (no extra hardware)

Keep K1 unwired, but add a software check in core.c that suppresses flipper output when K1 is not active. Replace lines 1733–1734 in update_hW():

// Before (current code):
PacDriveSetOutput(97, allSol >> 46 & 0x01); // left flipper
PacDriveSetOutput(98, allSol >> 44 & 0x01); // right flipper

// After (with software gate):
if (allSol >> 7 & 0x01) {
    PacDriveSetOutput(97, allSol >> 46 & 0x01);
    PacDriveSetOutput(98, allSol >> 44 & 0x01);
} else {
    PacDriveSetOutput(97, 0);
    PacDriveSetOutput(98, 0);
}

This respects the ROM's K1 enable signal in software without requiring any additional hardware. The coils simply do not receive drive current when the ROM has K1 off. Rebuild and test after this change.

Option 3 — Live with it (current approach)

Do nothing. K1 remains unwired, the software output remains commented out, and the flippers fire freely whenever the buttons are pressed. This is the current state of the build as of 2026-05-29. Because the ball is always in the out-hole during the video-game segment, this has no practical effect on gameplay.

Current status: Option 3 is active K1 is not wired. PAC-Drive terminal 10 and driver board JP4 are unused. The K1 software line remains commented out at core.c:1740. To pursue Option 1 or 2, revisit this section and follow the steps above.

21 Flipper Power PWM Adapter (optional / experimental — not used in the final build)

Optional & experimental — superseded This microcontroller PWM adapter was built and works, but it is not part of the final machine. Flipper strength was ultimately dialed in by mechanically adjusting the EOS switches instead — simpler, and it keeps the Arduino out of the flipper signal path. The sketches in tools/flipper_pwm/ and this section are kept for reference. If you do build it, the flipper channels must be spliced through the Arduino; to revert, restore the direct HCT04→gate connection.

The PAC-Drive and the custom driver board are on/off only — there is no PWM, so flipper strength cannot be tuned in software. With the dual-winding EOS coils correctly wired the flippers are safe, but they can still feel too strong because the power winding fires at full 43 V during the stroke. This optional add-on inserts a small microcontroller between the driver board's logic output and the flipper MOSFET gates to PWM the gate drive, giving an adjustable fire strength via a single potentiometer — with no changes to PinMAME or the driver board's design.

How it works (and why it suits the EOS coil)

The AQ-25-500/34-4500 coil shares the MOSFET drain between both windings: the power winding runs through the EOS switch, the hold winding connects directly. The adapter exploits this:

So the pot tunes fire strength while the hold stays firm. A naïve "PWM everything" approach would weaken the hold winding and make it droop and buzz — this timed fire-then-hold avoids that.

Try the EOS wiring first Correctly wiring the EOS switch in series with the power winding (see Flipper Wiring) alone often makes the flippers feel normal. Add this PWM adapter only if they are still too strong after the EOS fix is confirmed in circuit.

Controller board

This build uses a USB-C "Nano" board built around the ATmega328P (chip marked ATMEL MEGA328P U-KR) — a classic 328P, programmed over an onboard USB-serial bridge (no native USB). The PF1/PE0 silkscreen pins are 32U4 port names left over on this clone layout; they are vestigial and unused here. The functional pins (D2, D3, D9, D10, A0, +5V, GND) are all standard 328P pins.

Select the correct board in the Arduino IDE Choose "Arduino Nano", and for the Processor select "ATmega328P (Old Bootloader)"confirmed required for this board (uploading with the plain "ATmega328P" target fails with stk500 "not in sync" errors, because the IDE tries 115200 baud while this clone's bootloader runs at 57600). Uploads go through the board's USB-serial chip (CH340/CP210x) — install that driver on the uploading computer if needed. (A genuine Pro Micro instead uses "Arduino Leonardo" — see the Pro Micro sketch.)

Parts required

ItemNotes
ATmega328P "Nano" board (or Pro Micro)5 V / 16 MHz. The photographed USB-C ATmega328P board (marked MEGA328P), or any Arduino Nano / Pro Micro / Leonardo.
10 kΩ linear potentiometerSets fire strength for both flippers. Panel-mount so it can be adjusted without opening the board.
2 × 10 kΩ resistorGate-to-GND pulldowns — mandatory. One on each flipper MOSFET gate.
2 × 100–220 Ω resistorSeries gate resistors. The driver board likely already has one per channel — reuse it if so.

Pin connections (this board's silkscreen labels)

Board pinConnect toPurpose
D2Left flipper HCT04 output (channel 97)Fire-command input, left
D3Right flipper HCT04 output (channel 98)Fire-command input, right
D9Left flipper IRLZ34N gatePWM gate drive, left (Timer1)
D10Right flipper IRLZ34N gatePWM gate drive, right (Timer1)
A0Potentiometer wiperFire-strength set
+5VDriver board 5 V (JP9 pin 3)Logic power
GNDCommon ground bus (JP9 pin 1/2)Shared reference — required

Potentiometer outer legs go to +5V and GND.

Where it splices in

BEFORE: PAC-Drive 97/98 → HCT04 in → HCT04 out ─────────────────────► IRLZ34N gate → coil AFTER: PAC-Drive 97/98 → HCT04 in → HCT04 out → board D2/D3 (enable in) board D9/D10 → IRLZ34N gate → coil (cut the original HCT04 → gate net for both flipper channels)

Wiring sequence

1
Identify the two flipper channels
On the driver board, find the two IRLZ34N MOSFETs whose outputs feed the left and right flipper coils (PAC-Drive outputs 97 and 98).
2
Cut the HCT04 → gate net
For each of those two channels, cut the trace (or lift the series gate-resistor leg) between the SN74HCT04 output and the MOSFET gate, separating them.
3
Route the command signals in
HCT04 output (left) → board D2; HCT04 output (right) → board D3. These are high-impedance logic inputs.
4
Route the PWM gate drives out
Board D9 → left MOSFET gate; board D10 → right MOSFET gate, through the 100–220 Ω series gate resistor. Add a 10 kΩ resistor from each gate to GND.
5
Wire the pot and power
Pot wiper → A0, outer legs → +5V and GND. Power the board from the driver board's regulated 5 V into +5V, and tie GND to the common ground bus.
6
Leave the EOS switch in place
Keep the EOS switch in series with the power winding: it wires between Lug 1 (+43V) and Lug 2 (power winding high end); Lug 3 is the common and is the single wire to the driver output. The PWM (if used) sits on top of this; it does not replace it.
10 kΩ gate-to-GND pulldowns are required — do not skip During board boot, reset, and USB sketch uploads, D9/D10 are high-impedance. Without a pulldown the gates float and a MOSFET can partially turn on, energizing a flipper coil on its own. The pulldowns hold the MOSFETs off whenever the board is not actively driving them. Also ensure the controller, HCT04 logic, and MOSFET sources all share one common ground.

Bring-up & test (coils safe first)

Sketches in the repo tools/flipper_pwm/flipper_pwm_nano/flipper_pwm_nano.ino (this ATmega328P "Nano" board) and tools/flipper_pwm/flipper_pwm_promicro.ino (Pro Micro). The code is identical — only the header notes and the Arduino IDE board target differ. The coil's built-in flyback diodes double as the PWM freewheel path, so no extra diodes are needed.

22 Incandescent Lamp Fade

The original Baby Pac-Man playfield lit with incandescent bulbs — they glow up and cool down instead of snapping on and off. LEDs switch instantly, which reads as harsh. This build simulates the bulb behavior in software, on top of the Ultimate I/O's 256-level PWM, and every part of it is tunable from babypac.bat without recompiling.

Interactive tuner Open lamp-fading.html (next to this page) to see the exact fade curves plotted, watch a lamp pulse through each one, and copy generated babypac.bat lines. It runs the same integer math as the build.

How it works

Each lamp keeps a brightness value 0–255. Every 60 Hz frame, UpdateOutputs() in pacdrive.c eases that value toward the ROM's target (255 = on, 0 = off), then the whole 96-output board is pushed with a single Pac64SetLEDIntensities() bulk transfer. Because that bulk command needs ~20 ms before the next one, the push runs every other frame (~30 Hz) — still smooth, and the update cost is fixed no matter how many lamps are fading at once.

Why a flashing lamp still pulses The fade-out is exponential, like a filament cooling, so a lamp the ROM flashes rapidly never fully reaches 0 — it settles into a visible pulse — while a lamp switched off for good decays all the way to dark. That single behavior is what a fixed on/off (or a plain linear fade) can't do.

The three curve shapes

Fade-in and fade-out each pick an easing curve independently:

Perceptual gamma

LED PWM is linear in duty cycle, but the eye is not. A gamma curve (default 2.2) is applied to the output so the fade reads as smooth and even and the dim glow lingers naturally. Toward 1.0 gives the raw, punchier linear look; higher than 2.2 is punchier still with a longer low tail. Gamma is set independently for rise and fall.

Configuration — babypac.bat

All values are read once at launch and clamped to sane ranges. Speeds are 1–255 (higher = faster). The plain BABYPAC_FADE_CURVE / BABYPAC_FADE_GAMMA set both directions; the _RISE / _FALL variants override.

Environment variableControlsDefault
BABYPAC_FADE_RISEFade-in speed (1–255, higher = faster)32
BABYPAC_FADE_FALLFade-out speed (1–255, higher = faster)24
BABYPAC_FADE_CURVE_RISEFade-in curve: exp · s · linexp
BABYPAC_FADE_CURVE_FALLFade-out curve: exp · s · linexp
BABYPAC_FADE_GAMMA_RISEFade-in perceptual gamma (1.0 = off)2.2
BABYPAC_FADE_GAMMA_FALLFade-out perceptual gamma (1.0 = off)2.2
BABYPAC_FADE_CURVEShorthand — sets both rise & fall curve
BABYPAC_FADE_GAMMAShorthand — sets both rise & fall gamma
BABYPAC_LED_FADEBoard hardware fade (legacy "Approach A")0 (off)

A "real bulb" starting point — a quick exponential warm-up in, a slow constant fade-out with a long perceptual tail:

rem babypac.bat
call setvol 80
set BABYPAC_FADE_CURVE_RISE=exp
set BABYPAC_FADE_CURVE_FALL=lin
set BABYPAC_FADE_GAMMA_RISE=1.6
set BABYPAC_FADE_GAMMA_FALL=2.6
set BABYPAC_FADE_RISE=40
set BABYPAC_FADE_FALL=18
pinmame babypac -ultimateio -joystick -dmd_only -scanlines -skip_gameinfo

On startup the console prints the active values — Lamp fade: RISE speed=… curve=… gamma=… | FALL speed=… curve=… gamma=… — so you can confirm what took effect.

One caveat with mismatched gamma If a lamp is pulsing fast and its rise and fall gamma differ a lot, you may see a tiny brightness step at the turnaround, where the two curves meet mid-level. If that ever reads as a flicker, set the two gammas equal. Speed and curve don't have this interaction.

23 Input HW → Switch Function Map

Running with -ultimateio, this build does not use the normal named switch inputs. Instead core.c reads 64 generic inputs — "Input HW1" … "Input HW64" — directly into the switch matrix. You rebind those "Input HW" entries in Input (general) — they override the named "Slam Tilt", "Coin", etc. entries in Input (this game), which is why editing those has no effect. This table says what each "Input HW" controls.

The mapping rule Input HW NswMatrix column (N−1)÷8, bit (N−1) mod 8. Column 0 (HW1–8) is the cabinet column; playfield strobes ST0–ST5 are columns 1–6 → HW9–16, 17–24, 25–32, 33–40, 41–48, 49–56. HW57–64 are unused. Equivalently, for a playfield switch at strobe STc, return row r: Input HW = 9 + c×8 + r.
These inputs are now labeled As of this build, the 28 mapped inputs are renamed in src/inptport.c with real Baby Pac-Man names — e.g. BabyPac Slam, BabyPac #1 Drop Tgt (Lt), BabyPac Rt Spinner — so they read clearly in Input (general) instead of "Input HW24" etc. (HW1–4, the on-board diagnostic switches, don't respond to a mapped key in this build and are left generic.)

Relabeled inputs (from PinMAME Tab Menu re-mappings.csv)

The 28 mapped inputs, and the exact label each one now carries in the Input (general) menu in this build (the "Menu label" column is what was applied to src/inptport.c):

Input HWSwitch / functionMenu label (this build)
Input HW5Joystick RightBabyPac Joy Right
Input HW6Joystick LeftBabyPac Joy Left
Input HW7Joystick DownBabyPac Joy Down
Input HW8Joystick UpBabyPac Joy Up
Input HW9Right flipper EOSBabyPac Rt flip EOS
Input HW11Start Game for 2BabyPac Start 2
Input HW13Rebounds (2)BabyPac Rebounds (2)
Input HW14Start for Game 1BabyPac Start 1
Input HW15Right SpinnerBabyPac Rt Spinner
Input HW16Left SpinnerBabyPac Lt Spinner
Input HW17Coin 2 (right)BabyPac Coin 2
Input HW18Coin 1 (Left)BabyPac Coin 1
Input HW23Tilt (2)BabyPac Tilt (2)
Input HW24SlamBabyPac Slam
Input HW25Top Loop Lane (Right)BabyPac Top Loop (Rt)
Input HW28Top Loop Lane (Left)BabyPac Top Loop (Lt)
Input HW29TUNNEL OutlaneBabyPac TUNNEL Outlane
Input HW30FRUITS OutlaneBabyPac FRUITS Outlane
Input HW31Right Inside OutlaneBabyPac Rt Inside Outlane
Input HW32Left Inside OutlaneBabyPac Lt Inside Outlane
Input HW33#5 Drop Target (Right)BabyPac #5 Drop Tgt (Rt)
Input HW34#4 Drop TargetBabyPac #4 Drop Target
Input HW35#3 Drop Target (Center)BabyPac #3 Drop Tgt (Ctr)
Input HW36#2 Drop TargetBabyPac #2 Drop Target
Input HW37#1 Drop Target (Left)BabyPac #1 Drop Tgt (Lt)
Input HW38Outhole SaucerBabyPac Outhole Saucer
Input HW39Right Maze SaucerBabyPac Rt Saucer
Input HW40Left Maze SaucerBabyPac Lt Saucer

Cabinet & control switches (exact)

These are defined in src/wpc/byvidpin.h / byvidpin.c, so the mapping is definitive. The "source default key" is the compiled-in default; in this build the actual key comes from the I-PAC programming and cfg/babypac.cfg.

Input HWswMatrixFunctionSource default key
Input HW1[0] bit 0Self Test7
Input HW2[0] bit 1CPU Diagnostic (NMI)9
Input HW3[0] bit 2Sound Diagnostic0
Input HW4[0] bit 3Video Diagnostic (Video NMI)8
Input HW5[0] bit 4Joystick Right
Input HW6[0] bit 5Joystick Left
Input HW7[0] bit 6Joystick Down
Input HW8[0] bit 7Joystick Up
Input HW9[1] bit 0Right Flipper switch (EOS feedback)Right Shift
Input HW11[1] bit 2Start Player 22
Input HW14[1] bit 5Start Player 11
Input HW17[2] bit 0Coin 26
Input HW18[2] bit 1Coin 15
Input HW23[2] bit 6Ball TiltDel
Input HW24[2] bit 7Slam TiltHome

Playfield switches

The playfield switches fill the strobe columns (roughly Input HW9–HW56), per the Baby Pac-Man switch matrix in the operator's manual (drawing W-1280). The complete set of named switches:

Finding a specific switch's HW number Fastest on the machine: open Input (this game), actuate the physical switch, and watch which "Input HWxx" the menu highlights — that's the entry to rebind. Or read the switch's strobe/return off drawing W-1280 and apply Input HW = 9 + strobe×8 + row (returns A4J2-8…A4J2-15 are rows 0…7). To change the physical switch's key, remember there are two layers: WinIPAC (switch → key) and this map (key → matrix).

Last updated: 2026-07-20