+ else if (opcode[i]==0x2E) { // SWR
+ // Write entire word
+ emit_writeword_indexed(tl,-3,temp);
+ }
+ set_jump_target(done0, out);
+ set_jump_target(done1, out);
+ set_jump_target(done2, out);
+ if(!c||!memtarget)
+ add_stub_r(STORELR_STUB,jaddr,out,i,temp,i_regs,ccadj[i],reglist);
+ if(!(i_regs->waswritten&(1<<rs1[i])) && !HACK_ENABLED(NDHACK_NO_SMC_CHECK)) {
+ emit_addimm_no_flags(-ram_offset,temp);
+ #if defined(HOST_IMM8)
+ int ir=get_reg(i_regs->regmap,INVCP);
+ assert(ir>=0);
+ emit_cmpmem_indexedsr12_reg(ir,temp,1);
+ #else
+ emit_cmpmem_indexedsr12_imm(invalid_code,temp,1);
+ #endif
+ #if defined(HAVE_CONDITIONAL_CALL) && !defined(DESTRUCTIVE_SHIFT)
+ emit_callne(invalidate_addr_reg[temp]);
+ #else
+ void *jaddr2 = out;
+ emit_jne(0);
+ add_stub(INVCODE_STUB,jaddr2,out,reglist|(1<<HOST_CCREG),temp,0,0,0);
+ #endif
+ }
+}
+
+static void cop0_assemble(int i,struct regstat *i_regs)
+{
+ if(opcode2[i]==0) // MFC0
+ {
+ signed char t=get_reg(i_regs->regmap,rt1[i]);
+ u_int copr=(source[i]>>11)&0x1f;
+ //assert(t>=0); // Why does this happen? OOT is weird
+ if(t>=0&&rt1[i]!=0) {
+ emit_readword(®_cop0[copr],t);
+ }
+ }
+ else if(opcode2[i]==4) // MTC0
+ {
+ signed char s=get_reg(i_regs->regmap,rs1[i]);
+ char copr=(source[i]>>11)&0x1f;
+ assert(s>=0);
+ wb_register(rs1[i],i_regs->regmap,i_regs->dirty);
+ if(copr==9||copr==11||copr==12||copr==13) {
+ emit_readword(&last_count,HOST_TEMPREG);
+ emit_loadreg(CCREG,HOST_CCREG); // TODO: do proper reg alloc
+ emit_add(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+ emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
+ emit_writeword(HOST_CCREG,&Count);
+ }
+ // What a mess. The status register (12) can enable interrupts,
+ // so needs a special case to handle a pending interrupt.
+ // The interrupt must be taken immediately, because a subsequent
+ // instruction might disable interrupts again.
+ if(copr==12||copr==13) {
+ if (is_delayslot) {
+ // burn cycles to cause cc_interrupt, which will
+ // reschedule next_interupt. Relies on CCREG from above.
+ assem_debug("MTC0 DS %d\n", copr);
+ emit_writeword(HOST_CCREG,&last_count);
+ emit_movimm(0,HOST_CCREG);
+ emit_storereg(CCREG,HOST_CCREG);
+ emit_loadreg(rs1[i],1);
+ emit_movimm(copr,0);
+ emit_far_call(pcsx_mtc0_ds);
+ emit_loadreg(rs1[i],s);
+ return;
+ }
+ emit_movimm(start+i*4+4,HOST_TEMPREG);
+ emit_writeword(HOST_TEMPREG,&pcaddr);
+ emit_movimm(0,HOST_TEMPREG);
+ emit_writeword(HOST_TEMPREG,&pending_exception);
+ }
+ if(s==HOST_CCREG)
+ emit_loadreg(rs1[i],1);
+ else if(s!=1)
+ emit_mov(s,1);
+ emit_movimm(copr,0);
+ emit_far_call(pcsx_mtc0);
+ if(copr==9||copr==11||copr==12||copr==13) {
+ emit_readword(&Count,HOST_CCREG);
+ emit_readword(&next_interupt,HOST_TEMPREG);
+ emit_addimm(HOST_CCREG,-CLOCK_ADJUST(ccadj[i]),HOST_CCREG);
+ emit_sub(HOST_CCREG,HOST_TEMPREG,HOST_CCREG);
+ emit_writeword(HOST_TEMPREG,&last_count);
+ emit_storereg(CCREG,HOST_CCREG);
+ }
+ if(copr==12||copr==13) {
+ assert(!is_delayslot);
+ emit_readword(&pending_exception,14);
+ emit_test(14,14);
+ void *jaddr = out;
+ emit_jeq(0);
+ emit_readword(&pcaddr, 0);
+ emit_addimm(HOST_CCREG,2,HOST_CCREG);
+ emit_far_call(get_addr_ht);
+ emit_jmpreg(0);
+ set_jump_target(jaddr, out);
+ }
+ emit_loadreg(rs1[i],s);
+ }
+ else
+ {
+ assert(opcode2[i]==0x10);
+ //if((source[i]&0x3f)==0x10) // RFE
+ {
+ emit_readword(&Status,0);
+ emit_andimm(0,0x3c,1);
+ emit_andimm(0,~0xf,0);
+ emit_orrshr_imm(1,2,0);
+ emit_writeword(0,&Status);
+ }
+ }
+}
+
+static void cop1_unusable(int i,struct regstat *i_regs)
+{
+ // XXX: should just just do the exception instead
+ //if(!cop1_usable)
+ {
+ void *jaddr=out;
+ emit_jmp(0);
+ add_stub_r(FP_STUB,jaddr,out,i,0,i_regs,is_delayslot,0);
+ }
+}
+
+static void cop1_assemble(int i,struct regstat *i_regs)
+{
+ cop1_unusable(i, i_regs);
+}
+
+static void c1ls_assemble(int i,struct regstat *i_regs)
+{
+ cop1_unusable(i, i_regs);
+}
+
+// FP_STUB
+static void do_cop1stub(int n)
+{
+ literal_pool(256);
+ assem_debug("do_cop1stub %x\n",start+stubs[n].a*4);
+ set_jump_target(stubs[n].addr, out);
+ int i=stubs[n].a;
+// int rs=stubs[n].b;
+ struct regstat *i_regs=(struct regstat *)stubs[n].c;
+ int ds=stubs[n].d;
+ if(!ds) {
+ load_all_consts(regs[i].regmap_entry,regs[i].wasdirty,i);
+ //if(i_regs!=®s[i]) printf("oops: regs[i]=%x i_regs=%x",(int)®s[i],(int)i_regs);
+ }
+ //else {printf("fp exception in delay slot\n");}
+ wb_dirtys(i_regs->regmap_entry,i_regs->wasdirty);
+ if(regs[i].regmap_entry[HOST_CCREG]!=CCREG) emit_loadreg(CCREG,HOST_CCREG);
+ emit_movimm(start+(i-ds)*4,EAX); // Get PC
+ emit_addimm(HOST_CCREG,CLOCK_ADJUST(ccadj[i]),HOST_CCREG); // CHECK: is this right? There should probably be an extra cycle...
+ emit_far_jump(ds?fp_exception_ds:fp_exception);
+}
+
+static int cop2_is_stalling_op(int i, int *cycles)
+{
+ if (opcode[i] == 0x3a) { // SWC2
+ *cycles = 0;
+ return 1;
+ }
+ if (itype[i] == COP2 && (opcode2[i] == 0 || opcode2[i] == 2)) { // MFC2/CFC2
+ *cycles = 0;
+ return 1;