shithub: 8080

Download patch

ref: df6478340d048ba76ab8e96aaaf490930a778eae
author: Alex Musolino <[email protected]>
date: Sat Nov 28 22:14:37 EST 2020

initial commit

--- /dev/null
+++ b/8080.c
@@ -1,0 +1,226 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+CPU ocpu, cpu;
+Insn insn;
+
+uchar mem[MEMSZ];
+uchar *rom = &mem[0];
+uchar *ram = &mem[ROMSZ];
+uchar *vid = &mem[ROMSZ+RAMSZ];
+
+int interactive;
+int debug;
+int nbrkpts;
+int tracing;
+int ntraceops;
+TraceOp traceops[MAXTRACEOPS];
+BrkPt brkpts[MAXBRKPTS];
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s [-b addr] rom\n", argv0);
+	exits("usage");
+}
+
+int
+loadrom(char *file, u16int base)
+{
+	uchar *mp, *romend;
+	int c;
+	Biobuf *r;
+
+	r = Bopen(file, OREAD);
+	if(r == nil)
+		return -1;
+	romend = rom + ROMSZ;
+	for(;;){
+		for(mp = &rom[base]; mp < romend; mp++){
+			c = Bgetc(r);
+			if(c < 0)
+				goto Done;
+			*mp = c;
+		}
+	}
+Done:
+	Bterm(r);
+	return 0;
+}
+
+static void
+cpureset(void)
+{
+	memset(&ocpu, 0, sizeof(ocpu));
+	memset(&cpu, 0, sizeof(cpu));
+}
+
+static void
+cpustep(void)
+{
+	int i;
+	int ilen;
+	uchar buf[3];
+	static u64int counter;
+
+	for(i = 0; i < ntraceops; i++)
+		if(cpu.PC == traceops[i].addr)
+			if(traceops[i].op == Tpush){
+				dprint("@@@@ Tpush @ %2x\n", traceops[i].addr);
+				tracing++;
+			}
+
+	for(i = 0; i < nbrkpts; i++)
+		if(brkpts[i].enabled && cpu.PC == brkpts[i].addr){
+			brkpts[i].enabled = 0;
+			trap();
+		}
+
+	insn.pc = cpu.PC;
+	insn.op = decodeop(buf[0] = ifetch(&cpu));
+	if(insn.op == -1){
+		fprint(2, "illegal opcode %#.2uhhx @ pc=%#.4uhx\n", buf[0], insn.pc);
+		trap();
+	}
+	switch(ilen = insnlen(insn.op)){
+	case 2:
+		buf[1] = ifetch(&cpu);
+		break;
+	case 3:
+		buf[1] = ifetch(&cpu);
+		buf[2] = ifetch(&cpu);
+		break;
+	}
+	decodeinsn(&insn, buf, ilen);
+	cpuexec(&cpu, &insn);
+
+	counter++;
+	if((cpu.intr&Ienabled) != 0){
+		if(counter % 512 == 0){
+			push16(&cpu, cpu.PC);
+			cpu.PC = 2<<3;
+		}else if(counter % 256 == 0){
+			push16(&cpu, cpu.PC);
+			cpu.PC = 1<<3;
+		}
+	}
+
+	for(i = 0; i < nbrkpts; i++)
+		if(cpu.PC == brkpts[i].addr)
+			brkpts[i].enabled = 1;
+
+	for(i = 0; i < ntraceops; i++)
+		if(cpu.PC == traceops[i].addr)
+			if(traceops[i].op == Tpop){
+				dprint("@@@@ Tpop @ %2x\n", traceops[i].addr);
+				tracing--;
+			}
+
+	ocpu = cpu;
+}
+
+static void
+prompt(void)
+{
+	static char prev[256] = "";
+	int n;
+	char buf[256];
+
+	trapinit();
+	if(interactive)
+		print("8080> ");
+	n = read(0, buf, sizeof(buf) - 1);
+	if(n <= 0)
+		exits("eof");
+	if(buf[n-1] != '\n')
+		exits("nl");
+	buf[n-1] = 0;
+	if(strcmp(buf, "") == 0){
+		if(strcmp(prev, "") == 0)
+			return;
+		strcpy(buf, prev);
+	}
+	if(strcmp(buf, "das") == 0){
+		//das(&insn, mem);
+	}else if(strcmp(buf, "bpset") == 0){
+	}else if(strcmp(buf, "load") == 0){
+		if(loadrom("invaders.rom", 0) < 0)
+			fprint(2, "load failed: %r\n");
+	}else if(strcmp(buf, "reg") == 0){
+		dumpregs();
+	}else if(strcmp(buf, "run") == 0){
+		if(wastrap())
+			cpu = ocpu;
+		else
+			for(;;){
+				//print("%#.4uhx\t", cpu.PC);
+				//das(mem+cpu.PC,nelem(mem));
+				cpustep();
+			}
+	}else if(strcmp(buf, "step") == 0){
+		if(wastrap())
+			cpu = ocpu;
+		else{
+			print("%#.4uhx\t", cpu.PC);
+			das(mem+cpu.PC,nelem(mem));
+			cpustep();
+		}
+	}else if(strcmp(buf, "exit") == 0){
+		exits(0);
+	}else if(strcmp(buf, "reset") == 0){
+		cpureset();
+	}else{
+		fprint(2, "unknown command: %s\n", buf);
+		buf[0] = 0;
+	}
+	strcpy(prev, buf);
+}
+
+static int
+isatty(void)
+{
+	char buf[64];
+
+	if(fd2path(0, buf, sizeof(buf)) != 0)
+		return 0;
+	return strcmp(buf, "/dev/cons") == 0;
+}
+
+void
+main(int argc, char **argv)
+{
+	ARGBEGIN{
+	case 'b':
+		if(nbrkpts >= MAXBRKPTS)
+			sysfatal("too many breakpoints");
+		brkpts[nbrkpts].addr = strtoul(EARGF(usage()), 0, 0);
+		brkpts[nbrkpts].enabled = 1;
+		nbrkpts++;
+		break;
+	case 'd':
+		debug++;
+		break;
+	case 't':
+	case 'T':
+		if(ntraceops >= MAXTRACEOPS)
+			sysfatal("too many trace ops");
+		traceops[ntraceops].addr = strtoul(EARGF(usage()), 0, 0);
+		traceops[ntraceops].op = ARGC() == 'T' ? Tpush : Tpop;
+		ntraceops++;
+		break;
+	default:
+		usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+	interactive = isatty();
+	fmtinstall('I', insnfmt);
+	cpureset();
+	if(loadrom("invaders.rom", 0) < 0)
+		fprint(2, "load failed: %r\n");
+	for(;;)
+		prompt();
+}
--- /dev/null
+++ b/das.c
@@ -1,0 +1,1163 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "dat.h"
+#include "fns.h"
+
+static void Xshld(CPU*, Insn*);
+static void Xsbi(CPU*, Insn*);
+static void Xjm(CPU*, Insn*);
+static void Xdaa(CPU*, Insn*);
+static void Xcz(CPU*, Insn*);
+static void Xpchl(CPU*, Insn*);
+static void Xnop(CPU*, Insn*);
+static void Xjmp(CPU*, Insn*);
+static void Xlxi(CPU*, Insn*);
+static void Xmvi(CPU*, Insn*);
+static void Xpop(CPU*, Insn*);
+static void Xcall(CPU*, Insn*);
+static void Xldax(CPU*, Insn*);
+static void Xmov(CPU*, Insn*);
+static void Xinx(CPU*, Insn*);
+static void Xdcr(CPU*, Insn*);
+static void Xret(CPU*, Insn*);
+static void Xdad(CPU*, Insn*);
+static void Xsta(CPU*, Insn*);
+static void Xxra(CPU*, Insn*);
+static void Xout(CPU*, Insn*);
+static void Xora(CPU*, Insn*);
+static void Xlda(CPU*, Insn*);
+static void Xana(CPU*, Insn*);
+static void Xpush(CPU*, Insn*);
+static void Xxchg(CPU*, Insn*);
+static void Xinr(CPU*, Insn*);
+static void Xani(CPU*, Insn*);
+static void Xrar(CPU*, Insn*);
+static void Xori(CPU*, Insn*);
+static void Xcmp(CPU*, Insn*);
+static void Xrlc(CPU*, Insn*);
+static void Xrim(CPU*, Insn*);
+static void Xrrc(CPU*, Insn*);
+static void Xdcx(CPU*, Insn*);
+static void Xstax(CPU*, Insn*);
+static void Xcpi(CPU*, Insn*);
+static void Xadi(CPU*, Insn*);
+static void Xei(CPU*, Insn*);
+static void Xdi(CPU*, Insn*);
+static void Xin(CPU*, Insn*);
+static void Xjc(CPU*, Insn*);
+static void Xjz(CPU*, Insn*);
+static void Xjnc(CPU*, Insn*);
+static void Xjnz(CPU*, Insn*);
+static void Xrc(CPU*, Insn*);
+static void Xrnc(CPU*, Insn*);
+static void Xrnz(CPU*, Insn*);
+static void Xrz(CPU*, Insn*);
+static void Xsui(CPU*, Insn*);
+static void Xxthl(CPU*, Insn*);
+
+static int dec0(Insn*, uchar*, long);
+static int decaddr(Insn*, uchar*, long);
+static int decimm(Insn*, uchar*, long);
+static int decr00000xxx(Insn*, uchar*, long);
+static int decr00xxx000(Insn*, uchar*, long);
+static int decrimm(Insn*, uchar*, long);
+static int decrp(Insn*, uchar*, long);
+static int decrpimm(Insn*, uchar*, long);
+static int decrr(Insn*, uchar*, long);
+
+static int das0(Fmt*, Insn*);
+static int dasaddr(Fmt*, Insn*);
+static int dasimm(Fmt*, Insn*);
+static int dasr(Fmt*, Insn*);
+static int dasrimm(Fmt*, Insn*);
+static int dasrp(Fmt*, Insn*);
+static int dasrpimm(Fmt*, Insn*);
+static int dasrr(Fmt*, Insn*);
+
+static InsnType insntypes[] = {
+	[T0]		= { 1, das0, dec0 },
+	[Taddr] 	= { 3, dasaddr, decaddr },
+	[Timm]		= { 2, dasimm, decimm },
+	[Tr012]		= { 1, dasr, decr00000xxx },
+	[Tr345]		= { 1, dasr, decr00xxx000 },
+	[Trimm]		= { 2, dasrimm, decrimm },
+	[Trp]		= { 1, dasrp, decrp },
+	[Trpimm]	= { 3, dasrpimm, decrpimm },
+	[Trr]		= { 1, dasrr, decrr },
+};
+
+static ISA isa[] = {
+	[Oadc]{"ADC", Tr012},
+	[Oadd]{"ADD", Tr012},
+	[Oadi]{"ADI", Timm, Xadi},
+	[Oana]{"ANA", Tr012, Xana},
+	[Oani]{"ANI", Timm, Xani},
+	[Ocall]{"CALL", Taddr, Xcall},
+	[Ocm]{"CM", Taddr},
+	[Ocma]{"CMA", T0},
+	[Ocmc]{"CMC", T0},
+	[Ocmp]{"CMP", Tr012, Xcmp},
+	[Ocnc]{"CNC", Taddr},
+	[Ocpe]{"CPE", Taddr},
+	[Ocpi]{"CPI", Timm, Xcpi},
+	[Ocz]{"CZ", Taddr, Xcz},
+	[Odaa]{"DAA", T0, Xdaa},
+	[Odad]{"DAD", Trp, Xdad},
+	[Odcr]{"DCR", Tr345, Xdcr},
+	[Odcx]{"DCX", Trp, Xdcx},
+	[Odi]{"DI", T0, Xdi},
+	[Oei]{"EI", T0, Xei},
+	[Oin]{"IN", Timm, Xin},
+	[Oinr]{"INR", Tr345, Xinr},
+	[Oinx]{"INX", Trp, Xinx},
+	[Ojc]{"JC", Taddr, Xjc},
+	[Ojm]{"JM", Taddr, Xjm},
+	[Ojmp]{"JMP", Taddr, Xjmp},
+	[Ojnc]{"JNC", Taddr, Xjnc},
+	[Ojnz]{"JNZ", Taddr, Xjnz},
+	[Ojpo]{"JPO", Taddr},
+	[Ojz]{"JZ", Taddr, Xjz},
+	[Olda]{"LDA", Taddr, Xlda},
+	[Oldax]{"LDAX", Trp, Xldax},
+	[Olxi]{"LXI", Trpimm, Xlxi},
+	[Omov]{"MOV", Trr, Xmov},
+	[Omvi]{"MVI", Trimm, Xmvi},
+	[Onop]{"NOP", T0, Xnop},
+	[Oora]{"ORA", Tr012, Xora},
+	[Oori]{"ORI", Timm, Xori},
+	[Oout]{"OUT", Timm, Xout},
+	[Opchl]{"PCHL", T0, Xpchl},
+	[Opop]{"POP", Trp, Xpop},
+	[Opush]{"PUSH", Trp, Xpush},
+	[Orar]{"RAR", T0, Xrar},
+	[Orc]{"RC", T0, Xrc},
+	[Oret]{"RET", T0, Xret},
+	[Orim]{"RIM", T0},
+	[Orlc]{"RLC", T0, Xrlc},
+	[Orm]{"RM", T0},
+	[Ornc]{"RNC", T0, Xrnc},
+	[Ornz]{"RNZ", T0, Xrnz},
+	[Orp]{"RP", T0},
+	[Orpo]{"RPO", T0},
+	[Orrc]{"RRC", T0, Xrrc},
+	[Orst]{"RST", Timm},
+	[Orz]{"RZ", T0, Xrz},
+	[Osbb]{"SBB", Tr012},
+	[Osbi]{"SBI", Timm, Xsbi},
+	[Oshld]{"SHLD", Taddr, Xshld},
+	[Osim]{"SIM", T0},
+	[Osta]{"STA", Taddr, Xsta},
+	[Ostax]{"STAX", Trp, Xstax},
+	[Osub]{"SUB", Tr012},
+	[Osui]{"SUI", Timm, Xsui},
+	[Oxchg]{"XCHG", T0, Xxchg},
+	[Oxra]{"XRA", Tr012, Xxra},
+	[Oxri]{"XRI", Timm},
+	[Oxthl]{"XTHL", T0, Xxthl},
+};
+
+static char*
+opstr(int op)
+{
+	if(op < 0 || op >= nelem(isa))
+		return "XXX";
+	return isa[op].opstr;
+}
+
+static int
+das0(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s", opstr(insn->op));
+}
+
+static int
+dasr(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s %s", opstr(insn->op), rnam(insn->r1));
+}
+
+static int
+dasrr(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s %s, %s", opstr(insn->op),
+		rnam(insn->r1), rnam(insn->r2));
+}
+
+static int
+dasimm(Fmt *fmt, Insn *insn)
+{
+	char *fmtstr;
+
+	if(insn->op == Orst)
+		fmtstr = "%s %uhhd";
+	else
+		fmtstr = "%s #$%#.2uhhx";
+	return fmtprint(fmt, fmtstr, opstr(insn->op), insn->imm);
+}
+
+static int
+dasrimm(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s %s, #$%#.2uhhx", opstr(insn->op), rnam(insn->r1), insn->imm);
+}
+
+static int
+dasrp(Fmt *fmt, Insn *insn)
+{
+	char *rp;
+
+	rp = rpnam(insn->rp);
+	if(insn->rp == 3){
+		switch(insn->op){
+		case Opush:
+		case Opop:
+			rp = "PSW";
+			break;
+		case Odad:
+			rp = "SP";
+			break;
+		}
+	}
+	return fmtprint(fmt, "%s %s", opstr(insn->op), rp);
+}
+
+static int
+dasaddr(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s %#.4x", opstr(insn->op), insn->addr);
+}
+
+static int
+dasrpimm(Fmt *fmt, Insn *insn)
+{
+	return fmtprint(fmt, "%s %s, #$0x%.2uhhx%.2uhhx", opstr(insn->op),
+		insn->rp == 3 ? "SP" : rpnam(insn->rp), insn->imm1, insn->imm);
+}
+
+static int
+dec0(Insn*, uchar*, long)
+{
+	return 1;
+}
+
+static int
+decr00000xxx(Insn *insn, uchar *mem, long)
+{
+	insn->r1 = mem[0]&0x7;
+	return 1;
+}
+
+static int
+decr00xxx000(Insn *insn, uchar *mem, long)
+{
+	insn->r1 = (mem[0]>>3)&0x7;
+	return 1;
+}
+
+static int
+decrimm(Insn *insn, uchar *mem, long len)
+{
+	if(len < 2)
+		return 0;
+	insn->r1 = (mem[0]>>3)&0x7;
+	insn->imm = mem[1];
+	return 2;
+}
+
+static int
+decimm(Insn *insn, uchar *mem, long len)
+{
+	if(len < 2)
+		return 0;
+	insn->imm = mem[1];
+	return 2;
+}
+
+static int
+decaddr(Insn *insn, uchar *mem, long len)
+{
+	if(len < 3)
+		return 0;
+	insn->addr = (u8int)mem[1]|((u16int)mem[2])<<8;
+	return 3;
+}
+
+static int
+decrr(Insn *insn, uchar *mem, long)
+{
+	insn->r1 = (mem[0]>>3)&0x7;
+	insn->r2 = mem[0]&0x7;
+	if(insn->r1 == M && insn->r2 == M)
+		insn->op = Onop;
+	return 1;
+}
+
+static int
+decrp(Insn *insn, uchar *mem, long)
+{
+	insn->rp = (mem[0]>>4)&0x3;
+	return 1;
+}
+
+static int
+decrpimm(Insn *insn, uchar *mem, long len)
+{
+	if(len < 3)
+		return 0;
+	insn->rp = (mem[0]>>4)&0x3;
+	insn->imm = mem[1];
+	insn->imm1 = mem[2];
+	return 3;
+}
+
+int
+insnfmt(Fmt *fmt)
+{
+	Insn *insn;
+
+	insn = va_arg(fmt->args, Insn*);
+	if(insn->op < 0 || insn->op >= nelem(isa))
+		return fmtprint(fmt, "XXX");
+	return insntypes[isa[insn->op].type].das(fmt, insn);
+}
+
+int
+insnlen(u8int op)
+{
+	if(op >= nelem(isa))
+		return 0;
+	return insntypes[isa[op].type].len;
+}
+
+int
+decodeop(u8int b)
+{
+	if((b&0xc0) == 0x40)
+		return Omov;
+	switch(b){
+	case 0x0f:	return Orrc;
+	case 0x1f:	return Orar;
+	case 0x27:	return Odaa;
+	case 0x2f:	return Ocma;
+	case 0x3f:	return Ocmc;
+	case 0xc0:	return Ornz;
+	case 0xc2:	return Ojnz;
+	case 0xc3:	return Ojmp;
+	case 0xc4:	return Ocz;
+	case 0xc6:	return Oadi;
+	case 0xc8:	return Orz;
+	case 0xc9:	return Oret;
+	case 0xca:	return Ojz;
+	case 0xcc:	return Ocz;
+	case 0xcd:	return Ocall;
+	case 0xd0:	return Ornc;
+	case 0xd2:	return Ojnc;
+	case 0xd3:	return Oout;
+	case 0xd4:	return Ocnc;
+	case 0xd6:	return Osui;
+	case 0xd8:	return Orc;
+	case 0xda:	return Ojc;
+	case 0xdb:	return Oin;
+	case 0xde:	return Osbi;
+	case 0xe0:	return Orpo;
+	case 0xe2:	return Ojpo;
+	case 0xe3:	return Oxthl;
+	case 0xe6:	return Oani;
+	case 0xe9:	return Opchl;
+	case 0xea:	return Ojpe;
+	case 0xeb:	return Oxchg;
+	case 0xec:	return Ocpe;
+	case 0xee:	return Oxri;
+	case 0xf0:	return Orp;
+	case 0xf2:	return Ojp;
+	case 0xf3:	return Odi;
+	case 0xf6:	return Oori;
+	case 0xf8:	return Orm;
+	case 0xfa:	return Ojm;
+	case 0xfb:	return Oei;
+	case 0xfc:	return Ocm;
+	case 0xfe:	return Ocpi;
+	}
+	if((b&0x80) != 0){
+		switch(b&0xf8){
+		case 0x80:	return Oadd;
+		case 0x88:	return Oadc;
+		case 0x90:	return Osub;
+		case 0x98:	return Osbb;
+		case 0xa0:	return Oana;
+		case 0xa8:	return Oxra;
+		case 0xb0:	return Oora;
+		case 0xb8:	return Ocmp;
+		}
+		switch(b&0x7){
+		case 0x1:	return Opop;
+		case 0x5:
+			switch(b&0xf){
+			case 0x5:	return Opush;
+			}
+			break;
+		case 0x7:	return Orst;
+		}
+	}else{
+		if((b&0xc0) == 0x40)
+			return Omov;
+		switch(b&0x0f){
+		case 0x00:
+			switch(b){
+			case 0x00:	return Onop;
+			case 0x10:	return Onop;
+			case 0x20:	return Orim;
+			case 0x30:	return Osim;
+			}
+			break;
+		case 0x01:	return Olxi;
+		case 0x02:
+			switch((b>>5)&1){
+			case 0:	return Ostax;
+			case 1:
+				switch(b){
+				case 0x22:	return Oshld;
+				case 0x32:	return Osta;
+				}
+				break;
+			}
+			break;
+		case 0x03:	return Oinx;
+		case 0x07:	return Orlc;
+		case 0x08:	return Onop;
+		case 0x09:	return Odad;
+		case 0x0a:
+			switch((b>>5)&1){
+			case 0:	return Oldax;
+			case 1:	return Olda;
+			}
+			break;
+		case 0x0b:	return Odcx;
+		}
+		switch(b&0x07){
+		case 0x04:	return Oinr;
+		case 0x05:	return Odcr;
+		case 0x06:	return Omvi;
+		}
+	}
+	return -1;
+}
+
+int
+decodeinsn(Insn *insn, uchar *mem, long len)
+{
+	if(len < 1)
+		return 0;
+	if(insn->op == -1)
+		return -1;
+	return insntypes[isa[insn->op].type].dec(insn, mem, len);
+}
+
+static int
+decode(Insn *insn, uchar *mem, long len)
+{
+	if(len < 1)
+		return 0;
+	insn->op = decodeop(mem[0]);
+	return decodeinsn(insn, mem, len);
+}
+
+int
+das(uchar *mem, long mlen)
+{
+	int n;
+	Insn insn;
+
+	if((n = decode(&insn, mem, mlen)) < 0)
+		return -1;
+	print("\t%I\n", &insn);
+	return n;
+}
+
+int
+dasfile(char *file)
+{
+	Biobuf *r;
+	Insn insn;
+	int buflen, n;
+	u16int addr;
+	uchar buf[1024];
+	uchar *bp;
+
+	r = Bopen(file, OREAD);
+	if(r == nil)
+		return -1;
+	addr = 0;
+	buflen = 0;
+	bp = buf;
+	for(;;){
+		memmove(buf, bp, buflen);
+		bp = buf;
+		n = Bread(r, buf + buflen, sizeof(buf) - buflen);
+		if(n < 0){
+			Bterm(r);
+			return -1;
+		}
+		if(n == 0){
+			Bterm(r);
+			break;
+		}
+		buflen += n;
+		while(buflen > 0){
+			n = decode(&insn, bp, buflen);
+			if(n == 0)
+				break;
+			if(n < 0){
+				print("%#.4ux\t???\t%#.2uhhx\n", addr, bp[0]);
+				addr++;
+				bp++;
+				buflen--;
+			}else{
+				print("%#.4ux\t%I\n", addr, &insn);
+				addr += n;
+				bp += n;
+				buflen -= n;
+			}
+		}
+	}
+	while(buflen > 0){
+		print("%#.4x\t???\t%#.2x\n", addr, buf[0]);
+		addr++;
+		bp++;
+		buflen--;
+	}
+	return 0;
+}
+
+void
+cpuexec(CPU *cpu, Insn *insn)
+{
+	if(isa[insn->op].exec == nil){
+		fprint(2, "%s (%#.2uhhx) not implemented!\n", opstr(insn->op), insn->op);
+		trap();
+	}
+	itrace(opstr(insn->op));
+	isa[insn->op].exec(cpu, insn);
+}
+
+static u16int
+rpair(CPU *cpu, u8int rp)
+{
+	switch(rp){
+	case BC: return cpu->r[B]<<8 | cpu->r[C];
+	case DE: return cpu->r[D]<<8 | cpu->r[E];
+	case HL: return cpu->r[H]<<8 | cpu->r[L];
+	}
+	fatal("unknown register pair %d", rp);
+	return 0;
+}
+
+static void
+wpair(CPU *cpu, u8int rp, u16int x)
+{
+	cpu->r[(rp<<1)+B] = x>>8;
+	cpu->r[(rp<<1)+B+1] = x;
+}
+
+static void
+Xnop(CPU*, Insn*)
+{
+}
+
+static void
+Xjmp(CPU *cpu, Insn *insn)
+{
+	cpu->PC = insn->addr;
+}
+
+static void
+Xlxi(CPU *cpu, Insn *insn)
+{
+	if(insn->rp == 3)
+		cpu->SP = insn->imm1<<8|insn->imm;
+	else
+		wpair(cpu, insn->rp, insn->imm1<<8|insn->imm);
+}
+
+static void
+Xmvi(CPU *cpu, Insn *insn)
+{
+	u16int addr;
+
+	if(insn->r1 == M){
+		addr = rpair(cpu, HL);
+		memwrite(addr, insn->imm);
+	}else{
+		cpu->r[insn->r1] = insn->imm;
+	}
+}
+
+static void
+Xcall(CPU *cpu, Insn *insn)
+{
+	push16(cpu, cpu->PC);
+	cpu->PC = insn->addr;
+}
+
+static void
+Xldax(CPU *cpu, Insn *insn)
+{
+	cpu->r[A] = memread(rpair(cpu, insn->rp));
+}
+
+static void
+Xmov(CPU *cpu, Insn *insn)
+{
+	if(insn->r2 == M && insn->r1 == M)
+		return;
+	if(insn->r2 == M)
+		cpu->r[insn->r1] = memread(rpair(cpu, HL));
+	else if(insn->r1 == M)
+		memwrite(rpair(cpu, HL), cpu->r[insn->r2]);
+	else
+		cpu->r[insn->r1] = cpu->r[insn->r2];
+}
+
+static void
+Xinx(CPU *cpu, Insn *insn)
+{
+	wpair(cpu, insn->rp, rpair(cpu, insn->rp) + 1);
+}
+
+static void
+Xdcr(CPU *cpu, Insn *insn)
+{
+	u16int a, x;
+
+	if(insn->r1 == M){
+		a = rpair(cpu, HL);
+		x = memread(a);
+	}else{
+		a = 0;
+		x = cpu->r[insn->r1];
+	}
+
+	if(--x == 0)
+		cpu->flg |= Fzero;
+	cpu->flg &= ~Fsign;
+	if((x&0x80) != 0)
+		cpu->flg |= Fsign;
+
+	if(insn->r1 == M)
+		memwrite(a, x);
+	else
+		cpu->r[insn->r1] = x;
+}
+
+static void
+Xret(CPU *cpu, Insn*)
+{
+	cpu->PC = pop16(cpu);
+}
+
+static void
+Xdad(CPU *cpu, Insn *insn)
+{
+	u32int x;
+
+	if(insn->rp == 3){
+		x = cpu->SP;
+	}else{
+		x = rpair(cpu, insn->rp);
+	}
+	x += rpair(cpu, HL);
+	if(x>>16 > 0)
+		cpu->flg |= Fcarry;
+	//else
+		//cpu->flg &= ~Fcarry;
+	wpair(cpu, HL, x);
+}
+
+static void
+Xsta(CPU *cpu, Insn *insn)
+{
+	memwrite(insn->addr, cpu->r[A]);
+}
+
+static void
+Xxra(CPU *cpu, Insn *insn)
+{
+	u8int x;
+
+	if(insn->r1 == M)
+		x = memread(rpair(cpu, HL));
+	else
+		x = cpu->r[insn->r1];
+
+	cpu->r[A] ^= x;
+
+	if(cpu->r[A] == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((cpu->r[A] & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~(Fcarry|Fhcarry);
+}
+
+void
+iow(u16int a, u8int v)
+{
+}
+
+static void
+Xout(CPU *cpu, Insn *insn)
+{
+	iow(insn->imm<<8|insn->imm, cpu->r[A]);
+}
+
+static void
+Xora(CPU *cpu, Insn *insn)
+{
+	u8int x;
+
+	if(insn->r1 == M)
+		x = memread(rpair(cpu, HL));
+	else
+		x = cpu->r[insn->r1];
+
+	cpu->r[A] |= x;
+
+	if(cpu->r[A] == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((cpu->r[A] & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~(Fcarry|Fhcarry);
+}
+
+static void
+Xlda(CPU *cpu, Insn *insn)
+{
+	cpu->r[A] = memread(insn->addr);
+}
+
+static void
+Xana(CPU *cpu, Insn *insn)
+{
+	u8int x;
+
+	if(insn->r1 == M)
+		x = memread(rpair(cpu, HL));
+	else
+		x = cpu->r[insn->r1];
+
+	cpu->r[A] &= x;
+
+	if(cpu->r[A] == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((cpu->r[A] & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~(Fcarry|Fhcarry);
+}
+
+static void
+Xpush(CPU *cpu, Insn *insn)
+{
+	if(insn->rp == 3){
+		push8(cpu, cpu->r[A]);
+		push8(cpu, cpu->flg);
+	}else
+		push16(cpu, rpair(cpu, insn->rp));
+}
+
+static void
+Xpop(CPU *cpu, Insn *insn)
+{
+	if(insn->rp == 3){
+		cpu->flg = pop8(cpu);
+		cpu->r[A] = pop8(cpu);
+	}else
+		wpair(cpu, insn->rp, pop16(cpu));
+}
+
+static void
+Xxchg(CPU *cpu, Insn*)
+{
+	u16int x;
+
+	x = rpair(cpu, HL);
+	wpair(cpu, HL, rpair(cpu, DE));
+	wpair(cpu, DE, x);
+}
+
+static void
+Xinr(CPU *cpu, Insn *insn)
+{
+	u8int x;
+	u16int a;
+
+	if(insn->r1 == M){
+		a = memread(rpair(cpu, HL));
+		x = memread(a) + 1;
+		memwrite(a, x);
+	}else{
+		x = cpu->r[insn->r1] + 1;
+		cpu->r[insn->r1] = x;
+	}
+
+	if(x == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((x & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~Fhcarry;
+}
+
+static void
+Xani(CPU *cpu, Insn *insn)
+{
+	cpu->r[A] &= insn->imm;
+
+	if(cpu->r[A] == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((cpu->r[A] & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~(Fcarry|Fhcarry);
+}
+
+static void
+Xrar(CPU *cpu, Insn*)
+{
+	u8int ocarry;
+
+	ocarry = (cpu->flg&Fcarry) != 0;
+	if((cpu->r[A]&1) != 0)
+		cpu->flg |= Fcarry;
+	else
+		cpu->flg &= ~Fcarry;
+	cpu->r[A] = ocarry<<7|((cpu->r[A]>>1)&0x7f);
+}
+
+static void
+Xori(CPU *cpu, Insn *insn)
+{
+	cpu->r[A] |= insn->imm;
+
+	if(cpu->r[A] == 0)
+		cpu->flg |= Fzero;
+	else
+		cpu->flg &= ~Fzero;
+
+	if((cpu->r[A] & 0x80) != 0)
+		cpu->flg |= Fsign;
+	else
+		cpu->flg &= ~Fsign;
+
+	cpu->flg &= ~(Fcarry|Fhcarry);
+}
+
+static void
+Xcmp(CPU *cpu, Insn *insn)
+{
+	if(cpu->r[A] == insn->r1)
+		cpu->flg |= Fzero;
+	else{
+		cpu->flg &= ~Fzero;
+		if(cpu->r[A] < insn->r1)
+			cpu->flg |= Fcarry;
+		else
+			cpu->flg &= ~Fcarry;
+	}
+}
+
+static void
+Xrlc(CPU *cpu, Insn*)
+{
+	u8int ncarry;
+
+	ncarry = (cpu->r[A]&0x80) != 0;
+	if(ncarry != 0)
+		cpu->flg |= Fcarry;
+	else
+		cpu->flg &= ~Fcarry;
+	cpu->r[A] = ((cpu->r[A]<<1)&0xfe)|ncarry;
+}
+
+static void
+Xrrc(CPU *cpu, Insn*)
+{
+	u8int ncarry;
+
+	ncarry = cpu->r[A]&1;
+	if(ncarry != 0)
+		cpu->flg |= Fcarry;
+	else
+		cpu->flg &= ~Fcarry;
+	cpu->r[A] = ncarry<<7|((cpu->r[A]>>1)&0x7f);
+}
+
+static void
+Xdcx(CPU *cpu, Insn *insn)
+{
+	if(insn->rp == 3)
+		cpu->SP--;
+	else
+		wpair(cpu, insn->rp, rpair(cpu, insn->rp) - 1);
+}
+
+static void
+Xstax(CPU *cpu, Insn *insn)
+{
+	memwrite(rpair(cpu, insn->rp), cpu->r[A]);
+}
+
+static void
+Xcpi(CPU *cpu, Insn *insn)
+{
+	if(cpu->r[A] == insn->imm)
+		cpu->flg |= Fzero;
+	else{
+		cpu->flg &= ~Fzero;
+		if(cpu->r[A] < insn->imm)
+			cpu->flg |= Fcarry;
+		else
+			cpu->flg &= ~Fcarry;
+	}
+}
+
+static void
+psz(CPU *cpu, int f)
+{
+	u8int x, p;
+
+	if(cpu->r[A] == 0){
+		if(f & Fzero) cpu->flg |= Fzero;
+		if(f & Fsign) cpu->flg &= ~Fsign;
+	}else{
+		if(f & Fzero) cpu->flg &= ~Fzero;
+		if((cpu->r[A] & 0x80) != 0)
+			if(f & Fsign) cpu->flg |= Fsign;
+	}
+
+	if(f & Fparity){
+		x = cpu->r[A];
+		p = 0;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1); x >>= 1;
+		p += (x&1);
+		if((p & 1) != 0)
+			cpu->flg |= Fparity;
+		else
+			cpu->flg &= ~Fparity;
+	}
+}
+
+static void
+Xadi(CPU *cpu, Insn *insn)
+{
+	u16int x;
+
+	x = cpu->r[A] + insn->imm;
+	if((x>>8) > 0)
+		cpu->flg |= Fcarry;
+	else
+		cpu->flg &= ~Fcarry;
+	cpu->r[A] = x;
+	psz(cpu, Fparity|Fsign|Fzero);
+}
+
+static void
+Xei(CPU *cpu, Insn*)
+{
+	cpu->intr |= Ienabled;
+}
+
+static void
+Xdi(CPU *cpu, Insn*)
+{
+	cpu->intr &= ~Ienabled;
+}
+
+static void
+Xin(CPU *cpu, Insn*)
+{
+	cpu->r[A] = 0;
+}
+
+static void
+Xjc(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fcarry) != 0)
+		cpu->PC = insn->addr;
+}
+
+static void
+Xjz(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fzero) != 0)
+		cpu->PC = insn->addr;
+}
+
+static void
+Xjnc(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fcarry) == 0)
+		cpu->PC = insn->addr;
+}
+
+static void
+Xjnz(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fzero) == 0)
+		cpu->PC = insn->addr;
+}
+
+static void
+Xrc(CPU *cpu, Insn*)
+{
+	if((cpu->flg&Fcarry) != 0)
+		cpu->PC = pop16(cpu);
+}
+
+static void
+Xrnc(CPU *cpu, Insn*)
+{
+	if((cpu->flg&Fcarry) == 0)
+		cpu->PC = pop16(cpu);
+}
+
+static void
+Xrz(CPU *cpu, Insn*)
+{
+	if((cpu->flg&Fzero) != 0)
+		cpu->PC = pop16(cpu);
+}
+
+static void
+Xrnz(CPU *cpu, Insn*)
+{
+	if((cpu->flg&Fzero) == 0)
+		cpu->PC = pop16(cpu);
+}
+
+static void
+Xsui(CPU *cpu, Insn *insn)
+{
+	u8int a, b;
+	u16int x;
+
+	a = cpu->r[A];
+	b = -insn->imm;
+	if((((a&0xf)+(b&0xf))&0x10) != 0)
+		cpu->flg |= Fhcarry;
+	else
+		cpu->flg &= Fhcarry;
+	x = a + b;
+	if((x&0x100) != 0)
+		cpu->flg &= ~Fcarry;
+	else
+		cpu->flg |= Fcarry;
+	cpu->r[A] = x;
+	psz(cpu, Fparity|Fsign|Fzero);
+}
+
+static void
+Xxthl(CPU *cpu, Insn*)
+{
+	u8int x;
+
+	x = memread(cpu->SP+0);
+	memwrite(cpu->SP+0, cpu->r[L]);
+	cpu->r[L] = x;
+	x = memread(cpu->SP+1);
+	memwrite(cpu->SP+1, cpu->r[H]);
+	cpu->r[H] = x;
+}
+
+static void
+Xpchl(CPU *cpu, Insn*)
+{
+	cpu->PC = rpair(cpu, HL);
+}
+
+static void
+Xcz(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fzero) != 0){
+		push16(cpu, cpu->PC);
+		cpu->PC = insn->addr;
+	}
+}
+
+static void
+Xdaa(CPU *cpu, Insn*)
+{
+	u16int x;
+
+	if((cpu->flg&Fhcarry) != 0 || cpu->r[A] > 9){
+		x = (cpu->r[A]&0xf)+6;
+		if(x>>4 > 0)
+			cpu->flg |= Fhcarry;
+		else
+			cpu->flg &= ~Fhcarry;
+		cpu->r[A] = (cpu->r[A]&0xf0)|(x&0x0f);
+	}
+
+	if((cpu->flg&Fcarry) != 0 || (cpu->r[A]>>4) > 9){
+		x = (cpu->r[A]>>4)+6;
+		if(x>>4 > 0)
+			cpu->flg |= Fcarry;
+		else
+			cpu->flg &= ~Fcarry;
+		cpu->r[A] = (x<<4)|(cpu->r[A]&0xf);
+	}
+}
+
+static void
+Xjm(CPU *cpu, Insn *insn)
+{
+	if((cpu->flg&Fsign) != 0)
+		cpu->PC = insn->addr;
+}
+
+static void
+Xsbi(CPU *cpu, Insn *insn)
+{
+	insn->imm = -insn->imm;
+	Xadi(cpu, insn);
+}
+
+static void
+Xshld(CPU *cpu, Insn *insn)
+{
+	memwrite(insn->addr+0, cpu->r[L]);
+	memwrite(insn->addr+1, cpu->r[H]);
+}
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,202 @@
+typedef struct CPU CPU;
+typedef struct Insn Insn;
+typedef struct ISA ISA;
+typedef struct InsnType InsnType;
+
+struct CPU
+{
+	u8int flg;
+	u8int r[8];
+	u16int SP;
+	u16int PC;
+	u8int intr;
+};
+
+enum{
+	B,
+	C,
+	D,
+	E,
+	H,
+	L,
+	M, /* dummy */
+	A,
+};
+
+enum{
+	BC,
+	DE,
+	HL,
+};
+
+enum{
+	Fcarry = 1<<0,
+	Fparity = 1<<2,
+	Fhcarry = 1<<4,
+	Fzero = 1<<6,
+	Fsign = 1<<7,
+};
+
+enum{
+	Imask = 7<<0,
+	Ienabled = 1<<3,
+	Ipending = 7<<4,
+};
+
+struct Insn
+{
+	u16int pc;
+	s8int op;
+	u8int r1;
+	u8int r2;
+	u8int rp;
+	u16int addr;
+	u8int imm;
+	u8int imm1;
+};
+
+enum{
+	Oadc,
+	Oadd,
+	Oadi,
+	Oana,
+	Oani,
+	Ocall,
+	Ocm,
+	Ocma,
+	Ocmc,
+	Ocmp,
+	Ocnc,
+	Ocpe,
+	Ocpi,
+	Ocz,
+	Odaa,
+	Odad,
+	Odcr,
+	Odcx,
+	Odi,
+	Oei,
+	Oin,
+	Oinr,
+	Oinx,
+	Ojc,
+	Ojm,
+	Ojmp,
+	Ojnc,
+	Ojnz,
+	Ojp,
+	Ojpe,
+	Ojpo,
+	Ojz,
+	Olda,
+	Oldax,
+	Olxi,
+	Omov,
+	Omvi,
+	Onop,
+	Oora,
+	Oori,
+	Oout,
+	Opchl,
+	Opop,
+	Opush,
+	Orar,
+	Orc,
+	Oret,
+	Orim,
+	Orlc,
+	Orm,
+	Ornc,
+	Ornz,
+	Orp,
+	Orpo,
+	Orrc,
+	Orst,
+	Orz,
+	Osbb,
+	Osbi,
+	Oshld,
+	Osim,
+	Osta,
+	Ostax,
+	Osub,
+	Osui,
+	Oxchg,
+	Oxra,
+	Oxri,
+	Oxthl,
+};
+
+enum
+{
+	T0,
+	Taddr,
+	Timm,
+	Tr012,
+	Tr345,
+	Trimm,
+	Trp,
+	Trpimm,
+	Trr,
+};
+
+struct ISA
+{
+	char *opstr;
+	int type;
+	void (*exec)(CPU*, Insn*);
+};
+
+struct InsnType
+{
+	int len;
+	int (*das)(Fmt*, Insn*);
+	int (*dec)(Insn*, uchar*, long);
+};
+
+enum{
+	ROMSZ = 8*1024,
+	RAMSZ = 1*1024,
+	VIDSZ = 7*1024,
+	MEMSZ = ROMSZ+RAMSZ+VIDSZ,
+};
+
+extern u8int mem[MEMSZ];
+extern u8int *rom;
+extern u8int *ram;
+extern u8int *vid;
+
+enum
+{
+	Tpush,
+	Tpop,
+};
+
+typedef struct TraceOp TraceOp;
+struct TraceOp
+{
+	u16int addr;
+	uchar op;
+};
+
+typedef struct BrkPt BrkPt;
+struct BrkPt
+{
+	u16int addr;
+	uchar enabled;
+};
+
+enum{
+	MAXBRKPTS = 10,
+	MAXTRACEOPS = 10,
+};
+
+extern CPU ocpu, cpu;
+extern Insn insn;
+extern int debug;
+extern int tracing;
+extern int ntraceops;
+extern TraceOp traceops[MAXTRACEOPS];
+extern int nbrkpts;
+extern BrkPt brkpts[MAXBRKPTS];
+extern jmp_buf trapjmp;
--- /dev/null
+++ b/debug.c
@@ -1,0 +1,139 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+jmp_buf trapjmp;
+
+static void
+deltrap(void *u, char *msg)
+{
+	if(strcmp(msg, "interrupt") == 0){
+		notejmp(u, trapjmp, 1);
+		noted(NCONT);
+	}
+	noted(NDFLT);
+}
+
+void
+trapinit(void)
+{
+	static int init = 0;
+	if(init == 0){
+		notify(deltrap);
+		init++;
+	}
+}
+
+void
+trap(void)
+{
+	longjmp(trapjmp, 1);
+}
+
+char*
+rnam(u8int r)
+{
+	switch(r){
+	case B: return "B";
+	case C: return "C";
+	case D: return "D";
+	case E: return "E";
+	case H: return "H";
+	case L: return "L";
+	case M: return "M";
+	case A: return "A";
+	}
+	return "X";
+}
+
+char*
+rpnam(u8int r)
+{
+	switch(r){
+	case BC: return "BC";
+	case DE: return "DE";
+	case HL: return "HL";
+	}
+	return "XX";
+}
+
+void
+dumpinst(void)
+{
+	fprint(2, "op: %#.2x [%#.2x %#.2x]\n",
+		mem[insn.pc+0], mem[insn.pc+1], mem[insn.pc+2]);
+}
+
+static char
+flagchar(int f)
+{
+	switch(1<<f){
+	case Fcarry:	return 'C';
+	case Fparity:	return 'P';
+	case Fhcarry:	return 'H';
+	case Fzero:		return 'Z';
+	case Fsign:		return 'S';
+	}
+	return 0;
+}
+
+void
+dumpregs(void)
+{
+	int i;
+
+	fprint(2, "A=%#.2x\n", cpu.r[A]);
+	fprint(2, "B=%#.2x\n", cpu.r[B]);
+	fprint(2, "C=%#.2x\n", cpu.r[C]);
+	fprint(2, "D=%#.2x\n", cpu.r[D]);
+	fprint(2, "E=%#.2x\n", cpu.r[E]);
+	fprint(2, "H=%#.2x\n", cpu.r[H]);
+	fprint(2, "L=%#.2x\n", cpu.r[L]);
+	fprint(2, "F=%#.2x", cpu.flg);
+	if(cpu.flg != 0){
+		fprint(2, " (");
+		for(i = 0; i < 8; i++)
+			if((cpu.flg&1<<i) != 0)
+				fprint(2, "%c", flagchar(i));
+		fprint(2, ")");
+	}
+	fprint(2, "\n");
+	fprint(2, "PC=%#.4x\n", cpu.PC);
+	fprint(2, "SP=%#.4x\n", cpu.SP);
+}
+
+void
+dumpmem(u16int s, u16int e)
+{
+	while(s < e){
+		fprint(2, "%.4x: %.2x\n", s, mem[s]);
+		s++;
+	}
+}
+
+void
+itrace0(char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	fprint(2, "%#.4x ", insn.pc);
+	vfprint(2, fmt, args);
+	fprint(2, "\n");
+	va_end(args);
+}
+
+void
+fatal(char *fmt, ...)
+{
+	va_list args;
+
+	va_start(args, fmt);
+	vfprint(2, fmt, args);
+	fprint(2, "\n");
+	va_end(args);
+	dumpinst();
+	dumpregs();
+	exits("fatal");
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,36 @@
+/* debug */
+void	dumpregs(void);
+void	dumpmem(u16int, u16int);
+void	fatal(char *fmt, ...);
+void	itrace0(char *fmt, ...);
+char*	rnam(u8int);
+char*	rpnam(u8int);
+
+#define dprint(...) if(debug)fprint(2, __VA_ARGS__)
+#define itrace(...) if(tracing>0)itrace0(__VA_ARGS__)
+
+/* disassembler */
+#pragma	   varargck    type  "I"   Insn*
+int	insnfmt(Fmt*);
+int	das(uchar*, long);
+int dasfile(char*);
+
+/* isa */
+int	decodeop(u8int);
+int	decodeinsn(Insn*, uchar*, long);
+int	insnlen(u8int);
+void	cpuexec(CPU*, Insn*);
+
+/* traps */
+void	trapinit(void);
+void	trap(void);
+#define wastrap() setjmp(trapjmp)
+
+/* memory */
+u8int	memread(u16int);
+void	memwrite(u16int, u8int);
+u8int	ifetch(CPU*);
+u8int	pop8(CPU*);
+u16int	pop16(CPU*);
+void	push8(CPU*, u8int);
+void	push16(CPU*, u16int);
--- /dev/null
+++ b/mem.c
@@ -1,0 +1,62 @@
+#include <u.h>
+#include <libc.h>
+#include "dat.h"
+#include "fns.h"
+
+u8int
+memread(u16int a)
+{
+	if(a >= MEMSZ){
+		fprint(2, "memread failed addr=%#.4x op=%#0.2x pc=%#.4x\n", a, insn.op, ocpu.PC);
+		trap();
+	}
+	return mem[a];
+}
+
+void
+memwrite(u16int a, u8int x)
+{
+	if(a < ROMSZ || a >= MEMSZ){
+		fprint(2, "write failed addr=%#.4x op=%#0.2x pc=%#.4x\n", a, insn.op, ocpu.PC);
+		trap();
+	}
+	mem[a] = x;
+}
+
+u8int
+ifetch(CPU *cpu)
+{
+	if(cpu->PC >= ROMSZ){
+		fprint(2, "ifetch failed pc=%#.4x\n", cpu->PC);
+		trap();
+	}
+	return memread(cpu->PC++);
+}
+
+u8int
+pop8(CPU *cpu)
+{
+	return memread(cpu->SP++);
+}
+
+u16int
+pop16(CPU *cpu)
+{
+	u16int x;
+
+	x = memread(cpu->SP++);
+	return x | memread(cpu->SP++)<<8;
+}
+
+void
+push8(CPU *cpu, u8int x)
+{
+	memwrite(--cpu->SP, x);
+}
+
+void
+push16(CPU *cpu, u16int x)
+{
+	memwrite(--cpu->SP, x>>8);
+	memwrite(--cpu->SP, x&0xff);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,14 @@
+</$objtype/mkfile
+
+TARG=8080
+OFILES=\
+	8080.$O\
+	mem.$O\
+	debug.$O\
+	das.$O\
+
+HFILES=\
+	dat.h\
+	fns.h\
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/screen.c
@@ -1,0 +1,98 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <thread.h>
+
+enum{
+	Width = 224,
+	Height = 256,
+};
+
+uchar videomem[Width*Height/8];
+
+static void
+usage(void)
+{
+	fprint(2, "usage: %s\n", argv0);
+	exits("usage");
+}
+
+static void
+resize(int w, int h)
+{
+	int fd;
+
+	if((fd = open("/dev/wctl", OWRITE)) < 0)
+		return;
+	fprint(fd, "resize -dx %d -dy %d", w, h);
+	close(fd);
+}
+
+static void
+scanout(Channel *c, uchar *vmem)
+{
+	int i;
+	uchar *p;
+	Image *line;
+	Rectangle r;
+
+	line = allocimage(display, Rect(0,0,Width,1), GREY1, 0, DNofill);
+
+	for(;;){
+		p = vmem;
+		r.min = screen->r.min;
+		r.max = Pt(screen->r.max.x, screen->r.min.y+1);
+		for(i = 0; i < Height/2; i++){
+			loadimage(line, Rect(0,0,Width,1), p, Width/8);
+			draw(screen, r, line, nil, ZP);
+			r = rectaddpt(r, Pt(0, 1));
+			p += Width/8;
+		}
+		flushimage(display, 1);
+		if(c != nil)
+			nbsendul(c, 0);
+		for(; i < Height; i++){
+			loadimage(line, Rect(0,0,Width,1), p, Width/8);
+			draw(screen, r, line, nil, ZP);
+			r = rectaddpt(r, Pt(0, 1));
+			p += Width/8;
+		}
+		flushimage(display, 1);
+		if(c != nil)
+			nbsendul(c, 1);
+	}
+}
+
+static void
+initvmem(void)
+{
+	uchar *p;
+	int i;
+
+	p = videomem;
+	srand(time(nil));
+	for(i = 0; i < nelem(videomem); i++)
+		*p++ = rand();
+}
+
+void
+threadmain(int argc, char **argv)
+{
+	ARGBEGIN{
+	default:
+		usage();
+	}ARGEND;
+	if(argc != 0)
+		usage();
+	if(newwindow(nil) < 0)
+		sysfatal("newwindow: %r");
+	resize(Width, Height);
+	if(initdraw(nil, nil, nil) < 0)
+		sysfatal("initdraw: %r");
+	draw(screen, screen->r, display->black, nil, ZP);
+	flushimage(display, 1);
+	initvmem();
+	scanout(nil, videomem);
+	for(;;)
+		sleep(1000);
+}
--- /dev/null
+++ b/test.rc
@@ -1,0 +1,2 @@
+#!/bin/rc
+mk && {echo load; echo run} | 6.out