SearchA-ZP › PET 2001 Emulator (masswerk)

PET 2001 Emulator (masswerk)

2017 Freeware Online

The masswerk PET 2001 emulator is a browser-based Commodore PET 2001 emulator by Norbert Landsteiner, based on an original PET emulation by Thomas Skibo. It emulates the 6502 CPU and CRT display and adds keyboard, tape and disk image handling plus a built-in BASIC and 6502 assembler/disassembler.

Visit the official site ↗

Runs on: Web browser

PET 2001 Emulator (masswerk) Online Emulator

Play PET 2001 Emulator (masswerk) using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
PET BASICPET 2001 Emulator (masswerk)Commodore PET 2001greyOpen ⛶

Machines emulated

Chips

Notes

Embedding

masswerk's PET 2001 is a set of plain JavaScript modules. Vendor them and load them in dependency order, but skip its own controller (pet2001ctrl.js) — that owns the page UI and a requestAnimationFrame loop that can't be paused or stepped, which the debugger needs. Instead replicate the controller's run(): build the machine, hand it a canvas context and a keyboard, and drive it from a loop you control.

var ctx  = document.getElementById("screen").getContext("2d");
var keys = new PetKeys(null);          // physical keyboard → the PET key matrix
var pet  = new Pet2001();
pet.init(controllerStub, ctx, keys, { RAM_SIZE:0x4000, ROM_VERSION:"2" });
(function loop(){ pet.cycle(16667); requestAnimationFrame(loop); })();   // ~1 MHz / 60 Hz

The machine is plain objects. Everything the debugger needs is a method on the machine or its CPU:

MemberKindWhat it does
pet.cycle(ticks)methodRun the machine for ticks clocks (steps I/O + CPU); returns <0 if the CPU jams. cycle(1) runs one instruction — the single-step primitive.
pet.reset()methodReset the machine.
pet.read(a) / pet.write(a,v)methodCPU bus read/write — poke memory live.
pet.getCPUStatus()methodLive 6502 state: {pc,a,x,y,sp,sr, c,z,i,d,b,v,n}.
pet.setRegister(r,v)methodWrite a register (pc a x y sp sr).
pet.setFlag(f,v)methodWrite a status flag (c z i d b v n).
PetKeys(elementId)classThe keyboard: attaches window key events and drives io.setKeyrows(), the PET key matrix.

Because the CPU and memory are ordinary JavaScript, the debugger single-steps with cycle(1), reads and writes the registers through getCPUStatus / setRegister, and implements breakpoints and watchpoints as host-side checks around the loop — no changes to the emulator.

Debugger integration

The masswerk PET even ships its own debugger (petdebug.js) built on the same getCPUStatus / setRegister surface, which confirms the machine is fully introspectable from JavaScript. Our plug-in uses that surface directly.

  • Registers come from pet.getCPUStatus() each refresh and are written back with setRegister / setFlag.
  • Memory is pet.read/write; the hex view skips the $E8xx I/O window so auto-polling never trips a VIA side effect.
  • Single step is pet.cycle(1) (one instruction).
  • Breakpoints and watchpoints are host-side: the boot loop checks the PC against a set before each instruction, and wraps pet.write to catch a watched address.

Architecture

The PET 2001 is Commodore's first computer — a 6502, a discrete-logic video circuit driving a 40×25 character display, two 6520 PIAs and a 6522 VIA for I/O, and Commodore BASIC in ROM. masswerk's emulation keeps each as its own module:

  • cpu6502 — the 6502 core, cycle-stepped.
  • pet2001 — the machine: RAM, ROM banks and the bus routing that read/write follow.
  • pet2001video — the character-ROM video, rastered to the canvas.
  • pet2001io — the PIAs/VIA and the keyboard matrix (setKeyrows).
  • pet2001ieee — the IEEE-488 bus, used to load .prg programs.
  • pet2001roms — the character, BASIC and KERNAL ROMs, embedded.

It boots straight to ### COMMODORE BASIC ### — a complete, inspectable machine with nothing to load.

Sound

The PET's sound is a single bit: the 6522 VIA's CB2 line, gated by the shift register and clocked by timer T2, drives a small piezo speaker — a square-wave "CB2 sound". This is a V-native integration: masswerk's core already ships a complete WebAudio pipeline in pet2001audio.js (its own AudioContext with a ScriptProcessorNode and a gain node). The I/O chip samples the CB2 level every CPU tick and pushes it to that pipeline via audio.writeSignal(); the pipeline resamples the 1 MHz signal stream down to the context's sample rate on demand. We do not reroute it through the shared sink — we simply enable it and gate it.

The embed had the core built with USE_AUDIO:false and never loaded the audio module; we load pet2001audio.js and build with USE_AUDIO:true. Because the pitch and waveform come from the emulated VIA (T2 period + shift-register value) and the module resamples against audioContext.sampleRate internally, no rate constant needs to be set — pitch is inherent in the hardware emulation.

The mute contract lives on EMU_BOOT.transport: isMuted() returns our flag and setMute(m) drives the native pipeline — mute calls audio.setVolume(0) + audio.suspend(), unmute calls audio.setVolume(1) (which reconnects the gain chain and resumes the context). It starts muted, since browsers block audio before a user gesture; the shared Sound button unmutes from a real click.

The PET boots to the READY. BASIC prompt and stays there; we do not force any startup noise. Sound is fully supported through the native CB2 pipeline, so anything that drives it (a BASIC program that POKEs the VIA shift register and T2, or any software that toggles CB2) is heard the moment it plays, once the Sound button unmutes.