ドカタしながらCOMET II用アセンブラを30日で作る日記(10日目)

今日は、COMET II の VM を作成および、コンパイラのバグ修正をしました。
余力があったので、バイトコードをexeに追加して実行ファイルも出力するようにしてみました。
VMはまだバグがあるので怪しいのですが。



アセンブル方法

casc src.cas

or

casc src

src.casをアセンブルしてバイトコードsrc.cobを出力します。

  • exeオプションをつけると、実行ファイルが作れます。
casc src.cas -exe

or

casc src -exe

で、src.casからsrc.exeが出来ます。



実行方法

cas src.cob

or

cas src

src.cobをロードして実行します。
また、src.cobがない場合は、src.casを自動で探してアセンブルしてから実行します。

exeファイルを作って実行したい場合

cas src.cas -exe

or

cas src -exe

とすることでsrc.exeを作成し実行します。


以下ソースです。
casc.d

import std.file;
import std.string;
import std.path;
import std.stream;
import std.process;
import std.cstream;

/**
 * 
 */
class Token {
	union {
		char[] string;
		int data;
	}
	int type;
	int line;
	this()                       {}
	this(int t, int l)           { type = t; line = l; }
	this(int t, int l, int n)    { type = t; line = l; data = n; }
	this(int t, int l, char[] s) { type = t; line = l; string = s; }

	/**
	 * 
	 */
	char[] toString() {
		switch (type) {
		case SPACE:  return " ";
		case CAMMA:  return ",";
		case NUMBER: return std.string.toString(data);
		case STRING: return "'" ~ string.replace("'", "''") ~ "'";
		case ID:     return string;
		case LN:     return "\n";
		}
	}
}

/**
 * 
 */
enum {
	SPACE, OPERAND, NUMBER, CAMMA, LN, COMMENT, STRING,MINUS, ID,
}

/**
 * 
 */
int[char] typeSet(inout int[char] types, int type , char[] cs ...) {
	foreach (char c; cs) {
		types[c] = type;
	}
	return types;
}

/**
 * 
 */
Token[] lex(char[] str) {
	Token[] tokens;
	str = str.replace("\r\n", "\n").replace("\r", "\n");
	char[] string;
	int[char] types;
	int data = 0;
	int minus = 1;
	int line = 1;
	typeSet(types, SPACE, ' ', '\t');
	typeSet(types, LN, '\n');
	typeSet(types, COMMENT, ';');
	typeSet(types, STRING, '\'');
	typeSet(types, CAMMA, ',');
	typeSet(types, NUMBER, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
	typeSet(types, MINUS, '-');
loop:
	while (str.length != 0) {
	int type = (str[0] in types) ? types[str[0]] : -1;
//	printf("kore %c %d\n", str[0], type);
		
		switch (type) {
		case SPACE:
			str = str[1 .. str.length];
			while (str.length > 0 && (str[0] in types) && types[str[0]] == SPACE) {
				str = str[1 .. str.length];
			}
			tokens ~= new Token(SPACE, line);
			break;
		case COMMENT:
			while (str.length > 0 && str[0] != '\n') {
//				printf("%c",str[0]);
				str = str[1 .. str.length];
			}
			//if (str.length > 0) str = str[1 .. str.length];
			break;
		case LN:
			str = str[1 .. str.length];
			tokens ~= new Token(LN, line++);
			break;
		case CAMMA:
			str = str[1 .. str.length];
			tokens ~= new Token(CAMMA, line);
			break;
		case STRING:
			for (int i = 1; i < str.length; i++) {
				if (str[i] == '\'') {
					if (str.length > i + 1 && str[i + 1] == '\'') {
						continue;
					}
					tokens ~= new Token(STRING, line, str[1 .. i].replace("''", "'"));
					str = str[i + 1 .. str.length];
					continue loop;
				}
			}
			throw new Error("not terminate string");
			break;
		case MINUS:
			minus = -1;
		case NUMBER:
			data = 0;
			while(str.length > 0 && (str[0] >='0' && str[0] <= '9') ) {
				data *= 10;
				data += str[0] - '0';
				str = str[1 .. str.length];
			}
			tokens ~= new Token(NUMBER, line, data * minus);
			minus = 1;
			break;
		case -1:
			string = str[0 .. 1].dup;
			str    = str[1 .. str.length];
			while (str.length > 0 && 
				((str[0] >= 'a' && str[0] <= 'z') || 
				 (str[0] >= 'A' && str[0] <= 'Z') ||  
				 (str[0] >= '0' && str[0] <= '9')) ) {
				string ~= str[0];
				str = str[1 .. str.length];
			}
			tokens ~= new Token(ID, line, string);
		default:
			break;
		}
	}
	return tokens;
}

alias std.string.split split;

/**
 * 
 */
Token[][] split(Token[] tokens, int type) {
	Token[][] ts;
	Token[] ts2;
	bool f = false;
	foreach (Token t; tokens) {
		if (t.type == type) {
			ts ~= ts2.dup;
			ts2.length = 0;
			f = true;
			continue;
		}
		f = false;
		ts2 ~= t;
	}
	if (f || ts2.length > 0)
		ts ~= ts2;
	return ts;
}

/**
 *
 */
Token[] join(Token[][] tss) {
	Token[] ts;
	foreach (Token[] ts2; tss) {
		ts ~= ts2;
	}
	return ts;
}

/**
 * 
 */
Token[][][] lines(char[] str) {
	Token[] ts = lex(str);
	Token[][] tss = ts.split(LN);
	Token[][][] c;
	foreach (Token[] ts2; tss) {
		c ~= ts2.split(SPACE);
	}
	return c;
}

/**
 * 
 */
char[] toString(Token[] ts) {
	char[] rc;
	foreach (Token t; ts) {
		rc ~= t.toString();
	}
	return rc;
}

/**
 * 
 */
char[] toString(Token[][] tss) {
	char[] rc;
	char[] s = "";
	foreach (Token[] ts; tss) {
		rc ~= s ~ ts.toString();
		s = " ";
	}
	return rc;
}

int htoi(int c) {
	return ((c | 0x20) - '0') % ('W' - '0');
}
int htoi(char[] str) {
	int sum;
	foreach(char c; str)
		sum = sum * 16 + htoi(c);
	return sum;
}

Token[][][] macro(Token[][] c, char[] str) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	for(int i = 0; i < cp.length; i++) {
		str = str.replace("$" ~ std.string.toString(i+1),
			cp[i].toString());
	}
	if (c.length > 0) str = c[0].toString() ~ str;
	return str.lines();
}

Token[][][] expandMacro(Token[][][] cs) {
	Token[][][] cs2;
	foreach (Token[][] c; cs) {
		if (c.length <= 1) { cs2 ~= c; continue; }
		char[] s = c[1][0].string;
		switch (s) {
		case "START":{
			Token[][] cp = c[2 .. c.length].join().split(CAMMA);
			if (cp.length > 0) {
				if (startLabel != "")
					throw new Error("(" ~ std.string.toString(cp[0][0].line) ~ ")" ~ " The start label already defined");
				startLabel = cp[0][0].string;
			}
			cs2 ~= c;
		}
			break;
		case "IN":
			cs2 ~= c.macro("
 PUSH 0,GR1
 PUSH 0
 LAD  GR1,$1
 SVC  $2
 POP  GR1");
			break;
		case "OUT":
			cs2 ~= c.macro("
 PUSH 0,GR1
 PUSH 1
 LAD  GR1,$1
 SVC  $2
 POP  GR1");
			break;
		case "RPUSH":
			cs2 ~= c.macro("
 PUSH 0,GR1
 PUSH 0,GR2
 PUSH 0,GR3
 PUSH 0,GR4
 PUSH 0,GR5
 PUSH 0,GR6
 PUSH 0,GR7
");
			break;
		case "RPOP":
			cs2 ~= c.macro("
 POP GR7
 POP GR6
 POP GR5
 POP GR4
 POP GR3
 POP GR2
 POP GR1
");
			break;
		default:
			cs2 ~= c;
			break;
		}
	}
	return cs2;
}
Stream outd;
/**
 * 
 */
int main(char[][] argv) {
	if (argv.length < 2) {
		printf("casc filename [-exe]\n");
		return 1;
	}
	outd = dout;
	if (argv.length > 2 && argv[2] == "-exe") {
		outd = new MemoryStream();
	}
	char[] filename = argv[1];
	filename = filename.defaultExt("cas");
	char[][] ts =
		"NOP  ,op  ,00,"
		"LD   ,rr  ,10,"
		"ST   ,rax ,11,"
		"LAD  ,rax ,12,"
		"ADDA ,rr  ,20,"
		"SUBA ,rr  ,21,"
		"ADDL ,rr  ,22,"
		"SUBL ,rr  ,23,"
		"AND  ,rr  ,30,"
		"OR   ,rr  ,31,"
		"XOR  ,rr  ,32,"
		"CPA  ,rr  ,40,"
		"CPL  ,rr  ,41,"
		"SLA  ,rax,50,"
		"SRA  ,rax,51,"
		"SLL  ,rax,52,"
		"SRL  ,rax,53,"
		"JMI  ,ax ,61,"
		"JNZ  ,ax ,62,"
		"JZE  ,ax ,63,"
		"JUMP ,ax ,64,"
		"JPL  ,ax ,65,"
		"JOV  ,ax ,66,"
		"PUSH ,ax ,70,"
		"POP  ,r   ,71,"
		"CALL ,ax ,80,"
		"RET  ,op  ,81,"
		"SVC  ,ax ,F0,"
		"START,st  ,00,"
		"END  ,ed  ,00,"
		"DC   ,dc  ,00,"
		"DS   ,ds  ,00"
		.replace(" ","").split(",");
	void function(ubyte, Token[][],int[char[]])[char[]] fs;
	int  function(Token[][])[char[]] fls;
	ubyte[char[]] bs;
	for (int i = 0; i < ts.length; i += 3) {
		switch(ts[i + 1]) {
		case "op"  : fs[ts[i]] = &op  ; fls[ts[i]] = &opl  ; break;
		case "rr"  : fs[ts[i]] = &rr  ; fls[ts[i]] = &rrl  ; break;
		case "ax"  : fs[ts[i]] = &ax  ; fls[ts[i]] = &axl  ; break;
		case "rax" : fs[ts[i]] = &rax ; fls[ts[i]] = &raxl ; break;
		case "r"   : fs[ts[i]] = &r   ; fls[ts[i]] = &rl   ; break;
		case "st"  : fs[ts[i]] = &st  ; fls[ts[i]] = &stl  ; break;
		case "ed"  : fs[ts[i]] = &ed  ; fls[ts[i]] = &edl  ; break;
		case "dc"  : fs[ts[i]] = &dc  ; fls[ts[i]] = &dcl  ; break;
		case "ds"  : fs[ts[i]] = &ds  ; fls[ts[i]] = &dsl  ; break;
		}
		bs[ts[i]] = htoi(ts[i + 2]);
	}
	Token[][][] cs = (cast(char[])read(filename)).lines();
	int maxLine = cs.length;
	char[][] syms;
	int[char[]] symtable;
	int[char[]] symline;
	int address = 0;
	int symbolTableLength = 0;
	cs = expandMacro(cs);
	foreach (Token[][] c; cs) {
		if (c.length == 0) continue;
//		printf("// %.*s\n", c.toString());
		if (c[0].length > 0) {
			if (c[0][0].string in symtable)
				throw new Error("Label '" ~ c[0][0].string ~ "' already defined");
			syms ~= c[0][0].string;
			symtable[c[0][0].string] = address;
			symline[c[0][0].string] = c[0][0].line;
			symbolTableLength += c[0][0].string.length + 5;
			outd.printf("// %8.*s: %04x\n", c[0][0].string, address);
		}
		if (c.length == 1) continue;
		char[] s = c[1][0].string;
		address += fls[s](c);
	}
	if (!(startLabel in symtable)) {
		throw new Error("Start label '" ~ startLabel ~ "' is undefined");
	}
	outd.printf("// file size\n");
	p('C');p('S');p('L');p('2');
	int fileSize = 4 + 4 + 2 + 2 + address * 2 + symbolTableLength + 1;
	pw((fileSize>>16) & 0xffff);
	pw(fileSize & 0xffff);
	ln();
	outd.printf("// start address\n");
	pw(symtable[startLabel]);
	ln();
	outd.printf("// length\n");
	pw(address);
	ln();
	char[] last;
	address = 0;
	foreach (Token[][] c; cs) {
		if (c.length == 0) continue;
		outd.printf("// %04x: %.*s\n", address, c.toString());
		if (c.length == 1) continue;
		char[] s = c[1][0].string;
		address += fls[s](c);
		fs[s](bs[s], c, symtable);
		last = s;
		ln();
	}
	if (last != "END") {
		throw new Error("("~std.string.toString(maxLine)~") The program doesn't end by END instructions");
	}
	outd.printf("// SYMBOL TABLE\n");
	foreach (char[] l; syms) {
		int a = symtable[l];
		int line = symline[l];
		outd.printf("// %8.*s: %04x (%d)\n", l, a, line);
		foreach(char c; l)p(c);
		p(0);
		pw(a);
		pw(line);
		ln();
	}
	p(0);
	write(filename.addExt("cob"), cast(void[])output);
	if (argv.length > 2 && argv[2] == "-exe") {
		char[] src = cast(char[])read("cas.d");
		src = src.replace("compiledata = [];",
			"compiledata = ["~ cast(char[])(cast(MemoryStream)outd).data ~ "];") ;
		filename = filename.addExt("d");
		write(filename, cast(void[])src);
		return system("dmd -quiet -version=COMPILE " ~ filename);
	}
	return 0;
}

/**
 * 
 */
int regs(Token c) {
	char[][] regs = ["GR0", "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "GR7"];
	for (int i = 0; i < regs.length; i++) {
		if (c.type == ID && c.string == regs[i]) {
			return i;
		}
	}
	return -1;
}

/**
 * 
 */
void rr(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	
	int r[2];
	for (int i = 0; i < 2; i++) {
		r[i] = regs(cp[i][0]);
		if (r[i] == -1) return rax(b, c, symtable);
	}
	p(b | 0x4);
	p((r[0] << 4) | r[1]);
}

int rrl(Token[][] c) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	
	int r[2];
	for (int i = 0; i < 2; i++) {
		r[i] = regs(cp[i][0]);
		if (r[i] == -1) return raxl(c);
	}
	return 1;
}
/**
 * 
 */
void rax(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	int r = regs(cp[0][0]);
	ushort a = getNumber(cp[1][0], symtable);
	int x = 0;
	if (cp.length > 2 && cp[2].length > 0) {
		x = regs(cp[2][0]);
	}
	p(b);
	p((r << 4) | x);
	pw(a);
}
int raxl(Token[][] c) {
	return 2;
}

int getNumber(Token c, int[char[]] symtable) {
	if (c.type == NUMBER) return c.data;
	if (c.type == ID) {
		if (c.string in symtable)
			return symtable[c.string];
		throw new Error("Label " ~ c.string ~ " is defined");
	}
	throw new Error("");
}

/**
 * 
 */
void ax(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	int r = 0;//regs(cp[0][0]);
	ushort a = getNumber(cp[0][0], symtable);
	int x = 0;
	if (cp.length > 1 && cp[1].length > 0) {
		x = regs(cp[1][0]);
	}
	p(b);
	p((r << 4) | x);
	pw(a);
}
int axl(Token[][] c) {
	return 2;
}
/**
 * 
 */
void r(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	int r = regs(cp[0][0]);
	int x = 0;

	p(b);
	p((r << 4) | x);
}
int rl(Token[][] c) {
	return 1;
}
/**
 * 
 */
void st(ubyte b, Token[][] c, int[char[]] symtable) {
}
char[] startLabel;
int stl(Token[][] c) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	return 0;
}
/**
 * 
 */
void ed(ubyte b, Token[][] c, int[char[]] symtable) {}
int edl(Token[][] c) { return 0; }
/**
 * 
 */
void dc(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	if (cp[0][0].type == STRING) {
		for (int i = 0; i < cp[0][0].string.length; i++) {
			pw(cp[0][0].string[i]);
		}
		return;
	}
	int a = getNumber(cp[0][0], symtable);
	pw(a);
}
int dcl(Token[][] c) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	if (cp[0][0].type == STRING) {
		return cp[0][0].string.length;
	}
	return 1;
}
/**
 * 
 */
void ds(ubyte b, Token[][] c, int[char[]] symtable) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	if (cp[0][0].type != NUMBER)
		throw new Error("DS meirei no parameter ha suuji janaito dame desu");
	int a = cp[0][0].data;
	for (int i = 0; i < a; i++) {
		pw(0);
	}
}
int dsl(Token[][] c) {
	Token[][] cp = c[2 .. c.length].join().split(CAMMA);
	if (cp[0][0].type != NUMBER)
		throw new Error("DS meirei no parameter ha suuji janaito dame desu");
	int a = cp[0][0].data;
	return a;
}
/**
 * 1バイト出力
 */
void op(ubyte a, Token[][] c = null, int[char[]] symtable = null) {
	p(a);
	p(0);
}
int opl(Token[][] c) {
	return 1;
}
/**
 * 1バイト出力
 */
ubyte[] output;
void p(ubyte a) {
	if (cast(MemoryStream)outd)
		outd.printf("cast(ubyte)0x%02x,", cast(int)a);
	else
		outd.printf("0x%02x,", cast(int)a);
	output ~= a;
}
/**
 * 2バイト出力
 */
void pw(ushort w) {
	p((w >> 8) & 0xff);
	p(w & 0xff);
}
/**
 * 改行を表示する。
 */
void ln() {
	outd.printf("\n");
}

cas.d

import std.file;
import std.stream;
import std.cstream;
import std.path;
import std.process;

ushort PR;                 // Program Register
ushort SP;                 // Stack Pointer
ushort GR[8];              // General Registers
ushort mem[65536];
ushort OF;  // Overflow Flag : 演算結果が -32768 以下,あるいは 32767 以上の時,真
ushort SF;  // Sign Flag : 演算結果が負の時,真
ushort ZF;  // Zero Flag : 演算結果が零の時,真

int main(char[][] argv) {
	version(COMPILE) {
		static ubyte[] compiledata = [];
		load(compiledata);
	} else {
		if (argv.length < 2) {
			printf("cas filename\n");
			return 1;
		}
		char[] filename = argv[1].defaultExt("cob");
		if (!filename.exists()) {
			filename = filename.addExt("cas");
		}
		if (argv.length > 2 && argv[2] == "-exe") {
			int rc = system("casc " ~ filename.addExt("cas") ~ " -exe");
			if (rc != 0) return rc;
			return system(filename.addExt("exe"));
		}
		if (filename.getExt() == "cas") {
			int rc = system("casc " ~ filename);
			if (rc != 0) return rc;
			filename = filename.addExt("cob");
		}
		load(cast(ubyte[])read(filename));
	}
	int rc = run();
	printf("%d\n", rc);
	return rc;
}

ushort readShort(MemoryStream m) {
	ubyte b1,b2;
	m.read(b1);m.read(b2);
	return (b1 << 8) | b2;
}
uint readInt(MemoryStream m) {
	ubyte b1,b2,b3,b4;
	m.read(b1);m.read(b2); m.read(b3);m.read(b4);
	return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
}

void load(ubyte[] data) {
	MemoryStream m = new MemoryStream(data);
	if (m.data.length < 13)
		throw new Error("Load error. This is not CSL2 format");
	if (cast(char[])m.data[0 .. 4] != "CSL2")
		throw new Error("Load error. The file signature is bad");
	m.seekSet(4);
	uint filesize = readInt(m);
	PR = readShort(m);
	ushort length = readShort(m);
//	printf("fileSize = %d %d\n", filesize, m.data.length);
//	printf("start = %d length = %d", PR, length);
	if (filesize != m.data.length)
		throw new Error("Load error. Filesize is bad");
	for (int i = 0; i < length; i++) {
		mem[i] = readShort(m);
	}
//	printf("load ok\n");
}
ushort R1(ushort W1)      { return (W1 & 0x00f0) >> 4; }
ushort R2(ushort W1)      { return W1 & 0x000f; }
// 実効アドレス (effective address) : adr + [,x]
// インデックスレジスタ x は省略可能であり,省略された場合は 0 になる
// そのため,GC0 は x に使えない
ushort EADR(ushort W1) { return (W1 & 0x000f) ? mem[PR + 1] + GR[R2(W1)] : mem[PR + 1]; }
int run() {
  bool tracef = true;
  version(COMPILE) {tracef = false;}
  ushort w1, op, r1, r2;
    void ZFo(ushort d, int add) {
       OF = 0;
       ZF = d ? 0 : 1;
       SF = d & 0x8000 ? 1 : 0;
       PR += add;
    }
    void ZFoS(lazy int o, lazy ushort da) {
       OF = o();
       ushort d = da();
       ZF = d ? 0 : 1;
       SF = d & 0x8000 ? 1 : 0;
       PR += 2;
    }
    void ZFOA(int d, int add) {
       OF = (d < -32768 || 32767 < d) ? 1 : 0;
       ZF = d ? 0 : 1;
       SF = d & 0x8000 ? 1 : 0;
       PR += add;
    }
    void ZFOL(int d, int add) {
       OF = (0 <= cast(ushort)d && cast(ushort)d <= 65535) ? 1 : 0;
       ZF = d ? 0 : 1;
       SF = d & 0x8000 ? 1 : 0;
       PR += add;
    }
    void svc() {
            switch(mem[SP++]) {
              case 0x0: // IN
                char[] data = din.readLine();
                // 256文字以上は無視
                if (data.length > 256) data.length = 256;
                mem[EADR(w1)] = data.length;
                for (int i = 0; i < data.length; i++)
                  mem[GR[1]+i] = data[i];
                PR += 2;
                break;
              case 0x1: // OUT
                int length = mem[EADR(w1)];
                // TODO: 256文字チェック
                // TODO: マイナス値チェック
                for (int i = 0; i < length; i++)
                  dout.printf("%c", mem[GR[1]+i]);
                dout.printf("\n");
                dout.flush();
                PR += 2;
                break;
            }
  	}
loop:
  while(1) {
    w1 = mem[PR];
    op = w1 >> 8;
    r1      = R1(w1);
    r2      = R2(w1);
    if (tracef) {
      for (int i = 0;i < GR.length; i++) printf("%04x ", GR[i]);
      	printf("PR:%04x mem:%04x %04x SP:%04x ", PR, w1, mem[PR+1], SP);
      printf("FP:%c%c%c \n",  OF==0?'o':'O', SF==0?'s':'S', ZF==0?'z':'Z');
    }
    int tmp;
    int GRI(int r) { return cast(int)GR[r]; }
    ushort MADR (int i) { return mem[EADR(i)]; }
    int    MADRI(int i) { return cast(int)mem[EADR(i)]; }
    switch(op) {
  /* NOP          */case 0x00: PR++; break; // no operation
  /* LD   r,adr,x */case 0x10: GR[r1] = tmp =           MADR (w1); ZFo (tmp, 2); break; // load
  /* ST   r,adr,x */case 0x11: mem[EADR(w1)]= GR [r1];             PR += 2;      break; // store
  /* LAD  r,adr,x */case 0x12: GR[r1] =                 EADR (w1); PR += 2;      break; // load address
  /* LD   r1,r2   */case 0x14: GR[r1] = tmp = GR [r2];             ZFo (tmp, 1); break; // load
  /* ADDA r,adr,x */case 0x20: GR[r1] = tmp = GRI(r1) + MADRI(w1); ZFOA(tmp, 2); break; // add arithmetic
  /* SUBA r,adr,x */case 0x21: GR[r1] = tmp = GRI(r1) - MADRI(w1); ZFOA(tmp, 2); break; // subtract arithmetic
  /* ADDL r,adr,x */case 0x22: GR[r1] = tmp = GR [r1] + MADR (w1); ZFOL(tmp, 2); break; // add logical
  /* SUBL r,adr,x */case 0x23: GR[r1] = tmp = GR [r1] - MADR (w1); ZFOL(tmp, 2); break; // subtract logical
  /* ADDA r1,r2   */case 0x24: GR[r1] = tmp = GRI(r1) + GRI  (r2); ZFOA(tmp, 1); break; // add arithmetic
  /* SUBA r1,r2   */case 0x25: GR[r1] = tmp = GRI(r1) - GRI  (r2); ZFOA(tmp, 1); break; // subtract arithmetic
  /* ADDL r1,r2   */case 0x26: GR[r1] = tmp = GR [r1] + GR   [r2]; ZFOL(tmp, 1); break; // add logical
  /* SUBL r1,r2   */case 0x27: GR[r1] = tmp = GR [r1] - GR   [r2]; ZFOL(tmp, 1); break; // subtract logical
  /* AND  r,adr,x */case 0x30: GR[r1] = tmp = GR [r1] & MADR (w1); ZFo (tmp, 2); break; // and
  /* OR   r,adr,x */case 0x31: GR[r1] = tmp = GR [r1] | MADR (w1); ZFo (tmp, 2); break; // or
  /* XOR  r,adr,x */case 0x32: GR[r1] = tmp = GR [r1] ^ MADR (w1); ZFo (tmp, 2); break; // exclusive or
  /* AND  r1,r2   */case 0x34: GR[r1] = tmp = GR [r1] & GR   [r2]; ZFo (tmp, 1); break; // and
  /* OR   r1,r2   */case 0x35: GR[r1] = tmp = GR [r1] | GR   [r2]; ZFo (tmp, 1); break; // or
  /* XOR  r1,r2   */case 0x36: GR[r1] = tmp = GR [r1] ^ GR   [r2]; ZFo (tmp, 1); break; // exclusive or
  /* CPA  r,adr,x */case 0x40:          tmp = GRI(r1) - MADRI(w1); ZFo (tmp, 2); break; // compare arithmetic
  /* CPL  r,adr,x */case 0x41:          tmp = GR [r1] - MADR (w1); ZFo (tmp, 2); break; // compare logical
  /* CPA  r1,r2   */case 0x42:          tmp = GRI(r1) - GRI  (r2); ZFo (tmp, 1); break; // compare arithmetic
  /* CPL  r1,r2   */case 0x43:          tmp = GR [r1] - GR   [r2]; ZFo (tmp, 1); break; // compare logical
  /* SLA  r,adr,x */case 0x50: ZFoS((GRI(r1) << MADRI(w1) - 2) & 0x4000, GRI(r1) << MADRI(w1)); break; // shift left arithmetic
  /* SRA  r,adr,x */case 0x51: ZFoS((GRI(r1) >> MADRI(w1) - 1) & 0x0001, GRI(r1) >> MADRI(w1)); break; // shift right arithmetic
  /* SLL  r,adr,x */case 0x52: ZFoS((GR [r1] << MADR(w1) - 2) & 0x4000, GR [r1] << MADR(w1)); break; // shift left logical
  /* SRL  r,adr,x */case 0x53: ZFoS((GR [r1] >> MADR(w1) - 1) & 0x0001, GR [r1] >> MADR(w1)); break; // shift right logical
  /* JMI    adr,x */case 0x61: PR =  SF        ? EADR(w1) : PR + 2; break; // jump on minus
  /* JNZ    adr,x */case 0x62: PR = !ZF        ? EADR(w1) : PR + 2; break; // jump on non zero
  /* JZE    adr,x */case 0x63: PR =  ZF        ? EADR(w1) : PR + 2; break; // jump on zero
  /* JUMP   adr,x */case 0x64: PR =              EADR(w1)         ; break; // unconditional jump
  /* JPL    adr,x */case 0x65: PR = !SF && !ZF ? EADR(w1) : PR + 2; break; // jump on plus
  /* JOV    adr,x */case 0x66: PR = OF         ? EADR(w1) : PR + 2; break; // jump on overflow
  /* PUSH   adr,x */case 0x70: mem[--SP] = EADR(w1);    PR += 2;        break; // push
  /* POP  r       */case 0x71: GR[r1]    = mem[SP++];   PR++;           break; // pop
  /* CALL   adr,x */case 0x80: mem[--SP] = PR + 2;      PR = EADR(w1);  break; // call subroutine
  /* RET          */case 0x81: if (SP == 0) break loop; PR = mem[SP++]; break; // return from subroutine
  /* SVC    adr,x */case 0xf0: svc(); break; // supervisor call
    }
  }
  return GR[1];
}