ptitSeb
Serial Porter
Let's discuss about reicast dynarec.
I'll focus here on individual implementations of opcode. Most opcode are individual SH4 opcode, but there are a few higher level opcode too.
So, there are emiter and opcode.
Emiter syntax is quite simple, and is generaly:
XXX(op1, op2, op3, UpdateFlags, ShiftOp, op4, Condition).
But most of the latest (Shift, Update, etc...) can be optionnal. So we can have
MOV(r0, r1);
SUB(r0, r1, r2, true, S_LSR, 1, CC_MI);
I can add emiter when some are missing. Emiter are in core/arm_emitter/E_*.h
In the opcode, r0-r4 are scratch register, free to used
rd is the register that will return 32bit value
rd2 is the upper part for 64bit register return
r1, rs2 and rs3 are the parameters registers.
Also, be aware that rd, rd2, rs1, rs2 and rs3 are not always distinct.
Here is setpeq
case shop_setpeq:
{
EOR(r1, reg.mapg(op->rs1), reg.mapg(op->rs2));
MOVW(reg.mapg(op->rd), 0);
TST(r1, 0xFF000000);
TST(r1, 0x00FF0000, CC_NE);
TST(r1, 0x0000FF00, CC_NE);
TST(r1, 0x000000FF, CC_NE);
MOVW(reg.mapg(op->rd), 1, CC_EQ);
}
break;
And here is sbc
case shop_sbc:
//printf("sbc: r%d r%d r%d r%d r%d\n",reg.mapg(op->rd),reg.mapg(op->rd2),reg.mapg(op->rs1),reg.mapg(op->rs2), reg.mapg(op->rs3));
{
bool use_r0 = false;
if (reg.mapg(op->rd2)==reg.mapg(op->rs1) || reg.mapg(op->rd2)==reg.mapg(op->rs2))
use_r0 = true;
EOR((use_r0)?r0:reg.mapg(op->rd2),reg.mapg(op->rs3),1)
LSR((use_r0)?r0:reg.mapg(op->rd2),(use_r0)?r0:reg.mapg(op->rd2),1,true); //C=rs3, rd2=0
SBC(reg.mapg(op->rd), reg.mapg(op->rs1), reg.mapg(op->rs2), true);
MOV((use_r0)?r0:reg.mapg(op->rd2), 1, CC_CC);
if (use_r0)
MOV(reg.mapg(op->rd2), r0);
}
break;
the new shld
case shop_shld:
//printf("shld: r%d r%d r%d\n",reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
{
verify(!op->rs2.is_imm());
AND(r0, reg.mapg(op->rs2), 0x8000001F, true);
RSB(r0, r0, 0x80000020, CC_MI);
LSR(reg.mapg(op->rd), reg.mapg(op->rs1), r0, CC_MI);
LSL(reg.mapg(op->rd), reg.mapg(op->rs1), r0, CC_PL);
//MOV(reg.mapg(op->rd), reg.mapg(op->rs1), S_LSL, r0, CC_PL);
//MOV(reg.mapg(op->rd), reg.mapg(op->rs1), S_LSR, r0, CC_MI);
}
break;
And the Div (that are HLE functions)
case shop_div32u:
// Doesn't work
// algo from new arm dynarec from mupen64plus
//printf("div32u: r%d r%d r%d r%d\n",reg.mapg(op->rd2),reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
{
// remainder = r0, quotient = r1, HOST_TEMPREG = r2, copy de rs1 = r3, copy de rs2 = r4
MOV(r3, reg.mapg(op->rs1));
MOV(r4, reg.mapg(op->rs2), true);
MOV(r0, reg.mapg(op->rs1)); // dividend = d1 , divisor = d2
MVN(r1, 0);
B(10*4-8, CC_EQ);
CLZ(r2, r4);
MOV(r1, 1<1);
LSL(r4, r4, r2);
LSR(r1, r1, r2);
CMP(r0, r4);
SUB(r0, r0, r4, CC_CS);
ADC(r1, r1, r1, true);
MOV(r4, r4, S_LSR, 1, CC_CC);
B(-4*4-8, CC_CC);
MOV(reg.mapg(op->rd), r1);
MOV(reg.mapg(op->rd2), r0);
}
break;
case shop_div32s:
//printf("div32s r%d, r%d, r%d, r%d\n", reg.mapg(op->rd2),reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
// algo from dynarec from pcsxrearmed
// remainder = r0, quotient = r1, HOST_TEMPREG = r2, copy de rs1 = r3, copy de rs2 = r4
{
MOV(r3, reg.mapg(op->rs1));
MOV(r4, reg.mapg(op->rs2));
MOV(r0, reg.mapg(op->rs1), true);
MVN(r1, 0);
RSB(r1, r1, 0, CC_MI); // .. quotient and ..
RSB(r0, r0, 0, CC_MI); // .. remainder for div0 case (will be negated back after jump)
MOV(r2, reg.mapg(op->rs2), true);
B(13*4-8, CC_EQ); // Division by zero
RSB(r2, r2, 0, true, CC_MI);
CLZ(r1, r2);
LSL(r2, r2, r1);
ORR(r1, r1, 1<1);
LSR(r1, r1, r1);
CMP(r0, r2);
SUB(r0, r0, r2, CC_CS);
ADC(r1, r1, r1, true);
MOV(r2, r2, S_LSR, 1);
B(-4*4-8, CC_CC); // -4
TEQ(r3, r4, S_LSL, CC_AL);
RSB(reg.mapg(op->rd), r1, 0, CC_MI);
MOV(reg.mapg(op->rd), r1, CC_PL);
TST(r3, r3);
RSB(reg.mapg(op->rd2), r0, 0, CC_MI);
MOV(reg.mapg(op->rd2), r0, CC_PL);
}
break;
Note that the div32s (at least) is not correct. I have weird car behavour on Daytona with this one activated. *EDIT* fixed, the UpdateFlags should be in third line, not second. Updated the code snipet also. *EDIT2* No, it's not fixed.
I'll focus here on individual implementations of opcode. Most opcode are individual SH4 opcode, but there are a few higher level opcode too.
So, there are emiter and opcode.
Emiter syntax is quite simple, and is generaly:
XXX(op1, op2, op3, UpdateFlags, ShiftOp, op4, Condition).
But most of the latest (Shift, Update, etc...) can be optionnal. So we can have
MOV(r0, r1);
SUB(r0, r1, r2, true, S_LSR, 1, CC_MI);
I can add emiter when some are missing. Emiter are in core/arm_emitter/E_*.h
In the opcode, r0-r4 are scratch register, free to used
rd is the register that will return 32bit value
rd2 is the upper part for 64bit register return
r1, rs2 and rs3 are the parameters registers.
Also, be aware that rd, rd2, rs1, rs2 and rs3 are not always distinct.
Here is setpeq
case shop_setpeq:
{
EOR(r1, reg.mapg(op->rs1), reg.mapg(op->rs2));
MOVW(reg.mapg(op->rd), 0);
TST(r1, 0xFF000000);
TST(r1, 0x00FF0000, CC_NE);
TST(r1, 0x0000FF00, CC_NE);
TST(r1, 0x000000FF, CC_NE);
MOVW(reg.mapg(op->rd), 1, CC_EQ);
}
break;
And here is sbc
case shop_sbc:
//printf("sbc: r%d r%d r%d r%d r%d\n",reg.mapg(op->rd),reg.mapg(op->rd2),reg.mapg(op->rs1),reg.mapg(op->rs2), reg.mapg(op->rs3));
{
bool use_r0 = false;
if (reg.mapg(op->rd2)==reg.mapg(op->rs1) || reg.mapg(op->rd2)==reg.mapg(op->rs2))
use_r0 = true;
EOR((use_r0)?r0:reg.mapg(op->rd2),reg.mapg(op->rs3),1)
LSR((use_r0)?r0:reg.mapg(op->rd2),(use_r0)?r0:reg.mapg(op->rd2),1,true); //C=rs3, rd2=0
SBC(reg.mapg(op->rd), reg.mapg(op->rs1), reg.mapg(op->rs2), true);
MOV((use_r0)?r0:reg.mapg(op->rd2), 1, CC_CC);
if (use_r0)
MOV(reg.mapg(op->rd2), r0);
}
break;
the new shld
case shop_shld:
//printf("shld: r%d r%d r%d\n",reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
{
verify(!op->rs2.is_imm());
AND(r0, reg.mapg(op->rs2), 0x8000001F, true);
RSB(r0, r0, 0x80000020, CC_MI);
LSR(reg.mapg(op->rd), reg.mapg(op->rs1), r0, CC_MI);
LSL(reg.mapg(op->rd), reg.mapg(op->rs1), r0, CC_PL);
//MOV(reg.mapg(op->rd), reg.mapg(op->rs1), S_LSL, r0, CC_PL);
//MOV(reg.mapg(op->rd), reg.mapg(op->rs1), S_LSR, r0, CC_MI);
}
break;
And the Div (that are HLE functions)
case shop_div32u:
// Doesn't work
// algo from new arm dynarec from mupen64plus
//printf("div32u: r%d r%d r%d r%d\n",reg.mapg(op->rd2),reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
{
// remainder = r0, quotient = r1, HOST_TEMPREG = r2, copy de rs1 = r3, copy de rs2 = r4
MOV(r3, reg.mapg(op->rs1));
MOV(r4, reg.mapg(op->rs2), true);
MOV(r0, reg.mapg(op->rs1)); // dividend = d1 , divisor = d2
MVN(r1, 0);
B(10*4-8, CC_EQ);
CLZ(r2, r4);
MOV(r1, 1<1);
LSL(r4, r4, r2);
LSR(r1, r1, r2);
CMP(r0, r4);
SUB(r0, r0, r4, CC_CS);
ADC(r1, r1, r1, true);
MOV(r4, r4, S_LSR, 1, CC_CC);
B(-4*4-8, CC_CC);
MOV(reg.mapg(op->rd), r1);
MOV(reg.mapg(op->rd2), r0);
}
break;
case shop_div32s:
//printf("div32s r%d, r%d, r%d, r%d\n", reg.mapg(op->rd2),reg.mapg(op->rd),reg.mapg(op->rs1),reg.mapg(op->rs2));
// algo from dynarec from pcsxrearmed
// remainder = r0, quotient = r1, HOST_TEMPREG = r2, copy de rs1 = r3, copy de rs2 = r4
{
MOV(r3, reg.mapg(op->rs1));
MOV(r4, reg.mapg(op->rs2));
MOV(r0, reg.mapg(op->rs1), true);
MVN(r1, 0);
RSB(r1, r1, 0, CC_MI); // .. quotient and ..
RSB(r0, r0, 0, CC_MI); // .. remainder for div0 case (will be negated back after jump)
MOV(r2, reg.mapg(op->rs2), true);
B(13*4-8, CC_EQ); // Division by zero
RSB(r2, r2, 0, true, CC_MI);
CLZ(r1, r2);
LSL(r2, r2, r1);
ORR(r1, r1, 1<1);
LSR(r1, r1, r1);
CMP(r0, r2);
SUB(r0, r0, r2, CC_CS);
ADC(r1, r1, r1, true);
MOV(r2, r2, S_LSR, 1);
B(-4*4-8, CC_CC); // -4
TEQ(r3, r4, S_LSL, CC_AL);
RSB(reg.mapg(op->rd), r1, 0, CC_MI);
MOV(reg.mapg(op->rd), r1, CC_PL);
TST(r3, r3);
RSB(reg.mapg(op->rd2), r0, 0, CC_MI);
MOV(reg.mapg(op->rd2), r0, CC_PL);
}
break;
Last edited by a moderator: