Search › A-Z › G › Bit Corporation Gamate

Bit Corporation Gamate

1990 Public domain Online

The Gamate (Bit Corporation, 1990) is an 8-bit handheld game console and one of the many Game Boy rivals of its era. This emulator is authored from scratch in JavaScript around Mike Chambers' public-domain Fake6502 NMOS 6502 core, wired to the Gamate bus, its custom 160x150 four-shade LCD controller, the AY-3-8910 sound chip and the joystick. It is plugged into the in-page debugger, where you can single-step the 6502, set breakpoints and watchpoints, and inspect the whole memory map.

Runs on: Web browser

Bit Corporation Gamate Online Emulator

Play Bit Corporation Gamate using JavaScript directly in your browser.

Configurations

ConfigurationEmulatorMachineOSLegal
Bars & Beeps (6502 demo)Bit Corporation GamateBit Corporation GamateopenOpen ⛶

Machines emulated

Chips

Notes

Embedding

The Gamate emulator is authored in JavaScript with no WebAssembly and no CDN. Three self-hosted scripts make the machine: the vendored public-domain Fake6502.js (Mike Chambers' NMOS 6502 interpreter), the shared ay8910.js PSG, and gamate-machine.js — the Gamate bus, LCD video controller, joystick latch and IRQ timer wired around that CPU. The boot script owns the loop, so the debugger can pause, single-step and breakpoint it.

Boot and load the ROMs. A 4 KB open BIOS holds the CPU vectors at $FFFA–$FFFF; on reset it initialises the stack and jumps to the cartridge at $6000:

var gm = new Gamate();
gm.loadBIOS(biosBytes);                 // $E000..$EFFF, mirrored to $FFFF
gm.loadCart(cartBytes);                 // $6000..$DFFF
gm.attachAudio(AY8910, EmuAudio.sampleRate);
gm.reset();                            // pulls the reset vector from $FFFC/$FFFD

The machine surface. Everything the loop and the debugger touch is a plain method on the machine object:

MethodWhat it does
step()Execute one 6502 instruction and service the IRQ timer (an IRQ every 16384 cycles). Returns the cycle count.
stepInsn()Execute exactly one instruction with no timer side effects — the debugger's step primitive.
renderFrame()Scan the two VRAM bitplanes into the 160×150 RGBA framebuffer, applying scroll / blank / plane-swap.
read(a) / write(a,v)The live CPU bus (drives the video auto-increment port and the AY registers).
peek(a)A side-effect-free bus read for the debugger — never advances the VRAM port or touches a latch.
getReg(n) / setReg(n,v)Read / write A, X, Y, SP, PC, P on the live CPU.
key(bit,down)Press / release a button on the active-low JOY latch (D-pad, A, B, Start, Select).

Debugger integration

Because the machine is authored in JS the debugger wiring is direct: the boot script owns the run-loop and publishes window.EMU_BOOT, and gamate-debug.js calls EmuKit.defineMachine with the live handles.

A host-owned loop. With no breakpoints or watchpoints the loop runs a whole frame of cycles at once; otherwise it steps one instruction at a time, comparing PC against the breakpoint set and re-checking watched addresses between instructions:

function stepFrame(){
  var budget = gm.cyclesPerFrame(), used = 0;
  while (used < budget) {
    if (bps.has(gm.getReg('PC'))) { running = false; return; }
    var before = snapshotWatched();
    used += gm.step();                             // one 6502 instruction + IRQ timer
    if (watchedChanged(before)) { running = false; return; }
  }
}
  • Side-effect-free reads. The hex and disassembly views read through gm.peek(), which returns RAM / ROM / register-file bytes directly and, crucially, does not advance the VRAM auto-increment port or the AY latch, so auto-polling a view never corrupts machine state.
  • Direct register state. Fake6502 exposes a/x/y/sp/pc/flags as live fields; getReg/setReg read and write them, and the status byte is surfaced as clickable N/V/B/D/I/Z/C flag chips.
  • Execution breakpoints are a JS Set of PC values checked before each instruction; write watchpoints snapshot the watched addresses before an instruction (via peek) and compare after, pausing on a change.
  • Reused decoder. The Gamate CPU is a plain NMOS 6502, so the shared debugger/src/cpus/mos6502.js disassembler is bound directly — no new decoder was needed.

Architecture

The Gamate (Bit Corporation, 1990) is an 8-bit handheld game console and a Game Boy rival of its era. It is built around a 6502 (the UMC UM6502, an NMOS 6502 clone) clocked at 4.433 MHz ÷ 2 ≈ 2.22 MHz, with a 160×150 LCD showing four shades of grey.

  • 6502 CPU — accumulator A, index registers X and Y, stack pointer S, 16-bit PC and the N/V/B/D/I/Z/C status flags.
  • Memory map — 1 KB system RAM at $0000 (mirrored to $1FFF), the AY PSG at $4000, the joystick latch at $4400, the LCD registers at $5000, the cartridge window at $6000–$DFFF, and the 4 KB BIOS at $E000 (mirrored into the vector page).
  • Video — a custom LCD controller with 16 KB of video RAM addressed through a windowed, auto-incrementing port: XPOS/YPOS latch a 13-bit word address, and a byte-pair (two bitplanes) at that word carries eight 2-bit pixels. LCDCON controls display-blank, plane-swap, the window mode and the auto-increment direction; XSCROLL/YSCROLL scroll the visible window.
  • Timer & interrupts — a hardware timer asserts the 6502 IRQ line about 2.25 times per frame (every 16384 CPU cycles).

The 6502 core is Mike Chambers' compact public-domain Fake6502, reused unchanged; the rest of the machine — bus, LCD controller, PSG glue, input and timer — is authored in gamate-machine.js, and the debugger reads it live each refresh.

Sound

Pattern S (register-driven synthesis) with the shared AY-3-8910. The Gamate's sound hardware is a General Instrument AY-3-8910 PSG mapped at $4000–$400F and clocked at 4.433 MHz ÷ 4 = 1.108 MHz. Rather than write a new chip, the machine instantiates the site's shared debugger/src/chips/ay8910.js at that clock.

How writes are captured. The board decodes a write to $4000 + r as “set AY register r”, so the bus write handler simply calls ay.writeReg(a & 0x0f, v). The three square-wave tone channels, the noise LFSR and the hardware envelope are all reproduced by the shared core from those register values.

Sample rate / pitch. The AY core is constructed with EmuAudio.sampleRate, so it advances its generators by (clock/8)/sampleRate internal ticks per output sample and the pitch is exact with no resampling. Each video frame the loop calls ay.renderStereo(Math.round(EmuAudio.sampleRate/60)) and pushes the interleaved-stereo Int16 block to the shared EmuAudio sink; a one-pole DC blocker in the core removes the unipolar DAC offset.

Mute contract. window.EMU_BOOT.transport exposes isMuted() / setMute(m) delegated straight to EmuAudio. Audio starts muted (browsers block audio before a gesture); the shell's Sound button resumes the AudioContext and unmutes from a real click. Sound is fully supported and plays whenever a cartridge programs the AY-3-8910; the page starts muted until the Sound button is clicked.