您的位置:首页 > 编程语言 > C语言/C++

c++编写简易mips编译器

2016-05-30 22:23 1046 查看
由于在写计组实验时,需要首先将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++ mips 编译器