JIT動態編譯器的原理與實現之Interpreter(解釋器)的實現(三)

     接下來,就是要實現一個虛擬機了。記得編碼高質量的代碼中有一條:不要過早地優化你的代碼。因此,也本着按部就班的原則,我將從實現一個解釋器開始,逐步過渡到JIT動態編譯器,這樣的演化能夠使原理看起來更清晰。函數

     解釋器的原理很簡單,就是一條指令一條指令的解釋並執行。具體流程分爲:取出指令-解碼指令-執行-返回主流程。這樣造成一個無限循環,以下圖所示:oop

     這裏的主流程就是上篇定義的程序rom.bin。但rom.bin不能直接運行,須要一個解釋器來包裹它,來解釋執行。解釋器放在一個無限循環中,使得主流程無限運行不中止:優化

void loop()
{
    for(;;)
    {
	Interpreter(&CPUREG);			
    }
}

      這樣,整個虛擬機的運行能夠定義爲:編碼

    memInit();         //初始化內存
    ResetCPU(&CPUREG); //初始化CPU
    loadROM();         //加載rom.bin
    loop();            //執行主流程
    memFree();         //釋放內存

      接下來須要作的就是取出指令送入解釋器了。爲此須要定義讀寫內存的函數memGet和memSet:指針

void memSet(unsigned int, unsigned char);

unsigned char memGet(unsigned int);

void memSet(unsigned int addr, unsigned char data)
{
	char Str_Err[256];

	if(addr>64)
	{
	  sprintf(Str_Err, "MEM: invalid mem write: 0x%8x", addr);
	  MessageBox(NULL, Str_Err, "Warning", MB_OK);
	}
	else
	{
	  RAM[addr & 0xff]=data;
	}

}

unsigned char memGet(unsigned int addr)
{
	char Str_Err[256];
	unsigned char val = 0;

	if(addr>64)
	{
	  sprintf(Str_Err, "MEM: invalid mem read: 0x%8x", addr);
	  MessageBox(NULL, Str_Err, "Warning", MB_OK);
	}
	else
	{
		val=RAM[addr & 0xff];
	}

	return val;
}

      讀寫均爲一個字節。因爲上篇定義的CPU尋址範圍只有64字節大小,因此超過64字節就要給出錯誤提示。code

      而後須要爲每個CPU指令機器碼實現一個解碼執行函數:blog

void nop(REG*);
void mov(REG*);
void add(REG*);
void cmp(REG*);
void jmp(REG*);
void jcp(REG*);

void nop(REG* cpuREG)
{

	cpuREG->R_PC++;

	sprintf("NOP\n");

}

void mov(REG* cpuREG)
{

	memSet(cpuREG->R_PC+1, memGet(cpuREG->R_PC+2));

	sprintf("MOV [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2);

	cpuREG->R_PC+=3;
}

void add(REG* cpuREG)
{

	memSet(cpuREG->R_PC+1, memGet(cpuREG->R_PC+1)+memGet(cpuREG->R_PC+2));

	sprintf("ADD [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2);

	cpuREG->R_PC+=3;
}

void cmp(REG* cpuREG)
{

	if((memGet(cpuREG->R_PC+1)-memGet(cpuREG->R_PC+2)) < 0)
	{
		cpuREG->R_CMP=0;
	}
	else
	{
		cpuREG->R_CMP=1;
	}

	sprintf("CMP [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2);
	cpuREG->R_PC+=3;
}

void jmp(REG* cpuREG)
{

	sprintf("JMP [0x%4x] \n", cpuREG->R_PC+1);

	cpuREG->R_PC=memGet(cpuREG->R_PC+1);
}

void jcp(REG* cpuREG)
{

	sprintf("JCP [0x%4x], [0x%4x]\n", cpuREG->R_PC+1, cpuREG->R_PC+2);

	if(cpuREG->R_CMP==0)
	{
		cpuREG->R_PC=memGet(cpuREG->R_PC+1);
	}
	else
	{
		cpuREG->R_PC=memGet(cpuREG->R_PC+2);
	}
	
}

     這裏最重要的是要當心處理PC寄存器。一開始CPU初始化的時候,PC寄存器是設爲0的,而自定義的rom.bin也是從0地址開始執行的。若是你虛擬的CPU不是從0地址開始執行,那麼在CPU初始化的時候就要把PC寄存器設爲相應的開始地址。另外每一條指令可能涉及的地址數不相同,那麼PC寄存器的變更也要不一樣。最後,跳轉指令也可能要根據比較寄存器的內容來改變PC寄存器。內存

     作了如上的準備以後就能夠實現解釋器了。這裏用switch-case結構來決定哪條指令被執行。爲了簡單起見,用了一個函數指針來執行解碼函數:編譯器

void (*func)(REG*);

//Interpreter 
void Interpreter(REG* cpuREG)
{
	char Str_Err[256];

	switch(memGet(cpuREG->R_PC))
	{
	case 0:
		func=nop;
		break;
	case 1:
		func=mov;
		break;
	case 2:
		func=add;
		break;
	case 3:
		func=cmp;
		break;
	case 4:
		func=jmp;
		break;
	case 5:
		func=jcp;
		break;
	default:
		sprintf(Str_Err, "Unhandled Opcode (0x%4x) at [0x%4x]", memGet(cpuREG->R_PC), cpuREG->R_PC);
		MessageBox(NULL, Str_Err, "Warning", MB_OK);
		return;

	}

	func(cpuREG);

}

      首先從內存中取出數據,根據機器碼來決定執行解碼函數,最後執行。執行結果以下:虛擬機

相關文章
相關標籤/搜索