c++编写简易mips编译器
2016-05-30 22:23
1046 查看
由于在写计组实验时,需要首先将mips语句转换为二进制机器码,在没有编译器的辅助之下,只能一句一句对着码表,手动编译二进制机器码。一个不小心就会出错,出错了还很难找出错误的地方,有时还需要重新编译一遍。
因此,决定用c++实现一个简易的mips编译器,能编译基本的需要的mips语句为32位字长的二进制机器码。
首先,我们需要将mips指令编写在文本文件txt里面,如下图,采用的是我的另一篇博客(多周期cpu设计与实现)里的mips指令。
之后我们要将mips指令读入到c++文件中,并进行字符串的解析,从而进行译码。
在这里运用的是getline的方法将一行的mips指令以字符串的形式读入到buffer中。
接着利用strtok方法解析字符串,这里所运用的strtok方法安全性不高,网上资料建议是使用strtok_r,区别不大,读者可以搜索一下两个函数的具体用法。
利用strtok得到的应该是mips指令的第一个值,那么它应该是一个指令名称,例如“add”,“sub”, “jal”等等。
在这里我自定义一个函数int* fromooptobinary(string oop),它能根据不同的指令名称做相应的译码,返回值是一个整形数组,利用二维整形数组binary保存译码的结果也就是机器码。
这里的译码全部参考我的另一个博客(多周期cpu设计与实现):http://blog.csdn.net/zhongzi_l/article/details/51485113
int* fromooptobinary(string oop)的实现如下图:
由于oop指令名称有可能存在大小写混合的情况,因此使用string tolower(string oop)的自定义方法将大写字母转为小写字母
在这里网上资料推荐是使用stl的方法transition函数来转换大小写,但我感觉那个也挺麻烦的,而且只是将大写转为小写也挺容易实现:
接下来,由于有的指令例如“j 0x00000010”跳转指令,对于后面00000010十六进制地址需要转换为32位长度的二进制机器码,由此自定义了函数:
用到这个函数的地方在int* fromooptobinary(string oop)函数中:
接下来还有一个译码需要将寄存器号转换为五位的二进制机器码,或者也有的是十六位的二进制机器码,自定义函数:
用到这个函数的地方同样在int* fromooptobinary(string oop)函数中,例如:
到这里译码基本完成,接下来只需要将binary数组写到另外一个文本文件中即可:
其中ios::trunc意思是打开文件时首先将文件内容清空。
具体的c++文件读写可以参考http://blog.csdn.net/zhongzi_l/article/details/51541351
那么简易的mips编译器就这么愉快的完成了,当然其中也有很多不完善的地方,比如如果输入不是按照基本的输入格式,那么译码可能会出现不可预知的错误。
下面来看一下编译的结果:
结果与多周期cpu设计与实现中的值是一致的。编译成功。
接下来,我也利用这个自制简易mips编译器编译了一下我另外一个博客(单周期cpu设计和实现)里的mips指令,当然也做了一点修改,结果也是正确的。
本次做简易的mips编译器,过程中遇到不少的困难,但总体来说比较令我满意,之后希望能做更厉害的编译器。
因此,决定用c++实现一个简易的mips编译器,能编译基本的需要的mips语句为32位字长的二进制机器码。
首先,我们需要将mips指令编写在文本文件txt里面,如下图,采用的是我的另一篇博客(多周期cpu设计与实现)里的mips指令。
之后我们要将mips指令读入到c++文件中,并进行字符串的解析,从而进行译码。
int main() { int binary[32][32]; ifstream fin; fin.open("mips.txt", ios_base::in); if(!fin) { cerr << "Open Error!" << endl; exit(1); } int i, j; char buffer[256]; char* q = NULL; char* op; string oop; for(i = 0;i < 32;i++) { for(j = 0; j < 32; j++) { binary[i][j] = 0; } } j = 0; while(!fin.eof()) { fin.getline(buffer, 100); if(buffer[0] != 0) { q = NULL; op = strtok(buffer, ",$(),() "); oop = op; int* newline = fromooptobinary(oop); for(i = 0; i < 32; i++) { binary[j][i] = newline[i]; } } j++; } int count = j;
在这里运用的是getline的方法将一行的mips指令以字符串的形式读入到buffer中。
接着利用strtok方法解析字符串,这里所运用的strtok方法安全性不高,网上资料建议是使用strtok_r,区别不大,读者可以搜索一下两个函数的具体用法。
利用strtok得到的应该是mips指令的第一个值,那么它应该是一个指令名称,例如“add”,“sub”, “jal”等等。
在这里我自定义一个函数int* fromooptobinary(string oop),它能根据不同的指令名称做相应的译码,返回值是一个整形数组,利用二维整形数组binary保存译码的结果也就是机器码。
这里的译码全部参考我的另一个博客(多周期cpu设计与实现):http://blog.csdn.net/zhongzi_l/article/details/51485113
int* fromooptobinary(string oop)的实现如下图:
由于oop指令名称有可能存在大小写混合的情况,因此使用string tolower(string oop)的自定义方法将大写字母转为小写字母
在这里网上资料推荐是使用stl的方法transition函数来转换大小写,但我感觉那个也挺麻烦的,而且只是将大写转为小写也挺容易实现:
string tolower(string s) { int len = s.size(); for (int i=0; i<len; i++) { if ( s[i] >= 'A' && s[i] <= 'Z' ) { s[i] += ('a' - 'A' ) ; } } return s; }
接下来,由于有的指令例如“j 0x00000010”跳转指令,对于后面00000010十六进制地址需要转换为32位长度的二进制机器码,由此自定义了函数:
int* chartobinary(char *p) { int i = 0; int j = -1; int k = 0; int *q = new int[32]; for (i = 3; i < 10; i++) { int a = p[i] - '0'; j += 4; for(k = 0; k < 4; k++) { q[j] = a%2; a = a/2; j--; } j += 4; } return q; }
用到这个函数的地方在int* fromooptobinary(string oop)函数中:
if(oop == "j" || oop == "jal") { binary[0] = binary[1] = binary[2] = 1; if(oop == "jal") binary[4] = 1; p = strtok(NULL, ",$(),() "); int *k = chartobinary(p); for(i = 6; i < 32; i++) { binary[i] = k[i-6]; } }
接下来还有一个译码需要将寄存器号转换为五位的二进制机器码,或者也有的是十六位的二进制机器码,自定义函数:
int* inttobinary(int t, bool choose) { int i = 0; if(choose) { int* q = new int[5]; for(i = 4; i >= 0; i--) { q[i] = t%2; t=t/2; } return q; } else { bool check = false; if(t<0) { check = true; t = -t; } int* p = new int[16]; for(i = 15; i >= 0; i--) { p[i] = t%2; t=t/2; } if(check) { for(i = 15; i >= 0; i--) { if(p[i] == 0) p[i] = 1; else p[i] = 0; } int j = 15; while(p[j] != 0) { p[j] = 0; j--; } if(j>=0&&p[j]==0) { p[j] = 1; } } return p; } }
用到这个函数的地方同样在int* fromooptobinary(string oop)函数中,例如:
else if(oop == "move") { binary[0] = 1; p = strtok(NULL, ",$(),() "); int k = atoi(p); int* newline = inttobinary(k, true); p = strtok(NULL, ",$(),() "); k = atoi(p); int* newline2 = inttobinary(k, true); for(i = 6; i < 11; i++) { binary[i] = newline2[i-6]; binary[i+10] = newline[i-6]; } }
到这里译码基本完成,接下来只需要将binary数组写到另外一个文本文件中即可:
/*write to txt*/ ofstream out; out.open("data.txt", ios::out|ios::trunc); if(out.is_open()) { for(i = 0; i < count; i++) { for(j = 0; j < 32; j++) { out << binary[i][j]; if((j+1)%8 == 0 && j!=31) { out << " "; } } if(i != count - 1) { out << "\n"; } } cout << "compile success!" <<endl; out.close(); }
其中ios::trunc意思是打开文件时首先将文件内容清空。
具体的c++文件读写可以参考http://blog.csdn.net/zhongzi_l/article/details/51541351
那么简易的mips编译器就这么愉快的完成了,当然其中也有很多不完善的地方,比如如果输入不是按照基本的输入格式,那么译码可能会出现不可预知的错误。
下面来看一下编译的结果:
结果与多周期cpu设计与实现中的值是一致的。编译成功。
接下来,我也利用这个自制简易mips编译器编译了一下我另外一个博客(单周期cpu设计和实现)里的mips指令,当然也做了一点修改,结果也是正确的。
本次做简易的mips编译器,过程中遇到不少的困难,但总体来说比较令我满意,之后希望能做更厉害的编译器。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- 浅谈汇编器、编译器和解释器
- 让我们做个简单的解释器(三)
- 让我们做个简单的解释器(一)
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- 用 350 行代码从零开始,将 Lisp 编译成 JavaScript
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结