shithub: riscv

Download patch

ref: f6eacf471e61b188607690e4cdc28a2d94f7db14
parent: 8ac5227a3626dd70a51d2c091b8730ce0f2d8403
author: aiju <devnull@localhost>
date: Sat Feb 7 13:03:17 EST 2015

added games/c64

binary files /dev/null b/sys/lib/c64/basic.bin differ
binary files /dev/null b/sys/lib/c64/crom.bin differ
binary files /dev/null b/sys/lib/c64/kernal.bin differ
--- /dev/null
+++ b/sys/src/games/c64/c64.c
@@ -1,0 +1,373 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include <draw.h>
+#include <mouse.h>
+#include <keyboard.h>
+#include "dat.h"
+#include "fns.h"
+
+char *bindir = "/sys/lib/c64";
+Image *tmp, *bg, *red;
+Rectangle picr, progr;
+Mousectl *mc;
+QLock pauselock;
+int paused, scale;
+u8int *rom;
+int nrom;
+u64int keys;
+u16int joys;
+uchar *tape, tapever, tapeplay;
+ulong tapelen;
+int joymode;
+
+void
+progress(int a, int b)
+{
+	static int cur;
+	int w;
+	
+	if(b == 0 || a == 0){
+		if(cur != 0){
+			draw(screen, progr, bg, nil, ZP);
+			cur = 0;
+		}
+		return;
+	}
+	w = a * Dx(progr) / b;
+	if(cur == w)
+		return;
+	draw(screen, Rect(progr.min.x, progr.min.y, progr.min.x + w, progr.max.y), red, nil, ZP);
+	cur = w;
+}
+
+static void
+loadsys(char *name, u8int *p, int n)
+{
+	static char buf[256];
+	int fd;
+
+	snprint(buf, sizeof(buf), "%s/%s", bindir, name);
+	fd = open(buf, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	if(readn(fd, p, n) < n)
+		sysfatal("readn: %r");
+	close(fd);
+}
+
+static void
+loadrom(char *name)
+{
+	int fd;
+	
+	fd = open(name, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	nrom = seek(fd, 0, 2);
+	if(nrom > 4096)
+		sysfatal("large ROM not supported");
+	if((nrom & nrom-1) != 0)
+		sysfatal("non-power-of-two ROM size");
+	rom = malloc(nrom);
+	if(rom == nil)
+		sysfatal("malloc: %r");
+	pread(fd, rom, nrom, 0);
+	close(fd);
+}
+
+static void
+loadcart(char *name)
+{
+	int fd;
+	u16int t, l;
+	u8int buf[80];
+
+	fd = open(name, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	read(fd, buf, 80);
+	if(memcmp(buf, "C64 CARTRIDGE   ", 16) != 0)
+		sysfatal("not a c64 cartridge");
+	t = buf[0x16] << 8 | buf[0x17];
+	if(t != 0)
+		sysfatal("unsupported type %d", t);
+	if(buf[0x18] == 0) pla &= ~EXROM;
+	if(buf[0x19] == 0) pla &= ~GAME;
+	t = buf[0x4c] << 8 | buf[0x4d];
+	if(t < 0x8000 || t >= 0xc000 && t < 0xe000)
+		sysfatal("odd starting address %x", t);
+	if(t >= 0xe000)
+		t -= 0x4000;
+	t -= 0x8000;
+	l = buf[0x4e] << 8 | buf[0x4f];
+	if(l + t > 16384)
+		sysfatal("cart too large");
+	read(fd, cart + t, l);
+	close(fd);
+}
+
+static void
+loadtape(char *name)
+{
+	int fd;
+	uchar buf[20];
+	
+	fd = open(name, OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	read(fd, buf, 20);
+	if(memcmp(buf, "C64-TAPE-RAW", 12) != 0)
+		sysfatal("not a c64 raw tape");
+	tapever = buf[12];
+	if(tapever > 1)
+		sysfatal("unsupported tape version %d", tapever);
+	tapelen = buf[16] | buf[17] << 8 | buf[18] << 16 | buf[19] << 24;
+	tape = malloc(tapelen);
+	readn(fd, tape, tapelen);
+	close(fd);
+}
+
+static void
+keyproc(void *)
+{
+	int fd, i, setnmi;
+	u16int j;
+	u64int k;
+	static Rune keymap[64] = {
+		Kbs, '\n', Kleft, KF|7, KF|1, KF|3, KF|5, Kup,
+		'3', 'w', 'a', '4', 'z', 's', 'e', Kshift,
+		'5', 'r', 'd', '6', 'c', 'f', 't', 'x',
+		'7', 'y', 'g', '8', 'b', 'h', 'u', 'v',
+		'9', 'i', 'j', '0', 'm', 'k', 'o', 'n',
+		'\'', 'p', 'l', '-', '.', ':', '@', ',',
+		'[', '*', ';', Khome, Kalt, '=', ']', '/',
+		'1', Kins, '\t', '2', ' ', Kctl, 'q', Kdel
+	};
+	static char buf[256];
+	char *s;
+	Rune r;
+
+	fd = open("/dev/kbd", OREAD);
+	if(fd < 0)
+		sysfatal("open: %r");
+	for(;;){
+		if(read(fd, buf, sizeof(buf) - 1) <= 0)
+			sysfatal("read /dev/kbd: %r");
+		if(buf[0] == 'c'){
+			if(utfrune(buf, Kend)){
+				close(fd);
+				threadexitsall(nil);
+			}
+			if(utfrune(buf, KF|12))
+				trace ^= 1;
+		}
+		if(buf[0] != 'k' && buf[0] != 'K')
+			continue;
+		s = buf + 1;
+		j = 0;
+		k = 0;
+		setnmi = 0;
+		while(*s != 0){
+			s += chartorune(&r, s);
+			switch(r){
+			case Kend: close(fd); threadexitsall(nil);
+			case Kesc:
+				if(paused)
+					qunlock(&pauselock);
+				else
+					qlock(&pauselock);
+				paused = !paused;
+				break;
+			case '`':
+				setnmi = 1;
+				break;
+			case Kleft: if(joymode) j |= 1<<2+5*(joymode-1); break;
+			case Kright: if(joymode) j |= 1<<3+5*(joymode-1); break;
+			case Kup: if(joymode) j |= 1<<0+5*(joymode-1); break;
+			case Kdown: if(joymode) j |= 1<<1+5*(joymode-1); break;
+			case Kctl: if(joymode) j |= 1<<4+5*(joymode-1); break;
+			}
+			for(i = 0; i < 64; i++)
+				if(keymap[i] == r)
+					k |= 1ULL<<i;
+		}
+		if(setnmi)
+			nmi |= IRQRESTORE;
+		else
+			nmi &= ~IRQRESTORE;
+		keys = k;
+		joys = j;
+	}
+
+}
+
+static void
+screeninit(void)
+{
+	Point p, q;
+
+	p = divpt(addpt(screen->r.min, screen->r.max), 2);
+	picr = (Rectangle){subpt(p, Pt(picw/2*scale, pich/2*scale)), addpt(p, Pt(picw/2*scale, pich/2*scale))};
+	p.y += pich*scale*3/4;
+	q = Pt(Dx(screen->r) * 2/5, 8);
+	progr = (Rectangle){subpt(p, q), addpt(p, q)};
+	tmp = allocimage(display, Rect(0, 0, picw*scale, scale > 1 ? 1 : pich), XRGB32, 1, 0);
+	bg = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xCCCCCCFF);
+	red = allocimage(display, Rect(0, 0, 1, 1), screen->chan, 1, 0xFF0000FF);
+	draw(screen, screen->r, bg, nil, ZP);
+}
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [ -23a ] [ rom ]\n", argv0);
+	exits("usage");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	scale = 1;
+
+	memreset();
+
+	ARGBEGIN {
+	case '2':
+		scale = 2;
+		break;
+	case '3':
+		scale = 3;
+		break;
+	case 'c':
+		loadcart(EARGF(usage()));
+		break;
+	case 't':
+		loadtape(EARGF(usage()));
+		break;
+	case 'N':
+		region = NTSC0;
+		break;
+	case 'p':
+		region = PAL;
+		break;
+	case 'd':
+		bindir = strdup(EARGF(usage()));
+		break;
+	default:
+		usage();
+	} ARGEND;
+	if(argc >= 2)
+		usage();
+	loadsys("kernal.bin", krom, 8192);
+	loadsys("basic.bin", brom, 8192);
+	loadsys("crom.bin", crom, 4096);
+	
+	vicreset();
+	
+	if(initdraw(nil, nil, nil) < 0)
+		sysfatal("initdraw: %r");
+	mc = initmouse(nil, screen);
+	if(mc == nil)
+		sysfatal("initmouse: %r");
+	screeninit();
+	proccreate(keyproc, nil, mainstacksize);
+
+	nmien = IRQRESTORE;
+	pc = memread(0xFFFC) | memread(0xFFFD) << 8;
+	rP = FLAGI;
+	for(;;){
+		if(paused){
+				qlock(&pauselock);
+				qunlock(&pauselock);
+			}
+		step();
+	}
+}
+
+static void
+menu(void)
+{
+	enum { JOY, TAPE };
+	static char joystr[32] = "joy: none";
+	static char tapestr[32] = "tape: play";
+	static char *items[] = {
+		[JOY] joystr,
+		[TAPE] tapestr,
+		nil
+	};
+	static Menu m = {
+		items, nil, 0
+	};
+	
+	switch(menuhit(3, mc, &m, nil)){
+	case JOY:
+		joymode = (joymode + 1) % 3;
+		if(joymode == 0)
+			strcpy(joystr, "joy: none");
+		else
+			sprint(joystr, "joy: %d", joymode);
+		break;
+	case TAPE:
+		tapeplay ^= 1;
+		if(tapeplay == 0){
+			strcpy(tapestr, "tape: play");
+			progress(0, 0);
+		}else
+			strcpy(tapestr, "tape: stop");
+		break;
+	}
+}
+
+void
+flush(void)
+{
+	extern u8int pic[];
+//	vlong new, diff;
+//	static vlong old, delta;
+
+	if(nbrecvul(mc->resizec) > 0){
+		if(getwindow(display, Refnone) < 0)
+			sysfatal("resize failed: %r");
+		screeninit();
+	}
+	while(nbrecv(mc->c, &mc->Mouse) > 0)
+		if((mc->buttons & 4) != 0)
+			menu();
+	if(scale == 1){
+		loadimage(tmp, tmp->r, pic, picw*pich*4);
+		draw(screen, picr, tmp, nil, ZP);
+	}else{
+		Rectangle r;
+		uchar *s;
+		int w;
+
+		s = pic;
+		r = picr;
+		w = picw*4*scale;
+		while(r.min.y < picr.max.y){
+			loadimage(tmp, tmp->r, s, w);
+			s += w;
+			r.max.y = r.min.y+scale;
+			draw(screen, r, tmp, nil, ZP);
+			r.min.y = r.max.y;
+		}
+	}
+	flushimage(display, 1);
+/*
+	if(audioout() < 0){
+		new = nsec();
+		diff = 0;
+		if(old != 0){
+			diff = BILLION/60 - (new - old) - delta;
+			if(diff >= MILLION)
+				sleep(diff/MILLION);
+		}
+		old = nsec();
+		if(diff != 0){
+			diff = (old - new) - (diff / MILLION) * MILLION;
+			delta += (diff - delta) / 100;
+		}
+	}
+*/
+}
--- /dev/null
+++ b/sys/src/games/c64/cpu.c
@@ -1,0 +1,514 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+u16int pc, curpc;
+u8int rA, rX, rY, rS, rP;
+int nrdy, irq, nmi, nmi0, irqen, nmien;
+
+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)
+{
+	memread(pc);
+	if(brk)
+		pc++;
+	push16(pc);
+	push8(rP | 0x20 | (brk << 4));
+	pc = memread(0xFFFA | (!nmi << 2));
+	pc |= memread(0xFFFB | (!nmi << 2)) << 8;
+	rP |= FLAGI;
+}
+
+int trace;
+
+void
+step(void)
+{
+	u8int op;
+	u16int a, v;
+	
+	if(nrdy){
+		io();
+		return;
+	}
+	if((nmi & nmien) != 0 && (nmi0 & nmien) == 0){
+		nmi0 = nmi;
+		interrupt(1, 0);
+		return;
+	}
+	nmi0 = nmi;
+	if((irq & irqen) != 0 && (rP & FLAGI) == 0){
+		interrupt(0, 0);
+		return;
+	}
+	curpc = pc;
+	op = fetch8();
+	if(trace)
+		print("%.4x %.2x | %.2x %.2x %.2x | %.2x %.2x | %3d %3d %.6x %.6x\n", curpc, op, rA, rX, rY, rS, rP, ppux-3, ppuy, irq, nmi);
+	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:
+		print("undefined %#x (pc %#x)\n", op, curpc);
+		return;
+	}
+}
--- /dev/null
+++ b/sys/src/games/c64/dat.h
@@ -1,0 +1,95 @@
+typedef char s8int;
+
+extern u8int reg[47], crom[4096], krom[8192], brom[8192], cram[1024], cart[16384];
+
+extern u16int pc, curpc;
+extern u8int rP;
+extern int nrdy, irq, nmi, irqen, nmien, trace;
+
+extern u8int pla;
+
+extern uchar *tape, tapever, tapeplay;
+extern ulong tapelen;
+
+extern u16int ppux, ppuy, picw, pich;
+extern u64int keys;
+extern u16int joys;
+extern int scale, region;
+
+enum {
+	FLAGC = 1<<0,
+	FLAGZ = 1<<1,
+	FLAGI = 1<<2,
+	FLAGD = 1<<3,
+	FLAGB = 1<<4,
+	FLAGV = 1<<6,
+	FLAGN = 1<<7,
+};
+
+enum {
+	IRQRASTER = 1<<0,
+	IRQBGCOLL = 1<<1,
+	IRQSPRCOLL = 1<<2,
+	IRQLIGHT = 1<<3,
+	IRQTIMERA = 1<<4,
+	IRQTIMERB = 1<<5,
+	IRQTOD = 1<<6,
+	IRQSDR = 1<<7,
+	IRQFLAG = 1<<8,
+	IRQRESTORE = 1<<9,
+};
+
+enum{
+	MSBX = 0x10,
+	CTRL1 = 0x11,
+	RASTER = 0x12,
+	SPREN = 0x15,
+	CTRL2 = 0x16,
+	SPRYE = 0x17,
+	MEMP = 0x18,
+	IRQLATCH = 0x19,
+	IRQEN = 0x1a,
+	SPRDP = 0x1b,
+	SPRMC = 0x1c,
+	SPRXE = 0x1d,
+	SPRSPR = 0x1e,
+	SPRBG = 0x1f,
+	EC = 0x20,
+	BG0 = 0x21,
+	BG1 = 0x22,
+	BG2 = 0x23,
+	BG3 = 0x24,
+	SPRMC0 = 0x25,
+	SPRMC1 = 0x26,
+	SPRCOL = 0x27
+};
+
+enum {
+	LORAM = 1,
+	HIRAM = 2,
+	CHAREN = 4,
+	GAME = 8,
+	EXROM = 16,
+	
+	ECM = 0x40,
+	BMM = 0x20,
+	DEN = 0x10,
+	RSEL = 0x08,
+	CSEL = 0x08,
+	
+	MCM = 0x10,
+};
+
+enum {
+	BILLION = 1000*1000*1000,
+	MILLION = 1000*1000,
+	HZ = 3579545,
+	RATE = 44100,
+	SAMPDIV = HZ / 3 / RATE,
+};
+
+enum {
+	NTSC,
+	NTSC0,
+	PAL
+};
--- /dev/null
+++ b/sys/src/games/c64/fns.h
@@ -1,0 +1,11 @@
+u8int memread(u16int);
+void memwrite(u16int, u8int);
+void step(void);
+void vicstep(void);
+void flush(void);
+u8int vmemread(u16int);
+void io(void);
+void memreset(void);
+void vicreset(void);
+void bordset(void);
+void progress(int, int);
--- /dev/null
+++ b/sys/src/games/c64/mem.c
@@ -1,0 +1,304 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int pla;
+u8int ram[65536], krom[8192], brom[8192], crom[4096], cart[16384], cram[1024];
+u8int reg[47];
+u16int vicbank;
+u8int cia[32];
+u16int timer[4], timrel[4];
+
+enum {
+	TIMEREN = 1,
+	TIMERUND = 2,
+	TIMERSTOP = 8,
+	TIMERASRC = 0x20,
+	TIMERBSRC = 0x60,
+	TIMERBSYS = 0,
+	TIMERBA = 0x40,
+};
+
+u8int 
+ciaread(int n, u8int a)
+{
+	u8int r;
+	int i;
+
+	switch(a){
+	case 0:
+		return (cia[0] | ~cia[2]) & (~joys >> 5 | 0xe0);
+	case 1:
+		if(!n){
+			r = 0;
+			for(i = 0; i < 8; i++)
+				if((cia[0] & 1<<i) == 0)
+					r |= keys >> 8 * i;
+			return (cia[1] | ~cia[3]) & ~r & (~joys | 0xe0);
+		}
+		break;
+	case 4: return timer[n*2];
+	case 5: return timer[n*2] >> 8;
+	case 6: return timer[n*2+1];
+	case 7: return timer[n*2+1] >> 8;
+	case 13:
+		if(n){
+			r = nmi >> 4 & 0x1f | ((nmi & nmien & 0x1f0) != 0) << 7;
+			nmi &= ~0x1f0;
+			return r;
+		}else{
+			r = irq >> 4 & 0x1f | ((irq & irqen & 0x1f0) != 0) << 7;
+			irq &= ~0x1f0;
+			return r;
+		}
+	}
+	return cia[n * 16 + a];
+}
+
+void
+ciawrite(int n, u8int a, u8int v)
+{
+	switch(a){
+	case 0:
+		if(n)
+			vicbank = (~v & 3) << 14;
+		break;
+	case 4: timrel[n*2] = v | timrel[n*2] & 0xff00; break;
+	case 5: timrel[n*2] = v << 8 | timrel[n*2] & 0xff; break;
+	case 6: timrel[n*2+1] = v | timrel[n*2+1] & 0xff00; break;
+	case 7: timrel[n*2+1] = v << 8 | timrel[n*2+1] & 0xff; break;
+	case 13:
+		if(n)
+			if((v & 0x80) != 0)
+				nmien |= v << 4 & 0x1f0;
+			else
+				nmien &= ~(v << 4 & 0x1f0);
+		else
+			if((v & 0x80) != 0)
+				irqen |= v << 4 & 0x1f0;
+			else
+				irqen &= ~(v << 4 & 0x1f0);
+		break;
+	case 14: case 15:
+		if((v & 0x10) != 0){
+			timer[n * 2 + (a & 1)] = timrel[n * 2 + (a & 1)];
+			v &= ~0x10;
+		}
+		break;
+	}
+	cia[n * 16 + a] = v;
+}
+
+u8int
+mioread(u16int a)
+{
+	u8int b, v;
+
+	switch(a & 0xc00){
+	case 0:
+		b = a & 63;
+		switch(b){
+		case CTRL1:
+			return reg[b] & 0x7f | ppuy >> 1 & 0x80;
+		case RASTER:
+			return ppuy;
+		case IRQLATCH:
+			return irq & 0xf | (irq & irqen & 0xf) + 0x7f & 0x80;
+		case IRQEN:
+			return irqen & 0xf;
+		case SPRSPR:
+		case SPRBG:
+			v = reg[b];
+			reg[b] = 0;
+			return v;
+		}
+		if(b >= 0x20)
+			return reg[b] | 0xf0;
+		if(b >= 47)
+			return 0xff;
+		return reg[b];
+	case 0x800:
+		return cram[a & 0x3ff];
+	case 0xc00:
+		if((a & 0x200) == 0)
+			return ciaread(a >> 8 & 1, a & 0xf);
+	default:
+		return 0xff;
+	}
+}
+
+void
+miowrite(u16int a, u8int v)
+{
+	u8int b;
+
+	switch(a & 0xc00){
+	case 0:
+		b = a & 63;
+		if(b >= 0x20)
+			v &= 0xf;
+		switch(b){
+		case CTRL2: v |= 0xc0; break;
+		case IRQLATCH:
+			v |= 0xf0;
+			irq &= ~(v & 0xf);
+			break;
+		case IRQEN:
+			irqen = irqen & ~0xf | v & 0xf;
+			v |= 0xf0;
+			break;
+		}
+		if(b < 47)
+			reg[b] = v;
+		if(b == CTRL1 || b == CTRL2)
+			bordset();
+		return;
+	case 0x800:
+		cram[a & 0x3ff] = v & 0xf;
+		return;
+	case 0xc00:
+		if((a & 0x200) == 0)
+			ciawrite(a >> 8 & 1, a & 0xf, v);
+		return;
+	}
+}
+
+void
+tapestep(void)
+{
+	static int tapectr;
+	static int idx;
+	
+	if((ram[1] & 1<<5) != 0)
+		return;
+	if(tapectr == 0){
+		if(idx >= tapelen){
+			progress(0, 0);
+			tapeplay = 0;
+			idx = 0;
+			return;
+		}
+		tapectr = tape[idx++] << 3;
+		if(tapever == 1 && tapectr == 0){
+			tapectr = tape[idx++];
+			tapectr |= tape[idx++] << 8;
+			tapectr |= tape[idx++] << 16;
+		}
+		progress(idx, tapelen);
+	}else{
+		tapectr--;
+		if(tapectr == 0)
+			irq |= IRQFLAG;
+	}
+}
+
+void
+timerstep(void)
+{
+	int i, at;
+	u8int a, b;
+	u16int *t;
+	
+	for(i = 0; i < 2; i++){
+		a = cia[i * 16 + 14];
+		b = cia[i * 16 + 15];
+		at = 0;
+		t = &timer[2 * i];
+		if((a & (TIMEREN|TIMERASRC)) == TIMEREN){
+			t[0]--;
+			if(t[0] == 0){
+				at = 1;
+				if(i)
+					nmi |= IRQTIMERA;
+				else
+					irq |= IRQTIMERA;
+				if((a & TIMERSTOP) != 0)
+					cia[i * 16 + 14] &= ~TIMEREN;
+				t[0] = timrel[2 * i];
+			}
+		}
+		if((b & TIMEREN) != 0 && ((b & TIMERBSRC) == TIMERBSYS || (b & TIMERBSRC) == TIMERBA && at)){
+			t[1]--;
+			if(t[1] == 0){
+				if(i)
+					nmi |= IRQTIMERB;
+				else
+					irq |= IRQTIMERB;
+				if((b & TIMERSTOP) == 0)
+					cia[i * 16 + 15] &= ~TIMEREN;
+				t[1] = timrel[2 * i + 1];
+			}
+		}
+	}
+	if(tapeplay)
+		tapestep();
+}
+
+void
+io(void)
+{
+	vicstep();
+	timerstep();
+}
+
+u8int
+memread(u16int a)
+{
+	io();
+	if(a == 1)
+		return ram[1] & ~(1<<4) | (tapeplay ^ 1) << 4;
+	switch(a >> 12){
+	case 8: case 9:
+		if((pla & (EXROM|GAME)) == EXROM || (pla & (EXROM|HIRAM|LORAM)) == (HIRAM|LORAM))
+			return cart[a & 0x1fff];
+		goto def;
+	case 10: case 11:
+		if((pla & (GAME|HIRAM|LORAM)) == (GAME|HIRAM|LORAM))
+			return brom[a & 0x1fff];
+		if((pla & (EXROM|GAME|HIRAM)) == HIRAM)
+			return cart[8192 + (a & 0x1fff)];
+		goto def;
+	case 13:
+		if((pla & (HIRAM|LORAM)) == 0 || pla == 1)
+			goto def;
+		if((pla & CHAREN) == 0 && (pla & (EXROM|GAME)) != EXROM)
+			return crom[a & 0xfff];
+		return mioread(a & 0xfff);
+	case 14: case 15:
+		if((pla & (EXROM|GAME)) == EXROM)
+			return cart[8192 + (a & 0x1fff)];
+		if((pla & HIRAM) == HIRAM)
+			return krom[a & 0x1fff];
+	def:
+	default:
+		return ram[a];
+	}
+}
+
+void
+memwrite(u16int a, u8int v)
+{
+	if(a >> 12 == 13 && !((pla & (HIRAM|LORAM)) == 0 || pla == 1 || (pla & CHAREN) == 0 && (pla & (EXROM|GAME)) != EXROM))
+		miowrite(a & 0xfff, v);
+	ram[a] = v;
+	if(a == 1)
+		pla = pla & ~7 | v & 7;
+	io();
+}
+
+u8int
+vmemread(u16int a)
+{
+	a |= vicbank;
+	if((a & 0x7000) == 0x1000)
+		return crom[a & 0xfff];
+	return ram[a];
+}
+
+void
+memreset(void)
+{
+	pla = 0x1f;
+}
--- /dev/null
+++ b/sys/src/games/c64/mkfile
@@ -1,0 +1,13 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin/games
+TARG=2600
+OFILES=\
+	c64.$O\
+	cpu.$O\
+	mem.$O\
+	vic.$O\
+	
+HFILES=dat.h fns.h
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/c64/vic.c
@@ -1,0 +1,366 @@
+#include <u.h>
+#include <libc.h>
+#include <thread.h>
+#include "dat.h"
+#include "fns.h"
+
+int region, picidx;
+u16int ppux, ppuy, lastx, wrapx, maxy, lvis, rvis, uvis, dvis, picw, pich, lbord, rbord, ubord, dbord, spr0;
+u16int vc, vcbase, vmli;
+u8int badln, rc, displ, fract, visreg, hbord, vbord, rbord0, lbord0;
+u16int chrp[40];
+u8int pic[400*300*3*3];
+u64int pxs, npxs, npxs0, opxs;
+u8int fg;
+
+typedef struct spr spr;
+enum {
+	SPRFDMA = 1,
+	SPRFYEX = 2,
+	SPRFDISP = 4,
+};
+struct spr {
+	u8int flags;
+	u32int data;
+	u8int mc, mcbase, dp;
+	u16int x;
+} sp[8];
+
+void
+bordset(void)
+{
+	int r, c;
+
+	r = (reg[CTRL1] & RSEL) != 0;
+	c = (reg[CTRL2] & CSEL) != 0;
+	lbord = c ? 0x14 : 0x1c;
+	lbord0 = c ? 0xf0 : 0xe0;
+	rbord = c ? 0x154 : 0x14c;
+	rbord0 = c ? 0x0f : 0x3f;
+	ubord = r ? 0x33 : 0x37;
+	dbord = r ? 0xfb : 0xf7;
+	if((reg[CTRL1] & DEN) == 0)
+		ubord = -1;
+}
+
+void
+vicreset(void)
+{
+	switch(region){
+	case NTSC0:
+		lastx = 0x1fc;
+		wrapx = 0x19c;
+		maxy = 262;
+		picw = 412;
+		pich = 234;
+		spr0 = 0x16c;
+		lvis = 0x1e4;
+		rvis = 0x184;
+		uvis = 41;
+		dvis = 13;
+		break;
+	case NTSC:
+		lastx = 0x1fc;
+		wrapx = 0x19c;
+		maxy = 263;
+		picw = 419;
+		pich = 235;
+		spr0 = 0x174;
+		lvis = 0x1e4;
+		rvis = 0x18c;
+		uvis = 41;
+		dvis = 13;
+		break;
+	case PAL:
+		lastx = 0x1f4;
+		wrapx = 0x194;
+		maxy = 312;
+		picw = 404;
+		pich = 284;
+		spr0 = 0x164;
+		lvis = 0x1dc;
+		rvis = 0x17c;
+		uvis = 16;
+		dvis = 300;
+		break;
+	}
+	ppux = 4;
+	ppuy = 0;
+	bordset();
+}
+
+void
+pixeldraw(u64int p, int n)
+{
+	int i;
+	static u8int cr[] = {0, 255, 136, 170, 204, 0, 0, 238, 221, 102, 255, 51, 119, 170, 0, 187};
+	static u8int cg[] = {0, 255, 0, 255, 68, 204, 0, 238, 136, 68, 119, 51, 119, 255, 136, 187};
+	static u8int cb[] = {0, 255, 0, 238, 204, 85, 170, 119, 85, 0, 119, 51, 119, 102, 255, 187};
+	u8int *q, c;
+	
+	q = pic + picidx * 4;
+	for(i = 0; i < n; i++){
+		c = p >> 56;
+		p <<= 8;
+	
+		q[4 * i] = cb[c];
+		q[4 * i + 1] = cg[c];
+		q[4 * i + 2] = cr[c];
+	}
+	picidx += n;
+}
+
+void
+pixels(u8int d, u16int c)
+{
+	u8int c0, c1, c2, n;
+	int i;
+
+	npxs0 = npxs;
+	npxs = 0;
+	switch((reg[CTRL1] & (ECM|BMM) | reg[CTRL2] & MCM) >> 4){
+	case 0:
+		c0 = c >> 8;
+	normal:
+		fg = d;
+		for(i = 0; i < 8; i++){
+			npxs = npxs << 8 | ((d & 0x80) != 0 ? c0 : reg[BG0]);
+			d <<= 1;
+		}
+		break;
+	case 1:
+		c0 = c >> 8 & 7;
+		if((c & 0x800) == 0)
+			goto normal;
+		fg = d & 0xaa | d >> 1 & 0x55;
+		for(i = 0; i < 8; i += 2){
+			n = d >> 6;
+			npxs = npxs << 16 | (n == 3 ? c0 : reg[BG0 + n]) * 0x101;
+			d <<= 2;
+		}
+		break;
+	case 2:
+		c0 = c & 15;
+		c1 = c >> 4 & 15;
+		fg = d;
+		for(i = 0; i < 8; i++){
+			npxs = npxs << 8 | ((d & 0x80) != 0 ? c1 : c0);
+			d <<= 1;
+		}
+		break;
+	case 3:
+		c0 = c & 15;
+		c1 = c >> 4 & 15;
+		c2 = c >> 8;
+		fg = d & 0xaa | d >> 1 & 0x55;
+		for(i = 0; i < 8; i += 2){
+			n = d >> 6;
+			switch(n){
+			default: n = reg[BG0]; break;
+			case 1: n = c1; break;
+			case 2: n = c0; break;
+			case 3: n = c2;
+			}
+			npxs = npxs << 16 | n * 0x101;
+			d <<= 2;
+		}
+		break;
+	case 4:
+		c0 = c >> 8;
+		fg = d;
+		for(i = 0; i < 8; i++){
+			npxs = npxs << 8 | ((d & 0x80) != 0 ? c0 : reg[BG0 + (d >> 6 & 3)]);
+			d <<= 1;
+		}
+		break;
+	default:
+		fg = 0;
+		break;
+	}
+}
+
+void
+bgpixels(void)
+{
+	int i, j, x;
+	u8int h;
+	u8int spract, s, o;
+	
+	h = hbord;
+	opxs = 0;
+	for(i = 0; i < 8; i++){
+		if((reg[CTRL2] + 4 & 7) == i)
+			pxs = i >= 4 ? npxs : npxs0;
+		o = pxs >> 56;
+		pxs <<= 8;
+
+		x = ppux + i;
+		spract = 0;
+		for(j = 0; j < 8; j++)
+			if((sp[j].flags & SPRFDISP) != 0 && (u16int)(x-sp[j].x) < 48 && (sp[j].data & ((reg[SPRMC]&1<<j)?3:2)<<22) != 0)
+				spract |= 1<<j;
+		if((spract & spract - 1) != 0 && vbord == 0){
+			reg[SPRSPR] |= spract;
+			irq |= IRQSPRCOLL;
+		}
+		if(fg != 0 && spract != 0 && vbord == 0){
+			reg[SPRBG] |= spract;
+			irq |= IRQBGCOLL;
+		}
+		s = spract & ~(reg[SPRDP] & ((s8int)fg) >> 7);
+		if(s != 0)
+			for(j = 0; j < 8; j++)
+				if((s & 1<<j) != 0){
+					if((reg[SPRMC] & 1<<j) != 0)
+						switch(sp[j].data >> 22 & 3){
+						case 1: o = reg[SPRMC0]; break;
+						case 2: o = reg[SPRCOL+j]; break;
+						case 3: o = reg[SPRMC1]; break;
+						}
+					else
+						o = reg[SPRCOL+j];
+					break;
+				}
+		if((h & 0x80) != 0)
+			o = reg[EC];
+		opxs = opxs << 8 | o;
+		h <<= 1;
+		fg <<= 1;
+		for(j = 0; j < 8; j++)
+			if((u16int)(x-sp[j].x) < 48 && ((reg[SPRXE] & 1<<j) == 0 || (x-sp[j].x & 1) != 0))
+				if((reg[SPRMC] & 1<<j) != 0){
+					if((x-sp[j].x & 1) != 0)
+						sp[j].data <<= 2;
+				}else
+					sp[j].data <<= 1;
+	}
+}
+
+void
+vicstep(void)
+{
+	u16int gaddr;
+	int i;
+
+	if(ppuy == 0x30 && (reg[CTRL1] & DEN) != 0)
+		fract = 1;
+	badln = ((ppuy ^ reg[CTRL1]) & 7) == 0 && fract;
+	hbord = ((s8int)(hbord << 7)) >> 7;
+	if(ppux == rbord && hbord == 0)
+		hbord = rbord0;
+	else if(ppux == lbord){
+		if(ppuy == dbord)
+			vbord = 1;
+		if(ppuy == ubord)
+			vbord = 0;
+		if(!vbord)
+			hbord = lbord0;
+	}
+	if(badln)
+		displ = 1;
+	if(ppux == 4){
+		vc = vcbase;
+		vmli = 0;
+		if(badln)
+			rc = 0;
+	}
+	if(ppux == 12)
+		for(i = 0; i < 8; i++){
+			if((sp[i].flags & SPRFDISP) == 0 || (reg[SPRYE] & 1<<i) != 0 && (sp[i].flags & SPRFYEX) == 0)
+				continue;
+			sp[i].mcbase += 3;
+			if(sp[i].mcbase == 63)
+				sp[i].flags &= ~(SPRFDMA|SPRFDISP);
+		}
+	if(ppux >= 0x14 && ppux <= 0x14c){
+		if((reg[CTRL1] & BMM) != 0)
+			gaddr = (reg[MEMP] & 0x08) << 10 | vc << 3 | rc;
+		else
+			gaddr = (reg[MEMP] & 0x0e) << 10 | (chrp[vmli] & 0xff) << 3 | rc;
+		if(!displ)
+			gaddr = 0x3fff;
+		if((reg[CTRL1] & ECM) != 0)
+			gaddr &= ~0x600;
+		pixels(vmemread(gaddr) & -displ, chrp[vmli] & -displ);
+		vmli++;
+		vc = vc + 1 & 0x3ff;
+	}
+	if(visreg && (ppux >= lvis || ppux < rvis)){
+		bgpixels();
+		pixeldraw(opxs, ppux == lvis ? region == NTSC ? 3 : 4 : 8);
+	}
+	if(ppux == 0x14c){
+		for(i = 0; i < 8; i++){
+			if((reg[SPRYE] & 1<<i) != 0)
+				sp[i].flags ^= SPRFYEX;
+			if((reg[SPREN] & 1<<i) == 0 || reg[2*i+1] != (u8int)ppuy)
+				continue;
+			sp[i].flags |= SPRFDMA;
+			sp[i].mcbase = 0;
+			if((reg[SPRYE] & 1<<i) != 0)
+				sp[i].flags &= ~SPRFYEX;		
+		}
+	}else if(ppux == 0x154)
+		npxs = reg[BG0] * 0x0101010101010101ULL;
+	if(badln){
+		if(ppux == lastx - 8)
+			nrdy = 1;
+		else if(ppux >= 0xc && ppux <= 0x144)
+			chrp[vmli] = vmemread(vc | (reg[MEMP] & 0xf0) << 6) | cram[vc] << 8;
+		else if(ppux == 0x154)
+			nrdy = 0;
+	}
+	if(ppux == 0x164){
+		if(displ && rc == 7){
+			displ = badln;
+			vcbase = vc;
+		}
+		if(displ)
+			rc = rc + 1 & 7;
+		for(i = 0; i < 8; i++){
+			sp[i].mc = sp[i].mcbase;
+			if((sp[i].flags & SPRFDMA) != 0 && reg[2*i+1] == (u8int)ppuy)
+				sp[i].flags |= SPRFDISP;
+		}
+	}
+	if((u16int)(ppux - spr0) < 128){
+		i = ppux - spr0 >> 4;
+		nrdy = (sp[i].flags & SPRFDMA) != 0;
+		if((ppux & 8) == 0){
+			sp[i].dp = vmemread((reg[MEMP] & 0xf0) << 6 | 0x3f8 | i);
+			sp[i].x = nrdy ? reg[2 * i] | reg[MSBX] << 8 - i & 0x100 : -1;
+			if(nrdy)
+				sp[i].data = vmemread(sp[i].dp << 6 | sp[i].mc++) << 16;
+		}else if(nrdy){
+			sp[i].data = sp[i].data & 0xff00ff | vmemread(sp[i].dp << 6 | sp[i].mc++) << 8;
+			sp[i].data = sp[i].data & 0xffff00 | vmemread(sp[i].dp << 6 | sp[i].mc++);
+		}
+	}else if(ppux - spr0 == 128)
+		nrdy = 0;
+	if(ppux == wrapx){
+		ppuy++;
+		if(ppuy == maxy){
+			flush();
+			ppuy = 0;
+			vcbase = 0;
+		}
+		if((ppuy & 0xff) == reg[RASTER] && ((ppuy ^ reg[CTRL1] << 1) & 0x100) == 0)
+			irq |= IRQRASTER;
+		if(ppuy == dbord)
+			vbord = 1;
+		else if(ppuy == ubord)
+			vbord = 0;
+		else if(ppuy == dvis)
+			visreg = 0;
+		if(ppuy == uvis){
+			picidx = 0;
+			visreg = 1;
+		}
+		if(ppuy == 0xf7)
+			fract = 0;
+	}
+	if(ppux == lastx)
+		ppux = 4;
+	else
+		ppux += 8;
+}