+#endif
+ }
+}
+
+static void cop2_get_dreg(u_int copr,signed char tl,signed char temp)
+{
+ switch (copr) {
+ case 1:
+ case 3:
+ case 5:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ emit_readword((int)®_cop2d[copr],tl);
+ emit_signextend16(tl,tl);
+ emit_writeword(tl,(int)®_cop2d[copr]); // hmh
+ break;
+ case 7:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ emit_readword((int)®_cop2d[copr],tl);
+ emit_andimm(tl,0xffff,tl);
+ emit_writeword(tl,(int)®_cop2d[copr]);
+ break;
+ case 15:
+ emit_readword((int)®_cop2d[14],tl); // SXY2
+ emit_writeword(tl,(int)®_cop2d[copr]);
+ break;
+ case 28:
+ case 29:
+ emit_readword((int)®_cop2d[9],temp);
+ emit_testimm(temp,0x8000); // do we need this?
+ emit_andimm(temp,0xf80,temp);
+ emit_andne_imm(temp,0,temp);
+ emit_shrimm(temp,7,tl);
+ emit_readword((int)®_cop2d[10],temp);
+ emit_testimm(temp,0x8000);
+ emit_andimm(temp,0xf80,temp);
+ emit_andne_imm(temp,0,temp);
+ emit_orrshr_imm(temp,2,tl);
+ emit_readword((int)®_cop2d[11],temp);
+ emit_testimm(temp,0x8000);
+ emit_andimm(temp,0xf80,temp);
+ emit_andne_imm(temp,0,temp);
+ emit_orrshl_imm(temp,3,tl);
+ emit_writeword(tl,(int)®_cop2d[copr]);
+ break;
+ default:
+ emit_readword((int)®_cop2d[copr],tl);
+ break;
+ }
+}
+
+static void cop2_put_dreg(u_int copr,signed char sl,signed char temp)
+{
+ switch (copr) {
+ case 15:
+ emit_readword((int)®_cop2d[13],temp); // SXY1
+ emit_writeword(sl,(int)®_cop2d[copr]);
+ emit_writeword(temp,(int)®_cop2d[12]); // SXY0
+ emit_readword((int)®_cop2d[14],temp); // SXY2
+ emit_writeword(sl,(int)®_cop2d[14]);
+ emit_writeword(temp,(int)®_cop2d[13]); // SXY1
+ break;
+ case 28:
+ emit_andimm(sl,0x001f,temp);
+ emit_shlimm(temp,7,temp);
+ emit_writeword(temp,(int)®_cop2d[9]);
+ emit_andimm(sl,0x03e0,temp);
+ emit_shlimm(temp,2,temp);
+ emit_writeword(temp,(int)®_cop2d[10]);
+ emit_andimm(sl,0x7c00,temp);
+ emit_shrimm(temp,3,temp);
+ emit_writeword(temp,(int)®_cop2d[11]);
+ emit_writeword(sl,(int)®_cop2d[28]);
+ break;
+ case 30:
+ emit_movs(sl,temp);
+ emit_mvnmi(temp,temp);
+#ifdef HAVE_ARMV5
+ emit_clz(temp,temp);
+#else
+ emit_movs(temp,HOST_TEMPREG);
+ emit_movimm(0,temp);
+ emit_jeq((int)out+4*4);
+ emit_addpl_imm(temp,1,temp);
+ emit_lslpls_imm(HOST_TEMPREG,1,HOST_TEMPREG);
+ emit_jns((int)out-2*4);
+#endif
+ emit_writeword(sl,(int)®_cop2d[30]);
+ emit_writeword(temp,(int)®_cop2d[31]);
+ break;
+ case 31:
+ break;
+ default:
+ emit_writeword(sl,(int)®_cop2d[copr]);
+ break;
+ }
+}
+
+void cop2_assemble(int i,struct regstat *i_regs)
+{
+ u_int copr=(source[i]>>11)&0x1f;
+ signed char temp=get_reg(i_regs->regmap,-1);
+ if (opcode2[i]==0) { // MFC2
+ signed char tl=get_reg(i_regs->regmap,rt1[i]);
+ if(tl>=0&&rt1[i]!=0)
+ cop2_get_dreg(copr,tl,temp);
+ }
+ else if (opcode2[i]==4) { // MTC2
+ signed char sl=get_reg(i_regs->regmap,rs1[i]);
+ cop2_put_dreg(copr,sl,temp);
+ }
+ else if (opcode2[i]==2) // CFC2
+ {
+ signed char tl=get_reg(i_regs->regmap,rt1[i]);
+ if(tl>=0&&rt1[i]!=0)
+ emit_readword((int)®_cop2c[copr],tl);
+ }
+ else if (opcode2[i]==6) // CTC2
+ {
+ signed char sl=get_reg(i_regs->regmap,rs1[i]);
+ switch(copr) {
+ case 4:
+ case 12:
+ case 20:
+ case 26:
+ case 27:
+ case 29:
+ case 30:
+ emit_signextend16(sl,temp);
+ break;
+ case 31:
+ //value = value & 0x7ffff000;
+ //if (value & 0x7f87e000) value |= 0x80000000;
+ emit_shrimm(sl,12,temp);
+ emit_shlimm(temp,12,temp);
+ emit_testimm(temp,0x7f000000);
+ emit_testeqimm(temp,0x00870000);
+ emit_testeqimm(temp,0x0000e000);
+ emit_orrne_imm(temp,0x80000000,temp);
+ break;
+ default:
+ temp=sl;
+ break;
+ }
+ emit_writeword(temp,(int)®_cop2c[copr]);
+ assert(sl>=0);
+ }
+}
+
+static void c2op_prologue(u_int op,u_int reglist)
+{
+ save_regs_all(reglist);
+#ifdef PCNT
+ emit_movimm(op,0);
+ emit_call((int)pcnt_gte_start);
+#endif
+ emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0); // cop2 regs
+}
+
+static void c2op_epilogue(u_int op,u_int reglist)
+{
+#ifdef PCNT
+ emit_movimm(op,0);
+ emit_call((int)pcnt_gte_end);
+#endif
+ restore_regs_all(reglist);
+}
+
+static void c2op_call_MACtoIR(int lm,int need_flags)
+{
+ if(need_flags)
+ emit_call((int)(lm?gteMACtoIR_lm1:gteMACtoIR_lm0));
+ else
+ emit_call((int)(lm?gteMACtoIR_lm1_nf:gteMACtoIR_lm0_nf));
+}
+
+static void c2op_call_rgb_func(void *func,int lm,int need_ir,int need_flags)
+{
+ emit_call((int)func);
+ // func is C code and trashes r0
+ emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+ if(need_flags||need_ir)
+ c2op_call_MACtoIR(lm,need_flags);
+ emit_call((int)(need_flags?gteMACtoRGB:gteMACtoRGB_nf));
+}
+
+static void c2op_assemble(int i,struct regstat *i_regs)
+{
+ signed char temp=get_reg(i_regs->regmap,-1);
+ u_int c2op=source[i]&0x3f;
+ u_int hr,reglist_full=0,reglist;
+ int need_flags,need_ir;
+ for(hr=0;hr<HOST_REGS;hr++) {
+ if(i_regs->regmap[hr]>=0) reglist_full|=1<<hr;
+ }
+ reglist=reglist_full&0x100f;
+
+ if (gte_handlers[c2op]!=NULL) {
+ need_flags=!(gte_unneeded[i+1]>>63); // +1 because of how liveness detection works
+ need_ir=(gte_unneeded[i+1]&0xe00)!=0xe00;
+ assem_debug("gte op %08x, unneeded %016llx, need_flags %d, need_ir %d\n",
+ source[i],gte_unneeded[i+1],need_flags,need_ir);
+ if(new_dynarec_hacks&NDHACK_GTE_NO_FLAGS)
+ need_flags=0;
+ int shift = (source[i] >> 19) & 1;
+ int lm = (source[i] >> 10) & 1;
+ switch(c2op) {
+#ifndef DRC_DBG
+ case GTE_MVMVA: {
+#ifdef HAVE_ARMV5
+ int v = (source[i] >> 15) & 3;
+ int cv = (source[i] >> 13) & 3;
+ int mx = (source[i] >> 17) & 3;
+ reglist=reglist_full&0x10ff; // +{r4-r7}
+ c2op_prologue(c2op,reglist);
+ /* r4,r5 = VXYZ(v) packed; r6 = &MX11(mx); r7 = &CV1(cv) */
+ if(v<3)
+ emit_ldrd(v*8,0,4);
+ else {
+ emit_movzwl_indexed(9*4,0,4); // gteIR
+ emit_movzwl_indexed(10*4,0,6);
+ emit_movzwl_indexed(11*4,0,5);
+ emit_orrshl_imm(6,16,4);
+ }
+ if(mx<3)
+ emit_addimm(0,32*4+mx*8*4,6);
+ else
+ emit_readword((int)&zeromem_ptr,6);
+ if(cv<3)
+ emit_addimm(0,32*4+(cv*8+5)*4,7);
+ else
+ emit_readword((int)&zeromem_ptr,7);
+#ifdef __ARM_NEON__
+ emit_movimm(source[i],1); // opcode
+ emit_call((int)gteMVMVA_part_neon);
+ if(need_flags) {
+ emit_movimm(lm,1);
+ emit_call((int)gteMACtoIR_flags_neon);
+ }
+#else
+ if(cv==3&&shift)
+ emit_call((int)gteMVMVA_part_cv3sh12_arm);
+ else {
+ emit_movimm(shift,1);
+ emit_call((int)(need_flags?gteMVMVA_part_arm:gteMVMVA_part_nf_arm));
+ }
+ if(need_flags||need_ir)
+ c2op_call_MACtoIR(lm,need_flags);
+#endif
+#else /* if not HAVE_ARMV5 */
+ c2op_prologue(c2op,reglist);
+ emit_movimm(source[i],1); // opcode
+ emit_writeword(1,(int)&psxRegs.code);
+ emit_call((int)(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]));
+#endif
+ break;
+ }
+ case GTE_OP:
+ c2op_prologue(c2op,reglist);
+ emit_call((int)(shift?gteOP_part_shift:gteOP_part_noshift));
+ if(need_flags||need_ir) {
+ emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+ c2op_call_MACtoIR(lm,need_flags);
+ }
+ break;
+ case GTE_DPCS:
+ c2op_prologue(c2op,reglist);
+ c2op_call_rgb_func(shift?gteDPCS_part_shift:gteDPCS_part_noshift,lm,need_ir,need_flags);
+ break;
+ case GTE_INTPL:
+ c2op_prologue(c2op,reglist);
+ c2op_call_rgb_func(shift?gteINTPL_part_shift:gteINTPL_part_noshift,lm,need_ir,need_flags);
+ break;
+ case GTE_SQR:
+ c2op_prologue(c2op,reglist);
+ emit_call((int)(shift?gteSQR_part_shift:gteSQR_part_noshift));
+ if(need_flags||need_ir) {
+ emit_addimm(FP,(int)&psxRegs.CP2D.r[0]-(int)&dynarec_local,0);
+ c2op_call_MACtoIR(lm,need_flags);
+ }
+ break;
+ case GTE_DCPL:
+ c2op_prologue(c2op,reglist);
+ c2op_call_rgb_func(gteDCPL_part,lm,need_ir,need_flags);
+ break;
+ case GTE_GPF:
+ c2op_prologue(c2op,reglist);
+ c2op_call_rgb_func(shift?gteGPF_part_shift:gteGPF_part_noshift,lm,need_ir,need_flags);
+ break;
+ case GTE_GPL:
+ c2op_prologue(c2op,reglist);
+ c2op_call_rgb_func(shift?gteGPL_part_shift:gteGPL_part_noshift,lm,need_ir,need_flags);
+ break;
+#endif
+ default:
+ c2op_prologue(c2op,reglist);
+#ifdef DRC_DBG
+ emit_movimm(source[i],1); // opcode
+ emit_writeword(1,(int)&psxRegs.code);
+#endif
+ emit_call((int)(need_flags?gte_handlers[c2op]:gte_handlers_nf[c2op]));
+ break;
+ }
+ c2op_epilogue(c2op,reglist);