c++编写简易mips编译器

原创 2016年05月30日 22:23:18

由于在写计组实验时,需要首先将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编译器,过程中遇到不少的困难,但总体来说比较令我满意,之后希望能做更厉害的编译器。





./configure 常用参数说明

./configure 常用参数说明 ./configure 参数: Unix 下常用的 ‘./configure && make && make install’ 过...

mips下交叉编译iperf

mips下交叉编译iperf 因为交叉编译iperf需要用c++编译工具mips-linux-g++,参考下面这篇文章生成交叉编译c++工具mips-linux-g++,如果有相关工具和库文件,请忽略...
  • openswc
  • openswc
  • 2016年06月15日 09:49
  • 1205

一种ACM评判内核简易实现 使用C# WEB调用C++编译器的方法

作者:chenjieb520(两片森林)          相信很多做过ACM竞赛题目的人都知道:你提交了代码之后,系统就会对的代码进行编译,然后给出代码的编译的信息还有执行结果。很多人都不太明白...

c语言编写的简易文本编译器

  • 2011年10月05日 09:16
  • 6KB
  • 下载

mips tlb refill的编译器设计

mips的tlbrefill和tlb load exception处理函数,都是用自己的一套编译器来实现的。 这么做官方的解释是为了兼容,想想也是,只需要修改一下指令表就可以做到了。 编译器的核心...

一个模拟单周期cpu的mips编译器

  • 2014年01月22日 16:27
  • 13.45MB
  • 下载

MIPS在X86上的模拟器--Spim编译器

最近一年都在学习研究MIPS架构的东西,现在也在做Cavium的案子,所以把一些MIPS相关的东西整理一下。 在学习MIPS架构的时候,肯定要涉及到相关的指令集,在x86上BIOS都需要熟练掌握汇编...
  • pankul
  • pankul
  • 2013年03月15日 13:50
  • 4119

整理的一些warning(编译器是mips-linux-gcc)

这两天在公司就是忙活这些东西,记到blog上,免的以后忘了 warning: "/*" within comment 举例: /*************************...

mips-linux-gcc编译器警告消除

这两天在公司就是忙活这些东西,记到blog上,免的以后忘了 warning: "/*" within comment 举例: /*************************...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:c++编写简易mips编译器
举报原因:
原因补充:

(最多只允许输入30个字)