EME-8

A fantasy console with a homemade CPU. Custom 16-bit instruction set · assembler · games written in its own assembly.
EME-8 16-BIT · 128×96 · 60FPS
CART RUN
START
X
Z
KEYBOARD — ARROWS · Z=A · X=B · ENTER=START

Three launch cartridges, each written in EME-8 assembly and assembled in your browser. Click one to slot it in. Then open CODE to read — or rewrite — its source.

THE MACHINE

EME-8 is a 16-bit fantasy console: eight general-purpose registers (R0–R7), a 64KB address space, a 128×96 16-color framebuffer you draw into by writing memory, and memory-mapped I/O for input, randomness and sound. Everything on this page — CPU, assembler, games — was built from scratch in vanilla JavaScript.

MEMORY MAP

RANGEWHAT
0x0000–0x7FFFROM — your assembled program (32KB max)
0x8000–0xAFFFVRAM — one byte per pixel: addr = 0x8000 + y*128 + x, color 0–15
0xB000–0xEFFFRAM — 16KB for your variables; the stack grows down from 0xEFFE
0xF000+MMIO — the hardware registers below

HARDWARE REGISTERS (MMIO)

ADDRNAMEBEHAVIOR
0xF000INPUTread — bit0 ▲ · bit1 ▼ · bit2 ◀ · bit3 ▶ · bit4 A · bit5 B · bit6 START
0xF002RANDOMread — a fresh random 16-bit value every read
0xF004FRAMEread — frames elapsed since power-on
0xF006TONEwrite — play a square wave at that frequency (Hz)
0xF008TONEDURwrite — tone length in ms (default 120)

INSTRUCTION SET

GROUPINSTRUCTIONS
load / storeLDI rd, imm · MOV rd, rs · LD rd, [rs | addr] · ST [rs | addr], rd · LDB / STB byte variants
arithmeticADD SUB MUL AND OR XOR rd, rs · ADDI SUBI rd, imm · INC DEC NEG rd · SHL SHR rd, 0–15
compareCMP rd, rs · CMPI rd, imm — set Z (zero), N (negative), C (unsigned borrow)
branchJMP JZ JNZ JC JNC JN JNN addr — aliases: JLT=JN · JGE=JNN · JB=JC · JAE=JNC
calls / stackCALL addr · RET · PUSH rd · POP rd
machineVSYNC — end the frame · HLT — stop · NOP

ASSEMBLER

Labels (name:), constants (NAME = 0xB000), data (.word, .byte, .fill n, v), expressions like table+2, character literals 'A', hex as 0x1F or $1F, comments with ;.

YOUR FIRST PROGRAM

; a pixel chases the d-pad — paste this into CODE and run it
X = 0xB000
Y = 0xB002

main:
  LDI R0, 64
  ST [X], R0
  LDI R0, 48
  ST [Y], R0
loop:
  LD R0, [0xF000]      ; read input
  LD R1, [X]
  LD R2, [Y]
  MOV R3, R0
  SHL R3, 15           ; bit0 (up) into the sign bit
  JNN chk_down
  DEC R2
chk_down:
  MOV R3, R0
  SHL R3, 14
  JNN chk_left
  INC R2
chk_left:
  MOV R3, R0
  SHL R3, 13
  JNN chk_right
  DEC R1
chk_right:
  MOV R3, R0
  SHL R3, 12
  JNN move_done
  INC R1
move_done:
  ST [X], R1
  ST [Y], R2
  MOV R4, R2           ; addr = 0x8000 + y*128 + x
  SHL R4, 7
  ADD R4, R1
  ADDI R4, 0x8000
  LDI R5, 6            ; amber
  STB [R4], R5
  VSYNC
  JMP loop

It leaves a trail, because nothing ever clears the screen — on this machine, you are the graphics driver.