diff --git a/src/dynarec/arm64/dynarec_arm64_00.c b/src/dynarec/arm64/dynarec_arm64_00.c index 9acb193ed4..b42723075e 100644 --- a/src/dynarec/arm64/dynarec_arm64_00.c +++ b/src/dynarec/arm64/dynarec_arm64_00.c @@ -979,16 +979,37 @@ uintptr_t dynarec64_00(dynarec_arm_t* dyn, uintptr_t addr, uintptr_t ip, int nin *ok = 0; } break; - case 0x63: - if(rex.is32bits) { - // ARPL here - DEFAULT; - } else { - INST_NAME("MOVSXD Gd, Ed"); - nextop = F8; - GETGD; - if(rex.w) { - if(MODREG) { // reg <= reg + case 0x63: + if(rex.is32bits) { + // ARPL r/m16, r16 + // If dst.RPL < src.RPL then dst.RPL = src.RPL and ZF=1, else ZF=0. + // Only ZF is modified. + INST_NAME("ARPL Ew, Gw"); + nextop = F8; + SETFLAGS(X_ZF, SF_SUBSET); + SET_DFNONE(); + GETEW(x1, 0); + GETGW(x2); + // Extract RPL (low 2 bits) + UBFXw(x3, ed, 0, 2); + UBFXw(x4, gd, 0, 2); + // need_update = (dst_rpl < src_rpl) + CMPSw_REG(x3, x4); + CSETw(x5, cLT); + // ZF = need_update + BFIw(xFlags, x5, F_ZF, 1); + // If no update needed then skip the writeback. + CBZw_MARK(x5); + // Update dest selector's low 2 bits. + BFXILw(ed, gd, 0, 2); + EWBACK; + MARK; + } else { + INST_NAME("MOVSXD Gd, Ed"); + nextop = F8; + GETGD; + if(rex.w) { + if(MODREG) { // reg <= reg SXTWx(gd, TO_NAT((nextop & 7) + (rex.b << 3))); } else { // mem <= reg SMREAD(); diff --git a/src/emu/x64run.c b/src/emu/x64run.c index 98b33880d2..da8b6ae149 100644 --- a/src/emu/x64run.c +++ b/src/emu/x64run.c @@ -518,13 +518,27 @@ int Run(x64emu_t *emu, int step) break; case 0x63: /* MOVSXD Gd,Ed */ nextop = F8; - GETE4(0); - GETGD; if(rex.is32bits) { - // ARPL here - // faking to always happy... - SET_FLAG(F_ZF); + // ARPL r/m16, r16 + // If dest.RPL < src.RPL then dest.RPL = src.RPL and ZF=1, else ZF=0. + // Only ZF is modified. + CHECK_FLAGS(emu); + + GETEW(0); + GETGW; + uint16_t dst = EW->word[0]; + uint16_t src = GW->word[0]; + uint16_t dst_rpl = dst & 0x3; + uint16_t src_rpl = src & 0x3; + if(dst_rpl < src_rpl) { + EW->word[0] = (dst & 0xFFFC) | src_rpl; + SET_FLAG(F_ZF); + } else { + CLEAR_FLAG(F_ZF); + } } else { + GETE4(0); + GETGD; if(rex.w) GD->sq[0] = ED->sdword[0]; else diff --git a/src/libtools/decopcode.c b/src/libtools/decopcode.c index 53a5c1d4a3..c63e15e594 100644 --- a/src/libtools/decopcode.c +++ b/src/libtools/decopcode.c @@ -228,7 +228,6 @@ int decode_opcode(uintptr_t rip, int is32bits) case 0x39: case 0x3a: case 0x3b: - case 0x63: case 0x69: case 0x6B: case 0x84: @@ -238,6 +237,13 @@ int decode_opcode(uintptr_t rip, int is32bits) case 0x8E: nextop = addr[idx++]; return (MODREG)?0:(OPCODE_READ); + case 0x63: + // In 32-bit mode this opcode is ARPL Ew, Gw (read + conditional write to r/m16). + // In 64-bit mode this opcode is MOVSXD Gd, Ed (read-only). + nextop = addr[idx++]; + if(is32bits) + return (MODREG)?0:(OPCODE_WRITE|OPCODE_READ); + return (MODREG)?0:(OPCODE_READ); case 0x06: case 0x0E: case 0x16: diff --git a/tests32/extensions/arpl b/tests32/extensions/arpl new file mode 100755 index 0000000000..1eea2b55a7 Binary files /dev/null and b/tests32/extensions/arpl differ diff --git a/tests32/extensions/arpl.c b/tests32/extensions/arpl.c new file mode 100644 index 0000000000..09cad00494 --- /dev/null +++ b/tests32/extensions/arpl.c @@ -0,0 +1,98 @@ +#include +#include +#include + +static inline uint32_t read_eflags(void) { + uint32_t f; + __asm__ volatile( + "pushfl\n\t" + "popl %0\n\t" + : "=r"(f) + : + : "memory"); + return f; +} + +static inline void write_eflags(uint32_t f) { + __asm__ volatile( + "pushl %0\n\t" + "popfl\n\t" + : + : "r"(f) + : "cc", "memory"); +} + +static inline uint32_t do_arpl(uint32_t *eax_inout, uint32_t ebx, uint32_t eflags_in) { + uint32_t flags_out; + uint32_t eax = *eax_inout; + + __asm__ volatile( + "pushl %[fin]\n\t" + "popfl\n\t" + "arpl %%bx, %%ax\n\t" + "pushfl\n\t" + "popl %[fout]\n\t" + : [fout] "=r"(flags_out), "+a"(eax) + : "b"(ebx), [fin] "r"(eflags_in) + : "cc", "memory"); + + *eax_inout = eax; + return flags_out; +} + +int main(void) { + // Only check the status flags ARPL is documented to preserve (all but ZF). + // We use a mask of classic status flags: CF, PF, AF, ZF, SF, OF. + const uint32_t kStatusMask = (1u << 0) | (1u << 2) | (1u << 4) | (1u << 6) | (1u << 7) | (1u << 11); + const uint32_t kZF = (1u << 6); + + // Start from current flags so we don't fight reserved / privileged bits. + uint32_t base = read_eflags(); + // Baseline: CF=1, PF=1, AF=0, ZF=0, SF=1, OF=1. + uint32_t wanted = (base & ~kStatusMask) | (1u << 0) | (1u << 2) | (1u << 7) | (1u << 11); + wanted &= ~kZF; + + for (uint32_t dst_rpl = 0; dst_rpl < 4; ++dst_rpl) { + for (uint32_t src_rpl = 0; src_rpl < 4; ++src_rpl) { + uint32_t eax = 0xFFFF0000u | (0x1FCu + dst_rpl); // low 16 bits end with RPL + uint32_t ebx = 0xEEEE0000u | (0x201u + src_rpl); // low 16 bits end with RPL + + // Reset flags to known state before each ARPL. + write_eflags(wanted); + + uint32_t flags = do_arpl(&eax, ebx, wanted); + + uint16_t dst_before = (uint16_t)(0x1FCu + dst_rpl); + uint16_t src = (uint16_t)(0x201u + src_rpl); + + uint16_t exp_dst = dst_before; + uint32_t need_update = ((dst_before & 0x3) < (src & 0x3)); + if (need_update) { + exp_dst = (uint16_t)((dst_before & 0xFFFCu) | (src & 0x3u)); + } + + // Check destination low 16 and upper 16 untouched. + if (((uint16_t)eax) != exp_dst) { + printf("FAIL: dst mismatch dst_rpl=%u src_rpl=%u got=0x%04x exp=0x%04x\n", + dst_rpl, src_rpl, (unsigned)((uint16_t)eax), (unsigned)exp_dst); + return 1; + } + if ((eax & 0xFFFF0000u) != 0xFFFF0000u) { + printf("FAIL: upper eax modified dst_rpl=%u src_rpl=%u got=0x%08x\n", dst_rpl, src_rpl, eax); + return 1; + } + + // Check ZF and that other status flags were preserved. + uint32_t exp_flags = (wanted & ~kZF) | (need_update ? kZF : 0); + if (((flags ^ exp_flags) & kStatusMask) != 0) { + printf("FAIL: flags mismatch dst_rpl=%u src_rpl=%u got=0x%08x exp=0x%08x\n", + dst_rpl, src_rpl, flags, exp_flags); + return 1; + } + } + } + + printf("arpl: ok\n"); + return 0; +} + diff --git a/tests32/extensions/arpl.txt b/tests32/extensions/arpl.txt new file mode 100644 index 0000000000..648e02da38 --- /dev/null +++ b/tests32/extensions/arpl.txt @@ -0,0 +1 @@ +arpl: ok