about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/dynarec/rv64/dynarec_rv64_00.c160
-rw-r--r--src/dynarec/rv64/dynarec_rv64_emit_tests.c35
-rw-r--r--src/dynarec/rv64/dynarec_rv64_helper.h63
-rw-r--r--src/dynarec/rv64/rv64_emitter.h23
4 files changed, 268 insertions, 13 deletions
diff --git a/src/dynarec/rv64/dynarec_rv64_00.c b/src/dynarec/rv64/dynarec_rv64_00.c
index b654d1a9..882f3e52 100644
--- a/src/dynarec/rv64/dynarec_rv64_00.c
+++ b/src/dynarec/rv64/dynarec_rv64_00.c
@@ -238,10 +238,10 @@ uintptr_t dynarec64_00(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni
                     SRLI(x3, gd, 32);
                     UFLAG_OP1(x3);
                     UFLAG_DF(x3, d_imul32);
-                    ZEROUP(gd);
                 } else {
                     MULxw(gd, ed, x4);
                 }
+                ZEROUP(gd);
             }
             break;
 
@@ -476,10 +476,11 @@ uintptr_t dynarec64_00(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni
         case 0x99:
             INST_NAME("CDQ");
             if(rex.w) {
-                SRLI(xRDX, xRAX, 63);
+                SRAI(xRDX, xRAX, 63);
             } else {
                 SLLI(xRDX, xRAX, 32);
-                SRLI(xRDX, xRDX, 63);
+                SRAI(xRDX, xRDX, 63);
+                ZEROUP(xRDX);
             }
             break;
 
@@ -885,6 +886,159 @@ uintptr_t dynarec64_00(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int ni
             *ok = 0;
             break;
 
+        case 0xF7:
+            nextop = F8;
+            switch((nextop>>3)&7) {
+                case 0:
+                case 1:
+                    INST_NAME("TEST Ed, Id");
+                    SETFLAGS(X_ALL, SF_SET_PENDING);
+                    GETEDH(x1, 4);
+                    i64 = F32S;
+                    emit_test32c(dyn, ninst, rex, ed, i64, x3, x4, x5);
+                    break;
+                case 2:
+                    INST_NAME("NOT Ed");
+                    GETED(0);
+                    XORI(ed, ed, -1);
+                    if(!rex.w && MODREG)
+                        ZEROUP(ed);
+                    WBACK;
+                    break;
+                /*case 3:
+                    INST_NAME("NEG Ed");
+                    SETFLAGS(X_ALL, SF_SET_PENDING);
+                    GETED(0);
+                    emit_neg32(dyn, ninst, rex, ed, x3, x4);
+                    WBACK;
+                    break;*/
+                case 4:
+                    INST_NAME("MUL EAX, Ed");
+                    SETFLAGS(X_ALL, SF_PENDING);
+                    UFLAG_DF(x2, rex.w?d_mul64:d_mul32);
+                    GETED(0);
+                    if(rex.w) {
+                        if(ed==xRDX) gd=x3; else gd=xRDX;
+                        MULHU(gd, xRAX, ed);
+                        MUL(xRAX, xRAX, ed);
+                        if(gd!=xRDX) {MV(xRDX, gd);}
+                    } else {
+                        MUL(xRDX, xRAX, ed);  //64 <- 32x32
+                        AND(xRAX, xRDX, xMASK);
+                        SRLI(xRDX, xRDX, 32);
+                    }
+                    UFLAG_RES(xRAX);
+                    UFLAG_OP1(xRDX);
+                    break;
+                case 5:
+                    INST_NAME("IMUL EAX, Ed");
+                    SETFLAGS(X_ALL, SF_PENDING);
+                    UFLAG_DF(x2, rex.w?d_imul64:d_imul32);
+                    GETSED(0);
+                    if(rex.w) {
+                        if(ed==xRDX) gd=x3; else gd=xRDX;
+                        MULH(gd, xRAX, ed);
+                        MUL(xRAX, xRAX, ed);
+                        if(gd!=xRDX) {MV(xRDX, gd);}
+                    } else {
+                        MUL(xRDX, xRAX, ed);  //64 <- 32x32
+                        AND(xRAX, xRDX, xMASK);
+                        SRLI(xRDX, xRDX, 32);
+                    }
+                    UFLAG_RES(xRAX);
+                    UFLAG_OP1(xRDX);
+                    break;
+                case 6:
+                    INST_NAME("DIV Ed");
+                    SETFLAGS(X_ALL, SF_SET);
+                    if(!rex.w) {
+                        SET_DFNONE(x2);
+                        GETED(0);
+                        SLLI(x3, xRDX, 32);
+                        AND(x2, xRAX, xMASK);
+                        OR(x3, x3, x2);
+                        if(MODREG) {
+                            MV(x4, ed);
+                            ed = x4;
+                        }
+                        DIVU(x2, x3, ed);
+                        REMU(xRDX, x3, ed);
+                        AND(xRAX, x2, xMASK);
+                        ZEROUP(xRDX);
+                    } else {
+                        if(ninst 
+                           && dyn->insts[ninst-1].x64.addr 
+                           && *(uint8_t*)(dyn->insts[ninst-1].x64.addr)==0x31 
+                           && *(uint8_t*)(dyn->insts[ninst-1].x64.addr+1)==0xD2) {
+                            SET_DFNONE(x2);
+                            GETED(0);
+                            DIVU(x2, xRAX, ed);
+                            REMU(xRDX, xRAX, ed);
+                            MV(xRAX, x2);
+                        } else {
+                            GETEDH(x1, 0);  // get edd changed addr, so cannot be called 2 times for same op...
+                            BEQ_MARK(xRDX, xZR);
+                            if(ed!=x1) {MV(x1, ed);}
+                            CALL(div64, -1);
+                            B_NEXT_nocond;
+                            MARK;
+                            DIVU(x2, xRAX, ed);
+                            REMU(xRDX, xRAX, ed);
+                            MV(xRAX, x2);
+                            SET_DFNONE(x2);
+                        }
+                    }
+                    break;
+                case 7:
+                    INST_NAME("IDIV Ed");
+                    SETFLAGS(X_ALL, SF_SET);
+                    if(!rex.w) {
+                        SET_DFNONE(x2)
+                        GETSED(0);
+                        SLLI(x3, xRDX, 32);
+                        AND(x2, xRAX, xMASK);
+                        OR(x3, x3, x2);
+                        DIV(x2, x3, ed);
+                        REM(xRDX, x3, ed);
+                        AND(xRAX, x2, xMASK);
+                        ZEROUP(xRDX);
+                    } else {
+                        if(ninst && dyn->insts
+                           &&  dyn->insts[ninst-1].x64.addr 
+                           && *(uint8_t*)(dyn->insts[ninst-1].x64.addr)==0x48
+                           && *(uint8_t*)(dyn->insts[ninst-1].x64.addr+1)==0x99) {
+                            SET_DFNONE(x2)
+                            GETED(0);
+                            DIV(x2, xRAX, ed);
+                            REM(xRDX, xRAX, ed);
+                            AND(xRAX, x2, xMASK);
+                        } else {
+                            GETEDH(x1, 0);  // get edd changed addr, so cannot be called 2 times for same op...
+                            //Need to see if RDX==0 and RAX not signed
+                            // or RDX==-1 and RAX signed
+                            BNE_MARK2(xRDX, xZR);
+                            BLT_MARK(xRAX, xZR);
+                            MARK2;
+                            NOT(x2, xRDX);
+                            BNE_MARK3(x2, xZR);
+                            BLT_MARK(xRAX, xZR);
+                            MARK3;
+                            if(ed!=x1) MV(x1, ed);
+                            CALL((void*)idiv64, -1);
+                            B_NEXT_nocond;
+                            MARK;
+                            DIV(x2, xRAX, ed);
+                            REM(xRDX, xRAX, ed);
+                            MV(xRAX, x2);
+                            SET_DFNONE(x2)
+                        }
+                    }
+                    break;
+                default:
+                    DEFAULT;
+            };
+            break;
+
         case 0xFF:
             nextop = F8;
             switch((nextop>>3)&7) {
diff --git a/src/dynarec/rv64/dynarec_rv64_emit_tests.c b/src/dynarec/rv64/dynarec_rv64_emit_tests.c
index 59f2b495..9ea95d20 100644
--- a/src/dynarec/rv64/dynarec_rv64_emit_tests.c
+++ b/src/dynarec/rv64/dynarec_rv64_emit_tests.c
@@ -186,3 +186,38 @@ void emit_test32(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int s2, int
         emit_pf(dyn, ninst, s3, s4, s5);
     }
 }
+
+// emit TEST32 instruction, from test s1, s2, using s3 and s4 as scratch
+void emit_test32c(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int64_t c, int s3, int s4, int s5)
+{
+    CLEAR_FLAGS();
+    IFX_PENDOR0 {
+        SET_DF(s3, rex.w?d_tst64:d_tst32);
+    } else {
+        SET_DFNONE(s4);
+    }
+
+    if(c>=-2048 && c<=2047)
+        ANDI(s3, s1, c);
+    else {
+        MOV64x(s3, c);
+        AND(s3, s1, s3); // res = s1 & s2
+    }
+
+    IFX_PENDOR0 {
+        SDxw(s3, xEmu, offsetof(x64emu_t, res));
+    }
+    IFX(X_SF) {
+        if (!rex.w) ZEROUP(s3);
+        SRLI(s4, s3, rex.w?63:31);
+        BEQZ(s4, 8);
+        ORI(xFlags, xFlags, 1 << F_SF);
+    }
+    IFX(X_ZF) {
+        BNEZ(s3, 8);
+        ORI(xFlags, xFlags, 1 << F_ZF);
+    }
+    IFX(X_PF) {
+        emit_pf(dyn, ninst, s3, s4, s5);
+    }
+}
diff --git a/src/dynarec/rv64/dynarec_rv64_helper.h b/src/dynarec/rv64/dynarec_rv64_helper.h
index 9ae48da5..f05dc57e 100644
--- a/src/dynarec/rv64/dynarec_rv64_helper.h
+++ b/src/dynarec/rv64/dynarec_rv64_helper.h
@@ -72,7 +72,23 @@
                     LDxw(x1, wback, fixedaddress);      \
                     ed = x1;                            \
                 }
-
+// GETSED can use r1 for ed, and r2 for wback. ed will be sign extended!
+#define GETSED(D)  if(MODREG) {                         \
+                    ed = xRAX+(nextop&7)+(rex.b<<3);    \
+                    wback = 0;                          \
+                    if(!rex.w) {                        \
+                        ADDW(x1, ed, xZR);              \
+                        ed = x1;                        \
+                    }                                   \
+                } else {                                \
+                    SMREAD()                            \
+                    addr = geted(dyn, addr, ninst, nextop, &wback, x2, x1, &fixedaddress, rex, NULL, 1, D); \
+                    if(rex.w)                           \
+                        LD(x1, wback, fixedaddress);    \
+                    else                                \
+                        LW(x1, wback, fixedaddress);    \
+                    ed = x1;                            \
+                }
 // GETEDx can use r1 for ed, and r2 for wback. wback is 0 if ed is xEAX..xEDI
 #define GETEDx(D) if(MODREG) {                          \
                     ed = xRAX+(nextop&7)+(rex.b<<3);    \
@@ -83,6 +99,16 @@
                     LD(x1, wback, fixedaddress);        \
                     ed = x1;                            \
                 }
+//GETEDH can use hint for ed, and r1 or r2 for wback (depending on hint). wback is 0 if ed is xEAX..xEDI
+#define GETEDH(hint, D) if(MODREG) {                    \
+                    ed = xRAX+(nextop&7)+(rex.b<<3);    \
+                    wback = 0;                          \
+                } else {                                \
+                    SMREAD();                           \
+                    addr = geted(dyn, addr, ninst, nextop, &wback, (hint==x2)?x1:x2, ed, &fixedaddress, rex, NULL, 1, D); \
+                    LDxw(hint, wback, fixedaddress);    \
+                    ed = hint;                          \
+                }
 //GETEWW will use i for ed, and can use w for wback.
 #define GETEWW(w, i, D) if(MODREG) {        \
                     wback = xRAX+(nextop&7)+(rex.b<<3);\
@@ -173,22 +199,39 @@
 #define MARKLOCK dyn->insts[ninst].marklock = dyn->native_size
 #define GETMARKLOCK dyn->insts[ninst].marklock
 
+#define Bxx_gen(OP, M, reg1, reg2)      \
+    j64 = GET##M - dyn->native_size;    \
+    B##OP (reg1, reg2, j64)
+
 // Branch to MARK if reg1==reg2 (use j64)
-#define BEQ_MARK(reg1, reg2)           \
-    j64 = GETMARK-(dyn->native_size);  \
-    BEQ(reg1, reg2, j64)
+#define BEQ_MARK(reg1, reg2) Bxx_gen(EQ, MARK, reg1, reg2)
 // Branch to MARK if reg1!=reg2 (use j64)
-#define BNE_MARK(reg1, reg2)           \
-    j64 = GETMARK-(dyn->native_size);  \
-    BNE(reg1, reg2, j64)
+#define BNE_MARK(reg1, reg2) Bxx_gen(NE, MARK, reg1, reg2)
+// Branch to MARK if reg1<reg2 (use j64)
+#define BLT_MARK(reg1, reg2) Bxx_gen(LT, MARK, reg1, reg2)
+// Branch to MARK2 if reg1==reg2 (use j64)
+#define BEQ_MARK2(reg1, reg2) Bxx_gen(EQ, MARK2, reg1,reg2)
+// Branch to MARK2 if reg1!=reg2 (use j64)
+#define BNE_MARK2(reg1, reg2) Bxx_gen(NE, MARK2, reg1,reg2)
+// Branch to MARK2 if reg1<>reg2 (use j64)
+#define BLT_MARK2(reg1, reg2) Bxx_gen(LT, MARK2, reg1,reg2)
+// Branch to MARK3 if reg1==reg2 (use j64)
+#define BEQ_MARK3(reg1, reg2) Bxx_gen(EQ, MARK3, reg1, reg2)
+// Branch to MARK3 if reg1!=reg2 (use j64)
+#define BNE_MARK3(reg1, reg2) Bxx_gen(NE, MARK3, reg1, reg2)
+
 // Branch to NEXT if reg1==0 (use j64)
-#define CBZ_NEXT(reg1)                  \
+#define CBZ_NEXT(reg1)                 \
     j64 = (dyn->insts)?(dyn->insts[ninst].epilog-(dyn->native_size)):0; \
     BEQ(reg1, xZR, j64)
 // Branch to NEXT if reg1!=0 (use j64)
-#define CBNZ_NEXT(reg1)                 \
+#define CBNZ_NEXT(reg1)                \
     j64 = (dyn->insts)?(dyn->insts[ninst].epilog-(dyn->native_size)):0; \
     BNE(reg1, xZR, j64)
+// Branch to next instruction unconditionnal (use j64)
+#define B_NEXT_nocond                                               \
+    j64 = (dyn->insts)?(dyn->insts[ninst].epilog-(dyn->native_size)):0;\
+    B(j64)
 
 #define IFX(A)  if((dyn->insts[ninst].x64.gen_flags&(A)))
 #define IFX_PENDOR0  if((dyn->insts[ninst].x64.gen_flags&(X_PEND) || !dyn->insts[ninst].x64.gen_flags))
@@ -439,6 +482,7 @@ void* rv64_next(x64emu_t* emu, uintptr_t addr);
 #define emit_test8      STEPNAME(emit_test8)
 #define emit_test16     STEPNAME(emit_test16)
 #define emit_test32     STEPNAME(emit_test32)
+#define emit_test32c    STEPNAME(emit_test32)
 #define emit_add32      STEPNAME(emit_add32)
 #define emit_add32c     STEPNAME(emit_add32c)
 #define emit_add8       STEPNAME(emit_add8)
@@ -568,6 +612,7 @@ void emit_cmp32_0(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int s3, int
 //void emit_test8(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3, int s4, int s5);
 //void emit_test16(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3, int s4, int s5);
 void emit_test32(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int s2, int s3, int s4, int s5);
+void emit_test32c(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int64_t c, int s3, int s4, int s5);
 void emit_add32(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int s2, int s3, int s4, int s5);
 void emit_add32c(dynarec_rv64_t* dyn, int ninst, rex_t rex, int s1, int64_t c, int s2, int s3, int s4, int s5);
 //void emit_add8(dynarec_rv64_t* dyn, int ninst, int s1, int s2, int s3, int s4);
diff --git a/src/dynarec/rv64/rv64_emitter.h b/src/dynarec/rv64/rv64_emitter.h
index 7f13eb10..a7528827 100644
--- a/src/dynarec/rv64/rv64_emitter.h
+++ b/src/dynarec/rv64/rv64_emitter.h
@@ -293,14 +293,35 @@ f28–31  ft8–11  FP temporaries                  Caller
 #define SRAIxw(rd, rs1, imm)        if (rex.w) { SRAI(rd, rs1, imm); } else { SRAIW(rd, rs1, imm); }
 
 // RV32M
-// rd = rs1 * rs2
+// rd =(lower) rs1 * rs2 (both signed)
 #define MUL(rd, rs1, rs2)           EMIT(R_type(0b0000001, rs2, rs1, 0b000, rd, 0b0110011))
+// rd =(upper) rs1 * rs2 (both signed)
 #define MULH(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b001, rd, 0b0110011))
+// rd =(upper) (signed)rs1 * (unsigned)rs2
+#define MULHSU(rd, rs1, rs2)        EMIT(R_type(0b0000001, rs2, rs1, 0b010, rd, 0b0110011))
+// rd =(upper) rs1 * rs2 (both unsigned)
+#define MULHU(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b011, rd, 0b0110011))
+// rd =(upper) rs1 / rs2
+#define DIV(rd, rs1, rs2)           EMIT(R_type(0b0000001, rs2, rs1, 0b100, rd, 0b0110011))
+#define DIVU(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b101, rd, 0b0110011))
+// rd = rs1 mod rs2
+#define REM(rd, rs1, rs2)           EMIT(R_type(0b0000001, rs2, rs1, 0b110, rd, 0b0110011))
+#define REMU(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b111, rd, 0b0110011))
 
 // RV64M
 // rd = rs1 * rs2
 #define MULW(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b000, rd, 0b0111011))
 // rd = rs1 * rs2
 #define MULxw(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b000, rd, rex.w?0b0110011:0b0111011))
+// rd = rs1 / rs2
+#define DIVW(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b100, rd, 0b0111011))
+#define DIVxw(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b100, rd, rex.w?0b0110011:0b0111011))
+#define DIVUW(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b101, rd, 0b0111011))
+#define DIVUxw(rd, rs1, rs2)        EMIT(R_type(0b0000001, rs2, rs1, 0b101, rd, rex.w?0b0110011:0b0111011))
+// rd = rs1 mod rs2
+#define REMW(rd, rs1, rs2)          EMIT(R_type(0b0000001, rs2, rs1, 0b110, rd, 0b0111011))
+#define REMxw(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b110, rd, rex.w?0b0110011:0b0111011))
+#define REMUW(rd, rs1, rs2)         EMIT(R_type(0b0000001, rs2, rs1, 0b111, rd, 0b0111011))
+#define REMUxw(rd, rs1, rs2)        EMIT(R_type(0b0000001, rs2, rs1, 0b111, rd, rex.w?0b0110011:0b0111011))
 
 #endif //__RV64_EMITTER_H__