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

require 'metasm/cpu/arc/opcodes'
require 'metasm/decode'

module Metasm
class ARC
	def major_opcode(val, sz = 16)
		return val >> (sz == 16 ? 0xB : 0x1B)
	end

	def sub_opcode(val)
		return ((val >> 16) & 0x3f)
	end

	def build_opcode_bin_mask(op, sz)
		op.bin_mask = 0
		op.args.each { |f| op.bin_mask |= @fields_mask[f] << @fields_shift[f]}
		op.bin_mask = ((1 << sz)-1) ^ op.bin_mask
	end

	def build_bin_lookaside
		bin_lookaside = {}
		opcode_list.each{|mode,oplist|
			lookaside = {}
			# 2nd level to speed up lookaside for major 5
			lookaside[5] = {}
			oplist.each { |op|
				next if not op.bin.kind_of? Integer
				build_opcode_bin_mask(op, mode)
				mj = major_opcode(op.bin, mode)
				if mode == 32 and mj == 5
					(lookaside[mj][sub_opcode(op.bin)] ||= []) << op
				else
					(lookaside[mj] ||= []) << op
				end
			}
			bin_lookaside[mode] = lookaside
		}
		bin_lookaside
	end

	def instruction_size(edata)
		val = major_opcode(edata.decode_imm(:u16, @endianness))
		edata.ptr -= 2
		(val >= 0xC) ? 16 : 32
	end

	def memref_size(di)
		case di.opcode.name
		when 'ldb_s', 'stb_s', 'extb_s', 'sexb_s'; 1
		when 'ldw_s', 'stw_s', 'extw_s', 'sexw_s'; 2
		else 4
		end
	end

	def decode_bin(edata, sz)
		case sz
		when 16; edata.decode_imm(:u16, @endianness)
		when 32
			# wordswap
			val = edata.decode_imm(:u32, :little)
			((val >> 16) & 0xffff) | ((val & 0xffff) << 16)
		end
	end

	def decode_findopcode(edata)
		di = DecodedInstruction.new(self)

		@instrlength = instruction_size(edata)
		val = decode_bin(edata, @instrlength)
		edata.ptr -= @instrlength/8

		maj = major_opcode(val, @instrlength)
		lookaside = @bin_lookaside[@instrlength][maj]
		lookaside = lookaside[sub_opcode(val)] if @instrlength == 32 and maj == 5

		op = lookaside.select { |opcode|
			if false and (val & opcode.bin_mask) == opcode.bin
				puts "#{opcode.bin_mask.to_s(16)} - #{opcode.bin.to_s(16)} - #{(val & opcode.bin_mask).to_s(16)} -  #{opcode.name} - #{opcode.args}"
			end
			(val & opcode.bin_mask) == opcode.bin
		}

		if op.size == 2 and op.first.name == 'mov' and op.last.name == 'nop'
			op = op.last
		elsif op == nil or op.size != 1
			puts "[> I sense a disturbance in the force <]"
			op.to_a.each { |opcode| puts "#{opcode.name} - #{opcode.args} - #{Expression[opcode.bin]} - #{Expression[opcode.bin_mask]}" }
			puts "current value: #{Expression[val]}"
			puts "current value: 0b#{val.to_s(2)}"
			op = nil
		else
			op = op.first
		end

		di if di.opcode = op
	end

	Reduced_reg = [0, 1, 2, 3, 12, 13, 14, 15]
	def reduced_reg_set(i)
		Reduced_reg[i]
	end

	def decode_instr_op(edata, di)
		before_ptr = edata.ptr
		op = di.opcode
		di.instruction.opname = op.name
		val = decode_bin(edata, @instrlength)

		field_val = lambda { |f|
			r = (val >> @fields_shift[f]) & @fields_mask[f]
			case f

			# 16-bits instruction operands ------------------------------------------"
			when :ca, :cb, :cb2, :cb3, :cc;  r = reduced_reg_set(r)
			when :ch
				r = (((r & 7) << 3) | (r >> 5))
			when :@cbu7, :@cbu6, :@cbu5
				r = r & 0b11111
				r = (f == :@cbu7) ? r << 2 : ( (f == :@cbu6) ? r << 1 : r)
			when :cu5ee; r = r << 2
			when :cdisps13
				r = (Expression.make_signed(r,11) << 2) + ((di.address >> 2) << 2)
			when :cdisps10
				r = (Expression.make_signed(r, 9) << 1) + ((di.address >> 2) << 2)
			when :cdisps8
				r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)
			when :cdisps7
				r = (Expression.make_signed(r, 6) << 1) + ((di.address >> 2) << 2)
			when :cs9, :cs10, :cs11
				r = Expression.make_signed(r, ((f== :cs11 ? 11 : (f == :cs10 ? 10 : 9) )))
				r = (f == :cs11) ? r << 2 : ((f == :cs10) ? r << 1 : r)
			when :@cspu7;
				r = r << 2

			# 32-bits instruction operands ------------------------------------------"
			when :b
				r = (r >> 12) | ((r & 0x7) << 3)
			when :s8e
				r = ((r & 0x1) << 7) | (r >> 2)
				r = (Expression.make_signed(r, 8) << 1) + ((di.address >> 2) << 2)

			when :u6e
				r = (r << 1) + ((di.address >> 2) << 2)
			when :s9
				r = (Expression.make_signed(r, 7) << 1) + ((di.address >> 2) << 2)

			when :s12
				r = (r >> 6) | ((r & 0x3f) << 6)
				r = Expression.make_signed(r, 12)

			when :s12e
				r = (r >> 6) | ((r & 0x3f) << 6)
				r = (Expression.make_signed(r, 12) <<1 ) + ((di.address >> 2) << 2)

			when :s21e
				r = ((r & 0x3ff) << 10) | (r >> 11)
				r = (Expression.make_signed(r, 20) << 1) + ((di.address >> 2) << 2)

			when :s21ee # pc-relative
				r = ((r & 0x3ff) << 9) | (r >> 12)
				r = (Expression.make_signed(r, 19) << 2) + ((di.address >> 2) << 2)

			when :s25e # pc-relative
				r = ((r & 0xf) << 20)  | (((r >> 6) & 0x3ff) << 10) | (r >> 17)
				r = (Expression.make_signed(r, 24) << 1) + ((di.address >> 2) << 2)

			when :s25ee # pc-relative
				r = ((r & 0xf) << 19)  | (((r >> 6) & 0x3ff) << 9) | (r >> 18)
				r = (Expression.make_signed(r, 23) << 2) + ((di.address >> 2) << 2)

			when :@bs9
				r = r >> 3
				s9 = ((r & 1) << 8) | ((r >> 1) & 0xff)
				r = Expression.make_signed(s9, 9)

			when :bext, :cext, :@cext
				if ((r = field_val[(f == :bext) ? :b : :c]) == 0x3E)
					tmp = edata.decode_imm(:u32, :little)
					r = Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
				else
					r = GPR.new(r)
				end

			else r
			end
			r
		}

		# decode properties fields
		op.args.each { |a|
			case a
			when :flags15, :flags16
				di.instruction.opname += '.f' if field_val[a] != 0
			when :ccond
				di.instruction.opname += ('.' + @cond_suffix[field_val[a]]) if field_val[a] != 0
			when :delay5, :delay16
				di.instruction.opname += '.d' if field_val[a] != 0
			when :cache5, :cache11, :cache16
				di.instruction.opname +='.di' if field_val[a] != 0
			when :signext6, :signext16
				di.instruction.opname += '.x' if field_val[a] != 0
			when :wb3, :wb9, :wb22
				case field_val[a]
				when 1; di.instruction.opname += ((memref_size(di) == 2) ? '.ab' : '.a')
				when 2; di.instruction.opname += '.ab'
				when 3; di.instruction.opname += '.as'
				end
			when :sz1, :sz7, :sz16, :sz17
				case field_val[a]
				when 1; di.instruction.opname += 'b'
				when 2; di.instruction.opname += 'w'
				end
			else
				di.instruction.args << case a

				# 16-bits instruction operands ------------------------------------------"
				when :cr0; GPR.new 0
				when :ca, :cb, :cb2, :cb3, :cc; GPR.new(field_val[a])
				when :ch
					if ((r = field_val[a]) == 0x3E)
						tmp = edata.decode_imm(:u32, :little)
						Expression[(tmp >> 16) | ((tmp & 0xffff) << 16)]
					else
						GPR.new(r)
					end

				when :@gps9, :@gps10, :@gps11
					imm = (a == :@gps11) ? :cs11 : (a == :@gps10) ? :cs10 : :cs9
					Memref.new(GPR.new(26), Expression[field_val[imm]], memref_size(di))

				when :cu3, :cu5, :cu5ee, :cu6, :cu7, :cu7l, :cu8; Expression[field_val[a]]
				when :cs9, :cs10, :cs11;  Expression[field_val[a]]
				when :cdisps7, :cdisps8, :cdisps10, :cdisps13; Expression[field_val[a]]
				when :@cb; Memref.new(GPR.new(field_val[:cb]), nil, memref_size(di))
				when :@cbu7, :@cbu6, :@cbu5; Memref.new(GPR.new(field_val[:cb]), Expression[field_val[a]], memref_size(di))
				when :@cspu7; Memref.new(GPR.new(28), field_val[a], memref_size(di))
				when :@cbcc; Memref.new(field_val[:cb], field_val[:cc], memref_size(di))

				# 32-bits instruction operands ------------------------------------------"
				when :a, :b
					((r = field_val[a]) == 0x3E) ? :zero : GPR.new(r)
				when :b2; GPR.new field_val[:b]
				when :c; GPR.new field_val[a]
				when :bext, :cext; field_val[a]
				when :@cext
					target = field_val[a]
					(di.opcode.props[:setip] and target.kind_of? GPR) ? Memref.new(target, nil, memref_size(di)) : target

				when :@bextcext
					tmp = field_val[a]
					#c = tmp & 0x3F
					tmp = tmp >> 6
					#b = (tmp >> 12) | ((tmp & 0x7) << 3)
					Memref.new(field_val[:bext],  field_val[:cext], memref_size(di))

				when :u6, :u6e, :s8e, :s9, :s12; Expression[field_val[a]]
				when :s12e, :s21e, :s21ee, :s25e, :s25ee; Expression[field_val[a]]
				when :auxs12; AUX.new field_val[:s12]
				when :@c; Memref.new(GPR.new(field_val[a]),  nil, memref_size(di))
				when :@bcext; Memref.new(field_val[a],  nil, memref_size(di))
				#when :@bcext; Memref.new(field_val[:b], field_val[:cext], memref_size(di))
				when :@bs9
					# [b,s9] or [limm] if b = 0x3E
					base = field_val[:bext]
					Memref.new(base, (base.kind_of? GPR) ? Expression[field_val[a]] : nil, memref_size(di))

				# common instruction operands ------------------------------------------"
				when :zero; Expression[0]
				when :gp; GPR.new(26)
				when :sp, :sp2; GPR.new(28)
				when :blink; GPR.new(31)
				when :@ilink1; Memref.new(GPR.new(29), nil, memref_size(di))
				when :@ilink2; Memref.new(GPR.new(30), nil, memref_size(di))
				when :@blink;  Memref.new(GPR.new(31), nil, memref_size(di))

				else raise SyntaxError, "Internal error: invalid argument #{a} in #{op.name}"
				end
			end
		}

		di.bin_length += edata.ptr - before_ptr

		return if edata.ptr > edata.virtsize

		di
	end

	def disassembler_default_func
		df = DecodedFunction.new
		df.backtrace_binding = {}
		15.times { |i|
			df.backtrace_binding["r#{i}".to_sym] = Expression::Unknown
		}
		df.backtracked_for = []
		df.btfor_callback = lambda { |dasm, btfor, funcaddr, calladdr|
			if funcaddr != :default
				btfor
			elsif di = dasm.decoded[calladdr] and di.opcode.props[:saveip]
				btfor
			else []
			end
		}
		df
	end

	REG_SYMS = [:r26, :r27, :r28, :r29, :r30, :r31, :r60]
	def register_symbols
		REG_SYMS
	end

	def backtrace_binding
		@backtrace_binding ||= init_backtrace_binding
	end

	def opshift(op)
		op[/\d/].to_i
	end

	def with_res(arg)
		arg != :zero
	end

	def init_backtrace_binding
		sp = :r28
		blink = :r31

		@backtrace_binding ||= {}

		mask = lambda { |sz| (1 << sz)-1 }  # 32bits => 0xffff_ffff

		opcode_list.each{|mode, oplist|
			oplist.map { |ol| ol.name }.uniq.each { |op|
				binding = case op
					  when /^add/, /^sub/
						  lambda { |di, a0, a1, a2|
							  if (shift = opshift(op)) == 0
								  { a0 => Expression[[a1, :+, a2], :&, mask[32]] }
							  else
								  { a0 => Expression[[a1, :+, [a2, :<<, shift]], :&, mask[32]] }
							  end
						  }
					  when /^and/
						  lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, a2] } }
					  when /^asl/
						  lambda { |di, *a| { a[0] => Expression[[a[1], :<<, (a[2] ? a[2]:1)], :&, mask[32]] } }
					  when /^bxor/
						  lambda { |di, a0, a1, a2| { a0 => Expression[a1, :^, [1, :<<, a2]] }}
					  when /^bclr/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :&, Expression[mask[32], :^, Expression[1, :<<, a2]]] } }
					  when /^bset/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, Expression[1, :<<, a2]] } }
					  when /^jl/; lambda { |di, a0| { blink => Expression[di.next_addr] } }
					  when 'bl', 'bl_s', /^bl\./
						  # FIXME handle delay slot
						  # "This address is taken either from the first instruction following the branch (current PC) or the
						  # instruction after that (next PC) according to the delay slot mode (.d)."
						  lambda { |di, a0| { blink => Expression[di.next_addr] } }
					  when /^mov/, /^lr/, /^ld/; lambda { |di, a0, a1| { a0 => a1 } }
					  when /^neg/; lambda { |di, a0, a1| { a0 => Expression[[0, :-, a1], :&, mask[32]] } }
					  when /^not/; lambda { |di, a0, a1| { a0 => Expression[[:~, a1], :&, mask[32]] } }
					  when /^or/; lambda { |di, a0, a1, a2| { a0 => Expression[a1, :|, a2] } }
					  when /^st/, /^sr/;  lambda { |di, a0, a1| { a1 => a0 } }
					  when /^ex/; lambda { |di, a0, a1| { a1 => a0 , a0 => a1 } }
					  when 'push_s'
						  lambda { |di, a0| {
							  sp => Expression[sp, :-, 4],
							  Indirection[sp, @size/8, di.address] => Expression[a0]
						  } }
					  when 'pop_s'
						  lambda { |di, a0| {
							  a0 => Indirection[sp, @size/8, di.address],
							  sp => Expression[sp, :+, 4]
						  } }
					  end
				@backtrace_binding[op] ||= binding if binding
			}
		}

		@backtrace_binding
	end

	def get_backtrace_binding(di)
		a = di.instruction.args.map { |arg|
			case arg
			when GPR; arg.symbolic
			when Memref; arg.symbolic(di.address)
			else arg
			end
		}

		if binding = backtrace_binding[di.opcode.basename]
			binding[di, *a]
		else
			puts "unhandled instruction to backtrace: #{di}" if $VERBOSE
			{ :incomplete_binding => Expression[1] }
		end
	end

	def get_xrefs_x(dasm, di)
		return [] if not di.opcode.props[:setip]

		arg = case di.opcode.name
		      when 'b', 'b_s', /^j/, /^bl/, /^br/, 'lp'
			      expr = di.instruction.args.last
			      expr.kind_of?(Memref) ? expr.base : expr
		      else di.instruction.args.last
		      end

		[Expression[(arg.kind_of?(Reg) ? arg.symbolic : arg)]]
	end

	def backtrace_is_function_return(expr, di=nil)
		Expression[expr].reduce == Expression[register_symbols[5]]
	end

	def delay_slot(di=nil)
		return 0 if (not di) or (not di.opcode.props[:setip])
		return 1 if di.opcode.props[:delay_slot]
		(di.instruction.opname =~ /\.d/) ? 0 : 1
	end
end
end
