/*
Copyright Notice
================
BOCHS is Copyright 1994 by Kevin P. Lawton.

BOCHS is shareware for PERSONAL USE only.

For more information, read the file 'LICENSE' included in the bochs
distribution.  If you don't have access to this file, or have questions
regarding the licensing policy, the author may be contacted via:

    US Mail:  Kevin Lawton
              528 Lexington St.
              Waltham, MA 02154

    EMail:    bochs@tiac.net
*/





#define BX_ARITH_C 1

#include <stdio.h>
#include <assert.h>
#include "bx_bochs.h"

    


  void
bx_MUL_ALEb()
{
  Bit8u op2, op1;
  Bit16u product_16;
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;


  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  op1 = AL;

  /* op2 is a register or memory reference */
  if (op2_type == BX_REGISTER_REF) {
    BX_READ_8BIT_REG(op2, op2_addr);
    }
  else
    /* pointer, segment address pair */
    bx_access_virtual(op2_seg_reg, op2_addr, 1, BX_READ, &op2);

  product_16 = op1 * op2;

  /* set EFLAGS:
   * MUL affects the following flags: C,O
   */

  bx_cpu.eflags.cf = bx_cpu.eflags.of = ((product_16 & 0xFF00) != 0);

  /* now write product back to destination */

  AX = product_16;
}

  void
bx_MUL_eAXEv()
{

#ifdef BX_NO_64BIT_TYPE
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16u op1_16, op2_16, product_16h, product_16l;
  Bit32u product_32;

  Bit32u op1_32, op2_32;
  Bit16u a, b, c, d;
  Bit16u w0, w1, w2, w3;
  Bit32u t0, t1, t2;
  Bit32u bd, bc, ad, ac;
  Bit32u product_32h, product_32l;
  

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    op1_32 = EAX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

    /* multiplying two 32bit numbers, resulting with a 64bit number
     * is a little tricky since not all machines support 64bit numbers.
     * To handle this, I look at the the operands, op1_32 & op2_32 as
     * being composed of two 16bit components each:
     *
     *           31..16 15..0
     * op1_32:     a      b
     * op2_32:     c      d
     *
     * The 64bit result of op1_32 * op2_32 can be expressed by:
     *
     *   result_64 = (a * 2^16 + b) * (c * 2^16 + d)
     *
     * which after a little math is equivalent to...
     *
     *   result_64 = a*c*2^32 + a*d*2^16 + b*c*2^16 + b*d
     *
     *         63..48 47..32 31..16 15..0
     *         ----a*c------
     *                -----a*d-----
     *                -----b*c-----
     *                       -----b*d----
     *
     * now I calculate the 64bit result, 16bits at a time (one word),
     * starting from lowest to highest.
     */

    a = op1_32 >> 16;
    b = op1_32 & 0xFFFF;
    c = op2_32 >> 16;
    d = op2_32 & 0xFFFF;

    bd = b * d;
    bc = b * c;
    ad = a * d;
    ac = a * c;

    w0 = bd & 0xFFFF;
    t0 = (bd >> 16) + (ad & 0xFFFF) + (bc & 0xFFFF);
    w1 = t0 & 0xFFFF;
    t1 = (t0 >> 16) + (ad >> 16) + (bc >> 16) + (ac & 0xFFFF);
    w2 = t1 & 0xFFFF;
    t2 = (t1 >> 16) + (ac >> 16);
    w3 = t2;
   
    product_32h = (w3 << 16) + w2;
    product_32l = (w1 << 16) + w0;

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_32h != 0);

    /* now write product back to destination */

    EAX = product_32l;
    EDX = product_32h;
    }
  else { /* 16bit opsize mode */
    op1_16 = AX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    product_32 = op1_16 * op2_16;

    product_16l = (product_32 & 0xFFFF);
    product_16h = product_32 >> 16;

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_16h != 0);

    /* now write product back to destination */

    AX = product_16l;
    DX = product_16h;
    }

#else /* compiler offers 64bit type */

  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16u op1_16, op2_16, product_16h, product_16l;
  Bit32u product_32;

  Bit32u op1_32, op2_32, product_32h, product_32l;
  Bit64u product_64;
  

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    op1_32 = EAX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

   
    product_64 = ((Bit64u) op1_32) * ((Bit64u) op2_32);

    product_32l = (product_64 & 0xFFFFFFFF);
    product_32h = (product_64 >> 32);

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_32h != 0);

    /* now write product back to destination */

    EAX = product_32l;
    EDX = product_32h;
    }
  else { /* 16bit opsize mode */
    op1_16 = AX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    product_32 = op1_16 * op2_16;

    product_16l = (product_32 & 0xFFFF);
    product_16h = product_32 >> 16;

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_16h != 0);

    /* now write product back to destination */

    AX = product_16l;
    DX = product_16h;
    }
#endif  /* BX_NO_64BIT_TYPE */
}

  void
bx_IMUL_ALEb()
{
  Bit8u op2, op1;
  Bit8s sign_op1, sign_op2;
  Bit16u product_16;
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;


  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  op1 = AL;

  /* op2 is a register or memory reference */
  if (op2_type == BX_REGISTER_REF) {
    BX_READ_8BIT_REG(op2, op2_addr);
    }
  else
    /* pointer, segment address pair */
    bx_access_virtual(op2_seg_reg, op2_addr, 1, BX_READ, &op2);

  if (op1 > 0x7F) { /* negative */
    op1 = (0xFF - op1) + 1;
    sign_op1 = -1;
    }
  else
    sign_op1 = 1;

  if (op2 > 0x7F) { /* negative */
    op2 = (0xFF - op2) + 1;
    sign_op2 = -1;
    }
  else
    sign_op2 = 1;

  product_16 = op1 * op2;
  if (product_16  && (sign_op1 * sign_op2  <  0))
    product_16 = (0xFFFF - product_16) + 1;

  /* set EFLAGS:
   * MUL affects the following flags: C,O
   */

  bx_cpu.eflags.cf = bx_cpu.eflags.of = ((product_16 & 0xFF00) != 0);

  /* now write product back to destination */

  AX = product_16;
}


  void
bx_IMUL_eAXEv()
{
#ifdef BX_NO_64BIT_TYPE
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16u op1_16, op2_16, product_16h, product_16l;
  Bit32u product_32;
  Bit8s  sign_op1, sign_op2;

  Bit32u op1_32, op2_32;
  Bit16u a, b, c, d;
  Bit16u w0, w1, w2, w3;
  Bit32u t0, t1, t2;
  Bit32u bd, bc, ad, ac;
  Bit32u product_32h, product_32l;
  

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    op1_32 = EAX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

    if (op1_32 > 0x7FFFFFFF) { /* negative */
      op1_32 = (0xFFFFFFFF - op1_32) + 1;
      sign_op1 = -1;
      }
    else
      sign_op1 = 1;

    if (op2_32 > 0x7FFFFFFF) { /* negative */
      op2_32 = (0xFFFFFFFF - op2_32) + 1;
      sign_op2 = -1;
      }
    else
      sign_op2 = 1;

    /* multiplying two 32bit numbers, resulting with a 64bit number
     * is a little tricky since not all machines support 64bit numbers.
     * To handle this, I look at the the operands, op1_32 & op2_32 as
     * being composed of two 16bit components each:
     *
     *           31..16 15..0
     * op1_32:     a      b
     * op2_32:     c      d
     *
     * The 64bit result of op1_32 * op2_32 can be expressed by:
     *
     *   result_64 = (a * 2^16 + b) * (c * 2^16 + d)
     *
     * which after a little math is equivalent to...
     *
     *   result_64 = a*c*2^32 + a*d*2^16 + b*c*2^16 + b*d
     *
     *         63..48 47..32 31..16 15..0
     *         ----a*c------
     *                -----a*d-----
     *                -----b*c-----
     *                       -----b*d----
     *
     * now I calculate the 64bit result, 16bits at a time (one word),
     * starting from lowest to highest.
     */

    a = op1_32 >> 16;
    b = op1_32 & 0xFFFF;
    c = op2_32 >> 16;
    d = op2_32 & 0xFFFF;

    bd = b * d;
    bc = b * c;
    ad = a * d;
    ac = a * c;

    w0 = bd & 0xFFFF;
    t0 = (bd >> 16) + (ad & 0xFFFF) + (bc & 0xFFFF);
    w1 = t0 & 0xFFFF;
    t1 = (t0 >> 16) + (ad >> 16) + (bc >> 16) + (ac & 0xFFFF);
    w2 = t1 & 0xFFFF;
    t2 = (t1 >> 16) + (ac >> 16);
    w3 = t2;
   
    product_32h = (w3 << 16) + w2;
    product_32l = (w1 << 16) + w0;
    if (product_32h && product_32l && (sign_op1 * sign_op2 < 0)) {
      product_32h = ~product_32h;
      product_32l = ~product_32l;
      product_32l++;
      if (!product_32l) product_32h++;  /* carry */
      }

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_32h != 0);

    /* now write product back to destination */

    EAX = product_32l;
    EDX = product_32h;
    }
  else { /* 16bit opsize mode */
    op1_16 = AX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    product_32 = op1_16 * op2_16;

    product_16l = (product_32 & 0xFFFF);
    product_16h = product_32 >> 16;

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_16h != 0);

    /* now write product back to destination */

    AX = product_16l;
    DX = product_16h;
    }


#else /* compiler offers a 64 bit type */

  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16u op1_16, op2_16, product_16h, product_16l;
  Bit32u product_32;
  Bit8s  sign_op1, sign_op2;

  Bit32u op1_32, op2_32, product_32h, product_32l;
  Bit64u product_64;
  

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    op1_32 = EAX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

    if (op1_32 > 0x7FFFFFFF) { /* negative */
      op1_32 = (0xFFFFFFFF - op1_32) + 1;
      sign_op1 = -1;
      }
    else
      sign_op1 = 1;

    if (op2_32 > 0x7FFFFFFF) { /* negative */
      op2_32 = (0xFFFFFFFF - op2_32) + 1;
      sign_op2 = -1;
      }
    else
      sign_op2 = 1;

    product_64 = ((Bit64u) op1_32) * ((Bit64u) op2_32);

    if (product_64  && (sign_op1 * sign_op2 < 0)) {
      product_64 = -((Bit64s) product_64);
      }
 
    product_32l = (product_64 & 0xFFFFFFFF);
    product_32h = (product_64 >> 32);

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_32h != 0);

    /* now write product back to destination */

    EAX = product_32l;
    EDX = product_32h;
    }
  else { /* 16bit opsize mode */
    op1_16 = AX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    product_32 = op1_16 * op2_16;

    product_16l = (product_32 & 0xFFFF);
    product_16h = product_32 >> 16;

    /* set eflags:
     * MUL affects the following flags: C,O
     */

    bx_cpu.eflags.cf = bx_cpu.eflags.of = (product_16h != 0);

    /* now write product back to destination */

    AX = product_16l;
    DX = product_16h;
    }
#endif /* BX_NO_64BIT_TYPE */
}

  void
bx_DIV_ALEb()
{
  Bit8u op2, quotient_8l, remainder_8;
  Bit16u quotient_16, op1;
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;


  op1 = AX;

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  /* op2 is a register or memory reference */
  if (op2_type == BX_REGISTER_REF) {
    BX_READ_8BIT_REG(op2, op2_addr);
    }
  else
    /* pointer, segment address pair */
    bx_access_virtual(op2_seg_reg, op2_addr, 1, BX_READ, &op2);

  if (op2 == 0) {
    bx_printf(1, "divide by error in DIV_ALEb\n");
    }
  quotient_16 = op1 / op2;
  remainder_8 = op1 % op2;
  quotient_8l = quotient_16 & 0xFF;

  if (quotient_16 != quotient_8l) {
    bx_printf(1, "quotient too large causes divide error fault in DIV_ALEb\n");
    }

  /* set EFLAGS:
   * DIV affects the following flags: O,S,Z,A,P,C are undefined
   */

  /* now write quotient back to destination */

  AL = quotient_8l;
  AH = remainder_8;
}

  void
bx_DIV_eAXEv()
{
#ifdef BX_NO_64BIT_TYPE

  bx_printf(1, "DIV_eAXEv(): opcode not supported yet without\n  inherent 64-bit data type support!\n");

#else /* compiler offers a 64bit type */

  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16u op2_16, remainder_16, quotient_16l;
  Bit32u op1_32, quotient_32;

  Bit32u op2_32, eax, edx, remainder_32, quotient_32l;
  Bit64u op1_64, quotient_64;


  /* op2 is mod+r/m, op1 is the AX/EAX register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    eax = EAX;
    edx = EDX;
    op1_64 = (((Bit64u) edx) << 32) + eax;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

    if (op2_32 == 0) {
      bx_printf(1, "divide by 0 error in DIV_eAXEv\n");
      }
    quotient_64 = op1_64 / op2_32;
    remainder_32 = op1_64 % op2_32;
    quotient_32l = quotient_64 & 0xFFFFFFFF;

    if (quotient_64 != quotient_32l) {
      bx_printf(1, "quotient too large causes divide error fault in DIV_eAXEv\n");
      }

    /* set EFLAGS:
     * DIV affects the following flags: O,S,Z,A,P,C are undefined
     */

    /* now write quotient back to destination */

    EAX = quotient_32l;
    EDX = remainder_32;
    }
  else { /* 16 bit opsize mode */
    op1_32 = (((Bit32u) DX) << 16) + AX;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    if (op2_16 == 0) {
      bx_printf(1, "divide by 0 error in DIV_eAXEv\n");
      }
    quotient_32 = op1_32 / op2_16;
    remainder_16 = op1_32 % op2_16;
    quotient_16l = quotient_32 & 0xFFFF;

    if (quotient_32 != quotient_16l) {
      bx_printf(1, "quotient too large causes divide error fault in DIV_eAXEv\n");
      }

    /* set EFLAGS:
     * DIV affects the following flags: O,S,Z,A,P,C are undefined
     */

    /* now write quotient back to destination */

    AX = quotient_16l;
    DX = remainder_16;
    }
#endif /* BX_NO_64BIT_TYPE */
}

  void
bx_IDIV_ALEb()
{
  Bit8s op2, quotient_8l, remainder_8;
  Bit16s quotient_16, op1;
  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;


  op1 = AX;

  /* op2 is mod+r/m, op1 is the AL register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  /* op2 is a register or memory reference */
  if (op2_type == BX_REGISTER_REF) {
    BX_READ_8BIT_REG(op2, op2_addr);
    }
  else
    /* pointer, segment address pair */
    bx_access_virtual(op2_seg_reg, op2_addr, 1, BX_READ, &op2);

  if (op2 == 0) {
    bx_printf(1, "divide by error in DIV_ALEb\n");
    }

  quotient_16 = op1 / op2;
  remainder_8 = op1 % op2;
  quotient_8l = quotient_16 & 0xFF;

  if (quotient_16 != quotient_8l) {
    bx_printf(1, "quotient too large causes divide error fault in DIV_ALEb\n");
    }

  /* set EFLAGS:
   * DIV affects the following flags: O,S,Z,A,P,C are undefined
   */

  /* now write quotient back to destination */

  AL = quotient_8l;
  AH = remainder_8;
}

  void
bx_IDIV_eAXEv()
{
#ifdef BX_NO_64BIT_TYPE

  bx_printf(1, "IDIV_eAXEv(): opcode not supported yet without\n  inherent 64-bit data type support!\n");

#else /* compiler offers a 64bit type */

  Bit32u unused, op2_addr;
  int op2_type;
  bx_segment_reg_t *op2_seg_reg;

  Bit16s op2_16, ax, dx, remainder_16, quotient_16l;
  Bit32s op1_32, quotient_32;

  Bit32s op2_32, eax, edx, remainder_32, quotient_32l;
  Bit64s op1_64, quotient_64;


  /* op2 is mod+r/m, op1 is the AX/EAX register */
  bx_decode_exgx(&unused, &op2_addr, &op2_type, &op2_seg_reg);

  if (bx_32bit_opsize) {
    eax = EAX;
    edx = EDX;
    op1_64 = (((Bit64u) edx) << 32) + eax;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op2_32, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 4, BX_READ, &op2_32);

    if (op2_32 == 0) {
      bx_printf(1, "divide by 0 error in DIV_eAXEv\n");
      }
    quotient_64 = op1_64 / op2_32;
    remainder_32 = op1_64 % op2_32;
    quotient_32l = quotient_64 & 0xFFFFFFFF;

    if (quotient_64 != quotient_32l) {
      bx_printf(1, "quotient too large causes divide error fault in DIV_eAXEv\n");
      }

    /* set EFLAGS:
     * DIV affects the following flags: O,S,Z,A,P,C are undefined
     */

    /* now write quotient back to destination */

    EAX = quotient_32l;
    EDX = remainder_32;
    }
  else { /* 16 bit opsize mode */
    ax = AX;
    dx = DX;
    op1_32 = (((Bit32u) dx) << 16) + ax;

    /* op2 is a register or memory reference */
    if (op2_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op2_16, op2_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op2_seg_reg, op2_addr, 2, BX_READ, &op2_16);

    if (op2_16 == 0) {
      bx_printf(1, "divide by 0 error in DIV_eAXEv\n");
      }
    quotient_32 = op1_32 / op2_16;
    remainder_16 = op1_32 % op2_16;
    quotient_16l = quotient_32 & 0xFFFF;

    if (quotient_32 != quotient_16l) {
      bx_printf(1, "quotient too large causes divide error fault in DIV_eAXEv\n");
      }

    /* set EFLAGS:
     * DIV affects the following flags: O,S,Z,A,P,C are undefined
     */

    /* now write quotient back to destination */

    AX = quotient_16l;
    DX = remainder_16;
    }

#endif /* BX_NO_64BIT_TYPE */
}


void bx_IMUL_GvEvIv() {bx_invalid_instruction();}
void bx_IMUL_GvEvIb() {bx_invalid_instruction();}

void bx_IMUL_GvEv() {bx_invalid_instruction();}
