/*
    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 RealModeFirstStageCodeBlock extends FirstStageCodeBlock implements RealModeCodeBlock
{
    //private static final ProcessorException exceptionDB = new ProcessorException(Processor.PROC_EXCEPTION_DB, true);
    //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 exceptionNM = new ProcessorException(Processor.PROC_EXCEPTION_NM, true);
    //private static final ProcessorException exceptionDF = new ProcessorException(Processor.PROC_EXCEPTION_DF, 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, true);
    private static final ProcessorException exceptionGP = new ProcessorException(Processor.PROC_EXCEPTION_GP, true);
    //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, true);
    //private static final ProcessorException exceptionMC = new ProcessorException(Processor.PROC_EXCEPTION_MC, true);
    //private static final ProcessorException exceptionXF = new ProcessorException(Processor.PROC_EXCEPTION_XF, true);
    

    private boolean eipUpdated;

    public RealModeFirstStageCodeBlock()
    {
    }

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

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

    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("Failed To Execute Block: " + e);

	    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.handleRealModeException(e.getVector());
	}
        
	return Math.max(executeCount, 0);
    }

    private final void dispatchOpcode() throws ProcessorException
    {
        int uCode = getMicrocode();

	switch (uCode) {
	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 BOUND_O16_A16: bound_o16_a16(); 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_O16_A16: call_o16_a16(); return;
	case CALL_O32_A16: call_o32_a16(); return;

	case CALLF_O16_A16: callf_o16_a16(); return;

	case CALLN_O16_A16: calln_o16_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 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 CWD: cwd(); 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_O16_A16: enter_o16_a16(); return;

	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 FNLDENV_14: break;
	case FNSTENV_14: fnstenv_14(); return;
	case FNLDENV_28: break;
	case FNSTENV_28: break;
	    //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_O16_O8: in_o16_o8(); return;
	case IN_O32_O8: in_o32_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_A16:  ins_o8_a16();  return;

	case INT_O16_A16: int_o16_a16(); return;
	case INT3_O16_A16: int3_o16_a16(); return;
	case INTO_O16_A16: into_o16_a16(); return;

	case IRET_O16_A16: iret_o16_a16(); return;

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

	case JMP_O16_NEAR_RELATIVE: jmp_o16_near_relative(); return;

	case JMP_O16_NEAR_ABSOLUTE: jmp_o16_near_absolute(); return;

	case JMP_O8_SHORT: jmp_o8_short(); return;

	case JCXZ_A16: jcxz_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 JBE_O8:   jbe_o8();   return;
	case JBE_O16:  jbe_o16();  return;
	case JNBE_O8:  jnbe_o8();  return;
	case JNBE_O16: jnbe_o16(); return;

	case JL_O8:    jl_o8();    return;
	case JL_O16:   jl_o16();   return;
	case JNL_O8:   jnl_o8();   return;
	case JNL_O16:  jnl_o16();  return;

	case JLE_O8:   jle_o8();   return;
	case JLE_O16:  jle_o16();  return;
	case JNLE_O8:  jnle_o8();  return;
	case JNLE_O16: jnle_o16(); return;

	case JO_O8:    jo_o8();    return;
	case JO_O16:   jo_o16();   return;
	case JNO_O8:   jno_o8();   return;
	case JNO_O16:  jno_o16();  return;

	case JP_O8:    jp_o8();    return;
	case JP_O16:   jp_o16();   return;
	case JNP_O8:   jnp_o8();   return;
	case JNP_O16:  jnp_o16();  return;

	case JS_O8:    js_o8();    return;
	case JS_O16:   js_o16();   return;
	case JNS_O8:   jns_o8();   return;
	case JNS_O16:  jns_o16();  return;

	case JZ_O8:   jz_o8();   return;
	case JZ_O16:  jz_o16();  return;
	case JNZ_O8:  jnz_o8();  return;
	case JNZ_O16: jnz_o16(); return;

	case LAHF: lahf(); return;
	case SAHF: sahf(); return;

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

	case LEAVE_O16_A16: leave_o16_a16(); return;

	case LES_O16_A16: les_o16_a16(); return;
	case LDS_O16_A16: lds_o16_a16(); return;
	case LDS_O32_A16: lds_o32_a16(); return;
	case LFS_O16_A16: lfs_o16_a16(); return;
	case LGS_O16_A16: lgs_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 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 LOOP_O16_A16:   loop_o16_a16();   return;
	case LOOP_O32_A16:   loop_o32_a16();   return;
	case LOOPZ_O16_A16:  loopz_o16_a16();  return;
	case LOOPNZ_O16_A16: loopnz_o16_a16(); return;

	case LSS_O16_A16: lss_o16_a16(); return;
	case LSS_O32_A16: lss_o32_a16(); return;

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

	case MOV_TO_CR_O32: mov_to_cr_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 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_O8_O16: out_o8_o16(); return;
	case OUT_O8_O32: out_o8_o32(); 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_A16:  outs_o8_a16();  return;
	case OUTS_O16_A16: outs_o16_a16(); return;
	case OUTS_O32_A16: outs_o32_a16(); return;

	case POP_O16_A16: pop_o16_a16(); return;
	case POP_O32_A16: pop_o32_a16(); return;

	case POPA_O16_A16: popa_o16_a16(); return;
	case POPA_O32_A16: popa_o32_a16(); return;

	case POPF_O16_A16: popf_o16_a16(); return;
	case POPF_O32_A16: popf_o32_a16(); return;

	case PUSH_O16_A16: push_o16_a16(); return;
	case PUSH_O32_A16: push_o32_a16(); return;

	case PUSHA_O16_A16: pusha_o16_a16(); return;
	case PUSHA_O32_A16: pusha_o32_a16(); return;

	case PUSHF_O16_A16: pushf_o16_a16(); return;
	case PUSHF_O32_A16: pushf_o32_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 REPE_CMPS_O8_A16:  repe_cmps_o8_a16();  return;
	case REPE_CMPS_O16_A16: repe_cmps_o16_a16(); return;

	case REP_INS_O8_A16:  rep_ins_o8_a16();  return;
	case REP_INS_O16_A16: rep_ins_o16_a16(); return;
	case REP_INS_O32_A16: rep_ins_o32_a16(); 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_A16:  rep_outs_o8_a16();  return; 
	case REP_OUTS_O16_A16: rep_outs_o16_a16(); return;
	case REP_OUTS_O32_A16: rep_outs_o32_a16(); 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_A16: ret_o16_a16(); return;
	case RET_O32_A16: ret_o32_a16(); return;

	case RET_IW_O16_A16: ret_iw_o16_a16(); return;

	case RETF_O16_A16: retf_o16_a16(); return;

	case RETF_IW_O16_A16: 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 SALC: salc(); 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 SHL_O8:  shl_o8();  return;
	case SHL_O16: shl_o16(); return;
	case SHL_O32: shl_o32(); return;

	case SHLD_O16: shld_o16(); return;
	case SHLD_O32: shld_o32(); 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 SIDT_O16: sidt_o16(); return;
	case SIDT_O32: sidt_o32(); 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 WAIT: waitOp(); 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;

	case EIP_UPDATE: eipUpdate(); return;
	}
	System.out.println("Real Mode: Unimplemented Opcode: " + microcodes[microcodesPosition - 1]);
	throw unimplemented;
    }

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

    private final void bound_o16_a16() throws ProcessorException
    {
	int arrayBounds = getIntOperand();
	short lowerBound = (short)arrayBounds;
	short upperBound = (short)(arrayBounds >> 16);

	short index = getShortOperand();

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

    private final void call_o16_a16() throws ProcessorException
    {
	short offset = getShortOperand();
	if (((cpu.esp & 0xffff) < 2) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;

	//cpu.esp -= 2;
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp - 2) & 0xffff);
	cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
	cpu.eip = (cpu.eip + offset) & 0xffff;
        //System.out.println("call eip "+cpu.eip);
    }

    private final void call_o32_a16() throws ProcessorException
    {
	int offset = getIntOperand();
	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;

	if ((cpu.eip + offset) > 0xffff)
	    throw exceptionGP;

	//cpu.esp -= 4;
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp - 4) & 0xffff);
	cpu.ss.setDoubleWord(cpu.esp & 0xffff, cpu.eip);
	cpu.eip = cpu.eip + offset;
    }

    private final void callf_o16_a16() throws ProcessorException
    {
	int jump = getIntOperand();

	int tempCS = jump >>> 16;
	int tempIP = jump & 0xffff;

	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;
	//cpu.esp -= 2;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
	cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.cs.getSelector());
	//cpu.esp -= 2;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
	cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
	
	cpu.eip = tempIP;
        cpu.cs.setSelector(tempCS);
    }

    private final void calln_o16_a16() throws ProcessorException
    {
	int target = 0xffff & getShortOperand();
	if (((cpu.esp & 0xffff) < 2) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;
	//cpu.esp -= 2;
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp - 2) & 0xffff);
	cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
	cpu.eip = target;
    }

    private final void cpuid()
    {
	switch (cpu.eax) {
	case 0x00:
	    cpu.eax = 0x03; 
	    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 = 0x00000683; // Pentium III Model 8 Stepping 3
	    cpu.ebx = 0; //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 = 0;
	    cpu.ebx = 0;
	    cpu.ecx = 0;
	    cpu.edx = 0;
	    return;
	}
    }

    private final void enter_o16_a16() throws ProcessorException
    {
	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 & 0xffff) < (2 + frameSize)) && ((tempESP & 0xffff) > 0))
		throw exceptionSS;
	} else {
	    if (((tempESP & 0xffff) < (2 + frameSize + 2 * nestingLevel)) && ((tempESP & 0xffff) > 0))
		throw exceptionSS;
	}

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

        int frameTemp = tempESP & 0xffff;

	if (nestingLevel != 0) {
	    while (--nestingLevel != 0) {
		tempEBP = (tempEBP & ~0xffff) | ((tempEBP - 2) & 0xffff);
		tempESP = (tempESP & ~0xffff) | ((tempESP - 2) & 0xffff);
		cpu.ss.setWord(tempESP & 0xffff, cpu.ss.getWord(tempEBP & 0xffff));
	    }
	    
	    tempESP = (tempESP & ~0xffff) | ((tempESP - 2) & 0xffff);
	    cpu.ss.setWord(tempESP & 0xffff, (short)frameTemp);
	}
	
	cpu.ebp = (tempEBP & 0xffff0000) | (frameTemp & 0xffff);	
        cpu.esp = (tempESP & ~0xffff) | ((tempESP - frameSize) & 0xffff);	
    }

    private final void fnstenv_14()
    {
	Segment seg = getSegmentOperand();
	int addr = getAddressOperand();

	// control word
	seg.setWord(addr, (short)fpu.getControl());
	// status word
	seg.setWord(addr + 2, (short)fpu.getStatus());
	// tag word
	seg.setWord(addr + 4, (short)fpu.getTagWord());
	// instruction pointer
	seg.setWord(addr + 6, (short)fpu.lastIP);
	int w = (fpu.lastOpcode | (int)(fpu.lastIP << 12));
	seg.setWord(addr + 8, (short)w);
	// operand pointer
	seg.setWord(addr + 10, (short)fpu.lastData);
	w = (int)(fpu.lastData << 12);
	seg.setWord(addr + 12, (short)w);
    }

    private final void hlt() throws ProcessorException
    {
	while (true) 
        {
	    if (((cpu.getInterruptFlags() & Processor.IFLAGS_HARDWARE_INTERRUPT) != 0) && cpu.eflagsInterruptEnable)
		return;
	    
            cpu.waitForInterrupt(50);
	    cpu.processClock();
	}
    }
  
    private final void in_o8_o8() throws ProcessorException
    {
	int ioport = 0xff & getByteOperand();

	setByteOperand((byte)cpu.ioports.ioPortReadByte(ioport));
    }

    private final void in_o16_o8() throws ProcessorException
    {
	int ioport = 0xff & getByteOperand();

	setShortOperand((short)cpu.ioports.ioPortReadWord(ioport));
    }

    private final void in_o32_o8() throws ProcessorException
    {
	int ioport = 0xff & getByteOperand();

	setIntOperand((int)cpu.ioports.ioPortReadLong(ioport));
    }

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

	setByteOperand((byte)cpu.ioports.ioPortReadByte(ioport));
    }

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

	setShortOperand((short)cpu.ioports.ioPortReadWord(ioport));
    }

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

	setIntOperand((int)cpu.ioports.ioPortReadLong(ioport));
    }

    private final void ins_o8_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int di = (cpu.edi & 0xffff);
	byte a = (byte)cpu.ioports.ioPortReadByte(ioport);

	setByteOperand(a);
	
	if (cpu.eflagsDirection) {
	    di -= 1;
	} else {
	    di += 1;
	}
	cpu.edi &= ~0xffff;
	cpu.edi |= (di & 0xffff);
    }

    private final void into_o16_a16() throws ProcessorException
    {
	if (!cpu.getOverflowFlag()) return;

	int vector = 4;

        //if ((esp & 0xffff) < 6 && (esp & 0xffff) != 0)
        if (((cpu.esp & 0xffff) < 6) && ((cpu.esp & 0xffff) > 0)) {
	    throw new IllegalStateException("SS Processor Exception Thrown in \"handleInterrupt("+vector+")\"");
            //throw new ProcessorException(ProcessorException.SS);
	    //maybe just change vector value
	}
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        int eflags = cpu.getEFlags() & 0xffff;
        cpu.ss.setWord(cpu.esp & 0xffff, (short)eflags);
        cpu.eflagsInterruptEnable = false;
	cpu.eflagsInterruptEnableSoon = false;
        cpu.eflagsTrap = false;
        cpu.eflagsAlignmentCheck = false;
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.cs.getSelector());
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
        // read interrupt vector
        cpu.eip = 0xffff & cpu.idtr.getWord(4*vector);
        cpu.cs.setSelector(0xffff & cpu.idtr.getWord(4*vector+2));
    }

    private final void int_o16_a16() throws ProcessorException
    {
	int vector = 0xff & getByteOperand();
	if (vector == 0)
	    throw unimplemented;

        //if ((esp & 0xffff) < 6 && (esp & 0xffff) != 0)
        if (((cpu.esp & 0xffff) < 6) && ((cpu.esp & 0xffff) > 0)) {
	    throw new IllegalStateException("SS Processor Exception Thrown in \"handleInterrupt("+vector+")\"");
            //throw new ProcessorException(ProcessorException.SS);
	    //maybe just change vector value
	}
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        int eflags = cpu.getEFlags() & 0xffff;
        cpu.ss.setWord(cpu.esp & 0xffff, (short)eflags);
        cpu.eflagsInterruptEnable = false;
	cpu.eflagsInterruptEnableSoon = false;
        cpu.eflagsTrap = false;
        cpu.eflagsAlignmentCheck = false;
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.cs.getSelector());
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
        // read interrupt vector
        cpu.eip = 0xffff & cpu.idtr.getWord(4*vector);
        cpu.cs.setSelector(0xffff & cpu.idtr.getWord(4*vector+2));
    }

    private final void int3_o16_a16() throws ProcessorException
    {
	int vector = 3;

        //if ((esp & 0xffff) < 6 && (esp & 0xffff) != 0)
        if (((cpu.esp & 0xffff) < 6) && ((cpu.esp & 0xffff) > 0)) {
	    throw new IllegalStateException("SS Processor Exception Thrown in \"handleInterrupt("+vector+")\"");
            //throw new ProcessorException(ProcessorException.SS);
	    //maybe just change vector value
	}
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        int eflags = cpu.getEFlags() & 0xffff;
        cpu.ss.setWord(cpu.esp & 0xffff, (short)eflags);
        cpu.eflagsInterruptEnable = false;
	cpu.eflagsInterruptEnableSoon = false;
        cpu.eflagsTrap = false;
        cpu.eflagsAlignmentCheck = false;
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.cs.getSelector());
        cpu.esp = (cpu.esp & 0xffff0000) | (0xffff & (cpu.esp - 2));
        cpu.ss.setWord(cpu.esp & 0xffff, (short)cpu.eip);
        // read interrupt vector
        cpu.eip = 0xffff & cpu.idtr.getWord(4*vector);
        cpu.cs.setSelector(0xffff & cpu.idtr.getWord(4*vector+2));
    }

    private final void iret_o16_a16() throws ProcessorException
    {
	// 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 & 0xffff0000) | ((cpu.esp + 2) & 0xffff);
	cpu.cs.setSelector(cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff);
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp + 2) & 0xffff);
	int temp = cpu.ss.getWord(cpu.esp & 0xffff);
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp + 2) & 0xffff);
	int w = cpu.getEFlags() & ~0xffff;
	cpu.setEFlags(w | (temp & 0xffff));
    }

    private final void jmp_o16_far() throws ProcessorException
    {
	int offset = getIntOperand();
	cpu.eip = 0xffff & offset;
        cpu.cs.setSelector(0xffff & (offset >>> 16));
    }

    private final void jmp_o32_far() throws ProcessorException
    {
	long offset = getLongOperand();
	cpu.eip = (int)offset;
        cpu.cs.setSelector(0xffff & (int)(offset >>> 32));
    }

    private final void jmp_o16_near_relative() throws ProcessorException
    {
	short offset = getShortOperand();
	cpu.eip = (cpu.eip + offset) & 0xffff;
    }

    private final void jmp_o16_near_absolute() throws ProcessorException
    {
        int neweip = getShortOperand();
	cpu.eip = neweip & 0xffff;
    }

    private final void jmp_o8_short() throws ProcessorException
    {
	byte offset = getByteOperand();
	cpu.eip = (cpu.eip + offset) & 0xffff;
    }

    private final void jcxz_a16() throws ProcessorException
    {
	byte offset = getByteOperand();
	if ((cpu.ecx & 0xffff) != 0)
	    return;	
        cpu.eip = (cpu.eip + offset) & 0xffff;
    }

    private final void jcxz_a32() throws ProcessorException
    {
	int offset = getByteOperand();
        if (cpu.ecx != 0)
	    return;
	cpu.eip = (cpu.eip + offset) & 0xffff;
    }

    private final void jb_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsCarry) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jb_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsCarry) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jb_o32() throws ProcessorException
    {
	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() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsCarry) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnb_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (!cpu.eflagsCarry) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jbe_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsCarry || cpu.eflagsZero) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jbe_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsCarry || cpu.eflagsZero) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jnbe_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if ((!cpu.eflagsCarry) && (!cpu.eflagsZero)) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnbe_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if ((!cpu.eflagsCarry) && (!cpu.eflagsZero)) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jl_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign != cpu.eflagsOverflow) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jl_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign != cpu.eflagsOverflow) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jnl_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign == cpu.eflagsOverflow) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnl_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign == cpu.eflagsOverflow) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jle_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsZero || (cpu.eflagsSign != cpu.eflagsOverflow)) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jle_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsZero || (cpu.eflagsSign != cpu.eflagsOverflow)) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jnle_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if ((!cpu.eflagsZero) && (cpu.eflagsSign == cpu.eflagsOverflow)) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnle_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if ((!cpu.eflagsZero) && (cpu.eflagsSign == cpu.eflagsOverflow)) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jo_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsOverflow) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jo_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsOverflow) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jno_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsOverflow) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jno_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (!cpu.eflagsOverflow) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jp_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsParity) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jp_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsParity) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jnp_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsParity) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnp_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (!cpu.eflagsParity) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void js_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsSign) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void js_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsSign) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jns_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsSign) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jns_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (!cpu.eflagsSign) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jz_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (cpu.eflagsZero) {
            cpu.eip += offset;
            //System.out.println(" jz_o8 eip "+cpu.eip);
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jz_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (cpu.eflagsZero) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void jnz_o8() throws ProcessorException
    {
	byte offset = getByteOperand();
        if (!cpu.eflagsZero) {
            cpu.eip += offset;
            // check whether eip is outside of 0x0000 and 0xffff
            if ((cpu.eip & 0xFFFF0000) == 0)
                return;
            cpu.eip -= offset;
            throw exceptionGP;
        }
    }

    private final void jnz_o16() throws ProcessorException
    {
	short offset = getShortOperand();
        if (!cpu.eflagsZero) {
            cpu.eip += offset;
	    cpu.eip &= 0xffff;
        }

    }

    private final void lahf()
    {
        int result = 0x0200;
        if (cpu.eflagsSign) result |= 0x8000;
        if (cpu.eflagsZero) result |= 0x4000;
        if (cpu.eflagsAuxiliaryCarry) result |= 0x1000;
        if (cpu.eflagsParity) result |= 0x0400;
        if (cpu.eflagsCarry) result |= 0x0100;
        cpu.eax &= 0xffff00ff;
        cpu.eax |= result;
    }

    private final void sahf()
    {
        int ah = (cpu.eax & 0xff00);
        cpu.eflagsCarry          = (0 != (ah & 0x0100));
        cpu.eflagsParity         = (0 != (ah & 0x0400));
        cpu.eflagsAuxiliaryCarry = (0 != (ah & 0x1000));
        cpu.eflagsZero           = (0 != (ah & 0x4000));
        cpu.eflagsSign           = (0 != (ah & 0x8000));
    }

    private final void leave_o16_a16() throws ProcessorException
    {
	int tempESP = (cpu.esp & ~0xffff) | (cpu.ebp & 0xffff);;
	//cpu.esp = (cpu.esp & ~0xffff) | (cpu.ebp & 0xffff);
	int tempEBP = (cpu.ebp & ~0xffff) | (cpu.ss.getWord(tempESP & 0xffff) & 0xffff);
	//cpu.ebp = (cpu.ebp & 0xffff0000) | (cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff);
	if ((tempEBP > 0xffff) | (cpu.ebp < 0)) {
	    System.out.println("Throwing dodgy leave exception");
	    throw exceptionGP;	
	}
	cpu.esp = (tempESP & ~0xffff) | ((tempESP + 2) & 0xffff);
	cpu.ebp = tempEBP;
    }

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

	int pointer = getIntOperand();
	setShortOperand((short)pointer);
	cpu.es.setSelector(pointer >>> 16);
    }

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

	int pointer = getIntOperand();
	setShortOperand((short)pointer);
	cpu.ds.setSelector(pointer >>> 16);
    }

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

	long pointer = getLongOperand();
	setIntOperand((int)pointer);
	cpu.ds.setSelector((int)(pointer >> 32) & 0xffff);
    }


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

	int pointer = getIntOperand();
	setShortOperand((short)pointer);
	cpu.fs.setSelector(pointer >>> 16);
    }

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

	int pointer = getIntOperand();
	setShortOperand((short)pointer);
	cpu.gs.setSelector(pointer >>> 16);
    }

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

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

    private final void lidt_o16() throws ProcessorException
    {
	long operand = getLongOperand();
        cpu.idtr = cpu.createDescriptorTableSegment((int)((operand >>> 16) & 0x00ffffffl), (int)(operand & 0xffffl));
    }

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

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

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

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

    }

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

    private final void loopz_o16_a16() throws ProcessorException
    {
	byte offset = getByteOperand();
	cpu.ecx = (cpu.ecx & 0xffff0000) | (--cpu.ecx & 0xffff);
	if (((cpu.ecx & 0xffff) != 0) && cpu.eflagsZero) {
	    cpu.eip += offset;
	    cpu.eip &= 0xffff;
	}

    }

    private final void loopnz_o16_a16() throws ProcessorException
    {
	byte offset = getByteOperand();
	cpu.ecx = (cpu.ecx & 0xffff0000) | (--cpu.ecx & 0xffff);
	if (((cpu.ecx & 0xffff) != 0) && !cpu.eflagsZero) {
	    cpu.eip += offset;
	    cpu.eip &= 0xffff;
	}
    }

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

	int pointer = getIntOperand();
	setShortOperand((short)pointer);
	cpu.ss.setSelector((pointer >> 16) & 0xffff);
    }

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

	long pointer = getLongOperand();
	setIntOperand((int)pointer);
	cpu.ss.setSelector((int)(pointer >> 32) & 0xffff);
    }

    private final void mov_to_cr_o32() throws ProcessorException
    {
	int src = getIntOperand();
	switch (microcodes[microcodesPosition++]) {
	case CR0:
	    cpu.setCR0(src); break;
	default:
	    throw unimplemented;
	}
    }

    private final void out_o8_o8() throws ProcessorException
    {
	byte data = getByteOperand();
	int ioport = 0xff & getByteOperand();
	cpu.ioports.ioPortWriteByte(ioport, 0xff & data);
    }

    private final void out_o8_o16() throws ProcessorException
    {
	short data = getShortOperand();
	int ioport = 0xff & getByteOperand();
	cpu.ioports.ioPortWriteWord(ioport, 0xffff & data);
    }

    private final void out_o8_o32() throws ProcessorException
    {
	int data = getIntOperand();
	int ioport = 0xff & getByteOperand();
	cpu.ioports.ioPortWriteLong(ioport, data);
    }

    private final void out_o16_o8() throws ProcessorException
    {
	byte data = getByteOperand();
	int ioport = 0xffff & getShortOperand();
	cpu.ioports.ioPortWriteByte(ioport, 0xff & data);
    }

    private final void out_o16_o16() throws ProcessorException
    {
	short data = getShortOperand();
	int ioport = 0xffff & getShortOperand();
	cpu.ioports.ioPortWriteWord(ioport, 0xffff & data);
    }

    private final void out_o16_o32() throws ProcessorException
    {
	int data = getIntOperand();
	int ioport = 0xffff & getShortOperand();
	cpu.ioports.ioPortWriteLong(ioport, data);
    }

    private final void outs_o8_a16() throws ProcessorException
    {
	int si = (cpu.esi & 0xffff);
	int ioport = 0xffff & getShortOperand();
	int a = getByteOperand() & 0xff;

	cpu.ioports.ioPortWriteByte(ioport, a);

	if (cpu.eflagsDirection) {
	    si -= 1;
	} else {
	    si += 1;
	}
	cpu.esi &= ~0xffff;
	cpu.esi |= (si & 0xffff);
    }

    private final void outs_o16_a16() throws ProcessorException
    {
	int si = (cpu.esi & 0xffff);
	int ioport = 0xffff & getShortOperand();
	int a = getShortOperand() & 0xffff;

	cpu.ioports.ioPortWriteWord(ioport, a);

	if (cpu.eflagsDirection) {
	    si -= 2;
	} else {
	    si += 2;
	}
	cpu.esi &= ~0xffff;
	cpu.esi |= (si & 0xffff);
    }

    private final void outs_o32_a16() throws ProcessorException
    {
	int si = (cpu.esi & 0xffff);
	int ioport = 0xffff & getShortOperand();
	int a = getIntOperand();

	cpu.ioports.ioPortWriteByte(ioport, a);

	if (cpu.eflagsDirection) {
	    si -= 4;
	} else {
	    si += 4;
	}
	cpu.esi &= ~0xffff;
	cpu.esi |= (si & 0xffff);
    }

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

	setShortOperand(value);
    }

    private final void pop_o32_a16() throws ProcessorException
    {
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
	int value = cpu.ss.getDoubleWord((cpu.esp - 4) & 0xffff);
	if (microcodes[microcodesPosition] == SS)
	    cpu.eflagsInterruptEnable = false;

	setIntOperand(value);
    }

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

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

	cpu.edi &= 0xFFFF0000;
	cpu.esi &= 0xFFFF0000;
	cpu.ebp &= 0xFFFF0000;
	cpu.ebx &= 0xFFFF0000;
	cpu.edx &= 0xFFFF0000;
	cpu.ecx &= 0xFFFF0000;
	cpu.eax &= 0xFFFF0000;

	cpu.edi |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	cpu.esi |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	cpu.ebp |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	
	offset += 2; // yes - skip 2 bytes in order to skip SP
	
	cpu.ebx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	cpu.edx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	cpu.ecx |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	cpu.eax |= 0xffff & cpu.ss.getWord(0xffff & offset);
	offset += 2;
	
	offset &= 0xffff;
	cpu.esp &= ~0xffff;
	cpu.esp |= offset;
    }

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

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

	cpu.edi = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	cpu.esi = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	cpu.ebp = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 8;// yes - skip an extra 4 bytes in order to skip SP

	cpu.ebx = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	cpu.edx = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	cpu.ecx = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	cpu.eax = cpu.ss.getDoubleWord(0xffff & offset);
	offset += 4;
	
	offset &= 0xffff;
	cpu.esp &= ~0xffff;
	cpu.esp |= offset;
    }

    private final void popf_o16_a16() throws ProcessorException
    {
	//SS exception?
	int w = 0xffff & cpu.ss.getWord(cpu.esp & 0xffff);
	//cpu.esp += 2;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 2) & 0xffff);
	int temp = cpu.getEFlags() & ~0xffff;
	temp |= w;
	cpu.setEFlags(temp);
    }

    private final void popf_o32_a16() throws ProcessorException
    {
	//SS exception?
	int w = cpu.ss.getDoubleWord(cpu.esp & 0xffff);
	//cpu.esp += 4;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
	boolean vm = cpu.eflagsVirtual8086Mode;
	cpu.setEFlags(w);
	cpu.eflagsVirtual8086Mode = vm;
	cpu.eflagsVirtualInterrupt = false;
	cpu.eflagsVirtualInterruptPending = false;
    }

    private final void push_o16_a16() throws ProcessorException
    {
	short value = getShortOperand();
	if (((cpu.esp & 0xffff) < 2) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;

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

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

	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;
	cpu.esp = (cpu.esp & 0xffff0000) | ((cpu.esp - 4) & 0xffff);      
	cpu.ss.setDoubleWord(cpu.esp & 0xffff, value);
    }
    
    private final void pusha_o16_a16() throws ProcessorException
    {
	int offset = cpu.esp & 0xffff;
	int temp = cpu.esp;
	if ((offset < 16) && (offset > 0))
	    throw exceptionGP;
	
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.eax);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.ecx);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.edx);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.ebx);
	offset -= 2;
	cpu.ss.setWord(offset, (short) temp);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.ebp);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.esi);
	offset -= 2;
	cpu.ss.setWord(offset, (short) cpu.edi);
        
	offset &= 0xffff;
	cpu.esp &= ~0xffff;
	cpu.esp |= offset;
    }

    private final void pusha_o32_a16() throws ProcessorException
    {
	int offset = cpu.esp & 0xffff;
	int temp = cpu.esp;
	if ((offset < 32) && (offset > 0))
	    throw exceptionGP;
	
	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);
        
	offset &= 0xffff;
	cpu.esp &= ~0xffff;
	cpu.esp |= offset;
    }

    private final void pushf_o16_a16() throws ProcessorException
    {
	if (((cpu.esp & 0xffff) < 2) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;
	//cpu.esp -= 2;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 2) & 0xffff);
	short w = (short)cpu.getEFlags();
	cpu.ss.setWord(cpu.esp & 0xffff, w);
    }

    private final void pushf_o32_a16() throws ProcessorException
    {
	if (((cpu.esp & 0xffff) < 4) && ((cpu.esp & 0xffff) > 0))
	    throw exceptionSS;
	//cpu.esp -= 4;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp - 4) & 0xffff);
	int w = cpu.getEFlags() & 0xfcffff ;
	cpu.ss.setDoubleWord(cpu.esp & 0xffff, w);
    }

    private final void rep_ins_o8_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandsAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandsAddress;
	    // TODO:  put interrupt handling here, I think
	    int di = (cpu.edi & 0xffff);
	    byte a = (byte)cpu.ioports.ioPortReadByte(ioport);

	    setByteOperand(a);

	    if (cpu.eflagsDirection) {
		di -= 1;
	    } else {
		di += 1;
	    }
	    cpu.edi &= ~0xffff;
	    cpu.edi |= (di & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void rep_ins_o16_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandsAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandsAddress;
	    // TODO:  put interrupt handling here, I think
	    int di = (cpu.edi & 0xffff);
	    short a = (short)cpu.ioports.ioPortReadWord(ioport);

	    setShortOperand(a);

	    if (cpu.eflagsDirection) {
		di -= 2;
	    } else {
		di += 2;
	    }
	    cpu.edi &= ~0xffff;
	    cpu.edi |= (di & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void rep_ins_o32_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandsAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandsAddress;
	    // TODO:  put interrupt handling here, I think
	    int di = (cpu.edi & 0xffff);
	    int a = cpu.ioports.ioPortReadLong(ioport);

	    setIntOperand(a);

	    if (cpu.eflagsDirection) {
		di -= 4;
	    } else {
		di += 4;
	    }
	    cpu.edi &= ~0xffff;
	    cpu.edi |= (di & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void rep_outs_o8_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandAddress;
	    int si = (cpu.esi & 0xffff);
	    int a = getByteOperand() & 0xff;
	    cpu.ioports.ioPortWriteByte(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		si -= 1;
	    } else {
		si += 1;
	    }
	    cpu.esi &= ~0xffff;
	    cpu.esi |= (si & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void rep_outs_o16_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandAddress;
	    int si = (cpu.esi & 0xffff);
	    int a = getShortOperand() & 0xffff;
	    cpu.ioports.ioPortWriteWord(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		si -= 2;
	    } else {
		si += 2;
	    }
	    cpu.esi &= ~0xffff;
	    cpu.esi |= (si & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void rep_outs_o32_a16() throws ProcessorException
    {
	int ioport = 0xffff & getShortOperand();
	int operandAddress = microcodesPosition;
	skipOperand();
	while ((cpu.ecx & 0xffff) != 0) {
	    microcodesPosition = operandAddress;
	    int si = (cpu.esi & 0xffff);
	    int a = getIntOperand();

	    cpu.ioports.ioPortWriteLong(ioport, a);
	    
	    if (cpu.eflagsDirection) {
		si -= 4;
	    } else {
		si += 4;
	    }
	    cpu.esi &= ~0xffff;
	    cpu.esi |= (si & 0xffff);
	    cpu.ecx--;
	}
    }

    private final void ret_o16_a16() throws ProcessorException
    {
	// 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() throws ProcessorException
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	int tempEIP = cpu.ss.getDoubleWord(cpu.esp & 0xffff);
	if (tempEIP > 0xffff)
	    throw exceptionGP;

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

    private final void ret_iw_o16_a16() throws ProcessorException
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	short offset = getShortOperand();

	cpu.eip = cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff;
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 2 + offset) & 0xffff);
    }

    private final void retf_o16_a16() throws ProcessorException
    {
	// 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.cs.setSelector(cpu.ss.getWord((cpu.esp + 2) & 0xffff) & 0xffff);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4) & 0xffff);
    }

    private final void retf_iw_o16_a16() throws ProcessorException
    {
	// TODO:  supposed to throw SS exception
	// "if top 6 bytes of stack not within stack limits"
	short offset = getShortOperand();

	cpu.eip = cpu.ss.getWord(cpu.esp & 0xffff) & 0xffff;
	cpu.cs.setSelector(cpu.ss.getWord((cpu.esp + 2) & 0xffff) & 0xffff);
	cpu.esp = (cpu.esp & ~0xffff) | ((cpu.esp + 4 + offset) & 0xffff);
    }

    private final void salc()
    {
	if (cpu.eflagsCarry)
	    cpu.eax |= 0x000000ff;
	else
	    cpu.eax &= 0xffffff00;
    }

    private final void sidt_o16() throws ProcessorException
    {
	long result = (cpu.idtr.getLimit() & 0xffffl) | ((cpu.idtr.getBase() & 0xffffffl) << 16);
	setLongOperand(result);
    }

    private final void sidt_o32() throws ProcessorException
    {
	long result = (cpu.idtr.getLimit() & 0xffffl) | ((cpu.idtr.getBase() & 0xffffffffl) << 16);
	setLongOperand(result);
    }

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

    protected final void loadSegment(int segment, int selector)
    {
	switch (segment) {
	case CS: cpu.cs.setSelector(0xffff & selector); return;
	case DS: cpu.ds.setSelector(0xffff & selector); return;
	case SS: cpu.ss.setSelector(0xffff & selector); return;
	case ES: cpu.es.setSelector(0xffff & selector); return;
	case FS: cpu.fs.setSelector(0xffff & selector); return;
	case GS: cpu.gs.setSelector(0xffff & selector); return;
	}
    }

    private final void standardFlags(byte result)
    {
        cpu.eflagsZero = (result == 0);
        cpu.eflagsParity = parityMap[result & 0xff];
	cpu.eflagsSign = (result < 0);
    }

    private final void standardFlags(short result)
    {
        cpu.eflagsZero = (result == 0);
        cpu.eflagsParity = parityMap[result & 0xff];
	cpu.eflagsSign = (result < 0);
    }

    private final void standardFlags(int result)
    {
        cpu.eflagsZero = (result == 0);
        cpu.eflagsParity = parityMap[result & 0xff];
	cpu.eflagsSign = (result < 0);
    }

    private final void bitwiseFlags(byte result)
    {
	cpu.eflagsOverflow = false;
	cpu.eflagsCarry = false;
	standardFlags(result);
    }

    private final void bitwiseFlags(short result)
    {
	cpu.eflagsOverflow = false;
	cpu.eflagsCarry = false;
	standardFlags(result);
    }

    private final void bitwiseFlags(int result)
    {
	cpu.eflagsOverflow = false;
	cpu.eflagsCarry = false;
	standardFlags(result);
    }

    public int immediateCount(int operand)
    {
	return RealModeDecoder.immediateCount(operand);
    }

    public int operandCount(int operation)
    {
	return RealModeDecoder.operandCount(operation);
    }   
}
