struct opcode *op;
u32 cycles;
bool delay_slot;
+ bool load_delay;
u16 offset;
};
.state = state,
.cycles = inter->cycles,
.delay_slot = true,
- .block = NULL,
+ .load_delay = true,
};
bool run_first_op = false, dummy_ld = false, save_rs = false,
load_in_ds, branch_in_ds = false, branch_at_addr = false,
branch_taken;
- u32 old_rs, new_rt, new_rs = 0;
- u32 next_pc, ds_next_pc;
- u32 cause, epc;
+ u32 new_rt, old_rs = 0, new_rs = 0;
+ u32 next_pc, ds_next_pc, epc;
if (op->i.op == OP_CP0 && op->r.rs == OP_CP0_RFE) {
/* When an IRQ happens, the PSX exception handlers (when done)
* but on branch boundaries, we need to adjust the return
* address so that the GTE opcode is effectively executed.
*/
- cause = state->regs.cp0[13];
epc = state->regs.cp0[14];
- if (!(cause & 0x7c) && epc == pc - 4)
- pc -= 4;
+ if (epc == pc - 4) {
+ op_next = lightrec_read_opcode(state, epc);
+ if (op_next.i.op == OP_CP2)
+ pc -= 4;
+ }
}
if (inter->delay_slot) {
* interpreter in that case.
* Same goes for when we have a branch in a delay slot of another
* branch. */
- load_in_ds = opcode_is_load(op->c) || opcode_is_mfc(op->c);
+ load_in_ds = opcode_has_load_delay(op->c);
branch_in_ds = has_delay_slot(op->c);
if (branch) {
u32 *reg_cache = inter->state->regs.gpr;
u32 val, *flags = NULL;
- if (inter->block)
+ if (!inter->load_delay && inter->block)
flags = &inter->op->flags;
val = lightrec_rw(inter->state, inter->op->c,
}
if (!op_flag_no_hi(inter->op->flags)) {
- if (c.r.op >= 32)
+ if (c.r.op >= 32) {
reg_cache[reg_hi] = rs << (c.r.op - 32);
- else if (c.i.op == OP_META_MULT2)
- reg_cache[reg_hi] = (s32) rs >> (32 - c.r.op);
- else
- reg_cache[reg_hi] = rs >> (32 - c.r.op);
+ }
+ else if (c.i.op == OP_META_MULT2) {
+ if (c.r.op)
+ reg_cache[reg_hi] = (s32) rs >> (32 - c.r.op);
+ else
+ reg_cache[reg_hi] = (s32) rs >> 31;
+ } else {
+ if (c.r.op)
+ reg_cache[reg_hi] = rs >> (32 - c.r.op);
+ else
+ reg_cache[reg_hi] = 0;
+ }
}
return jump_next(inter);
[OP_META] = int_META,
[OP_META_MULT2] = int_META_MULT2,
[OP_META_MULTU2] = int_META_MULT2,
+ [OP_META_LWU] = int_load,
+ [OP_META_SWU] = int_store,
};
static const lightrec_int_func_t int_special[64] = {
static u32 lightrec_emulate_block_list(struct lightrec_state *state,
struct block *block, u32 offset)
{
- struct interpreter inter;
+ struct interpreter inter = {
+ .block = block,
+ .state = state,
+ .offset = offset,
+ .op = &block->opcode_list[offset],
+ };
u32 pc;
- inter.block = block;
- inter.state = state;
- inter.offset = offset;
- inter.op = &block->opcode_list[offset];
- inter.cycles = 0;
- inter.delay_slot = false;
-
pc = lightrec_int_op(&inter);
/* Add the cycles of the last branch */
if (offset < block->nb_ops)
return lightrec_emulate_block_list(state, block, offset);
- pr_err("PC 0x%x is outside block at PC 0x%x\n", pc, block->pc);
+ pr_err(PC_FMT" is outside block at "PC_FMT"\n", pc, block->pc);
lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
struct interpreter inter = {
.block = block,
.state = state,
- .offset = 0,
.op = op,
- .cycles = 0,
+ .load_delay = true,
};
bool branch_taken;
u32 reg_mask, next_pc;