Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions src/dynarec/arm64/dynarec_arm64_00.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
24 changes: 19 additions & 5 deletions src/emu/x64run.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/libtools/decopcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
Binary file added tests32/extensions/arpl
Binary file not shown.
98 changes: 98 additions & 0 deletions tests32/extensions/arpl.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

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;
}

1 change: 1 addition & 0 deletions tests32/extensions/arpl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
arpl: ok
Loading