Search › A-Z › S › Sharp MZ-80K
Sharp MZ-80K
This is a from-scratch JavaScript emulator of the Sharp MZ-80K, the Z80 home computer Sharp introduced in 1978 as its first "Clean Computer": the ROM holds only the SP-1002 monitor, so the machine boots to a monitor prompt and loads BASIC and everything else from cassette. The build emulates the Z80A, the flat memory map, the 40×25 character display drawn from the MZ-80K character-generator ROM, the Intel 8255 keyboard and cassette interface and the Intel 8253 timer, and boots to the SP-1002 prompt. It reuses the MIT Z80.js CPU core (the same core the shared debugger already targets) and wires the machine into that debugger, so you can single-step the Z80, set breakpoints and watchpoints and inspect memory, and type on a keyboard coloured like the real machine.
Runs on: Web browser
Sharp MZ-80K Online Emulator
Play Sharp MZ-80K using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| SP-1002 monitor | Sharp MZ-80K | Sharp MZ-80K | grey | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
The MZ-80K is a from-scratch JavaScript emulation written for this site; only the CPU is reused. The Zilog Z80 core is Molly Howell's MIT Z80.js (the same core the shared debugger already disassembles). Everything else - the memory map, the Intel 8255 keyboard/cassette PPI, the Intel 8253 timer and the character-cell video - is emulated in mz80k.js, and the whole machine runs from a host-owned loop so the debugger can pause and single-step it.
Boot. Construct the machine on a canvas; boot() fetches the two ROMs (the 4 KB SP-1002 monitor and the 2 KB character generator), resets the Z80 and starts the loop. The machine comes up at the monitor prompt:
var m = new MZ80K({ canvas: canvas, romBase: '/emulator/mz80k/src/' });
m.boot(); // fetch SP-1002 + CGROM, reset the Z80, run
// each animation frame runs one ~60Hz field of the Z80, then draws the 40x25 screen
The machine object. Everything the host and debugger need is a field or method on the machine - there is no wasm heap to reach into:
| Member | Kind | What it does |
|---|---|---|
m.cpu | field | The Z80 core. run_instruction() executes one instruction (the single-step primitive); get_pc() reads PC cheaply for breakpoints; getState()/setState() snapshot the registers. |
m.getReg(name) / m.setReg(name,v) | method | Focused register access (A F, B C D E H L, the AF/BC/DE/HL pairs, IX IY SP PC, I R) built over getState/setState. |
m.dbgRead(a) / m.dbgWrite(a,v) | method | Side-effect-free read of the 64K bus and the CPU write path - what the debugger's memory views read and poke. The 0xE000 I/O page reads back blank so inspecting it never disturbs the keyboard scan. |
m.setKey(strobe, bit, down) | method | Drive one cell of the 10×8 key matrix; the monitor scans it through the 8255 at 0xE000. |
Video. The display is 40×25 character cells. Each 0xD000 VRAM byte is a Sharp display code; the renderer draws it as an 8×8 cell from the MZ-80K character-generator ROM, so the monitor's text reads back exactly as the video hardware scanned it.
Debugger integration
Because the whole machine is ordinary JavaScript and the host owns the run loop, the debugger's controls need no changes to the CPU core: the loop can pause, single-step and check breakpoints between any two instructions. window.EMU_BOOT.transport maps the shared debugger onto the Z80:
- Registers are read live from the Z80 core each refresh - the bytes and the AF/BC/DE/HL pairs, IX IY SP PC, I R and the S Z H P/V N C flags - and written straight back through
getState/setState. - Disassembly uses the shared
z80decoder over the full 64 KB, so the SP-1002 monitor and anything you load read back as Z80 mnemonics. - Memory is the machine's side-effect-free
dbgRead; the0xE000-0xE7FFI/O window (8255 / 8253 / keyboard) is blanked so the auto-polling hex view never disturbs the timers or the key-matrix scan. - Single step is one
run_instruction(); breakpoints (checked againstget_pc()before each instruction) and write watchpoints (flagged inside the CPU memory-write path) are host-side checks in the boot loop that pause it on a hit. - Keyboard. The physical keyboard is mapped character-accurately to the MZ-80K key matrix; an on-screen keyboard coloured like the real machine drives the same matrix for touch.
Architecture
The MZ-80K (Sharp, 1978) is Sharp's first "Clean Computer": a cassette-based Z80A machine whose ROM holds only a monitor, so it boots to a prompt and loads its language from tape. This build keeps each part visible:
- Z80A CPU at ~2 MHz. The core interprets one instruction per
run_instruction(); the sharedz80decoder disassembles it. - Flat memory map - the SP-1002 monitor ROM at
0x0000-0x0FFF, 48 KB DRAM to0xCFFF, the 40×25 text VRAM at0xD000, and the memory-mapped I/O page at0xE000. There is no bank switching. - Intel 8255 PPI at
0xE000- Port A strobes the 10-line keyboard matrix, Port B reads the eight sense lines (active low), and Port C carries the vertical-blank, cursor-blink (555/556) and cassette status bits plus the video-gate output. - Intel 8253 PIT at
0xE004- counter 0 is the sound tone; the counter chain produces the real-time-clock interrupt (mode 1,RST 38h), the machine's only interrupt. - SP-1002 monitor - Sharp's bundled monitor ROM; it prints the prompt the machine boots to and provides the cassette load/save and memory commands.
The character-cell screen is drawn from the MZ-80K character-generator ROM: every VRAM byte repaints one 8×8 cell, exactly as the video hardware scanned it.
Sound
The MZ-80K makes sound with a single square-wave voice: Intel 8253 counter 0 (port 0xE004), programmed in mode 3, is fed a 1 MHz clock and its output is gated to a small speaker by the memory-mapped port 0xE008 (bit 0: writing an odd value turns the tone on, an even value off). The tone frequency is simply 1 000 000 / reload, where reload is the 16-bit divisor written LSB then MSB to 0xE004. This is the Pattern S case: the machine is authored from scratch and the core already decoded these writes but discarded them, so we synthesise the voice from the state the CPU sets.
Where it hooks. mz80k.js already latches counter 0's reload in its 8253 model; we added a soundGate flag set from the 0xE008 write, and a genAudio() that runs once per video frame inside the host loop. It reads the live counter-0 reload and the gate, and renders a square wave at EmuAudio.sampleRate (so the pitch is correct with no resampling), duplicating the mono voice to left and right and pushing exactly Math.round(EmuAudio.sampleRate / 60) interleaved-stereo Int16 samples per frame to the shared sink (debugger/src/audio.js). When the gate is low or the reload is not a usable divisor it pushes silence.
Mute contract. window.EMU_BOOT.transport exposes isMuted() / setMute(m), delegating to EmuAudio. It starts muted (browsers block audio before a user gesture); the shared Sound button resumes the AudioContext and unmutes from a real click.
The machine boots to the silent SP-1002 monitor prompt and stays there; we do not force any startup noise. Because the voice is generated each frame from the counter-0 reload and the gate, anything that programs it (an SP-5030 BASIC MUSIC/tempo sequence loaded from tape, or a monitor beep) is heard the moment it plays, once the Sound button unmutes.