shithub: riscv

Download patch

ref: a0cc369c15bdd404e1a60b4fd5b3a5156a041f74
parent: c67d0c699f7b52da534f75d3620776997b1e8d52
author: spew <devnull@localhost>
date: Tue Jan 10 06:42:11 EST 2017

games/mix: Add Knuth MIX emulator/assembler

diff: cannot open b/sys/src/games/mix/tests//null: file does not exist: 'b/sys/src/games/mix/tests//null' diff: cannot open b/sys/src/games/mix//null: file does not exist: 'b/sys/src/games/mix//null'
--- /dev/null
+++ b/sys/man/1/mix
@@ -1,0 +1,114 @@
+.TH MIX 1
+.SH NAME
+mix \- 
+.B MIX
+assembler and emulator
+.SH SYNOPSIS
+.B games/mix
+[
+.I file ...
+]
+.SH DESCRIPTION
+.I Mix
+is an assembler and emulator for Donald Knuth's
+fictitious
+.B MIX 
+architecture. The command assembles the named 
+.B MIXAL
+files into memory and then presents a command
+prompt to control an emulated
+.B MIX
+machine.
+.PP
+The following commands are accepted:
+.TP
+.B addr[(a:b)]
+Print the value at
+.I addr.
+An optional field specification is given by
+.I (a:b).
+.TP
+.B a [< file]
+Start the MIXAL assembler. The assembler will begin
+assembling at the address after the last assembled
+instruction. If no file is given, the assembler will
+accept instructions from the console.
+.TP
+.B b addr
+Set or unset a breakpoint at
+.I addr.
+.TP
+.B c
+Resets the
+.B MIX
+machine to a fresh state by clearing all memory
+and registers.
+.TP
+.B d addr
+Disassemble the instruction at
+.I addr.
+.TP
+.B o addr
+Print the alphanumeric
+.B MIX
+word at
+.I addr.
+.TP
+.B o(addr, d)
+Print
+.I d
+alphanumeric mix words starting at
+.I addr.
+.TP
+.B r*[(a:b)]
+Print the value in register
+.I r*
+where * is one of a, x, ax, j, or 1-6. An optional
+field specification is given by
+.I (a:b).
+.TP
+.B s
+Step through one instruction of the emulated
+.B MIX
+machine.
+.TP
+.B g
+Start the emulated
+.B MIX
+machine at the instruction specified by the
+.B END
+pseudo-instruction.
+.TP
+.B x
+Quit the emulator/assembler.
+.PP
+The
+.I addr
+field of the above instructions must be an integer between 0 and 3999
+inclusive. A number-sign (#) starts a comment which extends to the end
+of the line.
+.SH SOURCE
+.B /sys/src/cmd/games/mix
+.SH "SEE ALSO"
+.nf
+Donald Knuth, ``The Art of Computer Programming'', Volume 1. Section 1.3
+.PP
+.B /sys/src/cmd/games/mix/examples
+.SH BUGS
+As opposed to Knuth's specification,
+the
+.B ALF
+pseudo-instruction takes as argument five
+.B MIX
+characters surrounded by quotation marks.
+Unresolved forward references are assembled
+to 0 instead of to a location determined by
+the
+.B END
+psuedo-instruction.
+.PP
+The magnetic tapes and drum units are not
+implemented.
+.PP
+Comments are handled as described above and not
+as Knuth specifies.
--- /dev/null
+++ b/sys/src/games/mix/char.c
@@ -1,0 +1,124 @@
+#include <u.h>
+#include <libc.h>
+#include <avl.h>
+#include <bio.h>
+#include "mix.h"
+
+typedef
+struct Mixchar {
+	Rune r;
+	int m;
+} Mixchar;
+
+static Rune mixtor[] = {
+	' ',
+	'A',
+	'B',
+	'C',
+	'D',
+	'E',
+	'F',
+	'G',
+	'H',
+	'I',
+	L'Δ',
+	'J',
+	'K',
+	'L',
+	'M',
+	'N',
+	'O',
+	'P',
+	'Q',
+	'R',
+	L'Σ',
+	L'Π',
+	'S',
+	'T',
+	'U',
+	'V',
+	'W',
+	'X',
+	'Y',
+	'Z',
+	'0',
+	'1',
+	'2',
+	'3',
+	'4',
+	'5',
+	'6',
+	'7',
+	'8',
+	'9',
+	'.',
+	',',
+	'(',
+	')',
+	'+',
+	'-',
+	'*',
+	'/',
+	'=',
+	'$',
+	'<',
+	'>',
+	'@',
+	';',
+	':',
+	'\''
+};
+
+static Mixchar rtomix[nelem(mixtor)];
+
+static int
+runecmp(void *a, void *b)
+{
+	Rune ra, rb;
+
+	ra = ((Mixchar*)a)->r;
+	rb = ((Mixchar*)b)->r;
+
+	if(ra < rb)
+		return -1;
+	if(ra > rb)
+		return 1;
+	return 0;
+}
+
+void
+cinit(void)
+{
+	int i;
+	Mixchar *a;
+
+	for(i = 0; i < nelem(rtomix); i++) {
+		a = rtomix+i;
+		a->r = mixtor[i];
+		a->m = i;
+	}
+	qsort(rtomix, nelem(rtomix), sizeof(*rtomix), runecmp);
+}
+
+int
+runetomix(Rune r)
+{
+	Mixchar *c, l;
+
+	l.r = r;
+	c = (Mixchar*)bsearch(&l, rtomix, nelem(rtomix), sizeof(*rtomix), runecmp);
+	if(c == nil) {
+		print("Not found!!\n");
+		return -1;
+	}
+
+	return c->m;
+}
+
+Rune
+mixtorune(int m)
+{
+	if(m < nelem(mixtor))
+		return mixtor[m];
+	return -1;
+}
--- /dev/null
+++ b/sys/src/games/mix/mix.c
@@ -1,0 +1,1129 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <avl.h>
+#include "mix.h"
+#include "y.tab.h"
+
+struct Resvd {
+	char *name;
+	long lex;
+	int c;
+	int f;
+} res[] = {
+	{ "NOP",	LOP,	0,	F(0, 5) },
+	{ "ADD",	LOP,	1,	F(0, 5) },
+	{ "FADD",	LOP,	1,	6 },
+	{ "SUB",	LOP,	2,	F(0, 5) },
+	{ "FSUB",	LOP,	2,	6 },
+	{ "MUL",	LOP,	3,	F(0, 5) },
+	{ "FMUL",	LOP,	3,	6 },
+	{ "DIV",	LOP,	4,	F(0, 5) },
+	{ "FDIV",	LOP,	4,	6 },
+	{ "NUM",	LOP,	5,	0 },
+	{ "CHAR",	LOP,	5,	1 },
+	{ "HLT",	LOP,	5,	2 },
+	{ "SLA",	LOP,	6,	0 },
+	{ "SRA",	LOP,	6,	1 },
+	{ "SLAX",	LOP,	6,	2 },
+	{ "SRAX",	LOP,	6,	3 },
+	{ "SLC",	LOP,	6,	4 },
+	{ "SRC",	LOP,	6,	5 },
+	{ "MOVE",	LOP,	7,	1 },
+	{ "LDA",	LOP,	8,	F(0, 5) },
+	{ "LD1",	LOP,	9,	F(0, 5) },
+	{ "LD2",	LOP,	10,	F(0, 5) },
+	{ "LD3",	LOP,	11,	F(0, 5) },
+	{ "LD4",	LOP,	12,	F(0, 5) },
+	{ "LD5",	LOP,	13,	F(0, 5) },
+	{ "LD6",	LOP,	14,	F(0, 5) },
+	{ "LDX",	LOP,	15,	F(0, 5) },
+	{ "LDAN",	LOP,	16,	F(0, 5) },
+	{ "LD1N",	LOP,	17,	F(0, 5) },
+	{ "LD2N",	LOP,	18,	F(0, 5) },
+	{ "LD3N",	LOP,	19,	F(0, 5) },
+	{ "LD4N",	LOP,	20,	F(0, 5) },
+	{ "LD5N",	LOP,	21,	F(0, 5) },
+	{ "LD6N",	LOP,	22,	F(0, 5) },
+	{ "LDXN",	LOP,	23,	F(0, 5) },
+	{ "STA",	LOP,	24,	F(0, 5) },
+	{ "ST1",	LOP,	25,	F(0, 5) },
+	{ "ST2",	LOP,	26,	F(0, 5) },
+	{ "ST3",	LOP,	27,	F(0, 5) },
+	{ "ST4",	LOP,	28,	F(0, 5) },
+	{ "ST5",	LOP,	29,	F(0, 5) },
+	{ "ST6",	LOP,	30,	F(0, 5) },
+	{ "STX",	LOP,	31,	F(0, 5) },
+	{ "STJ",	LOP,	32,	F(0, 2) },
+	{ "STZ",	LOP,	33,	F(0, 5) },
+	{ "JBUS",	LOP,	34, 0 },
+	{ "IOC",	LOP,	35, 0 },
+	{ "IN",	LOP,	36,	0 },
+	{ "OUT",	LOP,	37,	0 },
+	{ "JRED",	LOP,	38,	0 },
+	{ "JMP",	LOP,	39, 0 },
+	{ "JSJ",	LOP,	39, 1 },
+	{ "JOV",	LOP,	39, 2 },
+	{ "JNOV",	LOP,	39, 3 },
+	{ "JL",	LOP,	39,	4 },
+	{ "JE",	LOP,	39,	5 },
+	{ "JG",	LOP,	39,	6 },
+	{ "JGE",	LOP,	39,	7 },
+	{ "JNE",	LOP,	39,	8 },
+	{ "JLE",	LOP,	39,	9 },
+	{ "JAN",	LOP,	40,	0 },
+	{ "JAZ",	LOP,	40,	1 },
+	{ "JAP",	LOP,	40,	2 },
+	{ "JANN",	LOP,	40,	3 },
+	{ "JANZ",	LOP,	40,	4 },
+	{ "JANP",	LOP,	40,	5 },
+	{ "J1N",	LOP,	41,	0 },
+	{ "J1Z",	LOP,	41,	1 },
+	{ "J1P",	LOP,	41,	2 },
+	{ "J1NN",	LOP,	41,	3 },
+	{ "J1NZ",	LOP,	41,	4 },
+	{ "J1NP",	LOP,	41,	5 },
+	{ "J2N",	LOP,	42,	0 },
+	{ "J2Z",	LOP,	42,	1 },
+	{ "J2P",	LOP,	42,	2 },
+	{ "J2NN",	LOP,	42,	3 },
+	{ "J2NZ",	LOP,	42,	4 },
+	{ "J2NP",	LOP,	42,	5 },
+	{ "J3N",	LOP,	43,	0 },
+	{ "J3Z",	LOP,	43,	1 },
+	{ "J3P",	LOP,	43,	2 },
+	{ "J3NN",	LOP,	43,	3 },
+	{ "J3NZ",	LOP,	43,	4 },
+	{ "J3NP",	LOP,	43,	5 },
+	{ "J4N",	LOP,	44,	0 },
+	{ "J4Z",	LOP,	44,	1 },
+	{ "J4P",	LOP,	44,	2 },
+	{ "J4NN",	LOP,	44,	3 },
+	{ "J4NZ",	LOP,	44,	4 },
+	{ "J4NP",	LOP,	44,	5 },
+	{ "J5N",	LOP,	45,	0 },
+	{ "J5Z",	LOP,	45,	1 },
+	{ "J5P",	LOP,	45,	2 },
+	{ "J5NN",	LOP,	45,	3 },
+	{ "J5NZ",	LOP,	45,	4 },
+	{ "J5NP",	LOP,	45,	5 },
+	{ "J6N",	LOP,	46,	0 },
+	{ "J6Z",	LOP,	46,	1 },
+	{ "J6P",	LOP,	46,	2 },
+	{ "J6NN",	LOP,	46,	3 },
+	{ "J6NZ",	LOP,	46,	4 },
+	{ "J6NP",	LOP,	46,	5 },
+	{ "JXN",	LOP,	47,	0 },
+	{ "JXZ",	LOP,	47,	1 },
+	{ "JXP",	LOP,	47,	2 },
+	{ "JXNN",	LOP,	47,	3 },
+	{ "JXNZ",	LOP,	47,	4 },
+	{ "JXNP",	LOP,	47,	5 },
+	{ "INCA",	LOP,	48,	0 },
+	{ "DECA",	LOP,	48,	1 },
+	{ "ENTA",	LOP,	48,	2 },
+	{ "ENNA",	LOP,	48,	3 },
+	{ "INC1",	LOP,	49,	0 },
+	{ "DEC1",	LOP,	49,	1 },
+	{ "ENT1",	LOP,	49,	2 },
+	{ "ENN1",	LOP,	49,	3 },
+	{ "INC2",	LOP,	50,	0 },
+	{ "DEC2",	LOP,	50,	1 },
+	{ "ENT2",	LOP,	50,	2 },
+	{ "ENN2",	LOP,	50,	3 },
+	{ "INC3",	LOP,	51,	0 },
+	{ "DEC3",	LOP,	51,	1 },
+	{ "ENT3",	LOP,	51,	2 },
+	{ "ENN3",	LOP,	51,	3 },
+	{ "INC4",	LOP,	52,	0 },
+	{ "DEC4",	LOP,	52,	1 },
+	{ "ENT4",	LOP,	52,	2 },
+	{ "ENN4",	LOP,	52,	3 },
+	{ "INC5",	LOP,	53,	0 },
+	{ "DEC5",	LOP,	53,	1 },
+	{ "ENT5",	LOP,	53,	2 },
+	{ "ENN5",	LOP,	53,	3 },
+	{ "INC6",	LOP,	54,	0 },
+	{ "DEC6",	LOP,	54,	1 },
+	{ "ENT6",	LOP,	54,	2 },
+	{ "ENN6",	LOP,	54,	3 },
+	{ "INCX",	LOP,	55,	0 },
+	{ "DECX",	LOP,	55,	1 },
+	{ "ENTX",	LOP,	55,	2 },
+	{ "ENNX",	LOP,	55,	3 },
+	{ "CMPA",	LOP,	56,	F(0, 5) },
+	{ "FCMP",	LOP,	56,	6 },
+	{ "CMP1",	LOP,	57,	F(0, 5) },
+	{ "CMP2",	LOP,	58,	F(0, 5) },
+	{ "CMP3",	LOP,	59,	F(0, 5) },
+	{ "CMP4",	LOP,	60,	F(0, 5) },
+	{ "CMP5",	LOP,	61,	F(0, 5) },
+	{ "CMP6",	LOP,	62,	F(0, 5) },
+	{ "CMPX",	LOP,	63,	F(0, 5) },
+	{ "EQU",	LEQU,	-1,	-1 },
+	{ "ORIG",	LORIG,	-1,	-1 },
+	{ "CON",	LCON,	-1,	-1 },
+	{ "ALF",	LALF,	-1,	-1 },
+	{ "END",	LEND,	-1,	-1 },
+	{ "0H",	LHERE,	0,	-1 },
+	{ "1H",	LHERE,	1,	-1 },
+	{ "2H",	LHERE,	2,	-1 },
+	{ "3H",	LHERE,	3,	-1 },
+	{ "4H",	LHERE,	4,	-1 },
+	{ "5H",	LHERE,	5,	-1 },
+	{ "6H",	LHERE,	6,	-1 },
+	{ "7H",	LHERE,	7,	-1 },
+	{ "8H",	LHERE,	8,	-1 },
+	{ "9H",	LHERE,	9,	-1 },
+	{ "0B",	LBACK,	0,	-1 },
+	{ "1B",	LBACK,	1,	-1 },
+	{ "2B",	LBACK,	2,	-1 },
+	{ "3B",	LBACK,	3,	-1 },
+	{ "4B",	LBACK,	4,	-1 },
+	{ "5B",	LBACK,	5,	-1 },
+	{ "6B",	LBACK,	6,	-1 },
+	{ "7B",	LBACK,	7,	-1 },
+	{ "8B",	LBACK,	8,	-1 },
+	{ "9B",	LBACK,	9,	-1 },
+	{ "0F",	LFORW,	0,	-1 },
+	{ "1F",	LFORW,	1,	-1 },
+	{ "2F",	LFORW,	2,	-1 },
+	{ "3F",	LFORW,	3,	-1 },
+	{ "4F",	LFORW,	4,	-1 },
+	{ "5F",	LFORW,	5,	-1 },
+	{ "6F",	LFORW,	6,	-1 },
+	{ "7F",	LFORW,	7,	-1 },
+	{ "8F",	LFORW,	8,	-1 },
+	{ "9F",	LFORW,	9,	-1 },
+};
+
+int mask[] = {
+	MASK1,
+	MASK2,
+	MASK3,
+	MASK4,
+	MASK5
+};
+
+int symcmp(Avl*, Avl*);
+Sym *sym(char*);
+
+void
+main(int argc, char **argv)
+{
+	int go;
+	char **ap;
+
+	go = 0;
+	ARGBEGIN {
+	case 'g': go++; break;
+	} ARGEND
+
+	cinit();
+	sinit();
+	fmtinstall('I', Ifmt);
+	vmstart = -1;
+	for(ap = argv; ap < argv+argc; ap++)
+		asmfile(*ap);
+	repl(go);
+	exits(nil);
+}
+
+void
+sinit(void)
+{
+	struct Resvd *r;
+	Sym *s;
+
+	syms = avlcreate(symcmp);
+	for(r = res; r < res + nelem(res); r++) {
+		s = sym(r->name);
+		s->lex = r->lex;
+		s->opc = r->c;
+		s->f = r->f;
+		avlinsert(syms, s);
+	}
+}
+
+int
+asmfile(char *file)
+{
+	int fd;
+
+	if((fd = open(file, OREAD)) == -1)
+		return -1;
+	Binit(&bin, fd, OREAD);
+	line = 1;
+	filename = file;
+	if(setjmp(errjmp) == 0)
+		yyparse();
+	Bterm(&bin);
+	close(fd);
+	return 0;
+}
+
+int
+unpack(u32int inst, int *apart, int *ipart, int *fpart)
+{
+	int opc;
+
+	opc = V(inst, F(5, 5));
+	*fpart = V(inst, F(4, 4));
+	*ipart = V(inst, F(3, 3));
+	*apart = V(inst, F(0, 2));
+	return opc;
+}
+
+int
+Ifmt(Fmt *f)
+{
+	u32int inst;
+	int i, apart, ipart, fpart, opc, a, b;
+
+	inst = va_arg(f->args, u32int);
+	opc = unpack(inst, &apart, &ipart, &fpart);
+	for(i = 0; i < nelem(res); i++) {
+		if(res[i].c == opc)
+			break;
+	}
+	UNF(a, b, fpart);
+	if(res[i+1].c != opc)
+		return fmtprint(f, "%s\t%d,%d(%d | %d:%d)", res[i].name, apart, ipart, fpart, a, b);
+	while(res[i].c == opc && i < nelem(res)) {
+		if(res[i].f == fpart)
+			return fmtprint(f, "%s\t%d,%d(%d | %d:%d)", res[i].name, apart, ipart, fpart, a, b);
+		i++;
+	}
+	return fmtprint(f, "%d\t%d,%d(%d | %d:%d)", opc, apart, ipart, fpart, a, b);
+}
+
+long
+yylex(void)
+{
+	static Rune buf[11];
+	Rune r, *bp, *ep;
+	static char cbuf[100];
+	int isnum;
+
+	if(yydone)
+		return -1;
+
+Loop:
+	r = Bgetrune(&bin);
+	switch(r) {
+	case Beof:
+		return -1;
+	case '\t':
+	case ' ':
+		goto Loop;
+	case '\n':
+		line++;
+	case '+':
+	case '-':
+	case '*':
+	case ':':
+	case ',':
+	case '(':
+	case ')':
+	case '=':
+		return r;
+	case '/':
+		r = Bgetrune(&bin);
+		if(r == '/')
+			return LSS;
+		else
+			Bungetrune(&bin);
+		return '/';
+	case '"':
+		for(bp = buf; bp < buf+5; bp++) {
+			*bp = Bgetrune(&bin);
+		}
+		if(Bgetrune(&bin) != '"')
+			yyerror("Bad string literal\n");
+		yylval.rbuf = buf;
+		return LSTR;
+	case '#':
+		skipto('\n');
+		line++;
+		return '\n';
+	}
+	bp = buf;
+	ep = buf+nelem(buf)-1;
+	isnum = 1;
+	for(;;) {
+		if(runetomix(r) == -1) 
+			yyerror("Invalid character %C", r);
+		if(bp == ep)
+			yyerror("Symbol or number too long");
+		*bp++ = r;
+		if(isnum && (r >= Runeself || !isdigit(r)))
+			isnum = 0;
+		r = Bgetrune(&bin);
+		switch(r) {
+		case Beof:
+		case '\t':
+		case '\n':
+		case '+':
+		case '-':
+		case '*':
+		case ':':
+		case ',':
+		case '(':
+		case ')':
+		case '=':
+		case ' ':
+		case '/':
+		case '#':
+			Bungetrune(&bin);
+			*bp = '\0';
+			goto End;
+		}
+	}
+End:
+	seprint(cbuf, cbuf+100, "%S", buf);
+	if(isnum) {
+		yylval.lval = strtol(cbuf, nil, 10);
+		return LNUM;
+	}
+	yylval.sym = sym(cbuf);
+	return yylval.sym->lex;
+}
+
+Sym*
+sym(char *name)
+{
+	Sym *s, l;
+
+	l.name = name;
+	s = (Sym*)avllookup(syms, &l);
+	if(s != nil)
+		return s;
+
+	s = emallocz(sizeof(*s) + strlen(name));
+	strcpy(s->nbuf, name);
+	s->name = s->nbuf;
+	s->lex = LSYMREF;
+	avlinsert(syms, s);
+	return s;
+}
+
+int
+symcmp(Avl *a, Avl *b)
+{
+	Sym *sa, *sb;
+
+	sa = (Sym*)a;
+	sb = (Sym*)b;
+	return strcmp(sa->name, sb->name);
+}
+
+void
+skipto(char c)
+{
+	Rune r;
+
+	for(;;) {
+		r = Bgetrune(&bin);
+		if(r != c && r != Beof)
+			continue;
+		return;
+	}
+}
+
+int
+mval(u32int a, int s, u32int m)
+{
+	int sign, val;
+
+	sign = a >> 31;
+	val = a>>s*BITS & m;
+	if(sign)
+		return -val;
+	return val;
+}
+
+int
+V(u32int w, int f)
+{
+	int a, b, d;
+
+	if(f == 0)
+		return 0;
+
+	UNF(a, b, f);
+	if(a > 0)
+		w &= ~SIGNB;
+	else
+		a++;
+
+	d = b - a;
+	if(a > 5 || b > 5 || d < 0 || d > 4)
+		vmerror("Invalid fpart %d", f);
+
+	return mval(w, 5-b, mask[d]);
+}
+
+int
+M(int a, int i)
+{
+	int off, r;
+
+	r = ri[i] & ~(MASK3<<2*BITS);
+	off = i == 0 ? 0 : mval(r, 0, MASK2);
+	return a + off;
+}
+
+void mixfadd(int){}
+
+void
+mixadd(int m, int f)
+{
+	int rval;
+	
+	rval = mval(ra, 0, MASK5);
+	rval += V(cells[m], f);
+	ra = rval < 0 ? -rval|SIGNB : rval;
+	if(ra & OVERB) {
+		ra &= ~OVERB;
+		ot = 1;
+	}
+}
+
+void mixfsub(int){}
+
+void
+mixsub(int m, int f)
+{
+	int rval;
+
+	rval = mval(ra, 0, MASK5);
+	rval -= V(cells[m], f);
+	ra = rval < 0 ? -rval|SIGNB : rval;
+	if(ra & OVERB) {
+		ra &= ~OVERB;
+		ot = 1;
+	}
+}
+
+void mixfmul(int){}
+
+void
+mixmul(int m, int f)
+{
+	vlong rval;
+	int signb;
+
+	rval = mval(ra, 0, MASK5);
+	rval *= V(cells[m], f);
+
+	if(rval < 0) {
+		rval = -rval;
+		signb = SIGNB;
+	} else
+		signb = 0;
+
+	ra = rval>>5*BITS & MASK5 | signb;
+	rx = rval & MASK5 | signb;
+}
+
+void mixfdiv(int){}
+
+void
+mixdiv(int m, int f)
+{
+	vlong rax, quot;
+	u32int xsignb, asignb;
+	int rem, v;
+
+	v = V(cells[m], f);
+	if(v == 0) {
+		ot = 1;
+		return;
+	}
+	rax = ra & MASK5;
+	rax <<= 5 * BITS;
+	rax |= rx & MASK5;
+	if(ra >> 31)
+		rax = -rax;
+
+	quot = rax / v;
+	rem = rax % v;
+
+	if(quot < 0) {
+		quot = -quot;
+		asignb = SIGNB;
+	} else
+		asignb = 0;
+
+	if(rem < 0) {
+		rem = -rem;
+		xsignb = SIGNB;
+	} else
+		xsignb = 0;
+
+	if(quot & ~MASK5)
+		ot = 1;
+
+	ra = quot & MASK5 | asignb;
+	rx = rem & MASK5 | xsignb;
+}
+
+void
+mixnum(void)
+{
+	int i, b;
+	u32int n;
+
+	n = 0;
+	for(i = 0; i < 5; i++) {
+		b = ra>>(4-i)*BITS & MASK1;
+		b %= 10;
+		n = 10*n + b;
+	}
+	for(i = 0; i < 5; i++) {
+		b = rx>>(4-i)*BITS & MASK1;
+		b %= 10;
+		n = 10*n + b;
+	}
+	ra &= ~MASK5;
+	ra |= n & MASK5;
+}
+
+void
+mixchar(void)
+{
+	int i;
+	u32int a, val;
+
+	val = ra & ~SIGNB;
+	for(i = 0; i < 5; i++) {
+		a = val % 10;
+		a += 30;
+		rx &= ~(MASK1 << i*BITS);
+		rx |= a << i*BITS;
+		val /= 10;
+	}
+	for(i = 0; i < 5; i++) {
+		a = val % 10;
+		a += 30;
+		ra &= ~(MASK1 << i*BITS);
+		ra |= a << i*BITS;
+		val /= 10;
+	}
+}
+
+void
+mixslra(int m, int left)
+{
+	u32int val;
+
+	if(m < 0)
+		vmerror("Bad shift A %d", m);
+	if(m > 4) {
+		ra &= ~MASK5;
+		return;
+	}
+	val = ra & MASK5;
+	ra &= ~MASK5;
+	if(left)
+		val <<= m * BITS;
+	else
+		val >>= m * BITS;
+	ra |= val & MASK5;
+}
+
+void
+mixslrax(int m, int left)
+{
+	u64int rax;
+
+	if(m < 0)
+		vmerror("Bad shift AX %d", m);
+	if(m > 9) {
+		ra &= ~MASK5;
+		rx &= ~MASK5;
+		return;
+	}
+	rax = ra & MASK5;
+	ra &= ~MASK5;
+	rax <<= 5 * BITS;
+	rax |= rx & MASK5;
+	rx &= ~MASK5;
+	if(left)
+		rax <<= m;
+	else
+		rax >>= m;
+	rx |= rax & MASK5;
+	ra |= rax>>5*BITS & MASK5;
+}
+
+void
+mixslc(int m)
+{
+	u64int rax, s;
+
+	if(m < 0)
+		vmerror("Bad shift SLC %d", m);
+
+	m %= 10;
+
+	rax = ra & MASK5;
+	ra &= ~MASK5;
+	rax <<= 5 * BITS;
+	rax |= rx & MASK5;
+	rx &= ~MASK5;
+
+	s = rax & mask[m]<<10-m;
+	rax <<= m;
+	rax &= ~mask[m];
+	rax |= s;
+
+	rx |= rax & MASK5;
+	ra |= rax>>5*BITS & MASK5;
+}
+
+void
+mixsrc(int m)
+{
+	u64int rax, s;
+
+	if(m < 0)
+		vmerror("Bad shift SRC %d", m);
+
+	m %= 10;
+
+	rax = ra & MASK5;
+	ra &= ~MASK5;
+	rax <<= 5 * BITS;
+	rax |= rx & MASK5;
+	rx &= ~MASK5;
+
+	s = rax & mask[m];
+	rax >>= m;
+	rax &= ~mask[m] << 10-m;
+	rax |= s<<10-m;
+
+	rx |= rax & MASK5;
+	ra |= rax>>5*BITS & MASK5;
+}
+
+void
+mixmove(int s, int f)
+{
+	int d;
+
+	if(f == 0)
+		return;
+
+	d = mval(ri[1], 0, MASK2);
+	if(d < 0 || d > 4000)
+		vmerror("Bad address MOVE %d", d);
+	memcpy(cells+d, cells+s, f*sizeof(u32int));
+	d += f;
+	d &= MASK2;
+	ri[1] = d < 0 ? -d|SIGNB : d;
+}
+
+u32int
+mixld(u32int v, int f)
+{
+	u32int w;
+	int a, b, d;
+
+	if(f == 5)
+		return v;
+
+	UNF(a, b, f);
+	w = 0;
+	if(a == 0) {
+		if(v >> 31)
+			w = SIGNB;
+		if(b == 0)
+			return w;
+		a++;
+	}
+
+	d = b - a;
+	if(a > 5 || b > 5 || d < 0 || d > 4)
+		vmerror("Bad fpart (%d:%d)", a, b);
+	v &= mask[d] << (5-b) * BITS;
+	v >>= (5-b) * BITS;
+	return w | v;
+}
+
+u32int
+mixst(u32int w, u32int v, int f)
+{
+	int a, b, d;
+
+	if(f == 5)
+		return v;
+
+	UNF(a, b, f);
+	if(a == 0) {
+		w = v>>31 ? w|SIGNB : w&~SIGNB;
+		if(b == 0)
+			return w;
+		a++;
+	}
+
+	d = b - a;
+	if(a > 5 || b > 5 || d < 0 || d > 4)
+		vmerror("Bad fpart (%d:%d)", a, b);
+	v &= mask[d];
+	v <<= (5-b) * BITS;
+	w &= ~(mask[d] << (5-b)*BITS);
+	return w | v;
+}
+
+int
+mixjbus(int /*m*/, int /*f*/, int ip)
+{
+	return ip+1;
+}
+
+void
+mixioc(int, int f)
+{
+	switch(f) {
+	case 18:
+	case 19:
+		print("\n");
+		break;
+	}
+}
+
+void mixin(int, int){}
+
+void
+mixout(int m, int f)
+{
+	switch(f) {
+	case 18:
+		mixprint(m, 24);
+		break;
+	case 19:
+		mixprint(m, 14);
+		break;
+	}
+}
+
+int
+mixjred(int m, int /*f*/, int /*ip*/)
+{
+	return m;
+}
+
+int
+mixjmp(int m, int ip)
+{
+	ri[0] = ip+1 & MASK2;
+	return m;
+}
+
+int
+mixjov(int m, int ip)
+{
+	if(ot) {
+		ot = 0;
+		ri[0] = ip+1 & MASK2;
+		return m;
+	}
+	return ip + 1;
+}
+
+int
+mixjnov(int m, int ip)
+{
+	if(ot) {
+		ot = 0;
+		return ip + 1;
+	}
+	ri[0] = ip+1 & MASK2;
+	return m;
+}
+
+int
+mixjc(int m, int ip, int c1, int c2)
+{
+	if(c1 || c2) {
+		ri[0] = ip+1 & MASK2;
+		return m;
+	}
+	return ip + 1;
+}
+
+int
+mixjaxic(int m, int ip, u32int r, u32int msk, int f)
+{
+	int v, c;
+
+	v = mval(r, 0, msk);
+	switch(f) {
+	default:	vmerror("Bad instruction JA condition: %d", f);
+	case 0:	c = v < 0;	break;
+	case 1:	c = v == 0;	break;
+	case 2:	c = v > 0;	break;
+	case 3:	c = v >= 0;	break;
+	case 4:	c = v != 0;	break;
+	case 5:	c = v <= 0;	break;
+	}
+
+	if(c) {
+		ri[0] = ip+1 & MASK2;
+		return m;
+	}
+	return ip + 1;
+}
+
+void
+mixinc(int m, u32int *r)
+{
+	int v;
+
+	v = mval(*r, 0, MASK5);
+	v += m;
+	*r = v < 0 ? -v|SIGNB : v;
+}
+
+void mixfcmp(void){}
+
+void
+mixcmp(int m, int f, u32int r)
+{
+	int v1, v2;
+
+	ce = cg = cl = 0;
+
+	v1 = V(r, f);
+	v2 = V(cells[m], f);
+	if(v1 < v2)
+		cl = 1;
+	else if(v1 > v2)
+		cg = 1;
+	else
+		ce = 1;
+}
+
+int
+mixvm(int ip, int once)
+{
+	u32int r;
+	int a, i, f, c, m, inst;
+
+	curpc = ip;
+	for (;;) {
+		if(curpc < 0 || curpc > 4000)
+			vmerror("Bad PC %d", curpc);
+		if(bp[curpc] && !once)
+			return curpc;
+		inst = cells[curpc];
+		a = V(inst, F(0, 2));
+		i = V(inst, F(3, 3));
+		f = V(inst, F(4, 4));
+		c = V(inst, F(5, 5));
+		m = M(a, i);
+		switch(c) {
+		default:
+			fprint(2, "Bad op!\n");
+			exits("error");
+		case 0:
+			break;
+		case 1:
+			if(f == 6)
+				mixfadd(inst);
+			else
+				mixadd(m, f);
+			break;
+		case 2:
+			if(f == 6)
+				mixfsub(inst);
+			else
+				mixsub(m, f);
+			break;
+		case 3:
+			if(f == 6)
+				mixfmul(inst);
+			else
+				mixmul(m, f);
+			break;
+		case 4:
+			if(f == 6)
+				mixfdiv(inst);
+			else
+				mixdiv(m, f);
+			break;
+		case 5:
+			switch(f) {
+			default:
+				vmerror("Bad instruction NUM or CHAR: %d", f);
+			case 0:
+				mixnum();
+				break;
+			case 1:
+				mixchar();
+				break;
+			case 2:
+				return -1;	/* HLT */
+			}
+			break;
+		case 6:
+			switch(f) {
+			default: vmerror("Bad instruction shift: %d", f);
+			case 0: mixslra(m, 1);	break;
+			case 1: mixslra(m, 0);	break;
+			case 2: mixslrax(m, 1);	break;
+			case 4: mixslrax(m, 0);	break;
+			case 5: mixslc(m);	break;
+			case 6: mixsrc(m);	break;
+			}
+			break;
+		case 7:
+			mixmove(m, f);
+			break;
+		case 8:
+			ra = mixld(cells[m], f);
+			break;
+		case 9: case 10: case 11:
+		case 12: case 13: case 14:
+			ri[c-8] = mixld(cells[m], f);
+			break;
+		case 15:
+			rx = mixld(cells[m], f);
+			break;
+		case 16:
+			ra = mixld(cells[m], f) ^ SIGNB;
+			break;
+		case 17: case 18: case 19:
+		case 20: case 21: case 22:
+			ri[c-16] = mixld(cells[m], f) ^ SIGNB;
+			break;
+		case 23:
+			rx = mixld(cells[m], f) ^ SIGNB;
+			break;
+		case 24:
+			cells[m] = mixst(cells[m], ra, f);
+			break;
+		case 25: case 26: case 27:
+		case 28: case 29: case 30:
+			r = ri[c-24] & ~(MASK3 << 2*BITS);
+			cells[m] = mixst(cells[m], r, f);
+			break;
+		case 31:
+			cells[m] = mixst(cells[m], rx, f);
+			break;
+		case 32:
+			r = ri[0] & ~(MASK3 << 2*BITS);
+			cells[m] = mixst(cells[m], r, f);
+			break;
+		case 33:
+			cells[m] = 0; /* STZ */
+			break;
+		case 34:
+			curpc = mixjbus(m, f, curpc);
+			goto Again;
+		case 35:
+			mixioc(m, f);
+			break;
+		case 36:
+			mixin(m, f);
+			break;
+		case 37:
+			mixout(m, f);
+			break;
+		case 38:
+			curpc = mixjred(m, f, curpc);
+			break;
+		case 39:
+			switch(f) {
+			default: vmerror("Bad jmp instruction: %d", f);
+			case 0: curpc = mixjmp(m, curpc);	break;
+			case 1: curpc = m;	break;	/* JSJ */
+			case 2: curpc = mixjov(m, curpc);	break;
+			case 3: curpc = mixjnov(m, curpc);	break;
+			case 4: curpc = mixjc(m, curpc, cl, 0);	break;
+			case 5: curpc = mixjc(m, curpc, ce, 0);	break;
+			case 6: curpc = mixjc(m, curpc, cg, 0);	break;
+			case 7: curpc = mixjc(m, curpc, cg, ce);	break;
+			case 8: curpc = mixjc(m, curpc, cl, cg);	break;
+			case 9: curpc = mixjc(m, curpc, cl, ce);	break;
+			}
+			goto Again;
+		case 40:
+			curpc = mixjaxic(m, curpc, ra, MASK5, f);
+			goto Again;
+		case 41: case 42: case 43:
+		case 44: case 45: case 46:
+			curpc = mixjaxic(m, curpc, ri[c-40], MASK2, f);
+			goto Again;
+		case 47:
+			curpc = mixjaxic(m, curpc, rx, MASK5, f);
+			goto Again;
+		case 48:
+			switch(f) {
+			case 0:	mixinc(m, &ra);	break;
+			case 1: mixinc(-m, &ra);	break;
+			case 2:
+				ra = m == 0
+					? inst & SIGNB
+					: m < 0 ? -m|SIGNB : m;
+				break;	/* ENTA */
+			case 3:
+				ra = m == 0
+					? ~inst & SIGNB
+					: m > 0 ? m|SIGNB : -m;
+				break;	/* ENNA */
+			}
+			break;
+		case 49: case 50: case 51:
+		case 52: case 53: case 54:
+			switch(f) {
+			case 0:	mixinc(m, ri+(c-48));	break;
+			case 1:	mixinc(-m, ri+(c-48));	break;
+			case 2:
+				ri[c-48] = m == 0
+					? inst & SIGNB
+					: m < 0 ? -m|SIGNB : m;
+				break;	/* ENT[1-6] */
+			case 3:
+				ri[c-48] = m == 0
+					? ~inst & SIGNB
+					: m > 0 ? m|SIGNB : -m;
+				break;	/* ENN[1-6] */
+			}
+			break;
+		case 55:
+			switch(f) {
+			case 0:	mixinc(m, &rx);	break;
+			case 1: mixinc(-m, &rx);	break;
+			case 2:	rx = m == 0
+					? inst & SIGNB
+					: m < 0 ? -m|SIGNB : m;
+				break;	/* ENTX */
+			case 3:	rx = m == 0
+					? ~inst & SIGNB
+					: m > 0 ? m|SIGNB : -m;
+				break;	/* ENNX */
+			}
+			break;
+		case 56:
+			if(f == 6)
+				mixfcmp();
+			else
+				mixcmp(m, f, ra);
+			break;
+		case 57: case 58: case 59:
+		case 60: case 61: case 62:
+			mixcmp(m, f, ri[c-56] & ~(MASK3<<2*BITS));
+			break;
+		case 63:
+			mixcmp(m, f, rx);
+			break;
+		}
+		curpc++;
+Again:
+		if(once)
+			return curpc;
+	}
+}
--- /dev/null
+++ b/sys/src/games/mix/mix.h
@@ -1,0 +1,85 @@
+#pragma varargck type "I" u32int
+
+typedef struct Sym Sym;
+typedef struct Refinst Refinst;
+typedef struct Wval Wval;
+typedef struct Con Con;
+
+struct Sym {
+	Avl;
+	char *name;
+	long lex;
+	union {
+		struct {
+			int opc, f;	/* LOP LHERE LBACK LFORW */
+		};
+		u32int mval;	/* LSYMDEF */
+		struct {
+			int *refs, i, max;	/* LSYMREF */
+		};
+	};
+	char nbuf[1];
+};
+
+struct Con {
+	Sym *sym;
+	u32int exp;
+	Con *link;
+};
+
+int mixvm(int, int);
+void repl(int);
+u32int mixst(u32int, u32int, int);
+
+void mixprint(int, int);
+
+long yylex(void);
+int yyparse(void);
+void yyerror(char*, ...);
+void vmerror(char*, ...);
+void skipto(char);
+Sym *sym(char*);
+void sinit(void);
+int asmfile(char*);
+int V(u32int, int);
+int Ifmt(Fmt*);
+
+Rune mixtorune(int);
+int runetomix(Rune);
+void cinit(void);
+
+void warn(char*, ...);
+void *emalloc(ulong);
+void *emallocz(ulong);
+void *erealloc(void*, ulong);
+char *estrdup(char*);
+char *strskip(char*);
+char *strim(char*);
+void *bsearch(void*, void*, long, int, int(*)(void*, void*));
+
+Avltree *syms;
+int star, line, vmstart, yydone, curpc;
+Con *cons;
+char *filename;
+extern int mask[5];
+u32int cells[4000];
+char bp[4000];
+jmp_buf errjmp;
+Biobuf bin;
+
+u32int ra, rx, ri[7];
+int ce, cl, cg, ot;
+
+#define F(a, b) 8*(a)+(b)
+#define UNF(a, b, f) ((a) = f/8, (b) = f%8)
+
+enum {
+	BITS = 6,
+	MASK1 = 63,
+	MASK2 = (63<<6) | MASK1,
+	MASK3 = (63<<12) | MASK2,
+	MASK4 = (63<<18) | MASK3,
+	MASK5 = (63<<24) | MASK4,
+	OVERB = 1<<30,
+	SIGNB = 1<<31,
+};
--- /dev/null
+++ b/sys/src/games/mix/mixal.y
@@ -1,0 +1,359 @@
+%{
+#include <u.h>
+#include <libc.h>
+#include <avl.h>
+#include <bio.h>
+#include "mix.h"
+%}
+
+%union {
+	Sym *sym;
+	long lval;
+	u32int mval;
+	Rune *rbuf;
+}
+
+%type	<lval>	wval apart exp aexp fpart ipart
+%type	<mval>	wval1
+%type	<sym>	loc reflit
+
+%token	<sym>	LSYMDEF LSYMREF LOP LEQU LORIG LCON LALF LEND
+%token	<sym>	LBACK LHERE LFORW
+%token	<lval>	LNUM
+%token	<rbuf>	LSTR
+
+%left '+' '-' '*' '/' LSS ':' ','
+
+%%
+
+prog:
+|	prog inst
+
+inst:
+	eol
+|	loc LOP apart ipart fpart eol
+	{
+		defloc($loc, star);
+		asm($LOP, $apart, $ipart, $fpart);
+	}
+|	loc LOP reflit ipart fpart eol
+	{
+		defloc($loc, star);
+		addref($reflit, star);
+		refasm($LOP, $ipart, $fpart);
+	}
+|	loc LEQU wval eol
+	{
+		defloc($loc, $wval);
+	}
+|	loc LORIG wval eol
+	{
+		defloc($loc, star);
+		star = $wval;
+	}
+|	loc LCON wval1 eol
+	{
+		defloc($loc, star);
+		cells[star++] = $wval1;
+	}
+|	loc LALF LSTR eol
+	{
+		defloc($loc, star);
+		alf(star++, $LSTR);
+	}
+|	loc LEND wval eol
+	{
+		endprog($wval);
+		defloc($loc, star);
+	}
+
+loc:
+	{
+		$$ = nil;
+	}
+|	LSYMREF
+	{
+		$$ = $LSYMREF;
+	}
+|	LHERE
+	{
+		Sym *f;
+		int l;
+
+		l = ($LHERE)->opc;
+		back[l] = star;
+		f = forw + l;
+		defloc(f, star);
+		f->lex = LSYMREF;
+		f->refs = nil;
+		f->i = f->max = 0;
+		$$ = nil;
+	}
+
+apart:
+	{
+		$$ = 0;
+	}
+|	exp
+|	LBACK
+	{
+		$$ = back[($LBACK)->opc];
+	}
+
+reflit:
+	LSYMREF
+|	'=' wval1 '='
+	{
+		$$ = con($wval1);
+	}
+|	LFORW
+	{
+		$$ = forw + ($LFORW)->opc;
+	}
+
+ipart:
+	{
+		$$ = 0;
+	}
+|	',' exp
+	{
+		$$ = $exp;
+	}
+
+fpart:
+	{
+		$$ = -1;
+	}
+|	'(' exp ')'
+	{
+		if($exp < 0)
+			yyerror("invalid fpart %d\n", $exp);
+		$$ = $exp;
+	}
+
+exp:
+	aexp
+|	'+' aexp
+	{
+		$$ = $aexp;
+	}
+|	'-' aexp
+	{
+		$$ = -$aexp;
+	}
+|	exp '+' aexp
+	{
+		$$ = $exp + $aexp;
+	}
+|	exp '-' aexp
+	{
+		$$ = $exp - $aexp;
+	}
+|	exp '*' aexp
+	{
+		$$ = $exp * $aexp;
+	}
+|	exp '/' aexp
+	{
+		$$ = ($exp) / $aexp;
+	}
+|	exp LSS aexp
+	{
+		$$ = (((vlong)$exp) << 30) / $aexp;
+	}
+|	exp ':' aexp
+	{
+		$$ = F($exp, $aexp);
+	}
+
+aexp:
+	LNUM
+|	LSYMDEF
+	{
+		u32int mval;
+
+		mval = ($LSYMDEF)->mval;
+		if(mval & SIGNB) {
+			mval &= ~SIGNB;
+			$$ = -((long)mval);
+		} else
+			$$ = mval;
+	}
+|	'*'
+	{
+		$$ = star;
+	}
+
+wval:
+	wval1
+	{
+		if($wval1 & SIGNB)
+			$$ = -(long)($wval1 & MASK5);
+		else
+			$$ = $wval1;
+	}
+
+wval1:
+	exp fpart
+	{
+		$$ = wval(0, $exp, $fpart);
+	}
+|	wval ',' exp fpart
+	{
+		$$ = wval($wval, $exp, $fpart);
+	}
+
+eol:
+	'\n'
+
+%%
+
+int back[10];
+Sym forw[10];
+
+void
+defrefs(Sym *sym, long apart)
+{
+	u32int inst, mval;
+	int *ref, *ep;
+
+	ep = sym->refs + sym->i;
+	for(ref = sym->refs; ref < ep; ref++) {
+		inst = cells[*ref];
+		inst &= ~(MASK2 << BITS*3);
+		if(apart < 0) {
+			mval = -apart;
+			inst |= SIGNB;
+		} else
+			mval = apart;
+		inst |= (mval&MASK2) << BITS*3;
+		cells[*ref] = inst;
+	}
+}
+
+void
+defloc(Sym *sym, long val)
+{
+	if(sym == nil)
+		return;
+	defrefs(sym, val);
+	free(sym->refs);
+	sym->lex = LSYMDEF;
+	sym->mval = val < 0 ? -val|SIGNB : val;
+}
+
+void
+addref(Sym *ref, long star)
+{
+	if(ref->refs == nil || ref->i == ref->max) {
+		ref->max = ref->max == 0 ? 3 : ref->max*2;
+		ref->refs = erealloc(ref->refs, ref->max * sizeof(int));
+	}
+	ref->refs[ref->i++] = star;
+}
+
+static void
+asm(Sym *op, long apart, long ipart, long fpart)
+{
+	u32int inst, mval;
+
+	inst = op->opc & MASK1;
+
+	if(fpart == -1)
+		inst |= (op->f&MASK1) << BITS;
+	else
+		inst |= (fpart&MASK1) << BITS;
+
+	inst |= (ipart&MASK1) << BITS*2;
+
+	if(apart < 0) {
+		mval = -apart;
+		inst |= SIGNB;
+	} else
+		mval = apart;
+	inst |= (mval&MASK2) << BITS*3;
+
+	cells[star++] = inst;
+}
+
+void
+refasm(Sym *op, long ipart, long fpart)
+{
+	u32int inst;
+
+	inst = op->opc & MASK1;
+
+	if(fpart == -1)
+		inst |= (op->f&MASK1) << BITS;
+	else
+		inst |= (fpart&MASK1) << BITS;
+
+	inst |= (ipart&MASK1) << BITS*2;
+
+	cells[star++] = inst;
+}
+
+Sym*
+con(u32int exp)
+{
+	Con *c;
+	static int i;
+	static char buf[20];
+
+	seprint(buf, buf+20, "con%d\n", i++);
+	c = emalloc(sizeof(*c));
+	c->sym = sym(buf);
+	c->exp = exp;
+	c->link = cons;
+	cons = c;
+	return c->sym;
+}
+
+void
+alf(int loc, Rune *b)
+{
+	u32int w;
+	int m;
+	Rune *r, *e;
+
+	w = 0;
+	e = b + 5;
+	for(r = b; r < e; r++) {
+		if((m = runetomix(*r)) == -1)
+			yyerror("Bad mixchar %C\n", *r);
+		w |= m;
+		if(r+1 < e)
+			w <<= BITS;
+	}
+	cells[loc] = w;
+}
+
+void
+endprog(int start)
+{
+	Con *c, *link;
+	for(c = cons; c != nil; c = link) {
+		defloc(c->sym, star);
+		cells[star++] = c->exp;
+		link = c->link;
+		free(c);
+	}
+	cons = nil;
+	vmstart = start;
+	yydone = 1;
+}
+
+u32int
+wval(u32int old, int exp, int f)
+{
+	if(f == -1) {
+		if(exp < 0)
+			return -exp | SIGNB;
+		else
+			return exp;
+	}
+
+	if(exp < 0)
+		return mixst(old, -exp&MASK5 | SIGNB, f);
+	return mixst(old, exp & MASK5, f);
+}
--- /dev/null
+++ b/sys/src/games/mix/mkfile
@@ -1,0 +1,13 @@
+</$objtype/mkfile
+
+TARG=mix
+YFILES=mixal.y
+OFILES=y.tab.$O\
+	mix.$O\
+	char.$O\
+	util.$O\
+	repl.$O
+HFILES=mix.h y.tab.h
+BIN=/$objtype/bin/games
+
+</sys/src/cmd/mkone
--- /dev/null
+++ b/sys/src/games/mix/repl.c
@@ -1,0 +1,289 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <bio.h>
+#include <avl.h>
+#include "mix.h"
+
+int
+getf(char *line)
+{
+	long a, b;
+
+	if(*line == '\0')
+		return 5;
+	if(*line++ != '(') 
+		return -1;
+	a = strtol(line, &line, 10);
+	if(*line++ != ':')
+		return -1;
+	b = strtol(line, &line, 10);
+	if(*line != ')')
+		return -1;
+	return F(a, b);
+}	
+
+int
+disp(char *line)
+{
+	int f;
+	long m;
+
+	if(setjmp(errjmp) == 1)
+		return -1;
+	m = strtol(line, &line, 10);
+	if((f = getf(line)) == -1)
+		return -1;
+	print("%d\n", V(cells[m], f));
+	return 0;
+}
+
+int
+dispreg(char *line)
+{
+	vlong rax;
+	char c;
+	int i, f;
+	u32int reg;
+
+	if(setjmp(errjmp) == 1)
+		return -1;
+
+	switch(c = *line++) {
+	case 'a':
+		if(*line == 'x') {
+			rax = ra & MASK5;
+			rax <<= 5 * BITS;
+			rax |= rx & MASK5;
+			if(ra >> 31)
+				rax = -rax;
+			print("%lld\n", rax);
+			return 0;
+		} else
+			reg = ra;
+		break;
+	case 'x':
+		reg = rx;
+		break;
+	case 'j':
+		reg = ri[0];
+		break;
+	default:
+		if(!isdigit(c))
+			return -1;
+		i = c - '0';
+		if(i < 1 || i > 6)
+			return -1;
+		reg = ri[i];
+	}
+
+	if((f = getf(line)) == -1)
+		return -1;
+
+	print("%d\n", V(reg, f));
+	return 0;
+}
+
+int
+breakp(char *line)
+{
+	long l;
+
+	line = strskip(line);
+	if(!isdigit(*line))
+		return -1;
+	l = strtol(line, nil, 10);
+	if(l < 0 || l > 4000)
+		return -1;
+	bp[l] ^= 1;
+	return 0;
+}
+
+int
+asm(char *l)
+{
+	char *file;
+
+	if(yydone) {
+		print("Assembly complete\n");
+		return 0;
+	}
+	l = strskip(l);
+	if(*l++ == '<') {
+		Bterm(&bin);
+		l = strskip(l);
+		file = estrdup(strim(l));
+		if(asmfile(file) == -1) {
+			free(file);
+			return -1;
+		}
+		Binit(&bin, 0, OREAD);
+		free(file);
+		return 0;
+	}
+
+	line = 1;
+	filename = "<stdin>";
+	if(setjmp(errjmp) == 0)
+		yyparse();
+	Bterm(&bin);
+	Binit(&bin, 0, OREAD);
+	return 0;
+}
+
+int
+disasm(char *line)
+{
+	long l;
+
+	line = strskip(line);
+	if(!isdigit(*line))
+		return -1;
+
+	l = strtol(line, nil, 10);
+	if(l < 0 || l > 4000)
+		return -1;
+	print("%I\n", cells[l]);
+	return 0;
+}
+
+void
+mixprint(int m, int words)
+{
+	int i;
+	u32int *wp, w;
+	Rune buf[6], *rp;
+
+	wp = cells+m;
+	while(words-- > 0) {
+		rp = buf;
+		w = *wp++;
+		for(i = 4; i > -1; i--)
+			*rp++ = mixtorune(w>>i*BITS & MASK1);
+		*rp = '\0';
+		print("%S", buf);
+	}
+	print("\n");
+}
+
+int
+out(char *line)
+{
+	long l, i;
+
+	line = strskip(line);
+	i = 1;
+	if(*line == '(') {
+		l = strtol(strskip(line+1), &line, 10);
+		line = strskip(line);
+		if(*line != ',')
+			return -1;
+		i = strtol(strskip(line+1), &line, 10);
+		line = strskip(line);
+		if(*line != ')')
+			return -1;
+	} else {
+		if(!isdigit(*line))
+			return -1;
+		l = strtol(line, nil, 10);
+	}
+	mixprint(l, i);
+	return 0;
+}
+
+void
+clearsyms(Sym *s)
+{
+	if(s == nil)
+		return;
+
+	clearsyms((Sym*)s->c[0]);
+	clearsyms((Sym*)s->c[1]);
+	free(s);
+}
+
+void
+repl(int go)
+{
+	char *line, c;
+	int len, once;
+		
+
+	Binit(&bin, 0, OREAD);
+
+	if(go) {
+		once = 0;
+		goto Go;
+	}
+
+	for(;;) {
+		print("MIX ");
+
+		if((line = Brdline(&bin, '\n')) == nil)
+			return;
+
+		if((len = Blinelen(&bin)) == 1)
+			continue;
+
+		line[len-1] = '\0';
+
+		once = 0;
+		switch(c = line[0]) {
+		Err:
+			print("?\n");
+			break;
+		default:
+			if(!isdigit(c)) 
+				goto Err;
+			if(disp(line) == -1)
+				goto Err;
+			break;
+		case 'a':
+			if(asm(line+1) == -1)
+				goto Err;
+			break;
+		case 'b':
+			if(breakp(line+1) == -1)
+				goto Err;
+			break;
+		case 'c':
+			ra = rx = ri[0] = ri[1] = ri[2] = ri[3] = ri[4] = ri[5] = ri[6] = 0;
+			memset(cells, 0, sizeof(cells));
+			vmstart = -1;
+			yydone = 0;
+			clearsyms((Sym*)syms->root);
+			syms->root = nil;
+			sinit();
+			break;
+		case 'd':
+			if(disasm(line+1) == -1)
+				goto Err;
+			break;
+		case 'o':
+			if(out(line+1) == -1)
+				goto Err;
+			break;
+		case 'r':
+			if(dispreg(line+1) == -1)
+				goto Err;
+			break;
+		case 's':
+			once = 1;
+		case 'g':
+		Go:
+			if(vmstart == -1)
+				goto Err;
+			if(setjmp(errjmp) == 0)
+				vmstart = mixvm(vmstart, once);
+			else
+				break;
+			if(vmstart == -1)
+				print("halted\n");
+			else
+				print("at %d:\t%I\n", vmstart, cells[vmstart]);
+			break;
+		case 'x':
+			return;
+		}
+	}
+}
--- /dev/null
+++ b/sys/src/games/mix/tests/maximum.m
@@ -1,0 +1,14 @@
+# Entry condition R1 = n.
+# Exit: RA = max R2 = index of max in X
+X	EQU	1000
+ORIG	3000
+MAXIMUM	STJ	EXIT	# Subroutine linkage.
+INIT	ENT3	0,1	# M1. Initialize k ← n.
+	JMP	CHANGEM	# j ← n, m ← X[n], k ← n-1.
+LOOP	CMPA	X,3	# M3. Compare.
+	JGE	*+3	# To M5 if m ≥ X[k].
+CHANGEM	ENT2	0,3	# M4. Change m. j ← k.
+	LDA	X,3	# m ← X[k].
+	DEC3	1	# M5. Decrease k.
+	J3P	LOOP	# M2. All tested? To M3 if k > 0.
+EXIT	JMP	*	# Return to main program.
--- /dev/null
+++ b/sys/src/games/mix/tests/maxmain.m
@@ -1,0 +1,25 @@
+	ORIG	X
+	CON	0
+	CON	3910
+	CON	23
+	CON	-45
+	CON	310
+	CON	475
+	CON	40291
+	CON	358
+	CON	20912
+RESULT	ALF	"MAX: "
+	CON	0
+	ALF	" AT: "
+	CON	0
+	ORIG	EXIT+1
+MAIN	ENT1	RESULT-X-1
+	JMP	MAXIMUM
+	CHAR
+	STX	RESULT+1
+	ENTA	,2
+	CHAR
+	STX	RESULT+3
+	OUT	RESULT(19)
+	HLT
+	END	MAIN
--- /dev/null
+++ b/sys/src/games/mix/tests/primes.m
@@ -1,0 +1,49 @@
+L	EQU	500
+PRINTER	EQU	18
+PRIME	EQU	-1
+BUF0	EQU	2000
+BUF1	EQU	BUF0+25
+	ORIG	3000
+START	IOC	0(PRINTER)
+	LD1	=1-L=
+	LD2	=3=
+2H	INC1	1
+	ST2	PRIME+L,1
+	J1Z	2F
+4H	INC2	2
+	ENT3	2
+6H	ENTA	0
+	ENTX	0,2
+	DIV	PRIME,3
+	JXZ	4B
+	CMPA	PRIME,3
+	INC3	1
+	JG	6B
+	JMP	2B
+2H	OUT	TITLE(PRINTER)
+	ENT4	BUF1+10
+	ENT5	-50
+2H	INC5	L+1
+4H	LDA	PRIME,5
+	CHAR
+	STX	0,4(1:4)
+	DEC4	1
+	DEC5	50
+	J5P	4B
+	OUT	0,4(PRINTER)
+	LD4	24,4
+	J5N	2B
+	HLT
+ORIG	PRIME+1
+	CON	2
+	ORIG	BUF0-5
+TITLE	ALF	"FIRST"
+	ALF	" FIVE"
+	ALF	" HUND"
+	ALF	"RED P"
+	ALF	"RIMES"
+	ORIG	BUF0+24
+	CON	BUF1+10
+	ORIG	BUF1+24
+	CON	BUF0+10
+	END	START
--- /dev/null
+++ b/sys/src/games/mix/util.c
@@ -1,0 +1,147 @@
+#include <u.h>
+#include <libc.h>
+#include <ctype.h>
+#include <avl.h>
+#include <bio.h>
+#include "mix.h"
+
+static char buf[1024];
+
+char*
+strskip(char *s) {
+	while(isspace(*s))
+		s++;
+	return s;
+}
+
+char*
+strim(char *s)
+{
+	char *t;
+
+	if(*s == '\0')
+		return s;
+
+	t = s + strlen(s) - 1;
+	while(isspace(*t) && t > s)
+		t--;
+	t[1] = '\0';
+	return s;
+}
+
+void
+yyerror(char *s, ...)
+{
+	char *bp;
+	va_list a;
+
+	bp = seprint(buf, buf+1024, "Assembly error: %s:%d: ", filename, line);
+	va_start(a, s);
+	bp = vseprint(bp, buf+1024, s, a);
+	va_end(a);
+	*bp++ = '\n';
+	write(2, buf, bp - buf);
+	longjmp(errjmp, 1);
+}
+
+void
+vmerror(char *s, ...)
+{
+	char *bp;
+	va_list a;
+
+	bp = seprint(buf, buf+1024, "VM error at %d: ", curpc);
+	va_start(a, s);
+	bp = vseprint(bp, buf+1024, s, a);
+	va_end(a);
+	*bp++ = '\n';
+	write(2, buf, bp - buf);
+	longjmp(errjmp, 1);
+}
+
+void
+error(char *s, ...)
+{
+	char *bp;
+	va_list a;
+
+	va_start(a, s);
+	bp = vseprint(buf, buf+1024, s, a);
+	va_end(a);
+	*bp++ = '\n';
+	write(2, buf, bp - buf);
+	exits("error");
+}
+
+void*
+emalloc(ulong s)
+{
+	void *v;
+
+	v = malloc(s);
+	if(v == nil)
+		error("Error allocating %lud: %r\n", s);
+	setmalloctag(v, getcallerpc(&s));
+	return v;
+}
+
+void*
+emallocz(ulong s)
+{
+	void *v;
+
+	v = malloc(s);
+	if(v == nil)
+		error("Error allocating %lud: %r", s);
+	memset(v, 0, s);
+	return v;
+}
+
+void*
+erealloc(void *p, ulong s)
+{
+	void *v;
+
+	v = realloc(p, s);
+	if(v == nil)
+		error("Error re-allocating %lud: %r", s);
+	setrealloctag(v, getcallerpc(&s));
+	return v;
+}
+
+char*
+estrdup(char *s)
+{
+	char *n;
+
+	n = strdup(s);
+	if(n == nil)
+		error("Error duplicating string %s: %r", s);
+	setmalloctag(n, getcallerpc(&s));
+	return n;
+}
+
+void*
+bsearch(void *k, void *a, long n, int w, int (*cmp)(void*, void*))
+{
+	void *e;
+	int c;
+
+	while(n > 0) {
+		e = (char*)a + w*(n/2);
+		c = cmp(k, e);
+		if(c == 0)
+			return e;
+
+		if(n == 1)
+			break;
+
+		if(c < 0)
+			n /= 2;
+		else {
+			a = e;
+			n -= n/2;
+		}
+	}
+	return nil;
+}
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -29,6 +29,7 @@
 	gba\
 	mahjongg\
 	mines\
+	mix\
 	music\
 	md\
 	nes\