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

awkCASLインタプリタD言語に移植したほうが楽なんじゃと思い、移植してみました。


元のソースはRoccoさんのもの
http://www.onicos.com/staff/iz/release/


これは、何日かかってるか微妙ですが、適当にawkのソースをDっぽく書き換えてみては、
めんどくさくなってやめてたのが数日。
気合で、ちゃんと動くようにしたのが1日。
この日記書いてるのが1日で、まぁ4日くらいですかねぇ。ということで。
これは、CASLなのか、CASL IIなのか謎なのだけど、動いたからイイヤと投げやり。


とりあえずソース貼っておきます。
これから、バイナリを出力したいなと。
以下ソースです。
ああ、エラーメッセージに日本語出力してるのですが、UTF-8です。
ckっていうcygwin用コンソール?使ってUTF-8出力してみてます。

/*
 * CASL インタープリタ
 */
import std.random;
import std.date;
import std.string : split = split, replace, atoi;
import std.file;
import std.regexp;
import std.stream;
import std.cstream;
import std.process;
import std.c.stdarg;

void print(char[] format, ...) {
    va_list ap;
    ap = cast(va_list) &format;
    ap += format.sizeof;
    dout.vprintf(format, ap);
	dout.flush();
}

class ExitException: Error {
	this() {
		super("");
	}
}

bool dumpFlag = false;
int line;
int nextmem;
char[][] srcprog;
int maxline;
int[65536] mem;
int[char[]] op;
int[char[]] symtab;
//
int[int] srcline;

int main(char[][] argv) {
	try {
		dumpFlag = true;
		if (argv.length != 2 && argv.length != 3) {
			print("casl file_name [-trace]\n");
			return 1;
		}
		assemble(cast(char[])read(argv[1]));
		print("start intr\n");
		intr(argv);
	} catch (ExitException e) {
		return 0;
	} catch (Exception e) {
		print("%.*s\n", e.toString());
		return 1;
	}
	return 0;
}

void assemble(char[] src) {
	static char[][] x = ["LD","ST","LEA","ADD","SUB","AND","OR","EOR","CPA",
		"CPL","SLA","SRA","SLL","SRL","JPZ","JMI","JNZ","JZE","JMP","START",
		"PUSH","POP","CALL","RET","END","DS","DC","IN","OUT","EXIT"];
	for (int i = 0; i < x.length; i++) {
		op["" ~ x[i]] = i;
	}
// アセンブラ第一段階
	line = nextmem = 1;
	bool start = false;
	srcprog = ("\n" ~ src.replace("\r\n", "\n")).split("\n");

	char[][][] tempfile;
	while (srcprog.length > line) {
		char[] _0 = (new RegExp(`;.*`,"g")).replace(srcprog[line], ""); // 注釈を取る
		char[][] _ = (new RegExp(`[\t]+`,"g")).split(_0);
		if (_.length == 1) _.length = 2;
		if (_[1] == "START") {
			if (start) {
				errper("START と END の対応がとれていません。");
			}
			start = true;
		}
		if (!start) {
			line++;
			continue;
		}
		symtab[_[0]] = nextmem;	// 名札の番地を覚える
		char[][] a;
		a.length = 3;
		if (_[1] != "") {		// _[1] は命令
			if (_[1] != "DC") {
				print("%.*s\n", _0);
				if (_.length > 2)
					a = _[2].split(",");
							// オペランドの分解
				if (a.length > 3) {
					errop();
				}
				a.length = 3;
			} else {
				if (_.length != 2 && _.length != 3) {
					print("%d\n", _.length);
					errper("DC 命令の値の指定が間違っています。");
				}
				a[0] = _[2];
			}

			if (_[1] == "RET" && (a[0] != "" || a[1] != "" || a[2] != "")) {
				errop();
			}
			if (_[0] != "" && (_[0].length > 6 || (new RegExp(`^[A-Z][A-Z0-9]*$`,"")).match(_[0]).length == 0 )) {
				errop();
			}

			if (!(_[1] in op)) {	// 命令が予約語にない
				errper(_[1] ~ "という命令はありません。");
			}

			if (a[0].length + a[1].length + a[2].length >= 72) {
				errper("オペランド欄は72文字までです。");
			}

			tempfile ~= [_[1], a[0], a[1], a[2]];
			if (_[1] == "DS") {
				if (!isdigit(a[0])) {
					errper("DS 命令の指定方法が間違っています。");
				}
				nextmem += toplus(atoi(a[0]));
			} else if (_[1] == "DC" && isstr(a[0])) {
				nextmem += a[0].length - 2;
			} else if (ismacro(_[1])) {	// マクロ
				nextmem += 2;
			} else {
				nextmem++;
			}
		}
		if (_[1] == "END") {
			if (!start) {
				errper("START と END の対応がとれていません。");
			}
			start = false;
		}
		line++;
	}
	if (start != 0) {
		errper("START と END の対応がとれていません。");
	}
	maxline = line - 1;

// アセンブラ第二段階
// adrnum アドレスナンバー
// grnum 汎用レジスタナンバー
// xrnum 指標レジスタナンバー
	char[] adrnum, grnum, xrnum;
	line = nextmem = 1;
	while (tempfile.length >= line) {
		char[][] _ = tempfile[line - 1];
		adrnum = "0";
		grnum = "  0";
		xrnum = "  5";				// デフォルトはダミーレジスタ
		srcline[nextmem] = line;	// アドレス番地に対応する
									// ソースプログラムの行番号
		if (!ismacro(_[0])) {
			if (op[_[0]] <= op["SRL"] || _[0] == "POP") {
						// _[2] がアドレス指定
				if (!checkgr(_[1], _[3]) ||
					_[0] == "POP" && (_[2] != "" || _[3] != "")) {
					errop();
				}
				adrnum = _[2];
				grnum = _[1];
				xrnum = (_[3] == "") ? ("  5") : (_[3]);
			} else if (isjump(_[0]) || _[0] == "PUSH" ||
				_[0] == "CALL" || _[0] == "START") {
									// _[1] がアドレス指定
				if (!checkgr("", _[2])) {
					errop();
				}
				adrnum = _[1];
				xrnum = (_[2] == "") ? ("  5") : (_[2]);
			}
			if (_[0] == "DC") {
				if (isnum(_[1])) {
					adrnum = _[1];
				} else if (isstr(_[1])) {
					setstr(_[1]);
					continue;
				} else {
					adrnum = std.string.toString(symtab[_[1]]);
				}
			} else if (_[0] == "DS") {
				int temp = toplus(atoi(_[1]));
				for (int i = 0; i < temp; i++) {
					mem[nextmem++] = (rand() % 65536) * 10000 + op["DS"] * 100;
				}
				pdump(nextmem - temp);
				line++;
				continue;
			}
			
			adrnum = std.string.toString(labeltonum(adrnum) % 65536);
			grnum = grnum[2..3];
			xrnum = xrnum[2..3];
			mem[nextmem] = atoi(adrnum) * 10000 + op[_[0]] * 100 + atoi(grnum) * 10 + atoi(xrnum);
			pdump(nextmem);
			nextmem++;
		} else { 			// マクロ命令
			int adr1 = labeltonum(_[1]);	// 番地1
			int adr2 = labeltonum(_[2]);	// 番地2
			mem[nextmem++] =  adr1 * 10000 + op[_[0]] * 100 + 5;
			mem[nextmem++] =  adr2 * 10000 + op[_[0]] * 100 + 5;
			pdump(nextmem - 2);
		}
		line++;
	}
}

// エラー行番号を出力
void perrline() {
	print("Error: line number in %d\n%.*s\n", line, srcprog[line]);
}
// 数字か
bool isnum(char[] num) {
	return (new RegExp(`^[+-]?[0-9]*$|^#[A-F0-9]*$`)).test(num) != 0;
}

// 10進数か
bool isdigit(char[] num) {
	return (new RegExp(`^[+-]?[0-9]*$`)).test(num) != 0;
}

// 文字か
bool isstr(char[] str) {
	return (new RegExp(`['].+[']`)).test(str) != 0;
}

// 0-9A-F を10進に
int xtod(char num) {
	if (num <= '9')
		return num - '0';
	return 10 + num - 'A';
}

// 10進に
int todigit(char[] num) {
	if (num == "")
		return 0;
	if (num[0] == '#') {	// 16進
		if (num.length != 5) {
			errper("16進数は4桁で書きましょう。\n");
		} else {
			return (xtod(num[1]) << 12) + (xtod(num[2]) << 8) + (xtod(num[3]) << 4) + xtod(num[4]);
		}
	} else if (num[0] == '\'') {	// 文字定数
		return num[1];
	} else {	// 10進
		int n = atoi(num);
		if (n < 0) {
			n += 65536;
		}
		return n;
	}
}

// ジャンプ命令か
bool isjump(char[] command) {
	return	op[command] >= op["JPZ"] &&
		op[command] <= op["JMP"];
}

// マクロ命令か
bool ismacro(char[] command) {
	return	command == "IN" ||
		command == "OUT";
}

// 名前なら番地に
int labeltonum(char[] num) {
	if (!isnum(num)) {
		if (symtab[num] == 0) {
			errper("ラベルが見つかりません。");
		}
		return toplus(symtab[num]);
	}
	return toplus(todigit(num));
}

// DC '文字列' の場合の処理
void setstr(char[] s) {
	s = s.replace("'", "");
	for (int i = 0; i < s.length; i++)
		mem[nextmem++] = s[i]*10000 + op["DC"]*100;
	pdump(nextmem - s.length);
	line++;
}

// メモリダンプの表示
void pdump(int m) {
	if (dumpFlag) {
		print("%8d:%15d %.*s\n", line, mem[m], srcprog[line]);
	}
}

// 正に変換
int toplus(int num) {
	while (num < 0)
		num += 65536;
	return num;
}

bool checkgr(char[] gr1, char[] gr2) {
	return !(gr1 != "" && !((new RegExp(`^GR[0-4]$`)).test(gr1) != 0) ||
		gr2 != "" && !((new RegExp(`^GR[1-4]$`)).test(gr2) != 0));
}

void errper(char[] str) {
	perrline();
	throw new Exception(str);
}

void errop() {
	perrline();
	throw new Exception("オペランド欄の指定方法が間違っています。");
}

bool endFlag;
bool tron;
int fr;
ushort[8] GR;

void intr(char[][] argv) {
	char[] jisstr = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ abcdefghijklmnopqrstuvwxyz";
	char[][int] JIS;
	for (int i = 0; i < jisstr.length; i++) {
		JIS[i + '0'] = jisstr[i .. i + 1];
	}

	if (argv.length > 2 && argv[2] == "-trace")
		tron = 1;
	int fr = (rand() % 3) - 1;
	endFlag = false;
// インタープリタ
	print("\n");

	for (int i = 0; i < 4; i++) GR[i] = rand() % 65536;
	
	GR[4] = 65535;			// スタックポインタ
	GR[5] = 0;				// ダミーレジスタ
	for (int pc = 1; pc >= 0; pc++) {
		
		int word = mem[pc];
		int adrnum = word / 10000;
		int opnum  = (word / 100) % 100;
		int grnum  = (word / 10) % 10;
		int xrnum  = word % 10;
		int adr    = (adrnum + GR[xrnum]) % 65536;
		line = srcline[pc];

		if (opnum == op["DC"] || opnum == op["DS"]) {
			continue;
		}
		if (line != 0 && tron) {
			print("line : %d\n%.*s\n", line, srcprog[line]);
		}
		
		if        (opnum == op["LD"]) {		// LD
			GR[grnum] = mem[adr] / 10000;
		} else if (opnum == op["ST"]) {		// ST
			int temp = (mem[adr] / 100) % 100;
			if (temp != op["DS"]) {
				segfault();
			}
			mem[adr] = GR[grnum];
			mem[adr] = mem[adr] * 10000 + temp * 100;
		} else if (opnum == op["LEA"]) {	// LEA
			fr = GR[grnum] = adr;
		} else if (opnum == op["ADD"]) {	// ADD
			GR[grnum] = GR[grnum] + (mem[adr] / 10000);
			GR[grnum] %= 65536;
			setfr(GR[grnum]);
		} else if (opnum == op["SUB"]) {	// SUB
			GR[grnum] -= (mem[adr] / 10000);
			if (GR[grnum] < 0) {
				GR[grnum] += 65536;
			}
			setfr(GR[grnum]);
		} else if (opnum == op["AND"]) {	// AND
			GR[grnum] = GR[grnum] & (mem[adr] / 10000);
			setfr(GR[grnum]);
		} else if (opnum == op["OR"]) {		// OR
			GR[grnum] = GR[grnum] | (mem[adr] / 10000);
			setfr(GR[grnum]);
		} else if (opnum == op["EOR"]) {	// EOR
			GR[grnum] = GR[grnum] ^ (mem[adr] / 10000);
			setfr(GR[grnum]);
		} else if (opnum == op["CPA"]) {	// CPA
			int temp1 = GR[grnum];
			int temp2 = (mem[adr] / 10000);
			if (temp1 > 32767) {
				temp1 -= 65536;
			}
			if (temp2 > 32767) {
				temp2 -= 65536;
			}
			fr = temp1 - temp2;
		} else if (opnum == op["CPL"]) {	// CPL
			fr = GR[grnum] - (mem[adr] / 10000);
		} else if (opnum == op["SLA"]) {	// SLA
			int temp1 = GR[grnum];
			int sign = temp1 > 32767 ? 32768 : 0;
			temp1 *= 2 ^ adr;
			temp1 %= 32768;
			temp1 += sign;
			GR[grnum] = temp1;
			setfr(temp1);
		} else if (opnum == op["SRA"]) {	// SRA
			int temp1 = GR[grnum];
			int sign = temp1 > 32767 ? 32768 : 0;
			if (sign == 0) {
				temp1 = (temp1 / (2 ^ adr));
			} else {
				temp1 -= sign;
				for (int i = adr; i > 0; i--) {
					temp1 = ((temp1 + sign) / 2);
				}
				temp1 += sign;
			}
			GR[grnum] = temp1;
			setfr(temp1);
		} else if (opnum == op["SLL"]) {	// SLL
			int temp1 = GR[grnum];
			temp1 = temp1 * 2 ^ adr;
			temp1 %= 65536;
			GR[grnum] = temp1;
			setfr(temp1);
		} else if (opnum == op["SRL"]) {	// SRL
			int temp1 = GR[grnum];
			temp1 = (temp1 / (2 ^ adr));
			temp1 %= 65536;
			GR[grnum] = temp1;
			setfr(temp1);
		} else if (opnum == op["START"]) {	// START
			if (adr != 0) {
				pc = (mem[pc] / 10000) - 1;
			}
		} else if (opnum == op["JPZ"]) {	// JPZ
			if (fr >= 0) {
				pc = (mem[pc] / 10000) - 1;
			}
		} else if (opnum == op["JMI"]) {	// JMI
			if (fr < 0) {
				pc = (mem[pc] / 10000) - 1;
			}
		} else if (opnum == op["JNZ"]) {	// JNZ
			if (fr != 0) {
				pc = (mem[pc] / 10000) - 1;
			}
		} else if (opnum == op["JZE"]) {	// JZE
			if (fr == 0) {
				pc = (mem[pc] / 10000) - 1;
			}
		} else if (opnum == op["JMP"]) {	// JMP
			pc = (mem[pc] / 10000) - 1;
		} else if (opnum == op["CALL"]) {	// CALL
			mem[--GR[4]] = (pc + 1) * 10000;
			pc = (mem[pc] / 10000) - 1;
		} else if (opnum == op["RET"]) {	// RET
			if (GR[4] == 65535) {
				errper("スタックアンダーフローが発生しました。");
			}
			pc = ((mem[GR[4]++]) / 10000) - 1;
		} else if (opnum == op["PUSH"]) {	// PUSH
			mem[--GR[4]] = adr * 10000;
		} else if (opnum == op["POP"]) {	// POP
			if (GR[4] == 65535) {
				errper("スタックアンダーフローが発生しました。\n");
			}
			GR[grnum] = mem[GR[4]++] / 10000;
		} else if (opnum == op["OUT"]) {	// OUT
			for (int i = 0; i < mem[mem[pc + 1] / 10000] / 10000; i++) {
				int a = mem[pc] / 10000 + i;
				print("%c", mem[mem[pc] / 10000 + i] / 10000);
			}
			print("\n");
			pc++;
		} else if (opnum == op["IN"]) {		// IN
			if ((mem[mem[pc] / 10000] / 100) % 100 != op["DS"]) {
				segfault();
			}
			char[] _0 = din.readLine();
			int len = _0.length;
			if (len > 80) {
				len = 80;
			}
			for (int i = 0; i < len; i++) {
				if ((mem[mem[pc] / 10000 + i] / 100) % 100 != op["DS"]) {
					segfault();
				}
				mem[mem[pc] / 10000 + i] = (_0[i]&0xff) * 10000 + op["DS"] * 100;
			}
			mem[mem[pc + 1] / 10000] = len * 10000 + op["DS"] * 100;
			pc++;
		} else if (opnum == op["EXIT"]) {	// EXIT
			if (!tron) {
				print("EXIT\n");
			}
			commandip();
			throw new ExitException();
		} else if (opnum == op["END"]) {
			throw new Exception("プログラムは EXIT 命令で終りましょう。\n");
		}
		if (fr > 32767) {
			fr = -1;
		}
		if (line != 0 && tron) {
			pgr();
			if (!endFlag) {
				commandip(); // コマンドインタープリタの呼びだし
			}
		}

	}

}


// 汎用レジスタの内容を表示する
void pgr() {
	for (int j = 0; j < 5; j++) {
		int temp = GR[j];
		if (temp > 32767)
			temp -= 65536;
		print("GR%1d: %-6d ", j, temp);
	}
	if (fr < 0)
		fr = -1;
	else if (fr > 0)
		fr = 1;
	else
		fr = 0;
	print("FR: %d\n", fr);
	print("\n");
}

// FR 値をセットする
void setfr(int num) {
	if (num == 0)
		fr = 0;
	else if (num > 32767)
		fr = -1;
	else
		fr = 1;
}

// Segmentation fault
void segfault() {
	perrline();
	print("セグメント例外が発生しました。\n");
	commandip();
	throw new Exception("セグメント例外発生のため終了します。");
}

void pbit(int x) {
	x = toplus(x) % 65536;
	for (int j = 0; j < 16; j++) {
		x *= 2;
		print("%d", (x < 65536) ? 0 : 1);
		x %= 65536;
	}
	print("\n");
}

void commandip() {
	char[] _0;
	while (true) {
		try {
			_0 = din.readLine();
			if (_0 == "") return;
			char[][] _ = (new RegExp(`[\t ]+`,"g")).split(_0);
			_.length = 3;
			if (_[0] == "list") {
				int j = (_[1] == "") ? 1 : atoi(_[1]);
				int k = (_[2] == "") ? maxline - 1 : atoi(_[2]);
				for (int i = j; i <= k; i++) {
					print("%8d: %.*s \n", i, srcprog[i]);
				}
			} else if (isnum(_[0])) {
				print("%d\n", cast(int)(mem[atoi(_[0])] / 10000));
			} else if (_[0] == "end") {
				endFlag = true;
				return;
			} else if (_[0] == "troff") {
				tron = false;
				return;
			} else if (_[0][0] == '!') {
				system(_[0][1..length]);
			} else if (_[0] == "bit") {
				pbit(nametoval(_[1], _[2]));
			} else {
				print("%d\n", nametoval(_[0], _[1]));
			}
		} catch (Exception e) {
			print("%.*s\n", e.toString());
		}
	}
	throw new ExitException();
}

int nametoval(char[] name, char[] offset) {
	if (isnum(name)) {
		if (isdigit(name)) {
			return atoi(name);
		}
		if (name.length != 5) {
			print("16進数は4桁で書きましょう。\n");
			return 0;
		}
		return (xtod(name[1]) << 12) + (xtod(name[2]) << 8) + (xtod(name[3]) << 4) + xtod(name[4]);
	}
	if ((new RegExp(`^GR[0-4]$`)).test(name) != 0) {
		return GR[name[2]-'0'];
	}
	if ((new RegExp("\'")).test(name) != 0)	{ // 文字定数
		return name[1];
	}
	if (name in symtab) {
		return (mem[symtab[name] + atoi(offset)] / 10000);
	}
	throw new Exception("シンボルテーブルに" ~ name ~ "が見つかりませんでした。");
}