/*
    JPC: A x86 PC Hardware Emulator for a pure Java Virtual Machine
    Release Version 2.0

    A project from the Physics Dept, The University of Oxford

    Copyright (C) 2007 Isis Innovation Limited

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License version 2 as published by
    the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
    Details (including contact information) can be found at: 

    www.physics.ox.ac.uk/jpc
*/

package org.jpc.emulator.processor;

import org.jpc.emulator.*;
import org.jpc.emulator.motherboard.*;
import org.jpc.emulator.memory.*;
import org.jpc.emulator.processor.fpu64.*;
import org.jpc.support.*;
import java.io.*;
import java.util.*;

public class Processor implements HardwareComponent
{
    public static final int STATE_VERSION = 1;
    public static final int STATE_MINOR_VERSION = 0;

    public static final int CLOCK_SPEED = 50; //CPU "Clock Speed" in MHz

    public static final int IFLAGS_HARDWARE_INTERRUPT = 0x1;
    public static final int IFLAGS_PROCESSOR_EXCEPTION = 0x2;
    public static final int IFLAGS_RESET_REQUEST = 0x4; 

    public static final int PROC_EXCEPTION_DE = 0x00; // Divide Error
    public static final int PROC_EXCEPTION_DB = 0x01; // Debug
    public static final int PROC_EXCEPTION_BP = 0x03; // Breakpoint
    public static final int PROC_EXCEPTION_OF = 0x04; // Overflow
    public static final int PROC_EXCEPTION_BR = 0x05; // BOUND Range Exceeded
    public static final int PROC_EXCEPTION_UD = 0x06; // Invalid Opcode (UnDefined)
    public static final int PROC_EXCEPTION_NM = 0x07; // Device Not Available (No Math Coprocessor)
    public static final int PROC_EXCEPTION_DF = 0x08; // Double Fault
    public static final int PROC_EXCEPTION_MF_09 = 0x09; // Coprocessor Segment Overrun
    public static final int PROC_EXCEPTION_TS = 0x0a; // Invalid TSS
    public static final int PROC_EXCEPTION_NP = 0x0b; // Segment Not Present
    public static final int PROC_EXCEPTION_SS = 0x0c; // Stack Segment Fault
    public static final int PROC_EXCEPTION_GP = 0x0d; // General Protection
    public static final int PROC_EXCEPTION_PF = 0x0e; // Page Fault
    public static final int PROC_EXCEPTION_MF_10 = 0x10; // Floating-Point Error
    public static final int PROC_EXCEPTION_AC = 0x11; // Alignment Check
    public static final int PROC_EXCEPTION_MC = 0x12; // Machine Check
    public static final int PROC_EXCEPTION_XF = 0x13; // SIMD Floating-Point Error
    public static final int PROC_EXCEPTION_MAX = 0x13; // Maximum exception vector value

    public static final int CR0_PROTECTION_ENABLE = 0x1;
    public static final int CR0_MONITOR_COPROCESSOR = 0x2;
    public static final int CR0_FPU_EMULATION = 0x4;
    public static final int CR0_TASK_SWITCHED = 0x8;
    public static final int CR0_NUMERIC_ERROR = 0x20;
    public static final int CR0_WRITE_PROTECT = 0x10000;
    public static final int CR0_ALIGNMENT_MASK = 0x40000;
    public static final int CR0_NOT_WRITETHROUGH = 0x20000000;
    public static final int CR0_CACHE_DISABLE = 0x40000000;
    public static final int CR0_PAGING = 0x80000000;

    public static final int CR3_PAGE_CACHE_DISABLE = 0x10;
    public static final int CR3_PAGE_WRITES_TRANSPARENT = 0x8;

    public static final int CR4_VIRTUAL8086_MODE_EXTENSIONS = 0x1;
    public static final int CR4_PROTECTED_MODE_VIRTUAL_INTERRUPTS = 0x2;
    public static final int CR4_TIME_STAMP_DISABLE = 0x4;
    public static final int CR4_DEBUGGING_EXTENSIONS = 0x8;
    public static final int CR4_PAGE_SIZE_EXTENSIONS = 0x10;
    public static final int CR4_PHYSICAL_ADDRESS_EXTENSION = 0x20;
    public static final int CR4_MACHINE_CHECK_ENABLE = 0x40;
    public static final int CR4_PAGE_GLOBAL_ENABLE = 0x80;
    public static final int CR4_PERFORMANCE_MONITORING_COUNTER_ENABLE = 0x100;
    public static final int CR4_OS_SUPPORT_FXSAVE_FXSTORE = 0x200;
    public static final int CR4_OS_SUPPORT_UNMASKED_SIMD_EXCEPTIONS = 0x400;

    public static final int SYSENTER_CS_MSR = 0x174;
    public static final int SYSENTER_ESP_MSR = 0x175;
    public static final int SYSENTER_EIP_MSR = 0x176;

    public int eax, ebx, edx, ecx;
    public int esi, edi, esp, ebp;
    public int eip;

    private int cr0, cr1, cr2, cr3, cr4;
    public int dr0, dr1, dr2, dr3, dr4, dr5, dr6, dr7;

    public Segment cs, ds, ss, es, fs, gs;
    public Segment idtr, gdtr, ldtr, tss;

    //protected int eflags;
    //program status and control register
    public boolean eflagsCarry; //done
    public boolean eflagsParity; //done
    public boolean eflagsAuxiliaryCarry; //done
    public boolean eflagsZero; //to do
    public boolean eflagsSign; //to do
    public boolean eflagsTrap;
    public boolean eflagsInterruptEnable;
    public boolean eflagsDirection;
    public boolean eflagsOverflow; //done
    public int     eflagsIOPrivilegeLevel;
    public boolean eflagsNestedTask;
    public boolean eflagsResume;
    public boolean eflagsVirtual8086Mode;
    public boolean eflagsAlignmentCheck;
    public boolean eflagsVirtualInterrupt;
    public boolean eflagsVirtualInterruptPending;
    public boolean eflagsID;
    public boolean eflagsInterruptEnableSoon;

    public LinearAddressSpace linearMemory;
    public PhysicalAddressSpace physicalMemory;
    public AlignmentCheckedAddressSpace alignmentCheckedMemory;
    public IOPortHandler ioports;

    private int interruptFlags;
    private InterruptController interruptController;
    private Clock virtualClock;
    private boolean alignmentChecking;

    private Hashtable modelSpecificRegisters;

    private long resetTime;
    private int currentPrivilegeLevel;

    public FpuState fpu;

    public Processor()
    {
	fpu = new FpuState64(this);
        linearMemory = null;
	physicalMemory = null;
        alignmentCheckedMemory = null;
	ioports = null;
        alignmentChecking = false;
	modelSpecificRegisters = new Hashtable();
    }

    public Processor(Processor p)
    {
        this.eax = p.eax;
        this.ebx = p.ebx;
        this.edx = p.edx;
        this.ecx = p.ecx;
        this.esi = p.esi;
        this.edi = p.edi;
        this.esp = p.esp;
        this.ebp = p.ebp;
        this.eip = p.eip;

//         private int cr0, cr1, cr2, cr3, cr4;

        this.dr0 = p.dr0;
        this.dr1 = p.dr1;
        this.dr2 = p.dr2;
        this.dr3 = p.dr3;
        this.dr4 = p.dr3;
        this.dr5 = p.dr5;
        this.dr6 = p.dr6;
        this.dr7 = p.dr7;

//         public Segment cs, ds, ss, es, fs, gs;
//         public Segment idtr, gdtr, ldtr, tss;
        this.cs = p.cs;
        this.ds = p.ds;
        this.ss = p.ss;
        this.es = p.es;
        this.fs = p.fs;
        this.gs = p.gs;
        this.idtr = p.idtr;
        this.gdtr = p.gdtr;
        this.ldtr = p.ldtr;
        this.tss = p.tss;

        this.eflagsCarry = p.eflagsCarry;
        this.eflagsParity = p.eflagsParity;
        this.eflagsAuxiliaryCarry = p.eflagsAuxiliaryCarry;
        this.eflagsZero = p.eflagsZero;
        this.eflagsSign = p.eflagsSign;
        this.eflagsTrap = p.eflagsTrap;
        this.eflagsInterruptEnable = p.eflagsInterruptEnable;
        this.eflagsDirection = p.eflagsDirection;
        this.eflagsOverflow = p.eflagsOverflow;
        this.eflagsIOPrivilegeLevel = p.eflagsIOPrivilegeLevel;
        this.eflagsNestedTask = p.eflagsNestedTask;
        this.eflagsResume = p.eflagsResume;
        this.eflagsVirtual8086Mode = p.eflagsVirtual8086Mode;
        this.eflagsAlignmentCheck = p.eflagsAlignmentCheck;
        this.eflagsVirtualInterrupt = p.eflagsVirtualInterrupt;
        this.eflagsVirtualInterruptPending = p.eflagsVirtualInterruptPending;
        this.eflagsID = p.eflagsID;
        this.eflagsInterruptEnableSoon = p.eflagsInterruptEnableSoon;
        
//         public LinearAddressSpace linearMemory;
//         public PhysicalAddressSpace physicalMemory;
//         public AlignmentCheckedAddressSpace alignmentCheckedMemory;
//         public IOPortHandler ioports;
        this.linearMemory = p.linearMemory;
        this.physicalMemory = p.physicalMemory;
        this.alignmentCheckedMemory = p.alignmentCheckedMemory;
        this.ioports = p.ioports;

//         private.interruptFlags;
//         private InterruptController interruptController;
//         private Clock virtualClock;
//         private boolean alignmentChecking;

//         private Hashtable modelSpecificRegisters;
//         private long resetTime;
//         private.currentPrivilegeLevel;
        
//         this.FpuState fpu;
        this.fpu = p.fpu;
    }

    public boolean equals(Processor p)
    {
        if ((this.eax == p.eax) &&
            (this.ebx == p.ebx) &&
            (this.edx == p.edx) &&
            (this.ecx == p.ecx) &&
            (this.esi == p.esi) &&
            (this.edi == p.edi) &&
            (this.esp == p.esp) &&
            (this.ebp == p.ebp) &&
            (this.eip == p.eip) &&
            (this.dr0 == p.dr0) &&
            (this.dr1 == p.dr1) &&
            (this.dr2 == p.dr2) &&
            (this.dr3 == p.dr3) &&
            (this.dr4 == p.dr3) &&
            (this.dr5 == p.dr5) &&
            (this.dr6 == p.dr6) &&
            (this.dr7 == p.dr7) &&
            (this.cs == p.cs) &&
            (this.ds == p.ds) &&
            (this.ss == p.ss) &&
            (this.es == p.es) &&
            (this.fs == p.fs) &&
            (this.gs == p.gs) &&
            (this.idtr == p.idtr) &&
            (this.gdtr == p.gdtr) &&
            (this.ldtr == p.ldtr) &&
            (this.tss == p.tss) &&
            (this.eflagsCarry == p.eflagsCarry) &&
            (this.eflagsParity == p.eflagsParity) &&
            (this.eflagsAuxiliaryCarry == p.eflagsAuxiliaryCarry) &&
            (this.eflagsZero == p.eflagsZero) &&
            (this.eflagsSign == p.eflagsSign) &&
            (this.eflagsTrap == p.eflagsTrap) &&
            (this.eflagsInterruptEnable == p.eflagsInterruptEnable) &&
            (this.eflagsDirection == p.eflagsDirection) &&
            (this.eflagsOverflow == p.eflagsOverflow) &&
            (this.eflagsIOPrivilegeLevel == p.eflagsIOPrivilegeLevel) &&
            (this.eflagsNestedTask == p.eflagsNestedTask) &&
            (this.eflagsResume == p.eflagsResume) &&
            (this.eflagsVirtual8086Mode == p.eflagsVirtual8086Mode) &&
            (this.eflagsAlignmentCheck == p.eflagsAlignmentCheck) &&
            (this.eflagsVirtualInterrupt == p.eflagsVirtualInterrupt) &&
            (this.eflagsVirtualInterruptPending == p.eflagsVirtualInterruptPending) &&
            (this.eflagsID == p.eflagsID) &&
            (this.eflagsInterruptEnableSoon == p.eflagsInterruptEnableSoon) &&
            (this.linearMemory == p.linearMemory) &&
            (this.physicalMemory == p.physicalMemory) &&
            (this.alignmentCheckedMemory == p.alignmentCheckedMemory) &&
            (this.ioports == p.ioports) &&
            (this.fpu == p.fpu))
            return true;
        else
            return false;
    }

    public int getEFlags()
    {
        int result = 0x2;
        if (getCarryFlag())
            result |= 0x1;
        if (getParityFlag())
            result |= 0x4;
        if (getAuxiliaryCarryFlag())
            result |= 0x10;
        if (getZeroFlag())
            result |= 0x40;
        if (getSignFlag())
            result |= 0x80;
        if (eflagsTrap)
            result |= 0x100;
        if (eflagsInterruptEnable)
            result |= 0x200;
        if (eflagsDirection)
            result |= 0x400;
        if (getOverflowFlag())
            result |= 0x800;
        result |= (eflagsIOPrivilegeLevel << 12);
        if (eflagsNestedTask)
            result |= 0x4000;
        if (eflagsResume)
            result |= 0x10000;
        if (eflagsVirtual8086Mode)
            result |= 0x20000;
        if (eflagsAlignmentCheck)
            result |= 0x40000;
        if (eflagsVirtualInterrupt)
            result |= 0x80000;
        if (eflagsVirtualInterruptPending)
            result |= 0x100000;
        if (eflagsID)
            result |= 0x200000;

        return result;
    }

    public void setEFlags(int eflags)
    {
	// TODO:  check that there aren't flags which can't be set this way!
        setCarryFlag((eflags & 1 ) != 0);
	setParityFlag((eflags & (1 << 2)) != 0);
	setAuxiliaryCarryFlag((eflags & (1 << 4)) != 0);
        setZeroFlag((eflags & (1 << 6)) != 0);
        setSignFlag((eflags & (1 <<  7)) != 0);
        eflagsTrap                    = ((eflags & (1 <<  8)) != 0);
        eflagsInterruptEnableSoon
            = eflagsInterruptEnable   = ((eflags & (1 <<  9)) != 0);
        eflagsDirection               = ((eflags & (1 << 10)) != 0);
        setOverflowFlag((eflags & (1 << 11)) != 0);
        eflagsIOPrivilegeLevel        = ((eflags >> 12) & 3);
        eflagsNestedTask              = ((eflags & (1 << 14)) != 0);
        eflagsResume                  = ((eflags & (1 << 16)) != 0);
        eflagsVirtual8086Mode         = ((eflags & (1 << 17)) != 0);

        eflagsVirtualInterrupt        = ((eflags & (1 << 19)) != 0);
        eflagsVirtualInterruptPending = ((eflags & (1 << 20)) != 0);
        eflagsID                      = ((eflags & (1 << 21)) != 0);

	if (eflagsVirtual8086Mode)
	    System.err.println("Should be in Virtual-8086 mode");

	if (eflagsAlignmentCheck != ((eflags & (1 << 18)) != 0)) {
	    eflagsAlignmentCheck = ((eflags & (1 << 18)) != 0);
	    checkAlignmentChecking();
	}
    }

    public void processClock()
    {
	virtualClock.process();
    }

    public void setCPL(int value)
    {
        currentPrivilegeLevel = value;
        linearMemory.setSupervisor(currentPrivilegeLevel == 0);
	checkAlignmentChecking();
    }

    public int getCPL()
    {
        return currentPrivilegeLevel;
    }

    public void reportFPUException()
    {
	if ((cr0 & CR0_NUMERIC_ERROR) == 0) {
	    System.err.println("Reporting FPU Error Via IRQ#13");
	    interruptController.setIRQ(13, 1);
	} else {
	    System.err.println("Reporting FPU Error Via Exception 0x10");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_MF_10, true);
	}
    }

    public synchronized void raiseInterrupt()
    {
        interruptFlags |= IFLAGS_HARDWARE_INTERRUPT;
        notifyAll();
    }

    public synchronized void waitForInterrupt(long time)
    {
        try
        {
            wait(time);
        }
        catch (Exception e) {}
    }
        
    public void clearInterrupt()
    {
        interruptFlags &= ~IFLAGS_HARDWARE_INTERRUPT;
    }

    public void requestReset()
    {
        interruptFlags |= IFLAGS_RESET_REQUEST;
    }

    public int getInterruptFlags()
    {
        return interruptFlags;
    }

    public boolean isProtectedMode()
    {
        return (cr0 & CR0_PROTECTION_ENABLE) == 1;
    }

    // Need to think about the TS flag for when we have an FPU - Section 2.5 Vol 3
    public void setCR0(int value)
    {
        value |= 0x10;
        int changedBits = value ^ cr0;
        if (changedBits == 0)
            return;

	//actually set the value!
        cr0 = value;

        boolean pagingChanged = (changedBits & CR0_PAGING) != 0;
        boolean cachingChanged = (changedBits & CR0_CACHE_DISABLE) != 0;
	boolean modeSwitch = (changedBits & CR0_PROTECTION_ENABLE) != 0;
	boolean wpUserPagesChanged = (changedBits & CR0_WRITE_PROTECT) != 0;
	boolean alignmentChanged = (changedBits & CR0_ALIGNMENT_MASK) != 0;

        if ((changedBits & CR0_NOT_WRITETHROUGH)!= 0)
            System.err.println("INFO: Unimplemented CR0 flags changed ("+Integer.toHexString(changedBits)+"). Now is "+Integer.toHexString(value));
	
	if (pagingChanged) {
            if (((value & CR0_PROTECTION_ENABLE) == 0) && ((value & CR0_PAGING) == 1))
                throw new ProcessorException(PROC_EXCEPTION_GP, 0, true);
        }
	
        if (alignmentChanged)
	    checkAlignmentChecking();
	
        if (pagingChanged || cachingChanged) {
            linearMemory.setPagingEnabled((value & CR0_PAGING) != 0);
            linearMemory.setPageCacheEnabled((value & CR0_CACHE_DISABLE) == 0);
	}

	if (modeSwitch) {
	    if ((value & CR0_PROTECTION_ENABLE) != 0) {
		convertSegmentsToProtectedMode();
		throw ModeSwitchException.PROTECTED_MODE_EXCEPTION;
	    } else {
		linearMemory.flush();
		setCPL(0);
                convertSegmentsToRealMode();
		throw ModeSwitchException.REAL_MODE_EXCEPTION;
	    }
	}

	if (wpUserPagesChanged)
	    linearMemory.setWriteProtectUserPages((value & CR0_WRITE_PROTECT) != 0);
    }

    public int getCR0()
    {
        return cr0;
    }

    public void setCR3(int value)
    {
        cr3 = value;
        linearMemory.setPageWriteThroughEnabled((value & CR3_PAGE_WRITES_TRANSPARENT) != 0);
        linearMemory.setPageCacheEnabled((value & CR3_PAGE_CACHE_DISABLE) == 0);
        linearMemory.setPageDirectoryBaseAddress(value);
    }

    public int getCR3()
    {
        return cr3;
    }

    public int getCR2()
    {
	return cr2;
    }

    public void setCR2(int value)
    {
        cr2 = value;
    }

    public void setCR4(int value)
    {
	if (cr4 == value)
	    return;

	cr4 = value;
        if ((cr4 & CR4_OS_SUPPORT_UNMASKED_SIMD_EXCEPTIONS) != 0)
            System.err.println("WARNING: SIMD instruction support modified in the processor");
        if ((cr4 & CR4_OS_SUPPORT_FXSAVE_FXSTORE) != 0)
            System.err.println("WARNING: FXSave and FXRStore flag enabled in the processor");
        if ((cr4 & CR4_DEBUGGING_EXTENSIONS) != 0)
            System.err.println("WARNING: debugging extensions enabled");
        if ((cr4 & CR4_TIME_STAMP_DISABLE) != 0)
            System.err.println("WARNING: timestamp restricted to CPL0");
        if ((cr4 & CR4_PHYSICAL_ADDRESS_EXTENSION) != 0)
            throw new IllegalStateException("36 Bit Addressing enabled");

        linearMemory.setGlobalPagesEnabled((value & CR4_PAGE_GLOBAL_ENABLE) != 0);
        linearMemory.setPageSizeExtensionsEnabled((cr4 & CR4_PAGE_SIZE_EXTENSIONS) != 0);
    }

    public int getCR4()
    {
	return cr4;
    }

    public void setDR0(int value)
    {
        dr0 = value;
    }
    public void setDR1(int value)
    {
        dr1 = value;
    }
    public void setDR2(int value)
    {
        dr2 = value;
    }
    public void setDR3(int value)
    {
        dr3 = value;
    }
    public void setDR6(int value)
    {
        dr6 = value;
    }
    public void setDR7(int value)
    {
        dr7 = value;
    }

    public int getDR0()
    {
        return dr0;
    }
    public int getDR1()
    {
        return dr1;
    }
    public int getDR2()
    {
        return dr2;
    }
    public int getDR3()
    {
        return dr3;
    }
    public int getDR6()
    {
        return dr6;
    }
    public int getDR7()
    {
        return dr7;
    }

    public long getMSR(int index)
    {
	try {
	    return ((Long)modelSpecificRegisters.get(new Integer(index))).longValue();
	} catch (NullPointerException e) {
	    System.err.println("Reading unset MSR " + index + " : Returning 0");
	    return 0l;
	}

    }

    public void setMSR(int index, long value)
    {
	modelSpecificRegisters.put(new Integer(index), new Long(value));
    }

    private void convertSegmentsToRealMode()
    {        
	cs = createRealModeSegment(cs.translateAddressRead(0) >>> 4);
	ds = createRealModeSegment(ds.translateAddressRead(0) >>> 4);
	ss = createRealModeSegment(ss.translateAddressRead(0) >>> 4);
	es = createRealModeSegment(es.translateAddressRead(0) >>> 4);
	fs = createRealModeSegment(fs.translateAddressRead(0) >>> 4);
	gs = createRealModeSegment(gs.translateAddressRead(0) >>> 4);
    }

    private void convertSegmentsToProtectedMode()
    {
	cs.setAddressSpace(linearMemory);
	ds.setAddressSpace(linearMemory);
	ss.setAddressSpace(linearMemory);
	es.setAddressSpace(linearMemory);
	fs.setAddressSpace(linearMemory);
	gs.setAddressSpace(linearMemory);
    }

    private void updateAlignmentCheckingInDataSegments()
    {
        if (alignmentChecking)
        {
            ds.setAddressSpace(alignmentCheckedMemory);
            ss.setAddressSpace(alignmentCheckedMemory);
            es.setAddressSpace(alignmentCheckedMemory);
            fs.setAddressSpace(alignmentCheckedMemory);
            gs.setAddressSpace(alignmentCheckedMemory);
        }
        else
        {
            ds.setAddressSpace(linearMemory);
            ss.setAddressSpace(linearMemory);
            es.setAddressSpace(linearMemory);
            fs.setAddressSpace(linearMemory);
            gs.setAddressSpace(linearMemory);
        }
    }

    public Segment createRealModeSegment(int selector)
    {
        return SegmentFactory.createRealModeSegment(physicalMemory, selector);
    }

    public Segment createDescriptorTableSegment(int base, int limit)
    {
	return SegmentFactory.createDescriptorTableSegment(linearMemory, base, limit);
    }

    public void correctAlignmentChecking(Segment segment)
    {
	if (alignmentChecking) {
	    if ((segment.getType() & 0x18) == 0x10) // Should make this a data segment
		segment.setAddressSpace(alignmentCheckedMemory);
	}
    }

    public Segment getSegment(int segmentSelector)
    {
        boolean isSup = linearMemory.isSupervisor();
	try 
        {
            long segmentDescriptor = 0;
            linearMemory.setSupervisor(true);
	    if ((segmentSelector & 0x4) != 0) 
                segmentDescriptor = ldtr.getQuadWord(segmentSelector & 0xfff8);
            else 
            {
		if (segmentSelector < 0x4)
		    return SegmentFactory.NULL_SEGMENT;
                segmentDescriptor = gdtr.getQuadWord(segmentSelector & 0xfff8);
	    }

            Segment result = SegmentFactory.createProtectedModeSegment(linearMemory, segmentSelector, segmentDescriptor);
            if (alignmentChecking)
            {
                if ((result.getType() & 0x18) == 0x10) // Should make this a data segment
                    result.setAddressSpace(alignmentCheckedMemory);
            }

            return result;	    
	} 
        finally 
        {
            linearMemory.setSupervisor(isSup);
	}
    }

    public void reset()
    {
	resetTime = System.currentTimeMillis();
        eax = ebx = ecx = edx = 0;
        edi = esi = ebp = esp = 0;
        edx = 0x00000633; // Pentium II Model 3 Stepping 3

        interruptFlags = 0;
        currentPrivilegeLevel = 0;
	linearMemory.reset();
	alignmentChecking = false;

	eip = 0x0000fff0;

        cr0 = CR0_CACHE_DISABLE | CR0_NOT_WRITETHROUGH | 0x10;
        cr2 = cr3 = cr4 = 0x0;

        dr0 = dr1 = dr2 = dr3 = 0x0;
        dr6 = 0xffff0ff0;
        dr7 = 0x00000700;

        eflagsCarry = eflagsParity = eflagsAuxiliaryCarry = eflagsZero = eflagsSign = eflagsTrap = eflagsInterruptEnable = false;
	carryCalculated = parityCalculated = auxiliaryCarryCalculated = zeroCalculated = signCalculated = true;
        eflagsDirection = eflagsOverflow = eflagsNestedTask = eflagsResume = eflagsVirtual8086Mode = false;
	overflowCalculated = true;

        eflagsAlignmentCheck = eflagsVirtualInterrupt = eflagsVirtualInterruptPending = eflagsID = false;

        eflagsIOPrivilegeLevel = 0;
        eflagsZero = true; //eflags  = 0x00000020;
	eflagsInterruptEnableSoon = false;

	cs = createRealModeSegment(0xf000);
	ds = createRealModeSegment(0);
	ss = createRealModeSegment(0);
	es = createRealModeSegment(0);
	fs = createRealModeSegment(0);
	gs = createRealModeSegment(0);

	idtr = SegmentFactory.createDescriptorTableSegment(physicalMemory, 0, 0xFFFF);
	ldtr = SegmentFactory.NULL_SEGMENT;
	gdtr = SegmentFactory.createDescriptorTableSegment(physicalMemory, 0, 0xFFFF);
	tss = SegmentFactory.NULL_SEGMENT;

	modelSpecificRegisters.clear();
	//Will need to set any MSRs here

	fpu.init();
    }

    public long getClockCount()
    {
	return (System.currentTimeMillis() - resetTime) * 1000 * Processor.CLOCK_SPEED;
    }

    public final int getInstructionPointer()
    {
        return cs.translateAddressRead(eip);
    }

    public final void processRealModeInterrupts()
    {
	if ((interruptFlags & IFLAGS_RESET_REQUEST) != 0) {
	    reset();
	    return;
	}

	try {
	    if(eflagsInterruptEnable && ((interruptFlags & IFLAGS_HARDWARE_INTERRUPT) != 0)) {
		interruptFlags &= ~IFLAGS_HARDWARE_INTERRUPT;
		int interruptNumber = interruptController.cpuGetInterrupt();
		handleRealModeInterrupt(interruptNumber);
	    }
	} catch (Exception e) {
	    System.err.println("Interrupt Handler Is Throwing An Exception!");
	}
	eflagsInterruptEnable = eflagsInterruptEnableSoon;
    }

    public final void processProtectedModeInterrupts()
    {
	if ((interruptFlags & IFLAGS_RESET_REQUEST) != 0) {
	    reset();
	    return;
	}

	try {
	    if(eflagsInterruptEnable && ((interruptFlags & IFLAGS_HARDWARE_INTERRUPT) != 0)) {
		interruptFlags &= ~IFLAGS_HARDWARE_INTERRUPT;
		int interruptNumber = interruptController.cpuGetInterrupt();
		handleHardProtectedModeInterrupt(interruptNumber);
	    }
	} catch (Exception e) {
	    System.err.println("Interrupt Handler Is Throwing An Exception!");
	    throw new RuntimeException();
	}
	eflagsInterruptEnable = eflagsInterruptEnableSoon;
    }

    public final void handleRealModeException(int vector)
    {
	if (vector <= PROC_EXCEPTION_MAX)
	    handleRealModeInterrupt(vector);
    }

    private final void handleRealModeInterrupt(int vector)
    {
        // TODO:  the following is for real-mode interrupts.
        // probably need to check mode here and act accordingly.
        //if ((esp & 0xffff) < 6 && (esp & 0xffff) != 0)
        if (((esp & 0xffff) < 6) && ((esp & 0xffff) > 0)) {
	    throw new IllegalStateException("SS Processor Exception Thrown in \"handleInterrupt("+vector+")\"");
            //throw new ProcessorException(ProcessorException.SS);
	    //maybe just change vector value
	}
        // hmm...an exception in an interrupt handler?  maybe not...
        
        vector *= 4;
        int newEip = 0xffff & idtr.getWord(vector);
        int newSelector = 0xffff & idtr.getWord(vector+2);

        short sesp = (short) esp;
        sesp -= 2;
        int eflags = getEFlags() & 0xffff;
        ss.setWord(sesp & 0xffff, (short)eflags);
        eflagsInterruptEnable = false;
	eflagsInterruptEnableSoon = false;
        eflagsTrap = false;
        eflagsAlignmentCheck = false;
        sesp -= 2;
        ss.setWord(sesp & 0xffff, (short)cs.getSelector());
        sesp -= 2;
        ss.setWord(sesp & 0xffff, (short)eip);
        esp = (0xFFFF0000 & esp) | (sesp & 0xFFFF);
        // read interrupt vector
        eip = newEip;

        if (!cs.setSelector(newSelector))
        {
            cs = createRealModeSegment(newSelector);
            setCPL(0);
        }
    }

    public final void handleProtectedModeException(int vector, boolean hasErrorCode, int errorCode)
    {
	int savedESP = esp;
	int savedEIP = eip;
	Segment savedCS = cs;
	Segment savedSS = ss;

	try {
	    followProtectedModeException(vector, hasErrorCode, errorCode, false, false);
	} catch (ProcessorException e) {
	    //return cpu to original state
	    esp = savedESP;
	    eip = savedEIP;
	    cs = savedCS;
	    ss = savedSS;

	    if (vector == PROC_EXCEPTION_DF) {
		System.err.println("Triple-Fault: Unhandleable, machine will halt!");
		throw new IllegalStateException("Triple Fault " + e);
	    } else if (e.combinesToDoubleFault(vector))
		handleProtectedModeException(PROC_EXCEPTION_DF, true, 0);
	    else
		handleProtectedModeException(e.getVector(), e.hasErrorCode(), e.getErrorCode());       
	}
    }

    public final void handleSoftProtectedModeInterrupt(int vector)
    {
	int savedESP = esp;
	int savedEIP = eip;
	Segment savedCS = cs;
	Segment savedSS = ss;

	try {
	    followProtectedModeException(vector, false, 0, false, true);
	} catch (ProcessorException e) {
	    //return cpu to original state
	    esp = savedESP;
	    eip = savedEIP;
	    cs = savedCS;
	    ss = savedSS;

	    //if (e.getVector() == PROC_EXCEPTION_DF) {
	    //System.err.println("Triple-Fault: Unhandleable, machine will halt!");
	    //throw new IllegalStateException("Triple Fault");
	    //else
	    handleProtectedModeException(e.getVector(), e.hasErrorCode(), e.getErrorCode());       
	}
    }

    public final void handleHardProtectedModeInterrupt(int vector)
    {
	int savedESP = esp;
	int savedEIP = eip;
	Segment savedCS = cs;
	Segment savedSS = ss;

	try {
	    followProtectedModeException(vector, false, 0, true, false);
	} catch (ProcessorException e) {
	    //return cpu to original state
	    esp = savedESP;
	    eip = savedEIP;
	    cs = savedCS;
	    ss = savedSS;

	    //if (e.getVector() == PROC_EXCEPTION_DF) {
	    //System.err.println("Triple-Fault: Unhandleable, machine will halt!");
	    //throw new IllegalStateException("Triple Fault");
	    //else
	    handleProtectedModeException(e.getVector(), e.hasErrorCode(), e.getErrorCode());       
	}
    }

    private final void checkGate(Segment gate, int selector, boolean software)
    {
	if (software) {
	    if (gate.getDPL() < currentPrivilegeLevel)
		throw new ProcessorException(PROC_EXCEPTION_GP, selector + 2, true);
	}
	
	if (!gate.isPresent())
	    throw new ProcessorException(PROC_EXCEPTION_NP, selector, true);
    }


    private final void followProtectedModeException(int vector, boolean hasErrorCode, int errorCode, boolean hardware, boolean software)
    {
        if (vector == PROC_EXCEPTION_PF)
            setCR2(linearMemory.getLastWalkedAddress());
        
	int selector = vector << 3; //multiply by 8 to get offset into idt	
	int EXT = hardware ? 1 : 0;

	Segment gate;
        boolean isSup = linearMemory.isSupervisor();
	try 
        {
            linearMemory.setSupervisor(true);
	    long descriptor = idtr.getQuadWord(selector);
	    gate = SegmentFactory.createProtectedModeSegment(linearMemory, selector, descriptor);
	} 
        catch (ProcessorException e) 
        {
	    throw new ProcessorException(PROC_EXCEPTION_GP, selector + 2 + EXT, true);
	}
        finally
        {
            linearMemory.setSupervisor(isSup);
        }

	switch (gate.getType()) {
	default:
	    System.err.println("Invalid Gate Type For Throwing Interrupt: 0x" + Integer.toHexString(gate.getType()));
	    throw new ProcessorException(PROC_EXCEPTION_GP, selector + 2 + EXT, true);
	case 0x05: //Interrupt Handler: Task Gate
	    throw new IllegalStateException("Unimplemented Interrupt Handler: Task Gate");
	case 0x06: //Interrupt Handler: 16-bit Interrupt Gate
	    throw new IllegalStateException("Unimplemented Interrupt Handler: 16-bit Interrupt Gate");
	case 0x07: //Interrupt Handler: 16-bit Trap Gate
	    throw new IllegalStateException("Unimplemented Interrupt Handler: 16-bit Trap Gate");
	case 0x0e: //Interrupt Handler: 32-bit Interrupt Gate
	    {
		SegmentFactory.InterruptGate32Bit interruptGate = ((SegmentFactory.InterruptGate32Bit)gate);

		checkGate(gate, selector, software);

		int targetSegmentSelector = interruptGate.getTargetSegment();
		
		Segment targetSegment;
		try {
		    targetSegment = getSegment(targetSegmentSelector);
		} catch (ProcessorException e) {
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		}
		
		if (targetSegment.getDPL() > currentPrivilegeLevel)
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		
		switch(targetSegment.getType()) {
		default:
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		    
		case 0x18: //Code, Execute-Only
		case 0x19: //Code, Execute-Only, Accessed
		case 0x1a: //Code, Execute/Read
		case 0x1b: //Code, Execute/Read, Accessed
		    {
			if (!targetSegment.isPresent())
			    throw new ProcessorException(PROC_EXCEPTION_NP, targetSegmentSelector + EXT, true);
			
			if (targetSegment.getDPL() < currentPrivilegeLevel) {
			    //INTER-PRIVILEGE-LEVEL
			    int newStackSelector = 0;
			    int newESP = 0;
			    if ((tss.getType() & 0x8) != 0) {
				int tssStackAddress = (targetSegment.getDPL() * 8) + 4;
				if ((tssStackAddress + 7) > tss.getLimit())
				    throw new ProcessorException(PROC_EXCEPTION_TS, tss.getSelector(), true);

                                isSup = linearMemory.isSupervisor();
				try 
                                {
                                    linearMemory.setSupervisor(true);
				    newStackSelector = 0xffff & tss.getWord(tssStackAddress + 4);
				    newESP = tss.getDoubleWord(tssStackAddress);
				}
                                finally 
                                {
                                    linearMemory.setSupervisor(isSup);
				}
			    } else {
				int tssStackAddress = (targetSegment.getDPL() * 4) + 2;
				if ((tssStackAddress + 4) > tss.getLimit())
				    throw new ProcessorException(PROC_EXCEPTION_TS, tss.getSelector(), true);
				newStackSelector = 0xffff & tss.getWord(tssStackAddress + 2);
				newESP = 0xffff & tss.getWord(tssStackAddress);
			    }

			    Segment newStackSegment = null;
			    try {
				newStackSegment = getSegment(newStackSelector);
			    } catch (ProcessorException e) {
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);
			    }

			    if (newStackSegment.getRPL() != targetSegment.getDPL())
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);

			    if ((newStackSegment.getDPL() !=  targetSegment.getDPL()) || ((newStackSegment.getType() & 0x1a) != 0x12))
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);

			    if (!(newStackSegment.isPresent()))
				throw new ProcessorException(PROC_EXCEPTION_SS, newStackSelector, true);

			    if (hasErrorCode) {
				if ((newStackSegment.getDefaultSizeFlag() && (esp < 24) && (esp > 0)) ||
				    !newStackSegment.getDefaultSizeFlag() && ((esp & 0xffff) < 24) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    } else {
				if ((newStackSegment.getDefaultSizeFlag() && (esp < 20) && (esp > 0)) ||
				    !newStackSegment.getDefaultSizeFlag() && ((esp & 0xffff) < 20) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    }

			    int targetOffset = interruptGate.getTargetOffset();
			    targetSegment.checkAddress(targetOffset);

			    int oldSS = ss.getSelector();
			    int oldESP = esp;
			    int oldCS = cs.getSelector();
			    int oldEIP = eip;
			    ss = newStackSegment;
			    esp = newESP;

			    cs = targetSegment;
			    eip = targetOffset;
			    setCPL(cs.getDPL());

			    if (ss.getDefaultSizeFlag()) {
				esp -= 4;
				ss.setDoubleWord(esp, oldSS);
				esp -= 4;
				ss.setDoubleWord(esp, oldESP);
				esp -= 4;
				ss.setDoubleWord(esp, getEFlags());
				esp -= 4;
				ss.setDoubleWord(esp, oldCS);
				esp -= 4;
				ss.setDoubleWord(esp, oldEIP);
				if (hasErrorCode) {
				    esp -= 4;
				    ss.setDoubleWord(esp, errorCode);
				}
			    } else {
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldSS);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldESP);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, getEFlags());
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldCS);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldEIP);
				if (hasErrorCode) {
				    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				    ss.setDoubleWord(esp & 0xffff, errorCode);
				}
			    }
			    
			    
			    eflagsInterruptEnable = eflagsInterruptEnableSoon = false;
			    
			    eflagsTrap = false;
			    eflagsNestedTask = false;
			    eflagsVirtual8086Mode = false;
			    eflagsResume = false;
			} else if (targetSegment.getDPL() == currentPrivilegeLevel) {
			    //SAME-PRIVILEGE
			    //check there is room on stack
			    if (hasErrorCode) {
				if ((ss.getDefaultSizeFlag() && (esp < 16) && (esp > 0)) ||
				    !ss.getDefaultSizeFlag() && ((esp & 0xffff) < 16) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    } else {
				if ((ss.getDefaultSizeFlag() && (esp < 12) && (esp > 0)) ||
				    !ss.getDefaultSizeFlag() && ((esp & 0xffff) < 12) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    }
			    
			    int targetOffset = interruptGate.getTargetOffset();
			    targetSegment.checkAddress(targetOffset);
			    

			    if (ss.getDefaultSizeFlag()) {
				esp -= 4;
				ss.setDoubleWord(esp, getEFlags());
				esp -= 4;
				ss.setDoubleWord(esp, cs.getSelector());
				esp -= 4;
				ss.setDoubleWord(esp, eip);
				if (hasErrorCode) {
				    esp -= 4;
				    ss.setDoubleWord(esp, errorCode);
				}
			    } else {
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, getEFlags());
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, cs.getSelector());
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, eip);
				if (hasErrorCode) {
				    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				    ss.setDoubleWord(esp & 0xffff, errorCode);
				}
			    }

			    cs = targetSegment;
			    eip = targetOffset;

                            cs.setRPL(currentPrivilegeLevel);
			    eflagsInterruptEnable = eflagsInterruptEnableSoon = false;

			    eflagsTrap = false;
			    eflagsNestedTask = false;
			    eflagsVirtual8086Mode = false;
			    eflagsResume = false;
			} else {
			    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true); 
			}
		    }
		    break;
		case 0x1c: //Code: Execute-Only, Conforming
		case 0x1d: //Code: Execute-Only, Conforming, Accessed
		case 0x1e: //Code: Execute/Read, Conforming
		case 0x1f: //Code: Execute/Read, Conforming, Accessed
		    {
			if (!targetSegment.isPresent())
			    throw new ProcessorException(PROC_EXCEPTION_NP, selector, true);
			
			//SAME-PRIVILEGE
			//check there is room on stack
			if (hasErrorCode) {
			    if ((ss.getDefaultSizeFlag() && (esp < 16) && (esp > 0)) ||
				!ss.getDefaultSizeFlag() && ((esp & 0xffff) < 16) && ((esp & 0xffff) > 0))
				throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			} else {
			    if ((ss.getDefaultSizeFlag() && (esp < 12) && (esp > 0)) ||
				!ss.getDefaultSizeFlag() && ((esp & 0xffff) < 12) && ((esp & 0xffff) > 0))
				throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			}
			
			int targetOffset = interruptGate.getTargetOffset();
			
			targetSegment.checkAddress(targetOffset);
			
			if (ss.getDefaultSizeFlag()) {
			    esp -= 4;
			    ss.setDoubleWord(esp, getEFlags());
			    esp -= 4;
			    ss.setDoubleWord(esp, cs.getSelector());
			    esp -= 4;
			    ss.setDoubleWord(esp, eip);
			    if (hasErrorCode) {
				esp -= 4;
				ss.setDoubleWord(esp, errorCode);
			    }
			} else {
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, getEFlags());
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, cs.getSelector());
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, eip);
			    if (hasErrorCode) {
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, errorCode);
			    }
			}
			
			cs = targetSegment;
			eip = targetOffset;

                        cs.setRPL(currentPrivilegeLevel);

			eflagsInterruptEnable = eflagsInterruptEnableSoon = false;
			eflagsTrap = false;
			eflagsNestedTask = false;
			eflagsVirtual8086Mode = false;
			eflagsResume = false;
		    }
		break;
		}
	    }
	break;
	case 0x0f: //Interrupt Handler: 32-bit Trap Gate
	    {
		SegmentFactory.TrapGate32Bit trapGate = ((SegmentFactory.TrapGate32Bit)gate);
		
		checkGate(gate, selector, software);

		int targetSegmentSelector = trapGate.getTargetSegment();
		
		Segment targetSegment;
		try {
		    targetSegment = getSegment(targetSegmentSelector);
		} catch (ProcessorException e) {
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		}
		
		if (targetSegment.getDPL() > currentPrivilegeLevel)
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		
		switch(targetSegment.getType()) {
		default:
		    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true);
		    
		case 0x18: //Code, Execute-Only
		case 0x19: //Code, Execute-Only, Accessed
		case 0x1a: //Code, Execute/Read
		case 0x1b: //Code, Execute/Read, Accessed
		    {
			if (!targetSegment.isPresent())
			    throw new ProcessorException(PROC_EXCEPTION_NP, targetSegmentSelector + EXT, true);
			
			if (targetSegment.getDPL() < currentPrivilegeLevel) {
			    //INTER-PRIVILEGE-LEVEL
			    int newStackSelector = 0;
			    int newESP = 0;
			    if ((tss.getType() & 0x8) != 0) {
				int tssStackAddress = (targetSegment.getDPL() * 8) + 4;
				if ((tssStackAddress + 7) > tss.getLimit())
				    throw new ProcessorException(PROC_EXCEPTION_TS, tss.getSelector(), true);

                                isSup = linearMemory.isSupervisor();
				try 
                                {
                                    linearMemory.setSupervisor(true);
				    newStackSelector = 0xffff & tss.getWord(tssStackAddress + 4);
				    newESP = tss.getDoubleWord(tssStackAddress);
				}
                                finally 
                                {
                                    linearMemory.setSupervisor(isSup);
				}
			    } else {
				int tssStackAddress = (targetSegment.getDPL() * 4) + 2;
				if ((tssStackAddress + 4) > tss.getLimit())
				    throw new ProcessorException(PROC_EXCEPTION_TS, tss.getSelector(), true);
				newStackSelector = 0xffff & tss.getWord(tssStackAddress + 2);
				newESP = 0xffff & tss.getWord(tssStackAddress);
			    }

			    Segment newStackSegment = null;
			    try {
				newStackSegment = getSegment(newStackSelector);
			    } catch (ProcessorException e) {
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);
			    }

			    if (newStackSegment.getRPL() != targetSegment.getDPL())
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);

			    if ((newStackSegment.getDPL() !=  targetSegment.getDPL()) || ((newStackSegment.getType() & 0x1a) != 0x12))
				throw new ProcessorException(PROC_EXCEPTION_TS, newStackSelector, true);

			    if (!(newStackSegment.isPresent()))
				throw new ProcessorException(PROC_EXCEPTION_SS, newStackSelector, true);

			    if (hasErrorCode) {
				if ((newStackSegment.getDefaultSizeFlag() && (esp < 24) && (esp > 0)) ||
				    !newStackSegment.getDefaultSizeFlag() && ((esp & 0xffff) < 24) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    } else {
				if ((newStackSegment.getDefaultSizeFlag() && (esp < 20) && (esp > 0)) ||
				    !newStackSegment.getDefaultSizeFlag() && ((esp & 0xffff) < 20) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    }

			    int targetOffset = trapGate.getTargetOffset();
			    targetSegment.checkAddress(targetOffset);

			    int oldSS = ss.getSelector();
			    int oldESP = esp;
			    int oldCS = cs.getSelector();
			    int oldEIP = eip;

			    ss = newStackSegment;
			    esp = newESP;

			    cs = targetSegment;
			    eip = targetOffset;
			    setCPL(cs.getDPL());

			    if (ss.getDefaultSizeFlag()) {
				esp -= 4;
				ss.setDoubleWord(esp, oldSS);
				esp -= 4;
				ss.setDoubleWord(esp, oldESP);
				esp -= 4;
				ss.setDoubleWord(esp, getEFlags());
				esp -= 4;
				ss.setDoubleWord(esp, oldCS);
				esp -= 4;
				ss.setDoubleWord(esp, oldEIP);
				if (hasErrorCode) {
				    esp -= 4;
				    ss.setDoubleWord(esp, errorCode);
				}
			    } else {
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldSS);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldESP);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, getEFlags());
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldCS);
				esp = (esp & ~0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, oldEIP);
				if (hasErrorCode) {
				    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				    ss.setDoubleWord(esp & 0xffff, errorCode);
				}
			    }
			    
			    eflagsTrap = false;
			    eflagsNestedTask = false;
			    eflagsVirtual8086Mode = false;
			    eflagsResume = false;
			} else if (targetSegment.getDPL() == currentPrivilegeLevel) {
			    //SAME-PRIVILEGE
			    //check there is room on stack
			    if (hasErrorCode) {
				if ((ss.getDefaultSizeFlag() && (esp < 16) && (esp > 0)) ||
				    !ss.getDefaultSizeFlag() && ((esp & 0xffff) < 16) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    } else {
				if ((ss.getDefaultSizeFlag() && (esp < 12) && (esp > 0)) ||
				    !ss.getDefaultSizeFlag() && ((esp & 0xffff) < 12) && ((esp & 0xffff) > 0))
				    throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			    }

			    int targetOffset = trapGate.getTargetOffset();
			    
			    targetSegment.checkAddress(targetOffset);
			    
			    if (ss.getDefaultSizeFlag()) {
				esp -= 4;
				ss.setDoubleWord(esp, getEFlags());
				esp -= 4;
				ss.setDoubleWord(esp, cs.getSelector());
				esp -= 4;
				ss.setDoubleWord(esp, eip);
				if (hasErrorCode) {
				    esp -= 4;
				    ss.setDoubleWord(esp, errorCode);
				}
			    } else {
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, getEFlags());
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, cs.getSelector());
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, eip);
				if (hasErrorCode) {
				    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				    ss.setDoubleWord(esp & 0xffff, errorCode);
				}
			    }
			    
			    cs = targetSegment;
			    eip = targetOffset;

                            cs.setRPL(currentPrivilegeLevel);

			    eflagsTrap = false;
			    eflagsNestedTask = false;
			    eflagsVirtual8086Mode = false;
			    eflagsResume = false;
			} else {
			    throw new ProcessorException(PROC_EXCEPTION_GP, targetSegmentSelector + EXT, true); 
			}
		    }
		    break;
		case 0x1c: //Code: Execute-Only, Conforming
		case 0x1d: //Code: Execute-Only, Conforming, Accessed
		case 0x1e: //Code: Execute/Read, Conforming
		case 0x1f: //Code: Execute/Read, Conforming, Accessed
		    {
			if (!targetSegment.isPresent())
			    throw new ProcessorException(PROC_EXCEPTION_NP, selector, true);
			
			//SAME-PRIVILEGE
			//check there is room on stack
			if (hasErrorCode) {
			    if ((ss.getDefaultSizeFlag() && (esp < 16) && (esp > 0)) ||
				!ss.getDefaultSizeFlag() && ((esp & 0xffff) < 16) && ((esp & 0xffff) > 0))
				throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			} else {
			    if ((ss.getDefaultSizeFlag() && (esp < 12) && (esp > 0)) ||
				!ss.getDefaultSizeFlag() && ((esp & 0xffff) < 12) && ((esp & 0xffff) > 0))
				throw new ProcessorException(PROC_EXCEPTION_SS, 0, true);
			}
			
			int targetOffset = trapGate.getTargetOffset();
			
			targetSegment.checkAddress(targetOffset);
			
			if (ss.getDefaultSizeFlag()) {
			    esp -= 4;
			    ss.setDoubleWord(esp, getEFlags());
			    esp -= 4;
			    ss.setDoubleWord(esp, cs.getSelector());
			    esp -= 4;
			    ss.setDoubleWord(esp, eip);
			    if (hasErrorCode) {
				esp -= 4;
				ss.setDoubleWord(esp, errorCode);
			    }
			} else {
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, getEFlags());
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, cs.getSelector());
			    esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
			    ss.setDoubleWord(esp & 0xffff, eip);
			    if (hasErrorCode) {
				esp = (esp & ~ 0xffff) | ((esp - 4) & 0xffff);
				ss.setDoubleWord(esp & 0xffff, errorCode);
			    }
			}
			
			cs = targetSegment;
			eip = targetOffset;
                        cs.setRPL(currentPrivilegeLevel);

			eflagsTrap = false;
			eflagsNestedTask = false;
			eflagsVirtual8086Mode = false;
			eflagsResume = false;
		    }
		break;
		}
	    }
	break;
	}
    }

    private void checkAlignmentChecking()
    {
	if ((getCPL() == 3) && eflagsAlignmentCheck && ((cr0 & CR0_ALIGNMENT_MASK) != 0)) {
	    if (!alignmentChecking) {
		System.err.println("Alignment Checking Enabled");
                alignmentChecking = true;
                updateAlignmentCheckingInDataSegments();
		//checking now enabled
	    }
	} else {
	    if (alignmentChecking) {
		System.err.println("Alignment Checking Disabled");
                alignmentChecking = false;
                updateAlignmentCheckingInDataSegments();
		//checking now disabled
	    }
	}
    }

    public boolean initialised()
    {
	boolean result = ((physicalMemory != null) && (linearMemory != null) && (ioports != null) && (interruptController != null) && (virtualClock != null));
	if (result)
	    reset();
	return result;
    }

    public void acceptComponent(HardwareComponent component)
    {
        if (component instanceof LinearAddressSpace)
        {
            linearMemory = (LinearAddressSpace) component;
            alignmentCheckedMemory = new AlignmentCheckedAddressSpace(linearMemory);
        }
        if (component instanceof PhysicalAddressSpace)
            physicalMemory = (PhysicalAddressSpace) component;
        if (component instanceof IOPortHandler)
            ioports = (IOPortHandler) component;
	if ((component instanceof InterruptController)
	    && component.initialised())
	    interruptController = (InterruptController)component;	   
	if (component instanceof Clock)
	    virtualClock = (Clock)component;
    }

    public void loadState(DataInputStream din) throws IOException
    {
        String tagline = din.readUTF();
        if (!din.readUTF().equals(getClass().getName()))
            throw new IOException("Invalid state data for processor");
        if (din.readInt() != STATE_VERSION)
            throw new IOException("Processor state version mismatch");
        if (din.readInt() < STATE_MINOR_VERSION)
            throw new IOException("Processor state minor version mismatch");

        eax = din.readInt();
        ecx = din.readInt();
        edx = din.readInt();
        ebx = din.readInt();
        esp = din.readInt();
        ebp = din.readInt();
        esi = din.readInt();
        edi = din.readInt();
        
        eip = din.readInt();
        
        setCR0(din.readInt());
        //setCR1(din.readInt());
        setCR2(din.readInt());
        setCR3(din.readInt());
        setCR4(din.readInt());
        
        dr0 = din.readInt();
        dr1 = din.readInt();
        dr2 = din.readInt();
        dr3 = din.readInt();
        dr4 = din.readInt();
        dr5 = din.readInt();
        dr6 = din.readInt();
        dr7 = din.readInt();
        
    }

    public void saveState(DataOutputStream dout) throws IOException
    {
        dout.writeUTF("JPC Processor");
        dout.writeUTF(getClass().getName());
        dout.writeInt(STATE_VERSION); //major version
        dout.writeInt(STATE_MINOR_VERSION); //minor version
        
        dout.writeInt(eax);
        dout.writeInt(ecx);
        dout.writeInt(edx);
        dout.writeInt(ebx);
        dout.writeInt(esp);
        dout.writeInt(ebp);
        dout.writeInt(esi);
        dout.writeInt(edi);
        
        dout.writeInt(eip);

        dout.writeInt(cr0);
        dout.writeInt(cr1);
        dout.writeInt(cr2);
        dout.writeInt(cr3);
        dout.writeInt(cr4);

        dout.writeInt(dr0);
        dout.writeInt(dr1);
        dout.writeInt(dr2);
        dout.writeInt(dr3);
        dout.writeInt(dr4);
        dout.writeInt(dr5);
        dout.writeInt(dr6);
        dout.writeInt(dr7);

        dout.writeInt(getEFlags());
        dout.writeInt(interruptFlags);
        dout.writeLong(resetTime);
        dout.writeInt(currentPrivilegeLevel);

	/*
        cs.saveState(dout);
        ds.saveState(dout);
        ss.saveState(dout);
        es.saveState(dout);
        fs.saveState(dout);
        gs.saveState(dout);

        idtr.saveState(dout);
        gdtr.saveState(dout);
        ldtr.saveState(dout);
        tss.saveState(dout);
	
        fpu.saveState(dout);
	*/
    }

    private int auxiliaryCarryOne, auxiliaryCarryTwo, auxiliaryCarryThree;
    private boolean auxiliaryCarryCalculated;
    private int auxiliaryCarryMethod;

    public static final int AC_XOR = 1;
    public static final int AC_BIT4_NEQ = 2;
    public static final int AC_LNIBBLE_MAX = 3;
    public static final int AC_LNIBBLE_ZERO = 4;
    public static final int AC_LNIBBLE_NZERO = 5;

    public boolean getAuxiliaryCarryFlag()
    {
	if (auxiliaryCarryCalculated)
	    return eflagsAuxiliaryCarry;
	else {
	    auxiliaryCarryCalculated = true;
	    switch (auxiliaryCarryMethod) {
	    case AC_XOR: return (eflagsAuxiliaryCarry = ((((auxiliaryCarryOne ^ auxiliaryCarryTwo) ^ auxiliaryCarryThree) & 0x10) != 0));
	    case AC_BIT4_NEQ: return (eflagsAuxiliaryCarry = ((auxiliaryCarryOne & 0x08) != (auxiliaryCarryTwo & 0x08)));
	    case AC_LNIBBLE_MAX: return (eflagsAuxiliaryCarry = ((auxiliaryCarryOne & 0xf) == 0xf));
	    case AC_LNIBBLE_ZERO: return (eflagsAuxiliaryCarry = ((auxiliaryCarryOne & 0xf) == 0x0));
	    case AC_LNIBBLE_NZERO: return (eflagsAuxiliaryCarry = ((auxiliaryCarryOne & 0xf) != 0x0));
	    }
	    System.err.println("Missing AC Flag Calculation Method");
	    return eflagsAuxiliaryCarry;
	}
    }

    private static final boolean[] parityMap;
    static 
    {
        parityMap = new boolean[256];
        for (int i=0; i<256; i++)
        {
            boolean val = true;
            for (int j=0; j<8; j++)
                if ((0x1 & (i >> j)) == 1)
                    val = !val;

            parityMap[i] = val;
        }
    } 

    public void setAuxiliaryCarryFlag(int dataOne, int dataTwo, int dataThree, int method)
    {
	auxiliaryCarryCalculated = false;
	auxiliaryCarryOne = dataOne;
	auxiliaryCarryTwo = dataTwo;
	auxiliaryCarryThree = dataThree;
	auxiliaryCarryMethod = method;
    }

    public void setAuxiliaryCarryFlag(int dataOne, int dataTwo, int method)
    {
	auxiliaryCarryCalculated = false;
	auxiliaryCarryOne = dataOne;
	auxiliaryCarryTwo = dataTwo;
	auxiliaryCarryMethod = method;
    }

    public void setAuxiliaryCarryFlag(int dataOne, int method)
    {
	auxiliaryCarryCalculated = false;
	auxiliaryCarryOne = dataOne;
	auxiliaryCarryMethod = method;
    }

    public void setAuxiliaryCarryFlag(boolean value)
    {
	auxiliaryCarryCalculated = true;
	eflagsAuxiliaryCarry = value;
    }

    private int parityOne;
    private boolean parityCalculated;

    public boolean getParityFlag()
    {
	if (parityCalculated)
	    return eflagsParity;
	else
	    parityCalculated = true;

        return (eflagsParity = parityMap[parityOne & 0xff]);

    }
    public void setParityFlag(boolean value)
    {
	parityCalculated = true;
	eflagsParity = value;
    }
    public void setParityFlag(int data)
    {
	parityCalculated = false;
	parityOne = data;
    }

    private int overflowOne, overflowTwo, overflowThree;
    private long overflowLong;
    private boolean overflowCalculated;
    private int overflowMethod;

    public static final int OF_NZ = 1;
    public static final int OF_NOT_BYTE = 2;
    public static final int OF_NOT_SHORT = 3;
    public static final int OF_NOT_INT = 4;

    public static final int OF_LOW_WORD_NZ = 5;
    public static final int OF_HIGH_BYTE_NZ = 6;

    public static final int OF_BIT6_XOR_CARRY = 7;
    public static final int OF_BIT7_XOR_CARRY = 8;
    public static final int OF_BIT14_XOR_CARRY = 9;
    public static final int OF_BIT15_XOR_CARRY = 10;
    public static final int OF_BIT30_XOR_CARRY = 11;
    public static final int OF_BIT31_XOR_CARRY = 12;

    public static final int OF_BIT7_XOR_BIT6 = 13;
    public static final int OF_BIT15_XOR_BIT14 = 14;
    public static final int OF_BIT31_XOR_BIT30 = 15;

    public static final int OF_BIT7_DIFFERENT = 16;
    public static final int OF_BIT15_DIFFERENT = 17;
    public static final int OF_BIT31_DIFFERENT = 18;

    public static final int OF_MAX_BYTE = 19;
    public static final int OF_MAX_SHORT = 20;
    public static final int OF_MAX_INT = 21;

    public static final int OF_MIN_BYTE = 22;
    public static final int OF_MIN_SHORT = 23;
    public static final int OF_MIN_INT = 24;

    public static final int OF_ADDSUB_BYTE = 25;
    public static final int OF_ADDSUB_SHORT = 26;
    public static final int OF_ADDSUB_INT = 27;

    public boolean getOverflowFlag()
    {
	if (overflowCalculated)
	    return eflagsOverflow;
	else {
	    overflowCalculated = true;
	    switch (overflowMethod) {
	    case OF_NZ: return (eflagsOverflow = (overflowOne != 0));
	    case OF_NOT_BYTE: return (eflagsOverflow = (overflowOne != (byte)overflowOne));
	    case OF_NOT_SHORT: return (eflagsOverflow = (overflowOne != (short)overflowOne));
	    case OF_NOT_INT: return (eflagsOverflow = (overflowLong != (int)overflowLong));

	    case OF_LOW_WORD_NZ: return (eflagsOverflow = ((overflowOne & 0xffff) != 0));
	    case OF_HIGH_BYTE_NZ: return (eflagsOverflow = ((overflowOne & 0xff00) != 0));

	    case OF_BIT6_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x40) != 0) ^ getCarryFlag()));
	    case OF_BIT7_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x80) != 0) ^ getCarryFlag()));
	    case OF_BIT14_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x4000) != 0) ^ getCarryFlag()));
	    case OF_BIT15_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x8000) != 0) ^ getCarryFlag()));
	    case OF_BIT30_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x40000000) != 0) ^ getCarryFlag()));
	    case OF_BIT31_XOR_CARRY: return (eflagsOverflow = (((overflowOne & 0x80000000) != 0) ^ getCarryFlag()));
		
	    case OF_BIT7_XOR_BIT6: return (eflagsOverflow = (((overflowOne & 0x80) != 0) ^ ((overflowOne & 0x40) != 0)));
	    case OF_BIT15_XOR_BIT14: return (eflagsOverflow = (((overflowOne & 0x8000) != 0) ^ ((overflowOne & 0x4000) != 0)));
	    case OF_BIT31_XOR_BIT30: return (eflagsOverflow = (((overflowOne & 0x80000000) != 0) ^ ((overflowOne & 0x40000000) != 0)));

	    case OF_BIT7_DIFFERENT: return (eflagsOverflow = ((overflowOne & 0x80) != (overflowTwo & 0x80)));
	    case OF_BIT15_DIFFERENT: return (eflagsOverflow = ((overflowOne & 0x8000) != (overflowTwo & 0x8000)));
	    case OF_BIT31_DIFFERENT: return (eflagsOverflow = ((overflowOne & 0x80000000) != (overflowTwo & 0x80000000)));

	    case OF_MAX_BYTE: return (eflagsOverflow = (overflowOne == 0x7f));
	    case OF_MAX_SHORT: return (eflagsOverflow = (overflowOne == 0x7fff));
	    case OF_MAX_INT: return (eflagsOverflow = (overflowOne == 0x7fffffff));
		
	    case OF_MIN_BYTE: return (eflagsOverflow = (overflowOne == (byte)0x80));
	    case OF_MIN_SHORT: return (eflagsOverflow = (overflowOne == (short)0x8000));
	    case OF_MIN_INT: return (eflagsOverflow = (overflowOne == 0x80000000));
		
	    case OF_ADDSUB_BYTE: {
		if ((byte)overflowThree > 0)
		    return (eflagsOverflow = ((byte)overflowOne < (byte)overflowTwo));
		else
		    return (eflagsOverflow = ((byte)overflowOne > (byte)overflowTwo));
	    }

	    case OF_ADDSUB_SHORT: {
		if ((short)overflowThree > 0)
		    return (eflagsOverflow = ((short)overflowOne < (short)overflowTwo));
		else
		    return (eflagsOverflow = ((short)overflowOne > (short)overflowTwo));
	    }

	    case OF_ADDSUB_INT: {
		if (overflowThree > 0)
		    return (eflagsOverflow = (overflowOne < overflowTwo));
		else
		    return (eflagsOverflow = (overflowOne > overflowTwo));
	    }
	    }
	    System.err.println("Missing OF Flag Calculation Method");
	    return eflagsOverflow;
	}
    }

    public void setOverflowFlag(boolean value)
    {
	overflowCalculated = true;
	eflagsOverflow = value;
    }

    public void setOverflowFlag(long dataOne, int method)
    {
	overflowCalculated = false;
	overflowLong = dataOne;
	overflowMethod = method;
    }

    public void setOverflowFlag(int dataOne, int method)
    {
	overflowCalculated = false;
	overflowOne = dataOne;
	overflowMethod = method;
    }

    public void setOverflowFlag(int dataOne, int dataTwo, int method)
    {
	overflowCalculated = false;
	overflowOne = dataOne;
	overflowTwo = dataTwo;
	overflowMethod = method;
    }

    public void setOverflowFlag(int dataOne, int dataTwo, int dataThree, int method)
    {
	overflowCalculated = false;
	overflowOne = dataOne;
	overflowTwo = dataTwo;
	overflowThree = dataThree;
	overflowMethod = method;
    }

    private int carryOne, carryTwo, carryThree;
    private long carryLong;
    private boolean carryCalculated;
    private int carryMethod;

    public static final int CY_NZ = 1;
    public static final int CY_NOT_BYTE = 2;
    public static final int CY_NOT_SHORT = 3;
    public static final int CY_NOT_INT = 4;

    public static final int CY_LOW_WORD_NZ = 5;
    public static final int CY_HIGH_BYTE_NZ = 6;

    public static final int CY_NTH_BIT_SET = 7;

    public static final int CY_GREATER_FF = 8;

    public static final int CY_TWIDDLE_FF = 9;
    public static final int CY_TWIDDLE_FFFF = 10;
    public static final int CY_TWIDDLE_FFFFFFFF = 11;

    public static final int CY_SHL_OUTBIT_BYTE = 12;
    public static final int CY_SHL_OUTBIT_SHORT = 13;
    public static final int CY_SHL_OUTBIT_INT = 14;

    public static final int CY_SHR_OUTBIT = 15;

    public static final int CY_LOWBIT = 16;

    public static final int CY_HIGHBIT_BYTE = 17;
    public static final int CY_HIGHBIT_SHORT = 18;
    public static final int CY_HIGHBIT_INT = 19;

    public static final int CY_OFFENDBIT_BYTE = 20;
    public static final int CY_OFFENDBIT_SHORT = 21;
    public static final int CY_OFFENDBIT_INT = 22;

    public boolean getCarryFlag()
    {
	if (carryCalculated)
	    return eflagsCarry;
	else {
	    carryCalculated = true;
	    switch (carryMethod) {
	    case CY_NZ: return (eflagsCarry = (carryOne != 0));
	    case CY_NOT_BYTE: return (eflagsCarry = (carryOne != (byte)carryOne));
	    case CY_NOT_SHORT: return (eflagsCarry = (carryOne != (short)carryOne));
	    case CY_NOT_INT: return (eflagsCarry = (carryLong != (int)carryLong));

	    case CY_LOW_WORD_NZ: return (eflagsCarry = ((carryOne & 0xffff) != 0));
	    case CY_HIGH_BYTE_NZ: return (eflagsCarry = ((carryOne & 0xff00) != 0));

	    case CY_NTH_BIT_SET: return (eflagsCarry = ((carryOne & (1 << carryTwo)) != 0));

	    case CY_GREATER_FF: return (eflagsCarry = (carryOne > 0xff));

	    case CY_TWIDDLE_FF: return (eflagsCarry = ((carryOne & (~0xff)) != 0));
	    case CY_TWIDDLE_FFFF: return (eflagsCarry = ((carryOne & (~0xffff)) != 0));
	    case CY_TWIDDLE_FFFFFFFF: return (eflagsCarry = ((carryLong & (~0xffffffffl)) != 0));

	    case CY_SHL_OUTBIT_BYTE: return (eflagsCarry = (((carryOne << (carryTwo - 1)) & 0x80) != 0));
	    case CY_SHL_OUTBIT_SHORT: return (eflagsCarry = (((carryOne << (carryTwo - 1)) & 0x8000) != 0));
	    case CY_SHL_OUTBIT_INT: return (eflagsCarry = (((carryOne << (carryTwo - 1)) & 0x80000000) != 0));

	    case CY_SHR_OUTBIT: return (eflagsCarry = (((carryOne >>> (carryTwo - 1)) & 0x1) != 0));

	    case CY_LOWBIT: return (eflagsCarry = ((carryOne & 0x1) != 0));
	    case CY_HIGHBIT_BYTE: return (eflagsCarry = ((carryOne & 0x80) != 0));
	    case CY_HIGHBIT_SHORT: return (eflagsCarry = ((carryOne & 0x8000) != 0));
	    case CY_HIGHBIT_INT: return (eflagsCarry = ((carryOne & 0x80000000) != 0));

	    case CY_OFFENDBIT_BYTE: return (eflagsCarry = ((carryOne & 0x100) != 0));
	    case CY_OFFENDBIT_SHORT: return (eflagsCarry = ((carryOne & 0x10000) != 0));
	    case CY_OFFENDBIT_INT: return (eflagsCarry = ((carryLong & 0x100000000l) != 0));
	    }
	    System.err.println("Missing CY Flag Calculation Method");
	    return eflagsCarry;
	}
    }

    public void setCarryFlag(boolean value)
    {
	carryCalculated = true;
	eflagsCarry = value;
    }

    public void setCarryFlag(long dataOne, int method)
    {
	carryCalculated = false;
	carryLong = dataOne;
	carryMethod = method;
    }

    public void setCarryFlag(int dataOne, int method)
    {
	carryCalculated = false;
	carryOne = dataOne;
	carryMethod = method;
    }

    public void setCarryFlag(int dataOne, int dataTwo, int method)
    {
	carryCalculated = false;
	carryOne = dataOne;
	carryTwo = dataTwo;
	carryMethod = method;
    }

    public void setCarryFlag(int dataOne, int dataTwo, int dataThree, int method)
    {
	carryCalculated = false;
	carryOne = dataOne;
	carryTwo = dataTwo;
	carryThree = dataThree;
	carryMethod = method;
    }

    private int zeroOne;
    private boolean zeroCalculated;
    private int zeroMethod;

    public boolean getZeroFlag()
    {
	if (zeroCalculated)
	    return eflagsZero;
	else {
	    zeroCalculated = true;
	    return (eflagsZero = (zeroOne == 0));
	}
    }

    public void setZeroFlag(boolean value)
    {
	zeroCalculated = true;
	eflagsZero = value;
    }

    public void setZeroFlag(int data)
    {
	zeroCalculated = false;
	zeroOne = data;
    }

    private int signOne;
    private boolean signCalculated;
    private int signMethod;

    public boolean getSignFlag()
    {
	if (signCalculated)
	    return eflagsSign;
	else {
	    signCalculated = true;
	    return (eflagsSign = (signOne < 0));
	}
    }

    public void setSignFlag(boolean value)
    {
	signCalculated = true;
	eflagsSign = value;
    }

    public void setSignFlag(int data)
    {
	signCalculated = false;
	signOne = data;
    }
}
