Identity and evidence precedence
This is the Bally WPC-Security physical product released 1995, IPDB 2358. The retained operations manual (part number 16-50039-101, "FINAL", March 1995, Midway Manufacturing Company) confirms Bally/1995 identity on its own title page independent of any VPX table metadata. It covers the twelve-driver tom_* clone tree: tom_13 (parent, production 1.3X), tom_06/tom_061 (0.6A/0.61A prototypes), tom_10f/tom_101f (1.0/1.01 French), tom_12/tom_12a/tom_121 (1.2-series revisions), tom_13c (2019 competition mod), tom_13f (2005 French rebuild), and tom_14h/tom_14hb (2005 "1.4 Home version" and its coin-play variant). Every one of these is a game-ROM revision for the same physical machine, registered through PinMAME's identical CORE_CLONEDEF(tom, <ver>, 13, ...) macro sharing one init_tom/tomGameData struct — the driver registration itself structurally proves there is no controller-address difference between any two tom_* drivers.
Evidence precedence for this definition: the retained known-working script is runtime and mechanism-causality ground truth; the Bally operations manual controls physical construction, part numbers, wiring, polarity, quantities, and device presence; pinned PinMAME controls controller generation, public address topology, and display metadata; the retained VPX geometry supplies normalized coordinates. The retained manual PDF carries an OCR text layer (Adobe Acrobat "Paper Capture"), but every printed table used here was independently verified against a 300 dpi render of the same page and transcribed into external:pinmame-review-artifacts/theatre-of-magic-1995/manual-transcription.md; the OCR text was never treated as authoritative on its own — multi-column tables in this manual garble under plain extraction exactly as warned.
ROM binding hazard: production firmware vs. the retained script's bound driver
The retained table's own script reads Const cGameName = "tom_14hb" '1.3x arcade rom - with credits. tom_14hb is genuinely cataloged by pinned PinMAME — it is not a fabricated identity — but it is not the machine's production firmware. Pinned src/wpc/driver.c dates every clone: tom_06/tom_061 "03/95 B" (prototype), tom_10f/tom_101f/tom_12/tom_12a/tom_121 "04/95 B", tom_13/tom_13f "08/95 B" (the newest 1995-dated revision, and the parent of the clone tree with no clone_of), and tom_14h/tom_14hb "10/96 B" carrying the driver.c comment "Theatre of Magic (1.4 Home version)" / "(1.4 Home version Coin Play)" — i.e. a post-arcade-run software revision for home conversions, not the shipped arcade firmware. This definition therefore binds tom_13 as the primary/production firmware, consistent with it being both the newest 1995-dated arcade revision and the clone-tree parent PinMAME itself treats as canonical.
Whether the retained script's choice of tom_14hb introduces any switch, coil, or lamp semantic difference from tom_13 was checked structurally rather than assumed: tom.c declares exactly one tomGameData struct and one init_tom function, and every CORE_CLONEDEF(tom, <ver>, 13, ...) entry (including 14h/14hb) shares them via the identical name/name machine-driver pair PinMAME's GAMEX macro expands to. There is no per-clone override anywhere in the driver. This proves the controller-address map, inverted-switch mask, flipper declaration, and mechanism hooks are byte-identical across every tom_* ROM; the only differences between revisions are ROM-internal game rules (ruleset changes, ghost-image fixes, localization, "Tiger Saw"/"Magic Post" feature toggles — see below). No conflicts entry was required for electrical semantics as a result; tom_14hb is recorded purely as a driver variant with physical_compatibility: "identical".
GEN_WPCSECURITY (PINMAME_HARDWARE_GEN_WPCSECURITY = 0x20, "Security chip, World Cup Soccer 3/94 - Jackbot 10/95") with wpc_dispDMD. No existing controller profile matched this generation (wpc-95, wpc-dcs, and wpc-fliptronic are all distinct hardware-generation bits), so this curation introduced controllers/pinmame/wpc-security.json, derived entirely from pinned source rather than by analogy. Electrically it is identical to WPC-DCS/WPC-Fliptronic (no integrated LPDC board, so 37-44 are simply unused; Fliptronic upper-flipper circuits 33-36 are freely repurposable when a driver declares no FLIP_SOL(FLIP_UR)/FLIP_SOL(FLIP_UL) bit) — GENWPC_HASWPC95 = GEN_WPC95 | GEN_WPC95DCS excludes GEN_WPCSECURITY. The only generation-specific difference is GENWPC_HASPIC = GEN_WPCSECURITY | GEN_WPC95 | GEN_WPC95DCS, which routes the internal switch-column-select register through an emulated security-PIC handshake (wpc_pic_r/wpc_pic_w) instead of the plain column reader WPC-DCS/Fliptronic use — this changes how the emulator scans the matrix internally but not the public address space or its normalization. Any future WPC-Security game should reuse this profile unchanged.
- Switches: dedicated coin-door D1-D8 (public 1-8), matrix 11-88 as drive column then return row, Fliptronic 111-118.
- Solenoids: physical drivers 1-18 and 21 (standard coils, motor, and flasher-bank positions); 20 and 24-28 (flashers/gen-purpose); Fliptronic upper-flipper circuits 33-35 repurposed for the trunk's Cube Magnet, Sub Ball Release, and Left Drain Magnet with no address translation (
tomGameData.hw.flippers = FLIP_SW(FLIP_L) | FLIP_SOL(FLIP_L), no FLIP_UR/FLIP_UL bit); Fliptronic lower-flipper circuits 45-48; PinMAME state channels 29-32; 37-44 wholly unused (no LPDC on this generation).
- Lamps: 8x8 matrix 11-88, all addresses populated except 82/83/84 (Not Used).
- GI: five strings on public addresses 0-4.
Switch-matrix opto sweep: zero disagreement
Every cell shaded "OPTO, TYPICALLY CLOSED" on the printed switch matrix (page 2-42) — column 3 rows 1-6 (31-36, the trough/subway optos) and column 5 rows 5-8 (55-58, the trunk's Cube Position optos) — is covered exactly by pinned PinMAME's tomGameData inverted-switch mask {0x00,0x00,0x00,0x3f,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x00} (column-index 3 = 0x3f = bits 0-5; column-index 5 = 0xF0 = bits 4-7). No shaded cell is left unnormalized and no unshaded cell is normalized: column 3 rows 7-8 (37 Spinner, 38 Right Lower Target) and column 4 (41-48, Lock/Popper/Eddy row) carry no shading and no mask bits. This is the same clean result Bally The Addams Family and Bally Kiss already produced on this project, and is the opposite outcome from Williams Monster Bash's Dracula-position conflict.
Three switches (45 Left Drain Eddy, 48 Right Drain Eddy, 85 Trunk Hit) are eddy-current proximity sensors, not optos: manual page 1-45 ("Eddy Sensor Calibration") states "Theatre of Magic uses Eddy Current Sensors to detect the pinball without the use of a mechanical switch. One eddy device can be found in the front of the trunk [85]. The other two are located in the outlane areas [45, 48]." These carry switch_type: "other" with the construction documented in physical.notes, matching the WPC-DCS-generation Star Trek: TNG proximity-sensor precedent (switch_type has no proximity enum value).
Fliptronic block: a manual self-contradiction resolved by the parts list
The switch-matrix wiring page (2-42) labels F5-F8 "Upper Right/Left Flipper EOS/Opto" with real-sounding descriptions, but the switch-locations parts list (2-43) prints F5, F6, F7, and F8 as Not Used with no part number — the same block as matrix positions 11, 12, 16, 17, 18. Per project policy ("prefer the parts list over the matrix page for labels"), and matching tomGameData.hw.flippers = FLIP_SW(FLIP_L) | FLIP_SOL(FLIP_L) (no upper-flipper bit at all), F5-F8 are recorded unused; the matrix page's text is disclosed in physical.notes as a stale generic Fliptronic-II-board template rather than modeled as fitted hardware.
The headline mechanism: the motorized rotating trunk
A single DC gearmotor (A-19782 Magic Trunk Motor Assembly, illustrated in the manual's page 1-50 teardown diagram: Molded Trunk, Motor Bracket, Stop Collar, Opto Board, Opto Interrupter) rotates the trunk box through solenoids 17 (solTrunkMotorCW) and 18 (solTrunkMotorCCW). The retained script's TrunkTimer_Timer tracks one continuous TrunkAngle counter (TrunkSpeed = 0.12 degrees/ms, clamped 0-270) and asserts each Cube Position opto only while the trunk sits within +/-15 degrees of that face's rotational home: switch 56 (Cube Position 1) near 0 degrees, switch 55 (Cube Position 4) near 90, switch 58 (Cube Position 3) near 180, switch 57 (Cube Position 2) near 270 — all four assert together while the trunk is in transit between any two detents, and each individually de-asserts only at its own parked position. Because there is no fixed playfield sensor object for any of the four (they live on the trunk's own internal "Opto Board"), all four are documented projections onto the rotating trunk's own table-object center (Primitive Trunk/Trunk1/Trunk2), the same allowance Williams Monster Bash's Dracula-position optos and Williams Star Trek: TNG's idol-wheel optos already established.
Solenoid 33 (Cube Magnet, a repurposed Fliptronic Upper-Right-Power circuit) holds a ball inside the rotating box; while held, the retained script repositions the ball every tick to follow the trunk's own rotation angle — the table's "vanishing ball" magic effect. The eddy-current Trunk Hit target (switch 85, positioned in front of the trunk) is only droppable while the trunk is parked at the 270-degree face (sw85.IsDropped = NOT Controller.Switch(57)), gating a ball-routing choice by the mechanism's rotational state.
The spinning subway and ball lock
A ball shot into the rear trunk entrance travels the subway (opto 36, then microswitch 47) and is released by solenoid 34 (Sub Ball Release, a repurposed Fliptronic Upper-Right-Hold circuit) onto Lock 1 (switch 41), progressing through Lock 2/3 (42/43) toward the Trap Door assembly (solenoids 3/14, switches shared with the trap door open/hold states). Solenoid 4 (Subway Popper) kicks the ball resting on the Popper switch (44) back onto the playfield. The 37 Spinner switch sits along this same inner path (matrix column 3 row 7, not shaded — it is a plain leaf spinner, not an opto, despite sitting in the same printed column as the trough/subway optos).
The captive-ball spinning disc: production vs. prototype
Two distinct captive-ball features exist in the retained evidence, and they must not be conflated. The production assembly is static: a ball rests captive behind the Left Bank Target (2) standups (switch 51) and can be struck to rock against Captive Ball Rest (52) and Captive Ball Top (77); it has no drive motor and its part numbers (A-18059-15, standard leaf switches) show no motorized construction. Separately, the retained script implements a motorized "Tiger Saw" spinning-disc prop (a rotating Saw/SawGold/SawGoldTeeth primitive, driven by solenoid 19 through SolTigerSaw/CPC_Timer) gated behind a table-author toggle TigerSaw, which defaults on (TigerSaw = 1) in this retained table. The script's own comment on the SolTigerSaw assignment reads '****** VPM controlled (only in prototypes). This is the script author's own admission that the motorized disc is prototype-only hardware — consistent with the printed Solenoid/Flasher Table marking address 19 NOT USED (with the driver transistor and connections still populated, i.e. a wired-but-unpopulated position, not a missing circuit) and with pinned PinMAME's own tom_06/tom_061 "prototype" clone descriptions. This definition therefore records solenoid 19 unused for the production 1.3X machine it binds, fully disclosing the prototype toggle and its default state in physical.notes.
The optional "Magic Post" (Center Post)
Solenoids 23 and 36 present a similar, independently-resolved pattern. The Solenoid/Flasher Table prints both NOT USED, but the Solenoid/Flashlamp Locations page assigns solenoid 23 a real bulb and assembly ("Save Post Flasher", #89/A-17803) — a genuine internal manual contradiction. The retained script resolves it directly: SolCallback(23) and SolCallback(36) ("Magic Post Flasher (***)" / "Magic Post Up/Down (***)") are implemented only when a table-author toggle CenterPost is enabled, and CenterPost = 0 (off) by default in this retained table — agreeing with the Solenoid/Flasher Table's NOT USED rather than the Locations page's "Save Post Flasher". Both solenoids are recorded unused on the production machine, with the disagreement disclosed in physical.notes; this is a documented field/factory-optional post feature, not a data error.
Lamps, flashers, and general illumination
All lamp-matrix addresses are populated except 82/83/84 (Not Used). Five addresses drive two bulbs each — 26 "Jacket Award", 45 "Safe Award", 54 "Lock Ball", 63 "Grand Finale", and 81 "Special" — and the retained table binds both bulbs of each pair at genuinely distinct playfield positions (several are left/right mirrored pairs, e.g. 81/81a sit at nearly identical y and mirrored x, consistent with a symmetric outlane-special insert printed on both sides). Lamp 85 ("Lamp In Cube") rides inside the rotating trunk; the retained script repositions its Light object every tick to follow Trunk.RotZ, so it is a documented projection onto the trunk's own table-object center rather than a fixed coordinate. Lamps 87/88 are the illuminated Buy-In/Start cabinet pushbuttons.
General illumination is where this machine's clearest unresolved conflict lives. The manual's own Solenoid/Flasher Table wires GI strings 1 and 2 (public PinMAME addresses 0 and 1) exclusively through Backbox-column connectors (J120) with #555 bulbs, while strings 3-5 (addresses 2-4) are wired exclusively through Playfield-column connectors (J121) with #44 bulbs — a clean, internally consistent split on one printed table. The retained script's UpdateGI(no, step) nonetheless implements cases 0 ("top") and 1 ("bottom left") by driving genuine large playfield Light collections (GITop+GIBumpers, GILeft) alongside a Table1.ColorGradeImage LUT swap — the routine reads as a stylized mood-lighting/color-grade effect keyed off which GI relay is active, not a literal per-bulb wiring replica; real backbox/insert-panel illumination would not plausibly drive dozens of scattered playfield bulbs. This is the same class of disagreement Williams Tales of the Arabian Nights already established for its own GI address 2 (a script visually contradicting its manual's backbox wiring). Per that precedent, the manual wins for spatial classification: GI addresses 0 and 1 are recorded not_applicable/cabinet_or_service despite the script's visual behavior, and the disagreement is a first-class conflict.gi-strings-1-2-backbox-vs-script-playfield-binding. GI addresses 2 and 3 (strings 3 and 4) are genuinely playfield per both sources and use the retained table's GIRight/GIMiddle emitter collections. GI address 4 (string 5) is playfield per the manual, but UpdateGI implements no case 4 at all, so no VPX object binds a coordinate to it; its definition entry carries no spatial key rather than a fabricated placement, the same honest-omission pattern Williams Star Trek: TNG established for three unresolvable lamps.
Other standard devices
Two slingshots (coils 9/10, score switches 61/62) and three SW-11A-37 jet bumpers (coils 11/12/13, switches 63/64/65 — Bottom/Middle/Top Jet) are standard WPC devices; the retained script's LeftSlingshot_Slingshot/RightSlingshot_Slingshot and Bumper3_Hit/Bumper2_Hit/Bumper1_Hit handlers pulse the matching matrix addresses and fire the matching coils in the same event. Solenoid 6 (Center Loop Post) and solenoid 8 (Top Diverter Post) route a ball between the inner and outer loops; solenoids 15/16 (Left/Right Up/Down Gate) admit a ball into a loop while blocking return travel. Two eddy-current outlane sensors (45/48) pair with the Right/Left Drain Magnets (solenoids 5/35, the latter a third repurposed Fliptronic circuit — Upper-Left-Power) for an automatic outlane save ("Hocus Pocus" per the manual's eddy-calibration diagram). The manual documents a player-pulled spring plunger (no auto-plunger solenoid is printed; the retained table carries a single Plunger object), unlike several later WPC-95 games in this project that use an auto-plunger kicker.
Author construction checklist
- Build the four-ball trough with the drain at Trough 4, the spring-plunger shooter lane, both slingshots, three jet bumpers, the motorized rotating trunk with its Cube Magnet and four position optos, the subway/lock chain feeding the trap door, the Vanish Lock captive-ball kickout, the static Left Bank Target captive ball, and the two eddy-current outlane magnets.
- Preserve opto polarity for 31-36 and 55-58; PinMAME already normalizes every printed opto on this machine (zero disagreement), so do not invert any of them again.
- Bind switches 45/48/85 as eddy-current proximity sensors, not optos or leaf switches.
- Treat Fliptronic circuits 33-35 (Cube Magnet, Sub Ball Release, Left Drain Magnet) as repurposed non-flipper coils with no address translation; do not model them as flipper hardware.
- Leave solenoid 19 (prototype Tiger Saw motor) and solenoids 23/36 (optional Magic Post) unfitted on the production machine, but disclose their retained-script toggles verbatim.
- Bind GI addresses 0/1 as backbox devices per the manual despite the script's playfield-collection behavior; bind GI 2/3 as playfield; leave GI 4 unplaced.
- Bind every dedicated switch 1-8, every matrix position 11-88 including the printed Not Used positions, Fliptronic 111-118 with 115-118 explicitly not installed, the eight CPU DIP bits, solenoids 1-50, lamps 11-88, GI 0-4, and the 128x32 DMD.
Sources
manual.bally.theatre-of-magic.1995: Bally Theatre of Magic operations manual, part number 16-50039-101, SHA-256 ef9348415031e5d851b3c46454a0df0178f7a2066223d54a42db0d6708e56d92.
manual-support.bally.theatre-of-magic.1995: retained human transcription, SHA-256 c7a4dd783f33e63413bc33b9dcdf99c7381f80e82eb84017cf0b2a1354d6b9fb.
vpx-script.tom-2-4: retained known-working embedded script, SHA-256 596c926f27c1782819a0184566f083a161be362fec7a3bbc634a9138d97b47c3, binding tom_14hb (a non-production driver variant; the primary firmware binding is tom_13).
vpx-table.tom-2-4: retained table "Theatre of Magic (Bally 1995) 2.4.vpx", SHA-256 5f8bb3e0493c408484e475516e2f2c3d84b3487dcfb63eb231bca2c40b531253, bounds left=0 top=0 right=952 bottom=2594.1 (unusually tall; normalized with x/952, y/2594.1, not the y/2162 divisor used by every standard-height WPC game curated so far).
pinmame.core.4ec52ff0ac133: src/wpc/sims/wpc/full/tom.c and the WPC-Security core/solenoid/flipper handling at the pinned revision.