#ifndef ROSE_X86INSTRUCTIONSEMANTICS_H
#define ROSE_X86INSTRUCTIONSEMANTICS_H

//#include "rose.h"
#include "semanticsModule.h"
#include <cassert>
#include <cstdio>
#include <iostream>
#include "rose.h"

#include "integerOps.h"


#include "SgAsmx86Instruction.h"
#include "SgAsmExpression.h"
#include "conversions.h"

/* Returns the segment register corresponding to the specified register reference address expression. */
static inline X86SegmentRegister getSegregFromMemoryReference(SgAsmMemoryReferenceExpression* mr) {
    X86SegmentRegister segreg = x86_segreg_none;
    SgAsmx86RegisterReferenceExpression* seg = isSgAsmx86RegisterReferenceExpression(mr->get_segment());
    if (seg) {
        ROSE_ASSERT(seg->get_register_class() == x86_regclass_segment);
        segreg = (X86SegmentRegister)(seg->get_register_number());
    } else {
        ROSE_ASSERT(!"Bad segment expr");
    }
    if (segreg == x86_segreg_none) segreg = x86_segreg_ds;
    return segreg;
}

template <typename Policy, template <size_t> class WordType>
struct X86InstructionSemantics {
#   ifdef Word
#       error "Having a macro called \"Word\" conflicts with x86InstructionSemantics.h"
#   else
#       define Word(Len) WordType<(Len)>
#   endif

    Policy& policy;

    X86InstructionSemantics(Policy& policy)
        : policy(policy)
        {}
    virtual ~X86InstructionSemantics() {}

    /* If the counter (cx register) is non-zero then decrement it. The return value is a flag indicating whether cx was
     * originally non-zero. */
    Word(1) stringop_setup_loop() {
        Word(1) ecxNotZero = policy.invert(policy.equalToZero(policy.readGPR(x86_gpr_cx)));
        policy.writeGPR(x86_gpr_cx,
                        policy.add(policy.readGPR(x86_gpr_cx),
                                   policy.ite(ecxNotZero, number<32>(-1), number<32>(0))));
        return ecxNotZero;
    }

    /* Instruction semantics for rep_stosN where N is 1(b), 2(w), or 4(d).
     * This method handles semantics for one iteration of stosN.
     * See https://siyobik.info/index.php?module=x86&id=279 */
    template<size_t N>
    void rep_stos_semantics(SgAsmx86Instruction *insn) {
        Word(1) ecxNotZero = policy.invert(policy.equalToZero(policy.readGPR(x86_gpr_cx)));

        /* Fill memory pointed to by ES:[DI] with contents of AX register if counter was not zero. */
        policy.writeMemory(x86_segreg_es,
                           policy.readGPR(x86_gpr_di),
                           extract<0, 8*N>(policy.readGPR(x86_gpr_ax)),
                           ecxNotZero);

        /* Update DI by incrementing or decrementing by number of bytes copied (only if counter is nonzero) */
        policy.writeGPR(x86_gpr_di,
                        policy.add(policy.readGPR(x86_gpr_di),
                                   policy.ite(ecxNotZero,
                                              policy.ite(policy.readFlag(x86_flag_df), number<32>(-N), number<32>(N)),
                                              number<32>(0))));

        /* Decrement the counter if not already zero. */
        policy.writeGPR(x86_gpr_cx,
                        policy.add(policy.readGPR(x86_gpr_cx),
                                   policy.ite(ecxNotZero, number<32>(-1), number<32>(0))));

        /* If counter is now nonzero then repeat this instruction, otherwise go to the next one. */
        ecxNotZero = policy.invert(policy.equalToZero(policy.readGPR(x86_gpr_cx)));
        policy.writeIP(policy.ite(ecxNotZero, number<32>((uint32_t)(insn->get_address())), policy.readIP()));
    }

    /* Instruction semantics for stosN where N is 1(b), 2(w), or 4(d) */
    template<size_t N>
    void stos_semantics(SgAsmx86Instruction *insn) {
        const SgAsmExpressionPtrList& operands = insn->get_operandList()->get_operands();
        ROSE_ASSERT(operands.size() == 0);
        ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);

        /* Fill memory pointed to by ES:[DI] with contents of AX. */
        policy.writeMemory(x86_segreg_es,
                           policy.readGPR(x86_gpr_di),
                           extract<0, 8*N>(policy.readGPR(x86_gpr_ax)),
                           policy.true_());

        /* Update DI */ /*FIXME: Is this correct? */
        policy.writeGPR(x86_gpr_di,
                        policy.add(policy.readGPR(x86_gpr_di),
                                   policy.ite(policy.readFlag(x86_flag_df), number<32>(-N), number<32>(N))));
    }
    

    template <size_t Len>
    Word(Len) invertMaybe(const Word(Len)& w, bool inv) {
        if (inv) {
            return policy.invert(w);
        } else {
            return w;
        }
    }

    template <size_t Len>
    Word(Len) number(uintmax_t v) {
        return policy.template number<Len>(v);
    }

    template <size_t From, size_t To, size_t Len>
        Word(To - From) extract(Word(Len) w) {
        return policy.template extract<From, To>(w);
    }

    template <size_t From, size_t To>
        Word(To) signExtend(Word(From) w) {
        return policy.template signExtend<From, To>(w);
    }

    template <size_t Len>
    Word(1) greaterOrEqualToTen(Word(Len) w) {
        Word(Len) carries = number<Len>(0);
        policy.addWithCarries(w, number<Len>(6), policy.false_(), carries);
        return extract<Len - 1, Len>(carries);
    }

    Word(32) readEflags() {
        return policy.concat(readFlags(), number<16>(0x0000));
    }

    Word(16) readFlags() {
        return policy.concat(policy.readFlag((X86Flag)0 ),
                             policy.concat(policy.readFlag((X86Flag)1 ),
                             policy.concat(policy.readFlag((X86Flag)2 ),
                             policy.concat(policy.readFlag((X86Flag)3 ),
                             policy.concat(policy.readFlag((X86Flag)4 ),
                             policy.concat(policy.readFlag((X86Flag)5 ),
                             policy.concat(policy.readFlag((X86Flag)6 ),
                             policy.concat(policy.readFlag((X86Flag)7 ),
                             policy.concat(policy.readFlag((X86Flag)8 ),
                             policy.concat(policy.readFlag((X86Flag)9 ),
                             policy.concat(policy.readFlag((X86Flag)10),
                             policy.concat(policy.readFlag((X86Flag)11),
                             policy.concat(policy.readFlag((X86Flag)12),
                             policy.concat(policy.readFlag((X86Flag)13),
                             policy.concat(policy.readFlag((X86Flag)14),
                                           policy.readFlag((X86Flag)15))))))))))))))));
    }

    template <size_t Len/*bits*/>
    Word(Len) readMemory(X86SegmentRegister segreg, const Word(32)& addr, Word(1) cond) {
        return policy.template readMemory<Len>(segreg, addr, cond);
    }

    Word(32) readEffectiveAddress(SgAsmExpression* expr) {
        assert (isSgAsmMemoryReferenceExpression(expr));
        return read32(isSgAsmMemoryReferenceExpression(expr)->get_address());
    }

    /* Returns an eight-bit value desribed by an instruction operand. */
    Word(8) read8(SgAsmExpression* e) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        Word(32) rawValue = policy.readGPR(reg);
                        switch (rre->get_position_in_register()) {
                            case x86_regpos_low_byte: return extract<0, 8>(rawValue);
                            case x86_regpos_high_byte: return extract<8, 16>(rawValue);
                            default: ROSE_ASSERT(!"Bad position in register");
                        }
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmBinaryAdd: {
                return policy.add(read8(isSgAsmBinaryAdd(e)->get_lhs()), read8(isSgAsmBinaryAdd(e)->get_rhs()));
            }
            case V_SgAsmBinaryMultiply: {
                SgAsmByteValueExpression* rhs = isSgAsmByteValueExpression(isSgAsmBinaryMultiply(e)->get_rhs());
                ROSE_ASSERT(rhs);
                SgAsmExpression* lhs = isSgAsmBinaryMultiply(e)->get_lhs();
                return extract<0, 8>(policy.unsignedMultiply(read8(lhs), read8(rhs)));
            }
            case V_SgAsmMemoryReferenceExpression: {
                return readMemory<8>(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                     readEffectiveAddress(e), policy.true_());
            }
            case V_SgAsmByteValueExpression:
            case V_SgAsmWordValueExpression:
            case V_SgAsmDoubleWordValueExpression:
            case V_SgAsmQuadWordValueExpression: {
                uint64_t val = SageInterface::getAsmSignedConstant(isSgAsmValueExpression(e));
                return number<8>(val & 0xFFU);
            }
            default: {
                fprintf(stderr, "Bad variant %s in read8\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Returns a 16-bit value described by an instruction operand. */
    Word(16) read16(SgAsmExpression* e) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                ROSE_ASSERT(rre->get_position_in_register() == x86_regpos_word);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        Word(32) rawValue = policy.readGPR(reg);
                        return extract<0, 16>(rawValue);
                    }
                    case x86_regclass_segment: {
                        X86SegmentRegister sr = (X86SegmentRegister)(rre->get_register_number());
                        Word(16) value = policy.readSegreg(sr);
                        return value;
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmBinaryAdd: {
                return policy.add(read16(isSgAsmBinaryAdd(e)->get_lhs()), read16(isSgAsmBinaryAdd(e)->get_rhs()));
            }
            case V_SgAsmBinaryMultiply: {
                SgAsmByteValueExpression* rhs = isSgAsmByteValueExpression(isSgAsmBinaryMultiply(e)->get_rhs());
                ROSE_ASSERT(rhs);
                SgAsmExpression* lhs = isSgAsmBinaryMultiply(e)->get_lhs();
                return extract<0, 16>(policy.unsignedMultiply(read16(lhs), read8(rhs)));
            }
            case V_SgAsmMemoryReferenceExpression: {
                return readMemory<16>(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                      readEffectiveAddress(e), policy.true_());
            }
            case V_SgAsmByteValueExpression:
            case V_SgAsmWordValueExpression:
            case V_SgAsmDoubleWordValueExpression:
            case V_SgAsmQuadWordValueExpression: {
                uint64_t val = SageInterface::getAsmSignedConstant(isSgAsmValueExpression(e));
                return number<16>(val & 0xFFFFU);
            }
            default: {
                fprintf(stderr, "Bad variant %s in read16\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Returns a 32-bit value described by an instruction operand. */
    Word(32) read32(SgAsmExpression* e) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        Word(32) rawValue = policy.readGPR(reg);
                        switch (rre->get_position_in_register()) {
                            case x86_regpos_dword:
                            case x86_regpos_all:
                                return rawValue;
                            case x86_regpos_word:
                                return policy.concat(extract<0, 16>(rawValue), number<16>(0));
                            default:
			      fprintf(stderr, "Error: position %d\n", rre->get_position_in_register());
			      ROSE_ASSERT(!"bad position in register");
                        }
                    }
                    case x86_regclass_segment: {
                        ROSE_ASSERT(rre->get_position_in_register() == x86_regpos_dword ||
                                    rre->get_position_in_register() == x86_regpos_all);
                        X86SegmentRegister sr = (X86SegmentRegister)(rre->get_register_number());
                        Word(16) value = policy.readSegreg(sr);
                        return policy.concat(value, number<16>(0));
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmBinaryAdd: {
                return policy.add(read32(isSgAsmBinaryAdd(e)->get_lhs()), read32(isSgAsmBinaryAdd(e)->get_rhs()));
            }
            case V_SgAsmBinaryMultiply: {
                SgAsmByteValueExpression* rhs = isSgAsmByteValueExpression(isSgAsmBinaryMultiply(e)->get_rhs());
                ROSE_ASSERT(rhs);
                SgAsmExpression* lhs = isSgAsmBinaryMultiply(e)->get_lhs();
                return extract<0, 32>(policy.unsignedMultiply(read32(lhs), read8(rhs)));
            }
            case V_SgAsmMemoryReferenceExpression: {
                return readMemory<32>(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                      readEffectiveAddress(e), policy.true_());
            }
            case V_SgAsmByteValueExpression:
            case V_SgAsmWordValueExpression:
            case V_SgAsmDoubleWordValueExpression:
            case V_SgAsmQuadWordValueExpression: {
                uint64_t val = SageInterface::getAsmSignedConstant(isSgAsmValueExpression(e));
                return number<32>(val & 0xFFFFFFFFU);
            }
            default: {
                fprintf(stderr, "Bad variant %s in read32\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Replaces the least significant byte of a general purpose register with a new value. */
    void updateGPRLowByte(X86GeneralPurposeRegister reg, const Word(8)& value) {
        Word(32) oldValue = policy.readGPR(reg);
        policy.writeGPR(reg, policy.concat(value, extract<8, 32>(oldValue)));
    }

    /* Replaces bits 8 through 15 of a 32-bit register with the specified 8-bit value. */
    void updateGPRHighByte(X86GeneralPurposeRegister reg, const Word(8)& value) {
        Word(32) oldValue = policy.readGPR(reg);
        policy.writeGPR(reg, policy.concat(extract<0, 8>(oldValue),
                                           policy.concat(value, extract<16, 32>(oldValue))));
    }

    /* Replaces the least significant 16 bits of a general purpose register with a new value. */
    void updateGPRLowWord(X86GeneralPurposeRegister reg, const Word(16)& value) {
        Word(32) oldValue = policy.readGPR(reg);
        policy.writeGPR(reg, policy.concat(value, extract<16, 32>(oldValue)));
    }

    /* Writes the specified eight-bit value to the location specified by an instruction operand. */
    void write8(SgAsmExpression* e, const Word(8)& value) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        switch (rre->get_position_in_register()) {
                            case x86_regpos_low_byte: updateGPRLowByte(reg, value); break;
                            case x86_regpos_high_byte: updateGPRHighByte(reg, value); break;
                            default: ROSE_ASSERT(!"Bad position in register");
                        }
                        break;
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmMemoryReferenceExpression: {
                policy.writeMemory(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                   readEffectiveAddress(e), value, policy.true_());
                break;
            }
            default: {
                fprintf(stderr, "Bad variant %s in write8\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Writes the specified 16-bit value to the location specified by an instruction operand. */
    void write16(SgAsmExpression* e, const Word(16)& value) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        switch (rre->get_position_in_register()) {
                            case x86_regpos_word: updateGPRLowWord(reg, value); break;
                            default: ROSE_ASSERT(!"Bad position in register");
                        }
                        break;
                    }
                    case x86_regclass_segment: {
                        X86SegmentRegister sr = (X86SegmentRegister)(rre->get_register_number());
                        policy.writeSegreg(sr, value);
                        break;
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmMemoryReferenceExpression: {
                policy.writeMemory(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                   readEffectiveAddress(e), value, policy.true_());
                break;
            }
            default: {
                fprintf(stderr, "Bad variant %s in write16\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Writes the specified 32-bit value to the location specified by an instruction operand. */
    void write32(SgAsmExpression* e, const Word(32)& value) {
        switch (e->variantT()) {
            case V_SgAsmx86RegisterReferenceExpression: {
                SgAsmx86RegisterReferenceExpression* rre = isSgAsmx86RegisterReferenceExpression(e);
                switch (rre->get_register_class()) {
                    case x86_regclass_gpr: {
                        X86GeneralPurposeRegister reg = (X86GeneralPurposeRegister)(rre->get_register_number());
                        switch (rre->get_position_in_register()) {
                            case x86_regpos_dword:
                            case x86_regpos_all: {
                                break;
                            }
                            default: ROSE_ASSERT(!"Bad position in register");
                        }
                        policy.writeGPR(reg, value);
                        break;
                    }
                    case x86_regclass_segment: { // Used for pop of segment registers
                        X86SegmentRegister sr = (X86SegmentRegister)(rre->get_register_number());
                        policy.writeSegreg(sr, extract<0, 16>(value));
                        break;
                    }
                    default: {
                        fprintf(stderr, "Bad register class %s\n", regclassToString(rre->get_register_class()));
                        abort();
                    }
                }
                break;
            }
            case V_SgAsmMemoryReferenceExpression: {
                policy.writeMemory(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(e)),
                                   readEffectiveAddress(e), value, policy.true_());
                break;
            }
            default: {
                fprintf(stderr, "Bad variant %s in write32\n", e->class_name().c_str());
                abort();
            }
        }
    }

    /* Returns true if W has an even number of bits set; false for an odd number */
    Word(1) parity(Word(8) w) {
        Word(1) p01 = policy.xor_(extract<0, 1>(w), extract<1, 2>(w));
        Word(1) p23 = policy.xor_(extract<2, 3>(w), extract<3, 4>(w));
        Word(1) p45 = policy.xor_(extract<4, 5>(w), extract<5, 6>(w));
        Word(1) p67 = policy.xor_(extract<6, 7>(w), extract<7, 8>(w));
        Word(1) p0123 = policy.xor_(p01, p23);
        Word(1) p4567 = policy.xor_(p45, p67);
        return policy.invert(policy.xor_(p0123, p4567));
    }

    /* Sets flags: parity, sign, and zero */
    template <size_t Len>
    void setFlagsForResult(const Word(Len)& result) {
        policy.writeFlag(x86_flag_pf, parity(extract<0, 8>(result)));
        policy.writeFlag(x86_flag_sf, extract<Len - 1, Len>(result));
        policy.writeFlag(x86_flag_zf, policy.equalToZero(result));
    }

    /* Sets flags conditionally. Sets parity, sign, and zero flags if COND is true. */
    template <size_t Len>
    void setFlagsForResult(const Word(Len)& result, Word(1) cond) {
        policy.writeFlag(x86_flag_pf, policy.ite(cond, parity(extract<0, 8>(result)), policy.readFlag(x86_flag_pf)));
        policy.writeFlag(x86_flag_sf, policy.ite(cond, extract<Len - 1, Len>(result), policy.readFlag(x86_flag_sf)));
        policy.writeFlag(x86_flag_zf, policy.ite(cond, policy.equalToZero(result), policy.readFlag(x86_flag_zf)));
    }

    /* Adds A and B and adjusts condition flags. Can be used for subtraction if B is two's complement and invertCarries is set. */
    template <size_t Len>
    Word(Len) doAddOperation(const Word(Len)& a, const Word(Len)& b, bool invertCarries, Word(1) carryIn) {
        Word(Len) carries = number<Len>(0);
        Word(Len) result = policy.addWithCarries(a, b, invertMaybe(carryIn, invertCarries), carries/*out*/);
        setFlagsForResult<Len>(result);
        policy.writeFlag(x86_flag_af, invertMaybe(extract<3, 4>(carries), invertCarries));
        policy.writeFlag(x86_flag_cf, invertMaybe(extract<Len - 1, Len>(carries), invertCarries));
        policy.writeFlag(x86_flag_of, policy.xor_(extract<Len - 1, Len>(carries), extract<Len - 2, Len - 1>(carries)));
        return result;
    }

    /* Conditionally adds A and B and adjusts condition flags. Can be used for subtraction if B is two's complement and
     * invertCarries is set. Does nothing if COND is false. */ 
    template <size_t Len>
    Word(Len) doAddOperation(const Word(Len)& a, const Word(Len)& b, bool invertCarries, Word(1) carryIn, Word(1) cond) {
        Word(Len) carries = number<Len>(0);
        Word(Len) result = policy.addWithCarries(a, b, invertMaybe(carryIn, invertCarries), carries/*out*/);
        setFlagsForResult<Len>(result, cond);
        policy.writeFlag(x86_flag_af,
                         policy.ite(cond,
                                    invertMaybe(extract<3, 4>(carries), invertCarries),
                                    policy.readFlag(x86_flag_af)));
        policy.writeFlag(x86_flag_cf,
                         policy.ite(cond,
                                    invertMaybe(extract<Len - 1, Len>(carries), invertCarries),
                                    policy.readFlag(x86_flag_cf)));
        policy.writeFlag(x86_flag_of,
                         policy.ite(cond,
                                    policy.xor_(extract<Len - 1, Len>(carries), extract<Len - 2, Len - 1>(carries)),
                                    policy.readFlag(x86_flag_of)));
        return result;
    }

    /* Does increment (decrement with DEC set), and adjusts condition flags. */
    template <size_t Len>
    Word(Len) doIncOperation(const Word(Len)& a, bool dec, bool setCarry) {
        Word(Len) carries = number<Len>(0);
        Word(Len) result = policy.addWithCarries(a, number<Len>(dec ? -1 : 1), policy.false_(), carries/*out*/);
        setFlagsForResult<Len>(result);
        policy.writeFlag(x86_flag_af, invertMaybe(extract<3, 4>(carries), dec));
        policy.writeFlag(x86_flag_of, policy.xor_(extract<Len - 1, Len>(carries), extract<Len - 2, Len - 1>(carries)));
        if (setCarry)
            policy.writeFlag(x86_flag_cf, invertMaybe(extract<Len - 1, Len>(carries), dec));
        return result;
    }

    /* Virtual so that we can subclass X86InstructionSemantics and have an opportunity to override the translation of any
     * instruction. */
    virtual void translate(SgAsmx86Instruction* insn) {
      // BERNAT, 16MAR10 - keep the IP symbolic.
      // PATCH
      policy.writeIP(policy.add(policy.readIP(), number<32>(insn->get_raw_bytes().size())));
      //policy.writeIP(number<32>((unsigned int)(insn->get_address() + insn->get_raw_bytes().size())));
      // END PATCH
        X86InstructionKind kind = insn->get_kind();
        const SgAsmExpressionPtrList& operands = insn->get_operandList()->get_operands();
        switch (kind) {

            case x86_mov: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: write8(operands[0], read8(operands[1])); break;
                    case 2: write16(operands[0], read16(operands[1])); break;
                    case 4: write32(operands[0], read32(operands[1])); break;
                    default: ROSE_ASSERT("Bad size"); break;
                }
                break;
            }

            case x86_xchg: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) temp = read8(operands[1]);
                        write8(operands[1], read8(operands[0]));
                        write8(operands[0], temp);
                        break;
                    }
                    case 2: {
                        Word(16) temp = read16(operands[1]);
                        write16(operands[1], read16(operands[0]));
                        write16(operands[0], temp);
                        break;
                    }
                    case 4: {
                        Word(32) temp = read32(operands[1]);
                        write32(operands[1], read32(operands[0]));
                        write32(operands[0], temp);
                        break;
                    }
                    default:
                        ROSE_ASSERT("Bad size");
                        break;
                }
                break;
            }

            case x86_movzx: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        write16(operands[0], policy.concat(read8(operands[1]), number<8>(0)));
                        break;
                    }
                    case 4: {
                        switch (numBytesInAsmType(operands[1]->get_type())) {
                            case 1: write32(operands[0], policy.concat(read8(operands[1]), number<24>(0))); break;
                            case 2: write32(operands[0], policy.concat(read16(operands[1]), number<16>(0))); break;
                            default: ROSE_ASSERT("Bad size");
                        }
                        break;
                    }
                    default:
                        ROSE_ASSERT("Bad size");
                        break;
                }
                break;
            }

            case x86_movsx: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        Word(8) op1 = read8(operands[1]);
                        Word(16) result = signExtend<8, 16>(op1);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        switch (numBytesInAsmType(operands[1]->get_type())) {
                            case 1: {
                                Word(8) op1 = read8(operands[1]);
                                Word(32) result = signExtend<8, 32>(op1);
                                write32(operands[0], result);
                                break;
                            }
                            case 2: {
                                Word(16) op1 = read16(operands[1]);
                                Word(32) result = signExtend<16, 32>(op1);
                                write32(operands[0], result);
                                break;
                            }
                            default:
                                ROSE_ASSERT("Bad size");
                        }
                        break;
                    }
                    default:
                        ROSE_ASSERT("Bad size");
                        break;
                }
                break;
            }

            case x86_cbw: {
                ROSE_ASSERT(operands.size() == 0);
                updateGPRLowWord(x86_gpr_ax, signExtend<8, 16>(extract<0, 8>(policy.readGPR(x86_gpr_ax))));
                break;
            }

            case x86_cwde: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeGPR(x86_gpr_ax, signExtend<16, 32>(extract<0, 16>(policy.readGPR(x86_gpr_ax))));
                break;
            }

            case x86_cwd: {
                ROSE_ASSERT(operands.size() == 0);
                updateGPRLowWord(x86_gpr_dx, extract<16, 32>(signExtend<16, 32>(extract<0, 16>(policy.readGPR(x86_gpr_ax)))));
                break;
            }

            case x86_cdq: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeGPR(x86_gpr_dx, extract<32, 64>(signExtend<32, 64>(policy.readGPR(x86_gpr_ax))));
                break;
            }

            case x86_lea: {
                ROSE_ASSERT(operands.size() == 2);
                write32(operands[0], readEffectiveAddress(operands[1]));
                break;
            }

            case x86_and: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = policy.and_(read8(operands[0]), read8(operands[1]));
                        setFlagsForResult<8>(result);
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = policy.and_(read16(operands[0]), read16(operands[1]));
                        setFlagsForResult<16>(result);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = policy.and_(read32(operands[0]), read32(operands[1]));
                        setFlagsForResult<32>(result);
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                policy.writeFlag(x86_flag_of, policy.false_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.false_());
                break;
            }

            case x86_or: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = policy.or_(read8(operands[0]), read8(operands[1]));
                        setFlagsForResult<8>(result);
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = policy.or_(read16(operands[0]), read16(operands[1]));
                        setFlagsForResult<16>(result);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = policy.or_(read32(operands[0]), read32(operands[1]));
                        setFlagsForResult<32>(result);
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                policy.writeFlag(x86_flag_of, policy.false_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.false_());
                break;
            }

            case x86_test: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = policy.and_(read8(operands[0]), read8(operands[1]));
                        setFlagsForResult<8>(result);
                        break;
                    }
                    case 2: {
                        Word(16) result = policy.and_(read16(operands[0]), read16(operands[1]));
                        setFlagsForResult<16>(result);
                        break;
                    }
                    case 4: {
                        Word(32) result = policy.and_(read32(operands[0]), read32(operands[1]));
                        setFlagsForResult<32>(result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                policy.writeFlag(x86_flag_of, policy.false_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.false_());
                break;
            }

            case x86_xor: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = policy.xor_(read8(operands[0]), read8(operands[1]));
                        setFlagsForResult<8>(result);
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = policy.xor_(read16(operands[0]), read16(operands[1]));
                        setFlagsForResult<16>(result);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = policy.xor_(read32(operands[0]), read32(operands[1]));
                        setFlagsForResult<32>(result);
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                policy.writeFlag(x86_flag_of, policy.false_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.false_());
                break;
            }

            case x86_not: {
                ROSE_ASSERT(operands.size() == 1);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = policy.invert(read8(operands[0]));
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = policy.invert(read16(operands[0]));
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = policy.invert(read32(operands[0]));
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_add: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doAddOperation<8>(read8(operands[0]), read8(operands[1]), false, policy.false_());
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doAddOperation<16>(read16(operands[0]), read16(operands[1]), false, policy.false_());
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doAddOperation<32>(read32(operands[0]), read32(operands[1]), false, policy.false_());
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_adc: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doAddOperation<8>(read8(operands[0]), read8(operands[1]), false,
                                                           policy.readFlag(x86_flag_cf));
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doAddOperation<16>(read16(operands[0]), read16(operands[1]), false,
                                                             policy.readFlag(x86_flag_cf));
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doAddOperation<32>(read32(operands[0]), read32(operands[1]), false,
                                                             policy.readFlag(x86_flag_cf));
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_sub: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doAddOperation<8>(read8(operands[0]), policy.invert(read8(operands[1])), true,
                                                           policy.false_());
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doAddOperation<16>(read16(operands[0]), policy.invert(read16(operands[1])), true,
                                                             policy.false_());
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doAddOperation<32>(read32(operands[0]), policy.invert(read32(operands[1])), true,
                                                             policy.false_());
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_sbb: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doAddOperation<8>(read8(operands[0]), policy.invert(read8(operands[1])), true,
                                                           policy.readFlag(x86_flag_cf));
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doAddOperation<16>(read16(operands[0]), policy.invert(read16(operands[1])), true,
                                                             policy.readFlag(x86_flag_cf));
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doAddOperation<32>(read32(operands[0]), policy.invert(read32(operands[1])), true,
                                                             policy.readFlag(x86_flag_cf));
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_cmp: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        doAddOperation<8>(read8(operands[0]), policy.invert(read8(operands[1])), true, policy.false_());
                        break;
                    }
                    case 2: {
                        doAddOperation<16>(read16(operands[0]), policy.invert(read16(operands[1])), true, policy.false_());
                        break;
                    }
                    case 4: {
                        doAddOperation<32>(read32(operands[0]), policy.invert(read32(operands[1])), true, policy.false_());
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_neg: {
                ROSE_ASSERT(operands.size() == 1);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doAddOperation<8>(number<8>(0), policy.invert(read8(operands[0])), true,
                                                           policy.false_());
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doAddOperation<16>(number<16>(0), policy.invert(read16(operands[0])), true,
                                                             policy.false_());
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doAddOperation<32>(number<32>(0), policy.invert(read32(operands[0])), true,
                                                             policy.false_());
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_inc: {
                ROSE_ASSERT(operands.size() == 1);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doIncOperation<8>(read8(operands[0]), false, false);
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doIncOperation<16>(read16(operands[0]), false, false);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doIncOperation<32>(read32(operands[0]), false, false);
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_dec: {
                ROSE_ASSERT(operands.size() == 1);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) result = doIncOperation<8>(read8(operands[0]), true, false);
                        write8(operands[0], result);
                        break;
                    }
                    case 2: {
                        Word(16) result = doIncOperation<16>(read16(operands[0]), true, false);
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) result = doIncOperation<32>(read32(operands[0]), true, false);
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_cmpxchg: {
                ROSE_ASSERT(operands.size() == 2);
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op0 = read8(operands[0]);
                        Word(8) oldAx = extract<0, 8>(policy.readGPR(x86_gpr_ax));
                        doAddOperation<8>(oldAx, policy.invert(op0), true, policy.false_());
                        write8(operands[0], policy.ite(policy.readFlag(x86_flag_zf), read8(operands[1]), op0));
                        updateGPRLowByte(x86_gpr_ax, policy.ite(policy.readFlag(x86_flag_zf), oldAx, op0));
                        break;
                    }
                    case 2: {
                        Word(16) op0 = read16(operands[0]);
                        Word(16) oldAx = extract<0, 16>(policy.readGPR(x86_gpr_ax));
                        doAddOperation<16>(oldAx, policy.invert(op0), true, policy.false_());
                        write16(operands[0], policy.ite(policy.readFlag(x86_flag_zf), read16(operands[1]), op0));
                        updateGPRLowWord(x86_gpr_ax, policy.ite(policy.readFlag(x86_flag_zf), oldAx, op0));
                        break;
                    }
                    case 4: {
                        Word(32) op0 = read32(operands[0]);
                        Word(32) oldAx = policy.readGPR(x86_gpr_ax);
                        doAddOperation<32>(oldAx, policy.invert(op0), true, policy.false_());
                        write32(operands[0], policy.ite(policy.readFlag(x86_flag_zf), read32(operands[1]), op0));
                        policy.writeGPR(x86_gpr_ax, policy.ite(policy.readFlag(x86_flag_zf), oldAx, op0));
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                        break;
                }
                break;
            }

            case x86_shl: {
                Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                Word(1) shiftCountZero = policy.equalToZero(shiftCount);
                policy.writeFlag(x86_flag_af, policy.ite(shiftCountZero, policy.readFlag(x86_flag_af), policy.undefined_()));
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op = read8(operands[0]);
                        Word(8) output = policy.shiftLeft(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<7, 8>(policy.shiftLeft(op, policy.add(shiftCount, number<5>(7)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<7, 8>(output), newCf)));
                        write8(operands[0], output);
                        setFlagsForResult<8>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    case 2: {
                        Word(16) op = read16(operands[0]);
                        Word(16) output = policy.shiftLeft(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<15, 16>(policy.shiftLeft(op, policy.add(shiftCount, number<5>(15)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<15, 16>(output), newCf)));
                        write16(operands[0], output);
                        setFlagsForResult<16>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[0]);
                        Word(32) output = policy.shiftLeft(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<31, 32>(policy.shiftLeft(op, policy.add(shiftCount, number<5>(31)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<31, 32>(output), newCf)));
                        write32(operands[0], output);
                        setFlagsForResult<32>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_shr: {
                Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                Word(1) shiftCountZero = policy.equalToZero(shiftCount);
                policy.writeFlag(x86_flag_af, policy.ite(shiftCountZero, policy.readFlag(x86_flag_af), policy.undefined_()));
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op = read8(operands[0]);
                        Word(8) output = policy.shiftRight(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(7)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero, policy.readFlag(x86_flag_of), extract<7, 8>(op)));
                        write8(operands[0], output);
                        setFlagsForResult<8>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    case 2: {
                        Word(16) op = read16(operands[0]);
                        Word(16) output = policy.shiftRight(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(15)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of,
                                         policy.ite(shiftCountZero, policy.readFlag(x86_flag_of), extract<15, 16>(op)));
                        write16(operands[0], output);
                        setFlagsForResult<16>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[0]);
                        Word(32) output = policy.shiftRight(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(31)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 extract<31, 32>(op)));
                        write32(operands[0], output);
                        setFlagsForResult<32>(output, policy.invert(shiftCountZero));
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_sar: {
                Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                Word(1) shiftCountZero = policy.equalToZero(shiftCount);
                Word(1) shiftCountNotZero = policy.invert(shiftCountZero);
                policy.writeFlag(x86_flag_af, policy.ite(shiftCountZero, policy.readFlag(x86_flag_af), policy.undefined_()));
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op = read8(operands[0]);
                        Word(8) output = policy.shiftRightArithmetic(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(7)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        /* No change with sc = 0, clear when sc = 1, undefined otherwise */
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.false_()));
                        write8(operands[0], output);
                        setFlagsForResult<8>(output, shiftCountNotZero);
                        break;
                    }
                    case 2: {
                        Word(16) op = read16(operands[0]);
                        Word(16) output = policy.shiftRightArithmetic(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(15)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        /* No change with sc = 0, clear when sc = 1, undefined otherwise */
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.false_()));
                        write16(operands[0], output);
                        setFlagsForResult<16>(output, shiftCountNotZero);
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[0]);
                        Word(32) output = policy.shiftRightArithmetic(op, shiftCount);
                        Word(1) newCf = policy.ite(shiftCountZero,
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op, policy.add(shiftCount, number<5>(31)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        /* No change with sc = 0, clear when sc = 1, undefined otherwise */
                        policy.writeFlag(x86_flag_of, policy.ite(shiftCountZero,
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.false_()));
                        write32(operands[0], output);
                        setFlagsForResult<32>(output, shiftCountNotZero);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_rol: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op = read8(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(8) output = policy.rotateLeft(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<0, 1>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<0, 1>(output), extract<7, 8>(output))));
                        write8(operands[0], output);
                        break;
                    }
                    case 2: {
                        Word(16) op = read16(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(16) output = policy.rotateLeft(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<0, 1>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<0, 1>(output), extract<15, 16>(output))));
                        write16(operands[0], output);
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(32) output = policy.rotateLeft(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<0, 1>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<0, 1>(output), extract<31, 32>(output))));
                        write32(operands[0], output);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_ror: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op = read8(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(8) output = policy.rotateRight(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<7, 8>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<6, 7>(output), extract<7, 8>(output))));
                        write8(operands[0], output);
                        break;
                    }
                    case 2: {
                        Word(16) op = read16(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(16) output = policy.rotateRight(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<15, 16>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<14, 15>(output), extract<15, 16>(output))));
                        write16(operands[0], output);
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[0]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[1]));
                        Word(32) output = policy.rotateRight(op, shiftCount);
                        policy.writeFlag(x86_flag_cf, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_cf),
                                                                 extract<31, 32>(output)));
                        policy.writeFlag(x86_flag_of, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_of),
                                                                 policy.xor_(extract<30, 31>(output), extract<31, 32>(output))));
                        write32(operands[0], output);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_shld: {
                Word(5) shiftCount = extract<0, 5>(read8(operands[2]));
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        Word(16) op1 = read16(operands[0]);
                        Word(16) op2 = read16(operands[1]);
                        Word(16) output1 = policy.shiftLeft(op1, shiftCount);
                        Word(16) output2 = policy.ite(policy.equalToZero(shiftCount),
                                                      number<16>(0),
                                                      policy.shiftRight(op2, policy.negate(shiftCount)));
                        Word(16) output = policy.or_(output1, output2);
                        Word(1) newCf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<15, 16>(policy.shiftLeft(op1, policy.add(shiftCount, number<5>(15)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        Word(1) newOf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_of), 
                                                   policy.xor_(extract<15, 16>(output), newCf));
                        policy.writeFlag(x86_flag_of, newOf);
                        write16(operands[0], output);
                        setFlagsForResult<16>(output);
                        policy.writeFlag(x86_flag_af, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_af),
                                                                 policy.undefined_()));
                        break;
                    }
                    case 4: {
                        Word(32) op1 = read32(operands[0]);
                        Word(32) op2 = read32(operands[1]);
                        Word(5) shiftCount = extract<0, 5>(read8(operands[2]));
                        Word(32) output1 = policy.shiftLeft(op1, shiftCount);
                        Word(32) output2 = policy.ite(policy.equalToZero(shiftCount),
                                                      number<32>(0),
                                                      policy.shiftRight(op2, policy.negate(shiftCount)));
                        Word(32) output = policy.or_(output1, output2);
                        Word(1) newCf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<31, 32>(policy.shiftLeft(op1, policy.add(shiftCount, number<5>(31)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        Word(1) newOf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_of), 
                                                   policy.xor_(extract<31, 32>(output), newCf));
                        policy.writeFlag(x86_flag_of, newOf);
                        write32(operands[0], output);
                        setFlagsForResult<32>(output);
                        policy.writeFlag(x86_flag_af, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_af),
                                                                 policy.undefined_()));
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_shrd: {
                Word(5) shiftCount = extract<0, 5>(read8(operands[2]));
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        Word(16) op1 = read16(operands[0]);
                        Word(16) op2 = read16(operands[1]);
                        Word(16) output1 = policy.shiftRight(op1, shiftCount);
                        Word(16) output2 = policy.ite(policy.equalToZero(shiftCount),
                                                      number<16>(0),
                                                      policy.shiftLeft(op2, policy.negate(shiftCount)));
                        Word(16) output = policy.or_(output1, output2);
                        Word(1) newCf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op1, policy.add(shiftCount, number<5>(15)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        Word(1) newOf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_of), 
                                                   policy.xor_(extract<15, 16>(output),
                                                               extract<15, 16>(op1)));
                        policy.writeFlag(x86_flag_of, newOf);
                        write16(operands[0], output);
                        setFlagsForResult<16>(output);
                        policy.writeFlag(x86_flag_af, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_af),
                                                                 policy.undefined_()));
                        break;
                    }
                    case 4: {
                        Word(32) op1 = read32(operands[0]);
                        Word(32) op2 = read32(operands[1]);
                        Word(32) output1 = policy.shiftRight(op1, shiftCount);
                        Word(32) output2 = policy.ite(policy.equalToZero(shiftCount),
                                                      number<32>(0),
                                                      policy.shiftLeft(op2, policy.negate(shiftCount)));
                        Word(32) output = policy.or_(output1, output2);
                        Word(1) newCf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_cf),
                                                   extract<0, 1>(policy.shiftRight(op1, policy.add(shiftCount, number<5>(31)))));
                        policy.writeFlag(x86_flag_cf, newCf);
                        Word(1) newOf = policy.ite(policy.equalToZero(shiftCount),
                                                   policy.readFlag(x86_flag_of), 
                                                   policy.xor_(extract<31, 32>(output),
                                                               extract<31, 32>(op1)));
                        policy.writeFlag(x86_flag_of, newOf);
                        write32(operands[0], output);
                        setFlagsForResult<32>(output);
                        policy.writeFlag(x86_flag_af, policy.ite(policy.equalToZero(shiftCount),
                                                                 policy.readFlag(x86_flag_af),
                                                                 policy.undefined_()));
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_bsf: {
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        Word(16) op = read16(operands[1]);
                        policy.writeFlag(x86_flag_zf, policy.equalToZero(op));
                        Word(16) result = policy.ite(policy.readFlag(x86_flag_zf),
                                                     read16(operands[0]),
                                                     policy.leastSignificantSetBit(op));
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[1]);
                        policy.writeFlag(x86_flag_zf, policy.equalToZero(op));
                        Word(32) result = policy.ite(policy.readFlag(x86_flag_zf),
                                                     read32(operands[0]),
                                                     policy.leastSignificantSetBit(op));
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_bsr: {
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 2: {
                        Word(16) op = read16(operands[1]);
                        policy.writeFlag(x86_flag_zf, policy.equalToZero(op));
                        Word(16) result = policy.ite(policy.readFlag(x86_flag_zf),
                                                     read16(operands[0]),
                                                     policy.mostSignificantSetBit(op));
                        write16(operands[0], result);
                        break;
                    }
                    case 4: {
                        Word(32) op = read32(operands[1]);
                        policy.writeFlag(x86_flag_zf, policy.equalToZero(op));
                        Word(32) result = policy.ite(policy.readFlag(x86_flag_zf),
                                                     read32(operands[0]),
                                                     policy.mostSignificantSetBit(op));
                        write32(operands[0], result);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                break;
            }

            case x86_bts: {
                ROSE_ASSERT(operands.size() == 2);
                /* All flags except CF are undefined */
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                if (isSgAsmMemoryReferenceExpression(operands[0]) && isSgAsmx86RegisterReferenceExpression(operands[1])) {
                    /* Special case allowing multi-word offsets into memory */
                    Word(32) addr = readEffectiveAddress(operands[0]);
                    int numBytes = numBytesInAsmType(operands[1]->get_type());
                    Word(32) bitnum = numBytes == 2 ? signExtend<16, 32>(read16(operands[1])) : read32(operands[1]);
                    Word(32) adjustedAddr = policy.add(addr, signExtend<29, 32>(extract<3, 32>(bitnum)));
                    Word(8) val = readMemory<8>(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(operands[0])),
                                                adjustedAddr, policy.true_());
                    Word(1) bitval = extract<0, 1>(policy.rotateRight(val, extract<0, 3>(bitnum)));
                    Word(8) result = policy.or_(val, policy.rotateLeft(number<8>(1), extract<0, 3>(bitnum)));
                    policy.writeFlag(x86_flag_cf, bitval);
                    policy.writeMemory(getSegregFromMemoryReference(isSgAsmMemoryReferenceExpression(operands[0])),
                                       adjustedAddr, result, policy.true_());
                } else {
                    /* Simple case */
                    switch (numBytesInAsmType(operands[0]->get_type())) {
                        case 2: {
                            Word(16) op0 = read16(operands[0]);
                            Word(4) bitnum = extract<0, 4>(read16(operands[1]));
                            Word(1) bitval = extract<0, 1>(policy.rotateRight(op0, bitnum));
                            Word(16) result = policy.or_(op0, policy.rotateLeft(number<16>(1), bitnum));
                            policy.writeFlag(x86_flag_cf, bitval);
                            write16(operands[0], result);
                            break;
                        }
                        case 4: {
                            Word(32) op0 = read32(operands[0]);
                            Word(5) bitnum = extract<0, 5>(read32(operands[1]));
                            Word(1) bitval = extract<0, 1>(policy.rotateRight(op0, bitnum));
                            Word(32) result = policy.or_(op0, policy.rotateLeft(number<32>(1), bitnum));
                            policy.writeFlag(x86_flag_cf, bitval);
                            write32(operands[0], result);
                            break;
                        }
                        default:
                            ROSE_ASSERT(!"Bad size");
                    }
                }
                break;
            }

            case x86_imul: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op0 = extract<0, 8>(policy.readGPR(x86_gpr_ax));
                        Word(8) op1 = read8(operands[0]);
                        Word(16) mulResult = policy.signedMultiply(op0, op1);
                        updateGPRLowWord(x86_gpr_ax, mulResult);
                        Word(1) carry = policy.invert(policy.or_(policy.equalToZero(policy.invert(extract<7, 16>(mulResult))),
                                                                 policy.equalToZero(extract<7, 16>(mulResult))));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    case 2: {
                        Word(16) op0 = operands.size() == 1 ?
                                       extract<0, 16>(policy.readGPR(x86_gpr_ax)) :
                                       read16(operands[operands.size() - 2]);
                        Word(16) op1 = read16(operands[operands.size() - 1]);
                        Word(32) mulResult = policy.signedMultiply(op0, op1);
                        if (operands.size() == 1) {
                            updateGPRLowWord(x86_gpr_ax, extract<0, 16>(mulResult));
                            updateGPRLowWord(x86_gpr_dx, extract<16, 32>(mulResult));
                        } else {
                            write16(operands[0], extract<0, 16>(mulResult));
                        }
                        Word(1) carry = policy.invert(policy.or_(policy.equalToZero(policy.invert(extract<7, 32>(mulResult))),
                                                                 policy.equalToZero(extract<7, 32>(mulResult))));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    case 4: {
                        Word(32) op0 = operands.size() == 1 ? policy.readGPR(x86_gpr_ax) : read32(operands[operands.size() - 2]);
                        Word(32) op1 = read32(operands[operands.size() - 1]);
                        Word(64) mulResult = policy.signedMultiply(op0, op1);
                        if (operands.size() == 1) {
                            policy.writeGPR(x86_gpr_ax, extract<0, 32>(mulResult));
                            policy.writeGPR(x86_gpr_dx, extract<32, 64>(mulResult));
                        } else {
                            write32(operands[0], extract<0, 32>(mulResult));
                        }
                        Word(1) carry = policy.invert(policy.or_(policy.equalToZero(policy.invert(extract<7, 64>(mulResult))),
                                                                 policy.equalToZero(extract<7, 64>(mulResult))));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                break;
            }

            case x86_mul: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(8) op0 = extract<0, 8>(policy.readGPR(x86_gpr_ax));
                        Word(8) op1 = read8(operands[0]);
                        Word(16) mulResult = policy.unsignedMultiply(op0, op1);
                        updateGPRLowWord(x86_gpr_ax, mulResult);
                        Word(1) carry = policy.invert(policy.equalToZero(extract<8, 16>(mulResult)));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    case 2: {
                        Word(16) op0 = extract<0, 16>(policy.readGPR(x86_gpr_ax));
                        Word(16) op1 = read16(operands[0]);
                        Word(32) mulResult = policy.unsignedMultiply(op0, op1);
                        updateGPRLowWord(x86_gpr_ax, extract<0, 16>(mulResult));
                        updateGPRLowWord(x86_gpr_dx, extract<16, 32>(mulResult));
                        Word(1) carry = policy.invert(policy.equalToZero(extract<16, 32>(mulResult)));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    case 4: {
                        Word(32) op0 = policy.readGPR(x86_gpr_ax);
                        Word(32) op1 = read32(operands[0]);
                        Word(64) mulResult = policy.unsignedMultiply(op0, op1);
                        policy.writeGPR(x86_gpr_ax, extract<0, 32>(mulResult));
                        policy.writeGPR(x86_gpr_dx, extract<32, 64>(mulResult));
                        Word(1) carry = policy.invert(policy.equalToZero(extract<32, 64>(mulResult)));
                        policy.writeFlag(x86_flag_cf, carry);
                        policy.writeFlag(x86_flag_of, carry);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                break;
            }

            case x86_idiv: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(16) op0 = extract<0, 16>(policy.readGPR(x86_gpr_ax));
                        Word(8) op1 = read8(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(16) divResult = policy.signedDivide(op0, op1);
                        Word(8) modResult = policy.signedModulo(op0, op1);
                        /* if result overflows, we should trap */
                        updateGPRLowWord(x86_gpr_ax, policy.concat(extract<0, 8>(divResult), modResult));
                        break;
                    }
                    case 2: {
                        Word(32) op0 = policy.concat(extract<0, 16>(policy.readGPR(x86_gpr_ax)),
                                                     extract<0, 16>(policy.readGPR(x86_gpr_dx)));
                        Word(16) op1 = read16(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(32) divResult = policy.signedDivide(op0, op1);
                        Word(16) modResult = policy.signedModulo(op0, op1);
                        /* if result overflows, we should trap */
                        updateGPRLowWord(x86_gpr_ax, extract<0, 16>(divResult));
                        updateGPRLowWord(x86_gpr_dx, modResult);
                        break;
                    }
                    case 4: {
                        Word(64) op0 = policy.concat(policy.readGPR(x86_gpr_ax), policy.readGPR(x86_gpr_dx));
                        Word(32) op1 = read32(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(64) divResult = policy.signedDivide(op0, op1);
                        Word(32) modResult = policy.signedModulo(op0, op1);
                        /* if result overflows, we should trap */
                        policy.writeGPR(x86_gpr_ax, extract<0, 32>(divResult));
                        policy.writeGPR(x86_gpr_dx, modResult);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                policy.writeFlag(x86_flag_of, policy.undefined_());
                break;
            }

            case x86_div: {
                switch (numBytesInAsmType(operands[0]->get_type())) {
                    case 1: {
                        Word(16) op0 = extract<0, 16>(policy.readGPR(x86_gpr_ax));
                        Word(8) op1 = read8(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(16) divResult = policy.unsignedDivide(op0, op1);
                        Word(8) modResult = policy.unsignedModulo(op0, op1);
                        /* if extract<8, 16> of divResult is non-zero (overflow), we should trap */
                        updateGPRLowWord(x86_gpr_ax, policy.concat(extract<0, 8>(divResult), modResult));
                        break;
                    }
                    case 2: {
                        Word(32) op0 = policy.concat(extract<0, 16>(policy.readGPR(x86_gpr_ax)),
                                                     extract<0, 16>(policy.readGPR(x86_gpr_dx)));
                        Word(16) op1 = read16(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(32) divResult = policy.unsignedDivide(op0, op1);
                        Word(16) modResult = policy.unsignedModulo(op0, op1);
                        /* if extract<16, 32> of divResult is non-zero (overflow), we should trap */
                        updateGPRLowWord(x86_gpr_ax, extract<0, 16>(divResult));
                        updateGPRLowWord(x86_gpr_dx, modResult);
                        break;
                    }
                    case 4: {
                        Word(64) op0 = policy.concat(policy.readGPR(x86_gpr_ax), policy.readGPR(x86_gpr_dx));
                        Word(32) op1 = read32(operands[0]);
                        /* if op1 == 0, we should trap */
                        Word(64) divResult = policy.unsignedDivide(op0, op1);
                        Word(32) modResult = policy.unsignedModulo(op0, op1);
                        /* if extract<32, 64> of divResult is non-zero (overflow), we should trap */
                        policy.writeGPR(x86_gpr_ax, extract<0, 32>(divResult));
                        policy.writeGPR(x86_gpr_dx, modResult);
                        break;
                    }
                    default:
                        ROSE_ASSERT(!"Bad size");
                }
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                policy.writeFlag(x86_flag_of, policy.undefined_());
                break;
            }

            case x86_aaa: {
                ROSE_ASSERT(operands.size() == 0);
                Word(1) incAh = policy.or_(policy.readFlag(x86_flag_af),
                                           greaterOrEqualToTen(extract<0, 4>(policy.readGPR(x86_gpr_ax))));
                updateGPRLowWord(x86_gpr_ax,
                                 policy.concat(policy.add(policy.ite(incAh, number<4>(6), number<4>(0)),
                                                          extract<0, 4>(policy.readGPR(x86_gpr_ax))),
                                               policy.concat(number<4>(0),
                                                             policy.add(policy.ite(incAh, number<8>(1), number<8>(0)),
                                                                        extract<8, 16>(policy.readGPR(x86_gpr_ax))))));
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_af, incAh);
                policy.writeFlag(x86_flag_cf, incAh);
                break;
            }

            case x86_aas: {
                ROSE_ASSERT(operands.size() == 0);
                Word(1) decAh = policy.or_(policy.readFlag(x86_flag_af),
                                           greaterOrEqualToTen(extract<0, 4>(policy.readGPR(x86_gpr_ax))));
                updateGPRLowWord(x86_gpr_ax,
                                 policy.concat(policy.add(policy.ite(decAh, number<4>(-6), number<4>(0)),
                                                          extract<0, 4>(policy.readGPR(x86_gpr_ax))),
                                               policy.concat(number<4>(0),
                                                             policy.add(policy.ite(decAh, number<8>(-1), number<8>(0)),
                                                                        extract<8, 16>(policy.readGPR(x86_gpr_ax))))));
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_sf, policy.undefined_());
                policy.writeFlag(x86_flag_zf, policy.undefined_());
                policy.writeFlag(x86_flag_pf, policy.undefined_());
                policy.writeFlag(x86_flag_af, decAh);
                policy.writeFlag(x86_flag_cf, decAh);
                break;
            }

            case x86_aam: {
                ROSE_ASSERT(operands.size() == 1);
                Word(8) al = extract<0, 8>(policy.readGPR(x86_gpr_ax));
                Word(8) divisor = read8(operands[0]);
                Word(8) newAh = policy.unsignedDivide(al, divisor);
                Word(8) newAl = policy.unsignedModulo(al, divisor);
                updateGPRLowWord(x86_gpr_ax, policy.concat(newAl, newAh));
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                setFlagsForResult<8>(newAl);
                break;
            }

            case x86_aad: {
                ROSE_ASSERT(operands.size() == 1);
                Word(8) al = extract<0, 8>(policy.readGPR(x86_gpr_ax));
                Word(8) ah = extract<8, 16>(policy.readGPR(x86_gpr_ax));
                Word(8) divisor = read8(operands[0]);
                Word(8) newAl = policy.add(al, extract<0, 8>(policy.unsignedMultiply(ah, divisor)));
                updateGPRLowWord(x86_gpr_ax, policy.concat(newAl, number<8>(0)));
                policy.writeFlag(x86_flag_of, policy.undefined_());
                policy.writeFlag(x86_flag_af, policy.undefined_());
                policy.writeFlag(x86_flag_cf, policy.undefined_());
                setFlagsForResult<8>(newAl);
                break;
            }

            case x86_bswap: {
                ROSE_ASSERT(operands.size() == 1);
                Word(32) oldVal = read32(operands[0]);
                Word(32) newVal = policy.concat(extract<24, 32>(oldVal),
                                                policy.concat(extract<16, 24>(oldVal),
                                                              policy.concat(extract<8, 16>(oldVal),
                                                                            extract<0, 8>(oldVal))));
                write32(operands[0], newVal);
                break;
            }

            case x86_push: {
                ROSE_ASSERT(operands.size() == 1);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(-4));
                policy.writeMemory(x86_segreg_ss, newSp, read32(operands[0]), policy.true_());
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_pushad: {
                ROSE_ASSERT(operands.size() == 0);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(-32));
                policy.writeMemory(x86_segreg_ss, newSp, policy.readGPR(x86_gpr_di), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(4)), policy.readGPR(x86_gpr_si), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(8)), policy.readGPR(x86_gpr_bp), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(12)), oldSp, policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(16)), policy.readGPR(x86_gpr_bx), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(20)), policy.readGPR(x86_gpr_dx), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(24)), policy.readGPR(x86_gpr_cx), policy.true_());
                policy.writeMemory(x86_segreg_ss, policy.add(newSp, number<32>(28)), policy.readGPR(x86_gpr_ax), policy.true_());
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_pushfd: {
                ROSE_ASSERT(operands.size() == 0);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(-4));
                policy.writeMemory(x86_segreg_ss, newSp, readEflags(), policy.true_());
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_pop: {
                ROSE_ASSERT(operands.size() == 1);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(4));
                write32(operands[0], readMemory<32>(x86_segreg_ss, oldSp, policy.true_()));
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_popad: {
                ROSE_ASSERT(operands.size() == 0);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(32));
                policy.writeGPR(x86_gpr_di, readMemory<32>(x86_segreg_ss, oldSp, policy.true_()));
                policy.writeGPR(x86_gpr_si, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(4)), policy.true_()));
                policy.writeGPR(x86_gpr_bp, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(8)), policy.true_()));
                policy.writeGPR(x86_gpr_bx, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(16)), policy.true_()));
                policy.writeGPR(x86_gpr_dx, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(20)), policy.true_()));
                policy.writeGPR(x86_gpr_cx, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(24)), policy.true_()));
                policy.writeGPR(x86_gpr_ax, readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(28)), policy.true_()));
                readMemory<32>(x86_segreg_ss, policy.add(oldSp, number<32>(12)), policy.true_());
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_leave: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeGPR(x86_gpr_sp, policy.readGPR(x86_gpr_bp));
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(4));
                policy.writeGPR(x86_gpr_bp, readMemory<32>(x86_segreg_ss, oldSp, policy.true_()));
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_call: {
                ROSE_ASSERT(operands.size() == 1);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, number<32>(-4));
                policy.writeMemory(x86_segreg_ss, newSp, policy.readIP(), policy.true_());
                policy.writeIP(policy.filterCallTarget(read32(operands[0])));
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_ret: {
                ROSE_ASSERT(operands.size() <= 1);
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                Word(32) extraBytes = (operands.size() == 1 ? read32(operands[0]) : number<32>(0));
                Word(32) oldSp = policy.readGPR(x86_gpr_sp);
                Word(32) newSp = policy.add(oldSp, policy.add(number<32>(4), extraBytes));
                policy.writeIP(policy.filterReturnTarget(readMemory<32>(x86_segreg_ss, oldSp, policy.true_())));
                policy.writeGPR(x86_gpr_sp, newSp);
                break;
            }

            case x86_loop: {
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                ROSE_ASSERT(operands.size() == 1);
                Word(32) oldCx = policy.readGPR(x86_gpr_cx);
                Word(32) newCx = policy.add(number<32>(-1), oldCx);
                policy.writeGPR(x86_gpr_cx, newCx);
                Word(1) doLoop = policy.invert(policy.equalToZero(newCx));
                policy.writeIP(policy.ite(doLoop, read32(operands[0]), policy.readIP()));
                break;
            }
            case x86_loopz: {
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                ROSE_ASSERT(operands.size() == 1);
                Word(32) oldCx = policy.readGPR(x86_gpr_cx);
                Word(32) newCx = policy.add(number<32>(-1), oldCx);
                policy.writeGPR(x86_gpr_cx, newCx);
                Word(1) doLoop = policy.and_(policy.invert(policy.equalToZero(newCx)), policy.readFlag(x86_flag_zf));
                policy.writeIP(policy.ite(doLoop, read32(operands[0]), policy.readIP()));
                break;
            }
            case x86_loopnz: {
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);
                ROSE_ASSERT(insn->get_operandSize() == x86_insnsize_32);
                ROSE_ASSERT(operands.size() == 1);
                Word(32) oldCx = policy.readGPR(x86_gpr_cx);
                Word(32) newCx = policy.add(number<32>(-1), oldCx);
                policy.writeGPR(x86_gpr_cx, newCx);
                Word(1) doLoop = policy.and_(policy.invert(policy.equalToZero(newCx)),
                                             policy.invert(policy.readFlag(x86_flag_zf)));
                policy.writeIP(policy.ite(doLoop, read32(operands[0]), policy.readIP()));
                break;
            }

            case x86_jmp: {
                ROSE_ASSERT(operands.size() == 1);
                policy.writeIP(policy.filterIndirectJumpTarget(read32(operands[0])));
                break;
            }


            /* Flag expressions that must be true for a conditional jump to occur. */
#           define FLAGCOMBO_ne    policy.invert(policy.readFlag(x86_flag_zf))
#           define FLAGCOMBO_e     policy.readFlag(x86_flag_zf)
#           define FLAGCOMBO_no    policy.invert(policy.readFlag(x86_flag_of))
#           define FLAGCOMBO_o     policy.readFlag(x86_flag_of)
#           define FLAGCOMBO_ns    policy.invert(policy.readFlag(x86_flag_sf))
#           define FLAGCOMBO_s     policy.readFlag(x86_flag_sf)
#           define FLAGCOMBO_po    policy.invert(policy.readFlag(x86_flag_pf))
#           define FLAGCOMBO_pe    policy.readFlag(x86_flag_pf)
#           define FLAGCOMBO_ae    policy.invert(policy.readFlag(x86_flag_cf))
#           define FLAGCOMBO_b     policy.readFlag(x86_flag_cf)
#           define FLAGCOMBO_be    policy.or_(FLAGCOMBO_b, FLAGCOMBO_e)
#           define FLAGCOMBO_a     policy.and_(FLAGCOMBO_ae, FLAGCOMBO_ne)
#           define FLAGCOMBO_l     policy.xor_(policy.readFlag(x86_flag_sf), policy.readFlag(x86_flag_of))
#           define FLAGCOMBO_ge    policy.invert(policy.xor_(policy.readFlag(x86_flag_sf), policy.readFlag(x86_flag_of)))
#           define FLAGCOMBO_le    policy.or_(FLAGCOMBO_e, FLAGCOMBO_l)
#           define FLAGCOMBO_g     policy.and_(FLAGCOMBO_ge, FLAGCOMBO_ne)
#           define FLAGCOMBO_cxz   policy.equalToZero(extract<0, 16>(policy.readGPR(x86_gpr_cx)))
#           define FLAGCOMBO_ecxz  policy.equalToZero(policy.readGPR(x86_gpr_cx))

#           define JUMP(tag) {                                                                                                 \
                ROSE_ASSERT(operands.size() == 1);                                                                             \
                policy.writeIP(policy.ite(FLAGCOMBO_##tag,                                                                     \
                                          read32(operands[0]),                                                                 \
                                          policy.readIP()));                                                                   \
            }
            case x86_jne:   JUMP(ne);   break;
            case x86_je:    JUMP(e);    break;
            case x86_jno:   JUMP(no);   break;
            case x86_jo:    JUMP(o);    break;
            case x86_jpo:   JUMP(po);   break;
            case x86_jpe:   JUMP(pe);   break;
            case x86_jns:   JUMP(ns);   break;
            case x86_js:    JUMP(s);    break;
            case x86_jae:   JUMP(ae);   break;
            case x86_jb:    JUMP(b);    break;
            case x86_jbe:   JUMP(be);   break;
            case x86_ja:    JUMP(a);    break;
            case x86_jle:   JUMP(le);   break;
            case x86_jg:    JUMP(g);    break;
            case x86_jge:   JUMP(ge);   break;
            case x86_jl:    JUMP(l);    break;
            case x86_jcxz:  JUMP(cxz);  break;
            case x86_jecxz: JUMP(ecxz); break;
#           undef JUMP

#           define SET(tag) {                                                                                                  \
                ROSE_ASSERT(operands.size() == 1);                                                                             \
                write8(operands[0], policy.concat(FLAGCOMBO_##tag, number<7>(0)));                                             \
            }
            case x86_setne: SET(ne); break;
            case x86_sete:  SET(e);  break;
            case x86_setno: SET(no); break;
            case x86_seto:  SET(o);  break;
            case x86_setpo: SET(po); break;
            case x86_setpe: SET(pe); break;
            case x86_setns: SET(ns); break;
            case x86_sets:  SET(s);  break;
            case x86_setae: SET(ae); break;
            case x86_setb:  SET(b);  break;
            case x86_setbe: SET(be); break;
            case x86_seta:  SET(a);  break;
            case x86_setle: SET(le); break;
            case x86_setg:  SET(g);  break;
            case x86_setge: SET(ge); break;
            case x86_setl:  SET(l);  break;
#           undef SET
                
#           define CMOV(tag) {                                                                                                 \
                ROSE_ASSERT(operands.size() == 2);                                                                             \
                switch (numBytesInAsmType(operands[0]->get_type())) {                                                          \
                    case 2: write16(operands[0], policy.ite(FLAGCOMBO_##tag, read16(operands[1]), read16(operands[0]))); break; \
                    case 4: write32(operands[0], policy.ite(FLAGCOMBO_##tag, read32(operands[1]), read32(operands[0]))); break; \
                    default: ROSE_ASSERT("Bad size"); break;                                                                   \
                }                                                                                                              \
            }
            case x86_cmovne:    CMOV(ne);       break;
            case x86_cmove:     CMOV(e);        break;
            case x86_cmovno:    CMOV(no);       break;
            case x86_cmovo:     CMOV(o);        break;
            case x86_cmovpo:    CMOV(po);       break;
            case x86_cmovpe:    CMOV(pe);       break;
            case x86_cmovns:    CMOV(ns);       break;
            case x86_cmovs:     CMOV(s);        break;
            case x86_cmovae:    CMOV(ae);       break;
            case x86_cmovb:     CMOV(b);        break;
            case x86_cmovbe:    CMOV(be);       break;
            case x86_cmova:     CMOV(a);        break;
            case x86_cmovle:    CMOV(le);       break;
            case x86_cmovg:     CMOV(g);        break;
            case x86_cmovge:    CMOV(ge);       break;
            case x86_cmovl:     CMOV(l);        break;
#           undef CMOV

            /* The flag expressions are no longer needed */
#           undef FLAGCOMBO_ne
#           undef FLAGCOMBO_e
#           undef FLAGCOMBO_ns
#           undef FLAGCOMBO_s
#           undef FLAGCOMBO_ae
#           undef FLAGCOMBO_b
#           undef FLAGCOMBO_be
#           undef FLAGCOMBO_a
#           undef FLAGCOMBO_l
#           undef FLAGCOMBO_ge
#           undef FLAGCOMBO_le
#           undef FLAGCOMBO_g
#           undef FLAGCOMBO_cxz
#           undef FLAGCOMBO_ecxz

            case x86_cld: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeFlag(x86_flag_df, policy.false_());
                break;
            }

            case x86_std: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeFlag(x86_flag_df, policy.true_());
                break;
            }

            case x86_clc: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeFlag(x86_flag_cf, policy.false_());
                break;
            }

            case x86_stc: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeFlag(x86_flag_cf, policy.true_());
                break;
            }

            case x86_cmc: {
                ROSE_ASSERT(operands.size() == 0);
                policy.writeFlag(x86_flag_cf, policy.invert(policy.readFlag(x86_flag_cf)));
                break;
            }

            case x86_nop:
                break;

#           define STRINGOP_LOAD_SI(len, cond)                                                                                 \
                readMemory<(8 * (len))>((insn->get_segmentOverride() == x86_segreg_none ?                                      \
                                         x86_segreg_ds :                                                                       \
                                         insn->get_segmentOverride()),                                                         \
                                        policy.readGPR(x86_gpr_si),                                                            \
                                        (cond))
            
#           define STRINGOP_LOAD_DI(len, cond)                                                                                 \
                readMemory<(8 * (len))>(x86_segreg_es, policy.readGPR(x86_gpr_di), (cond))

            /* If true, repeat this instruction, otherwise go to the next one */
#           define STRINGOP_LOOP_E                                                                                             \
                policy.writeIP(policy.ite(policy.and_(ecxNotZero, policy.readFlag(x86_flag_zf)),                               \
                                          number<32>((uint32_t)(insn->get_address())),                                         \
                                          policy.readIP()))

            /* If true, repeat this instruction, otherwise go to the next one */
#           define STRINGOP_LOOP_NE                                                                                            \
                policy.writeIP(policy.ite(policy.and_(ecxNotZero, policy.invert(policy.readFlag(x86_flag_zf))),                \
                                          number<32>((uint32_t)(insn->get_address())),                                         \
                                          policy.readIP()))

#           define REP_SCAS(suffix, len, repsuffix, loopmacro) {                                                               \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                Word(1) ecxNotZero = stringop_setup_loop();                                                                    \
                doAddOperation<(len * 8)>(extract<0, (len * 8)>(policy.readGPR(x86_gpr_ax)),                                   \
                                          policy.invert(STRINGOP_LOAD_DI(len, ecxNotZero)),                                    \
                                          true,                                                                                \
                                          policy.false_(),                                                                     \
                                          ecxNotZero);                                                                         \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
                loopmacro;                                                                                                     \
            }
            case x86_repne_scasb: REP_SCAS(b, 1, ne, STRINGOP_LOOP_NE); break;
            case x86_repne_scasw: REP_SCAS(w, 2, ne, STRINGOP_LOOP_NE); break;
            case x86_repne_scasd: REP_SCAS(d, 4, ne, STRINGOP_LOOP_NE); break;
            case x86_repe_scasb:  REP_SCAS(b, 1, e,  STRINGOP_LOOP_E);  break;
            case x86_repe_scasw:  REP_SCAS(w, 2, e,  STRINGOP_LOOP_E);  break;
            case x86_repe_scasd:  REP_SCAS(d, 4, e,  STRINGOP_LOOP_E);  break;
#           undef REP_SCAS

#           define SCAS(suffix, len) {                                                                                         \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                doAddOperation<(len * 8)>(extract<0, (len * 8)>(policy.readGPR(x86_gpr_ax)),                                   \
                                          policy.invert(STRINGOP_LOAD_DI(len, policy.true_())),                                \
                                          true,                                                                                \
                                          policy.false_(),                                                                     \
                                          policy.true_());                                                                     \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
            }
            case x86_scasb: SCAS(b, 1); break;
            case x86_scasw: SCAS(w, 2); break;
            case x86_scasd: SCAS(d, 4); break;
#           undef SCAS

#           define REP_CMPS(suffix, len, repsuffix, loopmacro) {                                                               \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                Word(1) ecxNotZero = stringop_setup_loop();                                                                    \
                doAddOperation<(len * 8)>(STRINGOP_LOAD_SI(len, ecxNotZero),                                                   \
                                          policy.invert(STRINGOP_LOAD_DI(len, ecxNotZero)),                                    \
                                          true,                                                                                \
                                          policy.false_(),                                                                     \
                                          ecxNotZero);                                                                         \
                policy.writeGPR(x86_gpr_si,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_si),                                                         \
                                           policy.ite(ecxNotZero,                                                              \
                                                      policy.ite(policy.readFlag(x86_flag_df),                                 \
                                                                 number<32>(-(len)),                                           \
                                                                 number<32>(len)),                                             \
                                                      number<32>(0))));                                                        \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(ecxNotZero,                                                              \
                                                      policy.ite(policy.readFlag(x86_flag_df),                                 \
                                                                 number<32>(-(len)),                                           \
                                                                 number<32>(len)),                                             \
                                                      number<32>(0))));                                                        \
                loopmacro;                                                                                                     \
            }
            case x86_repne_cmpsb: REP_CMPS(b, 1, ne, STRINGOP_LOOP_NE); break;
            case x86_repne_cmpsw: REP_CMPS(w, 2, ne, STRINGOP_LOOP_NE); break;
            case x86_repne_cmpsd: REP_CMPS(d, 4, ne, STRINGOP_LOOP_NE); break;
            case x86_repe_cmpsb:  REP_CMPS(b, 1, e,  STRINGOP_LOOP_E);  break;
            case x86_repe_cmpsw:  REP_CMPS(w, 2, e,  STRINGOP_LOOP_E);  break;
            case x86_repe_cmpsd:  REP_CMPS(d, 4, e,  STRINGOP_LOOP_E);  break;
#           undef REP_CMPS

#           define CMPS(suffix, len) {                                                                                         \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                doAddOperation<(len * 8)>(STRINGOP_LOAD_SI(len, policy.true_()),                                               \
                                          policy.invert(STRINGOP_LOAD_DI(len, policy.true_())),                                \
                                          true,                                                                                \
                                          policy.false_(),                                                                     \
                                          policy.true_());                                                                     \
                policy.writeGPR(x86_gpr_si,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_si),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
            }
            case x86_cmpsb: CMPS(b, 1); break;
            case x86_cmpsw: CMPS(w, 2); break;
            case x86_cmpsd: CMPS(d, 4); break;
#           undef CMPS

#           define MOVS(suffix, len) {                                                                                         \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                policy.writeMemory(x86_segreg_es,                                                                              \
                                   policy.readGPR(x86_gpr_di),                                                                 \
                                   STRINGOP_LOAD_SI(len, policy.true_()),                                                      \
                                   policy.true_());                                                                            \
                policy.writeGPR(x86_gpr_si,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_si),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
            }
            case x86_movsb: MOVS(b, 1); break;
            case x86_movsw: MOVS(w, 2); break;
            case x86_movsd: MOVS(d, 4); break;
#           undef MOVS

#           define REP_MOVS(suffix, len) {                                                                                     \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                Word(1) ecxNotZero = stringop_setup_loop();                                                                    \
                policy.writeMemory(x86_segreg_es, policy.readGPR(x86_gpr_di), STRINGOP_LOAD_SI(len, ecxNotZero), ecxNotZero);  \
                policy.writeGPR(x86_gpr_si,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_si),                                                         \
                                           policy.ite(ecxNotZero,                                                              \
                                                      policy.ite(policy.readFlag(x86_flag_df),                                 \
                                                                 number<32>(-(len)),                                           \
                                                                 number<32>(len)),                                             \
                                                      number<32>(0))));                                                        \
                policy.writeGPR(x86_gpr_di,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_di),                                                         \
                                           policy.ite(ecxNotZero,                                                              \
                                                      policy.ite(policy.readFlag(x86_flag_df),                                 \
                                                                 number<32>(-(len)),                                           \
                                                                 number<32>(len)),                                             \
                                                      number<32>(0))));                                                        \
                policy.writeIP(policy.ite(ecxNotZero, /* If true, repeat this instruction, otherwise go to the next one */     \
                                          number<32>((uint32_t)(insn->get_address())),                                         \
                                          policy.readIP()));                                                                   \
            }
            case x86_rep_movsb: REP_MOVS(b, 1); break;
            case x86_rep_movsw: REP_MOVS(w, 2); break;
            case x86_rep_movsd: REP_MOVS(d, 4); break;
#           undef MOVS

            case x86_stosb: {
                stos_semantics<1>(insn);
                break;
            }

            case x86_rep_stosb: {
                rep_stos_semantics<1>(insn);
                break;
            }

            case x86_stosw: {
                stos_semantics<2>(insn);
                break;
            }

            case x86_rep_stosw: {
                rep_stos_semantics<2>(insn);
                break;
            }

            case x86_stosd: {
                stos_semantics<4>(insn);
                break;
            }

            case x86_rep_stosd: {
                rep_stos_semantics<4>(insn);
                break;
            }

#           define LODS(suffix, len, regupdate) {                                                                              \
                ROSE_ASSERT(operands.size() == 0);                                                                             \
                ROSE_ASSERT(insn->get_addressSize() == x86_insnsize_32);                                                       \
                regupdate(x86_gpr_ax, STRINGOP_LOAD_SI(len, policy.true_()));                                                  \
                policy.writeGPR(x86_gpr_si,                                                                                    \
                                policy.add(policy.readGPR(x86_gpr_si),                                                         \
                                           policy.ite(policy.readFlag(x86_flag_df), number<32>(-(len)), number<32>(len))));    \
            }
            case x86_lodsb: LODS(b, 1, updateGPRLowByte); break;
            case x86_lodsw: LODS(w, 2, updateGPRLowWord); break;
            case x86_lodsd: LODS(d, 4, policy.writeGPR);  break;
#           undef LODS

#undef STRINGOP_LOAD_SI
#undef STRINGOP_LOAD_DI
#undef STRINGOP_UPDATE_CX
#undef STRINGOP_LOOP_E
#undef STRINGOP_LOOP_NE

            case x86_hlt: {
                ROSE_ASSERT(operands.size() == 0);
                policy.hlt();
                policy.writeIP(number<32>((uint32_t)(insn->get_address())));
                break;
            }

            case x86_rdtsc: {
                ROSE_ASSERT(operands.size() == 0);
                Word(64) tsc = policy.rdtsc();
                policy.writeGPR(x86_gpr_ax, extract<0, 32>(tsc));
                policy.writeGPR(x86_gpr_dx, extract<32, 64>(tsc));
                break;
            }

            case x86_int: {
                ROSE_ASSERT(operands.size() == 1);
                SgAsmByteValueExpression* bv = isSgAsmByteValueExpression(operands[0]);
                ROSE_ASSERT(bv);
                policy.interrupt(bv->get_value());
                break;
            }

                /* This is a dummy version that should be replaced later FIXME */
            case x86_fnstcw: {
                ROSE_ASSERT(operands.size() == 1);
                write16(operands[0], number<16>(0x37f));
                break;
            }

            case x86_fldcw: {
                ROSE_ASSERT(operands.size() == 1);
                read16(operands[0]); /* To catch access control violations */
                break;
            }

            default: {
#if 0
                std::cerr <<"Bad instruction [0x" <<std::hex <<insn->get_address() <<": " <<unparseInstruction(insn) <<"]"
                          <<" (skipping semantic analysis)\n";
#endif
                break;
            }
        }
    }

    void processInstruction(SgAsmx86Instruction* insn) {
        ROSE_ASSERT(insn);
        policy.startInstruction(insn);
        translate(insn);
        policy.finishInstruction(insn);
    }

    /*
    void processBlock(const SgAsmStatementPtrList& stmts, size_t begin, size_t end) {
        if (begin == end) return;
        policy.startBlock(stmts[begin]->get_address());
        for (size_t i = begin; i < end; ++i) {
            processInstruction(isSgAsmx86Instruction(stmts[i]));
        }
        policy.finishBlock(stmts[begin]->get_address());
    }
    */

    static bool isRepeatedStringOp(SgAsmx86Instruction* insn) {
        //SgAsmx86Instruction* insn = isSgAsmx86Instruction(s);
        if (!insn) return false;
        switch (insn->get_kind()) {
            case x86_repe_cmpsb: return true;
            case x86_repe_cmpsd: return true;
            case x86_repe_cmpsq: return true;
            case x86_repe_cmpsw: return true;
            case x86_repe_scasb: return true;
            case x86_repe_scasd: return true;
            case x86_repe_scasq: return true;
            case x86_repe_scasw: return true;
            case x86_rep_insb: return true;
            case x86_rep_insd: return true;
            case x86_rep_insw: return true;
            case x86_rep_lodsb: return true;
            case x86_rep_lodsd: return true;
            case x86_rep_lodsq: return true;
            case x86_rep_lodsw: return true;
            case x86_rep_movsb: return true;
            case x86_rep_movsd: return true;
            case x86_rep_movsq: return true;
            case x86_rep_movsw: return true;
            case x86_repne_cmpsb: return true;
            case x86_repne_cmpsd: return true;
            case x86_repne_cmpsq: return true;
            case x86_repne_cmpsw: return true;
            case x86_repne_scasb: return true;
            case x86_repne_scasd: return true;
            case x86_repne_scasq: return true;
            case x86_repne_scasw: return true;
            case x86_rep_outsb: return true;
            case x86_rep_outsd: return true;
            case x86_rep_outsw: return true;
            case x86_rep_stosb: return true;
            case x86_rep_stosd: return true;
            case x86_rep_stosq: return true;
            case x86_rep_stosw: return true;
            default: return false;
        }
    }

    static bool isHltOrInt(SgAsmx86Instruction* insn) {
        //SgAsmx86Instruction* insn = isSgAsmx86Instruction(s);
        if (!insn) return false;
        switch (insn->get_kind()) {
            case x86_hlt: return true;
            case x86_int: return true;
            default: return false;
        }
    }

#if 0
    void processBlock(SgAsmBlock* b) {
        const SgAsmStatementPtrList& stmts = b->get_statementList();
        if (stmts.empty()) return;
        if (!isSgAsmInstruction(stmts[0])) return; /* A block containing functions or something */
        size_t i = 0;
        while (i < stmts.size()) {
            size_t oldI = i;
            /* Advance until either i points to a repeated string op or it is just after a hlt or int */
            while (i < stmts.size() && !isRepeatedStringOp(stmts[i]) && (i == oldI || !isHltOrInt(stmts[i - 1]))) ++i;
            processBlock(stmts, oldI, i);
            if (i >= stmts.size()) break;
            if (isRepeatedStringOp(stmts[i])) {
                processBlock(stmts, i, i + 1);
                ++i;
            }
            ROSE_ASSERT(i != oldI);
        }
    }
#endif

};

#undef Word

#endif // ROSE_X86INSTRUCTIONSEMANTICS_H
