ref: a0cc369c15bdd404e1a60b4fd5b3a5156a041f74
parent: c67d0c699f7b52da534f75d3620776997b1e8d52
author: spew <devnull@localhost>
date: Tue Jan 10 06:42:11 EST 2017
games/mix: Add Knuth MIX emulator/assembler
--- /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)
+.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\