/* gen_code.c */
/* author: Edward A. Green */
/* purpose: to write assembler code */

#include "proj3.h"


/* generate assembly language program */
/* make_asm_code: */
void make_asm_code(BLOCK *blocks)
{
	BLOCK  *blk;
	CODE   *code;
	SYMTBL *tbl;
	int    num_parms;
	char   *s;
	int    i;
	void init_regs(void);
	void fill_local_addrs(BLOCK *blks);
	void find_memory_size(BLOCK *blks);
	void gen_code_rtn(CODE *code, BLOCK *blk);
	void gen_code_jump(CODE *code, BLOCK *blk);
	void gen_code_ary(CODE *code, BLOCK *blk);
	void gen_code1(CODE *code, BLOCK *blk);
	void gen_code3(CODE *code, BLOCK *blk);
	void dump_io_routines(void);
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char *strsave(char *);

	fill_local_addrs(blocks);
	find_memory_size(blocks);

	printf("#NO_APP\n");
	printf("gcc_compiled.:\n");
	printf(".text\n");
	printf("\t.align 2\n");

/*	Add floating point constant directives here */

	dump_io_routines();

	blocks->last->next=NULL;
	for (blk=blocks; blk!=NULL; blk=blk->next)
	  {
	  printf("\t.align 1\n");
	  if (blk->lead->flds[0][0]=='_')
	    {
	    printf(".globl _main\n");
	    printf("_main:\n");
	    num_parms=0;
	    }
	  else
	    {
	    printf(".globl %s\n",blk->lead->flds[0]);
	    printf("%s:\n",blk->lead->flds[0]);
	    s=strsave(blk->table->symbol);
	    s[strlen(s)-1]='\0';
	    tbl=sym_look_up(blk,s);
	    free(s);
	    num_parms=tbl->nparms;
	    }
	  printf("\tenter [],%d\n",blk->memsize);

	  blk->lead->last->next=NULL;

	  for (code=blk->lead; code!=NULL; code=code->next)
	    {
	    if (strlen(code->flds[0])>0 && code!=blk->lead)
	      printf("%s:\n",code->flds[0]);

	    switch (code->flds[1][0])
	      {
	      case('A'):
	      case('D'):
	      case('M'):
	      case('S'):
		gen_code3(code,blk);
		break;
	      case('P'):
		gen_code1(code,blk);
		break;
	      case('['):
		gen_code_ary(code,blk);
		break;
	      case('J'):
	      case('C'):
		gen_code_jump(code,blk);
		break;
	      case('R'):
		gen_code_rtn(code,blk);
		break;
	      }
	    }
	  blk->lead->last->next=blk->lead;

	  printf("\texit []\n");
	  printf("\tret %d\n",num_parms*4);

	  /* clear up temporaries between blocks */
	  init_regs();

	  }
	blocks->last->next=blocks;

	/* dump space for globals here */
	tbl=blocks->last->table;  /* position to main program block */
	while (tbl!=NULL)
	  {
	  i=4*((tbl->to-tbl->from)+1);
	  if (tbl->mem!=NULL && tbl->stype!='u')
	    printf(".comm %s,%d\n",tbl->mem,i);
	  tbl=tbl->next;
	  }
}

void gen_code_rtn(CODE *code, BLOCK *blk)
{
	char type;
	char name[100];
	SYMTBL *sym;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);

	if (blk->table->mem!=NULL)
	  {
	  /* look up type of return value */
	  strcpy(name,blk->table->symbol);
	  name[strlen(name)-1]='\0';
	  sym=sym_look_up(blk,name);
	  type='r';
	  if ('r'==sym->stype) type='f';

	  if (type=='r') printf("\tmovd %s,r0\n",sym->mem);
	  else           printf("\tmovf %s,f0\n",sym->mem);
	  }

	return;
}

void gen_code_jump(CODE *code, BLOCK *blk)
{
	SYMTBL *st;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char   *sym_addr;
	char   *op_code;
	char   addr;
	char   type;
	char   *f_flag;
	char   type_look_up(char *);
	char   addr_look_up(char *);
	char   *temp_look_up(char *,int,char,char);
	void   temp_remove(char *);
	void   spill_all_reg(int memsize);

	/* find the symbolic addresses of operand 1 */
	addr='d'; /* default address mode is data */
	if (strlen(code->flds[2])>0)
	  {
	  f_flag="";
	  if ((code->flds[2][0]>='0' && code->flds[2][0]<='9')
	     || code->flds[2][0]=='-')
	    {
	    sym_addr=code->flds[2];
	    if (strchr(code->flds[2],'.'))
	      {
	      f_flag="0f";
	      type='f';
	      }
	    else type='r';
	    }
	  else if (code->flds[2][0]=='T')
	    {
	    type=type_look_up(code->flds[2]);
	    addr=addr_look_up(code->flds[2]);
	    sym_addr=temp_look_up(code->flds[2],blk->memsize,type,'d');
	    }
	  else
	    {
	    st=sym_look_up(blk,code->flds[2]);
	    sym_addr=st->mem;
	    if (st->stype=='i') type='r'; else type='f';
	    }
	  }

	/* default branch condition */
	op_code="beq";

	switch (code->flds[1][1])
	  {
	  case('M'):
	    printf("\tbr %s\n",code->flds[4]);
	    break;

	  case('A'):
	    if (code->flds[4][0]=='_') return; /* CALL _EXIT */
	    spill_all_reg(blk->memsize);
	    printf("\tjsr %s\n",code->flds[4]);
	    break;

	  case('G'):
	    op_code="blt";

	  case('Z'):
	    if (addr=='a')
	      {
	      printf("\tmovd %s,r0\n",sym_addr);
	      sym_addr="0(r0)";
	      }
	    if (type=='r') printf("\tcmpqd 0,%s\n",sym_addr);
	    else	   printf("\tcmpf 0f0,%s%s\n",f_flag,sym_addr);
	    printf("\t%s %s\n",op_code,code->flds[4]);
	    if (code->flds[2][0]=='T') temp_remove(code->flds[2]);
	    break;
	  }
}


void gen_code_ary(CODE *code, BLOCK *blks)
{
	int    i;
	char   *sym_addr[3];
	char   *f_flag[3];
	char   type[3];
	char   addr[3];
	int    offset;
	char   *op_code;
	SYMTBL *sym;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char   *temp_look_up(char *, int, char, char);
	char   type_look_up(char *);
	char   addr_look_up(char *);
	void   temp_remove(char *);

	/* find the symbolic addresses of three operands */
	for (i=0; i<3; i++)
	  {
	  addr[i]='d';
	  f_flag[i]="";
	  if ((code->flds[i+2][0]>='0' &&
	      code->flds[i+2][0]<='9')
	   || code->flds[i+2][0]=='-')
	    {
	    sym_addr[i]=code->flds[i+2];
	    if (strchr(code->flds[i],'.'))
	      {
	      f_flag[i]="0f";
	      type[i]='f';
	      }
	    else type[i]='r';
	    }
	  else if (code->flds[i+2][0]=='T')
	    {
	    type[i]=type_look_up(code->flds[i+2]);
	    if (type[i]==0) type[i]=type[0];  /* adopt the type of ary */
	    addr[i]=addr_look_up(code->flds[i+2]);
	    if (addr[i]==0) addr[i]='a'; /* this should give an address */
	    sym_addr[i]=
	      temp_look_up(code->flds[i+2],blks->memsize,type[i],addr[i]);
	    }
	  else
	    {
	    sym=sym_look_up(blks,code->flds[i+2]);
	    sym_addr[i]=sym->mem;
	    if (sym->stype=='i') type[i]='r'; else type[i]='f';
	    if (i==0) offset=sym->from;  /* get array offset */
	    }
	  }

	/* resolve index of array.  Put value of index in r0. */
	if (addr[1]=='a')
	  {
	  printf("\tmovd %s,r1\n",sym_addr[1]);
	  printf("\tmovd 0(r1),r0\n");
	  }
	else
	  printf("\tmovd %s,r0\n",sym_addr[1]);

	/* adjust index for array offset */
	if (offset!=0) printf("\tsubd %d,r0\n",offset);

	/* if I were checking for valid bounds at run time, */
	/* that code would go here */

	/* decide on op code */
	/* decide on whether to move address (for temps) or data */
	/*    how to handle address references in 3rd parm */
	if (addr[2]=='a' && code->flds[1][2]!='=')
	  {
	  op_code="addr";
	  }
	else
	  {
	  /* see if operand needs dereferencing */
	  if (addr[2]=='a')  
	    {
            /* at this point, opcode must be []= */
	    printf("\tmovd %s,r1\n",sym_addr[2]);
	    sym_addr[2]="0(r1)";
	    }
	  /* decide on an op code for data transfers */
	  if (code->flds[1][2]=='=')  i=2; else i=0;
	  if (type[i]=='f')
	    {
	    if (type[2-i]=='r') op_code="truncfd";
	    if (type[2-i]=='f') op_code="movf";
	    }
	  if (type[i]=='r')
	    {
	    if (type[2-i]=='r') op_code="movd";
	    if (type[2-i]=='f') op_code="movdf";
	    }
	  if (code->flds[4][0]=='T') temp_remove(code->flds[4]);
	  }

	/* write the code */
	if (code->flds[1][2]=='=')
	  {
	  printf("\t%s %s%s,%s[r0:d]\n",
		  op_code,f_flag[2],sym_addr[2],sym_addr[0]);
	  if (code->flds[4][0]=='T') temp_remove(code->flds[3]);
	  }
	else
	  {
	  printf("\t%s %s[r0:d],%s\n",op_code,sym_addr[0],sym_addr[2]);
	  }

	if (code->flds[3][0]=='T') temp_remove(code->flds[2]);

}

void gen_code1(CODE *code, BLOCK *blk)
{
	SYMTBL *st;
	char   type;
	char   addr;
	char   *op_code;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char   *sym_addr;
	char   type_look_up(char *);
	char   addr_look_up(char *);
	char   *temp_look_up(char *, int, char, char);
	void   spill_all_reg(int memsize);

	/* we must spill registers if pushing (in preparation for a call) */
	if (!strcmp(code->flds[1],"PUSH")) spill_all_reg(blk->memsize);

	/* find the symbolic addresses of operand 1 */
	addr='d';
	if ((code->flds[4][0]>='0' && code->flds[4][0]<='9')
	   || code->flds[2][0]=='-')
	  {
	  /* an error condition */
	  sym_addr=code->flds[4];
	  }
	else if (code->flds[4][0]=='T')
	  {
	  type=type_look_up(code->flds[4]);
	  addr=addr_look_up(code->flds[4]);
	  if (type==0)  /* if no type, cast type as return value */
	    {
	    st=sym_look_up(blk,code->last->flds[4]);
	    type=st->stype;
	    if (type=='i') type='r'; else type='f';
	    }
	  sym_addr=temp_look_up(code->flds[4],blk->memsize,type,'d');
	  }
	else
	  {
	  st=sym_look_up(blk,code->flds[4]);
	  sym_addr=st->mem;
	  type='r';
	  if (st->stype=='r') type='f';
	  }

	/* find symbolic address of address modes of a */
	if (addr=='a')
	  {
	  printf("\tmovd %s,r1\n",sym_addr);
	  sym_addr="0(r1)";
	  }

	if (!strcmp("PULL",code->flds[1]))
	  {
	  /* find out where last jsr put it's return value */
	  st=sym_look_up(blk,code->last->flds[4]);
	  if (st->stype=='i')
	    {
	    if (type=='r') printf("\tmovd r0,%s\n",sym_addr);
	    if (type=='f') printf("\tmovdf r0,%s\n",sym_addr);
	    }
	  else
	    {
	    if (type=='r') printf("\tmovf f0,%s\n",sym_addr);
	    if (type=='f') printf("\ttruncfd f0,%s\n",sym_addr);
	    }
	  }
	else   /* PUSH */
	  {
	  printf("\taddr %s,tos\n",sym_addr);

	  /* pass flag type of read or write: 0:int, 1:real */
	  if  (!strcmp("write",code->next->flds[4])
	    || !strcmp("read",code->next->flds[4]))
	    {
	    if (type=='r') printf("\tmovd 0,tos\n");
	    else           printf("\tmovd 1,tos\n");
	    }
	  }
}

/* generate code for a three address statement (x:=y op z) */
void gen_code3(CODE *code, BLOCK *blks)
{
	int    i;
	char   *sym_addr[3];
	char   *f_flag[3];
	char   type[3];
	char   addr[3];
	SYMTBL *sym;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char   *temp_look_up(char *, int, char, char);
	void   temp_remove(char *);
	char   *reg;
	char   type_look_up(char *);
	char   addr_look_up(char *);
	void   gen_code_mov(CODE *code, BLOCK *blks);

	char *op_code[] = {"addd", "subd", "muld", "quod", "remd",
			   "addf", "subf", "mulf", "divf"};
	int op_code_ndx;

	/* look up proper op code */
	     if (!strcmp(code->flds[1],"ADD")) op_code_ndx=0;
	else if (!strcmp(code->flds[1],"SUB")) op_code_ndx=1;
	else if (!strcmp(code->flds[1],"MUL")) op_code_ndx=2;
	else if (!strcmp(code->flds[1],"DI"))  op_code_ndx=3;
	else if (!strcmp(code->flds[1],"DIV")) op_code_ndx=3;
	else if (!strcmp(code->flds[1],"MOD")) op_code_ndx=4;
	else if (!strcmp(code->flds[1],"MOV"))
	  {
	  gen_code_mov(code,blks);
	  return;
	  }

	/* find the symbolic addresses of three operands */
	for (i=0; i<3; i++) type[i]=' ';
	for (i=0; i<3; i++)
	  {
	  addr[i]='d';
	  f_flag[i]="";
	  if ((code->flds[i+2][0]>='0' &&
	       code->flds[i+2][0]<='9')
	    || code->flds[2][0]=='-')
	    {
	    sym_addr[i]=code->flds[i+2];
	    if (strchr(sym_addr[i],'.')!=NULL)
	      {
	      f_flag[i]="0f";
	      type[i]='f';
	      }
	    else type[i]='r';
	    }
	  else if (code->flds[i+2][0]=='T')
	    {
	    type[i]=type_look_up(code->flds[i+2]);
	    if (type[i]==0)
	      {
	      if (type[0]=='f' || type[1]=='f') type[i]='f';
	      else type[i]='r';
	      }
	    addr[i]=addr_look_up(code->flds[i+2]);
	    if (addr[i]==0) addr[i]='d';
	    sym_addr[i]=
	      temp_look_up(code->flds[i+2],blks->memsize,type[i],'d');
	    }
	  else
	    {
	    sym=sym_look_up(blks,code->flds[i+2]);
	    sym_addr[i]=sym->mem;
	    type[i]='r';
	    if (sym->stype=='r') type[i]='f';
	    }
	  }

	if (type[0]!='r' || type[1]!='r') op_code_ndx += 5;

	/* write the code */
	/* dereference address fields */
	if (addr[0]=='a')
	  {
	  printf("\tmovd %s,r0\n",sym_addr[0]);
	  sym_addr[0]="0(r0)";
	  }
	if (addr[1]=='a')
	  {
	  printf("\tmovd %s,r1\n",sym_addr[1]);
	  sym_addr[1]="0(r1)";
	  }

	if (type[0]=='r' && type[1]=='r')
	  {
	  printf("\tmovd %s,r0\n",sym_addr[0]);
	  printf("\t%s %s,r0\n",op_code[op_code_ndx],sym_addr[1]);
	  if (type[2]=='r')
	    printf("\tmovd r0,%s\n",sym_addr[2]);
	  else
	    printf("\tmovdf r0,%s\n",sym_addr[2]);
	  }

	if (type[0]=='r' && type[1]=='f')
	  {
	  printf("\tmovdf %s,f0\n",sym_addr[0]);
	  printf("\t%s %s%s,f0\n",op_code[op_code_ndx],f_flag[1],sym_addr[1]);
	  if (type[2]=='r')
	    printf("\ttruncfd f0,%s\n",sym_addr[2]);
	  else
	    printf("\tmovf f0,%s\n",sym_addr[2]);
	  }

	if (type[0]=='f' && type[1]=='r')
	  {
	  printf("\tmovf %s%s,f0\n",f_flag[0],sym_addr[0]);
	  printf("\tmovdf %s,f1\n",sym_addr[1]);
	  printf("\t%s f1,f0\n",op_code[op_code_ndx]);
	  if (type[2]=='r')
	    printf("\ttruncfd f0,%s\n",sym_addr[2]);
	  else
	    printf("\tmovf f0,%s\n",sym_addr[2]);
	  }

	if (type[0]=='f' && type[1]=='f')
	  {
	  printf("\tmovf %s%s,f0\n",f_flag[0],sym_addr[0]);
	  printf("\t%s %s%s,f0\n",op_code[op_code_ndx],f_flag[1],sym_addr[1]);
	  if (type[2]=='r')
	    printf("\ttruncfd f0,%s\n",sym_addr[2]);
	  else
	    printf("\tmovf f0,%s\n",sym_addr[2]);
	  }

	if (code->flds[2][0]=='T') temp_remove(code->flds[2]);

	if (code->flds[3][0]=='T') temp_remove(code->flds[3]);
}


void   gen_code_mov(CODE *code, BLOCK *blks)
{
	int    i;
	char   *sym_addr[3];
	char   type[3];
	char   addr[3];
	char   *f_flag[3];
	SYMTBL *sym;
	SYMTBL *sym_look_up(BLOCK *blk, char *var);
	char   *temp_look_up(char *, int, char, char);
	char   type_look_up(char *);
	char   addr_look_up(char *);
	void   temp_remove(char *);


	/* find the symbolic addresses of three operands */
	for (i=0; i<3; i++)
	  {
	  type[i]=' ';
	  f_flag[i]="";
	  if (i!=1)
	    {
	    if ((code->flds[i+2][0]>='0' &&
		 code->flds[i+2][0]<='9')
	      || code->flds[i+2][0]=='-')
	      {
	      sym_addr[i]=code->flds[i+2];
	      if (strchr(code->flds[2],'.'))
		{
		f_flag[i]="0f";
		type[i]='f';
		}
	      else
		{
		type[i]='r';
		}
	      }
	    else if (code->flds[i+2][0]=='T')
	      {
	      type[i]=type_look_up(code->flds[i+2]);
	      addr[i]=addr_look_up(code->flds[i+2]);
	      if (type[i]==0) type[i]=type[0];
	      sym_addr[i]=
		 temp_look_up(code->flds[i+2],blks->memsize,type[i],'d');
	      }
	    else
	      {
	      sym=sym_look_up(blks,code->flds[i+2]);
	      sym_addr[i]=sym->mem;
	      type[i]='r';
	      if (sym->stype=='r') type[i]='f';
	      }
	    }
	  }

	/* write the code */
	/* dereference address fields */
	if (addr[0]=='a')
	  {
	  printf("\tmovd %s,r0\n",sym_addr[0]);
	  sym_addr[0]="0(r0)";
	  }
	if (type[0]=='r')
	  {
	  if (type[2]=='r') printf("\tmovd %s,%s\n",sym_addr[0],sym_addr[2]);
	  if (type[2]=='f') printf("\tmovdf %s,%s\n",sym_addr[0],sym_addr[2]);
	  }
	else
	  {
	  if (type[2]=='r')
	    printf("\ttruncfd %s%s,%s\n",f_flag[0],sym_addr[0],sym_addr[2]);
	  if (type[2]=='f')
	    printf("\tmovf %s%s,%s\n",f_flag[0],sym_addr[0],sym_addr[2]);
	  }

	if (code->flds[2][0]=='T') temp_remove(code->flds[2]);
}


void dump_io_routines(void)
{
	/* c format strings */
	printf("LC1:\n");
	puts("\t.ascii \"%d\\12\\0\"");
	printf("LC2:\n");
	puts("\t.ascii \"%f\\12\\0\"");
	printf("LC3:\n");
	puts("\t.ascii \"%d\\0\"");
	printf("LC4:\n");
	puts("\t.ascii \"%f\\0\"");
	printf("LC5:\n");
	puts("\t.ascii \"integer> \\0\"");
	printf("LC6:\n");
	puts("\t.ascii \"real> \\0\"");


	/* write out write routine */
	printf("\t.align 1\n");
	printf(".globl write\n");
	printf("write:\n");
	printf("\tenter [],0\n");
	printf("\tcmpd 0,8(fp)\n");
	printf("\tbne write2\n");
	printf("\tmovd 0(12(fp)),tos\n");
	printf("\taddr LC1,tos\n");
	printf("\tbr write3\n");
	printf("write2:\n");
	printf("\tmovfl 0(12(fp)),tos\n");
	printf("\taddr LC2,tos\n");
	printf("write3:\n");
	printf("\tjsr _printf\n");
	printf("\texit []\n");
	printf("\tret 8\n");

	/* write out read routines (read routines have prompts) */
	printf("\t.align 1\n");
	printf(".globl read\n");

	printf("read:\n");
	printf("\tenter [],0\n");
	printf("\tcmpd 0,8(fp)\n");
	printf("\tbne read2\n");

	printf("\taddr LC5,tos\n");
	printf("\tjsr _printf\n");
	printf("\tmovd 12(fp),tos\n");
	printf("\taddr LC3,tos\n");

	printf("\tbr read3\n");

	printf("read2:\n");

	printf("\taddr LC6,tos\n");
	printf("\tjsr _printf\n");
	printf("\tmovd 12(fp),tos\n");
	printf("\taddr LC4,tos\n");

	printf("read3:\n");
	printf("\tjsr _scanf\n");
	printf("\texit []\n");
	printf("\tret 8\n");
}
