/*
 * Giovanni Agosta, Andrea Di Biagio
 * Politecnico di Milano, 2007
 * 
 * main.c
 * Formal Languages & Compilers Machine, 2007/2008
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "fetch.h"
#include "machine.h"

int main(int argc, char **argv)
{
	FILE *fp;       		/* puntatore al file oggetto    */
	int len=0;      		/* lunghezza del file oggetto   */
	unsigned int *code=NULL;	/* puntatore al codice caricato */
	int lcode = 0;  		/* lunghezza del codice         */
	int mmgmt=BASIC;		/* memory management - segmenti */
	int i;
	int breakat=-1; 		/* ferma l'esecuzione dopo N istruzioni */
	int count=0;    		/* numero di istruzioni eseguite        */
	pc=0;           		/* resetta il valore del program counter */
	
	/* Apertura del file oggetto */
	if (argc<2)
	{
		/* L'esecuzione termina poichè non è stato specificato il file
		 * oggetto come argomento */
		printf(	"Formal Languages & Compilers Machine, 2007/2008.\n"
			"\n\nSyntax:\n\tmace [options] objectfile\n");
	  	return NOARGS;
	}
	
	/* apre il file oggetto specificato come argomento */
  	fp = fopen(argv[argc-1],"r");
	if (fp == NULL)
	{
		#ifdef DEBUG
		fprintf(stderr,"Object file doesn't exist.\n");
		#endif
		
		/* termina l'esecuzione in quanto è stato specificato un file
		 * oggetto inesistente */
		return NOFILE;
	}
	
	for(i=1; i<argc-1; i++)
	{
		if (strcmp(argv[i],"segmented")==0) 
			mmgmt = SEGMENTED;
		else if (strcmp(argv[i],"break")==0)
		{
			char *error;
			
			/* legge il prossimo argomento come numero in base 10 */
			breakat = strtol(argv[i+1],&error,10);
			
			if (*error != '\0')
			{
				/* errore nella lettura dell'argomento */
				#ifdef DEBUG
				fprintf(stderr,"Error while reading option "
					"break: %d=%c, string=%s "
					"=> %d.\n",*error,*error,argv[i+1],breakat);
				#endif
				return WRONG_ARGS;
      			}
			i++; /* salta l'argomento gia' letto */
		}
	}

	#ifdef DEBUG
	fprintf(stderr,"Running with memory %d\n", mmgmt);
	#endif
	
	/* Resetta i registri e la memoria */
	for (i=0; i<NREGS; i++) reg[i] = 0;
	for (i=0; i<MEMSIZE; i++) mem[i] = 0;
	
	/* Calcola lunghezza del file */
	fseek(fp,0,SEEK_END);
	len = ftell(fp);
	fseek(fp,0,SEEK_SET);
	
	if (mmgmt==BASIC)
	{
		/* Usa una sola area di memoria per codice e dati */
		if (len>MEMSIZE)
		{
			#ifdef DEBUG
			fprintf(stderr,"out of memory.\n");
			#endif
	
			return MEM_FAULT;
		}
    		
		code = (unsigned int*)mem;
	} else
	{
		/* Alloca spazio in memoria per il codice */
		code = (unsigned int*) malloc(sizeof(unsigned int)*len);
	}

	#ifdef DEBUG
	fprintf(stderr,"Begin memory dump\n");
	#endif
  
	/* Carica il codice macchina in memoria   */
	for (i=0; i<len; i=i+2)
	{
		if (i==0)
      {
         unsigned int c1,c2,c3,c4;
         c1 = fgetc(fp);
         c2 = fgetc(fp);
         c3 = fgetc(fp);
         c4 = fgetc(fp);

         if (c1!='L' && c2 != 'F' && c3!='C' && c4!='M')
		   {
			   #ifdef DEBUG
			   fprintf(stderr,"Wrong object file format.\n");
			   #endif
		  
			   return WRONG_FORMAT;
		   }
         else
         {
            int value[4];
            int found;
            int count;

            /* initialize the local variables */
            count = 0;
            found = 0;
            memset(value, 0, 4);

            while (count < 4)
            {
               found += fread(value + count, 4, 1, fp);

               /* verify the header format */
               if (value[count] != 0)
               {
                  #ifdef DEBUG
                  fprintf(stderr,"Wrong object file format.\n");
                  #endif
        
                  return WRONG_FORMAT;
               }
               count ++;
            }
          }
      }
		
		if (!fread(code + lcode, 4, 1, fp))
         break;
		
		#ifdef DEBUG
		fprintf(stderr,"%d\t", code[lcode]);
		#endif
		
		lcode++;
	}
	
	#ifdef DEBUG
	fprintf(stderr,"\nEnd memory dump\n");
   #endif

	/* decodifica ed esegue il codice */
	for (pc=0; pc<lcode && pc>=0; )
	{
		pc = fetch_execute(code,pc);

		reg[0]=0; /* reset R0 to 0; R0 is wired to 0, so we ignore all writes */
		#ifdef DEBUG
		print_regs(stderr);
		fprintf(stderr,"C=%d, O=%d, N=%d, Z=%d\n", getflag(CARRY), getflag(OVERFLOW), getflag(NEGATIVE), getflag(ZERO));
		//print_stack(stderr);
		#endif
		
		count++; /* conta le istruzioni eseguite */
		if ((breakat>0) && (breakat<=count))
		{
			#ifdef DEBUG
			fprintf(stderr,"Break after %d instructions.\n",count);
			#endif
			return BREAK;
		}
		
		/* Check the HALT condition */
		if (pc == _HALT)
			return OK;
	}
	
	#ifdef DEBUG
	fprintf(stderr,"Memory access error.\n");
	#endif
	
	return pc;
}
