shithub: riscv

Download patch

ref: abf8c8bf2cd27541d6a102f7c0385c3fe281f578
parent: 1195ca910c393e542d6aa23035fa75719af1107e
author: qwx <devnull@localhost>
date: Sat May 12 15:21:48 EDT 2018

add port of aiju's port of games/2600

diff: cannot open b/sys/src/games/2600//null: file does not exist: 'b/sys/src/games/2600//null'
--- /dev/null
+++ b/sys/man/1/atari
@@ -1,0 +1,35 @@
+.SH ATARI 1
+.SH NAME
+2600 \- emulator
+.SH SYNOPSIS
+.B games/2600
+[
+.B -a
+]
+.I romfile
+.SH DESCRIPTION
+.I 2600
+is an emulator for the Atari 2600.
+It exectues the romfile given as an argument,
+and controls as if using a regular 4-direction 1-button joystick,
+using \fBspace\fR button and directional keys.
+The \fBq\fR, \fBw\fR, \fBe\fR, \fBr\fR keys correspond respectively to the reset, select, player 1 difficulty and color mode switches.
+Other keys:
+.TP
+Esc
+Pause the emulator.
+.TP
+Del
+Exit the emulator.
+.PP
+Command line options:
+.TP
+.B -a
+Enable audio output.
+.SH SOURCE
+.B /sys/src/games/2600
+.SH BUGS
+Yes.
+.SH HISTORY
+.I 2600
+first appeared in 9front (November, 2014).
--- /dev/null
+++ b/sys/src/games/2600/2600.c
@@ -1,0 +1,96 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <keyboard.h>
+#include <emu.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int *rom, *rop;
+u16int bnk[8];
+int mask = 0xfff;
+
+void
+togdifc(void)
+{
+	p0difc ^= 1<<6;
+}
+
+void
+togbw(void)
+{
+	bwmod ^= 1<<3;
+}
+
+static void
+loadrom(char *name)
+{
+	int i, sz, fd;
+
+	fd = open(name, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	sz = seek(fd, 0, 2);
+	switch(sz){
+	case 0x800: mask = 0x7ff;
+	case 0x1000: break;
+	case 0x3000: bnk[6] = 2<<12;
+	case 0x2000: bnk[5] = 1<<12; break;
+	case 0x4000: for(i=1; i<4; bnk[i+2] = i<<12, i++); break;
+	case 0x8000: for(i=1; i<8; bnk[i] = i<<12, i++); break;
+	default: sysfatal("unsupported ROM size");
+	}
+	rom = malloc(sz);
+	if(rom == nil)
+		sysfatal("malloc: %r");
+	rop = rom;
+	pread(fd, rom, sz, 0);
+	close(fd);
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	ARGBEGIN {
+	case 'a':
+		initaudio();
+		break;
+	default:
+		goto usage;
+	} ARGEND;
+	if(argc != 1){
+	usage:
+		fprint(2, "usage: %s [ -23a ] rom\n", argv0);
+		exits("usage");
+	}
+	loadrom(argv[0]);
+	initemu(PICW, PICH, 4, XRGB32, 1, nil);
+	regkey("a", ' ', 1<<4);
+	regkey("start", 'q', 1<<5);
+	regkey("control", 'w', 1<<6);
+	regkey("up", Kup, 1<<0);
+	regkey("down", Kdown, 1<<1);
+	regkey("left", Kleft, 1<<2);
+	regkey("right", Kright, 1<<3);
+	regkeyfn('e', togdifc);
+	regkeyfn('r', togbw);
+
+	pc = memread(0xFFFC) | memread(0xFFFD) << 8;
+	rP = FLAGI;
+	for(;;){
+		if(paused){
+			qlock(&pauselock);
+			qunlock(&pauselock);
+		}
+		step();
+	}
+}
+
+void
+flush(void)
+{
+	flushmouse(1);
+	flushscreen();
+	flushaudio(audioout);
+}
--- /dev/null
+++ b/sys/src/games/2600/aud.c
@@ -1,0 +1,81 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+static int sdiv[2], fdiv[2], cdiv[2], ch[2], sr[2] = {-1,-1};
+static short sbuf[2000*2], *sbufp;
+static int audfd;
+
+#define div(n) if(++cdiv[i] < n) break; cdiv[i] = 0
+
+static void
+channel(int i)
+{
+	sdiv[i] += HZ/114;
+	for(; sdiv[i] >= RATE; sdiv[i] -= RATE)
+		if(fdiv[i] >= (reg[AUDF0 + i] & 31)){
+			fdiv[i] = 0;
+			switch(reg[AUDC0 + i] & 15){
+			case 0: ch[i] = 1; break;
+			case 2: div(15);
+			case 1: ch[i] = sr[i] & 1; sr[i] = sr[i] >> 1 & 7 | (sr[i] << 2 ^ sr[i] << 3) & 8; break;
+			case 4: case 5: ch[i] ^= 1; break;
+			case 12: case 13: div(3); ch[i] ^= 1; break;
+			case 6: case 10: div(16); ch[i] ^= 1; break;
+			case 14: div(46); ch[i] ^= 1; break;
+			case 15: div(3);
+			case 7: case 9: ch[i] = sr[i] & 1; sr[i] = sr[i] >> 1 & 15 | (sr[i] << 2 ^ sr[i] << 4) & 16; break;
+			case 8: ch[i] = sr[i] & 1; sr[i] = sr[i] >> 1 & 255 | (sr[i] << 4 ^ sr[i] << 8) & 256; break;
+			case 3:
+				ch[i] = sr[i] & 1;
+				sr[i] = sr[i] & 15 | sr[i] >> 1 & 240 | (sr[i] << 2 ^ sr[i] << 3) & 256;
+				if((sr[i] & 256) != 0)
+					sr[i] = sr[i] & 496 | sr[i] >> 1 & 7 | (sr[i] << 2 ^ sr[i]) << 3 & 8;
+				break;
+			}
+		}else
+			fdiv[i]++;
+}
+
+void
+sample(void)
+{
+	int d;
+
+	if(sbufp == nil)
+		return;
+	channel(0);
+	channel(1);
+	d = ch[0] * (reg[AUDV0] & 15) + ch[1] * (reg[AUDV1] & 15);
+	if(sbufp < sbuf + nelem(sbuf) - 1){
+		*sbufp++ = d * 1000;
+		*sbufp++ = d * 1000;
+	}
+}
+
+int
+audioout(void)
+{
+	int rc;
+
+	if(sbufp == nil)
+		return -1;
+	if(sbufp == sbuf)
+		return 0;
+	rc = write(audfd, sbuf, (sbufp - sbuf) * 2);
+	if(rc > 0)
+		sbufp -= (rc+1)/2;
+	if(sbufp < sbuf)
+		sbufp = sbuf;
+	return 0;
+}
+
+void
+initaudio(void)
+{
+	audfd = open("/dev/audio", OWRITE);
+	if(audfd < 0)
+		return;
+	sbufp = sbuf;
+}
--- /dev/null
+++ b/sys/src/games/2600/cpu.c
@@ -1,0 +1,501 @@
+#include <u.h>
+#include <libc.h>
+#include <emu.h>
+#include "dat.h"
+#include "fns.h"
+
+u16int pc, curpc;
+u8int rA, rX, rY, rS, rP;
+int nrdy;
+
+static u8int
+fetch8(void)
+{
+	return memread(pc++);
+}
+
+static u16int
+fetch16(void)
+{
+	u16int r;
+	
+	r = memread(pc++);
+	r |= memread(pc++) << 8;
+	return r;
+}
+
+static void
+push8(u8int v)
+{
+	memwrite(0x100 | rS--, v);
+}
+
+static void
+push16(u16int v)
+{
+	memwrite(0x100 | rS--, v >> 8);
+	memwrite(0x100 | rS--, v);
+}
+
+static u8int
+pop8(void)
+{
+	return memread(0x100 | ++rS);
+}
+
+static u16int
+pop16(void)
+{
+	u16int v;
+	
+	v = memread(0x100 | ++rS);
+	v |= memread(0x100 | ++rS) << 8;
+	return v;
+}
+
+#define imm() fetch8()
+#define zp() memread(fetch8())
+#define zpX() memread(azpX(rX))
+#define zpY() memread(azpX(rY))
+#define abso() memread(fetch16())
+#define absX() memread(aabsX(rX, 0))
+#define absY() memread(aabsX(rY, 0))
+#define indX() memread(aindX())
+#define indY() memread(aindY(0))
+
+static u16int
+azpX(u8int a)
+{
+	u8int v;
+	
+	v = fetch8();
+	memread(v);
+	return v + a;
+}
+
+static u16int
+aabsX(u8int a, int wr)
+{
+	u16int v, c;
+	
+	v = fetch16();
+	c = (u8int)v + a & 0x100;
+	v += a;
+	if(c != 0 || wr)
+		memread(v - c);
+	return v;
+}
+
+static u16int
+aindX(void)
+{
+	u8int r;
+	u16int a;
+	
+	r = fetch8();
+	memread(r);
+	r += rX;
+	a = memread(r++);
+	a |= memread(r) << 8;
+	return a;
+}
+
+static u16int
+aindY(int wr)
+{
+	u8int r;
+	u16int a, c;
+	
+	r = fetch8();
+	a = memread(r++) + rY;
+	c = a & 0x100;
+	a += memread(r) << 8;
+	if(c != 0 || wr)
+		memread(a - c);
+	return a;
+}
+
+static void
+adc(u8int d)
+{
+	int r;
+	
+	if((rP & FLAGD) != 0){
+		r = (rA & 0xf) + (d & 0xf) + (rP & FLAGC);
+		if(r > 0x09)
+			r += 0x06;
+		if(r > 0x1f)
+			r -= 0x10;
+		r += (rA & 0xf0) + (d & 0xf0);
+	}else
+		r = rA + d + (rP & FLAGC);
+	rP &= ~(FLAGN | FLAGZ | FLAGV | FLAGC);
+	if((~(rA ^ d) & (rA ^ r)) & 0x80) rP |= FLAGV;
+	if((rP & FLAGD) != 0 && r > 0x9f)
+		r += 0x60;
+	if(r > 0xFF) rP |= FLAGC;
+	if(r & 0x80) rP |= FLAGN;
+	rA = r;
+	if(rA == 0) rP |= FLAGZ;
+}
+
+static u8int
+nz(u8int d)
+{
+	rP &= ~(FLAGN | FLAGZ);
+	if(d & 0x80) rP |= FLAGN;
+	if(d == 0) rP |= FLAGZ;
+	return d;
+}
+
+static void
+asl(u16int a)
+{
+	u8int v;
+
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	v = memread(a);
+	memwrite(a, v);
+	if(v & 0x80) rP |= FLAGC;
+	v <<= 1;
+	if(v == 0) rP |= FLAGZ;
+	if(v & 0x80) rP |= FLAGN;
+	memwrite(a, v);
+}
+
+static void
+lsr(u16int a)
+{
+	u8int v;
+
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	v = memread(a);
+	memwrite(a, v);
+	rP |= v & 1;
+	v >>= 1;
+	if(v == 0) rP |= FLAGZ;
+	if(v & 0x80) rP |= FLAGN;
+	memwrite(a, v);
+}
+
+static void
+branch(void)
+{
+	s8int t;
+	u16int npc;
+	
+	t = fetch8();
+	memread(pc);
+	npc = pc + t;
+	if((npc ^ pc) >> 8)
+		memread(pc & 0xff00 | npc & 0xff);
+	pc = npc;
+}
+
+static void
+cmp(u8int a, u8int d)
+{
+	rP &= ~(FLAGN | FLAGZ | FLAGC);
+	if(a == d) rP |= FLAGZ;
+	if(a >= d) rP |= FLAGC;
+	if((a - d) & 0x80) rP |= FLAGN;
+}
+
+static void
+dec(u16int a)
+{
+	u8int v;
+
+	v = memread(a);
+	memwrite(a, v);
+	memwrite(a, nz(v - 1));
+}
+
+static void
+inc(u16int a)
+{
+	u8int v;
+
+	v = memread(a);
+	memwrite(a, v);
+	v = nz(v + 1);
+	memwrite(a, v);
+}
+
+static void
+rol(u16int a)
+{
+	u8int v, b;
+	
+	v = memread(a);
+	memwrite(a, v);
+	b = rP & FLAGC;
+	rP &= ~(FLAGC | FLAGN | FLAGZ);
+	if(v & 0x80) rP |= FLAGC;
+	v = (v << 1) | b;
+	if(v & 0x80) rP |= FLAGN;
+	if(v == 0) rP |= FLAGZ;
+	memwrite(a, v);
+}
+
+static void
+ror(u16int a)
+{
+	u8int v, b;
+	
+	v = memread(a);
+	memwrite(a, v);
+	b = rP & FLAGC;
+	rP &= ~(FLAGC | FLAGN | FLAGZ);
+	rP |= v & 1;
+	v = (v >> 1) | (b << 7);
+	if(v & 0x80) rP |= FLAGN;
+	if(v == 0) rP |= FLAGZ;
+	memwrite(a, v);
+}
+
+static void
+sbc(u8int d)
+{
+	int r;
+	
+	if((rP & FLAGD) != 0){
+		d = ~d;
+		r = (rA & 0xf) + (d & 0xf) + (rP & FLAGC);
+		if(r < 0x10) r -= 0x06;
+		if(r < 0) r += 0x10;
+		r += (rA & 0xf0) + (d & 0xf0);
+	}else
+		r = rA + (u8int)~d + (rP & FLAGC);
+	rP &= ~(FLAGZ | FLAGV | FLAGC | FLAGN);
+	if(((rA ^ d) & (rA ^ r)) & 0x80) rP |= FLAGV;
+	if(r > 0xFF) rP |= FLAGC;
+	else if((rP & FLAGD) != 0)
+		r -= 0x60;
+	rA = r;
+	if(rA == 0) rP |= FLAGZ;
+	if(rA & 0x80) rP |= FLAGN;
+}
+
+static void
+interrupt(int nmi, int brk)
+{
+	fetch8();
+	push16(pc);
+	push8(rP | 0x20 | (brk << 4));
+	pc = memread(0xFFFA | (!nmi << 2));
+	pc |= memread(0xFFFB | (!nmi << 2)) << 8;
+	rP |= FLAGI;
+}
+
+void
+step(void)
+{
+	u8int op;
+	u16int a, v;
+
+	if(nrdy){
+		io();
+		return;
+	}
+	curpc = pc;
+	op = fetch8();
+	if(trace)
+		print("%.4x %.2x | %.2x %.2x %.2x | %.2x %.2x | %3d %3d\n", curpc, op, rA, rX, rY, rS, rP, ppux-3, ppuy);
+	switch(op){
+	case 0x00: fetch8(); interrupt(0, 1); return;
+	case 0x01: nz(rA |= indX()); return;
+	case 0x05: nz(rA |= zp()); return;
+	case 0x06: asl(fetch8()); return;
+	case 0x08: memread(pc); push8(rP | 0x30); return;
+	case 0x09: nz(rA |= imm()); return;
+	case 0x0A:
+		rP &= ~(FLAGN | FLAGZ | FLAGC);
+		if(rA & 0x80) rP |= FLAGC;
+		rA <<= 1;
+		if(rA == 0) rP |= FLAGZ;
+		if(rA & 0x80) rP |= FLAGN;
+		memread(pc);
+		return;
+	case 0x0D: nz(rA |= abso()); return;
+	case 0x0E: asl(fetch16()); return;
+	case 0x10: if((rP & FLAGN) == 0) branch(); else fetch8(); return;
+	case 0x11: nz(rA |= indY()); return;
+	case 0x15: nz(rA |= zpX()); return;
+	case 0x16: asl(azpX(rX)); return;
+	case 0x18: rP &= ~FLAGC; memread(pc); return;
+	case 0x19: nz(rA |= absY()); return;
+	case 0x1D: nz(rA |= absX()); return;
+	case 0x1E: asl(aabsX(rX, 1)); return;
+	case 0x20: v = fetch8(); memread(rS|0x100); push16(pc); pc = fetch8() << 8 | v; return;
+	case 0x21: nz(rA &= indX()); return;
+	case 0x24:
+		a = memread(fetch8());
+		rP &= ~(FLAGN | FLAGZ | FLAGV);
+		rP |= a & 0xC0;
+		if((a & rA) == 0) rP |= FLAGZ;
+		return;
+	case 0x25: nz(rA &= zp()); return;
+	case 0x26: rol(fetch8()); return;
+	case 0x28: memread(pc); memread(0x100|rS); rP = pop8() & 0xcf; return;
+	case 0x29: nz(rA &= imm()); return;
+	case 0x2A:
+		a = rP & FLAGC;
+		rP &= ~(FLAGC | FLAGZ | FLAGN);
+		if(rA & 0x80) rP |= FLAGC;
+		rA = (rA << 1) | a;
+		if(rA & 0x80) rP |= FLAGN;
+		if(rA == 0) rP |= FLAGZ;
+		memread(pc);
+		return;
+	case 0x2C:
+		a = memread(fetch16());
+		rP &= ~(FLAGN | FLAGZ | FLAGV);
+		rP |= a & 0xC0;
+		if((a & rA) == 0) rP |= FLAGZ;
+		return;
+	case 0x2D: nz(rA &= abso()); return;
+	case 0x2E: rol(fetch16()); return;
+	case 0x30: if((rP & FLAGN) != 0) branch(); else fetch8(); return;
+	case 0x31: nz(rA &= indY()); return;
+	case 0x35: nz(rA &= zpX()); return;
+	case 0x36: rol(azpX(rX)); return;
+	case 0x38: rP |= FLAGC; memread(pc); return;
+	case 0x39: nz(rA &= absY()); return;
+	case 0x3E: rol(aabsX(rX, 1)); return;
+	case 0x3D: nz(rA &= absX()); return;
+	case 0x40: fetch8(); memread(rS|0x100); rP = pop8() & 0xcf; pc = pop16(); return;
+	case 0x41: nz(rA ^= indX()); return;
+	case 0x45: nz(rA ^= zp()); return;
+	case 0x46: lsr(fetch8()); return;
+	case 0x48: memread(pc); push8(rA); return;
+	case 0x49: nz(rA ^= imm()); return;
+	case 0x4A:
+		rP &= ~(FLAGN | FLAGZ | FLAGC);
+		rP |= rA & 1;
+		rA >>= 1;
+		if(rA == 0) rP |= FLAGZ;
+		if(rA & 0x80) rP |= FLAGN;
+		memread(pc);
+		return;
+	case 0x4C: pc = fetch16(); return;
+	case 0x4D: nz(rA ^= abso()); return;
+	case 0x4E: lsr(fetch16()); return;
+	case 0x51: nz(rA ^= indY()); return;
+	case 0x56: lsr(azpX(rX)); return;
+	case 0x58: rP &= ~FLAGI; memread(pc); return;
+	case 0x50: if((rP & FLAGV) == 0) branch(); else fetch8(); return;
+	case 0x55: nz(rA ^= zpX()); return;
+	case 0x59: nz(rA ^= absY()); return;
+	case 0x5D: nz(rA ^= absX()); return;
+	case 0x5E: lsr(aabsX(rX, 1)); return;
+	case 0x60: fetch8(); memread(rS | 0x100); pc = pop16(); fetch8(); return;
+	case 0x61: adc(indX()); return;
+	case 0x65: adc(zp()); return;
+	case 0x66: ror(fetch8()); return;
+	case 0x68: memread(pc); memread(0x100|rS); nz(rA = pop8()); return;
+	case 0x69: adc(imm()); return;
+	case 0x6A:
+		a = rP & FLAGC;
+		rP &= ~(FLAGC | FLAGN | FLAGZ);
+		rP |= rA & 1;
+		rA = (rA >> 1) | (a << 7);
+		if(rA & 0x80) rP |= FLAGN;
+		if(rA == 0) rP |= FLAGZ;
+		memread(pc);
+		return;
+	case 0x6C: v = fetch16(); pc = memread(v) | (memread((v & 0xFF00) | (u8int)(v+1)) << 8); return;
+	case 0x6D: adc(abso()); return;
+	case 0x6E: ror(fetch16()); return;
+	case 0x70: if((rP & FLAGV) != 0) branch(); else fetch8(); return;
+	case 0x71: adc(indY()); return;
+	case 0x75: adc(zpX()); return;
+	case 0x76: ror(azpX(rX)); return;
+	case 0x78: rP |= FLAGI; memread(pc); return;
+	case 0x79: adc(absY()); return;
+	case 0x7D: adc(absX()); return;
+	case 0x7E: ror(aabsX(rX, 1)); return;
+	case 0x81: memwrite(aindX(), rA); return;
+	case 0x84: memwrite(fetch8(), rY); return;
+	case 0x85: memwrite(fetch8(), rA); return;
+	case 0x86: memwrite(fetch8(), rX); return;
+	case 0x88: nz(--rY); memread(pc); return;
+	case 0x8A: nz(rA = rX); memread(pc); return;
+	case 0x8C: memwrite(fetch16(), rY); return;
+	case 0x8D: memwrite(fetch16(), rA); return;
+	case 0x8E: memwrite(fetch16(), rX); return;
+	case 0x90: if((rP & FLAGC) == 0) branch(); else fetch8(); return;
+	case 0x91: memwrite(aindY(1), rA); return;
+	case 0x94: memwrite(azpX(rX), rY); return;
+	case 0x95: memwrite(azpX(rX), rA); return;
+	case 0x96: memwrite(azpX(rY), rX); return;
+	case 0x98: nz(rA = rY); memread(pc); return;
+	case 0x99: memwrite(aabsX(rY, 1), rA); return;
+	case 0x9A: rS = rX; memread(pc); return;
+	case 0x9D: memwrite(aabsX(rX, 1), rA); return;
+	case 0xA0: nz(rY = imm()); return;
+	case 0xA1: nz(rA = indX()); return;
+	case 0xA2: nz(rX = imm()); return;
+	case 0xA4: nz(rY = zp()); return;
+	case 0xA5: nz(rA = zp()); return;
+	case 0xA6: nz(rX = zp()); return;
+	case 0xA8: nz(rY = rA); memread(pc); return;
+	case 0xA9: nz(rA = imm()); return;
+	case 0xAA: nz(rX = rA); memread(pc); return;
+	case 0xAC: nz(rY = abso()); return;
+	case 0xAE: nz(rX = abso()); return;
+	case 0xAD: nz(rA = abso()); return;
+	case 0xB0: if((rP & FLAGC) != 0) branch(); else fetch8(); return;
+	case 0xB1: nz(rA = indY()); return;
+	case 0xB4: nz(rY = zpX()); return;
+	case 0xB5: nz(rA = zpX()); return;
+	case 0xB6: nz(rX = zpY()); return;
+	case 0xB8: rP &= ~FLAGV; memread(pc); return;
+	case 0xB9: nz(rA = absY()); return;
+	case 0xBA: nz(rX = rS); memread(pc); return;
+	case 0xBC: nz(rY = absX()); return;
+	case 0xBD: nz(rA = absX()); return;
+	case 0xBE: nz(rX = absY()); return;
+	case 0xC1: cmp(rA, indX()); return;
+	case 0xC5: cmp(rA, zp()); return;
+	case 0xC9: cmp(rA, imm()); return;
+	case 0xCD: cmp(rA, abso()); return;
+	case 0xD0: if((rP & FLAGZ) == 0) branch(); else fetch8(); return;
+	case 0xD1: cmp(rA, indY()); return;
+	case 0xD5: cmp(rA, zpX()); return;
+	case 0xD8: rP &= ~FLAGD; memread(pc); return;
+	case 0xD9: cmp(rA, absY()); return;
+	case 0xDD: cmp(rA, absX()); return;
+	case 0xC0: cmp(rY, imm()); return;
+	case 0xC4: cmp(rY, zp()); return;
+	case 0xC6: dec(fetch8()); return;
+	case 0xC8: nz(++rY); memread(pc); return;
+	case 0xCA: nz(--rX); memread(pc); return;
+	case 0xCC: cmp(rY, abso()); return;
+	case 0xCE: dec(fetch16()); return;
+	case 0xD6: dec(azpX(rX)); return;
+	case 0xDE: dec(aabsX(rX, 1)); return;
+	case 0xE0: cmp(rX, imm()); return;
+	case 0xE1: sbc(indX()); return;
+	case 0xE4: cmp(rX, zp()); return;
+	case 0xE5: sbc(zp()); return;
+	case 0xE6: inc(fetch8()); return;
+	case 0xE8: nz(++rX); memread(pc); return;
+	case 0xE9: sbc(imm()); return;
+	case 0xEA: memread(pc); return;
+	case 0xEC: cmp(rX, abso()); return;
+	case 0xED: sbc(abso()); return;
+	case 0xEE: inc(fetch16()); return;
+	case 0xF0: if((rP & FLAGZ) != 0) branch(); else fetch8(); return;
+	case 0xF1: sbc(indY()); return;
+	case 0xF5: sbc(zpX()); return;
+	case 0xF6: inc(azpX(rX)); return;
+	case 0xF8: rP |= FLAGD; memread(pc); return;
+	case 0xF9: sbc(absY()); return;
+	case 0xFD: sbc(absX()); return;
+	case 0xFE: inc(aabsX(rX, 1)); return;
+	default:
+		fprint(2, "undefined %#x (pc %#x)\n", op, curpc);
+		return;
+	}
+}
--- /dev/null
+++ b/sys/src/games/2600/dat.h
@@ -1,0 +1,80 @@
+extern u16int pc, curpc;
+extern u8int rP;
+extern int nrdy;
+extern int p0difc;
+extern int bwmod;
+
+extern int ppux, ppuy;
+extern u8int p0x, p1x, m0x, m1x, blx;
+extern u16int coll;
+
+extern u8int *rom, *rop;
+extern u16int bnk[];
+extern int mask;
+extern u8int reg[64];
+
+enum {
+	FLAGC = 1<<0,
+	FLAGZ = 1<<1,
+	FLAGI = 1<<2,
+	FLAGD = 1<<3,
+	FLAGB = 1<<4,
+	FLAGV = 1<<6,
+	FLAGN = 1<<7,
+};
+
+enum {
+	VSYNC,
+	VBLANK,
+	WSYNC,
+	RSYNC,
+	NUSIZ0,
+	NUSIZ1,
+	COLUP0,
+	COLUP1,
+	COLUPF,
+	COLUBK,
+	CTRLPF,
+	REFP0,
+	REFP1,
+	PF0,
+	PF1,
+	PF2,
+	RESP0,
+	RESP1,
+	RESM0,
+	RESM1,
+	RESBL,
+	AUDC0,
+	AUDC1,
+	AUDF0,
+	AUDF1,
+	AUDV0,
+	AUDV1,
+	GRP0,
+	GRP1,
+	ENAM0,
+	ENAM1,
+	ENABL,
+	HMP0,
+	HMP1,
+	HMM0,
+	HMM1,
+	HMBL,
+	VDELP0,
+	VDELP1,
+	VDELBL,
+	RESMP0,
+	RESMP1,
+	HMOVE,
+	HMCLR,
+	CXCLR,
+};
+
+enum {
+	PICW = 320,
+	PICH = 222,
+	HZ = 3579545,
+	RATE = 44100,
+	SAMPDIV = HZ / 3 / RATE,
+};
--- /dev/null
+++ b/sys/src/games/2600/fns.h
@@ -1,0 +1,9 @@
+u8int memread(u16int);
+void memwrite(u16int, u8int);
+void step(void);
+void tiastep(void);
+void flush(void);
+void io(void);
+void initaudio(void);
+void sample(void);
+int audioout(void);
--- /dev/null
+++ b/sys/src/games/2600/mem.c
@@ -1,0 +1,195 @@
+#include <u.h>
+#include <libc.h>
+#include <emu.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int ram[128], reg[64];
+static u8int timer, timerun, timerspeed;
+static u16int timerpre;
+static u8int grp0d, grp1d, enabld;
+
+static u8int
+tiaread(u8int a)
+{
+	if(a < 8)
+		return coll >> (a << 1 & 14) << 6;
+	if(a == 0xc)
+		return ~keys << 3 & 0x80;
+	return 0x80;
+}
+
+static void
+tiawrite(u8int a, u8int v)
+{
+	switch(a){
+	case VSYNC:
+		if((v & 2) != 0)
+			flush();
+		return;
+	case VBLANK:
+		if((v & 2) == 0)
+			ppuy = 0;
+		break;
+	case WSYNC: nrdy = 1; break;
+	case RESP0: p0x = ppux >= 160 ? 3 : ppux+5; break;
+	case RESP1: p1x = ppux >= 160 ? 3 : ppux+5; break;
+	case RESM0: m0x = ppux >= 160 ? 2 : ppux+4; break;
+	case RESM1: m1x = ppux >= 160 ? 2 : ppux+4; break;
+	case RESBL: blx = ppux >= 160 ? 2 : ppux+4; break;
+	case HMOVE:
+		p0x = (p0x - ((s8int) reg[HMP0] >> 4)) % 160;
+		p1x = (p1x - ((s8int) reg[HMP1] >> 4)) % 160;
+		m0x = (m0x - ((s8int) reg[HMM0] >> 4)) % 160;
+		m1x = (m1x - ((s8int) reg[HMM1] >> 4)) % 160;
+		blx = (blx - ((s8int) reg[HMBL] >> 4)) % 160;
+		break;
+	case HMCLR: reg[HMP0] = reg[HMP1] = reg[HMM0] = reg[HMM1] = reg[HMBL] = 0; break;
+	case VDELP0:
+		if((v & 1) == 0)
+			reg[GRP0] = grp0d;
+		break;
+	case VDELP1:
+		if((v & 1) == 0)
+			reg[GRP1] = grp1d;
+		break;
+	case VDELBL:
+		if((v & 1) == 0)
+			reg[ENABL] = enabld;
+		break;
+	case GRP0:
+		if((reg[VDELP1] & 1) != 0)
+			reg[GRP1] = grp1d;
+		if((reg[VDELP0] & 1) != 0){
+			grp0d = v;
+			return;
+		}
+		break;
+	case GRP1:
+		if((reg[VDELP0] & 1) != 0)
+			reg[GRP0] = grp0d;
+		if((reg[VDELBL] & 1) != 0)
+			reg[ENABL] = enabld;
+		if((reg[VDELP1] & 1) != 0){
+			grp1d = v;
+			return;
+		}
+		break;
+	case ENABL:
+		if((reg[VDELBL] & 1) != 0){
+			enabld = v;
+			return;
+		}
+		break;
+	case CXCLR:
+		coll = 0;
+		break;
+	}
+	reg[a] = v;
+}
+
+static u8int
+ioread(u8int a)
+{
+	u8int v;
+
+	switch(a){
+	case 0:
+		return ~(keys << 4);
+	case 2:
+		return keys >> 5 ^ 3 | bwmod | p0difc;
+	case 4:
+		timerspeed = 0;
+		return timer;
+	case 5:
+		v = timerun;
+		timerun &= ~(1<<6);
+		return v;
+	}
+	return 0;
+}
+
+static void
+iowrite(u8int a, u8int v)
+{
+	switch(a){
+	case 4:
+		timerpre = 1;
+		goto timer;
+	case 5:
+		timerpre = 8;
+		goto timer;
+	case 6:
+		timerpre = 64;
+		goto timer;
+	case 7:
+		timerpre = 1024;
+	timer:
+		timerun &= ~(1<<7);
+		timerspeed = v == 0;
+		timer = v - 1;
+		break;
+	}
+}
+
+u8int
+memread(u16int a)
+{
+	u8int v;
+
+	if((a & 0x1000) != 0)
+		v = rop[a & mask];
+	else if((a & 1<<7) == 0)
+		v = tiaread(a & 0xf);
+	else if((a & 1<<9) == 0)
+		v = ram[a & 0x7f];
+	else
+		v = ioread(a & 7);
+	if(a > 0xfff3 && a < 0xfffc)
+		rop = rom + bnk[a - 0xfff4];
+	io();
+	return v;
+}
+
+void
+memwrite(u16int a, u8int v)
+{
+	if((a & 0x1000) != 0){
+		;}
+	else if((a & 1<<7) == 0)
+		tiawrite(a & 0x3f, v);
+	else if((a & 1<<9) == 0)
+		ram[a & 0x7f] = v;
+	else
+		iowrite(a & 7, v);
+	if(a > 0xfff3 && a < 0xfffc)
+		rop = rom + bnk[a - 0xfff4];
+	io();
+}
+
+static void
+timerstep(void)
+{
+	static int cl;
+	
+	cl++;
+	if((timerspeed || (cl & timerpre - 1) == 0) && timer-- == 0){
+		timerspeed = 1;
+		timerun |= 3<<6;
+	}
+}
+
+void
+io(void)
+{
+	static int snddiv;
+
+	timerstep();
+	tiastep();
+	tiastep();
+	tiastep();
+	if(++snddiv == SAMPDIV){
+		snddiv = 0;
+		sample();
+	}
+}
--- /dev/null
+++ b/sys/src/games/2600/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=2600
+OFILES=\
+	2600.$O\
+	aud.$O\
+	cpu.$O\
+	mem.$O\
+	tia.$O\
+
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/2600/tia.c
@@ -1,0 +1,212 @@
+#include <u.h>
+#include <libc.h>
+#include <emu.h>
+#include "dat.h"
+#include "fns.h"
+
+int ppux=1, ppuy;
+int col, pri;
+u8int p0x, p1x, m0x, m1x, blx;
+u16int coll;
+u8int disp;
+int p0difc;
+int bwmod = 1<<3;
+
+enum {
+	SRCPF,
+	SRCP0,
+	SRCP1,
+	SRCM0,
+	SRCM1,
+	SRCBL,
+};
+
+static void
+pixeldraw(u8int v)
+{
+	u32int c;
+	union { u32int l; u8int c[4]; } u;
+	u32int *p;
+	static u32int col[] = {
+		0x000000, 0x404040, 0x6C6C6C, 0x909090, 0xB0B0B0, 0xC8C8C8, 0xDCDCDC, 0xECECEC, 
+		0x444400, 0x646410, 0x848424, 0xA0A034, 0xB8B840, 0xD0D050, 0xE8E85C, 0xFCFC68, 
+		0x702800, 0x844414, 0x985C28, 0xAC783C, 0xBC8C4C, 0xCCA05C, 0xDCB468, 0xECC878, 
+		0x841800, 0x983418, 0xAC5030, 0xC06848, 0xD0805C, 0xE09470, 0xECA880, 0xFCBC94, 
+		0x880000, 0x9C2020, 0xB03C3C, 0xC05858, 0xD07070, 0xE08888, 0xECA0A0, 0xFCB4B4, 
+		0x78005C, 0x8C2074, 0xA03C88, 0xB0589C, 0xC070B0, 0xD084C0, 0xDC9CD0, 0xECB0E0, 
+		0x480078, 0x602090, 0x783CA4, 0x8C58B8, 0xA070CC, 0xB484DC, 0xC49CEC, 0xD4B0FC, 
+		0x140084, 0x302098, 0x4C3CAC, 0x6858C0, 0x7C70D0, 0x9488E0, 0xA8A0EC, 0xBCB4FC, 
+		0x000088, 0x1C209C, 0x3840B0, 0x505CC0, 0x6874D0, 0x7C8CE0, 0x90A4EC, 0xA4B8FC, 
+		0x00187C, 0x1C3890, 0x3854A8, 0x5070BC, 0x6888CC, 0x7C9CDC, 0x90B4EC, 0xA4C8FC, 
+		0x002C5C, 0x1C4C78, 0x386890, 0x5084AC, 0x689CC0, 0x7CB4D4, 0x90CCE8, 0xA4E0FC, 
+		0x003C2C, 0x1C5C48, 0x387C64, 0x509C80, 0x68B494, 0x7CD0AC, 0x90E4C0, 0xA4FCD4, 
+		0x003C00, 0x205C20, 0x407C40, 0x5C9C5C, 0x74B474, 0x8CD08C, 0xA4E4A4, 0xB8FCB8, 
+		0x143800, 0x345C1C, 0x507C38, 0x6C9850, 0x84B468, 0x9CCC7C, 0xB4E490, 0xC8FCA4, 
+		0x2C3000, 0x4C501C, 0x687034, 0x848C4C, 0x9CA864, 0xB4C078, 0xCCD488, 0xE0EC9C, 
+		0x442800, 0x644818, 0x846830, 0xA08444, 0xB89C58, 0xD0B46C, 0xE8CC7C, 0xFCE08C, 
+	};
+	
+	c = col[v >> 1];
+	u.c[0] = c;
+	u.c[1] = c >> 8;
+	u.c[2] = c >> 16;
+	u.c[3] = 0xff;
+	p = (u32int *)pic + ppuy * PICW * scale + ppux * 2 * scale;
+	switch(scale){
+	case 16: *p++ = u.l; *p++ = u.l;
+	case 15: *p++ = u.l; *p++ = u.l;
+	case 14: *p++ = u.l; *p++ = u.l;
+	case 13: *p++ = u.l; *p++ = u.l;
+	case 12: *p++ = u.l; *p++ = u.l;
+	case 11: *p++ = u.l; *p++ = u.l;
+	case 10: *p++ = u.l; *p++ = u.l;
+	case 9: *p++ = u.l; *p++ = u.l;
+	case 8: *p++ = u.l; *p++ = u.l;
+	case 7: *p++ = u.l; *p++ = u.l;
+	case 6: *p++ = u.l; *p++ = u.l;
+	case 5: *p++ = u.l; *p++ = u.l;
+	case 4: *p++ = u.l; *p++ = u.l;
+	case 3: *p++ = u.l; *p++ = u.l;
+	case 2: *p++ = u.l; *p++ = u.l;
+	default: *p++ = u.l; *p = u.l;
+	}
+}
+
+static void
+pixel(u8int v, int p, int s)
+{
+	if(p > pri){
+		col = v;
+		pri = p;
+	}
+	disp |= 1<<s;
+}
+
+static void
+playfield(void)
+{
+	int x, p;
+	u8int c;
+	
+	x = ppux / 4;
+	if(x >= 20)
+		if((reg[CTRLPF] & 1) != 0)
+			x = 39 - x;
+		else
+			x = x - 20;
+	if(x < 4){
+		if((reg[PF0] & 0x10<<x) == 0)
+			return;
+	}else if(x < 12){
+		if((reg[PF1] & 0x800>>x) == 0)
+			return;
+	}else
+		if((reg[PF2] & 1<<x-12) == 0)
+			return;
+	if((reg[CTRLPF] & 6) == 2)
+		if(ppux < 80){
+			c = reg[COLUP0];
+			p = 3;
+		}else{
+			c = reg[COLUP1];
+			p = 2;
+		}
+	else{
+		c = reg[COLUPF];
+		p = (reg[CTRLPF] & 4) + 1;
+	}
+	pixel(c, p, SRCPF);
+}
+
+static void
+player(int n)
+{
+	u8int c;
+	int x;
+
+	c = reg[GRP0 + n];
+	x = ppux - (n ? p1x : p0x);
+	if(x < 0)
+		return;
+	switch(reg[NUSIZ0 + n] & 7){
+	default: if(x >= 8) return; break;
+	case 1: if(x >= 8 && (x < 16 || x >= 24)) return; break;
+	case 2: if(x >= 8 && (x < 32 || x >= 40)) return; break;
+	case 3: if(x >= 40 || ((x & 15) >= 8)) return; break;
+	case 4: if(x >= 8 && (x < 64 || x >= 72)) return; break;
+	case 5: if(x >= 16) return; x >>= 1; break;
+	case 6: if(x >= 72 || ((x & 31) >= 8)) return; break;
+	case 7: if(x >= 32) return; x >>= 2; break;
+	}
+	x &= 7;
+	if((reg[REFP0 + n] & 8) == 0)
+		x ^= 7;
+	if((c & 1<<x) == 0)
+		return;
+	c = reg[COLUP0 + n];
+	pixel(c, 3 - n, SRCP0 + n);
+}
+
+static void
+missile(int n)
+{
+	int x;
+
+	x = ppux - (n ? m1x : m0x);
+	if((reg[RESMP0 + n] & 2) != 0){
+		if(n)
+			m1x = p1x;
+		else
+			m0x = p0x;
+		return;
+	}
+	if(x < 0 || x >= 1<<(reg[NUSIZ0] >> 4 & 3) || (reg[ENAM0 + n] & 2) == 0)
+		return;
+	pixel(reg[COLUP0 + n], 3 - n, SRCM0 + n);
+}
+
+static void
+ball(void)
+{
+	int x;
+
+	x = ppux - blx;
+	if(x < 0 || x >= 1<<(reg[CTRLPF] >> 4 & 3) || (reg[ENABL] & 2) == 0)
+		return;
+	pixel(reg[COLUPF], (reg[CTRLPF] & 4) + 1, SRCBL);
+}
+
+void
+tiastep(void)
+{
+	static u16int colltab[64] = {
+		0x0000, 0x0000, 0x0000, 0x0020, 0x0000, 0x0080, 0x8000, 0x80a0,
+		0x0000, 0x0200, 0x0001, 0x0221, 0x0002, 0x0282, 0x8003, 0x82a3,
+		0x0000, 0x0800, 0x0008, 0x0828, 0x0004, 0x0884, 0x800c, 0x88ac,
+		0x4000, 0x4a00, 0x4009, 0x4a29, 0x4006, 0x4a86, 0xc00f, 0xcaaf,
+		0x0000, 0x2000, 0x0010, 0x2030, 0x0040, 0x20c0, 0x8050, 0xa0f0,
+		0x0100, 0x2300, 0x0111, 0x2331, 0x0142, 0x23c2, 0x8153, 0xa3f3,
+		0x0400, 0x2c00, 0x0418, 0x2c38, 0x0444, 0x2cc4, 0x845c, 0xacfc,
+		0x4500, 0x6f00, 0x4519, 0x6f39, 0x4546, 0x6fc6, 0xc55f, 0xefff,
+	};
+
+	if(ppuy < PICH && ppux < 160){
+		col = reg[COLUBK];
+		pri = 0;
+		disp = 0;
+		playfield();
+		player(0);
+		player(1);
+		missile(0);
+		missile(1);
+		ball();
+		coll |= colltab[disp];
+		pixeldraw(col);
+	}
+	if(ppux == 160)
+		nrdy = 0;
+	if(++ppux == 228){
+		ppuy++;
+		ppux = 0;
+	}
+}
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -24,6 +24,7 @@
 BIN=/$objtype/bin/games
 
 DIRS=\
+	2600\
 	blabs\
 	blit\
 	c64\