| 3142 | } |
| 3143 | |
| 3144 | void OpDispatchBuilder::DECOp(OpcodeArgs) { |
| 3145 | Ref Dest; |
| 3146 | Ref Result; |
| 3147 | const auto Size = GetSrcBitSize(Op); |
| 3148 | const bool IsLocked = DestIsLockedMem(Op); |
| 3149 | |
| 3150 | if (IsLocked) { |
| 3151 | HandledLock = true; |
| 3152 | |
| 3153 | Ref DestAddress = MakeSegmentAddress(Op, Op->Dest); |
| 3154 | |
| 3155 | // Use Add instead of Sub to avoid a NEG |
| 3156 | Dest = _AtomicFetchAdd(OpSizeFromSrc(Op), Constant(Size == 64 ? -1 : ((1ULL << Size) - 1)), DestAddress); |
| 3157 | } else { |
| 3158 | Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, {.AllowUpperGarbage = Size >= 32}); |
| 3159 | } |
| 3160 | |
| 3161 | CalculateDeferredFlags(); |
| 3162 | |
| 3163 | if (Size < 32 && CTX->HostFeatures.SupportsFlagM) { |
| 3164 | // Subtraction producing upper garbage |
| 3165 | Result = Sub(OpSize::i32Bit, Dest, 1); |
| 3166 | CalculatePF(Result); |
| 3167 | CalculateAF(Dest, Constant(1)); |
| 3168 | |
| 3169 | // Correctly set NZ flags, preserving C |
| 3170 | HandleNZCV_RMW(); |
| 3171 | _SetSmallNZV(OpSizeFromSrc(Op), Result); |
| 3172 | |
| 3173 | // Fix up V flag. DEC overflows only when decrementing a negative and |
| 3174 | // getting a positive. So compare the sign bits to calculate V. |
| 3175 | _RmifNZCV(_Andn(OpSize::i32Bit, Dest, Result), Size - 1, 1); |
| 3176 | } else { |
| 3177 | Result = CalculateFlags_SUB(OpSizeFromSrc(Op), Dest, Constant(1), false); |
| 3178 | } |
| 3179 | |
| 3180 | if (!IsLocked) { |
| 3181 | StoreResult(GPRClass, Op, Result, OpSize::iInvalid); |
| 3182 | } |
| 3183 | } |
| 3184 | |
| 3185 | void OpDispatchBuilder::STOSOp(OpcodeArgs) { |
| 3186 | if (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_ADDRESS_SIZE) { |
nothing calls this directly
no outgoing calls
no test coverage detected