# This module defines SuperH version 4, but should work against versions 1,2, and 3. 
# There is a SuperH version 4A (which has 4 byte instruction length) which has instructions incompatable
# with this.

# Based on "Renesas SH-4 Software Manual: Rev 6.00 2006.09 (i.e. rej09b0318_sh_4sm.pdf)

# Here's a nice webpage with all the insns clearly shown
# http://shared-ptr.com/sh_insns.html

# Address Space:
#       SuperH 4A has a 29-bit and a 32-bit address space mode,
#       and can do 32-bit addressing in 29-bit mode with virtual addressing.
#       The SH4 has a 29-bit physical address space, but can do 32-bit with virtual addressing.
#
# WARNING:
# WARNING:
# WARNING: Currently set up for 29-bit only for computed jumps/calls.  This needs to be configurable.
# WARNING:

# NOTE: SuperH 4 and floating point disassembly and decompiling precision.
# Many of the floating point instructions can do either single or double precision calculations depending 
# on a flag FPSCR_PR at runtime.  This means at disassembly stage we don't have all the information required
# determine the arguments to some floating point instructions.

# NOTE: SuperH 4 and floating point disassembly and decompiling  (fmov lengths).
# Some of the instructions can read and write floating point values in 4 or 8 byte values depending on the
# flag FPSCR_SZ.  This means at disassembly stage we don't have all the information required to determine the
# arguments to some floating point instructions.
# Note that the FPSCR_PR flag is never examined during any of the fmov insns.

# NOTE: SuperH and banking
# When the RB flag is not set R1->R7 come from bank0 and when RB is set R1->R7 come from bank1.  The RB mode is
# set in privileged mode.  We don't currently simulate this behavior.

# NOTE: SuperH and floating point banking
# When the flag FPSCR_FR is not set fr0->fr15 come from bank0 and when FPSCR.FR is set fr0->fr15 come from bank1.
# We don't currently simulate this behavior.

# NOTE: SuperH and memory map registers
# There are 7 32bit control registers (SR, GBR, SSR, SPC, SGR, DBR, VBR) these are mapped to 2 different memory
# areas 0x1C000000 and 0xFC000000. We don't currently simulate this behavior.

# NOTE: SuperH/Renesas return value convention.
# Renesas and gcc return most values from a function in r0 but floats are return in fr0 and doubles in dr0. 
# Ghidra calling spec has no way of specifying this behavior so such return values are not handled correctly.

# NOTE: SuperH/Renesas calling convention
# Renesas and gcc pass most values to a function via r4-r7 but floats are passed by fr4-fr7 and doubles in
# dr4, dr6, dr8, and dr10.   

# NOTE: floating point errors
# In implementing the floating point pcode we ignored many of the possible error conditions floating point
# could cause.  This allows us to produce much better looking decompiled code.

# NOTE: SuperH 4 Memory model
# SuperH 4 has a Memory Management Unit (i.e. MMU).  Which means that depending on mode the memory can
# look very different.  We don't model this behavior.  We also don't simulate the MMU address translation, 
# so all addresses are raw.


# Basic ================================================================================

define endian=$(ENDIAN); # Defined in file that includes this file

define alignment=2;

define space ram 	  type=ram_space 	  size=4 default;
define space register type=register_space size=4;

# Registers ============================================================================
# TODO deal with the 2 banks of registers
# TODO move os corret memory address
define register offset=0 size=4 [
	r0      r1      r2      r3      r4      r5      r6      r7
	r8      r9      r10     r11     r12     r13     r14     r15
	R0_BANK R1_BANK R2_BANK R3_BANK R4_BANK R5_BANK R6_BANK R7_BANK
];

# This is where "BANK0" is with RB=0 (normal), causes duplicate pair error in SLEIGH
#define register offset=0 size=4 [
#  R0_BANK0  R1_BANK0  R2_BANK0  R3_BANK0  R4_BANK0  R5_BANK0  R6_BANK0  R7_BANK0
#];
# This is where "BANK1" is with RB=1 (privileged), causes duplicate pair error in SLEIGH
#define register offset=0 size=4 [
#  R0_BANK1  R1_BANK1  R2_BANK1  R3_BANK1  R4_BANK1  R5_BANK1  R6_BANK1  R7_BANK1
#];
@if ENDIAN == "big"
define register offset=512 size=4 [
	fr0  fr1  fr2  fr3  fr4  fr5  fr6  fr7  fr8  fr9  fr10 fr11 fr12 fr13 fr14 fr15
	xf0  xf1  xf2  xf3  xf4  xf5  xf6  xf7  xf8  xf9  xf10 xf11 xf12 xf13 xf14 xf15
];

@endif

@if ENDIAN == "little"
define register offset=512 size=4 [
	fr1  fr0  fr3  fr2  fr5  fr4  fr7  fr6  fr9  fr8  fr11 fr10 fr13 fr12 fr15 fr14
	xf0  xf1  xf2  xf3  xf4  xf5  xf6  xf7  xf8  xf9  xf10 xf11 xf12 xf13 xf14 xf15
];

@endif

define register offset=512 size=8 [
	dr0  dr2  dr4  dr6  dr8  dr10 dr12 dr14
	xd0  xd2  xd4  xd6  xd8  xd10 xd12 xd14
];

define register offset=512 size=16 [
	fv0  fv4  fv8  fv12
];

# Control registers
define register offset=1024 size=4 [
	GBR SR  SSR SPC VBR SGR DBR
];

# SR component register fields (pseudo)
define register offset=1536 size=1 [
	MD    RB    BL    FD    M     Q     IMASK S     T
];

# System registers
define register offset=2048 size=4 [
	MACH  MACL  PR    PC    FPSCR FPUL
];

# FPSCR component register fields (pseudo)
define register offset=2560 size=1 [
	FPSCR_RM
	FPSCR_FLAG
	FPSCR_ENABLE
	FPSCR_CAUSE
	FPSCR_DN
	FPSCR_PR
	FPSCR_SZ
	FPSCR_FR
];

@define T_FLAG  "T"
@define S_FLAG  "S"
@define IMASK   "IMASK"
@define Q_FLAG  "Q"
@define M_FLAG  "M"
@define FD_FLAG "FD"
@define BL_FLAG "BL"
@define RB_FLAG "RB"
@define MD_FLAG "MD"

@define FPSCR_RM "FPSCR_RM"
@define FPSCR_FLAG "FPSCR_FLAG"
@define FPSCR_ENABLE "FPSCR_ENABLE"
@define FPSCR_CAUSE "FPSCR_CAUSE"
@define FPSCR_DN "FPSCR_DN"
@define FPSCR_PR "FPSCR_PR"
@define FPSCR_SZ "FPSCR_SZ"
@define FPSCR_FR "FPSCR_FR"

#
# SR pack and unpack support
#

# 0000 0000 0000 0000 0000 0000 0000 0001
@define T_MASK           "0x00000001"

# 0000 0000 0000 0000 0000 0000 0000 0010
@define S_MASK           "0x00000002"

# 0000 0000 0000 0000 0000 0000 1111 0000
@define IMASK_MASK       "0x000000F0"

# 0000 0000 0000 0000 0000 0001 0000 0000
@define Q_MASK           "0x00000100"

# 0000 0000 0000 0000 0000 0010 0000 0000
@define M_MASK           "0x00000200"

# 0000 0000 0000 0000 1000 0000 0000 0000
@define FD_MASK          "0x00008000"

# 0001 0000 0000 0000 0000 0000 0000 0000
@define BL_MASK          "0x10000000"

# 0010 0000 0000 0000 0000 0000 0000 0000
@define RB_MASK          "0x20000000"

# 0100 0000 0000 0000 0000 0000 0000 0000
@define MD_MASK          "0x40000000"

@define T_SHIFT          " 0"
@define S_SHIFT          " 1"
@define IMASK_SHIFT      " 4"
@define Q_SHIFT          " 8"
@define M_SHIFT          " 9"
@define FD_SHIFT         "15"
@define BL_SHIFT         "28"
@define RB_SHIFT         "29"
@define MD_SHIFT         "30"

macro genSRregister() {

	SR = 	
    (zext(T)     << $(T_SHIFT))     |
    (zext(S)     << $(S_SHIFT))     |
    (zext(IMASK) << $(IMASK_SHIFT)) |
    (zext(Q)     << $(Q_SHIFT))     |
    (zext(M)     << $(M_SHIFT))     |
    (zext(FD)    << $(FD_SHIFT))    |
    (zext(BL)    << $(BL_SHIFT))    |
    (zext(RB)    << $(RB_SHIFT))    |
    (zext(MD)    << $(MD_SHIFT));
}

macro splitSRregister() {

	splitTemp:4  = (SR & $(T_MASK))     >> $(T_SHIFT);
	T            = splitTemp:1;

	splitTemp    = (SR & $(S_MASK))     >> $(S_SHIFT);
	S            = splitTemp:1;

	splitTemp    = (SR & $(IMASK_MASK)) >> $(IMASK_SHIFT);
	IMASK        = splitTemp:1;

	splitTemp    = (SR & $(Q_MASK))     >> $(Q_SHIFT);
	Q            = splitTemp:1;

	splitTemp    = (SR & $(M_MASK))     >> $(M_SHIFT);
	M            = splitTemp:1;

	splitTemp    = (SR & $(FD_MASK))    >> $(FD_SHIFT);
	FD           = splitTemp:1;

	splitTemp    = (SR & $(BL_MASK))    >> $(BL_SHIFT);
	BL           = splitTemp:1;

	splitTemp    = (SR & $(RB_MASK))    >> $(RB_SHIFT);
	RB           = splitTemp:1;

	splitTemp    = (SR & $(MD_MASK))    >> $(MD_SHIFT);
	MD           = splitTemp:1;
}

#
# FPSCR pack and unpack support
#
@define  FPSCR_RM_SHIFT      " 0"
@define  FPSCR_FLAG_SHIFT    " 2"
@define  FPSCR_ENABLE_SHIFT  " 7"
@define  FPSCR_CAUSE_SHIFT   "12"
@define  FPSCR_DN_SHIFT      "18"
@define  FPSCR_PR_SHIFT      "19"
@define  FPSCR_SZ_SHIFT      "20"
@define  FPSCR_FR_SHIFT      "21"


# 0000 0000 0000 0000 0000 0000 0000 0011
@define  FPSCR_RM_MASK       "0x00000003"

# 0000 0000 0000 0000 0000 0000 0111 1100
@define  FPSCR_FLAG_MASK     "0x0000007C"

# 0000 0000 0000 0000 0000 1111 1000 0000
@define  FPSCR_ENABLE_MASK   "0x00000F80"

# 0000 0000 0000 0011 1111 0000 0000 0000
@define  FPSCR_CAUSE_MASK    "0x0003F000"

# 0000 0000 0000 0100 0000 0000 0000 0000
@define  FPSCR_DN_MASK       "0x00040000"

# 0000 0000 0000 1000 0000 0000 0000 0000
@define  FPSCR_PR_MASK       "0x00080000"

# 0000 0000 0001 0000 0000 0000 0000 0000
@define  FPSCR_SZ_MASK       "0x00100000"

# 0000 0000 0010 0000 0000 0000 0000 0000
@define  FPSCR_FR_MASK       "0x00200000"

# Bits 22-31 are not used

macro genFPSCRregister() {

	FPSCR = 	
    (zext(FPSCR_RM)     << $(FPSCR_RM_SHIFT))     |
    (zext(FPSCR_FLAG)   << $(FPSCR_FLAG_SHIFT))   |
    (zext(FPSCR_ENABLE) << $(FPSCR_ENABLE_SHIFT)) |
    (zext(FPSCR_CAUSE)  << $(FPSCR_CAUSE_SHIFT))  |
    (zext(FPSCR_DN)     << $(FPSCR_DN_SHIFT))     |
    (zext(FPSCR_PR)     << $(FPSCR_PR_SHIFT))     |
    (zext(FPSCR_SZ)     << $(FPSCR_SZ_SHIFT))     |
    (zext(FPSCR_FR)     << $(FPSCR_FR_SHIFT));
}

macro splitFPSCRregister() {

	splitTemp:4  = (FPSCR & $(FPSCR_RM_MASK))     >> $(FPSCR_RM_SHIFT);
	FPSCR_RM     = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_FLAG_MASK))   >> $(FPSCR_FLAG_SHIFT);
	FPSCR_FLAG   = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_ENABLE_MASK)) >> $(FPSCR_ENABLE_SHIFT);
	FPSCR_ENABLE = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_CAUSE_MASK))  >> $(FPSCR_CAUSE_SHIFT);
	FPSCR_CAUSE  = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_DN_MASK))     >> $(FPSCR_DN_SHIFT);
	FPSCR_DN     = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_PR_MASK))     >> $(FPSCR_PR_SHIFT);
	FPSCR_PR     = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_SZ_MASK))     >> $(FPSCR_SZ_SHIFT);
	FPSCR_SZ     = splitTemp:1;

	splitTemp    = (FPSCR & $(FPSCR_FR_MASK))     >> $(FPSCR_FR_SHIFT);
	FPSCR_FR     = splitTemp:1;
}

# Fields =================================================================================
define token instr(16)
	OP_0  = (12,15)
	OP_1  = ( 0, 3)
	OP_2  = ( 8,15)
	OP_3  = ( 0,15)
	OP_4  = ( 0, 7)
	OP_5  = ( 0, 8)
	OP_6  = ( 8, 8)
	OP_7  = ( 0, 4)
	OP_8  = ( 0, 9)
	OP_9  = ( 7, 7)
	OP_10 = ( 4, 4)

	BANK  = ( 4, 6) # bank register id
	M_0   = ( 4, 7) # register id
	M_1   = ( 5, 7) # register id
	M_2   = ( 8, 9) # register id
	FRM_0 = ( 4, 7) # float register id
	DRM_1 = ( 5, 7) # double register id
	XDM_1 = ( 5, 7) # double register id
	XDRM  = ( 4, 7) # double register id
	FVM_2 = ( 8, 9) # fv register id
	N_0   = ( 8,11) # register id
	N_1   = ( 9,11) # register id
	N_2   = (10,11) # register id
	FRN_0 = ( 8,11) # float register id
        FRN_1 = ( 8,10) # float register id
        FRN_2 = ( 8,10) # float register id
        DRN_0 = ( 8,10) # double register id
	DRN_1 = ( 9,11) # double register id
	XDN_1 = ( 9,11) # double register id
	XDRN  = ( 8,11) # float register id
	FVN_2 = (10,11) # fv register id
	I_0   = ( 0, 7) signed # immediate
	I_1   = ( 0,11) signed # immediate
	I_2   = ( 0, 3) signed # immediate
	U_0   = ( 0, 7) # immediate
	U_1   = ( 0,11) # immediate
	U_2   = ( 0, 3) # immediate
;

# Context variables ====================================================
# Attach variables =====================================================
# attach normal registers 
attach variables [ N_0 M_0 ] [
  r0  r1  r2  r3  r4  r5  r6  r7  r8  r9  r10  r11  r12  r13  r14  r15
];

# attach float registers 
attach variables [ FRN_0 FRM_0] [
  fr0  fr1  fr2  fr3  fr4  fr5  fr6  fr7  fr8  fr9  fr10  fr11  fr12  fr13  fr14  fr15
];

attach variables [ FRN_1 ] [
  fr0 fr2  fr4  fr6  fr8  fr10  fr12  fr14
];

attach variables [ FRN_2 ] [
  fr1  fr3  fr5  fr7  fr9  fr11  fr13  fr15
];

# attach double registers
attach variables [ DRN_1 DRM_1 ] [
  dr0 dr2 dr4 dr6 dr8 dr10 dr12 dr14
];

# attach double registers
attach variables [ DRN_0 ] [
  dr0 dr2 dr4 dr6 dr8 dr10 dr12 dr14
];

# attach extended double registers
attach variables [ XDN_1 XDM_1 ] [
  xd0 xd2 xd4 xd6 xd8 xd10 xd12 xd14
];

attach variables [ XDRN XDRM ] [
  dr0 xd0 dr2 xd2 dr4 xd4 dr6 xd6 dr8 xd8 dr10 xd10 dr12 xd12 dr14 xd14
];

# attach vf registers
attach variables [ FVN_2 FVM_2 ] [
  fv0 fv4 fv8 fv12
];

attach variables [ BANK ] [
   R0_BANK  R1_BANK  R2_BANK  R3_BANK  R4_BANK  R5_BANK  R6_BANK  R7_BANK
];

# Tables ================================================================
# Addressing Modes
#
# Register direct
# Rn			EA is Rn.
# 
# Register indirect
# @Rn			Rn contains EA
# 
# Register indirect with postincrement
# @Rn+			Rn contains EA
#               After EA calculation:
#				increment Rn by 1 for byte, 2 for word, 4 for long word, 8 quadword operand
# 
# Register indirect with predecrement
# @-Rn			Rn contains EA,
#               Before EA calculation:
#				increment Rn by 1 for byte, 2 for word, 4 for long word, 8 quadword operand
# 
# Register indirect with displacement
# @(disp:4, Rn)	EA is Rn + 4-bit displacement disp added. disp is zero extended,
#               then multiplied by 1 for byte, 2 for word, 4 for long word operand size
# 
# Indexed register indirect
# @(R0, Rn)		EA is Rn + R0
# 
# GBR indirect with displacement
# @(disp:8,GBR)	EA is GBR contents with 8-bit displacement added.
#				8-bit displacement disp added. disp is zero extended,
#               then multiplied by 1 for byte, 2 for word, 4 for long word operand size
# 
# Indexed GBR indirect
# @(R0, GBR)	EA is GBR + R0
# 
# PC-relative with displacement
# @(disp:8, PC)	EA is PC+4 + 8bit displacement. disp is zero extended,
#               then multiplied by 1 for byte, 2 for word, 4 for long word operand size
# 
# PC-relative
# disp:8		EA is PC+4 + 8-bit displacement.
#				disp is sign-extended and multiplied by 2.
# 
# PC-relative
# disp:12		EA is PC+4 + 12-bit displacement.
#				disp is sign-extended and multiplied by 2.
# 
# Rn			EA is PC+4 + Rn.
# 
M_0t:   M_0  is M_0 { export M_0; }

N_0t:   N_0  is N_0 { export N_0; }

N_0tjmp: @^N_0  is N_0 { export N_0; }

I_0t:    "#"^I_0  is I_0 { tmp:4 = I_0; export tmp; }

U_0t:    "#"^U_0                        is U_0            { tmp:4 = U_0; export tmp; }
U_0t1:   "#"^U_0^",@("^r0^","^GBR^")"  is U_0 & r0 & GBR { tmp:1 = U_0; export tmp; }

I_0t_r0: "#"I_0^","^r0  is I_0 & r0 { tmp:4 = I_0; export tmp; }

U_0t_r0: "#"U_0^","^r0  is U_0 & r0 { tmp:4 = U_0; export tmp; }

I_0tbranch: dest  is I_0 [ dest = inst_start + I_0*2 + 4; ] { export *:4 dest; }

I_1tbranch: dest  is I_1 [ dest = inst_start + I_1*2 + 4; ] { export *:4 dest; }

sr_N_0t:   SR^","^N_0  is N_0 & SR { export N_0; }

gbr_N_0t:  GBR^","^N_0  is N_0 & GBR { export N_0; }

vbr_N_0t:  VBR^","^N_0  is N_0 & VBR { export N_0; }

ssr_N_0t:  SSR^","^N_0  is N_0 & SSR { export N_0; }

spc_N_0t:  SPC^","^N_0  is N_0 & SPC { export N_0; }

sgr_N_0t:  SGR^","^N_0  is N_0 & SGR { export N_0; }

dbr_N_0t:  DBR^","^N_0  is N_0 & DBR { export N_0; }

sr_t:   SR  is OP_0 & SR { x:4 = 0; export x; } # dummy export, just looking for the display

gbr_t:  GBR  is OP_0 & GBR { x:4 = 0; export x; } # dummy export, just looking for the display

vbr_t:  VBR  is OP_0 & VBR { x:4 = 0; export x; } # dummy export, just looking for the display

ssr_t:  SSR  is OP_0 & SSR { x:4 = 0; export x; } # dummy export, just looking for the display

spc_t:  SPC  is OP_0 & SPC { x:4 = 0; export x; } # dummy export, just looking for the display

sgr_t:  SGR  is OP_0 & SGR { x:4 = 0; export x; } # dummy export, just looking for the display

dbr_t:  DBR  is OP_0 & DBR { x:4 = 0; export x; } # dummy export, just looking for the display

N_0t_sr:   N_0^","^SR      is N_0 & SR { export N_0; }

N_0t_gbr:  N_0^","^GBR      is N_0 & GBR { export N_0; }

N_0t_vbr:  N_0^","^VBR      is N_0 & VBR { export N_0; }

N_0t_ssr:  N_0^","^SSR      is N_0 & SSR { export N_0; }

N_0t_spc:  N_0^","^SPC      is N_0 & SPC { export N_0; }

# N_0t_sgr:  N_0^","^SGR   is N_0 & SGR { export N_0; }
N_0t_dbr:  N_0^","^DBR      is N_0 & DBR { export N_0; }

N_0t_bank: N_0         is N_0 { export N_0; }

N_0t_sr1:   @^N_0^"+,"^SR  is N_0 & SR { export N_0; }

N_0t_gbr1:  @^N_0^"+,"^GBR  is N_0 & GBR { export N_0; }

N_0t_vbr1:  @^N_0^"+,"^VBR  is N_0 & VBR { export N_0; }

N_0t_ssr1:  @^N_0^"+,"^SSR  is N_0 & SSR { export N_0; }

N_0t_spc1:  @^N_0^"+,"^SPC  is N_0 & SPC { export N_0; }

# N_0t_sgr1:  @^N_0^"+,"^SGR     is N_0 & SGR { export N_0;}
N_0t_dbr1:  @^N_0^"+,"^DBR  is N_0 & DBR { export N_0; }

N_0t_bank1: @^N_0^"+"  is N_0 { export N_0; }

FR0_t:   fr0  is OP_0 & fr0 { export fr0; }

XMTRX_t: "xmtrx"  is OP_0 { x:4 = 0; export x; } # dummy export, just looking for the display

mach_t:  MACH  is OP_0 & MACH { export MACH; }

macl_t:  MACL  is OP_0 & MACL { export MACL; }

fpul_t:  FPUL  is OP_0 & FPUL { export FPUL; }

fpscr_t: "FPSCR"  is OP_0 { x:4 = 0; export x; } # dummy export, just looking for the display

mach_N_0t:  MACH^","^N_0  is N_0 & MACH { export N_0; }

macl_N_0t:  MACL^","^N_0  is N_0 & MACL { export N_0; }

pr_N_0t:    PR^","^N_0  is N_0 & PR { export N_0; }

fpul_N_0t:  FPUL^","N_0  is N_0 & FPUL { export N_0; }

fpscr_N_0t: "FPSCR"^","N_0  is N_0 { export N_0; }

N_0t_mach:  N_0^","^MACH      is N_0 & MACH { export N_0; }

N_0t_macl:  N_0^","^MACL      is N_0 & MACL { export N_0; }

N_0t_pr:    N_0^","^PR      is N_0 & PR { export N_0; }

N_0t_fpul:  N_0^","^FPUL      is N_0 & FPUL { export N_0; }

N_0t_fpscr: N_0^",fpscr"      is N_0 { export N_0; }

N_0t_mach1:  @^N_0^"+,"^MACH  is N_0 & MACH { export N_0; }

N_0t_macl1:  @^N_0^"+,"^MACL  is N_0 & MACL { export N_0; }

N_0t_pr1:    @^N_0^"+,"^PR  is N_0 & PR { export N_0; }

N_0t_fpul1:  @^N_0^"+,"^FPUL  is N_0 & FPUL { export N_0; }

N_0t_fpscr1: @^N_0^"+,fpscr"  is N_0 { export N_0; }

M_0t_at1: @^M_0   is M_0 { export M_0; }

N_0t_at1: @^N_0   is N_0 { export N_0; }

M_0t_at: @^M_0^+  is M_0 { export M_0; }

N_0t_at: @^N_0^+  is N_0 { export N_0; }

FRM_0t: FRM_0  is FRM_0 { export FRM_0; }

FRN_0t: FRN_0  is FRN_0 { export FRN_0; }

DRM_1t: DRM_1  is DRM_1 { export DRM_1; }

DRN_1t: DRN_1  is DRN_1 { export DRN_1; }

FVM_2t: FVM_2  is FVM_2 { export FVM_2; }

FVN_2t: FVN_2  is FVN_2 { export FVN_2; }

N_0t_at_with_r0: "@("^r0^","^N_0^")"  is N_0 & r0 { export N_0; }

M_0t_at_with_r0: "@("^r0^","^M_0^")"  is M_0 & r0 { export M_0; }

N_0t_at_neg: "@-"^N_0  is N_0 { export N_0; }

U_2t_M0_dispr01: "@("^disp^","^M_0^")"  is U_2 & M_0  [ disp = U_2 * 1; ] { tmp4:4 = disp; export tmp4; }
U_2t_M0_dispr02: "@("^disp^","^M_0^")"  is U_2 & M_0  [ disp = U_2 * 2; ] { tmp4:4 = disp; export tmp4; }
U_2t_M0_dispr04: "@("^disp^","^M_0^")"  is U_2 & M_0  [ disp = U_2 * 4; ] { tmp4:4 = disp; export tmp4; }

U_2t_N0_dispr04: "@("^disp^","^N_0^")"  is U_2 & N_0  [ disp = U_2 * 4; ] { tmp4:4 = disp; export tmp4; }

# Bug in SLEIGH, needed "* 1"
U_0t_gbr_at_1: "@("^disp,GBR^")"  is U_0 & GBR [ disp = U_0 * 1; ] { tmp4:4 = disp + GBR; export tmp4; }
U_0t_gbr_at_2: "@("^disp,GBR^")"  is U_0 & GBR [ disp = U_0 * 2; ] { tmp4:4 = disp + GBR; export tmp4; }
U_0t_gbr_at_4: "@("^disp,GBR^")"  is U_0 & GBR [ disp = U_0 * 4; ] { tmp4:4 = disp + GBR; export tmp4; }

# Note: The 4 byte (MOVA) case needs the masking of the PC bottom 2 bits, page 345, paragraph 1:
#      "a value with the lower 2 bits adjusted to B00 is used in address calculation."
# Note: Only the 4 byte case needs the masking of the PC bottom 2 bits, page 336, paragraph 3:
#      "A value with the lower 2 bits adjusted to B00 is used in address calculation."
# (The 2 byte case always has the PC LSBit at 0 because all instructions are 2 byte aligned.)
U_0t_2pc: dest  is U_0  [ dest =   inst_start                + U_0*2 + 4; ] { export *:2 dest; }

U_0t_4pc: dest  is U_0  [ dest = ( inst_start & 0xfffffffc ) + U_0*4 + 4; ] { export *:4 dest; }

BANKt: BANK  is BANK { export BANK; }

N_0txx: r0",@"^N_0  is r0 & N_0 { tmp:4 = N_0; export tmp; }

# Constructors =======================================================================
# Binary Addition
# pattern 0011nnnnmmmm1100
# text    add <REG_M>,<REG_N>
# arch    arch_sh_up
:add M_0t,N_0t              is OP_0=0x3 & N_0t & M_0t & OP_1=0xc {

	N_0t = N_0t + M_0t;
}


# Binary Addition
# pattern 0111nnnniiiiiiii
# text    add #<imm>,<REG_N>
# arch    arch_sh_up
:add I_0t,N_0t              is OP_0=0x7 & N_0t & I_0t {

	N_0t = I_0t + N_0t; # NOTE I_0t already signed extended
}


# Binary Addition with Carry
# pattern 0011nnnnmmmm1110
# text    addc <REG_M>,<REG_N>
# arch    arch_sh_up
:addc M_0t,N_0t             is OP_0=0x3 & N_0t & M_0t & OP_1=0xe {
	local Tcopy:4 = zext($(T_FLAG));
	$(T_FLAG) = carry( N_0t, M_0t );
	local result:4 = N_0t + M_0t;
	$(T_FLAG) = $(T_FLAG) || carry( result, Tcopy );
	N_0t = result + Tcopy;	
}


# Binary Addition with Overflow Check
# pattern 0011nnnnmmmm1111
# text    addv <REG_M>,<REG_N>
# arch    arch_sh_up
:addv M_0t,N_0t             is OP_0=0x3 & N_0t & M_0t & OP_1=0xf {

	$(T_FLAG) = scarry(N_0t,M_0t);
	N_0t = N_0t + M_0t;
}


# Logical AND
# pattern 0010nnnnmmmm1001
# text    and <REG_M>,<REG_N>
# arch    arch_sh_up
:and M_0t,N_0t              is OP_0=0x2 & N_0t & M_0t & OP_1=0x9 {

	N_0t = N_0t & M_0t;
}

# Logical AND
# pattern 11001001iiiiiiii
# text    and #<imm>,R0
# arch    arch_sh_up
:and U_0t_r0                is OP_2=0xc9 & U_0t_r0 {

	r0 = r0 & U_0t_r0;
}

# Logical AND
# pattern 11001101iiiiiiii
# text    and.b #<imm>,@(R0,GBR)
# arch    arch_sh_up
:and.b U_0t1                is OP_2=0xcd & U_0t1 {

	*:1 (GBR + r0) = (*:1 (GBR + r0)) & U_0t1;
}


# Conditional Branch
# pattern 10001011iiiiiiii
# text    bf <bdisp8>
# arch    arch_sh_up
:bf I_0tbranch              is OP_2=0x8b & I_0tbranch {

	if ( $(T_FLAG) == 0 ) goto I_0tbranch;
}


# Conditional Branch with Delay
# pattern 10001111iiiiiiii
# text    bf/s <bdisp8>
# arch    arch_sh2_up
:bf^"/s" I_0tbranch         is OP_2=0x8f & I_0tbranch {

	local cond = $(T_FLAG);
	delayslot(1);
	if ( cond == 0 ) goto I_0tbranch;
} 


# Unconditional Branch
# pattern 1010iiiiiiiiiiii
# text    bra <bdisp12>
# arch    arch_sh_up
:bra I_1tbranch             is OP_0=0xa & I_1tbranch {

	delayslot(1);
	goto I_1tbranch;
}


# Unconditional Branch
# pattern 0000nnnn00100011
# text    braf <REG_N>
# arch    arch_sh2_up
:braf N_0                   is OP_0=0x0 & N_0 & OP_4=0x23 {

	local dest = N_0 + inst_next;
	delayslot(1);
	goto [dest];
}


# Branch to Subroutine Procedure
# pattern 1011iiiiiiiiiiii
# text    bsr <bdisp12>
# arch    arch_sh_up
:bsr I_1tbranch             is OP_0=0xb & I_1tbranch {

	delayslot(1);
	call I_1tbranch;
}


# Branch to Subroutine Procedure
# pattern 0000nnnn00000011
# text    bsrf <REG_N>
# arch    arch_sh2_up
:bsrf N_0                   is OP_0=0x0 & N_0 & OP_4=0x3 {

	PR = inst_next;
	local dest = N_0 + inst_next;
	delayslot(1);
	call [dest];
}


# Conditional Branch
# pattern 10001001iiiiiiii
# text    bt <bdisp8>
# arch    arch_sh_up
:bt I_0tbranch              is OP_2=0x89 & I_0tbranch {

	if ( $(T_FLAG) == 1 ) goto I_0tbranch;
}


# Conditional Branch with Delay
# pattern 10001101iiiiiiii
# text    bt/s <bdisp8>
# arch    arch_sh2_up
:bt^"/s" I_0tbranch         is OP_2=0x8d & I_0tbranch {
	local cond = $(T_FLAG);
	delayslot(1);
	if ( cond == 1 ) goto I_0tbranch;
}


# MAC Register Clear
# pattern 0000000000101000
# text    clrmac
# arch    arch_sh_up
:clrmac                     is OP_3=0x28 {

	MACH = 0;
	MACL = 0;
}


# S Bit Clear
# pattern 0000000001001000
# text    clrs
# arch    arch_sh_up
:clrs                       is OP_3=0x48 {

	$(S_FLAG) = 0;
}


# T Bit Clear
# pattern 0000000000001000
# text    clrt
# arch    arch_sh_up
:clrt                       is OP_3=0x8 {

	$(T_FLAG) = 0;
}


# Compare
# pattern 0011nnnnmmmm0000
# text    cmp/eq <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/eq" M_0t,N_0t        is OP_0=0x3 & N_0t & M_0t & OP_1=0x0 {

	$(T_FLAG) = ( N_0t == M_0t );
} 


# Compare
# pattern 0011nnnnmmmm0011
# text    cmp/ge <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/ge" M_0t,N_0t        is OP_0=0x3 & N_0t & M_0t & OP_1=0x3 {

	$(T_FLAG) = ( N_0t s>= M_0t );
}

# Compare
# pattern 0011nnnnmmmm0111
# text    cmp/gt <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/gt" M_0t,N_0t        is OP_0=0x3 & N_0t & M_0t & OP_1=0x7 {

	$(T_FLAG) = ( N_0t s> M_0t);
} 


# Compare
# pattern 0011nnnnmmmm0110
# text    cmp/hi <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/hi" M_0t,N_0t        is OP_0=0x3 & N_0t & M_0t & OP_1=0x6 {

	$(T_FLAG) = ( N_0t > M_0t );
}


# Compare
# pattern 0011nnnnmmmm0010
# text    cmp/hs <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/hs" M_0t,N_0t        is OP_0=0x3 & N_0t & M_0t & OP_1=0x2 {

	$(T_FLAG) = ( N_0t >= M_0t );
}


# Compare
# pattern 0100nnnn00010101
# text    cmp/pl <REG_N>
# arch    arch_sh_up
:cmp^"/pl" N_0t             is OP_0=0x4 & N_0t & OP_4=0x15 {

	$(T_FLAG) = ( N_0t s> 0 );
}

# Compare
# pattern 0100nnnn00010001
# text    cmp/pz <REG_N>
# arch    arch_sh_up
:cmp^"/pz" N_0t             is OP_0=0x4 & N_0t & OP_4=0x11 {

	$(T_FLAG) = ( N_0t s>= 0 );
}


# Compare
# pattern 0010nnnnmmmm1100
# text    cmp/str <REG_M>,<REG_N>
# arch    arch_sh_up
:cmp^"/str" M_0t,N_0t       is OP_0=0x2 & N_0t & M_0t & OP_1=0xc {

	temp:4 = M_0t ^ N_0t;
	HH:4 = (temp & 0xFF000000) >> 24;
	HL:4 = (temp & 0x00FF0000) >> 16;
	LH:4 = (temp & 0x0000FF00) >> 8;
	LL:4 =  temp & 0x000000FF;
	$(T_FLAG)   = (HH == 0) || (HL == 0 ) || (LH == 0) || (LL == 0);
}


# Compare
# pattern 10001111iiiiiiii
# pattern 10001000iiiiiiii
# text    cmp/eq #<imm>,R0
# arch    arch_sh_up
:cmp^"/eq" I_0t_r0          is OP_2=0x88 & I_0t_r0 {

	$(T_FLAG) = ( r0 == I_0t_r0 );
}


# Initialization for Signed Division
# pattern 0010nnnnmmmm0111
# text    div0s <REG_M>,<REG_N>
# arch    arch_sh_up
:div0s M_0t,N_0t            is OP_0=0x2 & N_0t & M_0t & OP_1=0x7 {

	$(Q_FLAG) = ( (N_0t & 0x80000000) != 0 );
	$(M_FLAG) = ( (M_0t & 0x80000000) != 0 );
	$(T_FLAG) = ( $(M_FLAG) != $(Q_FLAG) );
}


# Initialization for Unsigned Division
# pattern 0000000000011001
# text    div0u
# arch    arch_sh_up
:div0u                      is OP_3=0x19 {

	$(M_FLAG) = 0;
	$(Q_FLAG) = 0;
	$(T_FLAG) = 0;
}


# Division
# pattern 0011nnnnmmmm0100
# text    div1 <REG_M>,<REG_N>
# arch    arch_sh_up
:div1 M_0t,N_0t             is OP_0=0x3 &  OP_1=0x4 &  M_0t &  N_0t {

#@ifdef DIV1_ORIGINAL_IS_BROKEN
#	tmp2:4  =   M_0t;
#	N_0t    =   N_0t << 1;
#	N_0t    =   N_0t | zext($(T_FLAG)); # ???
#   tmp0:4  =   N_0t;
#	N_0t    =   N_0t - zext((($(Q_FLAG)+$(M_FLAG))!=1))*tmp2 + zext(($(Q_FLAG)+$(M_FLAG))==1)*tmp2;
#	tmp1:1  =   ( (($(Q_FLAG)+$(M_FLAG))!=1)*(N_0t > tmp0) + (($(Q_FLAG)+$(M_FLAG))==1)*(N_0t < tmp0));
#	$(Q_FLAG)       =   ((0x80000000 & N_0t)!=0);
#	Q1:1    =   tmp1*($(Q_FLAG)!=0) + (tmp1==0)*($(Q_FLAG)==0);
#	Q2:1    =   tmp1*($(Q_FLAG)==0) + (tmp1==0)*($(Q_FLAG)!=0);
#	$(Q_FLAG)       =   (($(M_FLAG)==1)*Q1 + ($(M_FLAG)!=1)*Q2);
#	$(T_FLAG) = ($(Q_FLAG)==$(M_FLAG));
#@endif # DIV1_ORIGINAL_IS_BROKEN
# DIV1_WITH_FORWARD_BRANCHES_WORKS_OK
	local Rm:4 = M_0t;
	local Rn:4 = N_0t;
	local old_Q:1 = $(Q_FLAG);
	$(Q_FLAG) = ((0x80000000 & Rn)!=0);
	local tmp2:4 = Rm;
	Rn = Rn << 1;
	Rn = Rn | zext($(T_FLAG));
	local tmp0:4 = Rn;
	local tmp1:1 = 0;

	if (old_Q == 0) && ($(M_FLAG) == 0) goto <section_1>;
	if (old_Q == 0) && ($(M_FLAG) == 1) goto <section_2>;
	if (old_Q == 1) && ($(M_FLAG) == 0) goto <section_3>;
# if (old_Q == 1) && ($(M_FLAG) == 1) ...
	Rn = Rn - tmp2;
	tmp1 = Rn > tmp0;
	if ($(Q_FLAG) == 0) goto <section_4a>;
# ($(Q_FLAG) == 1)
	$(Q_FLAG) = tmp1;
	goto <done>;
<section_4a>
	$(Q_FLAG) = tmp1 == 0;
	goto <done>;

<section_3> # (old_Q == 1) && ($(M_FLAG) == 0)
	Rn = Rn + tmp2;
	tmp1 = Rn < tmp0;
	if ($(Q_FLAG) == 0) goto <section_3a>;
# ($(Q_FLAG) == 1)
	$(Q_FLAG) = tmp1 == 0;
	goto <done>;
<section_3a>
	$(Q_FLAG) = tmp1;
	goto <done>;

<section_2> # (old_Q == 0) && ($(M_FLAG) == 1)
	Rn = Rn + tmp2;
	tmp1 = Rn < tmp0;
	if ($(Q_FLAG) == 0) goto <section_2a>;
# ($(Q_FLAG) == 1)
	$(Q_FLAG) = tmp1;
	goto <done>;
<section_2a>
	$(Q_FLAG) = tmp1 == 0;
	goto <done>;

<section_1> # (old_Q == 0) && ($(M_FLAG) == 0)
	Rn = Rn - tmp2;
	tmp1 = Rn > tmp0;
	if ($(Q_FLAG) == 0) goto <section_1a>;
# ($(Q_FLAG) == 1)
	$(Q_FLAG) = tmp1 == 0;
	goto <done>;
<section_1a>
	$(Q_FLAG) = tmp1;

<done>
	$(T_FLAG) = $(Q_FLAG) == $(M_FLAG);
	N_0t = Rn;

# DIV1_WITH_GOTOs_WORKS_OK

# TODO: the following is currently broken, it should be fixed to eliminate gotos in the code above

#@ifdef DIV1_STRAIGHT_CODE # BROKEN
#	Rm:4 = M_0t;
#	Rn:4 = N_0t;
#	old_Q:1 = $(Q_FLAG);
#	$(Q_FLAG) = ((0x80000000 & Rn)!=0);
#	tmp2:4 = Rm;
#	Rn = Rn << 1;
#	Rn = Rn | zext($(T_FLAG));
#	tmp0:4 = Rn;
#
#	oldQM_10_01_bool:1 = ( ( (old_Q == 1) && ($(M_FLAG) == 0) ) || ( (old_Q == 1) && ($(M_FLAG) == 0) ) );
#	oldQM_10_01:4 = zext(oldQM_10_01_bool) * 0xffffffff;
#
#	oldQM_11_00_bool:1 = ( (old_Q == 1) && ($(M_FLAG) == 1) ) || ( (old_Q == 0) && ($(M_FLAG) == 0) );
#	oldQM_11_00:4 = zext(oldQM_11_00_bool) * 0xffffffff;

#	Rn = (oldQM_10_01 & (Rn + tmp2)) | (oldQM_11_00 & (Rn - tmp2));

#	tmp1:1 = ( (oldQM_11_00 != 0) && (Rn > tmp0) ) | ( (oldQM_10_01 != 0) && (Rn < tmp0) );

#	QM_10_01:1 = ( ( ($(Q_FLAG) == 1) && ($(M_FLAG) == 0) ) || ( ($(Q_FLAG) == 1) && ($(M_FLAG) == 0) ) ) * 0xff;
#	QM_11_00:1 = ( ( ($(Q_FLAG) == 1) && ($(M_FLAG) == 1) ) || ( ($(Q_FLAG) == 0) && ($(M_FLAG) == 0) ) ) * 0xff;

#	$(Q_FLAG) = (QM_10_01 & tmp1) || (QM_11_00 & (tmp1 == 0));

#	$(T_FLAG) = $(Q_FLAG) == $(M_FLAG);
#	N_0t = Rn;
#@endif # DIV1_STRAIGHT_CODE
}

# Signed Double-Length Multiplication
# pattern 0011nnnnmmmm1101
# text    dmuls.l <REG_M>,<REG_N>
# arch    arch_sh2_up
:dmuls.l M_0t,N_0t          is OP_0=0x3 &  OP_1=0xd &  M_0t &  N_0t 
{
	local temp:8 = sext(M_0t) * sext(N_0t);
	MACL = temp[0,32];
	MACH = temp[32,32];
}

# Unsigned Double-Length Multiplication
# pattern 0011nnnnmmmm0101
# text    dmulu.l <REG_M>,<REG_N>
# arch    arch_sh2_up
:dmulu.l M_0t,N_0t          is OP_0=0x3 &  OP_1=0x5 &  M_0t &  N_0t {
	local temp:8 = zext(M_0t) * zext(N_0t);
	MACL = temp[0,32];
	MACH = temp[32,32];
}

# Decrement and Test
# pattern 0100nnnn00010000
# text    dt <REG_N>
# arch    arch_sh2_up
:dt N_0t                    is OP_0=0x4 & N_0t & OP_4=0x10 {

	N_0t = N_0t - 1;
	$(T_FLAG) = ( N_0t == 0 );
}


# Sign Extension
# pattern 0110nnnnmmmm1110
# text    exts.b <REG_M>,<REG_N>
# arch    arch_sh_up
:exts.b M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0xe {

	N_0t = sext(M_0t:1);
}

# Sign Extension
# pattern 0110nnnnmmmm1111
# text    exts.w <REG_M>,<REG_N>
# arch    arch_sh_up
:exts.w M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0xf {

	N_0t = sext(M_0t:2);
}


# Zero Extension
# pattern 0110nnnnmmmm1100
# text    extu.b <REG_M>,<REG_N>
# arch    arch_sh_up
:extu.b M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0xc {

	N_0t = zext(M_0t:1);
}


# Zero Extension
# pattern 0110nnnnmmmm1101
# text    extu.w <REG_M>,<REG_N>
# arch    arch_sh_up
:extu.w M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0xd {

	N_0t = zext(M_0t:2);
}


# Floating-Point Absolute Value
# pattern 1111nnnn01011101
# text    fabs <F_REG_N>
# arch    arch_sh2e_up
:fabs FRN_0                 is
      OP_0=0xf &  FRN_0 & DRN_1 & OP_4=0x5d {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0 = abs(FRN_0);
	goto <skip>;
	<doublePrecision>
	DRN_1 = abs(DRN_1);
	<skip>
}

# Floating-Point Addition
# pattern 1111nnnnmmmm0000
# text    fadd <F_REG_M>,<F_REG_N>
# arch    arch_sh2a_or_sh4_up
:fadd FRM_0,FRN_0           is
      OP_0=0xf & FRN_0 & DRN_1 & FRM_0 & DRM_1 & OP_1=0x0 {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0 = FRN_0 f+ FRM_0;
	goto <skip>;
	<doublePrecision>
	DRN_1 = DRN_1 f+ DRM_1;
	<skip>
}

# Floating-Point Comparison
# pattern 1111nnnnmmmm0100
# text    fcmp/eq <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fcmp^"/eq" FRM_0t,FRN_0t   is
            OP_0=0xf & FRN_0t & DRN_1t & FRM_0t & DRM_1t & OP_6 & OP_10 & OP_1=0x4 {
	if (!( $(FPSCR_PR) == 0 ) ) && (OP_6:1 == 0) && (OP_10:1 == 0) goto <doublePrecision>;
	$(T_FLAG) = ( FRN_0t f== FRM_0t );
	goto <skip>;
	<doublePrecision>
	$(T_FLAG) = ( DRN_1t f== DRM_1t );
	<skip>
}

# Floating-Point Comparison
# pattern 1111nnnnmmmm0101
# text    fcmp/gt <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fcmp^"/gt" FRM_0t,FRN_0t   is
            OP_0=0xf & FRM_0t & DRM_1t & FRN_0t & DRN_1t & OP_1=0x5 {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	$(T_FLAG) = ( FRN_0t f> FRM_0t );
	goto <skip>;
	<doublePrecision>
	$(T_FLAG) = ( DRN_1t f> DRM_1t );
	<skip>
}


# Double-Precision to Single-Precision Conversion
# pattern 1111nnn010111101
# text    fcnvds <D_REG_N>,FPUL
#   even though reserved to DblPrec, should decode, may not know at the time.
#   if this instruction shows up, it is most likely in DblPrec mode but not set
# arch    arch_sh2a_or_sh4_up
:fcnvds DRN_1t,FPUL         is OP_0=0xf & DRN_1t & OP_5=0x0bd & FPUL {

	# note: this instruction is undefined if not running in DblPrec mode.
	FPUL = float2float( DRN_1t );
}


# Single-Precision to Double-Precision Conversion
# pattern 1111nnn010101101
# text    fcnvsd FPUL,<D_REG_N>
#   even though reserved to DblPrec, should decode, may not know at the time.
#   if this instruction shows up, it is most likely in DblPrec mode but not set
# arch    arch_sh2a_or_sh4_up
:fcnvsd FPUL,DRN_1t         is OP_0=0xf & DRN_1t & OP_5=0xad & FPUL {

	# note: this instruction is undefined if not running in DblPrec mode.
	DRN_1t = float2float( FPUL );
}


# Floating-Point Division
# pattern 1111nnnnmmmm0011
# text    fdiv <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fdiv FRM_0t,FRN_0t         is
      OP_0=0xf & FRN_0t & DRN_1t & FRM_0t & DRM_1t & OP_1=0x3 {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0t = ( FRN_0t f/ FRM_0t );
	goto <skip>;
	<doublePrecision>
	DRN_1t = ( DRN_1t f/ DRM_1t );
	<skip>
}

# Floating-Point Inner Product
# pattern 1111nnmm11101101
# text    fipr <V_REG_M>,<V_REG_N>
#   even though reserved to DblPrec, should decode, may not know at the time.
#   if this instruction shows up, it is most likely in DblPrec mode but not set
# arch    arch_sh4_up
:fipr FVM_2t,FVN_2t         is OP_0=0xf & FVN_2t & FVM_2t & OP_4=0xed {

	# note: this instruction is undefined if not running in SinglePrec mode.
	if (!( $(FPSCR_PR) == 0 )) goto <skip>;
	# FVn dot FVm
	temp1:4 =          ( *[register]:4 (&:4 FVN_2t +  0) f* *[register]:4 (&:4 FVM_2t +  0) );
	temp1   = temp1 f+ ( *[register]:4 (&:4 FVN_2t +  4) f* *[register]:4 (&:4 FVM_2t +  4) );
	temp1   = temp1 f+ ( *[register]:4 (&:4 FVN_2t +  8) f* *[register]:4 (&:4 FVM_2t +  8) );
	temp1   = temp1 f+ ( *[register]:4 (&:4 FVN_2t + 12) f* *[register]:4 (&:4 FVM_2t + 12) );

	# summation goes to FR[n + 3]
	*[register]:4 (&:4 FVN_2t + 12) = temp1;
	<skip>
}

# 0.0 Load
# pattern 1111nnnn10001101
# text    fldi0 <F_REG_N>
# arch    arch_sh2e_up
#
# Note- The manual says this applies only to single float destination regs
#
:fldi0 FRN_0t               is OP_0=0xf & DRN_1t & FRN_0t & OP_4=0x8d {
	tmp1:4 = 0;
	FRN_0t = int2float(tmp1);
}

# 1.0 Load
# pattern 1111nnnn10011101
# text    fldi1 <F_REG_N>
# arch    arch_sh2e_up
#
# Note- the manual says FPSCR_PR applies only to single float regs, not to doubles.
# Manual also says FPSCR_PR should be 0, but gcc generates code where it's set to 1.
#
:fldi1 FRN_0t               is OP_0=0xf & FRN_0t & DRN_1t & OP_4=0x9d {
	tmp1:4 = 1;
	FRN_0t = int2float(tmp1);
}

# Transfer to System Register
# pattern 1111nnnn00011101
# text    flds <F_REG_N>,FPUL
# arch    # arch_sh2e_up
# Note: Field usage indicates FR[n], not FR[m].
:flds FRN_0t,FPUL           is OP_0=0xf &  FRN_0t &  OP_4=0x1d & FPUL {

	FPUL = FRN_0t;
}

# Integer to Floating-Point Conversion
# pattern 1111nnnn00101101
# text    float FPUL,<F_REG_N>
# arch    # arch_sh2e_up
:float FPUL,FRN_0t          is
       OP_0=0xf & OP_6 & FRN_0t & DRN_1t & OP_4=0x2d & FPUL {
	# If bit 6 is set this this is an odd FP register number, so need to do single float operation.
	# This is not in the manual but this seems to be the correct behavior.
	if (!( $(FPSCR_PR) == 0 ) ) && (OP_6:1 == 0x0) goto <doublePrecision>;
	FRN_0t = int2float( FPUL );
	goto <skip>;
	<doublePrecision>
	DRN_1t = int2float( FPUL );
	<skip>
}

# Floating-Point Multiply and Accumulate
# pattern 1111nnnnmmmm1110
# text    fmac FR0,<F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fmac FR0_t,FRM_0t,FRN_0t   is OP_0=0xf & FRN_0t & FRM_0t & OP_1=0xe & FR0_t {

	if (!( $(FPSCR_PR) == 0 )) goto <skip>;
	FRN_0t = ( FR0_t f* FRM_0t ) f+ FRN_0t;
	<skip>
}

# Floating-Point Transfer #1-4
# pattern 1111nnnnmmmm1100
# text    fmov <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
#
# Note- The manual says all the fmov insns should only look at the FPSCR_SZ flag
# to determine if this is a single or double float move. Ie, don't reference FPSCR_PR.
#
:fmov FRM_0,FRN_0           is
      OP_0=0xf & FRN_0 & XDRN & FRM_0 & XDRM & OP_1=0xc {
	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	FRN_0 = FRM_0;
	goto <skip>;
	<doubleWidth>
	XDRN = XDRM;
	<skip>
}

# Floating-Point Transfer #5-6
# pattern 1111nnn0mmmm1000
# text    fmov @<REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fmov.s M_0t_at1,FRN_0      is
      OP_0=0xf &  XDRN & FRN_0 & M_0t_at1 &  OP_1=0x8 {

	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	FRN_0 = *:4 M_0t_at1;
	goto <skip>;
	<doubleWidth>
	XDRN = *:8 M_0t_at1;
	<skip>
}

# Floating-Point Transfer #7-8
# pattern 1111nnnnmmmm1010
# text    fmov <F_REG_M>,@<REG_N>
# arch    arch_sh2e_up
#
# Note- Manual says to ignore FPSCR_PR but FPSCR_SZ must = 0
#
:fmov.s FRM_0,N_0t_at1      is

      OP_0=0xf &  N_0t_at1 &  FRM_0 & XDRM &  OP_1=0xa {
	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	*:4 N_0t_at1 = FRM_0;
	goto <skip>;
	<doubleWidth>
	*:8 N_0t_at1 = XDRM;
	<skip>
}

# Floating-Point Transfer #9-10
# pattern 1111nnnnmmmm1001
# text    fmov @<REG_M>+,<F_REG_N>
# arch    arch_sh2e_up
:fmov.s M_0t_at,FRN_0       is
      OP_0=0xf &  XDRN & FRN_0 &  M_0t_at &  OP_1=0x9 {

	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	FRN_0 = *:4 M_0t_at;
	M_0t_at = M_0t_at + 4;
	goto <skip>;
	<doubleWidth>
	XDRN = *:8 M_0t_at;
	M_0t_at = M_0t_at + 8;
	<skip>
}

# Floating-Point Transfer #11-12
# pattern 1111nnnnmmm01011
# text    fmov <F_REG_M>,@-<REG_N>
# arch    arch_sh2e_up
:fmov.s FRM_0,N_0t_at_neg   is
      OP_0=0xf &  N_0t_at_neg &  XDRM & FRM_0 &  OP_1=0xb {

	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 N_0t_at_neg = FRM_0;
	goto <skip>;
	<doubleWidth>
	N_0t_at_neg = N_0t_at_neg - 8;
	*:8 N_0t_at_neg = XDRM;
	<skip>
}

# Floating-Point Transfer #13-14
# pattern 1111nnnnmmmm0110
# text    fmov @(R0,<REG_M>),<F_REG_N>
# arch    arch_sh2e_up
:fmov.s M_0t_at_with_r0,FRN_0  is
      OP_0=0xf &  XDRN &  FRN_0 & M_0t_at_with_r0 &  OP_1=0x6 {

	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	FRN_0 = *:4 (r0 + M_0t_at_with_r0);
	goto <skip>;
	<doubleWidth>
	XDRN = *:8 (r0 + M_0t_at_with_r0);
	<skip>
}

# Floating-Point Transfer #15-16
# pattern 1111nnnnmmmm0111
# text    fmov @(R0,<REG_M>),<F_REG_N>
# arch    arch_sh2e_up
:fmov FRM_0,N_0t_at_with_r0 is
      OP_0=0xf  &  N_0t_at_with_r0 &  XDRM & FRM_0 &  OP_1=0x7 {

	if (!( $(FPSCR_SZ) == 0 )) goto <doubleWidth>;
	*:4 (r0 + N_0t_at_with_r0) = FRM_0;
	goto <skip>;
	<doubleWidth>
	*:8 (r0 + N_0t_at_with_r0) = XDRM;
	<skip>
}

# Floating-Point Multiplication
# pattern 1111nnnnmmmm0010
# text    fmul <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fmul FRM_0t,FRN_0t         is
      OP_0=0xf & FRN_0t & DRN_1t & FRM_0t & DRM_1t & OP_1=0x2 {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0t = FRN_0t f* FRM_0t;
	goto <skip>;
	<doublePrecision>
	DRN_1t = DRN_1t f* DRM_1t;
	<skip>
}

# Floating-Point Sign Inversion
# pattern 1111nnnn01001101
# text    fneg <F_REG_N>
# arch    arch_sh2e_up
:fneg FRN_0t                is
      OP_0=0xf & FRN_0t & DRN_1t & OP_6 & OP_4=0x4d {

	if (!( $(FPSCR_PR) == 0 )) && (OP_6:1 == 0) goto <doublePrecision>;
	FRN_0t = f- FRN_0t;
	goto <skip>;
	<doublePrecision>
	DRN_1t = f- DRN_1t;
	<skip>
}

# FR Bit Inversion
# pattern 1111101111111101
# text    frchg
# arch    # arch_sh4_up
:frchg                      is
    OP_3=0xfbfd {

	if (!( $(FPSCR_PR) == 0 )) goto <skip>;
	$(FPSCR_FR) = !$(FPSCR_FR);
	FPSCR = FPSCR ^ 0x00200000;
	<skip>
}


# SZ Bit Inversion
# pattern 1111001111111101
# text    fschg
# arch    arch_sh2a_or_sh4a_up (not in sh4)
:fschg                      is 
    OP_3=0xf3fd {

	if (!( $(FPSCR_PR) == 0 )) goto <skip>;
	FPSCR = FPSCR ^ 0x00100000;
	<skip>
}

# PR Bit Inversion
# pattern 1111011111111101
# text    fpchg
# arch    arch_sh2a_or_sh4_up
:fpchg                      is
    OP_3=0xf7fd {
	FPSCR = FPSCR ^ 0x00080000;
}

# Floating-Point Square Root
# pattern 1111nnnn01101101
# text    fsqrt <F_REG_N>
# arch    # arch_sh2a_or_sh3e_up
:fsqrt FRN_0t               is
       OP_0=0xf & FRN_0t & DRN_1t & OP_4=0x6d {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0t = sqrt( FRN_0t );
	goto <skip>;
	<doublePrecision>
	DRN_1t = sqrt( DRN_1t );
	<skip>
}

# Floating Point Square Reciprocal Approximate
# pattern 1111nnnn01111101			
# text	  fsrra <F_REG_N>
# arch    # arch_sh2a_or_sh3e_up
:fsrra FRN_0	is
	OP_0=0xF & FRN_0 & OP_4=0x7D {
	if (( $(FPSCR_PR) != 0 )) goto inst_next;       
	FRN_0 = int2float(1 :4) f/ sqrt( FRN_0 );
}

define pcodeop sin;
define pcodeop cos;

# Floating Point Sine And Cosine Approximate
# pattern 1111nnn011111101
# text	  fsca FPUL,<F_REG_N>
# arch    # arch_sh2a_or_sh3e_up
:fsca FPUL,DRN_0 is
	OP_0=0xF & FPUL & DRN_0 & FRN_1 & FRN_2  & OP_4=0xFD {
	if (( $(FPSCR_PR) != 0 )) goto inst_next;       
	
	angle:4 = int2float(0 :4);    			# float
        fraction:4 = 0x0000FFFF;			# long
	
	fraction = fraction & FPUL;			# extract sub-rotation
	angle 	 = int2float(fraction);			# convert to float
	
	two_pi:4 = 0x40c90fdb;				# 6.2831855.. as 32-bit float
    angle = (two_pi f* angle) f/ int2float(65536 :4);   # convert to radian
    
	local _sin:4 = sin(angle);			# call fake sin & cos
	local _cos:4 = cos(angle);
	
	FRN_1 = float2float(_sin);			# _sin goes to FR[n]
	FRN_2 = float2float(_cos);           		# _cos goes to FR[n+1]
}

# Transfer from System Register
# pattern 1111nnnn00001101
# text    fsts FPUL,<F_REG_N>
# arch    arch_sh2e_up
:fsts FPUL,FRN_0t           is OP_0=0xf & FRN_0t & OP_4=0xd & FPUL {

	FRN_0t = FPUL;
}

# Floating-Point Subtraction
# pattern 1111nnnnmmmm0001
# text    fsub <F_REG_M>,<F_REG_N>
# arch    arch_sh2e_up
:fsub FRM_0t,FRN_0t         is
      OP_0=0xf & FRN_0t & DRN_1t & FRM_0t & DRM_1t & OP_1=0x1 {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FRN_0t = FRN_0t f- FRM_0t;
	goto <skip>;
	<doublePrecision>
	DRN_1t = DRN_1t f- DRM_1t;
	<skip>
}

# Conversion to Integer
# pattern 1111nnnn00111101
# text    ftrc <F_REG_N>,FPUL
# # arch    arch_sh2e_up
# Note: Field usage indicates FR[n], not FR[m].
:ftrc FRN_0t,FPUL           is
      OP_0=0xf & FRN_0t & DRN_1t & OP_4=0x3d & FPUL {

	if (!( $(FPSCR_PR) == 0 )) goto <doublePrecision>;
	FPUL = trunc( FRN_0t );
	goto <skip>;
	<doublePrecision>
	FPUL = trunc( DRN_1t );
	<skip>
}

# Vector Transformation
# pattern 1111nn0111111101
# text    ftrv XMTRX_M4,<V_REG_n>
# arch    # arch_sh4_up
:ftrv XMTRX_t,FVN_2t        is OP_0=0xf &  FVN_2t &  OP_8=0x1fd & XMTRX_t {

	if (!( $(FPSCR_PR) == 0 )) goto <skip>;
	temp1:4 =          ( xf0  f* *[register]:4 (&:4 FVN_2t +  0) );
	temp1   = temp1 f+ ( xf4  f* *[register]:4 (&:4 FVN_2t +  4) );
	temp1   = temp1 f+ ( xf8  f* *[register]:4 (&:4 FVN_2t +  8) );
	temp1   = temp1 f+ ( xf12 f* *[register]:4 (&:4 FVN_2t + 12) );

	temp2:4 =          ( xf1  f* *[register]:4 (&:4 FVN_2t +  0) );
	temp2   = temp2 f+ ( xf5  f* *[register]:4 (&:4 FVN_2t +  4) );
	temp2   = temp2 f+ ( xf9  f* *[register]:4 (&:4 FVN_2t +  8) );
	temp2   = temp2 f+ ( xf13 f* *[register]:4 (&:4 FVN_2t + 12) );

	temp3:4 =          ( xf2  f* *[register]:4 (&:4 FVN_2t +  0) );
	temp3   = temp3 f+ ( xf6  f* *[register]:4 (&:4 FVN_2t +  4) );
	temp3   = temp3 f+ ( xf10 f* *[register]:4 (&:4 FVN_2t +  8) );
	temp3   = temp3 f+ ( xf14 f* *[register]:4 (&:4 FVN_2t + 12) );

	temp4:4 =          ( xf3  f* *[register]:4 (&:4 FVN_2t +  0) );
	temp4   = temp4 f+ ( xf7  f* *[register]:4 (&:4 FVN_2t +  4) );
	temp4   = temp4 f+ ( xf11 f* *[register]:4 (&:4 FVN_2t +  8) );
	temp4   = temp4 f+ ( xf15 f* *[register]:4 (&:4 FVN_2t + 12) );

	# summation goes to FR[n + 0] through FR[n + 3]
	*[register]:4 (&:4 FVN_2t +  0) = temp1;
	*[register]:4 (&:4 FVN_2t +  4) = temp2;
	*[register]:4 (&:4 FVN_2t +  8) = temp3;
	*[register]:4 (&:4 FVN_2t + 12) = temp4;
	<skip>
}

# Invalidate Instruction Cache block
# pattern 0000nnnn11100011
# text    icib @<REG_N>
# arch    arch_sh_up
define pcodeop InvalidateCacheBlock;

:icbi N_0t_at1              is OP_0=0x0 & N_0t_at1 & OP_4=0xe3 {
	tmp:4 = N_0t_at1;
	InvalidateCacheBlock(tmp);
}

# Unconditional Branch
# pattern 0100nnnn00101011
# text    jmp @<REG_N>
# arch    arch_sh_up
:jmp N_0tjmp                is OP_0=0x4 & N_0tjmp & OP_4=0x2b {

	PC = N_0tjmp;
	tmp:4 = PC;
	delayslot(1);
	goto [tmp];
}


# Branch to Subroutine Procedure
# pattern 0100nnnn00001011
# text    jsr @<REG_N>
# arch    arch_sh_up
:jsr N_0tjmp                is OP_0=0x4 & N_0tjmp & OP_4=0xb {

	PR = inst_next;
	PC = N_0tjmp;
	tmp:4 = PC;
	delayslot(1);
	call [tmp];
}


# Load to Control Register
# pattern 0100nnnn00001110
# text    ldc <REG_N>,SR
# arch    arch_sh_up
:ldc N_0t_sr                is OP_0=0x4 & N_0t_sr & OP_4=0xe {

	splitSRregister();
	SR = (N_0t_sr & 0x700083f3);
}


# Load to Control Register
# pattern 0100nnnn00011110
# text    ldc <REG_N>,GBR
# arch    arch_sh_up
:ldc N_0t_gbr               is OP_0=0x4 & N_0t_gbr & OP_4=0x1e {

	GBR = N_0t_gbr;
}


# Load to Control Register
# pattern 0100nnnn00101110
# text    ldc <REG_N>,VBR
# arch    arch_sh_up
:ldc N_0t_vbr               is OP_0=0x4 & N_0t_vbr & OP_4=0x2e {

	VBR = N_0t_vbr;
}


# pattern 0100nnnn00111110
# text    ldc <REG_N>,SSR
# arch    arch_sh3_nommu_up
:ldc N_0t_ssr               is OP_0=0x4 & N_0t_ssr & OP_4=0x3e {

	SSR = N_0t_ssr;
}


# pattern 0100nnnn01001110
# text    ldc <REG_N>,SPC
# arch    arch_sh3_nommu_up
:ldc N_0t_spc               is OP_0=0x4 & N_0t_spc & OP_4=0x4e {

	SPC = N_0t_spc;
}


# pattern 0100nnnn11111010
# text    ldc <REG_N>,DBR
# arch    arch_sh4_nommu_nofpu_up
:ldc N_0t_dbr               is OP_0=0x4 & N_0t_dbr & OP_4=0xfa {

	DBR = N_0t_dbr;
}

# pattern 0100nnnn1xxx1110
# text    ldc <REG_N>,Rn_BANK
# arch    arch_sh3_nommu_up
:ldc N_0t_bank,BANKt        is OP_0=0x4 & N_0t_bank & OP_9=0x1 & BANKt & OP_1=0xe {

	BANKt = N_0t_bank;
}


# Load to Control Register
# pattern 0100nnnn00000111
# text    ldc.l @<REG_N>+,SR
# arch    arch_sh_up
:ldc.l N_0t_sr1             is OP_0=0x4 & N_0t_sr1 & OP_4=0x7 {

	SR = (*:4 ( N_0t_sr1 )) & 0x700083F3;
	splitSRregister();
	N_0t_sr1 = N_0t_sr1 + 4;
}


# Load to Control Register
# pattern 0100nnnn00010111
# text    ldc.l @<REG_N>+,GBR
# arch    arch_sh_up
:ldc.l N_0t_gbr1            is OP_0=0x4 & N_0t_gbr1 & OP_4=0x17 {

	GBR = *:4 ( N_0t_gbr1 );
	N_0t_gbr1 = N_0t_gbr1 + 4;
}


# Load to Control Register
# pattern 0100nnnn00100111
# text    ldc.l @<REG_N>+,VBR
# arch    arch_sh_up
:ldc.l N_0t_vbr1            is OP_0=0x4 & N_0t_vbr1 & OP_4=0x27 {

	VBR = *:4 ( N_0t_vbr1 );
	N_0t_vbr1 = N_0t_vbr1 + 4;
}


# pattern 0100nnnn00110111
# text    ldc.l @<REG_N>+,SSR
# arch    arch_sh3_nommu_up
:ldc.l N_0t_ssr1            is OP_0=0x4 & N_0t_ssr1 & OP_4=0x37 {

	SSR = *:4 ( N_0t_ssr1 );
	N_0t_ssr1 = N_0t_ssr1 + 4;
}


# pattern 0100nnnn01000111
# text    ldc.l @<REG_N>+,SPC
# arch    arch_sh3_nommu_up
:ldc.l N_0t_spc1            is OP_0=0x4 & N_0t_spc1 & OP_4=0x47 {

	SPC = *:4 ( N_0t_spc1 );
	N_0t_spc1 = N_0t_spc1 + 4;
}


# pattern 0100nnnn11110110
# text    ldc.l @<REG_N>+,DBR
# arch    arch_sh4_nommu_nofpu_up
:ldc.l N_0t_dbr1            is OP_0=0x4 & N_0t_dbr1 & OP_4=0xf6 {

	DBR = *:4 ( N_0t_dbr1 );
	N_0t_dbr1 = N_0t_dbr1 + 4;
}


# pattern 0100nnnn1xxx0111
# text    ldc.l @<REG_N>+,Rn_BANK
# arch    arch_sh3_nommu_up
:ldc.l N_0t_bank1,BANKt     is OP_0=0x4 & N_0t_bank1 & OP_9=0x1 & BANKt & OP_1=0x7 {

	BANKt = *:4 ( N_0t_bank1 );
	N_0t_bank1 = N_0t_bank1 + 4;
}


# Load to FPU System Register
# pattern 0100nnnn01011010
# text    lds <REG_N>,FPUL
# arch    arch_sh2e_up
:lds N_0t_fpul              is OP_0=0x4 & N_0t_fpul & OP_4=0x5a {

	FPUL = N_0t_fpul;
}


# Load to FPU System Register
# pattern 0100nnnn01010110
# text    lds.l @<REG_M>+,FPUL
# arch    arch_sh2e_up
:lds.l N_0t_fpul1           is OP_0=0x4 & N_0t_fpul1 & OP_4=0x56 {

	FPUL = *:4 ( N_0t_fpul1 );
	N_0t_fpul1 = N_0t_fpul1 + 4;
}


# Load to FPU System Register
# pattern 0100nnnn01101010
# text    lds <REG_M>,FPSCR
# arch    arch_sh2e_up
# Note: FPSCR context cannot be supported from this instruction; contents of N_0 not known at disassembly time.
:lds N_0t_fpscr             is OP_0=0x4 & N_0t_fpscr & OP_4=0x6a {

	FPSCR = N_0t_fpscr & 0x003FFFFF;
	splitFPSCRregister();
}


# Load to FPU System Register
# pattern 0100nnnn01100110
# text    lds.l @<REG_M>+,FPSCR
# arch    arch_sh2e_up
# Note: FPSCR context cannot be supported from this instruction; contents of N_0 not known at disassembly time.
:lds.l N_0t_fpscr1          is OP_0=0x4 & N_0t_fpscr1 & OP_4=0x66 {

	FPSCR = (*:4 ( N_0t_fpscr1 )) & 0x003FFFFF;
	splitFPSCRregister();
	N_0t_fpscr1 = N_0t_fpscr1 + 4;
}


# Load to FPU System Register
# pattern 0100nnnn00001010
# text    lds <REG_N>,MACH
# arch    arch_sh_up
:lds N_0t_mach              is OP_0=0x4 & N_0t_mach & OP_4=0xa {

	MACH = N_0t_mach;
}


# Load to FPU System Register
# pattern 0100nnnn00011010
# text    lds <REG_N>,MACL
# arch    arch_sh_up
:lds N_0t_macl              is OP_0=0x4 & N_0t_macl & OP_4=0x1a {

	MACL = N_0t_macl;
}


# Load to FPU System Register
# pattern 0100nnnn00101010
# text    lds <REG_N>,PR
# arch    arch_sh_up
:lds N_0t_pr                is OP_0=0x4 & N_0t_pr & OP_4=0x2a {

	PR = N_0t_pr;
}


# Load to FPU System Register
# pattern 0100nnnn00000110
# text    lds.l @<REG_N>+,MACH
# arch    arch_sh_up
:lds.l N_0t_mach1           is OP_0=0x4 & N_0t_mach1 & OP_4=0x6 {

	MACH = *:4 ( N_0t_mach1 );
	N_0t_mach1 = N_0t_mach1 + 4;
}


# Load to FPU System Register
# pattern 0100nnnn00010110
# text    lds.l @<REG_N>+,MACL
# arch    arch_sh_up
:lds.l N_0t_macl1           is OP_0=0x4 & N_0t_macl1 & OP_4=0x16 {

	MACL = *:4 ( N_0t_macl1 );
	N_0t_macl1 = N_0t_macl1 + 4;
}


# Load to FPU System Register
# pattern 0100nnnn00100110
# text    lds.l @<REG_N>+,PR
# arch    arch_sh_up
:lds.l N_0t_pr1             is OP_0=0x4 & N_0t_pr1 & OP_4=0x26 {

	PR = *:4 ( N_0t_pr1 );
	N_0t_pr1 = N_0t_pr1 + 4;
}

define pcodeop LoadTranslationLookasideBuffer;

# Load to TLB
# pattern 0000000000111000
# text    ldtlb
# arch    arch_sh3_up
:ldtlb                      is OP_3=0x38 { LoadTranslationLookasideBuffer(); }

# Double-Precision Multiply-and-Accumulate Operation
# pattern 0000nnnnmmmm1111
# text    mac.l @<REG_M>+,@<REG_N>+
# arch    arch_sh2_up
define pcodeop mac_lOp;
:mac.l M_0t_at,N_0t_at      is OP_0=0x0 &  OP_1=0xf &  M_0t_at &  N_0t_at {
	local tmpM:8 = sext(*:4 M_0t_at);
	local tmpN:8 = sext(*:4 N_0t_at);
	local mac:8 = zext(MACL) + (zext(MACH) << 32);
	local product:8 = tmpM * tmpN;
	if ($(S_FLAG) == 0) goto <unsaturated>;
	mac = mac_lOp(mac,product);
	goto <end>;
<unsaturated>
    mac = mac + product;
<end>
	MACL = mac[0,32];
	MACH = mac[32,32];
	M_0t_at = M_0t_at + 4;
	N_0t_at = N_0t_at + 4;
}

# Single-Precision Multiply-and-Accumulate Operation
# pattern 0100nnnnmmmm1111
# text    mac.w @<REG_M>+,@<REG_N>+
# arch    arch_sh_up
define pcodeop mac_wOp;
:mac.w M_0t_at,N_0t_at      is OP_0=0x4 &  OP_1=0xf &  M_0t_at &  N_0t_at {
	local tmpM:4 = sext(*:2 M_0t_at);
	local tmpN:4 = sext(*:2 N_0t_at);
	local mac:8 = zext(MACL) + (zext(MACH) << 32);
	local product:4 = tmpN * tmpN;
	if ($(S_FLAG) == 0) goto <unsaturated>;
	mac = mac_wOp(mac,product);
    goto <end>;
<unsaturated>
    mac = mac + sext(product);
<end>
	MACL = mac[0,32];
	MACH = mac[32,32];
	M_0t_at = M_0t_at + 2;
	N_0t_at = N_0t_at + 2;
}

# Data Transfer
# pattern 0110nnnnmmmm0011
# text    mov <REG_M>,<REG_N>
# arch    arch_sh_up
:mov M_0t,N_0t              is OP_0=0x6 & N_0t & M_0t & OP_1=0x3 {

	N_0t = M_0t;
}


# Data Transfer
# pattern 0010nnnnmmmm0000
# text    mov.b <REG_M>,@<REG_N>
# arch    arch_sh_up
:mov.b M_0t,N_0t_at1        is OP_0=0x2 & N_0t_at1 & M_0t & OP_1=0x0 {

	*:1 ( N_0t_at1 ) = M_0t:1;
}


# Data Transfer
# pattern 0010nnnnmmmm0001
# text    mov.w <REG_M>,@<REG_N>
# arch    arch_sh_up
:mov.w M_0t,N_0t_at1        is OP_0=0x2 & N_0t_at1 & M_0t & OP_1=0x1 {

	*:2 ( N_0t_at1 ) = M_0t:2;
}


# Data Transfer
# pattern 0010nnnnmmmm0010
# text    mov.l <REG_M>,@<REG_N>
# arch    arch_sh_up
:mov.l M_0t,N_0t_at1        is OP_0=0x2 & N_0t_at1 & M_0t & OP_1=0x2 {

	*:4 ( N_0t_at1 ) = M_0t;
}


# Data Transfer
# pattern 0110nnnnmmmm0000
# text    mov.b @<REG_M>,<REG_N>
# arch    arch_sh_up
:mov.b M_0t_at1,N_0t        is OP_0=0x6 & N_0t & M_0t_at1 & OP_1=0x0 {

	N_0t = sext( *:1 ( M_0t_at1 ) );
}


# Data Transfer
# pattern 0110nnnnmmmm0001
# text    mov.w @<REG_M>,<REG_N>
# arch    arch_sh_up
:mov.w M_0t_at1,N_0t        is OP_0=0x6 & N_0t & M_0t_at1 & OP_1=0x1 {

	N_0t = sext( *:2 ( M_0t_at1 ) );
}


# Data Transfer
# pattern 0110nnnnmmmm0010
# text    mov.l @<REG_M>,<REG_N>
# arch    arch_sh_up
:mov.l M_0t_at1,N_0t        is OP_0=0x6 & N_0t & M_0t_at1 & OP_1=0x2 {

	N_0t = *:4 ( M_0t_at1 );
}


# Data Transfer
# pattern 0010nnnnmmmm0100
# text    mov.b <REG_M>,@-<REG_N>
# arch    arch_sh_up
:mov.b M_0t,N_0t_at_neg     is OP_0=0x2 & N_0t_at_neg & M_0t & OP_1=0x4 {

	N_0t_at_neg = N_0t_at_neg - 1;
	*:1 ( N_0t_at_neg ) = M_0t:1;
}


# Data Transfer
# pattern 0010nnnnmmmm0101
# text    mov.w <REG_M>,@-<REG_N>
# arch    arch_sh_up
:mov.w M_0t,N_0t_at_neg     is OP_0=0x2 & N_0t_at_neg & M_0t & OP_1=0x5 {

	N_0t_at_neg = N_0t_at_neg - 2;
	*:2 ( N_0t_at_neg ) = M_0t:2;
}


# Data Transfer
# pattern 0010nnnnmmmm0110
# text    mov.l <REG_M>,@-<REG_N>
# arch    arch_sh_up
:mov.l M_0t,N_0t_at_neg     is OP_0=0x2 & N_0t_at_neg & M_0t & OP_1=0x6 {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = M_0t;
}


# Data Transfer
# pattern 0110nnnnmmmm0100
# text    mov.b @<REG_M>+,<REG_N>
# arch    arch_sh_up
:mov.b M_0t_at,N_0t         is OP_0=0x6 & N_0t & M_0t_at & OP_1=0x4 {

	N_0t = sext( *:1 ( M_0t_at ) );
	M_0t_at = M_0t_at + 1;
}


# Data Transfer
# pattern 0110nnnnmmmm0101
# text    mov.w @<REG_M>+,<REG_N>
# arch    arch_sh_up
:mov.w M_0t_at,N_0t         is OP_0=0x6 & N_0t & M_0t_at & OP_1=0x5 {

	N_0t = sext( *:2 ( M_0t_at ) );
	M_0t_at = M_0t_at + 2;
}


# Data Transfer
# pattern 0110nnnnmmmm0110
# text    mov.l @<REG_M>+,<REG_N>
# arch    arch_sh_up
:mov.l M_0t_at,N_0t         is OP_0=0x6 & N_0t & M_0t_at & OP_1=0x6 {

	N_0t = *:4 ( M_0t_at );
	M_0t_at = M_0t_at + 4;
}


# Data Transfer
# pattern 0000nnnnmmmm0100
# text    mov.b <REG_M>,@(R0,<REG_N>)
# arch    arch_sh_up
:mov.b M_0t,N_0t_at_with_r0 is OP_0=0x0 & N_0t_at_with_r0 & M_0t & OP_1=0x4 {

	*:1 ( r0 + N_0t_at_with_r0 ) = M_0t:1;
}


# Data Transfer
# pattern 0000nnnnmmmm0101
# text    mov.w <REG_M>,@(R0,<REG_N>)
# arch    arch_sh_up
:mov.w M_0t,N_0t_at_with_r0 is OP_0=0x0 & N_0t_at_with_r0 & M_0t & OP_1=0x5 {

	*:2 ( r0 + N_0t_at_with_r0 ) = M_0t:2;
}


# Data Transfer
# pattern 0000nnnnmmmm0110
# text    mov.l <REG_M>,@(R0,<REG_N>)
# arch    arch_sh_up
:mov.l M_0t,N_0t_at_with_r0 is OP_0=0x0 & N_0t_at_with_r0 & M_0t & OP_1=0x6 {

	*:4 ( r0 + N_0t_at_with_r0 ) = M_0t;
}


# Data Transfer
# pattern 0000nnnnmmmm1100
# text    mov.b @(R0,<REG_M>),<REG_N>
# arch    arch_sh_up
:mov.b M_0t_at_with_r0,N_0t is OP_0=0x0 & N_0t & M_0t_at_with_r0 & OP_1=0xc {

	N_0t = sext( *:1 ( r0 + M_0t_at_with_r0 ) );
}


# Data Transfer
# pattern 0000nnnnmmmm1101
# text    mov.w @(R0,<REG_M>),<REG_N>
# arch    arch_sh_up
:mov.w M_0t_at_with_r0,N_0t is OP_0=0x0 & N_0t & M_0t_at_with_r0 & OP_1=0xd {

	N_0t = sext( *:2 ( r0 + M_0t_at_with_r0 ) );
}


# Data Transfer
# pattern 0000nnnnmmmm1110
# text    mov.l @(R0,<REG_M>),<REG_N>
# arch    arch_sh_up
:mov.l M_0t_at_with_r0,N_0t is OP_0=0x0 & N_0t & M_0t_at_with_r0 & OP_1=0xe {

	N_0t = *:4 ( r0 + M_0t_at_with_r0 );
}


# Data Transfer
# pattern 1110nnnniiiiiiii
# text    mov #<imm>,<REG_N>
# arch    arch_sh_up
:mov I_0t,N_0t              is OP_0=0xe & N_0t & I_0t {

	N_0t = I_0t; # NOTE I_0t already signed extended
}


# Data Transfer
# pattern 1001nnnndddddddd
# text    mov.w @(<disp>,PC),<REG_N>
# arch    arch_sh_up
:mov.w U_0t_2pc,N_0t        is OP_0=0x9 & N_0t & U_0t_2pc {

	N_0t = sext( U_0t_2pc ); # NOTE U_0t_2pc units is bytes
}


# Data Transfer
# pattern 1101nnnndddddddd
# text    mov.l @(<disp>,PC),<REG_N>
# arch    arch_sh_up
:mov.l U_0t_4pc,N_0t        is OP_0=0xd & N_0t & U_0t_4pc {

	N_0t = U_0t_4pc; # NOTE U_0t_4pc units is bytes
}


# Data Transfer
# pattern 11000100dddddddd
# text    mov.b @(<disp>,GBR),R0
# arch    arch_sh_up
:mov.b U_0t_gbr_at_1,r0     is OP_2=0xc4 & U_0t_gbr_at_1 & r0 {

	r0 = sext( *:1 ( U_0t_gbr_at_1 ) );
}


# Data Transfer
# pattern 11000101dddddddd
# text    mov.w @(<disp>,GBR),R0
# arch    arch_sh_up
:mov.w U_0t_gbr_at_2,r0     is OP_2=0xc5 & U_0t_gbr_at_2 & r0 {

	r0 = sext( *:2 ( U_0t_gbr_at_2 ) ); # NOTE U_0t_gbr_at_2 units is bytes
}


# Data Transfer
# pattern 11000110dddddddd
# text    mov.l @(<disp>,GBR),R0
# arch    arch_sh_up
:mov.l U_0t_gbr_at_4,r0     is OP_2=0xc6 & U_0t_gbr_at_4 & r0 {

	r0 = *:4 ( U_0t_gbr_at_4 ); # NOTE U_0t_gbr_at_4 units is bytes
}


# Data Transfer
# pattern 11000000dddddddd
# text    mov.b R0,@(<disp>,GBR)
# arch    arch_sh_up
:mov.b r0,U_0t_gbr_at_1     is OP_2=0xc0 & U_0t_gbr_at_1 & r0 {

	*:1 ( U_0t_gbr_at_1 ) = r0:1;
}


# Data Transfer
# pattern 11000001dddddddd
# text    mov.w R0,@(<disp>,GBR)
# arch    arch_sh_up
:mov.w r0,U_0t_gbr_at_2     is OP_2=0xc1 & U_0t_gbr_at_2 & r0 {

	*:2 ( U_0t_gbr_at_2 ) = r0:2; # NOTE U_0t_gbr_at_2 units is bytes
}


# Data Transfer
# pattern 11000010dddddddd
# text    mov.l R0,@(<disp>,GBR)
# arch    arch_sh_up
:mov.l r0, U_0t_gbr_at_4    is OP_2=0xc2 & U_0t_gbr_at_4 & r0 {

	*:4 ( U_0t_gbr_at_4 ) = r0; # NOTE U_0t_4_at_gbr_r0_1 units is bytes
}


# Data Transfer
# pattern 10000000mmmmdddd
# text    mov.b R0,@(<disp>,<REG_M>)
# arch    arch_sh_up
:mov.b r0,U_2t_M0_dispr01   is OP_2=0x80 & M_0t & U_2t_M0_dispr01 & r0 {

	*:1 ( U_2t_M0_dispr01 + M_0t ) = r0:1;
}


# Data Transfer
# pattern 10000001mmmmdddd
# text    mov.w R0,@(<disp>,<REG_M>)
# arch    arch_sh_up
:mov.w r0,U_2t_M0_dispr02   is OP_2=0x81 & M_0t & U_2t_M0_dispr02 & r0 {

	*:2 ( U_2t_M0_dispr02 + M_0t ) = r0:2; # NOTE U_2t_M0_dispr02 units is bytes
}


# Data Transfer
# pattern 0001nnnnmmmmdddd
# text    mov.l <REG_M>,@(<disp>,<REG_N>)
# arch    arch_sh_up
:mov.l M_0t,U_2t_N0_dispr04 is OP_0=0x1 & M_0t & N_0t & U_2t_N0_dispr04 {

	*:4 ( U_2t_N0_dispr04 + N_0t ) = M_0t; # NOTE U_2t_N0_dispr04 units is bytes 
}


# Data Transfer
# pattern 10000100mmmmdddd
# text    mov.b @(<disp>,<REG_M>),R0
# arch    arch_sh_up
:mov.b U_2t_M0_dispr01,r0   is OP_2=0x84 & M_0t & U_2t_M0_dispr01 & r0 {

	r0 = sext( *:1 ( U_2t_M0_dispr01 + M_0t ) );
}


# Data Transfer
# pattern 10000101mmmmdddd
# text    mov.w @(<disp>,<REG_M>),R0
# arch    arch_sh_up
:mov.w U_2t_M0_dispr02,r0   is OP_2=0x85 & U_2t_M0_dispr02 & M_0t & r0 {

	r0 = sext( *:2 ( U_2t_M0_dispr02 + M_0t ) ); # NOTE U_2t_M0_dispr02 units is bytes
}


# Data Transfer
# pattern 0101nnnnmmmmdddd
# text    mov.l @(<disp>,<REG_M>),<REG_N>
# arch    arch_sh_up
:mov.l U_2t_M0_dispr04,N_0t is OP_0=0x5 & N_0t & M_0t & U_2t_M0_dispr04 {

	N_0t = *:4 ( U_2t_M0_dispr04 + M_0t ); # NOTE U_2t_M0_dispr04 units is bytes
}


# Effective Address Transfer
# pattern 11000111iiiiiiii
# text    mova @(<disp>,PC),R0
# arch    arch_sh_up
:mova U_0t_4pc,r0           is OP_2=0xc7 & U_0t_4pc & r0 {

	r0 = &U_0t_4pc; # NOTE U_0t_4pc units is bytes
}


# MOVe with Cache block Allocation
# pattern 0000nnnn11000011
# text    movca.l R0,@<REG_N>
# arch    arch_sh4_nommu_nofpu_up
:movca.l N_0txx             is OP_0=0x0 & N_0txx & OP_4=0xc3 {

	*:4 ( N_0txx ) = r0; # NOTE ignore cache issues
}

# T Bit Transfer
# pattern 0000nnnn00101001
# text    movt <REG_N>
# arch 
:movt N_0t                  is OP_0=0x0 & N_0t & OP_4=0x29 {

	N_0t = zext($(T_FLAG));
}

# Move Unaligned Long
# pattern 0100mmmm10101001
# text    movua.l @<REG_M>,R0
# arch    
:movua.l N_0t_at,r0               is OP_0=0x4 & N_0t_at & r0 & OP_4=0xA9  {
	r0 = (*:4 ( N_0t_at ));                 
}

# Move Unaligned Long Pointer
# pattern 0100mmmm11101001
# text    movua.l @<REG_M>+,R0
# arch    
:movua.l N_0t_at,r0               is OP_0=0x4 & N_0t_at & r0 & OP_4=0xE9 {
	r0 = (*:4 ( N_0t_at ));
	N_0t_at = N_0t_at + 4;
}

:movua.l N_0t_at,r0               is OP_0=0x4 & N_0t_at & N_0=0 & r0 & OP_4=0xE9 {
	r0 = (*:4 ( N_0t_at ));
}

# Double-Precision Multiplication
# pattern 0000nnnnmmmm0111
# text    mul.l <REG_M>,<REG_N>
# arch 
:mul.l M_0t,N_0t            is OP_0=0x0 & N_0t & M_0t & OP_1=0x7 {

	MACL = N_0t * M_0t;
}


# Signed Multiplication
# pattern 0010nnnnmmmm1111
# text    muls.w <REG_M>,<REG_N>
# arch 
:muls.w M_0t,N_0t           is OP_0=0x2 & N_0t & M_0t & OP_1=0xf {

	MACL = sext(N_0t:2) * sext(M_0t:2);
}


# Unsigned Multiplication
# pattern 0010nnnnmmmm1110
# text    mulu.w <REG_M>,<REG_N>
# arch 
:mulu.w M_0t,N_0t           is OP_0=0x2 & N_0t & M_0t & OP_1=0xe {

	MACL = zext(N_0t:2) * zext(M_0t:2);
}


# Sign Inversion
# pattern 0110nnnnmmmm1011
# text    neg <REG_M>,<REG_N>
# arch 
:neg M_0t,N_0t              is OP_0=0x6 & N_0t & M_0t & OP_1=0xb {

	N_0t = -M_0t;
}


# Sign Inversion with Borrow
# pattern 0110nnnnmmmm1010
# text    negc <REG_M>,<REG_N>
# arch 
:negc M_0t,N_0t             is OP_0=0x6 & N_0t & M_0t & OP_1=0xa {
	local Tcopy:4 = zext($(T_FLAG));
	$(T_FLAG) = 0 != M_0t;
	local result:4 = - M_0t;
	$(T_FLAG) = $(T_FLAG) || (result < Tcopy);
	N_0t = result - Tcopy;
}


# No Operation
# pattern 0000000000001001
# text    nop
# arch 
:nop                        is OP_3=0x9 {  } # Empty on purpose


# Bit Inversion
# pattern 0110nnnnmmmm0111
# text    not <REG_M>,<REG_N>
# arch 
:not M_0t,N_0t              is OP_0=0x6 & N_0t & M_0t & OP_1=0x7 {

	N_0t = ~M_0t;
}

define pcodeop CacheBlockInvalidate;

# Operand Cache Block Invalidate 
# pattern 0000nnnn10010011
# text    ocbi @<REG_N>
# arch 
:ocbi N_0t_at1              is OP_0=0x0 & N_0t_at1 & OP_4=0x93 { CacheBlockInvalidate(N_0t_at1); }

define pcodeop CacheBlockPurge;

# Cache Block Purge
# pattern 0000nnnn10100011
# text    ocbp @<REG_N>
# arch 
:ocbp N_0t_at1              is OP_0=0x0 & N_0t_at1 & OP_4=0xa3 { CacheBlockPurge(N_0t_at1); }

define pcodeop CacheBlockWriteBack;

# TODO ocbwb 
# Cache Block Write-Back
# pattern 0000nnnn10110011
# text    ocbwb @<REG_N>
# arch 
:ocbwb N_0t_at1             is OP_0=0x0 & N_0t_at1 & OP_4=0xb3 { CacheBlockWriteBack(N_0t_at1); }


# Logical OR
# pattern 0010nnnnmmmm1011
# text    or <REG_M>,<REG_N>
# arch 
:or M_0t,N_0t               is OP_0=0x2 & N_0t & M_0t & OP_1=0xb {

	N_0t = N_0t | M_0t;
}


# Logical OR
# pattern 11001011iiiiiiii
# text    or #<imm>,R0
# arch 
:or U_0t_r0                 is OP_2=0xcb & U_0t_r0 {

	r0 = r0 | U_0t_r0;
}


# Logical OR
# pattern 11001111iiiiiiii
# text    or.b #<imm>,@(R0,GBR)
# arch 
:or.b U_0t1                 is OP_2=0xcf & U_0t1 {

	*:1 (GBR + r0) = ( *:1 (GBR + r0) ) | U_0t1;
}


# Prefetch to Data Cache
# pattern 0000nnnn10000011
# text    pref @<REG_N>
# arch 
:pref N_0tjmp               is OP_0=0x0 & N_0tjmp & OP_4=0x83 { } # Empty on purpose


# One-Bit Left Rotation through T Bit
# pattern 0100nnnn00100100
# text    rotcl <REG_N>
# arch 
:rotcl N_0t                 is OP_0=0x4 & N_0t & OP_4=0x24 {

	temp:1 = ( (N_0t & 0x80000000) != 0 );
	N_0t   = (N_0t << 1) | zext($(T_FLAG));
	$(T_FLAG)      = temp;
}

# One-Bit Right Rotation through T Bit
# pattern 0100nnnn00100101
# text    rotcr <REG_N>
# arch 
:rotcr N_0t                 is OP_0=0x4 & N_0t & OP_4=0x25 {

	temp:1 = ( (N_0t & 0x00000001) != 0 );
	N_0t   = ( N_0t >> 1 ) | ( zext($(T_FLAG)) << 31 );
	$(T_FLAG)      = temp;
}


# One-Bit Left Rotation
# pattern 0100nnnn00000100
# text    rotl <REG_N>
# arch 
:rotl N_0t                  is OP_0=0x4 & N_0t & OP_4=0x4 {

	$(T_FLAG)      = ( (N_0t & 0x80000000) != 0 );
	N_0t   = (N_0t << 1) | zext($(T_FLAG));
}


# One-Bit Right Rotation
# pattern 0100nnnn00000101
# text    rotr <REG_N>
# arch 
:rotr N_0t                  is OP_0=0x4 & N_0t & OP_4=0x5 {

	$(T_FLAG)      = ( (N_0t & 0x00000001) != 0 );
	N_0t   = ( N_0t >> 1 ) | ( zext($(T_FLAG)) << 31 );
}


# Return from Exception Handling
# pattern 0000000000101011
# text    rte
# arch 
:rte                        is OP_3=0x2b {

	SR = SSR;
	splitSRregister();
	PC = SPC;
	delayslot(1);
	return [PC];
}


# Return from Subroutine Procedure
# pattern 0000000000001011
# text    rts
# arch 
:rts                        is OP_3=0xb {

	PC = PR;
	delayslot(1);
	return [PC];
}


# S Bit Setting
# pattern 0000000001011000
# text    sets
# arch 
:sets                       is OP_3=0x58 {

	$(S_FLAG) = 1;
}


# T Bit Setting
# pattern 0000000000011000
# text    sett
# arch 
:sett                       is OP_3=0x18 {

	$(T_FLAG) = 1;
}


# Note: this constructor follows the description on page 393 precisely, though simpler,
# it should produce identical results to the pseudo-code above
:shad M_0t,N_0t             is OP_0=0x4 & N_0t & M_0t & OP_1=0xc {

	if ( M_0t s< 0 ) goto <shiftRight>;
	N_0t = N_0t << ( M_0t & 0x1f );
	goto <skip>;

	<shiftRight>
	N_0t = N_0t s>> ( ( ~M_0t & 0x1f ) + 1 );
	<skip>
}


# One-Bit Left Arithmetic Shift
# pattern 0100nnnn00100000
# text    shal <REG_N>
# arch 
:shal N_0t                  is OP_0=0x4 & N_0t & OP_4=0x20 {

	$(T_FLAG) = ( ( N_0t & 0x80000000 ) != 0 );
	N_0t = N_0t << 1;
}


# One-Bit Right Arithmetic Shift
# pattern 0100nnnn00100001
# text    shar <REG_N>
# arch 
:shar N_0t                  is OP_0=0x4 & N_0t & OP_4=0x21 {

	$(T_FLAG)    = ( ( N_0t & 0x00000001 ) != 0 );
	N_0t = N_0t s>> 1;
}


# Note: this constructor follows the description on page 397 precisely, though simpler,
# it should produce identical results to the pseudo-code above
:shld M_0t,N_0t             is OP_0=0x4 & N_0t & M_0t & OP_1=0xd {

	if ( M_0t s< 0 ) goto <shiftRight>;
	N_0t = N_0t << ( M_0t & 0x1f );
	goto <skip>;

	<shiftRight>
	N_0t = N_0t >> ( ( ~M_0t & 0x1f ) + 1 );
	<skip>
}


# n-Bit Left Logical Shift
# One-Bit Left Logical Shift
# pattern 0100nnnn00000000
# text    shll <REG_N>
# arch 
:shll N_0t                  is OP_0=0x4 & N_0t & OP_4=0x0 {

	$(T_FLAG) = ( ( N_0t & 0x80000000 ) != 0 );
	N_0t = N_0t << 1;
}


# n-Bit Left Logical Shift
# pattern 0100nnnn00001000
# text    shll2 <REG_N>
# arch 
:shll2 N_0t                 is OP_0=0x4 & N_0t & OP_4=0x8 {

	N_0t = ( N_0t << 2 );
}


# n-Bit Left Logical Shift
# pattern 0100nnnn00011000
# text    shll8 <REG_N>
# arch 
:shll8 N_0t                 is OP_0=0x4 & N_0t & OP_4=0x18 {

	N_0t = ( N_0t << 8 );
}


# pattern 0100nnnn00101000
# text    shll16 <REG_N>
# arch 
:shll16 N_0t                is OP_0=0x4 & N_0t & OP_4=0x28 {

	N_0t = ( N_0t << 16 );
}


# One-Bit Right Logical Shift
# pattern 0100nnnn00000001
# text    shlr <REG_N>
# arch 
:shlr N_0t                  is OP_0=0x4 & N_0t & OP_4=0x1 {

	$(T_FLAG) = ( ( N_0t & 0x00000001 ) != 0 );
	N_0t = N_0t >> 1;
}


# n-Bit Left Logical Shift
# pattern 0100nnnn00001001
# text    shlr2 <REG_N>
# arch 
:shlr2 N_0t                 is OP_0=0x4 & N_0t & OP_4=0x9 {

	N_0t = N_0t >> 2;
}


# n-Bit Left Logical Shift
# pattern 0100nnnn00011001
# text    shlr8 <REG_N>
# arch 
:shlr8 N_0t                 is OP_0=0x4 & N_0t & OP_4=0x19 {

	N_0t = N_0t >> 8;
}


# n-Bit Left Logical Shift
# pattern 0100nnnn00101001
# text    shlr16 <REG_N>
# arch 
:shlr16 N_0t                is OP_0=0x4 & N_0t & OP_4=0x29 {

	N_0t = N_0t >> 16;
}


# Transition to Power-Down Mode
# pattern 0000000000011011
# text    sleep
# arch 
:sleep                      is OP_3=0x1b {  } # empty on purpose


# Store from Control Register
# pattern 0000nnnn00000010
# text    stc SR,<REG_N>
# arch    arch_sh_up
:stc sr_N_0t                is OP_0=0x0 & sr_N_0t & OP_4=0x2 {

	genSRregister();
	sr_N_0t = SR;
}


# Store from Control Register
# pattern 0000nnnn00010010
# text    stc GBR,<REG_N>
# arch 
:stc gbr_N_0t               is OP_0=0x0 & gbr_N_0t & OP_4=0x12 {

	gbr_N_0t = GBR;
}


# Store from Control Register
# pattern 0000nnnn00100010
# text    stc VBR,<REG_N>
# arch    arch_sh_up
:stc vbr_N_0t               is OP_0=0x0 & vbr_N_0t & OP_4=0x22 {

	vbr_N_0t = VBR;
}


# pattern 0000nnnn00110010
# text    stc SSR,<REG_N>
# arch    arch_sh3_nommu_up
:stc ssr_N_0t               is OP_0=0x0 & ssr_N_0t & OP_4=0x32 {

	ssr_N_0t = SSR;
}


# pattern 0000nnnn01000010
# text    stc SPC,<REG_N>
# arch    arch_sh3_nommu_up
#
:stc spc_N_0t               is OP_0=0x0 & spc_N_0t & OP_4=0x42 {

	spc_N_0t = SPC;
}


# pattern 0000nnnn00111010
# text    stc SGR,<REG_N>
# arch    arch_sh4_nommu_nofpu_up
:stc sgr_N_0t               is OP_0=0x0 & sgr_N_0t & OP_4=0x3a {

	sgr_N_0t = SGR;
}


# pattern 0000nnnn11111010
# text    stc DBR,<REG_N>
# arch    arch_sh4_nommu_nofpu_up
:stc dbr_N_0t               is OP_0=0x0 & dbr_N_0t & OP_4=0xfa {

	dbr_N_0t = DBR;
}


# pattern 0000nnnn1xxx0010
# text    stc Rn_BANK,<REG_N>
# arch    arch_sh3_nommu_up
:stc BANKt,N_0t_bank        is OP_0=0x0 & N_0t_bank & OP_9=0x1 & BANKt & OP_1=0x2 {

	N_0t_bank = BANKt;
}


# Store from Control Register
# pattern 0100nnnn00000011
# text    stc.l SR,@-<REG_N>
# arch    arch_sh_up
:stc.l sr_t,N_0t_at_neg     is OP_0=0x4 & N_0t_at_neg & OP_4=0x3 & sr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	genSRregister();
	*:4 ( N_0t_at_neg ) = SR;
}


# Store from Control Register
# pattern 0100nnnn00010011
# text    stc.l GBR,@-<REG_N>
# arch    arch_sh_up
:stc.l gbr_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0x13 & gbr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = GBR;
}


# Store from Control Register
# pattern 0100nnnn00100011
# text    stc.l VBR,@-<REG_N>
# arch    arch_sh_up
:stc.l vbr_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0x23 & vbr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = VBR;
}


# pattern 0100nnnn00110011
# text    stc.l SSR,@-<REG_N>
# arch    arch_sh3_nommu_up
:stc.l ssr_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0x33 & ssr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = SSR;
}


# pattern 0100nnnn01000011
# text    stc.l SPC,@-<REG_N>
# arch    arch_sh3_nommu_up
:stc.l spc_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0x43 & spc_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = SPC;
}


# pattern 0100nnnn00110010
# text    stc.l SGR,@-<REG_N>
# arch    arch_sh4_nommu_nofpu_up
:stc.l sgr_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0x32 & sgr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = SGR;
}


# pattern 0100nnnn11110010
# text    stc.l DBR,@-<REG_N>
# arch    arch_sh4_nommu_nofpu_up
:stc.l dbr_t,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_4=0xf2 & dbr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = DBR;
}


# pattern 0100nnnn1xxx0011
# text    stc.l Rn_BANK,@-<REG_N>
# arch    arch_sh3_nommu_up
:stc.l BANKt,N_0t_at_neg    is OP_0=0x4 & N_0t_at_neg & OP_9=0x1 & BANKt & OP_1=0x3 {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = BANKt;
}


# Store from System Register
# pattern 0000nnnn00001010
# text    sts MACH,<REG_N>
# arch    arch_sh_up
:sts mach_N_0t              is OP_0=0x0 & mach_N_0t & OP_4=0xa {

	mach_N_0t = MACH;
}


# Store from System Register
# pattern 0000nnnn00011010
# text    sts MACL,<REG_N>
# arch    arch_sh_up
:sts macl_N_0t              is OP_0=0x0 & macl_N_0t & OP_4=0x1a {

	macl_N_0t = MACL;
}


# Store from System Register
# pattern 0000nnnn00101010
# text    sts PR,<REG_N>
# arch    arch_sh_up
:sts pr_N_0t                is OP_0=0x0 & pr_N_0t & OP_4=0x2a {

	pr_N_0t = PR;
}


# Store from Control Register
# pattern 0100nnnn00000010
# text    sts.l MACH,@-<REG_N>
# arch    arch_sh_up
:sts.l mach_t,N_0t_at_neg   is OP_0=0x4 & N_0t_at_neg & OP_4=0x2 & mach_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = MACH;
}


# Store from Control Register
# pattern 0100nnnn00010010
# text    sts.l MACL,@-<REG_N>
# arch    arch_sh_up
:sts.l macl_t,N_0t_at_neg   is OP_0=0x4 & N_0t_at_neg & OP_4=0x12 & macl_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = MACL;
}


# Store from Control Register
# pattern 0100nnnn00100010
# text    sts.l PR,@-<REG_N>
# arch    arch_sh_up
:sts.l PR,N_0t_at_neg       is OP_0=0x4 & N_0t_at_neg & OP_4=0x22 & PR {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = PR;
}


# Store from System Register
# pattern 0000nnnn01011010
# text    sts FPUL,<REG_N>
# arch    arch_sh2e_up
:sts fpul_N_0t              is OP_0=0x0 & fpul_N_0t & OP_4=0x5a {

	fpul_N_0t = FPUL;
}


# Store from System Register
# pattern 0000nnnn01101010
# text    sts FPSCR,<REG_N>
# arch    arch_sh2e_up
:sts fpscr_N_0t             is OP_0=0x0 & fpscr_N_0t & OP_4=0x6a {

	genFPSCRregister();
	fpscr_N_0t = FPSCR;
}


# Store from Control Register
# pattern 0100nnnn01010010
# text    sts.l FPUL,@-<REG_N>
# arch    arch_sh2e_up
:sts.l fpul_t,N_0t_at_neg   is OP_0=0x4 & N_0t_at_neg & OP_4=0x52 & fpul_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	*:4 ( N_0t_at_neg ) = FPUL;
}


# Store from Control Register
# pattern 0100nnnn01100010
# text    sts.l FPSCR,@-<REG_N>
# arch    arch_sh2e_up
:sts.l fpscr_t,N_0t_at_neg  is OP_0=0x4 & N_0t_at_neg & OP_4=0x62 & fpscr_t {

	N_0t_at_neg = N_0t_at_neg - 4;
	genFPSCRregister();
	*:4 ( N_0t_at_neg ) = FPSCR;
}


# Binary Subtraction
# pattern 0011nnnnmmmm1000
# text    sub <REG_M>,<REG_N>
# arch    arch_sh_up
:sub M_0t,N_0t              is OP_0=0x3 & N_0t & M_0t & OP_1=0x8 {

	N_0t = N_0t - M_0t;
}


# Binary Subtraction with Borrow
# pattern 0011nnnnmmmm1010
# text    subc <REG_M>,<REG_N>
# arch    arch_sh_up
:subc M_0t,N_0t             is OP_0=0x3 & N_0t & M_0t & OP_1=0xa {
	local Tcopy:4 = zext($(T_FLAG));
	$(T_FLAG) = N_0t < M_0t;
	local result:4 = N_0t - M_0t;
	$(T_FLAG) = $(T_FLAG) || (result < Tcopy);
	N_0t = result - Tcopy;
}


# Binary Subtraction with Underflow Check
# pattern 0011nnnnmmmm1011
# text    subv <REG_M>,<REG_N>
# arch    arch_sh_up
:subv M_0t,N_0t             is OP_0=0x3 & N_0t & M_0t & OP_1=0xb {
	$(T_FLAG) = sborrow(N_0t, M_0t);
	N_0t = N_0t - M_0t;
}


# Upper-/Lower-Half Swap
# pattern 0110nnnnmmmm1000
# text    swap.b <REG_M>,<REG_N>
# arch    arch_sh_up
:swap.b M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0x8 {

	N_0t    =   ( M_0t & 0xFFFF0000 )        |
            ( ( M_0t & 0x000000FF ) << 8 ) |
            ( ( M_0t & 0x0000FF00 ) >> 8 );
}


# Upper-/Lower-Half Swap
# pattern 0110nnnnmmmm1001
# text    swap.w <REG_M>,<REG_N>
# arch    arch_sh_up
:swap.w M_0t,N_0t           is OP_0=0x6 & N_0t & M_0t & OP_1=0x9 {

	N_0t   = ( M_0t << 16 ) | ( M_0t >> 16 );
}

# Synchronize Data Operation
# pattern 0000000010101011
# text synco
# arch arch_sh4a_up
define pcodeop SynchronizeDataOperation;

:synco                      is OP_3=0x00ab {
	SynchronizeDataOperation();
}


# Memory Test and Bit Setting
# pattern 0100nnnn00011011
# text    tas.b @<REG_N>
# arch    arch_sh_up
:tas.b N_0t_at1             is OP_0=0x4 & N_0t_at1 & OP_4=0x1b {

	temp:1 = *:1 ( N_0t_at1 );
	$(T_FLAG) = ( temp == 0 );
	temp = temp | 0x80;
	*:1 ( N_0t_at1 ) = temp;
}

define pcodeop TrapAlways;

# Trap Exception Handling
# pattern 11000011iiiiiiii
# text    trapa #<imm>
# arch    arch_sh_up
:trapa U_0t                 is OP_2=0xc3 & U_0t { TrapAlways(U_0t); }


# AND Operation T Bit Setting
# pattern 0010nnnnmmmm1000
# text    tst <REG_M>,<REG_N>
# arch    arch_sh_up
:tst M_0t,N_0t              is OP_0=0x2 & N_0t & M_0t & OP_1=0x8 {

	$(T_FLAG) = ( (N_0t & M_0t) == 0 );
}


# AND Operation T Bit Setting
# pattern 11001000iiiiiiii
# text    tst #<imm>,R0
# arch    arch_sh_up
:tst U_0t_r0                is OP_2=0xc8 & U_0t_r0 {

	$(T_FLAG) = ( ( r0 & U_0t_r0 ) == 0 );
}


# AND Operation T Bit Setting
# pattern 11001100iiiiiiii
# text    tst.b #<imm>,@(R0,GBR)
# arch    arch_sh_up
:tst.b U_0t1                is OP_2=0xcc & U_0t1 {

	$(T_FLAG) = ( ( (*:1 ( GBR + r0 )) & U_0t1 ) == 0 );
}


# pattern 0010nnnnmmmm1010
# text    xor <REG_M>,<REG_N>
# arch    arch_sh_up
:xor M_0t,N_0t              is OP_0=0x2 & N_0t & M_0t & OP_1=0xa {

	N_0t = N_0t ^ M_0t;
}


# Exclusive Logical OR
# pattern 11001010iiiiiiii
# text    xor #<imm>,R0
# arch    arch_sh_up
:xor U_0t_r0                is OP_2=0xca & U_0t_r0 {

	r0 = r0 ^ U_0t_r0;
}


# Exclusive Logical OR
# pattern 11001110iiiiiiii
# text    xor.b #<imm>,@(R0,GBR)
# arch    arch_sh_up
:xor.b U_0t1                is OP_2=0xce & U_0t1 {

	*:1 (GBR + r0) = ( *:1 (GBR + r0) ) ^ U_0t1;
}


# Middle Extraction from Linked Registers
# pattern 0010nnnnmmmm1101
# text    xtrct <REG_M>,<REG_N>
# arch    arch_sh_up
:xtrct M_0t,N_0t            is OP_0=0x2 & N_0t & M_0t & OP_1=0xd {

	N_0t = (M_0t << 16) | (N_0t >> 16);
}

