
package cz.karry.dop.core.alu;

import cz.karry.dop.core.*;
import cz.karry.dop.core.registers.LRegister;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author karry
 */
public class Flags implements SignalListener{

    public static final String LZERO_FLAG = "LZERO";
    public static final String A_FLAG = "AF";
    public static final String Z_FLAG = "ZF";
    public static final String S_FLAG = "SF";
    public static final String I_FLAG = "IF";
    public static final String OV_FLAG = "OVF";
    public static final String C_FLAG = "CF";

    private Signal lzero;
    private Signal cf;
    private Signal af;
    private Signal zf;
    private Signal ovf;
    private Signal iFlag;
    private Signal sf;    
    
    private LRegister lRegister;
    private Bus bus;

    private Core core;
    
    private boolean afD;
    private boolean zfD;
    private boolean sfD;
    private boolean ifD;
    private boolean ovfD;
    private boolean cfD;

    public Flags(Core core){
        try {
            this.lRegister = (LRegister) core.getRegister(Core.L_REG);
            core.getSignal(Core.OEPSW).addSignalListener(this);
            core.getSignal(Core.CLK).addSignalListener(this);
            core.getSignal(Core.RESET).addSignalListener(this);
        } catch (Exception ex) {
             Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.core = core;
        this.bus = core.bus;
        
        lzero = new Signal( LZERO_FLAG );
        af = new Signal( A_FLAG );
        zf = new Signal(Z_FLAG);
        sf = new Signal(S_FLAG);
        iFlag = new Signal(I_FLAG);
        ovf = new Signal(OV_FLAG);
        cf = new Signal(C_FLAG);

        lzero.addSignalListener(this);
        af.addSignalListener(this);
        zf.addSignalListener(this);
        sf.addSignalListener(this);
        iFlag.addSignalListener(this);
        ovf.addSignalListener(this);
        cf.addSignalListener(this);
        

        try{
            ((LRegister)core.getRegister(Core.L_REG)).setLZeroSignal( lzero );

            core.addSignal(lzero);
            core.addSignal(af);
            core.addSignal(zf);
            core.addSignal(sf);
            core.addSignal(iFlag);
            core.addSignal(ovf);
            core.addSignal(cf);
        } catch (Exception ex) {
             Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }            
    }


    public void signalChangePerformed(Signal signal, boolean active) {
        try {
            if (core.getSignal(Core.OEPSW).isActive()){
                bus.setValue( (getAsValue() & 0xff00) | lRegister.getValue() | (bus.getValue() & 0x0900));                
            }
            if (core.getSignal(Core.OEAF).isActive()){
                if (af.isActive()){
                    bus.setValue( 0xffff );
                }else{
                    bus.setValue(0);
                }
            }
            
            if (signal.getName().equals(Core.CLK) ){
                boolean pefB = core.getSignal(Core.PEF).isActive();
                if (active && (pefB || core.getSignal(Core.ECF).isActive())){
                    // AF
                    if (pefB){
                        afD = (core.bus.getValue() & 0x8000) == 0x8000;         // BUS<15>
                    }else{
                        afD = core.getSignal(Alu.AUXF).isActive();              // wtf? BUS<15> and AUXF is the same signal :)
                    }
                    
                    // ZF
                    if (pefB){
                        zfD = (core.bus.getValue() & 0x0100) == 0x0100;         // BUS<9>
                    }else{
                        if (core.getSignal(Alu.CZM).isActive()){
                            zfD = core.getSignal(Alu.ZERO).isActive() && zf.isActive();
                        }else{
                            zfD = core.getSignal(Alu.ZERO).isActive();
                        }
                    }
                    
                    // SF
                    if (pefB){
                        sfD = (core.bus.getValue() & 0x0200) == 0x0200;
                    }else{
                        sfD = core.getSignal(Alu.SIGN).isActive();
                    }
                                        
                    // OVF
                    if (pefB){
                        ovfD = (core.bus.getValue() & 0x2000) == 0x2000;
                    }else{
                        ovfD = core.getSignal(Alu.OVERF).isActive();
                    }
                    
                    // CF
                    if (pefB){
                        cfD = (core.bus.getValue() & 0x4000) == 0x4000;
                    }else{
                        boolean cfs0B = core.getSignal(Core.CFS0).isActive();
                        boolean cfs1B = core.getSignal(Core.CFS1).isActive();
                        
                        if (cfs1B){
                            cfD = core.getSignal(Alu.W0).isActive();
                        }else{
                            if (cfs0B){
                                cfD = true;
                            }else{
                                cfD = core.getSignal(Alu.CY).isActive();
                            }
                        }
                        
                    }
                    
                }
                
                // IF
                if (active && pefB){
                    ifD = (core.bus.getValue() & 0x1000) == 0x1000;             // BUS<12>
                }
                
                if (!active){
                    af.setState(afD);
                    zf.setState(zfD);
                    sf.setState(sfD);
                    iFlag.setState(ifD);
                    ovf.setState(ovfD);
                    cf.setState(cfD);
                }
            }
        } catch (Exception ex) {
             Logger.getLogger(Core.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        if (signal.getName().equals(Core.RESET)){
            afD  = false;
            zfD  = false;
            sfD  = false;
            ifD  = false;
            ovfD = false;
            cfD  = false;
    
            af.setState(afD);
            zf.setState(zfD);
            sf.setState(sfD);
            iFlag.setState(ifD);
            ovf.setState(ovfD);
            cf.setState(cfD);            
        }
        
    }

    public int getAsValue() {
        int result = 0;
        result = af.isActive()    ? result | 0x8000 : result;
        result = cf.isActive()    ? result | 0x4000 : result;
        result = ovf.isActive()   ? result | 0x2000 : result;
        result = iFlag.isActive() ? result | 0x1000 : result;
     // result = get??() ? result | 0x0800 : result;
        result = sf.isActive()    ? result | 0x0400 : result;
        result = zf.isActive()    ? result | 0x0200 : result;
     // result = get??() ? result | 0x0100 : result;
        
        return result;
    }


}
