Cracking Merit Trivia Whiz (1985-87)
How we broke every Merit 6221 trivia game, Trivia Whiz Eds. 1-5 and Tic Tac Trivia, by decoding a 189-entry bigram dictionary, the address-scramble permutation, and a second bitswap LUT hidden in the Z80 program ROM. 16,000 questions recovered.
April 19, 2026 · crack, merit, z80, trvwhiz, tictac
Trivia Whiz (and Tic Tac Trivia)
Merit Industries · 1985-1987 · Z80 6221 board · 5 Trivia Whiz editions + 2 Tic Tac variants · 15,989 records
The Games
If you walked into a bar in 1985, there was a decent chance of finding a Merit Trivia Whiz cabinet tucked next to the jukebox. A small countertop unit, 13-inch CRT, four colored buttons (A, B, C, D), a coin slot. You’d drop a quarter, get five questions, and try to beat the high score posted in attract mode. Categories rotated every round: Entertainment, General, Sports, and (on the uncensored version) Sex Trivia.
Merit Industries was a coin-op manufacturer out of Bensalem, Pennsylvania. They’re still around today, making the video poker machines you see in dive bars and truck stops. Their 1980s-era trivia line ran on a Z80 board numbered 6221, and across 1985-87 they shipped a whole family of games off that same hardware:
- Trivia Whiz Edition 1 (6221-00, 1985), the debut set
- Trivia Whiz Edition 2 (6221-05, 1985), new questions, same hardware
- Trivia Whiz Edition 3 (6221-05 U5-0D, 1985), the “reference” revision
- Trivia Whiz Edition 4 (6221-10, 1985), added a “Strange But True” True/False category
- Deluxe Trivia Whiz Edition 5 (6221-70, 1987), final revision, “deluxe” cabinet
- Tic Tac Trivia (6221-23, 1985-86), same hardware, tic-tac-toe gameplay grid where each cell is a trivia question
Same Z80 at 2.5 MHz. Same Intel 8255 PPI for I/O. Same Motorola 6845 CRT controller in character mode (no sprites, no tile scrolling, just text and a primitive score banner). The question data lived in a separate ROM region addressed through three memory-mapped I/O ports. That’s where Merit’s copy protection lived.
The Data
15,989 records across the seven Merit 6221 games:
Source / docs:
- Core decoder:
scripts/merit/decrypt.py - Trivia Whiz CLI:
scripts/merit/trvwhiz.py - Tic Tac CLI:
scripts/merit/tictac.py - MAME driver:
mame/src/mame/merit/merit.cpp
How We Cracked It
The Dictionary
The question ROMs are plaintext ASCII with interleaved tokens. Most
bytes in the range 0x20-0x7E are literal characters. Bytes 0x01-0x1F
and 0x60-0xFE are tokens that expand into 2-3 character strings via a
dictionary stored in the Z80 program ROM.
The dictionary is stored as <token_byte><2-3 chars><0x0D> entries. We
found it by the ` anchor: token 0x60 always expands to "TER ",
which appears in every Merit dictionary. Searching any of the program
ROMs (u5+u6) for TER\r (54 45 52 20 0D) lands on the byte after the
0x60 token, and the table streams from there.
189 entries. The same dictionary across every 6221 trivia game, Ed 1 through Ed 5 and Tic Tac all share byte-for-byte identical token tables. Merit reused the compression across their entire line. Once we had the dictionary, expansion was trivial:
for b in data:
if b in tokens: out.append(tokens[b]) # expand
elif 0x20 <= b <= 0x7E: out.append(chr(b)) # literal
else: out.append("\x00") # delimiter
The Record Format (Edition 3)
Records for Edition 3 read cleanly straight off the ROM after token expansion. Each one follows:
<2-byte prefix><question text>?_<correct>^<wrong_1>_<wrong_2>_<wrong_3>
_(0x5F), field separator^(0x5E), separates the correct answer from the first wrong answer?(0x3F), question terminator- The 2-byte prefix is
[letter/digit][punct], record metadata we don’t fully understand, probably category ID + difficulty tier
Sometimes a record is followed by a NOTE field, a free-form
explanation of the answer, which runs all the way to the next record’s
2-byte prefix without a separator. The parser finds the next field
containing ? to re-sync.
Edition 3 yielded 433 clean records from this simple pass.
The Hardware Scramble (Editions 1, 2, 4, 5, Tic Tac)
Every other Merit game uses the same underlying format, same dictionary, same record layout, but ships the ROM bytes in a scrambled order so that raw reads look like gibberish. The protection works like this:
The Z80 code needs to read a question byte at some logical position N.
To fetch it, the Z80 writes N’s three bytes (low/mid/high) to three
I/O ports at 0xD6xx, 0xDA00, 0xCE00. Before each write, the Z80
code runs the low nibble through a bitswap lookup table at a known
offset in the program ROM (the 16-byte table 00 08 04 0C 02 0A 06 0E 01 09 05 0D 03 0B 07 0F, reverse the low 4 bits). The Z80 writes the
pre-scrambled value; the hardware then does:
offset = (offset & 0xF0) | ((offset - key) & 0x0F) // key-subtract
offset = bitswap<8>(offset, 7,6,5,4, 0,1,2,3) // reverse low 4 bits
…and returns the byte at the resulting ROM position. The bitswap cancels the Z80’s pre-scramble. What’s left is the key-subtract.
For Edition 3 the key is 0 (subtract 0 is a no-op), so raw reads give clean text. For every other game the key is non-zero:
| Game | MAME init | Key |
|---|---|---|
| Trivia Whiz Ed 1 (trvwzh) | empty_init | 0 (burn-only bitswap) |
| Trivia Whiz Ed 2 (trvwz2) | init_key<2> | 2 |
| Trivia Whiz Ed 3 (trvwz3h) | empty_init | 0 |
| Trivia Whiz Ed 4 (trvwz4) | init_key<5> | 5 |
| Deluxe Trivia 5 (dtrvwz5) | init_dtrvwz5 | 6 |
| Tic Tac Trivia (tictac) | init_key<8> | 8 |
| Tic Tac Trivia (tictaca) | init_key<4> | 4 |
For key K ≠ 0, the residual transform on each byte’s address is:
raw_rom_low_nibble = bitswap((bitswap(z80_addr_low) - K) & 0x0F)
Undoing this is a simple 16-entry permutation table per key. Applied
per 16-byte window, the scrambled ROM bytes re-arrange into the same
readable <prefix><question>?_<A>^<W>_<W>_<W> layout as Edition 3.
Edition 1 is a special case: its ROM was burned in the
Z80-pre-scrambled order (low-nibble bitswap only, no key), so raw
reads look scrambled but a plain low-nibble bitswap undoes it. Ed 1
also uses a different record layout, no ? terminator, ^ as
an inline correct-answer marker between two answer fields:
<2-byte prefix><Q>_<A>_<A>^<A>_<A>
Where the ^ tells you which of the four answers is correct (0, 1, 2,
or 3).
The Yields
Trivia Whiz Ed 1 (trvwzh, key=0 burn) 2,999 records
Trivia Whiz Ed 2 (trvwz2, key=2) 3,558 records
Trivia Whiz Ed 3 (trvwz3h, clean) 433 records
Trivia Whiz Ed 4 (trvwz4, key=5) 1,617 records
Deluxe Trivia 5 (dtrvwz5, key=6) 2,198 records
Tic Tac Trivia (tictac, key=8) 3,112 records
Tic Tac Trivia (tictaca, key=4) 2,072 records
────────────────────────────────────────────────────
Total 15,989 records
Big jumps on Ed 4, Dlx 5, and both TicTac variants come from cracking
the per-game low-offset LUT (LUT2), a second bitswap table parked
just after the standard one in each program ROM. The Z80 code uses
LUT2 to pre-scramble only the low-address port writes; the earlier
extractor applied the standard bitswap everywhere, so bytes at the
positions LUT2 swaps for the standard table decoded as garbage.
What’s Still Imperfect
Residual noise across the Merit family now sits at 1-4% after the
LUT2 fix, down from 14-17% on the worst sets. What’s left looks like
Merit’s own data-entry typos: c0lony instead of colony, M0rdred
for Mordred, stray punctuation on decade possessives. The formatter
cleans up the obvious ones (see
hiding in that handwriting).
The sc-* BPROM on the question board, MAME notes it as “used as
KEY to decode questions”, was a red herring. Dumping all four
sc-* PROMs showed they implement exactly the same function as the
init_key<N> macro in MAME’s source; the real extra layer was the
LUT2 table sitting in the Z80 program ROM, not the BPROM.
Credits
- The dictionary anchor (
`TER+0x0D) and record format were worked out by inspecting a dumped Ed 3 ROM. - The Z80 pre-scramble + hardware transform came straight out of MAME’s
merit.cppdriver comments combined with disassembling the Z80 helper routine at program ROM offset~0x0929. - The
dec003.u13PAL16L8 on the Ed 4 question board was already brute-forced into MAME’s driver by TwistedTom (per commit history) years before we got here, we just needed to know which ROM set on mdk.cab had the complete file set.
References
Related
Cross-archive analyses
Trivia Whiz1 (1985-86, Merit Industries (Bensalem, PA)) · Arcade-Museum · Flyer · MAME romset:
trvwz / trvwz2 / trvwz3h / trvwz4↩︎