require_relative 'helper'
require 'intel/all'

class TestIntel < MiniTest::Test
  # MachineCodeX86Test initialize-release

  def setup
    @asm = Intel::MachineCodeX86.new
    @stream = asm.stream
  end

  attr_reader :asm, :stream

  def ttest_wtf_inline! #Fiddle disabled
    assert_equal 1000, X.new.counter
  end

  def ttest_wtf1? #Fiddle disabled
    assert_equal 42, add_to_n(41)
  end

  def ttest_wtf2? #Fiddle disabled
    assert_equal 42, passthrough(42)
  end

  # MachineCodeX86Test testing ow/od

  def test_mov_offset_eax
    256.m.mov asm.eax
    assert_equal [0xA3, 0, 1, 0, 0], stream
  end

  def test_mov_eax_offset
    asm.eax.mov 256.m
    assert_equal [0xA1, 0, 1, 0, 0], stream
  end

  # MachineCodeX86Test testing reg<32, mem

  def test_mov_al_big_offset
    asm.al.mov 256.m
    assert_equal [0xA0, 0, 1, 0, 0], stream
  end

  def test_mov_cl_m_edx
    asm.cl.mov asm.edx.m
    assert_equal [0x8A, 0b00001010], stream
  end

  def test_mov_ax_m_ecx
    asm.ax.mov asm.ecx.m
    assert_equal [0x66, 0x8B, 1], stream
  end

  def test_mov_cl_offset
    asm.cl.mov 1.m
    assert_equal [0x8A, 0x0D, 1, 0, 0, 0], stream
  end

  def test_mov_ax_big_offset
    asm.ax.mov 256.m
    assert_equal [0x66, 0xA1, 0, 1, 0, 0], stream
  end

  def test_mov_al_offset
    asm.al.mov 1.m
    assert_equal [0xA0, 1, 0, 0, 0], stream
  end

  def test_mov_cx_offset
    asm.cx.mov 1.m
    assert_equal [0x66, 0x8B, 0x0D, 1, 0, 0, 0], stream
  end

  def test_mov_cx_m_edx
    asm.cx.mov asm.edx.m
    assert_equal [0x66, 0x8B, 0b00001010], stream
  end

  def test_mov_ax_offset
    asm.ax.mov 1.m
    assert_equal [0x66, 0xA1, 1, 0, 0, 0], stream
  end

  def test_mov_al_m_ecx
    asm.al.mov asm.ecx.m
    assert_equal [0x8A, 1], stream
  end

  # MachineCodeX86Test testing /r reg/reg

  def test_mov_eax_ebx
    asm.eax.mov asm.ebx
    assert_equal [0x89, 0b11011000], stream
  end

  def test_mov_ebx_ecx
    asm.ebx.mov asm.ecx
    assert_equal [0x89, 0b11001011], stream
  end

  # MachineCodeX86Test testing /n reg

  def test_bt_ecx_literal
    asm.ecx.bt 1
    assert_equal [0x0F, 0xBA, 0b11100001, 1], stream
  end

  def test_fdivr
    asm.ecx.m.fdivr
    assert_equal [0xD8, 0b00000001], stream
  end

  # MachineCodeX86Test testing []

  def test_bracket_argument
    asm.eax.mov { asm.ecx }
    assert_equal [0x8B, 1], stream
  end

#   def test_bracket_receiver # HACK
#     asm.assemble { [asm.ecx].mov asm.eax }
#     assert_equal [0x89, 1], stream
#   end

  # MachineCodeX86Test testing ib/iw/id

  def test_add_eax_literal
    asm.eax.add 256
    assert_equal [5, 0, 1, 0, 0], stream
  end

  def test_add_ax_literal
    asm.ax.add 256
    assert_equal [0x66, 5, 0, 1], stream
  end

  def test_add_al_literal
    asm.al.add 1
    assert_equal [4, 1], stream
  end

  # MachineCodeX86Test testing rb/rw/rd

  def test_label_jmp_veryFar
    label = asm.label
    65536.times { asm.stream << 0x90 } # cheaters way to do nop, so it runs faster
    label.jmp
    assert_equal 65536 + 5, stream.size
    assert_equal [0xE9, 0xFB, 0xFF, 0xFE, 0xFF], stream[65536..-1]
  end

  def test_jmp_big_offset
    asm.jmp 256.m
    assert_equal [0xFF, 0b00100101, 0, 1, 0, 0], stream
  end

  def test_jmp_m_ecx
    asm.jmp asm.ecx.m
    assert_equal [0xFF, 0b00100001], stream
  end

  def test_jmp_infinite
    label = asm.label
    label.jmp
    assert_equal [0xEB, 0xFE], stream
  end

  def test_label_jmp_far
    asm.bits = 16
    label = asm.label
    256.times { stream << 0x90 } # cheaters way to do nop, so it runs faster
    label.jmp
    assert_equal 256 + 3, stream.size
    assert_equal [0xE9, 0xFD, 0xFE], stream[256..-1]
  end

  def test_jmp_forward_and_backward
    label = asm.future_label
    label.jmp
    asm.nop
    label.plant
    label.jmp
    assert_equal [0xE9, 1, 0, 0, 0, 0x90, 0xEB, 0xFE], stream
  end

  def test_jmp_forward
    label = asm.future_label
    label.jmp
    asm.nop
    label.plant
    assert_equal [0xE9, 1, 0, 0, 0, 0x90], stream
  end

  def test_jmp_offset
    asm.jmp 1.m
    assert_equal [0xFF, 0b00100101, 1, 0, 0, 0], stream
  end

  def test_jmp_far
    asm.jmp 256
    assert_equal [0xE9, 0xFB, 0, 0, 0], stream
  end

  def test_loop_infinite
    label = asm.label
    label.loop
    assert_equal [0xE2, 0xFE], stream
  end

  # MachineCodeX86Test testing o16/o32

  def test_adc_ax_literal
    asm.bits = 32
    asm.ax.add 1
    assert_equal [0x66, 5, 1, 0], stream
  end

  def test_adc_ax_literal16
    asm.bits = 32
    asm.ax.add 256
    assert_equal [0x66, 5, 0, 1], stream
  end

  def test_adc_eax_literal
    asm.bits = 16
    asm.eax.add 1
    assert_equal [0x67, 0x83, 0b11000000, 1], stream
  end

  def test_adc_eax_literal16
    asm.bits = 16
    asm.eax.add 256
    assert_equal [0x67, 5, 0, 1, 0, 0], stream
  end

  # MachineCodeX86Test testing cpuregs

  def test_mov_ecx_cr0
    asm.ecx.mov asm.cr0
    assert_equal [0x0f, 0x20, 0b11000001], stream
  end

  def test_mov_dr0Ecx
    asm.dr0.mov asm.ecx
    assert_equal [0x0f, 0x23, 0b11000001], stream
  end

  def test_mov_ecx_tr3
    asm.ecx.mov asm.tr3
    assert_equal [0x0f, 0x24, 0b11011001], stream
  end

  def test_mov_tr3_ecx
    asm.tr3.mov asm.ecx
    assert_equal [0x0f, 0x26, 0b11011001], stream
  end

  def test_mov_ecx_dr0
    asm.ecx.mov asm.dr0
    assert_equal [0x0f, 0x21, 0b11000001], stream
  end

  def test_mov_cr0_ecx
    asm.cr0.mov asm.ecx
    assert_equal [0x0f, 0x22, 0b11000001], stream
  end

  # MachineCodeX86Test testing /r reg/mem

  def test_mov_eax_m_ecx
    asm.eax.mov asm.ecx.m
    assert_equal [0x8B, 0b00000001], stream
  end

  def test_mov_ebx_m_ecx_edx_offset
    asm.ebx.mov(asm.ecx + asm.edx + 1)
    assert_equal [0x8B, 0b00011100, 0b01010001, 1], stream
  end

  def test_mov_ebx_m_ecx_edx
    asm.ebx.mov(asm.ecx + asm.edx)
    assert_equal [0x8B, 0b00011100, 0b00010001], stream
  end

  def test_mov_ebx_m_ecx_edx_big_offset
    asm.ebx.mov(asm.ecx + asm.edx + 256)
    assert_equal [0x8B, 0b00011100, 0b10010001, 0, 1, 0, 0], stream
  end

  def test_mov_eax_m_ecx_big_offset
    asm.eax.mov asm.ecx + 256
    assert_equal [0x8B, 0b10000001, 0, 1, 0, 0], stream
  end

  def test_mov_ecx_m_offset
    asm.ecx.mov 256.m
    assert_equal [0x8B, 0x0D, 0, 1, 0, 0], stream
  end

  def test_mov_eax_m_ecx_offset
    asm.eax.mov asm.ecx + 1
    assert_equal [0x8B, 0b01000001, 1], stream
  end

  # MachineCodeX86Test testing imm:imm

  def test_jmp_imm_Imm32
    asm.jmp 256, 65537
    assert_equal [0xEA, 1, 0, 1, 0, 0, 1], stream
  end

  def test_call_imm_Imm16
    asm.call 1, 2
    assert_equal [0x66, 0x9A, 2, 0, 1, 0], stream
  end

  def test_jmp_imm_Imm16
    asm.jmp 1, 2
    assert_equal [0x66, 0xEA, 2, 0, 1, 0], stream
  end

  def test_call_imm_Imm32
    asm.call 256, 65537
    assert_equal [0x9A, 1, 0, 1, 0, 0, 1], stream
  end

  # MachineCodeX86Test testing 0 args

  def test_ret
    asm.ret
    assert_equal [0xC3], stream
  end

  def test_nop
    asm.nop
    assert_equal [0x90], stream
  end

  def test_hlt
    asm.hlt
    assert_equal [0xF4], stream
  end

  # MachineCodeX86Test testing 3 args

  def test_imul_ecx_edx_immediate
    asm.ecx.imul asm.edx, 1
    assert_equal [0x6B, 0xCA, 1], stream
  end

  def test_imul_ecx_edx_big_immediate
    asm.ecx.imul asm.edx, 256
    assert_equal [0x69, 0xCA, 0, 1, 0, 0], stream
  end

  # MachineCodeX86Test testing fpureg

  def test_fadd_st0St1
    asm.st0.fadd asm.st1
    assert_equal [0xD8, 0xC1], stream
  end

  def test_fmulp_st1St0
    asm.st1.fmulp asm.st0
    assert_equal [0xDE, 0xC9], stream
  end

  def test_fadd_st0
    asm.st0.fadd
    assert_equal [0xD8, 0xC0], stream
  end

  def test_fadd_st1
    asm.st1.fadd
    assert_equal [0xD8, 0xC1], stream
  end

  # MachineCodeX86Test testing /n mem

  def test_fild_m_ecx_edx_offset
    (asm.ecx + asm.edx + 1).fild
    assert_equal [0xDB, 0b00000100, 0b01010001, 1], stream
  end

  def test_bt_m_ecx_literal
    # ... this shouldn't work. The machine code generated here does
    # not work and nasm complains that a size was not specified if you
    # try to write the same code in nasm -> mov [ecx], 1
    #
    # Some how we're meant to know this is invalid and throw an
    # error.. not sure how yet.
    asm.ecx.m.bt 1
    assert_equal [0x0F, 0xBA, 0b00100001, 1], stream
  end

  def test_fild_m_ecx_big_offset
    (asm.ecx + 256).fild
    assert_equal [0xDB, 0b10000001, 0, 1, 0, 0], stream
  end

  def test_fild_m_ecx_edx
    (asm.ecx + asm.edx).fild
    assert_equal [0xDB, 0b00000100, 0b00010001], stream
  end

  def test_fild_m_ecx_edx_big_offset
    (asm.ecx + asm.edx + 256).fild
    assert_equal [0xDB, 0b00000100, 0b10010001, 0, 1, 0, 0], stream
  end

  def test_fild_m_ecx
    asm.ecx.m.fild
    assert_equal [0xDB, 0b00000001], stream
  end

  def test_fild_m_ecx_offset
    (asm.ecx + 1).fild
    assert_equal [0xDB, 0b01000001, 1], stream
  end

  # MachineCodeX86Test testing processors

#   def testing_adc_edx_ecx # HACK
#     asm.processors.reject! { |processor| processor == '386' }
#     assert_raise NoMethodError do
#       asm.edx.adc asm.ecx
#     end
#   end

  # MachineCodeX86Test testing mmxreg

  def test_psllw_mm1Immediate
    asm.mm1.psllw 1
    assert_equal [0x0F, 0x71, 0xF1, 1], stream
  end

  def test_movd_eax_mm0
    asm.eax.movd asm.mm0
    assert_equal [0x0F, 0x7E, 0xC0], stream
  end

  def test_movd_mm0Eax
    asm.mm0.movd asm.eax
    assert_equal [0x0F, 0x6E, 0xC0], stream
  end

  # MachineCodeX86Test testing +r

  def test_mov_ecx_literal
    asm.ecx.mov 1
    assert_equal [0xB9, 1, 0, 0, 0], stream
  end

  def test_mov_edx_literal
    asm.edx.mov 1
    assert_equal [0xBA, 1, 0, 0, 0], stream
  end

  def test_mov_eax_literal
    asm.eax.mov 1
    assert_equal [0xB8, 1, 0, 0, 0], stream
  end

  def test_mov_ebx_literal
    asm.ebx.mov 1
    assert_equal [0xBB, 1, 0, 0, 0], stream
  end

  # MachineCodeX86Test testing /r mem/reg

  def test_mov_m_ecx_edx_offset_ebx
    (asm.ecx + asm.edx + 1).mov asm.ebx
    assert_equal [0x89, 0b00011100, 0b01010001, 1], stream
  end

  def test_mov_m_eax_ecx
    asm.eax.m.mov asm.ecx
    assert_equal [0x89, 0b00001000], stream
  end

  def test_mov_m_ecx_big_offset_ebx
    (asm.ecx + 256).mov asm.ebx
    assert_equal [0x89, 0b10011001, 0, 1, 0, 0], stream
  end

  def test_mov_m_ecx_edx_big_offset_ebx
    (asm.ecx + asm.edx + 256).mov asm.ebx
    assert_equal [0x89, 0b00011100, 0b10010001, 0, 1, 0, 0], stream
  end

  def test_mov_m_ecx_offset_ebx
    (asm.ecx + 1).mov asm.ebx
    assert_equal [0x89, 0b01011001, 1], stream
  end

  def test_mov_m_ecx_eax
    asm.ecx.m.mov asm.eax
    assert_equal [0x89, 0b00000001], stream
  end

  def test_mov_m_ecx_edx_ebx
    (asm.ecx + asm.edx).mov asm.ebx
    assert_equal [0x89, 0b00011100, 0b00010001], stream
  end

  # MachineCodeX86Test testing +cc

  def test_cmovne_eax_ecx
    asm.eax.cmovne asm.ecx
    assert_equal [0x0F, 0x45, 0xC1], stream
  end

  def test_cmove_eax_ecx
    asm.eax.cmove asm.ecx
    assert_equal [0x0F, 0x44, 0xC1], stream
  end

  def test_jnb
    asm.label.jnb
    assert_equal [0x73, 0xFE], stream
  end

  def test_jnc
    asm.label.jnc
    assert_equal [0x73, 0xFE], stream
  end

  def test_jno
    asm.label.jno
    assert_equal [0x71, 0xFE], stream
  end

  def test_jae
    asm.label.jae
    assert_equal [0x73, 0xFE], stream
  end

  def test_jnae
    asm.label.jnae
    assert_equal [0x72, 0xFE], stream
  end

  def test_cmovz_eax_ecx
    asm.eax.cmovz asm.ecx
    assert_equal [0x0F, 0x44, 0xC1], stream
  end

  def test_jb
    asm.label.jb
    assert_equal [0x72, 0xFE], stream
  end

  def test_jc
    asm.label.jc
    assert_equal [0x72, 0xFE], stream
  end

  def test_cmovnz_eax_ecx
    asm.eax.cmovnz asm.ecx
    assert_equal [0x0F, 0x45, 0xC1], stream
  end

  def test_jo
    asm.label.jo
    assert_equal [0x70, 0xFE], stream
  end

  # MachineCodeX86Test testing reg,1/cl

  def test_rcr_eax1
    asm.eax.rcr 1
    assert_equal [0xD1, 0xD8], stream
  end

  def test_rcr_eaxCl
    asm.eax.rcr asm.cl
    assert_equal [0xD3, 0xD8], stream
  end

  # MachineCodeX86Test testing segreg

  def test_mov_fs_m_eax
    asm.fs.mov asm.eax.m
    assert_equal [0x8E, 0x20], stream
  end

  def test_mov_eaxFs
    asm.eax.mov asm.fs
    assert_equal [0x8C, 0xE0], stream
  end

  def test_mov_m_eaxFs
    asm.eax.m.mov asm.fs
    assert_equal [0x8C, 0x20], stream
  end

  def test_mov_fs_eax
    asm.fs.mov asm.eax
    assert_equal [0x8E, 0xE0], stream
  end
end