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

void sign_extend_if_required(const Operand &dest, Operand &src) {
  if(dest.width > src.width) {
    rtl_sext(&src.val, &src.val, src.width);
    src.width = dest.width;
  }
}

namespace EHelperImpl {
  make_EHelper(test) {
    // `and` without write_back.
    sign_extend_if_required(*id_dest, *id_src);
    rtl_and(&t1, &id_dest->val, &id_src->val);
  
    rtl_update_ZFSF(&t1, id_dest->width);
    cpu_eflags::get<cpu_eflags::CF>() = cpu_eflags::get<cpu_eflags::OF>() = false;
  
    print_asm_template2(test);
  }
  
  make_EHelper(and_) {
    // rlib::printfln("debug: before test, id_dest={}, src={}", *id_dest, *id_src);
    sign_extend_if_required(*id_dest, *id_src);
    // rlib::printfln("debug: after test, id_dest={}, src={}", *id_dest, *id_src);
  
    rtl_and(&id_dest->val, &id_dest->val, &id_src->val);
    operand_write(id_dest, &id_dest->val);
  
    rtl_update_ZFSF(&id_dest->val, id_dest->width);
    cpu_eflags::get<cpu_eflags::CF>() = cpu_eflags::get<cpu_eflags::OF>() = false;
  
    print_asm_template2(and);
  }
  
  make_EHelper(xor_) {
    sign_extend_if_required(*id_dest, *id_src);
  
    rtl_xor(&id_dest->val, &id_dest->val, &id_src->val);
    operand_write(id_dest, &id_dest->val);
  
    rtl_update_ZFSF(&id_dest->val, id_dest->width);
    cpu_eflags::get<cpu_eflags::CF>() = cpu_eflags::get<cpu_eflags::OF>() = false;
  
    print_asm_template2(xor);
  }
  
  make_EHelper(or_) {
    sign_extend_if_required(*id_dest, *id_src);
  
    rtl_or(&id_dest->val, &id_dest->val, &id_src->val);
    operand_write(id_dest, &id_dest->val);
  
    rtl_update_ZFSF(&id_dest->val, id_dest->width);
    cpu_eflags::get<cpu_eflags::CF>() = cpu_eflags::get<cpu_eflags::OF>() = false;
  
    print_asm_template2(or);
  }
  
  make_EHelper(sar) {
    // r: dbt
    // warning: unnecessary to update CF and OF in NEMU
    if (id_dest->width == 1) {
    	id_dest->val = (int8_t)id_dest->val;
    }
    else if (id_dest->width == 2) {
    	id_dest->val = (int16_t)id_dest->val;
    }
    rtl_sar(&t2, &id_dest->val, &id_src->val);
    operand_write(id_dest, &t2);
    rtl_update_ZFSF(&t2, id_dest->width);
  
    print_asm_template2(sar);
  }
  
  make_EHelper(shl) {
    // r: dbt
    // unnecessary to update CF and OF in NEMU
    rtl_shl(&t2, &id_dest->val, &id_src->val);
    operand_write(id_dest, &t2);
    rtl_update_ZFSF(&t2, id_dest->width);  

    print_asm_template2(shl);
  }
  
  make_EHelper(shr) {
    // r: dbt
    // unnecessary to update CF and OF in NEMU
    rtl_shr(&t2, &id_dest->val, &id_src->val);
    operand_write(id_dest, &t2);
    rtl_update_ZFSF(&t2, id_dest->width);
  
    print_asm_template2(shr);
  }
  
  make_EHelper(setcc) {
    uint32_t cc = decoding.opcode & 0xf;
  
    rtl_setcc(&t2, cc);
    operand_write(id_dest, &t2);
  
    print_asm("set%s %s", get_cc_name(cc), id_dest->str);
  }
  
  make_EHelper(not_) {
    rtl_not(&id_dest->val, &id_dest->val);
    operand_write(id_dest, &id_dest->val);
  
    print_asm_template1(not);
  }
  
  make_EHelper(rol) {
    // r: dbt
	  rtl_shl(&t0, &id_dest->val, &id_src->val);
	  if (decoding.is_operand_size_16) {
	  	t3 = 0;
	  	rtl_addi(&t1, &t3, 16);
	  	rtl_sub(&t1, &t1, &id_src->val);
	  	rtl_shr(&t2, &id_dest->val, &t1);
	  } else {
	  	t3 = 0;
	  	rtl_addi(&t1, &t3, 32);
	  	rtl_sub(&t1, &t1, &id_src->val);
	  	rtl_shr(&t2, &id_dest->val, &t1);
	  }
	  rtl_or(&t0, &t0, &t2);
	  operand_write(id_dest, &t0);

	  // unnecessary to update CF and OF in NEMU
	  rtl_update_ZFSF(&t0, id_dest->width);
  
    print_asm_template1(rol);
  }

} // end namespace ehelperimpl
