From: Julian Seward Date: Fri, 23 Nov 2007 02:46:29 +0000 (+0000) Subject: Implement DAA/DAS/AAA/AAS. Really stupid and ugly instructions which X-Git-Tag: svn/VALGRIND_3_3_1^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2a96fbfc0176b6dc9a390b430d478c55fbbee9d6;p=thirdparty%2Fvalgrind.git Implement DAA/DAS/AAA/AAS. Really stupid and ugly instructions which might have made sense in 1973, but not now. Fixes #152501. git-svn-id: svn://svn.valgrind.org/vex/trunk@1800 --- diff --git a/VEX/priv/guest-x86/gdefs.h b/VEX/priv/guest-x86/gdefs.h index 3b36ea4f6a..3ac2ff3732 100644 --- a/VEX/priv/guest-x86/gdefs.h +++ b/VEX/priv/guest-x86/gdefs.h @@ -114,6 +114,8 @@ extern ULong x86g_calculate_RCL ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz ); +extern UInt x86g_calculate_daa_das_aaa_aas ( UInt AX_and_flags, UInt opcode ); + extern ULong x86g_check_fldcw ( UInt fpucw ); extern UInt x86g_create_fpucw ( UInt fpround ); diff --git a/VEX/priv/guest-x86/ghelpers.c b/VEX/priv/guest-x86/ghelpers.c index 784662c4a0..c89357603c 100644 --- a/VEX/priv/guest-x86/ghelpers.c +++ b/VEX/priv/guest-x86/ghelpers.c @@ -1875,6 +1875,135 @@ ULong x86g_calculate_RCL ( UInt arg, UInt rot_amt, UInt eflags_in, UInt sz ) } +/* CALLED FROM GENERATED CODE: CLEAN HELPER */ +/* Calculate both flags and value result for DAA/DAS/AAA/AAS. + AX value in low half of arg, OSZACP in upper half. + See guest-x86/toIR.c usage point for details. +*/ +static UInt calc_parity_8bit ( UInt w32 ) { + UInt i; + UInt p = 1; + for (i = 0; i < 8; i++) + p ^= (1 & (w32 >> i)); + return p; +} +UInt x86g_calculate_daa_das_aaa_aas ( UInt flags_and_AX, UInt opcode ) +{ + UInt r_AL = (flags_and_AX >> 0) & 0xFF; + UInt r_AH = (flags_and_AX >> 8) & 0xFF; + UInt r_O = (flags_and_AX >> (16 + X86G_CC_SHIFT_O)) & 1; + UInt r_S = (flags_and_AX >> (16 + X86G_CC_SHIFT_S)) & 1; + UInt r_Z = (flags_and_AX >> (16 + X86G_CC_SHIFT_Z)) & 1; + UInt r_A = (flags_and_AX >> (16 + X86G_CC_SHIFT_A)) & 1; + UInt r_C = (flags_and_AX >> (16 + X86G_CC_SHIFT_C)) & 1; + UInt r_P = (flags_and_AX >> (16 + X86G_CC_SHIFT_P)) & 1; + UInt result = 0; + + switch (opcode) { + case 0x27: { /* DAA */ + UInt old_AL = r_AL; + UInt old_C = r_C; + r_C = 0; + if ((r_AL & 0xF) > 9 || r_A == 1) { + r_AL = r_AL + 6; + r_C = old_C; + if (r_AL >= 0x100) r_C = 1; + r_A = 1; + } else { + r_A = 0; + } + if (old_AL > 0x99 || old_C == 1) { + r_AL = r_AL + 0x60; + r_C = 1; + } else { + r_C = 0; + } + /* O is undefined. S Z and P are set according to the + result. */ + r_AL &= 0xFF; + r_O = 0; /* let's say */ + r_S = (r_AL & 0x80) ? 1 : 0; + r_Z = (r_AL == 0) ? 1 : 0; + r_P = calc_parity_8bit( r_AL ); + break; + } + case 0x2F: { /* DAS */ + UInt old_AL = r_AL; + UInt old_C = r_C; + r_C = 0; + if ((r_AL & 0xF) > 9 || r_A == 1) { + Bool borrow = r_AL < 6; + r_AL = r_AL - 6; + r_C = old_C; + if (borrow) r_C = 1; + r_A = 1; + } else { + r_A = 0; + } + if (old_AL > 0x99 || old_C == 1) { + r_AL = r_AL - 0x60; + r_C = 1; + } else { + /* Intel docs are wrong: r_C = 0; */ + } + /* O is undefined. S Z and P are set according to the + result. */ + r_AL &= 0xFF; + r_O = 0; /* let's say */ + r_S = (r_AL & 0x80) ? 1 : 0; + r_Z = (r_AL == 0) ? 1 : 0; + r_P = calc_parity_8bit( r_AL ); + break; + } + case 0x37: { /* AAA */ + Bool nudge = r_AL > 0xF9; + if ((r_AL & 0xF) > 9 || r_A == 1) { + r_AL = r_AL + 6; + r_AH = r_AH + 1 + (nudge ? 1 : 0); + r_A = 1; + r_C = 1; + r_AL = r_AL & 0xF; + } else { + r_A = 0; + r_C = 0; + r_AL = r_AL & 0xF; + } + /* O S Z and P are undefined. */ + r_O = r_S = r_Z = r_P = 0; /* let's say */ + break; + } + case 0x3F: { /* AAS */ + Bool nudge = r_AL < 0x06; + if ((r_AL & 0xF) > 9 || r_A == 1) { + r_AL = r_AL - 6; + r_AH = r_AH - 1 - (nudge ? 1 : 0); + r_A = 1; + r_C = 1; + r_AL = r_AL & 0xF; + } else { + r_A = 0; + r_C = 0; + r_AL = r_AL & 0xF; + } + /* O S Z and P are undefined. */ + r_O = r_S = r_Z = r_P = 0; /* let's say */ + break; + } + default: + vassert(0); + } + result = ( (r_O & 1) << (16 + X86G_CC_SHIFT_O) ) + | ( (r_S & 1) << (16 + X86G_CC_SHIFT_S) ) + | ( (r_Z & 1) << (16 + X86G_CC_SHIFT_Z) ) + | ( (r_A & 1) << (16 + X86G_CC_SHIFT_A) ) + | ( (r_C & 1) << (16 + X86G_CC_SHIFT_C) ) + | ( (r_P & 1) << (16 + X86G_CC_SHIFT_P) ) + | ( (r_AH & 0xFF) << 8 ) + | ( (r_AL & 0xFF) << 0 ); + return result; +} + + /* CALLED FROM GENERATED CODE */ /* DIRTY HELPER (non-referentially-transparent) */ /* Horrible hack. On non-x86 platforms, return 1. */ diff --git a/VEX/priv/guest-x86/toIR.c b/VEX/priv/guest-x86/toIR.c index 2717f512d4..370cfcd521 100644 --- a/VEX/priv/guest-x86/toIR.c +++ b/VEX/priv/guest-x86/toIR.c @@ -11196,44 +11196,62 @@ DisResult disInstr_X86_WRK ( DIP("leave\n"); break; -//-- /* ---------------- Misc weird-ass insns --------------- */ -//-- -//-- case 0x27: /* DAA */ -//-- case 0x2F: /* DAS */ -//-- t1 = newTemp(cb); -//-- uInstr2(cb, GET, 1, ArchReg, R_AL, TempReg, t1); -//-- /* Widen %AL to 32 bits, so it's all defined when we push it. */ -//-- uInstr1(cb, WIDEN, 4, TempReg, t1); -//-- uWiden(cb, 1, False); -//-- uInstr0(cb, CALLM_S, 0); -//-- uInstr1(cb, PUSH, 4, TempReg, t1); -//-- uInstr1(cb, CALLM, 0, Lit16, -//-- opc == 0x27 ? VGOFF_(helper_DAA) : VGOFF_(helper_DAS) ); -//-- uFlagsRWU(cb, FlagsAC, FlagsSZACP, FlagO); -//-- uInstr1(cb, POP, 4, TempReg, t1); -//-- uInstr0(cb, CALLM_E, 0); -//-- uInstr2(cb, PUT, 1, TempReg, t1, ArchReg, R_AL); -//-- DIP(opc == 0x27 ? "daa\n" : "das\n"); -//-- break; -//-- -//-- case 0x37: /* AAA */ -//-- case 0x3F: /* AAS */ -//-- t1 = newTemp(cb); -//-- uInstr2(cb, GET, 2, ArchReg, R_EAX, TempReg, t1); -//-- /* Widen %AL to 32 bits, so it's all defined when we push it. */ -//-- uInstr1(cb, WIDEN, 4, TempReg, t1); -//-- uWiden(cb, 2, False); -//-- uInstr0(cb, CALLM_S, 0); -//-- uInstr1(cb, PUSH, 4, TempReg, t1); -//-- uInstr1(cb, CALLM, 0, Lit16, -//-- opc == 0x37 ? VGOFF_(helper_AAA) : VGOFF_(helper_AAS) ); -//-- uFlagsRWU(cb, FlagA, FlagsAC, FlagsEmpty); -//-- uInstr1(cb, POP, 4, TempReg, t1); -//-- uInstr0(cb, CALLM_E, 0); -//-- uInstr2(cb, PUT, 2, TempReg, t1, ArchReg, R_EAX); -//-- DIP(opc == 0x37 ? "aaa\n" : "aas\n"); -//-- break; -//-- + /* ---------------- Misc weird-ass insns --------------- */ + + case 0x27: /* DAA */ + case 0x2F: /* DAS */ + case 0x37: /* AAA */ + case 0x3F: /* AAS */ + /* An ugly implementation for some ugly instructions. Oh + well. */ + if (sz != 4) goto decode_failure; + t1 = newTemp(Ity_I32); + t2 = newTemp(Ity_I32); + /* Make up a 32-bit value (t1), with the old value of AX in the + bottom 16 bits, and the old OSZACP bitmask in the upper 16 + bits. */ + assign(t1, + binop(Iop_16HLto32, + unop(Iop_32to16, + mk_x86g_calculate_eflags_all()), + getIReg(2, R_EAX) + )); + /* Call the helper fn, to get a new AX and OSZACP value, and + poke both back into the guest state. Also pass the helper + the actual opcode so it knows which of the 4 instructions it + is doing the computation for. */ + vassert(opc == 0x27 || opc == 0x2F || opc == 0x37 || opc == 0x3F); + assign(t2, + mkIRExprCCall( + Ity_I32, 0/*regparm*/, "x86g_calculate_daa_das_aaa_aas", + &x86g_calculate_daa_das_aaa_aas, + mkIRExprVec_2( mkexpr(t1), mkU32( opc & 0xFF) ) + )); + putIReg(2, R_EAX, unop(Iop_32to16, mkexpr(t2) )); + + stmt( IRStmt_Put( OFFB_CC_OP, mkU32(X86G_CC_OP_COPY) )); + stmt( IRStmt_Put( OFFB_CC_DEP2, mkU32(0) )); + stmt( IRStmt_Put( OFFB_CC_DEP1, + binop(Iop_And32, + binop(Iop_Shr32, mkexpr(t2), mkU8(16)), + mkU32( X86G_CC_MASK_C | X86G_CC_MASK_P + | X86G_CC_MASK_A | X86G_CC_MASK_Z + | X86G_CC_MASK_S| X86G_CC_MASK_O ) + ) + ) + ); + /* Set NDEP even though it isn't used. This makes redundant-PUT + elimination of previous stores to this field work better. */ + stmt( IRStmt_Put( OFFB_CC_NDEP, mkU32(0) )); + switch (opc) { + case 0x27: DIP("daa\n"); break; + case 0x2F: DIP("das\n"); break; + case 0x37: DIP("aaa\n"); break; + case 0x3F: DIP("aas\n"); break; + default: vassert(0); + } + break; + //-- case 0xD4: /* AAM */ //-- case 0xD5: /* AAD */ //-- d32 = getIByte(delta); delta++;