sh2: handle some branch exceptions
authornotaz <notasas@gmail.com>
Fri, 18 Aug 2017 00:44:25 +0000 (03:44 +0300)
committertwinaphex <libretro@gmail.com>
Fri, 18 Aug 2017 03:14:33 +0000 (05:14 +0200)
cpu/sh2/compiler.c
cpu/sh2/compiler.h
cpu/sh2/mame/sh2pico.c

index 2dd3430..3f006c7 100644 (file)
@@ -2590,8 +2590,9 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
 
     default:
     default_:
-      elprintf_sh2(sh2, EL_ANOMALY,
-        "drc: illegal op %04x @ %08x", op, pc - 2);
+      if (!(op_flags[i] & OF_B_IN_DS))
+        elprintf_sh2(sh2, EL_ANOMALY,
+          "drc: illegal op %04x @ %08x", op, pc - 2);
 
       tmp = rcache_get_reg(SHR_SP, RC_GR_RMW);
       emith_sub_r_imm(tmp, 4*2);
@@ -2604,10 +2605,16 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
       // push PC
       rcache_get_reg_arg(0, SHR_SP);
       tmp = rcache_get_tmp_arg(1);
-      emith_move_r_imm(tmp, pc - 2);
+      if (drcf.pending_branch_indirect) {
+        tmp2 = rcache_get_reg(SHR_PC, RC_GR_READ);
+        emith_move_r_r(tmp, tmp2);
+      }
+      else
+        emith_move_r_imm(tmp, pc - 2);
       emit_memhandler_write(2);
       // obtain new PC
-      emit_memhandler_read_rr(SHR_PC, SHR_VBR, 4 * 4, 2);
+      v = (op_flags[i] & OF_B_IN_DS) ? 6 : 4;
+      emit_memhandler_read_rr(SHR_PC, SHR_VBR, v * 4, 2);
       // indirect jump -> back to dispatcher
       rcache_flush();
       emith_jump(sh2_drc_dispatcher);
@@ -4062,6 +4069,22 @@ void scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
         is_slave ? 's' : 'm', op, pc);
       break;
     }
+
+    if (op_flags[i] & OF_DELAY_OP) {
+      switch (opd->op) {
+      case OP_BRANCH:
+      case OP_BRANCH_CT:
+      case OP_BRANCH_CF:
+      case OP_BRANCH_R:
+      case OP_BRANCH_RF:
+        elprintf(EL_ANOMALY, "%csh2 drc: branch in DS @ %08x",
+          is_slave ? 's' : 'm', pc);
+        opd->op = OP_UNHANDLED;
+        op_flags[i] |= OF_B_IN_DS;
+        next_is_delay = 0;
+        break;
+      }
+    }
   }
   i_end = i;
   end_pc = pc;
index ef1944b..61d8d2d 100644 (file)
@@ -20,6 +20,7 @@ void sh2_drc_frame(void);
 #define OF_BTARGET    (1 << 1)
 #define OF_T_SET      (1 << 2) // T is known to be set
 #define OF_T_CLEAR    (1 << 3) // ... clear
+#define OF_B_IN_DS    (1 << 4)
 
 void scan_block(unsigned int base_pc, int is_slave,
                unsigned char *op_flags, unsigned int *end_pc,
index 7899878..7324166 100644 (file)
@@ -122,6 +122,18 @@ int sh2_execute_interpreter(SH2 *sh2, int cycles)
                {
                        sh2->ppc = sh2->delay;
                        opcode = RW(sh2, sh2->delay);
+
+                       // TODO: more branch types
+                       if ((opcode >> 13) == 5) { // BRA/BSR
+                               sh2->r[15] -= 4;
+                               WL(sh2, sh2->r[15], sh2->sr);
+                               sh2->r[15] -= 4;
+                               WL(sh2, sh2->r[15], sh2->pc);
+                               sh2->pc = RL(sh2, sh2->vbr + 6 * 4);
+                               sh2->icount -= 5;
+                               opcode = 9; // NOP
+                       }
+
                        sh2->pc -= 2;
                }
                else