struct opcode *op;
u32 cycles;
bool delay_slot;
+ bool load_delay;
u16 offset;
};
static inline u32 jump_next(struct interpreter *inter)
{
- inter->cycles += lightrec_cycles_of_opcode(inter->op->c);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, inter->op->c);
if (unlikely(inter->delay_slot))
return 0;
static inline u32 jump_after_branch(struct interpreter *inter)
{
- inter->cycles += lightrec_cycles_of_opcode(inter->op->c);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, inter->op->c);
if (unlikely(inter->delay_slot))
return 0;
u32 cycles;
if (!inter->delay_slot) {
- cycles = lightrec_cycles_of_opcode(inter->op->c);
+ cycles = lightrec_cycles_of_opcode(inter->state, inter->op->c);
if (!op_flag_no_ds(inter->op->flags) &&
has_delay_slot(inter->op->c))
- cycles += lightrec_cycles_of_opcode(next_op(inter)->c);
+ cycles += lightrec_cycles_of_opcode(inter->state, next_op(inter)->c);
inter->cycles += cycles;
inter->state->current_cycle += inter->cycles;
.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_rs, new_rt;
- 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) {
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);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, op_next);
old_rs = reg_cache[op_next.r.rs];
} else {
new_op.c = op_next;
reg_cache[op->r.rs] = old_rs;
}
- inter->cycles += lightrec_cycles_of_opcode(op_next);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, op_next);
}
} else {
next_pc = int_get_ds_pc(inter, 2);
if (dummy_ld)
reg_cache[op->r.rt] = new_rt;
- inter->cycles += lightrec_cycles_of_opcode(op->c);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, op->c);
if (branch_at_addr && branch_taken) {
/* If the branch at the target of the branch opcode is taken,
inter2.op = &new_op;
inter2.block = NULL;
- inter->cycles += lightrec_cycles_of_opcode(op_next);
+ inter->cycles += lightrec_cycles_of_opcode(inter->state, op_next);
pr_debug("Running delay slot of branch at target of impossible "
"branch\n");
static u32 int_unimplemented(struct interpreter *inter)
{
- pr_warn("Unimplemented opcode 0x%08x\n", inter->op->opcode);
+ lightrec_set_exit_flags(inter->state, LIGHTREC_EXIT_UNKNOWN_OP);
- return jump_next(inter);
+ return inter->block->pc + (inter->offset << 2);
}
static u32 int_jump(struct interpreter *inter, bool link)
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 */
- inter.cycles += lightrec_cycles_of_opcode(inter.op->c);
+ inter.cycles += lightrec_cycles_of_opcode(inter.state, inter.op->c);
state->current_cycle += inter.cycles;
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;