Scramble
Scramble (Konami, 1981; licensed to Stern Electronics in the US) is the arcade game that defined the horizontally-scrolling shooter, flying a jet through six distinct terrains against a steadily draining fuel gauge. This build runs the original Zilog Z80 board in the browser - the pure-TypeScript Konami Galaxian/Scramble hardware model from 8bitworkshop, bundled to a browser global - and boots straight into the attract-mode score table. 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, and set execution breakpoints and write watchpoints.
Runs on: Web browser
Scramble Online Emulator
Play Scramble using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| Scramble | Scramble | Scramble | grey | Open ⛶ |
Machine emulated
The Scramble arcade board (Konami, 1981) - a Zilog Z80 at 3.072 MHz on Galaxian-derived hardware: a 32-column tilemap with per-column scroll, hardware sprites and missiles, a twinkling star field and a 256×224 raster coloured through a 32-entry PROM, with a second Z80 and twin AY-3-8910 PSGs on a separate sound board. It is the machine that founded the horizontally-scrolling shooter.
Chips
Notes
Embedding
Scramble is the 1981 Konami arcade machine (licensed to Stern in the US): a Zilog Z80 at 3.072 MHz driving a 256×224 raster on Galaxian-derived tilemap-and-sprite hardware, with a second Z80 and twin AY-3-8910 PSGs on a separate sound board. This build vendors the pure-TypeScript GalaxianScrambleMachine from Steven Hugg's 8bitworkshop (MIT), bundled to a browser global with esbuild, and drives it from a host-owned loop so the debugger can control it.
Boot. One combined ROM image (main program at 0x0000–0x3FFF, character/sprite graphics at 0x4000, the colour PROM at 0x5000) is handed to the machine and the Z80 starts at 0x0000, dropping straight into the attract-mode score table:
var machine = new ScrambleCore.GalaxianScrambleMachine();
machine.reset();
machine.loadROM(SCRAMBLE_ROM); // 0x5020-byte combined image
machine.connectVideo(pixels); // 264x264 ARGB frame buffer
The machine object. The core exposes the whole board as ordinary methods and fields, so the host page can render, step and inject input:
| Member | Kind | What it does |
|---|---|---|
advanceFrame(trap) | method | Run one 60 Hz field, calling trap() before every instruction; when trap returns true it stops mid-frame — this is what powers stepping and breakpoints. |
cpu.advanceInsn() | method | Execute exactly one Z80 instruction — a single debugger step. |
cpu.saveState() / cpu.loadState(s) | method | Read or write the full Z80 register file (AF/BC/DE/HL and their primes, IX/IY, SP, PC, I/R, IFF) — how the debugger reads and pokes registers. |
readConst(a) | method | Side-effect-free memory read (skips the I/O windows) — what the debugger's hex and disassembly views call. |
read(a) / write(a,v) | method | The live CPU bus; both are wrapped here to route the PPI address blocks (0x8100–0x8203) into the two 8255s, and write also pauses the loop on a store to a watched address. |
inputs | field | The active-low input latches (joystick, fire, bomb, coin, 1P/2P start) that the control panel and keyboard drive; PPI #0 reads them on its port A/B/C inputs. |
Video. Each field the loop runs the machine's 264 scanlines, then copies the 264×264 ARGB frame buffer into an ImageData and blits its visible 256×224 window onto the canvas, rotated 90° (the Konami video RAM is stored sideways).
Debugger integration
The debugger drives a host-owned run loop. The Z80 interpreter runs one instruction at a time, and the machine's advanceFrame(trap) calls a trap callback before every instruction, so the loop can pause, single-step and check breakpoints between any two instructions — no changes to the CPU core were needed.
window.EMU_BOOT.transport exposes the controls the shared debugger calls:
- pause / resume / isPaused — stop or restart the
requestAnimationFrameloop. Resume first single-steps past a breakpoint it is parked on, so it does not re-trigger immediately. - stepInsn(n) — call
cpu.advanceInsn()exactly n times and redraw, so a single step advances the PC by one instruction. - step(n) — advance n whole 60 Hz fields (interrupts and all).
- breakpoints — a
Setof PC values. The frame trap returns true when the Z80's PC is in the set, pausing before that instruction; when the set is empty the trap is a no-op, so an idle debugger costs nothing. - watchpoints —
machine.writeis wrapped so a store to a watched address raises a flag, and the frame trap pauses the loop on the next instruction.
The plug-in (scramble-debug.js) reads these hooks and calls EmuKit.defineMachine with the full Z80 register set (bound to the shared z80 disassembler at /debugger/src/cpus/z80.js), five memory chips (the 64K bus, the 16K program ROM, the 2K work RAM, the 1K character VRAM with a tile view, and the object RAM) and an on-screen control panel. Registers are read and written through the core's saveState()/loadState(), so writing to a register in the debugger updates the running machine.
Architecture
The Konami Scramble board is the Galaxian hardware extended for a horizontally-scrolling shooter, modelled here as plain objects on the GalaxianScrambleMachine:
- CPU — a Zilog Z80 at 3.072 MHz (the 18.432 MHz master crystal ÷6), with the 64 KB address space behind an address-decoder bus: ROM at
0x0000, work RAM at0x4000, character VRAM at0x4800, object (sprite) RAM at0x5000, the interrupt / star / watchdog latches from0x6800, and the two 8255 PPIs at0x8100and0x8200. - I/O (8255 PPI) — two faithful Intel 8255 Programmable Peripheral Interfaces (
scramble-ppi.js) carry the whole I/O and main↔sound interface, exactly as on the PCB. PPI #0 (0x8100–0x8103) reads the inputs and DIP switches on ports A/B/C; PPI #1 (0x8200–0x8203) drives the sound board (port A = the command latch, port B = the IRQ / mute control line) and the protection port C. Each chip models mode 0 with the control register (mode-set and bit set/reset), per-port and per-port-C-nibble direction, and datasheet read/write semantics. - Video — a 32-column tilemap with per-column scroll, hardware sprites, missiles and a twinkling star field, coloured through a 32-entry PROM. The display is rotated 90°.
- Protection — Scramble's challenge/response protection lives on PPI #1 port C: the program writes nibbles that advance a state machine and reads the response back (its top bit is also mirrored into PPI #0 port C bits 5 and 7), so the main set boots past its start-up check.
- Sound — the full Konami sound board: a second Zilog Z80 running the real
ot1/ot2/ot3sound ROMs and driving two AY-3-8910 PSGs, wired to the main CPU by a command latch and an interrupt. It is emulated inscramble-sound.jsand its mix is fed to the shared audio sink. See the Sound notes below.
The main CPU takes one NMI per field (enabled through a latch at 0x6801); the host loop reproduces exactly that, running the 264 scanlines of the field between NMIs and feeding the watchdog the game expects.
Sound
This is the authentic Konami sound board, not a synthesized substitute. On the real Scramble PCB the audio is a second, independent Zilog Z80 running its own program ROMs and driving two AY-3-8910 PSGs. scramble-sound.js emulates that whole board: it instantiates a second Z80 (the same core the main CPU uses) executing the genuine ot1.5c / ot2.5d / ot3.5e sound ROMs (each SHA1-verified byte-exact against MAME's scramble set), and two instances of the shared canonical AY-3-8910 (/debugger/src/chips/ay8910.js). So what you hear is Scramble's real score and effects, produced by the real sound program.
Pattern. S-synthesis done faithfully: a real second CPU + real chips rather than a hand-written tune. The board is modelled from MAME (src/mame/galaxian/scramble.cpp, scramble_state::scramble): sound Z80 and both AYs clocked at 14318000/8 = 1 789 750 Hz; sound memory ROM 0x0000–0x2FFF, work RAM 0x8000–0x8FFF; AY access on the sound-CPU I/O ports (OUT 0x10/0x40 = register select for chip 1/2, IN/OUT 0x20/0x80 = data). AY #2's port A reads the command latch and its port B reads the Konami bi-quinary sound timer.
Command latch & interrupt. The main CPU talks to the sound Z80 through the real 8255 PPI #1 (scramble-ppi.js): a write to port A (0x8200) latches a one-byte command, and a write to port B (0x8201) whose bit 3 falls 1→0 clocks a flip-flop that asserts the sound Z80's /INT (IM 1 → RST 38h), cleared on interrupt-acknowledge; bit 4 mutes. These are the PPI's port A and port B output callbacks, so they fire only once the program has configured the chip — exactly as on hardware. The sound Z80's handler reads the command back through AY #2 and starts or stops the music and effects.
Rate & mixing. Both AYs are told EmuAudio.sampleRate so pitch is exact with no resampling. Each 60 Hz field the sound Z80 is run for one frame of T-states, interleaved with per-sample AY rendering (run ~cyclesPerSample cycles, render one sample, repeat) so mid-frame register writes land in time; the two chips' mono outputs are summed, DC-blocked, gained and clamped to interleaved-stereo Int16, exactly Math.round(EmuAudio.sampleRate/60) frames per field, then EmuAudio.push()-ed.
var snd = ScrambleSound.create(EmuAudio.sampleRate, SCRAMBLE_SOUND_ROM, ScrambleCore.Z80, AY8910);
var ppi1 = ScramblePPI.create({ // PPI #1: sound + protection
outPA: function(v){ snd.writeLatch(v); }, // port A out: sound command
outPB: function(v){ snd.writeControl(v); }, // port B out: IRQ edge + mute
outPC: function(v){ machine.scramble_protection_w(0x8202, v); },
inPC: function(){ return machine.m_protection_result; }
});
machine.read = machine.write = /* route 0x8100-0x8203 into the two PPIs */;
EmuAudio.push(snd.tick()); // once per field: sampleRate/60 stereo Int16
Out-of-the-box sound. Scramble's attract score-table is silent on real hardware — the sound board only plays once a game is running. So a short time after boot the loop auto-inserts a coin and presses 1P Start (re-arming if the machine falls back to silent attract); the real game then drives the real sound board. A human who is actually playing sends their own sound commands, so this never fires over live play, and it is suppressed while the debugger has the machine paused.
Mute. window.EMU_BOOT.transport.isMuted() / setMute(bool) delegate to EmuAudio. It starts muted — browsers block audio before a user gesture — so the Sound button unmutes from a real click; while muted, EmuAudio.push discards the samples the board keeps generating.
Caveats. The scramble board carries no RC output filters (MAME notes the filter map is dead code on this set), so none are modelled. Both 8255 PPIs are now emulated in full (scramble-ppi.js), so the inputs, DIP switches, protection and the whole sound path all run through the real chips rather than being special-cased.