/*
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
*/




#include "bx_bochs.h"




  void
bx_JO_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();

  if (bx_cpu.eflags.of)
    bx_cpu.eip += offset;
}

  void
bx_JNO_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.of)
    bx_cpu.eip += offset;
}

  void
bx_JB_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.cf)
    bx_cpu.eip += offset;
}

  void
bx_JNB_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.cf)
    bx_cpu.eip += offset;
}

  void
bx_JZ_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.zf)
    bx_cpu.eip += offset;
}

  void
bx_JNZ_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.zf)
    bx_cpu.eip += offset;
}

  void
bx_JBE_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.cf || bx_cpu.eflags.zf)
    bx_cpu.eip += offset;
}

  void
bx_JNBE_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.cf && !bx_cpu.eflags.zf)
    bx_cpu.eip += offset;
}

  void
bx_JS_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.sf)
    bx_cpu.eip += offset;
}

  void
bx_JNS_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.sf)
    bx_cpu.eip += offset;
}

  void
bx_JP_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.pf)
    bx_cpu.eip += offset;
}

  void
bx_JNP_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (!bx_cpu.eflags.pf)
    bx_cpu.eip += offset;
}

  void
bx_JL_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if ( bx_cpu.eflags.sf != bx_cpu.eflags.of )
    bx_cpu.eip += offset;
}

  void
bx_JNL_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.sf == bx_cpu.eflags.of)
    bx_cpu.eip += offset;
}

  void
bx_JLE_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if (bx_cpu.eflags.zf || (bx_cpu.eflags.sf != bx_cpu.eflags.of) )
    bx_cpu.eip += offset;
}

  void
bx_JNLE_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if ( (bx_cpu.eflags.sf == bx_cpu.eflags.of)  &&  !bx_cpu.eflags.zf)
    bx_cpu.eip += offset;
}

  void
bx_JCXZ_Jb()
{
  Bit8s offset;

  offset = (Bit8s) bx_fetch_next_byte();
  if ( CX == 0 )
    bx_cpu.eip += offset;
}


  void
bx_RETnear_Iw()
{
  Bit16s imm16;
  Bit32u eip;
  Bit16u ip;

  imm16 = bx_fetch_next_word();

  if (bx_32bit_opsize) {
    bx_pop_32(&eip);
    bx_cpu.eip = eip;
    ESP += imm16;
    }
  else {
    bx_pop_16(&ip);
    bx_cpu.eip = (Bit32u) ip;
    SP += imm16;
    }
}

  void
bx_RETnear()
{
  Bit32u eip;
  Bit16u ip;

  if (bx_32bit_opsize) {
    bx_pop_32(&eip);
    bx_cpu.eip = eip;
    }
  else {
    bx_pop_16(&ip);
    bx_cpu.eip = (Bit32u) ip;
    }
}

  void
bx_RETfar_Iw()
{
  Bit16s imm16;
  Bit32u eip, ecs;
  Bit16u ip, cs;

  imm16 = bx_fetch_next_word();

  if (bx_32bit_opsize) {
    bx_pop_32(&eip);
    bx_pop_32(&ecs);
    bx_cpu.eip = eip;
    bx_load_seg_reg(&bx_cpu.cs, (Bit16u) ecs);
    bx_cpu.esp += imm16;
    }
  else {
    bx_pop_16(&ip);
    bx_pop_16(&cs);
    bx_cpu.eip = (Bit32u) ip;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    SP += imm16;
    }

}

  void
bx_RETfar()
{
  Bit32u eip, ecs;
  Bit16u ip, cs;

  if (bx_32bit_opsize) {
    bx_pop_32(&eip);
    bx_pop_32(&ecs);
    bx_cpu.eip = eip;
    bx_load_seg_reg(&bx_cpu.cs, (Bit16u) ecs);
    }
  else {
    bx_pop_16(&ip);
    bx_pop_16(&cs);
    bx_cpu.eip = (Bit32u) ip;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
}

void bx_ENTER_IwIb() {bx_invalid_instruction();}
void bx_LEAVE() {bx_invalid_instruction();}

  void
bx_LOOPNE_Jb()
{
  Bit8s imm8;
  Bit32u count;

  imm8 = bx_fetch_next_byte();

  if (bx_32bit_addrsize)
    count = --bx_cpu.ecx;
  else
    count = --CX;

  if (bx_32bit_opsize) {
    if (count != 0  &&  bx_cpu.eflags.zf==0) {
      bx_cpu.eip += imm8;
      }
    }
  else {
    if (count != 0  &&  bx_cpu.eflags.zf==0) {
      IP += imm8;
      }
    }
}

  void
bx_LOOPE_Jb()
{
  Bit8s imm8;
  Bit32u count;

  imm8 = bx_fetch_next_byte();

  if (bx_32bit_addrsize)
    count = --bx_cpu.ecx;
  else
    count = --CX;

  if (bx_32bit_opsize) {
    if (count != 0  &&  bx_cpu.eflags.zf) {
      bx_cpu.eip += imm8;
      }
    }
  else {
    if (count != 0  &&  bx_cpu.eflags.zf) {
      IP += imm8;
      }
    }
}

  void
bx_LOOP_Jb()
{
  Bit8s imm8;
  Bit32u count;

  imm8 = bx_fetch_next_byte();

  if (bx_32bit_addrsize)
    count = --bx_cpu.ecx;
  else
    count = --CX;

  if (bx_32bit_opsize) {
    if (count != 0) {
      bx_cpu.eip += imm8;
      }
    }
  else {
    if (count != 0) {
      IP += imm8;
      }
    }
}

  void
bx_CALL_Av()
{
  Bit16s disp16;
  Bit32s disp32;

  if (bx_32bit_opsize) {
    disp32 = bx_fetch_next_dword();
    /* push 32 bit EA of next instruction */
    bx_push_32(bx_cpu.eip);
    bx_cpu.eip += disp32;
    }
  else { /* 16bit opsize mode */
    disp16 = bx_fetch_next_word();
    /* push 16 bit EA of next instruction */
    bx_push_16((Bit16u) bx_cpu.eip);
    bx_cpu.eip += disp16;
    }
}

  void
bx_CALL_Ap()
{
  Bit16u disp16;
  Bit32u disp32;
  Bit16u cs;


  if (bx_32bit_addrsize) {
    disp32 = bx_fetch_next_dword();
    cs     = bx_fetch_next_word();
    /* push CS */
    bx_push_16(bx_cpu.cs.selector.value);
    /* push 32 bit EA of next instruction */
    bx_push_32(bx_cpu.eip);
    bx_cpu.eip = disp32;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
  else { /* 16bit opsize mode */
    disp16 = bx_fetch_next_word();
    cs     = bx_fetch_next_word();
    /* push CS */
    bx_push_16(bx_cpu.cs.selector.value);
    /* push 16 bit EA of next instruction */
    bx_push_16((Bit16u) bx_cpu.eip);
    bx_cpu.eip = (Bit32u) disp16;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
}

  void
bx_CALL_Ev()
{
  Bit32u unused, op1_addr;
  int op1_type;
  bx_segment_reg_t *op1_seg_reg;

  /* for 32 bit operand size mode */
  Bit32u op1_32;

  /* for 16 bit operand size mode */
  Bit16u op1_16;


  bx_decode_exgx(&unused, &op1_addr, &op1_type, &op1_seg_reg);

  if (bx_32bit_opsize) { /* 32 bit operand size mode */

    bx_push_32(bx_cpu.eip);

    /* op1_32 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op1_32, op1_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op1_seg_reg, op1_addr, 4, BX_READ, &op1_32);

    bx_cpu.eip = op1_32;
    }
  else {
    bx_push_16((Bit16u) bx_cpu.eip);

    /* op1_16 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op1_16, op1_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op1_seg_reg, op1_addr, 2, BX_READ, &op1_16);

    bx_cpu.eip = op1_16;
    }
}

  void
bx_CALL_Ep()
{
  Bit32u unused, op1_addr;
  int op1_type;
  bx_segment_reg_t *op1_seg_reg;

  Bit16u cs;

  /* for 32 bit operand size mode */
  Bit32u op1_32;

  /* for 16 bit operand size mode */
  Bit16u op1_16;


  bx_decode_exgx(&unused, &op1_addr, &op1_type, &op1_seg_reg);

  if (bx_32bit_addrsize) { /* 32 bit operand size mode */

    /* op1_32 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      /* far indirect must specify a memory address */
      bx_invalid_instruction();
      }

    /* pointer, segment address pair */
    bx_access_virtual(op1_seg_reg, op1_addr, 4, BX_READ, &op1_32);
    bx_access_virtual(op1_seg_reg, op1_addr+4, 2, BX_READ, &cs);

    bx_push_16(bx_cpu.cs.selector.value);
    bx_push_32(bx_cpu.eip);

    bx_cpu.eip = op1_32;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
  else {

    /* op1_16 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      bx_invalid_instruction();
      }

    /* pointer, segment address pair */
    bx_access_virtual(op1_seg_reg, op1_addr, 2, BX_READ, &op1_16);
    bx_access_virtual(op1_seg_reg, op1_addr+2, 2, BX_READ, &cs);

    bx_push_16(bx_cpu.cs.selector.value);
    bx_push_16((Bit16u) bx_cpu.eip);

    bx_cpu.eip = op1_16;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
}


  void
bx_JMP_Jv()
{
  Bit16s imm16;
  Bit32s imm32;

  if (bx_32bit_opsize) {
    imm32 = bx_fetch_next_dword();
    bx_cpu.eip += imm32;
    }
  else { /* 16 bit operand size mode */
    imm16 = bx_fetch_next_word();
    bx_cpu.eip += imm16;
    }
}

  void
bx_JMP_Ap()
{
  Bit16u cs;
  Bit32u disp32;

  if (bx_32bit_opsize) {
    disp32 = bx_fetch_next_dword();
    }
  else {
    disp32 = bx_fetch_next_word();
    }

  cs = bx_fetch_next_word();

  bx_load_seg_reg(&bx_cpu.cs, cs);
  bx_cpu.eip = disp32;
}



  void
bx_JMP_Jb()
{
  Bit8s imm8;

  imm8 = bx_fetch_next_byte();
if (imm8 == -2) bx_printf(1, "endless loop detected!\n");
  bx_cpu.eip += imm8;
}

void bx_JO_Jv() {bx_invalid_instruction();}
void bx_JNO_Jv() {bx_invalid_instruction();}
void bx_JB_Jv() {bx_invalid_instruction();}
void bx_JNB_Jv() {bx_invalid_instruction();}
void bx_JZ_Jv() {bx_invalid_instruction();}
void bx_JNZ_Jv() {bx_invalid_instruction();}
void bx_JBE_Jv() {bx_invalid_instruction();}
void bx_JNBE_Jv() {bx_invalid_instruction();}
void bx_JS_Jv() {bx_invalid_instruction();}
void bx_JNS_Jv() {bx_invalid_instruction();}
void bx_JP_Jv() {bx_invalid_instruction();}
void bx_JNP_Jv() {bx_invalid_instruction();}
void bx_JL_Jv() {bx_invalid_instruction();}
void bx_JNL_Jv() {bx_invalid_instruction();}
void bx_JLE_Jv() {bx_invalid_instruction();}
void bx_JNLE_Jv() {bx_invalid_instruction();}



  void
bx_JMP_Ev()
{
  Bit32u unused, op1_addr;
  int op1_type;
  bx_segment_reg_t *op1_seg_reg;

  /* for 32 bit operand size mode */
  Bit32u op1_32;

  /* for 16 bit operand size mode */
  Bit16u op1_16;


  bx_decode_exgx(&unused, &op1_addr, &op1_type, &op1_seg_reg);

  if (bx_32bit_opsize) { /* 32 bit operand size mode */

    /* op1_32 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      BX_READ_32BIT_REG(op1_32, op1_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op1_seg_reg, op1_addr, 4, BX_READ, &op1_32);

    bx_cpu.eip = op1_32;
    }
  else {

    /* op1_16 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      BX_READ_16BIT_REG(op1_16, op1_addr);
      }
    else
      /* pointer, segment address pair */
      bx_access_virtual(op1_seg_reg, op1_addr, 2, BX_READ, &op1_16);

    bx_cpu.eip = op1_16;
    }
}

  /* Far indirect jump */

  void
bx_JMP_Ep()
{
  Bit32u unused, op1_addr;
  int op1_type;
  bx_segment_reg_t *op1_seg_reg;

  Bit16u cs;

  /* for 32 bit operand size mode */
  Bit32u op1_32;

  /* for 16 bit operand size mode */
  Bit16u op1_16;


  bx_decode_exgx(&unused, &op1_addr, &op1_type, &op1_seg_reg);

  if (bx_32bit_opsize) { /* 32 bit operand size mode */

    /* op1_32 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      /* far indirect must specify a memory address */
      bx_invalid_instruction();
      }

    /* pointer, segment address pair */
    bx_access_virtual(op1_seg_reg, op1_addr, 4, BX_READ, &op1_32);
    bx_access_virtual(op1_seg_reg, op1_addr+4, 2, BX_READ, &cs);

    bx_cpu.eip = op1_32;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
  else {

    /* op1_16 is a register or memory reference */
    if (op1_type == BX_REGISTER_REF) {
      bx_invalid_instruction();
      }

    /* pointer, segment address pair */
    bx_access_virtual(op1_seg_reg, op1_addr, 2, BX_READ, &op1_16);
    bx_access_virtual(op1_seg_reg, op1_addr+2, 2, BX_READ, &cs);

    bx_cpu.eip = op1_16;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    }
}

  void
bx_IRET()
{
  Bit32u eip, ecs, eflags;
  Bit16u ip, cs, flags;

  if (bx_32bit_opsize) {
    bx_pop_32(&eip);
    bx_pop_32(&ecs);
    bx_pop_32(&eflags);

    cs = (Bit16u) ecs;
    bx_load_seg_reg(&bx_cpu.cs, cs);
    bx_cpu.eip = eip;
    bx_write_eflags(eflags);
    }
  else {
    bx_pop_16(&ip);
    bx_pop_16(&cs);
    bx_pop_16(&flags);

    bx_load_seg_reg(&bx_cpu.cs, cs);
    bx_cpu.eip = (Bit32u) ip;
    bx_write_flags(flags);
    }
}
