Tan's Blog.

304Challenge:虚拟机指令执行

字数统计: 923阅读时长: 4 min
2019/04/27 Share

虚拟机指令的执行

执行流程图

总结昨天的算法,我画了一个图以更直观的表示出执行过程。

整数算术运算指令

今天先来学习下基本的加减乘除指令是怎样执行的?

指令含义操作码
ADD $R1,$R2,$R3$R1=$R2+$R316
SUB $R1,$R2,$R3$R1=$R2-R318
MULT $R1,$R2,$R3$R1=$R2*$R320
DIV $R1,$R2,$R3,$R4$R1=$R3/$R4和$R2=$R3%$R422,24

下面的代码部分就是这些操作的执行过程。

1
2
3
4
5
6
7
8
9
ADD:
R[RAM[R[$IP]+1]]=R[RAM[R[$IP]+2]]+R[RAM[R[$IP]+3]];
R[$IP]=R[$IP]+4;
SUB:
R[RAM[R[$IP]+1]]=R[RAM[R[$IP]+2]]-R[RAM[R[$IP]+3]];
R[$IP]=R[$IP]+4;
MULT:
R[RAM[R[$IP]+1]]=R[RAM[R[$IP]+2]] * R[RAM[R[$IP]+3]];
R[$IP]=R[$IP]+4;

加减乘处理过程都比较简单,取出地址的值,进行运算就可。而除这个操作就比较复杂一些,因为在执行指令之前需要检查除数是否为零。如果为0,虚拟机将把”0xFFFFFFFFFFFFFFFFF”放到除法指令的第一个寄存器操作数里去。

1
2
3
4
5
6
7
8
9
10
DIV:
if(R[RAM[R[$IP]+4]]==0){
ERROR0_LVL2("Divide by zero!\n")
R[RAM[R[$IP]+1]]=R[RAM[R[$IP]+2]] =0xFFFFFFFFFFFFFFFFF"
}
else{
R[RAM[R[$IP]+1]]=R[RAM[R[$IP]+3]]/R[RAM[R[$IP]+4]];
R[RAM[R[$IP]+2]]=R[RAM[R[$IP]+3]]%R[RAM[R[$IP]+4]];
}
R[$IP]=R[$IP]+5;

通过学习上面通用的算法,回到sub_E90函数中,突然惊奇的发现switch分支的最前面一部分就是整数算术运算指令的处理过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
case 128:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) + *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_25;
case 129:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) += __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 130:
v12 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) - *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_57;
case 131:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) -= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 132:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) * *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_25;
case 133:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) *= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 134:
v2 = 0;
v12 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) / *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_57;
case 135:
v2 = 0;
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) /= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 136:
v2 = 0;
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8)
% *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
return v2;
case 137:
v2 = 0;
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) %= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 138:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) & *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_25;
case 139:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) &= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 140:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) | *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_25;
case 141:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) |= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;
case 142:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) ^ *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
case 143:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) ^= __ROL2__(*((_WORD *)v7 + 1), 8);
return v2;

上面的代码部分涵盖了加、减、乘、除(整除、取余)、与、或、异或操作,其中每两个为一组,区别在于:前一个为三操作数的指令,后一个为两操作数的指令。拿三操作数加法指令为例分析。

1
2
3
4
5
6
case 128:
v11 = *((_WORD *)v1 + (unsigned __int8)v7[2] + 8) + *((_WORD *)v1 + (unsigned __int8)v7[3] + 8);
goto LABEL_25;
LABEL_25:
*((_WORD *)v1 + (unsigned __int8)v7[1] + 8) = v11;
return v2;

这里采用无符号整型数,将寄存器2和寄存器3里的值相加的结果传入v11,跳出到LABEL_25。在LABEL_25处,再将v11传入寄存器1中进行返回,采用了一个间接赋值操作机制。

总结

myvm中只涉及到整数的算术运算指令,如果遇到浮点型的数,可能就会出错,在这里需要完善一下,加一下格式转换和对小数点的处理等情况。明天将继续分析switch分支的情况。

CATALOG
  1. 1. 虚拟机指令的执行
    1. 1.1. 执行流程图
    2. 1.2. 整数算术运算指令
    3. 1.3. 总结