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
| RANGE | WHAT |
|---|---|
| 0x0000–0x7FFF | ROM — your assembled program (32KB max) |
| 0x8000–0xAFFF | VRAM — one byte per pixel: addr = 0x8000 + y*128 + x, color 0–15 |
| 0xB000–0xEFFF | RAM — 16KB for your variables; the stack grows down from 0xEFFE |
| 0xF000+ | MMIO — the hardware registers below |
HARDWARE REGISTERS (MMIO)
| ADDR | NAME | BEHAVIOR |
|---|---|---|
| 0xF000 | INPUT | read — bit0 ▲ · bit1 ▼ · bit2 ◀ · bit3 ▶ · bit4 A · bit5 B · bit6 START |
| 0xF002 | RANDOM | read — a fresh random 16-bit value every read |
| 0xF004 | FRAME | read — frames elapsed since power-on |
| 0xF006 | TONE | write — play a square wave at that frequency (Hz) |
| 0xF008 | TONEDUR | write — tone length in ms (default 120) |
INSTRUCTION SET
| GROUP | INSTRUCTIONS |
|---|---|
| load / store | LDI rd, imm · MOV rd, rs · LD rd, [rs | addr] · ST [rs | addr], rd · LDB / STB byte variants |
| arithmetic | ADD SUB MUL AND OR XOR rd, rs · ADDI SUBI rd, imm · INC DEC NEG rd · SHL SHR rd, 0–15 |
| compare | CMP rd, rs · CMPI rd, imm — set Z (zero), N (negative), C (unsigned borrow) |
| branch | JMP JZ JNZ JC JNC JN JNN addr — aliases: JLT=JN · JGE=JNN · JB=JC · JAE=JNC |
| calls / stack | CALL addr · RET · PUSH rd · POP rd |
| machine | VSYNC — 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.