MinIPSのアセンブラもどき以前の何か

「MinIPS(電気通信大学にてCPU教育用に用いられてるMIPSもどきプロセッサ)のアセンブラもどき以前の何か」略して「みか」を書きました。嘘です略しません。
http://konbu.s13.xrea.com/lib/minipsm.exe
何に使うかと言うと、たぶん電気通信大学電気通信学部情報工学科の情報工学実験プロセッサ課題をやるときです。使い方はこんな感じ。アセンブリプログラムを入力すると、16進数にアセンブルします。

C:\hoge>cat hoge.s
addi $1, $0, 100
add $1, $1, $1
sub $1,$2, $3
j 32

C:\hoge>minipsm asm hoge.s hoge.out

C:\hoge>cat hoge.out
20010064
00210820
00430822
08000020

C:\hoge>

あとまあ存在意味のわからん2<->16進数変換モードとかあります。Windowsの電卓とか使えばいいです。というかPerlだったらpack使ってワンライナーにできます。

C:\hoge>minipsm b2h
0000 1000 1100 1111
08CF
^Z

C:\hoge>minipsm h2b
ab10
1010 1011 0001 0000
^Z

C:\hoge>

引数与えずに呼びだすとusageじみたメッセージを出力します。

C:\hoge>minipsm
C:\hoge\minipsm.exe (asm|dsm|b2h|h2b) [input [output]]

dsm」とか、いかにもディスアセンブルしそうですけど嘘です。つくってないからありません。

以下コード。無意味にD言語で書きました。しかもたぶん、DMD2.007じゃないと動かんような気もします。そうでもなかったかもしれません。

import std.stream;
import std.cstream;
import std.string;
import std.ctype;
import std.regexp;

string radixalphabets(uint radix)
{
  static invariant char[] fullalphabets = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  return fullalphabets[0..radix];
}
bool isalphabet(char c, string alphabets)
{
  foreach(char alph; alphabets)
  {
    if(c==alph) return true;
  }
  return false;
}
string remsp(string str)
{
  char[] rem;
  foreach(char c; str){
    if(!isspace(c)) rem ~= c;
  }
  return rem.idup;
}
string bin2hex(string line)
{
  char[] hex;
  string binalph = radixalphabets(2);
  string hexalph = radixalphabets(16);
  string bin_s = remsp(line);
  for(int i=0;i+3<bin_s.length;i+=4){
    string byte_s = bin_s[i..i+4];
    byte h = 0;
    foreach(char c; byte_s){
      if(!isalphabet(c, binalph)){
        return "";
      }
      h = h*2 + (c-'0');
    }
    hex ~= hexalph[h];
  }
  return hex.idup;
}
string hex2bin(string line)
{
  char[] bin;
  string hex = line.toupper();
  string hexalph = radixalphabets(16);
  foreach(char c; hex){
    if(!isalphabet(c, hexalph)) return "";
    byte h = c>='A'? c-'A'+10 : c-'0';
    for(int i=8;i>0;i/=2){
      byte b = h / i;
      h -= b * i;
      bin ~= b==0 ? '0' : '1';
    }
    bin ~= ' ';
  }
  return bin.idup;
}
alias string delegate(string) convType;
convType[string] optable;
int[string] funtable;

string to_s(string num, int radix, int len)
{
  return to_s(atoi(num), radix, len);
}
string to_s(int num, int radix, int len)
{
  char[] tostr = new char[len];
  string base = toString(cast(long)num, cast(uint)radix);
  if(base.length==len) return base;
  for(int i=0;i<len;++i){
    tostr[i] = i+base.length<len ? '0' : base[i+base.length-len];
  }
  return tostr.idup;
}
void initasm()
{
  RegExp RPat = new RegExp("^\\s*(\\S+)\\s+\\$(\\d+)\\s*,\\s*\\$(\\d+)\\s*,\\s*\\$(\\d+)");
  RegExp IPat = new RegExp("^\\s*(\\S+)\\s+\\$(\\d+)\\s*,\\s*\\$(\\d+)\\s*,\\s*(\\d+)");
  RegExp MPat = new RegExp("^\\s*(\\S+)\\s+\\$(\\d+)\\s*,\\s*(\\d+)(\\(\\$(\\d+)\\))?");
  RegExp EPat = new RegExp("^\\s*(\\S+)\\s+\\$(.+)\\s*,\\s*\\$(.+)");
  RegExp JPat = new RegExp("^\\s*(\\S+)\\s+(\\d+)\\s*");

  string op_func(string line){
    if(string[] mat = RPat.match(line)){
      if(!(mat[1] in funtable)) return "";
      string op = "000000";
      string rs = to_s(mat[3], 2, 5);
      string rt = to_s(mat[4], 2, 5);
      string rd = to_s(mat[2], 2, 5);
      string shamt = "00000";
      string fun = to_s(funtable[mat[1]], 2, 6);
      string instruction = op~rs~rt~rd~shamt~fun;
      return bin2hex(instruction);
    }
    return "";
  }
  string op_shamt(string line){
    if(string[] mat = IPat.match(line)){
      if(!(mat[1] in funtable)) return "";
      string op = "000000";
      string rs = "00000";
      string rt = to_s(mat[3], 2, 5);
      string rd = to_s(mat[2], 2, 5);
      string shamt = to_s(mat[4], 2, 5);
      string fun = to_s(funtable[mat[1]], 2, 6);
      string instruction = op~rs~rt~rd~shamt~fun;
      return bin2hex(instruction);
    }
    return "";
  }
  string op_imm(string line){
    if(string[] mat = IPat.match(line)){
      if(!(mat[1] in funtable)) return "";
      string op = to_s(funtable[mat[1]], 2, 6);
      string rs = to_s(mat[3], 2, 5);
      string rd = to_s(mat[2], 2, 5);
      string imm = to_s(mat[4], 2, 16);
      string instruction = op~rs~rd~imm;
      return bin2hex(instruction);
    }
    return "";
  }
  string op_mem(string line){
    if(string[] mat = MPat.match(line)){
      if(!(mat[1] in funtable)) return "";
      string op = to_s(funtable[mat[1]], 2, 6);
      string rs = mat[5] ? to_s(mat[5], 2, 5) : to_s(0, 2, 5);
      string rd = to_s(mat[2], 2, 5);
      string imm = to_s(mat[3], 2, 16);
      string instruction = op~rs~rd~imm;
      return bin2hex(instruction);
    }
    return "";
  }
  string op_jump(string line){
    if(string[] mat = JPat.match(line)){
      if(!(mat[1] in funtable)) return "";
      string op = to_s(funtable[mat[1]], 2, 6);
      string target = to_s(mat[2], 2, 26);
      string instruction = op~target;
      return bin2hex(instruction);
    }
    return "";
  }

  optable["add"] = &op_func;
  optable["sub"] = &op_func;
  optable["addu"] = &op_func;
  optable["subu"] = &op_func;
  optable["and"] = &op_func;
  optable["or"] = &op_func;
  optable["sllv"] = &op_func;
  optable["srlv"] = &op_func;
  optable["srav"] = &op_func;
  optable["slt"] = &op_func;
  optable["sltu"] = &op_func;

  optable["sll"] = &op_shamt;
  optable["srl"] = &op_shamt;
  optable["sra"] = &op_shamt;

  optable["addi"] = &op_imm;
  optable["addiu"] = &op_imm;
  optable["andi"] = &op_imm;
  optable["ori"] = &op_imm;
  optable["beq"] = &op_imm;
  optable["bne"] = &op_imm;
  optable["slti"] = &op_imm;
  optable["sltiu"] = &op_imm;

  optable["lb"] = &op_mem;
  optable["lw"] = &op_mem;
  optable["sb"] = &op_mem;
  optable["sw"] = &op_mem;
  optable["lui"] = &op_mem;

  optable["j"] = &op_jump;
  optable["jr"] = &op_jump;

  optable["mfe"] = (string line){
    if(string[] mat = EPat.match(line)){
      return bin2hex("010000"~"00000"~"01110"~to_s(mat[2], 2, 5)~to_s(0,2,11));
    }
    return "".idup;
  };
  optable["mte"] = (string line){
    if(string[] mat = EPat.match(line)){
      return bin2hex("010000"~"00100"~to_s(mat[3], 2, 5)~"01100"~to_s(0,2,11));
    }
    return "".idup;
  };
  optable["rfe"] = (string){return bin2hex("010000"~"1"~to_s(0,2,19)~"100000");};

  funtable["add"] = 32;
  funtable["sub"] = 34;
  funtable["addu"] = 33;
  funtable["subu"] = 35;
  funtable["and"] = 36;
  funtable["or"] = 37;
  funtable["sllv"] = 4;
  funtable["srlv"] = 6;
  funtable["srav"] = 7;
  funtable["slt"] = 42;
  funtable["sltu"] = 43;

  funtable["sll"] = 0;
  funtable["srl"] = 2;
  funtable["sra"] = 3;

  funtable["addi"] = 8;
  funtable["addiu"] = 9;
  funtable["andi"] = 12;
  funtable["ori"] = 13;
  funtable["beq"] = 4;
  funtable["bne"] = 5;
  funtable["slti"] = 10;
  funtable["sltiu"] = 11;

  funtable["lb"] = 32;
  funtable["lw"] = 35;
  funtable["sb"] = 40;
  funtable["sw"] = 43;
  funtable["lui"] = 15;

  funtable["j"] = 2;
  funtable["jal"] = 3;
}
string asmble(string line)
{
  RegExp opPat = new RegExp("^\\s*(\\S+)\\s*");
  if(opPat.match(line)){
    string op = opPat.match(1);
    if(op in optable){
      return optable[op](line);
    }
    else{
      return op;
    }
  }
  return "";
}

int main(string[] args)
{
  Stream input, output;
  if(args.length==1){
    dout.writeLine(args[0]~" (asm|dsm|b2h|h2b) [input [output]]");
    return 0;
  }

  string function(string)[string] convs;
  convs["b2h"] = &bin2hex;
  convs["h2b"] = &hex2bin;
  convs["asm"] = &asmble;
  convs["dsm"] = &asmble;

  initasm();

  if(args.length==2){
    input = din;
    output = dout;
  }
  else if(args.length==3){
    input = new BufferedFile(args[2]);
    output = dout;
  }
  else{
    input = new BufferedFile(args[2]);
    output = new BufferedFile(args[3], FileMode.OutNew);
  }

  if(!(args[1] in convs)){
    dout.writeLine("unknown mode");
    return 1;
  }
  string function(string) conv = convs[args[1]];
  foreach(char[] line; input){
    output.writeLine(conv(line.idup));
  }
  output.flush();
  output.close();
  return 0;
}

なんかこー、センスねーなーとおもう。ぜってーもっとスマートに書けるし書くべき。ただまあ抑圧して、書かんでいるより馬鹿晒してる方がマシかなあとも思う。あー、そういやoptableとfuntableに分ける意味がわかんねー。ひどいなあ。op_hogehoge系のdelegateがほとんど似たようなことばかりやってて、似たコードをたくさんくりかえしてるのは罪悪だと思う。ふがふが。

ほんとはbindしてほげほげにしたかったけど、phobosのstd.bindはバグバグらしいとの情報を2chで聞いたから避ける。クロージャつかえばいいかそういや。

test