/**********************************************************/
/*                                                        */
/* SH2 emulator 1.60 (C source part)                      */
/* This part define only the initialisation stuff...      */
/* Emulation core itself is defined in the SH2a.asm file. */
/* Copyright 2002 Stéphane Dallongeville                  */
/*                                                        */
/**********************************************************/

#define SH2_DEBUG   0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

#include "sh2.h"
#include "debug.h"

#if SH2_DEBUG > 0
FILE *SH2_Debug = NULL;

#define SH2_LOG0(a) fprintf(SH2_Debug, a);
#define SH2_LOG1(a, b) fprintf(SH2_Debug, a, b);
#define SH2_LOG2(a, b, c) fprintf(SH2_Debug, a, b, c);
#define SH2_LOG3(a, b, c, d) fprintf(SH2_Debug, a, b, c, d);
#define SH2_LOG4(a, b, c, d, e) fprintf(SH2_Debug, a, b, c, d, e);
#else
#define SH2_LOG0(a)
#define SH2_LOG1(a, b)
#define SH2_LOG2(a, b, c)
#define SH2_LOG3(a, b, c, d)
#define SH2_LOG4(a, b, c, d, e)
#endif



/************************************************************/
/*                                                          */
/* Opcode Table :                                           */
/* Almost of the following table was taken from the         */
/* Tim's SH2 core (Retrodrives).                            */
/*                                                          */
/************************************************************/


typedef enum __ifmt
{
  IFMT_0,			// xxxx xxxx xxxx xxxx
  IFMT_n,			// xxxx nnnn xxxx xxxx
  IFMT_m,			// xxxx mmmm xxxx xxxx
  IFMT_nm,			// xxxx nnnn mmmm xxxx
  IFMT_md,			// xxxx xxxx mmmm dddd
  IFMT_nd4,			// xxxx xxxx nnnn dddd
  IFMT_nmd,			// xxxx nnnn mmmm dddd
  IFMT_d,			// xxxx xxxx dddd dddd
  IFMT_d12,			// xxxx dddd dddd dddd
  IFMT_nd8,			// xxxx nnnn dddd dddd
  IFMT_i,			// xxxx xxxx iiii iiii
  IFMT_ni,			// xxxx nnnn iiii iiii
} _IFMT;


typedef void SH2_INST_CALL ();


typedef struct
{
  _IFMT IFMT;
  UINT32 Mask;
  SH2_INST_CALL *Func;

} PACKED_OP_TAB;

PACKED_OP_TAB InitOp[] = {
  {IFMT_nm, 0x300C, SH2I_ADD},
  {IFMT_ni, 0x7000, SH2I_ADDI},
  {IFMT_nm, 0x300E, SH2I_ADDC},
  {IFMT_nm, 0x300F, SH2I_ADDV},
  {IFMT_nm, 0x2009, SH2I_AND},
  {IFMT_i, 0xC900, SH2I_ANDI},
  {IFMT_i, 0xCD00, SH2I_ANDM},
  {IFMT_d, 0x8B00, SH2I_BF},
  {IFMT_0, 0x8BFD, SH2I_BFfast},
  {IFMT_d, 0x8F00, SH2I_BFS},
  {IFMT_0, 0x8FFD, SH2I_BFSfast},
  {IFMT_d12, 0xA000, SH2I_BRA},
  {IFMT_0, 0xAFFE, SH2I_BRAfast1},
  {IFMT_0, 0xAFFD, SH2I_BRAfast2},
  {IFMT_m, 0x0023, SH2I_BRAF},
  {IFMT_d12, 0xB000, SH2I_BSR},
  {IFMT_n, 0x0003, SH2I_BSRF},
  {IFMT_d, 0x8900, SH2I_BT},
  {IFMT_d, 0x8D00, SH2I_BTS},
  {IFMT_0, 0x0028, SH2I_CLRMAC},
  {IFMT_0, 0x0008, SH2I_CLRT},
  {IFMT_nm, 0x3000, SH2I_CMPEQ},
  {IFMT_nm, 0x3003, SH2I_CMPGE},
  {IFMT_nm, 0x3007, SH2I_CMPGT},
  {IFMT_nm, 0x3006, SH2I_CMPHI},
  {IFMT_nm, 0x3002, SH2I_CMPHS},
  {IFMT_n, 0x4015, SH2I_CMPPL},
  {IFMT_n, 0x4011, SH2I_CMPPZ},
  {IFMT_nm, 0x200C, SH2I_CMPSTR},
  {IFMT_i, 0x8800, SH2I_CMPIM},
  {IFMT_nm, 0x2007, SH2I_DIV0S},
  {IFMT_0, 0x0019, SH2I_DIV0U},
  {IFMT_nm, 0x3004, SH2I_DIV1},
  {IFMT_nm, 0x300D, SH2I_DMULS},
  {IFMT_nm, 0x3005, SH2I_DMULU},
  {IFMT_n, 0x4010, SH2I_DT},
  {IFMT_nm, 0x600E, SH2I_EXTSB},
  {IFMT_nm, 0x600F, SH2I_EXTSW},
  {IFMT_nm, 0x600C, SH2I_EXTUB},
  {IFMT_nm, 0x600D, SH2I_EXTUW},
  {IFMT_n, 0x402B, SH2I_JMP},
  {IFMT_n, 0x400B, SH2I_JSR},
  {IFMT_m, 0x400E, SH2I_LDCSR},
  {IFMT_m, 0x401E, SH2I_LDCGBR},
  {IFMT_m, 0x402E, SH2I_LDCVBR},
  {IFMT_m, 0x4007, SH2I_LDCMSR},
  {IFMT_m, 0x4017, SH2I_LDCMGBR},
  {IFMT_m, 0x4027, SH2I_LDCMVBR},
  {IFMT_m, 0x400A, SH2I_LDSMACH},
  {IFMT_m, 0x401A, SH2I_LDSMACL},
  {IFMT_m, 0x402A, SH2I_LDSPR},
  {IFMT_m, 0x4006, SH2I_LDSMMACH},
  {IFMT_m, 0x4016, SH2I_LDSMMACL},
  {IFMT_m, 0x4026, SH2I_LDSMPR},
  {IFMT_nm, 0x000F, SH2I_MACL},
  {IFMT_nm, 0x400F, SH2I_MACW},
  {IFMT_nm, 0x6003, SH2I_MOV},
  {IFMT_nm, 0x2000, SH2I_MOVBS},
  {IFMT_nm, 0x2001, SH2I_MOVWS},
  {IFMT_nm, 0x2002, SH2I_MOVLS},
  {IFMT_nm, 0x6000, SH2I_MOVBL},
  {IFMT_nm, 0x6001, SH2I_MOVWL},
  {IFMT_nm, 0x6002, SH2I_MOVLL},
  {IFMT_nm, 0x2004, SH2I_MOVBM},
  {IFMT_nm, 0x2005, SH2I_MOVWM},
  {IFMT_nm, 0x2006, SH2I_MOVLM},
  {IFMT_nm, 0x6004, SH2I_MOVBP},
  {IFMT_nm, 0x6005, SH2I_MOVWP},
  {IFMT_nm, 0x6006, SH2I_MOVLP},
  {IFMT_nm, 0x0004, SH2I_MOVBS0},
  {IFMT_nm, 0x0005, SH2I_MOVWS0},
  {IFMT_nm, 0x0006, SH2I_MOVLS0},
  {IFMT_nm, 0x000C, SH2I_MOVBL0},
  {IFMT_nm, 0x000D, SH2I_MOVWL0},
  {IFMT_nm, 0x000E, SH2I_MOVLL0},
  {IFMT_ni, 0xE000, SH2I_MOVI},
  {IFMT_nd8, 0x9000, SH2I_MOVWI},
  {IFMT_nd8, 0xD000, SH2I_MOVLI},
  {IFMT_d, 0xC400, SH2I_MOVBLG},
  {IFMT_d, 0xC500, SH2I_MOVWLG},
  {IFMT_d, 0xC600, SH2I_MOVLLG},
  {IFMT_d, 0xC000, SH2I_MOVBSG},
  {IFMT_d, 0xC100, SH2I_MOVWSG},
  {IFMT_d, 0xC200, SH2I_MOVLSG},
  {IFMT_nd4, 0x8000, SH2I_MOVBS4},
  {IFMT_nd4, 0x8100, SH2I_MOVWS4},
  {IFMT_nmd, 0x1000, SH2I_MOVLS4},
  {IFMT_md, 0x8400, SH2I_MOVBL4},
  {IFMT_md, 0x8500, SH2I_MOVWL4},
  {IFMT_nmd, 0x5000, SH2I_MOVLL4},
  {IFMT_d, 0xC700, SH2I_MOVA},
  {IFMT_n, 0x0029, SH2I_MOVT},
  {IFMT_nm, 0x0007, SH2I_MULL},
  {IFMT_nm, 0x200F, SH2I_MULS},
  {IFMT_nm, 0x200E, SH2I_MULU},
  {IFMT_nm, 0x600B, SH2I_NEG},
  {IFMT_nm, 0x600A, SH2I_NEGC},
  {IFMT_0, 0x0009, SH2I_NOP},
  {IFMT_nm, 0x6007, SH2I_NOT},
  {IFMT_nm, 0x200B, SH2I_OR},
  {IFMT_i, 0xCB00, SH2I_ORI},
  {IFMT_i, 0xCF00, SH2I_ORM},
  {IFMT_n, 0x4024, SH2I_ROTCL},
  {IFMT_n, 0x4025, SH2I_ROTCR},
  {IFMT_n, 0x4004, SH2I_ROTL},
  {IFMT_n, 0x4005, SH2I_ROTR},
  {IFMT_0, 0x002B, SH2I_RTE},
  {IFMT_0, 0x000B, SH2I_RTS},
  {IFMT_0, 0x0018, SH2I_SETT},
  {IFMT_n, 0x4020, SH2I_SHAL},
  {IFMT_n, 0x4021, SH2I_SHAR},
  {IFMT_n, 0x4000, SH2I_SHLL},
  {IFMT_n, 0x4008, SH2I_SHLL2},
  {IFMT_n, 0x4018, SH2I_SHLL8},
  {IFMT_n, 0x4028, SH2I_SHLL16},
  {IFMT_n, 0x4001, SH2I_SHLR},
  {IFMT_n, 0x4009, SH2I_SHLR2},
  {IFMT_n, 0x4019, SH2I_SHLR8},
  {IFMT_n, 0x4029, SH2I_SHLR16},
  {IFMT_0, 0x001B, SH2I_SLEEP},
  {IFMT_n, 0x0002, SH2I_STCSR},
  {IFMT_n, 0x0012, SH2I_STCGBR},
  {IFMT_n, 0x0022, SH2I_STCVBR},
  {IFMT_n, 0x4003, SH2I_STCMSR},
  {IFMT_n, 0x4013, SH2I_STCMGBR},
  {IFMT_n, 0x4023, SH2I_STCMVBR},
  {IFMT_n, 0x000A, SH2I_STSMACH},
  {IFMT_n, 0x001A, SH2I_STSMACL},
  {IFMT_n, 0x002A, SH2I_STSPR},
  {IFMT_n, 0x4002, SH2I_STSMMACH},
  {IFMT_n, 0x4012, SH2I_STSMMACL},
  {IFMT_n, 0x4022, SH2I_STSMPR},
  {IFMT_nm, 0x3008, SH2I_SUB},
  {IFMT_nm, 0x300A, SH2I_SUBC},
  {IFMT_nm, 0x300B, SH2I_SUBV},
  {IFMT_nm, 0x6008, SH2I_SWAPB},
  {IFMT_nm, 0x6009, SH2I_SWAPW},
  {IFMT_n, 0x401B, SH2I_TAS},
  {IFMT_i, 0xC300, SH2I_TRAPA},
  {IFMT_nm, 0x2008, SH2I_TST},
  {IFMT_i, 0xC800, SH2I_TSTI},
  {IFMT_i, 0xCC00, SH2I_TSTM},
  {IFMT_nm, 0x200A, SH2I_XOR},
  {IFMT_i, 0xCA00, SH2I_XORI},
  {IFMT_i, 0xCE00, SH2I_XORM},
  {IFMT_nm, 0x200D, SH2I_XTRCT},
  {0, 0x0000, NULL},
};

PACKED_OP_TAB InitOpDS[] = {
  {IFMT_nm, 0x300C, SH2I_ADD_DS},
  {IFMT_ni, 0x7000, SH2I_ADDI_DS},
  {IFMT_nm, 0x300E, SH2I_ADDC_DS},
  {IFMT_nm, 0x300F, SH2I_ADDV_DS},
  {IFMT_nm, 0x2009, SH2I_AND_DS},
  {IFMT_i, 0xC900, SH2I_ANDI_DS},
  {IFMT_i, 0xCD00, SH2I_ANDM_DS},
  {IFMT_0, 0x0028, SH2I_CLRMAC_DS},
  {IFMT_0, 0x0008, SH2I_CLRT_DS},
  {IFMT_nm, 0x3000, SH2I_CMPEQ_DS},
  {IFMT_nm, 0x3003, SH2I_CMPGE_DS},
  {IFMT_nm, 0x3007, SH2I_CMPGT_DS},
  {IFMT_nm, 0x3006, SH2I_CMPHI_DS},
  {IFMT_nm, 0x3002, SH2I_CMPHS_DS},
  {IFMT_n, 0x4015, SH2I_CMPPL_DS},
  {IFMT_n, 0x4011, SH2I_CMPPZ_DS},
  {IFMT_nm, 0x200C, SH2I_CMPSTR_DS},
  {IFMT_i, 0x8800, SH2I_CMPIM_DS},
  {IFMT_nm, 0x2007, SH2I_DIV0S_DS},
  {IFMT_0, 0x0019, SH2I_DIV0U_DS},
  {IFMT_nm, 0x3004, SH2I_DIV1_DS},
  {IFMT_nm, 0x300D, SH2I_DMULS_DS},
  {IFMT_nm, 0x3005, SH2I_DMULU_DS},
  {IFMT_n, 0x4010, SH2I_DT_DS},
  {IFMT_nm, 0x600E, SH2I_EXTSB_DS},
  {IFMT_nm, 0x600F, SH2I_EXTSW_DS},
  {IFMT_nm, 0x600C, SH2I_EXTUB_DS},
  {IFMT_nm, 0x600D, SH2I_EXTUW_DS},
  {IFMT_m, 0x400E, SH2I_LDCSR_DS},
  {IFMT_m, 0x401E, SH2I_LDCGBR_DS},
  {IFMT_m, 0x402E, SH2I_LDCVBR_DS},
  {IFMT_m, 0x4007, SH2I_LDCMSR_DS},
  {IFMT_m, 0x4017, SH2I_LDCMGBR_DS},
  {IFMT_m, 0x4027, SH2I_LDCMVBR_DS},
  {IFMT_m, 0x400A, SH2I_LDSMACH_DS},
  {IFMT_m, 0x401A, SH2I_LDSMACL_DS},
  {IFMT_m, 0x402A, SH2I_LDSPR_DS},
  {IFMT_m, 0x4006, SH2I_LDSMMACH_DS},
  {IFMT_m, 0x4016, SH2I_LDSMMACL_DS},
  {IFMT_m, 0x4026, SH2I_LDSMPR_DS},
  {IFMT_nm, 0x000F, SH2I_MACL_DS},
  {IFMT_nm, 0x400F, SH2I_MACW_DS},
  {IFMT_nm, 0x6003, SH2I_MOV_DS},
  {IFMT_nm, 0x2000, SH2I_MOVBS_DS},
  {IFMT_nm, 0x2001, SH2I_MOVWS_DS},
  {IFMT_nm, 0x2002, SH2I_MOVLS_DS},
  {IFMT_nm, 0x6000, SH2I_MOVBL_DS},
  {IFMT_nm, 0x6001, SH2I_MOVWL_DS},
  {IFMT_nm, 0x6002, SH2I_MOVLL_DS},
  {IFMT_nm, 0x2004, SH2I_MOVBM_DS},
  {IFMT_nm, 0x2005, SH2I_MOVWM_DS},
  {IFMT_nm, 0x2006, SH2I_MOVLM_DS},
  {IFMT_nm, 0x6004, SH2I_MOVBP_DS},
  {IFMT_nm, 0x6005, SH2I_MOVWP_DS},
  {IFMT_nm, 0x6006, SH2I_MOVLP_DS},
  {IFMT_nm, 0x0004, SH2I_MOVBS0_DS},
  {IFMT_nm, 0x0005, SH2I_MOVWS0_DS},
  {IFMT_nm, 0x0006, SH2I_MOVLS0_DS},
  {IFMT_nm, 0x000C, SH2I_MOVBL0_DS},
  {IFMT_nm, 0x000D, SH2I_MOVWL0_DS},
  {IFMT_nm, 0x000E, SH2I_MOVLL0_DS},
  {IFMT_ni, 0xE000, SH2I_MOVI_DS},
  {IFMT_nd8, 0x9000, SH2I_MOVWI_DS},
  {IFMT_nd8, 0xD000, SH2I_MOVLI_DS},
  {IFMT_d, 0xC400, SH2I_MOVBLG_DS},
  {IFMT_d, 0xC500, SH2I_MOVWLG_DS},
  {IFMT_d, 0xC600, SH2I_MOVLLG_DS},
  {IFMT_d, 0xC000, SH2I_MOVBSG_DS},
  {IFMT_d, 0xC100, SH2I_MOVWSG_DS},
  {IFMT_d, 0xC200, SH2I_MOVLSG_DS},
  {IFMT_nd4, 0x8000, SH2I_MOVBS4_DS},
  {IFMT_nd4, 0x8100, SH2I_MOVWS4_DS},
  {IFMT_nmd, 0x1000, SH2I_MOVLS4_DS},
  {IFMT_md, 0x8400, SH2I_MOVBL4_DS},
  {IFMT_md, 0x8500, SH2I_MOVWL4_DS},
  {IFMT_nmd, 0x5000, SH2I_MOVLL4_DS},
  {IFMT_d, 0xC700, SH2I_MOVA_DS},
  {IFMT_n, 0x0029, SH2I_MOVT_DS},
  {IFMT_nm, 0x0007, SH2I_MULL_DS},
  {IFMT_nm, 0x200F, SH2I_MULS_DS},
  {IFMT_nm, 0x200E, SH2I_MULU_DS},
  {IFMT_nm, 0x600B, SH2I_NEG_DS},
  {IFMT_nm, 0x600A, SH2I_NEGC_DS},
  {IFMT_0, 0x0009, SH2I_NOP_DS},
  {IFMT_nm, 0x6007, SH2I_NOT_DS},
  {IFMT_nm, 0x200B, SH2I_OR_DS},
  {IFMT_i, 0xCB00, SH2I_ORI_DS},
  {IFMT_i, 0xCF00, SH2I_ORM_DS},
  {IFMT_n, 0x4024, SH2I_ROTCL_DS},
  {IFMT_n, 0x4025, SH2I_ROTCR_DS},
  {IFMT_n, 0x4004, SH2I_ROTL_DS},
  {IFMT_n, 0x4005, SH2I_ROTR_DS},
  {IFMT_0, 0x0018, SH2I_SETT_DS},
  {IFMT_n, 0x4020, SH2I_SHAL_DS},
  {IFMT_n, 0x4021, SH2I_SHAR_DS},
  {IFMT_n, 0x4000, SH2I_SHLL_DS},
  {IFMT_n, 0x4008, SH2I_SHLL2_DS},
  {IFMT_n, 0x4018, SH2I_SHLL8_DS},
  {IFMT_n, 0x4028, SH2I_SHLL16_DS},
  {IFMT_n, 0x4001, SH2I_SHLR_DS},
  {IFMT_n, 0x4009, SH2I_SHLR2_DS},
  {IFMT_n, 0x4019, SH2I_SHLR8_DS},
  {IFMT_n, 0x4029, SH2I_SHLR16_DS},
  {IFMT_0, 0x001B, SH2I_SLEEP_DS},
  {IFMT_n, 0x0002, SH2I_STCSR_DS},
  {IFMT_n, 0x0012, SH2I_STCGBR_DS},
  {IFMT_n, 0x0022, SH2I_STCVBR_DS},
  {IFMT_n, 0x4003, SH2I_STCMSR_DS},
  {IFMT_n, 0x4013, SH2I_STCMGBR_DS},
  {IFMT_n, 0x4023, SH2I_STCMVBR_DS},
  {IFMT_n, 0x000A, SH2I_STSMACH_DS},
  {IFMT_n, 0x001A, SH2I_STSMACL_DS},
  {IFMT_n, 0x002A, SH2I_STSPR_DS},
  {IFMT_n, 0x4002, SH2I_STSMMACH_DS},
  {IFMT_n, 0x4012, SH2I_STSMMACL_DS},
  {IFMT_n, 0x4022, SH2I_STSMPR_DS},
  {IFMT_nm, 0x3008, SH2I_SUB_DS},
  {IFMT_nm, 0x300A, SH2I_SUBC_DS},
  {IFMT_nm, 0x300B, SH2I_SUBV_DS},
  {IFMT_nm, 0x6008, SH2I_SWAPB_DS},
  {IFMT_nm, 0x6009, SH2I_SWAPW_DS},
  {IFMT_n, 0x401B, SH2I_TAS_DS},
  {IFMT_nm, 0x2008, SH2I_TST_DS},
  {IFMT_i, 0xC800, SH2I_TSTI_DS},
  {IFMT_i, 0xCC00, SH2I_TSTM_DS},
  {IFMT_nm, 0x200A, SH2I_XOR_DS},
  {IFMT_i, 0xCA00, SH2I_XORI_DS},
  {IFMT_i, 0xCE00, SH2I_XORM_DS},
  {IFMT_nm, 0x200D, SH2I_XTRCT_DS},
  {0, 0x0000, NULL},
};


/* Default READ/WRITE callback */
/*******************************/


UINT8 FASTCALL
Def_READB (UINT32 adr)
{
  print_debug ("SH2 read byte at %.8lx\n", adr); return 0;
}

UINT16 FASTCALL
Def_READW (UINT32 adr)
{
  print_debug ("SH2 read word at %.8lX\n", adr); return 0;
}

UINT32 FASTCALL
Def_READL (UINT32 adr)
{
  print_debug ("SH2 read long at %.8lX\n", adr); return 0;
}

void FASTCALL
Def_WRITEB (UINT32 adr, UINT8 data)
{
    print_debug ("SH2 write byte %.2X at %.8lX\n", data, adr);
}

void FASTCALL
Def_WRITEW (UINT32 adr, UINT16 data)
{
    print_debug ("SH2 write word %.4X at %.8lX\n", data, adr);
}

void FASTCALL
Def_WRITEL (UINT32 adr, UINT32 data)
{
    print_debug ("SH2 write long %.8X at %.8X\n", data, adr);
}



/* SH2 Initialisation C functions */
/**********************************/


// IMPORTANT : *Fetch have to be DWORD aligned !!!!

void
SH2_Add_Fetch (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
	       UINT16 * Fetch)
{
  UINT32 i;

  for (i = 0; i < 0x100; i++)
    {
      if (SH2->Fetch_Region[i].Fetch_Reg == NULL)
	{
	  SH2->Fetch_Region[i].Low_Adr = low_adr;
	  SH2->Fetch_Region[i].High_Adr = high_adr;
	  SH2->Fetch_Region[i].Fetch_Reg = &Fetch[0 - (low_adr >> 1)];
	  break;
	}
    }
}

UINT8 *get_sh2_userdata(SH2_CONTEXT *SH2, UINT32 ptr) {
    int i;
    for (i=0; i<0x100 && SH2->Fetch_Region[i].Fetch_Reg; i++) {
	if (SH2->Fetch_Region[i].Low_Adr <= ptr &&
		SH2->Fetch_Region[i].High_Adr >= ptr)
	    return (UINT8*)SH2->Fetch_Region[i].Fetch_Reg;
    }
    return NULL;
}

UINT8 *get_sh2_code_range(SH2_CONTEXT *SH2,UINT32 adr,UINT32 *start,UINT32 *end) {
    for (int i=0; i<0x100 && SH2->Fetch_Region[i].Fetch_Reg; i++) {
	if (SH2->Fetch_Region[i].Low_Adr <= adr &&
		SH2->Fetch_Region[i].High_Adr >= adr) {
	    *start = SH2->Fetch_Region[i].Low_Adr;
	    *end = SH2->Fetch_Region[i].High_Adr;
	    return (UINT8*)SH2->Fetch_Region[i].Fetch_Reg;
	}
    }
    *start = *end = 0;
    return NULL;
}

// IMPORTANT : *Fetch have to be DWORD aligned !!!!

void
SH2_Set_Fetch_Reg (SH2_CONTEXT * SH2, UINT32 reg, UINT32 low_adr,
		   UINT32 high_adr, UINT16 * Fetch)
{
  reg &= 0xFF;

  SH2->Fetch_Region[reg].Low_Adr = low_adr;
  SH2->Fetch_Region[reg].High_Adr = high_adr;
  SH2->Fetch_Region[reg].Fetch_Reg = &Fetch[0 - (low_adr >> 1)];
}


void
SH2_Add_ReadB (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
	       SH2_RB * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Read_Byte[i] = Func;
}


void
SH2_Add_ReadW (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
	       SH2_RW * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Read_Word[i] = Func;
}


void
SH2_Add_ReadL (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
	       SH2_RL * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Read_Long[i] = Func;
}


void
SH2_Add_WriteB (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
		SH2_WB * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Write_Byte[i] = Func;
}


void
SH2_Add_WriteW (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
		SH2_WW * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Write_Word[i] = Func;
}


void
SH2_Add_WriteL (SH2_CONTEXT * SH2, UINT32 low_adr, UINT32 high_adr,
		SH2_WL * Func)
{
  UINT32 i;

  for (i = (low_adr & 0xFF); i <= (high_adr & 0xFF); i++)
    SH2->Write_Long[i] = Func;
}

// Can't include directly savegame.h here, annoying conflict with deftypes
void AddSaveData(UINT32 id, UINT8 *src, UINT32 size); // savegame.c
void AddLoadCallback_ptr(void *callback,void *arg);
#ifndef ASCII_ID
#define ASCII_ID(a,b,c,d)	  ((a<<24) | (b<<16) | (c<<8) | (d<<0))
#endif

static SH2_CONTEXT mcopy, scopy;

static void postload_sh2(void *aSH2) {
    SH2_CONTEXT *SH2 = (SH2_CONTEXT *)aSH2;
    int slave = (SH2 != &M_SH2);
    if (!slave) {
	memcpy(SH2->Read_Byte, mcopy.Read_Byte, sizeof(SH2_RB*)*0x100);
	memcpy(SH2->Read_Word, mcopy.Read_Word, sizeof(SH2_RB*)*0x100);
	memcpy(SH2->Read_Long, mcopy.Read_Long, sizeof(SH2_RB*)*0x100);

	memcpy(SH2->Write_Byte, mcopy.Write_Byte, sizeof(SH2_RB*)*0x100);
	memcpy(SH2->Write_Word, mcopy.Write_Word, sizeof(SH2_RB*)*0x100);
	memcpy(SH2->Write_Long, mcopy.Write_Long, sizeof(SH2_RB*)*0x100);

	memcpy(SH2->Fetch_Region, mcopy.Fetch_Region, sizeof(FETCHREG)*0x100);
    }
    UINT32 pc = SH2->PC - SH2->Base_PC;
    int i=0;
    while (SH2->Fetch_Region[i].Fetch_Reg != NULL)
    {
	if (SH2->Fetch_Region[i].Low_Adr == SH2->Fetch_Start &&
		SH2->Fetch_Region[i].High_Adr == SH2->Fetch_End) {
	    SH2->Base_PC = (UINT32)SH2->Fetch_Region[i].Fetch_Reg;
	    SH2->PC = SH2->Base_PC + pc;
	    return;
	}
	i++;
    }
    printf("postload_sh2: couldn't find sh2 Base_PC, expect trouble...\n");
}

void
SH2_Map_Cache_Trough (SH2_CONTEXT * SH2)
{
    UINT32 i;

    for (i = 0; i < 0x10; i++)
    {
	SH2->Read_Byte[i + 0x20] = SH2->Read_Byte[i];
	SH2->Read_Word[i + 0x20] = SH2->Read_Word[i];
	SH2->Read_Long[i + 0x20] = SH2->Read_Long[i];
	SH2->Write_Byte[i + 0x20] = SH2->Write_Byte[i];
	SH2->Write_Word[i + 0x20] = SH2->Write_Word[i];
	SH2->Write_Long[i + 0x20] = SH2->Write_Long[i];
    }
    int slave = (SH2 != &M_SH2);
    AddSaveData(ASCII_ID('S','H','2',slave),(UINT8*)SH2, sizeof(SH2_CONTEXT));
    if (!slave)
	memcpy(&mcopy,&M_SH2,sizeof(SH2_CONTEXT));
    else
	memcpy(&scopy,&S_SH2,sizeof(SH2_CONTEXT));
    AddLoadCallback_ptr(postload_sh2,SH2);
}

int sh2Engine;

void
SH2_Init (SH2_CONTEXT * SH2, UINT32 slave)
{
  static UINT32 SH2_Initialised = 0;
  INT32 i, j, m, q, s, t, il;
  void *OP_Table_Temp[0x10000];

  sh2Engine = 1;

  if (!SH2_Initialised)
    {

#if SH2_DEBUG > 0
      SH2_Debug = fopen ("sh2.log", "wb");
#endif

      SH2_LOG0 ("Starting to log :\n\n");

      // Unpacking the 'normal' opcode jump table

      for (i = 0; i < 0x10000; i++)
	OP_Table_Temp[i] = SH2I_ILLEGAL;

      i = -1;

      while (InitOp[++i].Func != NULL)
	{
	  switch (InitOp[i].IFMT)
	    {
	    case IFMT_0:
	      OP_Table_Temp[InitOp[i].Mask] = InitOp[i].Func;
	      break;

	    case IFMT_n:
	    case IFMT_m:
	      for (j = 0; j < 0x10; j++)
		{
		  OP_Table_Temp[InitOp[i].Mask | (j << 8)] = InitOp[i].Func;
		}
	      break;

	    case IFMT_nm:
	      for (j = 0; j < 0x100; j++)
		{
		  OP_Table_Temp[InitOp[i].Mask | (j << 4)] = InitOp[i].Func;
		}
	      break;

	    case IFMT_md:
	    case IFMT_nd4:
	    case IFMT_d:
	    case IFMT_i:
	      for (j = 0; j < 0x100; j++)
		{
		  OP_Table_Temp[InitOp[i].Mask | j] = InitOp[i].Func;
		}
	      break;

	    case IFMT_nmd:
	    case IFMT_d12:
	    case IFMT_nd8:
	    case IFMT_ni:
	      for (j = 0; j < 0x1000; j++)
		{
		  OP_Table_Temp[InitOp[i].Mask | j] = InitOp[i].Func;
		}
	      break;
	    }
	}

      // Big Indian Opcode, we need to "byte-swap" the opcode table pointer

      for (i = 0; i < 0x10000; i++)
	{
	  j = (i >> 8) & 0xFF;
	  j |= (i & 0xFF) << 8;

	  OP_Table[j] = OP_Table_Temp[i];
	}

      // Now we unpack the 'Delay Slot' opcode jump table

      for (i = 0; i < 0x10000; i++)
	OP_Table_Temp[i] = SH2I_ILLEGAL_DS;

      i = -1;

      while (InitOpDS[++i].Func != NULL)
	{
	  switch (InitOpDS[i].IFMT)
	    {
	    case IFMT_0:
	      OP_Table_Temp[InitOpDS[i].Mask] = InitOpDS[i].Func;
	      break;

	    case IFMT_n:
	    case IFMT_m:
	      for (j = 0; j < 0x10; j++)
		{
		  OP_Table_Temp[InitOpDS[i].Mask | (j << 8)] =
		    InitOpDS[i].Func;
		}
	      break;

	    case IFMT_nm:
	      for (j = 0; j < 0x100; j++)
		{
		  OP_Table_Temp[InitOpDS[i].Mask | (j << 4)] =
		    InitOpDS[i].Func;
		}
	      break;

	    case IFMT_md:
	    case IFMT_nd4:
	    case IFMT_d:
	    case IFMT_i:
	      for (j = 0; j < 0x100; j++)
		{
		  OP_Table_Temp[InitOpDS[i].Mask | j] = InitOpDS[i].Func;
		}
	      break;

	    case IFMT_nmd:
	    case IFMT_d12:
	    case IFMT_nd8:
	    case IFMT_ni:
	      for (j = 0; j < 0x1000; j++)
		{
		  OP_Table_Temp[InitOpDS[i].Mask | j] = InitOpDS[i].Func;
		}
	      break;
	    }
	}

      // Big Indian Opcode, we need to "byte-swap" the opcode table pointer

      for (i = 0; i < 0x10000; i++)
	{
	  j = (i >> 8) & 0xFF;
	  j |= (i & 0xFF) << 8;

	  OPDS_Table[j] = OP_Table_Temp[i];
	}

      // Fast SR Set table initialisation

      for (i = 0; i < 0x400; i++)
	{
	  t = (i & 0x001) >> 0;
	  s = (i & 0x002) >> 1;
	  q = (i & 0x100) >> 8;
	  m = (i & 0x200) >> 9;
	  il = (i & 0x0F0) >> 4;

	  Set_SR_Table[i] =
	    (t << 0) | (s << 8) | (il << 16) | (m << 25) | (q << 24);

	  SH2_LOG2 ("SR Table %.3X = %.8X\n", i, Set_SR_Table[i]);
	}

      SH2_Initialised = 1;
    }

  memset (SH2, 0, sizeof (SH2_CONTEXT));

  // Default initialisation memory...

  SH2_Add_ReadB (SH2, 0x00, 0xFF, Def_READB);
  SH2_Add_ReadW (SH2, 0x00, 0xFF, Def_READW);
  SH2_Add_ReadL (SH2, 0x00, 0xFF, Def_READL);
  SH2_Add_WriteB (SH2, 0x00, 0xFF, Def_WRITEB);
  SH2_Add_WriteW (SH2, 0x00, 0xFF, Def_WRITEW);
  SH2_Add_WriteL (SH2, 0x00, 0xFF, Def_WRITEL);

  SH2_Add_ReadB (SH2, 0xC0, 0xC0, SH2_Read_Byte_C0);
  SH2_Add_ReadW (SH2, 0xC0, 0xC0, SH2_Read_Word_C0);
  SH2_Add_ReadL (SH2, 0xC0, 0xC0, SH2_Read_Long_C0);
  SH2_Add_WriteB (SH2, 0xC0, 0xC0, SH2_Write_Byte_C0);
  SH2_Add_WriteW (SH2, 0xC0, 0xC0, SH2_Write_Word_C0);
  SH2_Add_WriteL (SH2, 0xC0, 0xC0, SH2_Write_Long_C0);

  SH2_Add_ReadB (SH2, 0xFF, 0xFF, SH2_Read_Byte_FF);
  SH2_Add_ReadW (SH2, 0xFF, 0xFF, SH2_Read_Word_FF);
  SH2_Add_ReadL (SH2, 0xFF, 0xFF, SH2_Read_Long_FF);
  SH2_Add_WriteB (SH2, 0xFF, 0xFF, SH2_Write_Byte_FF);
  SH2_Add_WriteW (SH2, 0xFF, 0xFF, SH2_Write_Word_FF);
  SH2_Add_WriteL (SH2, 0xFF, 0xFF, SH2_Write_Long_FF);

  SH2->WDT_Tab[0] = 16 - 1;
  SH2->WDT_Tab[1] = 16 - 6;
  SH2->WDT_Tab[2] = 16 - 7;
  SH2->WDT_Tab[3] = 16 - 8;
  SH2->WDT_Tab[4] = 16 - 9;
  SH2->WDT_Tab[5] = 16 - 10;
  SH2->WDT_Tab[6] = 16 - 12;
  SH2->WDT_Tab[7] = 16 - 13;

  SH2->FRT_Tab[0] = 8 - 3;
  SH2->FRT_Tab[1] = 8 - 5;
  SH2->FRT_Tab[2] = 8 - 7;
  SH2->FRT_Tab[3] = 0;		// not supported !

  SH2->BCR1 = 0x03F0;
  if (slave)
    SH2->BCR1 |= 0x8000;
}
