ドカタしながらCOMET II用アセンブラを30日で作る日記(5,6,7,8日目)
awkのCASLのインタプリタを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 ~ "が見つかりませんでした。"); }