//***************************************************************************
// File name : locator.c
// Designer : Chih-Chieh Tsao
// Create date: Dec. 14, 2007
// Description:
//             locator code for dynamic loading, modified from static part
//
//***************************************************************************

#include <math.h>
#include "locator.h"

//#define DEBUG
#define DEBUG_A

/*******************************************************************
* Name: HexToDec
* Input: char *data, int length
* Output: int
* Description: convert hex number to dec number
********************************************************************/
int HexToDec(char *data, int length)
{
    int i;
    int res = 0;

    for(i=length-1; i>=0; i--)
    {
        if ((int)data[i] == 0)
            continue;

        else if ((int)data[i] <= 57)
            res += (((int)data[i]-48) * (int)pow (16, length-1-i));
        else
            res += (((int)data[i]-55) * (int)pow (16, length-1-i));
    }
    
    return res;
}


/*******************************************************************
* Name: eval_num_vars
* Input: int start, int end
* Output: int
* Description: evaluate how many variables in one global variables
********************************************************************/
int eval_num_vars(int start, int end)
{
    return (end - start + 1)/4;
}


/*******************************************************************
* Name: judge_data
* Input: char *data
* Output: int
* Description: check if the data is the data segment
********************************************************************/
int judge_data(char *data)
{
    if(strncmp(data, MEMINIT, strlen(MEMINIT)) == 0)
    {
	return 1;
    }
    else if(strncmp(data, MEMINITFUNC, strlen(MEMINITFUNC)) == 0)
    {
        return 2;
    }
    else if(strncmp(data, IMEM_RELOC, strlen(IMEM_RELOC)) == 0)
    {
        return 3;
    }
    else if(strcmp(data, TRAP) == 0)
    {
        return 4;
    }
    else 
    {
        return 0;
    }
}

/*******************************************************************
* Name: get_offset
* Input: char *data
* Output: int - the offset from data
* Description: get data segment started point
********************************************************************/
int get_offset(char *data)
{
    int i, length;
    length = strlen(data);
    for(i=strlen(MEMINIT);i<length;i++)
    {
	if(data[i] == '{')
	    return i;
    }
    return(-1);
}

/*******************************************************************
* Name: filldata
* Input:
*          char *data - the value will be fill in outFP
*          int size   - the data size of data, for example,  data = 0x123, size =3; data = 0x123456, size = 6
* Output: 
*          FILE *outFP : fill data in outFP
* Description:
*          fill the data in outFP, the total fill in size will be 8
*          if the input "size" is less than 8, it will fill 0 in outFP
********************************************************************/
void filldata(char *data, int size, FILE *outFP)
{
    fprintf(outFP, data);
}

/*******************************************************************
* Name: rearrangeData
* Input: void  (all_data, bss: two global variables)
* Output: int
* Description: 
*            rearrange, to make bss be followed by all_data
*            
********************************************************************/
int rearrangeData()
{
    int num_vars, bcount=0, dcount=0;
    int prev_start, prev_end, next_start = 0;
    gVars *tmpD, *tmpB;
    
    tmpD = all_data;
    tmpB = bss;
    
    while(tmpB != NULL)
    {
        bcount ++;
        num_vars = eval_num_vars(tmpB->start, tmpB->end);
        bss_size += 4*num_vars;

        if (bcount == 1)
            prev_start = tmpB->new_start = 0;
        else
            prev_start = next_start;

        prev_end = tmpB->new_end = (prev_start) + (4*num_vars - 1);
        next_start = tmpB->new_end + 1;
        tmpB = (gVars *) tmpB->link;

        if(tmpB == NULL)
            break;
    }    

#ifdef DEBUG
    fprintf(stderr, "\n [ bcount = %d ] \n", bcount);
    fprintf(stderr, "\n [ Next_START = %d ] \n", next_start);
#endif
    
    while(tmpD != NULL)
    {
        dcount ++;
        num_vars = eval_num_vars(tmpD->start, tmpD->end);
        data_size += 4*num_vars;

        prev_start = tmpD->new_start = next_start;
        prev_end = tmpD->new_end = (prev_start) + (4*num_vars - 1);
        next_start = tmpD->new_end + 1;
        tmpD = (gVars *) tmpD->link;

        if(tmpD == NULL)
            break;     
    }

#ifdef DEBUG
    fprintf(stderr, "\n [ dcount = %d ] \n", dcount);
    fprintf(stderr, "total_bss = %d\n", bcount);
    tmpB = bss;
    int i, j;
    for (i=0; i<bcount; i++, tmpB = (gVars *)tmpB->link)
    {
        fprintf(stderr, "BSS_data[%d] :: start = %d, end = %d\n", i+1, tmpB->start, tmpB->end);
        fprintf(stderr, "BSS_data[%d] :: NEWstart = %d, NEWend = %d\n", i+1, tmpB->new_start, tmpB->new_end);
        num_vars = eval_num_vars(tmpB->new_start, tmpB->new_end);
        for (j=0; j<num_vars; j++)
        {
            fprintf(stderr, "strlen = %d\n", strlen(tmpB->variables[j]));
            fprintf(stderr, "variables[%d] = %s\n", j, tmpB->variables[j]);
        }
        printf("\n--------------\n");
    }
    printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
    fprintf(stderr, "\ntotal_vars = %d\n", dcount);
    tmpD = all_data;
    for (i=0; i<dcount; i++, tmpD = (gVars *)tmpD->link)
    {
        fprintf(stderr, "Gdata[%d] :: start = %d, end = %d\n", i+1, tmpD->start, tmpD->end);
        fprintf(stderr, "Gdata[%d] :: NEWstart = %d, NEWend = %d\n", i+1, tmpD->new_start, tmpD->new_end);
        num_vars = eval_num_vars(tmpD->start, tmpD->end);
        for (j=0; j<num_vars; j++)
        {
            fprintf(stderr, "strlen = %d\n", strlen(tmpD->variables[j]));
            fprintf(stderr, "variables[%d] = %s\n", j, tmpD->variables[j]);
        }
        printf("\n--------------\n");
    }
    printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
#endif



    return 0;
}

/*******************************************************************
* Name: separate_bss
* Input: char *data, int data_count
* Output: void
* Description: separate bss from all_data
********************************************************************/
int separate_bss(gVars *data, int data_count)
{
    int i, num_vars, count, bss_count=0;
    int prev_start, prev_end, next_start;
    gVars *tmp, *tmp_bss;

    tmp = all_data;
    count = 0;       //count number of variables has been scanned
    while(tmp != NULL)
    {
        prev_start = tmp->start;
        prev_end =  tmp->end;
        
        tmp = (gVars *)tmp->link;
   
        if (tmp != NULL)
            next_start = tmp->start;
        else 
            break;
        
        if(count==0 && prev_start!=0)        
        {
            bss = calloc(1, sizeof(gVars));
            tmp_bss = bss;    
            bss->start = 0;
            bss->end = prev_end-1;
        }

        else if (next_start == prev_end + 1)
        {
            count ++;
            continue;
        }

        else if (next_start != prev_end + 1)
        {
            if (bss != NULL)
            {
                bss->link = calloc(1, sizeof(gVars));
                bss = (gVars *)bss->link;
            }
            else
            {
                bss = calloc(1, sizeof(gVars));
                tmp_bss = bss;    
            }

            bss->start = prev_end+1;
            bss->end = tmp->start-1;
        }        

        num_vars = eval_num_vars(bss->start, bss->end);
        bss->variables = calloc(num_vars, sizeof (char *));
            
        for(i=0; i<num_vars; i++)
        {
            bss->variables[i] = calloc(3, sizeof(char));
            bss->variables[i] = "0x0";
	}
  
        bss->link = NULL;
        count++;
        bss_count++;
    }

    return bss_count;
}


/*******************************************************************
* Name: parser
* Input: 
*        char *file_name : input file name
* Output: int (1: output file successfully; 0: some error exists)
* Description: 
*        parse the assembly file compiled by LCC,
*        geting the data list and instruction list 
*        for the subsequent locating operation.
********************************************************************/
int parser(char *file_name)
{
    FILE *fp, *outFP;
    char *output_file_name;
    char getStringLine[STRING_LINE];
    int remove[MAX_ILLEGAL];        //store illegal instruction location in kernel by LCC
    int i, pivot, length, num_vars, type, count=0, data_count=0, instr_loc=-1;
    int bss_count=0, vars_count=0, instr_count=0, total_vars=0;
    char *token_array, *var_tmp;
    gVars *tmp = all_data;
    instrInfo *tmp_ins = all_instr;


    // open read file and error check
    fp = fopen(file_name, "r");
    if(fp == NULL)
    {
	fprintf(stderr, "input file error\n");
	return -1;
    }

	
    // open write file and error check
    output_file_name = strcat(file_name, ".s");
    outFP = fopen(output_file_name, "w");
    if(outFP == NULL)
    {
 	fprintf(stderr, "output file error\n");
	return -1;
    }

    //data manipulate and parse variables
    while( (fgets(getStringLine, STRING_LINE, fp) != NULL) )
    {
        type = judge_data(getStringLine);

        if (type == 0)          //normal instr.
        {
            fprintf(outFP, getStringLine);
            token_array = strtok(getStringLine, " ");
            if ((token_array = strtok(NULL, " ")) != NULL)  //judge if encounter label
            {
                instr_loc ++;
                text_size += 4;
            }
        }

        else if(type == 1)      //data
	{
            length = strlen(MEMINIT);  //length = 8
            pivot = length - 1;     //pivot = 7
            
            if (data_count == 0)  //first item
            {
                all_data = calloc(1, sizeof(gVars));                 
                tmp = all_data;
            }
            else
            {
                all_data->link = calloc(1, sizeof(gVars));
                all_data = (gVars *)all_data->link;                
            }        
              
	    token_array = strtok(getStringLine+pivot, "{},");
            
	    while(token_array!=NULL && token_array[0] != '\n' && token_array[0] != '=' && count != 2)
	    {
                count += 1;
                
                if (count==1)
                    all_data->start = HexToDec(token_array+2, strlen(token_array)-2);
                else if (count==2)
                    all_data->end = HexToDec(token_array+2, strlen(token_array)-2);

	    	token_array = strtok(NULL, "{},");
	    }
            
            num_vars = eval_num_vars(all_data->start, all_data->end);         //number of data in each global variable

            all_data->variables = calloc(num_vars, sizeof (char *));

	    token_array = strtok(NULL, "{},");
            
            i = 0;
	    while(token_array!=NULL && token_array[0] != '\n')
	    {
                length = strlen(token_array);
                var_tmp = all_data->variables[i] = calloc(length, sizeof(char));
                                
#ifdef DEBUG
    fprintf(stderr, "malloc_length = %d\n", length);
#endif

                strcpy(all_data->variables[i], token_array);
#ifdef DEBUG
    fprintf(stderr, "After_malloc_length = %d\n", all_data->variables[i]);
#endif
		token_array = strtok(NULL, "{},");
                i++;
            }
             
            all_data->link = NULL;  
            vars_count += num_vars;
            data_count += 1;
	    count = 0;
	}   //end type == 1     

        else if (type == 2)       //first line
        {
            length = strlen(MEMINITFUNC);  //length = 8
            token_array = strtok(getStringLine+length, ";");
            total_vars = atoi(token_array);
        }
        
        else if (type == 3)       //instr. reloc infos.
        {
            length = strlen(IMEM_RELOC);
            token_array = strtok(getStringLine+length, "()"); 

            pivot = atoi(token_array);   //relocation offset in IMEM_RELOC statement
            if(instr_count==0)
	    {
                all_instr = calloc(1, sizeof(instrInfo));
                tmp_ins = all_instr;
            }
	    else
	    {
	        all_instr->link = calloc(1, sizeof(instrInfo));
                all_instr = (instrInfo *)all_instr->link;                
            }             
                        
            for(i=0; pivot>remove[i] && remove[i]!=0; i++)
                pivot--;

            all_instr->location = pivot;
            all_instr->link = NULL;
            instr_count ++; 

#ifdef DEBUG
    fprintf(stderr, "RELOC offset: %d; instr_count = %d\n", pivot, instr_count);
#endif
        }  //end type == 3

        else if (type == 4)       //trap
        {
            remove[count] = instr_loc + 1;
            count ++; 
        }

    } //end while()
    all_data = tmp;   
    all_instr = tmp_ins;
     
    if (total_vars != vars_count)
        bss_count = separate_bss(all_data, data_count);

    fprintf(stderr, "  Output file: %s\n", output_file_name);

    fclose(fp);
    fclose(outFP);

    // rearrange to make bss followed by data
    rearrangeData();

#ifdef DEBUG
    fprintf(stderr, "text_size = %d bytes !\n", text_size);
    fprintf(stderr, "\ntotal_vars = %d\n", data_count);
    tmp = all_data;
    int j=0;
    for (i=0; i<data_count; i++, tmp = (gVars *)tmp->link)
    {
        fprintf(stderr, "Gdata[%d] :: start = %d, end = %d\n", i+1, tmp->start, tmp->end);
        num_vars = eval_num_vars(tmp->start, tmp->end);
        for (j=0; j<num_vars; j++)
        {
            fprintf(stderr, "strlen = %d\n", strlen(tmp->variables[j]));
            fprintf(stderr, "variables[%d] = %s\n", j, tmp->variables[j]);
        }
        printf("\n--------------\n");
    }
    printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");

    fprintf(stderr, "total_bss = %d\n", bss_count);
    tmp = bss;
    for (i=0; i<bss_count; i++, tmp = (gVars *)tmp->link)
    {
        fprintf(stderr, "BSS_data[%d] :: start = %d, end = %d\n", i+1, tmp->start, tmp->end);
        num_vars = eval_num_vars(tmp->start, tmp->end);
        for (j=0; j<num_vars; j++)
        {
            fprintf(stderr, "strlen = %d\n", strlen(tmp->variables[j]));
            fprintf(stderr, "variables[%d] = %s\n", j, tmp->variables[j]);
        }
        printf("\n--------------\n");
    }
    printf("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
#endif

    return 0;
}

/*******************************************************************
* Name: loader
* Input: 
* Output: int
* Description: 
*        determine where the task would be placed 
*        in five preallocated task area statically.
*        For static locator, determined by user. (this function)
*        For dynamic locator, determined by OS.
********************************************************************/
int loader()
{
    char input;    

    fprintf(stderr, "The following five addresses:\nTask 1 (%#xd)\nTask 2 (%#xd)\nTask 3 (%#xd)\nTask 4 (%#xd)\nTask 5 (%#xd)\nSelect which address would you placed the task ? ", Task1_Start, Task2_Start, Task3_Start, Task4_Start, Task5_Start);

    input = getc(stdin);

    switch(atoi(&input))
    {
        case 1:
            task_addr = Task1_Start;
            taskD_addr = Task1_Data;
            taskH_addr = Task1_Heap;
            taskS_addr = Task1_Stack;
            taskF_addr = Task1_Frame;
            break;
        case 2:
            task_addr = Task2_Start;
            taskD_addr = Task2_Data;
            taskH_addr = Task2_Heap;
            taskS_addr = Task2_Stack;
            taskF_addr = Task2_Frame;
            break;
        case 3:
            task_addr = Task3_Start;
            taskD_addr = Task3_Data;
            taskH_addr = Task3_Heap;
            taskS_addr = Task3_Stack;
            taskF_addr = Task3_Frame;
            break;
        case 4:
            task_addr = Task4_Start;
            taskD_addr = Task4_Data;
            taskH_addr = Task4_Heap;
            taskS_addr = Task4_Stack;
            taskF_addr = Task4_Frame;
            break;
        case 5:
            task_addr = Task5_Start;
            taskD_addr = Task5_Data;
            taskH_addr = Task5_Heap;
            taskS_addr = Task5_Stack;
            taskF_addr = Task5_Frame;
            break;
        default:
            fprintf(stderr, "Input error!!!\n");
            return -1;
            break;
    }

#ifdef DEBUG_A
    fprintf(stderr, "The task code would be placed into address[%c]: %#xd\n", input, task_addr);   
    fprintf(stderr, "The task data start address:\t %#xd\n", taskD_addr);   
    fprintf(stderr, "The task heap start address:\t %#xd\n", taskH_addr);   
    fprintf(stderr, "The task stack address:\t\t %#xd\n", taskS_addr);   
#endif
     
    return 0;
}


/*******************************************************************
* Name: adjust
* Input: int k
* Output: int new_k
* Description: 
*             find k in which bss/data variables,
*             and update k to new value.
********************************************************************/
int adjust(int k)
{
    int new_k = 0;
    gVars *tmpB = bss;    
    gVars *tmpD = all_data;    

    while(tmpB != NULL)
    {
        if (k == tmpB->start)
        {
            new_k = tmpB->new_start;            
            return new_k;
        }
        
        tmpB = (gVars *)tmpB->link;

        if(tmpB == NULL)
            break;
    }

    while(tmpD != NULL)
    {
        if (k == tmpD->start)
        {
            new_k = tmpD->new_start;            
            return new_k;
        }
        
        tmpD = (gVars *)tmpD->link;

        if(tmpD == NULL)
            break;
    }

    fprintf(stderr, "Error! Can't find the correct variables (bss/data) !");
    return -1;
}


/*******************************************************************
* Name: locator
* Input: 
*        char *file_name : input file name
* Output: int
* Description: 
*        determine where the task would be placed 
*        in five preallocated task area statically.
*        For static locator, determined by user. (this function)
*        For dynamic locator, determined by OS.
********************************************************************/
int locator(char *file_name)
{
    FILE *fp, *outfp;
    char getStringLine[STRING_LINE], getStringLine2[STRING_LINE];
    char line[STRING_LINE], line2[STRING_LINE];
    char *output_file_name;
    char *token1, *token2;
    int k=0, new_k=0;          //store address
    int i=0, offset=0, length=0;
    instrInfo *tmp_instr = all_instr;

    // open read file and error check
    fp = fopen(file_name, "r");
    if(fp == NULL)
    {
	fprintf(stderr, "input file error\n");
	return -1;
    }
	
    file_name = strtok(file_name, ".");

    // open write file and error check
    output_file_name = strcat(file_name, ".wos");   //output file WithOut Startup code (wos)
    outfp = fopen(output_file_name, "w");
    if(outfp == NULL)
    {
 	fprintf(stderr, "output file error\n");
	return -1;
    }

    //find the instr. offset and locate it to right address
    while( fgets(getStringLine, STRING_LINE, fp) != NULL )
    {
        strcpy(line, getStringLine);

        if(tmp_instr==NULL)
        {
            fprintf(outfp, "%s", line);            
            i++;
            continue;
        }
        else
            offset = tmp_instr->location;


        token1 = strtok(getStringLine, " ,");   //get predicate
        token1 = strtok(NULL, " ,");            //get instruction

        if (token1 == NULL)     //true, when encounter label
        {
            fprintf(outfp, "%s", line);
            continue;
        }      


        if(i!=offset)
            fprintf(outfp, "%s", line);            
        else
        {
            token1 = strtok(NULL, " ,");           //get register           
            token1 = strtok(NULL, " ,");           //get address
            
            k = (atoi(token1) & 0xFFFF)<<16;

#ifdef DEBUG
    fprintf(stderr, "1st k = %d\n", k);
#endif

            if ( fgets(getStringLine2, STRING_LINE, fp) == NULL )    //get next line
            {
                fprintf(stderr, "Locating file error!!!\n");
                return -1; 
            }                      

            strcpy(line2, getStringLine2);
            i++;               
            token2 = strtok(getStringLine2, " ,");  //get predicate
            token2 = strtok(NULL, " ,");           //get instruction
            token2 = strtok(NULL, " ,");           //get register 
            token2 = strtok(NULL, " ,");           //get address

            //k += (atoi(token2) & 0xFFFF) + taskD_addr;
            k += (atoi(token2) & 0xFFFF);            

            new_k = adjust(k) + taskD_addr;           
 
#ifdef DEBUG
    fprintf(stderr, "token2 = %s; i = %d\n", token2, i);
    fprintf(stderr, "original instruction k = %u\n", k);
    fprintf(stderr, "new instruction k = %u\n", adjust(k));
    fprintf(stderr, "new k(add taskD) = %u;  16bit:%#xd\n", new_k, new_k);    
#endif

            //start locating to correct physical address            
            length = strlen(line) - strlen(token1);
            *(line + length) = '\0';
            fprintf(outfp, "%s%d\n", line, new_k>>16);            

            length = strlen(line2) - strlen(token2);
            *(line2 + length) = '\0';
            fprintf(outfp, "%s%d\n", line2, (new_k&0xFFFF));            

            tmp_instr = (instrInfo *)tmp_instr->link;       
        }

        i++;
    }

    fclose(fp);
    fclose(outfp);

    fprintf(stderr, "  Output file: %s (without Startup Code)\n", output_file_name);
    return 0;
}

