#! /usr/local/bin/gawk -f # CASL インタープリタ # # 作製者 神奈川大学理学部情報科学科2年 # Masamao Izumo # s925505@educ.info.kanagawa-u.ac.jp # # 初版作製日 1993年02月26日(金) # 改訂日 1993年11月05日(金) # # バージョン 3 # # 使用言語 gawk # # 動かし方 casl file_name.casl [-trace] # # オプション -trace トレースモードで実行 # BEGIN { dumpon = 0 srand() if (ARGC != 2 && ARGC != 3) { print "casl file_name [-trace]\n" exit(1) } goend = 0 srcfile = ARGV[1] if (ARGV[2] == "-trace") tron = 1 ARGV[1] = "-" tempfile = "/tmp/casl.tmp" n = split("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", x) for (i = 0; i <= n; i++) op[x[i]] = i jisstr = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ abcdefghijklmnopqrstuvwxyz" for (i = 32; i < length(jisstr) + 32; i++) JIS[i] = substr(jisstr, i - 31, 1); srand() for (i = 0; i < 4; i++) GR[i] = int(rand() * 65536) GR[4] = 65535 # スタックポインタ GR[5] = 0 # ダミーレジスタ fr = int(rand() * 3) - 1 # アセンブラ第一段階 FS = "[\t]+" line = nextmem = 1 start = 0 while (getline 0) { srcprog[line] = $0 # ソースプログラムを登録 sub(/;.*/, "") # 注釈を取る if ($2 == "START") { if (start == 1) { perrline() print "START と END の対応がとれていません。" exit(1) } start = 1 } if (start == 0) continue symtab[$1] = nextmem # 名札の番地を覚える if ($2 != "") { # $2 は命令 if ($2 != "DC") { n = split($3, a, ",") # オペランドの分解 if (n > 3) errop() } else { if (NF != 2 && NF != 3) { perrline() print "DC 命令の値の指定が間違っています。" exit(1) } a[1] = $3 } if ($2 == "RET" && (a[1] != "" || a[2] != "" || a[3] != "")) { errop() } if ($1 != "" && (length($1) > 6 || $1 !~ /^[A-Z][A-Z0-9]*$/)) errop() if (op[$2] == 0) { # 命令が予約語にない perrline() print $2 "という命令はありません。" exit(1) } if (length(a[1]) + length(a[2]) + length(a[3]) >= 72) { perrline() print "オペランド欄は72文字までです。" exit(1) } printf("%s\t%s\t%s\t%s\n",$2, a[1], a[2], a[3]) >tempfile if ($2 == "DS") { if (!isdigit(a[1])) { perrline() print "DS 命令の指定方法が間違っています。" exit(1) } nextmem += toplus(a[1]) } else if ($2 == "DC" && isstr(a[1])) nextmem += length(a[1]) - 2 else if (ismacro($2)) # マクロ nextmem += 2 else nextmem++ } if ($2 == "END") { if (start == 0) { perrline() print "START と END の対応がとれていません。" exit(1) } start = 0 } line++ } if (start != 0) { perrline() print "START と END の対応がとれていません。" exit(1) } maxline = line - 1 close(tempfile) # アセンブラ第二段階 # adrnum アドレスナンバー # grnum 汎用レジスタナンバー # xrnum 指標レジスタナンバー line = nextmem = 1 while (getline 0) { adrnum = grnum = 0 xrnum = 5 # デフォルトはダミーレジスタ srcline[nextmem] = line # アドレス番地に対応する # ソースプログラムの行番号 if (!ismacro($1)) { if (op[$1] <= op["SRL"] || $1 == "POP") { # $3 がアドレス指定 if (!checkgr($2, $4) || $1 == "POP" && ($3 != "" || $4 != "")) errop() adrnum = $3 grnum = $2 xrnum = ($4 == "") ? (" 5") : ($4) } else if (isjump($1) || $1 == "PUSH" || $1 == "CALL" || $1 == "START") { # $2 がアドレス指定 if (!checkgr("", $3)) errop() adrnum = $2 xrnum = ($3 == "") ? (" 5") : ($3) } if ($1 == "DC") { if (isnum($2)) adrnum = $2 else if (isstr($2)) { setstr() continue } else { adrnum = symtab[$2]; } } else if ($1 == "DS") { temp = toplus($2) for (i = 0; i < temp; i++) { mem[nextmem++] = sprintf("%5d%02d00", int(rand() * 65536), op["DS"]) } pdump(nextmem - temp) line++ continue } adrnum = labeltonum(adrnum) % 65536 grnum = substr(grnum, 3, 1) xrnum = substr(xrnum, 3, 1) mem[nextmem] = sprintf("%5d%02d%1d%1d", adrnum, op[$1], grnum, xrnum) pdump(nextmem) nextmem++ } else { #マクロ命令 adr1 = labeltonum($2) # 番地1 adr2 = labeltonum($3) # 番地2 mem[nextmem++] = sprintf("%5d%02d05", adr1, op[$1]) mem[nextmem++] = sprintf("%5d%02d05", adr2, op[$1]) pdump(nextmem - 2) } line++ } # インタープリタ printf("\n") FS = " " for (pc = 1; pc >= 0; pc++) { word = mem[pc] adrnum = int(word / 10000) opnum = int(word / 100) % 100 grnum = int(word / 10) % 10 xrnum = word % 10 adr = (adrnum + GR[xrnum]) % 65536 line = srcline[pc] if (opnum == op["DC"] || opnum == op["DS"]) continue if (line != 0 && tron) printf("line : %d\n%s\n", line, srcprog[line]) if (opnum == op["LD"]) { # LD GR[grnum] = int(mem[adr] / 10000) } else if (opnum == op["ST"]) { # ST temp = int(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 print "fr =", fr } else if (opnum == op["ADD"]) { # ADD GR[grnum] = GR[grnum] + int(mem[adr] / 10000) GR[grnum] %= 65536 setfr(GR[grnum]) } else if (opnum == op["SUB"]) { # SUB GR[grnum] -= int(mem[adr] / 10000) if (GR[grnum] < 0) GR[grnum] += 65536 setfr(GR[grnum]) } else if (opnum == op["AND"]) { # AND GR[grnum] = and(GR[grnum], int(mem[adr] / 1000)) setfr(GR[grnum]) } else if (opnum == op["OR"]) { # OR GR[grnum] = or(GR[grnum], int(mem[adr] / 10000)) setfr(GR[grnum]) } else if (opnum == op["EOR"]) { # EOR GR[grnum] = eor(GR[grnum], int(mem[adr] / 10000)) setfr(GR[grnum]) } else if (opnum == op["CPA"]) { # CPA temp1 = GR[grnum] temp2 = int(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] - int(mem[adr] / 10000) } else if (opnum == op["SLA"]) { #SLA temp1 = GR[grnum] sign = temp1 > 32767 ? 32768 : 0 temp1 *= 2 ^ adr temp1 %= 32768 temp1 += sign GR[grnum] = temp1 setfr(temp1) } else if (opnum == op["SRA"]) { #SRA temp1 = GR[grnum] sign = temp1 > 32767 ? 32768 : 0 if (sign == 0) temp1 = int(temp1 / (2 ^ adr)) else { temp1 -= sign for (i = adr; i > 0; i--) temp1 = int((temp1 + sign) / 2) temp1 += sign } GR[grnum] = temp1 setfr(temp1) } else if (opnum == op["SLL"]) { #SLL temp1 = GR[grnum] temp1 = temp1 * 2 ^ adr temp1 %= 65536 GR[grnum] = temp1 setfr(temp1) } else if (opnum == op["SRL"]) { #SRL temp1 = GR[grnum] temp1 = int (temp1 / (2 ^ adr)) temp1 %= 65536 GR[grnum] = temp1 setfr(temp1) } else if (opnum == op["START"]) { # START if (adr != 0) pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["JPZ"]) { # JPZ if (fr >= 0) pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["JMI"]) { # JMI if (fr < 0) pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["JNZ"]) { # JNZ if (fr != 0) pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["JZE"]) { # JZE if (fr == 0) pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["JMP"]) { # JMP pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["CALL"]) { # CALL mem[--GR[4]] = (pc + 1) * 10000 pc = int(mem[pc] / 10000) - 1 } else if (opnum == op["RET"]) { # RET if (GR[4] == 65535) { errline() print "スタックアンダーフローが発生しました。" exit(1) } pc = int((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) { errline() print "スタックアンダーフローが発生しました。" exit(1) } GR[grnum] = int(mem[GR[4]++] / 10000) } else if (opnum == op["OUT"]) { # OUT for (i = 0; i < int(mem[int(mem[pc + 1] / 10000)] / 10000); i++) printf("%1s", JIS[int(mem[int(mem[pc] / 10000) + i] / 10000)]) printf("\n") pc++ } else if (opnum == op["IN"]) { # IN if (int(mem[int(mem[pc] / 10000)] / 100) % 100 != op["DS"]) segfault() getline len = length($0) if (len > 80) len = 80 for (i = 0; i < len; i++) { if (int(mem[int(mem[pc] / 10000) + i] / 100) % 100 != op["DS"]) segfault() mem[int(mem[pc] / 10000) + i] = (jtoi(substr($0, i + 1, 1))) * 10000 + op["DS"] * 100 } mem[int(mem[pc + 1] / 10000)] = len * 10000 + op["DS"] * 100 pc++ } else if (opnum == op["EXIT"]) { # EXIT if (!tron) print "EXIT" commandip() system("rm /tmp/casl.tmp") exit(0) } else if (opnum == op["END"]) { print "プログラムは EXIT 命令で終りましょう。" exit(1) } if (fr > 32767) fr = -1 if (line != 0 && tron) { pgr() if (goend != 1) commandip() # コマンドインタープリタの呼びだし } } } function perrline() { # エラー行番号を出力 printf("Error: line number in %d\n%s\n", line, srcprog[line]) } # 数字か function isnum(num) { return num ~ /^[+-]?[0-9]*$|^#[A-F0-9]*$/ } # 10進数か function isdigit(num) { return num ~ /^[+-]?[0-9]*$/ } # 文字か function isstr(str) { return str ~ /['].+[']/ } # 0-9A-F を10進に function xtod(num) { if (num < 10) return num return 10 + jtoi(num) - jtoi("A") } # 10進に function todigit(num) { if (num == "") return 0 if (num ~ /#/) { # 16進 if (length(num) != 5) { perrline() print "16進数は4桁で書きましょう。\n" exit(1) } else return xtod(substr(num, 2, 1)) * 4096 + xtod(substr(num, 3, 1)) * 256 + xtod(substr(num, 4, 1)) * 16 + xtod(substr(num, 5, 1)) } else if (num ~ /'/) # 文字定数 return jtoi(substr(num, 2, 1)) else { # 10進 if (num < 0) num += 65536 } return num } # ジャンプ命令か function isjump(command) { return op[command] >= op["JPZ"] && op[command] <= op["JMP"] } # マクロ命令か function ismacro(command) { return command == "IN" || command == "OUT" } # 名前なら番地に function labeltonum(num) { if (!isnum(num)) { if ((num = symtab[num]) == 0) { perrline() print "ラベルが見つかりません。" exit(1) } } return toplus(todigit(num)) } # 汎用レジスタの内容を表示する function pgr(j) { for (j = 0; j < 5; j++) { temp = GR[j] if (temp > 32767) temp -= 65536 printf("GR%1d: %-6d ", j, temp) } if (fr < 0) fr = -1 else if (fr > 0) fr = 1 else fr = 0 print "FR: " fr printf("\n") } # JIS キャラクタに対応するコードを返す function jtoi(j) { return index(jisstr, j) + 31 } # FR 値をセットする function setfr(num) { if (num == 0) fr = 0 else if (num > 32767) fr = -1 else fr = 1 } # CASL 文字列を取ってくる function getstr(str) { gsub(/[']/, "", str) return str } # DC '文字列' の場合の処理 function setstr() { temp = $0 for (i = 0; i < length($2) - 2; i++) mem[nextmem++] = sprintf("%5d%02d00", jtoi(substr(getstr($2), i + 1, 1)), op["DC"]) $0 = temp pdump(nextmem - length($2) + 2) line++ } # メモリダンプの表示 function pdump(m) { if (dumpon) printf("%8d:%15d %s\n", line, mem[m], srcprog[line]) } # Segmentation fault function segfault() { perrline() print "セグメント例外が発生しました。" commandip() exit(1) } function eor(x, y) { return and(or(x, y), not(and(x, y))) } function pbit(x, j) { x = toplus(x) % 65536 for (j = 0; j < 16; j++) { x *= 2 printf("%d", (x < 65536) ? 0 : 1) x %= 65536 } printf("\n") } function not(x, j, ret, mask) { ret = 0 mask = 1 for (j = 0; j < 16; j++) { x /= 2 if (x == int(x)) ret += mask x = int(x) mask *= 2 } return ret } function and(x, y, j, ret, mask) { ret = 0 mask = 1 for (j = 0; j < 16; j++) { x /= 2 y /= 2 if (x != int(x) && y != int(y)) ret += mask x = int(x) y = int(y) mask *= 2 } return ret } function or(x, y, j, ret, mask) { ret = 0 mask = 1 for (j = 0; j < 16; j++) { x /= 2 y /= 2 if ((x != int(x)) || (y != int(y))) ret += mask x = int(x) y = int(y) mask *= 2 } return ret } # 正に変換 function toplus(num) { while (num < 0) num += 65536 return num } function checkgr(gr1, gr2) { return !(gr1 != "" && (gr1 !~ /^GR[0-4]$/) || gr2 != "" && (gr2 !~ /^GR[1-4]$/)) } function errop() { perrline() print "オペランド欄の指定方法が間違っています。" exit(1) } function commandip( j,k,l) { while ((l = getline) > 0) { if ($0 == "") return if ($1 == "list") { j = ($2 == "") ? 1 : $2 k = ($3 == "") ? maxline - 1 : $3 for (i = j; i <= k; i++) printf("%8d: %s \n", i, srcprog[i]) } else if (isnum($1)) { print int(mem[$1] / 10000) } else if ($1 == "end") { goend = 1 return } else if ($1 == "troff") { tron = 0 return } else if ($1 ~ /^!/) { system(substr($0, 2)) } else if ($1 == "bit") { pbit(nametoval($2, $3)) } else { print nametoval($1, $2) } } exit(0) } function nametoval(name, offset) { if (isnum(name)) { if (isdigit(name)) { return name } if (length(name) != 5) { print "16進数は4桁で書きましょう。\n" return 0 } return xtod(substr(name, 2, 1)) * 4096 + xtod(substr(name, 3, 1)) * 256 + xtod(substr(name, 4, 1)) * 16 + xtod(substr(name, 5, 1)) } if (name ~ /^GR[0-4]$/) { return GR[substr(name, 3, 1)] } if (name ~ /'/) # 文字定数 return jtoi(substr(name, 2, 1)) return int(mem[symtab[name] + offset] / 10000) } # バージョン1との変更点 # # ユーザの入力ミスに対するエラーを厳しくした。 # ! で始まるコマンドを UNIX のコマンドとして実行できるようにした。 # バージョン2・1との変更点 # 英子文字が使えるようになった。 # バージョン2との変更点(1993年11月05日(金)) # bit の引数に、ラベル、GR、16進、ジスキャラクタ('で囲った文字)を # 指定できるようになった。 # C-d で終了するようになった。