Search › A-Z › M › Mario Bros.
Mario Bros.
Mario Bros. (Nintendo, 1983), designed by Shigeru Miyamoto and Gunpei Yokoi, is the arcade platformer that first paired Mario with his brother Luigi, two plumbers clearing pests from the sewers by flipping them over from below and kicking them away. This build runs the original Zilog Z80 board in the browser, the real program ROMs on a JavaScript Z80 core plus a hand-written model of Nintendo’s tile-and-sprite video hardware and its Z80-DMA sprite copier, and boots straight into the attract-mode demo. It is wired to the emulators.org in-frame debugger so you can single-step the Z80, read and write the registers and the full 64K memory (video RAM sits at 0x7400, the object list at 0x7000), and set execution breakpoints and write watchpoints.
Runs on: Web browser
Mario Bros. Online Emulator
Play Mario Bros. using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Mario Bros. | Mario Bros. | Mario Bros. | grey | Open ⛶ |
Machine emulated
The Mario Bros. arcade board (Nintendo, 1983) - a Zilog Z80 main CPU with program ROM at 0x0000 and 0xF000, 6 KB of work/video RAM, a 32×32 background tilemap scrolled vertically, 16×16 hardware sprites copied by a Zilog Z80-DMA chip and a single 512×8 colour PROM, driving a 256×224 horizontal raster. An M58715 (i8039) with a discrete sound section handles audio on the real machine.
Chips
Notes
Embedding
Mario Bros. is Nintendo's 1983 arcade machine: a Zilog Z80 main CPU driving a 256×224 raster, the sequel-of-sorts to Donkey Kong that pairs Mario with his brother Luigi as plumbers stomping pests in the sewers. This build runs the original program ROMs on DrGoldfire/Z80.js (MIT), an instruction-at-a-time Z80 interpreter, wrapped in a hand-written model of the Mario Bros. board (memory map, tile/sprite video, the Z80-DMA sprite copier and the input latches). The graphics-ROM decode, colour-PROM palette, Z80-DMA and renderer are ported from MAME's nintendo/mario.cpp.
Boot. The program ROMs are loaded at 0x0000–0x5FFF and 0xF000–0xFFFF and the Z80 starts at 0x0000, dropping straight into the attract-mode demo:
var m = new Mario(); // decodes gfx ROMs + PROM, wires the Z80 and the Z80-DMA
m.reset(); // PC = 0x0000 -> attract mode
The machine object. Mario exposes the whole cabinet as ordinary methods, so the host page can render, step and inject input:
| Member | Kind | What it does |
|---|---|---|
stepInsn() | method | Fetch, decode and run one Z80 instruction; returns its T-cycle count. |
frameInterrupt() | method | Pulse the vertical-blank NMI (gated by the 0x7E84 mask latch the game writes) once per field. |
render(buf32) | method | Paint the 256×224 frame: the 32×32 vertically-scrolled background tilemap plus the hardware sprites the Z80-DMA copies into the sprite list. |
peek(a) / poke(a,v) | method | Side-effect-free access to the address space — what the debugger's memory views read and poke. |
setInput(id,down) | method | Drive the joystick / jump / coin / start latches read on ports 0x7C00 (IN0) and 0x7C80 (IN1). |
reset() | method | Clear RAM, reset the Z80-DMA and reset the Z80 — the cabinet's power-cycle. |
Video. Each animation frame the loop runs one field's worth of Z80 cycles, pulses the NMI, then re-renders: the background is a 32×32 tilemap of 8×8 2bpp characters (video RAM at 0x7400) scrolled vertically by the 0x7D00 register, and the sprites are 16×16 3bpp objects read from the list at 0x7000. Colour comes from a single 512×8 palette PROM (the inverted Nintendo half, like Donkey Kong), banked by the 0x7E83 latch; a graphics bank at 0x7E80 selects the character set.
Debugger integration
The debugger drives a host-owned run loop: because the Z80 interpreter runs one instruction at a time in JavaScript, the loop can pause, single-step and check breakpoints between any two instructions — no changes to the CPU core are needed.
window.EMU_BOOT.transport exposes the controls the shared debugger calls:
- pause / resume / isPaused — stop or restart the
requestAnimationFrameloop. - stepInsn(n) — call the core's
stepInsn()exactly n times and redraw, so a single step advances the PC by one Z80 instruction. - step(n) — advance n whole 60 Hz fields (each with its NMI).
- breakpoints — a
Setof PC values. When it is non-empty the loop reads the Z80's PC (viagetState()) before each instruction and pauses before executing an address in the set; when empty it runs a full field at speed, so an idle debugger costs nothing. - watchpoints — the machine's RAM-write path (shared by the Z80 and the Z80-DMA copier) is wrapped, so a write to a watched address sets a flag that pauses the loop after that instruction. The watch
Setis injected withsetWatchSet().
The plug-in (mariobros-debug.js) reads these hooks and calls EmuKit.defineMachine with the full Z80 register file (AF/BC/DE/HL and their bytes, IX/IY, SP, PC, I, R and the S Z H P/V N C flags), each read live from getState() and written back through setState(). It reuses the shared z80 disassembler (/debugger/src/cpus/z80.js) for the hex/disasm views over the 64 KB bus, the 24 KB low program ROM, the video RAM (rendered as tiles) and the object/sprite RAM. Because a watched address must be caught inside the Z80-DMA block copy as well as on plain CPU stores, the watchpoint check lives in the shared wrMem helper the DMA transfer also calls.
Architecture
The Mario Bros. board (Nintendo, 1983) is a single Zilog Z80 main CPU with Nintendo's tile-and-sprite video and a Z80-DMA chip that copies the sprite list, all modelled here as plain JavaScript hanging off Mario:
- CPU — a Z80 interpreter.
0x0000–0x5FFFand0xF000–0xFFFFare program ROM;0x6000–0x77FFis RAM, with the sprite/object list at0x7000and the video RAM at0x7400. - Video — a 32×32 background tilemap of 8×8 2bpp characters, scrolled vertically by
0x7D00, plus 16×16 3bpp sprites with per-object X/Y flip. A single 512×8 colour PROM (the Nintendo inverted half) supplies the 256-colour palette; the0x7E80graphics bank and0x7E83palette bank latches select the character set and colour table. - Z80-DMA — a Zilog Z80-DMA chip, programmed through
OUT (0),A, block-copies the sprite attributes into the object list each field when the game raises its READY line via the0x7E85latch bit. The register-write state machine and the memory-to-memory transfer are modelled after MAME's driver. - Input — the joystick (left/right), jump and start buttons are read on
0x7C00(IN0), coin on0x7C80(IN1), the DIP switches on0x7F80. - Interrupt — a single NMI per field at vertical blank, gated by the
0x7E84mask latch, is the timing the game depends on. - Sound — the real cabinet uses an M58715 (an 8049-class MCS-48) sound CPU driving an 8-bit DAC, plus three small discrete circuits. This build emulates that CPU for real (
i8035.js) running the original sound ROM, so the music and effects are the sound program's own output. The main Z80 talks to it through the0x7E00music command, the0x7F00–0x7F07sample/effect strobes and the0x7C00/0x7C80walk samples. See the Sound note.
Sound
Pattern: the real sound hardware. Mario Bros.' audio board is an M58715 — an 8049-class Intel MCS-48 sound CPU — running its sound program out of a 4 KB ROM (tma1-c-6k_e.6k) and driving an 8-bit DAC through an analog stage, with three small discrete circuits alongside. This build emulates that CPU for real: i8035.js is a from-scratch MCS-48 core (accumulator, PSW with the two register banks in the 64-byte internal RAM, the prescaled timer, the external and timer interrupts, the BUS/P1/P2/T0/T1 pins and the SEL MB program-bank bit) whose opcode dispatch mirrors MAME's s_mcs48_opcodes table one-for-one, including per-instruction machine-cycle counts. It executes the original sound ROM, so the jump, the coin and point jingles, the death tune, the crab / turtle / fly / freeze / POW effects and the background music are the sound program's own output — not a synthesiser.
The ROM & the undumped MCU. One sound ROM is vendored, byte-verified against MAME's ROM_START( mario ): tma1-c-6k_e.6k (4 KB, CRC32 06b9ff85, SHA1 111a29bcb9cda0d935675fa26eca6b099a88427f). The M58715 also has a 2 KB internal ROM (m58715-051p.5l) that has never been dumped; exactly as MAME does, this build supplies a 3-byte fake bootstrap (SEL MB1; JMP 0) in its place, which jumps to external 0x800 — whose first real opcode (ANL P2,#0xDF) clears P2.5, forcing the EA pin high so the whole 4 KB external ROM becomes the program. The core models that internal/external split through the EA pin.
Board wiring (from MAME nintendo/mario.cpp). mario.js's setSoundSink forwards the main-CPU sound writes to mariobros-sound.js: 0x7E00 is the music/tune command; the M58715 reads it (or a bank of the sound ROM) back through its MOVX port — tune_r returns the command when P2.7 is set, else soundrom[(P2 & 0x0F) × 256 + offset] — and writes each output byte to the DAC. 0x7F00 pulses the CPU's external INT (death); 0x7F01/0x7F02 feed T0/T1 (get-coin, freeze); 0x7F03–0x7F06 feed P1 (crab, turtle, fly, coin). The two footstep “walk” strobes (0x7C00/0x7C80) and the “skid” (0x7F07) drive discrete circuits.
Timing & mixing. The M58715 runs at its real 11 MHz / 15 = ~733 kHz machine-cycle rate; each video field the core is advanced one field's worth of cycles and the held DAC value is sampled at EmuAudio.sampleRate (zero-order hold, DC-blocked to emulate the AC-coupled analog stage), then exactly Math.round(EmuAudio.sampleRate/60) interleaved-stereo Int16 pairs are pushed to the shared sink per field. Attract mode may play the ROM's own music, and coining up and pressing Start runs the full sound program during play.
MarioSound.init(); // loads the sound ROM, builds the M58715
m.setSoundSink(function(a,v){ MarioSound.command(a,v); }); // board latch writes -> sound CPU
MarioSound.frame(); // run one field, push sampleRate/60 stereo Int16
Mute contract. window.EMU_BOOT.transport exposes isMuted()/setMute(m), delegated to EmuAudio; audio starts muted (browsers block audio before a gesture) and the shell's Sound button resumes the AudioContext and unmutes from a real click.
Discrete walk / skid. The two footstep sounds (0x7C00/0x7C80) and the skid (0x7F07) have no ROM behind them; they are now synthesised from MAME's real netlist (nintendo/nl_mario.cpp). Each voice is a 74123 one-shot gating the XOR of two SN74LS629 VCOs whose pitch is swept by an RC envelope off the one-shot — every one-shot width, sweep tau, VCO timing cap, the 329 Hz mixer low-pass and the skid's CD4020 ÷16 divider are the schematic's own values, so Mario's and Luigi's footsteps differ (different timing caps) exactly as on the board.
Honest limitation. Everything that flows through the M58715 DAC is the real sound program running the real ROM. The walk/skid voices are a netlist-derived DSP model: the '629 VCOs are modelled as frequency inversely proportional to their real timing cap, with one scale constant calibrated to the mixer corner (the absolute '629 frequency-vs-control-voltage curve is not reproduced), rather than a full nodal solve.