仮想マシン
仮想マシン(Virtual Machine)
独自に設計した仮想マシン(VM)
機械語の命令セットを定義し、そのアセンブラを
flex, bisonで実装。
また仮想マシン自体はC言語で実装。
VMの状態をリアルタイムで観察できるようにXでの表示を行った。
上記のようにベースのVMはC言語で実装したが、そのVM上で
動作する仮想マシンを上記のアセンブラ言語で実装した。
仮想マシン上で動作する仮想マシンを設け、プログラムを実行出来るようにした。
更にその上で動作する仮想マシンを動作させ...5世代まで仮想マシンを動作させてみた。
世代を経るに連れ段々と実行が遅くなってくるのが面白かった。
最近は動かしていないが、昔のPC上でXでVM内部の状態表示させながらだとかなり遅かった。
仮想マシンの機械語を定義する。簡単に実装するために
スタック型のCPUとした。基本命令数は15個。
ファイルのロードや標準入出力など面倒なので高水準命令を
追加で5つ定義している。
instructions.h
/* * instructions.h */ #define CODE_NOP 0 #define CODE_PUSH 1 #define CODE_POP 2 #define CODE_PUSHI 3 #define CODE_POPI 4 #define CODE_REMOVE 5 #define CODE_ADD 6 #define CODE_SUB 7 #define CODE_SFTR 8 #define CODE_SFTL 9 #define CODE_JUMP 10 #define CODE_JLT 11 #define CODE_JEQ 12 #define CODE_JGT 13 #define CODE_HALT 14 /* HIGH LEVEL INSTRUCTIONS */ #define CODE_LOAD 15 /* DEBUGGING INSTRUCTIONS */ #define CODE_OUT 16 #define CODE_DUMP 17 #define CODE_PRINT 18 #define CODE_BRK 19 /* -------------------------------------------------------- code data type definition +-----------------------------------+ | opcode | | operand | +-----------------------------------+ -------------------------------------------------------- load file data format +---------------+ - | | | | codes | | | | | data | | | | | | +---------------+ - -------------------------------------------------------- Description of each instructions [NOP] no operation. just increment PC NOP [PUSH] push data PUSH data SP++ MEM[SP] = data [POP] pop data POP data data = MEM[SP] SP--; [PUSHI] push data with index PUSHI MEM[SP] = data[MEM[SP]] [POPI] pop data with index POPI data[MEM[SP-1]] = MEM[SP] SP-- SP-- [REMOVE] remove stack REMOVE SP-- [ADD] add two data in stack ADD MEM[SP-1] = MEM[SP-1] + MEM[SP] SP--; [SUB] sub two data in stack SUB MEM[SP-1] = MEM[SP-1] - MEM[SP] SP--; [SFTR] shift right SFTR offset MEM[SP] = MEM[SP] >> offset [SFTL] shift left SFTL offset MEM[SP] = MEM[SP] << offset [JUMP] change PC JUMP location PC = location [JLT] jump if less than JLT location if(MEM[SP-1] < MEM[SP]) PC location SP = SP-2; [JEQ] jump if equal JEQ location if(MEM[SP-1] == MEM[SP]) PC location SP = SP-2; [JGT] jump if greater than JGT location if(MEM[SP-1] > MEM[SP]) PC location SP = SP-2; [HALT] halt VM HALT [LOAD] load file LOAD load file [OUT] print top stack value OUT print(MEM[SP]) [DUMP] print memory DUMP data for(i=0;i<8;i++) print(data[i]) [PRINT] print string PRINT [BRK] break BRK break the execution */
/* * vm.h */ #define MEM_LENGTH 4096
/* * vm.c */ #include <stdio.h> #include "vm.h" #include "instructions.h" /* instruction set definition */ #include "vminspect.h" /* inspector with X */ int swap(int src); static void strExtract(int offset, char *str); static char opCode(int pc); static short opRand(int pc); static void out(int data); static void initEnv(void); static void load(char *file_name, int offset); static void vm(void); static void memorydump(void); /* * Trace flag */ int d_trace_on = 0; int d_step_on = 1; int d_step_interval = 10000; /* 10msec. */ int d_instruction_count = 0; /* * register in CPU */ int pc; int fp; int sp; /* * memory */ int *mem; int memory[MEM_LENGTH]; #define CHECK_AREA(a) if((a < 0) || (MEM_LENGTH < a)){ printf("OUT OF RANGE %d\n", a); return; } /* * string buffer */ char string[128]; /*----------------------------------------------------------*/ int main(int argc, char *argv[]) { char *load_file; load_file = *(++argv); initEnv(); load(load_file, 0); vm(); #if 0 printf("Total instruction number is %d\n", d_instruction_count); #endif } /*----------------------------------------------------------*/ void initEnv(void) { int i; mem = memory; for(i=0;i<MEM_LENGTH;i++) mem[i] = 0; pc = 0; fp = 0; sp = MEM_LENGTH; initVmInspect(); } /*----------------------------------------------------------*/ void vm(void) { short offset; char c; for(;;){ d_instruction_count++; getEvent(); CHECK_AREA(pc) c = opCode(pc); switch(c){ case CODE_NOP: if(d_trace_on) printf("%4d: NOP\n", pc); pc++; break; case CODE_PUSH: offset = opRand(pc); if(d_trace_on) printf("%4d: PUSH %d\n", pc, offset); CHECK_AREA(sp-1) CHECK_AREA(fp+offset) sp--; mem[sp] = mem[fp+offset]; pc++; break; case CODE_POP: offset = opRand(pc); if(d_trace_on) printf("%4d: POP %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(fp+offset) mem[fp+offset] = mem[sp]; sp++; pc++; break; case CODE_PUSHI: if(d_trace_on) printf("%4d: PUSHI\n", pc); CHECK_AREA(sp) CHECK_AREA(fp+swap(mem[sp])) mem[sp] = mem[fp+swap(mem[sp])]; pc++; break; case CODE_POPI: if(d_trace_on) printf("%4d: POPI\n", pc); CHECK_AREA(sp) CHECK_AREA(sp+1) CHECK_AREA(fp+swap(mem[sp+1])) mem[fp+swap(mem[sp+1])] = mem[sp]; sp++; sp++; pc++; break; case CODE_REMOVE: if(d_trace_on) printf("%4d: REMOVE\n", pc); sp++; pc++; break; case CODE_ADD: if(d_trace_on) printf("%4d: ADD\n", pc); CHECK_AREA(sp) CHECK_AREA(sp+1) mem[sp+1] = swap(swap(mem[sp+1]) + swap(mem[sp])); sp++; pc++; break; case CODE_SUB: if(d_trace_on) printf("%4d: SUB\n", pc); CHECK_AREA(sp) CHECK_AREA(sp+1) mem[sp+1] = swap(swap(mem[sp+1]) - swap(mem[sp])); sp++; pc++; break; case CODE_SFTR: offset = opRand(pc); if(d_trace_on) printf("%4d: SFTR %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(fp+offset) mem[sp] = swap(swap(mem[sp]) >> swap(mem[fp+offset])); pc++; break; case CODE_SFTL: offset = opRand(pc); if(d_trace_on) printf("%4d: SFTR %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(fp+offset) mem[sp] = swap(swap(mem[sp]) << swap(mem[fp+offset])); pc++; break; case CODE_JUMP: offset = opRand(pc); if(d_trace_on) printf("%4d: JUMP %d\n", pc, offset); pc = fp + offset; break; case CODE_JLT: offset = opRand(pc); if(d_trace_on) printf("%4d: JLT %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(sp+1) if(swap(mem[sp+1]) < swap(mem[sp])) pc = fp + offset; else pc++; sp += 2; break; case CODE_JEQ: offset = opRand(pc); if(d_trace_on) printf("%4d: JEQ %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(sp+1) if(swap(mem[sp+1]) == swap(mem[sp])) pc = fp + offset; else pc++; sp += 2; break; case CODE_JGT: offset = opRand(pc); if(d_trace_on) printf("%4d: JGT %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(sp+1) if(swap(mem[sp+1]) > swap(mem[sp])) pc = fp + offset; else pc++; sp += 2; break; case CODE_HALT: if(d_trace_on) printf("%4d: HALT\n", pc); return; /* HIGH LEVEL INSTRUCTIONS */ case CODE_LOAD: if(d_trace_on) printf("%4d: LOAD %d\n", pc, offset); CHECK_AREA(sp) CHECK_AREA(sp+1) strExtract(fp+swap(mem[sp]), string); load(string, fp+swap(mem[sp+1])); sp++; sp++; pc++; break; /* DEBUGGING INSTRUCTIONS */ case CODE_OUT: CHECK_AREA(sp) out(mem[sp]); pc++; break; case CODE_DUMP: memorydump(); pc++; break; case CODE_PRINT: CHECK_AREA(sp) strExtract(fp+swap(mem[sp]), string); printf("%s\n", string); sp++; pc++; break; case CODE_BRK: d_step_on = 1; pc++; break; default: printf("Undefined instruction\n"); break; } } } /*----------------------------------------------------------*/ int swap(int src) { int dst; ((char *)(&dst))[0] = ((char *)(&src))[3]; ((char *)(&dst))[1] = ((char *)(&src))[2]; ((char *)(&dst))[2] = ((char *)(&src))[1]; ((char *)(&dst))[3] = ((char *)(&src))[0]; return dst; } /*----------------------------------------------------------*/ char opCode(int pc) { char c; c = ((char *)(&(mem[pc])))[0]; return c; } /*----------------------------------------------------------*/ short opRand(int pc) { short d; ((char *)(&d))[1] = ((char *)(&(mem[pc])))[2]; ((char *)(&d))[0] = ((char *)(&(mem[pc])))[3]; return d; } /*----------------------------------------------------------*/ void load(char *file_name, int offset) { FILE *file; int d; int i; char data[4]; file = fopen(file_name, "r"); if(file == NULL){ fprintf(stderr, "Source file cannot be opened\n"); exit(-1); } for(i=0;;i++){ d = getc(file); if(EOF == d) break; data[0] = (char)d; d = getc(file); if(EOF == d) break; data[1] = (char)d; d = getc(file); if(EOF == d) break; data[2] = (char)d; d = getc(file); if(EOF == d) break; data[3] = (char)d; CHECK_AREA(offset+i) mem[offset+i] = *((int *)data); } printf("load %s at %d - %d\n", file_name, offset, offset+i); fclose(file); } /*----------------------------------------------------------*/ static void strExtract(int offset, char *str) { int i; for(i=0; i<32; i++){ CHECK_AREA(offset+i) if(swap(mem[offset+i]) == 0) break; str[i] = (char)swap(mem[offset+i]); } str[i] = '\0'; } /*----------------------------------------------------------*/ void out(int data) { printf("OUT : %d\n", swap(data)); } /*----------------------------------------------------------*/ static void memorydump(void) { int address = 0; int d; char data[17]; int i, ptr; data[16] = 0; /* for string terminator */ ptr = 0; for(;;){ printf("%05X :", address); /* offset display */ for(i=0;i<16;i++){ data[i] = ((char *)mem)[ptr]; if(i % 4 == 0){ printf(" "); } printf("%02X", (unsigned char)data[i]); /* eliminate undisplayable character */ if((data[i] < 0x20) || (0x7E < data[i])){ data[i] = '.'; } ptr++; if(ptr >= MEM_LENGTH*4) break; } printf(" : %s", data); printf("\n"); address += 16; if(ptr >= MEM_LENGTH*4) break; } }
X表示部分のヘッダ
vminspect.h
/* * vminspect.h */ void initVmInspect(void); int getEvent(void); enum{ EVT_QUIT, EVT_NOP };
X表示処理部
vminspect.c
/* * vminspect.c */ #include <X11/Xlib.h> #include <X11/Xutil.h> #include <X11/keysym.h> #include <stdio.h> #include "vm.h" #include "vminspect.h" #define VMINSPECT 0 Display *display; int screen; Window window; GC gc; XEvent event; extern int pc; extern int fp; extern int sp; extern int mem[]; extern int swap(int src); extern int d_step_on; extern int d_trace_on; extern int d_step_interval; #define CANVAS_WIDTH 240 #define CANVAS_HEIGHT 260 #define FIELD_WIDTH 60 #define FIELD_HEIGHT 20 #define X_REG 50 #define Y_REG 120 #define FIELD_MARGIN 5 #define X_STACK 150 #define Y_STACK 40 #define X_PC_STRING 25 #define Y_PC_STRING 55 #define STRING_MARGIN 25 #define X_SOFFSET 5 #define Y_SOFFSET 15 #define STACK_NUM 8 int r_loc[3][4] = { { X_REG, Y_REG+(FIELD_HEIGHT+FIELD_MARGIN)*0, FIELD_WIDTH, FIELD_HEIGHT}, { X_REG, Y_REG+(FIELD_HEIGHT+FIELD_MARGIN)*1, FIELD_WIDTH, FIELD_HEIGHT}, { X_REG, Y_REG+(FIELD_HEIGHT+FIELD_MARGIN)*2, FIELD_WIDTH, FIELD_HEIGHT} }; int s_loc[STACK_NUM+1][4] = { { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*0, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*1, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*2, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*3, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*4, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*5, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*6, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*7, FIELD_WIDTH, FIELD_HEIGHT}, { X_STACK, Y_STACK+(FIELD_HEIGHT+FIELD_MARGIN)*8, FIELD_WIDTH, FIELD_HEIGHT} }; static void drawFrame(void); static void drawPC(int value); static void drawFP(int value); static void drawSP(int value); static void drawStack(int point, int value); static void drawValue(int rect[], int value); static void drawStackPoint(int point); void initVmInspect(void) { #if VMINSPECT char *display_name = ":0"; display = XOpenDisplay(display_name); screen = DefaultScreen(display); window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, 2, BlackPixel(display, screen), WhitePixel(display, screen)); XSelectInput(display, window, ExposureMask | KeyPressMask); XStoreName(display, window, "vm"); XMapRaised(display, window); gc = XCreateGC(display, window, 0, NULL); XSetForeground(display, gc, BlackPixel(display, screen)); XNextEvent(display, &event); drawFrame(); while(1){ char buf[10]; XNextEvent(display, &event); switch(event.type){ case KeyPress: if(XLookupString(&event.xkey, buf, sizeof(buf), NULL, NULL)){ switch(buf[0]){ case 's': d_step_on = 1; return; case 'g': d_step_on = 0; return; case 'q': exit(0); } } } } #endif } static void drawFrame(void) { int i; for(i=0;i<3;i++) XDrawRectangle(display, window, gc, r_loc[i][0], r_loc[i][1], r_loc[i][2], r_loc[i][3]); for(i=0;i<8;i++) XDrawRectangle(display, window, gc, s_loc[i][0], s_loc[i][1], s_loc[i][2], s_loc[i][3]); XDrawString(display, window, gc, r_loc[0][0]-25, r_loc[0][1]+15, "pc", 2); XDrawString(display, window, gc, r_loc[1][0]-25, r_loc[1][1]+15, "fp", 2); XDrawString(display, window, gc, r_loc[2][0]-25, r_loc[2][1]+15, "sp", 2); XDrawString(display, window, gc, s_loc[0][0], s_loc[0][1]-5, "stack", 5); for(i=0;i<STACK_NUM;i++){ char buf[8]; int len; sprintf(buf, "%d", MEM_LENGTH - STACK_NUM + i); len = strlen(buf); XDrawString(display, window, gc, s_loc[i][0]-28, s_loc[i][1]+15, buf, len); } } static void drawValue(int rect[], int value) { char buf[8]; int len; XSetForeground(display, gc, WhitePixel(display, screen)); XFillRectangle(display, window, gc, rect[0]+1, rect[1]+1, rect[2]-2, rect[3]-2); XFlush(display); #if 0 sprintf(buf, "%d", value); #else sprintf(buf, "%08x", value); #endif len = strlen(buf); XSetForeground(display, gc, BlackPixel(display, screen)); XDrawString(display, window, gc, rect[0]+X_SOFFSET, rect[1]+Y_SOFFSET, buf, len); XFlush(display); } static void drawPC(int value) { drawValue(r_loc[0], value); } static void drawFP(int value) { drawValue(r_loc[1], value); } static void drawSP(int value) { drawValue(r_loc[2], value); } static void drawStack(int point, int value) { int i; i = point - (MEM_LENGTH - STACK_NUM); drawValue(s_loc[i], value); } static void drawStackPoint(int point) { static int prevSP = MEM_LENGTH; int i; int x, y; i = prevSP - (MEM_LENGTH - STACK_NUM); XSetForeground(display, gc, WhitePixel(display, screen)); XFillRectangle(display, window, gc, s_loc[i][0] + s_loc[i][2] + 10, s_loc[i][1] + 10, 5, 5); XFlush(display); if((point < (MEM_LENGTH - STACK_NUM)) || (point > MEM_LENGTH)){ return; } i = point - (MEM_LENGTH - STACK_NUM); XSetForeground(display, gc, BlackPixel(display, screen)); XFillRectangle(display, window, gc, s_loc[i][0] + s_loc[i][2] + 10, s_loc[i][1] + 10, 5, 5); XFlush(display); prevSP = point; } int getEvent(void) { #if VMINSPECT char buf[10]; int ret; int i; drawPC(pc); drawFP(fp); drawSP(sp); for(i=MEM_LENGTH - STACK_NUM;i<MEM_LENGTH;i++){ drawStack(i, swap(mem[i])); } drawStackPoint(sp); while(1){ if(d_step_on == 1){ XNextEvent(display, &event); } else { #if 0 usleep(d_step_interval); #endif XCheckTypedEvent(display, KeyPress, &event); } switch(event.type){ case KeyPress: if(XLookupString(&event.xkey, buf, sizeof(buf), NULL, NULL)){ switch(buf[0]){ case 'q': ret = EVT_QUIT; exit(0); case 's': d_step_on = 1; return; case 'g': d_step_on = 0; return; case 't': d_trace_on = (d_trace_on == 0) ? 1 : 0; break; } } } } #endif }
makefile
Xを利用するためImakefileとする。
Imakefile
# # $ xmkmf # OBJS=vm.o vminspect.o SYS_LIBRARIES=$(XLIB) ComplexProgramTarget(vm)
■仮想マシン用アセンブラ関連
このアセンブラを生成するとasmというアセンブラ実行ファイルが出来る。
後述のマシン語のプログラム(ex. test.m)をアセンブルするには
$ asm test.m
と打つ。すると、test.oという仮想マシン上の実行ファイルが出来る。
asm.l
/* * asm.l */ %{ #include "y.tab.h" #include "asmutil.h" char *StrScan(void); int SkipComment(void); %} %option noyywrap ws [ \t] symbol [-:\n] digit [0-9] letter [a-zA-Z_] id {letter}({letter}|{digit})* string \"([^\"\\\n]|\\.)*\" other . %% {ws}+ {symbol} { return yytext[0]; } "NOP" { return NOP; } "PUSH" { return PUSH; } "POP" { return POP; } "PUSHI" { return PUSHI; } "POPI" { return POPI; } "REMOVE" { return REMOVE; } "ADD" { return ADD; } "SUB" { return SUB; } "SFTR" { return SFTR; } "SFTL" { return SFTL; } "JUMP" { return JUMP; } "JLT" { return JLT; } "JEQ" { return JEQ; } "JGT" { return JGT; } "HALT" { return HALT; } "LOAD" { return LOAD; } "OUT" { return OUT; } "DUMP" { return DUMP; } "PRINT" { return PRINT; } "BRK" { return BRK; } "DW" { return DW; } "DA" { return DA; } "DS" { return DS; } {digit}+ { sscanf(yytext, "%d", &yylval.Int); return NUM; } {id} { yylval.Char = IDentry(yytext, yyleng); return ID; } {string} { yylval.Char = StrScan(); return STR; } ";" { if(SkipComment() == EOF) return EOF; } {other} { fprintf(stderr, "Illegal char '%c' ignoredat line %d\n", yytext[0], yylineno); } %% /* * string scan */ char *StrScan(void) { char *p, *q; if(yytext[yyleng-1] != '\"') yyerror("Unterminated string literal"); for(p = yytext+1, q = yytext; p < yytext+(yyleng-1); *(q++) = *(p++)){ if(*p == '\\'){ if(*(++p) == 'n') *p = '\n'; else if(*(++p) == 't') *p = '\t'; } } *q = '\0'; yyleng = q - yytext; return yytext; } /* * skip comment */ int SkipComment(void) { int c; while(1){ c = input(); if(c == '\n'){ unput(c); return 1; } if(c == EOF){ unput(c); return EOF; } } }
asm.y
/* * asm.y */ %{ #include <stdio.h> #include "asmutil.h" #define YYDEBUG 0 /* * source/destination file name */ extern char srcFile[]; extern char dstFile[]; void setData(int pc_address, int setdata); void setCode(int pc_address, char opcode, short oprand); void getCode(int pc_address, char *opcode, short *oprand); /* * Input & Output Stream */ extern FILE *yyin; extern FILE *yyout; #include "instructions.h" #define NUM_OBJ 10240 static unsigned long *obj; int pc; %} %union { int Int; char *Char; } %token NOP %token PUSH %token POP %token PUSHI %token POPI %token REMOVE %token ADD %token SUB %token SFTR %token SFTL %token JUMP %token JLT %token JEQ %token JGT %token HALT %token LOAD %token OUT %token DUMP %token PRINT %token BRK %token DW %token DA %token DS %token <Int> NUM %token <Char> ID STR %right NEG %type <Int> num %% program : codes ; codes : | codes label code '\n' ; code : | NOP { setCode(pc, CODE_NOP, 0); pc++; } | PUSH ID { registerReference(CODE_PUSH, $2); pc++; } | POP ID { registerReference(CODE_POP, $2); pc++; } | PUSHI { setCode(pc, CODE_PUSHI, 0); pc++; } | POPI { setCode(pc, CODE_POPI, 0); pc++; } | REMOVE { setCode(pc, CODE_REMOVE, 0); pc++; } | ADD { setCode(pc, CODE_ADD, 0); pc++; } | SUB { setCode(pc, CODE_SUB, 0); pc++; } | SFTR ID { registerReference(CODE_SFTR, $2); pc++; } | SFTL ID { registerReference(CODE_SFTL, $2); pc++; } | JUMP ID { registerReference(CODE_JUMP, $2); pc++; } | JLT ID { registerReference(CODE_JLT, $2); pc++; } | JEQ ID { registerReference(CODE_JEQ, $2); pc++; } | JGT ID { registerReference(CODE_JGT, $2); pc++; } | HALT { setCode(pc, CODE_HALT, 0); pc++; } | LOAD { setCode(pc, CODE_LOAD, 0); pc++; } | OUT { setCode(pc, CODE_OUT, 0); pc++; } | DUMP { setCode(pc, CODE_DUMP, 0); pc++; } | PRINT { setCode(pc, CODE_PRINT, 0); pc++; } | BRK { setCode(pc, CODE_BRK, 0); pc++; } | DW num { setData(pc, $2); pc++; } | DW ID { registerReference(0, $2); pc++; } | DA num { int i; for(i=0;i<$2;i++){ setData(pc, 0); pc++; } } | DS STR { char *p; for(p=$2; *p != '\0'; p++){ setData(pc, (int)(*p)); pc++; } setData(pc, 0); pc++; } ; num : NUM { $$ = $1; } | '-' NUM %prec NEG { $$ = -$2; } ; label : | ID ':' { registerSymbol($1); } ; %% /************************************************************ * Output File Utility ************************************************************/ /*----------------------------------------------------------*/ void setData(int pc_address, int setdata) { unsigned long data; ((char *)(&data))[0] = ((char *)(&setdata))[3]; ((char *)(&data))[1] = ((char *)(&setdata))[2]; ((char *)(&data))[2] = ((char *)(&setdata))[1]; ((char *)(&data))[3] = ((char *)(&setdata))[0]; obj[pc_address] = data; } /*----------------------------------------------------------*/ void setCode(int pc_address, char opcode, short oprand) { unsigned long data; ((char *)(&data))[0] = opcode; ((char *)(&data))[1] = (char)0; ((char *)(&data))[2] = ((char *)(&oprand))[1]; ((char *)(&data))[3] = ((char *)(&oprand))[0]; obj[pc_address] = data; } /*----------------------------------------------------------*/ void getCode(int pc_address, char *opcode, short *oprand) { unsigned long data; char code; short rand; data = obj[pc_address]; code = ((char *)(&data))[0]; ((char *)(&rand))[1] = ((char *)(&data))[2]; ((char *)(&rand))[0] = ((char *)(&data))[3]; *opcode = code; *oprand = rand; } /*----------------------------------------------------------*/ void initObj(void) { int i; obj = (unsigned long *)malloc(NUM_OBJ*4); for(i=0;i<NUM_OBJ;i++){ obj[i] = 0; } pc = 0; } /*----------------------------------------------------------*/ void endObj(void) { int i; char *a; a = (char *)obj; for(i=0;i<pc;i++){ putc(*a++, yyout); putc(*a++, yyout); putc(*a++, yyout); putc(*a++, yyout); } free(obj); } /************************************************************ * Basic functions ************************************************************/ static char srcFile[256]; static char dstFile[256]; /*----------------------------------------------------------*/ yyerror(char *s) { extern int yylineno; printf(" ERROR %s : ", srcFile); printf("%s at line %d\n", s, yylineno); } /*----------------------------------------------------------*/ main(int argc, char *argv[]) { char *s; int i; #if YYDEBUG yydebug = 1; #endif for(i=0;i<256;i++){ srcFile[i] = 0; dstFile[0] = 0; } /* parse command line arguments */ if(--argc > 0 && (char)(*++argv)[0] == '-'){ for(s = (char *)*argv+1; *s != '\0'; s++){ switch(tolower(*s)){ case 'c': break; case 'd': break; } } argc--; argv++; } if(argc > 0){ strcpy(srcFile, (char *)*argv); } /* open source file */ yyin = fopen(srcFile, "r"); if(yyin == NULL){ fprintf(stderr, "Source file cannot be opened."); exit(-1); } /* open destination file */ for(i=0;srcFile[i]!=0;i++){ if(srcFile[i] == '.') break; dstFile[i] = srcFile[i]; } strcpy(&dstFile[i], ".o"); yyout = fopen(dstFile, "wb"); if(yyout == NULL){ fprintf(stderr, "Destination file cannot be opened."); fclose(yyin); exit(-1); } initObj(); initSymList(); /* parse input file */ yyparse(); endObj(); /* close source file */ fclose(yyin); /* close output file */ fclose(yyout); printf("### %s ###\n", dstFile); }
仮想マシンのアセンブラ用のlex, yaccで生成されるコードから利用するユーティリティ関数のヘッダ
asmutil.h
/* * asmutil.h */ void initSymList(void); char *IDentry(char *Name, int Len); void registerSymbol(char *name); int registerOffset(char *name);
仮想マシンのアセンブラ用のlex, yaccで生成されるコードから利用するユーティリティ関数本体
asmutil.c
/* * asmutil.c */ #include <stdio.h> #include "asmutil.h" extern int pc; typedef struct SymElem { char *data; short symaddr; short reflist; } SymElem; #define NUM_SYMELEM 128 static SymElem symlist[NUM_SYMELEM]; static int freeindex; static int findSymbol(char *name); void initSymList(void) { int i; for(i=0;i<NUM_SYMELEM;i++){ symlist[i].data = NULL; symlist[i].symaddr = -1; symlist[i].reflist = -1; } freeindex = 0; } /* * local function * find the symbol in symlist * if the symbol is found, return the index. * otherwise return -1 */ static int findSymbol(char *name) { int i; for(i=0;i<freeindex;i++){ if(strcmp(symlist[i].data, name) == 0) return i; } return -1; } /* * register syumbol * search symbol table * if sama name is found, return registered pointer * if samae name is not found, register the name * this function is just finding name */ char *IDentry(char *Name, int Len) { int i; i = findSymbol(Name); if(i != -1) return symlist[i].data; if(freeindex >= NUM_SYMELEM){ yyerror("Symbol Table Overflow"); } i = freeindex; freeindex++; symlist[i].data = (char *)malloc((size_t)Len); strcpy(symlist[i].data, Name); return symlist[i].data; } /* * Register symbol * this function is called when label definition detected * search symbol table. the same name should be detected. * because lexical analyzer has already registered the symbol. * if offset is already defined, it's error * set PC value to offset * if reflist is already defined, fill offset */ void registerSymbol(char *name) { int i; char code; short off; int pc_address; i = findSymbol(name); if(symlist[i].symaddr != -1){ yyerror("Duplicate definition the Symbol"); return; } symlist[i].symaddr = pc; if(symlist[i].reflist == -1) return; pc_address = symlist[i].reflist; while(1){ getCode(pc_address, &code, &off); setCode(pc_address, code, pc); if(off == 0) break; pc_address = pc_address + off; } symlist[i].reflist = -1; } /* * find the same symbol from symlist * list symbol chain * if the symbol has already been registered label, resolve offset chain */ void registerReference(int code, char *name) { int i; i = findSymbol(name); if(symlist[i].symaddr != -1){ setCode(pc, code, symlist[i].symaddr); return; } if(symlist[i].reflist == -1){ setCode(pc, code, 0); symlist[i].reflist = pc; } else{ setCode(pc, code, symlist[i].reflist - pc); symlist[i].reflist = pc; } }
■仮想マシン上のプログラム
VM1がVM2を自身の上にロードし、実行すると
VM2がVM3を自身の上にロードし、実行すると
VM3がVM4を自身の上にロードし、実行すると
VM4がVM5を自身の上にロードし、実行すると
VM5がtestを自身の上にロードし、実行する。
これらのVMの違いはロードするファイルが違うだけ。
上手くやればVMを世代によらず共通化できたと思うが
ベタでファイル名の部分だけ変えている。
vm1.m
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vm1.m ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initialize PUSH zero POP i initloop: PUSH i PUSH memp ADD PUSH zero POPI PUSH i PUSH one ADD POP i PUSH i PUSH maxmem JLT initloop ; load file PUSH memp PUSH filep LOAD ; execute exeloop: ; get byte code ; opcode -> c ; oprand -> d PUSH pc PUSH memp ADD PUSHI SFTR c24 POP c PUSH pc PUSH memp ADD PUSHI SFTL c16 SFTR c16 POP d ; decode opcode PUSH c PUSH nop JEQ c_nop PUSH c PUSH push JEQ c_push PUSH c PUSH pop JEQ c_pop PUSH c PUSH pushi JEQ c_pushi PUSH c PUSH popi JEQ c_popi PUSH c PUSH remove JEQ c_remove PUSH c PUSH add JEQ c_add PUSH c PUSH sub JEQ c_sub PUSH c PUSH sftr JEQ c_sftr PUSH c PUSH sftl JEQ c_sftl PUSH c PUSH jump JEQ c_jump PUSH c PUSH jlt JEQ c_jlt PUSH c PUSH jeq JEQ c_jeq PUSH c PUSH jgt JEQ c_jgt PUSH c PUSH halt JEQ c_halt ; high level instructions PUSH c PUSH load JEQ c_load ; debugging instructions PUSH c PUSH out JEQ c_out PUSH c PUSH dump JEQ c_dump PUSH c PUSH print JEQ c_print PUSH c PUSH brk JEQ c_brk ; undefined instruction PUSH undefp PRINT PUSH c OUT REMOVE JUMP finish ; execute each code c_nop: JUMP done c_push: PUSH sp PUSH one SUB POP sp PUSH sp PUSH memp ADD PUSH fp PUSH d ADD PUSH memp ADD PUSHI POPI JUMP done c_pop: PUSH fp PUSH d ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH one ADD POP sp JUMP done c_pushi: PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSHI POPI JUMP done c_popi: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH two ADD POP sp JUMP done c_remove: PUSH sp PUSH one ADD POP sp JUMP done c_add: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI ADD POPI PUSH sp PUSH one ADD POP sp JUMP done c_sub: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI SUB POPI PUSH sp PUSH one ADD POP sp JUMP done c_sftr: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTR d POPI JUMP done c_sftl: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTL d POPI JUMP done c_jump: PUSH d POP pc JUMP exeloop c_jlt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jlt1 JUMP done jlt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jeq: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JEQ jeq1 JUMP done jeq1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jgt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jgt1 JUMP done jgt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_halt: BRK JUMP finish c_load: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH two ADD POP sp LOAD JUMP done c_out: PUSH sp PUSH memp ADD PUSHI OUT REMOVE JUMP done c_dump: DUMP JUMP done c_print: PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH one ADD POP sp PRINT JUMP done c_brk: BRK ; increment pc done: PUSH pc PUSH one ADD POP pc JUMP exeloop finish: HALT ; data area ; constant values zero: DW 0 one: DW 1 two: DW 2 c16: DW 16 c24: DW 24 nop: DW 0 push: DW 1 pop: DW 2 pushi: DW 3 popi: DW 4 remove: DW 5 add: DW 6 sub: DW 7 sftr: DW 8 sftl: DW 9 jump: DW 10 jlt: DW 11 jeq: DW 12 jgt: DW 13 halt: DW 14 load: DW 15 out: DW 16 dump: DW 17 print: DW 18 brk: DW 19 ; temporary variables c: DW 0 d: DW 0 i: DW 0 ; global values undefp: DW undef undef: DS "Undefined Instruction" filep: DW file file: DS "vm2.o" pc: DW 0 fp: DW 0 sp: DW 2500 maxmem: DW 2500 memp: DW mem memsrt: DS "MEMSTART" mem: DA 2500 memend: DS "MEMEND"
vm2.m
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vm2.m ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initialize PUSH zero POP i initloop: PUSH i PUSH memp ADD PUSH zero POPI PUSH i PUSH one ADD POP i PUSH i PUSH maxmem JLT initloop ; load file PUSH memp PUSH filep LOAD ; execute exeloop: ; get byte code ; opcode -> c ; oprand -> d PUSH pc PUSH memp ADD PUSHI SFTR c24 POP c PUSH pc PUSH memp ADD PUSHI SFTL c16 SFTR c16 POP d ; decode opcode PUSH c PUSH nop JEQ c_nop PUSH c PUSH push JEQ c_push PUSH c PUSH pop JEQ c_pop PUSH c PUSH pushi JEQ c_pushi PUSH c PUSH popi JEQ c_popi PUSH c PUSH remove JEQ c_remove PUSH c PUSH add JEQ c_add PUSH c PUSH sub JEQ c_sub PUSH c PUSH sftr JEQ c_sftr PUSH c PUSH sftl JEQ c_sftl PUSH c PUSH jump JEQ c_jump PUSH c PUSH jlt JEQ c_jlt PUSH c PUSH jeq JEQ c_jeq PUSH c PUSH jgt JEQ c_jgt PUSH c PUSH halt JEQ c_halt ; high level instructions PUSH c PUSH load JEQ c_load ; debugging instructions PUSH c PUSH out JEQ c_out PUSH c PUSH dump JEQ c_dump PUSH c PUSH print JEQ c_print PUSH c PUSH brk JEQ c_brk ; undefined instruction PUSH undefp PRINT PUSH c OUT REMOVE JUMP finish ; execute each code c_nop: JUMP done c_push: PUSH sp PUSH one SUB POP sp PUSH sp PUSH memp ADD PUSH fp PUSH d ADD PUSH memp ADD PUSHI POPI JUMP done c_pop: PUSH fp PUSH d ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH one ADD POP sp JUMP done c_pushi: PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSHI POPI JUMP done c_popi: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH two ADD POP sp JUMP done c_remove: PUSH sp PUSH one ADD POP sp JUMP done c_add: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI ADD POPI PUSH sp PUSH one ADD POP sp JUMP done c_sub: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI SUB POPI PUSH sp PUSH one ADD POP sp JUMP done c_sftr: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTR d POPI JUMP done c_sftl: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTL d POPI JUMP done c_jump: PUSH d POP pc JUMP exeloop c_jlt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jlt1 JUMP done jlt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jeq: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JEQ jeq1 JUMP done jeq1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jgt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jgt1 JUMP done jgt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_halt: BRK JUMP finish c_load: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH two ADD POP sp LOAD JUMP done c_out: PUSH sp PUSH memp ADD PUSHI OUT REMOVE JUMP done c_dump: DUMP JUMP done c_print: PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH one ADD POP sp PRINT JUMP done c_brk: BRK ; increment pc done: PUSH pc PUSH one ADD POP pc JUMP exeloop finish: HALT ; data area ; constant values zero: DW 0 one: DW 1 two: DW 2 c16: DW 16 c24: DW 24 nop: DW 0 push: DW 1 pop: DW 2 pushi: DW 3 popi: DW 4 remove: DW 5 add: DW 6 sub: DW 7 sftr: DW 8 sftl: DW 9 jump: DW 10 jlt: DW 11 jeq: DW 12 jgt: DW 13 halt: DW 14 load: DW 15 out: DW 16 dump: DW 17 print: DW 18 brk: DW 19 ; temporary variables c: DW 0 d: DW 0 i: DW 0 ; global values undefp: DW undef undef: DS "Undefined Instruction" filep: DW file file: DS "vm3.o" pc: DW 0 fp: DW 0 sp: DW 1900 maxmem: DW 1900 memp: DW mem memsrt: DS "MEMSTART" mem: DA 1900 memend: DS "MEMEND"
vm3.m
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vm3.m ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initialize PUSH zero POP i initloop: PUSH i PUSH memp ADD PUSH zero POPI PUSH i PUSH one ADD POP i PUSH i PUSH maxmem JLT initloop ; load file PUSH memp PUSH filep LOAD ; execute exeloop: ; get byte code ; opcode -> c ; oprand -> d PUSH pc PUSH memp ADD PUSHI SFTR c24 POP c PUSH pc PUSH memp ADD PUSHI SFTL c16 SFTR c16 POP d ; decode opcode PUSH c PUSH nop JEQ c_nop PUSH c PUSH push JEQ c_push PUSH c PUSH pop JEQ c_pop PUSH c PUSH pushi JEQ c_pushi PUSH c PUSH popi JEQ c_popi PUSH c PUSH remove JEQ c_remove PUSH c PUSH add JEQ c_add PUSH c PUSH sub JEQ c_sub PUSH c PUSH sftr JEQ c_sftr PUSH c PUSH sftl JEQ c_sftl PUSH c PUSH jump JEQ c_jump PUSH c PUSH jlt JEQ c_jlt PUSH c PUSH jeq JEQ c_jeq PUSH c PUSH jgt JEQ c_jgt PUSH c PUSH halt JEQ c_halt ; high level instructions PUSH c PUSH load JEQ c_load ; debugging instructions PUSH c PUSH out JEQ c_out PUSH c PUSH dump JEQ c_dump PUSH c PUSH print JEQ c_print PUSH c PUSH brk JEQ c_brk ; undefined instruction PUSH undefp PRINT PUSH c OUT REMOVE JUMP finish ; execute each code c_nop: JUMP done c_push: PUSH sp PUSH one SUB POP sp PUSH sp PUSH memp ADD PUSH fp PUSH d ADD PUSH memp ADD PUSHI POPI JUMP done c_pop: PUSH fp PUSH d ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH one ADD POP sp JUMP done c_pushi: PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSHI POPI JUMP done c_popi: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH two ADD POP sp JUMP done c_remove: PUSH sp PUSH one ADD POP sp JUMP done c_add: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI ADD POPI PUSH sp PUSH one ADD POP sp JUMP done c_sub: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI SUB POPI PUSH sp PUSH one ADD POP sp JUMP done c_sftr: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTR d POPI JUMP done c_sftl: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTL d POPI JUMP done c_jump: PUSH d POP pc JUMP exeloop c_jlt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jlt1 JUMP done jlt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jeq: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JEQ jeq1 JUMP done jeq1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jgt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jgt1 JUMP done jgt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_halt: BRK JUMP finish c_load: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH two ADD POP sp LOAD JUMP done c_out: PUSH sp PUSH memp ADD PUSHI OUT REMOVE JUMP done c_dump: DUMP JUMP done c_print: PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH one ADD POP sp PRINT JUMP done c_brk: BRK ; increment pc done: PUSH pc PUSH one ADD POP pc JUMP exeloop finish: HALT ; data area ; constant values zero: DW 0 one: DW 1 two: DW 2 c16: DW 16 c24: DW 24 nop: DW 0 push: DW 1 pop: DW 2 pushi: DW 3 popi: DW 4 remove: DW 5 add: DW 6 sub: DW 7 sftr: DW 8 sftl: DW 9 jump: DW 10 jlt: DW 11 jeq: DW 12 jgt: DW 13 halt: DW 14 load: DW 15 out: DW 16 dump: DW 17 print: DW 18 brk: DW 19 ; temporary variables c: DW 0 d: DW 0 i: DW 0 ; global values undefp: DW undef undef: DS "Undefined Instruction" filep: DW file file: DS "vm4.o" pc: DW 0 fp: DW 0 sp: DW 1300 maxmem: DW 1300 memp: DW mem memsrt: DS "MEMSTART" mem: DA 1300 memend: DS "MEMEND"
vm4.m
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vm4.m ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initialize PUSH zero POP i initloop: PUSH i PUSH memp ADD PUSH zero POPI PUSH i PUSH one ADD POP i PUSH i PUSH maxmem JLT initloop ; load file PUSH memp PUSH filep LOAD ; execute exeloop: ; get byte code ; opcode -> c ; oprand -> d PUSH pc PUSH memp ADD PUSHI SFTR c24 POP c PUSH pc PUSH memp ADD PUSHI SFTL c16 SFTR c16 POP d ; decode opcode PUSH c PUSH nop JEQ c_nop PUSH c PUSH push JEQ c_push PUSH c PUSH pop JEQ c_pop PUSH c PUSH pushi JEQ c_pushi PUSH c PUSH popi JEQ c_popi PUSH c PUSH remove JEQ c_remove PUSH c PUSH add JEQ c_add PUSH c PUSH sub JEQ c_sub PUSH c PUSH sftr JEQ c_sftr PUSH c PUSH sftl JEQ c_sftl PUSH c PUSH jump JEQ c_jump PUSH c PUSH jlt JEQ c_jlt PUSH c PUSH jeq JEQ c_jeq PUSH c PUSH jgt JEQ c_jgt PUSH c PUSH halt JEQ c_halt ; high level instructions PUSH c PUSH load JEQ c_load ; debugging instructions PUSH c PUSH out JEQ c_out PUSH c PUSH dump JEQ c_dump PUSH c PUSH print JEQ c_print PUSH c PUSH brk JEQ c_brk ; undefined instruction PUSH undefp PRINT PUSH c OUT REMOVE JUMP finish ; execute each code c_nop: JUMP done c_push: PUSH sp PUSH one SUB POP sp PUSH sp PUSH memp ADD PUSH fp PUSH d ADD PUSH memp ADD PUSHI POPI JUMP done c_pop: PUSH fp PUSH d ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH one ADD POP sp JUMP done c_pushi: PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSHI POPI JUMP done c_popi: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH two ADD POP sp JUMP done c_remove: PUSH sp PUSH one ADD POP sp JUMP done c_add: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI ADD POPI PUSH sp PUSH one ADD POP sp JUMP done c_sub: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI SUB POPI PUSH sp PUSH one ADD POP sp JUMP done c_sftr: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTR d POPI JUMP done c_sftl: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTL d POPI JUMP done c_jump: PUSH d POP pc JUMP exeloop c_jlt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jlt1 JUMP done jlt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jeq: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JEQ jeq1 JUMP done jeq1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jgt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jgt1 JUMP done jgt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_halt: BRK JUMP finish c_load: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH two ADD POP sp LOAD JUMP done c_out: PUSH sp PUSH memp ADD PUSHI OUT REMOVE JUMP done c_dump: DUMP JUMP done c_print: PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH one ADD POP sp PRINT JUMP done c_brk: BRK ; increment pc done: PUSH pc PUSH one ADD POP pc JUMP exeloop finish: HALT ; data area ; constant values zero: DW 0 one: DW 1 two: DW 2 c16: DW 16 c24: DW 24 nop: DW 0 push: DW 1 pop: DW 2 pushi: DW 3 popi: DW 4 remove: DW 5 add: DW 6 sub: DW 7 sftr: DW 8 sftl: DW 9 jump: DW 10 jlt: DW 11 jeq: DW 12 jgt: DW 13 halt: DW 14 load: DW 15 out: DW 16 dump: DW 17 print: DW 18 brk: DW 19 ; temporary variables c: DW 0 d: DW 0 i: DW 0 ; global values undefp: DW undef undef: DS "Undefined Instruction" filep: DW file file: DS "vm5.o" pc: DW 0 fp: DW 0 sp: DW 700 maxmem: DW 700 memp: DW mem memsrt: DS "MEMSTART" mem: DA 700 memend: DS "MEMEND"
vm5.m
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; vm5.m ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; initialize PUSH zero POP i initloop: PUSH i PUSH memp ADD PUSH zero POPI PUSH i PUSH one ADD POP i PUSH i PUSH maxmem JLT initloop ; load file PUSH memp PUSH filep LOAD ; execute exeloop: ; get byte code ; opcode -> c ; oprand -> d PUSH pc PUSH memp ADD PUSHI SFTR c24 POP c PUSH pc PUSH memp ADD PUSHI SFTL c16 SFTR c16 POP d ; decode opcode PUSH c PUSH nop JEQ c_nop PUSH c PUSH push JEQ c_push PUSH c PUSH pop JEQ c_pop PUSH c PUSH pushi JEQ c_pushi PUSH c PUSH popi JEQ c_popi PUSH c PUSH remove JEQ c_remove PUSH c PUSH add JEQ c_add PUSH c PUSH sub JEQ c_sub PUSH c PUSH sftr JEQ c_sftr PUSH c PUSH sftl JEQ c_sftl PUSH c PUSH jump JEQ c_jump PUSH c PUSH jlt JEQ c_jlt PUSH c PUSH jeq JEQ c_jeq PUSH c PUSH jgt JEQ c_jgt PUSH c PUSH halt JEQ c_halt ; high level instructions PUSH c PUSH load JEQ c_load ; debugging instructions PUSH c PUSH out JEQ c_out PUSH c PUSH dump JEQ c_dump PUSH c PUSH print JEQ c_print PUSH c PUSH brk JEQ c_brk ; undefined instruction PUSH undefp PRINT PUSH c OUT REMOVE JUMP finish ; execute each code c_nop: JUMP done c_push: PUSH sp PUSH one SUB POP sp PUSH sp PUSH memp ADD PUSH fp PUSH d ADD PUSH memp ADD PUSHI POPI JUMP done c_pop: PUSH fp PUSH d ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH one ADD POP sp JUMP done c_pushi: PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSHI POPI JUMP done c_popi: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI POPI PUSH sp PUSH two ADD POP sp JUMP done c_remove: PUSH sp PUSH one ADD POP sp JUMP done c_add: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI ADD POPI PUSH sp PUSH one ADD POP sp JUMP done c_sub: PUSH sp PUSH one ADD PUSH memp ADD PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI SUB POPI PUSH sp PUSH one ADD POP sp JUMP done c_sftr: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTR d POPI JUMP done c_sftl: PUSH fp PUSH d ADD PUSH memp ADD PUSHI POP d PUSH sp PUSH memp ADD PUSH sp PUSH memp ADD PUSHI SFTL d POPI JUMP done c_jump: PUSH d POP pc JUMP exeloop c_jlt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jlt1 JUMP done jlt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jeq: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JEQ jeq1 JUMP done jeq1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_jgt: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH sp PUSH memp ADD PUSHI PUSH sp PUSH two ADD POP sp JLT jgt1 JUMP done jgt1: PUSH fp PUSH d ADD POP pc JUMP exeloop c_halt: BRK JUMP finish c_load: PUSH sp PUSH one ADD PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH two ADD POP sp LOAD JUMP done c_out: PUSH sp PUSH memp ADD PUSHI OUT REMOVE JUMP done c_dump: DUMP JUMP done c_print: PUSH sp PUSH memp ADD PUSHI PUSH fp ADD PUSH memp ADD PUSH sp PUSH one ADD POP sp PRINT JUMP done c_brk: BRK ; increment pc done: PUSH pc PUSH one ADD POP pc JUMP exeloop finish: HALT ; data area ; constant values zero: DW 0 one: DW 1 two: DW 2 c16: DW 16 c24: DW 24 nop: DW 0 push: DW 1 pop: DW 2 pushi: DW 3 popi: DW 4 remove: DW 5 add: DW 6 sub: DW 7 sftr: DW 8 sftl: DW 9 jump: DW 10 jlt: DW 11 jeq: DW 12 jgt: DW 13 halt: DW 14 load: DW 15 out: DW 16 dump: DW 17 print: DW 18 brk: DW 19 ; temporary variables c: DW 0 d: DW 0 i: DW 0 ; global values undefp: DW undef undef: DS "Undefined Instruction" filep: DW file file: DS "test.o" pc: DW 0 fp: DW 0 sp: DW 100 maxmem: DW 100 memp: DW mem memsrt: DS "MEMSTART" mem: DA 100 memend: DS "MEMEND"
仮想マシン上のテストプログラム
test.m
;;;;;;;;;;;;;;;;;;;;;;;;; ; test.m ; ; SAMPLE PROGRAM ; ;;;;;;;;;;;;;;;;;;;;;;;;; PUSH index PUSH ap ADD PUSH data POPI PUSH index PUSH ap ADD PUSHI OUT REMOVE loop: PUSH count PUSH one ADD OUT POP count PUSH count PUSH max JLT loop PUSH strp PRINT HALT count: DW 0 one: DW 1 max: DW 10 strp: DW str str: DS "Hello World" ap: DW array array: DA 10 index: DW 3 data: DW 7