/*
    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.memory.codeblock.basic;

import org.jpc.emulator.memory.codeblock.*;
import org.jpc.emulator.processor.*;
import org.jpc.emulator.processor.fpu64.*;
import org.jpc.emulator.memory.*;

public class ProtectedModeFirstStageCodeBlock extends FirstStageCodeBlock implements ProtectedModeCodeBlock
{
    //Static exceptions for those that can be
    //private static final ProcessorException exceptionDB = new ProcessorException(Processor.PROC_EXCEPTION_DB);
    private static final ProcessorException exceptionBP = new ProcessorException(Processor.PROC_EXCEPTION_BP, false);
    private static final ProcessorException exceptionOF = new ProcessorException(Processor.PROC_EXCEPTION_OF, false);
    private static final ProcessorException exceptionBR = new ProcessorException(Processor.PROC_EXCEPTION_BR, true);
    private static final ProcessorException exceptionDF = new ProcessorException(Processor.PROC_EXCEPTION_DF, 0, true);
    //private static final ProcessorException exceptionMF9 = new ProcessorException(Processor.PROC_EXCEPTION_MF_09); //not used beyond 386
    //private static final ProcessorException exceptionTS = new ProcessorException(Processor.PROC_EXCEPTION_TS);
    //private static final ProcessorException exceptionNP = new ProcessorException(Processor.PROC_EXCEPTION_NP);
    //private static final ProcessorException exceptionSS = new ProcessorException(Processor.PROC_EXCEPTION_SS);
    //private static final ProcessorException exceptionGP = new ProcessorException(Processor.PROC_EXCEPTION_GP);
    //private static final ProcessorException exceptionPF = new ProcessorException(Processor.PROC_EXCEPTION_PF);
    private static final ProcessorException exceptionMF10 = new ProcessorException(Processor.PROC_EXCEPTION_MF_10, true);
    private static final ProcessorException exceptionAC = new ProcessorException(Processor.PROC_EXCEPTION_AC, 0, true);
    private static final ProcessorException exceptionMC = new ProcessorException(Processor.PROC_EXCEPTION_MC, true); //true or undefined (dep on arch)
    private static final ProcessorException exceptionXF = new ProcessorException(Processor.PROC_EXCEPTION_XF, true);
    
    private boolean eipUpdated;

    public ProtectedModeFirstStageCodeBlock()
    {
    }

    public ProtectedModeFirstStageCodeBlock(int[] microcodes, int[] x86lengths)
    {
	super(microcodes, x86lengths);
    }

    public String toString()
    {
	return "ProtectedModeFirstStageCodeBlock";
    }

    public int execute(Processor cpu)
    {
        this.cpu = cpu;
	this.fpu = cpu.fpu;

	int lastMicrocodesPosition = 0;
        executeCount = this.getX86Count();
	eipUpdated = false;

	try 
        {
	    for (microcodesPosition = 0; microcodesPosition < microcodes.length;) 
            {
		dispatchOpcode();
		lastMicrocodesPosition = microcodesPosition;        
            }
	} 
        catch (ProcessorException e) 
        {
	    if (e.getVector() == -1)
		throw new IllegalStateException("Execute Failed");

	    if (eipUpdated) {
		if (e.pointsToSelf())
		    cpu.eip -= cumulativeX86Length[lastMicrocodesPosition];
		if (lastMicrocodesPosition >= 2)
		    cpu.eip += cumulativeX86Length[lastMicrocodesPosition - 2];
	    } else {
		if (e.pointsToSelf())
		    microcodesPosition = lastMicrocodesPosition;
		
		if (microcodesPosition > 0)
		    eipUpdate();
	    }

	    if (e.getVector() != Processor.PROC_EXCEPTION_PF) {
		System.out.println();
		System.out.println("EIP: 0x" + Integer.toHexString(cpu.eip));
		e.printStackTrace();
	    }

	    cpu.handleProtectedModeException(e.getVector(), e.hasErrorCode(), e.getErrorCode());
	}

        return Math.max(executeCount, 0);
    }

    private final void dispatchOpcode() throws ProcessorException
    {
	switch (getMicrocode()) {
	case EIP_UPDATE: eipUpdate(); return;

	case AAA: aaa(); return;
	case AAD: aad(); return;
	case AAM: aam(); return;
	case AAS: aas(); return;

	case ADC_O8:  adc_o8();  return;
	case ADC_O16: adc_o16(); return;
	case ADC_O32: adc_o32(); return;

	case ADD_O8:  add_o8();  return;
	case ADD_O16: add_o16(); return;
	case ADD_O32: add_o32(); return;

	case AND_O8:  and_o8();  return;
	case AND_O16: and_o16(); return;
	case AND_O32: and_o32(); return;

	case ARPL: arpl(); return;

	case BOUND_O32_A32: bound_o32_a32(); return;

	case BSF_O16: bsf_o16(); return;
	case BSF_O32: bsf_o32(); return;

	case BSR_O16: bsr_o16(); return;
	case BSR_O32: bsr_o32(); return;

	case BSWAP: bswap(); return;

	case BT_O16: bt_o16(); return;
	case BT_O32: bt_o32(); return;
	case BT_O16_O8: bt_o16_o8(); return;
	case BT_O32_O8: bt_o32_o8(); return;

	case BTC_O16: btc_o16(); return;
	case BTC_O32: btc_o32(); return;
	case BTC_O32_O8: btc_o32_o8(); return;

	case BTR_O16: btr_o16(); return;
	case BTR_O32: btr_o32(); return;
	case BTR_O32_O8: btr_o32_o8(); return;

	case BTS_O16: bts_o16(); return;
	case BTS_O32: bts_o32(); return;
	case BTS_O32_O8: bts_o32_o8(); return;

	case CALL_O32_A32:
	case CALL_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    call_o32_a32(); return;
		} else {
		    call_o32_a16(); return;
		}
	    }

	case CALL_O16_A32:
	case CALL_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    call_o16_a32(); return;
		} else {
		    call_o16_a16(); return;
		}
	    }

	case CALLF_O32_A32:
	case CALLF_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    callf_o32_a32(); return;
		} else
		    break;
	    }

	case CALLF_O16_A32:
	case CALLF_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else
		    callf_o16_a16(); return;
	    }

	case CALLN_O32_A32:
	case CALLN_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    calln_o32_a32(); return;
		} else {
		    calln_o32_a16(); return;
		}
	    }

	case CBW: cbw(); return;
	case CDQ: cdq(); return;

	case CLC: clc(); return;
	case CLD: cld(); return;
	case CLI: cli(); return;

	case CMC: cmc(); return;

	case CLTS: clts(); return;

	case CWD: cwd(); return;

	case CMOVB_O32:   cmovb_o32();   return;
	case CMOVNB_O32:  cmovnb_o32();  return;
	case CMOVBE_O32:  cmovbe_o32();  return;
	case CMOVNBE_O32: cmovnbe_o32(); return;
	case CMOVL_O32:   cmovl_o32();   return;
	case CMOVNL_O32:  cmovnl_o32();  return;
	case CMOVLE_O32:  cmovle_o32();  return;
	case CMOVNLE_O32: cmovnle_o32(); return;
	case CMOVO_O32:   cmovo_o32();   return;
	case CMOVNO_O32:  cmovno_o32();  return;
	case CMOVP_O32:   cmovp_o32();   return;
	case CMOVNP_O32:  cmovnp_o32();  return;
	case CMOVS_O32:   cmovs_o32();   return;
	case CMOVNS_O32:  cmovns_o32();  return;
	case CMOVZ_O32:   cmovz_o32();   return;
	case CMOVNZ_O32:  cmovnz_o32();  return;

	case CMP_O8:  cmp_o8();  return;
	case CMP_O16: cmp_o16(); return;
	case CMP_O32: cmp_o32(); return;

	case CMPS_O8_A16: cmps_o8_a16(); return;
	case CMPS_O16_A16: cmps_o16_a16(); return;
	case CMPS_O32_A16: cmps_o32_a16(); return;
	case CMPS_O8_A32:   cmps_o8_a32();   return;
	case CMPS_O16_A32:  cmps_o16_a32();  return;
	case CMPS_O32_A32:  cmps_o32_a32();  return;

	case CMPXCHG_O8:  cmpxchg_o8();  return;
	case CMPXCHG_O16: cmpxchg_o16(); return;
	case CMPXCHG_O32: cmpxchg_o32(); return;

	case CMPXCHG8B: cmpxchg8b(); return;

	case CPUID: cpuid(); return;

	case CWDE: cwde(); return;

	case DAA: daa(); return;
	case DAS: das(); return;

	case DEC_O8:  dec_o8();  return;
	case DEC_O16: dec_o16(); return;
	case DEC_O32: dec_o32(); return;

	case DIV_O8:  div_o8();  return;
	case DIV_O16: div_o16(); return;
	case DIV_O32: div_o32(); return;

	case ENTER_O32_A32:
	case ENTER_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    enter_o32_a32(); return;
		} else
		    break;
	    }

	case ENTER_O16_A32:
	case ENTER_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    enter_o16_a32(); return;
		} else
		    break;
	    }

	case F2XM1:    f2xm1();    return;
	case FABS: break; //590;
	case FADD:     fadd();     return;
	case FADD_SR:  fadd_sr();  return;
	case FADD_DR:  fadd_dr();  return;
	case FADDP:    faddp();    return;
	case FBLD: break; //551;
	case FBSTP: break; //553;
	case FCHS:     fchs();     return; //589;
	case FCLEX:    fclex();    return;//617;       
        case FCMOVB: break; //565;
	case FCMOVNB: break; //569;
	case FCMOVBE: break; //567;
	case FCMOVNBE: break; //571;
	case FCMOVE: break; //566;
        case FCMOVNE: break; //570;
	case FCMOVU: break; //568;
	case FCMOVNU: break; //572;
	case FCOM: fcom(); return; //557;
	case FCOM_SR:  fcom_sr();  return;
	case FCOM_DR:  fcom_dr();  return;
	case FCOMI: break; //574;
	case FCOMIP: break; //587;
	case FCOMP:  fcomp(); return;//558;
	case FCOMP_SR: fcomp_sr(); return;
	case FCOMP_DR: fcomp_dr(); return;
	case FCOMPP:   fcompp();   return;//619;
	case FCOS: break; //615;
    	case FDECSTP: break; //606;
	case FDIV:     fdiv();     return;
	case FDIV_SR:  fdiv_sr();  return;
	case FDIV_DR:  fdiv_dr();  return;
	case FDIVP:    fdivp();    return;
	case FDIVR:    fdivr();    return;
	case FDIVR_SR: fdivr_sr(); return;
	case FDIVR_DR: fdivr_dr(); return;
	case FDIVRP:   fdivrp();   return;
	case FFREE: break; //575;
	case FIADD_WI: break; //539;
	case FIADD_DI: break; //510;
	case FICOM_WI: break; //541;
	case FICOM_DI: break; //512;
	case FICOMP_WI: break; //542;
	case FICOMP_DI: break; //513;
	case FIDIV_WI: break; //545;
	case FIDIV_DI: break; //516;
	case FIDIVR_WI:  fidivr_wi(); return; //546;
	case FIDIVR_DI:  fidivr_di(); return; //517;
	case FILD_WI:  fild_wi(); return; //547;
	case FILD_DI:  fild_di();  return;
	case FILD_QI:  fild_qi();  return;
	case FIMUL_WI: break; //540;
	case FIMUL_DI: break; //511;
	case FINCSTP: break; //607;
	case FIST_WI:  fist_wi();  return;
	case FIST_DI:  fist_di();  return;
	case FISTP_WI: fistp_wi(); return;
	case FISTP_DI: fistp_di(); return;
	case FISTP_QI: fistp_qi(); return;
	case FISTTP_WI: break; //548;
	case FISTTP_DI: break; //519;
	case FISTTP_QI: break; //533;
	case FISUB_WI: break; //543;
	case FISUB_DI: break; //514;
	case FISUBR_WI: break; //544;
	case FISUBR_DI: break; //515;
	case FLD:      fld();      return;
	case FLD_SR:   fld_sr();   return;
	case FLD_DR:   fld_dr();   return;
	case FLD_XR: break; //522;
	case FLD1:     fld1();     return;
	case FLDL2E: break; //595;
	case FLDL2T: break; //594;
	case FLDLG2: break; //597;
	case FLDLN2: break; //598;       
	case FLDPI: break; //596;
	case FLDZ:     fldz();     return;
	case FLDCW:    fldcw();    return;
	case FMUL:     fmul();     return;
	case FMUL_SR:  fmul_sr();  return;
	case FMUL_DR:  fmul_dr();  return;
	case FMULP:    fmulp();    return;
	case FNINIT:   fninit();   return;
	case FNOP: break; //588;
	case FPATAN: break; //603;
	case FPREM:    fprem();    return;
	case FPREM1:   fprem1();   return;
	case FPTAN: break; //602;
	case FRNDINT:  frndint();  return; //612;
	case FSCALE:   fscale();   return;
        case FSIN: break; //614;
	case FSINCOS:  fsincos();  return;
	case FSQRT:    fsqrt();    return;
	case FST: break; //576;
	case FST_SR: break; //504;
	case FST_DR:   fst_dr();   return;
	case FSTP:     fstp();     return;
	case FSTP_SR:  fstp_sr();  return; //505;
	case FSTP_DR:  fstp_dr();  return;
	case FSTP_XR: break; //523;
	case FSTCW:    fstcw();    return;
	case FSTSW:    fstsw();    return;
	case FSUB:     fsub();     return;
	case FSUB_SR:  fsub_sr();  return;
	case FSUB_DR:  fsub_dr();  return;
	case FSUBP:    fsubp();    return;
	case FSUBR:    fsubr();    return;
	case FSUBR_SR: fsubr_sr(); return;
	case FSUBR_DR: fsubr_dr(); return;
	case FSUBRP:   fsubrp();   return;
	case FTST:     ftst();     return;
	case FUCOM:    fucom();    return;
	case FUCOMI: break; //573;
	case FUCOMIP: break; //586;
	case FUCOMP:   fucomp();   return;
	case FUCOMPP:  fucompp();  return;
	case FXAM: break; //592;
	case FXCH:     fxch();     return;
	case FXTRACT: break; //604;
	case FYL2X:    fyl2x();    return;
	case FYL2XP1:  fyl2xp1();  return;

	    //case FLDENV_1428: //506;
	    //case FSTENV_1428: //508;
	    
	    //case FRSTOR_98108: //536;
	    //case FSAVE_98108: //537;

	case HLT: hlt(); return;

	case IDIV_O8:  idiv_o8();  return;
	case IDIV_O16: idiv_o16(); return;
	case IDIV_O32: idiv_o32(); return;

	case IMUL_O16: imul_o16(); return;
	case IMUL_O32: imul_o32(); return;

	case IMUL_REGA_O8:  imul_rega_o8();  return;
	case IMUL_REGA_O16: imul_rega_o16(); return;
	case IMUL_REGA_O32: imul_rega_o32(); return;

	case IN_O8_O8:   in_o8_o8();  return;
	case IN_O8_O16:  in_o8_o16(); return;
	case IN_O16_O16: in_o16_o16(); return;
	case IN_O32_O16: in_o32_o16(); return;

	case INC_O8:  inc_o8();  return;
	case INC_O16: inc_o16(); return;
	case INC_O32: inc_o32(); return;

	case INS_O8_A32: ins_o8_a32(); return;
	case INS_O16_A32: ins_o16_a32(); return;
	case INS_O32_A32: ins_o32_a32(); return;

	case INT_O32_A32: int_o32_a32(); return;

	case INVD: invd(); return;

	case INVLPG: invlpg(); return;

	case IRET_O32_A32:
	case IRET_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    iret_o32_a32(); return;
		} else
		    break;
	    }

	case IRET_O16_A32:
	case IRET_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else
		    iret_o16_a16(); return;
	    }

	case JCXZ_A32: jcxz_a32(); return;

	case JB_O8:    jb_o8();    return;
	case JB_O16:   jb_o16();   return;
	case JB_O32:   jb_o32();   return;
	case JNB_O8:   jnb_o8();   return;
	case JNB_O16:  jnb_o16();  return;
	case JNB_O32:  jnb_o32();  return;

	case JBE_O8:   jbe_o8();   return;
	case JBE_O16:  jbe_o16();  return;
	case JBE_O32:  jbe_o32();  return;
	case JNBE_O8:  jnbe_o8();  return;
	case JNBE_O16: jnbe_o16(); return;
	case JNBE_O32: jnbe_o32(); return;

	case JL_O8:    jl_o8();    return;
	case JL_O16:   jl_o16();   return;
	case JL_O32:   jl_o32();   return;
	case JNL_O8:   jnl_o8();   return;
	case JNL_O16:  jnl_o16();  return;
	case JNL_O32:  jnl_o32();  return;

	case JLE_O8:   jle_o8();   return;
	case JLE_O16:  jle_o16();  return;
	case JLE_O32:  jle_o32();  return;
	case JNLE_O8:  jnle_o8();  return;
	case JNLE_O16: jnle_o16(); return;
	case JNLE_O32: jnle_o32(); return;

	case JO_O8:    jo_o8();    return;
	case JO_O16:   jo_o16();   return;
	case JO_O32:   jo_o32();   return;
	case JNO_O8:   jno_o8();   return;
	case JNO_O16:  jno_o16();  return;
	case JNO_O32:  jno_o32();  return;

	case JP_O8:    jp_o8();    return;
	case JP_O16:   jp_o16();   return;
	case JP_O32:   jp_o32();   return;
	case JNP_O8:   jnp_o8();   return;
	case JNP_O16:  jnp_o16();  return;
	case JNP_O32:  jnp_o32();  return;

	case JS_O8:    js_o8();    return;
	case JS_O16:   js_o16();   return;
	case JS_O32:   js_o32();   return;
	case JNS_O8:   jns_o8();   return;
	case JNS_O16:  jns_o16();  return;
	case JNS_O32:  jns_o32();  return;

	case JZ_O8:   jz_o8();   return;
	case JZ_O16:  jz_o16();  return;
	case JZ_O32:  jz_o32();  return;
	case JNZ_O8:  jnz_o8();  return;
	case JNZ_O16: jnz_o16(); return;
	case JNZ_O32: jnz_o32(); return;

	case JMP_O8_SHORT: jmp_o8_short(); return;
	case JMP_O16_NEAR_RELATIVE: jmp_o16_near_relative(); return;
	case JMP_O32_NEAR_RELATIVE: jmp_o32_near_relative(); return;
	case JMP_O16_NEAR_ABSOLUTE: jmp_o16_near_absolute(); return;
	case JMP_O32_NEAR_ABSOLUTE: jmp_o32_near_absolute(); return;

	case JMP_O16_FAR: jmp_o16_far(); return;
	case JMP_O32_FAR: jmp_o32_far(); return;

	case LOOP_O16_A16: loop_o16_a16(); return;
	case LOOP_O32_A32:   loop_o32_a32();   return;
	case LOOPNZ_O32_A32: loopnz_o32_a32(); return;

	case LEA_O16_A16: lea_o16_a16(); return;
	case LEA_O32_A32: lea_o32_a32(); return;

	case LEAVE_O32_A32:
	case LEAVE_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    leave_o32_a32(); return;
		} else {
		    leave_o32_a16(); return;
		}
	    }

	case LEAVE_O16_A32:
	case LEAVE_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    leave_o16_a32(); return;
		} else {
		    leave_o16_a16(); return;
		}
	    }

	case LGDT_O16: lgdt_o16(); return;
	case LGDT_O32: lgdt_o32(); return;
	case LIDT_O16: lidt_o16(); return;
	case LIDT_O32: lidt_o32(); return;
	case LLDT: lldt(); return;

	case LMSW: lmsw(); return;

	case LODS_O8_A16:  lods_o8_a16();  return;
	case LODS_O16_A16: lods_o16_a16(); return;
	case LODS_O32_A16: lods_o32_a16(); return;
	case LODS_O8_A32:  lods_o8_a32();  return;
	case LODS_O16_A32: lods_o16_a32(); return;
	case LODS_O32_A32: lods_o32_a32(); return;

	case LTR: ltr(); return;

	case LSS_O32_A32: lss_o32_a32(); return;

	case MOV_O8:  mov_o8();  return;
	case MOV_O16: mov_o16(); return;
	case MOV_O32: mov_o32(); return;

	case MOVS_O8_A16:  movs_o8_a16();  return;
	case MOVS_O16_A16: movs_o16_a16(); return;
	case MOVS_O8_A32:  movs_o8_a32();  return;
	case MOVS_O16_A32: movs_o16_a32(); return;
	case MOVS_O32_A32: movs_o32_a32(); return;
	case MOVS_O32_A16: movs_o32_a16(); return;

	case MOVSX_O16_O8: movsx_o16_o8(); return;
	case MOVSX_O32_O8: movsx_o32_o8(); return;
	case MOVSX_O16_O16: movsx_o16_o16(); return;
	case MOVSX_O32_O16: movsx_o32_o16(); return;

	case MOVZX_O16_O8: movzx_o16_o8(); return;
	case MOVZX_O32_O8: movzx_o32_o8(); return;
	case MOVZX_O16_O16: movzx_o16_o16(); return;
	case MOVZX_O32_O16: movzx_o32_o16(); return;

	case MOV_TO_CR_O32: mov_to_cr_o32(); return;
	case MOV_TO_DR_O32: mov_to_dr_o32(); return;
	case MOV_TO_SEG: mov_to_seg(); return;

	case MUL_O8:  mul_o8();  return;
	case MUL_O16: mul_o16(); return;
	case MUL_O32: mul_o32(); return;

	case NEG_O8:  neg_o8();  return;
	case NEG_O16: neg_o16(); return;
	case NEG_O32: neg_o32(); return;

	case NOP: return;

	case NOT_O8:  not_o8();  return;
	case NOT_O16: not_o16(); return;
	case NOT_O32: not_o32(); return;

	case OR_O8:  or_o8();  return;
	case OR_O16: or_o16(); return;
	case OR_O32: or_o32(); return;

	case OUT_O8_O8:   out_o8_o8();   return;
	case OUT_O16_O8:  out_o16_o8();  return;
	case OUT_O16_O16: out_o16_o16(); return;
	case OUT_O16_O32: out_o16_o32(); return;

	case OUTS_O8_A32: outs_o8_a32(); return;
	case OUTS_O16_A32: outs_o16_a32(); return;
	case OUTS_O32_A32: outs_o32_a32(); return;

	case POP_O32_A32:
	case POP_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    pop_o32_a32(); return;
		} else {
		    pop_o32_a16(); return;
		}
	    }

	case POP_O16_A32:
	case POP_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    pop_o16_a32(); return;
		} else {
		    pop_o16_a16(); return;
		}
	    }

	case POPA_O32_A32:
	case POPA_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    popa_o32_a32(); return;
		} else
		    break;
	    }

	case POPA_O16_A32:
	case POPA_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else {
		    popa_o16_a16(); return;
		}
	    }

	case POPF_O32_A32:
	case POPF_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    popf_o32_a32(); return;
		} else {
		    popf_o32_a16(); return;
		}
	    }

	case POPF_O16_A32:
	case POPF_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    popf_o16_a32(); return;
		} else
		    break;
	    }

	case PUSH_O16_A32:
	case PUSH_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    push_o16_a32(); return;
		} else {
		    push_o16_a16(); return;
		}
	    }
	case PUSH_O32_A32:
	case PUSH_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    push_o32_a32(); return;
		} else {
		    push_o32_a16(); return;
		}
	    }
	case PUSHA_O32_A32:
	case PUSHA_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    pusha_o32_a32(); return;
		} else
		    break;
	    }
	case PUSHF_O32_A32:
	case PUSHF_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    pushf_o32_a32(); return;
		} else {
		    pushf_o32_a16(); return;
		}
	    }

	case PUSHF_O16_A32:
	case PUSHF_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    pushf_o16_a32(); return;
		} else
		    pushf_o16_a16(); return;
	    }

	case RDMSR: rdmsr(); return;
	case RDTSC: rdtsc(); return;

	case REPE_CMPS_O8_A16:  repe_cmps_o8_a16();  return;
	case REPE_CMPS_O16_A16: repe_cmps_o16_a16(); return;
	case REPE_CMPS_O8_A32:   repe_cmps_o8_a32();   return;
	case REPE_CMPS_O16_A32:  repe_cmps_o16_a32();  return;
	case REPE_CMPS_O32_A32:  repe_cmps_o32_a32();  return;

	case REPNE_CMPS_O8_A32:   repne_cmps_o8_a32();   return;
	case REPNE_CMPS_O16_A32:  repne_cmps_o16_a32();  return;
	case REPNE_CMPS_O32_A32:  repne_cmps_o32_a32();  return;

	case REP_INS_O8_A32: rep_ins_o8_a32(); return;
	case REP_INS_O16_A32: rep_ins_o16_a32(); return;
	case REP_INS_O32_A32: rep_ins_o32_a32(); return;

	case REP_LODS_O8_A32:  rep_lods_o8_a32();  return;
	case REP_LODS_O16_A32: rep_lods_o16_a32(); return;
	case REP_LODS_O32_A32: rep_lods_o32_a32(); return;

	case REP_MOVS_O8_A16:  rep_movs_o8_a16();  return;
	case REP_MOVS_O16_A16: rep_movs_o16_a16(); return;
	case REP_MOVS_O32_A16: rep_movs_o32_a16(); return;
	case REP_MOVS_O8_A32:  rep_movs_o8_a32();  return;
	case REP_MOVS_O16_A32: rep_movs_o16_a32(); return;
	case REP_MOVS_O32_A32: rep_movs_o32_a32(); return;

	case REP_OUTS_O8_A32: rep_outs_o8_a32(); return;
	case REP_OUTS_O16_A32: rep_outs_o16_a32(); return;
	case REP_OUTS_O32_A32: rep_outs_o32_a32(); return;

	case REPE_SCAS_O8_A16:  repe_scas_o8_a16();  return;
	case REPE_SCAS_O16_A16: repe_scas_o16_a16(); return;
	case REPE_SCAS_O32_A16: repe_scas_o32_a16(); return;
	case REPE_SCAS_O8_A32:  repe_scas_o8_a32();  return;
	case REPE_SCAS_O16_A32: repe_scas_o16_a32(); return;
	case REPE_SCAS_O32_A32: repe_scas_o32_a32(); return;

	case REPNE_SCAS_O8_A16:  repne_scas_o8_a16();  return;
	case REPNE_SCAS_O16_A16: repne_scas_o16_a16(); return;
	case REPNE_SCAS_O32_A16: repne_scas_o32_a16(); return;
	case REPNE_SCAS_O8_A32:  repne_scas_o8_a32();  return;
	case REPNE_SCAS_O16_A32: repne_scas_o16_a32(); return;
	case REPNE_SCAS_O32_A32: repne_scas_o32_a32(); return;

	case REP_STOS_O8_A16:  rep_stos_o8_a16();  return;
	case REP_STOS_O16_A16: rep_stos_o16_a16(); return;
	case REP_STOS_O32_A16: rep_stos_o32_a16(); return;
	case REP_STOS_O8_A32:  rep_stos_o8_a32();  return;
	case REP_STOS_O16_A32: rep_stos_o16_a32(); return;
	case REP_STOS_O32_A32: rep_stos_o32_a32(); return;

	case RET_O16_A32:
	case RET_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else {
		    ret_o16_a16(); return;
		}
	    }

	case RET_O32_A32:
	case RET_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    ret_o32_a32(); return;
		} else {
		    ret_o32_a16(); return;
		}
	    }

	case RETF_O32_A32:
	case RETF_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    retf_o32_a32(); return;
		} else
		    break;
	    }

	case RETF_O16_A32:
	case RETF_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else {
		    retf_o16_a16(); return;
		}
	    }

	case RET_IW_O32_A32:
	case RET_IW_O32_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    ret_iw_o32_a32(); return;
		} else
		    break;
	    }

	case RETF_IW_O16_A32:
	case RETF_IW_O16_A16:
	    {
		if (cpu.ss.getDefaultSizeFlag()) {
		    break;
		} else {
		    retf_iw_o16_a16(); return;
		}
	    }

	case RCL_O8:  rcl_o8();  return;
	case RCL_O16: rcl_o16(); return;
	case RCL_O32: rcl_o32(); return;

	case RCR_O8:  rcr_o8();  return;
	case RCR_O16: rcr_o16(); return;
	case RCR_O32: rcr_o32(); return;

	case ROL_O8:  rol_o8();  return;
	case ROL_O16: rol_o16(); return;
	case ROL_O32: rol_o32(); return;

	case ROR_O8:  ror_o8();  return;
	case ROR_O16: ror_o16(); return;
	case ROR_O32: ror_o32(); return;

	case SAHF:    sahf();    return;

	case SAR_O8:  sar_o8();  return;
	case SAR_O16: sar_o16(); return;
	case SAR_O32: sar_o32(); return;

	case SBB_O8:  sbb_o8();  return;
	case SBB_O16: sbb_o16(); return;
	case SBB_O32: sbb_o32(); return;

	case SCAS_O8_A16:  scas_o8_a16();  return;
	case SCAS_O16_A16: scas_o16_a16(); return;
	case SCAS_O32_A16: scas_o32_a16(); return;
	case SCAS_O8_A32:  scas_o8_a32();  return;
	case SCAS_O16_A32: scas_o16_a32(); return;
	case SCAS_O32_A32: scas_o32_a32(); return;

	case SETB:   setb();   return;
	case SETNB:  setnb();  return;
	case SETBE:  setbe();  return;
	case SETNBE: setnbe(); return;
	case SETL:   setl();   return;
	case SETNL:  setnl();  return;
	case SETLE:  setle();  return;
	case SETNLE: setnle(); return;
	case SETO:   seto();   return;
	case SETNO:  setno();  return;
	case SETP:   setp();   return;
	case SETNP:  setnp();  return;
	case SETS:   sets();   return;
	case SETNS:  setns();  return;
	case SETZ:   setz();   return;
	case SETNZ:  setnz();  return;

	case SGDT_O32: sgdt_o32(); return;

	case SHL_O8:  shl_o8();  return;
	case SHL_O16: shl_o16(); return;
	case SHL_O32: shl_o32(); return;

	case SHLD_O32: shld_o32(); return;
	case SHLD_O16: shld_o16(); return;

	case SHR_O8:  shr_o8();  return;
	case SHR_O16: shr_o16(); return;
	case SHR_O32: shr_o32(); return;

	case SHRD_O32: shrd_o32(); return;
	case SHRD_O16: shrd_o16(); return;

	case SLDT: sldt(); return;

	case SMSW: smsw(); return;

	case STC: stc(); return;
	case STD: std(); return;
	case STI: sti(); return;

	case STOS_O8_A16:  stos_o8_a16();  return;
	case STOS_O16_A16: stos_o16_a16(); return;
	case STOS_O32_A16: stos_o32_a16(); return;
	case STOS_O8_A32:  stos_o8_a32();  return;
	case STOS_O16_A32: stos_o16_a32(); return;
	case STOS_O32_A32: stos_o32_a32(); return;

	case SUB_O8:  sub_o8();  return;
	case SUB_O16: sub_o16(); return;
	case SUB_O32: sub_o32(); return;

	case TEST_O8:  test_o8();  return;
	case TEST_O16: test_o16(); return;
	case TEST_O32: test_o32(); return;

	case UD2: ud2(); return;

	case WAIT: waitOp(); return;

	case WBINVD: wbinvd(); return;

	case WRMSR: wrmsr(); return;

	case XADD_O8:  xadd_o8();  return;
	case XADD_O16: xadd_o16(); return;
	case XADD_O32: xadd_o32(); return;

	case XCHG_O8:  xchg_o8();  return;
	case XCHG_O16: xchg_o16(); return;
	case XCHG_O32: xchg_o32(); return;

	case XLAT: xlat(); return;

	case XOR_O8:  xor_o8();  return;
	case XOR_O16: xor_o16(); return;
	case XOR_O32: xor_o32(); return;
	}
	System.err.println("Protected Mode: Unimplemented Opcode: " + microcodes[microcodesPosition - 1]);
	throw new ProcessorException(-1, true);
    }

    private final void eipUpdate()
    {
	if (!eipUpdated) {
	    eipUpdated = true;
	    cpu.eip = cpu.eip + cumulativeX86Length[microcodesPosition - 1];
	}
    }

    private final void arpl()
    {
	int srcSelector = getShortOperand() & 0xffff;
	int destSelector = getShortOperand() & 0xffff;

	if ((destSelector & 0x3) < (srcSelector & 0x3)) {
	    cpu.eflagsZero = true;
	    setShortOperand((short)((destSelector & ~0x3) | (srcSelector & 0x3)));
	} else {
	    cpu.eflagsZero = false;
	    skipOperand();
	}
    }

    private final void bound_o32_a32() 
    {
	long arrayBounds = getLongOperand();
	int lowerBound = (int)arrayBounds;
	int upperBound = (int)(arrayBounds >> 32);

	int index = getIntOperand();

	if ((index < lowerBound) || (index > upperBound))
	    throw exceptionBR;
    }

    private final void call_o32_a32() 
    {
	int tempEIP = cpu.eip + getIntOperand();
        
        cpu.cs.checkAddress(tempEIP);

	if ((cpu.esp < 4) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord(cpu.esp - 4, cpu.eip);

	cpu.esp -= 4;
	cpu.eip = tempEIP;
    }

    private final void call_o32_a16() 
    {
	int tempEIP = cpu.eip + getIntOperand();
        
        cpu.cs.checkAddress(tempEIP);

	if (((0xffff & cpu.esp) < 4) && ((0xffff & cpu.esp) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord((cpu.esp - 4) & 0xffff, cpu.eip);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);

	cpu.eip = tempEIP;
    }

    private final void call_o16_a32() 
    {
	int tempEIP = (cpu.eip + getShortOperand()) & 0xffff;
        
        cpu.cs.checkAddress(tempEIP);

	if ((cpu.esp < 2) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setWord(cpu.esp - 2, (short)cpu.eip);

	cpu.esp -= 2;
	cpu.eip = tempEIP;
    }

    private final void call_o16_a16() 
    {
	int tempEIP = (cpu.eip + getShortOperand()) & 0xffff;
        
        cpu.cs.checkAddress(tempEIP);

	if (((0xffff & cpu.esp) < 2) && ((0xffff & cpu.esp) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setWord((cpu.esp - 2) & 0xffff, (short)cpu.eip);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);

	cpu.eip = tempEIP;
    }

    private final void callf_o32_a32() 
    {
	long target = getLongOperand();
	int segmentSelector = (int)((target >> 32) & 0xffffl);
	int newEIP = (int) target;
	
        Segment newSegment = cpu.getSegment(segmentSelector);
        if (newSegment == SegmentFactory.NULL_SEGMENT)
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

	switch (newSegment.getType()) 
        { // segment type	    
	default: // not a valid segment descriptor for a jump
	    System.err.println("CALL -> Invalid Segment Type");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
	case 0x05: // Task Gate
	    System.err.println("CALL -> Task Gate");
	    throw new ProcessorException(-1, true);
	case 0x09: // TSS (Not Busy)
	case 0x0b: // TSS (Busy)
	    System.err.println("CALL -> TSS (Task-State Segment)");
	    throw new ProcessorException(-1, true);
	case 0x0c: // Call Gate
	    System.err.println("CALL -> Call Gate");
	    throw new ProcessorException(-1, true);
	case 0x18: // Non-conforming Code Segment
	case 0x19: // Non-conforming Code Segment
	case 0x1a: // Non-conforming Code Segment
	case 0x1b: // Non-conforming Code Segment
	    {
		if ((newSegment.getRPL() > cpu.getCPL()) || (newSegment.getDPL() != cpu.getCPL()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);

		if ((cpu.esp < 8) && (cpu.esp > 0))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
		
                newSegment.checkAddress(newEIP);

		cpu.ss.setDoubleWord(cpu.esp - 4, cpu.cs.getSelector());
		cpu.ss.setDoubleWord(cpu.esp - 8, cpu.eip);
		cpu.esp -= 8;

		cpu.cs = newSegment;
		cpu.cs.setRPL(cpu.getCPL());
		cpu.eip = newEIP;
		return;
	    }
	case 0x1c: // Conforming Code Segment (Not Readable & Not Accessed)
        case 0x1d: // Conforming Code Segment (Not Readable & Accessed)
        case 0x1e: // Conforming Code Segment (Readable & Not Accessed)
        case 0x1f: // Conforming Code Segment (Readable & Accessed)
	    System.err.println("CALL -> Conforming Code Segment");
	    throw new ProcessorException(-1, true);
        }
    }

    private final void callf_o16_a16() 
    {
	int target = getIntOperand();
	int segmentSelector = (target >>> 16) & 0xffff;
	int newEIP = target & 0xffff;
	
        Segment newSegment = cpu.getSegment(segmentSelector);
        if (newSegment == SegmentFactory.NULL_SEGMENT)
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

	switch (newSegment.getType()) 
        { // segment type	    
	default: // not a valid segment descriptor for a jump
	    System.err.println("CALL -> Invalid Segment Type");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
	case 0x01:
	case 0x03:
	    System.err.println("CALL -> 16bit TSS (Task-State Segment)");
	    throw new ProcessorException(-1, true);
	case 0x04: // 16bit Call Gate
	    {
		if ((newSegment.getDPL() < cpu.getCPL()) | (newSegment.getRPL() > newSegment.getDPL()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);
		
		int targetSegmentSelector = ((SegmentFactory.GateSegment)newSegment).getTargetSegment();
		
		Segment targetSegment;
		try {
		    targetSegment = cpu.getSegment(targetSegmentSelector);
		} catch (ProcessorException e) {
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, targetSegmentSelector, true);
		}
		
		if (targetSegment.getDPL() > cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, targetSegmentSelector, true);
		
		switch(targetSegment.getType()) {
		default:
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, targetSegmentSelector, 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(Processor.PROC_EXCEPTION_NP, targetSegmentSelector, true);
			
			if (targetSegment.getDPL() < cpu.getCPL()) {
			    //MORE-PRIVILEGE
			    int newStackSelector = 0;
			    int newESP = 0;
			    if ((cpu.tss.getType() & 0x8) != 0) {
				int tssStackAddress = (targetSegment.getDPL() * 8) + 4;
				if ((tssStackAddress + 7) > cpu.tss.getLimit())
				    throw new ProcessorException(Processor.PROC_EXCEPTION_TS, cpu.tss.getSelector(), true);
				
                                boolean isSup = cpu.linearMemory.isSupervisor();
				try {
				    cpu.linearMemory.setSupervisor(true);
				    newStackSelector = 0xffff & cpu.tss.getWord(tssStackAddress + 4);
				    newESP = cpu.tss.getDoubleWord(tssStackAddress);
				} finally {
				    cpu.linearMemory.setSupervisor(isSup);
				}
			    } else {
				int tssStackAddress = (targetSegment.getDPL() * 4) + 2;
				if ((tssStackAddress + 4) > cpu.tss.getLimit())
				    throw new ProcessorException(Processor.PROC_EXCEPTION_TS, cpu.tss.getSelector(), true);
				newStackSelector = 0xffff & cpu.tss.getWord(tssStackAddress + 2);
				newESP = 0xffff & cpu.tss.getWord(tssStackAddress);
			    }
			    
			    Segment newStackSegment = null;
			    try {
				newStackSegment = cpu.getSegment(newStackSelector);
			    } catch (ProcessorException e) {
				throw new ProcessorException(Processor.PROC_EXCEPTION_TS, newStackSelector, true);
			    }

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

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

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

			    int params = ((SegmentFactory.CallGate16Bit)newSegment).getParameterCount() & 0x1f;

			    if ((newStackSegment.getDefaultSizeFlag() && (cpu.esp < (8 + params)) && (cpu.esp > 0)) ||
				!newStackSegment.getDefaultSizeFlag() && ((cpu.esp & 0xffff) < (8 + params)) && ((cpu.esp & 0xffff) > 0))
				throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

			    
			    int targetOffset = ((SegmentFactory.GateSegment)newSegment).getTargetOffset();
			    targetSegment.checkAddress(targetOffset);

			    int oldSS = cpu.ss.getSelector();
			    Segment oldStack = cpu.ss;
			    int oldESP = cpu.esp;
			    int oldCS = cpu.cs.getSelector();
			    int oldEIP = cpu.eip;
			    cpu.ss = newStackSegment;
			    cpu.esp = newESP;
			    
			    cpu.cs = targetSegment;
			    cpu.eip = targetOffset;
			    cpu.setCPL(cpu.cs.getDPL());
			    
			    if (cpu.ss.getDefaultSizeFlag()) {
				cpu.esp -= 2;
				cpu.ss.setWord(cpu.esp, (short)oldSS);
				cpu.esp -= 2;
				cpu.ss.setWord(cpu.esp, (short)oldESP);

				oldESP += 2 * params;
				for (int i = 0; i < params; i++) {
				    cpu.esp -= 2;
				    cpu.ss.setWord(cpu.esp, oldStack.getWord(oldESP & 0xffff));
				    oldESP -= 2;
				}

				cpu.esp -= 2;
				cpu.ss.setWord(cpu.esp, (short)oldCS);
				cpu.esp -= 2;
				cpu.ss.setWord(cpu.esp, (short)oldEIP);
			    } else {
				cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
				cpu.ss.setWord(cpu.esp & 0xffff, (short)oldSS);
				cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
				cpu.ss.setWord(cpu.esp & 0xffff, (short)oldESP);

				oldESP += 2 * params;
				for (int i = 0; i < params; i++) {
				    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
				    cpu.ss.setWord(cpu.esp & 0xffff, oldStack.getWord(oldESP & 0xffff));
				    oldESP -= 2;
				}

				cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
				cpu.ss.setWord(cpu.esp & 0xffff, (short)oldCS);
				cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
				cpu.ss.setWord(cpu.esp & 0xffff, (short)oldEIP);
			    }			    
			} else {
			    System.err.println("CALL -> 32bit Call Gate SAME-PRIVILEGE");
			    throw new ProcessorException(-1, true);
			    //SAME-PRIVILEGE
			}
		    }
		    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(Processor.PROC_EXCEPTION_NP, targetSegmentSelector, true);
			
			System.err.println("CALL -> 32bit Call Gate SAME-PRIVILEGE");
			throw new ProcessorException(-1, true);
			//SAME-PRIVILEGE
		    }
		}    
	    }
	    break;
	case 0x05: // Task Gate
	    System.err.println("CALL -> Task Gate");
	    throw new ProcessorException(-1, true);
	case 0x09: // TSS (Not Busy)
	case 0x0b: // TSS (Busy)
	    System.err.println("CALL -> 32bit TSS (Task-State Segment)");
	    throw new ProcessorException(-1, true);
	case 0x0c: // Call Gate
	    System.err.println("CALL -> 32bit Call Gate");
	    throw new ProcessorException(-1, true);
	case 0x18: // Non-conforming Code Segment
	case 0x19: // Non-conforming Code Segment
	case 0x1a: // Non-conforming Code Segment
	case 0x1b: // Non-conforming Code Segment
	    {
		if ((newSegment.getRPL() > cpu.getCPL()) || (newSegment.getDPL() != cpu.getCPL()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);

		if ((cpu.esp < 4) && (cpu.esp > 0))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
		
                newSegment.checkAddress(newEIP);

		cpu.ss.setWord((cpu.esp - 2) & 0xffff, (short)cpu.cs.getSelector());
		cpu.ss.setWord((cpu.esp - 4) & 0xffff, (short)cpu.eip);
		cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);

		cpu.cs = newSegment;
		cpu.cs.setRPL(cpu.getCPL());
		cpu.eip = newEIP;
		return;
	    }
	case 0x1c: // Conforming Code Segment (Not Readable & Not Accessed)
        case 0x1d: // Conforming Code Segment (Not Readable & Accessed)
        case 0x1e: // Conforming Code Segment (Readable & Not Accessed)
        case 0x1f: // Conforming Code Segment (Readable & Accessed)
	    System.err.println("CALL -> Conforming Code Segment");
	    throw new ProcessorException(-1, true);
        }
    }

    private final void calln_o32_a32() 
    {
	int target = getIntOperand();

	cpu.cs.checkAddress(target);

	if ((cpu.esp < 4) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord(cpu.esp - 4, cpu.eip);
	cpu.esp -= 4;

	cpu.eip = target;
    }

    private final void calln_o32_a16() 
    {
	int target = getIntOperand();

	cpu.cs.checkAddress(target);

	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord((cpu.esp - 4) & 0xffff, cpu.eip);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);

	cpu.eip = target;
    }

    private final void clts() 
    {
	if (cpu.getCPL() != 0)
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

	int cr3 = cpu.getCR3();
	cr3 &= ~0x4; // clear TS flag;
	cpu.setCR3(cr3);
    }

    private final void cpuid()
    {
	switch (cpu.eax) {
	case 0x00:
	    cpu.eax = 0x02; 
	    cpu.ebx = 0x756e6547; /* "Genu", with G in the low nibble of BL */
	    cpu.edx = 0x49656e69; /* "ineI", with i in the low nibble of DL */
	    cpu.ecx = 0x6c65746e; /* "ntel", with n in the low nibble of CL */
	    return;
	case 0x01:
	    cpu.eax = 0x00000633; // Pentium II Model 8 Stepping 3
	    cpu.ebx = 8 << 8; //not implemented (should be brand index)
	    cpu.ecx = 0;

	    int features = 0;
	    features |= 0x01; //Have an FPU;
	    features |= (1<< 8);  // Support CMPXCHG8B instruction
	    features |= (1<< 4);  // implement TSC
	    features |= (1<< 5);  // support RDMSR/WRMSR
	    //features |= (1<<23);  // support MMX
	    //features |= (1<<24);  // Implement FSAVE/FXRSTOR instructions.
	    features |= (1<<15);  // Implement CMOV instructions.
	    //features |= (1<< 9);   // APIC on chip
	    //features |= (1<<25);  // support SSE
	    features |= (1<< 3);  // Support Page-Size Extension (4M pages)
	    features |= (1<<13);  // Support Global pages.
	    //features |= (1<< 6);  // Support PAE.
	    features |= (1<<11);  // SYSENTER/SYSEXIT
	    cpu.edx = features;
	    return;
	default:
	case 0x02:
	    cpu.eax = 0x410601;
	    cpu.ebx = 0;
	    cpu.ecx = 0;
	    cpu.edx = 0;
	    return;
	}
    }

    private final void enter_o16_a32() 
    {
	int operand = getIntOperand();

        int nestingLevel = (0xff & operand) % 32;
	int frameSize = 0xffff & (operand >> 16);

	int tempESP = cpu.esp;
	int tempEBP = cpu.ebp;

	if (nestingLevel == 0) {
	    if ((tempESP < (2 + frameSize)) && (tempESP > 0))
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	} else {
	    if ((tempESP < (2 + frameSize + 2 * nestingLevel)) && (tempESP > 0))
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	}

	tempESP -= 2;
	cpu.ss.setWord(tempESP, (short)tempEBP);
	
        int frameTemp = tempESP;
	
	if (nestingLevel > 0) {
	    while (--nestingLevel != 0) {
		tempEBP -= 2;
		tempESP -= 2;
		cpu.ss.setWord(tempESP, cpu.ss.getWord(tempEBP));
	    }
	    
	    tempESP -= 2;
	    cpu.ss.setWord(tempESP, (short)frameTemp);
	}
	
	cpu.ebp = frameTemp;
        cpu.esp = tempESP - frameSize;
    }

    private final void enter_o32_a32() 
    {
	int operand = getIntOperand();

        int nestingLevel = (0xff & operand) % 32;
	int frameSize = operand >>> 16;

	int tempESP = cpu.esp;
	int tempEBP = cpu.ebp;

	if (nestingLevel == 0) {
	    if ((tempESP < (4 + frameSize)) && (tempESP > 0))
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	} else {
	    if ((tempESP < (4 + frameSize + 4 * nestingLevel)) && (tempESP > 0))
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	}

	tempESP -= 4;
	cpu.ss.setDoubleWord(tempESP, tempEBP);

        int frameTemp = tempESP;

	if (nestingLevel != 0) {
	    while (--nestingLevel != 0) {
		tempEBP -= 4;
		tempESP -= 4;
		cpu.ss.setDoubleWord(tempESP, cpu.ss.getDoubleWord(tempEBP));
	    }
	    tempESP -= 4;
	    cpu.ss.setDoubleWord(tempESP, frameTemp);
	}
	
	cpu.ebp = frameTemp;
        cpu.esp = tempESP - frameSize;
    }


    private final void hlt() 
    {
	while (true) 
        {
	    if (((cpu.getInterruptFlags() & Processor.IFLAGS_HARDWARE_INTERRUPT) != 0) && cpu.eflagsInterruptEnable)
		return;
	    
            cpu.waitForInterrupt(50);
	    cpu.processClock();
	}
    }

    private final void in_o8_o8() 
    {
	int ioport = 0xff & getByteOperand();

	if (checkIOPermissionsByte(ioport))
	    setByteOperand((byte)cpu.ioports.ioPortReadByte(ioport));
	else {
	    System.err.println("IN_O8_O8: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void in_o8_o16() 
    {
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsByte(ioport))
	    setByteOperand((byte)cpu.ioports.ioPortReadByte(ioport));
	else {
	    System.err.println("IN_O8_O16: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void in_o16_o16() 
    {
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsShort(ioport))
	    setShortOperand((short)cpu.ioports.ioPortReadWord(ioport));
	else {
	    System.err.println("IN_O16_O16: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void in_o32_o16() 
    {
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsInt(ioport))
	    setIntOperand(cpu.ioports.ioPortReadLong(ioport));
	else {
	    System.err.println("IN_O32_O16: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    protected final void ins_o8_a32()
    {
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsByte(ioport)) {
	    System.err.println("INS_O8_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	byte a = (byte)cpu.ioports.ioPortReadByte(ioport);
	setByteOperand(a);
	
	if (cpu.eflagsDirection) {
	    cpu.edi -= 1;
	} else {
	    cpu.edi += 1;
	}
    }

    protected final void ins_o16_a32()
    {
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsShort(ioport)) {
	    System.err.println("INS_O16_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	short a = (short)cpu.ioports.ioPortReadWord(ioport);
	setShortOperand(a);
	
	if (cpu.eflagsDirection) {
	    cpu.edi -= 2;
	} else {
	    cpu.edi += 2;
	}
    }

    protected final void ins_o32_a32()
    {
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsInt(ioport)) {
	    System.err.println("INS_O32_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	int a = cpu.ioports.ioPortReadLong(ioport);
	setIntOperand(a);
	
	if (cpu.eflagsDirection) {
	    cpu.edi -= 4;
	} else {
	    cpu.edi += 4;
	}
    }

    private final void int_o32_a32() 
    {
	cpu.handleSoftProtectedModeInterrupt(0xff & getByteOperand());
    }

    private final void invd()
    {
	System.err.println("INVD: currently empty");
    }

    private final void invlpg() 
    {
        if (cpu.getCPL() != 0)
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

        Segment targetSegment = getSegmentOperand();
        int address = getAddressOperand();
        address = targetSegment.translateAddressRead(address);
        cpu.linearMemory.invalidateTLBEntry(address);
    }

    private final void iret_o32_a32() 
    {
	if (cpu.eflagsNestedTask) {
	    //TASK-RETURN
	    System.err.println("IRET: Task Return");
	    throw new ProcessorException(-1, true);
	} else {
	    try {
		cpu.ss.checkAddress(cpu.esp + 12);
	    } catch (ProcessorException e) {
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	    }
	    
	    int tempEIP = cpu.ss.getDoubleWord(cpu.esp);
	    int tempCS = 0xffff & cpu.ss.getDoubleWord(cpu.esp + 4);
	    int tempEFlags = cpu.ss.getDoubleWord(cpu.esp + 8);
	    //cpu.esp += 12; deferred until later
	    if ((tempEFlags & (1 << 17)) != 0)
		System.err.println("About to enable virtual8086 mode");
	    Segment returnSegment = cpu.getSegment(tempCS);
	    
	    if (returnSegment == SegmentFactory.NULL_SEGMENT)
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    
	    switch (returnSegment.getType()) {
	    default:
		System.err.println("Bad Segment Type For IRET");
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
	    case 0x18: //Code, Execute-Only
	    case 0x19: //Code, Execute-Only, Accessed
	    case 0x1a: //Code, Execute/Read
	    case 0x1b: //Code, Execute/Read, Accessed
		{
		    if (returnSegment.getRPL() < cpu.getCPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (!(returnSegment.isPresent()))
			throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		    
		    if (returnSegment.getRPL() > cpu.getCPL()) {
			//OUTER PRIVILEGE-LEVEL
			try {
			    cpu.ss.checkAddress(cpu.esp + 20);
			} catch (ProcessorException e) {
			    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
			}

			int returnESP = cpu.ss.getDoubleWord(cpu.esp + 12);
			int tempSS = 0xffff & cpu.ss.getDoubleWord(cpu.esp + 16);

			Segment returnStackSegment = cpu.getSegment(tempSS);
	    
			if ((returnStackSegment.getRPL() != returnSegment.getRPL()) || ((returnStackSegment.getType() & 0x12) != 0x12) ||
			    (returnStackSegment.getDPL() != returnSegment.getRPL())) 
			    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempSS, true);

			if (!returnStackSegment.isPresent())
			    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempSS, true);

			returnSegment.checkAddress(tempEIP);

			//cpu.esp += 20; //includes the 12 from earlier
			cpu.eip = tempEIP;
			cpu.cs = returnSegment;

			cpu.esp = returnESP;
			cpu.ss = returnStackSegment;

			int eflags = cpu.getEFlags();
			eflags &= ~0x254dd5;
			eflags |= (0x254dd5 & tempEFlags);

			if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel) {
			    eflags &= ~0x200;
			    eflags |= (0x200 & tempEFlags);
			}
			if (cpu.getCPL() == 0) {
			    eflags &= ~0x1a3000;
			    eflags |= (0x1a3000 & tempEFlags);
			}
			cpu.setEFlags(tempEFlags);

			cpu.setCPL(cpu.cs.getRPL());

			try {
			    if ((((cpu.es.getType() & 0x10) != 0) || ((cpu.es.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.es.getDPL()))
				cpu.es = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {}
			
			try {
			    if ((((cpu.ds.getType() & 0x10) != 0) || ((cpu.ds.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.ds.getDPL()))
				cpu.ds = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {}

			try {
			    if ((((cpu.fs.getType() & 0x10) != 0) || ((cpu.fs.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.fs.getDPL()))
				cpu.fs = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {}

			try {
			    if ((((cpu.gs.getType() & 0x10) != 0) || ((cpu.gs.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.gs.getDPL()))
				cpu.gs = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {}
		    } else {
			//SAME PRIVILEGE-LEVEL
			returnSegment.checkAddress(tempEIP);
	
			cpu.esp += 12;
			cpu.cs = returnSegment;
			cpu.eip = tempEIP;

			//Set EFlags
			int eflags = cpu.getEFlags();

			eflags &= ~0x254dd5;
			eflags |= (0x254dd5 & tempEFlags);

			if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel) {
			    eflags &= ~0x200;
			    eflags |= (0x200 & tempEFlags);
			}

			if (cpu.getCPL() == 0) {
			    eflags &= ~0x1a3000;
			    eflags |= (0x1a3000 & tempEFlags);

			}
			cpu.setEFlags(tempEFlags);
		    }
		}
		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 (returnSegment.getRPL() < cpu.getCPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (returnSegment.getDPL() > returnSegment.getRPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (!(returnSegment.isPresent()))
			throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		    
		    if (returnSegment.getRPL() > cpu.getCPL()) {
			//OUTER PRIVILEGE-LEVEL
			System.err.println("Conforming: OUTER PRIVILEGE-LEVEL");
			throw new ProcessorException(-1, true);
		    } else {
			//SAME PRIVILEGE-LEVEL
			System.err.println("Conforming: SAME PRIVILEGE-LEVEL");
			throw new ProcessorException(-1, true);
		    }
		}
	    }
	}
    }

    private final void iret_o16_a16() 
    {
	if (cpu.eflagsNestedTask) {
	    //TASK-RETURN
	    System.err.println("IRET: Task Return");
	    throw new ProcessorException(-1, true);
	} else {
	    try {
		cpu.ss.checkAddress((cpu.esp + 6) & 0xffff);
	    } catch (ProcessorException e) {
		throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	    }
	    
	    int tempEIP = cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff;
	    int tempCS = 0xffff & cpu.ss.getWord((cpu.esp + 2) & 0xffff);
	    int tempEFlags = 0xffff & cpu.ss.getWord((cpu.esp + 4) & 0xffff);
	    //cpu.esp += 6; deferred until later
	    if ((tempEFlags & (1 << 17)) != 0)
		System.err.println("About to enable virtual8086 mode");
	    Segment returnSegment = cpu.getSegment(tempCS);
	    
	    if (returnSegment == SegmentFactory.NULL_SEGMENT)
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    
	    switch (returnSegment.getType()) {
	    default:
		System.err.println("Bad Segment Type For IRET");
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
	    case 0x18: //Code, Execute-Only
	    case 0x19: //Code, Execute-Only, Accessed
	    case 0x1a: //Code, Execute/Read
	    case 0x1b: //Code, Execute/Read, Accessed
		{
		    if (returnSegment.getRPL() < cpu.getCPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (!(returnSegment.isPresent()))
			throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		    
		    if (returnSegment.getRPL() > cpu.getCPL()) {
			//OUTER PRIVILEGE-LEVEL
			try {
			    cpu.ss.checkAddress((cpu.esp + 10) & 0xffff);
			} catch (ProcessorException e) {
			    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
			}

			int returnESP = cpu.ss.getWord((cpu.esp + 6) & 0xffff) & 0xffff;
			int tempSS = 0xffff & cpu.ss.getWord((cpu.esp + 8) & 0xffff);

			Segment returnStackSegment = cpu.getSegment(tempSS);
	    
			if ((returnStackSegment.getRPL() != returnSegment.getRPL()) || ((returnStackSegment.getType() & 0x12) != 0x12) ||
			    (returnStackSegment.getDPL() != returnSegment.getRPL())) 
			    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempSS, true);

			if (!returnStackSegment.isPresent())
			    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempSS, true);

			returnSegment.checkAddress(tempEIP);

			//cpu.esp += 10; //includes the 6 from earlier
			cpu.eip = tempEIP;
			cpu.cs = returnSegment;

			cpu.esp = returnESP;
			cpu.ss = returnStackSegment;

			int eflags = cpu.getEFlags();
			eflags &= ~0x4dd5;
			eflags |= (0x4dd5 & tempEFlags);

			if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel) {
			    eflags &= ~0x200;
			    eflags |= (0x200 & tempEFlags);
			}

			if (cpu.getCPL() == 0) {
			    eflags &= ~0x3000;
			    eflags |= (0x3000 & tempEFlags);
			}
			cpu.setEFlags(tempEFlags);

			cpu.setCPL(cpu.cs.getRPL());

			try {
			    if ((((cpu.es.getType() & 0x10) != 0) || ((cpu.es.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.es.getDPL()))
				cpu.es = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {
			} catch (IllegalStateException e) {
			    System.err.println("ES is a Real Mode Segment, cannot assign null");
			}
			
			try {
			    if ((((cpu.ds.getType() & 0x10) != 0) || ((cpu.ds.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.ds.getDPL()))
				cpu.ds = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {
			} catch (IllegalStateException e) {
			    System.err.println("DS is a Real Mode Segment, cannot assign null");
			}

			try {
			    if ((((cpu.fs.getType() & 0x10) != 0) || ((cpu.fs.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.fs.getDPL()))
				cpu.fs = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {
			} catch (IllegalStateException e) {
			    System.err.println("FS is a Real Mode Segment, cannot assign null");
			}
			
			try {
			    if ((((cpu.gs.getType() & 0x10) != 0) || ((cpu.gs.getType() & 0x1c) == 0x18))
				&& (cpu.getCPL() > cpu.gs.getDPL()))
				cpu.gs = SegmentFactory.NULL_SEGMENT;
			} catch (ProcessorException e) {
			} catch (IllegalStateException e) {
			    System.err.println("GS is a Real Mode Segment, cannot assign null");
			}
		    } else {
			//SAME PRIVILEGE-LEVEL
			returnSegment.checkAddress(tempEIP);
	
			cpu.esp += 6;
			cpu.cs = returnSegment;
			cpu.eip = tempEIP;

			//Set EFlags
			int eflags = cpu.getEFlags();

			eflags &= ~0x4dd5;
			eflags |= (0x4dd5 & tempEFlags);

			if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel) {
			    eflags &= ~0x200;
			    eflags |= (0x200 & tempEFlags);
			}

			if (cpu.getCPL() == 0) {
			    eflags &= ~0x3000;
			    eflags |= (0x3000 & tempEFlags);

			}
			cpu.setEFlags(tempEFlags);
		    }
		}
		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 (returnSegment.getRPL() < cpu.getCPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (returnSegment.getDPL() > returnSegment.getRPL())
			throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		    
		    if (!(returnSegment.isPresent()))
			throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		    
		    if (returnSegment.getRPL() > cpu.getCPL()) {
			//OUTER PRIVILEGE-LEVEL
			System.err.println("Conforming: OUTER PRIVILEGE-LEVEL");
			throw new ProcessorException(-1, true);
		    } else {
			//SAME PRIVILEGE-LEVEL
			System.err.println("Conforming: SAME PRIVILEGE-LEVEL");
			throw new ProcessorException(-1, true);
		    }
		}
	    }
	}
    }

    private final void jcxz_a32() 
    {
	int offset = getByteOperand();
        if (cpu.ecx == 0) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

	    cpu.eip = newEIP;
        }
    }

    private final void jb_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;
        }
    }

    private final void jb_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

	    cpu.eip = newEIP;
        }

    }

    private final void jb_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

	    cpu.eip = newEIP;
        }

    }

    private final void jnb_o8() 
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;           
        }
    }

    private final void jnb_o16() 
    {
	short offset = getShortOperand();
        if (!cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

	    cpu.eip = newEIP;
        }
    }

    private final void jnb_o32() 
    {
	int offset = getIntOperand();
        if (!cpu.eflagsCarry) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

	    cpu.eip = newEIP;
        }
    }

    private final void jbe_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsCarry || cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jbe_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsCarry || cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;    
        }
    }

    private final void jbe_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsCarry || cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;    
        }
    }

    private final void jnbe_o8() 
    {
	byte offset = getByteOperand();
        if ((!cpu.eflagsCarry) && (!cpu.eflagsZero)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnbe_o16() 
    {
	short offset = getShortOperand();
        if ((!cpu.eflagsCarry) && (!cpu.eflagsZero)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnbe_o32() 
    {
	int offset = getIntOperand();
        if ((!cpu.eflagsCarry) && (!cpu.eflagsZero)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jl_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign != cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jl_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign != cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;           
        }
    }

    private final void jl_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsSign != cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;           
        }
    }

    private final void jnl_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign == cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnl_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign == cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void jnl_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsSign == cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void jle_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsZero || (cpu.eflagsSign != cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jle_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsZero || (cpu.eflagsSign != cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jle_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsZero || (cpu.eflagsSign != cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnle_o8() 
    {
	byte offset = getByteOperand();
        if ((!cpu.eflagsZero) && (cpu.eflagsSign == cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnle_o16() 
    {
	short offset = getShortOperand();
        if ((!cpu.eflagsZero) && (cpu.eflagsSign == cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void jnle_o32() 
    {
	int offset = getIntOperand();
        if ((!cpu.eflagsZero) && (cpu.eflagsSign == cpu.eflagsOverflow)) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void jo_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jo_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jo_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jno_o8() 
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jno_o16() 
    {
	short offset = getShortOperand();
        if (!cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jno_o32() 
    {
	int offset = getIntOperand();
        if (!cpu.eflagsOverflow) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jp_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jp_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;     
        }

    }

    private final void jp_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;     
        }

    }

    private final void jnp_o8() 
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnp_o16() 
    {
	short offset = getShortOperand();
        if (!cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;     
        }
    }

    private final void jnp_o32() 
    {
	int offset = getIntOperand();
        if (!cpu.eflagsParity) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;     
        }
    }

    private final void js_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void js_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void js_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;  
        }
    }

    private final void jns_o8() 
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jns_o16() 
    {
	short offset = getShortOperand();
        if (!cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;           
        }
    }

    private final void jns_o32() 
    {
	int offset = getIntOperand();
        if (!cpu.eflagsSign) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;           
        }
    }

    private final void jz_o8() 
    {
	byte offset = getByteOperand();
        if (cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jz_o16() 
    {
	short offset = getShortOperand();
        if (cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;        
        }
    }

    private final void jz_o32() 
    {
	int offset = getIntOperand();
        if (cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;        
        }
    }

    private final void jnz_o8() 
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnz_o16() 
    {
	short offset = getShortOperand();
        if (!cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jnz_o32() 
    {
	int offset = getIntOperand();
        if (!cpu.eflagsZero) {
            int newEIP = cpu.eip + offset;

	    cpu.cs.checkAddress(newEIP);// check whether eip is outside cs limit

            cpu.eip = newEIP;            
        }
    }

    private final void jmp_o8_short() 
    {
        int tempEIP = cpu.eip + getByteOperand();
	if (tempEIP == cpu.eip)
	    return; //first protected mode throws on a jump 0 (some segment problem?)

	cpu.cs.checkAddress(tempEIP);// check whether eip is outside cs limit

	cpu.eip = tempEIP;
    }

    private final void jmp_o16_near_absolute() 
    {
	int tempEIP = getShortOperand() & 0xffff;

	cpu.cs.checkAddress(tempEIP);// check whether eip is outside cs limit

	cpu.eip = tempEIP;
    }

    private final void jmp_o32_near_absolute() 
    {
	int tempEIP = getIntOperand();

	cpu.cs.checkAddress(tempEIP);// check whether eip is outside cs limit

	cpu.eip = tempEIP;
    }

    private final void jmp_o16_near_relative() 
    {
	int tempEIP = (cpu.eip + getShortOperand()) & 0xffff;

	cpu.cs.checkAddress(tempEIP);// check whether eip is outside cs limit

	cpu.eip = tempEIP;
    }

    private final void jmp_o32_near_relative() 
    {
	int tempEIP = cpu.eip + getIntOperand();

	cpu.cs.checkAddress(tempEIP);// check whether eip is outside cs limit

	cpu.eip = tempEIP;
    }

    private final void jmp_o16_far() 
    {
	int target = getIntOperand();
	int segmentSelector = (target >>> 16) & 0xffff;
	int newEIP = target & 0xffff;

        Segment newSegment = cpu.getSegment(segmentSelector);
        if (newSegment == SegmentFactory.NULL_SEGMENT)
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

	switch (newSegment.getType()) 
        { // segment type	    
	default: // not a valid segment descriptor for a jump
	    System.err.println("JMP -> Invalid Segment Type");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
	case 0x05: // Task Gate
	    System.err.println("JMP -> Task Gate");
	    throw new ProcessorException(-1, true);
	case 0x09: // TSS (Not Busy)
	case 0x0b: // TSS (Busy)   
	    System.err.println("JMP -> TSS");
	    throw new ProcessorException(-1, true);
	case 0x0c: // Call Gate
	    System.err.println("JMP -> Call Gate");
	    throw new ProcessorException(-1, true);
	case 0x18: // Non-conforming Code Segment
	case 0x19: // Non-conforming Code Segment
	case 0x1a: // Non-conforming Code Segment
	case 0x1b: // Non-conforming Code Segment
	    {
		if ((newSegment.getRPL() != cpu.getCPL()) || (newSegment.getDPL() > cpu.getCPL()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);

                newSegment.checkAddress(newEIP);

		newSegment.setRPL(cpu.getCPL());
		cpu.cs = newSegment;
		cpu.eip = newEIP;
		return;
	    }
        case 0x1c: // Conforming Code Segment (Not Readable & Not Accessed)
        case 0x1d: // Conforming Code Segment (Not Readable & Accessed)
        case 0x1e: // Conforming Code Segment (Readable & Not Accessed)
        case 0x1f: // Conforming Code Segment (Readable & Accessed)
	    {
		if (newSegment.getDPL() > cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);
                newSegment.checkAddress(newEIP);
		newSegment.setRPL(cpu.getCPL());
		cpu.cs = newSegment;
		cpu.eip = newEIP;
		return;
	    }
	}
    }

    private final void jmp_o32_far() 
    {
	long target = getLongOperand();
	int segmentSelector = (int)((target >> 32) & 0xffffl);
	int newEIP = (int) target;
	
        Segment newSegment = cpu.getSegment(segmentSelector);
        if (newSegment == SegmentFactory.NULL_SEGMENT)
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);

	switch (newSegment.getType()) 
        { // segment type	    
	default: // not a valid segment descriptor for a jump
	    System.err.println("JMP -> Invalid Segment Type");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
	case 0x05: // Task Gate
	    System.err.println("JMP -> Task Gate");
	    throw new ProcessorException(-1, true);
	case 0x09: // TSS (Not Busy)
	case 0x0b: // TSS (Busy)
	    System.err.println("JMP -> TSS (Task-State Segment)");
	    throw new ProcessorException(-1, true);
	case 0x0c: // Call Gate
	    System.err.println("JMP -> Call Gate");
	    throw new ProcessorException(-1, true);
	case 0x18: // Non-conforming Code Segment
	case 0x19: // Non-conforming Code Segment
	case 0x1a: // Non-conforming Code Segment
	case 0x1b: // Non-conforming Code Segment
	    {
		if ((newSegment.getRPL() != cpu.getCPL()) || (newSegment.getDPL() > cpu.getCPL()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);

                newSegment.checkAddress(newEIP);
		newSegment.setRPL(cpu.getCPL());
		cpu.cs = newSegment;
		cpu.eip = newEIP;
		return;
	    }
	case 0x1c: // Conforming Code Segment (Not Readable & Not Accessed)
        case 0x1d: // Conforming Code Segment (Not Readable & Accessed)
        case 0x1e: // Conforming Code Segment (Readable & Not Accessed)
        case 0x1f: // Conforming Code Segment (Readable & Accessed)
	    {
		if (newSegment.getDPL() > cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
		if (!newSegment.isPresent())
                    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, segmentSelector, true);
                newSegment.checkAddress(newEIP);

		newSegment.setRPL(cpu.getCPL());
		cpu.cs = newSegment;
		cpu.eip = newEIP;
		return;
	    }
        }
    }

    private final void loop_o16_a16() 
    {
	byte offset = getByteOperand();
	cpu.ecx = (cpu.ecx & 0xffff0000) | (--cpu.ecx & 0xffff);
	if ((cpu.ecx & 0xffff) != 0) {
	    cpu.cs.checkAddress((cpu.eip + offset) & 0xffff);
	    cpu.eip += offset;
	    cpu.eip &= 0xffff;
	}
    }

    private final void loop_o32_a32() 
    {
	byte offset = getByteOperand();
	cpu.ecx--;

	if (cpu.ecx != 0) {
	    cpu.cs.checkAddress(cpu.eip + offset);// check whether eip is outside cs limit
	    cpu.eip += offset;
	}
    }

    private final void loopnz_o32_a32() 
    {
	byte offset = getByteOperand();
	cpu.ecx--;

	if ((cpu.ecx != 0) && !cpu.eflagsZero) {
	    cpu.cs.checkAddress(cpu.eip + offset);// check whether eip is outside cs limit
	    cpu.eip += offset;
	}
    }

    private final void leave_o32_a16() 
    {
	cpu.ss.checkAddress(cpu.ebp);
	int tempESP = cpu.ebp;
	int tempEBP = cpu.ss.getDoubleWord(tempESP & 0xffff);
	cpu.esp = (cpu.esp & ~0xffff) | ((tempESP + 4) & 0xffff);
	cpu.ebp = tempEBP;
    }

    private final void leave_o32_a32() 
    {
	cpu.ss.checkAddress(cpu.ebp);
	int tempESP = cpu.ebp;
	int tempEBP = cpu.ss.getDoubleWord(tempESP);
	cpu.esp = tempESP + 4;
	cpu.ebp = tempEBP;
    }

    private final void leave_o16_a16() 
    {
	cpu.ss.checkAddress(cpu.ebp);
	int tempESP = cpu.ebp;
	int tempEBP = cpu.ss.getWord(tempESP & 0xffff);
	cpu.esp = (cpu.esp & ~0xffff) | ((tempESP + 2) & 0xffff);
	cpu.ebp = (cpu.ebp & ~0xffff) | (tempEBP & 0xffff);
    }

    private final void leave_o16_a32() 
    {
	cpu.ss.checkAddress(cpu.ebp);
	int tempESP = cpu.ebp;
	int tempEBP = cpu.ss.getWord(tempESP);
	cpu.esp = tempESP + 2;
	cpu.ebp = (cpu.ebp & ~0xffff) | (tempEBP & 0xffff);
    }

    private final void lgdt_o16() 
    {
	long operand = getLongOperand();
        cpu.gdtr = cpu.createDescriptorTableSegment((int)((operand >> 16) & 0x00ffffff), (int)(operand & 0xffff));
    }

    private final void lgdt_o32() 
    {
	long operand = getLongOperand();
        cpu.gdtr = cpu.createDescriptorTableSegment((int)(operand >> 16), (int)(operand & 0xffff));
    }

    private final void lidt_o16() 
    {
	long operand = getLongOperand();
        cpu.idtr = cpu.createDescriptorTableSegment((int)((operand >> 16) & 0x00ffffff), (int)(operand & 0xffff));
    }

    private final void lidt_o32() 
    {
	long operand = getLongOperand();
        cpu.idtr = cpu.createDescriptorTableSegment((int)(operand >> 16), (int)(operand & 0xffff));
    }

    private final void lldt() 
    {
	int selector = 0xffff & getShortOperand();

	if (selector == 0) {
	    cpu.ldtr = SegmentFactory.NULL_SEGMENT;
	    return;
	}

	Segment newSegment = cpu.getSegment(selector & ~0x4);


	if (newSegment.getType() != 0x02)
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, selector, true);

	if (!(newSegment.isPresent()))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, selector, true);

	cpu.ldtr = newSegment;
    }

    private final void lmsw() 
    {
	short operand = getShortOperand();

	cpu.setCR0((cpu.getCR0() & ~0xe) | (operand & 0xe));
    }

    private final void ltr() 
    {
	int selector = 0xffff & getShortOperand();

	if ((selector & 0x4) != 0) //must be gdtr table
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, selector, true);

	Segment newSegment = cpu.getSegment(selector);

	if ((newSegment.getType() != 0x01) && (newSegment.getType() != 0x09))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, selector, true);

	if (!(newSegment.isPresent()))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, selector, true);

	long descriptor = cpu.gdtr.getQuadWord(selector & 0xfff8) | (0x1l << 41); // set busy flag in segment descriptor
	cpu.gdtr.setQuadWord(selector & 0xfff8, descriptor);
	
	//reload segment
	cpu.tss = cpu.getSegment(selector);
    }

    private final void lss_o32_a32() 
    {
	//UD on source not memory location
        //if (invalid)
        //    throw exceptionUD;

	long pointer = getLongOperand();
	int offset = (int)pointer;
	int segmentSelector = (int)((pointer >> 32) & 0xffff);
	long segmentDescriptor = 0;

	if (segmentSelector < 0x4) {
	    skipOperand();
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

        Segment newSegment = null;
        try {
            newSegment = cpu.getSegment(segmentSelector);
        } catch (ProcessorException e) {
            throw new IllegalStateException(e.toString());
        }

        if ((newSegment.getRPL() != cpu.getCPL()) || (newSegment.getDPL() != cpu.getCPL()))
            throw new ProcessorException(Processor.PROC_EXCEPTION_GP, segmentSelector, true);
	
	cpu.ss = newSegment;
	cpu.eflagsInterruptEnable = false;
	
	setIntOperand(offset);
    }

    private final void mov_to_cr_o32() 
    {
	int src = getIntOperand();
	switch (microcodes[microcodesPosition++]) 
        {
	case CR0:
	    cpu.setCR0(src);
	    break;
	case CR2:
	    cpu.setCR2(src);
	    break;
        case CR3:
            cpu.setCR3(src);
            break;
	case CR4:
	    cpu.setCR4(src);
	    break;
	default:
	    throw new ProcessorException(-1, true);
	}
    }

    private final void mov_to_dr_o32() 
    {
	int src = getIntOperand();
	setIntOperand(src);
    }

    private final void mov_to_seg() 
    {
	int segmentSelector = 0xffff & getShortOperand();

	long segmentDescriptor = 0;

	if (segmentSelector < 0x4) {
	    switch (microcodes[microcodesPosition]) {
	    case SS:
		skipOperand();
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    case DS:
		skipOperand();
		cpu.ds = SegmentFactory.NULL_SEGMENT;
		return;
	    case ES:
		skipOperand();
		cpu.es = SegmentFactory.NULL_SEGMENT;
		return;
	    case FS:
		skipOperand();
		cpu.fs = SegmentFactory.NULL_SEGMENT;
		return;
	    case GS:
		skipOperand();
		cpu.gs = SegmentFactory.NULL_SEGMENT;
		return;
	    default:
		skipOperand();
		return;
	    }
	}

        Segment newSegment = null;
        try {
            newSegment = cpu.getSegment(segmentSelector);
        } catch (ProcessorException e) {
            throw new IllegalStateException(e.toString());
        }

	if (microcodes[microcodesPosition] == SS) 
        {
	    skipOperand();
	    cpu.ss = newSegment;
	    cpu.eflagsInterruptEnable = false;
	} 
        else
	    setSegment(newSegment);
    }

    private final void out_o8_o8() 
    {
	byte data = getByteOperand();
	int ioport = 0xff & getByteOperand();

	if (checkIOPermissionsByte(ioport))
	    cpu.ioports.ioPortWriteByte(ioport, 0xff & data);
	else {
	    System.err.println("OUT_O8_O8: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void out_o16_o8() 
    {
	byte data = getByteOperand();
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsByte(ioport))
	    cpu.ioports.ioPortWriteByte(ioport, 0xff & data);
	else {
	    System.err.println("OUT_O16_O8: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void out_o16_o16() 
    {
	short data = getShortOperand();
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsShort(ioport))
	    cpu.ioports.ioPortWriteWord(ioport, 0xffff & data);
	else {	    
	    System.err.println("OUT_O16_O16: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    private final void out_o16_o32() 
    {
	int data = getIntOperand();
	int ioport = 0xffff & getShortOperand();

	if (checkIOPermissionsInt(ioport))
	    cpu.ioports.ioPortWriteLong(ioport, data);
	else {
	    System.err.println("OUT_O16_O32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + " cpl:" + cpu.getCPL() + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
    }

    protected final void outs_o8_a32() 
    {
	int ioport = 0xffff & getShortOperand();
	int a = getByteOperand() & 0xff;

	if (!checkIOPermissionsByte(ioport)) {
	    System.err.println("OUTS_O8_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	cpu.ioports.ioPortWriteByte(ioport, a);
	
	if (cpu.eflagsDirection) {
	    cpu.esi -= 1;
	} else {
	    cpu.esi += 1;
	}
    }

    protected final void outs_o16_a32() 
    {
	int ioport = 0xffff & getShortOperand();
	int a = getShortOperand() & 0xffff;

	if (!checkIOPermissionsShort(ioport)) {
	    System.err.println("OUTS_O16_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	cpu.ioports.ioPortWriteWord(ioport, a);
	
	if (cpu.eflagsDirection) {
	    cpu.esi -= 2;
	} else {
	    cpu.esi += 2;
	}
    }

    protected final void outs_o32_a32() 
    {
	int ioport = 0xffff & getShortOperand();
	int a = getIntOperand();

	if (!checkIOPermissionsInt(ioport)) {
	    System.err.println("OUTS_O32_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	cpu.ioports.ioPortWriteLong(ioport, a);
	
	if (cpu.eflagsDirection) {
	    cpu.esi -= 4;
	} else {
	    cpu.esi += 4;
	}
    }

    private final void pop_o32_a32() 
    {
	int value = cpu.ss.getDoubleWord(cpu.esp);
	cpu.esp += 4;
	try {
	    setIntOperand(value);
	} catch (ProcessorException p) {
	    cpu.esp -= 4;
	    throw p;
	}

	if (microcodes[microcodesPosition] == SS)
	    cpu.eflagsInterruptEnable = false;
    }

    private final void pop_o32_a16() 
    {
	int value = cpu.ss.getDoubleWord(0xffff & cpu.esp);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
	try {
	    setIntOperand(value);
	} catch (ProcessorException p) {
	    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);
	    throw p;
	}

	if (microcodes[microcodesPosition] == SS)
	    cpu.eflagsInterruptEnable = false;
    }

    private final void pop_o16_a32() 
    {
	short value = cpu.ss.getWord(cpu.esp);

	cpu.esp += 2;
	try {
	    setShortOperand(value);
	} catch (ProcessorException p) {
	    cpu.esp -= 2;
	    throw p;
	}

	if (microcodes[microcodesPosition] == SS)
	    cpu.eflagsInterruptEnable = false;
    }

    private final void pop_o16_a16() 
    {
	short value = cpu.ss.getWord(0xffff & cpu.esp);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 2) & 0xffff);
	try {
	    setShortOperand(value);
	} catch (ProcessorException p) {
	    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
	    throw p;
	}
	
	if ( microcodes[microcodesPosition] == SS)
	    cpu.eflagsInterruptEnable = false;
    }

    private final void popa_o16_a16() 
    {
	int offset = 0xffff & cpu.esp;

	//Bochs claims no checking need on POPs
	//if (offset + 16 >= cpu.ss.limit)
	//    throw exceptionSS;

	int newedi = cpu.edi & 0xFFFF0000;
	int newesi = cpu.esi & 0xFFFF0000;
	int newebp = cpu.ebp & 0xFFFF0000;
	int newebx = cpu.ebx & 0xFFFF0000;
	int newedx = cpu.edx & 0xFFFF0000;
	int newecx = cpu.ecx & 0xFFFF0000;
	int neweax = cpu.eax & 0xFFFF0000;

	newedi |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	newesi |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	newebp |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	
	offset += 2; // yes - skip 2 bytes in order to skip SP
	
	newebx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	newedx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	newecx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	neweax |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	
	cpu.edi = newedi;
	cpu.esi = newesi;
	cpu.ebp = newebp;
	cpu.ebx = newebx;
	cpu.edx = newedx;
	cpu.ecx = newecx;
	cpu.eax = neweax;

	offset &= 0xffff;
	cpu.esp &= ~0xffff;
	cpu.esp |= offset;
    }

    private final void popa_o32_a32() 
    {
	int offset = cpu.esp;
	
	//Bochs claims no checking need on POPs
	//if (offset + 16 >= cpu.ss.limit)
	//    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	
	int newedi = cpu.ss.getDoubleWord(offset);
	offset += 4;
	int newesi = cpu.ss.getDoubleWord(offset);
	offset += 4;
	int newebp = cpu.ss.getDoubleWord(offset);
	offset += 8;// yes - skip an extra 4 bytes in order to skip ESP
	
	int newebx = cpu.ss.getDoubleWord(offset);
	offset += 4;
	int newedx = cpu.ss.getDoubleWord(offset);
	offset += 4;
	int newecx = cpu.ss.getDoubleWord(offset);
	offset += 4;
	int neweax = cpu.ss.getDoubleWord(offset);
	offset += 4;
	
	cpu.edi = newedi;
	cpu.esi = newesi;
	cpu.ebp = newebp;
	cpu.ebx = newebx;
	cpu.edx = newedx;
	cpu.ecx = newecx;
	cpu.eax = neweax;

	cpu.esp = offset;
    }

    private final void popf_o32_a32() 
    {
	//SS exception?
	int w = cpu.ss.getDoubleWord(cpu.esp);
	cpu.esp += 4;

	boolean vm = cpu.eflagsVirtual8086Mode;
	int iopl = cpu.eflagsIOPrivilegeLevel;
	boolean interruptFlag = cpu.eflagsInterruptEnable;
	boolean interruptFlagSoon = cpu.eflagsInterruptEnableSoon;
	cpu.setEFlags(w);
	cpu.eflagsVirtual8086Mode = vm;
	cpu.eflagsVirtualInterrupt = false;
	cpu.eflagsVirtualInterruptPending = false;
	if (cpu.getCPL() != 0) {
	    if (cpu.getCPL() > iopl) {
		cpu.eflagsIOPrivilegeLevel = iopl;
		cpu.eflagsInterruptEnable = interruptFlag;
		cpu.eflagsInterruptEnableSoon = interruptFlagSoon;
	    } else {
		cpu.eflagsIOPrivilegeLevel = iopl;
	    }
	}
    }

    private final void popf_o32_a16() 
    {
	//SS exception?
	int w = cpu.ss.getDoubleWord(cpu.esp & 0xffff);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);

	boolean vm = cpu.eflagsVirtual8086Mode;
	int iopl = cpu.eflagsIOPrivilegeLevel;
	boolean interruptFlag = cpu.eflagsInterruptEnable;
	boolean interruptFlagSoon = cpu.eflagsInterruptEnableSoon;
	cpu.setEFlags(w);
	cpu.eflagsVirtual8086Mode = vm;
	cpu.eflagsVirtualInterrupt = false;
	cpu.eflagsVirtualInterruptPending = false;
	if (cpu.getCPL() != 0) {
	    if (cpu.getCPL() > iopl) {
		cpu.eflagsIOPrivilegeLevel = iopl;
		cpu.eflagsInterruptEnable = interruptFlag;
		cpu.eflagsInterruptEnableSoon = interruptFlagSoon;
	    } else {
		cpu.eflagsIOPrivilegeLevel = iopl;
	    }
	}
    }

    private final void popf_o16_a32() 
    {
	//SS exception?
	int w = 0xffff & cpu.ss.getWord(cpu.esp);
	cpu.esp += 2;

	int current = cpu.getEFlags();
	w &= 0xffff;
	w |= (0xffff & current);

	int iopl = cpu.eflagsIOPrivilegeLevel;
	cpu.setEFlags(w);

	if (cpu.getCPL() != 0) {
	    cpu.eflagsIOPrivilegeLevel = iopl;
	}
    }

    private final void push_o16_a32() 
    {
	short value = getShortOperand();

	if ((cpu.esp < 2) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setWord(cpu.esp - 2, value);
	cpu.esp -= 2;
    }

    private final void push_o16_a16() 
    {
	short value = getShortOperand();

	if (((0xffff & cpu.esp) < 2) && ((0xffff & cpu.esp) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	
	cpu.ss.setWord((cpu.esp - 2) & 0xffff, value);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
    }


    private final void push_o32_a32() 
    {
	int value;
	switch (microcodes[microcodesPosition]) {
	case FS:
	case GS:
	    value = getIntOperand() & 0xffff;
	    break;
	default:
	    value = getIntOperand();
	    break;
	}

	if ((cpu.esp < 4) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord(cpu.esp - 4, value);
	cpu.esp -= 4;
    }

    private final void push_o32_a16() 
    {
	int value;
	switch (microcodes[microcodesPosition]) {
	case FS:
	case GS:
	    value = getIntOperand() & 0xffff;
	    break;
	default:
	    value = getIntOperand();
	    break;

	}

	if (((0xffff & cpu.esp) < 4) && ((0xffff & cpu.esp) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord((cpu.esp - 4) & 0xffff, value);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);
    }

    private final void pusha_o32_a32() 
    {
	int offset = cpu.esp;
	int temp = cpu.esp;
	if ((offset < 32) && (offset > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.eax);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.ecx);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.edx);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.ebx);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, temp);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.ebp);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.esi);
	offset -= 4;
	cpu.ss.setDoubleWord(offset, cpu.edi);
        
	cpu.esp = offset;
    }

    private final void pushf_o32_a32() 
    {
	int value = 0x00fcffff & cpu.getEFlags();
	
	if ((cpu.esp < 4) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord(cpu.esp - 4, value);
	cpu.esp -= 4;
    }

    private final void pushf_o32_a16() 
    {
	int value = 0x00fcffff & cpu.getEFlags();
	
	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setDoubleWord((cpu.esp - 4) & 0xffff, value);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);
    }

    private final void pushf_o16_a32() 
    {
	short value = (short)cpu.getEFlags();
	
	if ((cpu.esp < 2) && (cpu.esp > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setWord(cpu.esp - 2, value);
	cpu.esp -= 2;
    }

    private final void pushf_o16_a16() 
    {
	short value = (short)cpu.getEFlags();

	if (((cpu.esp & 0xffff) < 2) && ((cpu.esp & 0xffff) > 0))
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);

	cpu.ss.setWord((cpu.esp - 2) & 0xffff, value);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
    }

    private final void rdmsr() 
    {
	if (cpu.getCPL() == 0) {
	    switch (cpu.ecx) {
	    case 0x1b: // [IA32_]APIC_BASE
		cpu.eax = 0x10; cpu.edx = 0x00;
		return;
	    default:
		System.err.println("Unknown MSR: ECX:0x" + Integer.toHexString(cpu.ecx));
		//throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    }
	} else
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
    }

    private final void rdtsc() 
    {
	if (((cpu.getCR4() & 0x4) == 0) || (cpu.getCPL() == 0)) {
	    long tsc = cpu.getClockCount();
	    cpu.eax = (int)tsc;
	    cpu.edx = (int)(tsc >>> 32);
	} else
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
    }

    protected final void rep_ins_o8_a32()
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsByte(ioport)) {
	    System.err.println("REP_INS_O8_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	int operandAddress = microcodesPosition;
	skipOperand();

	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    // TODO:  put interrupt handling here, I think
	    byte a = (byte)cpu.ioports.ioPortReadByte(ioport);
	    setByteOperand(a);
	    
	    if (cpu.eflagsDirection) {
		cpu.edi -= 1;
	    } else {
		cpu.edi += 1;
	    }
	    cpu.ecx--;
	}
    }

    protected final void rep_ins_o16_a32()
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsShort(ioport)) {
	    System.err.println("REP_INS_O16_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	int operandAddress = microcodesPosition;
	skipOperand();

	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    // TODO:  put interrupt handling here, I think
	    short a = (short)cpu.ioports.ioPortReadWord(ioport);
	    setShortOperand(a);
	    
	    if (cpu.eflagsDirection) {
		cpu.edi -= 2;
	    } else {
		cpu.edi += 2;
	    }
	    cpu.ecx--;
	}
    }

    protected final void rep_ins_o32_a32()
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsInt(ioport)) {
	    System.err.println("REP_INS_O32_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}

	int operandAddress = microcodesPosition;
	skipOperand();

	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    // TODO:  put interrupt handling here, I think
	    int a = cpu.ioports.ioPortReadLong(ioport);
	    setIntOperand(a);
	    
	    if (cpu.eflagsDirection) {
		cpu.edi -= 4;
	    } else {
		cpu.edi += 4;
	    }
	    cpu.ecx--;
	}
    }

    protected final void rep_outs_o8_a32() 
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsByte(ioport)) {
	    System.err.println("REP_OUTS_O8_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	int operandAddress = microcodesPosition;
	skipOperand();
	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    int a = getByteOperand() & 0xff;
	    cpu.ioports.ioPortWriteByte(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		cpu.esi -= 1;
	    } else {
		cpu.esi += 1;
	    }
	    cpu.ecx--;
	}
    }

    protected final void rep_outs_o16_a32() 
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();

	if (!checkIOPermissionsShort(ioport)) {
	    System.err.println("REP_OUTS_O16_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	int operandAddress = microcodesPosition;
	skipOperand();
	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    int a = getShortOperand() & 0xffff;
	    cpu.ioports.ioPortWriteWord(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		cpu.esi -= 2;
	    } else {
		cpu.esi += 2;
	    }
	    cpu.ecx--;
	}
    }

    protected final void rep_outs_o32_a32() 
    {
	executeCount += cpu.ecx - 1;
	int ioport = 0xffff & getShortOperand();
	
	if (!checkIOPermissionsInt(ioport)) {
	    System.err.println("REP_OUTS_O32_A32: Denied IO Port Access [port:0x" + Integer.toHexString(ioport) + "]");
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	}
	
	int operandAddress = microcodesPosition;
	skipOperand();
	while (cpu.ecx != 0) {
	    microcodesPosition = operandAddress;
	    int a = getIntOperand();
	    cpu.ioports.ioPortWriteLong(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		cpu.esi -= 4;
	    } else {
		cpu.esi += 4;
	    }
	    cpu.ecx--;
	}
    }

    private final void ret_o16_a16() 
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	cpu.eip = cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 2) & 0xffff);
    }

    private final void ret_o32_a16() 
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	cpu.eip = cpu.ss.getDoubleWord(0xffff & cpu.esp);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
    }

    private final void ret_o32_a32() 
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	cpu.eip = cpu.ss.getDoubleWord(cpu.esp);
	cpu.esp = cpu.esp + 4;
    }

    private final void ret_iw_o32_a32() 
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	cpu.eip = cpu.ss.getDoubleWord(cpu.esp);
	cpu.esp = cpu.esp + 4 + getShortOperand();
    }

    private final void retf_o32_a32() 
    {
	try {
	    cpu.ss.checkAddress(cpu.esp + 8);
	} catch (ProcessorException e) {
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	}

	int tempEIP = cpu.ss.getDoubleWord(cpu.esp);
	int tempCS = 0xffff & cpu.ss.getDoubleWord(cpu.esp + 4);
	    
	Segment returnSegment = cpu.getSegment(tempCS);
	    
	if (returnSegment == SegmentFactory.NULL_SEGMENT)
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    
	switch (returnSegment.getType()) {
	default:
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
	    
	case 0x18: //Code, Execute-Only
	case 0x19: //Code, Execute-Only, Accessed
	case 0x1a: //Code, Execute/Read
	case 0x1b: //Code, Execute/Read, Accessed
	    {
		if (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp += 8;
		    System.err.println("Non-Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);

		    cpu.esp += 8;
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	    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 (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (returnSegment.getDPL() > returnSegment.getRPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp += 8;
		    System.err.println("Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);

		    cpu.esp += 8;
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	}
    }

    private final void retf_o16_a16() 
    {
	try {
	    cpu.ss.checkAddress(cpu.esp + 4);
	} catch (ProcessorException e) {
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	}

	int tempEIP = 0xffff & cpu.ss.getWord(0xffff & cpu.esp);
	int tempCS = 0xffff & cpu.ss.getWord(0xffff & (cpu.esp + 2));
	    
	Segment returnSegment = cpu.getSegment(tempCS);
	    
	if (returnSegment == SegmentFactory.NULL_SEGMENT)
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    
	switch (returnSegment.getType()) {
	default:
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
	    
	case 0x18: //Code, Execute-Only
	case 0x19: //Code, Execute-Only, Accessed
	case 0x1a: //Code, Execute/Read
	case 0x1b: //Code, Execute/Read, Accessed
	    {
		if (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    System.err.println("Non-Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);
		    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	    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 (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (returnSegment.getDPL() > returnSegment.getRPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    System.err.println("Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);

		    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	}
    }

    private final void retf_iw_o16_a16() 
    {
	int offset = getShortOperand();
	try {
	    cpu.ss.checkAddress(0xffff & (cpu.esp + 4 + offset));
	} catch (ProcessorException e) {
	    throw new ProcessorException(Processor.PROC_EXCEPTION_SS, 0, true);
	}

	int tempEIP = 0xffff & cpu.ss.getWord(0xffff & cpu.esp);
	int tempCS = 0xffff & cpu.ss.getWord(0xffff & (cpu.esp + 2));
	    
	Segment returnSegment = cpu.getSegment(tempCS);
	    
	if (returnSegment == SegmentFactory.NULL_SEGMENT)
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    
	switch (returnSegment.getType()) {
	default:
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
	    
	case 0x18: //Code, Execute-Only
	case 0x19: //Code, Execute-Only, Accessed
	case 0x1a: //Code, Execute/Read
	case 0x1b: //Code, Execute/Read, Accessed
	    {
		if (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    System.err.println("Non-Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);
		    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4 + offset) & 0xffff);
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	    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 (returnSegment.getRPL() < cpu.getCPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (returnSegment.getDPL() > returnSegment.getRPL())
		    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, tempCS, true);
		
		if (!(returnSegment.isPresent()))
		    throw new ProcessorException(Processor.PROC_EXCEPTION_NP, tempCS, true);
		
		if (returnSegment.getRPL() > cpu.getCPL()) {
		    //OUTER PRIVILEGE-LEVEL
		    //cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
		    System.err.println("Conforming: OUTER PRIVILEGE-LEVEL");
		    throw new ProcessorException(-1, true);
		} else {
		    //SAME PRIVILEGE-LEVEL
		    returnSegment.checkAddress(tempEIP);

		    cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4 + offset) & 0xffff);
		    cpu.eip = tempEIP;
		    cpu.cs = returnSegment;
		}
	    }
	}
    }

    private final void sahf()
    {
	int eflags = cpu.getEFlags();
	eflags &= ~0xff;
	eflags |= 0x2;
	eflags |= (cpu.eax & 0xd5);
	cpu.setEFlags(eflags);
    }    


    private final void sgdt_o32()
    {
	Segment targetSeg = getSegmentOperand();
	int targetAddress = getAddressOperand();

	targetSeg.setWord(targetAddress, (short)cpu.gdtr.getLimit());
	targetSeg.setDoubleWord(targetAddress + 2, cpu.gdtr.getBase());
    }

    private final void sldt()
    {
	setShortOperand((short)cpu.ldtr.getSelector());
    }

    private final void smsw()
    {
	setShortOperand((short)cpu.getCR0());
    }

    private final int ud2() 
    {
	throw exceptionUD;
    }

    private final void wbinvd()
    {
	System.err.println("WBINVD: currently empty");
    }

    private final void wrmsr() 
    {
	if (cpu.getCPL() == 0) {
	    switch (cpu.ecx) {
	    case 0x1b:
		return;
	    default:
		System.err.println("Unknown MSR: ECX:0x" + Integer.toHexString(cpu.ecx));
		//throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    }
	} else
	    throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
    }

    private final void setSegment(Segment segment)
    {
	switch(getMicrocode()) {
	case CS: cpu.cs = segment; return;
	case ES: cpu.es = segment; return;
	case SS: cpu.ss = segment; return;
	case DS: cpu.ds = segment; return;
	case FS: cpu.fs = segment; return;
	case GS: cpu.gs = segment; return;
	}
    }

    protected final void loadSegment(int segment, int selector)
    {
	selector &= 0xffff;

	long segmentDescriptor = 0;
	if (selector < 0x4) {
	    switch (segment) {
	    case SS:
		throw new ProcessorException(Processor.PROC_EXCEPTION_GP, 0, true);
	    case DS:
		cpu.ds = SegmentFactory.NULL_SEGMENT;
		return;
	    case ES:
		cpu.es = SegmentFactory.NULL_SEGMENT;
		return;
	    case FS:
		cpu.fs = SegmentFactory.NULL_SEGMENT;
		return;
	    case GS:
		cpu.gs = SegmentFactory.NULL_SEGMENT;
		return;
	    default:
		return;
	    }
	}

        Segment newSegment = null;
        try {
            newSegment = cpu.getSegment(selector);
        } catch (ProcessorException e) {
            throw new IllegalStateException(e.toString());
        }


	switch (segment) {
	case CS: cpu.cs = newSegment; return;
	case ES: cpu.es = newSegment; return;
	case SS:
	    cpu.ss = newSegment;
	    cpu.eflagsInterruptEnable = false;
	    return;
	case DS: cpu.ds = newSegment; return;
	case FS: cpu.fs = newSegment; return;
	case GS: cpu.gs = newSegment; return;
	}    }

    public final int immediateCount(int operand)
    {
	return ProtectedModeDecoder.immediateCount(operand);
    }

    public final int operandCount(int operation)
    {
	return ProtectedModeDecoder.operandCount(operation);
    }

    private final boolean checkIOPermissionsByte(int ioportAddress)
    {
	if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel)
	    return true;

	int ioPermMapBaseAddress = 0xffff & cpu.tss.getWord(102);
	try {
	    byte ioPermMapByte = cpu.tss.getByte(ioPermMapBaseAddress + (ioportAddress & ~0x7));
	    return (ioPermMapByte & (1 << (ioportAddress & 0x7))) == 0;
	} catch (ProcessorException p) {
	    if (p.getVector() == Processor.PROC_EXCEPTION_GP)
		return false;
	    else
		throw p;
	}
    }

    private final boolean checkIOPermissionsShort(int ioportAddress)
    {
	if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel)
	    return true;

	int ioPermMapBaseAddress = 0xffff & cpu.tss.getWord(102);
	try {
	    short ioPermMapShort = cpu.tss.getWord(ioPermMapBaseAddress + (ioportAddress & ~0x7));
	    return (ioPermMapShort & (3 << (ioportAddress & 0x7))) == 0;
	} catch (ProcessorException p) {
	    if (p.getVector() == Processor.PROC_EXCEPTION_GP)
		return false;
	    else
		throw p;
	}
    }

    private final boolean checkIOPermissionsInt(int ioportAddress)
    {
	if (cpu.getCPL() <= cpu.eflagsIOPrivilegeLevel)
	    return true;

	int ioPermMapBaseAddress = 0xffff & cpu.tss.getWord(102);
	try {
	    short ioPermMapShort = cpu.tss.getWord(ioPermMapBaseAddress + (ioportAddress & ~0x7));
	    return (ioPermMapShort & (7 << (ioportAddress & 0x7))) == 0;
	} catch (ProcessorException p) {
	    if (p.getVector() == Processor.PROC_EXCEPTION_GP)
		return false;
	    else
		throw p;
	}
    }    
}
