/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package targets.mips;

import common.Instruction;
import java.util.LinkedHashMap;

/**
 *
 * @author EUGENIO CARVALHO
 */
public class Descriprion {

    public static LinkedHashMap<String, Instruction> instOperators = new LinkedHashMap<String, Instruction>() {
        {
            put("+", new Instruction()
                    //        .Set("std", "add");
                    //        .Set("ime", "addi");
                    .Set("std", "addu")
                    .Set("ime", "addiu")
            );

            put("-", new Instruction()
                    //        .Set("std", "sub")
                    //        .Set("uns", "subu")
                    .Set("ime", "addiu")
                    .Set("std", "subu")
            );

            put("*", new Instruction()
                    //        .Set("std", "mult")
                    //        .Set("uns", "multu")
                    .Set("std", "mult"));

            put("/", new Instruction()
                    //        .Set("std", "div")
                    //        .Set("uns", "divu")
                    .Set("std", "divu")
            );
            put("%", new Instruction()
                    //        .Set("std", "div")
                    //        .Set("uns", "divu")
                    .Set("std", "divu")
            );

            put("&&", new Instruction()
                    .Set("std", "and")
                    .Set("ime", "andi")
                    .Set("assign", "and")
                    .Set("atribuicao_ime", "andi")
            );

            put("||", new Instruction()
                    .Set("std", "or")
                    .Set("ime", "ori")
                    .Set("assign", "or")
                    .Set("atribuicao_ime", "ori")
            );

            put("<", new Instruction()
                    .Set("std", "bltz")
                    .Set("assign", "sltu")
                    .Set("atribuicao_ime", "sltiu")
            );

            put(">", new Instruction()
                    .Set("std", "bgtz")
                    .Set("assign", "sgtu")
            );

            put("<=", new Instruction()
                    .Set("std", "blez")
                    .Set("assign", "sleu"));

            put(">=", new Instruction()
                    .Set("std", "bgez")
                    .Set("assign", "sgeu")
            );

            put("==", new Instruction()
                    .Set("std", "beq")
                    .Set("assign", "seq")
            );

            put("!=", new Instruction()
                    .Set("std", "bne")
                    .Set("assign", "sne")
            );

            put("<<", new Instruction()
                    .Set("std", "sllv")
                    .Set("ime", "sll")
            );
            put(">>", new Instruction()
                    .Set("std", "srlv")
                    .Set("ime", "srl")
            );
            put("|", new Instruction()
                    .Set("std", "or")
                    .Set("ime", "ori")
                    .Set("assign", "or")
                    .Set("atribuicao_ime", "ori")
            );
            put("^", new Instruction()
                    .Set("std", "xor")
                    .Set("ime", "xori")
                    .Set("assign", "xor")
                    .Set("atribuicao_ime", "xori")
            );
            put("&", new Instruction()
                    .Set("std", "and")
                    .Set("ime", "andi")
                    .Set("assign", "and")
                    .Set("atribuicao_ime", "andi")
            );
        }
    };
    public static LinkedHashMap<String, String> registers = new LinkedHashMap<String, String>() {
        {
            put("zero", "0");
            put("at", "1");
            put("v0", "2");
            put("v1", "3");
            put("a0", "4");
            put("a1", "5");
            put("a2", "6");
            put("a3", "7");
            put("t0", "8");
            put("t1", "9");
            put("t2", "10");
            put("t3", "11");
            put("t4", "12");
            put("t5", "13");
            put("t6", "14");
            put("t7", "15");
            put("s0", "16");
            put("s1", "17");
            put("s2", "18");
            put("s3", "19");
            put("s4", "20");
            put("s5", "21");
            put("s6", "22");
            put("s7", "23");
            put("t8", "24");
            put("t9", "25");
            put("k0", "26");
            put("k1", "27");
            put("gp", "28");
            put("sp", "29");
            put("fp", "30");
            put("ra", "31");
        }
    };
    public static LinkedHashMap<String, Instruction> Codops = new LinkedHashMap<String, Instruction>() {
        {
            put("nop", new Instruction("sll")
                    .Set("rs", "zero")
                    .Set("rt", "zero")
                    .Set("rd", "zero")
                    .Set("comment", "Nop"));

            put("stop", new Instruction()
                    .Set("type", "S")
                    .Set("inst", "stop")
                    .Set("codop", "111111")
                    .Set("txt", "11111111111111111111111111111111")
                    .Set("format", "S")
                    .Set("comment", "End of programa"));

            put("add", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "add")
                    .Set("codop", "000000")
                    .Set("func", "100000")
                    .Set("format", "R0"));

            put("addu", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "addu")
                    .Set("codop", "000000")
                    .Set("func", "100001")
                    .Set("format", "R0"));

            put("and", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "and")
                    .Set("codop", "000000")
                    .Set("func", "100100")
                    .Set("format", "R0"));

            put("div", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "div")
                    .Set("overflow", "mflo")
                    .Set("codop", "000000")
                    .Set("func", "011010")
                    .Set("format", "R1"));

            put("divu", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "divu")
                    .Set("/", "mflo")
                    .Set("%", "mfhi")
                    .Set("codop", "000000")
                    .Set("func", "011011")
                    .Set("format", "R1"));

            put("jr", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "jr")
                    .Set("codop", "000000")
                    .Set("func", "001000")
                    .Set("format", "R2"));

            put("mfhi", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "mfhi")
                    .Set("codop", "000000")
                    .Set("func", "010000")
                    .Set("format", "R5"));

            put("mflo", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "mflo")
                    .Set("codop", "000000")
                    .Set("func", "010010")
                    .Set("format", "R5"));

            put("mthi", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "mthi")
                    .Set("codop", "000000")
                    .Set("func", "010001")
                    .Set("format", "R5"));

            put("mtlo", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "mtlo")
                    .Set("codop", "000000")
                    .Set("func", "010011")
                    .Set("format", "R5"));

            put("mult", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "mult")
                    .Set("overflow", "mflo")
                    .Set("codop", "000000")
                    .Set("func", "011000")
                    .Set("format", "R1"));

            put("multu", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "multu")
                    .Set("overflow", "mflo")
                    .Set("codop", "000000")
                    .Set("func", "011001")
                    .Set("format", "R1"));

            put("nor", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "nor")
                    .Set("codop", "000000")
                    .Set("func", "100111")
                    .Set("format", "R0"));

            put("or", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "or")
                    .Set("codop", "000000")
                    .Set("func", "100101")
                    .Set("format", "R0"));

            put("sll", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "sll")
                    .Set("codop", "000000")
                    .Set("func", "000000")
                    .Set("rs", "zero")
                    .Set("rt", "zero")
                    .Set("rd", "zero")
                    .Set("offset", "0")
                    .Set("format", "R4"));

            put("slt", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "slt")
                    .Set("codop", "000000")
                    .Set("func", "101010")
                    .Set("format", "R0"));

            put("sra", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "sra")
                    .Set("codop", "000000")
                    .Set("func", "000011")
                    .Set("rs", "zero")
                    .Set("rt", "zero")
                    .Set("rd", "zero")
                    .Set("offset", "0")
                    .Set("format", "R4"));

            put("srl", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "srl")
                    .Set("codop", "000000")
                    .Set("func", "000010")
                    .Set("format", "R4"));

            put("sub", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "sub")
                    .Set("codop", "000000")
                    .Set("func", "100010")
                    .Set("format", "R0"));

            put("subu", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "subu")
                    .Set("codop", "000000")
                    .Set("func", "100011")
                    .Set("format", "R0"));

            put("xor", new Instruction()
                    .Set("type", "R")
                    .Set("sa", "0")
                    .Set("inst", "xor")
                    .Set("codop", "000000")
                    .Set("func", "100110")
                    .Set("format", "R0"));
            //
            // I instructions
            //

            put("addi", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "addi")
                    .Set("codop", "001000")
                    .Set("format", "I0"));

            put("addiu", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "addiu")
                    .Set("codop", "001001")
                    .Set("format", "I0"));

            put("andi", new Instruction() //r<0
                    .Set("type", "I")
                    .Set("inst", "andi")
                    .Set("codop", "001100")
                    .Set("format", "I0"));

            put("beq", new Instruction()//r = Set
                    .Set("type", "I")
                    .Set("inst", "beq")
                    .Set("codop", "000100")
                    .Set("format", "I0"));

            put("bgez", new Instruction()//r >= 0
                    .Set("type", "I")
                    .Set("inst", "bgez")
                    .Set("codop", "000001")
                    .Set("rt", "00001")
                    .Set("format", "I0"));

            put("bgtz", new Instruction()//r>0
                    .Set("type", "I")
                    .Set("inst", "bgtz")
                    .Set("codop", "000111")
                    .Set("rt", "zero")
                    .Set("format", "I0"));

            put("blez", new Instruction()//r<=0
                    .Set("type", "I")
                    .Set("inst", "blez")
                    .Set("codop", "000110")
                    .Set("rt", "zero")
                    .Set("format", "I0"));

            put("bltz", new Instruction()//r<0
                    .Set("type", "I")
                    .Set("inst", "bltz")
                    .Set("codop", "000001")
                    .Set("rt", "00000")
                    .Set("format", "I0"));

            put("bne", new Instruction()//r != Set
                    .Set("type", "I")
                    .Set("inst", "bne")
                    .Set("codop", "000101")
                    .Set("format", "I0"));

            put("lb", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lb")
                    .Set("codop", "100000c")
                    .Set("format", "I2"));

            put("lbu", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lbu")
                    .Set("codop", "100100")
                    .Set("format", "I2"));

            put("lh", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lh")
                    .Set("codop", "100001")
                    .Set("format", "I2"));

            put("lhu", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lhu")
                    .Set("codop", "100101")
                    .Set("format", "I2"));

            put("lui", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lui")
                    .Set("codop", "001111")
                    .Set("format", "I0"));

            put("lw", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "lw")
                    .Set("codop", "100011")
                    .Set("format", "I0"));

            put("ori", new Instruction() //r<0
                    .Set("type", "I")
                    .Set("inst", "ori")
                    .Set("codop", "001101")
                    .Set("format", "I1"));

            put("slti", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "slti")
                    .Set("codop", "001010")
                    .Set("format", "I0"));

            put("sltiu", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "sltiu")
                    .Set("codop", "001011")
                    .Set("format", "I2"));

            put("sltiu", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "sltiu")
                    .Set("codop", "001011")
                    .Set("format", "I0"));

            put("sw", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "sw")
                    .Set("codop", "101011")
                    .Set("format", "I0"));

            put("sb", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "sb")
                    .Set("codop", "101000")
                    .Set("format", "I0"));

            put("xori", new Instruction()
                    .Set("type", "I")
                    .Set("inst", "xori")
                    .Set("codop", "001110")
                    .Set("format", "I0"));
            //
            // J instructions
            //
            put("j", new Instruction()
                    .Set("type", "J")
                    .Set("inst", "j")
                    .Set("codop", "000010")
                    .Set("format", "J"));

            put("jal", new Instruction()
                    .Set("type", "J")
                    .Set("inst", "jal")
                    .Set("codop", "000011")
                    .Set("format", "J"));
            //
            // 
            //
        }
    };

    public static Instruction Instruction(String inst) throws Exception {
        if (!Codops.containsKey(inst)) {
            throw new Exception(String.format("Instruction '%s' not defined", inst));
        }
        return Codops.get(inst).copy();
    }

    public static String InstructionByOperator(Instruction inst) throws Exception {
        Instruction r = null;
        String sentence = inst.Get("type"), type = "std";
        String op = inst.Get("op");

        if (!op.equals("")) { // Se é um expr
            if (!instOperators.containsKey(op)) {
                throw new Exception("Operador {" + op + "} não definido.");
            }
            r = instOperators.get(op);

            if (r.Has("ime")
                    && (inst.eq("p1value", "true") || inst.eq("p2value", "true"))) {
                type = "ime";
            }
        }
        if (r == null) {
            throw new Exception(String.format("Sentenca '%s' não definida", sentence));
        }
        return r.Get(type);
    }

    public static String Register(String reg) {
        return registers.get(reg);
    }

}
