就是你以为这玩意很厉害,真正做起来才会发现:就这就这就这。
基于高级语言实现虚拟机
计算机原理 公选课期末作业
一。实验目的
通过高级语言以软件模拟仿真方式,实现冯诺依曼计算机系统,模拟计算机系统整机工作原理,直观展现硬件运行过程,将所学的软件基础知识与硬件基础是知识进行综合,锻炼同学的系统综合能力,加深同学对计算机组成原理的理解。
二。实验内容
使用高级语言模拟计算机的存储器、寄存器等部件,并设计实现一个特定的指令集,并设计合适的测试样例(如 A+B 问题、打印乘法表)对该虚拟机进行检测。
三。实验环境
选用 C++ 实现,基于 Windows 的 x86 系统。
四。实验核心
(1)存储器:
该虚拟机利用长度为 256 的整型数组 mem 模拟存储器,并利用数组下标模拟内存地址;存储器不仅存储了用户的指令序列,还负责存储数组。
(2)寄存器:
该虚拟机利用长度为 8 的 x 数组模拟寄存器,其中 寄存器对应了数组中的 , 寄存器对应了数组中的 ,以此类推。 此外,设置 pc 为指令序列的程序计数器。
(3)条件码
作者设置了条件码 ZF 用来判断每次操作后所得的结果是否为 0,以及 SF 判断操作结果是否为负,条件码用一个整型变量来模拟。
(4)指令集与指令表示:
作者设计了一系列指令,从而完成复杂的目标函数:
指令名称 | 功能 | 表示码 |
---|---|---|
ADD x1 x2 | 将 x2 加上 x1 存储在 x2 中 | 0001 |
SUB x1 x2 | 将 x2 减去 x1 存储在 x2 中 | 0010 |
MOV x1 x2 | 将 x1 的值移动到 x2 | 0011 |
MUL x1 x2 | 将 x2 乘上 x1 存储在 x2 中 | 0100 |
JMP x1 | 无条件跳转到地址 x1 | 0101 |
JGT x1 | 当 SF 为 1 时跳转到 x1 | 0110 |
JEQ x1 | 但 ZF 为 1 时跳转到 x1 | 1100 |
CMP x1 x2 | 将 x2 减去 x1,并设置条件码 | 0111 |
INP x2 | 读取某一个数值并存入 x2 | 1000 |
OTR x1 | 输出一个寄存器 x1 所存储的值 | 1001 |
OTC c | 输出 ASCII 码为 c 的字符 | 1010 |
RET | 程序结束标志 | 1011 |
在实现时,作者利用一个字符串表示各指令。一个指令至多两个空格来区分指令名称与操作数。
每个操作数通过不同的前缀进行区分:$
代表立即数,%
代表寄存器,B(...)
表示地址以及偏移量 B
。
(5)程序框架
(6) 流程图
笔者以“打印乘法表”为例,运行以下指令代码:
其中循环部分参考了 gcc 编译器中 jump 2 mid
的翻译方法。
MOV $1 %1
JMP $5
OTC $10
OTC $13
ADD $1 %1
CMP $10 %1
JEQ $20
MOV $1 %2
CMP %2 %1
JGT $2
MOV %1 %3
MUL %2 %3
OTR %1
OTC $42
OTR %2
OTC $61
OTR %3
OTC $32
ADD $1 %2
JMP $8
RET
流程以打印第一行为例:
寄存器 %1 | 寄存器 %2 | PC对应指令 |
---|---|---|
0 | 0 | ADD $1 %1 (第一个元素加一) |
1 | 0 | JMP $5 (跳转至内循环) |
1 | 0 | ADD $1 %2 (第二个元素加一) |
1 | 1 | CMP %2 %1 (判断内循环是否结束) |
1 | 1 | MOV %1 %3 (暂存 %1 至 %3) |
1 | 1 | MUL %2 %3 (将答案赋值到 %3) |
1 | 1 | OTR %1 …(打印答案) |
1 | 1 | JMP $8 (继续进行内循环) |
最终结果如下:
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
五。实验总结
(1)收获:
通过这次实验,作者对于计算机软件原理,编译原理等内容有了更深刻的理解;亲自动手实现虚拟机也令作者感到十分震撼,令作者更清楚地认识到汇编语言与指令集架构之于硬件与软件的联系。
(2)不足:
作者这次所实现的虚拟机仍有以下不足:
- 指令数量较少,不能实现更为复杂的功能;
- 没有错误检测模块,汇编代码安全性不足;
- 没有实现 GUI 模块,虚拟机仅能通过命令行实现;
爱谁整谁整去(
六。附录:虚拟机源码
#include <bits/stdc++.h>
#define cle(x) memset(x,0,sizeof(x))
#define ok cout<<'!'<<endl
#define inf 2147483647
#define ll long long
using namespace std;
struct inst{
int fir;
int p1, op1; // 操作属性与操作数:0-立即数, 1-寄存器下标, 2-地址下标;
int p2, op2; // 同上。
}mem[256], instr;
int x[9], pc, ZF, SF;
string sig, o1, o2;
int change(string s)
{
int l = s.size(), num = 0;
for (int i = 0; i<l; i++) num = num*10 + s[i] - '0';
return num;
}
void setting(int x, string a, string b) //设置操作数属性
{
if (a[0] == '$') mem[x].p1 = 0, mem[x].op1 = change( a.substr(1,a.size()-1) );
else if (a[0] == '%') mem[x].p1 = 1, mem[x].op1 = change( a.substr(1,a.size()-1) );
else if (a[0] == '(') mem[x].p1 = 2, mem[x].op1 = change( a.substr(1,a.size()-2) );
if (b[0] == '$') mem[x].p2 = 0, mem[x].op2 = change(b.substr(1, b.size() - 1));
else if (b[0] == '%') mem[x].p2 = 1, mem[x].op2 = change(b.substr(1, b.size() - 1));
else if (b[0] == '(') mem[x].p2 = 2, mem[x].op2 = change(b.substr(1, b.size() - 2));
}
void read()
{ int t = 0;
ifstream cin("list.txt"); // list 为指令序列文件
while ( !cin.eof() ) {
cin >> sig;
if (sig == "ADD") {
mem[t].fir = 0b0001;
cin >> o1 >> o2;
setting(t, o1, o2);
}
if (sig == "SUB") {
mem[t].fir = 0b0010;
cin >> o1 >> o2;
setting(t, o1, o2);
}
if (sig == "MOV") {
mem[t].fir = 0b0011;
cin >> o1 >> o2;
setting(t, o1, o2);
}
if (sig == "MUL") {
mem[t].fir = 0b0100;
cin >> o1 >> o2;
setting(t, o1, o2);
}
if (sig == "JMP") {
mem[t].fir = 0b0101;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "JGT") {
mem[t].fir = 0b0110;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "JEQ") {
mem[t].fir = 0b1100;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "CMP") {
mem[t].fir = 0b0111;
cin >> o1 >> o2;
setting(t, o1, o2);
}
if (sig == "INP") {
mem[t].fir = 0b1000;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "OTR") {
mem[t].fir = 0b1001;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "OTC") {
mem[t].fir = 0b1010;
cin >> o1;
setting(t, o1, o2);
}
if (sig == "RET") {
mem[t].fir = 0b1011;
}
t++, o2 = ".";
}
}
void operate(inst t)
{
int n1 = t.p1 == 1 ? x[t.op1] : t.op1 ; //读取操作数
switch (t.fir) //对每个指令转换为某种机器码,存储在 mem 中。
{
case 1:
x[t.op2] += n1;
break;
case 2:
x[t.op2] -= n1;
ZF = (x[t.op2] == 0);
break;
case 3:
x[t.op2] = n1;
break;
case 4:
x[t.op2] *= n1;
break;
case 5:
pc = n1;
break;
case 6:
if (SF) pc = n1;
break;
case 12:
if (ZF) pc = n1;
break;
case 7:
ZF = ( (n1 - x[t.op2]) == 0);
SF = ( n1 > x[t.op2] );
break;
case 8:
int a; cin >> a;
x[t.op1] = a;
break;
case 9:
cout << n1;
break;
case 10:
cout << (char)(n1);
break;
}
}
int main()
{
read(); //读取指令序列
pc = 0; //初始化程序计数器;
while ( 1 ) {
// cout << "%1 = " << x[1] << ", %2 = " << x[2] << " " << pc << endl;
instr = mem[pc], pc ++; //抓取当前指令,计数器加一
operate( instr ); // 执行指令内容
if (instr.fir == 11) break; // 结束程序
}
return 0;
}