/*++ Copyright (c) 1995-2000 Microsoft Corporation Module Name: fragmisc.c Abstract: Miscellaneous instuction fragments. Author: 12-Jun-1995 BarryBo Revision History: 24-Aug-1999 [askhalid] copied from 32-bit wx86 directory and make work for 64bit. 20-Sept-1999[barrybo] added FRAG2REF(CmpXchg8bFrag32, ULONGLONG) --*/ #include #include #include #include #include #include #include "wx86.h" #include "wx86nt.h" #include "fragp.h" #include "fragmisc.h" #include "cpunotif.h" #include "config.h" #include "mrsw.h" #include "cpuassrt.h" #if MSCPU #include "atomic.h" #endif ASSERTNAME; void CpupUnlockTCAndDoInterrupt( PTHREADSTATE cpu, int Interrupt ) { MrswReaderExit(&MrswTC); cpu->fTCUnlocked = TRUE; CpupDoInterrupt(Interrupt); // If we get here, CpupDoInterrupt returned due to CONTINUE_EXECUTION. // We need to redesign so we can jump to EndTranslatedCode now, as // the cache may have been flushed. CPUASSERT(FALSE); MrswReaderEnter(&MrswTC); cpu->fTCUnlocked = FALSE; } FRAG0(CbwFrag32) { eax = (signed long)(signed short)ax; } FRAG0(CbwFrag16) { ax = (signed short)(signed char)al; } FRAG0(PushEsFrag) { PUSH_LONG(ES); } FRAG0(PopEsFrag) { DWORD temp; POP_LONG(temp); ES = (USHORT)temp; } FRAG0(PushFsFrag) { PUSH_LONG(FS); } FRAG0(PopFsFrag) { DWORD temp; POP_LONG(temp); FS = (USHORT)temp; } FRAG0(PushGsFrag) { PUSH_LONG(GS); } FRAG0(PopGsFrag) { DWORD temp; POP_LONG(temp); GS = (USHORT)temp; } FRAG0(PushCsFrag) { PUSH_LONG(CS); } FRAG0(AasFrag) { if ( (al & 0x0f) > 9 || GET_AUXFLAG) { ah--; al = (al-6) & 0x0f; SET_CFLAG_ON; SET_AUXFLAG_ON; } else { SET_CFLAG_OFF; SET_AUXFLAG_OFF; al &= 0xf; } } FRAG0(PushSsFrag) { PUSH_LONG(SS); } FRAG0(PopSsFrag) { DWORD temp; POP_LONG(temp); SS = (USHORT)temp; } FRAG0(PushDsFrag) { PUSH_LONG(DS); } FRAG0(PopDsFrag) { DWORD temp; POP_LONG(temp); DS = (USHORT)temp; } FRAG0(DaaFrag) { if ((al & 0x0f) > 9 || GET_AUXFLAG) { al += 6; SET_AUXFLAG_ON; } else { SET_AUXFLAG_OFF; } if ((al & 0xf0) > 0x90 || GET_CFLAG) { al += 0x60; SET_CFLAG_ON; } else { SET_CFLAG_OFF; } SET_ZFLAG(al); SET_PFLAG(al); SET_SFLAG(al << (31-7)); // SET_SFLAG_IND(al & 0x80); } FRAG0(DasFrag) { if ( (al & 0x0f) > 9 || GET_AUXFLAG) { al -= 6; SET_AUXFLAG_ON; } else { SET_AUXFLAG_OFF; } if ( al > 0x9f || GET_CFLAG) { al -= 0x60; SET_CFLAG_ON; } else { SET_CFLAG_OFF; } SET_ZFLAG(al); SET_PFLAG(al); SET_SFLAG(al << (31-7)); // SET_SFLAG_IND(al & 0x80); } FRAG0(AaaFrag) { if ((al & 0x0f) > 9 || GET_AUXFLAG) { al=(al+6) & 0x0f; ah++; // inc ah SET_AUXFLAG_ON; SET_CFLAG_ON; } else { SET_AUXFLAG_OFF; SET_CFLAG_OFF; al &= 0xf; } } FRAG1IMM(AadFrag, BYTE) { al += ah * op1; ah = 0; SET_ZFLAG(al); SET_PFLAG(al); SET_SFLAG(al << (31-7)); // SET_SFLAG_IND(al & 0x80); } FRAG2(ImulFrag16, USHORT) { Imul3ArgFrag16(cpu, pop1, GET_SHORT(pop1), op2); } FRAG2(ImulFrag16A, USHORT) { Imul3ArgFrag16A(cpu, pop1, *pop1, op2); } FRAG3(Imul3ArgFrag16, USHORT, USHORT, USHORT) { long result; result = (long)(short)op2 * (long)(short)op3; PUT_SHORT(pop1, (USHORT)(short)result); if (HIWORD(result) == 0 || HIWORD(result) == 0xffff) { SET_CFLAG_OFF; SET_OFLAG_OFF; } else { SET_CFLAG_ON; SET_OFLAG_ON; } } FRAG3(Imul3ArgFrag16A, USHORT, USHORT, USHORT) { long result; result = (short)op2 * (short)op3; *pop1 = (USHORT)(short)result; if (HIWORD(result) == 0 || HIWORD(result) == 0xffff) { SET_CFLAG_OFF; SET_OFLAG_OFF; } else { SET_CFLAG_ON; SET_OFLAG_ON; } } FRAG2(ImulNoFlagsFrag16, USHORT) { short op1 = (short)GET_SHORT(pop1); PUT_SHORT(pop1, (op2 * (short)op2)); } FRAG2(ImulNoFlagsFrag16A, USHORT) { *(short *)pop1 *= (short)op2; } FRAG3(Imul3ArgNoFlagsFrag16, USHORT, USHORT, USHORT) { PUT_SHORT(pop1, ((short)op2 * (short)op3)); } FRAG3(Imul3ArgNoFlagsFrag16A, USHORT, USHORT, USHORT) { *pop1 = (USHORT)((short)op2 * (short)op3); } FRAG2(ImulFrag32, DWORD) { Imul3ArgFrag32(cpu, pop1, GET_LONG(pop1), op2); } FRAG2(ImulFrag32A, DWORD) { Imul3ArgFrag32A(cpu, pop1, *pop1, (long)op2); } FRAG3(Imul3ArgFrag32A, DWORD, DWORD, DWORD) { LARGE_INTEGER result; LONGLONG ll; ll = Int32x32To64((long)op2, (long)op3); result = *(LARGE_INTEGER *)≪ *pop1 = result.LowPart; if (result.HighPart == 0 || result.HighPart == 0xffffffff) { SET_CFLAG_OFF; SET_OFLAG_OFF; } else { SET_CFLAG_ON; SET_OFLAG_ON; } } FRAG3(Imul3ArgFrag32, DWORD, DWORD, DWORD) { LARGE_INTEGER result; LONGLONG ll; ll = Int32x32To64((long)op2, (long)op3); result = *(LARGE_INTEGER *)≪ PUT_LONG(pop1, result.LowPart); if (result.HighPart == 0 || result.HighPart == 0xffffffff) { SET_CFLAG_OFF; SET_OFLAG_OFF; } else { SET_CFLAG_ON; SET_OFLAG_ON; } } FRAG2(ImulNoFlagsFrag32, DWORD) { long op1 = (LONG)GET_LONG(pop1); PUT_LONG(pop1, (op1 * (long)op2)); } FRAG2(ImulNoFlagsFrag32A, DWORD) { *(long *)pop1 *= (long)op2; } FRAG3(Imul3ArgNoFlagsFrag32A, DWORD, DWORD, DWORD) { *pop1 = (DWORD)( (long)op2 * (long)op3); } FRAG3(Imul3ArgNoFlagsFrag32, DWORD, DWORD, DWORD) { PUT_LONG(pop1, ((long)op2 * (long)op3)); } FRAG0(SahfFrag) { DWORD dw = (DWORD)ah; SET_CFLAG(dw << 31); // CFLAG is low-bit of ah SET_PFLAG (!(dw & FLAG_PF)); // flag_pf contains an index into ParityBit[] array SET_AUXFLAG(dw); // AUX bit is already in the right place SET_ZFLAG (!(dw & FLAG_ZF)); // zf has inverse logic SET_SFLAG(dw << (31-7)); // SFLAG is bit 7 in AH } FRAG0(LahfFrag) { ah= 2 | // this bit is always set on Intel ((GET_CFLAG) ? FLAG_CF : 0) | ((GET_PFLAG) ? FLAG_PF : 0) | ((GET_AUXFLAG)? FLAG_AUX: 0) | ((cpu->flag_zf) ? 0 : FLAG_ZF) | // zf has inverse logic ((GET_SFLAG) ? FLAG_SF : 0); } FRAG1IMM(AamFrag, BYTE) { ah = al / op1; al %= op1; SET_ZFLAG(al); SET_PFLAG(al); SET_SFLAG(al << (31-7)); } FRAG0(XlatFrag) { al = GET_BYTE(ebx+al); } FRAG0(CmcFrag) { SET_CFLAG_IND(!GET_CFLAG); } FRAG0(ClcFrag) { SET_CFLAG_OFF; } FRAG0(StcFrag) { SET_CFLAG_ON; } FRAG0(CldFrag) { cpu->flag_df = 1; } FRAG0(StdFrag) { cpu->flag_df = 0xffffffff; } FRAG1(SetoFrag, BYTE) { PUT_BYTE(pop1, (BYTE)GET_OFLAGZO); } FRAG1(SetnoFrag, BYTE) { PUT_BYTE(pop1, (GET_OFLAG == 0)); } FRAG1(SetbFrag, BYTE) { PUT_BYTE(pop1, (BYTE)GET_CFLAGZO); } FRAG1(SetaeFrag, BYTE) { PUT_BYTE(pop1, (GET_CFLAG == 0)); } FRAG1(SeteFrag, BYTE) { PUT_BYTE(pop1, (cpu->flag_zf == 0)); // inverse logic } FRAG1(SetneFrag, BYTE) { PUT_BYTE(pop1, (cpu->flag_zf != 0)); // inverse logic } FRAG1(SetbeFrag, BYTE) { PUT_BYTE(pop1, (GET_CFLAG || cpu->flag_zf == 0)); // inverse logic } FRAG1(SetaFrag, BYTE) { PUT_BYTE(pop1, (GET_CFLAG == 0 && cpu->flag_zf != 0)); // inverse logic } FRAG1(SetsFrag, BYTE) { PUT_BYTE(pop1, (BYTE)GET_SFLAGZO); } FRAG1(SetnsFrag, BYTE) { PUT_BYTE(pop1, (GET_SFLAG == 0)); } FRAG1(SetpFrag, BYTE) { PUT_BYTE(pop1, (GET_PFLAG != 0)); } FRAG1(SetnpFrag, BYTE) { PUT_BYTE(pop1, (GET_PFLAG == 0)); } FRAG1(SetlFrag, BYTE) { PUT_BYTE(pop1, (GET_SFLAG != GET_OFLAG)); } FRAG1(SetgeFrag, BYTE) { PUT_BYTE(pop1, (GET_SFLAGZO == GET_OFLAGZO)); } FRAG1(SetleFrag, BYTE) { PUT_BYTE(pop1, (!cpu->flag_zf || (GET_SFLAG != GET_OFLAG))); // inverse logic } FRAG1(SetgFrag, BYTE) { PUT_BYTE(pop1, (cpu->flag_zf && !(GET_SFLAG ^ GET_OFLAG))); // inverse logic } FRAG2(Movzx8ToFrag16, USHORT) { PUT_SHORT(pop1, (USHORT)(BYTE)op2); } FRAG2(Movzx8ToFrag16A, USHORT) { *pop1 = (USHORT)(BYTE)op2; } FRAG2(Movsx8ToFrag16, USHORT) { PUT_SHORT(pop1, (USHORT)(short)(char)(BYTE)op2); } FRAG2(Movsx8ToFrag16A, USHORT) { *pop1 = (USHORT)(short)(char)(BYTE)op2; } FRAG2(Movzx8ToFrag32, DWORD) { PUT_LONG(pop1, (DWORD)(BYTE)op2); } FRAG2(Movzx8ToFrag32A, DWORD) { *pop1 = (DWORD)(BYTE)op2; } FRAG2(Movsx8ToFrag32, DWORD) { PUT_LONG(pop1, (DWORD)(long)(char)(BYTE)op2); } FRAG2(Movsx8ToFrag32A, DWORD) { *pop1 = (DWORD)(long)(char)(BYTE)op2; } FRAG2(Movzx16ToFrag32, DWORD) { PUT_LONG(pop1, (DWORD)(USHORT)op2); } FRAG2(Movzx16ToFrag32A, DWORD) { *pop1 = (DWORD)(USHORT)op2; } FRAG2(Movsx16ToFrag32, DWORD) { PUT_LONG(pop1, (DWORD)(long)(short)(USHORT)op2); } FRAG2(Movsx16ToFrag32A, DWORD) { *pop1 = (DWORD)(long)(short)(USHORT)op2; } FRAG1(BswapFrag32, DWORD) { DWORD d; PBYTE pSrc = (PBYTE)pop1; d = (pSrc[0] << 24) | (pSrc[1] << 16) | (pSrc[2] << 8) | pSrc[3]; // pop1 is always a pointer to a register, so an ALIGNED store is correct *pop1 = d; } FRAG2(ArplFrag, USHORT) { USHORT op1 = GET_SHORT(pop1); op2 &= 3; // just get the RPL bits of the selector if ((op1&3) < op2) { // RPL bits of DEST < RPL bits of SRC op1 = (op1 & ~3) | op2; // copy RPL bits from SRC to DEST PUT_SHORT(pop1, op1); // store DEST SET_ZFLAG(0); // ZF=1 } else { SET_ZFLAG(1); } } FRAG1(VerrFrag, USHORT) { USHORT op1 = GET_SHORT(pop1) & ~3; // mask off RPL bits if (op1 == KGDT_R3_CODE || // CS: selector op1 == KGDT_R3_DATA || // DS:, SS:, ES: selector op1 == KGDT_R3_TEB // FS: selector ) { SET_ZFLAG(0); // ZF=1 } else { SET_ZFLAG(1); // ZF=0 } } FRAG1(VerwFrag, USHORT) { USHORT op1 = GET_SHORT(pop1) & ~3; // mask off RPL bits if (op1 == KGDT_R3_DATA || // DS:, SS:, ES: selector op1 == KGDT_R3_TEB // FS: selector ) { SET_ZFLAG(0); // ZF=1 } else { SET_ZFLAG(1); // ZF=0 } } FRAG1(SmswFrag, USHORT) { // // This value is empirically discovered by running it on a Pentium // machine. CR0_PE, CR0_EX, and CR0_NE bits were set, and all others // notably CR0_MP, are clear. // PUT_SHORT(pop1, 0x31); } #if MSCPU FRAG0(IntOFrag) { if (GET_OFLAG) { Int4(); // raise overflow } } FRAG0(NopFrag) { } FRAG0(PrivilegedInstructionFrag) { PRIVILEGED_INSTR; } FRAG0(BadInstructionFrag) { Int6(); // Throw invalid opcode exception } FRAG2(FaultFrag, DWORD) { // pop1 = exception code // op2 = address where fault occurred #if DBG LOGPRINT((TRACELOG, "CPU: FaultFrag called\r\n")); #endif RtlRaiseStatus((NTSTATUS)(ULONGLONG)pop1); } #endif //MSCPU FRAG0(CPUID) { switch (eax) { case 0: eax = 1; // We are a 486 with CPUID (PPro returns 2) //ebx = 0x756e6547; // "GenuineIntel" //edx = 0x49656e69; //ecx = 0x6c65746e; ebx = 0x7263694d; // "Micr" with M in the low nibble of BL edx = 0x666f736f; // "osof" with o in the low nibble of DL ecx = 0x55504374; // "tCPU" with t in the low nibble of CL break; case 1: eax = (0 << 12) | // Type = 0 (2 bits) Original OEM Processor (4 << 8) | // Family = 4 (4 bits) 80486 (1 << 4) | // Model = 1 (4 bits) 0; // Stepping=0 (4 bits) edx = (fUseNPXEM) ? 1: 0; // bit 0: FPU on-chip. wx86cpu doesn't // support any other features. break; default: // // The Intel behavior indicates that if eax is out-of-range, the // results returned in the regsiters are unpredictable but it // doesn't fault. // break; } } FRAG2REF(CmpXchg8bFrag32, ULONGLONG) { ULONGLONG EdxEax; ULONGLONG Value; EdxEax = (((ULONGLONG)edx) << 32) | (ULONGLONG)eax; Value = *(ULONGLONG UNALIGNED *)pop1; if (Value == EdxEax) { ULONGLONG EcxEbx; EcxEbx = (ULONGLONG)ecx << 32 | (ULONGLONG)ebx; *(ULONGLONG UNALIGNED *)pop1 = EcxEbx; SET_ZFLAG(0); // zf has inverse logic } else { eax = (ULONG)Value; edx = (ULONG)(Value >> 32); SET_ZFLAG(1); // zf has inverse logic } } FRAG0(Rdtsc) { LARGE_INTEGER Counter; // This is cheese, but it will at least return a value that increases // over time. NtQueryPerformanceCounter(&Counter, NULL); edx = Counter.HighPart; eax = Counter.LowPart; }