📄 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 + -