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_2.c38
1 files changed, 34 insertions, 4 deletions
diff --git a/src/dynarec/rv64/dynarec_rv64_00_2.c b/src/dynarec/rv64/dynarec_rv64_00_2.c
index e62c73e9..fbabc49a 100644
--- a/src/dynarec/rv64/dynarec_rv64_00_2.c
+++ b/src/dynarec/rv64/dynarec_rv64_00_2.c
@@ -226,7 +226,7 @@ uintptr_t dynarec64_00_2(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int
             emit_test32(dyn, ninst, rex, ed, gd, x3, x4, x5);
             break;
         case 0x86:
-            INST_NAME("(LOCK)XCHG Eb, Gb");
+            INST_NAME("(LOCK) XCHG Eb, Gb");
             nextop = F8;
             if(MODREG) {
                 GETGB(x1);
@@ -240,9 +240,39 @@ uintptr_t dynarec64_00_2(dynarec_rv64_t* dyn, uintptr_t addr, uintptr_t ip, int
                 GETGB(x3);
                 addr = geted(dyn, addr, ninst, nextop, &ed, x2, x1, &fixedaddress, rex, LOCK_LOCK, 0, 0);
                 SMDMB();
-                LBU(x1, ed, 0);
-                SB(gd, ed, 0);
-                SMDMB();
+
+                // calculate shift amount
+                ANDI(x1, ed, 0x3);
+                SLLI(x1, x1, 3);
+
+                // align address to 4-bytes to use ll.w/sc.w
+                ADDI(x4, xZR, 0xffc);
+                AND(x6, ed, x4);
+
+                // load aligned data
+                LWU(x5, x6, 0);
+
+                // insert gd byte into the aligned data
+                ADDI(x4, xZR, 0xff);
+                SLL(x4, x4, x1);
+                NOT(x4, x4);
+                AND(x4, x5, x4);
+                SLL(x5, gd, x1);
+                OR(x4, x4, x5);
+
+                // do aligned ll/sc sequence
+                MARKLOCK;
+                LR_W(x1, x6, 1, 1);
+                SC_W(x5, x4, x6, 1, 1);
+                BNEZ_MARKLOCK(x5);
+
+                // calculate shift amount again
+                ANDI(x4, ed, 0x3);
+                SLLI(x4, x4, 3);
+
+                // extract loaded byte
+                SRL(x1, x1, x4);
+
                 gd = x1;
                 GBBACK(x3);
             }