/*
 * Simple PASCAL compiler
 * File name: code_generator.c 
 * ---------------------------
 * Assmebly code generator 
 *    Chakma Junan
 *    e993178@edu.tut.ac.jp
 *    Information and Computer Science
 *    Toyohashi University of Technology
 */
#include <stdio.h>
#include <string.h>
#include "getsym.h"

#define	BASE_TEMP_ADDRESS	1000
extern	TOKEN	tok;	
extern	FILE	*infile;
extern	FILE	*outfile;

extern	TOKEN	stack[100];
extern	int	top;
extern	int	procedure_flag;
extern	struct	list	*Table;
extern	struct	list	*procedure_table;

/*
 * Function: code_generator
 * Usage: (void)code_generator();
 * ------------------------------
 * This is a dummy function! It was originally written
 * for the diagonistic purpose only. It shouldn't ve
 * referred from anywhere. However, I was unwilling to
 * to remove it. Why? Cause it was the nucleus of 
 * these routines in this file.
 */
code_generator()
{
    register int temp1, temp2;
    temp1 = tok.value;
    fprintf(outfile, "Inside code_generator! \n");
    getsym();
    switch(tok.value) {
       case PLUS:
	   getsym(); temp2 = tok.value;
	   fprintf(outfile, "%d + %d \n", temp1, temp2);
	   break;
       case MINUS:
	   getsym(); temp2 = tok.value;
	   fprintf(outfile, "%d - %d \n", temp1, temp2);
	   break;
       case TIMES:
	   getsym(); temp2 = tok.value;
	   fprintf(outfile, "%d * %d \n", temp1, temp2);
	   break;
       default:
	   fprintf(outfile, "line: %d: %s: Unknown Operator. \n",
			    tok.sline, tok.charvalue);
           exit(1);
    }
}

/*
 * Function: gen_code
 * Usage: (void)gen_code(int sign);
 * --------------------------------
 * This function produces assembly codes depending on the sign
 * passed to this function. It takes the top two values(either
 * IDENTIFIER or NUMBER) from the stack as 'sign's operands. For
 * example, the following condition in the stack will produce
 * assembly codes equivalent to: 2 * x (assuming sign == TIMES).
 *                               ^^^^^
 *
 *           STACK                  STACK
 *           -----                  -----
 * top -> 4 |     |                |     |
 *           -----                  -----
 *        3 |  x  |       top -> 3 |     |    Here,
 *           -----                  -----
 *        2 |  2  |              2 |  T0 |      T0 = 2 * x
 *           -----                  -----
 *        1 |  y  |              1 |  y  |
 *
 */
gen_code(int sign)
{
    TOKEN token;
    static int tmp = 0;
    int address;

    gen_load("r2");
    top--;

    gen_load("r3");
    top--;

    token.attr = IDENTIFIER;
    token.value = 0;
    strcpy(token.charvalue, "TEMP");
    token.charvalue[4] = (char)('0'+ tmp);
    token.charvalue[5] = '\0';
    token.sline = BASE_TEMP_ADDRESS + tmp++;

    switch(sign)
    {
       case PLUS : fprintf(outfile, "      addr     r3, r2 \n"); break;
       case MINUS: fprintf(outfile, "      subr     r3, r2 \n"); break;
       case TIMES: fprintf(outfile, "      mulr     r3, r2 \n"); break;
       case DIV  : fprintf(outfile, "      divr     r3, r2 \n"); break;
       default   :    
	           fprintf(outfile, "Unknow sign to generate code. \n"); 
	           exit(1);
    }

    if(procedure_flag == 1)
    {
       if(strncmp(token.charvalue, "TEMP", 4) == 0)
       {
	  fprintf(outfile,"      store    r3, %d \n", token.sline);
       }
       else
       {
          address = find_address(token.charvalue, procedure_table);
	  if(address != -1)
             fprintf(outfile,"      store    r3, %d(BR) \n", address);
	  else
	  {
             address = find_address(token.charvalue, Table);
	     if(address != -1)
                fprintf(outfile,"      store    r3, %d \n", address);
	     else if(address == -1)
                error("'%s' not declared. \n", token.charvalue); 
	  }
       }
    } 
    else
    {
       if(strncmp(token.charvalue, "TEMP", 4) == 0)
       {
	  fprintf(outfile,"      store    r3, %d \n", token.sline);
       }
       else
       {
          address = find_address(token.charvalue, Table);
	  if(address != -1)
             fprintf(outfile,"      store    r3, %d \n", address);
	  else if(address == -1)
	     error("'%s' not declared. \n", token.charvalue);
       }
    }
    fprintf(outfile, "\n");
    stack[top++] = token;
}

/*
 * Function: gen_load
 * Usage: (void)gen_load(char *target);
 * ------------------------------------
 * Loads from the top of the stack to the 'target'.
 *
 *           STACK                  STACK
 *           -----                  -----
 * top -> 4 |     |                |     |
 *           -----                  -----
 *        3 |  x  |       top -> 3 |     |   if target = 'r3' 
 *           -----                  -----
 *        2 |  2  |              2 |  2  |      r3 = x
 *           -----                  -----
 *          |  y  |              1 |  y  |
 */
gen_load(char *target)
{
    int address;

    if(top <= 0) error("Empty STACK!!!!!!!!!!!!!! \n");
    if(stack[top - 1].attr == IDENTIFIER)
    {
       if(procedure_flag == 1)
       {
	  if(strncmp(stack[top - 1].charvalue, "TEMP", 4) == 0)
	     fprintf(outfile,"      load     %s, %d \n", target, stack[top-1].sline);
          else
	  {
	     address = find_address(stack[top - 1].charvalue, procedure_table);
	     if(address != -1)
                fprintf(outfile,"      load     %s, %d(BR) \n", target, address);
	     else
	     {
	        address = find_address(stack[top - 1].charvalue, Table);
	        if(address != -1)
                   fprintf(outfile,"      load     %s, %d \n", target, address);
		else if(address == -1)
		   error("'%s' not declared. \n", stack[top - 1].charvalue);
	     }
          }
       } else
       {
	  if(strncmp(stack[top - 1].charvalue, "TEMP", 4) == 0)
	     fprintf(outfile,"      load      %s, %d \n", target, stack[top-1].sline);
          else
	  {
	     address = find_address(stack[top - 1].charvalue, Table);
	     if(address != -1)
                fprintf(outfile,"      load      %s, %d \n", target, address);
	     else if(address == -1)
		error("'%s': not declared.", stack[top - 1].charvalue);
          }
       }
    }
    else if(stack[top - 1].attr == NUMBER)
    {
       fprintf(outfile,"      loadi    %s, %d \n", target, stack[top - 1].value);
    }
    if(stack[top - 1].sline < 0) 
       fprintf(outfile,"      muli     %s, -1 \n", target);
}

/*
 * Function: gen_store
 * Usage: (void)gen_store(char *tmp);
 * ----------------------------------
 * This function stores contents of the register 'r3' to 'tmp'. 
 */
gen_store(char *tmp)
{
    int address;

    if(procedure_flag == 1)
    {
	address = find_address(tmp, procedure_table);
	if(address != -1)
           fprintf(outfile,"      store    r3, %d(BR) \n", address);
	else
	{
	   address = find_address(tmp, Table);
	   if(address != -1)
              fprintf(outfile,"      store    r3, %d \n", address);
	   else if(address == -1)
	      error("Undeclared variable: %s", tmp);
	}
    } else
    {
	   address = find_address(tmp, Table);
	   if(address != -1)
              fprintf(outfile,"      store    r3, %d \n", address);
	   else if(address == -1)
	      error("Undeclared variable: %s", tmp);
    }
}

/*
 * Function: gen_call_before
 * Usage: (void)gen_call_before(struct list *local_table);
 * -------------------------------------------------------
 * This function is called before a function call. However, it should
 * only be called after the parameters are passed(actually stored).
 * Point to remember that it increases:
 *
 *     SP(stack pointer) = SP + number of variables in the table;
 *     BR(base register) = BR + number of variables in the table + 1;
 */
gen_call_before(struct list *local_table)
{
     fprintf(outfile,"      addi     sp, %d \n", countData(local_table));
     fprintf(outfile,"      addi     br, %d \n", 1 + countData(local_table));
}

/*
 * Function: gen_call_after
 * Usage: (void)gen_call_after(struct list *local_table, char *tmp);
 * -----------------------------------------------------------------
 * This function is called when a function call is made from the
 * source program. Point to remember that it decreases:
 *
 *     SP(stack pointer) = SP - number of variables in the table;
 *     BR(base register) = BR - number of variables in the table - 1;
 */
gen_call_after(struct list *local_table, char *tmp)
{
     fprintf(outfile,"      call     %s \n", tmp);
     fprintf(outfile,"      subi     sp, %d \n", countData(local_table));
     fprintf(outfile,"      subi     br, %d \n", 1 + countData(local_table));
}

/*
 * Function: gen_label
 * Usage: (void)gen_label(int local_label);
 * ----------------------------------------
 * This function produces label.
 */
gen_label(int local_label)
{
     fprintf(outfile, "L%d: \n", local_label);
}

/*
 * Function: gen_loadr
 * Usage: (void)gen_loadr(char *target, char *source);
 * ---------------------------------------------------
 * Loads from register 'source' to register 'target'
 */
gen_loadr(char *target, char *source)
{
    fprintf(outfile, "      loadr    %s, %s \n", target, source);
}

/*
 * Function: gen_cmpr
 * Usage: (void)gen_cmpr(char *target, char *source);
 * --------------------------------------------------
 * Compares registers!
 */
gen_cmpr(char *target, char *source)
{
    fprintf(outfile, "      cmpr     %s, %s \n", target, source);
}

/*
 * Function: gen_condition
 * Usage: (void)gen_condition(char *cond, int label);
 * --------------------------------------------
 * This function produces code to jump to label according
 * to the condition.
 */
gen_condition(char *cond, int label)
{
    fprintf(outfile, "      %s      L%d \n", cond, label);
}

/*
 * Function: gen_write
 * Usage: gen_write(char *tmp);
 * ----------------------------
 * It produces the codes to write variables on the screen. 
 * However, point to remember that, this function writes
 * something on the screen even if it can't find a variable
 * in the variable tables.
 */
gen_write(char *tmp)
{
    if(procedure_flag == 1)
       fprintf(outfile, "      load     r0, %d(BR) \n",
	       find_address(tmp, procedure_table));
    else
       fprintf(outfile, "      load     r0, %d\n", find_address(tmp, Table));
    
    fprintf(outfile, "      writed   r0 \n");
    fprintf(outfile, "      loadi    r0, '\\n' \n");
    fprintf(outfile, "      writec   r0 \n");
}

/*
 * Function: gen_store_param
 * Usage: (void)gen_store_param(int br_counter);
 * ---------------------------------------------
 * This function produces codes for parameter passing.
 */
gen_store_param(int br_counter)
{
    fprintf(outfile,"      store    r3, %d(BR) \n", br_counter);
}
