
package cz.karry.dop.core;

import cz.karry.dop.core.alu.Alu;
import cz.karry.dop.core.alu.Flags;
import cz.karry.dop.core.registers.IORegister;
import cz.karry.dop.core.registers.IrRegister;
import cz.karry.dop.core.registers.LRegister;
import cz.karry.dop.core.registers.Register;
import cz.karry.dop.core.registers.UpDownRegister;
import cz.karry.dop.core.registers.UpRegister;
import cz.karry.dop.parser.MInstruction;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author karry
 */
public class Core {
    
    
    // signal names
    public static final String CLK = "CLK";
    public static final String RESET = "RESET";
    public static final String WAIT = "WAIT";
    public static final String INT = "INT";
    
    public static final String CFS0 = "CFS0";
    public static final String CFS1 = "CFS1";
    public static final String CI0 = "CI0";
    public static final String ECD = "ECD";
    public static final String ECF = "ECF";
    public static final String ECINH = "ECINH";
    public static final String ECINL = "ECINL";
    public static final String ECIR = "ECIR";
    public static final String ECL = "ECL";
    public static final String ECOUTWR = "ECOUTWR";
    public static final String ECPC = "ECPC";
    public static final String ECS = "ECS";
    public static final String ECSP = "ECSP";
    public static final String ECW = "ECW";
    public static final String INTA = "INTA";
    public static final String LH = "LH";
    public static final String MRD = "MRD";
    public static final String MWR = "MWR";
    public static final String OEAB = "OEAB";
    public static final String OEAF = "OEAF";
    public static final String OED = "OED";
    public static final String OEINLH = "OEINLH";
    public static final String OEINSE = "OEINSE";
    public static final String OEINZE = "OEINZE";
    public static final String OEIT = "OEIT";
    public static final String OEL = "OEL";
    public static final String OEPC = "OEPC";
    public static final String OEPSW = "OEPSW";
    public static final String OES = "OES";
    public static final String OESP = "OESP";
    public static final String OET = "OET";
    public static final String OEU = "OEU";
    public static final String OEW = "OEW";
    public static final String OEWR = "OEWR";
    public static final String PED = "PED";
    public static final String PEF = "PEF";
    public static final String PEL = "PEL";
    public static final String PEPC = "PEPC";
    public static final String PES = "PES";
    public static final String PESP = "PESP";
    public static final String PET = "PET";
    public static final String PEU = "PEU";
    public static final String SWW = "SWW";
    public static final String UCF = "UCF";
    public static final String UDD = "UDD";
    public static final String UDSP = "UDSP";

    
    // register names
    public static final String IR_REG = "IR";
    public static final String S_REG  = "S";
    public static final String U_REG  = "U";
    public static final String T_REG  = "T";
    public static final String PC_REG = "PC";
    public static final String L_REG  = "L";
    public static final String SP_REG = "SP";
    public static final String D_REG  = "D";

    // bus names
    public static final String BUS  = "BUS";
    public static final String AB_BUS  = "AB";
    public static final String DB_BUS  = "DB";
    
    /*
    public static final Signal LOG1 = new Signal("LOG1");
    public static final Signal LOG0 = new Signal("LOG0");
    
    static{
        LOG1.setState(true);     
    }
     */
    
    private ArrayList<Signal> signals = new ArrayList<Signal>();
    private ArrayList<Register> registers = new ArrayList<Register>();
    private ArrayList<MInstructionComplete> mIClisteners = new ArrayList<MInstructionComplete>();

    public Bus bus = new Bus(BUS);
    public Bus ab_bus = new Bus(AB_BUS,0xffff);
    public Bus db_bus = new Bus(DB_BUS,0xff);
    private Controller controller;
    private Alu alu;
    private MemoryInterface memInterface;
    private cz.karry.dop.Logger logger;
    private Memory memory;

    public Core(cz.karry.dop.Logger logger) {
        this(new MInstruction[Features.MEMORY_SIZE], logger);
    }
    
    public Core(MInstruction[] memory, cz.karry.dop.Logger logger){
        
        this.logger = logger;
        
        try {
            for (int i=0; i<Features.VALID_SIGNALS.length;i++){
                addSignal(new Signal(Features.VALID_SIGNALS[i]));
            }
            
            addSignal(new Signal(CLK));
            addSignal(new Signal(RESET));
            addSignal(new Signal(WAIT));
            addSignal(new Signal(INT));

            registers.add( new IrRegister(  IR_REG,db_bus,bus, getSignal(RESET), getSignal(CLK), getSignal(ECIR), getSignal(OEIT) ) );
            registers.add( new IORegister(    U_REG,  bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PEU),  getSignal(OEU) ) );
            registers.add( new IORegister(    T_REG,  bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PET),  getSignal(OET) ) );
            registers.add( new LRegister(     L_REG,  bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PEL),  getSignal(OEL),  getSignal(ECL) ) );
            registers.add( new UpRegister(    PC_REG, bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PEPC), getSignal(OEPC), getSignal(ECPC)));
            registers.add( new UpRegister(    S_REG,  bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PES),  getSignal(OES),  getSignal(ECS)));
            registers.add( new UpDownRegister(SP_REG, bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PESP), getSignal(OESP), getSignal(ECSP), getSignal(UDSP) ) );
            registers.add( new UpDownRegister(D_REG,  bus,bus, getSignal(RESET), getSignal(CLK), getSignal(PED),  getSignal(OED),  getSignal(ECD),  getSignal(UDD) ) );
        } catch (Exception ex) {
            Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        this.controller = new Controller(this,memory,logger);
        this.alu = new Alu(this);
        this.memInterface = new MemoryInterface(this);
    }

    public void addMInstructionCompleteListener(MInstructionComplete listener) {
        mIClisteners.add(listener);
    }
    
//    Core(cz.karry.dop.core.Memory memory){
//        this();
//        memory.connectCore(this);
//    }
    
    public void setMemory(cz.karry.dop.core.Memory memory){
        if (this.memory != null){
            this.memory.disconnectCore();
        }
        this.memory = memory;
        memory.connectCore(this);
    }
    
    public void reset(){
        try {
            getSignal(RESET).setState(true);
            getSignal(RESET).setState(false);
        } catch (Exception ex) {
            Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }
        //clock();
        //this.controller.preClock();
        sendMInstructionComplete();
    }
    
    
    public void clock(){
        this.controller.preClock();
        try {
            getSignal(CLK).setState(true);
            getSignal(CLK).setState(false);
        } catch (Exception ex) {
            Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }    
        this.controller.postClock();
        sendMInstructionComplete();
    }

    public void setMiMemory(MInstruction[] memory) {
        this.controller.setMInstructionMemory(memory);
    }
    
    private void sendMInstructionComplete(){
        for (int i=0; i< mIClisteners.size(); i++){
            mIClisteners.get(i).mInstructionComplete();
        }        
    }
    
    public Signal getSignal(String name) throws Exception{
        for (int i=0; i< signals.size(); i++){
            if (signals.get(i).getName().equals(name))
                return signals.get(i);
        }
        throw new Exception("Signal \""+name+"\" not defined.");
    }
    
    public Controller getController(){
        return controller;
    }

    public int getRegisterValue(String name) throws Exception{
        return getRegister(name).getValue();
    }
    
    public Register getRegister(String name) throws Exception{
        for (int i=0; i< registers.size(); i++){
            if (registers.get(i).getName().equals(name))
                return registers.get(i);
        }
        throw new Exception("Register \""+name+"\" not defined.");
    }
    

    public Flags getFlags() {
        return this.alu.getFlags();
    }
    
    public Alu getAlu() {
        return this.alu;
    }
    
    public MemoryInterface getMemoryInterface(){
        return this.memInterface;
    }
    
    /*
    public ArrayList<Signal> getSignals(){
        return signals;
    }
     */
    
    public ArrayList<Register> getRegisters(){
        return registers;
    }
    
    public void addSignal(Signal signal) throws Exception{
        Signal exist = null;
        try {
            exist = getSignal(signal.getName());
        } catch (Exception ex) {}
        if (exist != null){
            throw new Exception("Signal is \""+signal.getName()+"\" already defined.");
        }
        signals.add(signal);
    }
}
