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


← Archive

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:

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:


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>

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:

GameMAME initKey
Trivia Whiz Ed 1 (trvwzh)empty_init0 (burn-only bitswap)
Trivia Whiz Ed 2 (trvwz2)init_key<2>2
Trivia Whiz Ed 3 (trvwz3h)empty_init0
Trivia Whiz Ed 4 (trvwz4)init_key<5>5
Deluxe Trivia 5 (dtrvwz5)init_dtrvwz56
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


References


Cross-archive analyses


  1. Trivia Whiz1 (1985-86, Merit Industries (Bensalem, PA)) · Arcade-Museum · Flyer · MAME romset: trvwz / trvwz2 / trvwz3h / trvwz4 ↩︎