Eli Fulkerson .com Home → AI Projects → Zappermario
 

Zapper Mario

A homebrew ROM hack of the NES multicart Super Mario Bros. + Duck Hunt (USA) (mapper 66). It lets you shoot Mario's enemies with the Zapper, adds two new Duck Hunt games and a dog that can be shot while he laughs at you, and makes the game-select menu work with the Zapper.

No ROM images are distributed here. You need your own dump of the original cartridge and apply the IPS patch from the download section to it; see Getting the ROM.

A Goomba shot with the Zapper flips over with the fireball puff and 100 points Game D: the dog holds up three ducks, one in his mouth

What it does:

Everything else (the SMB game itself, Duck Hunt Games A-C) is the original code.

Screenshots:

Duck Hunt title screen with Games A to E Game D: three ducks in the air, three shots, nine ducks per round

The laughing dog has just been shot: arms out, eyes X'd out, before he drops into the grass Game E: a randomly generated hunting ground with two trees and two bushes

Game E: another random hunting ground with a single tree and bush The multicart menu, which can be operated with the Zapper

The screenshots come from the project's own test emulator (tools/screenshots.py).

Download:

ZapperMario.ips is the patch, PATCHING.md describes it record by record, ips.py applies it (Python 3, no other dependencies), and ZapperMario-source.zip is the complete build environment: the new 6502 source, the assembler, the test emulator and the build script. No ROM images are included in any of them.


Listing directory https://download.elifulkerson.com/files/zappermario/1.0: PATCHING.md November 05 2026 02:33:53 4685 ASCII text, with very long lines (379) PATCHING.md.md5 November 05 2026 02:33:53 46 MD5 checksum PATCHING.md.sha1 November 05 2026 02:33:53 54 SHA1 checksum PATCHING.md.sha256 November 05 2026 02:33:53 78 SHA256 checksum PATCHING.md.sha512 November 05 2026 02:33:53 142 SHA512 checksum ZapperMario-source.zip November 05 2026 02:33:53 280993 Zip archive data, at least v1.0 to extract, compression method=store ZapperMario-source.zip.md5 November 05 2026 02:33:53 57 MD5 checksum ZapperMario-source.zip.sha1 November 05 2026 02:33:53 65 SHA1 checksum ZapperMario-source.zip.sha256 November 05 2026 02:33:53 89 SHA256 checksum ZapperMario-source.zip.sha512 November 05 2026 02:33:53 153 SHA512 checksum ZapperMario.ips November 05 2026 02:33:53 21272 IPS patch file ZapperMario.ips.md5 November 05 2026 02:33:53 50 MD5 checksum ZapperMario.ips.sha1 November 05 2026 02:33:53 58 SHA1 checksum ZapperMario.ips.sha256 November 05 2026 02:33:53 82 SHA256 checksum ZapperMario.ips.sha512 November 05 2026 02:33:53 146 SHA512 checksum ips.py November 05 2026 02:33:53 2288 Python script, ASCII text executable ips.py.md5 November 05 2026 02:33:53 41 MD5 checksum ips.py.sha1 November 05 2026 02:33:53 49 SHA1 checksum ips.py.sha256 November 05 2026 02:33:53 73 SHA256 checksum ips.py.sha512 November 05 2026 02:33:53 137 SHA512 checksum ↩ Browse the download server

Getting the ROM:

The hack is distributed as an IPS patch, ZapperMario.ips, which turns the unmodified original ROM into the Zapper Mario ROM. You must supply your own copy of the original.

1. Check that you have the right original ROM

The patch only works on this exact file (iNES header included):

FileSuper Mario Bros. + Duck Hunt (USA).nes
Size81,936 bytes (16-byte iNES header + 64 KB PRG + 16 KB CHR, mapper 66)
MD51306a0286248a0851005464d7ec8d785
SHA-1663ddef71cc808c5382a3426ef6d03b5499a51c8
CRC32666ee28e

Verify before patching, for example:

md5sum "Super Mario Bros. + Duck Hunt (USA).nes"                    # Linux / macOS
certutil -hashfile "Super Mario Bros. + Duck Hunt (USA).nes" MD5    # Windows

If the MD5 differs (different dump, headerless ROM, trained or overdumped copy), do not apply the patch; the result will not work.

2. Apply the patch

Use any IPS patcher:

3. Check the result

Patched ROM size98,320 bytes (header + 64 KB PRG + 32 KB CHR)
Patched ROM MD5055e4b5461be8e9a5b5c7ac50f4da70b
Patched ROM SHA-1646419c775d60729b35a1e0ff49b2d3997a4100c
Patch file MD500d2373da03411e73f12e174f399cdac (21,272 bytes)

The patched ROM is 16 KB larger than the original: the CHR ROM grows from two to four 8 KB banks (header byte 5 changes from 2 to 4) because the hurt-dog face needed a free sprite tile. Every IPS patcher handles this (the patch writes the whole appended area), but the result must be exactly 98,320 bytes.

4. Play it

Plug the Zapper into controller port 2 and a standard controller into port 1. Like every Zapper game it needs a CRT television, or an emulator with Zapper support (Mesen, FCEUX, Nestopia and others); the Zapper does not work on LCD screens. The hack has not yet been tried on real hardware.

Rules inherited from the fireball logic:

Because a shot is treated as a fireball hit, fireproof enemies are not harmed by shots either: Buzzy Beetles, Bullet Bills and Podoboos just get the explosion puff drawn on them (a dud, silent as in the original). Bowser's fire, fire bars and other non-enemy objects are not targets at all. A flattened Goomba cannot be shot again. One shot hits at most one enemy (the lowest enemy slot that the gun sees), and a shot costs a coin whether it hits or not.

How a shot works (Duck Hunt style):

When the trigger is pulled during normal play the game freezes for a few frames:

  1. one black frame (if the sensor sees light here the gun is pointed at a lamp and the shot is ignored),
  2. one frame per on-screen enemy showing only a solid white 32x32 box at that enemy's position (background off); the first enemy whose box the sensor sees is hit,
  3. the game resumes. Music keeps playing during the frozen frames.

So a shot costs between 2 and 8 frames.

Building from source:

python3 build.py            # reads "Super Mario Bros. + Duck Hunt (USA).nes", writes the patched ROM

Requires only Python 3 (and the original ROM in the project directory). Everything is self-contained: tools/asm6502.py is a small two-pass 6502 assembler that understands the syntax of the doppelganger SMB disassembly (src/SMBDIS.ASM), which is assembled first to obtain all SMB symbols and to verify, byte for byte, that bank 0 of the input ROM is the expected SMB build. Every byte patch to the original code is checked against the expected original bytes before it is applied.

To regenerate the patch after a change:

python3 tools/ips.py make "Super Mario Bros. + Duck Hunt (USA).nes" "Super Mario Bros. + Duck Hunt (Zapper Mario).nes" patch/ZapperMario.ips

It prints the hashes and the record list; update the tables in README.md and in patch/PATCHING.md (which also describes what each patch record changes).

Testing:

tools/nestest.c is a tiny headless NES emulator written for this project (mapper 66 with bus-conflict checking, scanline PPU with sprite-0 hit, Zapper model similar to Mesen's, no sound). tools/play.py drives it through 56 scripted checks:

gcc -O2 -o build/nestest tools/nestest.c
python3 tools/play.py

Sound cannot be tested this way (the emulator has no APU); it was checked by ear in a full emulator.

Under the hood: the Zapper in Super Mario Bros. (for hackers):

SMB's 32 KB bank has essentially no free space (a 6-byte hole at $F2CA is the largest), so the new code lives in the unused area of bank 1 (menu / Duck Hunt bank) at $A180. See the comment block at the top of src/zapper.asm.

Under the hood: the Duck Hunt changes (for hackers):

The Duck Hunt extensions live in src/duckhunt.asm, assembled into the free area of bank 1 right after the Zapper code (about 3.5 KB at $A860-$B680), and are wired in with 36 small byte patches listed in tools/dh_build.py. Things worth knowing:

A record-by-record description of the patch is in PATCHING.md, in the download section.

Source zip layout:

PathWhat it is
patch/ZapperMario.ips, patch/PATCHING.mdthe patch and its description
build.pybuilds the patched ROM from the original
src/zapper.asm, src/duckhunt.asmthe new code
src/SMBDIS.ASMdoppelganger's SMB disassembly, used for symbols and verification
tools/asm6502.py, tools/dis6502.pyassembler and disassembler
tools/dh_build.pyDuck Hunt build steps: generated tables, byte patches, CHR expansion
tools/ips.pyIPS make / apply
tools/nestest.c, tools/play.pyheadless test emulator and the scenario suite
tools/screenshots.pyrenders the screenshots on this page with the test emulator
tools/chr2png.py, tools/ppm2png.pytile sheet and screenshot converters
images/the screenshots

All tools are written for this project and depend only on Python 3 (and a C compiler for the test emulator). ROM files (*.nes) are excluded from the zip and never distributed.