Handle DIV overflow in lightrec and the interpreter
[pcsx_rearmed.git] / deps / lightrec / interpreter.c
index acc41ea..ff609a4 100644 (file)
@@ -150,8 +150,8 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                 * but on branch boundaries, we need to adjust the return
                 * address so that the GTE opcode is effectively executed.
                 */
-               cause = (*state->ops.cop0_ops.cfc)(state, 13);
-               epc = (*state->ops.cop0_ops.cfc)(state, 14);
+               cause = (*state->ops.cop0_ops.cfc)(state, op->c.opcode, 13);
+               epc = (*state->ops.cop0_ops.cfc)(state, op->c.opcode, 14);
 
                if (!(cause & 0x7c) && epc == pc - 4)
                        pc -= 4;
@@ -218,6 +218,8 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                        branch_taken = is_branch_taken(reg_cache, op_next);
                        pr_debug("Target of impossible branch is a branch, "
                                 "%staken.\n", branch_taken ? "" : "not ");
+                       inter->cycles += lightrec_cycles_of_opcode(op_next);
+                       old_rs = reg_cache[op_next.r.rs];
                } else {
                        new_op.c = op_next;
                        new_op.flags = 0;
@@ -248,16 +250,24 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                new_rt = reg_cache[op->r.rt];
 
        /* Execute delay slot opcode */
-       if (branch_at_addr)
-               ds_next_pc = int_branch(&inter2, pc, op_next, branch_taken);
-       else
-               ds_next_pc = (*int_standard[inter2.op->i.op])(&inter2);
+       ds_next_pc = (*int_standard[inter2.op->i.op])(&inter2);
+
+       if (branch_at_addr) {
+               if (op_next.i.op == OP_SPECIAL)
+                       /* TODO: Handle JALR setting $ra */
+                       ds_next_pc = old_rs;
+               else if (op_next.i.op == OP_J || op_next.i.op == OP_JAL)
+                       /* TODO: Handle JAL setting $ra */
+                       ds_next_pc = (pc & 0xf0000000) | (op_next.j.imm << 2);
+               else
+                       ds_next_pc = pc + 4 + ((s16)op_next.i.imm << 2);
+       }
 
        if (branch_at_addr && !branch_taken) {
                /* If the branch at the target of the branch opcode is not
                 * taken, we jump to its delay slot */
                next_pc = pc + sizeof(u32);
-       } else if (!branch && branch_in_ds) {
+       } else if (branch_at_addr || (!branch && branch_in_ds)) {
                next_pc = ds_next_pc;
        }
 
@@ -475,7 +485,8 @@ static u32 int_ctc(struct interpreter *inter)
        /* If we have a MTC0 or CTC0 to CP0 register 12 (Status) or 13 (Cause),
         * return early so that the emulator will be able to check software
         * interrupt status. */
-       if (op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
+       if (!(inter->op->flags & LIGHTREC_NO_DS) &&
+           op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
                return inter->block->pc + (op->offset + 1) * sizeof(u32);
        else
                return jump_next(inter);
@@ -487,13 +498,13 @@ static u32 int_cp0_RFE(struct interpreter *inter)
        u32 status;
 
        /* Read CP0 Status register (r12) */
-       status = state->ops.cop0_ops.mfc(state, 12);
+       status = state->ops.cop0_ops.mfc(state, inter->op->c.opcode, 12);
 
        /* Switch the bits */
        status = ((status & 0x3c) >> 2) | (status & ~0xf);
 
        /* Write it back */
-       state->ops.cop0_ops.ctc(state, 12, status);
+       state->ops.cop0_ops.ctc(state, inter->op->c.opcode, 12, status);
 
        return jump_next(inter);
 }
@@ -790,6 +801,9 @@ static u32 int_special_DIV(struct interpreter *inter)
        if (rt == 0) {
                hi = rs;
                lo = (rs < 0) * 2 - 1;
+       } else if ((rs == 0x80000000) && (rt == 0xFFFFFFFF)) {
+               lo = rs;
+               hi = 0;
        } else {
                lo = rs / rt;
                hi = rs % rt;