俺バーチャルマシン
バーチャルマシンってどんな感じだろーと思ってプログラミングした。多分こんな感じ。
// 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; }