/*
 * Arithmatic functions
 * This file contains:
 *
 * add, adc, sub, sbc, cmp
 * inc, dec
 * neg, not
 * imul, mul, idiv, div
 * xadd
 *
 * das, daa, aad, aam
 *
 * and, or, xor, test
 * shl, shr, sar, ror, rol, rcr, rcl
 * shld, shrd
 *
 * bts, btr, btc, bt
 * bsf, bsr
 *
 * Gets #included by cpu.macro.js
 *
*/
"use strict";

#define add8(dest, src)  cpu.add(dest, src, OPSIZE_8)
#define add16(dest, src) cpu.add(dest, src, OPSIZE_16)
#define add32(dest, src) cpu.add(dest, src, OPSIZE_32)

#define adc8(dest, src)  cpu.adc(dest, src, OPSIZE_8)
#define adc16(dest, src) cpu.adc(dest, src, OPSIZE_16)
#define adc32(dest, src) cpu.adc(dest, src, OPSIZE_32)

#define sub8(dest, src)  cpu.sub(dest, src, OPSIZE_8)
#define sub16(dest, src) cpu.sub(dest, src, OPSIZE_16)
#define sub32(dest, src) cpu.sub(dest, src, OPSIZE_32)

#define cmp8(dest, src)  cpu.sub(dest, src, OPSIZE_8)
#define cmp16(dest, src) cpu.sub(dest, src, OPSIZE_16)
#define cmp32(dest, src) cpu.sub(dest, src, OPSIZE_32)

#define sbb8(dest, src)  cpu.sbb(dest, src, OPSIZE_8)
#define sbb16(dest, src) cpu.sbb(dest, src, OPSIZE_16)
#define sbb32(dest, src) cpu.sbb(dest, src, OPSIZE_32)

v86.prototype.add = function(dest_operand, source_operand, op_size)
{
    this.last_op1 = dest_operand;
    this.last_op2 = source_operand;
    this.last_add_result = this.last_result = dest_operand + source_operand | 0;
    
    this.last_op_size = op_size;
    this.flags_changed = flags_all;

    return this.last_result;
}

v86.prototype.adc = function(dest_operand, source_operand, op_size)
{
    var cf = this.getcf();
    this.last_op1 = dest_operand;
    this.last_op2 = source_operand;
    this.last_add_result = this.last_result = (dest_operand + source_operand | 0) + cf | 0;
    
    this.last_op_size = op_size;
    this.flags_changed = flags_all;

    return this.last_result;
}

v86.prototype.cmp = function(dest_operand, source_operand, op_size)
{
    this.last_add_result = dest_operand;
    this.last_op2 = source_operand;
    this.last_op1 = this.last_result = dest_operand - source_operand | 0;

    this.last_op_size = op_size;
    this.flags_changed = flags_all;
}

v86.prototype.sub = function(dest_operand, source_operand, op_size)
{
    this.last_add_result = dest_operand;
    this.last_op2 = source_operand;
    this.last_op1 = this.last_result = dest_operand - source_operand | 0;
    
    this.last_op_size = op_size;
    this.flags_changed = flags_all;

    return this.last_result;
}

v86.prototype.sbb = function(dest_operand, source_operand, op_size)
{
    var cf = this.getcf();
    this.last_add_result = dest_operand;
    this.last_op2 = source_operand;
    this.last_op1 = this.last_result = dest_operand - source_operand - cf | 0;
    this.last_op_size = op_size;
    
    this.flags_changed = flags_all;

    return this.last_result;
}

/*
 * inc and dec
 */

#define inc8(dest) cpu.inc(dest, OPSIZE_8)
#define inc16(dest) cpu.inc(dest, OPSIZE_16)
#define inc32(dest) cpu.inc(dest, OPSIZE_32)

#define dec8(dest) cpu.dec(dest, OPSIZE_8)
#define dec16(dest) cpu.dec(dest, OPSIZE_16)
#define dec32(dest) cpu.dec(dest, OPSIZE_32)

v86.prototype.inc = function(dest_operand, op_size)
{
    this.flags = (this.flags & ~1) | this.getcf();
    this.last_op1 = dest_operand;
    this.last_op2 = 1;
    this.last_add_result = this.last_result = dest_operand + 1 | 0;
    this.last_op_size = op_size;
    
    this.flags_changed = flags_all & ~1;

    return this.last_result;
}

v86.prototype.dec = function(dest_operand, op_size)
{
    this.flags = (this.flags & ~1) | this.getcf();
    this.last_add_result = dest_operand;
    this.last_op2 = 1;
    this.last_op1 = this.last_result = dest_operand - 1 | 0;
    this.last_op_size = op_size;
    
    this.flags_changed = flags_all & ~1;

    return this.last_result;
}


/*
 * neg
 */
#define neg8(dest) neg(dest, OPSIZE_8)
#define neg16(dest) neg(dest, OPSIZE_16)
#define neg32(dest) neg(dest, OPSIZE_32)

v86.prototype.neg = function(dest_operand, op_size)
{
    this.last_op1 = this.last_result = -dest_operand | 0;
    
    this.flags_changed = flags_all;
    this.last_add_result = 0;
    this.last_op2 = dest_operand;
    this.last_op_size = op_size;

    return this.last_result;
}


/*
 * mul, imul, div, idiv
 *
 * Note: imul has some extra opcodes
 *       while other functions only allow
 *       ax * modrm
 */

v86.prototype.mul8 = function(source_operand)
{
    var result = source_operand * this.reg8[reg_al];

    this.reg16[reg_ax] = result;

    if(result < 0x100)
    {
        this.flags = this.flags & ~1 & ~flag_overflow;
    }
    else
    {
        this.flags = this.flags | 1 | flag_overflow;
    }

    this.flags_changed = 0;
}

v86.prototype.imul8 = function(source_operand)
{
    var result = source_operand * this.reg8s[reg_al];

    this.reg16[reg_ax] = result;

    if(result > 0x7F || result < -0x80)
    {
        this.flags = this.flags | 1 | flag_overflow;
    }
    else
    {
        this.flags = this.flags & ~1 & ~flag_overflow;
    }
    this.flags_changed = 0;
}

v86.prototype.mul16 = function(source_operand)
{
    var result = source_operand * this.reg16[reg_ax],
        high_result = result >>> 16;
    //console.log(h(a) + " * " + h(this.reg16[reg_ax]) + " = " + h(result));

    this.reg16[reg_ax] = result;
    this.reg16[reg_dx] = high_result;

    if(high_result === 0)
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    else
    {
        this.flags |= 1 | flag_overflow;
    }
    this.flags_changed = 0;
}

/*
 * imul with 1 argument
 * ax = ax * r/m
 */
v86.prototype.imul16 = function(source_operand)
{
    var result = source_operand * this.reg16s[reg_ax];

    this.reg16[reg_ax] = result;
    this.reg16[reg_dx] = result >> 16;

    if(result > 0x7FFF || result < -0x8000)
    {
        this.flags |= 1 | flag_overflow;
    }
    else
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    this.flags_changed = 0;
}

/*
 * imul with 2 or 3 arguments
 * reg = reg * r/m
 * reg = imm * r/m
 */
v86.prototype.imul_reg16 = function(operand1, operand2)
{
    dbg_assert(operand1 < 0x8000 && operand1 >= -0x8000);
    dbg_assert(operand2 < 0x8000 && operand2 >= -0x8000);

    var result = operand1 * operand2;

    if(result > 0x7FFF || result < -0x8000)
    {
        this.flags |= 1 | flag_overflow;
    }
    else
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    this.flags_changed = 0;

    return result;
}


#define do_mul32(a, b)\
    var a00 = a & 0xFFFF;\
    var a16 = a >>> 16;\
    var b00 = b & 0xFFFF;\
    var b16 = b >>> 16;\
    var low_result = a00 * b00;\
    var mid = (low_result >>> 16) + (a16 * b00 | 0) | 0;\
    var high_result = mid >>> 16;\
    mid = (mid & 0xFFFF) + (a00 * b16 | 0) | 0;\
    low_result = (mid << 16) | low_result & 0xFFFF;\
    high_result = ((mid >>> 16) + (a16 * b16 | 0) | 0) + high_result | 0;\


#define do_imul32(a, b)\
    var is_neg = false; \
    if(a < 0) { \
        is_neg = true; \
        a = -a | 0; \
    } \
    if(b < 0) { \
        is_neg = !is_neg; \
        b = -b | 0; \
    } \
    do_mul32(a, b); \
    if(is_neg) { \
        low_result = -low_result | 0; \
        high_result = ~high_result + !low_result | 0; \
    }

v86.prototype.mul32 = function(source_operand)
{
    var dest_operand = this.reg32s[reg_eax];

    do_mul32(dest_operand, source_operand);

    this.reg32s[reg_eax] = low_result;
    this.reg32s[reg_edx] = high_result;

    if(high_result === 0)
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    else
    {
        this.flags |= 1 | flag_overflow;
    }
    this.flags_changed = 0;

    //console.log(h(source_operand >>> 0, 8) + " * " + h(dest_operand >>> 0, 8));
    //console.log("= " + h(this.reg32[reg_edx], 8) + ":" + h(this.reg32[reg_eax], 8));
}

v86.prototype.imul32 = function(source_operand)
{
    dbg_assert(source_operand < 0x80000000 && source_operand >= -0x80000000);

    var dest_operand = this.reg32s[reg_eax];

    do_imul32(dest_operand, source_operand);
    
    this.reg32s[reg_eax] = low_result;
    this.reg32s[reg_edx] = high_result;

    if(high_result === (low_result >> 31))
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    else
    {
        this.flags |= 1 | flag_overflow;
    }
    this.flags_changed = 0;

    //console.log(target_operand + " * " + source_operand);
    //console.log("= " + h(this.reg32[reg_edx]) + " " + h(this.reg32[reg_eax]));
}

/*
 * imul with 2 or 3 arguments
 * reg = reg * r/m
 * reg = imm * r/m
 */
v86.prototype.imul_reg32 = function(operand1, operand2)
{
    dbg_assert(operand1 < 0x80000000 && operand1 >= -0x80000000);
    dbg_assert(operand2 < 0x80000000 && operand2 >= -0x80000000);

    do_imul32(operand1, operand2);

    if(high_result === (low_result >> 31))
    {
        this.flags &= ~1 & ~flag_overflow;
    }
    else
    {
        this.flags |= 1 | flag_overflow;
    }
    this.flags_changed = 0;

    return low_result;

    //console.log(operand + " * " + source_operand);
    //console.log("= " + this.reg32[reg]);
}
#undef do_mul32
#undef do_imul32

v86.prototype.div8 = function(source_operand)
{
    dbg_assert(source_operand >= 0 && source_operand < 0x100);

    var target_operand = this.reg16[reg_ax],
        result = target_operand / source_operand | 0;

    if(result >= 0x100 || source_operand === 0)
    {
        this.trigger_de();
    }
    else
    {
        this.reg8[reg_al] = result;
        this.reg8[reg_ah] = target_operand % source_operand;
    }
}

v86.prototype.idiv8 = function(source_operand)
{
    dbg_assert(source_operand >= -0x80 && source_operand < 0x80);

    var target_operand = this.reg16s[reg_ax],
        result = target_operand / source_operand | 0;

    if(result >= 0x80 || result <= -0x81 || source_operand === 0)
    {
        this.trigger_de();
    }
    else
    {
        this.reg8[reg_al] = result;
        this.reg8[reg_ah] = target_operand % source_operand;
    }
}

v86.prototype.div16 = function(source_operand)
{
    dbg_assert(source_operand >= 0 && source_operand < 0x10000);

    var 
        target_operand = (this.reg16[reg_ax] | this.reg16[reg_dx] << 16) >>> 0,
        result = target_operand / source_operand | 0;

    if(result >= 0x10000 || result < 0 || source_operand === 0)
    {
        this.trigger_de();
    }
    else
    {
        this.reg16[reg_ax] = result;
        this.reg16[reg_dx] = target_operand % source_operand;
    }
}

v86.prototype.idiv16 = function(source_operand)
{
    dbg_assert(source_operand >= -0x8000 && source_operand < 0x8000);

    var target_operand = this.reg16[reg_ax] | (this.reg16[reg_dx] << 16),
        result = target_operand / source_operand | 0;
    
    if(result >= 0x8000 || result <= -0x8001 || source_operand === 0)
    {
        this.trigger_de();
    }
    else
    {    
        this.reg16[reg_ax] = result;
        this.reg16[reg_dx] = target_operand % source_operand;
    }
}

// If the dividend is too large, the division cannot be done precisely using
// JavaScript's double floating point numbers. Run simple long divsion until
// the dividend is small enough
#define do_div32(div_low, div_high, quot)\
    if(div_high >= quot || !quot) this.trigger_de();\
    var result = 0;\
    if(div_high > 0x100000) {\
        var m = 0;\
        var i = 32;\
        var q = quot;\
        while(q > div_high) {\
            q >>>= 1;\
            i--;\
        }\
        while(div_high > 0x100000) {\
            if(div_high >= q) {\
                div_high -= q;\
                var sub = quot << i >>> 0;\
                if(sub > div_low) {\
                    div_high--;\
                }\
                div_low = div_low - sub >>> 0;\
                result |= 1 << i\
            }\
            i--;\
            q >>= 1;\
        }\
        result >>>= 0;\
    }\
    var div = div_low + div_high * 0x100000000;\
    var mod = div % quot;\
    result += div / quot | 0;\


v86.prototype.div32 = function(source_operand)
{
    dbg_assert(source_operand >= 0 && source_operand <= 0xffffffff);

    var dest_operand_low = this.reg32[reg_eax],
        dest_operand_high = this.reg32[reg_edx];

    do_div32(dest_operand_low, dest_operand_high, source_operand);

    if(result >= 0x100000000 || source_operand === 0)
    {
        dbg_log("div32 #DE: " + h(dest_operand_high, 8) + ":" + h(dest_operand_low, 8) + " div " + h(source_operand, 8));

        this.trigger_de();
    }
    else
    {    
        this.reg32s[reg_eax] = result;
        this.reg32s[reg_edx] = mod;
    }

    //console.log(h(dest_operand_high) + ":" + h(dest_operand_low) + " / " + h(source_operand));
    //console.log("= " + h(this.reg32[reg_eax]) + " rem " + h(this.reg32[reg_edx]));
}

v86.prototype.idiv32 = function(source_operand)
{
    dbg_assert(source_operand < 0x80000000 && source_operand >= -0x80000000);

    var dest_operand_low = this.reg32[reg_eax],
        dest_operand_high = this.reg32s[reg_edx],
        div_is_neg = false,
        is_neg = false;

    if(source_operand < 0)
    {
        is_neg = true;
        source_operand = -source_operand;
    }

    if(dest_operand_high < 0)
    {
        div_is_neg = true;
        is_neg = !is_neg;
        dest_operand_low = -dest_operand_low | 0;
        dest_operand_high = ~dest_operand_high + !dest_operand_low;
    }

    do_div32(dest_operand_low, dest_operand_high, source_operand);

    if(is_neg)
    {
        result = -result | 0;
    }

    if(div_is_neg)
    {
        mod = -mod | 0;
    }

    if(result >= 0x80000000 || result <= -0x80000001 || source_operand === 0)
    {
        dbg_log("div32 #DE: " + h(dest_operand_high, 8) + ":" + h(dest_operand_low, 8) + " div " + h(source_operand, 8));
        this.trigger_de();
    }
    else
    {    
        this.reg32s[reg_eax] = result;
        this.reg32s[reg_edx] = mod;
    }

    //console.log(h(dest_operand_high) + ":" + h(dest_operand_low) + " / " + h(source_operand));
    //console.log("= " + h(this.reg32[reg_eax]) + " rem " + h(this.reg32[reg_edx]));
}


v86.prototype.xadd8 = function(source_operand, reg)
{
    var tmp = this.reg8[reg];

    this.reg8[reg] = source_operand;

    return this.add(source_operand, tmp, OPSIZE_8);
}


v86.prototype.xadd16 = function(source_operand, reg)
{
    var tmp = this.reg16[reg];

    this.reg16[reg] = source_operand;

    return this.add(source_operand, tmp, OPSIZE_16);
}


v86.prototype.xadd32 = function(source_operand, reg)
{
    var tmp = this.reg32s[reg];

    this.reg32s[reg] = source_operand;

    return this.add(source_operand, tmp, OPSIZE_32);
}


v86.prototype.bcd_daa = function()
{
    //dbg_log("daa");
    // decimal adjust after addition
    var old_al = this.reg8[reg_al],
        old_cf = this.getcf(),
        old_af = this.getaf();

    this.flags &= ~1 & ~flag_adjust

    if((old_al & 0xF) > 9 || old_af)
    {
        this.reg8[reg_al] += 6;
        this.flags |= flag_adjust;
    }
    if(old_al > 0x99 || old_cf)
    {
        this.reg8[reg_al] += 0x60;
        this.flags |= 1;
    }

    this.last_result = this.reg8[reg_al];
    this.last_op_size = OPSIZE_8;
    this.last_op1 = this.last_op2 = 0;
    this.flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
}

v86.prototype.bcd_das = function()
{
    //dbg_log("das");
    // decimal adjust after subtraction
    var old_al = this.reg8[reg_al],
        old_cf = this.getcf();

    this.flags &= ~1;

    if((old_al & 0xF) > 9 || this.getaf())
    {
        this.reg8[reg_al] -= 6;
        this.flags |= flag_adjust;
        this.flags = this.flags & ~1 | old_cf | this.reg8[reg_al] >> 7;
    }
    else
    {
        this.flags &= ~flag_adjust;
    }

    if(old_al > 0x99 || old_cf)
    {
        this.reg8[reg_al] -= 0x60;
        this.flags |= 1;
    }

    this.last_result = this.reg8[reg_al];
    this.last_op_size = OPSIZE_8;
    this.last_op1 = this.last_op2 = 0;
    this.flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
}

v86.prototype.bcd_aam = function()
{
    //dbg_log("aam");
    // ascii adjust after multiplication
    var imm8 = this.read_imm8();

    if(imm8 === 0)
    {
        this.trigger_de();
    }
    else
    {
        var temp = this.reg8[reg_al];
        this.reg8[reg_ah] = temp / imm8;
        this.reg8[reg_al] = temp % imm8;

        this.last_result = this.reg8[reg_al];

        this.flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
        this.flags &= ~1 & ~flag_adjust & ~flag_overflow;
    }
}

v86.prototype.bcd_aad = function()
{
    //dbg_log("aad");
    // ascii adjust after division
    var imm8 = this.read_imm8();

    this.last_result = this.reg8[reg_al] + this.reg8[reg_ah] * imm8;
    this.reg16[reg_ax] = this.last_result & 0xFF;
    this.last_op_size = OPSIZE_8;

    this.flags_changed = flags_all & ~1 & ~flag_adjust & ~flag_overflow;
    this.flags &= ~1 & ~flag_adjust & ~flag_overflow;
}

v86.prototype.bcd_aaa = function()
{
    //dbg_log("aaa");
    if((this.reg8[reg_al] & 0xF) > 9 || this.getaf())
    {
        this.reg16[reg_ax] += 6;
        this.reg8[reg_ah] += 1;
        this.flags |= flag_adjust | 1;
    }
    else
    {
        this.flags &= ~flag_adjust & ~1;
    }
    this.reg8[reg_al] &= 0xF;

    this.flags_changed &= ~flag_adjust & ~1;
};


v86.prototype.bcd_aas = function()
{
    //dbg_log("aas");
    if((this.reg8[reg_al] & 0xF) > 9 || this.getaf())
    {
        this.reg16[reg_ax] -= 6;
        this.reg8[reg_ah] -= 1;
        this.flags |= flag_adjust | 1;
    }
    else
    {
        this.flags &= ~flag_adjust & ~1;
    }
    this.reg8[reg_al] &= 0xF;

    this.flags_changed &= ~flag_adjust & ~1;
}


/*                     \O
 * bitwise functions    |\
 *                     / \
 *
 * and, or, xor, test
 * shl, shr, sar, rol, ror, rcl, ror
 * shrd, shld
 *
 * bt, bts, btr, btc
 * bsf, bsr
 */

#define and8(dest, src)  cpu.and(dest, src, OPSIZE_8)
#define and16(dest, src) cpu.and(dest, src, OPSIZE_16)
#define and32(dest, src) cpu.and(dest, src, OPSIZE_32)

#define test8(dest, src)  cpu.and(dest, src, OPSIZE_8)
#define test16(dest, src) cpu.and(dest, src, OPSIZE_16)
#define test32(dest, src) cpu.and(dest, src, OPSIZE_32)

#define or8(dest, src)  cpu.or(dest, src, OPSIZE_8)
#define or16(dest, src) cpu.or(dest, src, OPSIZE_16)
#define or32(dest, src) cpu.or(dest, src, OPSIZE_32)

#define xor8(dest, src)  cpu.xor(dest, src, OPSIZE_8)
#define xor16(dest, src) cpu.xor(dest, src, OPSIZE_16)
#define xor32(dest, src) cpu.xor(dest, src, OPSIZE_32)

v86.prototype.and = function(dest_operand, source_operand, op_size)
{
    this.last_result = dest_operand & source_operand;
    
    this.last_op_size = op_size;
    this.flags &= ~1 & ~flag_overflow & ~flag_adjust;
    this.flags_changed = flags_all & ~1 & ~flag_overflow & ~flag_adjust;

    return this.last_result;
}

v86.prototype.or = function(dest_operand, source_operand, op_size)
{
    this.last_result = dest_operand | source_operand;
    
    this.last_op_size = op_size;
    this.flags &= ~1 & ~flag_overflow & ~flag_adjust;
    this.flags_changed = flags_all & ~1 & ~flag_overflow & ~flag_adjust;

    return this.last_result;
}

v86.prototype.xor = function(dest_operand, source_operand, op_size)
{
    this.last_result = dest_operand ^ source_operand;
    
    this.last_op_size = op_size;
    this.flags &= ~1 & ~flag_overflow & ~flag_adjust;
    this.flags_changed = flags_all & ~1 & ~flag_overflow & ~flag_adjust;

    return this.last_result;
}


/*
 * rotates and shifts
 */

v86.prototype.rol8 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }
    count &= 7;

    var result = dest_operand << count | dest_operand >> (8 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result & 1)
                | (result << 11 ^ result << 4) & flag_overflow;

    return result;
}

v86.prototype.rol16 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }
    count &= 15;

    var result = dest_operand << count | dest_operand >> (16 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result & 1)
                | (result << 11 ^ result >> 4) & flag_overflow;

    return result;
}

v86.prototype.rol32 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand << count | dest_operand >>> (32 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result & 1)
                | (result << 11 ^ result >> 20) & flag_overflow;

    return result;
}

v86.prototype.rcl8 = function(dest_operand, count)
{
    count %= 9;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand << count | this.getcf() << (count - 1) | dest_operand >> (9 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 8 & 1)
                | (result << 3 ^ result << 4) & flag_overflow;

    return result;
}

v86.prototype.rcl16 = function(dest_operand, count)
{
    count %= 17;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand << count | this.getcf() << (count - 1) | dest_operand >> (17 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 16 & 1) 
                | (result >> 5 ^ result >> 4) & flag_overflow;

    return result;
}

v86.prototype.rcl32 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand << count | this.getcf() << (count - 1);

    if(count > 1)
    {
        result |= dest_operand >>> (33 - count);
    }

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) | (dest_operand >>> (32 - count) & 1);
    this.flags |= (this.flags << 11 ^ result >> 20) & flag_overflow;

    return result;
}

v86.prototype.ror8 = function(dest_operand, count)
{
    count &= 7;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >> count | dest_operand << (8 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 7 & 1)
                | (result << 4 ^ result << 5) & flag_overflow;

    return result;
} 

v86.prototype.ror16 = function(dest_operand, count)
{
    count &= 15;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >> count | dest_operand << (16 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 15 & 1) 
                | (result >> 4 ^ result >> 3) & flag_overflow;

    return result;
}    

v86.prototype.ror32 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >>> count | dest_operand << (32 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 31 & 1) 
                | (result >> 20 ^ result >> 19) & flag_overflow;

    return result;
}

v86.prototype.rcr8 = function(dest_operand, count)
{
    count %= 9;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >> count | this.getcf() << (8 - count) | dest_operand << (9 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 8 & 1)
                | (result << 4 ^ result << 5) & flag_overflow;

    return result;
}    

v86.prototype.rcr16 = function(dest_operand, count)
{
    count %= 17;
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >> count | this.getcf() << (16 - count) | dest_operand << (17 - count);

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (result >> 16 & 1)
                | (result >> 4 ^ result >> 3) & flag_overflow;

    return result;
}

v86.prototype.rcr32 = function(dest_operand, count)
{
    if(!count)
    {
        return dest_operand;
    }

    var result = dest_operand >>> count | this.getcf() << (32 - count);

    if(count > 1)
    {
        result |= dest_operand << (33 - count);
    }

    this.flags_changed &= ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (dest_operand >> (count - 1) & 1)
                | (result >> 20 ^ result >> 19) & flag_overflow;

    return result;
}

v86.prototype.shl8 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand << count;

    this.last_op_size = OPSIZE_8;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (this.last_result >> 8 & 1)
                | (this.last_result << 3 ^ this.last_result << 4) & flag_overflow;

    return this.last_result;
}

v86.prototype.shl16 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand << count;

    this.last_op_size = OPSIZE_16;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (this.last_result >> 16 & 1)
                | (this.last_result >> 5 ^ this.last_result >> 4) & flag_overflow;

    return this.last_result;
}

v86.prototype.shl32 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand << count;

    this.last_op_size = OPSIZE_32;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    // test this
    this.flags = (this.flags & ~1 & ~flag_overflow) | (dest_operand >>> (32 - count) & 1);
    this.flags |= ((this.flags & 1) ^ (this.last_result >> 31 & 1)) << 11 & flag_overflow;

    return this.last_result;
}
    
v86.prototype.shr8 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >> count;

    this.last_op_size = OPSIZE_8;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (dest_operand >> (count - 1) & 1)
                | (dest_operand >> 7 & 1) << 11 & flag_overflow;

    return this.last_result;
}    

v86.prototype.shr16 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >> count;

    this.last_op_size = OPSIZE_16;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (dest_operand >> (count - 1) & 1) 
                | (dest_operand >> 4)  & flag_overflow;

    return this.last_result;
}    

v86.prototype.shr32 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >>> count;

    this.last_op_size = OPSIZE_32;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) 
                | (dest_operand >>> (count - 1) & 1)
                | (dest_operand >> 20) & flag_overflow;

    return this.last_result;
}

v86.prototype.sar8 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >> count;

    this.last_op_size = OPSIZE_8;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) | (dest_operand >> (count - 1) & 1);
    // of is zero

    return this.last_result;
}    

v86.prototype.sar16 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >> count;

    this.last_op_size = OPSIZE_16;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) | (dest_operand >> (count - 1) & 1);

    return this.last_result;
}

v86.prototype.sar32 = function(dest_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >> count;

    this.last_op_size = OPSIZE_32;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1 & ~flag_overflow) | (dest_operand >>> (count - 1) & 1);

    return this.last_result;
}


v86.prototype.shrd16 = function(dest_operand, source_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    if(count <= 16)
    {
        this.last_result = dest_operand >> count | source_operand << (16 - count);
        this.flags = (this.flags & ~1) | (dest_operand >> (count - 1) & 1);
    }
    else
    {
        this.last_result = dest_operand << (32 - count) | source_operand >> (count - 16);
        this.flags = (this.flags & ~1) | (source_operand >> (count - 17) & 1);
    }

    this.last_op_size = OPSIZE_16;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~flag_overflow) | ((this.last_result ^ dest_operand) >> 4 & flag_overflow);

    return this.last_result;
}

v86.prototype.shrd32 = function(dest_operand, source_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand >>> count | source_operand << (32 - count);

    this.last_op_size = OPSIZE_32;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1) | (dest_operand >>> (count - 1) & 1);
    this.flags = (this.flags & ~flag_overflow) | ((this.last_result ^ dest_operand) >> 20 & flag_overflow);

    return this.last_result;
}

v86.prototype.shld16 = function(dest_operand, source_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    if(count <= 16)
    {
        this.last_result = dest_operand << count | source_operand >>> (16 - count);
        this.flags = (this.flags & ~1) | (dest_operand >>> (16 - count) & 1);
    }
    else
    {
        this.last_result = dest_operand >> (32 - count) | source_operand << (count - 16);
        this.flags = (this.flags & ~1) | (source_operand >>> (32 - count) & 1);
    }

    this.last_op_size = OPSIZE_16;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~flag_overflow) | ((this.flags & 1) ^ (this.last_result >> 15 & 1)) << 11;

    return this.last_result;
}

v86.prototype.shld32 = function(dest_operand, source_operand, count)
{
    if(count === 0)
    {
        return dest_operand;
    }

    this.last_result = dest_operand << count | source_operand >>> (32 - count);

    this.last_op_size = OPSIZE_32;
    this.flags_changed = flags_all & ~1 & ~flag_overflow;
    this.flags = (this.flags & ~1) | (dest_operand >>> (32 - count) & 1);
    this.flags = (this.flags & ~flag_overflow) | ((this.flags & 1) ^ (this.last_result >> 31 & 1)) << 11;

    return this.last_result;
}


v86.prototype.bt_reg = function(bit_base, bit_offset)
{
    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;
}

v86.prototype.btc_reg = function(bit_base, bit_offset)
{
    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    return bit_base ^ 1 << bit_offset;
}

v86.prototype.bts_reg = function(bit_base, bit_offset)
{
    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    return bit_base | 1 << bit_offset;
}

v86.prototype.btr_reg = function(bit_base, bit_offset)
{
    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    return bit_base & ~(1 << bit_offset);
}

v86.prototype.bt_mem = function(virt_addr, bit_offset)
{
    var bit_base = this.safe_read8(virt_addr + (bit_offset >> 3));
    bit_offset &= 7;

    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;
}

v86.prototype.btc_mem = function(virt_addr, bit_offset)
{
    var phys_addr = this.translate_address_write(virt_addr + (bit_offset >> 3));
    var bit_base = this.memory.read8(phys_addr);

    bit_offset &= 7;

    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    this.memory.write8(phys_addr, bit_base ^ 1 << bit_offset);
}

v86.prototype.btr_mem = function(virt_addr, bit_offset)
{
    var phys_addr = this.translate_address_write(virt_addr + (bit_offset >> 3));
    var bit_base = this.memory.read8(phys_addr);

    bit_offset &= 7;

    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    this.memory.write8(phys_addr, bit_base & ~(1 << bit_offset));
}

v86.prototype.bts_mem = function(virt_addr, bit_offset)
{
    var phys_addr = this.translate_address_write(virt_addr + (bit_offset >> 3));
    var bit_base = this.memory.read8(phys_addr);

    bit_offset &= 7;

    this.flags = (this.flags & ~1) | (bit_base >> bit_offset & 1);
    this.flags_changed &= ~1;

    this.memory.write8(phys_addr, bit_base | 1 << bit_offset);
}

v86.prototype.bsf16 = function(old, bit_base)
{
    this.flags_changed = 0;

    if(bit_base === 0)
    {
        this.flags |= flag_zero;
        
        // not defined in the docs, but value doesn't change on my intel this
        return old;
    }
    else
    {
        this.flags &= ~flag_zero;

        // http://jsperf.com/lowest-bit-index
        return Math.int_log2(-bit_base & bit_base);
    }
}

v86.prototype.bsf32 = function(old, bit_base)
{
    this.flags_changed = 0;

    if(bit_base === 0)
    {
        this.flags |= flag_zero;

        return old;
    }
    else
    {
        this.flags &= ~flag_zero;

        return Math.int_log2((-bit_base & bit_base) >>> 0);
    }
}

v86.prototype.bsr16 = function(old, bit_base)
{
    this.flags_changed = 0;

    if(bit_base === 0)
    {
        this.flags |= flag_zero;
        return old;
    }
    else
    {
        this.flags &= ~flag_zero;

        return Math.int_log2(bit_base);
    }
}

v86.prototype.bsr32 = function(old, bit_base)
{
    this.flags_changed = 0;

    if(bit_base === 0)
    {
        this.flags |= flag_zero;
        return old;
    }
    else
    {
        this.flags &= ~flag_zero;

        return Math.int_log2(bit_base >>> 0);
    }
}


