俺バーチャルマシン

バーチャルマシンってどんな感じだろーと思ってプログラミングした。多分こんな感じ。

// vm.cpp

#include<iostream>
using namespace std;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef uchar rgst_t; // レジスタ型
#define MEM_SIZE (0xffff)

// アーキテクチャ
// ビット数 8bit
// 主メモリ 64Kbyte
// 命令語長 可変
// リトルエンディアン

// 命令におけるレジスタID
// A~Gに0~7を割り振る。

// 命令表
// ID 名前 フォーマット フラグ変化 (備考)
// 00 mov [move レジスタID 即値] zero
// 01 mov [move レジスタID レジスタID] zero 
// 02 lod [lod レジスタID メモリ番地] - ロード
// 03 add [add レジスタID 即値] zero 
// 04 add [add レジスタID レジスタID] zero 
// 05 sub [sub レジスタID 即値] zero 
// 06 sub [sub レジスタID レジスタID] zero
// 07 rsh [右シフト レジスタID シフト数] zero 右シフト
// 08 lsh [左シフト レジスタID シフト数] zero 左シフト
// 09 and [and レジスタID 即値] zero
// 0a and [and レジスタID レジスタID] zero
// 0b or [or レジスタID 即値] zero
// 0c or [or レジスタID レジスタID] zero
// 0d not [not レジスタID] zero
// 0e xor [xor レジスタID 即値] zero
// 0f xor [xor レジスタID レジスタID] zero
// 10 jp [jp 下位アドレス 上位アドレス] -
// 11 jpz [jpz 下位アドレス 上位アドレス] -
// 12 jpnz [jpnz 下位アドレス 上位アドレス] -
// 13 out [out レジスタID,メモリ番地] - ストア
// 14 push ab [] - ABレジスタをスタックにプッシュ
// 15 push cd [] - CDレジスタをスタックにプッシュ
// 16 push ef [] - EFレジスタをスタックにプッシュ
// 17 pop ab [] - スタックをポップしてA,Bレジスタにコピー
// 18 pop cd [] - スタックをポップしてC,Dレジスタにコピー
// 19 pop ef [] - スタックをポップしてE,Fレジスタにコピー
// 1a nop [] - no operation
// 1b dump [dump レジスタID] - レジスタの値をターミナルに(16進で)吐き出す
// 1c call [call メモリ上位アドレス 下位アドレス] - 戻りアドレスをスタックにプッシュしてジャンプ
// 1d ret [] - 戻りアドレスをポップしてジャンプ
// 1e printstr [] - DEレジスタで与えられたメモリの番地からNULL文字までターミナルに出力する
// ff halt

//
// 例) 10x10をcレジスタに格納
// バイナリ  |  ニモニック
//--------------------------
// 00 00 0a |  mov a, 0x0a
// 00 01 0a |  mov b, 0x0a
// 0f 02 02 |  xor c, c
//          | LABEL:
// 04 02 01 |  add c, b
// 05 00 01 |  sub a 1
// 12 09 00 |  jpnz LABEL
// 1b 02    |  dump c
// ff       |  halt
//
// 例) サブルーチンコール ..Aレジスタをサブルーチン先で退避させる
// バイナリ  |  ニモニック
//--------------------------
// 00 00 ff |  mov a, 0xff
// 1c 09 00 |  call LABEL
// 1b 00    |  dump a
// ff       |  halt
//          | LABEL:
// 14       |  push ab
// 0f 00 00 |  xor a, a
// 17       |  pop ab
// 1d       |  ret
//
// 例) hello, world!
// バイナリ  |  ニモニック
//--------------------------
// 00 03 00 |  mov d, 0x00
// 00 04 0a |  mov e, 0x0a
// 1e       |  printstr
// ff       |  halt
//          | DATA:
// 68 65 6c |  'h' 'e' 'l'
// 6c 6f 2c |  'l' 'o' ','
// 20 77 6f |  ' ' 'w' 'o'
// 72 6c 64 |  'r' 'l' 'd'
// 21 0a 00 |  '!' '\n' '\0'
//

#define HALT (0xff)

rgst_t A, B, C, D, E, F;  // 汎用レジスタ
rgst_t *registers[] = {&A,&B,&C,&D,&E,&F};
bool zerop; // ゼロフラグ
rgst_t sp; // スタックポインタ
rgst_t ic; // インストラクションカウンタ
uchar memory[MEM_SIZE]; // 主メモリ
uchar inst, arg1, arg2;

void init_vm()
{
  sp = MEM_SIZE;
  ic = 0;
}

// スタック操作
void push(uchar val)
{ memory[--sp] = val; }
uchar pop()
{ uchar x = memory[sp++];
  return x;
}

#define REGARG1 (*registers[arg1])
#define REGARG2 (*registers[arg2])
#define FUNC(OP, RHS)				\
  do{						\
    *registers[arg1] OP RHS;			\
    zerop = *registers[arg1]==0;		\
  }while(0)
// imediate value function macro
#define IMED(OP) FUNC(OP, arg2)
// register value function macro
#define RGST(OP) FUNC(OP, *registers[arg2])
    
// 命令に対応する関数の定義
void mov_rgst_imed(){ IMED(=); }
void mov_rgst_rgst(){ RGST(=); }
void lod(){ REGARG1 = memory[arg2]; }
void add_rgst_imed(){ IMED(+=); }
void add_rgst_rgst(){ RGST(+=); }
void sub_rgst_imed(){ IMED(-=); }
void sub_rgst_rgst(){ RGST(-=); }
void rsh(){ IMED(>>=); }
void lsh(){ IMED(<<=); }
void and_rgst_imed(){ IMED(&=); }
void and_rgst_rgst(){ RGST(&=); }
void or_rgst_imed(){ IMED(|=); }
void or_rgst_rgst(){ RGST(|=); }
void bitnot()
{
  REGARG1 = ~REGARG1;
  zerop = REGARG1==0;
}
void xor_rgst_imed(){ IMED(^=); }
void xor_rgst_rgst(){ RGST(^=); }
void jp()
{ ic = (arg2<<8) + arg1;
  ic -= 3; // インストラクションカウンタの帳尻合わせ (--;)
}
void jpz(){ if(zerop) jp(); }
void jpnz(){ if(!zerop) jp(); }
void out(){ memory[arg2]=REGARG1; }
void push_ab()
{
  push(*registers[0]);
  push(*registers[1]);
}
void push_cd()
{
  push(*registers[2]);
  push(*registers[3]);
}
void push_ef()
{
  push(*registers[4]);
  push(*registers[5]);
}
void pop_ab()
{
  *registers[1] = pop();
  *registers[0] = pop();
}
void pop_cd()
{
  *registers[3] = pop();
  *registers[2] = pop();
}
void pop_ef()
{
  *registers[5] = pop();
  *registers[4] = pop();
}
void nop()
{
  // nop
}
void dump()
{ printf("%x", (uint)REGARG1); }
void call()
{
  push(ic+3);
  jp();
}
void ret()
{
  ic = pop();
  ic--; // 帳尻合わせ・・
}
void printstr()
{
  int x=(D<<8)|E;
  while(memory[x] !='\0')
    printf("%c", memory[x++]);
}
/////////////////////

void (*inst_func[255])()={
  // それぞれの命令番号に対応する関数のポインタ
  mov_rgst_imed, mov_rgst_rgst,
  lod,
  add_rgst_imed, add_rgst_rgst,
  sub_rgst_imed, sub_rgst_rgst,
  rsh, lsh,
  and_rgst_imed, and_rgst_rgst,
  or_rgst_imed, or_rgst_rgst,
  bitnot,
  xor_rgst_imed, xor_rgst_rgst,
  jp, jpz, jpnz,
  out,
  push_ab, push_cd, push_ef,
  pop_ab, pop_cd, pop_ef,
  nop, dump,
  call, ret,
  printstr
};
int args[255]={
  // それぞれの命令のとる引数の数
  2,2,2,2,2,
  2,2,2,2,2,
  2,2,2,1,2,
  2,2,2,2,2,
  0,0,0,0,0,
  0,0,1,2,0,
  0,
};

void load_program(const char*prog_name)
{
  FILE *fp;
  int c;
  int idx=0;
  fp = fopen(prog_name, "rb");
  if(!fp){
    cerr << "cannot open file `" << prog_name << "'" << endl;
    exit(-1);
  }
  while((c = fgetc(fp)) != EOF)
    memory[idx++] = (uchar)c;
  fclose(fp);
}

void run_program()
{
  while(memory[ic] != HALT){
    inst = memory[ic];

    // set argument
    if(args[inst]>=1)
      arg1 = memory[ic + 1];
    if(args[inst]>=2)
      arg2 = memory[ic + 2];

    inst_func[inst]();
    ic += args[inst] + 1;
  }
}

int main(int argc, char **argv)
{
  if(argc < 2){
    printf("usage: ./vm binfile\n");
    return 0;
  }
  init_vm();

  printf("[note]: dumped value is hexadecimal.\n");
  load_program(argv[1]);
  run_program();
  return 0;
}