
package cz.karry.dop.core;

import cz.karry.dop.Logger;
import cz.karry.dop.core.alu.Flags;
import cz.karry.dop.parser.MInstruction;

/**
 *
 * @author karry
 */
public class Controller implements BusListener{
    
    public final static String OP = "OP";
    public final static String OP0 = "OP0";
    public final static String OP1 = "OP1";
    public final static String OP2 = "OP2";
    public final static String OP3 = "OP3";
    
    private final Core core;
    private int ACM=0;
    //private int AMN=0;
    private int ANM=0;
    private MInstruction[] memory = new MInstruction[Features.MEMORY_SIZE];
    private int SC;
    private int MA;
    private Signal op3;
    private Bus op;
    private Signal op2;
    private Signal op1;
    private Signal op0;
    private cz.karry.dop.Logger logger;

    Controller(final Core core, Logger logger) {
        this.core = core;
        this.logger = logger;
        System.err.println("dop.core.Controller._init() is OF valid signal/flag?? If yes, pleas fix me!");
 

        try {
            core.getSignal(Core.RESET).addSignalListener(new SignalListener() {
                public void signalChangePerformed(Signal signal, boolean active) {
                    if (active){
                        ACM = 0;
                        ANM = 0;
                        MA = 0;
                        postClock();
                    }
                }
            });
            core.getSignal(Core.CLK).addSignalListener(new SignalListener() {
                public void signalChangePerformed(Signal signal, boolean active) {
                    if (active){ // rising edge of clock
                        //ACM = ANM;
                    }else{ // falink edge of clock

                    }
                }
            });
        } catch (Exception ex) {
            //Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            logger.addError(ex.getMessage());
        }
        
        op = new Bus(OP);
        op.addBusListener(this);
        op0 = new Signal(OP0);
        op1 = new Signal(OP1);
        op2 = new Signal(OP2);
        op3 = new Signal(OP3);
    }

    Controller(Core core, MInstruction[] memory, Logger logger) {
        this(core, logger);
        setMInstructionMemory(memory);
    }


    void postClock(){
        ACM = ANM;
        
        
        if (ACM<0 || ACM>Features.MEMORY_SIZE){
            System.err.println("Microinstruction address ("+ACM+") is out of range! (Max value is "+(Features.MEMORY_SIZE-1)+")");
            ACM = 0;
        }
        if (memory[ACM] == null){
            System.err.println("Microinstruction on address ("+ACM+") is null!");
            ACM = 0;
        }
        this.SC = memory[ACM].getConditionCode();
        this.MA = memory[ACM].getNextAddress();
        
        // clear all signals
        for (int i=0; i<Features.VALID_SIGNALS.length;i++){
            try {
                core.getSignal(Features.VALID_SIGNALS[i]).setState(false);
            } catch (Exception ex) {
                //Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        
        // clear BUS 
        core.bus.setValue(0xffff);
        
        // set Alu OP code
        op.setValue(memory[ACM].getAluOpCode());
        
        // set nested signals
        String[] activeSignals = memory[ACM].getSignals();
        for (int i=0; i<activeSignals.length ;i++){
            try {
                core.getSignal(activeSignals[i]).setState(true);
            } catch (Exception ex) {
                //Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                logger.addError(ex.getMessage());
            }
        }
        
        
        // set ANM
        ANM = MA;
        int wait = 0;
        int intSignal = 0;
        int lzero  = 0;        
        int ovf    = 0;
        int sf     = 0;
        int zf     = 0;
        int cf     = 0;
        int ifFlag = 0;
        int of     = 0;

        try{
            wait = core.getSignal( Core.WAIT ).isActive()?0:1;       // wait signal is active in logick zero
            intSignal = core.getSignal( Core.INT ).isActive()?1:0;                        

            lzero  = core.getSignal(Flags.LZERO_FLAG).isActive() ?1:0;                        
            ovf    = core.getSignal(Flags.OV_FLAG).isActive()?1:0;                        
            sf     = core.getSignal(Flags.S_FLAG).isActive()?1:0;                        
            zf     = core.getSignal(Flags.Z_FLAG).isActive()?1:0;                        
            cf     = core.getSignal(Flags.C_FLAG).isActive()?1:0;                        
            ifFlag = core.getSignal(Flags.I_FLAG).isActive()?1:0;                        
            of     = core.getSignal(Flags.OV_FLAG).isActive()?1:0;      // FIXME            
        } catch (Exception ex) {
            //Logger.getLogger(Memory.class.getName()).log(Level.SEVERE, null, ex);
            logger.addError(ex.getMessage());                            
        }

        try{
            switch( memory[ACM].getConditionCode() ){
                case 0: break;
                case 1: ANM = (ANM & 0xFF8) | ( (core.getRegisterValue( Core.IR_REG ) & 0xE0) >> 5 ); break;
                case 2: ANM = (ANM & 0xFF8) | ( (core.getRegisterValue( Core.IR_REG ) & 0x70) >> 4 ); break;
                case 3: ANM = (ANM & 0xFF8) | ( (core.getRegisterValue( Core.IR_REG ) & 0x1C) >> 2 ); break;
                case 4: ANM = (ANM & 0xFF8) | ( (core.getRegisterValue( Core.IR_REG ) & 0x0E) >> 1 ); break;

                case 5: ANM = (ANM & 0xFFC) | ( (core.getRegisterValue( Core.IR_REG ) & 0x03) >> 0 ); break;
                case 6: ANM = (ANM & 0xFFC) | ( (core.getRegisterValue( Core.IR_REG ) & 0x0C) >> 2 ); break;
                case 7: ANM = (ANM & 0xFFC) | ( (core.getRegisterValue( Core.IR_REG ) & 0x01) >> 0 ) | ( (core.getRegisterValue( Core.IR_REG ) & 0x10) >> 3 ); break;
                case 8: ANM = (ANM & 0xFFC) | ( (core.getRegisterValue( Core.IR_REG ) & 0x06) >> 1 ); break;

                case 9: ANM = (ANM & 0xFFE) | ( (core.getRegisterValue( Core.IR_REG ) & 0x10) >> 4 ); break;
                case 10: ANM = (ANM & 0xFFE) | wait; break;
                case 11: ANM = (ANM & 0xFFE) | ( (core.getRegisterValue( Core.IR_REG ) & 0x01) >> 0 ); break;
                case 12: ANM = (ANM & 0xFFE) | ( (core.getRegisterValue( Core.IR_REG ) & 0x04) >> 2 ); break;
                case 13: ANM = (ANM & 0xFFE) | ( (core.getRegisterValue( Core.IR_REG ) & 0x20) >> 5 ); break;
                case 14: ANM = (ANM & 0xFFE) | lzero; break;
                case 15: ANM = (ANM & 0xFFE) | ovf; break;
                case 16: ANM = (ANM & 0xFFE) | sf; break;
                case 17: ANM = (ANM & 0xFFE) | zf; break;
                case 18: ANM = (ANM & 0xFFE) | cf; break;
                case 19: ANM = (ANM & 0xFFE) | (sf ^ of); break;
                case 20: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ lzero); break;
                case 21: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ ovf); break;
                case 22: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ sf); break;
                case 23: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ zf); break;
                case 24: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ cf); break;
                case 25: ANM = (ANM & 0xFFE) | ((core.getRegisterValue( Core.IR_REG ) & 0x01) ^ sf ^ of); break;
                case 26: ANM = (ANM & 0xFFE) | intSignal; break;
                case 27: ANM = (ANM & 0xFFE) | ifFlag; break;
                default: 
                    System.err.println("Ilegal condition code for this version DOP.");
            }
        }catch(Exception ex){
            //Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
            logger.addError(ex.getMessage());            
        }
        
    
    }
    

    void preClock(){

    
    }
    
    public int getACM(){
        return ACM;
    }
    
    public int getANM(){
        return ANM;
    }
    
    public MInstruction getActualInstruction(){
        return memory[ACM];
    }
    
    public MInstruction getNextInstruction(){
        return memory[ANM];
    }
    
    public int getMA(){
        return MA;
    }

    public int getSC(){
        return this.SC;
    }

    public void setMInstructionMemory(MInstruction[] memory) {
        this.memory = memory;
    }

    public MInstruction[] getMInstructionMemory() {
        return memory;
    }

    public void busChangePerformed(Bus bus, int value) {
        if (bus == op){
            if ((value & 0x01) == 0x01){
                op0.setState(true);
            }else{
                op0.setState(false);
            }
            if ((value & 0x02) == 0x02){
                op1.setState(true);
            }else{
                op1.setState(false);
            }
            if ((value & 0x04) == 0x04){
                op2.setState(true);
            }else{
                op2.setState(false);
            }
            if ((value & 0x08) == 0x08){
                op3.setState(true);
            }else{
                op3.setState(false);
            }
        }
    }
    
    public Bus getOPBus() {
        return op;
    }        
    
    public Signal getOP0Signal() {
        return op0;
    }
        
    public Signal getOP1Signal() {
        return op1;
    }
        
    public Signal getOP2Signal() {
        return op2;
    }
        
    public Signal getOP3Signal() {
        return op3;
    }
        

}
