SearchA-ZV › VIC-20 Emulator (mwenge)

VIC-20 Emulator (mwenge)

2013 Unlicensed Online

A pure-JavaScript Commodore VIC-20 emulator (mwenge's docs/vic20 core, a Willem-Dawson-style 6502 machine). It emulates the 6502 CPU, the VIC-6560/6561 video-and-sound chip and the two 6522 VIAs, boots straight to Commodore BASIC 2.0, and takes keyboard input. On emulators.org it is wired into the shared in-browser debugger, so you can single-step the 6502, set breakpoints and inspect memory.

View the source on GitHub ↗

Visit the official site ↗

Runs on: Web browser

VIC-20 Emulator (mwenge) Online Emulator

Play VIC-20 Emulator (mwenge) using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
VIC-20 BASICVIC-20 Emulator (mwenge)Commodore VIC-20greyOpen ⛶

Machines emulated

Chips

Notes

Embedding

mwenge's VIC-20 is a set of plain-global JavaScript modules (no bundler). Vendor them and load them in dependency order, then drive the machine yourself instead of calling vic20.execute() - its internal setTimeout loop cannot be paused or stepped from outside, which the debugger needs.

Boot. Construct the machine (it installs the CHARGEN / BASIC / KERNAL ROMs into mem and points the VIC-6560 at the <canvas id="canvas">), then run your own loop built on vic20.cycle() (one machine clock: steps both VIAs, the CPU and the VIC):

window.vic20 = new Vic20();   // must be the global 'vic20' (chip callbacks read it)
vic20.isPal = false; kerneldata = kerneldataNtsc;
vic20.init();                    // builds CPU / VIAs / VIC, installs ROMs, resets
var CYCLES = 16965;             // ~1.02 MHz / 60 Hz
(function loop(){
  for (var i = 0; i < CYCLES; i++) vic20.cycle();
  requestAnimationFrame(loop);
})();

The machine is plain objects. Everything the debugger needs is a live global, with no wasm heap to reach into:

MemberKindWhat it does
vic20.cycle()methodRun one machine clock (VIAs + one CPU sub-cycle + VIC). The stepping primitive.
vic20.cpu.isNextInst()methodTrue when the next cycle() will fetch a fresh opcode, the instruction boundary used for single-step and PC breakpoints.
vic20.cpu.getPc()methodLive program counter (checked against the breakpoint set at each boundary).
vic20.cpu.getRegisters()methodLive registers {pc,sp,a,x,y,p}.
vic20.cpu.setA/setX/setY(hex)methodWrite A/X/Y (upstream, hex-string arg). We added numeric setPc/setSp/setP so the debugger can write PC/SP/P too.
vic20.invisibleRead(a) / invisibleWrite(a,v)methodSide-effect-free bus access, reads VIC/VIA without clearing latches, for a live hex view.
memfieldThe raw 64 KB memory array, under the ROM/I/O routing.
keysdownfieldThe 8-byte VIC-20 keyboard-matrix state the VIA scans; press = set a row,col bit.

Because the CPU, bus and RAM are ordinary JavaScript, the debugger single-steps by running cycle() to the next instruction boundary, reads and writes registers straight through the CPU's get/set methods, and implements breakpoints and watchpoints as host-side checks around the loop. The only change to the core is a three-line numeric setPc/setSp/setP patch.

Debugger integration

The plug-in (mwenge-vic20-debug.js) hands the shared framework the machine surface and nothing else:

  • Registers come from vic20.cpu.getRegisters() each refresh and are written back with setA/setX/setY (hex) and the added numeric setPc/setSp/setP; the status flags toggle bits of P.
  • Memory is exposed as five chips: the side-effect-free CPU bus (invisibleRead/invisibleWrite), the raw 64 KB RAM, and the BASIC / KERNAL / Character ROM windows.
  • Single step runs cycle() until cpu.isNextInst(), i.e. exactly one instruction.
  • Breakpoints and watchpoints are host-side: the boot loop checks the PC against a set at each instruction boundary, and wraps vic20.write to catch a watched address.

Architecture

The VIC-20 (1981) was Commodore's first colour home computer: a 6502, the VIC-6560/6561 video-and-sound chip, two 6522 VIAs for I/O, and Commodore BASIC 2.0 in ROM. mwenge's emulation keeps each as its own object, clocked in lockstep one cycle at a time:

  • Cpu6502 - the 6502 core, driven a sub-cycle at a time via a micro-op table; cycle() advances it.
  • Vic6560 - the VIC video/sound chip, rastered into the canvas (text and the standard multicolour modes).
  • Via6522 ×2, the timers, keyboard-matrix scan and joystick/tape lines.
  • mem - the flat 64 KB array; vic20.read/write route reads and writes to RAM, ROM or the VIC/VIA registers.
  • The CHARGEN, BASIC and KERNAL ROMs are embedded (packed) in carts.js and copied into mem on reset.

It boots straight to **** CBM BASIC V2 **** - a complete, inspectable machine with nothing to load.