#include <vector>
#include <iostream>
#include "instructions.h"
#include "parameters.h"

void print_time(int x, int y, int z);

extern bool errorOccured;

instruction::instruction(string name_, int cline, table_instr *table){
    name = name_;
    instr_info infos = table->get(name);
    type = infos.type;
    exn = infos.exn;
    code = infos.code;
    line = cline;
    nb_int_arg = infos.nb_int_arg;
    arg[0] = 0;
    arg[1] = 0;
    arg[2] = 0;
}

void instruction::error(string msg){
    cerr << "Error in source file on line " << this->line << " : " << msg << endl;
    errorOccured = true;
}

/**
 * Execute an instruction
 * @return true if it enters in sleep mode
 **/
bool instruction::execute(int registers[18],vector<instruction>::iterator *it,vector<instruction>::iterator begin, int ram[SIZE_RAM], unsigned short input[NB_IO], unsigned short output[NB_IO], bool watch) {

int pos = (*it) - begin;

int operation = 0;          // result of operation
bool copy = true;           // true if need to copy result of operation into output register
bool sleep1 = false;

int &carry = registers[16];
int &zero = registers[17];

switch (code)
    {
    case 0:     operation = registers[arg[1]] + registers[arg[2]]; carry = (operation & 0x10000) ? 1 : 0;  break;
    case 1:     operation = registers[arg[1]] + registers[arg[2]] + carry; carry = (operation & 0x10000) ? 1 : 0;  break;
    case 2:     operation = registers[arg[1]] - registers[arg[2]]; carry = (registers[arg[1]]>=registers[arg[2]]) ? 1 : 0;  break;
    case 3:     operation = registers[arg[1]] - registers[arg[2]]; carry = ((unsigned short)registers[arg[1]]>=(unsigned short)registers[arg[2]]) ? 1 : 0;  break;
    case 4:     operation = registers[arg[1]] xor registers[arg[2]]; break;
    case 5:     operation = registers[arg[1]] or registers[arg[2]]; break;
    case 6:     operation = registers[arg[1]] and registers[arg[2]]; break;
    case 8:
            switch (exn)
            {
                case 0 : operation = registers[arg[1]]; break;
                case 1 : operation = not registers[arg[1]]; break;
                case 2 : carry = registers[arg[1]]&1 ; operation = (registers[arg[1]] >> 1) & 0x7fff; break;
		case 3 : carry = registers[arg[1]]&1 ; operation = ((registers[arg[1]] >> 1) & 0x7fff) | (registers[arg[1]] & 0x8000); break;
                case 4 : operation = ram[registers[arg[1]]]; break;
                case 5 : operation = input[arg[1]]; break;
                case 8 : sleep1=true; copy = false; break;
                default: cerr << "Error : unable to evaluate expression : " << name << endl; errorOccured = true; break;
            }
    break;
    case 9:
            switch (exn % 8)
            {
                case 0 : if(exn > 7)    operation = pos + 1;
                         else copy=false;
                         *it = begin + registers[arg[1]];
                         break;
                case 2 :
                         if(exn > 7)    operation = pos + 1;
                         else copy=false;
                         if (zero != 0) {
                            *it = begin + registers[arg[1]];
                        }else{
                            copy = false;
                            (*it)++;
                        }
                        break;
                case 4 :
                            if(exn > 7)    operation = pos + 1;
                            else copy=false;
                        if (carry != 0) {
                            *it = begin + registers[arg[1]];
                        }else{
                            (*it)++;
                        }
                        break;
                case 3 :
                            if(exn > 7)    operation = pos + 1;
                            else copy=false;
                        if (zero == 0) {
                            *it = begin + registers[arg[1]];
                        }else{
                            (*it)++;
                        }
                         break;
                case 5 :
                                if(exn > 7)    operation = pos + 1;
                                else copy=false;
                        if (carry == 0) {
                            *it = begin + registers[arg[1]];
                        }else{
                            (*it)++;
                        }
                        break;
                default: cerr << "Error : unable to evaluate expression : " << name << endl;errorOccured = true; break;
            }
    break;
    case 10:  operation = (registers[arg[0]] & 0x00ff) + ((arg[2]<<8) & 0xff00); break;
    case 11:  operation = (registers[arg[0]] & 0xff00) + (arg[2] & 0x00ff); break;
    case 12:  ram[registers[arg[2]]] = registers[arg[1]]; copy = false; break;
    case 13 : output[arg[1]] = registers[arg[2]];
              if(!watch){
                  cout << "Output " << arg[1] << " -> " << registers[arg[2]] << endl;
              }else if(arg[1]==0){
                  print_time((output[5]<<7) | output[4], (output[3]<<7) | output[2], (output[1]<<7) | output[0]);
              }
              copy = false; break;

    default: cerr << "Error : unable to evaluate expression : " << name << endl;errorOccured = true; break;
    }
    if (copy){
        registers[arg[0]] = operation & 0xffff;
	if(type!=jump){
		if(operation==0){
		    zero = 1;
		}else{
		    zero = 0;
		}
    	}
    }
    if(type!=jump){
        (*it)++;
    }
    return sleep1;
};

void instruction::print() {
    cout << line << " - " << name;
    int i;
    for (i=0;i<nb_int_arg;i++)  cout << " " << arg[i];
    cout << " " << s_arg;
    cout << endl;
};




void instruction::verif(table_label *labels,vector<instruction> *vect,vector<instruction>::iterator *it, bool verbose) {
    table_instr table;

    int dif = *it - (*vect).begin();
    if (verbose)        cout << dif << "/" << (*vect).end() - (*vect).begin() -1 << " : " << name;
    if (type == pseudo){
        if (verbose)    cout << " is a pseudoinstruction and will be replaced.";
        if (name == "la")  {
            name = "li"; nb_int_arg = 2;
            unsigned int laddress = (*labels).get(s_arg);
            arg[1] = laddress;
            s_arg = "";
        }
        else if (name == "li")  { // lhi and then lli

            unsigned int k = arg[1];
            name = "lhi"; type = ls; code = 10; exn = -1;
            arg[1] = k / 256;
		    instruction lli("lli", line, &table);
		    lli.arg[0] = arg[0];
		    lli.arg[1] = k % 256;
            (*vect).insert( *it+1, 1, lli );
        }
        else if (name == "addui" || name == "subui" || name == "subi" || name == "adduci")  {
            instruction op(name.substr(0,name.size()-1), line, &table);
            if(name=="addui" || name=="adduci"){
                op.arg[0] = arg[0]; op.arg[1] = arg[1]; op.arg[2] = 15;
                arg[1] = arg[2]; arg[0] = 15;
            }else{
                op.arg[0] = arg[0]; op.arg[1] = 15; op.arg[2] = arg[2];
                arg[1] = arg[1]; arg[0] = 15;
            }

            name = "li"; nb_int_arg = 2; type = pseudo;

            if (*it - (*vect).end() < -1)       (*vect).insert( (*it)+1, 1, op );
            else                                (*vect).push_back (op);
        }
        *it = (*vect).begin() + dif - 1;
     //   *it -= 1; // we only do one change at a time so we will have to come back
        // And so we immediately return !!!!
        if(verbose)     cout << endl;
        return;
    }
    if(verbose)     cout << endl;

// replace third int argument by exn
    if (exn >= 0)    arg[2] = exn;

// exchange arguments 1 and 2 fo lhi and lli instruction in order to ease the code computation
    if (name == "lhi" || name == "lli")  {
        nb_int_arg = 3;
        arg[2] = arg[1] & 0xff; arg[1] = 0xf & (arg[1]>>4);
    }

    if (name == "sh")  {
        nb_int_arg = 3;
        arg[2] = arg[0]; arg[0] = 0;
    }

    if (name == "output")  {
        nb_int_arg = 3;
        arg[2] = arg[1]; arg[1] = arg[0]; arg[0] = 0;
    }

// calculate instruction code
    instr_code = 4096*(0xf&code) + 256*(0xf&arg[0]) + 16*(0xf&arg[1]) + (0xf&arg[2]);
};
