c128
c128 is a Commodore 128 that runs in the browser and boots straight to the Commodore BASIC V7.0 READY screen. It is assembled for emulators.org from the pure-JavaScript c64js 8502/6502 core and VIC-IIe text renderer (Mikael Borgbrant, MIT) with a new MOS 8722 MMU that banks the BASIC 7.0 and KERNAL ROMs and the 128 KB two-bank RAM. Because the CPU, MMU and RAM are plain JavaScript, the whole machine can be paused, single-stepped and inspected live in the shared debugger.
The base c64js engine is on GitHub ↗
Runs on: Web browser
c128 Online Emulator
Play c128 using JavaScript directly in your browser.
Controls
Configurations
| Configuration | Emulator | Machine | OS | Legal | |
|---|---|---|---|---|---|
| C128 BASIC 7.0 | c128 | Commodore 128 | grey | Open ⛶ |
Machines emulated
Chips
Notes
Embedding
There is no drop-in pure-JavaScript Commodore 128, so this machine is assembled from parts: the readable c64js 6510 core (the 8502 is a 6502 with extra I/O-port pins, so the same interpreter runs C128 code unchanged), the c64js VIC-II text renderer for the 40-column screen, the two CIA 6526s for the keyboard matrix and the 60 Hz system interrupt, and a new c128-mmu.js that models the MOS 8722 MMU. Vendor src/** and load the scripts in dependency order, then drive the machine yourself instead of any built-in loop so the debugger can pause and single-step.
Boot. Install the four system ROMs, point the 8502 at the MMU as its bus, point the VIC-IIe at a <canvas>, reset, then run your own loop built on mos6510.process() (one 8502 instruction, returns its cycle count):
c128.mmu.installRoms(c128.roms.basiclo, c128.roms.basichi, c128.roms.kernal);
memoryManager.character = new Rom(0, c128.roms.character4k); // VIC char fetches
mos6510.init(c128.mmu); // bus = the MMU; reads reset vector $FFFC -> $FF3D
vic2.setScreenCanvas(canvas);
vic2.init(vicMemoryManager);
(function frame(){ // 19656 VIC sub-cycles per frame
for (var i = 0; i < 19656; i++) {
if (cpuCycles === 0) cpuCycles = mos6510.process();
cpuCycles--; vic2.process(i, 0);
}
memoryManager.cia1.process(19656); // CIA1 Timer A -> the system IRQ
requestAnimationFrame(frame);
})();
The 60 Hz system interrupt comes from CIA 1 Timer A, exactly as on the C64: the C128 hardware IRQ vector ($FF17) tail-calls the editor IRQ, which scans the keyboard and acknowledges the timer with LDA $DC0D. Feeding the timer each frame is what makes the cursor blink and the keyboard type.
Debugger integration
Because every part is plain JavaScript, the debugger reaches the whole machine with no wasm heap and no core patches:
| Member | Kind | What it does |
|---|---|---|
mos6510.process() | method | Execute exactly one 8502 instruction; returns its cycle count. The single-step primitive. |
mos6510.register | field | Live registers .a .x .y .pc .sp and .status (flags object with getStatusByte() / setStatusFlags(b)). |
c128.mmu.readByte(a) | method | Read the banked 8502 bus: RAM bank, BASIC LO/HI ROM, editor/KERNAL ROM or the I/O page, per the 8722 configuration register. |
c128.mmu.writeByte(a,v) | method | Write the bus (RAM under ROM stays in sync, like the real MMU). |
c128.mmu.getCR() | method | The current $FF00/$D500 configuration register, which banks are visible right now. |
memoryManager.ram.memory | field | Raw RAM bank 0 (screen $0400, colour $D800, zero page / stack in common RAM). |
c128.mmu.bank1 | field | Raw RAM bank 1 (the upper 64K). |
Breakpoints are a host-side Set the loop checks against mos6510.register.pc before each instruction; watchpoints wrap the bank-0 RAM write path and pause when a watched address is written. Single-step just calls mos6510.process() once. The disassembler and register/hex views are the shared mos6502 framework code, the 8502 decodes identically.
Architecture
The Commodore 128 is really three machines in one box; this build emulates the native C128 mode (8502 CPU, BASIC 7.0, 40-column VIC-IIe). The Z80 (CP/M mode) and the 8563 VDC 80-column screen are not rendered; the VDC is stubbed "ready" so the KERNAL's 80-column init falls through and the 40/80 key reads as 40 columns.
mos6510- the 6502/8502 CPU interpreter (c64js).process()runs one instruction.c128.mmu- the MOS 8722 MMU: the configuration register ($FF00/$D500), the load-config registers ($FF01-$FF04), 128K RAM in two banks with a common-RAM region, and ROM/I-O banking of $4000-$FFFF.vic2- the VIC-IIe, rastered into the canvas for the 40-column text screen (standard character mode).cia1/cia2- the two 6526s: keyboard matrix + Timer A system IRQ, and the VIC bank select.c128.roms- BASIC LO ($4000), BASIC HI ($8000), editor+KERNAL ($C000) and the character ROM, the stock Commodore images.
The three system ROMs plus the character ROM are Commodore firmware, self-hosted here and cited in README.txt; they are treated as grey and takedowns are honoured.