#    This file is part of Metasm, the Ruby assembly manipulation suite
#    Copyright (C) 2006-2009 Yoann GUILLOT
#
#    Licence is LGPL, see LICENCE in the top-level directory


require 'metasm/cpu/x86_64/opcodes'
require 'metasm/cpu/x86_64/encode'
require 'metasm/parse'

module Metasm
class X86_64
	def parse_parser_instruction(lexer, instr)
		case instr.raw.downcase
		when '.mode', '.bits'
			if tok = lexer.readtok and tok.type == :string and tok.raw == '64'
				lexer.skip_space
				raise instr, 'syntax error' if ntok = lexer.nexttok and ntok.type != :eol
			else
				raise instr, 'invalid cpu mode, 64bit only'
			end
		else super(lexer, instr)
		end
	end

	def parse_prefix(i, pfx)
		super(i, pfx) or (i.prefix[:sz] = 64 if pfx == 'code64')
	end

	# needed due to how ruby inheritance works wrt constants
	def parse_argregclasslist
		[Reg, SimdReg, SegReg, DbgReg, TstReg, CtrlReg, FpReg]
	end
	# same inheritance sh*t
	def parse_modrm(lex, tok, cpu)
		ModRM.parse(lex, tok, cpu)
	end

	def parse_instruction_checkproto(i)
		# check ah vs rex prefix
		return if i.args.find { |a| a.kind_of? Reg and a.sz == 8 and a.val >= 16 and
				op = opcode_list.find { |op_| op_.name == i.opname } and
				((not op.props[:auto64] and i.args.find { |aa| aa.respond_to? :sz and aa.sz == 64 }) or
				 i.args.find { |aa| aa.kind_of? Reg and aa.val >= 8 and aa.val < 16 } or	# XXX mov ah, cr12...
				 i.args.grep(ModRM).find { |aa| (aa.b and aa.b.val >= 8 and aa.b.val < 16) or (aa.i and aa.i.val >= 8 and aa.i.val < 16) })
			}
		super(i)
	end

	# check if the argument matches the opcode's argument spec
	def parse_arg_valid?(o, spec, arg)
		return if arg.kind_of? ModRM and ((arg.b and arg.b.val == 16 and arg.i) or (arg.i and arg.i.val == 16 and (arg.b or arg.s != 1)))	# cannot encode [rip+base], only [rip+imm]
		return if arg.kind_of? Reg and arg.sz >= 32 and arg.val == 16	# eip/rip only in modrm
		return if o.props[:auto64] and arg.respond_to? :sz and arg.sz == 32
		# vex c4/c5
		return if o.fields[:vex_r] and not o.fields[:vex_b] and (spec == :modrm or spec == :modrmxmm or spec == :modrmymm) and (((arg.kind_of?(SimdReg) or arg.kind_of?(Reg)) and arg.val >= 8) or (arg.kind_of?(ModRM) and ((arg.b and arg.b.val >= 8) or (arg.i and arg.i.val >= 8))))
		if o.name == 'movsxd'
			return if not arg.kind_of? Reg and not arg.kind_of? ModRM
			arg.sz ||= 32
			if spec == :reg
				return if not arg.kind_of? Reg
				return arg.sz >= 32
			else
				return arg.sz == 32
			end
		end
		return if o.name == 'xchg' and spec == :reg and o.args.include?(:reg_eax) and arg.kind_of?(Reg) and arg.sz == 32 and arg.val == 0

		super(o, spec, arg)
	end

	def check_reserved_name(name)
		Reg.s_to_i[name]
	end
end
end
