?? cgen.cpp
字號:
/******************************************************************
** 文件名: cgen.cpp
** 描 述: 這是一個實際的8086/8088匯編代碼生成器。將c-語言生成最終
** 目標代碼,由于匯編代碼本身的一些功能限制,及實現的復雜性,
** 在最終定型時,功能上作了一些取舍,如下:
** c-語言是支持float型數據的,也可以生成最終的代碼,但
** 帶有float型數據的代碼將不可運行。
** char型與int型數據統一使用16位寄存器,
** 為避免棧操作的復雜性,所有函數的局部變量將成為靜態
** 變量,這將導致遞歸調用雖能進行,但得不到正確的結果。
** 該語言,在條件與循環控制上做的比較完備,if{}else{}
** while{},for(){},break,continue均作過嚴格的測試,使語言
** 具有較強的過程控制能力。
******************************************************************/
#include "globals.h"
#include "scan.h"
#include "prase.h"
#include "symtab.h"
#include "analyze.h"
#include "cgen.h"
#include "ExternGla"
/*****************************************************************
**靜態成員變量的聲明及初始化
*****************************************************************/
Canalyzer* Cgenerator::m_panalyzer=NULL; //一個成員語義分析器。
int Cgenerator::m_iunique=0; //用于輔助生成唯一的符號地址。
/*****************************************************************
** 代碼生成器的構造函數,先構造一個語義分析器,語義分析器將先
** 調用文法分析器,再完成語義分析功能。
*****************************************************************/
Cgenerator::Cgenerator(){
try{
m_panalyzer= new Canalyzer;
cout<<"generating code..."<<endl;
codeGen(); //開始生成目標代碼。
}
catch (bad_alloc){ //處理堆分配異常。
cout<<"Can't be allocated in heap, the programme must be terminated."<<endl;
exit(1);}
catch (...){
cout<<"Something error,the program was terminated.";
exit(1);}
}
Cgenerator::~Cgenerator(){
delete m_panalyzer;
}
/***************************************************************
**代碼生成分三步進行:生成數據段,生成堆棧段,然后生成代碼段。
***************************************************************/
void Cgenerator::codeGen(void){
Data_seg();
Stack_seg();
Code_seg();
}
/****************************************************************
**生成數據段,首先將所有全局變量加上后綴"@Glo"后放入數據段,然后
**將各函數的參數,局部變量全部加上各自的作用域作為后綴放入數據段
**這樣,致使函數的成局部變量全成了靜態的,這是因為時間不足,而在
** 設計上采取的一個折衷辦法。
**遞歸調用將不能產生正確的結果。
*****************************************************************/
void Cgenerator::Data_seg(void){
CTreeNode *p=NULL,*t=NULL;
AllGlobals.code.write("Data_seg segment\n",18);
AllGlobals.code.write("\n@@Show@@\tdw\t?",14);//這是顯示例程用到的數據,每個程序都有。
p=m_panalyzer->m_syntaxTree;
do{
if(p->m_Ennodekind==DeclK)
generate_data("@Glo",p); //生成全局變量。
else{
t=p->m_pchild[0];
if(t){ //函數參數表生成。
do{
generate_data(t->m_pfather->m_strIDname, t);
t=t->m_pbrother;
}while(t!=NULL);
}
t=p->m_pchild[1];
if(t){ //函數局部變量生成。
do{
if(t->m_Ennodekind==DeclK)
generate_data(t->m_pfather->m_strIDname, t);
t=t->m_pbrother;
}while(t!=NULL);
}
}
p=p->m_pbrother;
}while(p!=NULL);
AllGlobals.code.write("\nData_seg ends\n\n",17);//整個數據段生成結束。
}
/**************************************************************************
** 向數據段寫入一個變量。
**************************************************************************/
void Cgenerator::generate_data(const char *pa_suffix,CTreeNode* pa_Declare){
strcat(pa_Declare->m_strIDname, pa_suffix); //為變量加上作用域后綴。
AllGlobals.code.write("\n",1);
AllGlobals.code.write(pa_Declare->m_strIDname,strlen(pa_Declare->m_strIDname));
AllGlobals.code.write("\t",1);
AllGlobals.code.write("DW\t?\n",5);//char與int統一占用16位寄存器。
return;
}
/**********************************************************************
** 生成堆棧段。(這是一段模板,每個程序都一樣)
*********************************************************************/
void Cgenerator::Stack_seg(void){
AllGlobals.code.write("Stack_seg segment\n",19);
AllGlobals.code.write("dw\t100 dup(?)\n",14);
AllGlobals.code.write("@tos\tlabel\tword\n",16);
AllGlobals.code.write("Stack_seg ends\n\n",17);
}
/********************************************************************
**生成代碼段,代碼段起始將有一段例行操作,完成后,將main函數寫在開始處
**接下來依次寫上各個函數,所有該編譯器所編譯的程序的所有代碼將在一個段
**中,程序大小受到嚴格限制。
*********************************************************************/
void Cgenerator::Code_seg(void){
CTreeNode *p=NULL;
AllGlobals.code.write("Code_seg segment\n",18);
AllGlobals.code.write("main\tproc\tfar\n",14);
AllGlobals.code.write("\tassume cs:Code_seg,ds:Data_seg,ss:Stack_seg\nstart:\n",52);
AllGlobals.code.write("mov\tax,Stack_seg\nmov\tss,ax\nmov\tsp,offset @tos\n",46);
AllGlobals.code.write("push\tds\nsub\tax,ax\npush\tax\n",26);
AllGlobals.code.write("mov\tax,Data_seg\nmov\tds,ax\n",26);
p=m_panalyzer->m_syntaxTree;
do{
if(p->m_Ennodekind==FuncK && strcmp(p->m_strIDname,"main")==0){
generate_func(p);break;} //先生成main函數。
p=p->m_pbrother;
}while(p!=NULL);
generate_show_func(); //再插入兩個用于顯示的函數。供write調用。
p=m_panalyzer->m_syntaxTree;
do{
if(p->m_Ennodekind==FuncK && strcmp(p->m_strIDname,"main")!=0){
AllGlobals.code.write("\n",1); //生成其他的函數。
AllGlobals.code.write(p->m_strIDname,strlen(p->m_strIDname));
AllGlobals.code.write("\tproc\tnear\n",11);
AllGlobals.code.write("\npop\tbp",7); // bp主要用于處理ip寄存器。
generate_func(p);}
p=p->m_pbrother;
}while(p!=NULL);
AllGlobals.code.write("\nCode_seg ends\n",16);
AllGlobals.code.write("\tend start",10);
}
/***************************************************************
**顯示例程,代碼生成器將為每個程序自動插入這段程序,供writed調用
**用于顯示數值。
****************************************************************/
void Cgenerator::generate_show_func(void){
AllGlobals.code.write("Show@@Show\tproc\tnear",20);
AllGlobals.code.write("\npop\tbp",7);
AllGlobals.code.write("\npop\tbx",7);
AllGlobals.code.write("\npush\tbp",8);
AllGlobals.code.write("\nmov\t@@Show@@,bx",16);
AllGlobals.code.write("\nmov\tcx,10000d",14);
AllGlobals.code.write("\ncmp\t@@Show@@,cx",16);
AllGlobals.code.write("\njl\t@@1000",10);
AllGlobals.code.write("\ncall\tShow@@22",14);
AllGlobals.code.write("\n@@1000:",8);
AllGlobals.code.write("\nmov\tcx,1000d",13);
AllGlobals.code.write("\ncmp\t@@Show@@,cx",16);
AllGlobals.code.write("\njl\t@@100",9);
AllGlobals.code.write("\ncall\tShow@@22",14);
AllGlobals.code.write("\n@@100:",7);
AllGlobals.code.write("\nmov\tcx,100d",12);
AllGlobals.code.write("\ncmp\t@@Show@@,cx",16);
AllGlobals.code.write("\njl\t@@10",8);
AllGlobals.code.write("\ncall\tShow@@22",14);
AllGlobals.code.write("\n@@10:",6);
AllGlobals.code.write("\nmov\tcx,10d",11);
AllGlobals.code.write("\ncmp\t@@Show@@,cx",16);
AllGlobals.code.write("\njl\t@@1",7);
AllGlobals.code.write("\ncall\tShow@@22",14);
AllGlobals.code.write("\n@@1:",5);
AllGlobals.code.write("\nmov\tcx,1d",10);
AllGlobals.code.write("\ncall\tShow@@22",14);
AllGlobals.code.write("\nret",4);
AllGlobals.code.write("\nShow@@Show\tendp",16);
AllGlobals.code.write("\nShow@@22\tproc\tnear",19);
AllGlobals.code.write("\nmov\tax,bx",10);
AllGlobals.code.write("\nmov\tdx,0",9);
AllGlobals.code.write("\ndiv\tcx",7);
AllGlobals.code.write("\nmov\tbx,dx",10);
AllGlobals.code.write("\nmov\tdl,al",10);
AllGlobals.code.write("\nadd\tdl,30h",11);
AllGlobals.code.write("\nmov\tah,2h",10);
AllGlobals.code.write("\nint\t21h",8);
AllGlobals.code.write("\nret",4);
AllGlobals.code.write("\nShow@@22\tendp",14);
}
/*********************************************************
**每個函數代碼的實際生成,首先將參數出棧保存,處理完ip,
**進入函數具體語句體的處理。
*********************************************************/
void Cgenerator::generate_func(CTreeNode* pa_Func){
CTreeNode *p=pa_Func->m_pchild[0];
if(strcmp(pa_Func->m_strIDname,"main")!=0){
generate_params(p); //參數出棧保存。
AllGlobals.code.write("\npush\tbp",8);} //處理ip.ip應始終位于棧的最頂層。
p=pa_Func->m_pchild[1];
if(p){
do{
generate_stmt(p); //生成具體的語句。
p=p->m_pbrother;
}while(p!=NULL);
}
if(strcmp(pa_Func->m_strIDname,"main")!=0){
AllGlobals.code.write("\npush\tbp",9);}
AllGlobals.code.write("\nret\n",5);
AllGlobals.code.write(pa_Func->m_strIDname,strlen(pa_Func->m_strIDname));
AllGlobals.code.write("\tendp\n\n",7);
}
/*****************************************************************
** 函數參數表的處理,每個函數應在起始處,將所有的參數出棧保存
******************************************************************/
void Cgenerator::generate_params(CTreeNode* pa_param){
CTreeNode *p=pa_param;
if(p){
do{
AllGlobals.code.write("\npop\tax\nmov\t",12);
AllGlobals.code.write(p->m_strIDname,strlen(p->m_strIDname));
AllGlobals.code.write(",ax\n",4);
p=p->m_pbrother;
}while(p!=NULL);
}
return;
}
/******************************************************************
**函數體內具體語句的處理,語句包括表達式和一般語句兩種。
******************************************************************/
void Cgenerator::generate_stmt(CTreeNode* pa_commpound){
switch(pa_commpound->m_Ennodekind){
case ExpK:
generator_exp(pa_commpound);
break;
case StmtK:
generator_substmt(pa_commpound);
break;
default:
break;
}
}
/**********************************************************************
** 表達式的生成,主要借用棧,采用遞歸方式進行。
***********************************************************************/
void Cgenerator::generator_exp(CTreeNode* pa_exp){
switch(pa_exp->kind.m_EnExpKind){
case ConstK:// 表達式指令中,遇到變量或常數,則壓棧。(變量名需擴展處理).
case IdK:
if(pa_exp->m_EnTypevalue==CCHAR && pa_exp->kind.m_EnExpKind==ConstK)
AllGlobals.code.write("\nmov\tax,\'",9);//字符常量要加單引號。
else
AllGlobals.code.write("\nmov\tax,",8);
if(pa_exp->kind.m_EnExpKind==IdK) //變量名作擴展處理。
if((pa_exp->m_EnTypevalue=m_panalyzer->m_Csymtab->
st_lookuptype(pa_exp->m_strIDname,pa_exp->m_strScope))!=ERROR)
strcat(pa_exp->m_strIDname,pa_exp->m_strScope);
else
strcat(pa_exp->m_strIDname,"@Glo");
AllGlobals.code.write(pa_exp->m_strIDname,strlen(pa_exp->m_strIDname));
if(pa_exp->m_EnTypevalue==CCHAR && pa_exp->kind.m_EnExpKind==ConstK)
AllGlobals.code.write("\'",1);
AllGlobals.code.write("\npush\tax",8);
break;
case OpK: //碰到操作符,則對他的操作數作遞歸處理,再處理操作符。
CTreeNode *p1=pa_exp->m_pchild[0];
CTreeNode *p2;
if(pa_exp->m_pchild[1]) p2=pa_exp->m_pchild[1];
else p2=NULL; //單目運算符的p2為NULL,
generate_stmt(p1);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -