SearchA-ZG › Gigatron TTL

Gigatron TTL

2018 BSD 2-Clause Online

Gigatron TTL is a pure-JavaScript emulator by Philip Thomas of the Gigatron, a working 8-bit home computer that contains no microprocessor at all. Its processor is a state machine wired from about three dozen 74-series TTL chips. It runs the open BSD ROM in the browser, booting straight to the Snake / Racer / Mandelbrot / Loader menu, and is plugged into the emulators.org debugger so you can single-step the native TTL instruction stream, set breakpoints and watch RAM.

Visit the source repository ↗

Visit the official site ↗

Runs on: Browser

Gigatron TTL Online Emulator

Play Gigatron TTL using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Gigatron main menu (ROMv1)Gigatron TTLGigatron TTLopenOpen ⛶

Machines emulated

Chips

Notes

Embedding

The Gigatron core (PhilThomas/gigatron, BSD) is two dependency-free ES modules: gigatron.js (the TTL state machine) and vga.js (the raster). We ignore the upstream main.js - its setInterval loop can't be paused or stepped, and it drags in jQuery / rxjs - and drive the two modules from our own boot.js instead.

Boot. Construct the machine, point the VGA at a <canvas>, fetch the ROM as 16-bit big-endian words, then run your own loop built on cpu.tick() (one TTL cycle = one native instruction) and vga.tick() (one pixel-clock):

import { Gigatron } from './gigatron.js';
import { Vga } from './vga.js';
const cpu = new Gigatron({ hz: 6250000, romAddressWidth: 16, ramAddressWidth: 15 });
const vga = new Vga(canvas, cpu, { horizontal:{ backPorch:48, visible:640 }, vertical:{ backPorch:34, visible:480 } });
const dv = new DataView(romBuffer);
for (let i = 0; i < dv.byteLength >> 1; i++) cpu.rom[i] = dv.getUint16(2*i);  // big-endian
(function frame(){
  for (let i = 0; i < 104167; i++) { cpu.tick(); vga.tick(); }  // ~1 video frame of cycles
  requestAnimationFrame(frame);
})();

The machine is plain JavaScript. Every register is a live field on the cpu object and both memories are ordinary typed arrays, nothing to reach into a wasm heap for:

MemberKindWhat it is
cpu.tick()methodExecute exactly one native instruction (one TTL clock). The single-step primitive.
cpu.pc / cpu.nextpcfieldProgram counter and the pre-fetched next PC (both 16-bit ROM word addresses).
cpu.ac .x .y .out .outxfieldAccumulator, index X, index Y, and the output / extended-output latches.
cpu.inRegfieldThe controller input latch - active-low: a pressed button clears its bit.
cpu.romUint16Array64K of 16-bit instruction words (the whole "program"; there is no other code store).
cpu.ramUint8Array32K RAM: the video framebuffer, zero page, and vCPU state + bytecode.

Input is one byte: OR a button's bit into a mask and write cpu.inReg = mask ^ 0xff. Because the CPU, ROM and RAM are all plain JavaScript, the debugger single-steps by calling cpu.tick(), reads and writes registers straight off cpu, and implements breakpoints and watchpoints as host-side checks around the loop, the vendored core is untouched.

Debugger integration

Two custom pieces plug the Gigatron into the shared debugger; the core is left pristine.

1. A native-instruction disassembler (/debugger/src/cpus/gigatron.js, registered as decoder gigatron). Each ROM word is decoded into its four fields:

const op   = (w >> 13) & 7;   // 0 ld 1 anda 2 ora 3 xora 4 adda 5 suba 6 st 7 branch
const mode = (w >> 10) & 7;   // RAM addressing mode / write target / branch condition
const bus  = (w >>  8) & 3;   // 0 immediate  1 [ram]  2 ac  3 in
const d    =  w & 0xff;         // 8-bit immediate / address / branch offset

ROM is 16-bit-word memory, but the shared disasm view hands the decoder a byte reader. So - exactly like cpus/lmc.js - the decoder pulls the full word from the live core via window.EMU_BOOT.peekRom(addr), and each word is length 1 so PC lines up with the disassembly rows.

2. A machine plug-in (gigatron-debug.js) that polls for window.EMU_BOOT (boot.js is an ES module, so it resolves after the classic framework scripts) and calls EmuKit.defineMachine with the register set (AC / X / Y / OUT / OUTX / IN / PC / nextPC, all read and write-back), the ROM (disasm) and RAM (hex + bits) chips, and an on-screen gamepad.

Breakpoints and watchpoints without touching the core. The loop in boot.js exposes both on EMU_BOOT.transport. When either set is non-empty it drops from whole-frame execution to instruction-by-instruction: it pauses after the instruction whose new cpu.pc is a breakpoint. For a write watchpoint it pre-decodes the upcoming word; if it is a store (op == 6) it recomputes the exact RAM address that store will hit from the current X/Y (mirroring the core's addr(), read-only) and pauses when that address is watched. No write hook is patched into the vendored gigatron.js.

Architecture

The Gigatron TTL (Marcel van Kervinck & Walter Belgers, 2018) is a fully functional 8-bit computer with no microprocessor at all: its "CPU" is a Harvard state machine wired from about three dozen 74-series TTL chips. A ROM steps one instruction per clock; there is no microcode and no off-the-shelf processor.

  • Native instruction set - every 16-bit ROM word is one instruction: a 3-bit op, a 3-bit mode, a 2-bit bus select and an 8-bit data field. Eight operations: ld anda ora xora adda suba st and a family of page-local branches (plus jmp through Y). Registers are just AC, X, Y and the OUT latch.
  • vCPU - because the native machine has no call stack or 16-bit ops, the ROM hosts a tiny interpreted virtual CPU (16-bit, memory-to-memory) whose bytecode lives in RAM. Games and the menu are written in vCPU; the debugger's ROM disassembly shows the native interpreter that runs it.
  • Video - there is no video chip. Software in the ROM races the beam, driving 6 colour bits + the two sync bits straight out of the OUT port; vga.js reconstructs the 640×480 picture from those port transitions.
  • Sound & input - sound is likewise generated in software on the extended-output latch; input is a single active-low byte from a SNES-style controller (D-pad + A/B/Select/Start).
  • Memory - 64K×16 ROM (the program) and 32K RAM (framebuffer + zero page + vCPU state). This build runs ROMv1, the original "At the Loop" ROM, booting to the Snake / Racer / Mandelbrot / Loader menu.