#include "cpu/exec.h"

namespace EHelperImpl {
  make_EHelper(mov) {
    operand_write(id_dest, &id_src->val);
    print_asm_template2(mov);
  }
  
  make_EHelper(push) {
    static_assert(sizeof(paddr_t) * 8 == 32);
    if(decoding.is_operand_size_16) {
      // 16b push
      rtl_push<2>(&id_dest->val);
    }
    else {
      // 32b push
      rtl_push<4>(&id_dest->val);
    }
  
    print_asm_template1(push);
  }
  
  make_EHelper(pop) {
    static_assert(sizeof(paddr_t) * 8 == 32);
    if(decoding.is_operand_size_16) {
      // 16b
      rtl_pop<2>(&id_dest->val);
    }
    else {
      // 32b
      rtl_pop<4>(&id_dest->val);
    }
    operand_write(id_dest, &id_dest->val);
  
    print_asm_template1(pop);
  }
  
  make_EHelper(pusha) {
    TODO();
  
    print_asm("pusha");
  }
  
  make_EHelper(popa) {
    TODO();
  
    print_asm("popa");
  }
  
  make_EHelper(leave) {
    rtl_mv(&cpu.esp, &cpu.ebp);
    if(decoding.is_operand_size_16) {
      rtlreg_t tmpReg;
      rtl_pop<2>(&tmpReg);
      reg_w(R_BP) = (uint16_t)tmpReg;
    }
    else {
      rtl_pop<4>(&cpu.ebp);
    }
  
    print_asm("leave");
  }
  
  make_EHelper(cltd) {
    // r: dbt
    if (decoding.is_operand_size_16) {
      rtl_sext(&t0, &reg_l(R_EAX), 2);
      rtl_shri(&reg_l(R_EDX), &t0, 16);
    }
    else {
      rtl_sari(&reg_l(R_EDX), &reg_l(R_EAX), 31);
    }
  
    print_asm(decoding.is_operand_size_16 ? "cwtl" : "cltd");
  }
  
  make_EHelper(cwtl) {
    if (decoding.is_operand_size_16) {
      TODO();
    }
    else {
      TODO();
    }
  
    print_asm(decoding.is_operand_size_16 ? "cbtw" : "cwtl");
  }
  
  make_EHelper(movsx) {
    id_dest->width = decoding.is_operand_size_16 ? 2 : 4;
    rtl_sext(&t0, &id_src->val, id_src->width);
    operand_write(id_dest, &t0);
    print_asm_template2(movsx);
  }
  
  make_EHelper(movzx) {
    id_dest->width = decoding.is_operand_size_16 ? 2 : 4;
    operand_write(id_dest, &id_src->val);
    print_asm_template2(movzx);
  }
  
  make_EHelper(lea) {
    operand_write(id_dest, &id_src->addr);
    print_asm_template2(lea);
  }

  make_EHelper(movs) {
    // movsb, movsw, movsd

    // address size is always 32bit.
    const auto source_index = cpu.esi, dest_index = cpu.edi;
    
    auto copy_bytes = 4;
    if(decoding.opcode == 0xa4) {
      // movsb
      copy_bytes = 1;
    }
    else if(decoding.is_operand_size_16) {
      // movsw
      // r: dbt
      copy_bytes = 2;
    }
    else {
      // movsd
    }
    vaddr_write(dest_index, vaddr_read(source_index, 4), copy_bytes);

    auto index_inc = copy_bytes * ( cpu_eflags::get<cpu_eflags::DF>() ? (-1) : 1 );
    cpu.esi += index_inc;
    cpu.edi += index_inc;
    
    print_asm_template2(movs);
  }
} // end namespace
