#    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/main'

module Metasm

class Sh4 < CPU
	def initialize(e = :little, transfersz = 0, fpprecision = 0)
		super()
		@endianness = e

		# transfer size mode
		# When SZ = 1 and big endian mode is selected, FMOV can
		# be used for double-precision floating-point data load or
		# store operations. In little endian mode, two 32-bit data size
		# moves must be executed, with SZ = 0, to load or store a
		# double-precision floating-point number.
		transfersz = 0 if @endianness == :little
		@transfersz = transfersz

		# PR = 0 : Floating point instructions are executed as single
		# precision operations.
		# PR = 1 : Floating point instructions are executed as double-
		# precision operations (the result of instructions for
		# which double-precision is not supported is undefined).
		# Setting [transfersz = fpprecision = 1] is reserved.
		# FPU operations are undefined in this mode.
		@fpprecision = fpprecision

		@size = 32
	end

	class Reg
		include Renderable

		def ==(o)
			o.class == self.class and (not respond_to?(:i) or o.i == i)
		end
	end

	# general purpose reg
	class GPR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..15).map { |i| "r#{i}".to_sym }

		def symbolic(di=nil) ; Sym[@i] end

		def render ; ["r#@i"] end
	end

	class RBANK < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..7).map { |i| "r#{i}_bank".to_sym }

		def symbolic(di=nil) ; Sym[@i] end

		def render ; ["r#{@i}_bank"] end
	end

	# floatting-point registers
	class FR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..15).map { |i| "fr#{i}".to_sym }

		def symbolic(di=nil) ; Sym[@i] end

		def render ; ["fr#@i"] end
	end

	# DR registers: double-precision floating-point registers
	# DR0 = {FR0, FR1}
	# DR2 = {FR2, FR3}
	# DR4 = {FR4, FR5}
	# DR6 = {FR6, FR7}
	# DR8 = {FR8, FR9}
	# DR10 = {FR10, FR11}
	# DR12 = {FR12, FR13}
	# DR14 = {FR14, FR15}
	class DR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..7).map { |i| "dr#{i*2}".to_sym }

		def symbolic(di=nil) ; Sym[@i/2] end

		def render ; ["dr#@i"] end
	end

	# Single-precision floating-point vector registers
	# FV0 = {FR0, FR1, FR2, FR3}
	# FV4 = {FR4, FR5, FR6, FR7},
	# FV8 = {FR8, FR9, FR10, FR11}
	# FV12 = {FR12, FR13, FR14, FR15}
	class FVR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..3).map { |i| "fv#{i*4}".to_sym }

		def symbolic(di=nil) ; Sym[@i/4] end

		def render ; ["fv#@i"] end
	end

	# Single-precision floating-point extended registers
	class XFR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..15).map { |i| "xf#{i}".to_sym }

		def symbolic(di=nil) ; Sym[@i] end

		def render ; ["xf#@i"] end
	end

	# XD registers: single-precision floating-point vector registers
	# XD0 = {XF0, XF1}
	# XD2 = {XF2, XF3}
	# XD4 = {XF4, XF5}
	# XD6 = {XF6, XF7}
	# XD8 = {XF8, XF9}
	# XD10 = {XF10, XF11}
	# XD12 = {XF12, XF13}
	# XD14 = {XF14, XF15}
	class XDR < Reg
		attr_accessor :i

		def initialize(i); @i = i end
		Sym = (0..7).map { |i| "xd#{i*2}".to_sym }

		def symbolic(di=nil) ; Sym[@i/2] end

		def render ; ["xd#@i"] end
	end

	# Single-precision floating-point extended register matrix
	class XMTRX < Reg
		def symbolic(di=nil) ; :xmtrx ; end
		def render ; ['xmtrx'] ; end
	end


	# Multiply-and-accumulate register high
	class MACH < Reg

		def symbolic(di=nil) ; :mach end
		def render ; ['mach'] end
	end

	# Multiply-and-accumulate register low
	class MACL < Reg

		def symbolic(di=nil) ; :macl end
		def render ; ['macl'] end
	end

	# Procedure register
	class PR < Reg

		def symbolic(di=nil) ; :pr end
		def render ; ['pr'] end
	end

	# Floating-point communication register
	class FPUL < Reg

		def symbolic(di=nil) ; :fpul end
		def render ; ['fpul'] end
	end

	# Program counter
	class PC < Reg

		def symbolic(di=nil) ; :pc end
		def render ; ['pc'] end
	end

	# Floating-point status/control register
	class FPSCR < Reg

		def symbolic(di=nil) ; :fpscr end
		def render ; ['fpscr'] end
	end

	#----------------------- Control registers -----------------------------

	# Status register
	class SR < Reg

		def symbolic(di=nil) ; :sr end
		def render ; ['sr'] end
	end

	# Saved status register
	class SSR < Reg

		def symbolic(di=nil) ; :ssr end
		def render ; ['ssr'] end
	end

	# Saved program counter
	class SPC < Reg

		def symbolic(di=nil) ; :spc end
		def render ; ['spc'] end
	end

	# Global base register
	class GBR < Reg

		def symbolic(di=nil) ; :spc end
		def render ; ['gbr'] end
	end

	# Vector base register
	class VBR < Reg

		def symbolic(di=nil) ; :spc end
		def render ; ['vbr'] end
	end

	# Saved general register
	class SGR < Reg

		def symbolic(di=nil) ; :sgr end
		def render ; ['sgr'] end
	end

	# Debug base register
	class DBR < Reg

		def symbolic(di=nil) ; :dbr end
		def render ; ['dbr'] end
	end

	class Memref
		# action: pre/post (inc/dec)rement
		attr_accessor :base, :disp, :action

		def initialize(base, offset, action = nil)
			base = Expression[base] if base.kind_of? Integer
			@base, @disp, @action = base, offset, action
		end

		def symbolic(di=nil)
			sz = 32
			sz = di.opcode.props[:memsz] if di
			b = @base
			b = b.symbolic if b.kind_of? Reg

			b = Expression[b, :-, sz/8] if @action == :pre

			if disp
				o = @disp
				o = o.symbolic if o.kind_of? Reg
				e = Expression[b, :+, o].reduce
			else
				e = Expression[b].reduce
			end

			Indirection[e, sz, (di.address if di)]
		end

		include Renderable

		def render
			if @disp
				#['@(', @base, ',', @disp, ')']
				['[', @base, '+', @disp, ']']
			else
				case @action
				when :pre then ['[--', @base, ']']
				when :post then ['[', @base, '++]']
				else ['[', @base, ']']
				#when :pre then ['@-', @base]
				#when :post then ['@', @base, '+']
				#else ['@', @base]
				end
			end
		end

	end

	def init_opcode_list
		init
	end

	def dbg_register_list
		@dbg_register_list ||= GPR::Sym
	end
end
end
