32x: drc: more wip, ARM untested
[picodrive.git] / cpu / sh2 / compiler.c
index 13c606b..336b1fa 100644 (file)
@@ -58,32 +58,73 @@ static u8 *tcache_ptrs[3];
 // ptr for code emiters
 static u8 *tcache_ptr;
 
+// host register tracking
+enum {
+  HR_FREE,
+  HR_CACHED, // 'val' has sh2_reg_e
+  HR_CACHED_DIRTY,
+  HR_CONST,  // 'val' has constant
+  HR_TEMP,   // reg used for temp storage
+};
+
+typedef struct {
+  u8 reg;
+  u8 type;
+  u16 stamp; // kind of a timestamp
+  u32 val;
+} temp_reg_t;
+
+// note: reg_temp[] must have at least the amount of
+// registers used by handlers in worst case (currently 4)
 #ifdef ARM
 #include "../drc/emit_arm.c"
 
 static const int reg_map_g2h[] = {
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+};
+
+static temp_reg_t reg_temp[] = {
+  {  0, },
+  {  1, },
+  { 12, },
+  { 14, },
+  {  2, },
+  {  3, },
 };
 
 #else
 #include "../drc/emit_x86.c"
 
 static const int reg_map_g2h[] = {
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
-       -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+  -1, -1, -1, -1,
+};
+
+// ax, cx, dx are usually temporaries by convention
+static temp_reg_t reg_temp[] = {
+  { xAX, },
+  { xBX, },
+  { xCX, },
+  { xDX, },
 };
 
 #endif
 
+#define T      0x00000001
+#define S      0x00000002
+#define I      0x000000f0
+#define Q      0x00000100
+#define M      0x00000200
+
 typedef enum {
   SHR_R0 = 0, SHR_R15 = 15,
   SHR_PC,  SHR_PPC, SHR_PR,   SHR_SR,
@@ -174,65 +215,332 @@ static block_desc *dr_add_block(u32 addr, int tcache_id, int *blk_id)
 
 // ---------------------------------------------------------------
 
-static void emit_move_r_imm32(sh2_reg_e dst, u32 imm)
+// register chache
+static u16 rcache_counter;
+
+static temp_reg_t *rcache_evict(void)
 {
-  int host_dst = reg_map_g2h[dst];
-  int tmp = 0;
-
-  if (host_dst != -1)
-    tmp = host_dst;
-  emith_move_r_imm(tmp, imm);
-  if (host_dst == -1)
-    emith_ctx_write(tmp, dst * 4);
+  // evict reg with oldest stamp
+  int i, oldest = -1;
+  u16 min_stamp = (u16)-1;
+
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++) {
+    if (reg_temp[i].type == HR_CACHED || reg_temp[i].type == HR_CACHED_DIRTY)
+      if (reg_temp[i].stamp <= min_stamp) {
+        min_stamp = reg_temp[i].stamp;
+        oldest = i;
+      }
+  }
+
+  if (oldest == -1) {
+    printf("no registers to evict, aborting\n");
+    exit(1);
+  }
+
+  i = oldest;
+  if (reg_temp[i].type == HR_CACHED_DIRTY) {
+    // writeback
+    emith_ctx_write(reg_temp[i].reg, reg_temp[i].val * 4);
+  }
+
+  return &reg_temp[i];
 }
 
-static void emit_move_r_r(sh2_reg_e dst, sh2_reg_e src)
+typedef enum {
+  RC_GR_READ,
+  RC_GR_WRITE,
+  RC_GR_RMW,
+} rc_gr_mode;
+
+// note: must not be called when doing conditional code
+static int rcache_get_reg(sh2_reg_e r, rc_gr_mode mode)
+{
+  temp_reg_t *tr;
+  int i;
+
+  // maybe already statically mapped?
+  i = reg_map_g2h[r];
+  if (i != -1)
+    return i;
+
+  rcache_counter++;
+
+  // maybe already cached?
+  for (i = ARRAY_SIZE(reg_temp) - 1; i >= 0; i--) {
+    if ((reg_temp[i].type == HR_CACHED || reg_temp[i].type == HR_CACHED_DIRTY) &&
+         reg_temp[i].val == r)
+    {
+      reg_temp[i].stamp = rcache_counter;
+      if (mode != RC_GR_READ)
+        reg_temp[i].type = HR_CACHED_DIRTY;
+      return reg_temp[i].reg;
+    }
+  }
+
+  // use any free reg
+  for (i = ARRAY_SIZE(reg_temp) - 1; i >= 0; i--) {
+    if (reg_temp[i].type == HR_FREE || reg_temp[i].type == HR_CONST) {
+      tr = &reg_temp[i];
+      goto do_alloc;
+    }
+  }
+
+  tr = rcache_evict();
+
+do_alloc:
+  if (mode != RC_GR_WRITE)
+    emith_ctx_read(tr->reg, r * 4);
+
+  tr->type = mode != RC_GR_READ ? HR_CACHED_DIRTY : HR_CACHED;
+  tr->val = r;
+  tr->stamp = rcache_counter;
+  return tr->reg;
+}
+
+static int rcache_get_tmp(void)
 {
-  int host_dst = reg_map_g2h[dst], host_src = reg_map_g2h[src];
-  int tmp = 0;
+  temp_reg_t *tr;
+  int i;
+
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+    if (reg_temp[i].type == HR_FREE || reg_temp[i].type == HR_CONST) {
+      tr = &reg_temp[i];
+      goto do_alloc;
+    }
+
+  tr = rcache_evict();
 
-  if (host_dst != -1 && host_src != -1) {
-    emith_move_r_r(host_dst, host_src);
+do_alloc:
+  tr->type = HR_TEMP;
+  return tr->reg;
+}
+
+static int rcache_get_arg_id(int arg)
+{
+  int i, r = 0;
+  host_arg2reg(r, arg);
+
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+    if (reg_temp[i].reg == r)
+      break;
+
+  if (i == ARRAY_SIZE(reg_temp))
+    // let's just say it's untracked arg reg
+    return r;
+
+  if (reg_temp[i].type == HR_CACHED_DIRTY) {
+    // writeback
+    emith_ctx_write(reg_temp[i].reg, reg_temp[i].val * 4);
+  }
+  else if (reg_temp[i].type == HR_TEMP) {
+    printf("arg %d reg %d already used, aborting\n", arg, r);
+    exit(1);
+  }
+
+  return i;
+}
+
+// get a reg to be used as function arg
+// it's assumed that regs are cleaned before call
+static int rcache_get_tmp_arg(int arg)
+{
+  int id = rcache_get_arg_id(arg);
+  reg_temp[id].type = HR_TEMP;
+
+  return reg_temp[id].reg;
+}
+
+// same but caches reg. RC_GR_READ only.
+static int rcache_get_reg_arg(int arg, sh2_reg_e r)
+{
+  int i, srcr, dstr, dstid;
+
+  dstid = rcache_get_arg_id(arg);
+  dstr = reg_temp[dstid].reg;
+
+  // maybe already statically mapped?
+  srcr = reg_map_g2h[r];
+  if (srcr != -1)
+    goto do_cache;
+
+  // maybe already cached?
+  for (i = ARRAY_SIZE(reg_temp) - 1; i >= 0; i--) {
+    if ((reg_temp[i].type == HR_CACHED || reg_temp[i].type == HR_CACHED_DIRTY) &&
+         reg_temp[i].val == r)
+    {
+      srcr = reg_temp[i].reg;
+      goto do_cache;
+    }
+  }
+
+  // must read
+  srcr = dstr;
+  emith_ctx_read(srcr, r * 4);
+
+do_cache:
+  if (srcr != dstr)
+    emith_move_r_r(dstr, srcr);
+
+  reg_temp[dstid].stamp = ++rcache_counter;
+  reg_temp[dstid].type = HR_CACHED;
+  reg_temp[dstid].val = r;
+  return dstr;
+}
+
+static void rcache_free_tmp(int hr)
+{
+  int i;
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+    if (reg_temp[i].reg == hr)
+      break;
+
+  if (i == ARRAY_SIZE(reg_temp) || reg_temp[i].type != HR_TEMP) {
+    printf("rcache_free_tmp fail: #%i hr %d, type %d\n", i, hr, reg_temp[i].type);
     return;
   }
 
-  if (host_src != -1)
-    tmp = host_src;
-  if (host_dst != -1)
-    tmp = host_dst;
+  reg_temp[i].type = HR_FREE;
+}
 
-  if (host_src == -1)
-    emith_ctx_read(tmp, src * 4);
-  if (host_dst == -1)
-    emith_ctx_write(tmp, dst * 4);
+static void rcache_clean(void)
+{
+  int i;
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+    if (reg_temp[i].type == HR_CACHED_DIRTY) {
+      // writeback
+      emith_ctx_write(reg_temp[i].reg, reg_temp[i].val * 4);
+      reg_temp[i].type = HR_CACHED;
+    }
 }
 
-static void emit_braf(sh2_reg_e reg, u32 pc)
+static void rcache_invalidate(void)
 {
-  int host_reg = reg_map_g2h[reg];
-  if (host_reg == -1) {
-    emith_ctx_read(0, reg * 4);
-  } else
-    emith_move_r_r(0, host_reg);
-  emith_add_r_imm(0, pc);
-
-  emith_ctx_write(0, SHR_PPC * 4);
+  int i;
+  for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+    reg_temp[i].type = HR_FREE;
+  rcache_counter = 0;
 }
 
-/*
-static int sh2_translate_op4(int op)
+static void rcache_flush(void)
 {
-  switch (op & 0x000f)
-  {
-  case 0x0b:
-  default:
-    emith_pass_arg(2, sh2, op);
-    emith_call(sh2_do_op);
+  rcache_clean();
+  rcache_invalidate();
+}
+
+// ---------------------------------------------------------------
+
+static void emit_move_r_imm32(sh2_reg_e dst, u32 imm)
+{
+  int hr = rcache_get_reg(dst, RC_GR_WRITE);
+  emith_move_r_imm(hr, imm);
+}
+
+static void emit_move_r_r(sh2_reg_e dst, sh2_reg_e src)
+{
+  int hr_d = rcache_get_reg(dst, RC_GR_WRITE);
+  int hr_s = rcache_get_reg(src, RC_GR_READ);
+
+  emith_move_r_r(hr_d, hr_s);
+}
+
+// arguments must be ready
+// reg cache must be clean before call
+static int emit_memhandler_read(int size)
+{
+  int ctxr;
+  host_arg2reg(ctxr, 1);
+  emith_move_r_r(ctxr, CONTEXT_REG);
+  switch (size) {
+  case 0: // 8
+    emith_call(p32x_sh2_read8);
+    break;
+  case 1: // 16
+    emith_call(p32x_sh2_read16);
+    break;
+  case 2: // 32
+    emith_call(p32x_sh2_read32);
     break;
   }
+  rcache_invalidate();
+  // assuming arg0 and retval reg matches
+  return rcache_get_tmp_arg(0);
+}
 
-  return 0;
+static void emit_memhandler_write(int size)
+{
+  int ctxr;
+  host_arg2reg(ctxr, 2);
+  emith_move_r_r(ctxr, CONTEXT_REG);
+  switch (size) {
+  case 0: // 8
+    emith_call(p32x_sh2_write8);
+    break;
+  case 1: // 16
+    emith_call(p32x_sh2_write16);
+    break;
+  case 2: // 32
+    emith_call(p32x_sh2_write32);
+    break;
+  }
+  rcache_invalidate();
 }
+
+/*
+MOV   #imm,Rn       1110nnnniiiiiiii
+MOV.W @(disp,PC),Rn 1001nnnndddddddd
+MOV.L @(disp,PC),Rn 1101nnnndddddddd
+MOV   Rm,Rn         0110nnnnmmmm0011
+MOV.B @Rm,Rn        0110nnnnmmmm0000
+MOV.W @Rm,Rn        0110nnnnmmmm0001
+MOV.L @Rm,Rn        0110nnnnmmmm0010
+MOV.B @Rm+,Rn       0110nnnnmmmm0100
+MOV.W @Rm+,Rn       0110nnnnmmmm0101
+MOV.L @Rm+,Rn       0110nnnnmmmm0110
+MOV.B R0,@(disp,Rn) 10000000nnnndddd
+MOV.W R0,@(disp,Rn) 10000001nnnndddd
+MOV.B @(disp,Rm),R0 10000100mmmmdddd
+MOV.W @(disp,Rm),R0 10000101mmmmdddd
+MOV.L @(disp,Rm),Rn 0101nnnnmmmmdddd
+MOV.B    R0,@(disp,GBR)   11000000dddddddd
+MOV.W    R0,@(disp,GBR)   11000001dddddddd
+MOV.L    R0,@(disp,GBR)   11000010dddddddd
+MOV.B    @(disp,GBR),R0   11000100dddddddd
+MOV.W    @(disp,GBR),R0   11000101dddddddd
+MOV.L    @(disp,GBR),R0   11000110dddddddd
+MOVA     @(disp,PC),R0    11000111dddddddd
+SWAP.B Rm,Rn              0110nnnnmmmm1000
+SWAP.W Rm,Rn              0110nnnnmmmm1001
+ADD         #imm,Rn  0111nnnniiiiiiii
+CMP/EQ      #imm,R0  10001000iiiiiiii
+EXTS.B Rm,Rn         0110nnnnmmmm1110
+EXTS.W Rm,Rn         0110nnnnmmmm1111
+EXTU.B Rm,Rn         0110nnnnmmmm1100
+EXTU.W Rm,Rn         0110nnnnmmmm1101
+MAC       @Rm+,@Rn+  0100nnnnmmmm1111
+NEG       Rm,Rn      0110nnnnmmmm1011
+NEGC      Rm,Rn      0110nnnnmmmm1010
+AND      #imm,R0             11001001iiiiiiii
+AND.B    #imm,@(R0,GBR)      11001101iiiiiiii
+NOT      Rm,Rn               0110nnnnmmmm0111
+OR       #imm,R0             11001011iiiiiiii
+OR.B     #imm,@(R0,GBR)      11001111iiiiiiii
+TAS.B    @Rn                 0100nnnn00011011
+TST      #imm,R0             11001000iiiiiiii
+TST.B    #imm,@(R0,GBR)      11001100iiiiiiii
+XOR      #imm,R0             11001010iiiiiiii
+XOR.B    #imm,@(R0,GBR)      11001110iiiiiiii
+SHLL2    Rn        0100nnnn00001000
+SHLR2    Rn        0100nnnn00001001
+SHLL8    Rn        0100nnnn00011000
+SHLR8    Rn        0100nnnn00011001
+SHLL16 Rn          0100nnnn00101000
+SHLR16 Rn          0100nnnn00101001
+LDC      Rm,GBR    0100mmmm00011110
+LDC      Rm,VBR    0100mmmm00101110
+LDS      Rm,MACH   0100mmmm00001010
+LDS      Rm,MACL   0100mmmm00011010
+LDS      Rm,PR     0100mmmm00101010
+TRAPA    #imm      11000011iiiiiiii
 */
 
 #define DELAYED_OP \
@@ -243,6 +551,18 @@ static int sh2_translate_op4(int op)
     goto default_; \
 }
 
+#define GET_Fx() \
+  ((op >> 4) & 0x0f)
+
+#define GET_Rm GET_Fx
+
+#define GET_Rn() \
+  ((op >> 8) & 0x0f)
+
+#define CHECK_FX_LT(n) \
+  if (GET_Fx() < n) \
+    goto default_
+
 static void *sh2_translate(SH2 *sh2, block_desc *other_block)
 {
   void *block_entry;
@@ -251,7 +571,7 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
   int op, delayed_op = 0, test_irq = 0;
   int tcache_id = 0, blkid = 0;
   int cycles = 0;
-  u32 tmp, tmp2;
+  u32 tmp, tmp2, tmp3, tmp4;
 
   // validate PC
   tmp = sh2->pc >> 29;
@@ -311,8 +631,31 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
 
     switch ((op >> 12) & 0x0f)
     {
+    /////////////////////////////////////////////
     case 0x00:
-      switch (op & 0x0f) {
+      switch (op & 0x0f)
+      {
+      case 0x02:
+        tmp = rcache_get_reg(GET_Rn(), RC_GR_WRITE);
+        switch (GET_Fx())
+        {
+        case 0: // STC SR,Rn  0000nnnn00000010
+          tmp2 = SHR_SR;
+          break;
+        case 1: // STC GBR,Rn 0000nnnn00010010
+          tmp2 = SHR_GBR;
+          break;
+        case 2: // STC VBR,Rn 0000nnnn00100010
+          tmp2 = SHR_VBR;
+          break;
+        default:
+          goto default_;
+        }
+        tmp3 = rcache_get_reg(tmp2, RC_GR_READ);
+        emith_move_r_r(tmp, tmp3);
+        if (tmp2 == SHR_SR)
+          emith_clear_msb(tmp, tmp, 20); // reserved bits defined by ISA as 0
+        goto end_op;
       case 0x03:
         CHECK_UNHANDLED_BITS(0xd0);
         // BRAF Rm    0000mmmm00100011
@@ -320,43 +663,533 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
         DELAYED_OP;
         if (!(op & 0x20))
           emit_move_r_imm32(SHR_PR, pc + 2);
-        emit_braf((op >> 8) & 0x0f, pc + 2);
+        tmp = rcache_get_reg(SHR_PPC, RC_GR_WRITE);
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        emith_move_r_r(tmp, tmp2);
+        emith_add_r_imm(tmp, pc + 2);
+        cycles++;
+        goto end_op;
+      case 0x04: // MOV.B Rm,@(R0,Rn)   0000nnnnmmmm0100
+      case 0x05: // MOV.W Rm,@(R0,Rn)   0000nnnnmmmm0101
+      case 0x06: // MOV.L Rm,@(R0,Rn)   0000nnnnmmmm0110
+        rcache_clean();
+        tmp  = rcache_get_reg_arg(0, SHR_R0);
+        tmp2 = rcache_get_reg_arg(1, GET_Rm());
+        tmp3 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        emith_add_r_r(tmp, tmp3);
+        emit_memhandler_write(op & 3);
+        goto end_op;
+      case 0x07:
+        // MUL.L     Rm,Rn      0000nnnnmmmm0111
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp3 = rcache_get_reg(SHR_MACL, RC_GR_WRITE);
+        emith_mul(tmp3, tmp2, tmp);
         cycles++;
         goto end_op;
+      case 0x08:
+        CHECK_UNHANDLED_BITS(0xf00);
+        switch (GET_Fx())
+        {
+        case 0: // CLRT               0000000000001000
+          tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_bic_r_imm(tmp, T);
+          break;
+        case 1: // SETT               0000000000011000
+          tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_or_r_imm(tmp, T);
+          break;
+        case 2: // CLRMAC             0000000000101000
+          tmp = rcache_get_reg(SHR_MACL, RC_GR_WRITE);
+          emith_move_r_imm(tmp, 0);
+          tmp = rcache_get_reg(SHR_MACH, RC_GR_WRITE);
+          emith_move_r_imm(tmp, 0);
+          break;
+        default:
+          goto default_;
+        }
+        goto end_op;
       case 0x09:
-        CHECK_UNHANDLED_BITS(0xf0);
-        // NOP        0000000000001001
+        switch (GET_Fx())
+        {
+        case 0: // NOP        0000000000001001
+          CHECK_UNHANDLED_BITS(0xf00);
+          break;
+        case 1: // DIV0U      0000000000011001
+          CHECK_UNHANDLED_BITS(0xf00);
+          tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_bic_r_imm(tmp, M|Q|T);
+          break;
+        case 2: // MOVT Rn    0000nnnn00101001
+          tmp  = rcache_get_reg(SHR_SR, RC_GR_READ);
+          tmp2 = rcache_get_reg(GET_Rn(), RC_GR_WRITE);
+          emith_clear_msb(tmp2, tmp, 31);
+          break;
+        default:
+          goto default_;
+        }
+        goto end_op;
+      case 0x0a:
+        tmp = rcache_get_reg(GET_Rn(), RC_GR_WRITE);
+        switch (GET_Fx())
+        {
+        case 0: // STS      MACH,Rn   0000nnnn00001010
+          tmp2 = SHR_MACH;
+          break;
+        case 1: // STS      MACL,Rn   0000nnnn00011010
+          tmp2 = SHR_MACL;
+          break;
+        case 2: // STS      PR,Rn     0000nnnn00101010
+          tmp2 = SHR_PR;
+          break;
+        default:
+          goto default_;
+        }
+        tmp2 = rcache_get_reg(tmp2, RC_GR_READ);
+        emith_move_r_r(tmp, tmp2);
         goto end_op;
       case 0x0b:
-        CHECK_UNHANDLED_BITS(0xd0);
-        DELAYED_OP;
-        if (!(op & 0x20)) {
-          // RTS        0000000000001011
+        CHECK_UNHANDLED_BITS(0xf00);
+        switch (GET_Fx())
+        {
+        case 0: // RTS        0000000000001011
+          DELAYED_OP;
           emit_move_r_r(SHR_PPC, SHR_PR);
           cycles++;
-        } else {
-          // RTE        0000000000101011
+          break;
+        case 1: // SLEEP      0000000000011011
+          emit_move_r_imm32(SHR_PC, pc - 2);
+          tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_clear_msb(tmp, tmp, 20); // clear cycles
+          test_irq = 1;
+          cycles = 1;
+          break;
+        case 2: // RTE        0000000000101011
           //emit_move_r_r(SHR_PC, SHR_PR);
           emit_move_r_imm32(SHR_PC, pc - 2);
+          rcache_flush();
           emith_pass_arg_r(0, CONTEXT_REG);
           emith_pass_arg_imm(1, op);
           emith_call(sh2_do_op);
           emit_move_r_r(SHR_PPC, SHR_PC);
           test_irq = 1;
           cycles += 3;
+          break;
+        default:
+          goto default_;
         }
         goto end_op;
+      case 0x0c: // MOV.B    @(R0,Rm),Rn      0000nnnnmmmm1100
+      case 0x0d: // MOV.W    @(R0,Rm),Rn      0000nnnnmmmm1101
+      case 0x0e: // MOV.L    @(R0,Rm),Rn      0000nnnnmmmm1110
+        rcache_clean();
+        tmp  = rcache_get_reg_arg(0, SHR_R0);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_add_r_r(tmp, tmp2);
+        tmp  = emit_memhandler_read(op & 3);
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_WRITE);
+        rcache_free_tmp(tmp);
+        if ((op & 3) != 2) {
+          emith_sext(tmp2, tmp, (op & 1) ? 16 : 8);
+        } else
+          emith_move_r_r(tmp2, tmp);
+        goto end_op;
+      case 0x0f: // MAC.L   @Rm+,@Rn+  0000nnnnmmmm1111
+        // TODO
+        break;
       }
       goto default_;
 
+    /////////////////////////////////////////////
+    case 0x01:
+      // MOV.L Rm,@(disp,Rn) 0001nnnnmmmmdddd
+      rcache_clean();
+      tmp  = rcache_get_reg_arg(0, GET_Rn());
+      tmp2 = rcache_get_reg_arg(1, GET_Rm());
+      emith_add_r_imm(tmp, (op & 0x0f) * 4);
+      emit_memhandler_write(2);
+      goto end_op;
+
+    case 0x02:
+      switch (op & 0x0f)
+      {
+      case 0x00: // MOV.B Rm,@Rn        0010nnnnmmmm0000
+      case 0x01: // MOV.W Rm,@Rn        0010nnnnmmmm0001
+      case 0x02: // MOV.L Rm,@Rn        0010nnnnmmmm0010
+        rcache_clean();
+        rcache_get_reg_arg(0, GET_Rn());
+        rcache_get_reg_arg(1, GET_Rm());
+        emit_memhandler_write(op & 3);
+        goto end_op;
+      case 0x04: // MOV.B Rm,@–Rn       0010nnnnmmmm0100
+      case 0x05: // MOV.W Rm,@–Rn       0010nnnnmmmm0101
+      case 0x06: // MOV.L Rm,@–Rn       0010nnnnmmmm0110
+        tmp = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        emith_sub_r_imm(tmp, (1 << (op & 3)));
+        rcache_clean();
+        rcache_get_reg_arg(0, GET_Rn());
+        rcache_get_reg_arg(1, GET_Rm());
+        emit_memhandler_write(op & 3);
+        goto end_op;
+      case 0x07: // DIV0S Rm,Rn         0010nnnnmmmm0111
+        tmp  = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp3 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_bic_r_imm(tmp, M|Q|T);
+        emith_tst_r_imm(tmp2, (1<<31));
+        EMITH_SJMP_START(DCOND_EQ);
+        emith_or_r_imm_c(DCOND_NE, tmp, Q);
+        EMITH_SJMP_END(DCOND_EQ);
+        emith_tst_r_imm(tmp3, (1<<31));
+        EMITH_SJMP_START(DCOND_EQ);
+        emith_or_r_imm_c(DCOND_NE, tmp, M);
+        EMITH_SJMP_END(DCOND_EQ);
+        emith_teq_r_r(tmp2, tmp3);
+        EMITH_SJMP_START(DCOND_PL);
+        emith_or_r_imm_c(DCOND_MI, tmp, T);
+        EMITH_SJMP_END(DCOND_PL);
+        goto end_op;
+      case 0x08: // TST Rm,Rn           0010nnnnmmmm1000
+        tmp  = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp3 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_bic_r_imm(tmp, T);
+        emith_tst_r_r(tmp2, tmp3);
+        EMITH_SJMP_START(DCOND_NE);
+        emith_or_r_imm_c(DCOND_EQ, tmp, T);
+        EMITH_SJMP_END(DCOND_NE);
+        goto end_op;
+      case 0x09: // AND Rm,Rn           0010nnnnmmmm1001
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_and_r_r(tmp, tmp2);
+        goto end_op;
+      case 0x0a: // XOR Rm,Rn           0010nnnnmmmm1010
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_eor_r_r(tmp, tmp2);
+        goto end_op;
+      case 0x0b: // OR  Rm,Rn           0010nnnnmmmm1011
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_or_r_r(tmp, tmp2);
+        goto end_op;
+      case 0x0c: // CMP/STR Rm,Rn       0010nnnnmmmm1100
+        tmp  = rcache_get_tmp();
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp3 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_eor_r_r_r(tmp, tmp2, tmp3);
+        tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        emith_bic_r_imm(tmp2, T);
+        emith_tst_r_imm(tmp, 0x000000ff);
+        EMITH_SJMP_START(DCOND_NE);
+        emith_or_r_imm_c(DCOND_EQ, tmp2, T);
+        EMITH_SJMP_END(DCOND_NE);
+        emith_tst_r_imm(tmp, 0x0000ff00);
+        EMITH_SJMP_START(DCOND_NE);
+        emith_or_r_imm_c(DCOND_EQ, tmp2, T);
+        EMITH_SJMP_END(DCOND_NE);
+        emith_tst_r_imm(tmp, 0x00ff0000);
+        EMITH_SJMP_START(DCOND_NE);
+        emith_or_r_imm_c(DCOND_EQ, tmp2, T);
+        EMITH_SJMP_END(DCOND_NE);
+        emith_tst_r_imm(tmp, 0xff000000);
+        EMITH_SJMP_START(DCOND_NE);
+        emith_or_r_imm_c(DCOND_EQ, tmp2, T);
+        EMITH_SJMP_END(DCOND_NE);
+        rcache_free_tmp(tmp);
+        goto end_op;
+      case 0x0d: // XTRCT  Rm,Rn        0010nnnnmmmm1101
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_lsr(tmp, tmp, 16);
+        emith_or_r_r_r_lsl(tmp, tmp, tmp2, 16);
+        goto end_op;
+      case 0x0e: // MULU.W Rm,Rn        0010nnnnmmmm1110
+      case 0x0f: // MULS.W Rm,Rn        0010nnnnmmmm1111
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp  = rcache_get_reg(SHR_MACL, RC_GR_WRITE);
+        if (op & 1) {
+          emith_sext(tmp, tmp2, 16);
+        } else
+          emith_clear_msb(tmp, tmp2, 16);
+        tmp3 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp2 = rcache_get_tmp();
+        if (op & 1) {
+          emith_sext(tmp2, tmp3, 16);
+        } else
+          emith_clear_msb(tmp2, tmp3, 16);
+        emith_mul(tmp, tmp, tmp2);
+        rcache_free_tmp(tmp2);
+//      FIXME: causes timing issues in Doom?
+//        cycles++;
+        goto end_op;
+      }
+      goto default_;
+
+    /////////////////////////////////////////////
+    case 0x03:
+      switch (op & 0x0f)
+      {
+      case 0x00: // CMP/EQ Rm,Rn        0011nnnnmmmm0000
+      case 0x02: // CMP/HS Rm,Rn        0011nnnnmmmm0010
+      case 0x03: // CMP/GE Rm,Rn        0011nnnnmmmm0011
+      case 0x06: // CMP/HI Rm,Rn        0011nnnnmmmm0110
+      case 0x07: // CMP/GT Rm,Rn        0011nnnnmmmm0111
+        tmp  = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp3 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        emith_bic_r_imm(tmp, T);
+        emith_cmp_r_r(tmp2, tmp3);
+        switch (op & 0x07)
+        {
+        case 0x00: // CMP/EQ
+          EMITH_SJMP_START(DCOND_NE);
+          emith_or_r_imm_c(DCOND_EQ, tmp, T);
+          EMITH_SJMP_END(DCOND_NE);
+          break;
+        case 0x02: // CMP/HS
+          EMITH_SJMP_START(DCOND_LO);
+          emith_or_r_imm_c(DCOND_HS, tmp, T);
+          EMITH_SJMP_END(DCOND_LO);
+          break;
+        case 0x03: // CMP/GE
+          EMITH_SJMP_START(DCOND_LT);
+          emith_or_r_imm_c(DCOND_GE, tmp, T);
+          EMITH_SJMP_END(DCOND_LT);
+          break;
+        case 0x06: // CMP/HI
+          EMITH_SJMP_START(DCOND_LS);
+          emith_or_r_imm_c(DCOND_HI, tmp, T);
+          EMITH_SJMP_END(DCOND_LS);
+          break;
+        case 0x07: // CMP/GT
+          EMITH_SJMP_START(DCOND_LE);
+          emith_or_r_imm_c(DCOND_GT, tmp, T);
+          EMITH_SJMP_END(DCOND_LE);
+          break;
+        }
+        goto end_op;
+      case 0x04: // DIV1    Rm,Rn       0011nnnnmmmm0100
+        // TODO
+        break;
+      case 0x05: // DMULU.L Rm,Rn       0011nnnnmmmm0101
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp3 = rcache_get_reg(SHR_MACL, RC_GR_WRITE);
+        tmp4 = rcache_get_reg(SHR_MACH, RC_GR_WRITE);
+        emith_mul_u64(tmp3, tmp4, tmp, tmp2);
+        goto end_op;
+      case 0x08: // SUB     Rm,Rn       0011nnnnmmmm1000
+      case 0x0c: // ADD     Rm,Rn       0011nnnnmmmm1100
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        if (op & 4) {
+          emith_add_r_r(tmp, tmp2);
+        } else
+          emith_sub_r_r(tmp, tmp2);
+        goto end_op;
+      case 0x0a: // SUBC    Rm,Rn       0011nnnnmmmm1010
+      case 0x0e: // ADDC    Rm,Rn       0011nnnnmmmm1110
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp3 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        if (op & 4) { // adc
+          emith_set_carry(tmp3);
+          emith_adcf_r_r(tmp, tmp2);
+          emith_carry_to_t(tmp3, 0);
+        } else {
+          emith_set_carry_sub(tmp3);
+          emith_sbcf_r_r(tmp, tmp2);
+          emith_carry_to_t(tmp3, 1);
+        }
+        goto end_op;
+      case 0x0b: // SUBV    Rm,Rn       0011nnnnmmmm1011
+      case 0x0f: // ADDV    Rm,Rn       0011nnnnmmmm1111
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp3 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+        emith_bic_r_imm(tmp3, T);
+        if (op & 4) {
+          emith_addf_r_r(tmp, tmp2);
+        } else
+          emith_subf_r_r(tmp, tmp2);
+        EMITH_SJMP_START(DCOND_VC);
+        emith_or_r_imm_c(DCOND_VS, tmp3, T);
+        EMITH_SJMP_END(DCOND_VC);
+        goto end_op;
+      case 0x0d: // DMULS.L Rm,Rn       0011nnnnmmmm1101
+        tmp  = rcache_get_reg(GET_Rn(), RC_GR_READ);
+        tmp2 = rcache_get_reg(GET_Rm(), RC_GR_READ);
+        tmp3 = rcache_get_reg(SHR_MACL, RC_GR_WRITE);
+        tmp4 = rcache_get_reg(SHR_MACH, RC_GR_WRITE);
+        emith_mul_s64(tmp3, tmp4, tmp, tmp2);
+        goto end_op;
+      }
+      goto default_;
+
+    /////////////////////////////////////////////
     case 0x04:
-      switch (op & 0x0f) {
-      case 0x07:
-        if ((op & 0xf0) != 0)
+      switch (op & 0x0f)
+      {
+      case 0x00:
+        switch (GET_Fx())
+        {
+        case 0: // SHLL Rn    0100nnnn00000000
+        case 2: // SHAL Rn    0100nnnn00100000
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_lslf(tmp, tmp, 1);
+          emith_carry_to_t(tmp2,  0);
+          goto end_op;
+        case 1: // DT Rn      0100nnnn00010000
+          if (p32x_sh2_read16(pc, sh2) == 0x8bfd) { // BF #-2
+            emith_sh2_dtbf_loop();
+            goto end_op;
+          }
+          tmp = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_bic_r_imm(tmp2, T);
+          emith_subf_r_imm(tmp, 1);
+          EMITH_SJMP_START(DCOND_NE);
+          emith_or_r_imm_c(DCOND_EQ, tmp2, T);
+          EMITH_SJMP_END(DCOND_NE);
+          goto end_op;
+        }
+        goto default_;
+      case 0x01:
+        switch (GET_Fx())
+        {
+        case 0: // SHLR Rn    0100nnnn00000001
+        case 2: // SHAR Rn    0100nnnn00100001
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          if (op & 0x20) {
+            emith_asrf(tmp, tmp, 1);
+          } else
+            emith_lsrf(tmp, tmp, 1);
+          emith_carry_to_t(tmp2, 0);
+          goto end_op;
+        case 1: // CMP/PZ Rn  0100nnnn00010001
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_bic_r_imm(tmp2, T);
+          emith_cmp_r_imm(tmp, 0);
+          EMITH_SJMP_START(DCOND_LT);
+          emith_or_r_imm_c(DCOND_GE, tmp2, T);
+          EMITH_SJMP_END(DCOND_LT);
+          goto end_op;
+        }
+        goto default_;
+      case 0x02:
+      case 0x03:
+        switch (op & 0x3f)
+        {
+        case 0x02: // STS.L    MACH,@–Rn 0100nnnn00000010
+          tmp = SHR_MACH;
+          break;
+        case 0x12: // STS.L    MACL,@–Rn 0100nnnn00010010
+          tmp = SHR_MACL;
+          break;
+        case 0x22: // STS.L    PR,@–Rn   0100nnnn00100010
+          tmp = SHR_PR;
+          break;
+        case 0x03: // STC.L    SR,@–Rn   0100nnnn00000011
+          tmp = SHR_SR;
+          break;
+        case 0x13: // STC.L    GBR,@–Rn  0100nnnn00010011
+          tmp = SHR_GBR;
+          break;
+        case 0x23: // STC.L    VBR,@–Rn  0100nnnn00100011
+          tmp = SHR_VBR;
+          break;
+        default:
           goto default_;
-        // LDC.L @Rm+,SR  0100mmmm00000111
-        test_irq = 1;
+        }
+        tmp2 = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        emith_sub_r_imm(tmp2, 4);
+        rcache_clean();
+        rcache_get_reg_arg(0, GET_Rn());
+        tmp3 = rcache_get_reg_arg(1, tmp);
+        if (tmp == SHR_SR)
+          emith_clear_msb(tmp3, tmp3, 20); // reserved bits defined by ISA as 0
+        emit_memhandler_write(2);
+        goto end_op;
+      case 0x04:
+      case 0x05:
+        switch (op & 0x3f)
+        {
+        case 0x04: // ROTL   Rn          0100nnnn00000100
+        case 0x05: // ROTR   Rn          0100nnnn00000101
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          if (op & 1) {
+            emith_rorf(tmp, tmp, 1);
+          } else
+            emith_rolf(tmp, tmp, 1);
+          emith_carry_to_t(tmp2, 0);
+          goto end_op;
+        case 0x24: // ROTCL  Rn          0100nnnn00100100
+        case 0x25: // ROTCR  Rn          0100nnnn00100101
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_set_carry(tmp2);
+          if (op & 1) {
+            emith_rorcf(tmp);
+          } else
+            emith_rolcf(tmp);
+          emith_carry_to_t(tmp2, 0);
+          goto end_op;
+        case 0x15: // CMP/PL Rn          0100nnnn00010101
+          tmp  = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+          tmp2 = rcache_get_reg(SHR_SR, RC_GR_RMW);
+          emith_bic_r_imm(tmp2, T);
+          emith_cmp_r_imm(tmp, 0);
+          EMITH_SJMP_START(DCOND_LE);
+          emith_or_r_imm_c(DCOND_GT, tmp2, T);
+          EMITH_SJMP_END(DCOND_LE);
+          goto end_op;
+        }
         goto default_;
+      case 0x06:
+      case 0x07:
+        switch (op & 0x3f)
+        {
+        case 0x06: // LDS.L @Rm+,MACH 0100mmmm00000110
+          tmp = SHR_MACH;
+          break;
+        case 0x16: // LDS.L @Rm+,MACL 0100mmmm00010110
+          tmp = SHR_MACL;
+          break;
+        case 0x26: // LDS.L @Rm+,PR   0100mmmm00100110
+          tmp = SHR_PR;
+          break;
+        case 0x07: // LDC.L @Rm+,SR   0100mmmm00000111
+          tmp = SHR_SR;
+          break;
+        case 0x17: // LDC.L @Rm+,GBR  0100mmmm00010111
+          tmp = SHR_GBR;
+          break;
+        case 0x27: // LDC.L @Rm+,VBR  0100mmmm00100111
+          tmp = SHR_VBR;
+          break;
+        default:
+          goto default_;
+        }
+        rcache_clean();
+        rcache_get_reg_arg(0, GET_Rn());
+        tmp2 = emit_memhandler_read(2);
+        if (tmp == SHR_SR) {
+          emith_write_sr(tmp2);
+          test_irq = 1;
+        } else {
+          tmp = rcache_get_reg(tmp, RC_GR_WRITE);
+          emith_move_r_r(tmp, tmp2);
+        }
+        rcache_free_tmp(tmp2);
+        tmp = rcache_get_reg(GET_Rn(), RC_GR_RMW);
+        emith_add_r_imm(tmp, 4);
+        goto end_op;
       case 0x0b:
         if ((op & 0xd0) != 0)
           goto default_;
@@ -377,6 +1210,7 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
       }
       goto default_;
 
+    /////////////////////////////////////////////
     case 0x08:
       switch (op & 0x0f00) {
       // BT/S label 10001101dddddddd
@@ -389,19 +1223,30 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
       // BT   label 10001001dddddddd
       case 0x0900:
       // BF   label 10001011dddddddd
-      case 0x0b00:
-        tmp = ((signed int)(op << 24) >> 23);
-        tmp2 = delayed_op ? SHR_PPC : SHR_PC;
-        emit_move_r_imm32(tmp2, pc + (delayed_op ? 2 : 0));
-        emith_test_t();
-        EMITH_CONDITIONAL(emit_move_r_imm32(tmp2, pc + tmp + 2), (op & 0x0200) ? 1 : 0);
+      case 0x0b00: {
+        // jmp_cond ~ cond when guest doesn't jump
+        int jmp_cond  = (op & 0x0200) ? DCOND_NE : DCOND_EQ;
+        int insn_cond = (op & 0x0200) ? DCOND_EQ : DCOND_NE;
+        signed int offs = ((signed int)(op << 24) >> 23);
+        tmp = rcache_get_reg(delayed_op ? SHR_PPC : SHR_PC, RC_GR_WRITE);
+        emith_move_r_imm(tmp, pc + (delayed_op ? 2 : 0));
+        emith_sh2_test_t();
+        EMITH_SJMP_START(jmp_cond);
+        if (!delayed_op)
+          offs += 2;
+        if (offs < 0) {
+          emith_sub_r_imm_c(insn_cond, tmp, -offs);
+        } else
+          emith_add_r_imm_c(insn_cond, tmp, offs);
+        EMITH_SJMP_END(jmp_cond);
         cycles += 2;
         if (!delayed_op)
           goto end_block;
         goto end_op;
-      }
+      }}
       goto default_;
 
+    /////////////////////////////////////////////
     case 0x0a:
       // BRA  label 1010dddddddddddd
       DELAYED_OP;
@@ -411,6 +1256,7 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
       cycles++;
       break;
 
+    /////////////////////////////////////////////
     case 0x0b:
       // BSR  label 1011dddddddddddd
       DELAYED_OP;
@@ -420,6 +1266,7 @@ static void *sh2_translate(SH2 *sh2, block_desc *other_block)
     default:
     default_:
       emit_move_r_imm32(SHR_PC, pc - 2);
+      rcache_flush();
       emith_pass_arg_r(0, CONTEXT_REG);
       emith_pass_arg_imm(1, op);
       emith_call(sh2_do_op);
@@ -431,6 +1278,7 @@ end_op:
       emit_move_r_r(SHR_PC, SHR_PPC);
 
     if (test_irq && delayed_op != 2) {
+      rcache_flush();
       emith_pass_arg_r(0, CONTEXT_REG);
       emith_call(sh2_test_irq);
       break;
@@ -468,10 +1316,9 @@ end_block:
     }
   }
 
-  if (reg_map_g2h[SHR_SR] == -1) {
-    emith_ctx_sub(cycles << 12, SHR_SR * 4);
-  } else
-    emith_sub_r_imm(reg_map_g2h[SHR_SR], cycles << 12);
+  tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+  emith_sub_r_imm(tmp, cycles << 12);
+  rcache_flush();
   emith_jump(sh2_drc_exit);
   tcache_ptrs[tcache_id] = tcache_ptr;