#include "cpu/exec.h"
#include "cpu/cc.h"

namespace EHelperImpl {
  make_EHelper(jmp) {
    // the target address is calculated at the decode stage
    rtl_j(decoding.jmp_eip);
  
    print_asm("jmp %x", decoding.jmp_eip);
  }
  
  make_EHelper(jcc) {
    // the target address is calculated at the decode stage
    uint32_t cc = decoding.opcode & 0xf;
    rtl_setcc(&t0, cc);
    rtl_li(&t1, 0);
    rtl_jrelop(RELOP_NE, &t0, &t1, decoding.jmp_eip);
  
    print_asm("j%s %x", get_cc_name(cc), decoding.jmp_eip);
  }
  
  make_EHelper(jmp_rm) {
    rtl_jr(&id_dest->val);
  
    print_asm("jmp *%s", id_dest->str);
  }
  
  make_EHelper(call) {
    // the target address is calculated at the decode stage
    const bool near = true;
    if(near) {
      if(decoding.is_operand_size_16) {
        throw std::runtime_error("call operand size 16 not implemented.");
      }
      else {
        // operand size 32b
        rtl_push<4>(&decoding.seq_eip);
        rtl_j(decoding.jmp_eip);
      }
      decoding_set_jmp(true);
    }
    // TODO: support far call
    // TODO();
  
    print_asm("call %x", decoding.jmp_eip);
  }
  
  make_EHelper(ret) {
    const bool near = true;
    if(near) {
      if(decoding.is_operand_size_16) {
        throw std::runtime_error("call operand size 16 not implemented.");
      }
      else {
        // operand size 32b
        rtl_pop<4>(&cpu.eip);
      }
      decoding_set_jmp(true);
    }
  
   // TODO: support far ret
    // TODO();
  
    print_asm("ret");
  }
  
  make_EHelper(call_rm) {
    RLIB_MACRO_DEBUG_ASSERT(decoding.is_operand_size_16 == false);
    // rlib::println("Reach call_rm. WARNING: this instruction impl is heavily broken.");

    rtl_push<4>(&decoding.seq_eip);
    rtl_jr(&id_dest->val);
    // TODO: this far call logic is absolutely wrong.
  
    print_asm("call *%s", id_dest->str);
  }
} // end namespace
